diff --git a/lib/models/osm_camera_node.dart b/lib/models/osm_camera_node.dart index 405f8f8..f2e17aa 100644 --- a/lib/models/osm_camera_node.dart +++ b/lib/models/osm_camera_node.dart @@ -11,8 +11,12 @@ class OsmCameraNode { required this.tags, }); - bool get hasDirection => tags.containsKey('direction'); - double? get directionDeg => - hasDirection ? double.tryParse(tags['direction']!) : null; + bool get hasDirection => + tags.containsKey('direction') || tags.containsKey('camera:direction'); + + double? get directionDeg { + final val = tags['direction'] ?? tags['camera:direction']; + return double.tryParse(val ?? ''); + } } diff --git a/lib/services/overpass_service.dart b/lib/services/overpass_service.dart index 276edbe..9f8a0e6 100644 --- a/lib/services/overpass_service.dart +++ b/lib/services/overpass_service.dart @@ -1,8 +1,7 @@ import 'dart:convert'; - import 'package:http/http.dart' as http; -import 'package:flutter_map/flutter_map.dart'; // LatLngBounds -import 'package:latlong2/latlong.dart'; // LatLng +import 'package:flutter_map/flutter_map.dart'; +import 'package:latlong2/latlong.dart'; import '../models/camera_profile.dart'; import '../models/osm_camera_node.dart'; @@ -16,7 +15,7 @@ class OverpassService { ) async { if (profiles.isEmpty) return []; - // Combine enabled profile types into a regex + // Build regex of surveillance:type values from enabled profiles final types = profiles .map((p) => p.tags['surveillance:type']) .whereType() @@ -35,20 +34,25 @@ class OverpassService { out body 100; '''; - final resp = - await http.post(Uri.parse(_endpoint), body: {'data': query.trim()}); - if (resp.statusCode != 200) return []; + try { + final resp = + await http.post(Uri.parse(_endpoint), body: {'data': query.trim()}); + if (resp.statusCode != 200) return []; - final data = jsonDecode(resp.body) as Map; - final elements = data['elements'] as List; + final data = jsonDecode(resp.body) as Map; + final elements = data['elements'] as List; - return elements.whereType>().map((e) { - return OsmCameraNode( - id: e['id'], - coord: LatLng(e['lat'], e['lon']), - tags: Map.from(e['tags'] ?? {}), - ); - }).toList(); + return elements.whereType>().map((e) { + return OsmCameraNode( + id: e['id'], + coord: LatLng(e['lat'], e['lon']), + tags: Map.from(e['tags'] ?? {}), + ); + }).toList(); + } catch (_) { + // Network error – return empty list silently + return []; + } } } diff --git a/lib/widgets/map_view.dart b/lib/widgets/map_view.dart index f566a76..be23920 100644 --- a/lib/widgets/map_view.dart +++ b/lib/widgets/map_view.dart @@ -73,15 +73,29 @@ class _MapViewState extends State { } Future _refreshCameras(AppState appState) async { - final bounds = _controller.camera.visibleBounds; + LatLngBounds? bounds; + try { + bounds = _controller.camera.visibleBounds; + } catch (_) { + return; // controller not ready yet + } final cams = await _overpass.fetchCameras(bounds, appState.enabledProfiles); if (mounted) setState(() => _cameras = cams); } + double _safeZoom() { + try { + return _controller.camera.zoom; + } catch (_) { + return 15.0; + } + } + @override Widget build(BuildContext context) { final appState = context.watch(); final session = appState.session; + final zoom = _safeZoom(); final markers = [ if (_currentLatLng != null) @@ -103,12 +117,10 @@ class _MapViewState extends State { final overlays = [ if (session != null && session.target != null) - _buildCone(session.target!, session.directionDegrees), + _buildCone(session.target!, session.directionDegrees, zoom), ..._cameras - .where((n) => n.hasDirection) - .map( - (n) => _buildCone(n.coord, n.directionDeg!), - ), + .where((n) => n.hasDirection && n.directionDeg != null) + .map((n) => _buildCone(n.coord, n.directionDeg!, zoom)), ]; return Stack( @@ -129,7 +141,8 @@ class _MapViewState extends State { ), children: [ TileLayer( - urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', + urlTemplate: + 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', userAgentPackageName: 'com.example.flock_map_app', ), PolygonLayer(polygons: overlays), @@ -146,9 +159,9 @@ class _MapViewState extends State { ); } - Polygon _buildCone(LatLng origin, double bearingDeg) { + Polygon _buildCone(LatLng origin, double bearingDeg, double zoom) { const halfAngle = 15.0; - const length = 0.08; // ~80 m + final length = 0.002 * math.pow(2, 15 - zoom); LatLng _project(double deg) { final rad = deg * math.pi / 180; diff --git a/pubspec.yaml b/pubspec.yaml index cc1632b..19d74e1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: flock_map_app -description: Simple OSM camera-mapping client +description: Simple OSM camera‑mapping client publish_to: "none" -version: 0.2.0 +version: 0.2.1 environment: sdk: ">=3.3.0 <4.0.0" @@ -14,7 +14,7 @@ dependencies: flutter_map: ^6.1.0 latlong2: ^0.9.0 geolocator: ^10.1.0 - http: ^1.2.1 # <- NEW for Overpass requests + http: ^1.2.1 dev_dependencies: flutter_test: