125 lines
3.8 KiB
Dart
125 lines
3.8 KiB
Dart
import 'dart:math' as math;
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_map/flutter_map.dart';
|
|
import 'package:latlong2/latlong.dart' as ll;
|
|
import 'map_provider_interface.dart';
|
|
|
|
class OpenStreetMapProvider implements AppMapProvider {
|
|
final MapController _mapController = MapController();
|
|
ll.LatLng? _lastFollowPos;
|
|
DateTime? _lastFollowTime;
|
|
|
|
@override
|
|
AppMapType get type => AppMapType.openStreetMap;
|
|
|
|
@override
|
|
String get name => 'OpenStreetMap';
|
|
|
|
@override
|
|
Widget buildMapView({
|
|
required BuildContext context,
|
|
required AppLatLng center,
|
|
required double zoom,
|
|
required List<AppMapMarker> markers,
|
|
required List<AppPolyline> polylines,
|
|
required bool isFollowMode,
|
|
required MapConfig config,
|
|
required ValueChanged<AppLatLng>? onPositionChanged,
|
|
required VoidCallback? onUserInteraction,
|
|
}) {
|
|
final centerLl = ll.LatLng(center.latitude, center.longitude);
|
|
|
|
if (isFollowMode) {
|
|
final now = DateTime.now();
|
|
final posDelta = _lastFollowPos == null
|
|
? 1.0
|
|
: (centerLl.latitude - _lastFollowPos!.latitude).abs() +
|
|
(centerLl.longitude - _lastFollowPos!.longitude).abs();
|
|
|
|
final shouldMove = _lastFollowPos == null ||
|
|
(posDelta > 0.00005 && (_lastFollowTime == null || now.difference(_lastFollowTime!).inMilliseconds > 500));
|
|
|
|
if (shouldMove) {
|
|
_lastFollowPos = centerLl;
|
|
_lastFollowTime = now;
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
try {
|
|
_mapController.move(centerLl, zoom);
|
|
} catch (_) {}
|
|
});
|
|
}
|
|
}
|
|
|
|
final media = MediaQuery.of(context);
|
|
final sizeKey = '${media.orientation}_${media.size.width.toInt()}x${media.size.height.toInt()}';
|
|
|
|
return FlutterMap(
|
|
key: ValueKey('osm_map_$sizeKey'),
|
|
mapController: _mapController,
|
|
options: MapOptions(
|
|
initialCenter: centerLl,
|
|
initialZoom: zoom,
|
|
minZoom: config.minZoom,
|
|
maxZoom: config.maxZoom,
|
|
backgroundColor: const Color(0xFF0F0F0F),
|
|
onPositionChanged: (position, hasGesture) {
|
|
if (hasGesture) {
|
|
onUserInteraction?.call();
|
|
}
|
|
if (onPositionChanged != null) {
|
|
final c = position.center;
|
|
onPositionChanged(AppLatLng(c.latitude, c.longitude));
|
|
}
|
|
},
|
|
),
|
|
children: [
|
|
TileLayer(
|
|
urlTemplate: config.tileUrlTemplate,
|
|
subdomains: config.subdomains,
|
|
userAgentPackageName: 'com.example.motorcycle_dash',
|
|
maxZoom: 20,
|
|
maxNativeZoom: 19,
|
|
),
|
|
PolylineLayer(
|
|
polylines: polylines.map((p) {
|
|
return Polyline(
|
|
points: p.points.map((pt) => ll.LatLng(pt.latitude, pt.longitude)).toList(),
|
|
strokeWidth: p.width,
|
|
color: p.color,
|
|
);
|
|
}).toList(),
|
|
),
|
|
MarkerLayer(
|
|
markers: markers.map((m) {
|
|
return Marker(
|
|
point: ll.LatLng(m.position.latitude, m.position.longitude),
|
|
width: 50,
|
|
height: 50,
|
|
child: GestureDetector(
|
|
onTap: m.onTap,
|
|
child: Transform.rotate(
|
|
angle: (m.heading * math.pi) / 180.0,
|
|
child: m.iconWidget ??
|
|
const Icon(
|
|
Icons.navigation,
|
|
color: Colors.cyanAccent,
|
|
size: 36,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}).toList(),
|
|
),
|
|
RichAttributionWidget(
|
|
attributions: [
|
|
TextSourceAttribution(
|
|
config.attributionText,
|
|
textStyle: const TextStyle(color: Colors.white70, fontSize: 10),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|