PHP判斷地理圍欄是否有重合

/* 判斷地理圍欄是否有重合
* $points 二維數組內含經緯度座標
* $arr 二維數組,元素內爲json格式的二維數組
* return bool true爲有重合  false爲沒有重合
**/
    function in_geo( array $points , array $arr ){
        if( empty( $points ) || !is_array( $points ) ){
            return false;
        }

        if( empty( $arr ) || !is_array( $arr ) ){
            return false;
        }

        # 處理新數據
        $new_points = [];
        foreach ( $points as $k => $v ){
            $new_points[] = [$v['latitude'],$v['longitude']];
        }

        # 獲取新數據的中心點
        $center = $this->GetCenterFromDegrees( $new_points );

        # 循環和已存在的區域中心點比對
        foreach ( $arr as $key => $val ){
            # 轉爲數組格式
            $val = json_decode( $val['points'] , true );

            # 獲取中心點座標
            $val = $this->update_array( $val );

            # 計算距離
            $range = $this->getDistance( $val[0] ,$val[1] , $center[0] , $center[1] );

            $arr[$key]['range'] = $range;
        }

        # 將數組由近到遠排序
        $arr = $this->array_sort( $arr );

        # 取最近的一條數據
        $new_arr = json_decode( $arr[0]['points'] , true );

        # 判斷新數據是否被包含在一個區域中
        foreach ( $new_arr as $kk => $vv ){
            $geo[] = $vv['longitude'] . ',' . $vv['latitude'];
        }

        for ( $p = 0; $p <= count( $points ) -1; $p++ ){
            $fences = $points[$p]['longitude'] . ',' . $points[$p]['latitude'];
            if( $this->in_fences( $geo , $fences ) ){
                return 1;
            }
        }

        # 獲取多邊形線段數組
        $arr_line = $this->changeLine( $new_arr );

        # 獲取新數據線段數組
        $points_line = $this->changeLine( $points );

        $num = 0;
        for ( $i = 0; $i <= count( $points_line ) -1; $i++ ){
            for ( $j = $i; $j <= count( $points_line ) -1; $j++){
                # 判斷是否有線段相交
                $line = $this -> isSegmentsIntersectant( $points_line[$i] , $arr_line[$j] );

                if( $line ){
                    $num++;
                }
            }
        }
        return $num;
    }

以下爲算法中運用到的其他算法

	[GetCenterFromDegrees方法](https://blog.csdn.net/weixin_44309184/article/details/97647381)

/*
 * 處理數組刪除不必要的元素並獲取中心點座標
 * $data 二維數組
 * return 數組中必要的元素和中心點座標
 * **/
    function update_array( array $data ){
        if( !is_array( $data ) ) return false;

        if( count( $data ) <= 1 ){
            foreach ( $data as $key => $val ){
                return [ $val['latitude'] , $val['longitude'] ];
            }
        }else {
            foreach ($data as $k => $v) {
                if ( array_key_exists('name', $v ) ) {
                    unset($data[$k]['address']);
                    unset($data[$k]['name']);
                } else {
                    unset($data[$k]['address']);
                }

                $data[$k][0] = $data[$k]['latitude'];
                $data[$k][1] = $data[$k]['longitude'];
                unset($data[$k]['latitude']);
                unset($data[$k]['longitude']);
            }
        }
        return $this->GetCenterFromDegrees( $data );
    }
    
    [getDistance方法](https://blog.csdn.net/weixin_44309184/article/details/97647717)

	/*
     * 對數組根據距離排序
     * **/
    function array_sort( array $data = [] ){
        # 判斷是否爲空
        if( empty( $data ) ){
            return [];
        }

        # 判斷是否爲數組
        if( !is_array( $data ) ){
            return [];
        }

        for ( $i = 0; $i < count( $data ); $i++ ){
            $arr = '';
            for ( $j = $i; $j < count( $data ) -1; $j++ ){
                if( $data[$i]['range'] > $data[$j+1]['range'] ){
                    $arr = $data[$i];

                    # 互換位置
                    $data[$i] = $data[$j+1];
                    $data[$j+1] = $arr;
                }
            }

            if ($data[$i]['range'] < 1) {
                $data[$i]['range'] = $data[$i]['range'] * 1000;
                $data[$i]['range'] = round( $data[$i]['range'] ) . 'm';
            } else {
                $data[$i]['range'] = round( $data[$i]['range'] , 2 ) . 'km';
            }

        }

        return $data;
    }

[in_fences方法](https://blog.csdn.net/weixin_44309184/article/details/97763428)


	/*
     * 將多邊形拆分爲多個線段算法
     * params $arr 需要進行拆解的二維數組
     * return array 返回三維數組
     * **/
    function changeLine( array $arr ){
        if( empty( $arr ) ){
            return [];
        }

        if( count( $arr ) == 1 ){
            return $arr;
        }

        for ( $i = 0; $i < count( $arr ) -1; $i++ ){
            for ( $j = $i +1; $j <= count( $arr ) -1; $j++ ){
                $data[] = [ [ 'x' => $arr[$i]['latitude'] , 'y' => $arr[$i]['longitude'] , 'name' => $arr[$i]['name'] ] , ['x' => $arr[$j]['latitude'] , 'y' => $arr[$j]['longitude'] , 'name' => $arr[$j]['name'] ] ];
            }
        }
        return $data;
    }

	//判斷兩多邊形線段是否相交
    function isSegmentsIntersectant($segA, $segB) {//線線
        $abc = ($segA[0]['x'] - $segB[0]['x']) * ($segA[1]['y'] - $segB[0]['y']) - ($segA[0]['y'] - $segB[0]['y']) * ($segA[1]['x'] - $segB[0]['x']);
        $abd = ($segA[0]['x'] - $segB[1]['x']) * ($segA[1]['y'] - $segB[1]['y']) - ($segA[0]['y'] - $segB[1]['y']) * ($segA[1]['x'] - $segB[1]['x']);
        if ($abc * $abd >= 0) {
            return false;
        }
        $cda = ($segB[0]['x'] - $segA[0]['x']) * ($segB[1]['y'] - $segA[0]['y']) - ($segB[0]['y'] - $segA[0]['y']) * ($segB[1]['x'] - $segA[0]['x']);
        $cdb = $cda + $abc - $abd;
        return !($cda * $cdb >= 0);
    }

該算法並非無問題,如有能人異士可改善,歡迎評論

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章