Le blog francophone consacré
aux technologies Esri

Tuto3: Accéder aux données de vos couches d'entités avec l'API Python ArcGIS

Je poursuis ma série de tutoriels consacrée à l'usage de l'API ArcGIS for Python. Aujourd'hui, je vous propose de voir comment interroger, afficher, requêter et exporter les données des couches d'entités de votre portail ArcGIS, tout cela depuis un script Python.

    
Le Notebook présenté ci-dessous est téléchargeable ici.
Ce tutoriel a pour objectif de vous présenter les classes, les propriétés et les méthodes de base qui permettent d'accéder aux données des couches d'entités. Au coeur de l'API Python ArcGIS, les capacités de requête, de filtrage et d'intérogation des données sont indispensables dans nombre de tâches d'analyse ou d'administration de vos données. A l'aide des exemples de code ci-dessous, vous pourrez élaborer des scripts plus évolués automatisant la récupération de couches d'entités, leur intérogation et leur filtrage mais également l'export des données en ligne vers des fichiers locaux...
In [1]:
from IPython.display import display
from arcgis.gis import GIS

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

Rechercher une couche d'entités

Commençons tout d'abord par rechercher des couches d'entités référencées et/ou hébergées sur votre portail ArcGIS.
In [7]:
# Recherche des couches d'entités en rapport avec le réseau de bus de Versailles
search_results = my_gis.content.search('Réseau BUS Versailles','Feature Layer')

# Accès au premier élément résultant de la recherche
reseau_bus_item = search_results[0]
reseau_bus_item
Out[7]:
Réseau BUS
Réseau de bus Versailles Grand-ParcFeature Layer Collection by glavenu_esrifrance
Last Modified: septembre 06, 2017
0 comments, 241 views
Un élément de type "collection de couches d'entités" sur un portail ArcGIS peut contenir plusieurs couches. Pour obtenir ces différentes couches, vous procéderez comme ceci:
In [8]:
reseau_bus_layers = reseau_bus_item.layers
reseau_bus_layers
Out[8]:
[<FeatureLayer url:"https://services.arcgis.com/d3voDfTFbHOCRwVR/arcgis/rest/services/Bus_VGP/FeatureServer/0">,
 <FeatureLayer url:"https://services.arcgis.com/d3voDfTFbHOCRwVR/arcgis/rest/services/Bus_VGP/FeatureServer/1">]

Accéder à une couche à l'aide de son ID

Les collections de couches d'entités peuvent également être récupérées directement avec l'ID de l'élément si ce dernier est connu.
In [12]:
bureaux_votes = my_gis.content.get('7bc2993f487e4336b755a7c33aad99a5')
bureaux_votes
Out[12]:
BureauDeVote
Bureaux de vote de la ville de NantesFeature Layer Collection by tdavid_esrifrance
Last Modified: septembre 28, 2016
0 comments, 916 views
Comme précédement, on peut récupérer les différentes couches d'entités (ou tables) de cette collection de couches d'entités.
In [13]:
bureaux_votes.layers
Out[13]:
[<FeatureLayer url:"https://services.arcgis.com/d3voDfTFbHOCRwVR/arcgis/rest/services/BureauDeVote/FeatureServer/0">,
 <FeatureLayer url:"https://services.arcgis.com/d3voDfTFbHOCRwVR/arcgis/rest/services/BureauDeVote/FeatureServer/1">,
 <FeatureLayer url:"https://services.arcgis.com/d3voDfTFbHOCRwVR/arcgis/rest/services/BureauDeVote/FeatureServer/2">]
Le code ci-dessous permet de boucler sur chacune de ces couches pour afficher leur nom.
In [14]:
for lyr in bureaux_votes.layers:
    print(lyr.properties.name)
Lieux de vote
Bureaux de vote
Ville

Accéder à une couche d'entités à partir du service d'entités sous-jacent

Un service d'entités ArcGIS (Enterprise ou Online) est référencé dans votre portail ArcGIS en tant que collection de couches d'entités. Il peut contenir différentes couches d'entités et tables. Dans l'API Python ArcGIS, vous gérez ce type d'élément avec la classe "arcgis.features.FeatureLayerCollection".
Cet objet "FeatureLayerCollection" peut se construire à l'aide de l'url du service ArcGIS.
In [16]:
from arcgis.features import FeatureLayerCollection

fs_url = 'https://services.arcgis.com/d3voDfTFbHOCRwVR/arcgis/rest/services/Courtance/FeatureServer'
flc_cadastre = FeatureLayerCollection(fs_url,my_gis)
Pour récupérer la liste des couches d'entités de ce service:
In [17]:
flc_cadastre.layers
Out[17]:
[<FeatureLayer url:"https://services.arcgis.com/d3voDfTFbHOCRwVR/arcgis/rest/services/Courtance/FeatureServer/0">,
 <FeatureLayer url:"https://services.arcgis.com/d3voDfTFbHOCRwVR/arcgis/rest/services/Courtance/FeatureServer/1">,
 <FeatureLayer url:"https://services.arcgis.com/d3voDfTFbHOCRwVR/arcgis/rest/services/Courtance/FeatureServer/2">,
 <FeatureLayer url:"https://services.arcgis.com/d3voDfTFbHOCRwVR/arcgis/rest/services/Courtance/FeatureServer/3">]
Récupérer la liste des tables de ce service:
In [18]:
flc_cadastre.tables
Out[18]:
[<Table url:"https://services.arcgis.com/d3voDfTFbHOCRwVR/arcgis/rest/services/Courtance/FeatureServer/4">]

Accédéder directement à une couche d'entités par son URL

Lorsque votre service web contient plusieurs couches, chacune d'entre-elle est accessible par l'URL de service suivi d'un numéro d'index. L'API Python ArcGIS vous permet de créer directement un objet "FeatureLayer" à partir de cette URL.
In [32]:
from arcgis.features import FeatureLayer
lyr_url = 'https://services.arcgis.com/d3voDfTFbHOCRwVR/arcgis/rest/services/Courtance/FeatureServer/2'

my_FL = FeatureLayer(lyr_url,my_gis)
my_FL
Out[32]:
<FeatureLayer url:"https://services.arcgis.com/d3voDfTFbHOCRwVR/arcgis/rest/services/Courtance/FeatureServer/2">

Accéder aux propriétés d'une couche d'entités

Vous savez désormais comment créer l'objet "FeatureLayer" représentant votre couche d'entités. Voyons maintenant comment accéder à certaines propriétés de cette dernière. Vous récupererez tout d'abord les propriétés de la couche via la propriété "properties".
La propriété "extent" permettra de récupérer l'étendue de la couche.
In [20]:
my_FL.properties.extent
Out[20]:
{
  "ymax": 5820178.721860941,
  "xmin": 246782.23017553228,
  "spatialReference": {
    "latestWkid": 3857,
    "wkid": 102100
  },
  "ymin": 5813925.058303243,
  "xmax": 253198.4213450217
}
La propriété "capabilities" permet de vérifier les types d'opérations disponibles sur la couche.
In [26]:
my_FL.properties.capabilities
Out[26]:
'Create,Delete,Query,Update,Editing,Extract,ChangeTracking'
La propriété "drawingInfo" permet d'accéder à différentes informations relatives à l'affichage de la couche, comme par exemple le type de rendu.
In [27]:
my_FL.properties.drawingInfo.renderer.type
Out[27]:
'simple'
Via l'objet "renderer" vous pourrez accéder aux détails concernant le rendu de la couche:
In [28]:
my_FL.properties.drawingInfo.renderer
Out[28]:
{
  "symbol": {
    "color": [
      72,
      136,
      92,
      191
    ],
    "type": "esriSFS",
    "style": "esriSFSSolid",
    "outline": {
      "width": 0.75,
      "color": [
        0,
        0,
        0,
        255
      ],
      "type": "esriSLS",
      "style": "esriSLSSolid"
    }
  },
  "type": "simple"
}
Pour récupérer les échelles mi et max d'affichage de la couche, vous utiliserez les propriétés "minScale" et "maxScale".
In [39]:
"Echelle min: " + str(my_FL.properties.minScale) + "  Echelle max: " + str(my_FL.properties.maxScale)
Out[39]:
'Echelle min: 144448  Echelle max: 1128'
Il peut parfois être intéressant de récupérer l'ID de l'élément référençant cette couche sur votre portail ArcGIS.
In [42]:
my_FL.properties.serviceItemId
Out[42]:
'2bc8bda2df424eefb4c7fe4271923028'

Accéder aux données d'une couche d'entités à l'aide de requêtes

Commençons par un premier exemple dans lequel nous souhaitons mettre le champ AE (Année Exploitation) à 2017 pour les cinémas d'Ile-de-France et 2016 pour les autres régions:
In [43]:
for f in my_FL.properties.fields:
    print(f['name'])
OBJECTID
TYPE
SURFACE
PARCELLE
Shape__Area
Shape__Length
GlobalID
La méthode "Query" permet de faire une requête et de récuperer une liste d'objet "Feature" correspondant au résultat de cette requête. De nombreux paramètres peuvent être indiqués lors d'une opération "query".
Commençons par exemple simple consistant à sélectionner les parcelles dont la surface est supérieure à 3000m2 avec dans le résultat uniquement les champs "PARCELLE" et "SURFACE".
In [67]:
query_result1 = my_FL.query(where='SURFACE > 5000', out_fields='PARCELLE,SURFACE', return_geometry=False)
len(query_result1.features)
Out[67]:
2
Si l'objectif est uniquement de compter le nombre d'entités satisfaisant la requête, vous pourrez exécuter la requête suivante:
In [56]:
query_result2 = my_FL.query(where='SURFACE > 1000', countOnly=True)
query_result2
Out[56]:
<FeatureSet> 21 features
Reprenons la première requête et affichons les attributs de la première entités sélectionnée:
In [91]:
query_result1.features[0].attributes
Out[91]:
{'OBJECTID': 1536, 'PARCELLE': 'AD0007', 'SURFACE': 5062.026}
On peut aussi afficher les données de toutes les entités à l'aide d'une boucle:
In [92]:
for f in query_result1.features:
    display(f.attributes)
{'OBJECTID': 1536, 'PARCELLE': 'AD0007', 'SURFACE': 5062.026}
{'OBJECTID': 1552, 'PARCELLE': 'AD0007', 'SURFACE': 6580.02}
La présentation et l'exploitation des données peut également se faire en utilisant un objet "spatialdataframe" qui se récupère à l'aide de la propriété "sdf". Les objets "spatialdataframe" de l'API Python ArcGIS héritent des objets "dataframe" de Python et permettent de nombreuses opérations et analyses avancées sur des données tabulaires et spatiales.
In [71]:
my_sdf = query_result1.sdf
my_sdf
Out[71]:

OBJECTID PARCELLE SURFACE
0 1536 AD0007 5062.026
1 1552 AD0007 6580.020
Le résultat d'une requête est un objet "FeatureSet" dont certaines méthodes sont communes avec l'objet "FeatureLayer". Par exemple, pour récupérer les champs du "FeatureSet", vous utiliserez la propriété "fields"
In [55]:
query_result1.fields
Out[55]:
[{'alias': 'OBJECTID',
  'defaultValue': None,
  'domain': None,
  'name': 'OBJECTID',
  'sqlType': 'sqlTypeOther',
  'type': 'esriFieldTypeOID'},
 {'alias': 'PARCELLE',
  'defaultValue': None,
  'domain': None,
  'length': 10,
  'name': 'PARCELLE',
  'sqlType': 'sqlTypeOther',
  'type': 'esriFieldTypeString'},
 {'alias': 'SURFACE',
  'defaultValue': None,
  'domain': None,
  'name': 'SURFACE',
  'sqlType': 'sqlTypeOther',
  'type': 'esriFieldTypeDouble'}]
Si l'objectif est uniquement de compter le nombre d'entités satisfaisant la requête, vous pourrez exécuter la requête suivante:
In [64]:
query_result2 = my_FL.query(where='SURFACE > 1000', return_count_only=True)
query_result2
Out[64]:
21

Requêter vos données dans différents systèmes de coordonnées

Par défaut, les resultats des requêtes sont renvoyés par défaut dans le même système de coordonnées que la couche source. Cependant, vous pouvez préciser, lors de la requête, un autre système de coordonnées en sortie. Vous le préciserez en utilisant l'argument "out_sr".
Par exemple, le résultat de notre première requête a renvoyé des géométrie dans le système de coordonnées 3857 (Web Based Mercator).
In [80]:
query_result3 = my_FL.query(where="PARCELLE LIKE 'AK%'")
query_result3.spatial_reference
Out[80]:
{'latestWkid': 3857, 'wkid': 102100}
In [81]:
query_result3.features[0].geometry
Out[81]:
{'rings': [[[252136.149696843, 5817815.7552909],
   [252134.571141199, 5817809.48834903],
   [252130.828002196, 5817794.62794569],
   [252125.523301028, 5817796.04561238],
   [252131.029306728, 5817816.99276241],
   [252136.149696843, 5817815.7552909]]]}
Les coordonnées sont, comme attendu en Web Mercator. Si vous souhaitez maintenant les obtenir en Latitude et Longitude, vous utiliserez le paramètre "out_sr" avec la valeur "wkid:4326":
In [83]:
query_result4 = my_FL.query(where="PARCELLE LIKE 'AK%'", out_sr="4326")
query_result4.spatial_reference
Out[83]:
{'latestWkid': 4326, 'wkid': 4326}
In [84]:
query_result4.features[0].geometry
Out[84]:
{'rings': [[[2.26497756951722, 46.2333055912101],
   [2.2649633891106, 46.2332666493096],
   [2.26492976392083, 46.233174308731],
   [2.26488211097946, 46.2331831179307],
   [2.26493157227021, 46.2333132806817],
   [2.26497756951722, 46.2333055912101]]]}
Ou encore en Lambert 93 (wkid: 2154):
In [88]:
query_result5 = my_FL.query(where="PARCELLE LIKE 'AK%'", out_sr="2154")
query_result5.spatial_reference
Out[88]:
{'latestWkid': 2154, 'wkid': 102110}
In [87]:
query_result5.features[0].geometry
Out[87]:
{'rings': [[[643356.436900693, 6570646.29148082],
   [643355.303881922, 6570641.97727361],
   [643352.61721304, 6570631.74725531],
   [643348.954104736, 6570632.75967728],
   [643352.900233288, 6570647.17837032],
   [643356.436900693, 6570646.29148082]]]}

Exporter les données de vos couches vers des fichiers locaux

L'API Python ArcGIS vous permet à tout moment des passerelles entre des données en ligne et des données locales.
Réalisons tout d'abord la requête suivante pour récupérer les parcelles de la section AD:
In [ ]:
query_result6 = my_FL.query(where="PARCELLE LIKE 'AD%'", out_sr="2154")
Vous pouvez utiliser l'objet standard "dataframe" de Pandas pour un export en CSV. Pour cela, vous utiliserez la méthode "df" directement sur le "FeatureSet" résultat de votre requête.
In [107]:
my_sdf = query_result6.df
my_sdf.to_csv('/Users/glavenu/Documents/SIG/temp/batiments_section_AD.csv')
En réalité, notre objet "my_sdf" est un objet "SpatialDataFrame" de l'API Python ArcGIS. Il étend l'objet "DataFrame" de Pandas pour apporter des capacités liées à la dimension spatiale de jeu de données (sélection spatiale, jointure, intersection, reprojection, affichage dans une carte web dans votre Notebook, ...). Par exemple, il permettra un export au format shapefile avec la méthode "to_featureclass". A noter que si les librairies ArcPy sont installées sur votre machine, d'autres formats d'export seront disponible comme le format "Géodatabase Fichier".
In [125]:
columns = ['PARCELLE', 'SURFACE', 'SHAPE']
my_sdf[columns].to_featureclass(out_location=r"/Users/glavenu/Documents/SIG/temp",
                                    out_name="batiments_section_AD.shp")
Out[125]:
'/Users/glavenu/Documents/SIG/temp/batiments_section_AD.shp'
Il est également possible d'utiliser directement l'objet "FeatureSet" et la méthode "save" pour exporter le résultat de la requête. Vous pourrez alors chisir entre un export au format csv (pour les tables) ou "json Esri" (pour les couches d'entités).
In [120]:
query_result6.save('/Users/glavenu/Documents/SIG/temp/','batiments_section_AD.csv')
Out[120]:
'/Users/glavenu/Documents/SIG/temp/batiments_section_AD.csv'
In [96]:
query_result6.save('/Users/glavenu/Documents/SIG/temp/','batiments_section_AD.json')
Out[96]:
'/Users/glavenu/Documents/SIG/temp/batiments_section_AD.json'
Il est également possible d'exporter le résultat d'une requête au format geojson. Pour cela, votre requête devra spécifier des géométries en Lat/Long comme la requête ci-dessous qui récupère l'ensemble des données de la couche:
In [126]:
query_result7 = my_FL.query(where="1=1", out_sr="4326")
Ensuite, à l'aide de la méthode "to_geojson" sur l'objet "FeatureSet" vous pouvrrez récupérer la chaîne de caractères correspondant à la description geojson de jeux de données. Il suffit alors de le conveertir en objet json puis de l'enregistrer dans un fichier local.
In [123]:
import json
str_geojson=query_result7.to_geojson
my_geojson = json.loads(str_geojson) 
with open('/Users/glavenu/Documents/SIG/temp/batiments_section_AD.geojson', 'w') as outfile:
    json.dump(my_geojson, outfile)

Republier une partie des données de vos couches sur votre portail

Nous avons vu comment accéder et réaliser des requêtes sur les couches d'entités de votre portail. Il peut arriver que la finalité soit de republier le résultat de ces requêtes en tant que couche d'entités sur votre portail. Pour cela, rien de plus simple, il suffira de reprendre l'objet "SpatialDataFrame" et d'utiliser la méthode "to_featurelayer". Vous préciserez alors le titre, l'objet GIS et les balises décrivant la couche d'entités à créer.
In [131]:
my_sdf = query_result6.df
columns = ['PARCELLE', 'SURFACE', 'SHAPE']
my_sdf[columns].to_featurelayer("Bâtiments de la section AD", gis=my_gis, tags=['Cadastre','Bâtiments'])
Out[131]:
Bâtiments de la section AD
Feature Layer Collection by glavenu_esrifrance
Last Modified: octobre 30, 2018
0 comments, 0 views


Partager cet article:

Rejoindre la discussion

    Les commentaires à propos de cet article: