gridviz 3.0.34 → 3.0.36
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.md +129 -129
- package/dist/gridviz.js +17487 -17471
- package/dist/gridviz.min.js +2 -2
- package/package.json +73 -73
- package/src/button/Button.js +67 -67
- package/src/button/FullscreenButton.js +121 -121
- package/src/button/ZoomButtons.js +103 -103
- package/src/core/Dataset.js +154 -154
- package/src/core/Drawable.js +78 -78
- package/src/core/GeoCanvas.js +443 -443
- package/src/core/Layer.js +23 -23
- package/src/core/Legend.js +108 -108
- package/src/core/Map.js +568 -552
- package/src/core/MultiResolutionDataset.js +93 -93
- package/src/core/Style.js +98 -98
- package/src/core/Tooltip.js +182 -182
- package/src/dataset/COGeo.js +25 -25
- package/src/dataset/CSVGrid.js +112 -112
- package/src/dataset/GeoTIFF.js +122 -122
- package/src/dataset/JSGrid.js +55 -55
- package/src/dataset/TiledGrid.js +302 -302
- package/src/dataset/worker.js +18 -18
- package/src/index.js +79 -79
- package/src/layer/BackgroundLayer.js +173 -173
- package/src/layer/BackgroundLayerImage.js +81 -81
- package/src/layer/BackgroundLayerWMS.js +104 -104
- package/src/layer/GeoJSONLayer.js +276 -276
- package/src/layer/GridLayer.js +166 -166
- package/src/layer/LabelLayer.js +194 -194
- package/src/legend/ColorCategoryLegend.js +109 -109
- package/src/legend/ColorDiscreteLegend.js +100 -100
- package/src/legend/ColorLegend.js +115 -115
- package/src/legend/OrientationLegend.js +89 -89
- package/src/legend/SizeLegend.js +224 -224
- package/src/legend/TernaryLegend.js +302 -302
- package/src/style/CompositionStyle.js +291 -291
- package/src/style/DotDensityStyle.js +133 -133
- package/src/style/ImageStyle.js +100 -100
- package/src/style/Interpolator.js +258 -258
- package/src/style/IsoFenceStyle.js +217 -217
- package/src/style/JoyPlotStyle.js +180 -180
- package/src/style/LegoStyle.js +132 -132
- package/src/style/MosaicStyle.js +99 -99
- package/src/style/NinjaStarStyle.js +102 -102
- package/src/style/PillarStyle.js +207 -207
- package/src/style/SegmentStyle.js +105 -105
- package/src/style/ShadingRayStyle.js +280 -280
- package/src/style/ShadingStyle.js +72 -72
- package/src/style/ShapeColorSizeStyle.js +147 -147
- package/src/style/SideCategoryStyle.js +112 -112
- package/src/style/SideStyle.js +217 -217
- package/src/style/SideTanakaStyle.js +154 -154
- package/src/style/SquareColorCategoryWebGLStyle.js +307 -294
- package/src/style/SquareColorWebGLStyle.js +126 -126
- package/src/style/StrokeStyle.js +135 -135
- package/src/style/TextStyle.js +110 -110
- package/src/style/TimeSeriesStyle.js +222 -222
- package/src/utils/WebGLRectangleColoring.js +106 -106
- package/src/utils/WebGLSquareColoring.js +118 -118
- package/src/utils/WebGLSquareColoringAdvanced.js +227 -227
- package/src/utils/WebGLSquareColoringKS.js +153 -153
- package/src/utils/scale.js +255 -255
- package/src/utils/stretching.js +120 -120
- package/src/utils/ternary.js +208 -208
- package/src/utils/utils.js +125 -125
- package/src/utils/webGLUtils.js +73 -73
- package/src/utils/zoomUtils.js +142 -142
|
@@ -1,276 +1,276 @@
|
|
|
1
|
-
//@ts-check
|
|
2
|
-
'use strict'
|
|
3
|
-
|
|
4
|
-
import { Layer } from '../core/Layer.js'
|
|
5
|
-
import { json } from 'd3-fetch'
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* @module layer
|
|
9
|
-
* @author Joseph Davies, Julien Gaffuri
|
|
10
|
-
*/
|
|
11
|
-
export class GeoJSONLayer extends Layer {
|
|
12
|
-
/**
|
|
13
|
-
* @param {object} opts
|
|
14
|
-
*/
|
|
15
|
-
constructor(opts) {
|
|
16
|
-
super(opts)
|
|
17
|
-
opts = opts || {}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* @private
|
|
21
|
-
* @type {string} */
|
|
22
|
-
this.url = opts.url
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* A preprocess to run on each feature after loading.
|
|
26
|
-
* It can be used to apply some specific treatment before, format the label data, project coordinates, etc.
|
|
27
|
-
* Return false if the label should not be kept.
|
|
28
|
-
* @private
|
|
29
|
-
* @type {function(object):boolean} */
|
|
30
|
-
this.preprocess = opts.preprocess
|
|
31
|
-
|
|
32
|
-
//for points
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* @private
|
|
36
|
-
* @type {function(object,number):string} */
|
|
37
|
-
this.shape = opts.shape || ((f, z) => 'circle')
|
|
38
|
-
/**
|
|
39
|
-
* In pixel
|
|
40
|
-
* @private
|
|
41
|
-
* @type {function(object,number):number} */
|
|
42
|
-
this.size = opts.size || ((f, z) => 10)
|
|
43
|
-
|
|
44
|
-
// for lines and polygon outlines
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* @private
|
|
48
|
-
* @type {function(object,number):string} */
|
|
49
|
-
this.strokeStyle = opts.strokeStyle || opts.color || ((f, z) => 'gray')
|
|
50
|
-
/**
|
|
51
|
-
* In pixel
|
|
52
|
-
* @private
|
|
53
|
-
* @type {function(object,number):number} */
|
|
54
|
-
this.lineWidth = opts.lineWidth || opts.width || ((f, z) => 2)
|
|
55
|
-
/**
|
|
56
|
-
* @private
|
|
57
|
-
* @type {function(object,number):Array.<number>|undefined} */
|
|
58
|
-
this.lineDash = opts.lineDash || ((f, z) => undefined)
|
|
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
|
-
|
|
69
|
-
/**
|
|
70
|
-
* @private
|
|
71
|
-
* @type {Array.<object> | undefined} */
|
|
72
|
-
this.fs = undefined
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* @private
|
|
76
|
-
* @type {string} */
|
|
77
|
-
this.loadingStatus = 'notLoaded'
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Draw the layer.
|
|
82
|
-
* @param {import("../core/GeoCanvas.js").GeoCanvas} geoCanvas The canvas where to draw the layer.
|
|
83
|
-
* @returns {void}
|
|
84
|
-
*/
|
|
85
|
-
draw(geoCanvas) {
|
|
86
|
-
//load data, if not done yet.
|
|
87
|
-
if (!this.fs) {
|
|
88
|
-
this.load(geoCanvas.redraw)
|
|
89
|
-
return
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
//
|
|
93
|
-
const z = geoCanvas.view.z
|
|
94
|
-
const ctx = geoCanvas.offscreenCtx
|
|
95
|
-
|
|
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()
|
|
149
|
-
}
|
|
150
|
-
} else {
|
|
151
|
-
console.error('Unexpected shape for point GeoJSON: ' + shape)
|
|
152
|
-
}
|
|
153
|
-
ctx.restore()
|
|
154
|
-
|
|
155
|
-
} else if (gt == 'LineString' || gt == 'MultiLineString') {
|
|
156
|
-
|
|
157
|
-
const col = this.strokeStyle(f, z)
|
|
158
|
-
if (!col || col === 'none') return
|
|
159
|
-
|
|
160
|
-
const wP = this.lineWidth(f, z)
|
|
161
|
-
if (!wP || wP < 0) return
|
|
162
|
-
|
|
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
|
-
}
|
|
178
|
-
|
|
179
|
-
ctx.restore()
|
|
180
|
-
|
|
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) {
|
|
191
|
-
if (cs.length < 2) continue
|
|
192
|
-
ctx.moveTo(cs[0][0], cs[0][1])
|
|
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) || [])
|
|
213
|
-
ctx.stroke()
|
|
214
|
-
}
|
|
215
|
-
}
|
|
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)
|
|
222
|
-
|
|
223
|
-
} else {
|
|
224
|
-
console.log('Unexpected geometry type in GeoJSONLayer: ' + gt)
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* Load data for labels, from URL this.url
|
|
230
|
-
* @param {function():void} callback
|
|
231
|
-
* @private
|
|
232
|
-
*/
|
|
233
|
-
async load(callback) {
|
|
234
|
-
if (!this.url) {
|
|
235
|
-
console.log('Failed loading GeoJSON data: No URL specified. ' + this.url)
|
|
236
|
-
this.loadingStatus = 'failed'
|
|
237
|
-
this.labels = []
|
|
238
|
-
return
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
//check if data already loaded
|
|
242
|
-
if (this.loadingStatus != 'notLoaded') return
|
|
243
|
-
|
|
244
|
-
//load data
|
|
245
|
-
this.loadingStatus = 'loading'
|
|
246
|
-
|
|
247
|
-
try {
|
|
248
|
-
const data_ = await json(this.url)
|
|
249
|
-
|
|
250
|
-
/** @type { Array.<object> } */
|
|
251
|
-
const data = data_.features
|
|
252
|
-
|
|
253
|
-
//preprocess/filter
|
|
254
|
-
if (this.preprocess) {
|
|
255
|
-
this.fs = []
|
|
256
|
-
for (const c of data) {
|
|
257
|
-
const b = this.preprocess(c)
|
|
258
|
-
if (b == false) continue
|
|
259
|
-
this.fs.push(c)
|
|
260
|
-
}
|
|
261
|
-
} else {
|
|
262
|
-
//store labels
|
|
263
|
-
this.fs = data
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
this.loadingStatus = 'loaded'
|
|
267
|
-
|
|
268
|
-
//redraw
|
|
269
|
-
if (callback) callback()
|
|
270
|
-
} catch (error) {
|
|
271
|
-
console.log('Failed loading GeoJSON from ' + this.url)
|
|
272
|
-
this.fs = []
|
|
273
|
-
this.loadingStatus = 'failed'
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
}
|
|
1
|
+
//@ts-check
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
import { Layer } from '../core/Layer.js'
|
|
5
|
+
import { json } from 'd3-fetch'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @module layer
|
|
9
|
+
* @author Joseph Davies, Julien Gaffuri
|
|
10
|
+
*/
|
|
11
|
+
export class GeoJSONLayer extends Layer {
|
|
12
|
+
/**
|
|
13
|
+
* @param {object} opts
|
|
14
|
+
*/
|
|
15
|
+
constructor(opts) {
|
|
16
|
+
super(opts)
|
|
17
|
+
opts = opts || {}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @private
|
|
21
|
+
* @type {string} */
|
|
22
|
+
this.url = opts.url
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* A preprocess to run on each feature after loading.
|
|
26
|
+
* It can be used to apply some specific treatment before, format the label data, project coordinates, etc.
|
|
27
|
+
* Return false if the label should not be kept.
|
|
28
|
+
* @private
|
|
29
|
+
* @type {function(object):boolean} */
|
|
30
|
+
this.preprocess = opts.preprocess
|
|
31
|
+
|
|
32
|
+
//for points
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @private
|
|
36
|
+
* @type {function(object,number):string} */
|
|
37
|
+
this.shape = opts.shape || ((f, z) => 'circle')
|
|
38
|
+
/**
|
|
39
|
+
* In pixel
|
|
40
|
+
* @private
|
|
41
|
+
* @type {function(object,number):number} */
|
|
42
|
+
this.size = opts.size || ((f, z) => 10)
|
|
43
|
+
|
|
44
|
+
// for lines and polygon outlines
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @private
|
|
48
|
+
* @type {function(object,number):string} */
|
|
49
|
+
this.strokeStyle = opts.strokeStyle || opts.color || ((f, z) => 'gray')
|
|
50
|
+
/**
|
|
51
|
+
* In pixel
|
|
52
|
+
* @private
|
|
53
|
+
* @type {function(object,number):number} */
|
|
54
|
+
this.lineWidth = opts.lineWidth || opts.width || ((f, z) => 2)
|
|
55
|
+
/**
|
|
56
|
+
* @private
|
|
57
|
+
* @type {function(object,number):Array.<number>|undefined} */
|
|
58
|
+
this.lineDash = opts.lineDash || ((f, z) => undefined)
|
|
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
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @private
|
|
71
|
+
* @type {Array.<object> | undefined} */
|
|
72
|
+
this.fs = undefined
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @private
|
|
76
|
+
* @type {string} */
|
|
77
|
+
this.loadingStatus = 'notLoaded'
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Draw the layer.
|
|
82
|
+
* @param {import("../core/GeoCanvas.js").GeoCanvas} geoCanvas The canvas where to draw the layer.
|
|
83
|
+
* @returns {void}
|
|
84
|
+
*/
|
|
85
|
+
draw(geoCanvas) {
|
|
86
|
+
//load data, if not done yet.
|
|
87
|
+
if (!this.fs) {
|
|
88
|
+
this.load(geoCanvas.redraw)
|
|
89
|
+
return
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
//
|
|
93
|
+
const z = geoCanvas.view.z
|
|
94
|
+
const ctx = geoCanvas.offscreenCtx
|
|
95
|
+
|
|
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()
|
|
149
|
+
}
|
|
150
|
+
} else {
|
|
151
|
+
console.error('Unexpected shape for point GeoJSON: ' + shape)
|
|
152
|
+
}
|
|
153
|
+
ctx.restore()
|
|
154
|
+
|
|
155
|
+
} else if (gt == 'LineString' || gt == 'MultiLineString') {
|
|
156
|
+
|
|
157
|
+
const col = this.strokeStyle(f, z)
|
|
158
|
+
if (!col || col === 'none') return
|
|
159
|
+
|
|
160
|
+
const wP = this.lineWidth(f, z)
|
|
161
|
+
if (!wP || wP < 0) return
|
|
162
|
+
|
|
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
|
+
}
|
|
178
|
+
|
|
179
|
+
ctx.restore()
|
|
180
|
+
|
|
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) {
|
|
191
|
+
if (cs.length < 2) continue
|
|
192
|
+
ctx.moveTo(cs[0][0], cs[0][1])
|
|
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) || [])
|
|
213
|
+
ctx.stroke()
|
|
214
|
+
}
|
|
215
|
+
}
|
|
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)
|
|
222
|
+
|
|
223
|
+
} else {
|
|
224
|
+
console.log('Unexpected geometry type in GeoJSONLayer: ' + gt)
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Load data for labels, from URL this.url
|
|
230
|
+
* @param {function():void} callback
|
|
231
|
+
* @private
|
|
232
|
+
*/
|
|
233
|
+
async load(callback) {
|
|
234
|
+
if (!this.url) {
|
|
235
|
+
console.log('Failed loading GeoJSON data: No URL specified. ' + this.url)
|
|
236
|
+
this.loadingStatus = 'failed'
|
|
237
|
+
this.labels = []
|
|
238
|
+
return
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
//check if data already loaded
|
|
242
|
+
if (this.loadingStatus != 'notLoaded') return
|
|
243
|
+
|
|
244
|
+
//load data
|
|
245
|
+
this.loadingStatus = 'loading'
|
|
246
|
+
|
|
247
|
+
try {
|
|
248
|
+
const data_ = await json(this.url)
|
|
249
|
+
|
|
250
|
+
/** @type { Array.<object> } */
|
|
251
|
+
const data = data_.features
|
|
252
|
+
|
|
253
|
+
//preprocess/filter
|
|
254
|
+
if (this.preprocess) {
|
|
255
|
+
this.fs = []
|
|
256
|
+
for (const c of data) {
|
|
257
|
+
const b = this.preprocess(c)
|
|
258
|
+
if (b == false) continue
|
|
259
|
+
this.fs.push(c)
|
|
260
|
+
}
|
|
261
|
+
} else {
|
|
262
|
+
//store labels
|
|
263
|
+
this.fs = data
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
this.loadingStatus = 'loaded'
|
|
267
|
+
|
|
268
|
+
//redraw
|
|
269
|
+
if (callback) callback()
|
|
270
|
+
} catch (error) {
|
|
271
|
+
console.log('Failed loading GeoJSON from ' + this.url)
|
|
272
|
+
this.fs = []
|
|
273
|
+
this.loadingStatus = 'failed'
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|