Skip to content

Instantly share code, notes, and snippets.

@harllos
Last active February 14, 2019 13:18
Show Gist options
  • Save harllos/7d85bd4bad5f7fad1987931b16b9fe82 to your computer and use it in GitHub Desktop.
Save harllos/7d85bd4bad5f7fad1987931b16b9fe82 to your computer and use it in GitHub Desktop.
oriá 2018
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title>Oriá 2018</title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.53.0/mapbox-gl.js'></script>
<script src="https://npmcdn.com/@turf/turf/turf.min.js"></script>
<script src="https://unpkg.com/supercluster@3.0.2/dist/supercluster.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chroma-js/1.3.4/chroma.min.js"></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.53.0/mapbox-gl.css' ' rel='stylesheet' />
<link href="https://api.mapbox.com/mapbox-assembly/v0.21.1/assembly.min.css" rel="stylesheet">
<script async defer src="https://api.mapbox.com/mapbox-assembly/v0.21.1/assembly.js"></script>
<style>
body {
margin: 0;
padding: 0;
}
#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
</style>
</head>
<body>
<div class='viewport-full relative clip'>
<div class='viewport-twothirds viewport-full-ml relative'>
<div id='map' class='absolute top left right bottom'></div>
</div>
<div class='absolute top-ml left z1 w-full w300-ml px12 py12'>
<div class='viewport-third h-auto-ml hmax-full bg-gray-dark round-ml shadow-darken5 scroll-auto'>
<div class='p24 my12 mx12 scroll-auto color-white'>
<h3 class='txt-l txt-bold my6 mx6'>Oriá 2018</h3>
<h5 class='txt-m txt-bold px12'>Agregar votos:</h5>
<div class='select-container py12' id="select-container">
<select class='select' id="select-option">
<option value="sum">sum</option>
<option value="count">count</option>
<option value="avg">avg</option>
<option value="min">min</option>
<option value="max">max</option>
</select>
<div class='select-arrow'></div>
</div>
</div>
</div>
</div>
<script>
var select_el = document.getElementById('select-option')
var select_value = select_el.value
var clusterRadius = 20;
var clusterMaxZoom = 16;
//Property of geojson data I want to aggregate on. Must be numeric for this example
var propertyToAggregate = "QT_VOTOS"
let data_url = "https://gist.githubusercontent.com/harllos/7cf557c6b8a0a7f44b26fb0efd4c388a/raw/bee464af9e0cf906720b47ee1748e163cff0755f/oria2018";
var mydata;
var currentZoom;
var color = 'YlOrRd';
var clusterData;
var worldBounds = [-180.0000, -90.0000, 180.0000, 90.0000];
// HELPER FUNCTIONS
function getFeatureDomain(geojson_data, myproperty) {
let data_domain = []
turf.propEach(geojson_data, function(currentProperties, featureIndex) {
data_domain.push(Math.round(Number(currentProperties[myproperty]) * 100 / 100))
})
return data_domain
}
function createColorStops(stops_domain, scale) {
let stops = []
stops_domain.forEach(function(d) {
stops.push([d, scale(d).hex()])
});
return stops
}
function createRadiusStops(stops_domain, min_radius, max_radius) {
let stops = []
let stops_len = stops_domain.length
let count = 1
stops_domain.forEach(function(d) {
stops.push([d, min_radius + (count / stops_len * (max_radius - min_radius))])
count += 1
});
return stops
}
//Supercluster with property aggregation
var cluster = supercluster({
radius: clusterRadius,
maxZoom: clusterMaxZoom,
initial: function() {
return {
count: 0,
sum: 0,
min: Infinity,
max: -Infinity
};
},
map: function(properties) {
return {
count: 1,
sum: Number(properties[propertyToAggregate]),
min: Number(properties[propertyToAggregate]),
max: Number(properties[propertyToAggregate])
};
},
reduce: function(accumulated, properties) {
accumulated.sum += Math.round(properties.sum * 100) / 100;
accumulated.count += properties.count;
accumulated.min = Math.round(Math.min(accumulated.min, properties.min) * 100) / 100;
accumulated.max = Math.round(Math.max(accumulated.max, properties.max) * 100) / 100;
accumulated.avg = Math.round(100 * accumulated.sum / accumulated.count) / 100;
}
});
mapboxgl.accessToken = 'pk.eyJ1IjoiaGFybGxvcyIsImEiOiJjamcyaXA2d2IxandwMzNwOWF2OWhlYmMxIn0.OtW1buMdQ9G60NkH2OGMfw';
var map = new mapboxgl.Map({
container: 'map',
style: "mapbox://styles/harllos/cjs3vclun049g1fpctk8qe8sb?optimize=true",
center: [-8,-37.105],
zoom: 9,
hash: true
});
map.on('style.load', function() {
fetch(data_url)
.then(res => res.json())
.then((out) => {
mydata = out;
// USE SUPERCLUSTER TO CLUSTER THE GEOJSON DATA
cluster.load(mydata.features);
// CREATE THE MAP
initmap();
})
.catch(err => console.error(err));
});
var colorStops, radiusStops;
function updateClusters(repaint) {
currentZoom = map.getZoom();
clusterData = turf.featureCollection(cluster.getClusters(worldBounds, Math.floor(currentZoom)))
let stops_domain = chroma.limits(getFeatureDomain(clusterData, select_value), 'e', 8)
//coloração original era definida pela linha abaixo. copiei, colei e utilizei escala logarítimica que tirei daqui: https://github.com/gka/chroma.js/
//var scale = chroma.scale(color).domain(stops_domain).mode('lab')
var scale = chroma.scale(color).domain([100, 2000], 5, 'log').mode('lab')
colorStops = createColorStops(stops_domain, scale)
radiusStops = createRadiusStops(stops_domain, 10, 25);
if (repaint) {
map.setPaintProperty('clusters', 'circle-color', {
property: select_value,
stops: colorStops
});
map.setPaintProperty('clusters', 'circle-radius', {
property: select_value,
stops: radiusStops
});
map.setPaintProperty('unclustered-point', 'circle-color', {
property: propertyToAggregate,
stops: colorStops
});
map.setLayoutProperty('cluster-count', "text-field", "{" + select_value + "}");
}
}
function initmap() {
updateClusters(false);
select_el.addEventListener('change', function(e) {
// Update selected aggregation on dropdown
select_value = select_el.value
updateClusters(true);
})
map.addSource("earthquakes", {
type: "geojson",
data: clusterData,
buffer: 1,
maxzoom: 14
});
map.addLayer({
id: "clusters",
type: "circle",
source: "earthquakes",
filter: ["has", "point_count"],
paint: {
"circle-color": {
property: select_value,
stops: colorStops
},
"circle-blur": 0.20,
"circle-radius": {
property: select_value,
type: "interval",
stops: radiusStops
}
}
}, "waterway-label");
map.addLayer({
id: "unclustered-point",
type: "circle",
source: "earthquakes",
filter: ["!has", "point_count"],
paint: {
"circle-color": {
property: propertyToAggregate,
stops: colorStops
},
"circle-radius": 4,
"circle-stroke-width": 1,
"circle-stroke-color": "#fff"
}
}, "waterway-label");
map.addLayer({
id: "cluster-count",
type: "symbol",
source: "earthquakes",
filter: ["has", "point_count"],
layout: {
"text-field": "{" + select_value + "}",
"text-font": ["DIN Offc Pro Medium", "Roboto Black"],
"text-size": 13
},
//paint: {
// "text-halo-color": "black",
//"text-halo-width": 0.5
//}
});
map.on('zoom', function() {
newZoom = map.getZoom();
if (Math.floor(currentZoom) == 0) {
currentZoom = 1
};
if (Math.floor(newZoom) != Math.floor(currentZoom)) {
currentZoom = newZoom
updateClusters(true);
map.getSource('earthquakes').setData(clusterData)
}
})
};
// adding menu but doing nothing yet
var popup = new mapboxgl.Popup({
closeButton: false,
closeOnClick: true
});
map.on('click', 'unclustered-point', function(e) {
// Change the cursor style as a UI indicator.
map.getCanvas().style.cursor = 'pointer';
var coordinates = e.features[0].geometry.coordinates.slice();
var description = '<h2>votos: '+e.features[0].properties.QT_VOTOS+'<h2>local: '+e.features[0].properties.nome_local+'</h2>'+'\n'
+'<h2>Endereço: ' +e.features[0].properties.Endereço+'</h2><p>município: '+ e.features[0].properties.NM_MUNICIPIO + '\n'
+'</h2><p>aptos: '+e.features[0].properties.QT_APTOS + '</h2><p>comparecimento: ' + e.features[0].properties.QT_COMPARECIMENTO
;
// Ensure that if the map is zoomed out such that multiple
// copies of the feature are visible, the popup appears
// over the copy being pointed to.
while (Math.abs(e.lngLat.lng - coordinates[0]) > 180) {
coordinates[0] += e.lngLat.lng > coordinates[0] ? 360 : -360;
}
// Populate the popup and set its coordinates
// based on the feature found.
popup.setLngLat(coordinates)
.setHTML(description)
.addTo(map);
});
map.on('mouseleave', 'unclustered-point', function() {
map.getCanvas().style.cursor = '';
popup.remove();
});
</script>
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment