@abi-software/flatmap-viewer 2.5.0-a.1 → 2.5.0-a.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.rst +1 -1
- package/package.json +2 -1
- package/src/controls/paths3d.js +76 -0
- package/src/flatmap-viewer.js +13 -0
- package/src/interactions.js +26 -94
- package/src/{layers.js → layers/index.js} +2 -2
- package/src/layers/paths3d.js +170 -0
- package/src/{styling.js → layers/styling.js} +2 -2
- package/src/pathways.js +37 -0
package/README.rst
CHANGED
|
@@ -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.5.0-a.
|
|
41
|
+
* ``npm install @abi-software/flatmap-viewer@2.5.0-a.2``
|
|
42
42
|
|
|
43
43
|
Documentation
|
|
44
44
|
-------------
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abi-software/flatmap-viewer",
|
|
3
|
-
"version": "2.5.0-a.
|
|
3
|
+
"version": "2.5.0-a.2",
|
|
4
4
|
"description": "Flatmap viewer using Maplibre GL",
|
|
5
5
|
"repository": "https://github.com/AnatomicMaps/flatmap-viewer.git",
|
|
6
6
|
"main": "src/main.js",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@babel/runtime": "^7.10.4",
|
|
21
21
|
"@deck.gl/core": "^8.9.33",
|
|
22
|
+
"@deck.gl/extensions": "^8.9.34",
|
|
22
23
|
"@deck.gl/layers": "^8.9.33",
|
|
23
24
|
"@deck.gl/mapbox": "^8.9.33",
|
|
24
25
|
"@fortawesome/fontawesome-free": "^6.4.0",
|
|
@@ -0,0 +1,76 @@
|
|
|
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 class Path3DControl
|
|
22
|
+
{
|
|
23
|
+
#button
|
|
24
|
+
#container
|
|
25
|
+
#map = null
|
|
26
|
+
#flatmap
|
|
27
|
+
|
|
28
|
+
constructor(flatmap)
|
|
29
|
+
{
|
|
30
|
+
this.#flatmap = flatmap
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
getDefaultPosition()
|
|
34
|
+
//==================
|
|
35
|
+
{
|
|
36
|
+
return 'top-right'
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
onAdd(map)
|
|
40
|
+
//========
|
|
41
|
+
{
|
|
42
|
+
this.#map = map
|
|
43
|
+
this.#container = document.createElement('div')
|
|
44
|
+
this.#container.className = 'maplibregl-ctrl'
|
|
45
|
+
this.#button = document.createElement('button')
|
|
46
|
+
this.#button.className = 'control-button text-button'
|
|
47
|
+
this.#button.setAttribute('type', 'button')
|
|
48
|
+
this.#button.setAttribute('aria-label', 'Show 3D paths')
|
|
49
|
+
this.#button.textContent = '3D'
|
|
50
|
+
this.#button.title = 'Show/hide 3D paths'
|
|
51
|
+
this.#container.appendChild(this.#button)
|
|
52
|
+
this.#container.addEventListener('click', this.onClick.bind(this))
|
|
53
|
+
return this.#container
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
onRemove()
|
|
57
|
+
//========
|
|
58
|
+
{
|
|
59
|
+
this.#container.parentNode.removeChild(this.#container)
|
|
60
|
+
this.#map = undefined
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
onClick(_event)
|
|
64
|
+
//=============
|
|
65
|
+
{
|
|
66
|
+
if (this.#button.classList.contains('control-active')) {
|
|
67
|
+
this.#flatmap.enable3dPaths(false)
|
|
68
|
+
this.#button.classList.remove('control-active')
|
|
69
|
+
} else {
|
|
70
|
+
this.#flatmap.enable3dPaths(true)
|
|
71
|
+
this.#button.classList.add('control-active')
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
//==============================================================================
|
package/src/flatmap-viewer.js
CHANGED
|
@@ -898,6 +898,19 @@ class FlatMap
|
|
|
898
898
|
}
|
|
899
899
|
}
|
|
900
900
|
|
|
901
|
+
/**
|
|
902
|
+
* Show/hide 3D path view.
|
|
903
|
+
*
|
|
904
|
+
* @param {boolean} [enable=true]
|
|
905
|
+
*/
|
|
906
|
+
enable3dPaths(enable=true)
|
|
907
|
+
//========================
|
|
908
|
+
{
|
|
909
|
+
if (this._userInteractions !== null) {
|
|
910
|
+
this._userInteractions.enable3dPaths(enable)
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
|
|
901
914
|
//==========================================================================
|
|
902
915
|
|
|
903
916
|
/**
|
package/src/interactions.js
CHANGED
|
@@ -22,10 +22,6 @@ limitations under the License.
|
|
|
22
22
|
|
|
23
23
|
//==============================================================================
|
|
24
24
|
|
|
25
|
-
import {colord} from "colord";
|
|
26
|
-
|
|
27
|
-
import {MapboxOverlay as DeckOverlay} from '@deck.gl/mapbox';
|
|
28
|
-
import {ArcLayer} from '@deck.gl/layers';
|
|
29
25
|
import maplibregl from 'maplibre-gl';
|
|
30
26
|
|
|
31
27
|
import {default as turfArea} from '@turf/area';
|
|
@@ -38,14 +34,16 @@ import polylabel from 'polylabel';
|
|
|
38
34
|
//==============================================================================
|
|
39
35
|
|
|
40
36
|
import {LayerManager} from './layers';
|
|
41
|
-
import {
|
|
42
|
-
import {VECTOR_TILES_SOURCE} from './styling';
|
|
37
|
+
import {PATHWAYS_LAYER, PathManager} from './pathways';
|
|
38
|
+
import {VECTOR_TILES_SOURCE} from './layers/styling';
|
|
39
|
+
import {Paths3DLayer} from './layers/paths3d'
|
|
43
40
|
import {SystemsManager} from './systems';
|
|
44
41
|
|
|
45
42
|
import {displayedProperties, InfoControl} from './controls/info';
|
|
46
43
|
import {BackgroundControl, LayerControl, NerveControl,
|
|
47
44
|
SCKANControl} from './controls/controls';
|
|
48
45
|
import {PathControl} from './controls/paths';
|
|
46
|
+
import {Path3DControl} from './controls/paths3d'
|
|
49
47
|
import {SearchControl} from './controls/search';
|
|
50
48
|
import {SystemsControl} from './controls/systems';
|
|
51
49
|
import {TaxonsControl} from './controls/taxons';
|
|
@@ -120,17 +118,12 @@ function getRenderedLabel(properties)
|
|
|
120
118
|
return properties.renderedLabel;
|
|
121
119
|
}
|
|
122
120
|
|
|
123
|
-
// Should this be in `pathways.js` ??
|
|
124
|
-
function pathColourRGB(pathType, alpha=255)
|
|
125
|
-
{
|
|
126
|
-
const rgb = colord(pathColour(pathType)).toRgb();
|
|
127
|
-
return [rgb.r, rgb.g, rgb.b, alpha];
|
|
128
|
-
}
|
|
129
|
-
|
|
130
121
|
//==============================================================================
|
|
131
122
|
|
|
132
123
|
export class UserInteractions
|
|
133
124
|
{
|
|
125
|
+
#paths3dLayer
|
|
126
|
+
|
|
134
127
|
constructor(flatmap)
|
|
135
128
|
{
|
|
136
129
|
this._flatmap = flatmap;
|
|
@@ -194,6 +187,9 @@ export class UserInteractions
|
|
|
194
187
|
// All taxons of connectivity paths are enabled by default
|
|
195
188
|
this.__enabledConnectivityTaxons = new Set(this._flatmap.taxonIdentifiers);
|
|
196
189
|
|
|
190
|
+
// Support 3D path view
|
|
191
|
+
this.#paths3dLayer = new Paths3DLayer(flatmap, this)
|
|
192
|
+
|
|
197
193
|
// Add various controls when running standalone
|
|
198
194
|
if (flatmap.options.standalone) {
|
|
199
195
|
// Add a control to search annotations if option set
|
|
@@ -225,73 +221,9 @@ export class UserInteractions
|
|
|
225
221
|
// Connectivity taxon control for AC maps
|
|
226
222
|
this._map.addControl(new TaxonsControl(flatmap));
|
|
227
223
|
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
const pathData = [...flatmap.annotations.values()]
|
|
231
|
-
.filter(ann => ann['tile-layer'] === 'pathways'
|
|
232
|
-
&& ann['geometry'] === 'LineString'
|
|
233
|
-
&& 'type' in ann && ann['type'].startsWith('line')
|
|
234
|
-
&& 'kind' in ann && !ann['kind'].includes('arterial') && !ann['kind'].includes('venous')
|
|
235
|
-
&& 'pathStartPosition' in ann
|
|
236
|
-
&& 'pathEndPosition' in ann)
|
|
237
224
|
|
|
238
|
-
|
|
239
|
-
{
|
|
240
|
-
"featureId": 37,
|
|
241
|
-
"id": "ilxtr_neuron-type-keast-10",
|
|
242
|
-
"kind": "sensory",
|
|
243
|
-
"label": "L6-S1 sensory neuron innervating bladder",
|
|
244
|
-
"models": "ilxtr:neuron-type-keast-10",
|
|
245
|
-
"sckan": true,
|
|
246
|
-
"source": "https://apinatomy.org/uris/models/keast-bladder",
|
|
247
|
-
"taxons": ["NCBITaxon:10116"],
|
|
248
|
-
"tile-layer": "pathways",
|
|
249
|
-
"type": "line",
|
|
250
|
-
"bounds": [1.5454426659162825, -1.6254174813389017, 1.7478459571498208, -1.3632864333949712],
|
|
251
|
-
"markerPosition": [1.6466443115330516, -1.4943519573669364],
|
|
252
|
-
"geometry": "LineString",
|
|
253
|
-
"layer": "neural-routes",
|
|
254
|
-
"pathStartPosition": [1.7478459571498208, -1.3632864333949712],
|
|
255
|
-
"pathEndPosition": [1.5454426659162825, -1.6254174813389017]
|
|
256
|
-
}
|
|
257
|
-
*/
|
|
258
|
-
|
|
259
|
-
const deckOverlay = new DeckOverlay({
|
|
260
|
-
layers: [
|
|
261
|
-
new ArcLayer({
|
|
262
|
-
id: 'arcs',
|
|
263
|
-
data: pathData,
|
|
264
|
-
pickable: true,
|
|
265
|
-
autoHighlight: true,
|
|
266
|
-
numSegments: 100,
|
|
267
|
-
onHover: (i, e) => {
|
|
268
|
-
//console.log('hover', i, e)
|
|
269
|
-
if (i.object) {
|
|
270
|
-
const lineFeatureId = +i.object.featureId;
|
|
271
|
-
this.__activateFeature(this.mapFeature(lineFeatureId));
|
|
272
|
-
for (const featureId of this.__pathManager.lineFeatureIds([lineFeatureId])) {
|
|
273
|
-
if (+featureId !== lineFeatureId) {
|
|
274
|
-
this.__activateFeature(this.mapFeature(featureId));
|
|
275
|
-
}
|
|
276
|
-
}
|
|
225
|
+
this._map.addControl(new Path3DControl(this));
|
|
277
226
|
}
|
|
278
|
-
},
|
|
279
|
-
onClick: (i, e) => {
|
|
280
|
-
console.log('click', i, e)
|
|
281
|
-
},
|
|
282
|
-
// Styles
|
|
283
|
-
getSourcePosition: f => f.pathStartPosition,
|
|
284
|
-
getTargetPosition: f => f.pathEndPosition,
|
|
285
|
-
getSourceColor: f => pathColourRGB(f.kind, 160),
|
|
286
|
-
getTargetColor: f => pathColourRGB(f.kind, 160),
|
|
287
|
-
highlightColor: o => pathColourRGB(o.object.kind),
|
|
288
|
-
getWidth: 3
|
|
289
|
-
})
|
|
290
|
-
],
|
|
291
|
-
getTooltip: ({object}) => object && object.label
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
this._map.addControl(deckOverlay);
|
|
295
227
|
|
|
296
228
|
// Handle mouse events
|
|
297
229
|
|
|
@@ -365,6 +297,12 @@ this._map.addControl(deckOverlay);
|
|
|
365
297
|
this._layerManager.activate(layerId, enable);
|
|
366
298
|
}
|
|
367
299
|
|
|
300
|
+
enable3dPaths(enable=true)
|
|
301
|
+
//========================
|
|
302
|
+
{
|
|
303
|
+
this.#paths3dLayer.enable(enable)
|
|
304
|
+
}
|
|
305
|
+
|
|
368
306
|
getSystems()
|
|
369
307
|
//==========
|
|
370
308
|
{
|
|
@@ -536,8 +474,8 @@ this._map.addControl(deckOverlay);
|
|
|
536
474
|
this._layerManager.setPaint({...this.__colourOptions, dimmed: false});
|
|
537
475
|
}
|
|
538
476
|
|
|
539
|
-
|
|
540
|
-
|
|
477
|
+
activateFeature(feature)
|
|
478
|
+
//======================
|
|
541
479
|
{
|
|
542
480
|
if (feature !== undefined) {
|
|
543
481
|
this._map.setFeatureState(feature, { active: true });
|
|
@@ -885,7 +823,7 @@ this._map.addControl(deckOverlay);
|
|
|
885
823
|
let tooltip = '';
|
|
886
824
|
if (displayInfo) {
|
|
887
825
|
if (!('tooltip' in features[0].properties)) {
|
|
888
|
-
this.
|
|
826
|
+
this.activateFeature(features[0]);
|
|
889
827
|
}
|
|
890
828
|
info = this._infoControl.featureInformation(features, event.lngLat);
|
|
891
829
|
}
|
|
@@ -898,11 +836,11 @@ this._map.addControl(deckOverlay);
|
|
|
898
836
|
tooltipFeature = lineFeatures[0];
|
|
899
837
|
for (const lineFeature of lineFeatures) {
|
|
900
838
|
const lineFeatureId = +lineFeature.properties.featureId; // Ensure numeric
|
|
901
|
-
this.
|
|
839
|
+
this.activateFeature(lineFeature);
|
|
902
840
|
const lineIds = new Set(lineFeatures.map(f => f.properties.featureId));
|
|
903
841
|
for (const featureId of this.__pathManager.lineFeatureIds(lineIds)) {
|
|
904
842
|
if (+featureId !== lineFeatureId) {
|
|
905
|
-
this.
|
|
843
|
+
this.activateFeature(this.mapFeature(featureId));
|
|
906
844
|
}
|
|
907
845
|
}
|
|
908
846
|
}
|
|
@@ -952,7 +890,7 @@ this._map.addControl(deckOverlay);
|
|
|
952
890
|
info = `<div id="info-control-info">${htmlList.join('\n')}</div>`;
|
|
953
891
|
}
|
|
954
892
|
}
|
|
955
|
-
this.
|
|
893
|
+
this.activateFeature(feature);
|
|
956
894
|
this.__activateRelatedFeatures(feature);
|
|
957
895
|
if ('hyperlink' in feature.properties) {
|
|
958
896
|
this._map.getCanvas().style.cursor = 'pointer';
|
|
@@ -1072,25 +1010,19 @@ this._map.addControl(deckOverlay);
|
|
|
1072
1010
|
if ('nerveId' in feature.properties) {
|
|
1073
1011
|
const nerveId = feature.properties.nerveId;
|
|
1074
1012
|
if (nerveId !== feature.id) {
|
|
1075
|
-
this.
|
|
1013
|
+
this.activateFeature(this.mapFeature(nerveId));
|
|
1076
1014
|
}
|
|
1077
1015
|
for (const featureId of this.__pathManager.nerveFeatureIds(nerveId)) {
|
|
1078
|
-
this.
|
|
1016
|
+
this.activateFeature(this.mapFeature(featureId));
|
|
1079
1017
|
}
|
|
1080
1018
|
}
|
|
1081
1019
|
if ('nodeId' in feature.properties) {
|
|
1082
1020
|
for (const featureId of this.__pathManager.pathFeatureIds(feature.properties.nodeId)) {
|
|
1083
|
-
this.
|
|
1021
|
+
this.activateFeature(this.mapFeature(featureId));
|
|
1084
1022
|
}
|
|
1085
1023
|
}
|
|
1086
1024
|
}
|
|
1087
1025
|
|
|
1088
|
-
enablePath(pathId, enable=true)
|
|
1089
|
-
//=============================
|
|
1090
|
-
{
|
|
1091
|
-
this.__pathManager.enablePath(pathId, enable);
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
1026
|
enablePathsBySystem(system, enable=true, force=false)
|
|
1095
1027
|
//===================================================
|
|
1096
1028
|
{
|
|
@@ -1315,7 +1247,7 @@ this._map.addControl(deckOverlay);
|
|
|
1315
1247
|
if (event.type === 'mouseenter') {
|
|
1316
1248
|
// Highlight on mouse enter
|
|
1317
1249
|
this.resetActiveFeatures_();
|
|
1318
|
-
this.
|
|
1250
|
+
this.activateFeature(feature);
|
|
1319
1251
|
} else {
|
|
1320
1252
|
this.selectionEvent_(event, feature)
|
|
1321
1253
|
}
|
|
@@ -22,10 +22,10 @@ limitations under the License.
|
|
|
22
22
|
|
|
23
23
|
//==============================================================================
|
|
24
24
|
|
|
25
|
-
import {PATHWAYS_LAYER} from '
|
|
25
|
+
import {PATHWAYS_LAYER} from '../pathways.js';
|
|
26
|
+
import * as utils from '../utils.js';
|
|
26
27
|
|
|
27
28
|
import * as style from './styling.js';
|
|
28
|
-
import * as utils from './utils.js';
|
|
29
29
|
|
|
30
30
|
const FEATURES_LAYER = 'features';
|
|
31
31
|
const RASTER_LAYERS_NAME = 'Background image layer';
|
|
@@ -0,0 +1,170 @@
|
|
|
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
|
+
import {colord} from 'colord'
|
|
22
|
+
import {ArcLayer} from '@deck.gl/layers'
|
|
23
|
+
import {MapboxOverlay as DeckOverlay} from '@deck.gl/mapbox'
|
|
24
|
+
import {PathStyleExtension} from '@deck.gl/extensions'
|
|
25
|
+
|
|
26
|
+
//==============================================================================
|
|
27
|
+
|
|
28
|
+
import {pathColour} from '../pathways'
|
|
29
|
+
|
|
30
|
+
// Should this be in `pathways.js` ??
|
|
31
|
+
function pathColourRGB(pathType, alpha=255)
|
|
32
|
+
//=========================================
|
|
33
|
+
{
|
|
34
|
+
const rgb = colord(pathColour(pathType)).toRgb()
|
|
35
|
+
return [rgb.r, rgb.g, rgb.b, alpha]
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
//==============================================================================
|
|
39
|
+
|
|
40
|
+
/*
|
|
41
|
+
TODO:
|
|
42
|
+
|
|
43
|
+
* Dashed paths
|
|
44
|
+
* Control by SCKAN status
|
|
45
|
+
* Control by taxon visibility
|
|
46
|
+
* Control by visible layer
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
Also see https://alasarr.github.io/deck.gl/docs/api-reference/core/layer#updatetriggers
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
{
|
|
53
|
+
"featureId": 37,
|
|
54
|
+
"id": "ilxtr_neuron-type-keast-10",
|
|
55
|
+
"kind": "sensory",
|
|
56
|
+
"label": "L6-S1 sensory neuron innervating bladder",
|
|
57
|
+
"models": "ilxtr:neuron-type-keast-10",
|
|
58
|
+
"sckan": true,
|
|
59
|
+
"source": "https://apinatomy.org/uris/models/keast-bladder",
|
|
60
|
+
"taxons": ["NCBITaxon:10116"],
|
|
61
|
+
"tile-layer": "pathways",
|
|
62
|
+
"type": "line",
|
|
63
|
+
"bounds": [1.5454426659162825, -1.6254174813389017, 1.7478459571498208, -1.3632864333949712],
|
|
64
|
+
"markerPosition": [1.6466443115330516, -1.4943519573669364],
|
|
65
|
+
"geometry": "LineString",
|
|
66
|
+
"layer": "neural-routes",
|
|
67
|
+
"pathStartPosition": [1.7478459571498208, -1.3632864333949712],
|
|
68
|
+
"pathEndPosition": [1.5454426659162825, -1.6254174813389017]
|
|
69
|
+
}
|
|
70
|
+
*/
|
|
71
|
+
|
|
72
|
+
//==============================================================================
|
|
73
|
+
|
|
74
|
+
export class Paths3DLayer
|
|
75
|
+
{
|
|
76
|
+
#deckOverlay = null
|
|
77
|
+
#enabled = false
|
|
78
|
+
#map
|
|
79
|
+
#pathData
|
|
80
|
+
#pathManager
|
|
81
|
+
#ui
|
|
82
|
+
|
|
83
|
+
constructor(flatmap, ui)
|
|
84
|
+
{
|
|
85
|
+
this.#ui = ui
|
|
86
|
+
this.#map = flatmap.map
|
|
87
|
+
this.#pathManager = ui.pathManager
|
|
88
|
+
this.#pathManager.addWatcher(this.#pathStateChanged.bind(this))
|
|
89
|
+
this.#pathData = [...flatmap.annotations.values()]
|
|
90
|
+
.filter(ann => ann['tile-layer'] === 'pathways'
|
|
91
|
+
&& ann['geometry'] === 'LineString'
|
|
92
|
+
&& 'type' in ann && ann['type'].startsWith('line')
|
|
93
|
+
&& 'kind' in ann // && !ann['kind'].includes('arterial') && !ann['kind'].includes('venous')
|
|
94
|
+
&& 'pathStartPosition' in ann
|
|
95
|
+
&& 'pathEndPosition' in ann)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
enable(enable=true)
|
|
99
|
+
//=================
|
|
100
|
+
{
|
|
101
|
+
if (enable && !this.#enabled) {
|
|
102
|
+
this.#setDeckOverlay()
|
|
103
|
+
this.#map.addControl(this.#deckOverlay)
|
|
104
|
+
} else if (!enable && this.#enabled) {
|
|
105
|
+
if (this.#deckOverlay) {
|
|
106
|
+
this.#map.removeControl(this.#deckOverlay)
|
|
107
|
+
this.#deckOverlay = null
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
this.#enabled = enable
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
#pathStateChanged()
|
|
114
|
+
//=================
|
|
115
|
+
{
|
|
116
|
+
if (this.#deckOverlay) {
|
|
117
|
+
this.#map.removeControl(this.#deckOverlay)
|
|
118
|
+
this.#setDeckOverlay()
|
|
119
|
+
this.#map.addControl(this.#deckOverlay)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
#setDeckOverlay()
|
|
124
|
+
//===============
|
|
125
|
+
{
|
|
126
|
+
this.#deckOverlay = new DeckOverlay({
|
|
127
|
+
layers: [
|
|
128
|
+
// Need to have two layers, one with dashed lines, one without
|
|
129
|
+
//
|
|
130
|
+
// Better, one layer per pathType and set/clear layer.visible...
|
|
131
|
+
//
|
|
132
|
+
new ArcLayer({
|
|
133
|
+
id: 'arcs',
|
|
134
|
+
data: this.#pathData
|
|
135
|
+
.filter(f => this.#pathManager.pathTypeEnabled(f.kind)),
|
|
136
|
+
pickable: true,
|
|
137
|
+
autoHighlight: true,
|
|
138
|
+
numSegments: 100,
|
|
139
|
+
onHover: (i, e) => {
|
|
140
|
+
//console.log('hover', i, e)
|
|
141
|
+
if (i.object) {
|
|
142
|
+
const lineFeatureId = +i.object.featureId
|
|
143
|
+
this.#ui.activateFeature(this.#ui.mapFeature(lineFeatureId))
|
|
144
|
+
for (const featureId of this.#pathManager.lineFeatureIds([lineFeatureId])) {
|
|
145
|
+
if (+featureId !== lineFeatureId) {
|
|
146
|
+
this.#ui.activateFeature(this.#ui.mapFeature(featureId))
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
onClick: (i, e) => {
|
|
152
|
+
console.log('click', i, e)
|
|
153
|
+
},
|
|
154
|
+
// Styles
|
|
155
|
+
getSourcePosition: f => f.pathStartPosition,
|
|
156
|
+
getTargetPosition: f => f.pathEndPosition,
|
|
157
|
+
getSourceColor: f => pathColourRGB(f.kind, 160),
|
|
158
|
+
getTargetColor: f => pathColourRGB(f.kind, 160),
|
|
159
|
+
highlightColor: o => pathColourRGB(o.object.kind),
|
|
160
|
+
getWidth: 3,
|
|
161
|
+
extensions: [new PathStyleExtension({dash: true})],
|
|
162
|
+
getDashArray: [3, 2],
|
|
163
|
+
dashJustified: true,
|
|
164
|
+
dashGapPickable: true,
|
|
165
|
+
})
|
|
166
|
+
],
|
|
167
|
+
getTooltip: ({object}) => object && object.label
|
|
168
|
+
})
|
|
169
|
+
}
|
|
170
|
+
}
|
|
@@ -26,8 +26,8 @@ export const VECTOR_TILES_SOURCE = 'vector-tiles';
|
|
|
26
26
|
|
|
27
27
|
//==============================================================================
|
|
28
28
|
|
|
29
|
-
import {UNCLASSIFIED_TAXON_ID} from '
|
|
30
|
-
import {PATH_STYLE_RULES} from '
|
|
29
|
+
import {UNCLASSIFIED_TAXON_ID} from '../flatmap-viewer';
|
|
30
|
+
import {PATH_STYLE_RULES} from '../pathways';
|
|
31
31
|
|
|
32
32
|
//==============================================================================
|
|
33
33
|
|
package/src/pathways.js
CHANGED
|
@@ -299,6 +299,7 @@ export class PathManager
|
|
|
299
299
|
enablePathsBySystem(system, enable, force=false)
|
|
300
300
|
//==============================================
|
|
301
301
|
{
|
|
302
|
+
let changed = false;
|
|
302
303
|
for (const pathId of system.pathIds) {
|
|
303
304
|
const path = this.__paths[pathId];
|
|
304
305
|
if (this.__pathtypeEnabled[path.pathType]
|
|
@@ -311,6 +312,7 @@ export class PathManager
|
|
|
311
312
|
for (const featureId of featureIds) {
|
|
312
313
|
this.__ui.enableFeature(featureId, enable, force);
|
|
313
314
|
}
|
|
315
|
+
changed = true
|
|
314
316
|
}
|
|
315
317
|
path.systemCount += (enable ? 1 : -1);
|
|
316
318
|
if (path.systemCount < 0) {
|
|
@@ -318,6 +320,9 @@ export class PathManager
|
|
|
318
320
|
}
|
|
319
321
|
// TODO? Show connectors and parent components of these paths??
|
|
320
322
|
}
|
|
323
|
+
if (changed) {
|
|
324
|
+
this.#notifyWatchers()
|
|
325
|
+
}
|
|
321
326
|
}
|
|
322
327
|
|
|
323
328
|
enablePathsByType(pathType, enable, force=false)
|
|
@@ -330,9 +335,16 @@ export class PathManager
|
|
|
330
335
|
this.__ui.enableFeature(featureId, enable, force);
|
|
331
336
|
}
|
|
332
337
|
this.__pathtypeEnabled[pathType] = enable;
|
|
338
|
+
this.#notifyWatchers()
|
|
333
339
|
}
|
|
334
340
|
}
|
|
335
341
|
|
|
342
|
+
pathTypeEnabled(pathType)
|
|
343
|
+
//=======================
|
|
344
|
+
{
|
|
345
|
+
return this.__pathtypeEnabled[pathType] || false
|
|
346
|
+
}
|
|
347
|
+
|
|
336
348
|
nodePathModels(nodeId)
|
|
337
349
|
//====================
|
|
338
350
|
{
|
|
@@ -360,6 +372,31 @@ export class PathManager
|
|
|
360
372
|
}
|
|
361
373
|
return nodeIds;
|
|
362
374
|
}
|
|
375
|
+
|
|
376
|
+
#lastWatcherId = 0
|
|
377
|
+
#watcherCallbacks = new Map()
|
|
378
|
+
|
|
379
|
+
addWatcher(callback)
|
|
380
|
+
//==================
|
|
381
|
+
{
|
|
382
|
+
this.#lastWatcherId += 1
|
|
383
|
+
this.#watcherCallbacks.set(this.#lastWatcherId, callback)
|
|
384
|
+
return this.#lastWatcherId
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
removeWatcher(watcherId)
|
|
388
|
+
//======================
|
|
389
|
+
{
|
|
390
|
+
this.#watcherCallbacks.delete(watcherId)
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
#notifyWatchers()
|
|
394
|
+
//===============
|
|
395
|
+
{
|
|
396
|
+
for (const callback of this.#watcherCallbacks.values()) {
|
|
397
|
+
callback()
|
|
398
|
+
}
|
|
399
|
+
}
|
|
363
400
|
}
|
|
364
401
|
|
|
365
402
|
//==============================================================================
|