Plotar shapefile com matplotlib

13

Eu estou tentando ler um shapefile e plotá-lo usando matplotlib. Aqui está o código:

import matplotlib.pyplot as plt
import shapefile   

shpFilePath = "D:\test.shp"  
listx=[]
listy=[]
test = shapefile.Reader(shpFilePath)
for sr in test.shapeRecords():
    for xNew,yNew in sr.shape.points:
        listx.append(xNew)
        listy.append(yNew)
plt.plot(listx,listy)
plt.show()

No entanto, recebo linhas conectando meus polígonos. Como posso desenhar os polígonos de forma que eles sejam o caminho no arquivo de forma. Aqui estão as capturas de tela do gráfico e do shapefile quando ele é aberto com o ArcGIS.Gerado por código Arquivo real

statBeginner
fonte
Não estou familiarizado com o leitor de shapefile, no entanto, posso dizer que você está apenas anexando todos os pontos do arquivo a uma grande lista sem separar cada forma em suas partes componentes. Você precisa de uma grande lista de formas para que você acrescentar cada pontos formas
Certo. Tem que encontrar uma maneira de separar as formas. Mas é isso que eu sou incapaz de fazer no momento.
statBeginner
@DanPatterson Você pode especificar como plotar várias formas na mesma figura depois que eu conseguir separar as formas? Se eu usar plt.plot (listx, listy) para todas as formas, ele continuará gerando uma nova figura sempre, em vez de usar a mesma figura.
statBeginner

Respostas:

10

Vou deixar para você como coletar as formas, mas este é o princípio

import numpy as np
from matplotlib import pyplot as p  #contains both numpy and pyplot
x1 = [-1,-1,10,10,-1]; y1 = [-1,10,10,-1,-1]
x2 = [21,21,29,29,21]; y2 = [21,29,29,21,21]
shapes = [[x1,y1],[x2,y2]]
for shape in shapes:
  x,y = shape
  p.plot(x,y)
p.show()

fonte
oh .. pergunto como eu perdi isso. Eu recebo as formas impressas em cores diferentes. Terá que corrigir isso :)
statBeginner
como obter ou isolar as diferentes formas?
precisa saber é
15

Para referências futuras, eis a solução que encontrei depois de seguir os conselhos acima.

import shapefile as shp  # Requires the pyshp package
import matplotlib.pyplot as plt

sf = shp.Reader("test.shp")

plt.figure()
for shape in sf.shapeRecords():
    x = [i[0] for i in shape.shape.points[:]]
    y = [i[1] for i in shape.shape.points[:]]
    plt.plot(x,y)
plt.show()

A figura resultante será muito colorida, mas você precisará ajustar as palavras-chave da plotagem.

ldocao
fonte
6
Eu sei que essas informações podem ser redundantes, mas para aqueles que ainda não estão familiarizados com o assunto, seria útil dizer que import shapefilese refere ao pyshppacote: pypi.python.org/pypi/pyshp
FaCoffee
Isso não é bom quando você tem um monte de ilhas, pois esses pontos serão conectados por linhas a pontos no continente, criando algo semelhante ao que o OP postou.
FaCoffee 28/09
1
@FaCoffee, você está certo. Minha resposta gis.stackexchange.com/a/309780/126618 deve resolver isso.
Gus
7

Você precisa usar caminhos e patches matplotlib e existe um módulo Python dedicado a plotar polígonos de shapefiles usando essas funções Descartes .

Como o Pyshp (shapefile) possui a convenção geo_interface ( Nova geo_interface para PyShp ), você pode usá-lo.

polys  = shapefile.Reader("polygon")
# first polygon
poly = polys.iterShapes().next().__geo_interface__
print poly
{'type': 'Polygon', 'coordinates': (((151116.87238259654, 135890.8706318218), (153492.19971554304, 134793.3055883224), (153934.50204650551, 133892.31935858406), (152623.97662143156, 131811.86024627919), (150903.91200102202, 130894.49244872745), (149347.66305874675, 132991.33312884573), (149151.08424498566, 134383.76639298678), (151116.87238259654, 135890.8706318218)),)}

O resultado é a representação GeoJSON da geometria e você pode usar a solução de Como plotar dados geográficos usando matplotlib / python

import matplotlib.pyplot as plt 
from descartes import PolygonPatch
BLUE = '#6699cc'
fig = plt.figure() 
ax = fig.gca() 
ax.add_patch(PolygonPatch(poly, fc=BLUE, ec=BLUE, alpha=0.5, zorder=2 ))
ax.axis('scaled')
plt.show()

insira a descrição da imagem aqui

gene
fonte
Isso é realmente útil, mas você pode fazer isso em um loop for se tiver vários polígonos para plotar?
FaCoffee #
Sim sem problema
gene
Notei que a descartessolução não funcionará se você tentar plotar dois shapefiles diferentes em duas subparcelas adjacentes usando fig, ax = plt.subplots(1,2,figsize=(15, 8))e então ax[0].add_patch(PolygonPatch(poly_geo, fc='#d3d3d3', ec='#000000', alpha=0, zorder=5))e ax[1].add_patch(PolygonPatch(poly_geo, fc='#d3d3d3', ec='#000000', alpha=0, zorder=5)). O resultado é uma imagem vazia. Qualquer ideia?
FaCoffee
2

Isso pode ser feito usando geopandas ou pyshp, conforme discutido nesta resposta . Geopandas usam matplotlib em seu back-end para plotagem.

SENHORA_
fonte
2

Além de responder a ldocao e responder à pergunta do FaCoffee. Quando você tiver ilhas isoladas e elas fizerem parte do mesmo recurso, poderá tentar a seguir:

import shapefile as shp
import matplotlib.pyplot as plt

sf = shp.Reader("test.shp")

plt.figure()
for shape in sf.shapeRecords():
    for i in range(len(shape.shape.parts)):
        i_start = shape.shape.parts[i]
        if i==len(shape.shape.parts)-1:
            i_end = len(shape.shape.points)
        else:
            i_end = shape.shape.parts[i+1]
        x = [i[0] for i in shape.shape.points[i_start:i_end]]
        y = [i[1] for i in shape.shape.points[i_start:i_end]]
        plt.plot(x,y)
plt.show()

Isso faz funcionar para mim. A propriedade "partes" de uma forma retorna os índices iniciais de diferentes geometrias dentro de um recurso.

felipesaam
fonte
0

Ainda assim, em um formato de arquivo de forma, pode haver várias partes. Isso plotará cada parte em uma forma, separadamente.

import matplotlib.pyplot as plt
import shapefile
import numpy as np

this_shapefile = shapefile.Reader(map_file_base) # whichever file
shape = this_shapefile.shape(i) # whichever shape
points = np.array(shape.points)

intervals = list(shape.parts) + [len(shape.points)]

ax = plt.gca()
ax.set_aspect(1)

for (i, j) in zip(intervals[:-1], intervals[1:]):
    ax.plot(*zip(*points[i:j]))
Gus
fonte