-
-
Notifications
You must be signed in to change notification settings - Fork 38
Add tilegrid 4326 #92
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
cd9dd96
a873066
73f91f4
2f49eac
f9bc00a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,78 +1,56 @@ | ||
package slippy | ||
|
||
import "math" | ||
import ( | ||
"fmt" | ||
"math" | ||
|
||
// ==== lat lon (aka WGS 84) ==== | ||
"github.com/go-spatial/geom" | ||
) | ||
|
||
// Lat2Tile takes a zoom and a lat to produce the lon | ||
func Lat2Tile(zoom uint, lat float64) (y uint) { | ||
latRad := lat * math.Pi / 180 | ||
|
||
return uint(math.Exp2(float64(zoom))* | ||
(1.0-math.Log( | ||
math.Tan(latRad)+ | ||
(1/math.Cos(latRad)))/math.Pi)) / | ||
2.0 | ||
} | ||
|
||
// Lon2Tile takes in a zoom and lon to produce the lat | ||
func Lon2Tile(zoom uint, lon float64) (x uint) { | ||
return uint(math.Exp2(float64(zoom)) * (lon + 180.0) / 360.0) | ||
type extents struct { | ||
NativeExtents *geom.Extent | ||
WGS84Extents *geom.Extent | ||
Grid TileGrid | ||
} | ||
|
||
// Tile2Lon will return the west most longitude | ||
func Tile2Lon(zoom, x uint) float64 { return float64(x)/math.Exp2(float64(zoom))*360.0 - 180.0 } | ||
|
||
// Tile2Lat will return the north most latitude | ||
func Tile2Lat(zoom, y uint) float64 { | ||
var n float64 = math.Pi | ||
if y != 0 { | ||
n = math.Pi - 2.0*math.Pi*float64(y)/math.Exp2(float64(zoom)) | ||
} | ||
|
||
return 180.0 / math.Pi * math.Atan(0.5*(math.Exp(n)-math.Exp(-n))) | ||
// SupportedProjections contains supported projection native and lat/long extents as well as tile layout ratio | ||
var SupportedProjections = map[uint]extents{ | ||
3857: extents{NativeExtents: &geom.Extent{-20026376.39, -20048966.10, 20026376.39, 20048966.10}, WGS84Extents: &geom.Extent{-180.0, -85.0511, 180.0, 85.0511}, Grid: GetGrid(3857)}, | ||
4326: extents{NativeExtents: &geom.Extent{-180.0, -90.0, 180.0, 90.0}, WGS84Extents: &geom.Extent{-180.0, -90.0, 180.0, 90.0}, Grid: GetGrid(4326)}, | ||
} | ||
|
||
// ==== Web Mercator ==== | ||
|
||
// WebMercatorMax is the max size in meters of a tile | ||
const WebMercatorMax = 20037508.34 | ||
|
||
// Tile2WebX returns the side of the tile in the -x side | ||
func Tile2WebX(zoom uint, n uint) float64 { | ||
// Tile2WebX returns the side of the tile in the -x side in webmercator | ||
func Tile2WebX(zoom uint, n uint, srid uint) float64 { | ||
res := (WebMercatorMax * 2) / math.Exp2(float64(zoom)) | ||
|
||
return -WebMercatorMax + float64(n)*res | ||
} | ||
|
||
// Tile2WebY returns the side of the tile in the +y side | ||
func Tile2WebY(zoom uint, n uint) float64 { | ||
// Tile2WebY returns the side of the tile in the +y side in webmercator | ||
func Tile2WebY(zoom uint, n uint, srid uint) float64 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The extra arg isn't really necessary here. If it's being used elsewhere to satisfy |
||
res := (WebMercatorMax * 2) / math.Exp2(float64(zoom)) | ||
|
||
return WebMercatorMax - float64(n)*res | ||
} | ||
|
||
// WebX2Tile returns the column of the tile given the web mercator x value | ||
func WebX2Tile(zoom uint, x float64) uint { | ||
res := (WebMercatorMax * 2) / math.Exp2(float64(zoom)) | ||
|
||
return uint((x + WebMercatorMax) / res) | ||
} | ||
|
||
// WebY2Tile returns the row of the tile given the web mercator y value | ||
func WebY2Tile(zoom uint, y float64) uint { | ||
res := (WebMercatorMax * 2) / math.Exp2(float64(zoom)) | ||
|
||
return uint(-(y - WebMercatorMax) / res) | ||
} | ||
|
||
// ==== pixels ==== | ||
|
||
// MvtTileDim is the number of pixels in a tile | ||
const MvtTileDim = 4096.0 | ||
|
||
// Pixels2Webs scalar conversion of pixels into web mercator units | ||
// PixelsToProjectedUnits scalar conversion of pixels into projected units | ||
// TODO (@ear7h): perhaps rethink this | ||
func Pixels2Webs(zoom uint, pixels uint) float64 { | ||
return WebMercatorMax * 2 / math.Exp2(float64(zoom)) * float64(pixels) / MvtTileDim | ||
func PixelsToProjectedUnits(zoom uint, pixels uint, srid uint) float64 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could be renamed |
||
switch srid { | ||
case 3857: | ||
return WebMercatorMax * 2 / math.Exp2(float64(zoom)) * float64(pixels) / MvtTileDim | ||
case 4326: | ||
return 360.0 / math.Exp2(float64(zoom)) * float64(pixels) / MvtTileDim / 2 | ||
default: | ||
panic(fmt.Sprintf("unsupported srid: %v", srid)) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
package slippy | ||
|
||
import ( | ||
"fmt" | ||
"math" | ||
) | ||
|
||
// TileGrid contains the tile layout, including ability to get WGS84 coordinates for tile extents | ||
type TileGrid interface { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From my understanding the
type TileGrid interface {
// returns the srid of the projection of this tiling scheme
// the geom types from the other methods will be in this projection.
SRID() int
// this should return the (exclusive) maximum tile in this zoom. AKA:
// Tile{z, MaxX+1, MaxY+1}.
// it is exclusive so this function can be easily used for boundary checks
Size(z uint) Tile
// converts from a point (in the tile scheme's projection) and zoom to a tile.
// ok will be false if the point is not valid in the projection
FromNative(z uint, pt geom.Point) (t *Tile, ok bool)
// Retuns the tile's upper left point. ok will be false if the tile is not valid.
// <strike>one thing to note about implementing this is that you can compare tiles directly:
// ok := tileScheme.Max(tile.Z) >= tile</strike>
// second thing to note is that the
ToNative(Tile) (pt geom.Point, ok bool)
}
// no need for the srid because f can be made a closure
func RangeFamilyAt(TileGrid, Tile, f func(*Tile) error) error
// Tile.ExtentXXXX aren't needed because of ToNative
// NewTileLatLon aren't needed beacuse of FromNative There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe that we'd still need a mechanism to support getting a tile's extent in both projected as well as WGS84 units to support downstream usage within Tegola. Part of the reasoning for separating tiling logic out to support equirectangular tile schemes is in support of tile rendering as "4326" and a non-square tile grid. But, again, I very well could be missing some of the vision for these changes. I'd love to chat further so that I'm on the same page! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was thinking that the conversion from one coordinate system to another should be decoupled from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Gotcha. Makes sense. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ear7h re: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @meilinger My mistake, that doesn't work 😬 They can be compared for equality but not ordering. |
||
ContainsIndex(zoom, x, y uint) bool | ||
GridSize(zoom uint) (x, y uint) | ||
MaxXY(zoom uint) (maxx, maxy uint) | ||
Lat2YIndex(zoom uint, lat float64) (gridy uint) | ||
Lon2XIndex(zoom uint, lon float64) (gridx uint) | ||
XIndex2Lon(zoom, x uint) (lon float64) | ||
YIndex2Lat(zoom, y uint) (lat float64) | ||
} | ||
|
||
func GetGrid(srid uint) TileGrid { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For convention, this function should be renamed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe that, from my experience, when someone mentions "4326" as the projection (which it is not), they are typically referring to equirectangular projection using lat/lon and WGS84 geoid upon a 2x1 tile ratio scheme (using square tiles, but 2x1 tile extent). But that is completely notional, not part of a spec and my experience is only a single data-point in usages. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I understand what's going on bit better now, thank you for the explanation. I always get confused with srid, projections, and datums. From what I've found after a brief search it seem like some programs expect the behavior you describe. So, I think the functionality here is good. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I hear ya, I still get confused as to difference of them as well. |
||
switch srid { | ||
case 4326: | ||
return &grid{tileExtentRatio: 2, srid: srid} | ||
case 3857: | ||
return &grid{tileExtentRatio: 1, srid: srid} | ||
default: | ||
panic(fmt.Sprintf("unsupported srid: %v", srid)) | ||
} | ||
} | ||
|
||
type grid struct { | ||
tileExtentRatio float64 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you document what this ratio is? x/y or y/x |
||
srid uint | ||
} | ||
|
||
func (g *grid) ContainsIndex(zoom, x, y uint) bool { | ||
xsize, ysize := g.GridSize(zoom) | ||
if x < xsize && y < ysize { | ||
return true | ||
} | ||
|
||
return false | ||
} | ||
|
||
func (g *grid) GridSize(zoom uint) (x, y uint) { | ||
dim := uint(math.Exp2(float64(zoom))) | ||
return uint(float64(dim) * g.tileExtentRatio), dim | ||
} | ||
|
||
func (g *grid) MaxXY(zoom uint) (maxx, maxy uint) { | ||
xsize, ysize := g.GridSize(zoom) | ||
|
||
return xsize - 1, ysize - 1 | ||
} | ||
|
||
func (g *grid) Lat2YIndex(zoom uint, lat float64) (gridy uint) { | ||
switch g.srid { | ||
case 3857: | ||
latRad := lat * math.Pi / 180 | ||
return uint(math.Exp2(float64(zoom))* | ||
(1.0-math.Log( | ||
math.Tan(latRad)+ | ||
(1/math.Cos(latRad)))/math.Pi)) / | ||
2.0 | ||
case 4326: | ||
return uint(math.Exp2(float64(zoom)) * -(lat - 90.0) / (360.0 / g.tileExtentRatio)) | ||
default: | ||
panic(fmt.Sprintf("unsupported srid: %v", g.srid)) | ||
} | ||
} | ||
|
||
func (g *grid) Lon2XIndex(zoom uint, lon float64) (gridx uint) { | ||
switch g.srid { | ||
case 3857: | ||
fallthrough | ||
case 4326: | ||
return uint(math.Exp2(float64(zoom)) * (lon + 180.0) / (360.0 / g.tileExtentRatio)) | ||
default: | ||
panic(fmt.Sprintf("unsupported srid: %v", g.srid)) | ||
} | ||
} | ||
|
||
func (g *grid) XIndex2Lon(zoom, x uint) (lon float64) { | ||
switch g.srid { | ||
case 3857: | ||
fallthrough | ||
case 4326: | ||
return float64(x)/math.Exp2(float64(zoom))*(360.0/g.tileExtentRatio) - 180.0 | ||
default: | ||
panic(fmt.Sprintf("unsupported srid: %v", g.srid)) | ||
} | ||
} | ||
|
||
func (g *grid) YIndex2Lat(zoom, y uint) (lat float64) { | ||
switch g.srid { | ||
case 3857: | ||
var n = math.Pi | ||
if y != 0 { | ||
n = math.Pi - 2.0*math.Pi*float64(y)/math.Exp2(float64(zoom)) | ||
} | ||
|
||
return 180.0 / math.Pi * math.Atan(0.5*(math.Exp(n)-math.Exp(-n))) | ||
case 4326: | ||
return -(180.0/math.Exp2(float64(zoom))*float64(y) - 90.0) | ||
default: | ||
panic(fmt.Sprintf("unsupported srid: %v", g.srid)) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The extra arg isn't really necessary here. If it's being used elsewhere to satisfy func (uint, uint, uint) float64 signature, a closure should be made there. Since there there isn't a type requirement in this package it shouldn't be forced here.