Observable Framework 1.7.0 GitHub️ 1.9k

Shapefile

The ESRI shapefile is a binary format for geometry. Shapefiles are often paired with dBASE table files for metadata. You can use the shapefile module to convert shapefiles to GeoJSON for use with libraries such as D3, Observable Plot, and Leaflet. To import the shapefile module:

import * as shapefile from "npm:shapefile";

Then, to read a .shp and .dbf file:

const collection = shapefile.read(
  ...(await Promise.all([
    FileAttachment("ne_110m_land/ne_110m_land.shp").stream(),
    FileAttachment("ne_110m_land/ne_110m_land.dbf").stream()
  ]))
);

(You can omit the .dbf file if you only need the geometry.)

The resulting collection is a promise to a GeoJSON FeatureCollection:

collection

To produce a map using Plot’s geo mark:

Plot.plot({
  projection: {
    type: "orthographic",
    rotate: [110, -30],
  },
  marks: [
    Plot.sphere(),
    Plot.graticule(),
    Plot.geo(collection, {fill: "currentColor"})
  ]
})

Or, streaming to a canvas:

const canvas = document.querySelector("#map-canvas");
const context = canvas.getContext("2d");
context.fillStyle = getComputedStyle(canvas).color;
context.clearRect(0, 0, canvas.width, canvas.height);

const path = d3.geoPath(d3.geoEquirectangular(), context);
const source = await shapefile.open(
  ...(await Promise.all([
    FileAttachment("ne_110m_land/ne_110m_land.shp").stream(),
    FileAttachment("ne_110m_land/ne_110m_land.dbf").stream()
  ]))
);

while (true) {
  const {done, value} = await source.read();
  if (done) break;
  context.beginPath();
  path(value);
  context.fill();
}