Skip to content

Commit

Permalink
feat: enable larger map for google static map.
Browse files Browse the repository at this point in the history
normally maximum static map  by google only 640.
NOTE: only works with 256 tile size
  • Loading branch information
galihrivanto committed Feb 14, 2023
1 parent f46d35b commit dc9c34e
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 25 deletions.
81 changes: 67 additions & 14 deletions context.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,13 +237,7 @@ func (m *Context) Attribution() string {
}

func (m *Context) tileSize() int {
// if layer is tile provider
tileSize := 256
if p, ok := m.tileProvider.(MapTileProvider); ok {
tileSize = p.TileSize()
}

return tileSize
return m.tileProvider.TileSize()
}

func (m *Context) determineBounds() s2.Rect {
Expand Down Expand Up @@ -512,6 +506,7 @@ func (m *Context) Render() (image.Image, error) {
tileSize := m.tileSize()
trans := newTransformer(m.width, m.height, zoom, center, tileSize)
img := image.NewRGBA(image.Rect(0, 0, trans.pWidth, trans.pHeight))

gc := gg.NewContextForRGBA(img)
if m.background != nil {
draw.Draw(img, img.Bounds(), &image.Uniform{m.background}, image.Point{}, draw.Src)
Expand Down Expand Up @@ -571,6 +566,7 @@ func (m *Context) RenderWithTransformer() (image.Image, *Transformer, error) {
tileSize := m.tileSize()
trans := newTransformer(m.width, m.height, zoom, center, tileSize)
img := image.NewRGBA(image.Rect(0, 0, trans.pWidth, trans.pHeight))

gc := gg.NewContextForRGBA(img)
if m.background != nil {
draw.Draw(img, img.Bounds(), &image.Uniform{m.background}, image.Point{}, draw.Src)
Expand Down Expand Up @@ -622,6 +618,39 @@ func (m *Context) RenderWithBounds() (image.Image, s2.Rect, error) {
return img, trans.Rect(), nil
}

// tileCallback .
type tileCallback func(zoom, x, y int, center s2.LatLng)

// loopTiles .
func loopTiles(tileSize, zoom int, trans *Transformer, cb tileCallback) {
tiles := (1 << uint(zoom))
for xx := 0; xx < trans.tCountX; xx++ {
x := trans.tOriginX + xx
if x < 0 {
x = x + tiles
} else if x >= tiles {
x = x - tiles
}
if x < 0 || x >= tiles {
log.Printf("Skipping out of bounds tile column %d/?", x)
continue
}
for yy := 0; yy < trans.tCountY; yy++ {
y := trans.tOriginY + yy
if y < 0 || y >= tiles {
log.Printf("Skipping out of bounds tile %d/%d", x, y)
continue
}

px := trans.pCenterX + (xx * tileSize)
py := trans.pCenterY + (yy * tileSize)
cc := trans.XYToLatLng(float64(px), float64(py))

cb(zoom, xx, yy, cc)
}
}
}

func (m *Context) renderTileLayer(gc *gg.Context, zoom int, trans *Transformer, tileSize int, provider MapTileProvider) error {
var wg sync.WaitGroup
tiles := (1 << uint(zoom))
Expand Down Expand Up @@ -677,20 +706,44 @@ func (m *Context) renderTileLayer(gc *gg.Context, zoom int, trans *Transformer,
}

func (m *Context) renderStaticMap(gc *gg.Context, zoom int, center s2.LatLng, trans *Transformer, provider StaticMapProvider) error {
fetchedTiles := make(chan *StaticMapTile)
t := NewStaticMapFetcher(provider, m.cache, m.online)
if m.userAgent != "" {
t.SetUserAgent(m.userAgent)
}

mm := &StaticMap{Zoom: zoom, X: center.Lng.Degrees(), Y: center.Lat.Degrees(), Width: trans.pWidth, Height: trans.pHeight}
err := t.Fetch(mm)
if err != nil {
log.Printf("Error downloading static map file: %s", err)
}
tileSize := m.tileSize()
var wg sync.WaitGroup
go func() {
loopTiles(m.tileSize(), zoom, trans, func(zoom, x, y int, center s2.LatLng) {
wg.Add(1)
mm := &StaticMapTile{
Zoom: zoom,
CenterX: center.Lng.Degrees(), CenterY: center.Lat.Degrees(),
X: x, Y: y, Size: tileSize}
go func(wg *sync.WaitGroup, tile *StaticMapTile, xx, yy int) {
defer wg.Done()
if err := t.Fetch(mm); err == nil {
tile.X = xx * tileSize
tile.Y = yy * tileSize
fetchedTiles <- mm
} else if err == errTileNotFound {
log.Printf("Error downloading tile file: %s (Ignored)", err)
} else {
log.Printf("Error downloading tile file: %s", err)
}
}(&wg, mm, x, y)
})

gc.DrawImage(mm.Img, 0, 0)
wg.Wait()
close(fetchedTiles)
}()

return err
for tile := range fetchedTiles {
gc.DrawImage(tile.Img, tile.X, tile.Y)
}

return nil
}

func (m *Context) renderLayer(gc *gg.Context, zoom int, center s2.LatLng, trans *Transformer, tileSize int, provider TileProvider) error {
Expand Down
23 changes: 21 additions & 2 deletions examples/google-static-map/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"fmt"
"log"
"os"

sm "github.com/flopp/go-staticmaps"
Expand Down Expand Up @@ -77,6 +78,12 @@ func (p *gMapProvider) Attribution() string {
return p.attribution
}

func (p *gMapProvider) TileSize() int {
// maximum google static width and height is 640
// use moderate size and lets library merge tiles
return 256
}

func (p *gMapProvider) GetURL(zoom int, x, y float64, width, height int) string {
// construct google static map url
var url string
Expand All @@ -98,20 +105,32 @@ func (p *gMapProvider) GetURL(zoom int, x, y float64, width, height int) string
url += fmt.Sprintf("&zoom=%d", zoom)
}

for _, style := range p.options.styles {
url += "&style=" + style
}

log.Println("url", url)

return url
}

func main() {
ctx := sm.NewContext()
ctx.SetSize(400, 300)
ctx.SetCenter(s2.LatLngFromDegrees(1.3011624468555132, 103.85775516239742))
ctx.SetZoom(17)
ctx.SetZoom(19)
ctx.SetSize(2048, 2048)
ctx.SetTileProvider(
GMapTileProvider(
GMapKey(os.Getenv("GOOGLE_MAP_KEY")),
GMapStyles(
"feature:poi|visibility:off",
),
),
)

// no cache
ctx.SetCache(nil)

img, err := ctx.Render()
if err != nil {
panic(err)
Expand Down
16 changes: 8 additions & 8 deletions static_map_fetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ type StaticMapFetcher struct {
}

// StaticMap defines a staticMapFile
type StaticMap struct {
Img image.Image
X, Y float64
Zoom, Width, Height int
type StaticMapTile struct {
Img image.Image
CenterX, CenterY float64
X, Y, Zoom, Size int
}

// NewStaticMapFetcher creates a new NewStaticMapFetcher struct
Expand Down Expand Up @@ -53,17 +53,17 @@ func cacheStaticMapFileName(cache TileCache, providerName string, zoom int, x, y
}

// Fetch download (or retrieves from the cache) a tile image for the specified zoom level and tile coordinates
func (t *StaticMapFetcher) Fetch(m *StaticMap) error {
func (t *StaticMapFetcher) Fetch(m *StaticMapTile) error {
if t.cache != nil {
fileName := cacheStaticMapFileName(t.cache, t.provider.Name(), m.Zoom, m.X, m.Y)
fileName := cacheStaticMapFileName(t.cache, t.provider.Name(), m.Zoom, m.CenterX, m.CenterY)
cachedImg, err := t.loadCache(fileName)
if err == nil {
m.Img = cachedImg
return nil
}
}

url := t.provider.GetURL(m.Zoom, m.X, m.Y, m.Width, m.Height)
url := t.provider.GetURL(m.Zoom, m.CenterX, m.CenterY, m.Size, m.Size)
data, err := t.download(url)
if err != nil {
return err
Expand All @@ -75,7 +75,7 @@ func (t *StaticMapFetcher) Fetch(m *StaticMap) error {
}

if t.cache != nil {
fileName := cacheStaticMapFileName(t.cache, t.provider.Name(), m.Zoom, m.X, m.Y)
fileName := cacheStaticMapFileName(t.cache, t.provider.Name(), m.Zoom, m.CenterX, m.CenterY)
if err := t.storeCache(fileName, data); err != nil {
log.Printf("Failed to store map tile as '%s': %s", fileName, err)
}
Expand Down
2 changes: 1 addition & 1 deletion tile_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import "fmt"
type TileProvider interface {
Name() string
Attribution() string
TileSize() int
}

// StaticMapProvider .
Expand All @@ -23,7 +24,6 @@ type StaticMapProvider interface {
type MapTileProvider interface {
TileProvider
IgnoreNotFound() bool
TileSize() int
Shards() []string
GetURL(shard string, zoom, x, y int) string
}
Expand Down

0 comments on commit dc9c34e

Please sign in to comment.