Tuto8: Charger des images géolocalisées et créer la couche de points correspondante sur votre portail ArcGIS
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 le chargement d'images géolocalisées depuis un répertoire vers votre portail ArcGIS. Par rapport au tutoriel précédent, celui-ci utilise les informations EXIF de géolocalisation stockées lors de la prise de vue pour créer une couche d'entités ponctuelles. Chaque point de la couche contient l'URL de l'image chargée sur le portail afin de pouvoir facilement l'afficher dans la fenêtre contextuelle (Popup) de la couche.
Le Notebook présenté ci-dessous est téléchargeable ici.
Le tutoriel ci-dessous permet de charger les images géolocalisées d'un répertoire local vers votre portail ArcGIS. Les informations de géolocalisation (exif) des images sont importées afin de créer une table CSV et une couche d'entités ponctuelles. Les attributs des points contiennent l'URL respectives des images chargées sur le portail afin de pouvoir les afficher facilement dans la fenêtre contextuelle (Popup) de la couche.
Création de l'objet GIS et authentification sur le portail ArcGIS. Deux variables permettent de spécifier le répertoire local contenant les images ainsi que l'extension des images à charger. Vous définissez aussi le nom du dossier dans lequel charger les images sur le portail ArcGIS.
In [1]:
import os, csv
from PIL import Image
from PIL.ExifTags import TAGS, GPSTAGS
from IPython.display import display
from arcgis.gis import *
gis = GIS("https://www.arcgis.com", "username", "password")
# Chemin d'accès aux photos
data_dir = "/Users/glavenu/Downloads/IleDeRé"
file_ext = ".jpg" # ".JPG" ".jpg" ".PNG" ".png" ...
portal_folder = "Mes Images"
La fonction ci-dessous permet de convertir les coordonnées GPS stockées en EXIF en degrés décimaux.
In [2]:
def convert_to_degrees(value):
d0 = value[0][0]
d1 = value[0][1]
d = float(d0) / float(d1)
m0 = value[1][0]
m1 = value[1][1]
m = float(m0) / float(m1)
s0 = value[2][0]
s1 = value[2][1]
s = float(s0) / float(s1)
return d + (m / 60.0) + (s / 3600.0)
Fonction permettant la lecture des informations de date et de géolocalisation dans les tags EXIF de l'image.
In [3]:
def read_GPSExif(img_path):
i = Image.open(img_path)
info = i._getexif()
latitude=0
latitude_ref=''
longitude=0
longitude_ref=''
altitude=0
date_time=""
for tag, value in info.items():
decoded = TAGS.get(tag, tag)
if decoded == "DateTimeOriginal":
date_time = value
if decoded == "GPSInfo":
for t in value:
sub_decoded = GPSTAGS.get(t, t)
if (sub_decoded == 'GPSLatitude'):
latitude=convert_to_degrees(value[t])
if (sub_decoded == 'GPSLatitudeRef'):
latitude_ref=value[t]
if (sub_decoded == 'GPSLongitude'):
longitude=convert_to_degrees(value[t])
if (sub_decoded == 'GPSLongitudeRef'):
longitude_ref=value[t]
if (sub_decoded == 'GPSAltitude'):
altitude=value[t][0]/value[t][1]
if (longitude_ref=="W"):
longitude=-1*longitude
if (latitude_ref=="S"):
latitude=-1*latitude
return [longitude,latitude,altitude,date_time]
Les lignes ci-dessous permettent de créer le dossier de destination des images sur le portail. Elles listent également les images du répertoire local.
In [4]:
#Creation du dossier sur ArcGIS Online
if portal_folder is not "":
gis.content.create_folder(portal_folder)
#Recupereration de la liste des fichiers
file_list = os.listdir(data_dir)
#Filtrage des fichiers
image_file_list = [x for x in file_list if x.endswith(file_ext)]
print("Nombre d'images trouvées: " + str(len(image_file_list)))
Les instructions suivantes permettent d'importer les images sur le portail et de construire en même temps le catalogue d'images qui sera ensuite consolider dans un fichier CSV.
In [5]:
n=0
# En-tête du fichier csv
csv_list = [["longitude","latitude","altitude","date_image","nom_image","url_image"]]
for current_image_file in image_file_list:
img_item = gis.content.add({'type':'Image', 'tags': 'Image', 'access': 'public'},
data_dir + '/' + current_image_file, data_dir + '/' + current_image_file,
None,None,portal_folder)
img_item.share(everyone=True, org=True, groups=None, allow_members_to_edit=False)
n+=1
display(img_item)
print(str(n) + '/' + str(len(image_file_list)))
img_info=read_GPSExif(data_dir + '/' + current_image_file)
img_info.append(current_image_file)
img_info.append('http://www.arcgis.com/sharing/rest/content/items/'+ img_item.id +'/data');
csv_list.append(img_info)
On construit ensuite le fichier CSV décrivant le catalogue des images chargées.
In [6]:
my_csv_file = data_dir + '/Catalogue_Images.csv'
with open(my_csv_file, 'w') as myfile:
wr = csv.writer(myfile, quoting=csv.QUOTE_ALL)
for csv_string in csv_list:
wr.writerow(csv_string)
myfile.close()
On charge ensuite le fichier CSV sur le portail puis on publie cette source de données en tant que couche d'entités.
In [7]:
csv_item = gis.content.add({'title':'Catalogue des images','type':'CSV', 'tags': 'Image', 'access': 'public'}, my_csv_file)
csv_item.move(portal_folder)
csv_lyr = csv_item.publish({'title':'Catalogue des images',
'name': 'Catalogue_Images',
'locationType':"coordinates",
'longitudeFieldName':"longitude",
'latitudeFieldName':"latitude"})
Le code ci-dessous modifie la définition de la couche d'entités pour lui définir une nouvelle symbologie par défaut (un symbole d'appareil photo).
In [8]:
from arcgis.features import FeatureLayer
my_flc = FeatureLayer.fromitem(csv_lyr)
update_dict = {'drawingInfo': {
'renderer': {
'type': "simple",
'symbol': {
'angle': 0,
'xoffset': 0,
'yoffset': 0,
'type': "esriPMS",
'url': "9baa7954-db69-4085-9ec9-573335f220e8",
'imageData': "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",
'contentType': "image/png",
'width': 15,
'height': 15
}
},
'transparency': 0
}}
my_flc.manager.update_definition(update_dict)
Out[8]:
La couche d'entités est désormais disponible, il ne vous reste plus qu'à la visauliser dans Jupyter Notebook ou directement dans votre portail.
In [9]:
csv_lyr
Out[9]:
0 comments :
Enregistrer un commentaire