見出し画像

MATLAB アプリで地図表示 ~ geocoding API で住所・ランドマーク名から緯度経度変換 ~ 無料アカウントでもOK!


まえがき

以前の記事で、MATLAB では基本機能として世界地図を様々な形式で表示できることを書きました。

しかし表示位置の指定には緯度経度が必要なため、住所やランドマーク名からは変換を行う必要があります。

geocoding API を使えばそれが可能ですので、それを利用して MATLAB アプリをつくってみます。

MATLAB Online でも動きますので、無料アカウントでもOKです!

MATLAB Mobile でもアプリが動くようになると良いのですが、今のところは未対応です。(検索 UI 部分以外は普通のスクリプトでもライブスクリプトでも動作しますが)


緯度経度変換と地図表示

・geocoding API

住所・ランドマーク名から緯度経度への変換機能を持つ API は、一般的に geocoding API と呼ばれます。

種類によって、住所のみでランドマーク非対応のもあります。

もちろん Google でも用意されていて無料枠もあるのですが、基本的には有料で、クレジットカードの登録やキーの取得が必要です。

今回、無料で登録等の必要もなく、ランドマーク検索にも対応している API を見つけました。

Aobaさん個人運営とのことです!(感謝!)
X/Twitter:https://twitter.com/geocoding_jp

ただし、「検索の頻度を、10秒に1回程度に抑えてください。」という制限があるので注意しましょう。

使い方は簡単、

Url = "https://www.geocoding.jp/api/?q=<address>";

で address 部分に住所やランドマーク名を入れ、

code = webread(Url);

とすれば緯度経度などの情報が返ってきます。

<google_maps></google_maps> で囲まれた部分に検索結果が、<lat></lat> が緯度、<lng></lng>が経度になります。

・地図表示

以下に実例を示します。

>> apiUrl = "https://www.geocoding.jp/api/?q=<address>";
>> reqUrl = replace(apiUrl,"address","goryoukaku");
>> code = webread(reqUrl)
code =

    '<?xml version="1.0" encoding="UTF-8" ?>
     <result>
     <version>1.2</version>
     <address>&lt;goryoukaku&gt;</address>
     <coordinate>
     <lat>41.796925</lat>
     <lng>140.756784</lng>
     <lat_dms>41,47,48.928</lat_dms>
     <lng_dms>140,45,24.422</lng_dms>
     </coordinate>
     <open_location_code>8RH2QQW4+QP</open_location_code>
     <url>https://www.geocoding.jp/?q=%3Cgoryoukaku%3E</url>
     <needs_to_verify>no</needs_to_verify>
     <google_maps>五稜郭跡 (柳野城跡)</google_maps>
     </result>
     '

検索ワードは、英語でもローマ字でも日本語でもOKです。

結果を表示するには以下のようにします。

locationeName = cell2mat(extractBetween(code,"<google_maps>","</google_maps>"));  % 検索結果
lat = str2double(cell2mat(extractBetween(code,"<lat>","</lat>")));  % latitude (緯度)
lng = str2double(cell2mat(extractBetween(code,"<lng>","</lng>")));  % longitude(経度)
lim = 0.005;  % geolimits (表示範囲)

gax = geoaxes('Basemap','streets');  % 道路マップ表示
gax.MapCenter = [lat lng];  % 中心位置指定
geolat = [lat - lim,  lat + lim];
geolng = [lng - lim,  lng + lim];
geolimits(gax, geolat, geolng)  % 表示範囲指定
gax.Title.String = locationeName;
実行結果

・パネル表示

地図表示用座標軸 geoaxes は通常の"座標軸"への表示ができないため、アプリ内ではパネルタブを使います。

デフォルトでは周りに空白が表示されるので、枠いっぱいに地図を表示するため

InnerPosition = OuterPosition;

に設定します。

枠を設けたれば例えば

pd = 0.1;
InnerPosition = [pd pd 1-pd2 1-pd2];

等とします。

また、デフォルトのオートリサイズモードではアプリサイズを変えたときにまた空白が表示されてしまうので、

AutoResizeChildren = 'off';

とします。


アプリ作成

住所・ランドマーク名から地図を表示する方法が分かったので、MATLAB のアプリ開発環境である App Designer を使ってアプリを作っていきましょう。

App Designer の使い方は以下の記事でも解説していますので、併せてご覧ください。


アプリ作成手順

1.起動
メニューの 新規 -> アプリ
で起動し、「空のアプリ」を選択します。

アプリ名は任意ですが、今回は addressMapApp として一旦保存します。
既にある関数名と同じにしないでください。

ファイル名を変えて保存すれば、自動的にソースコードが更新されます。GUI コンポーネント名に関しても同じです。
とても便利ですね!

2.GUI の作成
左ペインのコンポーネント ライブラリから選んで追加します。

-「パネル」をドロップ
サイズをマウス等で調整後 Ctrl+D で複製(Ctrl+C -> Ctrl+V と同じ)
今回は、位置 Position を
 app.Panel :20,147,300,300
 app.Panel_2:330,147,300,300
にしました

-「編集フィールド(テキスト)」をドロップ
サイズを調整後 Ctrl+D で複製
 ラベル:Query、 Position:135,67,300,32
 ラベル:Result、Position:136,22,300,32

-「ボタン」をドロップ
 Text:Search、 Position:453,72,100,23

Panel 設定画面
Query 設定画面
Search 設定画面

3.プロパティの追加
コードビューに切替え
左ペインのコードブラウザー -> プロパティ -> ➕ とすると

Property % Description

と表示されて自動的にそこに移動するので、以下のように書き換えます。

properties (Access = private)
    gax1, gax2
    lat = 41.796925;  lng = 140.756784;  % Initial position
    lim = 0.005;  % geolimits (view range)
    apiUrl = "https://www.geocoding.jp/api/?q=<address>";

    SPCTimer;  % timer object
    waitCnt = 10;  % 10s
    searchEnable = true;
end

上半分が地図表示用初期設定で、下半分が、検索頻度を10秒に1回に抑えるためのタイマー用設定です。

4.コールバック関数・内部関数の追加

-startupFcn
アプリ起動時に実行される関数です。

右ペイン [コンポーネント ブラウザー] 階層最上位のアプリノード(addressMapApp)を右クリックし、[コールバック] 、 [StartupFcn コールバックの追加] で追加し、以下のようにします。

    function startupFcn(app)
        geoMap = 'streets';
        p1 = app.Panel;
        p1.AutoResizeChildren = 'off';
        p1.Title = geoMap;
        p1.FontName = 'Arial';
        app.gax1 = geoaxes(p1,'Basemap',geoMap);
        app.gax1.LongitudeAxis.Visible = 'off';
        app.gax1.LatitudeAxis.Visible = 'off';
        % app.gax1.InnerPosition = [pd pd 1-pd*2 1-pd*2];
        app.gax1.InnerPosition = app.gax1.OuterPosition;

        geoMap = 'satellite';
        p2 = app.Panel_2;
        p2.AutoResizeChildren = 'off';
        p2.Title = geoMap;
        p2.FontName = 'Arial';
        app.gax2 = geoaxes(p2,'Basemap',geoMap);
        app.gax2.LongitudeAxis.Visible = 'off';
        app.gax2.LatitudeAxis.Visible = 'off';
        % app.gax2.InnerPosition = [pd pd 1-pd*2 1-pd*2];
        app.gax2.InnerPosition = app.gax2.OuterPosition;

        geolat = [app.lat - app.lim,  app.lat + app.lim];
        geolng = [app.lng - app.lim,  app.lng + app.lim];

        geolimits(app.gax1, geolat, geolng)
        geolimits(app.gax2, geolat, geolng)

        % Create timer object
        app.SPCTimer = timer(...
            'ExecutionMode', 'fixedRate', ...    % Run timer repeatedly
            'Period', 1.0, ...                   % Period in second
            'BusyMode', 'queue',...              % Queue timer callbacks when busy
            'TimerFcn', @app.SPCTimerFcn);       % Specify callback function
    end

左パネルに道路マップ(streets)、左に衛星画像(satellite)を表示します。

それぞれはマウスホイール、または地図の右上にマウスを持って行くと表示されるメニューで拡大率が変えられ、マウスドラッグで位置も変えられます。

最下部は、タイマーオブジェクトの設定です。

-SPCTimerFcn
タイマーオブジェクトで設定した時間間隔で自動的に呼び出されるタイマーコールバック関数です。

左ペインのコードブラウザー -> 関数 -> ➕ で SPCTimerFcn を作成します。
以下のように書き換えてください。

    function SPCTimerFcn(app,~,~)
        app.SearchButton.Text = num2str(app.waitCnt);  % カウントダウン表示
        app.waitCnt = app.waitCnt - 1;  % カウントダウン
        if (app.waitCnt < 0)
            app.SearchButton.Text = "Search";
            app.searchEnable = true;  % 検索許可
            stop(app.SPCTimer)  % stop timer
        end
    end

最初に "Search" ボタンをカウントダウン表示に切り替えます。
この関数は1秒毎に呼び出されるので、カウントダウンして10s以上経過したら "Search" に戻し、検索許可状態にしてタイマーをストップさせます。

-SearchButtonPushed
"Search" ボタンをクリックしたときの動作を記述します。

一旦「設計ビュー」に戻り、"Search" ボタンを右クリック -> コールバック -> ~コールバックの追加、でコールバックを生成し、以下のように書き換えます。

    function SearchButtonPushed(app, event)
        if (app.searchEnable)
            reqUrl = replace(app.apiUrl,"address",app.QueryTextArea.Value);
            code = webread(reqUrl);
            result = cell2mat(extractBetween(code,"<google_maps>","</google_maps>"));
            app.ResultTextArea.Value = result;

            app.lat = str2double(cell2mat(extractBetween(code,"<lat>","</lat>")));
            app.lng = str2double(cell2mat(extractBetween(code,"<lng>","</lng>")));

            app.gax1.MapCenter = [app.lat app.lng];
            app.gax2.MapCenter = [app.lat app.lng];

            app.searchEnable = false;
            start(app.SPCTimer)  % start timer
        end
    end

まだ前回の検索から10s以内で検索許可されていない場合は、何もしないで戻ります。

検索許可状態だった場合は、"Query" のテキストを取得して検索を掛け、その結果を "Result" に表示し、地図表示を更新します。

同時に再検索を非許可にし、タイマーをスタートさせます。

-UIFigureCloseRequest
ユーザーがアプリを閉じたときの終了動作を書きます。

コードビューに切替え、左ペインの
コードブラウザー -> コールバック -> ➕
で UIFigureCloseRequestFcn を選んで、 UIFigureCloseRequest を追加します。

UIFigureCloseRequest を追加
        function UIFigureCloseRequest(app, event)
            if strcmp(app.SPCTimer.Running, 'on')  % if timer is running then stop
                stop(app.SPCTimer);
            end
            delete(app.SPCTimer);  % delete timer object

            t = timerfindall;
            if ~isempty(t)
                delete(t)
            end

            delete(app)
        end     

タイマーが走っていれば止め、(必要に応じて)他にもタイマーが走っていれば削除します。


実行結果

いくつかの実行結果を示します。

英語ランドマーク名での検索例
日本語ランドマーク名での検索例
住所での検索例
メニューからのズーム操作例


ソースコード

一応添付しておきます。

(R2023b Prerelease 版では、マウスホイールでズーム率を変えると、ズーム自体はされますがエラーが出ます。他のバージョンをお使いください。)

R2023b 正式版ではエラーが出ないことを確認しました。

2023/09/16 追記


あとがき

いかがでしたでしょうか?

MATLAB と geocoding API を組み合わせることによって、簡単に住所・ランドマーク名から地図を表示するアプリが作れました。

特に必要はなかったのですが、フト、「簡単にできるよな?」と思い立ち作ってみました。(u_u)

まあ、たまに AppDesigner 使わないと忘れちゃいますからね!


AppDesigner を使うと簡単に色々なアプリが作れますので、まだ使ったことのない方は是非お試しを!

The title image was created using Adobe Generative Fill based on this picture.

Original Image


この記事が気に入ったらサポートをしてみませんか?