📷

【個人開発】24時間で消える位置情報に基づいた写真投稿サービスをつくりました

2023/12/03に公開

Intoro

お久しぶりです。2ヶ月ほど前に Minsta というデジタルスタンプラリーのサービスを公開し、Zenn に 記事 を投稿した者です。(まだ読んでない方はこちらの 記事 も是非読んでください🙏)
今回は、前回投稿した記事の中でも言っていた Minsta の仕組みをベースに別のサービスの開発に挑戦してみました。

今回作ったサービスは地図上に位置情報に紐づくコンテンツを作成したり閲覧するという点で汎用性はあると思うので、Minsta をベースに地図と位置情報を使った別のサービスを作ったりしたいなとかも考えています。

今回つくったサービスはこちら👇
https://photomap24.com

また、今回はコードも公開してみました。あまり参考になるようなレベルのものではないですがもし気になる方がいれば。。
https://github.com/wagao29/photomap24

どんなサービス?

PhotoMap24 は一言で言うと、24 時間で消える位置情報に基づいた写真投稿サービスです。写真を現在地や撮影場所に投稿することで地図上に写真が表示され、匿名のユーザーが様々な場所で撮影した写真を楽しむことができるといった内容になっています。

  • 投稿から24時間経過すると自動的に削除される
    • このコンセプトは某 ○napChat に似ている気がしますが、時間制限があることで気軽に投稿できる、よりリアルタイム性の高い写真が見れる利点があると思いました。
  • ログイン・登録不要
    • PhotoMap24 はログインや登録は排除しました。投稿は 24 時間で自動的に消えるのでユーザー側から投稿した写真を管理させる必要性があまりないのと、ユーザー目線に立った時にログインや登録のハードルは高いと感じるので今回は無くしてみました。
  • 英語ベースのサイト
    • 説明はほとんど不要なほどシンプルなサービスなので思い切って全て英語化しました。英語にすることで海外ユーザーからの投稿も期待してたりします。(今後海外向けにも何かしら宣伝を考えています)

UI

今回作った Web アプリは全てモーダル上で完結するようにしており、ルーティングすらない (URLも変わらない) 真のシングルページアプリケーション??です。
アップロード画面では写真を選択して Upload ボタンを押すだけで投稿することができ、アップロード時にオプションで写真の Exif GPS data (写真が撮影された場所の位置情報) を使用するかどうかを選択することができます。閲覧画面では左上に写真の公開期限の残り時間をリアルタイムで表示し、右下にアップロードされた位置情報の住所 (リンク) を表示させています。

アップロード画面 閲覧画面

技術スタック

技術スタックに関しては基本的には Minsta と変わらず React x Firebase のシンプルな構成になっていますが、今回は画像圧縮や Exif を解析するためのライブラリを新たに追加しました。また、地図の表示は今回も MapBox を使用し、地図のデザインは Monochrome というスタイルを使いました。

画像アップロード

画像圧縮・サムネイル生成

PhotoMap24 ではモーダルで表示するオリジナルの画像と地図上に表示するサムネイル画像の 2 種類の画像を使用しており、画像の圧縮やサムネイル画像生成はクライアント側で実行して Cloud Storage にアップロードするようにしました。また、画像の圧縮には compressorjs を使用し、サムネイルは Canvas を使って正方形に切り抜いて画像生成しています。

https://qiita.com/mo49/items/e62ddfc50762774fe2dd

Exif の使用

一般的にスマホやデジタルカメラで撮影した写真には Exif というカメラの情報や撮影日時・場所などのメタデータが付与されてます。PhotoMap24 では、この Exif から撮影場所の GPS data を取得し、今いる現在地だけでなく写真を撮影した場所にも投稿できるようにしました。Exif を取得できるライブラリはいくつか存在しますが、最初に入れた exif-js は TypeScript の型エラーがうまく解決できなかった (型定義が一部実装と合っていない気がする。。) ので、結局 blueimp-load-image というライブラリを使用しました。

ただ、ブラウザ上で Exif データを扱う上で一つ問題があり、 iOS Safari だと <input type="file"> から写真を選択してブラウザにロードすると、Exif が自動的に削除されるという仕様が存在します。この仕様は知らずに機能を作ってしまったので後から気づいて軽く落ち込んでいたのですが、よく調べたら iOS17 から 写真選択時に Exif の位置情報データを残すかどうかをユーザーが選択できるようになっていました。

最新の iOS でしか使えないという制限はあるものの、作った機能がなんとか無駄にならずに済んで良かったです。また、Exif データ自体は読み取った後にアップロード前には画像から削除するようにしています。

Firebase アーキテクチャ

Firestore

Firestore の構造は Minsta 同様に、ユーザーが作成する photo ドキュメントと単一のリストのみをもつ参照用の mapPhoto ドキュメントの 2 つから構成されています。(この構成についての詳細は こちら を見てください) また、以下の各フィールドを見るとわかると思いますが photo と mapPhoto で持たせているフィールドはほぼ同じになっています。これは今回、クライアント側から各 photo ドキュメントは取得せずに 1 つの mapPhoto ドキュメントを取得することで必要な情報が完結するようにしたためです。つまり、photo ドキュメントは mapPhoto ドキュメントの内容を更新する Cloud Functions をトリガーするだけの役割となっています。

~/
├── photos
│   ├── {photo_id}
│   │   ├── pos: 座標
│   │   ├── addr: 住所
│   │   ├── createdAt: 作成日時
│   │   └── expireAt: 有効期限
│    ...
└── mapPhotos
    └── {mapPhots_id}
        └── list[]
      	    ├── id: photo_id
	    ├── pos: 座標
	    ├── addr: 住所
            └── date: 作成日時

TTL ポリシー

本サービスでは 24 時間経過したら自動的に写真が削除される必要がありますが、この要件を満たすのに最適なのが Firestore の TTL ポリシーです。TTL ポリシーは Firestore 内のドキュメントの特定のフィールドを指定しておくことで、その時刻を経過するとドキュメントを自動的に削除してくれます。

https://firebase.google.com/docs/firestore/ttl?hl=ja

今回は photo ドキュメントの expireAt を TTL フィールドとして指定しており、この日時を経過するとドキュメントが削除されるようにしました。

Cloud Functions

今回は 2 つの Cloud Functions を実装しました。いずれも photo ドキュメントへの操作をトリガーとしており、以下のような処理フローの中で実行されます。

写真アップロード時

写真アップロード時には、クライアントから Firestore への photo ドキュメントの作成と Cloud Storage への画像のアップロードが行われます。この時 photo ドキュメントの作成を契機に photo ドキュメントへ expireAt フィールドの追加と mapPhotos ドキュメントへのデータ追加が Cloud Functions によって実行されます。

写真削除時

TTL ポリシーによって Firestore 内の期限切れの photo ドキュメントが削除されると、mapPhotos ドキュメントからのデータ削除と Cloud Storage からの画像削除が Cloud Functions によって実行されます。

さいごに

今回は以前作ったサービスの実装ベースがある状態からのスタートだったので、当たり前ですがかなりスムーズに開発を進めることができました。特に最初の環境構築だったり、開発が軌道に乗るまでの立ち上がりが早くできたのが大きかったと思います。やはり一度似たようなものをつくったことがあるという経験は大切だと改めて感じました。

最後に、PhotoMap24 はログイン・登録不要で使えるので、ぜひ日常の 1 枚をお気軽に投稿ください!🙇‍♂️

https://photomap24.com

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

Discussion