Public
Edited
Oct 26, 2023
16 forks
Importers
257 stars
Topographic MappingBubble mapChoroplethAccess to Family planningMD Counties Total Cases MapTendance de la production des déchets en Union EuropéenneAndy's Walgreens COVID-19 Tracker TrackerElection Maps for Incomplete ResultsA better U.S. house election results map?1983 Mayoral Election, Dot density mapsMastodon 🐘Cheat sheet bertinBertin.js: regular squaresWaterlinesNeumorphism Contour Density MapCartographic DoodlesStars and constellationsPlot: Grid choroplethHello Polygon MorphingMapping with pie chartsU.S. Geographic DataHow big are countries... like really!AttitudeB&W ChoroplethWeb Mercator Tile Visibility
MARTINI: Real-Time RTIN Terrain Mesh
"Magnifying-Glass" projectionsTissot's indicatrixAntipodal mapMapping gridded data with a Voronoi diagramA Map of Every BuildingUrbano Monti’s Planisphere (1587)Bivariate choroplethDIY HillshadeMapbox Map MakerWorld tourHillshaderSimplified Earth with curved shapesHexbin mapInner glowNicolosi vs. StereographicData-driven projections: Darwin's worldSatellite ground track visualizerDirection to shoreHello, OpenLayers!U.S. airports VoronoiHello, NYC Geosearch API!Mapbox Fly-ToSpilhaus shoreline mapWalmart’s growthHow well does population density predict U.S. voting outcomes?Drawing maps from geodata with D3 & ObservableHexgrid maps with d3-hexgridTissot's indicatrixWorld airports VoronoiSwiss Elevation Line GraphsVector tilesVersor draggingOrthographicSolar TerminatorStreaming ShapefilesFake GlobesPeirce Quincuncial🍃 LeafletU.S.G.S. World Earthquake MapUsing Mapbox GL JSUsing Google Maps
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
tileImg = fetchImage(`https://a.tiles.mapbox.com/v4/mapbox.terrain-rgb/${location}.png?access_token=${mapboxToken}`)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
errors = {
const errors = new Float32Array(gridSize * gridSize);

const numSmallestTriangles = tileSize * tileSize;
const numTriangles = numSmallestTriangles * 2 - 2; // 2 + 4 + 8 + ... 2^k = 2 * 2^k - 2
const lastLevelIndex = numTriangles - numSmallestTriangles;
// iterate over all possible triangles, starting from the smallest level
for (let i = numTriangles - 1; i >= 0; i--) {
// get triangle coordinates from its index in an implicit binary tree
let id = i + 2;
let ax = 0, ay = 0, bx = 0, by = 0, cx = 0, cy = 0;
if (id & 1) {
bx = by = cx = tileSize; // bottom-left triangle
} else {
ax = ay = cy = tileSize; // top-right triangle
}
while ((id >>= 1) > 1) {
const mx = (ax + bx) >> 1;
const my = (ay + by) >> 1;

if (id & 1) { // left half
bx = ax; by = ay;
ax = cx; ay = cy;
} else { // right half
ax = bx; ay = by;
bx = cx; by = cy;
}
cx = mx; cy = my;
}

// calculate error in the middle of the long edge of the triangle
const interpolatedHeight = (terrain[ay * gridSize + ax] + terrain[by * gridSize + bx]) / 2;
const middleIndex = ((ay + by) >> 1) * gridSize + ((ax + bx) >> 1);
const middleError = Math.abs(interpolatedHeight - terrain[middleIndex]);
if (i >= lastLevelIndex) { // smallest triangles
errors[middleIndex] = middleError;

} else { // bigger triangles; accumulate error with children
const leftChildError = errors[((ay + cy) >> 1) * gridSize + ((ax + cx) >> 1)];
const rightChildError = errors[((by + cy) >> 1) * gridSize + ((bx + cx) >> 1)];
errors[middleIndex] = Math.max(errors[middleIndex], middleError, leftChildError, rightChildError);
}
}
return errors;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
updatedGeometry = {
let i = 0;
const indices = geometry.index.array;
function processTriangle(ax, ay, bx, by, cx, cy) {
// middle of the long edge
const mx = (ax + bx) >> 1;
const my = (ay + by) >> 1;

if (Math.abs(ax - cx) + Math.abs(ay - cy) > 1 && errors[my * gridSize + mx] > maxError) {
// triangle doesn't approximate the surface well enough; split it into two
processTriangle(cx, cy, ax, ay, mx, my);
processTriangle(bx, by, cx, cy, mx, my);

} else {
// add a triangle to the final mesh
indices[i++] = ay * gridSize + ax;
indices[i++] = by * gridSize + bx;
indices[i++] = cy * gridSize + cx;
}
}

processTriangle(0, 0, tileSize, tileSize, tileSize, 0);
processTriangle(tileSize, tileSize, 0, 0, 0, tileSize);
geometry.index.needsUpdate = true;
geometry.setDrawRange(0, i);
renderer.render(scene, camera);
return geometry;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
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