Skip to content

Commit

Permalink
Removed tegola.BoundingBox and updated slippy go-spatial#270
Browse files Browse the repository at this point in the history
• Removed BoundingBox for tegola.Tile, and replaced it with
geom.BoundingBox.

• Added Bounds method to get the {north, east, south, west} values.
• Replaced [4]float64 with *geom.BoundingBox for BufferedExtent and
Extent on slippy.Tile.
  • Loading branch information
gdey committed Mar 9, 2018
1 parent a18e2af commit 8a49707
Show file tree
Hide file tree
Showing 22 changed files with 445 additions and 330 deletions.
17 changes: 0 additions & 17 deletions bounding_box.go

This file was deleted.

22 changes: 2 additions & 20 deletions cmd/tegola/cmd/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import (
"github.com/go-spatial/tegola/atlas"
"github.com/go-spatial/tegola/cache"
"github.com/go-spatial/tegola/internal/log"
"github.com/go-spatial/tegola/maths/webmercator"
"github.com/go-spatial/tegola/provider"
)

Expand Down Expand Up @@ -97,25 +96,8 @@ var cacheCmd = &cobra.Command{
}

zooms = append(zooms, t.Z)
// read the tile bounds, which will be in web mercator
tBounds := t.BoundingBox()
// convert the bounds points to lat lon
ul, err := webmercator.PToLonLat(tBounds.Minx, tBounds.Miny)
if err != nil {
log.Fatal(err)
}
lr, err := webmercator.PToLonLat(tBounds.Maxx, tBounds.Maxy)
if err != nil {
log.Fatal(err)
}
// use the tile bounds as the bounds for the job.
// the grid flips between web mercator and WGS84 which is why we use must use lat from one point and lon from the other
// TODO: this smells funny. Investigate why the grid is flipping - arolek
bounds[0] = ul[0]
bounds[1] = lr[1]

bounds[2] = lr[0]
bounds[3] = ul[1]
// read the tile bounds, which will be in lat, lon, it will be the north, east, south, west.
bounds = t.Bounds()
} else {
// bounding box caching
boundsParts := strings.Split(cacheBounds, ",")
Expand Down
2 changes: 1 addition & 1 deletion cmd/xyz2svg/cmd/draw.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ func drawFeatures(pname string, tiler provider.Tiler, layers []string, gid int,
}

// Clip and validate
ext := geom.NewBBox(pbb[0], pbb[1])
ext := geom.NewBBox([2]float64{pbb[0], pbb[1]}, [2]float64{pbb[2], pbb[3]})
vg, err := validate.CleanGeometry(ctx, sg, ext)

// Draw each of the steps.
Expand Down
30 changes: 29 additions & 1 deletion geom/bbox.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,20 @@ func (bb *BoundingBox) MinY() float64 {
return bb[1]
}

// TODO (gdey): look at how to have this function take into account the dpi.
func (bb *BoundingBox) XSpan() float64 {
if bb == nil {
return math.Inf(1)
}
return bb[2] - bb[0]
}
func (bb *BoundingBox) YSpan() float64 {
if bb == nil {
return math.Inf(1)
}
return bb[3] - bb[1]
}

func (bb *BoundingBox) BBox() [4]float64 {
return [4]float64{bb.MinX(), bb.MinY(), bb.MaxX(), bb.MaxY()}
}
Expand Down Expand Up @@ -194,7 +208,21 @@ func (bb *BoundingBox) ScaleBy(s float64) *BoundingBox {
if bb == nil {
return nil
}
return &BoundingBox{bb[0] * s, bb[1] * s, bb[2] * s, bb[3] * s}
return NewBBox(
[2]float64{bb[0] * s, bb[1] * s},
[2]float64{bb[2] * s, bb[3] * s},
)
}

// ExpandBy will expand bounding box by the given factor.
func (bb *BoundingBox) ExpandBy(s float64) *BoundingBox {
if bb == nil {
return nil
}
return NewBBox(
[2]float64{bb[0] - s, bb[1] - s},
[2]float64{bb[2] + s, bb[3] + s},
)
}

func (bb *BoundingBox) Clone() *BoundingBox {
Expand Down
81 changes: 74 additions & 7 deletions geom/bbox_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,9 +243,15 @@ func TestBBoxAttributes(t *testing.T) {
return pt[0] == x && pt[1] == y
}

fn := func(t *testing.T, bb *geom.BoundingBox) {
type tcase struct {
bb *geom.BoundingBox
xspan, yspan float64
}

fn := func(t *testing.T, tc tcase) {

t.Parallel()
bb := tc.bb

{
vert := bb.Vertices()
Expand Down Expand Up @@ -327,13 +333,36 @@ func TestBBoxAttributes(t *testing.T) {
}
cbb := bb.Clone()
if !cmp.BBox(bb, cbb) {
t.Errorf("Clone, equal, expected (%v) true got (%v) false", bb, cbb)
t.Errorf("Clone equal, expected (%v) true got (%v) false", bb, cbb)
}
xspan := bb.XSpan()
if tc.xspan == math.Inf(1) && xspan != tc.xspan {
t.Errorf("xspan, expected ∞ got %v", xspan)
} else {
if !cmp.Float(tc.xspan, xspan) {
t.Errorf("xspan, expected %v got %v", tc.xspan, xspan)
}
}
yspan := bb.YSpan()
if tc.yspan == math.Inf(1) && yspan != tc.yspan {
t.Errorf("yspan, expected ∞ got %v", yspan)
} else {
if !cmp.Float(tc.yspan, yspan) {
t.Errorf("yspan, expected %v got %v", tc.yspan, yspan)
}
}

}
tests := map[string]*geom.BoundingBox{
"std": &geom.BoundingBox{0.0, 0.0, 10.0, 10.0},
"nil": nil,
tests := map[string]tcase{
"std": tcase{
bb: &geom.BoundingBox{0.0, 0.0, 10.0, 10.0},
xspan: 10.0,
yspan: 10.0,
},
"nil": tcase{
bb: nil,
xspan: math.Inf(1),
yspan: math.Inf(1),
},
}
for name, tc := range tests {
tc := tc
Expand All @@ -349,7 +378,7 @@ func TestBBoxScaleBy(t *testing.T) {
}
fn := func(t *testing.T, tc tcase) {
sbb := tc.bb.ScaleBy(tc.scale)
if !reflect.DeepEqual(sbb, tc.ebb) {
if !cmp.BBox(tc.ebb, sbb) {
t.Errorf("Scale by, expected %v got %v", tc.ebb, sbb)
}
}
Expand All @@ -367,6 +396,44 @@ func TestBBoxScaleBy(t *testing.T) {
ebb: &geom.BoundingBox{0, 0, 20, 20},
scale: 2.0,
},
"-2.0 scale": tcase{
bb: &geom.BoundingBox{0, 0, 10, 10},
ebb: &geom.BoundingBox{-20, -20, 0, 0},
scale: -2.0,
},
}
for name, tc := range tests {
tc := tc
t.Run(name, func(t *testing.T) { fn(t, tc) })
}
}

func TestBBoxExpandBy(t *testing.T) {
type tcase struct {
bb *geom.BoundingBox
factor float64
ebb *geom.BoundingBox
}
fn := func(t *testing.T, tc tcase) {
sbb := tc.bb.ExpandBy(tc.factor)
if !cmp.BBox(tc.ebb, sbb) {
t.Errorf("Expand by, expected %v got %v", tc.ebb, sbb)
}
}
tests := map[string]tcase{
"nil": tcase{
factor: 2.0,
},
"1.0 factor": tcase{
bb: &geom.BoundingBox{0, 0, 10, 10},
ebb: &geom.BoundingBox{-1, -1, 11, 11},
factor: 1.0,
},
"-20.1 factor": tcase{
bb: &geom.BoundingBox{0, 0, 10, 10},
ebb: &geom.BoundingBox{-10.1, -10.1, 20.1, 20.1},
factor: -20.1,
},
}
for name, tc := range tests {
tc := tc
Expand Down
108 changes: 89 additions & 19 deletions geom/slippy/tile.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package slippy

import "math"
import (
"math"

"github.com/go-spatial/tegola/geom"
)

func NewTile(z, x, y uint64, buffer float64, srid uint64) *Tile {
return &Tile{
Expand All @@ -26,49 +30,115 @@ type Tile struct {
SRID uint64
}

func (t *Tile) ZXY() (uint64, uint64, uint64) {
return t.z, t.x, t.y
func (t *Tile) ZXY() (uint64, uint64, uint64) { return t.z, t.x, t.y }

func Tile2Lon(x, z uint64) float64 { return float64(x)/math.Exp2(float64(z))*360.0 - 180.0 }

func Tile2Lat(y, z uint64) float64 {
var n float64 = math.Pi
if y != 0 {
n = math.Pi - 2.0*math.Pi*float64(y)/math.Exp2(float64(z))
}

return 180.0 / math.Pi * math.Atan(0.5*(math.Exp(n)-math.Exp(-n)))
}

// TODO(arolek): return geom.Extent once it has been refactored
// Bounds returns the bounds of the Tile as defined by the East most longitude, North most latitude, West most longitude, South most latitude.
func (t *Tile) Bounds() [4]float64 {
east := Tile2Lon(t.x, t.z)
west := Tile2Lon(t.x+1, t.z)
north := Tile2Lat(t.y, t.z)
south := Tile2Lat(t.y+1, t.z)

return [4]float64{east, north, west, south}
}

/*
// This is how we convert from the Bounds, and TileSize to Extent for Webmercator.
bounds := t.Bounds()
east,north,west, south := bounds[0],bounds[1],bounds[2],bounds[3]
TileSize := 4096.0
c, err := webmercator.PToXY(east, north, west, south)
log.Println("c", c, "err", err)
extent := geom.NewBBox(
[2]float64{c[0], c[1]},
[2]float64{c[2], c[3]},
)
xspan := extent.XSpan()
yspan := extent.YSpan()
log.Println("Extent", extent, "MinX", extent.MinX(), "MinY", extent.MinY(), "xspan", xspan, "yspan", yspan)
// To get the Buffered Extent, we just need the extent and the Buffer size.
// Convert to tile coordinates.
nx := float64(int64((c[0] - extent.MinX()) * TileSize / xspan))
ny := float64(int64((c[1] - extent.MinY()) * TileSize / yspan))
mx := float64(int64((c[2] - extent.MinX()) * TileSize / xspan))
my := float64(int64((c[3] - extent.MinY()) * TileSize / yspan))
mextent := geom.NewBBox([2]float64{nx, ny}, [2]float64{mx, my}).ExpandBy(64)
log.Println("mxy[", nx, ny, mx, my, "]", "err", err, "mext", mextent)
bext := geom.NewBBox(
[2]float64{
(mextent.MinX() * xspan / TileSize) + extent.MinX(),
(mextent.MinY() * yspan / TileSize) + extent.MinY(),
},
[2]float64{
(mextent.MaxX() * xspan / TileSize) + extent.MinX(),
(mextent.MaxY() * yspan / TileSize) + extent.MinY(),
},
)
log.Println("bext", bext)
*/

// TODO(arolek): support alternative SRIDs. Currently this assumes 3857
// Extent will return the tile extent excluding the tile's buffer and the Extent's SRID
func (t *Tile) Extent() (extent [2][2]float64, srid uint64) {
func (t *Tile) Extent() (extent *geom.BoundingBox, srid uint64) {
max := 20037508.34

// resolution
res := (max * 2) / math.Exp2(float64(t.z))

// unbuffered extent
return [2][2]float64{
{
return geom.NewBBox(
[2]float64{
-max + (float64(t.x) * res), // MinX
max - (float64(t.y) * res), // Miny
},
{
[2]float64{
-max + (float64(t.x) * res) + res, // MaxX
max - (float64(t.y) * res) - res, // MaxY

},
}, t.SRID
), t.SRID
}

// BufferedExtent will return the tile extent including the tile's buffer and the Extent's SRID
func (t *Tile) BufferedExtent() (bufferedExtent [2][2]float64, srid uint64) {
func (t *Tile) BufferedExtent() (bufferedExtent *geom.BoundingBox, srid uint64) {
extent, _ := t.Extent()

// TODO(arolek): the following value is hard coded for MVT, but this concept needs to be abstracted to support different projections
mvtTileWidthHeight := 4096.0
// the bounds / extent
mvtTileExtent := [2][2]float64{{0 - t.Buffer, 0 - t.Buffer}, {mvtTileWidthHeight + t.Buffer, mvtTileWidthHeight + t.Buffer}}

xspan := extent[1][0] - extent[0][0]
yspan := extent[1][1] - extent[0][1]
mvtTileExtent := [4]float64{
0 - t.Buffer, 0 - t.Buffer,
mvtTileWidthHeight + t.Buffer, mvtTileWidthHeight + t.Buffer,
}

bufferedExtent[0][0] = (mvtTileExtent[0][0] * xspan / mvtTileWidthHeight) + extent[0][0]
bufferedExtent[0][1] = (mvtTileExtent[0][1] * yspan / mvtTileWidthHeight) + extent[0][1]
bufferedExtent[1][0] = (mvtTileExtent[1][0] * xspan / mvtTileWidthHeight) + extent[0][0]
bufferedExtent[1][1] = (mvtTileExtent[1][1] * yspan / mvtTileWidthHeight) + extent[0][1]
xspan := extent.MaxX() - extent.MinX()
yspan := extent.MaxY() - extent.MinY()

bufferedExtent = geom.NewBBox(
[2]float64{
(mvtTileExtent[0] * xspan / mvtTileWidthHeight) + extent.MinX(),
(mvtTileExtent[1] * yspan / mvtTileWidthHeight) + extent.MinY(),
},
[2]float64{
(mvtTileExtent[2] * xspan / mvtTileWidthHeight) + extent.MinX(),
(mvtTileExtent[3] * yspan / mvtTileWidthHeight) + extent.MinY(),
},
)
return bufferedExtent, t.SRID
}
Loading

0 comments on commit 8a49707

Please sign in to comment.