2013-04-12 10 views
6

Ich versuche gefüllte Polygone von Ländern auf der Weltkarte mit Matplotlib in Python zu plotten.Shapefile und Matplotlib: Plot Polygon Sammlung von Shapefile Koordinaten

Ich habe ein Shapefile mit Ländergrenzen Koordinaten jedes Landes. Nun möchte ich diese Koordinaten (für jedes Land) in ein Polygon mit Matplotlib umwandeln. Ohne Grundkarte zu verwenden. Leider kreuzen sich die Teile oder überschneiden sich. Gibt es einen Workarund, vielleicht die Entfernung von Punkt zu Punkt ... oder Neuordnung? enter image description here

+0

Wie hast du dieses Bild gemacht? Ich benutze immer Pfade dafür, wie in diesem Beispiel: http://matplotlib.org/examples/api/donut_demo.html –

Antwort

11

Ha! Ich habe herausgefunden, wie .. ich völlig vernachlässigt habe, die sf.shapes [i] .parts Informationen! Dann kommt es darauf an:

# -- import -- 
import shapefile 
import matplotlib.pyplot as plt 
import matplotlib.patches as patches 
from matplotlib.patches import Polygon 
from matplotlib.collections import PatchCollection 
# -- input -- 
sf = shapefile.Reader("./shapefiles/world_countries_boundary_file_world_2002") 
recs = sf.records() 
shapes = sf.shapes() 
Nshp = len(shapes) 
cns  = [] 
for nshp in xrange(Nshp): 
    cns.append(recs[nshp][1]) 
cns = array(cns) 
cm = get_cmap('Dark2') 
cccol = cm(1.*arange(Nshp)/Nshp) 
# -- plot -- 
fig  = plt.figure() 
ax  = fig.add_subplot(111) 
for nshp in xrange(Nshp): 
    ptchs = [] 
    pts  = array(shapes[nshp].points) 
    prt  = shapes[nshp].parts 
    par  = list(prt) + [pts.shape[0]] 
    for pij in xrange(len(prt)): 
    ptchs.append(Polygon(pts[par[pij]:par[pij+1]])) 
    ax.add_collection(PatchCollection(ptchs,facecolor=cccol[nshp,:],edgecolor='k', linewidths=.1)) 
ax.set_xlim(-180,+180) 
ax.set_ylim(-90,90) 
fig.savefig('test.png') 

Dann ist es wie folgt aussehen: enter image description here

+0

Sehr schön. Wenn ich dieses Skript ausführe, sehe ich, dass es Innenringe in Polygonen ignoriert, nicht immer ein Problem, aber etwas, auf das man achten sollte. –

+0

Wo definierst du die Funktion 'get_cmap'? –

+0

Können Sie Ihre Antwort basierend auf der Antwort von @Elendil in Bezug auf 'cm = matplotlib.cm.get_cmap ('Dark2')' ' –

3

Hier ist ein weiteres Stück Code, den ich verwendet Polygon Shape-Dateien zu zeichnen. Es verwendet GDAL/OGR lesen Shape-Datei und Diagramme korrekt Form Polygone Donut:

from osgeo import ogr 
import numpy as np 
import matplotlib.path as mpath 
import matplotlib.patches as mpatches 
import matplotlib.pyplot as plt 

# Extract first layer of features from shapefile using OGR 
ds = ogr.Open('world_countries_boundary_file_world_2002.shp') 
nlay = ds.GetLayerCount() 
lyr = ds.GetLayer(0) 

# Get extent and calculate buffer size 
ext = lyr.GetExtent() 
xoff = (ext[1]-ext[0])/50 
yoff = (ext[3]-ext[2])/50 

# Prepare figure 
fig = plt.figure() 
ax = fig.add_subplot(111) 
ax.set_xlim(ext[0]-xoff,ext[1]+xoff) 
ax.set_ylim(ext[2]-yoff,ext[3]+yoff) 

paths = [] 
lyr.ResetReading() 

# Read all features in layer and store as paths 
for feat in lyr: 
    geom = feat.geometry() 
    codes = [] 
    all_x = [] 
    all_y = [] 
    for i in range(geom.GetGeometryCount()): 
     # Read ring geometry and create path 
     r = geom.GetGeometryRef(i) 
     x = [r.GetX(j) for j in range(r.GetPointCount())] 
     y = [r.GetY(j) for j in range(r.GetPointCount())] 
     # skip boundary between individual rings 
     codes += [mpath.Path.MOVETO] + \ 
        (len(x)-1)*[mpath.Path.LINETO] 
     all_x += x 
     all_y += y 
    path = mpath.Path(np.column_stack((all_x,all_y)), codes) 
    paths.append(path) 

# Add paths as patches to axes 
for path in paths: 
    patch = mpatches.PathPatch(path, \ 
      facecolor='blue', edgecolor='black') 
    ax.add_patch(patch) 

ax.set_aspect(1.0) 
plt.show() 
1
from fiona import collection 
import matplotlib.pyplot as plt 
from descartes import PolygonPatch 
from matplotlib.collections import PatchCollection 
from itertools import imap 
from matplotlib.cm import get_cmap 

cm = get_cmap('Dark2') 

figure, axes = plt.subplots(1) 

source_path = "./shapefiles/world_countries_boundary_file_world_2002" 

with collection(source_path, 'r') as source: 
    patches = imap(PolygonPatch, (record['geometry'] for record in source) 

axes.add_collection(PatchCollection (patches, cmap=cm, linewidths=0.1)) 

axes.set_xlim(-180,+180) 
axes.set_ylim(-90,90) 

plt.show() 

Hinweis dies setzt voraus, Polygone, können Multipolygone werden Griffe in ähnlicher Weise mit

map(PolygonPatch, MultiPolygon(record['geometry'])) 
1

In Bezug auf @hannesk " s Antwort, sollten Sie die folgenden Importe hinzufügen: from numpy import array und import matplotlib und ersetzen Sie die Zeile cm = get_cmap('Dark2') von cm = matplotlib.cm.get_cmap('Dark2')

(Ich bin nicht so berühmt, um einen Kommentar zu dem vermerkten Post hinzuzufügen.)

+0

Ich habe verstanden, warum Sie es getan haben, aber nicht tun sollten. –

+0

Wie Sie zu verstehen scheinen, wäre dies als Kommentar geeigneter. Um einen Autor zu kritisieren oder um Klärung zu bitten, hinterlasse einen Kommentar unter seinem Beitrag - du kannst deine eigenen Beiträge jederzeit kommentieren, und sobald du genügend [Reputation] (http://stackoverflow.com/help/whats-reputation) hast, wirst du das tun in der Lage sein [jeden Beitrag kommentieren] (http://stackoverflow.com/help/privileges/comment). – Makyen

+0

Das nächste Mal, wenn ich den Fehler in der Post, ohne etwas zu sagen, anderen Kerl wird es in der Zukunft korrigieren, wenn sie es gefunden haben! Ich wollte nur helfen, wie ich kann, aber ich kann es nicht tun, wie Stack es erwartet, indem ich einen Kommentar schreibe ... Und dank deiner Stimme werde ich nie das Recht haben, Kommentare zu schreiben :-) – Elendil