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

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more