@angular/google-maps
Version:
Angular Google Maps
1,273 lines (1,265 loc) • 165 kB
JavaScript
import * as i0 from '@angular/core';
import { inject, ElementRef, NgZone, EventEmitter, PLATFORM_ID, Component, ChangeDetectionStrategy, ViewEncapsulation, Input, Output, Directive, InjectionToken, ContentChildren, NgModule, Injectable } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
import { BehaviorSubject, Observable, Subject, combineLatest, Subscription } from 'rxjs';
import { switchMap, take, map, takeUntil } from 'rxjs/operators';
/** Manages event on a Google Maps object, ensuring that events are added only when necessary. */
class MapEventManager {
_ngZone;
/** Pending listeners that were added before the target was set. */
_pending = [];
_listeners = [];
_targetStream = new BehaviorSubject(undefined);
/** Clears all currently-registered event listeners. */
_clearListeners() {
for (const listener of this._listeners) {
listener.remove();
}
this._listeners = [];
}
constructor(_ngZone) {
this._ngZone = _ngZone;
}
/** Gets an observable that adds an event listener to the map when a consumer subscribes to it. */
getLazyEmitter(name) {
return this._targetStream.pipe(switchMap(target => {
const observable = new Observable(observer => {
// If the target hasn't been initialized yet, cache the observer so it can be added later.
if (!target) {
this._pending.push({ observable, observer });
return undefined;
}
const listener = target.addListener(name, (event) => {
this._ngZone.run(() => observer.next(event));
});
// If there's an error when initializing the Maps API (e.g. a wrong API key), it will
// return a dummy object that returns `undefined` from `addListener` (see #26514).
if (!listener) {
observer.complete();
return undefined;
}
this._listeners.push(listener);
return () => listener.remove();
});
return observable;
}));
}
/** Sets the current target that the manager should bind events to. */
setTarget(target) {
const currentTarget = this._targetStream.value;
if (target === currentTarget) {
return;
}
// Clear the listeners from the pre-existing target.
if (currentTarget) {
this._clearListeners();
this._pending = [];
}
this._targetStream.next(target);
// Add the listeners that were bound before the map was initialized.
this._pending.forEach(subscriber => subscriber.observable.subscribe(subscriber.observer));
this._pending = [];
}
/** Destroys the manager and clears the event listeners. */
destroy() {
this._clearListeners();
this._pending = [];
this._targetStream.complete();
}
}
// Workaround for: https://github.com/bazelbuild/rules_nodejs/issues/1265
/** default options set to the Googleplex */
const DEFAULT_OPTIONS = {
center: { lat: 37.421995, lng: -122.084092 },
zoom: 17,
// Note: the type conversion here isn't necessary for our CI, but it resolves a g3 failure.
mapTypeId: 'roadmap',
};
/** Arbitrary default height for the map element */
const DEFAULT_HEIGHT = '500px';
/** Arbitrary default width for the map element */
const DEFAULT_WIDTH = '500px';
/**
* Angular component that renders a Google Map via the Google Maps JavaScript
* API.
* @see https://developers.google.com/maps/documentation/javascript/reference/
*/
class GoogleMap {
_elementRef = inject(ElementRef);
_ngZone = inject(NgZone);
_eventManager = new MapEventManager(inject(NgZone));
_mapEl;
_existingAuthFailureCallback;
/**
* The underlying google.maps.Map object
*
* See developers.google.com/maps/documentation/javascript/reference/map#Map
*/
googleMap;
/** Whether we're currently rendering inside a browser. */
_isBrowser;
/** Height of the map. Set this to `null` if you'd like to control the height through CSS. */
height = DEFAULT_HEIGHT;
/** Width of the map. Set this to `null` if you'd like to control the width through CSS. */
width = DEFAULT_WIDTH;
/**
* The Map ID of the map. This parameter cannot be set or changed after a map is instantiated.
* See: https://developers.google.com/maps/documentation/javascript/reference/map#MapOptions.mapId
*/
mapId;
/**
* Type of map that should be rendered. E.g. hybrid map, terrain map etc.
* See: https://developers.google.com/maps/documentation/javascript/reference/map#MapTypeId
*/
mapTypeId;
set center(center) {
this._center = center;
}
_center;
set zoom(zoom) {
this._zoom = zoom;
}
_zoom;
set options(options) {
this._options = options || DEFAULT_OPTIONS;
}
_options = DEFAULT_OPTIONS;
/** Event emitted when the map is initialized. */
mapInitialized = new EventEmitter();
/**
* See
* https://developers.google.com/maps/documentation/javascript/events#auth-errors
*/
authFailure = new EventEmitter();
/**
* See
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.bounds_changed
*/
boundsChanged = this._eventManager.getLazyEmitter('bounds_changed');
/**
* See
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.center_changed
*/
centerChanged = this._eventManager.getLazyEmitter('center_changed');
/**
* See
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.click
*/
mapClick = this._eventManager.getLazyEmitter('click');
/**
* See
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.dblclick
*/
mapDblclick = this._eventManager.getLazyEmitter('dblclick');
/**
* See
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.drag
*/
mapDrag = this._eventManager.getLazyEmitter('drag');
/**
* See
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.dragend
*/
mapDragend = this._eventManager.getLazyEmitter('dragend');
/**
* See
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.dragstart
*/
mapDragstart = this._eventManager.getLazyEmitter('dragstart');
/**
* See
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.heading_changed
*/
headingChanged = this._eventManager.getLazyEmitter('heading_changed');
/**
* See
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.idle
*/
idle = this._eventManager.getLazyEmitter('idle');
/**
* See
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.maptypeid_changed
*/
maptypeidChanged = this._eventManager.getLazyEmitter('maptypeid_changed');
/**
* See
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.mousemove
*/
mapMousemove = this._eventManager.getLazyEmitter('mousemove');
/**
* See
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.mouseout
*/
mapMouseout = this._eventManager.getLazyEmitter('mouseout');
/**
* See
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.mouseover
*/
mapMouseover = this._eventManager.getLazyEmitter('mouseover');
/**
* See
* developers.google.com/maps/documentation/javascript/reference/map#Map.projection_changed
*/
projectionChanged = this._eventManager.getLazyEmitter('projection_changed');
/**
* See
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.rightclick
*/
mapRightclick = this._eventManager.getLazyEmitter('rightclick');
/**
* See
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.tilesloaded
*/
tilesloaded = this._eventManager.getLazyEmitter('tilesloaded');
/**
* See
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.tilt_changed
*/
tiltChanged = this._eventManager.getLazyEmitter('tilt_changed');
/**
* See
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.zoom_changed
*/
zoomChanged = this._eventManager.getLazyEmitter('zoom_changed');
constructor() {
const platformId = inject(PLATFORM_ID);
this._isBrowser = isPlatformBrowser(platformId);
if (this._isBrowser) {
const googleMapsWindow = window;
if (!googleMapsWindow.google && (typeof ngDevMode === 'undefined' || ngDevMode)) {
throw Error('Namespace google not found, cannot construct embedded google ' +
'map. Please install the Google Maps JavaScript API: ' +
'https://developers.google.com/maps/documentation/javascript/' +
'tutorial#Loading_the_Maps_API');
}
this._existingAuthFailureCallback = googleMapsWindow.gm_authFailure;
googleMapsWindow.gm_authFailure = () => {
if (this._existingAuthFailureCallback) {
this._existingAuthFailureCallback();
}
this.authFailure.emit();
};
}
}
ngOnChanges(changes) {
if (changes['height'] || changes['width']) {
this._setSize();
}
const googleMap = this.googleMap;
if (googleMap) {
if (changes['options']) {
googleMap.setOptions(this._combineOptions());
}
if (changes['center'] && this._center) {
googleMap.setCenter(this._center);
}
// Note that the zoom can be zero.
if (changes['zoom'] && this._zoom != null) {
googleMap.setZoom(this._zoom);
}
if (changes['mapTypeId'] && this.mapTypeId) {
googleMap.setMapTypeId(this.mapTypeId);
}
}
}
ngOnInit() {
// It should be a noop during server-side rendering.
if (this._isBrowser) {
this._mapEl = this._elementRef.nativeElement.querySelector('.map-container');
this._setSize();
// Create the object outside the zone so its events don't trigger change detection.
// We'll bring it back in inside the `MapEventManager` only for the events that the
// user has subscribed to.
if (google.maps.Map) {
this._initialize(google.maps.Map);
}
else {
this._ngZone.runOutsideAngular(() => {
google.maps
.importLibrary('maps')
.then(lib => this._initialize(lib.Map));
});
}
}
}
_initialize(mapConstructor) {
this._ngZone.runOutsideAngular(() => {
this.googleMap = new mapConstructor(this._mapEl, this._combineOptions());
this._eventManager.setTarget(this.googleMap);
this.mapInitialized.emit(this.googleMap);
});
}
ngOnDestroy() {
this.mapInitialized.complete();
this._eventManager.destroy();
if (this._isBrowser) {
const googleMapsWindow = window;
googleMapsWindow.gm_authFailure = this._existingAuthFailureCallback;
}
}
/**
* See
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.fitBounds
*/
fitBounds(bounds, padding) {
this._assertInitialized();
this.googleMap.fitBounds(bounds, padding);
}
/**
* See
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.panBy
*/
panBy(x, y) {
this._assertInitialized();
this.googleMap.panBy(x, y);
}
/**
* See
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.panTo
*/
panTo(latLng) {
this._assertInitialized();
this.googleMap.panTo(latLng);
}
/**
* See
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.panToBounds
*/
panToBounds(latLngBounds, padding) {
this._assertInitialized();
this.googleMap.panToBounds(latLngBounds, padding);
}
/**
* See
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.getBounds
*/
getBounds() {
this._assertInitialized();
return this.googleMap.getBounds() || null;
}
/**
* See
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.getCenter
*/
getCenter() {
this._assertInitialized();
return this.googleMap.getCenter();
}
/**
* See
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.getClickableIcons
*/
getClickableIcons() {
this._assertInitialized();
return this.googleMap.getClickableIcons();
}
/**
* See
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.getHeading
*/
getHeading() {
this._assertInitialized();
return this.googleMap.getHeading();
}
/**
* See
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.getMapTypeId
*/
getMapTypeId() {
this._assertInitialized();
return this.googleMap.getMapTypeId();
}
/**
* See
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.getProjection
*/
getProjection() {
this._assertInitialized();
return this.googleMap.getProjection() || null;
}
/**
* See
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.getStreetView
*/
getStreetView() {
this._assertInitialized();
return this.googleMap.getStreetView();
}
/**
* See
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.getTilt
*/
getTilt() {
this._assertInitialized();
return this.googleMap.getTilt();
}
/**
* See
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.getZoom
*/
getZoom() {
this._assertInitialized();
return this.googleMap.getZoom();
}
/**
* See
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.controls
*/
get controls() {
this._assertInitialized();
return this.googleMap.controls;
}
/**
* See
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.data
*/
get data() {
this._assertInitialized();
return this.googleMap.data;
}
/**
* See
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.mapTypes
*/
get mapTypes() {
this._assertInitialized();
return this.googleMap.mapTypes;
}
/**
* See
* https://developers.google.com/maps/documentation/javascript/reference/map#Map.overlayMapTypes
*/
get overlayMapTypes() {
this._assertInitialized();
return this.googleMap.overlayMapTypes;
}
/** Returns a promise that resolves when the map has been initialized. */
_resolveMap() {
return this.googleMap
? Promise.resolve(this.googleMap)
: this.mapInitialized.pipe(take(1)).toPromise();
}
_setSize() {
if (this._mapEl) {
const styles = this._mapEl.style;
styles.height =
this.height === null ? '' : coerceCssPixelValue(this.height) || DEFAULT_HEIGHT;
styles.width = this.width === null ? '' : coerceCssPixelValue(this.width) || DEFAULT_WIDTH;
}
}
/** Combines the center and zoom and the other map options into a single object */
_combineOptions() {
const options = this._options || {};
return {
...options,
// It's important that we set **some** kind of `center` and `zoom`, otherwise
// Google Maps will render a blank rectangle which looks broken.
center: this._center || options.center || DEFAULT_OPTIONS.center,
zoom: this._zoom ?? options.zoom ?? DEFAULT_OPTIONS.zoom,
// Passing in an undefined `mapTypeId` seems to break tile loading
// so make sure that we have some kind of default (see #22082).
mapTypeId: this.mapTypeId || options.mapTypeId || DEFAULT_OPTIONS.mapTypeId,
mapId: this.mapId || options.mapId,
};
}
/** Asserts that the map has been initialized. */
_assertInitialized() {
if (!this.googleMap && (typeof ngDevMode === 'undefined' || ngDevMode)) {
throw Error('Cannot access Google Map information before the API has been initialized. ' +
'Please wait for the API to load before trying to interact with it.');
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: GoogleMap, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.0", type: GoogleMap, isStandalone: true, selector: "google-map", inputs: { height: "height", width: "width", mapId: "mapId", mapTypeId: "mapTypeId", center: "center", zoom: "zoom", options: "options" }, outputs: { mapInitialized: "mapInitialized", authFailure: "authFailure", boundsChanged: "boundsChanged", centerChanged: "centerChanged", mapClick: "mapClick", mapDblclick: "mapDblclick", mapDrag: "mapDrag", mapDragend: "mapDragend", mapDragstart: "mapDragstart", headingChanged: "headingChanged", idle: "idle", maptypeidChanged: "maptypeidChanged", mapMousemove: "mapMousemove", mapMouseout: "mapMouseout", mapMouseover: "mapMouseover", projectionChanged: "projectionChanged", mapRightclick: "mapRightclick", tilesloaded: "tilesloaded", tiltChanged: "tiltChanged", zoomChanged: "zoomChanged" }, exportAs: ["googleMap"], usesOnChanges: true, ngImport: i0, template: '<div class="map-container"></div><ng-content />', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: GoogleMap, decorators: [{
type: Component,
args: [{
selector: 'google-map',
exportAs: 'googleMap',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<div class="map-container"></div><ng-content />',
encapsulation: ViewEncapsulation.None,
}]
}], ctorParameters: () => [], propDecorators: { height: [{
type: Input
}], width: [{
type: Input
}], mapId: [{
type: Input
}], mapTypeId: [{
type: Input
}], center: [{
type: Input
}], zoom: [{
type: Input
}], options: [{
type: Input
}], mapInitialized: [{
type: Output
}], authFailure: [{
type: Output
}], boundsChanged: [{
type: Output
}], centerChanged: [{
type: Output
}], mapClick: [{
type: Output
}], mapDblclick: [{
type: Output
}], mapDrag: [{
type: Output
}], mapDragend: [{
type: Output
}], mapDragstart: [{
type: Output
}], headingChanged: [{
type: Output
}], idle: [{
type: Output
}], maptypeidChanged: [{
type: Output
}], mapMousemove: [{
type: Output
}], mapMouseout: [{
type: Output
}], mapMouseover: [{
type: Output
}], projectionChanged: [{
type: Output
}], mapRightclick: [{
type: Output
}], tilesloaded: [{
type: Output
}], tiltChanged: [{
type: Output
}], zoomChanged: [{
type: Output
}] } });
const cssUnitsPattern = /([A-Za-z%]+)$/;
/** Coerces a value to a CSS pixel value. */
function coerceCssPixelValue(value) {
if (value == null) {
return '';
}
return cssUnitsPattern.test(value) ? value : `${value}px`;
}
// Workaround for: https://github.com/bazelbuild/rules_nodejs/issues/1265
class MapBaseLayer {
_map = inject(GoogleMap);
_ngZone = inject(NgZone);
constructor() { }
ngOnInit() {
if (this._map._isBrowser) {
this._ngZone.runOutsideAngular(() => {
this._initializeObject();
});
this._assertInitialized();
this._setMap();
}
}
ngOnDestroy() {
this._unsetMap();
}
_assertInitialized() {
if (!this._map.googleMap) {
throw Error('Cannot access Google Map information before the API has been initialized. ' +
'Please wait for the API to load before trying to interact with it.');
}
}
_initializeObject() { }
_setMap() { }
_unsetMap() { }
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: MapBaseLayer, deps: [], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.0", type: MapBaseLayer, isStandalone: true, selector: "map-base-layer", exportAs: ["mapBaseLayer"], ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: MapBaseLayer, decorators: [{
type: Directive,
args: [{
selector: 'map-base-layer',
exportAs: 'mapBaseLayer',
}]
}], ctorParameters: () => [] });
// Workaround for: https://github.com/bazelbuild/rules_nodejs/issues/1265
/**
* Angular component that renders a Google Maps Bicycling Layer via the Google Maps JavaScript API.
*
* See developers.google.com/maps/documentation/javascript/reference/map#BicyclingLayer
*/
class MapBicyclingLayer {
_map = inject(GoogleMap);
_zone = inject(NgZone);
/**
* The underlying google.maps.BicyclingLayer object.
*
* See developers.google.com/maps/documentation/javascript/reference/map#BicyclingLayer
*/
bicyclingLayer;
/** Event emitted when the bicycling layer is initialized. */
bicyclingLayerInitialized = new EventEmitter();
ngOnInit() {
if (this._map._isBrowser) {
if (google.maps.BicyclingLayer && this._map.googleMap) {
this._initialize(this._map.googleMap, google.maps.BicyclingLayer);
}
else {
this._zone.runOutsideAngular(() => {
Promise.all([this._map._resolveMap(), google.maps.importLibrary('maps')]).then(([map, lib]) => {
this._initialize(map, lib.BicyclingLayer);
});
});
}
}
}
_initialize(map, layerConstructor) {
this._zone.runOutsideAngular(() => {
this.bicyclingLayer = new layerConstructor();
this.bicyclingLayerInitialized.emit(this.bicyclingLayer);
this._assertLayerInitialized();
this.bicyclingLayer.setMap(map);
});
}
ngOnDestroy() {
this.bicyclingLayer?.setMap(null);
}
_assertLayerInitialized() {
if (!this.bicyclingLayer) {
throw Error('Cannot interact with a Google Map Bicycling Layer before it has been initialized. ' +
'Please wait for the Transit Layer to load before trying to interact with it.');
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: MapBicyclingLayer, deps: [], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.0", type: MapBicyclingLayer, isStandalone: true, selector: "map-bicycling-layer", outputs: { bicyclingLayerInitialized: "bicyclingLayerInitialized" }, exportAs: ["mapBicyclingLayer"], ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: MapBicyclingLayer, decorators: [{
type: Directive,
args: [{
selector: 'map-bicycling-layer',
exportAs: 'mapBicyclingLayer',
}]
}], propDecorators: { bicyclingLayerInitialized: [{
type: Output
}] } });
// Workaround for: https://github.com/bazelbuild/rules_nodejs/issues/1265
/**
* Angular component that renders a Google Maps Circle via the Google Maps JavaScript API.
* @see developers.google.com/maps/documentation/javascript/reference/polygon#Circle
*/
class MapCircle {
_map = inject(GoogleMap);
_ngZone = inject(NgZone);
_eventManager = new MapEventManager(inject(NgZone));
_options = new BehaviorSubject({});
_center = new BehaviorSubject(undefined);
_radius = new BehaviorSubject(undefined);
_destroyed = new Subject();
/**
* Underlying google.maps.Circle object.
*
* @see developers.google.com/maps/documentation/javascript/reference/polygon#Circle
*/
circle; // initialized in ngOnInit
set options(options) {
this._options.next(options || {});
}
set center(center) {
this._center.next(center);
}
set radius(radius) {
this._radius.next(radius);
}
/**
* @see
* developers.google.com/maps/documentation/javascript/reference/polygon#Circle.center_changed
*/
centerChanged = this._eventManager.getLazyEmitter('center_changed');
/**
* @see
* developers.google.com/maps/documentation/javascript/reference/polygon#Circle.click
*/
circleClick = this._eventManager.getLazyEmitter('click');
/**
* @see
* developers.google.com/maps/documentation/javascript/reference/polygon#Circle.dblclick
*/
circleDblclick = this._eventManager.getLazyEmitter('dblclick');
/**
* @see
* developers.google.com/maps/documentation/javascript/reference/polygon#Circle.drag
*/
circleDrag = this._eventManager.getLazyEmitter('drag');
/**
* @see
* developers.google.com/maps/documentation/javascript/reference/polygon#Circle.dragend
*/
circleDragend = this._eventManager.getLazyEmitter('dragend');
/**
* @see
* developers.google.com/maps/documentation/javascript/reference/polygon#Circle.dragstart
*/
circleDragstart = this._eventManager.getLazyEmitter('dragstart');
/**
* @see
* developers.google.com/maps/documentation/javascript/reference/polygon#Circle.mousedown
*/
circleMousedown = this._eventManager.getLazyEmitter('mousedown');
/**
* @see
* developers.google.com/maps/documentation/javascript/reference/polygon#Circle.mousemove
*/
circleMousemove = this._eventManager.getLazyEmitter('mousemove');
/**
* @see
* developers.google.com/maps/documentation/javascript/reference/polygon#Circle.mouseout
*/
circleMouseout = this._eventManager.getLazyEmitter('mouseout');
/**
* @see
* developers.google.com/maps/documentation/javascript/reference/polygon#Circle.mouseover
*/
circleMouseover = this._eventManager.getLazyEmitter('mouseover');
/**
* @see
* developers.google.com/maps/documentation/javascript/reference/polygon#Circle.mouseup
*/
circleMouseup = this._eventManager.getLazyEmitter('mouseup');
/**
* @see
* developers.google.com/maps/documentation/javascript/reference/polygon#Circle.radius_changed
*/
radiusChanged = this._eventManager.getLazyEmitter('radius_changed');
/**
* @see
* developers.google.com/maps/documentation/javascript/reference/polygon#Circle.rightclick
*/
circleRightclick = this._eventManager.getLazyEmitter('rightclick');
/** Event emitted when the circle is initialized. */
circleInitialized = new EventEmitter();
constructor() { }
ngOnInit() {
if (!this._map._isBrowser) {
return;
}
this._combineOptions()
.pipe(take(1))
.subscribe(options => {
if (google.maps.Circle && this._map.googleMap) {
this._initialize(this._map.googleMap, google.maps.Circle, options);
}
else {
this._ngZone.runOutsideAngular(() => {
Promise.all([this._map._resolveMap(), google.maps.importLibrary('maps')]).then(([map, lib]) => {
this._initialize(map, lib.Circle, options);
});
});
}
});
}
_initialize(map, circleConstructor, options) {
// Create the object outside the zone so its events don't trigger change detection.
// We'll bring it back in inside the `MapEventManager` only for the events that the
// user has subscribed to.
this._ngZone.runOutsideAngular(() => {
this.circle = new circleConstructor(options);
this._assertInitialized();
this.circle.setMap(map);
this._eventManager.setTarget(this.circle);
this.circleInitialized.emit(this.circle);
this._watchForOptionsChanges();
this._watchForCenterChanges();
this._watchForRadiusChanges();
});
}
ngOnDestroy() {
this._eventManager.destroy();
this._destroyed.next();
this._destroyed.complete();
this.circle?.setMap(null);
}
/**
* @see
* developers.google.com/maps/documentation/javascript/reference/polygon#Circle.getBounds
*/
getBounds() {
this._assertInitialized();
return this.circle.getBounds();
}
/**
* @see
* developers.google.com/maps/documentation/javascript/reference/polygon#Circle.getCenter
*/
getCenter() {
this._assertInitialized();
return this.circle.getCenter();
}
/**
* @see
* developers.google.com/maps/documentation/javascript/reference/polygon#Circle.getDraggable
*/
getDraggable() {
this._assertInitialized();
return this.circle.getDraggable();
}
/**
* @see
* developers.google.com/maps/documentation/javascript/reference/polygon#Circle.getEditable
*/
getEditable() {
this._assertInitialized();
return this.circle.getEditable();
}
/**
* @see
* developers.google.com/maps/documentation/javascript/reference/polygon#Circle.getRadius
*/
getRadius() {
this._assertInitialized();
return this.circle.getRadius();
}
/**
* @see
* developers.google.com/maps/documentation/javascript/reference/polygon#Circle.getVisible
*/
getVisible() {
this._assertInitialized();
return this.circle.getVisible();
}
_combineOptions() {
return combineLatest([this._options, this._center, this._radius]).pipe(map(([options, center, radius]) => {
const combinedOptions = {
...options,
center: center || options.center,
radius: radius !== undefined ? radius : options.radius,
};
return combinedOptions;
}));
}
_watchForOptionsChanges() {
this._options.pipe(takeUntil(this._destroyed)).subscribe(options => {
this._assertInitialized();
this.circle.setOptions(options);
});
}
_watchForCenterChanges() {
this._center.pipe(takeUntil(this._destroyed)).subscribe(center => {
if (center) {
this._assertInitialized();
this.circle.setCenter(center);
}
});
}
_watchForRadiusChanges() {
this._radius.pipe(takeUntil(this._destroyed)).subscribe(radius => {
if (radius !== undefined) {
this._assertInitialized();
this.circle.setRadius(radius);
}
});
}
_assertInitialized() {
if (typeof ngDevMode === 'undefined' || ngDevMode) {
if (!this.circle) {
throw Error('Cannot interact with a Google Map Circle before it has been ' +
'initialized. Please wait for the Circle to load before trying to interact with it.');
}
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: MapCircle, deps: [], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.0", type: MapCircle, isStandalone: true, selector: "map-circle", inputs: { options: "options", center: "center", radius: "radius" }, outputs: { centerChanged: "centerChanged", circleClick: "circleClick", circleDblclick: "circleDblclick", circleDrag: "circleDrag", circleDragend: "circleDragend", circleDragstart: "circleDragstart", circleMousedown: "circleMousedown", circleMousemove: "circleMousemove", circleMouseout: "circleMouseout", circleMouseover: "circleMouseover", circleMouseup: "circleMouseup", radiusChanged: "radiusChanged", circleRightclick: "circleRightclick", circleInitialized: "circleInitialized" }, exportAs: ["mapCircle"], ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: MapCircle, decorators: [{
type: Directive,
args: [{
selector: 'map-circle',
exportAs: 'mapCircle',
}]
}], ctorParameters: () => [], propDecorators: { options: [{
type: Input
}], center: [{
type: Input
}], radius: [{
type: Input
}], centerChanged: [{
type: Output
}], circleClick: [{
type: Output
}], circleDblclick: [{
type: Output
}], circleDrag: [{
type: Output
}], circleDragend: [{
type: Output
}], circleDragstart: [{
type: Output
}], circleMousedown: [{
type: Output
}], circleMousemove: [{
type: Output
}], circleMouseout: [{
type: Output
}], circleMouseover: [{
type: Output
}], circleMouseup: [{
type: Output
}], radiusChanged: [{
type: Output
}], circleRightclick: [{
type: Output
}], circleInitialized: [{
type: Output
}] } });
// Workaround for: https://github.com/bazelbuild/rules_nodejs/issues/1265
/**
* Angular component that renders a Google Maps Directions Renderer via the Google Maps
* JavaScript API.
*
* See developers.google.com/maps/documentation/javascript/reference/directions#DirectionsRenderer
*/
class MapDirectionsRenderer {
_googleMap = inject(GoogleMap);
_ngZone = inject(NgZone);
_eventManager = new MapEventManager(inject(NgZone));
/**
* See developers.google.com/maps/documentation/javascript/reference/directions
* #DirectionsRendererOptions.directions
*/
set directions(directions) {
this._directions = directions;
}
_directions;
/**
* See developers.google.com/maps/documentation/javascript/reference/directions
* #DirectionsRendererOptions
*/
set options(options) {
this._options = options;
}
_options;
/**
* See developers.google.com/maps/documentation/javascript/reference/directions
* #DirectionsRenderer.directions_changed
*/
directionsChanged = this._eventManager.getLazyEmitter('directions_changed');
/** Event emitted when the directions renderer is initialized. */
directionsRendererInitialized = new EventEmitter();
/** The underlying google.maps.DirectionsRenderer object. */
directionsRenderer;
constructor() { }
ngOnInit() {
if (this._googleMap._isBrowser) {
if (google.maps.DirectionsRenderer && this._googleMap.googleMap) {
this._initialize(this._googleMap.googleMap, google.maps.DirectionsRenderer);
}
else {
this._ngZone.runOutsideAngular(() => {
Promise.all([this._googleMap._resolveMap(), google.maps.importLibrary('routes')]).then(([map, lib]) => {
this._initialize(map, lib.DirectionsRenderer);
});
});
}
}
}
_initialize(map, rendererConstructor) {
// Create the object outside the zone so its events don't trigger change detection.
// We'll bring it back in inside the `MapEventManager` only for the events that the
// user has subscribed to.
this._ngZone.runOutsideAngular(() => {
this.directionsRenderer = new rendererConstructor(this._combineOptions());
this._assertInitialized();
this.directionsRenderer.setMap(map);
this._eventManager.setTarget(this.directionsRenderer);
this.directionsRendererInitialized.emit(this.directionsRenderer);
});
}
ngOnChanges(changes) {
if (this.directionsRenderer) {
if (changes['options']) {
this.directionsRenderer.setOptions(this._combineOptions());
}
if (changes['directions'] && this._directions !== undefined) {
this.directionsRenderer.setDirections(this._directions);
}
}
}
ngOnDestroy() {
this._eventManager.destroy();
this.directionsRenderer?.setMap(null);
}
/**
* See developers.google.com/maps/documentation/javascript/reference/directions
* #DirectionsRenderer.getDirections
*/
getDirections() {
this._assertInitialized();
return this.directionsRenderer.getDirections();
}
/**
* See developers.google.com/maps/documentation/javascript/reference/directions
* #DirectionsRenderer.getPanel
*/
getPanel() {
this._assertInitialized();
return this.directionsRenderer.getPanel();
}
/**
* See developers.google.com/maps/documentation/javascript/reference/directions
* #DirectionsRenderer.getRouteIndex
*/
getRouteIndex() {
this._assertInitialized();
return this.directionsRenderer.getRouteIndex();
}
_combineOptions() {
const options = this._options || {};
return {
...options,
directions: this._directions || options.directions,
map: this._googleMap.googleMap,
};
}
_assertInitialized() {
if (typeof ngDevMode === 'undefined' || ngDevMode) {
if (!this.directionsRenderer) {
throw Error('Cannot interact with a Google Map Directions Renderer before it has been ' +
'initialized. Please wait for the Directions Renderer to load before trying ' +
'to interact with it.');
}
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: MapDirectionsRenderer, deps: [], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.0", type: MapDirectionsRenderer, isStandalone: true, selector: "map-directions-renderer", inputs: { directions: "directions", options: "options" }, outputs: { directionsChanged: "directionsChanged", directionsRendererInitialized: "directionsRendererInitialized" }, exportAs: ["mapDirectionsRenderer"], usesOnChanges: true, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: MapDirectionsRenderer, decorators: [{
type: Directive,
args: [{
selector: 'map-directions-renderer',
exportAs: 'mapDirectionsRenderer',
}]
}], ctorParameters: () => [], propDecorators: { directions: [{
type: Input
}], options: [{
type: Input
}], directionsChanged: [{
type: Output
}], directionsRendererInitialized: [{
type: Output
}] } });
// Workaround for: https://github.com/bazelbuild/rules_nodejs/issues/1265
/**
* Angular component that renders a Google Maps Ground Overlay via the Google Maps JavaScript API.
*
* See developers.google.com/maps/documentation/javascript/reference/image-overlay#GroundOverlay
*/
class MapGroundOverlay {
_map = inject(GoogleMap);
_ngZone = inject(NgZone);
_eventManager = new MapEventManager(inject(NgZone));
_opacity = new BehaviorSubject(1);
_url = new BehaviorSubject('');
_bounds = new BehaviorSubject(undefined);
_destroyed = new Subject();
_hasWatchers;
/**
* The underlying google.maps.GroundOverlay object.
*
* See developers.google.com/maps/documentation/javascript/reference/image-overlay#GroundOverlay
*/
groundOverlay;
/** URL of the image that will be shown in the overlay. */
set url(url) {
this._url.next(url);
}
/** Bounds for the overlay. */
get bounds() {
return this._bounds.value;
}
set bounds(bounds) {
this._bounds.next(bounds);
}
/** Whether the overlay is clickable */
clickable = false;
/** Opacity of the overlay. */
set opacity(opacity) {
this._opacity.next(opacity);
}
/**
* See
* developers.google.com/maps/documentation/javascript/reference/image-overlay#GroundOverlay.click
*/
mapClick = this._eventManager.getLazyEmitter('click');
/**
* See
* developers.google.com/maps/documentation/javascript/reference/image-overlay
* #GroundOverlay.dblclick
*/
mapDblclick = this._eventManager.getLazyEmitter('dblclick');
/** Event emitted when the ground overlay is initialized. */
groundOverlayInitialized = new EventEmitter();
constructor() { }
ngOnInit() {
if (this._map._isBrowser) {
// The ground overlay setup is slightly different from the other Google Maps objects in that
// we have to recreate the `GroundOverlay` object whenever the bounds change, because
// Google Maps doesn't provide an API to update the bounds of an existing overlay.
this._bounds.pipe(takeUntil(this._destroyed)).subscribe(bounds => {
if (this.groundOverlay) {
this.groundOverlay.setMap(null);
this.groundOverlay = undefined;
}
if (!bounds) {
return;
}
if (google.maps.GroundOverlay && this._map.googleMap) {
this._initialize(this._map.googleMap, google.maps.GroundOverlay, bounds);
}
else {
this._ngZone.runOutsideAngular(() => {
Promise.all([this._map._resolveMap(), google.maps.importLibrary('maps')]).then(([map, lib]) => {
this._initialize(map, lib.GroundOverlay, bounds);
});
});
}
});
}
}
_initialize(map, overlayConstructor, bounds) {
// Create the object outside the zone so its events don't trigger change detection.
// We'll bring it back in inside the `MapEventManager` only for the events that the
// user has subscribed to.
this._ngZone.runOutsideAngular(() => {
this.groundOverlay = new overlayConstructor(this._url.getValue(), bounds, {
clickable: this.clickable,
opacity: this._opacity.value,
});
this._assertInitialized();
this.groundOverlay.setMap(map);
this._eventManager.setTarget(this.groundOverlay);
this.groundOverlayInitialized.emit(this.groundOverlay);
// We only need to set up the watchers once.
if (!this._hasWatchers) {
this._hasWatchers = true;
this._watchForOpacityChanges();
this._watchForUrlChanges();
}
});
}
ngOnDestroy() {
this._eventManager.destroy();
this._destroyed.next();
this._destroyed.complete();
this.groundOverlay?.setMap(null);
}
/**
* See
* developers.google.com/maps/documentation/javascript/reference/image-overlay
* #GroundOverlay.getBounds
*/
getBounds() {
this._assertInitialized();
return this.groundOverlay.getBounds();
}
/**
* See
* developers.google.com/maps/documentation/javascript/reference/image-overlay
* #GroundOverlay.getOpacity
*/
getOpacity() {
this._assertInitialized();
return this.groundOverlay.getOpacity();
}
/**
* See
* developers.google.com/maps/documentation/javascript/reference/image-overlay
* #GroundOverlay.getUrl
*/
getUrl() {
this._assertInitialized();
return this.groundOverlay.getUrl();
}
_watchForOpacityChanges() {
this._opacity.pipe(takeUntil(this._destroyed)).subscribe(opacity => {
if (opacity != null) {
this.groundOverlay?.setOpacity(opacity);
}
});
}
_watchForUrlChanges() {
this._url.pipe(takeUntil(this._destroyed)).subscribe(url => {
const overlay = this.groundOverlay;
if (overlay) {
overlay.set('url', url);
// Google Maps only redraws the overlay if we re-set the map.
overlay.setMap(null);
overlay.setMap(this._map.googleMap);
}
});
}
_assertInitialized() {
if (typeof ngDevMode === 'undefined' || ngDevMode) {
if (!this.groundOverlay) {
throw Error('Cannot interact with a Google Map GroundOverlay before it has been initialized. ' +
'Please wait for the GroundOverlay to load before trying to interact with it.');
}
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: MapGroundOverlay, deps: [], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.0", type: MapGroundOverlay, isStandalone: true, selector: "map-ground-overlay", inputs: { url: "url", bounds: "bounds", clickable: "clickable", opacity: "opacity" }, outputs: { mapClick: "mapClick", mapDblclick: "mapDblclick", groundOverlayInitialized: "groundOverlayInitialized" }, exportAs: ["mapGroundOverlay"], ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: MapGroundOverlay, decorators: [{
type: Directive,
args: [{
selector: 'map-ground-overlay',
exportAs: 'mapGroundOverlay',
}]
}], ctorParameters: () => [], propDecorators: { url: [{
type: Input
}], bounds: [{
type: Input
}], clickable: [{
type: Input
}], opacity: [{
type: Input
}], mapClick: [{
type: Output
}], mapDblclick: [{
type: Output
}], groundOverlayInitialized: [{
type: Output
}] } });
// Workaround for: https://github.com/bazelbuild/rules_nodejs/issues/1265
/**
* Angular component that renders a Google Maps info window via the Google Maps JavaScript API.
*
* See developers.google.com/maps/documentation/javascript/reference/info-window
*/
class MapInfoWindow {
_googleMap = inject(GoogleMap);
_elementRef = inject(ElementRef);
_ngZone = inject(NgZone);
_eventManager = new MapEventManager(inject(NgZone));
_options = new BehaviorSubject({});
_position = new BehaviorSubject(undefined);
_destroy = new Subject();
/**
* Underlying google.maps.InfoWindow
*
* See developers.google.com/maps/documentation/javascript/reference/info-window#InfoWindow
*/
infoWindow;
set options(options) {
this._options.next(options || {});
}
set position(position) {
this._position.next(position);
}
/**
* See
* developers.google.com/maps/documentation/javascript/reference/info-window#InfoWindow.closeclick
*/
closeclick = this._eventManager.getLazyEmitter('closeclick');
/**
* See
* developers.google.com/maps/documentation/javascript/reference/info-window
* #InfoWindow.content_changed
*/
contentChanged = this._eventManager.getLaz