@abi-software/flatmap-viewer 2.5.1 → 2.5.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/README.rst +1 -1
- package/package.json +1 -2
- package/src/controls/annotation.js +70 -175
- package/src/flatmap-viewer.js +117 -20
- package/src/interactions.js +29 -9
- package/src/layers/paths3d.js +0 -1
- package/src/main.js +54 -1
- package/static/css/flatmap-viewer.css +3 -133
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.5.
|
|
41
|
+
* ``npm install @abi-software/flatmap-viewer@2.5.3``
|
|
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.5.
|
|
3
|
+
"version": "2.5.3",
|
|
4
4
|
"description": "Flatmap viewer using Maplibre GL",
|
|
5
5
|
"repository": "https://github.com/AnatomicMaps/flatmap-viewer.git",
|
|
6
6
|
"main": "src/main.js",
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
"author": "David Brooks",
|
|
18
18
|
"license": "MIT",
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@abi-software/mapbox-gl-draw-freehand-mode": "^2.2.0",
|
|
21
20
|
"@babel/runtime": "^7.10.4",
|
|
22
21
|
"@deck.gl/core": "^8.9.33",
|
|
23
22
|
"@deck.gl/layers": "^8.9.33",
|
|
@@ -44,161 +44,10 @@ limitations under the License.
|
|
|
44
44
|
import MapboxDraw from "@mapbox/mapbox-gl-draw"
|
|
45
45
|
import '@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css'
|
|
46
46
|
|
|
47
|
-
// NB: https://github.com/bemky/mapbox-gl-draw-freehand-mode/issues/25
|
|
48
|
-
import FreehandMode from 'mapbox-gl-draw-freehand-mode'
|
|
49
47
|
|
|
50
48
|
//==============================================================================
|
|
51
49
|
|
|
52
|
-
const
|
|
53
|
-
{
|
|
54
|
-
'id': 'highlight-active-points',
|
|
55
|
-
'type': 'circle',
|
|
56
|
-
'filter': ['all',
|
|
57
|
-
['==', '$type', 'Point'],
|
|
58
|
-
['==', 'meta', 'feature'],
|
|
59
|
-
['==', 'active', 'true']],
|
|
60
|
-
'paint': {
|
|
61
|
-
'circle-radius': 7,
|
|
62
|
-
'circle-color': '#080'
|
|
63
|
-
}
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
'id': 'points-are-red',
|
|
67
|
-
'type': 'circle',
|
|
68
|
-
'filter': ['all',
|
|
69
|
-
['==', '$type', 'Point'],
|
|
70
|
-
['==', 'meta', 'feature'],
|
|
71
|
-
['==', 'active', 'false']],
|
|
72
|
-
'paint': {
|
|
73
|
-
'circle-radius': 5,
|
|
74
|
-
'circle-color': '#800'
|
|
75
|
-
}
|
|
76
|
-
},
|
|
77
|
-
// ACTIVE (being drawn)
|
|
78
|
-
// line stroke
|
|
79
|
-
{
|
|
80
|
-
"id": "gl-draw-line",
|
|
81
|
-
"type": "line",
|
|
82
|
-
"filter": ["all", ["==", "$type", "LineString"], ["!=", "mode", "static"]],
|
|
83
|
-
"layout": {
|
|
84
|
-
"line-cap": "round",
|
|
85
|
-
"line-join": "round"
|
|
86
|
-
},
|
|
87
|
-
"paint": {
|
|
88
|
-
"line-color": "#D20C0C",
|
|
89
|
-
"line-dasharray": [0.2, 2],
|
|
90
|
-
"line-width": 2
|
|
91
|
-
}
|
|
92
|
-
},
|
|
93
|
-
// polygon fill
|
|
94
|
-
{
|
|
95
|
-
"id": "gl-draw-polygon-fill",
|
|
96
|
-
"type": "fill",
|
|
97
|
-
"filter": ["all", ["==", "$type", "Polygon"], ["!=", "mode", "static"]],
|
|
98
|
-
"paint": {
|
|
99
|
-
'fill-color': [
|
|
100
|
-
'case',
|
|
101
|
-
['boolean', ['feature-state', 'active'], false], '#D88',
|
|
102
|
-
'#020C0C'
|
|
103
|
-
],
|
|
104
|
-
"fill-outline-color": "#D20C0C",
|
|
105
|
-
"fill-opacity": 0.1
|
|
106
|
-
}
|
|
107
|
-
},
|
|
108
|
-
// polygon mid points
|
|
109
|
-
{
|
|
110
|
-
'id': 'gl-draw-polygon-midpoint',
|
|
111
|
-
'type': 'circle',
|
|
112
|
-
'filter': ['all',
|
|
113
|
-
['==', '$type', 'Point'],
|
|
114
|
-
['==', 'meta', 'midpoint']],
|
|
115
|
-
'paint': {
|
|
116
|
-
'circle-radius': 3,
|
|
117
|
-
'circle-color': '#fbb03b'
|
|
118
|
-
}
|
|
119
|
-
},
|
|
120
|
-
// polygon outline stroke
|
|
121
|
-
// This doesn't style the first edge of the polygon, which uses the line stroke styling instead
|
|
122
|
-
{
|
|
123
|
-
"id": "gl-draw-polygon-stroke-active",
|
|
124
|
-
"type": "line",
|
|
125
|
-
"filter": ["all", ["==", "$type", "Polygon"], ["!=", "mode", "static"]],
|
|
126
|
-
"layout": {
|
|
127
|
-
"line-cap": "round",
|
|
128
|
-
"line-join": "round"
|
|
129
|
-
},
|
|
130
|
-
"paint": {
|
|
131
|
-
"line-color": "#D20C0C",
|
|
132
|
-
"line-dasharray": [0.2, 2],
|
|
133
|
-
"line-width": 2
|
|
134
|
-
}
|
|
135
|
-
},
|
|
136
|
-
// vertex point halos
|
|
137
|
-
{
|
|
138
|
-
"id": "gl-draw-polygon-and-line-vertex-halo-active",
|
|
139
|
-
"type": "circle",
|
|
140
|
-
"filter": ["all", ["==", "meta", "vertex"], ["==", "$type", "Point"], ["!=", "mode", "static"]],
|
|
141
|
-
"paint": {
|
|
142
|
-
"circle-radius": 5,
|
|
143
|
-
"circle-color": "#FFF"
|
|
144
|
-
}
|
|
145
|
-
},
|
|
146
|
-
// vertex points
|
|
147
|
-
{
|
|
148
|
-
"id": "gl-draw-polygon-and-line-vertex-active",
|
|
149
|
-
"type": "circle",
|
|
150
|
-
"filter": ["all", ["==", "meta", "vertex"], ["==", "$type", "Point"], ["!=", "mode", "static"]],
|
|
151
|
-
"paint": {
|
|
152
|
-
"circle-radius": 3,
|
|
153
|
-
"circle-color": "#D20C0C",
|
|
154
|
-
}
|
|
155
|
-
},
|
|
156
|
-
|
|
157
|
-
// INACTIVE (static, already drawn)
|
|
158
|
-
// line stroke
|
|
159
|
-
{
|
|
160
|
-
"id": "gl-draw-line-static",
|
|
161
|
-
"type": "line",
|
|
162
|
-
"filter": ["all", ["==", "$type", "LineString"], ["==", "mode", "static"]],
|
|
163
|
-
"layout": {
|
|
164
|
-
"line-cap": "round",
|
|
165
|
-
"line-join": "round"
|
|
166
|
-
},
|
|
167
|
-
"paint": {
|
|
168
|
-
"line-color": "#000",
|
|
169
|
-
"line-width": 3
|
|
170
|
-
}
|
|
171
|
-
},
|
|
172
|
-
// polygon fill
|
|
173
|
-
{
|
|
174
|
-
"id": "gl-draw-polygon-fill-static",
|
|
175
|
-
"type": "fill",
|
|
176
|
-
"filter": ["all", ["==", "$type", "Polygon"], ["==", "mode", "static"]],
|
|
177
|
-
"paint": {
|
|
178
|
-
"fill-color": "#000",
|
|
179
|
-
"fill-outline-color": "#000",
|
|
180
|
-
"fill-opacity": 0.1
|
|
181
|
-
}
|
|
182
|
-
},
|
|
183
|
-
// polygon outline
|
|
184
|
-
{
|
|
185
|
-
"id": "gl-draw-polygon-stroke-static",
|
|
186
|
-
"type": "line",
|
|
187
|
-
"filter": ["all", ["==", "$type", "Polygon"], ["==", "mode", "static"]],
|
|
188
|
-
"layout": {
|
|
189
|
-
"line-cap": "round",
|
|
190
|
-
"line-join": "round"
|
|
191
|
-
},
|
|
192
|
-
"paint": {
|
|
193
|
-
"line-color": "#000",
|
|
194
|
-
"line-width": 3
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
]
|
|
198
|
-
|
|
199
|
-
//==============================================================================
|
|
200
|
-
|
|
201
|
-
const drawStyleIds = drawStyles.map(s => s.id)
|
|
50
|
+
const drawStyleIds = MapboxDraw.lib.theme.map(s => s.id)
|
|
202
51
|
|
|
203
52
|
export const DRAW_ANNOTATION_LAYERS = [...drawStyleIds.map(id => `${id}.cold`),
|
|
204
53
|
...drawStyleIds.map(id => `${id}.hot`)]
|
|
@@ -207,8 +56,6 @@ export const DRAW_ANNOTATION_LAYERS = [...drawStyleIds.map(id => `${id}.cold`),
|
|
|
207
56
|
|
|
208
57
|
export class AnnotationDrawControl
|
|
209
58
|
{
|
|
210
|
-
#visible
|
|
211
|
-
|
|
212
59
|
constructor(flatmap, visible=false)
|
|
213
60
|
{
|
|
214
61
|
MapboxDraw.constants.classes.CONTROL_BASE = 'maplibregl-ctrl'
|
|
@@ -216,7 +63,9 @@ export class AnnotationDrawControl
|
|
|
216
63
|
MapboxDraw.constants.classes.CONTROL_GROUP = 'maplibregl-ctrl-group'
|
|
217
64
|
|
|
218
65
|
this.__flatmap = flatmap
|
|
219
|
-
this
|
|
66
|
+
this.__committedFeatures = new Map()
|
|
67
|
+
this.__uncommittedFeatureIds = new Set()
|
|
68
|
+
this.__visible = visible
|
|
220
69
|
this.__draw = new MapboxDraw({
|
|
221
70
|
displayControlsDefault: false,
|
|
222
71
|
controls: {
|
|
@@ -226,12 +75,7 @@ export class AnnotationDrawControl
|
|
|
226
75
|
trash: true
|
|
227
76
|
},
|
|
228
77
|
userProperties: true,
|
|
229
|
-
keybindings: true
|
|
230
|
-
modes: {
|
|
231
|
-
...MapboxDraw.modes,
|
|
232
|
-
draw_polygon: FreehandMode
|
|
233
|
-
},
|
|
234
|
-
styles: drawStyles
|
|
78
|
+
keybindings: true
|
|
235
79
|
})
|
|
236
80
|
this.__map = null
|
|
237
81
|
}
|
|
@@ -255,7 +99,7 @@ export class AnnotationDrawControl
|
|
|
255
99
|
map.on('draw.create', this.createdFeature.bind(this))
|
|
256
100
|
map.on('draw.delete', this.deletedFeature.bind(this))
|
|
257
101
|
map.on('draw.update', this.updatedFeature.bind(this))
|
|
258
|
-
this.show(this
|
|
102
|
+
this.show(this.__visible)
|
|
259
103
|
return this.__container
|
|
260
104
|
}
|
|
261
105
|
|
|
@@ -272,17 +116,17 @@ export class AnnotationDrawControl
|
|
|
272
116
|
{
|
|
273
117
|
if (this.__container) {
|
|
274
118
|
this.__container.style.display = visible ? 'block' : 'none'
|
|
275
|
-
if (visible && !this
|
|
119
|
+
if (visible && !this.__visible) {
|
|
276
120
|
for (const layerId of DRAW_ANNOTATION_LAYERS) {
|
|
277
121
|
this.__map.setLayoutProperty(layerId, 'visibility', 'visible')
|
|
278
122
|
}
|
|
279
|
-
} else if (!visible && this
|
|
123
|
+
} else if (!visible && this.__visible) {
|
|
280
124
|
for (const layerId of DRAW_ANNOTATION_LAYERS) {
|
|
281
125
|
this.__map.setLayoutProperty(layerId, 'visibility', 'none')
|
|
282
126
|
}
|
|
283
127
|
}
|
|
284
128
|
}
|
|
285
|
-
this
|
|
129
|
+
this.__visible = visible
|
|
286
130
|
}
|
|
287
131
|
|
|
288
132
|
#cleanFeature(event)
|
|
@@ -292,13 +136,20 @@ export class AnnotationDrawControl
|
|
|
292
136
|
.map(f => {
|
|
293
137
|
return {
|
|
294
138
|
id: f.id,
|
|
139
|
+
type: 'Feature',
|
|
295
140
|
geometry: f.geometry
|
|
296
|
-
// properties
|
|
297
141
|
}
|
|
298
142
|
})
|
|
299
143
|
return features.length ? features[0] : null
|
|
300
144
|
}
|
|
301
145
|
|
|
146
|
+
#sendEvent(type, feature)
|
|
147
|
+
//=======================
|
|
148
|
+
{
|
|
149
|
+
this.__uncommittedFeatureIds.add(feature.id)
|
|
150
|
+
this.__flatmap.annotationEvent(type, feature)
|
|
151
|
+
}
|
|
152
|
+
|
|
302
153
|
createdFeature(event)
|
|
303
154
|
//===================
|
|
304
155
|
{
|
|
@@ -307,13 +158,12 @@ export class AnnotationDrawControl
|
|
|
307
158
|
// Set properties to indicate that this is a drawn annotation
|
|
308
159
|
this.__draw.setFeatureProperty(feature.id, 'drawn', true)
|
|
309
160
|
this.__draw.setFeatureProperty(feature.id, 'label', 'Drawn annotation')
|
|
310
|
-
// They
|
|
311
|
-
// for storage
|
|
161
|
+
// They need to be on the feature passed to the annotator for storage
|
|
312
162
|
feature.properties = {
|
|
313
163
|
user_drawn: true,
|
|
314
164
|
user_label: 'Drawn annotation'
|
|
315
165
|
}
|
|
316
|
-
this
|
|
166
|
+
this.#sendEvent('created', feature)
|
|
317
167
|
}
|
|
318
168
|
}
|
|
319
169
|
|
|
@@ -322,7 +172,11 @@ export class AnnotationDrawControl
|
|
|
322
172
|
{
|
|
323
173
|
const feature = this.#cleanFeature(event)
|
|
324
174
|
if (feature) {
|
|
325
|
-
this.
|
|
175
|
+
if (this.__uncommittedFeatureIds.has(feature.id)) {
|
|
176
|
+
// Ignore delete on an uncommitted create or update
|
|
177
|
+
} else {
|
|
178
|
+
this.#sendEvent('deleted', feature)
|
|
179
|
+
}
|
|
326
180
|
}
|
|
327
181
|
}
|
|
328
182
|
|
|
@@ -331,20 +185,61 @@ export class AnnotationDrawControl
|
|
|
331
185
|
{
|
|
332
186
|
const feature = this.#cleanFeature(event)
|
|
333
187
|
if (feature) {
|
|
334
|
-
this.
|
|
188
|
+
if (this.__uncommittedFeatureIds.has(feature.id)) {
|
|
189
|
+
// Ignore updates on an uncommitted create or update
|
|
190
|
+
} else {
|
|
191
|
+
this.#sendEvent('updated', feature)
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
commitEvent(event)
|
|
197
|
+
//================
|
|
198
|
+
{
|
|
199
|
+
const feature = event.feature
|
|
200
|
+
if (event.type === 'deleted') {
|
|
201
|
+
this.__committedFeatures.delete(feature.id)
|
|
202
|
+
} else {
|
|
203
|
+
this.__committedFeatures.set(feature.id, feature)
|
|
204
|
+
}
|
|
205
|
+
this.__uncommittedFeatureIds.delete(feature.id)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
rollbackEvent(event)
|
|
209
|
+
//==================
|
|
210
|
+
{
|
|
211
|
+
const feature = event.feature
|
|
212
|
+
if (event.type === 'created') {
|
|
213
|
+
this.__draw.delete(feature.id)
|
|
214
|
+
this.__committedFeatures.delete(feature.id)
|
|
215
|
+
this.__uncommittedFeatureIds.delete(feature.id)
|
|
216
|
+
} else if (event.type === 'deleted') {
|
|
217
|
+
this.__draw.add(feature)
|
|
218
|
+
this.__committedFeatures.set(feature.id, feature)
|
|
219
|
+
this.__uncommittedFeatureIds.delete(feature.id)
|
|
220
|
+
} else if (event.type === 'updated') {
|
|
221
|
+
const savedFeature = this.__committedFeatures.get(feature.id)
|
|
222
|
+
if (savedFeature) {
|
|
223
|
+
this.__draw.delete(feature.id)
|
|
224
|
+
this.__draw.add(savedFeature)
|
|
225
|
+
this.__uncommittedFeatureIds.delete(feature.id)
|
|
226
|
+
}
|
|
335
227
|
}
|
|
336
228
|
}
|
|
337
229
|
|
|
338
230
|
addFeature(feature)
|
|
339
231
|
//=================
|
|
340
232
|
{
|
|
341
|
-
|
|
233
|
+
feature = Object.assign({}, feature, {type: 'Feature'})
|
|
234
|
+
const ids = this.__draw.add(feature)
|
|
235
|
+
this.__committedFeatures.set(ids[0], feature)
|
|
236
|
+
this.__uncommittedFeatureIds.delete(ids[0])
|
|
342
237
|
}
|
|
343
238
|
|
|
344
|
-
|
|
345
|
-
|
|
239
|
+
refreshGeometry(feature)
|
|
240
|
+
//======================
|
|
346
241
|
{
|
|
347
|
-
this.__draw.
|
|
242
|
+
return this.__draw.get(feature.id) || null
|
|
348
243
|
}
|
|
349
244
|
}
|
|
350
245
|
|
package/src/flatmap-viewer.js
CHANGED
|
@@ -589,19 +589,32 @@ class FlatMap
|
|
|
589
589
|
}
|
|
590
590
|
|
|
591
591
|
/**
|
|
592
|
-
* Get feature ids of all nodes of a path model.
|
|
592
|
+
* Get GeoJSON feature ids of all nodes of a path model.
|
|
593
593
|
*
|
|
594
594
|
* @param {string} pathId The path model identifier
|
|
595
|
-
* @return {
|
|
595
|
+
* @return {Array<string>} GeoJSON identifiers of features on the path
|
|
596
596
|
*/
|
|
597
597
|
pathModelNodes(modelId)
|
|
598
598
|
//=====================
|
|
599
599
|
{
|
|
600
600
|
if (this._userInteractions !== null) {
|
|
601
|
-
return this._userInteractions.pathModelNodes(modelId)
|
|
601
|
+
return new [...this._userInteractions.pathModelNodes(modelId)]
|
|
602
602
|
}
|
|
603
603
|
}
|
|
604
604
|
|
|
605
|
+
/**
|
|
606
|
+
* Get GeoJSON feature ids of all features identified with a taxon.
|
|
607
|
+
*
|
|
608
|
+
* @param {string} taxonId The taxon identifier
|
|
609
|
+
* @return {Array<string>} GeoJSON identifiers of features on the path
|
|
610
|
+
*/
|
|
611
|
+
taxonFeatureIds(taxonId)
|
|
612
|
+
//======================
|
|
613
|
+
{
|
|
614
|
+
const featureIds = this.__taxonToFeatureIds.get(utils.normaliseId(taxonId))
|
|
615
|
+
return [...new Set(featureIds ? featureIds : [])]
|
|
616
|
+
}
|
|
617
|
+
|
|
605
618
|
get layers()
|
|
606
619
|
//==========
|
|
607
620
|
{
|
|
@@ -1051,35 +1064,83 @@ class FlatMap
|
|
|
1051
1064
|
}
|
|
1052
1065
|
|
|
1053
1066
|
/**
|
|
1054
|
-
* Generate an ``
|
|
1067
|
+
* Generate an ``annotation`` callback event when a drawn annotation has been created
|
|
1068
|
+
* a modified.
|
|
1055
1069
|
*
|
|
1056
|
-
* @param {string}
|
|
1057
|
-
* @param {
|
|
1058
|
-
*
|
|
1059
|
-
*
|
|
1070
|
+
* @param eventType {string} Either ``created``, ``updated`` or ``deleted``
|
|
1071
|
+
* @param feature {Object} A feature object with ``id``, ``type``, and ``geometry``
|
|
1072
|
+
* fields of a feature that has been created, updated or
|
|
1073
|
+
* deleted.
|
|
1060
1074
|
*/
|
|
1061
|
-
|
|
1062
|
-
|
|
1075
|
+
annotationEvent(eventType, feature)
|
|
1076
|
+
//=================================
|
|
1063
1077
|
{
|
|
1064
|
-
this.callback('annotation
|
|
1065
|
-
type:
|
|
1078
|
+
this.callback('annotation', {
|
|
1079
|
+
type: eventType,
|
|
1066
1080
|
feature: feature
|
|
1067
1081
|
});
|
|
1068
1082
|
}
|
|
1069
1083
|
|
|
1070
1084
|
/**
|
|
1071
|
-
*
|
|
1085
|
+
* Mark a drawn/changed annotation as having been accepted by the user.
|
|
1072
1086
|
*
|
|
1073
|
-
* @param {
|
|
1074
|
-
* @param {
|
|
1075
|
-
* @param {
|
|
1076
|
-
* @param {Object} feature.geometry The feature's geometry as GeoJSON
|
|
1087
|
+
* @param event {Object} The object as received in an annotation callback
|
|
1088
|
+
* @param event.type {string} Either ``created``, ``updated`` or ``deleted``
|
|
1089
|
+
* @param event.feature {Object} A feature object.
|
|
1077
1090
|
*/
|
|
1078
|
-
|
|
1079
|
-
|
|
1091
|
+
commitAnnotationEvent(event)
|
|
1092
|
+
//==========================
|
|
1093
|
+
{
|
|
1094
|
+
if (this._userInteractions) {
|
|
1095
|
+
this._userInteractions.commitAnnotationEvent(event)
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
/**
|
|
1100
|
+
* Mark a drawn/changed annotation as having been rejected by the user.
|
|
1101
|
+
*
|
|
1102
|
+
* @param event {Object} The object as received in an annotation callback
|
|
1103
|
+
* @param event.type {string} Either ``created``, ``updated`` or ``deleted``
|
|
1104
|
+
* @param event.feature {Object} A feature object.
|
|
1105
|
+
*/
|
|
1106
|
+
rollbackAnnotationEvent(event)
|
|
1107
|
+
//============================
|
|
1108
|
+
{
|
|
1109
|
+
if (this._userInteractions) {
|
|
1110
|
+
this._userInteractions.rollbackAnnotationEvent(event)
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
/**
|
|
1115
|
+
* Add a drawn feature to the annotation drawing tool.
|
|
1116
|
+
*
|
|
1117
|
+
* @param feature {Object} The feature to add
|
|
1118
|
+
* @param feature.id {string} The feature's id
|
|
1119
|
+
* @param feature.geometry {Object} The feature's geometry as GeoJSON
|
|
1120
|
+
*/
|
|
1121
|
+
addAnnotationFeature(feature)
|
|
1122
|
+
//===========================
|
|
1123
|
+
{
|
|
1124
|
+
if (this._userInteractions) {
|
|
1125
|
+
this._userInteractions.addAnnotationFeature(feature)
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
/**
|
|
1130
|
+
* Return the feature as it is currently drawn. This is so
|
|
1131
|
+
* the correct geometry can be saved with a feature should
|
|
1132
|
+
* a user make changes before submitting dialog provided
|
|
1133
|
+
* by an external annotator.
|
|
1134
|
+
*
|
|
1135
|
+
* @param feature {Object} The drawn feature to refresh.
|
|
1136
|
+
* @returns {Object|null} The feature with currently geometry or ``null``
|
|
1137
|
+
* if the feature has been deleted.
|
|
1138
|
+
*/
|
|
1139
|
+
refreshAnnotationFeatureGeometry(feature)
|
|
1140
|
+
//=======================================
|
|
1080
1141
|
{
|
|
1081
1142
|
if (this._userInteractions) {
|
|
1082
|
-
this._userInteractions.
|
|
1143
|
+
this._userInteractions.refreshAnnotationFeatureGeometry(feature)
|
|
1083
1144
|
}
|
|
1084
1145
|
}
|
|
1085
1146
|
|
|
@@ -1283,6 +1344,42 @@ class FlatMap
|
|
|
1283
1344
|
this._userInteractions.zoomToFeatures(featureIds, options);
|
|
1284
1345
|
}
|
|
1285
1346
|
}
|
|
1347
|
+
|
|
1348
|
+
/**
|
|
1349
|
+
* Select features on the map.
|
|
1350
|
+
*
|
|
1351
|
+
* @param {string | Array.<string>} geojsonIds A single GeoJSON feature identifiers
|
|
1352
|
+
* or an array of identifiers.
|
|
1353
|
+
*/
|
|
1354
|
+
selectGeoJSONFeatures(geojsonIds)
|
|
1355
|
+
//===============================
|
|
1356
|
+
{
|
|
1357
|
+
if (this._userInteractions !== null) {
|
|
1358
|
+
this._userInteractions.selectFeatures(geojsonIds)
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
/**
|
|
1363
|
+
* Select features and zoom the map to them.
|
|
1364
|
+
*
|
|
1365
|
+
* @param {string | Array.<string>} geojsonIds A single GeoJSON feature identifiers
|
|
1366
|
+
* or an array of identifiers.
|
|
1367
|
+
* @param {Object} [options]
|
|
1368
|
+
* @param {boolean} [options.noZoomIn=false] Don't zoom in (although zoom out as necessary)
|
|
1369
|
+
* @param {number} [options.padding=10] Padding in pixels around the composite bounding box
|
|
1370
|
+
*/
|
|
1371
|
+
zoomToGeoJSONFeatures(geojsonIds, options=null)
|
|
1372
|
+
//=============================================
|
|
1373
|
+
{
|
|
1374
|
+
options = utils.setDefaults(options, {
|
|
1375
|
+
select: true,
|
|
1376
|
+
highlight: false,
|
|
1377
|
+
padding:100
|
|
1378
|
+
})
|
|
1379
|
+
if (this._userInteractions !== null) {
|
|
1380
|
+
this._userInteractions.zoomToFeatures(geojsonIds, options)
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1286
1383
|
}
|
|
1287
1384
|
|
|
1288
1385
|
//==============================================================================
|
package/src/interactions.js
CHANGED
|
@@ -248,11 +248,11 @@ export class UserInteractions
|
|
|
248
248
|
this._map.addControl(new TaxonsControl(flatmap));
|
|
249
249
|
}
|
|
250
250
|
|
|
251
|
+
this._map.addControl(new Path3DControl(this));
|
|
252
|
+
|
|
251
253
|
if (flatmap.options.annotator) {
|
|
252
254
|
this._map.addControl(new AnnotatorControl(flatmap));
|
|
253
255
|
}
|
|
254
|
-
|
|
255
|
-
this._map.addControl(new Path3DControl(this));
|
|
256
256
|
}
|
|
257
257
|
|
|
258
258
|
// Add an initially hidden tool for drawing on the map.
|
|
@@ -326,15 +326,35 @@ export class UserInteractions
|
|
|
326
326
|
}
|
|
327
327
|
}
|
|
328
328
|
|
|
329
|
-
|
|
330
|
-
|
|
329
|
+
commitAnnotationEvent(event)
|
|
330
|
+
//==========================
|
|
331
331
|
{
|
|
332
332
|
if (this.#annotationDrawControl) {
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
333
|
+
this.#annotationDrawControl.commitEvent(event)
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
rollbackAnnotationEvent(event)
|
|
338
|
+
//==========================
|
|
339
|
+
{
|
|
340
|
+
if (this.#annotationDrawControl) {
|
|
341
|
+
this.#annotationDrawControl.rollbackEvent(event)
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
addAnnotationFeature(feature)
|
|
346
|
+
//===========================
|
|
347
|
+
{
|
|
348
|
+
if (this.#annotationDrawControl) {
|
|
349
|
+
this.#annotationDrawControl.addFeature(feature)
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
refreshAnnotationFeatureGeometry(feature)
|
|
354
|
+
//=======================================
|
|
355
|
+
{
|
|
356
|
+
if (this.#annotationDrawControl) {
|
|
357
|
+
this.#annotationDrawControl.refreshGeometry(feature)
|
|
338
358
|
}
|
|
339
359
|
}
|
|
340
360
|
|
package/src/layers/paths3d.js
CHANGED
package/src/main.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Flatmap viewer and annotation tool
|
|
4
4
|
|
|
5
|
-
Copyright (c) 2019
|
|
5
|
+
Copyright (c) 2019 - 2024 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.
|
|
@@ -27,6 +27,52 @@ export { MapManager };
|
|
|
27
27
|
|
|
28
28
|
//==============================================================================
|
|
29
29
|
|
|
30
|
+
class DrawControl
|
|
31
|
+
{
|
|
32
|
+
constructor(flatmap)
|
|
33
|
+
{
|
|
34
|
+
this._flatmap = flatmap
|
|
35
|
+
this._lastEvent = null
|
|
36
|
+
this._idField = document.getElementById('drawing-id')
|
|
37
|
+
|
|
38
|
+
this._okBtn = document.getElementById('drawing-ok')
|
|
39
|
+
if (this._okBtn) {
|
|
40
|
+
this._okBtn.addEventListener('click', e => {
|
|
41
|
+
if (this._lastEvent) {
|
|
42
|
+
const feature = this._flatmap.refreshAnnotationFeatureGeometry(this._lastEvent.feature)
|
|
43
|
+
this._flatmap.commitAnnotationEvent(this._lastEvent)
|
|
44
|
+
this._idField.innerText = ''
|
|
45
|
+
this._lastEvent = null
|
|
46
|
+
// Send `feature`, along with user comments, to the annotation service
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
this._cancelBtn = document.getElementById('drawing-cancel')
|
|
52
|
+
if (this._cancelBtn) {
|
|
53
|
+
this._cancelBtn.addEventListener('click', e => {
|
|
54
|
+
if (this._lastEvent) {
|
|
55
|
+
this._flatmap.rollbackAnnotationEvent(this._lastEvent)
|
|
56
|
+
this._idField.innerText = ''
|
|
57
|
+
this._lastEvent = null
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
handleEvent(event)
|
|
64
|
+
//================
|
|
65
|
+
{
|
|
66
|
+
console.log(event)
|
|
67
|
+
if (this._idField) {
|
|
68
|
+
this._idField.innerText = `Annotation ${event.type}, Id: ${event.feature.id}`
|
|
69
|
+
this._lastEvent = event
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
//==============================================================================
|
|
75
|
+
|
|
30
76
|
export async function standaloneViewer(map_endpoint=null, options={})
|
|
31
77
|
{
|
|
32
78
|
const requestUrl = new URL(window.location.href);
|
|
@@ -52,6 +98,7 @@ export async function standaloneViewer(map_endpoint=null, options={})
|
|
|
52
98
|
});
|
|
53
99
|
|
|
54
100
|
let currentMap = null;
|
|
101
|
+
let drawControl = null;
|
|
55
102
|
let defaultBackground = localStorage.getItem('flatmap-background-colour') || 'black';
|
|
56
103
|
|
|
57
104
|
const mapOptions = Object.assign({
|
|
@@ -90,6 +137,8 @@ export async function standaloneViewer(map_endpoint=null, options={})
|
|
|
90
137
|
mapManager.loadMap(id, 'map-canvas', (eventType, ...args) => {
|
|
91
138
|
if (args[0].type === 'control' && args[0].control === 'background') {
|
|
92
139
|
mapOptions.background = args[0].value;
|
|
140
|
+
} else if (eventType === 'annotation') {
|
|
141
|
+
drawControl.handleEvent(...args)
|
|
93
142
|
}
|
|
94
143
|
}, mapOptions)
|
|
95
144
|
.then(map => {
|
|
@@ -99,6 +148,7 @@ export async function standaloneViewer(map_endpoint=null, options={})
|
|
|
99
148
|
map.addMarker('UBERON:0001155'); // Colon
|
|
100
149
|
map.addMarker('UBERON:0001255'); // Bladder
|
|
101
150
|
currentMap = map;
|
|
151
|
+
drawControl = new DrawControl(map)
|
|
102
152
|
})
|
|
103
153
|
.catch(error => {
|
|
104
154
|
console.log(error);
|
|
@@ -180,3 +230,6 @@ export async function standaloneViewer(map_endpoint=null, options={})
|
|
|
180
230
|
|
|
181
231
|
loadMap(mapId, mapTaxon, mapSex);
|
|
182
232
|
}
|
|
233
|
+
|
|
234
|
+
//==============================================================================
|
|
235
|
+
|
|
@@ -230,139 +230,9 @@ label[for=layer-all-layers] {
|
|
|
230
230
|
font-weight: bold;
|
|
231
231
|
}
|
|
232
232
|
|
|
233
|
-
/*
|
|
234
|
-
.
|
|
235
|
-
|
|
233
|
+
/* Control buttons for standalone annotation */
|
|
234
|
+
.mapbox-gl-draw_ctrl-draw-btn.active {
|
|
235
|
+
background-color: #FBB !important;
|
|
236
236
|
}
|
|
237
237
|
|
|
238
|
-
#flatmap-annotation-panel {
|
|
239
|
-
display: flex;
|
|
240
|
-
flex-direction: column;
|
|
241
|
-
flex-wrap: nowrap;
|
|
242
|
-
height: 100%;
|
|
243
|
-
}
|
|
244
|
-
#flatmap-annotation-form
|
|
245
|
-
{
|
|
246
|
-
flex: auto;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
#flatmap-annotation-feature,
|
|
250
|
-
#flatmap-annotation-form,
|
|
251
|
-
#flatmap-annotation-existing
|
|
252
|
-
{
|
|
253
|
-
padding: 4px;
|
|
254
|
-
}
|
|
255
|
-
#flatmap-annotation-feature,
|
|
256
|
-
#flatmap-annotation-existing
|
|
257
|
-
{
|
|
258
|
-
background-color: #DDD;
|
|
259
|
-
border: 2px solid #888;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
#flatmap-annotation-existing
|
|
263
|
-
{
|
|
264
|
-
overflow: scroll;
|
|
265
|
-
max-height: 400px;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
.flatmap-annotation-prompt
|
|
269
|
-
{
|
|
270
|
-
font-weight: bold;
|
|
271
|
-
display: inline-block;
|
|
272
|
-
min-width: 80px;
|
|
273
|
-
padding-right: 6px;
|
|
274
|
-
vertical-align: top;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
.flatmap-annotation-value
|
|
278
|
-
{
|
|
279
|
-
display: inline-block;
|
|
280
|
-
vertical-align: top;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
#flatmap-annotation-formdata
|
|
284
|
-
{
|
|
285
|
-
display: flex;
|
|
286
|
-
flex-direction: column;
|
|
287
|
-
flex-wrap: nowrap;
|
|
288
|
-
padding-right: 4px;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
#flatmap-annotation-formdata div
|
|
292
|
-
{
|
|
293
|
-
padding-top: 4px;
|
|
294
|
-
padding-bottom: 4px;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
.flatmap-annotation-entry {
|
|
298
|
-
display: flex;
|
|
299
|
-
flex-wrap: nowrap;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
#flatmap-annotation-form input,
|
|
303
|
-
#flatmap-annotation-form .multiple,
|
|
304
|
-
#flatmap-annotation-form select,
|
|
305
|
-
#flatmap-annotation-form textarea
|
|
306
|
-
{
|
|
307
|
-
flex: auto;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
#flatmap-annotation-form label
|
|
311
|
-
{
|
|
312
|
-
padding-right: 6px;
|
|
313
|
-
display: inline-block;
|
|
314
|
-
width: 80px;
|
|
315
|
-
}
|
|
316
|
-
#flatmap-annotation-form .multiple,
|
|
317
|
-
#flatmap-annotation-form textarea
|
|
318
|
-
{
|
|
319
|
-
width: 360px;
|
|
320
|
-
}
|
|
321
|
-
#flatmap-annotation-form input[type="text"]
|
|
322
|
-
{
|
|
323
|
-
width: 300px;
|
|
324
|
-
}
|
|
325
|
-
#flatmap-annotation-form .multiple
|
|
326
|
-
{
|
|
327
|
-
padding:0;
|
|
328
|
-
}
|
|
329
|
-
#flatmap-annotation-form select
|
|
330
|
-
{
|
|
331
|
-
width: 160px;
|
|
332
|
-
}
|
|
333
|
-
#flatmap-annotation-form input[type="button"]
|
|
334
|
-
{
|
|
335
|
-
float: right;
|
|
336
|
-
background-color: #BBB;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
#annotation-feature-selection
|
|
340
|
-
{
|
|
341
|
-
display: flex;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
#annotation-feature-buttons
|
|
345
|
-
{
|
|
346
|
-
float: right;
|
|
347
|
-
padding-top: 10px;
|
|
348
|
-
padding-bottom: 10px;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
.jsPanel-title {
|
|
352
|
-
font-size: 1.4em !important;
|
|
353
|
-
font-weight: bold !important;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
.jsPanel-content
|
|
357
|
-
{
|
|
358
|
-
background-color: #EEE !important;
|
|
359
|
-
}
|
|
360
|
-
.jsPanel-hdr,
|
|
361
|
-
.jsPanel-ftr
|
|
362
|
-
{
|
|
363
|
-
background-color: #BBB !important;
|
|
364
|
-
}
|
|
365
|
-
.jsPanel-ftr-btn {
|
|
366
|
-
cursor: pointer;
|
|
367
|
-
}
|
|
368
238
|
/* End of file */
|