gridviz 2.1.16 → 3.0.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.
Files changed (67) hide show
  1. package/README.md +2 -3
  2. package/dist/gridviz.js +4971 -5654
  3. package/dist/gridviz.min.js +1 -1
  4. package/package.json +3 -2
  5. package/src/button/Button.js +14 -15
  6. package/src/button/FullscreenButton.js +14 -14
  7. package/src/button/ZoomButtons.js +2 -2
  8. package/src/{DatasetComponent.js → core/Dataset.js} +33 -12
  9. package/src/core/Drawable.js +60 -0
  10. package/src/{GeoCanvas.js → core/GeoCanvas.js} +65 -155
  11. package/src/core/Layer.js +24 -0
  12. package/src/{Legend.js → core/Legend.js} +24 -1
  13. package/src/core/Map.js +460 -0
  14. package/src/core/MultiResolutionDataset.js +96 -0
  15. package/src/core/Style.js +84 -0
  16. package/src/dataset/COGeo.js +5 -5
  17. package/src/dataset/CSVGrid.js +43 -41
  18. package/src/dataset/GeoTIFF.js +9 -14
  19. package/src/dataset/{LGrid.js → JSGrid.js} +9 -10
  20. package/src/dataset/TiledGrid.js +68 -45
  21. package/src/index.js +22 -29
  22. package/src/{BackgroundLayer.js → layer/BackgroundLayer.js} +27 -52
  23. package/src/{BackgroundLayerWMS.js → layer/BackgroundLayerWMS.js} +21 -45
  24. package/src/{LineLayer.js → layer/GeoJSONLayer.js} +22 -23
  25. package/src/layer/GridLayer.js +140 -0
  26. package/src/{LabelLayer.js → layer/LabelLayer.js} +25 -23
  27. package/src/legend/ColorCategoryLegend.js +9 -24
  28. package/src/legend/ColorDiscreteLegend.js +20 -24
  29. package/src/legend/ColorLegend.js +21 -33
  30. package/src/legend/OrientationLegend.js +90 -0
  31. package/src/legend/SizeLegend.js +153 -72
  32. package/src/style/CompositionStyle.js +71 -84
  33. package/src/style/ContourStyle.js +2 -36
  34. package/src/style/DotDensityStyle.js +50 -73
  35. package/src/style/IsoFenceStyle.js +59 -70
  36. package/src/style/JoyPlotStyle.js +41 -48
  37. package/src/style/LegoStyle.js +4 -4
  38. package/src/style/MosaicStyle.js +30 -39
  39. package/src/style/NinjaStarStyle.js +45 -68
  40. package/src/style/PillarStyle.js +70 -93
  41. package/src/style/SegmentStyle.js +37 -75
  42. package/src/style/ShapeColorSizeStyle.js +49 -98
  43. package/src/style/SideCategoryStyle.js +112 -0
  44. package/src/style/SideStyle.js +99 -133
  45. package/src/style/{SquareColorCatWGLStyle.js → SquareColorCategoryWebGLStyle.js} +28 -41
  46. package/src/style/{SquareColorWGLStyle.js → SquareColorWebGLStyle.js} +28 -49
  47. package/src/style/StrokeStyle.js +43 -70
  48. package/src/style/TanakaStyle.js +34 -132
  49. package/src/style/TextStyle.js +43 -64
  50. package/src/style/TimeSeriesStyle.js +44 -75
  51. package/src/utils/WebGLSquareColoringAdvanced.js +4 -4
  52. package/src/utils/scale.js +202 -0
  53. package/src/utils/stretching.js +86 -129
  54. package/src/utils/{Utils.js → utils.js} +15 -0
  55. package/src/utils/webGLUtils.js +4 -5
  56. package/src/utils/zoomUtils.js +30 -30
  57. package/src/App.js +0 -760
  58. package/src/Dataset.js +0 -76
  59. package/src/Layer.js +0 -103
  60. package/src/Style.js +0 -131
  61. package/src/dataset/GridTile.js +0 -41
  62. package/src/legend/SegmentOrientationLegend.js +0 -87
  63. package/src/legend/SegmentWidthLegend.js +0 -101
  64. package/src/style/CompositionStyle_.js +0 -293
  65. package/src/style/ShapeColorSizeStyle_.js +0 -108
  66. package/src/style/SideCatStyle.js +0 -172
  67. /package/src/{Tooltip.js → core/Tooltip.js} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gridviz",
3
- "version": "2.1.16",
3
+ "version": "3.0.0",
4
4
  "description": "Visualization tool for gridded statistics",
5
5
  "keywords": [
6
6
  "statistics",
@@ -49,10 +49,11 @@
49
49
  "src/**/*.js"
50
50
  ],
51
51
  "dependencies": {
52
+ "d3-array": "^3.2.4",
52
53
  "d3-color": "^3.1.0",
53
54
  "d3-fetch": "^3.0.1",
54
- "d3-format": "^3.1.0",
55
55
  "d3-random": "^3.0.1",
56
+ "d3-scale": "^4.0.2",
56
57
  "d3-selection": "^3.0.0",
57
58
  "d3-zoom": "^3.0.0"
58
59
  },
@@ -16,11 +16,10 @@ export class Button {
16
16
  * opts.x
17
17
  * opts.y
18
18
  */
19
- constructor(opts) {
20
- opts = opts || {}
19
+ constructor(opts = {}) {
21
20
 
22
- this.app = opts.app
23
- this.parentNode = opts.parentNode || opts.app.container
21
+ this.map = opts.map
22
+ this.parentNode = opts.parentNode || opts.map.container
24
23
 
25
24
  // the div element
26
25
  if (this.id) this.div = select('#' + this.id)
@@ -30,22 +29,22 @@ export class Button {
30
29
  if (this.id) this.div.attr('id', this.id)
31
30
  }
32
31
 
33
- if (opts.title) this.div.attr('title',opts.title)
34
- if (opts.class) this.div.attr('class',opts.class)
32
+ if (opts.title) this.div.attr('title', opts.title)
33
+ if (opts.class) this.div.attr('class', opts.class)
35
34
 
36
35
  // add events
37
36
  if (opts.onClickFunction) this.div.on('click', opts.onClickFunction)
38
37
 
39
38
  //set styles
40
- this.style('box-shadow','0 7px 8px rgba(0,47,103,.08), 0 0 22px rgba(0,47,103,.04), 0 12px 17px rgba(0,47,103,.04), 0 -4px 4px rgba(0,47,103,.04)') //.ecl-u-shadow-3
41
- this.style('background-color','#ffffff')
42
- this.style('position' , 'absolute')
43
- this.style('cursor' , 'pointer')
44
- this.style('display' , 'flex')
45
- this.style('justify-content' , 'center')
46
- this.style('align-items' , 'center')
47
- this.style('width' , '30px')
48
- this.style('height' , '30px')
39
+ this.style('box-shadow', '0 7px 8px rgba(0,47,103,.08), 0 0 22px rgba(0,47,103,.04), 0 12px 17px rgba(0,47,103,.04), 0 -4px 4px rgba(0,47,103,.04)') //.ecl-u-shadow-3
40
+ this.style('background-color', '#ffffff')
41
+ this.style('position', 'absolute')
42
+ this.style('cursor', 'pointer')
43
+ this.style('display', 'flex')
44
+ this.style('justify-content', 'center')
45
+ this.style('align-items', 'center')
46
+ this.style('width', '30px')
47
+ this.style('height', '30px')
49
48
  // this.style(padding , '4px'
50
49
 
51
50
 
@@ -43,9 +43,9 @@ export class FullscreenButton extends Button {
43
43
  </svg>
44
44
  `
45
45
 
46
- //save initial app dimensions
47
- this.defaultHeight = this.app.h
48
- this.defaultWidth = this.app.w
46
+ //save initial map dimensions
47
+ this.defaultHeight = this.map.h
48
+ this.defaultWidth = this.map.w
49
49
 
50
50
  // event handler
51
51
  this.div.on('click', (e) => {
@@ -73,21 +73,21 @@ export class FullscreenButton extends Button {
73
73
 
74
74
  onClickFunction(e) {
75
75
  if (this.isFullscreen) {
76
- this.closeFullscreen(this.app.container)
76
+ this.closeFullscreen(this.map.container)
77
77
  //resize canvas to default
78
- this.app.h = this.defaultHeight
79
- this.app.w = this.defaultWidth
80
- this.app.cg.h = this.defaultHeight
81
- this.app.cg.w = this.defaultWidth
82
- this.app.cg.canvas.setAttribute('width', '' + this.defaultWidth)
83
- this.app.cg.canvas.setAttribute('height', '' + this.defaultHeight)
84
- this.app.redraw()
78
+ this.map.h = this.defaultHeight
79
+ this.map.w = this.defaultWidth
80
+ this.map.geoCanvas.h = this.defaultHeight
81
+ this.map.geoCanvas.w = this.defaultWidth
82
+ this.map.geoCanvas.canvas.setAttribute('width', '' + this.defaultWidth)
83
+ this.map.geoCanvas.canvas.setAttribute('height', '' + this.defaultHeight)
84
+ this.map.redraw()
85
85
  this.isFullscreen = false
86
86
  } else {
87
- this.openFullscreen(this.app.container)
87
+ this.openFullscreen(this.map.container)
88
88
  //resize canvas to fullscreen
89
- this.app.h = window.screen.height
90
- this.app.w = window.screen.width
89
+ this.map.h = window.screen.height
90
+ this.map.w = window.screen.width
91
91
  this.isFullscreen = true
92
92
  }
93
93
  }
@@ -83,13 +83,13 @@ export class ZoomButtons extends Button {
83
83
 
84
84
  /* Zoom in */
85
85
  zoomIn(e) {
86
- this.app.setZoomFactor(this.app.getZoomFactor() * (1 - this.delta)).redraw()
86
+ this.map.setZoom(this.map.getZoom() * (1 - this.delta)).redraw()
87
87
  if (this.onZoom) this.onZoom(e)
88
88
  }
89
89
 
90
90
  /* Zoom out */
91
91
  zoomOut(e) {
92
- this.app.setZoomFactor(this.app.getZoomFactor() * (1 + this.delta)).redraw()
92
+ this.map.setZoom(this.map.getZoom() * (1 + this.delta)).redraw()
93
93
  if (this.onZoom) this.onZoom(e)
94
94
  }
95
95
  }
@@ -1,22 +1,33 @@
1
1
  //@ts-check
2
2
  'use strict'
3
3
 
4
+ /**
5
+ * A grid cell.
6
+ * @typedef {{x: number, y: number}} Cell */
7
+
4
8
  /**
5
9
  * A dataset component, of grid cells.
6
10
  * @abstract
7
11
  *
8
12
  * @author Joseph Davies, Julien Gaffuri
9
13
  */
10
- export class DatasetComponent {
14
+ export class Dataset {
11
15
  /**
16
+ * @param {import("./Map.js").Map} map The map.
12
17
  * @param {string} url The URL of the dataset.
13
18
  * @param {number} resolution The dataset resolution, in the CRS geographical unit.
14
- * @param {{preprocess?:function(import("./Dataset").Cell):boolean}} opts
19
+ * @param {{preprocess?:function(Cell):boolean}} opts
15
20
  * @abstract
16
21
  */
17
- constructor(url, resolution, opts = {}) {
22
+ constructor(map, url, resolution, opts = {}) {
18
23
  opts = opts || {}
19
24
 
25
+ /**
26
+ * The map.
27
+ * @protected
28
+ * @type {import("./Map.js").Map} */
29
+ this.map = map
30
+
20
31
  /**
21
32
  * The url of the dataset.
22
33
  * @protected
@@ -31,12 +42,12 @@ export class DatasetComponent {
31
42
 
32
43
  /**
33
44
  * A preprocess to run on each cell after loading. It can be used to apply some specific treatment before or compute a new column. And also to determine which cells to keep after loading.
34
- * @type {(function(import("./Dataset").Cell):boolean )| undefined } */
45
+ * @type {(function(Cell):boolean )| undefined } */
35
46
  this.preprocess = opts.preprocess || undefined
36
47
 
37
48
  /** The cells within the view
38
49
  * @protected
39
- * @type {Array.<import("./Dataset").Cell>} */
50
+ * @type {Array.<Cell>} */
40
51
  this.cellsViewCache = []
41
52
  }
42
53
 
@@ -44,18 +55,17 @@ export class DatasetComponent {
44
55
  * Request data within a geographic envelope.
45
56
  *
46
57
  * @abstract
47
- * @param {import("./Dataset").Envelope|undefined} extGeo
48
- * @param {function():void} callback
58
+ * @param {import("./GeoCanvas").Envelope|undefined} extGeo
49
59
  * @returns {this}
50
60
  */
51
- getData(extGeo, callback) {
61
+ getData(extGeo = undefined) {
52
62
  throw new Error('Method getData not implemented.')
53
63
  }
54
64
 
55
65
  /**
56
66
  * Fill the view cache with all cells which are within a geographical envelope.
57
67
  * @abstract
58
- * @param {import("./Dataset").Envelope} extGeo The view geographical envelope.
68
+ * @param {import("./GeoCanvas").Envelope} extGeo The view geographical envelope.
59
69
  * @returns {void}
60
70
  */
61
71
  updateViewCache(extGeo) {
@@ -66,8 +76,8 @@ export class DatasetComponent {
66
76
  * Get a cell under a given position, if any.
67
77
  *
68
78
  * @param {{x:number,y:number}} posGeo
69
- * @param {Array.<import("./Dataset").Cell>} cells Some cells from the dataset (a subset if necessary, usually the view cache).
70
- * @returns {import("./Dataset").Cell|undefined}
79
+ * @param {Array.<Cell>} cells Some cells from the dataset (a subset if necessary, usually the view cache).
80
+ * @returns {Cell|undefined}
71
81
  */
72
82
  getCellFromPosition(posGeo, cells) {
73
83
  //compute candidate cell position
@@ -106,8 +116,19 @@ export class DatasetComponent {
106
116
  return this.resolution
107
117
  }
108
118
 
109
- /** @returns {Array.<import("./Dataset").Cell>} */
119
+ /** @returns {Array.<Cell>} */
110
120
  getViewCache() {
111
121
  return this.cellsViewCache
112
122
  }
123
+
124
+ /**
125
+ * Return the relevant dataset for a specified zoom.
126
+ * @param {number} z
127
+ * @param {number} minPixelsPerCell
128
+ * @returns {Dataset|undefined}
129
+ * */
130
+ getDataset(z, minPixelsPerCell) {
131
+ return this
132
+ }
133
+
113
134
  }
@@ -0,0 +1,60 @@
1
+ //@ts-check
2
+ 'use strict'
3
+
4
+ /**
5
+ * This is an abstract class used to group elements shared between Layer and Style classes.
6
+ *
7
+ * @abstract
8
+ * @author Joseph Davies, Julien Gaffuri
9
+ */
10
+ export class Drawable {
11
+ /**
12
+ * @param {object} opts
13
+ */
14
+ constructor(opts) {
15
+ opts = opts || {}
16
+
17
+ /** A function specifying if the element should be visible or not.
18
+ * The function parameter is the zoom level.
19
+ * @type {function(number):boolean} */
20
+ this.visible = opts.visible
21
+
22
+ /** A function returning the alpha (transparency/opacity), between 0.0 (fully transparent) and 1.0 (fully opaque).
23
+ * The function parameter is the zoom level.
24
+ * (see CanvasRenderingContext2D: globalAlpha property)
25
+ * @type {(function(number):number)|undefined} */
26
+ this.alpha = opts.alpha
27
+
28
+ /** A function returning the blend operation.
29
+ * The function parameter is the zoom level.
30
+ * (see CanvasRenderingContext2D: globalCompositeOperation property)
31
+ * @type {function(number):GlobalCompositeOperation} */
32
+ this.blendOperation = opts.blendOperation || (z => "source-over")
33
+
34
+ /** @type {(function(number):string)|undefined} */
35
+ this.filterColor = opts.filterColor // (z) => "#eee7"
36
+ }
37
+
38
+ /**
39
+ * Draw layer filter.
40
+ *
41
+ * @param {import("./GeoCanvas.js").GeoCanvas} geoCanvas The canvas where to draw the layer.
42
+ * @returns {void}
43
+ * @abstract
44
+ */
45
+ drawFilter(geoCanvas) {
46
+ //no filter: return
47
+ if (!this.filterColor) return
48
+
49
+ //get filter
50
+ const fc = this.filterColor(geoCanvas.view.z)
51
+
52
+ //no filter: return
53
+ if (!fc || fc == 'none') return
54
+
55
+ //draw filter
56
+ geoCanvas.ctx.fillStyle = fc
57
+ geoCanvas.ctx.fillRect(0, 0, geoCanvas.w, geoCanvas.h)
58
+ }
59
+
60
+ }
@@ -3,8 +3,12 @@
3
3
 
4
4
  /** @typedef { {xMin: number, xMax: number, yMin: number, yMax: number} } Envelope */
5
5
 
6
+ /**
7
+ * A viewshed.
8
+ * @typedef {{x: number, y: number, z: number}} View */
9
+
6
10
  import { select } from 'd3-selection'
7
- import { zoom, zoomIdentity } from 'd3-zoom'
11
+ import { zoom as d3zoom, zoomIdentity } from 'd3-zoom'
8
12
 
9
13
  /**
10
14
  * A HTML canvas for geo data display, enhanced with zoom and pan capabilities.
@@ -15,11 +19,12 @@ export class GeoCanvas {
15
19
  /**
16
20
  * @constructor
17
21
  * @param {HTMLCanvasElement} canvas
18
- * @param {object} center Geographical coordinates of the center
19
- * @param {number} zf The zoom factor (pixel size, in ground m)
22
+ * @param {number} x The x coordinate of the view
23
+ * @param {number} y The y coordinate of the view
24
+ * @param {number} z The zoom level of the view (pixel size, in ground m)
20
25
  * @param {object} opts
21
26
  */
22
- constructor(canvas, center, zf, opts) {
27
+ constructor(canvas, x = 0, y = 0, z = 0, opts = undefined) {
23
28
  this.opts = opts || {}
24
29
 
25
30
  /** @type {HTMLCanvasElement} */
@@ -38,24 +43,22 @@ export class GeoCanvas {
38
43
  /**@type {CanvasRenderingContext2D} */
39
44
  this.ctx = ctx
40
45
 
41
- // set geo coordinates of the center
42
- this.center = center || { x: this.w * 0.5, y: this.h * 0.5 }
43
-
44
- // zoom factor: pixel size, in m/pix
45
- /** @type {number} */
46
- this.zf = zf
46
+ /**
47
+ * z: pixel size, in m/pix
48
+ * @type {View} */
49
+ this.view = { x: x, y: y, z: z }
47
50
 
48
51
  /** Background color.
49
52
  * @type {string} */
50
53
  this.backgroundColor = opts.backgroundColor || 'white'
51
54
 
52
- /** @type {function():void} */
55
+ /** @type {function(object|undefined):void} */
53
56
  this.onZoomStartFun = opts.onZoomStartFun
54
57
 
55
- /** @type {function():void} */
58
+ /** @type {function(object|undefined):void} */
56
59
  this.onZoomEndFun = opts.onZoomEndFun
57
60
 
58
- /** @type {function():void} */
61
+ /** @type {function(object|undefined):void} */
59
62
  this.onZoomFun = opts.onZoomFun
60
63
 
61
64
  //current extent
@@ -66,7 +69,7 @@ export class GeoCanvas {
66
69
  //rely on d3 zoom for pan/zoom
67
70
  if (!opts.disableZoom) {
68
71
  let tP = zoomIdentity
69
- const z = zoom()
72
+ const z = d3zoom()
70
73
  //to make the zooming a bit faster
71
74
  .wheelDelta((e) => -e.deltaY * (e.deltaMode === 1 ? 0.07 : e.deltaMode ? 1 : 0.004))
72
75
  .on('zoom', (e) => {
@@ -76,7 +79,7 @@ export class GeoCanvas {
76
79
  //pan
77
80
  const dx = tP.x - t.x
78
81
  const dy = tP.y - t.y
79
- this.pan(dx * this.getZf(), -dy * this.getZf())
82
+ this.pan(dx * this.view.z, -dy * this.view.z)
80
83
  } else {
81
84
  const se = e.sourceEvent
82
85
  if (se instanceof WheelEvent) {
@@ -106,17 +109,17 @@ export class GeoCanvas {
106
109
  })
107
110
  .on('start', (e) => {
108
111
  this.canvasSave.c = document.createElement('canvas')
109
- this.canvasSave.c.setAttribute('width', '' + this.w)
110
- this.canvasSave.c.setAttribute('height', '' + this.h)
111
- this.canvasSave.c.getContext('2d').drawImage(this.canvas, 0, 0)
112
- this.canvasSave.dx = 0
113
- this.canvasSave.dy = 0
114
- this.canvasSave.f = 1
112
+ this.canvasSave.c.setAttribute('width', '' + this.w);
113
+ this.canvasSave.c.setAttribute('height', '' + this.h);
114
+ this.canvasSave.c.getContext('2d')?.drawImage(this.canvas, 0, 0);
115
+ this.canvasSave.dx = 0;
116
+ this.canvasSave.dy = 0;
117
+ this.canvasSave.f = 1;
115
118
 
116
119
  if (this.onZoomStartFun) this.onZoomStartFun(e)
117
120
  })
118
121
  .on('end', (e) => {
119
- this.redraw(true)
122
+ this.redraw()
120
123
  this.canvasSave = { c: null, dx: 0, dy: 0, f: 1 }
121
124
 
122
125
  if (this.onZoomEndFun) this.onZoomEndFun(e)
@@ -127,39 +130,24 @@ export class GeoCanvas {
127
130
 
128
131
  /** Zoom extent, to limit zoom in and out
129
132
  * @type {Array.<number>} */
130
- this.zfExtent = [0, Infinity]
133
+ this.zoomExtent = opts.zoomExtent || [0, Infinity]
131
134
 
132
135
  /** Canvas state, to be used to avoid unnecessary redraws on zoom/pan
133
136
  * @type {{c:HTMLCanvasElement|null,dx:number,dy:number,f:number}} */
134
137
  this.canvasSave = { c: null, dx: 0, dy: 0, f: 1 }
135
138
  }
136
139
 
137
- /** @param {{x:number,y:number}} v Geographical coordinates of the center */
138
- setCenter(v) {
139
- this.center = v
140
- }
141
- /** @returns {{x:number,y:number}} Geographical coordinates of the center */
142
- getCenter() {
143
- return this.center
144
- }
140
+ /** @returns {View} */
141
+ getView() { return this.view }
145
142
 
146
- /** @param {number} v The zoom factor (pixel size, in ground m) */
147
- setZf(v) {
148
- this.zf = v
149
- if (this.slider) this.slider.attr('value', +this.zf)
150
- }
151
- /** @returns {number} The zoom factor (pixel size, in ground m) */
152
- getZf() {
153
- return this.zf
154
- }
155
143
 
156
144
  /** @param {Array.<number>} v */
157
- setZfExtent(v) {
158
- this.zfExtent = v
145
+ setZoomExtent(v) {
146
+ this.zoomExtent = v
159
147
  }
160
148
  /** @returns {Array.<number>} */
161
- getZfExtent() {
162
- return this.zfExtent
149
+ getZoomExtent() {
150
+ return this.zoomExtent
163
151
  }
164
152
 
165
153
  /** Initialise canvas transform with identity transformation. */
@@ -169,22 +157,21 @@ export class GeoCanvas {
169
157
 
170
158
  /** Initialise canvas transform with geo to screen transformation, so that geo objects can be drawn directly in geo coordinates. */
171
159
  setCanvasTransform() {
172
- const k = 1 / this.getZf()
173
- const tx = -this.center.x / this.getZf() + this.w * 0.5
174
- const ty = this.center.y / this.getZf() + this.h * 0.5
160
+ const k = 1 / this.view.z
161
+ const tx = -this.view.x / this.view.z + this.w * 0.5
162
+ const ty = this.view.y / this.view.z + this.h * 0.5
175
163
  this.ctx.setTransform(k, 0, 0, -k, tx, ty)
176
164
  }
177
165
 
178
166
  /** Get the transformation matrix to webGL screen coordinates, within [-1,1]*[-1,1] */
179
167
  getWebGLTransform() {
180
- const kx = 2.0 / (this.w * this.getZf())
181
- const ky = 2.0 / (this.h * this.getZf())
182
- return [kx, 0.0, 0.0, 0.0, ky, 0.0, -kx * this.center.x, -ky * this.center.y, 1.0]
168
+ const kx = 2.0 / (this.w * this.view.z)
169
+ const ky = 2.0 / (this.h * this.view.z)
170
+ return [kx, 0.0, 0.0, 0.0, ky, 0.0, -kx * this.view.x, -ky * this.view.y, 1.0]
183
171
  }
184
172
 
185
- /** The function specifying how to draw the map.
186
- * @param {boolean} strong */
187
- redraw(strong = true) {
173
+ /** The function specifying how to draw the map. */
174
+ redraw() {
188
175
  throw new Error('Method redraw not implemented.')
189
176
  }
190
177
 
@@ -207,13 +194,13 @@ export class GeoCanvas {
207
194
  */
208
195
  pan(dxGeo = 0, dyGeo = 0) {
209
196
  //TODO force extend to remain
210
- this.center.x += dxGeo
211
- this.center.y += dyGeo
197
+ this.view.x += dxGeo
198
+ this.view.y += dyGeo
212
199
  this.updateExtentGeo()
213
200
 
214
201
  if (this.canvasSave.c) {
215
- this.canvasSave.dx -= dxGeo / this.getZf()
216
- this.canvasSave.dy += dyGeo / this.getZf()
202
+ this.canvasSave.dx -= dxGeo / this.view.z
203
+ this.canvasSave.dy += dyGeo / this.view.z
217
204
  this.clear(this.backgroundColor)
218
205
  // this doesnt work on mobile https://github.com/eurostat/gridviz/issues/98
219
206
  this.ctx.drawImage(this.canvasSave.c, this.canvasSave.dx, this.canvasSave.dy)
@@ -222,31 +209,29 @@ export class GeoCanvas {
222
209
 
223
210
  /**
224
211
  * Zoom.
225
- * @param {number} f The zoom factor, within ]0, Infinity]. 1 is for no change. <1 to zoom-in, >1 to zoom-out.
212
+ * @param {number} f The , within ]0, Infinity]. 1 is for no change. <1 to zoom-in, >1 to zoom-out.
226
213
  * @param {number} xGeo The x geo position fixed in the screen.
227
214
  * @param {number} yGeo The y geo position fixed in the screen.
228
215
  */
229
- zoom(f = 1, xGeo = this.center.x, yGeo = this.center.y) {
216
+ zoom(f = 1, xGeo = this.view.x, yGeo = this.view.y) {
230
217
  //TODO force geo extend to remain
231
218
 
232
219
  //trying to zoom in/out beyond limit
233
- if (this.zfExtent[0] == this.getZf() && f <= 1) return
234
- if (this.zfExtent[1] == this.getZf() && f >= 1) return
220
+ if (this.zoomExtent[0] == this.view.z && f <= 1) return
221
+ if (this.zoomExtent[1] == this.view.z && f >= 1) return
235
222
 
236
223
  //ensure zoom extent preserved
237
- const newZf = f * this.getZf()
238
- if (newZf < this.zfExtent[0]) f = this.zfExtent[0] / this.getZf()
239
- if (newZf > this.zfExtent[1]) f = this.zfExtent[1] / this.getZf()
240
-
241
- this.setZf(f * this.getZf())
242
- const dxGeo = (xGeo - this.center.x) * (1 - f)
243
- this.center.x += dxGeo
244
- const dyGeo = (yGeo - this.center.y) * (1 - f)
245
- this.center.y += dyGeo
224
+ const newZf = f * this.view.z
225
+ if (newZf < this.zoomExtent[0]) f = this.zoomExtent[0] / this.view.z
226
+ if (newZf > this.zoomExtent[1]) f = this.zoomExtent[1] / this.view.z
227
+
228
+ this.view.z *= f
229
+ const dxGeo = (xGeo - this.view.x) * (1 - f)
230
+ this.view.x += dxGeo
231
+ const dyGeo = (yGeo - this.view.y) * (1 - f)
232
+ this.view.y += dyGeo
246
233
  this.updateExtentGeo()
247
234
 
248
- //TODO
249
- //this.redraw(false)
250
235
  if (this.canvasSave.c) {
251
236
  this.clear(this.backgroundColor)
252
237
  this.canvasSave.f /= f
@@ -296,113 +281,38 @@ export class GeoCanvas {
296
281
  * @returns {number} Screen x coordinate, in pix.
297
282
  */
298
283
  geoToPixX(xGeo) {
299
- return (xGeo - this.center.x) / this.getZf() + this.w * 0.5
284
+ return (xGeo - this.view.x) / this.view.z + this.w * 0.5
300
285
  }
301
286
  /**
302
287
  * @param {number} yGeo Geo y coordinate, in m.
303
288
  * @returns {number} Screen y coordinate, in pix.
304
289
  */
305
290
  geoToPixY(yGeo) {
306
- return -(yGeo - this.center.y) / this.getZf() + this.h * 0.5
291
+ return -(yGeo - this.view.y) / this.view.z + this.h * 0.5
307
292
  }
308
293
  /**
309
294
  * @param {number} x Screen x coordinate, in pix.
310
295
  * @returns {number} Geo x coordinate, in m.
311
296
  */
312
297
  pixToGeoX(x) {
313
- return (x - this.w * 0.5) * this.getZf() + this.center.x
298
+ return (x - this.w * 0.5) * this.view.z + this.view.x
314
299
  }
315
300
  /**
316
301
  * @param {number} y Screen y coordinate, in pix.
317
302
  * @returns {number} Geo y coordinate, in m.
318
303
  */
319
304
  pixToGeoY(y) {
320
- return -(y - this.h * 0.5) * this.getZf() + this.center.y
305
+ return -(y - this.h * 0.5) * this.view.z + this.view.y
321
306
  }
322
307
 
323
- /** Get x,y,z elements from URL and assign them to the view center and zoom level. */
308
+ /** Get x,y,z elements from URL and assign them to the view. */
324
309
  setViewFromURL() {
325
310
  const x = GeoCanvas.getParameterByName('x'),
326
311
  y = GeoCanvas.getParameterByName('y'),
327
312
  z = GeoCanvas.getParameterByName('z')
328
- const c = this.getCenter()
329
- if (x != null && x != undefined && !isNaN(+x)) c.x = +x
330
- if (y != null && y != undefined && !isNaN(+y)) c.y = +y
331
- if (z != null && z != undefined && !isNaN(+z)) this.setZf(+z)
332
- }
333
-
334
- /**
335
- *
336
- * @param {string} id
337
- * @param {object} opts
338
- * @returns {this}
339
- */
340
- addZoomSlider(id, opts) {
341
- opts = opts || {}
342
- opts.width = opts.width || '30px'
343
- opts.height = opts.height || '300px'
344
-
345
- //the div element
346
- const div = select('#' + id)
347
- if (div.empty()) {
348
- console.error('Could not find div element to build zoom slider. Id: ' + id)
349
- return this
350
- }
351
-
352
- const th = this
353
- /** */
354
- this.slider = div
355
- .append('input')
356
- .attr('type', 'range')
357
- .attr('min', this.getZfExtent()[0])
358
- .attr('max', this.getZfExtent()[1])
359
- .attr('value', this.getZf())
360
- .on('input', function (d) {
361
- if (!this || !this.value) return
362
- const v = +this.value
363
- select(this).attr('value', v)
364
- th.setZf(v)
365
- //redraw
366
- th.redraw()
367
- })
368
- .style('width', opts.width)
369
- .style('height', opts.height)
370
- .style('opacity', 0.7)
371
- .on('mouseover', function (d) {
372
- select(this).style('opacity', 1)
373
- })
374
- .on('mouseout', function (d) {
375
- select(this).style('opacity', 0.7)
376
- })
377
- .style('-webkit-appearance', 'slider-vertical') //for chrome
378
- .style('writing-mode', 'bt-lr') //for IE/edge
379
- .attr('orient', 'vertical') //for firefox
380
- .style('background', 'lightgray')
381
- .style('outline', 'none')
382
- .style('-webkit-transition', '.2s')
383
- .style('transition', 'opacity .2s')
384
-
385
- //TODO
386
- /*select(".slider::-webkit-slider-thumb")
387
- .style("-webkit-appearance", "none")
388
- .style("appearance", "none")
389
- .style("width", "30px")
390
- .style("height", "40px")
391
- .style("background", "black")
392
- .style("cursor", "pointer")*/
393
-
394
- /*select("slider::-moz-range-thumb")
395
- .style("-webkit-appearance", "none")
396
- .style("width", "30px")
397
- .style("height", "40px")
398
- .style("background", "#04AA6D")
399
- .style("cursor", "pointer")*/
400
- /*
401
- .slider::ms-thumb,
402
- .slider::-moz-range-thumb {
403
- }*/
404
-
405
- return this
313
+ if (x != null && x != undefined && !isNaN(+x)) this.view.x = +x
314
+ if (y != null && y != undefined && !isNaN(+y)) this.view.y = +y
315
+ if (z != null && z != undefined && !isNaN(+z)) this.view.z = +z
406
316
  }
407
317
 
408
318
  /**