diff --git a/google-maps/README.md b/google-maps/README.md index 70e56e7e7..8a2a4f4ae 100644 --- a/google-maps/README.md +++ b/google-maps/README.md @@ -297,6 +297,7 @@ export default MyMap; * [`enableAccessibilityElements(...)`](#enableaccessibilityelements) * [`enableCurrentLocation(...)`](#enablecurrentlocation) * [`setPadding(...)`](#setpadding) +* [`fitBounds(...)`](#fitbounds) * [`setOnBoundsChangedListener(...)`](#setonboundschangedlistener) * [`setOnCameraIdleListener(...)`](#setoncameraidlelistener) * [`setOnCameraMoveStartedListener(...)`](#setoncameramovestartedlistener) @@ -613,6 +614,22 @@ setPadding(padding: MapPadding) => Promise -------------------- +### fitBounds(...) + +```typescript +fitBounds(bounds: LatLngBounds, padding?: number | undefined) => Promise +``` + +Sets the map viewport to contain the given bounds. + +| Param | Type | Description | +| ------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------- | +| **`bounds`** | LatLngBounds | The bounds to fit in the viewport. | +| **`padding`** | number | Optional padding to apply in pixels. The bounds will be fit in the part of the map that remains after padding is removed. | + +-------------------- + + ### setOnBoundsChangedListener(...) ```typescript diff --git a/google-maps/android/src/main/java/com/capacitorjs/plugins/googlemaps/CapacitorGoogleMap.kt b/google-maps/android/src/main/java/com/capacitorjs/plugins/googlemaps/CapacitorGoogleMap.kt index 80fa64a96..e95cc3a14 100644 --- a/google-maps/android/src/main/java/com/capacitorjs/plugins/googlemaps/CapacitorGoogleMap.kt +++ b/google-maps/android/src/main/java/com/capacitorjs/plugins/googlemaps/CapacitorGoogleMap.kt @@ -666,6 +666,11 @@ class CapacitorGoogleMap( return googleMap?.projection?.visibleRegion?.latLngBounds ?: throw BoundsNotFoundError() } + fun fitBounds(bounds: LatLngBounds, padding: Int) { + val cameraUpdate = CameraUpdateFactory.newLatLngBounds(bounds, padding) + googleMap?.animateCamera(cameraUpdate) + } + private fun getScaledPixels(bridge: Bridge, pixels: Int): Int { // Get the screen's density scale val scale = bridge.activity.resources.displayMetrics.density diff --git a/google-maps/android/src/main/java/com/capacitorjs/plugins/googlemaps/CapacitorGoogleMapsPlugin.kt b/google-maps/android/src/main/java/com/capacitorjs/plugins/googlemaps/CapacitorGoogleMapsPlugin.kt index e4e908cf1..84c238f68 100644 --- a/google-maps/android/src/main/java/com/capacitorjs/plugins/googlemaps/CapacitorGoogleMapsPlugin.kt +++ b/google-maps/android/src/main/java/com/capacitorjs/plugins/googlemaps/CapacitorGoogleMapsPlugin.kt @@ -871,6 +871,32 @@ class CapacitorGoogleMapsPlugin : Plugin() { } } + @PluginMethod + fun fitBounds(call: PluginCall) { + try { + val id = call.getString("id") + id ?: throw InvalidMapIdError() + + val map = maps[id] + map ?: throw MapNotFoundError() + + val boundsObject = + call.getObject("bounds") ?: throw InvalidArgumentsError("bounds is missing") + + val padding = call.getInt("padding", 0)!! + + CoroutineScope(Dispatchers.Main).launch { + val bounds = createLatLngBounds(boundsObject) + map.fitBounds(bounds, padding) + call.resolve() + } + } catch (e: GoogleMapsError) { + handleError(call, e) + } catch (e: Exception) { + handleError(call, e) + } + } + @PluginMethod fun mapBoundsExtend(call: PluginCall) { try { diff --git a/google-maps/e2e-tests/src/pages/Map/Bounds.tsx b/google-maps/e2e-tests/src/pages/Map/Bounds.tsx index f2c919645..5b8f7a80d 100644 --- a/google-maps/e2e-tests/src/pages/Map/Bounds.tsx +++ b/google-maps/e2e-tests/src/pages/Map/Bounds.tsx @@ -1,5 +1,5 @@ import { useState } from 'react'; -import { GoogleMap } from '@capacitor/google-maps'; +import { GoogleMap, LatLngBounds } from '@capacitor/google-maps'; import { IonButton, IonCol, @@ -68,6 +68,29 @@ const BoundsMapPage: React.FC = () => { } } + async function fitBounds() { + setCommandOutput(''); + try { + const bounds = new LatLngBounds({ + southwest: { + lat: lat - 1, + lng: lng - 1, + }, + center: { + lat, + lng, + }, + northeast: { + lat: lat + 1, + lng: lng + 1, + }, + }); + await map!.fitBounds(bounds, 50); + } catch (err: any) { + setCommandOutput(err.message); + } + } + async function boundsContainsPoint() { setCommandOutput(''); try { @@ -108,6 +131,9 @@ const BoundsMapPage: React.FC = () => { Get Bounds + + Fit Bounds + Lat diff --git a/google-maps/ios/Plugin/CapacitorGoogleMapsPlugin.m b/google-maps/ios/Plugin/CapacitorGoogleMapsPlugin.m index 9f659fa7f..abd979e93 100644 --- a/google-maps/ios/Plugin/CapacitorGoogleMapsPlugin.m +++ b/google-maps/ios/Plugin/CapacitorGoogleMapsPlugin.m @@ -28,6 +28,7 @@ CAP_PLUGIN_METHOD(setPadding, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(onScroll, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(getMapBounds, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(fitBounds, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(mapBoundsContains, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(mapBoundsExtend, CAPPluginReturnPromise); ) diff --git a/google-maps/ios/Plugin/CapacitorGoogleMapsPlugin.swift b/google-maps/ios/Plugin/CapacitorGoogleMapsPlugin.swift index 6bcaf86ad..cd6b4e4be 100644 --- a/google-maps/ios/Plugin/CapacitorGoogleMapsPlugin.swift +++ b/google-maps/ios/Plugin/CapacitorGoogleMapsPlugin.swift @@ -748,6 +748,30 @@ public class CapacitorGoogleMapsPlugin: CAPPlugin, GMSMapViewDelegate { } } + @objc func fitBounds(_ call: CAPPluginCall) { + do { + guard let id = call.getString("id") else { + throw GoogleMapErrors.invalidMapId + } + + guard let map = self.maps[id] else { + throw GoogleMapErrors.mapNotFound + } + + guard let boundsObject = call.getObject("bounds") else { + throw GoogleMapErrors.invalidArguments("Invalid bounds provided") + } + + let bounds = try getGMSCoordinateBounds(boundsObject) + let padding = CGFloat(call.getInt("padding", 0)) + + map.fitBounds(bounds: bounds, padding: padding) + call.resolve() + } catch { + handleError(call, error: error) + } + } + @objc func mapBoundsExtend(_ call: CAPPluginCall) { do { guard let boundsObject = call.getObject("bounds") else { diff --git a/google-maps/ios/Plugin/Map.swift b/google-maps/ios/Plugin/Map.swift index 75ff99b2d..590345b9d 100644 --- a/google-maps/ios/Plugin/Map.swift +++ b/google-maps/ios/Plugin/Map.swift @@ -453,6 +453,13 @@ public class Map { return GMSCoordinateBounds(region: self.mapViewController.GMapView.projection.visibleRegion()) } + func fitBounds(bounds: GMSCoordinateBounds, padding: CGFloat) { + DispatchQueue.main.sync { + let cameraUpdate = GMSCameraUpdate.fit(bounds, withPadding: padding) + self.mapViewController.GMapView.animate(with: cameraUpdate) + } + } + private func getFrameOverflowBounds(frame: CGRect, mapBounds: CGRect) -> [CGRect] { var intersections: [CGRect] = [] diff --git a/google-maps/src/implementation.ts b/google-maps/src/implementation.ts index a13a2fb4a..5aa87198c 100644 --- a/google-maps/src/implementation.ts +++ b/google-maps/src/implementation.ts @@ -163,6 +163,12 @@ export interface EnableClusteringArgs { minClusterSize?: number; } +export interface FitBoundsArgs { + id: string; + bounds: LatLngBounds; + padding?: number; +} + export interface CapacitorGoogleMapsPlugin extends Plugin { create(options: CreateMapArgs): Promise; addMarker(args: AddMarkerArgs): Promise<{ id: string }>; @@ -189,6 +195,7 @@ export interface CapacitorGoogleMapsPlugin extends Plugin { onScroll(args: OnScrollArgs): Promise; dispatchMapEvent(args: { id: string; focus: boolean }): Promise; getMapBounds(args: { id: string }): Promise; + fitBounds(args: FitBoundsArgs): Promise; mapBoundsContains( args: MapBoundsContainsArgs, ): Promise<{ contains: boolean }>; diff --git a/google-maps/src/index.ts b/google-maps/src/index.ts index 6005f8a75..aaf949478 100644 --- a/google-maps/src/index.ts +++ b/google-maps/src/index.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/no-namespace */ import { + LatLngBounds, MapType, Marker, Polygon, @@ -9,7 +10,16 @@ import { } from './definitions'; import { GoogleMap } from './map'; -export { GoogleMap, MapType, Marker, Polygon, Circle, Polyline, StyleSpan }; +export { + GoogleMap, + LatLngBounds, + MapType, + Marker, + Polygon, + Circle, + Polyline, + StyleSpan, +}; declare global { export namespace JSX { diff --git a/google-maps/src/map.ts b/google-maps/src/map.ts index 3e68c0fc3..f1e4bf511 100644 --- a/google-maps/src/map.ts +++ b/google-maps/src/map.ts @@ -58,6 +58,12 @@ export interface GoogleMapInterface { enableAccessibilityElements(enabled: boolean): Promise; enableCurrentLocation(enabled: boolean): Promise; setPadding(padding: MapPadding): Promise; + /** + * Sets the map viewport to contain the given bounds. + * @param bounds The bounds to fit in the viewport. + * @param padding Optional padding to apply in pixels. The bounds will be fit in the part of the map that remains after padding is removed. + */ + fitBounds(bounds: LatLngBounds, padding?: number): Promise; setOnBoundsChangedListener( callback?: MapListenerCallback, ): Promise; @@ -489,6 +495,14 @@ export class GoogleMap { ); } + async fitBounds(bounds: LatLngBounds, padding?: number): Promise { + return CapacitorGoogleMaps.fitBounds({ + id: this.id, + bounds, + padding, + }); + } + initScrolling(): void { const ionContents = document.getElementsByTagName('ion-content'); diff --git a/google-maps/src/web.ts b/google-maps/src/web.ts index 862b04052..08fa9d75b 100644 --- a/google-maps/src/web.ts +++ b/google-maps/src/web.ts @@ -28,6 +28,7 @@ import type { OnScrollArgs, MapBoundsContainsArgs, EnableClusteringArgs, + FitBoundsArgs, MapBoundsExtendArgs, AddPolygonsArgs, RemovePolygonsArgs, @@ -248,6 +249,12 @@ export class CapacitorGoogleMapsWeb }); } + async fitBounds(_args: FitBoundsArgs): Promise { + const map = this.maps[_args.id].map; + const bounds = this.getLatLngBounds(_args.bounds); + map.fitBounds(bounds, _args.padding); + } + async addMarkers(_args: AddMarkersArgs): Promise<{ ids: string[] }> { const markerIds: string[] = []; const map = this.maps[_args.id];