@abi-software/flatmap-viewer 2.2.0-beta.3 → 2.2.0-beta.7

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.0-beta.3",
3
+ "version": "2.2.0-beta.7",
4
4
  "description": "Flatmap viewer using Maplibre GL",
5
5
  "repository": "https://github.com/ABI-Software/flatmap-viewer.git",
6
6
  "main": "src/main.js",
@@ -22,7 +22,7 @@ limitations under the License.
22
22
 
23
23
  //==============================================================================
24
24
 
25
- import maplibre from 'maplibre-gl';
25
+ import maplibregl from 'maplibre-gl';
26
26
  import 'maplibre-gl/dist/maplibre-gl.css';
27
27
 
28
28
  //==============================================================================
@@ -131,7 +131,7 @@ class FlatMap
131
131
 
132
132
  // Create the map
133
133
 
134
- this._map = new maplibre.Map(mapOptions);
134
+ this._map = new maplibregl.Map(mapOptions);
135
135
 
136
136
  // Show tile boundaries if debugging
137
137
 
@@ -146,7 +146,7 @@ class FlatMap
146
146
  // Do we want a fullscreen control?
147
147
 
148
148
  if (mapDescription.options.fullscreenControl === true) {
149
- this._map.addControl(new maplibre.FullscreenControl(), 'top-right');
149
+ this._map.addControl(new maplibregl.FullscreenControl(), 'top-right');
150
150
  }
151
151
 
152
152
  // Disable map rotation
@@ -176,7 +176,11 @@ class FlatMap
176
176
  this.setupUserInteractions_();
177
177
  } else if (this._initialState === null) {
178
178
  this._bounds = this._map.getBounds();
179
-
179
+ this._map.setMaxBounds(this._bounds);
180
+ const sw = maplibregl.MercatorCoordinate.fromLngLat(this._bounds.toArray()[0]);
181
+ const ne = maplibregl.MercatorCoordinate.fromLngLat(this._bounds.toArray()[1]);
182
+ this.__normalised_origin = [sw.x, ne.y];
183
+ this.__normalised_size = [ne.x - sw.x, sw.y - ne.y];
180
184
  if ('state' in this._options) {
181
185
  this._userInteractions.setState(this._options.state);
182
186
  }
@@ -559,7 +563,7 @@ class FlatMap
559
563
  //==================
560
564
  {
561
565
  if ('bounds' in this._options) {
562
- this._map.fitBounds(this._options['bounds']);
566
+ this._map.fitBounds(this._options['bounds'], {animate: false});
563
567
  }
564
568
  if ('center' in this._options) {
565
569
  this._map.setCenter(this._options['center']);
@@ -784,6 +788,7 @@ class FlatMap
784
788
  const exportedProperties = [
785
789
  'connectivity',
786
790
  'dataset',
791
+ 'kind',
787
792
  'label',
788
793
  'models',
789
794
  'nodeId',
@@ -822,6 +827,66 @@ class FlatMap
822
827
  });
823
828
  }
824
829
 
830
+ /**
831
+ * Generate callbacks as a result of panning/zooming the map.
832
+ *
833
+ * @param {boolean} enabled Generate callbacks when ``true``,
834
+ * otherwise disable them.
835
+ */
836
+ enablePanZoomEvents(enabled=true)
837
+ //===============================
838
+ {
839
+ if (this._userInteractions !== null) {
840
+ this._userInteractions.enablePanZoomEvents(enabled);
841
+ }
842
+ }
843
+
844
+ /**
845
+ * Generate a callback as a result of panning/zooming the map.
846
+ *
847
+ * @param {string} type The event type, ``pan`` or ``zoom``.
848
+ * @param {Array.<float>} origin The map's normalised top-left corner
849
+ * @param {Array.<float>} size The map's normalised size
850
+ */
851
+ panZoomEvent(type)
852
+ //================
853
+ {
854
+ const bounds = this._map.getBounds();
855
+ if (this.__normalised_origin !== undefined) {
856
+ const sw = maplibregl.MercatorCoordinate.fromLngLat(bounds.toArray()[0]);
857
+ const ne = maplibregl.MercatorCoordinate.fromLngLat(bounds.toArray()[1]);
858
+ const top_left = [(sw.x - this.__normalised_origin[0])/this.__normalised_size[0],
859
+ (ne.y - this.__normalised_origin[1])/this.__normalised_size[1]];
860
+ const size = [(ne.x - sw.x)/this.__normalised_size[0],
861
+ (sw.y - ne.y)/this.__normalised_size[1]];
862
+ this.callback('pan-zoom', {
863
+ type: type,
864
+ origin: top_left,
865
+ size: size
866
+ });
867
+ }
868
+ }
869
+
870
+ /**
871
+ * Pan/zoom the map to a new view
872
+ *
873
+ * @param {Array.<float>} origin The map's normalised top-left corner
874
+ * @param {Array.<float>} size The map's normalised size
875
+ */
876
+ panZoomTo(origin, size)
877
+ //=====================
878
+ {
879
+ if (this.__normalised_origin !== undefined) {
880
+ const sw_x = origin[0]*this.__normalised_size[0] + this.__normalised_origin[0];
881
+ const ne_y = origin[1]*this.__normalised_size[1] + this.__normalised_origin[1];
882
+ const ne_x = sw_x + size[0]*this.__normalised_size[0];
883
+ const sw_y = ne_y + size[1]*this.__normalised_size[1];
884
+ const sw = (new maplibregl.MercatorCoordinate(sw_x, sw_y, 0)).toLngLat();
885
+ const ne = (new maplibregl.MercatorCoordinate(ne_x, ne_y, 0)).toLngLat();
886
+ this._map.fitBounds([sw, ne], {animate: false});
887
+ }
888
+ }
889
+
825
890
  //==========================================================================
826
891
 
827
892
  search(text)
@@ -224,6 +224,11 @@ export class UserInteractions
224
224
  this._map.on('mousemove', this.mouseMoveEvent_.bind(this));
225
225
  this._lastFeatureMouseEntered = null;
226
226
  this._lastFeatureModelsMouse = null;
227
+
228
+ // Handle pan/zoom events
229
+ this._map.on('move', this.panZoomEvent_.bind(this, 'pan'));
230
+ this._map.on('zoom', this.panZoomEvent_.bind(this, 'zoom'));
231
+ this.__pan_zoom_enabled = false;
227
232
  }
228
233
 
229
234
  getState()
@@ -289,12 +294,12 @@ export class UserInteractions
289
294
  return this._selectedFeatureIds.has(+featureId);
290
295
  }
291
296
 
292
- selectFeature_(featureId)
293
- //=======================
297
+ selectFeature_(featureId, dim=true)
298
+ //=================================
294
299
  {
295
300
  featureId = +featureId; // Ensure numeric
296
301
  if (this._selectedFeatureIds.size === 0) {
297
- this._layerManager.setColour({...this.__colourOptions, dimmed: true});
302
+ this._layerManager.setColour({...this.__colourOptions, dimmed: dim});
298
303
  }
299
304
  if (this._selectedFeatureIds.has(featureId)) {
300
305
  this._selectedFeatureIds.set(featureId, this._selectedFeatureIds.get(featureId) + 1);
@@ -837,7 +842,10 @@ export class UserInteractions
837
842
  }
838
843
  }
839
844
  } else if (selecting) {
840
- this.selectFeature_(featureId);
845
+ const dim = !('properties' in feature
846
+ && 'kind' in feature.properties
847
+ && ['cell-type', 'scaffold', 'tissue'].indexOf(feature.properties.kind) >= 0);
848
+ this.selectFeature_(featureId, dim);
841
849
  } else {
842
850
  this.unselectFeature_(featureId);
843
851
  }
@@ -1103,6 +1111,20 @@ export class UserInteractions
1103
1111
 
1104
1112
  return true;
1105
1113
  }
1114
+
1115
+ enablePanZoomEvents(enabled=true)
1116
+ //===============================
1117
+ {
1118
+ this.__pan_zoom_enabled = enabled;
1119
+ }
1120
+
1121
+ panZoomEvent_(type)
1122
+ //=================
1123
+ {
1124
+ if (this.__pan_zoom_enabled) {
1125
+ this._flatmap.panZoomEvent(type);
1126
+ }
1127
+ }
1106
1128
  }
1107
1129
 
1108
1130
  //==============================================================================
package/src/styling.js CHANGED
@@ -117,12 +117,22 @@ export class FeatureFillLayer extends VectorStyleLayer
117
117
  const paintStyle = {
118
118
  'fill-color': [
119
119
  'case',
120
+ ['any',
121
+ ['==', ['get', 'kind'], 'scaffold'],
122
+ ['==', ['get', 'kind'], 'tissue'],
123
+ ['==', ['get', 'kind'], 'cell-type']
124
+ ], "white",
120
125
  ['boolean', ['feature-state', 'selected'], false], '#0F0',
121
126
  ['boolean', ['feature-state', 'active'], false], coloured ? '#D88' : '#CCC',
122
127
  'white' // background colour? body colour ??
123
128
  ],
124
129
  'fill-opacity': [
125
130
  'case',
131
+ ['any',
132
+ ['==', ['get', 'kind'], 'scaffold'],
133
+ ['==', ['get', 'kind'], 'tissue'],
134
+ ['==', ['get', 'kind'], 'cell-type']
135
+ ], 0.1,
126
136
  ['boolean', ['feature-state', 'selected'], false], 1.0,
127
137
  ['boolean', ['feature-state', 'active'], false], 0.8,
128
138
  (coloured && !dimmed) ? 0.01 : 0.5
@@ -164,12 +174,12 @@ export class FeatureBorderLayer extends VectorStyleLayer
164
174
  const outlined = !('outline' in options) || options.outline;
165
175
  const dimmed = 'dimmed' in options && options.dimmed;
166
176
  const lineColour = [ 'case' ];
177
+ lineColour.push(['boolean', ['feature-state', 'selected'], false]);
178
+ lineColour.push('red');
167
179
  if (coloured && outlined) {
168
180
  lineColour.push(['boolean', ['feature-state', 'active'], false]);
169
181
  lineColour.push('blue');
170
182
  }
171
- lineColour.push(['boolean', ['feature-state', 'selected'], false]);
172
- lineColour.push('red');
173
183
  lineColour.push('#444');
174
184
 
175
185
  const lineOpacity = [
@@ -254,7 +264,17 @@ export class PathLineLayer extends VectorStyleLayer
254
264
  {
255
265
  const filterType = dashed ? 'line-dash' : 'line';
256
266
  super(mapLayerId, sourceLayer, filterType);
257
- this.__filterType = filterType;
267
+ this.__filter = dashed ?
268
+ [
269
+ 'any',
270
+ ['==', 'type', `line-dash`]
271
+ ]
272
+ :
273
+ [
274
+ 'any',
275
+ ['==', 'type', 'bezier'],
276
+ ['==', 'type', `line`]
277
+ ];
258
278
  this.__dashed = dashed;
259
279
  }
260
280
 
@@ -278,7 +298,7 @@ export class PathLineLayer extends VectorStyleLayer
278
298
  ],
279
299
  'line-opacity': [
280
300
  'case',
281
- ['==', ['get', 'type'], 'bezier'], 0.3,
301
+ ['==', ['get', 'type'], 'bezier'], 1.0,
282
302
  ['boolean', ['get', 'invisible'], false], 0.001,
283
303
  ['boolean', ['feature-state', 'selected'], false], 1.0,
284
304
  ['boolean', ['feature-state', 'active'], false], 0.8,
@@ -289,7 +309,7 @@ export class PathLineLayer extends VectorStyleLayer
289
309
  'let',
290
310
  'width', [
291
311
  'case',
292
- ['==', ['get', 'type'], 'bezier'], 0.2,
312
+ ['==', ['get', 'type'], 'bezier'], 0.1,
293
313
  ['boolean', ['get', 'centreline'], false], 0.5,
294
314
  ['boolean', ['get', 'invisible'], false], 0.1,
295
315
  ['boolean', ['feature-state', 'selected'], false], 1.2,
@@ -319,10 +339,7 @@ export class PathLineLayer extends VectorStyleLayer
319
339
  'filter': [
320
340
  'all',
321
341
  ['==', '$type', 'LineString'],
322
- ['any',
323
- ['==', 'type', 'bezier'],
324
- ['==', 'type', `${this.__filterType}`]
325
- ]
342
+ this.__filter
326
343
  ],
327
344
  'paint': this.paintStyle(options)
328
345
  };
@@ -455,6 +472,7 @@ export class NervePolygonFill extends VectorStyleLayer
455
472
  ['==', '$type', 'Polygon'],
456
473
  ['any',
457
474
  ['==', 'type', 'bezier'],
475
+ ['==', 'type', 'junction'],
458
476
  ['==', 'type', 'nerve'],
459
477
  ['==', 'type', 'nerve-section']
460
478
  ]
@@ -462,13 +480,22 @@ export class NervePolygonFill extends VectorStyleLayer
462
480
  'paint': {
463
481
  'fill-color': [
464
482
  'case',
465
- ['==', ['get', 'kind'], 'bezier-control'], 'red',
466
- ['==', ['get', 'kind'], 'bezier-end'], 'green',
483
+ ['==', ['get', 'kind'], 'bezier-end'], 'red',
484
+ ['==', ['get', 'kind'], 'bezier-control'], 'green',
485
+ ['==', ['get', 'kind'], 'cns'], '#9B1FC1',
486
+ ['==', ['get', 'kind'], 'lcn'], '#F19E38',
487
+ ['==', ['get', 'kind'], 'para-post'], '#3F8F4A',
488
+ ['==', ['get', 'kind'], 'para-pre'], '#3F8F4A',
489
+ ['==', ['get', 'kind'], 'somatic'], '#98561D',
490
+ ['==', ['get', 'kind'], 'sensory'], '#2A62F6',
491
+ ['==', ['get', 'kind'], 'symp-post'], '#EA3423',
492
+ ['==', ['get', 'kind'], 'symp-pre'], '#EA3423',
467
493
  'white'
468
494
  ],
469
495
  'fill-opacity': [
470
496
  'case',
471
- ['==', ['get', 'type'], 'bezier'], 0.2,
497
+ ['==', ['get', 'type'], 'bezier'], 0.9,
498
+ ['==', ['get', 'type'], 'junction'], 0.4,
472
499
  0.01
473
500
  ]
474
501
  }