Hello, I have been trying to fill a shapefile with EXIF data from pictures taken by a drone.
I was able to create the shapefile and the desired fields in the attribute table, but I failed to populate the fields with the data. The script below always returns UpdateCursor() takes no arguments.
As I wrote this script with the input from other people on the internet (suggestions and actual scripts), I have no idea what to do next and how to resolve this issue. I welcome any advice and will appreciate the help.
import os
import arcpy
from pathlib import Path
IMAGE_FOLDER = Path(
r"\\...\InspectionServicesData\DRONE_INSPECTION_SERVICE\01_Projects\...\2_FIELD-WORK\DATA-DUMP\2022-08-13...\DJI_202208131356_022")
img_path = r"\\...\InspectionServicesData\DRONE_INSPECTION_SERVICE\01_Projects\...\2_FIELD-WORK\DATA-DUMP\2022-08-13...\DJI_202208131356_022"
image_path = os.listdir(img_path)
for img in image_path:
full_image_path = os.path.join(img_path, img)
exif_properties = {}
output_shpdata = r"C:\Users\...\OneDrive...\Desktop\EXIF_TEST\output_shpdata.shp"
shp_data = []
spatial_ref = arcpy.env.outputCoordinateSystem = arcpy.SpatialReference(4326)
for image in IMAGE_FOLDER.glob("*.jpg"):
exif_properties[image.name] = arcpy.GetImageEXIFProperties(image)
for name in exif_properties:
target_data = exif_properties[name][3]
root_path = r"\\...\InspectionServicesData\DRONE_INSPECTION_SERVICE\01_Projects\...\2_FIELD-WORK\DATA-DUMP\2022-08-13..."
full_path = "".join([root_path, "\\", name])
targlong = target_data['XMP:drone-dji:LRFTargetLon']
tarlat = target_data['XMP:drone-dji:LRFTargetLat']
tardist = target_data['XMP:drone-dji:LRFTargetDistance']
gimbalyaw = target_data['XMP:drone-dji:GimbalYawDegree']
shp_data.append([name, tardist, gimbalyaw, full_path, targlong, tarlat])
def output_shpdata_creator():
pt = arcpy.Point()
ptGeoms = []
for p in shp_data:
pt.X = p[4]
pt.Y = p[5]
ptGeoms.append(arcpy.PointGeometry(pt, spatial_ref))
arcpy.CopyFeatures_management(ptGeoms, output_shpdata)
arcpy.AddField_management(output_shpdata, "Name", "TEXT", 9, "", "", "refcode", "NULLABLE", "REQUIRED")
arcpy.AddField_management(output_shpdata, "Distance", "DOUBLE", 9, "", "", "refcode", "NULLABLE", "REQUIRED")
arcpy.AddField_management(output_shpdata, "YawDegree", "FLOAT", 9, "", "", "refcode", "NULLABLE", "REQUIRED")
arcpy.AddField_management(output_shpdata, "ImagePath", "TEXT", 9, "", "", "refcode", "NULLABLE", "REQUIRED")
arcpy.AddXY_management(output_shpdata)
count = 0
with arcpy.da.UpdateCursor(output_shpdata, ['Name', 'Distance', 'YawDegree', 'ImagePath']) as cursor:
for r in cursor:
r[0] = shp_data[count][0]
r[1] = shp_data[count][1]
r[2] = shp_data[count][2]
r[3] = shp_data[count][3]
cursor.updateRow(r)
count += 1
print(shp_data)
output_shpdata_creator()
Solved! Go to Solution.
Hmmm. I couldn't reproduce your error, and I have no idea what could cause it, the UpdateCursor part is OK.
General tips:
Try using InsertCursor instead:
import arcpy
from pathlib import Path
IMAGE_FOLDER = Path(r"\\...\InspectionServicesData\DRONE_INSPECTION_SERVICE\01_Projects\...\2_FIELD-WORK\DATA-DUMP\2022-08-13...\DJI_202208131356_022")
output_path = r"C:\Users\...\OneDrive...\Desktop\EXIF_TEST\"
output_name = "output_shpdata.shp"
spatial_ref = arcpy.env.outputCoordinateSystem = arcpy.SpatialReference(4326)
# create the ouput shape file and add fields
out_shp = arcpy.management.CreateFeatureclass(output_path, output_name, "POINT", spatial_reference=spatial_ref)
arcpy.management.AddField(out_shp, "Name", "TEXT")
arcpy.management.AddField(out_shp, "Distance", "DOUBLE")
arcpy.management.AddField(out_shp, "YawDegree", "FLOAT")
arcpy.management.AddField(out_shp, "ImagePath", "TEXT")
arcpy.management.AddField(out_shp, "Long", "DOUBLE")
arcpy.management.AddField(out_shp, "Lat", "DOUBLE")
# start the InsertCursor
with arcpy.da.InsertCursor(out_shp, ["SHAPE@", "Name", "Distance", "YawDegree", "ImagePath", "Long", "Lat"]) as cursor:
# loop through the image files
for image in IMAGE_FOLDER.glob("*.jpg"):
# extract exif properties
target_data = arcpy.GetImageEXIFProperties(image)[3]
long = target_data['XMP:drone-dji:LRFTargetLon']
lat = target_data['XMP:drone-dji:LRFTargetLat']
dist = target_data['XMP:drone-dji:LRFTargetDistance']
gimbalyaw = target_data['XMP:drone-dji:GimbalYawDegree']
# create the point geometry
geo_wgs84 = arcpy.PointGeometry(arcpy.Point(long, lat), arcpy.SpatialReference(4326))
geo = geo_wgs84.projectAs(spatial_ref)
# insert the values
row = [geo, image.name, dist, gimbalyaw, str(image), long, lat]
cursor.insertRow(row)
It would help if you used the code formatter (click the ..., then the </> icon to open the window to paste your code)
I would move the pt = arcpy.Point() under the for p in shp_data: loop. With it being outside of the loop, each iteration is using the same pt and it can be messing things up referencing the same object.
The shapefile gets created with fields, but do the points make it into the fc? You might have an empty shapefile and nothing for the updateRow() to update.
Hi Jeff, thanks for your reply. The points are created as well, maybe thanks to the function arcpy.AddXY_mangement. I mean, the attribute table shows the long/lat coordinates for all records, and all records/points are displayed. The other fields are empty.
It's very hard to read your script in plain text. Please post it formatted as code:
Thanks Johannes for the tip. I had no idea.
and if you need a bookmark for formatting
Code formatting ... the Community Version - Esri Community
Hmmm. I couldn't reproduce your error, and I have no idea what could cause it, the UpdateCursor part is OK.
General tips:
Try using InsertCursor instead:
import arcpy
from pathlib import Path
IMAGE_FOLDER = Path(r"\\...\InspectionServicesData\DRONE_INSPECTION_SERVICE\01_Projects\...\2_FIELD-WORK\DATA-DUMP\2022-08-13...\DJI_202208131356_022")
output_path = r"C:\Users\...\OneDrive...\Desktop\EXIF_TEST\"
output_name = "output_shpdata.shp"
spatial_ref = arcpy.env.outputCoordinateSystem = arcpy.SpatialReference(4326)
# create the ouput shape file and add fields
out_shp = arcpy.management.CreateFeatureclass(output_path, output_name, "POINT", spatial_reference=spatial_ref)
arcpy.management.AddField(out_shp, "Name", "TEXT")
arcpy.management.AddField(out_shp, "Distance", "DOUBLE")
arcpy.management.AddField(out_shp, "YawDegree", "FLOAT")
arcpy.management.AddField(out_shp, "ImagePath", "TEXT")
arcpy.management.AddField(out_shp, "Long", "DOUBLE")
arcpy.management.AddField(out_shp, "Lat", "DOUBLE")
# start the InsertCursor
with arcpy.da.InsertCursor(out_shp, ["SHAPE@", "Name", "Distance", "YawDegree", "ImagePath", "Long", "Lat"]) as cursor:
# loop through the image files
for image in IMAGE_FOLDER.glob("*.jpg"):
# extract exif properties
target_data = arcpy.GetImageEXIFProperties(image)[3]
long = target_data['XMP:drone-dji:LRFTargetLon']
lat = target_data['XMP:drone-dji:LRFTargetLat']
dist = target_data['XMP:drone-dji:LRFTargetDistance']
gimbalyaw = target_data['XMP:drone-dji:GimbalYawDegree']
# create the point geometry
geo_wgs84 = arcpy.PointGeometry(arcpy.Point(long, lat), arcpy.SpatialReference(4326))
geo = geo_wgs84.projectAs(spatial_ref)
# insert the values
row = [geo, image.name, dist, gimbalyaw, str(image), long, lat]
cursor.insertRow(row)
It works perfectly! Thanks a lot Johannes, you made my day.
Just another question. I am trying to convert the Python script into a geoprocessing script in ArcGIS Pro. Ideally, it would contain parameters for the user to fill in, like Image Folder and Destination Folder. In the Python script, the parameters would be linked to IMAGE_FOLDER, output_path and output_name. I don't know how to do that.
Also, I thought the result of the geoprocessing would be automatically added to the Contents pane, but it doesn't, even though in Options\Geoprocessing the Add output datasets to an open map is checked.
If you have some suggestions, I would appreciate. Thanks Johannes.