@abi-software/flatmap-viewer 2.3.0-b.1 → 2.3.0-b.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@2.3.0-b.1``
41
+ * ``npm install @abi-software/flatmap-viewer@2.3.0-b.2``
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.3.0-b.1",
3
+ "version": "2.3.0-b.2",
4
4
  "description": "Flatmap viewer using Maplibre GL",
5
5
  "repository": "https://github.com/AnatomicMaps/flatmap-viewer.git",
6
6
  "main": "src/main.js",
@@ -38,14 +38,12 @@
38
38
  "browser-sync": "^2.26.7",
39
39
  "bs-fullscreen-message": "^1.1.0",
40
40
  "clean-webpack-plugin": "^3.0.0",
41
- "css-loader": "^6.5.1",
41
+ "css-loader": "^6.7.3",
42
42
  "eslint": "^8.7.0",
43
43
  "express": "^4.17.1",
44
- "file-loader": "^6.2.0",
45
44
  "html-webpack-plugin": "^4.5.2",
46
45
  "strip-ansi": "^7.0.1",
47
- "style-loader": "^1.0.0",
48
- "url-loader": "^4.1.0",
46
+ "style-loader": "^3.3.2",
49
47
  "webpack": "^5.16.0",
50
48
  "webpack-cli": "^4.4.0",
51
49
  "webpack-dev-middleware": "^4.1.0",
package/src/annotation.js CHANGED
@@ -116,12 +116,6 @@ export class Annotator
116
116
  async __authorise(panel)
117
117
  //======================
118
118
  {
119
- /*
120
- const testUser = {name: 'Testing...'};
121
- this.__setUser(testUser);
122
- this.__authorised = true;
123
- return Promise.resolve(testUser);
124
- */
125
119
  const abortController = new AbortController();
126
120
  setTimeout((panel) => {
127
121
  if (this.user === 'undefined') {
@@ -2,7 +2,7 @@
2
2
 
3
3
  Flatmap viewer and annotation tool
4
4
 
5
- Copyright (c) 2019 David Brooks
5
+ Copyright (c) 2019 - 2023 David Brooks
6
6
 
7
7
  Licensed under the Apache License, Version 2.0 (the "License");
8
8
  you may not use this file except in compliance with the License.
@@ -18,13 +18,6 @@ limitations under the License.
18
18
 
19
19
  ******************************************************************************/
20
20
 
21
- 'use strict';
22
-
23
- //==============================================================================
24
-
25
-
26
- //==============================================================================
27
-
28
21
  // Make sure colour string is in `#rrggbb` form.
29
22
  // Based on https://stackoverflow.com/a/47355187
30
23
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  Flatmap viewer and annotation tool
4
4
 
5
- Copyright (c) 2019 David Brooks
5
+ Copyright (c) 2019 - 2023 David Brooks
6
6
 
7
7
  Licensed under the Apache License, Version 2.0 (the "License");
8
8
  you may not use this file except in compliance with the License.
@@ -18,11 +18,7 @@ limitations under the License.
18
18
 
19
19
  ******************************************************************************/
20
20
 
21
- 'use strict';
22
-
23
- //==============================================================================
24
-
25
- import { indexedProperties } from './search.js';
21
+ import { indexedProperties } from '../search.js';
26
22
 
27
23
  //==============================================================================
28
24
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  Flatmap viewer and annotation tool
4
4
 
5
- Copyright (c) 2019 David Brooks
5
+ Copyright (c) 2019 - 2023 David Brooks
6
6
 
7
7
  Licensed under the Apache License, Version 2.0 (the "License");
8
8
  you may not use this file except in compliance with the License.
@@ -45,10 +45,6 @@ limitations under the License.
45
45
 
46
46
  //==============================================================================
47
47
 
48
- 'use strict';
49
-
50
- //==============================================================================
51
-
52
48
  import maplibre from 'maplibre-gl';
53
49
 
54
50
  //==============================================================================
@@ -0,0 +1,113 @@
1
+ /******************************************************************************
2
+
3
+ Flatmap viewer and annotation tool
4
+
5
+ Copyright (c) 2019 - 2023 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
+
22
+ export class SearchControl
23
+ {
24
+ constructor(flatmap)
25
+ {
26
+ this.__flatmap = flatmap;
27
+ }
28
+
29
+ onAdd(map)
30
+ //========
31
+ {
32
+ this._map = map;
33
+ this._container = document.createElement('div');
34
+ this._container.className = 'maplibregl-ctrl search-control';
35
+
36
+ this._input = document.createElement('input');
37
+ this._input.id = 'search-control-input';
38
+ this._input.setAttribute('type', 'search');
39
+ this._input.setAttribute('visible', 'false');
40
+ this._input.setAttribute('placeholder', 'Search...');
41
+
42
+ this._button = document.createElement('button');
43
+ this._button.id = 'search-control-button';
44
+ this._button.className = 'control-button';
45
+ this._button.title = 'Search flatmap';
46
+ this._button.setAttribute('type', 'button');
47
+ this._button.setAttribute('aria-label', 'Search flatmap');
48
+ // https://iconmonstr.com/magnifier-6-svg/
49
+ this._button.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" id="search-control-icon" viewBox="0 0 24 24">
50
+ <path d="M21.172 24l-7.387-7.387c-1.388.874-3.024 1.387-4.785 1.387-4.971 0-9-4.029-9-9s4.029-9 9-9 9 4.029 9 9c0 1.761-.514 3.398-1.387 4.785l7.387 7.387-2.828 2.828zm-12.172-8c3.859 0 7-3.14 7-7s-3.141-7-7-7-7 3.14-7 7 3.141 7 7 7z"/>
51
+ </svg>`;
52
+ this._container.appendChild(this._button);
53
+
54
+ this._container.onclick = this.onClick_.bind(this);
55
+ return this._container;
56
+ }
57
+
58
+ getDefaultPosition()
59
+ //==================
60
+ {
61
+ return 'top-right';
62
+ }
63
+
64
+ onRemove()
65
+ //========
66
+ {
67
+ this._container.parentNode.removeChild(this._container);
68
+ this._map = undefined;
69
+ }
70
+
71
+ searchMap_(search=true)
72
+ //=====================
73
+ {
74
+ this._input = this._container.removeChild(this._input);
75
+ this._input.setAttribute('visible', 'false');
76
+ const text = this._input.value;
77
+ if (search && text !== '') {
78
+ const results = this.__flatmap.search(text);
79
+ this.__flatmap.showSearchResults(results);
80
+ }
81
+ }
82
+
83
+ onKeyDown_(e)
84
+ //===========
85
+ {
86
+ if (e.key === 'Enter') {
87
+ this.searchMap_();
88
+ } else if (e.key === 'Escape') {
89
+ this.searchMap_(false);
90
+ }
91
+ }
92
+
93
+ onClick_(e)
94
+ //=========
95
+ {
96
+ const targetId = ('rangeTarget' in e) ? e.rangeTarget.id : e.target.id; // FF has rangeTarget
97
+ if (['search-control-button', 'search-control-icon'].includes(targetId)) {
98
+ if (this._input.getAttribute('visible') === 'false') {
99
+ this._container.appendChild(this._input);
100
+ this._container.appendChild(this._button);
101
+ this._input.setAttribute('visible', 'true');
102
+ this._input.onkeydown = this.onKeyDown_.bind(this);
103
+ this._input.value = '';
104
+ this.__flatmap.clearSearchResults();
105
+ this._input.focus();
106
+ } else {
107
+ this.searchMap_();
108
+ }
109
+ }
110
+ }
111
+ }
112
+
113
+ //==============================================================================
@@ -0,0 +1,66 @@
1
+ /******************************************************************************
2
+
3
+ Flatmap viewer and annotation tool
4
+
5
+ Copyright (c) 2019 - 2023 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
+
22
+ import { Control } from './controls';
23
+
24
+ //==============================================================================
25
+
26
+ export class SystemsControl extends Control
27
+ {
28
+ constructor(flatmap, systems)
29
+ {
30
+ super(flatmap, 'system', 'systems');
31
+ this.__systems = systems;
32
+ }
33
+
34
+ __innerLinesHTML()
35
+ //================
36
+ {
37
+ const html = [];
38
+ for (const system of this.__systems) {
39
+ html.push(`<label for="${this.__prefix}${system.id}" style="background: ${system.colour};">${system.name}</label><input id="${this.__prefix}${system.id}" type="checkbox" checked/>`);
40
+ }
41
+ return html;
42
+ }
43
+
44
+ __enableAll(enable)
45
+ //=================
46
+ {
47
+ for (const system of this.__systems) {
48
+ const checkbox = document.getElementById(`${this.__prefix}${system.id}`);
49
+ if (checkbox) {
50
+ checkbox.checked = enable;
51
+ this.__flatmap.enableSystem(system.name, enable);
52
+ }
53
+ }
54
+ }
55
+
56
+ __enableControl(id, enable)
57
+ //=========================
58
+ {
59
+ for (const system of this.__systems) {
60
+ if (id === system.id) {
61
+ this.__flatmap.enableSystem(system.name, enable);
62
+ }
63
+ }
64
+ }
65
+
66
+ }
@@ -34,11 +34,12 @@ import '../static/css/flatmap-viewer.css';
34
34
  //==============================================================================
35
35
 
36
36
  import {MapServer} from './mapserver.js';
37
- import {MinimapControl} from './minimap.js';
38
- import {NavigationControl} from './controls.js';
39
37
  import {SearchIndex} from './search.js';
40
38
  import {UserInteractions} from './interactions.js';
41
39
 
40
+ import {MinimapControl} from './controls/minimap.js';
41
+ import {NavigationControl} from './controls/controls.js';
42
+
42
43
  import * as images from './images.js';
43
44
  import * as utils from './utils.js';
44
45
 
@@ -1301,6 +1302,7 @@ export class MapManager
1301
1302
  * @arg options.showPosition {boolean} Show ``position`` of tooltip.
1302
1303
  * @arg options.standalone {boolean} Viewer is running ``standalone``, as opposed to integrated into
1303
1304
  * another application so show a number of controls. Defaults to ``false``.
1305
+ * @arg options.annotator {boolean} Allow interactive annotation of features and paths.
1304
1306
  * @example
1305
1307
  * const humanMap1 = mapManager.loadMap('humanV1', 'div-1');
1306
1308
  *
@@ -34,14 +34,16 @@ import polylabel from 'polylabel';
34
34
  //==============================================================================
35
35
 
36
36
  import {Annotator} from './annotation';
37
- import {displayedProperties, InfoControl} from './info';
38
37
  import {LayerManager} from './layers';
39
38
  import {PATHWAYS_LAYER, Pathways} from './pathways';
39
+ import {COLOUR_ERROR, VECTOR_TILES_SOURCE} from './styling';
40
+ import {SystemsManager} from './systems';
41
+
42
+ import {displayedProperties, InfoControl} from './controls/info';
40
43
  import {BackgroundControl, LayerControl, NerveControl,
41
- PathControl, SCKANControl} from './controls';
42
- import {SearchControl} from './search';
43
- import {VECTOR_TILES_SOURCE} from './styling';
44
- import {SystemsControl, SystemsManager} from './systems';
44
+ PathControl, SCKANControl} from './controls/controls';
45
+ import {SearchControl} from './controls/search';
46
+ import {SystemsControl} from './controls/systems';
45
47
 
46
48
  import * as pathways from './pathways';
47
49
  import * as utils from './utils';
@@ -144,25 +146,16 @@ export class UserInteractions
144
146
  }
145
147
  }
146
148
 
147
- this.__featureIdToMapId = new Map();
148
- this.__setupAnnotation();
149
+ // Add annotation capability
150
+ if (flatmap.options.annotator) {
151
+ this.__setupAnnotation();
152
+ } else {
153
+ this.__annotator = null;
154
+ }
149
155
 
150
156
  // Note features that are FC systems
151
157
 
152
- this.__systems = new Map();
153
- for (const [id, ann] of this._flatmap.annotations) {
154
- if (ann['fc-class'] === 'fc-class:System') {
155
- if (this.__systems.has(ann.name)) {
156
- this.__systems.get(ann.name).featureIds.push(ann.featureId)
157
- } else {
158
- this.__systems.set(ann.name, {
159
- id: ann.name.replaceAll(' ', '_'),
160
- colour: ann.colour,
161
- featureIds: [ ann.featureId ]
162
- });
163
- }
164
- }
165
- }
158
+ this.__systemsManager = new SystemsManager(this._flatmap, this);
166
159
 
167
160
  // Add various controls when running standalone
168
161
 
@@ -191,7 +184,7 @@ export class UserInteractions
191
184
 
192
185
  // SCKAN path and SYSTEMS controls for FC maps
193
186
  if (flatmap.options.style === 'functional') {
194
- this._map.addControl(new SystemsControl(flatmap, this.__systems));
187
+ this._map.addControl(new SystemsControl(flatmap, this.__systemsManager.systems));
195
188
  this._map.addControl(new SCKANControl(flatmap, flatmap.options.layerOptions));
196
189
  }
197
190
  }
@@ -258,10 +251,10 @@ export class UserInteractions
258
251
  const annotated_features = await this.__annotator.annotated_features();
259
252
 
260
253
  // Flag features that have annotations
261
-
254
+ this.__featureIdToMapId = new Map();
262
255
  for (const [mapId, ann] of this._flatmap.annotations) {
263
256
  this.__featureIdToMapId.set(ann.id, mapId);
264
- const feature = this.mapFeature_(mapId);
257
+ const feature = this.mapFeature(mapId);
265
258
  if (feature !== undefined) {
266
259
  this._map.setFeatureState(feature, { 'map-annotation': true });
267
260
  if (annotated_features.indexOf(ann.id) >= 0) {
@@ -274,11 +267,13 @@ export class UserInteractions
274
267
  setFeatureAnnotated(featureId)
275
268
  //============================
276
269
  {
277
- // featureId v's geoJSON id
278
- const mapId = this.__featureIdToMapId.get(featureId);
279
- const feature = this.mapFeature_(mapId);
280
- if (feature !== undefined) {
281
- this._map.setFeatureState(feature, { 'annotated': true });
270
+ if (this.__annotator) {
271
+ // featureId v's geoJSON id
272
+ const mapId = this.__featureIdToMapId.get(featureId);
273
+ const feature = this.mapFeature(mapId);
274
+ if (feature !== undefined) {
275
+ this._map.setFeatureState(feature, { 'annotated': true });
276
+ }
282
277
  }
283
278
  }
284
279
 
@@ -304,34 +299,35 @@ export class UserInteractions
304
299
  getSystems()
305
300
  //==========
306
301
  {
307
- const systems = [];
308
- for (const system of this.__systems.values()) {
309
- systems.push({
310
- name: system.name,
311
- colour: system.colour,
312
- });
313
- }
314
- return systems;
302
+ return this.__systemsManager.systems;
315
303
  }
316
304
 
317
305
  enableSystem(systemName, enable=true)
318
306
  //===================================
319
307
  {
320
- if (this.__systems.has(systemName)) {
321
- for (const featureId of this.__systems.get(systemName).featureIds) {
322
- this.__enableFeatureWithChildren(featureId, enable);
308
+ this.__systemsManager.enable(systemName, enable);
309
+ }
310
+
311
+ enableFeatureWithChildren(featureId, enable=true)
312
+ //===============================================
313
+ {
314
+ const feature = this.mapFeature(featureId);
315
+ if (feature !== undefined) {
316
+ this.enableFeature(feature, enable);
317
+ for (const childFeatureId of feature.children) {
318
+ this.enableFeatureWithChildren(childFeatureId, enable);
323
319
  }
324
320
  }
325
321
  }
326
322
 
327
- __enableFeatureWithChildren(featureId, enable=true)
328
- //=================================================
323
+ __enableFeatureWithParents(featureId, enable=true)
324
+ //================================================
329
325
  {
330
- const feature = this.mapFeature_(featureId);
326
+ const feature = this.mapFeature(featureId);
331
327
  if (feature !== undefined) {
332
- this.__enableFeature(feature, enable);
333
- for (const childFeatureId of feature.children) {
334
- this.__enableFeatureWithChildren(childFeatureId, enable);
328
+ this.enableFeature(feature, enable);
329
+ for (const childFeatureId of feature.parents) {
330
+ this.__enableFeatureWithParents(childFeatureId, enable);
335
331
  }
336
332
  }
337
333
  }
@@ -348,8 +344,8 @@ export class UserInteractions
348
344
  }
349
345
  }
350
346
 
351
- __enableFeature(feature, enable=true)
352
- //===================================
347
+ enableFeature(feature, enable=true)
348
+ //=================================
353
349
  {
354
350
  if (feature !== undefined) {
355
351
  if (enable) {
@@ -361,8 +357,8 @@ export class UserInteractions
361
357
  }
362
358
  }
363
359
 
364
- mapFeature_(featureId)
365
- //====================
360
+ mapFeature(featureId)
361
+ //===================
366
362
  {
367
363
  const ann = this._flatmap.annotation(featureId);
368
364
  if (ann !== undefined) {
@@ -394,7 +390,7 @@ export class UserInteractions
394
390
  if (this._selectedFeatureIds.has(featureId)) {
395
391
  this._selectedFeatureIds.set(featureId, this._selectedFeatureIds.get(featureId) + 1);
396
392
  } else {
397
- const feature = this.mapFeature_(featureId);
393
+ const feature = this.mapFeature(featureId);
398
394
  if (feature !== undefined) {
399
395
  this._map.setFeatureState(feature, { 'selected': true });
400
396
  this._selectedFeatureIds.set(featureId, 1);
@@ -411,7 +407,7 @@ export class UserInteractions
411
407
  if (references > 1) {
412
408
  this._selectedFeatureIds.set(featureId, references - 1);
413
409
  } else {
414
- const feature = this.mapFeature_(featureId);
410
+ const feature = this.mapFeature(featureId);
415
411
  if (feature !== undefined) {
416
412
  this._map.removeFeatureState(feature, 'selected');
417
413
  this._selectedFeatureIds.delete(+featureId);
@@ -427,7 +423,7 @@ export class UserInteractions
427
423
  //==================
428
424
  {
429
425
  for (const featureId of this._selectedFeatureIds.keys()) {
430
- const feature = this.mapFeature_(featureId);
426
+ const feature = this.mapFeature(featureId);
431
427
  if (feature !== undefined) {
432
428
  this._map.removeFeatureState(feature, 'selected');
433
429
  }
@@ -457,7 +453,7 @@ export class UserInteractions
457
453
  //==========================
458
454
  {
459
455
  featureId = +featureId; // Ensure numeric
460
- this.__activateFeature(this.mapFeature_(featureId));
456
+ this.__activateFeature(this.mapFeature(featureId));
461
457
  }
462
458
 
463
459
  unhighlightFeatures_()
@@ -833,7 +829,7 @@ export class UserInteractions
833
829
  const lineIds = new Set(lineFeatures.map(f => f.properties.featureId));
834
830
  for (const featureId of this._pathways.lineFeatureIds(lineIds)) {
835
831
  if (+featureId !== lineFeatureId) {
836
- this.__activateFeature(this.mapFeature_(featureId));
832
+ this.__activateFeature(this.mapFeature(featureId));
837
833
  }
838
834
  }
839
835
  }
@@ -957,6 +953,10 @@ export class UserInteractions
957
953
  __annotationEvent(feature)
958
954
  //========================
959
955
  {
956
+ if (!this.__annotator) {
957
+ return;
958
+ }
959
+
960
960
  event.preventDefault();
961
961
 
962
962
  // Remove any tooltip
@@ -1016,12 +1016,12 @@ export class UserInteractions
1016
1016
  {
1017
1017
  if ('nerveId' in feature.properties) {
1018
1018
  for (const featureId of this._pathways.nerveFeatureIds(feature.properties.nerveId)) {
1019
- this.__activateFeature(this.mapFeature_(featureId));
1019
+ this.__activateFeature(this.mapFeature(featureId));
1020
1020
  }
1021
1021
  }
1022
1022
  if ('nodeId' in feature.properties) {
1023
1023
  for (const featureId of this._pathways.nodeFeatureIds(feature.properties.nodeId)) {
1024
- this.__activateFeature(this.mapFeature_(featureId));
1024
+ this.__activateFeature(this.mapFeature(featureId));
1025
1025
  }
1026
1026
  }
1027
1027
  }
@@ -1046,7 +1046,7 @@ export class UserInteractions
1046
1046
  //=====================================
1047
1047
  {
1048
1048
  for (const featureId of featureIds) {
1049
- const feature = this.mapFeature_(featureId);
1049
+ const feature = this.mapFeature(featureId);
1050
1050
  if (feature !== undefined) {
1051
1051
  if (enable) {
1052
1052
  this._map.removeFeatureState(feature, 'hidden');
@@ -1257,7 +1257,7 @@ export class UserInteractions
1257
1257
  const markerId = this.__markerIdByMarker.get(marker);
1258
1258
  const annotation = this.__annotationByMarkerId.get(markerId);
1259
1259
  // The marker's feature
1260
- const feature = this.mapFeature_(annotation.featureId);
1260
+ const feature = this.mapFeature(annotation.featureId);
1261
1261
  if (feature !== undefined) {
1262
1262
  if (event.type === 'mouseenter') {
1263
1263
  // Highlight on mouse enter
package/src/main.js CHANGED
@@ -60,7 +60,8 @@ export async function standaloneViewer(map_endpoint=null, options={})
60
60
  debug: false,
61
61
  minimap: false,
62
62
  showPosition: false,
63
- standalone: true
63
+ standalone: true,
64
+ annotator: true
64
65
  }, options);
65
66
 
66
67
  function loadMap(id, taxon, sex)
package/src/search.js CHANGED
@@ -36,99 +36,6 @@ export const indexedProperties = [
36
36
 
37
37
  //==============================================================================
38
38
 
39
- export class SearchControl
40
- {
41
- constructor(flatmap)
42
- {
43
- this.__flatmap = flatmap;
44
- }
45
-
46
- onAdd(map)
47
- //========
48
- {
49
- this._map = map;
50
- this._container = document.createElement('div');
51
- this._container.className = 'maplibregl-ctrl search-control';
52
-
53
- this._input = document.createElement('input');
54
- this._input.id = 'search-control-input';
55
- this._input.setAttribute('type', 'search');
56
- this._input.setAttribute('visible', 'false');
57
- this._input.setAttribute('placeholder', 'Search...');
58
-
59
- this._button = document.createElement('button');
60
- this._button.id = 'search-control-button';
61
- this._button.className = 'control-button';
62
- this._button.title = 'Search flatmap';
63
- this._button.setAttribute('type', 'button');
64
- this._button.setAttribute('aria-label', 'Search flatmap');
65
- // https://iconmonstr.com/magnifier-6-svg/
66
- this._button.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" id="search-control-icon" viewBox="0 0 24 24">
67
- <path d="M21.172 24l-7.387-7.387c-1.388.874-3.024 1.387-4.785 1.387-4.971 0-9-4.029-9-9s4.029-9 9-9 9 4.029 9 9c0 1.761-.514 3.398-1.387 4.785l7.387 7.387-2.828 2.828zm-12.172-8c3.859 0 7-3.14 7-7s-3.141-7-7-7-7 3.14-7 7 3.141 7 7 7z"/>
68
- </svg>`;
69
- this._container.appendChild(this._button);
70
-
71
- this._container.onclick = this.onClick_.bind(this);
72
- return this._container;
73
- }
74
-
75
- getDefaultPosition()
76
- //==================
77
- {
78
- return 'top-right';
79
- }
80
-
81
- onRemove()
82
- //========
83
- {
84
- this._container.parentNode.removeChild(this._container);
85
- this._map = undefined;
86
- }
87
-
88
- searchMap_(search=true)
89
- //=====================
90
- {
91
- this._input = this._container.removeChild(this._input);
92
- this._input.setAttribute('visible', 'false');
93
- const text = this._input.value;
94
- if (search && text !== '') {
95
- const results = this.__flatmap.search(text);
96
- this.__flatmap.showSearchResults(results);
97
- }
98
- }
99
-
100
- onKeyDown_(e)
101
- //===========
102
- {
103
- if (e.key === 'Enter') {
104
- this.searchMap_();
105
- } else if (e.key === 'Escape') {
106
- this.searchMap_(false);
107
- }
108
- }
109
-
110
- onClick_(e)
111
- //=========
112
- {
113
- const targetId = ('rangeTarget' in e) ? e.rangeTarget.id : e.target.id; // FF has rangeTarget
114
- if (['search-control-button', 'search-control-icon'].includes(targetId)) {
115
- if (this._input.getAttribute('visible') === 'false') {
116
- this._container.appendChild(this._input);
117
- this._container.appendChild(this._button);
118
- this._input.setAttribute('visible', 'true');
119
- this._input.onkeydown = this.onKeyDown_.bind(this);
120
- this._input.value = '';
121
- this.__flatmap.clearSearchResults();
122
- this._input.focus();
123
- } else {
124
- this.searchMap_();
125
- }
126
- }
127
- }
128
- }
129
-
130
- //==============================================================================
131
-
132
39
  export class SearchIndex
133
40
  {
134
41
  constructor()
package/src/styling.js CHANGED
@@ -135,19 +135,21 @@ export class FeatureFillLayer extends VectorStyleLayer
135
135
 
136
136
  paintStyle(options, changes=false)
137
137
  {
138
+ const ghostColour = '#D8D8D8'; // Function of BG colour?
138
139
  const coloured = !('colour' in options) || options.colour;
139
140
  const dimmed = 'dimmed' in options && options.dimmed;
140
141
  const paintStyle = {
141
142
  'fill-color': [
142
143
  'case',
143
144
  ['boolean', ['feature-state', 'selected'], false], COLOUR_SELECTED,
145
+ ['boolean', ['feature-state', 'hidden'], false], ghostColour,
144
146
  ['has', 'colour'], ['get', 'colour'],
145
147
  ['boolean', ['feature-state', 'active'], false], coloured ? '#D88' : '#CCC',
146
148
  'white' // background colour? body colour ??
147
149
  ],
148
150
  'fill-opacity': [
149
151
  'case',
150
- ['boolean', ['feature-state', 'hidden'], false], 0.01,
152
+ ['boolean', ['feature-state', 'hidden'], false], 0.1,
151
153
  ['boolean', ['feature-state', 'selected'], false], 0.7,
152
154
  ['has', 'opacity'], ['get', 'opacity'],
153
155
  ['has', 'colour'], 1.0,
@@ -358,6 +360,38 @@ export class FeatureDashLineLayer extends FeatureLineLayer
358
360
 
359
361
  //==============================================================================
360
362
 
363
+ function sckanFilter(options)
364
+ {
365
+ const sckanState = !'sckan' in options ? 'all'
366
+ : options.sckan.toLowerCase();
367
+ const sckanFilter =
368
+ sckanState == 'none' ? [
369
+ ['!has', 'sckan']
370
+ ] :
371
+ sckanState == 'valid' ? [[
372
+ 'any',
373
+ ['!has', 'sckan'],
374
+ [
375
+ 'all',
376
+ ['has', 'sckan'],
377
+ ['==', 'sckan', true]
378
+ ]
379
+ ]] :
380
+ sckanState == 'invalid' ? [[
381
+ 'any',
382
+ ['!has', 'sckan'],
383
+ [
384
+ 'all',
385
+ ['has', 'sckan'],
386
+ ['!=', 'sckan', true]
387
+ ]
388
+ ]] :
389
+ [ ];
390
+ return sckanFilter;
391
+ }
392
+
393
+ //==============================================================================
394
+
361
395
  export class AnnotatedPathLayer extends VectorStyleLayer
362
396
  {
363
397
  constructor(id, sourceLayer)
@@ -365,6 +399,15 @@ export class AnnotatedPathLayer extends VectorStyleLayer
365
399
  super(id, 'annotated-path', sourceLayer);
366
400
  }
367
401
 
402
+ makeFilter(options={})
403
+ {
404
+ return [
405
+ 'all',
406
+ ['==', '$type', 'LineString'],
407
+ ...sckanFilter(options)
408
+ ];
409
+ }
410
+
368
411
  paintStyle(options={}, changes=false)
369
412
  {
370
413
  const dimmed = 'dimmed' in options && options.dimmed;
@@ -404,7 +447,7 @@ export class AnnotatedPathLayer extends VectorStyleLayer
404
447
  return {
405
448
  ...super.style(),
406
449
  'type': 'line',
407
- 'filter': ['==', '$type', 'LineString'],
450
+ 'filter': this.makeFilter(options),
408
451
  'paint': this.paintStyle(options),
409
452
  'layout': {
410
453
  'line-cap': 'square'
@@ -427,31 +470,7 @@ export class PathLineLayer extends VectorStyleLayer
427
470
 
428
471
  makeFilter(options={})
429
472
  {
430
- const sckanState = !'sckan' in options ? 'all'
431
- : options.sckan.toLowerCase();
432
- const sckan_filter =
433
- sckanState == 'none' ? [
434
- ['!has', 'sckan']
435
- ] :
436
- sckanState == 'valid' ? [[
437
- 'any',
438
- ['!has', 'sckan'],
439
- [
440
- 'all',
441
- ['has', 'sckan'],
442
- ['==', 'sckan', true]
443
- ]
444
- ]] :
445
- sckanState == 'invalid' ? [[
446
- 'any',
447
- ['!has', 'sckan'],
448
- [
449
- 'all',
450
- ['has', 'sckan'],
451
- ['!=', 'sckan', true]
452
- ]
453
- ]] :
454
- [ ];
473
+ const sckan_filter = sckanFilter(options);
455
474
 
456
475
  return this.__dashed ? [
457
476
  'all',
package/src/systems.js CHANGED
@@ -16,61 +16,78 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
16
  See the License for the specific language governing permissions and
17
17
  limitations under the License.
18
18
 
19
- ******************************************************************************/
20
-
21
-
22
- import { Control } from './controls';
23
-
19
+ **/
24
20
  //==============================================================================
25
21
 
26
22
  export class SystemsManager
27
23
  {
28
- constructor()
29
- {
30
-
31
- }
32
- }
33
-
34
- //==============================================================================
35
-
36
- export class SystemsControl extends Control
37
- {
38
- constructor(flatmap, systems)
24
+ constructor(flatmap, ui, enabled=true)
39
25
  {
40
- super(flatmap, 'system', 'systems');
41
- this.__systems = systems;
42
- }
43
-
44
- __innerLinesHTML()
45
- //================
46
- {
47
- const html = [];
48
- for (const [name, system] of this.__systems.entries()) {
49
- html.push(`<label for="${this.__prefix}${system.id}" style="background: ${system.colour};">${name}</label><input id="${this.__prefix}${system.id}" type="checkbox" checked/>`);
26
+ this.__ui = ui;
27
+ this.__systems = new Map();
28
+ this.__enabledChildren = new Map();
29
+ for (const [id, ann] of flatmap.annotations) {
30
+ if (ann['fc-class'] === 'fc-class:System') {
31
+ if (this.__systems.has(ann.name)) {
32
+ this.__systems.get(ann.name).featureIds.push(ann.featureId)
33
+ } else {
34
+ this.__systems.set(ann.name, {
35
+ id: ann.name.replaceAll(' ', '_'),
36
+ colour: ann.colour,
37
+ featureIds: [ ann.featureId ],
38
+ enabled: enabled
39
+ });
40
+ }
41
+ for (const childId of ann['children']) {
42
+ if (enabled) {
43
+ const enabledCount = (this.__enabledChildren.has(childId))
44
+ ? this.__enabledChildren.get(childId)
45
+ : 0;
46
+ this.__enabledChildren.set(childId, enabledCount + 1);
47
+ } else {
48
+ this.__enabledChildren.set(childId, 0);
49
+ }
50
+ }
51
+ }
50
52
  }
51
- return html;
52
53
  }
53
54
 
54
- __enableAll(enable)
55
- //=================
55
+ get systems()
56
+ //===========
56
57
  {
58
+ const systems = [];
57
59
  for (const [name, system] of this.__systems.entries()) {
58
- const checkbox = document.getElementById(`${this.__prefix}${system.id}`);
59
- if (checkbox) {
60
- checkbox.checked = enable;
61
- this.__flatmap.enableSystem(name, enable);
62
- }
60
+ systems.push({
61
+ name: name,
62
+ id: system.id,
63
+ colour: system.colour,
64
+ enabled: system.enabled
65
+ });
63
66
  }
67
+ return systems;
64
68
  }
65
69
 
66
- __enableControl(id, enable)
67
- //=========================
70
+ enable(systemName, enable=true)
71
+ //=============================
68
72
  {
69
- for (const [name, system] of this.__systems.entries()) {
70
- if (id === system.id) {
71
- this.__flatmap.enableSystem(name, enable);
73
+ const system = this.__systems.get(systemName);
74
+ if (system !== undefined && enable !== system.enabled) {
75
+ for (const featureId of system.featureIds) {
76
+ const feature = this.__ui.mapFeature(featureId);
77
+ if (feature !== undefined) {
78
+ this.__ui.enableFeature(feature, enable);
79
+ for (const childFeatureId of feature.children) {
80
+ const enabledCount = this.__enabledChildren.get(childFeatureId);
81
+ if (enable && enabledCount === 0 || !enable && enabledCount == 1) {
82
+ this.__ui.enableFeatureWithChildren(childFeatureId, enable);
83
+ }
84
+ this.__enabledChildren.set(childFeatureId, enabledCount + (enable ? 1 : -1));
85
+ }
86
+ }
72
87
  }
88
+ system.enabled = enable;
73
89
  }
74
90
  }
75
-
76
91
  }
92
+
93
+ //==============================================================================
File without changes