Convertir le centre de gravité d'une entité polygonale en points à l'aide de Python

9

Je voudrais convertir certains fichiers shp basés sur des polygones qui ont plusieurs entités polygonales en points pour chaque entité qui représenteraient essentiellement le centriode de chaque entité polygonale. Je sais que dans le monde ArcGIS, je pourrais utiliser l'outil Feature To Point, mais je voudrais le conserver dans un script qui pourrait être exécuté sur des PC sans arcpy, donc je cherche une alternative open source à cela. Quelqu'un est-il au courant d'une bibliothèque que je pourrais utiliser pour cela, ainsi que des instructions sur la façon de l'utiliser pour y parvenir?

wilbev
la source
J'ai toujours plusieurs problèmes avec la réponse fournie par Gene ci-dessous. Les problèmes sont de savoir comment il réorganise les attributs de leur ordre d'origine en ordre alphabétique, ce qui est un problème. Deuxièmement, le fichier de forme est corrompu, peut-être à cause du fichier que j'essaie de convertir avec plus de 250 attributs.
wilbev
Il existe un outil standard appelé «Polygon Centroids» dans QGIS qui fait exactement cela - avez-vous besoin d'un script? Il serait assez facile de créer un script à l'aide de PyQGIS, je pense.
Dùn Caan
Il doit s'agir d'un script et fonctionner sur des PC qui n'ont pas QGIS dessus.
wilbev

Réponses:

9

Vous pouvez exécuter une ogr2ogrcommande (par exemple à partir d'un shell OSGeo4w). Par exemple sur un fichier de formes de pays:

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

Le nouveau fichier de formes countries_centroid.shpdoit être similaire à l'entrée, mais contenir juste un point par [Multi] Polygone.

@PEL montre également un bon exemple avec ST_PointOnSurface, qui est simple à remplacer dans cette commande.


Quelque chose de similaire peut être fait en Python, si nécessaire, mais cela peut prendre quelques lignes de code de plus:

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
la source
Je pense que vous avez proposé la solution la plus simple avec OGR et SQL. Je pense qu'il est plus sûr d'ajouter un paramètre à OGR avec -nlt Point
PEL
Malheureusement, je ne peux pas faire fonctionner cela. Je reçois une erreur indiquant que ST_Centroid n'est pas une fonction.
wilbev
1
Il a besoin de l'option de dialecte SQLite (comme indiqué) et de Spatialite intégré à GDAL, ce qui n'est pas toujours garanti. OSGeo4W a une bonne version de GDAL qui exécutera correctement cette commande.
Mike T
Je peux obtenir votre script supérieur dans la recommandation ogr2ogr pour travailler sans problème. Cependant, je dois le faire dans un script python autonome, donc j'essaie de faire fonctionner votre deuxième ensemble de code, c'est là que je continue vers le ST_Centroid n'est pas une erreur de fonction. Mon code est identique à ce que vous avez ci-dessus, y compris le dialecte sqlite.
wilbev
1
L'erreur que vous décrivez est lorsque GDAL a été construit sans le support de Spatialite. Certains packages gdal-python le supportent, mais pas tous. Essayez d'ouvrir un shell OSGeo4W et d'exécuter le script Python à partir de cet environnement. Je pense que l'ensemble par défaut de packages liés à gdal-bininclure ce support.
Mike T
9

Utilisez simplement Fiona ou GeoPandas (Python 2.7.x et 3.x)

Quelques polygones

entrez la description de l'image ici

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

entrez la description de l'image ici

Transformation en points (centroïdes)

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

entrez la description de l'image ici

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

Résultat

entrez la description de l'image ici

gène
la source
Merci pour le gène de réponse. Je pense que vous pouvez avoir une faute de frappe ci-dessus où la variable 'gdf' devrait être 'poly "à droite? Dans le code points.crs = gdf.crs. Je rencontre également quelques autres problèmes où le fichier .prj ne reçoit pas créé, il apparaît vide et l'ordre des champs d'attribut change leur ordre à partir des données de polygone car ils semblent maintenant être alphabétiques. Il est important qu'ils restent dans le même ordre. Vous savez comment garder l'attribut de champ dans le même ordre?
wilbev
Merci, corrigé. Pour l'ordre des champs d'attributs, modifiez simplement l'ordre du GeoPandas GeoDataFrame (= Pandas DataFrame)
gène
Merci Gene, mais je ne suis pas sûr de comprendre où je changerais cet ordre dans le code ici. Je rencontre également deux autres problèmes avec cela. Tout d'abord, le fichier * .prj est vide sur le nouveau fichier shp. Deuxièmement, lorsque j'essaie d'ouvrir le fichier shp dans le lecteur shp, cela donne une erreur d'ouverture du fichier comme s'il était corrompu. Il semble fonctionner sans être corrompu si le fichier shp n'a qu'une seule fonctionnalité, mais plusieurs sont là où il semble avoir des problèmes.
wilbev
Désolé, mais vous devez connaître les Pandas pour cela et je n'ai aucun problème avec le script avec mes données (j'utilise les dernières versions de GeoPandas, Fiona et Numpy)
gène
Je peux vous envoyer le fichier shp pour que vous puissiez voir par vous-même, mais ce fichier shp contient plus de 250 colonnes de données qui, j'imagine, créent des problèmes pour lui. J'ai essayé cela sur un fichier shp avec beaucoup moins d'attributs et cela ne semble pas avoir de problème.
wilbev
5

Une autre façon, peut-être plus «bas niveau», serait d'utiliser directement fionaet shapelypour le traitement des E / S et de la géométrie.

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
la source
Merci Loic. Cela résout définitivement le problème de tri que j'avais, mais cela ne résout pas le problème avec autant d'attributs entraînant la corruption du fichier. Auriez-vous d'autres idées sur la façon de surmonter ce problème? Je suppose que je devrai peut-être supprimer des attributs. Je peux vous envoyer un exemple de fichier si cela peut vous aider.
wilbev
@wilbev Envoyez un lien de téléchargement vers vos données si vous le pouvez. Sinon, je ne vois pas ce qui pourrait mal se passer.
Loïc Dutrieux
Loic, je vous ai envoyé un exemple de fichier. J'espère que cela vous donne une bonne idée du problème que je rencontre.
wilbev
@wilbev qu'entendez-vous par «le fichier est corrompu»? En utilisant le fichier que vous avez envoyé, j'ai pu produire les centroïdes et ouvrir le fichier de formes de sortie dans QGIS sans problème. La table des attributs reste inchangée entre les deux fichiers.
Loïc Dutrieux
Par corrompu, je veux dire qu'il s'agit essentiellement d'un fichier dbf vide car après avoir exécuté le script, il crée un fichier dbf de 1 Ko et lorsque vous l'ouvrez, il est complètement vide. Si je lance le même script exact, celui que vous listez ci-dessus, sur un fichier avec moins d'attributs, cela fonctionne sans problème. J'ai même essayé sur un deuxième PC et j'ai obtenu le même résultat. Je ne comprends pas.
wilbev
2

Je pense que la façon la plus simple est d'utiliser le format virtuel gdal / ogr. ( http://www.gdal.org/drv_vrt.html ) et dialecte SQL / SQLITE ( http://www.gdal.org/ogr_sql.html et https://www.gaia-gis.it/spatialite-3.0 .0-BETA / spatialite-sql-3.0.0.html )

Mon fichier de formes polygonales s'appelle poly.shp. Ensuite, je crée ce fichier XML appelé vrt.vrt. A l'intérieur de ce fichier (vrt.vrt), voici le contenu à convertir en points

<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>

À ce stade, vous pouvez intégrer ce fichier dans Qgis pour le valider. Bien sûr, le rendu est plus lent que la source brute car chaque entité est castée en tant que point sur chaque requête de rendu.

Ensuite, convertissez ce fichier (vrt.vrt) en quelque chose d'autre en utilisant les utilitaires gdal / ogr à partir d'un shell / script python

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

Vous obtenez un fichier de formes de points nommé point_from_vrt.shp.

PEL
la source
J'ai pu faire fonctionner cela mais je voudrais garder tout cela dans un script python car j'ai besoin de convertir des centaines de fichiers avec des noms de fichiers différents. Je voudrais utiliser la solution @Mike T mais j'obtiens une "Aucune fonction de ce type: ST_Centroid si j'utilise cela et j'ai également essayé ST_PointOnSurface qui dit également qu'il n'y a pas une telle fonction. Toute idée pourquoi cela indiquerait que ce ne sont pas des fonctions d'ExecuteSQL ()?
wilbev
Je reçois'wkbPoints' is not a valid value of the atomic type
Ben Sinclair