FURYU Tech Blog - フリュー株式会社

フリュー株式会社の開発者が技術情報を発信するブログです。

Tableauに旅行記録を可視化してみた

こちらは フリュー Advent Calendar 2024の24日目の記事になります。

こんにちは!
ピクトリンク事業部 商品技術開発部の堀江です。

本日はクリスマスイブですね🎄🎀
皆さんクリスマスっぽいことはしましたか?
私はちょうど先週クリスマスマーケットに行きました!今日・明日はケーキを食べながらのほほんとしようと思います。

今回の記事ですが、自分のスマホの写真フォルダにあるデータをTableauに可視化して、旅行の記録を作ってみたという内容になります。

アウトプットイメージ

以下の様な感じで、写真データの位置情報をもとに地図上にピンを指し、カーソルを合わせると写真・日付・タイトルが出てくるようなものを作ります。

アウトプットイメージ(feat. ひつじ君)

必要なもの

今回の作成で使ったものは以下です。

  • 写真データ(30枚くらい)
  • iPhone13 (iOS 18.1.1)
  • Google Drive
  • Google Colaboratory
  • Tableau Desktop 2022.1.5

Tableauにデータをインポートする前にデータの加工が必要だったので、Google colaboratoryでPythonを使って前処理をしました。

また、写真データは全てiPhoneのカメラロールから選んだため、手順の中には一部iOSでの操作が含まれます。

 

手順1:データの前処理

Tableauへ写真データをインポートするには、写真の詳細情報(Exif情報)をCSVへ出力して、そのCSVをデータソース化する必要があります。

そのため、まずは写真の詳細情報をPythonを使ってCSV化します。

 

1-1:HEIC→JPEG変換(任意)

iOS 11以降は、iPhoneで撮影された写真が「HEIC/HEIF」形式で保存されます。このままでも問題ないのですが、今回は処理の簡略化のために全てJPEGへ変換しました。やり方は色々あると思うので今回は省略します。

 

1-2:位置情報の埋め込み確認

CSV化の前に、写真データに必要な情報が埋め込まれているか確認します。

今回は位置情報が無かったデータが何個かあったので、手動で追加しました。

 

こちらもやり方は何通りかあると思うのですが、今回は単純にiPhoneのカメラロール上で位置情報を手動で追加しました。

 

1-3:Google Driveへ写真データをアップロード

写真データの用意ができたら、Tableauへ載せたいデータをGoogle Driveの適当な場所へアップロードします。アップロード前に写真データを何らかのサービス(Slackなど)を経由してしまうと、せっかく追加した位置情報データが消えてしまうものもあるので、出来る限り直接Drive上へアップロードします。

今回はマイドライブ/advent_calendar_2024/images/というフォルダを作り、images内へ写真をアップロードしました📷

Google Drive (images内)

1-4:ExifデータをCSV出力

ここからGoogle Colaboratory(以下:Colab)を使います。

先ほど作成したマイドライブ/advent_calendar_2024/内に新しくadvent_calendar_2024.ipynbというColabのファイルを作成しました。

Google Drive (advent_calendar_2024内)

このadvent_calendar_2024.ipynbを開き、編集していきます。

 

このColabでPythonを実行し、images内にアップロードした写真のExifデータをCSVへ出力します。

今回はChatGPT先生にコードを生成してもらいました。内容は以下の通りです。

①パッケージインストール

!pip install pyheif
!apt-get install -y libheif-examples

②CSV出力

import os
import csv
from PIL import Image
from PIL.ExifTags import TAGS, GPSTAGS

def get_exif_data(image_path):
    exif_data = {}
    try:
        image = Image.open(image_path)
        info = image._getexif()
        if info:
            for tag, value in info.items():
                decoded = TAGS.get(tag, tag)
                if decoded == "GPSInfo":
                    gps_data = {}
                    for t in value:
                        sub_decoded = GPSTAGS.get(t, t)
                        gps_data[sub_decoded] = value[t]
                    exif_data["GPSInfo"] = gps_data
                else:
                    exif_data[decoded] = value
    except Exception as e:
        print(f"Error: {e}")
    return exif_data

def extract_and_save_exif(image_folder, output_csv):
    with open(output_csv, 'w', newline='', encoding='utf-8') as csvfile:
        fieldnames = ['FileName', 'DateTime', 'Latitude', 'Longitude']
        writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
        writer.writeheader()

        for root, _, files in os.walk(image_folder):
            for file in files:
                if file.lower().endswith(('jpg', 'jpeg')):
                    image_path = os.path.join(root, file)
                    exif = get_exif_data(image_path)

                    # 日時データとGPSデータの取得
                    date_time = exif.get("DateTime", "N/A")
                    gps_info = exif.get("GPSInfo", {})
                    lat, lon = extract_gps(gps_info)

                    writer.writerow({
                        'FileName': file,
                        'DateTime': date_time,
                        'Latitude': lat,
                        'Longitude': lon
                    })

def extract_gps(gps_info):
    """GPSデータを度単位に変換"""
    def convert_to_degrees(value):
        d = float(value[0])
        m = float(value[1]) / 60.0
        s = float(value[2]) / 3600.0
        return d + m + s

    if gps_info:
        lat = convert_to_degrees(gps_info.get("GPSLatitude", [0, 0, 0]))
        lon = convert_to_degrees(gps_info.get("GPSLongitude", [0, 0, 0]))
        # 北緯・南緯、東経・西経の処理
        if gps_info.get("GPSLatitudeRef") == "S":
            lat = -lat
        if gps_info.get("GPSLongitudeRef") == "W":
            lon = -lon
        return lat, lon
    return "N/A", "N/A"

# 写真が保存されているフォルダと出力CSVを指定
extract_and_save_exif("/content/drive/My Drive/advent_calendar_2024/images/", "/content/drive/My Drive/advent_calendar_2024/output_exif_data.csv")

上記の①・②をそれぞれ順番に実行すると、先ほどのマイドライブ/advent_calendar_2024/に新しくoutput_exif_data.csvファイルが生成されているはずです。中身を確認して、以下画像のようにファイル名・日時・緯度経度の情報があればOKです。

output_exif_data.csv

手順2:Tableauにデータを可視化する

ここからいよいよTableau Desktopを操作していきます。

2-1:データのインポート

Tableau Desktopを開き、データの接続画面で先ほど作成したoutput_exif_data.csvを選択します。

Tableau Desktop - 接続画面「ファイルへ」⇒「その他」を選択

データの接続が正常に完了したら、ワークスペース画面に遷移します。

Tableau Desktop - ワークスペース

各フィールドはそれぞれ以下の様な内容です。

  • Date Time:写真の撮影日時(yyyy:MM:dd hh:mm:ss)
  • File Name:写真データのファイル名
  • Latitude:緯度
  • Longtitude:経度

2-2:データを可視化する - マップ上の調整

ここまで来たら、あとは自由にデータを可視化していきます。

マップを表示させるには、列シェルフに「Longtitude(経度)」を、行シェルフに「Latitude(緯度)」のフィールドを入れます。

その後、「File Name」フィールドをマークカードの「詳細」に入れると、地図上に丸マークが表示されます。このマークが写真を撮影した場所になります🌍✨

Tableau Desktop - マップ化

丸マークだと味気ないので、マップっぽく📌マークに変えてみます。

マークカードのタイプが「自動」になっているので、ここを「形状」に変更します。

Tableau Desktop - マークカード

タイプの変更後、マークカードの「形状」を押下すると、形状の編集画面が開かれます。ここから、好きな形状を選択します。

Tableau Desktop - 形状の編集①

今回は、ネットで拾ったPNG画像をカスタム形状に追加しています📌

カスタム形状の追加方法は調べたら沢山出てくるのでそちらを見てください。

 

ピンの画像を選択して元のワークスペースに戻ると…

Tableau Desktop - 形状変更後

なんだかそれっぽくなってきました!

 

せっかくTableauという分析ツールを使っているので、ピンの色を見ただけでいつぐらいに撮影したのかを分かりやすくしようと思います。

「Date Time」フィールドは現状文字列型になってしまっているので、日付型に変換します。「Date」というフィールドを作成し、以下のように入力します。

DATE(DATEPARSE("yyyy:MM:dd HH:mm:ss",[Date Time]))

 

そして、作成した「Date(年/月)」をマークカードの「色」へドラッグすると

Tableau Desktop - ピンの色変更①

色がついてくれました!

 

ただ、これだと色を見ただけではいつ時点のものか分からないので、日付が古いものほど黄色に、新しいものほど緑色にするようにします。

Tableau Desktop - ピンの色変更②

2-2:データを可視化する - ツールヒントの調整

マップ上に位置情報の可視化はできたので、続いてはカーソルを合わせた時に表示されるツールヒントの編集をしていきます。

現状だと、ただ単に詳細情報が文字で表示されるだけなので、以下画像のようにちょこっと調整します。

Tableau Desktop - ツールヒント修正①

このままだと、単にツールヒントに文字が表示されるだけなので、ピンの場所に合わせて写真が表示されるようにします。

場所に応じてツールヒント内の写真を動的に表示させるには、先ほどと同様にカスタム形状に写真データを追加する必要があります(このとき、カスタム形状に追加する写真ファイル名はTableauにインポートしたデータと同じファイル名にしておく)。

今回は、マイTableauレポジトリ/形状のフォルダ内に新規で「advent_calendar_2024」フォルダを作成し、そこに全写真データを追加しました。

カスタム形状を追加できたら、「File Name」フィールドの既定のプロパティにある「形状」を変更していきます。

Tableau Desktop - 既定のプロパティ変更

ここで、各「File Name」に対応する画像を紐づけられます。

Tableau Desktop - 形状の編集 [File Name]

各ファイル名に一つ一つ画像を紐づけてもいいのですが、データ項目・形状は全てファイル名順に並んでいるので、単に「パレットの割り当て」を押せば一発でいけるはず。

 

続いて、実際に写真データをツールヒントに表示させます。

そのために、まずツールヒント用のシートを新規で作成します。

この追加したシートの行フィールドに「File Name」、マークカードの「形状」に「File Name」を入れます。すると、以下画像のようになります。

Tableau Desktop - ツールヒント用シート(シート2)

画像サイズはよしなに調整してください。

 

ここまで長かったですが…、次がようやく最後の手順です!

 

最後に、元のマップのシート(シート1)に戻り、ツールヒントを編集します。

シート1のツールヒント編集画面で、以下画像のように画像を挿入したい箇所にシート2を挿入します。

Tableau Desktop - ツールヒント修正②

するとこのとおり

Tableau Desktop - マウスオーバー時

マウスオーバー時に写真が表示されるようになりました!

 

あとは、いい感じにシート2を調整したり、ファイル名に別名を付与したりすると、想定していたアウトプットイメージへ近づきます🐏💫

手順としては以上になりますが、他にも様々なデータ可視化が出来るので、ぜひ色々試してみてください。

さいごに

今回はじめてTableauでマップ×画像の可視化をやってみましたが、意外と簡単にできました。

今回は時間がなくてできませんでしたが、できるなら以下も試したいなと思います✊🏻

  • 写真がある/ないで都道府県自体に色分けする
  • 位置情報以外のデータも追加して、詳細な絞り込みや色分けができるようにする
  • 写真を数十枚だけでなく数百枚単位で追加する

最後まで読んでいただきありがとうございました!