Built with blockbuilder.org
forked from jonsadka's block: Mapping LA
license: mit |
Built with blockbuilder.org
forked from jonsadka's block: Mapping LA
<!DOCTYPE html> | |
<head> | |
<meta charset="utf-8"> | |
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' /> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
<script src='https://api.mapbox.com/mapbox-gl-js/v0.31.0/mapbox-gl.js'></script> | |
<link href='https://api.mapbox.com/mapbox-gl-js/v0.31.0/mapbox-gl.css' rel='stylesheet' /> | |
<style> | |
/* apply a natural box layout model to all elements, but allowing components to change */ | |
html { | |
box-sizing: border-box; | |
} | |
*, *:before, *:after { | |
box-sizing: inherit; | |
} | |
body { | |
margin:0;position:fixed;top:0;right:0;bottom:0;left:0; | |
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; | |
color: #1A2D37; | |
font-size: 12px; | |
} | |
#map { | |
position:absolute; | |
width: 100%; | |
height: 100%; | |
} | |
canvas { | |
position: absolute; | |
width: 100%; | |
height: 100%; | |
} | |
#property-card { | |
position: fixed; | |
height: 400px; | |
width: 300px; | |
top: 8px; | |
left: 8px; | |
border-radius: 4px; | |
background-color: #F2F2F4; | |
overflow: scroll; | |
} | |
.section { | |
background-color: #FFFFFF; | |
border-radius: 4px; | |
margin: 8px; | |
padding: 5px; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="map"></div> | |
<div id="property-card"></div> | |
<script> | |
function buildUrl(url, queryParams) { | |
url += '?'; | |
queryParams.forEach(function(param){ | |
url += `${param}&` | |
}) | |
return url.slice(0, -1); | |
} | |
</script> | |
<script> | |
//Setup mapbox-gl map | |
mapboxgl.accessToken = 'pk.eyJ1Ijoiam9uc2Fka2EiLCJhIjoiY2l4ejkzM3FuMDA0bzMycDl2ZXE0cGw2byJ9.YVf_mtIw3xABynhjwojKQg'; | |
const map = new mapboxgl.Map({ | |
center: [-118.4, 34], | |
container: 'map', // id of the container | |
pitch: 40, | |
scrollZoom: true, | |
style: 'mapbox://styles/jonsadka/cixzpqzbj003g2srjakw769o5', | |
zoom: 9 | |
}) | |
map.addControl(new mapboxgl.NavigationControl()) | |
function project(d) { | |
const position = getLatLong(d); | |
if (position) { | |
return map.project(position); | |
} | |
} | |
function getLatLong(d) { | |
const longitude = +d.center_lon || (+d.location_1 || {}).longitude; | |
const latitude = +d.center_lat || (+d.location_1 || {}).latitude; | |
if (longitude && latitude) { | |
return new mapboxgl.LngLat(longitude, latitude); | |
} | |
} | |
const container = map.getCanvasContainer(); | |
const canvas = d3.select(container).append('canvas').node(); | |
const boundingBox = document.body.getBoundingClientRect(); | |
canvas.width = boundingBox.width; | |
canvas.height = boundingBox.height; | |
const context = canvas.getContext('2d'); | |
const url06To16 = 'https://data.lacounty.gov/resource/hvzm-fn38.json'; | |
d3.queue() | |
.defer(d3.json, buildUrl(url06To16, [ | |
'$where=units > 22', '$limit=20000', 'yearbuilt=1989' | |
])) | |
.await(initialRender) | |
let diagram; | |
function initialRender(error, propertyData) { | |
if (error) return console.warn(error); | |
console.log( | |
propertyData.length, | |
propertyData[0], | |
getLatLong(propertyData[0]), | |
project(propertyData[0]) | |
) | |
propertyData.map(function(property){ | |
const point = project(property); | |
if (point) { | |
property.computed = property.computed || {}; | |
property.computed.x = point.x; | |
property.computed.y = point.y; | |
} | |
return property; | |
}) | |
const voronoi = d3.voronoi() | |
.x(function(d){ return d.computed.x}) | |
.y(function(d){ return d.computed.y}); | |
diagram = voronoi(propertyData); | |
// Initial render | |
render(propertyData); | |
// re-render our visualization whenever the view changes | |
map.on('viewreset', function() { | |
context.clearRect(0, 0, boundingBox.width, boundingBox.height); | |
propertyData.map(function(property){ | |
const point = project(property); | |
if (point) { | |
property.computed = property.computed || {}; | |
property.computed.x = point.x; | |
property.computed.y = point.y; | |
} | |
return property; | |
}) | |
diagram = voronoi(propertyData); | |
render(propertyData); | |
}) | |
map.on('move', function() { | |
context.clearRect(0, 0, boundingBox.width, boundingBox.height); | |
propertyData.map(function(property){ | |
const point = project(property); | |
if (point) { | |
property.computed = property.computed || {}; | |
property.computed.x = point.x; | |
property.computed.y = point.y; | |
} | |
return property; | |
}) | |
diagram = voronoi(propertyData); | |
render(propertyData); | |
}) | |
let lastProperty; | |
map.on('mousemove', function(event) { | |
const voronoiCell = diagram.find(event.point.x, event.point.y); | |
const propertyCard = document.getElementById('property-card'); | |
if (lastProperty !== voronoiCell.data.propertylocation) { | |
lastProperty = voronoiCell.data.propertylocation; | |
var content = document.createElement('div'); | |
content.innerText = lastProperty; | |
content.className = 'section'; | |
propertyCard.append(content); | |
propertyCard.scrollTop = propertyCard.scrollHeight; | |
} | |
}) | |
} | |
function render(data) { | |
const propertyValues = data.reduce(function(_propertyValues, property){ | |
if (property.roll_totlandimp) { | |
_propertyValues.push(+property.roll_totlandimp); | |
} | |
return _propertyValues; | |
}, []); | |
const propertyValueColor = d3.scaleLog() | |
.range(['hsl(62,100%,90%)', 'hsl(228,30%,20%)']) | |
.domain([d3.quantile(propertyValues, .01), d3.quantile(propertyValues, .99)]) | |
.interpolate(d3.interpolateHcl); | |
context.clearRect(0, 0, boundingBox.width, boundingBox.height); | |
data.forEach(function(property, idx) { | |
context.strokeStyle = '#F2F2F4'; | |
if (property.computed) { | |
context.fillStyle = propertyValueColor(+property.roll_totlandimp); | |
context.beginPath(); | |
context.arc(property.computed.x, property.computed.y, 4, 0, Math.PI * 2); | |
context.fill(); | |
context.stroke(); | |
} | |
}) | |
} | |
</script> | |
</body> |