<link rel="stylesheet" href="https://unpkg.com/leaflet@1.3.4/dist/leaflet.css"
integrity="sha512-puBpdR0798OZvTTbP4A8Ix/l+A4dHDD0DGqYW6RQ+9jxkRFclaxxQb/SJAWZfWAkuyeQUytO7+7N4QKrDh+drA=="
crossorigin=""/>
<script src="https://unpkg.com/leaflet@1.3.4/dist/leaflet.js"
integrity="sha512-nMMmRyTVoLYqjP9hrbed9S+FzjZHW5gY1TWCHA5ckwXZBadntCNs8kEqAWdrb9O7rxbCaA4lKTIWjDXZxflOcA=="
crossorigin=""></script>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"
integrity="sha256-VazP97ZCwtekAsvgPBSUwPFKdrwD3unUfSGVYrahUqU="
crossorigin="anonymous"></script>
<style>
html, body { margin: 0; padding: 0; height: 100%; }
#map { min-height: 100%; }
</style>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.3.4/dist/leaflet.css"
integrity="sha512-puBpdR0798OZvTTbP4A8Ix/l+A4dHDD0DGqYW6RQ+9jxkRFclaxxQb/SJAWZfWAkuyeQUytO7+7N4QKrDh+drA=="
crossorigin=""/>
<script src="https://unpkg.com/leaflet@1.3.4/dist/leaflet.js"
integrity="sha512-nMMmRyTVoLYqjP9hrbed9S+FzjZHW5gY1TWCHA5ckwXZBadntCNs8kEqAWdrb9O7rxbCaA4lKTIWjDXZxflOcA=="
crossorigin=""></script>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"
integrity="sha256-VazP97ZCwtekAsvgPBSUwPFKdrwD3unUfSGVYrahUqU="
crossorigin="anonymous"></script>
<style>
html, body { margin: 0; padding: 0; height: 100%; }
#map { min-height: 100%; }
</style>
<script>
var map = L.map('map', {
center: [38.1625228, -96.9633171],
zoom: 4
});
var Stamen_TerrainBackground = L.tileLayer('https://stamen-tiles-{s}.a.ssl.fastly.net/terrain-background/{z}/{x}/{y}{r}.{ext}', { attribution: 'Map tiles by Stamen Design, CC BY 3.0 — Map data © OpenStreetMap contributors', subdomains: 'abcd', minZoom: 0, maxZoom: 18, ext: 'png' });
//change the file name to yours
$.getJSON("https://github.com/clhyman/clhyman.github.io/blob/master/geoJson")
var info = processData(data);
createPropSymbols(info.timestamps, data);
createSliderUI(info.timestamps);
});
function processData(data) {
// First, initialize the variables to hold the timestamps and min/max population values
var timestamps = []; // square brackets to define an array of data
// because there are multiple timestamps
var min = Infinity; // for the min, begin with the largest possible value - infinity
var max = -Infinity;// for the max, begin with the smallest possible value - negative infinity
// Go through each row/feature of the data table
// Note data is the variable name in the function definition - processData(data)
for (var feature in data.features) {
var properties = data.features[feature].properties;
// At each row, go through the columns/attributes to get the values
for (var attribute in properties) {
if ( attribute != 'province' &&
attribute != 'POP' &&
attribute !='SUSP_COUNT' &&
attribute != 'LATITUDE' &&
attribute != 'LONGITUDE' )
timestamps.push(attribute); // The JS push() method adds new items to the end of an array
// and returns the new length of the array
}
if (properties[attribute] < min) {
min = properties[attribute]; // record/update the current smaller values as the min
}
if (properties[attribute] > max) {
max = properties[attribute]; // record/update the current larger values as the max
}
}
}
}
return {
timestamps : timestamps,
min : min,
max : max
}
}
// The function to draw the proportional symbols
function createPropSymbols(timestamps, data) {
cities = L.geoJson(data, {
// By default, Leaflet draws geojson points as simple markers
// To alter this, the pointToLayer function needs to be used
pointToLayer: function(feature, latlng) {
return L.circleMarker(latlng, { // we use circle marker for the points
fillColor: "#501e65", // fill color of the circles
color: '#501e65', // border color of the circles
weight: 2, // circle line weight in pixels
fillOpacity: 0.5 // fill opacity (0-1)
}).on({
mouseover: function(e) {
this.openPopup();
this.setStyle({fillColor: 'green'}); // fill color turns green when mouseover
},
mouseout: function(e) {
this.closePopup();
this.setStyle({fillColor: '#501e65'}); // fill turns original color when mouseout
}
});
}
}).addTo(map);
updatePropSymbols(timestamps[0]); // this function is defined below
// When loaded, the map will first show proportional symbols with the first timestamp's data
}
function createSliderUI(timestamps) { var sliderControl = L.control({ position: 'bottomleft'} ); // position of the slider // Another use of L.control :) sliderControl.onAdd = function(map) { //initialize a range slider with mousedown control var slider = L.DomUtil.create("input", "range-slider"); L.DomEvent.addListener(slider, 'mousedown', function(e) { L.DomEvent.stopPropagation(e); });
// Define the labels of the time slider as an array of strings
// Modify this for your data
var labels = ["1950", "1960","1970","1980", "1990", "2000","2010"];
$(slider)
.attr({
'type':'range',
'max': timestamps[timestamps.length-1],
'min':timestamps[0],
'step': 1, // Change this to match the numeric interval between adjacent timestamps
'value': String(timestamps[0])
})
.on('input change', function() {
updatePropSymbols($(this).val().toString()); // automatic update the map for the timestamp
var i = $.inArray(this.value,timestamps);
$(".temporal-legend").text(labels[i]); // automatic update the label for the timestamp
});
return slider;
} sliderControl.addTo(map); createTimeLabel("1950"); //The starting timestamp label }
// Add labels to the time slider when the map first loaded function createTimeLabel(startTimestamp) { var temporalLegend = L.control({position: 'bottomleft' }); // same position as the slider // One more use of L.control !! temporalLegend.onAdd = function(map) { var output = L.DomUtil.create("output", "temporal-legend"); $(output).text(startTimestamp); return output; } temporalLegend.addTo(map); }
// The function to update/resize each circle marker according to a value in the time series
function updatePropSymbols(timestamp) {
cities.eachLayer(function(layer) { // eachLayer() is an Leaflet function to iterate over the layers/points of the map
var props = layer.feature.properties; // attributes
var radius = calcPropRadius(props[timestamp]); // circle radius, calculation function defined below
// pop-up information (when mouseover) for each city is also defined here
var popupContent = props.name + ' ' + timestamp + ' population: ' + String(props[timestamp]);
layer.setRadius(radius); // Leaflet method for setting the radius of a circle
layer.bindPopup(popupContent, { offset: new L.Point(0,-radius) }); // bind the popup content, with an offset
});
}
// calculate the radius of the proportional symbols based on area
function calcPropRadius(attributeValue) {
var scaleFactor = 0.001; // the scale factor is used to scale the values; the units of the radius are in meters
// you may determine the scale factor accordingly based on the range of the values and the mapping scale
var area = attributeValue * scaleFactor;
return Math.sqrt(area/Math.PI); // the function return the radius of the circle to be used in the updatePropSymbols()
}
</script>