@abi-software/flatmap-viewer 2.2.7 → 2.3.0-a.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.rst 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.7``
41
+ * ``npm install @abi-software/flatmap-viewer@2.2.8``
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.2.7",
3
+ "version": "2.3.0-a.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/controls.js CHANGED
@@ -22,7 +22,6 @@ limitations under the License.
22
22
 
23
23
  //==============================================================================
24
24
 
25
- import * as pathways from './pathways.js';
26
25
 
27
26
  //==============================================================================
28
27
 
@@ -77,10 +76,11 @@ export class NavigationControl
77
76
 
78
77
  export class PathControl
79
78
  {
80
- constructor(flatmap)
79
+ constructor(flatmap, pathways)
81
80
  {
82
81
  this._flatmap = flatmap;
83
82
  this._map = undefined;
83
+ this.__pathTypes = pathways.pathTypes;
84
84
  }
85
85
 
86
86
  getDefaultPosition()
@@ -103,11 +103,11 @@ export class PathControl
103
103
 
104
104
  const innerHTML = [];
105
105
  innerHTML.push(`<label for="path-all-paths">ALL PATHS:</label><input id="path-all-paths" type="checkbox" checked/><div class="nerve-line"></div>`);
106
- for (const path of pathways.PATH_TYPES) {
106
+ for (const path of this.__pathTypes) {
107
107
  innerHTML.push(`<label for="path-${path.type}">${path.label}</label><input id="path-${path.type}" type="checkbox" checked/><div class="nerve-line nerve-${path.type}"></div>`);
108
108
  }
109
109
  this._legend.innerHTML = innerHTML.join('\n');
110
- this.__checkedCount = pathways.PATH_TYPES.length;
110
+ this.__checkedCount = this.__pathTypes.length;
111
111
  this.__halfCount = Math.trunc(this.__checkedCount/2);
112
112
 
113
113
  this._button = document.createElement('button');
@@ -150,11 +150,11 @@ export class PathControl
150
150
  event.target.indeterminate = false;
151
151
  }
152
152
  if (event.target.checked) {
153
- this.__checkedCount = pathways.PATH_TYPES.length;
153
+ this.__checkedCount = this.__pathTypes.length;
154
154
  } else {
155
155
  this.__checkedCount = 0;
156
156
  }
157
- for (const path of pathways.PATH_TYPES) {
157
+ for (const path of this.__pathTypes) {
158
158
  const pathCheckbox = document.getElementById(`path-${path.type}`);
159
159
  if (pathCheckbox) {
160
160
  pathCheckbox.checked = event.target.checked;
@@ -173,7 +173,7 @@ export class PathControl
173
173
  if (this.__checkedCount === 0) {
174
174
  allPathsCheckbox.checked = false;
175
175
  allPathsCheckbox.indeterminate = false;
176
- } else if (this.__checkedCount === pathways.PATH_TYPES.length) {
176
+ } else if (this.__checkedCount === this.__pathTypes.length) {
177
177
  allPathsCheckbox.checked = true;
178
178
  allPathsCheckbox.indeterminate = false;
179
179
  } else {
@@ -58,7 +58,8 @@ class FlatMap
58
58
  constructor(container, mapBaseUrl, mapDescription, resolve)
59
59
  {
60
60
  this._baseUrl = mapBaseUrl;
61
- this._id = mapDescription.id;
61
+ this.__id = mapDescription.id;
62
+ this.__uuid = mapDescription.uuid;
62
63
  this._details = mapDescription.details;
63
64
  this._created = mapDescription.created;
64
65
  this.__taxon = mapDescription.taxon;
@@ -75,7 +76,7 @@ class FlatMap
75
76
  this.__idToAnnotation = new Map();
76
77
  this.__datasetToFeatureIds = new Map();
77
78
  this.__modelToFeatureIds = new Map();
78
- this.___mapIdToFeatureIds = new Map();
79
+ this.__mapSourceToFeatureIds = new Map();
79
80
  for (const [featureId, annotation] of Object.entries(mapDescription.annotations)) {
80
81
  this.__addAnnotation(featureId, annotation);
81
82
  this.__searchIndex.indexMetadata(featureId, annotation);
@@ -280,7 +281,9 @@ class FlatMap
280
281
  pathTypes()
281
282
  //=========
282
283
  {
283
- return pathways.PATH_TYPES;
284
+ if (this._userInteractions !== null) {
285
+ return this._userInteractions.pathTypes();
286
+ }
284
287
  }
285
288
 
286
289
  /**
@@ -354,7 +357,7 @@ class FlatMap
354
357
  //==============
355
358
  {
356
359
  if (url.startsWith('/')) {
357
- return `${this._baseUrl}flatmap/${this._id}${url}`; // We don't want embedded `{` and `}` characters escaped
360
+ return `${this._baseUrl}flatmap/${this.__uuid}${url}`; // We don't want embedded `{` and `}` characters escaped
358
361
  } else if (!url.startsWith('http://') && !url.startsWith('https://')) {
359
362
  console.log(`Invalid URL (${url}) in map's sources`);
360
363
  }
@@ -402,7 +405,7 @@ class FlatMap
402
405
  get id()
403
406
  //======
404
407
  {
405
- return this._id;
408
+ return this.__uuid;
406
409
  }
407
410
 
408
411
  /**
@@ -424,7 +427,7 @@ class FlatMap
424
427
  get uniqueId()
425
428
  //============
426
429
  {
427
- return `${this._id}-${this._mapNumber}`;
430
+ return `${this.__uuid}-${this._mapNumber}`;
428
431
  }
429
432
 
430
433
  get activeLayerNames()
@@ -466,7 +469,7 @@ class FlatMap
466
469
  this.__idToAnnotation.set(featureId, ann);
467
470
  this.__updateFeatureIdMap('dataset', this.__datasetToFeatureIds, ann);
468
471
  this.__updateFeatureIdMap('models', this.__modelToFeatureIds, ann);
469
- this.__updateFeatureIdMap('source', this.___mapIdToFeatureIds, ann);
472
+ this.__updateFeatureIdMap('source', this.__mapSourceToFeatureIds, ann);
470
473
  }
471
474
 
472
475
  modelFeatureIds(anatomicalId)
@@ -490,7 +493,7 @@ class FlatMap
490
493
  if (featureIds.length == 0) {
491
494
  // We couldn't find a feature by anatomical id, so check dataset and source
492
495
  featureIds.extend(this.__datasetToFeatureIds.get(anatomicalIds));
493
- featureIds.extend(this.___mapIdToFeatureIds.get(anatomicalIds));
496
+ featureIds.extend(this.__mapSourceToFeatureIds.get(anatomicalIds));
494
497
  }
495
498
  if (featureIds.length == 0 && this._userInteractions !== null) {
496
499
  // We still haven't found a feature, so check connectivity
@@ -634,7 +637,7 @@ class FlatMap
634
637
  return {
635
638
  taxon: this.__taxon,
636
639
  biologicalSex: this.__biologicalSex,
637
- id: this._id
640
+ uuid: this.__uuid
638
641
  };
639
642
  }
640
643
 
@@ -1086,7 +1089,8 @@ export class MapManager
1086
1089
  latestMap_(identifier)
1087
1090
  //====================
1088
1091
  {
1089
- const mapDescribes = ('uuid' in identifier) ? identifier.uuid
1092
+ const mapDescribes = (identifier.constructor.name === "String") ? identifier
1093
+ : ('uuid' in identifier) ? identifier.uuid
1090
1094
  : ('taxon' in identifier) ? identifier.taxon
1091
1095
  : null;
1092
1096
  if (mapDescribes === null) {
@@ -1300,7 +1304,8 @@ export class MapManager
1300
1304
  this._mapNumber += 1;
1301
1305
  const flatmap = new FlatMap(container, this._mapServer.url(),
1302
1306
  {
1303
- id: mapId,
1307
+ id: map,
1308
+ uuid: mapId,
1304
1309
  details: mapIndex,
1305
1310
  taxon: map.taxon,
1306
1311
  biologicalSex: map.biologicalSex,
package/src/info.js CHANGED
@@ -247,13 +247,6 @@ export class InfoControl
247
247
  }
248
248
 
249
249
  const htmlList = [];
250
-
251
- if (this._flatmap.options.showPosition) {
252
- const position = location;
253
- htmlList.push(`<span class="info-name">Position:</span>`);
254
- htmlList.push(`<span class="info-value">(${position.lng}, ${position.lat})</span>`);
255
- }
256
-
257
250
  for (const values of displayValues.values()) {
258
251
  for (const prop of displayedProperties) {
259
252
  if (prop in values) {
@@ -151,8 +151,7 @@ export class UserInteractions
151
151
 
152
152
  if (flatmap.options.pathControls) {
153
153
  // Add controls to manage our pathways
154
-
155
- this._map.addControl(new PathControl(flatmap));
154
+ this._map.addControl(new PathControl(flatmap, this._pathways));
156
155
  }
157
156
 
158
157
  // Manage our layers
@@ -164,10 +163,8 @@ export class UserInteractions
164
163
 
165
164
  for (const [id, ann] of flatmap.annotations) {
166
165
  const feature = this.mapFeature_(id);
167
- this._map.setFeatureState(feature, { 'annotated': true });
168
- if ('error' in ann) {
169
- this._map.setFeatureState(feature, { 'annotation-error': true });
170
- console.log(`Annotation error, ${ann.layer}: ${ann.error} (${ann.text})`);
166
+ if (feature !== undefined) {
167
+ this._map.setFeatureState(feature, { 'annotated': true });
171
168
  }
172
169
  }
173
170
 
@@ -251,13 +248,15 @@ export class UserInteractions
251
248
  //====================
252
249
  {
253
250
  const ann = this._flatmap.annotation(featureId);
254
- return {
255
- id: featureId,
256
- source: VECTOR_TILES_SOURCE,
257
- sourceLayer: this._flatmap.options.separateLayers
258
- ? `${ann['layer']}_${ann['tile-layer']}`
259
- : ann['tile-layer']
260
- };
251
+ if (ann !== undefined) {
252
+ return {
253
+ id: featureId,
254
+ source: VECTOR_TILES_SOURCE,
255
+ sourceLayer: this._flatmap.options.separateLayers
256
+ ? `${ann['layer']}_${ann['tile-layer']}`
257
+ : ann['tile-layer']
258
+ };
259
+ }
261
260
  }
262
261
 
263
262
  featureSelected_(featureId)
@@ -277,8 +276,10 @@ export class UserInteractions
277
276
  this._selectedFeatureIds.set(featureId, this._selectedFeatureIds.get(featureId) + 1);
278
277
  } else {
279
278
  const feature = this.mapFeature_(featureId);
280
- this._map.setFeatureState(feature, { 'selected': true });
281
- this._selectedFeatureIds.set(featureId, 1);
279
+ if (feature !== undefined) {
280
+ this._map.setFeatureState(feature, { 'selected': true });
281
+ this._selectedFeatureIds.set(featureId, 1);
282
+ }
282
283
  }
283
284
  }
284
285
 
@@ -292,8 +293,10 @@ export class UserInteractions
292
293
  this._selectedFeatureIds.set(featureId, references - 1);
293
294
  } else {
294
295
  const feature = this.mapFeature_(featureId);
295
- this._map.removeFeatureState(feature, 'selected');
296
- this._selectedFeatureIds.delete(+featureId);
296
+ if (feature !== undefined) {
297
+ this._map.removeFeatureState(feature, 'selected');
298
+ this._selectedFeatureIds.delete(+featureId);
299
+ }
297
300
  }
298
301
  }
299
302
  if (this._selectedFeatureIds.size === 0) {
@@ -306,7 +309,9 @@ export class UserInteractions
306
309
  {
307
310
  for (const featureId of this._selectedFeatureIds.keys()) {
308
311
  const feature = this.mapFeature_(featureId);
309
- this._map.removeFeatureState(feature, 'selected');
312
+ if (feature !== undefined) {
313
+ this._map.removeFeatureState(feature, 'selected');
314
+ }
310
315
  }
311
316
  this._selectedFeatureIds.clear();
312
317
  this._layerManager.setColour({...this.__colourOptions, dimmed: false});
@@ -318,7 +323,8 @@ export class UserInteractions
318
323
  // Get the features covering the event's point that are in the active layers
319
324
 
320
325
  return this._map.queryRenderedFeatures(event.point).filter(f => {
321
- return (this.activeLayerNames.indexOf(f.sourceLayer) >= 0)
326
+ return (this.__enabledFeature(f)
327
+ && this.activeLayerNames.indexOf(f.sourceLayer) >= 0)
322
328
  && ('featureId' in f.properties);
323
329
  }
324
330
  );
@@ -327,8 +333,10 @@ export class UserInteractions
327
333
  __activateFeature(feature)
328
334
  //=======================
329
335
  {
330
- this._map.setFeatureState(feature, { active: true });
331
- this._activeFeatures.push(feature);
336
+ if (feature !== undefined) {
337
+ this._map.setFeatureState(feature, { active: true });
338
+ this._activeFeatures.push(feature);
339
+ }
332
340
  }
333
341
 
334
342
  resetActiveFeatures_()
@@ -617,17 +625,14 @@ export class UserInteractions
617
625
  const tooltips = [];
618
626
  for (const lineFeature of lineFeatures) {
619
627
  const properties = lineFeature.properties;
620
- if (this.__enabledFeature(lineFeature)) {
621
- const properties = lineFeature.properties;
622
- if ('label' in properties
623
- && (!('tooltip' in properties) || properties.tooltip)
624
- && !('labelled' in properties)) {
625
- let tooltip = '';
626
- const label = properties.label;
627
- const cleanLabel = (label.substr(0, 1).toUpperCase() + label.substr(1)).replaceAll("\n", "<br/>");
628
- if (!tooltips.includes(cleanLabel)) {
629
- tooltips.push(cleanLabel);
630
- }
628
+ if ('label' in properties
629
+ && (!('tooltip' in properties) || properties.tooltip)
630
+ && !('labelled' in properties)) {
631
+ let tooltip = '';
632
+ const label = properties.label;
633
+ const cleanLabel = (label.substr(0, 1).toUpperCase() + label.substr(1)).replaceAll("\n", "<br/>");
634
+ if (!tooltips.includes(cleanLabel)) {
635
+ tooltips.push(cleanLabel);
631
636
  }
632
637
  }
633
638
  }
@@ -662,13 +667,11 @@ export class UserInteractions
662
667
  __featureEvent(type, feature)
663
668
  //===========================
664
669
  {
665
- if (this.__enabledFeature(feature)) {
666
- if (feature.sourceLayer === PATHWAYS_LAYER) { // I suspect this is never true as source layer
667
- // names are like `neural_routes_pathways`
668
- return this._flatmap.featureEvent(type, this._pathways.pathProperties(feature));
669
- } else if ('properties' in feature) {
670
- return this._flatmap.featureEvent(type, feature.properties);
671
- }
670
+ if (feature.sourceLayer === PATHWAYS_LAYER) { // I suspect this is never true as source layer
671
+ // names are like `neural_routes_pathways`
672
+ return this._flatmap.featureEvent(type, this._pathways.pathProperties(feature));
673
+ } else if ('properties' in feature) {
674
+ return this._flatmap.featureEvent(type, feature.properties);
672
675
  }
673
676
  return false;
674
677
  }
@@ -704,7 +707,8 @@ export class UserInteractions
704
707
  }
705
708
 
706
709
  // Get all the features at the current point
707
- const features = this._map.queryRenderedFeatures(event.point);
710
+ const features = this._map.queryRenderedFeatures(event.point)
711
+ .filter(feature => this.__enabledFeature(feature));
708
712
  if (features.length === 0) {
709
713
  this._lastFeatureMouseEntered = null;
710
714
  this._lastFeatureModelsMouse = null;
@@ -742,17 +746,13 @@ export class UserInteractions
742
746
  && feature.properties.type.startsWith('line')) ));
743
747
  if (lineFeatures.length > 0) {
744
748
  tooltip = this.lineTooltip_(lineFeatures);
745
- const enabledFeatures = lineFeatures.filter(feature => this.__enabledFeature(feature));
746
- if (enabledFeatures.length > 0) {
747
- tooltip = this.lineTooltip_(enabledFeatures);
748
- for (const lineFeature of enabledFeatures) {
749
- const lineFeatureId = +lineFeature.properties.featureId; // Ensure numeric
750
- this.__activateFeature(lineFeature);
751
- const lineIds = new Set(enabledFeatures.map(f => f.properties.featureId));
752
- for (const featureId of this._pathways.lineFeatureIds(lineIds)) {
753
- if (+featureId !== lineFeatureId) {
754
- this.__activateFeature(this.mapFeature_(featureId));
755
- }
749
+ for (const lineFeature of lineFeatures) {
750
+ const lineFeatureId = +lineFeature.properties.featureId; // Ensure numeric
751
+ this.__activateFeature(lineFeature);
752
+ const lineIds = new Set(lineFeatures.map(f => f.properties.featureId));
753
+ for (const featureId of this._pathways.lineFeatureIds(lineIds)) {
754
+ if (+featureId !== lineFeatureId) {
755
+ this.__activateFeature(this.mapFeature_(featureId));
756
756
  }
757
757
  }
758
758
  }
@@ -874,7 +874,12 @@ export class UserInteractions
874
874
  //================
875
875
  {
876
876
  this.clearActiveMarker_();
877
- const clickedFeature = this._map.queryRenderedFeatures(event.point)[0];
877
+ const clickedFeatures = this._map.queryRenderedFeatures(event.point)
878
+ .filter(feature => this.__enabledFeature(feature));
879
+ if (clickedFeatures.length == 0){
880
+ return;
881
+ }
882
+ const clickedFeature = clickedFeatures[0];
878
883
  const originalEvent = event.originalEvent;
879
884
  if (clickedFeature === undefined || this._activeFeatures.length === 1) {
880
885
  this.selectionEvent_(originalEvent, clickedFeature);
@@ -920,7 +925,8 @@ export class UserInteractions
920
925
  //=======================
921
926
  {
922
927
  const state = this._map.getFeatureState(feature);
923
- return true;
928
+ return (state !== undefined
929
+ && (!('hidden' in state) || !state.hidden));
924
930
  }
925
931
 
926
932
  enablePaths_(enable, event)
@@ -937,11 +943,13 @@ export class UserInteractions
937
943
  {
938
944
  for (const featureId of featureIds) {
939
945
  const feature = this.mapFeature_(featureId);
940
- if (enable) {
941
- this._map.removeFeatureState(feature, 'hidden');
942
- } else {
943
- this._map.setFeatureState(feature, { 'hidden': true });
944
- this._disabledPathFeatures = true;
946
+ if (feature !== undefined) {
947
+ if (enable) {
948
+ this._map.removeFeatureState(feature, 'hidden');
949
+ } else {
950
+ this._map.setFeatureState(feature, { 'hidden': true });
951
+ this._disabledPathFeatures = true;
952
+ }
945
953
  }
946
954
  }
947
955
  }
@@ -957,6 +965,12 @@ export class UserInteractions
957
965
  }
958
966
  }
959
967
 
968
+ pathTypes()
969
+ //=========
970
+ {
971
+ return this._pathways.pathTypes;
972
+ }
973
+
960
974
  enablePath(pathType, enable=true)
961
975
  //===============================
962
976
  {
@@ -1149,14 +1163,15 @@ export class UserInteractions
1149
1163
  const annotation = this.__annotationByMarkerId.get(markerId);
1150
1164
  // The marker's feature
1151
1165
  const feature = this.mapFeature_(annotation.featureId);
1152
- if (event.type === 'mouseenter') {
1153
- // Highlight on mouse enter
1154
- this.resetActiveFeatures_();
1155
- this.__activateFeature(feature);
1156
- } else {
1157
- this.selectionEvent_(event, feature)
1166
+ if (feature !== undefined) {
1167
+ if (event.type === 'mouseenter') {
1168
+ // Highlight on mouse enter
1169
+ this.resetActiveFeatures_();
1170
+ this.__activateFeature(feature);
1171
+ } else {
1172
+ this.selectionEvent_(event, feature)
1173
+ }
1158
1174
  }
1159
-
1160
1175
  // Show tooltip
1161
1176
  const html = this.tooltipHtml_(annotation, true);
1162
1177
  this.__showToolTip(html, marker.getLngLat());
package/src/pathways.js CHANGED
@@ -26,16 +26,18 @@ export const PATHWAYS_LAYER = 'pathways';
26
26
 
27
27
  //==============================================================================
28
28
 
29
- export const PATH_TYPES = [
30
- { type: "cns", label: "CNS", colour: "#9B1FC1"},
31
- { type: "lcn", label: "Local circuit neuron", colour: "#F19E38"},
32
- { type: "para-pre", label: "Parasympathetic pre-ganglionic", colour: "#3F8F4A"},
33
- { type: "para-post", label: "Parasympathetic post-ganglionic", colour: "#3F8F4A"},
34
- { type: "sensory", label: "Sensory (afferent) neuron", colour: "#2A62F6"},
35
- { type: "somatic", label: "Somatic lower motor", colour: "#98561D"},
36
- { type: "symp-pre", label: "Sympathetic pre-ganglionic", colour: "#EA3423"},
37
- { type: "symp-post", label: "Sympathetic post-ganglionic", colour: "#EA3423"}
38
- ];
29
+ const PATH_TYPES = {
30
+ "cns": {label: "CNS", colour: "#9B1FC1"},
31
+ "lcn": {label: "Local circuit neuron", colour: "#F19E38"},
32
+ "para-pre": {label: "Parasympathetic pre-ganglionic", colour: "#3F8F4A"},
33
+ "para-post": {label: "Parasympathetic post-ganglionic", colour: "#3F8F4A"},
34
+ "sensory": {label: "Sensory (afferent) neuron", colour: "#2A62F6"},
35
+ "somatic": {label: "Somatic lower motor", colour: "#98561D"},
36
+ "symp-pre": {label: "Sympathetic pre-ganglionic", colour: "#EA3423"},
37
+ "symp-post": {label: "Sympathetic post-ganglionic", colour: "#EA3423"},
38
+ "other": {label: "Other neuron type", colour: "#888"},
39
+ "centreline": {label: "Nerve centrelines", colour: "#CC0"}
40
+ };
39
41
 
40
42
  //==============================================================================
41
43
 
@@ -120,7 +122,27 @@ export class Pathways
120
122
  }
121
123
  this._allFeatureIds = featureIds;
122
124
 
123
- this._typePaths = flatmap.pathways['type-paths']; // nerve-type: [pathIds]
125
+ // Map unknown path types to ``other``
126
+ this.__typePaths = {};
127
+ this.__typePaths['other'] = [];
128
+ this.__knownPathTypes = [];
129
+ for (const [pathType, paths] of Object.entries(flatmap.pathways['type-paths'])) {
130
+ if (pathType in PATH_TYPES && pathType !== 'other') {
131
+ this.__typePaths[pathType] = paths;
132
+ this.__knownPathTypes.push({'type': pathType, ...PATH_TYPES[pathType]});
133
+ } else {
134
+ this.__typePaths['other'].push(...paths);
135
+ }
136
+ }
137
+ if ('other' in this.__typePaths) {
138
+ this.__knownPathTypes.push({'type': 'other', ...PATH_TYPES['other']});
139
+ }
140
+ }
141
+
142
+ get pathTypes()
143
+ //=============
144
+ {
145
+ return this.__knownPathTypes;
124
146
  }
125
147
 
126
148
  addPathsToFeatureSet_(paths, featureSet)
@@ -253,8 +275,8 @@ export class Pathways
253
275
  //======================
254
276
  {
255
277
  const featureIds = new Set();
256
- if (pathType in this._typePaths) {
257
- this.addPathsToFeatureSet_(this._typePaths[pathType], featureIds);
278
+ if (pathType in this.__typePaths) {
279
+ this.addPathsToFeatureSet_(this.__typePaths[pathType], featureIds);
258
280
  }
259
281
  return featureIds;
260
282
  }
package/src/styling.js CHANGED
@@ -346,7 +346,8 @@ export class PathLineLayer extends VectorStyleLayer
346
346
  [
347
347
  'any',
348
348
  ['==', 'type', 'bezier'],
349
- ['==', 'type', `line`]
349
+ ['==', 'kind', 'centreline'],
350
+ ['==', 'type', 'line']
350
351
  ];
351
352
  this.__dashed = dashed;
352
353
  }
@@ -369,22 +370,25 @@ export class PathLineLayer extends VectorStyleLayer
369
370
  ['==', ['get', 'kind'], 'sensory'], '#2A62F6',
370
371
  ['==', ['get', 'kind'], 'symp-post'], '#EA3423',
371
372
  ['==', ['get', 'kind'], 'symp-pre'], '#EA3423',
373
+ ['==', ['get', 'kind'], 'centreline'], '#CC0',
372
374
  '#888'
373
375
  ],
374
376
  'line-opacity': [
375
377
  'case',
378
+ ['boolean', ['feature-state', 'hidden'], false], 0.1,
376
379
  ['==', ['get', 'type'], 'bezier'], 1.0,
377
380
  ['boolean', ['get', 'invisible'], false], 0.001,
378
381
  ['boolean', ['feature-state', 'selected'], false], 1.0,
379
382
  ['boolean', ['feature-state', 'active'], false], 0.8,
380
- ['boolean', ['feature-state', 'hidden'], false], 0.1,
381
- dimmed ? 0.1 : 0.4
383
+ ['==', ['get', 'kind'], 'centreline'], 0.2,
384
+ dimmed ? 0.1 : 0.5
382
385
  ],
383
386
  'line-width': [
384
387
  'let',
385
388
  'width', [
386
389
  'case',
387
390
  ['==', ['get', 'type'], 'bezier'], 0.1,
391
+ ['==', ['get', 'kind'], 'centreline'], 2,
388
392
  ['==', ['get', 'kind'], 'error'], 1,
389
393
  ['==', ['get', 'kind'], 'unknown'], 1,
390
394
  ['boolean', ['get', 'invisible'], false], 0.1,
@@ -452,22 +456,23 @@ export class FeatureNerveLayer extends VectorStyleLayer
452
456
  'filter': [
453
457
  'all',
454
458
  ['==', '$type', 'LineString'],
459
+ ['!=', 'kind', 'centreline'],
455
460
  ['==', 'type', 'nerve']
456
461
  ],
457
462
  'paint': {
458
463
  'line-color': [
459
464
  'case',
465
+ ['boolean', ['feature-state', 'hidden'], false], '#CCC',
460
466
  ['boolean', ['feature-state', 'active'], false], '#222',
461
467
  ['boolean', ['feature-state', 'selected'], false], 'red',
462
- ['boolean', ['feature-state', 'hidden'], false], '#CCC',
463
468
  '#888'
464
469
  ],
465
470
  'line-opacity': [
466
471
  'case',
467
- ['boolean', ['feature-state', 'active'], false], 0.9,
468
- ['boolean', ['feature-state', 'selected'], false], 0.9,
469
472
  ['boolean', ['feature-state', 'hidden'], false], 0.3,
470
473
  ['boolean', ['get', 'invisible'], false], 0.001,
474
+ ['boolean', ['feature-state', 'active'], false], 0.9,
475
+ ['boolean', ['feature-state', 'selected'], false], 0.9,
471
476
  0.9
472
477
  ],
473
478
  'line-dasharray': [2, 1],
@@ -198,12 +198,18 @@ li.flatmap-contextmenu-item:hover {
198
198
  label[for=path-all-paths] {
199
199
  font-weight: bold;
200
200
  }
201
+ .nerve-centreline {
202
+ background: #CC0;
203
+ }
201
204
  .nerve-cns {
202
205
  background: #9B1FC1;
203
206
  }
204
207
  .nerve-lcn {
205
208
  background: #F19E38;
206
209
  }
210
+ .nerve-other {
211
+ background: #888;
212
+ }
207
213
  .nerve-para-pre {
208
214
  background: #3F8F4A;
209
215
  }