mirror of
https://github.com/flopp/go-staticmaps.git
synced 2026-02-01 15:24:21 +00:00
added area.go
This commit is contained in:
parent
900e03ad0a
commit
37c675dbd0
17
README.md
17
README.md
@ -72,6 +72,7 @@ See [GoDoc](https://godoc.org/github.com/flopp/go-staticmaps/staticmaps) for a c
|
||||
-z, --zoom=ZOOMLEVEL Zoom factor
|
||||
-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
|
||||
|
||||
Help Options:
|
||||
-h, --help Show this help message
|
||||
@ -93,16 +94,26 @@ The `--marker` option defines one or more map markers of the same style. Use mul
|
||||
- `size:SIZE` - where `SIZE` is one of `mid`, `small`, `tiny` (default: `mid`)
|
||||
|
||||
### Paths
|
||||
The `--path` option defines a path or an area on the map. Use multiple `--path` options to add multiple paths/areas to the map.
|
||||
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|...
|
||||
|
||||
`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`)
|
||||
- `fillcolor:COLOR` - where `COLOR` is either of the form `0xRRGGBB`, `0xRRGGBBAA`, or one of `black`, `blue`, `brown`, `green`, `orange`, `purple`, `red`, `yellow`, `white` (default: none); if a fill color is specified, the path is drawn as a closed, filled area
|
||||
- `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)
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
@ -183,7 +194,7 @@ $ create-static-map --width 600 --height 400 -o map3.png -m "red|52.514536,13.35
|
||||
--center="-26.284973,134.303764" \
|
||||
--output "australia.png" \
|
||||
--marker "color:blue|-35.305200,149.121574" \
|
||||
--path "color:0x00FF00|fillcolor:0x00FF007F|weight:2|-25.994024,129.013847|-25.994024,137.989677|-16.537670,138.011649|\
|
||||
--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"
|
||||
|
||||

|
||||
|
||||
@ -54,6 +54,7 @@ func main() {
|
||||
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)
|
||||
@ -109,6 +110,15 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
for _, areaString := range opts.Areas {
|
||||
area, err := staticmaps.ParseAreaString(areaString)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
} else {
|
||||
m.AddArea(area)
|
||||
}
|
||||
}
|
||||
|
||||
img, err := m.Create()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
||||
97
staticmaps/area.go
Normal file
97
staticmaps/area.go
Normal file
@ -0,0 +1,97 @@
|
||||
// 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 staticmaps
|
||||
|
||||
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 strings.HasPrefix(ss, "color:") {
|
||||
var err error
|
||||
area.Color, err = ParseColorString(strings.TrimPrefix(ss, "color:"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if strings.HasPrefix(ss, "fill:") {
|
||||
var err error
|
||||
area.Fill, err = ParseColorString(strings.TrimPrefix(ss, "fill:"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if strings.HasPrefix(ss, "weight:") {
|
||||
var err error
|
||||
area.Weight, err = strconv.ParseFloat(strings.TrimPrefix(ss, "weight:"), 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()
|
||||
}
|
||||
@ -29,6 +29,7 @@ type MapCreator struct {
|
||||
|
||||
markers []*Marker
|
||||
paths []*Path
|
||||
areas []*Area
|
||||
|
||||
tileProvider *TileProvider
|
||||
}
|
||||
@ -87,6 +88,16 @@ func (m *MapCreator) ClearPaths() {
|
||||
m.paths = nil
|
||||
}
|
||||
|
||||
// AddArea adds an area to the MapCreator
|
||||
func (m *MapCreator) AddArea(area *Area) {
|
||||
m.areas = append(m.areas, area)
|
||||
}
|
||||
|
||||
// ClearAreas removes all areas from the MapCreator
|
||||
func (m *MapCreator) ClearAreas() {
|
||||
m.areas = nil
|
||||
}
|
||||
|
||||
func (m *MapCreator) determineBounds() s2.Rect {
|
||||
r := s2.EmptyRect()
|
||||
for _, marker := range m.markers {
|
||||
@ -95,6 +106,9 @@ func (m *MapCreator) determineBounds() s2.Rect {
|
||||
for _, path := range m.paths {
|
||||
r = r.Union(path.bounds())
|
||||
}
|
||||
for _, area := range m.areas {
|
||||
r = r.Union(area.bounds())
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
@ -112,6 +126,12 @@ func (m *MapCreator) determineExtraMarginPixels() float64 {
|
||||
p = pp
|
||||
}
|
||||
}
|
||||
for _, area := range m.areas {
|
||||
pp := area.extraMarginPixels()
|
||||
if pp > p {
|
||||
p = pp
|
||||
}
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
@ -231,29 +251,34 @@ func (m *MapCreator) Create() (image.Image, error) {
|
||||
}
|
||||
}
|
||||
|
||||
dc := gg.NewContextForRGBA(img)
|
||||
gc := gg.NewContextForRGBA(img)
|
||||
|
||||
for _, area := range m.areas {
|
||||
area.draw(gc, trans)
|
||||
}
|
||||
|
||||
for _, path := range m.paths {
|
||||
path.draw(dc, trans)
|
||||
path.draw(gc, trans)
|
||||
}
|
||||
|
||||
for _, marker := range m.markers {
|
||||
marker.draw(dc, trans)
|
||||
marker.draw(gc, trans)
|
||||
}
|
||||
|
||||
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 := dc.MeasureString(m.tileProvider.Attribution)
|
||||
_, textHeight := gc.MeasureString(m.tileProvider.Attribution)
|
||||
boxHeight := textHeight + 4.0
|
||||
dc = gg.NewContextForRGBA(croppedImg)
|
||||
dc.SetRGBA(0.0, 0.0, 0.0, 0.5)
|
||||
dc.DrawRectangle(0.0, float64(m.height)-boxHeight, float64(m.width), boxHeight)
|
||||
dc.Fill()
|
||||
dc.SetRGBA(1.0, 1.0, 1.0, 0.75)
|
||||
dc.DrawString(m.tileProvider.Attribution, 4.0, float64(m.height)-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
|
||||
}
|
||||
|
||||
@ -88,19 +88,19 @@ func (m *Marker) bounds() s2.Rect {
|
||||
return r
|
||||
}
|
||||
|
||||
func (m *Marker) draw(dc *gg.Context, trans *transformer) {
|
||||
dc.ClearPath()
|
||||
func (m *Marker) draw(gc *gg.Context, trans *transformer) {
|
||||
gc.ClearPath()
|
||||
|
||||
dc.SetLineJoin(gg.LineJoinRound)
|
||||
dc.SetLineWidth(1.0)
|
||||
gc.SetLineJoin(gg.LineJoinRound)
|
||||
gc.SetLineWidth(1.0)
|
||||
|
||||
radius := 0.5 * m.Size
|
||||
x, y := trans.ll2p(m.Position)
|
||||
dc.DrawArc(x, y-m.Size, radius, (90.0+60.0)*math.Pi/180.0, (360.0+90.0-60.0)*math.Pi/180.0)
|
||||
dc.LineTo(x, y)
|
||||
dc.ClosePath()
|
||||
dc.SetColor(m.Color)
|
||||
dc.FillPreserve()
|
||||
dc.SetRGB(0, 0, 0)
|
||||
dc.Stroke()
|
||||
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()
|
||||
}
|
||||
|
||||
@ -20,8 +20,6 @@ type Path struct {
|
||||
MapObject
|
||||
Positions []s2.LatLng
|
||||
Color color.Color
|
||||
IsFilled bool
|
||||
FillColor color.Color
|
||||
Weight float64
|
||||
}
|
||||
|
||||
@ -29,8 +27,6 @@ type Path struct {
|
||||
func ParsePathString(s string) (*Path, error) {
|
||||
path := new(Path)
|
||||
path.Color = color.RGBA{0xff, 0, 0, 0xff}
|
||||
path.IsFilled = false
|
||||
path.FillColor = color.Transparent
|
||||
path.Weight = 5.0
|
||||
|
||||
for _, ss := range strings.Split(s, "|") {
|
||||
@ -40,13 +36,6 @@ func ParsePathString(s string) (*Path, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if strings.HasPrefix(ss, "fillcolor:") {
|
||||
path.IsFilled = true
|
||||
var err error
|
||||
path.FillColor, err = ParseColorString(strings.TrimPrefix(ss, "fillcolor:"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if strings.HasPrefix(ss, "weight:") {
|
||||
var err error
|
||||
path.Weight, err = strconv.ParseFloat(strings.TrimPrefix(ss, "weight:"), 64)
|
||||
@ -77,29 +66,21 @@ func (p *Path) bounds() s2.Rect {
|
||||
return r
|
||||
}
|
||||
|
||||
func (p *Path) draw(dc *gg.Context, trans *transformer) {
|
||||
func (p *Path) draw(gc *gg.Context, trans *transformer) {
|
||||
if len(p.Positions) <= 1 {
|
||||
return
|
||||
}
|
||||
|
||||
dc.ClearPath()
|
||||
gc.ClearPath()
|
||||
|
||||
dc.SetLineWidth(p.Weight)
|
||||
dc.SetLineCap(gg.LineCapRound)
|
||||
dc.SetLineJoin(gg.LineJoinRound)
|
||||
gc.SetLineWidth(p.Weight)
|
||||
gc.SetLineCap(gg.LineCapRound)
|
||||
gc.SetLineJoin(gg.LineJoinRound)
|
||||
|
||||
for _, ll := range p.Positions {
|
||||
dc.LineTo(trans.ll2p(ll))
|
||||
gc.LineTo(trans.ll2p(ll))
|
||||
}
|
||||
|
||||
if p.IsFilled {
|
||||
dc.ClosePath()
|
||||
dc.SetColor(p.FillColor)
|
||||
dc.FillPreserve()
|
||||
dc.SetColor(p.Color)
|
||||
dc.Stroke()
|
||||
} else {
|
||||
dc.SetColor(p.Color)
|
||||
dc.Stroke()
|
||||
}
|
||||
gc.SetColor(p.Color)
|
||||
gc.Stroke()
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user