@abi-software/flatmap-viewer 2.2.11-devel.2 → 2.2.12-b.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 +1 -1
- package/src/controls.js +75 -31
- package/src/flatmap-viewer.js +54 -31
- package/src/info.js +5 -1
- package/src/interactions.js +113 -99
- package/src/layers.js +43 -18
- package/src/pathways.js +20 -7
- package/src/styling.js +77 -5
- package/static/flatmap-viewer.css +3 -30
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.2.
|
|
41
|
+
* ``npm install @abi-software/flatmap-viewer@2.2.12-b.2``
|
|
42
42
|
|
|
43
43
|
Documentation
|
|
44
44
|
-------------
|
package/package.json
CHANGED
package/src/controls.js
CHANGED
|
@@ -123,7 +123,11 @@ export class PathControl
|
|
|
123
123
|
if (checked != '') {
|
|
124
124
|
this.__checkedCount += 1;
|
|
125
125
|
}
|
|
126
|
-
|
|
126
|
+
const colour = path.colour || '#440';
|
|
127
|
+
const style = path.dashed ? `background: repeating-linear-gradient(to right,${colour} 0,${colour} 6px,transparent 6px,transparent 9px);`
|
|
128
|
+
: `background: ${colour};`;
|
|
129
|
+
|
|
130
|
+
innerHTML.push(`<label for="path-${path.type}">${path.label}</label><div class="nerve-line" style="${style}"></div><input id="path-${path.type}" type="checkbox" ${checked}/>`);
|
|
127
131
|
}
|
|
128
132
|
this._legend.innerHTML = innerHTML.join('\n');
|
|
129
133
|
this.__halfCount = Math.trunc(this.__pathTypes.length/2);
|
|
@@ -228,12 +232,9 @@ export class LayerControl
|
|
|
228
232
|
{
|
|
229
233
|
this.__map = map;
|
|
230
234
|
this.__container = document.createElement('div');
|
|
231
|
-
this.__container.className = 'maplibregl-ctrl';
|
|
232
|
-
this.__container.id = 'flatmap-layer-control';
|
|
233
|
-
|
|
235
|
+
this.__container.className = 'maplibregl-ctrl flatmap-control';
|
|
234
236
|
this.__layersControl = document.createElement('div');
|
|
235
|
-
this.__layersControl.
|
|
236
|
-
this.__layersControl.className = 'flatmap-layer-grid';
|
|
237
|
+
this.__layersControl.className = 'flatmap-control-grid';
|
|
237
238
|
|
|
238
239
|
const innerHTML = [];
|
|
239
240
|
innerHTML.push(`<label for="layer-all-layers">ALL LAYERS:</label><input id="layer-all-layers" type="checkbox" checked/>`);
|
|
@@ -323,7 +324,6 @@ export class LayerControl
|
|
|
323
324
|
|
|
324
325
|
//==============================================================================
|
|
325
326
|
|
|
326
|
-
|
|
327
327
|
const SCKAN_STATES = [
|
|
328
328
|
{
|
|
329
329
|
'id': 'VALID',
|
|
@@ -338,11 +338,11 @@ const SCKAN_STATES = [
|
|
|
338
338
|
|
|
339
339
|
export class SCKANControl
|
|
340
340
|
{
|
|
341
|
-
constructor(flatmap)
|
|
341
|
+
constructor(flatmap, options={sckan: 'valid'})
|
|
342
342
|
{
|
|
343
343
|
this.__flatmap = flatmap;
|
|
344
344
|
this.__map = undefined;
|
|
345
|
-
this.
|
|
345
|
+
this.__initialState = options.sckan || 'valid';
|
|
346
346
|
}
|
|
347
347
|
|
|
348
348
|
getDefaultPosition()
|
|
@@ -356,25 +356,22 @@ export class SCKANControl
|
|
|
356
356
|
{
|
|
357
357
|
this.__map = map;
|
|
358
358
|
this.__container = document.createElement('div');
|
|
359
|
-
this.__container.className = 'maplibregl-ctrl';
|
|
360
|
-
this.__container.id = 'flatmap-layer-control';
|
|
361
|
-
|
|
359
|
+
this.__container.className = 'maplibregl-ctrl flatmap-control';
|
|
362
360
|
this.__sckan = document.createElement('div');
|
|
363
|
-
this.__sckan.
|
|
364
|
-
this.__sckan.className = 'flatmap-layer-grid';
|
|
361
|
+
this.__sckan.className = 'flatmap-control-grid';
|
|
365
362
|
|
|
366
363
|
const innerHTML = [];
|
|
367
|
-
let checked = (this.
|
|
364
|
+
let checked = (this.__initialState === 'all') ? 'checked' : '';
|
|
368
365
|
innerHTML.push(`<label for="sckan-all-paths">ALL PATHS:</label><input id="sckan-all-paths" type="checkbox" ${checked}/>`);
|
|
369
366
|
for (const state of SCKAN_STATES) {
|
|
370
|
-
checked = (this.
|
|
367
|
+
checked = (this.__initialState.toUpperCase() === state.id) ? 'checked' : '';
|
|
371
368
|
innerHTML.push(`<label for="sckan-${state.id}">${state.description}</label><input id="sckan-${state.id}" type="checkbox" ${checked}/>`);
|
|
372
369
|
}
|
|
373
370
|
this.__sckan.innerHTML = innerHTML.join('\n');
|
|
374
371
|
|
|
375
372
|
this.__sckanCount = SCKAN_STATES.length;
|
|
376
|
-
this.__checkedCount = (this.
|
|
377
|
-
: (this.
|
|
373
|
+
this.__checkedCount = (this.__initialState === 'all') ? this.__sckanCount
|
|
374
|
+
: (this.__initialState === 'none') ? 0
|
|
378
375
|
: 1;
|
|
379
376
|
this.__halfCount = Math.trunc(this.__sckanCount/2);
|
|
380
377
|
|
|
@@ -407,7 +404,8 @@ export class SCKANControl
|
|
|
407
404
|
this.__container.appendChild(this.__sckan);
|
|
408
405
|
this.__button.setAttribute('control-visible', 'true');
|
|
409
406
|
const allLayersCheckbox = document.getElementById('sckan-all-paths');
|
|
410
|
-
allLayersCheckbox.indeterminate = this.
|
|
407
|
+
allLayersCheckbox.indeterminate = (this.__checkedCount > 0)
|
|
408
|
+
&& (this.__checkedCount < this.__sckanCount);
|
|
411
409
|
this.__sckan.focus();
|
|
412
410
|
} else {
|
|
413
411
|
this.__sckan = this.__container.removeChild(this.__sckan);
|
|
@@ -430,11 +428,12 @@ export class SCKANControl
|
|
|
430
428
|
const sckanCheckbox = document.getElementById(`sckan-${state.id}`);
|
|
431
429
|
if (sckanCheckbox) {
|
|
432
430
|
sckanCheckbox.checked = event.target.checked;
|
|
433
|
-
|
|
431
|
+
this.__flatmap.enableSckanPath(state.id, event.target.checked);
|
|
432
|
+
}
|
|
434
433
|
}
|
|
435
|
-
this.__flatmap.showSckanPaths(this.__state);
|
|
436
434
|
} else if (event.target.id.startsWith('sckan-')) {
|
|
437
435
|
const sckanId = event.target.id.substring(6);
|
|
436
|
+
this.__flatmap.enableSckanPath(sckanId, event.target.checked);
|
|
438
437
|
if (event.target.checked) {
|
|
439
438
|
this.__checkedCount += 1;
|
|
440
439
|
} else {
|
|
@@ -442,24 +441,14 @@ export class SCKANControl
|
|
|
442
441
|
}
|
|
443
442
|
const allLayersCheckbox = document.getElementById('sckan-all-paths');
|
|
444
443
|
if (this.__checkedCount === 0) {
|
|
445
|
-
this.__state = 'none';
|
|
446
444
|
allLayersCheckbox.checked = false;
|
|
447
445
|
allLayersCheckbox.indeterminate = false;
|
|
448
446
|
} else if (this.__checkedCount === this.__sckanCount) {
|
|
449
|
-
this.__state = 'all';
|
|
450
447
|
allLayersCheckbox.checked = true;
|
|
451
448
|
allLayersCheckbox.indeterminate = false;
|
|
452
449
|
} 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
450
|
allLayersCheckbox.indeterminate = true;
|
|
461
451
|
}
|
|
462
|
-
this.__flatmap.showSckanPaths(this.__state);
|
|
463
452
|
}
|
|
464
453
|
}
|
|
465
454
|
event.stopPropagation();
|
|
@@ -468,6 +457,61 @@ export class SCKANControl
|
|
|
468
457
|
|
|
469
458
|
//==============================================================================
|
|
470
459
|
|
|
460
|
+
export class NerveControl
|
|
461
|
+
{
|
|
462
|
+
constructor(flatmap, options={showCentrelines: false})
|
|
463
|
+
{
|
|
464
|
+
this.__flatmap = flatmap;
|
|
465
|
+
this.__map = undefined;
|
|
466
|
+
this.__visible = options.showCentrelines || false;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
getDefaultPosition()
|
|
470
|
+
//==================
|
|
471
|
+
{
|
|
472
|
+
return 'top-right';
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
onAdd(map)
|
|
476
|
+
//========
|
|
477
|
+
{
|
|
478
|
+
this.__map = map;
|
|
479
|
+
this.__container = document.createElement('div');
|
|
480
|
+
this.__container.className = 'maplibregl-ctrl';
|
|
481
|
+
|
|
482
|
+
this.__button = document.createElement('button');
|
|
483
|
+
this.__button.id = 'map-nerve-button';
|
|
484
|
+
this.__button.className = 'control-button text-button';
|
|
485
|
+
this.__button.setAttribute('type', 'button');
|
|
486
|
+
this.__button.setAttribute('aria-label', 'Show/hide nerve centrelines');
|
|
487
|
+
this.__button.textContent = 'NERVES';
|
|
488
|
+
this.__button.title = 'Show/hide nerve centrelines';
|
|
489
|
+
this.__container.appendChild(this.__button);
|
|
490
|
+
|
|
491
|
+
this.__container.addEventListener('click', this.onClick_.bind(this));
|
|
492
|
+
return this.__container;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
onRemove()
|
|
496
|
+
//========
|
|
497
|
+
{
|
|
498
|
+
this.__container.parentNode.removeChild(this.__container);
|
|
499
|
+
this.__map = undefined;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
onClick_(event)
|
|
503
|
+
//=============
|
|
504
|
+
{
|
|
505
|
+
if (event.target.id === 'map-nerve-button') {
|
|
506
|
+
this.__visible = !this.__visible;
|
|
507
|
+
this.__flatmap.enableCentrelines(this.__visible);
|
|
508
|
+
}
|
|
509
|
+
event.stopPropagation();
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
//==============================================================================
|
|
514
|
+
|
|
471
515
|
export class BackgroundControl
|
|
472
516
|
{
|
|
473
517
|
constructor(flatmap)
|
package/src/flatmap-viewer.js
CHANGED
|
@@ -275,21 +275,24 @@ class FlatMap
|
|
|
275
275
|
}
|
|
276
276
|
|
|
277
277
|
/**
|
|
278
|
-
* @returns {Array.<{type: string, label: string, colour: string}>} an array of objects giving path types
|
|
279
|
-
*
|
|
278
|
+
* @returns {Array.<{type: string, label: string, colour: string}>} an array of objects giving the path types
|
|
279
|
+
* present in the map along with their
|
|
280
|
+
* descriptions and colours
|
|
280
281
|
*/
|
|
281
282
|
pathTypes()
|
|
282
283
|
//=========
|
|
283
284
|
{
|
|
284
|
-
|
|
285
|
+
if (this._userInteractions !== null) {
|
|
286
|
+
return this._userInteractions.pathways.pathTypes();
|
|
287
|
+
}
|
|
285
288
|
}
|
|
286
289
|
|
|
287
290
|
/**
|
|
288
291
|
* Hide or show paths of a given type.
|
|
289
292
|
*
|
|
290
|
-
* @param
|
|
291
|
-
* @param
|
|
292
|
-
*
|
|
293
|
+
* @param {string} pathType The path type
|
|
294
|
+
* @param {boolean} enable Show or hide paths of that type. Defaults to
|
|
295
|
+
* ``true`` (show)
|
|
293
296
|
*/
|
|
294
297
|
enablePath(pathType, enable=true)
|
|
295
298
|
//===============================
|
|
@@ -300,32 +303,31 @@ class FlatMap
|
|
|
300
303
|
}
|
|
301
304
|
|
|
302
305
|
/**
|
|
303
|
-
* Hide or show all paths
|
|
306
|
+
* Hide or show all paths valid in SCKAN.
|
|
304
307
|
*
|
|
305
|
-
* @param
|
|
306
|
-
* @param
|
|
307
|
-
*
|
|
308
|
+
* @param {string} sckanState Either ``valid`` or ``invalid``
|
|
309
|
+
* @param {boolean} enable Show or hide paths with that SCKAN state.
|
|
310
|
+
* Defaults to ``true`` (show)
|
|
308
311
|
*/
|
|
309
|
-
|
|
310
|
-
|
|
312
|
+
enableSckanPath(sckanState, enable=true)
|
|
313
|
+
//======================================
|
|
311
314
|
{
|
|
312
315
|
if (this._userInteractions !== null) {
|
|
313
|
-
this._userInteractions.
|
|
316
|
+
this._userInteractions.enableSckanPath(sckanState, enable);
|
|
314
317
|
}
|
|
315
318
|
}
|
|
316
319
|
|
|
317
320
|
/**
|
|
318
|
-
* Hide or show
|
|
321
|
+
* Hide or show centrelines and nodes.
|
|
319
322
|
*
|
|
320
|
-
* @param
|
|
321
|
-
*
|
|
322
|
-
* of the type(s) otherwise only hide the paths
|
|
323
|
+
* @param {boolean} enable Show or centrelines and associated nodes.
|
|
324
|
+
* Defaults to ``true`` (show)
|
|
323
325
|
*/
|
|
324
|
-
|
|
325
|
-
|
|
326
|
+
enableCentrelines(enable=true)
|
|
327
|
+
//============================
|
|
326
328
|
{
|
|
327
329
|
if (this._userInteractions !== null) {
|
|
328
|
-
this._userInteractions.
|
|
330
|
+
this._userInteractions.enableCentrelines(enable);
|
|
329
331
|
}
|
|
330
332
|
}
|
|
331
333
|
|
|
@@ -664,18 +666,25 @@ class FlatMap
|
|
|
664
666
|
}
|
|
665
667
|
}
|
|
666
668
|
|
|
667
|
-
|
|
668
|
-
|
|
669
|
+
setPaint(options=null)
|
|
670
|
+
//====================
|
|
669
671
|
{
|
|
670
672
|
options = utils.setDefaults(options, {
|
|
671
673
|
colour: true,
|
|
672
674
|
outline: true
|
|
673
675
|
});
|
|
674
676
|
if (this._userInteractions !== null) {
|
|
675
|
-
this._userInteractions.
|
|
677
|
+
this._userInteractions.setPaint(options);
|
|
676
678
|
}
|
|
677
679
|
}
|
|
678
680
|
|
|
681
|
+
setColour(options=null)
|
|
682
|
+
//=====================
|
|
683
|
+
{
|
|
684
|
+
console.log('`setColour()` is deprecated; please use `setPaint()` instead.')
|
|
685
|
+
this.setPaint(options);
|
|
686
|
+
}
|
|
687
|
+
|
|
679
688
|
//==========================================================================
|
|
680
689
|
|
|
681
690
|
/**
|
|
@@ -776,22 +785,36 @@ class FlatMap
|
|
|
776
785
|
|
|
777
786
|
//==========================================================================
|
|
778
787
|
|
|
788
|
+
/**
|
|
789
|
+
* Get a list of a FC flatmap's systems.
|
|
790
|
+
*
|
|
791
|
+
* @return {Array.Object.<{name: string, colour: string}>} An array with system details
|
|
792
|
+
*/
|
|
793
|
+
getSystems()
|
|
794
|
+
//==========
|
|
795
|
+
{
|
|
796
|
+
if (this._userInteractions !== null) {
|
|
797
|
+
return this._userInteractions.getSystems();
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
//==========================================================================
|
|
801
|
+
|
|
779
802
|
/**
|
|
780
803
|
* Add a marker to the map.
|
|
781
804
|
*
|
|
782
805
|
* @param {string} anatomicalId The anatomical identifier of the feature on which
|
|
783
806
|
* to place the marker
|
|
784
|
-
* @param {string} [
|
|
785
|
-
*
|
|
786
|
-
*
|
|
807
|
+
* @param {string} [htmlElement=null] An optional parameter giving the DOM element to
|
|
808
|
+
* use as a marker. The default is a light blue,
|
|
809
|
+
* droplet-shaped SVG marker.
|
|
787
810
|
* @return {integer} The identifier for the resulting marker. -1 is returned if the
|
|
788
811
|
* map doesn't contain a feature with the given anatomical identifier
|
|
789
812
|
*/
|
|
790
|
-
addMarker(anatomicalId,
|
|
791
|
-
|
|
813
|
+
addMarker(anatomicalId, htmlElement=null)
|
|
814
|
+
//========================================
|
|
792
815
|
{
|
|
793
816
|
if (this._userInteractions !== null) {
|
|
794
|
-
return this._userInteractions.addMarker(anatomicalId,
|
|
817
|
+
return this._userInteractions.addMarker(anatomicalId, htmlElement);
|
|
795
818
|
}
|
|
796
819
|
return -1;
|
|
797
820
|
}
|
|
@@ -1272,9 +1295,9 @@ export class MapManager
|
|
|
1272
1295
|
// Note the kind of map
|
|
1273
1296
|
|
|
1274
1297
|
if ('style' in mapIndex) {
|
|
1275
|
-
mapOptions.style = mapIndex.style; // Currently ``
|
|
1298
|
+
mapOptions.style = mapIndex.style; // Currently ``anatomical`` or ``functional``
|
|
1276
1299
|
} else {
|
|
1277
|
-
mapOptions.style = 'flatmap';
|
|
1300
|
+
mapOptions.style = 'flatmap'; // Default is a generic ``flatmap``
|
|
1278
1301
|
}
|
|
1279
1302
|
|
|
1280
1303
|
// Mapmaker has changed the name of the field to indicate that indicates if
|
package/src/info.js
CHANGED
|
@@ -238,7 +238,11 @@ export class InfoControl
|
|
|
238
238
|
if (prop in feature.properties) {
|
|
239
239
|
const value = feature.properties[prop];
|
|
240
240
|
if (value !== undefined) {
|
|
241
|
-
|
|
241
|
+
if (prop === 'label') {
|
|
242
|
+
values[prop] = value.replaceAll("\n", "<br/>");
|
|
243
|
+
} else {
|
|
244
|
+
values[prop] = value;
|
|
245
|
+
}
|
|
242
246
|
}
|
|
243
247
|
}
|
|
244
248
|
});
|
package/src/interactions.js
CHANGED
|
@@ -37,8 +37,8 @@ import {ContextMenu} from './contextmenu.js';
|
|
|
37
37
|
import {displayedProperties} from './info.js';
|
|
38
38
|
import {InfoControl} from './info.js';
|
|
39
39
|
import {LayerManager} from './layers.js';
|
|
40
|
-
import {
|
|
41
|
-
import {BackgroundControl, LayerControl, PathControl, SCKANControl} from './controls.js';
|
|
40
|
+
import {PATHWAYS_LAYER, Pathways} from './pathways.js';
|
|
41
|
+
import {BackgroundControl, LayerControl, NerveControl, PathControl, SCKANControl} from './controls.js';
|
|
42
42
|
import {SearchControl} from './search.js';
|
|
43
43
|
import {VECTOR_TILES_SOURCE} from './styling.js';
|
|
44
44
|
|
|
@@ -118,13 +118,7 @@ export class UserInteractions
|
|
|
118
118
|
this.__annotationByMarkerId = new Map();
|
|
119
119
|
|
|
120
120
|
// Where to put labels and popups on a feature
|
|
121
|
-
this.
|
|
122
|
-
|
|
123
|
-
// MapLibre dynamically sets a transform on marker elements so in
|
|
124
|
-
// order to apply a scale transform we need to create marker icons
|
|
125
|
-
// inside the marker container <div>.
|
|
126
|
-
this._defaultMarkerHTML = new maplibre.Marker().getElement().innerHTML;
|
|
127
|
-
this._simulationMarkerHTML = new maplibre.Marker({color: '#005974'}).getElement().innerHTML;
|
|
121
|
+
this.__markerPositions = new Map();
|
|
128
122
|
|
|
129
123
|
// Fit the map to its initial position
|
|
130
124
|
|
|
@@ -139,7 +133,7 @@ export class UserInteractions
|
|
|
139
133
|
this._pathways = new Pathways(flatmap);
|
|
140
134
|
|
|
141
135
|
// The path types in this map
|
|
142
|
-
const mapPathTypes = this._pathways.pathTypes;
|
|
136
|
+
const mapPathTypes = this._pathways.pathTypes();
|
|
143
137
|
|
|
144
138
|
// Disable paths that are not initially shown
|
|
145
139
|
for (const path of mapPathTypes) {
|
|
@@ -167,20 +161,36 @@ export class UserInteractions
|
|
|
167
161
|
// Add a control to manage our layers
|
|
168
162
|
this._map.addControl(new LayerControl(flatmap, this._layerManager));
|
|
169
163
|
|
|
164
|
+
// Add a control for nerve centrelines if they are present
|
|
165
|
+
if (this._pathways.haveCentrelines) {
|
|
166
|
+
this._map.addControl(new NerveControl(flatmap, this._layerManager, {showCentrelines: false}));
|
|
167
|
+
this.enableCentrelines(false);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
170
|
// A SCKAN path control for FC maps
|
|
171
|
-
if (flatmap.options.style === '
|
|
172
|
-
this._map.addControl(new SCKANControl(flatmap));
|
|
171
|
+
if (flatmap.options.style === 'functional') {
|
|
172
|
+
this._map.addControl(new SCKANControl(flatmap, flatmap.options.layerOptions));
|
|
173
173
|
}
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
-
// Flag features that have annotations
|
|
177
|
-
// Also flag those features that are models of something
|
|
176
|
+
// Flag features that have annotations and not which are FC systems
|
|
178
177
|
|
|
178
|
+
this.__systems = [];
|
|
179
|
+
const seenSystems = [];
|
|
179
180
|
for (const [id, ann] of flatmap.annotations) {
|
|
180
181
|
const feature = this.mapFeature_(id);
|
|
181
182
|
if (feature !== undefined) {
|
|
182
183
|
this._map.setFeatureState(feature, { 'annotated': true });
|
|
183
184
|
}
|
|
185
|
+
if (ann['fc-class'] === 'FC_CLASS.SYSTEM') {
|
|
186
|
+
if (seenSystems.indexOf(ann['name']) < 0) {
|
|
187
|
+
seenSystems.push(ann['name']);
|
|
188
|
+
this.__systems.push({
|
|
189
|
+
name: ann['name'],
|
|
190
|
+
colour: ann['colour']
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
184
194
|
}
|
|
185
195
|
|
|
186
196
|
// Display a context menu on right-click
|
|
@@ -213,6 +223,12 @@ export class UserInteractions
|
|
|
213
223
|
this.__pan_zoom_enabled = false;
|
|
214
224
|
}
|
|
215
225
|
|
|
226
|
+
get pathways()
|
|
227
|
+
//============
|
|
228
|
+
{
|
|
229
|
+
return this._pathways;
|
|
230
|
+
}
|
|
231
|
+
|
|
216
232
|
getState()
|
|
217
233
|
//========
|
|
218
234
|
{
|
|
@@ -246,11 +262,11 @@ export class UserInteractions
|
|
|
246
262
|
}
|
|
247
263
|
}
|
|
248
264
|
|
|
249
|
-
|
|
250
|
-
|
|
265
|
+
setPaint(options)
|
|
266
|
+
//===============
|
|
251
267
|
{
|
|
252
268
|
this.__colourOptions = options;
|
|
253
|
-
this._layerManager.
|
|
269
|
+
this._layerManager.setPaint(options);
|
|
254
270
|
}
|
|
255
271
|
|
|
256
272
|
getLayers()
|
|
@@ -265,6 +281,12 @@ export class UserInteractions
|
|
|
265
281
|
this._layerManager.activate(layerId, enable);
|
|
266
282
|
}
|
|
267
283
|
|
|
284
|
+
getSystems()
|
|
285
|
+
//==========
|
|
286
|
+
{
|
|
287
|
+
return this.__systems;
|
|
288
|
+
}
|
|
289
|
+
|
|
268
290
|
mapFeature_(featureId)
|
|
269
291
|
//====================
|
|
270
292
|
{
|
|
@@ -291,7 +313,7 @@ export class UserInteractions
|
|
|
291
313
|
{
|
|
292
314
|
featureId = +featureId; // Ensure numeric
|
|
293
315
|
if (this._selectedFeatureIds.size === 0) {
|
|
294
|
-
this._layerManager.
|
|
316
|
+
this._layerManager.setPaint({...this.__colourOptions, dimmed: dim});
|
|
295
317
|
}
|
|
296
318
|
if (this._selectedFeatureIds.has(featureId)) {
|
|
297
319
|
this._selectedFeatureIds.set(featureId, this._selectedFeatureIds.get(featureId) + 1);
|
|
@@ -321,7 +343,7 @@ export class UserInteractions
|
|
|
321
343
|
}
|
|
322
344
|
}
|
|
323
345
|
if (this._selectedFeatureIds.size === 0) {
|
|
324
|
-
this._layerManager.
|
|
346
|
+
this._layerManager.setPaint({...this.__colourOptions, dimmed: false});
|
|
325
347
|
}
|
|
326
348
|
}
|
|
327
349
|
|
|
@@ -335,7 +357,7 @@ export class UserInteractions
|
|
|
335
357
|
}
|
|
336
358
|
}
|
|
337
359
|
this._selectedFeatureIds.clear();
|
|
338
|
-
this._layerManager.
|
|
360
|
+
this._layerManager.setPaint({...this.__colourOptions, dimmed: false});
|
|
339
361
|
}
|
|
340
362
|
|
|
341
363
|
activeFeaturesAtEvent_(event)
|
|
@@ -609,7 +631,7 @@ export class UserInteractions
|
|
|
609
631
|
location = this.__lastClickLngLat;
|
|
610
632
|
} else {
|
|
611
633
|
// Position popup at the feature's 'centre'
|
|
612
|
-
location = this.
|
|
634
|
+
location = this.__markerPosition(featureId, ann);
|
|
613
635
|
}
|
|
614
636
|
|
|
615
637
|
// Make sure the feature is on screen
|
|
@@ -651,9 +673,13 @@ export class UserInteractions
|
|
|
651
673
|
const tooltips = [];
|
|
652
674
|
for (const lineFeature of lineFeatures) {
|
|
653
675
|
const properties = lineFeature.properties;
|
|
654
|
-
if ('
|
|
655
|
-
|
|
656
|
-
|
|
676
|
+
if ('error' in properties) {
|
|
677
|
+
tooltips.push(`<div class="feature-error">Error: ${properties.error}</div>`)
|
|
678
|
+
}
|
|
679
|
+
if ('warning' in properties) {
|
|
680
|
+
tooltips.push(`<div class="feature-error">Warning: ${properties.warning}</div>`)
|
|
681
|
+
}
|
|
682
|
+
if ('label' in properties && (!('tooltip' in properties) || properties.tooltip)) {
|
|
657
683
|
let tooltip = '';
|
|
658
684
|
const label = properties.label;
|
|
659
685
|
const cleanLabel = (label.substr(0, 1).toUpperCase() + label.substr(1)).replaceAll("\n", "<br/>");
|
|
@@ -662,32 +688,37 @@ export class UserInteractions
|
|
|
662
688
|
}
|
|
663
689
|
}
|
|
664
690
|
}
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
}
|
|
668
|
-
return `<div class='flatmap-feature-label'>${tooltips.join('<hr/>')}</div>`;
|
|
691
|
+
return (tooltips.length === 0) ? ''
|
|
692
|
+
: `<div class='flatmap-feature-label'>${tooltips.join('<hr/>')}</div>`;
|
|
669
693
|
}
|
|
670
694
|
|
|
671
695
|
tooltipHtml_(properties, forceLabel=false)
|
|
672
696
|
//========================================
|
|
673
697
|
{
|
|
698
|
+
const tooltip = [];
|
|
699
|
+
if ('error' in properties) {
|
|
700
|
+
tooltip.push(`<div class="feature-error">Error: ${properties.error}</div>`)
|
|
701
|
+
}
|
|
702
|
+
if ('warning' in properties) {
|
|
703
|
+
tooltip.push(`<div class="feature-error">Warning: ${properties.warning}</div>`)
|
|
704
|
+
}
|
|
674
705
|
if (('label' in properties || 'hyperlink' in properties)
|
|
675
|
-
|
|
676
|
-
&& !('labelled' in properties)) {
|
|
706
|
+
&& (forceLabel || !('tooltip' in properties) || properties.tooltip)) {
|
|
677
707
|
const label = ('label' in properties) ? (properties.label.substr(0, 1).toUpperCase()
|
|
678
708
|
+ properties.label.substr(1)).replaceAll("\n", "<br/>")
|
|
679
709
|
: '';
|
|
680
710
|
if ('hyperlink' in properties) {
|
|
681
711
|
if (label === '') {
|
|
682
|
-
|
|
712
|
+
tooltip.push(`<a href='${properties.hyperlink}'>${properties.hyperlink}</a>`);
|
|
683
713
|
} else {
|
|
684
|
-
|
|
714
|
+
tooltip.push(`<a href='${properties.hyperlink}'>${label}</a></div>`);
|
|
685
715
|
}
|
|
686
716
|
} else {
|
|
687
|
-
|
|
717
|
+
tooltip.push(label);
|
|
688
718
|
}
|
|
689
719
|
}
|
|
690
|
-
return ''
|
|
720
|
+
return (tooltip.length === 0) ? ''
|
|
721
|
+
: `<div class='flatmap-feature-label'>${tooltip.join('<hr/>')}</div>`;
|
|
691
722
|
}
|
|
692
723
|
|
|
693
724
|
__featureEvent(type, feature)
|
|
@@ -976,6 +1007,7 @@ export class UserInteractions
|
|
|
976
1007
|
togglePaths()
|
|
977
1008
|
//===========
|
|
978
1009
|
{
|
|
1010
|
+
console.log('Depracated API function called: togglePaths()')
|
|
979
1011
|
if (this._disabledPathFeatures){
|
|
980
1012
|
this.enablePathFeatures_(true, this._pathways.allFeatureIds());
|
|
981
1013
|
this._disabledPathFeatures = false;
|
|
@@ -990,29 +1022,6 @@ export class UserInteractions
|
|
|
990
1022
|
this.enablePathFeatures_(enable, this._pathways.typeFeatureIds(pathType));
|
|
991
1023
|
}
|
|
992
1024
|
|
|
993
|
-
showPaths(pathTypes, enable=true)
|
|
994
|
-
//===============================
|
|
995
|
-
{
|
|
996
|
-
// Disable/enable all paths except those with `pathTypes`
|
|
997
|
-
if (Array.isArray(pathTypes)) {
|
|
998
|
-
for (const pathType of pathways.PATH_TYPES) {
|
|
999
|
-
if (pathTypes.indexOf(pathType.type) >= 0) {
|
|
1000
|
-
this.enablePath(pathType.type, enable)
|
|
1001
|
-
} else {
|
|
1002
|
-
this.enablePath(pathType.type, !enable)
|
|
1003
|
-
}
|
|
1004
|
-
}
|
|
1005
|
-
} else {
|
|
1006
|
-
for (const pathType of pathways.PATH_TYPES) {
|
|
1007
|
-
if (pathType.type === pathTypes) {
|
|
1008
|
-
this.enablePath(pathType.type, enable)
|
|
1009
|
-
} else {
|
|
1010
|
-
this.enablePath(pathType.type, !enable)
|
|
1011
|
-
}
|
|
1012
|
-
}
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1015
|
-
|
|
1016
1025
|
pathwaysFeatureIds(externalIds)
|
|
1017
1026
|
//=============================
|
|
1018
1027
|
{
|
|
@@ -1028,52 +1037,57 @@ export class UserInteractions
|
|
|
1028
1037
|
return this._pathways.nodePathModels(nodeId);
|
|
1029
1038
|
}
|
|
1030
1039
|
|
|
1031
|
-
|
|
1032
|
-
|
|
1040
|
+
enableCentrelines(show=true)
|
|
1041
|
+
//==========================
|
|
1042
|
+
{
|
|
1043
|
+
this.enablePath('centreline', show);
|
|
1044
|
+
this._layerManager.setPaint({showCentrelines: show});
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
enableSckanPath(sckanState, enable=true)
|
|
1048
|
+
//======================================
|
|
1033
1049
|
{
|
|
1034
|
-
this._layerManager.
|
|
1050
|
+
this._layerManager.enableSckanPath(sckanState, enable);
|
|
1035
1051
|
}
|
|
1036
1052
|
|
|
1037
1053
|
//==============================================================================
|
|
1038
1054
|
|
|
1039
|
-
//
|
|
1055
|
+
// Marker handling
|
|
1040
1056
|
|
|
1041
|
-
|
|
1042
|
-
//======================================
|
|
1057
|
+
__markerPosition(featureId, annotation)
|
|
1043
1058
|
{
|
|
1044
|
-
if (this.
|
|
1045
|
-
return this.
|
|
1059
|
+
if (this.__markerPositions.has(featureId)) {
|
|
1060
|
+
return this.__markerPositions.get(featureId);
|
|
1046
1061
|
}
|
|
1047
|
-
let position = annotation.centroid;
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1062
|
+
let position = annotation.markerPosition || annotation.centroid;
|
|
1063
|
+
if (position === null || position == undefined) {
|
|
1064
|
+
// Find where to place a label or popup on a feature
|
|
1065
|
+
const features = this._map.querySourceFeatures(VECTOR_TILES_SOURCE, {
|
|
1066
|
+
'sourceLayer': this._flatmap.options.separateLayers
|
|
1067
|
+
? `${annotation['layer']}_${annotation['tile-layer']}`
|
|
1068
|
+
: annotation['tile-layer'],
|
|
1069
|
+
'filter': [
|
|
1070
|
+
'all',
|
|
1071
|
+
[ '==', ['id'], parseInt(featureId) ],
|
|
1072
|
+
[ '==', ['geometry-type'], 'Polygon' ]
|
|
1073
|
+
]
|
|
1074
|
+
});
|
|
1075
|
+
if (features.length > 0) {
|
|
1076
|
+
const feature = features[0];
|
|
1077
|
+
const polygon = feature.geometry.coordinates;
|
|
1078
|
+
// Rough heuristic. Area is in km^2; below appears to be good enough.
|
|
1079
|
+
const precision = ('area' in feature.properties)
|
|
1080
|
+
? Math.sqrt(feature.properties.area)/500000
|
|
1081
|
+
: 0.1;
|
|
1082
|
+
position = polylabel(polygon, precision);
|
|
1083
|
+
}
|
|
1066
1084
|
}
|
|
1067
|
-
this.
|
|
1085
|
+
this.__markerPositions.set(featureId, position);
|
|
1068
1086
|
return position;
|
|
1069
1087
|
}
|
|
1070
1088
|
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
// Marker handling
|
|
1074
|
-
|
|
1075
|
-
addMarker(anatomicalId, markerType='')
|
|
1076
|
-
//====================================
|
|
1089
|
+
addMarker(anatomicalId, htmlElement=null)
|
|
1090
|
+
//=======================================
|
|
1077
1091
|
{
|
|
1078
1092
|
const featureIds = this._flatmap.modelFeatureIds(anatomicalId);
|
|
1079
1093
|
let markerId = -1;
|
|
@@ -1089,19 +1103,19 @@ export class UserInteractions
|
|
|
1089
1103
|
markerId = this.__lastMarkerId;
|
|
1090
1104
|
}
|
|
1091
1105
|
|
|
1106
|
+
// MapLibre dynamically sets a transform on marker elements so in
|
|
1107
|
+
// order to apply a scale transform we need to create marker icons
|
|
1108
|
+
// inside the marker container <div>.
|
|
1109
|
+
const markerHTML = htmlElement ? new maplibre.Marker({element: htmlElement})
|
|
1110
|
+
: new maplibre.Marker();
|
|
1111
|
+
|
|
1092
1112
|
const markerElement = document.createElement('div');
|
|
1093
1113
|
const markerIcon = document.createElement('div');
|
|
1094
|
-
|
|
1095
|
-
markerIcon.innerHTML = this._simulationMarkerHTML;
|
|
1096
|
-
} else {
|
|
1097
|
-
markerIcon.innerHTML = this._defaultMarkerHTML;
|
|
1098
|
-
}
|
|
1114
|
+
markerIcon.innerHTML = markerHTML.getElement().innerHTML;
|
|
1099
1115
|
markerIcon.className = 'flatmap-marker';
|
|
1100
1116
|
markerElement.appendChild(markerIcon);
|
|
1101
1117
|
|
|
1102
|
-
const markerPosition = (annotation
|
|
1103
|
-
? this.__centralPosition(featureId, annotation)
|
|
1104
|
-
: annotation.centroid;
|
|
1118
|
+
const markerPosition = this.__markerPosition(featureId, annotation);
|
|
1105
1119
|
const marker = new maplibre.Marker(markerElement)
|
|
1106
1120
|
.setLngLat(markerPosition)
|
|
1107
1121
|
.addTo(this._map);
|
package/src/layers.js
CHANGED
|
@@ -93,14 +93,12 @@ class MapStylingLayers
|
|
|
93
93
|
: sourceLayer;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
setPaint(options)
|
|
97
97
|
{
|
|
98
|
-
|
|
99
98
|
}
|
|
100
99
|
|
|
101
100
|
setFilter(options)
|
|
102
101
|
{
|
|
103
|
-
|
|
104
102
|
}
|
|
105
103
|
}
|
|
106
104
|
|
|
@@ -124,6 +122,8 @@ class MapFeatureLayers extends MapStylingLayers
|
|
|
124
122
|
this.__addStyleLayer(style.FeatureDashLineLayer);
|
|
125
123
|
this.__addStyleLayer(style.FeatureLineLayer);
|
|
126
124
|
this.__addStyleLayer(style.FeatureBorderLayer);
|
|
125
|
+
this.__addStyleLayer(style.CentrelineNodeFillLayer);
|
|
126
|
+
this.__addStyleLayer(style.CentrelineNodeBorderLayer);
|
|
127
127
|
}
|
|
128
128
|
this.__addPathwayStyleLayers();
|
|
129
129
|
if (vectorFeatures) {
|
|
@@ -134,9 +134,9 @@ class MapFeatureLayers extends MapStylingLayers
|
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
-
// Make sure our
|
|
137
|
+
// Make sure our paint options are set properly, in particular raster layer visibility
|
|
138
138
|
|
|
139
|
-
this.
|
|
139
|
+
this.setPaint(this.__layerOptions);
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
__addStyleLayer(styleClass, sourceLayer=FEATURES_LAYER)
|
|
@@ -167,8 +167,8 @@ class MapFeatureLayers extends MapStylingLayers
|
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
-
|
|
171
|
-
|
|
170
|
+
setPaint(options)
|
|
171
|
+
//===============
|
|
172
172
|
{
|
|
173
173
|
for (const layer of this.__layers) {
|
|
174
174
|
const paintStyle = layer.paintStyle(options, true);
|
|
@@ -208,8 +208,8 @@ class MapRasterLayers extends MapStylingLayers
|
|
|
208
208
|
this.__map.addLayer(styleLayer.style(this.__layerOptions));
|
|
209
209
|
this.__layers.push(styleLayer);
|
|
210
210
|
}
|
|
211
|
-
// Make sure our
|
|
212
|
-
this.
|
|
211
|
+
// Make sure our paint options are set properly, in particular raster layer visibility
|
|
212
|
+
this.setPaint(this.__layerOptions);
|
|
213
213
|
}
|
|
214
214
|
|
|
215
215
|
addLayer(layer)
|
|
@@ -220,12 +220,12 @@ class MapRasterLayers extends MapStylingLayers
|
|
|
220
220
|
this.__map.addLayer(rasterLayer.style(this.__layerOptions));
|
|
221
221
|
this.__layers.push(rasterLayer);
|
|
222
222
|
}
|
|
223
|
-
// Make sure our
|
|
224
|
-
this.
|
|
223
|
+
// Make sure our paint options are set properly, in particular raster layer visibility
|
|
224
|
+
this.setPaint(this.__layerOptions);
|
|
225
225
|
}
|
|
226
226
|
|
|
227
|
-
|
|
228
|
-
|
|
227
|
+
setPaint(options)
|
|
228
|
+
//===============
|
|
229
229
|
{
|
|
230
230
|
const coloured = !('colour' in options) || options.colour;
|
|
231
231
|
for (const layer of this.__layers) {
|
|
@@ -250,7 +250,7 @@ export class LayerManager
|
|
|
250
250
|
colour: true,
|
|
251
251
|
outline: true,
|
|
252
252
|
sckan: 'valid'
|
|
253
|
-
})
|
|
253
|
+
});
|
|
254
254
|
const backgroundLayer = new style.BackgroundLayer();
|
|
255
255
|
if ('background' in flatmap.options) {
|
|
256
256
|
this.__map.addLayer(backgroundLayer.style(flatmap.options.background));
|
|
@@ -305,19 +305,19 @@ export class LayerManager
|
|
|
305
305
|
this.__layerOptions.activeRasterLayer = enable;
|
|
306
306
|
for (const mapLayer of this.__mapLayers.values()) {
|
|
307
307
|
if (mapLayer.id !== RASTER_LAYERS_ID) {
|
|
308
|
-
mapLayer.
|
|
308
|
+
mapLayer.setPaint(this.__layerOptions);
|
|
309
309
|
}
|
|
310
310
|
}
|
|
311
311
|
}
|
|
312
312
|
}
|
|
313
313
|
}
|
|
314
314
|
|
|
315
|
-
|
|
316
|
-
|
|
315
|
+
setPaint(options={})
|
|
316
|
+
//==================
|
|
317
317
|
{
|
|
318
318
|
this.__layerOptions = utils.setDefaults(options, this.__layerOptions);
|
|
319
319
|
for (const mapLayer of this.__mapLayers.values()) {
|
|
320
|
-
mapLayer.
|
|
320
|
+
mapLayer.setPaint(this.__layerOptions);
|
|
321
321
|
}
|
|
322
322
|
}
|
|
323
323
|
|
|
@@ -329,6 +329,31 @@ export class LayerManager
|
|
|
329
329
|
mapLayer.setFilter(this.__layerOptions);
|
|
330
330
|
}
|
|
331
331
|
}
|
|
332
|
+
|
|
333
|
+
enableSckanPath(sckanState, enable=true)
|
|
334
|
+
//======================================
|
|
335
|
+
{
|
|
336
|
+
const currentState = this.__layerOptions.sckan;
|
|
337
|
+
const validEnabled = ['valid', 'all'].indexOf(currentState) >= 0;
|
|
338
|
+
const invalidEnabled = ['invalid', 'all'].indexOf(currentState) >= 0;
|
|
339
|
+
let newState = sckanState.toLowerCase();
|
|
340
|
+
if (newState === 'valid') {
|
|
341
|
+
if (enable && !validEnabled) {
|
|
342
|
+
newState = invalidEnabled ? 'all' : 'valid';
|
|
343
|
+
} else if (!enable && validEnabled) {
|
|
344
|
+
newState = invalidEnabled ? 'invalid' : 'none';
|
|
345
|
+
}
|
|
346
|
+
} else if (newState === 'invalid') {
|
|
347
|
+
if (enable && !invalidEnabled) {
|
|
348
|
+
newState = validEnabled ? 'all' : 'invalid';
|
|
349
|
+
} else if (!enable && invalidEnabled) {
|
|
350
|
+
newState = validEnabled ? 'valid' : 'none';
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
if (newState !== this.__layerOptions.sckan) {
|
|
354
|
+
this.setFilter({sckan: newState});
|
|
355
|
+
}
|
|
356
|
+
}
|
|
332
357
|
}
|
|
333
358
|
|
|
334
359
|
//==============================================================================
|
package/src/pathways.js
CHANGED
|
@@ -26,19 +26,20 @@ export const PATHWAYS_LAYER = 'pathways';
|
|
|
26
26
|
|
|
27
27
|
//==============================================================================
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
const PATH_TYPES = [
|
|
30
30
|
{ type: "cns", label: "CNS", colour: "#9B1FC1"},
|
|
31
31
|
{ type: "intracardiac", label: "Local circuit neuron", colour: "#F19E38"},
|
|
32
32
|
{ type: "para-pre", label: "Parasympathetic pre-ganglionic", colour: "#3F8F4A"},
|
|
33
|
-
{ type: "para-post", label: "Parasympathetic post-ganglionic", colour: "#3F8F4A"},
|
|
33
|
+
{ type: "para-post", label: "Parasympathetic post-ganglionic", colour: "#3F8F4A", dashed: true},
|
|
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
|
-
{ type: "symp-post", label: "Sympathetic post-ganglionic", colour: "#EA3423"},
|
|
37
|
+
{ type: "symp-post", label: "Sympathetic post-ganglionic", colour: "#EA3423", dashed: true},
|
|
38
38
|
{ type: "other", label: "Other neuron type", colour: "#888"},
|
|
39
39
|
{ type: "arterial", label: "Arterial blood vessel", colour: "#F00"},
|
|
40
40
|
{ type: "venous", label: "Venous blood vessel", colour: "#2F6EBA"},
|
|
41
|
-
{ type: "centreline", label: "Nerve centrelines", colour: "#
|
|
41
|
+
{ type: "centreline", label: "Nerve centrelines", colour: "#CCC", enabled: false},
|
|
42
|
+
{ type: "error", label: "Paths with errors or warnings", colour: "#FF0"}
|
|
42
43
|
];
|
|
43
44
|
|
|
44
45
|
export const PATH_STYLE_RULES =
|
|
@@ -142,16 +143,28 @@ export class Pathways
|
|
|
142
143
|
this.__typePaths['other'].push(...paths);
|
|
143
144
|
}
|
|
144
145
|
}
|
|
146
|
+
// Nerve centrelines are a special case with their own controls
|
|
147
|
+
this.__haveCentrelines = false;
|
|
145
148
|
}
|
|
146
149
|
|
|
147
|
-
get
|
|
148
|
-
|
|
150
|
+
get haveCentrelines()
|
|
151
|
+
//===================
|
|
152
|
+
{
|
|
153
|
+
return this.__haveCentrelines;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
pathTypes()
|
|
157
|
+
//=========
|
|
149
158
|
{
|
|
150
159
|
const pathTypes = [];
|
|
151
160
|
for (const pathType of PATH_TYPES) {
|
|
152
161
|
if (pathType.type in this.__typePaths
|
|
153
162
|
&& this.__typePaths[pathType.type].length > 0) {
|
|
154
|
-
|
|
163
|
+
if (pathType.type === 'centreline') {
|
|
164
|
+
this.__haveCentrelines = true;
|
|
165
|
+
} else {
|
|
166
|
+
pathTypes.push(pathType);
|
|
167
|
+
}
|
|
155
168
|
}
|
|
156
169
|
}
|
|
157
170
|
return pathTypes;
|
package/src/styling.js
CHANGED
|
@@ -162,7 +162,8 @@ export class FeatureFillLayer extends VectorStyleLayer
|
|
|
162
162
|
'filter': [
|
|
163
163
|
'all',
|
|
164
164
|
['==', '$type', 'Polygon'],
|
|
165
|
-
['!=', 'models', 'UBERON:0013702']
|
|
165
|
+
['!=', 'models', 'UBERON:0013702'],
|
|
166
|
+
['!has', 'node']
|
|
166
167
|
],
|
|
167
168
|
'layout': {
|
|
168
169
|
'fill-sort-key': ['get', 'scale']
|
|
@@ -244,7 +245,8 @@ export class FeatureBorderLayer extends VectorStyleLayer
|
|
|
244
245
|
'type': 'line',
|
|
245
246
|
'filter': [
|
|
246
247
|
'all',
|
|
247
|
-
['==', '$type', 'Polygon']
|
|
248
|
+
['==', '$type', 'Polygon'],
|
|
249
|
+
['!has', 'node']
|
|
248
250
|
],
|
|
249
251
|
'paint': this.paintStyle(options)
|
|
250
252
|
};
|
|
@@ -287,8 +289,8 @@ export class FeatureLineLayer extends VectorStyleLayer
|
|
|
287
289
|
'line-color': [
|
|
288
290
|
'case',
|
|
289
291
|
['boolean', ['feature-state', 'selected'], false], '#0F0',
|
|
290
|
-
['has', 'colour'], ['get', 'colour'],
|
|
291
292
|
['boolean', ['feature-state', 'active'], false], coloured ? '#888' : '#CCC',
|
|
293
|
+
['has', 'colour'], ['get', 'colour'],
|
|
292
294
|
['==', ['get', 'type'], 'network'], '#AFA202',
|
|
293
295
|
options.authoring ? '#C44' : '#444'
|
|
294
296
|
],
|
|
@@ -415,7 +417,6 @@ export class PathLineLayer extends VectorStyleLayer
|
|
|
415
417
|
['boolean', ['feature-state', 'selected'], false], '#0F0',
|
|
416
418
|
['boolean', ['feature-state', 'hidden'], false], '#CCC',
|
|
417
419
|
['==', ['get', 'type'], 'bezier'], 'red',
|
|
418
|
-
['has', 'error'], '#FFFE0E',
|
|
419
420
|
['==', ['get', 'kind'], 'unknown'], '#888',
|
|
420
421
|
...PATH_STYLE_RULES,
|
|
421
422
|
'#888'
|
|
@@ -424,6 +425,7 @@ export class PathLineLayer extends VectorStyleLayer
|
|
|
424
425
|
'case',
|
|
425
426
|
['boolean', ['feature-state', 'hidden'], false], 0.05,
|
|
426
427
|
['==', ['get', 'type'], 'bezier'], 1.0,
|
|
428
|
+
['==', ['get', 'kind'], 'error'], 1.0,
|
|
427
429
|
['boolean', ['get', 'invisible'], false], 0.001,
|
|
428
430
|
['boolean', ['feature-state', 'selected'], false], 1.0,
|
|
429
431
|
['boolean', ['feature-state', 'active'], false], 1.0,
|
|
@@ -434,7 +436,7 @@ export class PathLineLayer extends VectorStyleLayer
|
|
|
434
436
|
'width', ["*", [
|
|
435
437
|
'case',
|
|
436
438
|
['==', ['get', 'type'], 'bezier'], 0.1,
|
|
437
|
-
['
|
|
439
|
+
['==', ['get', 'kind'], 'error'], 1,
|
|
438
440
|
['==', ['get', 'kind'], 'unknown'], 1,
|
|
439
441
|
['boolean', ['get', 'invisible'], false], 0.1,
|
|
440
442
|
['boolean', ['feature-state', 'selected'], false], 0.6,
|
|
@@ -568,6 +570,76 @@ export class CentrelineTrackLayer extends CentrelineLayer
|
|
|
568
570
|
|
|
569
571
|
//==============================================================================
|
|
570
572
|
|
|
573
|
+
export class CentrelineNodeFillLayer extends VectorStyleLayer
|
|
574
|
+
{
|
|
575
|
+
constructor(id, sourceLayer)
|
|
576
|
+
{
|
|
577
|
+
super(id, 'node-fill', sourceLayer);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
paintStyle(options={}, changes=false)
|
|
581
|
+
{
|
|
582
|
+
const showNodes = options.showCentrelines || false;
|
|
583
|
+
const paintStyle = {
|
|
584
|
+
'fill-color': '#AFA202',
|
|
585
|
+
'fill-opacity': showNodes ? 0.7 : 0.01
|
|
586
|
+
}
|
|
587
|
+
return super.changedPaintStyle(paintStyle, changes);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
style(options)
|
|
591
|
+
{
|
|
592
|
+
return {
|
|
593
|
+
...super.style(),
|
|
594
|
+
'type': 'fill',
|
|
595
|
+
'filter': [
|
|
596
|
+
'all',
|
|
597
|
+
['==', '$type', 'Polygon'],
|
|
598
|
+
['has', 'node']
|
|
599
|
+
],
|
|
600
|
+
'layout': {
|
|
601
|
+
'fill-sort-key': ['get', 'scale']
|
|
602
|
+
},
|
|
603
|
+
'paint': this.paintStyle(options)
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
export class CentrelineNodeBorderLayer extends VectorStyleLayer
|
|
609
|
+
{
|
|
610
|
+
constructor(id, sourceLayer)
|
|
611
|
+
{
|
|
612
|
+
super(id, 'node-border', sourceLayer);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
paintStyle(options={}, changes=false)
|
|
616
|
+
{
|
|
617
|
+
const showNodes = options.showCentrelines || false;
|
|
618
|
+
const paintStyle = {
|
|
619
|
+
'line-color': '#AFA202',
|
|
620
|
+
'line-opacity': showNodes ? 0.7 : 0.01,
|
|
621
|
+
'line-width': 0.5
|
|
622
|
+
}
|
|
623
|
+
return super.changedPaintStyle(paintStyle, changes);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
style(options)
|
|
627
|
+
{
|
|
628
|
+
return {
|
|
629
|
+
...super.style(),
|
|
630
|
+
'type': 'line',
|
|
631
|
+
'filter': [
|
|
632
|
+
'all',
|
|
633
|
+
['==', '$type', 'Polygon'],
|
|
634
|
+
['has', 'node']
|
|
635
|
+
],
|
|
636
|
+
'paint': this.paintStyle(options)
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
//==============================================================================
|
|
642
|
+
|
|
571
643
|
export class FeatureNerveLayer extends VectorStyleLayer
|
|
572
644
|
{
|
|
573
645
|
constructor(id, sourceLayer)
|
|
@@ -202,40 +202,13 @@ li.flatmap-contextmenu-item:hover {
|
|
|
202
202
|
label[for=path-all-paths] {
|
|
203
203
|
font-weight: bold;
|
|
204
204
|
}
|
|
205
|
-
.nerve-cns {
|
|
206
|
-
background: #9B1FC1;
|
|
207
|
-
}
|
|
208
|
-
.nerve-intracardiac {
|
|
209
|
-
background: #F19E38;
|
|
210
|
-
}
|
|
211
|
-
.nerve-other {
|
|
212
|
-
background: #888;
|
|
213
|
-
}
|
|
214
|
-
.nerve-para-pre {
|
|
215
|
-
background: #3F8F4A;
|
|
216
|
-
}
|
|
217
|
-
.nerve-para-post {
|
|
218
|
-
background: repeating-linear-gradient(to right,#3F8F4A 0,#3F8F4A 6px,transparent 6px,transparent 9px)
|
|
219
|
-
}
|
|
220
|
-
.nerve-sensory {
|
|
221
|
-
background: #2A62F6;
|
|
222
|
-
}
|
|
223
|
-
.nerve-somatic {
|
|
224
|
-
background: #98561D;
|
|
225
|
-
}
|
|
226
|
-
.nerve-symp-pre {
|
|
227
|
-
background: #EA3423;
|
|
228
|
-
}
|
|
229
|
-
.nerve-symp-post {
|
|
230
|
-
background: repeating-linear-gradient(to right,#EA3423 0,#EA3423 6px,transparent 6px,transparent 9px)
|
|
231
|
-
}
|
|
232
205
|
|
|
233
206
|
/* Layer control */
|
|
234
207
|
|
|
235
|
-
|
|
208
|
+
.flatmap-control {
|
|
236
209
|
text-align: right;
|
|
237
210
|
}
|
|
238
|
-
.flatmap-
|
|
211
|
+
.flatmap-control-grid {
|
|
239
212
|
margin-top: 10px;
|
|
240
213
|
display: grid;
|
|
241
214
|
grid-template-columns: 3.8fr 0.2fr;
|
|
@@ -250,7 +223,7 @@ label[for=path-all-paths] {
|
|
|
250
223
|
padding: 4px;
|
|
251
224
|
opacity: 0.8;
|
|
252
225
|
}
|
|
253
|
-
.flatmap-
|
|
226
|
+
.flatmap-control-grid input {
|
|
254
227
|
height: 1.1em;
|
|
255
228
|
}
|
|
256
229
|
label[for=layer-all-layers] {
|