1
0
Fork 0
mirror of https://github.com/Luzifer/staticmap.git synced 2025-01-20 11:31:56 +00:00

Remove old vendoring

Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
Knut Ahlers 2021-02-19 19:21:48 +01:00
parent b480398d1c
commit bfc88cb04e
Signed by: luzifer
GPG key ID: 0066F03ED215AD7D
534 changed files with 0 additions and 229356 deletions

173
Gopkg.lock generated
View file

@ -1,173 +0,0 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
branch = "master"
name = "github.com/Luzifer/go-staticmaps"
packages = ["."]
revision = "320790ed53294a789e715b3d0d5da8110efea1a2"
[[projects]]
name = "github.com/Luzifer/go_helpers"
packages = [
"accessLogger",
"http",
"str"
]
revision = "b0da2aa67ecc05ee4c8848d679b4a11a2fa578b2"
version = "v2.6.0"
[[projects]]
name = "github.com/Luzifer/rconfig"
packages = ["."]
revision = "7aef1d393c1e2d0758901853b59981c7adc67c7e"
version = "v1.2.0"
[[projects]]
branch = "master"
name = "github.com/Wessie/appdirs"
packages = ["."]
revision = "6573e894f8e294cbae0c4e45c25ff9f2e2918a4e"
[[projects]]
name = "github.com/didip/tollbooth"
packages = [
".",
"errors",
"libstring",
"limiter"
]
revision = "c95eaa3ddc98f635a91e218b48727fb2e06613ea"
version = "v4.0.0"
[[projects]]
branch = "master"
name = "github.com/flopp/go-coordsparser"
packages = ["."]
revision = "845bca739e263e1cd38de25024a47b4d6acbfc1f"
[[projects]]
name = "github.com/fogleman/gg"
packages = ["."]
revision = "6166aa3c1afaee416f384645a81636267aee6d25"
version = "v1.0.0"
[[projects]]
branch = "master"
name = "github.com/golang/freetype"
packages = [
"raster",
"truetype"
]
revision = "e2365dfdc4a05e4b8299a783240d4a7d5a65d4e4"
[[projects]]
branch = "master"
name = "github.com/golang/geo"
packages = [
"r1",
"r2",
"r3",
"s1",
"s2"
]
revision = "e41ca803f92c4c1770133cfa5b4fc8249a7dbe82"
[[projects]]
name = "github.com/gorilla/context"
packages = ["."]
revision = "08b5f424b9271eedf6f9f0ce86cb9396ed337a42"
version = "v1.1.1"
[[projects]]
name = "github.com/gorilla/mux"
packages = ["."]
revision = "e3702bed27f0d39777b0b37b664b6280e8ef8fbf"
version = "v1.6.2"
[[projects]]
branch = "master"
name = "github.com/lucasb-eyer/go-colorful"
packages = ["."]
revision = "d9cec903b20cbeda6062366e460c2c1bdc717e4d"
[[projects]]
name = "github.com/patrickmn/go-cache"
packages = ["."]
revision = "a3647f8e31d79543b2d0f0ae2fe5c379d72cedc0"
version = "v2.1.0"
[[projects]]
name = "github.com/sirupsen/logrus"
packages = ["."]
revision = "c155da19408a8799da419ed3eeb0cb5db0ad5dbc"
version = "v1.0.5"
[[projects]]
name = "github.com/spf13/pflag"
packages = ["."]
revision = "583c0c0531f06d5278b7d917446061adc344b5cd"
version = "v1.0.1"
[[projects]]
branch = "master"
name = "github.com/tkrajina/gpxgo"
packages = ["gpx"]
revision = "7848cf26f5a58b4a4e23b89a4b67cfc3d52dd042"
[[projects]]
branch = "master"
name = "golang.org/x/crypto"
packages = ["ssh/terminal"]
revision = "027cca12c2d63e3d62b670d901e8a2c95854feec"
[[projects]]
branch = "master"
name = "golang.org/x/image"
packages = [
"font",
"font/basicfont",
"font/plan9font",
"math/fixed"
]
revision = "af66defab954cb421ca110193eed9477c8541e2a"
[[projects]]
branch = "master"
name = "golang.org/x/net"
packages = ["context"]
revision = "db08ff08e8622530d9ed3a0e8ac279f6d4c02196"
[[projects]]
branch = "master"
name = "golang.org/x/sys"
packages = [
"unix",
"windows"
]
revision = "6c888cc515d3ed83fc103cf1d84468aad274b0a7"
[[projects]]
branch = "master"
name = "golang.org/x/time"
packages = ["rate"]
revision = "fbb02b2291d28baffd63558aa44b4b56f178d650"
[[projects]]
branch = "v2"
name = "gopkg.in/validator.v2"
packages = ["."]
revision = "135c24b11c19e52befcae2ec3fca5d9b78c4e98e"
[[projects]]
name = "gopkg.in/yaml.v2"
packages = ["."]
revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
version = "v2.2.1"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "c59f72846eca4898dab7e7c3fc9a28b340f42aafbe4ac4866ad1ae33c382ea76"
solver-name = "gps-cdcl"
solver-version = 1

View file

@ -1,66 +0,0 @@
# Gopkg.toml example
#
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
#
# [prune]
# non-go = false
# go-tests = true
# unused-packages = true
[[constraint]]
branch = "master"
name = "github.com/Luzifer/go-staticmaps"
[[constraint]]
name = "github.com/Luzifer/go_helpers"
version = "2.3.1"
[[constraint]]
name = "github.com/Luzifer/rconfig"
version = "1.2.0"
[[constraint]]
name = "github.com/sirupsen/logrus"
version = "1.0.5"
[[constraint]]
name = "github.com/didip/tollbooth"
version = "4.0.0"
[[constraint]]
name = "github.com/fogleman/gg"
version = "1.0.0"
[[constraint]]
branch = "master"
name = "github.com/golang/geo"
[[constraint]]
name = "github.com/gorilla/mux"
version = "1.6.1"
[[constraint]]
branch = "master"
name = "github.com/lucasb-eyer/go-colorful"
[prune]
go-tests = true
unused-packages = true

View file

@ -1 +0,0 @@
*.png

View file

@ -1,36 +0,0 @@
language: go
go:
- 1.9
- master
install:
- go get -t ./...
matrix:
allow_failures:
- go: master
# Don't wait for tip tests to finish. Mark the test run green if the
# tests pass on the stable versions of Go.
fast_finish: true
notifications:
email: false
# Anything in before_script that returns a nonzero exit code will
# flunk the build and immediately stop. It's sorta like having
# set -e enabled in bash.
before_script:
- GO_FILES=$(find . -iname '*.go' -type f) # All the .go files
- go get github.com/golang/lint/golint # Linter
- go get honnef.co/go/tools/cmd/megacheck # Badass static analyzer/linter
- go get github.com/fzipp/gocyclo
# script always run to completion (set +e). All of these code checks are must haves
# in a modern Go project.
script:
- test -z $(gofmt -s -l $GO_FILES) # Fail if a .go file hasn't been formatted with gofmt
- go vet ./... # go vet is the official Go static analyzer
- megacheck ./... # "go vet on steroids" + linter
- gocyclo -over 19 $GO_FILES # forbid code with huge functions
- golint -set_exit_status $(go list ./...) # one last linter

View file

@ -1,21 +0,0 @@
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.

View file

@ -1,249 +0,0 @@
[![GoDoc](https://godoc.org/github.com/flopp/go-staticmaps?status.svg)](https://godoc.org/github.com/flopp/go-staticmaps)
[![Go Report Card](https://goreportcard.com/badge/github.com/flopp/go-staticmaps)](https://goreportcard.com/report/flopp/go-staticmaps)
[![Build Status](https://travis-ci.org/flopp/go-staticmaps.svg?branch=master)](https://travis-ci.org/flopp/go-staticmaps)
[![License MIT](https://img.shields.io/badge/license-MIT-lightgrey.svg?style=flat)](https://github.com/flopp/go-staticmaps/)
# go-staticmaps
A go (golang) library and command line tool to render static map images using OpenStreetMap tiles.
## What?
go-staticmaps is a golang library that allows you to create nice static map images from OpenStreetMap tiles, along with markers of different size and color, as well as paths and colored areas.
go-staticmaps comes with a command line tool called `create-static-map` for use in shell scripts, etc.
![Static map of the Berlin Marathon](https://raw.githubusercontent.com/flopp/flopp.github.io/master/go-staticmaps/berlin-marathon.png)
## How?
### Installation
Installing go-staticmaps is as easy as
```bash
go get -u github.com/flopp/go-staticmaps
```
For the command line tool, use
```bash
go get -u github.com/flopp/go-staticmaps/create-static-map
```
Of course, your local Go installation must be setup up properly.
### Library Usage
Create a 400x300 pixel map with a red marker:
```go
import (
"image/color"
"github.com/flopp/go-staticmaps"
"github.com/fogleman/gg"
"github.com/golang/geo/s2"
)
func main() {
ctx := sm.NewContext()
ctx.SetSize(400, 300)
ctx.AddMarker(sm.NewMarker(s2.LatLngFromDegrees(52.514536, 13.350151), color.RGBA{0xff, 0, 0, 0xff}, 16.0))
img, err := ctx.Render()
if err != nil {
panic(err)
}
if err := gg.SavePNG("my-map.png", img); err != nil {
panic(err)
}
}
```
See [GoDoc](https://godoc.org/github.com/flopp/go-staticmaps) for a complete documentation and the source code of the [command line tool](https://github.com/flopp/go-staticmaps/blob/master/create-static-map/create-static-map.go) for an example how to use the package.
### Command Line Usage
Usage:
create-static-map [OPTIONS]
Creates a static map
Application Options:
--width=PIXELS Width of the generated static map image (default: 512)
--height=PIXELS Height of the generated static map image (default: 512)
-o, --output=FILENAME Output file name (default: map.png)
-t, --type=MAPTYPE Select the map type; list possible map types with '--type list'
-c, --center=LATLNG Center coordinates (lat,lng) of the static map
-z, --zoom=ZOOMLEVEL Zoom factor
-b, --bbox=NW_LATLNG|SE_LATLNG
Set the bounding box (NW_LATLNG = north-western point of the
bounding box, SW_LATLNG = southe-western point of the bounding
box)
--background=COLOR Background color (default: transparent)
-u, --useragent=USERAGENT
Overwrite the default HTTP user agent string
-m, --marker=MARKER Add a marker to the static map
-p, --path=PATH Add a path to the static map
-a, --area=AREA Add an area to the static map
-C, --circle=CIRCLE Add a circle to the static map
Help Options:
-h, --help Show this help message
### General
The command line interface tries to resemble [Google's Static Maps API](https://developers.google.com/maps/documentation/static-maps/intro).
If neither `--bbox`, `--center`, nor `--zoom` are given, the map extent is determined from the specified markers, paths and areas.
`--background` lets you specify a color used for map areas that are not covered by map tiles (areas north of 85°/south of -85°).
### Markers
The `--marker` option defines one or more map markers of the same style. Use multiple `--marker` options to add markers of different styles.
--marker MARKER_STYLES|LATLNG|LATLNG|...
`LATLNG` is a comma separated pair of latitude and longitude, e.g. `52.5153,13.3564`.
`MARKER_STYLES` consists of a set of style descriptors separated by the pipe character `|`:
- `color:COLOR` - where `COLOR` is either of the form `0xRRGGBB`, `0xRRGGBBAA`, or one of `black`, `blue`, `brown`, `green`, `orange`, `purple`, `red`, `yellow`, `white` (default: `red`)
- `size:SIZE` - where `SIZE` is one of `mid`, `small`, `tiny`, or some number > 0 (default: `mid`)
- `label:LABEL` - where `LABEL` is an alpha numeric character, i.e. `A`-`Z`, `a`-`z`, `0`-`9`; (default: no label)
- `labelcolor:COLOR` - where `COLOR` is either of the form `0xRRGGBB`, `0xRRGGBBAA`, or one of `black`, `blue`, `brown`, `green`, `orange`, `purple`, `red`, `yellow`, `white` (default: `black` or `white`, depending on the marker color)
### Paths
The `--path` option defines a path on the map. Use multiple `--path` options to add multiple paths to the map.
--path PATH_STYLES|LATLNG|LATLNG|...
or
--path PATH_STYLES|gpx:my_gpx_file.gpx
`PATH_STYLES` consists of a set of style descriptors separated by the pipe character `|`:
- `color:COLOR` - where `COLOR` is either of the form `0xRRGGBB`, `0xRRGGBBAA`, or one of `black`, `blue`, `brown`, `green`, `orange`, `purple`, `red`, `yellow`, `white` (default: `red`)
- `weight:WEIGHT` - where `WEIGHT` is the line width in pixels (defaut: `5`)
### Areas
The `--area` option defines a closed area on the map. Use multiple `--area` options to add multiple areas to the map.
--area AREA_STYLES|LATLNG|LATLNG|...
`AREA_STYLES` consists of a set of style descriptors separated by the pipe character `|`:
- `color:COLOR` - where `COLOR` is either of the form `0xRRGGBB`, `0xRRGGBBAA`, or one of `black`, `blue`, `brown`, `green`, `orange`, `purple`, `red`, `yellow`, `white` (default: `red`)
- `weight:WEIGHT` - where `WEIGHT` is the line width in pixels (defaut: `5`)
- `fill:COLOR` - where `COLOR` is either of the form `0xRRGGBB`, `0xRRGGBBAA`, or one of `black`, `blue`, `brown`, `green`, `orange`, `purple`, `red`, `yellow`, `white` (default: none)
### Circles
The `--circles` option defines one or more circles of the same style. Use multiple `--circle` options to add circles of different styles.
--circle CIRCLE_STYLES|LATLNG|LATLNG|...
`LATLNG` is a comma separated pair of latitude and longitude, e.g. `52.5153,13.3564`.
`CIRCLE_STYLES` consists of a set of style descriptors separated by the pipe character `|`:
- `color:COLOR` - where `COLOR` is either of the form `0xRRGGBB`, `0xRRGGBBAA`, or one of `black`, `blue`, `brown`, `green`, `orange`, `purple`, `red`, `yellow`, `white` (default: `red`)
- `fill:COLOR` - where `COLOR` is either of the form `0xRRGGBB`, `0xRRGGBBAA`, or one of `black`, `blue`, `brown`, `green`, `orange`, `purple`, `red`, `yellow`, `white` (default: no fill color)
- `radius:RADIUS` - where `RADIUS` is te circle radius in meters (default: `100.0`)
- `weight:WEIGHT` - where `WEIGHT` is the line width in pixels (defaut: `5`)
## Examples
### Basic Maps
Centered at "N 52.514536 E 13.350151" with zoom level 10:
```bash
$ create-static-map --width 600 --height 400 -o map1.png -c "52.514536,13.350151" -z 10
```
![Example 1](https://raw.githubusercontent.com/flopp/flopp.github.io/master/go-staticmaps/map1.png)
A map with a marker at "N 52.514536 E 13.350151" with zoom level 14 (no need to specify the map's center - it is automatically computed from the marker(s)):
```bash
$ create-static-map --width 600 --height 400 -o map2.png -z 14 -m "52.514536,13.350151"
```
![Example 2](https://raw.githubusercontent.com/flopp/flopp.github.io/master/go-staticmaps/map2.png)
A map with two markers (red and green). If there are more than two markers in the map, a *good* zoom level can be determined automatically:
```bash
$ create-static-map --width 600 --height 400 -o map3.png -m "color:red|52.514536,13.350151" -m "color:green|52.516285,13.377746"
```
![Example 3](https://raw.githubusercontent.com/flopp/flopp.github.io/master/go-staticmaps/map3.png)
### Create a map of the Berlin Marathon
create-static-map --width 800 --height 600 \
--marker "color:green|52.5153,13.3564" \
--marker "color:red|52.5160,13.3711" \
--output "berlin-marathon.png" \
--path "color:blue|weight:2|gpx:berlin-marathon.gpx"
![Static map of the Berlin Marathon](https://raw.githubusercontent.com/flopp/flopp.github.io/master/go-staticmaps/berlin-marathon.png)
### Create a map of the US capitals
create-static-map --width 800 --height 400 \
--output "us-capitals.png" \
--marker "color:blue|size:tiny|32.3754,-86.2996|58.3637,-134.5721|33.4483,-112.0738|34.7244,-92.2789|\
38.5737,-121.4871|39.7551,-104.9881|41.7665,-72.6732|39.1615,-75.5136|30.4382,-84.2806|33.7545,-84.3897|\
21.2920,-157.8219|43.6021,-116.2125|39.8018,-89.6533|39.7670,-86.1563|41.5888,-93.6203|39.0474,-95.6815|\
38.1894,-84.8715|30.4493,-91.1882|44.3294,-69.7323|38.9693,-76.5197|42.3589,-71.0568|42.7336,-84.5466|\
44.9446,-93.1027|32.3122,-90.1780|38.5698,-92.1941|46.5911,-112.0205|40.8136,-96.7026|39.1501,-119.7519|\
43.2314,-71.5597|40.2202,-74.7642|35.6816,-105.9381|42.6517,-73.7551|35.7797,-78.6434|46.8084,-100.7694|\
39.9622,-83.0007|35.4931,-97.4591|44.9370,-123.0272|40.2740,-76.8849|41.8270,-71.4087|34.0007,-81.0353|\
44.3776,-100.3177|36.1589,-86.7821|30.2687,-97.7452|40.7716,-111.8882|44.2627,-72.5716|37.5408,-77.4339|\
47.0449,-122.9016|38.3533,-81.6354|43.0632,-89.4007|41.1389,-104.8165"
![Static map of the US capitals](https://raw.githubusercontent.com/flopp/flopp.github.io/master/go-staticmaps/us-capitals.png)
### Create a map of Australia
...where the Northern Territory is highlighted and the capital Canberra is marked.
create-static-map --width 800 --height 600 \
--center="-26.284973,134.303764" \
--output "australia.png" \
--marker "color:blue|-35.305200,149.121574" \
--area "color:0x00FF00|fill:0x00FF007F|weight:2|-25.994024,129.013847|-25.994024,137.989677|-16.537670,138.011649|\
-14.834820,135.385917|-12.293236,137.033866|-11.174554,130.398124|-12.925791,130.167411|-14.866678,129.002860"
![Static map of Australia](https://raw.githubusercontent.com/flopp/flopp.github.io/master/go-staticmaps/australia.png)
## Acknowledgements
Besides the go standard library, go-staticmaps uses
- [OpenStreetMap](http://openstreetmap.org/), [Thunderforest](http://www.thunderforest.com/), [OpenTopoMap](http://www.opentopomap.org/), [Stamen](http://maps.stamen.com/) and [Carto](http://carto.com) as map tile providers
- [Go Graphics](https://github.com/fogleman/gg) for 2D drawing
- [S2 geometry library](https://github.com/golang/geo) for spherical geometry calculations
- [appdirs](https://github.com/Wessie/appdirs) for platform specific system directories
- [gpxgo](github.com/tkrajina/gpxgo) for loading GPX files
- [go-coordsparser](https://github.com/flopp/go-coordsparser) for parsing geo coordinates
## Contributors
- [Kooper](https://github.com/Kooper): fixed *library usage examples*
- [felix](https://github.com/felix): added *more tile servers*
- [wiless](https://github.com/wiless): suggested to add user definable *marker label colors*
- [noki](https://github.com/Noki): suggested to add a user definable *bounding box*
- [digitocero](https://github.com/digitocero): reported and fixed *type mismatch error*
- [bcicen](https://github.com/bcicen): reported and fixed *syntax error in examples*
- [pshevtsov](https://github.com/pshevtsov): fixed *drawing of empty attribution strings*
- [Luzifer](https://github.com/Luzifer): added *overwritable user agent strings* to comply with the OSM tile usage policy
- [Jason Fox](https://github.com/jasonpfox): added `RenderWithBounds` function
- [Alexander A. Kapralov](https://github.com/alnkapa): initial *circles* implementation
- [tsukumaru](https://github.com/tsukumaru): added `NewArea` and `NewPath` functions
## License
Copyright 2016, 2017 Florian Pigorsch & Contributors. All rights reserved.
Use of this source code is governed by a MIT-style license that can be found in the LICENSE file.

View file

@ -1,104 +0,0 @@
// Copyright 2016, 2017 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
}
// NewArea creates a new Area
func NewArea(positions []s2.LatLng, col color.Color, fill color.Color, weight float64) *Area {
a := new(Area)
a.Positions = positions
a.Color = col
a.Fill = fill
a.Weight = weight
return a
}
// 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()
}

View file

@ -1,53 +0,0 @@
// Copyright 2016, 2017 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"
"math"
"github.com/golang/geo/s1"
"github.com/golang/geo/s2"
)
// CreateBBox creates a bounding box from a north-western point
// (lat/lng in degrees) and a south-eastern point (lat/lng in degrees).
// Note that you can create a bounding box wrapping over the antimeridian at
// lng=+-/180° by nwlng > selng.
func CreateBBox(nwlat float64, nwlng float64, selat float64, selng float64) (*s2.Rect, error) {
if nwlat < -90 || nwlat > 90 {
return nil, fmt.Errorf("Out of range nwlat (%f) must be in [-90, 90]", nwlat)
}
if nwlng < -180 || nwlng > 180 {
return nil, fmt.Errorf("Out of range nwlng (%f) must be in [-180, 180]", nwlng)
}
if selat < -90 || selat > 90 {
return nil, fmt.Errorf("Out of range selat (%f) must be in [-90, 90]", selat)
}
if selng < -180 || selng > 180 {
return nil, fmt.Errorf("Out of range selng (%f) must be in [-180, 180]", selng)
}
if nwlat == selat {
return nil, fmt.Errorf("nwlat and selat must not be equal")
}
if nwlng == selng {
return nil, fmt.Errorf("nwlng and selng must not be equal")
}
bbox := new(s2.Rect)
if selat < nwlat {
bbox.Lat.Lo = selat * math.Pi / 180.0
bbox.Lat.Hi = nwlat * math.Pi / 180.0
} else {
bbox.Lat.Lo = nwlat * math.Pi / 180.0
bbox.Lat.Hi = selat * math.Pi / 180.0
}
bbox.Lng = s1.IntervalFromEndpoints(nwlng*math.Pi/180.0, selng*math.Pi/180.0)
return bbox, nil
}

View file

@ -1,128 +0,0 @@
package sm
import (
"image/color"
"log"
"math"
"strings"
"strconv"
"github.com/flopp/go-coordsparser"
"github.com/fogleman/gg"
"github.com/golang/geo/s1"
"github.com/golang/geo/s2"
)
// Circle represents a circle on the map
type Circle struct {
MapObject
Position s2.LatLng
Color color.Color
Fill color.Color
Weight float64
Radius float64 // in m.
}
// NewCircle creates a new circle
func NewCircle(pos s2.LatLng, col, fill color.Color, radius, weight float64) *Circle {
return &Circle{
Position: pos,
Color: col,
Fill: fill,
Weight: weight,
Radius: radius,
}
}
// ParseCircleString parses a string and returns an array of circles
func ParseCircleString(s string) (circles []*Circle, err error) {
circles = make([]*Circle, 0, 0)
var col color.Color = color.RGBA{0xff, 0, 0, 0xff}
var fill color.Color = color.Transparent
radius := 100.0
weight := 5.0
for _, ss := range strings.Split(s, "|") {
if ok, suffix := hasPrefix(ss, "color:"); ok {
col, err = ParseColorString(suffix)
if err != nil {
return nil, err
}
} else if ok, suffix := hasPrefix(ss, "fill:"); ok {
fill, err = ParseColorString(suffix)
if err != nil {
return nil, err
}
} else if ok, suffix := hasPrefix(ss, "radius:"); ok {
if radius, err = strconv.ParseFloat(suffix, 64); err != nil {
return nil, err
}
} else if ok, suffix := hasPrefix(ss, "weight:"); ok {
if weight, err = strconv.ParseFloat(suffix, 64); err != nil {
return nil, err
}
} else {
lat, lng, err := coordsparser.Parse(ss)
if err != nil {
return nil, err
}
c := NewCircle(s2.LatLngFromDegrees(lat, lng), col, fill, radius, weight)
circles = append(circles, c)
}
}
return circles, nil
}
func (m *Circle) getLatLng(plus bool) s2.LatLng {
const (
R = 6371000.0
)
th := m.Radius / R
br := 0 / float64(s1.Degree)
if !plus {
th *= -1
}
lat := m.Position.Lat.Radians()
lat1 := math.Asin(math.Sin(lat)*math.Cos(th) + math.Cos(lat)*math.Sin(th)*math.Cos(br))
lng1 := m.Position.Lng.Radians() +
math.Atan2(math.Sin(br)*math.Sin(th)*math.Cos(lat),
math.Cos(th)-math.Sin(lat)*math.Sin(lat1))
return s2.LatLng{
Lat: s1.Angle(lat1),
Lng: s1.Angle(lng1),
}
}
func (m *Circle) extraMarginPixels() float64 {
return 0.5 * m.Weight
}
func (m *Circle) bounds() s2.Rect {
r := s2.EmptyRect()
r = r.AddPoint(m.getLatLng(false))
r = r.AddPoint(m.getLatLng(true))
return r
}
func (m *Circle) draw(gc *gg.Context, trans *transformer) {
if !CanDisplay(m.Position) {
log.Printf("Circle coordinates not displayable: %f/%f", m.Position.Lat.Degrees(), m.Position.Lng.Degrees())
return
}
ll := m.getLatLng(true)
x, y := trans.ll2p(m.Position)
x1, y1 := trans.ll2p(ll)
radius := math.Sqrt(math.Pow(x1-x, 2) + math.Pow(y1-y, 2))
gc.ClearPath()
gc.SetLineWidth(m.Weight)
gc.SetLineCap(gg.LineCapRound)
gc.SetLineJoin(gg.LineJoinRound)
gc.DrawCircle(x, y, radius)
gc.SetColor(m.Fill)
gc.FillPreserve()
gc.SetColor(m.Color)
gc.Stroke()
}

View file

@ -1,67 +0,0 @@
// Copyright 2016, 2017 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
case "transparent":
return color.RGBA{0x00, 0x00, 0x00, 0x00}, 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)
}

View file

@ -1,503 +0,0 @@
// Copyright 2016, 2017 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/color"
"image/draw"
"log"
"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
hasBoundingBox bool
boundingBox s2.Rect
background color.Color
markers []*Marker
paths []*Path
areas []*Area
circles []*Circle
overlays []*TileProvider
userAgent string
tileProvider *TileProvider
overrideAttribution *string
}
// 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.hasBoundingBox = false
t.background = nil
t.userAgent = ""
t.tileProvider = NewTileProviderOpenStreetMaps()
return t
}
// SetTileProvider sets the TileProvider to be used
func (m *Context) SetTileProvider(t *TileProvider) {
m.tileProvider = t
}
// SetUserAgent sets the HTTP user agent string used when downloading map tiles
func (m *Context) SetUserAgent(a string) {
m.userAgent = a
}
// 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
}
// SetBoundingBox sets the bounding box
func (m *Context) SetBoundingBox(bbox s2.Rect) {
m.boundingBox = bbox
m.hasBoundingBox = true
}
// SetBackground sets the background color (used as a fallback for areas without map tiles)
func (m *Context) SetBackground(col color.Color) {
m.background = col
}
// 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
}
// AddCircle adds an circle to the Context
func (m *Context) AddCircle(circle *Circle) {
m.circles = append(m.circles, circle)
}
// ClearCircles removes all circles from the Context
func (m *Context) ClearCircles() {
m.circles = nil
}
// AddOverlay adds an overlay to the Context
func (m *Context) AddOverlay(overlay *TileProvider) {
m.overlays = append(m.overlays, overlay)
}
// ClearOverlays removes all overlays from the Context
func (m *Context) ClearOverlays() {
m.overlays = nil
}
// OverrideAttribution sets a custom attribution string (or none if empty)
//
// Pay attention you might be violating the terms of usage for the
// selected map provider - only use the function if you are aware of this!
func (m *Context) OverrideAttribution(attribution string) {
m.overrideAttribution = &attribution
}
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())
}
for _, circle := range m.circles {
r = r.Union(circle.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
}
}
for _, circle := range m.circles {
if pp := circle.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 := maxX - minX
for dx < 0 {
dx = dx + 1
}
for dx > 1 {
dx = dx - 1
}
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
}
func (m *Context) determineZoomCenter() (int, s2.LatLng, error) {
bounds := m.determineBounds()
if m.hasBoundingBox && !m.boundingBox.IsEmpty() {
center := m.boundingBox.Center()
return m.determineZoom(m.boundingBox, center), center, nil
} else if m.hasCenter {
if m.hasZoom {
return m.zoom, m.center, nil
}
return m.determineZoom(bounds, m.center), m.center, nil
} else if !bounds.IsEmpty() {
center := bounds.Center()
if m.hasZoom {
return m.zoom, center, nil
}
return m.determineZoom(bounds, center), center, nil
}
return 0, s2.LatLngFromDegrees(0, 0), errors.New("Cannot determine map extent: no center coordinates given, no bounding box given, no content (markers, paths, areas) given")
}
type transformer struct {
zoom int
numTiles float64 // number of tiles per dimension at this zoom level
tileSize int // tile size in pixels from this provider
pWidth, pHeight int // pixel size of returned set of tiles
pCenterX, pCenterY int // pixel location of requested center in set of tiles
tCountX, tCountY int // download area in tile units
tCenterX, tCenterY float64 // tile index to requested center
tOriginX, tOriginY int // bottom left tile to download
pMinX, pMaxX int
}
func newTransformer(width int, height int, zoom int, llCenter s2.LatLng, tileSize int) *transformer {
t := new(transformer)
t.zoom = zoom
t.numTiles = math.Exp2(float64(t.zoom))
t.tileSize = tileSize
// fractional tile index to center of requested area
t.tCenterX, t.tCenterY = t.ll2t(llCenter)
ww := float64(width) / float64(tileSize)
hh := float64(height) / float64(tileSize)
// origin tile to fulfill request
t.tOriginX = int(math.Floor(t.tCenterX - 0.5*ww))
t.tOriginY = int(math.Floor(t.tCenterY - 0.5*hh))
// tiles in each axis to fulfill request
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
// final pixel dimensions of area returned
t.pWidth = t.tCountX * tileSize
t.pHeight = t.tCountY * tileSize
// Pixel location in returned image for center of requested area
t.pCenterX = int((t.tCenterX - float64(t.tOriginX)) * float64(tileSize))
t.pCenterY = int((t.tCenterY - float64(t.tOriginY)) * float64(tileSize))
t.pMinX = t.pCenterX - width/2
t.pMaxX = t.pMinX + width
return t
}
// ll2t returns fractional tile index for a lat/lng points
func (t *transformer) ll2t(ll s2.LatLng) (float64, float64) {
x := t.numTiles * (ll.Lng.Degrees() + 180.0) / 360.0
y := t.numTiles * (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)
offset := t.numTiles * float64(t.tileSize)
if x < float64(t.pMinX) {
for x < float64(t.pMinX) {
x = x + offset
}
} else if x >= float64(t.pMaxX) {
for x >= float64(t.pMaxX) {
x = x - offset
}
}
return x, y
}
// Rect returns an s2.Rect bounding box around the set of tiles described by transformer
func (t *transformer) Rect() (bbox s2.Rect) {
// transform from https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Go
invNumTiles := 1.0 / t.numTiles
// Get latitude bounds
n := math.Pi - 2.0*math.Pi*float64(t.tOriginY)*invNumTiles
bbox.Lat.Hi = math.Atan(0.5 * (math.Exp(n) - math.Exp(-n)))
n = math.Pi - 2.0*math.Pi*float64(t.tOriginY+t.tCountY)*invNumTiles
bbox.Lat.Lo = math.Atan(0.5 * (math.Exp(n) - math.Exp(-n)))
// Get longtitude bounds, much easier
bbox.Lng.Lo = float64(t.tOriginX)*invNumTiles*2.0*math.Pi - math.Pi
bbox.Lng.Hi = float64(t.tOriginX+t.tCountX)*invNumTiles*2.0*math.Pi - math.Pi
return bbox
}
// Render actually renders the map image including all map objects (markers, paths, areas)
func (m *Context) Render() (image.Image, error) {
zoom, center, err := m.determineZoomCenter()
if err != nil {
return nil, err
}
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)
if m.background != nil {
draw.Draw(img, img.Bounds(), &image.Uniform{m.background}, image.ZP, draw.Src)
}
// fetch and draw tiles to img
layers := []*TileProvider{m.tileProvider}
if m.overlays != nil {
layers = append(layers, m.overlays...)
}
for _, layer := range layers {
if err := m.renderLayer(gc, zoom, trans, tileSize, layer); err != nil {
return nil, err
}
}
// 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)
}
for _, circle := range m.circles {
circle.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)
attribution := m.tileProvider.Attribution
if m.overrideAttribution != nil {
attribution = *m.overrideAttribution
}
// draw attribution
if attribution == "" {
return croppedImg, nil
}
_, textHeight := gc.MeasureString(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(attribution, 4.0, float64(m.height)-4.0)
return croppedImg, nil
}
// RenderWithBounds actually renders the map image including all map objects (markers, paths, areas).
// The returned image covers requested area as well as any tiles necessary to cover that area, which may
// be larger than the request.
//
// Specific bounding box of returned image is provided to support image registration with other data
func (m *Context) RenderWithBounds() (image.Image, s2.Rect, error) {
zoom, center, err := m.determineZoomCenter()
if err != nil {
return nil, s2.Rect{}, err
}
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)
if m.background != nil {
draw.Draw(img, img.Bounds(), &image.Uniform{m.background}, image.ZP, draw.Src)
}
// fetch and draw tiles to img
layers := []*TileProvider{m.tileProvider}
if m.overlays != nil {
layers = append(layers, m.overlays...)
}
for _, layer := range layers {
if err := m.renderLayer(gc, zoom, trans, tileSize, layer); err != nil {
return nil, s2.Rect{}, err
}
}
// draw map objects
for _, area := range m.areas {
area.draw(gc, trans)
}
for _, path := range m.paths {
path.draw(gc, trans)
}
for _, circle := range m.circles {
circle.draw(gc, trans)
}
for _, marker := range m.markers {
marker.draw(gc, trans)
}
// draw attribution
if m.tileProvider.Attribution == "" {
return img, trans.Rect(), nil
}
_, textHeight := gc.MeasureString(m.tileProvider.Attribution)
boxHeight := textHeight + 4.0
gc.SetRGBA(0.0, 0.0, 0.0, 0.5)
gc.DrawRectangle(0.0, float64(trans.pHeight)-boxHeight, float64(trans.pWidth), 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 img, trans.Rect(), nil
}
func (m *Context) renderLayer(gc *gg.Context, zoom int, trans *transformer, tileSize int, provider *TileProvider) error {
t := NewTileFetcher(provider)
if m.userAgent != "" {
t.SetUserAgent(m.userAgent)
}
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
}
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)
} else {
if tileImg, err := t.Fetch(zoom, x, y); err == nil {
gc.DrawImage(tileImg, xx*tileSize, yy*tileSize)
} else {
log.Printf("Error downloading tile file: %s", err)
}
}
}
}
return nil
}

View file

@ -1,25 +0,0 @@
// Copyright 2016, 2017 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 (
"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)
}
// CanDisplay checks if pos is generally displayable (i.e. its latitude is in [-85,85])
func CanDisplay(pos s2.LatLng) bool {
const minLatitude float64 = -85.0
const maxLatitude float64 = 85.0
return (minLatitude <= pos.Lat.Degrees()) && (pos.Lat.Degrees() <= maxLatitude)
}

View file

@ -1,149 +0,0 @@
// Copyright 2016, 2017 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"
"log"
"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
LabelColor color.Color
}
// 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 = ""
if Luminance(m.Color) >= 0.5 {
m.LabelColor = color.RGBA{0x00, 0x00, 0x00, 0xff}
} else {
m.LabelColor = color.RGBA{0xff, 0xff, 0xff, 0xff}
}
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":