-
Notifications
You must be signed in to change notification settings - Fork 71
/
path.go
116 lines (104 loc) · 2.9 KB
/
path.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
// 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"
"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
}
// NewPath creates a new Path
func NewPath(positions []s2.LatLng, col color.Color, weight float64) *Path {
p := new(Path)
p.Positions = positions
p.Color = col
p.Weight = weight
return p
}
// ParsePathString parses a string and returns a path
func ParsePathString(s string) ([]*Path, error) {
paths := make([]*Path, 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
}
// ExtraMarginPixels returns the left, top, right, bottom pixel margin of the Path object, which is exactly the line width.
func (p *Path) ExtraMarginPixels() (float64, float64, float64, float64) {
return p.Weight, p.Weight, p.Weight, p.Weight
}
// Bounds returns the geographical boundary rect (excluding the actual pixel dimensions).
func (p *Path) Bounds() s2.Rect {
r := s2.EmptyRect()
for _, ll := range p.Positions {
r = r.AddPoint(ll)
}
return r
}
// Draw draws the object in the given graphical context.
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.LatLngToXY(ll))
}
gc.SetColor(p.Color)
gc.Stroke()
}