matplotlib艺术家的多边形收容测试

时间:2014-04-30 23:18:11

标签: python matplotlib polygon shapely cartopy

我有以下代码,最初从here.收集,使用matplotlib,shapely,cartopy绘制世界地图。

点击后,我需要确定它所在的国家/地区。我可以在画布上添加pick_event回调,但是,每个艺术家都会调用它。(cartopy.mpl.feature_artist.FeatureArtist,对应一个国家/地区)。

鉴于艺术家和带有x,y坐标的鼠标事件,我该如何确定收容?

我试过了artist.get_clip_box().contains,但它实际上不是一个多边形,而是一个普通的矩形。

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

如何在FeatureArtist中正确检查鼠标事件点的包含?

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()

1 个答案:

答案 0 :(得分:2)

好问题。遗憾的是,看起来FeatureArtist不是PathCollection的子类,因为它在技术上应该是,但它只是继承自Artist。这意味着,正如您已经发现的那样,遏制测试没有在艺术家身上定义,事实上,在当前状态下解决它并不是特别容易。

那就是说,我可能不会使用matplotlib包含功能来解决这个问题;鉴于我们有Shapely几何形状,并且包含是这种工具的面包和黄油,我会跟踪创造艺术家的形状几何,并询问它。然后,我将简单地使用以下函数挂钩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坐标,但在此之后,这是一个简单的例子,可以创建一个形状上的Point并检查每个国家几何形状的遏制。 / p>

完整的代码看起来像是:

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()

如果收容测试的数量增加的幅度远远超过此形状文件中的数量,我还会查看"preparing"每个国家/地区的几何图形,以获得相当大的性能提升。

HTH