diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 9eddbb27..1581ec46 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" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c787c707..6e6162b7 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/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 00000000..a62a26cd --- /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 diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d049671..3881de74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,18 @@ ## 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 -- fix urls to website -- fix urls of screenshots +- fix urls to website and embedded screenshots - remove unused `plugin_platform_interface` dependency ## 0.0.1 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0111249d..18a60822 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. diff --git a/README.md b/README.md index 3fda5e75..b39a177e 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/analysis_options.yaml b/analysis_options.yaml index 14872513..16c10983 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/android/build.gradle b/android/build.gradle index 77acfbdd..7241f08a 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' 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 aabad5de..60519a4d 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,8 @@ package com.github.josxha.maplibre import LngLat +import LngLatBounds +import MapCamera import MapLibreFlutterApi import MapLibreHostApi import MapOptions @@ -77,8 +79,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 @@ -146,6 +152,25 @@ 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 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)) @@ -169,6 +194,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 fdc2e16c..12ea0066 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") @@ -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,68 @@ 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, + ) + } +} + +/** + * 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) { @@ -164,6 +226,16 @@ private open class PigeonPigeonCodec : StandardMessageCodec() { ScreenLocation.fromList(it) } } + 132.toByte() -> { + return (readValue(buffer) as? List)?.let { + MapCamera.fromList(it) + } + } + 133.toByte() -> { + return (readValue(buffer) as? List)?.let { + LngLatBounds.fromList(it) + } + } else -> super.readValueOfType(type, buffer) } } @@ -181,6 +253,14 @@ private open class PigeonPigeonCodec : StandardMessageCodec() { stream.write(131) writeValue(stream, value.toList()) } + is MapCamera -> { + stream.write(132) + writeValue(stream, value.toList()) + } + is LngLatBounds -> { + stream.write(133) + writeValue(stream, value.toList()) + } else -> super.writeValue(stream, value) } } @@ -193,6 +273,13 @@ 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) + /** 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. */ @@ -203,6 +290,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. */ @@ -258,6 +350,42 @@ 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.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) { @@ -360,6 +488,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/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 00000000..6cb5bf10 --- /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 67a8061d..2423c695 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) - } + }*/ } diff --git a/docs/docs/features/supported-features.md b/docs/docs/features/supported-features.md index 8c6f0205..136517f3 100644 --- a/docs/docs/features/supported-features.md +++ b/docs/docs/features/supported-features.md @@ -4,38 +4,60 @@ 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 +This side provides a broad orientation about what functionality could and what +functionality is already added. + +### 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 | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | +| getVisibleRegion | ✅ | ✅ | ❌ | ➖ | ➖ | ➖ | \ 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 d4ab1d91..ddaf74dc 100644 --- a/docs/docs/getting-started/setup-android.md +++ b/docs/docs/getting-started/setup-android.md @@ -41,10 +41,20 @@ 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 +:::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. ```gradle title="android/app/build.gradle" @@ -61,15 +71,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 ``` diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index 10fed1e1..11f57b86 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -42,4 +42,7 @@ + + + diff --git a/example/integration_test/app.dart b/example/integration_test/app.dart index 5b476c36..049b9eb1 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 90eb968a..ebdfcd56 100644 --- a/example/integration_test/smoke_test.dart +++ b/example/integration_test/smoke_test.dart @@ -11,16 +11,120 @@ 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), isTrue); + }, + ); + testWidgets( + 'getCamera', + (tester) async { late final MapController ctrl; 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); + await tester.pumpAndSettle(); + final camera = await ctrl.getCamera(); + 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( + '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 { + late final MapController ctrl; + final app = App(onMapCreated: (controller) => ctrl = controller); + await tester.pumpWidget(app); + await tester.pumpAndSettle(); + await ctrl.flyTo( + center: Position(2, 1), + bearing: 2, + zoom: 2, + tilt: 2, + webSpeed: 100, + nativeDuration: Duration.zero, + ); + await tester.pumpAndSettle(); + final camera = await ctrl.getCamera(); + expect(camera.center.lng, 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(); + 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( + future, + throwsA(isA()), ); + }, + ); + testWidgets( + 'getMetersPerPixelAtLatitude', + (tester) async { + late final MapController ctrl; + 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); + final meters = await ctrl.getMetersPerPixelAtLatitude(23); + // TODO adjust value + expect(meters, closeTo(12345, 0.00001)); }, ); + 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(85.05112862791722, 0.00001)); + expect(region.latitudeSouth, closeTo(12345, 0.00001)); + expect(region.longitudeEast, closeTo(12345, 0.00001)); + expect(region.longitudeWest, closeTo(12345, 0.00001)); + }, + );*/ }); } diff --git a/example/lib/controller_page.dart b/example/lib/controller_page.dart index 9eaffde8..668312bc 100644 --- a/example/lib/controller_page.dart +++ b/example/lib/controller_page.dart @@ -63,6 +63,68 @@ 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.lng: ${camera.center.lng} +center.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('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'), + ), + 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/example/lib/main.dart b/example/lib/main.dart index a2d3095d..7ba170fb 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 bd515fa4..5cc0890e 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'; @@ -26,11 +25,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, diff --git a/ios/Classes/Pigeon.g.swift b/ios/Classes/Pigeon.g.swift index bf0bbaa9..3f0028de 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 @@ -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,76 @@ 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, + ] + } +} + +/// 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 { @@ -189,6 +259,10 @@ 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?]) + case 133: + return LngLatBounds.fromList(self.readValue() as! [Any?]) default: return super.readValue(ofType: type) } @@ -206,6 +280,12 @@ 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 if let value = value as? LngLatBounds { + super.writeByte(133) + super.writeValue(value.toList()) } else { super.writeValue(value) } @@ -233,6 +313,11 @@ 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) + /// 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. @@ -243,6 +328,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`. @@ -294,6 +382,39 @@ 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) + } + /// 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 { @@ -389,6 +510,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 a5a67b6c..4dc8023c 100644 --- a/lib/maplibre.dart +++ b/lib/maplibre.dart @@ -4,7 +4,10 @@ export 'package:geotypes/geotypes.dart'; export 'src/annotations.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'; export 'src/map_options.dart'; export 'src/sources.dart'; +export 'src/utils.dart'; diff --git a/lib/src/lng_lat_bounds.dart b/lib/src/lng_lat_bounds.dart new file mode 100644 index 00000000..a7d9d3fc --- /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_camera.dart b/lib/src/map_camera.dart new file mode 100644 index 00000000..6cc01fd3 --- /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 4f6ad8d4..9c4ec5ff 100644 --- a/lib/src/map_controller.dart +++ b/lib/src/map_controller.dart @@ -40,4 +40,18 @@ 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(); + + /// 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); + + /// 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 29fe3126..0e1e27e7 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 @@ -85,7 +85,7 @@ class MapOptions { } } -/// A longitude/latitude coordinate object +/// A longitude/latitude coordinate object. class LngLat { LngLat({ required this.lng, @@ -114,7 +114,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 +143,80 @@ 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, + ); + } +} + +/// 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 @@ -159,6 +233,12 @@ class _PigeonCodec extends StandardMessageCodec { } 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 if (value is LngLatBounds) { + buffer.putUint8(133); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -173,6 +253,10 @@ class _PigeonCodec extends StandardMessageCodec { return LngLat.decode(readValue(buffer)!); case 131: 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); } @@ -256,6 +340,67 @@ 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?)!; + } + } + + /// 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 = @@ -393,6 +538,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 074dab1a..9206e30d 100644 --- a/lib/src/native/widget_state.dart +++ b/lib/src/native/widget_state.dart @@ -147,4 +147,30 @@ 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, + ); + } + + @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/utils.dart b/lib/src/utils.dart new file mode 100644 index 00000000..7d28db0c --- /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/interop/events.dart b/lib/src/web/interop/events.dart index b4903cf0..9c1ee5a7 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/interop/map.dart b/lib/src/web/interop/map.dart index 45d7a0dd..d382bd45 100644 --- a/lib/src/web/interop/map.dart +++ b/lib/src/web/interop/map.dart @@ -43,6 +43,25 @@ 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(); + + /// 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]. @@ -80,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 23cc4bfb..f1795e15 100644 --- a/lib/src/web/widget_state.dart +++ b/lib/src/web/widget_state.dart @@ -1,6 +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'; @@ -15,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; @@ -124,6 +128,13 @@ final class MapLibreMapStateWeb extends State }.toJS, ); } + _map.on( + interop.MapEventType.moveEnd, + (interop.MapLibreEvent event) { + if (_moveCompleter?.isCompleted ?? true) return; + _moveCompleter?.complete(event); + }.toJS, + ); return _htmlElement; }, @@ -206,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 { @@ -251,4 +288,32 @@ 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(), + ); + + @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); + } + + @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 fa2cf540..c9b2c8b5 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" @@ -220,6 +220,152 @@ 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); +} + +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 { @@ -250,6 +396,20 @@ 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_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)) { @@ -259,6 +419,10 @@ 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); + 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); } } @@ -310,6 +474,36 @@ 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_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: @@ -318,6 +512,10 @@ 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); + 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); } @@ -444,6 +642,84 @@ 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(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 { @@ -639,6 +915,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 { @@ -745,6 +1058,28 @@ 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_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); @@ -820,6 +1155,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); @@ -831,6 +1187,12 @@ 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* 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); @@ -846,6 +1208,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) { @@ -858,6 +1223,12 @@ 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* 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); @@ -873,6 +1244,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) { @@ -907,6 +1281,38 @@ 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_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 478311d4..59f343eb 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_ @@ -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,8 +180,153 @@ 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); + +/** + * 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) + +/** + * 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: * @@ -190,11 +335,14 @@ 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 (*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); 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; /** @@ -258,6 +406,46 @@ 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_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 bf0bbaa9..3f0028de 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 @@ -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,76 @@ 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, + ] + } +} + +/// 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 { @@ -189,6 +259,10 @@ 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?]) + case 133: + return LngLatBounds.fromList(self.readValue() as! [Any?]) default: return super.readValue(ofType: type) } @@ -206,6 +280,12 @@ 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 if let value = value as? LngLatBounds { + super.writeByte(133) + super.writeValue(value.toList()) } else { super.writeValue(value) } @@ -233,6 +313,11 @@ 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) + /// 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. @@ -243,6 +328,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`. @@ -294,6 +382,39 @@ 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) + } + /// 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 { @@ -389,6 +510,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 733c1fe0..8bbe7a50 100644 --- a/pigeons/pigeon.dart +++ b/pigeons/pigeon.dart @@ -42,6 +42,15 @@ 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(); + + /// 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); @@ -64,6 +73,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() @@ -122,7 +135,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 +146,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 +156,33 @@ 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; +} + +/// 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/pigeons/run_code_gen.ps1 b/pigeons/run_code_gen.ps1 new file mode 100644 index 00000000..695b8ed0 --- /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 00000000..2378ca0a --- /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 diff --git a/windows/runner/pigeon.g.cpp b/windows/runner/pigeon.g.cpp index 28497538..9cb7609e 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 @@ -257,6 +257,154 @@ 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; +} + +// 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() {} @@ -273,6 +421,12 @@ EncodableValue PigeonInternalCodecSerializer::ReadValueOfType( case 131: { return CustomEncodableValue(ScreenLocation::FromEncodableList(std::get(ReadValue(stream)))); } + 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); } @@ -297,6 +451,16 @@ 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; + } + if (custom_value->type() == typeid(LngLatBounds)) { + stream->WriteByte(133); + WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); + return; + } } flutter::StandardCodecSerializer::WriteValue(value, stream); } @@ -386,6 +550,50 @@ 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.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) { @@ -561,6 +769,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 ba1abb36..9080da00 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_ @@ -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,89 @@ 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_; + +}; + + +// 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(); @@ -237,6 +321,11 @@ 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; + // 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, @@ -262,6 +351,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();