Published
Edited
Mar 2, 2022
Insert cell
# Interactive Map - Roosevelt Island - Strothenke + Weber
Insert cell
zips = FileAttachment("zips3.geojson").json()
Insert cell
nycParks = FileAttachment("parks2.geojson").json()
Insert cell
SubwayLines = FileAttachment("observable_subway lines.geojson").json()
Insert cell
SubwayStops = FileAttachment("observable_subway stops.geojson").json()
Insert cell
//Waterfront = FileAttachment("WATERFRONT.geojson").json()
Insert cell
d3 = require("d3@6")
Insert cell
bbox = FileAttachment("map_boundaries.geojson").json()
Insert cell
box = 'https://docs.google.com/spreadsheets/d/e/2PACX-1vSmbXic18-jY4ctqclgAS7egoC1haIuf9-mM95rqrJ-XRID95uFUfhjZ2O2-CZWeDXs0V3MJDSbFQA-/pub?output=csv'
Insert cell
studioSite = d3.csv(box,d3.autoType)
Insert cell
Food = 'https://docs.google.com/spreadsheets/d/e/2PACX-1vQEw210OGyujcFrTl4uuz8OCm-Hll65LqlrpS-QYziO327buQvbgj2kkrcpTexwTTGvHcFYB-tnZ6li/pub?gid=77546869&single=true&output=csv'
Insert cell
FoodPOI = d3.csv(Food,d3.autoType)
Insert cell
Areas = FileAttachment("custom_areas_1@4.geojson").json()
Insert cell
chart = {
const width = 960,
height = 900;
const svg = d3.create("svg")
.attr("viewBox", [-60, 0, width, height]);

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

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


// insert zips into map
g.selectAll("path") //d3 geopath
//svg
//.selectAll('path')
.data(zips.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", path) //The d attribute defines a path to be drawn, only applies to appended elements
.attr("fill",'black')
//.attr("fill", d => color(d.properties.POPULATION))
.style('fill-opacity','.05')
.style("stroke", "black")
.style("stroke-width", ".5")
.attr('stroke-opacity','.75')
//display site circle location
var cir = svg.append("g").attr("id", "paths");
cir = svg.selectAll("circle") //d3 geopath
.data(studioSite) //get data to define path
.enter() //there are more data than elements, this selects them
.append("circle") //appends path to data
.attr('cx',function(d) {return projection([d.long,d.lat])[0]})
.attr("cy",function(d) {return projection([d.long,d.lat])[1]})
.attr('r', 3)
.attr('fill', 'black')
.style('fill-opacity','1')
.style("stroke-width", "1")

//display site text name
var t = svg.selectAll('text')
t.enter().append('text')
.data(studioSite)
.enter()
.append('text')
.attr('class','annotation')
.attr('x','230')
.attr('y','590')
.attr("font-family", "Helvetica")
.attr('font-size','.9em')
.attr('fill','rgb(0,0,0)')
.attr('text-anchor','end')
.text(function(d){return d.name})
//display site text description
var t = svg.selectAll('text')
t.enter().append('text')
.data(studioSite)
.enter()
.append('text')
.attr('class','siteDescription')
.attr('x','230')
.attr('y','605')
.attr("font-family", "Helvetica")
.attr('font-size','.6em')
.attr('fill','rgb(0,0,0)')
.attr('text-anchor','end')
.text(function(d){return d.description})

//use word wrap function to limit line width
var wrap = svg.selectAll("text.siteDescription")
.each(function(d, i) { wrap_text(d3.select(this), 250) });

//display diagonal title line
var ln = svg.append("g").attr("id", "paths");
ln = svg.selectAll("line")
.data(studioSite) //get data to define path
.enter()
.append('line')
.attr('class','sbLn')
.attr('x1','250')
.attr('y1','595')
.attr('x2',function(d) {return projection([d.long,d.lat])[0]})
.attr("y2",function(d) {return projection([d.long,d.lat])[1]})
.attr('stroke-width','.8')
.attr('stroke-opacity','1')
.style('stroke','rgb(0,0,0)')

//display horizontal title line
svg
.append('line')
.attr('class','sbLn')
.attr('x1','238')
.attr('y1','595')
.attr('x2','250')
.attr('y2','595')
.attr('stroke-width','.8')
.attr('stroke-opacity','1')
.style('stroke','rgb(0,0,0)')

//use word wrap function to limit line width
var wrap = svg.selectAll("text.FoodPOI")
.each(function(d, i) { wrap_text(d3.select(this), 250) });


//add park labels/park fill
function addFill(event,d) {
d3.select(this).style("fill", "lightgreen")
d3.select(this).style('fill-opacity','.25')
svg
.append('text')
.attr('class','labels')
.attr('x','100')
.attr('y','100')
.attr('font-size','.85em')
.text(d.properties.signname)
}
//take away park labels/park fills
function removeFill(event,d) {
d3.selectAll('text.labels').remove()
d3.select(this).style("fill", "green")
d3.select(this).style('fill-opacity','.25')
}



// insert subway lines into map
g.selectAll("path2") //d3 geopath
//svg
//.selectAll('path')
.data(SubwayLines.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", path) //The d attribute defines a path to be drawn, only applies to appended elements
//.style("fill", "red")
.style('fill-opacity','0')
.style("stroke-width", ".35")
.style("stroke", "rgb(0,0,0)")


// insert subway stops into map
var c = svg.selectAll("circle") //d3 geopath
//svg
//.selectAll('path')
.data(SubwayStops.features) //get data to define path
.enter() //there are more data than elements, this selects them
.append("circle") //appends path to data
.attr('cx',function(d) {return path.centroid(d)[0]})
.attr("cy",function(d) {return path.centroid(d)[1]})
.attr('r',1.75)
.attr('fill','black')
.style('fill-opacity','1')
.on('mouseover', addBox)
.on('mouseout', removeBox)

//add subway stop tool tip
var tooltip = svg.append("g");
function addBox(event, d) {
var centroid = path.centroid(d);
tooltip.call(
callout,
`${d.properties.name}
${d.properties.line}, ${d.properties.line}`
);
tooltip.attr("transform", "translate(" + centroid + ")");
}

function removeBox(event, d) {
tooltip.call(callout,null)
}

/*
// insert waterfront into map
g.selectAll("path3") //d3 geopath
//svg
//.selectAll('path')
.data(Waterfront.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", path) //The d attribute defines a path to be drawn, only applies to appended elements
.style("fill", "tan")
.style('fill-opacity','0')
.style("stroke-width", ".2")
.style("stroke", "black")
.style("stroke-width", ".25")
.attr('stroke-opacity','.75')
*/

//Areas Actual
g.selectAll("path4") //d3 geopath
//svg
//.selectAll('path')
.data(Areas.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", path) //The d attribute defines a path to be drawn, only applies to appended elements
.style("fill", "rgb(0,128,0)")
.style('fill-opacity','.25')
.style("stroke-width", ".2")
.style("stroke", "rgb(0,128,0)")
.on('mouseover',addText)
.on('mouseleave',removeText)
//mouseover function for areas text
function addText(event, d){
svg.append("text") //appends path to data
.attr('class','areas')
.attr('x',path.centroid(d)[0])
.attr('y',path.centroid(d)[1]+50)
//.attr('x','200')
//.attr('y','500')
.attr("font-family", "Helvetica")
.attr('font-size','.6em')
.attr('text-anchor','end')
.attr('fill','rgb(0,0,0)')
.text(d.properties.custom_areas_name)
}
function removeText(event,d){
d3.selectAll('text.areas').remove()
}


/*
// insert population circles into map
var c = svg.selectAll("circle") //d3 geopath
.data(zips.features) //get data to define path
.enter() //there are more data than elements, this selects them
.append("circle") //appends path to data
.attr('cx',function(d) {return path.centroid(d)[0]})
.attr("cy",function(d) {return path.centroid(d)[1]})
.attr('r',function(d) {return d.properties.POPULATION/10000})
.attr('fill','black')
.style('fill-opacity','.9')
*/


// insert parks into map
g.selectAll("path5") //d3 geopath
//svg
//.selectAll('path')
.data(nycParks.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", 'green')
.style('fill-opacity','.25')
.style("stroke-width", ".2")
.style("stroke", 'green')
.on('mouseover', addFill)
.on('mouseleave',removeFill)

// insert food into map
//var cir = svg.append("g").attr("id", "paths");
//var cir2 = svg.selectAll("circle") //d3 geopath
svg
c.enter().append('circle')
.data(FoodPOI) //get data to define path
.enter() //there are more data than elements, this selects them
.append("circle") //appends path to data
.attr('cx',function(d) {return projection([d.long,d.lat])[0]})
.attr("cy",function(d) {return projection([d.long,d.lat])[1]})
.attr('r', 3)
.attr('fill', 'darkblue')
.style('fill-opacity','1')
.style("stroke-width", "1")
.on('mouseover', FoodText)
.on('mouseout',FoodErase)
//mouseover function for poi text
function FoodText(event,d){
console.log('hello')
svg.append("text") //appends path to data
.attr('class','foodPoi')
.attr('x','200')
.attr('y','500')
.attr("font-family", "Helvetica")
.attr('font-size','.8em')
.attr('text-anchor','end')
.attr('fill','rgb(0,0,0)')
//.style("font-weight","bold")
.text(d.name)
svg.append("text") //appends path to data
.attr('class','foodPoi')
.attr('x','200')
.attr('y','510')
.attr("font-family", "Helvetica")
.attr('font-size','.5em')
.attr('text-anchor','end')
.attr('fill','rgb(0,0,0)')
//.style("font-weight","bold")
.text(d.description)
}

function FoodErase(event,d){
d3.selectAll('text.foodPoi').remove()
}
return svg.node();
}
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
color = d3.scaleQuantize([1, 100000], d3.schemeBlues[9])
Insert cell
FileAttachment("WATERFRONT.geojson")
Insert cell
import { wrap_text, wrap_text_nchar } from "@ben-tanen/svg-text-and-tspan-word-wrapping"
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