41 lines
1.3 KiB
Go
41 lines
1.3 KiB
Go
package numerics
|
|
|
|
import "math"
|
|
|
|
// PointInPolygon reports whether (lat, lng) lies inside the closed polygon
|
|
// whose vertices are given as parallel latitude/longitude slices (degrees).
|
|
//
|
|
// The test is ray casting in plate-carrée space. Every longitude is
|
|
// normalised to within 180° of the first vertex before testing, so a polygon
|
|
// spanning the antimeridian is handled correctly as long as it spans no more
|
|
// than 180° in longitude. polyLat and polyLng must have equal length >= 3.
|
|
func PointInPolygon(lat, lng float64, polyLat, polyLng []float64) bool {
|
|
n := len(polyLat)
|
|
if n < 3 || len(polyLng) != n {
|
|
return false
|
|
}
|
|
ref := polyLng[0]
|
|
qx := NormalizeLng(lng, ref)
|
|
|
|
inside := false
|
|
for i, j := 0, n-1; i < n; j, i = i, i+1 {
|
|
yi, yj := polyLat[i], polyLat[j]
|
|
xi := NormalizeLng(polyLng[i], ref)
|
|
xj := NormalizeLng(polyLng[j], ref)
|
|
|
|
if (yi > lat) != (yj > lat) {
|
|
xIntersect := (xj-xi)*(lat-yi)/(yj-yi) + xi
|
|
if qx < xIntersect {
|
|
inside = !inside
|
|
}
|
|
}
|
|
}
|
|
return inside
|
|
}
|
|
|
|
// NormalizeLng rewrites v so that it lies within 180° of ref. For example,
|
|
// NormalizeLng(350, 10) returns -10. Used to make longitude comparisons
|
|
// continuous across the antimeridian.
|
|
func NormalizeLng(v, ref float64) float64 {
|
|
return ref + math.Mod(v-ref+540, 360) - 180
|
|
}
|