Published
Edited
Dec 6, 2021
2 forks
Insert cell
# NYC TOURISTS’ MAP _ Interactive & Zoomable
Insert cell
postTitle ="TOURISTS’ MAP"
Insert cell
zips = FileAttachment("zip_test4.geojson").json()
Insert cell
nycParks = FileAttachment("Open Space (Parks)_exported simplifed.geojson").json()
Insert cell
subway_lines = FileAttachment("subway_lines.geojson").json()
Insert cell
subway_station = FileAttachment("subway_station.geojson").json()
Insert cell
subway_station_smallCircle= FileAttachment("station circle small simple.geojson").json()
Insert cell
station_connection = FileAttachment("station connection@1.geojson").json()
Insert cell
attracting = FileAttachment("attracting@3.geojson").json()
Insert cell
waterfront = FileAttachment("waterfront@1.geojson").json()
Insert cell
vendorConnection = FileAttachment("vendor connection line.geojson").json()
Insert cell
vendor = FileAttachment("vendor@3.geojson").json()
Insert cell
waterfrontPath = FileAttachment("waterfront path.geojson").json()
Insert cell
Insert cell
boundary = FileAttachment("boundary@1.geojson").json()
Insert cell
waterfrontText = FileAttachment("waterfront text.geojson").json()
Insert cell
d3 = require("d3@6")
Insert cell
chart = {
const width = 960,
height = 900;
// Use Mercator projection
const projection = d3.geoMercator().fitSize([width - 50, height - 50], zips)
const path = d3.geoPath().projection(projection);

// const zoom = d3.zoom()
// .scaleExtent([1, 9])
// .translateExtent([[-(2 * width), -(.5 * height)], [2 * width, 1.5 * height]])
// .on("zoom", zoomed);


var svg = d3.create("svg")
.attr("viewBox", [-60, 0, width, height])
//.style("background",'rgb(0,0,40)');
.style("background",
`linear-gradient(to left, rgb(0,0,0) 0%, rgb(0,0,70) 100%)`);
svg.call(d3.zoom()
.extent([[0, 0], [width, height]])
.scaleExtent([1, 8])
.on("zoom", zoomed));

const g = svg.append("g")
.attr("id", "paths");

//add title
var subtitle= svg.selectAll('title')
.data("TOURISTS’ MAP")
.enter()
.append('text')
.attr('x',function(d,i){return i*23+5})
.attr('y','60')
.style("font", "30px roboto")
.attr('text-anchor','middle')
.text(function(d){return d})
.attr("stroke", "rgb(255,255,255)")
.attr("stroke-width", ".3")
.attr("fill", "rgb(255,255,255)")
.attr("transform", `translate(${0},${10})`)

var title= svg.selectAll('subtitle')
.data("of New York City")
.enter()
.append('text')
.attr('x',function(d,i){return i*15+5})
.attr('y','100')
.style("font", "20px roboto")
.attr('text-anchor','middle')
.text(function(d){return d})
.attr("stroke", "rgb(180,180,180)")
.attr("stroke-width", ".3")
.attr("fill", "rgb(180,180,180)")


function showDes(event, d){
tooltipDes.call(callout,
`${"Map Information:"}
${
"This is a zoomable map for tourists travelling in New York City. Circles, hexagons"+'\n'+
"and lines are pinned on the places welcomed by tourists to clearly demonstrate"+'\n'+
"which area attracts visitors most."+'\n'+
'\n'+
"The comparatively adjacent waterfront spots and historic districts are grouped"+'\n'+
"together, then forming closed polylines as ideal paths for people to"+'\n'+
"visit all these places efficiently; vendors with a distance less than 800 meters"+'\n'+
"are connected to form a network."+'\n'+
'\n'+
"The links between paths, networks and subway stations explicitly show which"+'\n'+
"stations are close to the sightseeing points."+'\n'+
'\n'+
"On the macrosclae, the average points of waterfront spots, historic spots,"+'\n'+
"vendor spots along with airports are considered as attractors, and flows at the "+'\n'+
"stations have a generic tendency of moving towards these spots, which are"+'\n'+
"depicted within the “magnetic field.”"}` );
tooltipDes.attr("transform", `translate(${340},${80})`);}

//remove map des tooltip
function offDes(event, d) {
tooltipDes.call(callout,null)}
// insert boundary
var bound = g.selectAll("boudnary") //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("d", path) //The d attribute defines a path to be drawn, only applies to appended elements
.style('fill-opacity','0')
.style("stroke-width", ".6")
.style("stroke","rgb(255,255,255)")
.style('stroke-opacity','.1')

//zip code boundary
const zipCode = g.append("g")
.selectAll("zipCode") //d3 geopath
.data(zips.features) //get data to define path
.attr("cursor", "pointer")
.enter() //there are more data than elements, this selects them
.append("path") //appends path to data
.attr("class","county")
.attr("d", path) //The d attribute defines a path to be drawn, only applies to appended elements
//.style("fill", d=>color (1/d.properties.POPULATION))
//.style('fill-opacity','0.1')
.style("fill", "rgb(0,0,0)")
.style('fill-opacity','0.95')
.style("stroke-width", ".2")
.style("stroke", "rgb(255,255,255)")
.style('stroke-opacity','.5')
.on("touchmove mousemove", showState)
.on("mouseout", leaveState)

var tooltip1 = svg.append("g");
var tooltip2 = svg.append("g");
function showState (event, d) {
tooltip1.
call(info,
`${"District Info: "}`)
.attr("class", "countyInfo")
.attr("transform", `translate(${150},${180}) scale(${1.5})`)
.append("text")
tooltip2.
call(info,
`${"ZipCode: "}${d.properties.ZIPCODE}
${"County: "}${d.properties.COUNTY}
${"Population: "}${d.properties.POPULATION}`)
.attr("class", "countyName")
.attr("transform", `translate(${150},${220})`)
.append("text")

d3.select(this)
.style("stroke-opacity",".8")
.style("stroke-width",".6")
.style("fill","rgb(175,238,238)")
.style('fill-opacity','.2')
}
function leaveState() {
d3.select(this) .style("stroke-opacity",".6");
d3.select(this) .style("stroke-width",".12");
d3.select(this) .style("fill", "rgb(0,0,0)");
d3.select(this) .style('fill-opacity','1');
tooltip1.select(this) .call(info,null);
tooltip2.select(this) .call(info,null);
}
//insert parks into the map
var park = g.selectAll("nycParks") //d3 geopath
.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", path) //The d attribute defines a path to be drawn, only applies to appended elements
.style("fill", "rgb(0,82,199)")
.style('fill-opacity','.15')
.style("stroke-width", ".5")
//insert attracting
var attractLine = g.selectAll("attracting") //d3 geopath
.data(attracting.features) //get data to define path
.enter() //there are more data than elements, this selects them
.append("path") //appends path to data
.attr('class','attract')
.attr("d", path) //The d attribute defines a path to be drawn, only applies to appended elements
.style('fill-opacity','0')
.style("stroke-width", ".3")
.style("stroke", "rgb(161,0,4)")
.style("stroke-opacity", "0.6")
//.on("mouseover", popup)
//.on("mouseout", back)

// metro lines
//insert metro lines into the map
var metroLine = g.selectAll("subway_lines") //d3 geopath
.data(subway_lines.features) //get data to define path
.enter() //there are more data than elements, this selects them
.append("path") //appends path to data
.attr('class','metroLine')
.attr("d", path) //The d attribute defines a path to be drawn, only applies to appended elements
.style('fill-opacity','0')
.style("stroke-width", ".4")
.style("stroke","rgb(184,0,134)")

//insert station small circle
var stationCircle = g.selectAll("stationCircle") //d3 geopath
.data(subway_station_smallCircle.features) //get data to define path
.enter() //there are more data than elements, this selects them
.append("path") //appends path to data
.attr("d", path) //The d attribute defines a path to be drawn, only applies to appended elements
.style("fill", "rgb(250,16,106)")
.style('fill-opacity','.11')
.style("stroke-width", ".2")
.style("stroke","rgb(236,59,171)")
.style('stroke-opacity','.6')

//insert station connection
var stationConnection = g.selectAll("stationConnection") //d3 geopath
.data(station_connection.features) //get data to define path
.enter() //there are more data than elements, this selects them
.append("path") //appends path to data
.attr("d", path) //The d attribute defines a path to be drawn, only applies to appended elements
.style("stroke-width", ".3")
.style("stroke", "rgb(184,0,134)")
.style("stroke-opacity", ".6")
.style("mix-blend-mode", "screen");
//insert metro stations into the map
var station= g.selectAll("subway_station")
.enter()
.append('circle')
.data(subway_station.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.7')
.attr ('fill', 'rgb(250,16,106)')
.style('fill-opacity','.26')
.on('mouseover', addCircle)
.on('mouseout', removeCircle)
.append("title")
.text(function(d){return (`${"Station Name:"}`+ "\n"+ d.properties.name)})

//insert waterfront small circle
var waterCircle = g.selectAll("waterfront") //d3 geopath
.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("d", path) //The d attribute defines a path to be drawn, only applies to appended elements
.style("fill", "rgb(232,127,26)")
.style('fill-opacity','.2')
.style("stroke-width", ".1")
.style("stroke","rgb(232,127,26)")
.style('stroke-opacity','.5')

//insert vendor
var vendorPos = g.selectAll("vendorposition") //d3 geopath
.data(vendor.features) //get data to define path
.enter() //there are more data than elements, this selects them
.append("path") //appends path to data
.attr("d", path) //The d attribute defines a path to be drawn, only applies to appended elements
.style("fill", "rgb(63,79,191)")
.style('fill-opacity','0.25')
.style("stroke-width", ".3")
.style("stroke","rgb(63,79,191)")
.style('stroke-opacity','.5')
.style("mix-blend-mode", "screen");
//insert vendor connection
var VendorConnection = g.selectAll("vendorConnection") //d3 geopath
.data(vendorConnection.features) //get data to define path
.enter() //there are more data than elements, this selects them
.append("path") //appends path to data
.attr("d", path) //The d attribute defines a path to be drawn, only applies to appended elements
.style("stroke-width", ".25")
.style("stroke", "rgb(10,42,191)")
.style("stroke-opacity", ".65")
.style("mix-blend-mode", "screen");

//insert waterfront path OPACITY
var waterPathOpa = g.selectAll("waterfrontPath") //d3 geopath
.data(waterfrontPath.features) //get data to define path
.enter() //there are more data than elements, this selects them
.append("path") //appends path to data
.attr("d", path) //The d attribute defines a path to be drawn, only applies to appended elements
.style("stroke-width", "4")
.style("stroke", "rgb(255,111,0)")
.style("stroke-opacity", ".2")
.style("fill-opacity", "0")

//insert waterfront path
var waterPath = g.selectAll("waterfrontPath") //d3 geopath
.data(waterfrontPath.features) //get data to define path
.enter() //there are more data than elements, this selects them
.append("path") //appends path to data
.attr("d", path) //The d attribute defines a path to be drawn, only applies to appended elements
.style("stroke-width", "1")
.style("stroke", "rgb(255,111,0)")
.style("stroke-opacity", ".8")
.style("fill-opacity", "0")
.on("mouseover", waterPathHighlight)
.on("mouseout", waterPathBack)

//pop up waterfront line
var tooltip4= svg.append("g");
function waterPathHighlight(event,d){
d3.select(this) .style("stroke","rgb(255,191,0)");
d3.select(this) .style('stroke-opacity','2');
d3.select(this) .style('stroke-width','1.4');
tooltip4.
call(info,
`${"Waterfront Tour Path"}`)
.attr("class", "waterpath")
.attr("transform", `translate(${158},${270})`)
.append("text")
}

function waterPathBack(event,d){
d3.select(this) .style("stroke","rgb(255,111,0)");
d3.select(this) .style('stroke-opacity','.8');
d3.select(this) .style('stroke-width','1');
tooltip4.call(info, [null]);
}

//insert historic path OPACITY
var historicPaOpa = g.selectAll("historicPath") //d3 geopath
.data(historicPath.features) //get data to define path
.enter() //there are more data than elements, this selects them
.append("path") //appends path to data
.attr("d", path) //The d attribute defines a path to be drawn, only applies to appended elements
.style("stroke-width", "4")
.style("stroke", "rgb(18,170,201)")
.style("stroke-opacity", ".2")
.style("fill-opacity", "0")
//insert historic path
var historicPa = g.selectAll("historicPath") //d3 geopath
.data(historicPath.features) //get data to define path
.enter() //there are more data than elements, this selects them
.append("path") //appends path to data
.attr("d", path) //The d attribute defines a path to be drawn, only applies to appended elements
.style("stroke-width", ".9")
.style("stroke", "rgb(18,170,201)")
.style("stroke-opacity", ".7")
.style("fill-opacity", "0")
.on("mouseover", historicPathHighlight)
.on("mouseout", historicPathBack)
//pop up historic line
var tooltip5= svg.append("g");
function historicPathHighlight(event,d){
d3.select(this) .style("stroke","rgb(0,255,174)");
d3.select(this) .style('stroke-opacity','1.3');
d3.select(this) .style('stroke-width','1.2');
tooltip5.
call(info,
`${"Historic Architecture Tour Path"}`)
.attr("class", "waterpath")
.attr("transform", `translate(${182},${270})`)
.append("text")
}
function historicPathBack(event,d){
d3.select(this) .style("stroke","rgb(18,170,201)");
d3.select(this) .style('stroke-opacity','.7');
d3.select(this) .style('stroke-width','.7');
tooltip5.call(info, [null]);
}

//insert waterfront circle
var waterPoint = g.selectAll("waterPoint")
//.enter()
//.append('path')
.data(waterfrontText.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',.3)
.attr('fill','rgb(200,200,200)')
.style('fill-opacity','.9')

//insert WaterPoint
var waterPointAll = g.selectAll("waterPointAll")
//.enter()
//.append('circle')
.data(waterfrontText.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',4)
.attr('fill','rgb(160,160,160)')
.style('fill-opacity','.25')
.on('mouseover', addWaterCircle)
.on('mouseout', removeCircle)
.append("title")
.text((d, i) => (`${"Waterfront Point:"}`+ "\n"+`${i}` +`${"."}`+ d.properties.site))

//add line from label to bike text
var lineText =
svg.selectAll('waterline')
.data(waterfrontText.features)
.enter()
.append('line')
.transition()
.attr('x1',function(d) {return path.centroid(d)[0]})
.attr('y1',function(d) {return path.centroid(d)[1]})
.attr('x2',80)
.attr('y2',function(d,i){return i*7+130})
.attr("stroke-width",".3")
.style("stroke-opacity",".55")
.style("stroke","rgb(150,150,150)")
//add waterfrontwaterfront text
var waterPointText = svg.selectAll('watertext')
.data(waterfrontText.features)
.enter()
.append('text')
.attr('x','0')
.attr('y',function(d,i){return i*7+10})
.style("font", "4.5px roboto")
.style("stroke-width", ".2")
.style("stroke", "rgb(150,150,150)")
.style("stroke-opacity", ".5")
.style("fill", "rgb(150,150,150)")
.style("fill-opacity", ".5")
.attr('text-anchor','begin')
.text(function(d){return d.properties.site})
.attr("transform", `translate(${0},${120})`)

// var rec = svg
//.append("rect")
//.attr("x", '100')
//.attr("y", '45')
//.attr("width", '290')
//.attr("height", '55')
//.style('fill-opacity','0')
//.style('stroke-opacity','0')
//.on("touchmove mousemove", showDes)
//.on("mouseout", offDes)


var recGrey = svg
.append("rect")
.attr("x", '321')
.attr("y", '51')
.attr("width", '50')
.attr("height", '20')
.style('fill-opacity','1')
.style('fill',"rgb(200,200,200)")
.style('stroke-opacity','0')
var recWhite = svg
.append("rect")
.attr("x", '320')
.attr("y", '50')
.attr("width", '50')
.attr("height", '20')
.style('fill-opacity','1')
.style('fill',"rgb(255,255,255)")
.style('stroke-opacity','0')
.on("click", showDes)
//.on("touchmove mousemove", showDes)
.on("mouseout", offDes)
var click= svg.selectAll('clicking')
.data("CLICK")
.enter()
.append('text')
.attr('x', function(d,i){return i*8+328})
.attr('y','64')
.style("font", "12px roboto")
.attr('text-anchor','middle')
.text(function(d){return d})
.attr("stroke", "rgb(80,80,80)")
.attr("stroke-width", ".6")
.attr("fill", "rgb(120,120,120)")
.on("click", showDes)
//.on("touchmove mousemove", showDes)
.on("mouseout", offDes)
var tooltipDes = svg.append("g");





function zoomed({transform}) {
d3.select('g')
.attr('transform', transform);}
//pop up line
function popup(event,d){
d3.select(this) .style("stroke","rgb(220,20,60)");
d3.select(this) .style('stroke-opacity','1');
d3.select(this) .style('stroke-width','1');
}

function back(event,d){
d3.select(this) .style("stroke","rgb(161,0,4)");
d3.select(this) .style('stroke-opacity','0.5');
d3.select(this) .style('stroke-width','.3');
}

//add subway stop tool tip
function addCircle(event, d) {
d3.select(this).append("circle") //appends path to data
d3.select(this).attr('class','lc')
d3.select(this).attr('cx', function(d){return path.centroid(d)[0]})
d3.select(this).attr("cy", function(d){return path.centroid(d)[1]})
d3.select(this).attr ('r',10)
d3.select(this).attr('fill','rgb(251,189,82)')
d3.select(this).style('fill-opacity','.5')
d3.select(this).style("mix-blend-mode", "screen")
}

function addWaterCircle(event, d) {
d3.select(this).append("circle") //appends path to data
d3.select(this).attr('class','lc')
d3.select(this).attr('cx', function(d){return path.centroid(d)[0]})
d3.select(this).attr("cy", function(d){return path.centroid(d)[1]})
d3.select(this).attr ('r',10)
d3.select(this).attr('fill','rgb(48,83,255)')
d3.select(this).style('fill-opacity','.5')
d3.select(this).style("mix-blend-mode", "screen")
}

//remove subway stop tool tip
function removeCircle(event, d) {
d3.selectAll("circle.lc").remove()
}

return svg.node();
}
Insert cell
Insert cell
Insert cell
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