gridviz 3.0.33 → 3.0.35
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/dist/gridviz.js +1128 -959
- package/dist/gridviz.min.js +1 -1
- package/package.json +1 -1
- package/src/core/Map.js +19 -3
- package/src/layer/GeoJSONLayer.js +132 -80
package/package.json
CHANGED
package/src/core/Map.js
CHANGED
|
@@ -90,6 +90,12 @@ export class Map {
|
|
|
90
90
|
* @type {Tooltip} */
|
|
91
91
|
this.tooltip = new Tooltip(opts.tooltip)
|
|
92
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Set to true so that the tooltip appears for the first layer starting from top where a cell is found.
|
|
95
|
+
* Otherwise, it shows a tooltip only for the first layer from the top with a cellInfoHTML property, even if there is no cell at the mouse position for this layer.
|
|
96
|
+
* @type {boolean} */
|
|
97
|
+
this.digForTooltip = opts.digForTooltip || false
|
|
98
|
+
|
|
93
99
|
// add event listeners to container
|
|
94
100
|
this.mouseOverHandler = (e) => this.focusCell(e)
|
|
95
101
|
this.mouseMoveHandler = (e) => this.focusCell(e)
|
|
@@ -330,15 +336,20 @@ export class Map {
|
|
|
330
336
|
* @protected
|
|
331
337
|
*/
|
|
332
338
|
getCellFocusInfo(posGeo) {
|
|
333
|
-
|
|
339
|
+
|
|
340
|
+
// go through the layers, starting from top one
|
|
334
341
|
const z = this.geoCanvas.view.z
|
|
335
342
|
for (let i = this.layers.length - 1; i >= 0; i--) {
|
|
343
|
+
|
|
336
344
|
/** @type {import("./Layer.js").Layer} */
|
|
337
345
|
const layer = this.layers[i]
|
|
346
|
+
|
|
338
347
|
if (layer.visible && !layer.visible(z)) continue
|
|
339
|
-
if (layer.cellInfoHTML === 'none') continue
|
|
348
|
+
if (layer.cellInfoHTML === 'none') continue
|
|
340
349
|
if (!layer.cellInfoHTML) continue
|
|
341
350
|
if (!layer.getDataset) continue
|
|
351
|
+
|
|
352
|
+
// get dataset
|
|
342
353
|
const dsc = layer.getDataset(z)
|
|
343
354
|
if (!dsc) continue
|
|
344
355
|
|
|
@@ -346,7 +357,12 @@ export class Map {
|
|
|
346
357
|
/** @type {import('./Dataset.js').Cell|undefined} */
|
|
347
358
|
const cell = dsc.getCellFromPosition(posGeo, dsc.getViewCache())
|
|
348
359
|
|
|
349
|
-
|
|
360
|
+
// no cell found at theis position for this layer
|
|
361
|
+
if (!cell)
|
|
362
|
+
// dig to the next layer below
|
|
363
|
+
if (this.digForTooltip) continue
|
|
364
|
+
// no tooltip to show
|
|
365
|
+
else return undefined
|
|
350
366
|
|
|
351
367
|
//rare case for a dataset with mixed resolutions
|
|
352
368
|
if (dsc.mixedResolution) {
|
|
@@ -30,6 +30,7 @@ export class GeoJSONLayer extends Layer {
|
|
|
30
30
|
this.preprocess = opts.preprocess
|
|
31
31
|
|
|
32
32
|
//for points
|
|
33
|
+
|
|
33
34
|
/**
|
|
34
35
|
* @private
|
|
35
36
|
* @type {function(object,number):string} */
|
|
@@ -39,36 +40,32 @@ export class GeoJSONLayer extends Layer {
|
|
|
39
40
|
* @private
|
|
40
41
|
* @type {function(object,number):number} */
|
|
41
42
|
this.size = opts.size || ((f, z) => 10)
|
|
42
|
-
/**
|
|
43
|
-
* @private
|
|
44
|
-
* @type {function(object,number):string} */
|
|
45
|
-
this.strokeStyle = opts.strokeStyle || ((f, z) => 'red')
|
|
46
|
-
/**
|
|
47
|
-
* @private
|
|
48
|
-
* @type {function(object,number):string} */
|
|
49
|
-
this.fillStyle = opts.fillStyle || ((f, z) => 'black')
|
|
50
|
-
/**
|
|
51
|
-
* In pixel
|
|
52
|
-
* @private
|
|
53
|
-
* @type {function(object,number):number} */
|
|
54
|
-
this.lineWidth = opts.lineWidth || ((f, z) => 2)
|
|
55
43
|
|
|
56
|
-
//for lines
|
|
44
|
+
// for lines and polygon outlines
|
|
57
45
|
|
|
58
46
|
/**
|
|
59
47
|
* @private
|
|
60
48
|
* @type {function(object,number):string} */
|
|
61
|
-
this.
|
|
49
|
+
this.strokeStyle = opts.strokeStyle || opts.color || ((f, z) => 'gray')
|
|
62
50
|
/**
|
|
63
51
|
* In pixel
|
|
64
52
|
* @private
|
|
65
53
|
* @type {function(object,number):number} */
|
|
66
|
-
this.
|
|
54
|
+
this.lineWidth = opts.lineWidth || opts.width || ((f, z) => 2)
|
|
67
55
|
/**
|
|
68
56
|
* @private
|
|
69
57
|
* @type {function(object,number):Array.<number>|undefined} */
|
|
70
58
|
this.lineDash = opts.lineDash || ((f, z) => undefined)
|
|
71
59
|
|
|
60
|
+
|
|
61
|
+
// for polygon filling and point symbol filling
|
|
62
|
+
/**
|
|
63
|
+
* @private
|
|
64
|
+
* @type {function(object,number):string} */
|
|
65
|
+
this.fillStyle = opts.fillStyle || ((f, z) => 'black')
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
72
69
|
/**
|
|
73
70
|
* @private
|
|
74
71
|
* @type {Array.<object> | undefined} */
|
|
@@ -96,81 +93,136 @@ export class GeoJSONLayer extends Layer {
|
|
|
96
93
|
const z = geoCanvas.view.z
|
|
97
94
|
const ctx = geoCanvas.offscreenCtx
|
|
98
95
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
96
|
+
// draw each feature
|
|
97
|
+
for (const f of this.fs)
|
|
98
|
+
this.drawGeometry(f, f.geometry, f.geometry.type, z, ctx)
|
|
99
|
+
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* @private
|
|
104
|
+
* Draw a geometry on the canvas, according to its type and the layer style.
|
|
105
|
+
* @param {object} f The feature to draw
|
|
106
|
+
* @param {object} geom The geometry of the feature to draw
|
|
107
|
+
* @param {string} gt The geometry type (e.g. 'Point', 'LineString', etc.)
|
|
108
|
+
* @param {number} z The current zoom level of the map
|
|
109
|
+
* @param {CanvasRenderingContext2D} ctx The canvas context to draw on
|
|
110
|
+
* @returns {void}
|
|
111
|
+
*/
|
|
112
|
+
drawGeometry(f, geom, gt, z, ctx) {
|
|
113
|
+
if (gt == 'Point' || gt == 'MultiPoint') {
|
|
114
|
+
|
|
115
|
+
//get style parameters for the point feature
|
|
116
|
+
const shape = this.shape(f, z)
|
|
117
|
+
if (!shape || shape == 'none') return
|
|
118
|
+
const size = this.size(f, z) * z
|
|
119
|
+
if (!size) return
|
|
120
|
+
|
|
121
|
+
const strokeStyle = this.strokeStyle(f, z)
|
|
122
|
+
const fillStyle = this.fillStyle(f, z)
|
|
123
|
+
const lineWidth = this.lineWidth(f, z) * z
|
|
124
|
+
|
|
125
|
+
//set canvas drawing parameters
|
|
126
|
+
ctx.save()
|
|
127
|
+
if (strokeStyle) ctx.strokeStyle = strokeStyle
|
|
128
|
+
if (fillStyle) ctx.fillStyle = fillStyle
|
|
129
|
+
if (lineWidth) ctx.lineWidth = lineWidth
|
|
130
|
+
|
|
131
|
+
let cs = geom.coordinates
|
|
132
|
+
if (gt == 'Point') cs = [cs]
|
|
133
|
+
|
|
134
|
+
if (shape == 'circle') {
|
|
135
|
+
//draw circle - fill and stroke
|
|
136
|
+
for (const c of cs) {
|
|
137
|
+
ctx.beginPath()
|
|
138
|
+
ctx.arc(c[0], c[1], size / 2, 0, 2 * Math.PI, false)
|
|
139
|
+
if (fillStyle) ctx.fill()
|
|
140
|
+
if (strokeStyle && lineWidth) ctx.stroke()
|
|
141
|
+
}
|
|
142
|
+
} else if (shape == 'square') {
|
|
143
|
+
//draw square - fill and stroke
|
|
144
|
+
for (const c of cs) {
|
|
145
|
+
ctx.beginPath()
|
|
146
|
+
ctx.rect(c[0] - size / 2, c[1] - size / 2, size, size)
|
|
147
|
+
if (fillStyle) ctx.fill()
|
|
148
|
+
if (strokeStyle && lineWidth) ctx.stroke()
|
|
139
149
|
}
|
|
140
|
-
} else
|
|
150
|
+
} else {
|
|
151
|
+
console.error('Unexpected shape for point GeoJSON: ' + shape)
|
|
152
|
+
}
|
|
153
|
+
ctx.restore()
|
|
154
|
+
|
|
155
|
+
} else if (gt == 'LineString' || gt == 'MultiLineString') {
|
|
141
156
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
if (!col || col == 'none') continue
|
|
145
|
-
ctx.strokeStyle = col
|
|
157
|
+
const col = this.strokeStyle(f, z)
|
|
158
|
+
if (!col || col === 'none') return
|
|
146
159
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
if (!wP || wP < 0) continue
|
|
150
|
-
ctx.lineWidth = wP * z
|
|
160
|
+
const wP = this.lineWidth(f, z)
|
|
161
|
+
if (!wP || wP < 0) return
|
|
151
162
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
163
|
+
ctx.save()
|
|
164
|
+
ctx.strokeStyle = col
|
|
165
|
+
ctx.lineWidth = wP * z
|
|
166
|
+
ctx.setLineDash(this.lineDash(f, z) || [])
|
|
167
|
+
|
|
168
|
+
//
|
|
169
|
+
const css = gt === 'LineString' ? [geom.coordinates] : geom.coordinates
|
|
170
|
+
|
|
171
|
+
for (const cs of css) {
|
|
172
|
+
if (cs.length < 2) continue
|
|
173
|
+
ctx.beginPath()
|
|
174
|
+
ctx.moveTo(cs[0][0], cs[0][1])
|
|
175
|
+
for (let i = 1; i < cs.length; i++) ctx.lineTo(cs[i][0], cs[i][1])
|
|
176
|
+
ctx.stroke()
|
|
177
|
+
}
|
|
155
178
|
|
|
156
|
-
|
|
157
|
-
if (gt == 'LineString') css = [css]
|
|
179
|
+
ctx.restore()
|
|
158
180
|
|
|
159
|
-
|
|
160
|
-
|
|
181
|
+
} else if (gt == 'Polygon' || gt == 'MultiPolygon') {
|
|
182
|
+
ctx.save()
|
|
183
|
+
|
|
184
|
+
// A Polygon is [ outerRing, ...holes ]. A MultiPolygon is [ [ outerRing, ...holes ], ... ]
|
|
185
|
+
const polygons = gt === 'Polygon' ? [geom.coordinates] : geom.coordinates
|
|
186
|
+
|
|
187
|
+
ctx.beginPath()
|
|
188
|
+
|
|
189
|
+
for (const rings of polygons) {
|
|
190
|
+
for (const cs of rings) {
|
|
161
191
|
if (cs.length < 2) continue
|
|
162
|
-
ctx.beginPath()
|
|
163
192
|
ctx.moveTo(cs[0][0], cs[0][1])
|
|
164
193
|
for (let i = 1; i < cs.length; i++) ctx.lineTo(cs[i][0], cs[i][1])
|
|
194
|
+
ctx.closePath()
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Fill
|
|
199
|
+
const fillStyle = this.fillStyle(f, z)
|
|
200
|
+
if (fillStyle && fillStyle !== 'none') {
|
|
201
|
+
ctx.fillStyle = fillStyle
|
|
202
|
+
ctx.fill('evenodd') // even-odd punches holes correctly regardless of ring winding order
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Stroke (optional, only if style provides a width)
|
|
206
|
+
const wP = this.lineWidth(f, z)
|
|
207
|
+
if (wP && wP > 0) {
|
|
208
|
+
const strokeStyle = this.strokeStyle?.(f, z) ?? fillStyle
|
|
209
|
+
if (strokeStyle && strokeStyle !== 'none') {
|
|
210
|
+
ctx.strokeStyle = strokeStyle
|
|
211
|
+
ctx.lineWidth = wP * z
|
|
212
|
+
ctx.setLineDash(this.lineDash?.(f, z) || [])
|
|
165
213
|
ctx.stroke()
|
|
166
214
|
}
|
|
167
|
-
} else {
|
|
168
|
-
console.log('Unsupported geometry type in GeoJSONLayer: ' + gt)
|
|
169
215
|
}
|
|
170
|
-
|
|
216
|
+
ctx.restore()
|
|
217
|
+
|
|
218
|
+
} else if (gt == 'GeometryCollection') {
|
|
219
|
+
//recursive call for each geometry in the collection
|
|
220
|
+
for (const g of geom.geometries)
|
|
221
|
+
this.drawGeometry(f, g, g.type, z, ctx)
|
|
171
222
|
|
|
172
|
-
|
|
173
|
-
|
|
223
|
+
} else {
|
|
224
|
+
console.log('Unexpected geometry type in GeoJSONLayer: ' + gt)
|
|
225
|
+
}
|
|
174
226
|
}
|
|
175
227
|
|
|
176
228
|
/**
|
|
@@ -180,7 +232,7 @@ export class GeoJSONLayer extends Layer {
|
|
|
180
232
|
*/
|
|
181
233
|
async load(callback) {
|
|
182
234
|
if (!this.url) {
|
|
183
|
-
console.log('Failed loading
|
|
235
|
+
console.log('Failed loading GeoJSON data: No URL specified. ' + this.url)
|
|
184
236
|
this.loadingStatus = 'failed'
|
|
185
237
|
this.labels = []
|
|
186
238
|
return
|
|
@@ -216,7 +268,7 @@ export class GeoJSONLayer extends Layer {
|
|
|
216
268
|
//redraw
|
|
217
269
|
if (callback) callback()
|
|
218
270
|
} catch (error) {
|
|
219
|
-
console.log('Failed loading
|
|
271
|
+
console.log('Failed loading GeoJSON from ' + this.url)
|
|
220
272
|
this.fs = []
|
|
221
273
|
this.loadingStatus = 'failed'
|
|
222
274
|
}
|