@abi-software/flatmap-viewer 2.2.10 → 2.2.11-devel.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abi-software/flatmap-viewer",
3
- "version": "2.2.10",
3
+ "version": "2.2.11-devel.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/controls.js CHANGED
@@ -22,6 +22,8 @@ limitations under the License.
22
22
 
23
23
  //==============================================================================
24
24
 
25
+ // Needed for Webpack
26
+ import zoomInButton from '../static/images/zoom-in-button.png'
25
27
 
26
28
  //==============================================================================
27
29
 
@@ -118,6 +120,9 @@ export class PathControl
118
120
  const innerHTML = [];
119
121
  innerHTML.push(`<label for="path-all-paths">ALL PATHS:</label><div class="nerve-line"></div><input id="path-all-paths" type="checkbox" checked/>`);
120
122
  for (const path of this.__pathTypes) {
123
+ // need to set style background instead of class...
124
+ // background: #2A62F6;
125
+ // background: repeating-linear-gradient(to right,#EA3423 0,#EA3423 6px,transparent 6px,transparent 9px)
121
126
  innerHTML.push(`<label for="path-${path.type}">${path.label}</label><div class="nerve-line nerve-${path.type}"></div><input id="path-${path.type}" type="checkbox" checked/>`);
122
127
  }
123
128
  this._legend.innerHTML = innerHTML.join('\n');
@@ -316,6 +321,133 @@ export class LayerControl
316
321
 
317
322
  //==============================================================================
318
323
 
324
+
325
+ const SCKAN_STATES = [
326
+ {
327
+ 'id': 'VALID',
328
+ 'description': 'Path consistent with SCKAN'
329
+ },
330
+ {
331
+ 'id': 'INVALID',
332
+ 'description': 'Path inconsistent with SCKAN'
333
+ }
334
+ ];
335
+
336
+
337
+ export class SCKANControl
338
+ {
339
+ constructor(flatmap)
340
+ {
341
+ this.__flatmap = flatmap;
342
+ this.__map = undefined;
343
+ }
344
+
345
+ getDefaultPosition()
346
+ //==================
347
+ {
348
+ return 'top-right';
349
+ }
350
+
351
+ onAdd(map)
352
+ //========
353
+ {
354
+ this.__map = map;
355
+ this.__container = document.createElement('div');
356
+ this.__container.className = 'maplibregl-ctrl';
357
+ this.__container.id = 'flatmap-layer-control';
358
+
359
+ this.__sckan = document.createElement('div');
360
+ this.__sckan.id = 'sckan-control-text';
361
+ this.__sckan.className = 'flatmap-layer-grid';
362
+
363
+ const innerHTML = [];
364
+ innerHTML.push(`<label for="sckan-all-paths">ALL PATHS:</label><input id="sckan-all-paths" type="checkbox" checked/>`);
365
+ for (const state of SCKAN_STATES) {
366
+ innerHTML.push(`<label for="sckan-${state.id}">${state.description}</label><input id="sckan-${state.id}" type="checkbox" checked/>`);
367
+ }
368
+ this.__sckan.innerHTML = innerHTML.join('\n');
369
+
370
+ this.__sckanCount = SCKAN_STATES.length;
371
+ this.__checkedCount = this.__sckanCount;
372
+ this.__halfCount = Math.trunc(this.__checkedCount/2);
373
+
374
+ this.__button = document.createElement('button');
375
+ this.__button.id = 'map-sckan-button';
376
+ this.__button.className = 'control-button text-button';
377
+ this.__button.setAttribute('type', 'button');
378
+ this.__button.setAttribute('aria-label', 'Show/hide valid SCKAN paths');
379
+ this.__button.setAttribute('control-visible', 'false');
380
+ this.__button.textContent = 'SCKAN';
381
+ this.__button.title = 'Show/hide valid SCKAN paths';
382
+ this.__container.appendChild(this.__button);
383
+
384
+ this.__container.addEventListener('click', this.onClick_.bind(this));
385
+ return this.__container;
386
+ }
387
+
388
+ onRemove()
389
+ //========
390
+ {
391
+ this.__container.parentNode.removeChild(this.__container);
392
+ this.__map = undefined;
393
+ }
394
+
395
+ onClick_(event)
396
+ //=============
397
+ {
398
+ if (event.target.id === 'map-sckan-button') {
399
+ if (this.__button.getAttribute('control-visible') === 'false') {
400
+ this.__container.appendChild(this.__sckan);
401
+ this.__button.setAttribute('control-visible', 'true');
402
+ this.__sckan.focus();
403
+ } else {
404
+ this.__sckan = this.__container.removeChild(this.__sckan);
405
+ this.__button.setAttribute('control-visible', 'false');
406
+ }
407
+ } else if (event.target.tagName === 'INPUT') {
408
+ if (event.target.id === 'sckan-all-paths') {
409
+ if (event.target.indeterminate) {
410
+ event.target.checked = (this.__checkedCount >= this.__halfCount);
411
+ event.target.indeterminate = false;
412
+ }
413
+ if (event.target.checked) {
414
+ this.__checkedCount = this.__sckanCount;
415
+ } else {
416
+ this.__checkedCount = 0;
417
+ }
418
+ for (const state of SCKAN_STATES) {
419
+ const sckanCheckbox = document.getElementById(`sckan-${state.id}`);
420
+ if (sckanCheckbox) {
421
+ sckanCheckbox.checked = event.target.checked;
422
+ this.__flatmap.showSckanPaths(state.id, event.target.checked);
423
+ }
424
+ }
425
+ } else if (event.target.id.startsWith('sckan-')) {
426
+ const sckanId = event.target.id.substring(6);
427
+ this.__flatmap.showSckanPaths(sckanId, event.target.checked);
428
+ if (event.target.checked) {
429
+ this.__checkedCount += 1;
430
+ } else {
431
+ this.__checkedCount -= 1;
432
+ }
433
+ const allLayersCheckbox = document.getElementById('sckan-all-paths');
434
+ if (this.__checkedCount === 0) {
435
+ allLayersCheckbox.checked = false;
436
+ allLayersCheckbox.indeterminate = false;
437
+ } else if (this.__checkedCount === this.__sckanCount) {
438
+ allLayersCheckbox.checked = true;
439
+ allLayersCheckbox.indeterminate = false;
440
+ } else {
441
+ allLayersCheckbox.indeterminate = true;
442
+ }
443
+ }
444
+ }
445
+ event.stopPropagation();
446
+ }
447
+ }
448
+
449
+ //==============================================================================
450
+
319
451
  export class BackgroundControl
320
452
  {
321
453
  constructor(flatmap)
package/src/editor.js ADDED
@@ -0,0 +1,198 @@
1
+ import { Bezier } from "bezier-js";
2
+
3
+
4
+ class BezierCurve
5
+ {
6
+ constructor(id, ...points) {
7
+ this.__id = id;
8
+ this.__bezier = new Bezier(...points);
9
+ }
10
+
11
+ asGeoJSON(samples=100)
12
+ {
13
+ const coords = [];
14
+ for (let ts = 0; ts <= samples; ts++) {
15
+ const pt = this.__bezier.get(float(ts)/float(samples));
16
+ coords.push([pt.x, pt.y]);
17
+ }
18
+ return {
19
+ 'type': 'Feature',
20
+ 'geometry': {
21
+ 'type': 'LineString',
22
+ 'coordinates': coords
23
+ },
24
+ 'properties': {
25
+ 'bezier': this.__id
26
+ }
27
+ };
28
+ }
29
+
30
+ asPoints()
31
+ {
32
+ const geojson = [];
33
+ for (const n of [0, 1, 2, 3]) {
34
+ const pt = this.__bezier.points[n];
35
+ geojson.push({
36
+ 'type': 'Feature',
37
+ 'geometry': {
38
+ 'type': 'Point',
39
+ 'coordinates': [pt.x, pt.y]
40
+ },
41
+ 'properties': {
42
+ 'bezier': this.__id,
43
+ 'point': n
44
+ }
45
+ });
46
+ }
47
+ return geojson;
48
+ }
49
+ }
50
+
51
+
52
+ export class NetworkEditor
53
+ {
54
+ constructor(flatmap)
55
+ {
56
+ this.__map = flatmap.map;
57
+ this.__canvas = this.__map.getCanvasContainer();
58
+
59
+ this.__geojson = { // lines and points as separate sources???
60
+ 'type': 'FeatureCollection',
61
+ 'features': [
62
+ {
63
+ 'type': 'Feature',
64
+ 'geometry': {
65
+ 'type': 'Point',
66
+ 'coordinates': [0, 0]
67
+ },
68
+ 'properties': {
69
+ 'id': 0
70
+ }
71
+ }
72
+ ]
73
+ };
74
+
75
+ // Add a single point to the map
76
+ this.__map.addSource('curves', {
77
+ 'type': 'geojson',
78
+ 'data': this.__geojson
79
+ });
80
+
81
+ this.__map.addLayer({
82
+ 'id': 'lines',
83
+ 'type': 'line',
84
+ 'source': 'curves',
85
+ 'paint': {
86
+ 'line-color': '#3887be'
87
+ }
88
+ });
89
+ this.__map.addLayer({
90
+ 'id': 'points',
91
+ 'type': 'circle',
92
+ 'source': 'curves',
93
+ 'paint': {
94
+ 'circle-radius': 10,
95
+ 'circle-color': '#3887be'
96
+ }
97
+ });
98
+
99
+ this.__currentPoint = null;
100
+
101
+ this.__map.on('mouseenter', this.mouseEnter.bind(this));
102
+ this.__map.on('mouseleave', this.mouseLeave.bind(this));
103
+ this.__map.on('mousedown', this.mouseDown.bind(this));
104
+ this.__map.on('touchstart', this.touchStart.bind(this));
105
+ }
106
+
107
+ addPoint(coords)
108
+ {
109
+ const nextId = this.__geojson.features.length;
110
+ this.__geojson.features.push({
111
+ 'type': 'Feature',
112
+ 'geometry': {
113
+ 'type': 'Point',
114
+ 'coordinates': coords
115
+ },
116
+ 'properties': {
117
+ 'id': nextId
118
+ }
119
+ });
120
+ this.__map.getSource('curves').setData(this.__geojson);
121
+ return nextId;
122
+ }
123
+
124
+ mouseEnter(e) {
125
+ console.log('Mouse enter...');
126
+
127
+ this.__map.setPaintProperty('lines', 'line-color', '#3bb2d0');
128
+ this.__map.setPaintProperty('points', 'circle-color', '#3bb2d0');
129
+ this.__canvas.style.cursor = 'move';
130
+ }
131
+
132
+
133
+ mouseLeave(e) {
134
+ this.__map.setPaintProperty('lines', 'line-color', '#3887be');
135
+ this.__map.setPaintProperty('points', 'circle-color', '#3887be');
136
+ this.__canvas.style.cursor = '';
137
+ }
138
+
139
+
140
+ mouseDown(e) {
141
+ console.log('Mouse down...')
142
+ // Prevent the default map drag behavior.
143
+ e.preventDefault();
144
+
145
+
146
+ const features = this.__map.queryRenderedFeatures(e.point, {'layers': ['lines', 'points']});
147
+ if (features.length === 0) {
148
+ const coords = e.lngLat;
149
+ this.__currentPoint = this.addPoint([coords.lng, coords.lat]);
150
+ } else {
151
+ const currentPoint = features[0].properties.id;
152
+ if (this.__currentPoint === null) {
153
+ this.__currentPoint = currentPoint;
154
+ } else if (this.__currentPoint === currentPoint) {
155
+ this.__currentPoint = null;
156
+ }
157
+ }
158
+
159
+ this.__canvas.style.cursor = 'grab';
160
+
161
+ this.__map.on('mousemove', this.onMove.bind(this));
162
+ this.__map.once('mouseup', this.onUp.bind(this));
163
+
164
+ }
165
+
166
+ touchStart(e) {
167
+ if (e.points.length !== 1) return;
168
+
169
+ // Prevent the default map drag behavior.
170
+ e.preventDefault();
171
+
172
+ this.__map.on('touchmove', this.onMove.bind(this));
173
+ this.__map.once('touchend', this.onUp.bind(this));
174
+ }
175
+
176
+ onMove(e) {
177
+ console.log('Mouse move...')
178
+
179
+ // Set a UI indicator for dragging.
180
+ this.__canvas.style.cursor = 'grabbing'; // ????
181
+
182
+ if (this.__currentPoint !== null) {
183
+ const coords = e.lngLat;
184
+ this.__geojson.features[this.__currentPoint].geometry.coordinates = [coords.lng, coords.lat];
185
+ this.__map.getSource('curves').setData(this.__geojson);
186
+ }
187
+ }
188
+
189
+ onUp(e) {
190
+ console.log('End draw at', e.lngLat)
191
+
192
+ this.__canvas.style.cursor = '';
193
+
194
+ // Unbind mouse/touch events
195
+ this.__map.off('mousemove', this.onMove.bind(this));
196
+ this.__map.off('touchmove', this.onMove.bind(this));
197
+ }
198
+ }
@@ -192,7 +192,8 @@ class FlatMap
192
192
  this._initialState = this.getState();
193
193
 
194
194
  // Add a minimap if option set
195
-
195
+ // Put this above search, info, etc...
196
+ // ==> add all controls here (via interactions.js...)
196
197
  if (this.options.minimap) {
197
198
  this._minimap = new MinimapControl(this, this.options.minimap);
198
199
  this._map.addControl(this._minimap);
@@ -314,6 +315,21 @@ class FlatMap
314
315
  }
315
316
  }
316
317
 
318
+ /**
319
+ * Hide or show all paths valid in SCKAN.
320
+ *
321
+ * @param {string} validity Either ``VALID`` or ``INVALID``
322
+ * @param {boolean} [enable=true] If ``true`` then only show the paths
323
+ * of the type(s) otherwise only hide the paths
324
+ */
325
+ showSckanPaths(validity, enable=true)
326
+ //===================================
327
+ {
328
+ if (this._userInteractions !== null) {
329
+ this._userInteractions.showSckanPaths(validity, enable);
330
+ }
331
+ }
332
+
317
333
  /**
318
334
  * Load images and patterns/textures referenced in style rules.
319
335
  *
@@ -834,15 +850,24 @@ class FlatMap
834
850
  'dataset',
835
851
  'kind',
836
852
  'label',
853
+ 'markup',
837
854
  'models',
838
855
  'nodeId',
839
- 'source'
856
+ 'source',
857
+ 'hyperlinks'
858
+ ];
859
+ const jsonProperties = [
860
+ 'hyperlinks'
840
861
  ];
841
862
  for (const property of exportedProperties) {
842
863
  if (property in properties) {
843
864
  const value = properties[property];
844
865
  if (value !== undefined) {
845
- data[property] = properties[property];
866
+ if (jsonProperties.indexOf(property) >= 0) {
867
+ data[property] = JSON.parse(properties[property])
868
+ } else {
869
+ data[property] = properties[property];
870
+ }
846
871
  }
847
872
  }
848
873
  }
@@ -1117,6 +1142,7 @@ export class MapManager
1117
1142
  let latestMap = null;
1118
1143
  let lastCreatedTime = '';
1119
1144
  for (const map of this._mapList) {
1145
+ // We can break/return if we have a UUID match...
1120
1146
  if (('uuid' in map && mapDescribes === map.uuid
1121
1147
  || mapDescribes === map.id
1122
1148
  || 'taxon' in map && mapDescribes === map.taxon
package/src/info.js CHANGED
@@ -29,6 +29,10 @@ import { indexedProperties } from './search.js';
29
29
  export const displayedProperties = [
30
30
  'id',
31
31
  'class',
32
+ 'featureId',
33
+ 'tile-layer',
34
+ 'layer',
35
+ 'kind',
32
36
  ...indexedProperties
33
37
  ];
34
38
 
@@ -38,7 +38,7 @@ import {displayedProperties} from './info.js';
38
38
  import {InfoControl} from './info.js';
39
39
  import {LayerManager} from './layers.js';
40
40
  import {PATH_TYPES, PATHWAYS_LAYER, Pathways} from './pathways.js';
41
- import {BackgroundControl, LayerControl, PathControl} from './controls.js';
41
+ import {BackgroundControl, LayerControl, PathControl, SCKANControl} from './controls.js';
42
42
  import {SearchControl} from './search.js';
43
43
  import {VECTOR_TILES_SOURCE} from './styling.js';
44
44
 
@@ -171,6 +171,8 @@ export class UserInteractions
171
171
 
172
172
  if (flatmap.options.layerControl) {
173
173
  this._map.addControl(new LayerControl(flatmap, this._layerManager));
174
+ // ************************************
175
+ //this._map.addControl(new SCKANControl(flatmap));
174
176
  }
175
177
 
176
178
  // Flag features that have annotations
@@ -178,6 +180,9 @@ export class UserInteractions
178
180
 
179
181
  for (const [id, ann] of flatmap.annotations) {
180
182
  const feature = this.mapFeature_(id);
183
+ if (id == 118) {
184
+ console.log(feature, ann);
185
+ }
181
186
  if (feature !== undefined) {
182
187
  this._map.setFeatureState(feature, { 'annotated': true });
183
188
  }
@@ -556,6 +561,7 @@ export class UserInteractions
556
561
  }
557
562
  bbox = expandBounds(bbox, annotation.bounds);
558
563
  if ('type' in annotation && annotation.type.startsWith('line')) {
564
+ // FC may have lines that are not pathways features, esp. when authoring...
559
565
  for (const pathFeatureId of this._pathways.lineFeatureIds([featureId])) {
560
566
  if (select) {
561
567
  this.selectFeature_(pathFeatureId);
@@ -777,6 +783,10 @@ export class UserInteractions
777
783
  }
778
784
  }
779
785
  } else {
786
+ // Allow tooltip for body but no highlighting/activation??
787
+ // More generally, only highlight a feature if all "within" the viewport??
788
+ // -- then as we zoom outermost features wouldn't highlight (but still show tooltip)
789
+ // -- or highlight in a more subtle way (eg. opacity change and not colour change)??
780
790
  let labelledFeatures = features.filter(feature => (('hyperlink' in feature.properties
781
791
  || 'label' in feature.properties
782
792
  || 'node' in feature.properties)
@@ -1022,6 +1032,11 @@ export class UserInteractions
1022
1032
  return this._pathways.nodePathModels(nodeId);
1023
1033
  }
1024
1034
 
1035
+ showSckanPaths(validity, enable=true)
1036
+ //===================================
1037
+ {
1038
+ }
1039
+
1025
1040
  //==============================================================================
1026
1041
 
1027
1042
  // Find where to place a label or popup on a feature
package/src/layers.js CHANGED
@@ -20,6 +20,9 @@ limitations under the License.
20
20
 
21
21
  'use strict';
22
22
 
23
+
24
+ // See https://stevage.github.io/map-gl-utils/ for layering ideas...
25
+
23
26
  //==============================================================================
24
27
 
25
28
  import {PATHWAYS_LAYER} from './pathways.js';
package/src/main.js CHANGED
@@ -92,6 +92,8 @@ export async function standaloneViewer(map_endpoint=null, options={})
92
92
  mapManager.loadMap(id, 'map-canvas', (eventType, ...args) => {
93
93
  if (args[0].type === 'control' && args[0].control === 'background') {
94
94
  mapOptions.background = args[0].value;
95
+ } else if (eventType === 'click') {
96
+ console.log(args);
95
97
  }
96
98
  }, mapOptions)
97
99
  .then(map => {
package/src/minimap.js CHANGED
@@ -300,7 +300,7 @@ export class MinimapControl
300
300
  const newBounds = this.moveTrackingRect_(offset);
301
301
 
302
302
  this._map.fitBounds(newBounds, {
303
- duration: 80,
303
+ duration: 80, // Why these options??
304
304
  noMoveStart: true
305
305
  });
306
306
  }
@@ -0,0 +1,617 @@
1
+ /******************************************************************************
2
+
3
+ Flatmap viewer and annotation tool
4
+
5
+ Copyright (c) 2019 David Brooks
6
+
7
+ Licensed under the Apache License, Version 2.0 (the "License");
8
+ you may not use this file except in compliance with the License.
9
+ You may obtain a copy of the License at
10
+
11
+ http://www.apache.org/licenses/LICENSE-2.0
12
+
13
+ Unless required by applicable law or agreed to in writing, software
14
+ distributed under the License is distributed on an "AS IS" BASIS,
15
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ See the License for the specific language governing permissions and
17
+ limitations under the License.
18
+
19
+ ******************************************************************************/
20
+
21
+ 'use strict';
22
+
23
+ //==============================================================================
24
+
25
+ // Needed for Webpack
26
+ import zoomInButton from '../static/images/zoom-in-button.png'
27
+
28
+ //==============================================================================
29
+
30
+ // Make sure colour string is in `#rrggbb` form.
31
+ // Based on https://stackoverflow.com/a/47355187
32
+
33
+ function standardise_color(str){
34
+ const canvas = document.createElement("canvas");
35
+ const ctx = canvas.getContext("2d");
36
+ ctx.fillStyle = str;
37
+ const colour = ctx.fillStyle;
38
+ canvas.remove()
39
+ return colour;
40
+ }
41
+
42
+ //==============================================================================
43
+
44
+ export class NavigationControl
45
+ {
46
+ constructor(flatmap)
47
+ {
48
+ this._flatmap = flatmap;
49
+ this._map = undefined;
50
+ }
51
+
52
+ getDefaultPosition()
53
+ //==================
54
+ {
55
+ return 'top-right';
56
+ }
57
+
58
+ onAdd(map)
59
+ //========
60
+ {
61
+ this._map = map;
62
+ this._container = document.createElement('div');
63
+ this._container.className = 'maplibregl-ctrl navigation-group';
64
+ this._container.innerHTML = `<button id="flatmap-zoom-in" class="navigation-zoom-in" type="button" title="Zoom in" aria-label="Zoom in"></button>
65
+ <button id="flatmap-zoom-out" class="navigation-zoom-out" type="button" title="Zoom out" aria-label="Zoom out"></button>
66
+ <button id="flatmap-reset" class="navigation-reset" type="button" title="Reset" aria-label="Reset"></button>`;
67
+ this._container.onclick = this.onClick_.bind(this);
68
+ return this._container;
69
+ }
70
+
71
+ onRemove()
72
+ //========
73
+ {
74
+ this._container.parentNode.removeChild(this._container);
75
+ this._map = undefined;
76
+ }
77
+
78
+ onClick_(e)
79
+ //=========
80
+ {
81
+ if (e.target.id === 'flatmap-zoom-in') {
82
+ this._flatmap.zoomIn();
83
+ } else if (e.target.id === 'flatmap-zoom-out') {
84
+ this._flatmap.zoomOut();
85
+ } else if (e.target.id === 'flatmap-reset') {
86
+ this._flatmap.resetMap();
87
+ }
88
+ }
89
+ }
90
+
91
+ //==============================================================================
92
+
93
+ export class PathControl
94
+ {
95
+ constructor(flatmap, pathTypes)
96
+ {
97
+ this._flatmap = flatmap;
98
+ this._map = undefined;
99
+ this.__pathTypes = pathTypes;
100
+ }
101
+
102
+ getDefaultPosition()
103
+ //==================
104
+ {
105
+ return 'top-right';
106
+ }
107
+
108
+ onAdd(map)
109
+ //========
110
+ {
111
+ this._map = map;
112
+ this._container = document.createElement('div');
113
+ this._container.className = 'maplibregl-ctrl';
114
+ this._container.id = 'flatmap-nerve-key';
115
+
116
+ this._legend = document.createElement('div');
117
+ this._legend.id = 'nerve-key-text';
118
+ this._legend.className = 'flatmap-nerve-grid';
119
+
120
+ const innerHTML = [];
121
+ innerHTML.push(`<label for="path-all-paths">ALL PATHS:</label><div class="nerve-line"></div><input id="path-all-paths" type="checkbox" checked/>`);
122
+ for (const path of this.__pathTypes) {
123
+ // need to set style background instead of class...
124
+ // background: #2A62F6;
125
+ // background: repeating-linear-gradient(to right,#EA3423 0,#EA3423 6px,transparent 6px,transparent 9px)
126
+ innerHTML.push(`<label for="path-${path.type}">${path.label}</label><div class="nerve-line nerve-${path.type}"></div><input id="path-${path.type}" type="checkbox" checked/>`);
127
+ }
128
+ this._legend.innerHTML = innerHTML.join('\n');
129
+ this.__checkedCount = this.__pathTypes.length;
130
+ this.__halfCount = Math.trunc(this.__checkedCount/2);
131
+
132
+ this._button = document.createElement('button');
133
+ this._button.id = 'nerve-key-button';
134
+ this._button.className = 'control-button text-button';
135
+ this._button.setAttribute('type', 'button');
136
+ this._button.setAttribute('aria-label', 'Nerve paths legend');
137
+ this._button.setAttribute('control-visible', 'false');
138
+ this._button.textContent = 'PATHS';
139
+ this._button.title = 'Show/hide neuron paths';
140
+ this._container.appendChild(this._button);
141
+
142
+ this._container.addEventListener('click', this.onClick_.bind(this));
143
+ return this._container;
144
+ }
145
+
146
+ onRemove()
147
+ //========
148
+ {
149
+ this._container.parentNode.removeChild(this._container);
150
+ this._map = undefined;
151
+ }
152
+
153
+ onClick_(event)
154
+ //=============
155
+ {
156
+ if (event.target.id === 'nerve-key-button') {
157
+ if (this._button.getAttribute('control-visible') === 'false') {
158
+ this._container.appendChild(this._legend);
159
+ this._button.setAttribute('control-visible', 'true');
160
+ this._legend.focus();
161
+ } else {
162
+ this._legend = this._container.removeChild(this._legend);
163
+ this._button.setAttribute('control-visible', 'false');
164
+ }
165
+ } else if (event.target.tagName === 'INPUT') {
166
+ if (event.target.id === 'path-all-paths') {
167
+ if (event.target.indeterminate) {
168
+ event.target.checked = (this.__checkedCount >= this.__halfCount);
169
+ event.target.indeterminate = false;
170
+ }
171
+ if (event.target.checked) {
172
+ this.__checkedCount = this.__pathTypes.length;
173
+ } else {
174
+ this.__checkedCount = 0;
175
+ }
176
+ for (const path of this.__pathTypes) {
177
+ const pathCheckbox = document.getElementById(`path-${path.type}`);
178
+ if (pathCheckbox) {
179
+ pathCheckbox.checked = event.target.checked;
180
+ this._flatmap.enablePath(path.type, event.target.checked);
181
+ }
182
+ }
183
+ } else if (event.target.id.startsWith('path-')) {
184
+ const pathType = event.target.id.substring(5);
185
+ this._flatmap.enablePath(pathType, event.target.checked);
186
+ if (event.target.checked) {
187
+ this.__checkedCount += 1;
188
+ } else {
189
+ this.__checkedCount -= 1;
190
+ }
191
+ const allPathsCheckbox = document.getElementById('path-all-paths');
192
+ if (this.__checkedCount === 0) {
193
+ allPathsCheckbox.checked = false;
194
+ allPathsCheckbox.indeterminate = false;
195
+ } else if (this.__checkedCount === this.__pathTypes.length) {
196
+ allPathsCheckbox.checked = true;
197
+ allPathsCheckbox.indeterminate = false;
198
+ } else {
199
+ allPathsCheckbox.indeterminate = true;
200
+ }
201
+ }
202
+ }
203
+ event.stopPropagation();
204
+ }
205
+ }
206
+
207
+ //==============================================================================
208
+
209
+ export class LayerControl
210
+ {
211
+ constructor(flatmap, layerManager)
212
+ {
213
+ this.__flatmap = flatmap;
214
+ this.__manager = layerManager;
215
+ this.__map = undefined;
216
+ }
217
+
218
+ getDefaultPosition()
219
+ //==================
220
+ {
221
+ return 'top-right';
222
+ }
223
+
224
+ onAdd(map)
225
+ //========
226
+ {
227
+ this.__map = map;
228
+ this.__container = document.createElement('div');
229
+ this.__container.className = 'maplibregl-ctrl';
230
+ this.__container.id = 'flatmap-layer-control';
231
+
232
+ this.__layers = document.createElement('div');
233
+ this.__layers.id = 'layer-control-text';
234
+ this.__layers.className = 'flatmap-layer-grid';
235
+
236
+ const innerHTML = [];
237
+ innerHTML.push(`<label for="layer-all-layers">ALL LAYERS:</label><input id="layer-all-layers" type="checkbox" checked/>`);
238
+ for (const layer of this.__manager.layers) {
239
+ innerHTML.push(`<label for="layer-${layer.id}">${layer.description}</label><input id="layer-${layer.id}" type="checkbox" checked/>`);
240
+ }
241
+ this.__layers.innerHTML = innerHTML.join('\n');
242
+
243
+ this.__layersCount = this.__manager.layers.length;
244
+ this.__checkedCount = this.__layersCount;
245
+ this.__halfCount = Math.trunc(this.__checkedCount/2);
246
+
247
+ this.__button = document.createElement('button');
248
+ this.__button.id = 'map-layers-button';
249
+ this.__button.className = 'control-button text-button';
250
+ this.__button.setAttribute('type', 'button');
251
+ this.__button.setAttribute('aria-label', 'Show/hide map layers');
252
+ this.__button.setAttribute('control-visible', 'false');
253
+ this.__button.textContent = 'LAYERS';
254
+ this.__button.title = 'Show/hide map layers';
255
+ this.__container.appendChild(this.__button);
256
+
257
+ this.__container.addEventListener('click', this.onClick_.bind(this));
258
+ return this.__container;
259
+ }
260
+
261
+ onRemove()
262
+ //========
263
+ {
264
+ this.__container.parentNode.removeChild(this.__container);
265
+ this.__map = undefined;
266
+ }
267
+
268
+ onClick_(event)
269
+ //=============
270
+ {
271
+ if (event.target.id === 'map-layers-button') {
272
+ if (this.__button.getAttribute('control-visible') === 'false') {
273
+ this.__container.appendChild(this.__layers);
274
+ this.__button.setAttribute('control-visible', 'true');
275
+ this.__layers.focus();
276
+ } else {
277
+ this.__layers = this.__container.removeChild(this.__layers);
278
+ this.__button.setAttribute('control-visible', 'false');
279
+ }
280
+ } else if (event.target.tagName === 'INPUT') {
281
+ if (event.target.id === 'layer-all-layers') {
282
+ if (event.target.indeterminate) {
283
+ event.target.checked = (this.__checkedCount >= this.__halfCount);
284
+ event.target.indeterminate = false;
285
+ }
286
+ if (event.target.checked) {
287
+ this.__checkedCount = this.__layersCount;
288
+ } else {
289
+ this.__checkedCount = 0;
290
+ }
291
+ for (const layer of this.__manager.layers) {
292
+ const layerCheckbox = document.getElementById(`layer-${layer.id}`);
293
+ if (layerCheckbox) {
294
+ layerCheckbox.checked = event.target.checked;
295
+ this.__manager.activate(layer.id, event.target.checked);
296
+ }
297
+ }
298
+ } else if (event.target.id.startsWith('layer-')) {
299
+ const layerId = event.target.id.substring(6);
300
+ this.__manager.activate(layerId, event.target.checked);
301
+ if (event.target.checked) {
302
+ this.__checkedCount += 1;
303
+ } else {
304
+ this.__checkedCount -= 1;
305
+ }
306
+ const allLayersCheckbox = document.getElementById('layer-all-layers');
307
+ if (this.__checkedCount === 0) {
308
+ allLayersCheckbox.checked = false;
309
+ allLayersCheckbox.indeterminate = false;
310
+ } else if (this.__checkedCount === this.__layersCount) {
311
+ allLayersCheckbox.checked = true;
312
+ allLayersCheckbox.indeterminate = false;
313
+ } else {
314
+ allLayersCheckbox.indeterminate = true;
315
+ }
316
+ }
317
+ }
318
+ event.stopPropagation();
319
+ }
320
+ }
321
+
322
+ //==============================================================================
323
+
324
+
325
+ const SCKAN_STATES = [
326
+ {
327
+ 'id': 'VALID',
328
+ 'description': 'Path consistent with SCKAN'
329
+ },
330
+ {
331
+ 'id': 'INVALID',
332
+ 'description': 'Path inconsistent with SCKAN'
333
+ }
334
+ ];
335
+
336
+
337
+ export class SCKANControl
338
+ {
339
+ constructor(flatmap)
340
+ {
341
+ this.__flatmap = flatmap;
342
+ this.__map = undefined;
343
+ }
344
+
345
+ getDefaultPosition()
346
+ //==================
347
+ {
348
+ return 'top-right';
349
+ }
350
+
351
+ onAdd(map)
352
+ //========
353
+ {
354
+ this.__map = map;
355
+ this.__container = document.createElement('div');
356
+ this.__container.className = 'maplibregl-ctrl';
357
+ this.__container.id = 'flatmap-layer-control';
358
+
359
+ this.__sckan = document.createElement('div');
360
+ this.__sckan.id = 'sckan-control-text';
361
+ this.__sckan.className = 'flatmap-layer-grid';
362
+
363
+ const innerHTML = [];
364
+ innerHTML.push(`<label for="sckan-all-paths">ALL PATHS:</label><input id="sckan-all-paths" type="checkbox" checked/>`);
365
+ for (const state of SCKAN_STATES) {
366
+ innerHTML.push(`<label for="sckan-${state.id}">${state.description}</label><input id="sckan-${state.id}" type="checkbox" checked/>`);
367
+ }
368
+ this.__sckan.innerHTML = innerHTML.join('\n');
369
+
370
+ this.__sckanCount = SCKAN_STATES.length;
371
+ this.__checkedCount = this.__sckanCount;
372
+ this.__halfCount = Math.trunc(this.__checkedCount/2);
373
+
374
+ this.__button = document.createElement('button');
375
+ this.__button.id = 'map-sckan-button';
376
+ this.__button.className = 'control-button text-button';
377
+ this.__button.setAttribute('type', 'button');
378
+ this.__button.setAttribute('aria-label', 'Show/hide valid SCKAN paths');
379
+ this.__button.setAttribute('control-visible', 'false');
380
+ this.__button.textContent = 'SCKAN';
381
+ this.__button.title = 'Show/hide valid SCKAN paths';
382
+ this.__container.appendChild(this.__button);
383
+
384
+ this.__container.addEventListener('click', this.onClick_.bind(this));
385
+ return this.__container;
386
+ }
387
+
388
+ onRemove()
389
+ //========
390
+ {
391
+ this.__container.parentNode.removeChild(this.__container);
392
+ this.__map = undefined;
393
+ }
394
+
395
+ onClick_(event)
396
+ //=============
397
+ {
398
+ if (event.target.id === 'map-sckan-button') {
399
+ if (this.__button.getAttribute('control-visible') === 'false') {
400
+ this.__container.appendChild(this.__sckan);
401
+ this.__button.setAttribute('control-visible', 'true');
402
+ this.__sckan.focus();
403
+ } else {
404
+ this.__sckan = this.__container.removeChild(this.__sckan);
405
+ this.__button.setAttribute('control-visible', 'false');
406
+ }
407
+ } else if (event.target.tagName === 'INPUT') {
408
+ if (event.target.id === 'sckan-all-paths') {
409
+ if (event.target.indeterminate) {
410
+ event.target.checked = (this.__checkedCount >= this.__halfCount);
411
+ event.target.indeterminate = false;
412
+ }
413
+ if (event.target.checked) {
414
+ this.__checkedCount = this.__sckanCount;
415
+ } else {
416
+ this.__checkedCount = 0;
417
+ }
418
+ for (const state of SCKAN_STATES) {
419
+ const sckanCheckbox = document.getElementById(`sckan-${state.id}`);
420
+ if (sckanCheckbox) {
421
+ sckanCheckbox.checked = event.target.checked;
422
+ this.__flatmap.showSckanPaths(state.id, event.target.checked);
423
+ }
424
+ }
425
+ } else if (event.target.id.startsWith('sckan-')) {
426
+ const sckanId = event.target.id.substring(6);
427
+ this.__flatmap.showSckanPaths(sckanId, event.target.checked);
428
+ if (event.target.checked) {
429
+ this.__checkedCount += 1;
430
+ } else {
431
+ this.__checkedCount -= 1;
432
+ }
433
+ const allLayersCheckbox = document.getElementById('sckan-all-paths');
434
+ if (this.__checkedCount === 0) {
435
+ allLayersCheckbox.checked = false;
436
+ allLayersCheckbox.indeterminate = false;
437
+ } else if (this.__checkedCount === this.__sckanCount) {
438
+ allLayersCheckbox.checked = true;
439
+ allLayersCheckbox.indeterminate = false;
440
+ } else {
441
+ allLayersCheckbox.indeterminate = true;
442
+ }
443
+ }
444
+ }
445
+ event.stopPropagation();
446
+ }
447
+ }
448
+
449
+
450
+
451
+
452
+ class StateControl
453
+ {
454
+ constructor(id, flatmap, states, controlFunction)
455
+ {
456
+ this.__id = id;
457
+ this.__flatmap = flatmap;
458
+ this.__states = states;
459
+ this.__controlFunction = controlFunction;
460
+ this.__map = undefined;
461
+ }
462
+
463
+ getDefaultPosition()
464
+ //==================
465
+ {
466
+ return 'top-right';
467
+ }
468
+
469
+ onAdd(map)
470
+ //========
471
+ {
472
+ this.__map = map;
473
+ this.__container = document.createElement('div');
474
+ this.__container.className = 'maplibregl-ctrl';
475
+ this.__container.id = 'flatmap-state-control';
476
+
477
+ this.__control = document.createElement('div');
478
+ this.__control.id = `${this.__id}-control-text`;
479
+ this.__control.className = 'flatmap-state-grid';
480
+
481
+ const innerHTML = [];
482
+ innerHTML.push(`<label for="sckan-all-paths">ALL PATHS:</label><input id="sckan-all-paths" type="checkbox" checked/>`);
483
+ for (const state of SCKAN_STATES) {
484
+ innerHTML.push(`<label for="sckan-${state.id}">${state.description}</label><input id="sckan-${state.id}" type="checkbox" checked/>`);
485
+ }
486
+ this.__sckan.innerHTML = innerHTML.join('\n');
487
+
488
+ this.__sckanCount = SCKAN_STATES.length;
489
+ this.__checkedCount = this.__sckanCount;
490
+ this.__halfCount = Math.trunc(this.__checkedCount/2);
491
+
492
+ this.__button = document.createElement('button');
493
+ this.__button.id = 'map-sckan-button';
494
+ this.__button.className = 'control-button text-button';
495
+ this.__button.setAttribute('type', 'button');
496
+ this.__button.setAttribute('aria-label', 'Show/hide valid SCKAN paths');
497
+ this.__button.setAttribute('control-visible', 'false');
498
+ this.__button.textContent = 'SCKAN';
499
+ this.__button.title = 'Show/hide valid SCKAN paths';
500
+ this.__container.appendChild(this.__button);
501
+
502
+ this.__container.addEventListener('click', this.onClick_.bind(this));
503
+ return this.__container;
504
+ }
505
+
506
+ onRemove()
507
+ //========
508
+ {
509
+ this.__container.parentNode.removeChild(this.__container);
510
+ this.__map = undefined;
511
+ }
512
+
513
+ onClick_(event)
514
+ //=============
515
+ {
516
+ if (event.target.id === 'map-sckan-button') {
517
+ if (this.__button.getAttribute('control-visible') === 'false') {
518
+ this.__container.appendChild(this.__sckan);
519
+ this.__button.setAttribute('control-visible', 'true');
520
+ this.__sckan.focus();
521
+ } else {
522
+ this.__sckan = this.__container.removeChild(this.__sckan);
523
+ this.__button.setAttribute('control-visible', 'false');
524
+ }
525
+ } else if (event.target.tagName === 'INPUT') {
526
+ if (event.target.id === 'sckan-all-paths') {
527
+ if (event.target.indeterminate) {
528
+ event.target.checked = (this.__checkedCount >= this.__halfCount);
529
+ event.target.indeterminate = false;
530
+ }
531
+ if (event.target.checked) {
532
+ this.__checkedCount = this.__sckanCount;
533
+ } else {
534
+ this.__checkedCount = 0;
535
+ }
536
+ for (const state of SCKAN_STATES) {
537
+ const sckanCheckbox = document.getElementById(`sckan-${state.id}`);
538
+ if (sckanCheckbox) {
539
+ sckanCheckbox.checked = event.target.checked;
540
+ this.__flatmap.showSckanPaths(state.id, event.target.checked);
541
+ }
542
+ }
543
+ } else if (event.target.id.startsWith('sckan-')) {
544
+ const sckanId = event.target.id.substring(6);
545
+ this.__flatmap.showSckanPaths(sckanId, event.target.checked);
546
+ if (event.target.checked) {
547
+ this.__checkedCount += 1;
548
+ } else {
549
+ this.__checkedCount -= 1;
550
+ }
551
+ const allLayersCheckbox = document.getElementById('sckan-all-paths');
552
+ if (this.__checkedCount === 0) {
553
+ allLayersCheckbox.checked = false;
554
+ allLayersCheckbox.indeterminate = false;
555
+ } else if (this.__checkedCount === this.__sckanCount) {
556
+ allLayersCheckbox.checked = true;
557
+ allLayersCheckbox.indeterminate = false;
558
+ } else {
559
+ allLayersCheckbox.indeterminate = true;
560
+ }
561
+ }
562
+ }
563
+ event.stopPropagation();
564
+ }
565
+ }
566
+
567
+ //==============================================================================
568
+
569
+ export class BackgroundControl
570
+ {
571
+ constructor(flatmap)
572
+ {
573
+ this.__flatmap = flatmap;
574
+ this.__map = undefined;
575
+ }
576
+
577
+ getDefaultPosition()
578
+ //==================
579
+ {
580
+ return 'bottom-right';
581
+ }
582
+
583
+ onAdd(map)
584
+ //========
585
+ {
586
+ this.__map = map;
587
+ this.__container = document.createElement('div');
588
+ this.__container.className = 'maplibregl-ctrl';
589
+ this.__colourDiv = document.createElement('div');
590
+ this.__colourDiv.setAttribute('aria-label', 'Change background colour');
591
+ this.__colourDiv.title = 'Change background colour';
592
+ const background = standardise_color(this.__flatmap.getBackgroundColour());
593
+ this.__colourDiv.innerHTML = `<input type="color" id="colourPicker" value="${background}">`;
594
+ this.__container.appendChild(this.__colourDiv);
595
+ this.__colourDiv.addEventListener('input', this.__updateColour.bind(this), false);
596
+ this.__colourDiv.addEventListener('change', this.__updateColour.bind(this), false);
597
+ return this.__container;
598
+ }
599
+
600
+ onRemove()
601
+ //========
602
+ {
603
+ this.__container.parentNode.removeChild(this.__container);
604
+ this.__map = undefined;
605
+ }
606
+
607
+ __updateColour(event)
608
+ //===================
609
+ {
610
+ const colour = event.target.value;
611
+ this.__flatmap.setBackgroundColour(colour);
612
+ this.__flatmap.controlEvent('change', 'background', colour)
613
+ event.stopPropagation();
614
+ }
615
+ }
616
+
617
+ //==============================================================================
package/src/pathways.js CHANGED
@@ -28,6 +28,7 @@ export const PATHWAYS_LAYER = 'pathways';
28
28
 
29
29
  export const PATH_TYPES = [
30
30
  { type: "cns", label: "CNS", colour: "#9B1FC1"},
31
+ { type: "intracardiac", label: "Local circuit neuron", colour: "#F19E38"},
31
32
  { type: "lcn", label: "Local circuit neuron", colour: "#F19E38"},
32
33
  { type: "para-pre", label: "Parasympathetic pre-ganglionic", colour: "#3F8F4A"},
33
34
  { type: "para-post", label: "Parasympathetic post-ganglionic", colour: "#3F8F4A"},
@@ -35,7 +36,9 @@ export const PATH_TYPES = [
35
36
  { type: "somatic", label: "Somatic lower motor", colour: "#98561D"},
36
37
  { type: "symp-pre", label: "Sympathetic pre-ganglionic", colour: "#EA3423"},
37
38
  { type: "symp-post", label: "Sympathetic post-ganglionic", colour: "#EA3423"},
38
- { type: "other", label: "Other neuron type", colour: "#888"}
39
+ { type: "other", label: "Other neuron type", colour: "#888"},
40
+ { type: "arterial", label: "Arterial blood vessel", colour: "#F00"},
41
+ { type: "venous", label: "Venous blood vessel", colour: "#2F6EBA"}
39
42
  ];
40
43
 
41
44
  export const PATH_STYLE_RULES =
package/src/search.js CHANGED
@@ -29,6 +29,7 @@ import MiniSearch from 'minisearch';
29
29
  // The properties of a feature we index and show
30
30
 
31
31
  export const indexedProperties = [
32
+ 'id',
32
33
  'label',
33
34
  'models',
34
35
  'source'
package/src/styling.js CHANGED
@@ -134,7 +134,8 @@ export class FeatureFillLayer extends VectorStyleLayer
134
134
  'fill-opacity': [
135
135
  'case',
136
136
  ['boolean', ['feature-state', 'selected'], false], 0.7,
137
- ['has', 'colour'], activeRasterLayer ? 0.008 : 1.0,
137
+ ['has', 'opacity'], ['get', 'opacity'],
138
+ ['has', 'colour'], 1.0,
138
139
  ['boolean', ['feature-state', 'active'], false], 0.7,
139
140
  ['has', 'node'], 0.3,
140
141
  ['any',
@@ -192,6 +193,7 @@ export class FeatureBorderLayer extends VectorStyleLayer
192
193
  lineColour.push('#000');
193
194
  lineColour.push(['has', 'node']);
194
195
  lineColour.push('#AFA202');
196
+ // this colour should be complement of background colour...
195
197
  lineColour.push('#444');
196
198
 
197
199
  const lineOpacity = [
@@ -261,7 +263,6 @@ export class FeatureLineLayer extends VectorStyleLayer
261
263
  :
262
264
  [
263
265
  'any',
264
- ['has', 'centreline'],
265
266
  ['==', 'type', 'bezier'],
266
267
  ['==', 'type', `line`]
267
268
  ];
@@ -278,7 +279,6 @@ export class FeatureLineLayer extends VectorStyleLayer
278
279
  ['has', 'colour'], ['get', 'colour'],
279
280
  ['boolean', ['feature-state', 'active'], false], coloured ? '#888' : '#CCC',
280
281
  ['==', ['get', 'type'], 'network'], '#AFA202',
281
- ['has', 'centreline'], '#888',
282
282
  options.authoring ? '#C44' : '#444'
283
283
  ],
284
284
  'line-opacity': [
@@ -292,7 +292,6 @@ export class FeatureLineLayer extends VectorStyleLayer
292
292
  'let',
293
293
  'width', [
294
294
  'case',
295
- ['has', 'centreline'], 1.2,
296
295
  ['==', ['get', 'type'], 'network'], 1.2,
297
296
  ['boolean', ['feature-state', 'selected'], false], 1.2,
298
297
  ['boolean', ['feature-state', 'active'], false], 1.2,
@@ -358,6 +357,7 @@ export class PathLineLayer extends VectorStyleLayer
358
357
  [
359
358
  'any',
360
359
  ['==', 'type', 'bezier'],
360
+ ['==', 'type', 'centreline'],
361
361
  ['==', 'type', `line`]
362
362
  ];
363
363
  this.__dashed = dashed;
@@ -372,7 +372,8 @@ export class PathLineLayer extends VectorStyleLayer
372
372
  ['boolean', ['feature-state', 'selected'], false], '#0F0',
373
373
  ['boolean', ['feature-state', 'hidden'], false], '#CCC',
374
374
  ['==', ['get', 'type'], 'bezier'], 'red',
375
- ['==', ['get', 'kind'], 'error'], '#FFFE0E',
375
+ ['==', ['get', 'type'], 'centreline'], '#00F',
376
+ ['has', 'error'], '#FFFE0E',
376
377
  ['==', ['get', 'kind'], 'unknown'], '#888',
377
378
  ...PATH_STYLE_RULES,
378
379
  '#888'
@@ -384,21 +385,23 @@ export class PathLineLayer extends VectorStyleLayer
384
385
  ['boolean', ['get', 'invisible'], false], 0.001,
385
386
  ['boolean', ['feature-state', 'selected'], false], 1.0,
386
387
  ['boolean', ['feature-state', 'active'], false], 1.0,
388
+ // Only dim lines when other lines are selected, not if just features selected??
387
389
  dimmed ? 0.1 : 0.8
388
390
  ],
389
391
  'line-width': [
390
392
  'let',
391
- 'width', [
393
+ 'width', ["*", [
392
394
  'case',
393
395
  ['==', ['get', 'type'], 'bezier'], 0.1,
394
- ['==', ['get', 'kind'], 'error'], 1,
396
+ ['has', 'error'], 1,
395
397
  ['==', ['get', 'kind'], 'unknown'], 1,
396
398
  ['boolean', ['get', 'invisible'], false], 0.1,
397
- ['boolean', ['feature-state', 'selected'], false], 1.2,
398
- ['boolean', ['feature-state', 'active'], false], 1.2,
399
- 0.8
400
- ], [
401
- 'interpolate',
399
+ ['boolean', ['feature-state', 'selected'], false], 0.6,
400
+ ['boolean', ['feature-state', 'active'], false], 0.9,
401
+ 0.6
402
+ ],
403
+ ['case', ['has', 'stroke-width'], ['get', 'stroke-width'], 1.0]],
404
+ ['interpolate',
402
405
  ['exponential', 2],
403
406
  ['zoom'],
404
407
  2, ["*", ['var', 'width'], ["^", 2, -0.5]],
@@ -408,7 +411,7 @@ export class PathLineLayer extends VectorStyleLayer
408
411
  ]
409
412
  };
410
413
  if (this.__dashed) {
411
- paintStyle['line-dasharray'] = [3, 2];
414
+ paintStyle['line-dasharray'] = [1, 1];
412
415
  }
413
416
  return super.changedPaintStyle(paintStyle, changes);
414
417
  }