Created
April 14, 2025 08:39
-
-
Save der-pw/9394d3324934b8000a06634adfde14d3 to your computer and use it in GitHub Desktop.
This script generates a GPX track from iPhone photos
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| """ | |
| pics2GPX.py | |
| This script generates a GPX track (output.gpx) from iPhone photos located in the 'pictures' folder. | |
| It extracts GPS coordinates and timestamps from the image metadata (EXIF) | |
| and builds a chronological track in GPX format. This file can be used for mapping, | |
| visualization, or importing into GPS applications. | |
| Requirements: | |
| - Python 3 | |
| - Install required packages with: | |
| pip install Pillow gpxpy | |
| Usage: | |
| 1. Place your iPhone photos in the “pictures” folder, which is located in the same folder like the script. | |
| 2. Run the script. py ./pics2GPX.py | |
| 3. The resulting GPX file (output.gpx) will be saved in the current directory. | |
| Created with help from my friend ChatGPT. | |
| """ | |
| import os | |
| from PIL import Image | |
| from PIL.ExifTags import TAGS, GPSTAGS | |
| import gpxpy | |
| import gpxpy.gpx | |
| from datetime import datetime | |
| # Converts GPS coordinates from EXIF format to decimal degrees | |
| def convert_to_degrees(value): | |
| def rational_to_float(r): | |
| return float(r[0]) / float(r[1]) if isinstance(r, tuple) else float(r) | |
| d = rational_to_float(value[0]) | |
| m = rational_to_float(value[1]) | |
| s = rational_to_float(value[2]) | |
| return d + (m / 60.0) + (s / 3600.0) | |
| # Extracts GPS metadata from EXIF data | |
| def get_gps_info(exif_data): | |
| gps_info = {} | |
| gps_data = exif_data.get("GPSInfo") | |
| if not gps_data: | |
| return {} | |
| for key in gps_data: | |
| decoded = GPSTAGS.get(key, key) | |
| gps_info[decoded] = gps_data[key] | |
| return gps_info | |
| # Reads EXIF data from an image | |
| def get_exif_data(image): | |
| exif = image._getexif() | |
| if not exif: | |
| return {} | |
| return { | |
| TAGS.get(tag, tag): value | |
| for tag, value in exif.items() | |
| } | |
| # Parses timestamp from EXIF metadata | |
| def parse_exif_datetime(exif): | |
| dt_str = exif.get("DateTimeOriginal") or exif.get("DateTime") | |
| if dt_str: | |
| try: | |
| return datetime.strptime(dt_str, "%Y:%m:%d %H:%M:%S") | |
| except Exception: | |
| pass | |
| return None | |
| # Main function: builds a GPX file from images in a folder | |
| def generate_gpx_from_images(image_folder): | |
| gpx = gpxpy.gpx.GPX() | |
| track = gpxpy.gpx.GPXTrack() | |
| gpx.tracks.append(track) | |
| segment = gpxpy.gpx.GPXTrackSegment() | |
| track.segments.append(segment) | |
| files = sorted(os.listdir(image_folder)) | |
| for file in files: | |
| if not file.lower().endswith((".jpg", ".jpeg")): | |
| continue | |
| path = os.path.join(image_folder, file) | |
| print(f"Checking file: {file}") | |
| try: | |
| img = Image.open(path) | |
| exif = get_exif_data(img) | |
| gps = get_gps_info(exif) | |
| if not gps: | |
| print(f"No GPS data in: {file}") | |
| continue | |
| lat = convert_to_degrees(gps["GPSLatitude"]) | |
| lon = convert_to_degrees(gps["GPSLongitude"]) | |
| if gps.get("GPSLatitudeRef") == "S": | |
| lat *= -1 | |
| if gps.get("GPSLongitudeRef") == "W": | |
| lon *= -1 | |
| ele = gps.get("GPSAltitude", None) | |
| if isinstance(ele, tuple): | |
| ele = ele[0] / ele[1] | |
| timestamp = parse_exif_datetime(exif) | |
| point = gpxpy.gpx.GPXTrackPoint( | |
| latitude=lat, | |
| longitude=lon, | |
| elevation=ele, | |
| time=timestamp | |
| ) | |
| segment.points.append(point) | |
| print(f" -> GPS: {lat:.6f}, {lon:.6f}, Time: {timestamp}") | |
| except Exception as e: | |
| print(f"Error with {file}: {e}") | |
| with open("output.gpx", "w") as f: | |
| f.write(gpx.to_xml()) | |
| print(f"\nGPX file created: output.gpx with {len(segment.points)} trackpoints") | |
| # Set the folder containing your images | |
| generate_gpx_from_images("pictures") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment