chart = {
const width = 960,
height = 900;
const projection = d3.geoMercator().fitSize([width - 50, height - 50], zips)
const path = d3.geoPath().projection(projection);
var svg = d3.create("svg")
.attr("viewBox", [-60, 0, width, height])
.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");
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})`);}
function offDes(event, d) {
tooltipDes.call(callout,null)}
var bound = g.selectAll("boudnary")
.data(boundary.features)
.enter()
.append("path")
.attr("d", path)
.style('fill-opacity','0')
.style("stroke-width", ".6")
.style("stroke","rgb(255,255,255)")
.style('stroke-opacity','.1')
const zipCode = g.append("g")
.selectAll("zipCode")
.data(zips.features)
.attr("cursor", "pointer")
.enter()
.append("path")
.attr("class","county")
.attr("d", path)
.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);
}
var park = g.selectAll("nycParks")
.data(nycParks.features)
.enter()
.append("path")
.attr('class','outlines')
.attr("d", path)
.style("fill", "rgb(0,82,199)")
.style('fill-opacity','.15')
.style("stroke-width", ".5")
var attractLine = g.selectAll("attracting")
.data(attracting.features)
.enter()
.append("path")
.attr('class','attract')
.attr("d", path)
.style('fill-opacity','0')
.style("stroke-width", ".3")
.style("stroke", "rgb(161,0,4)")
.style("stroke-opacity", "0.6")
var metroLine = g.selectAll("subway_lines")
.data(subway_lines.features)
.enter()
.append("path")
.attr('class','metroLine')
.attr("d", path)
.style('fill-opacity','0')
.style("stroke-width", ".4")
.style("stroke","rgb(184,0,134)")
var stationCircle = g.selectAll("stationCircle")
.data(subway_station_smallCircle.features)
.enter()
.append("path")
.attr("d", path)
.style("fill", "rgb(250,16,106)")
.style('fill-opacity','.11')
.style("stroke-width", ".2")
.style("stroke","rgb(236,59,171)")
.style('stroke-opacity','.6')
var stationConnection = g.selectAll("stationConnection")
.data(station_connection.features)
.enter()
.append("path")
.attr("d", path)
.style("stroke-width", ".3")
.style("stroke", "rgb(184,0,134)")
.style("stroke-opacity", ".6")
.style("mix-blend-mode", "screen");
var station= g.selectAll("subway_station")
.enter()
.append('circle')
.data(subway_station.features)
.enter()
.append("circle")
.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)})
var waterCircle = g.selectAll("waterfront")
.data(waterfront.features)
.enter()
.append("path")
.attr("d", path)
.style("fill", "rgb(232,127,26)")
.style('fill-opacity','.2')
.style("stroke-width", ".1")
.style("stroke","rgb(232,127,26)")
.style('stroke-opacity','.5')
var vendorPos = g.selectAll("vendorposition")
.data(vendor.features)
.enter()
.append("path")
.attr("d", path)
.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");
var VendorConnection = g.selectAll("vendorConnection")
.data(vendorConnection.features)
.enter()
.append("path")
.attr("d", path)
.style("stroke-width", ".25")
.style("stroke", "rgb(10,42,191)")
.style("stroke-opacity", ".65")
.style("mix-blend-mode", "screen");
var waterPathOpa = g.selectAll("waterfrontPath")
.data(waterfrontPath.features)
.enter()
.append("path")
.attr("d", path)
.style("stroke-width", "4")
.style("stroke", "rgb(255,111,0)")
.style("stroke-opacity", ".2")
.style("fill-opacity", "0")
var waterPath = g.selectAll("waterfrontPath")
.data(waterfrontPath.features)
.enter()
.append("path")
.attr("d", path)
.style("stroke-width", "1")
.style("stroke", "rgb(255,111,0)")
.style("stroke-opacity", ".8")
.style("fill-opacity", "0")
.on("mouseover", waterPathHighlight)
.on("mouseout", waterPathBack)
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]);
}
var historicPaOpa = g.selectAll("historicPath")
.data(historicPath.features)
.enter()
.append("path")
.attr("d", path)
.style("stroke-width", "4")
.style("stroke", "rgb(18,170,201)")
.style("stroke-opacity", ".2")
.style("fill-opacity", "0")
var historicPa = g.selectAll("historicPath")
.data(historicPath.features)
.enter()
.append("path")
.attr("d", path)
.style("stroke-width", ".9")
.style("stroke", "rgb(18,170,201)")
.style("stroke-opacity", ".7")
.style("fill-opacity", "0")
.on("mouseover", historicPathHighlight)
.on("mouseout", historicPathBack)
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]);
}
var waterPoint = g.selectAll("waterPoint")
.data(waterfrontText.features)
.enter()
.append("circle")
.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')
var waterPointAll = g.selectAll("waterPointAll")
.data(waterfrontText.features)
.enter()
.append("circle")
.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))
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)")
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 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("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("mouseout", offDes)
var tooltipDes = svg.append("g");
function zoomed({transform}) {
d3.select('g')
.attr('transform', transform);}
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');
}
function addCircle(event, d) {
d3.select(this).append("circle")
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")
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")
}
function removeCircle(event, d) {
d3.selectAll("circle.lc").remove()
}
return svg.node();
}