170 lines
5.2 KiB
JavaScript
170 lines
5.2 KiB
JavaScript
|
|
// rivet
|
|
var filter = {city:'London', country:null, mode:'qmprice'};
|
|
filter['countries'] = Array.from(new Set(data.features.map(function(d){return d['properties']['country']})));
|
|
rivets.bind(document.getElementById('overlay'), {filter: filter});
|
|
|
|
|
|
function clone(d){
|
|
return JSON.parse(JSON.stringify(d));
|
|
}
|
|
|
|
function percentile(arr, p) {
|
|
if (arr.length === 0) return 0;
|
|
if (typeof p !== 'number') throw new TypeError('p must be a number');
|
|
if (p <= 0) return arr[0];
|
|
if (p >= 1) return arr[arr.length - 1];
|
|
|
|
var index = arr.length * p,
|
|
lower = Math.floor(index),
|
|
upper = lower + 1,
|
|
weight = index % 1;
|
|
|
|
if (upper >= arr.length) return arr[lower];
|
|
return arr[lower] * (1 - weight) + arr[upper] * weight;
|
|
}
|
|
|
|
function update(){
|
|
// close overlay
|
|
var modal = document.getElementById('modal_overlay');
|
|
modal.classList.toggle('modal-open');
|
|
|
|
// init heatmap
|
|
heatmap = new HexgridHeatmap(map, "hexgrid-heatmap", "waterway-label");
|
|
heatmap.setIntensity(6); // dunno yet
|
|
heatmap.setSpread(0.15); // dunno yet
|
|
heatmap.setCellDensity(0.5); // small value == bigger hexagons
|
|
heatmap.setPropertyName(filter.mode);
|
|
|
|
|
|
// set filter
|
|
if(filter.city){
|
|
cityDim.filterExact(filter.city);
|
|
} else if(filter.country){
|
|
countryDim.filterExact(filter.country);
|
|
}else{
|
|
alert('nothing loadable');
|
|
}
|
|
filter.count = cityDim.top(Infinity).length;
|
|
|
|
var subset = {"type": "FeatureCollection", "features": []};
|
|
indexDim.top(Infinity).forEach(function(i){
|
|
subset.features.push(data.features[i.index]);
|
|
});
|
|
|
|
loadData(subset);
|
|
}
|
|
|
|
function loadData(subset){
|
|
heatmap.setData(subset);
|
|
var values = subset.features.map(function(d){return d['properties'][filter.mode]});
|
|
values = values.sort(function(a,b){return a-b;});
|
|
|
|
// setting the color stops, min is at 5th percentile, max at 95percentile
|
|
var min = values[Math.round(values.length * 0.05)];
|
|
var max = values[Math.round(values.length * 0.95)];
|
|
var colorStopsPerc = [
|
|
[0, "rgba(0,185,243,0)"],
|
|
[25, "rgba(0,185,243,0.24)"],
|
|
[60, "rgba(255,223,0,0.3)"],
|
|
[100, "rgba(255,105,0,0.3)"],
|
|
];
|
|
makeLegend(colorStopsPerc, min, max);
|
|
var colorStopsValue = colorStopsPerc.map(function(d){
|
|
return [min + d[0]*(max-min)/100, d[1]];
|
|
});
|
|
heatmap.setColorStops(colorStopsValue);
|
|
heatmap.update();
|
|
|
|
//get bounding box and zoom to that area
|
|
// we use a 1% percentile since some data can be corrupt
|
|
var longitudes = subset.features.map(function(d){return d.geometry.coordinates[0];}).sort(function(a,b){return a-b;});
|
|
var latitudes = subset.features.map(function(d){return d.geometry.coordinates[1];}).sort(function(a,b){return a-b;});
|
|
var minlng = percentile(longitudes, 0.01);
|
|
var maxlng = percentile(longitudes, 0.99);
|
|
var minlat = percentile(latitudes, 0.01);
|
|
var maxlat = percentile(latitudes, 0.99);
|
|
map.fitBounds([
|
|
[minlng, minlat],
|
|
[maxlng, maxlat]
|
|
]);
|
|
}
|
|
|
|
mapboxgl.accessToken = 'pk.eyJ1IjoiZGktdG8iLCJhIjoiY2o0bnBoYXcxMW1mNzJ3bDhmc2xiNWttaiJ9.ZccatVk_4shzoAsEUXXecA';
|
|
var map = new mapboxgl.Map({
|
|
container: 'map',
|
|
style: 'mapbox://styles/mapbox/light-v9',
|
|
center: [13.38032, 49.994210],
|
|
zoom: 5
|
|
});
|
|
|
|
|
|
map.on("load", function(){
|
|
var crossData = data.features.map(function(d, i){
|
|
//clone properties
|
|
var props = clone(d['properties']);
|
|
props['index'] = i;
|
|
return props;
|
|
});
|
|
cf = crossfilter(crossData);
|
|
qmDim = cf.dimension(function(d){return d.qm;});
|
|
cityDim = cf.dimension(function(d){return d.city;});
|
|
countryDim = cf.dimension(function(d){return d.country;});
|
|
rentDim = cf.dimension(function(d){return d.total_price;});
|
|
roomsDim = cf.dimension(function(d){return d.rooms;});
|
|
indexDim = cf.dimension(function(d){return d.index;});
|
|
});
|
|
|
|
// map.addInteraction('my-polygon-click-interaction', {
|
|
// type: 'click',
|
|
// target: 'polygons',
|
|
// handler: (e) => {
|
|
// console.log('Polygon clicked:', e.feature);
|
|
// map.setFeatureState(e.feature, {highlight: true});
|
|
// }
|
|
// });
|
|
|
|
|
|
|
|
function makeLegend(colorstops, minValue, maxValue){
|
|
/**
|
|
* colorstops: [[0, 'green'], [100, 'red']]
|
|
* @type {number}
|
|
*/
|
|
var svg_height = 300, svg_width=70;
|
|
var svg = d3.select('#svg');
|
|
var defs = svg
|
|
.attr('height', svg_height)
|
|
.attr('width', svg_width);
|
|
|
|
var linearGradient = svg.append("defs")
|
|
.append("linearGradient")
|
|
.attr("id", "linear-gradient");
|
|
|
|
linearGradient
|
|
.attr("x1", "0%")
|
|
.attr("y1", "100%")
|
|
.attr("x2", "0%")
|
|
.attr("y2", "0%");
|
|
|
|
svg.append("rect")
|
|
.attr("width", svg_width*0.4)
|
|
.attr("height", svg_height)
|
|
.attr('rx', 4)
|
|
.style("fill", "url(#linear-gradient)");
|
|
|
|
colorstops.forEach(function(d){
|
|
linearGradient.append("stop")
|
|
.attr("offset", d[0] + "%")
|
|
.attr("stop-color", d[1]);
|
|
});
|
|
|
|
|
|
var xScale = d3.scaleLinear().range([svg_height-20,0]).domain([minValue,maxValue]);
|
|
var xAxis = d3.axisRight(xScale).ticks(5);
|
|
|
|
svg.append("g")
|
|
.attr("class", "axis")
|
|
.attr("transform", "translate("+svg_width/2+"," + (10) + ")")
|
|
.call(xAxis);
|
|
}
|