Public
Edited
Jul 10, 2023
Fork of Maps
Insert cell
Insert cell
nycGeo
Insert cell
Insert cell
nycGeo.features[0]
Insert cell
Insert cell
getZip = feature => feature.properties.zcta
Insert cell
getZip(nycGeo.features[0])
Insert cell
Insert cell
margin = ({top: 20, right: 0, bottom: 0, left: 10})
Insert cell
mapWidth = 640 - margin.left - margin.right
Insert cell
mapHeight = 640 - margin.top - margin.bottom
Insert cell
Insert cell
projection = d3.geoAlbers()
.fitSize([mapWidth, mapHeight], nycGeo)
Insert cell
path = d3.geoPath().projection(projection)
Insert cell
Insert cell
path(nycGeo)
Insert cell
path(nycGeo).length
Insert cell
Insert cell
path(nycGeo.features[0])
Insert cell
Insert cell
{
// set up

const svg = d3.create('svg')
.attr('width', mapWidth + margin.left + margin.right)
.attr('height', mapHeight + margin.top + margin.bottom);

const g = svg.append("g")
.attr("transform", `translate(${margin.left}, ${margin.top})`);
// draw map

// draw one svg path per zip code
g.selectAll("path")
.data(nycGeo.features)
.join("path")
.attr("d", path)
.attr("fill", lightgray)
.attr("stroke", "white");

return svg.node();
}
Insert cell
Insert cell
zipToInjuries
Insert cell
maxInjuriesForZip = d3.max(Object.values(zipToInjuries))
Insert cell
Insert cell
Insert cell
{
const svg = d3.create('svg')
.attr('width', mapWidth + margin.left + margin.right)
.attr('height', mapHeight + margin.top + margin.bottom);

const g = svg.append("g")
.attr("transform", `translate(${margin.left}, ${margin.top})`);
// draw map

g.selectAll("path")
.data(nycGeo.features)
.join("path")
.attr("d", path)
.attr("fill", d => color(zipToInjuries[getZip(d)]))
.attr("stroke", "white");

return svg.node();
}
Insert cell
Insert cell
maxRadius = 10
Insert cell
// we want to map number of requests to the area of the circle,
// so we use scaleSqrt since the area of a circle is pi * (r^2)
radius = d3.scaleSqrt()
.domain([0, maxInjuriesForZip])
.range([0, maxRadius])
Insert cell
Insert cell
path.centroid(nycGeo.features[0])
Insert cell
{
const svg = d3.create('svg')
.attr('width', mapWidth + margin.left + margin.right)
.attr('height', mapHeight + margin.top + margin.bottom);

const g = svg.append("g")
.attr("transform", `translate(${margin.left}, ${margin.top})`);
// add legend

const legendGroup = g.append("g")
.attr('font-family', 'sans-serif')
.attr('font-size', 12);
legendGroup.append('text')
.text('Vehicle collision injuries, 2019')
const legendRows = legendGroup.selectAll("g")
.data([100, 200, 400, 800])
.join("g")
.attr("transform", (d, i) => `translate(0, ${15 + i * 2.5 * maxRadius})`);
legendRows.append("circle")
.attr("r", d => radius(d))
.attr("fill", "steelblue");

legendRows.append("text")
.attr("dominant-baseline", "middle")
.attr("x", maxRadius + 5)
.text(d => d);
// draw map

g.selectAll("path")
.data(nycGeo.features)
.join("path")
.attr("d", path)
.attr("fill", lightgray)
.attr("stroke", "white");

// draw circles

g.append("g")
.selectAll("circle")
.data(nycGeo.features)
.join("circle")
.attr("fill", "steelblue")
// set the position and size of the circles
.attr("transform", d => `translate(${path.centroid(d)})`)
.attr("r", d => radius(zipToInjuries[getZip(d)]));

return svg.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
nycGeo
Insert cell
china
Insert cell
china = FileAttachment("china@1.json").json()
Insert cell
karach = FileAttachment("map3.json").json()
Insert cell
karachi = FileAttachment("map4_1@8.json").json()
Insert cell
projection1 = d3.geoMercator()
.fitSize([mapWidth, mapHeight], karachi)
Insert cell
color = d3.scaleSequential()
.domain([0, 10])
.interpolator(d3.interpolateGreens)
.unknown(lightgray)
Insert cell
getGreen = feature => feature.properties.POP
Insert cell
path1 = d3.geoPath().projection(projection1)
Insert cell
{
const svg = d3.create('svg')
.attr('width', mapWidth + margin.left + margin.right)
.attr('height', mapHeight + margin.top + margin.bottom);

const g = svg.append("g")
.attr("transform", `translate(${margin.left}, ${margin.top})`);

var effectLayer = g.append('g')
.classed('effect-layer', true);

var mapLayer = g.append('g')
.classed('map-layer', true);

var width = 960,
height = 500,
centered;

var BASE_FONT = "'Helvetica Neue', Helvetica, Arial, sans-serif";

var FONTS = [
"Open Sans",
"Josefin Slab",
"Arvo",
];

var dummyText = g.append('text')
.classed('dummy-text', true)
.attr('x', 10)
.attr('y', 30)
.style('opacity', 0);

var bigText = g.append('text')
.classed('big-text', true)
.attr('x', 20)
.attr('y', 45)
.attr('font-size', 24);
// draw map

function textArt(text){
// Use random font
var fontIndex = Math.round(Math.random() * FONTS.length);
var fontFamily = FONTS[fontIndex] + ', ' + BASE_FONT;

bigText
.style('font-family', fontFamily)
.text(text);

// Use dummy text to compute actual width of the text
// getBBox() will return bounding box
dummyText
.style('font-family', fontFamily)
.text(text);
var bbox = dummyText.node().getBBox();

var textWidth = bbox.width;
var textHeight = bbox.height;
var xGap = 3;
var yGap = 1;

// Generate the positions of the text in the background
var xPtr = 0;
var yPtr = 0;
var positions = [];
var rowCount = 0;
while(yPtr < height){
while(xPtr < width){
var point = {
text: text,
index: positions.length,
x: xPtr,
y: yPtr
};
var dx = point.x - width/2 + textWidth/2;
var dy = point.y - height/2;
point.distance = dx*dx + dy*dy;

positions.push(point);
xPtr += textWidth + xGap;
}
rowCount++;
xPtr = rowCount%2===0 ? 0 : -textWidth/2;
xPtr += Math.random() * 10;
yPtr += textHeight + yGap;
}

var selection = effectLayer.selectAll('text')
.data(positions, function(d){return d.text+'/'+d.index;});

// Clear old ones
selection.exit().transition()
.style('opacity', 0)
.remove();

// Create text but set opacity to 0
const textEnter = selection.enter().append('text')
.text(function(d){return d.text;})
.attr('x', function(d){return d.x;})
.attr('y', function(d){return d.y;})
.style('font-family', fontFamily)
.style('fill', '#777')
.style('opacity', 0);

selection.merge(textEnter)
.style('font-family', fontFamily)
.attr('x', function(d){return d.x;})
.attr('y', function(d){return d.y;});
}

function fillFn(d){
return color(getGreen(d));
}

function nameFn(d){
return d && d.properties ? d.properties.UC+": "+ parseFloat(d.properties.sourcePOP).toFixed(2) : "NA";
}

// Get province name length
function nameLength(d){
var n = nameFn(d);
return n ? n.length : 0;
}

// When clicked, zoom in
function clicked(d) {
var x, y, k;

// Compute centroid of the selected path
if (d && centered !== d) {
var centroid = path1.centroid(d);
x = centroid[0];
y = centroid[1];
k = 4;
centered = d;
} else {
x = width / 2;
y = height / 2;
k = 1;
centered = null;
}

// Highlight the clicked province
mapLayer.selectAll('path')
.style('fill', function(d){return centered && d===centered ? '#D5708B' : fillFn(d);});

// Zoom
g.transition()
.duration(750)
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')scale(' + k + ')translate(' + -x + ',' + -y + ')');
}

function mouseover(d){
// // Highlight hovered province
// d3.select(this).style('fill', 'orange');

// Draw effects
textArt(nameFn(d));
}

function mouseout(d){
// Reset province color
// g.selectAll('path')
// .style('fill', function(d){return centered && d===centered ? '#D5708B' : fillFn(d);});

// Remove effect text
effectLayer.selectAll('text').transition()
.style('opacity', 0)
.remove();

// Clear province name
bigText.text('');
}

var features = karachi.features;

mapLayer.selectAll("path")
.data(features)
.enter().append('path')
.attr('d', path1)
.attr("stroke", "white")
.attr("fill", d => color(getGreen(d)))
// Styling
.on('mouseover', mouseover)
.on('mouseout', mouseout)
.on('click', clicked);

return svg.node();
}
Insert cell
d3 = require("d3@5")
Insert cell
karachi.features
Insert cell
karachi
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