mirror of
https://github.com/Luzifer/staticmap.git
synced 2024-12-21 05:11:18 +00:00
129 lines
3 KiB
Go
129 lines
3 KiB
Go
|
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()
|
||
|
}
|