func (e Envelope) Center() Point {
	if e.IsEmpty() {
		return Point{}
	}
	return e.min().
		Add(e.max()).
		Scale(0.5).
		asUncheckedPoint()
}
Center returns the center point of the envelope.
package geom
import (
	"fmt"
	"math"
	"github.com/peterstace/simplefeatures/rtree"
)
type Envelope struct {
	nanXORMinX uint64
	minY float64
	maxX float64
	maxY float64
}
var nan = math.Float64bits(math.NaN())
func encodeFloat64WithNaN(f float64) uint64 {
	return math.Float64bits(f) ^ nan
}
func (e Envelope) minX() float64 {
	return math.Float64frombits(e.nanXORMinX ^ nan)
}
func NewEnvelope(xys []XY) (Envelope, error) {
	var env Envelope
	for _, xy := range xys {
		var err error
		env, err = env.ExtendToIncludeXY(xy)
		if err != nil {
			return Envelope{}, err
		}
	}
	return env, nil
}
func newUncheckedEnvelope(min, max XY) Envelope {
	return Envelope{
		nanXORMinX: encodeFloat64WithNaN(min.X),
		minY:       min.Y,
		maxX:       max.X,
		maxY:       max.Y,
	}
}
func (e Envelope) min() XY {
	return XY{e.minX(), e.minY}
}
func (e Envelope) max() XY {
	return XY{e.maxX, e.maxY}
}
func (e Envelope) IsEmpty() bool {
	return math.IsNaN(e.minX())
}
func (e Envelope) IsPoint() bool {
	return !e.IsEmpty() && e.min() == e.max()
}
func (e Envelope) IsLine() bool {
	return !e.IsEmpty() && (e.minX() == e.maxX) != (e.minY == e.maxY)
}
func (e Envelope) IsRectangle() bool {
	return !e.IsEmpty() && e.minX() != e.maxX && e.minY != e.maxY
}
func (e Envelope) AsGeometry() Geometry {
	switch {
	case e.IsEmpty():
		return Geometry{}
	case e.IsPoint():
		return e.min().asUncheckedPoint().AsGeometry()
	case e.IsLine():
		ln := line{e.min(), e.max()}
		return ln.asLineString().AsGeometry()
	}
	minX := e.minX()
	floats := [...]float64{
		minX, e.minY,
		minX, e.maxY,
		e.maxX, e.maxY,
		e.maxX, e.minY,
		minX, e.minY,
	}
	seq := NewSequence(floats[:], DimXY)
	ls, err := NewLineString(seq)
	if err != nil {
		panic(fmt.Sprintf("constructing geometry from envelope: %v", err))
	}
	poly, err := NewPolygon([]LineString{ls})
	if err != nil {
		panic(fmt.Sprintf("constructing geometry from envelope: %v", err))
	}
	return poly.AsGeometry()
}
func (e Envelope) Min() Point {
	if e.IsEmpty() {
		return Point{}
	}
	return e.min().asUncheckedPoint()
}
func (e Envelope) Max() Point {
	if e.IsEmpty() {
		return Point{}
	}
	return e.max().asUncheckedPoint()
}
func (e Envelope) MinMaxXYs() (XY, XY, bool) {
	if e.IsEmpty() {
		return XY{}, XY{}, false
	}
	return e.min(), e.max(), true
}
func (e Envelope) ExtendToIncludeXY(xy XY) (Envelope, error) {
	if err := xy.validate(); err != nil {
		return Envelope{}, err
	}
	return e.uncheckedExtend(xy), nil
}
func (e Envelope) uncheckedExtend(xy XY) Envelope {
	if e.IsEmpty() {
		return newUncheckedEnvelope(xy, xy)
	}
	return newUncheckedEnvelope(
		XY{fastMin(e.minX(), xy.X), fastMin(e.minY, xy.Y)},
		XY{fastMax(e.maxX, xy.X), fastMax(e.maxY, xy.Y)},
	)
}
func (e Envelope) ExpandToIncludeEnvelope(o Envelope) Envelope {
	if e.IsEmpty() {
		return o
	}
	if o.IsEmpty() {
		return e
	}
	return newUncheckedEnvelope(
		XY{fastMin(e.minX(), o.minX()), fastMin(e.minY, o.minY)},
		XY{fastMax(e.maxX, o.maxX), fastMax(e.maxY, o.maxY)},
	)
}
func (e Envelope) Contains(p XY) bool {
	return !e.IsEmpty() &&
		p.validate() == nil &&
		p.X >= e.minX() && p.X <= e.maxX &&
		p.Y >= e.minY && p.Y <= e.maxY
}
func (e Envelope) Intersects(o Envelope) bool {
	return !e.IsEmpty() && !o.IsEmpty() &&
		(e.minX() <= o.maxX) && (e.maxX >= o.minX()) &&
		(e.minY <= o.maxY) && (e.maxY >= o.minY)
}