Le blog francophone consacré
aux technologies Esri

Tuto5: Compléter ou modifier les données de vos couches d'entités en "batch"

Je poursuis ma série de tutoriels consacrés à l'usage de l'API ArcGIS for Python. Aujourd'hui, je vous propose de voir comment automatiser la mise à jour et/ou l'ajout de données en "batch" sur vos couches d'entités ou tables hébergées sur  votre portail ArcGIS. Le tutoriel montre également comment supprimer tout ou partie des enregistrements de votre couche d'entités (ou de votre table).

     
Le Notebook présenté ci-dessous est téléchargeable ici.

Tuto 5: Compléter ou modifier les données de vos couches d'entités en "batch"

Dans ce tutoriel, nous allons voir comment écraser ou mettre à jour par lot les données d'une couche d'entités (ou d'une table). ArcGIS Online permet en effet de compléter ou de mettre à jour les données d'une couche en une seule opération. Si votre jeu de données est régulièrement complété ou modifié, vous pourrez automatiser avec l'API Python ArcGIS le processus d'actualisation sans avoir à supprimer, rechercharger et republier les données à chaque mise à jour.
In [35]:
from IPython.display import display
from arcgis.gis import GIS

my_gis = GIS('https://www.arcgis.com', 'username', 'password')
In [36]:
my_gis

Publication initiale de la couche

Considérons un fichier CSV contenant les localisations (latitude, longiture) des radars routiers fixes en France en 2017. Pour publier le service, vous devrez tout d'abord ajouter cette source de données à vos contenus sur le portail. Pour cela, vous récuperez l'objet "ContentManager" puis la méthode "add".
In [45]:
data_file = "/Users/glavenu/Documents/SIG/temp/radars.csv"
csv_item = my_gis.content.add({'title': 'Les radars en France', 
                                'tags': 'Radars,Sécurité routière,France'}, 
                                data_file)
csv_item
Out[45]:
Les radars en France
CSV by glavenu
Last Modified: novembre 11, 2018
0 comments, 0 views
La méthode "publish" permet ensuite de publier un service d'entités depuis cette source de données.
In [46]:
my_lyr = csv_item.publish({"name":"Les_radars_en_France",
                           "title":"Les radars en France",
                            "locationType":"coordinates",
                            "latitudeFieldName":"latitude",
                            "longitudeFieldName":"longitude"})
my_lyr.update(thumbnail='/Users/glavenu/Documents/SIG/temp/radars.png')
my_lyr
Out[46]:
Les radars en France
Feature Layer Collection by glavenu
Last Modified: novembre 11, 2018
0 comments, 0 views
Pour rappel, pour modifier les privilèges d'accès à cette couche, la méthode "share" vou permettra de définir le niveau de partage. Ici, l'élément est partagé avec les membres de l'organisation appartenant au groupe "Démo".
In [50]:
my_lyr.share(everyone=False, org=False, groups='Démo', allow_members_to_edit=False)
Out[50]:
{'itemId': '69c913bffb384e1a965766b7117d65bf', 'notSharedWith': ['Démo']}

Compléter et modifier les données de la couche d'entités

Imaginons maintenant que vous souhaitiez ajouter un deuxième fichier CSV contenant à la fois des modifications sur certains radars existants et les nouveaux radars installés depuis janvier 2018.
Comptons le nombre d'entités de la couche.
In [51]:
from arcgis.features import FeatureLayer
my_FL = FeatureLayer.fromitem(my_lyr)

update_indexes={'name': 'id_Index','fields': 'id','isAscending': False,'isUnique': True,'description': ''}
my_FL.manager.add_to_definition({'indexes':[update_indexes]})

my_query_result = my_FL.query(where="1=1", return_count_only=True)
my_query_result
Out[51]:
3321
Les instructions qui vont suivre vont permettre d'ajouter de nouvelles entités dans la couche d'entités mais également de modifier des entités existantes, notamment celle-ci possédant l'ID 15825:
In [52]:
my_query_result = my_FL.query(where="id=15825")
my_query_result.features[0]
Out[52]:
{"attributes": {"date_heure_dernier_changement": 1536150217000, "type": "Radar fixe", "equipement": "MORPHO", "emplacement": "LE LAMENTIN", "id": 15825, "direction": "FORT DE FRANCE vers LE MARIN", "latitude": 14.60521, "route": "A1", "departement": "972", "vitesse_poids_lourds_kmh": null, "ObjectId": 2340, "longueur_troncon_km": null, "vitesse_vehicules_legers_kmh": 90, "date_installation": 1392336000000, "date_heure_creation": 1536150217000, "longitude": -61.00321}, "geometry": {"y": 1643743.5967171323, "x": -6790846.273955135}}
Ajoutons maintenant le nouveau jeu de données. Quelle que soit la source de données initiale, celui-ci peut être dans n'importe lequel des formats suivants: sqlite, shapefile, filegdb, featureCollection, geojson, csv ou excel.
In [53]:
data_file = "/Users/glavenu/Documents/SIG/temp/radars_2018.csv"
csv_item = my_gis.content.add({'title': 'MAJ Radars 2018', 
                               'tags': 'Radars,Sécurité routière,France'}, 
                                data_file)
csv_item
Out[53]:
MAJ Radars 2018
CSV by glavenu
Last Modified: novembre 11, 2018
0 comments, 0 views
L'ID de ce nouvel élément sera nécessaire pour ajouter cette source de données... et pour la supprimer une fois le service d'entités actualisé
In [54]:
csv_item.id
Out[54]:
'f749be8db3874e4dbb04b03d3f33d566'
Avant de pouvoir ajouter la source de données, pour les fichiers CSV ou Excel, cette source de données doit être analysée afin de déterminer les champs qu'elle contient et les informations de localisation à utiliser pour mettre à jour la couche cible. Dans notre cas, les champs "latitude" et "longitude" seront pas défaut utilisés.
In [55]:
my_gis.content.analyze(item=csv_item.id)['publishParameters']
Out[55]:
{'columnDelimiter': ';',
 'editorTrackingInfo': {'allowAnonymousToDelete': True,
  'allowAnonymousToQuery': True,
  'allowAnonymousToUpdate': True,
  'allowOthersToDelete': False,
  'allowOthersToQuery': True,
  'allowOthersToUpdate': True,
  'enableEditorTracking': False,
  'enableOwnershipAccessControl': False},
 'latitudeFieldName': 'latitude',
 'layerInfo': {'advancedQueryCapabilities': {'supportsAdvancedQueryRelated': True,
   'supportsCountDistinct': True,
   'supportsDistinct': True,
   'supportsHavingClause': True,
   'supportsMaxRecordCountFactor': True,
   'supportsOrderBy': True,
   'supportsOutFieldSQLExpression': True,
   'supportsPagination': True,
   'supportsPaginationOnAggregatedQueries': True,
   'supportsQueryRelatedPagination': True,
   'supportsQueryWithDatumTransformation': True,
   'supportsQueryWithDistance': True,
   'supportsQueryWithResultType': True,
   'supportsReturningGeometryCentroid': False,
   'supportsReturningQueryExtent': True,
   'supportsSqlExpression': True,
   'supportsStatistics': True,
   'supportsTopFeaturesQuery': True},
  'allowGeometryUpdates': True,
  'capabilities': 'Create,Delete,Query,Update,Editing',
  'copyrightText': '',
  'currentVersion': 10.61,
  'defaultVisibility': True,
  'description': '',
  'displayField': '',
  'drawingInfo': {'renderer': {'symbol': {'contentType': 'image/png',
     'height': 15,
     'imageData':...
La méthode "append" permet d'ajouter et/ou de modifier des entités dans la couche d'entités cible. Vous indiquerez notamment les champs de la source à ajouter ("append_fields") avec ceux déjà existants dans la couche cible (field_mapping). Le paramètre "source_info" correspond aux informations de publication (nécessaire ici car le format est un CSV). Dernier paramètre indispensable, le paramètre "upsert_matching_field" permet de spécifier le champ identiant dans la couche cible qui permettra la mise en correspondance avec les enregistrements de la table à ajouter.
In [56]:
my_FL.append(item_id=csv_item.id, upload_format='csv', source_table_name=None, 
            field_mappings=["date_heure_creation","date_heure_dernier_changement","date_installation","id","direction","emplacement","equipement","latitude","longitude","route","type","vitesse_vehicules_legers_kmh","ObjectId"],
            source_info=my_gis.content.analyze(item=csv_item.id)['publishParameters'], 
            upsert=True, skip_updates=False, update_geometry=True, 
            append_fields=["date_heure_creation","date_heure_dernier_changement","date_installation","id","direction","emplacement","equipement","latitude","longitude","route","type","vitesse_vehicules_legers_kmh","ObjectId"],
            rollback=True, skip_inserts=False, upsert_matching_field="id")
Out[56]:
True
Vérifions tout d'abord le nombre d'entités désormais présentes dans la couche. On constate que 29 entités ont été ajoutées.
In [57]:
my_query_result = my_FL.query(where="1=1", return_count_only=True)
my_query_result
Out[57]:
3350
Vérifions que les attributs de l'entité possédant l'ID 15825 ont bien été actualisés.
In [58]:
my_query_result = my_FL.query(where="id=15825")
my_query_result.features[0]
Out[58]:
{"attributes": {"date_heure_dernier_changement": 1536150217000, "type": "Radar discriminant", "equipement": "MORPHO", "emplacement": "LE LAMENTIN", "id": 15825, "direction": "FORT DE FRANCE vers LE MARIN", "latitude": 14.60521, "route": "A1", "departement": "972", "vitesse_poids_lourds_kmh": null, "ObjectId": 2340, "longueur_troncon_km": null, "vitesse_vehicules_legers_kmh": 110, "date_installation": 1392336000000, "date_heure_creation": 1536150217000, "longitude": -61.00321}, "geometry": {"y": 1643743.5967171323, "x": -6790846.273955135}}
Maintenant que la source de données CSV a été utilisée pour compléter et mettre à jour la couche d'entités, elle peut être supprimée du portail.
In [59]:
csv_item.delete()
Out[59]:
True

Remplacer l'intégralité des données d'une couche d'entités

Dans certains cas d'usage vous pouvez avoir besoin, régulièrement et de manière autiomatisée, d'écraser la totalité des données d'une couche d'entités (ou d'une table) hébergée sur votre portail. Il existe une manière très simple de le faire en utilisant les instructions suivantes:
Commençons par charger une nouvelle source de données (ici un Shapefile) avec les informations démographiques sur le département de la Somme.
In [90]:
data_file = "/Users/glavenu/Documents/SIG/temp/DEMOGRAPHIE_2016.zip"
shp_item = my_gis.content.add({'title': 'Démographie des communes de la Somme - 2016', 
                            'tags': 'Démographie,Haut-de-France,Somme'},
                            data_file)
Publions cette source de données en tant que couche d'entités.
In [91]:
my_shp_lyr = shp_item.publish({'name':'Demographie_Communes_Dept_Somme',
                            'title':'Démographie des communes de la Somme - 2016',
                            'tags': 'Démographie,Haut-de-France,Somme'})
my_shp_lyr
Out[91]:
Démographie des communes de la Somme - 2016
Feature Layer Collection by glavenu
Last Modified: novembre 12, 2018
0 comments, 0 views
Pour pouvoir écraser vos données, vous devez disposer du nouveau fichier source (ici le shapefile contenant les données démographiques de 2017) et instancier l'objet "FeatureLayerCollectionManager" correspondant à votre couche d'entités. Ce dernier est instancié grâce à l'URL du service d'entités de votre couche.
In [92]:
new_data_file= "/Users/glavenu/Documents/SIG/temp/DEMOGRAPHIE_2017.zip"
In [93]:
from arcgis.features.managers import FeatureLayerCollectionManager
my_FLCollectionManager = FeatureLayerCollectionManager(my_shp_lyr.url,my_gis)
Une fois l'objet "FeatureLayerCollectionManager" instancié, la méthode "overwrite" vous permettra de remplacer la totalité des données de votre couche.
In [94]:
my_FLCollectionManager.overwrite(new_data_file)
Out[94]:
{'success': True}
La démarche sera exactement la même avec d'autres sources de données comme un fichier GeoJSON, une Géodatabase, ... y compris si le service contient plus couches/tables. On doit cependant noter les contraintes suivantes:
  • Seuls les couches d'entités et les tables hébergées peuvent être utilisées
  • La source de données d'origine utilisée pour la publication du service doit toujours exister sur le portail
  • La nouvelle source de données servant à écraser les données doit être dans le même format que la source de données d'origine
  • Le schéma de la nouvelle source de données doit être exactement le même que la source de données d'origine. Le nombre d'enregistrement peut bien entendu être différent.

Supprimer tout ou partie d'une couche d'entités

Dans certains cas, votre couche d'entités peut avoir besoin d'être régulièrement vidée sans pour autant avoir besoin d'y recharger immédiatement des données. Voici deux méthodes permettant de vider tout ou partie d'une couche d'entités.
Si vous souhaitez maîtriser les entités (ou les enregistrements) à supprimer, vous pourrez utiliser la méthode "delete_features" sur votre objet "FeatureLayer" qui offre l'avantage de pouvoir spécifier une requête SQL ("where=xxx").
In [98]:
my_FL = FeatureLayer.fromitem(my_shp_lyr)
my_FL.delete_features(where="Population>100000", rollback_on_failure=True)
Out[98]:
{'deleteResults': [{'globalId': None,
   'objectId': 19,
   'success': True,
   'uniqueId': 19}]}
Une autre possibilité consiste à utiliser la méthode "truncate" disponible sur l'objet "FeatureLayerManager". Dans ce cas, tous les enregistrements de la couche/table sont supprimés.
In [82]:
my_FL.manager.truncate()
Out[82]:
{'success': True}

Partager cet article:

Rejoindre la discussion

    Les commentaires à propos de cet article:

4 comments :

Anonyme a dit…

Bonjour Gaëtan,

Sauf erreur de ma part dans le paragraphe "Remplacer l'intégralité des données d'une couche d'entités", il semble qu'il y ait une variable qui ne soit pas la bonne :

IN [93] my_FLCollectionManager.overwrite(data_file_overwrite)

Ne serait-ce pas plutôt "my_FLCollectionManager.overwrite(new_data_file)" qui fait référence à la donnée source à utiliser pour écraser les données ?

Rémi

Anonyme a dit…

Bonjour,

Avec l'API Python, est-il possible d'écraser un Feature Layer hébergé sur AGOL directement à partir avec une classe d'entités de géodatabase fichier sans passer par un .zip ?

Si oui comment ?

Merci d'avance pour votre retour.

Gaëtan Lavenu a dit…

Bonjour Rémi,

Merci pour ce coup d'oeil aiguisé ! Effectivement, un changement tardif dans les noms de variables explique cette coquille. C'est corrigé dans l'article et dans le notebook en téléchargement.

Gaëtan Lavenu a dit…

Bonjour,

Concernant l'écrasement d'une Feature Layer, si celle-ci est initialement issue de la publication d'une classe d'entités de Géodatabase alors elle doit être écrasée par une Géodatabase de même structure (et donc chargée en tant que fichier ZIP). Cependant, d'autres approches sont possibles. Par exemple, on peut imaginer un script qui vide la couche d'entité puis ajoute les nouvelles entités, une par une (avec l'API Python ArcGIS), en lisant les nouvelles données directement depuis une classe d'entités de Géodatabase (ceci avec les librairies ArcPy). Cela impliquera simplement que le script tourne sur une machine disposant d'ArcGIS Desktop, ou ArcGIS Enterprise (GIS Server) ou ArcGIS Pro.

Cordialement,