diff --git a/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java b/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java index 23f476dcc..ffcc319be 100644 --- a/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java +++ b/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java @@ -441,6 +441,22 @@ public void onMethodCall(MethodCall call, MethodChannel.Result result) { result.success(reply); break; } + case "map#toScreenLocation": { + Map reply = new HashMap<>(); + PointF pointf = mapboxMap.getProjection().toScreenLocation(new LatLng(call.argument("latitude"),call.argument("longitude"))); + reply.put("x", pointf.x); + reply.put("y", pointf.y); + result.success(reply); + break; + } + case "map#toLatLng": { + Map reply = new HashMap<>(); + LatLng latlng = mapboxMap.getProjection().fromScreenLocation(new PointF( ((Double) call.argument("x")).floatValue(), ((Double) call.argument("y")).floatValue())); + reply.put("latitude", latlng.getLatitude()); + reply.put("longitude", latlng.getLongitude()); + result.success(reply); + break; + } case "camera#move": { final CameraUpdate cameraUpdate = Convert.toCameraUpdate(call.argument("cameraUpdate"), mapboxMap, density); if (cameraUpdate != null) { diff --git a/example/lib/map_ui.dart b/example/lib/map_ui.dart index 6e344495b..44378f62d 100644 --- a/example/lib/map_ui.dart +++ b/example/lib/map_ui.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:math'; + import 'package:flutter/material.dart'; import 'package:mapbox_gl/mapbox_gl.dart'; @@ -266,6 +268,10 @@ class MapUiBodyState extends State { }, onMapLongClick: (point, latLng) async { print("Map long press: ${point.x},${point.y} ${latLng.latitude}/${latLng.longitude}"); + Point convertedPoint = await mapController.toScreenLocation(latLng); + LatLng convertedLatLng = await mapController.toLatLng(point); + print("Map long press converted: ${convertedPoint.x},${convertedPoint.y} ${convertedLatLng.latitude}/${convertedLatLng.longitude}"); + List features = await mapController.queryRenderedFeatures(point, [], null); if (features.length>0) { print(features[0]); diff --git a/ios/Classes/MapboxMapController.swift b/ios/Classes/MapboxMapController.swift index 6384ee51a..5a66efd53 100644 --- a/ios/Classes/MapboxMapController.swift +++ b/ios/Classes/MapboxMapController.swift @@ -165,6 +165,26 @@ class MapboxMapController: NSObject, FlutterPlatformView, MGLMapViewDelegate, Ma reply["sw"] = [visibleRegion.sw.latitude, visibleRegion.sw.longitude] as NSObject reply["ne"] = [visibleRegion.ne.latitude, visibleRegion.ne.longitude] as NSObject result(reply) + case "map#toScreenLocation": + guard let arguments = methodCall.arguments as? [String: Any] else { return } + guard let latitude = arguments["latitude"] as? Double else { return } + guard let longitude = arguments["longitude"] as? Double else { return } + let latlng = CLLocationCoordinate2DMake(latitude, longitude) + let returnVal = mapView.convert(latlng, toPointTo: mapView) + var reply = [String: NSObject]() + reply["x"] = returnVal.x as NSObject + reply["y"] = returnVal.y as NSObject + result(reply) + case "map#toLatLng": + guard let arguments = methodCall.arguments as? [String: Any] else { return } + guard let x = arguments["x"] as? Double else { return } + guard let y = arguments["y"] as? Double else { return } + let screenPoint: CGPoint = CGPoint(x: y, y:y) + let coordinates: CLLocationCoordinate2D = mapView.convert(screenPoint, toCoordinateFrom: mapView) + var reply = [String: NSObject]() + reply["latitude"] = coordinates.latitude as NSObject + reply["longitude"] = coordinates.longitude as NSObject + result(reply) case "camera#move": guard let arguments = methodCall.arguments as? [String: Any] else { return } guard let cameraUpdate = arguments["cameraUpdate"] as? [Any] else { return } diff --git a/lib/mapbox_gl.dart b/lib/mapbox_gl.dart index b6438b1f5..9113dd6b2 100644 --- a/lib/mapbox_gl.dart +++ b/lib/mapbox_gl.dart @@ -34,7 +34,7 @@ export 'package:mapbox_gl_platform_interface/mapbox_gl_platform_interface.dart' Line, LineOptions; -part 'src/bitmap.dart'; + part 'src/controller.dart'; part 'src/mapbox_map.dart'; part 'src/global.dart'; diff --git a/lib/src/bitmap.dart b/lib/src/bitmap.dart deleted file mode 100644 index 5393ba594..000000000 --- a/lib/src/bitmap.dart +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -part of mapbox_gl; - -/// Defines a bitmap image. For a marker, this class can be used to set the -/// image of the marker icon. For a ground overlay, it can be used to set the -/// image to place on the surface of the earth. -class BitmapDescriptor { - const BitmapDescriptor._(this._json); - - static const double hueRed = 0.0; - static const double hueOrange = 30.0; - static const double hueYellow = 60.0; - static const double hueGreen = 120.0; - static const double hueCyan = 180.0; - static const double hueAzure = 210.0; - static const double hueBlue = 240.0; - static const double hueViolet = 270.0; - static const double hueMagenta = 300.0; - static const double hueRose = 330.0; - - /// Creates a BitmapDescriptor that refers to the default marker image. - static const BitmapDescriptor defaultMarker = - BitmapDescriptor._(['defaultMarker']); - - /// Creates a BitmapDescriptor that refers to a colorization of the default - /// marker image. For convenience, there is a predefined set of hue values. - /// See e.g. [hueYellow]. - static BitmapDescriptor defaultMarkerWithHue(double hue) { - assert(0.0 <= hue && hue < 360.0); - return BitmapDescriptor._(['defaultMarker', hue]); - } - - /// Creates a BitmapDescriptor using the name of a bitmap image in the assets - /// directory. - static BitmapDescriptor fromAsset(String assetName, {String package}) { - if (package == null) { - return BitmapDescriptor._(['fromAsset', assetName]); - } else { - return BitmapDescriptor._(['fromAsset', assetName, package]); - } - } - - final dynamic _json; - - dynamic _toJson() => _json; //ignore: unused_element -} diff --git a/lib/src/controller.dart b/lib/src/controller.dart index 660389ecc..13e226e50 100644 --- a/lib/src/controller.dart +++ b/lib/src/controller.dart @@ -670,4 +670,21 @@ class MapboxMapController extends ChangeNotifier { await MapboxGlPlatform.getInstance(_id) .setSymbolTextIgnorePlacement(enable); } + + /// Returns the point on the screen that corresponds to a geographical coordinate ([latLng]). The screen location is in screen pixels (not display pixels) relative to the top left of the map (not of the whole screen) + /// + /// Note: The resulting x and y coordinates are rounded to [int] on web, on other platforms they may differ very slightly (in the range of about 10^-10) from the actual nearest screen coordinate. + /// You therefore might want to round them appropriately, depending on your use case. + /// + /// Returns null if [latLng] is not currently visible on the map. + Future toScreenLocation(LatLng latLng) async{ + return MapboxGlPlatform.getInstance(_id).toScreenLocation(latLng); + } + + /// Returns the geographic location (as [LatLng]) that corresponds to a point on the screen. The screen location is specified in screen pixels (not display pixels) relative to the top left of the map (not the top left of the whole screen). + Future toLatLng(Point screenLocation) async{ + return MapboxGlPlatform.getInstance(_id).toLatLng(screenLocation); + } + + } diff --git a/mapbox_gl_platform_interface/lib/src/location.dart b/mapbox_gl_platform_interface/lib/src/location.dart index 617943453..6372e4ca9 100644 --- a/mapbox_gl_platform_interface/lib/src/location.dart +++ b/mapbox_gl_platform_interface/lib/src/location.dart @@ -103,3 +103,4 @@ class LatLngBounds { @override int get hashCode => hashValues(southwest, northeast); } + diff --git a/mapbox_gl_platform_interface/lib/src/mapbox_gl_platform_interface.dart b/mapbox_gl_platform_interface/lib/src/mapbox_gl_platform_interface.dart index da022c739..b53873293 100644 --- a/mapbox_gl_platform_interface/lib/src/mapbox_gl_platform_interface.dart +++ b/mapbox_gl_platform_interface/lib/src/mapbox_gl_platform_interface.dart @@ -210,4 +210,14 @@ abstract class MapboxGlPlatform { throw UnimplementedError( 'setSymbolTextIgnorePlacement() has not been implemented.'); } + + Future toScreenLocation(LatLng latLng) async{ + throw UnimplementedError( + 'toScreenLocation() has not been implemented.'); + } + + Future toLatLng(Point screenLocation) async{ + throw UnimplementedError( + 'toLatLng() has not been implemented.'); + } } diff --git a/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart b/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart index e1513daf7..1f512e27c 100644 --- a/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart +++ b/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart @@ -182,23 +182,22 @@ class MethodChannelMapboxGl extends MapboxGlPlatform { } @override - Future> addSymbols(List options, [List data]) async { + Future> addSymbols(List options, + [List data]) async { final List symbolIds = await _channel.invokeMethod( 'symbols#addAll', { 'options': options.map((o) => o.toJson()).toList(), }, ); - final List symbols = symbolIds.asMap().map( - (i, id) => MapEntry( + final List symbols = symbolIds + .asMap() + .map((i, id) => MapEntry( i, - Symbol( - id, - options.elementAt(i), - data != null && data.length > i ? data.elementAt(i) : null - ) - ) - ).values.toList(); + Symbol(id, options.elementAt(i), + data != null && data.length > i ? data.elementAt(i) : null))) + .values + .toList(); return symbols; } @@ -212,7 +211,7 @@ class MethodChannelMapboxGl extends MapboxGlPlatform { } @override - Future getSymbolLatLng(Symbol symbol) async{ + Future getSymbolLatLng(Symbol symbol) async { Map mapLatLng = await _channel.invokeMethod('symbol#getGeometry', { 'symbol': symbol._id, @@ -249,7 +248,7 @@ class MethodChannelMapboxGl extends MapboxGlPlatform { } @override - Future> getLineLatLngs(Line line) async{ + Future> getLineLatLngs(Line line) async { List latLngList = await _channel.invokeMethod('line#getGeometry', { 'line': line._id, @@ -453,4 +452,32 @@ class MethodChannelMapboxGl extends MapboxGlPlatform { return new Future.error(e); } } + + @override + Future toScreenLocation(LatLng latLng) async { + try { + var screenPosMap = await _channel + .invokeMethod('map#toScreenLocation', { + 'latitude': latLng.latitude, + 'longitude':latLng.longitude, + }); + return Point(screenPosMap['x'], screenPosMap['y']); + } on PlatformException catch (e) { + return new Future.error(e); + } + } + + @override + Future toLatLng(Point screenLocation) async { + try { + var latLngMap = await _channel + .invokeMethod('map#toLatLng', { + 'x': screenLocation.x, + 'y':screenLocation.y, + }); + return LatLng(latLngMap['latitude'], latLngMap['longitude']); + } on PlatformException catch (e) { + return new Future.error(e); + } + } } diff --git a/mapbox_gl_web/lib/src/mapbox_map_controller.dart b/mapbox_gl_web/lib/src/mapbox_map_controller.dart index f1a090d30..668c88fb1 100644 --- a/mapbox_gl_web/lib/src/mapbox_map_controller.dart +++ b/mapbox_gl_web/lib/src/mapbox_map_controller.dart @@ -615,4 +615,16 @@ class MapboxMapController extends MapboxGlPlatform _map.keyboard.disable(); } } + + @override + Future toScreenLocation(LatLng latLng) async { + var screenPosition = _map.project(LngLat(latLng.longitude, latLng.latitude)); + return Point(screenPosition.x.round(), screenPosition.y.round()); + } + + @override + Future toLatLng(Point screenLocation) async { + var lngLat = _map.unproject(mapbox.Point(screenLocation.x, screenLocation.y)); + return LatLng(lngLat.lat, lngLat.lng); + } }