@abi-software/flatmap-viewer 2.5.10 → 2.6.0
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 +5 -4
- package/src/controls/annotation.js +4 -4
- package/src/flatmap-viewer.js +20 -5
- package/src/interactions.js +25 -42
- package/src/layers/filter.js +310 -0
- package/src/layers/index.js +132 -38
- package/src/layers/paths3d.js +60 -10
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.
|
|
41
|
+
* ``npm install @abi-software/flatmap-viewer@2.6.0``
|
|
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.
|
|
3
|
+
"version": "2.6.0",
|
|
4
4
|
"description": "Flatmap viewer using Maplibre GL",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
"license": "MIT",
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@babel/runtime": "^7.10.4",
|
|
24
|
-
"@deck.gl/core": "^8.9.
|
|
25
|
-
"@deck.gl/layers": "^8.9.
|
|
26
|
-
"@deck.gl/mapbox": "^8.9.
|
|
24
|
+
"@deck.gl/core": "^8.9.35",
|
|
25
|
+
"@deck.gl/layers": "^8.9.35",
|
|
26
|
+
"@deck.gl/mapbox": "^8.9.35",
|
|
27
27
|
"@mapbox/mapbox-gl-draw": "^1.4.3",
|
|
28
28
|
"@turf/area": "^6.5.0",
|
|
29
29
|
"@turf/bbox": "^6.5.0",
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"@turf/projection": "^6.5.0",
|
|
32
32
|
"bezier-js": "^6.1.0",
|
|
33
33
|
"colord": "^2.9.3",
|
|
34
|
+
"core-js-pure": "^3.36.1",
|
|
34
35
|
"html-es6cape": "^2.0.2",
|
|
35
36
|
"maplibre-gl": ">=3.6.0",
|
|
36
37
|
"mathjax-full": "^3.2.2",
|
|
@@ -264,14 +264,14 @@ export class AnnotationDrawControl
|
|
|
264
264
|
}
|
|
265
265
|
}
|
|
266
266
|
|
|
267
|
-
|
|
268
|
-
|
|
267
|
+
clearFeatures()
|
|
268
|
+
//=============
|
|
269
269
|
{
|
|
270
270
|
this.__draw.deleteAll()
|
|
271
271
|
}
|
|
272
272
|
|
|
273
|
-
|
|
274
|
-
|
|
273
|
+
removeFeature()
|
|
274
|
+
//=============
|
|
275
275
|
{
|
|
276
276
|
this.__draw.trash()
|
|
277
277
|
}
|
package/src/flatmap-viewer.js
CHANGED
|
@@ -436,6 +436,21 @@ class FlatMap
|
|
|
436
436
|
return this.__uuid;
|
|
437
437
|
}
|
|
438
438
|
|
|
439
|
+
/**
|
|
440
|
+
* The map's URL on the map server.
|
|
441
|
+
*
|
|
442
|
+
* @type string
|
|
443
|
+
*/
|
|
444
|
+
get url()
|
|
445
|
+
//========
|
|
446
|
+
{
|
|
447
|
+
let url = this.makeServerUrl('')
|
|
448
|
+
if (url.endsWith('/')) {
|
|
449
|
+
return url.substring(0, url.length - 1)
|
|
450
|
+
}
|
|
451
|
+
return url
|
|
452
|
+
}
|
|
453
|
+
|
|
439
454
|
/**
|
|
440
455
|
* The map's ``index.json`` as returned from the map server.
|
|
441
456
|
*
|
|
@@ -1122,18 +1137,18 @@ class FlatMap
|
|
|
1122
1137
|
//======================
|
|
1123
1138
|
{
|
|
1124
1139
|
if (this._userInteractions) {
|
|
1125
|
-
this._userInteractions.
|
|
1140
|
+
this._userInteractions.clearAnnotationFeatures()
|
|
1126
1141
|
}
|
|
1127
1142
|
}
|
|
1128
1143
|
|
|
1129
1144
|
/**
|
|
1130
|
-
*
|
|
1145
|
+
* Delete the selected drawn feature
|
|
1131
1146
|
*/
|
|
1132
|
-
|
|
1133
|
-
|
|
1147
|
+
removeAnnotationFeature()
|
|
1148
|
+
//=======================
|
|
1134
1149
|
{
|
|
1135
1150
|
if (this._userInteractions) {
|
|
1136
|
-
this._userInteractions.
|
|
1151
|
+
this._userInteractions.removeAnnotationFeature()
|
|
1137
1152
|
}
|
|
1138
1153
|
}
|
|
1139
1154
|
|
package/src/interactions.js
CHANGED
|
@@ -36,7 +36,6 @@ import polylabel from 'polylabel';
|
|
|
36
36
|
import {LayerManager} from './layers';
|
|
37
37
|
import {PATHWAYS_LAYER, PathManager} from './pathways';
|
|
38
38
|
import {VECTOR_TILES_SOURCE} from './layers/styling';
|
|
39
|
-
import {Paths3DLayer} from './layers/paths3d'
|
|
40
39
|
import {SystemsManager} from './systems';
|
|
41
40
|
|
|
42
41
|
import {displayedProperties, InfoControl} from './controls/info';
|
|
@@ -132,7 +131,6 @@ export class UserInteractions
|
|
|
132
131
|
{
|
|
133
132
|
#annotationDrawControl = null
|
|
134
133
|
#minimap = null
|
|
135
|
-
#paths3dLayer = null
|
|
136
134
|
|
|
137
135
|
constructor(flatmap)
|
|
138
136
|
{
|
|
@@ -166,9 +164,7 @@ export class UserInteractions
|
|
|
166
164
|
|
|
167
165
|
flatmap.setInitialPosition();
|
|
168
166
|
|
|
169
|
-
//
|
|
170
|
-
|
|
171
|
-
this._layerManager = new LayerManager(flatmap);
|
|
167
|
+
// Track enabled features
|
|
172
168
|
|
|
173
169
|
this.__featureEnabledCount = new Map(Array.from(this._flatmap.annotations.keys()).map(k => [+k, 0]));
|
|
174
170
|
|
|
@@ -180,9 +176,16 @@ export class UserInteractions
|
|
|
180
176
|
this.__pathManager = new PathManager(flatmap, this, featuresEnabled);
|
|
181
177
|
|
|
182
178
|
// The path types in this map
|
|
179
|
+
|
|
183
180
|
const mapPathTypes = this.__pathManager.pathTypes();
|
|
184
181
|
|
|
182
|
+
// Add and manage our layers. NB. this needs to after we have a
|
|
183
|
+
// path manager but before path enabled state is set.
|
|
184
|
+
|
|
185
|
+
this._layerManager = new LayerManager(flatmap, this);
|
|
186
|
+
|
|
185
187
|
// Set initial enabled state of paths
|
|
188
|
+
|
|
186
189
|
for (const path of mapPathTypes) {
|
|
187
190
|
this.__pathManager.enablePathsByType(path.type, path.enabled, true);
|
|
188
191
|
}
|
|
@@ -216,9 +219,6 @@ export class UserInteractions
|
|
|
216
219
|
this._map.addControl(new NavigationControl(flatmap), position);
|
|
217
220
|
}
|
|
218
221
|
|
|
219
|
-
// Support 3D path view
|
|
220
|
-
this.#paths3dLayer = new Paths3DLayer(flatmap, this)
|
|
221
|
-
|
|
222
222
|
// Add various controls when running standalone
|
|
223
223
|
if (flatmap.options.standalone) {
|
|
224
224
|
// Add a control to search annotations if option set
|
|
@@ -300,7 +300,8 @@ export class UserInteractions
|
|
|
300
300
|
return {
|
|
301
301
|
center: this._map.getCenter().toArray(),
|
|
302
302
|
zoom: this._map.getZoom(),
|
|
303
|
-
|
|
303
|
+
bearing: this._map.getBearing(),
|
|
304
|
+
pitch: this._map.getPitch()
|
|
304
305
|
};
|
|
305
306
|
}
|
|
306
307
|
|
|
@@ -308,14 +309,11 @@ export class UserInteractions
|
|
|
308
309
|
//=============
|
|
309
310
|
{
|
|
310
311
|
// Restore the map to a saved state
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
options['zoom'] = state.zoom;
|
|
317
|
-
if ('center' in state) {
|
|
318
|
-
options['around'] = state.center;
|
|
312
|
+
|
|
313
|
+
const options = Object.assign({}, state)
|
|
314
|
+
if ('zoom' in options) {
|
|
315
|
+
if ('center' in options) {
|
|
316
|
+
options['around'] = options.center;
|
|
319
317
|
} else {
|
|
320
318
|
options['around'] = [0, 0];
|
|
321
319
|
}
|
|
@@ -365,19 +363,19 @@ export class UserInteractions
|
|
|
365
363
|
}
|
|
366
364
|
}
|
|
367
365
|
|
|
368
|
-
|
|
369
|
-
|
|
366
|
+
clearAnnotationFeatures()
|
|
367
|
+
//=======================
|
|
370
368
|
{
|
|
371
369
|
if (this.#annotationDrawControl) {
|
|
372
|
-
this.#annotationDrawControl.
|
|
370
|
+
this.#annotationDrawControl.clearFeatures()
|
|
373
371
|
}
|
|
374
372
|
}
|
|
375
373
|
|
|
376
|
-
|
|
377
|
-
|
|
374
|
+
removeAnnotationFeature()
|
|
375
|
+
//=======================
|
|
378
376
|
{
|
|
379
377
|
if (this.#annotationDrawControl) {
|
|
380
|
-
this.#annotationDrawControl.
|
|
378
|
+
this.#annotationDrawControl.removeFeature()
|
|
381
379
|
}
|
|
382
380
|
}
|
|
383
381
|
|
|
@@ -437,9 +435,6 @@ export class UserInteractions
|
|
|
437
435
|
//================
|
|
438
436
|
{
|
|
439
437
|
this._layerManager.setPaint(options)
|
|
440
|
-
if (this.#paths3dLayer) {
|
|
441
|
-
this.#paths3dLayer.setPaint(options)
|
|
442
|
-
}
|
|
443
438
|
}
|
|
444
439
|
|
|
445
440
|
setPaint(options)
|
|
@@ -464,9 +459,7 @@ export class UserInteractions
|
|
|
464
459
|
enable3dPaths(enable=true)
|
|
465
460
|
//========================
|
|
466
461
|
{
|
|
467
|
-
|
|
468
|
-
this.#paths3dLayer.enable(enable)
|
|
469
|
-
}
|
|
462
|
+
this._layerManager.set3dMode(enable)
|
|
470
463
|
}
|
|
471
464
|
|
|
472
465
|
getSystems()
|
|
@@ -508,18 +501,14 @@ export class UserInteractions
|
|
|
508
501
|
//===============================
|
|
509
502
|
{
|
|
510
503
|
this._map.removeFeatureState(feature, key)
|
|
511
|
-
|
|
512
|
-
this.#paths3dLayer.removeFeatureState(feature.id, key)
|
|
513
|
-
}
|
|
504
|
+
this._layerManager.removeFeatureState(feature, key)
|
|
514
505
|
}
|
|
515
506
|
|
|
516
507
|
#setFeatureState(feature, state)
|
|
517
508
|
//==============================
|
|
518
509
|
{
|
|
519
510
|
this._map.setFeatureState(feature, state)
|
|
520
|
-
|
|
521
|
-
this.#paths3dLayer.setFeatureState(feature.id, state)
|
|
522
|
-
}
|
|
511
|
+
this._layerManager.setFeatureState(feature, state)
|
|
523
512
|
}
|
|
524
513
|
|
|
525
514
|
enableMapFeature(feature, enable=true)
|
|
@@ -992,13 +981,7 @@ export class UserInteractions
|
|
|
992
981
|
#renderedFeatures(point)
|
|
993
982
|
//======================
|
|
994
983
|
{
|
|
995
|
-
|
|
996
|
-
if (this.#paths3dLayer) {
|
|
997
|
-
features = this.#paths3dLayer.queryFeaturesAtPoint(point)
|
|
998
|
-
}
|
|
999
|
-
if (features.length === 0) {
|
|
1000
|
-
features = this._map.queryRenderedFeatures(point)
|
|
1001
|
-
}
|
|
984
|
+
const features = this._layerManager.featuresAtPoint(point)
|
|
1002
985
|
return features.filter(feature => this.__featureEnabled(feature));
|
|
1003
986
|
}
|
|
1004
987
|
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
/******************************************************************************
|
|
2
|
+
|
|
3
|
+
Flatmap viewer and annotation tool
|
|
4
|
+
|
|
5
|
+
Copyright (c) 2019 - 2024 David Brooks
|
|
6
|
+
|
|
7
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
you may not use this file except in compliance with the License.
|
|
9
|
+
You may obtain a copy of the License at
|
|
10
|
+
|
|
11
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
|
|
13
|
+
Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
See the License for the specific language governing permissions and
|
|
17
|
+
limitations under the License.
|
|
18
|
+
|
|
19
|
+
******************************************************************************/
|
|
20
|
+
|
|
21
|
+
import Set from 'core-js-pure/actual/set'
|
|
22
|
+
|
|
23
|
+
//==============================================================================
|
|
24
|
+
|
|
25
|
+
export class PropertiesFilter
|
|
26
|
+
{
|
|
27
|
+
#filter
|
|
28
|
+
|
|
29
|
+
constructor(filter=true)
|
|
30
|
+
//======================
|
|
31
|
+
{
|
|
32
|
+
if (filter.constructor !== Object) { // We allow boolean values
|
|
33
|
+
this.#filter = filter
|
|
34
|
+
} else {
|
|
35
|
+
this.#filter = Object.assign({}, filter)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
clear()
|
|
40
|
+
//=====
|
|
41
|
+
{
|
|
42
|
+
if (this.#filter !== true) {
|
|
43
|
+
this.#filter = true
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
expand(filter)
|
|
48
|
+
//============
|
|
49
|
+
{
|
|
50
|
+
if (this.#filter === false) {
|
|
51
|
+
this.#filter = filter
|
|
52
|
+
} else if (this.#filter !== true) {
|
|
53
|
+
const copiedFilter = Object.assign({}, filter)
|
|
54
|
+
this.#filter = { "OR": [this.#filter, copiedFilter] }
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
invert()
|
|
59
|
+
//======
|
|
60
|
+
{
|
|
61
|
+
if (this.#filter === false) {
|
|
62
|
+
this.#filter = true
|
|
63
|
+
} else if (this.#filter === true) {
|
|
64
|
+
this.#filter = false
|
|
65
|
+
} else {
|
|
66
|
+
const copiedFilter = Object.assign({}, filter)
|
|
67
|
+
this.#filter = { "NOT": copiedFilter }
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
getFilter()
|
|
72
|
+
//=========
|
|
73
|
+
{
|
|
74
|
+
return this.#filter
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
makeStyleFilter()
|
|
78
|
+
//===============
|
|
79
|
+
{
|
|
80
|
+
return this.#makeStyleFilter(this.#filter)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
match(properties)
|
|
84
|
+
//===============
|
|
85
|
+
{
|
|
86
|
+
return this.#match(properties, this.#filter)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
narrow(filter)
|
|
90
|
+
//============
|
|
91
|
+
{
|
|
92
|
+
if (this.#filter === true) {
|
|
93
|
+
this.#filter = filter
|
|
94
|
+
} else if (this.#filter !== false) {
|
|
95
|
+
const copiedFilter = Object.assign({}, filter)
|
|
96
|
+
this.#filter = { "AND": [this.#filter, copiedFilter] }
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
setFilter(filter)
|
|
101
|
+
//===============
|
|
102
|
+
{
|
|
103
|
+
if (filter.constructor !== Object) {
|
|
104
|
+
this.#filter = filter
|
|
105
|
+
} else {
|
|
106
|
+
this.#filter = Object.assign({}, filter)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
#makeStyleFilter(filter)
|
|
111
|
+
//======================
|
|
112
|
+
{
|
|
113
|
+
if (filter.constructor !== Object) {
|
|
114
|
+
return !!filter
|
|
115
|
+
}
|
|
116
|
+
const styleFilter = []
|
|
117
|
+
for (const [key, expr] of Object.entries(filter)) {
|
|
118
|
+
if (key === 'AND' || key === 'OR') {
|
|
119
|
+
if (Array.isArray(expr) && expr.length >= 2) {
|
|
120
|
+
styleFilter.push((key === 'AND') ? 'all' : 'any',
|
|
121
|
+
...expr.map(e => this.#makeStyleFilter(e)))
|
|
122
|
+
} else {
|
|
123
|
+
console.warn(`makeFilter: Invalid ${key} operands: ${expr}`)
|
|
124
|
+
}
|
|
125
|
+
} else if (key === 'HAS') {
|
|
126
|
+
styleFilter.push('has', expr)
|
|
127
|
+
} else if (key === 'NOT') {
|
|
128
|
+
const filterExpr = this.#makeStyleFilter(expr)
|
|
129
|
+
if (Array.isArray(filterExpr)) {
|
|
130
|
+
if (filterExpr.length === 2 && ['has', '!has'].includes(filterExpr[0])) {
|
|
131
|
+
if (filterExpr[0] === 'has') {
|
|
132
|
+
styleFilter.push('!has', filterExpr[1])
|
|
133
|
+
} else {
|
|
134
|
+
styleFilter.push('has', filterExpr[1])
|
|
135
|
+
}
|
|
136
|
+
} else if (filterExpr.length === 3 && ['==', '!='].includes(filterExpr[0])) {
|
|
137
|
+
if (filterExpr[0] === '==') {
|
|
138
|
+
styleFilter.push('!=', filterExpr[1], filterExpr[2])
|
|
139
|
+
} else {
|
|
140
|
+
styleFilter.push('==', filterExpr[1], filterExpr[2])
|
|
141
|
+
}
|
|
142
|
+
} else {
|
|
143
|
+
styleFilter.push('!', filterExpr)
|
|
144
|
+
}
|
|
145
|
+
} else {
|
|
146
|
+
styleFilter.push(!filterExpr)
|
|
147
|
+
}
|
|
148
|
+
} else {
|
|
149
|
+
if (Array.isArray(expr)) {
|
|
150
|
+
styleFilter.push('any', ...expr.map(e => ['==', key, e]))
|
|
151
|
+
} else {
|
|
152
|
+
styleFilter.push('==', key, expr)
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return styleFilter
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
#match(properties, filter)
|
|
160
|
+
//========================
|
|
161
|
+
{
|
|
162
|
+
if (filter.constructor !== Object) {
|
|
163
|
+
return !!filter
|
|
164
|
+
}
|
|
165
|
+
for (const [key, expr] of Object.entries(filter)) {
|
|
166
|
+
let matched = true
|
|
167
|
+
if (key === 'AND' || key === 'OR') {
|
|
168
|
+
if (Array.isArray(expr) && expr.length >= 2) {
|
|
169
|
+
const matches = expr.map(e => this.#match(properties, e))
|
|
170
|
+
matched = (key === 'AND') ? matches.reduce((result, match) => result && match, true)
|
|
171
|
+
: matches.reduce((result, match) => result || match, false)
|
|
172
|
+
} else {
|
|
173
|
+
console.warn(`makeFilter: Invalid ${key} operands: ${expr}`)
|
|
174
|
+
}
|
|
175
|
+
} else if (key === 'HAS') {
|
|
176
|
+
matched = (expr in properties)
|
|
177
|
+
} else if (key === 'NOT') {
|
|
178
|
+
matched = !this.#match(properties, expr)
|
|
179
|
+
} else if (key in properties) {
|
|
180
|
+
const value = properties[key]
|
|
181
|
+
if (Array.isArray(value)) {
|
|
182
|
+
if (Array.isArray(expr)) {
|
|
183
|
+
matched = !(new Set(value).isDisjointFrom(new Set(expr)))
|
|
184
|
+
} else {
|
|
185
|
+
matched = value.includes(expr)
|
|
186
|
+
}
|
|
187
|
+
} else if (Array.isArray(expr)) {
|
|
188
|
+
matched = expr.includes(value)
|
|
189
|
+
} else {
|
|
190
|
+
matched = (value === expr)
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (!matched) {
|
|
194
|
+
return false
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return true
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
//==============================================================================
|
|
202
|
+
|
|
203
|
+
const testProperties = {
|
|
204
|
+
prop: 1,
|
|
205
|
+
prop1: 5,
|
|
206
|
+
prop2: 11,
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function testFilter(filter)
|
|
210
|
+
//=========================
|
|
211
|
+
{
|
|
212
|
+
const featureFilter = new PropertiesFilter(filter)
|
|
213
|
+
console.log(filter, '--->', featureFilter.makeStyleFilter(), featureFilter.match(testProperties))
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function testFilters()
|
|
217
|
+
//====================
|
|
218
|
+
{
|
|
219
|
+
/*
|
|
220
|
+
{ HAS: 'prop' } ---> [ 'has', 'prop' ]
|
|
221
|
+
{ prop: 1 } ---> [ '==', 'prop', 1 ]
|
|
222
|
+
{ NOT: { prop: 1 } } ---> [ '!=', 'prop', 1 ]
|
|
223
|
+
{ NOT: { prop: [ 1, 2 ] } } ---> [ '!', [ 'any', [ '==', 'prop', 1 ], [ '==', 'prop', 2 ] ] ]
|
|
224
|
+
{ OR: [ { prop1: 10 }, { prop2: 11 } ] } ---> [ 'any', [ '==', 'prop1', 10 ], [ '==', 'prop2', 11 ] ]
|
|
225
|
+
{ AND: [ { prop1: 10 }, { prop2: 11 } ] } ---> [ 'all', [ '==', 'prop1', 10 ], [ '==', 'prop2', 11 ] ]
|
|
226
|
+
{ OR: [ { AND: [Array] }, { AND: [Array] } ] } ---> [
|
|
227
|
+
'any',
|
|
228
|
+
[ 'all', [ '!=', 'prop1', 10 ], [ '==', 'prop2', 11 ] ],
|
|
229
|
+
[ 'all', [ '==', 'prop3', 10 ], [ '==', 'prop4', 11 ] ]
|
|
230
|
+
]
|
|
231
|
+
{ NOT: { OR: [ [Object], [Object] ] } } ---> [
|
|
232
|
+
'!',
|
|
233
|
+
[ 'any', [ 'all', [Array], [Array] ], [ 'all', [Array], [Array] ] ]
|
|
234
|
+
]
|
|
235
|
+
*/
|
|
236
|
+
|
|
237
|
+
console.log('test properties', testProperties)
|
|
238
|
+
|
|
239
|
+
testFilter({
|
|
240
|
+
"HAS": "prop"
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
testFilter({
|
|
244
|
+
"prop": 1
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
testFilter({
|
|
248
|
+
"NOT": {
|
|
249
|
+
"prop": 1
|
|
250
|
+
}
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
testFilter({
|
|
254
|
+
"prop": [1, 2]
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
testFilter({
|
|
258
|
+
"NOT": {
|
|
259
|
+
"prop": [1, 2]
|
|
260
|
+
}
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
testFilter({
|
|
264
|
+
"OR": [
|
|
265
|
+
{"prop1": 10},
|
|
266
|
+
{"prop2": 11}
|
|
267
|
+
]
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
testFilter({
|
|
271
|
+
"AND": [
|
|
272
|
+
{"prop1": 10},
|
|
273
|
+
{"prop2": 11}
|
|
274
|
+
]
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
testFilter({
|
|
278
|
+
"OR": [{
|
|
279
|
+
"AND": [
|
|
280
|
+
{ "NOT": {"prop1": 10}},
|
|
281
|
+
{"prop2": 11}
|
|
282
|
+
]}, {
|
|
283
|
+
"AND": [
|
|
284
|
+
{"prop3": 10},
|
|
285
|
+
{"prop4": 11}
|
|
286
|
+
]}
|
|
287
|
+
]
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
testFilter({
|
|
291
|
+
"NOT": {
|
|
292
|
+
"OR": [{
|
|
293
|
+
"AND": [
|
|
294
|
+
{"prop1": 10},
|
|
295
|
+
{"prop2": 11}
|
|
296
|
+
]}, {
|
|
297
|
+
"AND": [
|
|
298
|
+
{"prop3": 10},
|
|
299
|
+
{"prop4": 11}
|
|
300
|
+
]}
|
|
301
|
+
]
|
|
302
|
+
}
|
|
303
|
+
})
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
//==============================================================================
|
|
307
|
+
|
|
308
|
+
//testFilters()
|
|
309
|
+
|
|
310
|
+
//==============================================================================
|
package/src/layers/index.js
CHANGED
|
@@ -27,6 +27,9 @@ import * as utils from '../utils.js';
|
|
|
27
27
|
|
|
28
28
|
import * as style from './styling.js';
|
|
29
29
|
|
|
30
|
+
import {Paths3DLayer} from './paths3d'
|
|
31
|
+
import {PropertiesFilter} from './filter'
|
|
32
|
+
|
|
30
33
|
const FEATURES_LAYER = 'features';
|
|
31
34
|
const RASTER_LAYERS_NAME = 'Background image layer';
|
|
32
35
|
const RASTER_LAYERS_ID = 'background-image-layer';
|
|
@@ -64,6 +67,12 @@ class MapStylingLayers
|
|
|
64
67
|
return this.__active;
|
|
65
68
|
}
|
|
66
69
|
|
|
70
|
+
get map()
|
|
71
|
+
//=======
|
|
72
|
+
{
|
|
73
|
+
return this.__map
|
|
74
|
+
}
|
|
75
|
+
|
|
67
76
|
addLayer(styleLayer, options)
|
|
68
77
|
//===========================
|
|
69
78
|
{
|
|
@@ -106,6 +115,8 @@ class MapStylingLayers
|
|
|
106
115
|
|
|
107
116
|
class MapFeatureLayers extends MapStylingLayers
|
|
108
117
|
{
|
|
118
|
+
#pathLayers = []
|
|
119
|
+
|
|
109
120
|
constructor(flatmap, layer, options)
|
|
110
121
|
{
|
|
111
122
|
super(flatmap, layer, options);
|
|
@@ -137,35 +148,46 @@ class MapFeatureLayers extends MapStylingLayers
|
|
|
137
148
|
this.setPaint(this.__layerOptions);
|
|
138
149
|
}
|
|
139
150
|
|
|
140
|
-
__addStyleLayer(styleClass, sourceLayer=FEATURES_LAYER)
|
|
141
|
-
|
|
151
|
+
__addStyleLayer(styleClass, sourceLayer=FEATURES_LAYER, path2dLayer=false)
|
|
152
|
+
//========================================================================
|
|
142
153
|
{
|
|
143
154
|
const styleLayer = new styleClass(`${this.__id}_${sourceLayer}`,
|
|
144
|
-
this.vectorSourceId(sourceLayer))
|
|
145
|
-
this.addLayer(styleLayer, this.__layerOptions)
|
|
155
|
+
this.vectorSourceId(sourceLayer))
|
|
156
|
+
this.addLayer(styleLayer, this.__layerOptions)
|
|
157
|
+
if (path2dLayer) {
|
|
158
|
+
this.#pathLayers.push(styleLayer)
|
|
159
|
+
}
|
|
146
160
|
}
|
|
147
161
|
|
|
148
162
|
__addPathwayStyleLayers()
|
|
149
163
|
//=======================
|
|
150
164
|
{
|
|
151
|
-
const pathwaysVectorSource = this.vectorSourceId(PATHWAYS_LAYER)
|
|
165
|
+
const pathwaysVectorSource = this.vectorSourceId(PATHWAYS_LAYER)
|
|
152
166
|
if (this.__map.getSource('vector-tiles')
|
|
153
167
|
.vectorLayerIds
|
|
154
168
|
.includes(pathwaysVectorSource)) {
|
|
155
|
-
this.__addStyleLayer(style.AnnotatedPathLayer, PATHWAYS_LAYER)
|
|
169
|
+
this.__addStyleLayer(style.AnnotatedPathLayer, PATHWAYS_LAYER, true)
|
|
156
170
|
|
|
157
|
-
this.__addStyleLayer(style.CentrelineEdgeLayer, PATHWAYS_LAYER)
|
|
158
|
-
this.__addStyleLayer(style.CentrelineTrackLayer, PATHWAYS_LAYER)
|
|
171
|
+
this.__addStyleLayer(style.CentrelineEdgeLayer, PATHWAYS_LAYER)
|
|
172
|
+
this.__addStyleLayer(style.CentrelineTrackLayer, PATHWAYS_LAYER)
|
|
159
173
|
|
|
160
|
-
this.__addStyleLayer(style.PathLineLayer, PATHWAYS_LAYER)
|
|
161
|
-
this.__addStyleLayer(style.PathDashlineLayer, PATHWAYS_LAYER)
|
|
174
|
+
this.__addStyleLayer(style.PathLineLayer, PATHWAYS_LAYER, true)
|
|
175
|
+
this.__addStyleLayer(style.PathDashlineLayer, PATHWAYS_LAYER, true)
|
|
162
176
|
|
|
163
|
-
this.__addStyleLayer(style.NervePolygonBorder, PATHWAYS_LAYER)
|
|
164
|
-
this.__addStyleLayer(style.NervePolygonFill, PATHWAYS_LAYER)
|
|
165
|
-
this.__addStyleLayer(style.FeatureNerveLayer, PATHWAYS_LAYER)
|
|
177
|
+
this.__addStyleLayer(style.NervePolygonBorder, PATHWAYS_LAYER, true)
|
|
178
|
+
this.__addStyleLayer(style.NervePolygonFill, PATHWAYS_LAYER, true)
|
|
179
|
+
this.__addStyleLayer(style.FeatureNerveLayer, PATHWAYS_LAYER, true)
|
|
166
180
|
|
|
167
|
-
this.__addStyleLayer(style.PathHighlightLayer, PATHWAYS_LAYER)
|
|
168
|
-
this.__addStyleLayer(style.PathDashHighlightLayer, PATHWAYS_LAYER)
|
|
181
|
+
this.__addStyleLayer(style.PathHighlightLayer, PATHWAYS_LAYER, true)
|
|
182
|
+
this.__addStyleLayer(style.PathDashHighlightLayer, PATHWAYS_LAYER, true)
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
enablePaths2dLayer(visible)
|
|
187
|
+
//=========================
|
|
188
|
+
{
|
|
189
|
+
for (const layer of this.#pathLayers) {
|
|
190
|
+
this.map.setLayoutProperty(layer.id, 'visibility', visible ? 'visible' : 'none')
|
|
169
191
|
}
|
|
170
192
|
}
|
|
171
193
|
|
|
@@ -243,11 +265,14 @@ class MapRasterLayers extends MapStylingLayers
|
|
|
243
265
|
|
|
244
266
|
export class LayerManager
|
|
245
267
|
{
|
|
246
|
-
|
|
268
|
+
#featureLayers = new Map()
|
|
269
|
+
#paths3dLayer = null
|
|
270
|
+
#rasterLayer = null
|
|
271
|
+
|
|
272
|
+
constructor(flatmap, ui)
|
|
247
273
|
{
|
|
248
274
|
this.__flatmap = flatmap;
|
|
249
275
|
this.__map = flatmap.map;
|
|
250
|
-
this.__mapLayers = new Map;
|
|
251
276
|
this.__layerOptions = utils.setDefaults(flatmap.options.layerOptions, {
|
|
252
277
|
colour: true,
|
|
253
278
|
outline: true,
|
|
@@ -266,28 +291,37 @@ export class LayerManager
|
|
|
266
291
|
|
|
267
292
|
// Image layers are below all feature layers
|
|
268
293
|
const bodyLayer = flatmap.layers[0];
|
|
269
|
-
|
|
294
|
+
this.#rasterLayer = new MapRasterLayers(this.__flatmap,
|
|
270
295
|
this.__layerOptions,
|
|
271
296
|
bodyLayer.id); // body layer if not FC??
|
|
272
297
|
for (const layer of flatmap.layers) {
|
|
273
|
-
|
|
298
|
+
this.#rasterLayer.addLayer(layer);
|
|
274
299
|
}
|
|
275
|
-
this.__mapLayers.set(RASTER_LAYERS_ID, rasterLayers);
|
|
276
300
|
} else {
|
|
277
301
|
this.__layerOptions.activeRasterLayer = false;
|
|
278
302
|
}
|
|
279
303
|
for (const layer of flatmap.layers) {
|
|
280
|
-
this.
|
|
281
|
-
|
|
282
|
-
|
|
304
|
+
this.#featureLayers.set(layer.id, new MapFeatureLayers(this.__flatmap,
|
|
305
|
+
layer,
|
|
306
|
+
this.__layerOptions));
|
|
283
307
|
}
|
|
308
|
+
|
|
309
|
+
// Support 3D path view
|
|
310
|
+
this.#paths3dLayer = new Paths3DLayer(flatmap, ui)
|
|
284
311
|
}
|
|
285
312
|
|
|
286
313
|
get layers()
|
|
287
314
|
//==========
|
|
288
315
|
{
|
|
289
|
-
const layers = []
|
|
290
|
-
|
|
316
|
+
const layers = []
|
|
317
|
+
if (this.#rasterLayer) {
|
|
318
|
+
layers.push({
|
|
319
|
+
id: this.#rasterLayer.id,
|
|
320
|
+
description: this.#rasterLayer.description,
|
|
321
|
+
enabled: this.#rasterLayer.active
|
|
322
|
+
})
|
|
323
|
+
}
|
|
324
|
+
for (const mapLayer of this.#featureLayers.values()) {
|
|
291
325
|
layers.push({
|
|
292
326
|
id: mapLayer.id,
|
|
293
327
|
description: mapLayer.description,
|
|
@@ -306,26 +340,63 @@ export class LayerManager
|
|
|
306
340
|
activate(layerId, enable=true)
|
|
307
341
|
//============================
|
|
308
342
|
{
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
this.
|
|
314
|
-
|
|
315
|
-
if (mapLayer.id !== RASTER_LAYERS_ID) {
|
|
316
|
-
mapLayer.setPaint(this.__layerOptions);
|
|
317
|
-
}
|
|
343
|
+
if (layerId === RASTER_LAYERS_ID) {
|
|
344
|
+
if (this.#rasterLayer) {
|
|
345
|
+
this.#rasterLayer.activate(enable)
|
|
346
|
+
this.__layerOptions.activeRasterLayer = enable
|
|
347
|
+
for (const mapLayer of this.#featureLayers.values()) {
|
|
348
|
+
mapLayer.setPaint(this.__layerOptions)
|
|
318
349
|
}
|
|
319
350
|
}
|
|
351
|
+
} else {
|
|
352
|
+
const layer = this.#featureLayers.get(layerId)
|
|
353
|
+
if (layer) {
|
|
354
|
+
layer.activate(enable)
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
featuresAtPoint(point)
|
|
360
|
+
//====================
|
|
361
|
+
{
|
|
362
|
+
let features = []
|
|
363
|
+
if (this.#paths3dLayer) {
|
|
364
|
+
features = this.#paths3dLayer.queryFeaturesAtPoint(point)
|
|
365
|
+
}
|
|
366
|
+
if (features.length === 0) {
|
|
367
|
+
features = this.__map.queryRenderedFeatures(point)
|
|
368
|
+
}
|
|
369
|
+
return features
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
removeFeatureState(feature, key)
|
|
373
|
+
//==============================
|
|
374
|
+
{
|
|
375
|
+
if (this.#paths3dLayer) {
|
|
376
|
+
this.#paths3dLayer.removeFeatureState(feature.id, key)
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
setFeatureState(feature, state)
|
|
381
|
+
//=============================
|
|
382
|
+
{
|
|
383
|
+
if (this.#paths3dLayer) {
|
|
384
|
+
this.#paths3dLayer.setFeatureState(feature.id, state)
|
|
320
385
|
}
|
|
321
386
|
}
|
|
322
387
|
|
|
323
388
|
setPaint(options={})
|
|
324
389
|
//==================
|
|
325
390
|
{
|
|
326
|
-
this.__layerOptions = utils.setDefaults(options, this.__layerOptions)
|
|
327
|
-
|
|
328
|
-
|
|
391
|
+
this.__layerOptions = utils.setDefaults(options, this.__layerOptions)
|
|
392
|
+
if (this.#rasterLayer) {
|
|
393
|
+
this.#rasterLayer.setPaint(this.__layerOptions)
|
|
394
|
+
}
|
|
395
|
+
for (const mapLayer of this.#featureLayers.values()) {
|
|
396
|
+
mapLayer.setPaint(this.__layerOptions)
|
|
397
|
+
}
|
|
398
|
+
if (this.#paths3dLayer) {
|
|
399
|
+
this.#paths3dLayer.setPaint(options)
|
|
329
400
|
}
|
|
330
401
|
}
|
|
331
402
|
|
|
@@ -333,9 +404,32 @@ export class LayerManager
|
|
|
333
404
|
//===================
|
|
334
405
|
{
|
|
335
406
|
this.__layerOptions = utils.setDefaults(options, this.__layerOptions);
|
|
336
|
-
for (const mapLayer of this.
|
|
407
|
+
for (const mapLayer of this.#featureLayers.values()) {
|
|
337
408
|
mapLayer.setFilter(this.__layerOptions);
|
|
338
409
|
}
|
|
410
|
+
if (this.#paths3dLayer) {
|
|
411
|
+
const sckanState = options.sckan || 'valid'
|
|
412
|
+
const sckanFilter = (sckanState == 'none') ? {NOT: {HAS: 'sckan'}} :
|
|
413
|
+
(sckanState == 'valid') ? {sckan: true} :
|
|
414
|
+
(sckanState == 'invalid') ? {NOT: {sckan: true}} :
|
|
415
|
+
true
|
|
416
|
+
const featureFilter = new PropertiesFilter(sckanFilter)
|
|
417
|
+
if ('taxons' in options) {
|
|
418
|
+
featureFilter.narrow({taxons: options.taxons})
|
|
419
|
+
}
|
|
420
|
+
this.#paths3dLayer.setFilter(featureFilter)
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
set3dMode(enable=true)
|
|
425
|
+
//====================
|
|
426
|
+
{
|
|
427
|
+
if (this.#paths3dLayer) {
|
|
428
|
+
this.#paths3dLayer.enable(enable)
|
|
429
|
+
for (const mapLayer of this.#featureLayers.values()) {
|
|
430
|
+
mapLayer.enablePaths2dLayer(!enable)
|
|
431
|
+
}
|
|
432
|
+
}
|
|
339
433
|
}
|
|
340
434
|
|
|
341
435
|
enableSckanPaths(sckanState, enable=true)
|
package/src/layers/paths3d.js
CHANGED
|
@@ -26,6 +26,7 @@ import GL from '@luma.gl/constants'
|
|
|
26
26
|
//==============================================================================
|
|
27
27
|
|
|
28
28
|
import {pathColourArray} from '../pathways'
|
|
29
|
+
import {PropertiesFilter} from './filter'
|
|
29
30
|
|
|
30
31
|
//==============================================================================
|
|
31
32
|
|
|
@@ -60,6 +61,7 @@ class ArcMapLayer extends ArcLayer
|
|
|
60
61
|
shaders.vs = `#version 300 es\n${shaders.vs}`
|
|
61
62
|
return shaders
|
|
62
63
|
}
|
|
64
|
+
|
|
63
65
|
setDataProperty(featureId, key, enabled)
|
|
64
66
|
//======================================
|
|
65
67
|
{
|
|
@@ -139,10 +141,12 @@ export class Paths3DLayer
|
|
|
139
141
|
#deckOverlay = null
|
|
140
142
|
#dimmed = false
|
|
141
143
|
#enabled = false
|
|
144
|
+
#featureFilter = new PropertiesFilter()
|
|
142
145
|
#featureToLayer = new Map()
|
|
143
146
|
#knownTypes = []
|
|
144
147
|
#map
|
|
145
148
|
#pathData
|
|
149
|
+
#pathFilters
|
|
146
150
|
#pathManager
|
|
147
151
|
#pathStyles
|
|
148
152
|
#ui
|
|
@@ -162,7 +166,27 @@ export class Paths3DLayer
|
|
|
162
166
|
&& 'pathEndPosition' in ann)
|
|
163
167
|
.map(ann => [ann.featureId, ann]))
|
|
164
168
|
this.#pathStyles = new Map(this.#pathManager.pathStyles().map(s => [s.type, s]))
|
|
165
|
-
|
|
169
|
+
const knownTypes = [...this.#pathStyles.keys()].filter(t => t !== 'other')
|
|
170
|
+
this.#pathFilters = new Map(
|
|
171
|
+
[...this.#pathStyles.keys()]
|
|
172
|
+
.map(pathType => [pathType, new PropertiesFilter({
|
|
173
|
+
OR: [{
|
|
174
|
+
AND: [
|
|
175
|
+
{kind: knownTypes},
|
|
176
|
+
{kind: pathType}
|
|
177
|
+
],
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
AND: [
|
|
181
|
+
{
|
|
182
|
+
NOT: {kind: knownTypes}
|
|
183
|
+
},
|
|
184
|
+
(pathType === 'other')
|
|
185
|
+
]
|
|
186
|
+
}]
|
|
187
|
+
})
|
|
188
|
+
])
|
|
189
|
+
)
|
|
166
190
|
}
|
|
167
191
|
|
|
168
192
|
enable(enable=true)
|
|
@@ -223,6 +247,30 @@ export class Paths3DLayer
|
|
|
223
247
|
}
|
|
224
248
|
}
|
|
225
249
|
|
|
250
|
+
setFilter(featureFilter)
|
|
251
|
+
//======================
|
|
252
|
+
{
|
|
253
|
+
this.#featureFilter = featureFilter
|
|
254
|
+
if (this.#deckOverlay) {
|
|
255
|
+
const updatedLayers = new Map()
|
|
256
|
+
for (const [pathType, layer] of this.#arcLayers.entries()) {
|
|
257
|
+
layer.featureIds.forEach(id => this.#featureToLayer.delete(+id))
|
|
258
|
+
const pathStyle = this.#pathStyles.get(pathType)
|
|
259
|
+
if (pathStyle) {
|
|
260
|
+
const updatedLayer = pathStyle.dashed
|
|
261
|
+
? new ArcDashedLayer(this.#layerOptions(pathType))
|
|
262
|
+
: new ArcMapLayer(this.#layerOptions(pathType))
|
|
263
|
+
updatedLayer.featureIds.forEach(id => this.#featureToLayer.set(+id, layer))
|
|
264
|
+
updatedLayers.set(pathType, updatedLayer)
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
this.#arcLayers = updatedLayers
|
|
268
|
+
this.#deckOverlay.setProps({
|
|
269
|
+
layers: [...this.#arcLayers.values()]
|
|
270
|
+
})
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
226
274
|
setPaint(options)
|
|
227
275
|
//===============
|
|
228
276
|
{
|
|
@@ -236,11 +284,14 @@ export class Paths3DLayer
|
|
|
236
284
|
#addArcLayer(pathType)
|
|
237
285
|
//====================
|
|
238
286
|
{
|
|
239
|
-
const
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
287
|
+
const pathStyle = this.#pathStyles.get(pathType)
|
|
288
|
+
if (pathStyle) {
|
|
289
|
+
const layer = pathStyle.dashed
|
|
290
|
+
? new ArcDashedLayer(this.#layerOptions(pathType))
|
|
291
|
+
: new ArcMapLayer(this.#layerOptions(pathType))
|
|
292
|
+
layer.featureIds.forEach(id => this.#featureToLayer.set(+id, layer))
|
|
293
|
+
this.#arcLayers.set(pathType, layer)
|
|
294
|
+
}
|
|
244
295
|
}
|
|
245
296
|
|
|
246
297
|
#removeArcLayer(pathType)
|
|
@@ -283,13 +334,12 @@ export class Paths3DLayer
|
|
|
283
334
|
}
|
|
284
335
|
}
|
|
285
336
|
|
|
286
|
-
|
|
287
337
|
#layerOptions(pathType)
|
|
288
338
|
//=====================
|
|
289
339
|
{
|
|
290
|
-
const
|
|
291
|
-
|
|
292
|
-
|
|
340
|
+
const filter = this.#pathFilters.get(pathType)
|
|
341
|
+
const pathData = (filter ? [...this.#pathData.values()].filter(ann => filter.match(ann))
|
|
342
|
+
: []).filter(ann => this.#featureFilter.match(ann))
|
|
293
343
|
return {
|
|
294
344
|
id: `arc-${pathType}`,
|
|
295
345
|
data: pathData,
|