Published
Edited
Jul 27, 2022
Insert cell
# NYC Firefighting Drone
Insert cell
d3 = require("d3@7", "d3-geo-scale-bar@1.2", "d3-tile@1")
Insert cell
scaleBar = d3.geoScaleBar()
.orient(d3.geoScaleTop)
.top(.93)
.left(.08)
//.label("Yards")
.units({units: "500 Feet", radius: 20902259.664}) // Will do the same as the previous two lines
//.units({units: "350 Yards", radius: 6967419.888})
//.distance(width <= 400 ? 1200 : 1000)
.distance(2000)
//.labelAnchor("middle")
//.tickSize(null)
//.tickValues(null)
.label(null)
.tickPadding(5) // How far the tick text labels are from the lines
.tickFormat((d, i, e) => i === e.length - 1 ? `${d} Feets` : d)
.tickValues([2000])
.tickSize(7)

Insert cell
chart = {
const width = 960,
height = 1000;
const svg = d3.create("svg")
.attr("viewBox", [50, 50, width-100, height-100]); // adjusting position of the view box
const squareCanvas = svg.append('rect')
.attr('fill', d3.rgb(46,46,46))
.attr('x', 0)
.attr('y', 0)
.attr('width', 960)
.attr('height', 950)

// Use Mercator projection
var projection = d3
.geoMercator()
.fitSize([width - 50, height - 50], bb_test);

var path1 = d3.geoPath().projection(projection);//path variable for each use of d3 geopath
var path2 = d3.geoPath().projection(projection);
var path3 = d3.geoPath().projection(projection);
var path4 = d3.geoPath().projection(projection);
var path5 = d3.geoPath().projection(projection);
var path6 = d3.geoPath().projection(projection);
var path7 = d3.geoPath().projection(projection);
var path8 = d3.geoPath().projection(projection);
var path9 = d3.geoPath().projection(projection);
var path10 = d3.geoPath().projection(projection);
var path11 = d3.geoPath().projection(projection);
var path12 = d3.geoPath().projection(projection);
var path13 = d3.geoPath().projection(projection);
var g = svg.append("g").attr("id", "paths");
//Container for the gradients
var defs = svg.append("defs");
//i'm not using the boundary lines, so I'm going to comment them out
/*
g.selectAll("path1") //d3 geopath
.data(boundary.features) //get data to define path
.enter() //there are more data than elements, this selects them
.append("path") //appends path to data
.attr('class','outlines')
.attr("d", path1) //The d attribute defines a path to be drawn, only applies to appended elements
.style("fill", "none")
.style('stroke-opacity','1')
.style("stroke-width", '.5')
.style("stroke", "rgb(0,0,0)")
*/
g.selectAll("path2") //d3 geopath
.data(subline.features) //get data to define path
.enter() //there are more data than elements, this selects them
.append("path") //appends path to data
.attr('class','outlines')
.attr("d", path2) //The d attribute defines a path to be drawn, only applies to appended elements
.style("fill", "none")
.style('stroke-opacity','1')
.style("stroke-width", '.2')
.style("stroke", "rgb(255,255,255)")

g.selectAll("path3") //d3 geopath
.data(street.features) //get data to define path
.enter() //there are more data than elements, this selects them
.append("path") //appends path to data
.attr('class','outlines')
.attr("d", path3) //The d attribute defines a path to be drawn, only applies to appended elements
.style("fill", "none")
.style('stroke-opacity','1')
.style("stroke-width", '.20')
.style("stroke", "rgb(255,255,255)")

g.selectAll("path3") //d3 geopath
.data(footprint.features) //get data to define path
.enter() //there are more data than elements, this selects them
.append("path") //appends path to data
.attr('class','outlines')
.attr("d", path3) //The d attribute defines a path to be drawn, only applies to appended elements
.style("fill", "rgb(255,255,255)")
.style("fill-opacity", "0")
.style('stroke-opacity','1')
.style("stroke-width", '.3')
.style("stroke", "rgb(255,255,255)")

g.selectAll("path4") //d3 geopath
.data(fireBattalion.features) //get data to define path
.enter() //there are more data than elements, this selects them
.append("path") //appends path to data
.attr('class','outlines')
.attr("d", path4) //The d attribute defines a path to be drawn, only applies to appended elements
.style("fill", "none")
.style('stroke-opacity','1')
.style("stroke-width", '2.5')
.style("stroke", "rgb(255,255,255)")

g.selectAll("path5") //d3 geopath
.data(park.features) //get data to define path
.enter() //there are more data than elements, this selects them
.append("path") //appends path to data
.attr('class','outlines')
.attr("d", path5) //The d attribute defines a path to be drawn, only applies to appended elements
.style("fill", "rgb(255,255,255)")
.style("fill-opacity", "0.5")
.style('stroke-opacity','1')
.style("stroke-width", '.8')
.style("stroke", "rgb(255,255,255)")

g.selectAll("path9") //d3 geopath
.data(backgroundsquare.features) //get data to define path
.enter() //there are more data than elements, this selects them
.append("path") //appends path to data
.attr('class','outlines')
.attr("d", path9) //The d attribute defines a path to be drawn, only applies to appended elements
.style("fill", "rgb(255,255,255)")
.style("fill-opacity", "0.7")
.style('stroke-opacity','1')
.style("stroke-width", '0')
.style("stroke", "rgb(255,255,255)")

g.selectAll("path13") //d3 geopath
.data(boundaryBox.features) //get data to define path
.enter() //there are more data than elements, this selects them
.append("path") //appends path to data
.attr('class','outlines')
.attr("d", path13) //The d attribute defines a path to be drawn, only applies to appended elements
.style("fill", "rgb(255,255,255)")
.style("fill-opacity", "0")
.style('stroke-opacity','1')
.style("stroke-width", '4')
.style("stroke", "rgb(255,255,255)")

svg
.append('text')
.attr('x','65')
.attr('y','85')
.attr("font-family", "Helvetica")
.attr('font-size','1.25em')
.style("font-weight","bold")
//.style("font-style","italic")
.style("fill",'rgb(255,255,255)')
.text('FINANCIAL DISTRICT')

svg.append('text')//use this for second and all subsequent text add
.attr("x", '790')
.attr("y", '833')
.attr('class','text')
.attr('font-family','helvetica')
.attr('font-size','0.6em')
.attr('font-weight','bold')
.style("fill", "rgb(0,0,0)")
.attr('text-anchor','start')
.text('DEPLOYMENT POINT')
svg.append('text')//use this for second and all subsequent text add
.attr("x", '790')
.attr("y", '871')
.attr('class','text')
.attr('font-family','helvetica')
.attr('font-size','0.6em')
.attr('font-weight','bold')
.style("fill", "rgb(0,0,0)")
.attr('text-anchor','start')
.text('NYCLINKS')

svg.append('text')//use this for second and all subsequent text add
.attr("x", '790')
.attr("y", '909')
.attr('class','text')
.attr('font-family','helvetica')
.attr('font-size','0.6em')
.attr('font-weight','bold')
.style("fill", "rgb(0,0,0)")
.attr('text-anchor','start')
.text('DRONE ROUTE')
g.selectAll("path10") //d3 geopath
.data(reference1.features) //get data to define path
.enter() //there are more data than elements, this selects them
.append("path") //appends path to data
.attr('class','marklines')
.attr("d", path10) //The d attribute defines a path to be drawn, only applies to appended elements
.style("fill", "rgb(178,249,251)")
.style("fill-opacity", "1")
.style('stroke-opacity','0.5')
.style("stroke-width", '0')
.style("stroke", "rgb(178,249,251)")
.on('mouseover',reference1Mouse)
.on('mouseout',reference1Mouseout)

function reference1Mouse(event,d){
d3.select(this).style("stroke-width","8");
d3.select(this).style("cursor", "pointer");
svg.append('text')//use this for second and all subsequent text add
.attr("x", '790')
.attr("y", '833')
.attr('class','referencetext')
.attr('font-family','helvetica')
.attr('font-size','0.6em')
.attr('font-weight','bold')
.style("fill", "rgb(178,249,251)")
.attr('text-anchor','start')
.text('DEPLOYMENT POINT')
}
function reference1Mouseout(event,d){
d3.select(this).style("stroke-width","0");
d3.select(this).style("cursor", "default");
d3.selectAll('text.referencetext').remove()
}


g.selectAll("path11") //d3 geopath
.data(reference2.features) //get data to define path
.enter() //there are more data than elements, this selects them
.append("path") //appends path to data
.attr('class','marklines')
.attr("d", path11) //The d attribute defines a path to be drawn, only applies to appended elements
.style("fill", "rgb(247,4,110)")
.style("fill-opacity", "1")
.style('stroke-opacity','0.5')
.style("stroke-width", '0')
.style("stroke", "rgb(247,4,110)")
.on('mouseover',reference2Mouse)
.on('mouseout',reference2Mouseout)

function reference2Mouse(event,d){
d3.select(this).style("stroke-width","8");
d3.select(this).style("cursor", "pointer");
svg.append('text')//use this for second and all subsequent text add
.attr("x", '790')
.attr("y", '871')
.attr('class','referencetext')
.attr('font-family','helvetica')
.attr('font-size','0.6em')
.attr('font-weight','bold')
.style("fill", "rgb(178,249,251)")
.attr('text-anchor','start')
.text('NYCLINKS')
}
function reference2Mouseout(event,d){
d3.select(this).style("stroke-width","0");
d3.select(this).style("cursor", "default");
d3.selectAll('text.referencetext').remove()
}


g.selectAll("path12") //d3 geopath
.data(referenceline.features) //get data to define path
.enter() //there are more data than elements, this selects them
.append("path") //appends path to data
.attr('class','marklines')
.attr("d", path12) //The d attribute defines a path to be drawn, only applies to appended elements
.style("fill", "rgb(178,249,251)")
.style("fill-opacity", "0")
.style('stroke-opacity','1')
.style("stroke-width", '3')
.style("stroke", "rgb(178,249,251)")
.on('mouseover',referencelineMouse)
.on('mouseout',referencelineMouseout)

function referencelineMouse(event,d){
d3.select(this).style("stroke-width","8");
d3.select(this).style("cursor", "pointer");
svg.append('text')//use this for second and all subsequent text add
.attr("x", '790')
.attr("y", '909')
.attr('class','referencetext')
.attr('font-family','helvetica')
.attr('font-size','0.6em')
.attr('font-weight','bold')
.style("fill", "rgb(178,249,251)")
.attr('text-anchor','start')
.text('DRONE ROUTE')
}
function referencelineMouseout(event,d){
d3.select(this).style("stroke-width","3");
d3.select(this).style("cursor", "default");
d3.selectAll('text.referencetext').remove()
}


g.selectAll("path7") //interactive circle
.data (interactiveCircle2.features) //get data to define path
.enter() //there are more data than elements, this selects them
.append("path") //appends path to data
.attr('class','outlines')
.attr("d", path8) //The d attribute defines a path to be drawn, only applies to appended elements
.style("fill", "rgb(247,4,110)")
.style("fill-opacity", "0")
.style('stroke-opacity','1')
.style("stroke-width", '0')
.style("stroke", "rgb(247,4,110)")
.on('mouseover',circleMouse)
.on('mouseout',circleMouseout)
g.selectAll("path7") //interactive circle
.data(interactiveCircle1.features) //get data to define path
.enter() //there are more data than elements, this selects them
.append("path") //appends path to data
.attr('class','outlines')
.attr("d", path7) //The d attribute defines a path to be drawn, only applies to appended elements
.style("fill", "rgb(255,255,255)")
.style("fill-opacity", "0")
.style('stroke-opacity','1')
.style("stroke-width", '0')
.style("stroke", "rgb(178,249,251)")
.on('mouseover',circleMouse)
.on('mouseout',circleMouseout)
function circleMouse(event,d){
d3.select(this).style("stroke-width","2");
}
function circleMouseout(event,d){
d3.select(this).style("stroke-width","0");
}

var c = svg.selectAll("circle") //drone point
.data(firefightingdrone.features)
.enter() //there are more data than elements, this selects them
.append("circle") //appends path to data
.attr("cx", function(d) {return path1.centroid(d)[0]})
.attr("cy", function(d) {return path1.centroid(d)[1]})
.attr('r',5)
.attr('fill', "rgb(178,249,251)")
.style('full-opacity','1')
.style('stroke-opacity','0.5')
.style("stroke-width", '0')
.style("stroke", "rgb(178,249,251)")
.on('mouseover',DroneMouse)
.on('mouseout',DroneMouseout)

function DroneMouse(event,d){
d3.select(this).style("stroke-width","12");
d3.select(this).style("cursor", "wait");
}
function DroneMouseout(event,d){
d3.select(this).style("stroke-width","0");
d3.select(this).style("cursor", "default");
}


//NYCLINK TEXT AND LINE

c.enter().append("circle") //d3 geopath
.data(NycLink)
.attr("class", "points")
.enter() //there are more data than elements, this selects them
.append("circle") //appends path to data
.attr("cx", function(d) {return projection([d.Longtitude,d.Latitude])[0]})
.attr("cy", function(d) {return projection([d.Longtitude,d.Latitude])[1]})
.attr('r',3)
.attr('fill','rgb(247,4,110)')
.style('fill-opacity','1')
.style("stroke-width","0")
.style('stroke-opacity','0.5')
.style("stroke","rgb(247,4,110)")
.on('mouseover',NYCMouse)
.on('mouseout',NYCMouseout)

function NYCMouse(event,d){
d3.select(this).style("stroke-width","12");

d3.select(this).style("cursor", "pointer");

svg.append('text')//use this for second and all subsequent text add
.attr("x", '270')
.attr("y", '700')
.attr('class','NYCtext')
.attr('font-family','helvetica')
.attr('font-size','0.8em')
.attr('font-weight','600')
.style("fill", "rgb(255,255,255)")
.attr('text-anchor','end')
.text(d.RelativeLocation)

svg
.append('line')
.attr('x1',projection([d.Longtitude,d.Latitude])[0])
.attr('y1',projection([d.Longtitude,d.Latitude])[1])
.attr('x2',740)
.attr('y2',projection([d.Longtitude,d.Latitude])[1])
.attr('class','NYCLine')
.attr('stroke',"rgb(255,255,255)")
.attr('stroke-width',"1.5")

svg
.append('line')
.attr('x1',projection([d.Longtitude,d.Latitude])[0])
.attr('y1',projection([d.Longtitude,d.Latitude])[1])
.attr('x2',projection([d.Longtitude,d.Latitude])[0])
.attr('y2',700)
.attr('class','NYCLine')
.attr('stroke',"rgb(255,255,255)")
.attr('stroke-width',"1.5")

svg
.append('line')
.attr('x1',projection([d.Longtitude,d.Latitude])[0])
.attr('y1',700)
.attr('x2',270)
.attr('y2',700)
.attr('class','NYCLine')
.attr('stroke',"rgb(255,255,255)")
.attr('stroke-width',"1.5")

svg.append('text')//use this for second and all subsequent text add
.attr("x", '740')
.attr("y", projection([d.Longtitude,d.Latitude])[1]+4)
.attr('class','NYCtext')
.attr('font-family','helvetica')
.attr('font-size','0.8em')
.attr('font-weight','600')
.style("fill", "rgb(255,255,255)")
.attr('text-anchor','start')
.text(d.Detail_Location)
}
function NYCMouseout(event,d){
d3.select(this).style("stroke-width","0");
d3.select(this).style("cursor", "default");
d3.selectAll('line.NYCLine').remove()
d3.selectAll('text.NYCtext').remove()
}


svg
.append('text')
.attr('class','station')
.attr('x','160')
.attr('y','675')
.attr("font-family", "Helvetica")
.attr('font-size','1em')
.attr('text-anchor','middle')
.attr("font-weight","bold")
.attr('fill','rgb(255,255,255)')
.text('RELATIVE LOCATION')

svg
.append('text')
.attr('class','station')
.attr('x','809')
.attr('y','557')
.attr("font-family", "Helvetica")
.attr('font-size','1em')
.attr('text-anchor','middle')
.attr("font-weight","bold")
.attr('fill','rgb(255,255,255)')
.text('ADDRESS')

svg
.append('line')
.attr('class','sbLn')
.attr('x1',280)
.attr('y1',683)
.attr('x2',55)
.attr('y2',683)
.attr('stroke-width','1.8')
.attr('stroke-opacity','1')
.style('stroke','rgb(255,255,255)')

svg.append('rect')
.attr('fill', d3.rgb(255,255,255))
.attr('x', 0)
.attr('y', 880)
.attr('width', 280)
.attr('height', 90)
.style('opacity','0.7')

g.selectAll("path6") //d3 geopath
.data(droneRoutes.features) //get data to define path
.enter() //there are more data than elements, this selects them
.append("path") //appends path to data
.attr('class','outlines')
.attr("d", path6) //The d attribute defines a path to be drawn, only applies to appended elements
//.style("fill", "rgb(178,249,251)")
.style('fill','none')
.style("fill-opacity", "0")
.style('stroke-opacity','1')
.style("stroke-width", '2')
.style("stroke", "rgb(178,249,251)")
.on('mouseover',LineMouse)
.on('mouseout',LineMouseout)
function LineMouse(event,d){
d3.select(this).style("stroke-width","3");
}
function LineMouseout(event,d){
d3.select(this).style("stroke-width","2");
}


scaleBar
.projection(projection)
.size([width, height]);
svg.append("g")
.call(scaleBar);


return svg.node();
}
Insert cell
boundaryBox = FileAttachment("boundary box@1.geojson").json()
Insert cell
referenceline = FileAttachment("referenceline.geojson").json()
Insert cell
reference1 = FileAttachment("reference1.geojson").json()
Insert cell
interactiveCircle2 = FileAttachment("interactive circle 2.geojson").json()
Insert cell
backgroundsquare = FileAttachment("backgroundsquare.geojson").json()
Insert cell
interactiveCircle1 = FileAttachment("interactive circle 1@2.geojson").json()
Insert cell
subSts = FileAttachment("sub_stops.geojson").json()
Insert cell
bbox = FileAttachment("site_boundary.geojson").json()
Insert cell
droneRoutes = FileAttachment("drone routes@2.geojson").json()
Insert cell
subLns = FileAttachment("subway_lines.geojson").json()
Insert cell
boundary = FileAttachment("nyc_boundary.geojson").json()
Insert cell
bb_test = FileAttachment("box.geojson").json()
Insert cell
subline = FileAttachment("subline.geojson").json()
Insert cell
station = FileAttachment("station.geojson").json()
Insert cell
park = FileAttachment("parksimplified.geojson").json()
Insert cell
footprint = FileAttachment("footprint.geojson").json()
Insert cell
linknyc = FileAttachment("linknyc@2.geojson").json()
Insert cell
fireBattalion = FileAttachment("fire battalion@4.geojson").json()
Insert cell
street = FileAttachment("street.geojson").json()
Insert cell
firefightingdrone = FileAttachment("firefightingdrone.geojson").json()
Insert cell
Insert cell
Insert cell
import { wrap_text, wrap_text_nchar } from "@ben-tanen/svg-text-and-tspan-word-wrapping"
Insert cell
reference2 = FileAttachment("reference2.geojson").json()
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