Skip to content

Commit 34b8b0d

Browse files
authored
Merge 8cb8bf5 into 802ab25
2 parents 802ab25 + 8cb8bf5 commit 34b8b0d

File tree

5 files changed

+228
-9
lines changed

5 files changed

+228
-9
lines changed

encoding/mvt/feature.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import (
66
"log"
77

88
"github.com/go-spatial/geom"
9-
"github.com/go-spatial/geom/encoding/wkt"
109
vectorTile "github.com/go-spatial/geom/encoding/mvt/vector_tile"
10+
"github.com/go-spatial/geom/encoding/wkt"
1111
)
1212

1313
var (
@@ -36,7 +36,6 @@ func (f Feature) String() string {
3636
return fmt.Sprintf("encoding error for geom geom, %v", err)
3737
}
3838

39-
4039
if f.ID != nil {
4140
return fmt.Sprintf("{Feature: %v, GEO: %v, Tags: %+v}", *f.ID, g, f.Tags)
4241
}
@@ -255,6 +254,23 @@ func encodeGeometry(ctx context.Context, geometry geom.Geometry) (g []uint32, vt
255254
}
256255
return g, vectorTile.Tile_POLYGON, nil
257256

257+
case *geom.MultiPolygon:
258+
if t == nil {
259+
return g, vectorTile.Tile_POLYGON, nil
260+
}
261+
262+
polygons := t.Polygons()
263+
for _, p := range polygons {
264+
lines := geom.Polygon(p).LinearRings()
265+
for _, l := range lines {
266+
points := geom.LineString(l).Verticies()
267+
g = append(g, c.MoveTo(points[0])...)
268+
g = append(g, c.LineTo(points[1:]...)...)
269+
g = append(g, c.ClosePath())
270+
}
271+
}
272+
return g, vectorTile.Tile_POLYGON, nil
273+
258274
default:
259275
return nil, vectorTile.Tile_UNKNOWN, ErrUnknownGeometryType
260276
}

planar/makevalid/walker/walker_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ func TestPolygonForTriangle(t *testing.T) {
218218
},
219219
idx: []int{0},
220220
polygons: [][][][2]float64{
221-
{{{0, 0}, {10, 0}, {10, 10}, {0, 10}, {0, 7}}, {{0, 7}, {7, 7}, {7, 2}}},
221+
{{{0, 0}, {10, 0}, {10, 10}, {0, 10}}, {{0, 7}, {7, 7}, {7, 2}}},
222222
{{{0, 0}, {10, 0}, {10, 10}, {0, 10}}},
223223
},
224224
seenTriangles: [][]int{

planar/planar.go

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,10 @@ func IsPointOnLine(pt [2]float64, l1, l2 [2]float64) bool {
5151
switch {
5252
case !defined:
5353
// line is vertical, so if we the y values are the same it's on the line.
54-
return cmp.Float(pt[1], l1[1])
55-
case m == 0:
56-
// line is horizontal, so if the x values are the same it's on the line.
5754
return cmp.Float(pt[0], l1[0])
55+
case m == 0:
56+
// line is horizontal, so if the y values are the same it's on the line.
57+
return cmp.Float(pt[1], l1[1])
5858
default:
5959
y := (m * pt[0]) + b
6060
return cmp.Float(pt[1], y)
@@ -89,6 +89,20 @@ func IsPointOnLineSegment(pt geom.Point, seg geom.Line) bool {
8989

9090
}
9191

92+
// PointOnLineAt will return a point on the given line at the distance from the
93+
// origin of the line
94+
func PointOnLineAt(ln geom.Line, distance float64) geom.Point {
95+
96+
lineDist := math.Sqrt(ln.LenghtSquared())
97+
ratio := distance / lineDist
98+
var x, y float64
99+
100+
x = ln[0][0] + (ratio * (ln[1][0] - ln[0][0]))
101+
y = ln[0][1] + (ratio * (ln[1][1] - ln[0][1]))
102+
return geom.Point{x, y}
103+
}
104+
105+
// IsCCW will check if the given points are in counter-clockwise order.
92106
func IsCCW(a, b, c geom.Point) bool {
93107
return geom.Triangle{[2]float64(a), [2]float64(b), [2]float64(c)}.Area() > 0
94108
}

planar/planar_test.go

Lines changed: 188 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"strconv"
66
"testing"
77

8+
"github.com/go-spatial/geom/cmp"
9+
810
"github.com/go-spatial/geom"
911
)
1012

@@ -51,6 +53,71 @@ func TestSlope(t *testing.T) {
5153
t.Run(strconv.FormatInt(int64(i), 10), func(t *testing.T) { fn(t, tc) })
5254
}
5355
}
56+
func TestIsPointOnLine(t *testing.T) {
57+
type tcase struct {
58+
desc string
59+
point geom.Point
60+
segment geom.Line
61+
expected bool
62+
}
63+
fn := func(tc tcase) (string, func(*testing.T)) {
64+
return fmt.Sprintf("%v on %v", tc.point, tc.segment),
65+
func(t *testing.T) {
66+
if tc.expected != IsPointOnLine(tc.point, tc.segment[0], tc.segment[1]) {
67+
t.Errorf("expected %v, got %v", tc.expected, !tc.expected)
68+
}
69+
}
70+
}
71+
tests := [...]tcase{
72+
{
73+
// Diagonal line
74+
point: geom.Point{1, 1},
75+
segment: geom.Line{{0, 0}, {1, 10}},
76+
},
77+
{
78+
// Vertical line
79+
point: geom.Point{1, 1},
80+
segment: geom.Line{{0, 0}, {0, 10}},
81+
},
82+
{
83+
// Vertical line
84+
point: geom.Point{1, 1},
85+
segment: geom.Line{{0, 10}, {10, 10}},
86+
},
87+
{
88+
// horizontal line
89+
point: geom.Point{1, 1},
90+
segment: geom.Line{{1, 0}, {1, 10}},
91+
expected: true,
92+
},
93+
{
94+
// horizontal line
95+
point: geom.Point{1, 100},
96+
segment: geom.Line{{1, 0}, {1, 10}},
97+
expected: true,
98+
},
99+
{
100+
// horizontal line
101+
point: geom.Point{1, -100},
102+
segment: geom.Line{{1, 0}, {1, 10}},
103+
expected: true,
104+
},
105+
{
106+
// horizontal line on close to the end point
107+
point: geom.Point{-0.5, 0},
108+
segment: geom.Line{{1, 0}, {1, 10}},
109+
},
110+
{
111+
// horizontal line on the end point
112+
point: geom.Point{1, 0},
113+
segment: geom.Line{{1, 0}, {1, 10}},
114+
expected: true,
115+
},
116+
}
117+
for _, tc := range tests {
118+
t.Run(fn(tc))
119+
}
120+
}
54121

55122
func TestIsPointOnLineSegment(t *testing.T) {
56123
type tcase struct {
@@ -63,7 +130,7 @@ func TestIsPointOnLineSegment(t *testing.T) {
63130
return fmt.Sprintf("%v on %v", tc.point, tc.segment),
64131
func(t *testing.T) {
65132
if tc.expected != IsPointOnLineSegment(tc.point, tc.segment) {
66-
t.Errorf("got %v, expected %v", !tc.expected, tc.expected)
133+
t.Errorf("expected %v, got %v", tc.expected, !tc.expected)
67134
}
68135
}
69136
}
@@ -105,3 +172,123 @@ func TestIsPointOnLineSegment(t *testing.T) {
105172
t.Run(fn(tc))
106173
}
107174
}
175+
176+
func TestIsCCW(t *testing.T) {
177+
type tcase struct {
178+
desc string
179+
p1, p2, p3 geom.Point
180+
is bool
181+
}
182+
fn := func(tc tcase) func(*testing.T) {
183+
return func(t *testing.T) {
184+
got := IsCCW(tc.p1, tc.p2, tc.p3)
185+
if got != tc.is {
186+
t.Errorf(
187+
"%v:%v:%v, expected %v got %v",
188+
tc.p1, tc.p2, tc.p3,
189+
tc.is, got,
190+
)
191+
return
192+
}
193+
}
194+
}
195+
196+
tests := []tcase{
197+
{
198+
p1: geom.Point{0, 0},
199+
p2: geom.Point{1, 0},
200+
p3: geom.Point{1, 1},
201+
is: true,
202+
},
203+
{
204+
p1: geom.Point{204, 694},
205+
p2: geom.Point{-2511, -3640},
206+
p3: geom.Point{3462, -3640},
207+
is: true,
208+
},
209+
{
210+
p2: geom.Point{204, 694},
211+
p3: geom.Point{-2511, -3640},
212+
p1: geom.Point{3462, -3640},
213+
is: true,
214+
},
215+
{
216+
p3: geom.Point{204, 694},
217+
p1: geom.Point{-2511, -3640},
218+
p2: geom.Point{3462, -3640},
219+
is: true,
220+
},
221+
{
222+
p1: geom.Point{-2511, -3640},
223+
p2: geom.Point{204, 694},
224+
p3: geom.Point{3462, -3640},
225+
is: false,
226+
},
227+
{
228+
p1: geom.Point{-2511, 3640},
229+
p2: geom.Point{204, 694},
230+
p3: geom.Point{3462, -3640},
231+
is: false,
232+
},
233+
}
234+
235+
for _, tc := range tests {
236+
t.Run(tc.desc, fn(tc))
237+
}
238+
}
239+
func TestPointOnLineAt(t *testing.T) {
240+
241+
type tcase struct {
242+
desc string
243+
line geom.Line
244+
distance float64
245+
point geom.Point
246+
}
247+
248+
fn := func(tc tcase) func(*testing.T) {
249+
return func(t *testing.T) {
250+
got := PointOnLineAt(tc.line, tc.distance)
251+
if !cmp.GeomPointEqual(tc.point, got) {
252+
t.Errorf("point, expected %v, got %v", tc.point, got)
253+
}
254+
}
255+
}
256+
257+
tests := []tcase{
258+
{
259+
desc: "simple test case",
260+
line: geom.Line{{0, 0}, {10, 0}},
261+
distance: 5.0,
262+
point: geom.Point{5, 0},
263+
},
264+
{
265+
line: geom.Line{{204, 694}, {-2511, -3640}},
266+
distance: 100,
267+
point: geom.Point{150.9122535714552, 609.2551406919657},
268+
},
269+
{
270+
line: geom.Line{{204, 694}, {475.500, 8853}},
271+
distance: 100,
272+
point: geom.Point{207.3257728713106, 793.9446808730132},
273+
},
274+
{
275+
line: geom.Line{{204, 694}, {369, 793}},
276+
distance: 100,
277+
point: geom.Point{289.7492925712544, 745.4495755427527},
278+
},
279+
{
280+
line: geom.Line{{204, 694}, {426, 539}},
281+
distance: 100,
282+
point: geom.Point{285.9925374282251, 636.7529581019149},
283+
},
284+
{
285+
line: geom.Line{{204, 694}, {273, 525}},
286+
distance: 100,
287+
point: geom.Point{241.79928289224065, 601.4191476987149},
288+
},
289+
}
290+
291+
for _, tc := range tests {
292+
t.Run(tc.desc, fn(tc))
293+
}
294+
}

planar/triangulate/gdey/quadedge/subdivision/subdivision.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,11 +253,13 @@ func (sd *Subdivision) InsertSite(x geom.Point) bool {
253253
}
254254
}
255255

256+
// appendNonrepeate will append points to an array if that point
257+
// is not the same as the point immediately prior
256258
func appendNonrepeat(pts []geom.Point, v geom.Point) []geom.Point {
257259
if len(pts) == 0 || cmp.GeomPointEqual(v, pts[len(pts)-1]) {
258-
return append(pts, v)
260+
return pts
259261
}
260-
return pts
262+
return append(pts, v)
261263
}
262264

263265
func selectCorrectEdges(from, to *quadedge.Edge) (cfrom, cto *quadedge.Edge) {

0 commit comments

Comments
 (0)