どマイナーな場所の最短経路を探索する(MATLAB＋Excel)

# はじめに
時間やコストを最小限にして行きたい場所がある！
けれどもgoogleマップや、Yahoo!乗換案内では取り扱いがない・・・
そんな困りごとも簡単に解決できます、<b>そうMATLABならね。</b>

と茶番はさておき、MATLABの便利な関数の一つに[最短経路探索](https://jp.mathworks.com/help/matlab/ref/graph.shortestpath.html)があります。
面白そう ＆ MATLAB布教に役立ちそうなのでMATLAB芸を考えました。

〇想定する読者
　MATLAB初心者、MATLAB芸人を志す人

〇この記事で覚えて頂きたいこと
・shortestpath関数マジ便利
・Figureの機能「データヒント」の存在
・[table型](https://qiita.com/kouichi-c-nakamura/items/6c6cd52ad62a0bf7ffb5)の便利さおよび扱い方
・<b>ExcelファイルをMATLAB table型に読み込むためのコツ(地味に重要)</b>

なおMATLABのバージョンは2015b以降で、<b>ツールボックスは一切不要です。</b>
また下記、解説の都合上コードが途切れ途切れなので、１つのファイルで見たいかたは[GitHub](https://github.com/motorcontrolman/matlab/blob/master/ion_path/ion_path.m)を参照下さい。
# ①座標データの作成
まずはMATLABに画像データを読み込みます。
今回は[イオン東根店](https://trip-yamagata-japan.com/barrier-free/detail/index/365)のマップを作ります。
岩手に住んでる訳ではないですが、丁度いい感じのマップだったので。。。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/5d58eebd-2b65-b235-c6e0-f4ee8cbb8b35.png)
東根駅側からイオンに入り、見切り品の刺身を買ってくる最短経路とか気になりますよね？気になれ。（ちなみに過去には、某夢の国を題材に同様のMATLAB芸をEXPOでやった人がいました）


```matlab
img = imread('https://trip-yamagata-japan.com/barrier-free/images/higashine12-0-1.jpg');
f = figure;
imshow(img);
f.OuterPosition = [100 100 700 600];%画像が小さいので大きめに表示、なくてもOK

hold on;
axis on;
```


次に、座標データを作成します。
どうやってやるかというと、<b>Figure上の座標を読み、それをExcelに写経します。</b>
この作業はMATLABだけでも出来ますが、MATLAB初心者であるなら積極的にExcelを使うべきと思います。

Figure上の座標を読むのは、<b>「データヒント」</b>をクリックした上で所望の場所をクリックすることで可能です。
「データヒント」の表示場所はバージョンにより異なる場合があるので注意下さい。
最近のバージョンなら画像の右上にカーソルを持っていくと浮かび上がると思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/d8cf7346-3c51-b604-c156-ff706b00341b.png)
[X,Y]をエクセルに写経しましょう。
なおシフト押しながら所望の場所をクリックすれば複数の座標が同時に見れます。

座標のエクセルファイルについては、下記のルールで作成しましょう。
<b>下記のルールは「table型」で読ませる際に推奨するものです。</b>
・ファイル名は英数字のみにする。今回は「address.xlsx」
・１行目はデータ名で、ここも英数字のみにする。今回は「No x y」
・２行目以降はデータ
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/3bc47192-da52-f578-1a11-efd0cd522fb4.png" width=300>


#### 参考までに
エクセル写経を拒否したい方は右クリック→カーソルデータを~　でMATLABに直接読み込めます。
ただしこの場合、読み込んだ場合のデータの型が構造体で、後処理が面倒になる点注意下さい。
(struct2table関数を使うとよさげ)
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/a472ffa2-ad16-6883-4184-12ec341b65ca.png" width=400>


# ②パスデータの作成
<b>MATLAB上にマップ画像と場所ナンバーを重ねて表示した上で、パスをExcelに記入します。</b>
前準備として、①で作成したaddress.xlsxをMATLABに読み込みます。
これにはMATLAB上でaddress.xlsxをダブルクリックして
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/398f6867-becc-11b3-5c8c-115ef956d225.png" width = 400>

「選択のインポート」をクリックします。出力タイプがtableとなっている点に注目しましょう。
<b>①に示したルールでExcelファイルを作ったおかげで、MATLABに読み込んだ後の定数名が自動設定されます。超便利。</b>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/72cd647a-eceb-9c08-a680-2ad9f7b47e90.png)


場所ナンバーの表示は、下記コマンドで表示させます。
暫定のパスを作成することで作業を楽にします。

```matlab
G = graph(address.No(1),address.No(end)); %暫定のパスの作成
p = plot(G,'XData',address.x,'YData',address.y);
```
繰り返しになりますが、①に示したルールでExcelファイルを作ったおかげで場所ナンバーをaddress.Noで、x,y座標をaddress.x,address.yで呼ぶことが出来ます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/294e4c28-190b-181c-4d20-d41ea4f855a1.png)

上記の1（ナンバー最小値）から42（最大値）のパスは暫定的なもので、本来不要ですが
`p = plot(G,'XData',address.x,'YData',address.y);`
のコマンドは、グラフGの中に最小値および最大値を含むパスが少なくとも１回ないとエラーが出るので暫定パスを設定することでこれを回避しています。

パスのエクセルファイルは下記のルールで作成します。
エラー回避の目的で、上記の暫定パス（1から42）も書いておくと後が楽です。
・ファイル名は「path.xlsx」
・１行目はデータ名「A B」
・２行目以降はデータ
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/c3f66c72-644f-9c17-8c33-d1c4868efa1c.png" width = "200">

ある程度作りこんだら、MATLABにインポートしてパスを表示させましょう。

```matlab
G = graph(path.A , path.B); %暫定のパスの作成
p = plot(G,'XData',address.x,'YData',address.y);
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/b8627707-e87e-823a-7fe6-e35806939981.png)

暫定パスである1-42が邪魔です。また37-6,6-1が不足、8-38が余分です。
ここまでくるとExcel編集面倒なので、MATLAB上で直接修正します。
色々な方法がありますが、データ数がそれほど多くないなら手作業でいいでしょう。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/1eb7688e-8547-7bf9-b923-aa04e265945e.png" width = 400>
graph型はよく分からん！という場合はtable型のpathを修正、再度
`G = graph(path.A , path.B); `
でgraph型を作り直してもよいでしょう。

# ③２点間の距離を計算、最短経路を探索

```matlab
dx = address.x(path.A) - address.x(path.B);
dy = address.y(path.A) - address.y(path.B);
Dist = hypot(dx,dy);
G = graph(path.A, path.B,Dist); %距離を重みに設定
p = plot(G,'XData',address.x,'YData',address.y);

path1 = shortestpath(G,29,16); %ダイクストラ法による最短経路探索
highlight(p,path1,'EdgeColor','g','LineWidth',2); %ハイライト表示
```

hypotは２乗ルートの計算です。ベクトルを引数に出来るので超便利。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/27e02d2a-2e13-f122-2308-33936d30a782.png)

なんだか無難な結果になってしまいましたが、今回はここまで。
「通路に人がいる場所は通りにくいので極力避ける」等の制約を加えると楽しそうです。

# 参考
この最短経路探索は、ダイクストラ法によるものです。
ダイクストラ法の解説は、ヨビノリさんの動画が超分かり易いので皆見ましょう。
https://www.youtube.com/watch?v=X1AsMlJdiok