计算船只到岸或海岸线的距离

时间:2017-08-01 14:31:44

标签: python geometry gis

对于200M GPS(lon,lat)坐标的数据集,我想计算到最近的陆地或海岸线的近似距离,作为一个名为distance_to_shore的函数,它将返回该岸的距离和国家。

我使用了国家/地区边界和海岸线的形状文件:http://www.naturalearthdata.com/

有些考虑因素是海洋难以进入的极点是2688公里。所以这将是离岸最大可能的距离,这可以用来创建某种边界框。我想计算地球曲率(不是欧几里得)的计算方法,例如: Haversine,或Vincenty方法。

为此,我开始查看scipy.spatial.cKDTree,但这不允许使用Haversine距离度量标准。另一方面,sklearn.neighbors.BallTree确实允许Haversine距离度量,但我不能让它工作。这是我到目前为止的代码。注:理想情况下,该函数应该被矢量化。

############################### ###############################

感谢所有输入,这是我在Python中解决它的方式,包括下载相关形状文件的功能,需要一些清理

import os
import numpy as np
import pandas as pd

import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap

import shapely as sp
import cartopy.io.shapereader as shpreader
import ssl
import urllib.request
import zipfile

from shutil import rmtree
from dbfread import DBF
from scipy import spatial
from sklearn.neighbors import NearestNeighbors, BallTree
from pyproj import Proj, transform

from math import *

coastline = np.load(os.path.join(os.path.dirname(__file__),
                    '../data/shape_files/coast_coords_10m.npy'))

ports = np.load(os.path.join(os.path.dirname(__file__),
                '../data/shape_files/ports_coords.npy'))

def extract_geom_meta(country):
    '''
    extract from each geometry the name of the country
    and the geom_point data. The output will be a list
    of tuples and the country name as the last element.
    '''
    geoms = country.geometry
    coords = np.empty(shape=[0, 2])
    for geom in geoms:
        coords = np.append(coords, geom.exterior.coords, axis = 0)

    country_name = country.attributes["ADMIN"]
    return [coords, country_name]

def save_coastline_shape_file():
    '''
    store shp files locally, this functions will download
    shapefiles for the whole planet.
    '''
    ne_earth = shpreader.natural_earth(resolution = '10m',
                                       category = 'cultural',
                                       name='admin_0_countries')
    reader = shpreader.Reader(ne_earth)
    countries = reader.records()
    # extract and create separate objects
    world_geoms = [extract_geom_meta(country) for country in countries]
    coords_countries = np.vstack([[np.array(x[:-1]), x[-1]]
                                    for x in world_geoms])
    coastline = np.save(os.path.join(os.path.dirname(__file__),
                        '../data/shape_files/coast_coords_10m.npy')
                        , coords_countries)
    print('Saving coordinates (...)')

def distance_to_shore(lon, lat):
    '''
    This function will create a numpy array of distances
    to shore. It will contain and ID for AIS points and
    the distance to the nearest coastline point.
    '''
    coastline_coords = np.vstack([np.flip(x[0][0], axis=1) for x in coastline])
    countries = np.hstack([np.repeat(str(x[1]), len(x[0][0])) for x in coastline])
    tree = BallTree(np.radians(coastline_coords), metric='haversine')
    coords = pd.concat([np.radians(lat), np.radians(lon)], axis=1)
    dist, ind = tree.query(coords, k=1)
    df_distance_to_shore = pd.Series(dist.flatten()*6371, name='distance_to_shore')
    df_countries = pd.Series(countries[ind].flatten(), name='shore_country')
    return pd.concat([df_distance_to_shore, df_countries], axis=1)

3 个答案:

答案 0 :(得分:4)

解决这个问题的有效方法是存储你所有的海岸线 使用测地距离指向vantage point tree 您的指标(衡量标准满足要求的重要性) triangle inequality)。然后,对于每个船只,您可以查询VP 树找到了封闭点。

如果有 M 海岸点和 N 船只。然后到了 构造VP树需要 M log M 距离计算。每 查询需要log M 距离计算。距离计算 对于椭圆体大约需要2.5μs。所以总时间是 ( M + N )log M ×2.5μs。

以下是使用我的库GeographicLib(版本1.47或更高版本)的代码 进行这个计算。这只是一个精简版 为NearestNeighbor class提供的示例。

// Example of using the GeographicLib::NearestNeighbor class.  Read lon/lat
// points for coast from coast.txt and lon/lat for vessels from vessels.txt.
// For each vessel, print to standard output: the index for the closest point
// on coast and the distance to it.

// This requires GeographicLib version 1.47 or later.

// Compile/link with, e.g.,
// g++ -I/usr/local/include -lGeographic -L/usr/local/bin -Wl,-rpath=/usr/local/lib -o coast coast.cpp

// Run time for 30000 coast points and 46217 vessels is 3 secs.

#include <iostream>
#include <exception>
#include <vector>
#include <fstream>

#include <GeographicLib/NearestNeighbor.hpp>
#include <GeographicLib/Geodesic.hpp>

using namespace std;
using namespace GeographicLib;

// A structure to hold a geographic coordinate.
struct pos {
  double _lat, _lon;
  pos(double lat = 0, double lon = 0) : _lat(lat), _lon(lon) {}
};

// A class to compute the distance between 2 positions.
class DistanceCalculator {
private:
  Geodesic _geod;
public:
  explicit DistanceCalculator(const Geodesic& geod) : _geod(geod) {}
  double operator() (const pos& a, const pos& b) const {
    double d;
    _geod.Inverse(a._lat, a._lon, b._lat, b._lon, d);
    if ( !(d >= 0) )
      // Catch illegal positions which result in d = NaN
      throw GeographicErr("distance doesn't satisfy d >= 0");
    return d;
  }
};

int main() {
  try {
    // Read in coast
    vector<pos> coast;
    double lat, lon;
    {
      ifstream is("coast.txt");
      if (!is.good())
        throw GeographicErr("coast.txt not readable");
      while (is >> lon >> lat)
        coast.push_back(pos(lat, lon));
      if (coast.size() == 0)
        throw GeographicErr("need at least one location");
    }

    // Define a distance function object
    DistanceCalculator distance(Geodesic::WGS84());

    // Create NearestNeighbor object
    NearestNeighbor<double, pos, DistanceCalculator>
      coastset(coast, distance);

    ifstream is("vessels.txt");
    double d;
    int count = 0;
    vector<int> k;
    while (is >> lon >> lat) {
      ++count;
      d = coastset.Search(coast, distance, pos(lat, lon), k);
      if (k.size() != 1)
          throw GeographicErr("unexpected number of results");
      cout << k[0] << " " << d << "\n";
    }
  }
  catch (const exception& e) {
    cerr << "Caught exception: " << e.what() << "\n";
    return 1;
  }
}

这个例子是用C ++编写的。要使用python,你需要找到一个python 执行VP树然后你可以使用 python version of GeographicLib用于距离计算。

P.S。 GeographicLib使用精确算法测量测地距离 满足三角不等式。 Vincenty方法失败了 收敛近似对映点,也满足三角形 不等式。

ADDENDUM :这是python实现: 安装vptree和geographiclib

pip install vptree geographiclib

海岸点(lon,lat)位于coast.txt;船位(lon,lat)是 在vessels.txt。运行

import numpy
import vptree
from geographiclib.geodesic import Geodesic

def geoddist(p1, p2):
  # p1 = [lon1, lat1] in degrees
  # p2 = [lon2, lat2] in degrees
  return Geodesic.WGS84.Inverse(p1[1], p1[0], p2[1], p2[0])['s12']

coast = vptree.VPTree(numpy.loadtxt('coast.txt'), geoddist, 8)
print('vessel closest-coast dist')
for v in numpy.loadtxt('vessels.txt'):
  c = coast.get_nearest_neighbor(v)
  print(list(v), list(c[1]), c[0])

对于30000个海岸点和46217个船只,这需要18分3秒。 这比我预期的要长。构建树的时间是 1分16秒所以的总时间应该大约3分钟。

对于30000个海岸点和46217个船只,这需要4分钟(使用 vptree的1.1.1版)。 为了比较,使用GeographicLib C ++库的时间是3 秒。

LATER :我调查了为什么python vptree很慢。的数量 设置树的距离计算与GeographicLib相同 C ++实现和python vptree包:387248,大概是 M log M M = 30000.(这里的日志是基数2,我设置了桶 对于两种实现,大小为1以便于比较。)平均值 每个容器查找C ++的距离计算次数 实现是14.7,接近预期值,log M = 14.9。但是,python实现的等效统计信息是 108.9,7.4的较大因子。

各种因素影响VP树的效率:选择 有利位置,如何搜索搜索等。讨论这些 GeographicLib实现的注意事项是here。 我将ping这个python包的作者。

以后:我已提交pull request治愈专业 python包vptree的效率问题。 CPU时间 我的测试现在大约4分钟。每个查询的距离计算次数是 16.7(接近GeographicLib :: NearestNeighbor的数字,14.7)。

答案 1 :(得分:0)

这里的关键是你需要使用“大圆”(orthodromic)距离计算,它旨在找到球体表面上两点之间的距离。虽然地球不是一个完美的球体,但这样的计算会让你非常接近(在0.5%以内),如果距离不够近,可以应用非球形调整。

互联网上有很多关于这个公式的文件。您将需要寻找包含X-Y-Z而非极坐标的封闭式解决方案,或将您的GPS坐标转换为极坐标,两者之一。

答案 2 :(得分:0)

您需要一个Great Circle距离计算公式。这些有时被称为球形余弦定律 Haversine, Vincenty ,公式。

然后,您可以计算每个船只到海岸线语料库中最近点的距离。在运行整个Great Circle公式之前,使用边界框计算来排除不相关的点通常很有帮助。

构建海岸线语料库时,如果原始海岸线数据中包含长段,则可能需要使用插值来添加额外的海岸线点。这是因为您计算距离最近的的距离不是最近的。查找Great Circle插值。

如果你的船只靠近任一极(井,靠近北极,看看南极是如何在陆地上),那么标准的Great Circle公式和边界矩形就会变得很时髦。在这种情况下,你可能应该使用Vincenty公式。

这是一篇关于使用带有索引的DBMS进行此类目的的文章。 https://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/

如果您需要NOAA图表级精度,您可能需要了解Universal Transverse Mercator投影。这超出了Stack Overflow答案的范围。

相关问题