Navigációs képernyőn útvonal megjelenítése supabase táblából és navigációs képernyővel történő indítás beállítások alapján
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 6s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 6s
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user