Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
50611: builtins: implement ST_GeoHash for Geometry and Geography types r=otan a=juanjcsr

This PR adds a function to calculate the geohash representation,
imitating the PostGIS funcitonality. If no precision is
specified, the function calculates the geohash according
to the feature. For points, it uses a default precision
of 20 characters. For larger features, it estimates a
precision according to the size of the feature.

Release note (sql change): Implements ST_GeoHash for
geometry and geography types.

Resolves cockroachdb#48397
Resolves cockroachdb#48943


Co-authored-by: Juan Carlos <[email protected]>
  • Loading branch information
craig[bot] and juanjcsr committed Jul 24, 2020
2 parents 655f2b9 + 35399f1 commit 6c4190d
Show file tree
Hide file tree
Showing 8 changed files with 396 additions and 1 deletion.
8 changes: 8 additions & 0 deletions docs/generated/sql/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -1128,6 +1128,14 @@ Bottom Left.</p>
</span></td></tr>
<tr><td><a name="st_geographyfromtext"></a><code>st_geographyfromtext(val: <a href="string.html">string</a>) &rarr; geography</code></td><td><span class="funcdesc"><p>Returns the Geography from a WKT or EWKT representation.</p>
</span></td></tr>
<tr><td><a name="st_geohash"></a><code>st_geohash(geography: geography) &rarr; <a href="string.html">string</a></code></td><td><span class="funcdesc"><p>Returns a GeoHash representation of the geeographywith full precision if a point is provided, or with variable precision based on the size of the feature.</p>
</span></td></tr>
<tr><td><a name="st_geohash"></a><code>st_geohash(geography: geography, precision: <a href="int.html">int</a>) &rarr; <a href="string.html">string</a></code></td><td><span class="funcdesc"><p>Returns a GeoHash representation of the geography with the supplied precision.</p>
</span></td></tr>
<tr><td><a name="st_geohash"></a><code>st_geohash(geometry: geometry) &rarr; <a href="string.html">string</a></code></td><td><span class="funcdesc"><p>Returns a GeoHash representation of the geometry with full precision if a point is provided, or with variable precision based on the size of the feature. This will error any coordinates are outside the bounds of longitude/latitude.</p>
</span></td></tr>
<tr><td><a name="st_geohash"></a><code>st_geohash(geometry: geometry, precision: <a href="int.html">int</a>) &rarr; <a href="string.html">string</a></code></td><td><span class="funcdesc"><p>Returns a GeoHash representation of the geometry with the supplied precision. This will error any coordinates are outside the bounds of longitude/latitude.</p>
</span></td></tr>
<tr><td><a name="st_geomcollfromtext"></a><code>st_geomcollfromtext(str: <a href="string.html">string</a>, srid: <a href="int.html">int</a>) &rarr; geometry</code></td><td><span class="funcdesc"><p>Returns the Geometry from a WKT or EWKT representation with an SRID. If the shape underneath is not GeometryCollection, NULL is returned. If the SRID is present in both the EWKT and the argument, the argument value is used.</p>
</span></td></tr>
<tr><td><a name="st_geomcollfromtext"></a><code>st_geomcollfromtext(val: <a href="string.html">string</a>) &rarr; geometry</code></td><td><span class="funcdesc"><p>Returns the Geometry from a WKT or EWKT representation. If the shape underneath is not GeometryCollection, NULL is returned.</p>
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ require (
github.com/peterbourgon/g2s v0.0.0-20170223122336-d4e7ad98afea // indirect
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5
github.com/pierrec/lz4 v2.4.1+incompatible // indirect
github.com/pierrre/geohash v1.0.0
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4
github.com/pmezard/go-difflib v1.0.0
github.com/prometheus/client_golang v1.1.0
Expand Down
16 changes: 16 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/CloudyKit/fastprinter v0.0.0-20170127035650-74b38d55f37a/go.mod h1:EFZQ978U7x8IRnstaskI3IysnWY5Ao3QgZUKOXlsAdw=
github.com/CloudyKit/jet v2.1.3-0.20180809161101-62edd43e4f88+incompatible/go.mod h1:HPYO+50pSWkPoj9Q/eq0aRGByCL6ScRlUmiEX5Zgm+w=
github.com/Codefor/geohash v0.0.0-20140723084247-1b41c28e3a9d h1:iG9B49Q218F/XxXNRM7k/vWf7MKmLIS8AcJV9cGN4nA=
github.com/Codefor/geohash v0.0.0-20140723084247-1b41c28e3a9d/go.mod h1:RVnhzAX71far8Kc3TQeA0k/dcaEKUnTDSOyet/JCmGI=
github.com/DATA-DOG/go-sqlmock v1.3.2 h1:2L2f5t3kKnCLxnClDD/PrDfExFFa1wjESgxHG/B1ibo=
github.com/DATA-DOG/go-sqlmock v1.3.2/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/DataDog/zstd v1.3.6-0.20190409195224-796139022798/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
Expand All @@ -70,6 +72,8 @@ github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWso
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8=
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/TomiHiltunen/geohash-golang v0.0.0-20150112065804-b3e4e625abfb h1:wumPkzt4zaxO4rHPBrjDK8iZMR41C1qs7njNqlacwQg=
github.com/TomiHiltunen/geohash-golang v0.0.0-20150112065804-b3e4e625abfb/go.mod h1:QiYsIBRQEO+Z4Rz7GoI+dsHVneZNONvhczuA+llOZNM=
github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM=
github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
Expand Down Expand Up @@ -107,6 +111,8 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/biogo/store v0.0.0-20160505134755-913427a1d5e8 h1:tYoz1OeRpx3dJZlh9T4dQt4kAndcmpl+VNdzbSgFC/0=
github.com/biogo/store v0.0.0-20160505134755-913427a1d5e8/go.mod h1:Iev9Q3MErcn+w3UOJD/DkEzllvugfdx7bGcMOFhvr/4=
github.com/broady/gogeohash v0.0.0-20120525094510-7b2c40d64042 h1:iEdmkrNMLXbM7ecffOAtZJQOQUTE4iMonxrb5opUgE4=
github.com/broady/gogeohash v0.0.0-20120525094510-7b2c40d64042/go.mod h1:f1L9YvXvlt9JTa+A17trQjSMM6bV40f+tHjB+Pi+Fqk=
github.com/cenkalti/backoff v2.1.1+incompatible h1:tKJnvO2kl0zmb/jA5UKAt4VoEVw1qxKWjE/Bpp46npY=
github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
Expand Down Expand Up @@ -232,6 +238,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a h1:yDWHCSQ40h88yih2JAcL6Ls/kVkSE8GFACTGVnMPruw=
github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a/go.mod h1:7Ga40egUymuWXxAe151lTNnCv97MddSOVsjpPPkityA=
github.com/fanixk/geohash v0.0.0-20150324002647-c1f9b5fa157a h1:Fyfh/dsHFrC6nkX7H7+nFdTd1wROlX/FxEIWVpKYf1U=
github.com/fanixk/geohash v0.0.0-20150324002647-c1f9b5fa157a/go.mod h1:UgNw+PTmmGN8rV7RvjvnBMsoTU8ZXXnaT3hYsDTBlgQ=
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
Expand Down Expand Up @@ -535,6 +543,8 @@ github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/I
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mmatczuk/go_generics v0.0.0-20181212143635-0aaa050f9bab h1:QjLgi/L+MjxysinrA8KkNZLf3cAhTluBoSXUvFeN144=
github.com/mmatczuk/go_generics v0.0.0-20181212143635-0aaa050f9bab/go.mod h1:Fs8p4al9GNa3b42YfZOoUVMdjQ2WlNEYlnoboJKPpJ8=
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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
Expand Down Expand Up @@ -582,6 +592,10 @@ github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCr
github.com/pierrec/lz4 v0.0.0-20190327172049-315a67e90e41/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
github.com/pierrec/lz4 v2.4.1+incompatible h1:mFe7ttWaflA46Mhqh+jUfjp2qTbPYxLB2/OyBppH9dg=
github.com/pierrec/lz4 v2.4.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pierrre/compare v1.0.2 h1:k4IUsHgh+dbcAOIWCfxVa/7G6STjADH2qmhomv+1quc=
github.com/pierrre/compare v1.0.2/go.mod h1:8UvyRHH+9HS8Pczdd2z5x/wvv67krDwVxoOndaIIDVU=
github.com/pierrre/geohash v1.0.0 h1:f/zfjdV4rVofTCz1FhP07T+EMQAvcMM2ioGZVt+zqjI=
github.com/pierrre/geohash v1.0.0/go.mod h1:atytaeVa21hj5F6kMebHYPf8JbIrGxK2FSzN2ajKXms=
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4 h1:49lOXmGaUpV9Fz3gd7TFZY106KVlPVa5jcYD1gaQf98=
Expand Down Expand Up @@ -668,6 +682,8 @@ github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/the42/cartconvert v0.0.0-20131203171324-aae784c392b8 h1:I4DY8wLxJXCrMYzDM6lKCGc3IQwJX0PlTLsd3nQqI3c=
github.com/the42/cartconvert v0.0.0-20131203171324-aae784c392b8/go.mod h1:fWO/msnJVhHqN1yX6OBoxSyfj7TEj1hHiL8bJSQsK30=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/twpayne/go-geom v1.3.2 h1:4E9aSr+B5y+wjTFf2fNmxsnYSZD2k1nSdDyos3ZQ/EY=
github.com/twpayne/go-geom v1.3.2/go.mod h1:vFTJTOBkeN903mbhV6FYSTOUjglpV6WA8J11fNiUXaQ=
Expand Down
118 changes: 118 additions & 0 deletions pkg/geo/encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import (
"github.com/cockroachdb/cockroach/pkg/geo/geopb"
"github.com/cockroachdb/cockroach/pkg/geo/geoprojbase"
"github.com/cockroachdb/errors"
"github.com/golang/geo/s1"
"github.com/pierrre/geohash"
"github.com/twpayne/go-geom"
"github.com/twpayne/go-geom/encoding/ewkb"
"github.com/twpayne/go-geom/encoding/geojson"
Expand Down Expand Up @@ -179,6 +181,122 @@ func SpatialObjectToKML(so geopb.SpatialObject) (string, error) {
return buf.String(), nil
}

// GeoHashAutoPrecision means to calculate the precision of SpatialObjectToGeoHash
// based on input, up to 32 characters.
const GeoHashAutoPrecision = 0

// GeoHashMaxPrecision is the maximum precision for GeoHashes.
// 20 is picked as doubles have 51 decimals of precision, and each base32 position
// can contain 5 bits of data. As we have two points, we use floor((2 * 51) / 5) = 20.
const GeoHashMaxPrecision = 20

// SpatialObjectToGeoHash transforms a given SpatialObject to a GeoHash.
func SpatialObjectToGeoHash(so geopb.SpatialObject, p int) (string, error) {
if so.BoundingBox == nil {
return "", nil
}
bbox := so.BoundingBox
if so.Type == geopb.SpatialObjectType_GeographyType {
// Convert bounding box back to degrees.
bbox = &geopb.BoundingBox{
LoX: s1.Angle(bbox.LoX).Degrees(),
HiX: s1.Angle(bbox.HiX).Degrees(),
LoY: s1.Angle(bbox.LoY).Degrees(),
HiY: s1.Angle(bbox.HiY).Degrees(),
}
}
if bbox.LoX < -180 || bbox.HiX > 180 || bbox.LoY < -90 || bbox.HiY > 90 {
return "", errors.Newf(
"object has bounds greater than the bounds of lat/lng, got (%f %f, %f %f)",
bbox.LoX, bbox.LoY,
bbox.HiX, bbox.HiY,
)
}

// Get precision using the bounding box if required.
if p <= GeoHashAutoPrecision {
p = getPrecisionForBBox(bbox)
}

// Support up to 20, which is the same as PostGIS.
if p > GeoHashMaxPrecision {
p = GeoHashMaxPrecision
}

bbCenterLng := bbox.LoX + (bbox.HiX-bbox.LoX)/2.0
bbCenterLat := bbox.LoY + (bbox.HiY-bbox.LoY)/2.0

return geohash.Encode(bbCenterLat, bbCenterLng, p), nil
}

// getPrecisionForBBox is a function imitating PostGIS's ability to go from
// a world bounding box and truncating a GeoHash to fit the given bounding box.
// The algorithm halves the world bounding box until it intersects with the
// feature bounding box to get a precision that will encompass the entire
// bounding box.
func getPrecisionForBBox(bbox *geopb.BoundingBox) int {
bitPrecision := 0

// This is a point, for points we use the full bitPrecision.
if bbox.LoX == bbox.HiX && bbox.LoY == bbox.HiY {
return GeoHashMaxPrecision
}

// Starts from a world bounding box:
lonMin := -180.0
lonMax := 180.0
latMin := -90.0
latMax := 90.0

// Each iteration shrinks the world bounding box by half in the dimension that
// does not fit, making adjustments each iteration until it intersects with
// the object bbox.
for {
lonWidth := lonMax - lonMin
latWidth := latMax - latMin
latMaxDelta, lonMaxDelta, latMinDelta, lonMinDelta := 0.0, 0.0, 0.0, 0.0

// Look at whether the longitudes of the bbox are to the left or
// the right of the world bbox longitudes, shrinks it and makes adjustments
// for the next iteration.
if bbox.LoX > lonMin+lonWidth/2.0 {
lonMinDelta = lonWidth / 2.0
} else if bbox.HiX < lonMax-lonWidth/2.0 {
lonMaxDelta = lonWidth / -2.0
}
// Look at whether the latitudes of the bbox are to the left or
// the right of the world bbox latitudes, shrinks it and makes adjustments
// for the next iteration.
if bbox.LoY > latMin+latWidth/2.0 {
latMinDelta = latWidth / 2.0
} else if bbox.HiY < latMax-latWidth/2.0 {
latMaxDelta = latWidth / -2.0
}

// Every change we make that splits the box up adds precision.
// If we detect no change, we've intersected a box and so must exit.
precisionDelta := 0
if lonMinDelta != 0.0 || lonMaxDelta != 0.0 {
lonMin += lonMinDelta
lonMax += lonMaxDelta
precisionDelta++
} else {
break
}
if latMinDelta != 0.0 || latMaxDelta != 0.0 {
latMin += latMinDelta
latMax += latMaxDelta
precisionDelta++
} else {
break
}
bitPrecision += precisionDelta
}
// Each character can represent 5 bits of bitPrecision.
// As such, divide by 5 to get GeoHash precision.
return bitPrecision / 5
}

// StringToByteOrder returns the byte order of string.
func StringToByteOrder(s string) binary.ByteOrder {
switch strings.ToLower(s) {
Expand Down
163 changes: 163 additions & 0 deletions pkg/geo/encode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,166 @@ func TestSpatialObjectToKML(t *testing.T) {
})
}
}

func TestSpatialObjectToGeoHash(t *testing.T) {
testCases := []struct {
desc string
a string
p int
expected string
}{
{
desc: "POINT EMPTY",
a: "POINT EMPTY",
p: 0,
expected: "",
},
{
desc: "POLYGON EMPTY",
a: "POLYGON EMPTY",
p: 0,
expected: "",
},
{
desc: "Point at 0,0",
a: "POINT(0.0 0.0)",
p: 16,
expected: "s000000000000000",
},
{
"Point at 90, 0",
"POINT(90.0 0.0)",
16,
"w000000000000000",
},
{
"Point at a random location",
"SRID=4004;POINT(20.012345 -20.012345)",
15,
"kkqnpkue9ktbpe5",
},
{
"GeoHash from a MultiPolygon",
"POLYGON((-71.1776585052917 42.3902909739571,-71.1776820268866 42.3903701743239, -71.1776063012595 42.3903825660754,-71.1775826583081 42.3903033653531,-71.1776585052917 42.3902909739571))",
20,
"drt3hkfj8gw86nz6tbx7",
},
{
"Point at a random location",
"SRID=4004;POINT(20.0123451111111111 -20.012345111111111)",
20,
"kkqnpkue9kqp6mbe5c6b",
},
{
"Polygon to check automatic precision",
"POLYGON((-8.359375000000018 34.36143956369891,-3.4375000000000178 34.36143956369891,-3.4375000000000178 30.8077684261472,-8.359375000000018 30.8077684261472,-8.359375000000018 34.36143956369891))",
-1,
"e",
},
{
"Polygon to check manual precision",
"POLYGON((-8.359375000000018 34.36143956369891,-3.4375000000000178 34.36143956369891,-3.4375000000000178 30.8077684261472,-8.359375000000018 30.8077684261472,-8.359375000000018 34.36143956369891))",
5,
"evgc3",
},
{
"Polygon to check automatic precision",
"POLYGON((-99.18139024416594 19.420811187791617,-99.17177720705656 19.433762205907612,-99.16903062502531 19.424372820694032,-99.17589708010344 19.415306692500074,-99.19134660402922 19.409802010814566,-99.17795701662688 19.40526860361888,-99.21709581057219 19.40624005865604,-99.18139024416594 19.420811187791617))",
-1,
"9g3q",
},
{
"Polygon to check full precision",
"POLYGON((-99.18139024416594 19.420811187791617,-99.17177720705656 19.433762205907612,-99.16903062502531 19.424372820694032,-99.17589708010344 19.415306692500074,-99.19134660402922 19.409802010814566,-99.17795701662688 19.40526860361888,-99.21709581057219 19.40624005865604,-99.18139024416594 19.420811187791617))",
20,
"9g3qqz1yfh51x7uke7fz",
},
{
"GeoHash from a LineString",
"LineString(-99.22962622216731 19.468542204204024,-99.2289395766595 19.46902774319579)",
-1,
"9g3qvbp",
},
{
"GeoHash from a LineString full precision",
"LineString(-99.22962622216731 19.468542204204024,-99.2289395766595 19.46902774319579)",
20,
"9g3qvbpmyhefh1ecdhpw",
},
{
"GeoHash of LineString crossing DateLine",
"LINESTRING(-179 0, 179 0)",
GeoHashAutoPrecision,
"",
},
{
"GeoHash of LineString crossing DateLine",
"LINESTRING(-179 0, 179 0)",
5,
"s0000",
},
{
"GEOMTRYCOLLECTION EMPTY",
"SRID=4326;GEOMETRYCOLLECTION EMPTY",
20,
"",
},
}

t.Run("geometry", func(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
a, err := ParseGeometry(tc.a)
require.NoError(t, err)
geohash, err := SpatialObjectToGeoHash(a.SpatialObject(), tc.p)
require.NoError(t, err)
require.Equal(t, tc.expected, geohash)
})
}
})
t.Run("geography", func(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
a, err := ParseGeography(tc.a)
require.NoError(t, err)
geohash, err := SpatialObjectToGeoHash(a.SpatialObject(), tc.p)
require.NoError(t, err)
require.Equal(t, tc.expected, geohash)
})
}
})

t.Run("crossing the date line", func(t *testing.T) {
a, err := ParseGeography("LINESTRING(179 0, -179 0)")
require.NoError(t, err)
geohash, err := SpatialObjectToGeoHash(a.SpatialObject(), GeoHashAutoPrecision)
require.NoError(t, err)
require.Equal(t, "", geohash)
})

t.Run("geohashes errors with invalid bounds", func(t *testing.T) {
for _, tc := range []struct {
desc string
a string
p int
}{
{
"Point at 90, 181",
"POINT(90.0 181.0)",
16,
},
{
"Point at 90, 181",
"POINT(-990 181)",
16,
},
} {
t.Run(tc.desc, func(t *testing.T) {
a, err := ParseGeometry(tc.a)
require.NoError(t, err)
_, err = SpatialObjectToGeoHash(a.SpatialObject(), tc.p)
require.Error(t, err)
})
}
})
}
Loading

0 comments on commit 6c4190d

Please sign in to comment.