1 of 110

@ionic-native / google-maps

�Cordova GoogleMaps plugin project

��Masashi Katsumata

2 of 110

Ionic framework

3 of 110

@ionic-native/google-maps

  • Embed native Google Maps view in your app�
  • One code for both Android, iOS and Browser (from v4.12.0)
  • Easy to use�
  • Faster than Google Maps Javascript API v3�
  • Free to use. Apache License v2

4 of 110

・UI components

・View management

・TypeScript -> JS

...etc

Your code run on this browser view

Bridge between native system and your code!

5 of 110

@ionic-native/google-maps

Let’s see the demo!

6 of 110

<button ion-button>

(HTML)

Google Map!

(native view)

7 of 110

The Map

is touchable

The side menu is

touchable!!

The Map

is not touchable!

8 of 110

Google Map View is under the browser!!�(and this is not a HTML element)

This is native Google Maps view, which is not

a HTML element.

9 of 110

touch layer�(native)

Is this on map or browser?

Pass

the touch event to the map or browser

10 of 110

Hello world

11 of 110

Create a project

API key

12 of 110

Create a project

API key

13 of 110

Enable billing

API key

You need to register your payment way.��But don’t worry, you probably won’t charge.

Because Google Maps Android API and Google Maps SDK for iOS are free.

(Except geocoding, StreetView)

14 of 110

Are Google Maps APIs free to use?

  • Yes!! �"Google Maps Android API v2" and "Google Maps SDK for iOS" are free, and unlimited usage (at this time)!�
  • But no!!�If you run your app on the browser platform, this plugin uses Google Maps JavaScript API v3. �
  • Bonus: you can get $200 credit / month for other Maps APIs�(Google Directions API, Google Maps JavaScript API v3, and so on)

Thus, this plugin is also free!

15 of 110

Enable Google Maps APIs

API key

16 of 110

Enable Google Maps APIs

API key

This is your API key

Restrict your key when you release your app

17 of 110

Create a project

API key

Create project

$> ionic start myApp sidemenu --type=angular

? Integrate your new app with Cordova to target native iOS and Android? YES

$> npm install @ionic-native/core�

$> npm install @ionic-native/google-maps�

18 of 110

Install cordova-plugin-googlemaps

API key

Create project

$> ionic cordova plugin add cordova-plugin-googlemaps

19 of 110

Edit `config.xml`

<?xml version='1.0' encoding='utf-8'?>

<widget ...>

<preferencename="GOOGLE_MAPS_ANDROID_API_KEY" value="(api key)" />

<preference

name="GOOGLE_MAPS_IOS_API_KEY" value="(api key)" />

</widget>

API key

Create project

20 of 110

src/app/home/home.page.html

<ion-content padding>

<h3>Ionic GoogleMaps Starter</h3>

<div id="map_canvas">

<button ion-button (click)="onButtonClick($event)">Start demo</button>

</div>�

</ion-content>

Add a div (map div)

API key

Create project

HTML & CSS

21 of 110

src/pages/home/home.scss

#map_canvas {

height: 90%;

}

100 x 100 pixels are minimum requirement dimension.

API key

Create project

HTML & CSS

Background styles become be transparent

by the maps plugin mandatory!

You can set the background color through

Environment.setBackgroundColor()

22 of 110

src/app/home/home.page.ts

import { Component, OnInit } from '@angular/core';

import { Platform } from '@ionic/angular';

import { GoogleMaps, GoogleMap } from '@ionic-native/google-maps/ngx';

export class HomePage implements OnInit {

map: GoogleMap;

constructor(private platform:Platform) {}

async ngOnInit() {� // you need to wait `platform.ready()`await this.platform.ready();

await this.loadMap();

}

API key

Create project

HTML & CSS

Coding

Important!!!

23 of 110

If you forget that …

ionViewDidLoad() is executed�(DOM elements are ready)

platform.ready() is executed later�(the native plugins are ready to use)

Then you get this error message���

24 of 110

src/app/home/home.page.ts

loadMap() {� // If you want to run your app� // on browser, insert this line.� Environment.setEnv({� 'API_KEY_FOR_BROWSER_RELEASE': '',� 'API_KEY_FOR_BROWSER_DEBUG':''� });

// Create a map� // after the view is ready� // and the native platform is ready.� this.map = GoogleMaps.create('map_canvas');

}

API key

Create project

HTML & CSS

Coding

25 of 110

Run it!!

$> ionic cordova run android

$> ionic cordova run browser -l

API key

Create project

HTML & CSS

Coding

Run it!

26 of 110

Marker

@ionic-native/google-maps

27 of 110

Add a marker

this.map.addMarker({

title: '@ionic-native/google-maps',

icon: 'blue',

animation: 'DROP',

position: {

lat: 43.0741904,

lng: -89.3809802

}

}).then((marker: Marker) => {

marker.showInfoWindow();

});

28 of 110

Add a marker

from v4.8.2 -

let marker: Marker = this.map.addMarkerSync({

title: '@ionic-native/google-maps',

icon: 'blue',

animation: 'DROP',

position: {

lat: 43.0741904,

lng: -89.3809802

}

});

marker.showInfoWindow();

29 of 110

icon property

this.map.addMarkerSync({

title: '@ionic-native/google-maps',

icon: 'blue',

animation: 'DROP',

position: {

lat: 43.0741904,

lng: -89.3809802

}

});

color name : blue, red, green, yellow ....

(157 color names are defined in this plugin)

rgb(), rgba() , hsl(), hsla(), #RGB, #RGBA :

./assets/icon.png (jpg, gif, and png) :

http(s)://yourserver/icon.png :

cdvfile:// …. /icon.png :

data:image/png;base64,iVBORw0KGgo...CC :

:

30 of 110

icon property

let POINTS: BaseArrayClass<any> = new BaseArrayClass<any>([

{

position: {lat:41.79883, lng:140.75675},

iconData: "./assets/imgs/Number-1-icon.png"

},

{

position: {lat:41.799240000000005, lng:140.75875000000002},

iconData: "http://icons.iconarchive.com/.../24/Number-2-icon.png"

},

{

position: {lat:41.797650000000004, lng:140.75905},

iconData: {

url: "http://icons.iconarchive.com/.../48/Number-3-icon.png",

size: { width: 24, height: 24}

}

},

{

position: {lat:41.79637, lng:140.76018000000002},

title: "4",

iconData: "blue"

},

{

position: {lat:41.79567, lng:140.75845},

title: "5",

iconData: "data:image/png;base64,iVBORw...CC"

}

]);

31 of 110

Marker events

  • MARKER_CLICK�
  • MARKER_DRAG_START
  • MARKER_DRAG
  • MARKER_DRAG_END�
  • INFO_CLICK
  • INFO_LONG_CLICK
  • INFO_OPEN
  • INFO_CLOSE

32 of 110

Add event listener

Listen the event only one time

Listen the event multiple times

marker.addEventListenerOnce(GoogleMapsEvent.MARKER_CLICK).then();

// Alias method

marker.one(GoogleMapsEvent.MARKER_CLICK).then();

marker.addEventListener(GoogleMapsEvent.MARKER_CLICK).subscribe();

// Alias method

marker.on(GoogleMapsEvent.MARKER_CLICK).subscribe();

33 of 110

Remove event listener

Listen the event only one time

// Remove particular event listener

marker.off(GoogleMapsEvent.MARKER_CLICK, this.onMarkerClick);

// Remove all event listeners for the MARKER_CLICK event

marker.off(GoogleMapsEvent.MARKER_CLICK);

// Remove all event listeners of all events

marker.off();

34 of 110

Example

this.map.addMarker({

position: {

lat: 43.0741804,

lng: -89.381

},

title: "A",

disableAutoPan: true

}).then(this.onMarkerAdded);

this.map.addMarker({

position: {

lat: 43.0741804,

lng: -89.382

},

title: "B",

disableAutoPan: true

}).then(this.onMarkerAdded);

onMarkerAdded(marker: Marker) {

marker.one(GoogleMapsEvent.MARKER_CLICK).then(() => {

alert("Marker" + marker.getTitle() + " is clicked");

});

}

35 of 110

Polyline

@ionic-native/google-maps

36 of 110

Polyline

let AIR_PORTS = [� HND_AIR_PORT, HNL_AIR_PORT, SFO_AIR_PORT�];��this.map.addPolyline({� points: AIR_PORTS,� color: '#AA00FF',� width: 10,� geodesic: true,� clickable: true // clickable = false in default�}).then((polyline: Polyline) => {� polyline.on(GoogleMapsEvent.POLYLINE_CLICK).subscribe((params: any) => {� let position: LatLng = <LatLng>params[0];� this.map.addMarker({� position: position,� title: position.toUrlValue(),� disableAutoPan: true� }).then((marker: Marker) => {� marker.showInfoWindow();� });� });�});

Click event with LatLng

(Because this plugin calculates own way)

37 of 110

Polyline

let AIR_PORTS = [� HND_AIR_PORT, HNL_AIR_PORT, SFO_AIR_PORT�];��let polyline: Polyline = this.map.addPolylineSync({� points: AIR_PORTS,� color: '#AA00FF',� width: 10,� geodesic: true,� clickable: true // clickable = false in default�})

polyline.on(GoogleMapsEvent.POLYLINE_CLICK).subscribe((params: any) => {� let position: LatLng = <LatLng>params[0];� let marker: Marker = this.map.addMarkerSync({� position: position,� title: position.toUrlValue(),� disableAutoPan: true� });� marker.showInfoWindow();�});

from v4.8.2 -

38 of 110

Polygon

@ionic-native/google-maps

39 of 110

Polygon

let GORYOKAKU_POINTS: ILatLng[] = [� {lat: 41.79883, lng: 140.75675},� {lat: 41.799240000000005, lng: 140.75875000000002},� {lat: 41.797650000000004, lng: 140.75905},� …� {lat: 41.79909000000001, lng: 140.75465}�];��this.map.addPolygon({� 'points':GORYOKAKU_POINTS,� 'strokeColor' : '#AA00FF', � 'fillColor' : '#00FFAA',� 'strokeWidth': 10�}.then((polygon: Polygon) => {

...

});

Just pass ILatLng[]

40 of 110

Polygon

let GORYOKAKU_POINTS: ILatLng[] = [� {lat: 41.79883, lng: 140.75675},� {lat: 41.799240000000005, lng: 140.75875000000002},� {lat: 41.797650000000004, lng: 140.75905},� …� {lat: 41.79909000000001, lng: 140.75465}�];�let polygon: Polygon = this.map.addPolygonSync({� 'points':GORYOKAKU_POINTS,� 'strokeColor' : '#AA00FF', � 'fillColor' : '#00FFAA',� 'strokeWidth': 10�});

from v4.8.2 -

41 of 110

Circle

@ionic-native/google-maps

42 of 110

Circle

let center: ILatLng = {"lat": 32, "lng": -97};�let radius = 300; // radius (meter)��this.map.addCircle({� 'center': center,� 'radius': radius,� 'strokeColor' : '#AA00FF',� 'strokeWidth': 5,� 'fillColor' : '#00880055'�}).then((circle: Circle) => {

marker.on('position_changed').subscribe((params: any) => {� let newValue: ILatLng = <ILatLng>params[1];

let newRadius: number = Spherical.computeDistanceBetween(� center, newValue);

circle.setRadius(newRadius);� });�});

43 of 110

Circle

let center: ILatLng = {"lat": 32, "lng": -97};�let radius = 300; // radius (meter)��let circle: Circle = this.map.addCircleSync({� 'center': center,� 'radius': radius,� 'strokeColor' : '#AA00FF',� 'strokeWidth': 5,� 'fillColor' : '#00880055'�});

marker.on('position_changed').subscribe((params: any) => {� let newValue: ILatLng = <ILatLng>params[1];

let newRadius: number = Spherical.computeDistanceBetween(� center, newValue);

circle.setRadius(newRadius);�});�

from v4.8.2 -

44 of 110

GroundOverlay

@ionic-native/google-maps

45 of 110

GroundOverlay

this.map.one(GoogleMapsEvent.MAP_READY).then(() => {� return this.map.addGroundOverlay({� 'url': 'assets/imgs/newark_nj_1922.jpg',� 'bounds': bounds,� 'opacity': 0.5,� 'clickable': true // default = false� });�}).then((groundOverlay: GroundOverlay) => {�� // Catch the GROUND_OVERLAY_CLICK event� groundOverlay.on(GoogleMapsEvent.GROUND_OVERLAY_CLICK).subscribe(() => {� groundOverlay.setImage('assets/imgs/newark_nj_1922_2.jpg');� });��});

46 of 110

GroundOverlay

let groundOverlay: GroundOverlay = this.map.addGroundOverlaySync({� 'url': 'assets/imgs/newark_nj_1922.jpg',� 'bounds': bounds,� 'opacity': 0.5,� 'clickable': true // default = false� });�});��// Catch the GROUND_OVERLAY_CLICK event�groundOverlay.on(GoogleMapsEvent.GROUND_OVERLAY_CLICK).subscribe(() => {� groundOverlay.setImage('assets/imgs/newark_nj_1922_2.jpg');�});��

from v4.8.2 -

47 of 110

TileOverlay

@ionic-native/google-maps

48 of 110

TileOverlay

this.map.addTileOverlay({� getTile: (x: number, y: number, zoom: number) => {� return "http://tile.stamen.com/watercolor/" +� zoom + "/" + x + "/" + y + ".jpg";� },�� // draw the debug information on tiles� debug: false,�� opacity: 1.0�});

You can generate various URL

49 of 110

TileOverlay

let tileOverlay: TileOverlay = this.map.addTileOverlaySync({� getTile: (x: number, y: number, zoom: number) => {� return "http://tile.stamen.com/watercolor/" +� zoom + "/" + x + "/" + y + ".jpg";� },�� // draw the debug information on tiles� debug: false,�� opacity: 1.0�});

from v4.8.2 -

50 of 110

HtmlInfoWindow

@ionic-native/google-maps

51 of 110

HtmlInfoWindow(1)

let htmlInfoWindow = new HtmlInfoWindow();�let frame: HTMLElement = document.createElement('div');�frame.innerHTML = [� '<h3>Hearst Castle</h3>',� '<img src="assets/imgs/hearst_castle.jpg">'�].join("");�frame.getElementsByTagName("img")[0].addEventListener("click", () => {� htmlInfoWindow.setBackgroundColor('red');�});�htmlInfoWindow.setContent(frame, {width: "280px", height: "330px"});��this.map.addMarker({� position: {lat: 35.685208, lng: -121.168225},� draggable: true,� disableAutoPan: true�}).then((marker: Marker) => {� marker.on(GoogleMapsEvent.MARKER_CLICK).subscribe(() => {� htmlInfoWindow.open(marker);� });�});

52 of 110

HtmlInfoWindow(2)

onMarkerClick(params: any[]) {� // Get a marker instance from the passed parameters� let marker: Marker = params.pop();�� // Create a component� const compFactory = this.resolver.resolveComponentFactory(CustomTag);� let compRef: ComponentRef<CustomTag> = compFactory.create(this.injector);� compRef.instance.myTitle = marker.get('myTitle');� this.appRef.attachView(compRef.hostView);�� let div = document.createElement('div');� div.appendChild(compRef.location.nativeElement);�� // Dynamic rendering� this._ngZone.run(() => {� this.htmInfoWindow.setContent(div);� this.htmInfoWindow.open(marker);� });�}

53 of 110

Marker cluster

@ionic-native/google-maps

54 of 110

MarkerCluster

this.map.addMarkerCluster({

markers: data,

icons: [

{

min: 3, max: 9,

url: "./assets/markercluster/small.png",

label: { color: "white" }

},

{

min: 10,

url: "./assets/markercluster/large.png",

label: { color: "white" }

}

]

}).then((markerCluster: MarkerCluster) => {

markerCluster.on(GoogleMapsEvent.MARKER_CLICK).subscribe((params) => {

let marker: Marker = params[1];

marker.setTitle(marker.get("name"));

marker.setSnippet(marker.get("address"));

marker.showInfoWindow();

});

});

55 of 110

MarkerCluster

from v4.8.2 -

let markerCluster: MarkerCluster = this.map.addMarkerClusterSync({

markers: data,

icons: [

{

min: 3, max: 9,

url: "./assets/markercluster/small.png",

label: { color: "white" }

},

{

min: 10,

url: "./assets/markercluster/large.png",

label: { color: "white" }

}

]

});

markerCluster.on(GoogleMapsEvent.MARKER_CLICK).subscribe((params) => {

let marker: Marker = params[1];

marker.setTitle(marker.get("name"));

marker.setSnippet(marker.get("address"));

marker.showInfoWindow();

});

56 of 110

Geocoding

@ionic-native/google-maps

57 of 110

Geocoding

Geocoding for one address

// Address -> latitude,longitude

Geocoder.geocode({

"address": this.search_address

})

.then((results: GeocoderResult[]) => {

console.log(results);

return this.map1.addMarker({

'position': results[0].position,

'title': JSON.stringify(results[0].position)

})

})

.then(...)

58 of 110

Batch geocoding

Pass locations as array

Get a mvc array first,

then `finish` event is notified.

Just 1.9 sec!

Geocoder.geocode({

// US Capital cities

"address": [

"Montgomery, AL, USA", "Juneau, AK, USA", ...� "Madison, WI, USA", "Cheyenne, Wyoming, USA"

]

})

.then((mvcArray: BaseArrayClass<GeocoderResult[]>) => {

});

59 of 110

BaseClass

@ionic-native/google-maps

60 of 110

All classes extend Base class

BaseClass

BaseArrayClass

Marker

Circle

Polyline

Polygon

GroundOverlay

TileOverlay

MarkerCluster

Map

61 of 110

BaseClass

  • set()�
  • get()�
  • bindTo()�
  • trigger()�
  • on() / addEventListener()�
  • one() / addEventListenerOnce()�
  • off()�
  • empty()�
  • destroy()

62 of 110

set() and get()

"Hello_changed" event occurs

obj.set(key, value, noNotify?)

obj.get(key)

let myObj: BaseClass = new BaseClass();

myObj.set("hello", "world");

console.log(myObj.get("hello"));

63 of 110

(key)_changed event

let myObj: BaseClass = new BaseClass();�

myObj.on("hello_changed").subscribe((params) => {

console.log(params);

});�

myObj.set("hello", "world");

myObj.set("hello", "world2");

hello_changed

hello_changed

64 of 110

Status change event

marker.setPosition(...);

marker.setTitle("....");

position_changed

title_changed

65 of 110

Own property

this.map.addMarker({

position: { lat: 43.0741704, lng: -89.3809802},

count: 0

})

.then((marker: Marker) => {

marker.on(GoogleMapsEvent.MARKER_CLICK).subscribe(() => {

marker.set("count", marker.get("count") + 1);

});�

marker.on("count_changed").subscribe((params: []) => {

let oldValue = params[0];

let newValue = params[1];

let key = params[2];

marker.setTitle("'" + key + "' is changed from '" +� oldValue + "' to '" + newValue + "'");

});�

});

66 of 110

Own property

from v4.8.2 -

let marker: Marker = this.map.addMarkerSync({

position: { lat: 43.0741704, lng: -89.3809802},

count: 0

});

marker.on(GoogleMapsEvent.MARKER_CLICK).subscribe(() => {

marker.set("count", marker.get("count") + 1);

});�

marker.on("count_changed").subscribe((params: []) => {

let oldValue = params[0];

let newValue = params[1];

let key = params[2];

marker.setTitle("'" + key + "' is changed from '" +� oldValue + "' to '" + newValue + "'");

});

67 of 110

noNotify option

let myObj: BaseClass = new BaseClass();

�myObj.on("hello_changed").subscribe((params) => {

console.log(params);

});�

myObj.set("hello", "world", true);

hello_changed

DO NOT

OCCUR

68 of 110

bindTo()

let objA: BaseClass = new BaseClass();

let objB: BaseClass = new BaseClass();�

objA.bindTo("hello", objB, "world");�

objA.set("hello", "こんにちは");

objB.get("world");

objA

objB

hello

world

"こんにちは"

"こんにちは"

sourceObj.bindTo(targetKey, dest, destKey?, noNotify?)

69 of 110

bindTo()

this.map.addMarker({

position: {lat: 43.0741704, lng: -89.3809802},

draggable: true

})

.then((marker: Marker) => {

this.map.addCircle({

center: marker.getPosition(),

radius: 10,

fillColor: "rgba(0, 0, 255, 0.5)",

strokeColor: "rgba(0, 0, 255, 0.75)",

strokeWidth: 1

}).then((circle: Circle) => {

marker.bindTo("position", circle, "center");

});

});

70 of 110

bindTo()

from v4.8.2 -

let marker: Marker = this.map.addMarkerSync({

position: {lat: 43.0741704, lng: -89.3809802},

draggable: true

});

let circle: Circle = this.map.addCircleSync({

center: marker.getPosition(),

radius: 10,

fillColor: "rgba(0, 0, 255, 0.5)",

strokeColor: "rgba(0, 0, 255, 0.75)",

strokeWidth: 1

});

marker.bindTo("position", circle, "center");

71 of 110

trigger()

obj.trigger(eventName, args?...)

createMarkers() {

let bounds = this.map.getVisibleRegion();

let sw = bounds.southwest, ne = bounds.northeast;

let diffY = (ne.lat - sw.lat), diffX = (ne.lng - sw.lng);

for (let i = 0; i < 10; i++) {

this.map.addMarker({

'position': {

'lat': sw.lat + diffY * Math.random(),

'lng': sw.lng + diffX * Math.random()

}

}).then((marker:Marker) => {

this.map.on('animate').subscribe((params: []) => {

let animation: string = params[0];

marker.setAnimation(animation);

});

});

}

}

onButtonClick() {

let btnTxt: string = event.srcElement.innerText;

this.map.trigger("animate", btnTxt);

}

72 of 110

Deep understanding

Internal command queue

73 of 110

Cordova executes all methods in asynchronously!

this.map.addMarker({

})

exec("Map", "loadPlugin", "Marker");

map.addMarker()

(native code)

.then((marker:Marker) => {

});

74 of 110

What would be occurred like this code?

this.map.clear();

for (let i = 0; i < positions.length; i++) {

this.map.addMarker({

position: positions[i]

}).then((marker: Marker) => {

this.markers.push(marker);

});

}

75 of 110

map.clear() is slow...

this.map.clear();

for (let i = 0; i < positions.length; i++) {

this.map.addMarker({

position: positions[i]

}).then((marker: Marker) => {

this.markers.push(marker);

});

}

Correct way:

this.map.clear().then(() => {

….

});

76 of 110

Don’t worry, this plugin handles correctly!

this.map.clear();

for (let i = 0; i < positions.length; i++) {

this.map.addMarker({

position: positions[i]

}).then((marker: Marker) => {

this.markers.push(marker);

});

}

77 of 110

Because this plugin has own command queue!

map.clear()

addMarker()

addMarker()

addMarker()

addMarker()

addMarker()

addMarker()

addMarker()

addMarker()

addMarker()

addMarker()

Execute method in synchronous

(Stop all other methods)

addMarker()

addMarker()

addMarker()

addMarker()

addMarker()

addMarker()

addMarker()

addMarker()

addMarker()

addMarker()

Execute 10 methods in parallel at a once

78 of 110

Limit 10 is problem sometimes...

Sorry, there is no solution currently.

79 of 110

synchronous methods

  • googleMaps.create()
  • map.setCameraZoom()
  • map.panBy()
  • map.clear()
  • map.setCameraTilt()
  • map.setCameraBearing()
  • map.moveCameraZoomIn()
  • map.moveCameraZoomOut()
  • map.animateCameraZoomIn()
  • map.animateCameraZoomOut()
  • map.animateCamera()
  • map.moveCamera()
  • map.setMyLocationEnabled()
  • map.getMyLocation()
  • map.remove()
  • map.setDiv()

80 of 110

@ionic-native/google-maps� version 4.8.2

Breaking changes are introduced

81 of 110

Add new methods:

  • map.addMarkerSync()
  • map.addCircleSync()
  • map.addPolylineSync()
  • map.addPolygonSync()
  • map.addTileOverlaySync()
  • map.addGroundOverlaySync()
  • map.addMarkerClusterSync()

this.map.addMarker({

position: {lat: …, lng: …},� title: "Hello world"

}).then((marker:Marker) => {

marker.showInfoWindow();

});

from v4.8.2 -

Used be ...

let marker:Marker = this.map.addMarkerSync({

position: {lat: …, lng: …},� title: "Hello world"

});�

marker.showInfoWindow();

You can still use this syntax code

82 of 110

MAP_READY event

this.map = GoogleMaps.create('map_canvas');�this.map.one(GoogleMapsEvent.MAP_READY).then(() => { this.map.addMarker({

position: {lat: …, lng: …},� title: "Hello world"

});�});

from v4.8.2 -

Used be ...

this.map = GoogleMaps.create('map_canvas');��let marker:Marker = this.map.addMarkerSync({

position: {lat: …, lng: …},� title: "Hello world"

});�

83 of 110

However, if you use map.getVisibleRegion()...

this.map = GoogleMaps.create('map_canvas');�

let visibleRegion: VisibleRegion = this.map.getVisibleRegion();

console.log(visibleRegion); // == null

You still have to need to wait MAP_READY event for this case!

84 of 110

StreetView panorama

class StreetViewPage {� let panorama: StreetViewPanorama;

ionViewDidLoad() {

this.loadPanorama();

}�� loadPanorama() {

// Create a map after the view is loaded.

// (platform is already ready in app.component.ts)

this.panorama = GoogleMaps.createPanorama('pano_canvas', {

camera: {

target: {lat: 42.345573, lng: -71.098326}

}

});

}

}

85 of 110

BaseArrayClass

@ionic-native/google-maps

86 of 110

BaseArrayClass

  • insertAt()�
  • getArray()�
  • getAt()�
  • setAt()�
  • removeAt()�
  • getLength()�
  • reverse()�
  • sort()
  • indexOf()�
  • empty()�
  • push()�
  • pop()
  • map()�
  • mapAsync()�
  • forEach()

  • forEachAsync()�
  • filter()�
  • filterAsync()�

87 of 110

forEach()

baseArray.forEach(fn)

0

1

2

3

4

88 of 110

forEach()

let baseArray: BaseArrayClass<ILatLng> = new BaseArrayClass(positions);��baseArray.forEach((position: ILatLng, idx: number) => {�

this.map.addMarker({

position: position

}).then((marker: Marker) => {

this.markers.push(marker);

});�

});

baseArray.forEach(fn)

89 of 110

forEachAsync()

0

1

2

3

4

baseArray.forEachAsync(fn).then()

Async task

(i.e. setTimeout())

90 of 110

forEachAsync()

let baseArray: BaseArrayClass<ILatLng> = new BaseArrayClass(positions);��baseArray.forEachAsync((position: ILatLng, next:() => void) => {�

this.map.addMarker({

position: position

}).then((marker: Marker) => {

this.markers.push(marker);

next();

});�

}.then(() => {

console.log('finish!');

});

baseArray.forEachAsync(fn).then()

91 of 110

map()

0

1

2

3

4

a

b

c

d

e

a

b

c

d

e

baseArray.map(fn)

92 of 110

map()

let baseArray: BaseArrayClass<Marker> = new BaseArrayClass(this.markers);��let titles: [] = baseArray.map((marker: Marker, idx: number) => {�

return marker.getTitle();�

});

baseArray.map(fn)

93 of 110

mapAsync()

0

1

2

3

4

a

b

c

d

e

a

b

c

d

e

Async task

(i.e. setTimeout())

baseArray.mapAsync(fn).then()

94 of 110

mapAsync()

let baseArray: BaseArrayClass<ILatLng> = new BaseArrayClass(positions);��baseArray.mapAsync((position: ILatLng, next: (result: any) => void) => {�

this.map.addMarker({

position: position

}).then(next);�

}).then(markers: Marker[]) => {

console.log('finish!');

});

baseArray.mapAsync(fn).then()

95 of 110

filter()

0

1

2

3

4

true

false

true

false

true

a

c

e

baseArray.filter(fn)

96 of 110

filter()

let baseArray: BaseArrayClass<Marker> = new BaseArrayClass(this.markers);��let matchedMarkers: [] = baseArray.filter((marker: Marker, idx: number) => {�

return marker.get('category') === 'restaurant';

});

baseArray.filter(fn)

97 of 110

filterAsync()

0

1

2

3

4

true

false

true

false

true

a

c

e

Async task

(i.e. setTimeout())

baseArray.filterAsync(fn).then()

98 of 110

filterAsync()

let baseArray: BaseArrayClass<Marker> = new BaseArrayClass(this.markers);��baseArray.filter((marker: Marker, next: (result: boolean) => void) => {�

// i.e. Get data from remote database

http_request� .get('https://yourserver/getData/', {id: marker.get('dbId')})� .then((locationData: object) => {

// detect later

next(location.category === 'restaurant');� });�

}).then(matchedMarkers: Marker[]) => {

console.log('finish');

});

baseArray.filterAsync(fn).then()

Asynchronous process

99 of 110

push(), pop()

let baseArray: BaseArrayClass = new BaseArrayClass();����baseArray.push('a');��baseArray.push('b');��baseArray.push('c');

�baseArray.pop();

baseArray.pop();�

baseArray.pop();

insert_at event

remove_at event

100 of 110

BaseArrayClass example

let points: ILatLng[] = [

{lat: 33.91636924837674, lng: -118.39605331420898},

{lat: 33.90205144970967, lng: -118.39639663696288},

{lat: 33.90190897196702, lng: -118.37905883789062},

{lat: 33.89471353635718, lng: -118.3787155151367}

];

this.map = this.googleMaps.create('map_canvas', {

camera: {

target: points

}

});

this.map.one(GoogleMapsEvent.MAP_READY).then(() => {

return this.map.addPolyline({

points: points

});

})

//continue to next page...

101 of 110

BaseArrayClass example

.then((polyline: Polyline) => {

let baseArray: BaseArrayClass<ILatLng> = polyline.getPoints();

baseArray.mapAsync((point: ILatLng, next: Function) => {

this.map.addMarker({

position: point,

draggable: true

}).then(next);

}, (markers: Marker[]) => {

markers.forEach((marker: Marker, idx: number) => {

marker.on('position_changed').subscribe((params: []) => {

baseArray.setAt(idx, params[1]);

});

});

// trigger the position_changed event for the first calculation.

markers[0].trigger('position_changed', null, markers[0].getPosition());

});

baseArray.on('set_at', () => {

this._ngZone.run(() => {

let distanceMeter: number = Spherical.computeLength(baseArray);

this.distance = (distanceMeter * 0.000621371192).toFixed(2) + " miles";

});

});

});

polyline.getPoints() returns BaseArrayClass

Calculate the distance

when any markers are dragged

102 of 110

BaseArrayClass example

103 of 110

KML Overlay

@ionic-native/google-maps

104 of 110

KML overlay

this.loading = this.loadingCtrl.create({

content: 'Please wait...'

});

this.loading.present();

this.map.addKmlOverlay({

url: "assets/kmloverlay/polygon.kml"

}).then((kmlOverlay: KmlOverlay) => {

this.loading.dismiss();

console.log(kmlOverlay);

this.map.moveCamera(kmlOverlay.getDefaultViewport());

// You can get additional information

kmlOverlay.on(GoogleMapsEvent.KML_CLICK).subscribe((params: any) => {

let overlay: Polygon = params[0]; // depends on overlay

let latLng: ILatLng = params[1];

console.log(overlay, latLng);

});

});

Fits camera

to view kml overall

105 of 110

LocationService

@ionic-native/google-maps

106 of 110

LocationService

let option: MyLocationOptions = {

// true: use GPS as much as possible (use battery usage alot)

//

// false: use network location or lastKnownLocation

// (save battery usage)

enableHighAccuracy: true

};

LocationService.getMyLocation(option).then((location: MyLocation) => {

this.map = GoogleMaps.create({

'camera': location.latLng,

'zoom': 16

});

...

}).catch((error: any) => {

// Can not get location, permission refused, and so on...

console.log(error);

});

107 of 110

Good example projects

How to use this plugin in real project

108 of 110

Main page + detail page

Special thanks for battika

109 of 110

Use two maps in two pages

There is a map and “Open Second Page” button in home.html.

If you tap on the button, open a detail page using

this.navCtrl.push(SecondPage,{},{animate:false});

In this case, ionic keeps the first page but css visibility = hidden.

The maps plugin can detect this particular case, and detach the main page map automatically.

Then create another map for detail page.

After closing the second page, there is no more div element for the second map. So the maps plugin destroys the second map automatically.

Then the maps plugin attaches the map view to browser automatically, when the main page becomes visibility = visible.

110 of 110

Plugin repository�https://github.com/mapsplugin/cordova-plugin-googlemaps

Official documentshttps://github.com/ionic-team/ionic-native-google-maps/blob/master/documents/README.md

Code repository�https://github.com/mapsplugin/ionic-googlemaps-quickdemo

Google Developers Expert of

Google Maps API��Masashi Katsumata

Cordova GoogleMaps plugin author

&