Skip to content

Commit 02b4a37

Browse files
authored
Added MapController.pointToLatLng() to get LatLng of given screen point (#1115)
* Added MapController.pointToLatLng() to get LatLng of given screen point Ref #496, ref #607, ref #981, ref #1010 * Better pointToLatLng example * dartfmt * Fix zoom breaking pointToLatLng() * Updated example to help fix the rotation issue * Rebased on latest * Fix pointToLatLng with rotation * Fix trailing lines (3x attempts) Co-authored-by: Polo <gitpjp@pm.me> and ibrierley
1 parent f6e2b23 commit 02b4a37

File tree

5 files changed

+178
-0
lines changed

5 files changed

+178
-0
lines changed

example/lib/main.dart

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
22
import 'package:flutter_map_example/pages/epsg4326_crs.dart';
33
import 'package:flutter_map_example/pages/map_inside_listview.dart';
44
import 'package:flutter_map_example/pages/network_tile_provider.dart';
5+
import 'package:flutter_map_example/pages/point_to_latlng.dart';
56

67
import './pages/animated_map_controller.dart';
78
import './pages/circle.dart';
@@ -82,6 +83,7 @@ class MyApp extends StatelessWidget {
8283
ResetTileLayerPage.route: (context) => const ResetTileLayerPage(),
8384
EPSG4326Page.route: (context) => const EPSG4326Page(),
8485
MaxBoundsPage.route: (context) => const MaxBoundsPage(),
86+
PointToLatLngPage.route: (context) => const PointToLatLngPage(),
8587
},
8688
);
8789
}
+134
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import 'dart:async';
2+
3+
import 'package:flutter/material.dart';
4+
import 'package:flutter_map/flutter_map.dart';
5+
import 'package:latlong2/latlong.dart';
6+
7+
import '../widgets/drawer.dart';
8+
9+
class PointToLatLngPage extends StatefulWidget {
10+
static const String route = 'point_to_latlng';
11+
12+
const PointToLatLngPage({Key? key}) : super(key: key);
13+
14+
@override
15+
PointToLatlngPage createState() {
16+
return PointToLatlngPage();
17+
}
18+
}
19+
20+
class PointToLatlngPage extends State<PointToLatLngPage> {
21+
late final MapController mapController;
22+
late final StreamSubscription mapEventSubscription;
23+
final pointSize = 40.0;
24+
final pointY = 200.0;
25+
26+
LatLng? latLng;
27+
28+
@override
29+
void initState() {
30+
super.initState();
31+
mapController = MapController();
32+
33+
mapEventSubscription = mapController.mapEventStream
34+
.listen((mapEvent) => onMapEvent(mapEvent, context));
35+
36+
Future.delayed(Duration.zero, () {
37+
mapController.onReady.then((_) => _updatePointLatLng(context));
38+
});
39+
}
40+
41+
@override
42+
Widget build(BuildContext context) {
43+
return Scaffold(
44+
appBar: AppBar(title: const Text('PointToLatlng')),
45+
floatingActionButton: Column(
46+
mainAxisAlignment: MainAxisAlignment.end,
47+
children: [
48+
FloatingActionButton(
49+
child: const Icon(Icons.rotate_right),
50+
onPressed: () => mapController.rotate(60.0),
51+
),
52+
const SizedBox(height: 15),
53+
FloatingActionButton(
54+
child: const Icon(Icons.cancel),
55+
onPressed: () => mapController.rotate(0.0),
56+
),
57+
],
58+
),
59+
drawer: buildDrawer(context, PointToLatLngPage.route),
60+
body: Stack(
61+
children: [
62+
FlutterMap(
63+
mapController: mapController,
64+
options: MapOptions(
65+
center: LatLng(51.5, -0.09),
66+
zoom: 5.0,
67+
minZoom: 3.0,
68+
),
69+
children: [
70+
TileLayerWidget(
71+
options: TileLayerOptions(
72+
urlTemplate:
73+
'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
74+
subdomains: ['a', 'b', 'c'])),
75+
if (latLng != null)
76+
MarkerLayerWidget(
77+
options: MarkerLayerOptions(
78+
markers: [
79+
Marker(
80+
width: pointSize,
81+
height: pointSize,
82+
point: latLng!,
83+
builder: (ctx) => const FlutterLogo(),
84+
)
85+
],
86+
))
87+
],
88+
),
89+
Container(
90+
color: Colors.white,
91+
height: 60,
92+
child: Center(
93+
child: Column(
94+
mainAxisAlignment: MainAxisAlignment.center,
95+
children: [
96+
Text(
97+
'flutter logo (${latLng?.latitude.toStringAsPrecision(4)},${latLng?.longitude.toStringAsPrecision(4)})',
98+
textAlign: TextAlign.center,
99+
),
100+
],
101+
))),
102+
Positioned(
103+
top: pointY - pointSize / 2,
104+
left: _getPointX(context) - pointSize / 2,
105+
child: Icon(Icons.crop_free, size: pointSize))
106+
],
107+
),
108+
);
109+
}
110+
111+
void onMapEvent(MapEvent mapEvent, BuildContext context) {
112+
_updatePointLatLng(context);
113+
}
114+
115+
void _updatePointLatLng(context) {
116+
final pointX = _getPointX(context);
117+
118+
final latLng = mapController.pointToLatLng(CustomPoint(pointX, pointY));
119+
120+
setState(() {
121+
this.latLng = latLng;
122+
});
123+
}
124+
125+
double _getPointX(BuildContext context) {
126+
return MediaQuery.of(context).size.width / 2;
127+
}
128+
129+
@override
130+
void dispose() {
131+
super.dispose();
132+
mapEventSubscription.cancel();
133+
}
134+
}

example/lib/widgets/drawer.dart

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import 'package:flutter_map_example/pages/epsg4326_crs.dart';
33
import 'package:flutter_map_example/pages/map_inside_listview.dart';
44
import 'package:flutter_map_example/pages/marker_rotate.dart';
55
import 'package:flutter_map_example/pages/network_tile_provider.dart';
6+
import 'package:flutter_map_example/pages/point_to_latlng.dart';
67

78
import '../pages/animated_map_controller.dart';
89
import '../pages/circle.dart';
@@ -250,6 +251,8 @@ Drawer buildDrawer(BuildContext context, String currentRoute) {
250251
),
251252
_buildMenuItem(context, const Text('Map inside listview'),
252253
MapInsideListViewPage.route, currentRoute),
254+
_buildMenuItem(context, const Text('Point to LatLng'),
255+
PointToLatLngPage.route, currentRoute),
253256
],
254257
),
255258
);

lib/flutter_map.dart

+3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import 'dart:math';
55

66
import 'package:flutter/widgets.dart';
77
import 'package:flutter_map/src/core/center_zoom.dart';
8+
import 'package:flutter_map/src/core/point.dart';
89
import 'package:flutter_map/src/geo/crs/crs.dart';
910
import 'package:flutter_map/src/geo/latlng_bounds.dart';
1011
import 'package:flutter_map/src/gestures/interactive_flag.dart';
@@ -148,6 +149,8 @@ abstract class MapController {
148149
set state(MapState state);
149150
void dispose();
150151

152+
LatLng? pointToLatLng(CustomPoint point);
153+
151154
factory MapController() => MapControllerImpl();
152155
}
153156

lib/src/map/map.dart

+36
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,42 @@ class MapControllerImpl implements MapController {
7878
return _state.rotate(degree, id: id, source: MapEventSource.mapController);
7979
}
8080

81+
@override
82+
LatLng? pointToLatLng(CustomPoint localPoint) {
83+
if (_state.originalSize == null) {
84+
return null;
85+
}
86+
87+
final width = _state.originalSize!.x;
88+
final height = _state.originalSize!.y;
89+
90+
final localPointCenterDistance =
91+
CustomPoint((width / 2) - localPoint.x, (height / 2) - localPoint.y);
92+
final mapCenter =
93+
_state.options.crs.latLngToPoint(_state.center, _state.zoom);
94+
95+
var point = mapCenter - localPointCenterDistance;
96+
97+
if (_state.rotation != 0.0) {
98+
point = rotatePoint(mapCenter, point);
99+
}
100+
101+
return _state.options.crs.pointToLatLng(point, _state.zoom);
102+
}
103+
104+
CustomPoint<num> rotatePoint(
105+
CustomPoint<num> mapCenter, CustomPoint<num> point) {
106+
final m = Matrix4.identity()
107+
..translate(mapCenter.x.toDouble(), mapCenter.y.toDouble())
108+
..rotateZ(-_state.rotationRad)
109+
..translate(-mapCenter.x.toDouble(), -mapCenter.y.toDouble());
110+
111+
final tp = MatrixUtils.transformPoint(
112+
m, Offset(point.x.toDouble(), point.y.toDouble()));
113+
114+
return CustomPoint(tp.dx, tp.dy);
115+
}
116+
81117
@override
82118
Stream<MapEvent> get mapEventStream => _mapEventSink.stream;
83119
}

0 commit comments

Comments
 (0)