Skip to content

Commit

Permalink
tree: implement casts between box2d and geometry
Browse files Browse the repository at this point in the history
Release note (sql change): Implement the ability to cast between box2d
and geometry types.
  • Loading branch information
otan committed Aug 18, 2020
1 parent 3ea329f commit 51b89db
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 29 deletions.
21 changes: 21 additions & 0 deletions pkg/geo/bbox.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,27 @@ func (b *CartesianBoundingBox) Covers(o *CartesianBoundingBox) bool {
b.LoY <= o.HiY && o.HiY <= b.HiY
}

// ToGeomT converts a BoundingBox to a GeomT.
func (b *CartesianBoundingBox) ToGeomT() geom.T {
if b.LoX == b.HiX && b.LoY == b.HiY {
return geom.NewPointFlat(geom.XY, []float64{b.LoX, b.LoY})
}
if b.LoX == b.HiX || b.LoY == b.HiY {
return geom.NewLineStringFlat(geom.XY, []float64{b.LoX, b.LoY, b.HiX, b.HiY})
}
return geom.NewPolygonFlat(
geom.XY,
[]float64{
b.LoX, b.LoY,
b.LoX, b.HiY,
b.HiX, b.HiY,
b.HiX, b.LoY,
b.LoX, b.LoY,
},
[]int{10},
)
}

// boundingBoxFromGeomT returns a bounding box from a given geom.T.
// Returns nil if no bounding box was found.
func boundingBoxFromGeomT(g geom.T, soType geopb.SpatialObjectType) (*geopb.BoundingBox, error) {
Expand Down
40 changes: 11 additions & 29 deletions pkg/geo/geomfn/envelope.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ package geomfn

import (
"github.com/cockroachdb/cockroach/pkg/geo"
"github.com/cockroachdb/errors"
"github.com/twpayne/go-geom"
)

Expand All @@ -22,34 +23,15 @@ func Envelope(g *geo.Geometry) (*geo.Geometry, error) {
if g.Empty() {
return g, nil
}
bbox := g.CartesianBoundingBox()
if bbox.LoX == bbox.HiX && bbox.LoY == bbox.HiY {
return geo.NewGeometryFromGeomT(
geom.NewPointFlat(geom.XY, []float64{bbox.LoX, bbox.LoY}).SetSRID(int(g.SRID())),
)
t := g.CartesianBoundingBox().ToGeomT()
switch t := t.(type) {
case *geom.Point:
return geo.NewGeometryFromGeomT(t.SetSRID(int(g.SRID())))
case *geom.LineString:
return geo.NewGeometryFromGeomT(t.SetSRID(int(g.SRID())))
case *geom.Polygon:
return geo.NewGeometryFromGeomT(t.SetSRID(int(g.SRID())))
default:
return nil, errors.Newf("unknown geom type: %T", t)
}
if bbox.LoX == bbox.HiX || bbox.LoY == bbox.HiY {
return geo.NewGeometryFromGeomT(
geom.NewLineStringFlat(
geom.XY,
[]float64{
bbox.LoX, bbox.LoY,
bbox.HiX, bbox.HiY,
},
).SetSRID(int(g.SRID())),
)
}
return geo.NewGeometryFromGeomT(
geom.NewPolygonFlat(
geom.XY,
[]float64{
bbox.LoX, bbox.LoY,
bbox.LoX, bbox.HiY,
bbox.HiX, bbox.HiY,
bbox.HiX, bbox.LoY,
bbox.LoX, bbox.LoY,
},
[]int{10},
).SetSRID(int(g.SRID())),
)
}
34 changes: 34 additions & 0 deletions pkg/sql/logictest/testdata/logic_test/geospatial_bbox
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,37 @@ NULL
BOX(-1 -1,1 1)
BOX(4 -5,4 -5)
BOX(-1 -5,4 1)

subtest cast_test

query T
SELECT
ST_AsEWKT(b::box2d::geometry)
FROM ( VALUES
(NULL),
('box(-1 -1,-1 -1)'),
('box(1 3,20 3)'),
('box(5.5 -10, 5.5 60)'),
('box(-1 -2, 4 6)')
) tbl(b)
----
NULL
POINT (-1 -1)
LINESTRING (1 3, 20 3)
LINESTRING (5.5 -10, 5.5 60)
POLYGON ((-1 -2, -1 6, 4 6, 4 -2, -1 -2))

query T
SELECT
g::geometry::box2d
FROM ( VALUES
('point empty'),
(null),
('point(5 5)'),
('linestring(4 5, 9 10)')
) tbl(g)
----
NULL
NULL
BOX(5 5,5 5)
BOX(4 5,9 10)
16 changes: 16 additions & 0 deletions pkg/sql/sem/tree/casts.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ var validCasts = []castInfo{
{from: types.UnknownFamily, to: types.Box2DFamily, volatility: VolatilityImmutable},
{from: types.StringFamily, to: types.Box2DFamily, volatility: VolatilityImmutable},
{from: types.CollatedStringFamily, to: types.Box2DFamily, volatility: VolatilityImmutable},
{from: types.GeometryFamily, to: types.Box2DFamily, volatility: VolatilityImmutable},
{from: types.Box2DFamily, to: types.Box2DFamily, volatility: VolatilityImmutable},

// Casts to GeographyFamily.
{from: types.UnknownFamily, to: types.GeographyFamily, volatility: VolatilityImmutable},
Expand All @@ -125,6 +127,7 @@ var validCasts = []castInfo{

// Casts to GeometryFamily.
{from: types.UnknownFamily, to: types.GeometryFamily, volatility: VolatilityImmutable},
{from: types.Box2DFamily, to: types.GeometryFamily, volatility: VolatilityImmutable},
{from: types.BytesFamily, to: types.GeometryFamily, volatility: VolatilityImmutable},
{from: types.JsonFamily, to: types.GeometryFamily, volatility: VolatilityImmutable},
{from: types.StringFamily, to: types.GeometryFamily, volatility: VolatilityImmutable},
Expand Down Expand Up @@ -709,6 +712,13 @@ func PerformCast(ctx *EvalContext, d Datum, t *types.T) (Datum, error) {
return ParseDBox2D(string(*d))
case *DCollatedString:
return ParseDBox2D(d.Contents)
case *DBox2D:
return d, nil
case *DGeometry:
if d.Geometry.Empty() {
return DNull, nil
}
return NewDBox2D(d.CartesianBoundingBox()), nil
}

case types.GeographyFamily:
Expand Down Expand Up @@ -794,6 +804,12 @@ func PerformCast(ctx *EvalContext, d Datum, t *types.T) (Datum, error) {
return nil, err
}
return &DGeometry{g}, nil
case *DBox2D:
g, err := geo.NewGeometryFromGeomT(d.ToGeomT())
if err != nil {
return nil, err
}
return &DGeometry{g}, nil
case *DBytes:
g, err := geo.ParseGeometryFromEWKB(geopb.EWKB(*d))
if err != nil {
Expand Down

0 comments on commit 51b89db

Please sign in to comment.