@lgv/visualization-map 0.0.1 → 0.0.3

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": "@lgv/visualization-map",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "type": "module",
5
5
  "description": "ES6 d3.js core visualization scaffold object and utilities for maps.",
6
6
  "main": "src/index.js",
@@ -12,7 +12,7 @@
12
12
  "build": "webpack build --config webpack.prod.js",
13
13
  "coverage": "c8 --reporter=html --reporter=text ava --node-arguments='--experimental-json-modules'",
14
14
  "start": "webpack serve --config webpack.dev.js",
15
- "startdocker": "webpack serve --config webpack.dev.js --host 0.0.0.0 --public 0.0.0.0",
15
+ "startdocker": "webpack serve --config webpack.dev.js --host 0.0.0.0 --allowed-hosts all",
16
16
  "test": "npx ava --verbose --serial --timeout 1m --node-arguments='--experimental-json-modules'"
17
17
  },
18
18
  "repository": {
@@ -36,13 +36,14 @@
36
36
  "css-loader": "^6.7.2",
37
37
  "html-webpack-plugin": "^5.3.2",
38
38
  "style-loader": "^3.3.1",
39
- "webpack-cli": "^4.7.2",
40
- "webpack-dev-server": "^3.11.2"
39
+ "webpack": "^5.86.0",
40
+ "webpack-cli": "^5.1.4",
41
+ "webpack-dev-server": "^4.15.1"
41
42
  },
42
43
  "dependencies": {
43
44
  "@deck.gl/core": "^8.8.20",
44
45
  "@deck.gl/layers": "^8.8.20",
45
- "@lgv/visualization-chart": "^0.3.5",
46
+ "@lgv/visualization-chart": "^0.3.21",
46
47
  "@msrvida/vega-deck.gl": "^3.3.4",
47
48
  "leaflet": "^1.9.2",
48
49
  "vega": "^5.22.1"
@@ -6,12 +6,13 @@ const configuration = {
6
6
  };
7
7
 
8
8
  const configurationColor = {
9
- p1: ["#eb4034","#34ebe5"]
9
+ p1: ["#0a86ca", "#0a86ca"],
10
+ p2: ["#F7BBB5", "#F37765", "#EF4223", "#992B20", "#37150E"]
10
11
  };
11
12
 
12
13
  const configurationLayout = {
13
14
  height: process.env.LGV_HEIGHT || 400,
14
- tileserver: process.env.LGV_TILESERVER || "https://stamen-tiles-{s}.a.ssl.fastly.net/toner-lite/{z}/{x}/{y}{r}.png",
15
+ tileserver: process.env.LGV_TILESERVER || "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
15
16
  width: process.env.LGV_WIDTH || 400
16
17
  }
17
18
 
@@ -30,21 +30,25 @@ class HeatLayout extends MapData {
30
30
 
31
31
  if (this.map && data.type == "FeatureCollection") {
32
32
 
33
- // project to xy
34
- result = data.features.map(d => {
35
-
36
- // convert to xy
37
- let xy = this.map.latLngToContainerPoint(L.latLng(d.geometry.coordinates[1], d.geometry.coordinates[0]));
33
+ let bounds = this.map.getBounds();
38
34
 
39
- return {
40
- count: d.properties.count,
41
- id: d.properties.id,
42
- label: d.properties.name,
43
- x: xy.x,
44
- y: xy.y
45
- }
46
-
47
- });
35
+ // project to xy
36
+ result = data.features
37
+ .filter(d => d.geometry.coordinates[1] >= bounds._southWest.lat && d.geometry.coordinates[1] <= bounds._northEast.lat && d.geometry.coordinates[0] >= bounds._southWest.lng && d.geometry.coordinates[0] <= bounds._northEast.lng)
38
+ .map(d => {
39
+
40
+ // convert to xy
41
+ let xy = this.map.latLngToContainerPoint(L.latLng(d.geometry.coordinates[1], d.geometry.coordinates[0]));
42
+
43
+ return {
44
+ count: d.properties.count,
45
+ id: d.properties.id,
46
+ label: d.properties.name,
47
+ x: xy.x,
48
+ y: xy.y
49
+ }
50
+
51
+ });
48
52
 
49
53
  }
50
54
 
@@ -11,7 +11,7 @@ import { HeatLayout as HL } from "../layout/heat.js";
11
11
  import { configuration, configurationColor, configurationLayout } from "../configuration.js";
12
12
 
13
13
  /**
14
- * VisualizationMap is a map abstraction.
14
+ * Heatmap is a map abstraction for a heatmap visualization.
15
15
  * @param {object} data - usually array; occasionally object
16
16
  * @param {string} label - identifer for chart brand
17
17
  * @param {float} height - specified height of chart
@@ -43,7 +43,7 @@ class Heatmap extends VisualizationMap {
43
43
  let isHighDensityDisplay = window.devicePixelRatio > 1 && window.innerWidth * window.devicePixelRatio > 3360 && window.innerHeight * window.devicePixelRatio > 1942;
44
44
 
45
45
  // set starting size
46
- let base = isHighDensityDisplay ? this.unit * 1.75 : this.unit * 1.5;
46
+ let base = isHighDensityDisplay ? this.unit * 1.75 : this.unit * 2;
47
47
  let mark = base;
48
48
 
49
49
  // determine mark size
@@ -72,12 +72,7 @@ class Heatmap extends VisualizationMap {
72
72
  /**
73
73
  * Generate main visualization, i.e. everything that sits inside the map.
74
74
  */
75
- generateChart(e) {
76
-
77
- // process data
78
- // update data object now that map exists
79
- this.Data.map = this.map;
80
- this.data = this.Data.update;
75
+ generateChart() {
81
76
 
82
77
  // generate vega specification
83
78
  let spec = this.generateSpecification();
@@ -123,11 +118,11 @@ class Heatmap extends VisualizationMap {
123
118
  return {
124
119
  width: this.width,
125
120
  height: this.height,
126
- data: [{ name: this.name, values: this.data }],
121
+ data: [{ name: this.name, values: this.isXY ? this.data.results : this.data }],
127
122
  scales: [
128
123
  {
129
124
  name: "color",
130
- type: "log",
125
+ type: "linear",
131
126
  domain: { data: this.name, field: "count" },
132
127
  range: { scheme: this.name, count: configurationColor.p1.length }
133
128
  }
@@ -143,6 +138,7 @@ class Heatmap extends VisualizationMap {
143
138
  y: { field: "y" },
144
139
  size: { value: this.mark },
145
140
  fill: { scale: "color", field: "count" },
141
+ opacity: { value: 1 }
146
142
  },
147
143
  },
148
144
  }
@@ -21,8 +21,16 @@ class VisualizationMap extends LinearGrid {
21
21
  // initialize inheritance
22
22
  super(data, width, height, MapData, label, name);
23
23
 
24
+ // determine type of data object
25
+ this.determineDataType();
26
+
27
+ // determine x/y center
28
+ let x = this.isFeatureCollection ? mean(this.data.features, d => d.geometry.coordinates[1]) : (this.isPoint ? this.data.coordinates[1] : (this.isXY ? mean([this.data.metadata.bounds[0][1],this.data.metadata.bounds[1][1]]) : 0));
29
+
30
+ let y = this.isFeatureCollection ? mean(this.data.features, d => d.geometry.coordinates[0]) : (this.isPoint ? this.data.coordinates[0] : (this.isXY ? mean([this.data.metadata.bounds[0][0],this.data.metadata.bounds[1][0]]) : 0));
31
+
24
32
  // update self
25
- this.center = [mean(this.data.features, d => d.geometry.coordinates[1]), mean(this.data.features, d => d.geometry.coordinates[0])];
33
+ this.center = [x,y];
26
34
  this.map = null;
27
35
  this.tileserver = configurationLayout.tileserver;
28
36
  this.zoom = 1;
@@ -30,12 +38,46 @@ class VisualizationMap extends LinearGrid {
30
38
  }
31
39
 
32
40
  /**
33
- * Generate main visualization, i.e. everything that sits inside the svg.
41
+ * Attach event handlers for map events.
34
42
  */
35
- generateChart() {
43
+ attachEvents() {
44
+ this.map.on("moveend", e => {
45
+
46
+ // update dimensions on class
47
+ let containerSize = this.map.getSize();
48
+ this.heightSpecified = containerSize.y;
49
+ this.widthSpecified = containerSize.x;
50
+
51
+ // process data
52
+ // update data object now that map exists
53
+ this.Data.map = this.map;
54
+ if (this.Data.data) this.data = this.Data.update;
55
+
56
+ // render overlay
57
+ this.generateChart();
58
+
59
+ // broadcast event
60
+ this.container.dispatch("map-change", {
61
+ bubbles: true
62
+ });
63
+
64
+ });
65
+ }
36
66
 
37
- //
67
+ /**
68
+ * Generate main visualization, i.e. everything that sits inside the svg.
69
+ */
70
+ determineDataType() {
71
+ this.isFeatureCollection = this.data && this.data.features;
72
+ this.isPoint = this.data && this.data.coordinates && this.data.type.lower() == "point";
73
+ this.isXY = this.data && this.data.metadata && this.data.results;
74
+ }
38
75
 
76
+ /**
77
+ * Generate main visualization, i.e. everything that sits inside the svg.
78
+ */
79
+ generateChart() {
80
+ // empty method for specific map visualizations to overwrite
39
81
  }
40
82
 
41
83
  /**
@@ -43,21 +85,59 @@ class VisualizationMap extends LinearGrid {
43
85
  */
44
86
  generateCore() {
45
87
 
46
- // determine averaged center of coordinates
47
- // without curvature of earth accounted for
88
+ // pull dom element styles
89
+ let styles = window.getComputedStyle(this.container.node());
90
+ let height = parseFloat(styles.height.split("px")[0]);
91
+ let width = parseFloat(styles.width.split("px")[0]);
92
+
93
+ // check for unset width or height
94
+ if (height == 0 || width == 0) {
95
+
96
+ // add dom element attribute
97
+ this.container.node().setAttribute(`data-${this.branding}`, this.name);
98
+
99
+ // generate css for account for unset dimension(s)
100
+ let property = `[data-${this.branding}="${this.name}"]`;
101
+ let value = width == 0 && height == 0 ? `{ height: ${this.height}px; width: ${this.width}px; }` : (width == 0 ? `{ width: ${this.width}px; }` : `{ height: ${this.height}px; }`);
102
+
103
+ // generate stylesheet and insert in document
104
+ let style = document.createElement("style");
105
+ style.type = "text/css";
106
+ style.append(document.createTextNode(`${property} ${value}`));
107
+ document.head.append(style);
108
+
109
+ }
110
+
111
+ // update stored dimensions
112
+ if (height > 0) this.heightSpecified = height;
113
+ if (width > 0) this.widthSpecified = width;
114
+
115
+ // initialize map
48
116
  this.map = L.map(this.container.node(), {
49
117
  center: this.center,
50
118
  zoom: this.zoom
51
119
  });
52
120
 
53
121
  // add handlers for leaflet events
54
- this.map.on("moveend", this.generateChart.bind(this));
122
+ this.attachEvents();
55
123
 
56
124
  // update tile server
57
125
  L.tileLayer(this.tileserver).addTo(this.map);
58
126
 
59
- // fit to center + distance
60
- this.map.fitBounds(L.geoJSON(this.Data.source).getBounds());
127
+ // determine bounding box
128
+ let bounds = (this.isFeatureCollection || this.isPoint) ? L.geoJSON(this.Data.source).getBounds() : (this.isXY ? this.data.metadata.bounds : this.map.getBounds());
129
+
130
+ // fit to bounds
131
+ if (this.isFeatureCollection || this.isXY) {
132
+ // several data points
133
+ this.map.fitBounds(bounds);
134
+ } else if (this.isPoint) {
135
+ // zoom out if only provided single data point
136
+ this.map.setView(bounds.getCenter(), this.map.getMaxZoom() / 2);
137
+ } else {
138
+ // no geo data provided so center world
139
+ this.map.setView(bounds.getCenter());
140
+ }
61
141
 
62
142
  }
63
143
 
@@ -74,6 +154,40 @@ class VisualizationMap extends LinearGrid {
74
154
 
75
155
  }
76
156
 
157
+ /**
158
+ * Update visualization.
159
+ * @param {object} data - geojson
160
+ * @param {float} height - specified height of chart
161
+ * @param {float} width - specified width of chart
162
+ */
163
+ update(data, width, height) {
164
+
165
+ // update self
166
+ this.heightSpecified = height || this.heightSpecified;
167
+ this.widthSpecified = width || this.widthSpecified;
168
+
169
+ // if layout data object
170
+ if (this.Data.height && this.Data.width) {
171
+
172
+ // update layout attributes
173
+ this.Data.height = this.heightSpecified;
174
+ this.Data.width = this.widthSpecified;
175
+
176
+ }
177
+
178
+ // recalculate values
179
+ this.Data.source = data;
180
+ this.Data.data = this.Data.update;
181
+ this.data = this.Data.data;
182
+
183
+ // determine type of data object
184
+ this.determineDataType();
185
+
186
+ // generate visualization
187
+ this.generateChart();
188
+
189
+ }
190
+
77
191
  }
78
192
 
79
193
  export { VisualizationMap };