6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

WikimediaAdvent Calendar 2020

Day 21

JavaScriptのみでWikipediaへのQRコードをOpenStreetMapから作る

Posted at

シンプルなサンプルプログラムで解説

まち歩きマップメーカー」のソースを解説しようとしたけど複雑なので諦めました。代わりに、サンプルプログラムを作ったので、それの解説を行います。

対象者

  • JavaScriptでフロントエンド開発をされている方
  • WikipediaをWebアプリで利用したいと考えている方
  • OpenStreetMapのデータを取得したいと考えている方
  • QRコード生成のサンプルプログラムが欲しい方

利用するライブラリ

ソースコードとデモ

ソース解説

index.html
<html>
	<head>
		<link rel="shortcut icon" href="favicon.ico" />
		<link rel="stylesheet" href="base.css" />
		<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet@1.6.0/dist/leaflet.css" />
		<script defer src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
		<script defer src="https://cdn.jsdelivr.net/npm/leaflet@1.6.0/dist/leaflet-src.js"></script>
		<script defer src="https://cdn.jsdelivr.net/npm/qrcode-svg@1.1.0/lib/qrcode.min.js"></script>
		<script defer src="./osmtogeojson.js"></script>
		<script defer src="./makeqr.js"></script>
	</head>
	<body>
		<div id="mapid"></div>
	</body>
</html>

シンプルにcssとライブラリの読み込みのみ。makeqr.jsはプログラム本体です。
leafletで地図を表示するため、"mapid"のidを付与したdivを配置しています。

makeqr.js(メイン処理)
"use strict";
var LL = {};								// 緯度(latitude)と経度(longitude)
const OvServer = 'https://overpass-api.de/api/interpreter'
const api_query = 'way["name"="大阪城"]';
const api_wikipedia = "wikipedia.org/w/api.php?format=json&action=query&prop=extracts&exintro=&explaintext=&titles=";

// initialize leaflet
let map = L.map('mapid', { center: [34.6867, 135.5273], zoom: 16 });
let osm_std = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxNativeZoom: 19, attribution: '&copy; <a href="https://osm.org/copyright">OpenStreetMap</a> contributors' }).addTo(map);

// get OpenStreetMap data & get Wikipedia text & write QR Code
osmdata_get(api_query).then(geojson => {
    let coords = geojson.features[0].geometry.coordinates[0][0];
    let wiki = geojson.features[0].properties.wikipedia.split(':');
    let url = encodeURI(`https://${wiki[0]}.wikipedia.org/wiki/${wiki[1]}`);
    let latlng = { lat: coords[1], lng: coords[0] };
    wikipedia_get(wiki[0], wiki[1]).then(text => qr_add(url, latlng, text));   // make QR Code
});

最初にconstで利用するサーバーと検索用クエリを定義しています。
api_queryはOverPass APIに投げるクエリ(詳細はOverPass APIのススメ)。

const後は、背景地図を表示させるleafletの初期化処理を行っています。
※緯度経度をパラメータに指定しています。場所は大阪城の天守閣周辺です。

その後、関数osmdata_getを呼んで、OpenStreetMapから大阪城を取得します。
取得後、変数coordsで緯度経度を取得し、変数wikiに["ja","大阪城"]が入ります。
※OpenStreetMapのwikipediaタグは「言語:記事名」の文字列となっているため。

変数wikiの設定後、変数urlにwikipediaへのリンクURLを設定(エンコード含む)。
変数latlngは、変数coordsをオブジェクト化(単に値の入れ直し)しています。

変数の設定後、関数wikipedia_getを呼び、その返り値を関数qr_addへ引き渡して、
QRコードを生成させています。最後に地図上に表示させて完了となります。

makeqr.js(関数部分)
// get OpenStreetMap data
function osmdata_get(ovpass) {
    return new Promise((resolve) => {
        LL.NW = map.getBounds().getNorthWest();
        LL.SE = map.getBounds().getSouthEast();
        let maparea = '(' + LL.SE.lat + ',' + LL.NW.lng + ',' + LL.NW.lat + ',' + LL.SE.lng + ');';
        let query = OvServer + '?data=[out:json][timeout:30];(' + ovpass + maparea + ');out body;>;out skel qt;'
        $.get(query).done(function (data) {
            let geojson = osmtogeojson(data, { flatProperties: true });
            resolve(geojson);
        });
    });
};

// get Wikipedia text
function wikipedia_get(lang, name) {      // get wikipedia contents
    return new Promise((resolve) => {
        let encurl = "https://" + lang + "." + api_wikipedia + encodeURI(name);
        $.get({ url: encurl, dataType: "jsonp" }, function (data) {
            let key = Object.keys(data.query.pages);
            let text = data.query.pages[key].extract;
            resolve(text);
        });
    });
}

function qr_add(url, latlng, text) {
    let qrcode = new QRCode({ content: url, join: true, container: "svg", width: 128, height: 128 });
    let data = qrcode.svg();
    let icon = L.divIcon({ "className": "icon", "iconSize": [512, 128], "html": `<div style="float: left;">${data}</div><div>${text}</div>` });
    let qr_marker = L.marker(new L.LatLng(latlng.lat, latlng.lng), { icon: icon, draggable: true });
    qr_marker.addTo(map);
};

サンプルで用意した自作関数は3つのみ。上から説明していきますね。

osmdata_get(ovpass)

  • ovpassは、OpenStreetMapのデータを取得するOverPass APIのクエリ文です。
  • サンプルでは「way["name"="大阪城"]」の文字列が入っています。
  • 緯度経度などをURLにセットして、APIサーバーからgetする内容です。
  • 処理の成功後、osmtogeojsonでgeoJsonへ変換して値を返します。

wikipedia_get(lang, name)

  • langは、Wikipediaの言語(日本語版はja)。サンプルでは「ja」が入ります。
  • nameは、Wikipediaの記事名。サンプルでは「大阪城」が入ります。
  • 記事名をエンコードしてからwikipediaのAPIを呼び出しています。
  • 変数api_wikipediaは最初のconstに書いてあり、概要取得のパラメータです。
  • get後、少し面倒なオブジェクトが返ってくるので対処しています。

qr_add(url, latlng, text)

  • urlは、QRコードが指し示すURLを指定します。
  • latlngは、OpenStreetMapから取得した大阪城の移動経度が入ります。
  • textは、Wikipediaから取得した記事の概要テキストが入ります。
  • 変数qrcodeは、urlをQRコードに変換した後のオブジェクトが入ります。
  • 変数dataは、qrcodeから<svg>タグのテキストを生成しています。
  • 変数iconは、QRコードと概要をアイコン化したオブジェクトが入ります。
  • その後、変数iconをleafletのアイコンとして表示させています。

如何でしたでしょうか?

思ったより簡単に、OpenStreetMapとWikipediaからデータを取得できました。
データが取得出来たら、QRコードにしてリンクしても、表示させても良しです。
アイデア次第で様々なことに活用できると思います。しかも全世界対応ですよ!

なお、以下のライセンスに関する説明を読んでからデータを利用してください。

おまけ

6
2
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
6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?