gridviz 3.0.11 → 3.0.13

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gridviz",
3
- "version": "3.0.11",
3
+ "version": "3.0.13",
4
4
  "description": "Visualization tool for gridded statistics",
5
5
  "keywords": [
6
6
  "statistics",
@@ -56,16 +56,16 @@
56
56
  "d3-scale": "^4.0.2",
57
57
  "d3-selection": "^3.0.0",
58
58
  "d3-zoom": "^3.0.0",
59
- "jsdoc": "^4.0.2"
59
+ "jsdoc": "^4.0.4"
60
60
  },
61
61
  "devDependencies": {
62
62
  "@babel/preset-env": "^7.25.4",
63
63
  "babel-loader": "^8.1.0",
64
64
  "jshint": "^2.12.0",
65
65
  "prettier": "2.8.8",
66
- "webpack": "^5.94.0",
66
+ "webpack": "^5.96.1",
67
67
  "webpack-cli": "^5.1.4",
68
- "webpack-dev-server": "^4.15.1",
68
+ "webpack-dev-server": "^5.1.0",
69
69
  "webpack-livereload-plugin": "^3.0.2"
70
70
  },
71
71
  "engines": {
@@ -129,6 +129,16 @@ export class GeoCanvas {
129
129
  }
130
130
  //select(this.canvas).call(z);
131
131
 
132
+ //center extent
133
+ /** @type {number|undefined} */
134
+ this.xMin = opts.centerExtent? opts.centerExtent[0] : undefined
135
+ /** @type {number|undefined} */
136
+ this.yMin = opts.centerExtent? opts.centerExtent[1] : undefined
137
+ /** @type {number|undefined} */
138
+ this.xMax = opts.centerExtent? opts.centerExtent[2] : undefined
139
+ /** @type {number|undefined} */
140
+ this.yMax = opts.centerExtent? opts.centerExtent[3] : undefined
141
+
132
142
  /** Zoom extent, to limit zoom in and out
133
143
  * @type {Array.<number>} */
134
144
  this.zoomExtent = opts.zoomExtent || [0, Infinity]
@@ -142,6 +152,18 @@ export class GeoCanvas {
142
152
  getView() { return this.view }
143
153
 
144
154
 
155
+ /** @param {Array.<number>} v */
156
+ setCenterExtent(v) {
157
+ this.xMin = v[0]
158
+ this.yMin = v[1]
159
+ this.xMax = v[2]
160
+ this.yMax = v[3]
161
+ }
162
+ /** @returns {Array.<number|undefined>} */
163
+ getCenterExtent() {
164
+ return [this.xMin, this.yMin, this.xMax, this.yMax]
165
+ }
166
+
145
167
  /** @param {Array.<number>} v */
146
168
  setZoomExtent(v) {
147
169
  this.zoomExtent = v
@@ -194,7 +216,14 @@ export class GeoCanvas {
194
216
  * @param {number} dyGeo
195
217
  */
196
218
  pan(dxGeo = 0, dyGeo = 0) {
197
- //TODO force extend to remain
219
+
220
+ //ensures x/y extent
221
+ if(this.xMin != undefined && this.view.x + dxGeo < this.xMin) dxGeo = this.xMin - this.view.x
222
+ if(this.yMin != undefined && this.view.y + dyGeo < this.yMin) dyGeo = this.yMin - this.view.y
223
+ if(this.xMax != undefined && this.view.x + dxGeo > this.xMax) dxGeo = this.xMax - this.view.x
224
+ if(this.yMax != undefined && this.view.y + dyGeo > this.yMax) dyGeo = this.yMax - this.view.y
225
+
226
+ //pan
198
227
  this.view.x += dxGeo
199
228
  this.view.y += dyGeo
200
229
  this.updateExtentGeo()
@@ -210,7 +239,7 @@ export class GeoCanvas {
210
239
 
211
240
  /**
212
241
  * Zoom.
213
- * @param {number} f The , within ]0, Infinity]. 1 is for no change. <1 to zoom-in, >1 to zoom-out.
242
+ * @param {number} f The zoom factor, within ]0, Infinity]. 1 is for no change. <1 to zoom-in, >1 to zoom-out.
214
243
  * @param {number} xGeo The x geo position fixed in the screen.
215
244
  * @param {number} yGeo The y geo position fixed in the screen.
216
245
  */
@@ -227,9 +256,19 @@ export class GeoCanvas {
227
256
  if (newZf > this.zoomExtent[1]) f = this.zoomExtent[1] / this.view.z
228
257
 
229
258
  this.view.z *= f
230
- const dxGeo = (xGeo - this.view.x) * (1 - f)
259
+
260
+ //compute pan
261
+ let dxGeo = (xGeo - this.view.x) * (1 - f)
262
+ let dyGeo = (yGeo - this.view.y) * (1 - f)
263
+
264
+ //ensures x/y extent
265
+ if(this.xMin != undefined && this.view.x + dxGeo < this.xMin) dxGeo = this.xMin - this.view.x
266
+ if(this.yMin != undefined && this.view.y + dyGeo < this.yMin) dyGeo = this.yMin - this.view.y
267
+ if(this.xMax != undefined && this.view.x + dxGeo > this.xMax) dxGeo = this.xMax - this.view.x
268
+ if(this.yMax != undefined && this.view.y + dyGeo > this.yMax) dyGeo = this.yMax - this.view.y
269
+
270
+ //pan
231
271
  this.view.x += dxGeo
232
- const dyGeo = (yGeo - this.view.y) * (1 - f)
233
272
  this.view.y += dyGeo
234
273
  this.updateExtentGeo()
235
274
 
package/src/core/Map.js CHANGED
@@ -343,6 +343,16 @@ export class Map {
343
343
  }
344
344
 
345
345
 
346
+ /** @returns {Array.<number|undefined>} */
347
+ getCenterExtent() {
348
+ return this.geoCanvas.getCenterExtent()
349
+ }
350
+ /** @param {Array.<number>} val @returns {this} */
351
+ setCenterExtent(val) {
352
+ this.geoCanvas.setCenterExtent(val)
353
+ return this
354
+ }
355
+
346
356
  /** @returns {Array.<number>} */
347
357
  getZoomExtent() {
348
358
  return this.geoCanvas.getZoomExtent()
package/src/index.js CHANGED
@@ -50,14 +50,13 @@ export { ColorDiscreteLegend } from './legend/ColorDiscreteLegend.js'
50
50
  export { ColorCategoryLegend } from './legend/ColorCategoryLegend.js'
51
51
  export { SizeLegend, sizeLegend, sizeLegendViewScale, sizeDiscreteLegend, sizeDiscreteViewScaleLegend } from './legend/SizeLegend.js'
52
52
  export { OrientationLegend, orientationLegend } from './legend/OrientationLegend.js'
53
- export { TrivariateLegend } from './legend/TrivariateLegend.js'
53
+ export { TernaryLegend } from './legend/TernaryLegend.js'
54
54
 
55
55
  // export { goToStraight, zoomTo } from "./utils/zoomUtils"
56
56
  export * from './utils/stretching.js'
57
57
  export * from './utils/scale.js'
58
- export * from './utils/trivariate.js'
59
- export * from './utils/chernoff.js'
60
- export { nice, loadImage } from './utils/utils.js'
58
+ export * from './utils/ternary.js'
59
+ export { nice } from './utils/utils.js'
61
60
 
62
61
  import { GeoCanvas } from './core/GeoCanvas.js'
63
62
  export const getParameterByName = GeoCanvas.getParameterByName
@@ -16,11 +16,12 @@ export class ColorLegend extends Legend {
16
16
  super(opts)
17
17
  opts = opts || {}
18
18
 
19
- //a function [0,1]->color for continuous colors
20
- //or array of colors for discrete colors
19
+ //a function [0,1]->color for continuous colors.
20
+ //it can take as second argument the viewscale.
21
21
  this.colorScale = opts.colorScale
22
22
 
23
23
  //function (t[0,1]) -> value (for label text)
24
+ //it can take as second argument the viewscale.
24
25
  this.textScale = opts.textScale || (t => t)
25
26
 
26
27
  this.margin = opts.margin || 5
@@ -9,7 +9,7 @@ import { Legend } from '../core/Legend.js'
9
9
  * @module legend
10
10
  * @author Julien Gaffuri
11
11
  */
12
- export class TrivariateLegend extends Legend {
12
+ export class TernaryLegend extends Legend {
13
13
  /** @param {Object} opts */
14
14
  constructor(opts) {
15
15
  super(opts)
@@ -14,17 +14,19 @@ export class ImageStyle extends Style {
14
14
  super(opts)
15
15
  opts = opts || {}
16
16
 
17
- /** A function returning the image code of a cell.
17
+ /** A function returning the image URL of a cell.
18
18
  * @type {function(import('../core/Dataset.js').Cell, number, number, object):string} */
19
- this.imageCode = opts.imageCode || (() => '') //(c,r,z,vs) => {}
20
-
21
- /** The dictionnary code -> image
22
- * @type {object} */
23
- this.images = opts.images || {}
19
+ this.image = opts.image || (() => '') //(c,r,z,vs) => {}
24
20
 
25
21
  /** The image size in ground meters
26
22
  * @type {function(import('../core/Dataset.js').Cell, number, number, object):number} */
27
23
  this.size = opts.size || ((cell, resolution) => resolution)
24
+
25
+ /** Dictionnary of preloaded images. url -> image
26
+ * @private
27
+ * @type {object} */
28
+ this.cache = {}
29
+
28
30
  }
29
31
 
30
32
  /**
@@ -33,7 +35,7 @@ export class ImageStyle extends Style {
33
35
  * @param {number} resolution
34
36
  * @override
35
37
  */
36
- draw(cells, geoCanvas, resolution) {
38
+ async draw(cells, geoCanvas, resolution) {
37
39
 
38
40
  //
39
41
  const z = geoCanvas.view.z,
@@ -48,28 +50,47 @@ export class ImageStyle extends Style {
48
50
  //
49
51
  for (let cell of cells) {
50
52
 
51
- //get cell image code
52
- const code = this.imageCode(cell, resolution, z, viewScale)
53
-
54
- //get image
55
- const image = this.images[code]
56
- if (!image) continue
53
+ //get cell image url
54
+ const url = this.image(cell, resolution, z, viewScale)
55
+ if (!url) continue
57
56
 
58
57
  //size and position values
59
58
  let sizePix = this.size(cell, resolution, z, viewScale) / z
60
59
  if (!sizePix) continue
61
- const d = (resolutionPix - sizePix) / 2
62
-
63
- try {
64
- geoCanvas.ctx.drawImage(image, geoCanvas.geoToPixX(cell.x) + d, geoCanvas.geoToPixY(cell.y) + d -resolutionPix, sizePix, sizePix)
65
-
66
- /*/red color filter
67
- geoCanvas.ctx.globalCompositeOperation = 'source-in';
68
- geoCanvas.ctx.fillStyle = 'red';
69
- geoCanvas.ctx.fillRect(geoCanvas.geoToPixX(cell.x) + d, geoCanvas.geoToPixY(cell.y) + d, sizePix, sizePix);
70
- geoCanvas.ctx.globalCompositeOperation = 'source-over';*/
71
- } catch (error) {
72
- console.error(error)
60
+
61
+ //get image from cache
62
+ const image = this.cache[url]
63
+
64
+ //loading, keep waiting
65
+ if (image == "loading") return;
66
+
67
+ //no image: load it
68
+ else if (!image) {
69
+ //tag as loading
70
+ this.cache[url] = "loading"
71
+
72
+ //define image
73
+ const img = new Image()
74
+ img.onload = () => {
75
+ //store image data in cache and redraw
76
+ this.cache[url] = img
77
+ geoCanvas.redraw()
78
+ }
79
+ img.onerror = () => {
80
+ //case when no image
81
+ console.warn("Could not retrieve image from", url)
82
+ }
83
+ //set URL to launch the download
84
+ img.src = url
85
+
86
+ } else {
87
+ //draw image
88
+ const d = (resolutionPix - sizePix) / 2
89
+ try {
90
+ geoCanvas.ctx.drawImage(image, geoCanvas.geoToPixX(cell.x) + d, geoCanvas.geoToPixY(cell.y) + d - resolutionPix, sizePix, sizePix)
91
+ } catch (error) {
92
+ console.error(error)
93
+ }
73
94
  }
74
95
 
75
96
  }
@@ -5,7 +5,7 @@ import { interpolateLab } from "d3-interpolate"
5
5
 
6
6
 
7
7
 
8
- export const trivariateClassifier = (properties, totalFunction, opts = {}) => {
8
+ export const ternaryClassifier = (properties, totalFunction, opts = {}) => {
9
9
 
10
10
  //the three properties
11
11
  const p0 = properties[0], p1 = properties[1], p2 = properties[2]
@@ -83,7 +83,7 @@ export const trivariateClassifier = (properties, totalFunction, opts = {}) => {
83
83
 
84
84
 
85
85
 
86
- export const trivariateColorClassifier = (properties, totalFunction, colors, opts = {}) => {
86
+ export const ternaryColorClassifier = (properties, totalFunction, colors, opts = {}) => {
87
87
 
88
88
  //the three colors
89
89
  const [color0, color1, color2] = colors || ["red", "green", "blue"]
@@ -102,7 +102,7 @@ export const trivariateColorClassifier = (properties, totalFunction, colors, opt
102
102
  const centerColor = opts.centerColor || colorInterpolation(mixColorFunction(color0, color1), color2)(0.333)
103
103
 
104
104
  //make classifier
105
- const classifier = trivariateClassifier(properties, totalFunction, opts)
105
+ const classifier = ternaryClassifier(properties, totalFunction, opts)
106
106
 
107
107
  //the output color classifier method
108
108
  const fun = c => {
@@ -134,7 +134,7 @@ export const trivariateColorClassifier = (properties, totalFunction, colors, opt
134
134
  const orderedIndexesDec = arr => [...arr.keys()].sort((a, b) => arr[b] - arr[a]);
135
135
  const orderedIndexesInc = arr => [...arr.keys()].sort((a, b) => arr[a] - arr[b]);
136
136
 
137
- export const trivariateClassifier = (properties, totalFunction, opts = {}) => {
137
+ export const ternaryClassifier = (properties, totalFunction, opts = {}) => {
138
138
  const lowThreshold = opts.lowThreshold || [1 / 3, 1 / 3, 1 / 3]
139
139
  const highThreshold = opts.highThreshold || [2 / 3, 2 / 3, 2 / 3]
140
140
  const colors = opts.colors || ["red", "green", "blue"]
@@ -175,7 +175,7 @@ export const trivariateClassifier = (properties, totalFunction, opts = {}) => {
175
175
  */
176
176
 
177
177
  /*
178
- export const trivariateClassifier3 = (properties, totalFunction, opts = {}) => {
178
+ export const ternaryClassifier3 = (properties, totalFunction, opts = {}) => {
179
179
  const [a01, a12, a20] = opts.thresholds || [1/3, 1/3, 1/3]
180
180
  const [c0, c1, c2] = opts.colors || ["red", "green", "blue"]
181
181
  const centerColor = opts.centerColor || "gray"
@@ -29,6 +29,8 @@ export function nice(v, multiples = [8, 6, 5, 4, 2.5, 2]) {
29
29
  }
30
30
 
31
31
 
32
+ /*
33
+ //no longer used
32
34
  export function loadImage(src) {
33
35
  return new Promise((resolve, reject) => {
34
36
  const img = new Image();
@@ -37,7 +39,7 @@ export function loadImage(src) {
37
39
  img.src = src;
38
40
  });
39
41
  }
40
-
42
+ */
41
43
 
42
44
 
43
45
  /*
@@ -1,24 +0,0 @@
1
- //@ts-check
2
- 'use strict'
3
-
4
- import { loadImage } from '../utils/utils.js'
5
-
6
- //
7
-
8
- /**
9
- * Load chernoff images
10
- * See for example: https://github.com/jgaffuri/chernoff-faces
11
- * @param {object} out
12
- * @param {string} baseURL
13
- * @param {function|undefined} callback
14
- * @returns
15
- */
16
- export async function loadChernoffFacesImages(out, baseURL, callback = undefined) {
17
- out = out || {}
18
- for (let s of ["f", "m"])
19
- for (let a of ["y", "m", "o"])
20
- for (let i = 0; i <= 2; i++)
21
- out[s + a + i] = await loadImage(baseURL + (s + a + i) + ".png")
22
- if (callback) callback()
23
- return out
24
- }