@abi-software/flatmap-viewer 2.3.3-b.4 → 2.4.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.3.3-b.4``
41
+ * ``npm install @abi-software/flatmap-viewer@2.4.0-a.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.3-b.4",
3
+ "version": "2.4.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/annotation.js CHANGED
@@ -508,7 +508,7 @@ export class Annotator
508
508
  border: '2px solid #080',
509
509
  borderRadius: '.5rem',
510
510
  panelSize: 'auto auto',
511
- position: 'left-top',
511
+ position: 'left-top 50 70',
512
512
  content: panelContent,
513
513
  data: features[0].properties,
514
514
  closeOnEscape: true,
@@ -588,7 +588,7 @@ export class Annotator
588
588
  border: '2px solid #080',
589
589
  borderRadius: '.5rem',
590
590
  panelSize: '725px auto',
591
- position: 'left-top',
591
+ position: 'left-top 50 70',
592
592
  data: {
593
593
  flatmap: this.__flatmap
594
594
  },
@@ -25,8 +25,10 @@ import { indexedProperties } from '../search.js';
25
25
  export const displayedProperties = [
26
26
  'id',
27
27
  'class',
28
+ 'cd-class',
28
29
  'fc-class',
29
30
  'fc-kind',
31
+ 'name',
30
32
  ...indexedProperties
31
33
  ];
32
34
 
@@ -137,6 +139,7 @@ export class InfoControl
137
139
  this._active = true;
138
140
  button.classList.add('control-button-active');
139
141
  } else {
142
+ this.reset();
140
143
  this._active = false;
141
144
  button.classList.remove('control-button-active');
142
145
  }
@@ -194,7 +197,7 @@ export class InfoControl
194
197
  const featureIds = [];
195
198
  const displayFeatures = [];
196
199
  for (const feat of featureList) {
197
- if (featureIds.indexOf(feat.id) < 0) {
200
+ if (!featureIds.includes(feat.id)) {
198
201
  featureIds.push(feat.id);
199
202
  const displayFeat = {};
200
203
  displayProperties.forEach(prop => {
@@ -132,7 +132,7 @@ export class MinimapControl
132
132
  let width = DEFAULTS.width;
133
133
  if (typeof this._options.width === 'string') {
134
134
  width = parseInt(this._options.width);
135
- if (this._options.width.indexOf('%') > 0) {
135
+ if (this._options.width.includes('%')) {
136
136
  width = width*mapCanvasElement.width/100;
137
137
  }
138
138
  } else if (typeof this._options.width === 'number') {
@@ -33,7 +33,7 @@ import '../static/css/flatmap-viewer.css';
33
33
 
34
34
  //==============================================================================
35
35
 
36
- import {MapServer} from './mapserver.js';
36
+ import {MapServer, loadJSON} from './mapserver.js';
37
37
  import {SearchIndex} from './search.js';
38
38
  import {UserInteractions} from './interactions.js';
39
39
 
@@ -77,11 +77,15 @@ class FlatMap
77
77
  this.__datasetToFeatureIds = new Map();
78
78
  this.__modelToFeatureIds = new Map();
79
79
  this.__mapSourceToFeatureIds = new Map();
80
+ this.__annIdToFeatureId = new Map();
80
81
 
81
82
  for (const [featureId, annotation] of Object.entries(mapDescription.annotations)) {
82
83
  this.__addAnnotation(featureId, annotation);
83
84
  this.__searchIndex.indexMetadata(featureId, annotation);
84
85
  }
86
+ if (this.options.annotator) {
87
+ this.__addAnnotatedComments();
88
+ }
85
89
 
86
90
  // Set base of source URLs in map's style
87
91
 
@@ -165,7 +169,7 @@ class FlatMap
165
169
  if (mapDescription.options.navigationControl) {
166
170
  const value = mapDescription.options.navigationControl;
167
171
  const position = ((typeof value === 'string')
168
- && (['top-left', 'top-right', 'bottom-right', 'bottom-left'].indexOf(value) >= 0))
172
+ && ['top-left', 'top-right', 'bottom-right', 'bottom-left'].includes(value))
169
173
  ? value : 'bottom-right';
170
174
  this._map.addControl(new NavigationControl(this), position);
171
175
  }
@@ -207,6 +211,25 @@ class FlatMap
207
211
  });
208
212
  }
209
213
 
214
+ async __addAnnotatedComments()
215
+ //============================
216
+ {
217
+ const url = this.makeServerUrl('', 'annotator/')
218
+ const annotatedFeatures = await loadJSON(url);
219
+ for (const annotatedId of annotatedFeatures) {
220
+ const featureId = this.__annIdToFeatureId.get(annotatedId);
221
+ if (featureId) {
222
+ const url = this.makeServerUrl(annotatedId, 'annotator/')
223
+ const annotations = await loadJSON(url);
224
+ for (const annotation of annotations) { // In order of most recent to oldest
225
+ if ('rdfs:comment' in annotation) {
226
+ this.__searchIndex.indexText(featureId, annotation['rdfs:comment']);
227
+ }
228
+ }
229
+ }
230
+ }
231
+ }
232
+
210
233
  async setupUserInteractions_()
211
234
  //============================
212
235
  {
@@ -498,6 +521,7 @@ class FlatMap
498
521
  this.__updateFeatureIdMap('dataset', this.__datasetToFeatureIds, ann);
499
522
  this.__updateFeatureIdMap('models', this.__modelToFeatureIds, ann);
500
523
  this.__updateFeatureIdMap('source', this.__mapSourceToFeatureIds, ann);
524
+ this.__annIdToFeatureId.set(ann.id, featureId);
501
525
  }
502
526
 
503
527
  modelFeatureIds(anatomicalId)
@@ -943,7 +967,7 @@ class FlatMap
943
967
  if (property in properties) {
944
968
  const value = properties[property];
945
969
  if (value !== undefined) {
946
- if (jsonProperties.indexOf(property) >= 0) {
970
+ if (jsonProperties.includes(property)) {
947
971
  data[property] = JSON.parse(properties[property])
948
972
  } else {
949
973
  data[property] = properties[property];
@@ -261,7 +261,7 @@ export class UserInteractions
261
261
  const feature = this.mapFeature(mapId);
262
262
  if (feature !== undefined) {
263
263
  this._map.setFeatureState(feature, { 'map-annotation': true });
264
- if (annotated_features.indexOf(ann.id) >= 0) {
264
+ if (annotated_features.includes(ann.id)) {
265
265
  this._map.setFeatureState(feature, { 'annotated': true });
266
266
  }
267
267
  }
@@ -402,19 +402,36 @@ export class UserInteractions
402
402
  selectFeature(featureId, dim=true)
403
403
  //================================
404
404
  {
405
- featureId = +featureId; // Ensure numeric
406
- if (this._selectedFeatureIds.size === 0) {
407
- this._layerManager.setPaint({...this.__colourOptions, dimmed: dim});
405
+ const ann = this._flatmap.annotation(featureId);
406
+ if ('sckan' in ann) {
407
+ const sckanState = this._layerManager.sckanState;
408
+ if (sckanState === 'none'
409
+ || sckanState === 'valid' && !ann.sckan
410
+ || sckanState === 'invalid' && ann.sckan) {
411
+ return false;
412
+ }
408
413
  }
414
+ featureId = +featureId; // Ensure numeric
415
+ let result = false;
416
+ const noSelection = (this._selectedFeatureIds.size === 0);
409
417
  if (this._selectedFeatureIds.has(featureId)) {
410
418
  this._selectedFeatureIds.set(featureId, this._selectedFeatureIds.get(featureId) + 1);
419
+ result = true;
411
420
  } else {
412
421
  const feature = this.mapFeature(featureId);
413
422
  if (feature !== undefined) {
414
- this._map.setFeatureState(feature, { 'selected': true });
415
- this._selectedFeatureIds.set(featureId, 1);
423
+ const state = this._map.getFeatureState(feature);
424
+ if (state !== undefined && (!('hidden' in state) || !state.hidden)) {
425
+ this._map.setFeatureState(feature, { 'selected': true });
426
+ this._selectedFeatureIds.set(featureId, 1);
427
+ result = true;
428
+ }
416
429
  }
417
430
  }
431
+ if (result && noSelection) {
432
+ this._layerManager.setPaint({...this.__colourOptions, dimmed: dim});
433
+ }
434
+ return result;
418
435
  }
419
436
 
420
437
  unselectFeature(featureId)
@@ -566,10 +583,11 @@ export class UserInteractions
566
583
  for (const featureId of featureIds) {
567
584
  const annotation = this._flatmap.annotation(featureId);
568
585
  if (annotation) {
569
- this.selectFeature(featureId);
570
- if ('type' in annotation && annotation.type.startsWith('line')) {
571
- for (const pathFeatureId of this.__pathManager.lineFeatureIds([featureId])) {
572
- this.selectFeature(pathFeatureId);
586
+ if (this.selectFeature(featureId)) {
587
+ if ('type' in annotation && annotation.type.startsWith('line')) {
588
+ for (const pathFeatureId of this.__pathManager.lineFeatureIds([featureId])) {
589
+ this.selectFeature(pathFeatureId);
590
+ }
573
591
  }
574
592
  }
575
593
  }
@@ -609,13 +627,15 @@ export class UserInteractions
609
627
  for (const featureId of featureIds) {
610
628
  const annotation = this._flatmap.annotation(featureId);
611
629
  if (annotation) {
612
- this.selectFeature(featureId);
613
- bbox = expandBounds(bbox, annotation.bounds);
614
- if ('type' in annotation && annotation.type.startsWith('line')) {
615
- for (const pathFeatureId of this.__pathManager.lineFeatureIds([featureId])) {
616
- this.selectFeature(pathFeatureId);
617
- const pathAnnotation = this._flatmap.annotation(pathFeatureId)
618
- bbox = expandBounds(bbox, pathAnnotation.bounds);
630
+ if (this.selectFeature(featureId)) {
631
+ bbox = expandBounds(bbox, annotation.bounds);
632
+ if ('type' in annotation && annotation.type.startsWith('line')) {
633
+ for (const pathFeatureId of this.__pathManager.lineFeatureIds([featureId])) {
634
+ if (this.selectFeature(pathFeatureId)) {
635
+ const pathAnnotation = this._flatmap.annotation(pathFeatureId)
636
+ bbox = expandBounds(bbox, pathAnnotation.bounds);
637
+ }
638
+ }
619
639
  }
620
640
  }
621
641
  }
@@ -882,7 +902,7 @@ export class UserInteractions
882
902
  const htmlList = [];
883
903
  const featureIds = [];
884
904
  for (const feature of labelledFeatures) {
885
- if (featureIds.indexOf(feature.id) < 0) {
905
+ if (!featureIds.includes(feature.id)) {
886
906
  featureIds.push(feature.id);
887
907
  for (const prop of debugProperties) {
888
908
  if (prop in feature.properties) {
@@ -918,26 +938,34 @@ export class UserInteractions
918
938
  //=======================================
919
939
  {
920
940
  // Show a tooltip
921
- if (html !== '') {
922
- this._tooltip = new maplibre.Popup({
923
- closeButton: false,
924
- closeOnClick: false,
925
- maxWidth: 'none',
926
- className: 'flatmap-tooltip-popup'
927
- });
941
+ if (html !== '' || this._flatmap.options.showId && feature !== null) {
942
+ let header = '';
928
943
  if (this._flatmap.options.showPosition) {
929
944
  const pt = turf.point(lngLat.toArray());
930
945
  const gps = turfProjection.toMercator(pt);
931
946
  const coords = gps.geometry.coordinates;
932
- const header = (feature === null)
947
+ header = (feature === null)
933
948
  ? JSON.stringify(coords)
934
- : `${JSON.stringify(coords)} (${feature.id} ${feature.properties['id']})`;
949
+ : `${JSON.stringify(coords)} (${feature.id})`;
950
+ }
951
+ if (this._flatmap.options.showId && feature !== null && 'id' in feature.properties) {
952
+ header = `${header} ${feature.properties.id}`;
953
+ }
954
+ if (header !== '') {
935
955
  html = `<span>${header}</span><br/>${html}`;
936
956
  }
937
- this._tooltip
938
- .setLngLat(lngLat)
939
- .setHTML(html)
940
- .addTo(this._map);
957
+ if (html !== '') {
958
+ this._tooltip = new maplibre.Popup({
959
+ closeButton: false,
960
+ closeOnClick: false,
961
+ maxWidth: 'none',
962
+ className: 'flatmap-tooltip-popup'
963
+ });
964
+ this._tooltip
965
+ .setLngLat(lngLat)
966
+ .setHTML(html)
967
+ .addTo(this._map);
968
+ }
941
969
  }
942
970
  }
943
971
 
@@ -948,7 +976,7 @@ export class UserInteractions
948
976
  const clickedFeatureId = feature.id;
949
977
  const dim = !('properties' in feature
950
978
  && 'kind' in feature.properties
951
- && ['cell-type', 'scaffold', 'tissue'].indexOf(feature.properties.kind) >= 0);
979
+ && ['cell-type', 'scaffold', 'tissue'].includes(feature.properties.kind));
952
980
  if (!(event.ctrlKey || event.metaKey)) {
953
981
  let selecting = true;
954
982
  for (const featureId of this._selectedFeatureIds.keys()) {
@@ -1149,7 +1177,7 @@ export class UserInteractions
1149
1177
 
1150
1178
  for (const featureId of featureIds) {
1151
1179
  const annotation = this._flatmap.annotation(featureId);
1152
- if (annotation.geometry.indexOf('Polygon') < 0) {
1180
+ if (!annotation.geometry.includes('Polygon')) {
1153
1181
  continue;
1154
1182
  }
1155
1183
  if (!('marker' in annotation)) {
@@ -1230,7 +1258,7 @@ export class UserInteractions
1230
1258
  for (const [marker, id] of this.__markerIdByMarker.entries()) {
1231
1259
  if (visibleBounds.contains(marker.getLngLat())) {
1232
1260
  const annotation = this.__annotationByMarkerId.get(id);
1233
- if (anatomicalIds.indexOf(annotation.models) < 0) {
1261
+ if (!anatomicalIds.includes(annotation.models)) {
1234
1262
  anatomicalIds.push(annotation.models);
1235
1263
  }
1236
1264
  }
@@ -1247,7 +1275,7 @@ export class UserInteractions
1247
1275
  return;
1248
1276
  }
1249
1277
 
1250
- if (['mouseenter', 'mouseleave', 'click'].indexOf(event.type) >= 0) {
1278
+ if (['mouseenter', 'mouseleave', 'click'].includes(event.type)) {
1251
1279
  this.__activeMarker = marker;
1252
1280
 
1253
1281
  // Remove any existing tooltips
@@ -1257,7 +1285,7 @@ export class UserInteractions
1257
1285
  // Reset cursor
1258
1286
  marker.getElement().style.cursor = 'default';
1259
1287
 
1260
- if (['mouseenter', 'click'].indexOf(event.type) >= 0) {
1288
+ if (['mouseenter', 'click'].includes(event.type)) {
1261
1289
  const markerId = this.__markerIdByMarker.get(marker);
1262
1290
  const annotation = this.__annotationByMarkerId.get(markerId);
1263
1291
  // The marker's feature
package/src/layers.js CHANGED
@@ -115,8 +115,7 @@ class MapFeatureLayers extends MapStylingLayers
115
115
  // if no image layers then make feature borders (and lines?) more visible...??
116
116
  if (haveVectorLayers) {
117
117
  const featuresVectorSource = this.vectorSourceId(FEATURES_LAYER);
118
- const vectorFeatures = vectorTileSource.vectorLayerIds
119
- .indexOf(featuresVectorSource) >= 0;
118
+ const vectorFeatures = vectorTileSource.vectorLayerIds.includes(featuresVectorSource);
120
119
  if (vectorFeatures) {
121
120
  this.__addStyleLayer(style.FeatureFillLayer);
122
121
  this.__addStyleLayer(style.FeatureDashLineLayer);
@@ -152,7 +151,7 @@ class MapFeatureLayers extends MapStylingLayers
152
151
  const pathwaysVectorSource = this.vectorSourceId(PATHWAYS_LAYER);
153
152
  if (this.__map.getSource('vector-tiles')
154
153
  .vectorLayerIds
155
- .indexOf(pathwaysVectorSource) >= 0) {
154
+ .includes(pathwaysVectorSource)) {
156
155
  this.__addStyleLayer(style.AnnotatedPathLayer, PATHWAYS_LAYER);
157
156
 
158
157
  this.__addStyleLayer(style.CentrelineEdgeLayer, PATHWAYS_LAYER);
@@ -298,6 +297,12 @@ export class LayerManager
298
297
  return layers;
299
298
  }
300
299
 
300
+ get sckanState()
301
+ //==============
302
+ {
303
+ return this.__layerOptions.sckan;
304
+ }
305
+
301
306
  activate(layerId, enable=true)
302
307
  //============================
303
308
  {
@@ -337,8 +342,8 @@ export class LayerManager
337
342
  //=======================================
338
343
  {
339
344
  const currentState = this.__layerOptions.sckan;
340
- const validEnabled = ['valid', 'all'].indexOf(currentState) >= 0;
341
- const invalidEnabled = ['invalid', 'all'].indexOf(currentState) >= 0;
345
+ const validEnabled = ['valid', 'all'].includes(currentState);
346
+ const invalidEnabled = ['invalid', 'all'].includes(currentState);
342
347
  let newState = sckanState.toLowerCase();
343
348
  if (newState === 'valid') {
344
349
  if (enable && !validEnabled) {
package/src/main.js CHANGED
@@ -59,6 +59,7 @@ export async function standaloneViewer(map_endpoint=null, options={})
59
59
  background: defaultBackground,
60
60
  debug: false,
61
61
  minimap: false,
62
+ showId: true,
62
63
  showPosition: false,
63
64
  standalone: true,
64
65
  annotator: true
package/src/mapserver.js CHANGED
@@ -22,6 +22,24 @@ limitations under the License.
22
22
 
23
23
  //==============================================================================
24
24
 
25
+ export async function loadJSON(url)
26
+ //=================================
27
+ {
28
+ const response = await fetch(url, {
29
+ method: 'GET',
30
+ headers: {
31
+ "Accept": "application/json; charset=utf-8",
32
+ "Cache-Control": "no-store"
33
+ }
34
+ });
35
+ if (!response.ok) {
36
+ throw new Error(`Cannot access ${url}`);
37
+ }
38
+ return response.json();
39
+ }
40
+
41
+ //==============================================================================
42
+
25
43
  export class MapServer
26
44
  {
27
45
  constructor(url)
@@ -39,18 +57,7 @@ export class MapServer
39
57
  async loadJSON(relativePath)
40
58
  //==========================
41
59
  {
42
- const url = this.url(relativePath);
43
- const response = await fetch(url, {
44
- method: 'GET',
45
- headers: {
46
- "Accept": "application/json; charset=utf-8",
47
- "Cache-Control": "no-store"
48
- }
49
- });
50
- if (!response.ok) {
51
- throw new Error(`Cannot access ${url}`);
52
- }
53
- return response.json();
60
+ return loadJSON(this.url(relativePath));
54
61
  }
55
62
  }
56
63
 
package/src/pathways.js CHANGED
@@ -42,7 +42,7 @@ const PATH_TYPES = [
42
42
  { type: "arterial", label: "Arterial blood vessel", colour: "#F00", enabled: false},
43
43
  { type: "venous", label: "Venous blood vessel", colour: "#2F6EBA", enabled: false},
44
44
  { type: "centreline", label: "Nerve centrelines", colour: "#CCC", enabled: false},
45
- { type: "error", label: "Paths with errors or warnings", colour: "#FF0"}
45
+ { type: "error", label: "Paths with errors or warnings", colour: "#FF0", enabled: false}
46
46
  ];
47
47
 
48
48
  export const PATH_STYLE_RULES =
@@ -65,12 +65,11 @@ export class PathManager
65
65
  }
66
66
  }
67
67
  }
68
- this.__pathModelPaths = {}; // pathModelId: [pathIds]
69
- this.__pathToPathModel = {};
70
-
71
- this.__paths = {};
72
- const pathLines = {}; // pathId: [lineIds]
73
- const pathNerves = {}; // pathId: [nerveIds]
68
+ this.__pathModelPaths = {}; // pathModelId: [pathIds]
69
+ this.__pathToPathModel = {}; // pathId: pathModelId
70
+ this.__paths = {}; // pathId: path
71
+ const pathLines = {}; // pathId: [lineIds]
72
+ const pathNerves = {}; // pathId: [nerveIds]
74
73
  if ('paths' in flatmap.pathways) {
75
74
  for (const [pathId, path] of Object.entries(flatmap.pathways.paths)) {
76
75
  pathLines[pathId] = path.lines;
package/src/search.js CHANGED
@@ -56,18 +56,19 @@ export class SearchIndex
56
56
  if (prop in metadata) {
57
57
  const text = metadata[prop];
58
58
  if (!textSeen.includes(text)) {
59
- this.addTerm_(featureId, text);
59
+ this.indexText(featureId, text);
60
60
  textSeen.push(text);
61
61
  }
62
62
  }
63
63
  }
64
64
  }
65
65
 
66
- addTerm_(featureId, text)
67
- //=======================
66
+ indexText(featureId, text)
67
+ //========================
68
68
  {
69
69
  text = text.replace(new RegExp('<br/>', 'g'), ' ')
70
- .replace('\n', ' ');
70
+ .replace(new RegExp('\n', 'g'), ' ')
71
+ ;
71
72
  if (text) {
72
73
  this._searchEngine.add({
73
74
  id: this._featureIds.length,
@@ -95,7 +96,7 @@ export class SearchIndex
95
96
  const options = {};
96
97
  let results = [];
97
98
  text = text.trim()
98
- if (text.length > 2 && ["'", '"'].indexOf(text.slice(0, 1)) >= 0) {
99
+ if (text.length > 2 && ["'", '"'].includes(text.slice(0, 1))) {
99
100
  text = text.replaceAll(text.slice(0, 1), '');
100
101
  results = this._searchEngine.search(text, {prefix: true, combineWith: 'AND'});
101
102
  } else if (text.length > 1) {
package/src/styling.js CHANGED
@@ -31,7 +31,7 @@ import {PATH_STYLE_RULES} from './pathways.js';
31
31
  //==============================================================================
32
32
 
33
33
  const COLOUR_ACTIVE = 'blue';
34
- const COLOUR_ANNOTATED = '#0F0';
34
+ const COLOUR_ANNOTATED = '#C8F';
35
35
  const COLOUR_SELECTED = '#0F0';
36
36
  const COLOUR_HIDDEN = '#D8D8D8';
37
37
 
@@ -418,15 +418,18 @@ export class AnnotatedPathLayer extends VectorStyleLayer
418
418
 
419
419
  paintStyle(options={}, changes=false)
420
420
  {
421
+ const dimmed = 'dimmed' in options && options.dimmed;
421
422
  const exclude = 'excludeAnnotated' in options && options.excludeAnnotated;
422
423
  const paintStyle = {
423
424
  'line-color': COLOUR_ANNOTATED,
424
425
  'line-dasharray': [5, 0.5, 3, 0.5],
425
426
  'line-opacity': [
426
427
  'case',
428
+ ['boolean', ['feature-state', 'active'], false], 0.8,
429
+ ['boolean', ['feature-state', 'selected'], false], 0.8,
427
430
  ['boolean', ['feature-state', 'hidden'], false], 0.05,
428
431
  ['boolean', ['feature-state', 'annotated'], false],
429
- (exclude ? 0.05 : 0.8),
432
+ ((exclude || dimmed) ? 0.05 : 0.8),
430
433
  0.6
431
434
  ],
432
435
  'line-width': [
@@ -435,7 +438,11 @@ export class AnnotatedPathLayer extends VectorStyleLayer
435
438
  ['case',
436
439
  ['boolean', ['feature-state', 'hidden'], false], 0.0,
437
440
  ['boolean', ['feature-state', 'annotated'], false],
438
- exclude ? 0.0 : (['*', 1.2, ['case', ['has', 'stroke-width'], ['get', 'stroke-width'], 1.0]]),
441
+ exclude ? 0.0 : (['*', 1.1, ['case',
442
+ ['has', 'stroke-width'], ['get', 'stroke-width'],
443
+ ['boolean', ['feature-state', 'active'], false], 1.1,
444
+ ['boolean', ['feature-state', 'active'], false], 1.1,
445
+ 1.0]]),
439
446
  0.0
440
447
  ],
441
448
  STROKE_INTERPOLATION
@@ -446,7 +453,6 @@ export class AnnotatedPathLayer extends VectorStyleLayer
446
453
 
447
454
  style(options)
448
455
  {
449
- const dimmed = 'dimmed' in options && options.dimmed;
450
456
  return {
451
457
  ...super.style(),
452
458
  'type': 'line',
package/src/systems.js CHANGED
@@ -16,18 +16,26 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
16
  See the License for the specific language governing permissions and
17
17
  limitations under the License.
18
18
 
19
- **/
19
+ ******************************************************************************/
20
+
21
+ const FC_KIND = {
22
+ SYSTEM: ['fc:System', 'fc-class:System'],
23
+ ORGAN: ['fc:Organ', 'fc-class:Organ'],
24
+ FTU: ['fc:Ftu', 'fc-class:Ftu']
25
+ };
26
+
20
27
  //==============================================================================
21
28
 
22
29
  export class SystemsManager
23
30
  {
24
31
  constructor(flatmap, ui, enabled=false)
25
32
  {
33
+ this.__flatmap = flatmap;
26
34
  this.__ui = ui;
27
35
  this.__systems = new Map();
28
36
  this.__enabledChildren = new Map();
29
- for (const [id, ann] of flatmap.annotations) {
30
- if (ann['fc-class'] === 'fc-class:System') {
37
+ for (const [_, ann] of flatmap.annotations) {
38
+ if (FC_KIND.SYSTEM.includes(ann['fc-class'])) {
31
39
  const systemId = ann.name.replaceAll(' ', '_');
32
40
  if (this.__systems.has(systemId)) {
33
41
  this.__systems.get(systemId).featureIds.push(ann.featureId)
@@ -37,7 +45,8 @@ export class SystemsManager
37
45
  colour: ann.colour,
38
46
  featureIds: [ ann.featureId ],
39
47
  enabled: false,
40
- pathIds: ('path-ids' in ann) ? ann['path-ids'] : []
48
+ pathIds: ('path-ids' in ann) ? ann['path-ids'] : [],
49
+ organs: this.__children(ann.children, FC_KIND.ORGAN)
41
50
  });
42
51
  this.__ui.enableFeature(ann.featureId, false, true);
43
52
  }
@@ -57,6 +66,26 @@ export class SystemsManager
57
66
  }
58
67
  }
59
68
 
69
+ __children(childFeatureIds, childClass)
70
+ //=====================================
71
+ {
72
+ const children = [];
73
+ for (const childFeatureId of childFeatureIds || []) {
74
+ const childAnnotation = this.__flatmap.annotation(childFeatureId);
75
+ if (childAnnotation !== undefined && childClass.includes(childAnnotation['fc-class'])) {
76
+ const child = {
77
+ label: childAnnotation.label,
78
+ models: childAnnotation.models
79
+ };
80
+ if (childClass === FC_KIND.ORGAN) {
81
+ child.ftus = this.__children(childAnnotation.children, FC_KIND.FTU)
82
+ };
83
+ children.push(child);
84
+ }
85
+ }
86
+ return children;
87
+ }
88
+
60
89
  get systems()
61
90
  //===========
62
91
  {
@@ -66,7 +95,8 @@ export class SystemsManager
66
95
  id: systemId,
67
96
  name: system.name,
68
97
  colour: system.colour,
69
- enabled: system.enabled
98
+ enabled: system.enabled,
99
+ organs: system.organs
70
100
  });
71
101
  }
72
102
  return systems;
package/src/utils.js CHANGED
@@ -35,7 +35,7 @@ export class List extends Array {
35
35
  }
36
36
 
37
37
  contains(element) {
38
- return (super.indexOf(element) >= 0);
38
+ return (super.includes(element));
39
39
  }
40
40
 
41
41
  extend(other) {
@@ -96,12 +96,12 @@ export class Mutex
96
96
 
97
97
  export function normaliseId(id)
98
98
  {
99
- if (id.indexOf(':') < 0) {
99
+ if (!id.includes(':')) {
100
100
  return id;
101
101
  }
102
102
  const parts = id.split(':')
103
103
  const lastPart = parts[parts.length - 1]
104
- if (['http', 'https', 'urn'].indexOf(parts[0]) >= 0 || '0123456789'.indexOf(lastPart[0]) < 0) {
104
+ if (['http', 'https', 'urn'].includes(parts[0]) || !'0123456789'.includes(lastPart[0])) {
105
105
  return id;
106
106
  }
107
107
  parts[parts.length - 1] = lastPart.padStart(8, '0');