@ionic-native / google-maps
Cordova GoogleMaps plugin project
Masashi Katsumata
Ionic framework
@ionic-native/google-maps
● Embed native Google Maps view in your app
● One code for both Android and iOS
● Easy to use
● Faster than Google Maps Javascript API v3
● Free to use. Apache License v2
・UI components
・View management
・TypeScript -> JS
...etc
Your code run on
this browser view
Bridge between
native system
and your code!
@ionic-native/google-maps
Let’s see the
demo!
<button ion-button>(HTML)
Google Map!
(native view)
The Map
is
touchable
The side menu
is
touchable!!
The Map
is not
touchable!
Google Map is under the browser!!
touch layer
(native)
Is this on map
or browser?
Pass
the touch event to
the map or browser
Hello world
Google Maps API key
https://console.developers.google.com/projectselector/apis/library
API key
Enable Google Maps APIs
Enable Both APIs
API key
Create credential
Start from here!
API key
Generate an API key
This is the API key
Restrict your key
when you
release your app
API key
Create a project & install plugins
$> ionic start myApp sidemenu
$> npm install @ionic-native/google-maps @ionic-native/core
$> ionic cordova plugin add cordova-plugin-googlemaps
--variable API_KEY_FOR_ANDROID=”.....”
--variable API_KEY_FOR_IOS=”.....”
Please use the
latest version
anytime!
API key Create project
app/src/app.module.ts
Add this line
Add this line
API key Create project
import { BrowserModule } from '@angular/platform-browser';
...
import { GoogleMaps } from "@ionic-native/google-maps";
@NgModule({
....
providers: [
StatusBar,
SplashScreen,
GoogleMaps,
{provide: ErrorHandler, useClass: IonicErrorHandler}
]
})
export class AppModule {}
src/pages/home/home.html
Add a div (map div)
API key Create project HTML & CSS
<ion-content padding>
<h3>Ionic GoogleMaps Starter</h3>
<div id="map_canvas">
<button ion-button (click)="onButtonClick($event)">Start demo</button>
</div>
</ion-content>
src/pages/home/home.scss
page-home {
#map_canvas {
height: 90%;
}
}
Background styles become be transparent
by the maps plugin mandatory!
You can set the background color through
Environment.setBackgroundColor()
API key Create project HTML & CSS
src/app/app.components.ts
API key Create project HTML & CSS Coding
export class MyApp {
@ViewChild(Nav) nav: Nav;
rootPage: any;
constructor(public platform: Platform, public statusBar: StatusBar, public splashScreen: SplashScreen) {
this.initializeApp();
}
initializeApp() {
this.platform.ready().then(() => {
this.rootPage = HomePage;
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
this.statusBar.styleDefault();
this.splashScreen.hide();
});
}
Set the first page
after the platform.read() is called
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
src/pages/home/home.ts
API key Create project HTML & CSS Coding
import { GoogleMaps, GoogleMap, GoogleMapsEvent } from '@ionic-native/google-maps';
export class HomePage {
map: GoogleMap;
constructor( public navCtrl: NavController, private googleMaps: GoogleMaps ) { }
ionViewDidLoad() {
this.loadMap();
}
src/pages/home/home.ts
loadMap() {
// Create a map after the view is ready and the native platform is ready.
this.map = this.googleMaps.create('map_canvas');
// Wait the maps plugin is ready until the MAP_READY event
this.map.one(GoogleMapsEvent.MAP_READY).then(() => {
console.log('map is ready to use.');
});
}
API key Create project HTML & CSS Coding
Run it!!
$> ionic cordova run android
API key Create project HTML & CSS Coding Run it!
Marker
@ionic-native/google-maps
Add a marker
this.map.addMarker({
title: 'Ionic',
icon: 'blue',
animation: 'DROP',
position: {
lat: 43.0741904,
lng: -89.3809802
}
}).then((marker: Marker) => {
marker.showInfoWindow();
});
icon property
this.map.addMarker({
title: 'Ionic',
icon: 'blue',
animation: 'DROP',
position: {
lat: 43.0741904,
lng: -89.3809802
}
}).then((marker: Marker) => {
marker.showInfoWindow();
});
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 :
:
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"
}
]);
Marker events
● MARKER_CLICK
● MARKER_DRAG_START
● MARKER_DRAG
● MARKER_DRAG_END
● INFO_CLICK
● INFO_LONG_CLICK
● INFO_OPEN
● INFO_CLOSE
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();
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();
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");
});
}
Polyline
@ionic-native/google-maps
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)
Polygon
@ionic-native/google-maps
Polygon
Just pass ILatLng[]
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) => {
...
});
Circle
@ionic-native/google-maps
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 =
this.spherical.computeDistanceBetween(center, newValue);
circle.setRadius(newRadius);
});
});
GroundOverlay
@ionic-native/google-maps
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');
});
});
TileOverlay
@ionic-native/google-maps
TileOverlay
You can generate various URL
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
});
HtmlInfoWindow
@ionic-native/google-maps
HtmlInfoWindow
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);
});
});
Marker cluster
@ionic-native/google-maps
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();
});
});
Geocoding
@ionic-native/google-maps
Geocoding
Geocoding for one address
// Address -> latitude,longitude
this.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(...)
Batch geocoding
Pass locations as array
Get a mvc array first,
then `finish` event is
notified.
Just 1.9 sec!
this.geocoder.geocode({
// US Capital cities
"address": [
"Montgomery, AL, USA", "Juneau, AK, USA", ...
"Madison, WI, USA", "Cheyenne, Wyoming, USA"
]
})
.then((mvcArray: BaseArrayClass<GeocoderResult[]>) => {
});
BaseClass
@ionic-native/google-maps
All classes extend Base class
BaseClass
BaseArrayClass Marker
Circle Polyline Polygon
GroundOverlay TileOverlay MarkerCluster
Map
BaseClass
● set()
● get()
● bindTo()
● trigger()
● on() / addEventListener()
● one() / addEventListenerOnce()
● off()
● empty()
● destroy()
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"));
(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
Status change event
marker.setPosition(...);
marker.setTitle("....");
position_changed
title_changed
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 + "'");
});
});
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
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?)
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");
});
});
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);
}
Deep understanding
Internal command queue
All methods are executed in asynchronously!
this.map.addMarker({
…
})
exec("Map", "loadPlugin",
"Marker");
map.addMarker()
(native code)
.then((marker:Marker) =>
{
});
adding Marker after map.clear()
this.map.clear();
for (let i = 0; i < positions.length; i++) {
this.map.addMarker({
position: positions[i]
}).then((marker: Marker) => {
this.markers.push(marker);
});
}
map.clear() is slow...
Correct way:
this.map.clear().then(() => {
….
});
this.map.clear();
for (let i = 0; i < positions.length; i++) {
this.map.addMarker({
position: positions[i]
}).then((marker: Marker) => {
this.markers.push(marker);
});
}
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
Limit 10 is problem sometimes...
Sorry, there is no solution currently.
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()
BaseArrayClass
@ionic-native/google-maps
BaseArrayClass
● insertAt()
● getArray()
● getAt()
● setAt()
● removeAt()
● getLength()
● reverse()
● sort()
● indexOf()
● empty()
● push()
● pop()
● map()
● mapAsync()
● forEach()
● forEachAsync()
● filter()
● filterAsync()
forEach() baseArray.forEach(fn)
0 1 2 3 4
forEach() baseArray.forEach(fn)
let baseArray: BaseArrayClass<ILatLng> = new BaseArrayClass(positions);
baseArray.forEach((position: ILatLng, idx: number) => {
this.map.addMarker({
position: positions[i]
}).then((marker: Marker) => {
this.markers.push(marker);
});
});
forEachAsync()
0 1 2 3 4
baseArray.forEachAsync(fn).then()
Async task
(i.e. setTimeout())
forEachAsync() baseArray.forEachAsync(fn).then()
let baseArray: BaseArrayClass<ILatLng> = new BaseArrayClass(positions);
baseArray.forEachAsync((position: ILatLng, next:() => void) => {
this.map.addMarker({
position: positions[i]
}).then((marker: Marker) => {
this.markers.push(marker);
next();
});
}.then(() => {
console.log('finish!');
});
0 1 2 3 4
a b c d e
a b c d e
map() baseArray.map(fn)
let baseArray: BaseArrayClass<Marker> = new BaseArrayClass(this.markers);
let titles: [] = baseArray.map((position: ILatLng, idx: number) => {
return this.markers[idx].getTitle();
});
map() baseArray.map(fn)
0 1 2 3 4
a b c d e
a b c d e
Async task
(i.e. setTimeout())
mapAsync() baseArray.mapAsync(fn).then()
let baseArray: BaseArrayClass<ILatLng> = new BaseArrayClass(positions);
baseArray.mapAsync((position: ILatLng, next: (result: any) => void) => {
this.map.addMarker({
position: positions[i]
}).then(next);
}).then(markers: Marker[]) => {
console.log('finish!');
});
mapAsync() baseArray.mapAsync(fn).then()
0 1 2 3 4
true false true false true
a c e
filter() baseArray.filter(fn)
filter() baseArray.filter(fn)
let baseArray: BaseArrayClass<Marker> = new BaseArrayClass(this.markers);
let matchedMarkers: [] = baseArray.filter((marker: Marker, idx: number) => {
return marker.get('category') === 'restaurant';
});
0 1 2 3 4
true false true false true
a c e
Async task
(i.e. setTimeout())
filterAsync() baseArray.filterAsync(fn).then()
filterAsync() baseArray.filterAsync(fn).then()
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');
});
Asynchronous
process
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
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...
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 = this.spherical.computeLength(baseArray);
this.distance = (distanceMeter * 0.000621371192).toFixed(2) + " miles";
});
});
});
polyline.getPoints() returns
BaseArrayClass
Calculate the distance
when any markers are dragged
BaseArrayClass example
Plugin repository
https://github.com/mapsplugin/
cordova-plugin-googlemaps
Official documents
https://github.com/mapsplugin/cord
ova-plugin-googlemaps-doc
Code repository
https://github.com/mapsplugin/ionic-
googlemaps-quickdemo
Google Developers Expert of
Google Maps API
Masashi Katsumata
Cordova GoogleMaps plugin
author
&

@Ionic native/google-maps

  • 1.
    @ionic-native / google-maps CordovaGoogleMaps plugin project Masashi Katsumata
  • 2.
  • 3.
    @ionic-native/google-maps ● Embed nativeGoogle Maps view in your app ● One code for both Android and iOS ● Easy to use ● Faster than Google Maps Javascript API v3 ● Free to use. Apache License v2
  • 4.
    ・UI components ・View management ・TypeScript-> JS ...etc Your code run on this browser view Bridge between native system and your code!
  • 5.
  • 6.
  • 7.
    The Map is touchable The sidemenu is touchable!! The Map is not touchable!
  • 8.
    Google Map isunder the browser!!
  • 9.
    touch layer (native) Is thison map or browser? Pass the touch event to the map or browser
  • 10.
  • 11.
    Google Maps APIkey https://console.developers.google.com/projectselector/apis/library API key
  • 12.
    Enable Google MapsAPIs Enable Both APIs API key
  • 13.
  • 14.
    Generate an APIkey This is the API key Restrict your key when you release your app API key
  • 15.
    Create a project& install plugins $> ionic start myApp sidemenu $> npm install @ionic-native/google-maps @ionic-native/core $> ionic cordova plugin add cordova-plugin-googlemaps --variable API_KEY_FOR_ANDROID=”.....” --variable API_KEY_FOR_IOS=”.....” Please use the latest version anytime! API key Create project
  • 16.
    app/src/app.module.ts Add this line Addthis line API key Create project import { BrowserModule } from '@angular/platform-browser'; ... import { GoogleMaps } from "@ionic-native/google-maps"; @NgModule({ .... providers: [ StatusBar, SplashScreen, GoogleMaps, {provide: ErrorHandler, useClass: IonicErrorHandler} ] }) export class AppModule {}
  • 17.
    src/pages/home/home.html Add a div(map div) API key Create project HTML & CSS <ion-content padding> <h3>Ionic GoogleMaps Starter</h3> <div id="map_canvas"> <button ion-button (click)="onButtonClick($event)">Start demo</button> </div> </ion-content>
  • 18.
    src/pages/home/home.scss page-home { #map_canvas { height:90%; } } Background styles become be transparent by the maps plugin mandatory! You can set the background color through Environment.setBackgroundColor() API key Create project HTML & CSS
  • 19.
    src/app/app.components.ts API key Createproject HTML & CSS Coding export class MyApp { @ViewChild(Nav) nav: Nav; rootPage: any; constructor(public platform: Platform, public statusBar: StatusBar, public splashScreen: SplashScreen) { this.initializeApp(); } initializeApp() { this.platform.ready().then(() => { this.rootPage = HomePage; // Okay, so the platform is ready and our plugins are available. // Here you can do any higher level native things you might need. this.statusBar.styleDefault(); this.splashScreen.hide(); }); } Set the first page after the platform.read() is called
  • 20.
    If you forgetthat … 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
  • 21.
    src/pages/home/home.ts API key Createproject HTML & CSS Coding import { GoogleMaps, GoogleMap, GoogleMapsEvent } from '@ionic-native/google-maps'; export class HomePage { map: GoogleMap; constructor( public navCtrl: NavController, private googleMaps: GoogleMaps ) { } ionViewDidLoad() { this.loadMap(); }
  • 22.
    src/pages/home/home.ts loadMap() { // Createa map after the view is ready and the native platform is ready. this.map = this.googleMaps.create('map_canvas'); // Wait the maps plugin is ready until the MAP_READY event this.map.one(GoogleMapsEvent.MAP_READY).then(() => { console.log('map is ready to use.'); }); } API key Create project HTML & CSS Coding
  • 23.
    Run it!! $> ioniccordova run android API key Create project HTML & CSS Coding Run it!
  • 24.
  • 25.
    Add a marker this.map.addMarker({ title:'Ionic', icon: 'blue', animation: 'DROP', position: { lat: 43.0741904, lng: -89.3809802 } }).then((marker: Marker) => { marker.showInfoWindow(); });
  • 26.
    icon property this.map.addMarker({ title: 'Ionic', icon:'blue', animation: 'DROP', position: { lat: 43.0741904, lng: -89.3809802 } }).then((marker: Marker) => { marker.showInfoWindow(); }); 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 : :
  • 27.
    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" } ]);
  • 28.
    Marker events ● MARKER_CLICK ●MARKER_DRAG_START ● MARKER_DRAG ● MARKER_DRAG_END ● INFO_CLICK ● INFO_LONG_CLICK ● INFO_OPEN ● INFO_CLOSE
  • 29.
    Add event listener Listenthe 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();
  • 30.
    Remove event listener Listenthe 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();
  • 31.
    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"); }); }
  • 32.
  • 33.
    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)
  • 34.
  • 35.
    Polygon Just pass ILatLng[] letGORYOKAKU_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) => { ... });
  • 36.
  • 37.
    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 = this.spherical.computeDistanceBetween(center, newValue); circle.setRadius(newRadius); }); });
  • 38.
  • 39.
    GroundOverlay this.map.one(GoogleMapsEvent.MAP_READY).then(() => { returnthis.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'); }); });
  • 40.
  • 41.
    TileOverlay You can generatevarious URL 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 });
  • 42.
  • 43.
    HtmlInfoWindow 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); }); });
  • 44.
  • 45.
    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(); }); });
  • 46.
  • 47.
    Geocoding Geocoding for oneaddress // Address -> latitude,longitude this.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(...)
  • 48.
    Batch geocoding Pass locationsas array Get a mvc array first, then `finish` event is notified. Just 1.9 sec! this.geocoder.geocode({ // US Capital cities "address": [ "Montgomery, AL, USA", "Juneau, AK, USA", ... "Madison, WI, USA", "Cheyenne, Wyoming, USA" ] }) .then((mvcArray: BaseArrayClass<GeocoderResult[]>) => { });
  • 49.
  • 50.
    All classes extendBase class BaseClass BaseArrayClass Marker Circle Polyline Polygon GroundOverlay TileOverlay MarkerCluster Map
  • 51.
    BaseClass ● set() ● get() ●bindTo() ● trigger() ● on() / addEventListener() ● one() / addEventListenerOnce() ● off() ● empty() ● destroy()
  • 52.
    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"));
  • 53.
    (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
  • 54.
  • 55.
    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 + "'"); }); });
  • 56.
    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
  • 57.
    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?)
  • 58.
    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"); }); });
  • 59.
    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); }
  • 60.
  • 61.
    All methods areexecuted in asynchronously! this.map.addMarker({ … }) exec("Map", "loadPlugin", "Marker"); map.addMarker() (native code) .then((marker:Marker) => { });
  • 62.
    adding Marker aftermap.clear() this.map.clear(); for (let i = 0; i < positions.length; i++) { this.map.addMarker({ position: positions[i] }).then((marker: Marker) => { this.markers.push(marker); }); }
  • 63.
    map.clear() is slow... Correctway: this.map.clear().then(() => { …. }); this.map.clear(); for (let i = 0; i < positions.length; i++) { this.map.addMarker({ position: positions[i] }).then((marker: Marker) => { this.markers.push(marker); }); }
  • 64.
    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
  • 65.
    Limit 10 isproblem sometimes... Sorry, there is no solution currently.
  • 66.
    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()
  • 67.
  • 68.
    BaseArrayClass ● insertAt() ● getArray() ●getAt() ● setAt() ● removeAt() ● getLength() ● reverse() ● sort() ● indexOf() ● empty() ● push() ● pop() ● map() ● mapAsync() ● forEach() ● forEachAsync() ● filter() ● filterAsync()
  • 69.
  • 70.
    forEach() baseArray.forEach(fn) let baseArray:BaseArrayClass<ILatLng> = new BaseArrayClass(positions); baseArray.forEach((position: ILatLng, idx: number) => { this.map.addMarker({ position: positions[i] }).then((marker: Marker) => { this.markers.push(marker); }); });
  • 71.
    forEachAsync() 0 1 23 4 baseArray.forEachAsync(fn).then() Async task (i.e. setTimeout())
  • 72.
    forEachAsync() baseArray.forEachAsync(fn).then() let baseArray:BaseArrayClass<ILatLng> = new BaseArrayClass(positions); baseArray.forEachAsync((position: ILatLng, next:() => void) => { this.map.addMarker({ position: positions[i] }).then((marker: Marker) => { this.markers.push(marker); next(); }); }.then(() => { console.log('finish!'); });
  • 73.
    0 1 23 4 a b c d e a b c d e map() baseArray.map(fn)
  • 74.
    let baseArray: BaseArrayClass<Marker>= new BaseArrayClass(this.markers); let titles: [] = baseArray.map((position: ILatLng, idx: number) => { return this.markers[idx].getTitle(); }); map() baseArray.map(fn)
  • 75.
    0 1 23 4 a b c d e a b c d e Async task (i.e. setTimeout()) mapAsync() baseArray.mapAsync(fn).then()
  • 76.
    let baseArray: BaseArrayClass<ILatLng>= new BaseArrayClass(positions); baseArray.mapAsync((position: ILatLng, next: (result: any) => void) => { this.map.addMarker({ position: positions[i] }).then(next); }).then(markers: Marker[]) => { console.log('finish!'); }); mapAsync() baseArray.mapAsync(fn).then()
  • 77.
    0 1 23 4 true false true false true a c e filter() baseArray.filter(fn)
  • 78.
    filter() baseArray.filter(fn) let baseArray:BaseArrayClass<Marker> = new BaseArrayClass(this.markers); let matchedMarkers: [] = baseArray.filter((marker: Marker, idx: number) => { return marker.get('category') === 'restaurant'; });
  • 79.
    0 1 23 4 true false true false true a c e Async task (i.e. setTimeout()) filterAsync() baseArray.filterAsync(fn).then()
  • 80.
    filterAsync() baseArray.filterAsync(fn).then() 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'); }); Asynchronous process
  • 81.
    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
  • 82.
    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...
  • 83.
    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 = this.spherical.computeLength(baseArray); this.distance = (distanceMeter * 0.000621371192).toFixed(2) + " miles"; }); }); }); polyline.getPoints() returns BaseArrayClass Calculate the distance when any markers are dragged
  • 84.
  • 85.
    Plugin repository https://github.com/mapsplugin/ cordova-plugin-googlemaps Official documents https://github.com/mapsplugin/cord ova-plugin-googlemaps-doc Coderepository https://github.com/mapsplugin/ionic- googlemaps-quickdemo Google Developers Expert of Google Maps API Masashi Katsumata Cordova GoogleMaps plugin author &