現在位置を取得し、近くのランドマークを探す(wordpress版)
以前のエントリ「現在位置を取得し、近くのランドマークを探す」のwordpress版です。
カスタム投稿(landmark)を作成し、その情報をもとにWP REST APIで取得し描画していきます。
今回はプラグインのAdvanced Custom Fields(以下ACF)を使用します。
GoogleMapsのAPIも使用していきます。(比較したい地点の経度と緯度がわかっていればGoogleMapsのAPIを使わなくても大丈夫)
カスタム投稿をREST APIで取得できる様にする
show_in_rest
とrest_base
にAPI周りのことを記載すると/wp-json/wp/v2/landmark/
でカスタム投稿(landmark)を取得することができます。
function create_post_type()
{
$supports = [
'title', // title
'editor', // editor
];
register_post_type(
'landmark', // カスタム投稿
array(
'label' => 'ランドマーク',
'public' => true,
'has_archive' => false,
'menu_position' => 7,
'supports' => $supports,
'show_in_rest' => true, //rest api での取得を有効にする
'rest_base' => 'landmark' // rest api での取得名 /wp-json/wp/v2/landmark/
)
);
}
add_action('init', 'create_post_type');
ACFのGoogleマップを使う
ACFには多くのものが備わっていますが、今回はGoogleマップを使って楽に位置情報を入力していきます。
ACF | Google Map
https://www.advancedcustomfields.com/resources/google-map/
ACFでGoogleMapを選択します。
カスタム投稿記事へ移動すると、「このページでは Google マップが正しく読み込まれませんでした。」とポップアップが出てくるのでGoogleMapのAPIを設定していきます。
APIキーの設定
Google Cloud プラットフォームへ行き、新しいプロジェクトを作成します。
有効にするAPIは下記3つ。
- Geocoding API
- Maps JavaScript API
- Places API
左メニューの「APIとサービス」>「認証情報」をクリックし、上部の「CREATE CREDENTIALS」ボタンをクリックAPIキーを発行します。
APIキーが発行されたら、キーをコピーしておきます。
発行したAPIキーはそのままではいけないので制限をかけておきます。発行したAPIキー名をクリックしてキーの制限を行います。
アプリケーションの制限を「HTTPリファラー(ウェブサイト)」にし、「ウェブサイトの制限」に任意のURLを設定します。
functions.phpに記述追加
functions.phpにGoogleMapを使える様にする記述を追加します。
function my_setup()
{
global $global_api_key;
$global_api_key = '先ほど取得したAPIキー';
}
add_action('after_setup_theme', 'my_setup');
function my_acf_google_map_api($api)
{
global $global_api_key;
$api['key'] = $global_api_key;
return $api;
}
add_filter('acf/fields/google_map/api', 'my_acf_google_map_api');
無事に管理画面でGoogleMapが読み込まれました。
カスタム投稿のカスタムフィールドをREST APIの項目に含める
デフォルトだとREST APIにはカスタム投稿の値は含まれていないため、functions.phpに追記していきます。
//wp rest api カスタマイズ
add_action('rest_api_init', 'api_add_fields');
function api_add_fields()
{
register_rest_field(
'landmark', //カスタム投稿名
'googlemap', //ACF カスタムフィールド名
array(
'get_callback' => 'register_fields',
'update_callback' => null,
'schema' => null,
)
);
}
function register_fields($post, $name)
{
return get_post_meta($post['id'], $name, true);
}
WP REST APIで呼び出す
jsについては以前のエントリ「現在位置を取得し、近くのランドマークを探す」の方も併せてご覧ください。
htmlとjs
<div class="show-landmark-button-area">
<button type="button" id="show-landmark-button">近くの東京ランドマーク</button>
</div>
<div id="show-landmark-area">
<p>現在位置から50,000m以内のランドマーク</p>
<ul id="show-landmark">
</ul>
</div>
<div id="loading-area"></div>
// ヒュベニの公式
const hubeny = (lat1, lng1, lat2, lng2) => {
function rad(deg) {
return deg * Math.PI / 180;
}
// 緯度経度をラジアンに変換
lat1 = rad(lat1);
lng1 = rad(lng1);
lat2 = rad(lat2);
lng2 = rad(lng2);
// 緯度差
var latDiff = lat1 - lat2;
// 経度差算
var lngDiff = lng1 - lng2;
// 平均緯度
var latAvg = (lat1 + lat2) / 2.0;
// 赤道半径
var a = 6378137.0;
// 極半径
var b = 6356752.314140356;
// 第一離心率^2
var e2 = 0.00669438002301188;
// 赤道上の子午線曲率半径
var a1e2 = 6335439.32708317;
var sinLat = Math.sin(latAvg);
var W2 = 1.0 - e2 * (sinLat * sinLat);
// 子午線曲率半径M
var M = a1e2 / (Math.sqrt(W2) * W2);
// 卯酉線曲率半径
var N = a / Math.sqrt(W2);
t1 = M * latDiff;
t2 = N * Math.cos(latAvg) * lngDiff;
return Math.sqrt((t1 * t1) + (t2 * t2));
}
//ローディング
const loading = (status) => {
const loadingArea = document.getElementById('loading-area');
if (status === 'show') {
loadingArea.classList.add('is-show');
}
if (status === 'hidden') {
loadingArea.classList.remove('is-show');
}
}
//距離整形
const dataShaping = (distance) => {
let restult = Math.round(distance);
restult = Number(restult).toLocaleString();
return restult;
}
//リスト出力
const resultList = (array) => {
const showLandmark = document.getElementById('show-landmark');
let resultContent = '';
if (array.length < 0) {
resultContent = '<li class="empty">近くにありません。</li>';
} else {
for (let i = 0; i < array.length; i++) {
resultContent = resultContent + `<li><div><p>${array[i]['title']}</p><p>現在位置との距離 : 約${dataShaping(array[i]['result'])}m</p></div></li>`;
}
}
showLandmark.innerHTML = resultContent;
loading('hidden');
}
const NUM = 50000;
const getSuccess = (pos) => {
//現在地の緯度経度
let lat1 = pos.coords.latitude;
let lng1 = pos.coords.longitude;
let resultArray = [];
// REST API取得
fetch("https://xxxx.xx/wp-json/wp/v2/landmark/")
.then(response => {
if (response.ok) {
return response.json();
} else {
return Promise.reject(new Error('エラーです'));
}
})
.then(data => {
// console.log(data);
for (let i = 0; i < data.length; i++) {
lat2 = data[i].googlemap.lat; //軽度
lng2 = data[i].googlemap.lng; //緯度
let result = hubeny(lat1, lng1, lat2, lng2);
if (result < NUM) {
let resultData = [];
// console.log(data[i]);
resultData['title'] = data[i].title.rendered;
resultData['result'] = result;
resultData['googlemap'] = data[i].googlemap;
resultArray.push(resultData);
}
}
// console.log(resultArray);
resultList(resultArray);
});
};
//エラー
const geoError = () => {
var pos = {
'coords': {
'latitude': 0,
'longitude': 0
}
};
getSuccess(pos);
loading('hidden');
console.log('取得失敗');
};
// GeoLocationAPIで現在地の座標を取得する
const showLandmarkButton = document.getElementById("show-landmark-button");
showLandmarkButton.addEventListener('click', () => {
loading('show');
navigator.geolocation.getCurrentPosition(getSuccess, geoError, {
enableHighAccuracy: true
});
})
REST APIを取得する
https://xxxx.xx/wp-json/wp/v2/landmark/
で、あらかじめ設定しておいたカスタム投稿(landmark)を取得できます。
fetch("https://xxxx.xx/wp-json/wp/v2/landmark/")
.then(response => {
if (response.ok) {
return response.json();
} else {
return Promise.reject(new Error('エラーです'));
}
})
.then(data => {
console.log(data);
});
取得したデータをconsole.log
などで見てみると下記の様に。
このデータのうちgooglemap
の項目は、取得できる様にfunctions.phpに記載したカスタムフールドです。
ACFのGoogleMapを取得すると、中に配列があり、経度緯度等の情報が入っています。この経度緯度を使って現在位置との2地点距離を比較します。
content: {
rendered: '',
protected: false
}
date: "2022-08-16T09:30:27"
date_gmt: "2022-08-16T09:30:27"
googlemap: {//カスタムフィールド
address: '日本、東京都渋谷区道玄坂1丁目12−1 渋谷マークシティ',
lat: 35.6581085,
lng: 139.6986263,
zoom: 14,
place_id: 'ChIJQcMHZ6qMGGARWHRxn-Vn8uA',
…
}
guid: {
rendered: 'https://xxxx.xx/?post_type=landmark&p=43'
}
id: 43
link: "https://xxxx.xx/landmark/%e6%b8%8b%e8%b0%b7%e3%83%9e%e3%83%bc%e3%82%af%e3%82%b7%e3%83%86%e3%82%a3/"
modified: "2022-08-16T09:31:18"
modified_gmt: "2022-08-16T09:31:18"
slug: "%e6%b8%8b%e8%b0%b7%e3%83%9e%e3%83%bc%e3%82%af%e3%82%b7%e3%83%86%e3%82%a3"
status: "publish"
template: ""
title: {
rendered: '渋谷マークシティ'
}
type: "landmark"
_links: {
self: Array(1),
collection: Array(1),
about: Array(1),
wp: attachment: Array(1),
curies: Array(1)
}
【補足】PHPでACFのGoogleMapの経度緯度を取得する場合
$googlemap = get_field('googlemap');
//ACFのgooglemapは取得すると配列になっている
// var_dump($googlemap);
//経度緯度
echo $googlemap['lat'];
echo $googlemap['lng'];
ACFのGoogleMapを必ずしも使う必要はない
今回はACFのGoogleMapを使って管理画面からGoogleMapを使って手軽に地点を設定できる様にしましたが、その地点の経度緯度がわかれば計算ができるので、GoogleMapを使わず、経度と緯度を入力するカスタムフィールドを作成して、それを参照すればGoogleMapsのAPIを取得せずに2地点間の距離を比較することができます。
参考サイト
WordPress REST API で、カスタム投稿タイプなどの情報を取得する | Tips Note by TAM
https://www.tam-tam.co.jp/tipsnote/cms/post9688.html
WordPressのREST APIにカスタムフィールドがないなら自分で追加すればいいじゃない | オレDEV.com
https://dev.ore-shika.com/post/wp-rest-custom-fields/
位置情報取得や2地点間距離取得についての参考サイト
位置情報 API – Web API | MDN
https://developer.mozilla.org/ja/docs/Web/API/Geolocation_API
Geolocation.getCurrentPosition() – Web API | MDN
https://developer.mozilla.org/ja/docs/Web/API/Geolocation/getCurrentPosition
地球上の2地点間の距離を取得するアルゴリズム(ヒュベニ or 球面三角法)比較【JavaScript】 | 404 motivation not found
https://tech-blog.s-yoshiki.com/entry/8/?referer=https://www.google.com/