@hpcc-js/map 3.5.2 → 3.5.4

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.
Files changed (96) hide show
  1. package/LICENSE +43 -43
  2. package/README.md +88 -88
  3. package/TopoJSON/BR.json +122 -122
  4. package/TopoJSON/GB_idx.json +1 -1
  5. package/TopoJSON/IE_idx.json +1 -1
  6. package/TopoJSON/ND_idx.json +1 -1
  7. package/TopoJSON/countries.json +257 -257
  8. package/TopoJSON/us-counties.json +16550 -16550
  9. package/TopoJSON/us-states.json +458 -458
  10. package/dist/index.js +1 -1
  11. package/dist/index.js.map +1 -1
  12. package/dist/index.umd.cjs +1 -1
  13. package/dist/index.umd.cjs.map +1 -1
  14. package/package.json +9 -9
  15. package/src/CanvasPinLayer.ts +99 -99
  16. package/src/CanvasPins.ts +397 -397
  17. package/src/Choropleth.css +26 -26
  18. package/src/Choropleth.ts +203 -203
  19. package/src/ChoroplethContinents.ts +13 -13
  20. package/src/ChoroplethCounties.ts +111 -111
  21. package/src/ChoroplethCountries.ts +100 -100
  22. package/src/ChoroplethStates.ts +103 -103
  23. package/src/ChoroplethStatesHeat.ts +8 -8
  24. package/src/GMap.css +20 -20
  25. package/src/GMap.ts +880 -880
  26. package/src/GMapCounties.ts +93 -93
  27. package/src/GMapGraph.ts +61 -61
  28. package/src/GMapHeat.ts +27 -27
  29. package/src/GMapLayered.ts +94 -94
  30. package/src/GMapPin.ts +115 -115
  31. package/src/GMapPinLine.ts +138 -138
  32. package/src/GeoHash.css +14 -14
  33. package/src/GeoHash.ts +139 -139
  34. package/src/Graph.css +9 -9
  35. package/src/Graph.ts +98 -98
  36. package/src/Graticule.css +12 -12
  37. package/src/Graticule.ts +97 -97
  38. package/src/Heat.ts +87 -87
  39. package/src/IChoropleth.ts +8 -8
  40. package/src/Layer.ts +99 -99
  41. package/src/Layered.css +18 -18
  42. package/src/Layered.ts +206 -206
  43. package/src/Lines.css +8 -8
  44. package/src/Lines.ts +78 -78
  45. package/src/OpenStreet.css +15 -15
  46. package/src/OpenStreet.ts +126 -126
  47. package/src/Pins.css +17 -17
  48. package/src/Pins.ts +350 -350
  49. package/src/Projection.ts +42 -42
  50. package/src/TestHeatMap.ts +8 -8
  51. package/src/TopoJSONChoropleth.ts +125 -125
  52. package/src/Utility.ts +484 -484
  53. package/src/__package__.ts +3 -3
  54. package/src/index.ts +33 -33
  55. package/src/leaflet/AlbersPR.ts +48 -48
  56. package/src/leaflet/Blank.ts +9 -9
  57. package/src/leaflet/Circles.ts +139 -139
  58. package/src/leaflet/ClusterCircles.css +26 -26
  59. package/src/leaflet/ClusterCircles.ts +88 -88
  60. package/src/leaflet/Countries.ts +43 -43
  61. package/src/leaflet/DrawLayer.ts +167 -167
  62. package/src/leaflet/FeatureLayer.ts +138 -138
  63. package/src/leaflet/GMap.ts +44 -44
  64. package/src/leaflet/HeatLayer.ts +77 -77
  65. package/src/leaflet/Icons.ts +60 -60
  66. package/src/leaflet/Leaflet.css +3 -3
  67. package/src/leaflet/Leaflet.ts +239 -239
  68. package/src/leaflet/MapBox.ts +35 -35
  69. package/src/leaflet/Markers.ts +109 -109
  70. package/src/leaflet/OpenStreet.ts +27 -27
  71. package/src/leaflet/Path.ts +138 -138
  72. package/src/leaflet/Pins.ts +73 -73
  73. package/src/leaflet/Polygons.ts +113 -113
  74. package/src/leaflet/Region.ts +138 -138
  75. package/src/leaflet/Text.ts +99 -99
  76. package/src/leaflet/TileLayer.ts +81 -81
  77. package/src/leaflet/TopoJSON.ts +146 -146
  78. package/src/leaflet/US.ts +15 -15
  79. package/src/leaflet/USCounties.ts +43 -43
  80. package/src/leaflet/USStates.ts +41 -41
  81. package/src/leaflet/World.css +3 -3
  82. package/src/leaflet/World.ts +172 -172
  83. package/src/leaflet/index.ts +18 -18
  84. package/src/leaflet/leaflet-shim.ts +18 -18
  85. package/src/leaflet/plugins/BeautifyIcon.css +44 -44
  86. package/src/leaflet/plugins/BeautifyIcon.ts +190 -190
  87. package/src/leaflet/plugins/BeutifyIcon.licence +20 -20
  88. package/src/leaflet/plugins/D3SvgOverlay.css +2 -2
  89. package/src/leaflet/plugins/D3SvgOverlay.licence +20 -20
  90. package/src/leaflet/plugins/D3SvgOverlay.ts +175 -175
  91. package/src/leaflet/plugins/HeatLayer.license +21 -21
  92. package/src/leaflet/plugins/HeatLayer.ts +224 -224
  93. package/src/leaflet/plugins/Leaflet.GoogleMutant.ts +424 -424
  94. package/src/leaflet/plugins/lru_map.ts +352 -352
  95. package/src/test.ts +114 -114
  96. package/types/Layered.d.ts +1 -1
package/src/GMap.ts CHANGED
@@ -1,880 +1,880 @@
1
- import { HTMLWidget } from "@hpcc-js/common";
2
- import { AbsoluteSurface } from "@hpcc-js/layout";
3
- import { promiseTimeout } from "@hpcc-js/util";
4
- import { map as d3Map } from "d3-collection";
5
- import * as _GoogleMapsLoader from "google-maps";
6
-
7
- const GoogleMapsLoader = _GoogleMapsLoader.default || _GoogleMapsLoader;
8
-
9
- import "../src/GMap.css";
10
-
11
- declare const window: any;
12
-
13
- export let google: any = null;
14
-
15
- let timout = 15000;
16
- export function requireGoogleMapTimeout(ms: number) {
17
- timout = ms;
18
- }
19
-
20
- let _googleMapPromise;
21
- export function requireGoogleMap(customGoogle?: any) {
22
- if (!_googleMapPromise) {
23
- _googleMapPromise = promiseTimeout(timout, new Promise<void>(function (resolve, reject) {
24
- if (google) {
25
- resolve();
26
- } else if (customGoogle) {
27
- google = customGoogle;
28
- resolve();
29
- } else {
30
- if (!window.__hpcc_gmap_apikey) {
31
- console.warn("__hpcc_gmap_apikey does not contain a valid API key, reverting to developers key (expect limited performance)");
32
- }
33
- try {
34
- GoogleMapsLoader.KEY = window.__hpcc_gmap_apikey || "AIzaSyDwGn2i1i_pMZvnqYJN1BksD_tjYaCOWKg";
35
- GoogleMapsLoader.LIBRARIES = ["geometry", "drawing"];
36
- GoogleMapsLoader.load(function (_google) {
37
- google = _google;
38
- resolve();
39
- });
40
- } catch (e) {
41
- console.warn(`Failed to initialize Google Map API: ${e.message}`);
42
- resolve();
43
- }
44
- }
45
- })).catch(e => {
46
- console.warn(`Timed out initializing Google Map API after ${timout}ms.`);
47
- });
48
- }
49
- return _googleMapPromise;
50
- }
51
-
52
- function createOverlay(map, worldSurface, viewportSurface) {
53
- function Overlay(map2, worldSurface2, viewportSurface2) {
54
- google.maps.OverlayView.call(this);
55
- this._div = null;
56
-
57
- this._worldSurface = worldSurface2;
58
- this._viewportSurface = viewportSurface2;
59
-
60
- this._map = map2;
61
- this.setMap(map2);
62
-
63
- const context = this;
64
- google.maps.event.addListener(map2, "bounds_changed", function () {
65
- context.draw();
66
- });
67
- google.maps.event.addListener(map2, "projection_changed", function () {
68
- context.draw();
69
- });
70
-
71
- this._prevWorldMin = { x: 0, y: 0 };
72
- this._prevWorldMax = { x: 0, y: 0 };
73
- this._prevMin = { x: 0, y: 0 };
74
- this._prevMax = { x: 0, y: 0 };
75
- }
76
- Overlay.prototype = google.maps.OverlayView.prototype;
77
-
78
- Overlay.prototype.onAdd = function () {
79
- this.div = document.createElement("div");
80
-
81
- this._viewportSurface.target(null)
82
- .target(this.div)
83
- .units("pixels");
84
-
85
- const panes = this.getPanes();
86
- panes.overlayMouseTarget.appendChild(this.div);
87
- };
88
-
89
- Overlay.prototype.draw = function () {
90
- const projection = this.getProjection();
91
- if (!projection)
92
- return;
93
-
94
- const bounds = this._map.getBounds();
95
- const center = projection.fromLatLngToDivPixel(bounds.getCenter());
96
- const sw = projection.fromLatLngToDivPixel(bounds.getSouthWest());
97
- const ne = projection.fromLatLngToDivPixel(bounds.getNorthEast());
98
-
99
- const min = {
100
- x: sw.x,
101
- y: ne.y
102
- };
103
- const max = {
104
- x: ne.x,
105
- y: sw.y
106
- };
107
-
108
- const worldWidth = projection.getWorldWidth();
109
- while (max.x < min.x + 100) { // Ignoe dateline from being the rect.
110
- max.x += worldWidth;
111
- }
112
- while (min.x > center.x) {
113
- min.x -= worldWidth;
114
- max.x -= worldWidth;
115
- }
116
-
117
- if (min.x !== this._prevMin.x || min.y !== this._prevMin.y || max.x !== this._prevMax.x || max.y !== this._prevMax.y) {
118
- this._viewportSurface
119
- .resize({ width: 0, height: 0 })
120
- .widgetX(min.x)
121
- .widgetY(min.y)
122
- .widgetWidth(max.x - min.x)
123
- .widgetHeight(max.y - min.y)
124
- ;
125
- // FF Issue on initial render (GH-1855) ---
126
- if (this._viewportSurface._renderCount) {
127
- this._viewportSurface.render();
128
- this._prevMin = min;
129
- this._prevMax = max;
130
- } else {
131
- this._viewportSurface.lazyRender();
132
- }
133
- }
134
-
135
- const worldMin = projection.fromLatLngToDivPixel(new google.maps.LatLng(85, -179.9));
136
- const worldMax = projection.fromLatLngToDivPixel(new google.maps.LatLng(-85, 179.9));
137
- while (worldMax.x < worldMin.x + 100) { // Ignoe dateline from being the rect.
138
- worldMax.x += worldWidth;
139
- }
140
- while (worldMin.x > center.x) {
141
- worldMin.x -= worldWidth;
142
- worldMax.x -= worldWidth;
143
- }
144
- if (worldMin.x !== this._prevWorldMin.x || worldMin.y !== this._prevWorldMin.y || worldMax.x !== this._prevWorldMax.x || worldMax.y !== this._prevWorldMax.y) {
145
- this._worldSurface
146
- .widgetX(worldMin.x)
147
- .widgetY(worldMin.y)
148
- .widgetWidth(worldMax.x - worldMin.x)
149
- .widgetHeight(worldMax.y - worldMin.y)
150
- .render()
151
- ;
152
- this._prevWorldMin = worldMax;
153
- this._prevWorldMax = worldMax;
154
- }
155
- };
156
-
157
- Overlay.prototype.onRemove = function () {
158
- this._viewportSurface.target(null);
159
- this._div.parentNode.removeChild(this._div);
160
- this._div = null;
161
- };
162
-
163
- return new Overlay(map, worldSurface, viewportSurface);
164
- }
165
-
166
- class UserShapeSelectionBag {
167
- _userShapes: any[];
168
- mapContext;
169
-
170
- constructor(mapObj) {
171
- this._userShapes = [];
172
- this.mapContext = mapObj;
173
- }
174
-
175
- add(_) {
176
- const idx = this._userShapes.indexOf(_);
177
- if (idx >= 0) {
178
- return;
179
- }
180
- this._userShapes.push(_);
181
- }
182
-
183
- remove(_) {
184
- const idx = this._userShapes.indexOf(_);
185
- if (idx >= 0) {
186
- this._userShapes.splice(idx, 1);
187
- }
188
- _.setMap(null);
189
- }
190
-
191
- save() {
192
- return this._userShapes.map(shape => this._saveShape(shape));
193
- }
194
-
195
- load(_) {
196
- this._deserializeShapes(_);
197
- }
198
-
199
- _saveShape(shape) {
200
- const retVal: any = {};
201
-
202
- const createShapes = {
203
- circle: (_) => {
204
- retVal.type = "circle";
205
- retVal.pos = {
206
- lat: _.center.lat(),
207
- lng: _.center.lng()
208
- };
209
- retVal.radius = _.radius;
210
- },
211
- rectangle: (_) => {
212
- retVal.type = "rectangle";
213
- retVal.bounds = {
214
- ne: _.bounds.getNorthEast(),
215
- sw: _.bounds.getSouthWest()
216
- };
217
- },
218
- polygon: (_) => {
219
- retVal.type = _.__hpcc_type;
220
-
221
- const vertices = _.getPath();
222
-
223
- retVal.vertices = [];
224
-
225
- for (let i = 0; i < vertices.length; i++) {
226
- retVal.vertices.push(vertices.getAt(i));
227
- }
228
- },
229
- polyline: (_) => {
230
- createShapes.polygon(_);
231
- }
232
- };
233
-
234
- createShapes[shape.__hpcc_type](shape);
235
- retVal.strokeWeight = shape.strokeWeight;
236
- retVal.fillColor = shape.fillColor;
237
- retVal.fillOpacity = shape.fillOpacity;
238
- retVal.editable = shape.editable;
239
- retVal.clickable = shape.clickable || true;
240
-
241
- return retVal;
242
- }
243
-
244
- _deserializeShapes(_shapes) {
245
-
246
- const shapes = JSON.parse(_shapes);
247
-
248
- const defOptions = {
249
- strokeWeight: 0,
250
- fillOpacity: 0.45,
251
- fillColor: "#1f77b4",
252
- editable: true,
253
- clickable: true
254
- };
255
-
256
- const createShapes = {
257
- circle: (_, map) => {
258
- const shape = new google.maps.Circle({
259
- strokeWeight: _.strokeWeight || defOptions.strokeWeight,
260
- fillColor: _.fillColor || defOptions.fillColor,
261
- fillOpacity: _.fillOpacity || defOptions.fillOpacity,
262
- editable: _.editable || defOptions.editable,
263
- clickable: _.clickable || defOptions.clickable,
264
- map,
265
- center: _.pos,
266
- radius: _.radius
267
- });
268
- return shape;
269
- },
270
- rectangle: (_, map) => {
271
- const shape = new google.maps.Rectangle({
272
- strokeWeight: _.strokeWeight || defOptions.strokeWeight,
273
- fillColor: _.fillColor || defOptions.fillColor,
274
- fillOpacity: _.fillOpacity || defOptions.fillOpacity,
275
- editable: _.editable || defOptions.editable,
276
- clickable: _.clickable || defOptions.clickable,
277
- map,
278
- bounds: {
279
- north: _.bounds.ne.lat,
280
- west: _.bounds.sw.lng,
281
- south: _.bounds.sw.lat,
282
- east: _.bounds.ne.lng
283
- }
284
- });
285
- return shape;
286
- },
287
- polygon: (_, map) => {
288
- const shape = new google.maps.Polygon({
289
- strokeWeight: _.strokeWeight || defOptions.strokeWeight,
290
- fillColor: _.fillColor || defOptions.fillColor,
291
- fillOpacity: _.fillOpacity || defOptions.fillOpacity,
292
- editable: _.editable || defOptions.editable,
293
- clickable: _.clickable || defOptions.clickable,
294
- map,
295
- paths: _.vertices
296
- });
297
- return shape;
298
- },
299
- polyline: (_, map) => {
300
- const shape = new google.maps.Polyline({
301
- strokeWeight: _.strokeWeight || defOptions.strokeWeight,
302
- fillColor: _.fillColor || defOptions.fillColor,
303
- fillOpacity: _.fillOpacity || defOptions.fillOpacity,
304
- editable: _.editable || defOptions.editable,
305
- clickable: _.clickable || defOptions.clickable,
306
- map,
307
- path: _.vertices
308
- });
309
- return shape;
310
- }
311
- };
312
-
313
- for (let i = 0; i < shapes.length; i++) {
314
- const shape = createShapes[shapes[i].type](shapes[i], this.mapContext._googleMap);
315
- this.mapContext.onDrawingComplete({ type: shapes[i].type, overlay: shape });
316
- }
317
- }
318
- }
319
-
320
- export class GMap extends HTMLWidget {
321
- _overlay;
322
- _userShapes;
323
- _worldSurface;
324
- _viewportSurface;
325
- _googleMapNode;
326
- _googleMap;
327
- _googleGeocoder;
328
- _prevCenterLat;
329
- _prevCenterLong;
330
- _googleStreetViewService;
331
- _googleMapPanorama;
332
- _prevZoom;
333
- _prevStreetView;
334
- _circleMap;
335
- _pinMap;
336
- _drawingManager;
337
- _prevCenterAddress;
338
- _userShapeSelection;
339
-
340
- constructor() {
341
- super();
342
-
343
- this._tag = "div";
344
-
345
- const context = this;
346
- function calcProjection(surface, lat, long) {
347
- const projection = context._overlay.getProjection();
348
- const retVal = projection.fromLatLngToDivPixel(new google.maps.LatLng(lat, long));
349
- const worldWidth = projection.getWorldWidth();
350
- const widgetX = parseFloat(surface.widgetX());
351
- const widgetY = parseFloat(surface.widgetY());
352
- const widgetWidth = parseFloat(surface.widgetWidth());
353
- retVal.x -= widgetX;
354
- retVal.y -= widgetY;
355
- while (retVal.x < 0) {
356
- retVal.x += worldWidth;
357
- }
358
- while (retVal.x > widgetWidth) {
359
- retVal.x -= worldWidth;
360
- }
361
- return retVal;
362
- }
363
-
364
- this._userShapes = new UserShapeSelectionBag(this);
365
-
366
- this._worldSurface = new AbsoluteSurface();
367
- this._worldSurface.project = function (lat, long) {
368
- return calcProjection(this, lat, long);
369
- };
370
-
371
- this._viewportSurface = new AbsoluteSurface();
372
- this._viewportSurface.project = function (lat, long) {
373
- return calcProjection(this, lat, long);
374
- };
375
- }
376
-
377
- data(_?) {
378
- const retVal = HTMLWidget.prototype.data.apply(this, arguments);
379
- return retVal;
380
- }
381
-
382
- getMapType() {
383
- switch (this.type()) {
384
- case "terrain":
385
- return google.maps.MapTypeId.TERRAIN;
386
- case "road":
387
- return google.maps.MapTypeId.ROADMAP;
388
- case "satellite":
389
- return google.maps.MapTypeId.SATELLITE;
390
- case "hybrid":
391
- return google.maps.MapTypeId.HYBRID;
392
- default:
393
- return google.maps.MapTypeId.ROADMAP;
394
- }
395
- }
396
-
397
- getMapOptions() {
398
- return {
399
- panControl: this.panControl(),
400
- zoomControl: this.zoomControl(),
401
- fullscreenControl: this.fullscreenControl(),
402
- mapTypeControl: this.mapTypeControl(),
403
- scaleControl: this.scaleControl(),
404
- streetViewControl: this.streetViewControl(),
405
- overviewMapControl: this.overviewMapControl(),
406
- overviewMapControlOptions: { opened: true },
407
- styles: this.googleMapStyles()
408
- };
409
- }
410
-
411
- size(_?) {
412
- const retVal = HTMLWidget.prototype.size.apply(this, arguments);
413
- if (arguments.length && this._googleMapNode) {
414
- this._googleMapNode
415
- .style("width", _.width + "px")
416
- .style("height", _.height + "px")
417
- ;
418
- google.maps.event.trigger(this._googleMap, "resize");
419
- }
420
- return retVal;
421
- }
422
-
423
- enter(domNode, element) {
424
- super.enter(domNode, element);
425
- const context = this;
426
- this._googleGeocoder = new google.maps.Geocoder();
427
- this._googleMapNode = element.append("div")
428
- .style("width", this.width() + "px")
429
- .style("height", this.height() + "px")
430
- ;
431
- this._googleMap = new google.maps.Map(this._googleMapNode.node(), {
432
- zoom: this.zoom(),
433
- center: new google.maps.LatLng(this.centerLat(), this.centerLong()),
434
- mapTypeId: this.getMapType(),
435
- disableDefaultUI: true
436
- });
437
- this._overlay = createOverlay(this._googleMap, this._worldSurface, this._viewportSurface);
438
- this._googleMap.addListener("center_changed", function () {
439
- context.centerLat(context._googleMap.center.lat());
440
- context._prevCenterLat = context.centerLat();
441
- context.centerLong(context._googleMap.center.lng());
442
- context._prevCenterLong = context.centerLong();
443
- context._googleMapPanorama.setPosition({ lat: context.centerLat(), lng: context.centerLong() });
444
- context.zoom(context._googleMap.getZoom());
445
- context._prevZoom = context.zoom();
446
- context._overlay.draw();
447
- });
448
- this._googleMap.addListener("zoom_changed", function () {
449
- context.zoom(context._googleMap.zoom);
450
- context._prevZoom = context.zoom();
451
- });
452
- this._googleStreetViewService = new google.maps.StreetViewService();
453
- this._googleMapPanorama = this._googleMap.getStreetView();
454
- this._googleMapPanorama.addListener("visible_changed", function () {
455
- context.streetView(context._googleMapPanorama.getVisible());
456
- context._prevStreetView = context.streetView();
457
- });
458
-
459
- this._circleMap = d3Map([]);
460
- this._pinMap = d3Map([]);
461
-
462
- this._prevCenterLat = this.centerLat();
463
- this._prevCenterLong = this.centerLong();
464
- this._prevZoom = this.zoom();
465
-
466
- // Init drawing tools with default options.
467
- const defOptions = {
468
- strokeWeight: 0,
469
- fillOpacity: 0.45,
470
- fillColor: "#1f77b4",
471
- editable: true,
472
- clickable: true
473
- };
474
- this._drawingManager = new google.maps.drawing.DrawingManager({
475
- drawingMode: google.maps.drawing.OverlayType.MARKER,
476
- drawingControl: true,
477
- drawingControlOptions: {
478
- position: google.maps.ControlPosition.TOP_CENTER,
479
- drawingModes: ["polygon", "rectangle", "circle"]
480
- },
481
- rectangleOptions: defOptions,
482
- circleOptions: defOptions,
483
- polygonOptions: defOptions
484
- });
485
-
486
- if (this.drawingState()) {
487
- this._userShapes.load(this.drawingState());
488
- }
489
- }
490
-
491
- update(domNode, element) {
492
- const context = this;
493
- this._googleMapNode
494
- .style("width", this.width() + "px")
495
- .style("height", this.height() + "px")
496
- ;
497
-
498
- this._googleMap.setMapTypeId(this.getMapType());
499
- this._googleMap.setOptions(this.getMapOptions());
500
-
501
- if (this.centerAddress_exists() && this._prevCenterAddress !== this.centerAddress()) {
502
- this._prevCenterAddress = this.centerAddress();
503
- this._googleGeocoder.geocode({ address: this.centerAddress() }, function (results, status) {
504
- if (status === google.maps.GeocoderStatus.OK) {
505
- const bounds = results[0].geometry.bounds || results[0].geometry.viewport;
506
- context._googleMap.fitBounds(bounds);
507
- if (context.streetView() && context.useComputedHeading()) {
508
- context.streetViewAt({
509
- lat: results[0].geometry.location.lat(),
510
- lng: results[0].geometry.location.lng()
511
- }, 50);
512
- }
513
- } else {
514
- context.statusError({
515
- data: results,
516
- status
517
- });
518
- }
519
- });
520
- }
521
- if (this._prevCenterLat !== this.centerLat() || this._prevCenterLong !== this.centerLong()) {
522
- this._googleMap.setCenter(new google.maps.LatLng(this.centerLat(), this.centerLong()));
523
-
524
- this._prevCenterLat = this.centerLat();
525
- this._prevCenterLong = this.centerLong();
526
- }
527
- if (this._prevZoom !== this.zoom()) {
528
- this._googleMap.setZoom(this.zoom());
529
-
530
- this._prevZoom = this.zoom();
531
- }
532
- this.updateCircles();
533
- this.updatePins();
534
- if (this._prevStreetView !== this.streetView()) {
535
- if (this.streetView()) {
536
- this._googleMapPanorama.setPosition({ lat: this.centerLat(), lng: this.centerLong() });
537
- this._googleMapPanorama.setPov({
538
- heading: 0,
539
- pitch: 0
540
- });
541
- this._googleMapPanorama.setVisible(true);
542
- } else {
543
- this._googleMapPanorama.setVisible(false);
544
- }
545
- this._prevStreetView = this.streetView();
546
- }
547
-
548
- // Enable or disable drawing tools.
549
- if (this.drawingTools()) {
550
- this._drawingManager.setMap(this._googleMap);
551
-
552
- // Add drawing complete listener to maintain array of drawingState.
553
- google.maps.event.addListener(
554
- this._drawingManager,
555
- "overlaycomplete",
556
- function () {
557
- GMap.prototype.onDrawingComplete.apply(context, arguments);
558
- });
559
- } else {
560
- this._drawingManager.setMap(null);
561
- google.maps.event.clearInstanceListeners(this._drawingManager);
562
- }
563
- }
564
-
565
- render(callback?) {
566
- const context = this;
567
- const args = arguments;
568
- requireGoogleMap().then(() => {
569
- super.render.apply(context, args);
570
- });
571
- return this;
572
- }
573
- streetViewAt(pos, radius = 1000) {
574
- const context = this;
575
- const source = this.outdoorStreetViewOnly() ? google.maps.StreetViewSource.OUTDOOR : google.maps.StreetViewSource.DEFAULT;
576
- this._googleStreetViewService.getPanorama({ location: pos, radius, source }, function (data, status) {
577
- if (status === "OK") {
578
- const marker = new google.maps.Marker({
579
- position: pos,
580
- map: context._googleMap
581
- });
582
- const heading = google.maps.geometry.spherical.computeHeading(data.location.latLng, new google.maps.LatLng(pos.lat, pos.lng));
583
- context._googleMapPanorama.setPano(data.location.pano);
584
-
585
- context._googleMapPanorama.setPov({
586
- heading,
587
- pitch: 0
588
- });
589
- if (!context.showStreetViewMarker()) {
590
- marker.setVisible(false);
591
- }
592
- context._googleMapPanorama.setVisible(true);
593
- const listener = google.maps.event.addListener(context._googleMap.getStreetView(), "visible_changed", function () {
594
- if (!this.getVisible()) {
595
- marker.setMap(null);
596
- google.maps.event.removeListener(listener);
597
- }
598
- });
599
- } else {
600
- context.statusError({ data, status });
601
- }
602
- });
603
- }
604
-
605
- updateCircles() {
606
- function rowID(row) {
607
- return row[0] + "_" + row[1];
608
- }
609
-
610
- const circle_enter = [];
611
- const circle_update = [];
612
- const circle_exit = d3Map(this._circleMap.keys(), function (d: any) { return d; });
613
- this.data().forEach(function (row) {
614
- circle_exit.remove(rowID(row));
615
- if (row[3] && !this._circleMap.has(rowID(row))) {
616
- circle_enter.push(row);
617
- } else if (row[3] && this._circleMap.has(rowID(row))) {
618
- circle_update.push(row);
619
- } else if (!row[3] && this._circleMap.has(rowID(row))) {
620
- circle_exit.set(rowID(row), true);
621
- }
622
- }, this);
623
-
624
- circle_enter.forEach(function (row) {
625
- const marker = this.createCircle(row[0], row[1], row[3]);
626
- this._circleMap.set(rowID(row), marker);
627
- }, this);
628
-
629
- circle_update.forEach(function (row) {
630
- // this._pinMap.get(rowID(row)).setIcon(this.createIcon(row[3]));
631
- }, this);
632
-
633
- const context = this;
634
- circle_exit.each(function (row) {
635
- context._circleMap.get(row).setMap(null);
636
- context._circleMap.remove(row);
637
- });
638
- }
639
-
640
- updatePins() {
641
- function rowID(row) {
642
- return row[0] + "_" + row[1];
643
- }
644
-
645
- const pin_enter = [];
646
- const pin_update = [];
647
- const pin_exit = d3Map(this._pinMap.keys(), function (d: any) { return d; });
648
- this.data().forEach(function (row) {
649
- pin_exit.remove(rowID(row));
650
- if (row[2] && !this._pinMap.has(rowID(row))) {
651
- pin_enter.push(row);
652
- } else if (row[2] && this._pinMap.has(rowID(row))) {
653
- pin_update.push(row);
654
- } else if (!row[2] && this._pinMap.has(rowID(row))) {
655
- pin_exit.set(rowID(row), true);
656
- }
657
- }, this);
658
-
659
- pin_enter.forEach(function (row) {
660
- const marker = this.createMarker(row[0], row[1], row[2]);
661
- this._pinMap.set(rowID(row), marker);
662
- }, this);
663
-
664
- pin_update.forEach(function (row) {
665
- this._pinMap.get(rowID(row)).setIcon(this.createIcon(row[2]));
666
- }, this);
667
-
668
- const context = this;
669
- pin_exit.each(function (row) {
670
- context._pinMap.get(row).setMap(null);
671
- context._pinMap.remove(row);
672
- });
673
- }
674
-
675
- createIcon(pinObj: { fillColor: string; fillOpacity?: number; strokeColor?: string }) {
676
- return {
677
- path: "M 0,0 C -2,-20 -10,-22 -10,-30 A 10,10 0 1,1 10,-30 C 10,-22 2,-20 0,0 z M -2,-30", // a 2,2 0 1,1 4,0 2,2 0 1,1",
678
- fillColor: pinObj.fillColor,
679
- fillOpacity: pinObj.fillOpacity || 0.8,
680
- scale: 0.5,
681
- strokeColor: pinObj.strokeColor || "black",
682
- strokeWeight: 0.25
683
- };
684
- }
685
-
686
- createMarker(lat, lng, pinObj: { fillColor: string; fillOpacity?: number; strokeColor?: string; title?: string }) {
687
- return new google.maps.Marker({
688
- position: new google.maps.LatLng(lat, lng),
689
- animation: google.maps.Animation.DROP,
690
- title: pinObj.title || "",
691
- icon: this.createIcon(pinObj),
692
- map: this._googleMap
693
- });
694
- }
695
-
696
- createCircle(lat, lng, circleObj: { radius?: number; fillColor?: string; strokeColor?: string }) {
697
- circleObj.radius = circleObj.radius || 1;
698
- return new google.maps.Circle({
699
- center: new google.maps.LatLng(lat, lng),
700
- radius: 16093 * circleObj.radius / 10, // 16093 === 10 miles in metres
701
- fillColor: circleObj.fillColor || "red",
702
- strokeColor: circleObj.strokeColor || circleObj.fillColor || "black",
703
- strokeWeight: 0.5,
704
- map: this._googleMap
705
- });
706
- }
707
-
708
- zoomTo(selection, singleMaxZoom?) {
709
- if (!this._renderCount) return this;
710
- singleMaxZoom = singleMaxZoom || this.singleZoomToMaxZoom();
711
- let foundCount = 0;
712
- const latlngbounds = new google.maps.LatLngBounds();
713
- selection.forEach(function (item) {
714
- const gLatLong = new google.maps.LatLng(item[0], item[1]);
715
- latlngbounds.extend(gLatLong);
716
- ++foundCount;
717
- });
718
- switch (foundCount) {
719
- case 0:
720
- break;
721
- case 1:
722
- this._googleMap.setCenter(latlngbounds.getCenter());
723
- this._googleMap.setZoom(singleMaxZoom);
724
- break;
725
- default:
726
- this._googleMap.fitBounds(latlngbounds);
727
- }
728
- return this;
729
- }
730
-
731
- zoomToFit() {
732
- return this.zoomTo(this.data());
733
- }
734
-
735
- drawingOptions(_) {
736
- if (!arguments.length) {
737
- return this._drawingManager;
738
- }
739
- this._drawingManager.setOptions(_);
740
- return this;
741
- }
742
-
743
- userShapeSelection(_) {
744
- if (!arguments.length) return this._userShapeSelection;
745
- if (this._userShapeSelection) {
746
- this._userShapeSelection.setEditable(false);
747
- }
748
- this._userShapeSelection = _;
749
- if (this._userShapeSelection) {
750
- this._userShapeSelection.setEditable(true);
751
- }
752
- return this;
753
- }
754
-
755
- deleteUserShape(_) {
756
- if (this._userShapeSelection === _) {
757
- this.userShapeSelection(null);
758
- }
759
- this._userShapes.remove(_);
760
- }
761
-
762
- onDrawingComplete(event) {
763
- if (event.type !== google.maps.drawing.OverlayType.MARKER) {
764
- this._drawingManager.setDrawingMode(null);
765
- const newShape = event.overlay;
766
- newShape.__hpcc_type = event.type;
767
- this._userShapes.add(newShape);
768
- const context = this;
769
- let ctrl = false;
770
- window.addEventListener("keydown", function (e: any) {
771
- if (e.keyIdentifier === "Control" || e.ctrlKey === true) {
772
- ctrl = true;
773
- }
774
- });
775
- window.addEventListener("keyup", function (e) {
776
- if (e.ctrlKey === false) {
777
- ctrl = false;
778
- }
779
- });
780
- google.maps.event.addListener(newShape, "click", function (ev) {
781
- context.userShapeSelection(newShape);
782
- if (ev && ctrl === true) {
783
- context.deleteUserShape(newShape);
784
- context.drawingState(
785
- JSON.stringify(context._userShapes.save()));
786
- }
787
- return false;
788
- });
789
- this.userShapeSelection(newShape);
790
- this.drawingState(
791
- JSON.stringify(this._userShapes.save()));
792
- }
793
- }
794
- statusError(response) {
795
- console.warn("Data not found for this location.");
796
- }
797
- }
798
- GMap.prototype._class += " map_GMap";
799
-
800
- export interface GMap {
801
- type(): string;
802
- type(_: string): this;
803
- type_exists(): boolean;
804
- centerLat(): number;
805
- centerLat(_: number);
806
- centerLat_exists(): boolean;
807
- centerLong(): number;
808
- centerLong(_: number): this;
809
- centerLong_exists(): boolean;
810
- centerAddress(): string;
811
- centerAddress(_: string): this;
812
- centerAddress_exists(): boolean;
813
- zoom(): number;
814
- zoom(_: number): this;
815
- zoom_exists(): boolean;
816
- singleZoomToMaxZoom(): number;
817
- singleZoomToMaxZoom(_: number): this;
818
- panControl(): boolean;
819
- panControl(_: boolean): this;
820
- panControl_exists(): boolean;
821
- zoomControl(): boolean;
822
- zoomControl(_: boolean): this;
823
- zoomControl_exists(): boolean;
824
- scaleControl(): boolean;
825
- scaleControl(_: boolean): this;
826
- scaleControl_exists(): boolean;
827
- mapTypeControl(): boolean;
828
- mapTypeControl(_: boolean): this;
829
- mapTypeControl_exists(): boolean;
830
- fullscreenControl(): boolean;
831
- fullscreenControl(_: boolean): this;
832
- fullscreenControl_exists(): boolean;
833
- streetViewControl(): boolean;
834
- streetViewControl(_: boolean): this;
835
- streetViewControl_exists(): boolean;
836
- overviewMapControl(): boolean;
837
- overviewMapControl(_: boolean): this;
838
- overviewMapControl_exists(): boolean;
839
- streetView(): boolean;
840
- streetView(_: boolean): this;
841
- streetView_exists(): boolean;
842
- drawingTools(): boolean;
843
- drawingTools(_: boolean): this;
844
- drawingTools_exists(): boolean;
845
- drawingState(): string;
846
- drawingState(_: string): this;
847
- drawingState_exists(): boolean;
848
- useComputedHeading(): boolean;
849
- useComputedHeading(_: boolean): this;
850
- showStreetViewMarker(): boolean;
851
- showStreetViewMarker(_: boolean): this;
852
- outdoorStreetViewOnly(): boolean;
853
- outdoorStreetViewOnly(_: boolean): this;
854
-
855
- googleMapStyles(): object;
856
- googleMapStyles(_: object): this;
857
- googleMapStyles_exists(): boolean;
858
- }
859
-
860
- GMap.prototype.publish("outdoorStreetViewOnly", false, "boolean", "If true, streetView will only display outdoor locations");
861
- GMap.prototype.publish("showStreetViewMarker", false, "boolean", "If true, streetView marker will be hidden");
862
- GMap.prototype.publish("useComputedHeading", false, "boolean", "If true, centerAddress streetView compute the ideal panorama heading");
863
- GMap.prototype.publish("type", "road", "set", "Map Type", ["terrain", "road", "satellite", "hybrid"], { tags: ["Basic"] });
864
- GMap.prototype.publish("centerLat", 42.877742, "number", "Center Latitude", null, { tags: ["Basic"] });
865
- GMap.prototype.publish("centerLong", -97.380979, "number", "Center Longitude", null, { tags: ["Basic"] });
866
- GMap.prototype.publish("centerAddress", null, "string", "Address to center map on", null, { tags: ["Basic"], optional: true });
867
- GMap.prototype.publish("zoom", 4, "number", "Zoom Level", null, { tags: ["Basic"] });
868
- GMap.prototype.publish("singleZoomToMaxZoom", 14, "number", "Max zoomTo level with single item");
869
- GMap.prototype.publish("panControl", true, "boolean", "Pan Controls", null, { tags: ["Basic"] });
870
- GMap.prototype.publish("zoomControl", true, "boolean", "Zoom Controls", null, { tags: ["Basic"] });
871
- GMap.prototype.publish("scaleControl", true, "boolean", "Scale Controls", null, { tags: ["Basic"] });
872
- GMap.prototype.publish("mapTypeControl", false, "boolean", "Map Type Controls", null, { tags: ["Basic"] });
873
- GMap.prototype.publish("fullscreenControl", false, "boolean", "Fullscreen Controls", null, { tags: ["Basic"] });
874
- GMap.prototype.publish("streetViewControl", false, "boolean", "StreetView Controls", null, { tags: ["Basic"] });
875
- GMap.prototype.publish("overviewMapControl", false, "boolean", "OverviewMap Controls", null, { tags: ["Basic"] });
876
- GMap.prototype.publish("streetView", false, "boolean", "Streetview", null, { tags: ["Basic"] });
877
- GMap.prototype.publish("drawingTools", false, "boolean", "Drawing Tools", null, { tags: ["Basic"] });
878
- GMap.prototype.publish("drawingState", "", "string", "Map Drawings", null, { disable: w => w.drawingTools() === false });
879
-
880
- GMap.prototype.publish("googleMapStyles", {}, "object", "Styling for map colors etc", null, { tags: ["Basic"] });
1
+ import { HTMLWidget } from "@hpcc-js/common";
2
+ import { AbsoluteSurface } from "@hpcc-js/layout";
3
+ import { promiseTimeout } from "@hpcc-js/util";
4
+ import { map as d3Map } from "d3-collection";
5
+ import * as _GoogleMapsLoader from "google-maps";
6
+
7
+ const GoogleMapsLoader = _GoogleMapsLoader.default || _GoogleMapsLoader;
8
+
9
+ import "../src/GMap.css";
10
+
11
+ declare const window: any;
12
+
13
+ export let google: any = null;
14
+
15
+ let timout = 15000;
16
+ export function requireGoogleMapTimeout(ms: number) {
17
+ timout = ms;
18
+ }
19
+
20
+ let _googleMapPromise;
21
+ export function requireGoogleMap(customGoogle?: any) {
22
+ if (!_googleMapPromise) {
23
+ _googleMapPromise = promiseTimeout(timout, new Promise<void>(function (resolve, reject) {
24
+ if (google) {
25
+ resolve();
26
+ } else if (customGoogle) {
27
+ google = customGoogle;
28
+ resolve();
29
+ } else {
30
+ if (!window.__hpcc_gmap_apikey) {
31
+ console.warn("__hpcc_gmap_apikey does not contain a valid API key, reverting to developers key (expect limited performance)");
32
+ }
33
+ try {
34
+ GoogleMapsLoader.KEY = window.__hpcc_gmap_apikey || "AIzaSyDwGn2i1i_pMZvnqYJN1BksD_tjYaCOWKg";
35
+ GoogleMapsLoader.LIBRARIES = ["geometry", "drawing"];
36
+ GoogleMapsLoader.load(function (_google) {
37
+ google = _google;
38
+ resolve();
39
+ });
40
+ } catch (e) {
41
+ console.warn(`Failed to initialize Google Map API: ${e.message}`);
42
+ resolve();
43
+ }
44
+ }
45
+ })).catch(e => {
46
+ console.warn(`Timed out initializing Google Map API after ${timout}ms.`);
47
+ });
48
+ }
49
+ return _googleMapPromise;
50
+ }
51
+
52
+ function createOverlay(map, worldSurface, viewportSurface) {
53
+ function Overlay(map2, worldSurface2, viewportSurface2) {
54
+ google.maps.OverlayView.call(this);
55
+ this._div = null;
56
+
57
+ this._worldSurface = worldSurface2;
58
+ this._viewportSurface = viewportSurface2;
59
+
60
+ this._map = map2;
61
+ this.setMap(map2);
62
+
63
+ const context = this;
64
+ google.maps.event.addListener(map2, "bounds_changed", function () {
65
+ context.draw();
66
+ });
67
+ google.maps.event.addListener(map2, "projection_changed", function () {
68
+ context.draw();
69
+ });
70
+
71
+ this._prevWorldMin = { x: 0, y: 0 };
72
+ this._prevWorldMax = { x: 0, y: 0 };
73
+ this._prevMin = { x: 0, y: 0 };
74
+ this._prevMax = { x: 0, y: 0 };
75
+ }
76
+ Overlay.prototype = google.maps.OverlayView.prototype;
77
+
78
+ Overlay.prototype.onAdd = function () {
79
+ this.div = document.createElement("div");
80
+
81
+ this._viewportSurface.target(null)
82
+ .target(this.div)
83
+ .units("pixels");
84
+
85
+ const panes = this.getPanes();
86
+ panes.overlayMouseTarget.appendChild(this.div);
87
+ };
88
+
89
+ Overlay.prototype.draw = function () {
90
+ const projection = this.getProjection();
91
+ if (!projection)
92
+ return;
93
+
94
+ const bounds = this._map.getBounds();
95
+ const center = projection.fromLatLngToDivPixel(bounds.getCenter());
96
+ const sw = projection.fromLatLngToDivPixel(bounds.getSouthWest());
97
+ const ne = projection.fromLatLngToDivPixel(bounds.getNorthEast());
98
+
99
+ const min = {
100
+ x: sw.x,
101
+ y: ne.y
102
+ };
103
+ const max = {
104
+ x: ne.x,
105
+ y: sw.y
106
+ };
107
+
108
+ const worldWidth = projection.getWorldWidth();
109
+ while (max.x < min.x + 100) { // Ignoe dateline from being the rect.
110
+ max.x += worldWidth;
111
+ }
112
+ while (min.x > center.x) {
113
+ min.x -= worldWidth;
114
+ max.x -= worldWidth;
115
+ }
116
+
117
+ if (min.x !== this._prevMin.x || min.y !== this._prevMin.y || max.x !== this._prevMax.x || max.y !== this._prevMax.y) {
118
+ this._viewportSurface
119
+ .resize({ width: 0, height: 0 })
120
+ .widgetX(min.x)
121
+ .widgetY(min.y)
122
+ .widgetWidth(max.x - min.x)
123
+ .widgetHeight(max.y - min.y)
124
+ ;
125
+ // FF Issue on initial render (GH-1855) ---
126
+ if (this._viewportSurface._renderCount) {
127
+ this._viewportSurface.render();
128
+ this._prevMin = min;
129
+ this._prevMax = max;
130
+ } else {
131
+ this._viewportSurface.lazyRender();
132
+ }
133
+ }
134
+
135
+ const worldMin = projection.fromLatLngToDivPixel(new google.maps.LatLng(85, -179.9));
136
+ const worldMax = projection.fromLatLngToDivPixel(new google.maps.LatLng(-85, 179.9));
137
+ while (worldMax.x < worldMin.x + 100) { // Ignoe dateline from being the rect.
138
+ worldMax.x += worldWidth;
139
+ }
140
+ while (worldMin.x > center.x) {
141
+ worldMin.x -= worldWidth;
142
+ worldMax.x -= worldWidth;
143
+ }
144
+ if (worldMin.x !== this._prevWorldMin.x || worldMin.y !== this._prevWorldMin.y || worldMax.x !== this._prevWorldMax.x || worldMax.y !== this._prevWorldMax.y) {
145
+ this._worldSurface
146
+ .widgetX(worldMin.x)
147
+ .widgetY(worldMin.y)
148
+ .widgetWidth(worldMax.x - worldMin.x)
149
+ .widgetHeight(worldMax.y - worldMin.y)
150
+ .render()
151
+ ;
152
+ this._prevWorldMin = worldMax;
153
+ this._prevWorldMax = worldMax;
154
+ }
155
+ };
156
+
157
+ Overlay.prototype.onRemove = function () {
158
+ this._viewportSurface.target(null);
159
+ this._div.parentNode.removeChild(this._div);
160
+ this._div = null;
161
+ };
162
+
163
+ return new Overlay(map, worldSurface, viewportSurface);
164
+ }
165
+
166
+ class UserShapeSelectionBag {
167
+ _userShapes: any[];
168
+ mapContext;
169
+
170
+ constructor(mapObj) {
171
+ this._userShapes = [];
172
+ this.mapContext = mapObj;
173
+ }
174
+
175
+ add(_) {
176
+ const idx = this._userShapes.indexOf(_);
177
+ if (idx >= 0) {
178
+ return;
179
+ }
180
+ this._userShapes.push(_);
181
+ }
182
+
183
+ remove(_) {
184
+ const idx = this._userShapes.indexOf(_);
185
+ if (idx >= 0) {
186
+ this._userShapes.splice(idx, 1);
187
+ }
188
+ _.setMap(null);
189
+ }
190
+
191
+ save() {
192
+ return this._userShapes.map(shape => this._saveShape(shape));
193
+ }
194
+
195
+ load(_) {
196
+ this._deserializeShapes(_);
197
+ }
198
+
199
+ _saveShape(shape) {
200
+ const retVal: any = {};
201
+
202
+ const createShapes = {
203
+ circle: (_) => {
204
+ retVal.type = "circle";
205
+ retVal.pos = {
206
+ lat: _.center.lat(),
207
+ lng: _.center.lng()
208
+ };
209
+ retVal.radius = _.radius;
210
+ },
211
+ rectangle: (_) => {
212
+ retVal.type = "rectangle";
213
+ retVal.bounds = {
214
+ ne: _.bounds.getNorthEast(),
215
+ sw: _.bounds.getSouthWest()
216
+ };
217
+ },
218
+ polygon: (_) => {
219
+ retVal.type = _.__hpcc_type;
220
+
221
+ const vertices = _.getPath();
222
+
223
+ retVal.vertices = [];
224
+
225
+ for (let i = 0; i < vertices.length; i++) {
226
+ retVal.vertices.push(vertices.getAt(i));
227
+ }
228
+ },
229
+ polyline: (_) => {
230
+ createShapes.polygon(_);
231
+ }
232
+ };
233
+
234
+ createShapes[shape.__hpcc_type](shape);
235
+ retVal.strokeWeight = shape.strokeWeight;
236
+ retVal.fillColor = shape.fillColor;
237
+ retVal.fillOpacity = shape.fillOpacity;
238
+ retVal.editable = shape.editable;
239
+ retVal.clickable = shape.clickable || true;
240
+
241
+ return retVal;
242
+ }
243
+
244
+ _deserializeShapes(_shapes) {
245
+
246
+ const shapes = JSON.parse(_shapes);
247
+
248
+ const defOptions = {
249
+ strokeWeight: 0,
250
+ fillOpacity: 0.45,
251
+ fillColor: "#1f77b4",
252
+ editable: true,
253
+ clickable: true
254
+ };
255
+
256
+ const createShapes = {
257
+ circle: (_, map) => {
258
+ const shape = new google.maps.Circle({
259
+ strokeWeight: _.strokeWeight || defOptions.strokeWeight,
260
+ fillColor: _.fillColor || defOptions.fillColor,
261
+ fillOpacity: _.fillOpacity || defOptions.fillOpacity,
262
+ editable: _.editable || defOptions.editable,
263
+ clickable: _.clickable || defOptions.clickable,
264
+ map,
265
+ center: _.pos,
266
+ radius: _.radius
267
+ });
268
+ return shape;
269
+ },
270
+ rectangle: (_, map) => {
271
+ const shape = new google.maps.Rectangle({
272
+ strokeWeight: _.strokeWeight || defOptions.strokeWeight,
273
+ fillColor: _.fillColor || defOptions.fillColor,
274
+ fillOpacity: _.fillOpacity || defOptions.fillOpacity,
275
+ editable: _.editable || defOptions.editable,
276
+ clickable: _.clickable || defOptions.clickable,
277
+ map,
278
+ bounds: {
279
+ north: _.bounds.ne.lat,
280
+ west: _.bounds.sw.lng,
281
+ south: _.bounds.sw.lat,
282
+ east: _.bounds.ne.lng
283
+ }
284
+ });
285
+ return shape;
286
+ },
287
+ polygon: (_, map) => {
288
+ const shape = new google.maps.Polygon({
289
+ strokeWeight: _.strokeWeight || defOptions.strokeWeight,
290
+ fillColor: _.fillColor || defOptions.fillColor,
291
+ fillOpacity: _.fillOpacity || defOptions.fillOpacity,
292
+ editable: _.editable || defOptions.editable,
293
+ clickable: _.clickable || defOptions.clickable,
294
+ map,
295
+ paths: _.vertices
296
+ });
297
+ return shape;
298
+ },
299
+ polyline: (_, map) => {
300
+ const shape = new google.maps.Polyline({
301
+ strokeWeight: _.strokeWeight || defOptions.strokeWeight,
302
+ fillColor: _.fillColor || defOptions.fillColor,
303
+ fillOpacity: _.fillOpacity || defOptions.fillOpacity,
304
+ editable: _.editable || defOptions.editable,
305
+ clickable: _.clickable || defOptions.clickable,
306
+ map,
307
+ path: _.vertices
308
+ });
309
+ return shape;
310
+ }
311
+ };
312
+
313
+ for (let i = 0; i < shapes.length; i++) {
314
+ const shape = createShapes[shapes[i].type](shapes[i], this.mapContext._googleMap);
315
+ this.mapContext.onDrawingComplete({ type: shapes[i].type, overlay: shape });
316
+ }
317
+ }
318
+ }
319
+
320
+ export class GMap extends HTMLWidget {
321
+ _overlay;
322
+ _userShapes;
323
+ _worldSurface;
324
+ _viewportSurface;
325
+ _googleMapNode;
326
+ _googleMap;
327
+ _googleGeocoder;
328
+ _prevCenterLat;
329
+ _prevCenterLong;
330
+ _googleStreetViewService;
331
+ _googleMapPanorama;
332
+ _prevZoom;
333
+ _prevStreetView;
334
+ _circleMap;
335
+ _pinMap;
336
+ _drawingManager;
337
+ _prevCenterAddress;
338
+ _userShapeSelection;
339
+
340
+ constructor() {
341
+ super();
342
+
343
+ this._tag = "div";
344
+
345
+ const context = this;
346
+ function calcProjection(surface, lat, long) {
347
+ const projection = context._overlay.getProjection();
348
+ const retVal = projection.fromLatLngToDivPixel(new google.maps.LatLng(lat, long));
349
+ const worldWidth = projection.getWorldWidth();
350
+ const widgetX = parseFloat(surface.widgetX());
351
+ const widgetY = parseFloat(surface.widgetY());
352
+ const widgetWidth = parseFloat(surface.widgetWidth());
353
+ retVal.x -= widgetX;
354
+ retVal.y -= widgetY;
355
+ while (retVal.x < 0) {
356
+ retVal.x += worldWidth;
357
+ }
358
+ while (retVal.x > widgetWidth) {
359
+ retVal.x -= worldWidth;
360
+ }
361
+ return retVal;
362
+ }
363
+
364
+ this._userShapes = new UserShapeSelectionBag(this);
365
+
366
+ this._worldSurface = new AbsoluteSurface();
367
+ this._worldSurface.project = function (lat, long) {
368
+ return calcProjection(this, lat, long);
369
+ };
370
+
371
+ this._viewportSurface = new AbsoluteSurface();
372
+ this._viewportSurface.project = function (lat, long) {
373
+ return calcProjection(this, lat, long);
374
+ };
375
+ }
376
+
377
+ data(_?) {
378
+ const retVal = HTMLWidget.prototype.data.apply(this, arguments);
379
+ return retVal;
380
+ }
381
+
382
+ getMapType() {
383
+ switch (this.type()) {
384
+ case "terrain":
385
+ return google.maps.MapTypeId.TERRAIN;
386
+ case "road":
387
+ return google.maps.MapTypeId.ROADMAP;
388
+ case "satellite":
389
+ return google.maps.MapTypeId.SATELLITE;
390
+ case "hybrid":
391
+ return google.maps.MapTypeId.HYBRID;
392
+ default:
393
+ return google.maps.MapTypeId.ROADMAP;
394
+ }
395
+ }
396
+
397
+ getMapOptions() {
398
+ return {
399
+ panControl: this.panControl(),
400
+ zoomControl: this.zoomControl(),
401
+ fullscreenControl: this.fullscreenControl(),
402
+ mapTypeControl: this.mapTypeControl(),
403
+ scaleControl: this.scaleControl(),
404
+ streetViewControl: this.streetViewControl(),
405
+ overviewMapControl: this.overviewMapControl(),
406
+ overviewMapControlOptions: { opened: true },
407
+ styles: this.googleMapStyles()
408
+ };
409
+ }
410
+
411
+ size(_?) {
412
+ const retVal = HTMLWidget.prototype.size.apply(this, arguments);
413
+ if (arguments.length && this._googleMapNode) {
414
+ this._googleMapNode
415
+ .style("width", _.width + "px")
416
+ .style("height", _.height + "px")
417
+ ;
418
+ google.maps.event.trigger(this._googleMap, "resize");
419
+ }
420
+ return retVal;
421
+ }
422
+
423
+ enter(domNode, element) {
424
+ super.enter(domNode, element);
425
+ const context = this;
426
+ this._googleGeocoder = new google.maps.Geocoder();
427
+ this._googleMapNode = element.append("div")
428
+ .style("width", this.width() + "px")
429
+ .style("height", this.height() + "px")
430
+ ;
431
+ this._googleMap = new google.maps.Map(this._googleMapNode.node(), {
432
+ zoom: this.zoom(),
433
+ center: new google.maps.LatLng(this.centerLat(), this.centerLong()),
434
+ mapTypeId: this.getMapType(),
435
+ disableDefaultUI: true
436
+ });
437
+ this._overlay = createOverlay(this._googleMap, this._worldSurface, this._viewportSurface);
438
+ this._googleMap.addListener("center_changed", function () {
439
+ context.centerLat(context._googleMap.center.lat());
440
+ context._prevCenterLat = context.centerLat();
441
+ context.centerLong(context._googleMap.center.lng());
442
+ context._prevCenterLong = context.centerLong();
443
+ context._googleMapPanorama.setPosition({ lat: context.centerLat(), lng: context.centerLong() });
444
+ context.zoom(context._googleMap.getZoom());
445
+ context._prevZoom = context.zoom();
446
+ context._overlay.draw();
447
+ });
448
+ this._googleMap.addListener("zoom_changed", function () {
449
+ context.zoom(context._googleMap.zoom);
450
+ context._prevZoom = context.zoom();
451
+ });
452
+ this._googleStreetViewService = new google.maps.StreetViewService();
453
+ this._googleMapPanorama = this._googleMap.getStreetView();
454
+ this._googleMapPanorama.addListener("visible_changed", function () {
455
+ context.streetView(context._googleMapPanorama.getVisible());
456
+ context._prevStreetView = context.streetView();
457
+ });
458
+
459
+ this._circleMap = d3Map([]);
460
+ this._pinMap = d3Map([]);
461
+
462
+ this._prevCenterLat = this.centerLat();
463
+ this._prevCenterLong = this.centerLong();
464
+ this._prevZoom = this.zoom();
465
+
466
+ // Init drawing tools with default options.
467
+ const defOptions = {
468
+ strokeWeight: 0,
469
+ fillOpacity: 0.45,
470
+ fillColor: "#1f77b4",
471
+ editable: true,
472
+ clickable: true
473
+ };
474
+ this._drawingManager = new google.maps.drawing.DrawingManager({
475
+ drawingMode: google.maps.drawing.OverlayType.MARKER,
476
+ drawingControl: true,
477
+ drawingControlOptions: {
478
+ position: google.maps.ControlPosition.TOP_CENTER,
479
+ drawingModes: ["polygon", "rectangle", "circle"]
480
+ },
481
+ rectangleOptions: defOptions,
482
+ circleOptions: defOptions,
483
+ polygonOptions: defOptions
484
+ });
485
+
486
+ if (this.drawingState()) {
487
+ this._userShapes.load(this.drawingState());
488
+ }
489
+ }
490
+
491
+ update(domNode, element) {
492
+ const context = this;
493
+ this._googleMapNode
494
+ .style("width", this.width() + "px")
495
+ .style("height", this.height() + "px")
496
+ ;
497
+
498
+ this._googleMap.setMapTypeId(this.getMapType());
499
+ this._googleMap.setOptions(this.getMapOptions());
500
+
501
+ if (this.centerAddress_exists() && this._prevCenterAddress !== this.centerAddress()) {
502
+ this._prevCenterAddress = this.centerAddress();
503
+ this._googleGeocoder.geocode({ address: this.centerAddress() }, function (results, status) {
504
+ if (status === google.maps.GeocoderStatus.OK) {
505
+ const bounds = results[0].geometry.bounds || results[0].geometry.viewport;
506
+ context._googleMap.fitBounds(bounds);
507
+ if (context.streetView() && context.useComputedHeading()) {
508
+ context.streetViewAt({
509
+ lat: results[0].geometry.location.lat(),
510
+ lng: results[0].geometry.location.lng()
511
+ }, 50);
512
+ }
513
+ } else {
514
+ context.statusError({
515
+ data: results,
516
+ status
517
+ });
518
+ }
519
+ });
520
+ }
521
+ if (this._prevCenterLat !== this.centerLat() || this._prevCenterLong !== this.centerLong()) {
522
+ this._googleMap.setCenter(new google.maps.LatLng(this.centerLat(), this.centerLong()));
523
+
524
+ this._prevCenterLat = this.centerLat();
525
+ this._prevCenterLong = this.centerLong();
526
+ }
527
+ if (this._prevZoom !== this.zoom()) {
528
+ this._googleMap.setZoom(this.zoom());
529
+
530
+ this._prevZoom = this.zoom();
531
+ }
532
+ this.updateCircles();
533
+ this.updatePins();
534
+ if (this._prevStreetView !== this.streetView()) {
535
+ if (this.streetView()) {
536
+ this._googleMapPanorama.setPosition({ lat: this.centerLat(), lng: this.centerLong() });
537
+ this._googleMapPanorama.setPov({
538
+ heading: 0,
539
+ pitch: 0
540
+ });
541
+ this._googleMapPanorama.setVisible(true);
542
+ } else {
543
+ this._googleMapPanorama.setVisible(false);
544
+ }
545
+ this._prevStreetView = this.streetView();
546
+ }
547
+
548
+ // Enable or disable drawing tools.
549
+ if (this.drawingTools()) {
550
+ this._drawingManager.setMap(this._googleMap);
551
+
552
+ // Add drawing complete listener to maintain array of drawingState.
553
+ google.maps.event.addListener(
554
+ this._drawingManager,
555
+ "overlaycomplete",
556
+ function () {
557
+ GMap.prototype.onDrawingComplete.apply(context, arguments);
558
+ });
559
+ } else {
560
+ this._drawingManager.setMap(null);
561
+ google.maps.event.clearInstanceListeners(this._drawingManager);
562
+ }
563
+ }
564
+
565
+ render(callback?) {
566
+ const context = this;
567
+ const args = arguments;
568
+ requireGoogleMap().then(() => {
569
+ super.render.apply(context, args);
570
+ });
571
+ return this;
572
+ }
573
+ streetViewAt(pos, radius = 1000) {
574
+ const context = this;
575
+ const source = this.outdoorStreetViewOnly() ? google.maps.StreetViewSource.OUTDOOR : google.maps.StreetViewSource.DEFAULT;
576
+ this._googleStreetViewService.getPanorama({ location: pos, radius, source }, function (data, status) {
577
+ if (status === "OK") {
578
+ const marker = new google.maps.Marker({
579
+ position: pos,
580
+ map: context._googleMap
581
+ });
582
+ const heading = google.maps.geometry.spherical.computeHeading(data.location.latLng, new google.maps.LatLng(pos.lat, pos.lng));
583
+ context._googleMapPanorama.setPano(data.location.pano);
584
+
585
+ context._googleMapPanorama.setPov({
586
+ heading,
587
+ pitch: 0
588
+ });
589
+ if (!context.showStreetViewMarker()) {
590
+ marker.setVisible(false);
591
+ }
592
+ context._googleMapPanorama.setVisible(true);
593
+ const listener = google.maps.event.addListener(context._googleMap.getStreetView(), "visible_changed", function () {
594
+ if (!this.getVisible()) {
595
+ marker.setMap(null);
596
+ google.maps.event.removeListener(listener);
597
+ }
598
+ });
599
+ } else {
600
+ context.statusError({ data, status });
601
+ }
602
+ });
603
+ }
604
+
605
+ updateCircles() {
606
+ function rowID(row) {
607
+ return row[0] + "_" + row[1];
608
+ }
609
+
610
+ const circle_enter = [];
611
+ const circle_update = [];
612
+ const circle_exit = d3Map(this._circleMap.keys(), function (d: any) { return d; });
613
+ this.data().forEach(function (row) {
614
+ circle_exit.remove(rowID(row));
615
+ if (row[3] && !this._circleMap.has(rowID(row))) {
616
+ circle_enter.push(row);
617
+ } else if (row[3] && this._circleMap.has(rowID(row))) {
618
+ circle_update.push(row);
619
+ } else if (!row[3] && this._circleMap.has(rowID(row))) {
620
+ circle_exit.set(rowID(row), true);
621
+ }
622
+ }, this);
623
+
624
+ circle_enter.forEach(function (row) {
625
+ const marker = this.createCircle(row[0], row[1], row[3]);
626
+ this._circleMap.set(rowID(row), marker);
627
+ }, this);
628
+
629
+ circle_update.forEach(function (row) {
630
+ // this._pinMap.get(rowID(row)).setIcon(this.createIcon(row[3]));
631
+ }, this);
632
+
633
+ const context = this;
634
+ circle_exit.each(function (row) {
635
+ context._circleMap.get(row).setMap(null);
636
+ context._circleMap.remove(row);
637
+ });
638
+ }
639
+
640
+ updatePins() {
641
+ function rowID(row) {
642
+ return row[0] + "_" + row[1];
643
+ }
644
+
645
+ const pin_enter = [];
646
+ const pin_update = [];
647
+ const pin_exit = d3Map(this._pinMap.keys(), function (d: any) { return d; });
648
+ this.data().forEach(function (row) {
649
+ pin_exit.remove(rowID(row));
650
+ if (row[2] && !this._pinMap.has(rowID(row))) {
651
+ pin_enter.push(row);
652
+ } else if (row[2] && this._pinMap.has(rowID(row))) {
653
+ pin_update.push(row);
654
+ } else if (!row[2] && this._pinMap.has(rowID(row))) {
655
+ pin_exit.set(rowID(row), true);
656
+ }
657
+ }, this);
658
+
659
+ pin_enter.forEach(function (row) {
660
+ const marker = this.createMarker(row[0], row[1], row[2]);
661
+ this._pinMap.set(rowID(row), marker);
662
+ }, this);
663
+
664
+ pin_update.forEach(function (row) {
665
+ this._pinMap.get(rowID(row)).setIcon(this.createIcon(row[2]));
666
+ }, this);
667
+
668
+ const context = this;
669
+ pin_exit.each(function (row) {
670
+ context._pinMap.get(row).setMap(null);
671
+ context._pinMap.remove(row);
672
+ });
673
+ }
674
+
675
+ createIcon(pinObj: { fillColor: string; fillOpacity?: number; strokeColor?: string }) {
676
+ return {
677
+ path: "M 0,0 C -2,-20 -10,-22 -10,-30 A 10,10 0 1,1 10,-30 C 10,-22 2,-20 0,0 z M -2,-30", // a 2,2 0 1,1 4,0 2,2 0 1,1",
678
+ fillColor: pinObj.fillColor,
679
+ fillOpacity: pinObj.fillOpacity || 0.8,
680
+ scale: 0.5,
681
+ strokeColor: pinObj.strokeColor || "black",
682
+ strokeWeight: 0.25
683
+ };
684
+ }
685
+
686
+ createMarker(lat, lng, pinObj: { fillColor: string; fillOpacity?: number; strokeColor?: string; title?: string }) {
687
+ return new google.maps.Marker({
688
+ position: new google.maps.LatLng(lat, lng),
689
+ animation: google.maps.Animation.DROP,
690
+ title: pinObj.title || "",
691
+ icon: this.createIcon(pinObj),
692
+ map: this._googleMap
693
+ });
694
+ }
695
+
696
+ createCircle(lat, lng, circleObj: { radius?: number; fillColor?: string; strokeColor?: string }) {
697
+ circleObj.radius = circleObj.radius || 1;
698
+ return new google.maps.Circle({
699
+ center: new google.maps.LatLng(lat, lng),
700
+ radius: 16093 * circleObj.radius / 10, // 16093 === 10 miles in metres
701
+ fillColor: circleObj.fillColor || "red",
702
+ strokeColor: circleObj.strokeColor || circleObj.fillColor || "black",
703
+ strokeWeight: 0.5,
704
+ map: this._googleMap
705
+ });
706
+ }
707
+
708
+ zoomTo(selection, singleMaxZoom?) {
709
+ if (!this._renderCount) return this;
710
+ singleMaxZoom = singleMaxZoom || this.singleZoomToMaxZoom();
711
+ let foundCount = 0;
712
+ const latlngbounds = new google.maps.LatLngBounds();
713
+ selection.forEach(function (item) {
714
+ const gLatLong = new google.maps.LatLng(item[0], item[1]);
715
+ latlngbounds.extend(gLatLong);
716
+ ++foundCount;
717
+ });
718
+ switch (foundCount) {
719
+ case 0:
720
+ break;
721
+ case 1:
722
+ this._googleMap.setCenter(latlngbounds.getCenter());
723
+ this._googleMap.setZoom(singleMaxZoom);
724
+ break;
725
+ default:
726
+ this._googleMap.fitBounds(latlngbounds);
727
+ }
728
+ return this;
729
+ }
730
+
731
+ zoomToFit() {
732
+ return this.zoomTo(this.data());
733
+ }
734
+
735
+ drawingOptions(_) {
736
+ if (!arguments.length) {
737
+ return this._drawingManager;
738
+ }
739
+ this._drawingManager.setOptions(_);
740
+ return this;
741
+ }
742
+
743
+ userShapeSelection(_) {
744
+ if (!arguments.length) return this._userShapeSelection;
745
+ if (this._userShapeSelection) {
746
+ this._userShapeSelection.setEditable(false);
747
+ }
748
+ this._userShapeSelection = _;
749
+ if (this._userShapeSelection) {
750
+ this._userShapeSelection.setEditable(true);
751
+ }
752
+ return this;
753
+ }
754
+
755
+ deleteUserShape(_) {
756
+ if (this._userShapeSelection === _) {
757
+ this.userShapeSelection(null);
758
+ }
759
+ this._userShapes.remove(_);
760
+ }
761
+
762
+ onDrawingComplete(event) {
763
+ if (event.type !== google.maps.drawing.OverlayType.MARKER) {
764
+ this._drawingManager.setDrawingMode(null);
765
+ const newShape = event.overlay;
766
+ newShape.__hpcc_type = event.type;
767
+ this._userShapes.add(newShape);
768
+ const context = this;
769
+ let ctrl = false;
770
+ window.addEventListener("keydown", function (e: any) {
771
+ if (e.keyIdentifier === "Control" || e.ctrlKey === true) {
772
+ ctrl = true;
773
+ }
774
+ });
775
+ window.addEventListener("keyup", function (e) {
776
+ if (e.ctrlKey === false) {
777
+ ctrl = false;
778
+ }
779
+ });
780
+ google.maps.event.addListener(newShape, "click", function (ev) {
781
+ context.userShapeSelection(newShape);
782
+ if (ev && ctrl === true) {
783
+ context.deleteUserShape(newShape);
784
+ context.drawingState(
785
+ JSON.stringify(context._userShapes.save()));
786
+ }
787
+ return false;
788
+ });
789
+ this.userShapeSelection(newShape);
790
+ this.drawingState(
791
+ JSON.stringify(this._userShapes.save()));
792
+ }
793
+ }
794
+ statusError(response) {
795
+ console.warn("Data not found for this location.");
796
+ }
797
+ }
798
+ GMap.prototype._class += " map_GMap";
799
+
800
+ export interface GMap {
801
+ type(): string;
802
+ type(_: string): this;
803
+ type_exists(): boolean;
804
+ centerLat(): number;
805
+ centerLat(_: number);
806
+ centerLat_exists(): boolean;
807
+ centerLong(): number;
808
+ centerLong(_: number): this;
809
+ centerLong_exists(): boolean;
810
+ centerAddress(): string;
811
+ centerAddress(_: string): this;
812
+ centerAddress_exists(): boolean;
813
+ zoom(): number;
814
+ zoom(_: number): this;
815
+ zoom_exists(): boolean;
816
+ singleZoomToMaxZoom(): number;
817
+ singleZoomToMaxZoom(_: number): this;
818
+ panControl(): boolean;
819
+ panControl(_: boolean): this;
820
+ panControl_exists(): boolean;
821
+ zoomControl(): boolean;
822
+ zoomControl(_: boolean): this;
823
+ zoomControl_exists(): boolean;
824
+ scaleControl(): boolean;
825
+ scaleControl(_: boolean): this;
826
+ scaleControl_exists(): boolean;
827
+ mapTypeControl(): boolean;
828
+ mapTypeControl(_: boolean): this;
829
+ mapTypeControl_exists(): boolean;
830
+ fullscreenControl(): boolean;
831
+ fullscreenControl(_: boolean): this;
832
+ fullscreenControl_exists(): boolean;
833
+ streetViewControl(): boolean;
834
+ streetViewControl(_: boolean): this;
835
+ streetViewControl_exists(): boolean;
836
+ overviewMapControl(): boolean;
837
+ overviewMapControl(_: boolean): this;
838
+ overviewMapControl_exists(): boolean;
839
+ streetView(): boolean;
840
+ streetView(_: boolean): this;
841
+ streetView_exists(): boolean;
842
+ drawingTools(): boolean;
843
+ drawingTools(_: boolean): this;
844
+ drawingTools_exists(): boolean;
845
+ drawingState(): string;
846
+ drawingState(_: string): this;
847
+ drawingState_exists(): boolean;
848
+ useComputedHeading(): boolean;
849
+ useComputedHeading(_: boolean): this;
850
+ showStreetViewMarker(): boolean;
851
+ showStreetViewMarker(_: boolean): this;
852
+ outdoorStreetViewOnly(): boolean;
853
+ outdoorStreetViewOnly(_: boolean): this;
854
+
855
+ googleMapStyles(): object;
856
+ googleMapStyles(_: object): this;
857
+ googleMapStyles_exists(): boolean;
858
+ }
859
+
860
+ GMap.prototype.publish("outdoorStreetViewOnly", false, "boolean", "If true, streetView will only display outdoor locations");
861
+ GMap.prototype.publish("showStreetViewMarker", false, "boolean", "If true, streetView marker will be hidden");
862
+ GMap.prototype.publish("useComputedHeading", false, "boolean", "If true, centerAddress streetView compute the ideal panorama heading");
863
+ GMap.prototype.publish("type", "road", "set", "Map Type", ["terrain", "road", "satellite", "hybrid"], { tags: ["Basic"] });
864
+ GMap.prototype.publish("centerLat", 42.877742, "number", "Center Latitude", null, { tags: ["Basic"] });
865
+ GMap.prototype.publish("centerLong", -97.380979, "number", "Center Longitude", null, { tags: ["Basic"] });
866
+ GMap.prototype.publish("centerAddress", null, "string", "Address to center map on", null, { tags: ["Basic"], optional: true });
867
+ GMap.prototype.publish("zoom", 4, "number", "Zoom Level", null, { tags: ["Basic"] });
868
+ GMap.prototype.publish("singleZoomToMaxZoom", 14, "number", "Max zoomTo level with single item");
869
+ GMap.prototype.publish("panControl", true, "boolean", "Pan Controls", null, { tags: ["Basic"] });
870
+ GMap.prototype.publish("zoomControl", true, "boolean", "Zoom Controls", null, { tags: ["Basic"] });
871
+ GMap.prototype.publish("scaleControl", true, "boolean", "Scale Controls", null, { tags: ["Basic"] });
872
+ GMap.prototype.publish("mapTypeControl", false, "boolean", "Map Type Controls", null, { tags: ["Basic"] });
873
+ GMap.prototype.publish("fullscreenControl", false, "boolean", "Fullscreen Controls", null, { tags: ["Basic"] });
874
+ GMap.prototype.publish("streetViewControl", false, "boolean", "StreetView Controls", null, { tags: ["Basic"] });
875
+ GMap.prototype.publish("overviewMapControl", false, "boolean", "OverviewMap Controls", null, { tags: ["Basic"] });
876
+ GMap.prototype.publish("streetView", false, "boolean", "Streetview", null, { tags: ["Basic"] });
877
+ GMap.prototype.publish("drawingTools", false, "boolean", "Drawing Tools", null, { tags: ["Basic"] });
878
+ GMap.prototype.publish("drawingState", "", "string", "Map Drawings", null, { disable: w => w.drawingTools() === false });
879
+
880
+ GMap.prototype.publish("googleMapStyles", {}, "object", "Styling for map colors etc", null, { tags: ["Basic"] });