From 9136f399ab1ade73f2e0e0cd6fcdf2b8fd282282 Mon Sep 17 00:00:00 2001 From: Joscha <34318751+josxha@users.noreply.github.com> Date: Sat, 14 Sep 2024 02:09:51 +0200 Subject: [PATCH 01/37] add getCamera() --- .../josxha/maplibre/MapLibreMapController.kt | 9 + .../com/github/josxha/maplibre/Pigeon.g.kt | 67 ++++- example/lib/controller_page.dart | 26 ++ ios/Classes/Pigeon.g.swift | 64 ++++- lib/maplibre.dart | 1 + lib/src/map_camera.dart | 29 ++ lib/src/map_controller.dart | 3 + lib/src/native/pigeon.g.dart | 261 +++++++++--------- lib/src/native/widget_state.dart | 11 + lib/src/web/interop/map.dart | 14 + lib/src/web/widget_state.dart | 8 + linux/pigeon.g.cc | 172 ++++++++++++ linux/pigeon.g.h | 86 +++++- macos/Classes/Pigeon.g.swift | 64 ++++- pigeons/pigeon.dart | 24 +- windows/runner/pigeon.g.cpp | 111 ++++++++ windows/runner/pigeon.g.h | 52 +++- 17 files changed, 865 insertions(+), 137 deletions(-) create mode 100644 lib/src/map_camera.dart diff --git a/android/src/main/kotlin/com/github/josxha/maplibre/MapLibreMapController.kt b/android/src/main/kotlin/com/github/josxha/maplibre/MapLibreMapController.kt index aabad5d..a2a1e40 100644 --- a/android/src/main/kotlin/com/github/josxha/maplibre/MapLibreMapController.kt +++ b/android/src/main/kotlin/com/github/josxha/maplibre/MapLibreMapController.kt @@ -1,6 +1,7 @@ package com.github.josxha.maplibre import LngLat +import MapCamera import MapLibreFlutterApi import MapLibreHostApi import MapOptions @@ -146,6 +147,14 @@ class MapLibreMapController( callback(Result.success(LngLat(latLng.longitude, latLng.latitude))) } + override fun getCamera(callback: (Result) -> Unit) { + val position = mapLibreMap.cameraPosition + val target = mapLibreMap.cameraPosition.target!! + val center = LngLat(target.longitude, target.latitude) + val camera = MapCamera(center, position.zoom, position.tilt, position.bearing) + callback(Result.success(camera)) + } + override fun addFillLayer(id: String, sourceId: String, callback: (Result) -> Unit) { mapLibreMap.style?.addLayer(FillLayer(id, sourceId)) callback(Result.success(Unit)) diff --git a/android/src/main/kotlin/com/github/josxha/maplibre/Pigeon.g.kt b/android/src/main/kotlin/com/github/josxha/maplibre/Pigeon.g.kt index fdc2e16..c455ab5 100644 --- a/android/src/main/kotlin/com/github/josxha/maplibre/Pigeon.g.kt +++ b/android/src/main/kotlin/com/github/josxha/maplibre/Pigeon.g.kt @@ -94,7 +94,7 @@ data class MapOptions ( } /** - * A longitude/latitude coordinate object + * A longitude/latitude coordinate object. * * Generated class from Pigeon that represents data sent in messages. */ @@ -121,7 +121,7 @@ data class LngLat ( } /** - * A pixel location / location on the device screen + * A pixel location / location on the device screen. * * Generated class from Pigeon that represents data sent in messages. */ @@ -146,6 +146,37 @@ data class ScreenLocation ( ) } } + +/** + * The current position of the map camera. + * + * Generated class from Pigeon that represents data sent in messages. + */ +data class MapCamera ( + val center: LngLat, + val zoom: Double, + val tilt: Double, + val bearing: Double +) + { + companion object { + fun fromList(pigeonVar_list: List): MapCamera { + val center = pigeonVar_list[0] as LngLat + val zoom = pigeonVar_list[1] as Double + val tilt = pigeonVar_list[2] as Double + val bearing = pigeonVar_list[3] as Double + return MapCamera(center, zoom, tilt, bearing) + } + } + fun toList(): List { + return listOf( + center, + zoom, + tilt, + bearing, + ) + } +} private open class PigeonPigeonCodec : StandardMessageCodec() { override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { return when (type) { @@ -164,6 +195,11 @@ private open class PigeonPigeonCodec : StandardMessageCodec() { ScreenLocation.fromList(it) } } + 132.toByte() -> { + return (readValue(buffer) as? List)?.let { + MapCamera.fromList(it) + } + } else -> super.readValueOfType(type, buffer) } } @@ -181,6 +217,10 @@ private open class PigeonPigeonCodec : StandardMessageCodec() { stream.write(131) writeValue(stream, value.toList()) } + is MapCamera -> { + stream.write(132) + writeValue(stream, value.toList()) + } else -> super.writeValue(stream, value) } } @@ -193,6 +233,11 @@ interface MapLibreHostApi { fun jumpTo(center: LngLat?, zoom: Double?, bearing: Double?, pitch: Double?, callback: (Result) -> Unit) /** Animate the viewport of the map to a new location. */ fun flyTo(center: LngLat?, zoom: Double?, bearing: Double?, pitch: Double?, durationMs: Long, callback: (Result) -> Unit) + /** + * Get the current camera position with the map center, zoom level, camera + * tilt and map rotation. + */ + fun getCamera(callback: (Result) -> Unit) /** Convert a coordinate to a location on the screen. */ fun toScreenLocation(lng: Double, lat: Double, callback: (Result) -> Unit) /** Convert a screen location to a coordinate. */ @@ -258,6 +303,24 @@ interface MapLibreHostApi { channel.setMessageHandler(null) } } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.maplibre.MapLibreHostApi.getCamera$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + api.getCamera{ result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(wrapResult(data)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } run { val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.maplibre.MapLibreHostApi.toScreenLocation$separatedMessageChannelSuffix", codec) if (api != null) { diff --git a/example/lib/controller_page.dart b/example/lib/controller_page.dart index 9eaffde..571b9f5 100644 --- a/example/lib/controller_page.dart +++ b/example/lib/controller_page.dart @@ -63,6 +63,32 @@ class _ControllerPageState extends State { }, child: const Text('Fly to Iceland'), ), + OutlinedButton( + onPressed: () async { + final camera = await _controller.getCamera(); + debugPrint(camera.toString()); + if (context.mounted) { + await showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('MapCenter'), + content: Text(''' +center: Position(lng: ${camera.center.lng}, lat: ${camera.center.lat}) +zoom: ${camera.zoom} +bearing: ${camera.bearing} +tilt: ${camera.tilt}'''), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('OK'), + ), + ], + ), + ); + } + }, + child: const Text('Get MapCamera'), + ), ], ), ), diff --git a/ios/Classes/Pigeon.g.swift b/ios/Classes/Pigeon.g.swift index bf0bbaa..be6fe6d 100644 --- a/ios/Classes/Pigeon.g.swift +++ b/ios/Classes/Pigeon.g.swift @@ -122,7 +122,7 @@ struct MapOptions { } } -/// A longitude/latitude coordinate object +/// A longitude/latitude coordinate object. /// /// Generated class from Pigeon that represents data sent in messages. struct LngLat { @@ -151,7 +151,7 @@ struct LngLat { } } -/// A pixel location / location on the device screen +/// A pixel location / location on the device screen. /// /// Generated class from Pigeon that represents data sent in messages. struct ScreenLocation { @@ -180,6 +180,41 @@ struct ScreenLocation { } } +/// The current position of the map camera. +/// +/// Generated class from Pigeon that represents data sent in messages. +struct MapCamera { + var center: LngLat + var zoom: Double + var tilt: Double + var bearing: Double + + + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> MapCamera? { + let center = pigeonVar_list[0] as! LngLat + let zoom = pigeonVar_list[1] as! Double + let tilt = pigeonVar_list[2] as! Double + let bearing = pigeonVar_list[3] as! Double + + return MapCamera( + center: center, + zoom: zoom, + tilt: tilt, + bearing: bearing + ) + } + func toList() -> [Any?] { + return [ + center, + zoom, + tilt, + bearing, + ] + } +} + private class PigeonPigeonCodecReader: FlutterStandardReader { override func readValue(ofType type: UInt8) -> Any? { switch type { @@ -189,6 +224,8 @@ private class PigeonPigeonCodecReader: FlutterStandardReader { return LngLat.fromList(self.readValue() as! [Any?]) case 131: return ScreenLocation.fromList(self.readValue() as! [Any?]) + case 132: + return MapCamera.fromList(self.readValue() as! [Any?]) default: return super.readValue(ofType: type) } @@ -206,6 +243,9 @@ private class PigeonPigeonCodecWriter: FlutterStandardWriter { } else if let value = value as? ScreenLocation { super.writeByte(131) super.writeValue(value.toList()) + } else if let value = value as? MapCamera { + super.writeByte(132) + super.writeValue(value.toList()) } else { super.writeValue(value) } @@ -233,6 +273,9 @@ protocol MapLibreHostApi { func jumpTo(center: LngLat?, zoom: Double?, bearing: Double?, pitch: Double?, completion: @escaping (Result) -> Void) /// Animate the viewport of the map to a new location. func flyTo(center: LngLat?, zoom: Double?, bearing: Double?, pitch: Double?, durationMs: Int64, completion: @escaping (Result) -> Void) + /// Get the current camera position with the map center, zoom level, camera + /// tilt and map rotation. + func getCamera(completion: @escaping (Result) -> Void) /// Convert a coordinate to a location on the screen. func toScreenLocation(lng: Double, lat: Double, completion: @escaping (Result) -> Void) /// Convert a screen location to a coordinate. @@ -294,6 +337,23 @@ class MapLibreHostApiSetup { } else { flyToChannel.setMessageHandler(nil) } + /// Get the current camera position with the map center, zoom level, camera + /// tilt and map rotation. + let getCameraChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.maplibre.MapLibreHostApi.getCamera\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + getCameraChannel.setMessageHandler { _, reply in + api.getCamera { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + getCameraChannel.setMessageHandler(nil) + } /// Convert a coordinate to a location on the screen. let toScreenLocationChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.maplibre.MapLibreHostApi.toScreenLocation\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) if let api = api { diff --git a/lib/maplibre.dart b/lib/maplibre.dart index a5a67b6..b8f9cda 100644 --- a/lib/maplibre.dart +++ b/lib/maplibre.dart @@ -5,6 +5,7 @@ export 'package:geotypes/geotypes.dart'; export 'src/annotations.dart'; export 'src/layers.dart'; export 'src/map.dart'; +export 'src/map_camera.dart'; export 'src/map_controller.dart'; export 'src/map_options.dart'; export 'src/sources.dart'; diff --git a/lib/src/map_camera.dart b/lib/src/map_camera.dart new file mode 100644 index 0000000..6cc01fd --- /dev/null +++ b/lib/src/map_camera.dart @@ -0,0 +1,29 @@ +import 'package:geotypes/geotypes.dart'; + +/// The current camera position on the map. +class MapCamera { + /// Default constructor for a [MapCamera]. + const MapCamera({ + required this.center, + required this.zoom, + required this.tilt, + required this.bearing, + }); + + /// The position of the map center. + final Position center; + + /// The zoom level of the map. + final double zoom; + + /// The bearing of the map. + final double bearing; + + /// The camera tilt of the map. + final double tilt; + + @override + String toString() => 'MapCamera(' + 'center: Position(lng: ${center.lng}, lat: ${center.lat}), ' + 'zoom: $zoom, bearing: $bearing, tilt: $tilt)'; +} diff --git a/lib/src/map_controller.dart b/lib/src/map_controller.dart index 4f6ad8d..bc31f00 100644 --- a/lib/src/map_controller.dart +++ b/lib/src/map_controller.dart @@ -40,4 +40,7 @@ abstract interface class MapController { /// Add a new layer to the map. The source must be added before adding it to /// the map. Future addLayer(Layer layer); + + /// Get the current camera position on the map. + Future getCamera(); } diff --git a/lib/src/native/pigeon.g.dart b/lib/src/native/pigeon.g.dart index 29fe312..694ebb2 100644 --- a/lib/src/native/pigeon.g.dart +++ b/lib/src/native/pigeon.g.dart @@ -15,8 +15,7 @@ PlatformException _createConnectionError(String channelName) { ); } -List wrapResponse( - {Object? result, PlatformException? error, bool empty = false}) { +List wrapResponse({Object? result, PlatformException? error, bool empty = false}) { if (empty) { return []; } @@ -85,7 +84,7 @@ class MapOptions { } } -/// A longitude/latitude coordinate object +/// A longitude/latitude coordinate object. class LngLat { LngLat({ required this.lng, @@ -114,7 +113,7 @@ class LngLat { } } -/// A pixel location / location on the device screen +/// A pixel location / location on the device screen. class ScreenLocation { ScreenLocation({ required this.x, @@ -143,6 +142,44 @@ class ScreenLocation { } } +/// The current position of the map camera. +class MapCamera { + MapCamera({ + required this.center, + required this.zoom, + required this.tilt, + required this.bearing, + }); + + LngLat center; + + double zoom; + + double tilt; + + double bearing; + + Object encode() { + return [ + center, + zoom, + tilt, + bearing, + ]; + } + + static MapCamera decode(Object result) { + result as List; + return MapCamera( + center: result[0]! as LngLat, + zoom: result[1]! as double, + tilt: result[2]! as double, + bearing: result[3]! as double, + ); + } +} + + class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @override @@ -150,15 +187,18 @@ class _PigeonCodec extends StandardMessageCodec { if (value is int) { buffer.putUint8(4); buffer.putInt64(value); - } else if (value is MapOptions) { + } else if (value is MapOptions) { buffer.putUint8(129); writeValue(buffer, value.encode()); - } else if (value is LngLat) { + } else if (value is LngLat) { buffer.putUint8(130); writeValue(buffer, value.encode()); - } else if (value is ScreenLocation) { + } else if (value is ScreenLocation) { buffer.putUint8(131); writeValue(buffer, value.encode()); + } else if (value is MapCamera) { + buffer.putUint8(132); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -167,12 +207,14 @@ class _PigeonCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 129: + case 129: return MapOptions.decode(readValue(buffer)!); - case 130: + case 130: return LngLat.decode(readValue(buffer)!); - case 131: + case 131: return ScreenLocation.decode(readValue(buffer)!); + case 132: + return MapCamera.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); } @@ -183,11 +225,9 @@ class MapLibreHostApi { /// Constructor for [MapLibreHostApi]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - MapLibreHostApi( - {BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + MapLibreHostApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = - messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; final BinaryMessenger? pigeonVar_binaryMessenger; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); @@ -195,22 +235,15 @@ class MapLibreHostApi { final String pigeonVar_messageChannelSuffix; /// Move the viewport of the map to a new location without any animation. - Future jumpTo({ - required LngLat? center, - required double? zoom, - required double? bearing, - required double? pitch, - }) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.maplibre.MapLibreHostApi.jumpTo$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + Future jumpTo({required LngLat? center, required double? zoom, required double? bearing, required double? pitch,}) async { + final String pigeonVar_channelName = 'dev.flutter.pigeon.maplibre.MapLibreHostApi.jumpTo$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = await pigeonVar_channel - .send([center, zoom, bearing, pitch]) as List?; + final List? pigeonVar_replyList = + await pigeonVar_channel.send([center, zoom, bearing, pitch]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -225,24 +258,15 @@ class MapLibreHostApi { } /// Animate the viewport of the map to a new location. - Future flyTo({ - required LngLat? center, - required double? zoom, - required double? bearing, - required double? pitch, - required int durationMs, - }) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.maplibre.MapLibreHostApi.flyTo$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + Future flyTo({required LngLat? center, required double? zoom, required double? bearing, required double? pitch, required int durationMs,}) async { + final String pigeonVar_channelName = 'dev.flutter.pigeon.maplibre.MapLibreHostApi.flyTo$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = await pigeonVar_channel - .send([center, zoom, bearing, pitch, durationMs]) - as List?; + final List? pigeonVar_replyList = + await pigeonVar_channel.send([center, zoom, bearing, pitch, durationMs]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -256,12 +280,39 @@ class MapLibreHostApi { } } + /// Get the current camera position with the map center, zoom level, camera + /// tilt and map rotation. + Future getCamera() async { + final String pigeonVar_channelName = 'dev.flutter.pigeon.maplibre.MapLibreHostApi.getCamera$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = + await pigeonVar_channel.send(null) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as MapCamera?)!; + } + } + /// Convert a coordinate to a location on the screen. Future toScreenLocation(double lng, double lat) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.maplibre.MapLibreHostApi.toScreenLocation$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final String pigeonVar_channelName = 'dev.flutter.pigeon.maplibre.MapLibreHostApi.toScreenLocation$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -288,10 +339,8 @@ class MapLibreHostApi { /// Convert a screen location to a coordinate. Future toLngLat(double x, double y) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.maplibre.MapLibreHostApi.toLngLat$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final String pigeonVar_channelName = 'dev.flutter.pigeon.maplibre.MapLibreHostApi.toLngLat$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -317,12 +366,9 @@ class MapLibreHostApi { } /// Add a fill layer to the map style. - Future addFillLayer( - {required String id, required String sourceId}) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.maplibre.MapLibreHostApi.addFillLayer$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + Future addFillLayer({required String id, required String sourceId}) async { + final String pigeonVar_channelName = 'dev.flutter.pigeon.maplibre.MapLibreHostApi.addFillLayer$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -343,12 +389,9 @@ class MapLibreHostApi { } /// Add a circle layer to the map style. - Future addCircleLayer( - {required String id, required String sourceId}) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.maplibre.MapLibreHostApi.addCircleLayer$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + Future addCircleLayer({required String id, required String sourceId}) async { + final String pigeonVar_channelName = 'dev.flutter.pigeon.maplibre.MapLibreHostApi.addCircleLayer$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -369,12 +412,9 @@ class MapLibreHostApi { } /// Add a GeoJSON source to the map style. - Future addGeoJsonSource( - {required String id, required String data}) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.maplibre.MapLibreHostApi.addGeoJsonSource$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + Future addGeoJsonSource({required String id, required String data}) async { + final String pigeonVar_channelName = 'dev.flutter.pigeon.maplibre.MapLibreHostApi.addGeoJsonSource$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -417,19 +457,11 @@ abstract class MapLibreFlutterApi { /// Callback when the user performs a long lasting click on the map. void onLongClick(LngLat point); - static void setUp( - MapLibreFlutterApi? api, { - BinaryMessenger? binaryMessenger, - String messageChannelSuffix = '', - }) { - messageChannelSuffix = - messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + static void setUp(MapLibreFlutterApi? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) { + messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.maplibre.MapLibreFlutterApi.getOptions$messageChannelSuffix', - pigeonChannelCodec, + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.maplibre.MapLibreFlutterApi.getOptions$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { pigeonVar_channel.setMessageHandler(null); @@ -440,19 +472,15 @@ abstract class MapLibreFlutterApi { return wrapResponse(result: output); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onStyleLoaded$messageChannelSuffix', - pigeonChannelCodec, + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onStyleLoaded$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { pigeonVar_channel.setMessageHandler(null); @@ -463,26 +491,22 @@ abstract class MapLibreFlutterApi { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onClick$messageChannelSuffix', - pigeonChannelCodec, + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onClick$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { pigeonVar_channel.setMessageHandler(null); } else { pigeonVar_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onClick was null.'); + 'Argument for dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onClick was null.'); final List args = (message as List?)!; final LngLat? arg_point = (args[0] as LngLat?); assert(arg_point != null, @@ -492,26 +516,22 @@ abstract class MapLibreFlutterApi { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onSecondaryClick$messageChannelSuffix', - pigeonChannelCodec, + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onSecondaryClick$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { pigeonVar_channel.setMessageHandler(null); } else { pigeonVar_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onSecondaryClick was null.'); + 'Argument for dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onSecondaryClick was null.'); final List args = (message as List?)!; final LngLat? arg_point = (args[0] as LngLat?); assert(arg_point != null, @@ -521,26 +541,22 @@ abstract class MapLibreFlutterApi { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onDoubleClick$messageChannelSuffix', - pigeonChannelCodec, + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onDoubleClick$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { pigeonVar_channel.setMessageHandler(null); } else { pigeonVar_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onDoubleClick was null.'); + 'Argument for dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onDoubleClick was null.'); final List args = (message as List?)!; final LngLat? arg_point = (args[0] as LngLat?); assert(arg_point != null, @@ -550,26 +566,22 @@ abstract class MapLibreFlutterApi { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onLongClick$messageChannelSuffix', - pigeonChannelCodec, + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onLongClick$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { pigeonVar_channel.setMessageHandler(null); } else { pigeonVar_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onLongClick was null.'); + 'Argument for dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onLongClick was null.'); final List args = (message as List?)!; final LngLat? arg_point = (args[0] as LngLat?); assert(arg_point != null, @@ -579,9 +591,8 @@ abstract class MapLibreFlutterApi { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } diff --git a/lib/src/native/widget_state.dart b/lib/src/native/widget_state.dart index 074dab1..be0a1a4 100644 --- a/lib/src/native/widget_state.dart +++ b/lib/src/native/widget_state.dart @@ -147,4 +147,15 @@ final class MapLibreMapStateNative extends State @override void onLongClick(pigeon.LngLat point) => _options.onLongClick?.call(Position(point.lng, point.lat)); + + @override + Future getCamera() async { + final camera = await _hostApi.getCamera(); + return MapCamera( + center: camera.center.toPosition(), + zoom: camera.zoom, + tilt: camera.tilt, + bearing: camera.bearing, + ); + } } diff --git a/lib/src/web/interop/map.dart b/lib/src/web/interop/map.dart index 45d7a0d..6121734 100644 --- a/lib/src/web/interop/map.dart +++ b/lib/src/web/interop/map.dart @@ -43,6 +43,20 @@ extension type Map._(Camera _) implements Camera { /// Clean up and release all internal JS resources associated with this map. external void remove(); + + /// Returns the map's geographical centerpoint. + external LngLat getCenter(); + + /// Returns the map's current zoom level. + external num getZoom(); + + /// Returns the map's current bearing. The bearing is the compass direction + /// that is "up"; for example, a bearing of 90° orients the map so that + /// east is up. + external num getBearing(); + + /// Returns the map's current pitch (tilt). + external num getPitch(); } /// Anonymous MapOptions for the MapLibre JavaScript [Map]. diff --git a/lib/src/web/widget_state.dart b/lib/src/web/widget_state.dart index 23cc4bf..cc579a9 100644 --- a/lib/src/web/widget_state.dart +++ b/lib/src/web/widget_state.dart @@ -251,4 +251,12 @@ final class MapLibreMapStateWeb extends State ); } } + + @override + Future getCamera() async => MapCamera( + center: _map.getCenter().toPosition(), + zoom: _map.getZoom().toDouble(), + tilt: _map.getPitch().toDouble(), + bearing: _map.getBearing().toDouble(), + ); } diff --git a/linux/pigeon.g.cc b/linux/pigeon.g.cc index fa2cf54..58943ac 100644 --- a/linux/pigeon.g.cc +++ b/linux/pigeon.g.cc @@ -220,6 +220,80 @@ static MaplibreScreenLocation* maplibre_screen_location_new_from_list(FlValue* v return maplibre_screen_location_new(x, y); } +struct _MaplibreMapCamera { + GObject parent_instance; + + MaplibreLngLat* center; + double zoom; + double tilt; + double bearing; +}; + +G_DEFINE_TYPE(MaplibreMapCamera, maplibre_map_camera, G_TYPE_OBJECT) + +static void maplibre_map_camera_dispose(GObject* object) { + MaplibreMapCamera* self = MAPLIBRE_MAP_CAMERA(object); + g_clear_object(&self->center); + G_OBJECT_CLASS(maplibre_map_camera_parent_class)->dispose(object); +} + +static void maplibre_map_camera_init(MaplibreMapCamera* self) { +} + +static void maplibre_map_camera_class_init(MaplibreMapCameraClass* klass) { + G_OBJECT_CLASS(klass)->dispose = maplibre_map_camera_dispose; +} + +MaplibreMapCamera* maplibre_map_camera_new(MaplibreLngLat* center, double zoom, double tilt, double bearing) { + MaplibreMapCamera* self = MAPLIBRE_MAP_CAMERA(g_object_new(maplibre_map_camera_get_type(), nullptr)); + self->center = MAPLIBRE_LNG_LAT(g_object_ref(center)); + self->zoom = zoom; + self->tilt = tilt; + self->bearing = bearing; + return self; +} + +MaplibreLngLat* maplibre_map_camera_get_center(MaplibreMapCamera* self) { + g_return_val_if_fail(MAPLIBRE_IS_MAP_CAMERA(self), nullptr); + return self->center; +} + +double maplibre_map_camera_get_zoom(MaplibreMapCamera* self) { + g_return_val_if_fail(MAPLIBRE_IS_MAP_CAMERA(self), 0.0); + return self->zoom; +} + +double maplibre_map_camera_get_tilt(MaplibreMapCamera* self) { + g_return_val_if_fail(MAPLIBRE_IS_MAP_CAMERA(self), 0.0); + return self->tilt; +} + +double maplibre_map_camera_get_bearing(MaplibreMapCamera* self) { + g_return_val_if_fail(MAPLIBRE_IS_MAP_CAMERA(self), 0.0); + return self->bearing; +} + +static FlValue* maplibre_map_camera_to_list(MaplibreMapCamera* self) { + FlValue* values = fl_value_new_list(); + fl_value_append_take(values, fl_value_new_custom_object(130, G_OBJECT(self->center))); + fl_value_append_take(values, fl_value_new_float(self->zoom)); + fl_value_append_take(values, fl_value_new_float(self->tilt)); + fl_value_append_take(values, fl_value_new_float(self->bearing)); + return values; +} + +static MaplibreMapCamera* maplibre_map_camera_new_from_list(FlValue* values) { + FlValue* value0 = fl_value_get_list_value(values, 0); + MaplibreLngLat* center = MAPLIBRE_LNG_LAT(fl_value_get_custom_value_object(value0)); + FlValue* value1 = fl_value_get_list_value(values, 1); + double zoom = fl_value_get_float(value1); + FlValue* value2 = fl_value_get_list_value(values, 2); + double tilt = fl_value_get_float(value2); + FlValue* value3 = fl_value_get_list_value(values, 3); + double bearing = fl_value_get_float(value3); + return maplibre_map_camera_new(center, zoom, tilt, bearing); +} + G_DECLARE_FINAL_TYPE(MaplibreMessageCodec, maplibre_message_codec, MAPLIBRE, MESSAGE_CODEC, FlStandardMessageCodec) struct _MaplibreMessageCodec { @@ -250,6 +324,13 @@ static gboolean maplibre_message_codec_write_maplibre_screen_location(FlStandard return fl_standard_message_codec_write_value(codec, buffer, values, error); } +static gboolean maplibre_message_codec_write_maplibre_map_camera(FlStandardMessageCodec* codec, GByteArray* buffer, MaplibreMapCamera* value, GError** error) { + uint8_t type = 132; + g_byte_array_append(buffer, &type, sizeof(uint8_t)); + g_autoptr(FlValue) values = maplibre_map_camera_to_list(value); + return fl_standard_message_codec_write_value(codec, buffer, values, error); +} + static gboolean maplibre_message_codec_write_value(FlStandardMessageCodec* codec, GByteArray* buffer, FlValue* value, GError** error) { if (fl_value_get_type(value) == FL_VALUE_TYPE_CUSTOM) { switch (fl_value_get_custom_type(value)) { @@ -259,6 +340,8 @@ static gboolean maplibre_message_codec_write_value(FlStandardMessageCodec* codec return maplibre_message_codec_write_maplibre_lng_lat(codec, buffer, MAPLIBRE_LNG_LAT(fl_value_get_custom_value_object(value)), error); case 131: return maplibre_message_codec_write_maplibre_screen_location(codec, buffer, MAPLIBRE_SCREEN_LOCATION(fl_value_get_custom_value_object(value)), error); + case 132: + return maplibre_message_codec_write_maplibre_map_camera(codec, buffer, MAPLIBRE_MAP_CAMERA(fl_value_get_custom_value_object(value)), error); } } @@ -310,6 +393,21 @@ static FlValue* maplibre_message_codec_read_maplibre_screen_location(FlStandardM return fl_value_new_custom_object(131, G_OBJECT(value)); } +static FlValue* maplibre_message_codec_read_maplibre_map_camera(FlStandardMessageCodec* codec, GBytes* buffer, size_t* offset, GError** error) { + g_autoptr(FlValue) values = fl_standard_message_codec_read_value(codec, buffer, offset, error); + if (values == nullptr) { + return nullptr; + } + + g_autoptr(MaplibreMapCamera) value = maplibre_map_camera_new_from_list(values); + if (value == nullptr) { + g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED, "Invalid data received for MessageData"); + return nullptr; + } + + return fl_value_new_custom_object(132, G_OBJECT(value)); +} + static FlValue* maplibre_message_codec_read_value_of_type(FlStandardMessageCodec* codec, GBytes* buffer, size_t* offset, int type, GError** error) { switch (type) { case 129: @@ -318,6 +416,8 @@ static FlValue* maplibre_message_codec_read_value_of_type(FlStandardMessageCodec return maplibre_message_codec_read_maplibre_lng_lat(codec, buffer, offset, error); case 131: return maplibre_message_codec_read_maplibre_screen_location(codec, buffer, offset, error); + case 132: + return maplibre_message_codec_read_maplibre_map_camera(codec, buffer, offset, error); default: return FL_STANDARD_MESSAGE_CODEC_CLASS(maplibre_message_codec_parent_class)->read_value_of_type(codec, buffer, offset, type, error); } @@ -444,6 +544,45 @@ static MaplibreMapLibreHostApiFlyToResponse* maplibre_map_libre_host_api_fly_to_ return self; } +G_DECLARE_FINAL_TYPE(MaplibreMapLibreHostApiGetCameraResponse, maplibre_map_libre_host_api_get_camera_response, MAPLIBRE, MAP_LIBRE_HOST_API_GET_CAMERA_RESPONSE, GObject) + +struct _MaplibreMapLibreHostApiGetCameraResponse { + GObject parent_instance; + + FlValue* value; +}; + +G_DEFINE_TYPE(MaplibreMapLibreHostApiGetCameraResponse, maplibre_map_libre_host_api_get_camera_response, G_TYPE_OBJECT) + +static void maplibre_map_libre_host_api_get_camera_response_dispose(GObject* object) { + MaplibreMapLibreHostApiGetCameraResponse* self = MAPLIBRE_MAP_LIBRE_HOST_API_GET_CAMERA_RESPONSE(object); + g_clear_pointer(&self->value, fl_value_unref); + G_OBJECT_CLASS(maplibre_map_libre_host_api_get_camera_response_parent_class)->dispose(object); +} + +static void maplibre_map_libre_host_api_get_camera_response_init(MaplibreMapLibreHostApiGetCameraResponse* self) { +} + +static void maplibre_map_libre_host_api_get_camera_response_class_init(MaplibreMapLibreHostApiGetCameraResponseClass* klass) { + G_OBJECT_CLASS(klass)->dispose = maplibre_map_libre_host_api_get_camera_response_dispose; +} + +static MaplibreMapLibreHostApiGetCameraResponse* maplibre_map_libre_host_api_get_camera_response_new(MaplibreMapCamera* return_value) { + MaplibreMapLibreHostApiGetCameraResponse* self = MAPLIBRE_MAP_LIBRE_HOST_API_GET_CAMERA_RESPONSE(g_object_new(maplibre_map_libre_host_api_get_camera_response_get_type(), nullptr)); + self->value = fl_value_new_list(); + fl_value_append_take(self->value, fl_value_new_custom_object(132, G_OBJECT(return_value))); + return self; +} + +static MaplibreMapLibreHostApiGetCameraResponse* maplibre_map_libre_host_api_get_camera_response_new_error(const gchar* code, const gchar* message, FlValue* details) { + MaplibreMapLibreHostApiGetCameraResponse* self = MAPLIBRE_MAP_LIBRE_HOST_API_GET_CAMERA_RESPONSE(g_object_new(maplibre_map_libre_host_api_get_camera_response_get_type(), nullptr)); + self->value = fl_value_new_list(); + fl_value_append_take(self->value, fl_value_new_string(code)); + fl_value_append_take(self->value, fl_value_new_string(message != nullptr ? message : "")); + fl_value_append_take(self->value, details != nullptr ? fl_value_ref(details) : fl_value_new_null()); + return self; +} + G_DECLARE_FINAL_TYPE(MaplibreMapLibreHostApiToScreenLocationResponse, maplibre_map_libre_host_api_to_screen_location_response, MAPLIBRE, MAP_LIBRE_HOST_API_TO_SCREEN_LOCATION_RESPONSE, GObject) struct _MaplibreMapLibreHostApiToScreenLocationResponse { @@ -745,6 +884,17 @@ static void maplibre_map_libre_host_api_fly_to_cb(FlBasicMessageChannel* channel self->vtable->fly_to(center, zoom, bearing, pitch, duration_ms, handle, self->user_data); } +static void maplibre_map_libre_host_api_get_camera_cb(FlBasicMessageChannel* channel, FlValue* message_, FlBasicMessageChannelResponseHandle* response_handle, gpointer user_data) { + MaplibreMapLibreHostApi* self = MAPLIBRE_MAP_LIBRE_HOST_API(user_data); + + if (self->vtable == nullptr || self->vtable->get_camera == nullptr) { + return; + } + + g_autoptr(MaplibreMapLibreHostApiResponseHandle) handle = maplibre_map_libre_host_api_response_handle_new(channel, response_handle); + self->vtable->get_camera(handle, self->user_data); +} + static void maplibre_map_libre_host_api_to_screen_location_cb(FlBasicMessageChannel* channel, FlValue* message_, FlBasicMessageChannelResponseHandle* response_handle, gpointer user_data) { MaplibreMapLibreHostApi* self = MAPLIBRE_MAP_LIBRE_HOST_API(user_data); @@ -831,6 +981,9 @@ void maplibre_map_libre_host_api_set_method_handlers(FlBinaryMessenger* messenge g_autofree gchar* fly_to_channel_name = g_strdup_printf("dev.flutter.pigeon.maplibre.MapLibreHostApi.flyTo%s", dot_suffix); g_autoptr(FlBasicMessageChannel) fly_to_channel = fl_basic_message_channel_new(messenger, fly_to_channel_name, FL_MESSAGE_CODEC(codec)); fl_basic_message_channel_set_message_handler(fly_to_channel, maplibre_map_libre_host_api_fly_to_cb, g_object_ref(api_data), g_object_unref); + g_autofree gchar* get_camera_channel_name = g_strdup_printf("dev.flutter.pigeon.maplibre.MapLibreHostApi.getCamera%s", dot_suffix); + g_autoptr(FlBasicMessageChannel) get_camera_channel = fl_basic_message_channel_new(messenger, get_camera_channel_name, FL_MESSAGE_CODEC(codec)); + fl_basic_message_channel_set_message_handler(get_camera_channel, maplibre_map_libre_host_api_get_camera_cb, g_object_ref(api_data), g_object_unref); g_autofree gchar* to_screen_location_channel_name = g_strdup_printf("dev.flutter.pigeon.maplibre.MapLibreHostApi.toScreenLocation%s", dot_suffix); g_autoptr(FlBasicMessageChannel) to_screen_location_channel = fl_basic_message_channel_new(messenger, to_screen_location_channel_name, FL_MESSAGE_CODEC(codec)); fl_basic_message_channel_set_message_handler(to_screen_location_channel, maplibre_map_libre_host_api_to_screen_location_cb, g_object_ref(api_data), g_object_unref); @@ -858,6 +1011,9 @@ void maplibre_map_libre_host_api_clear_method_handlers(FlBinaryMessenger* messen g_autofree gchar* fly_to_channel_name = g_strdup_printf("dev.flutter.pigeon.maplibre.MapLibreHostApi.flyTo%s", dot_suffix); g_autoptr(FlBasicMessageChannel) fly_to_channel = fl_basic_message_channel_new(messenger, fly_to_channel_name, FL_MESSAGE_CODEC(codec)); fl_basic_message_channel_set_message_handler(fly_to_channel, nullptr, nullptr, nullptr); + g_autofree gchar* get_camera_channel_name = g_strdup_printf("dev.flutter.pigeon.maplibre.MapLibreHostApi.getCamera%s", dot_suffix); + g_autoptr(FlBasicMessageChannel) get_camera_channel = fl_basic_message_channel_new(messenger, get_camera_channel_name, FL_MESSAGE_CODEC(codec)); + fl_basic_message_channel_set_message_handler(get_camera_channel, nullptr, nullptr, nullptr); g_autofree gchar* to_screen_location_channel_name = g_strdup_printf("dev.flutter.pigeon.maplibre.MapLibreHostApi.toScreenLocation%s", dot_suffix); g_autoptr(FlBasicMessageChannel) to_screen_location_channel = fl_basic_message_channel_new(messenger, to_screen_location_channel_name, FL_MESSAGE_CODEC(codec)); fl_basic_message_channel_set_message_handler(to_screen_location_channel, nullptr, nullptr, nullptr); @@ -907,6 +1063,22 @@ void maplibre_map_libre_host_api_respond_error_fly_to(MaplibreMapLibreHostApiRes } } +void maplibre_map_libre_host_api_respond_get_camera(MaplibreMapLibreHostApiResponseHandle* response_handle, MaplibreMapCamera* return_value) { + g_autoptr(MaplibreMapLibreHostApiGetCameraResponse) response = maplibre_map_libre_host_api_get_camera_response_new(return_value); + g_autoptr(GError) error = nullptr; + if (!fl_basic_message_channel_respond(response_handle->channel, response_handle->response_handle, response->value, &error)) { + g_warning("Failed to send response to %s.%s: %s", "MapLibreHostApi", "getCamera", error->message); + } +} + +void maplibre_map_libre_host_api_respond_error_get_camera(MaplibreMapLibreHostApiResponseHandle* response_handle, const gchar* code, const gchar* message, FlValue* details) { + g_autoptr(MaplibreMapLibreHostApiGetCameraResponse) response = maplibre_map_libre_host_api_get_camera_response_new_error(code, message, details); + g_autoptr(GError) error = nullptr; + if (!fl_basic_message_channel_respond(response_handle->channel, response_handle->response_handle, response->value, &error)) { + g_warning("Failed to send response to %s.%s: %s", "MapLibreHostApi", "getCamera", error->message); + } +} + void maplibre_map_libre_host_api_respond_to_screen_location(MaplibreMapLibreHostApiResponseHandle* response_handle, MaplibreScreenLocation* return_value) { g_autoptr(MaplibreMapLibreHostApiToScreenLocationResponse) response = maplibre_map_libre_host_api_to_screen_location_response_new(return_value); g_autoptr(GError) error = nullptr; diff --git a/linux/pigeon.g.h b/linux/pigeon.g.h index 478311d..4bc51dd 100644 --- a/linux/pigeon.g.h +++ b/linux/pigeon.g.h @@ -105,7 +105,7 @@ gboolean maplibre_map_options_get_listens_on_long_click(MaplibreMapOptions* obje /** * MaplibreLngLat: * - * A longitude/latitude coordinate object + * A longitude/latitude coordinate object. */ G_DECLARE_FINAL_TYPE(MaplibreLngLat, maplibre_lng_lat, MAPLIBRE, LNG_LAT, GObject) @@ -144,7 +144,7 @@ double maplibre_lng_lat_get_lat(MaplibreLngLat* object); /** * MaplibreScreenLocation: * - * A pixel location / location on the device screen + * A pixel location / location on the device screen. */ G_DECLARE_FINAL_TYPE(MaplibreScreenLocation, maplibre_screen_location, MAPLIBRE, SCREEN_LOCATION, GObject) @@ -180,6 +180,67 @@ double maplibre_screen_location_get_x(MaplibreScreenLocation* object); */ double maplibre_screen_location_get_y(MaplibreScreenLocation* object); +/** + * MaplibreMapCamera: + * + * The current position of the map camera. + */ + +G_DECLARE_FINAL_TYPE(MaplibreMapCamera, maplibre_map_camera, MAPLIBRE, MAP_CAMERA, GObject) + +/** + * maplibre_map_camera_new: + * center: field in this object. + * zoom: field in this object. + * tilt: field in this object. + * bearing: field in this object. + * + * Creates a new #MapCamera object. + * + * Returns: a new #MaplibreMapCamera + */ +MaplibreMapCamera* maplibre_map_camera_new(MaplibreLngLat* center, double zoom, double tilt, double bearing); + +/** + * maplibre_map_camera_get_center + * @object: a #MaplibreMapCamera. + * + * Gets the value of the center field of @object. + * + * Returns: the field value. + */ +MaplibreLngLat* maplibre_map_camera_get_center(MaplibreMapCamera* object); + +/** + * maplibre_map_camera_get_zoom + * @object: a #MaplibreMapCamera. + * + * Gets the value of the zoom field of @object. + * + * Returns: the field value. + */ +double maplibre_map_camera_get_zoom(MaplibreMapCamera* object); + +/** + * maplibre_map_camera_get_tilt + * @object: a #MaplibreMapCamera. + * + * Gets the value of the tilt field of @object. + * + * Returns: the field value. + */ +double maplibre_map_camera_get_tilt(MaplibreMapCamera* object); + +/** + * maplibre_map_camera_get_bearing + * @object: a #MaplibreMapCamera. + * + * Gets the value of the bearing field of @object. + * + * Returns: the field value. + */ +double maplibre_map_camera_get_bearing(MaplibreMapCamera* object); + G_DECLARE_FINAL_TYPE(MaplibreMapLibreHostApiResponseHandle, maplibre_map_libre_host_api_response_handle, MAPLIBRE, MAP_LIBRE_HOST_API_RESPONSE_HANDLE, GObject) /** @@ -190,6 +251,7 @@ G_DECLARE_FINAL_TYPE(MaplibreMapLibreHostApiResponseHandle, maplibre_map_libre_h typedef struct { void (*jump_to)(MaplibreLngLat* center, double* zoom, double* bearing, double* pitch, MaplibreMapLibreHostApiResponseHandle* response_handle, gpointer user_data); void (*fly_to)(MaplibreLngLat* center, double* zoom, double* bearing, double* pitch, int64_t duration_ms, MaplibreMapLibreHostApiResponseHandle* response_handle, gpointer user_data); + void (*get_camera)(MaplibreMapLibreHostApiResponseHandle* response_handle, gpointer user_data); void (*to_screen_location)(double lng, double lat, MaplibreMapLibreHostApiResponseHandle* response_handle, gpointer user_data); void (*to_lng_lat)(double x, double y, MaplibreMapLibreHostApiResponseHandle* response_handle, gpointer user_data); void (*add_fill_layer)(const gchar* id, const gchar* source_id, MaplibreMapLibreHostApiResponseHandle* response_handle, gpointer user_data); @@ -258,6 +320,26 @@ void maplibre_map_libre_host_api_respond_fly_to(MaplibreMapLibreHostApiResponseH */ void maplibre_map_libre_host_api_respond_error_fly_to(MaplibreMapLibreHostApiResponseHandle* response_handle, const gchar* code, const gchar* message, FlValue* details); +/** + * maplibre_map_libre_host_api_respond_get_camera: + * @response_handle: a #MaplibreMapLibreHostApiResponseHandle. + * @return_value: location to write the value returned by this method. + * + * Responds to MapLibreHostApi.getCamera. + */ +void maplibre_map_libre_host_api_respond_get_camera(MaplibreMapLibreHostApiResponseHandle* response_handle, MaplibreMapCamera* return_value); + +/** + * maplibre_map_libre_host_api_respond_error_get_camera: + * @response_handle: a #MaplibreMapLibreHostApiResponseHandle. + * @code: error code. + * @message: error message. + * @details: (allow-none): error details or %NULL. + * + * Responds with an error to MapLibreHostApi.getCamera. + */ +void maplibre_map_libre_host_api_respond_error_get_camera(MaplibreMapLibreHostApiResponseHandle* response_handle, const gchar* code, const gchar* message, FlValue* details); + /** * maplibre_map_libre_host_api_respond_to_screen_location: * @response_handle: a #MaplibreMapLibreHostApiResponseHandle. diff --git a/macos/Classes/Pigeon.g.swift b/macos/Classes/Pigeon.g.swift index bf0bbaa..be6fe6d 100644 --- a/macos/Classes/Pigeon.g.swift +++ b/macos/Classes/Pigeon.g.swift @@ -122,7 +122,7 @@ struct MapOptions { } } -/// A longitude/latitude coordinate object +/// A longitude/latitude coordinate object. /// /// Generated class from Pigeon that represents data sent in messages. struct LngLat { @@ -151,7 +151,7 @@ struct LngLat { } } -/// A pixel location / location on the device screen +/// A pixel location / location on the device screen. /// /// Generated class from Pigeon that represents data sent in messages. struct ScreenLocation { @@ -180,6 +180,41 @@ struct ScreenLocation { } } +/// The current position of the map camera. +/// +/// Generated class from Pigeon that represents data sent in messages. +struct MapCamera { + var center: LngLat + var zoom: Double + var tilt: Double + var bearing: Double + + + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> MapCamera? { + let center = pigeonVar_list[0] as! LngLat + let zoom = pigeonVar_list[1] as! Double + let tilt = pigeonVar_list[2] as! Double + let bearing = pigeonVar_list[3] as! Double + + return MapCamera( + center: center, + zoom: zoom, + tilt: tilt, + bearing: bearing + ) + } + func toList() -> [Any?] { + return [ + center, + zoom, + tilt, + bearing, + ] + } +} + private class PigeonPigeonCodecReader: FlutterStandardReader { override func readValue(ofType type: UInt8) -> Any? { switch type { @@ -189,6 +224,8 @@ private class PigeonPigeonCodecReader: FlutterStandardReader { return LngLat.fromList(self.readValue() as! [Any?]) case 131: return ScreenLocation.fromList(self.readValue() as! [Any?]) + case 132: + return MapCamera.fromList(self.readValue() as! [Any?]) default: return super.readValue(ofType: type) } @@ -206,6 +243,9 @@ private class PigeonPigeonCodecWriter: FlutterStandardWriter { } else if let value = value as? ScreenLocation { super.writeByte(131) super.writeValue(value.toList()) + } else if let value = value as? MapCamera { + super.writeByte(132) + super.writeValue(value.toList()) } else { super.writeValue(value) } @@ -233,6 +273,9 @@ protocol MapLibreHostApi { func jumpTo(center: LngLat?, zoom: Double?, bearing: Double?, pitch: Double?, completion: @escaping (Result) -> Void) /// Animate the viewport of the map to a new location. func flyTo(center: LngLat?, zoom: Double?, bearing: Double?, pitch: Double?, durationMs: Int64, completion: @escaping (Result) -> Void) + /// Get the current camera position with the map center, zoom level, camera + /// tilt and map rotation. + func getCamera(completion: @escaping (Result) -> Void) /// Convert a coordinate to a location on the screen. func toScreenLocation(lng: Double, lat: Double, completion: @escaping (Result) -> Void) /// Convert a screen location to a coordinate. @@ -294,6 +337,23 @@ class MapLibreHostApiSetup { } else { flyToChannel.setMessageHandler(nil) } + /// Get the current camera position with the map center, zoom level, camera + /// tilt and map rotation. + let getCameraChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.maplibre.MapLibreHostApi.getCamera\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + getCameraChannel.setMessageHandler { _, reply in + api.getCamera { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + getCameraChannel.setMessageHandler(nil) + } /// Convert a coordinate to a location on the screen. let toScreenLocationChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.maplibre.MapLibreHostApi.toScreenLocation\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) if let api = api { diff --git a/pigeons/pigeon.dart b/pigeons/pigeon.dart index 733c1fe..91762ca 100644 --- a/pigeons/pigeon.dart +++ b/pigeons/pigeon.dart @@ -42,6 +42,11 @@ abstract interface class MapLibreHostApi { required int durationMs, }); + /// Get the current camera position with the map center, zoom level, camera + /// tilt and map rotation. + @async + MapCamera getCamera(); + /// Convert a coordinate to a location on the screen. @async ScreenLocation toScreenLocation(double lng, double lat); @@ -122,7 +127,7 @@ class MapOptions { final bool listensOnLongClick; } -/// A longitude/latitude coordinate object +/// A longitude/latitude coordinate object. class LngLat { const LngLat({required this.lng, required this.lat}); @@ -133,7 +138,7 @@ class LngLat { final double lat; } -/// A pixel location / location on the device screen +/// A pixel location / location on the device screen. class ScreenLocation { const ScreenLocation({required this.x, required this.y}); @@ -143,3 +148,18 @@ class ScreenLocation { /// The y coordinate final double y; } + +/// The current position of the map camera. +class MapCamera { + const MapCamera({ + required this.center, + required this.zoom, + required this.tilt, + required this.bearing, + }); + + final LngLat center; + final double zoom; + final double tilt; + final double bearing; +} diff --git a/windows/runner/pigeon.g.cpp b/windows/runner/pigeon.g.cpp index 2849753..7d6a4c0 100644 --- a/windows/runner/pigeon.g.cpp +++ b/windows/runner/pigeon.g.cpp @@ -257,6 +257,87 @@ ScreenLocation ScreenLocation::FromEncodableList(const EncodableList& list) { return decoded; } +// MapCamera + +MapCamera::MapCamera( + const LngLat& center, + double zoom, + double tilt, + double bearing) + : center_(std::make_unique(center)), + zoom_(zoom), + tilt_(tilt), + bearing_(bearing) {} + +MapCamera::MapCamera(const MapCamera& other) + : center_(std::make_unique(*other.center_)), + zoom_(other.zoom_), + tilt_(other.tilt_), + bearing_(other.bearing_) {} + +MapCamera& MapCamera::operator=(const MapCamera& other) { + center_ = std::make_unique(*other.center_); + zoom_ = other.zoom_; + tilt_ = other.tilt_; + bearing_ = other.bearing_; + return *this; +} + +const LngLat& MapCamera::center() const { + return *center_; +} + +void MapCamera::set_center(const LngLat& value_arg) { + center_ = std::make_unique(value_arg); +} + + +double MapCamera::zoom() const { + return zoom_; +} + +void MapCamera::set_zoom(double value_arg) { + zoom_ = value_arg; +} + + +double MapCamera::tilt() const { + return tilt_; +} + +void MapCamera::set_tilt(double value_arg) { + tilt_ = value_arg; +} + + +double MapCamera::bearing() const { + return bearing_; +} + +void MapCamera::set_bearing(double value_arg) { + bearing_ = value_arg; +} + + +EncodableList MapCamera::ToEncodableList() const { + EncodableList list; + list.reserve(4); + list.push_back(CustomEncodableValue(*center_)); + list.push_back(EncodableValue(zoom_)); + list.push_back(EncodableValue(tilt_)); + list.push_back(EncodableValue(bearing_)); + return list; +} + +MapCamera MapCamera::FromEncodableList(const EncodableList& list) { + MapCamera decoded( + std::any_cast(std::get(list[0])), + std::get(list[1]), + std::get(list[2]), + std::get(list[3])); + return decoded; +} + PigeonInternalCodecSerializer::PigeonInternalCodecSerializer() {} @@ -273,6 +354,9 @@ EncodableValue PigeonInternalCodecSerializer::ReadValueOfType( case 131: { return CustomEncodableValue(ScreenLocation::FromEncodableList(std::get(ReadValue(stream)))); } + case 132: { + return CustomEncodableValue(MapCamera::FromEncodableList(std::get(ReadValue(stream)))); + } default: return flutter::StandardCodecSerializer::ReadValueOfType(type, stream); } @@ -297,6 +381,11 @@ void PigeonInternalCodecSerializer::WriteValue( WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); return; } + if (custom_value->type() == typeid(MapCamera)) { + stream->WriteByte(132); + WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); + return; + } } flutter::StandardCodecSerializer::WriteValue(value, stream); } @@ -386,6 +475,28 @@ void MapLibreHostApi::SetUp( channel.SetMessageHandler(nullptr); } } + { + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.maplibre.MapLibreHostApi.getCamera" + prepended_suffix, &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + api->GetCamera([reply](ErrorOr&& output) { + if (output.has_error()) { + reply(WrapError(output.error())); + return; + } + EncodableList wrapped; + wrapped.push_back(CustomEncodableValue(std::move(output).TakeValue())); + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } { BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.maplibre.MapLibreHostApi.toScreenLocation" + prepended_suffix, &GetCodec()); if (api != nullptr) { diff --git a/windows/runner/pigeon.g.h b/windows/runner/pigeon.g.h index ba1abb3..26ab975 100644 --- a/windows/runner/pigeon.g.h +++ b/windows/runner/pigeon.g.h @@ -134,7 +134,7 @@ class MapOptions { }; -// A longitude/latitude coordinate object +// A longitude/latitude coordinate object. // // Generated class from Pigeon that represents data sent in messages. class LngLat { @@ -157,6 +157,7 @@ class LngLat { static LngLat FromEncodableList(const flutter::EncodableList& list); flutter::EncodableList ToEncodableList() const; friend class MapOptions; + friend class MapCamera; friend class MapLibreHostApi; friend class MapLibreFlutterApi; friend class PigeonInternalCodecSerializer; @@ -166,7 +167,7 @@ class LngLat { }; -// A pixel location / location on the device screen +// A pixel location / location on the device screen. // // Generated class from Pigeon that represents data sent in messages. class ScreenLocation { @@ -197,6 +198,50 @@ class ScreenLocation { }; +// The current position of the map camera. +// +// Generated class from Pigeon that represents data sent in messages. +class MapCamera { + public: + // Constructs an object setting all fields. + explicit MapCamera( + const LngLat& center, + double zoom, + double tilt, + double bearing); + + ~MapCamera() = default; + MapCamera(const MapCamera& other); + MapCamera& operator=(const MapCamera& other); + MapCamera(MapCamera&& other) = default; + MapCamera& operator=(MapCamera&& other) noexcept = default; + const LngLat& center() const; + void set_center(const LngLat& value_arg); + + double zoom() const; + void set_zoom(double value_arg); + + double tilt() const; + void set_tilt(double value_arg); + + double bearing() const; + void set_bearing(double value_arg); + + + private: + static MapCamera FromEncodableList(const flutter::EncodableList& list); + flutter::EncodableList ToEncodableList() const; + friend class MapLibreHostApi; + friend class MapLibreFlutterApi; + friend class PigeonInternalCodecSerializer; + std::unique_ptr center_; + double zoom_; + double tilt_; + double bearing_; + +}; + + class PigeonInternalCodecSerializer : public flutter::StandardCodecSerializer { public: PigeonInternalCodecSerializer(); @@ -237,6 +282,9 @@ class MapLibreHostApi { const double* pitch, int64_t duration_ms, std::function reply)> result) = 0; + // Get the current camera position with the map center, zoom level, camera + // tilt and map rotation. + virtual void GetCamera(std::function reply)> result) = 0; // Convert a coordinate to a location on the screen. virtual void ToScreenLocation( double lng, From 0685e144c91e9e8c2c9a746c519d369d81449129 Mon Sep 17 00:00:00 2001 From: Joscha <34318751+josxha@users.noreply.github.com> Date: Sat, 14 Sep 2024 02:18:41 +0200 Subject: [PATCH 02/37] dart format --- lib/src/native/pigeon.g.dart | 196 +++++++++++++++++++++++------------ 1 file changed, 129 insertions(+), 67 deletions(-) diff --git a/lib/src/native/pigeon.g.dart b/lib/src/native/pigeon.g.dart index 694ebb2..1c4ec6f 100644 --- a/lib/src/native/pigeon.g.dart +++ b/lib/src/native/pigeon.g.dart @@ -15,7 +15,8 @@ PlatformException _createConnectionError(String channelName) { ); } -List wrapResponse({Object? result, PlatformException? error, bool empty = false}) { +List wrapResponse( + {Object? result, PlatformException? error, bool empty = false}) { if (empty) { return []; } @@ -179,7 +180,6 @@ class MapCamera { } } - class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @override @@ -187,16 +187,16 @@ class _PigeonCodec extends StandardMessageCodec { if (value is int) { buffer.putUint8(4); buffer.putInt64(value); - } else if (value is MapOptions) { + } else if (value is MapOptions) { buffer.putUint8(129); writeValue(buffer, value.encode()); - } else if (value is LngLat) { + } else if (value is LngLat) { buffer.putUint8(130); writeValue(buffer, value.encode()); - } else if (value is ScreenLocation) { + } else if (value is ScreenLocation) { buffer.putUint8(131); writeValue(buffer, value.encode()); - } else if (value is MapCamera) { + } else if (value is MapCamera) { buffer.putUint8(132); writeValue(buffer, value.encode()); } else { @@ -207,13 +207,13 @@ class _PigeonCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 129: + case 129: return MapOptions.decode(readValue(buffer)!); - case 130: + case 130: return LngLat.decode(readValue(buffer)!); - case 131: + case 131: return ScreenLocation.decode(readValue(buffer)!); - case 132: + case 132: return MapCamera.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -225,9 +225,11 @@ class MapLibreHostApi { /// Constructor for [MapLibreHostApi]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - MapLibreHostApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + MapLibreHostApi( + {BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + pigeonVar_messageChannelSuffix = + messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; final BinaryMessenger? pigeonVar_binaryMessenger; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); @@ -235,15 +237,22 @@ class MapLibreHostApi { final String pigeonVar_messageChannelSuffix; /// Move the viewport of the map to a new location without any animation. - Future jumpTo({required LngLat? center, required double? zoom, required double? bearing, required double? pitch,}) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.maplibre.MapLibreHostApi.jumpTo$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + Future jumpTo({ + required LngLat? center, + required double? zoom, + required double? bearing, + required double? pitch, + }) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.maplibre.MapLibreHostApi.jumpTo$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([center, zoom, bearing, pitch]) as List?; + final List? pigeonVar_replyList = await pigeonVar_channel + .send([center, zoom, bearing, pitch]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -258,15 +267,24 @@ class MapLibreHostApi { } /// Animate the viewport of the map to a new location. - Future flyTo({required LngLat? center, required double? zoom, required double? bearing, required double? pitch, required int durationMs,}) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.maplibre.MapLibreHostApi.flyTo$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + Future flyTo({ + required LngLat? center, + required double? zoom, + required double? bearing, + required double? pitch, + required int durationMs, + }) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.maplibre.MapLibreHostApi.flyTo$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([center, zoom, bearing, pitch, durationMs]) as List?; + final List? pigeonVar_replyList = await pigeonVar_channel + .send([center, zoom, bearing, pitch, durationMs]) + as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -283,8 +301,10 @@ class MapLibreHostApi { /// Get the current camera position with the map center, zoom level, camera /// tilt and map rotation. Future getCamera() async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.maplibre.MapLibreHostApi.getCamera$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.maplibre.MapLibreHostApi.getCamera$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -311,8 +331,10 @@ class MapLibreHostApi { /// Convert a coordinate to a location on the screen. Future toScreenLocation(double lng, double lat) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.maplibre.MapLibreHostApi.toScreenLocation$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.maplibre.MapLibreHostApi.toScreenLocation$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -339,8 +361,10 @@ class MapLibreHostApi { /// Convert a screen location to a coordinate. Future toLngLat(double x, double y) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.maplibre.MapLibreHostApi.toLngLat$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.maplibre.MapLibreHostApi.toLngLat$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -366,9 +390,12 @@ class MapLibreHostApi { } /// Add a fill layer to the map style. - Future addFillLayer({required String id, required String sourceId}) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.maplibre.MapLibreHostApi.addFillLayer$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + Future addFillLayer( + {required String id, required String sourceId}) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.maplibre.MapLibreHostApi.addFillLayer$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -389,9 +416,12 @@ class MapLibreHostApi { } /// Add a circle layer to the map style. - Future addCircleLayer({required String id, required String sourceId}) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.maplibre.MapLibreHostApi.addCircleLayer$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + Future addCircleLayer( + {required String id, required String sourceId}) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.maplibre.MapLibreHostApi.addCircleLayer$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -412,9 +442,12 @@ class MapLibreHostApi { } /// Add a GeoJSON source to the map style. - Future addGeoJsonSource({required String id, required String data}) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.maplibre.MapLibreHostApi.addGeoJsonSource$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + Future addGeoJsonSource( + {required String id, required String data}) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.maplibre.MapLibreHostApi.addGeoJsonSource$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -457,11 +490,19 @@ abstract class MapLibreFlutterApi { /// Callback when the user performs a long lasting click on the map. void onLongClick(LngLat point); - static void setUp(MapLibreFlutterApi? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) { - messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + static void setUp( + MapLibreFlutterApi? api, { + BinaryMessenger? binaryMessenger, + String messageChannelSuffix = '', + }) { + messageChannelSuffix = + messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.maplibre.MapLibreFlutterApi.getOptions$messageChannelSuffix', pigeonChannelCodec, + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.maplibre.MapLibreFlutterApi.getOptions$messageChannelSuffix', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { pigeonVar_channel.setMessageHandler(null); @@ -472,15 +513,19 @@ abstract class MapLibreFlutterApi { return wrapResponse(result: output); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onStyleLoaded$messageChannelSuffix', pigeonChannelCodec, + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onStyleLoaded$messageChannelSuffix', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { pigeonVar_channel.setMessageHandler(null); @@ -491,22 +536,26 @@ abstract class MapLibreFlutterApi { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onClick$messageChannelSuffix', pigeonChannelCodec, + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onClick$messageChannelSuffix', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { pigeonVar_channel.setMessageHandler(null); } else { pigeonVar_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onClick was null.'); + 'Argument for dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onClick was null.'); final List args = (message as List?)!; final LngLat? arg_point = (args[0] as LngLat?); assert(arg_point != null, @@ -516,22 +565,26 @@ abstract class MapLibreFlutterApi { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onSecondaryClick$messageChannelSuffix', pigeonChannelCodec, + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onSecondaryClick$messageChannelSuffix', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { pigeonVar_channel.setMessageHandler(null); } else { pigeonVar_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onSecondaryClick was null.'); + 'Argument for dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onSecondaryClick was null.'); final List args = (message as List?)!; final LngLat? arg_point = (args[0] as LngLat?); assert(arg_point != null, @@ -541,22 +594,26 @@ abstract class MapLibreFlutterApi { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onDoubleClick$messageChannelSuffix', pigeonChannelCodec, + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onDoubleClick$messageChannelSuffix', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { pigeonVar_channel.setMessageHandler(null); } else { pigeonVar_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onDoubleClick was null.'); + 'Argument for dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onDoubleClick was null.'); final List args = (message as List?)!; final LngLat? arg_point = (args[0] as LngLat?); assert(arg_point != null, @@ -566,22 +623,26 @@ abstract class MapLibreFlutterApi { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onLongClick$messageChannelSuffix', pigeonChannelCodec, + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onLongClick$messageChannelSuffix', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { pigeonVar_channel.setMessageHandler(null); } else { pigeonVar_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onLongClick was null.'); + 'Argument for dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onLongClick was null.'); final List args = (message as List?)!; final LngLat? arg_point = (args[0] as LngLat?); assert(arg_point != null, @@ -591,8 +652,9 @@ abstract class MapLibreFlutterApi { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); } }); } From 0b7bd3e6ea22d4bffdd6a0c6757982f58ddd6f60 Mon Sep 17 00:00:00 2001 From: Joscha <34318751+josxha@users.noreply.github.com> Date: Sat, 14 Sep 2024 02:30:53 +0200 Subject: [PATCH 03/37] add tests for flyTo, jumpTo --- example/integration_test/app.dart | 6 ++-- example/integration_test/smoke_test.dart | 46 ++++++++++++++++++++---- 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/example/integration_test/app.dart b/example/integration_test/app.dart index 5b476c3..049b9eb 100644 --- a/example/integration_test/app.dart +++ b/example/integration_test/app.dart @@ -7,11 +7,13 @@ void main() { class App extends StatelessWidget { const App({ - super.key, + this.options, this.onMapCreated, this.onStyleLoaded, + super.key, }); + final MapOptions? options; final MapCreatedCallback? onMapCreated; final VoidCallback? onStyleLoaded; @@ -21,7 +23,7 @@ class App extends StatelessWidget { title: 'MapLibre Demo', home: Scaffold( body: MapLibreMap( - options: MapOptions(center: Position(0, 0)), + options: options ?? MapOptions(center: Position(0, 0)), onMapCreated: onMapCreated, onStyleLoaded: onStyleLoaded, ), diff --git a/example/integration_test/smoke_test.dart b/example/integration_test/smoke_test.dart index 90eb968..30e9501 100644 --- a/example/integration_test/smoke_test.dart +++ b/example/integration_test/smoke_test.dart @@ -11,15 +11,49 @@ void main() { testWidgets( 'render map', (tester) async { - // ignore: unused_local_variable + await tester.pumpWidget(const App()); + await tester.pumpAndSettle(); + expect(tester.allWidgets.any((w) => w is MapLibreMap), true); + }, + ); + testWidgets( + 'jumpTo', + (tester) async { late final MapController ctrl; - final app = App( - onMapCreated: (controller) => ctrl = controller, - ); + final app = App(onMapCreated: (controller) => ctrl = controller); await tester.pumpWidget(app); await tester.pumpAndSettle(); - expect(tester.allWidgets.any((w) => w is MapLibreMap), true); - // await ctrl.jumpTo(center: Position(1, 1), bearing: 1, zoom: 1, tilt: 1); + await ctrl.jumpTo(center: Position(1, 1), bearing: 1, zoom: 1, tilt: 1); + await tester.pumpAndSettle(); + final camera = await ctrl.getCamera(); + expect(camera.center, Position(1, 1)); + expect(camera.zoom, 1); + expect(camera.bearing, 1); + expect(camera.tilt, 1); + }, + ); + ; + testWidgets( + 'flyTo', + (tester) async { + late final MapController ctrl; + final app = App(onMapCreated: (controller) => ctrl = controller); + await tester.pumpWidget(app); + await tester.pumpAndSettle(); + await ctrl.flyTo( + center: Position(2, 2), + bearing: 2, + zoom: 2, + tilt: 2, + webSpeed: 100, + nativeDuration: Duration.zero, + ); + await tester.pumpAndSettle(); + final camera = await ctrl.getCamera(); + expect(camera.center, Position(2, 2)); + expect(camera.zoom, 2); + expect(camera.bearing, 2); + expect(camera.tilt, 2); }, ); }); From e6c27da891bc89ea64a493434ffe165dc2236663 Mon Sep 17 00:00:00 2001 From: Joscha <34318751+josxha@users.noreply.github.com> Date: Sat, 14 Sep 2024 02:40:18 +0200 Subject: [PATCH 04/37] Update smoke_test.dart --- example/integration_test/smoke_test.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/example/integration_test/smoke_test.dart b/example/integration_test/smoke_test.dart index 30e9501..90cfaab 100644 --- a/example/integration_test/smoke_test.dart +++ b/example/integration_test/smoke_test.dart @@ -32,7 +32,6 @@ void main() { expect(camera.tilt, 1); }, ); - ; testWidgets( 'flyTo', (tester) async { From dd0549f707a39e0426af44597069a3d0c4ca22a7 Mon Sep 17 00:00:00 2001 From: Joscha <34318751+josxha@users.noreply.github.com> Date: Sat, 14 Sep 2024 02:45:33 +0200 Subject: [PATCH 05/37] use closeTo --- example/integration_test/smoke_test.dart | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/example/integration_test/smoke_test.dart b/example/integration_test/smoke_test.dart index 90cfaab..9875b8b 100644 --- a/example/integration_test/smoke_test.dart +++ b/example/integration_test/smoke_test.dart @@ -13,7 +13,7 @@ void main() { (tester) async { await tester.pumpWidget(const App()); await tester.pumpAndSettle(); - expect(tester.allWidgets.any((w) => w is MapLibreMap), true); + expect(tester.allWidgets.any((w) => w is MapLibreMap), isTrue); }, ); testWidgets( @@ -26,10 +26,11 @@ void main() { await ctrl.jumpTo(center: Position(1, 1), bearing: 1, zoom: 1, tilt: 1); await tester.pumpAndSettle(); final camera = await ctrl.getCamera(); - expect(camera.center, Position(1, 1)); - expect(camera.zoom, 1); - expect(camera.bearing, 1); - expect(camera.tilt, 1); + expect(camera.center.lng, closeTo(1, 0.00001)); + expect(camera.center.lat, closeTo(1, 0.00001)); + expect(camera.zoom, closeTo(1, 0.00001)); + expect(camera.bearing, closeTo(1, 0.00001)); + expect(camera.tilt, closeTo(1, 0.00001)); }, ); testWidgets( @@ -49,10 +50,11 @@ void main() { ); await tester.pumpAndSettle(); final camera = await ctrl.getCamera(); - expect(camera.center, Position(2, 2)); - expect(camera.zoom, 2); - expect(camera.bearing, 2); - expect(camera.tilt, 2); + expect(camera.center.lng, closeTo(2, 0.00001)); + expect(camera.center.lat, closeTo(2, 0.00001)); + expect(camera.zoom, closeTo(2, 0.00001)); + expect(camera.bearing, closeTo(2, 0.00001)); + expect(camera.tilt, closeTo(2, 0.00001)); }, ); }); From beef12088407e0899e7bfb54b8028f7d48cd508c Mon Sep 17 00:00:00 2001 From: Joscha <34318751+josxha@users.noreply.github.com> Date: Sat, 14 Sep 2024 03:03:23 +0200 Subject: [PATCH 06/37] more tests --- example/integration_test/smoke_test.dart | 49 ++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/example/integration_test/smoke_test.dart b/example/integration_test/smoke_test.dart index 9875b8b..e04fcac 100644 --- a/example/integration_test/smoke_test.dart +++ b/example/integration_test/smoke_test.dart @@ -1,3 +1,4 @@ +import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:maplibre/maplibre.dart'; @@ -17,10 +18,13 @@ void main() { }, ); testWidgets( - 'jumpTo', + 'getCamera', (tester) async { late final MapController ctrl; - final app = App(onMapCreated: (controller) => ctrl = controller); + final app = App( + onMapCreated: (controller) => ctrl = controller, + options: MapOptions(center: Position(1, 2)), + ); await tester.pumpWidget(app); await tester.pumpAndSettle(); await ctrl.jumpTo(center: Position(1, 1), bearing: 1, zoom: 1, tilt: 1); @@ -33,6 +37,23 @@ void main() { expect(camera.tilt, closeTo(1, 0.00001)); }, ); + testWidgets( + 'jumpTo', + (tester) async { + late final MapController ctrl; + final app = App(onMapCreated: (controller) => ctrl = controller); + await tester.pumpWidget(app); + await tester.pumpAndSettle(); + await ctrl.jumpTo(center: Position(1, 2), bearing: 1, zoom: 1, tilt: 1); + await tester.pumpAndSettle(); + final camera = await ctrl.getCamera(); + expect(camera.center.lng, closeTo(1, 0.00001)); + expect(camera.center.lat, closeTo(2, 0.00001)); + expect(camera.zoom, closeTo(1, 0.00001)); + expect(camera.bearing, closeTo(1, 0.00001)); + expect(camera.tilt, closeTo(1, 0.00001)); + }, + ); testWidgets( 'flyTo', (tester) async { @@ -41,7 +62,7 @@ void main() { await tester.pumpWidget(app); await tester.pumpAndSettle(); await ctrl.flyTo( - center: Position(2, 2), + center: Position(2, 1), bearing: 2, zoom: 2, tilt: 2, @@ -51,11 +72,31 @@ void main() { await tester.pumpAndSettle(); final camera = await ctrl.getCamera(); expect(camera.center.lng, closeTo(2, 0.00001)); - expect(camera.center.lat, closeTo(2, 0.00001)); + expect(camera.center.lat, closeTo(1, 0.00001)); expect(camera.zoom, closeTo(2, 0.00001)); expect(camera.bearing, closeTo(2, 0.00001)); expect(camera.tilt, closeTo(2, 0.00001)); }, ); + testWidgets( + 'flyTo cancel', + (tester) async { + late final MapController ctrl; + final app = App(onMapCreated: (controller) => ctrl = controller); + await tester.pumpWidget(app); + await tester.pumpAndSettle(); + await expectLater( + ctrl.flyTo( + center: Position(2, 2), + bearing: 2, + zoom: 2, + tilt: 2, + webSpeed: 0.1, + nativeDuration: const Duration(seconds: 10), + ), + throwsA(isA()), + ); + }, + ); }); } From 54ca48a538c730d994820b95d7de539e3f2a8386 Mon Sep 17 00:00:00 2001 From: Joscha <34318751+josxha@users.noreply.github.com> Date: Sat, 14 Sep 2024 12:48:39 +0200 Subject: [PATCH 07/37] remove usePathUrlStrategy() --- example/lib/controller_page.dart | 3 ++- example/lib/main.dart | 2 -- example/lib/menu_page.dart | 5 ----- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/example/lib/controller_page.dart b/example/lib/controller_page.dart index 571b9f5..a16a92c 100644 --- a/example/lib/controller_page.dart +++ b/example/lib/controller_page.dart @@ -73,7 +73,8 @@ class _ControllerPageState extends State { builder: (context) => AlertDialog( title: const Text('MapCenter'), content: Text(''' -center: Position(lng: ${camera.center.lng}, lat: ${camera.center.lat}) +center.lng: ${camera.center.lng} +center.lat: ${camera.center.lat} zoom: ${camera.zoom} bearing: ${camera.bearing} tilt: ${camera.tilt}'''), diff --git a/example/lib/main.dart b/example/lib/main.dart index a2d3095..7ba170f 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter_web_plugins/url_strategy.dart'; import 'package:maplibre_example/annotations_page.dart'; import 'package:maplibre_example/callbacks_page.dart'; import 'package:maplibre_example/controller_page.dart'; @@ -10,7 +9,6 @@ import 'package:maplibre_example/two_maps_page.dart'; import 'package:maplibre_example/web_controls_page.dart'; void main() { - usePathUrlStrategy(); runApp(const MyApp()); } diff --git a/example/lib/menu_page.dart b/example/lib/menu_page.dart index bd515fa..b34a920 100644 --- a/example/lib/menu_page.dart +++ b/example/lib/menu_page.dart @@ -26,11 +26,6 @@ class MenuPage extends StatelessWidget { iconData: Icons.map, location: StyledMapPage.location, ), - ItemCard( - label: 'Kiosk Mode', - iconData: Icons.movie, - location: KioskPage.location, - ), ItemCard( label: 'Annotations', iconData: Icons.location_on, From a136965222cc373032f4a93b0b5de78ba0195567 Mon Sep 17 00:00:00 2001 From: Joscha <34318751+josxha@users.noreply.github.com> Date: Sat, 14 Sep 2024 13:41:47 +0200 Subject: [PATCH 08/37] Create CONTRIBUTING.md --- CONTRIBUTING.md | 50 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..0111249 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,50 @@ +# Contributing + +We welcome contributions to this repository. Please follow these steps if +you're interested in making contributions: + +## Preceding steps + +1. Ensure that + existing [pull requests](https://github.com/josxha/flutter-maplibre/pulls) + and [issues](https://github.com/josxha/flutter-maplibre/issues) don’t + already cover your contribution or question. +2. If your pull request will add a major or breaking change, please open a + [feature request](https://github.com/josxha/flutter-maplibre/issues/new/choose) + first to start a discussion. +3. The example app under [/example](/example) is used as a reference point. + If you have a bug and can't tell if it's related to the package, please try + to reproduce it with the example app or create some other way to reproduce + your issue. +4. Ensure that what you have planned is possible by verifying this functionality + is supported + by [maplibre-native](https://github.com/maplibre/maplibre-native) + and [maplibre-gl-js](https://github.com/maplibre/maplibre-gl-js). + +## Start contributing + +1. [Fork the repository](https://github.com/josxha/flutter-maplibre/fork) and + push your changes to a new branch to avoid conflicts in your forked + repository. +2. Along with your contribution please also adapt the example app to showcase + any new features or APIs you have developed. This allows quick testing and + gives evaluating users a better idea about the functionality of the package. +3. Consider updating + the [documentation](https://github.com/josxha/flutter-maplibre/tree/main/docs/docs) + to explain users how to implement. +4. If there are any changes that developers should be aware of, please update + the [CHANGELOG.md](https://github.com/josxha/flutter-maplibre/blob/main/CHANGELOG.md) + file together with your pull request. +5. If you have a pull request that isn't complete yet and want to get + feedback, consider + to [open a draft pull request](https://github.com/josxha/flutter-maplibre/pulls). + This helps others to get + involved into the changes more early and allows to link the pull request to + open issues. Give a quick summary about your changes listing any related + issues that exist. Screenshots and videos are or course welcome, too. + Use a [conventional](https://www.conventionalcommits.org/) title if you like. +6. When your contribution is ready to review, disable the draft state of your + pull request and update the summary by editing your initial pull request + message. This summary will go into the commit details of the squashed commit. + +Thanks for every contribution you make to the project. \ No newline at end of file From ea100a48f44e92be9affb8a85d252bf90a8b5d93 Mon Sep 17 00:00:00 2001 From: Joscha <34318751+josxha@users.noreply.github.com> Date: Sat, 14 Sep 2024 13:46:29 +0200 Subject: [PATCH 09/37] Create CODE_OF_CONDUCT.md --- CODE_OF_CONDUCT.md | 134 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..dc4f8f4 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,134 @@ +# Contributor Covenant Code of Conduct + +![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg) + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official email address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +[info@joscha-eckert.de](mailto:info@joscha-eckert.de). +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations From ba9df8406d9c6ed937db32737312275427ae3733 Mon Sep 17 00:00:00 2001 From: Joscha <34318751+josxha@users.noreply.github.com> Date: Sat, 14 Sep 2024 13:56:18 +0200 Subject: [PATCH 10/37] Create SECURITY.md --- SECURITY.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..6c81afa --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,30 @@ +# Security Policy + +## Acceptable Use + +We generally invite security researchers to search for vulnerabilities +in our services. We kindly ask to not put any actual user data or +production systems at risk. + +## Reporting Vulnerabilities + +Report vulnerabilities via e-mail to . We do not +offer a GPG key for encryption. + +Please make sure that you include the following information: + +- Which version is affected +- How can the bug be used/exploited +- Explanation of the risk + +If you have not received an answer within a couple of days, feel free +to contact us again. + +For used open source software, we recommend to file bug reports and/or +pull requests against the upstream repositories. This includes hardening +instructions in the installation documentation. + +## About this Policy + +This policy is based on the MIT licensed security policy of +[digitalfabrik/security-policy](https://github.com/digitalfabrik/security-policy). \ No newline at end of file From 5e658a8621432bb0529f7e53133065e32c367fc0 Mon Sep 17 00:00:00 2001 From: Joscha <34318751+josxha@users.noreply.github.com> Date: Sat, 14 Sep 2024 14:01:21 +0200 Subject: [PATCH 11/37] add funding --- example/lib/menu_page.dart | 1 - pubspec.yaml | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/example/lib/menu_page.dart b/example/lib/menu_page.dart index b34a920..5cc0890 100644 --- a/example/lib/menu_page.dart +++ b/example/lib/menu_page.dart @@ -3,7 +3,6 @@ import 'package:flutter/material.dart'; import 'package:maplibre_example/annotations_page.dart'; import 'package:maplibre_example/callbacks_page.dart'; import 'package:maplibre_example/controller_page.dart'; -import 'package:maplibre_example/kiosk_page.dart'; import 'package:maplibre_example/styled_map_page.dart'; import 'package:maplibre_example/two_maps_page.dart'; import 'package:maplibre_example/web_controls_page.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index 231dbf5..88d8baa 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -8,6 +8,9 @@ documentation: https://flutter-maplibre.pages.dev/docs topics: [ map, maplibre, mvt ] +funding: + - https://www.paypal.com/paypalme/joschaeckert + platforms: android: web: From 59f755788e64f2f8195e10b63ec7bb7fbe3a7ca1 Mon Sep 17 00:00:00 2001 From: Joscha <34318751+josxha@users.noreply.github.com> Date: Sat, 14 Sep 2024 14:32:16 +0200 Subject: [PATCH 12/37] add publish ci --- .github/workflows/publish.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .github/workflows/publish.yml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..a62a26c --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,12 @@ +name: Publish to pub.dev + +on: + push: + tags: + - 'v[0-9]+.[0-9]+.[0-9]+*' + +jobs: + publish: + permissions: + id-token: write # Required for authentication using OIDC + uses: dart-lang/setup-dart/.github/workflows/publish.yml@v1 From 588c3c84fa2f4b414a4c1662fe3da23c63162807 Mon Sep 17 00:00:00 2001 From: Joscha <34318751+josxha@users.noreply.github.com> Date: Sat, 14 Sep 2024 15:03:32 +0200 Subject: [PATCH 13/37] Update setup-android.md --- docs/docs/getting-started/setup-android.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/docs/getting-started/setup-android.md b/docs/docs/getting-started/setup-android.md index d4ab1d9..d30369f 100644 --- a/docs/docs/getting-started/setup-android.md +++ b/docs/docs/getting-started/setup-android.md @@ -41,10 +41,10 @@ buildscript { The `maplibre` package on Flutter uses [platform views](https://docs.flutter.dev/platform-integration/android/platform-views) -to display the native map. This requires an Android SDK version of at least 23 +to display the native map. This requires an Android SDK version of at least 23 (Android 6.0). -Open your `android/app/build.gradle` file and ensure that it is set to 23 or +Open your `android/app/build.gradle` file and ensure that it is set to 23 or higher. ```gradle title="android/app/build.gradle" @@ -61,15 +61,17 @@ android { ## Set the permissions If you want to show the user's location on the map you need to add -the `ACCESS_COARSE_LOCATION` or `ACCESS_FINE_LOCATION` permission in the -application manifest `android/app/src/main/AndroidManifest.xml`. +the permissions in the application +manifest`android/app/src/main/AndroidManifest.xml`. ```xml title="android/app/src/main/AndroidManifest.xml" // highlight-start - + + + // highlight-end ``` From 0d6671ab01c00f849c730f372652986f4f6d0734 Mon Sep 17 00:00:00 2001 From: Joscha <34318751+josxha@users.noreply.github.com> Date: Sun, 15 Sep 2024 17:44:04 +0200 Subject: [PATCH 14/37] add android location permissions --- docs/docs/getting-started/setup-android.md | 2 +- example/android/app/src/main/AndroidManifest.xml | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/docs/getting-started/setup-android.md b/docs/docs/getting-started/setup-android.md index d30369f..e49fe4e 100644 --- a/docs/docs/getting-started/setup-android.md +++ b/docs/docs/getting-started/setup-android.md @@ -61,7 +61,7 @@ android { ## Set the permissions If you want to show the user's location on the map you need to add -the permissions in the application +the permissions in the application manifest`android/app/src/main/AndroidManifest.xml`. ```xml title="android/app/src/main/AndroidManifest.xml" diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index 10fed1e..11f57b8 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -42,4 +42,7 @@ + + + From ca0552e60f62c68055ca2a8665a225dd83f9b411 Mon Sep 17 00:00:00 2001 From: Joscha <34318751+josxha@users.noreply.github.com> Date: Sun, 15 Sep 2024 20:31:15 +0200 Subject: [PATCH 15/37] Update CHANGELOG.md --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d04967..d6684d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,7 @@ ## 0.0.1+1 -- fix urls to website -- fix urls of screenshots +- fix urls to website and embedded screenshots - remove unused `plugin_platform_interface` dependency ## 0.0.1 From a0636205eca9cd8cab2d2d732f36520a9712fed4 Mon Sep 17 00:00:00 2001 From: Joscha <34318751+josxha@users.noreply.github.com> Date: Sun, 15 Sep 2024 21:05:50 +0200 Subject: [PATCH 16/37] update docs --- docs/docs/features/supported-features.md | 70 ++++++++++++++-------- docs/docs/getting-started/setup-android.md | 10 ++++ 2 files changed, 56 insertions(+), 24 deletions(-) diff --git a/docs/docs/features/supported-features.md b/docs/docs/features/supported-features.md index 8c6f020..2c472b0 100644 --- a/docs/docs/features/supported-features.md +++ b/docs/docs/features/supported-features.md @@ -5,37 +5,59 @@ sidebar_position: 1 # Supported Features This is a broad orientation about what functionality could be added. The list -is orientated on MapLibre GL JS and the flutter-maplibre-gl map controller. -Some controller methods will be changed to provide a different annotation API. - -| Feature | web | android | iOS | windows | macOS | linux | -|------------------------------------------|-----|---------|-----|---------|-------|-------| -| Map | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | -| MapController | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | -| maplibre-gl-js Web Controls | ✅ | ➖ | ➖ | ➖ | ➖ | ➖ | -| Offline | ➖ | ❌ | ❌ | ➖ | ➖ | ➖ | -| click callback | ✅ | ❌ | ❌ | ➖ | ➖ | ➖ | -| long click callback | ❌ | ❌ | ❌ | ➖ | ➖ | ➖ | -| secondary click callback | ✅ | ❌ | ❌ | ➖ | ➖ | ➖ | -| controller.jumpTo() | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | -| controller.flyTo() | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | -| controller.addSource() | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | -| controller.addLayer() | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | -| controller.setMyLocationTrackingMode() | ❌ | ❌ | ❌ | ➖ | ➖ | ➖ | -| controller.setMapLanguage() | ❌ | ❌ | ❌ | ➖ | ➖ | ➖ | -| controller.toScreenLocation() | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | -| controller.toLatLng() | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | -| controller.getMetersPerPixelAtLatitude() | ❌ | ❌ | ❌ | ➖ | ➖ | ➖ | - -#### Legend +is orientated on MapLibre GL JS and the flutter-maplibre-gl map + me controller methods will be changed to provide a different annotation API. + +### Legend - ✅ implemented - ❌ not (yet) implemented - ➖ not supported +### Other Platforms + +iOS support is planned. However, there is currently no ETA. + Support for windows, macOS and linux is currently not possible because of the lack of platform views of these platforms. - Windows: https://github.com/flutter/flutter/issues/31713 - MacOS: https://github.com/flutter/flutter/issues/41722 -- Linux: https://github.com/flutter/flutter/issues/41724 \ No newline at end of file +- Linux: https://github.com/flutter/flutter/issues/41724 + +### General Functionality + +| Feature | web | android | iOS | windows | macOS | linux | +|---------------------|-----|---------|-----|---------|-------|-------| +| Map | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | +| MapController | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | +| UI Controls for web | ✅ | ➖ | ➖ | ➖ | ➖ | ➖ | +| Offline | ➖ | ❌ | ❌ | ➖ | ➖ | ➖ | +| Events | ❌ | ❌ | ❌ | ➖ | ➖ | ➖ | +| Snapshotter | ❌ | ❌ | ❌ | ➖ | ➖ | ➖ | +| Annotations | ❌ | ❌ | ❌ | ➖ | ➖ | ➖ | + +### Gestures and other Callbacks + +| Feature | web | android | iOS | windows | macOS | linux | +|------------------|-----|---------|-----|---------|-------|-------| +| onMapCreated | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | +| onStyleLoaded | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | +| onClick | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | +| onDoubleClick | ✅ | ❌ | ❌ | ➖ | ➖ | ➖ | +| onSecondaryClick | ✅ | ❌ | ❌ | ➖ | ➖ | ➖ | +| onLongClick | ❌ | ✅ | ❌ | ➖ | ➖ | ➖ | + +### Map Controller + +| Feature | web | android | iOS | windows | macOS | linux | +|------------------------------|-----|---------|-----|---------|-------|-------| +| jumpTo | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | +| flyTo | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | +| addSource | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | +| addLayer | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | +| setMyLocationTrackingMode | ❌ | ❌ | ❌ | ➖ | ➖ | ➖ | +| setMapLanguage | ❌ | ❌ | ❌ | ➖ | ➖ | ➖ | +| toScreenLocation | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | +| toLatLng | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | +| getMetersPerPixelAtLatitude | ❌ | ❌ | ❌ | ➖ | ➖ | ➖ | \ No newline at end of file diff --git a/docs/docs/getting-started/setup-android.md b/docs/docs/getting-started/setup-android.md index e49fe4e..ddaf74d 100644 --- a/docs/docs/getting-started/setup-android.md +++ b/docs/docs/getting-started/setup-android.md @@ -44,6 +44,16 @@ uses [platform views](https://docs.flutter.dev/platform-integration/android/plat to display the native map. This requires an Android SDK version of at least 23 (Android 6.0). +:::tip + +The app compiles with a minSdk version of down to 21. However, the MapLibre map +won't be visible on these Android versions. You can check for the SDK version +with [device_info_plus](https://pub.dev/packages/device_info_plus) +and render an alternative map as a workaround or limit the supported Android +versions to Android 6.0 and onwards like demonstrated here. + +::: + Open your `android/app/build.gradle` file and ensure that it is set to 23 or higher. From ab2d4d3c7bdd43de286cddc31dbf1dc1e0a4abb8 Mon Sep 17 00:00:00 2001 From: Joscha <34318751+josxha@users.noreply.github.com> Date: Sun, 15 Sep 2024 21:06:52 +0200 Subject: [PATCH 17/37] bump maplibre-native to 11.4.0 --- android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/build.gradle b/android/build.gradle index 77acfbd..7241f08 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -50,7 +50,7 @@ android { } dependencies { - implementation 'org.maplibre.gl:android-sdk:11.3.0' + implementation 'org.maplibre.gl:android-sdk:11.4.0' // implementation 'org.maplibre.gl:android-plugin-annotation-v9:3.0.0' // implementation 'org.maplibre.gl:android-plugin-offline-v9:3.0.0' // implementation 'com.squareup.okhttp3:okhttp:4.12.0' From 137dee5ebc2f725e48e5ada5208b8091e6c723b6 Mon Sep 17 00:00:00 2001 From: Joscha <34318751+josxha@users.noreply.github.com> Date: Sun, 15 Sep 2024 21:17:00 +0200 Subject: [PATCH 18/37] Update supported-features.md --- docs/docs/features/supported-features.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/docs/features/supported-features.md b/docs/docs/features/supported-features.md index 2c472b0..f86c37a 100644 --- a/docs/docs/features/supported-features.md +++ b/docs/docs/features/supported-features.md @@ -4,9 +4,8 @@ sidebar_position: 1 # Supported Features -This is a broad orientation about what functionality could be added. The list -is orientated on MapLibre GL JS and the flutter-maplibre-gl map - me controller methods will be changed to provide a different annotation API. +This side provides a broad orientation about what functionality could and what +functionality is already added. ### Legend From c868f170ab90972ef1fbc2133ecf283beee46a6e Mon Sep 17 00:00:00 2001 From: Joscha <34318751+josxha@users.noreply.github.com> Date: Sun, 15 Sep 2024 21:56:35 +0200 Subject: [PATCH 19/37] add getMetersPerPixelAtLatitude() --- .../josxha/maplibre/MapLibreMapController.kt | 11 ++- .../com/github/josxha/maplibre/Pigeon.g.kt | 24 ++++- docs/docs/features/supported-features.md | 2 +- example/integration_test/smoke_test.dart | 13 +++ example/lib/controller_page.dart | 23 ++++- ios/Classes/Pigeon.g.swift | 22 ++++- lib/maplibre.dart | 1 + lib/src/map_controller.dart | 8 ++ lib/src/native/pigeon.g.dart | 33 ++++++- lib/src/native/widget_state.dart | 4 + lib/src/utils.dart | 7 ++ lib/src/web/widget_state.dart | 98 +++++++++++-------- linux/pigeon.g.cc | 66 ++++++++++++- linux/pigeon.g.h | 26 ++++- macos/Classes/Pigeon.g.swift | 22 ++++- pigeons/pigeon.dart | 4 + windows/runner/pigeon.g.cpp | 30 +++++- windows/runner/pigeon.g.h | 5 +- 18 files changed, 347 insertions(+), 52 deletions(-) create mode 100644 lib/src/utils.dart diff --git a/android/src/main/kotlin/com/github/josxha/maplibre/MapLibreMapController.kt b/android/src/main/kotlin/com/github/josxha/maplibre/MapLibreMapController.kt index a2a1e40..c7bb422 100644 --- a/android/src/main/kotlin/com/github/josxha/maplibre/MapLibreMapController.kt +++ b/android/src/main/kotlin/com/github/josxha/maplibre/MapLibreMapController.kt @@ -78,8 +78,12 @@ class MapLibreMapController( override fun onMapReady(mapLibreMap: MapLibreMap) { this.mapLibreMap = mapLibreMap - this.mapLibreMap.addOnMapClickListener(this) - this.mapLibreMap.addOnMapLongClickListener(this) + if (initialOptions.listensOnClick) { + this.mapLibreMap.addOnMapClickListener(this) + } + if (initialOptions.listensOnLongClick) { + this.mapLibreMap.addOnMapLongClickListener(this) + } val style = Style.Builder().fromUri(initialOptions.style) mapLibreMap.setStyle(style) { loadedStyle -> this.style = loadedStyle @@ -178,6 +182,9 @@ class MapLibreMapController( callback(Result.success(Unit)) } + override fun getMetersPerPixelAtLatitude(latitude: Double): Double = + mapLibreMap.projection.getMetersPerPixelAtLatitude(latitude) + override fun onMapClick(point: LatLng): Boolean { flutterApi.onClick(LngLat(point.longitude, point.latitude)) { } return true diff --git a/android/src/main/kotlin/com/github/josxha/maplibre/Pigeon.g.kt b/android/src/main/kotlin/com/github/josxha/maplibre/Pigeon.g.kt index c455ab5..4732201 100644 --- a/android/src/main/kotlin/com/github/josxha/maplibre/Pigeon.g.kt +++ b/android/src/main/kotlin/com/github/josxha/maplibre/Pigeon.g.kt @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v22.3.0), do not edit directly. +// Autogenerated from Pigeon (v22.4.0), do not edit directly. // See also: https://pub.dev/packages/pigeon @file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") @@ -248,6 +248,11 @@ interface MapLibreHostApi { fun addCircleLayer(id: String, sourceId: String, callback: (Result) -> Unit) /** Add a GeoJSON source to the map style. */ fun addGeoJsonSource(id: String, data: String, callback: (Result) -> Unit) + /** + * Returns the distance spanned by one pixel at the specified latitude and + * current zoom level. + */ + fun getMetersPerPixelAtLatitude(latitude: Double): Double companion object { /** The codec used by MapLibreHostApi. */ @@ -423,6 +428,23 @@ interface MapLibreHostApi { channel.setMessageHandler(null) } } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.maplibre.MapLibreHostApi.getMetersPerPixelAtLatitude$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val latitudeArg = args[0] as Double + val wrapped: List = try { + listOf(api.getMetersPerPixelAtLatitude(latitudeArg)) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } } } } diff --git a/docs/docs/features/supported-features.md b/docs/docs/features/supported-features.md index f86c37a..1285eb3 100644 --- a/docs/docs/features/supported-features.md +++ b/docs/docs/features/supported-features.md @@ -59,4 +59,4 @@ lack of platform views of these platforms. | setMapLanguage | ❌ | ❌ | ❌ | ➖ | ➖ | ➖ | | toScreenLocation | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | | toLatLng | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | -| getMetersPerPixelAtLatitude | ❌ | ❌ | ❌ | ➖ | ➖ | ➖ | \ No newline at end of file +| getMetersPerPixelAtLatitude | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | \ No newline at end of file diff --git a/example/integration_test/smoke_test.dart b/example/integration_test/smoke_test.dart index e04fcac..5387c29 100644 --- a/example/integration_test/smoke_test.dart +++ b/example/integration_test/smoke_test.dart @@ -98,5 +98,18 @@ void main() { ); }, ); + testWidgets( + 'getMetersPerPixelAtLatitude', + (tester) async { + late final MapController ctrl; + final app = App(onMapCreated: (controller) => ctrl = controller); + await tester.pumpWidget(app); + await tester.pumpAndSettle(); + await expectLater( + ctrl.getMetersPerPixelAtLatitude(23), + closeTo(12345, 0.00001), // TODO adjust value + ); + }, + ); }); } diff --git a/example/lib/controller_page.dart b/example/lib/controller_page.dart index a16a92c..1b38383 100644 --- a/example/lib/controller_page.dart +++ b/example/lib/controller_page.dart @@ -88,7 +88,28 @@ tilt: ${camera.tilt}'''), ); } }, - child: const Text('Get MapCamera'), + child: const Text('Current MapCamera'), + ), + OutlinedButton( + onPressed: () async { + final camera = await _controller.getCamera(); + final lat = camera.center.lat.toDouble(); + final meters = + await _controller.getMetersPerPixelAtLatitude(lat); + debugPrint('latitude: $lat: $meters m/px'); + if (context.mounted) { + ScaffoldMessenger.of(context) + ..hideCurrentSnackBar() + ..showSnackBar( + SnackBar( + content: Text( + 'latitude: $lat: $meters m/px', + ), + ), + ); + } + }, + child: const Text('meter/pixel at center'), ), ], ), diff --git a/ios/Classes/Pigeon.g.swift b/ios/Classes/Pigeon.g.swift index be6fe6d..0106cd1 100644 --- a/ios/Classes/Pigeon.g.swift +++ b/ios/Classes/Pigeon.g.swift @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v22.3.0), do not edit directly. +// Autogenerated from Pigeon (v22.4.0), do not edit directly. // See also: https://pub.dev/packages/pigeon import Foundation @@ -286,6 +286,9 @@ protocol MapLibreHostApi { func addCircleLayer(id: String, sourceId: String, completion: @escaping (Result) -> Void) /// Add a GeoJSON source to the map style. func addGeoJsonSource(id: String, data: String, completion: @escaping (Result) -> Void) + /// Returns the distance spanned by one pixel at the specified latitude and + /// current zoom level. + func getMetersPerPixelAtLatitude(latitude: Double) throws -> Double } /// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. @@ -449,6 +452,23 @@ class MapLibreHostApiSetup { } else { addGeoJsonSourceChannel.setMessageHandler(nil) } + /// Returns the distance spanned by one pixel at the specified latitude and + /// current zoom level. + let getMetersPerPixelAtLatitudeChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.maplibre.MapLibreHostApi.getMetersPerPixelAtLatitude\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + getMetersPerPixelAtLatitudeChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let latitudeArg = args[0] as! Double + do { + let result = try api.getMetersPerPixelAtLatitude(latitude: latitudeArg) + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + getMetersPerPixelAtLatitudeChannel.setMessageHandler(nil) + } } } /// Generated protocol from Pigeon that represents Flutter messages that can be called from Swift. diff --git a/lib/maplibre.dart b/lib/maplibre.dart index b8f9cda..75e07a2 100644 --- a/lib/maplibre.dart +++ b/lib/maplibre.dart @@ -9,3 +9,4 @@ export 'src/map_camera.dart'; export 'src/map_controller.dart'; export 'src/map_options.dart'; export 'src/sources.dart'; +export 'src/utils.dart'; diff --git a/lib/src/map_controller.dart b/lib/src/map_controller.dart index bc31f00..e5894ce 100644 --- a/lib/src/map_controller.dart +++ b/lib/src/map_controller.dart @@ -43,4 +43,12 @@ abstract interface class MapController { /// Get the current camera position on the map. Future getCamera(); + + /// Returns the distance spanned by one pixel at the specified latitude and + /// current zoom level. + /// + /// The distance between pixels decreases as the latitude approaches the + /// poles. This relationship parallels the relationship between longitudinal + /// coordinates at different latitudes. + Future getMetersPerPixelAtLatitude(double latitude); } diff --git a/lib/src/native/pigeon.g.dart b/lib/src/native/pigeon.g.dart index 1c4ec6f..19b6764 100644 --- a/lib/src/native/pigeon.g.dart +++ b/lib/src/native/pigeon.g.dart @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v22.3.0), do not edit directly. +// Autogenerated from Pigeon (v22.4.0), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers @@ -466,6 +466,37 @@ class MapLibreHostApi { return; } } + + /// Returns the distance spanned by one pixel at the specified latitude and + /// current zoom level. + Future getMetersPerPixelAtLatitude(double latitude) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.maplibre.MapLibreHostApi.getMetersPerPixelAtLatitude$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = + await pigeonVar_channel.send([latitude]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as double?)!; + } + } } abstract class MapLibreFlutterApi { diff --git a/lib/src/native/widget_state.dart b/lib/src/native/widget_state.dart index be0a1a4..8f5a67d 100644 --- a/lib/src/native/widget_state.dart +++ b/lib/src/native/widget_state.dart @@ -158,4 +158,8 @@ final class MapLibreMapStateNative extends State bearing: camera.bearing, ); } + + @override + Future getMetersPerPixelAtLatitude(double latitude) async => + _hostApi.getMetersPerPixelAtLatitude(latitude); } diff --git a/lib/src/utils.dart b/lib/src/utils.dart new file mode 100644 index 0000000..7d28db0 --- /dev/null +++ b/lib/src/utils.dart @@ -0,0 +1,7 @@ +import 'dart:math'; + +/// pre compiled factor to convert a coordinate in radian to degrees. +const radian2degree = pi / 180; + +/// circumference of the Earth +const circumferenceOfEarth = 40075016.686; diff --git a/lib/src/web/widget_state.dart b/lib/src/web/widget_state.dart index cc579a9..7a3ce86 100644 --- a/lib/src/web/widget_state.dart +++ b/lib/src/web/widget_state.dart @@ -1,4 +1,5 @@ import 'dart:js_interop'; +import 'dart:math'; import 'dart:ui_web'; import 'package:flutter/widgets.dart'; @@ -22,7 +23,7 @@ final class MapLibreMapStateWeb extends State void initState() { platformViewRegistry.registerViewFactory( _viewName, - (int viewId, [dynamic params]) { + (int viewId, [dynamic params]) { _htmlElement = HTMLDivElement() ..style.padding = '0' ..style.margin = '0' @@ -49,45 +50,52 @@ final class MapLibreMapStateWeb extends State // add controls for (final control in _options.controls) { final jsControl = switch (control) { - final ScaleControl control => interop.ScaleControl( - interop.ScaleControlOptions( - maxWidth: control.maxWidth, - unit: control.unit.name, + final ScaleControl control => + interop.ScaleControl( + interop.ScaleControlOptions( + maxWidth: control.maxWidth, + unit: control.unit.name, + ), + ), + final GeolocateControl control => + interop.GeolocateControl( + interop.GeolocateControlOptions( + positionOptions: interop.PositionOptions( + enableHighAccuracy: + control.positionOptions.enableHighAccuracy, + maximumAge: + control.positionOptions.maximumAge.inMilliseconds, + timeout: control.positionOptions.timeout.inMilliseconds, + ), + ), ), - ), - final GeolocateControl control => interop.GeolocateControl( - interop.GeolocateControlOptions( - positionOptions: interop.PositionOptions( - enableHighAccuracy: - control.positionOptions.enableHighAccuracy, - maximumAge: - control.positionOptions.maximumAge.inMilliseconds, - timeout: control.positionOptions.timeout.inMilliseconds, + final AttributionControl control => + interop.AttributionControl( + interop.AttributionControlOptions( + compact: control.compact, + customAttribution: control.customAttribution, ), ), - ), - final AttributionControl control => interop.AttributionControl( - interop.AttributionControlOptions( - compact: control.compact, - customAttribution: control.customAttribution, + final FullscreenControl _ => + interop.FullscreenControl( + interop.FullscreenControlOptions(), ), - ), - final FullscreenControl _ => interop.FullscreenControl( - interop.FullscreenControlOptions(), - ), - final LogoControl control => interop.LogoControl( - interop.LogoControlOptions(compact: control.compact), - ), - final NavigationControl control => interop.NavigationControl( - interop.NavigationControlOptions( - showCompass: control.showCompass, - showZoom: control.showZoom, - visualizePitch: control.visualizePitch, + final LogoControl control => + interop.LogoControl( + interop.LogoControlOptions(compact: control.compact), + ), + final NavigationControl control => + interop.NavigationControl( + interop.NavigationControlOptions( + showCompass: control.showCompass, + showZoom: control.showZoom, + visualizePitch: control.visualizePitch, + ), + ), + final TerrainControl control => + interop.TerrainControl( + interop.TerrainControlOptions(source: control.source), ), - ), - final TerrainControl control => interop.TerrainControl( - interop.TerrainControlOptions(source: control.source), - ), }; _map.addControl(jsControl); } @@ -95,7 +103,7 @@ final class MapLibreMapStateWeb extends State if (widget.onStyleLoaded case final VoidCallback callback) { _map.on( interop.MapEventType.load, - (interop.MapMouseEvent event) { + (interop.MapMouseEvent event) { callback(); }.toJS, ); @@ -103,7 +111,7 @@ final class MapLibreMapStateWeb extends State if (_options.onClick case final OnClickCallback callback) { _map.on( interop.MapEventType.click, - (interop.MapMouseEvent event) { + (interop.MapMouseEvent event) { callback(event.lngLat.toPosition()); }.toJS, ); @@ -111,7 +119,7 @@ final class MapLibreMapStateWeb extends State if (_options.onDoubleClick case final OnClickCallback callback) { _map.on( interop.MapEventType.dblclick, - (interop.MapMouseEvent event) { + (interop.MapMouseEvent event) { callback(event.lngLat.toPosition()); }.toJS, ); @@ -119,7 +127,7 @@ final class MapLibreMapStateWeb extends State if (_options.onSecondaryClick case final OnClickCallback callback) { _map.on( interop.MapEventType.contextmenu, - (interop.MapMouseEvent event) { + (interop.MapMouseEvent event) { callback(event.lngLat.toPosition()); }.toJS, ); @@ -253,10 +261,20 @@ final class MapLibreMapStateWeb extends State } @override - Future getCamera() async => MapCamera( + Future getCamera() async => + MapCamera( center: _map.getCenter().toPosition(), zoom: _map.getZoom().toDouble(), tilt: _map.getPitch().toDouble(), bearing: _map.getBearing().toDouble(), ); + + @override + Future getMetersPerPixelAtLatitude(double latitude) async { + final zoom = _map.getZoom(); + // https://wiki.openstreetmap.org/wiki/Zoom_levels + return circumferenceOfEarth * + cos(latitude * radian2degree) / + pow(2, zoom + 9); + } } diff --git a/linux/pigeon.g.cc b/linux/pigeon.g.cc index 58943ac..5f95cd4 100644 --- a/linux/pigeon.g.cc +++ b/linux/pigeon.g.cc @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v22.3.0), do not edit directly. +// Autogenerated from Pigeon (v22.4.0), do not edit directly. // See also: https://pub.dev/packages/pigeon #include "pigeon.g.h" @@ -778,6 +778,43 @@ static MaplibreMapLibreHostApiAddGeoJsonSourceResponse* maplibre_map_libre_host_ return self; } +struct _MaplibreMapLibreHostApiGetMetersPerPixelAtLatitudeResponse { + GObject parent_instance; + + FlValue* value; +}; + +G_DEFINE_TYPE(MaplibreMapLibreHostApiGetMetersPerPixelAtLatitudeResponse, maplibre_map_libre_host_api_get_meters_per_pixel_at_latitude_response, G_TYPE_OBJECT) + +static void maplibre_map_libre_host_api_get_meters_per_pixel_at_latitude_response_dispose(GObject* object) { + MaplibreMapLibreHostApiGetMetersPerPixelAtLatitudeResponse* self = MAPLIBRE_MAP_LIBRE_HOST_API_GET_METERS_PER_PIXEL_AT_LATITUDE_RESPONSE(object); + g_clear_pointer(&self->value, fl_value_unref); + G_OBJECT_CLASS(maplibre_map_libre_host_api_get_meters_per_pixel_at_latitude_response_parent_class)->dispose(object); +} + +static void maplibre_map_libre_host_api_get_meters_per_pixel_at_latitude_response_init(MaplibreMapLibreHostApiGetMetersPerPixelAtLatitudeResponse* self) { +} + +static void maplibre_map_libre_host_api_get_meters_per_pixel_at_latitude_response_class_init(MaplibreMapLibreHostApiGetMetersPerPixelAtLatitudeResponseClass* klass) { + G_OBJECT_CLASS(klass)->dispose = maplibre_map_libre_host_api_get_meters_per_pixel_at_latitude_response_dispose; +} + +MaplibreMapLibreHostApiGetMetersPerPixelAtLatitudeResponse* maplibre_map_libre_host_api_get_meters_per_pixel_at_latitude_response_new(double return_value) { + MaplibreMapLibreHostApiGetMetersPerPixelAtLatitudeResponse* self = MAPLIBRE_MAP_LIBRE_HOST_API_GET_METERS_PER_PIXEL_AT_LATITUDE_RESPONSE(g_object_new(maplibre_map_libre_host_api_get_meters_per_pixel_at_latitude_response_get_type(), nullptr)); + self->value = fl_value_new_list(); + fl_value_append_take(self->value, fl_value_new_float(return_value)); + return self; +} + +MaplibreMapLibreHostApiGetMetersPerPixelAtLatitudeResponse* maplibre_map_libre_host_api_get_meters_per_pixel_at_latitude_response_new_error(const gchar* code, const gchar* message, FlValue* details) { + MaplibreMapLibreHostApiGetMetersPerPixelAtLatitudeResponse* self = MAPLIBRE_MAP_LIBRE_HOST_API_GET_METERS_PER_PIXEL_AT_LATITUDE_RESPONSE(g_object_new(maplibre_map_libre_host_api_get_meters_per_pixel_at_latitude_response_get_type(), nullptr)); + self->value = fl_value_new_list(); + fl_value_append_take(self->value, fl_value_new_string(code)); + fl_value_append_take(self->value, fl_value_new_string(message != nullptr ? message : "")); + fl_value_append_take(self->value, details != nullptr ? fl_value_ref(details) : fl_value_new_null()); + return self; +} + G_DECLARE_FINAL_TYPE(MaplibreMapLibreHostApi, maplibre_map_libre_host_api, MAPLIBRE, MAP_LIBRE_HOST_API, GObject) struct _MaplibreMapLibreHostApi { @@ -970,6 +1007,27 @@ static void maplibre_map_libre_host_api_add_geo_json_source_cb(FlBasicMessageCha self->vtable->add_geo_json_source(id, data, handle, self->user_data); } +static void maplibre_map_libre_host_api_get_meters_per_pixel_at_latitude_cb(FlBasicMessageChannel* channel, FlValue* message_, FlBasicMessageChannelResponseHandle* response_handle, gpointer user_data) { + MaplibreMapLibreHostApi* self = MAPLIBRE_MAP_LIBRE_HOST_API(user_data); + + if (self->vtable == nullptr || self->vtable->get_meters_per_pixel_at_latitude == nullptr) { + return; + } + + FlValue* value0 = fl_value_get_list_value(message_, 0); + double latitude = fl_value_get_float(value0); + g_autoptr(MaplibreMapLibreHostApiGetMetersPerPixelAtLatitudeResponse) response = self->vtable->get_meters_per_pixel_at_latitude(latitude, self->user_data); + if (response == nullptr) { + g_warning("No response returned to %s.%s", "MapLibreHostApi", "getMetersPerPixelAtLatitude"); + return; + } + + g_autoptr(GError) error = NULL; + if (!fl_basic_message_channel_respond(channel, response_handle, response->value, &error)) { + g_warning("Failed to send response to %s.%s: %s", "MapLibreHostApi", "getMetersPerPixelAtLatitude", error->message); + } +} + void maplibre_map_libre_host_api_set_method_handlers(FlBinaryMessenger* messenger, const gchar* suffix, const MaplibreMapLibreHostApiVTable* vtable, gpointer user_data, GDestroyNotify user_data_free_func) { g_autofree gchar* dot_suffix = suffix != nullptr ? g_strdup_printf(".%s", suffix) : g_strdup(""); g_autoptr(MaplibreMapLibreHostApi) api_data = maplibre_map_libre_host_api_new(vtable, user_data, user_data_free_func); @@ -999,6 +1057,9 @@ void maplibre_map_libre_host_api_set_method_handlers(FlBinaryMessenger* messenge g_autofree gchar* add_geo_json_source_channel_name = g_strdup_printf("dev.flutter.pigeon.maplibre.MapLibreHostApi.addGeoJsonSource%s", dot_suffix); g_autoptr(FlBasicMessageChannel) add_geo_json_source_channel = fl_basic_message_channel_new(messenger, add_geo_json_source_channel_name, FL_MESSAGE_CODEC(codec)); fl_basic_message_channel_set_message_handler(add_geo_json_source_channel, maplibre_map_libre_host_api_add_geo_json_source_cb, g_object_ref(api_data), g_object_unref); + g_autofree gchar* get_meters_per_pixel_at_latitude_channel_name = g_strdup_printf("dev.flutter.pigeon.maplibre.MapLibreHostApi.getMetersPerPixelAtLatitude%s", dot_suffix); + g_autoptr(FlBasicMessageChannel) get_meters_per_pixel_at_latitude_channel = fl_basic_message_channel_new(messenger, get_meters_per_pixel_at_latitude_channel_name, FL_MESSAGE_CODEC(codec)); + fl_basic_message_channel_set_message_handler(get_meters_per_pixel_at_latitude_channel, maplibre_map_libre_host_api_get_meters_per_pixel_at_latitude_cb, g_object_ref(api_data), g_object_unref); } void maplibre_map_libre_host_api_clear_method_handlers(FlBinaryMessenger* messenger, const gchar* suffix) { @@ -1029,6 +1090,9 @@ void maplibre_map_libre_host_api_clear_method_handlers(FlBinaryMessenger* messen g_autofree gchar* add_geo_json_source_channel_name = g_strdup_printf("dev.flutter.pigeon.maplibre.MapLibreHostApi.addGeoJsonSource%s", dot_suffix); g_autoptr(FlBasicMessageChannel) add_geo_json_source_channel = fl_basic_message_channel_new(messenger, add_geo_json_source_channel_name, FL_MESSAGE_CODEC(codec)); fl_basic_message_channel_set_message_handler(add_geo_json_source_channel, nullptr, nullptr, nullptr); + g_autofree gchar* get_meters_per_pixel_at_latitude_channel_name = g_strdup_printf("dev.flutter.pigeon.maplibre.MapLibreHostApi.getMetersPerPixelAtLatitude%s", dot_suffix); + g_autoptr(FlBasicMessageChannel) get_meters_per_pixel_at_latitude_channel = fl_basic_message_channel_new(messenger, get_meters_per_pixel_at_latitude_channel_name, FL_MESSAGE_CODEC(codec)); + fl_basic_message_channel_set_message_handler(get_meters_per_pixel_at_latitude_channel, nullptr, nullptr, nullptr); } void maplibre_map_libre_host_api_respond_jump_to(MaplibreMapLibreHostApiResponseHandle* response_handle) { diff --git a/linux/pigeon.g.h b/linux/pigeon.g.h index 4bc51dd..d652b00 100644 --- a/linux/pigeon.g.h +++ b/linux/pigeon.g.h @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v22.3.0), do not edit directly. +// Autogenerated from Pigeon (v22.4.0), do not edit directly. // See also: https://pub.dev/packages/pigeon #ifndef PIGEON_PIGEON_G_H_ @@ -243,6 +243,29 @@ double maplibre_map_camera_get_bearing(MaplibreMapCamera* object); G_DECLARE_FINAL_TYPE(MaplibreMapLibreHostApiResponseHandle, maplibre_map_libre_host_api_response_handle, MAPLIBRE, MAP_LIBRE_HOST_API_RESPONSE_HANDLE, GObject) +G_DECLARE_FINAL_TYPE(MaplibreMapLibreHostApiGetMetersPerPixelAtLatitudeResponse, maplibre_map_libre_host_api_get_meters_per_pixel_at_latitude_response, MAPLIBRE, MAP_LIBRE_HOST_API_GET_METERS_PER_PIXEL_AT_LATITUDE_RESPONSE, GObject) + +/** + * maplibre_map_libre_host_api_get_meters_per_pixel_at_latitude_response_new: + * + * Creates a new response to MapLibreHostApi.getMetersPerPixelAtLatitude. + * + * Returns: a new #MaplibreMapLibreHostApiGetMetersPerPixelAtLatitudeResponse + */ +MaplibreMapLibreHostApiGetMetersPerPixelAtLatitudeResponse* maplibre_map_libre_host_api_get_meters_per_pixel_at_latitude_response_new(double return_value); + +/** + * maplibre_map_libre_host_api_get_meters_per_pixel_at_latitude_response_new_error: + * @code: error code. + * @message: error message. + * @details: (allow-none): error details or %NULL. + * + * Creates a new error response to MapLibreHostApi.getMetersPerPixelAtLatitude. + * + * Returns: a new #MaplibreMapLibreHostApiGetMetersPerPixelAtLatitudeResponse + */ +MaplibreMapLibreHostApiGetMetersPerPixelAtLatitudeResponse* maplibre_map_libre_host_api_get_meters_per_pixel_at_latitude_response_new_error(const gchar* code, const gchar* message, FlValue* details); + /** * MaplibreMapLibreHostApiVTable: * @@ -257,6 +280,7 @@ typedef struct { void (*add_fill_layer)(const gchar* id, const gchar* source_id, MaplibreMapLibreHostApiResponseHandle* response_handle, gpointer user_data); void (*add_circle_layer)(const gchar* id, const gchar* source_id, MaplibreMapLibreHostApiResponseHandle* response_handle, gpointer user_data); void (*add_geo_json_source)(const gchar* id, const gchar* data, MaplibreMapLibreHostApiResponseHandle* response_handle, gpointer user_data); + MaplibreMapLibreHostApiGetMetersPerPixelAtLatitudeResponse* (*get_meters_per_pixel_at_latitude)(double latitude, gpointer user_data); } MaplibreMapLibreHostApiVTable; /** diff --git a/macos/Classes/Pigeon.g.swift b/macos/Classes/Pigeon.g.swift index be6fe6d..0106cd1 100644 --- a/macos/Classes/Pigeon.g.swift +++ b/macos/Classes/Pigeon.g.swift @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v22.3.0), do not edit directly. +// Autogenerated from Pigeon (v22.4.0), do not edit directly. // See also: https://pub.dev/packages/pigeon import Foundation @@ -286,6 +286,9 @@ protocol MapLibreHostApi { func addCircleLayer(id: String, sourceId: String, completion: @escaping (Result) -> Void) /// Add a GeoJSON source to the map style. func addGeoJsonSource(id: String, data: String, completion: @escaping (Result) -> Void) + /// Returns the distance spanned by one pixel at the specified latitude and + /// current zoom level. + func getMetersPerPixelAtLatitude(latitude: Double) throws -> Double } /// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. @@ -449,6 +452,23 @@ class MapLibreHostApiSetup { } else { addGeoJsonSourceChannel.setMessageHandler(nil) } + /// Returns the distance spanned by one pixel at the specified latitude and + /// current zoom level. + let getMetersPerPixelAtLatitudeChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.maplibre.MapLibreHostApi.getMetersPerPixelAtLatitude\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + getMetersPerPixelAtLatitudeChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let latitudeArg = args[0] as! Double + do { + let result = try api.getMetersPerPixelAtLatitude(latitude: latitudeArg) + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + getMetersPerPixelAtLatitudeChannel.setMessageHandler(nil) + } } } /// Generated protocol from Pigeon that represents Flutter messages that can be called from Swift. diff --git a/pigeons/pigeon.dart b/pigeons/pigeon.dart index 91762ca..53f3ebe 100644 --- a/pigeons/pigeon.dart +++ b/pigeons/pigeon.dart @@ -69,6 +69,10 @@ abstract interface class MapLibreHostApi { required String id, required String data, }); + + /// Returns the distance spanned by one pixel at the specified latitude and + /// current zoom level. + double getMetersPerPixelAtLatitude(double latitude); } @FlutterApi() diff --git a/windows/runner/pigeon.g.cpp b/windows/runner/pigeon.g.cpp index 7d6a4c0..266092f 100644 --- a/windows/runner/pigeon.g.cpp +++ b/windows/runner/pigeon.g.cpp @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v22.3.0), do not edit directly. +// Autogenerated from Pigeon (v22.4.0), do not edit directly. // See also: https://pub.dev/packages/pigeon #undef _HAS_EXCEPTIONS @@ -672,6 +672,34 @@ void MapLibreHostApi::SetUp( channel.SetMessageHandler(nullptr); } } + { + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.maplibre.MapLibreHostApi.getMetersPerPixelAtLatitude" + prepended_suffix, &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_latitude_arg = args.at(0); + if (encodable_latitude_arg.IsNull()) { + reply(WrapError("latitude_arg unexpectedly null.")); + return; + } + const auto& latitude_arg = std::get(encodable_latitude_arg); + ErrorOr output = api->GetMetersPerPixelAtLatitude(latitude_arg); + if (output.has_error()) { + reply(WrapError(output.error())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue(std::move(output).TakeValue())); + reply(EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } } EncodableValue MapLibreHostApi::WrapError(std::string_view error_message) { diff --git a/windows/runner/pigeon.g.h b/windows/runner/pigeon.g.h index 26ab975..19b8c7d 100644 --- a/windows/runner/pigeon.g.h +++ b/windows/runner/pigeon.g.h @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v22.3.0), do not edit directly. +// Autogenerated from Pigeon (v22.4.0), do not edit directly. // See also: https://pub.dev/packages/pigeon #ifndef PIGEON_PIGEON_G_H_ @@ -310,6 +310,9 @@ class MapLibreHostApi { const std::string& id, const std::string& data, std::function reply)> result) = 0; + // Returns the distance spanned by one pixel at the specified latitude and + // current zoom level. + virtual ErrorOr GetMetersPerPixelAtLatitude(double latitude) = 0; // The codec used by MapLibreHostApi. static const flutter::StandardMessageCodec& GetCodec(); From 8a3c7d92716f33bd0c8fe5a71d485e3359c21ad3 Mon Sep 17 00:00:00 2001 From: Joscha <34318751+josxha@users.noreply.github.com> Date: Sun, 15 Sep 2024 23:50:19 +0200 Subject: [PATCH 20/37] fix flyTo cancellation on web --- analysis_options.yaml | 3 +- example/integration_test/smoke_test.dart | 2 +- lib/src/web/interop/events.dart | 15 ++- lib/src/web/widget_state.dart | 146 ++++++++++++++--------- 4 files changed, 103 insertions(+), 63 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 1487251..16c1098 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -7,4 +7,5 @@ analyzer: linter: rules: lines_longer_than_80_chars: false - flutter_style_todos: false \ No newline at end of file + flutter_style_todos: false + cascade_invocations: false \ No newline at end of file diff --git a/example/integration_test/smoke_test.dart b/example/integration_test/smoke_test.dart index 5387c29..4f24b70 100644 --- a/example/integration_test/smoke_test.dart +++ b/example/integration_test/smoke_test.dart @@ -92,7 +92,7 @@ void main() { zoom: 2, tilt: 2, webSpeed: 0.1, - nativeDuration: const Duration(seconds: 10), + nativeDuration: const Duration(days: 1), ), throwsA(isA()), ); diff --git a/lib/src/web/interop/events.dart b/lib/src/web/interop/events.dart index b4903cf..9c1ee5a 100644 --- a/lib/src/web/interop/events.dart +++ b/lib/src/web/interop/events.dart @@ -1,12 +1,20 @@ part of 'interop.dart'; +/// The base event for MapLibre +@JS() +extension type MapLibreEvent._(JSObject _) + implements JSObject { + external Map target; + external JSString type; + external T? originalEvent; +} + /// A mouse event on the map. @JS() -extension type MapMouseEvent._(JSObject _) implements JSObject { +extension type MapMouseEvent._(MapLibreEvent _) implements MapLibreEvent { /// Create a new [MapMouseEvent]. external MapMouseEvent(); - external Map target; external LngLat lngLat; external JSObject originalEvent; } @@ -30,4 +38,7 @@ abstract class MapEventType { /// Fired when the style has been loaded. static const load = 'load'; + + /// Fired once the map stops moving. + static const moveEnd = 'moveend'; } diff --git a/lib/src/web/widget_state.dart b/lib/src/web/widget_state.dart index 7a3ce86..8711a15 100644 --- a/lib/src/web/widget_state.dart +++ b/lib/src/web/widget_state.dart @@ -1,7 +1,9 @@ +import 'dart:async'; import 'dart:js_interop'; import 'dart:math'; import 'dart:ui_web'; +import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:maplibre/maplibre.dart'; import 'package:maplibre/src/web/extensions.dart'; @@ -16,6 +18,7 @@ final class MapLibreMapStateWeb extends State final _viewName = 'plugins.flutter.io/maplibre${_counter++}'; late HTMLDivElement _htmlElement; late interop.Map _map; + Completer? _moveCompleter; MapOptions get _options => widget.options; @@ -23,7 +26,7 @@ final class MapLibreMapStateWeb extends State void initState() { platformViewRegistry.registerViewFactory( _viewName, - (int viewId, [dynamic params]) { + (int viewId, [dynamic params]) { _htmlElement = HTMLDivElement() ..style.padding = '0' ..style.margin = '0' @@ -50,52 +53,45 @@ final class MapLibreMapStateWeb extends State // add controls for (final control in _options.controls) { final jsControl = switch (control) { - final ScaleControl control => - interop.ScaleControl( - interop.ScaleControlOptions( - maxWidth: control.maxWidth, - unit: control.unit.name, - ), - ), - final GeolocateControl control => - interop.GeolocateControl( - interop.GeolocateControlOptions( - positionOptions: interop.PositionOptions( - enableHighAccuracy: - control.positionOptions.enableHighAccuracy, - maximumAge: - control.positionOptions.maximumAge.inMilliseconds, - timeout: control.positionOptions.timeout.inMilliseconds, - ), - ), + final ScaleControl control => interop.ScaleControl( + interop.ScaleControlOptions( + maxWidth: control.maxWidth, + unit: control.unit.name, ), - final AttributionControl control => - interop.AttributionControl( - interop.AttributionControlOptions( - compact: control.compact, - customAttribution: control.customAttribution, + ), + final GeolocateControl control => interop.GeolocateControl( + interop.GeolocateControlOptions( + positionOptions: interop.PositionOptions( + enableHighAccuracy: + control.positionOptions.enableHighAccuracy, + maximumAge: + control.positionOptions.maximumAge.inMilliseconds, + timeout: control.positionOptions.timeout.inMilliseconds, ), ), - final FullscreenControl _ => - interop.FullscreenControl( - interop.FullscreenControlOptions(), - ), - final LogoControl control => - interop.LogoControl( - interop.LogoControlOptions(compact: control.compact), - ), - final NavigationControl control => - interop.NavigationControl( - interop.NavigationControlOptions( - showCompass: control.showCompass, - showZoom: control.showZoom, - visualizePitch: control.visualizePitch, - ), + ), + final AttributionControl control => interop.AttributionControl( + interop.AttributionControlOptions( + compact: control.compact, + customAttribution: control.customAttribution, ), - final TerrainControl control => - interop.TerrainControl( - interop.TerrainControlOptions(source: control.source), + ), + final FullscreenControl _ => interop.FullscreenControl( + interop.FullscreenControlOptions(), + ), + final LogoControl control => interop.LogoControl( + interop.LogoControlOptions(compact: control.compact), + ), + final NavigationControl control => interop.NavigationControl( + interop.NavigationControlOptions( + showCompass: control.showCompass, + showZoom: control.showZoom, + visualizePitch: control.visualizePitch, ), + ), + final TerrainControl control => interop.TerrainControl( + interop.TerrainControlOptions(source: control.source), + ), }; _map.addControl(jsControl); } @@ -103,7 +99,7 @@ final class MapLibreMapStateWeb extends State if (widget.onStyleLoaded case final VoidCallback callback) { _map.on( interop.MapEventType.load, - (interop.MapMouseEvent event) { + (interop.MapMouseEvent event) { callback(); }.toJS, ); @@ -111,7 +107,7 @@ final class MapLibreMapStateWeb extends State if (_options.onClick case final OnClickCallback callback) { _map.on( interop.MapEventType.click, - (interop.MapMouseEvent event) { + (interop.MapMouseEvent event) { callback(event.lngLat.toPosition()); }.toJS, ); @@ -119,7 +115,7 @@ final class MapLibreMapStateWeb extends State if (_options.onDoubleClick case final OnClickCallback callback) { _map.on( interop.MapEventType.dblclick, - (interop.MapMouseEvent event) { + (interop.MapMouseEvent event) { callback(event.lngLat.toPosition()); }.toJS, ); @@ -127,11 +123,18 @@ final class MapLibreMapStateWeb extends State if (_options.onSecondaryClick case final OnClickCallback callback) { _map.on( interop.MapEventType.contextmenu, - (interop.MapMouseEvent event) { + (interop.MapMouseEvent event) { callback(event.lngLat.toPosition()); }.toJS, ); } + _map.on( + interop.MapEventType.moveEnd, + (interop.MapLibreEvent event) { + if (_moveCompleter?.isCompleted ?? true) return; + _moveCompleter?.complete(event); + }.toJS, + ); return _htmlElement; }, @@ -214,17 +217,43 @@ final class MapLibreMapStateWeb extends State Duration nativeDuration = const Duration(seconds: 2), double webSpeed = 1.2, Duration? maxDuration, - }) async => - _map.flyTo( - interop.FlyToOptions( - center: center?.toLngLat(), - zoom: zoom, - bearing: bearing, - pitch: tilt, - speed: webSpeed, - maxDuration: maxDuration?.inMilliseconds, - ), - ); + }) async { + final destination = center?.toLngLat(); + _map.flyTo( + interop.FlyToOptions( + center: destination, + zoom: zoom, + bearing: bearing, + pitch: tilt, + speed: webSpeed, + maxDuration: maxDuration?.inMilliseconds, + ), + ); + final completer = _moveCompleter = Completer(); + final _ = await completer.future; + _moveCompleter = null; + + // check if the targeted values were reached or if the flight was cancelled + final newCenter = _map.getCenter(); + bool reachedCenter; + if (destination == null) { + reachedCenter = true; + } else { + final reachedLng = (destination.lng - newCenter.lng).abs() < 0.0000001; + final reachedLat = (destination.lat - newCenter.lat).abs() < 0.0000001; + reachedCenter = reachedLat && reachedLng; + } + final reachedZoom = zoom == null || zoom == _map.getZoom(); + final reachedBearing = bearing == null || bearing == _map.getBearing(); + final reachedTilt = tilt == null || tilt == _map.getPitch(); + + if (reachedCenter && reachedZoom && reachedBearing && reachedTilt) return; + + throw PlatformException( + code: 'CancellationException', + message: 'Animation cancelled.', + ); + } @override Future addSource(Source source) async { @@ -261,8 +290,7 @@ final class MapLibreMapStateWeb extends State } @override - Future getCamera() async => - MapCamera( + Future getCamera() async => MapCamera( center: _map.getCenter().toPosition(), zoom: _map.getZoom().toDouble(), tilt: _map.getPitch().toDouble(), From 497f91e8a13156529193290acbbdb9d618455f6c Mon Sep 17 00:00:00 2001 From: Joscha <34318751+josxha@users.noreply.github.com> Date: Mon, 16 Sep 2024 00:33:08 +0200 Subject: [PATCH 21/37] add getVisibleRegion --- .../josxha/maplibre/MapLibreMapController.kt | 14 +- .../com/github/josxha/maplibre/Pigeon.g.kt | 60 +++++++ docs/docs/features/supported-features.md | 23 +-- example/lib/controller_page.dart | 16 +- ios/Classes/Pigeon.g.swift | 58 ++++++ lib/maplibre.dart | 1 + lib/src/lng_lat_bounds.dart | 49 +++++ lib/src/map_controller.dart | 3 + lib/src/native/pigeon.g.dart | 72 ++++++++ lib/src/native/widget_state.dart | 11 ++ lib/src/web/interop/map.dart | 22 +++ lib/src/web/widget_state.dart | 11 ++ linux/pigeon.g.cc | 170 ++++++++++++++++++ linux/pigeon.g.h | 82 +++++++++ macos/Classes/Pigeon.g.swift | 58 ++++++ pigeons/pigeon.dart | 19 ++ windows/runner/pigeon.g.cpp | 97 ++++++++++ windows/runner/pigeon.g.h | 41 +++++ 18 files changed, 794 insertions(+), 13 deletions(-) create mode 100644 lib/src/lng_lat_bounds.dart diff --git a/android/src/main/kotlin/com/github/josxha/maplibre/MapLibreMapController.kt b/android/src/main/kotlin/com/github/josxha/maplibre/MapLibreMapController.kt index c7bb422..824e75e 100644 --- a/android/src/main/kotlin/com/github/josxha/maplibre/MapLibreMapController.kt +++ b/android/src/main/kotlin/com/github/josxha/maplibre/MapLibreMapController.kt @@ -1,6 +1,7 @@ -package com.github.josxha.maplibre +package com.githb.josxha.maplibre import LngLat +import LngLatBounds import MapCamera import MapLibreFlutterApi import MapLibreHostApi @@ -159,6 +160,17 @@ class MapLibreMapController( callback(Result.success(camera)) } + override fun getVisibleRegion(callback: (Result) -> Unit) { + val bounds = mapLibreMap.projection.visibleRegion.latLngBounds + val lngLatBounds = LngLatBounds( + bounds.longitudeWest, + bounds.longitudeEast, + bounds.latitudeSouth, + bounds.latitudeNorth + ) + callback(Result.success(lngLatBounds)) + } + override fun addFillLayer(id: String, sourceId: String, callback: (Result) -> Unit) { mapLibreMap.style?.addLayer(FillLayer(id, sourceId)) callback(Result.success(Unit)) diff --git a/android/src/main/kotlin/com/github/josxha/maplibre/Pigeon.g.kt b/android/src/main/kotlin/com/github/josxha/maplibre/Pigeon.g.kt index 4732201..12ea006 100644 --- a/android/src/main/kotlin/com/github/josxha/maplibre/Pigeon.g.kt +++ b/android/src/main/kotlin/com/github/josxha/maplibre/Pigeon.g.kt @@ -177,6 +177,37 @@ data class MapCamera ( ) } } + +/** + * LatLng bound object + * + * Generated class from Pigeon that represents data sent in messages. + */ +data class LngLatBounds ( + val longitudeWest: Double, + val longitudeEast: Double, + val latitudeSouth: Double, + val latitudeNorth: Double +) + { + companion object { + fun fromList(pigeonVar_list: List): LngLatBounds { + val longitudeWest = pigeonVar_list[0] as Double + val longitudeEast = pigeonVar_list[1] as Double + val latitudeSouth = pigeonVar_list[2] as Double + val latitudeNorth = pigeonVar_list[3] as Double + return LngLatBounds(longitudeWest, longitudeEast, latitudeSouth, latitudeNorth) + } + } + fun toList(): List { + return listOf( + longitudeWest, + longitudeEast, + latitudeSouth, + latitudeNorth, + ) + } +} private open class PigeonPigeonCodec : StandardMessageCodec() { override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { return when (type) { @@ -200,6 +231,11 @@ private open class PigeonPigeonCodec : StandardMessageCodec() { MapCamera.fromList(it) } } + 133.toByte() -> { + return (readValue(buffer) as? List)?.let { + LngLatBounds.fromList(it) + } + } else -> super.readValueOfType(type, buffer) } } @@ -221,6 +257,10 @@ private open class PigeonPigeonCodec : StandardMessageCodec() { stream.write(132) writeValue(stream, value.toList()) } + is LngLatBounds -> { + stream.write(133) + writeValue(stream, value.toList()) + } else -> super.writeValue(stream, value) } } @@ -238,6 +278,8 @@ interface MapLibreHostApi { * tilt and map rotation. */ fun getCamera(callback: (Result) -> Unit) + /** Get the visible region of the current map camera. */ + fun getVisibleRegion(callback: (Result) -> Unit) /** Convert a coordinate to a location on the screen. */ fun toScreenLocation(lng: Double, lat: Double, callback: (Result) -> Unit) /** Convert a screen location to a coordinate. */ @@ -326,6 +368,24 @@ interface MapLibreHostApi { channel.setMessageHandler(null) } } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.maplibre.MapLibreHostApi.getVisibleRegion$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + api.getVisibleRegion{ result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(wrapResult(data)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } run { val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.maplibre.MapLibreHostApi.toScreenLocation$separatedMessageChannelSuffix", codec) if (api != null) { diff --git a/docs/docs/features/supported-features.md b/docs/docs/features/supported-features.md index 1285eb3..136517f 100644 --- a/docs/docs/features/supported-features.md +++ b/docs/docs/features/supported-features.md @@ -49,14 +49,15 @@ lack of platform views of these platforms. ### Map Controller -| Feature | web | android | iOS | windows | macOS | linux | -|------------------------------|-----|---------|-----|---------|-------|-------| -| jumpTo | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | -| flyTo | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | -| addSource | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | -| addLayer | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | -| setMyLocationTrackingMode | ❌ | ❌ | ❌ | ➖ | ➖ | ➖ | -| setMapLanguage | ❌ | ❌ | ❌ | ➖ | ➖ | ➖ | -| toScreenLocation | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | -| toLatLng | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | -| getMetersPerPixelAtLatitude | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | \ No newline at end of file +| Feature | web | android | iOS | windows | macOS | linux | +|-----------------------------|-----|---------|-----|---------|-------|-------| +| jumpTo | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | +| flyTo | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | +| addSource | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | +| addLayer | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | +| setMyLocationTrackingMode | ❌ | ❌ | ❌ | ➖ | ➖ | ➖ | +| setMapLanguage | ❌ | ❌ | ❌ | ➖ | ➖ | ➖ | +| toScreenLocation | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | +| toLatLng | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | +| getMetersPerPixelAtLatitude | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | +| getVisibleRegion | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | \ No newline at end of file diff --git a/example/lib/controller_page.dart b/example/lib/controller_page.dart index 1b38383..668312b 100644 --- a/example/lib/controller_page.dart +++ b/example/lib/controller_page.dart @@ -109,7 +109,21 @@ tilt: ${camera.tilt}'''), ); } }, - child: const Text('meter/pixel at center'), + child: const Text('Meter/Pixel at center'), + ), + OutlinedButton( + onPressed: () async { + final region = await _controller.getVisibleRegion(); + debugPrint(region.toString()); + if (context.mounted) { + ScaffoldMessenger.of(context) + ..hideCurrentSnackBar() + ..showSnackBar( + SnackBar(content: Text(region.toString())), + ); + } + }, + child: const Text('Visible region'), ), ], ), diff --git a/ios/Classes/Pigeon.g.swift b/ios/Classes/Pigeon.g.swift index 0106cd1..3f0028d 100644 --- a/ios/Classes/Pigeon.g.swift +++ b/ios/Classes/Pigeon.g.swift @@ -215,6 +215,41 @@ struct MapCamera { } } +/// LatLng bound object +/// +/// Generated class from Pigeon that represents data sent in messages. +struct LngLatBounds { + var longitudeWest: Double + var longitudeEast: Double + var latitudeSouth: Double + var latitudeNorth: Double + + + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> LngLatBounds? { + let longitudeWest = pigeonVar_list[0] as! Double + let longitudeEast = pigeonVar_list[1] as! Double + let latitudeSouth = pigeonVar_list[2] as! Double + let latitudeNorth = pigeonVar_list[3] as! Double + + return LngLatBounds( + longitudeWest: longitudeWest, + longitudeEast: longitudeEast, + latitudeSouth: latitudeSouth, + latitudeNorth: latitudeNorth + ) + } + func toList() -> [Any?] { + return [ + longitudeWest, + longitudeEast, + latitudeSouth, + latitudeNorth, + ] + } +} + private class PigeonPigeonCodecReader: FlutterStandardReader { override func readValue(ofType type: UInt8) -> Any? { switch type { @@ -226,6 +261,8 @@ private class PigeonPigeonCodecReader: FlutterStandardReader { return ScreenLocation.fromList(self.readValue() as! [Any?]) case 132: return MapCamera.fromList(self.readValue() as! [Any?]) + case 133: + return LngLatBounds.fromList(self.readValue() as! [Any?]) default: return super.readValue(ofType: type) } @@ -246,6 +283,9 @@ private class PigeonPigeonCodecWriter: FlutterStandardWriter { } else if let value = value as? MapCamera { super.writeByte(132) super.writeValue(value.toList()) + } else if let value = value as? LngLatBounds { + super.writeByte(133) + super.writeValue(value.toList()) } else { super.writeValue(value) } @@ -276,6 +316,8 @@ protocol MapLibreHostApi { /// Get the current camera position with the map center, zoom level, camera /// tilt and map rotation. func getCamera(completion: @escaping (Result) -> Void) + /// Get the visible region of the current map camera. + func getVisibleRegion(completion: @escaping (Result) -> Void) /// Convert a coordinate to a location on the screen. func toScreenLocation(lng: Double, lat: Double, completion: @escaping (Result) -> Void) /// Convert a screen location to a coordinate. @@ -357,6 +399,22 @@ class MapLibreHostApiSetup { } else { getCameraChannel.setMessageHandler(nil) } + /// Get the visible region of the current map camera. + let getVisibleRegionChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.maplibre.MapLibreHostApi.getVisibleRegion\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + getVisibleRegionChannel.setMessageHandler { _, reply in + api.getVisibleRegion { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + getVisibleRegionChannel.setMessageHandler(nil) + } /// Convert a coordinate to a location on the screen. let toScreenLocationChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.maplibre.MapLibreHostApi.toScreenLocation\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) if let api = api { diff --git a/lib/maplibre.dart b/lib/maplibre.dart index 75e07a2..2a9393a 100644 --- a/lib/maplibre.dart +++ b/lib/maplibre.dart @@ -3,6 +3,7 @@ export 'package:geotypes/geotypes.dart'; export 'src/annotations.dart'; +export 'src/common.dart'; export 'src/layers.dart'; export 'src/map.dart'; export 'src/map_camera.dart'; diff --git a/lib/src/lng_lat_bounds.dart b/lib/src/lng_lat_bounds.dart new file mode 100644 index 0000000..a7d9d3f --- /dev/null +++ b/lib/src/lng_lat_bounds.dart @@ -0,0 +1,49 @@ +import 'package:flutter/foundation.dart'; + +/// LatLng bounds class. +@immutable +class LngLatBounds { + /// Create a new [LngLatBounds] object. + const LngLatBounds({ + required this.longitudeWest, + required this.longitudeEast, + required this.latitudeSouth, + required this.latitudeNorth, + }); + + /// The minimum longitude, most west + final double longitudeWest; + + /// The maximum longitude, most east + final double longitudeEast; + + /// The minimum latitude, most south + final double latitudeSouth; + + /// The maximum latitude, most north + final double latitudeNorth; + + @override + String toString() => 'LngLatBounds(' + 'longitudeWest: $longitudeWest, ' + 'longitudeEast: $longitudeEast, ' + 'latitudeSouth: $latitudeSouth, ' + 'latitudeNorth: $latitudeNorth)'; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is LngLatBounds && + runtimeType == other.runtimeType && + longitudeWest == other.longitudeWest && + longitudeEast == other.longitudeEast && + latitudeSouth == other.latitudeSouth && + latitudeNorth == other.latitudeNorth; + + @override + int get hashCode => + longitudeWest.hashCode ^ + longitudeEast.hashCode ^ + latitudeSouth.hashCode ^ + latitudeNorth.hashCode; +} diff --git a/lib/src/map_controller.dart b/lib/src/map_controller.dart index e5894ce..9c4ec5f 100644 --- a/lib/src/map_controller.dart +++ b/lib/src/map_controller.dart @@ -51,4 +51,7 @@ abstract interface class MapController { /// poles. This relationship parallels the relationship between longitudinal /// coordinates at different latitudes. Future getMetersPerPixelAtLatitude(double latitude); + + /// The smallest bounding box that includes the visible region. + Future getVisibleRegion(); } diff --git a/lib/src/native/pigeon.g.dart b/lib/src/native/pigeon.g.dart index 19b6764..0e1e27e 100644 --- a/lib/src/native/pigeon.g.dart +++ b/lib/src/native/pigeon.g.dart @@ -180,6 +180,43 @@ class MapCamera { } } +/// LatLng bound object +class LngLatBounds { + LngLatBounds({ + required this.longitudeWest, + required this.longitudeEast, + required this.latitudeSouth, + required this.latitudeNorth, + }); + + double longitudeWest; + + double longitudeEast; + + double latitudeSouth; + + double latitudeNorth; + + Object encode() { + return [ + longitudeWest, + longitudeEast, + latitudeSouth, + latitudeNorth, + ]; + } + + static LngLatBounds decode(Object result) { + result as List; + return LngLatBounds( + longitudeWest: result[0]! as double, + longitudeEast: result[1]! as double, + latitudeSouth: result[2]! as double, + latitudeNorth: result[3]! as double, + ); + } +} + class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @override @@ -199,6 +236,9 @@ class _PigeonCodec extends StandardMessageCodec { } else if (value is MapCamera) { buffer.putUint8(132); writeValue(buffer, value.encode()); + } else if (value is LngLatBounds) { + buffer.putUint8(133); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -215,6 +255,8 @@ class _PigeonCodec extends StandardMessageCodec { return ScreenLocation.decode(readValue(buffer)!); case 132: return MapCamera.decode(readValue(buffer)!); + case 133: + return LngLatBounds.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); } @@ -329,6 +371,36 @@ class MapLibreHostApi { } } + /// Get the visible region of the current map camera. + Future getVisibleRegion() async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.maplibre.MapLibreHostApi.getVisibleRegion$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = + await pigeonVar_channel.send(null) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as LngLatBounds?)!; + } + } + /// Convert a coordinate to a location on the screen. Future toScreenLocation(double lng, double lat) async { final String pigeonVar_channelName = diff --git a/lib/src/native/widget_state.dart b/lib/src/native/widget_state.dart index 8f5a67d..9206e30 100644 --- a/lib/src/native/widget_state.dart +++ b/lib/src/native/widget_state.dart @@ -162,4 +162,15 @@ final class MapLibreMapStateNative extends State @override Future getMetersPerPixelAtLatitude(double latitude) async => _hostApi.getMetersPerPixelAtLatitude(latitude); + + @override + Future getVisibleRegion() async { + final bounds = await _hostApi.getVisibleRegion(); + return LngLatBounds( + longitudeWest: bounds.longitudeWest, + longitudeEast: bounds.longitudeEast, + latitudeSouth: bounds.latitudeSouth, + latitudeNorth: bounds.latitudeNorth, + ); + } } diff --git a/lib/src/web/interop/map.dart b/lib/src/web/interop/map.dart index 6121734..d382bd4 100644 --- a/lib/src/web/interop/map.dart +++ b/lib/src/web/interop/map.dart @@ -57,6 +57,11 @@ extension type Map._(Camera _) implements Camera { /// Returns the map's current pitch (tilt). external num getPitch(); + + /// Returns the map's geographical bounds. When the bearing or pitch is + /// non-zero, the visible region is not an axis-aligned rectangle, and the + /// result is the smallest bounds that encompasses the visible region. + external LngLatBounds getBounds(); } /// Anonymous MapOptions for the MapLibre JavaScript [Map]. @@ -94,6 +99,23 @@ extension type LngLat._(JSObject _) implements JSObject { Position toPosition() => Position(lng, lat); } +/// A [LngLatBounds] object represents a geographical bounding box, +/// defined by its southwest and northeast points in longitude and latitude. +@JS() +extension type LngLatBounds._(JSObject _) implements JSObject { + /// Returns the west edge of the bounding box. + external num getWest(); + + /// Returns the south edge of the bounding box. + external num getSouth(); + + /// Returns the east edge of the bounding box. + external num getEast(); + + /// Returns the north edge of the bounding box. + external num getNorth(); +} + /// Options to specify the map bounds. @anonymous @JS() diff --git a/lib/src/web/widget_state.dart b/lib/src/web/widget_state.dart index 8711a15..f1795e1 100644 --- a/lib/src/web/widget_state.dart +++ b/lib/src/web/widget_state.dart @@ -305,4 +305,15 @@ final class MapLibreMapStateWeb extends State cos(latitude * radian2degree) / pow(2, zoom + 9); } + + @override + Future getVisibleRegion() async { + final bounds = _map.getBounds(); + return LngLatBounds( + longitudeWest: bounds.getWest().toDouble(), + longitudeEast: bounds.getEast().toDouble(), + latitudeSouth: bounds.getSouth().toDouble(), + latitudeNorth: bounds.getNorth().toDouble(), + ); + } } diff --git a/linux/pigeon.g.cc b/linux/pigeon.g.cc index 5f95cd4..c9b2c8b 100644 --- a/linux/pigeon.g.cc +++ b/linux/pigeon.g.cc @@ -294,6 +294,78 @@ static MaplibreMapCamera* maplibre_map_camera_new_from_list(FlValue* values) { return maplibre_map_camera_new(center, zoom, tilt, bearing); } +struct _MaplibreLngLatBounds { + GObject parent_instance; + + double longitude_west; + double longitude_east; + double latitude_south; + double latitude_north; +}; + +G_DEFINE_TYPE(MaplibreLngLatBounds, maplibre_lng_lat_bounds, G_TYPE_OBJECT) + +static void maplibre_lng_lat_bounds_dispose(GObject* object) { + G_OBJECT_CLASS(maplibre_lng_lat_bounds_parent_class)->dispose(object); +} + +static void maplibre_lng_lat_bounds_init(MaplibreLngLatBounds* self) { +} + +static void maplibre_lng_lat_bounds_class_init(MaplibreLngLatBoundsClass* klass) { + G_OBJECT_CLASS(klass)->dispose = maplibre_lng_lat_bounds_dispose; +} + +MaplibreLngLatBounds* maplibre_lng_lat_bounds_new(double longitude_west, double longitude_east, double latitude_south, double latitude_north) { + MaplibreLngLatBounds* self = MAPLIBRE_LNG_LAT_BOUNDS(g_object_new(maplibre_lng_lat_bounds_get_type(), nullptr)); + self->longitude_west = longitude_west; + self->longitude_east = longitude_east; + self->latitude_south = latitude_south; + self->latitude_north = latitude_north; + return self; +} + +double maplibre_lng_lat_bounds_get_longitude_west(MaplibreLngLatBounds* self) { + g_return_val_if_fail(MAPLIBRE_IS_LNG_LAT_BOUNDS(self), 0.0); + return self->longitude_west; +} + +double maplibre_lng_lat_bounds_get_longitude_east(MaplibreLngLatBounds* self) { + g_return_val_if_fail(MAPLIBRE_IS_LNG_LAT_BOUNDS(self), 0.0); + return self->longitude_east; +} + +double maplibre_lng_lat_bounds_get_latitude_south(MaplibreLngLatBounds* self) { + g_return_val_if_fail(MAPLIBRE_IS_LNG_LAT_BOUNDS(self), 0.0); + return self->latitude_south; +} + +double maplibre_lng_lat_bounds_get_latitude_north(MaplibreLngLatBounds* self) { + g_return_val_if_fail(MAPLIBRE_IS_LNG_LAT_BOUNDS(self), 0.0); + return self->latitude_north; +} + +static FlValue* maplibre_lng_lat_bounds_to_list(MaplibreLngLatBounds* self) { + FlValue* values = fl_value_new_list(); + fl_value_append_take(values, fl_value_new_float(self->longitude_west)); + fl_value_append_take(values, fl_value_new_float(self->longitude_east)); + fl_value_append_take(values, fl_value_new_float(self->latitude_south)); + fl_value_append_take(values, fl_value_new_float(self->latitude_north)); + return values; +} + +static MaplibreLngLatBounds* maplibre_lng_lat_bounds_new_from_list(FlValue* values) { + FlValue* value0 = fl_value_get_list_value(values, 0); + double longitude_west = fl_value_get_float(value0); + FlValue* value1 = fl_value_get_list_value(values, 1); + double longitude_east = fl_value_get_float(value1); + FlValue* value2 = fl_value_get_list_value(values, 2); + double latitude_south = fl_value_get_float(value2); + FlValue* value3 = fl_value_get_list_value(values, 3); + double latitude_north = fl_value_get_float(value3); + return maplibre_lng_lat_bounds_new(longitude_west, longitude_east, latitude_south, latitude_north); +} + G_DECLARE_FINAL_TYPE(MaplibreMessageCodec, maplibre_message_codec, MAPLIBRE, MESSAGE_CODEC, FlStandardMessageCodec) struct _MaplibreMessageCodec { @@ -331,6 +403,13 @@ static gboolean maplibre_message_codec_write_maplibre_map_camera(FlStandardMessa return fl_standard_message_codec_write_value(codec, buffer, values, error); } +static gboolean maplibre_message_codec_write_maplibre_lng_lat_bounds(FlStandardMessageCodec* codec, GByteArray* buffer, MaplibreLngLatBounds* value, GError** error) { + uint8_t type = 133; + g_byte_array_append(buffer, &type, sizeof(uint8_t)); + g_autoptr(FlValue) values = maplibre_lng_lat_bounds_to_list(value); + return fl_standard_message_codec_write_value(codec, buffer, values, error); +} + static gboolean maplibre_message_codec_write_value(FlStandardMessageCodec* codec, GByteArray* buffer, FlValue* value, GError** error) { if (fl_value_get_type(value) == FL_VALUE_TYPE_CUSTOM) { switch (fl_value_get_custom_type(value)) { @@ -342,6 +421,8 @@ static gboolean maplibre_message_codec_write_value(FlStandardMessageCodec* codec return maplibre_message_codec_write_maplibre_screen_location(codec, buffer, MAPLIBRE_SCREEN_LOCATION(fl_value_get_custom_value_object(value)), error); case 132: return maplibre_message_codec_write_maplibre_map_camera(codec, buffer, MAPLIBRE_MAP_CAMERA(fl_value_get_custom_value_object(value)), error); + case 133: + return maplibre_message_codec_write_maplibre_lng_lat_bounds(codec, buffer, MAPLIBRE_LNG_LAT_BOUNDS(fl_value_get_custom_value_object(value)), error); } } @@ -408,6 +489,21 @@ static FlValue* maplibre_message_codec_read_maplibre_map_camera(FlStandardMessag return fl_value_new_custom_object(132, G_OBJECT(value)); } +static FlValue* maplibre_message_codec_read_maplibre_lng_lat_bounds(FlStandardMessageCodec* codec, GBytes* buffer, size_t* offset, GError** error) { + g_autoptr(FlValue) values = fl_standard_message_codec_read_value(codec, buffer, offset, error); + if (values == nullptr) { + return nullptr; + } + + g_autoptr(MaplibreLngLatBounds) value = maplibre_lng_lat_bounds_new_from_list(values); + if (value == nullptr) { + g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED, "Invalid data received for MessageData"); + return nullptr; + } + + return fl_value_new_custom_object(133, G_OBJECT(value)); +} + static FlValue* maplibre_message_codec_read_value_of_type(FlStandardMessageCodec* codec, GBytes* buffer, size_t* offset, int type, GError** error) { switch (type) { case 129: @@ -418,6 +514,8 @@ static FlValue* maplibre_message_codec_read_value_of_type(FlStandardMessageCodec return maplibre_message_codec_read_maplibre_screen_location(codec, buffer, offset, error); case 132: return maplibre_message_codec_read_maplibre_map_camera(codec, buffer, offset, error); + case 133: + return maplibre_message_codec_read_maplibre_lng_lat_bounds(codec, buffer, offset, error); default: return FL_STANDARD_MESSAGE_CODEC_CLASS(maplibre_message_codec_parent_class)->read_value_of_type(codec, buffer, offset, type, error); } @@ -583,6 +681,45 @@ static MaplibreMapLibreHostApiGetCameraResponse* maplibre_map_libre_host_api_get return self; } +G_DECLARE_FINAL_TYPE(MaplibreMapLibreHostApiGetVisibleRegionResponse, maplibre_map_libre_host_api_get_visible_region_response, MAPLIBRE, MAP_LIBRE_HOST_API_GET_VISIBLE_REGION_RESPONSE, GObject) + +struct _MaplibreMapLibreHostApiGetVisibleRegionResponse { + GObject parent_instance; + + FlValue* value; +}; + +G_DEFINE_TYPE(MaplibreMapLibreHostApiGetVisibleRegionResponse, maplibre_map_libre_host_api_get_visible_region_response, G_TYPE_OBJECT) + +static void maplibre_map_libre_host_api_get_visible_region_response_dispose(GObject* object) { + MaplibreMapLibreHostApiGetVisibleRegionResponse* self = MAPLIBRE_MAP_LIBRE_HOST_API_GET_VISIBLE_REGION_RESPONSE(object); + g_clear_pointer(&self->value, fl_value_unref); + G_OBJECT_CLASS(maplibre_map_libre_host_api_get_visible_region_response_parent_class)->dispose(object); +} + +static void maplibre_map_libre_host_api_get_visible_region_response_init(MaplibreMapLibreHostApiGetVisibleRegionResponse* self) { +} + +static void maplibre_map_libre_host_api_get_visible_region_response_class_init(MaplibreMapLibreHostApiGetVisibleRegionResponseClass* klass) { + G_OBJECT_CLASS(klass)->dispose = maplibre_map_libre_host_api_get_visible_region_response_dispose; +} + +static MaplibreMapLibreHostApiGetVisibleRegionResponse* maplibre_map_libre_host_api_get_visible_region_response_new(MaplibreLngLatBounds* return_value) { + MaplibreMapLibreHostApiGetVisibleRegionResponse* self = MAPLIBRE_MAP_LIBRE_HOST_API_GET_VISIBLE_REGION_RESPONSE(g_object_new(maplibre_map_libre_host_api_get_visible_region_response_get_type(), nullptr)); + self->value = fl_value_new_list(); + fl_value_append_take(self->value, fl_value_new_custom_object(133, G_OBJECT(return_value))); + return self; +} + +static MaplibreMapLibreHostApiGetVisibleRegionResponse* maplibre_map_libre_host_api_get_visible_region_response_new_error(const gchar* code, const gchar* message, FlValue* details) { + MaplibreMapLibreHostApiGetVisibleRegionResponse* self = MAPLIBRE_MAP_LIBRE_HOST_API_GET_VISIBLE_REGION_RESPONSE(g_object_new(maplibre_map_libre_host_api_get_visible_region_response_get_type(), nullptr)); + self->value = fl_value_new_list(); + fl_value_append_take(self->value, fl_value_new_string(code)); + fl_value_append_take(self->value, fl_value_new_string(message != nullptr ? message : "")); + fl_value_append_take(self->value, details != nullptr ? fl_value_ref(details) : fl_value_new_null()); + return self; +} + G_DECLARE_FINAL_TYPE(MaplibreMapLibreHostApiToScreenLocationResponse, maplibre_map_libre_host_api_to_screen_location_response, MAPLIBRE, MAP_LIBRE_HOST_API_TO_SCREEN_LOCATION_RESPONSE, GObject) struct _MaplibreMapLibreHostApiToScreenLocationResponse { @@ -932,6 +1069,17 @@ static void maplibre_map_libre_host_api_get_camera_cb(FlBasicMessageChannel* cha self->vtable->get_camera(handle, self->user_data); } +static void maplibre_map_libre_host_api_get_visible_region_cb(FlBasicMessageChannel* channel, FlValue* message_, FlBasicMessageChannelResponseHandle* response_handle, gpointer user_data) { + MaplibreMapLibreHostApi* self = MAPLIBRE_MAP_LIBRE_HOST_API(user_data); + + if (self->vtable == nullptr || self->vtable->get_visible_region == nullptr) { + return; + } + + g_autoptr(MaplibreMapLibreHostApiResponseHandle) handle = maplibre_map_libre_host_api_response_handle_new(channel, response_handle); + self->vtable->get_visible_region(handle, self->user_data); +} + static void maplibre_map_libre_host_api_to_screen_location_cb(FlBasicMessageChannel* channel, FlValue* message_, FlBasicMessageChannelResponseHandle* response_handle, gpointer user_data) { MaplibreMapLibreHostApi* self = MAPLIBRE_MAP_LIBRE_HOST_API(user_data); @@ -1042,6 +1190,9 @@ void maplibre_map_libre_host_api_set_method_handlers(FlBinaryMessenger* messenge g_autofree gchar* get_camera_channel_name = g_strdup_printf("dev.flutter.pigeon.maplibre.MapLibreHostApi.getCamera%s", dot_suffix); g_autoptr(FlBasicMessageChannel) get_camera_channel = fl_basic_message_channel_new(messenger, get_camera_channel_name, FL_MESSAGE_CODEC(codec)); fl_basic_message_channel_set_message_handler(get_camera_channel, maplibre_map_libre_host_api_get_camera_cb, g_object_ref(api_data), g_object_unref); + g_autofree gchar* get_visible_region_channel_name = g_strdup_printf("dev.flutter.pigeon.maplibre.MapLibreHostApi.getVisibleRegion%s", dot_suffix); + g_autoptr(FlBasicMessageChannel) get_visible_region_channel = fl_basic_message_channel_new(messenger, get_visible_region_channel_name, FL_MESSAGE_CODEC(codec)); + fl_basic_message_channel_set_message_handler(get_visible_region_channel, maplibre_map_libre_host_api_get_visible_region_cb, g_object_ref(api_data), g_object_unref); g_autofree gchar* to_screen_location_channel_name = g_strdup_printf("dev.flutter.pigeon.maplibre.MapLibreHostApi.toScreenLocation%s", dot_suffix); g_autoptr(FlBasicMessageChannel) to_screen_location_channel = fl_basic_message_channel_new(messenger, to_screen_location_channel_name, FL_MESSAGE_CODEC(codec)); fl_basic_message_channel_set_message_handler(to_screen_location_channel, maplibre_map_libre_host_api_to_screen_location_cb, g_object_ref(api_data), g_object_unref); @@ -1075,6 +1226,9 @@ void maplibre_map_libre_host_api_clear_method_handlers(FlBinaryMessenger* messen g_autofree gchar* get_camera_channel_name = g_strdup_printf("dev.flutter.pigeon.maplibre.MapLibreHostApi.getCamera%s", dot_suffix); g_autoptr(FlBasicMessageChannel) get_camera_channel = fl_basic_message_channel_new(messenger, get_camera_channel_name, FL_MESSAGE_CODEC(codec)); fl_basic_message_channel_set_message_handler(get_camera_channel, nullptr, nullptr, nullptr); + g_autofree gchar* get_visible_region_channel_name = g_strdup_printf("dev.flutter.pigeon.maplibre.MapLibreHostApi.getVisibleRegion%s", dot_suffix); + g_autoptr(FlBasicMessageChannel) get_visible_region_channel = fl_basic_message_channel_new(messenger, get_visible_region_channel_name, FL_MESSAGE_CODEC(codec)); + fl_basic_message_channel_set_message_handler(get_visible_region_channel, nullptr, nullptr, nullptr); g_autofree gchar* to_screen_location_channel_name = g_strdup_printf("dev.flutter.pigeon.maplibre.MapLibreHostApi.toScreenLocation%s", dot_suffix); g_autoptr(FlBasicMessageChannel) to_screen_location_channel = fl_basic_message_channel_new(messenger, to_screen_location_channel_name, FL_MESSAGE_CODEC(codec)); fl_basic_message_channel_set_message_handler(to_screen_location_channel, nullptr, nullptr, nullptr); @@ -1143,6 +1297,22 @@ void maplibre_map_libre_host_api_respond_error_get_camera(MaplibreMapLibreHostAp } } +void maplibre_map_libre_host_api_respond_get_visible_region(MaplibreMapLibreHostApiResponseHandle* response_handle, MaplibreLngLatBounds* return_value) { + g_autoptr(MaplibreMapLibreHostApiGetVisibleRegionResponse) response = maplibre_map_libre_host_api_get_visible_region_response_new(return_value); + g_autoptr(GError) error = nullptr; + if (!fl_basic_message_channel_respond(response_handle->channel, response_handle->response_handle, response->value, &error)) { + g_warning("Failed to send response to %s.%s: %s", "MapLibreHostApi", "getVisibleRegion", error->message); + } +} + +void maplibre_map_libre_host_api_respond_error_get_visible_region(MaplibreMapLibreHostApiResponseHandle* response_handle, const gchar* code, const gchar* message, FlValue* details) { + g_autoptr(MaplibreMapLibreHostApiGetVisibleRegionResponse) response = maplibre_map_libre_host_api_get_visible_region_response_new_error(code, message, details); + g_autoptr(GError) error = nullptr; + if (!fl_basic_message_channel_respond(response_handle->channel, response_handle->response_handle, response->value, &error)) { + g_warning("Failed to send response to %s.%s: %s", "MapLibreHostApi", "getVisibleRegion", error->message); + } +} + void maplibre_map_libre_host_api_respond_to_screen_location(MaplibreMapLibreHostApiResponseHandle* response_handle, MaplibreScreenLocation* return_value) { g_autoptr(MaplibreMapLibreHostApiToScreenLocationResponse) response = maplibre_map_libre_host_api_to_screen_location_response_new(return_value); g_autoptr(GError) error = nullptr; diff --git a/linux/pigeon.g.h b/linux/pigeon.g.h index d652b00..59f343e 100644 --- a/linux/pigeon.g.h +++ b/linux/pigeon.g.h @@ -241,6 +241,67 @@ double maplibre_map_camera_get_tilt(MaplibreMapCamera* object); */ double maplibre_map_camera_get_bearing(MaplibreMapCamera* object); +/** + * MaplibreLngLatBounds: + * + * LatLng bound object + */ + +G_DECLARE_FINAL_TYPE(MaplibreLngLatBounds, maplibre_lng_lat_bounds, MAPLIBRE, LNG_LAT_BOUNDS, GObject) + +/** + * maplibre_lng_lat_bounds_new: + * longitude_west: field in this object. + * longitude_east: field in this object. + * latitude_south: field in this object. + * latitude_north: field in this object. + * + * Creates a new #LngLatBounds object. + * + * Returns: a new #MaplibreLngLatBounds + */ +MaplibreLngLatBounds* maplibre_lng_lat_bounds_new(double longitude_west, double longitude_east, double latitude_south, double latitude_north); + +/** + * maplibre_lng_lat_bounds_get_longitude_west + * @object: a #MaplibreLngLatBounds. + * + * Gets the value of the longitudeWest field of @object. + * + * Returns: the field value. + */ +double maplibre_lng_lat_bounds_get_longitude_west(MaplibreLngLatBounds* object); + +/** + * maplibre_lng_lat_bounds_get_longitude_east + * @object: a #MaplibreLngLatBounds. + * + * Gets the value of the longitudeEast field of @object. + * + * Returns: the field value. + */ +double maplibre_lng_lat_bounds_get_longitude_east(MaplibreLngLatBounds* object); + +/** + * maplibre_lng_lat_bounds_get_latitude_south + * @object: a #MaplibreLngLatBounds. + * + * Gets the value of the latitudeSouth field of @object. + * + * Returns: the field value. + */ +double maplibre_lng_lat_bounds_get_latitude_south(MaplibreLngLatBounds* object); + +/** + * maplibre_lng_lat_bounds_get_latitude_north + * @object: a #MaplibreLngLatBounds. + * + * Gets the value of the latitudeNorth field of @object. + * + * Returns: the field value. + */ +double maplibre_lng_lat_bounds_get_latitude_north(MaplibreLngLatBounds* object); + G_DECLARE_FINAL_TYPE(MaplibreMapLibreHostApiResponseHandle, maplibre_map_libre_host_api_response_handle, MAPLIBRE, MAP_LIBRE_HOST_API_RESPONSE_HANDLE, GObject) G_DECLARE_FINAL_TYPE(MaplibreMapLibreHostApiGetMetersPerPixelAtLatitudeResponse, maplibre_map_libre_host_api_get_meters_per_pixel_at_latitude_response, MAPLIBRE, MAP_LIBRE_HOST_API_GET_METERS_PER_PIXEL_AT_LATITUDE_RESPONSE, GObject) @@ -275,6 +336,7 @@ typedef struct { void (*jump_to)(MaplibreLngLat* center, double* zoom, double* bearing, double* pitch, MaplibreMapLibreHostApiResponseHandle* response_handle, gpointer user_data); void (*fly_to)(MaplibreLngLat* center, double* zoom, double* bearing, double* pitch, int64_t duration_ms, MaplibreMapLibreHostApiResponseHandle* response_handle, gpointer user_data); void (*get_camera)(MaplibreMapLibreHostApiResponseHandle* response_handle, gpointer user_data); + void (*get_visible_region)(MaplibreMapLibreHostApiResponseHandle* response_handle, gpointer user_data); void (*to_screen_location)(double lng, double lat, MaplibreMapLibreHostApiResponseHandle* response_handle, gpointer user_data); void (*to_lng_lat)(double x, double y, MaplibreMapLibreHostApiResponseHandle* response_handle, gpointer user_data); void (*add_fill_layer)(const gchar* id, const gchar* source_id, MaplibreMapLibreHostApiResponseHandle* response_handle, gpointer user_data); @@ -364,6 +426,26 @@ void maplibre_map_libre_host_api_respond_get_camera(MaplibreMapLibreHostApiRespo */ void maplibre_map_libre_host_api_respond_error_get_camera(MaplibreMapLibreHostApiResponseHandle* response_handle, const gchar* code, const gchar* message, FlValue* details); +/** + * maplibre_map_libre_host_api_respond_get_visible_region: + * @response_handle: a #MaplibreMapLibreHostApiResponseHandle. + * @return_value: location to write the value returned by this method. + * + * Responds to MapLibreHostApi.getVisibleRegion. + */ +void maplibre_map_libre_host_api_respond_get_visible_region(MaplibreMapLibreHostApiResponseHandle* response_handle, MaplibreLngLatBounds* return_value); + +/** + * maplibre_map_libre_host_api_respond_error_get_visible_region: + * @response_handle: a #MaplibreMapLibreHostApiResponseHandle. + * @code: error code. + * @message: error message. + * @details: (allow-none): error details or %NULL. + * + * Responds with an error to MapLibreHostApi.getVisibleRegion. + */ +void maplibre_map_libre_host_api_respond_error_get_visible_region(MaplibreMapLibreHostApiResponseHandle* response_handle, const gchar* code, const gchar* message, FlValue* details); + /** * maplibre_map_libre_host_api_respond_to_screen_location: * @response_handle: a #MaplibreMapLibreHostApiResponseHandle. diff --git a/macos/Classes/Pigeon.g.swift b/macos/Classes/Pigeon.g.swift index 0106cd1..3f0028d 100644 --- a/macos/Classes/Pigeon.g.swift +++ b/macos/Classes/Pigeon.g.swift @@ -215,6 +215,41 @@ struct MapCamera { } } +/// LatLng bound object +/// +/// Generated class from Pigeon that represents data sent in messages. +struct LngLatBounds { + var longitudeWest: Double + var longitudeEast: Double + var latitudeSouth: Double + var latitudeNorth: Double + + + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> LngLatBounds? { + let longitudeWest = pigeonVar_list[0] as! Double + let longitudeEast = pigeonVar_list[1] as! Double + let latitudeSouth = pigeonVar_list[2] as! Double + let latitudeNorth = pigeonVar_list[3] as! Double + + return LngLatBounds( + longitudeWest: longitudeWest, + longitudeEast: longitudeEast, + latitudeSouth: latitudeSouth, + latitudeNorth: latitudeNorth + ) + } + func toList() -> [Any?] { + return [ + longitudeWest, + longitudeEast, + latitudeSouth, + latitudeNorth, + ] + } +} + private class PigeonPigeonCodecReader: FlutterStandardReader { override func readValue(ofType type: UInt8) -> Any? { switch type { @@ -226,6 +261,8 @@ private class PigeonPigeonCodecReader: FlutterStandardReader { return ScreenLocation.fromList(self.readValue() as! [Any?]) case 132: return MapCamera.fromList(self.readValue() as! [Any?]) + case 133: + return LngLatBounds.fromList(self.readValue() as! [Any?]) default: return super.readValue(ofType: type) } @@ -246,6 +283,9 @@ private class PigeonPigeonCodecWriter: FlutterStandardWriter { } else if let value = value as? MapCamera { super.writeByte(132) super.writeValue(value.toList()) + } else if let value = value as? LngLatBounds { + super.writeByte(133) + super.writeValue(value.toList()) } else { super.writeValue(value) } @@ -276,6 +316,8 @@ protocol MapLibreHostApi { /// Get the current camera position with the map center, zoom level, camera /// tilt and map rotation. func getCamera(completion: @escaping (Result) -> Void) + /// Get the visible region of the current map camera. + func getVisibleRegion(completion: @escaping (Result) -> Void) /// Convert a coordinate to a location on the screen. func toScreenLocation(lng: Double, lat: Double, completion: @escaping (Result) -> Void) /// Convert a screen location to a coordinate. @@ -357,6 +399,22 @@ class MapLibreHostApiSetup { } else { getCameraChannel.setMessageHandler(nil) } + /// Get the visible region of the current map camera. + let getVisibleRegionChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.maplibre.MapLibreHostApi.getVisibleRegion\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + getVisibleRegionChannel.setMessageHandler { _, reply in + api.getVisibleRegion { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + getVisibleRegionChannel.setMessageHandler(nil) + } /// Convert a coordinate to a location on the screen. let toScreenLocationChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.maplibre.MapLibreHostApi.toScreenLocation\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) if let api = api { diff --git a/pigeons/pigeon.dart b/pigeons/pigeon.dart index 53f3ebe..8bbe7a5 100644 --- a/pigeons/pigeon.dart +++ b/pigeons/pigeon.dart @@ -47,6 +47,10 @@ abstract interface class MapLibreHostApi { @async MapCamera getCamera(); + /// Get the visible region of the current map camera. + @async + LngLatBounds getVisibleRegion(); + /// Convert a coordinate to a location on the screen. @async ScreenLocation toScreenLocation(double lng, double lat); @@ -167,3 +171,18 @@ class MapCamera { final double tilt; final double bearing; } + +/// LatLng bound object +class LngLatBounds { + const LngLatBounds({ + required this.longitudeWest, + required this.longitudeEast, + required this.latitudeSouth, + required this.latitudeNorth, + }); + + final double longitudeWest; + final double longitudeEast; + final double latitudeSouth; + final double latitudeNorth; +} diff --git a/windows/runner/pigeon.g.cpp b/windows/runner/pigeon.g.cpp index 266092f..9cb7609 100644 --- a/windows/runner/pigeon.g.cpp +++ b/windows/runner/pigeon.g.cpp @@ -338,6 +338,73 @@ MapCamera MapCamera::FromEncodableList(const EncodableList& list) { return decoded; } +// LngLatBounds + +LngLatBounds::LngLatBounds( + double longitude_west, + double longitude_east, + double latitude_south, + double latitude_north) + : longitude_west_(longitude_west), + longitude_east_(longitude_east), + latitude_south_(latitude_south), + latitude_north_(latitude_north) {} + +double LngLatBounds::longitude_west() const { + return longitude_west_; +} + +void LngLatBounds::set_longitude_west(double value_arg) { + longitude_west_ = value_arg; +} + + +double LngLatBounds::longitude_east() const { + return longitude_east_; +} + +void LngLatBounds::set_longitude_east(double value_arg) { + longitude_east_ = value_arg; +} + + +double LngLatBounds::latitude_south() const { + return latitude_south_; +} + +void LngLatBounds::set_latitude_south(double value_arg) { + latitude_south_ = value_arg; +} + + +double LngLatBounds::latitude_north() const { + return latitude_north_; +} + +void LngLatBounds::set_latitude_north(double value_arg) { + latitude_north_ = value_arg; +} + + +EncodableList LngLatBounds::ToEncodableList() const { + EncodableList list; + list.reserve(4); + list.push_back(EncodableValue(longitude_west_)); + list.push_back(EncodableValue(longitude_east_)); + list.push_back(EncodableValue(latitude_south_)); + list.push_back(EncodableValue(latitude_north_)); + return list; +} + +LngLatBounds LngLatBounds::FromEncodableList(const EncodableList& list) { + LngLatBounds decoded( + std::get(list[0]), + std::get(list[1]), + std::get(list[2]), + std::get(list[3])); + return decoded; +} + PigeonInternalCodecSerializer::PigeonInternalCodecSerializer() {} @@ -357,6 +424,9 @@ EncodableValue PigeonInternalCodecSerializer::ReadValueOfType( case 132: { return CustomEncodableValue(MapCamera::FromEncodableList(std::get(ReadValue(stream)))); } + case 133: { + return CustomEncodableValue(LngLatBounds::FromEncodableList(std::get(ReadValue(stream)))); + } default: return flutter::StandardCodecSerializer::ReadValueOfType(type, stream); } @@ -386,6 +456,11 @@ void PigeonInternalCodecSerializer::WriteValue( WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); return; } + if (custom_value->type() == typeid(LngLatBounds)) { + stream->WriteByte(133); + WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); + return; + } } flutter::StandardCodecSerializer::WriteValue(value, stream); } @@ -497,6 +572,28 @@ void MapLibreHostApi::SetUp( channel.SetMessageHandler(nullptr); } } + { + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.maplibre.MapLibreHostApi.getVisibleRegion" + prepended_suffix, &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + api->GetVisibleRegion([reply](ErrorOr&& output) { + if (output.has_error()) { + reply(WrapError(output.error())); + return; + } + EncodableList wrapped; + wrapped.push_back(CustomEncodableValue(std::move(output).TakeValue())); + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } { BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.maplibre.MapLibreHostApi.toScreenLocation" + prepended_suffix, &GetCodec()); if (api != nullptr) { diff --git a/windows/runner/pigeon.g.h b/windows/runner/pigeon.g.h index 19b8c7d..9080da0 100644 --- a/windows/runner/pigeon.g.h +++ b/windows/runner/pigeon.g.h @@ -242,6 +242,45 @@ class MapCamera { }; +// LatLng bound object +// +// Generated class from Pigeon that represents data sent in messages. +class LngLatBounds { + public: + // Constructs an object setting all fields. + explicit LngLatBounds( + double longitude_west, + double longitude_east, + double latitude_south, + double latitude_north); + + double longitude_west() const; + void set_longitude_west(double value_arg); + + double longitude_east() const; + void set_longitude_east(double value_arg); + + double latitude_south() const; + void set_latitude_south(double value_arg); + + double latitude_north() const; + void set_latitude_north(double value_arg); + + + private: + static LngLatBounds FromEncodableList(const flutter::EncodableList& list); + flutter::EncodableList ToEncodableList() const; + friend class MapLibreHostApi; + friend class MapLibreFlutterApi; + friend class PigeonInternalCodecSerializer; + double longitude_west_; + double longitude_east_; + double latitude_south_; + double latitude_north_; + +}; + + class PigeonInternalCodecSerializer : public flutter::StandardCodecSerializer { public: PigeonInternalCodecSerializer(); @@ -285,6 +324,8 @@ class MapLibreHostApi { // Get the current camera position with the map center, zoom level, camera // tilt and map rotation. virtual void GetCamera(std::function reply)> result) = 0; + // Get the visible region of the current map camera. + virtual void GetVisibleRegion(std::function reply)> result) = 0; // Convert a coordinate to a location on the screen. virtual void ToScreenLocation( double lng, From a38ef55252d9fe77960f781aad0a9e1430e973e4 Mon Sep 17 00:00:00 2001 From: Joscha <34318751+josxha@users.noreply.github.com> Date: Mon, 16 Sep 2024 00:35:58 +0200 Subject: [PATCH 22/37] fix export --- lib/maplibre.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/maplibre.dart b/lib/maplibre.dart index 2a9393a..4dc8023 100644 --- a/lib/maplibre.dart +++ b/lib/maplibre.dart @@ -3,8 +3,8 @@ export 'package:geotypes/geotypes.dart'; export 'src/annotations.dart'; -export 'src/common.dart'; export 'src/layers.dart'; +export 'src/lng_lat_bounds.dart'; export 'src/map.dart'; export 'src/map_camera.dart'; export 'src/map_controller.dart'; From 523432b61bd4076538d2697df948783b3246457a Mon Sep 17 00:00:00 2001 From: Joscha <34318751+josxha@users.noreply.github.com> Date: Mon, 16 Sep 2024 00:39:45 +0200 Subject: [PATCH 23/37] fix android package, update changelog --- CHANGELOG.md | 7 +++++++ .../com/github/josxha/maplibre/MapLibreMapController.kt | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6684d6..3881de7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ ## unreleased +### Features + - add `duration` parameter to `flyTo()` +- `flyTo()` returns after the animation completes or throws an exception if it + has been cancelled + +### Bug fixes + - fix `jumpTo()` never returns ## 0.0.1+1 diff --git a/android/src/main/kotlin/com/github/josxha/maplibre/MapLibreMapController.kt b/android/src/main/kotlin/com/github/josxha/maplibre/MapLibreMapController.kt index 824e75e..60519a4 100644 --- a/android/src/main/kotlin/com/github/josxha/maplibre/MapLibreMapController.kt +++ b/android/src/main/kotlin/com/github/josxha/maplibre/MapLibreMapController.kt @@ -1,4 +1,4 @@ -package com.githb.josxha.maplibre +package com.github.josxha.maplibre import LngLat import LngLatBounds From f2f2201f865c1c92ffd3d324b276db91b0618c30 Mon Sep 17 00:00:00 2001 From: Joscha <34318751+josxha@users.noreply.github.com> Date: Mon, 16 Sep 2024 00:46:58 +0200 Subject: [PATCH 24/37] Update smoke_test.dart --- example/integration_test/smoke_test.dart | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/example/integration_test/smoke_test.dart b/example/integration_test/smoke_test.dart index 4f24b70..8142d42 100644 --- a/example/integration_test/smoke_test.dart +++ b/example/integration_test/smoke_test.dart @@ -111,5 +111,20 @@ void main() { ); }, ); + testWidgets( + 'getVisibleRegion', + (tester) async { + late final MapController ctrl; + final app = App(onMapCreated: (controller) => ctrl = controller); + await tester.pumpWidget(app); + await tester.pumpAndSettle(); + final region = await ctrl.getVisibleRegion(); + // TODO adjust values + expect(region.latitudeNorth, closeTo(12345, 0.00001)); + expect(region.latitudeSouth, closeTo(12345, 0.00001)); + expect(region.longitudeEast, closeTo(12345, 0.00001)); + expect(region.longitudeWest, closeTo(12345, 0.00001)); + }, + ); }); } From 34769943ec5ce476d35764f7ca6c12444e265e1b Mon Sep 17 00:00:00 2001 From: Joscha <34318751+josxha@users.noreply.github.com> Date: Mon, 16 Sep 2024 00:58:25 +0200 Subject: [PATCH 25/37] disable flyTo cancel test --- example/integration_test/smoke_test.dart | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/example/integration_test/smoke_test.dart b/example/integration_test/smoke_test.dart index 8142d42..aaddab3 100644 --- a/example/integration_test/smoke_test.dart +++ b/example/integration_test/smoke_test.dart @@ -78,26 +78,28 @@ void main() { expect(camera.tilt, closeTo(2, 0.00001)); }, ); - testWidgets( + /*testWidgets( 'flyTo cancel', (tester) async { late final MapController ctrl; final app = App(onMapCreated: (controller) => ctrl = controller); await tester.pumpWidget(app); await tester.pumpAndSettle(); + final future = ctrl.flyTo( + center: Position(2, 2), + bearing: 2, + zoom: 2, + tilt: 2, + webSpeed: 0.1, + nativeDuration: const Duration(days: 1), + ); + // TODO perform gesture await expectLater( - ctrl.flyTo( - center: Position(2, 2), - bearing: 2, - zoom: 2, - tilt: 2, - webSpeed: 0.1, - nativeDuration: const Duration(days: 1), - ), + future, throwsA(isA()), ); }, - ); + );*/ testWidgets( 'getMetersPerPixelAtLatitude', (tester) async { From cbd689c46f7ea4bebf3e6be788a1b3eb03245309 Mon Sep 17 00:00:00 2001 From: Joscha <34318751+josxha@users.noreply.github.com> Date: Mon, 16 Sep 2024 01:09:15 +0200 Subject: [PATCH 26/37] Update smoke_test.dart --- example/integration_test/smoke_test.dart | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/example/integration_test/smoke_test.dart b/example/integration_test/smoke_test.dart index aaddab3..35fafbb 100644 --- a/example/integration_test/smoke_test.dart +++ b/example/integration_test/smoke_test.dart @@ -1,4 +1,3 @@ -import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:maplibre/maplibre.dart'; @@ -11,7 +10,7 @@ void main() { group('controller', () { testWidgets( 'render map', - (tester) async { + (tester) async { await tester.pumpWidget(const App()); await tester.pumpAndSettle(); expect(tester.allWidgets.any((w) => w is MapLibreMap), isTrue); @@ -19,7 +18,7 @@ void main() { ); testWidgets( 'getCamera', - (tester) async { + (tester) async { late final MapController ctrl; final app = App( onMapCreated: (controller) => ctrl = controller, @@ -39,7 +38,7 @@ void main() { ); testWidgets( 'jumpTo', - (tester) async { + (tester) async { late final MapController ctrl; final app = App(onMapCreated: (controller) => ctrl = controller); await tester.pumpWidget(app); @@ -56,7 +55,7 @@ void main() { ); testWidgets( 'flyTo', - (tester) async { + (tester) async { late final MapController ctrl; final app = App(onMapCreated: (controller) => ctrl = controller); await tester.pumpWidget(app); @@ -102,27 +101,27 @@ void main() { );*/ testWidgets( 'getMetersPerPixelAtLatitude', - (tester) async { + (tester) async { late final MapController ctrl; final app = App(onMapCreated: (controller) => ctrl = controller); await tester.pumpWidget(app); await tester.pumpAndSettle(); - await expectLater( - ctrl.getMetersPerPixelAtLatitude(23), - closeTo(12345, 0.00001), // TODO adjust value - ); + final meters = await ctrl.getMetersPerPixelAtLatitude(23); + // TODO adjust value + expect(meters, closeTo(12345, 0.00001)); }, ); testWidgets( 'getVisibleRegion', - (tester) async { + (tester) async { late final MapController ctrl; final app = App(onMapCreated: (controller) => ctrl = controller); await tester.pumpWidget(app); await tester.pumpAndSettle(); final region = await ctrl.getVisibleRegion(); // TODO adjust values - expect(region.latitudeNorth, closeTo(12345, 0.00001)); + expect(region.latitudeNorth, closeTo(85.05112862791722, 0.00001)); + print(region); expect(region.latitudeSouth, closeTo(12345, 0.00001)); expect(region.longitudeEast, closeTo(12345, 0.00001)); expect(region.longitudeWest, closeTo(12345, 0.00001)); From 837d8643c2d7d1f824a917c39270cf76b683a3cd Mon Sep 17 00:00:00 2001 From: Joscha <34318751+josxha@users.noreply.github.com> Date: Mon, 16 Sep 2024 01:21:08 +0200 Subject: [PATCH 27/37] Update smoke_test.dart --- example/integration_test/smoke_test.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/example/integration_test/smoke_test.dart b/example/integration_test/smoke_test.dart index 35fafbb..19bf785 100644 --- a/example/integration_test/smoke_test.dart +++ b/example/integration_test/smoke_test.dart @@ -98,7 +98,7 @@ void main() { throwsA(isA()), ); }, - );*/ + ); testWidgets( 'getMetersPerPixelAtLatitude', (tester) async { @@ -121,11 +121,10 @@ void main() { final region = await ctrl.getVisibleRegion(); // TODO adjust values expect(region.latitudeNorth, closeTo(85.05112862791722, 0.00001)); - print(region); expect(region.latitudeSouth, closeTo(12345, 0.00001)); expect(region.longitudeEast, closeTo(12345, 0.00001)); expect(region.longitudeWest, closeTo(12345, 0.00001)); }, - ); + );*/ }); } From d06d4115466100c877ca6eaddcec6c7b5966dc29 Mon Sep 17 00:00:00 2001 From: Joscha <34318751+josxha@users.noreply.github.com> Date: Mon, 16 Sep 2024 01:39:25 +0200 Subject: [PATCH 28/37] Update smoke_test.dart --- example/integration_test/smoke_test.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/example/integration_test/smoke_test.dart b/example/integration_test/smoke_test.dart index 19bf785..039db86 100644 --- a/example/integration_test/smoke_test.dart +++ b/example/integration_test/smoke_test.dart @@ -10,7 +10,7 @@ void main() { group('controller', () { testWidgets( 'render map', - (tester) async { + (tester) async { await tester.pumpWidget(const App()); await tester.pumpAndSettle(); expect(tester.allWidgets.any((w) => w is MapLibreMap), isTrue); @@ -18,7 +18,7 @@ void main() { ); testWidgets( 'getCamera', - (tester) async { + (tester) async { late final MapController ctrl; final app = App( onMapCreated: (controller) => ctrl = controller, @@ -38,7 +38,7 @@ void main() { ); testWidgets( 'jumpTo', - (tester) async { + (tester) async { late final MapController ctrl; final app = App(onMapCreated: (controller) => ctrl = controller); await tester.pumpWidget(app); @@ -55,7 +55,7 @@ void main() { ); testWidgets( 'flyTo', - (tester) async { + (tester) async { late final MapController ctrl; final app = App(onMapCreated: (controller) => ctrl = controller); await tester.pumpWidget(app); From 99d545f4163d1b676b35e0bd52b9b82c8e44200c Mon Sep 17 00:00:00 2001 From: Joscha <34318751+josxha@users.noreply.github.com> Date: Mon, 16 Sep 2024 10:00:21 +0200 Subject: [PATCH 29/37] Update smoke_test.dart --- example/integration_test/smoke_test.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/example/integration_test/smoke_test.dart b/example/integration_test/smoke_test.dart index 039db86..dc61504 100644 --- a/example/integration_test/smoke_test.dart +++ b/example/integration_test/smoke_test.dart @@ -53,7 +53,7 @@ void main() { expect(camera.tilt, closeTo(1, 0.00001)); }, ); - testWidgets( + /*testWidgets( 'flyTo', (tester) async { late final MapController ctrl; @@ -77,7 +77,7 @@ void main() { expect(camera.tilt, closeTo(2, 0.00001)); }, ); - /*testWidgets( + testWidgets( 'flyTo cancel', (tester) async { late final MapController ctrl; From 2947990690117ef89adc6e97da36a7a2fe047630 Mon Sep 17 00:00:00 2001 From: Joscha <34318751+josxha@users.noreply.github.com> Date: Mon, 16 Sep 2024 10:36:58 +0200 Subject: [PATCH 30/37] disable integration-test-android ci find something more reliable --- .github/workflows/ci.yml | 62 ++++++++++++------------ example/integration_test/smoke_test.dart | 5 +- 2 files changed, 34 insertions(+), 33 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c787c70..6e6162b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,37 +50,37 @@ jobs: # cache: true # - run: flutter pub get # - run: flutter test integration_test --no-pub -r expanded - integration-test-android: - name: "Integration Tests on Android" - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - api-level: [ 23 ] # TODO add 34 - timeout-minutes: 30 - defaults: - run: - working-directory: example - steps: - - uses: actions/checkout@v4 - - name: Setup Flutter SDK - uses: subosito/flutter-action@v2 - with: - cache: true - - uses: actions/setup-java@v4 - with: - java-version: '17' - distribution: 'temurin' - cache: 'gradle' - - name: "Get Flutter dependencies" - run: flutter pub get - - name: Run integration tests - uses: reactivecircus/android-emulator-runner@v2 - with: - api-level: ${{ matrix.api-level }} - arch: x86_64 - emulator-boot-timeout: 1800 # 30 minutes - script: cd example && flutter test integration_test -r expanded --timeout=none +# integration-test-android: +# name: "Integration Tests on Android" +# runs-on: ubuntu-latest +# strategy: +# fail-fast: false +# matrix: +# api-level: [ 23 ] # TODO add 34 +# timeout-minutes: 30 +# defaults: +# run: +# working-directory: example +# steps: +# - uses: actions/checkout@v4 +# - name: Setup Flutter SDK +# uses: subosito/flutter-action@v2 +# with: +# cache: true +# - uses: actions/setup-java@v4 +# with: +# java-version: '17' +# distribution: 'temurin' +# cache: 'gradle' +# - name: "Get Flutter dependencies" +# run: flutter pub get +# - name: Run integration tests +# uses: reactivecircus/android-emulator-runner@v2 +# with: +# api-level: ${{ matrix.api-level }} +# arch: x86_64 +# emulator-boot-timeout: 1800 # 30 minutes +# script: cd example && flutter test integration_test -r expanded --timeout=none integration-test-web: name: "Integration Tests on Web" runs-on: ubuntu-latest diff --git a/example/integration_test/smoke_test.dart b/example/integration_test/smoke_test.dart index dc61504..c645447 100644 --- a/example/integration_test/smoke_test.dart +++ b/example/integration_test/smoke_test.dart @@ -1,3 +1,4 @@ +import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:maplibre/maplibre.dart'; @@ -53,7 +54,7 @@ void main() { expect(camera.tilt, closeTo(1, 0.00001)); }, ); - /*testWidgets( + testWidgets( 'flyTo', (tester) async { late final MapController ctrl; @@ -125,6 +126,6 @@ void main() { expect(region.longitudeEast, closeTo(12345, 0.00001)); expect(region.longitudeWest, closeTo(12345, 0.00001)); }, - );*/ + ); }); } From 503bface203d38f8b4ff0a4342848611586b9534 Mon Sep 17 00:00:00 2001 From: Joscha <34318751+josxha@users.noreply.github.com> Date: Mon, 16 Sep 2024 10:45:00 +0200 Subject: [PATCH 31/37] Update smoke_test.dart --- example/integration_test/smoke_test.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/example/integration_test/smoke_test.dart b/example/integration_test/smoke_test.dart index c645447..04f97f6 100644 --- a/example/integration_test/smoke_test.dart +++ b/example/integration_test/smoke_test.dart @@ -102,7 +102,7 @@ void main() { ); testWidgets( 'getMetersPerPixelAtLatitude', - (tester) async { + (tester) async { late final MapController ctrl; final app = App(onMapCreated: (controller) => ctrl = controller); await tester.pumpWidget(app); @@ -114,7 +114,7 @@ void main() { ); testWidgets( 'getVisibleRegion', - (tester) async { + (tester) async { late final MapController ctrl; final app = App(onMapCreated: (controller) => ctrl = controller); await tester.pumpWidget(app); From 901f682e0f155acf4cb2d1bab6885e66eaf49248 Mon Sep 17 00:00:00 2001 From: Joscha <34318751+josxha@users.noreply.github.com> Date: Mon, 16 Sep 2024 10:57:00 +0200 Subject: [PATCH 32/37] add kotlin test file --- .../maplibre/MapLibreMapControllerTest.kt | 64 +++++++++++++++++++ ...brePluginTest.kt => MapLibrePluginTest.kt} | 14 +--- 2 files changed, 67 insertions(+), 11 deletions(-) create mode 100644 android/src/test/kotlin/com/github/josxha/maplibre/MapLibreMapControllerTest.kt rename android/src/test/kotlin/com/github/josxha/maplibre/{MaplibrePluginTest.kt => MapLibrePluginTest.kt} (56%) diff --git a/android/src/test/kotlin/com/github/josxha/maplibre/MapLibreMapControllerTest.kt b/android/src/test/kotlin/com/github/josxha/maplibre/MapLibreMapControllerTest.kt new file mode 100644 index 0000000..6cb5bf1 --- /dev/null +++ b/android/src/test/kotlin/com/github/josxha/maplibre/MapLibreMapControllerTest.kt @@ -0,0 +1,64 @@ +package com.github.josxha.maplibre + +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +internal class MapLibreMapControllerTest { + + @BeforeEach + fun setUp() { + } + + @AfterEach + fun tearDown() { + } + + @Test + fun jumpTo() { + } + + @Test + fun flyTo() { + } + + @Test + fun toScreenLocation() { + } + + @Test + fun toLngLat() { + } + + @Test + fun getCamera() { + } + + @Test + fun getVisibleRegion() { + } + + @Test + fun addFillLayer() { + } + + @Test + fun addCircleLayer() { + } + + @Test + fun addGeoJsonSource() { + } + + @Test + fun getMetersPerPixelAtLatitude() { + } + + @Test + fun onMapClick() { + } + + @Test + fun onMapLongClick() { + } +} \ No newline at end of file diff --git a/android/src/test/kotlin/com/github/josxha/maplibre/MaplibrePluginTest.kt b/android/src/test/kotlin/com/github/josxha/maplibre/MapLibrePluginTest.kt similarity index 56% rename from android/src/test/kotlin/com/github/josxha/maplibre/MaplibrePluginTest.kt rename to android/src/test/kotlin/com/github/josxha/maplibre/MapLibrePluginTest.kt index 67a8061..2423c69 100644 --- a/android/src/test/kotlin/com/github/josxha/maplibre/MaplibrePluginTest.kt +++ b/android/src/test/kotlin/com/github/josxha/maplibre/MapLibrePluginTest.kt @@ -5,16 +5,8 @@ import io.flutter.plugin.common.MethodChannel import kotlin.test.Test import org.mockito.Mockito -/* - * This demonstrates a simple unit test of the Kotlin portion of this plugin's implementation. - * - * Once you have built the plugin's example app, you can run these tests from the command - * line by running `./gradlew testDebugUnitTest` in the `example/android/` directory, or - * you can run them directly from IDEs that support JUnit such as Android Studio. - */ - -internal class MaplibrePluginTest { - @Test +internal class MapLibrePluginTest { + /*@Test fun onMethodCall_getPlatformVersion_returnsExpectedValue() { val plugin = MaplibrePlugin() @@ -23,5 +15,5 @@ internal class MaplibrePluginTest { plugin.onMethodCall(call, mockResult) Mockito.verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE) - } + }*/ } From d70a2bf6ba8aa068b1afcd4acf29d4167f8760bd Mon Sep 17 00:00:00 2001 From: Joscha <34318751+josxha@users.noreply.github.com> Date: Mon, 16 Sep 2024 10:58:05 +0200 Subject: [PATCH 33/37] Update smoke_test.dart --- example/integration_test/smoke_test.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/example/integration_test/smoke_test.dart b/example/integration_test/smoke_test.dart index 04f97f6..c06e476 100644 --- a/example/integration_test/smoke_test.dart +++ b/example/integration_test/smoke_test.dart @@ -78,7 +78,7 @@ void main() { expect(camera.tilt, closeTo(2, 0.00001)); }, ); - testWidgets( + /*testWidgets( 'flyTo cancel', (tester) async { late final MapController ctrl; @@ -126,6 +126,6 @@ void main() { expect(region.longitudeEast, closeTo(12345, 0.00001)); expect(region.longitudeWest, closeTo(12345, 0.00001)); }, - ); + );*/ }); } From f6e306688212487932bb0e3319d80ffd586e01fd Mon Sep 17 00:00:00 2001 From: Joscha <34318751+josxha@users.noreply.github.com> Date: Mon, 16 Sep 2024 10:59:31 +0200 Subject: [PATCH 34/37] Update smoke_test.dart --- example/integration_test/smoke_test.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/example/integration_test/smoke_test.dart b/example/integration_test/smoke_test.dart index c06e476..ebdfcd5 100644 --- a/example/integration_test/smoke_test.dart +++ b/example/integration_test/smoke_test.dart @@ -1,4 +1,3 @@ -import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:maplibre/maplibre.dart'; From 47fa3f1a99b56ef09d1aa7c9ad0050b2b92f63f4 Mon Sep 17 00:00:00 2001 From: Joscha <34318751+josxha@users.noreply.github.com> Date: Mon, 16 Sep 2024 12:29:04 +0200 Subject: [PATCH 35/37] add code generator script --- README.md | 18 ++++++++++++++---- pigeons/run_code_gen.ps1 | 16 ++++++++++++++++ pigeons/run_code_gen.sh | 12 ++++++++++++ 3 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 pigeons/run_code_gen.ps1 create mode 100644 pigeons/run_code_gen.sh diff --git a/README.md b/README.md index 3fda5e7..b39a177 100644 --- a/README.md +++ b/README.md @@ -65,13 +65,23 @@ welcome. #### Run Code Generation +We use code generation from [pigeon](https://pub.dev/packages/pigeon). +If you change the file [pigeons/pigeon.dart](pigeons/pigeon.dart) you'll have +to run the code generator. +Use the generator script (or run the commands yourself) to generate the code. + +On unix systems (macos, linux): ```bash -flutter pub global activate pigeon # only once -dart run pigeon --input pigeons/pigeon.dart -cp ios/Classes/Pigeon.g.swift macos/Classes/Pigeon.g.swift -dart format . +./pigeon/run.sh ``` +On windows systems: + +```powershell +.\pigeon\run.sh +``` + + #### Test with WebAssembly This package supports WebAssembly builds! 🥳 diff --git a/pigeons/run_code_gen.ps1 b/pigeons/run_code_gen.ps1 new file mode 100644 index 0000000..695b8ed --- /dev/null +++ b/pigeons/run_code_gen.ps1 @@ -0,0 +1,16 @@ +# Get the list of globally installed Flutter packages +$packages = flutter pub global list + +# Check if "pigeon" is installed +if ($packages -match "pigeon") { + Write-Host "pigeon installed" +} else { + flutter pub global activate pigeon +} + +# Run pigeon and format Dart files +dart run pigeon --input pigeons/pigeon.dart +dart format . + +# Copy file from ios to macos directory +Copy-Item ios/Classes/Pigeon.g.swift -Destination macos/Classes/Pigeon.g.swift \ No newline at end of file diff --git a/pigeons/run_code_gen.sh b/pigeons/run_code_gen.sh new file mode 100644 index 0000000..2378ca0 --- /dev/null +++ b/pigeons/run_code_gen.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +packages=$(flutter pub global list) +if echo "$packages" | grep -q "pigeon"; then + echo "pigeon installed" +else + flutter pub global activate pigeon # only once +fi + +dart run pigeon --input pigeons/pigeon.dart +dart format . +cp ios/Classes/Pigeon.g.swift macos/Classes/Pigeon.g.swift From 89e6ed3a36a84c0bdfc638b8c3966e853552d014 Mon Sep 17 00:00:00 2001 From: Joscha <34318751+josxha@users.noreply.github.com> Date: Mon, 16 Sep 2024 12:35:55 +0200 Subject: [PATCH 36/37] Update CONTRIBUTING.md --- CONTRIBUTING.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0111249..18a6082 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -35,7 +35,11 @@ you're interested in making contributions: 4. If there are any changes that developers should be aware of, please update the [CHANGELOG.md](https://github.com/josxha/flutter-maplibre/blob/main/CHANGELOG.md) file together with your pull request. -5. If you have a pull request that isn't complete yet and want to get +5. If you change the [./pigeons/pigeon.dart](./pigeons/pigeon.dart) file, you'll + have to run the [pigeon](https://pub.dev/packages/pigeon) code generation. + You can use [./pigeons/run_code_gen.sh](./pigeons/run_code_gen.sh) + or [./pigeons/run_code_gen.ps1](./pigeons/run_code_gen.ps1). +6. If you have a pull request that isn't complete yet and want to get feedback, consider to [open a draft pull request](https://github.com/josxha/flutter-maplibre/pulls). This helps others to get @@ -43,7 +47,7 @@ you're interested in making contributions: open issues. Give a quick summary about your changes listing any related issues that exist. Screenshots and videos are or course welcome, too. Use a [conventional](https://www.conventionalcommits.org/) title if you like. -6. When your contribution is ready to review, disable the draft state of your +7. When your contribution is ready to review, disable the draft state of your pull request and update the summary by editing your initial pull request message. This summary will go into the commit details of the squashed commit. From b2d83a6a087ebbc4bc953a0426df45a089b167e3 Mon Sep 17 00:00:00 2001 From: Joscha <34318751+josxha@users.noreply.github.com> Date: Mon, 16 Sep 2024 12:40:38 +0200 Subject: [PATCH 37/37] Update dependabot.yml --- .github/dependabot.yml | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 9eddbb2..1581ec4 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,17 +1,14 @@ version: 2 enable-beta-ecosystems: true updates: - - package-ecosystem: "pub" + - package-ecosystem: "github-actions" directory: "/" schedule: interval: "daily" - ignore: - - dependency-name: "*" - update-types: - - "version-update:semver-minor" - - "version-update:semver-patch" - package-ecosystem: "pub" - directory: "/example" + directories: + - "/" + - "/example" schedule: interval: "daily" ignore: @@ -19,10 +16,6 @@ updates: update-types: - "version-update:semver-minor" - "version-update:semver-patch" - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "weekly" - package-ecosystem: "npm" directory: "/docs" schedule: @@ -30,4 +23,10 @@ updates: open-pull-requests-limit: 10 groups: dependencies: - patterns: [ "*" ] \ No newline at end of file + patterns: [ "*" ] + - package-ecosystem: "gradle" + directories: + - "/android" + - "/example/android" + schedule: + interval: "daily"