Published
Edited
Feb 25, 2021
4 stars
Insert cell
Insert cell
Insert cell
Insert cell
basemap = {
const tileSize = 256;
const tile = d3.tile()
.size([width, height])
.scale(projection.scale() * 2 * Math.PI)
.translate(projection([0, 0]))
const tiles = tile();
const [x0, y0] = tiles[0];
const [x1, y1] = tiles[tiles.length - 1];
const offscreenContext = DOM.context2d((x1 - x0 + 1) * tileSize, (y1 - y0 + 1) * tileSize);
for (const [x, y, image] of await Promise.all(tiles.map(([x, y, z]) => new Promise((resolve, reject) => {
const image = new Image;
image.onerror = reject;
image.onload = () => resolve(image);
image.src = url(x, y, z);
}).then(image => [x, y, image])))) {
offscreenContext.drawImage(image, (x - x0) * tileSize, (y - y0) * tileSize, tileSize, tileSize);
}
const context = DOM.context2d(width, height);
context.drawImage(
offscreenContext.canvas,
Math.round((x0 + tiles.translate[0]) * tiles.scale),
Math.round((y0 + tiles.translate[1]) * tiles.scale),
(x1 - x0 + 1) * tiles.scale,
(y1 - y0 + 1) * tiles.scale
);
return context.canvas;
}
Insert cell
{
let svg = d3.select(mapdiv).select('svg');
svg.selectAll('#my-interesting').remove()
let g = svg.append('g').attr('id', 'my-interesting')
g.selectAll('path')
.data(interesting)
.enter().append('path')
.attr('d', path)
.attr('pointer-events', 'none')
.attr('fill', 'none')
.attr('stroke', 'red')
.attr('stroke-width', 3)
return md`### Plot interesting features`
}
Insert cell
interesting
Insert cell
{
let svg = d3.select(mapdiv).select('svg');
let pts = d3.merge(linestrings.features.map(d => d.geometry.coordinates))

pts = pts.map(d => {
let projected = projection(d);
projected.lonlat = d;
return projected;
});
svg.selectAll('#my-voronoi').remove()
let g = svg.append('g').attr('id', 'my-voronoi')
let voronoi = d3.voronoi().extent([[0, 0], [width, height]]);
let diagram = voronoi(pts);
// let circles = g.selectAll('circle')
// .data(pts)
// .enter()
// .append('circle')
// .attr('pointer-events', 'none')
// .attr('fill', 'black')
// .attr('r', 1)
// .attr('transform', d => `translate(${d})`);
let polygons = g.append("g")
.attr("class", "polygons")
.selectAll("path")
.data(diagram.polygons())
.enter()
.append("path")
.attr('fill', 'transparent')
// .attr('stroke', 'steelblue')
// .attr('stroke-width', 0.1)
.attr("d", function(d) { return d ? "M" + d.join("L") + "Z" : null; })
.on('dblclick', function(d) {
end.geometry.coordinates = d.data.lonlat;
updatePath();
})
.on('click', function(d) {
start.geometry.coordinates = d.data.lonlat;
updatePath();
});
updatePath()
return md`### Voronoi diagram`
}
Insert cell
Insert cell
lengths = linestrings.features.map(d => d.geometry.coordinates.length)
Insert cell
start = { return {
type: "Feature",
geometry: {
type: "Point",
coordinates: linestrings.features[1].geometry.coordinates[0] // Last point in linestring
},
properties: {}
}}
Insert cell
end = { return {
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": linestrings.features[2].geometry.coordinates[0] // Last point in linestring
},
"properties": {}
};
}
Insert cell
Insert cell
function updatePath() {
let p = pathFinder.findPath(start, end);
let g = { type: "Feature", geometry: {type: "LineString", coordinates: p.path} };
let svg = d3.select(mapdiv).select('svg');
svg.selectAll('#my-p').remove()
svg.append('path')
.attr('id', 'my-p')
.attr('d', path(g))
.attr('pointer-events', 'none')
.attr('fill', 'none')
.attr('stroke', 'black')
.attr('stroke-width', 3)
svg.select('#my-start').remove();
svg.append('path')
.attr('id', 'my-start')
.attr('pointer-events', 'none')
.attr('d', path(start))
.attr('fill', "green")
svg.select('#my-end').remove();
svg.append('path')
.attr('id', 'my-end')
.attr('pointer-events', 'none')
.attr('d', path(end))
.attr('fill', "red")
}
Insert cell
pathFinder = new PathFinder(linestrings);
Insert cell
Insert cell
Insert cell
Insert cell
projection = d3.geoMercator()
.center([-79.0086, 34.6182]) // Lumberton: 34.6182° N, 79.0086° W
// .center([-77.0369, 38.9072]) // DC: 38.9072° N, 77.0369° W

.scale(Math.pow(2, 22) / (2 * Math.PI))
.translate([width / 2, height / 2])
Insert cell
Insert cell
path = d3.geoPath(projection).pointRadius(10);
Insert cell
Insert cell
Insert cell
Insert cell
interesting
Insert cell
import { linestrings, interesting } from '@pbogden/osm-data'
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