Skip to content

Commit

Permalink
Add BoundingDiagonal() method to Envelope
Browse files Browse the repository at this point in the history
The main usecase for this method is to help a user construct a compact
representation of an envelope, for example as WKB. Rather than encoding
the polygon representation of the Envelope, the LineString
representation can be used instead.

The Polygon representation uses 89 bytes (1 for the byte order, 4 to
encode the number of rings, 4 to encode the number of points in the
outer ring, and then 80 to represent the 5 points in the ring itself).
The LineString representation only uses 37 bytes (1 for the byte order,
4 to encode the number of points, and 32 to represent 2 points).
  • Loading branch information
peterstace committed Feb 4, 2023
1 parent 8e2f45d commit 2705ec1
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
- Make `IgnoreOrder` a function rather than a global variable to prevent
consumers from erroneously altering its behaviour.

- Add a `BoundingDiagonal` method to the `Envelope` type.

## v0.41.0

2022-11-15
Expand Down
24 changes: 24 additions & 0 deletions geom/type_envelope.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,3 +295,27 @@ func (e Envelope) box() (rtree.Box, bool) {
MaxY: e.maxY,
}, !e.IsEmpty()
}

// BoundingDiagonal returns the LineString that goes from the minimum point in
// the Envelope to the point in the Envelope. If the envelope is degenerate and
// represents a single point, then a Point is returned instead of a LineString.
// If the Envelope is empty, then the empty Geometry (representing an empty
// GeometryCollection) is returned.
func (e Envelope) BoundingDiagonal() Geometry {
if e.IsEmpty() {
return Geometry{}
}
if e.IsPoint() {
return e.min().asUncheckedPoint().AsGeometry()
}

// The Envelope has already been validated, so it's safe to skip validation
// when constructing the diagonal.
coords := []float64{e.minX(), e.minY, e.maxX, e.maxY}
seq := NewSequence(coords, DimXY)
ls, err := NewLineString(seq, DisableAllValidations)
if err != nil {
panic("programming error: validations disabled by LineString validation failed")
}
return ls.AsGeometry()
}
27 changes: 27 additions & 0 deletions geom/type_envelope_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -582,3 +582,30 @@ func BenchmarkEnvelopeTransformXY(b *testing.B) {
expectNoErr(b, err)
}
}

func TestBoundingDiagonal(t *testing.T) {
for i, tc := range []struct {
env []XY
want string
}{
{
nil,
"GEOMETRYCOLLECTION EMPTY",
},
{
[]XY{{1, 2}},
"POINT(1 2)",
},
{
[]XY{{3, 2}, {1, 4}},
"LINESTRING(1 2,3 4)",
},
} {
t.Run(strconv.Itoa(i), func(t *testing.T) {
env, err := NewEnvelope(tc.env)
expectNoErr(t, err)
got := env.BoundingDiagonal()
expectGeomEqWKT(t, got, tc.want)
})
}
}

0 comments on commit 2705ec1

Please sign in to comment.