Public
Edited
Nov 29, 2023
Fork of D3 U.S. map
Insert cell
Insert cell
Insert cell
createVisual()
Insert cell
async function createVisual() {
let svg = htl.html`<svg viewBox="0 0 975 610">
<g fill="none" stroke="#000" stroke-linejoin="round" stroke-linecap="round">
<path stroke-width="0.5" d="${path(states)}"></path>
<path d="${path(nation)}"></path>
</g>
</svg>`;
let visual = d3.select(svg);
plotParks(visual);
createCircle(visual);
document.getElementById('circleSlider').value = '150'; // reset slider
return visual.node();
}
Insert cell
path = d3Geo.geoPath()
Insert cell
states = topojson.mesh(us, us.objects.states, (a, b) => a !== b)
Insert cell
nation = topojson.feature(us, us.objects.nation)
Insert cell
us = FileAttachment("counties-albers-10m.json").json() // Already projected to Albers-USA!
Insert cell
d3Geo = require("d3-geo@3")
Insert cell
d3 = require('d3@7');
Insert cell
Insert cell
projection = d3Geo.geoAlbersUsa().scale(1300).translate([487.5, 305])
Insert cell
// Custom projection function
function customProjection(coordinates) {
const scale = 1300;
const translateX = 487.5;
const translateY = 305;

// Apply scaling and translation
const projectedX = coordinates[0] * scale + translateX;
const projectedY = coordinates[1] * scale + translateY;

return [projectedX, projectedY];
}
Insert cell
parkData = FileAttachment("parklocations.csv").csv().then(data => {
return data.map(d => ({
park: d.PARK,
latitude: +d.LATITUDE,
longitude: +d.LONGITUDE,
state: d.STATE
}));
});
Insert cell
function plotParks(visual) {
visual.selectAll('.parks')
.data(parkData, d => d.park)
.join('circle')
.attr('r', 2)
.attr('class', 'parks')
.style('fill', 'gray')
.attr('cx', (d, i) => (i + 1) * 5)
/*d => {
var projectedPoint = customProjection([d.longitude, d.latitude]);
return projectedPoint[0];
}) */
.attr('cy', (d, i) => (i + 1) * 5)
/* d => {
var projectedPoint = customProjection([d.longitude, d.latitude]);
return projectedPoint[1];
}) */

// mouseover events

.on('mouseover', function (event, d) {
d3.select(this).style('fill', 'lightpink');
visual.append('text')
.attr('class', 'parkLabel')
.attr('x', 500)
.attr('y', 500)
.text(d.park)
})
.on('mouseout', function(event, d) {
visual.selectAll('.parkLabel').remove()
d3.select(this).style('fill', 'gray')
})
};
Insert cell
function createCircle(visual) {

// make the big circle
const circle1 = visual.append('circle')
.join('circle')
.attr('cx', 400)
.attr('cy', 250)
.attr('r', 150)
.style('fill', 'lightgreen')
.attr('opacity', 0.5)
.attr('id', 'circle1')
.attr('class', 'draggable');

// allow dragging

let isDragging = false;

visual.selectAll('.draggable').call(d3.drag()
.on('start', function() {
isDragging = true;
d3.select(this).attr('stroke', 'orange') // change stroke to orange when dragging
d3.select(this).attr('stroke-width', '3');
})
.on('drag', function(event) {
if (isDragging) {
d3.select(this)
.attr('cx', event.x)
.attr('cy', event.y);
}
})
.on('end', function() {
isDragging = false;
d3.select(this).attr('stroke', null);
}));

// resizing

d3.select('#circleSlider')
.on('input', function() {
let newRadius = this.value; // get the value of the slider
d3.select('#circle1')
.attr('r', newRadius); // update radius
});
}
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