@lgv/visualization-map 0.0.4 → 0.0.7
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/.gitlab-ci.yml +0 -4
- package/package.json +4 -2
- package/src/index.js +4 -1
- package/src/layout/heat.js +2 -5
- package/src/layout/map.js +23 -0
- package/src/visualization/cluster.js +63 -0
- package/src/visualization/heatmap.js +2 -2
- package/src/visualization/index.js +10 -9
- package/src/visualization/marker-cluster.css +108 -0
- package/src/visualization/route.js +43 -0
- package/webpack.common.js +8 -0
package/.gitlab-ci.yml
CHANGED
|
@@ -6,10 +6,6 @@ stages:
|
|
|
6
6
|
include:
|
|
7
7
|
###################################### GITLAB TEMPLATES ######################################
|
|
8
8
|
- template: Security/SAST.gitlab-ci.yml
|
|
9
|
-
|
|
10
|
-
# overwrites
|
|
11
|
-
- project: "lgensinger/cicd-templates"
|
|
12
|
-
file: "node/module/eslint-sast.yml"
|
|
13
9
|
|
|
14
10
|
- project: "lgensinger/cicd-templates"
|
|
15
11
|
file: "node/module/nodejs-scan-sast.yml"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lgv/visualization-map",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "ES6 d3.js core visualization scaffold object and utilities for maps.",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"browser-env": "^3.3.0",
|
|
35
35
|
"c8": "^7.11.3",
|
|
36
36
|
"css-loader": "^6.7.2",
|
|
37
|
+
"file-loader": "^6.2.0",
|
|
37
38
|
"html-webpack-plugin": "^5.3.2",
|
|
38
39
|
"style-loader": "^3.3.1",
|
|
39
40
|
"webpack": "^5.86.0",
|
|
@@ -43,9 +44,10 @@
|
|
|
43
44
|
"dependencies": {
|
|
44
45
|
"@deck.gl/core": "^8.8.20",
|
|
45
46
|
"@deck.gl/layers": "^8.8.20",
|
|
46
|
-
"@lgv/visualization-chart": "^0.3.
|
|
47
|
+
"@lgv/visualization-chart": "^0.3.26",
|
|
47
48
|
"@msrvida/vega-deck.gl": "^3.3.4",
|
|
48
49
|
"leaflet": "^1.9.2",
|
|
50
|
+
"leaflet.markercluster": "^1.5.3",
|
|
49
51
|
"vega": "^5.22.1"
|
|
50
52
|
},
|
|
51
53
|
"ava": {
|
package/src/index.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { HeatLayout } from "./layout/heat.js";
|
|
2
|
+
import { MapLayout } from "./layout/map.js";
|
|
2
3
|
|
|
3
4
|
import { MapData } from "./structure/index.js";
|
|
4
5
|
|
|
6
|
+
import { Clustermap } from "./visualization/cluster.js";
|
|
5
7
|
import { Heatmap } from "./visualization/heatmap.js";
|
|
8
|
+
import { Routemap } from "./visualization/route.js";
|
|
6
9
|
import { VisualizationMap } from "./visualization/index.js";
|
|
7
10
|
|
|
8
|
-
export { HeatLayout, Heatmap, MapData, VisualizationMap };
|
|
11
|
+
export { Clustermap, HeatLayout, Heatmap, MapData, MapLayout, Routemap, VisualizationMap };
|
package/src/layout/heat.js
CHANGED
|
@@ -1,22 +1,19 @@
|
|
|
1
1
|
import L from "leaflet";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { MapLayout } from "./map.js";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* HeatLayout is a data-bound layout abstraction for a heatmap.
|
|
7
7
|
* @param {array} data - geojson
|
|
8
8
|
* @param {class} map - Leaflet map object
|
|
9
9
|
*/
|
|
10
|
-
class HeatLayout extends
|
|
10
|
+
class HeatLayout extends MapLayout {
|
|
11
11
|
|
|
12
12
|
constructor(data, map) {
|
|
13
13
|
|
|
14
14
|
// initialize inheritance
|
|
15
15
|
super(data);
|
|
16
16
|
|
|
17
|
-
// update self
|
|
18
|
-
this.map = map;
|
|
19
|
-
|
|
20
17
|
}
|
|
21
18
|
|
|
22
19
|
/**
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { MapData } from "../structure/index.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* MapLayout is a data-bound layout abstraction for a leaflet map.
|
|
5
|
+
* @param {array} data - geojson
|
|
6
|
+
* @param {class} map - Leaflet map object
|
|
7
|
+
*/
|
|
8
|
+
class MapLayout extends MapData {
|
|
9
|
+
|
|
10
|
+
constructor(data, map) {
|
|
11
|
+
|
|
12
|
+
// initialize inheritance
|
|
13
|
+
super(data);
|
|
14
|
+
|
|
15
|
+
// update self
|
|
16
|
+
this.map = map;
|
|
17
|
+
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export { MapLayout };
|
|
23
|
+
export default MapLayout;
|
|
@@ -0,0 +1,63 @@
|
|
|
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;
|
|
@@ -21,10 +21,10 @@ import { configuration, configurationColor, configurationLayout } from "../confi
|
|
|
21
21
|
*/
|
|
22
22
|
class Heatmap extends VisualizationMap {
|
|
23
23
|
|
|
24
|
-
constructor(data, width=configurationLayout.width, height=configurationLayout.height, HeatLayout=null, label=configuration.branding, name=configuration.name) {
|
|
24
|
+
constructor(data, width=configurationLayout.width, height=configurationLayout.height, HeatLayout=null, tileServerUrl=configurationLayout.tileserver, label=configuration.branding, name=configuration.name) {
|
|
25
25
|
|
|
26
26
|
// initialize inheritance
|
|
27
|
-
super(data, width, height, HeatLayout ? HeatLayout : new HL(data), label, name);
|
|
27
|
+
super(data, width, height, HeatLayout ? HeatLayout : new HL(data), tileServerUrl, label, name);
|
|
28
28
|
|
|
29
29
|
// initialize vega
|
|
30
30
|
VegaDeckGl.use(vega, deck, layers, luma);
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { LinearGrid } from "@lgv/visualization-chart";
|
|
2
2
|
import { mean } from "d3-array";
|
|
3
3
|
import L from "leaflet";
|
|
4
4
|
import "leaflet/dist/leaflet.css";
|
|
5
|
+
import "leaflet/dist/images/marker-shadow.png";
|
|
6
|
+
import "./marker-cluster.css";
|
|
5
7
|
|
|
6
8
|
import { configuration, configurationLayout } from "../configuration.js";
|
|
7
9
|
|
|
@@ -16,7 +18,7 @@ import { configuration, configurationLayout } from "../configuration.js";
|
|
|
16
18
|
*/
|
|
17
19
|
class VisualizationMap extends LinearGrid {
|
|
18
20
|
|
|
19
|
-
constructor(data, width=configurationLayout.width, height=configurationLayout.height, MapData=null, label=configuration.branding, name=configuration.name) {
|
|
21
|
+
constructor(data, width=configurationLayout.width, height=configurationLayout.height, MapData=null, tileServerUrl=configurationLayout.tileserver, label=configuration.branding, name=configuration.name) {
|
|
20
22
|
|
|
21
23
|
// initialize inheritance
|
|
22
24
|
super(data, width, height, MapData, label, name);
|
|
@@ -25,14 +27,13 @@ class VisualizationMap extends LinearGrid {
|
|
|
25
27
|
this.determineDataType();
|
|
26
28
|
|
|
27
29
|
// 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
|
-
|
|
31
|
-
|
|
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));
|
|
32
|
+
|
|
32
33
|
// update self
|
|
33
34
|
this.center = [x,y];
|
|
34
35
|
this.map = null;
|
|
35
|
-
this.
|
|
36
|
+
this.tileServerUrl = tileServerUrl;
|
|
36
37
|
this.zoom = 1;
|
|
37
38
|
|
|
38
39
|
}
|
|
@@ -68,7 +69,7 @@ class VisualizationMap extends LinearGrid {
|
|
|
68
69
|
* Generate main visualization, i.e. everything that sits inside the svg.
|
|
69
70
|
*/
|
|
70
71
|
determineDataType() {
|
|
71
|
-
this.isFeatureCollection = this.data && this.Data.source.features;
|
|
72
|
+
this.isFeatureCollection = this.data !== (null || undefined) && this.Data.source.features !== (null || undefined);
|
|
72
73
|
this.isPoint = this.data && this.data.coordinates && this.data.type.lower() == "point";
|
|
73
74
|
this.isXY = this.data && this.data.metadata && this.data.results;
|
|
74
75
|
}
|
|
@@ -143,7 +144,7 @@ class VisualizationMap extends LinearGrid {
|
|
|
143
144
|
this.attachEvents();
|
|
144
145
|
|
|
145
146
|
// update tile server
|
|
146
|
-
L.tileLayer(this.
|
|
147
|
+
L.tileLayer(this.tileServerUrl).addTo(this.map);
|
|
147
148
|
|
|
148
149
|
// fit to bounds
|
|
149
150
|
this.fitToData();
|
|
@@ -0,0 +1,108 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
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
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Generate main visualization, i.e. everything that sits inside the map.
|
|
32
|
+
*/
|
|
33
|
+
generateChart() {
|
|
34
|
+
|
|
35
|
+
// add lines
|
|
36
|
+
L.geoJSON(this.data).addTo(this.map);
|
|
37
|
+
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export { Routemap };
|
|
43
|
+
export default Routemap;
|