@abi-software/flatmap-viewer 2.3.0-b.2 → 2.3.1-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/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.3.0-b.2``
41
+ * ``npm install @abi-software/flatmap-viewer@2.3.1-b.1``
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.3.0-b.2",
3
+ "version": "2.3.1-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",
package/src/annotation.js CHANGED
@@ -82,9 +82,10 @@ function stopSpinner(panel)
82
82
 
83
83
  export class Annotator
84
84
  {
85
- constructor(flatmap)
85
+ constructor(flatmap, ui)
86
86
  {
87
87
  this.__flatmap = flatmap;
88
+ this.__ui = ui;
88
89
  this.__haveAnnotation = false;
89
90
  this.__user = undefined;
90
91
  this.__savedStatusMessage = '';
@@ -444,20 +445,134 @@ export class Annotator
444
445
  });
445
446
  }
446
447
 
447
- annotate(feature, closedCallback)
448
- //===============================
448
+ __chooseFeatureProperties(features, callback)
449
+ //===========================================
449
450
  {
450
- this.__currentFeatureId = feature.properties['id']
451
+ this.__ui.selectFeature(features[0].id);
452
+
453
+ // Feature chooser is only for multiple selections
454
+ if (features.length === 1
455
+ || features[0].properties['cd-class'] !== 'celldl:Connection'
456
+ || (features.length === 2
457
+ && features[1].properties['cd-class'] !== 'celldl:Connection')) {
458
+ callback(features[0].properties);
459
+ return;
460
+ }
461
+ const featureList = [];
462
+ const featureProperties = new Map();
463
+ const featureSeen = new Set();
464
+ let selected = 'selected'; // Select the first entry
465
+ for (const feature of features) {
466
+ if (feature.properties['cd-class'] !== 'celldl:Connection'
467
+ || feature.properties['id'] == undefined
468
+ || featureSeen.has(feature.properties['id'])) {
469
+ continue;
470
+ }
471
+ const mapFeature = this.__ui.mapFeature(feature.id);
472
+ const annotated = (mapFeature !== undefined)
473
+ ? this.__ui._map.getFeatureState(mapFeature)['annotated']
474
+ : false;
475
+ let label = '';
476
+ if (feature.properties.models) {
477
+ label = ` -- ${feature.properties.label.split('\n')[0]} (${feature.properties.models})`;
478
+ }
479
+ featureList.push(`<option value="${feature.id}" ${selected}>${annotated ? '*' : '&nbsp;'} ${feature.properties.id} -- ${feature.properties.kind}${label}</option>`);
480
+ featureProperties.set(+feature.id, feature.properties);
481
+ featureSeen.add(feature.properties['id']);
482
+ selected = '';
483
+ }
484
+ if (featureList.length == 0) {
485
+ callback(undefined);
486
+ return;
487
+ } else if (featureList.length == 1) {
488
+ callback(featureProperties.values().next().value);
489
+ return;
490
+ }
491
+ const panelContent = `
492
+ <div id="annotation-feature-selection">
493
+ <div>
494
+ <label for="annotation-feature-selector">Select feature:</label>
495
+ <select id="annotation-feature-selector" size="${Math.min(featureList.length, 7)}">
496
+ ${featureList.join('\n')}
497
+ </select>
498
+ </div>
499
+ <div id="annotation-feature-buttons">
500
+ <input id="annotation-feature-cancel" type="button" value="Cancel"/>
501
+ <input id="annotation-feature-annotate" type="button" value="Annotate"/>
502
+ </div>
503
+ </div>`;
504
+ this.__panel = jsPanel.create({
505
+ theme: 'light',
506
+ border: '2px solid #080',
507
+ borderRadius: '.5rem',
508
+ panelSize: 'auto auto',
509
+ position: 'left-top',
510
+ content: panelContent,
511
+ data: features[0].properties,
512
+ closeOnEscape: true,
513
+ closeOnBackdrop: false,
514
+ headerTitle: 'Select feature to annotate',
515
+ headerControls: 'closeonly xs',
516
+ callback: ((panel) => {
517
+ const selector = document.getElementById('annotation-feature-selector');
518
+ selector.onchange = (e) => {
519
+ if (e.target.value !== '') {
520
+ this.__ui.unselectFeatures();
521
+ this.__ui.selectFeature(e.target.value);
522
+ this.__panel.options.data = featureProperties.get(+e.target.value);
523
+ }
524
+ };
525
+ selector.ondblclick = (e) => {
526
+ if (e.target.value !== '') {
527
+ const properties = this.__panel.options.data;
528
+ this.__panel.close();
529
+ callback(properties);
530
+ }
531
+ }
532
+ selector.focus();
533
+ document.getElementById('annotation-feature-cancel')
534
+ .onclick = (e) => {
535
+ this.__panel.close();
536
+ callback(undefined);
537
+ };
538
+ document.getElementById('annotation-feature-annotate')
539
+ .onclick = (e) => {
540
+ const properties = this.__panel.options.data;
541
+ this.__panel.close();
542
+ callback(properties);
543
+ };
544
+ }).bind(this)
545
+ });
546
+ document.addEventListener('jspanelcloseduser', (e) => { callback(undefined) }, false);
547
+ }
548
+
549
+ annotate(features, closedCallback)
550
+ //================================
551
+ {
552
+ // provide a list of features so dialog needs to first provide selection list
553
+ // and highlight current one as user scrolls...
451
554
 
555
+ this.__chooseFeatureProperties(features, (featureProperties) => {
556
+ if (featureProperties) {
557
+ this.__annotateFeature(featureProperties, closedCallback);
558
+ } else {
559
+ closedCallback();
560
+ }
561
+ });
562
+ }
563
+
564
+ __annotateFeature(featureProperties, callback)
565
+ //============================================
566
+ {
567
+ this.__currentFeatureId = featureProperties['id'];
452
568
  if (this.__currentFeatureId === undefined) {
453
- closedCallback();
569
+ callback();
454
570
  return;
455
571
  }
456
-
457
572
  const panelContent = [];
458
573
  panelContent.push('<div id="flatmap-annotation-panel">');
459
574
  panelContent.push(' <div id="flatmap-annotation-feature">');
460
- panelContent.push(...this.__featureHtml(feature.properties));
575
+ panelContent.push(...this.__featureHtml(featureProperties));
461
576
  panelContent.push(' </div>');
462
577
  panelContent.push(' <form id="flatmap-annotation-form"></form>');
463
578
  panelContent.push(' <div id="flatmap-annotation-existing"></div>');
@@ -517,7 +632,7 @@ export class Annotator
517
632
  });
518
633
 
519
634
  // should we warn if unsaved changes when closing??
520
- document.addEventListener('jspanelclosed', closedCallback, false);
635
+ document.addEventListener('jspanelclosed', callback, false);
521
636
  }
522
637
 
523
638
  async annotated_features()
@@ -260,20 +260,6 @@ class FlatMap
260
260
  this._map.zoomOut();
261
261
  }
262
262
 
263
- /**
264
- * Toggle the visibility of paths on the map.zoomIn
265
- *
266
- * * If some paths are hidden then all paths are made visible.
267
- * * If all paths are visible then they are all hidden.
268
- */
269
- togglePaths()
270
- //===========
271
- {
272
- if (this._userInteractions !== null) {
273
- this._userInteractions.togglePaths();
274
- }
275
- }
276
-
277
263
  /**
278
264
  * @returns {Array.<{type: string, label: string, colour: string}>} an array of objects giving the path types
279
265
  * present in the map along with their
@@ -104,8 +104,6 @@ export class UserInteractions
104
104
  this._infoControl = null;
105
105
  this._tooltip = null;
106
106
 
107
- this._disabledPathFeatures = false;
108
-
109
107
  this._inQuery = false;
110
108
  this._modal = false;
111
109
 
@@ -247,7 +245,7 @@ export class UserInteractions
247
245
  {
248
246
  // Add annotation capability
249
247
 
250
- this.__annotator = new Annotator(this._flatmap);
248
+ this.__annotator = new Annotator(this._flatmap, this);
251
249
  const annotated_features = await this.__annotator.annotated_features();
252
250
 
253
251
  // Flag features that have annotations
@@ -357,6 +355,14 @@ export class UserInteractions
357
355
  }
358
356
  }
359
357
 
358
+ __featureEnabled(feature)
359
+ //=======================
360
+ {
361
+ const state = this._map.getFeatureState(feature);
362
+ return (state !== undefined
363
+ && (!('hidden' in state) || !state.hidden));
364
+ }
365
+
360
366
  mapFeature(featureId)
361
367
  //===================
362
368
  {
@@ -380,8 +386,8 @@ export class UserInteractions
380
386
  return this._selectedFeatureIds.has(+featureId);
381
387
  }
382
388
 
383
- selectFeature_(featureId, dim=true)
384
- //=================================
389
+ selectFeature(featureId, dim=true)
390
+ //================================
385
391
  {
386
392
  featureId = +featureId; // Ensure numeric
387
393
  if (this._selectedFeatureIds.size === 0) {
@@ -398,8 +404,8 @@ export class UserInteractions
398
404
  }
399
405
  }
400
406
 
401
- unselectFeature_(featureId)
402
- //=========================
407
+ unselectFeature(featureId)
408
+ //========================
403
409
  {
404
410
  featureId = +featureId; // Ensure numeric
405
411
  if (this._selectedFeatureIds.has(featureId)) {
@@ -419,8 +425,8 @@ export class UserInteractions
419
425
  }
420
426
  }
421
427
 
422
- __unselectFeatures()
423
- //==================
428
+ unselectFeatures()
429
+ //================
424
430
  {
425
431
  for (const featureId of this._selectedFeatureIds.keys()) {
426
432
  const feature = this.mapFeature(featureId);
@@ -433,7 +439,7 @@ export class UserInteractions
433
439
  }
434
440
 
435
441
  __activateFeature(feature)
436
- //=======================
442
+ //========================
437
443
  {
438
444
  if (feature !== undefined) {
439
445
  this._map.setFeatureState(feature, { active: true });
@@ -499,16 +505,15 @@ export class UserInteractions
499
505
  //=====
500
506
  {
501
507
  this.__clearModal();
502
- this.clearActiveMarker_();
503
- this.__unselectFeatures();
504
- this.enablePathFeatures_(true, this._pathways.allFeatureIds());
505
- this._disabledPathFeatures = false;
508
+ this.__clearActiveMarker();
509
+ this.unselectFeatures();
510
+ this.__enablePathFeatures(this._pathways.allFeatureIds(), true);
506
511
  }
507
512
 
508
513
  clearSearchResults(reset=true)
509
514
  //============================
510
515
  {
511
- this.__unselectFeatures();
516
+ this.unselectFeatures();
512
517
  }
513
518
 
514
519
  /**
@@ -544,14 +549,14 @@ export class UserInteractions
544
549
  //========================
545
550
  {
546
551
  if (featureIds.length) {
547
- this.__unselectFeatures();
552
+ this.unselectFeatures();
548
553
  for (const featureId of featureIds) {
549
554
  const annotation = this._flatmap.annotation(featureId);
550
555
  if (annotation) {
551
- this.selectFeature_(featureId);
556
+ this.selectFeature(featureId);
552
557
  if ('type' in annotation && annotation.type.startsWith('line')) {
553
558
  for (const pathFeatureId of this._pathways.lineFeatureIds([featureId])) {
554
- this.selectFeature_(pathFeatureId);
559
+ this.selectFeature(pathFeatureId);
555
560
  }
556
561
  }
557
562
  }
@@ -588,7 +593,7 @@ export class UserInteractions
588
593
  const highlight = (options.highlight === true);
589
594
  if (featureIds.length) {
590
595
  this.unhighlightFeatures_();
591
- if (select) this.__unselectFeatures();
596
+ if (select) this.unselectFeatures();
592
597
  let bbox = null;
593
598
  if (options.noZoomIn) {
594
599
  const bounds = this._map.getBounds().toArray();
@@ -598,7 +603,7 @@ export class UserInteractions
598
603
  const annotation = this._flatmap.annotation(featureId);
599
604
  if (annotation) {
600
605
  if (select) {
601
- this.selectFeature_(featureId);
606
+ this.selectFeature(featureId);
602
607
  } else if (highlight) {
603
608
  this.highlightFeature_(featureId);
604
609
  }
@@ -606,7 +611,7 @@ export class UserInteractions
606
611
  if ('type' in annotation && annotation.type.startsWith('line')) {
607
612
  for (const pathFeatureId of this._pathways.lineFeatureIds([featureId])) {
608
613
  if (select) {
609
- this.selectFeature_(pathFeatureId);
614
+ this.selectFeature(pathFeatureId);
610
615
  } else if (highlight) {
611
616
  this.highlightFeature_(pathFeatureId);
612
617
  }
@@ -639,8 +644,8 @@ export class UserInteractions
639
644
 
640
645
  // Highlight the feature
641
646
 
642
- this.__unselectFeatures();
643
- this.selectFeature_(featureId);
647
+ this.unselectFeatures();
648
+ this.selectFeature(featureId);
644
649
 
645
650
  // Find the pop-up's postion
646
651
 
@@ -675,7 +680,7 @@ export class UserInteractions
675
680
  //============
676
681
  {
677
682
  this.__clearModal();
678
- this.__unselectFeatures();
683
+ this.unselectFeatures();
679
684
  }
680
685
 
681
686
  removeTooltip_()
@@ -785,7 +790,7 @@ export class UserInteractions
785
790
 
786
791
  // Get all the features at the current point
787
792
  const features = this._map.queryRenderedFeatures(event.point)
788
- .filter(feature => this.__enabledFeature(feature));
793
+ .filter(feature => this.__featureEnabled(feature));
789
794
  if (features.length === 0) {
790
795
  this._lastFeatureMouseEntered = null;
791
796
  this._lastFeatureModelsMouse = null;
@@ -931,27 +936,27 @@ export class UserInteractions
931
936
  break;
932
937
  }
933
938
  }
934
- this.__unselectFeatures();
939
+ this.unselectFeatures();
935
940
  if (selecting) {
936
941
  for (const feature of this._activeFeatures) {
937
- this.selectFeature_(feature.id, dim);
942
+ this.selectFeature(feature.id, dim);
938
943
  }
939
944
  }
940
945
  } else {
941
946
  const clickedSelected = this.featureSelected_(clickedFeatureId);
942
947
  for (const feature of this._activeFeatures) {
943
948
  if (clickedSelected) {
944
- this.unselectFeature_(feature.id);
949
+ this.unselectFeature(feature.id);
945
950
  } else {
946
- this.selectFeature_(feature.id, dim);
951
+ this.selectFeature(feature.id, dim);
947
952
  }
948
953
  }
949
954
  }
950
955
  }
951
956
  }
952
957
 
953
- __annotationEvent(feature)
954
- //========================
958
+ __annotationEvent(features)
959
+ //=========================
955
960
  {
956
961
  if (!this.__annotator) {
957
962
  return;
@@ -962,15 +967,12 @@ export class UserInteractions
962
967
  // Remove any tooltip
963
968
  this.removeTooltip_();
964
969
 
965
- // Select the feature
966
- this.selectFeature_(feature.id);
967
-
968
970
  // Don't respond to mouse events while the dialog is open
969
971
  this.setModal_();
970
972
 
971
973
  // The annotation dialog...
972
- this.__annotator.annotate(feature, e => {
973
- this.__unselectFeatures();
974
+ this.__annotator.annotate(features, () => {
975
+ this.unselectFeatures();
974
976
  this.__clearModal();
975
977
  });
976
978
  }
@@ -982,25 +984,25 @@ export class UserInteractions
982
984
  return;
983
985
  }
984
986
 
985
- this.clearActiveMarker_();
987
+ this.__clearActiveMarker();
986
988
  const clickedFeatures = this._map.queryRenderedFeatures(event.point)
987
- .filter(feature => this.__enabledFeature(feature));
989
+ .filter(feature => this.__featureEnabled(feature));
988
990
  if (clickedFeatures.length == 0){
989
- this.__unselectFeatures();
991
+ this.unselectFeatures();
990
992
  return;
991
993
  }
992
- const clickedFeature = clickedFeatures[0];
993
994
  const originalEvent = event.originalEvent;
994
995
  if (originalEvent.altKey) {
995
- this.__annotationEvent(clickedFeature);
996
+ this.__annotationEvent(clickedFeatures);
996
997
  return;
997
998
  }
998
999
 
1000
+ const clickedFeature = clickedFeatures[0];
999
1001
  this.selectionEvent_(originalEvent, clickedFeature);
1000
1002
  if (this._modal) {
1001
1003
  // Remove tooltip, reset active features, etc
1002
1004
  this.__resetFeatureDisplay();
1003
- this.__unselectFeatures();
1005
+ this.unselectFeatures();
1004
1006
  this.__clearModal();
1005
1007
  } else if (clickedFeature !== undefined) {
1006
1008
  this.__lastClickLngLat = event.lngLat;
@@ -1026,54 +1028,18 @@ export class UserInteractions
1026
1028
  }
1027
1029
  }
1028
1030
 
1029
- __enabledFeature(feature)
1030
- //=======================
1031
- {
1032
- const state = this._map.getFeatureState(feature);
1033
- return (state !== undefined
1034
- && (!('hidden' in state) || !state.hidden));
1035
- }
1036
-
1037
- enablePaths_(enable, event)
1038
- //=========================
1039
- {
1040
- const nodeId = event.target.getAttribute('featureId');
1041
- this.enablePathFeatures_(enable, this._pathways.pathFeatureIds(nodeId));
1042
- this.__clearModal();
1043
- }
1044
-
1045
- enablePathFeatures_(enable, featureIds)
1046
- //=====================================
1031
+ __enablePathFeatures(featureIds, enable)
1032
+ //======================================
1047
1033
  {
1048
1034
  for (const featureId of featureIds) {
1049
- const feature = this.mapFeature(featureId);
1050
- if (feature !== undefined) {
1051
- if (enable) {
1052
- this._map.removeFeatureState(feature, 'hidden');
1053
- } else {
1054
- this._map.setFeatureState(feature, { 'hidden': true });
1055
- this._disabledPathFeatures = true;
1056
- }
1057
- }
1058
- }
1059
- }
1060
-
1061
- togglePaths()
1062
- //===========
1063
- {
1064
- console.log('Depracated API function called: togglePaths()')
1065
- if (this._disabledPathFeatures){
1066
- this.enablePathFeatures_(true, this._pathways.allFeatureIds());
1067
- this._disabledPathFeatures = false;
1068
- } else {
1069
- this.enablePathFeatures_(false, this._pathways.allFeatureIds());
1035
+ this.enableFeature(this.mapFeature(featureId), enable);
1070
1036
  }
1071
1037
  }
1072
1038
 
1073
1039
  enablePath(pathType, enable=true)
1074
1040
  //===============================
1075
1041
  {
1076
- this.enablePathFeatures_(enable, this._pathways.typeFeatureIds(pathType));
1042
+ this.__enablePathFeatures(this._pathways.typeFeatureIds(pathType), enable);
1077
1043
  }
1078
1044
 
1079
1045
  pathwaysFeatureIds(externalIds)
@@ -1278,7 +1244,7 @@ export class UserInteractions
1278
1244
  event.stopPropagation();
1279
1245
  }
1280
1246
 
1281
- clearActiveMarker_()
1247
+ __clearActiveMarker()
1282
1248
  //==================
1283
1249
  {
1284
1250
  if (this.__activeMarker !== null) {
@@ -1292,7 +1258,7 @@ export class UserInteractions
1292
1258
  {
1293
1259
  const marker = this.__activeMarker;
1294
1260
  if (markerId !== this.__markerIdByMarker.get(marker)) {
1295
- this.clearActiveMarker_();
1261
+ this.__clearActiveMarker();
1296
1262
  return false;
1297
1263
  }
1298
1264
 
@@ -1311,7 +1277,7 @@ export class UserInteractions
1311
1277
  element.innerHTML = content;
1312
1278
  }
1313
1279
 
1314
- element.addEventListener('click', e => this.clearActiveMarker_());
1280
+ element.addEventListener('click', e => this.__clearActiveMarker());
1315
1281
 
1316
1282
  this._tooltip = new maplibre.Popup({
1317
1283
  closeButton: false,
package/src/pathways.js CHANGED
@@ -37,8 +37,8 @@ const PATH_TYPES = [
37
37
  { type: "symp-pre", label: "Sympathetic pre-ganglionic", colour: "#EA3423"},
38
38
  { type: "symp-post", label: "Sympathetic post-ganglionic", colour: "#EA3423", dashed: true},
39
39
  { type: "other", label: "Other neuron type", colour: "#888"},
40
- { type: "arterial", label: "Arterial blood vessel", colour: "#F00"},
41
- { type: "venous", label: "Venous blood vessel", colour: "#2F6EBA"},
40
+ { type: "arterial", label: "Arterial blood vessel", colour: "#F00", enabled: false},
41
+ { type: "venous", label: "Venous blood vessel", colour: "#2F6EBA", enabled: false},
42
42
  { type: "centreline", label: "Nerve centrelines", colour: "#CCC", enabled: false},
43
43
  { type: "error", label: "Paths with errors or warnings", colour: "#FF0"}
44
44
  ];
package/src/styling.js CHANGED
@@ -33,6 +33,7 @@ import {PATH_STYLE_RULES} from './pathways.js';
33
33
  const COLOUR_ACTIVE = 'blue';
34
34
  const COLOUR_ANNOTATED = '#0F0';
35
35
  const COLOUR_SELECTED = '#0F0';
36
+ const COLOUR_HIDDEN = '#D8D8D8';
36
37
 
37
38
  const CENTRELINE_ACTIVE = '#444';
38
39
  const CENTRELINE_COLOUR = '#CCC';
@@ -135,14 +136,13 @@ export class FeatureFillLayer extends VectorStyleLayer
135
136
 
136
137
  paintStyle(options, changes=false)
137
138
  {
138
- const ghostColour = '#D8D8D8'; // Function of BG colour?
139
139
  const coloured = !('colour' in options) || options.colour;
140
140
  const dimmed = 'dimmed' in options && options.dimmed;
141
141
  const paintStyle = {
142
142
  'fill-color': [
143
143
  'case',
144
144
  ['boolean', ['feature-state', 'selected'], false], COLOUR_SELECTED,
145
- ['boolean', ['feature-state', 'hidden'], false], ghostColour,
145
+ ['boolean', ['feature-state', 'hidden'], false], COLOUR_HIDDEN,
146
146
  ['has', 'colour'], ['get', 'colour'],
147
147
  ['boolean', ['feature-state', 'active'], false], coloured ? '#D88' : '#CCC',
148
148
  'white' // background colour? body colour ??
@@ -195,6 +195,8 @@ export class FeatureBorderLayer extends VectorStyleLayer
195
195
  const dimmed = 'dimmed' in options && options.dimmed;
196
196
  const activeRasterLayer = 'activeRasterLayer' in options && options.activeRasterLayer;
197
197
  const lineColour = [ 'case' ];
198
+ lineColour.push(['boolean', ['feature-state', 'hidden'], false]);
199
+ lineColour.push(COLOUR_HIDDEN);
198
200
  lineColour.push(['boolean', ['feature-state', 'selected'], false]);
199
201
  lineColour.push(FEATURE_SELECTED_BORDER);
200
202
  if (coloured && outlined) {
@@ -298,6 +300,7 @@ export class FeatureLineLayer extends VectorStyleLayer
298
300
  const paintStyle = {
299
301
  'line-color': [
300
302
  'case',
303
+ ['boolean', ['feature-state', 'hidden'], false], COLOUR_HIDDEN,
301
304
  ['boolean', ['feature-state', 'selected'], false], COLOUR_SELECTED,
302
305
  ['boolean', ['feature-state', 'active'], false], coloured ? '#888' : '#CCC',
303
306
  ['has', 'colour'], ['get', 'colour'],
@@ -306,6 +309,7 @@ export class FeatureLineLayer extends VectorStyleLayer
306
309
  ],
307
310
  'line-opacity': [
308
311
  'case',
312
+ ['boolean', ['feature-state', 'hidden'], false], 0.01,
309
313
  ['boolean', ['feature-state', 'selected'], false], 1.0,
310
314
  ['has', 'colour'], 1.0,
311
315
  ['boolean', ['feature-state', 'active'], false], 1.0,
@@ -499,7 +503,7 @@ export class PathLineLayer extends VectorStyleLayer
499
503
  'line-color': [
500
504
  'case',
501
505
  ['boolean', ['feature-state', 'selected'], false], COLOUR_SELECTED,
502
- ['boolean', ['feature-state', 'hidden'], false], '#CCC',
506
+ ['boolean', ['feature-state', 'hidden'], false], COLOUR_HIDDEN,
503
507
  ['==', ['get', 'type'], 'bezier'], 'red',
504
508
  ['==', ['get', 'kind'], 'unknown'], '#888',
505
509
  ...PATH_STYLE_RULES,
@@ -507,7 +511,7 @@ export class PathLineLayer extends VectorStyleLayer
507
511
  ],
508
512
  'line-opacity': [
509
513
  'case',
510
- ['boolean', ['feature-state', 'hidden'], false], 0.05,
514
+ ['boolean', ['feature-state', 'hidden'], false], 0.01,
511
515
  ['==', ['get', 'type'], 'bezier'], 1.0,
512
516
  ['==', ['get', 'kind'], 'error'], 1.0,
513
517
  ['boolean', ['get', 'invisible'], false], 0.001,
@@ -523,7 +527,7 @@ export class PathLineLayer extends VectorStyleLayer
523
527
  ['==', ['get', 'kind'], 'error'], 1,
524
528
  ['==', ['get', 'kind'], 'unknown'], 1,
525
529
  ['boolean', ['get', 'invisible'], false], 0.1,
526
- ['boolean', ['feature-state', 'selected'], false], 0.6,
530
+ ['boolean', ['feature-state', 'selected'], false], 2.0,
527
531
  ['boolean', ['feature-state', 'active'], false], 0.9,
528
532
  0.6
529
533
  ],
@@ -745,7 +749,7 @@ export class FeatureNerveLayer extends VectorStyleLayer
745
749
  'paint': {
746
750
  'line-color': [
747
751
  'case',
748
- ['boolean', ['feature-state', 'hidden'], false], '#CCC',
752
+ ['boolean', ['feature-state', 'hidden'], false], COLOUR_HIDDEN,
749
753
  ['boolean', ['feature-state', 'active'], false], NERVE_ACTIVE,
750
754
  ['boolean', ['feature-state', 'selected'], false], NERVE_SELECTED,
751
755
  '#888'
package/src/systems.js CHANGED
@@ -35,7 +35,8 @@ export class SystemsManager
35
35
  id: ann.name.replaceAll(' ', '_'),
36
36
  colour: ann.colour,
37
37
  featureIds: [ ann.featureId ],
38
- enabled: enabled
38
+ enabled: enabled,
39
+ pathIds: ('path-ids' in ann) ? ann['path-ids'] : []
39
40
  });
40
41
  }
41
42
  for (const childId of ann['children']) {
@@ -86,6 +87,11 @@ export class SystemsManager
86
87
  }
87
88
  }
88
89
  system.enabled = enable;
90
+
91
+ // Enable/disable all paths associated with the system
92
+ for (const pathId of system.pathIds) {
93
+ this.__ui.enableFeature(this.__ui.mapFeature(pathId), enable);
94
+ }
89
95
  }
90
96
  }
91
97
  }
@@ -342,6 +342,18 @@ label[for=layer-all-layers] {
342
342
  background-color: #BBB;
343
343
  }
344
344
 
345
+ #annotation-feature-selection
346
+ {
347
+ display: flex;
348
+ }
349
+
350
+ #annotation-feature-buttons
351
+ {
352
+ float: right;
353
+ padding-top: 10px;
354
+ padding-bottom: 10px;
355
+ }
356
+
345
357
  .jsPanel-title {
346
358
  font-size: 1.4em !important;
347
359
  font-weight: bold !important;