I created an interactive web map with d3. I am fairly new to web programming and am interested what you think about the code. The map is based on unemployment rates for each NUTS-2 regeion in europe. I created a sample JSON, so you maybe can use this for a working reproducible example. I hope the code provided is not too much.
sample JSON
{
"type": "FeatureCollection",
"crs": {
"type": "name",
"properties": {
"name": "urn:ogc:def:crs:OGC:1.3:CRS84"
}
},
"features": [{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [[[10.00, 45.00], [10.00, 50.00], [15.00, 50.00], [15.00, 45.00], [10.00, 45.00]]]
},
"properties": {
"2005": "7.8",
"2006": "7.3",
"2007": "4.1",
"2008": "4.2",
"2009": "7",
"2010": "4",
"2011": "1.8",
"2012": "4.6",
"2013": "4.3",
"2014": "4.8",
"2015": "7.2",
"NUTS_ID": "AT11",
"STAT_LEVL_": 2,
"SHAPE_Leng": 4.87315,
"SHAPE_Area": 0.467459,
"Region": "Burgenland (AT)"
},
"id": 1453
}, {
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [[[5.00, 45.00], [5.00, 50.00], [10.00, 50.00], [10.00, 45.00], [10.00, 45.00]]]
},
"properties": {
"2005": "12.7",
"2006": "12.5",
"2007": "12",
"2008": "10.7",
"2009": "12.7",
"2010": "10.9",
"2011": "12.5",
"2012": "12.6",
"2013": "15",
"2014": "15.1",
"2015": "15.2",
"NUTS_ID": "AT12",
"STAT_LEVL_": 2,
"SHAPE_Leng": 8.788773,
"SHAPE_Area": 2.321136,
"Region": "Niederösterreich"
},
"id": 1454
}
]
}
HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title> Europe Unemployment (NUTS-2 Regions) </title>
<script src='https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js'></script>
<script src="//code.jquery.com/ui/1.12.0/jquery-ui.js"></script>
<script src='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.6/d3.min.js'></script>
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.0/themes/smoothness/jquery-ui.css">
<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css' type='text/css'>
<link rel="stylesheet" href="css/customCSS" type="text/css">
</head>
<body>
<h1 style="color:#ffffff;">Europe - Unemploymentrate</h1>
<button id="resetButton" class="btn">Reset</button>
<div class="wrapper">
<div id="svgMap"></div>
<div class="infobox-wrapper">
<div class="infobox" id="regionName">
Europe
</div>
<div class="infobox" id="infobox-content">
<p>Year: <span class="infoBox" id="currentYear">2005</span></p>
<p>Unemployment: <span class="infoBox" id="unemploymentRate">100</span> %</p>
<p>Inhabitants: <span class="infoBox">default</span></p>
<p>Country: <span class="infoBox">default</span></p>
</div>
<div class="infobox" id="infobox-plot">
<div id="svgPlot"></div>
</div>
</div>
</div>
<div class="wrapper">
<div id="playButtonSlider" class="btn glyphicon glyphicon-play"></div>
<div id="slider"></div>
</div>
<script src="javascript/EUmap.js"></script>
</body>
</html>
CSS
body {
background-color: #18121E;
margin: 50px;
}
#svgMap {
width: 65%;
height: 100%;
background-color: #233237;
border: 2px solid grey;
border-radius: 10px;
float: left;
}
.infobox-wrapper {
width: 35%;
height: 100%;
padding: 5px 0px 0px 15px;
float: right;
}
.infobox {
margin-top: 15px;
padding: 5px 5px 5px 10px;
border: 2px solid grey;
border-radius: 10px;
background-color:#233237;
color:#ffffff;
}
/* RESET BUTTON */
.btn {
-webkit-border-radius: 15;
-moz-border-radius: 15;
border-radius: 15px;
background: #7c100a;
border: solid #ffffff 1px;
text-decoration: none;
}
.btn:hover {
background: #7d5f5e;
text-decoration: none;
}
#resetButton {
margin: 15px 0 0 10px;
position: absolute;
color: #ffffff;
font-size:0.8em;
}
/* SLIDER + SLIDER PLAY BUTTON */
#playButtonSlider {
margin: 10px 0 0 15px;
float: left;
font-size:0.7em;
}
.glyphicon {
color: #ffffff;
padding: 3px 5px 3px 5px;
}
#slider {
margin: 15px 0 0 25px;
width: 50%;
background-color: #233237;
float: left;
}
#slider label {
position: absolute;
width: 20px;
margin-top: 20px;
margin-left: -20px;
text-align: center;
font-size: 0.8em;
font-family: "Lucida Console", Monaco, monospace;
color: grey;
}
.ui-slider-tick-mark {
display: inline-block;
width: 2px;
background: grey;
height: 10px;
position: absolute;
}
.ui-widget-content .ui-corner-all {
background: #7c100a;
border-color: #ffffff;
}
.ui-widget-content .ui-corner-all:hover {
background: #7d5f5e;
border-color: #ffffff;
}
/* PLOT */
.euPlot {
fill: steelblue;
stroke: white;
}
.yAxis path{
stroke: white;
color:white;
}
.yAxis line{
stroke: white;
color:white;
}
.yAxis text{
fill: white;
}
.xAxis path{
stroke: white;
}
.xAxis line{
stroke: white;
}
.xAxis text{
fill: white;
}
Javascript
// Slider
$("#slider").slider({
value: 2005,
min: 2005,
max: 2015,
step: 0.1,
create: function(event, ui) {
setSliderTicks(event.target);
},
})
.each(function() {
var opt = $(this).data().uiSlider.options;
var vals = opt.max - opt.min;
for (var i = 0; i <= vals; i++) {
var el = $('<label>' + (i + opt.min) + '</label>').css('left', (i / vals * 100) + '%');
$("#slider").append(el);
}
});
function setSliderTicks(el) {
var $slider = $(el);
var max = $slider.slider("option", "max");
var min = $slider.slider("option", "min");
var spacing = 100 / (max - min);
$slider.find('.ui-slider-tick-mark').remove();
for (var i = 1; i < max - min; i++) {
$('<span class="ui-slider-tick-mark"></span>').css('left', (spacing * i) + '%').appendTo($slider);
}
}
// Responsive Design
var windowWidth = window.innerWidth;
var windowHeight = window.innerHeight;
var wrapperWidth = 0.9 * windowWidth;
var wrapperHeight;
if (windowWidth < windowHeight) {
wrapperHeight = 0.5 * windowHeight;
} else {
wrapperHeight = 0.75 * windowHeight;
}
$(".wrapper").css("width", wrapperWidth);
$(".wrapper").css("height", wrapperHeight);
// Map EU
var svgMapWidth = (0.65 * wrapperWidth) - 4;
var svgMapHeight = wrapperHeight - 4;
var myProjection = d3.geoMercator()
.center([8.00, 57.00])
.scale(500)
.translate([svgMapWidth / 2, svgMapHeight / 2]);
var geoPath = d3.geoPath().projection(myProjection);
var color = d3.scaleThreshold()
.domain([0, 2, 4, 6, 8, 10, 15, 20, 25, 50])
.range(["#006837", "#1a9850", "#66bd63", "#a6d96a", "#d9ef8b", "#fee08b", "#fdae61", "#f46d43", "#d73027", "#a50026", "#808080"]);
var svgEurope = d3.select("#svgMap").append("svg")
.attr("width", svgMapWidth)
.attr("height", svgMapHeight);
var g = svgEurope.append("g");
var zoom = d3.zoom()
.scaleExtent([0.25, 8])
.on("zoom", zoomed);
svgEurope.call(zoom);
d3.queue()
.defer(d3.json, "data/sampleData.json")
.await(initializeData);
function initializeData(error, euRegionsJSON) {
if (error) throw error;
$('#slider').slider().bind('slidechange',e => renderMap(e,euRegionsJSON, sliderValue)); //same as $('#slider').slider().bind('slidechange',function e() {return renderMap(e,euRegionsJSON, sliderValue)});
$('#slider').trigger('slidechange');
};
var sliderValue = 2005;
$('#slider').slider().bind('slidechange', function(event, ui) {
sliderValue = $('#slider').slider("value");
sliderValue = Math.floor(sliderValue);
$("#currentYear").text(sliderValue);
});
$("#playButtonSlider").on("click",playButtonSlider);
function playButtonSlider() {
function startTimer(counter) {
if(counter <= 10) {
setTimeout(function(){
sliderValue = $("#slider").slider('value',2005+counter);
counter += 0.1;
startTimer(counter);
},1);
}
}
startTimer(0);
}
function renderMap(error, euRegionsJSON, sliderValue) {
g.selectAll(".euMap").remove();
g.selectAll("path")
.data(euRegionsJSON.features)
.enter().append("path")
.attr("d", geoPath)
.attr("class", "euMap")
.attr("fill", function(d) {return color(d.properties[sliderValue]);})
.style("stroke", "black")
.style("stroke-width", "0.4px")
.on("click",function(d) {
highlight(this);
setInfoBoxContent(d);
createPlot(d);
});
}
function highlight(mapClickedPath){
d3.selectAll('path').style('fill',null);
d3.select(mapClickedPath).style("fill", "#3333ff");
}
function setInfoBoxContent(mapClickedFeature) {
$('#regionName').text(mapClickedFeature.properties.Region);
$('#unemploymentRate').text(mapClickedFeature.properties[sliderValue]);
}
function zoomed() {
g.style("stroke-width", 1.5 / d3.event.transform.k + "px");
g.attr("transform", d3.event.transform);
}
$("#resetButton").on("click",reset);
function reset(){
svgEurope.transition().call(zoom.transform, d3.zoomIdentity);
d3.selectAll('path').style('fill',null);
$('#regionName').text("Europe");
$('#unemploymentRate').text("100");
}
// Barplot
var margin = {top: 10, right: 20, bottom: 35, left: 20};
var svgPlotWidth = (0.33 * wrapperWidth) - margin.left - margin.right;
var svgPlotHeight = 250 - margin.top - margin.bottom;
var svgEuropeDiagramm = d3.select("#svgPlot").append("svg")
.attr("width", svgPlotWidth + margin.left + margin.right)
.attr("height", svgPlotHeight + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var x = d3.scaleBand()
.rangeRound([0, svgPlotWidth])
.padding(0.25);
var y = d3.scaleLinear()
.rangeRound([svgPlotHeight, 0]);
function createPlot(mapClickedFeature) {
var getFeatureEntries = d3.entries(mapClickedFeature.properties)
var featureEntries = [];
for (var i=0;i<=10;i++){
if (getFeatureEntries[i].value == "NA") {
getFeatureEntries[i].value = 0;
featureEntries.push(getFeatureEntries[i])
} else {
featureEntries.push(getFeatureEntries[i])
}
}
x.domain(featureEntries.map(function(d) { return d.key; }));
y.domain([0, 50]);
svgEuropeDiagramm.selectAll(".euPlot").remove();
svgEuropeDiagramm.selectAll(".euPlot")
.data(featureEntries)
.enter().append("rect")
.attr("class", "euPlot")
.attr("x", function(d) { return x(d.key); })
.attr("width", x.bandwidth())
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return svgPlotHeight - y(d.value); });
svgEuropeDiagramm.selectAll(".xAxis").remove();
svgEuropeDiagramm.append("g")
.attr("transform", "translate(0," + svgPlotHeight + ")")
.attr("class","xAxis")
.call(d3.axisBottom(x))
.selectAll("text")
.attr("y", 0)
.attr("x", 9)
.attr("dy", ".35em")
.attr("transform", "rotate(90)")
.style("text-anchor", "start");
svgEuropeDiagramm.selectAll(".yAxis").remove();
svgEuropeDiagramm.append("g")
.attr("class","yAxis")
.call(d3.axisLeft(y));
}