@agm/core
Version:
Angular components for Google Maps
1,453 lines (1,434 loc) • 137 kB
JavaScript
import { __awaiter } from 'tslib';
import { Injectable, NgZone, ɵɵdefineInjectable, ɵɵinject, InjectionToken, Optional, Inject, LOCALE_ID, Directive, Input, EventEmitter, Output, Self, Component, ElementRef, PLATFORM_ID, ContentChildren, QueryList, forwardRef, NgModule } from '@angular/core';
import { Observable, BehaviorSubject, from, timer, ReplaySubject, bindCallback, of, throwError, fromEventPattern, merge, Subject } from 'rxjs';
import { flatMap, sample, switchMap, map, shareReplay, multicast, startWith, skip, distinctUntilChanged, takeUntil } from 'rxjs/operators';
import { isPlatformServer } from '@angular/common';
class MapsAPILoader {
}
MapsAPILoader.decorators = [
{ type: Injectable }
];
/**
* Wrapper class that handles the communication with the Google Maps Javascript
* API v3
*/
class GoogleMapsAPIWrapper {
constructor(_loader, _zone) {
this._loader = _loader;
this._zone = _zone;
this._map =
new Promise((resolve) => { this._mapResolver = resolve; });
}
createMap(el, mapOptions) {
return this._zone.runOutsideAngular(() => {
return this._loader.load().then(() => {
const map = new google.maps.Map(el, mapOptions);
this._mapResolver(map);
return;
});
});
}
setMapOptions(options) {
return this._zone.runOutsideAngular(() => {
this._map.then((m) => { m.setOptions(options); });
});
}
/**
* Creates a google map marker with the map context
*/
createMarker(options = {}, addToMap = true) {
return this._zone.runOutsideAngular(() => {
return this._map.then((map) => {
if (addToMap) {
options.map = map;
}
return new google.maps.Marker(options);
});
});
}
createInfoWindow(options) {
return this._zone.runOutsideAngular(() => {
return this._map.then(() => new google.maps.InfoWindow(options));
});
}
/**
* Creates a google.map.Circle for the current map.
*/
createCircle(options) {
return this._zone.runOutsideAngular(() => {
return this._map.then((map) => {
options.map = map;
return new google.maps.Circle(options);
});
});
}
/**
* Creates a google.map.Rectangle for the current map.
*/
createRectangle(options) {
return this._zone.runOutsideAngular(() => {
return this._map.then((map) => {
options.map = map;
return new google.maps.Rectangle(options);
});
});
}
createPolyline(options) {
return this._zone.runOutsideAngular(() => {
return this.getNativeMap().then((map) => {
const line = new google.maps.Polyline(options);
line.setMap(map);
return line;
});
});
}
createPolygon(options) {
return this._zone.runOutsideAngular(() => {
return this.getNativeMap().then((map) => {
const polygon = new google.maps.Polygon(options);
polygon.setMap(map);
return polygon;
});
});
}
/**
* Creates a new google.map.Data layer for the current map
*/
createDataLayer(options) {
return this._zone.runOutsideAngular(() => {
return this._map.then(m => {
const data = new google.maps.Data(options);
data.setMap(m);
return data;
});
});
}
/**
* Creates a TransitLayer instance for a map
* @returns a new transit layer object
*/
createTransitLayer() {
return this._zone.runOutsideAngular(() => {
return this._map.then((map) => {
const newLayer = new google.maps.TransitLayer();
newLayer.setMap(map);
return newLayer;
});
});
}
/**
* Creates a BicyclingLayer instance for a map
* @returns a new bicycling layer object
*/
createBicyclingLayer() {
return this._zone.runOutsideAngular(() => {
return this._map.then((map) => {
const newLayer = new google.maps.BicyclingLayer();
newLayer.setMap(map);
return newLayer;
});
});
}
/**
* Determines if given coordinates are insite a Polygon path.
*/
containsLocation(latLng, polygon) {
return this._map.then(() => google.maps.geometry.poly.containsLocation(latLng, polygon));
}
subscribeToMapEvent(eventName) {
return new Observable((observer) => {
this._map.then(m => m.addListener(eventName, () => this._zone.run(() => observer.next(arguments[0]))));
});
}
clearInstanceListeners() {
return this._zone.runOutsideAngular(() => {
this._map.then((map) => {
google.maps.event.clearInstanceListeners(map);
});
});
}
setCenter(latLng) {
return this._zone.runOutsideAngular(() => {
return this._map.then((map) => map.setCenter(latLng));
});
}
getZoom() {
return this._zone.runOutsideAngular(() => {
return this._map.then((map) => map.getZoom());
});
}
getBounds() {
return this._zone.runOutsideAngular(() => {
return this._map.then((map) => map.getBounds());
});
}
getMapTypeId() {
return this._zone.runOutsideAngular(() => {
return this._map.then((map) => map.getMapTypeId());
});
}
setZoom(zoom) {
return this._zone.runOutsideAngular(() => {
return this._map.then((map) => map.setZoom(zoom));
});
}
getCenter() {
return this._zone.runOutsideAngular(() => {
return this._map.then((map) => map.getCenter());
});
}
panTo(latLng) {
return this._zone.runOutsideAngular(() => {
return this._map.then((map) => map.panTo(latLng));
});
}
panBy(x, y) {
return this._zone.runOutsideAngular(() => {
return this._map.then((map) => map.panBy(x, y));
});
}
fitBounds(latLng, padding) {
return this._zone.runOutsideAngular(() => {
return this._map.then((map) => map.fitBounds(latLng, padding));
});
}
panToBounds(latLng, padding) {
return this._zone.runOutsideAngular(() => {
return this._map.then((map) => map.panToBounds(latLng, padding));
});
}
/**
* Returns the native Google Maps Map instance. Be careful when using this instance directly.
*/
getNativeMap() { return this._map; }
/**
* Triggers the given event name on the map instance.
*/
triggerMapEvent(eventName) {
return this._map.then((m) => google.maps.event.trigger(m, eventName));
}
}
GoogleMapsAPIWrapper.decorators = [
{ type: Injectable }
];
GoogleMapsAPIWrapper.ctorParameters = () => [
{ type: MapsAPILoader },
{ type: NgZone }
];
class CircleManager {
constructor(_apiWrapper, _zone) {
this._apiWrapper = _apiWrapper;
this._zone = _zone;
this._circles = new Map();
}
addCircle(circle) {
this._apiWrapper.getNativeMap().then(() => this._circles.set(circle, this._apiWrapper.createCircle({
center: { lat: circle.latitude, lng: circle.longitude },
clickable: circle.clickable,
draggable: circle.draggable,
editable: circle.editable,
fillColor: circle.fillColor,
fillOpacity: circle.fillOpacity,
radius: circle.radius,
strokeColor: circle.strokeColor,
strokeOpacity: circle.strokeOpacity,
strokePosition: google.maps.StrokePosition[circle.strokePosition],
strokeWeight: circle.strokeWeight,
visible: circle.visible,
zIndex: circle.zIndex,
})));
}
/**
* Removes the given circle from the map.
*/
removeCircle(circle) {
return this._circles.get(circle).then((c) => {
c.setMap(null);
this._circles.delete(circle);
});
}
setOptions(circle, options) {
return __awaiter(this, void 0, void 0, function* () {
return this._circles.get(circle).then((c) => {
const actualParam = options.strokePosition;
options.strokePosition = google.maps.StrokePosition[actualParam];
c.setOptions(options);
});
});
}
getBounds(circle) {
return this._circles.get(circle).then((c) => c.getBounds());
}
getCenter(circle) {
return this._circles.get(circle).then((c) => c.getCenter());
}
getRadius(circle) {
return this._circles.get(circle).then((c) => c.getRadius());
}
setCenter(circle) {
return this._circles.get(circle).then(c => c.setCenter({ lat: circle.latitude, lng: circle.longitude }));
}
setEditable(circle) {
return this._circles.get(circle).then(c => c.setEditable(circle.editable));
}
setDraggable(circle) {
return this._circles.get(circle).then(c => c.setDraggable(circle.draggable));
}
setVisible(circle) {
return this._circles.get(circle).then(c => c.setVisible(circle.visible));
}
setRadius(circle) {
return this._circles.get(circle).then(c => c.setRadius(circle.radius));
}
getNativeCircle(circle) {
return this._circles.get(circle);
}
createEventObservable(eventName, circle) {
return new Observable((observer) => {
let listener = null;
this._circles.get(circle).then((c) => {
listener = c.addListener(eventName, (e) => this._zone.run(() => observer.next(e)));
});
return () => {
if (listener !== null) {
listener.remove();
}
};
});
}
}
CircleManager.decorators = [
{ type: Injectable }
];
CircleManager.ctorParameters = () => [
{ type: GoogleMapsAPIWrapper },
{ type: NgZone }
];
/**
* Manages all Data Layers for a Google Map instance.
*/
class DataLayerManager {
constructor(_wrapper, _zone) {
this._wrapper = _wrapper;
this._zone = _zone;
this._layers = new Map();
}
/**
* Adds a new Data Layer to the map.
*/
addDataLayer(layer) {
const newLayer = this._wrapper.createDataLayer({
style: layer.style,
})
.then(d => {
if (layer.geoJson) {
// NOTE: accessing "features" on google.maps.Data is undocumented
this.getDataFeatures(d, layer.geoJson).then(features => d.features = features);
}
return d;
});
this._layers.set(layer, newLayer);
}
deleteDataLayer(layer) {
this._layers.get(layer).then(l => {
l.setMap(null);
this._layers.delete(layer);
});
}
updateGeoJson(layer, geoJson) {
this._layers.get(layer).then(l => {
l.forEach(feature => {
l.remove(feature);
// NOTE: accessing "features" on google.maps.Data is undocumented
const index = l.features.indexOf(feature, 0);
if (index > -1) {
l.features.splice(index, 1);
}
});
this.getDataFeatures(l, geoJson).then(features => l.features = features);
});
}
setDataOptions(layer, options) {
this._layers.get(layer).then(l => {
l.setControlPosition(options.controlPosition);
l.setControls(options.controls);
l.setDrawingMode(options.drawingMode);
l.setStyle(options.style);
});
}
/**
* Creates a Google Maps event listener for the given DataLayer as an Observable
*/
createEventObservable(eventName, layer) {
return new Observable((observer) => {
this._layers.get(layer).then((d) => {
d.addListener(eventName, (e) => this._zone.run(() => observer.next(e)));
});
});
}
/**
* Extract features from a geoJson using google.maps Data Class
* @param d : google.maps.Data class instance
* @param geoJson : url or geojson object
*/
getDataFeatures(d, geoJson) {
return new Promise((resolve, reject) => {
if (typeof geoJson === 'object') {
try {
const features = d.addGeoJson(geoJson);
resolve(features);
}
catch (e) {
reject(e);
}
}
else if (typeof geoJson === 'string') {
d.loadGeoJson(geoJson, null, resolve);
}
else {
reject(`Impossible to extract features from geoJson: wrong argument type`);
}
});
}
}
DataLayerManager.decorators = [
{ type: Injectable }
];
DataLayerManager.ctorParameters = () => [
{ type: GoogleMapsAPIWrapper },
{ type: NgZone }
];
/**
* Class to implement when you what to be able to make it work with the auto fit bounds feature
* of AGM.
*/
class FitBoundsAccessor {
}
/**
* The FitBoundsService is responsible for computing the bounds of the a single map.
*/
class FitBoundsService {
constructor(loader) {
this._boundsChangeSampleTime$ = new BehaviorSubject(200);
this._includeInBounds$ = new BehaviorSubject(new Map());
this.bounds$ = from(loader.load()).pipe(flatMap(() => this._includeInBounds$), sample(this._boundsChangeSampleTime$.pipe(switchMap(time => timer(0, time)))), map(includeInBounds => this._generateBounds(includeInBounds)), shareReplay(1));
}
_generateBounds(includeInBounds) {
const bounds = new google.maps.LatLngBounds();
includeInBounds.forEach(b => bounds.extend(b));
return bounds;
}
addToBounds(latLng) {
const id = this._createIdentifier(latLng);
if (this._includeInBounds$.value.has(id)) {
return;
}
const boundsMap = this._includeInBounds$.value;
boundsMap.set(id, latLng);
this._includeInBounds$.next(boundsMap);
}
removeFromBounds(latLng) {
const boundsMap = this._includeInBounds$.value;
boundsMap.delete(this._createIdentifier(latLng));
this._includeInBounds$.next(boundsMap);
}
changeFitBoundsChangeSampleTime(timeMs) {
this._boundsChangeSampleTime$.next(timeMs);
}
getBounds$() {
return this.bounds$;
}
_createIdentifier(latLng) {
return `${latLng.lat}+${latLng.lng}`;
}
}
FitBoundsService.decorators = [
{ type: Injectable }
];
FitBoundsService.ctorParameters = () => [
{ type: MapsAPILoader }
];
class AgmGeocoder {
constructor(loader) {
const connectableGeocoder$ = new Observable(subscriber => {
loader.load().then(() => subscriber.next());
})
.pipe(map(() => this._createGeocoder()), multicast(new ReplaySubject(1)));
connectableGeocoder$.connect(); // ignore the subscription
// since we will remain subscribed till application exits
this.geocoder$ = connectableGeocoder$;
}
geocode(request) {
return this.geocoder$.pipe(switchMap((geocoder) => this._getGoogleResults(geocoder, request)));
}
_getGoogleResults(geocoder, request) {
const geocodeObservable = bindCallback(geocoder.geocode);
return geocodeObservable(request).pipe(switchMap(([results, status]) => {
if (status === google.maps.GeocoderStatus.OK) {
return of(results);
}
return throwError(status);
}));
}
_createGeocoder() {
return new google.maps.Geocoder();
}
}
AgmGeocoder.ɵprov = ɵɵdefineInjectable({ factory: function AgmGeocoder_Factory() { return new AgmGeocoder(ɵɵinject(MapsAPILoader)); }, token: AgmGeocoder, providedIn: "root" });
AgmGeocoder.decorators = [
{ type: Injectable, args: [{ providedIn: 'root' },] }
];
AgmGeocoder.ctorParameters = () => [
{ type: MapsAPILoader }
];
class WindowRef {
getNativeWindow() { return window; }
}
class DocumentRef {
getNativeDocument() { return document; }
}
const BROWSER_GLOBALS_PROVIDERS = [WindowRef, DocumentRef];
var GoogleMapsScriptProtocol;
(function (GoogleMapsScriptProtocol) {
GoogleMapsScriptProtocol[GoogleMapsScriptProtocol["HTTP"] = 1] = "HTTP";
GoogleMapsScriptProtocol[GoogleMapsScriptProtocol["HTTPS"] = 2] = "HTTPS";
GoogleMapsScriptProtocol[GoogleMapsScriptProtocol["AUTO"] = 3] = "AUTO";
})(GoogleMapsScriptProtocol || (GoogleMapsScriptProtocol = {}));
/**
* Token for the config of the LazyMapsAPILoader. Please provide an object of type {@link
* LazyMapsAPILoaderConfig}.
*/
const LAZY_MAPS_API_CONFIG = new InjectionToken('angular-google-maps LAZY_MAPS_API_CONFIG');
class LazyMapsAPILoader extends MapsAPILoader {
constructor(config = null, w, d, localeId) {
super();
this.localeId = localeId;
this._SCRIPT_ID = 'agmGoogleMapsApiScript';
this.callbackName = `agmLazyMapsAPILoader`;
this._config = config || {};
this._windowRef = w;
this._documentRef = d;
}
load() {
const window = this._windowRef.getNativeWindow();
if (window.google && window.google.maps) {
// Google maps already loaded on the page.
return Promise.resolve();
}
if (this._scriptLoadingPromise) {
return this._scriptLoadingPromise;
}
// this can happen in HMR situations or Stackblitz.io editors.
const scriptOnPage = this._documentRef.getNativeDocument().getElementById(this._SCRIPT_ID);
if (scriptOnPage) {
this._assignScriptLoadingPromise(scriptOnPage);
return this._scriptLoadingPromise;
}
const script = this._documentRef.getNativeDocument().createElement('script');
script.type = 'text/javascript';
script.async = true;
script.defer = true;
script.id = this._SCRIPT_ID;
script.src = this._getScriptSrc(this.callbackName);
this._assignScriptLoadingPromise(script);
this._documentRef.getNativeDocument().body.appendChild(script);
return this._scriptLoadingPromise;
}
_assignScriptLoadingPromise(scriptElem) {
this._scriptLoadingPromise = new Promise((resolve, reject) => {
this._windowRef.getNativeWindow()[this.callbackName] = () => {
resolve();
};
scriptElem.onerror = (error) => {
reject(error);
};
});
}
_getScriptSrc(callbackName) {
const protocolType = (this._config && this._config.protocol) || GoogleMapsScriptProtocol.HTTPS;
let protocol;
switch (protocolType) {
case GoogleMapsScriptProtocol.AUTO:
protocol = '';
break;
case GoogleMapsScriptProtocol.HTTP:
protocol = 'http:';
break;
case GoogleMapsScriptProtocol.HTTPS:
protocol = 'https:';
break;
}
const hostAndPath = this._config.hostAndPath || 'maps.googleapis.com/maps/api/js';
const queryParams = {
v: this._config.apiVersion || 'quarterly',
callback: callbackName,
key: this._config.apiKey,
client: this._config.clientId,
channel: this._config.channel,
libraries: this._config.libraries,
region: this._config.region,
language: this._config.language || (this.localeId !== 'en-US' ? this.localeId : null),
};
const params = Object.keys(queryParams)
.filter((k) => queryParams[k] != null)
.filter((k) => {
// remove empty arrays
return !Array.isArray(queryParams[k]) ||
(Array.isArray(queryParams[k]) && queryParams[k].length > 0);
})
.map((k) => {
// join arrays as comma seperated strings
const i = queryParams[k];
if (Array.isArray(i)) {
return { key: k, value: i.join(',') };
}
return { key: k, value: queryParams[k] };
})
.map((entry) => {
return `${entry.key}=${entry.value}`;
})
.join('&');
return `${protocol}//${hostAndPath}?${params}`;
}
}
LazyMapsAPILoader.decorators = [
{ type: Injectable }
];
LazyMapsAPILoader.ctorParameters = () => [
{ type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [LAZY_MAPS_API_CONFIG,] }] },
{ type: WindowRef },
{ type: DocumentRef },
{ type: String, decorators: [{ type: Inject, args: [LOCALE_ID,] }] }
];
class MarkerManager {
constructor(_mapsWrapper, _zone) {
this._mapsWrapper = _mapsWrapper;
this._zone = _zone;
this._markers = new Map();
}
convertAnimation(uiAnim) {
return __awaiter(this, void 0, void 0, function* () {
if (uiAnim === null) {
return null;
}
else {
return this._mapsWrapper.getNativeMap().then(() => google.maps.Animation[uiAnim]);
}
});
}
deleteMarker(markerDirective) {
const markerPromise = this._markers.get(markerDirective);
if (markerPromise == null) {
// marker already deleted
return Promise.resolve();
}
return markerPromise.then((marker) => {
return this._zone.run(() => {
marker.setMap(null);
this._markers.delete(markerDirective);
});
});
}
updateMarkerPosition(marker) {
return this._markers.get(marker).then((m) => m.setPosition({ lat: marker.latitude, lng: marker.longitude }));
}
updateTitle(marker) {
return this._markers.get(marker).then((m) => m.setTitle(marker.title));
}
updateLabel(marker) {
return this._markers.get(marker).then((m) => { m.setLabel(marker.label); });
}
updateDraggable(marker) {
return this._markers.get(marker).then((m) => m.setDraggable(marker.draggable));
}
updateIcon(marker) {
return this._markers.get(marker).then((m) => m.setIcon(marker.iconUrl));
}
updateOpacity(marker) {
return this._markers.get(marker).then((m) => m.setOpacity(marker.opacity));
}
updateVisible(marker) {
return this._markers.get(marker).then((m) => m.setVisible(marker.visible));
}
updateZIndex(marker) {
return this._markers.get(marker).then((m) => m.setZIndex(marker.zIndex));
}
updateClickable(marker) {
return this._markers.get(marker).then((m) => m.setClickable(marker.clickable));
}
updateAnimation(marker) {
return __awaiter(this, void 0, void 0, function* () {
const m = yield this._markers.get(marker);
m.setAnimation(yield this.convertAnimation(marker.animation));
});
}
addMarker(marker) {
const markerPromise = new Promise((resolve) => __awaiter(this, void 0, void 0, function* () {
return this._mapsWrapper.createMarker({
position: { lat: marker.latitude, lng: marker.longitude },
label: marker.label,
draggable: marker.draggable,
icon: marker.iconUrl,
opacity: marker.opacity,
visible: marker.visible,
zIndex: marker.zIndex,
title: marker.title,
clickable: marker.clickable,
animation: yield this.convertAnimation(marker.animation),
}).then(resolve);
}));
this._markers.set(marker, markerPromise);
}
getNativeMarker(marker) {
return this._markers.get(marker);
}
createEventObservable(eventName, marker) {
return new Observable(observer => {
this._markers.get(marker).then(m => m.addListener(eventName, e => this._zone.run(() => observer.next(e))));
});
}
}
MarkerManager.decorators = [
{ type: Injectable }
];
MarkerManager.ctorParameters = () => [
{ type: GoogleMapsAPIWrapper },
{ type: NgZone }
];
class InfoWindowManager {
constructor(_mapsWrapper, _zone, _markerManager) {
this._mapsWrapper = _mapsWrapper;
this._zone = _zone;
this._markerManager = _markerManager;
this._infoWindows = new Map();
}
deleteInfoWindow(infoWindow) {
const iWindow = this._infoWindows.get(infoWindow);
if (iWindow == null) {
// info window already deleted
return Promise.resolve();
}
return iWindow.then((i) => {
return this._zone.run(() => {
i.close();
this._infoWindows.delete(infoWindow);
});
});
}
setPosition(infoWindow) {
return this._infoWindows.get(infoWindow).then((i) => i.setPosition({
lat: infoWindow.latitude,
lng: infoWindow.longitude,
}));
}
setZIndex(infoWindow) {
return this._infoWindows.get(infoWindow)
.then((i) => i.setZIndex(infoWindow.zIndex));
}
open(infoWindow) {
return this._infoWindows.get(infoWindow).then((w) => {
if (infoWindow.hostMarker != null) {
return this._markerManager.getNativeMarker(infoWindow.hostMarker).then((marker) => {
return this._mapsWrapper.getNativeMap().then((map) => w.open(map, marker));
});
}
return this._mapsWrapper.getNativeMap().then((map) => w.open(map));
});
}
close(infoWindow) {
return this._infoWindows.get(infoWindow).then((w) => w.close());
}
setOptions(infoWindow, options) {
return this._infoWindows.get(infoWindow).then((i) => i.setOptions(options));
}
addInfoWindow(infoWindow) {
const options = {
content: infoWindow.content,
maxWidth: infoWindow.maxWidth,
zIndex: infoWindow.zIndex,
disableAutoPan: infoWindow.disableAutoPan,
};
if (typeof infoWindow.latitude === 'number' && typeof infoWindow.longitude === 'number') {
options.position = { lat: infoWindow.latitude, lng: infoWindow.longitude };
}
const infoWindowPromise = this._mapsWrapper.createInfoWindow(options);
this._infoWindows.set(infoWindow, infoWindowPromise);
}
/**
* Creates a Google Maps event listener for the given InfoWindow as an Observable
*/
createEventObservable(eventName, infoWindow) {
return new Observable((observer) => {
this._infoWindows.get(infoWindow).then((i) => {
i.addListener(eventName, (e) => this._zone.run(() => observer.next(e)));
});
});
}
}
InfoWindowManager.decorators = [
{ type: Injectable }
];
InfoWindowManager.ctorParameters = () => [
{ type: GoogleMapsAPIWrapper },
{ type: NgZone },
{ type: MarkerManager }
];
/**
* Manages all KML Layers for a Google Map instance.
*/
class KmlLayerManager {
constructor(_wrapper, _zone) {
this._wrapper = _wrapper;
this._zone = _zone;
this._layers = new Map();
}
/**
* Adds a new KML Layer to the map.
*/
addKmlLayer(layer) {
const newLayer = this._wrapper.getNativeMap().then(m => {
return new google.maps.KmlLayer({
clickable: layer.clickable,
map: m,
preserveViewport: layer.preserveViewport,
screenOverlays: layer.screenOverlays,
suppressInfoWindows: layer.suppressInfoWindows,
url: layer.url,
zIndex: layer.zIndex,
});
});
this._layers.set(layer, newLayer);
}
setOptions(layer, options) {
this._layers.get(layer).then(l => l.setOptions(options));
}
deleteKmlLayer(layer) {
this._layers.get(layer).then(l => {
l.setMap(null);
this._layers.delete(layer);
});
}
/**
* Creates a Google Maps event listener for the given KmlLayer as an Observable
*/
createEventObservable(eventName, layer) {
return new Observable((observer) => {
this._layers.get(layer).then((m) => {
m.addListener(eventName, (e) => this._zone.run(() => observer.next(e)));
});
});
}
}
KmlLayerManager.decorators = [
{ type: Injectable }
];
KmlLayerManager.ctorParameters = () => [
{ type: GoogleMapsAPIWrapper },
{ type: NgZone }
];
/**
* This class manages Transit and Bicycling Layers for a Google Map instance.
*/
class LayerManager {
constructor(_wrapper) {
this._wrapper = _wrapper;
this._layers = new Map();
}
/**
* Adds a transit layer to a map instance.
* @param layer - a TransitLayer object
* @param _options - TransitLayerOptions options
* @returns void
*/
addTransitLayer(layer) {
const newLayer = this._wrapper.createTransitLayer();
this._layers.set(layer, newLayer);
}
/**
* Adds a bicycling layer to a map instance.
* @param layer - a bicycling layer object
* @param _options - BicyclingLayer options
* @returns void
*/
addBicyclingLayer(layer) {
const newLayer = this._wrapper.createBicyclingLayer();
this._layers.set(layer, newLayer);
}
/**
* Deletes a map layer
* @param layer - the layer to delete
*/
deleteLayer(layer) {
return this._layers.get(layer).then(currentLayer => {
currentLayer.setMap(null);
this._layers.delete(layer);
});
}
}
LayerManager.decorators = [
{ type: Injectable }
];
LayerManager.ctorParameters = () => [
{ type: GoogleMapsAPIWrapper }
];
/**
* When using the NoOpMapsAPILoader, the Google Maps API must be added to the page via a `<script>`
* Tag.
* It's important that the Google Maps API script gets loaded first on the page.
*/
class NoOpMapsAPILoader {
load() {
if (!window.google || !window.google.maps) {
throw new Error('Google Maps API not loaded on page. Make sure window.google.maps is available!');
}
return Promise.resolve();
}
}
function createMVCEventObservable(array) {
const eventNames = ['insert_at', 'remove_at', 'set_at'];
return fromEventPattern(handler => eventNames.map(eventName => array.addListener(eventName, (index, previous) => handler.apply(array, [{ newArr: array.getArray(), eventName, index, previous }]))), (_handler, evListeners) => evListeners.forEach(evListener => evListener.remove()));
}
class MvcArrayMock {
constructor() {
this.vals = [];
this.listeners = {
remove_at: [],
insert_at: [],
set_at: [],
};
}
clear() {
for (let i = this.vals.length - 1; i >= 0; i--) {
this.removeAt(i);
}
}
getArray() {
return [...this.vals];
}
getAt(i) {
return this.vals[i];
}
getLength() {
return this.vals.length;
}
insertAt(i, elem) {
this.vals.splice(i, 0, elem);
this.listeners.insert_at.forEach(listener => listener(i));
}
pop() {
const deleted = this.vals.pop();
this.listeners.remove_at.forEach(listener => listener(this.vals.length, deleted));
return deleted;
}
push(elem) {
this.vals.push(elem);
this.listeners.insert_at.forEach(listener => listener(this.vals.length - 1));
return this.vals.length;
}
removeAt(i) {
const deleted = this.vals.splice(i, 1)[0];
this.listeners.remove_at.forEach(listener => listener(i, deleted));
return deleted;
}
setAt(i, elem) {
const deleted = this.vals[i];
this.vals[i] = elem;
this.listeners.set_at.forEach(listener => listener(i, deleted));
}
forEach(callback) {
this.vals.forEach(callback);
}
addListener(eventName, handler) {
const listenerArr = this.listeners[eventName];
listenerArr.push(handler);
return {
remove: () => {
listenerArr.splice(listenerArr.indexOf(handler), 1);
},
};
}
bindTo() { throw new Error('Not implemented'); }
changed() { throw new Error('Not implemented'); }
get() { throw new Error('Not implemented'); }
notify() { throw new Error('Not implemented'); }
set() { throw new Error('Not implemented'); }
setValues() { throw new Error('Not implemented'); }
unbind() { throw new Error('Not implemented'); }
unbindAll() { throw new Error('Not implemented'); }
}
class PolygonManager {
constructor(_mapsWrapper, _zone) {
this._mapsWrapper = _mapsWrapper;
this._zone = _zone;
this._polygons = new Map();
}
addPolygon(path) {
const polygonPromise = this._mapsWrapper.createPolygon({
clickable: path.clickable,
draggable: path.draggable,
editable: path.editable,
fillColor: path.fillColor,
fillOpacity: path.fillOpacity,
geodesic: path.geodesic,
paths: path.paths,
strokeColor: path.strokeColor,
strokeOpacity: path.strokeOpacity,
strokeWeight: path.strokeWeight,
visible: path.visible,
zIndex: path.zIndex,
});
this._polygons.set(path, polygonPromise);
}
updatePolygon(polygon) {
const m = this._polygons.get(polygon);
if (m == null) {
return Promise.resolve();
}
return m.then((l) => this._zone.run(() => { l.setPaths(polygon.paths); }));
}
setPolygonOptions(path, options) {
return this._polygons.get(path).then((l) => { l.setOptions(options); });
}
deletePolygon(paths) {
const m = this._polygons.get(paths);
if (m == null) {
return Promise.resolve();
}
return m.then((l) => {
return this._zone.run(() => {
l.setMap(null);
this._polygons.delete(paths);
});
});
}
getPath(polygonDirective) {
return this._polygons.get(polygonDirective)
.then((polygon) => polygon.getPath().getArray());
}
getPaths(polygonDirective) {
return this._polygons.get(polygonDirective)
.then((polygon) => polygon.getPaths().getArray().map((p) => p.getArray()));
}
createEventObservable(eventName, path) {
return new Observable((observer) => {
this._polygons.get(path).then((l) => {
l.addListener(eventName, (e) => this._zone.run(() => observer.next(e)));
});
});
}
createPathEventObservable(agmPolygon) {
return __awaiter(this, void 0, void 0, function* () {
const polygon = yield this._polygons.get(agmPolygon);
const paths = polygon.getPaths();
const pathsChanges$ = createMVCEventObservable(paths);
return pathsChanges$.pipe(startWith({ newArr: paths.getArray() }), // in order to subscribe to them all
switchMap(parentMVEvent => merge(... // rest parameter
parentMVEvent.newArr.map((chMVC, index) => createMVCEventObservable(chMVC)
.pipe(map(chMVCEvent => ({ parentMVEvent, chMVCEvent, pathIndex: index })))))
.pipe(// start the merged ob with an event signinifing change to parent
startWith({ parentMVEvent, chMVCEvent: null, pathIndex: null }))), skip(1), // skip the manually added event
map(({ parentMVEvent, chMVCEvent, pathIndex }) => {
let retVal;
if (!chMVCEvent) {
retVal = {
newArr: parentMVEvent.newArr.map(subArr => subArr.getArray().map(latLng => latLng.toJSON())),
eventName: parentMVEvent.eventName,
index: parentMVEvent.index,
};
if (parentMVEvent.previous) {
retVal.previous = parentMVEvent.previous.getArray();
}
}
else {
retVal = {
newArr: parentMVEvent.newArr.map(subArr => subArr.getArray().map(latLng => latLng.toJSON())),
pathIndex,
eventName: chMVCEvent.eventName,
index: chMVCEvent.index,
};
if (chMVCEvent.previous) {
retVal.previous = chMVCEvent.previous;
}
}
return retVal;
}));
});
}
}
PolygonManager.decorators = [
{ type: Injectable }
];
PolygonManager.ctorParameters = () => [
{ type: GoogleMapsAPIWrapper },
{ type: NgZone }
];
class PolylineManager {
constructor(_mapsWrapper, _zone) {
this._mapsWrapper = _mapsWrapper;
this._zone = _zone;
this._polylines = new Map();
}
static _convertPoints(line) {
const path = line._getPoints().map((point) => {
return { lat: point.latitude, lng: point.longitude };
});
return path;
}
static _convertPath(path) {
const symbolPath = google.maps.SymbolPath[path];
if (typeof symbolPath === 'number') {
return symbolPath;
}
else {
return path;
}
}
static _convertIcons(line) {
const icons = line._getIcons().map(agmIcon => ({
fixedRotation: agmIcon.fixedRotation,
offset: agmIcon.offset,
repeat: agmIcon.repeat,
icon: {
anchor: new google.maps.Point(agmIcon.anchorX, agmIcon.anchorY),
fillColor: agmIcon.fillColor,
fillOpacity: agmIcon.fillOpacity,
path: PolylineManager._convertPath(agmIcon.path),
rotation: agmIcon.rotation,
scale: agmIcon.scale,
strokeColor: agmIcon.strokeColor,
strokeOpacity: agmIcon.strokeOpacity,
strokeWeight: agmIcon.strokeWeight,
},
}));
// prune undefineds;
icons.forEach(icon => {
Object.entries(icon).forEach(([key, val]) => {
if (typeof val === 'undefined') {
delete icon[key];
}
});
if (typeof icon.icon.anchor.x === 'undefined' ||
typeof icon.icon.anchor.y === 'undefined') {
delete icon.icon.anchor;
}
});
return icons;
}
addPolyline(line) {
const polylinePromise = this._mapsWrapper.getNativeMap()
.then(() => [PolylineManager._convertPoints(line),
PolylineManager._convertIcons(line)])
.then(([path, icons]) => this._mapsWrapper.createPolyline({
clickable: line.clickable,
draggable: line.draggable,
editable: line.editable,
geodesic: line.geodesic,
strokeColor: line.strokeColor,
strokeOpacity: line.strokeOpacity,
strokeWeight: line.strokeWeight,
visible: line.visible,
zIndex: line.zIndex,
path,
icons,
}));
this._polylines.set(line, polylinePromise);
}
updatePolylinePoints(line) {
const path = PolylineManager._convertPoints(line);
const m = this._polylines.get(line);
if (m == null) {
return Promise.resolve();
}
return m.then((l) => this._zone.run(() => l.setPath(path)));
}
updateIconSequences(line) {
return __awaiter(this, void 0, void 0, function* () {
yield this._mapsWrapper.getNativeMap();
const icons = PolylineManager._convertIcons(line);
const m = this._polylines.get(line);
if (m == null) {
return;
}
return m.then(l => this._zone.run(() => l.setOptions({ icons })));
});
}
setPolylineOptions(line, options) {
return this._polylines.get(line).then((l) => { l.setOptions(options); });
}
deletePolyline(line) {
const m = this._polylines.get(line);
if (m == null) {
return Promise.resolve();
}
return m.then((l) => {
return this._zone.run(() => {
l.setMap(null);
this._polylines.delete(line);
});
});
}
getMVCPath(agmPolyline) {
return __awaiter(this, void 0, void 0, function* () {
const polyline = yield this._polylines.get(agmPolyline);
return polyline.getPath();
});
}
getPath(agmPolyline) {
return __awaiter(this, void 0, void 0, function* () {
return (yield this.getMVCPath(agmPolyline)).getArray();
});
}
createEventObservable(eventName, line) {
return new Observable((observer) => {
this._polylines.get(line).then((l) => {
l.addListener(eventName, (e) => this._zone.run(() => observer.next(e)));
});
});
}
createPathEventObservable(line) {
return __awaiter(this, void 0, void 0, function* () {
const mvcPath = yield this.getMVCPath(line);
return createMVCEventObservable(mvcPath);
});
}
}
PolylineManager.decorators = [
{ type: Injectable }
];
PolylineManager.ctorParameters = () => [
{ type: GoogleMapsAPIWrapper },
{ type: NgZone }
];
class RectangleManager {
constructor(_apiWrapper, _zone) {
this._apiWrapper = _apiWrapper;
this._zone = _zone;
this._rectangles = new Map();
}
addRectangle(rectangle) {
this._apiWrapper.getNativeMap().then(() => this._rectangles.set(rectangle, this._apiWrapper.createRectangle({
bounds: {
north: rectangle.north,
east: rectangle.east,
south: rectangle.south,
west: rectangle.west,
},
clickable: rectangle.clickable,
draggable: rectangle.draggable,
editable: rectangle.editable,
fillColor: rectangle.fillColor,
fillOpacity: rectangle.fillOpacity,
strokeColor: rectangle.strokeColor,
strokeOpacity: rectangle.strokeOpacity,
strokePosition: google.maps.StrokePosition[rectangle.strokePosition],
strokeWeight: rectangle.strokeWeight,
visible: rectangle.visible,
zIndex: rectangle.zIndex,
})));
}
/**
* Removes the given rectangle from the map.
*/
removeRectangle(rectangle) {
return this._rectangles.get(rectangle).then((r) => {
r.setMap(null);
this._rectangles.delete(rectangle);
});
}
setOptions(rectangle, options) {
return this._rectangles.get(rectangle).then((r) => {
const actualStrokePosition = options.strokePosition;
options.strokePosition = google.maps.StrokePosition[actualStrokePosition];
r.setOptions(options);
});
}
getBounds(rectangle) {
return this._rectangles.get(rectangle).then((r) => r.getBounds());
}
setBounds(rectangle) {
return this._rectangles.get(rectangle).then((r) => {
return r.setBounds({
north: rectangle.north,
east: rectangle.east,
south: rectangle.south,
west: rectangle.west,
});
});
}
setEditable(rectangle) {
return this._rectangles.get(rectangle).then((r) => {
return r.setEditable(rectangle.editable);
});
}
setDraggable(rectangle) {
return this._rectangles.get(rectangle).then((r) => {
return r.setDraggable(rectangle.draggable);
});
}
setVisible(rectangle) {
return this._rectangles.get(rectangle).then((r) => {
return r.setVisible(rectangle.visible);
});
}
createEventObservable(eventName, rectangle) {
return new Observable((subsrciber) => {
let listener = null;
this._rectangles.get(rectangle).then((r) => {
listener = r.addListener(eventName, (e) => this._zone.run(() => subsrciber.next(e)));
});
return () => {
if (listener !== null) {
listener.remove();
}
};
});
}
}
RectangleManager.decorators = [
{ type: Injectable }
];
RectangleManager.ctorParameters = () => [
{ type: GoogleMapsAPIWrapper },
{ type: NgZone }
];
let layerId = 0;
/*
* This directive adds a bicycling layer to a google map instance
* <agm-bicycling-layer [visible]="true|false"> <agm-bicycling-layer>
* */
class AgmBicyclingLayer {
constructor(_manager) {
this._manager = _manager;
this._addedToManager = false;
this._id = (layerId++).toString();
/**
* Hide/show bicycling layer
*/
this.visible = true;
}
ngOnInit() {
if (this._addedToManager) {
return;
}
this._manager.addBicyclingLayer(this);
this._addedToManager = true;
}
/** @internal */
id() { return this._id; }
/** @internal */
toString() { return `AgmBicyclingLayer-${this._id.toString()}`; }
/** @internal */
ngOnDestroy() {
this._manager.deleteLayer(this);
}
}
AgmBicyclingLayer.decorators = [
{ type: Directive, args: [{
selector: 'agm-bicycling-layer',
},] }
];
AgmBicyclingLayer.ctorParameters = () => [
{ type: LayerManager }
];
AgmBicyclingLayer.propDecorators = {
visible: [{ type: Input }]
};
class AgmCircle {
constructor(_manager) {
this._manager = _manager;
/**
* Indicates whether this Circle handles mouse events. Defaults to true.
*/
this.clickable = true;
/**
* If set to true, the user can drag this circle over the map. Defaults to false.
*/
// tslint:disable-next-line:no-input-rename
this.draggable = false;
/**
* If set to true, the user can edit this circle by dragging the control points shown at
* the center and around the circumference of the circle. Defaults to false.
*/
this.editable = false;
/**
* The radius in meters on the Earth's surface.
*/
this.radius = 0;
/**
* The stroke position. Defaults to CENTER.
* This property is not supported on Internet Explorer 8 and earlier.
*/
this.strokePosition = 'CENTER';
/**
* The stroke width in pixels.
*/
this.strokeWeight = 0;
/**
* Whether this circle is visible on the map. Defaults to true.
*/
this.visible = true;
/**
* This event is fired when the circle's center is changed.
*/
this.centerChange = new EventEmitter();
/**
* This event emitter gets emitted when the user clicks on the circle.
*/
this.circleClick = new EventEmitter();
/**
* This event emitter gets emitted when the user clicks on the circle.
*/
this.circleDblClick = new EventEmitter();
/**
* This event is repeatedly fired while the user drags the circle.
*/
// tslint:disable-next-line: no-output-native
this.drag = new EventEmitter();
/**
* This event is fired when the user stops dragging the circle.
*/
this.dragEnd = new EventEmitter();
/**
* This event is fired when the user starts dragging the circle.
*/
this.dragStart = new EventEmitter();
/**
* This event is fired when the DOM mousedown event is fired on the circle.
*/
this.mouseDown = new EventEmitter();
/**
* This event is fired when the DOM mousemove event is fired on the circle.
*/
this.mouseMove = new EventEmitter();
/**
* This event is fired on circle mouseout.
*/
this.mouseOut = new EventEmitter();
/**
* This event is fired on circle mouseover.
*/
this.mouseOver = new EventEmitter();
/**
* This event is fired when the DOM mouseup event is fired on the circle.
*/
this.mouseUp = new EventEmitter();
/**
* This event is fired when the circle's radius is changed.
*/
this.radiusChange = new EventEmitter();
/**
* This event is fired when the circle is right-clicked on.
*/
this.rightClick = new EventEmitter();
this._circleAddedToManager = false;
this._eventSubscriptions = [];
}
/** @internal */
ngOnInit() {
this._manager.addCircle(this);
this._circleAddedToManager = true;
this._registerEventListeners();
}
/** @internal */
ngOnChanges(changes) {
if (!this._circleAddedToManager)