diff --git a/go.mod b/go.mod index c5c282c..f0ee1ca 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,17 @@ module github.com/Willyham/hashfill -go 1.12 +go 1.20 require ( - github.com/mmcloughlin/geohash v0.9.0 - github.com/paulsmith/gogeos v0.1.2 - github.com/stretchr/testify v1.3.0 - github.com/twpayne/go-geom v1.0.5 + github.com/engelsjk/polygol v0.0.3 + github.com/mmcloughlin/geohash v0.10.0 + github.com/stretchr/testify v1.8.4 + github.com/twpayne/go-geom v1.5.3 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/engelsjk/splay-tree v0.0.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 15af9c4..fa6734f 100644 --- a/go.sum +++ b/go.sum @@ -1,40 +1,21 @@ -github.com/DATA-DOG/go-sqlmock v1.3.2/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= -github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= -github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/containerd/continuity v0.0.0-20181203112020-004b46473808/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/d4l3k/messagediff v1.2.1 h1:ZcAIMYsUg0EAp9X+tt8/enBE/Q8Yd5kzPynLyKptt9U= -github.com/d4l3k/messagediff v1.2.1/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/alecthomas/assert/v2 v2.4.0 h1:/ZiZ0NnriAWPYYO+4eOjgzNELrFQLaHNr92mHSHFj9U= +github.com/alecthomas/repr v0.3.0 h1:NeYzUPfjjlqHY4KtzgKJiWd6sVq2eNUPTi34PiFGjY8= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/mmcloughlin/geohash v0.9.0 h1:FihR004p/aE1Sju6gcVq5OLDqGcMnpBY+8moBqIsVOs= -github.com/mmcloughlin/geohash v0.9.0/go.mod h1:oNZxQo5yWJh0eMQEP/8hwQuVx9Z9tjwFUqcTB1SmG0c= -github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/ory/dockertest v3.3.4+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= -github.com/paulsmith/gogeos v0.1.2 h1:PASLPRO7sjXZLERnQ98EKqY4l9zjQW+irDD5FFRms8I= -github.com/paulsmith/gogeos v0.1.2/go.mod h1:7GN4vaVO09zFKjDPYsAoeA1j+8GuSicOlnbKo+A0AZM= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/engelsjk/polygol v0.0.3 h1:EG+yyPd/sPLNYUQ1KzlBz6OHfa0+EgAHQYLwsSgxF4k= +github.com/engelsjk/polygol v0.0.3/go.mod h1:I11mpyToT6JcjiEYf7TtRjX326/rLxSNfT5MI1W6cy4= +github.com/engelsjk/splay-tree v0.0.1 h1:9jWYhlLxSTubl8+Lgk/4mSe8iMQITwbRu+eVMkNTsUM= +github.com/engelsjk/splay-tree v0.0.1/go.mod h1:5TalkXJDy1DjM0Dj1g4WcZ5bn1Y3YwUnPYH+XWtAXFY= +github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= +github.com/mmcloughlin/geohash v0.10.0 h1:9w1HchfDfdeLc+jFEf/04D27KP7E2QmpDu52wPbJWRE= +github.com/mmcloughlin/geohash v0.10.0/go.mod h1:oNZxQo5yWJh0eMQEP/8hwQuVx9Z9tjwFUqcTB1SmG0c= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/twpayne/go-geom v1.0.5 h1:XZBfc3Wx0dj4p17ZfmzqxnU9fTTa3pY4YG5RngKsVNI= -github.com/twpayne/go-geom v1.0.5/go.mod h1:gO3i8BeAvZuihwwXcw8dIOWXebCzTmy3uvXj9dZG2RA= -github.com/twpayne/go-kml v1.0.0/go.mod h1:LlvLIQSfMqYk2O7Nx8vYAbSLv4K9rjMvLlEdUKWdjq0= -github.com/twpayne/go-polyline v1.0.0/go.mod h1:ICh24bcLYBX8CknfvNPKqoTbe+eg+MX1NPyJmSBo7pU= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/twpayne/go-geom v1.5.3 h1:UdH93XzTwpwPiAV38DJ74yg+9/YV9/WCGbKN+NmSvVA= +github.com/twpayne/go-geom v1.5.3/go.mod h1:scDv/u90MVD6K+/7cA44kQt9fD6M/n+VuLddERxWYR8= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/predicates.go b/predicates.go index 3e64f38..89487f5 100644 --- a/predicates.go +++ b/predicates.go @@ -1,8 +1,8 @@ package hashfill import ( + "github.com/engelsjk/polygol" "github.com/mmcloughlin/geohash" - "github.com/paulsmith/gogeos/geos" geom "github.com/twpayne/go-geom" ) @@ -30,58 +30,78 @@ func (f intersectsFunc) Intersects(p *geom.Polygon, hash string) (bool, error) { return f(p, hash) } -// Intersects tests if the geofence contains the hash by doing a geos intersection. +// Intersects tests if the geofence contains the hash. var Intersects = intersectsFunc(func(geofence *geom.Polygon, hash string) (bool, error) { - hashGeo := hashToGeometry(hash) - fence := polygonToGeometry(geofence) - return fence.Intersects(hashGeo) + hashGeom := hashToGeom(hash) + out, err := polygol.Intersection(hashGeom, g2p(geofence)) + if err != nil { + return false, err + } + if len(out) == 0 { + return false, nil + } else { + return true, nil + } }) -// Contains tests if the geofence contains the hash by doing a geos contains. +// Contains tests if the geofence contains the hash. var Contains = containsFunc(func(geofence *geom.Polygon, hash string) (bool, error) { - hashGeo := hashToGeometry(hash) - fence := polygonToGeometry(geofence) - return fence.Contains(hashGeo) -}) - -func geomToGeosCoord(coord geom.Coord) geos.Coord { - return geos.Coord{ - X: coord.X(), - Y: coord.Y(), + hashGeom := hashToGeom(hash) + fenceGeom := g2p(geofence) + out, err := polygol.Intersection(fenceGeom, hashGeom) + if err != nil { + return false, err } -} - -func geomToGeosCoords(coords []geom.Coord) []geos.Coord { - out := make([]geos.Coord, len(coords)) - for i := 0; i < len(coords); i++ { - out[i] = geomToGeosCoord(coords[i]) + if len(out) == 0 { + return false, nil + } else { + o, err := polygol.XOR(hashGeom, out) + if err != nil { + return false, err + } + if len(o) == 0 { + return true, err + } + return false, nil } - return out -} +}) -// hashToGeometry converts a a geohash to a geos polygon by taking its bounding box. -func hashToGeometry(hash string) *geos.Geometry { +func hashToGeom(hash string) polygol.Geom { bounds := geohash.BoundingBox(hash) - return geos.Must(geos.NewPolygon([]geos.Coord{ - geos.NewCoord(bounds.MinLng, bounds.MinLat), - geos.NewCoord(bounds.MinLng, bounds.MaxLat), - geos.NewCoord(bounds.MaxLng, bounds.MaxLat), - geos.NewCoord(bounds.MaxLng, bounds.MinLat), - geos.NewCoord(bounds.MinLng, bounds.MinLat), - })) + return polygol.Geom{{{ + {bounds.MinLng, bounds.MinLat}, + {bounds.MinLng, bounds.MaxLat}, + {bounds.MaxLng, bounds.MaxLat}, + {bounds.MaxLng, bounds.MinLat}, + {bounds.MinLng, bounds.MinLat}, + }}} } -func polygonToGeometry(geofence *geom.Polygon) *geos.Geometry { - // Convert the outer shell to geos format. - shell := geofence.LinearRing(0).Coords() - shellGeos := geomToGeosCoords(shell) +func g2p(g geom.T) [][][][]float64 { + + var coords [][][]geom.Coord + + switch v := g.(type) { + case *geom.Polygon: + coords = [][][]geom.Coord{v.Coords()} + case *geom.MultiPolygon: + coords = v.Coords() + } - // Convert each hole to geos format. - numHoles := geofence.NumLinearRings() - 1 - holes := make([][]geos.Coord, numHoles) - for i := 0; i < numHoles; i++ { - holes[i] = geomToGeosCoords(geofence.LinearRing(i).Coords()) + p := make([][][][]float64, len(coords)) + + for i := range coords { + p[i] = make([][][]float64, len(coords[i])) + for j := range coords[i] { + p[i][j] = make([][]float64, len(coords[i][j])) + for k := range coords[i][j] { + coord := coords[i][j][k] + pt := make([]float64, 2) + pt[0], pt[1] = coord.X(), coord.Y() + p[i][j][k] = pt + } + } } - return geos.Must(geos.NewPolygon(shellGeos, holes...)) + return p }