-
-
Notifications
You must be signed in to change notification settings - Fork 38
Basic Planar fns, Simplifier interface #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1 @@ | ||
package encoding | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/go-spatial/geom" | ||
) | ||
|
||
type ErrUnknownGeometry struct { | ||
Geom geom.Geometry | ||
} | ||
|
||
func (e ErrUnknownGeometry) Error() string { | ||
return fmt.Sprintf("unknown geometry: %T", e.Geom) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package geom | ||
|
||
import "fmt" | ||
|
||
// ErrUnknownGeometry represents an objects that is not a known geom geometry. | ||
type ErrUnknownGeometry struct { | ||
Geom Geometry | ||
} | ||
|
||
func (e ErrUnknownGeometry) Error() string { | ||
return fmt.Sprintf("unknown geometry: %T", e.Geom) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package planar | ||
|
||
import ( | ||
"math" | ||
) | ||
|
||
const Rad = math.Pi / 180 | ||
|
||
type PointLineDistanceFunc func(line [2][2]float64, point [2]float64) float64 | ||
|
||
// PerpendicularDistance provides the distance between a line and a point in Euclidean space. | ||
// ref: https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line#Line_defined_by_two_points | ||
func PerpendicularDistance(line [2][2]float64, point [2]float64) float64 { | ||
|
||
deltaX := line[1][0] - line[0][0] | ||
deltaY := line[1][1] - line[0][1] | ||
deltaXSq := deltaX * deltaX | ||
deltaYSq := deltaY * deltaY | ||
|
||
num := math.Abs((deltaY * point[0]) - (deltaX * point[1]) + (line[1][0] * line[0][1]) - (line[1][1] * line[0][0])) | ||
denom := math.Sqrt(deltaYSq + deltaXSq) | ||
if denom == 0 { | ||
return 0 | ||
} | ||
return num / denom | ||
} | ||
|
||
// Slope — finds the Slope of a line | ||
func Slope(line [2][2]float64) (m, b float64, defined bool) { | ||
dx := line[1][0] - line[0][0] | ||
dy := line[1][1] - line[0][1] | ||
if dx == 0 || dy == 0 { | ||
// if dx == 0 then m == 0; and the intercept is y. | ||
// However if the lines are verticle then the slope is not defined. | ||
return 0, line[0][1], dx != 0 | ||
} | ||
m = dy / dx | ||
b = line[0][1] - (m * line[0][0]) | ||
return m, b, true | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package planar | ||
|
||
import ( | ||
"strconv" | ||
"testing" | ||
) | ||
|
||
func TestSlope(t *testing.T) { | ||
type tcase struct { | ||
line [2][2]float64 | ||
m, b float64 | ||
defined bool | ||
} | ||
|
||
fn := func(t *testing.T, tc tcase) { | ||
t.Parallel() | ||
gm, gb, gd := Slope(tc.line) | ||
if tc.defined != gd { | ||
t.Errorf("sloped defined, expected %v got %v", tc.defined, gd) | ||
return | ||
} | ||
// if the slope is not defined, line is verticle and m,b don't have good values. | ||
if !tc.defined { | ||
return | ||
} | ||
if tc.m != gm { | ||
t.Errorf("sloped, expected %v got %v", tc.m, gm) | ||
|
||
} | ||
if tc.b != gb { | ||
t.Errorf("sloped intercept, expected %v got %v", tc.b, gb) | ||
} | ||
} | ||
tests := []tcase{ | ||
{ | ||
line: [2][2]float64{{0, 0}, {10, 10}}, | ||
m: 1, | ||
b: 0, | ||
defined: true, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With all the test framework in place, you'll want to add some more tests. For instance, this won't catch a case where x/y get swapped. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I don't have very good tests in there right now. I was still in the process of converting over the code, and did not have time to add all the test cases. This was just to get the framework for the test in place, to make it easy to add more. |
||
}, | ||
{ | ||
line: [2][2]float64{{1, 7}, {1, 17}}, | ||
defined: false, | ||
}, | ||
} | ||
for i := range tests { | ||
tc := tests[i] | ||
t.Run(strconv.FormatInt(int64(i), 10), func(t *testing.T) { fn(t, tc) }) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
package planar | ||
|
||
import "github.com/go-spatial/geom" | ||
|
||
// Simplifer is an interface for Simplifying geometries. | ||
type Simplifer interface { | ||
Simplify(linestring [][2]float64, isClosed bool) ([][2]float64, error) | ||
} | ||
|
||
func simplifyPolygon(simplifer Simplifer, plg [][][2]float64, isClosed bool) (ret [][][2]float64, err error) { | ||
ret = make([][][2]float64, len(plg)) | ||
for i := range plg { | ||
ls, err := simplifer.Simplify(plg[i], isClosed) | ||
if err != nil { | ||
return nil, err | ||
} | ||
ret[i] = ls | ||
} | ||
return ret, nil | ||
|
||
} | ||
|
||
// Simplify will simplify the provided geometry using the provided simplifer. | ||
// If the simplifer is nil, no simplification will be attempted. | ||
func Simplify(simplifer Simplifer, geometry geom.Geometry) (geom.Geometry, error) { | ||
|
||
if simplifer == nil { | ||
return geometry, nil | ||
} | ||
|
||
switch gg := geometry.(type) { | ||
|
||
case geom.Collectioner: | ||
|
||
geos := gg.Geometries() | ||
coll := make([]geom.Geometry, len(geos)) | ||
for i := range geos { | ||
geo, err := Simplify(simplifer, geos[i]) | ||
if err != nil { | ||
return nil, err | ||
} | ||
coll[i] = geo | ||
} | ||
return geom.Collection(coll), nil | ||
|
||
case geom.MultiPolygoner: | ||
|
||
plys := gg.Polygons() | ||
mply := make([][][][2]float64, len(plys)) | ||
for i := range plys { | ||
ply, err := simplifyPolygon(simplifer, plys[i], true) | ||
if err != nil { | ||
return nil, err | ||
} | ||
mply[i] = ply | ||
} | ||
return geom.MultiPolygon(mply), nil | ||
|
||
case geom.Polygoner: | ||
|
||
ply, err := simplifyPolygon(simplifer, gg.LinearRings(), true) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return geom.Polygon(ply), nil | ||
|
||
case geom.MultiLineStringer: | ||
|
||
mls, err := simplifyPolygon(simplifer, gg.LineStrings(), false) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return geom.MultiLineString(mls), nil | ||
|
||
case geom.LineStringer: | ||
|
||
ls, err := simplifer.Simplify(gg.Verticies(), false) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return geom.LineString(ls), nil | ||
|
||
default: // Points, MutliPoints or anything else. | ||
return geometry, nil | ||
|
||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a reference to: https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Line defined by two points" section
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done