@abi-software/flatmap-viewer 2.2.2-beta.4 → 2.2.3

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.2-beta.4``
41
+ * ``npm install @abi-software/flatmap-viewer@2.2.3``
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.2-beta.4",
3
+ "version": "2.2.3",
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
@@ -113,11 +113,11 @@ export class PathControl
113
113
  this._button = document.createElement('button');
114
114
  this._button.id = 'nerve-key-button';
115
115
  this._button.className = 'control-button';
116
- this._button.title = 'Nerve paths legend';
117
116
  this._button.setAttribute('type', 'button');
118
117
  this._button.setAttribute('aria-label', 'Nerve paths legend');
119
118
  this._button.setAttribute('legend-visible', 'false');
120
- this._button.textContent = 'PTH';
119
+ this._button.textContent = 'PATHS';
120
+ this._button.title = 'Show/hide neuron paths';
121
121
  this._container.appendChild(this._button);
122
122
 
123
123
  this._container.addEventListener('click', this.onClick_.bind(this));
@@ -949,11 +949,11 @@ class FlatMap
949
949
  }
950
950
  }
951
951
 
952
- showSearchResults(searchResults, padding=100)
953
- //===========================================
952
+ showSearchResults(searchResults)
953
+ //==============================
954
954
  {
955
955
  if (this._userInteractions !== null) {
956
- this._userInteractions.zoomToFeatures(searchResults.featureIds, {padding: padding});
956
+ this._userInteractions.showSearchResults(searchResults.featureIds);
957
957
  }
958
958
  }
959
959
 
@@ -423,40 +423,6 @@ export class UserInteractions
423
423
  this._modal = false;
424
424
  }
425
425
 
426
- enablePaths_(enable, event)
427
- //=========================
428
- {
429
- this._contextMenu.hide();
430
- const nodeId = event.target.getAttribute('featureId');
431
- this.enablePathFeatures_(enable, this._pathways.pathFeatureIds(nodeId));
432
- this.__clearModal();
433
- }
434
-
435
- enablePathFeatures_(enable, featureIds)
436
- //=====================================
437
- {
438
- for (const featureId of featureIds) {
439
- const feature = this.mapFeature_(featureId);
440
- if (enable) {
441
- this._map.removeFeatureState(feature, 'hidden');
442
- } else {
443
- this._map.setFeatureState(feature, { 'hidden': true });
444
- this._disabledPathFeatures = true;
445
- }
446
- }
447
- }
448
-
449
- togglePaths()
450
- //===========
451
- {
452
- if (this._disabledPathFeatures){
453
- this.enablePathFeatures_(true, this._pathways.allFeatureIds());
454
- this._disabledPathFeatures = false;
455
- } else {
456
- this.enablePathFeatures_(false, this._pathways.allFeatureIds());
457
- }
458
- }
459
-
460
426
  reset()
461
427
  //=====
462
428
  {
@@ -521,6 +487,12 @@ export class UserInteractions
521
487
  }
522
488
  }
523
489
 
490
+ showSearchResults(featureIds)
491
+ //===========================
492
+ {
493
+ this.zoomToFeatures(featureIds, {highlight: true, noZoomIn: true});
494
+ }
495
+
524
496
  /**
525
497
  * Zoom map to features.
526
498
  *
@@ -528,19 +500,23 @@ export class UserInteractions
528
500
  * @param {Object} [options]
529
501
  * @param {boolean} [options.select=true] Select the features zoomed to
530
502
  * @param {boolean} [options.highlight=false] Highlight the features zoomed to
531
- * @param {number} [options.padding=100] Padding around the composite bounding box
503
+ * @param {boolean} [options.noZoomIn=false] Don't zoom in (although zoom out as necessary)
504
+ * @param {number} [options.padding=10] Padding in pixels around the composite bounding box
532
505
  */
533
506
  zoomToFeatures(featureIds, options=null)
534
507
  //======================================
535
508
  {
536
- options = utils.setDefaultOptions(options, {select: true, highlight: false, padding:100});
509
+ options = utils.setDefaultOptions(options, {select: true, highlight: false, noZoomIn: false, padding:10});
537
510
  const select = (options.select === true);
538
511
  const highlight = (options.highlight === true);
539
- const padding = options.padding || 100;
540
512
  if (featureIds.length) {
541
513
  this.unhighlightFeatures_();
542
514
  if (select) this.__unselectFeatures();
543
515
  let bbox = null;
516
+ if (options.noZoomIn) {
517
+ const bounds = this._map.getBounds().toArray();
518
+ bbox = [...bounds[0], ...bounds[1]];
519
+ }
544
520
  for (const featureId of featureIds) {
545
521
  const annotation = this._flatmap.annotation(featureId);
546
522
  if (annotation) {
@@ -565,7 +541,7 @@ export class UserInteractions
565
541
  }
566
542
  if (bbox !== null) {
567
543
  this._map.fitBounds(bbox, {
568
- padding: padding,
544
+ padding: options.padding,
569
545
  animate: false
570
546
  });
571
547
  }
@@ -640,14 +616,17 @@ export class UserInteractions
640
616
  const tooltips = [];
641
617
  for (const lineFeature of lineFeatures) {
642
618
  const properties = lineFeature.properties;
643
- if ('label' in properties
644
- && (!('tooltip' in properties) || properties.tooltip)
645
- && !('labelled' in properties)) {
646
- let tooltip = '';
647
- const label = properties.label;
648
- const cleanLabel = (label.substr(0, 1).toUpperCase() + label.substr(1)).replaceAll("\n", "<br/>");
649
- if (!tooltips.includes(cleanLabel)) {
650
- tooltips.push(cleanLabel);
619
+ if (this.__enabledFeature(lineFeature)) {
620
+ const properties = lineFeature.properties;
621
+ if ('label' in properties
622
+ && (!('tooltip' in properties) || properties.tooltip)
623
+ && !('labelled' in properties)) {
624
+ let tooltip = '';
625
+ const label = properties.label;
626
+ const cleanLabel = (label.substr(0, 1).toUpperCase() + label.substr(1)).replaceAll("\n", "<br/>");
627
+ if (!tooltips.includes(cleanLabel)) {
628
+ tooltips.push(cleanLabel);
629
+ }
651
630
  }
652
631
  }
653
632
  }
@@ -682,10 +661,13 @@ export class UserInteractions
682
661
  __featureEvent(type, feature)
683
662
  //===========================
684
663
  {
685
- if (feature.sourceLayer === PATHWAYS_LAYER) {
686
- return this._flatmap.featureEvent(type, this._pathways.pathProperties(feature));
687
- } else if ('properties' in feature) {
688
- return this._flatmap.featureEvent(type, feature.properties);
664
+ if (this.__enabledFeature(feature)) {
665
+ if (feature.sourceLayer === PATHWAYS_LAYER) { // I suspect this is never true as source layer
666
+ // names are like `neural_routes_pathways`
667
+ return this._flatmap.featureEvent(type, this._pathways.pathProperties(feature));
668
+ } else if ('properties' in feature) {
669
+ return this._flatmap.featureEvent(type, feature.properties);
670
+ }
689
671
  }
690
672
  return false;
691
673
  }
@@ -754,18 +736,22 @@ export class UserInteractions
754
736
  }
755
737
  info = this._infoControl.featureInformation(features, event.lngLat);
756
738
  }
757
- const lineFeatures = features.filter(feature => (('type' in feature.properties
758
- && feature.properties.type.startsWith('line'))
759
- || 'centreline' in feature.properties));
739
+ const lineFeatures = features.filter(feature => ('centreline' in feature.properties
740
+ || ('type' in feature.properties
741
+ && feature.properties.type.startsWith('line')) ));
760
742
  if (lineFeatures.length > 0) {
761
743
  tooltip = this.lineTooltip_(lineFeatures);
762
- for (const lineFeature of lineFeatures) {
763
- const lineFeatureId = +lineFeature.properties.featureId; // Ensure numeric
764
- this.activateFeature_(lineFeature);
765
- const lineIds = new Set(lineFeatures.map(f => f.properties.featureId));
766
- for (const featureId of this._pathways.lineFeatureIds(lineIds)) {
767
- if (+featureId !== lineFeatureId) {
768
- this.activateFeature_(this.mapFeature_(featureId));
744
+ const enabledFeatures = lineFeatures.filter(feature => this.__enabledFeature(feature));
745
+ if (enabledFeatures.length > 0) {
746
+ tooltip = this.lineTooltip_(enabledFeatures);
747
+ for (const lineFeature of enabledFeatures) {
748
+ const lineFeatureId = +lineFeature.properties.featureId; // Ensure numeric
749
+ this.activateFeature_(lineFeature);
750
+ const lineIds = new Set(enabledFeatures.map(f => f.properties.featureId));
751
+ for (const featureId of this._pathways.lineFeatureIds(lineIds)) {
752
+ if (+featureId !== lineFeatureId) {
753
+ this.activateFeature_(this.mapFeature_(featureId));
754
+ }
769
755
  }
770
756
  }
771
757
  }
@@ -816,12 +802,8 @@ export class UserInteractions
816
802
  if ('nerveId' in feature.properties) {
817
803
  if (feature.properties.active) {
818
804
  this.activateFeature_(feature);
819
- } else {
820
- tooltip = '';
821
- }
822
- if (feature.properties.nerveId !== feature.properties.featureId) {
823
- this.activateNerveFeatures_(feature.properties.nerveId);
824
805
  }
806
+ this.activateNerveFeatures_(feature.properties.nerveId);
825
807
  } else {
826
808
  this.activateFeature_(feature);
827
809
  }
@@ -855,8 +837,8 @@ export class UserInteractions
855
837
  }
856
838
  }
857
839
 
858
- selectionEvent_(domEvent, feature)
859
- //================================
840
+ selectionEvent_(event, feature)
841
+ //=============================
860
842
  {
861
843
  const multipleSelect = event.ctrlKey || event.metaKey;
862
844
  if (!multipleSelect) {
@@ -891,18 +873,29 @@ export class UserInteractions
891
873
  //================
892
874
  {
893
875
  this.clearActiveMarker_();
894
- const feature = this._activeFeatures[0]
895
- this.selectionEvent_(event.originalEvent, feature);
876
+ const clickedFeature = this._map.queryRenderedFeatures(event.point)[0];
877
+ const originalEvent = event.originalEvent;
878
+ if (clickedFeature === undefined || this._activeFeatures.length === 1) {
879
+ this.selectionEvent_(originalEvent, clickedFeature);
880
+ } else if (this._activeFeatures.length > 1) {
881
+ const multipleSelect = originalEvent.ctrlKey || originalEvent.metaKey;
882
+ if (!multipleSelect) {
883
+ this.__unselectFeatures();
884
+ }
885
+ for (const feature of this._activeFeatures) {
886
+ this.selectFeature_(feature.id);
887
+ }
888
+ }
896
889
  if (this._modal) {
897
890
  // Remove tooltip, reset active features, etc
898
891
  this.__resetFeatureDisplay();
899
892
  this.__unselectFeatures();
900
893
  this.__clearModal();
901
- } else if (feature !== undefined) {
894
+ } else if (clickedFeature !== undefined) {
902
895
  this.__lastClickLngLat = event.lngLat;
903
- this.__featureEvent('click', feature);
904
- if ('hyperlink' in feature.properties) {
905
- window.open(feature.properties.hyperlink, '_blank');
896
+ this.__featureEvent('click', clickedFeature);
897
+ if ('properties' in clickedFeature && 'hyperlink' in clickedFeature.properties) {
898
+ window.open(clickedFeature.properties.hyperlink, '_blank');
906
899
  }
907
900
  }
908
901
  }
@@ -915,6 +908,47 @@ export class UserInteractions
915
908
  }
916
909
  }
917
910
 
911
+ __enabledFeature(feature)
912
+ //=======================
913
+ {
914
+ const state = this._map.getFeatureState(feature);
915
+ return !(('hidden' in state && state.hidden));
916
+ }
917
+
918
+ enablePaths_(enable, event)
919
+ //=========================
920
+ {
921
+ this._contextMenu.hide();
922
+ const nodeId = event.target.getAttribute('featureId');
923
+ this.enablePathFeatures_(enable, this._pathways.pathFeatureIds(nodeId));
924
+ this.__clearModal();
925
+ }
926
+
927
+ enablePathFeatures_(enable, featureIds)
928
+ //=====================================
929
+ {
930
+ for (const featureId of featureIds) {
931
+ const feature = this.mapFeature_(featureId);
932
+ if (enable) {
933
+ this._map.removeFeatureState(feature, 'hidden');
934
+ } else {
935
+ this._map.setFeatureState(feature, { 'hidden': true });
936
+ this._disabledPathFeatures = true;
937
+ }
938
+ }
939
+ }
940
+
941
+ togglePaths()
942
+ //===========
943
+ {
944
+ if (this._disabledPathFeatures){
945
+ this.enablePathFeatures_(true, this._pathways.allFeatureIds());
946
+ this._disabledPathFeatures = false;
947
+ } else {
948
+ this.enablePathFeatures_(false, this._pathways.allFeatureIds());
949
+ }
950
+ }
951
+
918
952
  enablePath(pathType, enable=true)
919
953
  //===============================
920
954
  {
package/src/styling.js CHANGED
@@ -117,7 +117,6 @@ export class FeatureFillLayer extends VectorStyleLayer
117
117
  const paintStyle = {
118
118
  'fill-color': [
119
119
  'case',
120
- ['boolean', ['feature-state', 'selected'], false], '#0F0',
121
120
  ['has', 'colour'], ['get', 'colour'],
122
121
  ['boolean', ['feature-state', 'active'], false], coloured ? '#D88' : '#CCC',
123
122
  ['any',
@@ -134,10 +133,10 @@ export class FeatureFillLayer extends VectorStyleLayer
134
133
  ['==', ['get', 'kind'], 'cell-type'],
135
134
  ], 0.1,
136
135
  ['has', 'node'], 0.3,
137
- ['boolean', ['feature-state', 'selected'], false], 1.0,
138
136
  ['boolean', ['feature-state', 'active'], false], 0.8,
137
+ ['boolean', ['feature-state', 'selected'], false], 0.01,
139
138
  ['has', 'colour'], 0.008,
140
- (coloured && !dimmed) ? 0.01 : 0.5
139
+ (coloured && !dimmed) ? 0.01 : 0.1
141
140
  ]
142
141
  };
143
142
  return super.changedPaintStyle(paintStyle, changes);
@@ -198,18 +197,18 @@ export class FeatureBorderLayer extends VectorStyleLayer
198
197
  }
199
198
  lineOpacity.push(['boolean', ['feature-state', 'selected'], false]);
200
199
  lineOpacity.push(0.9);
201
- lineOpacity.push((outlined && !dimmed) ? 0.3 : 0.01);
200
+ lineOpacity.push((outlined && !dimmed) ? 0.3 : 0.1);
202
201
 
203
202
  const lineWidth = [
204
203
  'case',
205
204
  ['boolean', ['get', 'invisible'], false], 0.2,
206
205
  ];
206
+ lineWidth.push(['boolean', ['feature-state', 'selected'], false]);
207
+ lineWidth.push(2.5);
207
208
  if (coloured && outlined) {
208
209
  lineWidth.push(['boolean', ['feature-state', 'active'], false]);
209
210
  lineWidth.push(1);
210
211
  }
211
- lineWidth.push(['boolean', ['feature-state', 'selected'], false]);
212
- lineWidth.push(1.5);
213
212
  lineWidth.push((coloured && outlined) ? 0.5 : 0.1);
214
213
 
215
214
  return super.changedPaintStyle({
@@ -359,6 +358,8 @@ export class PathLineLayer extends VectorStyleLayer
359
358
  'case',
360
359
  ['boolean', ['feature-state', 'hidden'], false], '#CCC',
361
360
  ['==', ['get', 'type'], 'bezier'], 'red',
361
+ ['==', ['get', 'kind'], 'error'], '#FFFE0E',
362
+ ['==', ['get', 'kind'], 'unknown'], '#FFC1DE',
362
363
  ['==', ['get', 'kind'], 'cns'], '#9B1FC1',
363
364
  ['==', ['get', 'kind'], 'lcn'], '#F19E38',
364
365
  ['==', ['get', 'kind'], 'para-post'], '#3F8F4A',
@@ -383,6 +384,8 @@ export class PathLineLayer extends VectorStyleLayer
383
384
  'width', [
384
385
  'case',
385
386
  ['==', ['get', 'type'], 'bezier'], 0.1,
387
+ ['==', ['get', 'kind'], 'error'], 1,
388
+ ['==', ['get', 'kind'], 'unknown'], 1,
386
389
  ['boolean', ['get', 'invisible'], false], 0.1,
387
390
  ['boolean', ['feature-state', 'selected'], false], 1.2,
388
391
  ['boolean', ['feature-state', 'active'], false], 0.9,
@@ -456,8 +459,7 @@ export class FeatureNerveLayer extends VectorStyleLayer
456
459
  ['boolean', ['feature-state', 'active'], false], '#222',
457
460
  ['boolean', ['feature-state', 'selected'], false], 'red',
458
461
  ['boolean', ['feature-state', 'hidden'], false], '#CCC',
459
- ['boolean', ['get', 'active'], false], '#888',
460
- '#FFF'
462
+ '#888'
461
463
  ],
462
464
  'line-opacity': [
463
465
  'case',