tidwall / geojson

GeoJSON for Go. Used by Tile38
MIT License
130 stars 28 forks source link

Transmeridian polygons fail on ContainsPoint #21

Open timcooijmans opened 1 year ago

timcooijmans commented 1 year ago

Hi,

I'm using your library to calculate if a point is within the polygon defined by a Uber H3 res0. Example code:

package main

import (
    "fmt"
    "github.com/tidwall/geojson/geometry"
    "github.com/uber/h3-go/v4"
)

func main() {

    res0 := h3.Cell(h3.IndexFromString("8003fffffffffff"))
    boundary := make([]geometry.Point, 0)
    fmt.Println("boundary: ")
    for _, point := range res0.Boundary() {
        point := geometry.Point{Y: point.Lat, X: point.Lng}
        boundary = append(boundary, point)
        fmt.Println(point)
    }

    poly := geometry.NewPoly(boundary, nil, nil)

    fmt.Println("point:")
    point := geometry.Point{Y: res0.LatLng().Lat, X: res0.LatLng().Lng}

    fmt.Println("poly contains point")
    fmt.Println(poly.ContainsPoint(point))

}

Some H3Cells are transmeridian such as this example. ContainsPoint return false while actually the point is the centerpoint (res0.LatLng()) of the polygon. I tried wrapping the longitudes to a 0 - 360 degrees system, but this also doesn't work because the point isn't valid anymore.

tidwall commented 1 year ago

Something looks off about the polygon that h3 is generating.

When I run your code I get these points:

{145.5581976913369 87.36469532319619}
{-163.45686807900947 76.14556732608257}
{-131.70883908792965 69.37134141076518}
{-100.82187020582201 67.53592431503803}
{-66.90449925088507 72.20470505499345}
{-34.758417980284634 81.27137179020501}

A valid polygon is usually closed, where the first and last point are the same. Also the 145.5581976913369 to -163.45686807900947 are pretty far apart.

I made some minor changes in order to output a (valid'ish) geojson document.

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "geometry": {
        "type": "Polygon",
        "coordinates": [
          [
            [145.5581976913369, 87.36469532319619],
            [-163.45686807900947, 76.14556732608257],
            [-131.70883908792965, 69.37134141076518],
            [-100.82187020582201, 67.53592431503803],
            [-66.90449925088507, 72.20470505499345],
            [-34.758417980284634, 81.27137179020501],
            [145.5581976913369, 87.36469532319619]
          ]
        ]
      },
      "properties": {}
    },
    {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [-107.42920224303745, 79.220986356276]
      },
      "properties": {}
    }
  ]
}

In the output above, the Point is outside of the generated polygon.

Some H3Cells are transmeridian such as this example. ContainsPoint return false while actually the point is the centerpoint (res0.LatLng()) of the polygon. I tried wrapping the longitudes to a 0 - 360 degrees system, but this also doesn't work because the point isn't valid anymore.

This library generally follows GeoJSON spec where geometries should stay within (-180 -90),(180, 90) boundary, and not cross the antimeridian.

If the hexagonal polygon overlaps the antimeridian or the poles then it should be cut up into multiple polygons to conform to the GeoJSON spec, and this library.

https://www.rfc-editor.org/rfc/rfc7946#section-5.3 https://www.rfc-editor.org/rfc/rfc7946#section-3.1.9

timcooijmans commented 1 year ago

Thank you for the extensive reply! Let's take this back to the H3 community, because this indeed seems off. Sorry I should have drawn the polygon by myself before asking.