@abi-software/flatmap-viewer 2.7.2 → 2.7.3-a.1

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 (121) hide show
  1. package/dist/flatmapviewer.es.js +80577 -0
  2. package/dist/flatmapviewer.umd.js +1687 -0
  3. package/dist/lib/index.d.ts +4 -0
  4. package/dist/style.css +1 -0
  5. package/package.json +10 -8
  6. package/lib/index.ts +0 -10
  7. package/src/contextmenu.js +0 -97
  8. package/src/controls/annotation.js +0 -302
  9. package/src/controls/controls.js +0 -645
  10. package/src/controls/flightpaths.js +0 -95
  11. package/src/controls/info.js +0 -291
  12. package/src/controls/minimap.js +0 -442
  13. package/src/controls/paths.js +0 -143
  14. package/src/controls/search.js +0 -113
  15. package/src/controls/systems.js +0 -75
  16. package/src/controls/taxons.js +0 -73
  17. package/src/flatmap-viewer.js +0 -1789
  18. package/src/images.js +0 -66
  19. package/src/interactions.js +0 -1569
  20. package/src/layers/cluster.js +0 -177
  21. package/src/layers/filter.js +0 -310
  22. package/src/layers/flightpaths.js +0 -383
  23. package/src/layers/index.js +0 -478
  24. package/src/layers/styling.js +0 -1077
  25. package/src/main.js +0 -272
  26. package/src/mapserver.js +0 -64
  27. package/src/mathjax.js +0 -100
  28. package/src/pathways.js +0 -427
  29. package/src/search.js +0 -137
  30. package/src/systems.js +0 -146
  31. package/src/utils.js +0 -152
  32. package/static/css/flatmap-viewer.css +0 -238
  33. package/static/icons/favicon.ico +0 -0
  34. package/static/images/active.png +0 -0
  35. package/static/images/inactive.png +0 -0
  36. package/static/images/reset-map-active.png +0 -0
  37. package/static/images/reset-map-button.png +0 -0
  38. package/static/images/rounded-background.png +0 -0
  39. package/static/images/zoom-in-active.png +0 -0
  40. package/static/images/zoom-in-button.png +0 -0
  41. package/static/images/zoom-out-active.png +0 -0
  42. package/static/images/zoom-out-button.png +0 -0
  43. package/thirdParty/maplibre-gl-svg/CHANGELOG.md +0 -13
  44. package/thirdParty/maplibre-gl-svg/LICENSE +0 -21
  45. package/thirdParty/maplibre-gl-svg/LICENSE.md +0 -21
  46. package/thirdParty/maplibre-gl-svg/README.md +0 -24
  47. package/thirdParty/maplibre-gl-svg/assets/Add custom SVG template to template manager.jpg +0 -0
  48. package/thirdParty/maplibre-gl-svg/assets/All built-in SVG templates as HTML markers.jpg +0 -0
  49. package/thirdParty/maplibre-gl-svg/assets/All built-in SVG templates as symbols.jpg +0 -0
  50. package/thirdParty/maplibre-gl-svg/assets/Fill polygon with built-in SVG template.jpg +0 -0
  51. package/thirdParty/maplibre-gl-svg/assets/HTML Marker with Custom SVG Template.jpg +0 -0
  52. package/thirdParty/maplibre-gl-svg/assets/HTML Marker with built-in SVG template.jpg +0 -0
  53. package/thirdParty/maplibre-gl-svg/assets/Line layer with built-in SVG template.jpg +0 -0
  54. package/thirdParty/maplibre-gl-svg/assets/Load SVG from URL.jpg +0 -0
  55. package/thirdParty/maplibre-gl-svg/assets/SVG template options.jpg +0 -0
  56. package/thirdParty/maplibre-gl-svg/assets/Smiley_face_changed.svg +0 -37
  57. package/thirdParty/maplibre-gl-svg/assets/Symbol layer with built-in SVG template.jpg +0 -0
  58. package/thirdParty/maplibre-gl-svg/assets/image-templates/arrow-up-thin.png +0 -0
  59. package/thirdParty/maplibre-gl-svg/assets/image-templates/arrow-up.png +0 -0
  60. package/thirdParty/maplibre-gl-svg/assets/image-templates/car.png +0 -0
  61. package/thirdParty/maplibre-gl-svg/assets/image-templates/checker-rotated.png +0 -0
  62. package/thirdParty/maplibre-gl-svg/assets/image-templates/checker.png +0 -0
  63. package/thirdParty/maplibre-gl-svg/assets/image-templates/circles-spaced.png +0 -0
  64. package/thirdParty/maplibre-gl-svg/assets/image-templates/circles.png +0 -0
  65. package/thirdParty/maplibre-gl-svg/assets/image-templates/diagonal-lines-down.png +0 -0
  66. package/thirdParty/maplibre-gl-svg/assets/image-templates/diagonal-lines-up.png +0 -0
  67. package/thirdParty/maplibre-gl-svg/assets/image-templates/diagonal-stripes-down.png +0 -0
  68. package/thirdParty/maplibre-gl-svg/assets/image-templates/diagonal-stripes-up.png +0 -0
  69. package/thirdParty/maplibre-gl-svg/assets/image-templates/dots.png +0 -0
  70. package/thirdParty/maplibre-gl-svg/assets/image-templates/flag-triangle.png +0 -0
  71. package/thirdParty/maplibre-gl-svg/assets/image-templates/flag.png +0 -0
  72. package/thirdParty/maplibre-gl-svg/assets/image-templates/grid-lines.png +0 -0
  73. package/thirdParty/maplibre-gl-svg/assets/image-templates/hexagon-rounded-thick.png +0 -0
  74. package/thirdParty/maplibre-gl-svg/assets/image-templates/hexagon-rounded.png +0 -0
  75. package/thirdParty/maplibre-gl-svg/assets/image-templates/hexagon-thick.png +0 -0
  76. package/thirdParty/maplibre-gl-svg/assets/image-templates/hexagon.png +0 -0
  77. package/thirdParty/maplibre-gl-svg/assets/image-templates/marker-arrow.png +0 -0
  78. package/thirdParty/maplibre-gl-svg/assets/image-templates/marker-ball-pin.png +0 -0
  79. package/thirdParty/maplibre-gl-svg/assets/image-templates/marker-circle.png +0 -0
  80. package/thirdParty/maplibre-gl-svg/assets/image-templates/marker-flat.png +0 -0
  81. package/thirdParty/maplibre-gl-svg/assets/image-templates/marker-square-cluster.png +0 -0
  82. package/thirdParty/maplibre-gl-svg/assets/image-templates/marker-square-rounded-cluster.png +0 -0
  83. package/thirdParty/maplibre-gl-svg/assets/image-templates/marker-square-rounded.png +0 -0
  84. package/thirdParty/maplibre-gl-svg/assets/image-templates/marker-square.png +0 -0
  85. package/thirdParty/maplibre-gl-svg/assets/image-templates/marker-thick.png +0 -0
  86. package/thirdParty/maplibre-gl-svg/assets/image-templates/marker.png +0 -0
  87. package/thirdParty/maplibre-gl-svg/assets/image-templates/pin-round.png +0 -0
  88. package/thirdParty/maplibre-gl-svg/assets/image-templates/pin.png +0 -0
  89. package/thirdParty/maplibre-gl-svg/assets/image-templates/rotated-grid-lines.png +0 -0
  90. package/thirdParty/maplibre-gl-svg/assets/image-templates/rotated-grid-stripes.png +0 -0
  91. package/thirdParty/maplibre-gl-svg/assets/image-templates/rounded-square-thick.png +0 -0
  92. package/thirdParty/maplibre-gl-svg/assets/image-templates/rounded-square.png +0 -0
  93. package/thirdParty/maplibre-gl-svg/assets/image-templates/triangle-arrow-left.png +0 -0
  94. package/thirdParty/maplibre-gl-svg/assets/image-templates/triangle-arrow-up.png +0 -0
  95. package/thirdParty/maplibre-gl-svg/assets/image-templates/triangle-thick.png +0 -0
  96. package/thirdParty/maplibre-gl-svg/assets/image-templates/triangle.png +0 -0
  97. package/thirdParty/maplibre-gl-svg/assets/image-templates/x-fill.png +0 -0
  98. package/thirdParty/maplibre-gl-svg/assets/image-templates/zig-zag-vertical.png +0 -0
  99. package/thirdParty/maplibre-gl-svg/assets/image-templates/zig-zag.png +0 -0
  100. package/thirdParty/maplibre-gl-svg/build/build.js +0 -210
  101. package/thirdParty/maplibre-gl-svg/dist/maplibre-gl-svg.js +0 -339
  102. package/thirdParty/maplibre-gl-svg/dist/maplibre-gl-svg.min.js +0 -4
  103. package/thirdParty/maplibre-gl-svg/docs/docs.md +0 -375
  104. package/thirdParty/maplibre-gl-svg/examples/Add custom SVG template to template manager.html +0 -101
  105. package/thirdParty/maplibre-gl-svg/examples/All built-in SVG templates as HTML markers.html +0 -82
  106. package/thirdParty/maplibre-gl-svg/examples/All built-in SVG templates as symbols.html +0 -124
  107. package/thirdParty/maplibre-gl-svg/examples/Fill polygon with built-in SVG template.html +0 -94
  108. package/thirdParty/maplibre-gl-svg/examples/HTML Marker with Custom SVG Template.html +0 -86
  109. package/thirdParty/maplibre-gl-svg/examples/HTML Marker with built-in SVG template.html +0 -83
  110. package/thirdParty/maplibre-gl-svg/examples/Line layer with built-in SVG template.html +0 -129
  111. package/thirdParty/maplibre-gl-svg/examples/Load SVG from URL.html +0 -96
  112. package/thirdParty/maplibre-gl-svg/examples/SVG template options.html +0 -264
  113. package/thirdParty/maplibre-gl-svg/examples/Symbol layer with built-in SVG template.html +0 -93
  114. package/thirdParty/maplibre-gl-svg/index.html +0 -151
  115. package/thirdParty/maplibre-gl-svg/package-lock.json +0 -5882
  116. package/thirdParty/maplibre-gl-svg/package.json +0 -49
  117. package/thirdParty/maplibre-gl-svg/src/SvgManager.ts +0 -186
  118. package/thirdParty/maplibre-gl-svg/src/SvgTemplateManager.ts +0 -144
  119. package/thirdParty/maplibre-gl-svg/src/index.ts +0 -4
  120. package/thirdParty/maplibre-gl-svg/tsconfig.json +0 -31
  121. package/thirdParty/maplibre-gl-svg/typings/index.d.ts +0 -111
@@ -1,1789 +0,0 @@
1
- /******************************************************************************
2
-
3
- Flatmap viewer and annotation tool
4
-
5
- Copyright (c) 2019 David Brooks
6
-
7
- Licensed under the Apache License, Version 2.0 (the "License");
8
- you may not use this file except in compliance with the License.
9
- You may obtain a copy of the License at
10
-
11
- http://www.apache.org/licenses/LICENSE-2.0
12
-
13
- Unless required by applicable law or agreed to in writing, software
14
- distributed under the License is distributed on an "AS IS" BASIS,
15
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
- See the License for the specific language governing permissions and
17
- limitations under the License.
18
-
19
- ******************************************************************************/
20
-
21
- 'use strict';
22
-
23
- //==============================================================================
24
-
25
- import maplibregl from 'maplibre-gl';
26
- import 'maplibre-gl/dist/maplibre-gl.css';
27
-
28
- //==============================================================================
29
-
30
- // Load our stylesheet last so we can overide styling rules
31
-
32
- import '../static/css/flatmap-viewer.css';
33
-
34
- //==============================================================================
35
-
36
- import {MapServer} from './mapserver.js';
37
- import {SearchIndex} from './search.js';
38
- import {UserInteractions} from './interactions.js';
39
-
40
- import {APINATOMY_PATH_PREFIX} from './pathways';
41
-
42
- import {loadClusterIcons} from './layers/cluster'
43
-
44
- import * as images from './images.js';
45
- import * as utils from './utils.js';
46
-
47
- //==============================================================================
48
-
49
- const MAP_MAKER_SEPARATE_LAYERS_VERSION = 1.4;
50
-
51
- //==============================================================================
52
-
53
- /**
54
- * The taxon identifier used when none has been given.
55
- *
56
- * @type {string}
57
- */
58
- export const UNCLASSIFIED_TAXON_ID = 'NCBITaxon:2787823'; // unclassified entries
59
-
60
- //==============================================================================
61
-
62
- /**
63
- * Maps are not created directly but instead are created and loaded by
64
- * :meth:`LoadMap` of :class:`MapManager`.
65
- */
66
- export class FlatMap
67
- {
68
- constructor(container, mapBaseUrl, mapDescription, resolve)
69
- {
70
- this._baseUrl = mapBaseUrl;
71
- this.__id = mapDescription.id;
72
- this.__uuid = mapDescription.uuid;
73
- this.__details = mapDescription.details;
74
- this.__provenance = mapDescription.provenance;
75
- this.__created = mapDescription.created;
76
- this.__taxon = mapDescription.taxon;
77
- this.__biologicalSex = mapDescription.biologicalSex;
78
- this._mapNumber = mapDescription.number;
79
- this._callback = mapDescription.callback;
80
- this._layers = mapDescription.layers;
81
- this._markers = mapDescription.markers;
82
- this._options = mapDescription.options;
83
- this._pathways = mapDescription.pathways;
84
- this._resolve = resolve;
85
- this._map = null;
86
- this.__searchIndex = new SearchIndex(this);
87
- this.__idToAnnotation = new Map();
88
- this.__datasetToFeatureIds = new Map();
89
- this.__modelToFeatureIds = new Map();
90
- this.__mapSourceToFeatureIds = new Map();
91
- this.__annIdToFeatureId = new Map();
92
- this.__taxonToFeatureIds = new Map();
93
-
94
- for (const [featureId, annotation] of Object.entries(mapDescription.annotations)) {
95
- this.__addAnnotation(featureId, annotation);
96
- this.__searchIndex.indexMetadata(featureId, annotation);
97
- }
98
-
99
- // Set base of source URLs in map's style
100
-
101
- for (const [id, source] of Object.entries(mapDescription.style.sources)) {
102
- if (source.url) {
103
- source.url = this.makeServerUrl(source.url);
104
- }
105
- if (source.tiles) {
106
- const tiles = [];
107
- for (const tileUrl of source.tiles) {
108
- tiles.push(this.makeServerUrl(tileUrl));
109
- }
110
- source.tiles = tiles;
111
- }
112
- }
113
-
114
- // Ensure rounded background images (for feature labels) are loaded
115
-
116
- if (!('images' in mapDescription.options)) {
117
- mapDescription.options.images = [];
118
- }
119
- for (const image of images.LABEL_BACKGROUNDS) {
120
- let found = false;
121
- for (const im of mapDescription.options.images) {
122
- if (image.id === im.id) {
123
- found = true;
124
- break;
125
- }
126
- }
127
- if (!found) {
128
- mapDescription.options.images.push(image);
129
- }
130
- }
131
-
132
- // Set options for the map
133
-
134
- const mapOptions = {
135
- style: mapDescription.style,
136
- container: container,
137
- attributionControl: false
138
- };
139
-
140
- if ('maxZoom' in mapDescription.options) {
141
- mapOptions.maxZoom = mapDescription.options.maxZoom;
142
- }
143
- if ('minZoom' in mapDescription.options) {
144
- mapOptions.minZoom = mapDescription.options.minZoom;
145
- }
146
-
147
- // Only show location in address bar when debugging
148
-
149
- mapOptions.hash = (mapDescription.options.debug === true);
150
-
151
- // Create the map
152
-
153
- this._map = new maplibregl.Map(mapOptions);
154
-
155
- // Show tile boundaries if debugging
156
-
157
- if (mapDescription.options.debug === true) {
158
- this._map.showTileBoundaries = true;
159
- }
160
-
161
- // Don't wrap around at +/-180 degrees
162
-
163
- this._map.setRenderWorldCopies(false);
164
-
165
- // Disable map rotation
166
-
167
- //this._map.dragRotate.disable();
168
- //this._map.touchZoomRotate.disableRotation();
169
-
170
- // Finish initialisation when all sources have loaded
171
- // and map has rendered
172
-
173
- this._userInteractions = null;
174
- this._initialState = null;
175
-
176
- this._map.on('idle', () => {
177
- if (this._userInteractions === null) {
178
- this.setupUserInteractions_();
179
- } else if (this._initialState === null) {
180
- this._map.setMinZoom(3.0);
181
- this._map.setMaxBounds(null);
182
- this._map.setRenderWorldCopies(true);
183
- this._bounds = this._map.getBounds();
184
- const bounds = this._bounds.toArray();
185
- const sw = maplibregl.MercatorCoordinate.fromLngLat(bounds[0]);
186
- const ne = maplibregl.MercatorCoordinate.fromLngLat(bounds[1]);
187
- this.__normalised_origin = [sw.x, ne.y];
188
- this.__normalised_size = [ne.x - sw.x, sw.y - ne.y];
189
- if ('state' in this._options) {
190
- this._userInteractions.setState(this._options.state);
191
- }
192
- this._initialState = this.getState();
193
- if (this._userInteractions.minimap) {
194
- this._userInteractions.minimap.initialise()
195
- }
196
- this._resolve(this);
197
- }
198
- });
199
- }
200
-
201
- async setupUserInteractions_()
202
- //============================
203
- {
204
- // Load any images required by the map
205
- for (const image of this._options.images) {
206
- await this.addImage(image.id, image.url, '', image.options);
207
- }
208
-
209
- // Load icons used for clustered markers
210
- await loadClusterIcons(this._map)
211
-
212
- // Layers have now loaded so finish setting up
213
- this._userInteractions = new UserInteractions(this);
214
- }
215
-
216
- /**
217
- * The flatmap's bounds.
218
- */
219
- get bounds()
220
- //==========
221
- {
222
- return this._bounds;
223
- }
224
-
225
- // Map control methods
226
-
227
- /**
228
- * Reset a map to its initial state.
229
- */
230
- resetMap()
231
- //========
232
- {
233
- if (this._initialState !== null) {
234
- this.setState(this._initialState);
235
- }
236
- if (this._userInteractions !== null) {
237
- this._userInteractions.reset();
238
- }
239
- }
240
-
241
- /**
242
- * Zoom the map in.
243
- */
244
- zoomIn()
245
- //======
246
- {
247
- this._map.zoomIn();
248
- }
249
-
250
- /**
251
- * Zoom the map out.
252
- */
253
- zoomOut()
254
- //=======
255
- {
256
- this._map.zoomOut();
257
- }
258
-
259
- /**
260
- * @returns {Array.<{type: string, label: string, colour: string}>} an array of objects giving the path types
261
- * present in the map along with their
262
- * descriptions and colours
263
- */
264
- pathTypes()
265
- //=========
266
- {
267
- if (this._userInteractions !== null) {
268
- return this._userInteractions.pathManager.pathTypes();
269
- }
270
- }
271
-
272
- /**
273
- * Hide or show paths of a given type.
274
- *
275
- * @param {string} pathType The path type
276
- * @param {boolean} enable Show or hide paths of that type. Defaults to
277
- * ``true`` (show)
278
- */
279
- enablePath(pathType, enable=true)
280
- //===============================
281
- {
282
- if (this._userInteractions !== null) {
283
- this._userInteractions.enablePathsByType(pathType, enable);
284
- }
285
- }
286
-
287
- /**
288
- * Hide or show all paths valid in SCKAN.
289
- *
290
- * @param {string} sckanState Either ``valid`` or ``invalid``
291
- * @param {boolean} enable Show or hide paths with that SCKAN state.
292
- * Defaults to ``true`` (show)
293
- */
294
- enableSckanPath(sckanState, enable=true)
295
- //======================================
296
- {
297
- if (this._userInteractions !== null) {
298
- this._userInteractions.enableSckanPaths(sckanState, enable);
299
- }
300
- }
301
-
302
- /**
303
- * Show or hide connectivity features observed in particular species.
304
- *
305
- * @param {string | Array.<string>} taxonId(s) A single taxon identifier
306
- * or an array of identifiers.
307
- * @param {boolean} enable Show or hide connectivity paths and features.
308
- * Defaults to ``true`` (show)
309
- */
310
- enableConnectivityByTaxonIds(taxonIds, enable=true)
311
- //=================================================
312
- {
313
- if (this._userInteractions !== null) {
314
- if (Array.isArray(taxonIds)) {
315
- this._userInteractions.enableConnectivityByTaxonIds(taxonIds, enable);
316
- } else {
317
- this._userInteractions.enableConnectivityByTaxonIds([taxonIds], enable);
318
- }
319
- }
320
- }
321
-
322
- /**
323
- * Hide or show centrelines and nodes.
324
- *
325
- * @param {boolean} enable Show or centrelines and associated nodes.
326
- * Defaults to ``true`` (show)
327
- */
328
- enableCentrelines(enable=true)
329
- //============================
330
- {
331
- if (this._userInteractions !== null) {
332
- this._userInteractions.enableCentrelines(enable);
333
- }
334
- }
335
-
336
- /**
337
- * Load images and patterns/textures referenced in style rules.
338
- *
339
- * @private
340
- */
341
- loadImage_(url)
342
- //=============
343
- {
344
- return new Promise((resolve, reject) => {
345
- this._map.loadImage(url, (error, image) => {
346
- if (error) reject(error);
347
- else resolve(image);
348
- });
349
- });
350
- }
351
-
352
- loadEncodedImage_(encodedImageUrl)
353
- //================================
354
- {
355
- return new Promise((resolve, reject) => {
356
- const image = new Image();
357
- image.src = encodedImageUrl;
358
- image.onload = (e) => resolve(e.target);
359
- });
360
- }
361
-
362
- async addImage(id, path, baseUrl, options={})
363
- //===========================================
364
- {
365
- if (!this._map.hasImage(id)) {
366
- const image = await (path.startsWith('data:image') ? this.loadEncodedImage_(path)
367
- : this.loadImage_(path.startsWith('/') ? this.makeServerUrl(path)
368
- : new URL(path, baseUrl)));
369
- this._map.addImage(id, image, options);
370
- }
371
- }
372
-
373
- makeServerUrl(url, resource='flatmap/')
374
- //=====================================
375
- {
376
- if (url.startsWith('http://') || url.startsWith('https://')) {
377
- return url;
378
- } else if (url.startsWith('/')) {
379
- // We don't want embedded `{` and `}` characters escaped
380
- return `${this._baseUrl}${resource}${this.__uuid}${url}`;
381
- } else {
382
- return `${this._baseUrl}${resource}${this.__uuid}/${url}`;
383
- }
384
- }
385
-
386
- /**
387
- * The taxon identifier of the species described by the map.
388
- *
389
- * @type string
390
- */
391
- get taxon()
392
- //=========
393
- {
394
- return this.__taxon;
395
- }
396
-
397
- /**
398
- * The biological sex identifier of the species described by the map.
399
- *
400
- * @type string
401
- */
402
- get biologicalSex()
403
- //=================
404
- {
405
- return this.__biologicalSex;
406
- }
407
-
408
- /**
409
- * The map's creation time.
410
- *
411
- * @type string
412
- */
413
- get created()
414
- //===========
415
- {
416
- return this.__created;
417
- }
418
-
419
- /**
420
- * The map's id as specified at generation time.
421
- *
422
- * @type string
423
- */
424
- get id()
425
- //======
426
- {
427
- return this.__id;
428
- }
429
-
430
- /**
431
- * The map's unique universal identifier.
432
- *
433
- * For published maps this is different to the map's ``id``;
434
- * it might be the same as ``id`` for unpublished maps.
435
- *
436
- * @type string
437
- */
438
- get uuid()
439
- //========
440
- {
441
- return this.__uuid;
442
- }
443
-
444
- /**
445
- * The map's URL on the map server.
446
- *
447
- * @type string
448
- */
449
- get url()
450
- //========
451
- {
452
- let url = this.makeServerUrl('')
453
- if (url.endsWith('/')) {
454
- return url.substring(0, url.length - 1)
455
- }
456
- return url
457
- }
458
-
459
- /**
460
- * The map's ``index.json`` as returned from the map server.
461
- *
462
- * @type Object
463
- */
464
- get details()
465
- //===========
466
- {
467
- return this.__details;
468
- }
469
-
470
- /**
471
- * The map's provenance as returned from the map server.
472
- *
473
- * @type Object
474
- */
475
- get provenance()
476
- //==============
477
- {
478
- return this.__provenance;
479
- }
480
-
481
- /**
482
- * A unique identifier for the map within the viewer.
483
- *
484
- * @type string
485
- */
486
- get uniqueId()
487
- //============
488
- {
489
- return `${this.__uuid}-${this._mapNumber}`;
490
- }
491
-
492
- get annotations()
493
- //===============
494
- {
495
- return this.__idToAnnotation;
496
- }
497
-
498
- annotation(featureId)
499
- //===================
500
- {
501
- return this.__idToAnnotation.get(featureId.toString());
502
- }
503
-
504
- /**
505
- * Flag the feature as having external annotation.
506
- *
507
- * @param {string} featureId The feature's external identifier
508
- */
509
- setFeatureAnnotated(featureId)
510
- //============================
511
- {
512
- if (this._userInteractions !== null) {
513
- this._userInteractions.setFeatureAnnotated(featureId);
514
- }
515
- }
516
-
517
- __updateFeatureIdMapEntry(propertyId, featureIdMap, featureId)
518
- //============================================================
519
- {
520
- const id = utils.normaliseId(propertyId)
521
- const featureIds = featureIdMap.get(id);
522
- if (featureIds) {
523
- featureIds.push(featureId);
524
- } else {
525
- featureIdMap.set(id, [featureId]);
526
- }
527
- }
528
-
529
- __updateFeatureIdMap(property, featureIdMap, annotation, missingId=null)
530
- //======================================================================
531
- {
532
- if (property in annotation && annotation[property].length) {
533
- const propertyId = annotation[property];
534
- if (Array.isArray(propertyId)) {
535
- for (const id of propertyId) {
536
- this.__updateFeatureIdMapEntry(id, featureIdMap, annotation.featureId);
537
- }
538
- } else {
539
- this.__updateFeatureIdMapEntry(propertyId, featureIdMap, annotation.featureId);
540
- }
541
- } else if (missingId !== null
542
- && 'models' in annotation
543
- && annotation.models.startsWith(APINATOMY_PATH_PREFIX)) {
544
- this.__updateFeatureIdMapEntry(missingId, featureIdMap, annotation.featureId);
545
- }
546
- }
547
-
548
- __addAnnotation(featureId, ann)
549
- //=============================
550
- {
551
- ann.featureId = featureId;
552
- this.__idToAnnotation.set(featureId, ann);
553
- this.__updateFeatureIdMap('dataset', this.__datasetToFeatureIds, ann);
554
- this.__updateFeatureIdMap('models', this.__modelToFeatureIds, ann);
555
- this.__updateFeatureIdMap('source', this.__mapSourceToFeatureIds, ann);
556
- this.__updateFeatureIdMap('taxons', this.__taxonToFeatureIds, ann, UNCLASSIFIED_TAXON_ID);
557
- this.__annIdToFeatureId.set(ann.id, featureId);
558
- }
559
-
560
- modelFeatureIds(anatomicalId)
561
- //===========================
562
- {
563
- const featureIds = this.__modelToFeatureIds.get(utils.normaliseId(anatomicalId));
564
- return featureIds ? featureIds : [];
565
- }
566
-
567
- modelFeatureIdList(anatomicalIds)
568
- //===============================
569
- {
570
- const featureIds = new utils.List();
571
- if (Array.isArray(anatomicalIds)) {
572
- for (const id of anatomicalIds) {
573
- featureIds.extend(this.modelFeatureIds(id));
574
- }
575
- } else {
576
- featureIds.extend(this.modelFeatureIds(anatomicalIds));
577
- }
578
- if (featureIds.length == 0) {
579
- // We couldn't find a feature by anatomical id, so check dataset and source
580
- featureIds.extend(this.__datasetToFeatureIds.get(anatomicalIds));
581
- featureIds.extend(this.__mapSourceToFeatureIds.get(anatomicalIds));
582
- }
583
- if (featureIds.length == 0 && this._userInteractions !== null) {
584
- // We still haven't found a feature, so check connectivity
585
- featureIds.extend(this._userInteractions.pathFeatureIds(anatomicalIds));
586
- }
587
- return featureIds;
588
- }
589
-
590
- modelForFeature(featureId)
591
- //========================
592
- {
593
- const ann = this.__idToAnnotation.get(featureId);
594
- return (ann && 'models' in ann) ? utils.normaliseId(ann.models) : null;
595
- }
596
-
597
- /**
598
- * Get model terms of all paths connected to a node.
599
- *
600
- * @param {number} pathId The local (GeoJSON) identifier of a node
601
- * @return {set<string>} Model terms of all paths connected to the node
602
- */
603
- nodePathModels(nodeId)
604
- //====================
605
- {
606
- if (this._userInteractions !== null) {
607
- return this._userInteractions.nodePathModels(nodeId);
608
- }
609
- }
610
-
611
- /**
612
- * Get GeoJSON feature ids of all nodes of a path model.
613
- *
614
- * @param {string} pathId The path model identifier
615
- * @return {Array<string>} GeoJSON identifiers of features on the path
616
- */
617
- pathModelNodes(modelId)
618
- //=====================
619
- {
620
- if (this._userInteractions !== null) {
621
- return [...this._userInteractions.pathModelNodes(modelId)]
622
- }
623
- }
624
-
625
- /**
626
- * Get GeoJSON feature ids of all features identified with a taxon.
627
- *
628
- * @param {string} taxonId The taxon identifier
629
- * @return {Array<string>} GeoJSON identifiers of features on the path
630
- */
631
- taxonFeatureIds(taxonId)
632
- //======================
633
- {
634
- const featureIds = this.__taxonToFeatureIds.get(utils.normaliseId(taxonId))
635
- return [...new Set(featureIds ? featureIds : [])]
636
- }
637
-
638
- get layers()
639
- //==========
640
- {
641
- return this._layers;
642
- }
643
-
644
- get map()
645
- //=======
646
- {
647
- return this._map;
648
- }
649
-
650
- get markers()
651
- //===========
652
- {
653
- return this._markers;
654
- }
655
-
656
- /**
657
- * The anatomical identifiers of features in the map.
658
- *
659
- * @type {string|Array.<string>}
660
- */
661
- get anatomicalIdentifiers()
662
- //=========================
663
- {
664
- return [...this.__modelToFeatureIds.keys()]
665
- }
666
-
667
- /**
668
- * The taxon identifiers of species which the map's connectivity has been observed in.
669
- *
670
- * @type {string|Array.<string>}
671
- */
672
- get taxonIdentifiers()
673
- //====================
674
- {
675
- return [...this.__taxonToFeatureIds.keys()]
676
- }
677
-
678
- /**
679
- * Datasets associated with the map.
680
- *
681
- * @type {string|Array.<string>}
682
- */
683
- get datasets()
684
- //============
685
- {
686
- return [...this.__datasetToFeatureIds.keys()]
687
- }
688
-
689
- get options()
690
- //===========
691
- {
692
- return this._options;
693
- }
694
-
695
- get pathways()
696
- //============
697
- {
698
- return this._pathways;
699
- }
700
-
701
- /**
702
- * Get the map's zoom settings.
703
- *
704
- * @return {Object.<{minZoom: number, zoom: number, maxZoom: number}>} The map's minimum, current, and maximum zoom levels.
705
- */
706
- getZoom()
707
- //=======
708
- {
709
- return {
710
- mapUUID: this.__uuid,
711
- minZoom: this._map.getMinZoom(),
712
- zoom: this._map.getZoom(),
713
- maxZoom: this._map.getMaxZoom()
714
- }
715
- }
716
-
717
- callback(type, data, ...args)
718
- //===========================
719
- {
720
- if (this._callback) {
721
- data.mapUUID = this.__uuid;
722
- return this._callback(type, data, ...args);
723
- }
724
- }
725
-
726
- setInitialPosition()
727
- //==================
728
- {
729
- if ('bounds' in this._options) {
730
- this._map.fitBounds(this._options['bounds'], {animate: false});
731
- }
732
- if ('center' in this._options) {
733
- this._map.setCenter(this._options['center']);
734
- }
735
- if ('zoom' in this._options) {
736
- this._map.setZoom(this._options['zoom']);
737
- }
738
- }
739
-
740
- close()
741
- //=====
742
- {
743
- if (this._map) {
744
- this._map.remove();
745
- this._map = null;
746
- }
747
- }
748
-
749
- resize()
750
- //======
751
- {
752
- // Resize our map
753
-
754
- this._map.resize();
755
- }
756
-
757
- getIdentifier()
758
- //=============
759
- {
760
- // Return identifiers for reloading the map
761
-
762
- return {
763
- taxon: this.__taxon,
764
- biologicalSex: this.__biologicalSex,
765
- uuid: this.__uuid
766
- };
767
- }
768
-
769
- getState()
770
- //========
771
- {
772
- return (this._userInteractions !== null) ? this._userInteractions.getState() : {};
773
- }
774
-
775
- setState(state)
776
- //=============
777
- {
778
- if (this._userInteractions !== null) {
779
- this._userInteractions.setState(state);
780
- }
781
- }
782
-
783
- showPopup(featureId, content, options)
784
- //====================================
785
- {
786
- if (this._userInteractions !== null) {
787
- this._userInteractions.showPopup(featureId, content, options);
788
- }
789
- }
790
-
791
- setPaint(options=null)
792
- //====================
793
- {
794
- options = utils.setDefaults(options, {
795
- colour: true,
796
- outline: true
797
- });
798
- if (this._userInteractions !== null) {
799
- this._userInteractions.setPaint(options);
800
- }
801
- }
802
-
803
- setColour(options=null)
804
- //=====================
805
- {
806
- console.log('`setColour()` is deprecated; please use `setPaint()` instead.')
807
- this.setPaint(options);
808
- }
809
-
810
- //==========================================================================
811
-
812
- /**
813
- * Get the map's current background colour.
814
- *
815
- * @return {string} The background colour.
816
- */
817
- getBackgroundColour()
818
- //===================
819
- {
820
- return this._map.getPaintProperty('background', 'background-color');
821
- }
822
-
823
- /**
824
- * Get the map's current background opacity.
825
- *
826
- * @return {number} The background opacity.
827
- */
828
- getBackgroundOpacity()
829
- //====================
830
- {
831
- return this._map.getPaintProperty('background', 'background-opacity');
832
- }
833
-
834
- /**
835
- * Sets the map's background colour.
836
- *
837
- * @param {string} colour The colour
838
- */
839
- setBackgroundColour(colour)
840
- //=========================
841
- {
842
- localStorage.setItem('flatmap-background-colour', colour);
843
-
844
- this._map.setPaintProperty('background', 'background-color', colour);
845
-
846
- if (this._userInteractions.minimap) {
847
- this._userInteractions.minimap.setBackgroundColour(colour);
848
- }
849
- }
850
-
851
- /**
852
- * Sets the map's background opacity.
853
- *
854
- * @param {number} opacity The opacity
855
- */
856
- setBackgroundOpacity(opacity)
857
- //===========================
858
- {
859
- this._map.setPaintProperty('background', 'background-opacity', opacity);
860
-
861
- if (this._userInteractions.minimap) {
862
- this._userInteractions.minimap.setBackgroundOpacity(opacity);
863
- }
864
- }
865
-
866
- /**
867
- * Show and hide the minimap.
868
- *
869
- * @param {boolean} show Set false to hide minimap
870
- */
871
- showMinimap(show)
872
- //===============
873
- {
874
- if (this._userInteractions.minimap) {
875
- this._userInteractions.minimap.show(show);
876
- }
877
-
878
- }
879
-
880
- //==========================================================================
881
-
882
- /**
883
- * Get a list of the flatmap's layers.
884
- *
885
- * @return {Array.<{id: string, description: string, enabled: boolean}>} An array with layer details
886
- */
887
- getLayers()
888
- //=========
889
- {
890
- if (this._userInteractions !== null) {
891
- return this._userInteractions.getLayers();
892
- }
893
- }
894
-
895
- /**
896
- * @param {string} layerId The layer identifier to enable
897
- * @param {boolean} enable Show or hide the layer. Defaults to ``true`` (show)
898
- *
899
- */
900
- enableLayer(layerId, enable=true)
901
- //===============================
902
- {
903
- if (this._userInteractions !== null) {
904
- return this._userInteractions.enableLayer(layerId, enable);
905
- }
906
- }
907
-
908
- /**
909
- * Show/hide flight path view.
910
- *
911
- * @param {boolean} [enable=true]
912
- */
913
- enableFlightPaths(enable=true)
914
- //============================
915
- {
916
- if (this._userInteractions !== null) {
917
- this._userInteractions.enableFlightPaths(enable)
918
- }
919
- }
920
-
921
- //==========================================================================
922
-
923
- /**
924
- * Get a list of a FC flatmap's systems.
925
- *
926
- * @return {Array.<{id: string, name: string, colour: string, enabled: boolean}>} An array with system details
927
- */
928
- getSystems()
929
- //==========
930
- {
931
- if (this._userInteractions !== null) {
932
- return this._userInteractions.getSystems();
933
- }
934
- }
935
-
936
- /**
937
- * @param {string} systemId The identifier of the system to enable
938
- * @param {boolean} enable Show or hide the system. Defaults to ``true`` (show)
939
- *
940
- */
941
- enableSystem(systemId, enable=true)
942
- //===================================
943
- {
944
- if (this._userInteractions !== null) {
945
- return this._userInteractions.enableSystem(systemId, enable);
946
- }
947
- }
948
-
949
- //==========================================================================
950
-
951
- /**
952
- * Add a marker to the map.
953
- *
954
- * @param {string} anatomicalId The anatomical identifier of the feature on which
955
- * to place the marker.
956
- * @arg {Object} options Configurable options for the marker.
957
- * @arg {string} options.className Space-separated CSS class names to add to marker element.
958
- * @arg {string} options.cluster The marker will be clustered together with other geographically
959
- * close markers. Defaults to ``true``.
960
- * @arg {string} options.colour Colour of the marker. Defaults to ``'#005974'``
961
- * (dark cyan).
962
- * @arg {string} options.element The DOM element to use as a marker. The default is
963
- * a dark blue droplet-shaped SVG marker.
964
- * @return {integer} The identifier for the resulting marker. -1 is returned if the
965
- * map doesn't contain a feature with the given anatomical identifier
966
- */
967
- addMarker(anatomicalId, options={})
968
- //==================================
969
- {
970
- options = Object.assign({cluster: true}, options)
971
- if (this._userInteractions !== null) {
972
- return this._userInteractions.addMarker(anatomicalId, options);
973
- }
974
- return -1;
975
- }
976
-
977
- /**
978
- * Add a list of markers to the map.
979
- *
980
- * @param {Array.<string>} anatomicalId Anatomical identifiers of features on which
981
- * to place markers.
982
- * @arg {Object} options Configurable options for the markers.
983
- * @arg {string} options.className Space-separated CSS class names to add to marker elemens.
984
- * @arg {string} options.cluster The markers will be clustered together with other geographically
985
- * close markers. Defaults to ``true``.
986
- * @arg {string} options.colour Colour of the markers. Defaults to ``'#005974'``
987
- * (dark cyan).
988
- * @arg {string} options.element The DOM element to use as a marker. The default is
989
- * a dark blue droplet-shaped SVG marker.
990
- * @return {array.<integer>} The identifiers of the resulting markers. -1 is returned if the
991
- * map doesn't contain a feature with the given anatomical identifier
992
- */
993
- addMarkers(anatomicalIds, options={})
994
- //====================================
995
- {
996
- options = Object.assign({cluster: true}, options)
997
- const markerIds = []
998
- for (const anatomicalId of anatomicalIds) {
999
- if (this._userInteractions !== null) {
1000
- markerIds.push(this._userInteractions.addMarker(anatomicalId, options))
1001
- } else {
1002
- markerIds.push(-1)
1003
- }
1004
- }
1005
- return markerIds
1006
- }
1007
-
1008
- /**
1009
- * Remove a marker from the map.
1010
- *
1011
- * @param {integer} markerId The identifier of the marker, as returned
1012
- * by ``addMarker()``
1013
- */
1014
- removeMarker(markerId)
1015
- //====================
1016
- {
1017
- if (markerId > -1 && this._userInteractions !== null) {
1018
- this._userInteractions.removeMarker(markerId);
1019
- }
1020
- }
1021
-
1022
- /**
1023
- * Remove all markers from the map.
1024
- */
1025
- clearMarkers()
1026
- //============
1027
- {
1028
- if (this._userInteractions !== null) {
1029
- this._userInteractions.clearMarkers();
1030
- }
1031
- }
1032
-
1033
- /**
1034
- * Return the set of anatomical identifiers visible in the current map view.
1035
- *
1036
- * @return {Array.<string>} A list of identifiers
1037
- */
1038
- visibleMarkerAnatomicalIds()
1039
- //==========================
1040
- {
1041
- if (this._userInteractions !== null) {
1042
- return this._userInteractions.visibleMarkerAnatomicalIds();
1043
- }
1044
- }
1045
-
1046
- /**
1047
- * Shows a popup at a marker.
1048
- *
1049
- * This method should only be called in response to a ``mouseenter`` event
1050
- * passed to the map's ``callback`` function otherwise a popup won't be shown.
1051
- *
1052
- * @param {integer} markerId The identifier of the marker
1053
- * @param {string | DOMElement} content The popup's content
1054
- * @param {Object} options
1055
- * @returns {boolean} Return true if the popup is shown
1056
- *
1057
- * The resulting popup is given a class name of ``flatmap-tooltip-popup``.
1058
- */
1059
- showMarkerPopup(markerId, content, options={})
1060
- //============================================
1061
- {
1062
- if (this._userInteractions !== null) {
1063
- return this._userInteractions.showMarkerPopup(markerId, content, options);
1064
- }
1065
- return false;
1066
- }
1067
-
1068
- __exportedProperties(properties)
1069
- //==============================
1070
- {
1071
- const data = {};
1072
- const exportedProperties = [
1073
- 'id',
1074
- 'featureId',
1075
- 'connectivity',
1076
- 'dataset',
1077
- 'kind',
1078
- 'label',
1079
- 'models',
1080
- 'source',
1081
- 'taxons',
1082
- 'hyperlinks',
1083
- 'completeness',
1084
- 'missing-nodes',
1085
- 'alert',
1086
- 'biological-sex'
1087
- ];
1088
- for (const property of exportedProperties) {
1089
- if (property in properties) {
1090
- const value = properties[property];
1091
- if (value !== undefined) {
1092
- if ((Array.isArray(value) && value.length)
1093
- || (value.constructor === Object && Object.keys(value).length)) {
1094
- data[property] = value
1095
- } else if (property === 'featureId') {
1096
- data[property] = +value; // Ensure numeric
1097
- } else {
1098
- data[property] = value;
1099
- }
1100
- }
1101
- }
1102
- }
1103
- if (Object.keys(data).length > 0) {
1104
- data['type'] = 'feature';
1105
- }
1106
- return data;
1107
- }
1108
-
1109
- /**
1110
- * Show or hide a tool for drawing regions to annotate on the map.
1111
- *
1112
- * @param {boolean} [visible=true]
1113
- */
1114
- showAnnotator(visible=true)
1115
- //=========================
1116
- {
1117
- if (this._userInteractions !== null) {
1118
- this._userInteractions.showAnnotator(visible)
1119
- }
1120
- }
1121
-
1122
- /**
1123
- * Generate an ``annotation`` callback event when a drawn annotation has been created
1124
- * a modified.
1125
- *
1126
- * @param eventType {string} Either ``created``, ``updated`` or ``deleted``
1127
- * @param feature {Object} A feature object with ``id``, ``type``, and ``geometry``
1128
- * fields of a feature that has been created, updated or
1129
- * deleted.
1130
- */
1131
- annotationEvent(eventType, feature)
1132
- //=================================
1133
- {
1134
- this.callback('annotation', {
1135
- type: eventType,
1136
- feature: feature
1137
- });
1138
- }
1139
-
1140
- /**
1141
- * Mark a drawn/changed annotation as having been accepted by the user.
1142
- *
1143
- * @param event {Object} The object as received in an annotation callback
1144
- * @param event.type {string} Either ``created``, ``updated`` or ``deleted``
1145
- * @param event.feature {Object} A feature object.
1146
- */
1147
- commitAnnotationEvent(event)
1148
- //==========================
1149
- {
1150
- if (this._userInteractions) {
1151
- this._userInteractions.commitAnnotationEvent(event)
1152
- }
1153
- }
1154
-
1155
- /**
1156
- * Mark a drawn/changed annotation as having been rejected by the user.
1157
- *
1158
- * @param event {Object} The object as received in an annotation callback
1159
- * @param event.type {string} Either ``created``, ``updated`` or ``deleted``
1160
- * @param event.feature {Object} A feature object.
1161
- */
1162
- rollbackAnnotationEvent(event)
1163
- //============================
1164
- {
1165
- if (this._userInteractions) {
1166
- this._userInteractions.rollbackAnnotationEvent(event)
1167
- }
1168
- }
1169
-
1170
- /**
1171
- * Clear all drawn annotations from current annotation layer.
1172
- */
1173
- clearAnnotationFeature()
1174
- //======================
1175
- {
1176
- if (this._userInteractions) {
1177
- this._userInteractions.clearAnnotationFeatures()
1178
- }
1179
- }
1180
-
1181
- /**
1182
- * Delete the selected drawn feature
1183
- */
1184
- removeAnnotationFeature()
1185
- //=======================
1186
- {
1187
- if (this._userInteractions) {
1188
- this._userInteractions.removeAnnotationFeature()
1189
- }
1190
- }
1191
-
1192
- /**
1193
- * Add a drawn feature to the annotation drawing tool.
1194
- *
1195
- * @param feature {Object} The feature to add
1196
- * @param feature.id {string} The feature's id
1197
- * @param feature.geometry {Object} The feature's geometry as GeoJSON
1198
- */
1199
- addAnnotationFeature(feature)
1200
- //===========================
1201
- {
1202
- if (this._userInteractions) {
1203
- this._userInteractions.addAnnotationFeature(feature)
1204
- }
1205
- }
1206
-
1207
- /**
1208
- * Return the feature as it is currently drawn. This is so
1209
- * the correct geometry can be saved with a feature should
1210
- * a user make changes before submitting dialog provided
1211
- * by an external annotator.
1212
- *
1213
- * @param feature {Object} The drawn feature to refresh.
1214
- * @returns {Object|null} The feature with currently geometry or ``null``
1215
- * if the feature has been deleted.
1216
- */
1217
- refreshAnnotationFeatureGeometry(feature)
1218
- //=======================================
1219
- {
1220
- if (this._userInteractions) {
1221
- return this._userInteractions.refreshAnnotationFeatureGeometry(feature)
1222
- }
1223
- }
1224
-
1225
- /**
1226
- * Changes draw to another mode. The mode argument must be one of the following:
1227
- * `simple_select`, `direct_select`, `draw_line_string`,
1228
- * `draw_polygon` or `draw_point`. Options is accepted in first three modes.
1229
- * More details in mapbox-gl-draw github repository.
1230
- *
1231
- * @param type {Object} The object
1232
- * @param type.mode {string} Either ``simple_select``, ``direct_select``, etc
1233
- * @param type.options {Object} Feature id(s) object.
1234
- */
1235
- changeAnnotationDrawMode(type)
1236
- //============================
1237
- {
1238
- if (this._userInteractions) {
1239
- this._userInteractions.changeAnnotationDrawMode(type)
1240
- }
1241
- }
1242
-
1243
- /**
1244
- * Generate a callback as a result of some event with a flatmap feature.
1245
- *
1246
- * @param {string} eventType The event type
1247
- * @param {Object} properties Properties associated with the feature
1248
- */
1249
- featureEvent(eventType, properties)
1250
- //=================================
1251
- {
1252
- const data = this.__exportedProperties(properties);
1253
-
1254
- if (Object.keys(data).length > 0) {
1255
- this.callback(eventType, data);
1256
- return true;
1257
- }
1258
- return false;
1259
- }
1260
-
1261
- /**
1262
- * Return properties associated with a feature.
1263
- *
1264
- * @param {number} featureId The feature's internal (GeoJSON) id
1265
- * @returns {Object} Properties associated with the feature
1266
- */
1267
- featureProperties(featureId)
1268
- //==========================
1269
- {
1270
- const properties = this.annotation(featureId);
1271
- return properties ? this.__exportedProperties(properties) : {};
1272
- }
1273
-
1274
- /**
1275
- * Generate a callback as a result of some event with a marker.
1276
- *
1277
- * @param {string} eventType The event type
1278
- * @param {integer} markerId The marker identifier
1279
- * @param {string} anatomicalId The anatomical identifier for the marker
1280
- */
1281
- markerEvent(eventType, markerId, anatomicalId)
1282
- //============================================
1283
- {
1284
- this.callback(eventType, {
1285
- type: 'marker',
1286
- id: markerId,
1287
- models: anatomicalId
1288
- });
1289
- }
1290
-
1291
- /**
1292
- * Generate a callback as a result of some event in a control.
1293
- *
1294
- * @param {string} eventType The event type
1295
- * @param {string} control The name of the control
1296
- * @param {string} value The value of the control
1297
- */
1298
- controlEvent(eventType, control, value)
1299
- //=====================================
1300
- {
1301
- this.callback(eventType, {
1302
- type: 'control',
1303
- control: control,
1304
- value: value
1305
- });
1306
- }
1307
-
1308
- /**
1309
- * Generate callbacks as a result of panning/zooming the map.
1310
- *
1311
- * @param {boolean} enabled Generate callbacks when ``true``,
1312
- * otherwise disable them.
1313
- */
1314
- enablePanZoomEvents(enabled=true)
1315
- //===============================
1316
- {
1317
- if (this._userInteractions !== null) {
1318
- this._userInteractions.enablePanZoomEvents(enabled);
1319
- }
1320
- }
1321
-
1322
- /**
1323
- * Generate a callback as a result of panning/zooming the map.
1324
- *
1325
- * @param {string} type The event type, ``pan`` or ``zoom``.
1326
- * @param {Array.<float>} origin The map's normalised top-left corner
1327
- * @param {Array.<float>} size The map's normalised size
1328
- */
1329
- panZoomEvent(type)
1330
- //================
1331
- {
1332
- const bounds = this._map.getBounds();
1333
- if (this.__normalised_origin !== undefined) {
1334
- const sw = maplibregl.MercatorCoordinate.fromLngLat(bounds.toArray()[0]);
1335
- const ne = maplibregl.MercatorCoordinate.fromLngLat(bounds.toArray()[1]);
1336
- const top_left = [(sw.x - this.__normalised_origin[0])/this.__normalised_size[0],
1337
- (ne.y - this.__normalised_origin[1])/this.__normalised_size[1]];
1338
- const size = [(ne.x - sw.x)/this.__normalised_size[0],
1339
- (sw.y - ne.y)/this.__normalised_size[1]];
1340
- this.callback('pan-zoom', {
1341
- type: type,
1342
- origin: top_left,
1343
- size: size
1344
- });
1345
- }
1346
- }
1347
-
1348
- /**
1349
- * Pan/zoom the map to a new view
1350
- *
1351
- * @param {Array.<float>} origin The map's normalised top-left corner
1352
- * @param {Array.<float>} size The map's normalised size
1353
- */
1354
- panZoomTo(origin, size)
1355
- //=====================
1356
- {
1357
- if (this.__normalised_origin !== undefined) {
1358
- const sw_x = origin[0]*this.__normalised_size[0] + this.__normalised_origin[0];
1359
- const ne_y = origin[1]*this.__normalised_size[1] + this.__normalised_origin[1];
1360
- const ne_x = sw_x + size[0]*this.__normalised_size[0];
1361
- const sw_y = ne_y + size[1]*this.__normalised_size[1];
1362
- const sw = (new maplibregl.MercatorCoordinate(sw_x, sw_y, 0)).toLngLat();
1363
- const ne = (new maplibregl.MercatorCoordinate(ne_x, ne_y, 0)).toLngLat();
1364
- this._map.fitBounds([sw, ne], {animate: false});
1365
- }
1366
- }
1367
-
1368
- //==========================================================================
1369
-
1370
- /**
1371
- * Find features with labels or terms matching ``text``.
1372
- *
1373
- * @param {string} text The text to search
1374
- * @param {boolean} [auto=false] If ``true`` return suggestions of text to search for.
1375
- * @return Either a ``Searchresults`` object with fields of ``featureIds`` and ``results``,
1376
- * where ``results`` has ``featureId``, ``score``, ``terms`` and ``text`` fields,
1377
- * or a ``Suggestion`` object containing suggested matches
1378
- * (see https://lucaong.github.io/minisearch/modules/_minisearch_.html#suggestion).
1379
- */
1380
- search(text, auto=false)
1381
- //======================
1382
- {
1383
- if (auto) {
1384
- return this.__searchIndex.auto_suggest(text);
1385
- } else {
1386
- return this.__searchIndex.search(text);
1387
- }
1388
- }
1389
-
1390
- clearSearchResults()
1391
- //==================
1392
- {
1393
- if (this._userInteractions !== null) {
1394
- this._userInteractions.clearSearchResults();
1395
- }
1396
- }
1397
-
1398
- showSearchResults(searchResults)
1399
- //==============================
1400
- {
1401
- if (this._userInteractions !== null) {
1402
- this._userInteractions.showSearchResults(searchResults.featureIds);
1403
- }
1404
- }
1405
-
1406
- //==========================================================================
1407
-
1408
- /**
1409
- * Select features on the map.
1410
- *
1411
- * @param {Array.<string>} externalIds An array of anaotomical terms identifing features to select
1412
- */
1413
- selectFeatures(externalIds)
1414
- //=========================
1415
- {
1416
- if (this._userInteractions !== null) {
1417
- const featureIds = this.modelFeatureIdList(externalIds);
1418
- this._userInteractions.selectFeatures(featureIds);
1419
- }
1420
- }
1421
-
1422
- /**
1423
- * Select features and zoom the map to them.
1424
- *
1425
- * @param {Array.<string>} featureIds An array of feature identifiers
1426
- * @param {Object} [options]
1427
- * @param {boolean} [options.noZoomIn=false] Don't zoom in (although zoom out as necessary)
1428
- * @param {number} [options.padding=10] Padding in pixels around the composite bounding box
1429
- */
1430
- zoomToFeatures(externalIds, options=null)
1431
- //=======================================
1432
- {
1433
- options = utils.setDefaults(options, {
1434
- select: true,
1435
- highlight: false,
1436
- padding:100
1437
- });
1438
- if (this._userInteractions !== null) {
1439
- const featureIds = this.modelFeatureIdList(externalIds);
1440
- this._userInteractions.zoomToFeatures(featureIds, options);
1441
- }
1442
- }
1443
-
1444
- /**
1445
- * Select features on the map.
1446
- *
1447
- * @param {string | Array.<string>} geojsonIds A single GeoJSON feature identifiers
1448
- * or an array of identifiers.
1449
- */
1450
- selectGeoJSONFeatures(geojsonIds)
1451
- //===============================
1452
- {
1453
- if (this._userInteractions !== null) {
1454
- this._userInteractions.selectFeatures(geojsonIds)
1455
- }
1456
- }
1457
-
1458
- /**
1459
- * Select features and zoom the map to them.
1460
- *
1461
- * @param {string | Array.<string>} geojsonIds A single GeoJSON feature identifiers
1462
- * or an array of identifiers.
1463
- * @param {Object} [options]
1464
- * @param {boolean} [options.noZoomIn=false] Don't zoom in (although zoom out as necessary)
1465
- * @param {number} [options.padding=10] Padding in pixels around the composite bounding box
1466
- */
1467
- zoomToGeoJSONFeatures(geojsonIds, options=null)
1468
- //=============================================
1469
- {
1470
- options = utils.setDefaults(options, {
1471
- select: true,
1472
- highlight: false,
1473
- padding:100
1474
- })
1475
- if (this._userInteractions !== null) {
1476
- this._userInteractions.zoomToFeatures(geojsonIds, options)
1477
- }
1478
- }
1479
- }
1480
-
1481
- //==============================================================================
1482
-
1483
- /**
1484
- * A manager for FlatMaps.
1485
- * @example
1486
- * const mapManager = new MapManger('https://mapcore-demo.org/flatmaps/');
1487
- */
1488
- export class MapManager
1489
- {
1490
- /* Create a MapManager */
1491
- constructor(mapServerUrl, options={})
1492
- {
1493
- this._mapServer = new MapServer(mapServerUrl);
1494
- this._options = options;
1495
-
1496
- this._mapList = [];
1497
- this._mapNumber = 0;
1498
-
1499
- this._initialisingMutex = new utils.Mutex();
1500
- this._initialised = false;
1501
- }
1502
-
1503
- async ensureInitialised_()
1504
- //========================
1505
- {
1506
- return await this._initialisingMutex.dispatch(async () => {
1507
- if (!this._initialised) {
1508
- this._mapList = [];
1509
- const maps = await this._mapServer.loadJSON('');
1510
- // Check map schema version (set by mapmaker) and
1511
- // remove maps we can't view (giving a console warning...)
1512
- for (const map of maps) {
1513
- // Are features in separate vector tile source layers?
1514
- map.separateLayers = ('version' in map && map.version >= MAP_MAKER_SEPARATE_LAYERS_VERSION);
1515
- this._mapList.push(map);
1516
- }
1517
- this._initialised = true;
1518
- }
1519
- });
1520
- }
1521
-
1522
- allMaps()
1523
- //=======
1524
- {
1525
- return new Promise(async(resolve, reject) => {
1526
- await this.ensureInitialised_();
1527
- const allMaps = {};
1528
- for (const map of this._mapList) {
1529
- const id = ('uuid' in map) ? map.uuid : map.id;
1530
- allMaps[id] = map;
1531
- }
1532
- resolve(allMaps);
1533
- });
1534
- }
1535
-
1536
- findMap_(identifier)
1537
- //==================
1538
- {
1539
- return new Promise(async(resolve, reject) => {
1540
- await this.ensureInitialised_();
1541
- resolve(this.lookupMap_(identifier));
1542
- });
1543
- }
1544
-
1545
- latestMap_(identifier)
1546
- //====================
1547
- {
1548
- const mapDescribes = (identifier.constructor.name === "String") ? identifier
1549
- : ('uuid' in identifier) ? identifier.uuid
1550
- : ('taxon' in identifier) ? identifier.taxon
1551
- : null;
1552
- if (mapDescribes === null) {
1553
- return null;
1554
- }
1555
- let latestMap = null;
1556
- let lastCreatedTime = '';
1557
- for (const map of this._mapList) {
1558
- if (('uuid' in map && mapDescribes === map.uuid
1559
- || mapDescribes === map.id
1560
- || 'taxon' in map && mapDescribes === map.taxon
1561
- || mapDescribes === map.source)
1562
- && (!('biologicalSex' in identifier)
1563
- || ('biologicalSex' in map
1564
- && identifier.biologicalSex === map.biologicalSex))) {
1565
- if ('created' in map) {
1566
- if (lastCreatedTime < map.created) {
1567
- lastCreatedTime = map.created;
1568
- latestMap = map;
1569
- }
1570
- } else {
1571
- latestMap = map;
1572
- break;
1573
- }
1574
- }
1575
- }
1576
- return latestMap;
1577
- }
1578
-
1579
- lookupMap_(identifier)
1580
- //====================
1581
- {
1582
- if (typeof identifier === 'object') {
1583
- return this.latestMap_(identifier);
1584
- }
1585
- return this.latestMap_({uuid: identifier});
1586
- }
1587
-
1588
- /**
1589
- * Load and display a FlatMap.
1590
- *
1591
- * @arg identifier {string|Object} A string or object identifying the map to load. If a string its
1592
- * value can be either the map's ``uuid``, assigned at generation time,
1593
- * or taxon and biological sex identifiers of the species that the map
1594
- * represents. The latest version of a map is loaded unless it has been
1595
- * identified using a ``uuid`` (see below).
1596
- * @arg identifier.taxon {string} The taxon identifier of the species represented by the map. This is
1597
- * specified as metadata in the map's source file.)
1598
- * @arg identifier.biologicalSex {string} The biological sex of the species represented by the map.
1599
- * This is specified as metadata in the map's source file.)
1600
- * @arg identifier.uuid {string} The unique uuid the flatmap. If given then this exact map will
1601
- * be loaded, overriding ``taxon`` and ``biologicalSex``.
1602
- * @arg container {string} The id of the HTML container in which to display the map.
1603
- * @arg callback {function(string, Object)} A callback function, invoked when events occur with the map. The
1604
- * first parameter gives the type of event, the second provides
1605
- * details about the event.
1606
- * @arg options {Object} Configurable options for the map.
1607
- * @arg options.background {string} Background colour of flatmap. Defaults to ``white``.
1608
- * @arg options.debug {boolean} Enable debugging mode.
1609
- * @arg options.flightPaths {boolean} Enable flight path (3D) view of neuron paths
1610
- * @arg options.fullscreenControl {boolean} Add a ``Show full screen`` button to the map.
1611
- * @arg options.layerOptions {Object} Options to control colour and outlines of features
1612
- * @arg options.layerOptions.colour {boolean} Use colour fill (if available) for features. Defaults to ``true``.
1613
- * @arg options.layerOptions.outline {boolean} Show the border of features. Defaults to ``true``.
1614
- * @arg options.layerOptions.sckan {string} Show neuron paths known to SCKAN: values are ``valid`` (default),
1615
- * ``invalid``, ``all`` or ``none``.
1616
- * @arg options.minimap {boolean|Object} Display a MiniMap of the flatmap. Defaults to ``false``.
1617
- * @arg options.minimap.position {string} The minimap's position: ``bottom-left`` (default), ``bottom-right``,
1618
- * ``top-left`` or ``top-right``.
1619
- * @arg options.minimap.width {number|string} The width of the minimap. Defaults to ``320px``. Can also
1620
- * be given as a percentage of the flatmap's width, e.g. ``10%``.
1621
- * The minimap's ``height`` is determined from its width using
1622
- * the flatmap's aspect ratio.
1623
- * @arg options.maxZoom {number} The maximum zoom level of the map.
1624
- * @arg options.minZoom {number} The minimum zoom level of the map.
1625
- * @arg options.navigationControl {boolean} Add navigation controls (zoom buttons) to the map.
1626
- * @arg options.showPosition {boolean} Show ``position`` of tooltip.
1627
- * @arg options.standalone {boolean} Viewer is running ``standalone``, as opposed to integrated into
1628
- * another application so show a number of controls. Defaults to ``false``.
1629
- * @example
1630
- * const humanMap1 = mapManager.loadMap('humanV1', 'div-1');
1631
- *
1632
- * const humanMap2 = mapManager.loadMap('NCBITaxon:9606', 'div-2');
1633
- *
1634
- * const humanMap3 = mapManager.loadMap({taxon: 'NCBITaxon:9606'}, 'div-3');
1635
- *
1636
- * const humanMap4 = mapManager.loadMap(
1637
- * {uuid: 'a563be90-9225-51c1-a84d-00ed2d03b7dc'},
1638
- * 'div-4');
1639
- */
1640
- loadMap(identifier, container, callback, options={})
1641
- //==================================================
1642
- {
1643
- return new Promise(async(resolve, reject) => {
1644
- try {
1645
- const map = await this.findMap_(identifier);
1646
- if (map === null) {
1647
- reject(`Unknown map: ${JSON.stringify(identifier)}`);
1648
- };
1649
-
1650
- // Load the maps index file
1651
-
1652
- const mapId = ('uuid' in map) ? map.uuid : map.id;
1653
- const mapIndex = await this._mapServer.loadJSON(`flatmap/${mapId}/`);
1654
- const mapIndexId = ('uuid' in mapIndex) ? mapIndex.uuid : mapIndex.id;
1655
- if (mapId !== mapIndexId) {
1656
- throw new Error(`Map '${mapId}' has wrong ID in index`);
1657
- }
1658
- const mapOptions = Object.assign({}, this._options, options);
1659
-
1660
- // If bounds are not specified in options then set them
1661
-
1662
- if (!('bounds' in options) && ('bounds' in mapIndex)) {
1663
- mapOptions['bounds'] = mapIndex['bounds'];
1664
- }
1665
-
1666
- // Note the kind of map
1667
-
1668
- if ('style' in mapIndex) {
1669
- mapOptions.style = mapIndex.style; // Currently ``anatomical`` or ``functional``
1670
- } else {
1671
- mapOptions.style = 'flatmap'; // Default is a generic ``flatmap``
1672
- }
1673
-
1674
- // Mapmaker has changed the name of the field to indicate that indicates if
1675
- // there are raster layers
1676
- if (!('image-layers' in mapIndex) && ('image_layer' in mapIndex)) {
1677
- mapIndex['image-layers'] = mapIndex['image_layer'];
1678
- }
1679
-
1680
- // Get details about the map's layers
1681
-
1682
- let mapLayers = [];
1683
- if (!('version' in mapIndex) || mapIndex.version <= 1.0) {
1684
- for (const layer of mapIndex.layers) {
1685
- // Set layer data if the layer just has an id specified
1686
- if (typeof layer === 'string') {
1687
- mapLayers.push({
1688
- id: layer,
1689
- description: layer.charAt(0).toUpperCase() + layer.slice(1),
1690
- selectable: true
1691
- });
1692
- } else {
1693
- mapLayers.push(layer);
1694
- }
1695
- }
1696
- } else {
1697
- mapLayers = await this._mapServer.loadJSON(`flatmap/${mapId}/layers`);
1698
- }
1699
-
1700
- // Get the map's style file
1701
-
1702
- const mapStyle = await this._mapServer.loadJSON(`flatmap/${mapId}/style`);
1703
-
1704
- // Make sure the style has glyphs defined
1705
-
1706
- if (!('glyphs' in mapStyle)) {
1707
- mapStyle.glyphs = 'https://fonts.openmaptiles.org/{fontstack}/{range}.pbf';
1708
- }
1709
-
1710
- // Get the map's pathways
1711
-
1712
- const pathways = await this._mapServer.loadJSON(`flatmap/${mapId}/pathways`);
1713
-
1714
- // Get the map's annotations
1715
-
1716
- const annotations = await this._mapServer.loadJSON(`flatmap/${mapId}/annotations`);
1717
-
1718
- // Get the map's provenance
1719
-
1720
- const provenance = await this._mapServer.loadJSON(`flatmap/${mapId}/metadata`);
1721
-
1722
- // Get additional marker details for the map
1723
-
1724
- const mapMarkers = await this._mapServer.loadJSON(`flatmap/${mapId}/markers`);
1725
-
1726
- // Set zoom range if not specified as an option
1727
-
1728
- if ('vector-tiles' in mapStyle.sources) {
1729
- if (!('minZoom' in mapOptions)) {
1730
- mapOptions['minZoom'] = mapStyle.sources['vector-tiles'].minzoom;
1731
- }
1732
- if (!('maxZoom' in mapOptions)) {
1733
- mapOptions['maxZoom'] = mapStyle.sources['vector-tiles'].maxzoom;
1734
- }
1735
- }
1736
-
1737
- // Make sure ``layerOptions`` are set
1738
-
1739
- if ('layerOptions' in mapOptions) {
1740
- if (!('colour' in mapOptions.layerOptions)) {
1741
- mapOptions.layerOptions.colour = true;
1742
- }
1743
- if (!('outline' in mapOptions.layerOptions)) {
1744
- mapOptions.layerOptions.outline = true;
1745
- }
1746
- } else {
1747
- mapOptions.layerOptions = {
1748
- colour: true,
1749
- outline: true
1750
- };
1751
- }
1752
- mapOptions.layerOptions.authoring = ('authoring' in mapIndex) ? mapIndex.authoring : false;
1753
-
1754
- // Are features in separate vector tile source layers?
1755
-
1756
- mapOptions.separateLayers = map.separateLayers;
1757
-
1758
- // Display the map
1759
-
1760
- this._mapNumber += 1;
1761
- const flatmap = new FlatMap(container, this._mapServer.url(),
1762
- {
1763
- id: map,
1764
- uuid: mapId,
1765
- details: mapIndex,
1766
- taxon: map.taxon,
1767
- biologicalSex: map.biologicalSex,
1768
- style: mapStyle,
1769
- options: mapOptions,
1770
- layers: mapLayers,
1771
- markers: mapMarkers,
1772
- annotations: annotations,
1773
- number: this._mapNumber,
1774
- pathways: pathways,
1775
- provenance, provenance,
1776
- callback: callback
1777
- },
1778
- resolve);
1779
-
1780
- return flatmap;
1781
-
1782
- } catch (err) {
1783
- reject(err);
1784
- }
1785
- });
1786
- }
1787
- }
1788
-
1789
- //==============================================================================