28 votos

Filtrar por cuadro delimitador en GeoPandas

Tengo un DataFrame de GeoPandas en EPSG:4326 y crearía un nuevo DataFrame compuesto por todas las filas que caen dentro de un determinado cuadro delimitador.

Primero obtengo el cuadro delimitador que me interesa (que en realidad es el cuadro delimitador de otro DataFrame):

print(df_sussex.total_bounds)
[ -1.57239292  50.57467674   0.14528384  51.27465152]

Luego hago un DataFrame que consiste sólo en ese cuadro delimitador:

pts = gpd.GeoDataFrame(df_sussex.total_bounds)

Y, por último, intento obtener todas las características que se cruzan con ese cuadro delimitador:

sac_sussex = gpd.overlay(pts, df_sac, how='intersection')

Pero esto me da:

AttributeError: No geometry data set yet (expected in column geometría'.

¿Qué estoy haciendo mal?

1 votos

El problema es que estás utilizando el método 'total_bounds'. Éste sólo produce una tupla con los puntos máximo y mínimo del cuadro delimitador. El método a utilizar es 'envelope'; previo a construir su respectivo GeoDataFrame .

45voto

Karl M. Davis Puntos 11

Puede utilizar la función cx en un geodataframe para seleccionar filas dentro de un cuadro delimitador. Para sus marcos de ejemplo:

xmin, ymin, xmax, ymax = df_sussex.total_bounds
sac_sussex = df_sac.cx[xmin:xmax, ymin:ymax]

En http://geopandas.org/indexing.html :

Además de los métodos estándar de pandas, GeoPandas también proporciona indexación basada en coordenadas con la función cx indexador, que corta utilizando un cuadro delimitador. Se devolverán las geometrías de GeoSeries o GeoDataFrame que intersecten el cuadro delimitador.

0 votos

Esta solución me ha funcionado. Gracias. Sin embargo, me preguntaba si hay una manera más rápida de implementar. Filtrado de OSM uso de la tierra y los lugares que caen dentro de un cuadro delimitador de la provincia.

5 votos

Tenga en cuenta que .cx hace algo ligeramente distinto que el gpd.overlay ya que selecciona las filas que intersecan el cuadro delimitador pero deja las geometrías intactas, mientras que la solución gpd.overlay sólo devolverá las partes de las geometrías en el cuadro delimitador. Dependiendo de la situación, es posible que desee uno u otro.

11voto

Yada Puntos 9489

El problema se debe a que está utilizando el método 'total_bounds'. Sólo produce una tupla con los puntos máximo y mínimo del cuadro delimitador. El método a utilizar es 'envelope'; previo a construir su respectivo 'GeoDataFrame'. Por ejemplo, leyendo mis shapefiles como GeoDataFrame :

import geopandas as gpd
pol1 = gpd.GeoDataFrame.from_file("pyqgis_data/polygon1.shp")
pol8 = gpd.GeoDataFrame.from_file("pyqgis_data/polygon8.shp")

Cuadro delimitador del edificio de pol1 y creando su respectivo GeoDataFrame :

bounding_box = pol1.envelope
df = gpd.GeoDataFrame(gpd.GeoSeries(bounding_box), columns=['geometry'])

Intersección de ambos GeoDataFrame :

intersections = gpd.overlay(df, pol8, how='intersection')

Trazado de resultados:

from matplotlib import pyplot as plt
plt.ion()
intersections.plot() 

enter image description here

Funcionó como se esperaba.

Nota de edición:

Utilizando el método 'total_bounds' (porque el método 'envelope' devuelve la caja delimitadora para cada característica de polígonos), se puede utilizar este enfoque:

from matplotlib import pyplot as plt
import geopandas as gpd
from shapely.geometry import Point, Polygon

pol1 = gpd.GeoDataFrame.from_file("pyqgis_data/polygon1.shp")
pol8 = gpd.GeoDataFrame.from_file("pyqgis_data/polygon8.shp")

bbox = pol1.total_bounds

p1 = Point(bbox[0], bbox[3])
p2 = Point(bbox[2], bbox[3])
p3 = Point(bbox[2], bbox[1])
p4 = Point(bbox[0], bbox[1])

np1 = (p1.coords.xy[0][0], p1.coords.xy[1][0])
np2 = (p2.coords.xy[0][0], p2.coords.xy[1][0])
np3 = (p3.coords.xy[0][0], p3.coords.xy[1][0])
np4 = (p4.coords.xy[0][0], p4.coords.xy[1][0])

bb_polygon = Polygon([np1, np2, np3, np4])

df2 = gpd.GeoDataFrame(gpd.GeoSeries(bb_polygon), columns=['geometry'])

intersections2 = gpd.overlay(df2, pol8, how='intersection')

plt.ion()
intersections2.plot()

y el resultado es idéntico.

1voto

Jeff S. Puntos 30

Una solución rápida para esto si la forma por la que quieres recortar es una caja:

from shapely.geometry import box

bbox = box(*df_sussex.total_bounds)
df_clipped = gpd.mask(df, mask=bbox)

Utiliza el shapely.geometry.box() función.

0 votos

gpd.mask() no es una función GeoPandas, tal vez debería decir df_clipped = gpd.clip(df, mask=bbox) ?

0voto

mishmash Puntos 141

Otra opción si desea utilizar un búfer en el cuadro delimitador.

def filter_by_overlay_and_bounding_box(from_shape, filter, buffer_dist=0):
"""
Given a input shape (from_shape), a filter gdb and a buffer dist, this function returns
geometry from 'from_shape' which lies within the bounds on the filter shape + buffer
Args:
    from_shape (): 
    filter ():
    buffer_dist ():

Returns:

"""
    import shapely
    bound = shapely.geometry.box(*filter.total_bounds).buffer(buffer_dist)
    bound = gpd.GeoDataFrame(gpd.GeoSeries(bound), columns=['geometry'])
    bound = bound.set_crs(from_shape.crs)
    return gpd.overlay(from_shape, bound, how='intersection')

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X