@abi-software/flatmap-viewer 2.2.11-devel.1 → 2.2.11-devel.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abi-software/flatmap-viewer",
3
- "version": "2.2.11-devel.1",
3
+ "version": "2.2.11-devel.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",
@@ -23,7 +23,7 @@
23
23
  "@turf/helpers": "^6.1.4",
24
24
  "@turf/projection": "^6.5.0",
25
25
  "bezier-js": "^6.1.0",
26
- "maplibre-gl": ">=1.15.3",
26
+ "maplibre-gl": ">=2.4.0",
27
27
  "minisearch": "^2.2.1",
28
28
  "polylabel": "^1.1.0"
29
29
  },
package/src/controls.js CHANGED
@@ -22,8 +22,6 @@ limitations under the License.
22
22
 
23
23
  //==============================================================================
24
24
 
25
- // Needed for Webpack
26
- import zoomInButton from '../static/images/zoom-in-button.png'
27
25
 
28
26
  //==============================================================================
29
27
 
@@ -119,15 +117,16 @@ export class PathControl
119
117
 
120
118
  const innerHTML = [];
121
119
  innerHTML.push(`<label for="path-all-paths">ALL PATHS:</label><div class="nerve-line"></div><input id="path-all-paths" type="checkbox" checked/>`);
120
+ this.__checkedCount = 0;
122
121
  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/>`);
122
+ const checked = !('enabled' in path) || path.enabled ? 'checked' : '';
123
+ if (checked != '') {
124
+ this.__checkedCount += 1;
125
+ }
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
127
  }
128
128
  this._legend.innerHTML = innerHTML.join('\n');
129
- this.__checkedCount = this.__pathTypes.length;
130
- this.__halfCount = Math.trunc(this.__checkedCount/2);
129
+ this.__halfCount = Math.trunc(this.__pathTypes.length/2);
131
130
 
132
131
  this._button = document.createElement('button');
133
132
  this._button.id = 'nerve-key-button';
@@ -157,6 +156,9 @@ export class PathControl
157
156
  if (this._button.getAttribute('control-visible') === 'false') {
158
157
  this._container.appendChild(this._legend);
159
158
  this._button.setAttribute('control-visible', 'true');
159
+ const allPathsCheckbox = document.getElementById('path-all-paths');
160
+ allPathsCheckbox.indeterminate = this.__checkedCount < this.__pathTypes.length
161
+ && this.__checkedCount > 0;
160
162
  this._legend.focus();
161
163
  } else {
162
164
  this._legend = this._container.removeChild(this._legend);
@@ -211,7 +213,7 @@ export class LayerControl
211
213
  constructor(flatmap, layerManager)
212
214
  {
213
215
  this.__flatmap = flatmap;
214
- this.__manager = layerManager;
216
+ this.__layers = layerManager.layers;
215
217
  this.__map = undefined;
216
218
  }
217
219
 
@@ -229,18 +231,18 @@ export class LayerControl
229
231
  this.__container.className = 'maplibregl-ctrl';
230
232
  this.__container.id = 'flatmap-layer-control';
231
233
 
232
- this.__layers = document.createElement('div');
233
- this.__layers.id = 'layer-control-text';
234
- this.__layers.className = 'flatmap-layer-grid';
234
+ this.__layersControl = document.createElement('div');
235
+ this.__layersControl.id = 'layer-control-text';
236
+ this.__layersControl.className = 'flatmap-layer-grid';
235
237
 
236
238
  const innerHTML = [];
237
239
  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) {
240
+ for (const layer of this.__layers) {
239
241
  innerHTML.push(`<label for="layer-${layer.id}">${layer.description}</label><input id="layer-${layer.id}" type="checkbox" checked/>`);
240
242
  }
241
- this.__layers.innerHTML = innerHTML.join('\n');
243
+ this.__layersControl.innerHTML = innerHTML.join('\n');
242
244
 
243
- this.__layersCount = this.__manager.layers.length;
245
+ this.__layersCount = this.__layers;
244
246
  this.__checkedCount = this.__layersCount;
245
247
  this.__halfCount = Math.trunc(this.__checkedCount/2);
246
248
 
@@ -270,11 +272,11 @@ export class LayerControl
270
272
  {
271
273
  if (event.target.id === 'map-layers-button') {
272
274
  if (this.__button.getAttribute('control-visible') === 'false') {
273
- this.__container.appendChild(this.__layers);
275
+ this.__container.appendChild(this.__layersControl);
274
276
  this.__button.setAttribute('control-visible', 'true');
275
- this.__layers.focus();
277
+ this.__layersControl.focus();
276
278
  } else {
277
- this.__layers = this.__container.removeChild(this.__layers);
279
+ this.__layersControl = this.__container.removeChild(this.__layersControl);
278
280
  this.__button.setAttribute('control-visible', 'false');
279
281
  }
280
282
  } else if (event.target.tagName === 'INPUT') {
@@ -288,16 +290,16 @@ export class LayerControl
288
290
  } else {
289
291
  this.__checkedCount = 0;
290
292
  }
291
- for (const layer of this.__manager.layers) {
293
+ for (const layer of this.__layers) {
292
294
  const layerCheckbox = document.getElementById(`layer-${layer.id}`);
293
295
  if (layerCheckbox) {
294
296
  layerCheckbox.checked = event.target.checked;
295
- this.__manager.activate(layer.id, event.target.checked);
297
+ this.__flatmap.enableLayer(layer.id, event.target.checked);
296
298
  }
297
299
  }
298
300
  } else if (event.target.id.startsWith('layer-')) {
299
301
  const layerId = event.target.id.substring(6);
300
- this.__manager.activate(layerId, event.target.checked);
302
+ this.__flatmap.enableLayer(layerId, event.target.checked);
301
303
  if (event.target.checked) {
302
304
  this.__checkedCount += 1;
303
305
  } else {
@@ -340,6 +342,7 @@ export class SCKANControl
340
342
  {
341
343
  this.__flatmap = flatmap;
342
344
  this.__map = undefined;
345
+ this.__state = 'valid';
343
346
  }
344
347
 
345
348
  getDefaultPosition()
@@ -361,15 +364,19 @@ export class SCKANControl
361
364
  this.__sckan.className = 'flatmap-layer-grid';
362
365
 
363
366
  const innerHTML = [];
364
- innerHTML.push(`<label for="sckan-all-paths">ALL PATHS:</label><input id="sckan-all-paths" type="checkbox" checked/>`);
367
+ let checked = (this.__state === 'all') ? 'checked' : '';
368
+ innerHTML.push(`<label for="sckan-all-paths">ALL PATHS:</label><input id="sckan-all-paths" type="checkbox" ${checked}/>`);
365
369
  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/>`);
370
+ checked = (this.__state.toUpperCase() === state.id) ? 'checked' : '';
371
+ innerHTML.push(`<label for="sckan-${state.id}">${state.description}</label><input id="sckan-${state.id}" type="checkbox" ${checked}/>`);
367
372
  }
368
373
  this.__sckan.innerHTML = innerHTML.join('\n');
369
374
 
370
375
  this.__sckanCount = SCKAN_STATES.length;
371
- this.__checkedCount = this.__sckanCount;
372
- this.__halfCount = Math.trunc(this.__checkedCount/2);
376
+ this.__checkedCount = (this.__state === 'all') ? this.__sckanCount
377
+ : (this.__state === 'none') ? 0
378
+ : 1;
379
+ this.__halfCount = Math.trunc(this.__sckanCount/2);
373
380
 
374
381
  this.__button = document.createElement('button');
375
382
  this.__button.id = 'map-sckan-button';
@@ -399,6 +406,8 @@ export class SCKANControl
399
406
  if (this.__button.getAttribute('control-visible') === 'false') {
400
407
  this.__container.appendChild(this.__sckan);
401
408
  this.__button.setAttribute('control-visible', 'true');
409
+ const allLayersCheckbox = document.getElementById('sckan-all-paths');
410
+ allLayersCheckbox.indeterminate = this.__state.toLowerCase().includes('valid');
402
411
  this.__sckan.focus();
403
412
  } else {
404
413
  this.__sckan = this.__container.removeChild(this.__sckan);
@@ -411,20 +420,21 @@ export class SCKANControl
411
420
  event.target.indeterminate = false;
412
421
  }
413
422
  if (event.target.checked) {
423
+ this.__state = 'all';
414
424
  this.__checkedCount = this.__sckanCount;
415
425
  } else {
426
+ this.__state = 'none';
416
427
  this.__checkedCount = 0;
417
428
  }
418
429
  for (const state of SCKAN_STATES) {
419
430
  const sckanCheckbox = document.getElementById(`sckan-${state.id}`);
420
431
  if (sckanCheckbox) {
421
432
  sckanCheckbox.checked = event.target.checked;
422
- this.__flatmap.showSckanPaths(state.id, event.target.checked);
423
- }
433
+ }
424
434
  }
435
+ this.__flatmap.showSckanPaths(this.__state);
425
436
  } else if (event.target.id.startsWith('sckan-')) {
426
437
  const sckanId = event.target.id.substring(6);
427
- this.__flatmap.showSckanPaths(sckanId, event.target.checked);
428
438
  if (event.target.checked) {
429
439
  this.__checkedCount += 1;
430
440
  } else {
@@ -432,14 +442,24 @@ export class SCKANControl
432
442
  }
433
443
  const allLayersCheckbox = document.getElementById('sckan-all-paths');
434
444
  if (this.__checkedCount === 0) {
445
+ this.__state = 'none';
435
446
  allLayersCheckbox.checked = false;
436
447
  allLayersCheckbox.indeterminate = false;
437
448
  } else if (this.__checkedCount === this.__sckanCount) {
449
+ this.__state = 'all';
438
450
  allLayersCheckbox.checked = true;
439
451
  allLayersCheckbox.indeterminate = false;
440
452
  } else {
453
+ if (event.target.checked) {
454
+ this.__state = sckanId;
455
+ } else if (sckanId === 'VALID') {
456
+ this.__state = 'invalid';
457
+ } else {
458
+ this.__state = 'valid';
459
+ }
441
460
  allLayersCheckbox.indeterminate = true;
442
461
  }
462
+ this.__flatmap.showSckanPaths(this.__state);
443
463
  }
444
464
  }
445
465
  event.stopPropagation();
@@ -192,8 +192,7 @@ class FlatMap
192
192
  this._initialState = this.getState();
193
193
 
194
194
  // Add a minimap if option set
195
- // Put this above search, info, etc...
196
- // ==> add all controls here (via interactions.js...)
195
+
197
196
  if (this.options.minimap) {
198
197
  this._minimap = new MinimapControl(this, this.options.minimap);
199
198
  this._map.addControl(this._minimap);
@@ -322,11 +321,11 @@ class FlatMap
322
321
  * @param {boolean} [enable=true] If ``true`` then only show the paths
323
322
  * of the type(s) otherwise only hide the paths
324
323
  */
325
- showSckanPaths(validity, enable=true)
326
- //===================================
324
+ showSckanPaths(state='valid')
325
+ //===========================
327
326
  {
328
327
  if (this._userInteractions !== null) {
329
- this._userInteractions.showSckanPaths(validity, enable);
328
+ this._userInteractions.showSckanPaths(state);
330
329
  }
331
330
  }
332
331
 
@@ -444,12 +443,6 @@ class FlatMap
444
443
  return `${this.__uuid}-${this._mapNumber}`;
445
444
  }
446
445
 
447
- get activeLayerNames()
448
- //====================
449
- {
450
- return this._userInteractions.activeLayerNames;
451
- }
452
-
453
446
  get annotations()
454
447
  //===============
455
448
  {
@@ -755,6 +748,34 @@ class FlatMap
755
748
 
756
749
  //==========================================================================
757
750
 
751
+ /**
752
+ * Get a list of the flatmap's layers.
753
+ *
754
+ * @return {Array.Object.<{id: string, description: string, enabled: boolean}>} An array with layer details
755
+ */
756
+ getLayers()
757
+ //=========
758
+ {
759
+ if (this._userInteractions !== null) {
760
+ return this._userInteractions.getLayers();
761
+ }
762
+ }
763
+
764
+ /**
765
+ * @param {string} layerId The layer identifier to enable
766
+ * @param {boolean} enable Show or hide the layer. Defaults to ``true`` (show)
767
+ *
768
+ */
769
+ enableLayer(layerId, enable=true)
770
+ //===============================
771
+ {
772
+ if (this._userInteractions !== null) {
773
+ return this._userInteractions.enableLayer(layerId, enable);
774
+ }
775
+ }
776
+
777
+ //==========================================================================
778
+
758
779
  /**
759
780
  * Add a marker to the map.
760
781
  *
@@ -850,7 +871,6 @@ class FlatMap
850
871
  'dataset',
851
872
  'kind',
852
873
  'label',
853
- 'markup',
854
874
  'models',
855
875
  'nodeId',
856
876
  'source',
@@ -1142,7 +1162,6 @@ export class MapManager
1142
1162
  let latestMap = null;
1143
1163
  let lastCreatedTime = '';
1144
1164
  for (const map of this._mapList) {
1145
- // We can break/return if we have a UUID match...
1146
1165
  if (('uuid' in map && mapDescribes === map.uuid
1147
1166
  || mapDescribes === map.id
1148
1167
  || 'taxon' in map && mapDescribes === map.taxon
@@ -1194,13 +1213,12 @@ export class MapManager
1194
1213
  * @arg options {Object} Configurable options for the map.
1195
1214
  * @arg options.background {string} Background colour of flatmap. Defaults to ``white``.
1196
1215
  * @arg options.debug {boolean} Enable debugging mode.
1197
- * @arg options.featureInfo {boolean} Show information about features as a tooltip. The tooltip is active
1198
- * on selected features and, for non-selected features, when the
1199
- * ``info`` control is enabled. More details are shown in debug mode.
1200
1216
  * @arg options.fullscreenControl {boolean} Add a ``Show full screen`` button to the map.
1201
1217
  * @arg options.layerOptions {Object} Options to control colour and outlines of features
1202
1218
  * @arg options.layerOptions.colour {boolean} Use colour fill (if available) for features. Defaults to ``true``.
1203
1219
  * @arg options.layerOptions.outline {boolean} Show the border of features. Defaults to ``true``.
1220
+ * @arg options.layerOptions.sckan {string} Show neuron paths known to SCKAN: values are ``valid`` (default),
1221
+ * ``invalid``, ``all`` or ``none``.
1204
1222
  * @arg options.minimap {boolean|Object} Display a MiniMap of the flatmap. Defaults to ``false``.
1205
1223
  * @arg options.minimap.position {string} The minimap's position: ``bottom-left`` (default), ``bottom-right``,
1206
1224
  * ``top-left`` or ``top-right``.
@@ -1211,9 +1229,9 @@ export class MapManager
1211
1229
  * @arg options.maxZoom {number} The maximum zoom level of the map.
1212
1230
  * @arg options.minZoom {number} The minimum zoom level of the map.
1213
1231
  * @arg options.navigationControl {boolean} Add navigation controls (zoom buttons) to the map.
1214
- * @arg options.pathControl {boolean} Add buttons to control pathways including via a color-coded legend.
1215
- * @arg options.searchable {boolean} Add a control to search for features on a map.
1216
1232
  * @arg options.showPosition {boolean} Show ``position`` of tooltip.
1233
+ * @arg options.standalone {boolean} Viewer is running ``standalone``, as opposed to integrated into
1234
+ * another application so show a number of controls. Defaults to ``false``.
1217
1235
  * @example
1218
1236
  * const humanMap1 = mapManager.loadMap('humanV1', 'div-1');
1219
1237
  *
@@ -1251,6 +1269,14 @@ export class MapManager
1251
1269
  mapOptions['bounds'] = mapIndex['bounds'];
1252
1270
  }
1253
1271
 
1272
+ // Note the kind of map
1273
+
1274
+ if ('style' in mapIndex) {
1275
+ mapOptions.style = mapIndex.style; // Currently ``flatmap`` or ``fcdiagram``
1276
+ } else {
1277
+ mapOptions.style = 'flatmap';
1278
+ }
1279
+
1254
1280
  // Mapmaker has changed the name of the field to indicate that indicates if
1255
1281
  // there are raster layers
1256
1282
  if (!('image-layers' in mapIndex) && ('image_layer' in mapIndex)) {
@@ -1326,11 +1352,6 @@ export class MapManager
1326
1352
  };
1327
1353
  }
1328
1354
  mapOptions.layerOptions.authoring = ('authoring' in mapIndex) ? mapIndex.authoring : false;
1329
- if ('style' in mapIndex) {
1330
- mapOptions.layerOptions.style = mapIndex.style;
1331
- } else {
1332
- mapOptions.layerOptions.style = 'flatmap';
1333
- }
1334
1355
 
1335
1356
  // Are features in separate vector tile source layers?
1336
1357
 
package/src/info.js CHANGED
@@ -29,10 +29,6 @@ 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',
36
32
  ...indexedProperties
37
33
  ];
38
34
 
@@ -130,49 +130,47 @@ export class UserInteractions
130
130
 
131
131
  flatmap.setInitialPosition();
132
132
 
133
- // Add a control to search annotations if option set
134
-
135
- if (flatmap.options.searchable) {
136
- this._map.addControl(new SearchControl(flatmap));
137
- }
138
-
139
- // Show information about features
140
-
141
- if (flatmap.options.featureInfo || flatmap.options.searchable) {
142
- this._infoControl = new InfoControl(flatmap);
143
- if (flatmap.options.featureInfo) {
144
- this._map.addControl(this._infoControl);
145
- }
146
- }
147
-
148
133
  // Add and manage our layers
149
134
 
150
135
  this._layerManager = new LayerManager(flatmap);
151
136
 
152
- // Control background colour (NB. this depends on having map layers created)
137
+ // Path visibility is either controlled externally or by a local control
153
138
 
154
- if (flatmap.options.backgroundControl) {
155
- this._map.addControl(new BackgroundControl(flatmap));
139
+ this._pathways = new Pathways(flatmap);
140
+
141
+ // The path types in this map
142
+ const mapPathTypes = this._pathways.pathTypes;
143
+
144
+ // Disable paths that are not initially shown
145
+ for (const path of mapPathTypes) {
146
+ if ('enabled' in path && !path.enabled) {
147
+ this.enablePath(path.type, false);
148
+ }
156
149
  }
157
150
 
158
- // Neural pathways which are either controlled externally
159
- // or by our local controls
151
+ // Add various controls when running standalone
160
152
 
161
- this._pathways = new Pathways(flatmap);
153
+ if (flatmap.options.standalone) {
154
+ // Add a control to search annotations if option set
155
+ this._map.addControl(new SearchControl(flatmap));
162
156
 
163
- // Add a control to manage our pathways
157
+ // Show information about features
158
+ this._infoControl = new InfoControl(flatmap);
159
+ this._map.addControl(this._infoControl);
164
160
 
165
- if (flatmap.options.pathControls) {
166
- // Restrict to path types that are on the map...
167
- this._map.addControl(new PathControl(flatmap, this._pathways.pathTypes));
168
- }
161
+ // Control background colour (NB. this depends on having map layers created)
162
+ this._map.addControl(new BackgroundControl(flatmap));
169
163
 
170
- // Add a control to manage our layers
164
+ // Add a control to manage our paths
165
+ this._map.addControl(new PathControl(flatmap, mapPathTypes));
171
166
 
172
- if (flatmap.options.layerControl) {
167
+ // Add a control to manage our layers
173
168
  this._map.addControl(new LayerControl(flatmap, this._layerManager));
174
- // ************************************
175
- //this._map.addControl(new SCKANControl(flatmap));
169
+
170
+ // A SCKAN path control for FC maps
171
+ if (flatmap.options.style === 'fcdiagram') {
172
+ this._map.addControl(new SCKANControl(flatmap));
173
+ }
176
174
  }
177
175
 
178
176
  // Flag features that have annotations
@@ -180,9 +178,6 @@ export class UserInteractions
180
178
 
181
179
  for (const [id, ann] of flatmap.annotations) {
182
180
  const feature = this.mapFeature_(id);
183
- if (id == 118) {
184
- console.log(feature, ann);
185
- }
186
181
  if (feature !== undefined) {
187
182
  this._map.setFeatureState(feature, { 'annotated': true });
188
183
  }
@@ -226,7 +221,7 @@ export class UserInteractions
226
221
  return {
227
222
  center: this._map.getCenter().toArray(),
228
223
  zoom: this._map.getZoom(),
229
- layers: this.activeLayerNames
224
+ layers: this.layers
230
225
  };
231
226
  }
232
227
 
@@ -258,10 +253,16 @@ export class UserInteractions
258
253
  this._layerManager.setColour(options);
259
254
  }
260
255
 
261
- get activeLayerNames()
262
- //====================
256
+ getLayers()
257
+ //=========
263
258
  {
264
- return this._layerManager.activeLayerNames;
259
+ return this._layerManager.layers;
260
+ }
261
+
262
+ enableLayer(layerId, enable=true)
263
+ //===============================
264
+ {
265
+ this._layerManager.activate(layerId, enable);
265
266
  }
266
267
 
267
268
  mapFeature_(featureId)
@@ -561,7 +562,6 @@ export class UserInteractions
561
562
  }
562
563
  bbox = expandBounds(bbox, annotation.bounds);
563
564
  if ('type' in annotation && annotation.type.startsWith('line')) {
564
- // FC may have lines that are not pathways features, esp. when authoring...
565
565
  for (const pathFeatureId of this._pathways.lineFeatureIds([featureId])) {
566
566
  if (select) {
567
567
  this.selectFeature_(pathFeatureId);
@@ -783,10 +783,6 @@ export class UserInteractions
783
783
  }
784
784
  }
785
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)??
790
786
  let labelledFeatures = features.filter(feature => (('hyperlink' in feature.properties
791
787
  || 'label' in feature.properties
792
788
  || 'node' in feature.properties)
@@ -1032,9 +1028,10 @@ export class UserInteractions
1032
1028
  return this._pathways.nodePathModels(nodeId);
1033
1029
  }
1034
1030
 
1035
- showSckanPaths(validity, enable=true)
1036
- //===================================
1031
+ showSckanPaths(state='valid')
1032
+ //===========================
1037
1033
  {
1034
+ this._layerManager.setFilter({sckan: state});
1038
1035
  }
1039
1036
 
1040
1037
  //==============================================================================