Convierta el centroide de entidades poligonales en puntos usando Python

9

Me gustaría convertir algunos archivos shp basados ​​en polígonos que tienen varias características de polígono en puntos para cada característica que representarían esencialmente el centro de cada característica de polígono. Sé que en el mundo de ArcGIS podría usar la herramienta Feature To Point, pero me gustaría mantener esto en una secuencia de comandos que se pueda ejecutar en PC que no tengan arcpy, así que estoy buscando una alternativa de código abierto para eso. ¿Alguien sabe de una biblioteca que podría usar para esto junto con alguna dirección sobre cómo aprovecharlo para lograr esto?

wilbev
fuente
Todavía tengo múltiples problemas con la respuesta que Gene proporcionó a continuación. Los problemas son cómo reordena los atributos de su orden original a alfabético, lo cual es un problema. En segundo lugar, el archivo de forma se corrompe posiblemente debido al archivo que estoy tratando de convertir que tiene más de 250 atributos.
wilbev
Hay una herramienta estándar llamada 'Polygon Centroids' en QGIS que hace exactamente esto: ¿requiere un script? Sería bastante fácil escribir script usando PyQGIS, creo.
Dùn Caan
Debe ser un script y trabajar en PC que no tengan QGIS en ellos.
wilbev

Respuestas:

9

Puede ejecutar un ogr2ogrcomando (por ejemplo, desde un Shell OSGeo4w). Por ejemplo, en un archivo de forma de países:

cd path/to/shapefiles
ogr2ogr -sql "SELECT ST_Centroid(geometry), * FROM countries" -dialect sqlite countries_centroid.shp countries.shp

El nuevo archivo de forma countries_centroid.shpdebe ser similar a la entrada, pero solo debe contener un punto por [Multi] Polígono.

@PEL también muestra un buen ejemplo con ST_PointOnSurface, que es fácil de sustituir en este comando.


Se puede hacer algo similar en Python, si es necesario, pero puede tomar algunas líneas de código más:

import os
from osgeo import ogr

ogr.UseExceptions()
os.chdir('path/to/shapefiles')

ds = ogr.Open('countries.shp')
ly = ds.ExecuteSQL('SELECT ST_Centroid(geometry), * FROM countries', dialect='sqlite')
drv = ogr.GetDriverByName('Esri shapefile')
ds2 = drv.CreateDataSource('countries_centroid.shp')
ds2.CopyLayer(ly, '')
ly = ds = ds2 = None  # save, close
Mike T
fuente
Creo que has propuesto una solución más simple con OGR y SQL. Creo que es más seguro agregar un parámetro a OGR con -nlt Point
PEL
Lamentablemente no puedo hacer que esto funcione. Recibo un error alegando que ST_Centroid no funciona.
wilbev
1
Necesita la opción de dialecto SQLite (como se muestra) y Spatialite integrado en GDAL, que no siempre está garantizado. OSGeo4W tiene una buena versión de GDAL que ejecutará este comando correctamente.
Mike T
Puedo hacer que tu script superior dentro del comando ogr2ogr funcione sin problemas. Sin embargo, necesito hacer esto en un script de Python autónomo, así que estoy tratando de hacer que su segundo conjunto de código funcione, que es donde continúo con el ST_Centroid no es un error de función. Mi código es idéntico al que tiene arriba, incluido el dialecto sqlite.
wilbev
1
El error que describe es cuando GDAL se creó sin compatibilidad con Spatialite. Algunos paquetes de gdal-python admiten esto, pero no todos. Intente abrir un shell OSGeo4W y ejecutar el script Python desde ese entorno. Creo que el conjunto predeterminado de paquetes relacionados para gdal-binincluir este soporte.
Mike T
9

Simplemente use Fiona o GeoPandas (Python 2.7.xy 3.x)

Algunos polígonos

ingrese la descripción de la imagen aquí

import geopandas as gpd
# GeoDataFrame creation
poly = gpd.read_file("geoch_poly.shp")
poly.head()

ingrese la descripción de la imagen aquí

Transformación a puntos (centroides)

# copy poly to new GeoDataFrame
points = poly.copy()
# change the geometry
points.geometry = points['geometry'].centroid
# same crs
points.crs =poly.crs
points.head()

ingrese la descripción de la imagen aquí

# save the shapefile
points.to_file('geoch_centroid.shp')

Resultado

ingrese la descripción de la imagen aquí

gene
fuente
Gracias por el gen de respuesta. Creo que puede tener un error tipográfico arriba donde la variable 'gdf' debería ser 'poli ", ¿verdad? En el código points.crs = gdf.crs. También estoy experimentando un par de otros problemas en los que el archivo .prj no está obteniendo creado, se muestra vacío y el orden de los campos de atributo está cambiando su orden de los datos del polígono ya que ahora parecen ser alfabéticos. Es importante que permanezcan en el mismo orden. Usted sabe de una manera de mantener el atributo de campo en el mismo orden?
wilbev
Gracias, corregido. Para el orden de los campos de atributos, simplemente cambie el orden del GeoPandas GeoDataFrame (= Pandas DataFrame)
gene
Gracias Gene, pero no estoy seguro de entender dónde cambiaría ese orden en el código aquí. También me encuentro con otros dos problemas con esto. Primero, el archivo * .prj está en blanco en el nuevo archivo shp. En segundo lugar, cuando intento abrir el archivo shp en el lector shp, aparece un error al abrir el archivo como si estuviera dañado. Parece que funciona sin corromperse si el archivo shp solo tiene una característica única, pero parece que tiene problemas.
wilbev
Lo siento, pero necesitas conocer a Pandas para eso y no tengo ningún problema con el script con mis datos (uso las últimas versiones de GeoPandas, Fiona y Numpy)
gene
Puedo enviarle el archivo shp para que pueda verlo usted mismo, pero este archivo shp tiene más de 250 columnas de datos que imagino que le están creando problemas. Intenté esto en un archivo shp con muchos menos atributos y no parece tener ningún problema.
wilbev
5

Otra forma, quizás más 'de bajo nivel', sería usar directamente fionay shapelypara E / S y procesamiento de geometría.

import fiona
from shapely.geometry import shape, mapping

with fiona.open('input_shapefile.shp') as src:
    meta = src.meta
    meta['schema']['geometry'] = 'Point'
    with fiona.open('output_shapefile.shp', 'w', **meta) as dst:
        for f in src:
            centroid = shape(f['geometry']).centroid
            f['geometry'] = mapping(centroid)
            dst.write(f)
Loïc Dutrieux
fuente
Gracias Loic Eso definitivamente soluciona el problema de clasificación que estaba teniendo, pero no soluciona el problema con tantos atributos que hacen que el archivo se corrompa. ¿Tendrías alguna otra idea sobre cómo superar ese problema? Supongo que puede que necesite eliminar atributos. Puedo enviarle un archivo de ejemplo si sería de ayuda.
wilbev
@wilbev Envía un enlace de descarga a tus datos si puedes. De lo contrario, no veo qué podría estar mal.
Loïc Dutrieux
Loic, te envié un archivo de ejemplo. Espero que eso te dé una buena idea del problema con el que me encuentro.
wilbev
@wilbev ¿qué quieres decir con "el archivo se corrompe"? Utilizando el archivo que envió, pude generar los centroides y abrir el archivo de forma de salida en QGIS sin problemas. La tabla de atributos permanece sin cambios entre ambos archivos.
Loïc Dutrieux
Por corrupto, quiero decir que es esencialmente un archivo dbf vacío, ya que después de ejecutar el script crea un archivo dbf de 1 KB y cuando lo abres está completamente vacío. Si ejecuto el mismo script exacto, el que enumeras arriba, en un archivo con menos atributos, funciona sin problemas. Incluso probé en una segunda PC y obtuve el mismo resultado. No lo entiendo
wilbev
2

Creo que la forma más fácil es usar gdal / ogr Virtual Format. ( http://www.gdal.org/drv_vrt.html ) y dialecto SQL / SQLITE ( http://www.gdal.org/ogr_sql.html y https://www.gaia-gis.it/spatialite-3.0 .0-BETA / spatialite-sql-3.0.0.html )

Mi archivo de forma poligonal se llama poly.shp. Luego creo este XML como archivo llamado vrt.vrt. Dentro de este archivo (vrt.vrt), aquí el contenido para convertir a puntos

<OGRVRTDataSource>
    <OGRVRTLayer name="poly">
        <SrcDataSource relativeToVRT="1">poly.shp</SrcDataSource>
        <SrcSQL dialect="sqlite">SELECT ST_PointOnSurface(geometry) as geom_point, poly.* from poly</SrcSQL>
        <GeometryType>wkbPoints</GeometryType> 
        <GeometryField name="geom_point" />
    </OGRVRTLayer>
</OGRVRTDataSource>

En este momento, puede integrar este archivo en Qgis para validarlo. Por supuesto, la representación es más lenta que la fuente sin procesar porque cada característica se convierte como punto en cada consulta de representación.

Después, convierta este archivo (vrt.vrt) en otra cosa usando las utilidades gdal / ogr de un shell / script de python

os.system("ogr2ogr point_from_vrt.shp vrt.vrt poly")

Obtiene un archivo de forma de punto llamado point_from_vrt.shp.

PEL
fuente
Pude hacer que esto funcionara, pero me gustaría mantener todo esto dentro de un script de Python, ya que necesito convertir cientos de archivos, todos con diferentes nombres de archivo. Me gustaría usar la solución @Mike T, pero obtengo un "No hay tal función: ST_Centroid si lo uso y también probé ST_PointOnSurface, que también dice que no existe tal función. Cualquier idea de por qué estaría indicando que estas no son funciones de ExecuteSQL ()?
wilbev
Obtengo'wkbPoints' is not a valid value of the atomic type
Ben Sinclair