Загрузка данных


import sys
import os
from PIL import Image
import piexif
from fractions import Fraction


def dms_to_decimal(gps_data, gps_ref):
    """
    Преобразует координаты из формата градусы/минуты/секунды (рациональные числа)
    в десятичные градусы.
    gps_data: кортеж из трёх рациональных чисел ((deg_num, deg_den), (min_num, min_den), (sec_num, sec_den))
    gps_ref: символ направления ('N', 'S', 'E', 'W')
    """
    if not gps_data:
        return None

    # Извлекаем градусы, минуты, секунды как float
    deg = gps_data[0][0] / gps_data[0][1] if gps_data[0][1] != 0 else 0
    minutes = gps_data[1][0] / gps_data[1][1] if gps_data[1][1] != 0 else 0
    seconds = gps_data[2][0] / gps_data[2][1] if gps_data[2][1] != 0 else 0

    decimal = deg + minutes / 60.0 + seconds / 3600.0

    # Корректируем знак в зависимости от полушария
    if gps_ref in ['S', 'W']:
        decimal = -decimal

    return decimal


def format_value(value):
    """
    Преобразует значение тега в читаемую строку.
    Обрабатывает bytes, рациональные числа, списки и т.д.
    """
    if isinstance(value, bytes):
        try:
            return value.decode('utf-8', errors='replace')
        except:
            return str(value)
    elif isinstance(value, tuple) and all(isinstance(x, tuple) for x in value):
        # Предполагаем рациональные числа
        formatted = []
        for num, den in value:
            if den == 0:
                formatted.append('0')
            else:
                # Сокращаем дробь, если нужно, или выводим как десятичную
                frac = Fraction(num, den)
                if frac.denominator == 1:
                    formatted.append(str(frac.numerator))
                else:
                    formatted.append(f"{num}/{den} ({num/den:.3f})")
        return ', '.join(formatted)
    elif isinstance(value, tuple):
        # Простой кортеж (например, версия)
        return ', '.join(str(x) for x in value)
    else:
        return str(value)


def extract_metadata(image_path):
    """
    Извлекает и выводит все метаданные изображения.
    Возвращает координаты (lat, lon) в десятичных градусах, если они есть.
    """
    if not os.path.isfile(image_path):
        print(f"Ошибка: файл '{image_path}' не найден.")
        return None

    # 1. Основная информация через Pillow
    try:
        img = Image.open(image_path)
        print("\n=== Основная информация ===")
        print(f"Формат: {img.format}")
        print(f"Размер: {img.size[0]} x {img.size[1]} пикселей")
        print(f"Режим: {img.mode}")
        if hasattr(img, 'info') and img.info:
            print("\n--- Дополнительные поля из img.info ---")
            for key, value in img.info.items():
                if key != 'exif':  # EXIF обработаем отдельно
                    print(f"{key}: {format_value(value)}")
    except Exception as e:
        print(f"Не удалось открыть изображение: {e}")
        return None

    # 2. EXIF данные
    print("\n=== EXIF метаданные ===")
    lat = lon = None

    try:
        exif_dict = piexif.load(image_path)
    except Exception as e:
        print(f"Не удалось загрузить EXIF: {e}")
        exif_dict = {}

    # Словари с именами тегов для каждого IFD
    ifd_names = {
        '0th': 'Изображение (0th)',
        'Exif': 'Exif',
        'GPS': 'GPS',
        'Interop': 'Interoperability',
        '1st': 'Thumbnail (1st)'
    }

    for ifd_name, ifd_data in exif_dict.items():
        if ifd_name == 'thumbnail':
            if ifd_data:
                print(f"\n--- Thumbnail ---")
                print(f"Размер thumbnail: {len(ifd_data)} байт")
            continue

        if not ifd_data:
            continue

        print(f"\n--- {ifd_names.get(ifd_name, ifd_name)} ---")
        # Получаем словарь тегов для этого IFD
        tags = piexif.TAGS.get(ifd_name, {})
        for tag_id, value in ifd_data.items():
            tag_name = tags.get(tag_id, {}).get('name', f'Unknown (0x{tag_id:04X})')
            # Преобразуем значение в читаемый вид
            readable_value = format_value(value)
            print(f"{tag_name} ({tag_id}): {readable_value}")

            # Извлекаем GPS координаты, если они есть
            if ifd_name == 'GPS':
                if tag_id == piexif.GPSIFD.GPSLatitude:
                    lat_data = value
                elif tag_id == piexif.GPSIFD.GPSLatitudeRef:
                    lat_ref = value.decode() if isinstance(value, bytes) else value
                elif tag_id == piexif.GPSIFD.GPSLongitude:
                    lon_data = value
                elif tag_id == piexif.GPSIFD.GPSLongitudeRef:
                    lon_ref = value.decode() if isinstance(value, bytes) else value

    # 3. Преобразуем GPS координаты в десятичный формат
    if 'lat_data' in locals() and 'lat_ref' in locals() and 'lon_data' in locals() and 'lon_ref' in locals():
        lat = dms_to_decimal(lat_data, lat_ref)
        lon = dms_to_decimal(lon_data, lon_ref)
        print(f"\n--- GPS координаты (десятичные) ---")
        print(f"Широта: {lat:.6f}, Долгота: {lon:.6f}")
    else:
        print("\nGPS координаты не найдены.")

    return lat, lon


def generate_google_maps_link(lat, lon):
    """Генерирует ссылку на Google Maps для заданных координат."""
    if lat is not None and lon is not None:
        return f"https://www.google.com/maps?q={lat},{lon}"
    else:
        return None


if __name__ == "__main__":
    # Получаем имя файла из аргументов командной строки или запрашиваем ввод
    if len(sys.argv) > 1:
        filename = sys.argv[1]
    else:
        filename = input("Введите путь к изображению: ").strip()

    coords = extract_metadata(filename)

    if coords and coords[0] is not None and coords[1] is not None:
        link = generate_google_maps_link(coords[0], coords[1])
        print(f"\n=== Ссылка на Google Maps ===\n{link}")
    else:
        print("\nНе удалось определить GPS-координаты для построения ссылки.")