@lgv/visualization-map 0.0.8 → 1.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.
Binary file
@@ -0,0 +1,78 @@
1
+ <html>
2
+
3
+ <head>
4
+ <title>Map</title>
5
+ <link rel="icon" type="image/x-icon" href="favicon.ico">
6
+ <link href="style.css" rel="stylesheet" />
7
+ <script src="main.js" defer="defer" type="module"></script>
8
+ </head>
9
+
10
+ <body>
11
+
12
+ <header>
13
+
14
+ <hgroup>
15
+ <h1>Map</h1>
16
+ <p>@lgv</p>
17
+ </hgroup>
18
+
19
+ <div>
20
+ <a target="_blank" href="https://gitlab.com/lgensinger/visualization-map">git</a>
21
+ <a target="_blank" href="https://www.npmjs.com/package/@lgv/visualization-map">npm</a>
22
+ </div>
23
+
24
+ </header>
25
+
26
+ <main>
27
+
28
+ <figure></figure>
29
+
30
+ <section>
31
+ <h1>Source Data</h1>
32
+ <p>Minimal required to render visualization.</p>
33
+ <pre><code></code></pre>
34
+ </section>
35
+
36
+ <section>
37
+ <h1>Events</h1>
38
+ <table>
39
+ <thead>
40
+ <th>element</th>
41
+ <th>name</th>
42
+ <th>description</th>
43
+ </thead>
44
+ <tbody>
45
+ <tr>
46
+ <td>circle</td>
47
+ <td>bubble-click</td>
48
+ <td>onclick for an individual svg circle element</td>
49
+ </tr>
50
+ <tr>
51
+ <td>circle</td>
52
+ <td>bubble-mouseover</td>
53
+ <td>mouseover for an individual svg circle element</td>
54
+ </tr>
55
+ <tr>
56
+ <td>circle</td>
57
+ <td>bubbble-mouseout</td>
58
+ <td>move mouse outside the bounds of an svg circle element to clear the mouseover</td>
59
+ </tr>
60
+ </tbody>
61
+ </table>
62
+ <pre><code></code></pre>
63
+ </section>
64
+
65
+ </main>
66
+
67
+ <footer>
68
+ <a target="_blank" href="https://gitlab.com/lgensinger">
69
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 380"><defs><style>.cls-1{fill:#171321;}</style></defs><g id="LOGO"><path class="cls-1" d="M282.83,170.73l-.27-.69-26.14-68.22a6.81,6.81,0,0,0-2.69-3.24,7,7,0,0,0-8,.43,7,7,0,0,0-2.32,3.52l-17.65,54H154.29l-17.65-54A6.86,6.86,0,0,0,134.32,99a7,7,0,0,0-8-.43,6.87,6.87,0,0,0-2.69,3.24L97.44,170l-.26.69a48.54,48.54,0,0,0,16.1,56.1l.09.07.24.17,39.82,29.82,19.7,14.91,12,9.06a8.07,8.07,0,0,0,9.76,0l12-9.06,19.7-14.91,40.06-30,.1-.08A48.56,48.56,0,0,0,282.83,170.73Z"/></g></svg>
70
+ </a>
71
+ <a target="_blank" href="https://github.com/lgensinger">
72
+ <svg viewBox="0 0 98 96" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z"></svg>
73
+ </a>
74
+ </footer>
75
+
76
+ </body>
77
+
78
+ </html>
@@ -0,0 +1,143 @@
1
+ import packageJson from "../package.json";
2
+ import { VisualizationMap } from "../src/index.js";
3
+ import { processEvent, renderDefault } from "@lgv/visualization-chart";
4
+
5
+ let data = {
6
+ "type": "FeatureCollection",
7
+ "features": [
8
+ {
9
+ "type": "Feature",
10
+ "properties": {},
11
+ "geometry": {
12
+ "coordinates": [
13
+ [
14
+ [
15
+ -0.18311949782085435,
16
+ 51.524112572236476
17
+ ],
18
+ [
19
+ -0.19328128598888838,
20
+ 51.511578089614886
21
+ ],
22
+ [
23
+ -0.18542083599513148,
24
+ 51.4978346986118
25
+ ],
26
+ [
27
+ -0.15282841947237102,
28
+ 51.505455119848165
29
+ ],
30
+ [
31
+ -0.1558361451737369,
32
+ 51.52401252514309
33
+ ],
34
+ [
35
+ -0.18311949782085435,
36
+ 51.524112572236476
37
+ ]
38
+ ]
39
+ ],
40
+ "type": "Polygon"
41
+ }
42
+ },
43
+ {
44
+ "type": "Feature",
45
+ "properties": {},
46
+ "geometry": {
47
+ "coordinates": [
48
+ [
49
+ [
50
+ -0.11372536280845225,
51
+ 51.495850326660104
52
+ ],
53
+ [
54
+ -0.11372536280845225,
55
+ 51.484758714307475
56
+ ],
57
+ [
58
+ -0.09431987304824929,
59
+ 51.484758714307475
60
+ ],
61
+ [
62
+ -0.09431987304824929,
63
+ 51.495850326660104
64
+ ],
65
+ [
66
+ -0.11372536280845225,
67
+ 51.495850326660104
68
+ ]
69
+ ]
70
+ ],
71
+ "type": "Polygon"
72
+ }
73
+ },
74
+ {
75
+ "type": "Feature",
76
+ "properties": {},
77
+ "geometry": {
78
+ "coordinates": [
79
+ [
80
+ [
81
+ -0.1074645619268324,
82
+ 51.52894445636778
83
+ ],
84
+ [
85
+ -0.10883628720753791,
86
+ 51.52166505153471
87
+ ],
88
+ [
89
+ -0.10013275748593742,
90
+ 51.52599979138691
91
+ ],
92
+ [
93
+ -0.1074645619268324,
94
+ 51.52894445636778
95
+ ]
96
+ ]
97
+ ],
98
+ "type": "Polygon"
99
+ }
100
+ }
101
+ ]
102
+ };
103
+
104
+ // get elements
105
+ let container = document.getElementsByTagName("figure")[0];
106
+ let sourceContainer = document.getElementsByTagName("code")[0];
107
+ let outputContainer = document.getElementsByTagName("code")[1];
108
+
109
+ /**
110
+ * Render initial visualization.
111
+ * @param {element} container - DOM element
112
+ * @param {array} data - object with key/value pairs of path
113
+ */
114
+ function startup(data,container) {
115
+
116
+ // update version in header
117
+ let h1 = document.querySelector("h1");
118
+ let title = h1.innerText;
119
+ h1.innerHTML = `${title} <span>v${packageJson.version}</span>`;
120
+
121
+ // render source data
122
+ renderDefault(data,sourceContainer,outputContainer);
123
+
124
+ // determine configs
125
+ let width = container.offsetWidth;
126
+ let height = width*0.75;
127
+
128
+ // initialize
129
+ const m = new VisualizationMap(null,data,width,height);
130
+
131
+ // render visualization
132
+ m.render(container);
133
+
134
+ }
135
+
136
+ // load document
137
+ document.onload = startup(data,container);
138
+
139
+ // attach events
140
+ container.outputContainer = outputContainer;
141
+ //container.addEventListener("bubble-click",processEvent);
142
+ //container.addEventListener("bubble-mouseover", processEvent);
143
+ //container.addEventListener("bubble-mouseout", processEvent);
@@ -0,0 +1,55 @@
1
+ @import "~highlight.js/styles/atom-one-dark.css";
2
+ @import "~@lgv/visualization-chart/src/example/style/index.css";
3
+
4
+ main > hgroup {
5
+ align-items: center;
6
+ display: flex;
7
+ justify-content: space-between;
8
+ margin-bottom: 1em;
9
+ }
10
+
11
+ main > hgroup h1 {
12
+ font-size: 1.5em;
13
+ font-weight: 700;
14
+ margin-right: 1em;
15
+ }
16
+
17
+ main hgroup div {
18
+ display: flex;
19
+ }
20
+
21
+ main button {
22
+ border: 0.2em;
23
+ border-style: solid;
24
+ cursor: pointer;
25
+ font-size: 0.75em;
26
+ padding: 0.5em;
27
+ text-transform: uppercase;
28
+ }
29
+
30
+ main hgroup div button {
31
+ border: 0 none;
32
+ background: #525252;
33
+ border-radius: 1em;
34
+ color: white;
35
+ text-transform: none;
36
+ }
37
+
38
+ main hgroup div button:first-of-type {
39
+ border-bottom-right-radius: 0;
40
+ border-top-right-radius: 0;
41
+ }
42
+
43
+ main hgroup div button:last-of-type {
44
+ background: #292929;
45
+ border-bottom-left-radius: 0;
46
+ border-top-left-radius: 0;
47
+ }
48
+
49
+ main button:hover {
50
+ background: grey;
51
+ }
52
+
53
+ main figure {
54
+ max-width: 100%;
55
+ }
@@ -0,0 +1,38 @@
1
+ import path from "path";
2
+ import { dirname } from "path";
3
+ import { fileURLToPath } from "url";
4
+
5
+ import HtmlBundlerPlugin from "html-bundler-webpack-plugin";
6
+
7
+ const __dirname = dirname(fileURLToPath(import.meta.url));
8
+
9
+ const commonConfig = {
10
+
11
+ module: {
12
+ rules: [
13
+ {
14
+ test: /\.(ico|png|jp?g|svg)/,
15
+ type: "asset",
16
+ generator: { filename: "img/[name].[hash:8][ext]" },
17
+ parser: { dataUrlCondition: { maxSize: 2 * 1024 } }
18
+ }
19
+ ]
20
+ },
21
+
22
+ output: {
23
+ path: path.resolve(__dirname, "dist"),
24
+ clean: true
25
+ },
26
+
27
+ plugins: [
28
+ new HtmlBundlerPlugin({
29
+ entry: { index: "./example/index.html" },
30
+ js: { filename: "[name].[contenthash:8].js" },
31
+ css: { filename: "[name].[contenthash:8].css" }
32
+ })
33
+ ]
34
+
35
+ };
36
+
37
+ export { commonConfig };
38
+ export default commonConfig;
@@ -0,0 +1,42 @@
1
+ import path from "path";
2
+ import { dirname } from "path";
3
+ import { fileURLToPath } from "url";
4
+
5
+ import { merge } from "webpack-merge";
6
+
7
+ const __dirname = dirname(fileURLToPath(import.meta.url));
8
+
9
+ import { commonConfig as visConfig } from "../webpack.common.js";
10
+ import { commonConfig } from "./webpack.common.js";
11
+
12
+ const webpackConfig = merge(visConfig, commonConfig, {
13
+
14
+ devServer: {
15
+ allowedHosts: ["all"],
16
+ client: {
17
+ webSocketURL: {
18
+ hostname: "0.0.0.0",
19
+ pathname: "/ws",
20
+ password: "dev-server",
21
+ port: 8080,
22
+ protocol: "ws",
23
+ username: "webpack",
24
+ }
25
+ },
26
+ static: {
27
+ directory: path.resolve(__dirname, "dist"),
28
+ publicPath: "/"
29
+ },
30
+ watchFiles: {
31
+ paths: ["example/**/*.*"],
32
+ options: { usePolling: true },
33
+ },
34
+ webSocketServer: "ws"
35
+ },
36
+
37
+ mode: "development"
38
+
39
+ });
40
+
41
+ export { webpackConfig };
42
+ export default webpackConfig;
@@ -0,0 +1,19 @@
1
+ import { commonConfig } from "./webpack.common.js";
2
+
3
+ import { merge } from "webpack-merge";
4
+
5
+ const webpackConfig = merge(commonConfig, {
6
+
7
+ mode: "production",
8
+
9
+ output: {
10
+ library: {
11
+ name: "MapExample",
12
+ type: "umd"
13
+ }
14
+ }
15
+
16
+ });
17
+
18
+ export { webpackConfig };
19
+ export default webpackConfig;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lgv/visualization-map",
3
- "version": "0.0.8",
3
+ "version": "1.0.0",
4
4
  "type": "module",
5
5
  "description": "ES6 d3.js core visualization scaffold object and utilities for maps.",
6
6
  "main": "src/index.js",
@@ -10,9 +10,10 @@
10
10
  },
11
11
  "scripts": {
12
12
  "build": "webpack build --config webpack.prod.js",
13
+ "build-example": "webpack build --config example/webpack.dev.js",
13
14
  "coverage": "c8 --reporter=html --reporter=text ava --node-arguments='--experimental-json-modules'",
15
+ "example": "webpack serve --config example/webpack.dev.js",
14
16
  "start": "webpack serve --config webpack.dev.js",
15
- "startdocker": "webpack serve --config webpack.dev.js --host 0.0.0.0 --allowed-hosts all",
16
17
  "test": "npx ava --verbose --serial --timeout 1m --node-arguments='--experimental-json-modules'"
17
18
  },
18
19
  "repository": {
@@ -20,8 +21,10 @@
20
21
  "url": "https://gitlab.com/lgensinger/visualization-map.git"
21
22
  },
22
23
  "keywords": [
23
- "visualization",
24
- "d3"
24
+ "d3",
25
+ "leaflet",
26
+ "vega",
27
+ "visualization"
25
28
  ],
26
29
  "author": "lgensinger",
27
30
  "license": "MIT",
@@ -30,25 +33,23 @@
30
33
  },
31
34
  "homepage": "https://gitlab.com/lgensinger/visualization-map#readme",
32
35
  "devDependencies": {
36
+ "@fontsource/inter": "^5.1.0",
33
37
  "ava": "^4.2.0",
34
38
  "browser-env": "^3.3.0",
35
39
  "c8": "^7.11.3",
36
- "css-loader": "^6.7.2",
40
+ "css-loader": "^7.1.2",
37
41
  "file-loader": "^6.2.0",
42
+ "highlight.js": "^11.10.0",
43
+ "html-bundler-webpack-plugin": "^4.4.1",
38
44
  "html-webpack-plugin": "^5.3.2",
39
45
  "style-loader": "^3.3.1",
40
46
  "webpack": "^5.86.0",
41
47
  "webpack-cli": "^5.1.4",
42
- "webpack-dev-server": "^4.15.1"
48
+ "webpack-dev-server": "^5.0.4"
43
49
  },
44
50
  "dependencies": {
45
- "@deck.gl/core": "^8.8.20",
46
- "@deck.gl/layers": "^8.8.20",
47
- "@lgv/visualization-chart": "^0.3.26",
48
- "@msrvida/vega-deck.gl": "^3.3.4",
49
- "leaflet": "^1.9.2",
50
- "leaflet.markercluster": "^1.5.3",
51
- "vega": "^5.22.1"
51
+ "@lgv/visualization-chart": "^1.0.3",
52
+ "leaflet": "^1.9.4"
52
53
  },
53
54
  "ava": {
54
55
  "files": [
@@ -5,16 +5,11 @@ const configuration = {
5
5
  name: packagejson.name.replace("/", "-").slice(1)
6
6
  };
7
7
 
8
- const configurationColor = {
9
- p1: ["#0a86ca", "#0a86ca"],
10
- p2: ["#F7BBB5", "#F37765", "#EF4223", "#992B20", "#37150E"]
11
- };
12
-
13
8
  const configurationLayout = {
14
9
  height: process.env.LGV_HEIGHT || 400,
15
10
  tileserver: process.env.LGV_TILESERVER || "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
16
11
  width: process.env.LGV_WIDTH || 400
17
12
  }
18
13
 
19
- export { configuration, configurationColor, configurationLayout };
14
+ export { configuration, configurationLayout };
20
15
  export default configuration;
package/src/index.js CHANGED
@@ -1,11 +1,9 @@
1
- import { HeatLayout } from "./layout/heat.js";
1
+ import { configurationLayout } from "./configuration.js";
2
+
2
3
  import { MapLayout } from "./layout/map.js";
3
4
 
4
5
  import { MapData } from "./structure/index.js";
5
6
 
6
- import { Clustermap } from "./visualization/cluster.js";
7
- import { Heatmap } from "./visualization/heatmap.js";
8
- import { Routemap } from "./visualization/route.js";
9
7
  import { VisualizationMap } from "./visualization/index.js";
10
8
 
11
- export { Clustermap, HeatLayout, Heatmap, MapData, MapLayout, Routemap, VisualizationMap };
9
+ export { configurationLayout, MapData, MapLayout, VisualizationMap };
@@ -1,15 +1,16 @@
1
- import { LinearGrid } from "@lgv/visualization-chart";
1
+ import { select } from "d3-selection";
2
+ import { debounce, LinearGrid } from "@lgv/visualization-chart";
2
3
  import { mean } from "d3-array";
3
4
  import L from "leaflet";
4
5
  import "leaflet/dist/leaflet.css";
5
6
  import "leaflet/dist/images/marker-shadow.png";
6
- import "./marker-cluster.css";
7
7
 
8
8
  import { configuration, configurationLayout } from "../configuration.js";
9
9
 
10
10
  /**
11
11
  * VisualizationMap is a map abstraction.
12
12
  * @param {object} data - usually array; occasionally object
13
+ * @param {node} dom - HTML dom node
13
14
  * @param {string} label - identifer for chart brand
14
15
  * @param {float} height - specified height of chart
15
16
  * @param {class} MapData - lgv data class
@@ -18,17 +19,14 @@ import { configuration, configurationLayout } from "../configuration.js";
18
19
  */
19
20
  class VisualizationMap extends LinearGrid {
20
21
 
21
- constructor(data, width=configurationLayout.width, height=configurationLayout.height, MapData=null, tileServerUrl=configurationLayout.tileserver, label=configuration.branding, name=configuration.name) {
22
+ constructor(dom=null, data, width=configurationLayout.width, height=configurationLayout.height, MapData=null, tileServerUrl=configurationLayout.tileserver, label=configuration.branding, name=configuration.name) {
22
23
 
23
24
  // initialize inheritance
24
25
  super(data, width, height, MapData, label, name);
25
26
 
26
- // determine type of data object
27
- this.determineDataType();
28
-
29
27
  // determine x/y center
30
- let x = this.isFeatureCollection ? mean(this.data.features, d => typeof(d.geometry.coordinates[0]) == "object" ? mean(d.geometry.coordinates.map(x => x[1]).flat()) : 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));
31
- let y = this.isFeatureCollection ? mean(this.data.features, d => typeof(d.geometry.coordinates[0]) == "object" ? mean(d.geometry.coordinates.map(x => x[0]).flat()) : 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));
28
+ let x = mean(this.data.features, d => typeof(d.geometry.coordinates[0]) == "object" ? mean(d.geometry.coordinates.map(x => x[1]).flat()) : d.geometry.coordinates[1]);
29
+ let y = mean(this.data.features, d => typeof(d.geometry.coordinates[0]) == "object" ? mean(d.geometry.coordinates.map(x => x[0]).flat()) : d.geometry.coordinates[0]);
32
30
 
33
31
  // update self
34
32
  this.center = [x,y];
@@ -36,42 +34,30 @@ class VisualizationMap extends LinearGrid {
36
34
  this.tileServerUrl = tileServerUrl;
37
35
  this.zoom = 1;
38
36
 
37
+ // if dom node provided set as the map container
38
+ if (dom) this.container = select(dom);
39
+
39
40
  }
40
41
 
41
42
  /**
42
- * Attach event handlers for map events.
43
+ * Determine if data is geojson feature collection.
43
44
  */
44
- attachEvents() {
45
- this.map.on("moveend", e => {
46
-
47
- // update dimensions on class
48
- let containerSize = this.map.getSize();
49
- this.heightSpecified = containerSize.y;
50
- this.widthSpecified = containerSize.x;
51
-
52
- // process data
53
- // update data object now that map exists
54
- this.Data.map = this.map;
55
- if (this.Data.data) this.data = this.Data.update;
56
-
57
- // render overlay
58
- this.generateChart();
59
-
60
- // broadcast event
61
- this.container.dispatch("map-change", {
62
- bubbles: true
63
- });
45
+ get isFeatureCollection() {
46
+ return this.data !== (null || undefined) && this.Data.source.features !== (null || undefined);
47
+ }
64
48
 
65
- });
49
+ /**
50
+ * Determine if data is geojson point.
51
+ */
52
+ get isPoint() {
53
+ return this.data && this.data.coordinates && this.data.type.lower() == "point";
66
54
  }
67
55
 
68
56
  /**
69
- * Generate main visualization, i.e. everything that sits inside the svg.
57
+ * Attach event handlers for map events.
70
58
  */
71
- determineDataType() {
72
- this.isFeatureCollection = this.data !== (null || undefined) && this.Data.source.features !== (null || undefined);
73
- this.isPoint = this.data && this.data.coordinates && this.data.type.lower() == "point";
74
- this.isXY = this.data && this.data.metadata && this.data.results;
59
+ attachEvents() {
60
+ this.map.on("moveend", debounce(() => this.processMoveEnd(),2000));
75
61
  }
76
62
 
77
63
  /**
@@ -80,10 +66,10 @@ class VisualizationMap extends LinearGrid {
80
66
  fitToData() {
81
67
 
82
68
  // determine bounding box
83
- let bounds = (this.isFeatureCollection || this.isPoint) ? L.geoJSON(this.Data.source).getBounds() : (this.isXY ? this.data.metadata.bounds : this.map.getBounds());
69
+ let bounds = (this.isFeatureCollection || this.isPoint) ? L.geoJSON(this.Data.source).getBounds() : this.map.getBounds();
84
70
 
85
71
  // fit to bounds
86
- if (this.isFeatureCollection || this.isXY) {
72
+ if (this.isFeatureCollection) {
87
73
  // several data points
88
74
  this.map.fitBounds(bounds);
89
75
  } else if (this.isPoint) {
@@ -140,15 +126,9 @@ class VisualizationMap extends LinearGrid {
140
126
  zoom: this.zoom
141
127
  });
142
128
 
143
- // add handlers for leaflet events
144
- this.attachEvents();
145
-
146
129
  // update tile server
147
130
  L.tileLayer(this.tileServerUrl).addTo(this.map);
148
131
 
149
- // fit to bounds
150
- this.fitToData();
151
-
152
132
  }
153
133
 
154
134
  /**
@@ -162,6 +142,16 @@ class VisualizationMap extends LinearGrid {
162
142
  // generate patterns
163
143
  this.generatePatterns();
164
144
 
145
+ // add handlers for leaflet events
146
+ this.attachEvents();
147
+
148
+ }
149
+
150
+ /**
151
+ * Callback for map event on movened.
152
+ */
153
+ processMoveEnd() {
154
+ this.updateMap();
165
155
  }
166
156
 
167
157
  /**
@@ -185,19 +175,52 @@ class VisualizationMap extends LinearGrid {
185
175
 
186
176
  }
187
177
 
178
+ // update data in class
179
+ this.updateData(data);
180
+
181
+ // generate visualization
182
+ this.generateChart();
183
+
184
+ // fit to bounds
185
+ this.fitToData();
186
+
187
+ }
188
+
189
+ /**
190
+ * Update visualization.
191
+ * @param {object} data - geojson
192
+ */
193
+ updateData(data) {
194
+
188
195
  // recalculate values
189
196
  this.Data.source = data;
190
197
  this.Data.data = this.Data.update;
191
198
  this.data = this.Data.data;
192
199
 
193
- // determine type of data object
194
- this.determineDataType();
200
+ }
195
201
 
196
- // generate visualization
202
+ /**
203
+ * Update map state.
204
+ */
205
+ updateMap() {
206
+ console.log("FIRE!")
207
+ // update dimensions on class
208
+ let containerSize = this.map.getSize();
209
+ this.heightSpecified = containerSize.y;
210
+ this.widthSpecified = containerSize.x;
211
+
212
+ // process data
213
+ // update data object now that map exists
214
+ this.Data.map = this.map;
215
+ if (this.Data.data) this.data = this.Data.update;
216
+
217
+ // render overlay
197
218
  this.generateChart();
198
219
 
199
- // fit to bounds
200
- this.fitToData();
220
+ // broadcast event
221
+ this.container.dispatch("map-change", {
222
+ bubbles: true
223
+ });
201
224
 
202
225
  }
203
226
 
package/webpack.common.js CHANGED
@@ -32,7 +32,7 @@ const commonConfig = {
32
32
  rules: [
33
33
  {
34
34
  test: /\.css$/i,
35
- use: ["style-loader", "css-loader"]
35
+ use: ["css-loader"]
36
36
  },
37
37
  {
38
38
  test: /\.(png|jpe?g|gif)$/i,
@@ -1,59 +0,0 @@
1
- import L from "leaflet";
2
-
3
- import { MapLayout } from "./map.js";
4
-
5
- /**
6
- * HeatLayout is a data-bound layout abstraction for a heatmap.
7
- * @param {array} data - geojson
8
- * @param {class} map - Leaflet map object
9
- */
10
- class HeatLayout extends MapLayout {
11
-
12
- constructor(data, map) {
13
-
14
- // initialize inheritance
15
- super(data);
16
-
17
- }
18
-
19
- /**
20
- * Condition data for visualization requirements.
21
- * @param {array} data - geojson
22
- * @returns An object or array of data specifically formatted to the visualization type.
23
- */
24
- condition(data) {
25
-
26
- let result = data;
27
-
28
- if (this.map && data.type == "FeatureCollection") {
29
-
30
- let bounds = this.map.getBounds();
31
-
32
- // project to xy
33
- result = data.features
34
- .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)
35
- .map(d => {
36
-
37
- // convert to xy
38
- let xy = this.map.latLngToContainerPoint(L.latLng(d.geometry.coordinates[1], d.geometry.coordinates[0]));
39
-
40
- return {
41
- count: d.properties.count,
42
- id: d.properties.id,
43
- label: d.properties.name,
44
- x: xy.x,
45
- y: xy.y
46
- }
47
-
48
- });
49
-
50
- }
51
-
52
- return result;
53
-
54
- }
55
-
56
- }
57
-
58
- export { HeatLayout };
59
- export default HeatLayout;
@@ -1,63 +0,0 @@
1
- import L from "leaflet";
2
- import MarkerClusterGroup from "leaflet.markercluster";
3
- import defaultIcon from "leaflet/dist/images/marker-icon.png";
4
-
5
- import { VisualizationMap } from "./index.js";
6
- import { MapLayout as ML } from "../layout/map.js";
7
-
8
- import { configuration, configurationLayout } from "../configuration.js";
9
-
10
- /**
11
- * Clustermap is a map abstraction for a clustered visualization.
12
- * @param {object} data - usually array; occasionally object
13
- * @param {string} label - identifer for chart brand
14
- * @param {float} height - specified height of chart
15
- * @param {object} markerIcon - Leaflet object
16
- * @param {class} MapData - lgv data class
17
- * @param {string} name - name of chart
18
- * @param {float} width - specified width of chart
19
- */
20
- class Clustermap extends VisualizationMap {
21
-
22
- constructor(data, width=configurationLayout.width, height=configurationLayout.height, MapLayout=null, markerIcon=L.icon({iconUrl: defaultIcon}), tileServerUrl=configurationLayout.tileserver, label=configuration.branding, name=configuration.name) {
23
-
24
- // initialize inheritance
25
- super(data, width, height, MapLayout ? MapLayout : new ML(data), tileServerUrl, label, name);
26
-
27
- // set default icon
28
- L.Marker.prototype.setIcon(markerIcon);
29
-
30
- // initialize marker group
31
- this.markerClusterGroup = L.markerClusterGroup();
32
-
33
- }
34
-
35
- /**
36
- * Generate main visualization, i.e. everything that sits inside the map.
37
- */
38
- generateChart() {
39
-
40
- // check if marker group exists
41
- if (!this.map.hasLayer(this.markerClusterGroup)) {
42
-
43
- // add popup info to marker
44
- let geoJsonLayer = L.geoJson(this.data, {
45
- onEachFeature: function (feature, layer) {
46
- layer.bindPopup(`<div><div id="popup-poster"><div data-id="${feature.properties.id}" style="background-image: url(${feature.properties.poster})"></div></div><div><p><strong>${feature.properties.label}</strong></p><p>${feature.properties.value}</p></div>`);
47
- }
48
- });
49
-
50
- // add markers to group
51
- this.markerClusterGroup.addLayer(geoJsonLayer);
52
-
53
- // add marker layer to map
54
- this.map.addLayer(this.markerClusterGroup);
55
-
56
- }
57
-
58
- }
59
-
60
- }
61
-
62
- export { Clustermap };
63
- export default Clustermap;
@@ -1,153 +0,0 @@
1
- import * as deck from '@deck.gl/core';
2
- import * as layers from '@deck.gl/layers';
3
- import * as luma from '@luma.gl/core';
4
- import * as VegaDeckGl from "@msrvida/vega-deck.gl";
5
- import L from "leaflet";
6
- import * as vega from "vega";
7
-
8
- import { VisualizationMap } from "./index.js";
9
- import { HeatLayout as HL } from "../layout/heat.js";
10
-
11
- import { configuration, configurationColor, configurationLayout } from "../configuration.js";
12
-
13
- /**
14
- * Heatmap is a map abstraction for a heatmap visualization.
15
- * @param {object} data - usually array; occasionally object
16
- * @param {string} label - identifer for chart brand
17
- * @param {float} height - specified height of chart
18
- * @param {class} MapData - lgv data class
19
- * @param {string} name - name of chart
20
- * @param {float} width - specified width of chart
21
- */
22
- class Heatmap extends VisualizationMap {
23
-
24
- constructor(data, width=configurationLayout.width, height=configurationLayout.height, HeatLayout=null, tileServerUrl=configurationLayout.tileserver, label=configuration.branding, name=configuration.name) {
25
-
26
- // initialize inheritance
27
- super(data, width, height, HeatLayout ? HeatLayout : new HL(data), tileServerUrl, label, name);
28
-
29
- // initialize vega
30
- VegaDeckGl.use(vega, deck, layers, luma);
31
-
32
- // update self
33
- this.imageOverlay = null;
34
-
35
- }
36
-
37
- /**
38
- * Generate mark size.
39
- * @returns An integer representing the render size of a visual mark.
40
- */
41
- get mark() {
42
-
43
- let isHighDensityDisplay = window.devicePixelRatio > 1 && window.innerWidth * window.devicePixelRatio > 3360 && window.innerHeight * window.devicePixelRatio > 1942;
44
-
45
- // set starting size
46
- let base = isHighDensityDisplay ? this.unit * 1.75 : this.unit * 2;
47
- let mark = base;
48
-
49
- // determine mark size
50
- switch (true) {
51
- case (this.zoom >= 16):
52
- mark = base - 1;
53
- break;
54
- case (this.zoom >= 14):
55
- mark = base - 2;
56
- break;
57
- case (this.zoom >= 12):
58
- mark = base - 3;
59
- break;
60
- case (this.zoom >= 8):
61
- mark = base - 4;
62
- break;
63
- case (this.zoom >= 0):
64
- mark = base - 5;
65
- break;
66
- }
67
-
68
- return mark;
69
-
70
- }
71
-
72
- /**
73
- * Generate main visualization, i.e. everything that sits inside the map.
74
- */
75
- generateChart() {
76
-
77
- // generate vega specification
78
- let spec = this.generateSpecification();
79
-
80
- // render vega with deck.gl
81
- let view = new VegaDeckGl.ViewGl(vega.parse(spec))
82
- .renderer("deck.gl")
83
- .run();
84
-
85
- // generate url blob
86
- view.toImageURL("svg")
87
- .then(url => {
88
-
89
- // check if overlay exists
90
- if (this.imageOverlay) {
91
-
92
- // update existing
93
- this.imageOverlay.setUrl(url);
94
- this.imageOverlay.setBounds(this.map.getBounds());
95
-
96
- } else {
97
-
98
- // initialize
99
- this.imageOverlay = L.imageOverlay(url, this.map.getBounds());
100
-
101
- // update map
102
- this.imageOverlay.addTo(this.map);
103
-
104
- }
105
-
106
- });
107
-
108
- }
109
-
110
- /**
111
- * Generate vega specification in prep for render.
112
- */
113
- generateSpecification() {
114
-
115
- // register custom scheme
116
- vega.scheme(this.name, configurationColor.p1);
117
-
118
- return {
119
- width: this.width,
120
- height: this.height,
121
- data: [{ name: this.name, values: this.isXY ? this.data.results : this.data }],
122
- scales: [
123
- {
124
- name: "color",
125
- type: "linear",
126
- domain: { data: this.name, field: "count" },
127
- range: { scheme: this.name, count: configurationColor.p1.length }
128
- }
129
- ],
130
- marks: [
131
- {
132
- type: "symbol",
133
- from: { data: this.name },
134
- encode: {
135
- update: {
136
- shape: { value: "square" },
137
- x: { field: "x" },
138
- y: { field: "y" },
139
- size: { value: this.mark },
140
- fill: { scale: "color", field: "count" },
141
- opacity: { value: 1 }
142
- },
143
- },
144
- }
145
- ]
146
- }
147
-
148
- }
149
-
150
- }
151
-
152
- export { Heatmap };
153
- export default Heatmap;
@@ -1,108 +0,0 @@
1
- :root {
2
-
3
- --marker-cluster-small: rgba(181, 226, 140, 0.6);
4
- --marker-cluster-small-div: rgba(110, 204, 57, 0.6);
5
- --marker-cluster-medium: rgba(241, 211, 87, 0.6);
6
- --marker-cluster-medium-div: rgba(240, 194, 12, 0.6);
7
- --marker-cluster-large: rgba(253, 156, 115, 0.6);
8
- --marker-cluster-large-div: rgba(241, 128, 23, 0.6);
9
-
10
- }
11
-
12
- .marker-cluster-small {
13
- background-color: var(--marker-cluster-small);
14
- }
15
- .marker-cluster-small div {
16
- background-color: var(--marker-cluster-small-div);
17
- }
18
-
19
- .marker-cluster-medium {
20
- background-color: var(--marker-cluster-medium);
21
- }
22
- .marker-cluster-medium div {
23
- background-color: var(--marker-cluster-medium-div);
24
- }
25
-
26
- .marker-cluster-large {
27
- background-color: var(--marker-cluster-large);
28
- }
29
- .marker-cluster-large div {
30
- background-color: var(--marker-cluster-large-div);
31
- }
32
-
33
- .marker-cluster {
34
- background-clip: padding-box;
35
- border-radius: 2.5em;
36
- }
37
- .marker-cluster div {
38
- width: 2.5em;
39
- height: 2.5em;
40
- text-align: center;
41
- border-radius: 2.5em;
42
- display: flex;
43
- align-items: center;
44
- justify-content:center;
45
- margin-left: 0.45em;
46
- margin-top: 0.45em;
47
- }
48
-
49
- /***************** POP UP *****************/
50
-
51
- .cluster-map .leaflet-popup-content {
52
-
53
- margin-bottom: 0;
54
- margin-left: 0;
55
- margin-top: 0;
56
-
57
- }
58
-
59
- /* wrap */
60
- .cluster-map .leaflet-popup-content > div {
61
-
62
- display: flex;
63
-
64
- }
65
-
66
- /* poster parent */
67
- .cluster-map .leaflet-popup-content > div > div:first-of-type {
68
-
69
- height: 100%;
70
- overflow: hidden;
71
-
72
- }
73
-
74
- /* poster */
75
- .cluster-map .leaflet-popup-content > div > div:first-of-type > div {
76
-
77
- background-position: center;
78
- background-repeat: no-repeat;
79
- background-size: cover;
80
- cursor: pointer;
81
- height: 5em;
82
- margin-right: 1em;
83
- transition: .3s cubic-bezier(.25,.8,.25,1);
84
- transition-property: transform;
85
- width: 5em;
86
-
87
- }
88
-
89
- .cluster-map .leaflet-popup-content > div > div:first-of-type:hover > div,
90
- .cluster-map .leaflet-popup-content > div > div:first-of-type:focus > div {
91
-
92
- transform: scale(1.1);
93
-
94
- }
95
-
96
- /* text wrap */
97
- .cluster-map .leaflet-popup-content > div > div:last-of-type {
98
-
99
- margin-top: 1em;
100
-
101
- }
102
-
103
- /* text */
104
- .cluster-map .leaflet-popup-content p {
105
-
106
- margin: 0;
107
-
108
- }
@@ -1,52 +0,0 @@
1
- import { select, Selection } from "d3-selection";
2
- //import { transition, Transition } from "d3-transition";
3
- import transition from "d3-transition";
4
- import "d3-transition";
5
- import L from "leaflet";
6
-
7
- import { VisualizationMap } from "./index.js";
8
- import { MapLayout as ML } from "../layout/map.js";
9
-
10
- import { configuration, configurationLayout } from "../configuration.js";
11
-
12
- /**
13
- * Routemap is a map abstraction for a routing visualization.
14
- * @param {object} data - usually array; occasionally object
15
- * @param {string} label - identifer for chart brand
16
- * @param {float} height - specified height of chart
17
- * @param {class} MapData - lgv data class
18
- * @param {string} name - name of chart
19
- * @param {float} width - specified width of chart
20
- */
21
- class Routemap extends VisualizationMap {
22
-
23
- constructor(data, width=configurationLayout.width, height=configurationLayout.height, MapLayout=null, tileServerUrl=configurationLayout.tileserver, label=configuration.branding, name=configuration.name) {
24
-
25
- // initialize inheritance
26
- super(data, width, height, MapLayout ? MapLayout : new ML(data), tileServerUrl, label, name);
27
-
28
- // initialize feature group
29
- this.geoJsonLayer = null;
30
-
31
- }
32
-
33
- /**
34
- * Generate main visualization, i.e. everything that sits inside the map.
35
- */
36
- generateChart() {
37
-
38
- // clear if exists
39
- if (this.geoJsonLayer) this.geoJsonLayer.clearLayers();
40
-
41
- // initialize feature group
42
- this.geoJsonLayer = L.geoJSON(this.data);
43
-
44
- // add lines
45
- this.geoJsonLayer.addTo(this.map);
46
-
47
- }
48
-
49
- }
50
-
51
- export { Routemap };
52
- export default Routemap;