matplotlib artist中的多边形包含测试 [英] Polygon containment test in matplotlib artist

查看:146
本文介绍了matplotlib artist中的多边形包含测试的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下代码,最初是从此处收集的. matplotlib匀称地绘制出世界地图.

I have the following code, gathered initially from here., which uses matplotlib, shapely, cartopy to draw a world map.

单击时,我需要确定是在哪个国家/地区进行的.我可以在画布上添加一个pick_event回调,但是,每个艺术家都调用它.(cartopy.mpl.feature_artist.FeatureArtist,对应于一个国家).

When a click is made, I need to determine on which country it was made. I am able to add a pick_event callback to the canvas, however, it is called on every artist.(cartopy.mpl.feature_artist.FeatureArtist, which corresponds to a country).

给出一个艺术家和一个带有x,y坐标的鼠标事件,我如何确定收容措施?

Given an artist and a mouse event with x, y coordinates, how can I determine containment?

我已经尝试过artist.get_clip_box().contains,但它实际上不是多边形,而是纯矩形.

I've tried artist.get_clip_box().contains, but it is not really a polygon, rather a plain rectangle.

FeatureArist的默认遏制测试是None,因此我必须添加自己的遏制测试.

The default containment test for the FeatureArists is None, so I had to add my own containment test.

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

How can I correctly check for the containment of the mouse event point, inside the 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()

推荐答案

好问题.可悲的是,看起来FeatureArtist并不是PathCollection的子类,从技术上来说应该是,但是它只是继承自Artist.这意味着,正如您已经发现的那样,围堵测试尚未在艺术家上进行定义,实际上,在当前状态下变通并不是特别容易.

Good question. Sadly, it looks like the FeatureArtist isn't a subclass of PathCollection, as it technically should be, but it simply inherits from Artist. This means that, as you've already spotted, the containment test isn't defined on the artist, and in truth, it isn't particularly easy to work around in its current state.

也就是说,我可能不会使用matplotlib包含功能来解决这个问题.鉴于我们具有Shapely几何形状,并且遏制就是这种工具的基础,所以我会跟踪创建艺术家的形状几何形状,并进行审问.然后,我将简单地使用函数遵循以下方式加入matplotlib的通用事件处理:

That said, I probably wouldn't have approached this using the matplotlib containment functionality; given that we have Shapely geometries, and that containment is the bread and butter of such a tool, I'd keep track of the shapely geometry that went into creating the artist, and interrogate that. I'd then simply hook into matplotlib's generic event handling with a function along the lines of:

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并检查每个国家/地区几何形状的包含性的简单情况.

The difficulty in this function was getting hold of the x and y coordinates in terms of latitudes and longitudes, but after that, it is a simple case of creating a shapely Point and checking containment on each of the countries' geometries.

完整代码如下:

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

如果遏制测试的数量增加到比此shape文件中的数量更多,我还将查看每个国家/地区的几何形状,以显着提高性能.

If the number of containment tests increase much more than that within this shape file, I'd also be looking at "preparing" each country's geometry, for a pretty major performance boost.

HTH

这篇关于matplotlib artist中的多边形包含测试的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆