【旅の】NextcloudのMaps使ってみた【思い出】

Docker



最近Nextcloudでいろいろアプリを入れていて、
便利な、使えるアプリがないか探すのが楽しくなっています。

今回はそのなかでMapsというアプリが良かったので、紹介します。

Mapsとはどんなアプリか

簡単に言うと、Google Mapsみたいな世界地図に、Nextcloudに置いてある画像ファイルの位置をマッピングするアプリです。

自分の環境ではこんな感じ。
Nextcloud上に旅行の写真を保存していて、昔撮った写真を思い返すことができます。
なかなか楽しい。

動画はややカクカクしていますが、実動作はスムーズです。

日本は住んでいるので当然として、昔ドイツに駐在していたので欧州、妻が香港人なので香港、がそれぞれ写真枚数多くマップされています。

↑の動画で見てわかる通り、アフリカ南西部に75枚、南アメリカに3枚、それぞれ写真があり。

自分はアフリカも南アメリカも行ったことがないので、当然これらの写真は間違いなのですが、調べた限りでは画像ファイルによって以下の現象がありそうでした。

画像ファイルの緯度経度が(0,0)
アフリカ南西部の海が(0,0)なのでそこに配置される。
 スマホで撮ったときに誤った値が保存された
 GPS通信がうまくいかなかった
 画像アプリで編集時に緯度経度情報が消えた
などいろいろ原因がありそうだが、どれもNextcloud Mapsではどうにもならない。

画像ファイルの緯度経度が(NaN,NaN)
Nextcloud MapsでDivisionByZeroErrorが発生、cron.phpが強制終了した。
少しソースコードを手直しして対応した。

画像ファイルの緯度経度が実際の値と異なる
自分の場合はアフリカ大陸に何枚かマップされた。
スマホで撮ったときに誤った値が保存されたと思われる。
Nextcloud Mapsではどうにもならない。

導入手順

まず他のアプリ同様管理者でログインしAppsを選択。



Multimediaに移動すると、Mapsですぐ見つかると思います。
↓の画像ではインストール済なので最上段に表示されていますが、未インストール時はアルファベット順なので、下に表示されているはず。



以上でMapsの導入は終了です。

写真をMapsに読み込ませる

Mapsを入れただけですと、Nextcloudにある画像ファイルとのリンク付けは行われませんので、手動で実施する必要があります。

以下のコマンドを実行し、次回cron.php実行時にMapsへのリンク付を行うように指示します。
自分はdocker-compose環境なのでsudo docker-compose execでやっています。ご自身の環境に合わせて読み替えてください。

# 全ユーザーに対して行う場合
sudo docker-compose exec --user www-data nextcloud php occ maps:scan-photos
# 特定のユーザーに対して行う場合
sudo docker-compose exec --user www-data nextcloud php occ maps:scan-photos $(userid)



次にcron.phpを実行させます。
すでに設定済みならしばらく放置しておくとそのうちMapsの世界地図に写真が追加されます。

自分はcron.php実行が未設定だったので、手で実行させました。
これを機にcron.php環境を整えるならrcdailey/nextcloud-cronjobというDockerイメージが使いやすかったです。

sudo docker-compose exec --user www-data nextcloud php /var/www/html/cron.php



私の環境ですと自分と妻とで合計40000枚くらい画像があり、またcron.phpは実行時間が14分くらいになると自動的に一度終了する動作仕様のようだったので、何度も実行する必要がありました。
以下のようにワンライナーで対応。

while true; do sudo docker-compose exec --user www-data nextcloud php /var/www/html/cron.php; sleep 1; done



また、PHPの動作メモリが128MBでは足りず、一時的に2048MBに増やしたら安定しました。
 512Mでもダメでした。

ちなみに出ていたエラーはこちら。

PHP Fatal error: Allowed memory size of 536870912 bytes exhausted (tried to allocate 4606288 bytes) in /var/www/html/lib/private/Files/Storage/Local.php on line 277



さらに先ほど書いたとおり、緯度経度が(NaN,NaN)の画像があると以下のようなエラーが出て強制終了してしまいます。

DivisionByZeroError: Division by zero in /var/www/html/apps/maps/lib/Service/PhotofilesService.php:414
Stack trace:
#0 /var/www/html/apps/maps/lib/Service/PhotofilesService.php(264): OCA\Maps\Service\PhotofilesService->getExif()
#1 /var/www/html/apps/maps/lib/BackgroundJob/AddPhotoJob.php(56): OCA\Maps\Service\PhotofilesService->addPhotoNow()
#2 /var/www/html/lib/public/BackgroundJob/Job.php(79): OCA\Maps\BackgroundJob\AddPhotoJob->run()
#3 /var/www/html/lib/public/BackgroundJob/QueuedJob.php(47): OCP\BackgroundJob\Job->execute()
#4 /var/www/html/cron.php(126): OCP\BackgroundJob\QueuedJob->execute()
#5 {main}



そこで暫定的に以下の通りソースコードを修正して対応。
apps/maps/lib/Service/PhotofilesService.php
 最新版だとやらなくてもいいかも。

# private function getExif($file) 内部

# 変更前
foreach ($gps as $key => $value){
    $pos = strpos($value, '/');
    if ($pos !== false){
        $temp = explode('/',$value);
        $gps[$key] = $temp[0] / $temp[1];
    }
}
$file_object = new \stdClass();
//calculate the decimal degree
$file_object->lat = $LatM * ($gps['LatDegree'] + ($gps['LatMinute'] / 60) + ($gps['LatgSeconds'] / 3600));
$file_object->lng = $LongM * ($gps['LongDegree'] + ($gps['LongMinute'] / 60) + ($gps['LongSeconds'] / 3600));
$has_info = true;

# 変更後
$NaNFound = false;
foreach ($gps as $key => $value){
    $pos = strpos($value, '/');
    if ($pos !== false){
        $temp = explode('/',$value);
        if('0' !== $temp[1]) {
            $gps[$key] = $temp[0] / $temp[1];
        } else {
            $NaNFound = true;
            break;
        }
    }
}
$file_object = new \stdClass();
if (false == $NaNFound) {
    //calculate the decimal degree
    $file_object->lat = $LatM * ($gps['LatDegree'] + ($gps['LatMinute'] / 60) + ($gps['LatgSeconds'] / 3600));
    $file_object->lng = $LongM * ($gps['LongDegree'] + ($gps['LongMinute'] / 60) + ($gps['LongSeconds'] / 3600));
    $has_info = true;
}

簡単に解説しておくと、緯度経度が(NaN,NaN)の場合は$gps[‘LatDegree’]及び$gps[‘LongDegree’]が’0/0’という文字列になっていました。

そのため、$gps[$key] = $temp[0] / $temp[1];で0除算となり例外が発生していました。

そこで、緯度経度が(NaN,NaN)なら位置情報はnullとしました。
少し手抜きにも見えますが、例外で落ちるよりは良かろうと。

また、↑の修正によってこちらも修正が必要でした。
apps/maps/lib/Service/PhotofilesService.php

# private function insertPhoto($photo, $userId, $exif) 内部

# 変更前
$photoEntity->setLat(
    is_numeric($exif->lat)&&!is_nan($exif->lat) ? $exif->lat : null
);
$photoEntity->setLng(
    is_numeric($exif->lng)&&!is_nan($exif->lng) ? $exif->lng : null
);

# 変更後
$photoEntity->setLat(
    isset($exif->lat)&&is_numeric($exif->lat)&&!is_nan($exif->lat) ? $exif->lat : null
);
$photoEntity->setLng(
    isset($exif->lng)&&is_numeric($exif->lng)&&!is_nan($exif->lng) ? $exif->lng : null
);

先の修正で緯度と経度がnullになることがあるので、isset()でのチェックを追加しました。

以上を行うと、以下のようにMapsアプリで写真が地図にマッピングされると思います。
 妻のアカウントで実施👩



写真の枚数が多いと地図上に写真が表示されるまで少し時間がかかります。
htopで見ると、php-fpmとmysqldがバリバリ動いていて、ラズパイだと仕方ないかな…と。



ちなみにMaps自体は写真の他にデバイス位置のトラッキング機能もあるみたいです。
もし各地に旅行に行くようでしたら、連携を有効にしておくとおもしろいかも。

終わりに

いかがでしたか。

旅行などで外出先の写真をたくさんお持ちの方は、きっと楽しめると思いますよ!

Comments

タイトルとURLをコピーしました