import 'package:flutter/material.dart'; enum AppMapType { openStreetMap, googleMaps, awsMaps } class AppLatLng { final double latitude; final double longitude; const AppLatLng(this.latitude, this.longitude); @override bool operator ==(Object other) => identical(this, other) || other is AppLatLng && runtimeType == other.runtimeType && latitude == other.latitude && longitude == other.longitude; @override int get hashCode => latitude.hashCode ^ longitude.hashCode; } class AppMapMarker { final String id; final AppLatLng position; final Widget? iconWidget; final double heading; // degrees (0-360) final String? label; final VoidCallback? onTap; const AppMapMarker({ required this.id, required this.position, this.iconWidget, this.heading = 0.0, this.label, this.onTap, }); } class AppPolyline { final String id; final List points; final Color color; final double width; const AppPolyline({ required this.id, required this.points, this.color = Colors.cyanAccent, this.width = 4.0, }); } class MapConfig { final String tileUrlTemplate; final String attributionText; final String userAgent; final double minZoom; final double maxZoom; final List subdomains; final bool isDarkMode; final bool offlineOnly; const MapConfig({ this.tileUrlTemplate = 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', this.attributionText = '© OpenStreetMap contributors', this.userAgent = 'MotorcycleDashboard/1.0 (contact: support@earnable.store)', this.minZoom = 3.0, this.maxZoom = 19.0, this.subdomains = const [], this.isDarkMode = false, this.offlineOnly = false, }); const MapConfig.tacticalDark({ this.tileUrlTemplate = 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', this.attributionText = '© OpenStreetMap contributors', this.userAgent = 'MotorcycleDashboard/1.0 (contact: support@earnable.store)', this.minZoom = 3.0, this.maxZoom = 19.0, this.subdomains = const [], this.isDarkMode = true, this.offlineOnly = false, }); const MapConfig.satellite4K({ this.tileUrlTemplate = 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', this.attributionText = '© Esri, Maxar, Earthstar Geographics', this.userAgent = 'MotorcycleDashboard/1.0 (contact: support@earnable.store)', this.minZoom = 1.0, this.maxZoom = 19.0, this.subdomains = const [], this.isDarkMode = false, this.offlineOnly = false, }); const MapConfig.elevation3D({ this.tileUrlTemplate = 'https://tile.opentopomap.org/{z}/{x}/{y}.png', this.attributionText = '© OpenTopoMap, © OpenStreetMap contributors', this.userAgent = 'MotorcycleDashboard/1.0 (contact: support@earnable.store)', this.minZoom = 3.0, this.maxZoom = 17.0, this.subdomains = const ['a', 'b', 'c'], this.isDarkMode = false, this.offlineOnly = false, }); const MapConfig.awsLocation({ required String apiKey, String region = 'us-east-2', this.offlineOnly = false, }) : tileUrlTemplate = 'https://maps.geo.$region.amazonaws.com/v2/tiles/raster.satellite/{z}/{x}/{y}?key=$apiKey', attributionText = '© AWS, Maxar, Earthstar Geographics', userAgent = 'MotorcycleDashboard/1.0 (contact: support@earnable.store)', minZoom = 1.0, maxZoom = 19.0, subdomains = const [], isDarkMode = false; } abstract class AppMapProvider { AppMapType get type; String get name; Widget buildMapView({ required BuildContext context, required AppLatLng center, required double zoom, double bearing = 0.0, required List markers, required List polylines, required bool isFollowMode, required MapConfig config, required ValueChanged? onPositionChanged, required VoidCallback? onUserInteraction, }); }