ドナルドのいない街を可視化してみた。
市区町村ごとのマクドナルド店舗数で色付けしています。色が濃い地域ほど店舗が多くなります。
白く塗られた地域がマクドナルドが一店舗もない「ドナルドのいない街」です。
経緯
Geoクラスタではマクドナルドの店舗データを使った分析が流行っているらしいので、周回遅れながらこのビッグウェーブに乗ってみることにしました。
日本一マクドナルドから遠い場所 - Qiita
「本当に」日本一マクドナルドから遠い場所|ヌノカワ ユウスケ|note
日本一マクドナルドから遠い場所はPostGISで求められるかな? - Qiita
日本一マクドナルドから遠い場所 をより精確に求めてみた - Qiita
とはいえ、一番遠い場所を見つける別のアプローチも思いつかなかったので、今回は、GISを使った分析の基本、「ポリゴンごとに含まれるポイントをカウントして塗り分け地図を作る」をすることに。
データ
・マクドナルド店舗データ
店舗検索 | お店をさがす | McDonald’sのページより、デベロッパーツールを使って店舗情報を取得しました。
・市区町村ポリゴン
全国市区町村界データ | ESRIジャパンよりダウンロードしたshapeファイルをGeoJSONに変換して使っています。
市区町村ごとの店舗数を求める
下記画像のようにポイントデータとポリゴンデータを用意し、各ポリゴンに含まれるポイントデータを抽出します。
QGISなどを使うことで簡単に計算することができるのですが、今回はTurf.jsを使って計算しました。
Turf.jsは、地理情報を分析するための様々な機能をもつJavaScriptライブラリです。
このブログでもいくつかサンプルを載せているので興味があるかたは使ってみてください。
(一部、バージョンアップにより使えなくなった機能があります)
Turf.jsは、フロントエンドだけでなくnode.jsのスクリプトとしても実行できるので、バックエンドやローカルで利用することもできます。
今回は、データ量が多いのでローカルで計算しました。
turf.jsインストール
turf.jsはnpmでインストールすることができます。
1 |
$ npm install @turf/turf |
pointsWithinPolygon
turf.jsでは、複数のポイントデータの中から、特定のポリゴン内に含まれるポイントだけを抽出する pointsWithinPolygon という関数があるので、この関数をつかって市区町村ごとの店舗数を求めます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
var points = turf.points([ [-46.6318, -23.5523], [-46.6246, -23.5325], [-46.6062, -23.5513], [-46.663, -23.554], [-46.643, -23.557] ]); var searchWithin = turf.polygon([[ [-46.653,-23.543], [-46.634,-23.5346], [-46.613,-23.543], [-46.614,-23.559], [-46.631,-23.567], [-46.653,-23.560], [-46.653,-23.543] ]]); var ptsWithin = turf.pointsWithinPolygon(points, searchWithin); |
サンプルコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
const fs = require('fs'); const turf = require('@turf/turf'); //市区町村ポリゴンデータの読み込み const geojson = JSON.parse(fs.readFileSync('japan_ver81.geojson', 'utf8')); //マクドナルド店舗データの読み込み const macdonald = JSON.parse(fs.readFileSync('macdonald.json', 'utf8')); //マクドナルド店舗データをgeojson形式に変換 const points = turf.featureCollection( macdonald.map(function(d){ const p = turf.point([d.longitude, d.latitude], d); return p }) ); //市区町村境界データの各ポリゴン内に含まれるマクドナルド店舗を抽出し、プロパティに追加 geojson.features.forEach(function(feature){ if(!feature.geometry) return null; try { var ptsWithin = turf.pointsWithinPolygon(points, feature); //ポリゴン内に含まれるポイントを抽出 } catch(e){ console.log(e); console.log(feature); } feature.properties.mac_count = ptsWithin.features.length; //店舗数をプロパティに追加 feature.properties.mac_data = ptsWithin.features; //店舗情報を追加 }); console.log(JSON.stringify(geojson)); //コンソールにGeoJSONを出力する |
実行
1 |
$ node addMacCountData.js > macMap.geojson |
地図に載せて見た
作成したGeoJSONデータは、GeoJSONに対応した地図ライブラリや地図サービスなどに読みこませて表示することができます。
今回は、以前記事にした、GeoJSONをフロントエンドでベクトルタイル化して表示する」方法で地図をつくりました。
おまけ
店舗数の多い地域のランキングなどを調べてみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
マック有り地域 939 マック無し地域 971 マック有り地域の割合 49.16% 店舗数TOP10 1位 東京都 八王子市 18店舗 2位 東京都 足立区 17店舗 3位 兵庫県 尼崎市 16店舗 4位 千葉県 柏市 16店舗 5位 栃木県 宇都宮市 15店舗 6位 神奈川県 藤沢市 15店舗 7位 東京都 江戸川区 14店舗 8位 東京都 練馬区 14店舗 9位 鹿児島県 鹿児島市 14店舗 10位 東京都 大田区 14店舗 一人当たり店舗数TOP10 1位 東京都 八王子市 18店舗 人口:562795人 2位 沖縄県 島尻郡八重瀬町 1店舗 人口:30093人 3位 兵庫県 尼崎市 16店舗 人口:463940人 4位 千葉県 柏市 16店舗 人口:409001人 5位 栃木県 宇都宮市 15店舗 人口:521820人 6位 神奈川県 藤沢市 15店舗 人口:426024人 7位 東京都 江戸川区 14店舗 人口:686387人 8位 東京都 練馬区 14店舗 人口:719109人 9位 鹿児島県 鹿児島市 14店舗 人口:607382人 10位 東京都 大田区 14店舗 人口:712057人 店舗0地域人口TOP10 1位 広島県 広島市安芸区 0店舗 人口:80830人 2位 宮城県 栗原市 0店舗 人口:71748人 3位 香川県 三豊市 0店舗 人口:68084人 4位 宮城県 気仙沼市 0店舗 人口:66733人 5位 福島県 南相馬市 0店舗 人口:63930人 6位 埼玉県 蓮田市 0店舗 人口:62481人 7位 熊本県 合志市 0店舗 人口:59702人 8位 新潟県 新潟市西蒲区 0店舗 人口:59482人 9位 北海道 北広島市 0店舗 人口:59348人 10位 新潟県 佐渡市 0店舗 人口:58527人 |
やはり関東圏に店舗の多い地域が集まっている感じです。
市区町村ごとの人口のデータがあったので「一人当たり店舗数が多い地域」で計算してみたけど順位はあまり変わらず。ただ一点、沖縄県島尻郡八重瀬町の店舗(東風平店)が突如ランクインします。
八重瀬町はもともとは、東風平町を含む4町村が合併するはずだったのが、いろいろあって2町村のみの合併になったとのことで、他のマック有り地域より人口が少ないので一人当たりの店舗数が多くなったっぽいです、
逆に人口が多い地域でマックがない地域を調べてみると、九州・四国が少ないみたい。
1位の広島市安芸区は飛地になっていて、分断されたちょうど隙間に店舗がありました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
const fs = require('fs'); const turf = require('@turf/turf'); const geojson = JSON.parse(fs.readFileSync('html/japan.geojson', 'utf8')); const data = geojson.features.map(d => { return d.properties; }); const haveMac = data.filter(d => d.mac_count > 0); const noMac = data.filter(d => !d.mac_count > 0); console.log("マック有り地域", data.length); console.log("マック無し地域", data.length); console.log("マック有り地域の割合", ~~((haveMac.length/data.length) * 10000) /100 + "%") const output = function(arg ,flag){ arg.forEach( (d, i) => { const ranking = i +1; let address = [d["KEN"],"t", d["GUN"], d["SEIREI"], d["SIKUCHOSON"]].join(""); //null消し let str = `${ranking}位 ${address}tt${d.mac_count}店舗`; if(flag) str += `t人口:${d.P_NUM}人`; console.log(str); }) } console.log("n店舗数TOP10") haveMac.sort((a,b) => b.mac_count - a.mac_count) const top10 = haveMac.slice(0, 10); output(top10, false); console.log("n一人当たり店舗数TOP10") haveMac.sort((a,b) => (b.mac_countd/b.P_NUM) - (a.mac_count/a.P_NUM) ) const top10perP = haveMac.slice(0, 10); output(top10perP, true); console.log("n店舗0地域人口TOP10") noMac.sort((a,b) => b.P_NUM - a.P_NUM ) const noMacTop10perP = noMac.slice(0, 10); output(noMacTop10perP, true); |