@abi-software/flatmap-viewer 2.2.1-beta.9 → 2.2.2-beta.2
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 -1
- package/src/controls.js +42 -50
- package/src/editor.js +198 -0
- package/src/flatmap-viewer.js +17 -2
- package/src/interactions.js +9 -7
- package/src/styling.js +1 -1
- package/static/flatmap-viewer.css +13 -3
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.2.
|
|
41
|
+
* ``npm install @abi-software/flatmap-viewer@2.2.2-beta.2``
|
|
42
42
|
|
|
43
43
|
Documentation
|
|
44
44
|
-------------
|
package/package.json
CHANGED
package/src/controls.js
CHANGED
|
@@ -75,7 +75,7 @@ export class NavigationControl
|
|
|
75
75
|
|
|
76
76
|
//==============================================================================
|
|
77
77
|
|
|
78
|
-
export class
|
|
78
|
+
export class PathControl
|
|
79
79
|
{
|
|
80
80
|
constructor(flatmap)
|
|
81
81
|
{
|
|
@@ -102,10 +102,13 @@ export class NerveKey
|
|
|
102
102
|
this._legend.className = 'flatmap-nerve-grid';
|
|
103
103
|
|
|
104
104
|
const innerHTML = [];
|
|
105
|
+
innerHTML.push(`<label for="path-all-paths">ALL PATHS:</label><input id="path-all-paths" type="checkbox" checked/><div class="nerve-line"></div>`);
|
|
105
106
|
for (const path of pathways.PATH_TYPES) {
|
|
106
|
-
innerHTML.push(`<
|
|
107
|
+
innerHTML.push(`<label for="path-${path.type}">${path.label}</label><input id="path-${path.type}" type="checkbox" checked/><div class="nerve-line nerve-${path.type}"></div>`);
|
|
107
108
|
}
|
|
108
109
|
this._legend.innerHTML = innerHTML.join('\n');
|
|
110
|
+
this.__checkedCount = pathways.PATH_TYPES.length;
|
|
111
|
+
this.__halfCount = Math.trunc(this.__checkedCount/2);
|
|
109
112
|
|
|
110
113
|
this._button = document.createElement('button');
|
|
111
114
|
this._button.id = 'nerve-key-button';
|
|
@@ -114,7 +117,7 @@ export class NerveKey
|
|
|
114
117
|
this._button.setAttribute('type', 'button');
|
|
115
118
|
this._button.setAttribute('aria-label', 'Nerve paths legend');
|
|
116
119
|
this._button.setAttribute('legend-visible', 'false');
|
|
117
|
-
this._button.textContent = '
|
|
120
|
+
this._button.textContent = 'PTH';
|
|
118
121
|
this._container.appendChild(this._button);
|
|
119
122
|
|
|
120
123
|
this._container.addEventListener('click', this.onClick_.bind(this));
|
|
@@ -140,10 +143,42 @@ export class NerveKey
|
|
|
140
143
|
this._legend = this._container.removeChild(this._legend);
|
|
141
144
|
this._button.setAttribute('legend-visible', 'false');
|
|
142
145
|
}
|
|
143
|
-
} else {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
146
|
+
} else if (event.target.tagName === 'INPUT') {
|
|
147
|
+
if (event.target.id === 'path-all-paths') {
|
|
148
|
+
if (event.target.indeterminate) {
|
|
149
|
+
event.target.checked = (this.__checkedCount >= this.__halfCount);
|
|
150
|
+
event.target.indeterminate = false;
|
|
151
|
+
}
|
|
152
|
+
if (event.target.checked) {
|
|
153
|
+
this.__checkedCount = pathways.PATH_TYPES.length;
|
|
154
|
+
} else {
|
|
155
|
+
this.__checkedCount = 0;
|
|
156
|
+
}
|
|
157
|
+
for (const path of pathways.PATH_TYPES) {
|
|
158
|
+
const pathCheckbox = document.getElementById(`path-${path.type}`);
|
|
159
|
+
if (pathCheckbox) {
|
|
160
|
+
pathCheckbox.checked = event.target.checked;
|
|
161
|
+
this._flatmap.enablePath(path.type, event.target.checked);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
} else if (event.target.id.startsWith('path-')) {
|
|
165
|
+
const pathType = event.target.id.substring(5);
|
|
166
|
+
this._flatmap.enablePath(pathType, event.target.checked);
|
|
167
|
+
if (event.target.checked) {
|
|
168
|
+
this.__checkedCount += 1;
|
|
169
|
+
} else {
|
|
170
|
+
this.__checkedCount -= 1;
|
|
171
|
+
}
|
|
172
|
+
const allPathsCheckbox = document.getElementById('path-all-paths');
|
|
173
|
+
if (this.__checkedCount === 0) {
|
|
174
|
+
allPathsCheckbox.checked = false;
|
|
175
|
+
allPathsCheckbox.indeterminate = false;
|
|
176
|
+
} else if (this.__checkedCount === pathways.PATH_TYPES.length) {
|
|
177
|
+
allPathsCheckbox.checked = true;
|
|
178
|
+
allPathsCheckbox.indeterminate = false;
|
|
179
|
+
} else {
|
|
180
|
+
allPathsCheckbox.indeterminate = true;
|
|
181
|
+
}
|
|
147
182
|
}
|
|
148
183
|
}
|
|
149
184
|
event.stopPropagation();
|
|
@@ -151,46 +186,3 @@ export class NerveKey
|
|
|
151
186
|
}
|
|
152
187
|
|
|
153
188
|
//==============================================================================
|
|
154
|
-
|
|
155
|
-
export class PathControl
|
|
156
|
-
{
|
|
157
|
-
constructor(flatmap)
|
|
158
|
-
{
|
|
159
|
-
this._flatmap = flatmap;
|
|
160
|
-
this._map = undefined;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
getDefaultPosition()
|
|
164
|
-
//==================
|
|
165
|
-
{
|
|
166
|
-
return 'top-right';
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
onAdd(map)
|
|
170
|
-
//========
|
|
171
|
-
{
|
|
172
|
-
this._map = map;
|
|
173
|
-
this._container = document.createElement('div');
|
|
174
|
-
this._container.className = 'maplibregl-ctrl';
|
|
175
|
-
this._container.id = 'flatmap-path-control';
|
|
176
|
-
this._container.innerHTML = `<button class="control-button" id="path-control-button"
|
|
177
|
-
type="button" title="Show/hide paths" aria-label="Show/hide paths">PTH</button>`;
|
|
178
|
-
this._container.onclick = this.onClick_.bind(this);
|
|
179
|
-
return this._container;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
onRemove()
|
|
183
|
-
//========
|
|
184
|
-
{
|
|
185
|
-
this._container.parentNode.removeChild(this._container);
|
|
186
|
-
this._map = undefined;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
onClick_(event)
|
|
190
|
-
//=============
|
|
191
|
-
{
|
|
192
|
-
this._flatmap.togglePaths();
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
//==============================================================================
|
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
|
+
}
|
package/src/flatmap-viewer.js
CHANGED
|
@@ -283,12 +283,27 @@ class FlatMap
|
|
|
283
283
|
return pathways.PATH_TYPES;
|
|
284
284
|
}
|
|
285
285
|
|
|
286
|
+
/**
|
|
287
|
+
* Hide or show paths of a given type.
|
|
288
|
+
*
|
|
289
|
+
* @param {string} pathType The path type
|
|
290
|
+
* @param {boolean} [enable=true] If ``true`` then only show the paths
|
|
291
|
+
* of the type otherwise only hide the paths
|
|
292
|
+
*/
|
|
293
|
+
enablePath(pathType, enable=true)
|
|
294
|
+
//===============================
|
|
295
|
+
{
|
|
296
|
+
if (this._userInteractions !== null) {
|
|
297
|
+
this._userInteractions.enablePath(pathType, enable);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
286
301
|
/**
|
|
287
302
|
* Hide or show all paths except those of the given type.
|
|
288
303
|
*
|
|
289
304
|
* @param {string|Array.<string>} pathTypes The path type(s)
|
|
290
|
-
* @param {boolean} [enable=true] If ``true`` then only show the
|
|
291
|
-
* type(s) otherwise only hide the
|
|
305
|
+
* @param {boolean} [enable=true] If ``true`` then only show the paths
|
|
306
|
+
* of the type(s) otherwise only hide the paths
|
|
292
307
|
*/
|
|
293
308
|
showPaths(pathTypes, enable=true)
|
|
294
309
|
//===============================
|
package/src/interactions.js
CHANGED
|
@@ -37,7 +37,7 @@ import {displayedProperties} from './info.js';
|
|
|
37
37
|
import {InfoControl} from './info.js';
|
|
38
38
|
import {LayerManager} from './layers.js';
|
|
39
39
|
import {PATHWAYS_LAYER, Pathways} from './pathways.js';
|
|
40
|
-
import {
|
|
40
|
+
import {PathControl} from './controls.js';
|
|
41
41
|
import {SearchControl} from './search.js';
|
|
42
42
|
import {VECTOR_TILES_SOURCE} from './styling.js';
|
|
43
43
|
|
|
@@ -152,10 +152,6 @@ export class UserInteractions
|
|
|
152
152
|
// Add controls to manage our pathways
|
|
153
153
|
|
|
154
154
|
this._map.addControl(new PathControl(flatmap));
|
|
155
|
-
|
|
156
|
-
// Add a key showing nerve types
|
|
157
|
-
|
|
158
|
-
this._map.addControl(new NerveKey(flatmap));
|
|
159
155
|
}
|
|
160
156
|
|
|
161
157
|
// Manage our layers
|
|
@@ -919,6 +915,12 @@ export class UserInteractions
|
|
|
919
915
|
}
|
|
920
916
|
}
|
|
921
917
|
|
|
918
|
+
enablePath(pathType, enable=true)
|
|
919
|
+
//===============================
|
|
920
|
+
{
|
|
921
|
+
this.enablePathFeatures_(enable, this._pathways.typeFeatureIds(pathType));
|
|
922
|
+
}
|
|
923
|
+
|
|
922
924
|
showPaths(pathTypes, enable=true)
|
|
923
925
|
//===============================
|
|
924
926
|
{
|
|
@@ -928,10 +930,10 @@ export class UserInteractions
|
|
|
928
930
|
|
|
929
931
|
if (Array.isArray(pathTypes)) {
|
|
930
932
|
for (const pathType of pathTypes) {
|
|
931
|
-
this.
|
|
933
|
+
this.enablePath(pathType, enable);
|
|
932
934
|
}
|
|
933
935
|
} else {
|
|
934
|
-
this.
|
|
936
|
+
this.enablePath(pathTypes, enable);
|
|
935
937
|
}
|
|
936
938
|
|
|
937
939
|
this._disabledPathFeatures = true;
|
package/src/styling.js
CHANGED
|
@@ -263,7 +263,7 @@ export class FeatureLineLayer extends VectorStyleLayer
|
|
|
263
263
|
'case',
|
|
264
264
|
['boolean', ['feature-state', 'selected'], false], '#0F0',
|
|
265
265
|
['has', 'colour'], ['get', 'colour'],
|
|
266
|
-
['boolean', ['feature-state', 'active'], false], coloured ? '#
|
|
266
|
+
['boolean', ['feature-state', 'active'], false], coloured ? '#888' : '#CCC',
|
|
267
267
|
['==', ['get', 'type'], 'network'], '#AFA202',
|
|
268
268
|
['has', 'centreline'], '#888',
|
|
269
269
|
('style' in options && options.style === 'authoring') ? '#C44' : '#444'
|
|
@@ -176,18 +176,28 @@ li.flatmap-contextmenu-item:hover {
|
|
|
176
176
|
.flatmap-nerve-grid {
|
|
177
177
|
margin-top: 10px;
|
|
178
178
|
display: grid;
|
|
179
|
-
grid-template-columns:
|
|
179
|
+
grid-template-columns: 3fr 0.2fr 0.8fr;
|
|
180
180
|
column-gap: 10px;
|
|
181
181
|
row-gap: 0.2em;
|
|
182
182
|
width: 300px;
|
|
183
183
|
font-size: 10pt;
|
|
184
184
|
cursor: pointer;
|
|
185
185
|
text-align: left;
|
|
186
|
+
background: white;
|
|
187
|
+
border: 1px solid gray;
|
|
188
|
+
padding: 4px;
|
|
189
|
+
opacity: 0.8;
|
|
186
190
|
}
|
|
187
|
-
.nerve-
|
|
188
|
-
|
|
191
|
+
.flatmap-nerve-grid input {
|
|
192
|
+
height: 1.1em;
|
|
193
|
+
}
|
|
194
|
+
.flatmap-nerve-grid .nerve-line {
|
|
195
|
+
margin: 8px 0;
|
|
189
196
|
height: 3px;
|
|
190
197
|
}
|
|
198
|
+
label[for=path-all-paths] {
|
|
199
|
+
font-weight: bold;
|
|
200
|
+
}
|
|
191
201
|
.nerve-cns {
|
|
192
202
|
background: #9B1FC1;
|
|
193
203
|
}
|