Public
Edited
Jan 24, 2024
Insert cell
# When In Rome
Insert cell
d3 = require("d3@6")
Insert cell
chart = {
const width = 1100, height = 960;
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width-50, height-50]);

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

var path1 = 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 path6 = d3.geoPath().projection(projection);
var path7 = d3.geoPath().projection(projection);

var geopaths = svg.append("g").attr("id", "paths");
var spots = svg.selectAll("circle")
var polyline = svg.selectAll("polyline").enter().append("polyline")

geopaths.selectAll("path1")
.data(region.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) // defines a path to be drawn and only applies it to appended elements
.style("fill", "rgb(255,239,224)")
.style("fill-opacity", "1")
// .style('stroke-opacity','.1')
// .style("stroke-width", '.5')
// .style("stroke", "rgb(0,0,0)")
geopaths.selectAll("path2")
.data(districts.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) // defines a path to be drawn and only applies it to appended elements
.style("fill", "rgb(255,249,244)")
.style("fill-opacity", "1")
// .style('stroke-opacity','.1')
// .style("stroke-width", '.5')
// .style("stroke", "rgb(0,0,0)")

geopaths.selectAll("path3")
.data(water.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) // defines a path to be drawn and only applies it to appended elements
.style("fill", "rgb(185,222,285)")
.style("fill-opacity", "1")
// .style('stroke-opacity','.1')
// .style("stroke-width", '.5')
// .style("stroke", "rgb(0,0,0)")

geopaths.selectAll("path4")
.data(buildings.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) // defines a path to be drawn and only applies it to appended elements
.style("fill", "rgb(255,218,185)")
.style("fill-opacity", "1")
// .style('stroke-opacity','.1')
// .style("stroke-width", '.5')
// .style("stroke", "rgb(0,0,0)")

geopaths.selectAll("path6")
.data(railwayLines.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) // defines a path to be drawn and only applies it to appended elements
.style("fill", "none")
.style('stroke-opacity','1')
.style("stroke-width", '.25')
.style("stroke", "rgb(0,0,0)")

// any way to separate data based on properties?
// change size of point on QGIS and reexport file
geopaths.selectAll("path7")
.data(transitStations.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) // defines a path to be drawn and only applies it to appended elements
.style("fill", "rgb(0,0,0)")
.style("fill-opacity", ".1")
// .style('stroke-opacity','1')
// .style("stroke-width", '.25')
// .style("stroke", "rgb(0,0,0)")

spots.data(places) // get data
.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)
.style('stroke','black')
.style('stroke-width','1')
.style('fill','rgb(255,185,187)')
.style('fill-opacity','1')
.on('mouseover',placeText)
.on('mouseout',removePlaceText)

function placeText(event,d) {
// console.log(d)

svg.append("text")
.attr('x', width-225)
.attr('y', 100)
.attr('class','place_position')
.style('font-family','roboto+slab')
.style('font-size','16px')
.style('font-weight','regular')
.text(d.name)

// control how many pixels wide our text block can be
var wrap = svg.selectAll("text.place_position")
.each(function(d, i) {
wrap_text(d3.select(this), 100)
});

svg.append("line")
.attr('x1', width-230)
.attr('y1', 100)
.attr('x2', projection([d.long,d.lat])[0])
.attr('y2', projection([d.long,d.lat])[1])
.attr('class','fLine')
.style('stroke-width', '1')
.style("stroke", "rgb(0,0,0)")
.style('stroke-dasharray', '6 4')

// polyline.data(name)
// .enter() // there are more data than elements, this selects them
// .append("polyline") // appends path to data
// .attr("points", function(d) {return d}) // we use a function to loop through the points
// .attr("class", 'place')
// .style('fill','black')
// .style('stroke','none')
// .style('stroke-width','.5')
}

function removePlaceText() {
d3.selectAll('text.place_position').remove() // geometry type, then class name
d3.selectAll('line.fLine').remove()
// d3.selectAll('polyline.place').remove()
}
// how do you order text so it's above the geopaths?
svg.append("text")
.attr('x','100')
.attr('y','100')
.style('font-family','roboto+slab')
.style('font-size','32px')
.style('font-weight','bold')
// .style('fill','purple') // text color
.text('Journey in Rome')

svg.append("text")
.attr('x','100')
.attr('y','130')
.style('font-family','roboto+slab')
.style('font-size','24px')
.style('font-weight','regular')
.text('Urban Data F23')
return svg.node();
}
Insert cell
import { wrap_text, wrap_text_nchar } from "@ben-tanen/svg-text-and-tspan-word-wrapping"
Insert cell
list = 'https://docs.google.com/spreadsheets/d/e/2PACX-1vQle1GARQMbbPzm2soNpReZB7FdSacDXG_eoQM4ucdIFlPnmLIrwrokQcW623kYqXuKQ4Hk7-UFwaez/pub?gid=0&single=true&output=csv'
Insert cell
places = d3.csv(list,d3.autoType)
Insert cell
transitStations = FileAttachment("transit stations.geojson").json()
Insert cell
railwayLines = FileAttachment("railway lines.geojson").json()
Insert cell
roads = FileAttachment("roads.geojson").json()
Insert cell
buildings = FileAttachment("buildings.geojson").json()
Insert cell
water = FileAttachment("water bodies.geojson").json()
Insert cell
districts = FileAttachment("districts@2.geojson").json()
Insert cell
region = FileAttachment("region.geojson").json()
Insert cell
border = FileAttachment("border.geojson").json()
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