はじめに
本記事はMAヒーローズ・リーグ Advent Calendar 2021の12/4の記事となります。
今年も親子でヒーローズリーグ2021に参加しました。作成した作品は「toio地球儀」というもので、toioを2台用いた地球儀型の緯度経度入出力デバイスです。toio地球儀自体に興味がある方は、次の記事を確認して頂ければと思います。
本記事では、この「toio地球儀」を利用した1アプリケーションである「Scope」に着目し、どのようにWeb APIを利用し、緯度・経度から有益な情報を取得したかを記載します。ただし、本親子はWeb技術を勉強し始めてからまだ1年足らずですので、記載しているコード上は技術的に稚拙な点があるかと思います。その点はご容赦ください。
地球ブラウザ「Scope」とは
地球ブラウザ「Scope」とは、toio地球儀と連携し、ユーザが入力した地球上の位置情報から、その位置に関連する情報をWebブラウザに表示するアプリケーションです。全部で8つのWeb APIを利用しており、位置情報(緯度・経度)から得られる思いつく限りの情報を1画面にまとめて提示します。開発時のコンセプトは「神様が人の暮らしを覗き込むときに利用する道具」でした。
「Scope」は、気になる場所をクリックすると、その場所に関する情報を手軽に確認することができる、暇つぶしにはちょうどよいアプリです。本来はtoio地球儀で緯度・経度を入力して利用することを想定したアプリではありますが、マウス操作にも対応し、一般利用できるようにしたものが↓です。画面左下にある地図をクリックすると、その場所に虫眼鏡マークが付き、その場所に関連する情報が表示されます。虫眼鏡のそばに表示されるカメラマークは、Webカメラ画像を取得した場所を表しています。
See the Pen Scope iframe by sunagimo (@sunagimo2) on CodePen.
このScopeで表示している情報と、そのときに利用しているWeb APIは次の通りです。
- 地名:Google Geocoding
- 現在の気象情報:Azure Maps Weather
- 街並み画像:Google Street View
- 周辺地図:Google Maps
- 近隣のWebカメラ画像:Windy Webcams
- その地域の急上昇動画:YouTube Data API
- 地域のニュース:Bing News Search
- 地域のヒットソング:Spotify Web API
以後、この緯度・経度情報から各種情報への変換に関し、実例を用いて説明します。
緯度・経度から各種情報へ変換する8種類の事例
1. Google Geocodingによる地名情報の取得
Google Geocodingを用いると、緯度経度情報から、その場所の地名情報を取得することが可能です。下の例は、(36.8, 137.9)の緯度経度情報から取得した地名情報です。
本機能を利用するには、Google Cloud Platform で Geocoding API を有効にし、本APIを利用するためのAPI Keyを取得しておく必要があります。この手順に関しては、各種記事がありますので参考にするとよいと思います。
Geocoding API の API Key が取得できましたら、次のようなコードにより緯度経度(変数ichi)から地名情報を取得します。
<!DOCTYPE html>
<html lang="ja">
<head>
<script src="https://unpkg.com/jquery/dist/jquery.min.js"></script>
<script src="https://maps.googleapis.com/maps/api/js?key=[取得したAPIキー]&libraries=&v=weekly"
defer></script>
</head>
<body>
<script>
// 取得する緯度経度情報
let ichi = {
lat: 36.8,
lng: 137.9
};
let geocoder;
// ページの最初
$(document).ready(function () {
// Geocoder の生成
geocoder = new google.maps.Geocoder();
// 緯度経度情報から地名の取得
geocoder.geocode({ 'location': ichi }, function (results, status) {
if (status == 'OK') {
console.log(results);
}
});
});
</script>
</body>
</html>
resultsの中に地名情報が格納されており、基本的にはresults[0]の中のaddress_componentsを辿っていけば、住所がすべて取得できます。また、address_componentsのtypesにcontryが含まれるものは国名を示しており、その時のshort_nameが「Country codes」となります。このContry codesは他のWeb API の入力値として必要になりますので、取得できた場合は変数に保存しましょう。
例えば、上のコードを実行した場合は、次のような結果が取得できます。この場合、results[0].address_components[4].short_name がその緯度経度のCountry codesです。
2. Azure Maps Weatherによる現在の気象情報の取得
Azure Maps Weather を利用すると、緯度経度情報から、その場所の気象情報を取得することが可能です。下の例は、(36.8, 137.9)の緯度経度情報から取得した気象情報です。
本機能を利用するには、Microsoft Azure で Azure Maps を有効にし、本APIを利用するためのAPI Keyを取得しておく必要があります。
API Key が取得できましたら、次のようなコードにより気象情報を取得します。関数引数のlatとlngには、それぞれ緯度、経度を数値で入力します。
<!DOCTYPE html>
<html lang="ja">
<head>
<script src="https://unpkg.com/jquery/dist/jquery.min.js"></script>
</head>
<body>
<script>
// Microsoft Azure で取得した API Key
let AzureMapKey = [取得したAPIキー];
// 取得する緯度経度情報
let ichi = {
lat: 36.8,
lng: 137.9
};
// ページの最初
$(document).ready(function () {
update_weather(ichi.lat, ichi.lng);
});
// 現在位置の天気情報を取得
function update_weather(lat, lng) {
$.ajax({
url: 'https://atlas.microsoft.com/weather/currentConditions/json?api-version=1.0&language=ja-JP&subscription-key='
+ AzureMapKey + '&query=' + lat.toString() + ',' + lng.toString(),
type: 'GET',
}).then(
function (results) {
console.log(results);
},
function () {
console.log("error");
}
);
}
</script>
</body>
</html>
成功すると、次のような結果が取得できます。results[0] の中を辿っていけば、ほしい情報が得られるはずです。なお、海の上など、気象情報が提供されていない地域に関しては、本関数が失敗を返すことが結構あります。エラー対策はしっかり行っておきましょう。
3. Google Street Viewによる街並み画像の取得
Google Street View を利用すると、緯度経度の街並み画像(Street view)を取得することができ、マウス操作で街並み画像の拡大縮小、向きの変更などができるようになります。下の例は、(36.8, 137.9)の緯度経度情報から取得した街並み画像です。
本機能を利用するには、Google Cloud Platform で Maps JavaScript API を有効にし、本APIを利用するためのAPI Keyを取得しておく必要があります。上で記載した Geocoding API でAPI Key を生成している場合、そのKeyの利用範囲に本APIも含めておくと、一つのKeyで両方とも利用することが可能です。
なお、Street View Static API という、もっとぴったりの名前のAPIもありますが、そちらはマウス操作に対応していません。
API Key が取得できましたら、次のようなコードで街並み画像を取得します。なお、街並み画像を表示する領域用に、panorama_areaをidとして持つ領域が定義されているものとします。
<!DOCTYPE html>
<html lang="ja">
<head>
<script src="https://unpkg.com/jquery/dist/jquery.min.js"></script>
<script src="https://maps.googleapis.com/maps/api/js?key=[取得したAPIキー]&libraries=&v=weekly"
defer></script>
</head>
<body>
<div id="panorama_area" style="width:200pt; height:200pt;"></div>
<script>
// 取得する緯度経度情報
let ichi = {
lat: 36.8,
lng: 137.9
};
let panorama;
let StreetViewService;
let search_radius = 5000; // 5km
// ページの最初
$(document).ready(function () {
// Street View の定義
panorama = new google.maps.StreetViewPanorama(
document.getElementById("panorama_area"),
{
linksControl: false,
panControl: false,
zoomControl: false,
addressControl: false,
clickToGo: false,
imageDateControl: false,
showRoadLabels: false,
panControl: false,
zoom: 0,
}
);
// StreetViewService
StreetViewService = new google.maps.StreetViewService();
update_panorama(ichi.lat, ichi.lng);
});
// 指定した緯度経度の半径5km内にある街並み画像を取得
function update_panorama(lat, lng) {
search_lating = new google.maps.LatLng(lat, lng);
StreetViewService.getPanorama({ location: search_lating, preference: 'nearest', radius: search_radius }, check_street_view_pos).catch(errObj => {
//例外を捕まえる必要がある。
});
}
// 結果取得時のコールバック関数
function check_street_view_pos(data, status) {
if (status === "OK") {
// 街並み画像が見つかったので、その画像を表示
panorama.setPosition(data.location.latLng);
} else {
// 街並み画像が見つからなかった
}
}
</script>
</body>
</html>
緯度経度からの街並み画像の表示は、街並み画像の取得と、表示に分けて実行します。街並み画像の取得には、StreetViewService を利用し、指定した緯度経度に最も近い街並み画像のある地点を検索します。このとき、検索範囲をm単位で取得することが可能です。日本では、数kmの検索範囲を設定すれば、まず街並み画像は見つかりますが、海外ではかなり範囲を広くしないと画像が取得できな場合があります。
街並み画像が見つかった場合、StreetViewPanoramaを利用し、その位置の街並み画像を画面に表示します。
4. Google Mapsによる周辺地図の取得
有名なGoogle Maps を利用すると、緯度経度の周辺地図を表示することができます。下の例は、(36.8, 137.9)の緯度経度情報から取得した周辺地図画像です。
この機能も、「Google Street Viewによる街並み画像の取得」同様、Maps JavaScript API を有効にすることで利用可能です。次のようなコードで街並み画像を取得します。なお、周辺地図画像を表示する領域用に、map_areaをidとして持つ領域が定義されているものとします。
<!DOCTYPE html>
<html lang="ja">
<head>
<script src="https://unpkg.com/jquery/dist/jquery.min.js"></script>
<script src="https://maps.googleapis.com/maps/api/js?key=[取得したAPIキー]&libraries=&v=weekly"
defer></script>
</head>
<body>
<div id="map_area" style="width:200pt; height:200pt;"></div>
<script>
// 取得する緯度経度情報
let ichi = {
lat: 36.8,
lng: 137.9
};
let map;
// ページの最初
$(document).ready(function () {
// Map の定義
map = new google.maps.Map(
document.getElementById("map_area"),
{
// 周辺地図表示時のオプション
zoom: 2,
clickableIcons: false,
fullscreenControl: false,
mapTypeControl: false,
streetViewControl: false,
zoomControl: false,
draggable: true,
});
update_map(ichi.lat, ichi.lng);
});
// 指定した緯度経度を中心とする地図を表示する
function update_map(lat, lng) {
lating = new google.maps.LatLng(lat, lng);
map.setCenter(lating);
}
</script>
</body>
</html>
周辺地図には、フキダシアイコンや画像などを配置することも可能です。
5. Windy Webcamsによる近隣のWebカメラ画像の取得
Windy Webcams を利用すると、特定の緯度経度の近くに配置されたWebカメラから画像を取得することができます。下の例は、(36.8, 137.9)の緯度経度情報から取得したWebカメラ画像です。
本機能を利用するには、Windy のサイトで Webcams API を有効にし、本APIを利用するためのAPI Keyを取得しておく必要があります。趣味で利用する範囲であれば、無料枠で行けると思います。
API Keyが取得できたら、次のコードでWebカメラ画像を取得できます。
<!DOCTYPE html>
<html lang="ja">
<head>
<script src="https://unpkg.com/jquery/dist/jquery.min.js"></script>
</head>
<body>
<img id="windy" style="width:400pt; height:200pt;"></img>
<script>
// 取得する緯度経度情報
let ichi = {
lat: 36.8,
lng: 137.9
};
// ページの最初
$(document).ready(function () {
update_windy(ichi.lat, ichi.lng);
});
// 緯度経度(lat, lng)に最も近いWebカメラ画像の取得
function update_windy(lat, lng) {
$.ajax({
url: 'https://api.windy.com/api/webcams/v2/list/orderby=distance/nearby=' + lat.toString() + ',' + lng.toString() +
',10000/limit=1?show=webcams:image,location',
type: 'GET',
headers: {
'x-windy-key': [取得したAPIキー],
},
}).then(
function (result) {
if (result.result.webcams.length != 0) {
console.log(result);
// カメラ画像
document.getElementById("windy").src = result.result.webcams[0].image.current.preview;
}
else {
console.log("no camera");
}
},
function () {
}
);
}
</script>
</body>
</html>
上のコードを実行すると、次のような結果が帰って来ます。result.result.webcams[0].image.current.preview を img のsrcに設定すれば、その画像を表示することが可能です。
6. YouTube Data APIによるその地域の急上昇動画の取得
YouTube Data API を利用すると、その国のYouTube 急上昇動画を取得することができます。下の例は、2021/11/28時点の日本国の急上昇動画です。Google がどのようなアルゴリズムで急上昇動画を選定しているかはわかりませんが、一応、その時点で最も着目すべきYouTube動画ではあるようです。
本機能を利用するには、Google Cloud Platform で YouTube Data API V3 を有効にし、本APIを利用するためのAPI Keyを取得しておく必要があります。上の Geocoding API ですでにAPI Key を生成している場合、そのKeyの利用範囲に、本APIも含めておくと、一つのKeyで利用法とも利用することが可能です。
API Keyが取得できたら、次のコードでその国の急上昇動画を取得できます。なお、引数として入力する国名は上記Google Geocoding API で取得したアルファベット2文字の「Country codes」が必要です。例えば日本では "JP"、アメリカでは "US"です。
<!DOCTYPE html>
<html lang="ja">
<head>
<script src="https://unpkg.com/jquery/dist/jquery.min.js"></script>
</head>
<body>
<script>
let GoogleKey = [APIキー情報]; // Google から取得した API Key
// ページの最初
$(document).ready(function () {
update_youtube("JP");
});
// 引数で指定した国の急上昇動画を取得
function update_youtube(CountryCodes) {
$.ajax({
url: 'https://www.googleapis.com/youtube/v3/videos?key=' + GoogleKey +
'&chart=mostPopular&maxResults=1®ionCode=' + CountryCodes,
dataType: 'json',
}).then(
function (result) {
// この国の現時点での急上昇動画のID
console.log(result.items[0].id);
},
function () {
console.log("err");
}
);
}
</script>
</body>
</html>
動画のIDが取得できてしまえば、iframe埋め込みでの動画プレーヤーでの動画再生が可能となります。当然、YouTubeが浸透していない国では、上記スクリプトはエラーとなりますのでご注意ください。
7. Bing News Searchによる地域のニュースの取得
Bing News Searchを用いると、指定した国のトップニュースを取得することが可能です。下の例は、2021/11/28 17:30時点での日本国のトップニュースです。例のコロナウィルスに「オミクロン」なんて可愛い名前が付いたのですね。
本機能利用には、Microsoft Azure で Bing News Search を有効にし、本APIを利用するためのAPI Keyを取得しておく必要があります。
API Keyが取得できたら、次のコードでその国のトップニュース情報が取得できます。なお、引数として入力する国名は上記Google Geocoding API で取得したアルファベット2文字の「Country codes」が必要です。例えば日本では "JP"、アメリカでは "US"です。このコードでは"news1_img" "news2_img"をIDとして持つニュースのサムネイル画像配置用のimgタグと、"news1_text" "news2_text" をIDとして持つニュースタイトル配置用のdivタグがあることを想定しています。
<!DOCTYPE html>
<html lang="ja">
<head>
<script src="https://unpkg.com/jquery/dist/jquery.min.js"></script>
</head>
<body>
<img id="news1_img" style="width:100pt; height:100pt;"></img>
<p id="news1_text" style="width:100pt; height:100pt;"></p>
<script>
let api_key = [APIキー情報];
// ページの最初
$(document).ready(function () {
update_news("JP");
});
// 引数で指定した国のニューストピックを取得し、Viewに反映
function update_news(CountryCodes) {
$.ajax({
url: 'https://api.bing.microsoft.com/v7.0/news/search?count=2&cc=' + CountryCodes,
type: 'GET',
headers: {
'Ocp-Apim-Subscription-Key': api_key,
'Accept-Language': 'Jp',
},
}).then(
function (result) {
if (result.value[0].image) {
// サムネネイルがない場合があるので注意
document.getElementById("news1_img").src = result.value[0].image.thumbnail.contentUrl;
}
document.getElementById("news1_text").innerText = result.value[0].name;
},
function () {
}
);
}
</script>
</body>
</html>
8. google cloud translation によるニュースタイトルの翻訳
上記の方法でニュースのタイトル情報を取得した後、Web APIを使い、それを日本語に翻訳することが可能です。
翻訳機能を実装するのはとても簡単です。これまでに入手したGoogle Cloud PlatformのAPIキーに、Cloud translation API を紐づけるだけです。すると、Street view API 等と同じAPIキーを使い、翻訳を行うことができるようになります。当然、課金は合算で行われます。
簡単な使い方は次の通りです。この例では "مرحبًا" という文章を日本語に翻訳し、pタグ内に出力します。元の文章の言語を指定していないことに注目してください。サービス側で勝手に元の言語を識別し、日本語に翻訳しています。もちろん入力パラメータで元の言語を指定することも可能です。
<!DOCTYPE html>
<html lang="ja">
<head>
<script src="https://unpkg.com/jquery/dist/jquery.min.js"></script>
</head>
<body>
<p id="text">aaa</p>
<script>
async function honyaku(src) {
try {
const response = await $.ajax({
url: 'https://translation.googleapis.com/language/translate/v2',
dataType: 'json',
headers: {
'X-goog-api-key': [取得したAPIキー],
'Content-Type': 'application/json; charset=utf-8',
},
data: {
"q": src,
"target": "ja",
"format": "text",
}
});
return response.data.translations[0].translatedText;
} catch (error) {
return "err";
}
}
$(document).ready(async function () {
let translatedText = await honyaku("مرحبًا");
document.getElementById('text').innerText = translatedText;
});
</script>
</body>
</html>
9. Spotify Web APIによる地域のヒットソングの再生
地域のヒットソングを再生するには、「地域のヒットソングの取得」と「取得した楽曲の再生」を行う必要があります。まず、地域のヒットソングを取得するには、Spotify Charts にアクセスし、楽曲のURLを取得する必要がありました。当初は、これもWeb APIで取得したかったのですが、どうしてもその手法がわかりませんでした。そのため、上記サイトを1日に一度巡回し、各国のヒットソングのURLを取得するようにしました。
地域のヒットソングは、ページ毎に分かれて管理されており、日本のヒットソングはこのページに格納されています。他の国のヒットソングを取得した場合は、URL の JP 部分にGoogle Geocodingで取得したCountry codesを入れて見てください。本記事執筆時点では、68の国と地域のヒットソングが取得できています。
このSpotify のサイトは、各楽曲を次のようなフォーマットで管理しています。
このサイトから、1位だけの情報を取得したいのであれば、次のようなXPathでデータを取り出すことが可能です。
// 楽曲へのリンク
var link = xmlDoc.evaluate("//*[@id='content']/div/div/div/span/table/tbody/tr[1]/td[1]/a/@href", xmlDoc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
// 楽曲のサムネイル画像
var img = xmlDoc.evaluate("//*[@id='content']/div/div/div/span/table/tbody/tr[1]/td[1]/a/img/@src", xmlDoc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
// アーティスト名
var artist = xmlDoc.evaluate("//*[@id='content']/div/div/div/span/table/tbody/tr[1]/td[4]/strong/text()", xmlDoc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
// 曲名
var title = xmlDoc.evaluate("//*[@id='content']/div/div/div/span/table/tbody/tr[1]/td[4]/span/text()", xmlDoc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
例えば、次のようなスクリプトを作成し、cronで定期的に実行することで、楽曲情報を取得できます。
#!/bin/bash
STR1="https://spotifycharts.com/regional/"
STR2="/daily/latest"
function startfile() {
echo "let ranking = [" >ranking.js
}
function endfile() {
echo "];" >>ranking.js
}
function getinfo() {
curl --user-agent "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.48 Safari/537.36" \
-o tmp $STR1$1$STR2
echo -n " [\"" >>ranking.js
echo -n $1 | tr '[:lower:]' '[:upper:]' >>ranking.js
echo -n "\", \"" >>ranking.js
xmllint --html --xpath "//*[@id='content']/div/div/div/span/table/tbody/tr[1]/td[4]/span/text()" tmp | sed -e "s/\"//g" >>ranking.js
echo -n "\", \"" >>ranking.js
xmllint --html --xpath "//*[@id='content']/div/div/div/span/table/tbody/tr[1]/td[4]/strong/text()" tmp | sed -e "s/\"//g" >>ranking.js
echo -n "\", \"" >>ranking.js
xmllint --html --xpath "//*[@id='content']/div/div/div/span/table/tbody/tr[1]/td[1]/a/@href" tmp | sed -e "s/ href=\"\([^\"]*\)\"/\1/g" |
sed -e "s/^.\{31\}//" >>ranking.js
echo -n "\", \"" >>ranking.js
echo -n $STR1$1$STR2 >>ranking.js
echo -n "\", \"" >>ranking.js
xmllint --html --xpath "//*[@id='content']/div/div/div/span/table/tbody/tr[1]/td[1]/a/img/@src" tmp | sed -e "s/ src=\"\([^\"]*\)\"/\1/g" >>ranking.js
echo "\"]," >>ranking.js
}
startfile
getinfo jp >/dev/null 2>&1
getinfo us >/dev/null 2>&1
endfile
Web API を利用した楽曲の再生には、Spotify Premium への入会が必要です。最初この制約がわからず、API呼び出しがエラーとなる現象に悩みましたが、Spotify Premium への入会によりエラーが発生しなくなりました。なお、2022年1月時点でSpotify Premium を退会したところ、再度エラーが出るようになりましたので、間違いないと思います。
Spotify Premium への入会したユーザは、Spotify Web APIを用いることでWeb APIを利用した楽曲再生が実現できます。まず、Spotify for Developersにアクセスし、Client ID と Client Secret を取得します。そして、Authorization Code Flow に従い、access_token と refresh_token を取得します。
下記のコードは、CountryCodesで国情報を指定すると、その国のヒットソングを再生するコードとなります。ちょっとややこしいですが、定期的にrefresh_spotifyを呼び出し、access_token をリフレッシュする必要があります。そして、そのaccess_token を用い、楽曲の再生を行います。再生されるデバイスは、現在のSpotify楽曲を再生しているデバイスですので、再生したいデバイスで事前に何らかの楽曲を再生しておくとよいでしょう。ranking 部分は上で説明した「地域のヒットソングの取得」により、日々更新されているものとします。
let access_token = [access_token];
let refresh_token = [refresh_token];
let authorization_basic = ["Client ID:Client Secret"をbase64化したもの];
// 一定時間ごとに呼び出し、spotify_token を更新する
function refresh_spotify() {
$.ajax({
url: 'https://accounts.spotify.com/api/token',
type: 'POST',
headers: {
'Authorization': 'Basic ' + authorization_basic,
},
data: {
grant_type: 'refresh_token',
refresh_token: refresh_token,
}
}).then(
function (result) {
access_token = result.access_token;
},
);
}
// 1日に一度巡回し、更新された各国のヒットソングの情報
let ranking = [
["AE", "by Adele", "Easy On Me", "46IZ0fSY2mpAiktS3KOqds", "https://spotifycharts.com/regional/ae/daily/latest", "https://i.scdn.co/image/ab67616d00004851c6b577e4c4a6d326354a89f7"],
["AR", "by TINI, L-Gante", "Bar", "0lJE8f0lx8mUSfMyxeYpiC", "https://spotifycharts.com/regional/ar/daily/latest", "https://i.scdn.co/image/ab67616d000048517b1a8b1a92561bb5d16d6b4c"],
// 以後略
];
// 特定の国のヒットソングを再生する
function play_sound(CountryCodes) {
for (let i = 0; i < ranking.length; i++) {
if (ranking[i][0] == CountryCodes) {
var spotify_data = {
uris: ["spotify:track:2MvIexkUblP1QdpBzKot3N"],
};
potify_data.uris[0] = 'spotify:track:' + ranking[i][3];
// 曲を再生
$.ajax({
url: 'https://api.spotify.com/v1/me/player/play',
type: 'PUT',
headers: {
'Authorization': 'Bearer ' + access_token,
},
data: JSON.stringify(spotify_data),
}).then(
function (result) {
},
function () {
}
);
break;
}
}
}
まとめ
本記事では、緯度・経度情報を入力とし、各種Web APIで様々な情報を取得する例を示しました。どれも簡単に有益な情報が取得できるAPIばかりですので、是非使って見てください。