Public
Edited
May 8, 2024
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
import {interval} from '@mootari/range-slider'
Insert cell
viewof time = interval([0, 24], {
step: 1,
value: [18, 23],
label: 'Time Range',
description: 'Choose a range of time to view crimes. Time counted as military time.',
})
Insert cell
startTime1 = time[0]
Insert cell
endTime1 = time[1]
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
map7 = {
const container = yield htl.html`<div style="height: 500px"></div>`;
const map = L.map(container, {
minZoom: 14.5,
maxZoom: 17.5,
});
map.setView([40.80794694447276, -73.96214143872722], 16);

L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}@2x.png', {
attribution: '&copy; OpenStreetMap contributors'
}).addTo(map);

var phoneIcon = L.icon({
iconUrl: "https://cdn0.iconfinder.com/data/icons/map-locations-and-tourism/512/47-512.png",
//iconUrl: FileAttachment("telephone.png").url(),
iconSize: [21.12, 24.42], // size of the icon
iconAnchor: [16, 37], // point of the icon which will correspond to marker's location
popupAnchor: [-3, -30] // point from which the popup should open relative to the iconAnchor
});

var gaurdIcon = L.icon({
iconUrl: "https://static.thenounproject.com/png/63811-200.png",
//iconUrl: FileAttachment("telephone.png").url(),
iconSize: [32, 37], // size of the icon
iconAnchor: [16, 37], // point of the icon which will correspond to marker's location
popupAnchor: [-3, -50] // point from which the popup should open relative to the iconAnchor
});
L.geoJSON(columbia_university, {
onEachFeature: function(feature, layer) {
if (feature.properties && feature.properties.name) {
layer.bindPopup(feature.properties.name);
}
}
}).addTo(map);

L.geoJSON(columbia_call_boxes, {
onEachFeature: function(feature, layer) {
L.marker([feature.geometry.coordinates[1],feature.geometry.coordinates[0]], {icon: phoneIcon}).addTo(map);
}
})

const crimeLayer = L.layerGroup().addTo(map);

$: if (options2) {
crimeLayer.clearLayers(); // Clear previous layers

// Filter Clery crime data
const filteredCleryData = data.filter(d => options2.includes(d.filterCat));
let finalFilteredCleryData = filteredCleryData; // Presume no time filter needed initially

// Filter Campus crime data
let filteredCampusCrimes = campusCrimes.filter(d => options2.includes(d.filterCat));
let finalFilteredCampusCrimes = filteredCampusCrimes; // Presume no time filter needed initially

// Apply time filter only if both startTime and endTime are set
if (startTime1 != null && endTime1 != null) {
finalFilteredCleryData = filteredCleryData.filter(d => {
const crimeTime = getHourFromTimeString(d.TIME);
return crimeTime >= startTime1 && crimeTime <= endTime1;
});

finalFilteredCampusCrimes = filteredCampusCrimes.filter(d => {
const crimeTime = getHourFromTimeString(d.CMPLNT_FR_TM);
return crimeTime >= startTime1 && crimeTime <= endTime1;
});
}

finalFilteredCleryData.forEach(d => {if (!isNaN(d.LATITUDE) && !isNaN(d.LONGITUDE)) {
const color = colorScheme(d.filterCat);
const jitterFactorOne = Math.random()*0.000154
const jitterFactorTwo = Math.random()*0.000154
const polygon = L.polygon([
[d.LATITUDE - 0.00015 + jitterFactorOne, d.LONGITUDE + jitterFactorTwo],
[d.LATITUDE + jitterFactorOne, d.LONGITUDE - 0.000077 + jitterFactorTwo],
[d.LATITUDE + jitterFactorOne, d.LONGITUDE + 0.000077 + jitterFactorTwo]
], {
fillColor: color,
color: color,
weight: 1,
opacity: 1,
fillOpacity: 1
}).addTo(crimeLayer);

L.circle([d.LATITUDE + 0.00001 + jitterFactorOne, d.LONGITUDE + jitterFactorTwo], {
radius: 6.52,
fillColor: color,
color: color,
weight: 1,
opacity: 1,
fillOpacity: 1
}).addTo(crimeLayer).bindPopup(`<strong>Description:</strong> ${d.DESCRIPTION}`).bindTooltip(`<strong><u>Clery Crime:</u></strong> ${d.CRIME}<br> <strong>Date:</strong> ${d.TIME.substring(0, d.TIME.indexOf(" "))}<br><strong>Time:</strong> ${d.TIME.substring(d.TIME.indexOf(" "), d.TIME.length)}`);
}
});

finalFilteredCampusCrimes.forEach(d => {if (!isNaN(d.Latitude) && !isNaN(d.Longitude)) {
const color = colorScheme(d.filterCat);
const jitterFactorOne = Math.random()*0.000924
const jitterFactorTwo = Math.random()*0.000924
L.rectangle([
[parseFloat(d.Latitude) - 0.0000385 + jitterFactorOne, parseFloat(d.Longitude) - 0.0000385 + jitterFactorTwo],
[parseFloat(d.Latitude) + 0.0000385 + jitterFactorOne, parseFloat(d.Longitude) + 0.0000385 + jitterFactorTwo]
], {
fillColor: color,
color: color,
weight: 1,
opacity: .5,
fillOpacity: .5
}).addTo(crimeLayer).bindTooltip(`<strong><u>NYPD Crime:</u></strong> ${d.OFNS_DESC}<br><strong>Date:</strong> ${d.CMPLNT_FR_DT}<br><strong>Time:</strong> ${d.CMPLNT_FR_TM}`);
}
});
}

// add each line manually so it has a set name
var route = worseroute;
var latlngs = [];
for(const point of route)
{
latlngs.push([point.A,point.B]);
}
var worserouteLine = L.polyline(latlngs, {
color: "purple",
weight: 4,
opacity: 0
});
worserouteLine.addTo(map).on('mouseover', function(e) {
var layer = e.target;
layer.setStyle({
weight: 6
})}).on('mouseout',function(e) {
var layer = e.target;
layer.setStyle({
weight: 4
})
});


route = routeSample;
var latlngs = [];
for(const point of route)
{
latlngs.push([point.A,point.B]);
}
var routeSampleLine = L.polyline(latlngs, {
color: "purple",
weight: 4,
opacity: 0
});
routeSampleLine.addTo(map).on('mouseover', function(e) {
var layer = e.target;
layer.setStyle({
weight: 6
})}).on('mouseout',function(e) {
var layer = e.target;
layer.setStyle({
weight: 4
})
});



route = TCBroad;
var latlngs = [];
for(const point of route)
{
latlngs.push([point.A,point.B]);
}
var TCBroadLine = L.polyline(latlngs, {
color: "purple",
weight: 4,
opacity: 0
});
TCBroadLine.addTo(map).on('mouseover', function(e) {
var layer = e.target;
layer.setStyle({
weight: 6
})}).on('mouseout',function(e) {
var layer = e.target;
layer.setStyle({
weight: 4
})
});




route = TCAm;
var latlngs = [];
for(const point of route)
{
latlngs.push([point.A,point.B]);
}
var TCAmLine = L.polyline(latlngs, {
color: "purple",
weight: 4,
opacity: 0
});
TCAmLine.addTo(map).on('mouseover', function(e) {
var layer = e.target;
layer.setStyle({
weight: 6
})}).on('mouseout',function(e) {
var layer = e.target;
layer.setStyle({
weight: 4
})
});


if(paths=="Barnard to SIPA via 120th")
{
worserouteLine.setStyle({
opacity: 1
});
}
else if(paths=="Barnard to SIPA via College Walk")
{
routeSampleLine.setStyle({
opacity: 1
});
}
else if(paths=="Butler to TC via Broadway")
{
TCBroadLine.setStyle({
opacity: 1
});
}
else if(paths== "Butler to TC via Amsterdam")
{
TCAmLine.setStyle({
opacity: 1
});
}





var legend = L.control({position: 'topleft'});


legend.onAdd = function (map) {
var div = L.DomUtil.create('div', 'info legend');

// set white background
div.style.backgroundColor = 'white';
div.style.padding = '10px';

div.innerHTML += '<div style="font-weight: bold; font-size: 16px; text-align: center; ">Legend</div>';

// Add the image and label to the legend
div.innerHTML +=
'<img src="https://cdn0.iconfinder.com/data/icons/map-locations-and-tourism/512/47-512.png" style="width: 20px; height: 20px;"> Call Booth'+ '<br>';

// Add NYPD crime data
div.innerHTML +=
'<div style="display: flex; align-items: center; margin-bottom: 5px;">' +
'<div style="width: 8px; height: 8px; border: 1.5px solid black; margin-right: 9px; margin-left: 5px;"></div>' +
'<span>NYPD Crime Incident</span>' +
'</div>';

// Add Clery Crime Data
div.innerHTML +=
'<div style="display: flex; align-items: center; margin-bottom: 3px;">' +
'<svg width="25" height="20" style="margin-right: 5px; margin-left: -5px;">' + // Adjusted left margin
'<circle cx="15" cy="5.5" r="5" style="fill: white; stroke: black; stroke-width: 1; clip-path: url(#circleClip);"></circle>' +
'<polygon points="10,6 15,15 20,6" style="fill: white; stroke: black;"></polygon>' +
'<line x1="10" y1="6" x2="20" y2="6" style="stroke: white; stroke-width: 1;"></line>' +
'</svg>' +
'<span style="margin-top: 2px;"> Clery Crime Incidents</span>' +
'</div>';

// Add route to map
div.innerHTML +=
'<div style="display: flex; align-items: center; margin-bottom: 1.7px;">' +
'<svg width="25" height="10" style="margin-right: 5px; margin-left: -5px;">' +
'<line x1="5" y1=4 x2="20" y2="4" style="stroke: purple; stroke-width: 4;"></line>' +
'</svg>' +
'<span style="margin-top: -2px;"> Route</span>' +
'</div>';
// Add color codes
for (var i = 1; i < (crimes.length+1); i++) {
//div.innerHTML +=
//'<i style="background:' + adjustedColorList[i-1] + '"></i> ' + crimes[i-1] + '<br>';
div.innerHTML += '<div style="display: inline-block; width: 15px; height: 15px; border-radius: 50%; background-color: ' + adjustedColorList[i-1] + '; margin-right: 5px;"></div>' + crimes[i-1] + '<br>';
}


return div;
};

legend.addTo(map);



return container;
}

Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
workbook = FileAttachment("RouteSample.xlsx").xlsx()
Insert cell
routeSample = workbook.sheet(0, {
headers: false,
// range: "A1:J10"
})
Insert cell
WorseRoute.csv
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
TCBroad = FileAttachment("routeTCBroad@2.csv").csv()
Insert cell
TCAm = FileAttachment("routeTCAm@2.csv").csv()
Insert cell
CleryNewCats.csv
Type SQL, then Shift-Enter. Ctrl-space for more options.

Insert cell
Insert cell
Insert cell
Insert cell
import {Legend, Swatches} from "@d3/color-legend"
Insert cell
groupedCrimes = crimes.map(function(crime) {
if (crime === "Attempted Robbery") {
return "Robbery";
} else if (crime === "Motorcycle Theft") {
return "Motor Vehicle Theft";
} else if (crime === "Burglary Pattern") {
return "Burglary";
} else {
return crime;
}
});
Insert cell
adjustedColorList = colorList.filter((color, index) => index !== 1 && index !== 11);
Insert cell
colorList = ["#4e79a7","#f28e2c","#e15759","#76b7b2","#59a14f","#edc949","#af7aa1","#ff9da7","#9c755f","#bab0ab", "#d16f9b", "#6f6dc7"];
Insert cell
colorScheme = d3.scaleOrdinal(crimes, adjustedColorList);
Insert cell
Insert cell
columbia_university = FileAttachment("Columbia_University.geojson").json()
Insert cell
Insert cell
Insert cell
smallCrimeData = FileAttachment("ManhattanCrimesSmall.csv").csv()
Insert cell
cityCrimeData = FileAttachment("ManhattanCrimes.csv").csv()
Insert cell
// snake code
L.Polyline.include({

// Hi-res timestamp indicating when the last calculations for vertices and
// distance took place.
_snakingTimestamp: 0,

// How many rings and vertices we've already visited
// Yeah, yeah, "rings" semantically only apply to polygons, but L.Polyline
// internally uses that nomenclature.
_snakingRings: 0,
_snakingVertices: 0,

// Distance to draw (in screen pixels) since the last vertex
_snakingDistance: 0,

// Flag
_snaking: false,


/// TODO: accept a 'map' parameter, fall back to addTo() in case
/// performance.now is not available.
snakeIn: function(){

if (this._snaking) { return; }

if ( !('performance' in window) ||
!('now' in window.performance) ||
!this._map) {
return;
}

this._snaking = true;
this._snakingTime = performance.now();
this._snakingVertices = this._snakingRings = this._snakingDistance = 0;

if (!this._snakeLatLngs) {
this._snakeLatLngs = L.LineUtil.isFlat(this._latlngs) ?
[ this._latlngs ] :
this._latlngs ;
}

// Init with just the first (0th) vertex in a new ring
// Twice because the first thing that this._snake is is chop the head.
this._latlngs = [[ this._snakeLatLngs[0][0], this._snakeLatLngs[0][0] ]];

this._update();
this._snake();
this.fire('snakestart');
return this;
},


_snake: function(){

var now = performance.now();
var diff = now - this._snakingTime; // In milliseconds
var forward = diff * this.options.snakingSpeed / 1000; // In pixels
this._snakingTime = now;

// Chop the head from the previous frame
this._latlngs[ this._snakingRings ].pop();

return this._snakeForward(forward);
},

_snakeForward: function(forward) {

// If polyline has been removed from the map stop _snakeForward
if (!this._map) return;
// Calculate distance from current vertex to next vertex
var currPoint = this._map.latLngToContainerPoint(
this._snakeLatLngs[ this._snakingRings ][ this._snakingVertices ]);
var nextPoint = this._map.latLngToContainerPoint(
this._snakeLatLngs[ this._snakingRings ][ this._snakingVertices + 1 ]);

var distance = currPoint.distanceTo(nextPoint);

// console.log('Distance to next point:', distance, '; Now at: ', this._snakingDistance, '; Must travel forward:', forward);
// console.log('Vertices: ', this._latlngs);

if (this._snakingDistance + forward > distance) {
// Jump to next vertex
this._snakingVertices++;
this._latlngs[ this._snakingRings ].push( this._snakeLatLngs[ this._snakingRings ][ this._snakingVertices ] );

if (this._snakingVertices >= this._snakeLatLngs[ this._snakingRings ].length - 1 ) {
if (this._snakingRings >= this._snakeLatLngs.length - 1 ) {
return this._snakeEnd();
} else {
this._snakingVertices = 0;
this._snakingRings++;
this._latlngs[ this._snakingRings ] = [
this._snakeLatLngs[ this._snakingRings ][ this._snakingVertices ]
];
}
}

this._snakingDistance -= distance;
return this._snakeForward(forward);
}

this._snakingDistance += forward;

var percent = this._snakingDistance / distance;

var headPoint = nextPoint.multiplyBy(percent).add(
currPoint.multiplyBy( 1 - percent )
);

// Put a new head in place.
var headLatLng = this._map.containerPointToLatLng(headPoint);
this._latlngs[ this._snakingRings ].push(headLatLng);

this.setLatLngs(this._latlngs);
this.fire('snake');
L.Util.requestAnimFrame(this._snake, this);
},

_snakeEnd: function() {

this.setLatLngs(this._snakeLatLngs);
this._snaking = false;
this.fire('snakeend');

}

});


L.Polyline.mergeOptions({
snakingSpeed: 200 // In pixels/sec
});




L.LayerGroup.include({

_snakingLayers: [],
_snakingLayersDone: 0,

snakeIn: function() {

if ( !('performance' in window) ||
!('now' in window.performance) ||
!this._map ||
this._snaking) {
return;
}


this._snaking = true;
this._snakingLayers = [];
this._snakingLayersDone = 0;
var keys = Object.keys(this._layers);
for (var i in keys) {
var key = keys[i];
this._snakingLayers.push(this._layers[key]);
}
this.clearLayers();

this.fire('snakestart');
return this._snakeNext();
},


_snakeNext: function() {


if (this._snakingLayersDone >= this._snakingLayers.length) {
this.fire('snakeend');
this._snaking = false;
return;
}

var currentLayer = this._snakingLayers[this._snakingLayersDone];

this._snakingLayersDone++;

this.addLayer(currentLayer);
if ('snakeIn' in currentLayer) {
currentLayer.once('snakeend', function(){
setTimeout(this._snakeNext.bind(this), this.options.snakingPause);
}, this);
currentLayer.snakeIn();
} else {
setTimeout(this._snakeNext.bind(this), this.options.snakingPause);
}


this.fire('snake');
return this;
}

});


L.LayerGroup.mergeOptions({
snakingPause: 200
});
Insert cell
campusCrimes = FileAttachment("ManhattanCrimesCampusRegion@1.csv").csv()
Insert cell
CrimeTypesCount - count.csv
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
count
X
Type of Crime
Y
NYC Data
Color
Type of Crime
Size
Facet X
Facet Y
Mark
bar
Type Chart, then Shift-Enter. Ctrl-space for more options.

Insert cell
viewof update_btn = Inputs.Button("update data values")
Insert cell
update_demo = {
var width = 500,
height = 400;
var dataset = count;
var xScale = d3.scaleBand()
.domain(d3.range(dataset.length))
.rangeRound([0, width])
.padding(0.05);
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset)])
.range([0, height]);
const svg = d3.select(DOM.svg(width, height));
svg.selectAll("rect")
.data(dataset)
.enter().append("rect")
.attr("x", (d, i) => xScale(i))
.attr("y", d => height - yScale(d))
.attr("width", xScale.bandwidth())
.attr("height", d => yScale(d))
.attr("fill", "steelblue");
// This function is called by the cell that waits for the button click event
svg.node().update = () => {
// Update all rects with newly generated random values
svg.selectAll("rect")
.data(dataset.map(x => Math.random()*25))
.transition() // this and next 2 lines: optional animation
.duration(800)
.ease(d3.easeLinear)
.attr("y", d => height - yScale(d))
.attr("height", d => yScale(d));
};

return svg.node();
}
Insert cell
Inputs = require("@observablehq/inputs@0.7.8/dist/inputs.umd.min.js")
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