Published
Edited
Nov 21, 2019
9 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
viewof crimeBars = V({
width: 300,
height: 400,
autosize: {
type: "pad",
//contains: "padding"
},
data: {values: vanCrime2},
config: {
background: "white"
},
transform: [
//filter by our click selection
//We need to tell vega-lite how to parse the dates we pass it for filtering using
//our brush selection below
{filter: {field: "NEIGHBOURHOOD", oneOf:mapSelection}}
],
mark: "rect",
selection: {
offenseSelect: {fields: ["Offense"], on: "click", type: "multi"}
},
encoding: {
x: {field: "Offense", type: "nominal"},
y: {field: "*", type: "quantitative", aggregate: "count"},
color: {field: "Offense Category", type: "nominal"}
}
})
Insert cell
Insert cell
ObsBSelection = Generators.observe(notify => { //creates generator that observes changes within the event listener.
const signaled = (name, value) => notify(value); //two arguments for keyvalue pair. Notifies when updated
crimeBars.addSignalListener("offenseSelect", signaled); //Adds signal listener to our selection
notify(crimeBars.signal("offenseSelect")); //notifies when signal exists
return () => crimeBars.removeSignalListener("offenseSelect", signaled); // removes signal listener to clear
})
Insert cell
Insert cell
bSelection = {
if(isEmpty(ObsBSelection)){
return Array.from(new Set(vanCrime2.map(d => d.Offense))) //Set is a new datastructure in ES6 -> only unique values
} else {
return ObsBSelection.Offense
}
}
Insert cell
function isEmpty( obj ) {
for ( var prop in obj ) {
return false;
}
return true;
}
Insert cell
Insert cell
Z = V({
width: 700,
height: 300,
data: {values: vanCrime2},
mark: "area",
transform: [
{filter: {field: "Offense", oneOf: bSelection}}, //filter by our click selection
//We need to tell vega-lite how to parse the dates we pass it for filtering using
//our brush selection below
{filter: {timeUnit: "yearmonthdate",field: "Date", range: BrSelection}}
],
encoding: {
x: { field: "Date", type: "temporal"},
y: {field: "*", type: "quantitative", aggregate: "count"},
color: {field: "Offense Category", type: "nominal"}
}
})
Insert cell
Insert cell
viewof crimeTime = V({
width: 500,
height: 300,
data: {values: vanCrime2},
mark: "area",
transform: [
{filter: {field: "Offense", oneOf: bSelection}} //filter by our click selection
//We need to tell vega-lite how to parse the dates we pass it for filtering using
//our brush selection below
],
selection: {
timeBrush: {type: "interval", encodings: ["x"]}
},
encoding: {
x: {field: "Date", type: "temporal"},
y: {field: "*", type: "quantitative", aggregate: "count"},
color: {field: "Offense Category", type: "nominal"}
}
})
Insert cell
Insert cell
mutable ObsBrSelection = Generators.observe(notify => { //creates generator that observes changes within the event listener.
const signaled = (name, value) => notify(value); //two arguments for keyvalue pair. Notifies when updated
crimeTime.addSignalListener("timeBrush", signaled); //Adds signal listener to our selection
notify(crimeTime.signal("timeBrush")); //notifies when signal exists
return () => crimeTime.removeSignalListener("timeBrush", signaled); // removes signal listener to clear
})
Insert cell
BrSelection = {
if(isEmpty(mutable ObsBrSelection)){
// use d3 to get min and max and format them correctly
// we need to convert the string date to UTC so d3 can parse it
// for its extent function. Then we convert it back to the same format
return d3.extent(vanCrime2, d => dateFormat(timeParse(d.Date)))
} else {
// The returned data is UNIX Epoch format, meaning it is in milliseconds.
// We need to convert it to UTC and then to the date format used in our data.
return ObsBrSelection.Date.map(d => dateFormat(epochParse(d)))
}
}
Insert cell
//mutable bakeselect = BrSelection
Insert cell
timeParse = d3.timeParse("%m/%d/%y")
Insert cell
dateFormat = d3.timeFormat("%m/%d/%y")
Insert cell
epochParse = d3.timeParse("%Q"); //milliseconds UNIX epoch
Insert cell
Insert cell
//We are writing the event listener function for each feature in our geojson layer
function onEachFeature(feature, layer) {
layer.on({
click:whenClicked //callback function
});
}
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
//We define an null variable to be replaced by our callback function
mutable nHood = null
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
viewof CrimeMap2 = {
let container = DOM.element('div', { style: `width:${width}px;height:${width/2}px` });
yield container
let map = L.map(container).setView([49.2527, -123.1207], 11.5);
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: onEachFeature
}).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.
let crimePoints = FilteredGeoCrimes.features.map(feature =>
feature.geometry.coordinates.slice().reverse().concat([0.1]));
map.on('click', () =>
mutable nHood = null
);

let crimeLayer = heatLayer(crimePoints).addTo(map);
}
Insert cell
Type JavaScript, then Shift-Enter. Ctrl-space for more options. Arrow ↑/↓ to switch modes.

Insert cell
Insert cell
FilteredFlatCrimes = {
mutable circBreak = vanCrime2.filter(d => bSelection.includes(d.Offense))
.filter(d => mapSelection.includes(d.NEIGHBOURHOOD))
.filter(d => timeParse(d.Date) >= timeParse(BrSelection[0]) && timeParse(d.Date) <= timeParse(BrSelection[1]))

}
Insert cell
mutable circBreak = null
Insert cell
FilteredGeoCrimes = {
return new Object({type: "FeatureCollection", features: vanCrimeFiltered })
}
Insert cell
vanCrimeFiltered = vanCrime.features.filter(d => bSelection.includes(d.properties.Offense))
.filter(d => timeParse(d.properties.Date) >= timeParse(BrSelection[0]) && timeParse(d.properties.Date) <= timeParse(BrSelection[1]))
.filter(d => mapSelection.includes(d.properties.NEIGHBOURHOOD))
Insert cell
Insert cell
Insert cell
dashboard = html`<table style="width:100%">
<tr>
<td>${viewof crimeBars}</td>
<td>${viewof CrimeMap2}</td>
</tr>
</table>`

Insert cell
Insert cell
Insert cell
Insert cell
L = require('leaflet@1.2.0')
Insert cell
html`<link href='${resolve('leaflet@1.2.0/dist/leaflet.css')}' rel='stylesheet' />`
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
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