Add OSM backend for map browser

This commit is contained in:
Ming Ming 2024-07-22 23:17:20 +08:00
parent 812c8eee9c
commit 3c94741da3
5 changed files with 189 additions and 1 deletions

View file

@ -49,6 +49,9 @@ class _MapViewState extends State<_MapView> {
}, },
googleClusterBuilder: (context, dataPoints) => googleClusterBuilder: (context, dataPoints) =>
_GoogleMarkerBuilder(context).build(dataPoints), _GoogleMarkerBuilder(context).build(dataPoints),
osmClusterBuilder: (context, dataPoints) => _OsmMarker(
count: dataPoints.length,
),
contentPadding: EdgeInsets.only( contentPadding: EdgeInsets.only(
top: MediaQuery.of(context).padding.top, top: MediaQuery.of(context).padding.top,
bottom: MediaQuery.of(context).padding.bottom, bottom: MediaQuery.of(context).padding.bottom,
@ -68,6 +71,30 @@ class _MapViewState extends State<_MapView> {
InteractiveMapController? _controller; InteractiveMapController? _controller;
} }
class _OsmMarker extends StatelessWidget {
const _OsmMarker({
required this.count,
});
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: Theme.of(context).colorScheme.primary,
),
child: Center(
child: Text(
count.toString(),
style: const TextStyle(color: Colors.white),
),
),
);
}
final int count;
}
class _PanelContainer extends StatefulWidget { class _PanelContainer extends StatefulWidget {
const _PanelContainer({ const _PanelContainer({
required this.isShow, required this.isShow,

View file

@ -26,6 +26,14 @@ packages:
url: "https://gitlab.com/nc-photos/plus_plugins" url: "https://gitlab.com/nc-photos/plus_plugins"
source: git source: git
version: "3.1.1" version: "3.1.1"
animated_stack_widget:
dependency: transitive
description:
name: animated_stack_widget
sha256: ce4788dd158768c9d4388354b6fb72600b78e041a37afc4c279c63ecafcb9408
url: "https://pub.dev"
source: hosted
version: "0.0.4"
args: args:
dependency: transitive dependency: transitive
description: description:
@ -545,6 +553,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.1.0" version: "6.1.0"
flutter_map_marker_cluster:
dependency: transitive
description:
name: flutter_map_marker_cluster
sha256: a324f48da5ee83a3f29fd8d08b4b1e6e3114ff5c6cab910124d6a2e1f06f08cc
url: "https://pub.dev"
source: hosted
version: "1.3.6"
flutter_map_marker_popup:
dependency: transitive
description:
name: flutter_map_marker_popup
sha256: ec563bcbae24a18ac16815fb75ac5ab33ccba609e14db70e252a67de19c6639c
url: "https://pub.dev"
source: hosted
version: "6.1.2"
flutter_plugin_android_lifecycle: flutter_plugin_android_lifecycle:
dependency: transitive dependency: transitive
description: description:

View file

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:np_gps_map/src/gps_map.dart'; import 'package:np_gps_map/src/gps_map.dart';
import 'package:np_gps_map/src/interactive_map/google.dart'; import 'package:np_gps_map/src/interactive_map/google.dart';
import 'package:np_gps_map/src/interactive_map/osm.dart';
import 'package:np_gps_map/src/map_coord.dart'; import 'package:np_gps_map/src/map_coord.dart';
import 'package:np_gps_map/src/util.dart'; import 'package:np_gps_map/src/util.dart';
import 'package:np_platform_util/np_platform_util.dart'; import 'package:np_platform_util/np_platform_util.dart';
@ -26,6 +27,7 @@ class InteractiveMap extends StatelessWidget {
this.initialZoom, this.initialZoom,
this.dataPoints, this.dataPoints,
this.onClusterTap, this.onClusterTap,
this.osmClusterBuilder,
this.googleClusterBuilder, this.googleClusterBuilder,
this.contentPadding, this.contentPadding,
this.onMapCreated, this.onMapCreated,
@ -35,7 +37,15 @@ class InteractiveMap extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (providerHint == GpsMapProvider.osm || if (providerHint == GpsMapProvider.osm ||
(getRawPlatform() == NpPlatform.android && !isNewGMapsRenderer())) { (getRawPlatform() == NpPlatform.android && !isNewGMapsRenderer())) {
return const SizedBox.shrink(); return OsmInteractiveMap(
initialPosition: initialPosition,
initialZoom: initialZoom,
dataPoints: dataPoints,
onClusterTap: onClusterTap,
clusterBuilder: osmClusterBuilder,
contentPadding: contentPadding,
onMapCreated: onMapCreated,
);
} else { } else {
return GoogleInteractiveMap( return GoogleInteractiveMap(
initialPosition: initialPosition, initialPosition: initialPosition,
@ -57,6 +67,7 @@ class InteractiveMap extends StatelessWidget {
final List<DataPoint>? dataPoints; final List<DataPoint>? dataPoints;
final void Function(List<DataPoint> dataPoints)? onClusterTap; final void Function(List<DataPoint> dataPoints)? onClusterTap;
final GoogleClusterBuilder? googleClusterBuilder; final GoogleClusterBuilder? googleClusterBuilder;
final OsmClusterBuilder? osmClusterBuilder;
final EdgeInsets? contentPadding; final EdgeInsets? contentPadding;
final void Function(InteractiveMapController controller)? onMapCreated; final void Function(InteractiveMapController controller)? onMapCreated;
} }

View file

@ -0,0 +1,125 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_marker_cluster/flutter_map_marker_cluster.dart';
import 'package:latlong2/latlong.dart';
import 'package:np_common/object_util.dart';
import 'package:np_gps_map/src/interactive_map.dart';
import 'package:np_gps_map/src/map_coord.dart';
typedef OsmClusterBuilder = Widget Function(
BuildContext context, List<DataPoint> dataPoints);
class OsmInteractiveMap extends StatefulWidget {
const OsmInteractiveMap({
super.key,
this.initialPosition,
this.initialZoom,
this.dataPoints,
this.clusterBuilder,
this.onClusterTap,
this.contentPadding,
this.onMapCreated,
});
@override
State<StatefulWidget> createState() => _OsmInteractiveMapState();
final MapCoord? initialPosition;
final double? initialZoom;
final List<DataPoint>? dataPoints;
final OsmClusterBuilder? clusterBuilder;
final void Function(List<DataPoint> dataPoints)? onClusterTap;
final EdgeInsets? contentPadding;
final void Function(InteractiveMapController controller)? onMapCreated;
}
class _OsmInteractiveMapState extends State<OsmInteractiveMap> {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
if (_parentController == null) {
_parentController = _ParentController(_controller);
widget.onMapCreated?.call(_parentController!);
}
});
}
@override
Widget build(BuildContext context) {
return FlutterMap(
mapController: _controller,
options: MapOptions(
initialCenter: widget.initialPosition?.toLatLng() ?? const LatLng(0, 0),
initialZoom: max(2.5, widget.initialZoom ?? 2.5),
minZoom: 2.5,
),
children: [
TileLayer(
urlTemplate: "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
),
if (widget.dataPoints != null)
MarkerClusterLayerWidget(
options: MarkerClusterLayerOptions(
markers: widget.dataPoints!
.map((e) => _OsmDataPoint(
original: e,
child: _buildMarker(context, [e]),
))
.toList(),
builder: (context, markers) => _buildMarker(
context,
markers.cast<_OsmDataPoint>().map((e) => e.original).toList(),
),
),
),
Padding(
padding: widget.contentPadding ?? EdgeInsets.zero,
child: const SimpleAttributionWidget(
source: Text("OpenStreetMap contributors"),
),
),
],
);
}
Widget _buildMarker(BuildContext context, List<DataPoint> dataPoints) {
if (widget.clusterBuilder == null) {
return const SizedBox.shrink();
} else {
return GestureDetector(
onTap: widget.onClusterTap?.let((l) => () => l(dataPoints)),
child: widget.clusterBuilder!(context, dataPoints),
);
}
}
_ParentController? _parentController;
late final _controller = MapController();
}
class _OsmDataPoint extends Marker {
_OsmDataPoint({
required this.original,
required super.child,
}) : super(point: original.position.toLatLng());
final DataPoint original;
}
class _ParentController implements InteractiveMapController {
const _ParentController(this.controller);
@override
void setPosition(MapCoord position) {
controller.move(position.toLatLng(), 10);
}
final MapController controller;
}
extension on MapCoord {
LatLng toLatLng() => LatLng(latitude, longitude);
}

View file

@ -12,6 +12,7 @@ dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
flutter_map: ^6.1.0 flutter_map: ^6.1.0
flutter_map_marker_cluster: ^1.3.6
google_maps_flutter: 2.5.3 google_maps_flutter: 2.5.3
google_maps_cluster_manager: 3.1.0 google_maps_cluster_manager: 3.1.0
latlong2: any latlong2: any