Skip to content

Commit

Permalink
Added MapController.pointToLatLng() to get LatLng of given screen poi…
Browse files Browse the repository at this point in the history
…nt (#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 <[email protected]> and ibrierley
  • Loading branch information
HugoHeneault authored May 20, 2022
1 parent f6e2b23 commit 02b4a37
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 0 deletions.
2 changes: 2 additions & 0 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_map_example/pages/epsg4326_crs.dart';
import 'package:flutter_map_example/pages/map_inside_listview.dart';
import 'package:flutter_map_example/pages/network_tile_provider.dart';
import 'package:flutter_map_example/pages/point_to_latlng.dart';

import './pages/animated_map_controller.dart';
import './pages/circle.dart';
Expand Down Expand Up @@ -82,6 +83,7 @@ class MyApp extends StatelessWidget {
ResetTileLayerPage.route: (context) => const ResetTileLayerPage(),
EPSG4326Page.route: (context) => const EPSG4326Page(),
MaxBoundsPage.route: (context) => const MaxBoundsPage(),
PointToLatLngPage.route: (context) => const PointToLatLngPage(),
},
);
}
Expand Down
134 changes: 134 additions & 0 deletions example/lib/pages/point_to_latlng.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';

import '../widgets/drawer.dart';

class PointToLatLngPage extends StatefulWidget {
static const String route = 'point_to_latlng';

const PointToLatLngPage({Key? key}) : super(key: key);

@override
PointToLatlngPage createState() {
return PointToLatlngPage();
}
}

class PointToLatlngPage extends State<PointToLatLngPage> {
late final MapController mapController;
late final StreamSubscription mapEventSubscription;
final pointSize = 40.0;
final pointY = 200.0;

LatLng? latLng;

@override
void initState() {
super.initState();
mapController = MapController();

mapEventSubscription = mapController.mapEventStream
.listen((mapEvent) => onMapEvent(mapEvent, context));

Future.delayed(Duration.zero, () {
mapController.onReady.then((_) => _updatePointLatLng(context));
});
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('PointToLatlng')),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
FloatingActionButton(
child: const Icon(Icons.rotate_right),
onPressed: () => mapController.rotate(60.0),
),
const SizedBox(height: 15),
FloatingActionButton(
child: const Icon(Icons.cancel),
onPressed: () => mapController.rotate(0.0),
),
],
),
drawer: buildDrawer(context, PointToLatLngPage.route),
body: Stack(
children: [
FlutterMap(
mapController: mapController,
options: MapOptions(
center: LatLng(51.5, -0.09),
zoom: 5.0,
minZoom: 3.0,
),
children: [
TileLayerWidget(
options: TileLayerOptions(
urlTemplate:
'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
subdomains: ['a', 'b', 'c'])),
if (latLng != null)
MarkerLayerWidget(
options: MarkerLayerOptions(
markers: [
Marker(
width: pointSize,
height: pointSize,
point: latLng!,
builder: (ctx) => const FlutterLogo(),
)
],
))
],
),
Container(
color: Colors.white,
height: 60,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'flutter logo (${latLng?.latitude.toStringAsPrecision(4)},${latLng?.longitude.toStringAsPrecision(4)})',
textAlign: TextAlign.center,
),
],
))),
Positioned(
top: pointY - pointSize / 2,
left: _getPointX(context) - pointSize / 2,
child: Icon(Icons.crop_free, size: pointSize))
],
),
);
}

void onMapEvent(MapEvent mapEvent, BuildContext context) {
_updatePointLatLng(context);
}

void _updatePointLatLng(context) {
final pointX = _getPointX(context);

final latLng = mapController.pointToLatLng(CustomPoint(pointX, pointY));

setState(() {
this.latLng = latLng;
});
}

double _getPointX(BuildContext context) {
return MediaQuery.of(context).size.width / 2;
}

@override
void dispose() {
super.dispose();
mapEventSubscription.cancel();
}
}
3 changes: 3 additions & 0 deletions example/lib/widgets/drawer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:flutter_map_example/pages/epsg4326_crs.dart';
import 'package:flutter_map_example/pages/map_inside_listview.dart';
import 'package:flutter_map_example/pages/marker_rotate.dart';
import 'package:flutter_map_example/pages/network_tile_provider.dart';
import 'package:flutter_map_example/pages/point_to_latlng.dart';

import '../pages/animated_map_controller.dart';
import '../pages/circle.dart';
Expand Down Expand Up @@ -250,6 +251,8 @@ Drawer buildDrawer(BuildContext context, String currentRoute) {
),
_buildMenuItem(context, const Text('Map inside listview'),
MapInsideListViewPage.route, currentRoute),
_buildMenuItem(context, const Text('Point to LatLng'),
PointToLatLngPage.route, currentRoute),
],
),
);
Expand Down
3 changes: 3 additions & 0 deletions lib/flutter_map.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'dart:math';

import 'package:flutter/widgets.dart';
import 'package:flutter_map/src/core/center_zoom.dart';
import 'package:flutter_map/src/core/point.dart';
import 'package:flutter_map/src/geo/crs/crs.dart';
import 'package:flutter_map/src/geo/latlng_bounds.dart';
import 'package:flutter_map/src/gestures/interactive_flag.dart';
Expand Down Expand Up @@ -148,6 +149,8 @@ abstract class MapController {
set state(MapState state);
void dispose();

LatLng? pointToLatLng(CustomPoint point);

factory MapController() => MapControllerImpl();
}

Expand Down
36 changes: 36 additions & 0 deletions lib/src/map/map.dart
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,42 @@ class MapControllerImpl implements MapController {
return _state.rotate(degree, id: id, source: MapEventSource.mapController);
}

@override
LatLng? pointToLatLng(CustomPoint localPoint) {
if (_state.originalSize == null) {
return null;
}

final width = _state.originalSize!.x;
final height = _state.originalSize!.y;

final localPointCenterDistance =
CustomPoint((width / 2) - localPoint.x, (height / 2) - localPoint.y);
final mapCenter =
_state.options.crs.latLngToPoint(_state.center, _state.zoom);

var point = mapCenter - localPointCenterDistance;

if (_state.rotation != 0.0) {
point = rotatePoint(mapCenter, point);
}

return _state.options.crs.pointToLatLng(point, _state.zoom);
}

CustomPoint<num> rotatePoint(
CustomPoint<num> mapCenter, CustomPoint<num> point) {
final m = Matrix4.identity()
..translate(mapCenter.x.toDouble(), mapCenter.y.toDouble())
..rotateZ(-_state.rotationRad)
..translate(-mapCenter.x.toDouble(), -mapCenter.y.toDouble());

final tp = MatrixUtils.transformPoint(
m, Offset(point.x.toDouble(), point.y.toDouble()));

return CustomPoint(tp.dx, tp.dy);
}

@override
Stream<MapEvent> get mapEventStream => _mapEventSink.stream;
}
Expand Down

0 comments on commit 02b4a37

Please sign in to comment.