import os import random import json import sqlite3 from contextlib import closing # with close sqlite import flask from PIL import Image as PIL_Image # Image file exif data for GPS coordinate extraction import PIL.ExifTags as PIL_ExifTags import geopy.distance app = flask.Flask(__name__) def decimal_coords(coords, ref): decimal_degrees = coords[0] + coords[1] / 60 + coords[2] / 3600 if ref == "S" or ref =='W' : decimal_degrees = -decimal_degrees return decimal_degrees def get_exif(image_path: os.PathLike) -> dict: # Extract EXIF metadata as a dictionary with readable tags image = PIL_Image.open(image_path) # Load the image from the specified path exif = { PIL_ExifTags.TAGS[k]: v for k, v in image._getexif().items() if k in PIL_ExifTags.TAGS } return exif def image_coordinates(exif: dict) -> (float, float): # Extract raw GPS data for latitude (north) and longitude (east) north = exif['GPSInfo'][2] # Latitude data in degrees, minutes, and seconds east = exif['GPSInfo'][4] # Longitude data in degrees, minutes, and seconds # Convert latitude and longitude from degrees-minutes-seconds to decimal format lat = ((((north[0]*60) + north[1])*60) + north[2]) / 60 / 60 lng = ((((east[0]*60) + east[1])*60) + east[2]) / 60 / 60 return float(lat),float(lng) @app.route('/favicon.ico') def favicon(): return flask.send_from_directory(os.path.join(app.root_path, 'static'), 'favicon.ico', mimetype='image/vnd.microsoft.icon') @app.route('/get_random_image.json') def get_random_image(): return random.choice(pics) @app.route("/guess.json", methods=['POST']) def guess(): data = json.loads(flask.request.data) exif = get_exif(os.path.join(pic_root_path, data['image_name'])) correct_coordinates = image_coordinates(exif) guess_coordinates = (data['coordinates']['lat'], data['coordinates']['lng']) distance = geopy.distance.geodesic(correct_coordinates, guess_coordinates) distance_meters = round(distance.meters) image_unique_id = exif['ImageUniqueID'] with closing(sqlite3.connect("guess_where.db")) as connection: with closing(connection.cursor()) as cursor: cursor.execute("""INSERT INTO guesses( picture_name, image_unique_id, correct_coordinates, guess_coordinates, distance_meters ) VALUES (?, ?, ?, ?, ?) """, ( data['image_name'], image_unique_id, f"{correct_coordinates[0]}, {correct_coordinates[1]}", f"{data['coordinates']['lat']}, {data['coordinates']['lng']}", distance_meters )) connection.commit() return {'correct_coordinates': correct_coordinates, 'distance_meters': distance_meters} @app.route("/") def game(): return flask.render_template('game.html') pic_root_path = 'src/static/pics/mine' pics_original = os.listdir(pic_root_path) pics = [] # Check if all images have GPS coordinates on application start for pic in pics_original: img_path = os.path.join(pic_root_path, pic) try: exif = get_exif(img_path) correct_coordinates = image_coordinates(exif) pics.append(pic) except Exception as ex: print(f'Error loading GPS coordinates for: {img_path}') raise ex with closing(sqlite3.connect("guess_where.db")) as connection: with closing(connection.cursor()) as cursor: rows = cursor.execute(""" CREATE TABLE IF NOT EXISTS guesses ( picture_name TEXT, image_unique_id TEXT, correct_coordinates TEXT, guess_coordinates TEXT, distance_meters INT, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP ) """).fetchall()