Skip to content

Instantly share code, notes, and snippets.

@EE2dev
Last active June 24, 2018 20:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save EE2dev/d17037089246a3b23cba1a3ca8ed0cc1 to your computer and use it in GitHub Desktop.
Save EE2dev/d17037089246a3b23cba1a3ca8ed0cc1 to your computer and use it in GitHub Desktop.
leaflet and d3.legend
license: mit
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.3.1/dist/leaflet.css"
integrity="sha512-Rksm5RenBEKSKFjgI3a41vrjkw4EVPlJ3+OiI65vTjIdo9brlAacEuKOiQ5OFh7cOI1bkDwLqdLw3Zg0cRJAAQ=="
crossorigin=""/>
<!-- Make sure you put this AFTER Leaflet's CSS -->
<script src="https://unpkg.com/leaflet@1.3.1/dist/leaflet.js"
integrity="sha512-/Nsx9X4HebavoBvEBuyp3I7od5tA0UzAxs+j83KgC8PU0kgB4XiK4Lfe4y4cgBtaRJQEIFCW+oC506aPT2L1zw=="
crossorigin=""></script>
<script src="http://d3js.org/d3.v5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-legend/2.25.6/d3-legend.min.js"></script>
<style>
#map { height: 400px; }
aside, pre { display: none; }
div#allLegends { position: relative; }
div.legend { position: relative; width: 300px; float: left; }
</style>
</head>
<body>
<aside class="myDataset">group,lat,long,index,address
BK,48.157630,11.648014,1800,loithringer
BK,48.141579,11.604729,100,sdakjh
BK,48.143640,11.603463,1800,dsfasfslk 3.
BK,48.157630,11.648014,1800,loithringer
BK,48.141579,11.604729,100,sdakjh
BK,48.143640,11.603463,1800,dsfasfslk 3.
BK,48.143827,11.602895,500, sadkfjalsfj
BK,48.143640,11.603463,,dsfasfslk 3.
BK,48.143827,11.602895,, sadkfjalsfj
BK,48.144099,11.602444,120, hansenstrasse 4A</aside>
<aside class="d2">group,lat,long,value
KK,48.157630,11.648014,30
KK,48.141579,11.604729,1800
KK,48.143640,11.603463,200</aside>
<aside class="d3">group,lat,long,value
KI,48.157630,11.648014,Sepp
KI,48.141579,11.604729,Schorsch
KI,48.143640,11.603463,Hans</aside>
<div id="map"></div>
<div id="allLegends"></div>
<script>
var mbAttr = 'Map data &copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, ' +
'<a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, ' +
'Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
mbUrl = 'https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=pk.eyJ1IjoiZWUyZGV2IiwiYSI6ImNqaWdsMXJvdTE4azIzcXFscTB1Nmcwcm4ifQ.hECfwyQtM7RtkBtydKpc5g';
var grayscale = L.tileLayer(mbUrl, {id: 'mapbox.light', attribution: mbAttr}),
satellite = L.tileLayer(mbUrl, {id: 'mapbox.satellite', attribution: mbAttr}),
streets = L.tileLayer(mbUrl, {id: 'mapbox.streets', attribution: mbAttr});
var layerGroups = {};
var dataParameters = [];
var dp;
//////////////////////////////////////////////////////////////////
// Usage:
// 1. set class of aside element above to match the name of the data
// 2. insert data into aside element
// 3. specify the following dp (data parameters)
//
// options for scales:
// "scaleThreshold", "scaleOrdinal", "scaleOrdinal" or "scaleQuantile"
//////////////////////////////////////////////////////////////////
// ------> for more data sets, copy FROM here
// dataset 1
dataParameters.push({});
dp = dataParameters[dataParameters.length - 1];
// specify start
dp.selector = "aside.myDataset";
dp.name = dp.selector.split(".").pop();
dp.delimiter = ",";
dp.numColumns = ["lat","long","index"];
dp.valueColumn = "index";
dp.scaleType = "scaleThreshold";
// specify end
dp.data = readData(dp.selector, dp.delimiter, dp.numColumns, dp.valueColumn);
dp.scale = getScale(dp.data, dp.scaleType, dp.valueColumn);
dp.group = L.layerGroup();
addCircles(dp);
addLegend(dp.scale, dp.scaleType, dp.name);
// ------> for more data sets, copy TO here
// dataset2
dataParameters.push({});
dp = dataParameters[dataParameters.length - 1];
// specify start
dp.selector = "aside.d2";
dp.name = dp.selector.split(".").pop();
dp.delimiter = ",";
dp.numColumns = ["lat","long"];
dp.valueColumn = "value";
dp.scaleType = "scaleQuantize";
// specify end
dp.data = readData(dp.selector, dp.delimiter, dp.numColumns, dp.valueColumn);
dp.scale = getScale(dp.data, dp.scaleType, dp.valueColumn);
dp.group = L.layerGroup();
addCircles(dp);
addLegend(dp.scale, dp.scaleType, dp.name);
// dataset3
dataParameters.push({});
dp = dataParameters[dataParameters.length - 1];
// specify start
dp.selector = "aside.d3";
dp.name = dp.selector.split(".").pop();
dp.delimiter = ",";
dp.numColumns = ["lat","long"];
dp.valueColumn = "value";
dp.scaleType = "scaleOrdinal";
// specify end
dp.data = readData(dp.selector, dp.delimiter, dp.numColumns, dp.valueColumn);
dp.scale = getScale(dp.data, dp.scaleType, dp.valueColumn);
dp.group = L.layerGroup();
addCircles(dp);
addLegend(dp.scale, dp.scaleType, dp.name);
// dataset 4
dataParameters.push({});
dp = dataParameters[dataParameters.length - 1];
// specify start
dp.selector = "aside.myDataset";
dp.name = "The quantile dataset";
dp.delimiter = ",";
dp.numColumns = ["lat","long","index"];
dp.valueColumn = "index";
dp.scaleType = "scaleQuantile";
// specify end
dp.data = readData(dp.selector, dp.delimiter, dp.numColumns, dp.valueColumn);
dp.scale = getScale(dp.data, dp.scaleType, dp.valueColumn);
dp.group = L.layerGroup();
addCircles(dp);
addLegend(dp.scale, dp.scaleType, dp.name);
var map = L.map('map', {
center: [48.1523107, 11.6231103],
zoom: 14,
layers: [grayscale, dataParameters[0].group]
});
var baseLayers = {
"Grayscale": grayscale,
"Streets": streets,
"Satellite": satellite
};
var overlays = {};
dataParameters.forEach(function(ele) {
overlays[ele.name] = ele.group;
})
L.control.layers(baseLayers, overlays).addTo(map);
/////////////////////////////////////////
// helper functions
/////////////////////////////////////////
function addLegend(scale, scaleType, title) {
var svg = d3.select("#allLegends")
.append("div")
.attr("class", "legend " + title)
.append("svg")
.style("width", 300)
.style("height", 300);
svg.append("g")
.attr("class", "legend")
.attr("transform", "translate(20,20)");
var legend = d3.legendColor()
.labelFormat(d3.format(".2f"))
.title(title);
if (scaleType === "scaleThreshold") {
legend = legend.labels(d3.legendHelpers.thresholdLabels);
}
legend.scale(scale);
svg.select("g.legend")
.call(legend);
}
function addCircles(dp) {
var circle;
var colorScale = dp.scale;
dp.data.forEach(function(element) {
circle = L.circle([element.lat, element.long], {
color: colorScale(element[dp.valueColumn]),
fillColor: colorScale(element[dp.valueColumn]),
fillOpacity: 1,
radius: 5
}).addTo(dp.group);
circle.bindPopup("Wert: " + element[dp.valueColumn] + "<br>Adresse: " + element.address);
});
}
// see also: http://d3indepth.com/scales/
function getScale(data, scaleType, valueCol) {
var scale;
if (scaleType === "scaleThreshold") {
var min = d3.min(data, function(d) { return d[valueCol]; });
var max = d3.max(data, function(d) { return d[valueCol]; });
var d = (max-min)/7;
scale = d3.scaleThreshold()
.domain([min+1*d,min+2*d,min+3*d,min+4*d,min+5*d,min+6*d])
.range(['#ffffe0','#ffd59b','#ffa474','#f47461','#db4551','#b81b34','#8b0000']);
} else if (scaleType === "scaleQuantize") {
scale = d3.scaleQuantize()
.domain(d3.extent(data, function(d) { return d[valueCol]; }))
.range(['#ffffe0','#ffd59b','#ffa474','#f47461','#db4551','#b81b34','#8b0000']);
} else if (scaleType === "scaleQuantile") {
scale = d3.scaleQuantile()
.domain(data.map(function(d) { return d[valueCol]; }).sort(function(a, b){return a-b}))
.range(['#ffffe0','#ffd59b','#ffa474','#f47461','#db4551','#b81b34','#8b0000']);
} else if (scaleType === "scaleOrdinal") {
scale = d3.scaleOrdinal()
.domain(data.map(function(d) { return d[valueCol]; }))
.range(d3.schemePaired);
}
return scale;
}
function readData(selector, delimiter, columnsNum, valueCol) {
var psv = d3.dsvFormat(delimiter);
var initialData = psv.parse(d3.select(selector).text());
_data = initialData.filter(function(e) { return e[valueCol].length !== 0; });
console.log("Skipped: " + (initialData.length - _data.length) + " rows.");
if (typeof columnsNum !== "undefined") {
_data.forEach( function (row) {
convertToNumber(row, columnsNum);
});
}
console.log(_data);
return _data;
}
function convertToNumber(d, _columnsNum) {
for (var perm in d) {
if (_columnsNum.indexOf(perm) > -1)
if (Object.prototype.hasOwnProperty.call(d, perm)) {
d[perm] = +d[perm];
}
}
return d;
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment