Published
Edited
Jul 28, 2022
Insert cell
# Green Bus Shelter Project
Insert cell
d3 = require("d3@6","d3-geo-scale-bar@1.2","d3-tile@1")
Insert cell
scaleBar = d3.geoScaleBar()
.left(0.78)
.top(.905)
.units({units: "150 yards", radius: 6967419.888}) // The units will be miles instead of kilometers
.distance(1200) // The distance of the scale bar
.label("1200 meters") // The label on top of the scale bar
.labelAnchor("middle") // The position of the label (you can also pass "start" and "end")
Insert cell
chart = {
const width = 960, //size of the map on screen
height = 960;
const svg = d3.create("svg")
.attr("viewBox", [50, 50, width-100, height-100])
.style("background", "#001B2E"); //adjusting position of the view box


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

var path1 = d3.geoPath().projection(projection); //path variabble 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 g = svg.append("g").attr("id", "paths");

g.selectAll("path4") //d3 geopath
.data(borough.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(30,30,30)")
.style("fill-opacity", "0.8")
.style('stroke-opacity','0.8')
.style("stroke-width", '0.8')
.style("stroke", "#e6e6e6")

g.selectAll("path2") //d3 geopath
.data(stcenterlineTrimed.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", '0.8')
.style("stroke", "#383838")

/*
g.selectAll("path5") //d3 geopath
.data(sbline.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", '1.2')
.style("stroke", "#0075C4")
*/
g.selectAll("path3") //d3 geopath
.data(parks.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", "#a1caae")
.style("fill-opacity", "1")
.style('stroke-opacity','0')
.style("stroke-width", '.5')
.style("stroke", "#729B79")

.on('mouseover', addFill)
.on('mouseleave', removeFill)

/*
g.selectAll("path6") //d3 geopath
.data(siteBoundaries.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(255,255,255)")
.style("fill-opacity", "0.6")
.style('stroke-opacity','1')
.style("stroke-width", '2')
.style("stroke", "rgb(128,0,0)")
.attr('stroke-dasharray','8 8')
*/
g.selectAll("path7") //d3 geopath
//svg
//.selectAll('path')
.data(connections.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-opacity','1')
.style("stroke-width", "0.8")
.style("stroke", "#E76B74")
.style('stroke-opacity','.5')
.on('mouseover', addroute)
.on('mouseleave',removeroute)
/*
g.selectAll("path8") //d3 geopath
//svg
//.selectAll('path')
.data(fountainsConnections.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-opacity','1')
.style('stroke-opacity','.7')
.style("stroke-width", ".5")
.style("stroke", "#0075C4")
.on('mouseover', addroutefountain)
.on('mouseleave',removeroutefountain)
*/
g.selectAll("path9") //d3 geopath
//svg
//.selectAll('path')
.data(dronelinebus.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-opacity','0')
.style("stroke-width", "1.7")
.style("stroke", "#E9B872")
//attr('stroke-dasharray','5 7')
.on('mouseover', addroutedrone)
.on('mouseleave',removeroutedrone)

g.selectAll("path10") //d3 geopath
//svg
//.selectAll('path')
.data(dronelinef.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-opacity','0')
.style("stroke-width", "1.7")
.style("stroke", "#007DD0")
//.attr('stroke-dasharray','5 7')
.on('mouseover', addroutedronef)
.on('mouseleave',removeroutedronef)
/*
g.selectAll("path11") //d3 geopath
//svg
//.selectAll('path')
.data(oos.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-opacity','0')
.style("stroke-width", "1")
.style("stroke", "#E76B74")
.on('mouseover', addFillOOS)
.on('mouseleave',removeFillOOS)

g.selectAll("path12") //d3 geopath
//svg
//.selectAll('path')
.data(oob.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-opacity','0')
.style("stroke-width", "1")
.style("stroke", "#E76B74")
.on('mouseover', addFillOOB)
.on('mouseleave',removeFillOOB)

*/
var c = svg.selectAll("circle") //d3 geopath
.data(bus.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',1.8)
.attr('fill','rgb(247,86,124)')
.style('fill-opacity','0.8')
.style("stroke", "white")
.style("stroke-width", ".1")
.on('mouseover', addST)
.on('mouseleave',removeST)
/*
c.enter().append('circle')
.data(drinking.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',3.5)
.attr('fill','rgb(32,129,195)')
.style('fill-opacity','1')
*/
c.enter().append('circle') //d3 geopath
.data(drinking.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',1.8)
.attr('fill','#007DD0')
.style('fill-opacity','0.8')
.style("stroke", "white")
.style("stroke-width", ".1")
.on('mouseover', addBox)
.on('mouseleave',removeBox)
/*
var t = svg.selectAll("text ") //d3 geopath
.data(bus.features)
.enter() //there are more data than elements, this selects them
.append("text") //appends path to data
.attr("x", function(d) {return path1.centroid(d)[0]+6})
.attr("y", function(d) {return path1.centroid(d)[1]+3.5})
.attr('font-family','helvetica')
.attr('font-size','.4em')
.attr('font-weight','200')
.attr('fill','rgb(0,0,0)')
.style('fill-opacity','1')
.text(function(d) {return d.properties.at_between})

t.enter().append('text')
.data(drinking.features)
.enter() //there are more data than elements, this selects them
.append("text") //appends path to data
.attr("x", function(d) {return path1.centroid(d)[0]+6})
.attr("y", function(d) {return path1.centroid(d)[1]+3.5})
.attr('font-family','helvetica')
.attr('font-size','.4em')
.attr('font-weight','200')
.attr('fill','rgb(0,0,0)')
.style('fill-opacity','1')
.text(function(d) {return d.properties.position})
*/

svg
.append('rect')
.attr('fill', "rgb(255,255,255)")
.attr('x', 0)
.attr('y', 0)
.attr('width', 120)
.attr('height', 960)
svg
.append('rect')
.attr('fill', "rgb(255,255,255)")
.attr('x', 840)
.attr('y', 0)
.attr('width', 120)
.attr('height', 960)

svg
.append('rect')
.attr('fill', "rgb(255,255,255)")
.attr('x', 0)
.attr('y', 840)
.attr('width', 960)
.attr('height', 120)

svg
.append('rect')
.attr('fill', "rgb(255,255,255)")
.attr('x', 0)
.attr('y', 0)
.attr('width', 960)
.attr('height', 120)

svg
.append('circle')
.attr('fill', "rgb(247,86,124)")
.attr('cx', 135)
.attr('cy', 135)
.attr('r',3.5)

svg
.append("text")
.attr('x',146)
.attr('y',137)
.attr('font-family','helvetica')
.attr('font-size','0.45em')
//.attr('font-weight','Bold')
.attr('fill','white')
.text('Bus Shelter')
svg
.append('circle')
.attr('fill', "#007DD0")
.attr('cx', 135)
.attr('cy', 150)
.attr('r',3.5)

svg
.append("text")
.attr('x',146)
.attr('y',152)
.attr('font-family','helvetica')
.attr('font-size','0.45em')
//.attr('font-weight','Bold')
.attr('fill','white')
.text('Drinking Fountain')

svg
.append('line')
.attr('x1',128)
.attr('y1',165)
.attr('x2',143)
.attr('y2',165)
.style("stroke-width", '0.5')
.style("stroke", "#E76B74")

svg
.append("text")
.attr('x',146)
.attr('y',167)
.attr('font-family','helvetica')
.attr('font-size','0.45em')
//.attr('font-weight','Bold')
.attr('fill','white')
.text('Route to Closest Collection Point')
svg
.append('line')
.attr('x1',128)
.attr('y1',180)
.attr('x2',143)
.attr('y2',180)
.style("stroke-width", '2')
.style("stroke", "#E9B872")

svg
.append("text")
.attr('x',146)
.attr('y',182)
.attr('font-family','helvetica')
.attr('font-size','0.45em')
//.attr('font-weight','Bold')
.attr('fill','white')
.text('Delivering Route of Seeds')

svg
.append('line')
.attr('x1',128)
.attr('y1',195)
.attr('x2',143)
.attr('y2',195)
.style("stroke-width", '2')
.style("stroke", "#007DD0")

svg
.append("text")
.attr('x',146)
.attr('y',197)
.attr('font-family','helvetica')
.attr('font-size','0.45em')
//.attr('font-weight','Bold')
.attr('fill','white')
.text('Irrigation Route of Water')




svg
.append("text")
.attr('class','title')
.attr('x',360)
.attr('y','100')
.attr('font-family','helvetica')
.attr('font-size','1.5em')
.attr('font-weight','Bold')
.attr('fill','rgb(0,0,0)')
.text('Green Bus Shelter Project')

var tooltip = svg.append("g");
function addBox(event, d) {
var centroid = path1.centroid(d);
tooltip.call(
callout,
`${d.properties.signname}
${d.properties.fountain_t}
${d.properties.position}`
);
tooltip.attr("transform", "translate(" + centroid + ")");
d3.select(this).style("stroke-width", '2.5')
d3.select(this).style("stroke", 'white')
}

function removeBox(event, d) {
tooltip.call(callout,null)
d3.select(this).style("stroke-width", '.1')
d3.select(this).style("stroke", 'white')
}
function addST(event, d) {
var centroid = path2.centroid(d);
tooltip.call(
callout,
`${d.properties.at_between}
${d.properties.location}`
);
tooltip.attr("transform", "translate(" + centroid + ")");
d3.select(this).style("stroke-width", '2.5')
d3.select(this).style("stroke", 'white')
}

function removeST(event, d) {
tooltip.call(callout,null)
d3.select(this).style("stroke-width", '.1')
d3.select(this).style("stroke", 'white')
}
function addroute(event,d) {
d3.select(this).style("stroke-width", "2.5")
d3.select(this).style("stroke", "#FF4D4D")
d3.select(this).style('fill-opacity','1')
d3.select(this).style('stroke-opacity','1')

}

function removeroute(event,d) {
d3.select(this).style("stroke-width", "0.9")
d3.select(this).style("stroke", "#E76B74")
d3.select(this).style('fill-opacity','1')
d3.select(this).style('stroke-opacity','.5')
}

function addroutefountain(event,d) {
d3.select(this).style("stroke-width", "2.5")
d3.select(this).style("stroke", "#FF4D4D")
d3.select(this).style('fill-opacity','.8')
svg
.append('text')
.attr('x','75')
.attr('y','110')
.attr('class','review')
.attr("font-family", "Helvetica")
.attr('font-size','.6em')
.style("font-weight","bold")
//.style("font-style","italic")
.style("fill",'rgb(255,211,0)')
.text('Green Bus Shelter Project')
}
function removeroutefountain(event,d) {
d3.select(this).style("stroke-width", "0.7")
d3.select(this).style("stroke", "#CC9999")
d3.select(this).style('fill-opacity','1')
d3.selectAll('text.review').remove()
}

function addroutedrone(event,d) {
d3.select(this).style("stroke-width", "2.8")
d3.select(this).style("stroke", "#f8d6a4")
d3.select(this).style('fill-opacity','0')
d3.select(this).attr('stroke-dasharray','0 0')
svg
.append('text')
.attr('x','120')
.attr('y','880')
.attr('class','review')
.attr("font-family", "Helvetica")
.attr('font-size','1.1em')
.style("font-weight","bold")
//.style("font-style","italic")
.style("fill",'black')
.text(d.properties.Text)


}
function removeroutedrone(event,d) {
d3.select(this).style("stroke-width", "1.7")
d3.select(this).style("stroke", "#E9B872")
d3.select(this).style('fill-opacity','0')
//d3.select(this).attr('stroke-dasharray','6 6')
d3.selectAll('text.review').remove()
}

function addroutedronef(event,d) {
d3.select(this).style("stroke-width", "2.8")
d3.select(this).style("stroke", "#007DD0")
d3.select(this).attr('stroke-dasharray','0 0')
d3.select(this).style('fill-opacity','0')

svg
.append('text')
.attr('x','120')
.attr('y','880')
.attr('class','review')
.attr("font-family", "Helvetica")
.attr('font-size','1.1em')
.style("font-weight","bold")
//.style("font-style","italic")
.style("fill",'black')
.text(d.properties.Text)

}
function removeroutedronef(event,d) {
d3.select(this).style("stroke-width", "1.7")
d3.select(this).style("stroke", "#007DD0")
d3.select(this).style('fill-opacity','0')
//d3.select(this).attr('stroke-dasharray','6 6')
d3.selectAll('text.review').remove()
}


function addFill(event,d) {
d3.select(this).style("fill", "#4EFFC5")
d3.select(this).style("stroke-width", "2")
d3.select(this).style("stroke", "white")
d3.select(this).style('fill-opacity','.8')

svg
.append('text')
.attr("class","labels")
.attr('x','125')
.attr('y','870')
.attr('font-size','.85em')
.attr('fill','black')
.style("font-weight","bold")
.attr("font-family", "Helvetica")
.text(d.properties.park_name);
svg
.append('line')
.attr('x1',path1.centroid(d)[0])
.attr('y1',path1.centroid(d)[1])
.attr('x2',path1.centroid(d)[0])
.attr('y2',850)
.attr('class','pizzaLine')
.style("stroke-width", '.5')
.style("stroke", "white")
.attr('stroke-dasharray','7 7')

svg
.append('line')
.attr('x1',path1.centroid(d)[0])
.attr('y1',840)
.attr('x2',path1.centroid(d)[0])
.attr('y2',851)
.attr('class','pizzaLine')
.style("stroke-width", '.5')
.style("stroke", "black")
.attr('stroke-dasharray','7 7')

svg
.append('line')
.attr('x1',path1.centroid(d)[0])
.attr('y1',851)
.attr('x2',120)
.attr('y2',851)
.attr('class','pizzaLine')
.style("stroke-width", '.5')
.style("stroke", "black")
.attr('stroke-dasharray','7 7')

svg
.append('line')
.attr('x1',120)
.attr('y1',851)
.attr('x2',120)
.attr('y2',890)
.attr('class','pizzaLine')
.style("stroke-width", '.5')
.style("stroke", "black")
//.attr('stroke-dasharray','7 7')

svg
.append('line')
.attr('x1',120)
.attr('y1',890)
.attr('x2',250)
.attr('y2',890)
.attr('class','pizzaLine')
.style("stroke-width", '.5')
.style("stroke", "black")
//.attr('stroke-dasharray','7 7')


svg
.append('text')
.attr("class","labels")
.attr('x','125')
.attr('y','880')
.attr('font-size','.5em')
.attr('fill','black')
.attr("font-family", "Helvetica")

.text(d.properties.landuse);
}

function removeFill(event,d) {
d3.selectAll("text.labels").remove()
d3.select(this).style("stroke-width", "0")
d3.select(this).style("fill", "#a1caae")
d3.select(this).style('fill-opacity','1')
d3.selectAll('line.pizzaLine').remove()
}

function addFillOOS(event,d) {
d3.select(this).style("fill", "#4EFFC5")
d3.select(this).style("stroke-width", "1.5")
d3.select(this).style("stroke", "white")
d3.select(this).style('fill-opacity','.8')

}

function removeFillOOS(event,d) {
d3.selectAll("text.labels").remove()
d3.select(this).style("stroke-width", "0")
d3.select(this).style("fill", "#86CBC0")
d3.select(this).style('fill-opacity','.8')
}
function addFillOOB(event,d) {
d3.select(this).style("fill", "#4EFFC5")
d3.select(this).style("stroke-width", "1.5")
d3.select(this).style("stroke", "white")
d3.select(this).style('fill-opacity','.8')

}

function removeFillOOB(event,d) {
d3.selectAll("text.labels").remove()
d3.select(this).style("stroke-width", "0")
d3.select(this).style("fill", "#86CBC0")
d3.select(this).style('fill-opacity','1')
}

var t = svg.selectAll("text") //d3 geopath
.data(busroutestop1.features)
.enter() //there are more data than elements, this selects them
.append("text") //appends path to data
.attr("x", function(d) {return path1.centroid(d)[0]})
.attr("y", function(d) {return path1.centroid(d)[1]-8})
.attr('font-family','helvetica')
.style('font-size','.4em')
.attr('text-anchor','middle')
.style("font-weight","bold")
.style("fill","white")
.text(function(d) {return d.properties.at_between})

var t = svg.selectAll("text") //d3 geopath
.data(busroutestop3.features)
.enter() //there are more data than elements, this selects them
.append("text") //appends path to data
.attr("x", function(d) {return path1.centroid(d)[0]})
.attr("y", function(d) {return path1.centroid(d)[1]-8})
.attr('font-family','helvetica')
.style('font-size','.4em')
.attr('text-anchor','middle')
.style("font-weight","bold")
.style("fill","white")
.text(function(d) {return d.properties.at_between})

t.enter().append('text') //d3 geopath
.data(busroutestop2.features)
.enter() //there are more data than elements, this selects them
.append("text") //appends path to data
.attr("x", function(d) {return path1.centroid(d)[0]})
.attr("y", function(d) {return path1.centroid(d)[1]-8})
.attr('font-family','helvetica')
.style('font-size','.4em')
.attr('text-anchor','middle')
.style("font-weight","bold")
.style("fill","white")
.text(function(d) {return d.properties.at_between})

t.enter().append('text') //d3 geopath
.data(busroutestop3.features)
.enter() //there are more data than elements, this selects them
.append("text") //appends path to data
.attr("x", function(d) {return path1.centroid(d)[0]})
.attr("y", function(d) {return path1.centroid(d)[1]-8})
.attr('font-family','helvetica')
.style('font-size','.4em')
.attr('text-anchor','middle')
.style("font-weight","bold")
.style("fill","white")
.text(function(d) {return d.properties.at_between})

/*
svg
.append("text")
.attr('class','title')
.attr('x','510')
.attr('y','250')
.attr('font-family','helvetica')
.attr('font-size','.5em')
.attr('font-weight','Bold')
.attr('fill','rgb(0,0,0)')
.text('Site A')

svg
.append("text")
.attr('class','title')
.attr('x','785')
.attr('y','609')
.attr('font-family','helvetica')
.attr('font-size','.5em')
.attr('font-weight','Bold')
.attr('fill','rgb(0,0,0)')
.text('Site B')
*/
scaleBar
.projection(projection)
.size([width, height]);

svg.append("g")
.call(scaleBar);

return svg.node();
}
Insert cell
siteBoundaries = FileAttachment("site boundaries.geojson").json()
Insert cell
borough = FileAttachment("borough.geojson").json()
Insert cell
bbox1 = FileAttachment("bbox.geojson").json()
Insert cell
bus = FileAttachment("bus.geojson").json()
Insert cell
drinking = FileAttachment("drinking.geojson").json()
Insert cell
parks = FileAttachment("parks-2.geojson").json()
Insert cell
ssline = FileAttachment("ssline.geojson").json()
Insert cell
sbline = FileAttachment("sbline.geojson").json()
Insert cell
connections = FileAttachment("connections@1.geojson").json()
Insert cell
callout = (g, value) => {
if (!value) return g.style("display", "none");

g
.style("display", null)
.style("pointer-events", "none")
.style("font", "10px sans-serif");

const path = g.selectAll("path")
.data([null])
.join("path")
.attr("fill", "white")
.attr("stroke", "black");

const text = g.selectAll("text")
.data([null])
.join("text")
.call(text => text
.selectAll("tspan")
.data((value + "").split(/\n/))
.join("tspan")
.attr("x", 0)
.attr("y", (d, i) => `${i * 1.1}em`)
.style("font-weight", (_, i) => i ? null : "bold")
.text(d => d));

const {x, y, width: w, height: h} = text.node().getBBox();

text.attr("transform", `translate(${-w / 2},${15 - y})`);
path.attr("d", `M${-w / 2 - 10},5H-5l5,-5l5,5H${w / 2 + 10}v${h + 20}h-${w + 20}z`);
}
Insert cell
fountainsConnections = FileAttachment("fountains-connections@1.geojson").json()
Insert cell
bbox2 = FileAttachment("bbox-2.geojson").json()
Insert cell
dronelinebus = FileAttachment("dronelinebus-1.geojson").json()
Insert cell
dronelinef = FileAttachment("dronelinef-3.geojson").json()
Insert cell
oob = FileAttachment("OOB.geojson").json()
Insert cell
oos = FileAttachment("OOS.geojson").json()
Insert cell
bbox3 = FileAttachment("bbox3.geojson").json()
Insert cell
stcenterlineTrimed = FileAttachment("stcenterline-trimed.geojson").json()
Insert cell
busroutestop1 = FileAttachment("busroutestop1.geojson").json()
Insert cell
busroutestop2 = FileAttachment("busroutestop2.geojson").json()
Insert cell
busroutestop3 = FileAttachment("busroutestop3.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