@abi-software/flatmap-viewer 2.5.0-a.2 → 2.5.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/README.rst +1 -1
- package/package.json +3 -3
- package/src/controls/annotation.js +351 -0
- package/src/controls/controls.js +68 -0
- package/src/controls/minimap.js +3 -8
- package/src/controls/paths3d.js +14 -0
- package/src/flatmap-viewer.js +55 -35
- package/src/interactions.js +167 -49
- package/src/layers/paths3d.js +253 -88
- package/src/main.js +1 -0
- package/src/pathways.js +29 -9
package/src/interactions.js
CHANGED
|
@@ -40,11 +40,13 @@ import {Paths3DLayer} from './layers/paths3d'
|
|
|
40
40
|
import {SystemsManager} from './systems';
|
|
41
41
|
|
|
42
42
|
import {displayedProperties, InfoControl} from './controls/info';
|
|
43
|
-
import {BackgroundControl, LayerControl, NerveControl,
|
|
43
|
+
import {AnnotatorControl, BackgroundControl, LayerControl, NerveControl,
|
|
44
44
|
SCKANControl} from './controls/controls';
|
|
45
|
+
import {AnnotationDrawControl, DRAW_ANNOTATION_LAYERS} from './controls/annotation'
|
|
45
46
|
import {PathControl} from './controls/paths';
|
|
46
47
|
import {Path3DControl} from './controls/paths3d'
|
|
47
48
|
import {SearchControl} from './controls/search';
|
|
49
|
+
import {MinimapControl} from './controls/minimap';
|
|
48
50
|
import {SystemsControl} from './controls/systems';
|
|
49
51
|
import {TaxonsControl} from './controls/taxons';
|
|
50
52
|
import {latex2Svg} from './mathjax';
|
|
@@ -110,10 +112,13 @@ function labelPosition(feature)
|
|
|
110
112
|
function getRenderedLabel(properties)
|
|
111
113
|
{
|
|
112
114
|
if (!('renderedLabel' in properties)) {
|
|
113
|
-
const label = ('label' in properties) ?
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
115
|
+
const label = ('label' in properties) ? properties.label
|
|
116
|
+
: ('user_label' in properties) ? properties.user_label
|
|
117
|
+
: ''
|
|
118
|
+
const uppercaseLabel = (label !== '') ? (label.substr(0, 1).toUpperCase()
|
|
119
|
+
+ label.substr(1)).replaceAll("\n", "<br/>")
|
|
120
|
+
: ''
|
|
121
|
+
properties.renderedLabel = uppercaseLabel.replaceAll(/`\$([^\$]*)\$`/g, math => latex2Svg(math.slice(2, -2)));
|
|
117
122
|
}
|
|
118
123
|
return properties.renderedLabel;
|
|
119
124
|
}
|
|
@@ -122,14 +127,16 @@ function getRenderedLabel(properties)
|
|
|
122
127
|
|
|
123
128
|
export class UserInteractions
|
|
124
129
|
{
|
|
125
|
-
#
|
|
130
|
+
#annotationDrawControl = null
|
|
131
|
+
#minimap = null
|
|
132
|
+
#paths3dLayer = null
|
|
126
133
|
|
|
127
134
|
constructor(flatmap)
|
|
128
135
|
{
|
|
129
136
|
this._flatmap = flatmap;
|
|
130
137
|
this._map = flatmap.map;
|
|
131
138
|
|
|
132
|
-
this._activeFeatures =
|
|
139
|
+
this._activeFeatures = new Set()
|
|
133
140
|
this._selectedFeatureIds = new Map();
|
|
134
141
|
this._currentPopup = null;
|
|
135
142
|
this._infoControl = null;
|
|
@@ -139,7 +146,6 @@ export class UserInteractions
|
|
|
139
146
|
this._modal = false;
|
|
140
147
|
|
|
141
148
|
// Default colour settings
|
|
142
|
-
|
|
143
149
|
this.__colourOptions = {colour: true, outline: true};
|
|
144
150
|
|
|
145
151
|
// Marker placement and interaction
|
|
@@ -187,6 +193,26 @@ export class UserInteractions
|
|
|
187
193
|
// All taxons of connectivity paths are enabled by default
|
|
188
194
|
this.__enabledConnectivityTaxons = new Set(this._flatmap.taxonIdentifiers);
|
|
189
195
|
|
|
196
|
+
// Add a minimap if option set
|
|
197
|
+
if (flatmap.options.minimap) {
|
|
198
|
+
this.#minimap = new MinimapControl(flatmap, flatmap.options.minimap);
|
|
199
|
+
this._map.addControl(this.#minimap);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Do we want a fullscreen control?
|
|
203
|
+
if (flatmap.options.fullscreenControl === true) {
|
|
204
|
+
this._map.addControl(new maplibregl.FullscreenControl(), 'top-right');
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Add navigation controls if option set
|
|
208
|
+
if (flatmap.options.navigationControl) {
|
|
209
|
+
const value = flatmap.options.navigationControl;
|
|
210
|
+
const position = ((typeof value === 'string')
|
|
211
|
+
&& ['top-left', 'top-right', 'bottom-right', 'bottom-left'].includes(value))
|
|
212
|
+
? value : 'bottom-right';
|
|
213
|
+
this._map.addControl(new NavigationControl(flatmap), position);
|
|
214
|
+
}
|
|
215
|
+
|
|
190
216
|
// Support 3D path view
|
|
191
217
|
this.#paths3dLayer = new Paths3DLayer(flatmap, this)
|
|
192
218
|
|
|
@@ -222,9 +248,17 @@ export class UserInteractions
|
|
|
222
248
|
this._map.addControl(new TaxonsControl(flatmap));
|
|
223
249
|
}
|
|
224
250
|
|
|
251
|
+
if (flatmap.options.annotator) {
|
|
252
|
+
this._map.addControl(new AnnotatorControl(flatmap));
|
|
253
|
+
}
|
|
254
|
+
|
|
225
255
|
this._map.addControl(new Path3DControl(this));
|
|
226
256
|
}
|
|
227
257
|
|
|
258
|
+
// Add an initially hidden tool for drawing on the map.
|
|
259
|
+
this.#annotationDrawControl = new AnnotationDrawControl(flatmap, false)
|
|
260
|
+
this._map.addControl(this.#annotationDrawControl)
|
|
261
|
+
|
|
228
262
|
// Handle mouse events
|
|
229
263
|
|
|
230
264
|
this._map.on('click', this.clickEvent_.bind(this));
|
|
@@ -239,6 +273,12 @@ export class UserInteractions
|
|
|
239
273
|
this.__pan_zoom_enabled = false;
|
|
240
274
|
}
|
|
241
275
|
|
|
276
|
+
get minimap()
|
|
277
|
+
//===========
|
|
278
|
+
{
|
|
279
|
+
return this.#minimap
|
|
280
|
+
}
|
|
281
|
+
|
|
242
282
|
get pathManager()
|
|
243
283
|
//===============
|
|
244
284
|
{
|
|
@@ -278,11 +318,40 @@ export class UserInteractions
|
|
|
278
318
|
}
|
|
279
319
|
}
|
|
280
320
|
|
|
321
|
+
showAnnotator(visible=true)
|
|
322
|
+
//=========================
|
|
323
|
+
{
|
|
324
|
+
if (this.#annotationDrawControl) {
|
|
325
|
+
this.#annotationDrawControl.show(visible)
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
modifyDrawnAnnotatorFeature(operation, feature)
|
|
330
|
+
//=============================================
|
|
331
|
+
{
|
|
332
|
+
if (this.#annotationDrawControl) {
|
|
333
|
+
if (operation === 'add') {
|
|
334
|
+
this.#annotationDrawControl.addFeature(feature)
|
|
335
|
+
} else if (operation === 'remove') {
|
|
336
|
+
this.#annotationDrawControl.removeFeature(feature)
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
#setPaint(options)
|
|
342
|
+
//================
|
|
343
|
+
{
|
|
344
|
+
this._layerManager.setPaint(options)
|
|
345
|
+
if (this.#paths3dLayer) {
|
|
346
|
+
this.#paths3dLayer.setPaint(options)
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
281
350
|
setPaint(options)
|
|
282
351
|
//===============
|
|
283
352
|
{
|
|
284
353
|
this.__colourOptions = options;
|
|
285
|
-
this
|
|
354
|
+
this.#setPaint(options);
|
|
286
355
|
}
|
|
287
356
|
|
|
288
357
|
getLayers()
|
|
@@ -300,7 +369,9 @@ export class UserInteractions
|
|
|
300
369
|
enable3dPaths(enable=true)
|
|
301
370
|
//========================
|
|
302
371
|
{
|
|
303
|
-
this.#paths3dLayer
|
|
372
|
+
if (this.#paths3dLayer) {
|
|
373
|
+
this.#paths3dLayer.enable(enable)
|
|
374
|
+
}
|
|
304
375
|
}
|
|
305
376
|
|
|
306
377
|
getSystems()
|
|
@@ -332,19 +403,43 @@ export class UserInteractions
|
|
|
332
403
|
return undefined;
|
|
333
404
|
}
|
|
334
405
|
|
|
406
|
+
#getFeatureState(feature)
|
|
407
|
+
//=======================
|
|
408
|
+
{
|
|
409
|
+
return this._map.getFeatureState(feature)
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
#removeFeatureState(feature, key)
|
|
413
|
+
//===============================
|
|
414
|
+
{
|
|
415
|
+
this._map.removeFeatureState(feature, key)
|
|
416
|
+
if (this.#paths3dLayer) {
|
|
417
|
+
this.#paths3dLayer.removeFeatureState(feature.id, key)
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
#setFeatureState(feature, state)
|
|
422
|
+
//==============================
|
|
423
|
+
{
|
|
424
|
+
this._map.setFeatureState(feature, state)
|
|
425
|
+
if (this.#paths3dLayer) {
|
|
426
|
+
this.#paths3dLayer.setFeatureState(feature.id, state)
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
335
430
|
enableMapFeature(feature, enable=true)
|
|
336
431
|
//====================================
|
|
337
432
|
{
|
|
338
433
|
if (feature !== undefined) {
|
|
339
|
-
const state = this
|
|
434
|
+
const state = this.#getFeatureState(feature);
|
|
340
435
|
if ('hidden' in state) {
|
|
341
436
|
if (enable) {
|
|
342
|
-
this
|
|
437
|
+
this.#removeFeatureState(feature, 'hidden');
|
|
343
438
|
} else if (!state.hidden) {
|
|
344
|
-
this
|
|
439
|
+
this.#setFeatureState(feature, { hidden: true });
|
|
345
440
|
}
|
|
346
441
|
} else if (!enable) {
|
|
347
|
-
this
|
|
442
|
+
this.#setFeatureState(feature, { hidden: true });
|
|
348
443
|
}
|
|
349
444
|
this.__enableFeatureMarker(feature.id, enable);
|
|
350
445
|
}
|
|
@@ -392,11 +487,11 @@ export class UserInteractions
|
|
|
392
487
|
//=======================
|
|
393
488
|
{
|
|
394
489
|
if (feature.id) {
|
|
395
|
-
const state = this
|
|
490
|
+
const state = this.#getFeatureState(feature);
|
|
396
491
|
return (state !== undefined
|
|
397
492
|
&& (!('hidden' in state) || !state.hidden));
|
|
398
493
|
}
|
|
399
|
-
return
|
|
494
|
+
return DRAW_ANNOTATION_LAYERS.includes(feature.layer.id)
|
|
400
495
|
}
|
|
401
496
|
|
|
402
497
|
featureSelected_(featureId)
|
|
@@ -409,7 +504,7 @@ export class UserInteractions
|
|
|
409
504
|
//================================
|
|
410
505
|
{
|
|
411
506
|
const ann = this._flatmap.annotation(featureId);
|
|
412
|
-
if ('sckan' in ann) {
|
|
507
|
+
if (ann && 'sckan' in ann) {
|
|
413
508
|
const sckanState = this._layerManager.sckanState;
|
|
414
509
|
if (sckanState === 'none'
|
|
415
510
|
|| sckanState === 'valid' && !ann.sckan
|
|
@@ -426,16 +521,16 @@ export class UserInteractions
|
|
|
426
521
|
} else {
|
|
427
522
|
const feature = this.mapFeature(featureId);
|
|
428
523
|
if (feature !== undefined) {
|
|
429
|
-
const state = this
|
|
524
|
+
const state = this.#getFeatureState(feature);
|
|
430
525
|
if (state !== undefined && (!('hidden' in state) || !state.hidden)) {
|
|
431
|
-
this
|
|
526
|
+
this.#setFeatureState(feature, { selected: true });
|
|
432
527
|
this._selectedFeatureIds.set(featureId, 1);
|
|
433
528
|
result = true;
|
|
434
529
|
}
|
|
435
530
|
}
|
|
436
531
|
}
|
|
437
532
|
if (result && noSelection) {
|
|
438
|
-
this
|
|
533
|
+
this.#setPaint({...this.__colourOptions, dimmed: dim});
|
|
439
534
|
}
|
|
440
535
|
return result;
|
|
441
536
|
}
|
|
@@ -451,13 +546,13 @@ export class UserInteractions
|
|
|
451
546
|
} else {
|
|
452
547
|
const feature = this.mapFeature(featureId);
|
|
453
548
|
if (feature !== undefined) {
|
|
454
|
-
this
|
|
549
|
+
this.#removeFeatureState(feature, 'selected');
|
|
455
550
|
this._selectedFeatureIds.delete(+featureId);
|
|
456
551
|
}
|
|
457
552
|
}
|
|
458
553
|
}
|
|
459
554
|
if (this._selectedFeatureIds.size === 0) {
|
|
460
|
-
this
|
|
555
|
+
this.#setPaint({...this.__colourOptions, dimmed: false});
|
|
461
556
|
}
|
|
462
557
|
}
|
|
463
558
|
|
|
@@ -467,28 +562,42 @@ export class UserInteractions
|
|
|
467
562
|
for (const featureId of this._selectedFeatureIds.keys()) {
|
|
468
563
|
const feature = this.mapFeature(featureId);
|
|
469
564
|
if (feature !== undefined) {
|
|
470
|
-
this
|
|
565
|
+
this.#removeFeatureState(feature, 'selected');
|
|
471
566
|
}
|
|
472
567
|
}
|
|
473
568
|
this._selectedFeatureIds.clear();
|
|
474
|
-
this
|
|
569
|
+
this.#setPaint({...this.__colourOptions, dimmed: false});
|
|
475
570
|
}
|
|
476
571
|
|
|
477
572
|
activateFeature(feature)
|
|
478
573
|
//======================
|
|
479
574
|
{
|
|
480
575
|
if (feature !== undefined) {
|
|
481
|
-
this
|
|
482
|
-
this._activeFeatures.
|
|
576
|
+
this.#setFeatureState(feature, { active: true });
|
|
577
|
+
this._activeFeatures.add(feature);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
activateLineFeatures(lineFeatures)
|
|
582
|
+
//================================
|
|
583
|
+
{
|
|
584
|
+
for (const lineFeature of lineFeatures) {
|
|
585
|
+
const lineFeatureId = +lineFeature.properties.featureId // Ensure numeric
|
|
586
|
+
this.activateFeature(lineFeature)
|
|
587
|
+
const lineIds = new Set(lineFeatures.map(f => f.properties.featureId))
|
|
588
|
+
for (const featureId of this.__pathManager.lineFeatureIds(lineIds)) {
|
|
589
|
+
this.activateFeature(this.mapFeature(featureId))
|
|
590
|
+
}
|
|
483
591
|
}
|
|
484
592
|
}
|
|
485
593
|
|
|
486
594
|
resetActiveFeatures_()
|
|
487
595
|
//====================
|
|
488
596
|
{
|
|
489
|
-
|
|
490
|
-
this
|
|
597
|
+
for (const feature of this._activeFeatures) {
|
|
598
|
+
this.#removeFeatureState(feature, 'active');
|
|
491
599
|
}
|
|
600
|
+
this._activeFeatures.clear()
|
|
492
601
|
}
|
|
493
602
|
|
|
494
603
|
smallestAnnotatedPolygonFeature_(features)
|
|
@@ -500,7 +609,7 @@ export class UserInteractions
|
|
|
500
609
|
let smallestFeature = null;
|
|
501
610
|
for (const feature of features) {
|
|
502
611
|
if (feature.geometry.type.includes('Polygon')
|
|
503
|
-
&& this
|
|
612
|
+
&& this.#getFeatureState(feature)['map-annotation']) {
|
|
504
613
|
const polygon = turf.geometry(feature.geometry.type, feature.geometry.coordinates);
|
|
505
614
|
const area = turfArea(polygon);
|
|
506
615
|
if (smallestFeature === null || smallestArea > area) {
|
|
@@ -733,7 +842,9 @@ export class UserInteractions
|
|
|
733
842
|
tooltip.push(`<div class="feature-error">Warning: ${properties.warning}</div>`)
|
|
734
843
|
}
|
|
735
844
|
let renderedLabel;
|
|
736
|
-
if (('label' in properties
|
|
845
|
+
if (('label' in properties
|
|
846
|
+
|| 'hyperlink' in properties
|
|
847
|
+
|| 'user_label' in properties)
|
|
737
848
|
&& (forceLabel || !('tooltip' in properties) || properties.tooltip)) {
|
|
738
849
|
const renderedLabel = getRenderedLabel(properties);
|
|
739
850
|
if ('hyperlink' in properties) {
|
|
@@ -775,6 +886,19 @@ export class UserInteractions
|
|
|
775
886
|
this.resetActiveFeatures_();
|
|
776
887
|
}
|
|
777
888
|
|
|
889
|
+
#renderedFeatures(point)
|
|
890
|
+
//======================
|
|
891
|
+
{
|
|
892
|
+
let features = []
|
|
893
|
+
if (this.#paths3dLayer) {
|
|
894
|
+
features = this.#paths3dLayer.queryFeaturesAtPoint(point)
|
|
895
|
+
}
|
|
896
|
+
if (features.length === 0) {
|
|
897
|
+
features = this._map.queryRenderedFeatures(point)
|
|
898
|
+
}
|
|
899
|
+
return features.filter(feature => this.__featureEnabled(feature));
|
|
900
|
+
}
|
|
901
|
+
|
|
778
902
|
mouseMoveEvent_(event)
|
|
779
903
|
//====================
|
|
780
904
|
{
|
|
@@ -793,8 +917,7 @@ export class UserInteractions
|
|
|
793
917
|
}
|
|
794
918
|
|
|
795
919
|
// Get all the features at the current point
|
|
796
|
-
const features = this
|
|
797
|
-
.filter(feature => this.__featureEnabled(feature));
|
|
920
|
+
const features = this.#renderedFeatures(event.point)
|
|
798
921
|
if (features.length === 0) {
|
|
799
922
|
this._lastFeatureMouseEntered = null;
|
|
800
923
|
this._lastFeatureModelsMouse = null;
|
|
@@ -834,19 +957,11 @@ export class UserInteractions
|
|
|
834
957
|
if (lineFeatures.length > 0) {
|
|
835
958
|
tooltip = this.lineTooltip_(lineFeatures);
|
|
836
959
|
tooltipFeature = lineFeatures[0];
|
|
837
|
-
|
|
838
|
-
const lineFeatureId = +lineFeature.properties.featureId; // Ensure numeric
|
|
839
|
-
this.activateFeature(lineFeature);
|
|
840
|
-
const lineIds = new Set(lineFeatures.map(f => f.properties.featureId));
|
|
841
|
-
for (const featureId of this.__pathManager.lineFeatureIds(lineIds)) {
|
|
842
|
-
if (+featureId !== lineFeatureId) {
|
|
843
|
-
this.activateFeature(this.mapFeature(featureId));
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
|
-
}
|
|
960
|
+
this.activateLineFeatures(lineFeatures)
|
|
847
961
|
} else {
|
|
848
962
|
let labelledFeatures = features.filter(feature => (('hyperlink' in feature.properties
|
|
849
|
-
|| 'label' in feature.properties
|
|
963
|
+
|| 'label' in feature.properties
|
|
964
|
+
|| 'user_label' in feature.properties)
|
|
850
965
|
&& (!('tooltip' in feature.properties)
|
|
851
966
|
|| feature.properties.tooltip)))
|
|
852
967
|
.sort((a, b) => (a.properties.area - b.properties.area));
|
|
@@ -859,6 +974,9 @@ export class UserInteractions
|
|
|
859
974
|
labelledFeatures = groupFeatures;
|
|
860
975
|
}
|
|
861
976
|
const feature = labelledFeatures[0];
|
|
977
|
+
if (feature.properties.user_drawn) {
|
|
978
|
+
feature.id = feature.properties.id
|
|
979
|
+
}
|
|
862
980
|
tooltip = this.tooltipHtml_(feature.properties);
|
|
863
981
|
tooltipFeature = feature;
|
|
864
982
|
if (this._flatmap.options.debug) { // Do this when Info on and not debug??
|
|
@@ -943,7 +1061,7 @@ export class UserInteractions
|
|
|
943
1061
|
//=============================
|
|
944
1062
|
{
|
|
945
1063
|
if (feature !== undefined) {
|
|
946
|
-
const clickedFeatureId = feature.id;
|
|
1064
|
+
const clickedFeatureId = +feature.id;
|
|
947
1065
|
const dim = !('properties' in feature
|
|
948
1066
|
&& 'kind' in feature.properties
|
|
949
1067
|
&& ['cell-type', 'scaffold', 'tissue'].includes(feature.properties.kind));
|
|
@@ -982,9 +1100,9 @@ export class UserInteractions
|
|
|
982
1100
|
}
|
|
983
1101
|
|
|
984
1102
|
this.__clearActiveMarker();
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
if (clickedFeatures.length == 0){
|
|
1103
|
+
|
|
1104
|
+
const clickedFeatures = this.#renderedFeatures(event.point)
|
|
1105
|
+
if (clickedFeatures.length == 0) {
|
|
988
1106
|
this.unselectFeatures();
|
|
989
1107
|
return;
|
|
990
1108
|
}
|
|
@@ -1060,7 +1178,7 @@ export class UserInteractions
|
|
|
1060
1178
|
//=========================================
|
|
1061
1179
|
{
|
|
1062
1180
|
this.__pathManager.enablePathsByType('centreline', enable, force);
|
|
1063
|
-
this
|
|
1181
|
+
this.#setPaint({showCentrelines: enable});
|
|
1064
1182
|
}
|
|
1065
1183
|
|
|
1066
1184
|
enableSckanPaths(sckanState, enable=true)
|
|
@@ -1087,7 +1205,7 @@ export class UserInteractions
|
|
|
1087
1205
|
excludeAnnotated(exclude=false)
|
|
1088
1206
|
//=============================
|
|
1089
1207
|
{
|
|
1090
|
-
this
|
|
1208
|
+
this.#setPaint({excludeAnnotated: exclude});
|
|
1091
1209
|
}
|
|
1092
1210
|
|
|
1093
1211
|
//==============================================================================
|