@theseam/ui-common 0.3.1 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/breadcrumbs/_breadcrumbs-theme.scss +3 -3
- package/breadcrumbs/breadcrumbs/breadcrumbs.component.scss +10 -10
- package/bundles/theseam-ui-common-form-field.umd.js +1 -1
- package/bundles/theseam-ui-common-form-field.umd.js.map +1 -1
- package/bundles/theseam-ui-common-google-maps.umd.js +2202 -0
- package/bundles/theseam-ui-common-google-maps.umd.js.map +1 -0
- package/bundles/theseam-ui-common-menu.umd.js +1 -0
- package/bundles/theseam-ui-common-menu.umd.js.map +1 -1
- package/bundles/theseam-ui-common-utils.umd.js +610 -136
- package/bundles/theseam-ui-common-utils.umd.js.map +1 -1
- package/esm2015/form-field/input.directive.js +2 -2
- package/esm2015/google-maps/google-maps/google-maps.component.js +261 -0
- package/esm2015/google-maps/google-maps-contextmenu.js +113 -0
- package/esm2015/google-maps/google-maps-controls.service.js +70 -0
- package/esm2015/google-maps/google-maps-feature-helpers.js +177 -0
- package/esm2015/google-maps/google-maps-places-autocomplete/google-maps-places-autocomplete.component.js +195 -0
- package/esm2015/google-maps/google-maps-places-autocomplete/google-maps-places-autocomplete.directive.js +163 -0
- package/esm2015/google-maps/google-maps-recenter-button-control/google-maps-recenter-button-control.component.js +57 -0
- package/esm2015/google-maps/google-maps-upload-button-control/google-maps-upload-button-control.component.js +119 -0
- package/esm2015/google-maps/google-maps.module.js +45 -0
- package/esm2015/google-maps/google-maps.service.js +344 -0
- package/esm2015/google-maps/map-control.component.js +65 -0
- package/esm2015/google-maps/map-controls-service.js +4 -0
- package/esm2015/google-maps/map-file-drop/map-file-drop.component.js +135 -0
- package/esm2015/google-maps/map-value-manager.service.js +46 -0
- package/esm2015/google-maps/public-api.js +14 -0
- package/esm2015/google-maps/theseam-ui-common-google-maps.js +6 -0
- package/esm2015/menu/menu-toggle.directive.js +2 -1
- package/esm2015/utils/geo-json/coerce-feature-collection.js +44 -0
- package/esm2015/utils/geo-json/geo-json-to-area.js +11 -0
- package/esm2015/utils/geo-json/is-feature-collection.validator.js +21 -0
- package/esm2015/utils/geo-json/is-only-geometry-types.js +23 -0
- package/esm2015/utils/geo-json/is-only-geometry-types.validator.js +32 -0
- package/esm2015/utils/geo-json/merge-polygons.js +35 -0
- package/esm2015/utils/geo-json/no-inner-rings.validator.js +63 -0
- package/esm2015/utils/geo-json/no-kinks.validator.js +39 -0
- package/esm2015/utils/geo-json/read-geo-file.js +99 -0
- package/esm2015/utils/geo-json/split-multi-polygons.js +29 -0
- package/esm2015/utils/is-null-or-undefined.js +1 -1
- package/esm2015/utils/public-api.js +11 -1
- package/fesm2015/theseam-ui-common-form-field.js +1 -1
- package/fesm2015/theseam-ui-common-form-field.js.map +1 -1
- package/fesm2015/theseam-ui-common-google-maps.js +1729 -0
- package/fesm2015/theseam-ui-common-google-maps.js.map +1 -0
- package/fesm2015/theseam-ui-common-menu.js +1 -0
- package/fesm2015/theseam-ui-common-menu.js.map +1 -1
- package/fesm2015/theseam-ui-common-utils.js +477 -94
- package/fesm2015/theseam-ui-common-utils.js.map +1 -1
- package/form-field/theseam-ui-common-form-field.metadata.json +1 -1
- package/framework/top-bar/_top-bar-theme.scss +5 -5
- package/google-maps/google-maps/google-maps.component.d.ts +89 -0
- package/google-maps/google-maps-contextmenu.d.ts +15 -0
- package/google-maps/google-maps-controls.service.d.ts +23 -0
- package/google-maps/google-maps-feature-helpers.d.ts +37 -0
- package/google-maps/google-maps-places-autocomplete/google-maps-places-autocomplete.component.d.ts +104 -0
- package/google-maps/google-maps-places-autocomplete/google-maps-places-autocomplete.directive.d.ts +80 -0
- package/google-maps/google-maps-recenter-button-control/google-maps-recenter-button-control.component.d.ts +21 -0
- package/google-maps/google-maps-upload-button-control/google-maps-upload-button-control.component.d.ts +34 -0
- package/google-maps/google-maps.module.d.ts +2 -0
- package/google-maps/google-maps.service.d.ts +53 -0
- package/google-maps/map-control.component.d.ts +20 -0
- package/google-maps/map-controls-service.d.ts +13 -0
- package/google-maps/map-file-drop/map-file-drop.component.d.ts +34 -0
- package/google-maps/map-value-manager.service.d.ts +18 -0
- package/google-maps/package.json +11 -0
- package/google-maps/public-api.d.ts +13 -0
- package/google-maps/theseam-ui-common-google-maps.d.ts +5 -0
- package/google-maps/theseam-ui-common-google-maps.metadata.json +1 -0
- package/package.json +17 -10
- package/utils/geo-json/coerce-feature-collection.d.ts +2 -0
- package/utils/geo-json/geo-json-to-area.d.ts +6 -0
- package/utils/geo-json/is-feature-collection.validator.d.ts +3 -0
- package/utils/geo-json/is-only-geometry-types.d.ts +5 -0
- package/utils/geo-json/is-only-geometry-types.validator.d.ts +4 -0
- package/utils/geo-json/merge-polygons.d.ts +9 -0
- package/utils/geo-json/no-inner-rings.validator.d.ts +10 -0
- package/utils/geo-json/no-kinks.validator.d.ts +3 -0
- package/utils/geo-json/read-geo-file.d.ts +7 -0
- package/utils/geo-json/split-multi-polygons.d.ts +8 -0
- package/utils/is-null-or-undefined.d.ts +1 -1
- package/utils/public-api.d.ts +10 -0
- package/utils/theseam-ui-common-utils.metadata.json +1 -1
|
@@ -0,0 +1,1729 @@
|
|
|
1
|
+
import { __awaiter, __decorate } from 'tslib';
|
|
2
|
+
import { FocusMonitor } from '@angular/cdk/a11y';
|
|
3
|
+
import { coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';
|
|
4
|
+
import { Injectable, NgZone, ViewContainerRef, InjectionToken, Injector, ComponentFactoryResolver, Component, ChangeDetectionStrategy, Optional, Inject, Input, HostListener, ElementRef, Renderer2, EventEmitter, forwardRef, HostBinding, Output, ViewChild, Directive, NgModule } from '@angular/core';
|
|
5
|
+
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
6
|
+
import { Observable, Subject, fromEvent, BehaviorSubject, from, interval } from 'rxjs';
|
|
7
|
+
import { takeUntil, switchMap, tap, startWith, filter, take, mapTo } from 'rxjs/operators';
|
|
8
|
+
import { AgmMap, AgmCoreModule } from '@agm/core';
|
|
9
|
+
import { faFileImport, faCrosshairs, faSearchLocation } from '@fortawesome/free-solid-svg-icons';
|
|
10
|
+
import { mixinDisabled, InputBoolean, InputNumber } from '@theseam/ui-common/core';
|
|
11
|
+
import { MenuComponent, TheSeamMenuModule } from '@theseam/ui-common/menu';
|
|
12
|
+
import { notNullOrUndefined, isNullOrUndefined, readGeoFile } from '@theseam/ui-common/utils';
|
|
13
|
+
import { ESCAPE } from '@angular/cdk/keycodes';
|
|
14
|
+
import booleanContains from '@turf/boolean-contains';
|
|
15
|
+
import { polygon, multiPolygon } from '@turf/helpers';
|
|
16
|
+
import { ObserversModule } from '@angular/cdk/observers';
|
|
17
|
+
import { CommonModule } from '@angular/common';
|
|
18
|
+
import { InputDirective, TheSeamFormFieldModule } from '@theseam/ui-common/form-field';
|
|
19
|
+
import { TheSeamIconModule } from '@theseam/ui-common/icon';
|
|
20
|
+
import { TheSeamSharedModule } from '@theseam/ui-common/shared';
|
|
21
|
+
|
|
22
|
+
var AppFeaturePropertyName;
|
|
23
|
+
(function (AppFeaturePropertyName) {
|
|
24
|
+
AppFeaturePropertyName["IsSelected"] = "__app__isSelected";
|
|
25
|
+
})(AppFeaturePropertyName || (AppFeaturePropertyName = {}));
|
|
26
|
+
function isAppFeatureProperty(propertyName) {
|
|
27
|
+
return Object.values(AppFeaturePropertyName)
|
|
28
|
+
.findIndex(value => value === propertyName) !== -1;
|
|
29
|
+
}
|
|
30
|
+
function isFeatureSelected(feature) {
|
|
31
|
+
const isSelected = feature.getProperty(AppFeaturePropertyName.IsSelected);
|
|
32
|
+
return coerceBooleanProperty(isSelected);
|
|
33
|
+
}
|
|
34
|
+
function setFeatureSelected(feature, isSelected) {
|
|
35
|
+
feature.setProperty(AppFeaturePropertyName.IsSelected, isSelected);
|
|
36
|
+
}
|
|
37
|
+
// TODO: Check performance of cloning a google.maps.Data instance, so the
|
|
38
|
+
// properties can be removed with the google maps api, instead of on the
|
|
39
|
+
// resulting json.
|
|
40
|
+
function stripAppFeaturePropertiesFromJson(json) {
|
|
41
|
+
if (notNullOrUndefined(json) && Array.isArray(json === null || json === void 0 ? void 0 : json.features)) {
|
|
42
|
+
for (const feature of json.features) {
|
|
43
|
+
if (notNullOrUndefined(feature === null || feature === void 0 ? void 0 : feature.properties)) {
|
|
44
|
+
for (const k of Object.keys(feature.properties)) {
|
|
45
|
+
if (isAppFeatureProperty(k)) {
|
|
46
|
+
feature.properties[k] = undefined;
|
|
47
|
+
delete feature.properties[k];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Searches for a Feature in Data that contains the provided Feature and can use
|
|
56
|
+
* it as a cutout area.
|
|
57
|
+
*/
|
|
58
|
+
function getPossibleExteriorFeature(data, feature) {
|
|
59
|
+
let exteriorPolygonFeature;
|
|
60
|
+
data.forEach(f => {
|
|
61
|
+
if (f !== feature && (f.getGeometry().getType() === 'Polygon' && featureContains(f, feature))) {
|
|
62
|
+
exteriorPolygonFeature = f;
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
return exteriorPolygonFeature;
|
|
66
|
+
}
|
|
67
|
+
function addInnerFeatureCutoutToExteriorFeature(exteriorFeature, innerFeature) {
|
|
68
|
+
// NOTE: Other geometries may support cutouts, but our map shapes editor only
|
|
69
|
+
// supports polygons currently, so we will need to handle other geometry types
|
|
70
|
+
// here if we start allowing users to draw shapes other than polygon.
|
|
71
|
+
if (exteriorFeature.getGeometry().getType() !== 'Polygon' || innerFeature.getGeometry().getType() !== 'Polygon') {
|
|
72
|
+
throw Error(`Inner cutout is only supported by Polygon gemoetry.`);
|
|
73
|
+
}
|
|
74
|
+
const featurePolygon = innerFeature.getGeometry();
|
|
75
|
+
const exteriorPolygon = exteriorFeature.getGeometry();
|
|
76
|
+
exteriorFeature.setGeometry(new google.maps.Data.Polygon([
|
|
77
|
+
...exteriorPolygon.getArray(),
|
|
78
|
+
featurePolygon.getAt(0).getArray().reverse()
|
|
79
|
+
]));
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Google maps paths don't always start and stop at the exact same position, so
|
|
83
|
+
* this will fix that for turfjs.
|
|
84
|
+
*/
|
|
85
|
+
function fixPathDifferentStartingAndEndingPoint(coordinates) {
|
|
86
|
+
if (coordinates.length <= 1) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const start = coordinates[0];
|
|
90
|
+
const end = coordinates[coordinates.length - 1];
|
|
91
|
+
if (start[0] === end[0] && start[1] === end[1]) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
coordinates.push(coordinates[0]);
|
|
95
|
+
}
|
|
96
|
+
function polygonCoordinates(polygon) {
|
|
97
|
+
return polygon.getArray().map(linRing => {
|
|
98
|
+
const coords = linRing.getArray().map(x => [x.lng(), x.lat()]);
|
|
99
|
+
fixPathDifferentStartingAndEndingPoint(coords);
|
|
100
|
+
return coords;
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
function multiPolygonCoordinates(multiPolygon) {
|
|
104
|
+
return multiPolygon.getArray().map(x => polygonCoordinates(x));
|
|
105
|
+
}
|
|
106
|
+
function toTurfJsPolygon(polygon$1) {
|
|
107
|
+
return polygon(polygonCoordinates(polygon$1));
|
|
108
|
+
}
|
|
109
|
+
function toTurfJsMultiPolygon(multiPolygon$1) {
|
|
110
|
+
return multiPolygon(multiPolygonCoordinates(multiPolygon$1));
|
|
111
|
+
}
|
|
112
|
+
function toTurfJsFeature(googleFeature) {
|
|
113
|
+
if (googleFeature.getGeometry().getType() === 'Polygon') {
|
|
114
|
+
return toTurfJsPolygon(googleFeature.getGeometry());
|
|
115
|
+
}
|
|
116
|
+
else if (googleFeature.getGeometry().getType() === 'MultiPolygon') {
|
|
117
|
+
return toTurfJsMultiPolygon(googleFeature.getGeometry());
|
|
118
|
+
}
|
|
119
|
+
throw Error(`Unexpected geometry.`);
|
|
120
|
+
}
|
|
121
|
+
function featureContains(featureA, featureB) {
|
|
122
|
+
const polygonA = toTurfJsFeature(featureA);
|
|
123
|
+
const polygonB = toTurfJsFeature(featureB);
|
|
124
|
+
return booleanContains(polygonA, polygonB);
|
|
125
|
+
}
|
|
126
|
+
function createDataFeatureFromPolygon(polygon) {
|
|
127
|
+
const arr = polygon.getPath().getArray();
|
|
128
|
+
return new google.maps.Data.Feature({
|
|
129
|
+
geometry: new google.maps.Data.Polygon([arr])
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
function getBoundsWithAllFeatures(data) {
|
|
133
|
+
const bounds = new google.maps.LatLngBounds();
|
|
134
|
+
data.forEach(feature => {
|
|
135
|
+
const geometry = feature.getGeometry();
|
|
136
|
+
geometry.forEachLatLng(latLng => {
|
|
137
|
+
bounds.extend(latLng);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
return bounds;
|
|
141
|
+
}
|
|
142
|
+
function getFeatureBounds(feature) {
|
|
143
|
+
const bounds = new google.maps.LatLngBounds();
|
|
144
|
+
const geometry = feature.getGeometry();
|
|
145
|
+
geometry.forEachLatLng(latLng => {
|
|
146
|
+
bounds.extend(latLng);
|
|
147
|
+
});
|
|
148
|
+
return bounds;
|
|
149
|
+
}
|
|
150
|
+
function getFeatureCenter(feature) {
|
|
151
|
+
return getFeatureBounds(feature).getCenter();
|
|
152
|
+
}
|
|
153
|
+
function removeAllFeatures(data) {
|
|
154
|
+
data.forEach(f => data.remove(f));
|
|
155
|
+
}
|
|
156
|
+
function getFeaturesCount(data) {
|
|
157
|
+
let count = 0;
|
|
158
|
+
data.forEach(() => count++);
|
|
159
|
+
return count;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* NOTE: Original events are not emitted, because filtering may omit events.
|
|
163
|
+
*/
|
|
164
|
+
function createFeatureChangeObservable(data, ngZone) {
|
|
165
|
+
return new Observable(subscriber => {
|
|
166
|
+
const listeners = [];
|
|
167
|
+
ngZone.runOutsideAngular(() => {
|
|
168
|
+
listeners.push(data.addListener('setgeometry', (event) => {
|
|
169
|
+
ngZone.run(() => { subscriber.next(undefined); });
|
|
170
|
+
}));
|
|
171
|
+
listeners.push(data.addListener('addfeature', (event) => {
|
|
172
|
+
ngZone.run(() => { subscriber.next(undefined); });
|
|
173
|
+
}));
|
|
174
|
+
listeners.push(data.addListener('removefeature', (event) => {
|
|
175
|
+
ngZone.run(() => { subscriber.next(undefined); });
|
|
176
|
+
}));
|
|
177
|
+
listeners.push(data.addListener('setproperty', (event) => {
|
|
178
|
+
if (!isAppFeatureProperty(event.name)) {
|
|
179
|
+
ngZone.run(() => { subscriber.next(undefined); });
|
|
180
|
+
}
|
|
181
|
+
}));
|
|
182
|
+
listeners.push(data.addListener('removeproperty', (event) => {
|
|
183
|
+
if (!isAppFeatureProperty(event.name)) {
|
|
184
|
+
ngZone.run(() => { subscriber.next(undefined); });
|
|
185
|
+
}
|
|
186
|
+
}));
|
|
187
|
+
});
|
|
188
|
+
return () => {
|
|
189
|
+
listeners.forEach(google.maps.event.removeListener);
|
|
190
|
+
};
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// TODO: Close on map losing focus.
|
|
195
|
+
class GoogleMapsContextMenu {
|
|
196
|
+
constructor(_map, _menu, _position, _vcr, _ngZone, _data, _feature) {
|
|
197
|
+
this._map = _map;
|
|
198
|
+
this._menu = _menu;
|
|
199
|
+
this._vcr = _vcr;
|
|
200
|
+
this._ngZone = _ngZone;
|
|
201
|
+
this._data = _data;
|
|
202
|
+
this._feature = _feature;
|
|
203
|
+
const tplRef = this._menu.templateRef;
|
|
204
|
+
if (tplRef === undefined || tplRef === null) {
|
|
205
|
+
throw Error(`Menu template is not defined.`);
|
|
206
|
+
}
|
|
207
|
+
const ref = this._vcr.createEmbeddedView(tplRef);
|
|
208
|
+
ref.detectChanges();
|
|
209
|
+
const listeners = [];
|
|
210
|
+
const ngUnsubscribe = new Subject();
|
|
211
|
+
// TODO: Fix the initial focus. setTimeout avoids the wrong contextmenu
|
|
212
|
+
// getting triggered, but the first item flashes and looks off this way.
|
|
213
|
+
setTimeout(() => {
|
|
214
|
+
this._menu.focusFirstItem('program');
|
|
215
|
+
});
|
|
216
|
+
this._menu.closed.pipe(takeUntil(ngUnsubscribe)).subscribe(v => {
|
|
217
|
+
this.close();
|
|
218
|
+
});
|
|
219
|
+
fromEvent(document, 'keydown').pipe(takeUntil(ngUnsubscribe)).subscribe((event) => {
|
|
220
|
+
if (event.keyCode === ESCAPE) {
|
|
221
|
+
this.close();
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
const __cleanup = () => this._cleanupFn();
|
|
225
|
+
class GoogleMapsContextMenuOverlayView extends google.maps.OverlayView {
|
|
226
|
+
constructor(position, content) {
|
|
227
|
+
super();
|
|
228
|
+
this.position = position;
|
|
229
|
+
this.position = position;
|
|
230
|
+
this.containerDiv = document.createElement('div');
|
|
231
|
+
this.containerDiv.style.cursor = 'auto';
|
|
232
|
+
this.containerDiv.style.height = '0',
|
|
233
|
+
this.containerDiv.style.position = 'absolute';
|
|
234
|
+
this.containerDiv.appendChild(content);
|
|
235
|
+
// Optionally stop clicks, etc., from bubbling up to the map.
|
|
236
|
+
GoogleMapsContextMenuOverlayView.preventMapHitsAndGesturesFrom(this.containerDiv);
|
|
237
|
+
}
|
|
238
|
+
/** Called when the view is added to the map. */
|
|
239
|
+
onAdd() {
|
|
240
|
+
// tslint:disable-next-line: no-non-null-assertion
|
|
241
|
+
this.getPanes().floatPane.appendChild(this.containerDiv);
|
|
242
|
+
}
|
|
243
|
+
/** Called when the view is removed from the map. */
|
|
244
|
+
onRemove() {
|
|
245
|
+
if (this.containerDiv.parentElement) {
|
|
246
|
+
this.containerDiv.parentElement.removeChild(this.containerDiv);
|
|
247
|
+
}
|
|
248
|
+
__cleanup();
|
|
249
|
+
}
|
|
250
|
+
/** Called each frame when the view needs to draw itself. */
|
|
251
|
+
draw() {
|
|
252
|
+
// tslint:disable-next-line: no-non-null-assertion
|
|
253
|
+
const divPosition = this.getProjection().fromLatLngToDivPixel(this.position);
|
|
254
|
+
// Hide the popup when it is far out of view.
|
|
255
|
+
const display = Math.abs(divPosition.x) < 4000 && Math.abs(divPosition.y) < 4000
|
|
256
|
+
? 'block'
|
|
257
|
+
: 'none';
|
|
258
|
+
if (display === 'block') {
|
|
259
|
+
this.containerDiv.style.left = divPosition.x + 'px';
|
|
260
|
+
this.containerDiv.style.top = divPosition.y + 'px';
|
|
261
|
+
}
|
|
262
|
+
if (this.containerDiv.style.display !== display) {
|
|
263
|
+
this.containerDiv.style.display = display;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
this._overlayView = new GoogleMapsContextMenuOverlayView(_position, ref.rootNodes[0]);
|
|
268
|
+
this._overlayView.setMap(this._map);
|
|
269
|
+
this._ngZone.runOutsideAngular(() => {
|
|
270
|
+
listeners.push(this._data.addListener('removefeature', (event) => {
|
|
271
|
+
if (event.feature === this._feature) {
|
|
272
|
+
this._ngZone.run(() => { this.close(); });
|
|
273
|
+
}
|
|
274
|
+
}));
|
|
275
|
+
listeners.push(this._data.addListener('setproperty', (event) => {
|
|
276
|
+
if (event.feature === this._feature && !isFeatureSelected(this._feature)) {
|
|
277
|
+
this._ngZone.run(() => { this.close(); });
|
|
278
|
+
}
|
|
279
|
+
}));
|
|
280
|
+
listeners.push(this._data.addListener('removeproperty', (event) => {
|
|
281
|
+
if (event.feature === this._feature && !isFeatureSelected(this._feature)) {
|
|
282
|
+
this._ngZone.run(() => { this.close(); });
|
|
283
|
+
}
|
|
284
|
+
}));
|
|
285
|
+
listeners.push(this._map.addListener('click', (event) => {
|
|
286
|
+
this._ngZone.run(() => { this.close(); });
|
|
287
|
+
}));
|
|
288
|
+
listeners.push(this._data.addListener('click', (event) => {
|
|
289
|
+
this._ngZone.run(() => { this.close(); });
|
|
290
|
+
}));
|
|
291
|
+
});
|
|
292
|
+
this._cleanupFn = () => {
|
|
293
|
+
ngUnsubscribe.next();
|
|
294
|
+
ngUnsubscribe.complete();
|
|
295
|
+
listeners.forEach(google.maps.event.removeListener);
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
close() {
|
|
299
|
+
this._overlayView.setMap(null);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
var MapValueSource;
|
|
304
|
+
(function (MapValueSource) {
|
|
305
|
+
MapValueSource["Input"] = "input";
|
|
306
|
+
MapValueSource["FeatureChange"] = "featurechange";
|
|
307
|
+
})(MapValueSource || (MapValueSource = {}));
|
|
308
|
+
class MapValueManagerService {
|
|
309
|
+
constructor() {
|
|
310
|
+
this._valueChangedSubject = new Subject();
|
|
311
|
+
this.valueChanged = this._valueChangedSubject.asObservable();
|
|
312
|
+
}
|
|
313
|
+
setValue(value, source) {
|
|
314
|
+
if (value === this._value) {
|
|
315
|
+
return false;
|
|
316
|
+
}
|
|
317
|
+
if (value === null || value === undefined) {
|
|
318
|
+
this._value = value;
|
|
319
|
+
const _change = {
|
|
320
|
+
source,
|
|
321
|
+
value: this._value,
|
|
322
|
+
};
|
|
323
|
+
this._valueChangedSubject.next(_change);
|
|
324
|
+
return true;
|
|
325
|
+
}
|
|
326
|
+
if (JSON.stringify(value) === JSON.stringify(this._value)) {
|
|
327
|
+
return true;
|
|
328
|
+
}
|
|
329
|
+
// TODO: Validate object is valid geojson.
|
|
330
|
+
this._value = value;
|
|
331
|
+
const change = {
|
|
332
|
+
source,
|
|
333
|
+
value: this._value,
|
|
334
|
+
};
|
|
335
|
+
this._valueChangedSubject.next(change);
|
|
336
|
+
return true;
|
|
337
|
+
}
|
|
338
|
+
get value() {
|
|
339
|
+
return this._value;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
MapValueManagerService.decorators = [
|
|
343
|
+
{ type: Injectable }
|
|
344
|
+
];
|
|
345
|
+
MapValueManagerService.ctorParameters = () => [];
|
|
346
|
+
|
|
347
|
+
const DEFAULT_POLYGON_OPTIONS = {
|
|
348
|
+
clickable: true,
|
|
349
|
+
draggable: true,
|
|
350
|
+
editable: true,
|
|
351
|
+
// fillColor: 'rgba(60,150,60,1)',
|
|
352
|
+
// strokeColor: 'rgba(155,255,0,1)'
|
|
353
|
+
};
|
|
354
|
+
const DEFAULT_DRAWING_MANAGER_OPTIONS = () => ({
|
|
355
|
+
drawingControl: true,
|
|
356
|
+
drawingControlOptions: {
|
|
357
|
+
drawingModes: [
|
|
358
|
+
google.maps.drawing.OverlayType.POLYGON
|
|
359
|
+
]
|
|
360
|
+
},
|
|
361
|
+
polygonOptions: DEFAULT_POLYGON_OPTIONS,
|
|
362
|
+
drawingMode: null
|
|
363
|
+
});
|
|
364
|
+
const ɵ0 = DEFAULT_DRAWING_MANAGER_OPTIONS;
|
|
365
|
+
const FEATURE_STYLE_OPTIONS_DEFAULT = {
|
|
366
|
+
clickable: true,
|
|
367
|
+
visible: true,
|
|
368
|
+
// zIndex?: number;
|
|
369
|
+
// cursor?: string;
|
|
370
|
+
draggable: false,
|
|
371
|
+
editable: false,
|
|
372
|
+
fillColor: 'teal',
|
|
373
|
+
fillOpacity: 0.3,
|
|
374
|
+
strokeColor: 'blue',
|
|
375
|
+
strokeOpacity: 1,
|
|
376
|
+
strokeWeight: 2,
|
|
377
|
+
};
|
|
378
|
+
const FEATURE_STYLE_OPTIONS_SELECTED = Object.assign(Object.assign({}, FEATURE_STYLE_OPTIONS_DEFAULT), { draggable: true, editable: true, fillColor: 'green', fillOpacity: 0.7, strokeColor: 'limegreen', strokeOpacity: 1, strokeWeight: 2 });
|
|
379
|
+
class GoogleMapsService {
|
|
380
|
+
constructor(_mapValueManager, _ngZone, _vcr) {
|
|
381
|
+
this._mapValueManager = _mapValueManager;
|
|
382
|
+
this._ngZone = _ngZone;
|
|
383
|
+
this._vcr = _vcr;
|
|
384
|
+
this._ngUnsubscribe = new Subject();
|
|
385
|
+
this._mapReadySubject = new BehaviorSubject(false);
|
|
386
|
+
this._featureContextMenu = null;
|
|
387
|
+
this._activeContextMenu = null;
|
|
388
|
+
this._allowDrawingHoleInPolygon = false;
|
|
389
|
+
this.mapReady$ = this._mapReadySubject.asObservable();
|
|
390
|
+
}
|
|
391
|
+
get mapReady() { return this._mapReadySubject.value; }
|
|
392
|
+
ngOnDestroy() {
|
|
393
|
+
this._ngUnsubscribe.next();
|
|
394
|
+
this._ngUnsubscribe.complete();
|
|
395
|
+
}
|
|
396
|
+
setMap(map) {
|
|
397
|
+
this.googleMap = map;
|
|
398
|
+
this._mapReadySubject.next(true);
|
|
399
|
+
this._initDrawingManager();
|
|
400
|
+
this._initFeatureStyling();
|
|
401
|
+
this._initFeatureChangeListeners();
|
|
402
|
+
}
|
|
403
|
+
setBaseLatLng(lat, lng) {
|
|
404
|
+
this._baseLatLng = { lat, lng };
|
|
405
|
+
}
|
|
406
|
+
// TODO: Refactor out of the service meant to just wrap the google maps api.
|
|
407
|
+
setFeatureContextMenu(menu) {
|
|
408
|
+
this._featureContextMenu = menu;
|
|
409
|
+
}
|
|
410
|
+
getDiv() {
|
|
411
|
+
this._assertInitialized();
|
|
412
|
+
return this.googleMap.getDiv();
|
|
413
|
+
}
|
|
414
|
+
fitBounds(bounds, padding) {
|
|
415
|
+
this._assertInitialized();
|
|
416
|
+
this.googleMap.fitBounds(bounds, padding);
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Iterates the map's features and removes any that are selected.
|
|
420
|
+
*/
|
|
421
|
+
deleteSelection() {
|
|
422
|
+
this._assertInitialized();
|
|
423
|
+
const mapData = this.googleMap.data;
|
|
424
|
+
mapData.forEach(f => {
|
|
425
|
+
if (isFeatureSelected(f)) {
|
|
426
|
+
mapData.remove(f);
|
|
427
|
+
}
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Stops the current drawing.
|
|
432
|
+
*/
|
|
433
|
+
stopDrawing() {
|
|
434
|
+
this._ngZone.runOutsideAngular(() => {
|
|
435
|
+
if (isNullOrUndefined(this._drawingManager) || this._drawingManager.getDrawingMode() === null) {
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
// Listening for the completion event of the overlay currently being drawn
|
|
439
|
+
// and removing it. I haven't found a way to cancel the current drawing,
|
|
440
|
+
// without this hack that assumes our listeners will not run into a race
|
|
441
|
+
// condition.
|
|
442
|
+
const listener = google.maps.event.addListener(this._drawingManager, 'overlaycomplete', (event) => {
|
|
443
|
+
event.overlay.setMap(null);
|
|
444
|
+
listener.remove();
|
|
445
|
+
});
|
|
446
|
+
// To fake canceling the current drawing, without disabling the drawing
|
|
447
|
+
// mode, the drawin mode is being unset then immediately set back. When
|
|
448
|
+
// the mode is unset the 'overlaycomplete' event will fire, which will
|
|
449
|
+
// give a reference to the current overlay to remove, then it is set back
|
|
450
|
+
// to the mode the user was using. To the user is should just seem like
|
|
451
|
+
// the current drawing was canceled and they can start drawing again.
|
|
452
|
+
const mode = this._drawingManager.getDrawingMode();
|
|
453
|
+
this._drawingManager.setDrawingMode(null);
|
|
454
|
+
this._drawingManager.setDrawingMode(mode);
|
|
455
|
+
// 'overlaycomplete' should fire immediately, unless an overlay hadn't
|
|
456
|
+
// started drawing. This timeout will make sure the listener gets removed.
|
|
457
|
+
setTimeout(() => {
|
|
458
|
+
listener.remove();
|
|
459
|
+
});
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
_initDrawingManager() {
|
|
463
|
+
if (notNullOrUndefined(this._drawingManager)) {
|
|
464
|
+
throw Error(`DrawingManager is already initialized.`);
|
|
465
|
+
}
|
|
466
|
+
this._assertInitialized();
|
|
467
|
+
const options = DEFAULT_DRAWING_MANAGER_OPTIONS();
|
|
468
|
+
const drawingManager = new google.maps.drawing.DrawingManager(options);
|
|
469
|
+
drawingManager.setMap(this.googleMap);
|
|
470
|
+
this._drawingManager = drawingManager;
|
|
471
|
+
this._drawingManager.addListener('drawingmode_changed', event => {
|
|
472
|
+
var _a;
|
|
473
|
+
if (((_a = this._drawingManager) === null || _a === void 0 ? void 0 : _a.getDrawingMode()) !== null) {
|
|
474
|
+
this._assertInitialized();
|
|
475
|
+
this.googleMap.data.forEach(f => {
|
|
476
|
+
if (isFeatureSelected(f)) {
|
|
477
|
+
setFeatureSelected(f, false);
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
addControl(element, position) {
|
|
484
|
+
this._assertInitialized();
|
|
485
|
+
this.googleMap.controls[position].push(element);
|
|
486
|
+
}
|
|
487
|
+
setData(data) {
|
|
488
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
489
|
+
this._assertInitialized();
|
|
490
|
+
removeAllFeatures(this.googleMap.data);
|
|
491
|
+
this.googleMap.data.addGeoJson(data);
|
|
492
|
+
this.googleMap.fitBounds(getBoundsWithAllFeatures(this.googleMap.data));
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
// TODO: Refactor out of the service meant to just wrap the google maps api.
|
|
496
|
+
reCenterOnFeatures() {
|
|
497
|
+
this._assertInitialized();
|
|
498
|
+
if (getFeaturesCount(this.googleMap.data) === 0) {
|
|
499
|
+
if (!this._baseLatLng) {
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
this.googleMap.panTo(this._baseLatLng);
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
this.googleMap.fitBounds(getBoundsWithAllFeatures(this.googleMap.data));
|
|
506
|
+
// TODO: Fix to pan to center. Currently fitBounds results in the expected
|
|
507
|
+
// result, but pantToBounds animates.
|
|
508
|
+
// this.googleMap.panToBounds(getBoundsWithAllFeatures(this.googleMap.data))
|
|
509
|
+
}
|
|
510
|
+
allowDrawingHoleInPolygon(allow) {
|
|
511
|
+
this._allowDrawingHoleInPolygon = allow;
|
|
512
|
+
}
|
|
513
|
+
setFileInputHandler(handler) {
|
|
514
|
+
this._fileInputHandler;
|
|
515
|
+
}
|
|
516
|
+
getFileInputHandler() {
|
|
517
|
+
return this._fileInputHandler;
|
|
518
|
+
}
|
|
519
|
+
_initFeatureStyling() {
|
|
520
|
+
this._assertInitialized();
|
|
521
|
+
// Disable any selection when clicking the map.
|
|
522
|
+
//
|
|
523
|
+
// TODO: There may be a better way to do this that would be more accurate or
|
|
524
|
+
// additional events that should be listened to, such as the disabling
|
|
525
|
+
// selection when the map looses focus.
|
|
526
|
+
this.googleMap.addListener('click', (even) => {
|
|
527
|
+
this._assertInitialized();
|
|
528
|
+
this.googleMap.data.forEach(f => setFeatureSelected(f, false));
|
|
529
|
+
});
|
|
530
|
+
// Determine what the style of the features are.
|
|
531
|
+
this.googleMap.data.setStyle((feature) => {
|
|
532
|
+
if (isFeatureSelected(feature)) {
|
|
533
|
+
return FEATURE_STYLE_OPTIONS_SELECTED;
|
|
534
|
+
}
|
|
535
|
+
return FEATURE_STYLE_OPTIONS_DEFAULT;
|
|
536
|
+
});
|
|
537
|
+
// Select a feature when clicked.
|
|
538
|
+
this.googleMap.data.addListener('click', (event) => {
|
|
539
|
+
this._assertInitialized();
|
|
540
|
+
setFeatureSelected(event.feature, true);
|
|
541
|
+
this.googleMap.data.forEach(f => {
|
|
542
|
+
if (f !== event.feature && isFeatureSelected(f)) {
|
|
543
|
+
setFeatureSelected(f, false);
|
|
544
|
+
}
|
|
545
|
+
});
|
|
546
|
+
});
|
|
547
|
+
// Set a style on hovered features that can be selected.
|
|
548
|
+
this.googleMap.data.addListener('mouseover', (event) => {
|
|
549
|
+
this._assertInitialized();
|
|
550
|
+
this.googleMap.data.revertStyle();
|
|
551
|
+
if (!this.isDrawing() && !isFeatureSelected(event.feature)) {
|
|
552
|
+
this.googleMap.data.overrideStyle(event.feature, { strokeWeight: 4 });
|
|
553
|
+
}
|
|
554
|
+
});
|
|
555
|
+
// Remove any hover styles when mouse moves away.
|
|
556
|
+
this.googleMap.data.addListener('mouseout', (event) => {
|
|
557
|
+
this._assertInitialized();
|
|
558
|
+
this.googleMap.data.revertStyle();
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
_initFeatureChangeListeners() {
|
|
562
|
+
this._assertInitialized();
|
|
563
|
+
createFeatureChangeObservable(this.googleMap.data, this._ngZone).pipe(switchMap(() => from(this.getGeoJson()).pipe(tap(geoJson => this._mapValueManager.setValue(geoJson, MapValueSource.FeatureChange)))), takeUntil(this._ngUnsubscribe)).subscribe();
|
|
564
|
+
this.googleMap.data.addListener('contextmenu', (event) => {
|
|
565
|
+
if (!isFeatureSelected(event.feature)) {
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
this._openContextMenuForFeature(event.feature, event.latLng);
|
|
569
|
+
});
|
|
570
|
+
if (notNullOrUndefined(this._drawingManager)) {
|
|
571
|
+
google.maps.event.addListener(this._drawingManager, 'polygoncomplete', (polygon) => {
|
|
572
|
+
var _a;
|
|
573
|
+
// The DrawingManager doesn't seem to have a way to access the overlays,
|
|
574
|
+
// so if the map is not set then it shouldn'y be considered a successful
|
|
575
|
+
// completion. I am canceling the active drawing by disabling drawing
|
|
576
|
+
// mode and setting the map null in the 'overlaycomplete' event, which
|
|
577
|
+
// fires before the 'polygoncomplete' event.
|
|
578
|
+
if (isNullOrUndefined(polygon.getMap())) {
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
this._assertInitialized();
|
|
582
|
+
// Create a map feature of the drawn polygon.
|
|
583
|
+
const feature = createDataFeatureFromPolygon(polygon);
|
|
584
|
+
// Remove the drawn polygon.
|
|
585
|
+
polygon.setMap(null);
|
|
586
|
+
// Stop drawing.
|
|
587
|
+
(_a = this._drawingManager) === null || _a === void 0 ? void 0 : _a.setDrawingMode(null);
|
|
588
|
+
// Check if the feature should be used as a cutout to an existing
|
|
589
|
+
// feature or added as it's own feature.
|
|
590
|
+
const exteriorPolygonFeature = this._allowDrawingHoleInPolygon
|
|
591
|
+
? getPossibleExteriorFeature(this.googleMap.data, feature)
|
|
592
|
+
: undefined;
|
|
593
|
+
if (exteriorPolygonFeature) {
|
|
594
|
+
addInnerFeatureCutoutToExteriorFeature(exteriorPolygonFeature, feature);
|
|
595
|
+
setFeatureSelected(exteriorPolygonFeature, true);
|
|
596
|
+
}
|
|
597
|
+
else {
|
|
598
|
+
this.googleMap.data.add(feature);
|
|
599
|
+
setFeatureSelected(feature, true);
|
|
600
|
+
}
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
isDrawing() {
|
|
605
|
+
if (isNullOrUndefined(this._drawingManager)) {
|
|
606
|
+
return true;
|
|
607
|
+
}
|
|
608
|
+
return this._drawingManager.getDrawingMode() !== null;
|
|
609
|
+
}
|
|
610
|
+
// TODO: Refactor out of the service meant to just wrap the google maps api.
|
|
611
|
+
hasSelectedFeature() {
|
|
612
|
+
this._assertInitialized();
|
|
613
|
+
let isSelected = false;
|
|
614
|
+
this.googleMap.data.forEach(f => {
|
|
615
|
+
if (isFeatureSelected(f)) {
|
|
616
|
+
isSelected = true;
|
|
617
|
+
}
|
|
618
|
+
});
|
|
619
|
+
return isSelected;
|
|
620
|
+
}
|
|
621
|
+
// TODO: Refactor out of the service meant to just wrap the google maps api.
|
|
622
|
+
getSelectedFeature() {
|
|
623
|
+
this._assertInitialized();
|
|
624
|
+
let feature = null;
|
|
625
|
+
this.googleMap.data.forEach(f => {
|
|
626
|
+
if (isFeatureSelected(f)) {
|
|
627
|
+
feature = f;
|
|
628
|
+
}
|
|
629
|
+
});
|
|
630
|
+
return feature;
|
|
631
|
+
}
|
|
632
|
+
// TODO: Refactor out of the service meant to just wrap the google maps api.
|
|
633
|
+
openContextMenu() {
|
|
634
|
+
const feature = this.getSelectedFeature();
|
|
635
|
+
if (feature) {
|
|
636
|
+
this._openContextMenuForFeature(feature);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
// TODO: Refactor out of the service meant to just wrap the google maps api.
|
|
640
|
+
_openContextMenuForFeature(feature, position) {
|
|
641
|
+
if (this._activeContextMenu) {
|
|
642
|
+
this._activeContextMenu.close();
|
|
643
|
+
this._activeContextMenu = null;
|
|
644
|
+
}
|
|
645
|
+
this._assertInitialized();
|
|
646
|
+
let _position = position;
|
|
647
|
+
if (!_position) {
|
|
648
|
+
_position = getFeatureCenter(feature);
|
|
649
|
+
}
|
|
650
|
+
if (this._featureContextMenu) {
|
|
651
|
+
this._activeContextMenu = new GoogleMapsContextMenu(this.googleMap, this._featureContextMenu, _position, this._vcr, this._ngZone, this.googleMap.data, feature);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
getGeoJson(removeAppProperties = true) {
|
|
655
|
+
return new Promise((resolve, reject) => {
|
|
656
|
+
this._assertInitialized();
|
|
657
|
+
this.googleMap.data.toGeoJson(f => {
|
|
658
|
+
if (removeAppProperties) {
|
|
659
|
+
stripAppFeaturePropertiesFromJson(f);
|
|
660
|
+
}
|
|
661
|
+
resolve(f);
|
|
662
|
+
});
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
/** Asserts that the map has been initialized. */
|
|
666
|
+
_assertInitialized() {
|
|
667
|
+
if (!this.googleMap && (typeof ngDevMode === 'undefined' || ngDevMode)) {
|
|
668
|
+
throw Error('Cannot access Google Map information before the API has been initialized. ' +
|
|
669
|
+
'Please wait for the API to load before trying to interact with it.');
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
GoogleMapsService.decorators = [
|
|
674
|
+
{ type: Injectable }
|
|
675
|
+
];
|
|
676
|
+
GoogleMapsService.ctorParameters = () => [
|
|
677
|
+
{ type: MapValueManagerService },
|
|
678
|
+
{ type: NgZone },
|
|
679
|
+
{ type: ViewContainerRef }
|
|
680
|
+
];
|
|
681
|
+
|
|
682
|
+
const MAP_CONTROLS_SERVICE = new InjectionToken('MAP_CONTROLS_SERVICE');
|
|
683
|
+
const MAP_CONTROL_DATA = new InjectionToken('MAP_CONTROL_DATA');
|
|
684
|
+
|
|
685
|
+
class MapControlRef {
|
|
686
|
+
constructor(_googleMaps, _componentFactoryResolver, _injector, _controlDef) {
|
|
687
|
+
var _a;
|
|
688
|
+
this._googleMaps = _googleMaps;
|
|
689
|
+
this._componentFactoryResolver = _componentFactoryResolver;
|
|
690
|
+
this._injector = _injector;
|
|
691
|
+
this._controlDef = _controlDef;
|
|
692
|
+
this._destroyedSubject = new Subject();
|
|
693
|
+
this.destroyed = this._destroyedSubject.asObservable();
|
|
694
|
+
const component = this._controlDef.component;
|
|
695
|
+
const factory = this._componentFactoryResolver.resolveComponentFactory(component);
|
|
696
|
+
const providers = [];
|
|
697
|
+
if (this._controlDef.data) {
|
|
698
|
+
providers.push({
|
|
699
|
+
provide: MAP_CONTROL_DATA,
|
|
700
|
+
useValue: this._controlDef.data
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
const injector = Injector.create({
|
|
704
|
+
providers,
|
|
705
|
+
parent: this._injector,
|
|
706
|
+
});
|
|
707
|
+
this._componentRef = factory.create(injector);
|
|
708
|
+
this._componentRef.changeDetectorRef.detectChanges();
|
|
709
|
+
const position = (_a = this._controlDef.position) !== null && _a !== void 0 ? _a : google.maps.ControlPosition.LEFT_BOTTOM;
|
|
710
|
+
this._googleMaps.addControl(this._componentRef.location.nativeElement, position);
|
|
711
|
+
this._addedAtPosition = position;
|
|
712
|
+
}
|
|
713
|
+
destroy() {
|
|
714
|
+
const googleMaps = this._googleMaps.googleMap;
|
|
715
|
+
if (googleMaps !== undefined) {
|
|
716
|
+
let idx = -1;
|
|
717
|
+
googleMaps.controls[this._addedAtPosition].forEach((elem, index) => {
|
|
718
|
+
if (elem === this._componentRef.location.nativeElement) {
|
|
719
|
+
idx = index;
|
|
720
|
+
}
|
|
721
|
+
});
|
|
722
|
+
if (idx === -1) {
|
|
723
|
+
throw Error(`Unable to destroy control. Control not found.`);
|
|
724
|
+
}
|
|
725
|
+
googleMaps.controls[this._addedAtPosition].removeAt(idx);
|
|
726
|
+
}
|
|
727
|
+
this._componentRef.destroy();
|
|
728
|
+
this._destroyedSubject.next();
|
|
729
|
+
this._destroyedSubject.complete();
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
class GoogleMapsControlsService {
|
|
733
|
+
constructor(_googleMaps, _componentFactoryResolver, _injector) {
|
|
734
|
+
this._googleMaps = _googleMaps;
|
|
735
|
+
this._componentFactoryResolver = _componentFactoryResolver;
|
|
736
|
+
this._injector = _injector;
|
|
737
|
+
}
|
|
738
|
+
add(control) {
|
|
739
|
+
return new MapControlRef(this._googleMaps, this._componentFactoryResolver, this._injector, control);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
GoogleMapsControlsService.decorators = [
|
|
743
|
+
{ type: Injectable }
|
|
744
|
+
];
|
|
745
|
+
GoogleMapsControlsService.ctorParameters = () => [
|
|
746
|
+
{ type: GoogleMapsService },
|
|
747
|
+
{ type: ComponentFactoryResolver },
|
|
748
|
+
{ type: Injector }
|
|
749
|
+
];
|
|
750
|
+
|
|
751
|
+
/**
|
|
752
|
+
*
|
|
753
|
+
*/
|
|
754
|
+
class TheSeamGoogleMapsRecenterButtonControlComponent {
|
|
755
|
+
constructor(_googleMaps, _data) {
|
|
756
|
+
this._googleMaps = _googleMaps;
|
|
757
|
+
this._ngUnsubscribe = new Subject();
|
|
758
|
+
this._listeners = [];
|
|
759
|
+
if (_data) {
|
|
760
|
+
if (_data.hasOwnProperty('label')) {
|
|
761
|
+
this.label = _data.label;
|
|
762
|
+
}
|
|
763
|
+
if (_data.hasOwnProperty('icon')) {
|
|
764
|
+
this.icon = _data.icon;
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
_onClick(event) {
|
|
769
|
+
this._googleMaps.reCenterOnFeatures();
|
|
770
|
+
}
|
|
771
|
+
/** @ignore */
|
|
772
|
+
ngOnDestroy() {
|
|
773
|
+
this._listeners.forEach(l => l());
|
|
774
|
+
this._ngUnsubscribe.next();
|
|
775
|
+
this._ngUnsubscribe.complete();
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
TheSeamGoogleMapsRecenterButtonControlComponent.decorators = [
|
|
779
|
+
{ type: Component, args: [{
|
|
780
|
+
// tslint:disable-next-line: component-selector
|
|
781
|
+
selector: 'button[seam-google-maps-recenter-button-control]',
|
|
782
|
+
template: "<seam-icon [icon]=\"icon\" iconType=\"image-fill\"></seam-icon>\n",
|
|
783
|
+
host: {
|
|
784
|
+
'[attr.draggable]': 'false',
|
|
785
|
+
'[attr.aria-label]': 'label',
|
|
786
|
+
'[attr.title]': 'label',
|
|
787
|
+
'type': 'button',
|
|
788
|
+
'class': 'gmnoprint gm-control-active'
|
|
789
|
+
},
|
|
790
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
791
|
+
styles: [":host{background:none white;border:0px;margin:10px;padding:11px;text-transform:none;-webkit-appearance:none;-moz-appearance:none;appearance:none;position:absolute;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;border-radius:2px;height:40px;width:40px;box-shadow:#0000004d 0 1px 4px -1px;overflow:hidden}\n"]
|
|
792
|
+
},] }
|
|
793
|
+
];
|
|
794
|
+
TheSeamGoogleMapsRecenterButtonControlComponent.ctorParameters = () => [
|
|
795
|
+
{ type: GoogleMapsService },
|
|
796
|
+
{ type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [MAP_CONTROL_DATA,] }] }
|
|
797
|
+
];
|
|
798
|
+
TheSeamGoogleMapsRecenterButtonControlComponent.propDecorators = {
|
|
799
|
+
label: [{ type: Input }],
|
|
800
|
+
icon: [{ type: Input }],
|
|
801
|
+
_onClick: [{ type: HostListener, args: ['click', ['event'],] }]
|
|
802
|
+
};
|
|
803
|
+
|
|
804
|
+
/**
|
|
805
|
+
*
|
|
806
|
+
*/
|
|
807
|
+
class TheSeamGoogleMapsUploadButtonControlComponent {
|
|
808
|
+
constructor(_elementRef, _mapValueManager, _renderer, _googleMaps, _data) {
|
|
809
|
+
this._elementRef = _elementRef;
|
|
810
|
+
this._mapValueManager = _mapValueManager;
|
|
811
|
+
this._renderer = _renderer;
|
|
812
|
+
this._googleMaps = _googleMaps;
|
|
813
|
+
this._ngUnsubscribe = new Subject();
|
|
814
|
+
this._listeners = [];
|
|
815
|
+
if (_data) {
|
|
816
|
+
if (_data.hasOwnProperty('label')) {
|
|
817
|
+
this.label = _data.label;
|
|
818
|
+
}
|
|
819
|
+
if (_data.hasOwnProperty('icon')) {
|
|
820
|
+
this.icon = _data.icon;
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
this._fileInputElement = this._createHiddenInput();
|
|
824
|
+
this._renderer.appendChild(this._elementRef.nativeElement, this._fileInputElement);
|
|
825
|
+
}
|
|
826
|
+
_onClick(event) {
|
|
827
|
+
this._fileInputElement.click();
|
|
828
|
+
}
|
|
829
|
+
/** @ignore */
|
|
830
|
+
ngOnDestroy() {
|
|
831
|
+
this._listeners.forEach(l => l());
|
|
832
|
+
this._ngUnsubscribe.next();
|
|
833
|
+
this._ngUnsubscribe.complete();
|
|
834
|
+
}
|
|
835
|
+
_getFile(throwIfInvalidFiles = true) {
|
|
836
|
+
const files = this._fileInputElement.files;
|
|
837
|
+
if (files === null || files.length === 0) {
|
|
838
|
+
return null;
|
|
839
|
+
}
|
|
840
|
+
if (throwIfInvalidFiles) {
|
|
841
|
+
if (files.length > 1) {
|
|
842
|
+
throw Error(`Only one file can be imported at a time.`);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
return files[0];
|
|
846
|
+
}
|
|
847
|
+
_importFile(file) {
|
|
848
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
849
|
+
const json = yield readGeoFile(file);
|
|
850
|
+
this._mapValueManager.setValue(json, MapValueSource.Input);
|
|
851
|
+
this._resetInput();
|
|
852
|
+
});
|
|
853
|
+
}
|
|
854
|
+
_createHiddenInput() {
|
|
855
|
+
const fileInputElement = this._renderer.createElement('input');
|
|
856
|
+
this._renderer.setAttribute(fileInputElement, 'type', 'file');
|
|
857
|
+
this._renderer.setAttribute(fileInputElement, 'hidden', '');
|
|
858
|
+
this._renderer.setAttribute(fileInputElement, 'accept', '.json,.geojson,.shp,.zip');
|
|
859
|
+
this._listeners.push(this._renderer.listen(fileInputElement, 'change', (event) => {
|
|
860
|
+
const file = this._getFile();
|
|
861
|
+
if (file === null) {
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
864
|
+
const fileImportHandler = this._googleMaps.getFileInputHandler();
|
|
865
|
+
if (fileImportHandler) {
|
|
866
|
+
fileImportHandler(file);
|
|
867
|
+
}
|
|
868
|
+
else {
|
|
869
|
+
this._importFile(file);
|
|
870
|
+
}
|
|
871
|
+
}));
|
|
872
|
+
return fileInputElement;
|
|
873
|
+
}
|
|
874
|
+
_createTemporaryFormElement() {
|
|
875
|
+
return this._renderer.createElement('form');
|
|
876
|
+
}
|
|
877
|
+
/**
|
|
878
|
+
* Reset input element, so that the same file can be added again.
|
|
879
|
+
*/
|
|
880
|
+
_resetInput() {
|
|
881
|
+
const formElement = this._createTemporaryFormElement();
|
|
882
|
+
this._renderer.appendChild(formElement, this._fileInputElement);
|
|
883
|
+
formElement.reset();
|
|
884
|
+
this._renderer.appendChild(this._elementRef.nativeElement, this._fileInputElement);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
TheSeamGoogleMapsUploadButtonControlComponent.decorators = [
|
|
888
|
+
{ type: Component, args: [{
|
|
889
|
+
// tslint:disable-next-line: component-selector
|
|
890
|
+
selector: 'button[seam-google-maps-upload-button-control]',
|
|
891
|
+
template: "<seam-icon [icon]=\"icon\" iconType=\"image-fill\"></seam-icon>\n",
|
|
892
|
+
host: {
|
|
893
|
+
'[attr.draggable]': 'false',
|
|
894
|
+
'[attr.aria-label]': 'label',
|
|
895
|
+
'[attr.title]': 'label',
|
|
896
|
+
'type': 'button',
|
|
897
|
+
'class': 'gmnoprint gm-control-active'
|
|
898
|
+
},
|
|
899
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
900
|
+
styles: [":host{background:none white;border:0px;margin:10px;padding:11px;text-transform:none;-webkit-appearance:none;-moz-appearance:none;appearance:none;position:absolute;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;border-radius:2px;height:40px;width:40px;box-shadow:#0000004d 0 1px 4px -1px;overflow:hidden}\n"]
|
|
901
|
+
},] }
|
|
902
|
+
];
|
|
903
|
+
TheSeamGoogleMapsUploadButtonControlComponent.ctorParameters = () => [
|
|
904
|
+
{ type: ElementRef },
|
|
905
|
+
{ type: MapValueManagerService },
|
|
906
|
+
{ type: Renderer2 },
|
|
907
|
+
{ type: GoogleMapsService },
|
|
908
|
+
{ type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [MAP_CONTROL_DATA,] }] }
|
|
909
|
+
];
|
|
910
|
+
TheSeamGoogleMapsUploadButtonControlComponent.propDecorators = {
|
|
911
|
+
label: [{ type: Input }],
|
|
912
|
+
icon: [{ type: Input }],
|
|
913
|
+
_onClick: [{ type: HostListener, args: ['click', ['event'],] }]
|
|
914
|
+
};
|
|
915
|
+
|
|
916
|
+
class TheSeamGoogleMapsComponentBase {
|
|
917
|
+
constructor(_elementRef) {
|
|
918
|
+
this._elementRef = _elementRef;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
const _TheSeamGoogleMapsMixinBase = mixinDisabled(TheSeamGoogleMapsComponentBase);
|
|
922
|
+
/**
|
|
923
|
+
* A wrapper for googlemap.
|
|
924
|
+
*/
|
|
925
|
+
class TheSeamGoogleMapsComponent extends _TheSeamGoogleMapsMixinBase {
|
|
926
|
+
constructor(elementRef, _focusMonitor, _googleMaps, _mapValueManager) {
|
|
927
|
+
super(elementRef);
|
|
928
|
+
this._focusMonitor = _focusMonitor;
|
|
929
|
+
this._googleMaps = _googleMaps;
|
|
930
|
+
this._mapValueManager = _mapValueManager;
|
|
931
|
+
this._ngUnsubscribe = new Subject();
|
|
932
|
+
this._fileUploadControlDef = {
|
|
933
|
+
component: TheSeamGoogleMapsUploadButtonControlComponent,
|
|
934
|
+
data: { label: 'Import Geo File', icon: faFileImport },
|
|
935
|
+
position: 6 /* google.maps.ControlPosition.LEFT_BOTTOM */,
|
|
936
|
+
};
|
|
937
|
+
this._reCenterControlDef = {
|
|
938
|
+
component: TheSeamGoogleMapsRecenterButtonControlComponent,
|
|
939
|
+
data: { label: 'Center on Field', icon: faCrosshairs },
|
|
940
|
+
position: 9 /* google.maps.ControlPosition.RIGHT_BOTTOM */,
|
|
941
|
+
};
|
|
942
|
+
this._focusOrigin = null;
|
|
943
|
+
/**
|
|
944
|
+
* Set the tab index to `-1` to allow the root element of the
|
|
945
|
+
* component to receive `focus` event from javascript, but not get focused by
|
|
946
|
+
* keyboard navigation.
|
|
947
|
+
*/
|
|
948
|
+
this._tabIndex = -1;
|
|
949
|
+
this.fileDropEnabled = true;
|
|
950
|
+
this.fileUploadControlEnabled = false;
|
|
951
|
+
this.fullscreenControlEnabled = true;
|
|
952
|
+
this.reCenterControlEnabled = true;
|
|
953
|
+
this.mapTypeControlEnabled = true;
|
|
954
|
+
this.streetViewControlEnabled = false;
|
|
955
|
+
this.allowDrawingHoleInPolygon = false;
|
|
956
|
+
this.zoom = 14;
|
|
957
|
+
this.longitude = -98.570209;
|
|
958
|
+
this.latitude = 37.633814;
|
|
959
|
+
this.mapReady = new EventEmitter();
|
|
960
|
+
this._focusMonitor.monitor(this._elementRef, true).pipe(tap(origin => this._focusOrigin = origin), takeUntil(this._ngUnsubscribe)).subscribe();
|
|
961
|
+
this._mapValueManager.valueChanged.pipe(tap(change => {
|
|
962
|
+
if (this.onChange) {
|
|
963
|
+
this.onChange(change.value);
|
|
964
|
+
}
|
|
965
|
+
if (this.onTouched) {
|
|
966
|
+
this.onTouched();
|
|
967
|
+
}
|
|
968
|
+
}), tap(changed => {
|
|
969
|
+
if (this._googleMaps.mapReady && changed.source !== MapValueSource.FeatureChange) {
|
|
970
|
+
this._googleMaps.setData(changed.value);
|
|
971
|
+
}
|
|
972
|
+
}), takeUntil(this._ngUnsubscribe)).subscribe();
|
|
973
|
+
this._googleMaps.setBaseLatLng(this.latitude, this.longitude);
|
|
974
|
+
}
|
|
975
|
+
set value(value) {
|
|
976
|
+
this._mapValueManager.setValue(value, MapValueSource.Input);
|
|
977
|
+
}
|
|
978
|
+
get value() {
|
|
979
|
+
return this._mapValueManager.value;
|
|
980
|
+
}
|
|
981
|
+
set tabIndex(value) { this._tabIndex = coerceNumberProperty(value); }
|
|
982
|
+
get tabIndex() { return this._tabIndex; }
|
|
983
|
+
set fileImportHandler(value) {
|
|
984
|
+
this._googleMaps.setFileInputHandler(value);
|
|
985
|
+
}
|
|
986
|
+
get _attrDisabled() { return this.disabled || null; }
|
|
987
|
+
get _attrTabIndex() { return this.disabled ? -1 : (this.tabIndex || 0); }
|
|
988
|
+
/** @ignore */
|
|
989
|
+
ngOnInit() {
|
|
990
|
+
this._googleMaps.setFeatureContextMenu(this.featureContextMenu);
|
|
991
|
+
fromEvent(window, 'keydown').pipe(tap((event) => {
|
|
992
|
+
switch (event.code) {
|
|
993
|
+
case 'Delete':
|
|
994
|
+
this._googleMaps.deleteSelection();
|
|
995
|
+
event.preventDefault();
|
|
996
|
+
event.stopPropagation();
|
|
997
|
+
break;
|
|
998
|
+
case 'Escape':
|
|
999
|
+
this._googleMaps.stopDrawing();
|
|
1000
|
+
event.preventDefault();
|
|
1001
|
+
event.stopPropagation();
|
|
1002
|
+
break;
|
|
1003
|
+
case 'ContextMenu':
|
|
1004
|
+
this._googleMaps.openContextMenu();
|
|
1005
|
+
event.preventDefault();
|
|
1006
|
+
event.stopPropagation();
|
|
1007
|
+
break;
|
|
1008
|
+
}
|
|
1009
|
+
}), takeUntil(this._ngUnsubscribe)).subscribe();
|
|
1010
|
+
}
|
|
1011
|
+
/** @ignore */
|
|
1012
|
+
ngOnDestroy() {
|
|
1013
|
+
this._focusMonitor.stopMonitoring(this._elementRef);
|
|
1014
|
+
this._ngUnsubscribe.next();
|
|
1015
|
+
this._ngUnsubscribe.complete();
|
|
1016
|
+
}
|
|
1017
|
+
/** @ignore */
|
|
1018
|
+
ngAfterViewInit() { }
|
|
1019
|
+
ngOnChanges(changes) {
|
|
1020
|
+
let updateBase = false;
|
|
1021
|
+
if (changes.hasOwnProperty('latitude')) {
|
|
1022
|
+
this.latitude = changes['latitude'].currentValue;
|
|
1023
|
+
updateBase = true;
|
|
1024
|
+
}
|
|
1025
|
+
if (changes.hasOwnProperty('longitude')) {
|
|
1026
|
+
this.longitude = changes['longitude'].currentValue;
|
|
1027
|
+
updateBase = true;
|
|
1028
|
+
}
|
|
1029
|
+
if (updateBase) {
|
|
1030
|
+
this._googleMaps.setBaseLatLng(this.latitude, this.longitude);
|
|
1031
|
+
}
|
|
1032
|
+
if (changes.hasOwnProperty('allowDrawingHoleInPolygon')) {
|
|
1033
|
+
this._googleMaps.allowDrawingHoleInPolygon(this.allowDrawingHoleInPolygon);
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
writeValue(value) {
|
|
1037
|
+
this.value = value;
|
|
1038
|
+
}
|
|
1039
|
+
registerOnChange(fn) {
|
|
1040
|
+
this.onChange = fn;
|
|
1041
|
+
}
|
|
1042
|
+
registerOnTouched(fn) {
|
|
1043
|
+
this.onTouched = fn;
|
|
1044
|
+
}
|
|
1045
|
+
setDisabledState(isDisabled) {
|
|
1046
|
+
this.disabled = isDisabled;
|
|
1047
|
+
}
|
|
1048
|
+
fitBounds(bounds, padding) {
|
|
1049
|
+
this._googleMaps.fitBounds(bounds, padding);
|
|
1050
|
+
}
|
|
1051
|
+
getGeoJson() {
|
|
1052
|
+
return this._googleMaps.getGeoJson();
|
|
1053
|
+
}
|
|
1054
|
+
hasFocus() {
|
|
1055
|
+
return this._focusOrigin !== null && this._focusOrigin !== undefined;
|
|
1056
|
+
}
|
|
1057
|
+
/** Focuses the button. */
|
|
1058
|
+
focus() {
|
|
1059
|
+
this._getHostElement().focus();
|
|
1060
|
+
}
|
|
1061
|
+
_getHostElement() {
|
|
1062
|
+
return this._elementRef.nativeElement;
|
|
1063
|
+
}
|
|
1064
|
+
_onMapReady(theMap) {
|
|
1065
|
+
var _a;
|
|
1066
|
+
this._googleMaps.setMap(theMap);
|
|
1067
|
+
this._googleMaps.setData(this._mapValueManager.value);
|
|
1068
|
+
// NOTE: AgmMap has a race condition problem that causes the input latitude,
|
|
1069
|
+
// longitude, and zoom to get ignored, before googlemaps emits
|
|
1070
|
+
// 'center_changed'. This should avoid the issue, until we stop using AgmMap
|
|
1071
|
+
// or upgrade to a more recent version that may not have the issue anymore.
|
|
1072
|
+
this._googleMaps.reCenterOnFeatures();
|
|
1073
|
+
(_a = this._googleMaps.googleMap) === null || _a === void 0 ? void 0 : _a.setZoom(this.zoom);
|
|
1074
|
+
}
|
|
1075
|
+
_onClickDeleteFeature() {
|
|
1076
|
+
this._googleMaps.deleteSelection();
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
TheSeamGoogleMapsComponent.decorators = [
|
|
1080
|
+
{ type: Component, args: [{
|
|
1081
|
+
selector: 'seam-google-maps',
|
|
1082
|
+
template: "<agm-map\n [latitude]=\"latitude\"\n [longitude]=\"longitude\"\n [zoom]=\"zoom\"\n [mapTypeControl]=\"true\"\n mapTypeId=\"hybrid\"\n [streetViewControl]=\"false\"\n (mapReady)=\"_onMapReady($event)\"\n [fullscreenControl]=\"$any(fullscreenControlEnabled)\">\n</agm-map>\n<seam-menu #featureContextMenu>\n <button seamMenuItem (click)=\"_onClickDeleteFeature()\">Delete</button>\n</seam-menu>\n<seam-map-file-drop *ngIf=\"fileDropEnabled\"></seam-map-file-drop>\n\n<seam-map-control *ngIf=\"fileUploadControlEnabled\" [def]=\"_fileUploadControlDef\"></seam-map-control>\n<seam-map-control *ngIf=\"reCenterControlEnabled\" [def]=\"_reCenterControlDef\"></seam-map-control>\n",
|
|
1083
|
+
inputs: [
|
|
1084
|
+
'disabled'
|
|
1085
|
+
],
|
|
1086
|
+
providers: [
|
|
1087
|
+
MapValueManagerService,
|
|
1088
|
+
GoogleMapsService,
|
|
1089
|
+
{ provide: MAP_CONTROLS_SERVICE, useClass: GoogleMapsControlsService },
|
|
1090
|
+
{
|
|
1091
|
+
provide: NG_VALUE_ACCESSOR,
|
|
1092
|
+
// tslint:disable-next-line: no-use-before-declare
|
|
1093
|
+
useExisting: forwardRef(() => TheSeamGoogleMapsComponent),
|
|
1094
|
+
multi: true
|
|
1095
|
+
}
|
|
1096
|
+
],
|
|
1097
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
1098
|
+
exportAs: 'seamGoogleMaps',
|
|
1099
|
+
styles: [":host{display:block;position:relative;height:400px;width:100%}:host.show-focus-border.cdk-focused{outline:0;box-shadow:0 0 0 .2rem #357ebd40}:host.show-focus-border[disabled]{opacity:.65}agm-map{height:inherit;width:inherit}\n"]
|
|
1100
|
+
},] }
|
|
1101
|
+
];
|
|
1102
|
+
TheSeamGoogleMapsComponent.ctorParameters = () => [
|
|
1103
|
+
{ type: ElementRef },
|
|
1104
|
+
{ type: FocusMonitor },
|
|
1105
|
+
{ type: GoogleMapsService },
|
|
1106
|
+
{ type: MapValueManagerService }
|
|
1107
|
+
];
|
|
1108
|
+
TheSeamGoogleMapsComponent.propDecorators = {
|
|
1109
|
+
value: [{ type: Input }],
|
|
1110
|
+
tabIndex: [{ type: Input }],
|
|
1111
|
+
fileDropEnabled: [{ type: Input }],
|
|
1112
|
+
fileUploadControlEnabled: [{ type: Input }],
|
|
1113
|
+
fullscreenControlEnabled: [{ type: Input }],
|
|
1114
|
+
reCenterControlEnabled: [{ type: Input }],
|
|
1115
|
+
mapTypeControlEnabled: [{ type: Input }],
|
|
1116
|
+
streetViewControlEnabled: [{ type: Input }],
|
|
1117
|
+
allowDrawingHoleInPolygon: [{ type: Input }],
|
|
1118
|
+
fileImportHandler: [{ type: Input }],
|
|
1119
|
+
_attrDisabled: [{ type: HostBinding, args: ['attr.disabled',] }],
|
|
1120
|
+
_attrTabIndex: [{ type: HostBinding, args: ['attr.tabindex',] }],
|
|
1121
|
+
zoom: [{ type: Input }],
|
|
1122
|
+
longitude: [{ type: Input }],
|
|
1123
|
+
latitude: [{ type: Input }],
|
|
1124
|
+
mapReady: [{ type: Output }],
|
|
1125
|
+
agmMap: [{ type: ViewChild, args: [AgmMap, { static: true },] }],
|
|
1126
|
+
featureContextMenu: [{ type: ViewChild, args: ['featureContextMenu', { static: true, read: MenuComponent },] }],
|
|
1127
|
+
agmMapTpl: [{ type: ViewChild, args: [AgmMap, { static: true, read: ElementRef },] }]
|
|
1128
|
+
};
|
|
1129
|
+
__decorate([
|
|
1130
|
+
InputBoolean()
|
|
1131
|
+
], TheSeamGoogleMapsComponent.prototype, "fileDropEnabled", void 0);
|
|
1132
|
+
__decorate([
|
|
1133
|
+
InputBoolean()
|
|
1134
|
+
], TheSeamGoogleMapsComponent.prototype, "fileUploadControlEnabled", void 0);
|
|
1135
|
+
__decorate([
|
|
1136
|
+
InputBoolean()
|
|
1137
|
+
], TheSeamGoogleMapsComponent.prototype, "fullscreenControlEnabled", void 0);
|
|
1138
|
+
__decorate([
|
|
1139
|
+
InputBoolean()
|
|
1140
|
+
], TheSeamGoogleMapsComponent.prototype, "reCenterControlEnabled", void 0);
|
|
1141
|
+
__decorate([
|
|
1142
|
+
InputBoolean()
|
|
1143
|
+
], TheSeamGoogleMapsComponent.prototype, "mapTypeControlEnabled", void 0);
|
|
1144
|
+
__decorate([
|
|
1145
|
+
InputBoolean()
|
|
1146
|
+
], TheSeamGoogleMapsComponent.prototype, "streetViewControlEnabled", void 0);
|
|
1147
|
+
__decorate([
|
|
1148
|
+
InputBoolean()
|
|
1149
|
+
], TheSeamGoogleMapsComponent.prototype, "allowDrawingHoleInPolygon", void 0);
|
|
1150
|
+
__decorate([
|
|
1151
|
+
InputNumber()
|
|
1152
|
+
], TheSeamGoogleMapsComponent.prototype, "zoom", void 0);
|
|
1153
|
+
__decorate([
|
|
1154
|
+
InputNumber()
|
|
1155
|
+
], TheSeamGoogleMapsComponent.prototype, "longitude", void 0);
|
|
1156
|
+
__decorate([
|
|
1157
|
+
InputNumber()
|
|
1158
|
+
], TheSeamGoogleMapsComponent.prototype, "latitude", void 0);
|
|
1159
|
+
|
|
1160
|
+
const SEAM_GOOGLE_PLACES_AUTOCOMPLETE_DEFAULT_OPTIONS = {
|
|
1161
|
+
componentRestrictions: { country: 'US' }
|
|
1162
|
+
};
|
|
1163
|
+
class TheSeamGoogleMapsPlacesAutocompleteDirective {
|
|
1164
|
+
constructor(_elementRef, _ngZone) {
|
|
1165
|
+
this._elementRef = _elementRef;
|
|
1166
|
+
this._ngZone = _ngZone;
|
|
1167
|
+
this._autoCompleteReadySubject = new Subject();
|
|
1168
|
+
this._ngUnsubscribe = new Subject();
|
|
1169
|
+
this._placeChangedPending = [];
|
|
1170
|
+
this._listeners = [];
|
|
1171
|
+
this._options = SEAM_GOOGLE_PLACES_AUTOCOMPLETE_DEFAULT_OPTIONS;
|
|
1172
|
+
this._attrType = 'text';
|
|
1173
|
+
this.placeChanged = this._autoCompleteReadySubject.pipe(startWith(undefined), switchMap(() => this._createPlaceChangedObservable()));
|
|
1174
|
+
}
|
|
1175
|
+
set options(value) {
|
|
1176
|
+
this._options = value || SEAM_GOOGLE_PLACES_AUTOCOMPLETE_DEFAULT_OPTIONS;
|
|
1177
|
+
}
|
|
1178
|
+
ngOnInit() {
|
|
1179
|
+
this._ngZone.runOutsideAngular(() => {
|
|
1180
|
+
this._untilGoogleMapsApiLoaded().pipe(takeUntil(this._ngUnsubscribe)).subscribe(() => {
|
|
1181
|
+
this.autoComplete = new google.maps.places.Autocomplete(this.getHostElement(), this._options);
|
|
1182
|
+
this._placeChangedPending.forEach(pending => pending.observable.subscribe(pending.subscriber));
|
|
1183
|
+
this._autoCompleteReadySubject.next();
|
|
1184
|
+
});
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1187
|
+
ngOnDestroy() {
|
|
1188
|
+
this._listeners.forEach(l => l.remove());
|
|
1189
|
+
this._listeners = [];
|
|
1190
|
+
this._ngUnsubscribe.next();
|
|
1191
|
+
this._ngUnsubscribe.complete();
|
|
1192
|
+
}
|
|
1193
|
+
ngOnChanges(changes) {
|
|
1194
|
+
if (this.autoComplete && changes['options']) {
|
|
1195
|
+
this.autoComplete.setOptions(this._options);
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
_untilGoogleMapsApiLoaded() {
|
|
1199
|
+
return interval(500).pipe(filter(() => !!(window.google && window.google.maps && window.google.maps.version)), take(1), mapTo(undefined));
|
|
1200
|
+
}
|
|
1201
|
+
/**
|
|
1202
|
+
* Returns the bounds to which predictions are biased.
|
|
1203
|
+
*/
|
|
1204
|
+
getBounds() {
|
|
1205
|
+
this._assertInitialized();
|
|
1206
|
+
return this.autoComplete.getBounds();
|
|
1207
|
+
}
|
|
1208
|
+
/**
|
|
1209
|
+
* Returns the fields to be included for the Place in the details response
|
|
1210
|
+
* when the details are successfully retrieved. For a list of fields see
|
|
1211
|
+
* [PlaceResult](https://developers.google.com/maps/documentation/javascript/reference/places-service#PlaceResult).
|
|
1212
|
+
*/
|
|
1213
|
+
getFields() {
|
|
1214
|
+
this._assertInitialized();
|
|
1215
|
+
return this.autoComplete.getFields();
|
|
1216
|
+
}
|
|
1217
|
+
/**
|
|
1218
|
+
* Returns the details of the Place selected by user if the details were
|
|
1219
|
+
* successfully retrieved. Otherwise returns a stub Place object, with the
|
|
1220
|
+
* name property set to the current value of the input field.
|
|
1221
|
+
*/
|
|
1222
|
+
getPlace() {
|
|
1223
|
+
this._assertInitialized();
|
|
1224
|
+
return this.autoComplete.getPlace();
|
|
1225
|
+
}
|
|
1226
|
+
/**
|
|
1227
|
+
* Sets the preferred area within which to return Place results. Results are
|
|
1228
|
+
* biased towards, but not restricted to, this area.
|
|
1229
|
+
*/
|
|
1230
|
+
setBounds(bounds) {
|
|
1231
|
+
this._assertInitialized();
|
|
1232
|
+
// tslint:disable-next-line: no-non-null-assertion
|
|
1233
|
+
return this.autoComplete.setBounds(bounds);
|
|
1234
|
+
}
|
|
1235
|
+
/**
|
|
1236
|
+
* Sets the component restrictions. Component restrictions are used to
|
|
1237
|
+
* restrict predictions to only those within the parent component. For
|
|
1238
|
+
* example, the country.
|
|
1239
|
+
*/
|
|
1240
|
+
setComponentRestrictions(restrictions) {
|
|
1241
|
+
this._assertInitialized();
|
|
1242
|
+
// tslint:disable-next-line: no-non-null-assertion
|
|
1243
|
+
return this.autoComplete.setComponentRestrictions(restrictions);
|
|
1244
|
+
}
|
|
1245
|
+
/**
|
|
1246
|
+
* Sets the fields to be included for the Place in the details response when
|
|
1247
|
+
* the details are successfully retrieved. For a list of fields see
|
|
1248
|
+
* [PlaceResult](https://developers.google.com/maps/documentation/javascript/reference/places-service#PlaceResult).
|
|
1249
|
+
*/
|
|
1250
|
+
setFields(fields) {
|
|
1251
|
+
this._assertInitialized();
|
|
1252
|
+
// tslint:disable-next-line: no-non-null-assertion
|
|
1253
|
+
return this.autoComplete.setFields(fields);
|
|
1254
|
+
}
|
|
1255
|
+
/** */
|
|
1256
|
+
setOptions(options) {
|
|
1257
|
+
this._assertInitialized();
|
|
1258
|
+
// tslint:disable-next-line: no-non-null-assertion
|
|
1259
|
+
return this.autoComplete.setOptions(options);
|
|
1260
|
+
}
|
|
1261
|
+
/**
|
|
1262
|
+
* Sets the types of predictions to be returned. For supported types, see the
|
|
1263
|
+
* [developer's guide](https://developers.google.com/maps/documentation/javascript/places-autocomplete#constrain-place-types).
|
|
1264
|
+
* If no types are specified, all types will be returned.
|
|
1265
|
+
*/
|
|
1266
|
+
setTypes(types) {
|
|
1267
|
+
this._assertInitialized();
|
|
1268
|
+
// tslint:disable-next-line: no-non-null-assertion
|
|
1269
|
+
return this.autoComplete.setTypes(types);
|
|
1270
|
+
}
|
|
1271
|
+
/** Focuses the input. */
|
|
1272
|
+
focus() {
|
|
1273
|
+
this._elementRef.nativeElement.focus();
|
|
1274
|
+
}
|
|
1275
|
+
/** Unfocuses the input. */
|
|
1276
|
+
blur() {
|
|
1277
|
+
this._elementRef.nativeElement.blur();
|
|
1278
|
+
}
|
|
1279
|
+
getHostElement() {
|
|
1280
|
+
return this._elementRef.nativeElement;
|
|
1281
|
+
}
|
|
1282
|
+
_createPlaceChangedObservable() {
|
|
1283
|
+
const observable = new Observable(subscriber => {
|
|
1284
|
+
if (!this.autoComplete) {
|
|
1285
|
+
this._placeChangedPending.push({ observable, subscriber });
|
|
1286
|
+
return undefined;
|
|
1287
|
+
}
|
|
1288
|
+
const listener = this.autoComplete.addListener('place_changed', (event) => {
|
|
1289
|
+
this._ngZone.run(() => subscriber.next(event));
|
|
1290
|
+
});
|
|
1291
|
+
this._listeners.push(listener);
|
|
1292
|
+
return () => listener.remove();
|
|
1293
|
+
});
|
|
1294
|
+
return observable;
|
|
1295
|
+
}
|
|
1296
|
+
/** Asserts that the map has been initialized. */
|
|
1297
|
+
_assertInitialized() {
|
|
1298
|
+
if (!this.autoComplete && (typeof ngDevMode === 'undefined' || ngDevMode)) {
|
|
1299
|
+
throw Error('Cannot access Google Map Places information before the API has been initialized. ' +
|
|
1300
|
+
'Please wait for the API to load before trying to interact with it.');
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
TheSeamGoogleMapsPlacesAutocompleteDirective.decorators = [
|
|
1305
|
+
{ type: Directive, args: [{
|
|
1306
|
+
selector: 'input[seamGoogleMapsPlacesAutocomplete]',
|
|
1307
|
+
exportAs: 'seamGoogleMapsPlacesAutocomplete'
|
|
1308
|
+
},] }
|
|
1309
|
+
];
|
|
1310
|
+
TheSeamGoogleMapsPlacesAutocompleteDirective.ctorParameters = () => [
|
|
1311
|
+
{ type: ElementRef },
|
|
1312
|
+
{ type: NgZone }
|
|
1313
|
+
];
|
|
1314
|
+
TheSeamGoogleMapsPlacesAutocompleteDirective.propDecorators = {
|
|
1315
|
+
options: [{ type: Input }],
|
|
1316
|
+
placeChanged: [{ type: Output }],
|
|
1317
|
+
_attrType: [{ type: HostBinding, args: ['attr.type',] }]
|
|
1318
|
+
};
|
|
1319
|
+
|
|
1320
|
+
/**
|
|
1321
|
+
*
|
|
1322
|
+
*/
|
|
1323
|
+
class TheSeamGoogleMapsPlacesAutoCompleteComponent {
|
|
1324
|
+
constructor(_elementRef) {
|
|
1325
|
+
this._elementRef = _elementRef;
|
|
1326
|
+
this._ngUnsubscribe = new Subject();
|
|
1327
|
+
this._autoCompleteReadySubject = new Subject();
|
|
1328
|
+
this._placeChangedPending = [];
|
|
1329
|
+
this.label = 'Search by address, place or name';
|
|
1330
|
+
this.icon = faSearchLocation;
|
|
1331
|
+
this.disabled = false;
|
|
1332
|
+
this.placeholder = 'Enter address, place or name';
|
|
1333
|
+
this.autocorrect = 'off';
|
|
1334
|
+
this.autocapitalize = 'off';
|
|
1335
|
+
this._tabIndex = -1;
|
|
1336
|
+
this._options = SEAM_GOOGLE_PLACES_AUTOCOMPLETE_DEFAULT_OPTIONS;
|
|
1337
|
+
this.placeChanged = this._autoCompleteReadySubject.pipe(startWith(undefined), switchMap(() => this._createPlaceChangedObservable()));
|
|
1338
|
+
}
|
|
1339
|
+
/**
|
|
1340
|
+
* Set the tab index to `-1` to allow the root element of the
|
|
1341
|
+
* component to receive `focus` event from javascript, but not get focused by
|
|
1342
|
+
* keyboard navigation.
|
|
1343
|
+
*/
|
|
1344
|
+
set tabIndex(value) { this._tabIndex = coerceNumberProperty(value); }
|
|
1345
|
+
get tabIndex() { return this._tabIndex; }
|
|
1346
|
+
set options(value) {
|
|
1347
|
+
this._options = value || SEAM_GOOGLE_PLACES_AUTOCOMPLETE_DEFAULT_OPTIONS;
|
|
1348
|
+
}
|
|
1349
|
+
set __autocompleteDirective(value) {
|
|
1350
|
+
this._autoCompleteDirective = value;
|
|
1351
|
+
this._untilAutoCompleteReady().pipe(takeUntil(this._ngUnsubscribe)).subscribe(() => {
|
|
1352
|
+
this.autoComplete = this._autoCompleteDirective.autoComplete;
|
|
1353
|
+
this._placeChangedPending.forEach(pending => pending.observable.subscribe(pending.subscriber));
|
|
1354
|
+
this._placeChangedPending = [];
|
|
1355
|
+
});
|
|
1356
|
+
}
|
|
1357
|
+
get _attrTabIndex() { return this.disabled ? -1 : (this.tabIndex || 0); }
|
|
1358
|
+
_onClick(event) {
|
|
1359
|
+
this._inputDirective.focus();
|
|
1360
|
+
}
|
|
1361
|
+
_onFocus() {
|
|
1362
|
+
var _a;
|
|
1363
|
+
(_a = this._inputDirective) === null || _a === void 0 ? void 0 : _a.focus();
|
|
1364
|
+
}
|
|
1365
|
+
ngOnInit() { }
|
|
1366
|
+
/** @ignore */
|
|
1367
|
+
ngOnDestroy() {
|
|
1368
|
+
this._ngUnsubscribe.next();
|
|
1369
|
+
this._ngUnsubscribe.complete();
|
|
1370
|
+
}
|
|
1371
|
+
_untilAutoCompleteReady() {
|
|
1372
|
+
return interval(500).pipe(filter(() => !!this._autoCompleteDirective.autoComplete), take(1), mapTo(undefined));
|
|
1373
|
+
}
|
|
1374
|
+
/**
|
|
1375
|
+
* Returns the bounds to which predictions are biased.
|
|
1376
|
+
*/
|
|
1377
|
+
getBounds() {
|
|
1378
|
+
this._assertInitialized();
|
|
1379
|
+
return this.autoComplete.getBounds();
|
|
1380
|
+
}
|
|
1381
|
+
/**
|
|
1382
|
+
* Returns the fields to be included for the Place in the details response
|
|
1383
|
+
* when the details are successfully retrieved. For a list of fields see
|
|
1384
|
+
* [PlaceResult](https://developers.google.com/maps/documentation/javascript/reference/places-service#PlaceResult).
|
|
1385
|
+
*/
|
|
1386
|
+
getFields() {
|
|
1387
|
+
this._assertInitialized();
|
|
1388
|
+
return this.autoComplete.getFields();
|
|
1389
|
+
}
|
|
1390
|
+
/**
|
|
1391
|
+
* Returns the details of the Place selected by user if the details were
|
|
1392
|
+
* successfully retrieved. Otherwise returns a stub Place object, with the
|
|
1393
|
+
* name property set to the current value of the input field.
|
|
1394
|
+
*/
|
|
1395
|
+
getPlace() {
|
|
1396
|
+
this._assertInitialized();
|
|
1397
|
+
return this.autoComplete.getPlace();
|
|
1398
|
+
}
|
|
1399
|
+
/**
|
|
1400
|
+
* Sets the preferred area within which to return Place results. Results are
|
|
1401
|
+
* biased towards, but not restricted to, this area.
|
|
1402
|
+
*/
|
|
1403
|
+
setBounds(bounds) {
|
|
1404
|
+
this._assertInitialized();
|
|
1405
|
+
// tslint:disable-next-line: no-non-null-assertion
|
|
1406
|
+
return this.autoComplete.setBounds(bounds);
|
|
1407
|
+
}
|
|
1408
|
+
/**
|
|
1409
|
+
* Sets the component restrictions. Component restrictions are used to
|
|
1410
|
+
* restrict predictions to only those within the parent component. For
|
|
1411
|
+
* example, the country.
|
|
1412
|
+
*/
|
|
1413
|
+
setComponentRestrictions(restrictions) {
|
|
1414
|
+
this._assertInitialized();
|
|
1415
|
+
// tslint:disable-next-line: no-non-null-assertion
|
|
1416
|
+
return this.autoComplete.setComponentRestrictions(restrictions);
|
|
1417
|
+
}
|
|
1418
|
+
/**
|
|
1419
|
+
* Sets the fields to be included for the Place in the details response when
|
|
1420
|
+
* the details are successfully retrieved. For a list of fields see
|
|
1421
|
+
* [PlaceResult](https://developers.google.com/maps/documentation/javascript/reference/places-service#PlaceResult).
|
|
1422
|
+
*/
|
|
1423
|
+
setFields(fields) {
|
|
1424
|
+
this._assertInitialized();
|
|
1425
|
+
// tslint:disable-next-line: no-non-null-assertion
|
|
1426
|
+
return this.autoComplete.setFields(fields);
|
|
1427
|
+
}
|
|
1428
|
+
/** */
|
|
1429
|
+
setOptions(options) {
|
|
1430
|
+
this._assertInitialized();
|
|
1431
|
+
// tslint:disable-next-line: no-non-null-assertion
|
|
1432
|
+
return this.autoComplete.setOptions(options);
|
|
1433
|
+
}
|
|
1434
|
+
/**
|
|
1435
|
+
* Sets the types of predictions to be returned. For supported types, see the
|
|
1436
|
+
* [developer's guide](https://developers.google.com/maps/documentation/javascript/places-autocomplete#constrain-place-types).
|
|
1437
|
+
* If no types are specified, all types will be returned.
|
|
1438
|
+
*/
|
|
1439
|
+
setTypes(types) {
|
|
1440
|
+
this._assertInitialized();
|
|
1441
|
+
// tslint:disable-next-line: no-non-null-assertion
|
|
1442
|
+
return this.autoComplete.setTypes(types);
|
|
1443
|
+
}
|
|
1444
|
+
/** Focuses the input. */
|
|
1445
|
+
focus() {
|
|
1446
|
+
this._elementRef.nativeElement.focus();
|
|
1447
|
+
}
|
|
1448
|
+
/** Unfocuses the input. */
|
|
1449
|
+
blur() {
|
|
1450
|
+
this._elementRef.nativeElement.blur();
|
|
1451
|
+
}
|
|
1452
|
+
getHostElement() {
|
|
1453
|
+
return this._elementRef.nativeElement;
|
|
1454
|
+
}
|
|
1455
|
+
_createPlaceChangedObservable() {
|
|
1456
|
+
const observable = new Observable(subscriber => {
|
|
1457
|
+
if (!this.autoComplete) {
|
|
1458
|
+
this._placeChangedPending.push({ observable, subscriber });
|
|
1459
|
+
return undefined;
|
|
1460
|
+
}
|
|
1461
|
+
const sub = this._autoCompleteDirective.placeChanged.subscribe(subscriber);
|
|
1462
|
+
return () => sub.unsubscribe();
|
|
1463
|
+
});
|
|
1464
|
+
return observable;
|
|
1465
|
+
}
|
|
1466
|
+
/** Asserts that the map has been initialized. */
|
|
1467
|
+
_assertInitialized() {
|
|
1468
|
+
if (!this.autoComplete && (typeof ngDevMode === 'undefined' || ngDevMode)) {
|
|
1469
|
+
throw Error('Cannot access Google Map Places information before the API has been initialized. ' +
|
|
1470
|
+
'Please wait for the API to load before trying to interact with it.');
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
TheSeamGoogleMapsPlacesAutoCompleteComponent.decorators = [
|
|
1475
|
+
{ type: Component, args: [{
|
|
1476
|
+
selector: 'seam-google-maps-places-autocomplete',
|
|
1477
|
+
template: "<div *ngIf=\"icon\" class=\"search-container--icon-wrapper\">\r\n <seam-icon [icon]=\"icon\" class=\"text-muted\" size=\"lg\"></seam-icon>\r\n</div>\r\n<label *ngIf=\"label || placeholder\" [attr.for]=\"inp.id\" class=\"sr-only\">{{ label || placeholder }}</label>\r\n<input #inp=\"seamInput\"\r\n seamInput\r\n seamGoogleMapsPlacesAutocomplete\r\n [disabled]=\"disabled\"\r\n [attr.placeholder]=\"placeholder\"\r\n [attr.autocorrect]=\"autocorrect\"\r\n [attr.autocapitalize]=\"autocapitalize\"\r\n [options]=\"_options\"\r\n/>\r\n",
|
|
1478
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
1479
|
+
exportAs: 'seamGoogleMapsPlacesAutoComplete',
|
|
1480
|
+
styles: [":host{position:relative;width:100%}:host>input{padding-left:40px}:host .search-container--icon-wrapper{position:absolute;top:6px;left:10px;bottom:0px}:host .search-container--icon-wrapper ::ng-deep .svg-inline--fa{vertical-align:middle}\n"]
|
|
1481
|
+
},] }
|
|
1482
|
+
];
|
|
1483
|
+
TheSeamGoogleMapsPlacesAutoCompleteComponent.ctorParameters = () => [
|
|
1484
|
+
{ type: ElementRef }
|
|
1485
|
+
];
|
|
1486
|
+
TheSeamGoogleMapsPlacesAutoCompleteComponent.propDecorators = {
|
|
1487
|
+
label: [{ type: Input }],
|
|
1488
|
+
icon: [{ type: Input }],
|
|
1489
|
+
disabled: [{ type: Input }],
|
|
1490
|
+
placeholder: [{ type: Input }],
|
|
1491
|
+
autocorrect: [{ type: Input }],
|
|
1492
|
+
autocapitalize: [{ type: Input }],
|
|
1493
|
+
tabIndex: [{ type: Input }],
|
|
1494
|
+
options: [{ type: Input }],
|
|
1495
|
+
placeChanged: [{ type: Output }],
|
|
1496
|
+
_inputDirective: [{ type: ViewChild, args: ['inp', { read: InputDirective, static: true },] }],
|
|
1497
|
+
__autocompleteDirective: [{ type: ViewChild, args: [TheSeamGoogleMapsPlacesAutocompleteDirective, { static: true },] }],
|
|
1498
|
+
_attrTabIndex: [{ type: HostBinding, args: ['attr.tabindex',] }],
|
|
1499
|
+
_onClick: [{ type: HostListener, args: ['click', ['event'],] }],
|
|
1500
|
+
_onFocus: [{ type: HostListener, args: ['focus', ['$event'],] }]
|
|
1501
|
+
};
|
|
1502
|
+
__decorate([
|
|
1503
|
+
InputBoolean()
|
|
1504
|
+
], TheSeamGoogleMapsPlacesAutoCompleteComponent.prototype, "disabled", void 0);
|
|
1505
|
+
|
|
1506
|
+
class TheSeamMapsControlComponent {
|
|
1507
|
+
constructor(_googleMaps, _googleMapsControls) {
|
|
1508
|
+
this._googleMaps = _googleMaps;
|
|
1509
|
+
this._googleMapsControls = _googleMapsControls;
|
|
1510
|
+
this._ngUnsubscribe = new Subject();
|
|
1511
|
+
}
|
|
1512
|
+
set def(value) {
|
|
1513
|
+
if (value !== this._def) {
|
|
1514
|
+
this._remove();
|
|
1515
|
+
this._def = value;
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
/** @ignore */
|
|
1519
|
+
ngOnInit() {
|
|
1520
|
+
this._googleMaps.mapReady$.pipe(tap(ready => {
|
|
1521
|
+
if (ready) {
|
|
1522
|
+
this._add();
|
|
1523
|
+
}
|
|
1524
|
+
else {
|
|
1525
|
+
this._remove();
|
|
1526
|
+
}
|
|
1527
|
+
}), takeUntil(this._ngUnsubscribe)).subscribe();
|
|
1528
|
+
}
|
|
1529
|
+
/** @ignore */
|
|
1530
|
+
ngOnDestroy() {
|
|
1531
|
+
this._remove();
|
|
1532
|
+
this._ngUnsubscribe.next();
|
|
1533
|
+
this._ngUnsubscribe.complete();
|
|
1534
|
+
}
|
|
1535
|
+
_add() {
|
|
1536
|
+
if (this._controlRef) {
|
|
1537
|
+
return;
|
|
1538
|
+
}
|
|
1539
|
+
if (this._def === null || this._def === undefined) {
|
|
1540
|
+
return;
|
|
1541
|
+
}
|
|
1542
|
+
this._controlRef = this._googleMapsControls.add(this._def);
|
|
1543
|
+
}
|
|
1544
|
+
_remove() {
|
|
1545
|
+
var _a;
|
|
1546
|
+
(_a = this._controlRef) === null || _a === void 0 ? void 0 : _a.destroy();
|
|
1547
|
+
this._controlRef = undefined;
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
TheSeamMapsControlComponent.decorators = [
|
|
1551
|
+
{ type: Component, args: [{
|
|
1552
|
+
selector: 'seam-map-control',
|
|
1553
|
+
template: '',
|
|
1554
|
+
changeDetection: ChangeDetectionStrategy.OnPush
|
|
1555
|
+
},] }
|
|
1556
|
+
];
|
|
1557
|
+
TheSeamMapsControlComponent.ctorParameters = () => [
|
|
1558
|
+
{ type: GoogleMapsService },
|
|
1559
|
+
{ type: GoogleMapsControlsService, decorators: [{ type: Inject, args: [MAP_CONTROLS_SERVICE,] }] }
|
|
1560
|
+
];
|
|
1561
|
+
TheSeamMapsControlComponent.propDecorators = {
|
|
1562
|
+
def: [{ type: Input }]
|
|
1563
|
+
};
|
|
1564
|
+
|
|
1565
|
+
/**
|
|
1566
|
+
*
|
|
1567
|
+
*/
|
|
1568
|
+
class TheSeamMapFileDropComponent {
|
|
1569
|
+
constructor(_elementRef, _ngZone, _googleMaps, _mapValueManager, _renderer) {
|
|
1570
|
+
this._elementRef = _elementRef;
|
|
1571
|
+
this._ngZone = _ngZone;
|
|
1572
|
+
this._googleMaps = _googleMaps;
|
|
1573
|
+
this._mapValueManager = _mapValueManager;
|
|
1574
|
+
this._renderer = _renderer;
|
|
1575
|
+
this._ngUnsubscribe = new Subject();
|
|
1576
|
+
this._listeners = [];
|
|
1577
|
+
this._globalDragInProgress = false;
|
|
1578
|
+
this._handleDragOverEvent = (event) => {
|
|
1579
|
+
if (!this._dropAllowed()) {
|
|
1580
|
+
return;
|
|
1581
|
+
}
|
|
1582
|
+
if (!this._isSupportedDataTransferTypes(event.dataTransfer)) {
|
|
1583
|
+
return;
|
|
1584
|
+
}
|
|
1585
|
+
event.preventDefault();
|
|
1586
|
+
event.dataTransfer.dropEffect = 'copy';
|
|
1587
|
+
};
|
|
1588
|
+
this._handleDropEvent = (event) => {
|
|
1589
|
+
if (!this._dropAllowed()) {
|
|
1590
|
+
return;
|
|
1591
|
+
}
|
|
1592
|
+
event.preventDefault();
|
|
1593
|
+
event.stopPropagation();
|
|
1594
|
+
this._renderer.setStyle(this._elementRef.nativeElement, 'display', 'none');
|
|
1595
|
+
// TODO: Show error if multiple files?
|
|
1596
|
+
if (!this._isSupportedDataTransfer(event.dataTransfer)) {
|
|
1597
|
+
return;
|
|
1598
|
+
}
|
|
1599
|
+
const item = event.dataTransfer.items[0];
|
|
1600
|
+
const file = item.getAsFile();
|
|
1601
|
+
readGeoFile(file).then(json => {
|
|
1602
|
+
this._mapValueManager.setValue(json, MapValueSource.Input);
|
|
1603
|
+
});
|
|
1604
|
+
};
|
|
1605
|
+
this._handleDragEnterEvent = (event) => {
|
|
1606
|
+
if (!this._dropAllowed()) {
|
|
1607
|
+
return;
|
|
1608
|
+
}
|
|
1609
|
+
if (!this._isSupportedDataTransferTypes(event.dataTransfer)) {
|
|
1610
|
+
return;
|
|
1611
|
+
}
|
|
1612
|
+
this._renderer.setStyle(this._elementRef.nativeElement, 'display', 'block');
|
|
1613
|
+
};
|
|
1614
|
+
this._handleDragLeaveEvent = (event) => {
|
|
1615
|
+
if (!this._dropAllowed()) {
|
|
1616
|
+
return;
|
|
1617
|
+
}
|
|
1618
|
+
this._renderer.setStyle(this._elementRef.nativeElement, 'display', 'none');
|
|
1619
|
+
};
|
|
1620
|
+
}
|
|
1621
|
+
/** @ignore */
|
|
1622
|
+
ngOnInit() {
|
|
1623
|
+
this._googleMaps.mapReady$.pipe(tap(ready => {
|
|
1624
|
+
if (ready) {
|
|
1625
|
+
this._enableFileDrop();
|
|
1626
|
+
}
|
|
1627
|
+
else {
|
|
1628
|
+
this._disableFileDrop();
|
|
1629
|
+
}
|
|
1630
|
+
}), takeUntil(this._ngUnsubscribe)).subscribe();
|
|
1631
|
+
}
|
|
1632
|
+
/** @ignore */
|
|
1633
|
+
ngOnDestroy() {
|
|
1634
|
+
this._disableFileDrop();
|
|
1635
|
+
this._ngUnsubscribe.next();
|
|
1636
|
+
this._ngUnsubscribe.complete();
|
|
1637
|
+
}
|
|
1638
|
+
_enableFileDrop() {
|
|
1639
|
+
const divElement = this._googleMaps.getDiv();
|
|
1640
|
+
if (this._mapDiv === divElement) {
|
|
1641
|
+
return;
|
|
1642
|
+
}
|
|
1643
|
+
if (this._mapDiv !== undefined) {
|
|
1644
|
+
// Disable file drop on previous map div.
|
|
1645
|
+
this._disableFileDrop();
|
|
1646
|
+
}
|
|
1647
|
+
this._mapDiv = divElement;
|
|
1648
|
+
this._ngZone.runOutsideAngular(() => {
|
|
1649
|
+
this._listeners.push(this._renderer.listen('document', 'dragstart', (event) => {
|
|
1650
|
+
this._globalDragInProgress = true;
|
|
1651
|
+
}));
|
|
1652
|
+
this._listeners.push(this._renderer.listen('document', 'dragend', (event) => {
|
|
1653
|
+
this._globalDragInProgress = false;
|
|
1654
|
+
}));
|
|
1655
|
+
this._listeners.push(this._renderer.listen(divElement, 'dragover', this._handleDragOverEvent));
|
|
1656
|
+
this._listeners.push(this._renderer.listen(this._elementRef.nativeElement, 'dragover', this._handleDragOverEvent));
|
|
1657
|
+
this._listeners.push(this._renderer.listen(this._elementRef.nativeElement, 'drop', this._handleDropEvent));
|
|
1658
|
+
this._listeners.push(this._renderer.listen(divElement, 'dragenter', this._handleDragEnterEvent));
|
|
1659
|
+
this._listeners.push(this._renderer.listen(this._elementRef.nativeElement, 'dragleave', this._handleDragLeaveEvent));
|
|
1660
|
+
});
|
|
1661
|
+
}
|
|
1662
|
+
_disableFileDrop() {
|
|
1663
|
+
if (this._listeners.length > 0) {
|
|
1664
|
+
this._listeners.forEach(l => l());
|
|
1665
|
+
this._listeners = [];
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
_dropAllowed() {
|
|
1669
|
+
return !this._globalDragInProgress;
|
|
1670
|
+
}
|
|
1671
|
+
_isSupportedDataTransfer(dataTransfer) {
|
|
1672
|
+
return dataTransfer.files.length === 1 && dataTransfer.types[0] === 'Files';
|
|
1673
|
+
}
|
|
1674
|
+
_isSupportedDataTransferTypes(dataTransfer) {
|
|
1675
|
+
return dataTransfer.types[0] === 'Files';
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
TheSeamMapFileDropComponent.decorators = [
|
|
1679
|
+
{ type: Component, args: [{
|
|
1680
|
+
selector: 'seam-map-file-drop',
|
|
1681
|
+
template: "",
|
|
1682
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
1683
|
+
styles: [":host{display:none;position:absolute;top:0;left:0;width:100%;height:100%;z-index:9999!important;background-color:#1e1e1eb3}\n"]
|
|
1684
|
+
},] }
|
|
1685
|
+
];
|
|
1686
|
+
TheSeamMapFileDropComponent.ctorParameters = () => [
|
|
1687
|
+
{ type: ElementRef },
|
|
1688
|
+
{ type: NgZone },
|
|
1689
|
+
{ type: GoogleMapsService },
|
|
1690
|
+
{ type: MapValueManagerService },
|
|
1691
|
+
{ type: Renderer2 }
|
|
1692
|
+
];
|
|
1693
|
+
|
|
1694
|
+
class TheSeamGoogleMapsModule {
|
|
1695
|
+
}
|
|
1696
|
+
TheSeamGoogleMapsModule.decorators = [
|
|
1697
|
+
{ type: NgModule, args: [{
|
|
1698
|
+
declarations: [
|
|
1699
|
+
TheSeamGoogleMapsComponent,
|
|
1700
|
+
TheSeamMapFileDropComponent,
|
|
1701
|
+
TheSeamGoogleMapsUploadButtonControlComponent,
|
|
1702
|
+
TheSeamGoogleMapsRecenterButtonControlComponent,
|
|
1703
|
+
TheSeamMapsControlComponent,
|
|
1704
|
+
TheSeamGoogleMapsPlacesAutocompleteDirective,
|
|
1705
|
+
TheSeamGoogleMapsPlacesAutoCompleteComponent,
|
|
1706
|
+
],
|
|
1707
|
+
imports: [
|
|
1708
|
+
CommonModule,
|
|
1709
|
+
ObserversModule,
|
|
1710
|
+
TheSeamSharedModule,
|
|
1711
|
+
TheSeamMenuModule,
|
|
1712
|
+
TheSeamIconModule,
|
|
1713
|
+
TheSeamFormFieldModule,
|
|
1714
|
+
AgmCoreModule,
|
|
1715
|
+
],
|
|
1716
|
+
exports: [
|
|
1717
|
+
TheSeamGoogleMapsComponent,
|
|
1718
|
+
TheSeamGoogleMapsPlacesAutocompleteDirective,
|
|
1719
|
+
TheSeamGoogleMapsPlacesAutoCompleteComponent
|
|
1720
|
+
]
|
|
1721
|
+
},] }
|
|
1722
|
+
];
|
|
1723
|
+
|
|
1724
|
+
/**
|
|
1725
|
+
* Generated bundle index. Do not edit.
|
|
1726
|
+
*/
|
|
1727
|
+
|
|
1728
|
+
export { AppFeaturePropertyName, GoogleMapsContextMenu, GoogleMapsControlsService, GoogleMapsService, MapControlRef, MapValueManagerService, MapValueSource, SEAM_GOOGLE_PLACES_AUTOCOMPLETE_DEFAULT_OPTIONS, TheSeamGoogleMapsComponent, TheSeamGoogleMapsModule, TheSeamGoogleMapsPlacesAutoCompleteComponent, TheSeamGoogleMapsPlacesAutocompleteDirective, TheSeamGoogleMapsRecenterButtonControlComponent, TheSeamGoogleMapsUploadButtonControlComponent, TheSeamMapFileDropComponent, TheSeamMapsControlComponent, addInnerFeatureCutoutToExteriorFeature, createDataFeatureFromPolygon, createFeatureChangeObservable, featureContains, fixPathDifferentStartingAndEndingPoint, getBoundsWithAllFeatures, getFeatureBounds, getFeatureCenter, getFeaturesCount, getPossibleExteriorFeature, isAppFeatureProperty, isFeatureSelected, multiPolygonCoordinates, polygonCoordinates, removeAllFeatures, setFeatureSelected, stripAppFeaturePropertiesFromJson, toTurfJsFeature, toTurfJsMultiPolygon, toTurfJsPolygon, ɵ0, MAP_CONTROLS_SERVICE as ɵb, MAP_CONTROL_DATA as ɵc };
|
|
1729
|
+
//# sourceMappingURL=theseam-ui-common-google-maps.js.map
|