实现某坐标点是否在某区域范围内
代码
package cronjob
import (
"fmt"
"math"
"strconv"
"strings"
)
//实例化一个面
func NewGISArea(points []Point) *Area {
area := new(Area)
area.Points = points
return area
}
type Point struct {
Lng float64 `json:"lng"` //经度(180°W-180°E) -180 - 0 - 180
Lat float64 `json:"lat"` //纬度(90°S-90°N)-90 -0 -90
}
type Area struct {
Points []Point //多边形坐标 顺时针方向
}
//判断一个点是否在一个面内
//如果点位于多边形的顶点或边上,也算做点在多边形内,直接返回true
//@param point 指定点坐标
func (a *Area) PointInArea(point Point) bool {
pointNum := len(a.Points) //点个数
intersectCount := 0 //cross points count of x
precision := 2e-10 //浮点类型计算时候与0比较时候的容差
p1 := Point{} //neighbour bound vertices
p2 := Point{}
p := point //测试点
p1 = a.Points[0] //left vertex
for i := 0; i < pointNum; i++ {
if (p.Lng == p1.Lng && p.Lat == p1.Lat) {
return true
}
p2 = a.Points[i%pointNum]
if (p.Lat < math.Min(p1.Lat, p2.Lat) || p.Lat > math.Max(p1.Lat, p2.Lat)) {
p1 = p2
continue //next ray left point
}
if (p.Lat > math.Min(p1.Lat, p2.Lat) && p.Lat < math.Max(p1.Lat, p2.Lat)) {
if (p.Lng <= math.Max(p1.Lng, p2.Lng)) { //x is before of ray
if (p1.Lat == p2.Lat && p.Lng >= math.Min(p1.Lng, p2.Lng)) {
return true
}
if (p1.Lng == p2.Lng) { //ray is vertical
if (p1.Lng == p.Lng) { //overlies on a vertical ray
return true
} else { //before ray
intersectCount++
}
} else { //cross point on the left side
xinters := (p.Lat-p1.Lat)*(p2.Lng-p1.Lng)/(p2.Lat-p1.Lat) + p1.Lng
if (math.Abs(p.Lng-xinters) < precision) {
return true
}
if (p.Lng < xinters) { //before ray
intersectCount++
}
}
}
} else { //special case when ray is crossing through the vertex
if (p.Lat == p2.Lat && p.Lng <= p2.Lng) { //p crossing over p2
p3 := a.Points[(i+1)%pointNum]
if (p.Lat >= math.Min(p1.Lat, p3.Lat) && p.Lat <= math.Max(p1.Lat, p3.Lat)) {
intersectCount++
} else {
intersectCount += 2
}
}
}
p1 = p2 //next ray left point
}
if intersectCount%2 == 0 { //偶数在多边形外
return false
} else { //奇数在多边形内
return true
}
}
/**
CheckNewGISArea 检测坐标点是否在某一区域内
Param: areastring 区域范围 string
pointLng 检测坐标点 经度 string
pointLat 检测坐标点 纬度 string
Return: bool
false 否
true 是
*/
func CheckNewGISArea(areastring string,pointLng string,pointLat string) bool {
var poi string
poi = "118.183506,39.626009,118.190501,39.626009,118.190501,39.618406,118.183506,39.618406,118.152262,39.709224,118.16106,39.709224,118.16106,39.704288,118.152262,39.704288"
poi = areastring
strpoi := strings.Split(poi,",")
var pois []Point
for i:=0; i<len(strpoi); i=i+2 {
fmt.Println("Lng:" + strpoi[i] + ",Lat:" + strpoi[i+1])
//string到float64/32
float64a,_ := strconv.ParseFloat(strpoi[i], 64)
float64b,_ := strconv.ParseFloat(strpoi[i+1], 64)
pois = append(pois, Point{float64a,float64b}) //结构体数组赋值
fmt.Println(pois)
}
//points := []Point{{Lng:1,Lat:1},{Lng:-1,Lat:2},{Lng:-3,Lat:8},{Lng:0,Lat:4},{Lng:2.1,Lat:6.8},{Lng:9,Lat:-3.1}}
fmt.Println(pois)
area := NewGISArea(pois)
var positionLng float64
var positionLat float64
positionLng,_ = strconv.ParseFloat(pointLng,64)
positionLat,_ = strconv.ParseFloat(pointLat,64)
point := Point{Lng:positionLng,Lat:positionLat}
rt := area.PointInArea(point)
fmt.Print("点是否在多边形内:",rt)
return rt
}
测试代码:
package cronjob
// 作测试文件名要注意 需要带_test字样
import (
"fmt"
"strconv"
"strings"
"testing"
)
func TestNewGISArea(t *testing.T) {
var poi string
poi = "118.183506,39.626009,118.190501,39.626009,118.190501,39.618406,118.183506,39.618406,118.152262,39.709224,118.16106,39.709224,118.16106,39.704288,118.152262,39.704288"
strpoi := strings.Split(poi,",")
var pois []Point
for i:=0; i<len(strpoi); i=i+2 {
fmt.Println("Lng:" + strpoi[i] + ",Lat:" + strpoi[i+1])
//string到float64/32
float64a,_ := strconv.ParseFloat(strpoi[i], 64)
float64b,_ := strconv.ParseFloat(strpoi[i+1], 64)
pois = append(pois, Point{float64a,float64b}) //结构体数组赋值
fmt.Println(pois)
}
//points := []Point{{Lng:1,Lat:1},{Lng:-1,Lat:2},{Lng:-3,Lat:8},{Lng:0,Lat:4},{Lng:2.1,Lat:6.8},{Lng:9,Lat:-3.1}}
fmt.Println(pois)
area := NewGISArea(pois)
var positionLng float64
var positionLat float64
positionLng = 118.183506
positionLat = 39.626009
point := Point{Lng:positionLng,Lat:positionLat}
rt := area.PointInArea(point)
fmt.Print("点是否在多边形内:",rt)
}