@abi-software/flatmap-viewer 2.2.0-beta.9 → 2.2.1-beta.2

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``
41
+ * ``npm install @abi-software/flatmap-viewer@2.2.1-beta.2``
42
42
 
43
43
  Documentation
44
44
  -------------
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@abi-software/flatmap-viewer",
3
- "version": "2.2.0-beta.9",
3
+ "version": "2.2.1-beta.2",
4
4
  "description": "Flatmap viewer using Maplibre GL",
5
- "repository": "https://github.com/ABI-Software/flatmap-viewer.git",
5
+ "repository": "https://github.com/AnatomicMaps/flatmap-viewer.git",
6
6
  "main": "src/main.js",
7
7
  "files": [
8
8
  "src",
@@ -21,7 +21,7 @@
21
21
  "@turf/area": "^6.0.1",
22
22
  "@turf/bbox": "^6.0.1",
23
23
  "@turf/helpers": "^6.1.4",
24
- "maplibre-gl": "^2.1.0",
24
+ "maplibre-gl": ">=1.15.3",
25
25
  "minisearch": "^2.2.1",
26
26
  "polylabel": "^1.1.0"
27
27
  },
@@ -45,6 +45,10 @@ import * as utils from './utils.js';
45
45
 
46
46
  //==============================================================================
47
47
 
48
+ const MAP_MAKER_SEPARATE_LAYERS_VERSION = 1.4;
49
+
50
+ //==============================================================================
51
+
48
52
  /**
49
53
  * Maps are not created directly but instead are created and loaded by
50
54
  * :meth:`LoadMap` of :class:`MapManager`.
@@ -629,9 +633,10 @@ class FlatMap
629
633
  }
630
634
  }
631
635
 
632
- setColour(options={colour: true, outline: true})
633
- //==============================================
636
+ setColour(options=null)
637
+ //=====================
634
638
  {
639
+ options = utils.setDefaultOptions(options, {colour: true, outline: true});
635
640
  if (this._userInteractions !== null) {
636
641
  this._userInteractions.setColour(options);
637
642
  }
@@ -962,9 +967,10 @@ class FlatMap
962
967
  * @param {boolean} [options.highlight=false] Highlight the features zoomed to
963
968
  * @param {number} [options.padding=100] Padding around the composite bounding box
964
969
  */
965
- zoomToFeatures(externalIds, options={select:true, highlight:false, padding:100})
966
- //==============================================================================
970
+ zoomToFeatures(externalIds, options=null)
971
+ //=======================================
967
972
  {
973
+ options = utils.setDefaultOptions(options, {select: true, highlight: false, padding:100});
968
974
  if (this._userInteractions !== null) {
969
975
  const featureIds = this.modelFeatureIdList(externalIds);
970
976
  this._userInteractions.zoomToFeatures(featureIds, options);
@@ -999,9 +1005,15 @@ export class MapManager
999
1005
  {
1000
1006
  return await this._initialisingMutex.dispatch(async () => {
1001
1007
  if (!this._initialised) {
1002
- this._mapList = await this._mapServer.loadJSON('');
1008
+ this._mapList = [];
1009
+ const maps = await this._mapServer.loadJSON('');
1003
1010
  // Check map schema version (set by mapmaker) and
1004
1011
  // remove maps we can't view (giving a console warning...)
1012
+ for (const map of maps) {
1013
+ // Are features in separate vector tile source layers?
1014
+ map.separateLayers = ('version' in map && map.version >= MAP_MAKER_SEPARATE_LAYERS_VERSION);
1015
+ this._mapList.push(map);
1016
+ }
1005
1017
  this._initialised = true;
1006
1018
  }
1007
1019
  });
@@ -1058,7 +1070,9 @@ export class MapManager
1058
1070
  let latestMap = null;
1059
1071
  let lastCreatedTime = '';
1060
1072
  for (const map of this._mapList) {
1061
- if (mapDescribes === map.describes
1073
+ if (mapDescribes === (('taxon' in map) ? map.taxon
1074
+ : ('describes' in map) ? map.describes
1075
+ : map.id)
1062
1076
  || mapDescribes === map.id
1063
1077
  || mapDescribes === map.source) {
1064
1078
  if ('created' in map) {
@@ -1085,7 +1099,9 @@ export class MapManager
1085
1099
  return flatmap;
1086
1100
  }
1087
1101
  }
1088
- if ('describes' in identifier) {
1102
+ if ('taxon' in identifier) {
1103
+ mapDescribes = identifier.taxon;
1104
+ } else if ('describes' in identifier) {
1089
1105
  mapDescribes = identifier.describes;
1090
1106
  }
1091
1107
  } else {
@@ -1102,8 +1118,8 @@ export class MapManager
1102
1118
  * or a taxon identifier of the species that the map represents. The
1103
1119
  * latest version of a map is loaded unless it has been identified
1104
1120
  * by ``source`` (see below).
1105
- * @arg identifier.describes {string} The taxon identifier of the map. This is specified as metadata
1106
- * in the map's source file.)
1121
+ * @arg identifier.taxon {string} The taxon identifier of the map. This is specified as metadata
1122
+ * in the map's source file.)
1107
1123
  * @arg identifier.source {string} The URL of the source file from which the map has
1108
1124
  * been generated. If given then this exact map will be
1109
1125
  * loaded.
@@ -1138,7 +1154,7 @@ export class MapManager
1138
1154
  *
1139
1155
  * const humanMap2 = mapManager.loadMap('NCBITaxon:9606', 'div-2');
1140
1156
  *
1141
- * const humanMap3 = mapManager.loadMap({describes: 'NCBITaxon:9606'}, 'div-3');
1157
+ * const humanMap3 = mapManager.loadMap({taxon: 'NCBITaxon:9606'}, 'div-3');
1142
1158
  *
1143
1159
  * const humanMap4 = mapManager.loadMap(
1144
1160
  * {source: 'https://models.physiomeproject.org/workspace/585/rawfile/650adf9076538a4bf081609df14dabddd0eb37e7/Human_Body.pptx'},
@@ -1151,7 +1167,7 @@ export class MapManager
1151
1167
  try {
1152
1168
  const map = await this.findMap_(identifier);
1153
1169
  if (map === null) {
1154
- reject(`Unknown map for ${JSON.stringify(identifier)}`);
1170
+ reject(`Unknown map: ${JSON.stringify(identifier)}`);
1155
1171
  };
1156
1172
 
1157
1173
  // Load the maps index file
@@ -1243,6 +1259,11 @@ export class MapManager
1243
1259
  outline: true
1244
1260
  };
1245
1261
  }
1262
+ mapOptions.layerOptions.authoring = ('authoring' in mapIndex && mapIndex.authoring);
1263
+
1264
+ // Are features in separate vector tile source layers?
1265
+
1266
+ mapOptions.separateLayers = map.separateLayers;
1246
1267
 
1247
1268
  // Display the map
1248
1269
 
@@ -224,6 +224,7 @@ export class UserInteractions
224
224
  this._map.on('mousemove', this.mouseMoveEvent_.bind(this));
225
225
  this._lastFeatureMouseEntered = null;
226
226
  this._lastFeatureModelsMouse = null;
227
+ this.__lastClickLngLat = null;
227
228
 
228
229
  // Handle pan/zoom events
229
230
  this._map.on('move', this.panZoomEvent_.bind(this, 'pan'));
@@ -284,7 +285,9 @@ export class UserInteractions
284
285
  return {
285
286
  id: featureId,
286
287
  source: VECTOR_TILES_SOURCE,
287
- sourceLayer: ann['tile-layer']
288
+ sourceLayer: this._flatmap.options.separateLayers
289
+ ? `${ann['layer']}_${ann['tile-layer']}`
290
+ : ann['tile-layer']
288
291
  };
289
292
  }
290
293
 
@@ -559,9 +562,10 @@ export class UserInteractions
559
562
  * @param {boolean} [options.highlight=false] Highlight the features zoomed to
560
563
  * @param {number} [options.padding=100] Padding around the composite bounding box
561
564
  */
562
- zoomToFeatures(featureIds, options={select: true, highlight: false, padding:100})
563
- //===============================================================================
565
+ zoomToFeatures(featureIds, options=null)
566
+ //======================================
564
567
  {
568
+ options = utils.setDefaultOptions(options, {select: true, highlight: false, padding:100});
565
569
  const select = (options.select === true);
566
570
  const highlight = (options.highlight === true);
567
571
  const padding = options.padding || 100;
@@ -617,9 +621,17 @@ export class UserInteractions
617
621
  this.unselectFeatures_();
618
622
  this.selectFeature_(featureId);
619
623
 
620
- // Position popup at the feature's 'centre'
624
+ // Find the pop-up's postion
621
625
 
622
- const location = this.__centralPosition(featureId, ann);
626
+ let location = null;
627
+ if ('positionAtLastClick' in options
628
+ && options.positionAtLastClick
629
+ && this.__lastClickLngLat !== null) {
630
+ location = this.__lastClickLngLat;
631
+ } else {
632
+ // Position popup at the feature's 'centre'
633
+ location = this.__centralPosition(featureId, ann);
634
+ }
623
635
 
624
636
  // Make sure the feature is on screen
625
637
 
@@ -655,7 +667,7 @@ export class UserInteractions
655
667
  && !('labelled' in properties)) {
656
668
  const label = properties.label;
657
669
  const capitalisedLabel = label.substr(0, 1).toUpperCase() + label.substr(1);
658
- return `<div class='flatmap-feature-label'>${capitalisedLabel}</div>`;
670
+ return `<div class='flatmap-feature-label'>${capitalisedLabel.replaceAll("\n", "<br/>")}</div>`;
659
671
  }
660
672
  return '';
661
673
  }
@@ -734,8 +746,9 @@ export class UserInteractions
734
746
  }
735
747
  info = this._infoControl.featureInformation(features, event.lngLat);
736
748
  }
737
- const lineFeatures = features.filter(feature => ('type' in feature.properties
738
- && feature.properties.type.startsWith('line')));
749
+ const lineFeatures = features.filter(feature => (('type' in feature.properties
750
+ && feature.properties.type.startsWith('line'))
751
+ || 'centreline' in feature.properties));
739
752
  if (lineFeatures.length > 0) {
740
753
  const lineFeature = lineFeatures[0];
741
754
  const lineFeatureId = +lineFeature.properties.featureId; // Ensure numeric
@@ -748,7 +761,8 @@ export class UserInteractions
748
761
  }
749
762
  }
750
763
  } else {
751
- let labelledFeatures = features.filter(feature => ('label' in feature.properties
764
+ let labelledFeatures = features.filter(feature => (('label' in feature.properties
765
+ || 'node' in feature.properties)
752
766
  && (!('tooltip' in feature.properties)
753
767
  || feature.properties.tooltip)))
754
768
  .sort((a, b) => (a.properties.area - b.properties.area));
@@ -861,6 +875,7 @@ export class UserInteractions
861
875
  const feature = this._activeFeatures[0]
862
876
  this.selectionEvent_(event.originalEvent, feature);
863
877
  if (feature !== undefined) {
878
+ this.__lastClickLngLat = event.lngLat;
864
879
  this.__featureEvent('click', feature);
865
880
  }
866
881
  }
@@ -918,7 +933,9 @@ export class UserInteractions
918
933
  }
919
934
  let position = annotation.centroid;
920
935
  const features = this._map.querySourceFeatures(VECTOR_TILES_SOURCE, {
921
- 'sourceLayer': annotation['tile-layer'],
936
+ 'sourceLayer': this._flatmap.options.separateLayers
937
+ ? `${annotation['layer']}_${annotation['tile-layer']}`
938
+ : annotation['tile-layer'],
922
939
  'filter': [
923
940
  'all',
924
941
  [ '==', ['id'], parseInt(featureId) ],
package/src/layers.js CHANGED
@@ -27,6 +27,8 @@ import {PATHWAYS_LAYER} from './pathways.js';
27
27
  import * as style from './styling.js';
28
28
  import * as utils from './utils.js';
29
29
 
30
+ const FEATURES_LAYER = 'features'
31
+
30
32
  //==============================================================================
31
33
 
32
34
  class MapFeatureLayer
@@ -34,12 +36,19 @@ class MapFeatureLayer
34
36
  constructor(flatmap, layer, options)
35
37
  {
36
38
  this.__map = flatmap.map;
39
+ this.__separateLayers = flatmap.options.separateLayers;
37
40
  this.__id = layer.id;
38
41
  this.__rasterLayers = [];
39
42
  this.__styleLayers = [];
40
43
 
41
- const haveVectorLayers = (typeof this.__map.getSource('vector-tiles') !== 'undefined');
42
- if (haveVectorLayers) {
44
+ const vectorTileSource = this.__map.getSource('vector-tiles');
45
+ const haveVectorLayers = (typeof vectorTileSource !== 'undefined');
46
+ const featuresVectorLayerId = this.__separateLayers
47
+ ? `${this.__id}_${FEATURES_LAYER}`
48
+ : FEATURES_LAYER;
49
+ const vectorFeatures = haveVectorLayers
50
+ && vectorTileSource.vectorLayerIds.indexOf(featuresVectorLayerId) >= 0;
51
+ if (vectorFeatures) {
43
52
  this.__addStyleLayer(style.BodyLayer, options);
44
53
  }
45
54
  if (flatmap.details['image_layer']) {
@@ -49,13 +58,18 @@ class MapFeatureLayer
49
58
  }
50
59
  // if no image layers then make feature borders (and lines?) more visible...??
51
60
  if (haveVectorLayers) {
52
- this.__addStyleLayer(style.FeatureFillLayer, options);
53
- this.__addStyleLayer(style.FeatureLineLayer, options);
54
- this.__addStyleLayer(style.FeatureBorderLayer, options);
61
+ if (vectorFeatures) {
62
+ this.__addStyleLayer(style.FeatureFillLayer, options);
63
+ this.__addStyleLayer(style.FeatureDashLineLayer, options);
64
+ this.__addStyleLayer(style.FeatureLineLayer, options);
65
+ this.__addStyleLayer(style.FeatureBorderLayer, options);
66
+ }
55
67
  this.__addPathwayStyleLayers(options);
56
- this.__addStyleLayer(style.FeatureLargeSymbolLayer, options);
57
- if (!flatmap.options.tooltips) {
58
- this.__addStyleLayer(style.FeatureSmallSymbolLayer, options);
68
+ if (vectorFeatures) {
69
+ this.__addStyleLayer(style.FeatureLargeSymbolLayer, options);
70
+ if (!flatmap.options.tooltips) {
71
+ this.__addStyleLayer(style.FeatureSmallSymbolLayer, options);
72
+ }
59
73
  }
60
74
  }
61
75
 
@@ -81,9 +95,12 @@ class MapFeatureLayer
81
95
  __addPathwayStyleLayers(options)
82
96
  //==============================
83
97
  {
98
+ const pathwaysVectorLayerId = this.__separateLayers
99
+ ? `${this.__id}_${PATHWAYS_LAYER}`
100
+ : PATHWAYS_LAYER;
84
101
  if (this.__map.getSource('vector-tiles')
85
102
  .vectorLayerIds
86
- .indexOf(PATHWAYS_LAYER) >= 0) {
103
+ .indexOf(pathwaysVectorLayerId) >= 0) {
87
104
  this.__addStyleLayer(style.PathLineLayer, options, PATHWAYS_LAYER);
88
105
  this.__addStyleLayer(style.PathDashlineLayer, options, PATHWAYS_LAYER);
89
106
  this.__addStyleLayer(style.NervePolygonBorder, options, PATHWAYS_LAYER);
@@ -92,10 +109,12 @@ class MapFeatureLayer
92
109
  }
93
110
  }
94
111
 
95
- __addStyleLayer(styleClass, options, sourceLayer='features')
96
- //==========================================================
112
+ __addStyleLayer(styleClass, options, sourceLayer=FEATURES_LAYER)
113
+ //==============================================================
97
114
  {
98
- const styleLayer = new styleClass(this.__id, sourceLayer);
115
+ const layerId = `${this.__id}_${sourceLayer}`;
116
+ const source = this.__separateLayers ? layerId : sourceLayer;
117
+ const styleLayer = new styleClass(layerId, source);
99
118
  this.__map.addLayer(styleLayer.style(options));
100
119
  this.__styleLayers.push(styleLayer);
101
120
  }
@@ -188,9 +207,10 @@ export class LayerManager
188
207
  }
189
208
  }
190
209
 
191
- setColour(options={colour: true, outline: true})
192
- //===============================================
210
+ setColour(options=null)
211
+ //=====================
193
212
  {
213
+ options = utils.setDefaultOptions(options, {colour: true, outline: true});
194
214
  for (const layer of this.__layers.values()) {
195
215
  layer.setColour(options)
196
216
  }
package/src/search.js CHANGED
@@ -180,7 +180,7 @@ export class SearchIndex
180
180
  let results = [];
181
181
  text = text.trim()
182
182
  if (text.length > 2 && ["'", '"'].indexOf(text.slice(0, 1)) >= 0) {
183
- text = (text.slice(0, 1) === text.slice(-1)) ? text.slice(1, -1) : text.slice(1)
183
+ text = text.replaceAll(text.slice(0, 1), '');
184
184
  results = this._searchEngine.search(text, {prefix: true, combineWith: 'AND'});
185
185
  } else if (text.length > 1) {
186
186
  results = this._searchEngine.search(text, {prefix: true});
package/src/styling.js CHANGED
@@ -28,9 +28,9 @@ export const VECTOR_TILES_SOURCE = 'vector-tiles';
28
28
 
29
29
  class VectorStyleLayer
30
30
  {
31
- constructor(mapLayerId, sourceLayer, idPrefix)
31
+ constructor(id, suffix, sourceLayer)
32
32
  {
33
- this.__id = `${mapLayerId}_${sourceLayer}_${idPrefix}`;
33
+ this.__id = `${id}_${suffix}`;
34
34
  this.__sourceLayer = sourceLayer;
35
35
  this.__lastPaintStyle = {};
36
36
  }
@@ -78,9 +78,9 @@ class VectorStyleLayer
78
78
 
79
79
  export class BodyLayer extends VectorStyleLayer
80
80
  {
81
- constructor(mapLayerId, sourceLayer)
81
+ constructor(id, sourceLayer)
82
82
  {
83
- super(mapLayerId, sourceLayer, 'body');
83
+ super(id, 'body', sourceLayer);
84
84
  }
85
85
 
86
86
  style(options)
@@ -105,9 +105,9 @@ export class BodyLayer extends VectorStyleLayer
105
105
 
106
106
  export class FeatureFillLayer extends VectorStyleLayer
107
107
  {
108
- constructor(mapLayerId, sourceLayer)
108
+ constructor(id, sourceLayer)
109
109
  {
110
- super(mapLayerId, sourceLayer, 'fill');
110
+ super(id, 'fill', sourceLayer);
111
111
  }
112
112
 
113
113
  paintStyle(options, changes=false)
@@ -117,13 +117,13 @@ export class FeatureFillLayer extends VectorStyleLayer
117
117
  const paintStyle = {
118
118
  'fill-color': [
119
119
  'case',
120
- ['any',
121
- ['==', ['get', 'kind'], 'scaffold'],
122
- ['==', ['get', 'kind'], 'tissue'],
123
- ['==', ['get', 'kind'], 'cell-type']
124
- ], "white",
125
- ['boolean', ['feature-state', 'selected'], false], '#0F0',
120
+ ['has', 'colour'], ['get', 'colour'],
126
121
  ['boolean', ['feature-state', 'active'], false], coloured ? '#D88' : '#CCC',
122
+ ['boolean', ['feature-state', 'selected'], false], '#0F0',
123
+ ['any',
124
+ ['==', ['get', 'kind'], 'scaffold']
125
+ ], 'white',
126
+ ['has', 'node'], '#AFA202',
127
127
  'white' // background colour? body colour ??
128
128
  ],
129
129
  'fill-opacity': [
@@ -131,10 +131,12 @@ export class FeatureFillLayer extends VectorStyleLayer
131
131
  ['any',
132
132
  ['==', ['get', 'kind'], 'scaffold'],
133
133
  ['==', ['get', 'kind'], 'tissue'],
134
- ['==', ['get', 'kind'], 'cell-type']
134
+ ['==', ['get', 'kind'], 'cell-type'],
135
135
  ], 0.1,
136
+ ['has', 'node'], 0.3,
136
137
  ['boolean', ['feature-state', 'selected'], false], 1.0,
137
138
  ['boolean', ['feature-state', 'active'], false], 0.8,
139
+ ['has', 'colour'], 0.008,
138
140
  (coloured && !dimmed) ? 0.01 : 0.5
139
141
  ]
140
142
  };
@@ -163,9 +165,9 @@ export class FeatureFillLayer extends VectorStyleLayer
163
165
 
164
166
  export class FeatureBorderLayer extends VectorStyleLayer
165
167
  {
166
- constructor(mapLayerId, sourceLayer)
168
+ constructor(id, sourceLayer)
167
169
  {
168
- super(mapLayerId, sourceLayer, 'border');
170
+ super(id, 'border', sourceLayer);
169
171
  }
170
172
 
171
173
  paintStyle(options, changes=false)
@@ -180,6 +182,10 @@ export class FeatureBorderLayer extends VectorStyleLayer
180
182
  lineColour.push(['boolean', ['feature-state', 'active'], false]);
181
183
  lineColour.push('blue');
182
184
  }
185
+ lineColour.push(['has', 'colour']);
186
+ lineColour.push(['get', 'colour']);
187
+ lineColour.push(['has', 'node']);
188
+ lineColour.push('#AFA202');
183
189
  lineColour.push('#444');
184
190
 
185
191
  const lineOpacity = [
@@ -230,9 +236,66 @@ export class FeatureBorderLayer extends VectorStyleLayer
230
236
 
231
237
  export class FeatureLineLayer extends VectorStyleLayer
232
238
  {
233
- constructor(mapLayerId, sourceLayer)
239
+ constructor(id, sourceLayer, dashed=false)
240
+ {
241
+ const filterType = dashed ? 'line-dash' : 'line';
242
+ super(id, `divider-${filterType}`, sourceLayer);
243
+ this.__filter = dashed ?
244
+ [
245
+ 'any',
246
+ ['==', 'type', `line-dash`]
247
+ ]
248
+ :
249
+ [
250
+ 'any',
251
+ ['==', 'type', 'bezier'],
252
+ ['==', 'type', `line`]
253
+ ];
254
+ this.__dashed = dashed;
255
+ }
256
+
257
+ paintStyle(options)
234
258
  {
235
- super(mapLayerId, sourceLayer, 'divider-line');
259
+ const coloured = !('colour' in options) || options.colour;
260
+ const paintStyle = {
261
+ 'line-color': [
262
+ 'case',
263
+ ['has', 'colour'], ['get', 'colour'],
264
+ ['boolean', ['feature-state', 'active'], false], coloured ? '#D88' : '#CCC',
265
+ ['boolean', ['feature-state', 'selected'], false], '#0F0',
266
+ ['==', ['get', 'type'], 'network'], '#AFA202',
267
+ ['has', 'centreline'], '#888',
268
+ ('authoring' in options && options.authoring) ? '#C44' : '#444'
269
+ ],
270
+ 'line-opacity': [
271
+ 'case',
272
+ ['boolean', ['feature-state', 'active'], false], 1.0,
273
+ 0.3
274
+ ],
275
+ 'line-width': [
276
+ 'let',
277
+ 'width', [
278
+ 'case',
279
+ ['has', 'centreline'], 1.2,
280
+ ['==', ['get', 'type'], 'network'], 1.2,
281
+ ['boolean', ['feature-state', 'active'], false], 1.2,
282
+ ('authoring' in options && options.authoring) ? 0.7 : 0.5
283
+ ], [
284
+ 'interpolate',
285
+ ['exponential', 2],
286
+ ['zoom'],
287
+ 2, ["*", ['var', 'width'], ["^", 2, -0.5]],
288
+ 7, ["*", ['var', 'width'], ["^", 2, 2.5]],
289
+ 9, ["*", ['var', 'width'], ["^", 2, 4.0]]
290
+ ]
291
+ ]
292
+ // Need to vary width based on zoom??
293
+ // Or opacity??
294
+ };
295
+ if (this.__dashed) {
296
+ paintStyle['line-dasharray'] = [3, 2];
297
+ }
298
+ return paintStyle;
236
299
  }
237
300
 
238
301
  style(options)
@@ -241,47 +304,34 @@ export class FeatureLineLayer extends VectorStyleLayer
241
304
  ...super.style(),
242
305
  'type': 'line',
243
306
  'filter': [
244
- 'all',
245
- ['==', '$type', 'LineString']
307
+ 'all',
308
+ ['==', '$type', 'LineString'],
309
+ this.__filter
246
310
  // not for paths...
247
311
  ],
248
- 'paint': {
249
- 'line-color': [
250
- 'case',
251
- ['==', ['get', 'type'], 'network'], '#AFA202',
252
- '#444'
253
- ],
254
- 'line-opacity': 0.3,
255
- 'line-width': [
256
- 'let',
257
- 'width', [
258
- 'case',
259
- ['==', ['get', 'type'], 'network'], 1.2,
260
- 0.1
261
- ], [
262
- 'interpolate',
263
- ['exponential', 2],
264
- ['zoom'],
265
- 2, ["*", ['var', 'width'], ["^", 2, -0.5]],
266
- 7, ["*", ['var', 'width'], ["^", 2, 2.5]],
267
- 9, ["*", ['var', 'width'], ["^", 2, 4.0]]
268
- ]
269
- ]
270
- // Need to vary width based on zoom??
271
- // Or opacity??
272
- }
312
+ 'paint': this.paintStyle(options)
273
313
  };
274
314
  }
275
315
  }
276
316
 
277
317
  //==============================================================================
278
318
 
319
+ export class FeatureDashLineLayer extends FeatureLineLayer
320
+ {
321
+ constructor(id, sourceLayer)
322
+ {
323
+ super(id, sourceLayer, true);
324
+ }
325
+ }
326
+
327
+ //==============================================================================
328
+
279
329
  export class PathLineLayer extends VectorStyleLayer
280
330
  {
281
- constructor(mapLayerId, sourceLayer, dashed=false)
331
+ constructor(id, sourceLayer, dashed=false)
282
332
  {
283
333
  const filterType = dashed ? 'line-dash' : 'line';
284
- super(mapLayerId, sourceLayer, filterType);
334
+ super(id, filterType, sourceLayer);
285
335
  this.__filter = dashed ?
286
336
  [
287
337
  'any',
@@ -312,7 +362,7 @@ export class PathLineLayer extends VectorStyleLayer
312
362
  ['==', ['get', 'kind'], 'sensory'], '#2A62F6',
313
363
  ['==', ['get', 'kind'], 'symp-post'], '#EA3423',
314
364
  ['==', ['get', 'kind'], 'symp-pre'], '#EA3423',
315
- 'red'
365
+ '#888'
316
366
  ],
317
367
  'line-opacity': [
318
368
  'case',
@@ -321,7 +371,7 @@ export class PathLineLayer extends VectorStyleLayer
321
371
  ['boolean', ['feature-state', 'selected'], false], 1.0,
322
372
  ['boolean', ['feature-state', 'active'], false], 0.8,
323
373
  ['boolean', ['feature-state', 'hidden'], false], 0.1,
324
- dimmed ? 0.1 : 0.5
374
+ dimmed ? 0.1 : 0.4
325
375
  ],
326
376
  'line-width': [
327
377
  'let',
@@ -330,7 +380,7 @@ export class PathLineLayer extends VectorStyleLayer
330
380
  ['==', ['get', 'type'], 'bezier'], 0.1,
331
381
  ['boolean', ['get', 'invisible'], false], 0.1,
332
382
  ['boolean', ['feature-state', 'selected'], false], 1.2,
333
- ['boolean', ['feature-state', 'active'], false], 1.0,
383
+ ['boolean', ['feature-state', 'active'], false], 0.9,
334
384
  0.8
335
385
  ], [
336
386
  'interpolate',
@@ -370,9 +420,9 @@ export class PathLineLayer extends VectorStyleLayer
370
420
 
371
421
  export class PathDashlineLayer extends PathLineLayer
372
422
  {
373
- constructor(mapLayerId, sourceLayer)
423
+ constructor(id, sourceLayer)
374
424
  {
375
- super(mapLayerId, sourceLayer, true);
425
+ super(id, sourceLayer, true);
376
426
  }
377
427
  }
378
428
 
@@ -380,9 +430,9 @@ export class PathDashlineLayer extends PathLineLayer
380
430
 
381
431
  export class FeatureNerveLayer extends VectorStyleLayer
382
432
  {
383
- constructor(mapLayerId, sourceLayer)
433
+ constructor(id, sourceLayer)
384
434
  {
385
- super(mapLayerId, sourceLayer, 'nerve-path');
435
+ super(id, 'nerve-path', sourceLayer);
386
436
  }
387
437
 
388
438
  style(options)
@@ -433,9 +483,9 @@ export class FeatureNerveLayer extends VectorStyleLayer
433
483
 
434
484
  export class NervePolygonBorder extends VectorStyleLayer
435
485
  {
436
- constructor(mapLayerId, sourceLayer)
486
+ constructor(id, sourceLayer)
437
487
  {
438
- super(mapLayerId, sourceLayer, 'nerve-border');
488
+ super(id, 'nerve-border', sourceLayer);
439
489
  }
440
490
 
441
491
  style(options)
@@ -477,9 +527,9 @@ export class NervePolygonBorder extends VectorStyleLayer
477
527
 
478
528
  export class NervePolygonFill extends VectorStyleLayer
479
529
  {
480
- constructor(mapLayerId, sourceLayer)
530
+ constructor(id, sourceLayer)
481
531
  {
482
- super(mapLayerId, sourceLayer, 'nerve-fill');
532
+ super(id, 'nerve-fill', sourceLayer);
483
533
  }
484
534
 
485
535
  style(options)
@@ -527,9 +577,9 @@ export class NervePolygonFill extends VectorStyleLayer
527
577
 
528
578
  export class FeatureLargeSymbolLayer extends VectorStyleLayer
529
579
  {
530
- constructor(mapLayerId, sourceLayer)
580
+ constructor(id, sourceLayer)
531
581
  {
532
- super(mapLayerId, sourceLayer, 'large-symbol');
582
+ super(id, 'large-symbol', sourceLayer);
533
583
  }
534
584
 
535
585
  style(options)
@@ -571,9 +621,9 @@ export class FeatureLargeSymbolLayer extends VectorStyleLayer
571
621
 
572
622
  export class FeatureSmallSymbolLayer extends VectorStyleLayer
573
623
  {
574
- constructor(mapLayerId, sourceLayer)
624
+ constructor(id, sourceLayer)
575
625
  {
576
- super(mapLayerId, sourceLayer, 'small-symbol');
626
+ super(id, 'small-symbol', sourceLayer);
577
627
  }
578
628
 
579
629
  style(options)
@@ -614,7 +664,7 @@ export class FeatureSmallSymbolLayer extends VectorStyleLayer
614
664
 
615
665
  export class BackgroundLayer
616
666
  {
617
- constructor(rasterLayerId)
667
+ constructor()
618
668
  {
619
669
  this.__id = 'background';
620
670
  }
@@ -627,7 +677,7 @@ export class BackgroundLayer
627
677
  style(backgroundColour)
628
678
  {
629
679
  return {
630
- 'id': 'background',
680
+ 'id': this.__id,
631
681
  'type': 'background',
632
682
  'paint': {
633
683
  'background-color': backgroundColour,
@@ -641,9 +691,9 @@ export class BackgroundLayer
641
691
 
642
692
  export class RasterLayer
643
693
  {
644
- constructor(rasterLayerId)
694
+ constructor(id)
645
695
  {
646
- this.__id = rasterLayerId;
696
+ this.__id = id;
647
697
  }
648
698
 
649
699
  get id()
package/src/utils.js CHANGED
@@ -109,3 +109,18 @@ export function normaliseId(id)
109
109
  }
110
110
 
111
111
  //==============================================================================
112
+
113
+ export function setDefaultOptions(options, defaultOptions)
114
+ {
115
+ if (options === undefined || options === null) {
116
+ return defaultOptions;
117
+ }
118
+ for (const [key, value] of Object.entries(defaultOptions)) {
119
+ if (!(key in options)) {
120
+ options[key] = value;
121
+ }
122
+ }
123
+ return options;
124
+ }
125
+
126
+ //==============================================================================
@@ -96,6 +96,10 @@ li.flatmap-contextmenu-item:hover {
96
96
  overflow: scroll;
97
97
  }
98
98
 
99
+ .info-control-entry {
100
+ border-bottom: 1px solid gray;
101
+ }
102
+
99
103
  /* Navigation control buttons */
100
104
 
101
105
  .maplibregl-ctrl button.navigation-zoom-in {