@abi-software/flatmap-viewer 2.2.1-beta.1 → 2.2.1-beta.10

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
@@ -2,7 +2,7 @@
2
2
  Flatmap Viewer
3
3
  ==============
4
4
 
5
- A viewer for anatomical flatmaps generated by `flatmap-maker <https://github.com/dbrnz/flatmap-maker>`_. The viewer is intended to be a component of a larger Javascript web application, although may be used standalone for local flatmap development and testing. Flatmap content is obtained from a `flatmap-server <https://github.com/dbrnz/flatmap-server>`_.
5
+ A viewer for anatomical flatmaps generated by `flatmap-maker <https://github.com/AnatomicMaps/flatmap-maker>`_. The viewer is intended to be a component of a larger Javascript web application, although may be used standalone for local flatmap development and testing. Flatmap content is obtained from a `flatmap-server <https://github.com/AnatomicMaps/flatmap-server>`_.
6
6
 
7
7
 
8
8
  Standalone Use
@@ -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.1-beta.1``
41
+ * ``npm install @abi-software/flatmap-viewer@2.2.1-beta.10``
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.1-beta.1",
3
+ "version": "2.2.1-beta.10",
4
4
  "description": "Flatmap viewer using Maplibre GL",
5
5
  "repository": "https://github.com/AnatomicMaps/flatmap-viewer.git",
6
6
  "main": "src/main.js",
@@ -21,6 +21,7 @@
21
21
  "@turf/area": "^6.0.1",
22
22
  "@turf/bbox": "^6.0.1",
23
23
  "@turf/helpers": "^6.1.4",
24
+ "bezier-js": "^6.1.0",
24
25
  "maplibre-gl": ">=1.15.3",
25
26
  "minisearch": "^2.2.1",
26
27
  "polylabel": "^1.1.0"
@@ -1191,6 +1191,12 @@ export class MapManager
1191
1191
  mapOptions['pathControls'] = true;
1192
1192
  }
1193
1193
 
1194
+ // Mapmaker's changed the name of the field to indicate that indicates if
1195
+ // there are raster layers
1196
+ if (!('image-layers' in mapIndex) && ('image_layer' in mapIndex)) {
1197
+ mapIndex['image-layers'] = mapIndex['image_layer'];
1198
+ }
1199
+
1194
1200
  // Get details about the map's layers
1195
1201
 
1196
1202
  let mapLayers = [];
@@ -1259,7 +1265,13 @@ export class MapManager
1259
1265
  outline: true
1260
1266
  };
1261
1267
  }
1262
- mapOptions.layerOptions.authoring = ('authoring' in mapIndex && mapIndex.authoring);
1268
+ if ('authoring' in mapIndex) {
1269
+ mapOptions.layerOptions.style == 'authoring'
1270
+ } else if ('style' in mapIndex) {
1271
+ mapOptions.layerOptions.style = mapIndex.style;
1272
+ } else {
1273
+ mapOptions.layerOptions.style = 'flatmap';
1274
+ }
1263
1275
 
1264
1276
  // Are features in separate vector tile source layers?
1265
1277
 
@@ -162,34 +162,6 @@ export class UserInteractions
162
162
 
163
163
  this._layerManager = new LayerManager(flatmap);
164
164
 
165
- // Add the map's layers
166
-
167
- // Layers have an id, either layer-N or an assigned name
168
- // Some layers might have a description. These are the selectable layers,
169
- // unless they are flagged as `no-select`
170
- // Selectable layers have opacity 0 unless active, in which case they have opacity 1.
171
- // `no-select` layers have opacity 0.5
172
- // Background layer has opacity 0.2
173
-
174
- const layersById = new Map();
175
- const layerBackgroundIds = [];
176
- for (const layer of flatmap.layers) {
177
- layer.backgroundLayers = [];
178
- layersById.set(layer.id, layer);
179
- }
180
- for (const layer of flatmap.layers) {
181
- if (layer.background_for) {
182
- const l = layersById.get(layer.background_for);
183
- l.backgroundLayers.push(layer);
184
- layerBackgroundIds.push(layer.id);
185
- }
186
- }
187
- for (const layer of flatmap.layers) {
188
- if (layerBackgroundIds.indexOf(layer.id) < 0) {
189
- this._layerManager.addLayer(layer, flatmap.options.layerOptions);
190
- }
191
- }
192
-
193
165
  // Flag features that have annotations
194
166
  // Also flag those features that are models of something
195
167
 
@@ -205,7 +177,7 @@ export class UserInteractions
205
177
  // Display a context menu on right-click
206
178
 
207
179
  this._lastContextTime = 0;
208
- this._contextMenu = new ContextMenu(flatmap, this.clearModal_.bind(this));
180
+ this._contextMenu = new ContextMenu(flatmap, this.__clearModal.bind(this));
209
181
  this._map.on('contextmenu', this.contextMenuEvent_.bind(this));
210
182
 
211
183
  // Display a context menu with a touch longer than 0.5 second
@@ -332,8 +304,8 @@ export class UserInteractions
332
304
  }
333
305
  }
334
306
 
335
- unselectFeatures_()
336
- //=================
307
+ __unselectFeatures()
308
+ //==================
337
309
  {
338
310
  for (const featureId of this._selectedFeatureIds.keys()) {
339
311
  const feature = this.mapFeature_(featureId);
@@ -449,8 +421,8 @@ export class UserInteractions
449
421
  this._modal = true;
450
422
  }
451
423
 
452
- clearModal_(event)
453
- //================
424
+ __clearModal(event)
425
+ //=================
454
426
  {
455
427
  this._modal = false;
456
428
  }
@@ -461,7 +433,7 @@ export class UserInteractions
461
433
  this._contextMenu.hide();
462
434
  const nodeId = event.target.getAttribute('featureId');
463
435
  this.enablePathFeatures_(enable, this._pathways.pathFeatureIds(nodeId));
464
- this.clearModal_();
436
+ this.__clearModal();
465
437
  }
466
438
 
467
439
  enablePathFeatures_(enable, featureIds)
@@ -492,9 +464,9 @@ export class UserInteractions
492
464
  reset()
493
465
  //=====
494
466
  {
495
- this.clearModal_();
467
+ this.__clearModal();
496
468
  this.clearActiveMarker_();
497
- this.unselectFeatures_();
469
+ this.__unselectFeatures();
498
470
  this.enablePathFeatures_(true, this._pathways.allFeatureIds());
499
471
  this._disabledPathFeatures = false;
500
472
  }
@@ -502,7 +474,7 @@ export class UserInteractions
502
474
  clearSearchResults(reset=true)
503
475
  //============================
504
476
  {
505
- this.unselectFeatures_();
477
+ this.__unselectFeatures();
506
478
  }
507
479
 
508
480
  /**
@@ -538,7 +510,7 @@ export class UserInteractions
538
510
  //========================
539
511
  {
540
512
  if (featureIds.length) {
541
- this.unselectFeatures_();
513
+ this.__unselectFeatures();
542
514
  for (const featureId of featureIds) {
543
515
  const annotation = this._flatmap.annotation(featureId);
544
516
  if (annotation) {
@@ -571,7 +543,7 @@ export class UserInteractions
571
543
  const padding = options.padding || 100;
572
544
  if (featureIds.length) {
573
545
  this.unhighlightFeatures_();
574
- if (select) this.unselectFeatures_();
546
+ if (select) this.__unselectFeatures();
575
547
  let bbox = null;
576
548
  for (const featureId of featureIds) {
577
549
  const annotation = this._flatmap.annotation(featureId);
@@ -618,7 +590,7 @@ export class UserInteractions
618
590
 
619
591
  // Highlight the feature
620
592
 
621
- this.unselectFeatures_();
593
+ this.__unselectFeatures();
622
594
  this.selectFeature_(featureId);
623
595
 
624
596
  // Find the pop-up's postion
@@ -640,7 +612,7 @@ export class UserInteractions
640
612
  }
641
613
  this.setModal_();
642
614
  this._currentPopup = new maplibre.Popup(options).addTo(this._map);
643
- this._currentPopup.on('close', this.clearModal_.bind(this));
615
+ this._currentPopup.on('close', this.__clearPopup.bind(this));
644
616
  this._currentPopup.setLngLat(location);
645
617
  if (typeof content === 'object') {
646
618
  this._currentPopup.setDOMContent(content);
@@ -650,6 +622,13 @@ export class UserInteractions
650
622
  }
651
623
  }
652
624
 
625
+ __clearPopup()
626
+ //============
627
+ {
628
+ this.__clearModal();
629
+ this.__unselectFeatures();
630
+ }
631
+
653
632
  removeTooltip_()
654
633
  //==============
655
634
  {
@@ -659,15 +638,47 @@ export class UserInteractions
659
638
  }
660
639
  }
661
640
 
641
+ lineTooltip_(lineFeatures)
642
+ //========================
643
+ {
644
+ const tooltips = [];
645
+ for (const lineFeature of lineFeatures) {
646
+ const properties = lineFeature.properties;
647
+ if ('label' in properties
648
+ && (!('tooltip' in properties) || properties.tooltip)
649
+ && !('labelled' in properties)) {
650
+ let tooltip = '';
651
+ const label = properties.label;
652
+ const cleanLabel = (label.substr(0, 1).toUpperCase() + label.substr(1)).replaceAll("\n", "<br/>");
653
+ if (!tooltips.includes(cleanLabel)) {
654
+ tooltips.push(cleanLabel);
655
+ }
656
+ }
657
+ }
658
+ if (tooltips.length === 0) {
659
+ return '';
660
+ }
661
+ return `<div class='flatmap-feature-label'>${tooltips.join('<hr/>')}</div>`;
662
+ }
663
+
662
664
  tooltipHtml_(properties, forceLabel=false)
663
665
  //========================================
664
666
  {
665
- if ('label' in properties
667
+ if (('label' in properties || 'hyperlink' in properties)
666
668
  && (forceLabel || !('tooltip' in properties) || properties.tooltip)
667
669
  && !('labelled' in properties)) {
668
- const label = properties.label;
669
- const capitalisedLabel = label.substr(0, 1).toUpperCase() + label.substr(1);
670
- return `<div class='flatmap-feature-label'>${capitalisedLabel.replaceAll("\n", "<br/>")}</div>`;
670
+ let tooltip = '';
671
+ if ('label' in properties) {
672
+ const label = properties.label;
673
+ tooltip = (label.substr(0, 1).toUpperCase() + label.substr(1)).replaceAll("\n", "<br/>");
674
+ } else {
675
+ tooltip = properties.hyperlink
676
+ }
677
+ if ('hyperlink' in properties) {
678
+ return `<div class='flatmap-feature-label'><a href='{properties.hyperlink}'>${tooltip}</a></div>`;
679
+ } else {
680
+ return `<div class='flatmap-feature-label'>${tooltip}</div>`;
681
+ }
671
682
  }
672
683
  return '';
673
684
  }
@@ -683,36 +694,37 @@ export class UserInteractions
683
694
  return false;
684
695
  }
685
696
 
686
- mouseMoveEvent_(event)
687
- //====================
697
+ __resetFeatureDisplay()
698
+ //=====================
688
699
  {
689
- // No tooltip when context menu is open
690
-
691
- if (this._modal) {
692
- return;
693
- }
694
-
695
700
  // Remove any existing tooltip
696
-
697
701
  this.removeTooltip_();
698
702
 
699
703
  // Reset cursor
700
-
701
704
  this._map.getCanvas().style.cursor = 'default';
702
705
 
703
706
  // Reset any active features
704
-
705
707
  this.resetActiveFeatures_();
708
+ }
706
709
 
707
- // Reset any info display
710
+ mouseMoveEvent_(event)
711
+ //====================
712
+ {
713
+ // No tooltip when context menu is open
714
+ if (this._modal) {
715
+ return;
716
+ }
708
717
 
718
+ // Remove tooltip, reset active features, etc
719
+ this.__resetFeatureDisplay();
720
+
721
+ // Reset any info display
709
722
  const displayInfo = (this._infoControl && this._infoControl.active);
710
723
  if (displayInfo) {
711
724
  this._infoControl.reset()
712
725
  }
713
726
 
714
727
  // Get all the features at the current point
715
-
716
728
  const features = this._map.queryRenderedFeatures(event.point);
717
729
  if (features.length === 0) {
718
730
  this._lastFeatureMouseEntered = null;
@@ -750,18 +762,20 @@ export class UserInteractions
750
762
  && feature.properties.type.startsWith('line'))
751
763
  || 'centreline' in feature.properties));
752
764
  if (lineFeatures.length > 0) {
753
- const lineFeature = lineFeatures[0];
754
- const lineFeatureId = +lineFeature.properties.featureId; // Ensure numeric
755
- tooltip = this.tooltipHtml_(lineFeature.properties);
756
- this.activateFeature_(lineFeature);
757
- const lineIds = new Set(lineFeatures.map(f => f.properties.featureId));
758
- for (const featureId of this._pathways.lineFeatureIds(lineIds)) {
759
- if (+featureId !== lineFeatureId) {
760
- this.activateFeature_(this.mapFeature_(featureId));
765
+ tooltip = this.lineTooltip_(lineFeatures);
766
+ for (const lineFeature of lineFeatures) {
767
+ const lineFeatureId = +lineFeature.properties.featureId; // Ensure numeric
768
+ this.activateFeature_(lineFeature);
769
+ const lineIds = new Set(lineFeatures.map(f => f.properties.featureId));
770
+ for (const featureId of this._pathways.lineFeatureIds(lineIds)) {
771
+ if (+featureId !== lineFeatureId) {
772
+ this.activateFeature_(this.mapFeature_(featureId));
773
+ }
761
774
  }
762
775
  }
763
776
  } else {
764
- let labelledFeatures = features.filter(feature => (('label' in feature.properties
777
+ let labelledFeatures = features.filter(feature => (('hyperlink' in feature.properties
778
+ || 'label' in feature.properties
765
779
  || 'node' in feature.properties)
766
780
  && (!('tooltip' in feature.properties)
767
781
  || feature.properties.tooltip)))
@@ -803,11 +817,20 @@ export class UserInteractions
803
817
  }
804
818
  info = `<div id="info-control-info">${htmlList.join('\n')}</div>`;
805
819
  }
806
- this.activateFeature_(feature);
807
820
  if ('nerveId' in feature.properties) {
821
+ if (feature.properties.active) {
822
+ this.activateFeature_(feature);
823
+ } else {
824
+ tooltip = '';
825
+ }
808
826
  if (feature.properties.nerveId !== feature.properties.featureId) {
809
827
  this.activateNerveFeatures_(feature.properties.nerveId);
810
828
  }
829
+ } else {
830
+ this.activateFeature_(feature);
831
+ }
832
+ if ('hyperlink' in feature.properties) {
833
+ this._map.getCanvas().style.cursor = 'pointer';
811
834
  }
812
835
  }
813
836
  }
@@ -841,7 +864,7 @@ export class UserInteractions
841
864
  {
842
865
  const multipleSelect = event.ctrlKey || event.metaKey;
843
866
  if (!multipleSelect) {
844
- this.unselectFeatures_();
867
+ this.__unselectFeatures();
845
868
  }
846
869
  if (feature !== undefined) {
847
870
  const featureId = feature.id;
@@ -874,9 +897,17 @@ export class UserInteractions
874
897
  this.clearActiveMarker_();
875
898
  const feature = this._activeFeatures[0]
876
899
  this.selectionEvent_(event.originalEvent, feature);
877
- if (feature !== undefined) {
900
+ if (this._modal) {
901
+ // Remove tooltip, reset active features, etc
902
+ this.__resetFeatureDisplay();
903
+ this.__unselectFeatures();
904
+ this.__clearModal();
905
+ } else if (feature !== undefined) {
878
906
  this.__lastClickLngLat = event.lngLat;
879
907
  this.__featureEvent('click', feature);
908
+ if ('hyperlink' in feature.properties) {
909
+ window.open(feature.properties.hyperlink, '_blank');
910
+ }
880
911
  }
881
912
  }
882
913
 
package/src/layers.js CHANGED
@@ -33,7 +33,7 @@ const FEATURES_LAYER = 'features'
33
33
 
34
34
  class MapFeatureLayer
35
35
  {
36
- constructor(flatmap, layer, options)
36
+ constructor(flatmap, layer, background_layers=true)
37
37
  {
38
38
  this.__map = flatmap.map;
39
39
  this.__separateLayers = flatmap.options.separateLayers;
@@ -41,6 +41,7 @@ class MapFeatureLayer
41
41
  this.__rasterLayers = [];
42
42
  this.__styleLayers = [];
43
43
 
44
+ const layerOptions = flatmap.options.layerOptions;
44
45
  const vectorTileSource = this.__map.getSource('vector-tiles');
45
46
  const haveVectorLayers = (typeof vectorTileSource !== 'undefined');
46
47
  const featuresVectorLayerId = this.__separateLayers
@@ -48,33 +49,36 @@ class MapFeatureLayer
48
49
  : FEATURES_LAYER;
49
50
  const vectorFeatures = haveVectorLayers
50
51
  && vectorTileSource.vectorLayerIds.indexOf(featuresVectorLayerId) >= 0;
51
- if (vectorFeatures) {
52
- this.__addStyleLayer(style.BodyLayer, options);
53
- }
54
- if (flatmap.details['image_layer']) {
55
- for (const raster_layer_id of layer['image-layers']) {
56
- const layerId = this.__addRasterLayer(raster_layer_id, options);
52
+ if (background_layers) {
53
+ if (vectorFeatures) {
54
+ this.__addStyleLayer(style.BodyLayer, layerOptions);
55
+ }
56
+ if (flatmap.details['image-layers']) {
57
+ for (const raster_layer_id of layer['image-layers']) {
58
+ this.__addRasterLayer(raster_layer_id, layerOptions);
59
+ }
57
60
  }
58
61
  }
59
62
  // if no image layers then make feature borders (and lines?) more visible...??
60
63
  if (haveVectorLayers) {
61
64
  if (vectorFeatures) {
62
- this.__addStyleLayer(style.FeatureFillLayer, options);
63
- this.__addStyleLayer(style.FeatureLineLayer, options);
64
- this.__addStyleLayer(style.FeatureBorderLayer, options);
65
+ this.__addStyleLayer(style.FeatureFillLayer, layerOptions);
66
+ this.__addStyleLayer(style.FeatureDashLineLayer, layerOptions);
67
+ this.__addStyleLayer(style.FeatureLineLayer, layerOptions);
68
+ this.__addStyleLayer(style.FeatureBorderLayer, layerOptions);
65
69
  }
66
- this.__addPathwayStyleLayers(options);
70
+ this.__addPathwayStyleLayers(layerOptions);
67
71
  if (vectorFeatures) {
68
- this.__addStyleLayer(style.FeatureLargeSymbolLayer, options);
72
+ this.__addStyleLayer(style.FeatureLargeSymbolLayer, layerOptions);
69
73
  if (!flatmap.options.tooltips) {
70
- this.__addStyleLayer(style.FeatureSmallSymbolLayer, options);
74
+ this.__addStyleLayer(style.FeatureSmallSymbolLayer, layerOptions);
71
75
  }
72
76
  }
73
77
  }
74
78
 
75
- // Make sure our colpur options are set properly, in particular raster layer visibility
79
+ // Make sure our colour options are set properly, in particular raster layer visibility
76
80
 
77
- this.setColour(options);
81
+ this.setColour(layerOptions);
78
82
  }
79
83
 
80
84
  get id()
@@ -147,12 +151,31 @@ export class LayerManager
147
151
  this.__mapLayers = new Map;
148
152
  this.__activeLayers = [];
149
153
  this.__activeLayerNames = [];
154
+ this.__rasterLayers = [];
155
+ const layerOptions = flatmap.options.layerOptions;
156
+ const fcDiagram = ('style' in layerOptions && layerOptions.style == 'fcdiagram');
150
157
  const backgroundLayer = new style.BackgroundLayer();
151
- if ('background' in flatmap.options) {
158
+ if (fcDiagram) {
159
+ this.__map.addLayer(backgroundLayer.style('black', 1));
160
+ }
161
+ else if ('background' in flatmap.options) {
152
162
  this.__map.addLayer(backgroundLayer.style(flatmap.options.background));
153
163
  } else {
154
164
  this.__map.addLayer(backgroundLayer.style('white'));
155
165
  }
166
+ // Add the map's layers
167
+ if (fcDiagram && flatmap.details['image-layers']) {
168
+ for (const layer of flatmap.layers) {
169
+ for (const raster_layer_id of layer['image-layers']) {
170
+ const rasterLayer = new style.RasterLayer(raster_layer_id);
171
+ this.__map.addLayer(rasterLayer.style(layerOptions));
172
+ this.__rasterLayers.push(rasterLayer);
173
+ }
174
+ }
175
+ }
176
+ for (const layer of flatmap.layers) {
177
+ this.addLayer(layer, !fcDiagram);
178
+ }
156
179
  }
157
180
 
158
181
  get activeLayerNames()
@@ -161,12 +184,12 @@ export class LayerManager
161
184
  return this.__activeLayerNames;
162
185
  }
163
186
 
164
- addLayer(layer, options)
165
- //======================
187
+ addLayer(layer, background_layers=true)
188
+ //=====================================
166
189
  {
167
190
  this.__mapLayers.set(layer.id, layer);
168
191
 
169
- const layers = new MapFeatureLayer(this.__flatmap, layer, options);
192
+ const layers = new MapFeatureLayer(this.__flatmap, layer, background_layers);
170
193
  const layerId = this.__flatmap.mapLayerId(layer.id);
171
194
  this.__layers.set(layerId, layers);
172
195
  }
package/src/styling.js CHANGED
@@ -117,12 +117,12 @@ export class FeatureFillLayer extends VectorStyleLayer
117
117
  const paintStyle = {
118
118
  'fill-color': [
119
119
  'case',
120
- ['boolean', ['feature-state', 'active'], false], coloured ? '#D88' : '#CCC',
121
120
  ['boolean', ['feature-state', 'selected'], false], '#0F0',
121
+ ['has', 'colour'], ['get', 'colour'],
122
+ ['boolean', ['feature-state', 'active'], false], coloured ? '#D88' : '#CCC',
122
123
  ['any',
123
124
  ['==', ['get', 'kind'], 'scaffold']
124
125
  ], 'white',
125
- ['has', 'colour'], ['get', 'colour'],
126
126
  ['has', 'node'], '#AFA202',
127
127
  'white' // background colour? body colour ??
128
128
  ],
@@ -133,10 +133,10 @@ export class FeatureFillLayer extends VectorStyleLayer
133
133
  ['==', ['get', 'kind'], 'tissue'],
134
134
  ['==', ['get', 'kind'], 'cell-type'],
135
135
  ], 0.1,
136
- ['has', 'colour'], 0.008,
137
136
  ['has', 'node'], 0.3,
138
137
  ['boolean', ['feature-state', 'selected'], false], 1.0,
139
138
  ['boolean', ['feature-state', 'active'], false], 0.8,
139
+ ['has', 'colour'], 0.008,
140
140
  (coloured && !dimmed) ? 0.01 : 0.5
141
141
  ]
142
142
  };
@@ -236,68 +236,105 @@ export class FeatureBorderLayer extends VectorStyleLayer
236
236
 
237
237
  export class FeatureLineLayer extends VectorStyleLayer
238
238
  {
239
- constructor(id, sourceLayer)
239
+ constructor(id, sourceLayer, dashed=false)
240
240
  {
241
- super(id, 'divider-line', sourceLayer);
241
+ const filterType = dashed ? 'line-dash' : 'line';
242
+ super(id, `feature-${filterType}`, sourceLayer);
243
+ this.__filter = dashed ?
244
+ [
245
+ 'any',
246
+ ['==', 'type', `line-dash`]
247
+ ]
248
+ :
249
+ [
250
+ 'any',
251
+ ['has', 'centreline'],
252
+ ['==', 'type', 'bezier'],
253
+ ['==', 'type', `line`]
254
+ ];
255
+ this.__dashed = dashed;
242
256
  }
243
257
 
244
- style(options)
258
+ paintStyle(options)
245
259
  {
246
260
  const coloured = !('colour' in options) || options.colour;
261
+ const paintStyle = {
262
+ 'line-color': [
263
+ 'case',
264
+ ['boolean', ['feature-state', 'selected'], false], '#0F0',
265
+ ['has', 'colour'], ['get', 'colour'],
266
+ ['boolean', ['feature-state', 'active'], false], coloured ? '#888' : '#CCC',
267
+ ['==', ['get', 'type'], 'network'], '#AFA202',
268
+ ['has', 'centreline'], '#888',
269
+ ('style' in options && options.style === 'authoring') ? '#C44' : '#444'
270
+ ],
271
+ 'line-opacity': [
272
+ 'case',
273
+ ['boolean', ['feature-state', 'selected'], false], 1.0,
274
+ ['boolean', ['feature-state', 'active'], false], 1.0,
275
+ 0.3
276
+ ],
277
+ 'line-width': [
278
+ 'let',
279
+ 'width', [
280
+ 'case',
281
+ ['has', 'centreline'], 1.2,
282
+ ['==', ['get', 'type'], 'network'], 1.2,
283
+ ['boolean', ['feature-state', 'selected'], false], 1.2,
284
+ ['boolean', ['feature-state', 'active'], false], 1.2,
285
+ ('style' in options && options.style === 'authoring') ? 0.7 : 0.5
286
+ ], [
287
+ 'interpolate',
288
+ ['exponential', 2],
289
+ ['zoom'],
290
+ 2, ["*", ['var', 'width'], ["^", 2, -0.5]],
291
+ 7, ["*", ['var', 'width'], ["^", 2, 2.5]],
292
+ 9, ["*", ['var', 'width'], ["^", 2, 4.0]]
293
+ ]
294
+ ]
295
+ // Need to vary width based on zoom??
296
+ // Or opacity??
297
+ };
298
+ if (this.__dashed) {
299
+ paintStyle['line-dasharray'] = [3, 2];
300
+ }
301
+ return paintStyle;
302
+ }
303
+
304
+ style(options)
305
+ {
247
306
  return {
248
307
  ...super.style(),
249
308
  'type': 'line',
250
309
  'filter': [
251
- 'all',
252
- ['==', '$type', 'LineString']
310
+ 'all',
311
+ ['==', '$type', 'LineString'],
312
+ this.__filter
253
313
  // not for paths...
254
314
  ],
255
- 'paint': {
256
- 'line-color': [
257
- 'case',
258
- ['boolean', ['feature-state', 'active'], false], coloured ? '#D88' : '#CCC',
259
- ['boolean', ['feature-state', 'selected'], false], '#0F0',
260
- ['==', ['get', 'type'], 'network'], '#AFA202',
261
- ['has', 'centreline'], '#888',
262
- ('authoring' in options && options.authoring) ? '#C44' : '#444'
263
- ],
264
- 'line-opacity': [
265
- 'case',
266
- ['boolean', ['feature-state', 'active'], false], 1.0,
267
- 0.3
268
- ],
269
- 'line-width': [
270
- 'let',
271
- 'width', [
272
- 'case',
273
- ['has', 'centreline'], 1.2,
274
- ['==', ['get', 'type'], 'network'], 1.2,
275
- ['boolean', ['feature-state', 'active'], false], 1.2,
276
- ('authoring' in options && options.authoring) ? 0.7 : 0.1
277
- ], [
278
- 'interpolate',
279
- ['exponential', 2],
280
- ['zoom'],
281
- 2, ["*", ['var', 'width'], ["^", 2, -0.5]],
282
- 7, ["*", ['var', 'width'], ["^", 2, 2.5]],
283
- 9, ["*", ['var', 'width'], ["^", 2, 4.0]]
284
- ]
285
- ]
286
- // Need to vary width based on zoom??
287
- // Or opacity??
288
- }
315
+ 'paint': this.paintStyle(options)
289
316
  };
290
317
  }
291
318
  }
292
319
 
293
320
  //==============================================================================
294
321
 
322
+ export class FeatureDashLineLayer extends FeatureLineLayer
323
+ {
324
+ constructor(id, sourceLayer)
325
+ {
326
+ super(id, sourceLayer, true);
327
+ }
328
+ }
329
+
330
+ //==============================================================================
331
+
295
332
  export class PathLineLayer extends VectorStyleLayer
296
333
  {
297
334
  constructor(id, sourceLayer, dashed=false)
298
335
  {
299
336
  const filterType = dashed ? 'line-dash' : 'line';
300
- super(id, filterType, sourceLayer);
337
+ super(id, `path-${filterType}`, sourceLayer);
301
338
  this.__filter = dashed ?
302
339
  [
303
340
  'any',
@@ -417,7 +454,8 @@ export class FeatureNerveLayer extends VectorStyleLayer
417
454
  ['boolean', ['feature-state', 'active'], false], '#222',
418
455
  ['boolean', ['feature-state', 'selected'], false], 'red',
419
456
  ['boolean', ['feature-state', 'hidden'], false], '#CCC',
420
- '#888'
457
+ ['boolean', ['get', 'active'], false], '#888',
458
+ '#FFF'
421
459
  ],
422
460
  'line-opacity': [
423
461
  'case',
@@ -640,14 +678,14 @@ export class BackgroundLayer
640
678
  return this.__id;
641
679
  }
642
680
 
643
- style(backgroundColour)
681
+ style(backgroundColour, opacity=0.1)
644
682
  {
645
683
  return {
646
684
  'id': this.__id,
647
685
  'type': 'background',
648
686
  'paint': {
649
687
  'background-color': backgroundColour,
650
- 'background-opacity': 0.1
688
+ 'background-opacity': opacity
651
689
  }
652
690
  };
653
691
  }