zoukankan      html  css  js  c++  java
  • 射线法判断点是否在多边形内(可用于地图)

    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'  
      }
    C实现

    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,"	",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()  
      
       
    Python实现

    3. 代码实现——iOS

    - (BOOL)rayCasting:(NSValue *)p array:(NSArray *)poly{
        CLLocationDegrees px = [p MACoordinateValue].longitude;
        CLLocationDegrees py = [p MACoordinateValue].latitude;
        BOOL flag = false;
        
        
        NSInteger j ;//= poly.count - 1;
        for (int i = 0; i < poly.count ; i ++) {
            CLLocationCoordinate2D point = [poly[i] MACoordinateValue];
            j = i - 1;
            if (i == 0) {
                j = poly.count - 1;
            }
            CLLocationCoordinate2D comparePoint = [poly[j] MACoordinateValue];
            CLLocationDegrees sx = point.longitude;
            CLLocationDegrees sy = point.latitude;
            CLLocationDegrees tx = comparePoint.longitude;
            CLLocationDegrees ty = comparePoint.latitude;
            
            // 点与多边形顶点重合
            if((sx == px && sy == py) || (tx == px && ty == py)) {
                return YES;
            }
            
            // 判断线段两端点是否在射线两侧
            if((sy < py && ty >= py) || (sy >= py && ty < py)) {
                // 线段上与射线 Y 坐标相同的点的 X 坐标
                CLLocationDegrees x = sx + (py - sy) * (tx - sx) / (ty - sy);
                
                // 点在多边形的边上
                if(x == px) {
                    return YES;
                }
                
                // 射线穿过多边形的边界
                if(x > px) {
                    flag = !flag;
                }
            }
        }
        
        // 射线穿过多边形边界的次数为奇数时点在多边形内
        return flag ? YES : NO;
    }
    3. 代码实现——swift
    func rayCasting(p : CGPoint, poly : [CGPoint]) -> String  {
        var px = p.x ,
        py = p.y ,
        flag = false
        
        for(var i = 0, l = poly.count, j = l - 1; i < l; j = i, i++) {
           
            if i == 0 {
                j = l-1;
            }else{
                j = i - 1;
            }
            
            let 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 坐标
                let x = sx + (py - sy) * (tx - sx) / (ty - sy)
                
                // 点在多边形的边上
                if(x == px) {
                    return "on"
                }
                
                // 射线穿过多边形的边界
                if(x > px) {
                    flag = !flag
                }
            }
        }
        
        // 射线穿过多边形边界的次数为奇数时点在多边形内
        return flag ? "in" : "out"
    }
    swift实现
     
    参考文章:
  • 相关阅读:
    SpringBoot list查询方法
    eclipse创建web项目
    loadrunner获取返回值为乱码
    连接数据库
    lr并发量和迭代的区别
    LoadRunner11.00入门教程出现的问题
    python学习杂记--函数参数的设置
    adb logcat的命令行开启和关闭
    python学习杂记--pycharm控制台输出乱码
    python学习杂记--装饰器
  • 原文地址:https://www.cnblogs.com/On1Key/p/5673886.html
Copyright © 2011-2022 走看看