123 lines
4.2 KiB
Dart
123 lines
4.2 KiB
Dart
import 'dart:convert';
|
||
import 'package:be_happy/domain/entities/point.dart';
|
||
|
||
class Zone {
|
||
final int id;
|
||
final String title;
|
||
final String description;
|
||
final String type;
|
||
final bool isActive;
|
||
final String shapeType;
|
||
// Трехмерная структура для поддержки MultiPolygon и дыр (holes):
|
||
// geometry[индекс_полигона][индекс_кольца][индекс_точки]
|
||
// индекс_кольца == 0 — внешняя граница, > 0 — внутренние вырезы (дыры)
|
||
final List<List<List<Point>>> geometry;
|
||
final String speedLimit;
|
||
|
||
Zone({
|
||
required this.id,
|
||
required this.title,
|
||
required this.description,
|
||
required this.type,
|
||
required this.isActive,
|
||
required this.shapeType,
|
||
required this.geometry,
|
||
required this.speedLimit,
|
||
});
|
||
|
||
// Геттер для обратной совместимости (возвращает внешнее кольцо первого полигона)
|
||
List<Point> get points {
|
||
if (geometry.isNotEmpty && geometry[0].isNotEmpty) {
|
||
return geometry[0][0];
|
||
}
|
||
return [];
|
||
}
|
||
|
||
factory Zone.fromJson(Map<String, dynamic> json) {
|
||
final zoneCoordinates = json['coordinates'] as Map<String, dynamic>? ?? {};
|
||
final String coordsString = zoneCoordinates['coordinates'] ?? '[]';
|
||
final String shapeType = zoneCoordinates['type'] ?? 'Polygon';
|
||
|
||
List<List<List<Point>>> parsedGeometry = [];
|
||
|
||
try {
|
||
final dynamic decoded = jsonDecode(coordsString);
|
||
|
||
if (decoded is List && decoded.isNotEmpty) {
|
||
if (shapeType == 'Polygon') {
|
||
// Структура: [[[lon, lat], [lon, lat], ...], [hole1], ...]
|
||
List<List<Point>> polygonRings = [];
|
||
for (var ring in decoded) {
|
||
if (ring is List) {
|
||
List<Point> ringPoints = [];
|
||
for (var coord in ring) {
|
||
if (coord is List && coord.length >= 2) {
|
||
// В GeoJSON сначала идет Longitude, затем Latitude
|
||
ringPoints.add(Point(
|
||
(coord[1] as num).toDouble(),
|
||
(coord[0] as num).toDouble(),
|
||
));
|
||
}
|
||
}
|
||
if (ringPoints.isNotEmpty) polygonRings.add(ringPoints);
|
||
}
|
||
}
|
||
if (polygonRings.isNotEmpty) parsedGeometry.add(polygonRings);
|
||
|
||
} else if (shapeType == 'MultiPolygon') {
|
||
// Структура: [[[[lon, lat], ...]], [[[lon, lat], ...]]]
|
||
for (var poly in decoded) {
|
||
if (poly is List) {
|
||
List<List<Point>> polygonRings = [];
|
||
for (var ring in poly) {
|
||
if (ring is List) {
|
||
List<Point> ringPoints = [];
|
||
for (var coord in ring) {
|
||
if (coord is List && coord.length >= 2) {
|
||
ringPoints.add(Point(
|
||
(coord[1] as num).toDouble(),
|
||
(coord[0] as num).toDouble(),
|
||
));
|
||
}
|
||
}
|
||
if (ringPoints.isNotEmpty) polygonRings.add(ringPoints);
|
||
}
|
||
}
|
||
if (polygonRings.isNotEmpty) parsedGeometry.add(polygonRings);
|
||
}
|
||
}
|
||
} else {
|
||
// Fallback для LineString или плоских списков
|
||
List<Point> ringPoints = [];
|
||
for (var coord in decoded) {
|
||
if (coord is List && coord.length >= 2) {
|
||
ringPoints.add(Point(
|
||
(coord[1] as num).toDouble(),
|
||
(coord[0] as num).toDouble(),
|
||
));
|
||
}
|
||
}
|
||
if (ringPoints.isNotEmpty) parsedGeometry.add([ringPoints]);
|
||
}
|
||
}
|
||
} catch (e) {
|
||
print("PARSE ERROR for Zone ID ${json['id']}: $e");
|
||
}
|
||
|
||
return Zone(
|
||
id: json['id'] ?? 0,
|
||
title: json['title'] ?? 'Unknown',
|
||
description: json['description'] ?? '',
|
||
type: json['type'] ?? '',
|
||
isActive: json['isActive'] ?? false,
|
||
speedLimit: json['speedLimit'] ?? '',
|
||
shapeType: shapeType,
|
||
geometry: parsedGeometry,
|
||
);
|
||
}
|
||
|
||
@override
|
||
String toString() {
|
||
return 'Zone{id: $id, title: $title, type: $type, polygonsCount: ${geometry.length}}';
|
||
}
|
||
} |