射线法判断地图上点是否在多边形内

1. 射线法介绍
在地图应用上,我们会经常需要判断一个点是否位于多边形区域内,这里介绍下采用射线法如何实现。
算法思想:从待判断的点向某一个方向引射线,计算和多边形交点的个数,如果个数是偶数或者0,则点在多边形外,如果是奇数,则在多边形内,如下图:
这里有两种情况需要特殊处理:
1) 射线经过顶点:当射线经过顶点时,判断就会出现异常情况。
2) 点在边上:这种情况也不能用交点个数的奇偶性来判断了,要快速地判断这个点是否在边上:

2. 代码实现——C语言

/**
   * @description 射线法判断点是否在多边形内部
   * @param {Object} p 待判断的点,格式:{ x: X坐标, y: Y坐标 }
   * @param {Array} poly 多边形顶点,数组成员的格式同 p
   * @return {String} 点 p 和多边形 poly 的几何关系
   */
  function rayCasting(p, poly) {
    var px = p.x,
        py = p.y,
        flag = false

    for(var i = 0, l = poly.length, j = l - 1; i < l; j = i, i++) {
      var sx = poly[i].x,
          sy = poly[i].y,
          tx = poly[j].x,
          ty = poly[j].y

      // 点与多边形顶点重合
      if((sx === px && sy === py) || (tx === px && ty === py)) {
        return 'on'
      }

      // 判断线段两端点是否在射线两侧
      if((sy < py && ty >= py) || (sy >= py && ty < py)) {
        // 线段上与射线 Y 坐标相同的点的 X 坐标
        var x = sx + (py - sy) * (tx - sx) / (ty - sy)

        // 点在多边形的边上
        if(x === px) {
          return 'on'
        }

        // 射线穿过多边形的边界
        if(x > px) {
          flag = !flag
        }
      }
    }

    // 射线穿过多边形边界的次数为奇数时点在多边形内
    return flag ? 'in' : 'out'
  }

3. 代码实现——Python

#!/usr/bin/env python
#coding=utf-8
import sys,os,time

class Point:
    lng = ''
    lat = ''

    def __init__(self,lng,lat):
        self.lng = lng
        self.lat = lat

    def show(self):
        print self.lng,"\t",self.lat

#求外包矩形
def getPolygonBounds(points):
    length = len(points)
    top = down = left = right = points[0]
    for i in range(1,length):
        if points[i].lng > top.lng:
            top = points[i]
        elif points[i].lng < down.lng:
            down = points[i]
        else:
            pass
        if points[i].lat > right.lat:
            right = points[i]
        elif points[i].lat < left.lat:
            left = points[i]
        else:
            pass

    point0 = Point(top.lng,left.lat)
    point1 = Point(top.lng,right.lat)
    point2 = Point(down.lng,right.lat)
    point3 = Point(down.lng,left.lat)
    polygonBounds = [point0,point1,point2,point3]
    return polygonBounds

#判断点是否在外包矩形外
def isPointInRect(point,polygonBounds):
    #print "%f>=%f %f<=%f %f>=%f %f<=%f"  % (point.lng,polygonBounds[3].lng,point.lng,polygonBounds[0].lng,point.lat,polygonBounds[3].lat,point.lat,polygonBounds[2].lat) 
    if point.lng >= polygonBounds[3].lng and point.lng <= polygonBounds[0].lng and point.lat >= polygonBounds[3].lat and point.lat <= polygonBounds[2].lat:
        return True
    else:
        return False

#采用射线法判断点集里的每个点是否在多边形集内,返回在多边形集内的点集
def isPointsInPolygons(xyset,polygonset):
    inpolygonsetxyList = []
    for points in polygonset:
        #求外包矩形
        polygonBounds = getPolygonBounds(points)
        for point in xyset:
            #判断是否在外包矩形内,如果不在,直接返回false
            if not isPointInRect(point,polygonBounds):
                #print "out of the Rect"
                continue

            length = len(points)
            p = point
            p1 = points[0]
            flag = False
            for i in range(1,length):
                p2 = points[i]
                #点与多边形顶点重合
                if (p.lng == p1.lng and p.lat == p1.lat) or (p.lng == p2.lng and p.lat == p2.lat):
                    #print "On the Vertex"
                    inpolygonsetxyList.append(p)
                    break
                #判断线段两端点是否在射线两侧
                if (p2.lat < p.lat and p1.lat >= p.lat) or (p2.lat >= p.lat and p1.lat < p.lat):
                    #print "On both sides"
                    #线段上与射线 Y 坐标相同的点的 X 坐标
                    if (p2.lat == p1.lat):
                        x = (p1.lng + p2.lng)/2
                    else:
                        #x = p2.lng + (p.lat - p2.lat)*(p1.lng - p2.lng)/(p1.lat -p.lat)
                        x = p2.lng - (p2.lat - p.lat)*(p2.lng - p1.lng)/(p2.lat - p1.lat)
                    #点在多边形的边上
                    if (x == p.lng):
                        #print "On the Edge"
                        inpolygonsetxyList.append(p)
                        break
                    #射线穿过多边形的边界
                    if (x > p.lng):
                        #print "i:[%d] throw p1[%f %f] p2[%f %f]" % (i,p1.lng,p1.lat,p2.lng,p2.lat)
                        flag = not flag
                    else:
                        #print "i:[%d] not throw p1[%f %f] p2[%f %f]" % (i,p1.lng,p1.lat,p2.lng,p2.lat)
                        pass
                else:
                    #print "i:[%d] not on both sides p1[%f %f] p2[%f %f]" % (i,p1.lng,p1.lat,p2.lng,p2.lat)
                    pass
        
                p1 = p2
            if flag:
                inpolygonsetxyList.append(p)
    return inpolygonsetxyList

if __name__ == "__main__":
    xyset = []
    polygonset = []
   
    #加载所有的多边形到polygonset
    polyList = ["116.325011 31.068331 116.441755 31.525895 117.184675 31.290317 116.882203 30.927891 116.500131 31.086453 116.325011 31.068331","116.393091 39.921916 116.393413 39.914510 116.393091 39.921916"]
    for line in polyList:
        line = line.strip()
        points = []
        strList = line.split()
        pointslen = len(strList) 
        if (pointslen%2 != 0):
            #print "ERROR: invalid pointslen[%d]" % pointslen
            continue
        for i in range(0,pointslen,2):
            temp = Point(float(strList[i]),float(strList[i+1]))
            points.append(temp)
        mid=pointslen/2 - 1
        #print "mid:[%d]" % mid
        if (points[0].lng != points[mid].lng or points[0].lat != points[mid].lat):
            #print "ERROR: invalid polygon,begin[%f,%f],end[%f,%f]" % (points[0].lng,points[0].lat,points[pointslen/2].lng,points[pointslen/2].lat)
            continue
        polygonset.append(points)

    #加载map的所有输入点到点集xyset
	xyList = ["116.860971 31.467001","116.256027 32.074063","116.616875 31.195181"]
    for line in xyList:
        line = line.strip()
        xy = line.split()
        if len(xy) != 2:
            continue
        try:
            x = float(xy[0])
            y = float(xy[1])
        except ValueError:
            continue
        point = Point(x,y)
        xyset.append(point)
    if not xyset:
        sys.exit(0)
		
    inpolygonList = isPointsInPolygons(xyset,polygonset)
  
    for point in inpolygonList:
        point.show()

        

参考文章:
http://blog.csdn.net/u010429424/article/details/44830379
http://www.html-js.com/article/1528
http://www.360doc.com/content/12/1105/14/7662927_245870913.shtml


你可能感兴趣的:(算法)