Fil/d3-geo-voronoi

voronoi.polygons([data]) outputs non-compliant GeoJSON

nunojpg opened this issue · 9 comments

Hi!

The current output of polygons looks like this:

{  
   "type":"FeatureCollection",
   "features":[  
      {  
         "type":"Polygon",
         "coordinates": ...
         "properties":...
      },...

According to http://geojson.org/:

Geometric objects with additional properties are Feature objects.

So to make it compliant it should be instead like:

{  
   "type":"FeatureCollection",
   "features":[  
      {  
         "type":"Feature",
         "geometry":{  
            "type":"Polygon",
            "coordinates":...
         },
         "properties":...
      },...
...

Thanks!

Fil commented

Excellent remark. Would you mind sending a PR?

I also had to fix the others, now with google maps all this works without any change:

            map.data.addGeoJson(voronoi.links())
            map.data.addGeoJson(voronoi.polygons())
            map.data.addGeoJson(voronoi.hull())
            map.data.addGeoJson(voronoi.triangles())

Unfortunately polygons is still not working correctly / not compliant because it is not respecting the right-hand rule.

You can validate the output for example at: http://geojsonlint.com/

This is a problem for the basic case where you have 2 points, so both polygons will have the same coordinates, but they enclose opposite globe areas.

I think you should take a look at it, because fixing it will probably break other (non-conformant) applications.

As per RFC 7946:

3.1.6. Polygon

To specify a constraint specific to Polygons, it is useful to
introduce the concept of a linear ring:

o A linear ring is a closed LineString with four or more positions.

o The first and last positions are equivalent, and they MUST contain
identical values; their representation SHOULD also be identical.

o A linear ring is the boundary of a surface or the boundary of a
hole in a surface.

o A linear ring MUST follow the right-hand rule with respect to the
area it bounds, i.e., exterior rings are counterclockwise, and
holes are clockwise.

Also mind that Sphere is not valid type (geometry.type = "Sphere";).

And the bug I'm seeing might be also due to:

3.1.9. Antimeridian Cutting

In representing Features that cross the antimeridian,
interoperability is improved by modifying their geometry. Any
geometry that crosses the antimeridian SHOULD be represented by
cutting it in two such that neither part's representation crosses the
antimeridian.

For example, a line extending from 45 degrees N, 170 degrees E across
the antimeridian to 45 degrees N, 170 degrees W should be cut in two
and represented as a MultiLineString.

It seems that part of this issue, or all of it, might be to a non-compliante on Google Maps. I just filled the bug with the team:

https://issuetracker.google.com/issues/63581224

Fil commented

Yes the {type: "Sphere"} representation is specific to d3-geo (see https://gis.stackexchange.com/questions/42681/how-do-i-represent-the-whole-earth-as-a-polygon).

I don't think d3-geo-voronoi should cut shapes across the antimeridian — maybe add a clipper in your application when it's needed. It's one of those infuriating rules :)

As for the coding style, let's try and stick to d3's style as much as possible.

I think Sphere was used because of the lack of the right hand rule, which didn't exist initially on GeoJSON. But for me I don't care for the Sphere or antimeridian cutting case, but following the right hand rule is quite important, as otherwise the polygons created are ambiguous (and they fail GeoJSON linting).

Fil commented

I'm not sure if your comment about the right-hand rule implies that there is still a bug? I think we're good, but I might be wrong :) If that's the case please open an issue.

Yes. The output json still fails GeoJSON linting due to violating the right hand rule. I'll open a issue with tests or examples later today, since fixing it might be over my league.