Python >> Tutoriel Python >  >> Python Tag >> Matplotlib

Conversion d'objets de contour Matplotlib en objets Shapely

Si j'utilise votre premier exemple matplotlib - extraction de données à partir de courbes de niveau

import matplotlib.pyplot as plt
x = [1,2,3,4]
y = [1,2,3,4]
m = [[15,14,13,12],[14,12,10,8],[13,10,7,4],[12,8,4,0]]
cs = plt.contour(x,y,m)

Le résultat est :

Le nombre d'éléments (lignes) est donné par :

len(cs.collection)
7

et le résultat que vous voulez est l'aire d'un des polygones (avec contourf() :7 polygones)

En fait, la liste xy déterminée par :

p = cs.collections[0].get_paths()[0]
v = p.vertices
x = v[:,0]
y = v[:,1]

sont les coordonnées des anneaux linéaires extérieurs des polygones colorés. Alors

from shapely.geometry import polygon
for i in range(len(cs.collections)):
    p = cs.collections[i].get_paths()[0]
    v = p.vertices
    x = v[:,0]
    y = v[:,1]
    poly = Polygon([(i[0], i[1]) for i in zip(x,y)])
    print i, poly
    0 POLYGON ((4 3.5, 4 4, 3.5 4, 4 3.5))
    1 POLYGON ((4 3, 4 3, 4 3.5, 3.5 4, 3 4, 3 4, 3 4, 4 3, 4 3))
    2 POLYGON ((4 2.5, 4 3, 4 3, 3 4, 3 4, 2.5 4, 3 3.333333333333333, 3.333333333333333 3, 4 2.5))
    3 POLYGON ((4 2, 4 2, 4 2.5, 3.333333333333333 3, 3 3.333333333333333, 2.5 4, 2 4, 2 4, 2 4, 2.666666666666667 3, 3 2.666666666666667, 4 2, 4 2))
    4 POLYGON ((3 2, 4 1.5, 4 2, 4 2, 3 2.666666666666667, 2.666666666666667 3, 2 4, 2 4, 1.5 4, 2 3, 2 3, 3 2, 3 2))
    5 POLYGON ((4 1, 4 1, 4 1.5, 3 2, 3 2, 2 3, 2 3, 1.5 4, 1 4, 1 4, 1.333333333333333 3, 2 2, 2 2, 3 1.333333333333333, 4 1))
    6 POLYGON ((2 1, 2 1, 3 1, 4 1, 3 1.333333333333333, 2 2, 2 2, 1.333333333333333 3, 1 4, 1 3, 1 2, 1 2, 2 1))
    7 POLYGON ((1 1, 2 1, 1 2, 1 1))

Tracé du Polygone 4

et le résultat est donné par poly.area

Mais il existe d'autres solutions comme dans matplotlib - utilisateurs :pyplot :Extraire le contourset sans tracer ni stackoverflow :Python :trouver les courbes de niveau de matplotlib.pyplot.contour() avec le module non documenté matplotlib._cntr sans rien tracer.


Problème avec la réponse acceptée :

Pour compléter la réponse acceptée, il convient de noter que la méthode échouera si l'une de ces réponses est vraie :

  1. Il existe plusieurs polygones pour un niveau donné
  2. Il y a des "trous" dans le polygone (dans ce cas, la réponse acceptée fonctionnerait mais créerait un polygone invalide qui peut être problématique sur toute la ligne)

Code :

Le code suivant résoudrait les deux problèmes à la fois :

from shapely import geometry
for col in cs.collections:
    # Loop through all polygons that have the same intensity level
    for contour_path in col.get_paths(): 
        # Create the polygon for this intensity level
        # The first polygon in the path is the main one, the following ones are "holes"
        for ncp,cp in enumerate(contour_path.to_polygons()):
            x = cp[:,0]
            y = cp[:,1]
            new_shape = geometry.Polygon([(i[0], i[1]) for i in zip(x,y)])
            if ncp == 0:
                poly = new_shape
            else:
                # Remove the holes if there are any
                poly = poly.difference(new_shape)
                # Can also be left out if you want to include all rings

        # do something with polygon
        print poly 

Explications :

  1. Si plusieurs polygones existent avec le même niveau d'intensité,.get_paths() contiendra plus d'un élément. Par conséquent, boucler sur .get_paths() permet de ne manquer aucun polygone.
  2. S'il y a des trous, le vertices La propriété renvoie tous les points des polygones, qu'ils soient à l'extérieur ou à l'intérieur. Par conséquent, il faut créer un polygone avec l'extérieur et supprimer tous les polygones à l'intérieur. Utilisation de .to_polygons() permet d'obtenir tous les polygones (extérieur et intérieur), le premier étant celui extérieur. Avec le difference fonction, vous pouvez supprimer tous les trous.