1. 程式人生 > >點位是否在不規則的地圖範圍內

點位是否在不規則的地圖範圍內

https://blog.csdn.net/u013239236/article/details/52213661

需求是:一個點(經緯度)是否在一個多邊形內部,多邊形有多個點構成,每個點是一個實際的經緯度座標,有多個點構成一個多邊形,

演算法數學上實現思路: 判斷一個點是在一個多邊形內部的集中情況
第一:目標點在多邊形的某一個頂點上,我們認為目標點在多邊形內部
第二:目標點在多邊形的任意一天邊上,我們認為目標點在多邊形內部
第三:這種情況就比較複雜了,不在某天邊上,也不和任何一個頂點重合.這時候就需要我們自己去算了,解決方案是將目標點的Y座標與多邊形的每一個點進行比較,我們會得到一個目標點所在的行與多邊形邊的交點的列表。如果目標點的兩邊點的個數都是奇數個則該目標點在多邊形內,否則在多邊形外。

這種演算法適合凸多邊形也適合凹多邊形,所以是一種通用的演算法,同時也解決了多邊形的點的順序不同導致的形狀不同,比如一個五邊形,可以是凸五邊形,也可以是一個凹五邊形,這個根據點的位置和順序決定的。


有了數學上的實現思路,辣麼我們就可以用java 或者去他語言去實現一個點(經緯度)是否在一個多邊形內部了(多個點構成)。

我們先寫一個 對點和線的一些公用方法,


package cn.liuzw.point;
import java.util.ArrayList;
 
/**
 *  <span style="font-family: Arial; font-size: 14px; line-height: 26px;">點和線的一些公用方法</span><br/>
 * 
 * @author liuZhiwei
 * 2016年8月6日 下午3:48:38
 */
public class Point {
    
    /**
     *  是否有 橫斷<br/>
     *  引數為四個點的座標
     * @param px1
     * @param py1
     * @param px2
     * @param py2
     * @param px3
     * @param py3
     * @param px4
     * @param py4
     * @return  
     */
    public boolean isIntersect ( double px1 , double py1 , double px2 , double py2 , double px3 , double py3 , double px4 ,  
            double py4 )  
    {  
        boolean flag = false;  
        double d = (px2 - px1) * (py4 - py3) - (py2 - py1) * (px4 - px3);  
        if ( d != 0 )  
        {  
            double r = ((py1 - py3) * (px4 - px3) - (px1 - px3) * (py4 - py3)) / d;  
            double s = ((py1 - py3) * (px2 - px1) - (px1 - px3) * (py2 - py1)) / d;  
            if ( (r >= 0) && (r <= 1) && (s >= 0) && (s <= 1) )  
            {  
                flag = true;  
            }  
        }  
        return flag;  
    } 
    /**
     *  目標點是否在目標邊上邊上<br/>
     *  
     * @param px0 目標點的經度座標
     * @param py0 目標點的緯度座標
     * @param px1 目標線的起點(終點)經度座標
     * @param py1 目標線的起點(終點)緯度座標
     * @param px2 目標線的終點(起點)經度座標
     * @param py2 目標線的終點(起點)緯度座標
     * @return
     */
    public boolean isPointOnLine ( double px0 , double py0 , double px1 , double py1 , double px2 , double py2 )  
    {  
        boolean flag = false;  
        double ESP = 1e-9;//無限小的正數
        if ( (Math.abs(Multiply(px0, py0, px1, py1, px2, py2)) < ESP) && ((px0 - px1) * (px0 - px2) <= 0)  
                && ((py0 - py1) * (py0 - py2) <= 0) )  
        {  
            flag = true;  
        }  
        return flag;  
    } 
    public double Multiply ( double px0 , double py0 , double px1 , double py1 , double px2 , double py2 )  
    {  
        return ((px1 - px0) * (py2 - py0) - (px2 - px0) * (py1 - py0));  
    }
    /**
     * 判斷目標點是否在多邊形內(由多個點組成)<br/>
     * 
     * @param px 目標點的經度座標
     * @param py 目標點的緯度座標
     * @param polygonXA 多邊形的經度座標集合
     * @param polygonYA 多邊形的緯度座標集合
     * @return
     */
    public boolean isPointInPolygon ( double px , double py , ArrayList<Double> polygonXA , ArrayList<Double> polygonYA )  
    {  
        boolean isInside = false;  
        double ESP = 1e-9;  
        int count = 0;  
        double linePoint1x;  
        double linePoint1y;  
        double linePoint2x = 180;  
        double linePoint2y;  
 
        linePoint1x = px;  
        linePoint1y = py;  
        linePoint2y = py;  
 
        for (int i = 0; i < polygonXA.size() - 1; i++)  
        {  
            double cx1 = polygonXA.get(i);  
            double cy1 = polygonYA.get(i);  
            double cx2 = polygonXA.get(i + 1);  
            double cy2 = polygonYA.get(i + 1); 
            //如果目標點在任何一條線上
            if ( isPointOnLine(px, py, cx1, cy1, cx2, cy2) )  
            {  
                return true;  
            }
            //如果線段的長度無限小(趨於零)那麼這兩點實際是重合的,不足以構成一條線段
            if ( Math.abs(cy2 - cy1) < ESP )  
            {  
                continue;  
            }  
            //第一個點是否在以目標點為基礎衍生的平行緯度線
            if ( isPointOnLine(cx1, cy1, linePoint1x, linePoint1y, linePoint2x, linePoint2y) )  
            {  
                //第二個點在第一個的下方,靠近赤道緯度為零(最小緯度)
                if ( cy1 > cy2 )  
                    count++;  
            }
            //第二個點是否在以目標點為基礎衍生的平行緯度線
            else if ( isPointOnLine(cx2, cy2, linePoint1x, linePoint1y, linePoint2x, linePoint2y) )  
            {  
                //第二個點在第一個的上方,靠近極點(南極或北極)緯度為90(最大緯度)
                if ( cy2 > cy1 )  
                    count++;  
            }
            //由兩點組成的線段是否和以目標點為基礎衍生的平行緯度線相交
            else if ( isIntersect(cx1, cy1, cx2, cy2, linePoint1x, linePoint1y, linePoint2x, linePoint2y) )  
            {  
                count++;  
            }  
        }  
        if ( count % 2 == 1 )  
        {  
            isInside = true;  
        }  
 
        return isInside;  
    }  
}
裡面有三個方法,兩個原子方法,一個主方法
第一個判斷是否與橫斷 目標端和y軸 也就是東經0也叫西經180的一條經度線構成的一天平行於緯度(赤道)的一天線段和多邊形的任意一天線段是否有橫斷。引數是四個點的座標分別是目標點和目標點組成線段的一個點(在西經180度上) 多邊形的任意連續的兩個點(可以構成一條線段)

第二個是目標點是否在目標邊上邊上,

點三個是主方法,外接只需要呼叫這個方法,這個而方法的邏輯就是,第一步先判斷目標點的座標是否和多邊形的任意一個頂點的座標重合,如果重合直接放回true,說明目標點在該多邊形內,如果都不重合,在判斷是否在多邊形的任意一條邊上,如果在 返回true,如果以上兩種情況都不成立在理由低三種演算法,計算目標點平行於赤道的線和多邊形的焦點,在判斷焦點的個數。如果l兩邊的焦點是基數個返回true。

我可以寫main方法去測試該程式。

package cn.liuzw.bean;
 
import java.util.Random;
 
/**
 * 點實體類<br/>
 * 
 * @author liuZhiwei
 * 2016年8月15日 下午7:16:14
 */
public class Area {
    
    public static Random rd=new Random();
    
    public Double createDouble(){
        return (double) rd.nextInt(1000);
    }
    public Area() {
        this.px = createDouble();
        this.py = createDouble();
    }
    
    public Area(Double px, Double py) {
        super();
        this.px = px;
        this.py = py;
    }
    
    public Area(Double px, Double py, String name) {
        super();
        this.px = px;
        this.py = py;
        this.name = name;
    }
    private Double px;
    private Double py;
    private String name;
    public Double getPx() {
        return px;
    }
    public void setPx(Double px) {
        this.px = px;
    }
    public Double getPy() {
        return py;
    }
    public void setPy(Double py) {
        this.py = py;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Area [px=" + px + ", py=" + py + ", name=" + name + "]";
    }
    
    public String getPoint() {
        StringBuffer buffer=new StringBuffer();
        buffer.append("(").append(px).append(",").append(py).append(")");
        return buffer.toString();
    }    
}
main方法測試類
package cn.liuzw.test;
 
import java.util.ArrayList;
import java.util.List;
 
import cn.liuzw.bean.Area;
import cn.liuzw.point.Point;
 
public class GraphicalMain {
    public static void main(String[] args) {
        //(113.944421,22.528841) (113.94629,22.529208)
        //isPointInPolygon(area.createDouble(),area.createDouble()); (114.082402,22.550271) 114.075323,22.543528)
        isPointInPolygon(113.947871,22.52804);
        
    }
    private static Boolean isPointInPolygon( double px , double py ){
        Area a1=new Area(113.941853,22.530777);
        Area a3=new Area(113.94788,22.527597);
        Area a2=new Area(113.940487,22.527789);
        Area a4=new Area(113.947925,22.530618);
        Area a5=new Area(113.941772,22.530727);
        List<Area> areas=new ArrayList<Area>();
        areas.add(a1);
        areas.add(a2);
        areas.add(a3);
        areas.add(a4);
        ArrayList<Double> polygonXA = new ArrayList<Double>();  
        ArrayList<Double> polygonYA = new ArrayList<Double>(); 
        for(int i=0;i<areas.size();i++){
            Area area=areas.get(i);
            polygonXA.add(area.getPx());
            polygonYA.add(area.getPy());
        }
        Point point=new Point();
        Boolean flag= point.isPointInPolygon(px, py, polygonXA, polygonYA);
        StringBuffer buffer=new StringBuffer();
        buffer.append("目標點").append("(").append(px).append(",").append(py).append(")").append("\n");
        buffer.append(flag?"在":"不在").append("\t").append("由\n");
        for(int i=0;i<areas.size();i++){
            Area area=areas.get(i);
            buffer.append(area.getPoint()).append("; ");
            //buffer.append("第"+i+"個點"+area.getPoint()).append("\n");
            System.out.println("第"+(i+1)+"個點"+area.getPoint());
        }
        StringBuffer sb=new StringBuffer();
        sb.append("目標點:").append("(").append(px).append(",").append(py).append(")").append("\n");
        System.out.println(sb);
        buffer.append(areas.size()).append("個點組成的").append(areas.size()).append("邊行內");
        System.out.println(buffer.toString());
        return  flag;
    }
}