@abi-software/flatmap-viewer 2.6.1 → 2.7.0-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.
- package/README.rst +2 -2
- package/lib/index.ts +9 -0
- package/package.json +11 -21
- package/src/controls/annotation.js +1 -1
- package/src/controls/{paths3d.js → flightpaths.js} +11 -6
- package/src/flatmap-viewer.js +33 -14
- package/src/interactions.js +64 -33
- package/src/layers/cluster.js +191 -0
- package/src/layers/{paths3d.js → flightpaths.js} +3 -4
- package/src/layers/index.js +43 -22
- package/src/main.js +41 -0
- package/src/pathways.js +9 -4
- package/src/types.ts +26 -0
- package/src/utils.js +1 -0
package/README.rst
CHANGED
|
@@ -26,7 +26,7 @@ Running
|
|
|
26
26
|
|
|
27
27
|
::
|
|
28
28
|
|
|
29
|
-
$ npm
|
|
29
|
+
$ npm run dev
|
|
30
30
|
|
|
31
31
|
Maps can then be viewed at http://localhost:3000
|
|
32
32
|
|
|
@@ -38,7 +38,7 @@ The map server endpoint is specified as ``MAP_ENDPOINT`` in ``src/main.js``. It
|
|
|
38
38
|
Package Installation
|
|
39
39
|
====================
|
|
40
40
|
|
|
41
|
-
* ``npm install @abi-software/flatmap-viewer@2.6.
|
|
41
|
+
* ``npm install @abi-software/flatmap-viewer@2.6.2``
|
|
42
42
|
|
|
43
43
|
Documentation
|
|
44
44
|
-------------
|
package/lib/index.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//==============================================================================
|
|
2
|
+
|
|
3
|
+
import {FlatMap, MapManager} from '../src/flatmap-viewer'
|
|
4
|
+
|
|
5
|
+
//==============================================================================
|
|
6
|
+
|
|
7
|
+
export {FlatMap, MapManager}
|
|
8
|
+
|
|
9
|
+
//==============================================================================
|
package/package.json
CHANGED
|
@@ -1,26 +1,28 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abi-software/flatmap-viewer",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.0-a.1",
|
|
4
4
|
"description": "Flatmap viewer using Maplibre GL",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/AnatomicMaps/flatmap-viewer.git"
|
|
8
8
|
},
|
|
9
|
+
"type": "module",
|
|
9
10
|
"main": "src/main.js",
|
|
10
11
|
"files": [
|
|
12
|
+
"lib",
|
|
11
13
|
"src",
|
|
12
14
|
"static"
|
|
13
15
|
],
|
|
14
16
|
"scripts": {
|
|
15
17
|
"test": "echo \"Error: no test specified\" && exit 1",
|
|
16
|
-
"
|
|
17
|
-
"build": "
|
|
18
|
+
"dev": "vite serve app --port 3000",
|
|
19
|
+
"build": "tsc --p ./tsconfig-build.json && vite build",
|
|
20
|
+
"preview": "vite preview",
|
|
18
21
|
"docs": "cd docs; poetry run make html"
|
|
19
22
|
},
|
|
20
23
|
"author": "David Brooks",
|
|
21
24
|
"license": "MIT",
|
|
22
25
|
"dependencies": {
|
|
23
|
-
"@babel/runtime": "^7.10.4",
|
|
24
26
|
"@deck.gl/core": "^8.9.35",
|
|
25
27
|
"@deck.gl/layers": "^8.9.35",
|
|
26
28
|
"@deck.gl/mapbox": "^8.9.35",
|
|
@@ -33,28 +35,16 @@
|
|
|
33
35
|
"colord": "^2.9.3",
|
|
34
36
|
"core-js-pure": "^3.36.1",
|
|
35
37
|
"html-es6cape": "^2.0.2",
|
|
36
|
-
"maplibre-gl": ">=
|
|
38
|
+
"maplibre-gl": ">=4.1.0",
|
|
37
39
|
"mathjax-full": "^3.2.2",
|
|
38
40
|
"minisearch": "^2.2.1",
|
|
39
41
|
"polylabel": "^1.1.0"
|
|
40
42
|
},
|
|
41
43
|
"devDependencies": {
|
|
42
|
-
"@
|
|
43
|
-
"@babel/plugin-transform-runtime": "^7.5.5",
|
|
44
|
-
"@babel/preset-env": "^7.10.4",
|
|
45
|
-
"babel-loader": "^8.1.0",
|
|
46
|
-
"browser-sync": "^2.26.7",
|
|
47
|
-
"bs-fullscreen-message": "^1.1.0",
|
|
48
|
-
"clean-webpack-plugin": "^3.0.0",
|
|
49
|
-
"css-loader": "^6.7.3",
|
|
44
|
+
"@types/node": "^20.12.7",
|
|
50
45
|
"eslint": "^8.7.0",
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"style-loader": "^3.3.2",
|
|
55
|
-
"webpack": "^5.16.0",
|
|
56
|
-
"webpack-cli": "^4.4.0",
|
|
57
|
-
"webpack-dev-middleware": "^4.1.0",
|
|
58
|
-
"webpack-node-externals": "^1.7.2"
|
|
46
|
+
"typescript": "^5.2.2",
|
|
47
|
+
"vite": "^5.1.4",
|
|
48
|
+
"vite-plugin-dts": "^3.8.1"
|
|
59
49
|
}
|
|
60
50
|
}
|
|
@@ -18,7 +18,7 @@ limitations under the License.
|
|
|
18
18
|
|
|
19
19
|
******************************************************************************/
|
|
20
20
|
|
|
21
|
-
export class
|
|
21
|
+
export class FlightPathControl
|
|
22
22
|
{
|
|
23
23
|
#button
|
|
24
24
|
#container
|
|
@@ -26,9 +26,10 @@ export class Path3DControl
|
|
|
26
26
|
#map = null
|
|
27
27
|
#flatmap
|
|
28
28
|
|
|
29
|
-
constructor(flatmap)
|
|
29
|
+
constructor(flatmap, enabled)
|
|
30
30
|
{
|
|
31
31
|
this.#flatmap = flatmap
|
|
32
|
+
this.#enabled = !!enabled
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
getDefaultPosition()
|
|
@@ -46,11 +47,15 @@ export class Path3DControl
|
|
|
46
47
|
this.#button = document.createElement('button')
|
|
47
48
|
this.#button.className = 'control-button text-button'
|
|
48
49
|
this.#button.setAttribute('type', 'button')
|
|
49
|
-
this.#button.setAttribute('aria-label', 'Show
|
|
50
|
+
this.#button.setAttribute('aria-label', 'Show flight paths')
|
|
50
51
|
this.#button.textContent = '3D'
|
|
51
|
-
this.#button.title = 'Show/hide
|
|
52
|
+
this.#button.title = 'Show/hide flight paths'
|
|
52
53
|
this.#container.appendChild(this.#button)
|
|
53
54
|
this.#container.addEventListener('click', this.onClick.bind(this))
|
|
55
|
+
if (this.#enabled) {
|
|
56
|
+
this.#button.classList.add('control-active')
|
|
57
|
+
this.__setBackground()
|
|
58
|
+
}
|
|
54
59
|
return this.#container
|
|
55
60
|
}
|
|
56
61
|
|
|
@@ -75,11 +80,11 @@ export class Path3DControl
|
|
|
75
80
|
//=============
|
|
76
81
|
{
|
|
77
82
|
if (this.#button.classList.contains('control-active')) {
|
|
78
|
-
this.#flatmap.
|
|
83
|
+
this.#flatmap.enableFlightPaths(false)
|
|
79
84
|
this.#button.classList.remove('control-active')
|
|
80
85
|
this.#enabled = false
|
|
81
86
|
} else {
|
|
82
|
-
this.#flatmap.
|
|
87
|
+
this.#flatmap.enableFlightPaths(true)
|
|
83
88
|
this.#button.classList.add('control-active')
|
|
84
89
|
this.#enabled = true
|
|
85
90
|
}
|
package/src/flatmap-viewer.js
CHANGED
|
@@ -33,12 +33,14 @@ import '../static/css/flatmap-viewer.css';
|
|
|
33
33
|
|
|
34
34
|
//==============================================================================
|
|
35
35
|
|
|
36
|
-
import {MapServer
|
|
36
|
+
import {MapServer} from './mapserver.js';
|
|
37
37
|
import {SearchIndex} from './search.js';
|
|
38
38
|
import {UserInteractions} from './interactions.js';
|
|
39
39
|
|
|
40
40
|
import {APINATOMY_PATH_PREFIX} from './pathways';
|
|
41
41
|
|
|
42
|
+
import {loadClusterIcons} from './layers/cluster'
|
|
43
|
+
|
|
42
44
|
import * as images from './images.js';
|
|
43
45
|
import * as utils from './utils.js';
|
|
44
46
|
|
|
@@ -61,7 +63,7 @@ export const UNCLASSIFIED_TAXON_ID = 'NCBITaxon:2787823'; // unclassified entr
|
|
|
61
63
|
* Maps are not created directly but instead are created and loaded by
|
|
62
64
|
* :meth:`LoadMap` of :class:`MapManager`.
|
|
63
65
|
*/
|
|
64
|
-
class FlatMap
|
|
66
|
+
export class FlatMap
|
|
65
67
|
{
|
|
66
68
|
constructor(container, mapBaseUrl, mapDescription, resolve)
|
|
67
69
|
{
|
|
@@ -204,6 +206,9 @@ class FlatMap
|
|
|
204
206
|
await this.addImage(image.id, image.url, '', image.options);
|
|
205
207
|
}
|
|
206
208
|
|
|
209
|
+
// Load icons used for clustered markers
|
|
210
|
+
await loadClusterIcons(this._map)
|
|
211
|
+
|
|
207
212
|
// Layers have now loaded so finish setting up
|
|
208
213
|
this._userInteractions = new UserInteractions(this);
|
|
209
214
|
}
|
|
@@ -901,15 +906,15 @@ class FlatMap
|
|
|
901
906
|
}
|
|
902
907
|
|
|
903
908
|
/**
|
|
904
|
-
* Show/hide
|
|
909
|
+
* Show/hide flight path view.
|
|
905
910
|
*
|
|
906
911
|
* @param {boolean} [enable=true]
|
|
907
912
|
*/
|
|
908
|
-
|
|
909
|
-
|
|
913
|
+
enableFlightPaths(enable=true)
|
|
914
|
+
//============================
|
|
910
915
|
{
|
|
911
916
|
if (this._userInteractions !== null) {
|
|
912
|
-
this._userInteractions.
|
|
917
|
+
this._userInteractions.enableFlightPaths(enable)
|
|
913
918
|
}
|
|
914
919
|
}
|
|
915
920
|
|
|
@@ -966,6 +971,21 @@ class FlatMap
|
|
|
966
971
|
return -1;
|
|
967
972
|
}
|
|
968
973
|
|
|
974
|
+
addMarkers(anatomicalIds, options={})
|
|
975
|
+
//====================================
|
|
976
|
+
{
|
|
977
|
+
const markerIds = []
|
|
978
|
+
for (const anatomicalId of anatomicalIds) {
|
|
979
|
+
if (this._userInteractions !== null) {
|
|
980
|
+
markerIds.push(this._userInteractions.addMarker(anatomicalId, options))
|
|
981
|
+
} else {
|
|
982
|
+
markerIds.push(-1)
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
return markerIds
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
|
|
969
989
|
/**
|
|
970
990
|
* Remove a marker from the map.
|
|
971
991
|
*
|
|
@@ -975,7 +995,7 @@ class FlatMap
|
|
|
975
995
|
removeMarker(markerId)
|
|
976
996
|
//====================
|
|
977
997
|
{
|
|
978
|
-
if (this._userInteractions !== null) {
|
|
998
|
+
if (markerId > -1 && this._userInteractions !== null) {
|
|
979
999
|
this._userInteractions.removeMarker(markerId);
|
|
980
1000
|
}
|
|
981
1001
|
}
|
|
@@ -1046,19 +1066,17 @@ class FlatMap
|
|
|
1046
1066
|
'alert',
|
|
1047
1067
|
'biological-sex'
|
|
1048
1068
|
];
|
|
1049
|
-
const jsonProperties = [
|
|
1050
|
-
'hyperlinks'
|
|
1051
|
-
];
|
|
1052
1069
|
for (const property of exportedProperties) {
|
|
1053
1070
|
if (property in properties) {
|
|
1054
1071
|
const value = properties[property];
|
|
1055
1072
|
if (value !== undefined) {
|
|
1056
|
-
if (
|
|
1057
|
-
|
|
1073
|
+
if ((Array.isArray(value) && value.length)
|
|
1074
|
+
|| (value.constructor === Object && Object.keys(value).length)) {
|
|
1075
|
+
data[property] = value
|
|
1058
1076
|
} else if (property === 'featureId') {
|
|
1059
|
-
data[property] = +
|
|
1077
|
+
data[property] = +value; // Ensure numeric
|
|
1060
1078
|
} else {
|
|
1061
|
-
data[property] =
|
|
1079
|
+
data[property] = value;
|
|
1062
1080
|
}
|
|
1063
1081
|
}
|
|
1064
1082
|
}
|
|
@@ -1569,6 +1587,7 @@ export class MapManager
|
|
|
1569
1587
|
* @arg options {Object} Configurable options for the map.
|
|
1570
1588
|
* @arg options.background {string} Background colour of flatmap. Defaults to ``white``.
|
|
1571
1589
|
* @arg options.debug {boolean} Enable debugging mode.
|
|
1590
|
+
* @arg options.flightPaths {boolean} Enable flight path (3D) view of neuron paths
|
|
1572
1591
|
* @arg options.fullscreenControl {boolean} Add a ``Show full screen`` button to the map.
|
|
1573
1592
|
* @arg options.layerOptions {Object} Options to control colour and outlines of features
|
|
1574
1593
|
* @arg options.layerOptions.colour {boolean} Use colour fill (if available) for features. Defaults to ``true``.
|
package/src/interactions.js
CHANGED
|
@@ -43,7 +43,7 @@ import {AnnotatorControl, BackgroundControl, LayerControl, NerveControl,
|
|
|
43
43
|
SCKANControl} from './controls/controls';
|
|
44
44
|
import {AnnotationDrawControl, DRAW_ANNOTATION_LAYERS} from './controls/annotation'
|
|
45
45
|
import {PathControl} from './controls/paths';
|
|
46
|
-
import {
|
|
46
|
+
import {FlightPathControl} from './controls/flightpaths'
|
|
47
47
|
import {SearchControl} from './controls/search';
|
|
48
48
|
import {MinimapControl} from './controls/minimap';
|
|
49
49
|
import {SystemsControl} from './controls/systems';
|
|
@@ -180,12 +180,14 @@ export class UserInteractions
|
|
|
180
180
|
const mapPathTypes = this.__pathManager.pathTypes();
|
|
181
181
|
|
|
182
182
|
// Add and manage our layers. NB. this needs to after we have a
|
|
183
|
-
// path manager but before
|
|
183
|
+
// path manager but before paths are enabled
|
|
184
184
|
|
|
185
185
|
this._layerManager = new LayerManager(flatmap, this);
|
|
186
186
|
|
|
187
187
|
// Set initial enabled state of paths
|
|
188
188
|
|
|
189
|
+
this.__pathManager.enablePathLines(true, true)
|
|
190
|
+
|
|
189
191
|
for (const path of mapPathTypes) {
|
|
190
192
|
this.__pathManager.enablePathsByType(path.type, path.enabled, true);
|
|
191
193
|
}
|
|
@@ -251,7 +253,7 @@ export class UserInteractions
|
|
|
251
253
|
this._map.addControl(new TaxonsControl(flatmap));
|
|
252
254
|
}
|
|
253
255
|
|
|
254
|
-
this._map.addControl(new
|
|
256
|
+
this._map.addControl(new FlightPathControl(this, flatmap.options.flightPaths));
|
|
255
257
|
|
|
256
258
|
if (flatmap.options.annotator) {
|
|
257
259
|
this._map.addControl(new AnnotatorControl(flatmap));
|
|
@@ -265,6 +267,11 @@ export class UserInteractions
|
|
|
265
267
|
this.#annotationDrawControl = new AnnotationDrawControl(flatmap, false)
|
|
266
268
|
this._map.addControl(this.#annotationDrawControl)
|
|
267
269
|
|
|
270
|
+
// Set initial path viewing mode
|
|
271
|
+
if (flatmap.options.flightPaths === true) {
|
|
272
|
+
this._layerManager.setFlightPathMode(true)
|
|
273
|
+
}
|
|
274
|
+
|
|
268
275
|
// Handle mouse events
|
|
269
276
|
|
|
270
277
|
this._map.on('click', this.clickEvent_.bind(this));
|
|
@@ -456,10 +463,10 @@ export class UserInteractions
|
|
|
456
463
|
this._layerManager.activate(layerId, enable);
|
|
457
464
|
}
|
|
458
465
|
|
|
459
|
-
|
|
460
|
-
|
|
466
|
+
enableFlightPaths(enable=true)
|
|
467
|
+
//============================
|
|
461
468
|
{
|
|
462
|
-
this._layerManager.
|
|
469
|
+
this._layerManager.setFlightPathMode(enable)
|
|
463
470
|
}
|
|
464
471
|
|
|
465
472
|
getSystems()
|
|
@@ -1305,6 +1312,7 @@ export class UserInteractions
|
|
|
1305
1312
|
// Marker handling
|
|
1306
1313
|
|
|
1307
1314
|
__markerPosition(featureId, annotation)
|
|
1315
|
+
//=====================================
|
|
1308
1316
|
{
|
|
1309
1317
|
if (this.__markerPositions.has(featureId)) {
|
|
1310
1318
|
return this.__markerPositions.get(featureId);
|
|
@@ -1364,23 +1372,29 @@ export class UserInteractions
|
|
|
1364
1372
|
markerOptions.className = options.className;
|
|
1365
1373
|
}
|
|
1366
1374
|
const markerPosition = this.__markerPosition(featureId, annotation);
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1375
|
+
if (options.cluster && this._layerManager) {
|
|
1376
|
+
this._layerManager.addMarker(markerId, markerPosition, annotation)
|
|
1377
|
+
} else {
|
|
1378
|
+
const marker = new maplibregl.Marker(markerOptions)
|
|
1379
|
+
.setLngLat(markerPosition)
|
|
1380
|
+
.addTo(this._map);
|
|
1381
|
+
|
|
1382
|
+
|
|
1383
|
+
markerElement.addEventListener('mouseenter',
|
|
1384
|
+
this.markerMouseEvent_.bind(this, marker, anatomicalId));
|
|
1385
|
+
markerElement.addEventListener('mousemove',
|
|
1386
|
+
this.markerMouseEvent_.bind(this, marker, anatomicalId));
|
|
1387
|
+
markerElement.addEventListener('mouseleave',
|
|
1388
|
+
this.markerMouseEvent_.bind(this, marker, anatomicalId));
|
|
1389
|
+
markerElement.addEventListener('click',
|
|
1390
|
+
this.markerMouseEvent_.bind(this, marker, anatomicalId));
|
|
1391
|
+
|
|
1392
|
+
this.__markerIdByMarker.set(marker, markerId);
|
|
1393
|
+
this.__markerIdByFeatureId.set(+featureId, markerId);
|
|
1394
|
+
this.__annotationByMarkerId.set(markerId, annotation);
|
|
1395
|
+
if (!this.__featureEnabled(this.mapFeature(+featureId))) {
|
|
1396
|
+
markerElement.style.visibility = 'hidden';
|
|
1397
|
+
}
|
|
1384
1398
|
}
|
|
1385
1399
|
}
|
|
1386
1400
|
}
|
|
@@ -1429,28 +1443,46 @@ export class UserInteractions
|
|
|
1429
1443
|
return anatomicalIds;
|
|
1430
1444
|
}
|
|
1431
1445
|
|
|
1446
|
+
// Separate out MapLibre specific code and result of mouse event (tooltip,
|
|
1447
|
+
// client message, etc) so clustering code can also use this to process
|
|
1448
|
+
// events.
|
|
1449
|
+
|
|
1432
1450
|
markerMouseEvent_(marker, anatomicalId, event)
|
|
1433
1451
|
//============================================
|
|
1434
1452
|
{
|
|
1435
1453
|
// No tooltip when context menu is open
|
|
1436
1454
|
if (this._modal
|
|
1437
1455
|
|| (this.__activeMarker !== null && event.type === 'mouseleave')) {
|
|
1438
|
-
return
|
|
1456
|
+
return
|
|
1439
1457
|
}
|
|
1440
1458
|
|
|
1441
1459
|
if (['mouseenter', 'mouseleave', 'click'].includes(event.type)) {
|
|
1442
|
-
this.__activeMarker = marker
|
|
1460
|
+
this.__activeMarker = marker
|
|
1443
1461
|
|
|
1444
|
-
// Remove any
|
|
1445
|
-
|
|
1446
|
-
marker.setPopup(null);
|
|
1462
|
+
// Remove any tooltip
|
|
1463
|
+
marker.setPopup(null)
|
|
1447
1464
|
|
|
1448
1465
|
// Reset cursor
|
|
1449
1466
|
marker.getElement().style.cursor = 'default';
|
|
1450
1467
|
|
|
1468
|
+
|
|
1469
|
+
const markerId = this.__markerIdByMarker.get(marker)
|
|
1470
|
+
const annotation = this.__annotationByMarkerId.get(markerId)
|
|
1471
|
+
|
|
1472
|
+
this.markerEvent_(event, markerId, marker.getLngLat(),
|
|
1473
|
+
anatomicalId, annotation)
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
markerEvent_(event, markerId, markerPosition, anatomicalId, annotation)
|
|
1478
|
+
//=====================================================================
|
|
1479
|
+
{
|
|
1480
|
+
if (['mouseenter', 'mouseleave', 'click'].includes(event.type)) {
|
|
1481
|
+
|
|
1482
|
+
// Remove any existing tooltips
|
|
1483
|
+
this.removeTooltip_();
|
|
1484
|
+
|
|
1451
1485
|
if (['mouseenter', 'click'].includes(event.type)) {
|
|
1452
|
-
const markerId = this.__markerIdByMarker.get(marker);
|
|
1453
|
-
const annotation = this.__annotationByMarkerId.get(markerId);
|
|
1454
1486
|
// The marker's feature
|
|
1455
1487
|
const feature = this.mapFeature(annotation.featureId);
|
|
1456
1488
|
if (feature !== undefined) {
|
|
@@ -1464,13 +1496,12 @@ export class UserInteractions
|
|
|
1464
1496
|
}
|
|
1465
1497
|
// Show tooltip
|
|
1466
1498
|
const html = this.tooltipHtml_(annotation, true);
|
|
1467
|
-
this.__showToolTip(html,
|
|
1499
|
+
this.__showToolTip(html, markerPosition);
|
|
1468
1500
|
|
|
1469
1501
|
// Send marker event message
|
|
1470
1502
|
this._flatmap.markerEvent(event.type, markerId, anatomicalId);
|
|
1471
1503
|
}
|
|
1472
1504
|
}
|
|
1473
|
-
event.stopPropagation();
|
|
1474
1505
|
}
|
|
1475
1506
|
|
|
1476
1507
|
__clearActiveMarker()
|
|
@@ -1519,7 +1550,7 @@ export class UserInteractions
|
|
|
1519
1550
|
.setLngLat(location)
|
|
1520
1551
|
.setDOMContent(element);
|
|
1521
1552
|
|
|
1522
|
-
// Set the
|
|
1553
|
+
// Set the marker tooltip and show it
|
|
1523
1554
|
marker.setPopup(this._tooltip);
|
|
1524
1555
|
marker.togglePopup();
|
|
1525
1556
|
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/******************************************************************************
|
|
2
|
+
|
|
3
|
+
Flatmap viewer and annotation tool
|
|
4
|
+
|
|
5
|
+
Copyright (c) 2019 - 2023 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
|
+
import {SvgManager, SvgTemplateManager} from '../../thirdParty/maplibre-gl-svg/src'
|
|
22
|
+
|
|
23
|
+
//==============================================================================
|
|
24
|
+
|
|
25
|
+
const markerLargeCircle = `<svg xmlns="http://www.w3.org/2000/svg" width="calc(28 * {scale})" height="calc(39 * {scale})" viewBox="-1 -1 27 42">
|
|
26
|
+
<ellipse style="fill: rgb(0, 0, 0); fill-opacity: 0.2;" cx="12" cy="36" rx="8" ry="4"/>
|
|
27
|
+
<path d="M12.25.25a12.254 12.254 0 0 0-12 12.494c0 6.444 6.488 12.109 11.059 22.564.549 1.256 1.333 1.256 1.882 0
|
|
28
|
+
C17.762 24.853 24.25 19.186 24.25 12.744A12.254 12.254 0 0 0 12.25.25Z"
|
|
29
|
+
style="fill:{color};stroke:{secondaryColor};stroke-width:1"/>
|
|
30
|
+
<circle cx="12.5" cy="12.5" r="9" fill="{secondaryColor}"/>
|
|
31
|
+
<text x="12" y="17.5" style="font-size:14px;fill:#000;text-anchor:middle">{text}</text>
|
|
32
|
+
</svg>`
|
|
33
|
+
|
|
34
|
+
const markerSmallCircle = `<svg xmlns="http://www.w3.org/2000/svg" width="calc(28 * {scale})" height="calc(39 * {scale})" viewBox="-1 -1 27 42">
|
|
35
|
+
<ellipse style="fill: rgb(0, 0, 0); fill-opacity: 0.2;" cx="12" cy="36" rx="8" ry="4"/>
|
|
36
|
+
<path d="M12.25.25a12.254 12.254 0 0 0-12 12.494c0 6.444 6.488 12.109 11.059 22.564.549 1.256 1.333 1.256 1.882 0
|
|
37
|
+
C17.762 24.853 24.25 19.186 24.25 12.744A12.254 12.254 0 0 0 12.25.25Z"
|
|
38
|
+
style="fill:{color};stroke:{secondaryColor};stroke-width:1"/>
|
|
39
|
+
<circle cx="12.5" cy="12.5" r="5" fill="{secondaryColor}"/>
|
|
40
|
+
</svg>`
|
|
41
|
+
|
|
42
|
+
//==============================================================================
|
|
43
|
+
|
|
44
|
+
export async function loadClusterIcons(map)
|
|
45
|
+
{
|
|
46
|
+
SvgTemplateManager.addTemplate('marker-large-circle', markerLargeCircle, false)
|
|
47
|
+
SvgTemplateManager.addTemplate('marker-small-circle', markerSmallCircle, false)
|
|
48
|
+
|
|
49
|
+
const svgManager = new SvgManager(map)
|
|
50
|
+
await svgManager.createFromTemplate('clustered-marker', 'marker-large-circle', '#EE5900', '#fff')
|
|
51
|
+
await svgManager.createFromTemplate('unclustered-marker', 'marker-small-circle', '#005974', '#fff')
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
//==============================================================================
|
|
55
|
+
|
|
56
|
+
export class ClusteredMarkerLayer
|
|
57
|
+
{
|
|
58
|
+
#flatmap
|
|
59
|
+
#map
|
|
60
|
+
#points = {
|
|
61
|
+
type: 'FeatureCollection',
|
|
62
|
+
features: []
|
|
63
|
+
}
|
|
64
|
+
#ui
|
|
65
|
+
|
|
66
|
+
seenmove = false
|
|
67
|
+
|
|
68
|
+
constructor(flatmap, ui)
|
|
69
|
+
{
|
|
70
|
+
this.#flatmap = flatmap
|
|
71
|
+
this.#ui = ui
|
|
72
|
+
this.#map = flatmap.map
|
|
73
|
+
|
|
74
|
+
this.#map.addSource('markers', {
|
|
75
|
+
type: 'geojson',
|
|
76
|
+
data: this.#points,
|
|
77
|
+
cluster: true, // Adds the ``point_count`` property to source data
|
|
78
|
+
clusterMaxZoom: 9, // Max zoom to cluster points on
|
|
79
|
+
clusterRadius: 50 // Radius of each cluster when clustering points (defaults to 50)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
this.#map.addLayer({
|
|
83
|
+
id: 'clustered-markers',
|
|
84
|
+
type: 'symbol',
|
|
85
|
+
source: 'markers',
|
|
86
|
+
filter: ['has', 'point_count'],
|
|
87
|
+
'layout': {
|
|
88
|
+
'icon-image': 'clustered-marker',
|
|
89
|
+
'icon-allow-overlap': true,
|
|
90
|
+
'icon-ignore-placement': true,
|
|
91
|
+
'icon-offset': [0, -17],
|
|
92
|
+
'icon-size': 0.8,
|
|
93
|
+
'text-field': '{point_count_abbreviated}',
|
|
94
|
+
'text-size': 10,
|
|
95
|
+
'text-offset': [0, -1.93]
|
|
96
|
+
}
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
this.#map.addLayer({
|
|
100
|
+
id: 'single-points',
|
|
101
|
+
type: 'symbol',
|
|
102
|
+
source: 'markers',
|
|
103
|
+
filter: ['!', ['has', 'point_count']],
|
|
104
|
+
'layout': {
|
|
105
|
+
'icon-image': 'unclustered-marker',
|
|
106
|
+
'icon-allow-overlap': true,
|
|
107
|
+
'icon-ignore-placement': true,
|
|
108
|
+
'icon-offset': [0, -17],
|
|
109
|
+
'icon-size': 0.6
|
|
110
|
+
}
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
// inspect a cluster on click
|
|
114
|
+
this.#map.on('click', 'clustered-markers', async (e) => {
|
|
115
|
+
const features = this.#map.queryRenderedFeatures(e.point, {
|
|
116
|
+
layers: ['clustered-markers']
|
|
117
|
+
})
|
|
118
|
+
console.log('Cluster marker', features)
|
|
119
|
+
const clusterId = features[0].properties.cluster_id
|
|
120
|
+
const zoom = await this.#map.getSource('markers').getClusterExpansionZoom(clusterId)
|
|
121
|
+
this.#map.easeTo({
|
|
122
|
+
center: features[0].geometry.coordinates,
|
|
123
|
+
zoom
|
|
124
|
+
})
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
this.#map.on('click', 'single-points', this.singleMarkerEvent.bind(this))
|
|
128
|
+
this.#map.on('mouseenter', 'single-points', this.singleMarkerEvent.bind(this))
|
|
129
|
+
this.#map.on('mousemove', 'single-points', this.singleMarkerEvent.bind(this))
|
|
130
|
+
// this.#map.on('mouseleave', 'single-points', this.singleMarkerEvent.bind(this))
|
|
131
|
+
|
|
132
|
+
this.#map.on('mouseenter', 'clustered-markers', () => {
|
|
133
|
+
this.#map.getCanvas().style.cursor = 'pointer'
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
this.#map.on('mouseleave', 'clustered-markers', () => {
|
|
137
|
+
this.#map.getCanvas().style.cursor = ''
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
// Also 'mousemove'...
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
singleMarkerEvent(event)
|
|
144
|
+
//======================
|
|
145
|
+
{
|
|
146
|
+
const features = this.#map.queryRenderedFeatures(event.point, {
|
|
147
|
+
layers: ['single-points']
|
|
148
|
+
})
|
|
149
|
+
for (const feature of features) {
|
|
150
|
+
if (event.type === 'mousemove' && !this.seenMove) {
|
|
151
|
+
console.log('Single marker', event.type, feature)
|
|
152
|
+
this.seenMove = true
|
|
153
|
+
} else if (event.type !== 'mousemove') {
|
|
154
|
+
console.log('Single marker', event.type, feature)
|
|
155
|
+
this.seenMove = false
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const properties = feature.properties
|
|
159
|
+
const position = properties.markerPosition.slice(1, -1).split(',').map(p => +p)
|
|
160
|
+
this.#ui.markerEvent_(event, feature.id, position, properties.models, properties)
|
|
161
|
+
}
|
|
162
|
+
event.originalEvent.stopPropagation()
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
addMarker(id, position, properties={})
|
|
166
|
+
//====================================
|
|
167
|
+
{
|
|
168
|
+
this.#points.features.push({
|
|
169
|
+
type: 'Feature',
|
|
170
|
+
id,
|
|
171
|
+
properties,
|
|
172
|
+
geometry: {
|
|
173
|
+
type: 'Point',
|
|
174
|
+
coordinates: position
|
|
175
|
+
}
|
|
176
|
+
})
|
|
177
|
+
this.#map.getSource('markers')
|
|
178
|
+
.setData(this.#points)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
clearMarkers()
|
|
182
|
+
//============
|
|
183
|
+
{
|
|
184
|
+
this.#points.features = []
|
|
185
|
+
this.#map.getSource('markers')
|
|
186
|
+
.setData(this.#points)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
//==============================================================================
|
|
191
|
+
|
|
@@ -135,7 +135,7 @@ class ArcDashedLayer extends ArcMapLayer
|
|
|
135
135
|
|
|
136
136
|
//==============================================================================
|
|
137
137
|
|
|
138
|
-
export class
|
|
138
|
+
export class FlightPathLayer
|
|
139
139
|
{
|
|
140
140
|
#arcLayers = new Map()
|
|
141
141
|
#deckOverlay = null
|
|
@@ -344,14 +344,12 @@ export class Paths3DLayer
|
|
|
344
344
|
id: `arc-${pathType}`,
|
|
345
345
|
data: pathData,
|
|
346
346
|
pickable: true,
|
|
347
|
-
autoHighlight: true,
|
|
348
347
|
numSegments: 400,
|
|
349
348
|
// Styles
|
|
350
349
|
getSourcePosition: f => f.pathStartPosition,
|
|
351
350
|
getTargetPosition: f => f.pathEndPosition,
|
|
352
351
|
getSourceColor: this.#pathColour.bind(this),
|
|
353
352
|
getTargetColor: this.#pathColour.bind(this),
|
|
354
|
-
highlightColor: o => this.#pathColour(o.object),
|
|
355
353
|
opacity: 1.0,
|
|
356
354
|
getWidth: 3,
|
|
357
355
|
}
|
|
@@ -366,13 +364,14 @@ export class Paths3DLayer
|
|
|
366
364
|
source: 'vector-tiles',
|
|
367
365
|
sourceLayer: `${pickedObject.layer}_${pickedObject['tile-layer']}`,
|
|
368
366
|
properties: pickedObject,
|
|
369
|
-
|
|
367
|
+
flightPath: true
|
|
370
368
|
}
|
|
371
369
|
}
|
|
372
370
|
|
|
373
371
|
#setupDeckOverlay()
|
|
374
372
|
//=================
|
|
375
373
|
{
|
|
374
|
+
// One overlay layer for each path style
|
|
376
375
|
[...this.#pathStyles.values()].filter(style => this.#pathManager.pathTypeEnabled(style.type))
|
|
377
376
|
.forEach(style => this.#addArcLayer(style.type))
|
|
378
377
|
this.#deckOverlay = new DeckOverlay({
|
package/src/layers/index.js
CHANGED
|
@@ -25,9 +25,10 @@ limitations under the License.
|
|
|
25
25
|
import {PATHWAYS_LAYER} from '../pathways.js';
|
|
26
26
|
import * as utils from '../utils.js';
|
|
27
27
|
|
|
28
|
+
import {ClusteredMarkerLayer} from './cluster'
|
|
28
29
|
import * as style from './styling.js';
|
|
29
30
|
|
|
30
|
-
import {
|
|
31
|
+
import {FlightPathLayer} from './flightpaths'
|
|
31
32
|
import {PropertiesFilter} from './filter'
|
|
32
33
|
|
|
33
34
|
const FEATURES_LAYER = 'features';
|
|
@@ -183,8 +184,8 @@ class MapFeatureLayers extends MapStylingLayers
|
|
|
183
184
|
}
|
|
184
185
|
}
|
|
185
186
|
|
|
186
|
-
|
|
187
|
-
|
|
187
|
+
setFlatPathMode(visible)
|
|
188
|
+
//======================
|
|
188
189
|
{
|
|
189
190
|
for (const layer of this.#pathLayers) {
|
|
190
191
|
this.map.setLayoutProperty(layer.id, 'visibility', visible ? 'visible' : 'none')
|
|
@@ -266,7 +267,8 @@ class MapRasterLayers extends MapStylingLayers
|
|
|
266
267
|
export class LayerManager
|
|
267
268
|
{
|
|
268
269
|
#featureLayers = new Map()
|
|
269
|
-
#
|
|
270
|
+
#markerLayer = null
|
|
271
|
+
#flightPathLayer = null
|
|
270
272
|
#rasterLayer = null
|
|
271
273
|
|
|
272
274
|
constructor(flatmap, ui)
|
|
@@ -306,8 +308,11 @@ export class LayerManager
|
|
|
306
308
|
this.__layerOptions));
|
|
307
309
|
}
|
|
308
310
|
|
|
309
|
-
// Support
|
|
310
|
-
this.#
|
|
311
|
+
// Support flight path view
|
|
312
|
+
this.#flightPathLayer = new FlightPathLayer(flatmap, ui)
|
|
313
|
+
|
|
314
|
+
// Show clustered markers in a layer
|
|
315
|
+
this.#markerLayer = new ClusteredMarkerLayer(flatmap, ui)
|
|
311
316
|
}
|
|
312
317
|
|
|
313
318
|
get layers()
|
|
@@ -356,12 +361,24 @@ export class LayerManager
|
|
|
356
361
|
}
|
|
357
362
|
}
|
|
358
363
|
|
|
364
|
+
addMarker(id, position, properties={})
|
|
365
|
+
//====================================
|
|
366
|
+
{
|
|
367
|
+
this.#markerLayer.addMarker(id, position, properties)
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
clearMarkers()
|
|
371
|
+
//============
|
|
372
|
+
{
|
|
373
|
+
this.#markerLayer.clearMarkers()
|
|
374
|
+
}
|
|
375
|
+
|
|
359
376
|
featuresAtPoint(point)
|
|
360
377
|
//====================
|
|
361
378
|
{
|
|
362
379
|
let features = []
|
|
363
|
-
if (this.#
|
|
364
|
-
features = this.#
|
|
380
|
+
if (this.#flightPathLayer) {
|
|
381
|
+
features = this.#flightPathLayer.queryFeaturesAtPoint(point)
|
|
365
382
|
}
|
|
366
383
|
if (features.length === 0) {
|
|
367
384
|
features = this.__map.queryRenderedFeatures(point)
|
|
@@ -372,16 +389,16 @@ export class LayerManager
|
|
|
372
389
|
removeFeatureState(feature, key)
|
|
373
390
|
//==============================
|
|
374
391
|
{
|
|
375
|
-
if (this.#
|
|
376
|
-
this.#
|
|
392
|
+
if (this.#flightPathLayer) {
|
|
393
|
+
this.#flightPathLayer.removeFeatureState(feature.id, key)
|
|
377
394
|
}
|
|
378
395
|
}
|
|
379
396
|
|
|
380
397
|
setFeatureState(feature, state)
|
|
381
398
|
//=============================
|
|
382
399
|
{
|
|
383
|
-
if (this.#
|
|
384
|
-
this.#
|
|
400
|
+
if (this.#flightPathLayer) {
|
|
401
|
+
this.#flightPathLayer.setFeatureState(feature.id, state)
|
|
385
402
|
}
|
|
386
403
|
}
|
|
387
404
|
|
|
@@ -395,8 +412,8 @@ export class LayerManager
|
|
|
395
412
|
for (const mapLayer of this.#featureLayers.values()) {
|
|
396
413
|
mapLayer.setPaint(this.__layerOptions)
|
|
397
414
|
}
|
|
398
|
-
if (this.#
|
|
399
|
-
this.#
|
|
415
|
+
if (this.#flightPathLayer) {
|
|
416
|
+
this.#flightPathLayer.setPaint(options)
|
|
400
417
|
}
|
|
401
418
|
}
|
|
402
419
|
|
|
@@ -407,27 +424,31 @@ export class LayerManager
|
|
|
407
424
|
for (const mapLayer of this.#featureLayers.values()) {
|
|
408
425
|
mapLayer.setFilter(this.__layerOptions);
|
|
409
426
|
}
|
|
410
|
-
if (this.#
|
|
427
|
+
if (this.#flightPathLayer) {
|
|
428
|
+
// * @arg options.layerOptions.sckan {string} Show neuron paths known to SCKAN: values are ``valid`` (default),
|
|
429
|
+
// * ``invalid``, ``all`` or ``none``.
|
|
430
|
+
|
|
431
|
+
|
|
411
432
|
const sckanState = options.sckan || 'valid'
|
|
412
433
|
const sckanFilter = (sckanState == 'none') ? {NOT: {HAS: 'sckan'}} :
|
|
413
434
|
(sckanState == 'valid') ? {sckan: true} :
|
|
414
|
-
(sckanState == 'invalid') ? {NOT: {sckan: true}} :
|
|
435
|
+
(sckanState == 'invalid') ? {NOT: {sckan: true}} : // {sckan: false} is different...
|
|
415
436
|
true
|
|
416
437
|
const featureFilter = new PropertiesFilter(sckanFilter)
|
|
417
438
|
if ('taxons' in options) {
|
|
418
439
|
featureFilter.narrow({taxons: options.taxons})
|
|
419
440
|
}
|
|
420
|
-
this.#
|
|
441
|
+
this.#flightPathLayer.setFilter(featureFilter)
|
|
421
442
|
}
|
|
422
443
|
}
|
|
423
444
|
|
|
424
|
-
|
|
425
|
-
|
|
445
|
+
setFlightPathMode(enable=true)
|
|
446
|
+
//============================
|
|
426
447
|
{
|
|
427
|
-
if (this.#
|
|
428
|
-
this.#
|
|
448
|
+
if (this.#flightPathLayer) {
|
|
449
|
+
this.#flightPathLayer.enable(enable)
|
|
429
450
|
for (const mapLayer of this.#featureLayers.values()) {
|
|
430
|
-
mapLayer.
|
|
451
|
+
mapLayer.setFlatPathMode(!enable)
|
|
431
452
|
}
|
|
432
453
|
}
|
|
433
454
|
}
|
package/src/main.js
CHANGED
|
@@ -25,6 +25,41 @@ limitations under the License.
|
|
|
25
25
|
import { MapManager } from './flatmap-viewer';
|
|
26
26
|
export { MapManager };
|
|
27
27
|
|
|
28
|
+
const ALL_MARKERS = [
|
|
29
|
+
// Most of these are around the heart
|
|
30
|
+
'UBERON:0003382',
|
|
31
|
+
'UBERON:0002348',
|
|
32
|
+
'UBERON:0002349',
|
|
33
|
+
'UBERON:0003379',
|
|
34
|
+
'UBERON:0001986',
|
|
35
|
+
'UBERON:0001074',
|
|
36
|
+
'UBERON:0003381',
|
|
37
|
+
'UBERON:0002165',
|
|
38
|
+
'UBERON:0002080',
|
|
39
|
+
'UBERON:0000948',
|
|
40
|
+
'UBERON:0002084',
|
|
41
|
+
'UBERON:0002078',
|
|
42
|
+
'UBERON:0002079',
|
|
43
|
+
'UBERON:0002349',
|
|
44
|
+
'UBERON:0002408',
|
|
45
|
+
'UBERON:0007240',
|
|
46
|
+
'UBERON:0002359',
|
|
47
|
+
|
|
48
|
+
'UBERON:0001508',
|
|
49
|
+
'UBERON:0037094',
|
|
50
|
+
'ILX:0738305',
|
|
51
|
+
|
|
52
|
+
'UBERON:0000948', // {className: 'heart-marker'}); // Heart
|
|
53
|
+
'UBERON:0002048', // Lung
|
|
54
|
+
'UBERON:0000945', // Stomach
|
|
55
|
+
'UBERON:0001155', // Colon
|
|
56
|
+
'UBERON:0001255', // Bladder
|
|
57
|
+
'UBERON:0001759', // Vagus
|
|
58
|
+
|
|
59
|
+
'UBERON:0016508', // Pelvic ganglion
|
|
60
|
+
|
|
61
|
+
]
|
|
62
|
+
|
|
28
63
|
//==============================================================================
|
|
29
64
|
|
|
30
65
|
class DrawControl
|
|
@@ -110,6 +145,7 @@ export async function standaloneViewer(map_endpoint=null, options={})
|
|
|
110
145
|
showPosition: false,
|
|
111
146
|
standalone: true,
|
|
112
147
|
annotator: true,
|
|
148
|
+
flightPaths: true
|
|
113
149
|
}, options);
|
|
114
150
|
|
|
115
151
|
function loadMap(id, taxon, sex)
|
|
@@ -139,15 +175,20 @@ export async function standaloneViewer(map_endpoint=null, options={})
|
|
|
139
175
|
mapOptions.background = args[0].value;
|
|
140
176
|
} else if (eventType === 'annotation') {
|
|
141
177
|
drawControl.handleEvent(...args)
|
|
178
|
+
} else {
|
|
179
|
+
//console.log(eventType, ...args)
|
|
142
180
|
}
|
|
143
181
|
}, mapOptions)
|
|
144
182
|
.then(map => {
|
|
183
|
+
/*
|
|
145
184
|
map.addMarker('UBERON:0000948', {className: 'heart-marker'}); // Heart
|
|
146
185
|
map.addMarker('UBERON:0002048'); // Lung
|
|
147
186
|
map.addMarker('UBERON:0000945'); // Stomach
|
|
148
187
|
map.addMarker('UBERON:0001155'); // Colon
|
|
149
188
|
map.addMarker('UBERON:0001255'); // Bladder
|
|
150
189
|
map.addMarker('UBERON:0001759'); // Vagus
|
|
190
|
+
*/
|
|
191
|
+
map.addMarkers(ALL_MARKERS, {cluster: true})
|
|
151
192
|
currentMap = map;
|
|
152
193
|
drawControl = new DrawControl(map)
|
|
153
194
|
})
|
package/src/pathways.js
CHANGED
|
@@ -61,7 +61,7 @@ export function pathColourArray(pathType, alpha=255)
|
|
|
61
61
|
{
|
|
62
62
|
const rgb = colord(PathTypeMap.has(pathType)
|
|
63
63
|
? PathTypeMap.get(pathType).colour
|
|
64
|
-
: '
|
|
64
|
+
: PathTypeMap.get('other').colour).toRgb()
|
|
65
65
|
return [rgb.r, rgb.g, rgb.b, alpha]
|
|
66
66
|
}
|
|
67
67
|
|
|
@@ -92,9 +92,6 @@ export class PathManager
|
|
|
92
92
|
pathLines[pathId] = path.lines;
|
|
93
93
|
pathNerves[pathId] = path.nerves;
|
|
94
94
|
this.__paths[pathId] = path;
|
|
95
|
-
for (const lineId of path.lines) {
|
|
96
|
-
this.__ui.enableFeature(lineId, enabled, true);
|
|
97
|
-
}
|
|
98
95
|
this.__paths[pathId].systemCount = 0;
|
|
99
96
|
if ('models' in path) {
|
|
100
97
|
const modelId = path['models'];
|
|
@@ -316,6 +313,14 @@ export class PathManager
|
|
|
316
313
|
return featureIds;
|
|
317
314
|
}
|
|
318
315
|
|
|
316
|
+
enablePathLines(enable, force=false)
|
|
317
|
+
//==================================
|
|
318
|
+
{
|
|
319
|
+
for (const lineId of Object.keys(this.__pathsByLine)) {
|
|
320
|
+
this.__ui.enableFeature(lineId, enable, force)
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
319
324
|
enablePathsBySystem(system, enable, force=false)
|
|
320
325
|
//==============================================
|
|
321
326
|
{
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/******************************************************************************
|
|
2
|
+
|
|
3
|
+
Flatmap viewer and annotation tool
|
|
4
|
+
|
|
5
|
+
Copyright (c) 2019 - 2024 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
|
+
export type Constructor<T> = new(...args: any[]) => T
|
|
22
|
+
|
|
23
|
+
export type ObjectRecord = Record<string, any>
|
|
24
|
+
|
|
25
|
+
//==============================================================================
|
|
26
|
+
|
package/src/utils.js
CHANGED