Public
Edited
May 20
Insert cell
Insert cell
Insert cell
topo = FileAttachment("chicagoNeighborhoods.json").json()
Insert cell
crimes = FileAttachment("chicago_crimes_april_2025@1.csv").csv({typed: true}).then(function (data) {
// The date has a special formating not automatically detected by d3,
// so we need to parse it using UTC rather than local time
const parseDate = d3.utcParse("%m/%d/%Y %I:%M:%S %p");
data.forEach(function(d) {
d.Date = parseDate(d.Date);
});
return data;
})
Insert cell
crimes
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
Insert cell
import {vl} from '@vega/vega-lite-api-v5'
Insert cell
mapGraphic = {
const map = vl.markGeoshape({fill: "#ddd", stroke: "#fff", strokeWidth: 1})
.data(vl.topojson(topo).feature("chicago_neighborhoods"));

const circlesOfCrimes = vl.markCircle()
.data(crimes)
.encode(
vl.latitude().fieldQ('Latitude'),
vl.longitude().fieldQ('Longitude'),
vl.color().fieldN('Primary Type')
)

return vl.layer(map, circlesOfCrimes)
.project(
vl.projection('transverseMercator').rotate([90, 40.5])
)
.width(width*0.40)
.height(500)
.render()

}
Insert cell
barGraphic = {

const bar = vl.markBar()
.data(crimes)
.encode(
vl.x().fieldN('Primary Type'),
vl.y().count('IUCR').title(null),
vl.color().fieldN('Primary Type')
)
.title('Number of Crimes by Type')
.width(width*0.30)
.height(200)

return bar.render()
}
Insert cell
markLineGraphic = {

const markLine = vl.markLine()
.data(crimes)
.encode(
vl.x().fieldT('Date').timeUnit('monthdate'),
vl.y().count('IUCR').title(null),
vl.color().fieldN('Primary Type')
)
.title('Number of Crimes by Day')
.width(width*0.30)
.height(150)

return markLine.render()
}
Insert cell
Insert cell
dashboard = {

const map = vl.markGeoshape({fill: "#ddd", stroke: "#fff", strokeWidth: 1})
.data(vl.topojson(topo).feature("chicago_neighborhoods"));

const circlesOfCrimes = vl.markCircle()
.data(crimes)
.encode(
vl.latitude().fieldQ('Latitude'),
vl.longitude().fieldQ('Longitude'),
vl.color().field('Primary Type')
)



const bar = vl.markBar()
.data(crimes)
.encode(
vl.x().field('Primary Type'),
vl.y().count('IUCR').title(null),
vl.color().fieldN('Primary Type')
)
.title('Number of Crimes by Type')
.width(width*0.30)
.height(200)

const markLine = vl.markLine()
.data(crimes)
.encode(
vl.x().fieldT('Date').timeUnit('monthdate'),
vl.y().count('IUCR').title(null),
vl.color().fieldN('Primary Type')
)
.title('Number of Crimes by Day')
.width(width*0.30)
.height(150)

return vl.hconcat(
vl.layer(map, circlesOfCrimes)
.project(vl.projection('transverseMercator').rotate([90, 40.5]))
.width(width*0.40)
.height(500)
, vl.vconcat(bar,markLine)).resolve({scale: {size: 'independent'}})
.render()
}
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