@abi-software/flatmap-viewer 2.2.10 → 2.2.11-b.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/package.json +2 -2
- package/src/controls.js +168 -16
- package/src/flatmap-viewer.js +65 -18
- package/src/interactions.js +45 -33
- package/src/layers.js +67 -37
- package/src/main.js +1 -5
- package/src/pathways.js +5 -2
- package/src/styling.js +175 -49
- package/static/flatmap-viewer.css +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abi-software/flatmap-viewer",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.11-b.1",
|
|
4
4
|
"description": "Flatmap viewer using Maplibre GL",
|
|
5
5
|
"repository": "https://github.com/AnatomicMaps/flatmap-viewer.git",
|
|
6
6
|
"main": "src/main.js",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"@turf/helpers": "^6.1.4",
|
|
24
24
|
"@turf/projection": "^6.5.0",
|
|
25
25
|
"bezier-js": "^6.1.0",
|
|
26
|
-
"maplibre-gl": ">=
|
|
26
|
+
"maplibre-gl": ">=2.4.0",
|
|
27
27
|
"minisearch": "^2.2.1",
|
|
28
28
|
"polylabel": "^1.1.0"
|
|
29
29
|
},
|
package/src/controls.js
CHANGED
|
@@ -117,12 +117,16 @@ export class PathControl
|
|
|
117
117
|
|
|
118
118
|
const innerHTML = [];
|
|
119
119
|
innerHTML.push(`<label for="path-all-paths">ALL PATHS:</label><div class="nerve-line"></div><input id="path-all-paths" type="checkbox" checked/>`);
|
|
120
|
+
this.__checkedCount = 0;
|
|
120
121
|
for (const path of this.__pathTypes) {
|
|
121
|
-
|
|
122
|
+
const checked = !('enabled' in path) || path.enabled ? 'checked' : '';
|
|
123
|
+
if (checked != '') {
|
|
124
|
+
this.__checkedCount += 1;
|
|
125
|
+
}
|
|
126
|
+
innerHTML.push(`<label for="path-${path.type}">${path.label}</label><div class="nerve-line nerve-${path.type}"></div><input id="path-${path.type}" type="checkbox" ${checked}/>`);
|
|
122
127
|
}
|
|
123
128
|
this._legend.innerHTML = innerHTML.join('\n');
|
|
124
|
-
this.
|
|
125
|
-
this.__halfCount = Math.trunc(this.__checkedCount/2);
|
|
129
|
+
this.__halfCount = Math.trunc(this.__pathTypes.length/2);
|
|
126
130
|
|
|
127
131
|
this._button = document.createElement('button');
|
|
128
132
|
this._button.id = 'nerve-key-button';
|
|
@@ -152,6 +156,9 @@ export class PathControl
|
|
|
152
156
|
if (this._button.getAttribute('control-visible') === 'false') {
|
|
153
157
|
this._container.appendChild(this._legend);
|
|
154
158
|
this._button.setAttribute('control-visible', 'true');
|
|
159
|
+
const allPathsCheckbox = document.getElementById('path-all-paths');
|
|
160
|
+
allPathsCheckbox.indeterminate = this.__checkedCount < this.__pathTypes.length
|
|
161
|
+
&& this.__checkedCount > 0;
|
|
155
162
|
this._legend.focus();
|
|
156
163
|
} else {
|
|
157
164
|
this._legend = this._container.removeChild(this._legend);
|
|
@@ -206,7 +213,7 @@ export class LayerControl
|
|
|
206
213
|
constructor(flatmap, layerManager)
|
|
207
214
|
{
|
|
208
215
|
this.__flatmap = flatmap;
|
|
209
|
-
this.
|
|
216
|
+
this.__layers = layerManager.layers;
|
|
210
217
|
this.__map = undefined;
|
|
211
218
|
}
|
|
212
219
|
|
|
@@ -224,18 +231,18 @@ export class LayerControl
|
|
|
224
231
|
this.__container.className = 'maplibregl-ctrl';
|
|
225
232
|
this.__container.id = 'flatmap-layer-control';
|
|
226
233
|
|
|
227
|
-
this.
|
|
228
|
-
this.
|
|
229
|
-
this.
|
|
234
|
+
this.__layersControl = document.createElement('div');
|
|
235
|
+
this.__layersControl.id = 'layer-control-text';
|
|
236
|
+
this.__layersControl.className = 'flatmap-layer-grid';
|
|
230
237
|
|
|
231
238
|
const innerHTML = [];
|
|
232
239
|
innerHTML.push(`<label for="layer-all-layers">ALL LAYERS:</label><input id="layer-all-layers" type="checkbox" checked/>`);
|
|
233
|
-
for (const layer of this.
|
|
240
|
+
for (const layer of this.__layers) {
|
|
234
241
|
innerHTML.push(`<label for="layer-${layer.id}">${layer.description}</label><input id="layer-${layer.id}" type="checkbox" checked/>`);
|
|
235
242
|
}
|
|
236
|
-
this.
|
|
243
|
+
this.__layersControl.innerHTML = innerHTML.join('\n');
|
|
237
244
|
|
|
238
|
-
this.__layersCount = this.
|
|
245
|
+
this.__layersCount = this.__layers;
|
|
239
246
|
this.__checkedCount = this.__layersCount;
|
|
240
247
|
this.__halfCount = Math.trunc(this.__checkedCount/2);
|
|
241
248
|
|
|
@@ -265,11 +272,11 @@ export class LayerControl
|
|
|
265
272
|
{
|
|
266
273
|
if (event.target.id === 'map-layers-button') {
|
|
267
274
|
if (this.__button.getAttribute('control-visible') === 'false') {
|
|
268
|
-
this.__container.appendChild(this.
|
|
275
|
+
this.__container.appendChild(this.__layersControl);
|
|
269
276
|
this.__button.setAttribute('control-visible', 'true');
|
|
270
|
-
this.
|
|
277
|
+
this.__layersControl.focus();
|
|
271
278
|
} else {
|
|
272
|
-
this.
|
|
279
|
+
this.__layersControl = this.__container.removeChild(this.__layersControl);
|
|
273
280
|
this.__button.setAttribute('control-visible', 'false');
|
|
274
281
|
}
|
|
275
282
|
} else if (event.target.tagName === 'INPUT') {
|
|
@@ -283,16 +290,16 @@ export class LayerControl
|
|
|
283
290
|
} else {
|
|
284
291
|
this.__checkedCount = 0;
|
|
285
292
|
}
|
|
286
|
-
for (const layer of this.
|
|
293
|
+
for (const layer of this.__layers) {
|
|
287
294
|
const layerCheckbox = document.getElementById(`layer-${layer.id}`);
|
|
288
295
|
if (layerCheckbox) {
|
|
289
296
|
layerCheckbox.checked = event.target.checked;
|
|
290
|
-
this.
|
|
297
|
+
this.__flatmap.enableLayer(layer.id, event.target.checked);
|
|
291
298
|
}
|
|
292
299
|
}
|
|
293
300
|
} else if (event.target.id.startsWith('layer-')) {
|
|
294
301
|
const layerId = event.target.id.substring(6);
|
|
295
|
-
this.
|
|
302
|
+
this.__flatmap.enableLayer(layerId, event.target.checked);
|
|
296
303
|
if (event.target.checked) {
|
|
297
304
|
this.__checkedCount += 1;
|
|
298
305
|
} else {
|
|
@@ -316,6 +323,151 @@ export class LayerControl
|
|
|
316
323
|
|
|
317
324
|
//==============================================================================
|
|
318
325
|
|
|
326
|
+
|
|
327
|
+
const SCKAN_STATES = [
|
|
328
|
+
{
|
|
329
|
+
'id': 'VALID',
|
|
330
|
+
'description': 'Path consistent with SCKAN'
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
'id': 'INVALID',
|
|
334
|
+
'description': 'Path inconsistent with SCKAN'
|
|
335
|
+
}
|
|
336
|
+
];
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
export class SCKANControl
|
|
340
|
+
{
|
|
341
|
+
constructor(flatmap)
|
|
342
|
+
{
|
|
343
|
+
this.__flatmap = flatmap;
|
|
344
|
+
this.__map = undefined;
|
|
345
|
+
this.__state = 'valid';
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
getDefaultPosition()
|
|
349
|
+
//==================
|
|
350
|
+
{
|
|
351
|
+
return 'top-right';
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
onAdd(map)
|
|
355
|
+
//========
|
|
356
|
+
{
|
|
357
|
+
this.__map = map;
|
|
358
|
+
this.__container = document.createElement('div');
|
|
359
|
+
this.__container.className = 'maplibregl-ctrl';
|
|
360
|
+
this.__container.id = 'flatmap-layer-control';
|
|
361
|
+
|
|
362
|
+
this.__sckan = document.createElement('div');
|
|
363
|
+
this.__sckan.id = 'sckan-control-text';
|
|
364
|
+
this.__sckan.className = 'flatmap-layer-grid';
|
|
365
|
+
|
|
366
|
+
const innerHTML = [];
|
|
367
|
+
let checked = (this.__state === 'all') ? 'checked' : '';
|
|
368
|
+
innerHTML.push(`<label for="sckan-all-paths">ALL PATHS:</label><input id="sckan-all-paths" type="checkbox" ${checked}/>`);
|
|
369
|
+
for (const state of SCKAN_STATES) {
|
|
370
|
+
checked = (this.__state.toUpperCase() === state.id) ? 'checked' : '';
|
|
371
|
+
innerHTML.push(`<label for="sckan-${state.id}">${state.description}</label><input id="sckan-${state.id}" type="checkbox" ${checked}/>`);
|
|
372
|
+
}
|
|
373
|
+
this.__sckan.innerHTML = innerHTML.join('\n');
|
|
374
|
+
|
|
375
|
+
this.__sckanCount = SCKAN_STATES.length;
|
|
376
|
+
this.__checkedCount = (this.__state === 'all') ? this.__sckanCount
|
|
377
|
+
: (this.__state === 'none') ? 0
|
|
378
|
+
: 1;
|
|
379
|
+
this.__halfCount = Math.trunc(this.__sckanCount/2);
|
|
380
|
+
|
|
381
|
+
this.__button = document.createElement('button');
|
|
382
|
+
this.__button.id = 'map-sckan-button';
|
|
383
|
+
this.__button.className = 'control-button text-button';
|
|
384
|
+
this.__button.setAttribute('type', 'button');
|
|
385
|
+
this.__button.setAttribute('aria-label', 'Show/hide valid SCKAN paths');
|
|
386
|
+
this.__button.setAttribute('control-visible', 'false');
|
|
387
|
+
this.__button.textContent = 'SCKAN';
|
|
388
|
+
this.__button.title = 'Show/hide valid SCKAN paths';
|
|
389
|
+
this.__container.appendChild(this.__button);
|
|
390
|
+
|
|
391
|
+
this.__container.addEventListener('click', this.onClick_.bind(this));
|
|
392
|
+
return this.__container;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
onRemove()
|
|
396
|
+
//========
|
|
397
|
+
{
|
|
398
|
+
this.__container.parentNode.removeChild(this.__container);
|
|
399
|
+
this.__map = undefined;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
onClick_(event)
|
|
403
|
+
//=============
|
|
404
|
+
{
|
|
405
|
+
if (event.target.id === 'map-sckan-button') {
|
|
406
|
+
if (this.__button.getAttribute('control-visible') === 'false') {
|
|
407
|
+
this.__container.appendChild(this.__sckan);
|
|
408
|
+
this.__button.setAttribute('control-visible', 'true');
|
|
409
|
+
const allLayersCheckbox = document.getElementById('sckan-all-paths');
|
|
410
|
+
allLayersCheckbox.indeterminate = this.__state.toLowerCase().includes('valid');
|
|
411
|
+
this.__sckan.focus();
|
|
412
|
+
} else {
|
|
413
|
+
this.__sckan = this.__container.removeChild(this.__sckan);
|
|
414
|
+
this.__button.setAttribute('control-visible', 'false');
|
|
415
|
+
}
|
|
416
|
+
} else if (event.target.tagName === 'INPUT') {
|
|
417
|
+
if (event.target.id === 'sckan-all-paths') {
|
|
418
|
+
if (event.target.indeterminate) {
|
|
419
|
+
event.target.checked = (this.__checkedCount >= this.__halfCount);
|
|
420
|
+
event.target.indeterminate = false;
|
|
421
|
+
}
|
|
422
|
+
if (event.target.checked) {
|
|
423
|
+
this.__state = 'all';
|
|
424
|
+
this.__checkedCount = this.__sckanCount;
|
|
425
|
+
} else {
|
|
426
|
+
this.__state = 'none';
|
|
427
|
+
this.__checkedCount = 0;
|
|
428
|
+
}
|
|
429
|
+
for (const state of SCKAN_STATES) {
|
|
430
|
+
const sckanCheckbox = document.getElementById(`sckan-${state.id}`);
|
|
431
|
+
if (sckanCheckbox) {
|
|
432
|
+
sckanCheckbox.checked = event.target.checked;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
this.__flatmap.showSckanPaths(this.__state);
|
|
436
|
+
} else if (event.target.id.startsWith('sckan-')) {
|
|
437
|
+
const sckanId = event.target.id.substring(6);
|
|
438
|
+
if (event.target.checked) {
|
|
439
|
+
this.__checkedCount += 1;
|
|
440
|
+
} else {
|
|
441
|
+
this.__checkedCount -= 1;
|
|
442
|
+
}
|
|
443
|
+
const allLayersCheckbox = document.getElementById('sckan-all-paths');
|
|
444
|
+
if (this.__checkedCount === 0) {
|
|
445
|
+
this.__state = 'none';
|
|
446
|
+
allLayersCheckbox.checked = false;
|
|
447
|
+
allLayersCheckbox.indeterminate = false;
|
|
448
|
+
} else if (this.__checkedCount === this.__sckanCount) {
|
|
449
|
+
this.__state = 'all';
|
|
450
|
+
allLayersCheckbox.checked = true;
|
|
451
|
+
allLayersCheckbox.indeterminate = false;
|
|
452
|
+
} else {
|
|
453
|
+
if (event.target.checked) {
|
|
454
|
+
this.__state = sckanId;
|
|
455
|
+
} else if (sckanId === 'VALID') {
|
|
456
|
+
this.__state = 'invalid';
|
|
457
|
+
} else {
|
|
458
|
+
this.__state = 'valid';
|
|
459
|
+
}
|
|
460
|
+
allLayersCheckbox.indeterminate = true;
|
|
461
|
+
}
|
|
462
|
+
this.__flatmap.showSckanPaths(this.__state);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
event.stopPropagation();
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
//==============================================================================
|
|
470
|
+
|
|
319
471
|
export class BackgroundControl
|
|
320
472
|
{
|
|
321
473
|
constructor(flatmap)
|
package/src/flatmap-viewer.js
CHANGED
|
@@ -314,6 +314,21 @@ class FlatMap
|
|
|
314
314
|
}
|
|
315
315
|
}
|
|
316
316
|
|
|
317
|
+
/**
|
|
318
|
+
* Hide or show all paths valid in SCKAN.
|
|
319
|
+
*
|
|
320
|
+
* @param {string} validity Either ``VALID`` or ``INVALID``
|
|
321
|
+
* @param {boolean} [enable=true] If ``true`` then only show the paths
|
|
322
|
+
* of the type(s) otherwise only hide the paths
|
|
323
|
+
*/
|
|
324
|
+
showSckanPaths(state='valid')
|
|
325
|
+
//===========================
|
|
326
|
+
{
|
|
327
|
+
if (this._userInteractions !== null) {
|
|
328
|
+
this._userInteractions.showSckanPaths(state);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
317
332
|
/**
|
|
318
333
|
* Load images and patterns/textures referenced in style rules.
|
|
319
334
|
*
|
|
@@ -428,12 +443,6 @@ class FlatMap
|
|
|
428
443
|
return `${this.__uuid}-${this._mapNumber}`;
|
|
429
444
|
}
|
|
430
445
|
|
|
431
|
-
get activeLayerNames()
|
|
432
|
-
//====================
|
|
433
|
-
{
|
|
434
|
-
return this._userInteractions.activeLayerNames;
|
|
435
|
-
}
|
|
436
|
-
|
|
437
446
|
get annotations()
|
|
438
447
|
//===============
|
|
439
448
|
{
|
|
@@ -739,6 +748,34 @@ class FlatMap
|
|
|
739
748
|
|
|
740
749
|
//==========================================================================
|
|
741
750
|
|
|
751
|
+
/**
|
|
752
|
+
* Get a list of the flatmap's layers.
|
|
753
|
+
*
|
|
754
|
+
* @return {Array.Object.<{id: string, description: string, enabled: boolean}>} An array with layer details
|
|
755
|
+
*/
|
|
756
|
+
getLayers()
|
|
757
|
+
//=========
|
|
758
|
+
{
|
|
759
|
+
if (this._userInteractions !== null) {
|
|
760
|
+
return this._userInteractions.getLayers();
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
/**
|
|
765
|
+
* @param {string} layerId The layer identifier to enable
|
|
766
|
+
* @param {boolean} enable Show or hide the layer. Defaults to ``true`` (show)
|
|
767
|
+
*
|
|
768
|
+
*/
|
|
769
|
+
enableLayer(layerId, enable=true)
|
|
770
|
+
//===============================
|
|
771
|
+
{
|
|
772
|
+
if (this._userInteractions !== null) {
|
|
773
|
+
return this._userInteractions.enableLayer(layerId, enable);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
//==========================================================================
|
|
778
|
+
|
|
742
779
|
/**
|
|
743
780
|
* Add a marker to the map.
|
|
744
781
|
*
|
|
@@ -836,13 +873,21 @@ class FlatMap
|
|
|
836
873
|
'label',
|
|
837
874
|
'models',
|
|
838
875
|
'nodeId',
|
|
839
|
-
'source'
|
|
876
|
+
'source',
|
|
877
|
+
'hyperlinks'
|
|
878
|
+
];
|
|
879
|
+
const jsonProperties = [
|
|
880
|
+
'hyperlinks'
|
|
840
881
|
];
|
|
841
882
|
for (const property of exportedProperties) {
|
|
842
883
|
if (property in properties) {
|
|
843
884
|
const value = properties[property];
|
|
844
885
|
if (value !== undefined) {
|
|
845
|
-
|
|
886
|
+
if (jsonProperties.indexOf(property) >= 0) {
|
|
887
|
+
data[property] = JSON.parse(properties[property])
|
|
888
|
+
} else {
|
|
889
|
+
data[property] = properties[property];
|
|
890
|
+
}
|
|
846
891
|
}
|
|
847
892
|
}
|
|
848
893
|
}
|
|
@@ -1168,13 +1213,12 @@ export class MapManager
|
|
|
1168
1213
|
* @arg options {Object} Configurable options for the map.
|
|
1169
1214
|
* @arg options.background {string} Background colour of flatmap. Defaults to ``white``.
|
|
1170
1215
|
* @arg options.debug {boolean} Enable debugging mode.
|
|
1171
|
-
* @arg options.featureInfo {boolean} Show information about features as a tooltip. The tooltip is active
|
|
1172
|
-
* on selected features and, for non-selected features, when the
|
|
1173
|
-
* ``info`` control is enabled. More details are shown in debug mode.
|
|
1174
1216
|
* @arg options.fullscreenControl {boolean} Add a ``Show full screen`` button to the map.
|
|
1175
1217
|
* @arg options.layerOptions {Object} Options to control colour and outlines of features
|
|
1176
1218
|
* @arg options.layerOptions.colour {boolean} Use colour fill (if available) for features. Defaults to ``true``.
|
|
1177
1219
|
* @arg options.layerOptions.outline {boolean} Show the border of features. Defaults to ``true``.
|
|
1220
|
+
* @arg options.layerOptions.sckan {string} Show neuron paths known to SCKAN: values are ``valid`` (default),
|
|
1221
|
+
* ``invalid``, ``all`` or ``none``.
|
|
1178
1222
|
* @arg options.minimap {boolean|Object} Display a MiniMap of the flatmap. Defaults to ``false``.
|
|
1179
1223
|
* @arg options.minimap.position {string} The minimap's position: ``bottom-left`` (default), ``bottom-right``,
|
|
1180
1224
|
* ``top-left`` or ``top-right``.
|
|
@@ -1185,9 +1229,9 @@ export class MapManager
|
|
|
1185
1229
|
* @arg options.maxZoom {number} The maximum zoom level of the map.
|
|
1186
1230
|
* @arg options.minZoom {number} The minimum zoom level of the map.
|
|
1187
1231
|
* @arg options.navigationControl {boolean} Add navigation controls (zoom buttons) to the map.
|
|
1188
|
-
* @arg options.pathControl {boolean} Add buttons to control pathways including via a color-coded legend.
|
|
1189
|
-
* @arg options.searchable {boolean} Add a control to search for features on a map.
|
|
1190
1232
|
* @arg options.showPosition {boolean} Show ``position`` of tooltip.
|
|
1233
|
+
* @arg options.standalone {boolean} Viewer is running ``standalone``, as opposed to integrated into
|
|
1234
|
+
* another application so show a number of controls. Defaults to ``false``.
|
|
1191
1235
|
* @example
|
|
1192
1236
|
* const humanMap1 = mapManager.loadMap('humanV1', 'div-1');
|
|
1193
1237
|
*
|
|
@@ -1225,6 +1269,14 @@ export class MapManager
|
|
|
1225
1269
|
mapOptions['bounds'] = mapIndex['bounds'];
|
|
1226
1270
|
}
|
|
1227
1271
|
|
|
1272
|
+
// Note the kind of map
|
|
1273
|
+
|
|
1274
|
+
if ('style' in mapIndex) {
|
|
1275
|
+
mapOptions.style = mapIndex.style; // Currently ``flatmap`` or ``fcdiagram``
|
|
1276
|
+
} else {
|
|
1277
|
+
mapOptions.style = 'flatmap';
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1228
1280
|
// Mapmaker has changed the name of the field to indicate that indicates if
|
|
1229
1281
|
// there are raster layers
|
|
1230
1282
|
if (!('image-layers' in mapIndex) && ('image_layer' in mapIndex)) {
|
|
@@ -1300,11 +1352,6 @@ export class MapManager
|
|
|
1300
1352
|
};
|
|
1301
1353
|
}
|
|
1302
1354
|
mapOptions.layerOptions.authoring = ('authoring' in mapIndex) ? mapIndex.authoring : false;
|
|
1303
|
-
if ('style' in mapIndex) {
|
|
1304
|
-
mapOptions.layerOptions.style = mapIndex.style;
|
|
1305
|
-
} else {
|
|
1306
|
-
mapOptions.layerOptions.style = 'flatmap';
|
|
1307
|
-
}
|
|
1308
1355
|
|
|
1309
1356
|
// Are features in separate vector tile source layers?
|
|
1310
1357
|
|
package/src/interactions.js
CHANGED
|
@@ -38,7 +38,7 @@ import {displayedProperties} from './info.js';
|
|
|
38
38
|
import {InfoControl} from './info.js';
|
|
39
39
|
import {LayerManager} from './layers.js';
|
|
40
40
|
import {PATH_TYPES, PATHWAYS_LAYER, Pathways} from './pathways.js';
|
|
41
|
-
import {BackgroundControl, LayerControl, PathControl} from './controls.js';
|
|
41
|
+
import {BackgroundControl, LayerControl, PathControl, SCKANControl} from './controls.js';
|
|
42
42
|
import {SearchControl} from './search.js';
|
|
43
43
|
import {VECTOR_TILES_SOURCE} from './styling.js';
|
|
44
44
|
|
|
@@ -130,47 +130,47 @@ export class UserInteractions
|
|
|
130
130
|
|
|
131
131
|
flatmap.setInitialPosition();
|
|
132
132
|
|
|
133
|
-
// Add a control to search annotations if option set
|
|
134
|
-
|
|
135
|
-
if (flatmap.options.searchable) {
|
|
136
|
-
this._map.addControl(new SearchControl(flatmap));
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Show information about features
|
|
140
|
-
|
|
141
|
-
if (flatmap.options.featureInfo || flatmap.options.searchable) {
|
|
142
|
-
this._infoControl = new InfoControl(flatmap);
|
|
143
|
-
if (flatmap.options.featureInfo) {
|
|
144
|
-
this._map.addControl(this._infoControl);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
133
|
// Add and manage our layers
|
|
149
134
|
|
|
150
135
|
this._layerManager = new LayerManager(flatmap);
|
|
151
136
|
|
|
152
|
-
//
|
|
137
|
+
// Path visibility is either controlled externally or by a local control
|
|
153
138
|
|
|
154
|
-
|
|
155
|
-
|
|
139
|
+
this._pathways = new Pathways(flatmap);
|
|
140
|
+
|
|
141
|
+
// The path types in this map
|
|
142
|
+
const mapPathTypes = this._pathways.pathTypes;
|
|
143
|
+
|
|
144
|
+
// Disable paths that are not initially shown
|
|
145
|
+
for (const path of mapPathTypes) {
|
|
146
|
+
if ('enabled' in path && !path.enabled) {
|
|
147
|
+
this.enablePath(path.type, false);
|
|
148
|
+
}
|
|
156
149
|
}
|
|
157
150
|
|
|
158
|
-
//
|
|
159
|
-
// or by our local controls
|
|
151
|
+
// Add various controls when running standalone
|
|
160
152
|
|
|
161
|
-
|
|
153
|
+
if (flatmap.options.standalone) {
|
|
154
|
+
// Add a control to search annotations if option set
|
|
155
|
+
this._map.addControl(new SearchControl(flatmap));
|
|
162
156
|
|
|
163
|
-
|
|
157
|
+
// Show information about features
|
|
158
|
+
this._infoControl = new InfoControl(flatmap);
|
|
159
|
+
this._map.addControl(this._infoControl);
|
|
164
160
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
this._map.addControl(new PathControl(flatmap, this._pathways.pathTypes));
|
|
168
|
-
}
|
|
161
|
+
// Control background colour (NB. this depends on having map layers created)
|
|
162
|
+
this._map.addControl(new BackgroundControl(flatmap));
|
|
169
163
|
|
|
170
|
-
|
|
164
|
+
// Add a control to manage our paths
|
|
165
|
+
this._map.addControl(new PathControl(flatmap, mapPathTypes));
|
|
171
166
|
|
|
172
|
-
|
|
167
|
+
// Add a control to manage our layers
|
|
173
168
|
this._map.addControl(new LayerControl(flatmap, this._layerManager));
|
|
169
|
+
|
|
170
|
+
// A SCKAN path control for FC maps
|
|
171
|
+
if (flatmap.options.style === 'fcdiagram') {
|
|
172
|
+
this._map.addControl(new SCKANControl(flatmap));
|
|
173
|
+
}
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
// Flag features that have annotations
|
|
@@ -221,7 +221,7 @@ export class UserInteractions
|
|
|
221
221
|
return {
|
|
222
222
|
center: this._map.getCenter().toArray(),
|
|
223
223
|
zoom: this._map.getZoom(),
|
|
224
|
-
layers: this.
|
|
224
|
+
layers: this.layers
|
|
225
225
|
};
|
|
226
226
|
}
|
|
227
227
|
|
|
@@ -253,10 +253,16 @@ export class UserInteractions
|
|
|
253
253
|
this._layerManager.setColour(options);
|
|
254
254
|
}
|
|
255
255
|
|
|
256
|
-
|
|
257
|
-
|
|
256
|
+
getLayers()
|
|
257
|
+
//=========
|
|
258
258
|
{
|
|
259
|
-
return this._layerManager.
|
|
259
|
+
return this._layerManager.layers;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
enableLayer(layerId, enable=true)
|
|
263
|
+
//===============================
|
|
264
|
+
{
|
|
265
|
+
this._layerManager.activate(layerId, enable);
|
|
260
266
|
}
|
|
261
267
|
|
|
262
268
|
mapFeature_(featureId)
|
|
@@ -1022,6 +1028,12 @@ export class UserInteractions
|
|
|
1022
1028
|
return this._pathways.nodePathModels(nodeId);
|
|
1023
1029
|
}
|
|
1024
1030
|
|
|
1031
|
+
showSckanPaths(state='valid')
|
|
1032
|
+
//===========================
|
|
1033
|
+
{
|
|
1034
|
+
this._layerManager.setFilter({sckan: state});
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1025
1037
|
//==============================================================================
|
|
1026
1038
|
|
|
1027
1039
|
// Find where to place a label or popup on a feature
|
package/src/layers.js
CHANGED
|
@@ -35,12 +35,13 @@ const RASTER_LAYERS_ID = 'background-image-layer';
|
|
|
35
35
|
|
|
36
36
|
class MapStylingLayers
|
|
37
37
|
{
|
|
38
|
-
constructor(flatmap,
|
|
38
|
+
constructor(flatmap, layer, options)
|
|
39
39
|
{
|
|
40
40
|
this.__map = flatmap.map;
|
|
41
|
-
this.__id =
|
|
42
|
-
this.
|
|
41
|
+
this.__id = layer.id;
|
|
42
|
+
this.__description = layer.description;
|
|
43
43
|
this.__active = true;
|
|
44
|
+
this.__layers = [];
|
|
44
45
|
this.__layerOptions = options;
|
|
45
46
|
this.__separateLayers = flatmap.options.separateLayers;
|
|
46
47
|
}
|
|
@@ -51,6 +52,12 @@ class MapStylingLayers
|
|
|
51
52
|
return this.__id;
|
|
52
53
|
}
|
|
53
54
|
|
|
55
|
+
get description()
|
|
56
|
+
//===============
|
|
57
|
+
{
|
|
58
|
+
return this.__description;
|
|
59
|
+
}
|
|
60
|
+
|
|
54
61
|
get active()
|
|
55
62
|
//==========
|
|
56
63
|
{
|
|
@@ -85,6 +92,16 @@ class MapStylingLayers
|
|
|
85
92
|
return this.__separateLayers ? `${this.__id}_${sourceLayer}`
|
|
86
93
|
: sourceLayer;
|
|
87
94
|
}
|
|
95
|
+
|
|
96
|
+
setColour(options)
|
|
97
|
+
{
|
|
98
|
+
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
setFilter(options)
|
|
102
|
+
{
|
|
103
|
+
|
|
104
|
+
}
|
|
88
105
|
}
|
|
89
106
|
|
|
90
107
|
//==============================================================================
|
|
@@ -93,7 +110,7 @@ class MapFeatureLayers extends MapStylingLayers
|
|
|
93
110
|
{
|
|
94
111
|
constructor(flatmap, layer, options)
|
|
95
112
|
{
|
|
96
|
-
super(flatmap, layer
|
|
113
|
+
super(flatmap, layer, options);
|
|
97
114
|
const vectorTileSource = this.__map.getSource('vector-tiles');
|
|
98
115
|
const haveVectorLayers = (typeof vectorTileSource !== 'undefined');
|
|
99
116
|
|
|
@@ -108,7 +125,7 @@ class MapFeatureLayers extends MapStylingLayers
|
|
|
108
125
|
this.__addStyleLayer(style.FeatureLineLayer);
|
|
109
126
|
this.__addStyleLayer(style.FeatureBorderLayer);
|
|
110
127
|
}
|
|
111
|
-
this.__addPathwayStyleLayers(
|
|
128
|
+
this.__addPathwayStyleLayers();
|
|
112
129
|
if (vectorFeatures) {
|
|
113
130
|
this.__addStyleLayer(style.FeatureLargeSymbolLayer);
|
|
114
131
|
if (!flatmap.options.tooltips) {
|
|
@@ -138,8 +155,12 @@ class MapFeatureLayers extends MapStylingLayers
|
|
|
138
155
|
if (this.__map.getSource('vector-tiles')
|
|
139
156
|
.vectorLayerIds
|
|
140
157
|
.indexOf(pathwaysVectorSource) >= 0) {
|
|
158
|
+
this.__addStyleLayer(style.CentrelineEdgeLayer, PATHWAYS_LAYER);
|
|
159
|
+
this.__addStyleLayer(style.CentrelineTrackLayer, PATHWAYS_LAYER);
|
|
160
|
+
|
|
141
161
|
this.__addStyleLayer(style.PathLineLayer, PATHWAYS_LAYER);
|
|
142
162
|
this.__addStyleLayer(style.PathDashlineLayer, PATHWAYS_LAYER);
|
|
163
|
+
|
|
143
164
|
this.__addStyleLayer(style.NervePolygonBorder, PATHWAYS_LAYER);
|
|
144
165
|
this.__addStyleLayer(style.NervePolygonFill, PATHWAYS_LAYER);
|
|
145
166
|
this.__addStyleLayer(style.FeatureNerveLayer, PATHWAYS_LAYER);
|
|
@@ -156,6 +177,17 @@ class MapFeatureLayers extends MapStylingLayers
|
|
|
156
177
|
}
|
|
157
178
|
}
|
|
158
179
|
}
|
|
180
|
+
|
|
181
|
+
setFilter(options)
|
|
182
|
+
//================
|
|
183
|
+
{
|
|
184
|
+
for (const layer of this.__layers) {
|
|
185
|
+
const filter = layer.makeFilter(options);
|
|
186
|
+
if (filter !== null) {
|
|
187
|
+
this.__map.setFilter(layer.id, filter, {validate: true});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
159
191
|
}
|
|
160
192
|
|
|
161
193
|
//==============================================================================
|
|
@@ -164,7 +196,11 @@ class MapRasterLayers extends MapStylingLayers
|
|
|
164
196
|
{
|
|
165
197
|
constructor(flatmap, options, bodyLayerId=null)
|
|
166
198
|
{
|
|
167
|
-
|
|
199
|
+
const rasterLayer = {
|
|
200
|
+
id: RASTER_LAYERS_ID,
|
|
201
|
+
description: RASTER_LAYERS_NAME
|
|
202
|
+
};
|
|
203
|
+
super(flatmap, rasterLayer, options);
|
|
168
204
|
if (bodyLayerId !== null) {
|
|
169
205
|
const layerId = `${bodyLayerId}_${FEATURES_LAYER}`;
|
|
170
206
|
const source = flatmap.options.separateLayers ? layerId : FEATURES_LAYER;
|
|
@@ -209,11 +245,11 @@ export class LayerManager
|
|
|
209
245
|
{
|
|
210
246
|
this.__flatmap = flatmap;
|
|
211
247
|
this.__map = flatmap.map;
|
|
212
|
-
this.__layers = new Map;
|
|
213
248
|
this.__mapLayers = new Map;
|
|
214
249
|
this.__layerOptions = utils.setDefaults(flatmap.options.layerOptions, {
|
|
215
250
|
colour: true,
|
|
216
|
-
outline: true
|
|
251
|
+
outline: true,
|
|
252
|
+
sckan: 'valid'
|
|
217
253
|
});;
|
|
218
254
|
const backgroundLayer = new style.BackgroundLayer();
|
|
219
255
|
if ('background' in flatmap.options) {
|
|
@@ -234,44 +270,29 @@ export class LayerManager
|
|
|
234
270
|
for (const layer of flatmap.layers) {
|
|
235
271
|
rasterLayers.addLayer(layer);
|
|
236
272
|
}
|
|
237
|
-
this.__layers.set(RASTER_LAYERS_ID, {
|
|
238
|
-
id: RASTER_LAYERS_ID,
|
|
239
|
-
description: RASTER_LAYERS_NAME
|
|
240
|
-
});
|
|
241
273
|
this.__mapLayers.set(RASTER_LAYERS_ID, rasterLayers);
|
|
242
274
|
} else {
|
|
243
275
|
this.__layerOptions.activeRasterLayer = false;
|
|
244
276
|
}
|
|
245
277
|
for (const layer of flatmap.layers) {
|
|
246
|
-
|
|
278
|
+
this.__mapLayers.set(layer.id, new MapFeatureLayers(this.__flatmap,
|
|
279
|
+
layer,
|
|
280
|
+
this.__layerOptions));
|
|
247
281
|
}
|
|
248
282
|
}
|
|
249
283
|
|
|
250
|
-
get activeLayerNames()
|
|
251
|
-
//====================
|
|
252
|
-
{
|
|
253
|
-
const activeNames = [];
|
|
254
|
-
for (const mapLayer of this.__mapLayers.values()) {
|
|
255
|
-
if (mapLayer.active) {
|
|
256
|
-
activeNames.push(mapLayer.id);
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
return activeNames;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
__addFeatureLayer(layer)
|
|
263
|
-
//======================
|
|
264
|
-
{
|
|
265
|
-
this.__layers.set(layer.id, layer);
|
|
266
|
-
this.__mapLayers.set(layer.id, new MapFeatureLayers(this.__flatmap,
|
|
267
|
-
layer,
|
|
268
|
-
this.__layerOptions));
|
|
269
|
-
}
|
|
270
|
-
|
|
271
284
|
get layers()
|
|
272
285
|
//==========
|
|
273
286
|
{
|
|
274
|
-
|
|
287
|
+
const layers = [];
|
|
288
|
+
for (const mapLayer of this.__mapLayers.values()) {
|
|
289
|
+
layers.push({
|
|
290
|
+
id: mapLayer.id,
|
|
291
|
+
description: mapLayer.description,
|
|
292
|
+
enabled: mapLayer.active
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
return layers;
|
|
275
296
|
}
|
|
276
297
|
|
|
277
298
|
activate(layerId, enable=true)
|
|
@@ -291,14 +312,23 @@ export class LayerManager
|
|
|
291
312
|
}
|
|
292
313
|
}
|
|
293
314
|
|
|
294
|
-
setColour(options=
|
|
295
|
-
|
|
315
|
+
setColour(options={})
|
|
316
|
+
//===================
|
|
296
317
|
{
|
|
297
318
|
this.__layerOptions = utils.setDefaults(options, this.__layerOptions);
|
|
298
319
|
for (const mapLayer of this.__mapLayers.values()) {
|
|
299
320
|
mapLayer.setColour(this.__layerOptions);
|
|
300
321
|
}
|
|
301
322
|
}
|
|
323
|
+
|
|
324
|
+
setFilter(options={})
|
|
325
|
+
//===================
|
|
326
|
+
{
|
|
327
|
+
this.__layerOptions = utils.setDefaults(options, this.__layerOptions);
|
|
328
|
+
for (const mapLayer of this.__mapLayers.values()) {
|
|
329
|
+
mapLayer.setFilter(this.__layerOptions);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
302
332
|
}
|
|
303
333
|
|
|
304
334
|
//==============================================================================
|
package/src/main.js
CHANGED
|
@@ -57,14 +57,10 @@ export async function standaloneViewer(map_endpoint=null, options={})
|
|
|
57
57
|
const mapOptions = Object.assign({
|
|
58
58
|
tooltips: true,
|
|
59
59
|
background: defaultBackground,
|
|
60
|
-
backgroundControl: true,
|
|
61
60
|
debug: false,
|
|
62
61
|
minimap: false,
|
|
63
|
-
searchable: true,
|
|
64
|
-
featureInfo: true,
|
|
65
62
|
showPosition: false,
|
|
66
|
-
|
|
67
|
-
layerControl: true
|
|
63
|
+
standalone: true
|
|
68
64
|
}, options);
|
|
69
65
|
|
|
70
66
|
function loadMap(id, taxon, sex)
|
package/src/pathways.js
CHANGED
|
@@ -28,14 +28,17 @@ export const PATHWAYS_LAYER = 'pathways';
|
|
|
28
28
|
|
|
29
29
|
export const PATH_TYPES = [
|
|
30
30
|
{ type: "cns", label: "CNS", colour: "#9B1FC1"},
|
|
31
|
-
{ type: "
|
|
31
|
+
{ type: "intracardiac", label: "Local circuit neuron", colour: "#F19E38"},
|
|
32
32
|
{ type: "para-pre", label: "Parasympathetic pre-ganglionic", colour: "#3F8F4A"},
|
|
33
33
|
{ type: "para-post", label: "Parasympathetic post-ganglionic", colour: "#3F8F4A"},
|
|
34
34
|
{ type: "sensory", label: "Sensory (afferent) neuron", colour: "#2A62F6"},
|
|
35
35
|
{ type: "somatic", label: "Somatic lower motor", colour: "#98561D"},
|
|
36
36
|
{ type: "symp-pre", label: "Sympathetic pre-ganglionic", colour: "#EA3423"},
|
|
37
37
|
{ type: "symp-post", label: "Sympathetic post-ganglionic", colour: "#EA3423"},
|
|
38
|
-
{ type: "other", label: "Other neuron type", colour: "#888"}
|
|
38
|
+
{ type: "other", label: "Other neuron type", colour: "#888"},
|
|
39
|
+
{ type: "arterial", label: "Arterial blood vessel", colour: "#F00"},
|
|
40
|
+
{ type: "venous", label: "Venous blood vessel", colour: "#2F6EBA"},
|
|
41
|
+
{ type: "centreline", label: "Nerve centrelines", colour: "#2F6EBA", enabled: false}
|
|
39
42
|
];
|
|
40
43
|
|
|
41
44
|
export const PATH_STYLE_RULES =
|
package/src/styling.js
CHANGED
|
@@ -44,6 +44,11 @@ class VectorStyleLayer
|
|
|
44
44
|
return this.__id;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
makeFilter(options)
|
|
48
|
+
{
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
47
52
|
paintStyle(options, changes=false)
|
|
48
53
|
{
|
|
49
54
|
return {};
|
|
@@ -134,7 +139,8 @@ export class FeatureFillLayer extends VectorStyleLayer
|
|
|
134
139
|
'fill-opacity': [
|
|
135
140
|
'case',
|
|
136
141
|
['boolean', ['feature-state', 'selected'], false], 0.7,
|
|
137
|
-
['has', '
|
|
142
|
+
['has', 'opacity'], ['get', 'opacity'],
|
|
143
|
+
['has', 'colour'], 1.0,
|
|
138
144
|
['boolean', ['feature-state', 'active'], false], 0.7,
|
|
139
145
|
['has', 'node'], 0.3,
|
|
140
146
|
['any',
|
|
@@ -183,7 +189,7 @@ export class FeatureBorderLayer extends VectorStyleLayer
|
|
|
183
189
|
const activeRasterLayer = 'activeRasterLayer' in options && options.activeRasterLayer;
|
|
184
190
|
const lineColour = [ 'case' ];
|
|
185
191
|
lineColour.push(['boolean', ['feature-state', 'selected'], false]);
|
|
186
|
-
lineColour.push('
|
|
192
|
+
lineColour.push('black');
|
|
187
193
|
if (coloured && outlined) {
|
|
188
194
|
lineColour.push(['boolean', ['feature-state', 'active'], false]);
|
|
189
195
|
lineColour.push('blue');
|
|
@@ -249,26 +255,32 @@ export class FeatureBorderLayer extends VectorStyleLayer
|
|
|
249
255
|
|
|
250
256
|
export class FeatureLineLayer extends VectorStyleLayer
|
|
251
257
|
{
|
|
252
|
-
constructor(id, sourceLayer,
|
|
258
|
+
constructor(id, sourceLayer, options={})
|
|
253
259
|
{
|
|
260
|
+
const dashed = ('dashed' in options && options.dashed);
|
|
254
261
|
const filterType = dashed ? 'line-dash' : 'line';
|
|
255
262
|
super(id, `feature-${filterType}`, sourceLayer);
|
|
256
|
-
this.
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
263
|
+
this.__dashed = dashed;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
makeFilter(options={})
|
|
267
|
+
{
|
|
268
|
+
return this.__dashed ? [
|
|
269
|
+
'all',
|
|
270
|
+
['==', '$type', 'LineString'],
|
|
271
|
+
['==', 'type', `line-dash`]
|
|
272
|
+
] : [
|
|
273
|
+
'all',
|
|
274
|
+
['==', '$type', 'LineString'],
|
|
262
275
|
[
|
|
263
276
|
'any',
|
|
264
|
-
['has', 'centreline'],
|
|
265
277
|
['==', 'type', 'bezier'],
|
|
266
278
|
['==', 'type', `line`]
|
|
267
|
-
]
|
|
268
|
-
|
|
279
|
+
]
|
|
280
|
+
];
|
|
269
281
|
}
|
|
270
282
|
|
|
271
|
-
paintStyle(options)
|
|
283
|
+
paintStyle(options, changes=false)
|
|
272
284
|
{
|
|
273
285
|
const coloured = !('colour' in options) || options.colour;
|
|
274
286
|
const paintStyle = {
|
|
@@ -278,7 +290,6 @@ export class FeatureLineLayer extends VectorStyleLayer
|
|
|
278
290
|
['has', 'colour'], ['get', 'colour'],
|
|
279
291
|
['boolean', ['feature-state', 'active'], false], coloured ? '#888' : '#CCC',
|
|
280
292
|
['==', ['get', 'type'], 'network'], '#AFA202',
|
|
281
|
-
['has', 'centreline'], '#888',
|
|
282
293
|
options.authoring ? '#C44' : '#444'
|
|
283
294
|
],
|
|
284
295
|
'line-opacity': [
|
|
@@ -292,7 +303,6 @@ export class FeatureLineLayer extends VectorStyleLayer
|
|
|
292
303
|
'let',
|
|
293
304
|
'width', [
|
|
294
305
|
'case',
|
|
295
|
-
['has', 'centreline'], 1.2,
|
|
296
306
|
['==', ['get', 'type'], 'network'], 1.2,
|
|
297
307
|
['boolean', ['feature-state', 'selected'], false], 1.2,
|
|
298
308
|
['boolean', ['feature-state', 'active'], false], 1.2,
|
|
@@ -312,7 +322,7 @@ export class FeatureLineLayer extends VectorStyleLayer
|
|
|
312
322
|
if (this.__dashed) {
|
|
313
323
|
paintStyle['line-dasharray'] = [3, 2];
|
|
314
324
|
}
|
|
315
|
-
return paintStyle;
|
|
325
|
+
return super.changedPaintStyle(paintStyle, changes);
|
|
316
326
|
}
|
|
317
327
|
|
|
318
328
|
style(options)
|
|
@@ -320,12 +330,7 @@ export class FeatureLineLayer extends VectorStyleLayer
|
|
|
320
330
|
return {
|
|
321
331
|
...super.style(),
|
|
322
332
|
'type': 'line',
|
|
323
|
-
'filter':
|
|
324
|
-
'all',
|
|
325
|
-
['==', '$type', 'LineString'],
|
|
326
|
-
this.__filter
|
|
327
|
-
// not for paths...
|
|
328
|
-
],
|
|
333
|
+
'filter': this.makeFilter(options),
|
|
329
334
|
'paint': this.paintStyle(options)
|
|
330
335
|
};
|
|
331
336
|
}
|
|
@@ -337,7 +342,7 @@ export class FeatureDashLineLayer extends FeatureLineLayer
|
|
|
337
342
|
{
|
|
338
343
|
constructor(id, sourceLayer)
|
|
339
344
|
{
|
|
340
|
-
super(id, sourceLayer, true);
|
|
345
|
+
super(id, sourceLayer, {dashed: true});
|
|
341
346
|
}
|
|
342
347
|
}
|
|
343
348
|
|
|
@@ -345,25 +350,63 @@ export class FeatureDashLineLayer extends FeatureLineLayer
|
|
|
345
350
|
|
|
346
351
|
export class PathLineLayer extends VectorStyleLayer
|
|
347
352
|
{
|
|
348
|
-
constructor(id, sourceLayer,
|
|
353
|
+
constructor(id, sourceLayer, options={})
|
|
349
354
|
{
|
|
355
|
+
const dashed = ('dashed' in options && options.dashed);
|
|
350
356
|
const filterType = dashed ? 'line-dash' : 'line';
|
|
351
357
|
super(id, `path-${filterType}`, sourceLayer);
|
|
352
|
-
this.
|
|
353
|
-
|
|
358
|
+
this.__dashed = dashed;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
makeFilter(options={})
|
|
362
|
+
{
|
|
363
|
+
const sckanState = !'sckan' in options ? 'all'
|
|
364
|
+
: options.sckan.toLowerCase();
|
|
365
|
+
const sckan_filter =
|
|
366
|
+
sckanState == 'none' ? [
|
|
367
|
+
['!has', 'sckan']
|
|
368
|
+
] :
|
|
369
|
+
sckanState == 'valid' ? [[
|
|
354
370
|
'any',
|
|
355
|
-
['
|
|
356
|
-
|
|
357
|
-
|
|
371
|
+
['!has', 'sckan'],
|
|
372
|
+
[
|
|
373
|
+
'all',
|
|
374
|
+
['has', 'sckan'],
|
|
375
|
+
['==', 'sckan', true]
|
|
376
|
+
]
|
|
377
|
+
]] :
|
|
378
|
+
sckanState == 'invalid' ? [[
|
|
379
|
+
'any',
|
|
380
|
+
['!has', 'sckan'],
|
|
381
|
+
[
|
|
382
|
+
'all',
|
|
383
|
+
['has', 'sckan'],
|
|
384
|
+
['!=', 'sckan', true]
|
|
385
|
+
]
|
|
386
|
+
]] :
|
|
387
|
+
[ ];
|
|
388
|
+
|
|
389
|
+
return this.__dashed ? [
|
|
390
|
+
'all',
|
|
391
|
+
['==', '$type', 'LineString'],
|
|
392
|
+
['==', 'type', `line-dash`],
|
|
393
|
+
...sckan_filter
|
|
394
|
+
] : [
|
|
395
|
+
'all',
|
|
396
|
+
['==', '$type', 'LineString'],
|
|
358
397
|
[
|
|
359
398
|
'any',
|
|
360
399
|
['==', 'type', 'bezier'],
|
|
361
|
-
[
|
|
362
|
-
|
|
363
|
-
|
|
400
|
+
[
|
|
401
|
+
'all',
|
|
402
|
+
['==', 'type', `line`],
|
|
403
|
+
...sckan_filter
|
|
404
|
+
]
|
|
405
|
+
]
|
|
406
|
+
];
|
|
364
407
|
}
|
|
365
408
|
|
|
366
|
-
paintStyle(options, changes=false)
|
|
409
|
+
paintStyle(options={}, changes=false)
|
|
367
410
|
{
|
|
368
411
|
const dimmed = 'dimmed' in options && options.dimmed;
|
|
369
412
|
const paintStyle = {
|
|
@@ -372,7 +415,7 @@ export class PathLineLayer extends VectorStyleLayer
|
|
|
372
415
|
['boolean', ['feature-state', 'selected'], false], '#0F0',
|
|
373
416
|
['boolean', ['feature-state', 'hidden'], false], '#CCC',
|
|
374
417
|
['==', ['get', 'type'], 'bezier'], 'red',
|
|
375
|
-
['
|
|
418
|
+
['has', 'error'], '#FFFE0E',
|
|
376
419
|
['==', ['get', 'kind'], 'unknown'], '#888',
|
|
377
420
|
...PATH_STYLE_RULES,
|
|
378
421
|
'#888'
|
|
@@ -388,17 +431,18 @@ export class PathLineLayer extends VectorStyleLayer
|
|
|
388
431
|
],
|
|
389
432
|
'line-width': [
|
|
390
433
|
'let',
|
|
391
|
-
'width', [
|
|
434
|
+
'width', ["*", [
|
|
392
435
|
'case',
|
|
393
436
|
['==', ['get', 'type'], 'bezier'], 0.1,
|
|
394
|
-
['
|
|
437
|
+
['has', 'error'], 1,
|
|
395
438
|
['==', ['get', 'kind'], 'unknown'], 1,
|
|
396
439
|
['boolean', ['get', 'invisible'], false], 0.1,
|
|
397
|
-
['boolean', ['feature-state', 'selected'], false],
|
|
398
|
-
['boolean', ['feature-state', 'active'], false],
|
|
399
|
-
0.
|
|
400
|
-
],
|
|
401
|
-
'
|
|
440
|
+
['boolean', ['feature-state', 'selected'], false], 0.6,
|
|
441
|
+
['boolean', ['feature-state', 'active'], false], 0.9,
|
|
442
|
+
0.6
|
|
443
|
+
],
|
|
444
|
+
['case', ['has', 'stroke-width'], ['get', 'stroke-width'], 1.0]],
|
|
445
|
+
['interpolate',
|
|
402
446
|
['exponential', 2],
|
|
403
447
|
['zoom'],
|
|
404
448
|
2, ["*", ['var', 'width'], ["^", 2, -0.5]],
|
|
@@ -408,21 +452,17 @@ export class PathLineLayer extends VectorStyleLayer
|
|
|
408
452
|
]
|
|
409
453
|
};
|
|
410
454
|
if (this.__dashed) {
|
|
411
|
-
paintStyle['line-dasharray'] = [
|
|
455
|
+
paintStyle['line-dasharray'] = [1, 1];
|
|
412
456
|
}
|
|
413
457
|
return super.changedPaintStyle(paintStyle, changes);
|
|
414
458
|
}
|
|
415
459
|
|
|
416
|
-
style(options)
|
|
460
|
+
style(options={})
|
|
417
461
|
{
|
|
418
462
|
return {
|
|
419
463
|
...super.style(),
|
|
420
464
|
'type': 'line',
|
|
421
|
-
'filter':
|
|
422
|
-
'all',
|
|
423
|
-
['==', '$type', 'LineString'],
|
|
424
|
-
this.__filter
|
|
425
|
-
],
|
|
465
|
+
'filter': this.makeFilter(options),
|
|
426
466
|
'layout': {
|
|
427
467
|
'line-cap': 'butt'
|
|
428
468
|
},
|
|
@@ -437,8 +477,93 @@ export class PathDashlineLayer extends PathLineLayer
|
|
|
437
477
|
{
|
|
438
478
|
constructor(id, sourceLayer)
|
|
439
479
|
{
|
|
440
|
-
super(id, sourceLayer, true);
|
|
480
|
+
super(id, sourceLayer, {dashed: true});
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
//==============================================================================
|
|
485
|
+
|
|
486
|
+
class CentrelineLayer extends VectorStyleLayer
|
|
487
|
+
{
|
|
488
|
+
constructor(id, type, sourceLayer)
|
|
489
|
+
{
|
|
490
|
+
super(id, `centreline-${type}`, sourceLayer);
|
|
491
|
+
this.__type = type;
|
|
441
492
|
}
|
|
493
|
+
|
|
494
|
+
paintStyle(options, changes=false)
|
|
495
|
+
{
|
|
496
|
+
const coloured = !('colour' in options) || options.colour;
|
|
497
|
+
const paintStyle = {
|
|
498
|
+
'line-color': (this.__type == 'edge') ? '#000' : [
|
|
499
|
+
'case',
|
|
500
|
+
['boolean', ['feature-state', 'selected'], false], '#0F0',
|
|
501
|
+
['boolean', ['feature-state', 'active'], false], '#444',
|
|
502
|
+
'#CCC'
|
|
503
|
+
],
|
|
504
|
+
'line-opacity': [
|
|
505
|
+
'case',
|
|
506
|
+
['boolean', ['feature-state', 'hidden'], false], 0.01,
|
|
507
|
+
['boolean', ['feature-state', 'selected'], false], 1.0,
|
|
508
|
+
['boolean', ['feature-state', 'active'], false], 1.0,
|
|
509
|
+
0.8
|
|
510
|
+
],
|
|
511
|
+
'line-width': [
|
|
512
|
+
'let',
|
|
513
|
+
'width',
|
|
514
|
+
(this.__type == 'edge') ? 1.6 : 1.2,
|
|
515
|
+
[
|
|
516
|
+
'interpolate',
|
|
517
|
+
['exponential', 2],
|
|
518
|
+
['zoom'],
|
|
519
|
+
2, ["*", ['var', 'width'], ["^", 2, -0.5]],
|
|
520
|
+
7, ["*", ['var', 'width'], ["^", 2, 2.5]],
|
|
521
|
+
9, ["*", ['var', 'width'], ["^", 2, 4.0]]
|
|
522
|
+
]
|
|
523
|
+
]
|
|
524
|
+
// Need to vary width based on zoom??
|
|
525
|
+
// Or opacity??
|
|
526
|
+
};
|
|
527
|
+
return super.changedPaintStyle(paintStyle, changes);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
style(options)
|
|
531
|
+
{
|
|
532
|
+
return {
|
|
533
|
+
...super.style(),
|
|
534
|
+
'type': 'line',
|
|
535
|
+
'filter': [
|
|
536
|
+
'all',
|
|
537
|
+
['==', '$type', 'LineString'],
|
|
538
|
+
['==', 'kind', 'centreline']
|
|
539
|
+
],
|
|
540
|
+
'paint': this.paintStyle(options),
|
|
541
|
+
'layout': {
|
|
542
|
+
'line-cap': 'square',
|
|
543
|
+
'line-join': 'bevel'
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
export class CentrelineEdgeLayer extends CentrelineLayer
|
|
551
|
+
{
|
|
552
|
+
constructor(id, sourceLayer)
|
|
553
|
+
{
|
|
554
|
+
super(id, 'edge', sourceLayer);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
export class CentrelineTrackLayer extends CentrelineLayer
|
|
560
|
+
{
|
|
561
|
+
constructor(id, sourceLayer)
|
|
562
|
+
{
|
|
563
|
+
super(id, 'track', sourceLayer);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
|
|
442
567
|
}
|
|
443
568
|
|
|
444
569
|
//==============================================================================
|
|
@@ -458,6 +583,7 @@ export class FeatureNerveLayer extends VectorStyleLayer
|
|
|
458
583
|
'filter': [
|
|
459
584
|
'all',
|
|
460
585
|
['==', '$type', 'LineString'],
|
|
586
|
+
['!=', 'kind', 'centreline'],
|
|
461
587
|
['==', 'type', 'nerve']
|
|
462
588
|
],
|
|
463
589
|
'paint': {
|