Fil / d3-geo-voronoi

Voronoi / Delaunay tessellations on the sphere
ISC License
229 stars 24 forks source link

polygons() not working #4

Closed nunojpg closed 7 years ago

nunojpg commented 7 years ago

Consider:

        let points = {
            "type": "FeatureCollection",
            "features": [
                {
                    "type": "Feature",
                    "id": "north",
                    "properties": {},
                    "geometry": {
                        "type": "Point",
                        "coordinates": [
                            0,
                            20
                        ]
                    }
                },
                {
                    "type": "Feature",
                    "id": "south",
                    "properties": {},
                    "geometry": {
                        "type": "Point",
                        "coordinates": [
                            0,
                            -20
                        ]
                    }
                }
            ]
        }

        let voronoi = d3.geoVoronoi()(points).polygons()

voronoi is now:

{
    "type": "FeatureCollection",
    "features": [
        {
            "type": "Feature",
            "geometry": {
                "type": "Polygon",
                "coordinates": [
                    [
                        [
                            0,
                            0
                        ],
                        [
                            -90,
                            0
                        ],
                        [
                            -180,
                            0
                        ],
                        [
                            90,
                            0
                        ],
                        [
                            0,
                            0
                        ]
                    ]
                ]
            },
            "properties": {
                "site": {
                    "type": "Feature",
                    "id": "north",
                    "properties": {},
                    "geometry": {
                        "type": "Point",
                        "coordinates": [
                            0,
                            20
                        ]
                    },
                    "index": 0
                },
                "sitecoordinates": [
                    0,
                    20
                ],
                "neighbours": [
                    1
                ]
            }
        },
        {
            "type": "Feature",
            "geometry": {
                "type": "Polygon",
                "coordinates": [
                    [
                        [
                            0,
                            0
                        ],
                        [
                            90,
                            0
                        ],
                        [
                            -180,
                            0
                        ],
                        [
                            -90,
                            0
                        ],
                        [
                            0,
                            0
                        ]
                    ]
                ]
            },
            "properties": {
                "site": {
                    "type": "Feature",
                    "id": "south",
                    "properties": {},
                    "geometry": {
                        "type": "Point",
                        "coordinates": [
                            0,
                            -20
                        ]
                    },
                    "index": 1
                },
                "sitecoordinates": [
                    0,
                    -20
                ],
                "neighbours": [
                    0
                ]
            }
        }
    ]
}

As both input points were symmetric to the equator, the voronoi polygons are the north and south hemisphere, but geoVoronoi outputs zero area polygons (lines).

This can be plotted/linted at http://geojsonlint.com/.

Fil commented 7 years ago

Well if I log d3.geoArea(voronoi.features[0]) I get 2π.

The polygons are well-defined and if you look at the coordinates, you can see that they follow the left-hand rule (ahem!). But this is the rule of d3-geo!

Quoting README:

the exterior ring for polygons smaller than a hemisphere must be clockwise, while the exterior ring for polygons larger than a hemisphere must be anticlockwise. (…) This winding order convention is also used by TopoJSON and ESRI shapefiles; however, it is the opposite convention of GeoJSON’s RFC 7946.

I made a block to demonstrate how everything is displayed correctly, and geoArea and geoContains work (see the console).

https://bl.ocks.org/Fil/40e62f5cfb2627fd9ef3c1a8568a1e83

nunojpg commented 7 years ago

Got it that d3 uses the opposite winding order as the RFC.

BUT, there is something beside that, and I can't understand how geoArea gives you any area, as all the Polygon coordinates are on the equator. All the latitudes are 0...

nunojpg commented 7 years ago

Also, your example seems to fail as soon as a third point is added (I tried 0,0). All features coordinates become NaN.

Fil commented 7 years ago

All the latitudes are 0

that's precisely the boundary of the polygon, which defines the north (respectively south) hemisphere, given the winding order.

example seems to fail with (0,0)

both planar and geo voronoi diagrams implementations have issues with precisely aligned points. Try adding a little bit of randomness in the coordinates, and it works. (Maybe that's an error that could be caught or avoided.)

nunojpg commented 7 years ago

A solution for my problem (not a issue with geo-voronoi) was provided at: https://github.com/d3/d3-geo/issues/104

nunojpg commented 7 years ago

@Fil I made a little demo on Leaflet, with polygons() and find(). Works perfectly and the code is very simple because there is no need to do any conversion to svg/canvas overlay pixels.

http://bl.ocks.org/nunojpg/cc204e57cc6c57dcc1f1ba311393e1f1

(click to activate the find() functionality)