位置情報付き写真をGoogle Earthに表示する方法
📸 位置情報付き写真をGoogle Earthに表示する方法
🔍 概要
スマートフォンや位置情報付きカメラで撮影した写真(JPEG/HEICなど)を、フォルダを選択するだけで Google Earth(KMZ)に表示できるツールをご紹介します。
🛠 手順
Step 1|写真をフォルダに入れる
撮影した写真を1つのフォルダにまとめておきます。JPEG, HEIC, HEIF形式に対応しています。
Step 2|PythonスクリプトでKMZ作成
以下のPythonスクリプトを実行すると、フォルダを選ぶだけで、Google Earthに表示できるKMZが自動作成されます。
✅ 必要ライブラリ(初回のみ):
pip install pillow pillow-heif simplekml tqdm exifread
✅ スクリプトファイル例: photos_to_kmz_gui_v5.py
💻 Pythonスクリプト全文
#!/usr/bin/env python3
"""
photos_to_kmz_gui_v5.py
---------------------------------------
* ExifRead → Pillow fallback で GPS を抽出
* HEIC / HEIF 対応 (pillow-heif)
* リサイズ(長辺 maxsize px、デフォルト 1600)
* tqdm 進捗バー付き
依存:
pip install pillow pillow-heif simplekml tqdm exifread
"""
import sys, zipfile
from pathlib import Path
import tkinter as tk
from tkinter import filedialog, messagebox
import exifread
from PIL import Image, ExifTags
try:
import pillow_heif
pillow_heif.register_heif_opener()
except ImportError:
pass
import simplekml
from tqdm import tqdm
# ---------- GPS extraction ----------
def gps_from_exifread(path):
try:
with open(path, 'rb') as f:
tags = exifread.process_file(f, details=False, stop_tag='GPS GPSLongitude')
if 'GPS GPSLatitude' not in tags or 'GPS GPSLongitude' not in tags:
return None, None
def conv(tag):
vals = tag.values
d = vals[0].num / vals[0].den
m = vals[1].num / vals[1].den
s = vals[2].num / vals[2].den
return d + m/60 + s/3600
lat = conv(tags['GPS GPSLatitude'])
lon = conv(tags['GPS GPSLongitude'])
if tags.get('GPS GPSLatitudeRef', 'N').values != 'N':
lat = -lat
if tags.get('GPS GPSLongitudeRef', 'E').values != 'E':
lon = -lon
return lat, lon
except Exception:
return None, None
def gps_from_pillow(path):
try:
with Image.open(path) as im:
exif = im._getexif()
if not exif:
return None, None
gps = {}
for k, v in exif.items():
if ExifTags.TAGS.get(k) == 'GPSInfo':
gps = {ExifTags.GPSTAGS.get(t, t): v[t] for t in v}
break
if not gps:
return None, None
def r(x):
return x.numerator / x.denominator if hasattr(x,'numerator') else x[0] / x[1]
def dms(d, ref):
return (-1 if ref in ('S','W') else 1) * (r(d[0]) + r(d[1])/60 + r(d[2])/3600)
lat = dms(gps['GPSLatitude'], gps.get('GPSLatitudeRef','N'))
lon = dms(gps['GPSLongitude'], gps.get('GPSLongitudeRef','E'))
return lat, lon
except Exception:
return None, None
def get_gps(path):
lat, lon = gps_from_exifread(path)
if lat is None:
lat, lon = gps_from_pillow(path)
return lat, lon
# ---------- GUI ----------
def select_folder():
root = tk.Tk()
root.withdraw()
folder = filedialog.askdirectory(title='写真フォルダを選択してください')
root.destroy()
return Path(folder) if folder else None
# ---------- Resize ----------
def save_resized(src: Path, dst: Path, maxsize=1600, quality=80):
try:
with Image.open(src) as img:
img.thumbnail((maxsize,maxsize), Image.LANCZOS)
if img.mode in ('RGBA','P'):
img = img.convert('RGB')
img.save(dst, 'JPEG', quality=quality, optimize=True)
return True
except Exception as e:
print('[WARN] resize failed', src, e)
return False
# ---------- Main ----------
def main():
import argparse, shutil
p = argparse.ArgumentParser(description='Geotagged photos → KMZ v5')
p.add_argument('folder', nargs='?', help='写真フォルダ')
p.add_argument('-o','--output', help='KMZ 出力パス')
p.add_argument('--maxsize', type=int, default=1600, help='長辺px上限')
p.add_argument('--quality', type=int, default=80, help='JPEG品質')
args = p.parse_args()
folder = Path(args.folder).resolve() if args.folder else select_folder()
if not folder or not folder.is_dir():
print('フォルダが選択されませんでした'); sys.exit(0)
out = Path(args.output).resolve() if args.output else folder.with_suffix('.kmz')
imgs = [p for p in folder.rglob('*') if p.suffix.lower() in ('.jpg','.jpeg','.heic','.heif')]
if not imgs:
messagebox.showinfo('結果','画像ファイルが見つかりませんでした'); sys.exit(0)
tmp = Path('_kmz_tmp'); files = tmp/'files'; files.mkdir(parents=True, exist_ok=True)
kml = simplekml.Kml(); added = 0
for img in tqdm(imgs, desc='Processing', unit='img'):
lat, lon = get_gps(img)
if lat is None:
continue
dst = files / (img.stem + '.jpg') # すべて JPG として保存
if not dst.exists():
save_resized(img, dst, args.maxsize, args.quality)
p = kml.newpoint(name=img.name, coords=[(lon, lat)])
p.description = f"<img src='files/{dst.name}' width='600'/>"
added += 1
if added == 0:
messagebox.showinfo('結果','ジオタグ付き画像が見つかりませんでした'); sys.exit(0)
tmp_kml = tmp/'doc.kml'; kml.save(str(tmp_kml))
with zipfile.ZipFile(out,'w', zipfile.ZIP_DEFLATED) as z:
z.write(tmp_kml,'doc.kml')
for f in files.iterdir():
z.write(f, f'files/{f.name}')
shutil.rmtree(tmp, ignore_errors=True)
messagebox.showinfo('完了', f'{out} を作成しました\\n登録ポイント: {added} 枚')
if __name__ == '__main__':
main()
📦 補足情報
- 対応画像:
.jpg
/.jpeg
/.heic
/.heif
- GPSが埋め込まれていない画像はスキップされます
- KMZには画像がリサイズされて埋め込まれます(デフォルト:長辺1600px)
現場写真の地理情報可視化に、ぜひご活用ください。
コメント
コメントを投稿