shapely и matplotlib точка-в-многоугольнике не точны с геолокацией

Я тестирую функцию «точка в многоугольнике» с помощью matplotlib и shapely.

Вот карта, содержащая многоугольник Бермудского треугольника.

Функции точек в многоугольниках на картах Google ясно показывают, что testingPoint и testingPoint2 находятся внутри многоугольника, что является правильным результатом.

если я проверяю две точки в matplotlib и shapely, только точка2 проходит тест.

In [1]: from matplotlib.path import Path

In [2]: p = Path([[25.774252, -80.190262], [18.466465, -66.118292], [32.321384, -64.75737]]) 

In [3]: p1=[27.254629577800088, -76.728515625]

In [4]: p2=[27.254629577800088, -74.928515625]

In [5]: p.contains_point(p1)
Out[5]: 0

In [6]: p.contains_point(p2)
Out[6]: 1

shapely показывает тот же результат, что и matplotlib.

In [1]: from shapely.geometry import Polygon, Point

In [2]: poly = Polygon(([25.774252, -80.190262], [18.466465, -66.118292], [32.321384, -64.75737]))

In [3]: p1=Point(27.254629577800088, -76.728515625)

In [4]: p2=Point(27.254629577800088, -74.928515625)

In [5]: poly.contains(p1)
Out[5]: False

In [6]: poly.contains(p2)
Out[6]: True

Что здесь происходит на самом деле? Алгоритм Google лучше этих двух?

Спасибо


person Chung    schedule 24.01.2014    source источник


Ответы (4)


Помните: мир не плоский! Если вам нужен ответ на проекцию Google Maps, вам нужно спроецировать географические координаты на spherical Mercator, чтобы получить другой набор координат X и Y. Pyproj может помочь с этим, просто убедитесь, что вы изменили свои координатные оси до (т.е.: X, Y или долгота, широта).

import pyproj
from shapely.geometry import Polygon, Point
from shapely.ops import transform
from functools import partial

project = partial(
    pyproj.transform,
    pyproj.Proj(init='epsg:4326'),
    pyproj.Proj('+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs'))

poly = Polygon(([-80.190262, 25.774252], [-66.118292, 18.466465], [-64.75737, 32.321384]))
p1 = Point(-76.728515625, 27.254629577800088)

# Old answer, using long/lat coordinates
poly.contains(p1)  # False
poly.distance(p1)  # 0.01085626429747994 degrees

# Translate to spherical Mercator or Google projection
poly_g = transform(project, poly)
p1_g = transform(project, p1)

poly_g.contains(p1_g)  # True
poly_g.distance(p1_g)  # 0.0 meters

Кажется, получил правильный ответ.

person Mike T    schedule 29.01.2014
comment
Большое спасибо за идею, проблема решена. Кстати, я обнаружил, что shapely намного медленнее, чем matplotlib, если запустить его, скажем, 1000 раз. - person Chung; 29.01.2014
comment
Полезно знать, мне нужно посмотреть, что делает matplotlib намного быстрее. - person Mike T; 29.01.2014
comment
Есть ли способ сделать преобразование с помощью matplotlib вместо этого? - person Mikael S.; 08.08.2014

Хотя вы уже приняли ответ, но в дополнение к ответу @MikeT я добавлю его для будущих посетителей, которые могут захотеть сделать то же самое с matplotlib и basemap. в mpl_toolkit :

from mpl_toolkits.basemap import Basemap
from matplotlib.path import Path


# Mercator Projection
# http://matplotlib.org/basemap/users/merc.html
m = Basemap(projection='merc', llcrnrlat=-80, urcrnrlat=80,
            llcrnrlon=-180, urcrnrlon=180, lat_ts=20, resolution='c')

# Poly vertices
p = [[25.774252, -80.190262], [18.466465, -66.118292], [32.321384, -64.75737]]

# Projected vertices
p_projected = [m(x[1], x[0]) for x in p]

# Create the Path
p_path = Path(p_projected)

# Test points
p1 = [27.254629577800088, -76.728515625]
p2 = [27.254629577800088, -74.928515625]

# Test point projection
p1_projected = m(p1[1], p1[0])
p2_projected = m(p2[1], p2[0])

if __name__ == '__main__':
    print(p_path.contains_point(p1_projected))  # Prints 1
    print(p_path.contains_point(p2_projected))  # Prints 1
person Sam R.    schedule 08.08.2014

Я просто сделал это, чтобы проверить, действительно ли точки находятся внутри треугольника:

from matplotlib import pylab as plt
poly = [[25.774252, -80.190262],
        [18.466465, -66.118292],
        [32.321384, -64.75737],
        [25.774252, -80.190262]]
x = [point[0] for point in poly]
y = [point[1] for point in poly]
p1 = [27.254629577800088, -76.728515625]
p2 = [27.254629577800088, -74.928515625]
plt.plot(x,y,p1[0],p1[1],'*r',p2[0],p2[1],'*b')
plt.show()

На изображении показано, что точка 1 не находится внутри треугольника

Теперь, когда вы используете Google Maps и многоугольник отображается в сферических координатах, треугольник деформируется, об этом следует помнить.

Во всяком случае, построение ваших данных с помощью kml в Google Earth также показывает точку за пределами треугольника?!

<kml>
<Document>
<Placemark><name>Point 1</name><Point>
<coordinates> -76.728515625, 27.254629577800088,0</coordinates></Point></Placemark>
<Placemark><name>Point 2</name><Point>
<coordinates>-74.928515625, 27.254629577800088,     0</coordinates></Point></Placemark>
<Placemark><name>Poly</name><Polygon>
<outerBoundaryIs><LinearRing>
<coordinates> -80.190262,25.774252 -66.118292,18.466465 -64.75737,32.321384 -80.190262,25.774252</coordinates>
</LinearRing></outerBoundaryIs>
</Polygon></Placemark>
</Document>
</kml>

Тот же внешний вид, что и на изображении matplotlib, точка 1 немного выходит за пределы треугольника при построении в евклидовых 2D-координатах. Для геометрических вычислений в геокоординатах используйте QGIS Python Console или GDAL/OGR Tools. Или вы можете использовать API карт Google, как в примере, который связан с этим страница, где рассматривается тема двухмерной и геодезической геометрии.

person Schuh    schedule 24.01.2014
comment
Спасибо за объяснение. Может ли matplotlib использовать сферические координаты для сопоставления? - person Chung; 24.01.2014
comment
Что такое «x» и «y» в вашем коде? Ваш пример не полный. - person Spacedman; 27.05.2014

Чтобы проверить, содержит ли многоугольник несколько точек, я бы использовал matplotlib contains_points, задокументированный здесь: http://matplotlib.org/api/path_api.html#matplotlib.path.Path.contains_points

Это делает один большой вызов с использованием массива numpy, поэтому он эффективен. Обратите внимание, что вы можете передать радиус, который на самом деле раздувает или смещает многоугольник, вы также можете трансформировать (проекции...) перед выполнением проверки.

person Christophe Roussy    schedule 13.07.2016