@abi-software/flatmap-viewer 2.2.6 → 2.2.8

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.6``
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.6",
3
+ "version": "2.2.8",
4
4
  "description": "Flatmap viewer using Maplibre GL",
5
5
  "repository": "https://github.com/AnatomicMaps/flatmap-viewer.git",
6
6
  "main": "src/main.js",
@@ -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);
@@ -354,7 +355,7 @@ class FlatMap
354
355
  //==============
355
356
  {
356
357
  if (url.startsWith('/')) {
357
- return `${this._baseUrl}flatmap/${this._id}${url}`; // We don't want embedded `{` and `}` characters escaped
358
+ return `${this._baseUrl}flatmap/${this.__uuid}${url}`; // We don't want embedded `{` and `}` characters escaped
358
359
  } else if (!url.startsWith('http://') && !url.startsWith('https://')) {
359
360
  console.log(`Invalid URL (${url}) in map's sources`);
360
361
  }
@@ -402,7 +403,7 @@ class FlatMap
402
403
  get id()
403
404
  //======
404
405
  {
405
- return this._id;
406
+ return this.__uuid;
406
407
  }
407
408
 
408
409
  /**
@@ -424,7 +425,7 @@ class FlatMap
424
425
  get uniqueId()
425
426
  //============
426
427
  {
427
- return `${this._id}-${this._mapNumber}`;
428
+ return `${this.__uuid}-${this._mapNumber}`;
428
429
  }
429
430
 
430
431
  get activeLayerNames()
@@ -466,7 +467,7 @@ class FlatMap
466
467
  this.__idToAnnotation.set(featureId, ann);
467
468
  this.__updateFeatureIdMap('dataset', this.__datasetToFeatureIds, ann);
468
469
  this.__updateFeatureIdMap('models', this.__modelToFeatureIds, ann);
469
- this.__updateFeatureIdMap('source', this.___mapIdToFeatureIds, ann);
470
+ this.__updateFeatureIdMap('source', this.__mapSourceToFeatureIds, ann);
470
471
  }
471
472
 
472
473
  modelFeatureIds(anatomicalId)
@@ -490,7 +491,7 @@ class FlatMap
490
491
  if (featureIds.length == 0) {
491
492
  // We couldn't find a feature by anatomical id, so check dataset and source
492
493
  featureIds.extend(this.__datasetToFeatureIds.get(anatomicalIds));
493
- featureIds.extend(this.___mapIdToFeatureIds.get(anatomicalIds));
494
+ featureIds.extend(this.__mapSourceToFeatureIds.get(anatomicalIds));
494
495
  }
495
496
  if (featureIds.length == 0 && this._userInteractions !== null) {
496
497
  // We still haven't found a feature, so check connectivity
@@ -634,7 +635,7 @@ class FlatMap
634
635
  return {
635
636
  taxon: this.__taxon,
636
637
  biologicalSex: this.__biologicalSex,
637
- id: this._id
638
+ uuid: this.__uuid
638
639
  };
639
640
  }
640
641
 
@@ -1086,7 +1087,8 @@ export class MapManager
1086
1087
  latestMap_(identifier)
1087
1088
  //====================
1088
1089
  {
1089
- const mapDescribes = ('uuid' in identifier) ? identifier.uuid
1090
+ const mapDescribes = (identifier.constructor.name === "String") ? identifier
1091
+ : ('uuid' in identifier) ? identifier.uuid
1090
1092
  : ('taxon' in identifier) ? identifier.taxon
1091
1093
  : null;
1092
1094
  if (mapDescribes === null) {
@@ -1095,10 +1097,13 @@ export class MapManager
1095
1097
  let latestMap = null;
1096
1098
  let lastCreatedTime = '';
1097
1099
  for (const map of this._mapList) {
1098
- if ('uuid' in map && mapDescribes === map.uuid
1100
+ if (('uuid' in map && mapDescribes === map.uuid
1099
1101
  || mapDescribes === map.id
1100
1102
  || 'taxon' in map && mapDescribes === map.taxon
1101
- || mapDescribes === map.source) {
1103
+ || mapDescribes === map.source)
1104
+ && (!('biologicalSex' in identifier)
1105
+ || ('biologicalSex' in map
1106
+ && identifier.biologicalSex === map.biologicalSex))) {
1102
1107
  if ('created' in map) {
1103
1108
  if (lastCreatedTime < map.created) {
1104
1109
  lastCreatedTime = map.created;
@@ -1110,13 +1115,6 @@ export class MapManager
1110
1115
  }
1111
1116
  }
1112
1117
  }
1113
- if (latestMap !== null) {
1114
- if ('biologicalSex' in identifier
1115
- && 'biologicalSex' in latestMap
1116
- && identifier.biologicalSex !== latestMap.biologicalSex) {
1117
- latestMap = null;
1118
- }
1119
- }
1120
1118
  return latestMap;
1121
1119
  }
1122
1120
 
@@ -1140,7 +1138,7 @@ export class MapManager
1140
1138
  * @arg identifier.taxon {string} The taxon identifier of the species represented by the map. This is
1141
1139
  * specified as metadata in the map's source file.)
1142
1140
  * @arg identifier.biologicalSex {string} The biological sex of the species represented by the map.
1143
- * This is specified as metadatain the map's source file.)
1141
+ * This is specified as metadata in the map's source file.)
1144
1142
  * @arg identifier.uuid {string} The unique uuid the flatmap. If given then this exact map will
1145
1143
  * be loaded, overriding ``taxon`` and ``biologicalSex``.
1146
1144
  * @arg container {string} The id of the HTML container in which to display the map.
@@ -1304,7 +1302,8 @@ export class MapManager
1304
1302
  this._mapNumber += 1;
1305
1303
  const flatmap = new FlatMap(container, this._mapServer.url(),
1306
1304
  {
1307
- id: mapId,
1305
+ id: map,
1306
+ uuid: mapId,
1308
1307
  details: mapIndex,
1309
1308
  taxon: map.taxon,
1310
1309
  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) {
@@ -164,10 +164,8 @@ export class UserInteractions
164
164
 
165
165
  for (const [id, ann] of flatmap.annotations) {
166
166
  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})`);
167
+ if (feature !== undefined) {
168
+ this._map.setFeatureState(feature, { 'annotated': true });
171
169
  }
172
170
  }
173
171
 
@@ -251,13 +249,15 @@ export class UserInteractions
251
249
  //====================
252
250
  {
253
251
  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
- };
252
+ if (ann !== undefined) {
253
+ return {
254
+ id: featureId,
255
+ source: VECTOR_TILES_SOURCE,
256
+ sourceLayer: this._flatmap.options.separateLayers
257
+ ? `${ann['layer']}_${ann['tile-layer']}`
258
+ : ann['tile-layer']
259
+ };
260
+ }
261
261
  }
262
262
 
263
263
  featureSelected_(featureId)
@@ -277,8 +277,10 @@ export class UserInteractions
277
277
  this._selectedFeatureIds.set(featureId, this._selectedFeatureIds.get(featureId) + 1);
278
278
  } else {
279
279
  const feature = this.mapFeature_(featureId);
280
- this._map.setFeatureState(feature, { 'selected': true });
281
- this._selectedFeatureIds.set(featureId, 1);
280
+ if (feature !== undefined) {
281
+ this._map.setFeatureState(feature, { 'selected': true });
282
+ this._selectedFeatureIds.set(featureId, 1);
283
+ }
282
284
  }
283
285
  }
284
286
 
@@ -292,8 +294,10 @@ export class UserInteractions
292
294
  this._selectedFeatureIds.set(featureId, references - 1);
293
295
  } else {
294
296
  const feature = this.mapFeature_(featureId);
295
- this._map.removeFeatureState(feature, 'selected');
296
- this._selectedFeatureIds.delete(+featureId);
297
+ if (feature !== undefined) {
298
+ this._map.removeFeatureState(feature, 'selected');
299
+ this._selectedFeatureIds.delete(+featureId);
300
+ }
297
301
  }
298
302
  }
299
303
  if (this._selectedFeatureIds.size === 0) {
@@ -306,7 +310,9 @@ export class UserInteractions
306
310
  {
307
311
  for (const featureId of this._selectedFeatureIds.keys()) {
308
312
  const feature = this.mapFeature_(featureId);
309
- this._map.removeFeatureState(feature, 'selected');
313
+ if (feature !== undefined) {
314
+ this._map.removeFeatureState(feature, 'selected');
315
+ }
310
316
  }
311
317
  this._selectedFeatureIds.clear();
312
318
  this._layerManager.setColour({...this.__colourOptions, dimmed: false});
@@ -318,7 +324,8 @@ export class UserInteractions
318
324
  // Get the features covering the event's point that are in the active layers
319
325
 
320
326
  return this._map.queryRenderedFeatures(event.point).filter(f => {
321
- return (this.activeLayerNames.indexOf(f.sourceLayer) >= 0)
327
+ return (this.__enabledFeature(f)
328
+ && this.activeLayerNames.indexOf(f.sourceLayer) >= 0)
322
329
  && ('featureId' in f.properties);
323
330
  }
324
331
  );
@@ -327,8 +334,10 @@ export class UserInteractions
327
334
  __activateFeature(feature)
328
335
  //=======================
329
336
  {
330
- this._map.setFeatureState(feature, { active: true });
331
- this._activeFeatures.push(feature);
337
+ if (feature !== undefined) {
338
+ this._map.setFeatureState(feature, { active: true });
339
+ this._activeFeatures.push(feature);
340
+ }
332
341
  }
333
342
 
334
343
  resetActiveFeatures_()
@@ -617,17 +626,14 @@ export class UserInteractions
617
626
  const tooltips = [];
618
627
  for (const lineFeature of lineFeatures) {
619
628
  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
- }
629
+ if ('label' in properties
630
+ && (!('tooltip' in properties) || properties.tooltip)
631
+ && !('labelled' in properties)) {
632
+ let tooltip = '';
633
+ const label = properties.label;
634
+ const cleanLabel = (label.substr(0, 1).toUpperCase() + label.substr(1)).replaceAll("\n", "<br/>");
635
+ if (!tooltips.includes(cleanLabel)) {
636
+ tooltips.push(cleanLabel);
631
637
  }
632
638
  }
633
639
  }
@@ -662,13 +668,11 @@ export class UserInteractions
662
668
  __featureEvent(type, feature)
663
669
  //===========================
664
670
  {
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
- }
671
+ if (feature.sourceLayer === PATHWAYS_LAYER) { // I suspect this is never true as source layer
672
+ // names are like `neural_routes_pathways`
673
+ return this._flatmap.featureEvent(type, this._pathways.pathProperties(feature));
674
+ } else if ('properties' in feature) {
675
+ return this._flatmap.featureEvent(type, feature.properties);
672
676
  }
673
677
  return false;
674
678
  }
@@ -704,7 +708,8 @@ export class UserInteractions
704
708
  }
705
709
 
706
710
  // Get all the features at the current point
707
- const features = this._map.queryRenderedFeatures(event.point);
711
+ const features = this._map.queryRenderedFeatures(event.point)
712
+ .filter(feature => this.__enabledFeature(feature));
708
713
  if (features.length === 0) {
709
714
  this._lastFeatureMouseEntered = null;
710
715
  this._lastFeatureModelsMouse = null;
@@ -742,17 +747,13 @@ export class UserInteractions
742
747
  && feature.properties.type.startsWith('line')) ));
743
748
  if (lineFeatures.length > 0) {
744
749
  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
- }
750
+ for (const lineFeature of lineFeatures) {
751
+ const lineFeatureId = +lineFeature.properties.featureId; // Ensure numeric
752
+ this.__activateFeature(lineFeature);
753
+ const lineIds = new Set(lineFeatures.map(f => f.properties.featureId));
754
+ for (const featureId of this._pathways.lineFeatureIds(lineIds)) {
755
+ if (+featureId !== lineFeatureId) {
756
+ this.__activateFeature(this.mapFeature_(featureId));
756
757
  }
757
758
  }
758
759
  }
@@ -874,7 +875,12 @@ export class UserInteractions
874
875
  //================
875
876
  {
876
877
  this.clearActiveMarker_();
877
- const clickedFeature = this._map.queryRenderedFeatures(event.point)[0];
878
+ const clickedFeatures = this._map.queryRenderedFeatures(event.point)
879
+ .filter(feature => this.__enabledFeature(feature));
880
+ if (clickedFeatures.length == 0){
881
+ return;
882
+ }
883
+ const clickedFeature = clickedFeatures[0];
878
884
  const originalEvent = event.originalEvent;
879
885
  if (clickedFeature === undefined || this._activeFeatures.length === 1) {
880
886
  this.selectionEvent_(originalEvent, clickedFeature);
@@ -920,7 +926,8 @@ export class UserInteractions
920
926
  //=======================
921
927
  {
922
928
  const state = this._map.getFeatureState(feature);
923
- return true;
929
+ return (state !== undefined
930
+ && (!('hidden' in state) || !state.hidden));
924
931
  }
925
932
 
926
933
  enablePaths_(enable, event)
@@ -937,11 +944,13 @@ export class UserInteractions
937
944
  {
938
945
  for (const featureId of featureIds) {
939
946
  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;
947
+ if (feature !== undefined) {
948
+ if (enable) {
949
+ this._map.removeFeatureState(feature, 'hidden');
950
+ } else {
951
+ this._map.setFeatureState(feature, { 'hidden': true });
952
+ this._disabledPathFeatures = true;
953
+ }
945
954
  }
946
955
  }
947
956
  }
@@ -1149,14 +1158,15 @@ export class UserInteractions
1149
1158
  const annotation = this.__annotationByMarkerId.get(markerId);
1150
1159
  // The marker's feature
1151
1160
  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)
1161
+ if (feature !== undefined) {
1162
+ if (event.type === 'mouseenter') {
1163
+ // Highlight on mouse enter
1164
+ this.resetActiveFeatures_();
1165
+ this.__activateFeature(feature);
1166
+ } else {
1167
+ this.selectionEvent_(event, feature)
1168
+ }
1158
1169
  }
1159
-
1160
1170
  // Show tooltip
1161
1171
  const html = this.tooltipHtml_(annotation, true);
1162
1172
  this.__showToolTip(html, marker.getLngLat());
package/src/pathways.js CHANGED
@@ -34,7 +34,8 @@ export const PATH_TYPES = [
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"},
38
+ { type: "other", label: "Other neuron type", colour: "#888"}
38
39
  ];
39
40
 
40
41
  //==============================================================================
@@ -120,7 +121,21 @@ export class Pathways
120
121
  }
121
122
  this._allFeatureIds = featureIds;
122
123
 
123
- this._typePaths = flatmap.pathways['type-paths']; // nerve-type: [pathIds]
124
+ // Construct a list of path types we know about
125
+ const pathTypes = [];
126
+ for (const pathType of PATH_TYPES) {
127
+ pathTypes.push(pathType.type);
128
+ }
129
+ // Map unknown path types to ``other``
130
+ this.__typePaths = {};
131
+ this.__typePaths['other'] = [];
132
+ for (const [pathType, paths] of Object.entries(flatmap.pathways['type-paths'])) {
133
+ if (pathTypes.indexOf(pathType) >= 0) {
134
+ this.__typePaths[pathType] = paths;
135
+ } else {
136
+ this.__typePaths['other'].push(...paths);
137
+ }
138
+ }
124
139
  }
125
140
 
126
141
  addPathsToFeatureSet_(paths, featureSet)
@@ -253,8 +268,8 @@ export class Pathways
253
268
  //======================
254
269
  {
255
270
  const featureIds = new Set();
256
- if (pathType in this._typePaths) {
257
- this.addPathsToFeatureSet_(this._typePaths[pathType], featureIds);
271
+ if (pathType in this.__typePaths) {
272
+ this.addPathsToFeatureSet_(this.__typePaths[pathType], featureIds);
258
273
  }
259
274
  return featureIds;
260
275
  }
package/src/styling.js CHANGED
@@ -373,12 +373,12 @@ export class PathLineLayer extends VectorStyleLayer
373
373
  ],
374
374
  'line-opacity': [
375
375
  'case',
376
+ ['boolean', ['feature-state', 'hidden'], false], 0.1,
376
377
  ['==', ['get', 'type'], 'bezier'], 1.0,
377
378
  ['boolean', ['get', 'invisible'], false], 0.001,
378
379
  ['boolean', ['feature-state', 'selected'], false], 1.0,
379
380
  ['boolean', ['feature-state', 'active'], false], 0.8,
380
- ['boolean', ['feature-state', 'hidden'], false], 0.1,
381
- dimmed ? 0.1 : 0.4
381
+ dimmed ? 0.1 : 0.5
382
382
  ],
383
383
  'line-width': [
384
384
  'let',
@@ -457,17 +457,17 @@ export class FeatureNerveLayer extends VectorStyleLayer
457
457
  'paint': {
458
458
  'line-color': [
459
459
  'case',
460
+ ['boolean', ['feature-state', 'hidden'], false], '#CCC',
460
461
  ['boolean', ['feature-state', 'active'], false], '#222',
461
462
  ['boolean', ['feature-state', 'selected'], false], 'red',
462
- ['boolean', ['feature-state', 'hidden'], false], '#CCC',
463
463
  '#888'
464
464
  ],
465
465
  'line-opacity': [
466
466
  'case',
467
- ['boolean', ['feature-state', 'active'], false], 0.9,
468
- ['boolean', ['feature-state', 'selected'], false], 0.9,
469
467
  ['boolean', ['feature-state', 'hidden'], false], 0.3,
470
468
  ['boolean', ['get', 'invisible'], false], 0.001,
469
+ ['boolean', ['feature-state', 'active'], false], 0.9,
470
+ ['boolean', ['feature-state', 'selected'], false], 0.9,
471
471
  0.9
472
472
  ],
473
473
  'line-dasharray': [2, 1],
@@ -204,6 +204,9 @@ label[for=path-all-paths] {
204
204
  .nerve-lcn {
205
205
  background: #F19E38;
206
206
  }
207
+ .nerve-other {
208
+ background: #888;
209
+ }
207
210
  .nerve-para-pre {
208
211
  background: #3F8F4A;
209
212
  }