summaryrefslogtreecommitdiff
path: root/vendor/github.com/flopp
diff options
context:
space:
mode:
authorFelix Hanley <felix@userspace.com.au>2016-11-21 15:56:46 +0000
committerFelix Hanley <felix@userspace.com.au>2016-11-21 15:56:46 +0000
commit411565dc3c87851017376545383d4afa65d9f833 (patch)
tree44733ff8242c193a95115b27f9e4e88ad3eadde1 /vendor/github.com/flopp
parent98da73fe927ee67b62c1f286b0adb649a20c373c (diff)
downloadcrjw-maps-411565dc3c87851017376545383d4afa65d9f833.tar.gz
crjw-maps-411565dc3c87851017376545383d4afa65d9f833.tar.bz2
Add vendor code
Diffstat (limited to 'vendor/github.com/flopp')
-rw-r--r--vendor/github.com/flopp/go-coordsparser/LICENSE21
-rw-r--r--vendor/github.com/flopp/go-coordsparser/coordsparser.go172
-rw-r--r--vendor/github.com/flopp/go-staticmaps/LICENSE21
-rw-r--r--vendor/github.com/flopp/go-staticmaps/area.go94
-rw-r--r--vendor/github.com/flopp/go-staticmaps/color.go65
-rw-r--r--vendor/github.com/flopp/go-staticmaps/context.go277
-rw-r--r--vendor/github.com/flopp/go-staticmaps/create-static-map/create-static-map.go134
-rw-r--r--vendor/github.com/flopp/go-staticmaps/map_object.go13
-rw-r--r--vendor/github.com/flopp/go-staticmaps/marker.go125
-rw-r--r--vendor/github.com/flopp/go-staticmaps/path.go103
-rw-r--r--vendor/github.com/flopp/go-staticmaps/tile_fetcher.go153
-rw-r--r--vendor/github.com/flopp/go-staticmaps/tile_provider.go98
-rw-r--r--vendor/github.com/flopp/go-staticmaps/util.go18
13 files changed, 1294 insertions, 0 deletions
diff --git a/vendor/github.com/flopp/go-coordsparser/LICENSE b/vendor/github.com/flopp/go-coordsparser/LICENSE
new file mode 100644
index 0000000..7417b3d
--- /dev/null
+++ b/vendor/github.com/flopp/go-coordsparser/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Florian Pigorsch
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/flopp/go-coordsparser/coordsparser.go b/vendor/github.com/flopp/go-coordsparser/coordsparser.go
new file mode 100644
index 0000000..6836892
--- /dev/null
+++ b/vendor/github.com/flopp/go-coordsparser/coordsparser.go
@@ -0,0 +1,172 @@
+// Copyright 2016 Florian Pigorsch. All rights reserved.
+//
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package coordsparser is a library for parsing (geographic) coordinates in various string formats
+package coordsparser
+
+import (
+ "fmt"
+ "regexp"
+ "strconv"
+)
+
+// Parse parses a coordinate string and returns a lat/lng pair or an error
+func Parse(s string) (float64, float64, error) {
+ lat, lng, err := ParseD(s)
+ if err == nil {
+ return lat, lng, nil
+ }
+
+ lat, lng, err = ParseHD(s)
+ if err == nil {
+ return lat, lng, nil
+ }
+
+ lat, lng, err = ParseHDM(s)
+ if err == nil {
+ return lat, lng, nil
+ }
+
+ lat, lng, err = ParseHDMS(s)
+ if err == nil {
+ return lat, lng, nil
+ }
+
+ return 0, 0, fmt.Errorf("Cannot parse coordinates: %s", s)
+}
+
+// ParseD parses a coordinate string of the form "D.DDDD D.DDDD" and returns a lat/lng pair or an error
+func ParseD(s string) (float64, float64, error) {
+ re := regexp.MustCompile(`^\s*([+-]?[\d\.]+)\s*(,|;|:|\s)\s*([+-]?[\d\.]+)\s*$`)
+
+ matches := re.FindStringSubmatch(s)
+ if matches == nil {
+ return 0, 0, fmt.Errorf("Cannot parse 'D' string: %s", s)
+ }
+
+ lat, err := strconv.ParseFloat(matches[1], 64)
+ if err != nil || lat < -90 || lat > 90 {
+ return 0, 0, fmt.Errorf("Cannot parse 'D' string: %s", s)
+ }
+
+ lng, err := strconv.ParseFloat(matches[3], 64)
+ if err != nil || lng < -180 || lng > 180 {
+ return 0, 0, fmt.Errorf("Cannot parse 'D' string: %s", s)
+ }
+
+ return lat, lng, nil
+}
+
+// ParseHD parses a coordinate string of the form "H D.DDDD H D.DDDD" and returns a lat/lng pair or an error
+func ParseHD(s string) (float64, float64, error) {
+ re := regexp.MustCompile(`^\s*([NnSs])\s*([\d\.]+)\s+([EeWw])\s*([\d\.]+)\s*$`)
+
+ matches := re.FindStringSubmatch(s)
+ if matches == nil {
+ return 0, 0, fmt.Errorf("Cannot parse 'HD' string: %s", s)
+ }
+
+ lat, err := strconv.ParseFloat(matches[2], 64)
+ if err != nil || lat > 90 {
+ return 0, 0, fmt.Errorf("Cannot parse 'HD' string: %s", s)
+ }
+ if matches[1] == "S" || matches[1] == "s" {
+ lat = -lat
+ }
+
+ lng, err := strconv.ParseFloat(matches[4], 64)
+ if err != nil || lng > 180 {
+ return 0, 0, fmt.Errorf("Cannot parse 'HD' string: %s", s)
+ }
+ if matches[3] == "W" || matches[3] == "w" {
+ lng = -lng
+ }
+
+ return lat, lng, nil
+}
+
+// ParseHDM parses a coordinate string of the form "H D M.MMM H D M.MMM" and returns a lat/lng pair or an error
+func ParseHDM(s string) (float64, float64, error) {
+ re := regexp.MustCompile(`^\s*([NnSs])\s*([\d]+)\s+([\d.]+)\s+([EeWw])\s*([\d]+)\s+([\d.]+)\s*$`)
+
+ matches := re.FindStringSubmatch(s)
+ if matches == nil {
+ return 0, 0, fmt.Errorf("Cannot parse 'HDM' string: %s", s)
+ }
+
+ latDeg, err := strconv.ParseFloat(matches[2], 64)
+ if err != nil || latDeg > 90 {
+ return 0, 0, fmt.Errorf("Cannot parse 'HDM' string: %s", s)
+ }
+ latMin, err := strconv.ParseFloat(matches[3], 64)
+ if err != nil || latMin >= 60 {
+ return 0, 0, fmt.Errorf("Cannot parse 'HDM' string: %s", s)
+ }
+ lat := latDeg + latMin/60.0
+ if matches[1] == "S" || matches[1] == "s" {
+ lat = -lat
+ }
+
+ lngDeg, err := strconv.ParseFloat(matches[5], 64)
+ if err != nil || lngDeg > 180 {
+ return 0, 0, fmt.Errorf("Cannot parse 'HDM' string: %s", s)
+ }
+ lngMin, err := strconv.ParseFloat(matches[6], 64)
+ if err != nil || lngMin >= 60 {
+ return 0, 0, fmt.Errorf("Cannot parse 'HDM' string: %s", s)
+ }
+ lng := lngDeg + lngMin/60.0
+ if matches[4] == "W" || matches[4] == "w" {
+ lng = -lng
+ }
+
+ return lat, lng, nil
+}
+
+// ParseHDMS parses a coordinate string of the form "H D M S.SSS H D M S.SSS" and returns a lat/lng pair or an error
+func ParseHDMS(s string) (float64, float64, error) {
+ re := regexp.MustCompile(`^\s*([NnSs])\s*([\d]+)\s+([\d]+)\s+([\d.]+)\s+([EeWw])\s*([\d]+)\s+([\d]+)\s+([\d.]+)\s*$`)
+
+ matches := re.FindStringSubmatch(s)
+ if matches == nil {
+ return 0, 0, fmt.Errorf("Cannot parse 'HDMS' string: %s", s)
+ }
+
+ latDeg, err := strconv.ParseFloat(matches[2], 64)
+ if err != nil || latDeg > 90 {
+ return 0, 0, fmt.Errorf("Cannot parse 'HDMS' string: %s", s)
+ }
+ latMin, err := strconv.ParseFloat(matches[3], 64)
+ if err != nil || latMin >= 60 {
+ return 0, 0, fmt.Errorf("Cannot parse 'HDMS' string: %s", s)
+ }
+ latSec, err := strconv.ParseFloat(matches[4], 64)
+ if err != nil || latSec >= 60 {
+ return 0, 0, fmt.Errorf("Cannot parse 'HDMS' string: %s", s)
+ }
+ lat := latDeg + latMin/60.0 + latSec/3600.0
+ if matches[1] == "S" || matches[1] == "s" {
+ lat = -lat
+ }
+
+ lngDeg, err := strconv.ParseFloat(matches[6], 64)
+ if err != nil || lngDeg > 180 {
+ return 0, 0, fmt.Errorf("Cannot parse 'HDMS' string: %s", s)
+ }
+ lngMin, err := strconv.ParseFloat(matches[7], 64)
+ if err != nil || lngMin >= 60 {
+ return 0, 0, fmt.Errorf("Cannot parse 'HDMS' string: %s", s)
+ }
+ lngSec, err := strconv.ParseFloat(matches[8], 64)
+ if err != nil || lngSec >= 60 {
+ return 0, 0, fmt.Errorf("Cannot parse 'HDMS' string: %s", s)
+ }
+ lng := lngDeg + lngMin/60.0 + lngSec/3600.0
+ if matches[5] == "W" || matches[5] == "w" {
+ lng = -lng
+ }
+
+ return lat, lng, nil
+}
diff --git a/vendor/github.com/flopp/go-staticmaps/LICENSE b/vendor/github.com/flopp/go-staticmaps/LICENSE
new file mode 100644
index 0000000..7417b3d
--- /dev/null
+++ b/vendor/github.com/flopp/go-staticmaps/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Florian Pigorsch
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/flopp/go-staticmaps/area.go b/vendor/github.com/flopp/go-staticmaps/area.go
new file mode 100644
index 0000000..387105a
--- /dev/null
+++ b/vendor/github.com/flopp/go-staticmaps/area.go
@@ -0,0 +1,94 @@
+// Copyright 2016 Florian Pigorsch. All rights reserved.
+//
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package sm
+
+import (
+ "image/color"
+ "strconv"
+ "strings"
+
+ "github.com/flopp/go-coordsparser"
+ "github.com/fogleman/gg"
+ "github.com/golang/geo/s2"
+)
+
+// Area represents a area or area on the map
+type Area struct {
+ MapObject
+ Positions []s2.LatLng
+ Color color.Color
+ Fill color.Color
+ Weight float64
+}
+
+// ParseAreaString parses a string and returns an area
+func ParseAreaString(s string) (*Area, error) {
+ area := new(Area)
+ area.Color = color.RGBA{0xff, 0, 0, 0xff}
+ area.Fill = color.Transparent
+ area.Weight = 5.0
+
+ for _, ss := range strings.Split(s, "|") {
+ if ok, suffix := hasPrefix(ss, "color:"); ok {
+ var err error
+ area.Color, err = ParseColorString(suffix)
+ if err != nil {
+ return nil, err
+ }
+ } else if ok, suffix := hasPrefix(ss, "fill:"); ok {
+ var err error
+ area.Fill, err = ParseColorString(suffix)
+ if err != nil {
+ return nil, err
+ }
+ } else if ok, suffix := hasPrefix(ss, "weight:"); ok {
+ var err error
+ area.Weight, err = strconv.ParseFloat(suffix, 64)
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ lat, lng, err := coordsparser.Parse(ss)
+ if err != nil {
+ return nil, err
+ }
+ area.Positions = append(area.Positions, s2.LatLngFromDegrees(lat, lng))
+ }
+
+ }
+ return area, nil
+}
+
+func (p *Area) extraMarginPixels() float64 {
+ return 0.5 * p.Weight
+}
+
+func (p *Area) bounds() s2.Rect {
+ r := s2.EmptyRect()
+ for _, ll := range p.Positions {
+ r = r.AddPoint(ll)
+ }
+ return r
+}
+
+func (p *Area) draw(gc *gg.Context, trans *transformer) {
+ if len(p.Positions) <= 1 {
+ return
+ }
+
+ gc.ClearPath()
+ gc.SetLineWidth(p.Weight)
+ gc.SetLineCap(gg.LineCapRound)
+ gc.SetLineJoin(gg.LineJoinRound)
+ for _, ll := range p.Positions {
+ gc.LineTo(trans.ll2p(ll))
+ }
+ gc.ClosePath()
+ gc.SetColor(p.Fill)
+ gc.FillPreserve()
+ gc.SetColor(p.Color)
+ gc.Stroke()
+}
diff --git a/vendor/github.com/flopp/go-staticmaps/color.go b/vendor/github.com/flopp/go-staticmaps/color.go
new file mode 100644
index 0000000..ea73ffd
--- /dev/null
+++ b/vendor/github.com/flopp/go-staticmaps/color.go
@@ -0,0 +1,65 @@
+// Copyright 2016 Florian Pigorsch. All rights reserved.
+//
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package sm
+
+import (
+ "fmt"
+ "image/color"
+ "regexp"
+ "strings"
+)
+
+// ParseColorString parses hex color strings (i.e. `0xRRGGBB`, `#RRGGBB`, `0xRRGGBBAA`, `#RRGGBBAA`), and names colors (e.g. 'black', 'blue', ...)
+func ParseColorString(s string) (color.Color, error) {
+ s = strings.ToLower(strings.TrimSpace(s))
+
+ re := regexp.MustCompile(`^(0x|#)([A-Fa-f0-9]{6})$`)
+ matches := re.FindStringSubmatch(s)
+ if matches != nil {
+ var r, g, b int
+ fmt.Sscanf(matches[2], "%2x%2x%2x", &r, &g, &b)
+ return color.RGBA{uint8(r), uint8(g), uint8(b), 0xff}, nil
+ }
+
+ re = regexp.MustCompile(`^(0x|#)([A-Fa-f0-9]{8})$`)
+ matches = re.FindStringSubmatch(s)
+ if matches != nil {
+ var r, g, b, a int
+ fmt.Sscanf(matches[2], "%2x%2x%2x%2x", &r, &g, &b, &a)
+ rr := float64(r) * float64(a) / 256.0
+ gg := float64(g) * float64(a) / 256.0
+ bb := float64(b) * float64(a) / 256.0
+ return color.RGBA{uint8(rr), uint8(gg), uint8(bb), uint8(a)}, nil
+ }
+
+ switch s {
+ case "black":
+ return color.RGBA{0x00, 0x00, 0x00, 0xff}, nil
+ case "blue":
+ return color.RGBA{0x00, 0x00, 0xff, 0xff}, nil
+ case "brown":
+ return color.RGBA{0x96, 0x4b, 0x00, 0xff}, nil
+ case "green":
+ return color.RGBA{0x00, 0xff, 0x00, 0xff}, nil
+ case "orange":
+ return color.RGBA{0xff, 0x7f, 0x00, 0xff}, nil
+ case "purple":
+ return color.RGBA{0x7f, 0x00, 0x7f, 0xff}, nil
+ case "red":
+ return color.RGBA{0xff, 0x00, 0x00, 0xff}, nil
+ case "yellow":
+ return color.RGBA{0xff, 0xff, 0x00, 0xff}, nil
+ case "white":
+ return color.RGBA{0xff, 0xff, 0xff, 0xff}, nil
+ }
+ return color.Transparent, fmt.Errorf("Cannot parse color string: %s", s)
+}
+
+// Luminance computes the luminance (~ brightness) of the given color. Range: 0.0 for black to 1.0 for white.
+func Luminance(col color.Color) float64 {
+ r, g, b, _ := col.RGBA()
+ return (float64(r)*0.299 + float64(g)*0.587 + float64(b)*0.114) / float64(0xffff)
+}
diff --git a/vendor/github.com/flopp/go-staticmaps/context.go b/vendor/github.com/flopp/go-staticmaps/context.go
new file mode 100644
index 0000000..3541f44
--- /dev/null
+++ b/vendor/github.com/flopp/go-staticmaps/context.go
@@ -0,0 +1,277 @@
+// Copyright 2016 Florian Pigorsch. All rights reserved.
+//
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package sm (~ static maps) renders static map images from OSM tiles with markers, paths, and filled areas.
+package sm
+
+import (
+ "errors"
+ "image"
+ "image/draw"
+ "math"
+
+ "github.com/fogleman/gg"
+ "github.com/golang/geo/s2"
+)
+
+// Context holds all information about the map image that is to be rendered
+type Context struct {
+ width int
+ height int
+
+ hasZoom bool
+ zoom int
+
+ hasCenter bool
+ center s2.LatLng
+
+ markers []*Marker
+ paths []*Path
+ areas []*Area
+
+ tileProvider *TileProvider
+}
+
+// NewContext creates a new instance of Context
+func NewContext() *Context {
+ t := new(Context)
+ t.width = 512
+ t.height = 512
+ t.hasZoom = false
+ t.hasCenter = false
+ t.tileProvider = NewTileProviderMapQuest()
+ return t
+}
+
+// SetTileProvider sets the TileProvider to be used
+func (m *Context) SetTileProvider(t *TileProvider) {
+ m.tileProvider = t
+}
+
+// SetSize sets the size of the generated image
+func (m *Context) SetSize(width, height int) {
+ m.width = width
+ m.height = height
+}
+
+// SetZoom sets the zoom level
+func (m *Context) SetZoom(zoom int) {
+ m.zoom = zoom
+ m.hasZoom = true
+}
+
+// SetCenter sets the center coordinates
+func (m *Context) SetCenter(center s2.LatLng) {
+ m.center = center
+ m.hasCenter = true
+}
+
+// AddMarker adds a marker to the Context
+func (m *Context) AddMarker(marker *Marker) {
+ m.markers = append(m.markers, marker)
+}
+
+// ClearMarkers removes all markers from the Context
+func (m *Context) ClearMarkers() {
+ m.markers = nil
+}
+
+// AddPath adds a path to the Context
+func (m *Context) AddPath(path *Path) {
+ m.paths = append(m.paths, path)
+}
+
+// ClearPaths removes all paths from the Context
+func (m *Context) ClearPaths() {
+ m.paths = nil
+}
+
+// AddArea adds an area to the Context
+func (m *Context) AddArea(area *Area) {
+ m.areas = append(m.areas, area)
+}
+
+// ClearAreas removes all areas from the Context
+func (m *Context) ClearAreas() {
+ m.areas = nil
+}
+
+func (m *Context) determineBounds() s2.Rect {
+ r := s2.EmptyRect()
+ for _, marker := range m.markers {
+ r = r.Union(marker.bounds())
+ }
+ for _, path := range m.paths {
+ r = r.Union(path.bounds())
+ }
+ for _, area := range m.areas {
+ r = r.Union(area.bounds())
+ }
+ return r
+}
+
+func (m *Context) determineExtraMarginPixels() float64 {
+ p := 0.0
+ for _, marker := range m.markers {
+ if pp := marker.extraMarginPixels(); pp > p {
+ p = pp
+ }
+ }
+ for _, path := range m.paths {
+ if pp := path.extraMarginPixels(); pp > p {
+ p = pp
+ }
+ }
+ for _, area := range m.areas {
+ if pp := area.extraMarginPixels(); pp > p {
+ p = pp
+ }
+ }
+ return p
+}
+
+func (m *Context) determineZoom(bounds s2.Rect, center s2.LatLng) int {
+ b := bounds.AddPoint(center)
+ if b.IsEmpty() || b.IsPoint() {
+ return 15
+ }
+
+ tileSize := m.tileProvider.TileSize
+ margin := 4.0 + m.determineExtraMarginPixels()
+ w := (float64(m.width) - 2.0*margin) / float64(tileSize)
+ h := (float64(m.height) - 2.0*margin) / float64(tileSize)
+ minX := (b.Lo().Lng.Degrees() + 180.0) / 360.0
+ maxX := (b.Hi().Lng.Degrees() + 180.0) / 360.0
+ minY := (1.0 - math.Log(math.Tan(b.Lo().Lat.Radians())+(1.0/math.Cos(b.Lo().Lat.Radians())))/math.Pi) / 2.0
+ maxY := (1.0 - math.Log(math.Tan(b.Hi().Lat.Radians())+(1.0/math.Cos(b.Hi().Lat.Radians())))/math.Pi) / 2.0
+ dx := math.Abs(maxX - minX)
+ dy := math.Abs(maxY - minY)
+
+ zoom := 1
+ for zoom < 30 {
+ tiles := float64(uint(1) << uint(zoom))
+ if dx*tiles > w || dy*tiles > h {
+ return zoom - 1
+ }
+ zoom = zoom + 1
+ }
+
+ return 15
+}
+
+type transformer struct {
+ zoom int
+ tileSize int
+ pWidth, pHeight int
+ pCenterX, pCenterY int
+ tCountX, tCountY int
+ tCenterX, tCenterY float64
+ tOriginX, tOriginY int
+}
+
+func newTransformer(width int, height int, zoom int, llCenter s2.LatLng, tileSize int) *transformer {
+ t := new(transformer)
+ t.zoom = zoom
+ t.tileSize = tileSize
+ t.tCenterX, t.tCenterY = t.ll2t(llCenter)
+
+ ww := float64(width) / float64(tileSize)
+ hh := float64(height) / float64(tileSize)
+
+ t.tOriginX = int(math.Floor(t.tCenterX - 0.5*ww))
+ t.tOriginY = int(math.Floor(t.tCenterY - 0.5*hh))
+
+ t.tCountX = 1 + int(math.Floor(t.tCenterX+0.5*ww)) - t.tOriginX
+ t.tCountY = 1 + int(math.Floor(t.tCenterY+0.5*hh)) - t.tOriginY
+
+ t.pWidth = t.tCountX * tileSize
+ t.pHeight = t.tCountY * tileSize
+
+ t.pCenterX = int((t.tCenterX - float64(t.tOriginX)) * float64(tileSize))
+ t.pCenterY = int((t.tCenterY - float64(t.tOriginY)) * float64(tileSize))
+
+ return t
+}
+
+func (t *transformer) ll2t(ll s2.LatLng) (float64, float64) {
+ tiles := math.Exp2(float64(t.zoom))
+ x := tiles * (ll.Lng.Degrees() + 180.0) / 360.0
+ y := tiles * (1 - math.Log(math.Tan(ll.Lat.Radians())+(1.0/math.Cos(ll.Lat.Radians())))/math.Pi) / 2.0
+ return x, y
+}
+
+func (t *transformer) ll2p(ll s2.LatLng) (float64, float64) {
+ x, y := t.ll2t(ll)
+ x = float64(t.pCenterX) + (x-t.tCenterX)*float64(t.tileSize)
+ y = float64(t.pCenterY) + (y-t.tCenterY)*float64(t.tileSize)
+ return x, y
+}
+
+// Render actually renders the map image including all map objects (markers, paths, areas)
+func (m *Context) Render() (image.Image, error) {
+ bounds := m.determineBounds()
+
+ center := m.center
+ if !m.hasCenter {
+ if bounds.IsEmpty() {
+ return nil, errors.New("No center coordinates specified, cannot determine center from markers")
+ }
+ center = bounds.Center()
+ }
+
+ zoom := m.zoom
+ if !m.hasZoom {
+ zoom = m.determineZoom(bounds, center)
+ }
+
+ tileSize := m.tileProvider.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)
+
+ // fetch and draw tiles to img
+ t := NewTileFetcher(m.tileProvider)
+ for xx := 0; xx < trans.tCountX; xx++ {
+ x := trans.tOriginX + xx
+ if x < 0 {
+ x = x + (1 << uint(zoom))
+ }
+ for yy := 0; yy < trans.tCountY; yy++ {
+ y := trans.tOriginY + yy
+ if tileImg, err := t.Fetch(zoom, x, y); err == nil {
+ gc.DrawImage(tileImg, xx*tileSize, yy*tileSize)
+ }
+ }
+ }
+
+ // draw map objects
+ for _, area := range m.areas {
+ area.draw(gc, trans)
+ }
+ for _, path := range m.paths {
+ path.draw(gc, trans)
+ }
+ for _, marker := range m.markers {
+ marker.draw(gc, trans)
+ }
+
+ // crop image
+ croppedImg := image.NewRGBA(image.Rect(0, 0, int(m.width), int(m.height)))
+ draw.Draw(croppedImg, image.Rect(0, 0, int(m.width), int(m.height)),
+ img, image.Point{trans.pCenterX - int(m.width)/2, trans.pCenterY - int(m.height)/2},
+ draw.Src)
+
+ // draw attribution
+ _, textHeight := gc.MeasureString(m.tileProvider.Attribution)
+ boxHeight := textHeight + 4.0
+ gc = gg.NewContextForRGBA(croppedImg)
+ gc.SetRGBA(0.0, 0.0, 0.0, 0.5)
+ gc.DrawRectangle(0.0, float64(m.height)-boxHeight, float64(m.width), boxHeight)
+ gc.Fill()
+ gc.SetRGBA(1.0, 1.0, 1.0, 0.75)
+ gc.DrawString(m.tileProvider.Attribution, 4.0, float64(m.height)-4.0)
+
+ return croppedImg, nil
+}
diff --git a/vendor/github.com/flopp/go-staticmaps/create-static-map/create-static-map.go b/vendor/github.com/flopp/go-staticmaps/create-static-map/create-static-map.go
new file mode 100644
index 0000000..7b8d429
--- /dev/null
+++ b/vendor/github.com/flopp/go-staticmaps/create-static-map/create-static-map.go
@@ -0,0 +1,134 @@
+// Copyright 2016 Florian Pigorsch. All rights reserved.
+//
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "fmt"
+ "log"
+ "os"
+ "sort"
+
+ "github.com/flopp/go-coordsparser"
+ "github.com/flopp/go-staticmaps"
+ "github.com/fogleman/gg"
+ "github.com/golang/geo/s2"
+ "github.com/jessevdk/go-flags"
+)
+
+func getTileProviderOrExit(name string) *sm.TileProvider {
+ tileProviders := sm.GetTileProviders()
+ tp := tileProviders[name]
+ if tp != nil {
+ return tp
+ }
+
+ if name != "list" {
+ fmt.Println("Bad map type:", name)
+ }
+ fmt.Println("Possible map types (to be used with --type/-t):")
+ // print sorted keys
+ keys := make([]string, 0, len(tileProviders))
+ for k := range tileProviders {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+ for _, k := range keys {
+ fmt.Println(k)
+ }
+ os.Exit(0)
+
+ return nil
+}
+
+func main() {
+ var opts struct {
+ // ClearCache bool `long:"clear-cache" description:"Clears the tile cache"`
+ Width int `long:"width" description:"Width of the generated static map image" value-name:"PIXELS" default:"512"`
+ Height int `long:"height" description:"Height of the generated static map image" value-name:"PIXELS" default:"512"`
+ Output string `short:"o" long:"output" description:"Output file name" value-name:"FILENAME" default:"map.png"`
+ Type string `short:"t" long:"type" description:"Select the map type; list possible map types with '--type list'" value-name:"MAPTYPE"`
+ Center string `short:"c" long:"center" description:"Center coordinates (lat,lng) of the static map" value-name:"LATLNG"`
+ Zoom int `short:"z" long:"zoom" description:"Zoom factor" value-name:"ZOOMLEVEL"`
+ Markers []string `short:"m" long:"marker" description:"Add a marker to the static map" value-name:"MARKER"`
+ Paths []string `short:"p" long:"path" description:"Add a path to the static map" value-name:"PATH"`
+ Areas []string `short:"a" long:"area" description:"Add an area to the static map" value-name:"AREA"`
+ }
+
+ parser := flags.NewParser(&opts, flags.HelpFlag|flags.PassDoubleDash)
+ parser.LongDescription = `Creates a static map`
+ _, err := parser.Parse()
+
+ if parser.FindOptionByLongName("help").IsSet() {
+ parser.WriteHelp(os.Stdout)
+ os.Exit(0)
+ }
+
+ ctx := sm.NewContext()
+
+ if parser.FindOptionByLongName("type").IsSet() {
+ tp := getTileProviderOrExit(opts.Type)
+ if tp != nil {
+ ctx.SetTileProvider(tp)
+ }
+ }
+
+ ctx.SetSize(opts.Width, opts.Height)
+
+ if parser.FindOptionByLongName("zoom").IsSet() {
+ ctx.SetZoom(opts.Zoom)
+ }
+
+ if parser.FindOptionByLongName("center").IsSet() {
+ lat, lng, err := coordsparser.Parse(opts.Center)
+ if err != nil {
+ log.Fatal(err)
+ } else {
+ ctx.SetCenter(s2.LatLngFromDegrees(lat, lng))
+ }
+ }
+
+ for _, markerString := range opts.Markers {
+ markers, err := sm.ParseMarkerString(markerString)
+ if err != nil {
+ log.Fatal(err)
+ } else {
+ for _, marker := range markers {
+ ctx.AddMarker(marker)
+ }
+ }
+ }
+
+ for _, pathString := range opts.Paths {
+ paths, err := sm.ParsePathString(pathString)
+ if err != nil {
+ log.Fatal(err)
+ } else {
+ for _, path := range paths {
+ ctx.AddPath(path)
+ }
+ }
+ }
+
+ for _, areaString := range opts.Areas {
+ area, err := sm.ParseAreaString(areaString)
+ if err != nil {
+ log.Fatal(err)
+ } else {
+ ctx.AddArea(area)
+ }
+ }
+
+ img, err := ctx.Render()
+ if err != nil {
+ log.Fatal(err)
+ return
+ }
+
+ if err = gg.SavePNG(opts.Output, img); err != nil {
+ log.Fatal(err)
+ return
+ }
+}
diff --git a/vendor/github.com/flopp/go-staticmaps/map_object.go b/vendor/github.com/flopp/go-staticmaps/map_object.go
new file mode 100644
index 0000000..d174b51
--- /dev/null
+++ b/vendor/github.com/flopp/go-staticmaps/map_object.go
@@ -0,0 +1,13 @@
+package sm
+
+import (
+ "github.com/fogleman/gg"
+ "github.com/golang/geo/s2"
+)
+
+// MapObject is the interface for all objects on the map
+type MapObject interface {
+ bounds() s2.Rect
+ extraMarginPixels() float64
+ draw(dc *gg.Context, trans *transformer)
+}
diff --git a/vendor/github.com/flopp/go-staticmaps/marker.go b/vendor/github.com/flopp/go-staticmaps/marker.go
new file mode 100644
index 0000000..6e88984
--- /dev/null
+++ b/vendor/github.com/flopp/go-staticmaps/marker.go
@@ -0,0 +1,125 @@
+// Copyright 2016 Florian Pigorsch. All rights reserved.
+//
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package sm
+
+import (
+ "fmt"
+ "image/color"
+ "math"
+ "strconv"
+ "strings"
+
+ "github.com/flopp/go-coordsparser"
+ "github.com/fogleman/gg"
+ "github.com/golang/geo/s2"
+)
+
+// Marker represents a marker on the map
+type Marker struct {
+ MapObject
+ Position s2.LatLng
+ Color color.Color
+ Size float64
+ Label string
+}
+
+// NewMarker creates a new Marker
+func NewMarker(pos s2.LatLng, col color.Color, size float64) *Marker {
+ m := new(Marker)
+ m.Position = pos
+ m.Color = col
+ m.Size = size
+ m.Label = ""
+ return m
+}
+
+func parseSizeString(s string) (float64, error) {
+ switch {
+ case s == "mid":
+ return 16.0, nil
+ case s == "small":
+ return 12.0, nil
+ case s == "tiny":
+ return 8.0, nil
+ }
+
+ if ss, err := strconv.ParseFloat(s, 64); err != nil && ss > 0 {
+ return ss, nil
+ }
+
+ return 0.0, fmt.Errorf("Cannot parse size string: %s", s)
+}
+
+// ParseMarkerString parses a string and returns an array of markers
+func ParseMarkerString(s string) ([]*Marker, error) {
+ markers := make([]*Marker, 0, 0)
+
+ var color color.Color = color.RGBA{0xff, 0, 0, 0xff}
+ size := 16.0
+ label := ""
+
+ for _, ss := range strings.Split(s, "|") {
+ if ok, suffix := hasPrefix(ss, "color:"); ok {
+ var err error
+ color, err = ParseColorString(suffix)
+ if err != nil {
+ return nil, err
+ }
+ } else if ok, suffix := hasPrefix(ss, "label:"); ok {
+ label = suffix
+ } else if ok, suffix := hasPrefix(ss, "size:"); ok {
+ var err error
+ size, err = parseSizeString(suffix)
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ lat, lng, err := coordsparser.Parse(ss)
+ if err != nil {
+ return nil, err
+ }
+ m := NewMarker(s2.LatLngFromDegrees(lat, lng), color, size)
+ m.Label = label
+ markers = append(markers, m)
+ }
+ }
+ return markers, nil
+}
+
+func (m *Marker) extraMarginPixels() float64 {
+ return 1.0 + 1.5*m.Size
+}
+
+func (m *Marker) bounds() s2.Rect {
+ r := s2.EmptyRect()
+ r = r.AddPoint(m.Position)
+ return r
+}
+
+func (m *Marker) draw(gc *gg.Context, trans *transformer) {
+ gc.ClearPath()
+ gc.SetLineJoin(gg.LineJoinRound)
+ gc.SetLineWidth(1.0)
+
+ radius := 0.5 * m.Size
+ x, y := trans.ll2p(m.Position)
+ gc.DrawArc(x, y-m.Size, radius, (90.0+60.0)*math.Pi/180.0, (360.0+90.0-60.0)*math.Pi/180.0)
+ gc.LineTo(x, y)
+ gc.ClosePath()
+ gc.SetColor(m.Color)
+ gc.FillPreserve()
+ gc.SetRGB(0, 0, 0)
+ gc.Stroke()
+
+ if m.Label != "" {
+ if Luminance(m.Color) >= 0.5 {
+ gc.SetColor(color.RGBA{0x00, 0x00, 0x00, 0xff})
+ } else {
+ gc.SetColor(color.RGBA{0xff, 0xff, 0xff, 0xff})
+ }
+ gc.DrawStringAnchored(m.Label, x, y-m.Size, 0.5, 0.5)
+ }
+}
diff --git a/vendor/github.com/flopp/go-staticmaps/path.go b/vendor/github.com/flopp/go-staticmaps/path.go
new file mode 100644
index 0000000..1c580e6
--- /dev/null
+++ b/vendor/github.com/flopp/go-staticmaps/path.go
@@ -0,0 +1,103 @@
+// Copyright 2016 Florian Pigorsch. All rights reserved.
+//
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package sm
+
+import (
+ "image/color"
+ "strconv"
+ "strings"
+
+ "github.com/flopp/go-coordsparser"
+ "github.com/fogleman/gg"
+ "github.com/golang/geo/s2"
+ "github.com/tkrajina/gpxgo/gpx"
+)
+
+// Path represents a path or area on the map
+type Path struct {
+ MapObject
+ Positions []s2.LatLng
+ Color color.Color
+ Weight float64
+}
+
+// ParsePathString parses a string and returns a path
+func ParsePathString(s string) ([]*Path, error) {
+ paths := make([]*Path, 0, 0)
+ currentPath := new(Path)
+ currentPath.Color = color.RGBA{0xff, 0, 0, 0xff}
+ currentPath.Weight = 5.0
+
+ for _, ss := range strings.Split(s, "|") {
+ if ok, suffix := hasPrefix(ss, "color:"); ok {
+ var err error
+ if currentPath.Color, err = ParseColorString(suffix); err != nil {
+ return nil, err
+ }
+ } else if ok, suffix := hasPrefix(ss, "weight:"); ok {
+ var err error
+ if currentPath.Weight, err = strconv.ParseFloat(suffix, 64); err != nil {
+ return nil, err
+ }
+ } else if ok, suffix := hasPrefix(ss, "gpx:"); ok {
+ gpxData, err := gpx.ParseFile(suffix)
+ if err != nil {
+ return nil, err
+ }
+ for _, trk := range gpxData.Tracks {
+ for _, seg := range trk.Segments {
+ p := new(Path)
+ p.Color = currentPath.Color
+ p.Weight = currentPath.Weight
+ for _, pt := range seg.Points {
+ p.Positions = append(p.Positions, s2.LatLngFromDegrees(pt.GetLatitude(), pt.GetLongitude()))
+ }
+ if len(p.Positions) > 0 {
+ paths = append(paths, p)
+ }
+ }
+ }
+ } else {
+ lat, lng, err := coordsparser.Parse(ss)
+ if err != nil {
+ return nil, err
+ }
+ currentPath.Positions = append(currentPath.Positions, s2.LatLngFromDegrees(lat, lng))
+ }
+ }
+ if len(currentPath.Positions) > 0 {
+ paths = append(paths, currentPath)
+ }
+ return paths, nil
+}
+
+func (p *Path) extraMarginPixels() float64 {
+ return 0.5 * p.Weight
+}
+
+func (p *Path) bounds() s2.Rect {
+ r := s2.EmptyRect()
+ for _, ll := range p.Positions {
+ r = r.AddPoint(ll)
+ }
+ return r
+}
+
+func (p *Path) draw(gc *gg.Context, trans *transformer) {
+ if len(p.Positions) <= 1 {
+ return
+ }
+
+ gc.ClearPath()
+ gc.SetLineWidth(p.Weight)
+ gc.SetLineCap(gg.LineCapRound)
+ gc.SetLineJoin(gg.LineJoinRound)
+ for _, ll := range p.Positions {
+ gc.LineTo(trans.ll2p(ll))
+ }
+ gc.SetColor(p.Color)
+ gc.Stroke()
+}
diff --git a/vendor/github.com/flopp/go-staticmaps/tile_fetcher.go b/vendor/github.com/flopp/go-staticmaps/tile_fetcher.go
new file mode 100644
index 0000000..e02a71f
--- /dev/null
+++ b/vendor/github.com/flopp/go-staticmaps/tile_fetcher.go
@@ -0,0 +1,153 @@
+// Copyright 2016 Florian Pigorsch. All rights reserved.
+//
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package sm
+
+import (
+ "bytes"
+ "fmt"
+ "image"
+ _ "image/jpeg" // to be able to decode jpegs
+ _ "image/png" // to be able to decode pngs
+ "io"
+ "io/ioutil"
+ "log"
+ "net/http"
+ "os"
+ "path/filepath"
+
+ "github.com/Wessie/appdirs"
+)
+
+// TileFetcher downloads map tile images from a TileProvider
+type TileFetcher struct {
+ tileProvider *TileProvider
+ cacheDir string
+ useCaching bool
+}
+
+// NewTileFetcher creates a new Tilefetcher struct
+func NewTileFetcher(tileProvider *TileProvider) *TileFetcher {
+ t := new(TileFetcher)
+ t.tileProvider = tileProvider
+ app := appdirs.New("go-staticmaps", "flopp.net", "0.1")
+ t.cacheDir = fmt.Sprintf("%s/%s", app.UserCache(), tileProvider.Name)
+ t.useCaching = true
+ return t
+}
+
+func (t *TileFetcher) url(zoom, x, y int) string {
+ shard := ""
+ ss := len(t.tileProvider.Shards)
+ if len(t.tileProvider.Shards) > 0 {
+ shard = t.tileProvider.Shards[(x+y)%ss]
+ }
+ return t.tileProvider.getURL(shard, zoom, x, y)
+}
+
+func (t *TileFetcher) cacheFileName(zoom int, x, y int) string {
+ return fmt.Sprintf("%s/%d/%d/%d", t.cacheDir, zoom, x, y)
+}
+
+// ToggleCaching enables/disables caching
+func (t *TileFetcher) ToggleCaching(enabled bool) {
+ t.useCaching = enabled
+}
+
+// Fetch download (or retrieves from the cache) a tile image for the specified zoom level and tile coordinates
+func (t *TileFetcher) Fetch(zoom, x, y int) (image.Image, error) {
+ if t.useCaching {
+ fileName := t.cacheFileName(zoom, x, y)
+ cachedImg, err := t.loadCache(fileName)
+ if err == nil {
+ return cachedImg, nil
+ }
+ }
+
+ url := t.url(zoom, x, y)
+ data, err := t.download(url)
+ if err != nil {
+ return nil, err
+ }
+
+ img, _, err := image.Decode(bytes.NewBuffer(data))
+ if err != nil {
+ return nil, err
+ }
+
+ if t.useCaching {
+ fileName := t.cacheFileName(zoom, x, y)
+ if err := t.storeCache(fileName, data); err != nil {
+ log.Printf("Failed to store map tile as '%s': %s", fileName, err)
+ }
+ }
+
+ return img, nil
+}
+
+func (t *TileFetcher) download(url string) ([]byte, error) {
+ resp, err := http.Get(url)
+ defer resp.Body.Close()
+
+ if err != nil {
+ return nil, err
+ }
+
+ contents, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return nil, err
+ }
+
+ return contents, nil
+}
+
+func (t *TileFetcher) loadCache(fileName string) (image.Image, error) {
+ file, err := os.Open(fileName)
+ if err != nil {
+ return nil, err
+ }
+ defer file.Close()
+
+ img, _, err := image.Decode(file)
+ if err != nil {
+ return nil, err
+ }
+
+ return img, nil
+}
+
+func (t *TileFetcher) createCacheDir(path string) error {
+ src, err := os.Stat(path)
+ if err != nil {
+ if os.IsNotExist(err) {
+ return os.MkdirAll(path, 0777)
+ }
+ return err
+ }
+ if src.IsDir() {
+ return nil
+ }
+ return fmt.Errorf("File exists but is not a directory: %s", path)
+}
+
+func (t *TileFetcher) storeCache(fileName string, data []byte) error {
+ dir, _ := filepath.Split(fileName)
+
+ if err := t.createCacheDir(dir); err != nil {
+ return err
+ }
+
+ file, err := os.Create(fileName)
+ if err != nil {
+ return err
+ }
+ defer file.Close()
+
+ if _, err = io.Copy(file, bytes.NewBuffer(data)); err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/flopp/go-staticmaps/tile_provider.go b/vendor/github.com/flopp/go-staticmaps/tile_provider.go
new file mode 100644
index 0000000..e54400c
--- /dev/null
+++ b/vendor/github.com/flopp/go-staticmaps/tile_provider.go
@@ -0,0 +1,98 @@
+// Copyright 2016 Florian Pigorsch. All rights reserved.
+//
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package sm
+
+import "fmt"
+
+// TileProvider encapsulates all infos about a map tile provider service (name, url scheme, attribution, etc.)
+type TileProvider struct {
+ Name string
+ Attribution string
+ TileSize int
+ URLPattern string // "%[1]s" => shard, "%[2]d" => zoom, "%[3]d" => x, "%[4]d" => y
+ Shards []string
+}
+
+func (t *TileProvider) getURL(shard string, zoom, x, y int) string {
+ return fmt.Sprintf(t.URLPattern, shard, zoom, x, y)
+}
+
+// NewTileProviderMapQuest creates a TileProvider struct for mapquest's tile service
+func NewTileProviderMapQuest() *TileProvider {
+ t := new(TileProvider)
+ t.Name = "mapquest"
+ t.Attribution = "Maps (c) MapQuest; Data (c) OSM and contributors, ODbL"
+ t.TileSize = 256
+ t.URLPattern = "http://otile%[1]s.mqcdn.com/tiles/1.0.0/osm/%[2]d/%[3]d/%[4]d.png"
+ t.Shards = []string{"1", "2", "3", "4"}
+ return t
+}
+
+func newTileProviderThunderforest(name string) *TileProvider {
+ t := new(TileProvider)
+ t.Name = fmt.Sprintf("thunderforest-%s", name)
+ t.Attribution = "Maps (c) Thundeforest; Data (c) OSM and contributors, ODbL"
+ t.TileSize = 256
+ t.URLPattern = "https://%[1]s.tile.thunderforest.com/" + name + "/%[2]d/%[3]d/%[4]d.png"
+ t.Shards = []string{"a", "b", "c"}
+ return t
+}
+
+// NewTileProviderThunderforestLandscape creates a TileProvider struct for thundeforests's 'landscape' tile service
+func NewTileProviderThunderforestLandscape() *TileProvider {
+ return newTileProviderThunderforest("landscape")
+}
+
+// NewTileProviderThunderforestOutdoors creates a TileProvider struct for thundeforests's 'outdoors' tile service
+func NewTileProviderThunderforestOutdoors() *TileProvider {
+ return newTileProviderThunderforest("outdoors")
+}
+
+// NewTileProviderThunderforestTransport creates a TileProvider struct for thundeforests's 'transport' tile service
+func NewTileProviderThunderforestTransport() *TileProvider {
+ return newTileProviderThunderforest("transport")
+}
+
+// NewTileProviderStamenToner creates a TileProvider struct for stamens' 'toner' tile service
+func NewTileProviderStamenToner() *TileProvider {
+ t := new(TileProvider)
+ t.Name = "stamen-toner"
+ t.Attribution = "Maps (c) Stamen; Data (c) OSM and contributors, ODbL"
+ t.TileSize = 256
+ t.URLPattern = "http://%[1]s.tile.stamen.com/toner/%[2]d/%[3]d/%[4]d.png"
+ t.Shards = []string{"a", "b", "c", "d"}
+ return t
+}
+
+// NewTileProviderOpenTopoMap creates a TileProvider struct for opentopomaps's tile service
+func NewTileProviderOpenTopoMap() *TileProvider {
+ t := new(TileProvider)
+ t.Name = "opentopomap"
+ t.Attribution = "Maps (c) OpenTopoMap [CC-BY-SA]; Data (c) OSM and contributors [ODbL]; Data (c) SRTM"
+ t.TileSize = 256
+ t.URLPattern = "http://%[1]s.tile.opentopomap.org/%[2]d/%[3]d/%[4]d.png"
+ t.Shards = []string{"a", "b", "c"}
+ return t
+}
+
+// GetTileProviders returns a map of all available TileProviders
+func GetTileProviders() map[string]*TileProvider {
+ m := make(map[string]*TileProvider)
+
+ list := []*TileProvider{
+ NewTileProviderMapQuest(),
+ NewTileProviderThunderforestLandscape(),
+ NewTileProviderThunderforestOutdoors(),
+ NewTileProviderThunderforestTransport(),
+ NewTileProviderStamenToner(),
+ NewTileProviderOpenTopoMap()}
+
+ for _, tp := range list {
+ m[tp.Name] = tp
+ }
+
+ return m
+}
diff --git a/vendor/github.com/flopp/go-staticmaps/util.go b/vendor/github.com/flopp/go-staticmaps/util.go
new file mode 100644
index 0000000..3f5d4ad
--- /dev/null
+++ b/vendor/github.com/flopp/go-staticmaps/util.go
@@ -0,0 +1,18 @@
+// Copyright 2016 Florian Pigorsch. All rights reserved.
+//
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package sm
+
+import (
+ "strings"
+)
+
+// hasPrefix checks if 's' has prefix 'prefix'; returns 'true' and the remainder on success, and 'false', 's' otherwise.
+func hasPrefix(s string, prefix string) (bool, string) {
+ if strings.HasPrefix(s, prefix) {
+ return true, strings.TrimPrefix(s, prefix)
+ }
+ return false, s
+}