paulmach / orb

Types and utilities for working with 2d geometry in Golang
MIT License
886 stars 103 forks source link

Point in Polygon is not working as intended #150

Open guppykang opened 3 months ago

guppykang commented 3 months ago

i have a feature collection in the form of a geojson. i used vscode's geojson visualizer and it shows me exactly what i'm expecting as seen below.

Screenshot 2024-05-28 at 7 22 28 PM

i test the following cases to see if my code is working correctly. all features are polygons and multi polygons.

MontanaCoordinates = ValidateLocationParams{
        Latitude:  46.8797,
        Longitude: -110.3626,
    } // should not work

    SanDiegoCoordinates = ValidateLocationParams{
        Latitude:  32.7157,
        Longitude: 117.1611,
    } // should work

    NewYorkCoordinates = ValidateLocationParams{
        Latitude:  40.7128,
        Longitude: 74.0060,
    } // should work

    TorontoCoordinates = ValidateLocationParams{
        Latitude:  43.65107,
        Longitude: 79.347015,
    } // should not work

    LondonCoordinates = ValidateLocationParams{
        Latitude:  51.5074,
        Longitude: 0.1278,
    } // should work

    IrelandCoordinates = ValidateLocationParams{
        Latitude:  53.1424,
        Longitude: 7.6921,
    } // should not work

    AfricaCoordinates = ValidateLocationParams{
        Latitude:  8.7832,
        Longitude: 34.5085,
    } // should not work

    HawaiiCoordinates = ValidateLocationParams{
        Latitude:  19.8968,
        Longitude: 155.5828,
    } // should work

the code below is what i use to see if a single coordinate is inside any of the polygons. it gets all the coordinates it should not work for correctly, but it fails to verify correctly the coordinates that should be working. my geojson looks something like this:

{
    "type": "FeatureCollection",
    "features": [
        {
            "type": "Feature",
            "properties": {
                "GEO_ID": "0400000US25",
                "STATE": "25",
                "NAME": "Massachusetts",
                "LSAD": "",
                "CENSUSAREA": 7800.058
            },
            "geometry": {
                "type": "MultiPolygon",
                "coordinates": [XXX],
             },
       },
      ...
      ...
func (s *service) ValidateDepositLocation(params ValidateLocationParams) error {
    if DepositRegions == nil {
        collection, err := s.readGeoJSONFile("operatingRegions.geojson")
        if err != nil {
            ctx.Errorf("Failed to process operatingRegions.geojson: %v", err)
            return err
        }
        DepositRegions = collection
    }

    if s.isInCollection(ctx, params.Latitude, params.Longitude, DepositRegions) {
        return nil
    }
    return ErrLocationNotValid
}

func (s *service) readGeoJSONFile(filename string) (*orb.Collection, error) {
    file, err := os.ReadFile(filename)
    if err != nil {
        return nil, fmt.Errorf("failed to read %s: %v", filename, err)
    }

    collection := orb.Collection{}
    fc, err := geojson.UnmarshalFeatureCollection(file)
    if err != nil {
        return nil, fmt.Errorf("failed to unmarshal %s: %v", filename, err)
    }

    for _, feature := range fc.Features {
        if feature.Geometry != nil {
            collection = append(collection, feature.Geometry)
        } else {
            return nil, fmt.Errorf("feature has no geometry %v", feature.ID)
        }
    }

    return &collection, nil
}

func (s *service) isInCollection(lat float64, lon float64, collection *orb.Collection) bool {
    if collection == nil {
        ctx.Error("Collection is nil")
        return false
    }

    point := orb.Point{lon, lat}

    for _, g := range *collection {
        switch geo := g.(type) {
        case orb.Polygon:
            if planar.PolygonContains(geo, point) {
                return true
            }
        case orb.MultiPolygon:
            if planar.MultiPolygonContains(geo, point) {
                return true
            }
        default:
            ctx.Errorf("Unsupported geometry type: %v", g.GeoJSONType())
        }
    }

    return false
}