Python matplotlib中的多边形包容测试

Python matplotlib中的多边形包容测试,python,matplotlib,polygon,shapely,cartopy,Python,Matplotlib,Polygon,Shapely,Cartopy,我有以下代码,最初从中收集,它使用matplotlib、shapely、cartopy绘制世界地图 单击时,我需要确定是在哪个国家进行的。我可以向画布添加一个pick\u事件回调,但是,它会对每个艺术家调用。(cartopy.mpl.feature\u artist.FeatureArtist,对应于一个国家) 给定一个艺术家和一个具有x,y坐标的鼠标事件,如何确定包容 我尝试了artist.get\u clip\u box()。包含,但它实际上不是一个多边形,而是一个普通的矩形 Feature

我有以下代码,最初从中收集,它使用matplotlib、shapely、cartopy绘制世界地图

单击时,我需要确定是在哪个国家进行的。我可以向画布添加一个
pick\u事件
回调,但是,它会对每个艺术家调用。(cartopy.mpl.feature\u artist.FeatureArtist,对应于一个国家)

给定一个艺术家和一个具有x,y坐标的鼠标事件,如何确定包容

我尝试了
artist.get\u clip\u box()。包含
,但它实际上不是一个多边形,而是一个普通的矩形

FeatureArist
s的默认包含测试是
None
,因此我必须添加自己的包含测试

如何正确检查FeatureArtister中鼠标事件点的包含

import cartopy.crs as ccrs
import matplotlib.pyplot as plt
import cartopy.io.shapereader as shpreader
import itertools, pdb, subprocess, time, traceback
from itertools import *
import numpy as np
from pydoc import help as h

shapename = 'admin_0_countries'
countries_shp = shpreader.natural_earth(resolution='110m',
                                        category='cultural', name=shapename)

earth_colors = np.array([(199, 233, 192),
                                (161, 217, 155),
                                (116, 196, 118),
                                (65, 171, 93),
                                (35, 139, 69),
                                ]) / 255.
earth_colors = itertools.cycle(earth_colors)

ax = plt.axes(projection=ccrs.PlateCarree())


def contains_test ( artist, ev ):
    print "contain test called"
    #this containmeint test is always true, because it is a large rectangle, not a polygon
    #how to define correct containment test
    print "click contained in %s?: %s" % (artist.countryname, artist.get_clip_box().contains(ev.x, ev.y))
    return True, {}

for country in shpreader.Reader(countries_shp).records():
    # print country.attributes['name_long'], earth_colors.next()
    art = ax.add_geometries(country.geometry, ccrs.PlateCarree(),
                      facecolor=earth_colors.next(),
                      label=country.attributes['name_long'])

    art.countryname = country.attributes["name_long"] 
    art.set_picker(True)
    art.set_contains(contains_test)
    def pickit ( ev ):
        print "pickit called"
        print ev.artist.countryname



def onpick ( event ):
    print "pick event fired"

ax.figure.canvas.mpl_connect("pick_event", onpick)


def onclick(event):
    print 'button=%s, x=%s, y=%s, xdata=%s, ydata=%s'%(event.button, event.x, event.y, event.xdata, event.ydata)

ax.figure.canvas.mpl_connect('button_press_event', onclick)
plt.show()

好问题。遗憾的是,FeatureArtister看起来并不是PathCollection的子类,技术上应该是这样,但它只是从Artist继承的。这意味着,正如您已经注意到的,包容测试并没有在艺术家身上定义,事实上,在其当前状态下处理它并不特别容易

也就是说,我可能不会使用matplotlib包含功能来实现这一点;考虑到我们有匀称的几何图形,而包容是这样一个工具的基本组成部分,我会跟踪创建艺术家的匀称几何图形,并对此进行质询。然后,我将使用一个函数简单地连接到matplotlib的通用事件处理中,该函数大致如下:

def onclick(event):
    if event.inaxes and isinstance(event.inaxes, cartopy.mpl.geoaxes.GeoAxes):
        ax = event.inaxes
        target = ccrs.PlateCarree()
        lon, lat = target.transform_point(event.xdata, event.ydata,
                                          ax.projection)
        point = sgeom.Point(lon, lat)
        for country, (geom, artist) in country_to_geom_and_artist.items():
            if geom.contains(point):
                print 'Clicked on {}'.format(country)
                break
这个函数的难点在于掌握纬度和经度的x和y坐标,但在这之后,创建一个形状良好的点并检查每个国家几何体的包容度是一个简单的例子

完整代码如下所示:

import cartopy.crs as ccrs
import matplotlib.pyplot as plt
import cartopy.io.shapereader as shpreader
import cartopy.mpl.geoaxes
import itertools
import numpy as np
import shapely.geometry as sgeom


shapename = 'admin_0_countries'
countries_shp = shpreader.natural_earth(resolution='110m',
                                        category='cultural', name=shapename)

earth_colors = np.array([(199, 233, 192), (161, 217, 155),
                         (116, 196, 118), (65, 171, 93),
                         (35, 139, 69)]) / 255.
earth_colors = itertools.cycle(earth_colors)

ax = plt.axes(projection=ccrs.Robinson())

# Store a mapping of {country name: (shapely_geom, cartopy_feature)}
country_to_geom_and_artist = {}

for country in shpreader.Reader(countries_shp).records():
    artist = ax.add_geometries(country.geometry, ccrs.PlateCarree(),
                               facecolor=earth_colors.next(),
                               label=repr(country.attributes['name_long']))
    country_to_geom_and_artist[country.attributes['name_long']] = (country.geometry, artist)


def onclick(event):
    if event.inaxes and isinstance(event.inaxes, cartopy.mpl.geoaxes.GeoAxes):
        ax = event.inaxes
        target = ccrs.PlateCarree()
        lon, lat = target.transform_point(event.xdata, event.ydata,
                                          ax.projection)
        point = sgeom.Point(lon, lat)
        for country, (geom, artist) in country_to_geom_and_artist.items():
            if geom.contains(point):
                print 'Clicked on {}'.format(country)
                break

ax.figure.canvas.mpl_connect('button_press_event', onclick)
plt.show()
如果安全壳测试的数量增加得远远超过这个形状文件中的数量,我还将查看每个国家的几何结构,以获得相当大的性能提升


HTH

您可能希望将标题更改为更一般的标题,这样问题会更有用,并获得更多的意见。