d3-geo icon indicating copy to clipboard operation
d3-geo copied to clipboard

Projection.stream only supports points with x and y (no z)

Open ekh64 opened this issue 7 years ago • 4 comments

I was working to implement your Map Pan & Zoom IV example without baking in the projection to the json file.

As a result I've been playing around with projection.stream, and discovered that, unlike geoTransform, when I pass in a stream, the point function only receives x and y params, whereas the geoTransform receives x, y, and z params.

So when I write the following:

const geoTransform({
  point: function(x, y, z) {
    if (z >= area) this.stream.point(x * scale + translate[0], y * scale + translate[1]);
  }
});
geoPath().projection({ stream: s => projection.stream(simplify.stream(s)) })

my projection doesn't render because z is undefined in the point function. Alternatively reversing the order the streams are called has the same result. But either stream called individually works as expected (simplify.stream calls the point function with three params and projection.stream(s) renders my map, but without dynamic simplification.

ekh64 avatar Apr 11 '17 21:04 ekh64

Hmm. We’d need to change all the parts of the projection pipeline to pass along z (transformRadians, preclip, projectResample, postclip), which is a bit of a pain, especially since it presumably adds a small amount of overhead to the common case where z is not needed.

Another option would be to call d3.geoProject on load instead of using projection.stream; then you save your projected GeoJSON and you don’t need to reproject on pan & zoom; you just apply your transform and dynamic simplification. And you don’t lose z.

If you’re using topojson.presimplify to compute z, it takes a topology as input and after d3.geoProject you have individual GeoJSON features. So you’d also need to call topojson.topology to construct a new topology after projecting.

I’m not sure if there’s a better way to do it at the moment.

mbostock avatar Apr 11 '17 21:04 mbostock

Thanks for the advice, I'll try out those suggestions. Really appreciate the fast response.

ekh64 avatar Apr 12 '17 14:04 ekh64

I'm wondering where the best place is to highlight this in the docs? I spent a while scratching my head over this before browsing the issues and finding this. Perhaps under the docs for projection.stream()?

projection.stream(stream) <>

Returns a projection stream for the specified output stream. Any input geometry is projected before being streamed to the output stream. A typical projection involves several geometry transformations: the input geometry is first converted to radians, rotated on three axes, clipped to the small circle or cut along the antimeridian, and lastly projected to the plane with adaptive resampling, scale and translation. a projection stream, unlike a transform stream, only accepts 2D [x, y] coordinates, not 3D [x, y, z] coordinates

gmaclennan avatar May 31 '17 02:05 gmaclennan

More precisely, the geometry stream implemented by projection.stream accepts an arbitrary number of dimensions as input, but only observes and outputs the first two dimensions (x and y). You could implement a geometry stream that transforms more than two dimensions, meaning that is supported by the stream interface, but it’s not currently supported by d3.geoProjection implementation.

mbostock avatar May 31 '17 14:05 mbostock