0
0

CordovaアプリでGoogleMapからロケーション情報を共有する

Last updated at Posted at 2024-08-16

散歩ルート計画アプリを作った の作成時に習得したテクニックです。

GoogleMapでスポットをクリックすると以下のような詳細ダイアログが表示されます。
そのダイアログの右上に共有ボタンがあり、これを選択すると他のアプリにこの情報が共有されます。

image.png

この共有機能をCordovaアプリで実現してみます。

共有内容

共有ボタンをクリックすると、以下のような共有先アプリの選択ダイアログが表示されます。
今回はこの選択先アプリに含まれるようにします。

image.png

この共有機能は一般的な機能で、いろんなアプリで使えます。以下は、乗換案内アプリでの共有です。
この経路を共有・登録を選択します。

image.png

その他 を選択します。

image.png

こんな感じで共有先アプリが選択できます。

image.png

共有を受け取る

共有を受け取るには、以下のプラグインを使います。

以下のように、GitHubから直接インストールしてください。

> cordova plugin add https://github.com/napolitano/cordova-plugin-intent

次にconfig.xmlに以下を追記します。

    <platform name="android">
      <config-file target="AndroidManifest.xml" parent="/manifest/application/activity">
          <intent-filter>
              <action android:name="android.intent.action.SEND" />
              <category android:name="android.intent.category.DEFAULT" />
              <data android:mimeType="text/plain" />
          </intent-filter>
      </config-file>
      <preference name="AndroidLaunchMode" value="singleTask"/>
    </platform>

GoogleMap等からの共有でアプリが立ち上がる時に別インスタンスが立ち上がるのを避けるため、singleTaskの指定もしておいた方がよいです。

一緒に、androidのnamespaceも追記します。

<widget id="jp.or.myhome.sample.PlanningClient" version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0" xmlns:android="http://schemas.android.com/apk/res/android">

あとは、Javascriptで以下を実行しておきます。

        window.plugins.intent.setNewIntentHandler(this.onNewIntent);

受け取りたい情報は、 intent.extras["android.intent.extra.TEXT"] にあります。

        onNewIntent: async function (intent) {
            console.log("onNewIntent:", intent);
            if (!intent.extras || !intent.extras["android.intent.extra.TEXT"])
                return;

以下の条件に合致するとき、GoogleMapからの共有であることがわかるようです。

if (intent.extras["androidx.core.app.EXTRA_CALLING_PACKAGE"] == "com.google.android.apps.maps")

intent.extras["android.intent.extra.TITLE"] というのも付けてくれるようです。
何を渡してくれるかは共有元のアプリケーションに依存します。

GoogleMapが渡してくれる情報と解析

GoogleMapから共有される情報は以下のようなURLです。

https://maps.app.goo.gl/WiWYny4XvgKU6nKH6

これだけではよくわからないのですが、さらに処理を進めます。
このURLにアクセスすると別のURLにリダイレクトされます。そのURLに少し情報が含まれてきます。

https://www.google.co.jp/maps/place/鬼怒川ライン下り/@36.8256636,139.7118355,16.92z/data=!4m6!3m5!1s0x601f9f37204fa7cb:0x45a454a53ed1f670!8m2!3d36.8257502!4d139.7155352!16s/g/1ttpg_bn?coh=219816&entry=tts&g_ep=EgoyMDI0MDgxMi4wKgBIAVAD

緯度経度とスポット名称があるようです。
この緯度経度は少し荒いため、正確な位置情報から少しずれてしまうようです。
そこで、この緯度経度の近辺100m四方で、スポット名称を検索してみます。
検索には、GeoCoderを使います。

GeoCoderで検索し、スポットを見つけるとPlace IDが返ってきますので、Place APIを使ってPlaceIDから詳細なスポット情報を取得します。

実装編

リダイレクトURLの取得

リダイレクトURLを取得するには、クライアント側のJavascriptではできず、サーバ側のNode.jsの力を借りることとしました。redirect: "manual" がポイントです。

	if( event.path == '/planning-parse-redirect') {
		var response = await fetch( body.url, {redirect: "manual" });
		if( response.status == 301 || response.status == 302 ){
			var url = new URL(response.headers.get('location'), response.url);
			return new Response({ location: url.toString(), status: response.status });
		}else{
			throw new Error("not redirect");
		}
	}else

緯度経路・スポット名の抽出

以下のようなURLから、緯度経路・スポット名を抽出します。

https://www.google.co.jp/maps/place/鬼怒川ライン下り/@36.8256636,139.7118355,16.92z/data=!4m6!3m5!1s0x601f9f37204fa7cb:0x45a454a53ed1f670!8m2!3d36.8257502!4d139.7155352!16s/g/1ttpg_bn?coh=219816&entry=tts&g_ep=EgoyMDI0MDgxMi4wKgBIAVAD

function extractPlaceData(url) {
    const namePattern = /\/maps\/place\/([^\/]+)\//;
    const coordinatePattern = /@([-+\d.]+),([-+\d.]+)/;

    const nameMatch = url.match(namePattern);
    const name = nameMatch ? decodeURIComponent(nameMatch[1].replace(/\+/g, ' ')) : null;

    const coordinateMatch = url.match(coordinatePattern);
    const latitude = coordinateMatch ? parseFloat(coordinateMatch[1]) : null;
    const longitude = coordinateMatch ? parseFloat(coordinateMatch[2]) : null;

    return {
        name: name,
        lat: latitude,
        lng: longitude,
    };
}

PlaceIDの取得

指定した緯度経路の周辺100m四方の範囲を以下のように生成します。

function createBounds(center, radius) {
    const latPerMeter = 1 / 110574;
    const lngPerMeter = 1 / (111320 * Math.cos(center.lat() * Math.PI / 180));

    const latOffset = radius * latPerMeter;
    const lngOffset = radius * lngPerMeter;

    const northEast = new google.maps.LatLng(center.lat() + latOffset, center.lng() + lngOffset);
    const southWest = new google.maps.LatLng(center.lat() - latOffset, center.lng() - lngOffset);

    return new google.maps.LatLngBounds(southWest, northEast);
}

あとは、以下のようにして呼び出せばよいです。

    return new Promise((resolve, reject) => {
        geocoder.geocode({ address: result.name, bounds: bounds }, (results, status) => {
            if (status === "OK") {
                places.getDetails({ placeId: results[0].place_id, fields: ["name", "geometry", "place_id", "website"] }, (place, status) => {
                    if (status == "OK")
                        return resolve(place);
                    else
                        return reject(status);
                });
            } else {
                return reject(status);
            }
        });
    });
}

geocoder.geocodeの部分です。
geocoderはあらかじめ以下のようにして生成しておきます。
geocoder = new google.maps.Geocoder();

これで、PlaceIDが返ってきます。
ここから、Place APIを使ってスポットの詳細な情報を取得します。
places.getDetailsの部分です。
placesは以下のようにあらかじめ生成しておきます。
places = new google.maps.places.PlacesService(map);

それにより、正確な緯度経度やスポット名称等が得られます。

PlaceAPIを使うためには、APIキーを払い出すときに、Place APIも有効なキーである必要があります。
また、ライブラリの宣言時に、librariesとしてplaceを含める必要があります。

<script src="https://maps.googleapis.com/maps/api/js?key=【APIキー】&libraries=places&callback=initMap" async defer></script>

(参考)
https://developers.google.com/maps/documentation/javascript/reference/geocoder?hl=ja
https://developers.google.com/maps/documentation/javascript/reference/places-service?hl=ja

以上

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0