21 |
22 | ## Purpose
23 |
24 | Under the hood this package makes use of the native Maps SDK for Android and iOS. The native Maps SDK has much better performance than the JS equivalent. It also adds support for offline caching. On top of that it ("Dynamic Maps") is completely free to use (as of February 2022) ([native pricing](https://developers.google.com/maps/billing-and-pricing/pricing)), in contrary to the JS SDK ([JS pricing](https://developers.google.com/maps/documentation/javascript/usage-and-billing#new-payg)).
25 |
26 | See if this plugin is a good fit for your use case in [this section](about/should-you-use-this-plugin.md).
27 |
28 | ## Maintainers
29 |
30 | | Maintainer | GitHub | Mail |
31 | | ------------ | --------------------------------------- | ---------------------------------------------------------- |
32 | | Hemang Kumar | [hemangsk](https://github.com/hemangsk) | hemangsk@gmail.com |
33 |
34 | ## Limitations
35 |
36 | - Right now, its not possible to allow the rendered Map view to scroll along with the page, it remains at its fixed position.
37 | - This plugins only adds support for using the native iOS and Android SDK. If you want to make use of the JavaScript SDK as well (for a possible webapp) you should implement that separately. Of course it should possible to integrate this into the plugin in the future. If you want to help out with this, please start a PR.
38 |
39 | ## Contributors ✨
40 |
41 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
42 |
43 |
44 |
45 |
46 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
72 |
--------------------------------------------------------------------------------
/docs/_sidebar.md:
--------------------------------------------------------------------------------
1 | - About
2 |
3 | - [Should you use this plugin?](about/should-you-use-this-plugin.md)
4 | - [Out of scope features](about/out-of-scope-features.md)
5 |
6 | - Getting started
7 |
8 | - [Installation](getting-started/installation.md)
9 | - [Quick start](getting-started/quickstart.md)
10 |
11 | - Guide
12 |
13 | - [Setting up the WebView](guide/setup-webview.md)
14 | - [Troubleshooting](guide/troubleshooting.md)
15 |
16 | - [Advanced concepts](advanced-concepts/)
17 |
18 | - [Transparent WebView](advanced-concepts/transparent-webview.md)
19 | - [Touch delegation](advanced-concepts/touch-delegation.md)
20 |
21 | - [API Reference](api.md)
22 |
--------------------------------------------------------------------------------
/docs/about/out-of-scope-features.md:
--------------------------------------------------------------------------------
1 | # Features that are out of scope for this plugin
2 |
3 | There are certain features that are not baked in to this plugin. Examples are Launch Urls and (reverse) Geocoding. Several good reasons exist to not implement this in the plugin. The official Google APIs are already fairly simple and easily accessible from JavaScript. Also it would add overhead supporting this for the maintainers of the plugin. It would also require you, the user of the plugin, to learn non-standard APIs to be able to implement it.
4 |
5 | Nonetheless we have written a few quick guides on these features to get you started
6 |
7 | ## Launch URLs
8 |
9 | > Using Maps URLs, you can build a universal, cross-platform URL to launch Google Maps and perform searches, get directions and navigation, and display map views and panoramic images. The URL syntax is the same regardless of the platform in use.
10 | >
11 | > You don't need a Google API key to use Maps URLs.
12 |
13 | Location search
14 |
15 | In a location search, you search for a specific location using a place name, address, or comma-separated latitude/longitude coordinates, and the resulting map displays a pin at that location. These three examples illustrate searches for the same location, CenturyLink Field (a sports stadium in Seattle, WA), using different location values.
16 |
17 | Example 1: Searching for the place name "CenturyLink Field" results in the following map:
18 |
19 | `https://www.google.com/maps/search/?api=1&query=centurylink+field`
20 |
21 | Example 2: Searching for CenturyLink Field using latitude/longitude coordinates as well as the place ID results in the following map:
22 |
23 | `https://www.google.com/maps/search/?api=1&query=47.5951518%2C-122.3316393&query_place_id=ChIJKxjxuaNqkFQR3CK6O1HNNqY`
24 |
25 | Read more about Launch URLs in the official [Google documentation](https://developers.google.com/maps/documentation/urls/get-started).
26 |
27 | ## Geocoding
28 |
29 |
Geocoding
30 |
31 | > Geocoding is the process of converting addresses (like "1600 Amphitheatre Parkway, Mountain View, CA") into geographic coordinates (like latitude 37.423021 and longitude -122.083739), which you can use to place markers on a map, or position the map
32 |
33 | You can start using the Geocoding API by using the API url directly. An example:
34 |
35 | `https://maps.googleapis.com/maps/api/geocode/json?address=Washington&key=YOUR_API_KEY`
36 |
37 | You could also use the offical JavaScript SDK like so:
38 |
39 | ```js
40 | geocoder
41 | .geocode({ address: "Some address" })
42 | .then((response) => {
43 | if (response.results[0]) {
44 | console.log(response.results);
45 | } else {
46 | console.log("No results found");
47 | }
48 | })
49 | .catch((e) => console.error(e));
50 | ```
51 |
52 |
Reverse geocoding
53 |
54 | > Reverse geocoding is the process of converting geographic coordinates into a human-readable address.
55 |
56 | You can start using the reverse Geocoding API by using the API url directly. An example:
57 |
58 | `https://maps.googleapis.com/maps/api/geocode/json?latlng=40.714224,-73.961452&key=YOUR_API_KEY`
59 |
60 | You could also use the offical JavaScript SDK like so:
61 |
62 | ```js
63 | geocoder
64 | .geocode({ location: { lat: 40.731, lng: -73.997 } })
65 | .then((response) => {
66 | if (response.results[0]) {
67 | console.log(response.results);
68 | } else {
69 | console.log("No results found");
70 | }
71 | })
72 | .catch((e) => console.error(e));
73 | ```
74 |
75 | Read more about geocoding in the official [Google documentation](https://developers.google.com/maps/documentation/geocoding/overview).
76 |
--------------------------------------------------------------------------------
/docs/about/should-you-use-this-plugin.md:
--------------------------------------------------------------------------------
1 | # Should you use this plugin?
2 |
3 |
Purpose
4 |
5 | Under the hood this package makes use of the native Maps SDK for Android and iOS. The native Maps SDK has much better performance than the JS equivalent. It also adds support for offline caching. On top of that it ("Dynamic Maps") is completely free to use (as of February 2022) ([native pricing](https://developers.google.com/maps/billing-and-pricing/pricing)), in contrary to the JS SDK ([JS pricing](https://developers.google.com/maps/documentation/javascript/usage-and-billing#new-payg)).
6 |
7 |
This plugin likely is a good fit for you if ...
8 |
9 |
... you are building an app which main focus is the Map
10 |
11 | If your app revolves mainly around a Map, like for example Uber or Lime, this plugin is probably a good fit for your concept.
12 |
13 |
... you have one or a few Maps in your app that need a performance boost
14 |
15 | If your app utilizes a one or a few Map instances that can remain at its fixed position on the page (see [Limitations](/#limitations)) that need a performance boost (because for example a lot of markers are being rendered) than this plugin is probably a good fit.
16 |
17 |
This plugin likely is not a good fit for you if ...
18 |
19 |
... you are only using the Map to show a single static place
20 |
21 | If your app only uses Google Maps to show a single static place (e.g. to show the location of a restaurant), you should probably use an iframe instead (which should be free to use as well). You can do a Google search on "embed Google Maps free". Or take a look a [this site](https://www.embedgooglemap.net/) for example.
22 |
23 |
... you are already using the JavaScript SDK with success
24 |
25 | If your app already successfully uses the JavaScript SDK, and does not suffer of performance issues, it might not be worth the extra work/overhead to implement this plugin. Especially when your current/planned usage falls under the free tier the JavaScript SDK offers.
26 |
--------------------------------------------------------------------------------
/docs/advanced-concepts/README.md:
--------------------------------------------------------------------------------
1 | # Advanced concepts
2 |
3 | In this section you can learn more about the advanced concepts that power this plugin. It should not be necessary to read through this to make use of the plugin. But it could help answer questions you might have. It could also help you understand certain concepts better. Also, it's just cool to know these things and brag about to your colleagues 😏.
4 |
--------------------------------------------------------------------------------
/docs/advanced-concepts/touch-delegation.md:
--------------------------------------------------------------------------------
1 | # Touch delegation
2 |
3 | > WIP
4 |
5 | This plugin generates native map views, and puts them behind the `WebView`. You can read more about that [here](advanced-concepts/transparent-webview.md). Because of that, advanced mechanisms are put into place, to make sure the Map is still interactable. This section explains the (advanced) techniques behind the "touch delegation" that make that possible.
6 |
7 | Normally when a view overlaps another view in Java or Swift, the lower view is not touchable. You can compare this to the way `z-index` works in HTML.
8 |
9 | This plugin, however, works around that by detecting the point a user touches the screen and consequently detecting whether that touch is meant for either the `WebView` or one of the Map instances.
10 |
--------------------------------------------------------------------------------
/docs/advanced-concepts/transparent-webview.md:
--------------------------------------------------------------------------------
1 | # Transparent WebView
2 |
3 | Whenever [`createMap`](api.md#createmap) is being called, a native Google Maps instance ([Android](https://developers.google.com/android/reference/com/google/android/gms/maps/MapView), [iOS](https://developers.google.com/maps/documentation/ios-sdk/reference/interface_g_m_s_map_view)) is added to the app. Such an instance is a native view which thus lives in the native side of the code base (Java and Swift). This means that they are **not** HTMLElements, **not** a `
` or anything HTML related.
4 |
5 | These view instances are being rendered **behind** the `WebView` ([Android](https://developer.android.com/reference/android/webkit/WebView), [iOS](https://developer.apple.com/documentation/webkit/wkwebview)). The `WebView` itself is made transparent. This has the major benefit that you can render any kind of HTMLElement on top of the native Map.
6 |
7 | > Read more about how we managed to make both the Map instances and `WebView` interactable in [this section](advanced-concepts/touch-delegation.md).
8 |
9 |
Android
10 |
11 | On Android this plugin makes sure that the `WebView` overlays the Map by this piece of code:
12 |
13 | ```Java
14 | bridge.getWebView().bringToFront();
15 | ```
16 |
17 | Simple yet effective.
18 |
19 | Since the `WebView` has a background color by default, the plugin also makes sure the background is transparent by the following code snippet:
20 |
21 | ```Java
22 | bridge.getWebView().setBackgroundColor(Color.TRANSPARENT);
23 | ```
24 |
25 | Of course the webapp itself also may have elements that are not transparent by default. An example of this is the `` element. The plugin automatically tries to make the `` element transparent by adding `background: 'transparent';` to the `style=""` attribute\*.
26 |
27 | This is done by this piece of code:
28 |
29 | ```Java
30 | bridge.getWebView().loadUrl("javascript:document.documentElement.style.backgroundColor = 'transparent';void(0);");
31 | ```
32 |
33 | !> \* This means in theory it is possible that this is overwritten by some CSS property in your setup. Learn how to make sure the Map is viewable in [this guide](guide/setup-webview.md).
34 |
35 |
iOS
36 |
37 | On iOS this plugin makes sure that the `WebView` overlays the Map by "sending" the Map view to the back:
38 |
39 | ```swift
40 | self.bridge?.viewController?.view.sendSubviewToBack(customMapView.view)
41 | ```
42 |
43 | Simple yet effective.
44 |
45 | Since the `WebView` has a background color by default, the plugin also makes sure the background is transparent by the following code snippet:
46 |
47 | ```Swift
48 | self.customWebView?.isOpaque = false
49 | self.customWebView?.backgroundColor = .clear
50 | ```
51 |
52 | Of course the webapp itself also may have elements that are not transparent by default. An example of this is the `` element. The plugin automatically tries to make the `` element transparent by adding `background: 'transparent';` to the `style=""` attribute\*.
53 |
54 | This is done by this piece of code:
55 |
56 | ```Swift
57 | let javascript = "document.documentElement.style.backgroundColor = 'transparent'"
58 | self.customWebView?.evaluateJavaScript(javascript)
59 | ```
60 |
61 | !> \* This means in theory it is possible that this is overwritten by some CSS property in your setup. Learn how to make sure the Map is viewable in [this guide](guide/setup-webview.md).
62 |
--------------------------------------------------------------------------------
/docs/assets/xcode-subclass-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/capacitor-community/google-maps/d82f24298c06bc95fd64eb202b1ceb3fff716582/docs/assets/xcode-subclass-1.png
--------------------------------------------------------------------------------
/docs/assets/xcode-subclass-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/capacitor-community/google-maps/d82f24298c06bc95fd64eb202b1ceb3fff716582/docs/assets/xcode-subclass-2.png
--------------------------------------------------------------------------------
/docs/coverpage.md:
--------------------------------------------------------------------------------
1 | # @capacitor-community/google-maps
2 |
3 | > Capacitor Plugin using native Google Maps SDK for Android and iOS.
4 |
5 | - ⚡️ Native performance
6 | - 💸 Free to use\*
7 | - 🔌 Same API for both iOS and Android
8 | - ✨ Most features from JS SDK
9 |
10 | [GitHub](https://github.com/capacitor-community/google-maps)
11 | [Get Started](#home)
12 |
--------------------------------------------------------------------------------
/docs/getting-started/installation.md:
--------------------------------------------------------------------------------
1 | # Installation
2 |
3 | ## Install package from npm
4 |
5 | ```bash
6 | npm i --save @capacitor-community/google-maps
7 | npx cap sync
8 | ```
9 |
10 | ## Setup
11 |
12 | ### Obtain API Keys
13 |
14 | You must add an API key for the Maps SDK to any app that uses the SDK.
15 |
16 | Before you start using the Maps SDK, you need a project with a billing account and the Maps SDK (both for Android and iOS) enabled. Google requires you to have a valid billing account setup, although likely you will not be charged anything. See the [Should you use this plugin?](about/should-you-use-this-plugin.md) section for more information on pricing.
17 |
18 | Extensive and detailed steps can be found here:
19 |
20 | - [Android](https://developers.google.com/maps/documentation/android-sdk/get-api-key)
21 | - [iOS](https://developers.google.com/maps/documentation/ios-sdk/get-api-key)
22 |
23 | You should have two API keys by the end of this step. Let's proceed.
24 |
25 | ### Adding API keys to your App
26 |
27 | #### Android
28 |
29 | Please follow the [official Google guide](https://developers.google.com/maps/documentation/android-sdk/config#step_2_add_your_api_key_to_the_project). See "Step 2: Add your API key to the project". You can skip Step 1, since this plugin already takes care of that.
30 |
31 | Alternatively, you can (**but really should not**) use the following quick and dirty way:
32 |
33 | In your `AndroidManifest.xml` add the following lines:
34 |
35 | ```diff
36 |
37 | ...
38 | +
41 | ...
42 |
43 | ```
44 |
45 | where `YOUR_ANDROID_MAPS_API_KEY` is the API key you aqcuired in the previous step.
46 |
47 | Again: please **do not** use this alternative method for anything other than testing purposes, since you **will** leak your API Key.
48 |
49 | #### iOS
50 |
51 | On iOS, the API key needs to be set programmatically. This is done using the [`initialize`](./api.md#initialize) method.
52 |
53 | ### Edit Main.storyboard (iOS only)
54 |
55 | On iOS a little extra step should be done. This is needed for touch delegation, which you can read more about in the [advanced concepts section](advanced-concepts/touch-delegation.md).
56 |
57 | In this step you will subclass the default ViewController (called `CAPBridgeViewController`). This is fairly straight forward, as this library already includes the required custom ViewController. The following two images explain in detail how to do that.
58 |
59 | Select the `Main.storyboard` file in the Project Navigator, select the **Bridge View Controller** in the **Bridge View Controller Scene**, select the **Identity Inspector** on the right.
60 |
61 |
62 |
63 | Finally, select `CustomMapViewController` from the dropdown under **Custom Class**.
64 |
65 |
66 |
67 | > If you want to know more about subclassing `CAPBridgeViewController`, you can read about it in the [official Capacitor documentation](https://capacitorjs.com/docs/ios/viewcontroller).
68 |
--------------------------------------------------------------------------------
/docs/getting-started/quickstart.md:
--------------------------------------------------------------------------------
1 | # Quick start
2 |
3 | Before you continue, make sure you have followed all the steps from the [Installation section](installation).
4 |
5 | ## Initializing a Map instance
6 |
7 | After following all installation steps, you can follow this small guide to quickly setup a simple Map instance.
8 |
9 | ### Setting up your HTML
10 |
11 | The Maps SDK renders a native element (`MapView`) behind your webapp (`WebView`). You need to specify the boundaries (which is explained later) that is rendered in. So it is NOT an HTMLElement, but rather a native element. It can therefore not be styled, mutated or listened to like you would with a 'normal' HTMLElement.
12 |
13 | > Read more about this concept in the "Advanced concepts" section.
14 |
15 | At the moment, the only drawback of this is, that the map instance does not size and move along with the div that it is attached to. This is a known limitation and it may be solved in the future as there are some known solutions as well. However, most use cases would use a Map instance that stays at a fixed position anyway.
16 |
17 | Therefore the only requirement right now is to make sure the 'foster element' (the element which you are going to attach the Map instance to) remains at the same position. This can be achieved by preventing it to be able to scroll and to make the current view portrait (or landscape) only.
18 |
19 | So let's get to it. Let's assume you have the following layout:
20 |
21 | ```html
22 |
23 |
24 |
25 |
26 |
27 | Maps SDK for Capacitor - Basic Example
28 |
38 |
39 |
40 |
41 |
42 |
43 | ```
44 |
45 | Note that it can be anything really. Like with Capacitor itself, it does not matter what framework you are using. Angular, Vue, React, Svelte... they are all supported.
46 |
47 | ### Setting up your JavaScript
48 |
49 | The Plugin can be imported as follows:
50 |
51 | ```javascript
52 | import { CapacitorGoogleMaps } from "@capacitor-community/google-maps";
53 | ```
54 |
55 | Let's assume you have imported the Plugin correctly. A simple Maps instance can then be initialized as follows:
56 |
57 | ```javascript
58 | const initializeMap = async () => {
59 | // first of all, you should initialize the Maps SDK:
60 | await CapacitorGoogleMaps.initialize({
61 | key: "YOUR_IOS_MAPS_API_KEY",
62 | devicePixelRatio: window.devicePixelRatio, // this line is very important
63 | });
64 |
65 | // then get the element you want to attach the Maps instance to:
66 | const element = document.getElementById("container");
67 |
68 | // afterwards get its boundaries like so:
69 | const boundingRect = element.getBoundingClientRect();
70 |
71 | // we can now create the map using the boundaries of #container
72 | try {
73 | const result = await CapacitorGoogleMaps.createMap({
74 | boundingRect: {
75 | width: Math.round(boundingRect.width),
76 | height: Math.round(boundingRect.height),
77 | x: Math.round(boundingRect.x),
78 | y: Math.round(boundingRect.y),
79 | },
80 | });
81 |
82 | // remove background, so map can be seen
83 | // (you can read more about this in the "Setting up the WebView" guide)
84 | element.style.background = "";
85 |
86 | // finally set `data-maps-id` attribute for delegating touch events
87 | element.setAttribute("data-maps-id", result.googleMap.mapId);
88 |
89 | alert("Map loaded successfully");
90 | } catch (e) {
91 | alert("Map failed to load");
92 | }
93 | };
94 |
95 | (function () {
96 | // on page load, execute the above method
97 | initializeMap();
98 |
99 | // Some frameworks and a recommended lifecycle hook you could use to initialize the Map:
100 | // Ionic: `ionViewDidEnter`
101 | // Angular: `mounted`
102 | // Vue: `mounted`
103 | // React: `componentDidMount`
104 |
105 | // Of course you can also initialize the Map on different events, like clicking on a button.
106 | // Just make sure you do not unnecessarily initialize it multiple times.
107 | })();
108 | ```
109 |
110 | ## What's next?
111 |
112 | As the previous example shows, it is really easy to integrate the Maps SDK into your app. But, of course, there are many more possibilities.
113 |
114 | - Read the Guides to learn more about the plugin.
115 | - Refer to the [API Reference](api.md#api-reference-🔌) to see all available methods the plugin offers.
116 | - Take a look at some examples [here](https://github.com/capacitor-community/google-maps-examples).
117 |
--------------------------------------------------------------------------------
/docs/guide/setup-webview.md:
--------------------------------------------------------------------------------
1 | # Setting up the WebView
2 |
3 | The Map instances are rendered behind the WebView. Because of this, it should be made sure the native WebView and the concerned HTMLElements inside it, are transparent.
4 |
5 | > If you want to learn more about this concept you can refer to the [advanced concepts section](advanced-concepts/transparent-webview.md)
6 |
7 | This plugin takes care of making the native WebView and the `` transparent\*. You will have to take care of the rest (the `div`'s and other HTMLElements overlaying the Map). This is a deliberate choice, because this plugin could impossibly take care of all the different project setups and an infinite number of different CSS possibilities.
8 |
9 | !> \* The `` element is made transparent by adding `background: 'transparent';` to the `style=""` attribute. So in theory it is possible that this is overwritten by some CSS property in your setup.
10 |
11 | ## Debugging
12 |
13 | So how to go about debugging which HTMLElements are not transparent and thus blocking the Map from being viewable?
14 |
15 | What you could do to discover what divs are responsible for it, is the following:
16 |
17 | 1. Inspect your webapp in your browser.
18 | 1. Add some very noticeable and distinguishable background color to ``.
19 | 1. Then delete the most upper root `div` and see if you can see the background color of ``.
20 | 1. You do? Nice. Undo the deletion and remove the child of that `div` and see if you can see the background color of ``.
21 | 1. Repeat the previous step until you figured out what `div`'s are responsible for overlaying the Map.
22 |
--------------------------------------------------------------------------------
/docs/guide/troubleshooting.md:
--------------------------------------------------------------------------------
1 | # Troubleshooting
2 |
3 | ##### I have a `Error: Plugin GoogleMaps does not respond to method call` error message on iOS
4 |
5 | In Xcode click on `Product` > `Clean Build Folder` and try to build again.
6 |
7 | ##### I have a `Cannot resolve symbol GoogleMaps` error message in Android Studio
8 |
9 | In Android Studio click `File` > `Sync Project with Gradle Files` and try to build again.
10 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Document
6 |
7 |
8 |
12 |
16 |
17 |
18 |
19 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/ios/.npmignore:
--------------------------------------------------------------------------------
1 | Pods
2 | Build
3 | xcuserdata
--------------------------------------------------------------------------------
/ios/Plugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/Plugin.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Plugin.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ios/Plugin.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Plugin/BoundingRect.swift:
--------------------------------------------------------------------------------
1 | import Capacitor
2 |
3 | class BoundingRect {
4 | public static let WIDTH_KEY: String! = "width";
5 | public static let HEIGHT_KEY: String! = "height";
6 | public static let X_KEY: String! = "x";
7 | public static let Y_KEY: String! = "y";
8 |
9 | private static let WIDTH_DEFAULT: Double! = 500.0;
10 | private static let HEIGHT_DEFAULT: Double! = 500.0;
11 | private static let X_DEFAULT: Double! = 0.0;
12 | private static let Y_DEFAULT: Double! = 0.0;
13 |
14 | public var width: Double!;
15 | public var height: Double!;
16 | public var x: Double!;
17 | public var y: Double!;
18 |
19 | public init () {
20 | self.width = BoundingRect.WIDTH_DEFAULT;
21 | self.height = BoundingRect.HEIGHT_DEFAULT;
22 | self.x = BoundingRect.X_DEFAULT;
23 | self.y = BoundingRect.Y_DEFAULT;
24 | }
25 |
26 | func updateFromJSObject(_ object: JSObject) {
27 | self.width = object[BoundingRect.WIDTH_KEY] as? Double ?? BoundingRect.WIDTH_DEFAULT;
28 | self.height = object[BoundingRect.HEIGHT_KEY] as? Double ?? BoundingRect.HEIGHT_DEFAULT;
29 | self.x = object[BoundingRect.X_KEY] as? Double ?? BoundingRect.X_DEFAULT;
30 | self.y = object[BoundingRect.Y_KEY] as? Double ?? BoundingRect.Y_DEFAULT;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/ios/Plugin/CustomMapViewController.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Capacitor
3 | import GoogleMaps
4 |
5 | class CustomWKWebView: WKWebView {
6 | var customMapViews = [String : CustomMapView]();
7 |
8 | open override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
9 | let view = super.hitTest(point, with: event)
10 | let values = self.customMapViews.map({ $0.value })
11 | for customMapView in values {
12 | let convertedPoint = self.convert(point, to: customMapView.GMapView)
13 | let mapView = customMapView.GMapView.hitTest(convertedPoint, with: event)
14 | let contentView = scrollView.subviews[self.customMapViews.count]
15 |
16 | if (mapView != nil), contentView.layer.pixelColorAtPoint(point: point) == true{
17 | return mapView
18 | }
19 | }
20 | return view
21 | }
22 | }
23 |
24 | class CustomMapViewController: CAPBridgeViewController, UIScrollViewDelegate {
25 | open override func webView(with frame: CGRect, configuration: WKWebViewConfiguration) -> WKWebView {
26 | return CustomWKWebView(frame: frame, configuration: configuration)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/ios/Plugin/CustomMapViewEvents.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import Capacitor
3 |
4 | public class CustomMapViewEvents: CAPPlugin {
5 | func lastResultForCallbackId(callbackId: String, result: PluginCallResultData) {}
6 | func resultForCallbackId(callbackId: String, result: PluginCallResultData?) {}
7 | }
8 |
--------------------------------------------------------------------------------
/ios/Plugin/CustomMarker.swift:
--------------------------------------------------------------------------------
1 | import Capacitor
2 | import GoogleMaps
3 |
4 | class CustomMarker: GMSMarker {
5 | var id: String! = NSUUID().uuidString.lowercased();
6 |
7 | public func updateFromJSObject(_ markerData: JSObject) {
8 | let position = markerData["position"] as? JSObject ?? JSObject();
9 |
10 | let latitude = position["latitude"] as? Double ?? 0.0;
11 | let longitude = position["longitude"] as? Double ?? 0.0;
12 |
13 | let preferences = markerData["preferences"] as? JSObject ?? JSObject();
14 |
15 | self.position = CLLocationCoordinate2D(latitude: latitude, longitude: longitude);
16 |
17 | self.title = preferences["title"] as? String ?? nil;
18 |
19 | self.snippet = preferences["snippet"] as? String ?? nil;
20 |
21 | self.opacity = preferences["opacity"] as? Float ?? 1.0;
22 |
23 | self.isFlat = preferences["isFlat"] as? Bool ?? false;
24 |
25 | self.isDraggable = preferences["isDraggable"] as? Bool ?? false;
26 |
27 | self.zIndex = Int32.init(preferences["zIndex"] as? Int ?? 0);
28 |
29 | let anchor = preferences["anchor"] as? JSObject ?? JSObject();
30 | let anchorX = anchor["x"] as? Double ?? 0.5;
31 | let anchorY = anchor["y"] as? Double ?? 1.0;
32 | self.groundAnchor = CGPoint.init(x: anchorX, y: anchorY);
33 |
34 | let metadata: JSObject = preferences["metadata"] as? JSObject ?? JSObject();
35 | self.userData = [
36 | "markerId": self.id!,
37 | "metadata": metadata
38 | ] as? JSObject ?? JSObject();
39 | }
40 |
41 | public static func getResultForMarker(_ marker: GMSMarker, mapId: String) -> PluginCallResultData {
42 | let tag: JSObject = marker.userData as! JSObject;
43 |
44 | return [
45 | "marker": [
46 | "mapId": mapId,
47 | "markerId": tag["markerId"] ?? nil,
48 | "position": [
49 | "latitude": marker.position.latitude,
50 | "longitude": marker.position.longitude
51 | ],
52 | "preferences": [
53 | "title": marker.title,
54 | "snippet": marker.snippet,
55 | "opacity": marker.opacity,
56 | "isFlat": marker.isFlat,
57 | "isDraggable": marker.isDraggable,
58 | "zIndex": marker.zIndex,
59 | "anchor": [
60 | "x": marker.groundAnchor.x,
61 | "y": marker.groundAnchor.y
62 | ],
63 | "metadata": tag["metadata"] ?? JSObject()
64 | ]
65 | ]
66 | ];
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/ios/Plugin/CustomPolygon.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import GoogleMaps
3 | import Capacitor
4 |
5 | class CustomPolygon : GMSPolygon {
6 | var id: String! = NSUUID().uuidString.lowercased();
7 |
8 | public func updateFromJSObject(_ polygonData: JSObject) {
9 | let pathArray = polygonData["path"] as? [JSObject] ?? [JSObject]()
10 | let path = CustomPolygon.pathFromJson(pathArray)
11 | self.path = path
12 |
13 | let preferences = polygonData["preferences"] as? JSObject ?? JSObject()
14 |
15 | let holesArray = preferences["holes"] as? [[JSObject]] ?? [[JSObject]]()
16 | let holes = holesArray.map { holePathArray in
17 | return CustomPolygon.pathFromJson(holePathArray)
18 | }
19 | self.holes = holes
20 |
21 | self.strokeWidth = preferences["strokeWidth"] as? Double ?? 10.0
22 |
23 | if let strokeColor = preferences["strokeColor"] as? String {
24 | self.strokeColor = UIColor.capacitor.color(fromHex: strokeColor) ?? nil
25 | }
26 |
27 | if let fillColor = preferences["fillColor"] as? String {
28 | self.fillColor = UIColor.capacitor.color(fromHex: fillColor) ?? nil
29 | }
30 |
31 | self.title = preferences["title"] as? String ?? ""
32 | self.zIndex = Int32.init(preferences["zIndex"] as? Int ?? 1)
33 | self.geodesic = preferences["isGeodesic"] as? Bool ?? false
34 | self.isTappable = preferences["isClickable"] as? Bool ?? false
35 |
36 | let metadata: JSObject = preferences["metadata"] as? JSObject ?? JSObject()
37 | self.userData = [
38 | "polygonId": self.id!,
39 | "metadata": metadata
40 | ] as? JSObject ?? JSObject()
41 | }
42 |
43 | public static func getResultForPolygon(_ polygon: GMSPolygon, mapId: String) -> PluginCallResultData {
44 | let tag: JSObject = polygon.userData as! JSObject
45 | let holes: [GMSPath] = polygon.holes ?? [GMSPath]()
46 |
47 | return [
48 | "polygon": [
49 | "mapId": mapId,
50 | "polygonId": tag["polygonId"] ?? "",
51 | "path": CustomPolygon.jsonFromPath(polygon.path),
52 | "preferences": [
53 | "title": polygon.title ?? "",
54 | "holes": holes.map { path in
55 | return CustomPolygon.jsonFromPath(path)
56 | },
57 | "strokeWidth": polygon.strokeWidth,
58 | "strokeColor": polygon.strokeColor ?? "",
59 | "fillColor": polygon.fillColor ?? "",
60 | "zIndex": polygon.zIndex,
61 | "isGeodesic": polygon.geodesic,
62 | "isClickable": polygon.isTappable,
63 | "metadata": tag["metadata"] ?? JSObject()
64 | ]
65 | ]
66 | ];
67 | }
68 |
69 |
70 | private static func jsonFromPath(_ path: GMSPath?) -> [JSObject] {
71 | guard let path = path else {
72 | return [JSObject]()
73 | }
74 | let size = path.count()
75 | var result: [JSObject] = []
76 | for i in stride(from: 0, to: size, by: 1) {
77 | let coord = path.coordinate(at: i)
78 | result.append(CustomPolygon.jsonFromCoord(coord))
79 | }
80 | return result
81 | }
82 |
83 | private static func jsonFromCoord(_ coord: CLLocationCoordinate2D) -> JSObject {
84 | return ["latitude" : coord.latitude, "longitude": coord.longitude]
85 | }
86 |
87 | private static func pathFromJson(_ latLngArray: [JSObject]) -> GMSPath {
88 | let path = GMSMutablePath()
89 | latLngArray.forEach { point in
90 | if let lat = point["latitude"] as? Double, let long = point["longitude"] as? Double {
91 | let coord = CLLocationCoordinate2D(latitude: lat, longitude: long)
92 | path.add(coord)
93 | }
94 | }
95 |
96 | return path as GMSPath
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/ios/Plugin/ImageCache/ImageURLLoadable.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | protocol ImageURLLoadable {
4 | static var shared: ImageURLLoadable { get }
5 | func image(at urlString: String, resizeWidth: Int, resizeHeight: Int, completion: @escaping VoidReturnClosure)
6 | func clear(completion: @escaping NoArgsClosure)
7 | }
8 |
9 | protocol ImageCachable {
10 | var imageCache: ImageURLLoadable { get }
11 | }
12 |
--------------------------------------------------------------------------------
/ios/Plugin/ImageCache/NativeImageCache.swift:
--------------------------------------------------------------------------------
1 | import SDWebImage
2 |
3 | final class NativeImageCache: ImageURLLoadable {
4 | static let shared: ImageURLLoadable = NativeImageCache()
5 |
6 | private lazy var cache: NSCache = {
7 | NSCache()
8 | }()
9 |
10 | private init(){}
11 |
12 | func image(at urlString: String, resizeWidth: Int, resizeHeight: Int, completion: @escaping VoidReturnClosure) {
13 | guard let url = URL(string: urlString) else {
14 | completion(nil)
15 | return
16 | }
17 |
18 | // Generate custom key based on the size,
19 | // so we can cache the resized variant of the image as well.
20 | let key = "\(urlString)\(resizeWidth)\(resizeHeight)"
21 |
22 | if let image = cache.object(forKey: key as AnyObject) as? UIImage {
23 | // If the resized image is found in the cache,
24 | // return it.
25 | completion(image)
26 | } else {
27 | // Otherwise, we should download the original image,
28 | SDWebImageDownloader.shared.downloadImage(with: url, options: [], context: nil, progress: nil) { image, _, _, _ in
29 | // then resize it to the preferred size,
30 | guard let resizedImage = image?.resize(targetSize: CGSize(width: resizeWidth, height: resizeHeight)) else {
31 | completion(nil)
32 | return
33 | }
34 | // save it in the cache,
35 | self.cache.setObject(resizedImage, forKey: key as AnyObject)
36 | // and return it.
37 | completion(resizedImage)
38 | }
39 | }
40 | }
41 |
42 | func clear(completion: @escaping NoArgsClosure) {
43 | cache.removeAllObjects()
44 | completion()
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/ios/Plugin/ImageCache/SDWebImageCache.swift:
--------------------------------------------------------------------------------
1 | import SDWebImage
2 |
3 | final class SDWebImageCache: ImageURLLoadable {
4 | static let shared: ImageURLLoadable = SDWebImageCache()
5 |
6 | private let cache = SDImageCache.shared
7 | private let downloadManager = SDWebImageManager.shared
8 |
9 | private init() {
10 | cache.config.maxDiskAge = 7 * 24 * 60 * 60
11 | }
12 |
13 | func image(at urlString: String, resizeWidth: Int, resizeHeight: Int, completion: @escaping VoidReturnClosure) {
14 | guard let url = URL(string: urlString) else {
15 | completion(nil)
16 | return
17 | }
18 |
19 | // Generate custom key based on the size,
20 | // so we can cache the resized variant of the image as well.
21 | let key = "\(urlString)\(resizeWidth)\(resizeHeight)"
22 |
23 | SDImageCache.shared.queryCacheOperation(forKey: key, done: { (image, data, type) in
24 | if let image = image {
25 | // If the resized image is found in the cache,
26 | // return it.
27 | completion(image)
28 | } else {
29 | // Otherwise, we should download the original image,
30 | self.downloadManager.loadImage(with: url, options: [], context: nil, progress: nil) { image, _, _, _, _, _ in
31 | // then resize it to the preferred size,
32 | guard let resizedImage = image?.resize(targetSize: CGSize(width: resizeWidth, height: resizeHeight)) else {
33 | completion(nil)
34 | return
35 | }
36 | // save it in the cache,
37 | SDImageCache.shared.store(resizedImage, forKey: key, completion: {
38 | // and return it.
39 | completion(resizedImage)
40 | })
41 | }
42 | }
43 | })
44 | }
45 |
46 | func clear(completion: @escaping NoArgsClosure) {
47 | cache.clearDisk(onCompletion: completion)
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/ios/Plugin/ImageCache/TypeAliases.swift:
--------------------------------------------------------------------------------
1 | typealias NoArgsClosure = () -> Void
2 | typealias VoidReturnClosure = (T) -> Void
3 |
--------------------------------------------------------------------------------
/ios/Plugin/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 | NSPrincipalClass
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/ios/Plugin/MapCameraPosition.swift:
--------------------------------------------------------------------------------
1 | import Capacitor
2 | import GoogleMaps
3 |
4 | class MapCameraPosition {
5 | public static let TARGET_KEY: String! = "target";
6 | public static let LATITUDE_KEY: String! = "latitude";
7 | public static let LONGITUDE_KEY: String! = "longitude";
8 | public static let BEARING_KEY: String! = "bearing";
9 | public static let TILT_KEY: String! = "tilt";
10 | public static let ZOOM_KEY: String! = "zoom";
11 |
12 | private static let TARGET_DEFAULT: JSObject! = JSObject();
13 | private static let LATITUDE_DEFAULT: Double! = 0.0;
14 | private static let LONGITUDE_DEFAULT: Double! = 0.0;
15 | private static let BEARING_DEFAULT: Double! = 0.0;
16 | private static let TILT_DEFAULT: Double! = 0.0;
17 | private static let ZOOM_DEFAULT: Float! = 12.0;
18 |
19 | public var latitude: Double!;
20 | public var longitude: Double!;
21 | public var bearing: Double!;
22 | public var tilt: Double!;
23 | public var zoom: Float!;
24 |
25 | public init () {
26 | self.latitude = MapCameraPosition.LATITUDE_DEFAULT;
27 | self.longitude = MapCameraPosition.LONGITUDE_DEFAULT;
28 | self.bearing = MapCameraPosition.BEARING_DEFAULT;
29 | self.tilt = MapCameraPosition.TILT_DEFAULT;
30 | self.zoom = MapCameraPosition.ZOOM_DEFAULT;
31 | }
32 |
33 | func getCameraPosition() -> GMSCameraPosition {
34 | return GMSCameraPosition.camera(
35 | withLatitude: self.latitude,
36 | longitude: self.longitude,
37 | zoom: self.zoom,
38 | bearing: self.bearing,
39 | viewingAngle: self.tilt
40 | )
41 | }
42 |
43 | func updateFromJSObject(_ object: JSObject, baseCameraPosition: GMSCameraPosition?) {
44 | let target: JSObject = object[MapCameraPosition.TARGET_KEY] as? JSObject ?? MapCameraPosition.TARGET_DEFAULT;
45 |
46 | var latitude = baseCameraPosition?.target.latitude ?? MapCameraPosition.LATITUDE_DEFAULT
47 | if (target[MapCameraPosition.LATITUDE_KEY] != nil) {
48 | // TODO: validate latitude
49 | latitude = target[MapCameraPosition.LATITUDE_KEY] as? Double ?? latitude
50 | }
51 | self.latitude = latitude;
52 |
53 | var longitude = baseCameraPosition?.target.longitude ?? MapCameraPosition.LONGITUDE_DEFAULT
54 | if (target[MapCameraPosition.LONGITUDE_KEY] != nil) {
55 | // TODO: validate longitude
56 | longitude = target[MapCameraPosition.LONGITUDE_KEY] as? Double ?? longitude
57 | }
58 | self.longitude = longitude;
59 |
60 | var bearing = baseCameraPosition?.bearing ?? MapCameraPosition.BEARING_DEFAULT
61 | if (object[MapCameraPosition.BEARING_KEY] != nil) {
62 | // TODO: validate bearing
63 | bearing = object[MapCameraPosition.BEARING_KEY] as? Double ?? bearing
64 | }
65 | self.bearing = bearing;
66 |
67 | var tilt = baseCameraPosition?.viewingAngle ?? MapCameraPosition.TILT_DEFAULT
68 | if (object[MapCameraPosition.TILT_KEY] != nil) {
69 | // TODO: validate tilt
70 | tilt = object[MapCameraPosition.TILT_KEY] as? Double ?? tilt
71 | }
72 | self.tilt = tilt;
73 |
74 | var zoom = baseCameraPosition?.zoom ?? MapCameraPosition.ZOOM_DEFAULT
75 | if (object[MapCameraPosition.ZOOM_KEY] != nil) {
76 | // TODO: validate zoom
77 | zoom = object[MapCameraPosition.ZOOM_KEY] as? Float ?? zoom
78 | }
79 | self.zoom = zoom;
80 | }
81 |
82 | func getJSObject(_ cameraPosition: GMSCameraPosition) -> JSObject {
83 | return [
84 | MapCameraPosition.TARGET_KEY: [
85 | MapCameraPosition.LATITUDE_KEY: cameraPosition.target.latitude,
86 | MapCameraPosition.LONGITUDE_KEY: cameraPosition.target.longitude
87 | ],
88 | MapCameraPosition.BEARING_KEY: cameraPosition.bearing,
89 | MapCameraPosition.TILT_KEY: cameraPosition.viewingAngle,
90 | MapCameraPosition.ZOOM_KEY: cameraPosition.zoom
91 | ]
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/ios/Plugin/MapPreferences.swift:
--------------------------------------------------------------------------------
1 | import Capacitor
2 |
3 | class MapPreferences {
4 | public var gestures: MapPreferencesGestures!
5 | public var controls: MapPreferencesControls!
6 | public var appearance: MapPreferencesAppearance!
7 |
8 | public init() {
9 | self.gestures = MapPreferencesGestures();
10 | self.controls = MapPreferencesControls();
11 | self.appearance = MapPreferencesAppearance();
12 | }
13 |
14 | open func updateFromJSObject(_ preferences: JSObject!) {
15 | if preferences != nil {
16 | // update gestures
17 | let gesturesObject: JSObject! = preferences["gestures"] as? JSObject ?? JSObject();
18 | self.gestures.updateFromJSObject(object: gesturesObject);
19 | // update controls
20 | let controlsObject: JSObject! = preferences["controls"] as? JSObject ?? JSObject();
21 | self.controls.updateFromJSObject(object: controlsObject);
22 | // update appearance
23 | let appearanceObject: JSObject! = preferences["appearance"] as? JSObject ?? JSObject();
24 | self.appearance.updateFromJSObject(object: appearanceObject);
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/ios/Plugin/MapPreferencesAppearance.swift:
--------------------------------------------------------------------------------
1 | import Capacitor
2 | import GoogleMaps
3 |
4 | class MapPreferencesAppearance {
5 | public static let TYPE_KEY: String! = "type"
6 | public static let STYLE_KEY: String! = "style"
7 | public static let BUILDINGS_SHOWN_KEY: String! = "isBuildingsShown"
8 | public static let INDOOR_SHOWN_KEY: String! = "isIndoorShown"
9 | public static let MY_LOCATION_DOT_SHOWN_KEY: String! = "isMyLocationDotShown"
10 | public static let TRAFFIC_SHOWN_KEY: String! = "isTrafficShown"
11 |
12 | var type: GMSMapViewType = GMSMapViewType.normal;
13 |
14 | var style: GMSMapStyle? = nil;
15 |
16 | var _isBuildingsShown: Bool = true
17 | var isBuildingsShown: Bool! {
18 | get {
19 | return _isBuildingsShown
20 | }
21 | set (newVal) {
22 | _isBuildingsShown = newVal ?? true
23 | }
24 | }
25 |
26 | var _isIndoorShown: Bool = true
27 | var isIndoorShown: Bool! {
28 | get {
29 | return _isIndoorShown
30 | }
31 | set (newVal) {
32 | _isIndoorShown = newVal ?? true
33 | }
34 | }
35 |
36 | var _isMyLocationDotShown: Bool = false
37 | var isMyLocationDotShown: Bool! {
38 | get {
39 | return _isMyLocationDotShown
40 | }
41 | set (newVal) {
42 | _isMyLocationDotShown = newVal ?? false
43 | }
44 | }
45 |
46 | var _isTrafficShown: Bool = false
47 | var isTrafficShown: Bool! {
48 | get {
49 | return _isTrafficShown
50 | }
51 | set (newVal) {
52 | _isTrafficShown = newVal ?? false
53 | }
54 | }
55 |
56 | func updateFromJSObject(object: JSObject) {
57 | if (object[MapPreferencesAppearance.TYPE_KEY] != nil) {
58 | let type = object[MapPreferencesAppearance.TYPE_KEY] as? Int;
59 | if (type != nil) {
60 | if (type == 0) {
61 | self.type = GMSMapViewType.none;
62 | } else if (type == 1) {
63 | self.type = GMSMapViewType.normal;
64 | } else if (type == 2) {
65 | self.type = GMSMapViewType.satellite;
66 | } else if (type == 3) {
67 | self.type = GMSMapViewType.terrain;
68 | } else if (type == 4) {
69 | self.type = GMSMapViewType.hybrid;
70 | } else {
71 | self.type = GMSMapViewType.normal;
72 | }
73 | }
74 | }
75 |
76 | if (object[MapPreferencesAppearance.STYLE_KEY] != nil) {
77 | let style = object[MapPreferencesAppearance.STYLE_KEY] as? String;
78 | if (style == nil) {
79 | self.style = nil;
80 | } else {
81 | do {
82 | try self.style = GMSMapStyle(jsonString: style ?? "{}");
83 | } catch {
84 | print("error parsing style json string");
85 | }
86 | }
87 | }
88 |
89 | self.isBuildingsShown = object[MapPreferencesAppearance.BUILDINGS_SHOWN_KEY] as? Bool;
90 | self.isIndoorShown = object[MapPreferencesAppearance.INDOOR_SHOWN_KEY] as? Bool;
91 | self.isMyLocationDotShown = object[MapPreferencesAppearance.MY_LOCATION_DOT_SHOWN_KEY] as? Bool;
92 | self.isTrafficShown = object[MapPreferencesAppearance.TRAFFIC_SHOWN_KEY] as? Bool;
93 | }
94 |
95 | func getJSObject(_ mapView: GMSMapView) -> JSObject {
96 | var type = 1;
97 | if (mapView.mapType == GMSMapViewType.none) {
98 | type = 0;
99 | } else if (mapView.mapType == GMSMapViewType.normal) {
100 | type = 1;
101 | } else if (mapView.mapType == GMSMapViewType.satellite) {
102 | type = 2;
103 | } else if (mapView.mapType == GMSMapViewType.terrain) {
104 | type = 3;
105 | } else if (mapView.mapType == GMSMapViewType.hybrid) {
106 | type = 4;
107 | }
108 |
109 | return [
110 | MapPreferencesAppearance.TYPE_KEY: type,
111 | MapPreferencesAppearance.BUILDINGS_SHOWN_KEY: mapView.isBuildingsEnabled,
112 | MapPreferencesAppearance.INDOOR_SHOWN_KEY: mapView.isIndoorEnabled,
113 | MapPreferencesAppearance.MY_LOCATION_DOT_SHOWN_KEY: mapView.isMyLocationEnabled,
114 | MapPreferencesAppearance.TRAFFIC_SHOWN_KEY: mapView.isTrafficEnabled
115 | ]
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/ios/Plugin/MapPreferencesControls.swift:
--------------------------------------------------------------------------------
1 | import Capacitor
2 | import GoogleMaps
3 |
4 | class MapPreferencesControls {
5 | public static let COMPASS_BUTTON_KEY: String! = "isCompassButtonEnabled"
6 | public static let INDOOR_LEVEL_PICKER_KEY: String! = "isIndoorLevelPickerEnabled"
7 | public static let MAP_TOOLBAR_KEY: String! = "isMapToolbarEnabled" // (android only)
8 | public static let MY_LOCATION_BUTTON_KEY: String! = "isMyLocationButtonEnabled"
9 | public static let ZOOM_BUTTONS_KEY: String! = "isZoomButtonsEnabled" // (android only)
10 |
11 | var _isCompassButtonEnabled: Bool = true
12 | var isCompassButtonEnabled: Bool! {
13 | get {
14 | return _isCompassButtonEnabled
15 | }
16 | set (newVal) {
17 | _isCompassButtonEnabled = newVal ?? true
18 | }
19 | }
20 |
21 | var _isIndoorLevelPickerEnabled: Bool = false
22 | var isIndoorLevelPickerEnabled: Bool! {
23 | get {
24 | return _isIndoorLevelPickerEnabled
25 | }
26 | set (newVal) {
27 | _isIndoorLevelPickerEnabled = newVal ?? false
28 | }
29 | }
30 |
31 | var _isMyLocationButtonEnabled: Bool = true
32 | var isMyLocationButtonEnabled: Bool! {
33 | get {
34 | return _isMyLocationButtonEnabled
35 | }
36 | set (newVal) {
37 | _isMyLocationButtonEnabled = newVal ?? true
38 | }
39 | }
40 |
41 | func updateFromJSObject(object: JSObject) {
42 | self.isCompassButtonEnabled = object[MapPreferencesControls.COMPASS_BUTTON_KEY] as? Bool;
43 | self.isIndoorLevelPickerEnabled = object[MapPreferencesControls.INDOOR_LEVEL_PICKER_KEY] as? Bool;
44 | self.isMyLocationButtonEnabled = object[MapPreferencesControls.MY_LOCATION_BUTTON_KEY] as? Bool;
45 | }
46 |
47 | func getJSObject(_ mapView: GMSMapView) -> JSObject {
48 | return [
49 | MapPreferencesControls.COMPASS_BUTTON_KEY: mapView.settings.compassButton,
50 | MapPreferencesControls.INDOOR_LEVEL_PICKER_KEY: mapView.settings.indoorPicker,
51 | MapPreferencesControls.MAP_TOOLBAR_KEY: false, // (android only)
52 | MapPreferencesControls.MY_LOCATION_BUTTON_KEY: mapView.settings.myLocationButton,
53 | MapPreferencesControls.ZOOM_BUTTONS_KEY: false, // (android only)
54 | ]
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/ios/Plugin/MapPreferencesGestures.swift:
--------------------------------------------------------------------------------
1 | import Capacitor
2 | import GoogleMaps
3 |
4 | class MapPreferencesGestures {
5 | public static let ROTATE_ALLOWED_KEY: String! = "isRotateAllowed"
6 | public static let SCROLL_ALLOWED_KEY: String! = "isScrollAllowed"
7 | public static let SCROLL_ALLOWED_DURING_ROTATE_OR_ZOOM_KEY: String! = "isScrollAllowedDuringRotateOrZoom"
8 | public static let TILT_ALLOWED_KEY: String! = "isTiltAllowed"
9 | public static let ZOOM_ALLOWED_KEY: String! = "isZoomAllowed"
10 |
11 | var _isRotateAllowed: Bool = true
12 | var isRotateAllowed: Bool! {
13 | get {
14 | return _isRotateAllowed
15 | }
16 | set (newVal) {
17 | _isRotateAllowed = newVal ?? true
18 | }
19 | }
20 |
21 | var _isScrollAllowed: Bool = true
22 | var isScrollAllowed: Bool! {
23 | get {
24 | return _isScrollAllowed
25 | }
26 | set (newVal) {
27 | _isScrollAllowed = newVal ?? true
28 | }
29 | }
30 |
31 | var _isScrollAllowedDuringRotateOrZoom: Bool = true
32 | var isScrollAllowedDuringRotateOrZoom: Bool! {
33 | get {
34 | return _isScrollAllowedDuringRotateOrZoom
35 | }
36 | set (newVal) {
37 | _isScrollAllowedDuringRotateOrZoom = newVal ?? true
38 | }
39 | }
40 |
41 | var _isTiltAllowed: Bool = true
42 | var isTiltAllowed: Bool! {
43 | get {
44 | return _isTiltAllowed
45 | }
46 | set (newVal) {
47 | _isTiltAllowed = newVal ?? true
48 | }
49 | }
50 |
51 | var _isZoomAllowed: Bool = true
52 | var isZoomAllowed: Bool! {
53 | get {
54 | return _isZoomAllowed
55 | }
56 | set (newVal) {
57 | _isZoomAllowed = newVal ?? true
58 | }
59 | }
60 |
61 | func updateFromJSObject(object: JSObject) {
62 | self.isRotateAllowed = object[MapPreferencesGestures.ROTATE_ALLOWED_KEY] as? Bool;
63 | self.isScrollAllowed = object[MapPreferencesGestures.SCROLL_ALLOWED_KEY] as? Bool;
64 | self.isScrollAllowedDuringRotateOrZoom = object[MapPreferencesGestures.SCROLL_ALLOWED_DURING_ROTATE_OR_ZOOM_KEY] as? Bool;
65 | self.isScrollAllowed = object[MapPreferencesGestures.TILT_ALLOWED_KEY] as? Bool;
66 | self.isScrollAllowed = object[MapPreferencesGestures.ZOOM_ALLOWED_KEY] as? Bool;
67 | }
68 |
69 | func getJSObject(_ mapView: GMSMapView) -> JSObject {
70 | return [
71 | MapPreferencesGestures.ROTATE_ALLOWED_KEY: mapView.settings.rotateGestures,
72 | MapPreferencesGestures.SCROLL_ALLOWED_KEY: mapView.settings.scrollGestures,
73 | MapPreferencesGestures.SCROLL_ALLOWED_DURING_ROTATE_OR_ZOOM_KEY: mapView.settings.allowScrollGesturesDuringRotateOrZoom,
74 | MapPreferencesGestures.TILT_ALLOWED_KEY: mapView.settings.tiltGestures,
75 | MapPreferencesGestures.ZOOM_ALLOWED_KEY: mapView.settings.zoomGestures
76 | ]
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/ios/Plugin/Plugin.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | //! Project version number for Plugin.
4 | FOUNDATION_EXPORT double PluginVersionNumber;
5 |
6 | //! Project version string for Plugin.
7 | FOUNDATION_EXPORT const unsigned char PluginVersionString[];
8 |
9 | // In this header, you should import all the public headers of your framework using statements like #import
10 |
11 |
--------------------------------------------------------------------------------
/ios/Plugin/Plugin.m:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | // Define the plugin using the CAP_PLUGIN Macro, and
5 | // each method the plugin supports using the CAP_PLUGIN_METHOD macro.
6 | CAP_PLUGIN(CapacitorGoogleMaps, "CapacitorGoogleMaps",
7 | CAP_PLUGIN_METHOD(initialize, CAPPluginReturnPromise);
8 | CAP_PLUGIN_METHOD(createMap, CAPPluginReturnPromise);
9 | CAP_PLUGIN_METHOD(updateMap, CAPPluginReturnPromise);
10 | CAP_PLUGIN_METHOD(clearMap, CAPPluginReturnNone);
11 | CAP_PLUGIN_METHOD(removeMap, CAPPluginReturnPromise);
12 | CAP_PLUGIN_METHOD(moveCamera, CAPPluginReturnPromise);
13 | CAP_PLUGIN_METHOD(addMarker, CAPPluginReturnPromise);
14 | CAP_PLUGIN_METHOD(addMarkers, CAPPluginReturnPromise);
15 | CAP_PLUGIN_METHOD(removeMarker, CAPPluginReturnPromise);
16 | CAP_PLUGIN_METHOD(addPolygon, CAPPluginReturnPromise);
17 | CAP_PLUGIN_METHOD(removePolygon, CAPPluginReturnPromise);
18 | CAP_PLUGIN_METHOD(didTapInfoWindow, CAPPluginReturnCallback);
19 | CAP_PLUGIN_METHOD(didCloseInfoWindow, CAPPluginReturnCallback);
20 | CAP_PLUGIN_METHOD(didTapMap, CAPPluginReturnCallback);
21 | CAP_PLUGIN_METHOD(didLongPressMap, CAPPluginReturnCallback);
22 | CAP_PLUGIN_METHOD(didTapMarker, CAPPluginReturnCallback);
23 | CAP_PLUGIN_METHOD(didBeginDraggingMarker, CAPPluginReturnCallback);
24 | CAP_PLUGIN_METHOD(didDragMarker, CAPPluginReturnCallback);
25 | CAP_PLUGIN_METHOD(didEndDraggingMarker, CAPPluginReturnCallback);
26 | CAP_PLUGIN_METHOD(didTapMyLocationButton, CAPPluginReturnCallback);
27 | CAP_PLUGIN_METHOD(didTapMyLocationDot, CAPPluginReturnCallback);
28 | CAP_PLUGIN_METHOD(didTapPoi, CAPPluginReturnCallback);
29 | CAP_PLUGIN_METHOD(didBeginMovingCamera, CAPPluginReturnCallback);
30 | CAP_PLUGIN_METHOD(didMoveCamera, CAPPluginReturnCallback);
31 | CAP_PLUGIN_METHOD(didEndMovingCamera, CAPPluginReturnCallback);
32 | )
33 |
--------------------------------------------------------------------------------
/ios/Plugin/Utilities/CALayer+pixelColorAtPoint.swift:
--------------------------------------------------------------------------------
1 | extension CALayer {
2 | func pixelColorAtPoint(point:CGPoint) -> Bool {
3 | var pixel: [UInt8] = [0, 0, 0, 0]
4 | let colourSpace = CGColorSpaceCreateDeviceRGB()
5 | let alphaInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue)
6 | let context = CGContext(data: &pixel, width: 1, height: 1, bitsPerComponent: 8, bytesPerRow: 4, space: colourSpace, bitmapInfo: alphaInfo.rawValue)
7 |
8 | context?.translateBy(x: -point.x, y: -point.y)
9 |
10 | self.render(in: context!)
11 |
12 | return CGFloat(pixel[0]) == 0
13 | && CGFloat(pixel[1]) == 0
14 | && CGFloat(pixel[2]) == 0
15 | && CGFloat(pixel[3]) == 0
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/ios/Plugin/Utilities/LinkedList.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | public class Node {
4 | var value: T
5 | var next: Node?
6 |
7 | init(value: T) {
8 | self.value = value
9 | }
10 | }
11 |
12 | public class List {
13 | fileprivate var head: Node?
14 | private var tail: Node?
15 |
16 | public var isEmpty: Bool {
17 | return head == nil
18 | }
19 | public var first: Node? {
20 | return head
21 | }
22 | public var last: Node? {
23 | return tail
24 | }
25 |
26 | init(elements: [T]) {
27 | elements.forEach {
28 | self.append(value: $0)
29 | }
30 | }
31 |
32 | public func append(value: T) {
33 | let newNode = Node(value: value)
34 |
35 | if let tailNode = tail {
36 | tailNode.next = newNode
37 | } else {
38 | head = newNode
39 | }
40 |
41 | tail = newNode
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/ios/Plugin/Utilities/UIImage+resize.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | extension UIImage {
4 | func resize(targetSize: CGSize) -> UIImage? {
5 | let widthRatio = targetSize.width / size.width
6 | let heightRatio = targetSize.height / size.height
7 |
8 | var newSize: CGSize
9 | if(widthRatio > heightRatio) {
10 | newSize = CGSize(width: size.width * heightRatio, height: size.height * heightRatio)
11 | } else {
12 | newSize = CGSize(width: size.width * widthRatio, height: size.height * widthRatio)
13 | }
14 |
15 | let rect = CGRect(origin: .zero, size: newSize)
16 |
17 | UIGraphicsBeginImageContextWithOptions(newSize, false, UIScreen.main.scale)
18 | self.draw(in: rect)
19 | let newImage = UIGraphicsGetImageFromCurrentImageContext()
20 | UIGraphicsEndImageContext()
21 |
22 | return newImage
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/ios/PluginTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/ios/PluginTests/PluginTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | import Capacitor
3 | @testable import Plugin
4 |
5 | class PluginTests: XCTestCase {
6 |
7 | override func setUp() {
8 | super.setUp()
9 | // Put setup code here. This method is called before the invocation of each test method in the class.
10 | }
11 |
12 | override func tearDown() {
13 | // Put teardown code here. This method is called after the invocation of each test method in the class.
14 | super.tearDown()
15 | }
16 |
17 | func testEcho() {
18 | // This is an example of a functional test case for a plugin.
19 | // Use XCTAssert and related functions to verify your tests produce the correct results.
20 |
21 | let value = "Hello, World!"
22 | let plugin = MyPlugin()
23 |
24 | let call = CAPPluginCall(callbackId: "test", options: [
25 | "value": value
26 | ], success: { (result, call) in
27 | let resultValue = result!.data["value"] as? String
28 | XCTAssertEqual(value, resultValue)
29 | }, error: { (err) in
30 | XCTFail("Error shouldn't have been called")
31 | })
32 |
33 | plugin.echo(call!)
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/ios/Podfile:
--------------------------------------------------------------------------------
1 | platform :ios, '13.0'
2 |
3 | def capacitor_pods
4 | # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
5 | pod 'Capacitor', :path => '../node_modules/@capacitor/ios'
6 | pod 'CapacitorCordova', :path => '../node_modules/@capacitor/ios'
7 | end
8 |
9 | target 'Plugin' do
10 | use_frameworks!
11 | pod 'GoogleMaps'
12 | capacitor_pods
13 | end
14 |
15 | target 'PluginTests' do
16 | use_frameworks!
17 | pod 'GoogleMaps'
18 | capacitor_pods
19 | end
20 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@capacitor-community/google-maps",
3 | "version": "2.0.0-beta.10",
4 | "description": "Plugin using native Maps API for Android and iOS.",
5 | "main": "dist/esm/index.js",
6 | "module": "dist/esm/index.js",
7 | "types": "dist/esm/index.d.ts",
8 | "scripts": {
9 | "docgen": "docgen --api CapacitorGoogleMapsPlugin --output-readme ./docs/api.md",
10 | "build": "npm run clean && tsc",
11 | "clean": "rimraf ./dist",
12 | "watch": "tsc --watch",
13 | "prepublishOnly": "npm run build",
14 | "prepare": "npm run build"
15 | },
16 | "author": "@capacitor-community",
17 | "license": "MIT",
18 | "devDependencies": {
19 | "@capacitor/android": "^6.0.0",
20 | "@capacitor/core": "^6.0.0",
21 | "@capacitor/docgen": "git+https://github.com/DutchConcepts/capacitor-docgen.git",
22 | "@capacitor/ios": "^6.0.0",
23 | "all-contributors-cli": "^6.20.0",
24 | "rimraf": "^3.0.0",
25 | "typescript": "~4.1.5"
26 | },
27 | "peerDependencies": {
28 | "@capacitor/core": "^6.0.0"
29 | },
30 | "files": [
31 | "dist/",
32 | "ios/",
33 | "android/",
34 | "CapacitorCommunityGoogleMaps.podspec"
35 | ],
36 | "keywords": [
37 | "capacitor",
38 | "plugin",
39 | "native"
40 | ],
41 | "capacitor": {
42 | "ios": {
43 | "src": "ios"
44 | },
45 | "android": {
46 | "src": "android"
47 | }
48 | },
49 | "repository": {
50 | "type": "git",
51 | "url": "git+https://github.com/capacitor-community/google-maps.git"
52 | },
53 | "bugs": {
54 | "url": "https://github.com/capacitor-community/google-maps/issues"
55 | },
56 | "homepage": "https://github.com/capacitor-community/google-maps#readme",
57 | "directories": {
58 | "doc": "docs"
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import nodeResolve from 'rollup-plugin-node-resolve';
2 |
3 | export default {
4 | input: 'dist/esm/index.js',
5 | output: {
6 | file: 'dist/plugin.js',
7 | format: 'iife',
8 | name: 'capacitorPlugin',
9 | sourcemap: true
10 | },
11 | plugins: [
12 | nodeResolve()
13 | ]
14 | };
--------------------------------------------------------------------------------
/src/definitions.ts:
--------------------------------------------------------------------------------
1 | import { PluginListenerHandle } from "@capacitor/core";
2 |
3 | import {
4 | // methods
5 | InitializeOptions,
6 | CreateMapOptions,
7 | CreateMapResult,
8 | UpdateMapOptions,
9 | UpdateMapResult,
10 | RemoveMapOptions,
11 | ClearMapOptions,
12 | MoveCameraOptions,
13 | ElementFromPointResultOptions,
14 | AddMarkerOptions,
15 | AddMarkerResult,
16 | AddMarkersOptions,
17 | AddMarkersResult,
18 | RemoveMarkerOptions,
19 | AddPolygonOptions,
20 | AddPolygonResult,
21 | RemovePolygonOptions,
22 | // events
23 | DidTapInfoWindowCallback,
24 | DidCloseInfoWindowCallback,
25 | DidTapMapCallback,
26 | DidLongPressMapCallback,
27 | DidTapMarkerCallback,
28 | DidBeginDraggingMarkerCallback,
29 | DidDragMarkerCallback,
30 | DidEndDraggingMarkerCallback,
31 | DidTapMyLocationButtonCallback,
32 | DidTapMyLocationDotCallback,
33 | DidTapPoiCallback,
34 | DidBeginMovingCameraCallback,
35 | DidMoveCameraCallback,
36 | DidEndMovingCameraCallback,
37 | } from "./interfaces";
38 |
39 | export type CallbackID = string;
40 |
41 | export interface DefaultEventOptions {
42 | mapId: string;
43 | }
44 |
45 | export interface DefaultEventWithPreventDefaultOptions {
46 | mapId: string;
47 | preventDefault?: boolean;
48 | }
49 |
50 | export interface DidRequestElementFromPointResult {
51 | eventChainId: string;
52 | point?: {
53 | x: number;
54 | y: number;
55 | };
56 | }
57 |
58 | export interface CapacitorGoogleMapsPlugin {
59 | initialize(options: InitializeOptions): Promise;
60 |
61 | createMap(options: CreateMapOptions): Promise;
62 |
63 | updateMap(options: UpdateMapOptions): Promise;
64 |
65 | removeMap(options: RemoveMapOptions): Promise;
66 |
67 | clearMap(options: ClearMapOptions): Promise;
68 |
69 | moveCamera(options: MoveCameraOptions): Promise;
70 |
71 | addMarker(options: AddMarkerOptions): Promise;
72 |
73 | addMarkers(options: AddMarkersOptions): Promise;
74 |
75 | removeMarker(options: RemoveMarkerOptions): Promise;
76 |
77 | addPolygon(options: AddPolygonOptions): Promise;
78 |
79 | removePolygon(options: RemovePolygonOptions): Promise;
80 |
81 | didTapInfoWindow(
82 | options: DefaultEventOptions,
83 | callback: DidTapInfoWindowCallback
84 | ): Promise;
85 |
86 | didCloseInfoWindow(
87 | options: DefaultEventOptions,
88 | callback: DidCloseInfoWindowCallback
89 | ): Promise;
90 |
91 | didTapMap(
92 | options: DefaultEventOptions,
93 | callback: DidTapMapCallback
94 | ): Promise;
95 |
96 | didLongPressMap(
97 | options: DefaultEventOptions,
98 | callback: DidLongPressMapCallback
99 | ): Promise;
100 |
101 | didTapMarker(
102 | options: DefaultEventWithPreventDefaultOptions,
103 | callback: DidTapMarkerCallback
104 | ): Promise;
105 |
106 | didBeginDraggingMarker(
107 | options: DefaultEventOptions,
108 | callback: DidBeginDraggingMarkerCallback
109 | ): Promise;
110 |
111 | didDragMarker(
112 | options: DefaultEventOptions,
113 | callback: DidDragMarkerCallback
114 | ): Promise;
115 |
116 | didEndDraggingMarker(
117 | options: DefaultEventOptions,
118 | callback: DidEndDraggingMarkerCallback
119 | ): Promise;
120 |
121 | didTapMyLocationButton(
122 | options: DefaultEventWithPreventDefaultOptions,
123 | callback: DidTapMyLocationButtonCallback
124 | ): Promise;
125 |
126 | didTapMyLocationDot(
127 | options: DefaultEventOptions,
128 | callback: DidTapMyLocationDotCallback
129 | ): Promise;
130 |
131 | didTapPoi(
132 | options: DefaultEventOptions,
133 | callback: DidTapPoiCallback
134 | ): Promise;
135 |
136 | didBeginMovingCamera(
137 | options: DefaultEventOptions,
138 | callback: DidBeginMovingCameraCallback
139 | ): Promise;
140 |
141 | didMoveCamera(
142 | options: DefaultEventOptions,
143 | callback: DidMoveCameraCallback
144 | ): Promise;
145 |
146 | didEndMovingCamera(
147 | options: DefaultEventOptions,
148 | callback: DidEndMovingCameraCallback
149 | ): Promise;
150 |
151 | /**
152 | * After `didRequestElementFromPoint` fires, this method is used to let the WebView know whether or not to delegate the touch event to a certain MapView.
153 | * It is handled automatically and you should probably not use it.
154 | */
155 | elementFromPointResult(options: ElementFromPointResultOptions): Promise;
156 |
157 | /**
158 | * This listens for touch events on the WebView.
159 | * It is handled automatically and you should probably not use it.
160 | */
161 | addListener(
162 | eventName: "didRequestElementFromPoint",
163 | listenerFunc: (result: DidRequestElementFromPointResult) => void
164 | ): Promise;
165 | }
166 |
167 | export * from "./interfaces";
168 |
169 | // methods to implement:
170 | // - GoogleMap.animateCamera
171 | // - GoogleMap.snapshot
172 | // - GoogleMap.setInfoWindowAdapter (HTMLElement)
173 |
174 | // listeners to implement:
175 | // - a lot
176 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import { registerPlugin } from "@capacitor/core";
2 | import {
3 | CapacitorGoogleMapsPlugin,
4 | ElementFromPointResultOptions,
5 | } from "./definitions";
6 |
7 | const CapacitorGoogleMaps = registerPlugin(
8 | "CapacitorGoogleMaps",
9 | {
10 | web: () => import("./web").then((m) => new m.CapacitorGoogleMapsWeb()),
11 | }
12 | );
13 |
14 | CapacitorGoogleMaps.addListener("didRequestElementFromPoint", (data) => {
15 | const object: ElementFromPointResultOptions = {
16 | eventChainId: data?.eventChainId,
17 | mapId: null,
18 | isSameNode: false,
19 | };
20 |
21 | const { x, y } = data?.point || {};
22 |
23 | if (x && y) {
24 | const element = document.elementFromPoint(x, y);
25 |
26 | const mapId = element?.getAttribute?.("data-maps-id");
27 | if (mapId) {
28 | // if (ref.isSameNode(element)) {
29 | // object.isSameNode = true;
30 | // }
31 | object.mapId = mapId;
32 | object.isSameNode = true;
33 | }
34 | }
35 |
36 | CapacitorGoogleMaps.elementFromPointResult(object);
37 | });
38 |
39 | export * from "./definitions";
40 | export { CapacitorGoogleMaps };
41 |
--------------------------------------------------------------------------------
/src/interfaces/events/DidBeginDraggingMarker.ts:
--------------------------------------------------------------------------------
1 | import { Marker } from "./../../definitions";
2 |
3 | export interface DidBeginDraggingMarkerResult {
4 | marker: Marker;
5 | }
6 |
7 | export type DidBeginDraggingMarkerCallback = (
8 | result: DidBeginDraggingMarkerResult | null,
9 | err?: any
10 | ) => void;
11 |
--------------------------------------------------------------------------------
/src/interfaces/events/DidBeginMovingCamera.ts:
--------------------------------------------------------------------------------
1 | import { CameraMovementReason } from "./../../definitions";
2 |
3 | export interface DidBeginMovingCameraResult {
4 | reason: CameraMovementReason;
5 | }
6 |
7 | export type DidBeginMovingCameraCallback = (
8 | result: DidBeginMovingCameraResult,
9 | err?: any
10 | ) => void;
11 |
--------------------------------------------------------------------------------
/src/interfaces/events/DidCloseInfoWindow.ts:
--------------------------------------------------------------------------------
1 | import { Marker } from "./../../definitions";
2 |
3 | export interface DidCloseInfoWindowResult {
4 | marker: Marker;
5 | }
6 |
7 | export type DidCloseInfoWindowCallback = (
8 | result: DidCloseInfoWindowResult | null,
9 | err?: any
10 | ) => void;
11 |
--------------------------------------------------------------------------------
/src/interfaces/events/DidDragMarker.ts:
--------------------------------------------------------------------------------
1 | import { Marker } from "./../../definitions";
2 |
3 | export interface DidDragMarkerResult {
4 | marker: Marker;
5 | }
6 |
7 | export type DidDragMarkerCallback = (
8 | result: DidDragMarkerResult | null,
9 | err?: any
10 | ) => void;
11 |
--------------------------------------------------------------------------------
/src/interfaces/events/DidEndDraggingMarker.ts:
--------------------------------------------------------------------------------
1 | import { Marker } from "./../../definitions";
2 |
3 | export interface DidEndDraggingMarkerResult {
4 | marker: Marker;
5 | }
6 |
7 | export type DidEndDraggingMarkerCallback = (
8 | result: DidEndDraggingMarkerResult | null,
9 | err?: any
10 | ) => void;
11 |
--------------------------------------------------------------------------------
/src/interfaces/events/DidEndMovingCamera.ts:
--------------------------------------------------------------------------------
1 | import { CameraPosition } from "./../../definitions";
2 |
3 | export interface DidEndMovingCameraResult {
4 | cameraPosition: CameraPosition;
5 | }
6 |
7 | export type DidEndMovingCameraCallback = (
8 | result: DidEndMovingCameraResult,
9 | err?: any
10 | ) => void;
11 |
--------------------------------------------------------------------------------
/src/interfaces/events/DidLongPressMap.ts:
--------------------------------------------------------------------------------
1 | import { LatLng } from "./../../definitions";
2 |
3 | export interface DidLongPressMapResult {
4 | position: LatLng;
5 | }
6 |
7 | export type DidLongPressMapCallback = (
8 | result: DidLongPressMapResult | null,
9 | err?: any
10 | ) => void;
11 |
--------------------------------------------------------------------------------
/src/interfaces/events/DidMoveCamera.ts:
--------------------------------------------------------------------------------
1 | export type DidMoveCameraCallback = (
2 | result: undefined | null,
3 | err?: any
4 | ) => void;
5 |
--------------------------------------------------------------------------------
/src/interfaces/events/DidTapInfoWindow.ts:
--------------------------------------------------------------------------------
1 | import { Marker } from "./../../definitions";
2 |
3 | export interface DidTapInfoWindowResult {
4 | marker: Marker;
5 | }
6 |
7 | export type DidTapInfoWindowCallback = (
8 | result: DidTapInfoWindowResult | null,
9 | err?: any
10 | ) => void;
11 |
--------------------------------------------------------------------------------
/src/interfaces/events/DidTapMap.ts:
--------------------------------------------------------------------------------
1 | import { LatLng } from "./../../definitions";
2 |
3 | export interface DidTapMapResult {
4 | position: LatLng;
5 | }
6 |
7 | export type DidTapMapCallback = (
8 | result: DidTapMapResult | null,
9 | err?: any
10 | ) => void;
11 |
--------------------------------------------------------------------------------
/src/interfaces/events/DidTapMarker.ts:
--------------------------------------------------------------------------------
1 | import { Marker } from "./../../definitions";
2 |
3 | export interface DidTapMarkerResult {
4 | marker: Marker;
5 | }
6 |
7 | export type DidTapMarkerCallback = (
8 | result: DidTapMarkerResult | null,
9 | err?: any
10 | ) => void;
11 |
--------------------------------------------------------------------------------
/src/interfaces/events/DidTapMyLocationButton.ts:
--------------------------------------------------------------------------------
1 | export type DidTapMyLocationButtonCallback = (
2 | result: undefined | null,
3 | err?: any
4 | ) => void;
5 |
--------------------------------------------------------------------------------
/src/interfaces/events/DidTapMyLocationDot.ts:
--------------------------------------------------------------------------------
1 | import { LatLng } from "./../../definitions";
2 |
3 | export interface DidTapMyLocationDotResult {
4 | position: LatLng;
5 | }
6 |
7 | export type DidTapMyLocationDotCallback = (
8 | result: DidTapMyLocationDotResult | null,
9 | err?: any
10 | ) => void;
11 |
--------------------------------------------------------------------------------
/src/interfaces/events/DidTapPoi.ts:
--------------------------------------------------------------------------------
1 | import { PointOfInterest } from "./../../definitions";
2 |
3 | export interface DidTapPoiResult {
4 | poi: PointOfInterest;
5 | }
6 |
7 | export type DidTapPoiCallback = (
8 | result: DidTapPoiResult | null,
9 | err?: any
10 | ) => void;
11 |
--------------------------------------------------------------------------------
/src/interfaces/index.ts:
--------------------------------------------------------------------------------
1 | // methods
2 | export { InitializeOptions } from "./methods/Initialize";
3 | export { CreateMapOptions, CreateMapResult } from "./methods/CreateMap";
4 | export { UpdateMapOptions, UpdateMapResult } from "./methods/UpdateMap";
5 | export { RemoveMapOptions } from "./methods/RemoveMap";
6 | export { ClearMapOptions } from "./methods/ClearMap";
7 | export { MoveCameraOptions } from "./methods/MoveCamera";
8 | export { ElementFromPointResultOptions } from "./methods/ElementFromPointResult";
9 | export { AddMarkerOptions, AddMarkerResult } from "./methods/AddMarker";
10 | export {
11 | AddMarkersOptions,
12 | MarkerInputEntry,
13 | AddMarkersResult,
14 | } from "./methods/AddMarkers";
15 | export { RemoveMarkerOptions } from "./methods/RemoveMarker";
16 | export { AddPolygonOptions, AddPolygonResult } from "./methods/AddPolygon";
17 | export { RemovePolygonOptions } from "./methods/RemovePolygon";
18 |
19 | // events
20 | export * from "./events/DidTapInfoWindow";
21 | export * from "./events/DidCloseInfoWindow";
22 | export * from "./events/DidTapMap";
23 | export * from "./events/DidLongPressMap";
24 | export * from "./events/DidTapMarker";
25 | export * from "./events/DidBeginDraggingMarker";
26 | export * from "./events/DidDragMarker";
27 | export * from "./events/DidEndDraggingMarker";
28 | export * from "./events/DidTapMyLocationButton";
29 | export * from "./events/DidTapMyLocationDot";
30 | export * from "./events/DidTapPoi";
31 | export * from "./events/DidBeginMovingCamera";
32 | export * from "./events/DidMoveCamera";
33 | export * from "./events/DidEndMovingCamera";
34 |
35 | // models
36 | export { CameraMovementReason } from "./models/GoogleMap/Camera/MovementReason";
37 | export { CameraPosition } from "./models/GoogleMap/Camera/Position";
38 | export { Marker } from "./models/GoogleMap/Marker/Marker";
39 | export { MarkerPreferences } from "./models/GoogleMap/Marker/MarkerPreferences";
40 | export { MarkerIcon } from "./models/GoogleMap/Marker/MarkerIcon";
41 | export { MarkerIconSize } from "./models/GoogleMap/Marker/MarkerIconSize";
42 | export { MapAppearance } from "./models/GoogleMap/Appearance";
43 | export { MapControls } from "./models/GoogleMap/Controls";
44 | export { MapGestures } from "./models/GoogleMap/Gestures";
45 | export { GoogleMap } from "./models/GoogleMap/GoogleMap";
46 | export { MapPreferences } from "./models/GoogleMap/Preferences";
47 | export { PointOfInterest } from "./models/GoogleMap/PointOfInterest";
48 | export { Polygon } from "./models/GoogleMap/Polygon/Polygon";
49 | export { PolygonPreferences } from "./models/GoogleMap/Polygon/PolygonPreferences";
50 | export { BoundingRect } from "./models/BoundingRect";
51 | export { LatLng } from "./models/LatLng";
52 |
--------------------------------------------------------------------------------
/src/interfaces/methods/AddMarker.ts:
--------------------------------------------------------------------------------
1 | import { LatLng, Marker, MarkerPreferences } from "./../../definitions";
2 |
3 | export interface AddMarkerOptions {
4 | /**
5 | * @since 2.0.0
6 | */
7 | mapId: string;
8 | /**
9 | * @since 2.0.0
10 | */
11 | position: LatLng;
12 | /**
13 | * @since 2.0.0
14 | */
15 | preferences?: MarkerPreferences;
16 | }
17 |
18 | export interface AddMarkerResult {
19 | /**
20 | * @since 2.0.0
21 | */
22 | marker: Marker;
23 | }
24 |
--------------------------------------------------------------------------------
/src/interfaces/methods/AddMarkers.ts:
--------------------------------------------------------------------------------
1 | import { LatLng, MarkerPreferences } from "./../../definitions";
2 |
3 | export interface MarkerInputEntry {
4 | /**
5 | * @since 2.0.0
6 | */
7 | position: LatLng;
8 | /**
9 | * @since 2.0.0
10 | */
11 | preferences?: MarkerPreferences;
12 | }
13 |
14 | export interface AddMarkersOptions {
15 | /**
16 | * @since 2.0.0
17 | */
18 | mapId: string;
19 | /**
20 | * @since 2.0.0
21 | */
22 | markers: MarkerInputEntry[];
23 | }
24 |
25 | export interface MarkerOutputEntry {
26 | /**
27 | * GUID representing the unique id of this marker
28 | *
29 | * @since 2.0.0
30 | */
31 | markerId: string;
32 | /**
33 | * @since 2.0.0
34 | */
35 | position: LatLng;
36 | /**
37 | * @since 2.0.0
38 | */
39 | preferences: MarkerPreferences;
40 | }
41 |
42 | export interface AddMarkersResult {
43 | /**
44 | * @since 2.0.0
45 | */
46 | mapId: string;
47 | /**
48 | * @since 2.0.0
49 | */
50 | markers: MarkerOutputEntry[];
51 | }
52 |
--------------------------------------------------------------------------------
/src/interfaces/methods/AddPolygon.ts:
--------------------------------------------------------------------------------
1 | import { Polygon, PolygonPreferences, LatLng } from "../../definitions";
2 |
3 | export interface AddPolygonOptions {
4 | /**
5 | * GUID representing the map this polygon is a part of
6 | *
7 | * @since 2.0.0
8 | */
9 | mapId: string;
10 | /**
11 | * The path (outline) is specified by a list of vertices in clockwise or counterclockwise order.
12 | * It is not necessary for the start and end points to coincide;
13 | * if they do not, the polygon will be automatically closed.
14 | * Line segments are drawn between consecutive points in the shorter of the two directions (east or west).
15 | *
16 | * @since 2.0.0
17 | */
18 | path: LatLng[];
19 | /**
20 | * @since 2.0.0
21 | */
22 | preferences?: PolygonPreferences;
23 | }
24 |
25 | export interface AddPolygonResult {
26 | /**
27 | * @since 2.0.0
28 | */
29 | polygon: Polygon;
30 | }
31 |
--------------------------------------------------------------------------------
/src/interfaces/methods/ClearMap.ts:
--------------------------------------------------------------------------------
1 | export interface ClearMapOptions {
2 | /**
3 | * @since 2.0.0
4 | */
5 | mapId: string;
6 | }
7 |
--------------------------------------------------------------------------------
/src/interfaces/methods/CreateMap.ts:
--------------------------------------------------------------------------------
1 | import {
2 | GoogleMap,
3 | BoundingRect,
4 | MapPreferences,
5 | CameraPosition,
6 | } from "./../../definitions";
7 |
8 | export interface CreateMapOptions {
9 | /**
10 | * @since 2.0.0
11 | */
12 | element?: HTMLElement;
13 | /**
14 | * @since 2.0.0
15 | */
16 | boundingRect?: BoundingRect;
17 | /**
18 | * @since 2.0.0
19 | */
20 | cameraPosition: CameraPosition;
21 | /**
22 | * @since 2.0.0
23 | */
24 | preferences?: MapPreferences;
25 | }
26 |
27 | export interface CreateMapResult {
28 | /**
29 | * @since 2.0.0
30 | */
31 | googleMap: GoogleMap;
32 | }
33 |
--------------------------------------------------------------------------------
/src/interfaces/methods/ElementFromPointResult.ts:
--------------------------------------------------------------------------------
1 | export interface ElementFromPointResultOptions {
2 | eventChainId: string;
3 | mapId: string | null;
4 | isSameNode: boolean;
5 | }
6 |
--------------------------------------------------------------------------------
/src/interfaces/methods/Initialize.ts:
--------------------------------------------------------------------------------
1 | export interface InitializeOptions {
2 | /**
3 | * Defines the pixel ratio of the current device in the current state.
4 | * Recommended to be set by using `window.devicePixelRatio`.
5 | * This is needed because real pixels on a device do not necessarily correspond with how pixels are calculated in a WebView.
6 | *
7 | * @since 2.0.0
8 | */
9 | devicePixelRatio: number;
10 | /**
11 | * (iOS only)
12 | * API Key for Google Maps SDK for iOS.
13 | * Hence it is only required on iOS.
14 | *
15 | * @since 1.0.0
16 | */
17 | key?: string;
18 | }
19 |
--------------------------------------------------------------------------------
/src/interfaces/methods/MoveCamera.ts:
--------------------------------------------------------------------------------
1 | import { CameraPosition } from "./../../definitions";
2 |
3 | export interface MoveCameraOptions {
4 | /**
5 | * The identifier of the map to which this method should be applied.
6 | *
7 | * @since 2.0.0
8 | */
9 | mapId: string;
10 | /**
11 | * @since 2.0.0
12 | */
13 | cameraPosition: CameraPosition;
14 | /**
15 | * The duration of the animation in milliseconds.
16 | * If not specified, or equals or smaller than 0, the camera movement will be immediate
17 | *
18 | * @default 0
19 | * @since 2.0.0
20 | */
21 | duration?: number;
22 | /**
23 | * By default the moveCamera method uses the current CameraPosition as the base.
24 | * That means that if, for example, the CameraPosition.target is not specified,
25 | * the current CameraPosition.target will be used.
26 | * Among other things, this default behaviour allows you to set the zoom without moving the map.
27 | * Or move the map without changing the current zoom.
28 | * If instead of this default behaviour, the previous CameraPosition (the one you gave the previous
29 | * time you called `moveCamera` or `createMap`) should be used as the base, this parameter should be set to `false`.
30 | * But be cautious when using this.
31 | * If the user made changes to the CameraPosition (e.g. by scrolling or zooming the map),
32 | * those changes will be undone because it will be overwritten by the last explicitly set CameraPosition.
33 | * The CameraPosition is only "explicitly set" with these methods: `createMap` and `moveCamera`.
34 | *
35 | * @default true
36 | * @since 2.0.0
37 | */
38 | useCurrentCameraPositionAsBase?: boolean;
39 | }
40 |
--------------------------------------------------------------------------------
/src/interfaces/methods/RemoveMap.ts:
--------------------------------------------------------------------------------
1 | export interface RemoveMapOptions {
2 | /**
3 | * @since 2.0.0
4 | */
5 | mapId: string;
6 | }
7 |
--------------------------------------------------------------------------------
/src/interfaces/methods/RemoveMarker.ts:
--------------------------------------------------------------------------------
1 | export interface RemoveMarkerOptions {
2 | /**
3 | * @since 2.0.0
4 | */
5 | mapId: string;
6 | /**
7 | * @since 2.0.0
8 | */
9 | markerId: string;
10 | }
11 |
--------------------------------------------------------------------------------
/src/interfaces/methods/RemovePolygon.ts:
--------------------------------------------------------------------------------
1 | export interface RemovePolygonOptions {
2 | /**
3 | * @since 2.0.0
4 | */
5 | mapId: string;
6 | /**
7 | * @since 2.0.0
8 | */
9 | polygonId: string;
10 | }
11 |
--------------------------------------------------------------------------------
/src/interfaces/methods/UpdateMap.ts:
--------------------------------------------------------------------------------
1 | import { GoogleMap, BoundingRect, MapPreferences } from "./../../definitions";
2 |
3 | export interface UpdateMapOptions {
4 | /**
5 | * @since 2.0.0
6 | */
7 | mapId: string;
8 | /**
9 | * @since 2.0.0
10 | */
11 | element?: HTMLElement;
12 | /**
13 | * @since 2.0.0
14 | */
15 | boundingRect?: BoundingRect;
16 | /**
17 | * @since 2.0.0
18 | */
19 | preferences?: MapPreferences;
20 | }
21 |
22 | export interface UpdateMapResult {
23 | /**
24 | * @since 2.0.0
25 | */
26 | googleMap: GoogleMap;
27 | }
28 |
--------------------------------------------------------------------------------
/src/interfaces/models/BoundingRect.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @todo: documentation
3 | */
4 | export interface BoundingRect {
5 | width: number;
6 | height: number;
7 | x: number;
8 | y: number;
9 | }
10 |
--------------------------------------------------------------------------------
/src/interfaces/models/GoogleMap/Appearance.ts:
--------------------------------------------------------------------------------
1 | enum MapType {
2 | /**
3 | * No base map tiles.
4 | *
5 | * @since 2.0.0
6 | */
7 | None = 0,
8 | /**
9 | * Basic map.
10 | *
11 | * @since 2.0.0
12 | */
13 | Normal = 1,
14 | /**
15 | * Satellite imagery with no labels.
16 | *
17 | * @since 2.0.0
18 | */
19 | Satellite = 2,
20 | /**
21 | * Topographic data.
22 | *
23 | * @since 2.0.0
24 | */
25 | Terrain = 3,
26 | /**
27 | * Satellite imagery with roads and labels.
28 | *
29 | * @since 2.0.0
30 | */
31 | Hybrid = 4,
32 | }
33 |
34 | /**
35 | * Aggregates all appearance parameters such as showing 3d building, indoor maps, the my-location (blue) dot and traffic.
36 | * Additionally, it also holds parameters such as the type of map tiles and the overall styling of the base map.
37 | */
38 | export interface MapAppearance {
39 | /**
40 | * Controls the type of map tiles that should be displayed.
41 | *
42 | * @default MapType.Normal
43 | * @since 2.0.0
44 | */
45 | type?: MapType;
46 | /**
47 | * Holds details about a style which can be applied to a map.
48 | * When set to `null` the default styling will be used.
49 | * With style options you can customize the presentation of the standard Google map styles, changing the visual display of features like roads, parks, and other points of interest.
50 | * As well as changing the style of these features, you can also hide features entirely.
51 | * This means that you can emphasize particular components of the map or make the map complement the content of your app.
52 | * For more information check: https://developers.google.com/maps/documentation/ios-sdk/style-reference
53 | * Or use the wizard for generating JSON: https://mapstyle.withgoogle.com/
54 | *
55 | * @default null
56 | * @since 2.0.0
57 | */
58 | style?: string | null;
59 | /**
60 | * If `true`, 3D buildings will be shown where available.
61 | *
62 | * @default true
63 | * @since 2.0.0
64 | */
65 | isBuildingsShown?: boolean;
66 | /**
67 | * If `true`, indoor maps are shown, where available.
68 | * If this is set to false, caches for indoor data may be purged and any floor currently selected by the end-user may be reset.
69 | *
70 | * @default true
71 | * @since 2.0.0
72 | */
73 | isIndoorShown?: boolean;
74 | /**
75 | * If `true`, the my-location (blue) dot and accuracy circle are shown.
76 | *
77 | * @default false
78 | * @since 2.0.0
79 | */
80 | isMyLocationDotShown?: boolean;
81 | /**
82 | * If `true`, the map draws traffic data, if available.
83 | * This is subject to the availability of traffic data.
84 | *
85 | * @default false
86 | * @since 2.0.0
87 | */
88 | isTrafficShown?: boolean;
89 | }
90 |
--------------------------------------------------------------------------------
/src/interfaces/models/GoogleMap/Camera/MovementReason.ts:
--------------------------------------------------------------------------------
1 | export enum CameraMovementReason {
2 | /**
3 | * Camera motion initiated in response to user gestures on the map.
4 | * For example: pan, tilt, pinch to zoom, or rotate.
5 | *
6 | * @since 2.0.0
7 | */
8 | Gesture = 1,
9 | /**
10 | * Indicates that this is part of a programmatic change - for example, via methods such as `moveCamera`.
11 | * This may also be the case if a user has tapped on the My Location or compass buttons, which generate animations that change the camera.
12 | *
13 | * @since 2.0.0
14 | */
15 | Other = 2,
16 | }
17 |
--------------------------------------------------------------------------------
/src/interfaces/models/GoogleMap/Camera/Position.ts:
--------------------------------------------------------------------------------
1 | import { LatLng } from "./../../../../definitions";
2 |
3 | /**
4 | * The map view is modeled as a camera looking down on a flat plane.
5 | * The position of the camera (and hence the rendering of the map) is specified by the following properties: target (latitude/longitude location), bearing, tilt, and zoom.
6 | * More information can be found here: https://developers.google.com/maps/documentation/android-sdk/views#the_camera_position
7 | */
8 | export interface CameraPosition {
9 | /**
10 | * The camera target is the location of the center of the map, specified as latitude and longitude co-ordinates.
11 | *
12 | * @since 2.0.0
13 | */
14 | target?: LatLng;
15 | /**
16 | * The camera bearing is the direction in which a vertical line on the map points, measured in degrees clockwise from north.
17 | * Someone driving a car often turns a road map to align it with their direction of travel, while hikers using a map and compass usually orient the map so that a vertical line is pointing north.
18 | * The Maps API lets you change a map's alignment or bearing.
19 | * For example, a bearing of 90 degrees results in a map where the upwards direction points due east.
20 | *
21 | * @since 2.0.0
22 | */
23 | bearing?: number;
24 | /**
25 | * The tilt defines the camera's position on an arc between directly over the map's center position and the surface of the Earth, measured in degrees from the nadir (the direction pointing directly below the camera).
26 | * When you change the viewing angle, the map appears in perspective, with far-away features appearing smaller, and nearby features appearing larger.
27 | *
28 | * @since 2.0.0
29 | */
30 | tilt?: number;
31 | /**
32 | * The zoom level of the camera determines the scale of the map.
33 | * At larger zoom levels more detail can be seen on the screen,
34 | * while at smaller zoom levels more of the world can be seen on the screen.
35 | *
36 | * @since 2.0.0
37 | */
38 | zoom?: number;
39 | }
40 |
--------------------------------------------------------------------------------
/src/interfaces/models/GoogleMap/Controls.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Aggregates all control parameters such as enabling the compass, my-location and zoom buttons as well as the toolbar.
3 | */
4 | export interface MapControls {
5 | /**
6 | * If `true`, the compass button is enabled.
7 | * The compass is an icon on the map that indicates the direction of north on the map.
8 | * If enabled, it is only shown when the camera is rotated away from its default orientation (bearing of 0).
9 | * When a user taps the compass, the camera orients itself to its default orientation and fades away shortly after.
10 | * If disabled, the compass will never be displayed.
11 | *
12 | * @default true
13 | * @since 2.0.0
14 | */
15 | isCompassButtonEnabled?: boolean;
16 | // TODO:
17 | // /**
18 | // * If `true`, the indoor level picker is enabled.
19 | // * If the button is enabled, it is only shown when `MapAppearance.isIndoorShown === true`.
20 | // * Additionally, it is only visible when the view is focused on a building with indoor floor data.
21 | // *
22 | // */
23 | // isIndoorLevelPickerEnabled: boolean;
24 | /**
25 | * (Android only)
26 | *
27 | * If `true`, the Map Toolbar is enabled.
28 | * If enabled, and the Map Toolbar can be shown in the current context, users will see a bar with various context-dependent actions, including 'open this map in the Google Maps app' and 'find directions to the highlighted marker in the Google Maps app'.
29 | *
30 | * @default false
31 | * @since 2.0.0
32 | */
33 | isMapToolbarEnabled?: boolean;
34 | /**
35 | * If `true`, the my-location button is enabled.
36 | * This is a button visible on the map that, when tapped by users, will center the map on the current user location.
37 | * If the button is enabled, it is only shown when `MapAppearance.isMyLocationDotShown === true`.
38 | *
39 | * @default true
40 | * @since 2.0.0
41 | */
42 | isMyLocationButtonEnabled?: boolean;
43 | /**
44 | * (Android only)
45 | * If `true`, the zoom controls are enabled.
46 | * The zoom controls are a pair of buttons (one for zooming in, one for zooming out) that appear on the screen when enabled.
47 | * When pressed, they cause the camera to zoom in (or out) by one zoom level.
48 | * If disabled, the zoom controls are not shown.
49 | *
50 | * @default false
51 | * @since 2.0.0
52 | */
53 | isZoomButtonsEnabled?: boolean;
54 | }
55 |
--------------------------------------------------------------------------------
/src/interfaces/models/GoogleMap/Gestures.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Aggregates all gesture parameters such as allowing for rotating, scrolling, tilting and zooming the map.
3 | */
4 | export interface MapGestures {
5 | /**
6 | * If `true`, rotate gestures are allowed.
7 | * If enabled, users can use a two-finger rotate gesture to rotate the camera.
8 | * If disabled, users cannot rotate the camera via gestures.
9 | * This setting doesn't restrict the user from tapping the compass button to reset the camera orientation,
10 | * nor does it restrict programmatic movements and animation of the camera.
11 | *
12 | * @default true
13 | * @since 2.0.0
14 | */
15 | isRotateAllowed?: boolean;
16 | /**
17 | * If `true`, scroll gestures are allowed.
18 | * If enabled, users can swipe to pan the camera.
19 | * If disabled, swiping has no effect.
20 | * This setting doesn't restrict programmatic movement and animation of the camera.
21 | *
22 | * @default true
23 | * @since 2.0.0
24 | */
25 | isScrollAllowed?: boolean;
26 | /**
27 | * If `true`, scroll gestures can take place at the same time as a zoom or rotate gesture.
28 | * If enabled, users can scroll the map while rotating or zooming the map.
29 | * If disabled, the map cannot be scrolled while the user rotates or zooms the map using gestures.
30 | * This setting doesn't disable scroll gestures entirely, only during rotation and zoom gestures,
31 | * nor does it restrict programmatic movements and animation of the camera.
32 | *
33 | * @default true
34 | * @since 2.0.0
35 | */
36 | isScrollAllowedDuringRotateOrZoom?: boolean;
37 | /**
38 | * If `true`, tilt gestures are allowed.
39 | * If enabled, users can use a two-finger vertical down swipe to tilt the camera.
40 | * If disabled, users cannot tilt the camera via gestures.
41 | * This setting doesn't restrict users from tapping the compass button to reset the camera orientation,
42 | * nor does it restrict programmatic movement and animation of the camera.
43 | *
44 | * @default true
45 | * @since 2.0.0
46 | */
47 | isTiltAllowed?: boolean;
48 | /**
49 | * If `true`, zoom gestures are allowed.
50 | * If enabled, users can either double tap/two-finger tap or pinch to zoom the camera.
51 | * If disabled, these gestures have no effect.
52 | * This setting doesn't affect the zoom buttons,
53 | * nor does it restrict programmatic movement and animation of the camera.
54 | *
55 | * @default true
56 | * @since 2.0.0
57 | */
58 | isZoomAllowed?: boolean;
59 | }
60 |
--------------------------------------------------------------------------------
/src/interfaces/models/GoogleMap/GoogleMap.ts:
--------------------------------------------------------------------------------
1 | import { MapPreferences, CameraPosition } from "./../../../definitions";
2 |
3 | export interface GoogleMap {
4 | /**
5 | * GUID representing the unique id of this map
6 | *
7 | * @since 2.0.0
8 | */
9 | mapId: string;
10 | /**
11 | * @since 2.0.0
12 | */
13 | cameraPosition: CameraPosition;
14 | /**
15 | * @since 2.0.0
16 | */
17 | preferences: MapPreferences;
18 | }
19 |
--------------------------------------------------------------------------------
/src/interfaces/models/GoogleMap/Marker/Marker.ts:
--------------------------------------------------------------------------------
1 | import { MarkerPreferences, LatLng } from "./../../../../definitions";
2 |
3 | export interface Marker {
4 | /**
5 | * GUID representing the map this marker is part of
6 | *
7 | * @since 2.0.0
8 | */
9 | mapId: string;
10 | /**
11 | * GUID representing the unique id of this marker
12 | *
13 | * @since 2.0.0
14 | */
15 | markerId: string;
16 | /**
17 | * @since 2.0.0
18 | */
19 | position: LatLng;
20 | /**
21 | * @since 2.0.0
22 | */
23 | preferences: MarkerPreferences;
24 | }
25 |
--------------------------------------------------------------------------------
/src/interfaces/models/GoogleMap/Marker/MarkerIcon.ts:
--------------------------------------------------------------------------------
1 | import { MarkerIconSize } from "./../../../../definitions";
2 |
3 | /**
4 | * A data class representing icon/marker.
5 | */
6 | export interface MarkerIcon {
7 | /**
8 | * URL path to icon
9 | *
10 | * @since 2.0.0
11 | */
12 | url: string;
13 | /**
14 | * Target icon size in pixels. Defaults to 30x30 if not specified.
15 | *
16 | * @since 2.0.0
17 | */
18 | size?: MarkerIconSize;
19 | }
20 |
--------------------------------------------------------------------------------
/src/interfaces/models/GoogleMap/Marker/MarkerIconSize.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * A data class representing a pair of "width" and "height" in pixels.
3 | */
4 | export interface MarkerIconSize {
5 | /**
6 | * Width in pixels
7 | *
8 | * @default 30
9 | * @since 2.0.0
10 | */
11 | width: number;
12 | /**
13 | * Height in pixels
14 | *
15 | * @default 30
16 | * @since 2.0.0
17 | */
18 | height: number;
19 | }
20 |
--------------------------------------------------------------------------------
/src/interfaces/models/GoogleMap/Marker/MarkerPreferences.ts:
--------------------------------------------------------------------------------
1 | import { MarkerIcon } from "./../../../../definitions";
2 |
3 | export interface MarkerPreferences {
4 | /**
5 | * A text string that's displayed in an info window when the user taps the marker.
6 | * You can change this value at any time.
7 | *
8 | * @default null
9 | * @since 2.0.0
10 | */
11 | title?: string;
12 | /**
13 | * Additional text that's displayed below the title. You can change this value at any time.
14 | *
15 | * @default null
16 | * @since 2.0.0
17 | */
18 | snippet?: string;
19 | /**
20 | * This is a value from 0 to 1, where 0 means the marker is completely transparent and 1 means the marker is completely opaque.
21 | *
22 | * @default 1
23 | * @since 2.0.0
24 | */
25 | opacity?: number;
26 | /**
27 | * Controls whether this marker should be flat against the Earth's surface (`true`) or a billboard facing the camera (`false`).
28 | *
29 | * @default false
30 | * @since 2.0.0
31 | */
32 | isFlat?: boolean;
33 | /**
34 | * Controls whether this marker can be dragged interactively.
35 | * When a marker is draggable, it can be moved by the user by long pressing on the marker.
36 | *
37 | * @default false
38 | * @since 2.0.0
39 | */
40 | isDraggable?: boolean;
41 | /**
42 | * The z-index specifies the stack order of this marker, relative to other markers on the map.
43 | * A marker with a high z-index is drawn on top of markers with lower z-indexes.
44 | * Markers are always drawn above tile layers and other non-marker overlays (ground overlays,
45 | * polylines, polygons, and other shapes) regardless of the z-index of the other overlays.
46 | * Markers are effectively considered to be in a separate z-index group compared to other overlays.
47 | *
48 | * @default 0
49 | * @since 2.0.0
50 | */
51 | zIndex?: number;
52 | /**
53 | * Specifies the anchor to be at a particular point in the marker image.
54 | *
55 | * The anchor specifies the point in the icon image that is anchored to the marker's position on the Earth's surface.
56 | *
57 | * The anchor point is specified in the continuous space [0.0, 1.0] x [0.0, 1.0], where (0, 0) is the top-left corner of the image, and (1, 1) is the bottom-right corner.
58 | *
59 | * Read more about it here: https://developers.google.com/android/reference/com/google/android/gms/maps/model/MarkerOptions#anchor(float,%20float)
60 | *
61 | * @default { x: 0.5, y: 1 }
62 | * @since 2.0.0
63 | */
64 | anchor?: { x: number; y: number };
65 | /**
66 | * @default undefined
67 | * @since 2.0.0
68 | */
69 | icon?: MarkerIcon;
70 | /**
71 | * You can use this property to associate an arbitrary object with this overlay.
72 | * The Google Maps SDK neither reads nor writes this property.
73 | * Note that metadata should not hold any strong references to any Maps objects,
74 | * otherwise a retain cycle may be created (preventing objects from being released).
75 | *
76 | * @default {}
77 | * @since 2.0.0
78 | */
79 | metadata?: { [key: string]: any };
80 | }
81 |
--------------------------------------------------------------------------------
/src/interfaces/models/GoogleMap/PointOfInterest.ts:
--------------------------------------------------------------------------------
1 | import { LatLng } from "./../../../definitions";
2 |
3 | export interface PointOfInterest {
4 | /**
5 | * The name of the POI.
6 | *
7 | * @since 2.0.0
8 | */
9 | name: String;
10 | /**
11 | * The placeId of the POI.
12 | * Read more about what you can use a placeId for here: https://developers.google.com/maps/documentation/places/web-service/place-id
13 | *
14 | * @since 2.0.0
15 | */
16 | placeId: String;
17 | /**
18 | * @since 2.0.0
19 | */
20 | position: LatLng;
21 | }
22 |
--------------------------------------------------------------------------------
/src/interfaces/models/GoogleMap/Polygon/Polygon.ts:
--------------------------------------------------------------------------------
1 | import { LatLng, PolygonPreferences } from "../../../../definitions";
2 |
3 | export interface Polygon {
4 | /**
5 | * GUID representing the map this polygon is a part of
6 | *
7 | * @since 2.0.0
8 | */
9 | mapId: string;
10 | /**
11 | * GUID representing the unique id of this polygon
12 | *
13 | * @since 2.0.0
14 | */
15 | polygonId: string;
16 | /**
17 | * The path (outline) is specified by a list of vertices in clockwise or counterclockwise order.
18 | * It is not necessary for the start and end points to coincide;
19 | * if they do not, the polygon will be automatically closed.
20 | * Line segments are drawn between consecutive points in the shorter of the two directions (east or west).
21 | *
22 | * @since 2.0.0
23 | */
24 | path: LatLng[];
25 | /**
26 | * @since 2.0.0
27 | */
28 | preferences?: PolygonPreferences;
29 | }
30 |
--------------------------------------------------------------------------------
/src/interfaces/models/GoogleMap/Polygon/PolygonPreferences.ts:
--------------------------------------------------------------------------------
1 | import { LatLng } from "../../../../definitions";
2 |
3 | export interface PolygonPreferences {
4 | /**
5 | * A hole is a region inside the polygon that is not filled.
6 | * A hole is specified in exactly the same way as the path of the polygon itself.
7 | * A hole must be fully contained within the outline.
8 | * Multiple holes can be specified, however overlapping holes are not supported.
9 | *
10 | * @since 2.0.0
11 | */
12 | holes?: LatLng[][];
13 | /**
14 | * Line segment width in screen pixels.
15 | * The width is constant and independent of the camera's zoom level.
16 | *
17 | * @default 10
18 | * @since 2.0.0
19 | */
20 | strokeWidth?: number;
21 | /**
22 | * Line segment color in HEX format (with transparency).
23 | *
24 | * @default #000000 (black)
25 | * @since 2.0.0
26 | */
27 | strokeColor?: string;
28 | /**
29 | * Fill color in HEX format (with transparency).
30 | *
31 | * @default #00000000 (transparent)
32 | * @since 2.0.0
33 | */
34 | fillColor?: string;
35 | /**
36 | * The z-index specifies the stack order of this polygon, relative to other polygons on the map.
37 | * A polygon with a higher z-index is drawn on top of those with lower indices.
38 | * Markers are always drawn above tile layers and other non-marker overlays (ground overlays,
39 | * polylines, polygons, and other shapes) regardless of the z-index of the other overlays.
40 | * Markers are effectively considered to be in a separate z-index group compared to other overlays.
41 | *
42 | * @default 0
43 | * @since 2.0.0
44 | */
45 | zIndex?: number;
46 | /**
47 | * Sets the visibility of this polygon.
48 | * When not visible, a polygon is not drawn, but it keeps all its other properties.
49 | *
50 | * @default true
51 | * @since 2.0.0
52 | */
53 | isVisible?: boolean;
54 | /**
55 | * If `true`, then each segment is drawn as a geodesic.
56 | * If `false`, each segment is drawn as a straight line on the Mercator projection.
57 | *
58 | * @default false
59 | * @since 2.0.0
60 | */
61 | isGeodesic?: boolean;
62 | /**
63 | * If you want to handle events fired when the user clicks the polygon, set this property to `true`.
64 | * You can change this value at any time.
65 | *
66 | * @default false
67 | * @since 2.0.0
68 | */
69 | isClickable?: boolean;
70 | /**
71 | * You can use this property to associate an arbitrary object with this overlay.
72 | * The Google Maps SDK neither reads nor writes this property.
73 | * Note that metadata should not hold any strong references to any Maps objects,
74 | * otherwise a retain cycle may be created (preventing objects from being released).
75 | *
76 | * @default {}
77 | * @since 2.0.0
78 | */
79 | metadata?: { [key: string]: any };
80 | }
81 |
--------------------------------------------------------------------------------
/src/interfaces/models/GoogleMap/Preferences.ts:
--------------------------------------------------------------------------------
1 | import {
2 | MapAppearance,
3 | MapControls,
4 | MapGestures,
5 | } from "./../../../definitions";
6 |
7 | export interface MapPreferences {
8 | /**
9 | * @since 2.0.0
10 | */
11 | gestures?: MapGestures;
12 | /**
13 | * @since 2.0.0
14 | */
15 | controls?: MapControls;
16 | /**
17 | * @since 2.0.0
18 | */
19 | appearance?: MapAppearance;
20 |
21 | maxZoom?: number; // @todo: Sets a preferred upper bound for the camera zoom.
22 |
23 | minZoom?: number; // @todo: Sets a preferred lower bound for the camera zoom.
24 |
25 | padding?: any; // @todo: Sets padding on the map.
26 |
27 | liteMode?: boolean; // @todo
28 | }
29 |
--------------------------------------------------------------------------------
/src/interfaces/models/LatLng.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * A data class representing a pair of latitude and longitude coordinates, stored as degrees.
3 | */
4 | export interface LatLng {
5 | /**
6 | * Latitude, in degrees. This value is in the range [-90, 90].
7 | *
8 | * @since 2.0.0
9 | */
10 | latitude: number;
11 | /**
12 | * Longitude, in degrees. This value is in the range [-180, 180].
13 | *
14 | * @since 2.0.0
15 | */
16 | longitude: number;
17 | }
18 |
--------------------------------------------------------------------------------
/src/web.ts:
--------------------------------------------------------------------------------
1 | import { WebPlugin } from "@capacitor/core";
2 |
3 | import {
4 | CapacitorGoogleMapsPlugin,
5 | CallbackID,
6 | InitializeOptions,
7 | CreateMapOptions,
8 | CreateMapResult,
9 | UpdateMapOptions,
10 | UpdateMapResult,
11 | RemoveMapOptions,
12 | ClearMapOptions,
13 | MoveCameraOptions,
14 | ElementFromPointResultOptions,
15 | AddMarkerOptions,
16 | AddMarkerResult,
17 | AddMarkersOptions,
18 | AddMarkersResult,
19 | RemoveMarkerOptions,
20 | AddPolygonOptions,
21 | AddPolygonResult,
22 | RemovePolygonOptions,
23 | DidTapInfoWindowCallback,
24 | DidCloseInfoWindowCallback,
25 | DidTapMapCallback,
26 | DidLongPressMapCallback,
27 | DidTapMarkerCallback,
28 | DidBeginDraggingMarkerCallback,
29 | DidDragMarkerCallback,
30 | DidEndDraggingMarkerCallback,
31 | DidTapMyLocationButtonCallback,
32 | DidTapMyLocationDotCallback,
33 | DidTapPoiCallback,
34 | DidBeginMovingCameraCallback,
35 | DidMoveCameraCallback,
36 | DidEndMovingCameraCallback,
37 | DefaultEventOptions,
38 | DefaultEventWithPreventDefaultOptions,
39 | } from "./definitions";
40 |
41 | export class CapacitorGoogleMapsWeb
42 | extends WebPlugin
43 | implements CapacitorGoogleMapsPlugin
44 | {
45 | constructor() {
46 | super({
47 | name: "CapacitorGoogleMaps",
48 | platforms: ["web"],
49 | });
50 | }
51 |
52 | async initialize(_options: InitializeOptions): Promise {
53 | throw this.unimplemented("Not implemented on web.");
54 | }
55 |
56 | async createMap(_options: CreateMapOptions): Promise {
57 | throw this.unimplemented("Not implemented on web.");
58 | }
59 |
60 | async removeMap(_options: RemoveMapOptions): Promise {
61 | throw this.unimplemented("Not implemented on web.");
62 | }
63 |
64 | async clearMap(_options: ClearMapOptions): Promise {
65 | throw this.unimplemented("Not implemented on web.");
66 | }
67 |
68 | async updateMap(_options: UpdateMapOptions): Promise {
69 | throw this.unimplemented("Not implemented on web.");
70 | }
71 |
72 | async moveCamera(_options: MoveCameraOptions): Promise {
73 | throw this.unimplemented("Not implemented on web.");
74 | }
75 |
76 | async addMarker(_options: AddMarkerOptions): Promise {
77 | throw this.unimplemented("Not implemented on web.");
78 | }
79 |
80 | async addMarkers(_options: AddMarkersOptions): Promise {
81 | throw this.unimplemented("Not implemented on web.");
82 | }
83 |
84 | async removeMarker(_options: RemoveMarkerOptions): Promise {
85 | throw this.unimplemented("Not implemented on web.");
86 | }
87 |
88 | async addPolygon(_options: AddPolygonOptions): Promise {
89 | throw this.unimplemented("Not implemented on web.");
90 | }
91 |
92 | async removePolygon(_options: RemovePolygonOptions): Promise {
93 | throw this.unimplemented("Not implemented on web.");
94 | }
95 |
96 | async didTapInfoWindow(
97 | _options: DefaultEventOptions,
98 | _callback: DidTapInfoWindowCallback
99 | ): Promise {
100 | throw this.unimplemented("Not implemented on web.");
101 | }
102 |
103 | async didCloseInfoWindow(
104 | _options: DefaultEventOptions,
105 | _callback: DidCloseInfoWindowCallback
106 | ): Promise {
107 | throw this.unimplemented("Not implemented on web.");
108 | }
109 |
110 | async didTapMap(
111 | _options: DefaultEventOptions,
112 | _callback: DidTapMapCallback
113 | ): Promise {
114 | throw this.unimplemented("Not implemented on web.");
115 | }
116 |
117 | async didLongPressMap(
118 | _options: DefaultEventOptions,
119 | _callback: DidLongPressMapCallback
120 | ): Promise {
121 | throw this.unimplemented("Not implemented on web.");
122 | }
123 |
124 | async didTapMarker(
125 | _options: DefaultEventWithPreventDefaultOptions,
126 | _callback: DidTapMarkerCallback
127 | ): Promise {
128 | throw this.unimplemented("Not implemented on web.");
129 | }
130 |
131 | async didBeginDraggingMarker(
132 | _options: DefaultEventOptions,
133 | _callback: DidBeginDraggingMarkerCallback
134 | ): Promise {
135 | throw this.unimplemented("Not implemented on web.");
136 | }
137 |
138 | async didDragMarker(
139 | _options: DefaultEventOptions,
140 | _callback: DidDragMarkerCallback
141 | ): Promise {
142 | throw this.unimplemented("Not implemented on web.");
143 | }
144 |
145 | async didEndDraggingMarker(
146 | _options: DefaultEventOptions,
147 | _callback: DidEndDraggingMarkerCallback
148 | ): Promise {
149 | throw this.unimplemented("Not implemented on web.");
150 | }
151 |
152 | async didTapMyLocationButton(
153 | _options: DefaultEventWithPreventDefaultOptions,
154 | _callback: DidTapMyLocationButtonCallback
155 | ): Promise {
156 | throw this.unimplemented("Not implemented on web.");
157 | }
158 |
159 | async didTapMyLocationDot(
160 | _options: DefaultEventWithPreventDefaultOptions,
161 | _callback: DidTapMyLocationDotCallback
162 | ): Promise {
163 | throw this.unimplemented("Not implemented on web.");
164 | }
165 |
166 | async didTapPoi(
167 | _options: DefaultEventOptions,
168 | _callback: DidTapPoiCallback
169 | ): Promise {
170 | throw this.unimplemented("Not implemented on web.");
171 | }
172 |
173 | async didBeginMovingCamera(
174 | _options: DefaultEventOptions,
175 | _callback: DidBeginMovingCameraCallback
176 | ): Promise {
177 | throw this.unimplemented("Not implemented on web.");
178 | }
179 |
180 | async didMoveCamera(
181 | _options: DefaultEventOptions,
182 | _callback: DidMoveCameraCallback
183 | ): Promise {
184 | throw this.unimplemented("Not implemented on web.");
185 | }
186 |
187 | async didEndMovingCamera(
188 | _options: DefaultEventOptions,
189 | _callback: DidEndMovingCameraCallback
190 | ): Promise {
191 | throw this.unimplemented("Not implemented on web.");
192 | }
193 |
194 | async elementFromPointResult(
195 | _options: ElementFromPointResultOptions
196 | ): Promise {
197 | throw this.unimplemented("Not implemented on web.");
198 | }
199 | }
200 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowSyntheticDefaultImports": true,
4 | "declaration": true,
5 | "experimentalDecorators": true,
6 | "lib": [
7 | "dom",
8 | "es2017"
9 | ],
10 | "module": "esnext",
11 | "moduleResolution": "node",
12 | "noImplicitAny": true,
13 | "noUnusedLocals": true,
14 | "noUnusedParameters": true,
15 | "outDir": "dist/esm",
16 | "sourceMap": true,
17 | "target": "es2017"
18 | },
19 | "files": [
20 | "src/index.ts"
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------