d3 / d3-geo

Geographic projections, spherical shapes and spherical trigonometry.
https://d3js.org/d3-geo
Other
1.03k stars 160 forks source link

Convenience “unstitch” for getting back WGS84? #113

Open mbostock opened 7 years ago

mbostock commented 7 years ago

Currently to convert D3 and TopoJSON spherical coordinates to equirectangular WGS84 used by standard GeoJSON, you need to project through d3.geoEquirectangular with a scale of 180 / π, and then through d3.geoIdentity to flip the y-axis. For example:

topo2geo countries=- -i world/110m.json \
  | geoproject 'd3.geoEquirectangular().scale(180 / Math.PI).translate([0, 0])' \
  | geoproject 'd3.geoIdentity().reflectY(true)' \
  > countries.json

It’d be nice if there were a single built-in projection, say d3.geoWgs84, that did this in one go.

Fil commented 1 year ago

geoproject is not really suitable for this, unless we have a way to opt out of its handling of the polygons' winding order (e.g. https://github.com/d3/d3-geo-projection/pull/185).

It might be interesting to think about this together with the rewind utility https://observablehq.com/@fil/rewind (#138).

My current script for this task is a bit complicated, since it applies geoproject (to apply clipAntemeridian), then fixes the shortcomings:

function unstitch(a) {
  a = JSON.parse(JSON.stringify(a)); // deep copy
  a = d3.geoProject(
    a,
    d3
      .geoEquirectangular()
      .scale(180 / Math.PI)
      .translate([0, 0])
  );

  // flip polygons
  for (const f of a.features) {
    if (f.geometry.type === "Polygon")
      f.geometry.coordinates.forEach((ring) =>
        ring.forEach((point) => (point[1] *= -1))
      );
    else if (f.geometry.type === "MultiPolygon")
      f.geometry.coordinates.forEach((poly) =>
        poly.forEach((ring) => ring.forEach((point) => (point[1] *= -1)))
      );
  }

  // fix winding order (for Fiji in 50m)
  return rewind(a);
}