@abi-software/flatmap-viewer 2.3.0-a.1 → 2.3.0-a.3

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/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
+ }
@@ -29,18 +29,17 @@ import 'maplibre-gl/dist/maplibre-gl.css';
29
29
 
30
30
  // Load our stylesheet last so we can overide styling rules
31
31
 
32
- import '../static/flatmap-viewer.css';
32
+ import '../static/css/flatmap-viewer.css';
33
33
 
34
34
  //==============================================================================
35
35
 
36
36
  import {MapServer} from './mapserver.js';
37
37
  import {MinimapControl} from './minimap.js';
38
38
  import {NavigationControl} from './controls.js';
39
- import {SearchIndex, SearchResults} from './search.js';
39
+ import {SearchIndex} from './search.js';
40
40
  import {UserInteractions} from './interactions.js';
41
41
 
42
42
  import * as images from './images.js';
43
- import * as pathways from './pathways.js';
44
43
  import * as utils from './utils.js';
45
44
 
46
45
  //==============================================================================
@@ -275,23 +274,24 @@ class FlatMap
275
274
  }
276
275
 
277
276
  /**
278
- * @returns {Array.<{type: string, label: string, colour: string}>} an array of objects giving path types
279
- * with their descriptions and colours
277
+ * @returns {Array.<{type: string, label: string, colour: string}>} an array of objects giving the path types
278
+ * present in the map along with their
279
+ * descriptions and colours
280
280
  */
281
281
  pathTypes()
282
282
  //=========
283
283
  {
284
284
  if (this._userInteractions !== null) {
285
- return this._userInteractions.pathTypes();
285
+ return this._userInteractions.pathways.pathTypes();
286
286
  }
287
287
  }
288
288
 
289
289
  /**
290
290
  * Hide or show paths of a given type.
291
291
  *
292
- * @param {string} pathType The path type
293
- * @param {boolean} [enable=true] If ``true`` then only show the paths
294
- * of the type otherwise only hide the paths
292
+ * @param {string} pathType The path type
293
+ * @param {boolean} enable Show or hide paths of that type. Defaults to
294
+ * ``true`` (show)
295
295
  */
296
296
  enablePath(pathType, enable=true)
297
297
  //===============================
@@ -302,17 +302,31 @@ class FlatMap
302
302
  }
303
303
 
304
304
  /**
305
- * Hide or show all paths except those of the given type.
305
+ * Hide or show all paths valid in SCKAN.
306
306
  *
307
- * @param {string|Array.<string>} pathTypes The path type(s)
308
- * @param {boolean} [enable=true] If ``true`` then only show the paths
309
- * of the type(s) otherwise only hide the paths
307
+ * @param {string} sckanState Either ``valid`` or ``invalid``
308
+ * @param {boolean} enable Show or hide paths with that SCKAN state.
309
+ * Defaults to ``true`` (show)
310
310
  */
311
- showPaths(pathTypes, enable=true)
312
- //===============================
311
+ enableSckanPath(sckanState, enable=true)
312
+ //======================================
313
+ {
314
+ if (this._userInteractions !== null) {
315
+ this._userInteractions.enableSckanPath(sckanState, enable);
316
+ }
317
+ }
318
+
319
+ /**
320
+ * Hide or show centrelines and nodes.
321
+ *
322
+ * @param {boolean} enable Show or centrelines and associated nodes.
323
+ * Defaults to ``true`` (show)
324
+ */
325
+ enableCentrelines(enable=true)
326
+ //============================
313
327
  {
314
328
  if (this._userInteractions !== null) {
315
- this._userInteractions.showPaths(pathTypes, enable);
329
+ this._userInteractions.enableCentrelines(enable);
316
330
  }
317
331
  }
318
332
 
@@ -404,6 +418,20 @@ class FlatMap
404
418
  */
405
419
  get id()
406
420
  //======
421
+ {
422
+ return this.__id;
423
+ }
424
+
425
+ /**
426
+ * The map's unique universal identifier.
427
+ *
428
+ * For published maps this is different to the map's ``id``;
429
+ * it might be the same as ``id`` for unpublished maps.
430
+ *
431
+ * @type string
432
+ */
433
+ get uuid()
434
+ //========
407
435
  {
408
436
  return this.__uuid;
409
437
  }
@@ -430,12 +458,6 @@ class FlatMap
430
458
  return `${this.__uuid}-${this._mapNumber}`;
431
459
  }
432
460
 
433
- get activeLayerNames()
434
- //====================
435
- {
436
- return this._userInteractions.activeLayerNames;
437
- }
438
-
439
461
  get annotations()
440
462
  //===============
441
463
  {
@@ -578,9 +600,10 @@ class FlatMap
578
600
  //=======
579
601
  {
580
602
  return {
581
- 'minZoom': this._map.getMinZoom(),
582
- 'zoom': this._map.getZoom(),
583
- 'maxZoom': this._map.getMaxZoom()
603
+ mapUUID: this.__uuid,
604
+ minZoom: this._map.getMinZoom(),
605
+ zoom: this._map.getZoom(),
606
+ maxZoom: this._map.getMaxZoom()
584
607
  }
585
608
  }
586
609
 
@@ -588,6 +611,7 @@ class FlatMap
588
611
  //===========================
589
612
  {
590
613
  if (this._callback) {
614
+ data.mapUUID = this.__uuid;
591
615
  return this._callback(type, data, ...args);
592
616
  }
593
617
  }
@@ -623,12 +647,6 @@ class FlatMap
623
647
  this._map.resize();
624
648
  }
625
649
 
626
- mapLayerId(name)
627
- //==============
628
- {
629
- return `${this.uniqueId}/${name}`;
630
- }
631
-
632
650
  getIdentifier()
633
651
  //=============
634
652
  {
@@ -663,15 +681,25 @@ class FlatMap
663
681
  }
664
682
  }
665
683
 
666
- setColour(options=null)
667
- //=====================
684
+ setPaint(options=null)
685
+ //====================
668
686
  {
669
- options = utils.setDefaultOptions(options, {colour: true, outline: true});
687
+ options = utils.setDefaults(options, {
688
+ colour: true,
689
+ outline: true
690
+ });
670
691
  if (this._userInteractions !== null) {
671
- this._userInteractions.setColour(options);
692
+ this._userInteractions.setPaint(options);
672
693
  }
673
694
  }
674
695
 
696
+ setColour(options=null)
697
+ //=====================
698
+ {
699
+ console.log('`setColour()` is deprecated; please use `setPaint()` instead.')
700
+ this.setPaint(options);
701
+ }
702
+
675
703
  //==========================================================================
676
704
 
677
705
  /**
@@ -704,6 +732,8 @@ class FlatMap
704
732
  setBackgroundColour(colour)
705
733
  //=========================
706
734
  {
735
+ localStorage.setItem('flatmap-background-colour', colour);
736
+
707
737
  this._map.setPaintProperty('background', 'background-color', colour);
708
738
 
709
739
  if (this._minimap) {
@@ -742,22 +772,80 @@ class FlatMap
742
772
 
743
773
  //==========================================================================
744
774
 
775
+ /**
776
+ * Get a list of the flatmap's layers.
777
+ *
778
+ * @return {Array.Object.<{id: string, description: string, enabled: boolean}>} An array with layer details
779
+ */
780
+ getLayers()
781
+ //=========
782
+ {
783
+ if (this._userInteractions !== null) {
784
+ return this._userInteractions.getLayers();
785
+ }
786
+ }
787
+
788
+ /**
789
+ * @param {string} layerId The layer identifier to enable
790
+ * @param {boolean} enable Show or hide the layer. Defaults to ``true`` (show)
791
+ *
792
+ */
793
+ enableLayer(layerId, enable=true)
794
+ //===============================
795
+ {
796
+ if (this._userInteractions !== null) {
797
+ return this._userInteractions.enableLayer(layerId, enable);
798
+ }
799
+ }
800
+
801
+ //==========================================================================
802
+
803
+ /**
804
+ * Get a list of a FC flatmap's systems.
805
+ *
806
+ * @return {Array.Object.<{name: string, colour: string}>} An array with system details
807
+ */
808
+ getSystems()
809
+ //==========
810
+ {
811
+ if (this._userInteractions !== null) {
812
+ return this._userInteractions.getSystems();
813
+ }
814
+ }
815
+
816
+ /**
817
+ * @param {string} systemName The name of the system to enable
818
+ * @param {boolean} enable Show or hide the system. Defaults to ``true`` (show)
819
+ *
820
+ */
821
+ enableSystem(systemName, enable=true)
822
+ //===================================
823
+ {
824
+ if (this._userInteractions !== null) {
825
+ return this._userInteractions.enableSystem(systemName, enable);
826
+ }
827
+ }
828
+
829
+ //==========================================================================
830
+
745
831
  /**
746
832
  * Add a marker to the map.
747
833
  *
748
- * @param {string} anatomicalId The anatomical identifier of the feature on which
749
- * to place the marker
750
- * @param {string} [markerType=''] An optional parameter giving the type of marker
751
- * to use. Apart from the default, the only marker
752
- * type recognised is ``simulation``
753
- * @return {integer} The identifier for the resulting marker. -1 is returned if the
754
- * map doesn't contain a feature with the given anatomical identifier
834
+ * @param {string} anatomicalId The anatomical identifier of the feature on which
835
+ * to place the marker.
836
+ * @arg {Object} options Configurable options for the marker.
837
+ * @arg {string} options.colour Colour of the default marker. Defaults to ``'#005974'``
838
+ * (dark blue).
839
+ * @arg {string} options.element The DOM element to use as a marker. The default is
840
+ * a dark blue droplet-shaped SVG marker.
841
+ * @return {integer} The identifier for the resulting marker. -1 is returned if the
842
+ * map doesn't contain a feature with the given anatomical identifier
755
843
  */
756
- addMarker(anatomicalId, markerType='')
757
- //====================================
844
+ addMarker(anatomicalId, options={})
845
+ //==================================
758
846
  {
759
847
  if (this._userInteractions !== null) {
760
- return this._userInteractions.addMarker(anatomicalId, markerType);
848
+ return this._userInteractions.addMarker(anatomicalId, options);
761
849
  }
762
850
  return -1;
763
851
  }
@@ -839,13 +927,21 @@ class FlatMap
839
927
  'label',
840
928
  'models',
841
929
  'nodeId',
842
- 'source'
930
+ 'source',
931
+ 'hyperlinks'
932
+ ];
933
+ const jsonProperties = [
934
+ 'hyperlinks'
843
935
  ];
844
936
  for (const property of exportedProperties) {
845
937
  if (property in properties) {
846
938
  const value = properties[property];
847
939
  if (value !== undefined) {
848
- data[property] = properties[property];
940
+ if (jsonProperties.indexOf(property) >= 0) {
941
+ data[property] = JSON.parse(properties[property])
942
+ } else {
943
+ data[property] = properties[property];
944
+ }
849
945
  }
850
946
  }
851
947
  }
@@ -874,6 +970,23 @@ class FlatMap
874
970
  });
875
971
  }
876
972
 
973
+ /**
974
+ * Generate a callback as a result of some event in a control.
975
+ *
976
+ * @param {string} eventType The event type
977
+ * @param {string} control The name of the control
978
+ * @param {string} value The value of the control
979
+ */
980
+ controlEvent(eventType, control, value)
981
+ //=====================================
982
+ {
983
+ this.callback(eventType, {
984
+ type: 'control',
985
+ control: control,
986
+ value: value
987
+ });
988
+ }
989
+
877
990
  /**
878
991
  * Generate callbacks as a result of panning/zooming the map.
879
992
  *
@@ -1014,7 +1127,11 @@ class FlatMap
1014
1127
  zoomToFeatures(externalIds, options=null)
1015
1128
  //=======================================
1016
1129
  {
1017
- options = utils.setDefaultOptions(options, {select: true, highlight: false, padding:100});
1130
+ options = utils.setDefaults(options, {
1131
+ select: true,
1132
+ highlight: false,
1133
+ padding:100
1134
+ });
1018
1135
  if (this._userInteractions !== null) {
1019
1136
  const featureIds = this.modelFeatureIdList(externalIds);
1020
1137
  this._userInteractions.zoomToFeatures(featureIds, options);
@@ -1150,13 +1267,12 @@ export class MapManager
1150
1267
  * @arg options {Object} Configurable options for the map.
1151
1268
  * @arg options.background {string} Background colour of flatmap. Defaults to ``white``.
1152
1269
  * @arg options.debug {boolean} Enable debugging mode.
1153
- * @arg options.featureInfo {boolean} Show information about features as a tooltip. The tooltip is active
1154
- * on selected features and, for non-selected features, when the
1155
- * ``info`` control is enabled. More details are shown in debug mode.
1156
1270
  * @arg options.fullscreenControl {boolean} Add a ``Show full screen`` button to the map.
1157
1271
  * @arg options.layerOptions {Object} Options to control colour and outlines of features
1158
1272
  * @arg options.layerOptions.colour {boolean} Use colour fill (if available) for features. Defaults to ``true``.
1159
1273
  * @arg options.layerOptions.outline {boolean} Show the border of features. Defaults to ``true``.
1274
+ * @arg options.layerOptions.sckan {string} Show neuron paths known to SCKAN: values are ``valid`` (default),
1275
+ * ``invalid``, ``all`` or ``none``.
1160
1276
  * @arg options.minimap {boolean|Object} Display a MiniMap of the flatmap. Defaults to ``false``.
1161
1277
  * @arg options.minimap.position {string} The minimap's position: ``bottom-left`` (default), ``bottom-right``,
1162
1278
  * ``top-left`` or ``top-right``.
@@ -1167,9 +1283,9 @@ export class MapManager
1167
1283
  * @arg options.maxZoom {number} The maximum zoom level of the map.
1168
1284
  * @arg options.minZoom {number} The minimum zoom level of the map.
1169
1285
  * @arg options.navigationControl {boolean} Add navigation controls (zoom buttons) to the map.
1170
- * @arg options.pathControl {boolean} Add buttons to control pathways including via a color-coded legend.
1171
- * @arg options.searchable {boolean} Add a control to search for features on a map.
1172
1286
  * @arg options.showPosition {boolean} Show ``position`` of tooltip.
1287
+ * @arg options.standalone {boolean} Viewer is running ``standalone``, as opposed to integrated into
1288
+ * another application so show a number of controls. Defaults to ``false``.
1173
1289
  * @example
1174
1290
  * const humanMap1 = mapManager.loadMap('humanV1', 'div-1');
1175
1291
  *
@@ -1207,13 +1323,15 @@ export class MapManager
1207
1323
  mapOptions['bounds'] = mapIndex['bounds'];
1208
1324
  }
1209
1325
 
1210
- // Default is to show path controls
1326
+ // Note the kind of map
1211
1327
 
1212
- if (!('pathControls' in mapOptions)) {
1213
- mapOptions['pathControls'] = true;
1328
+ if ('style' in mapIndex) {
1329
+ mapOptions.style = mapIndex.style; // Currently ``anatomical`` or ``functional``
1330
+ } else {
1331
+ mapOptions.style = 'flatmap'; // Default is a generic ``flatmap``
1214
1332
  }
1215
1333
 
1216
- // Mapmaker's changed the name of the field to indicate that indicates if
1334
+ // Mapmaker has changed the name of the field to indicate that indicates if
1217
1335
  // there are raster layers
1218
1336
  if (!('image-layers' in mapIndex) && ('image_layer' in mapIndex)) {
1219
1337
  mapIndex['image-layers'] = mapIndex['image_layer'];
@@ -1287,13 +1405,7 @@ export class MapManager
1287
1405
  outline: true
1288
1406
  };
1289
1407
  }
1290
- if ('authoring' in mapIndex) {
1291
- mapOptions.layerOptions.style == 'authoring'
1292
- } else if ('style' in mapIndex) {
1293
- mapOptions.layerOptions.style = mapIndex.style;
1294
- } else {
1295
- mapOptions.layerOptions.style = 'flatmap';
1296
- }
1408
+ mapOptions.layerOptions.authoring = ('authoring' in mapIndex) ? mapIndex.authoring : false;
1297
1409
 
1298
1410
  // Are features in separate vector tile source layers?
1299
1411
 
package/src/info.js CHANGED
@@ -238,7 +238,11 @@ export class InfoControl
238
238
  if (prop in feature.properties) {
239
239
  const value = feature.properties[prop];
240
240
  if (value !== undefined) {
241
- values[prop] = value;
241
+ if (prop === 'label') {
242
+ values[prop] = value.replaceAll("\n", "<br/>");
243
+ } else {
244
+ values[prop] = value;
245
+ }
242
246
  }
243
247
  }
244
248
  });