package geo import ( "encoding/json" "strings" "testing" ) const squarePolygon = `{ "type": "Polygon", "coordinates": [[ [-91.0, 38.0], [-90.0, 38.0], [-90.0, 39.0], [-91.0, 39.0], [-91.0, 38.0] ]] }` func TestContainsPointInsideSimplePolygon(t *testing.T) { got, err := ContainsPoint([]byte(squarePolygon), Point{Longitude: -90.5, Latitude: 38.5}) if err != nil { t.Fatalf("ContainsPoint() error = %v", err) } if !got { t.Fatalf("ContainsPoint() = false, want true") } } func TestContainsPointAcceptsRawMessage(t *testing.T) { got, err := ContainsPoint(json.RawMessage(squarePolygon), Point{Longitude: -90.5, Latitude: 38.5}) if err != nil { t.Fatalf("ContainsPoint() error = %v", err) } if !got { t.Fatalf("ContainsPoint() = false, want true") } } func TestContainsPointOutsideSimplePolygon(t *testing.T) { got, err := ContainsPoint([]byte(squarePolygon), Point{Longitude: -89.5, Latitude: 38.5}) if err != nil { t.Fatalf("ContainsPoint() error = %v", err) } if got { t.Fatalf("ContainsPoint() = true, want false") } } func TestContainsPointOnBoundary(t *testing.T) { got, err := ContainsPoint([]byte(squarePolygon), Point{Longitude: -91.0, Latitude: 38.5}) if err != nil { t.Fatalf("ContainsPoint() error = %v", err) } if !got { t.Fatalf("ContainsPoint() = false, want true") } } func TestContainsPointInHoleReturnsFalse(t *testing.T) { const polygonWithHole = `{ "type": "Polygon", "coordinates": [ [[0,0],[10,0],[10,10],[0,10],[0,0]], [[4,4],[6,4],[6,6],[4,6],[4,4]] ] }` got, err := ContainsPoint([]byte(polygonWithHole), Point{Longitude: 5, Latitude: 5}) if err != nil { t.Fatalf("ContainsPoint() error = %v", err) } if got { t.Fatalf("ContainsPoint() = true, want false") } } func TestContainsPointOnHoleBoundaryReturnsTrue(t *testing.T) { const polygonWithHole = `{ "type": "Polygon", "coordinates": [ [[0,0],[10,0],[10,10],[0,10],[0,0]], [[4,4],[6,4],[6,6],[4,6],[4,4]] ] }` got, err := ContainsPoint([]byte(polygonWithHole), Point{Longitude: 4, Latitude: 5}) if err != nil { t.Fatalf("ContainsPoint() error = %v", err) } if !got { t.Fatalf("ContainsPoint() = false, want true") } } func TestContainsPointInsideOneMultiPolygonMember(t *testing.T) { const multiPolygon = `{ "type": "MultiPolygon", "coordinates": [ [[[0,0],[1,0],[1,1],[0,1],[0,0]]], [[[10,10],[12,10],[12,12],[10,12],[10,10]]] ] }` got, err := ContainsPoint([]byte(multiPolygon), Point{Longitude: 11, Latitude: 11}) if err != nil { t.Fatalf("ContainsPoint() error = %v", err) } if !got { t.Fatalf("ContainsPoint() = false, want true") } } func TestContainsPointUsesLongitudeLatitudeOrder(t *testing.T) { const narrowPolygon = `{ "type": "Polygon", "coordinates": [[ [-91.0, 38.0], [-90.0, 38.0], [-90.0, 39.0], [-91.0, 39.0], [-91.0, 38.0] ]] }` got, err := ContainsPoint([]byte(narrowPolygon), Point{Longitude: -90.5, Latitude: 38.5}) if err != nil { t.Fatalf("ContainsPoint() error = %v", err) } if !got { t.Fatalf("ContainsPoint() = false, want true") } got, err = ContainsPoint([]byte(narrowPolygon), Point{Longitude: 38.5, Latitude: -90.5}) if err != nil { t.Fatalf("ContainsPoint() reversed error = %v", err) } if got { t.Fatalf("ContainsPoint() with reversed coordinate values = true, want false") } } func TestContainsPointUnsupportedGeometryError(t *testing.T) { _, err := ContainsPoint([]byte(`{"type":"Point","coordinates":[-90,38]}`), Point{Longitude: -90, Latitude: 38}) if err == nil { t.Fatalf("ContainsPoint() error = nil, want error") } if !strings.Contains(err.Error(), `unsupported geojson geometry type "Point"`) { t.Fatalf("ContainsPoint() error = %q", err) } } func TestContainsPointInvalidJSONError(t *testing.T) { _, err := ContainsPoint([]byte(`{"type":"Polygon"`), Point{Longitude: -90, Latitude: 38}) if err == nil { t.Fatalf("ContainsPoint() error = nil, want error") } if !strings.Contains(err.Error(), "decode geojson geometry") { t.Fatalf("ContainsPoint() error = %q", err) } } func TestContainsPointMalformedCoordinatesError(t *testing.T) { _, err := ContainsPoint([]byte(`{"type":"Polygon","coordinates":[[[1]]]}`), Point{Longitude: 1, Latitude: 1}) if err == nil { t.Fatalf("ContainsPoint() error = nil, want error") } if !strings.Contains(err.Error(), "need longitude and latitude") { t.Fatalf("ContainsPoint() error = %q", err) } } func TestContainsPointEmptyRingError(t *testing.T) { _, err := ContainsPoint([]byte(`{"type":"Polygon","coordinates":[[]]}`), Point{Longitude: 1, Latitude: 1}) if err == nil { t.Fatalf("ContainsPoint() error = nil, want error") } if !strings.Contains(err.Error(), "ring has no points") { t.Fatalf("ContainsPoint() error = %q", err) } }