Published
Edited
Nov 27, 2019
8 stars
Insert cell
Insert cell
viewof crimeCharts = V({
width: 400,
height:300,
data: {values: vanCrime2},
transform: [
{filter: {field: "NEIGHBOURHOOD", oneOf: mapSelection}}
],
config: {
background: "white"
},
hconcat: [{
transform: [
{filter: {selection: "timeBrush"}}
],
mark: "rect",
selection: {
offenseSelect: {fields: ["Offense"], on: "click", type: "multi"}
},
encoding: {
y: {field: "Offense", type: "nominal"},
x: {field: "*", type: "quantitative", aggregate: "count"},
color: {
condition: {
field: "Offense Category",
type: "nominal",
selection: "offenseSelect",
scale: {
domain: Array.from(new Set(vanCrime2.map(d => d['Offense Category']))),
range: ["#4e79a7","#f28e2c","#e15759","#76b7b2"]}},
value: "lightgrey"
}
}
}, {
transform: [
{filter: {selection: "offenseSelect"}}
],
width: 400,
mark: "line",
selection: {
timeBrush: {type: "interval", encodings: ["x"]}
},
encoding: {
x: {field: "Date", type: "temporal"},
y: {field: "*", type: "quantitative", aggregate: "count"},
color: {field: "Offense Category", type: "nominal", scale: {domain: Array.from(new Set(vanCrime2.map(d => d['Offense Category']))), range: ["#4e79a7","#f28e2c","#e15759","#76b7b2"]}}
}
}]
})
Insert cell
Insert cell
Insert cell
epochParse = d3.timeParse("%Q");//ms in unix epochs
Insert cell
timeParse = d3.timeParse("%m/%d/%y")
Insert cell
dateFormat = d3.timeFormat("%m/%d/%y")
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
viewof CrimeMap = {
let width = 900
let height = 400
let container = DOM.element('div', { style: `width:${width}px;height:${width/2}px` });
yield container
let map = L.map(container);
let osmLayer = L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}@2x.png', {
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
let VanAreasLayer = L.geoJson(vanAreas, { //instantiates a new geoJson layer using built in geoJson handling
weight: 2, //Attributes of polygons including the weight of boundaries and colors of map.
color: "#432",
onEachFeature: areaClick
}).bindPopup(function (Layer) { //binds a popup when clicking on each polygon to access underlying data
return Layer.feature.properties.NAME;
}).addTo(map); //Adds the layer to the map.
//This function formats our geojson file to be commensurate with leaflet. Geojson files are formatted using
//[lon, lat], we need to reverse this. we are creating an arrray of "feature(s)", that hold two lat lon coordinates
// and we reverse them. We then concatenate (or add on) final value which is the intensity. This is the strength of
// of each of these points. This heat map works by overlapping points and the added intensity of each dot changes the // color based on hot spots.
let crimePoints = FilteredGeoCrimes.features.map(feature =>
feature.geometry.coordinates.slice().reverse().concat([0.1]));
//We add this layer to the map.
let crimeLayer = heatLayer(crimePoints,
{
gradient:
{
0.0: 'yellow',
0.5: 'lightgreen',
1.0: 'purple'
},
radius: 10,
blur:15,
maxZoom: 16,
scaleRadius: true,
max: 0.1
}).addTo(map);
map.on('click', () =>
mutable nHood = null
);
map.fitBounds(VanAreasLayer.getBounds());
}
Insert cell
Insert cell
//We are writing the event listener function for each feature in our geojson layer
function areaClick(feature, layer) {
layer.on({
click:whenClicked //callback function
});
}
Insert cell
Insert cell
//We update our data in the function using mutable. This overrides observables defaults so we can push data from within //function scope globally
whenClicked = (event) => {
mutable nHood = event.target.feature.properties.NAME
}
Insert cell
mutable nHood = null
Insert cell
Insert cell
mapSelection = {
if(nHood == null ){
return Array.from(new Set(vanCrime2.map(d => d.NEIGHBOURHOOD))) //Set is a new datastructure in ES6 -> only unique values
} else {
return [nHood]
}
}
Insert cell
Insert cell
Insert cell
Insert cell
vanCrimeFiltered = vanCrime.features.filter(d => bSelection.includes(d.properties.Offense))
.filter(d => timeParse(d.properties.Date) >= timeParse(TSelection[0]) &&
timeParse(d.properties.Date) <= timeParse(TSelection[1]))
.filter(d => mapSelection.includes(d.properties.NEIGHBOURHOOD))
Insert cell
FilteredGeoCrimes = {
return new Object({type: "FeatureCollection", features: vanCrimeFiltered})
}
Insert cell
Insert cell
Insert cell
Insert cell
{
const button = html`<button>Fullscreen`;
button.onclick = () => button.parentElement.nextElementSibling.requestFullscreen();
return button;
}
Insert cell
html`<div style="
background: #fff;
display: grid;
height: ${screen.height / screen.width * 100}vw;
grid-template-areas:
'a'
'b';
grid-gap: 10px;
">
<div name="a" style="grid-area:a;border:solid 1px #ccc;text-align: center">${viewof crimeCharts}</div>
<div name="b" style="grid-area:b;border:solid 1px #ccc;text-align: center">${viewof CrimeMap}</div>
</div>`
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
vanCrime = FileAttachment("vanCrime.json").json()
Insert cell
vanAreas = FileAttachment("VanAreas.json").json()
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more