写真の位置情報から地図を表示するスクリプト[ChatGPT]

スマホで写真を撮ると、GPSを用いて位置情報も一緒に保存されます。イマドキのスマホ画像ビュアーを使えば、位置情報をもとに地図まで表示できて、どこで撮影した写真か分かってめちゃ便利。

でも写真をPCに移動すると、パッと地図を表示できなくて不便なのですよね。位置情報を取得してWebブラウザでGoogleMap表示できないかな…と思い、ChatGPTに聞いてみると

写真をドラッグ&ドロップすると、写真の位置情報データをもとに、
ブラウザでGoogleMapを開いてその場所の地図を表示する
Pythonスクリプト作ってください。

スパっとPythonスクリプト作ってくれました。

gps_to_gmap.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
写真ファイルのEXIFに含まれる位置情報(GPS)を読み取り、
Google マップでその座標をブラウザ表示します。

使い方:
  1) 画像ファイルをコマンドライン引数に渡す:
       python gps_to_gmap.py IMG_1234.JPG
       python gps_to_gmap.py IMG_*.JPG  # 複数可
  2) 引数が無い場合はファイルダイアログが開き、画像を選択できます。
  3) Windows ではこのファイルに画像をドロップしても OK(関連付け環境による)。

対応形式:
  - JPEG/JPG、PNG(まれにEXIFあり)、TIFF など Pillow がEXIFを読める形式
  - HEIC/HEIF は pillow-heif を別途インストールすれば対応可能です(任意)。

必要:
  pip install Pillow
  # HEICも扱う場合は
  pip install pillow-heif
"""

import os
import sys
import webbrowser
from typing import Optional, Tuple, List

try:
    from PIL import Image, ExifTags
except ImportError:
    print("Pillow が未インストールです。次を実行してください:  pip install Pillow")
    sys.exit(1)

# 任意: HEIC/HEIF対応 (あれば有効化)
try:
    import pillow_heif  # type: ignore
    pillow_heif.register_heif_opener()
except Exception:
    pass


def _to_float(x) -> float:
    """PillowのIFDRational等をfloatにするヘルパー。"""
    try:
        return float(x)
    except Exception:
        # 分数(tuples)の可能性
        try:
            num, den = x
            return float(num) / float(den)
        except Exception:
            return float(x)


def dms_to_deg(dms: Tuple, ref: str) -> float:
    """
    DMS形式((度, 分, 秒), Ref) を10進法に変換。
    dms は ((deg, min, sec)) または [deg, min, sec] の想定。
    ref は 'N','S','E','W'
    """
    if not dms or len(dms) != 3:
        raise ValueError("DMS配列の形式が不正です")

    deg = _to_float(dms[0])
    minute = _to_float(dms[1])
    sec = _to_float(dms[2])

    val = deg + (minute / 60.0) + (sec / 3600.0)
    if ref in ("S", "W"):
        val = -val
    return val


def extract_gps_from_exif(img_path: str) -> Optional[Tuple[float, float]]:
    """
    画像からEXIF GPS緯度・経度(10進)を取得。存在しなければNone。
    """
    try:
        img = Image.open(img_path)
    except Exception as e:
        print(f"[SKIP] 開けませんでした: {img_path} ({e})")
        return None

    exif = getattr(img, "_getexif", lambda: None)()
    if not exif:
        # PNGなどgetexif()が別仕様
        try:
            exif = img.getexif()
        except Exception:
            exif = None
    if not exif:
        return None

    # EXIFタグ名→IDの逆引き辞書を作る
    tag_map = {v: k for k, v in ExifTags.TAGS.items()}

    gps_tag_id = tag_map.get("GPSInfo")
    if gps_tag_id is None or gps_tag_id not in exif:
        return None

    gps_info_raw = exif[gps_tag_id]
    # PillowはGPSInfoをサブタグID→値の辞書で返す。サブタグ名へ解決。
    gps_tag_map = {v: k for k, v in ExifTags.GPSTAGS.items()}

    def gval(name: str, default=None):
        tid = gps_tag_map.get(name)
        return gps_info_raw.get(tid, default) if tid in gps_info_raw else default

    lat = gval("GPSLatitude")
    lat_ref = gval("GPSLatitudeRef")
    lon = gval("GPSLongitude")
    lon_ref = gval("GPSLongitudeRef")

    if not lat or not lat_ref or not lon or not lon_ref:
        return None

    try:
        lat_deg = dms_to_deg(lat, lat_ref)
        lon_deg = dms_to_deg(lon, lon_ref)
        return (lat_deg, lon_deg)
    except Exception as e:
        print(f"[WARN] GPSの解析に失敗: {img_path} ({e})")
        return None


def open_google_maps(lat: float, lon: float, zoom: int = 17) -> None:
    """
    Googleマップで座標を開く。ズームは 1-20 程度。
    """
    # クエリで十分。@lat,lon,zoom 形式でもOK
    url = f"https://www.google.com/maps?q={lat:.7f},{lon:.7f}&z={zoom}"
    webbrowser.open(url, new=2)  # new=2: 可能なら新しいタブ


def resolve_files(args: List[str]) -> List[str]:
    """引数から対象ファイルリストを作成。ディレクトリなら中の画像を拾う。"""
    exts = {".jpg", ".jpeg", ".jpe", ".tif", ".tiff", ".png", ".heic", ".heif", ".webp"}
    targets: List[str] = []
    for p in args:
        if not os.path.exists(p):
            print(f"[SKIP] 見つかりません: {p}")
            continue
        if os.path.isdir(p):
            for root, _, files in os.walk(p):
                for fn in files:
                    if os.path.splitext(fn.lower())[1] in exts:
                        targets.append(os.path.join(root, fn))
        else:
            if os.path.splitext(p.lower())[1] in exts:
                targets.append(p)
            else:
                print(f"[SKIP] 対象外拡張子: {p}")
    return targets


def pick_files_dialog() -> List[str]:
    """引数がない場合、ファイルダイアログで選択(複数可)。"""
    try:
        import tkinter as tk
        from tkinter import filedialog
    except Exception:
        return []

    root = tk.Tk()
    root.withdraw()
    filetypes = [
        ("Image files", "*.jpg *.jpeg *.jpe *.png *.tif *.tiff *.heic *.heif *.webp"),
        ("All files", "*.*"),
    ]
    paths = filedialog.askopenfilenames(title="位置情報つきの写真を選択", filetypes=filetypes)
    root.update()
    root.destroy()
    return list(paths)


def main():
    args = sys.argv[1:]
    files: List[str] = []

    if args:
        files = resolve_files(args)
    else:
        # 引数なし → ダイアログで選択
        files = pick_files_dialog()

    if not files:
        print("画像ファイルを指定してください(ドラッグ&ドロップでパス指定可能)。")
        print("例:  python gps_to_gmap.py IMG_1234.JPG  または  フォルダを指定")
        return

    opened_any = False
    for path in files:
        gps = extract_gps_from_exif(path)
        if gps is None:
            print(f"[NO GPS] EXIF位置情報がありません: {path}")
            continue
        lat, lon = gps
        print(f"[OPEN] {os.path.basename(path)} -> ({lat:.7f}, {lon:.7f})")
        open_google_maps(lat, lon, zoom=17)
        opened_any = True

    if not opened_any:
        print("指定ファイルに位置情報が見つかりませんでした。")


if __name__ == "__main__":
    main()

環境によってはドラッグ&ドロップがコピーになってしまいますね、と確認すると、ラッパーのバッチファイルも作ってくれました。

gps_to_gmap.bat

@echo off
setlocal
rem Pythonランチャー優先(全環境で安定)
set PY=py -3
set SCRIPT=%~dp0gps_to_gmap.py

if not exist "%SCRIPT%" (
  echo Script not found: %SCRIPT%
  pause
  exit /b 1
)

if "%~1"=="" (
  "%PY%" "%SCRIPT%"
  exit /b
)

"%PY%" "%SCRIPT%" %*

set PY=python だけ変更して、後は何も修正せずそのまま普通に動いた…。すごいな…。

 


ここまでやってから「写真 位置情報 地図」検索してみたところ、すでにそういうツールがいろいろあることに気が付きました…w

写真に含まれる位置情報を“Google Map”で地図表示「画像位置情報取得ツール」
https://forest.watch.impress.co.jp/docs/review/347868.html

UIもしっかり作られてて便利なので、これ使ってみます!

フォルダ開くと、一覧表示用に全写真ファイルの位置情報読みに行ってしまうのですが、これは一覧ヘッダの緯度・経度は非表示にしておけば良さそうですね。

 

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です