Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add reference to GeoJSON RFC7946 #79

Merged
merged 1 commit into from
Feb 28, 2017
Merged

add reference to GeoJSON RFC7946 #79

merged 1 commit into from
Feb 28, 2017

Conversation

tyrasd
Copy link
Contributor

@tyrasd tyrasd commented Feb 6, 2017

[Readme-only]

Mention the difference in handling of polygon winding order between d3 and GeoJSON RFC 7946. Also, change one "Polygon" code-example to have the "expected" RFC7946-incompatible clockwise winding.

mention the different handling of polygon winding orders between d3 and rfc7946. Also, change one "Polygon" code-example to have the expected non-RFC7946 clockwise winding.
@Fil
Copy link
Member

Fil commented Feb 18, 2017

LGTM.

@mbostock
Copy link
Member

It’s more complicated that this, unfortunately.

In the common case where WGS84 is used (the GeoJSON recommended coordinate system), D3 and GeoJSON use the same winding order. The only difference is that D3 assumes geodesic segments between points in LineStrings and rings, whereas GeoJSON assumes planar line segments in equirectangular coordinates. (The use of geodesics is necessary for rotational invariance and antimeridian cutting.)

However, when planar coordinates are used in D3, a difference in winding order arises because D3 uses the orientation preferred by 2D graphics systems (Canvas and SVG) with positive-y pointing down and positive-x pointing right. In contrast, WGS84 has positive-y pointing up because y represents latitude in decimal degrees.

@tyrasd
Copy link
Contributor Author

tyrasd commented Feb 18, 2017

In the common case where WGS84 is used (the GeoJSON recommended coordinate system), D3 and GeoJSON use the same winding order.

Sorry, I'm confused. I did try to enter a valid RFC7946 GeoJSON polygon (in WGS84 coordinates) into d3.geoBounds, and got a global spanning bbox as a result. How can that be if D3 assumes the same winding order as RFC7946 GeoJSON?

Also, GeoJSON assumes right-hand-rule winding which is counterclockwise winding and the readme here states that d3 assumes clockwise winding (note that the also mentioned postgis function called ForceRHR function does actually produce the opposite of the usual right hand rule definition).

@mbostock
Copy link
Member

mbostock commented Feb 18, 2017

There could be a bug in d3.geoBounds. Or it could be an issue of geodesics, such as the need to subdivide long segments (ST_Segmentize). If you want to report a bug, please include sufficient information to reproduce it.

But in any case if D3 and GeoJSON used opposite winding orders in the common case then you wouldn’t be able to display standard WGS84 GeoJSON, and that’s demonstrably false…

The issue with the README is that it’s clockwise in one coordinate system and counterclockwise in another coordinate system because y is flipped.

@mbostock
Copy link
Member

Here’s a quick summary of how d3-geo handles winding.

For spherical polygons, D3 uses PostGIS’s definition of the right-hand rule, which “means that the area that is bounded by the polygon is to the right of the boundary. In particular, the exterior ring is orientated in a clockwise direction and the interior rings in a counter-clockwise direction.” So the ring [[0,0],[0,10],[10,10],[10,0],[0,0]] is treated as a small polygon:

screen shot 2017-02-19 at 9 35 16 am

While the ring [[0,0],[10,0],[10,10],[0,10],[0,0]] is treated as a large polygon:

screen shot 2017-02-19 at 9 35 01 am

For planar polygons, D3 is mostly agnostic to winding order. When rendering, you can set the desired fill rule (SVG’s fill-rule property, or Canvas’s fillRule argument). When computing feature area, D3 assumes that holes have opposite winding order to exterior rings, but the result is the same whether the exterior ring is clockwise or anticlockwise.

screen shot 2017-02-19 at 9 42 58 am

screen shot 2017-02-19 at 9 43 10 am

I’m disappointed that RFC 7946 standardizes the opposite winding order to D3, Shapefiles and PostGIS. And I don’t see an easy way for D3 to change its behavior, since it would break all existing (spherical) GeoJSON used by D3. I’d be interested to see an empirical analysis of GeoJSON use in practice to see what winding order is commonly used.

@tyrasd
Copy link
Contributor Author

tyrasd commented Feb 20, 2017

I’m disappointed that RFC 7946 standardizes the opposite winding order to D3, Shapefiles and PostGIS.

IMHO, it's nothing to be disappointed about. The situation is a mess, but it was a mess even before GeoJSON was around: While Postgis and Shapefiles use clockwise winding order, GeoJSON opted to use the more common mathematical right-hand-rule (counter-clockwise) definition of surfaces (as used by OGC, KML, OpenGL, etc.). There's an interesting discussion in tilemill-project/tilemill#2110 about this, here are a couple of related comments:

@tyrasd
Copy link
Contributor Author

tyrasd commented Feb 20, 2017

For planar polygons, D3 is mostly agnostic to winding order.

But this repository (d3-geo) is mainly (?) about spherical polygons, right?

For spherical polygons, D3 uses PostGIS’s definition of the right-hand rule, which “means that the area that is bounded by the polygon is to the right of the boundary.

For clarity, it would make sense to not call this definition a right-hand-rule because it is orthogonal to the commonly used right-hand-rule in mathematical geometry (and other natural sciences such as physics): https://en.wikipedia.org/wiki/Right-hand_rule (https://xkcd.com/199/)

As stated above, this is the opposite of what GeoJSON RFC7946 has defined, so it would be nice to have that in this repository's readme: https://github.com/d3/d3-geo/pull/79/files#diff-04c6e90faac2675aa89e2176d2eec7d8R15

In particular, the exterior ring is orientated in a clockwise direction and the interior rings in a counter-clockwise direction.” So the ring [[0,0],[0,10],[10,10],[10,0],[0,0]] is treated as a small polygon:

OK, that's why I proposed to reflect this in https://github.com/d3/d3-geo/pull/79/files#diff-04c6e90faac2675aa89e2176d2eec7d8R538

@mbostock
Copy link
Member

mbostock commented Feb 20, 2017

But this repository (d3-geo) is mainly (?) about spherical polygons, right?

Depends on what you mean. This library is “mainly” about the projection of spherical geometry to planar geometry. Yet the recommended practice for most maps is to precompute the projected planar geometry on the server for faster rendering on the client. (The primary reasons to not precompute the geometry are if you have a dynamic projection in the client, which is rare, or for development expedience.) So the API must handle both types of geometry.

As for the ring example in the README, both cases are valid. Before your change, the example represents a typical planar polygon; after it represents a typical (small) spherical polygon.

I’m familiar with the mathematical right-hand rule, but it’s not obvious how that rule applies to polygon winding order; the common application of that rule is the relative orientation of x, y and z, and these polygons are two-dimensional. @sgilliesexplanation is helpful, so I think if we want to define the winding order explicitly we should include his explanation, or avoid reference to the right-hand rule entirely.

Related, it’s not sufficient to say that the exterior ring should be clockwise (or anticlockwise) because D3 uses spherical rather than planar equirectangular coordinates. Better to clarify that exterior ring for polygons smaller than a hemisphere should be clockwise; polygons larger than a hemisphere will use the opposite winding order. In other words both winding orders are valid but have opposite meaning.

My disappointment is that I was unaware of the standardization process and that this decision makes it much harder for D3 to consume “standard” GeoJSON. We could switch D3’s winding order convention, but this would be extremely disruptive to D3’s users: it would break all current usage of spherical GeoJSON in D3!

It is already the case that D3 must define a variant of GeoJSON because the GeoJSON standard codifies WGS84 input as equirectangular planar geometry, which precludes meaningfully apply spherical rotation; yet in practice this difference can be largely ignored.

@mbostock mbostock merged commit 586c231 into d3:master Feb 28, 2017
@mbostock
Copy link
Member

Merged with edits. Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants