Skip to content

Commit 874cf4f

Browse files
committed
1 parent 3b6349f commit 874cf4f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+22341
-0
lines changed

.babelrc

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"presets": ["env"]
3+
}

.bowerrc

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"directory": "lib"
3+
}

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules
2+
lib/leaflet/docs
3+
*.log
4+
*.log.*

.jshintrc

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"esversion": 6,
3+
"laxbreak": true
4+
}

README.md

+135
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
# (experimental) Regular-grid-cluster plugin/library for Leaflet
2+
3+
![image](./img.png)
4+
5+
## demo
6+
- [random data example](https://adammertel.github.io/Leaflet.RegularGridCluster/demo/random_data )
7+
- [two datasets example](https://adammertel.github.io/Leaflet.RegularGridCluster/demo/two_datasets )
8+
9+
10+
## motivation
11+
the basic idea of this library is to present the custom method to both **visualise and cluster** larger dataset (or to be able to visually compare correlation between two different datasets). This method is based on the idea of distributing the dataset(elements) into **regular shaped zones**. Plugin can work with **point data** in the form of L.Marker or L.Circle/L.CircleMarker. This concept is suitable to visualise **multivariate datasets**.
12+
Grid consists of 3 different component classes, each could styled separately or optionally turned on and off:
13+
- **cells** - regularly shaped polygon (squares and hexagons are implemented) (graphical variables - fill/stroke color, fill/stoke opacity, ...)
14+
- **markers** - circle-shaped marker centered to cell (graphical variables - radius, fill/stroke colorm fill/stoke opacity, ...)
15+
- **texts** - text inside the cell that holds selected information about clustered data (graphical variables - font, font size, color,...)
16+
17+
18+
## options
19+
- **gridMode** (*default: 'square'*) 'square' of 'hexagon' values. The shape of cells
20+
- **zoneSize** (*default: 10000*) size of the cell at a scale of 10
21+
- **gridOrigin** (*default: 'auto'*) setting the value of a L.latLng instance will manually define the SW corner of the grid. Value 'auto' will define the origin based on data and gridBoundsPadding,
22+
- **gridEnd** (*default: 'auto'*) NE corner of the grid. In case value "auto" is used, corner will be calculated from data and gridBoundsPadding option.
23+
- **gridBoundsPadding** (*default: 0.1*) ratio to extend bounding box of elements
24+
25+
- **showCells** (*default: true*) turning cells off and on
26+
- **showMarkers** (*default: true*) turning markers off and on
27+
- **showTexts** (*default: true*) turning texts off and on
28+
- **showEmptyCells** (*default: false*) whether display cells where no elements fall
29+
- **emptyCellOptions** styling of empty cells
30+
31+
32+
- **zoomShowElements** (*default: 10*) level of zoom when to turn elements on and off
33+
- **zoomHideGrid** (*default: 10*) level of zoom when to turn grid on and off
34+
35+
- **indexSize** (*default: 12*) for a better performence, zones and elements are pre-indexed. Option indexSize difenes the number of super-zones within each axis
36+
- **rules** (*default rules: {cells: {}, markers: {}, texts: {} }*) values to define how components will look like. Every rule can be defined as dynamic or static (see section **rules** or [random data example](https://adammertel.github.io/Leaflet.RegularGridCluster/demo/random_data ))
37+
38+
- **paneElementsZ** (*default: 10000*)
39+
- **paneCellsZ** (*default: 700*)
40+
- **paneMarkersZ** (*default: 800*)
41+
- **paneTextsZ** (*default: 900*)
42+
43+
44+
## rules
45+
Rules object consists of 3 key-values pairs - **cells, **markers** and **texts**. Most of the naming is technicaly the same as in the L.Path class (see [leaflet documentation](http://leafletjs.com/reference-1.0.3.html#path)). Differences are mainly within texts (*fontSize*, *fontWeight*).
46+
To create a dynamical rule, one has to define a **method**, **scale**, **style**, **domain** and **attribute** parameters:
47+
- **method** ('count', 'mean', 'median', 'mode', 'min', 'max' and 'sum') - operation how the result value will be calculated from the overlapping elements
48+
- **scale** ('size' - equal size, 'quantile', 'continuous') - how threshold values will be defined for intervals. 'Continous' value will create a continuous scale
49+
- **range** - array with values to set how to style the variable
50+
- **domain** - array of two values to define minimum (first value) and maximum (second value) of input manually. If this is not defined, maximum and minimum will be set based on the data.
51+
- **attribute** - name of the property that defines the rule (could be ommited in case, the method is 'count')
52+
53+
To test how these parameters work, please have a look at the [random data example](https://adammertel.github.io/Leaflet.RegularGridCluster/demo/random_data)
54+
55+
56+
## how to
57+
1. Create/Access some point data
58+
59+
```
60+
const maxX = 50, minX = 0, maxY = 49.5, minY = 0;
61+
const randomData = [];
62+
63+
for (i=0; i < 1000; i++) {
64+
const coordinates = [
65+
minX + Math.random() * (maxX - minX),
66+
minY + Math.random() * (maxY - minY)
67+
];
68+
const properties = {
69+
a: 5 + Math.floor(Math.random() * 5),
70+
b: Math.floor(Math.random() * 5)
71+
};
72+
73+
const marker = L.circleMarker(coordinates, {radius: 1, fillColor: 'black'});
74+
randomData.push( {
75+
marker: marker,
76+
properties: properties
77+
});
78+
};
79+
```
80+
81+
82+
2. Define the ruleset
83+
```
84+
const rules = {
85+
cells: {
86+
"fillColor": {
87+
"method": "mean",
88+
"attribute": "b",
89+
"scale": "size",
90+
"range": ["#d7191c","#fdae61","#ffffbf","#a6d96a","#1a9641"]
91+
},
92+
"color": "black",
93+
"fillOpacity": 0.2,
94+
"weight": 0
95+
},
96+
markers: {
97+
"color": "white",
98+
"weight": 2,
99+
"fillOpacity": 0.9,
100+
"fillColor": {
101+
"method": "mean",
102+
"attribute": "b",
103+
"scale": "continuous",
104+
"range": ["#ffffb2","#fecc5c","#fd8d3c","#e31a1c"]
105+
},
106+
"radius": {
107+
"method": "count",
108+
"attribute": "",
109+
"scale": "continuous",
110+
"range": [7, 17]
111+
}
112+
},
113+
texts: {}
114+
}
115+
```
116+
117+
3. create a new instance of L.RegularGridCluster and set the options and ruleset
118+
```
119+
const grid = L.regularGridCluster(
120+
{
121+
rules: rules,
122+
gridMode: 'hexagon,
123+
showCells: true,
124+
showMarkers: true,
125+
showTexts: false
126+
}
127+
);
128+
```
129+
130+
4. add data to the L.regularGridCluster instance
131+
```
132+
grid.addLayers(pointData);
133+
```
134+
135+

banner

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/*
2+
regular-grid-cluster plugin for leaflet
3+
https://github.com/adammertel/Leaflet.RegularGridCluster
4+
Adam Mertel | univie
5+
*/

bower.json

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"name": "leaflet-regular-grid-cluster",
3+
"description": "",
4+
"authors": [
5+
"Adam Mertel | UNIVIE"
6+
],
7+
"license": "ISC",
8+
"keywords": [
9+
"leaflet",
10+
"visualisation"
11+
],
12+
"main": "dist/leaflet-regulargridcluster.js",
13+
"homepage": "https://github.com/adammertel/Leaflet.RegularGridCluster",
14+
"ignore": [
15+
"node_modules",
16+
"bower_components"
17+
],
18+
"dependencies": {
19+
"leaflet": "^1.2.0"
20+
}
21+
}

demo/demo.css

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
body {
2+
margin: 0px;
3+
font-family: 'PT Sans Narrow', sans-serif;
4+
}
5+
6+
#map-content {
7+
top: 0;
8+
bottom: 0;
9+
left: 0;
10+
right: 0;
11+
position: absolute !important
12+
}
13+
14+
#panel {
15+
position: absolute;
16+
height: 90%;
17+
width: 350px;
18+
background-color: white;
19+
top: 2%;
20+
right: 2%;
21+
z-index: 999;
22+
padding-left: 15px;
23+
opacity: 0.95;
24+
border: 15px solid white;
25+
border-opacity: 0;
26+
font-size: 80%;
27+
}
28+
29+
.textarea-rules {
30+
width: 340px;
31+
font-size: 10px;
32+
margin-left: 4px;
33+
}
34+
35+
.marker-cluster {
36+
opacity: 1 !important;
37+
}
38+
39+
.active-cluster-element {
40+
opacity: 0.2;
41+
}
42+
43+
.select-row {
44+
margin-top: 4px;
45+
margin-bottom: 1px;
46+
margin-left: 5px
47+
}
48+
49+
h4 {
50+
margin-bottom: 2px
51+
}
52+
53+
select {
54+
margin-left: 5px
55+
}

demo/ext/zoomdisplay.css

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
.leaflet-control-zoom-display {
2+
background-color: #fff;
3+
border-bottom: 1px solid #ccc;
4+
width: 26px;
5+
height: 26px;
6+
line-height: 26px;
7+
display: block;
8+
text-align: center;
9+
text-decoration: none;
10+
color: black;
11+
padding-top: 0.3em;
12+
font: bold 12px/20px Tahoma, Verdana, sans-serif;
13+
}

demo/ext/zoomdisplay.js

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* L.Control.ZoomDisplay shows the current map zoom level
3+
*/
4+
5+
L.Control.ZoomDisplay = L.Control.extend({
6+
options: {
7+
position: 'topleft'
8+
},
9+
10+
onAdd: function(map) {
11+
this._map = map;
12+
this._container = L.DomUtil.create(
13+
'div',
14+
'leaflet-control-zoom-display leaflet-bar-part leaflet-bar'
15+
);
16+
this.updateMapZoom(map.getZoom());
17+
map.on('zoomend', this.onMapZoomEnd, this);
18+
return this._container;
19+
},
20+
21+
onRemove: function(map) {
22+
map.off('zoomend', this.onMapZoomEnd, this);
23+
},
24+
25+
onMapZoomEnd: function(e) {
26+
this.updateMapZoom(this._map.getZoom());
27+
},
28+
29+
updateMapZoom: function(zoom) {
30+
if (typeof zoom === 'undefined') {
31+
zoom = '';
32+
}
33+
this._container.innerHTML = zoom;
34+
}
35+
});
36+
37+
L.Map.mergeOptions({
38+
zoomDisplayControl: true
39+
});
40+
41+
L.Map.addInitHook(function() {
42+
if (this.options.zoomDisplayControl) {
43+
this.zoomDisplayControl = new L.Control.ZoomDisplay();
44+
this.addControl(this.zoomDisplayControl);
45+
}
46+
});
47+
48+
L.control.zoomDisplay = function(options) {
49+
return new L.Control.ZoomDisplay(options);
50+
};

0 commit comments

Comments
 (0)