Files
MobilApp/lib/models/vibro_nav_route.dart
T

200 lines
7.0 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:flutter/material.dart';
import 'package:latlong2/latlong.dart';
import 'package:terepi_seged/services/coord_converter_service.dart';
import 'package:get/get.dart';
/// Egy navigációs útvonal (kerülő, megközelítés…) a
/// `terepi_seged_vibro_nav_routes` táblából.
///
/// A `geometry` az EREDETI GeoJSON (wgs84 vagy eov), a `lines` pedig a
/// BETÖLTÉSKOR egyszer, WGS84-re átváltott, kirajzolásra kész vonalak —
/// a térkép így nem számol újra GNSS-frissítéskor.
class VibroNavRoute {
final String id;
final String projectId;
final String name;
final Map<String, dynamic> geometry;
final String crs; // 'auto' | 'wgs84' | 'eov'
final DateTime? validDate;
final String? vehicle;
final String routeKind; // 'detour' | 'access' | 'restricted'
final String? color;
final bool isActive;
final List<List<LatLng>> lines;
const VibroNavRoute({
required this.id,
required this.projectId,
required this.name,
required this.geometry,
required this.crs,
required this.validDate,
required this.vehicle,
required this.routeKind,
required this.color,
required this.isActive,
required this.lines,
});
factory VibroNavRoute.fromMap(Map<String, dynamic> m) {
final geometry = Map<String, dynamic>.from(m['geometry'] as Map);
final crs = (m['crs'] as String?) ?? 'auto';
final dateStr = m['valid_date'] as String?;
return VibroNavRoute(
id: m['id'] as String,
projectId: m['project_id'] as String,
name: (m['name'] as String?) ?? '',
geometry: geometry,
crs: crs,
validDate: dateStr == null ? null : DateTime.parse(dateStr),
vehicle: (m['vehicle'] as String?)?.trim().isEmpty ?? true
? null
: (m['vehicle'] as String).trim(),
routeKind: (m['route_kind'] as String?) ?? 'detour',
color: m['color'] as String?,
isActive: (m['is_active'] as bool?) ?? true,
lines: toWgsLines(geometry, crs),
);
}
int get pointCount => lines.fold(0, (s, l) => s + l.length);
String get dateLabel => validDate == null
? 'minden nap'
: '${validDate!.year}-${validDate!.month.toString().padLeft(2, '0')}-'
'${validDate!.day.toString().padLeft(2, '0')}';
String get vehicleLabel => vehicle ?? 'minden jármű';
// ═════════════════════════════════════════════════════════════════
// Fajták / színek
// ═════════════════════════════════════════════════════════════════
static const kinds = <String, String>{
'detour': 'Kerülő',
'access': 'Megközelítés',
'restricted': 'Tiltott / veszélyes',
};
static Color colorFor(String kind, String? hex) {
final custom = _parseHex(hex);
if (custom != null) return custom;
switch (kind) {
case 'access':
return const Color(0xFF2979FF);
case 'restricted':
return const Color(0xFFD50000);
case 'detour':
default:
return const Color(0xFFFFAB00);
}
}
static Color? _parseHex(String? hex) {
if (hex == null) return null;
var h = hex.trim().replaceFirst('#', '');
if (h.length == 6) h = 'FF$h';
if (h.length != 8) return null;
final v = int.tryParse(h, radix: 16);
return v == null ? null : Color(v);
}
// ═════════════════════════════════════════════════════════════════
// Geometria → WGS84 vonalak
// ═════════════════════════════════════════════════════════════════
/// Megállapítja a koordinátarendszert az ELSŐ koordináta alapján
/// (egy geometrián belül egységes vetületet feltételezünk).
/// WGS84: |x| ≤ 180 és |y| ≤ 90. EOV: több tízezres–százezres értékek.
static String detectCrs(Map<String, dynamic> geometry) {
final first = _firstCoordinate(geometry);
if (first == null) return 'wgs84';
return (first[0].abs() <= 180 && first[1].abs() <= 90) ? 'wgs84' : 'eov';
}
static List<double>? _firstCoordinate(Map<String, dynamic> geometry) {
final lines = _rawLines(geometry);
for (final l in lines) {
if (l.isNotEmpty) return l.first;
}
return null;
}
/// A GeoJSON geometria nyers vonalai ([x, y] párok listájának listája).
static List<List<List<double>>> _rawLines(Map<String, dynamic> geometry) {
final type = geometry['type'];
final coords = geometry['coordinates'];
if (coords is! List) return const [];
List<List<double>> parseLine(dynamic raw) {
final out = <List<double>>[];
if (raw is! List) return out;
for (final c in raw) {
if (c is List && c.length >= 2 && c[0] is num && c[1] is num) {
out.add([(c[0] as num).toDouble(), (c[1] as num).toDouble()]);
}
}
return out;
}
if (type == 'LineString') return [parseLine(coords)];
if (type == 'MultiLineString') return coords.map(parseLine).toList();
return const [];
}
/// WGS84 [LatLng] vonalak. A nem véges vagy érvénytelen pontokat
/// eldobja (a LatLng NaN összeomlasztja a térképet), a 2 pontnál
/// rövidebb vonalakat kihagyja.
static List<List<LatLng>> toWgsLines(
Map<String, dynamic> geometry, String crs) {
final raw = _rawLines(geometry);
if (raw.isEmpty) return const [];
final effective = crs == 'auto' ? detectCrs(geometry) : crs;
final out = <List<LatLng>>[];
for (final line in raw) {
final pts = <LatLng>[];
for (final c in line) {
final ll = effective == 'eov' ? _eovToLatLng(c[0], c[1]) : _wgs(c);
if (ll != null) pts.add(ll);
}
if (pts.length >= 2) out.add(pts);
}
return out;
}
static LatLng? _wgs(List<double> c) {
var lon = c[0];
var lat = c[1];
// Gyakori hiba: felcserélt sorrend [szélesség, hosszúság]. Magyarország
// területén a szélesség 45–49, a hosszúság 16–23, ezért egyértelmű.
if (lon > 44 && lon < 50 && lat > 15 && lat < 24) {
final t = lon;
lon = lat;
lat = t;
}
if (!lon.isFinite || !lat.isFinite) return null;
if (lon.abs() > 180 || lat.abs() > 90) return null;
return LatLng(lat, lon);
}
static LatLng? _eovToLatLng(double a, double b) {
if (!Get.isRegistered<CoordConverterService>()) return null;
// Szabvány: [EOV Y (kelet ~400k–950k), EOV X (észak ~45k–350k)].
// Ha felcserélve érkezett, javítjuk.
var y = a;
var x = b;
if (y < 400000 && x > 400000) {
final t = y;
y = x;
x = t;
}
try {
final w = CoordConverterService.to.eovToWgsPoint(y, x);
if (w.x.isFinite && w.y.isFinite) return LatLng(w.y, w.x);
} catch (_) {}
return null;
}
}