九保すこひ@フリーランスエンジニア|累計300万PVのブログ運営中
さてさて、この間「夜景マップを作ってみる」でleaflet.js
を使ってみましたが、これすごく便利ですよね。
もちろん機能ではかなわないですが Googleマップは有料化してしまったので「ちょっとした地図」が必要な場合は、十分すぎる機能です。
ただ、前回使ってみたときに一点ある不満がありました。
それが・・・・・・
検索ができない
ことです。
例えば、「大阪駅周辺の地図がみたい」と思っても、今いる場所が大阪じゃない場合GPSデータを使えるわけではないので、
1.地図を掴んで、2.滑らせる
を延々とやらないといけなくなります。(ズームを変えるにしてもこの作業、面倒ですよね)
そこで❗
今回は、この欠点をleaflet
のプラグイン「leaflet-search」を使って実装してみることにします。
つくりたいのは、「駅名で検索して、自動でその位置へ移動する」機能です。
ぜひ何かの参考になりましたら嬉しいです。😄✨
「GPSをリアルタイムで取得できれば、
Pusherとかと連携して、
『見守り機能』もつくれそうですね👍」
開発環境: Laravel 8.x、leaflet 1.7、leaflet-search 3
目次
処理の流れ
駅名検索ができるようにするための処理は以下のとおりです。
- 地図上にある検索ボックスに駅名を入力
- Ajax でその駅名を送信
- 送信先で該当するデータを検索する
- 検索ボックスの下に候補を表示
- 選択するとその駅の位置へ移動
では実際に作業をしていきましょう❗
駅の位置データを用意する
では、まずはAjax
を通して取得する駅データを用意しましょう。
なんとありがたいことに、駅データ.jp というサイトさんが無料&商用OKなデータを公開してくれています。
ただし、ダウンロードには会員登録が必要なので以下のURLから登録すませておいてください。
📝 新規登録ページ:駅データ.jp:新規登録
会員登録が完了したら、ダウンロードページ からログインします。
すると、ページ左側のメニューがあるので、その中の「データダウンロード」をクリック。
ページが移動するので、「駅データ」の最新データをダウンロードしてください。形式はCSV
です。
そして、ファイルがダウンロードできたらstorage/app/csv
フォルダに移動しておいてください。
これでデータのダウンロードは完了です。
駅データをデータベースへ移す
さすがにCSV
のままでは検索などに利用しにくいので、内容を全てDBへ移動します。
以下のコマンドを実行してください。
php artisan make:model Station -msc
すると、「モデル」だけでなく「マイグレーション」と「コントローラー」ファイルが作成されるので中身を変更していきます。
マイグレーションの設定
まずはマイグレーションです。
database/migrations/****_**_**_******_create_stations_table.php
// 省略 public function up() { Schema::create('stations', function (Blueprint $table) { $table->id(); $table->string('name')->comment('駅名'); $table->double('latitude', 9, 6)->comment('緯度'); $table->double('longitude', 9, 6)->comment('経度'); $table->timestamps(); }); }
※ 今回は必要最低限のカラムだけ用意していますが、お好みで追加してください。
Seederの設定
続いてSeeder
でDB再構築する際に、先ほどのCSV
からデータが移行できるようにします。
database/seeders/StationSeeder.php
<?php namespace Database\Seeders; use App\Models\Station; use Illuminate\Database\Seeder; class StationSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { $csv_path = storage_path('app/csv/station20210312free.csv'); // ダウンロードしたファイルへのパス $file = new \SplFileObject($csv_path); $file->setFlags(\SplFileObject::READ_CSV); foreach ($file as $index => $row) { if($index > 0 && !is_null($row[0])) { $station_name = $row[2]; $longitude = floatval($row[9]); $latitude = floatval($row[10]); $station = new Station(); $station->name = $station_name; $station->longitude = $longitude; $station->latitude = $latitude; $station->save(); } } } }
次に、Seeder
はつくっただけでは有効にならないので、Laravel
側へ登録します。
database/seeders/DatabaseSeeder.php
public function run() { // 省略 $this->call(StationSeeder::class); }
では、以下のコマンドでデータベースを再構築してみましょう。
⚠ご注意: なお、10,000件以上の駅データがあるので少し時間がかかるかもしれません。
php artisan migrate:fresh --seed
再構築が完了すると、テーブルは以下のようになりました。
コントローラーの設定
そして、コントローラーに、「マップの表示」と「Ajaxで駅名検索する」機能をつくります。
app/Http/Controllers/StationController.php
<?php namespace App\Http\Controllers; use App\Models\Station; use Illuminate\Http\Request; class StationController extends Controller { public function index() { return view('station.index'); } public function list(Request $request) { if($request->filled('keyword')) { return Station::where('name', 'LIKE', '%'. $request->keyword .'%') ->take(5) ->get() ->map(function($station){ // leaflet-search 用にデータを加工する return [ 'loc' => [ $station->latitude, $station->longitude ], 'title' => $station->name ]; }); } return []; } }
この中では、検索キーワードがあるときは駅名で検索し、さらに取得できたデータをこの後で登場するプラグイン「leaflet-search」に合わせてデータを加工しています。
ルートをつくる
続いては、ルートです。
use App\Http\Controllers\StationController; // 省略 Route::get('station', [StationController::class, 'index'])->name('station.index'); Route::get('station/list', [StationController::class, 'list'])->name('station.list');
ビューをつくる
最後に、先ほどのコントローラーで指定したビューをつくります。
resources/views/station/index.blade.php
<html> <head> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css" rel="stylesheet"> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet-search@3.0.2/dist/leaflet-search.min.css"> </head> <body> <div id="app" class="p-4"> <h1>Leaflet に駅名検索をつくるサンプル</h1> <div id="my-map" style="height:500px;"></div> </div> <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script> <script src="https://cdn.jsdelivr.net/npm/leaflet-search@3.0.2/dist/leaflet-search.min.js"></script> <script> window.addEventListener('load', () => { const tileLayerUrl = 'https://cyberjapandata.gsi.go.jp/xyz/pale/{z}/{x}/{y}.png'; const attribution = '<a href="https://www.gsi.go.jp/kikakuchousei/kikakuchousei40182.html" target="_blank" rel="noopener">国土地理院</a>'; const map = L.map('my-map').setView([35.71006814475588, 139.81069905886525], 15); // スタートは東京スカイツリー L.tileLayer(tileLayerUrl, { attribution: attribution }).addTo(map); L.control.search({ url: '{{ route('station.list') }}?keyword={s}', position: 'topright', textErr: '候補が見つかりませんでした', textPlaceholder: '駅名で検索...', textCancel: 'キャンセル' }).addTo(map); }); </script> </body> </html>
この中で重要なのは、プラグイン「leaflet-search」を使っている部分です。
パラメータのurl
に先ほどコントローラーで作ったlist()
へのURLをセットしていますが、ここの{s}
の部分が検索ボックスに入力されたテキストと置き換わります。
つまり、実際のURL
は以下のようになります。
URL例:
http://******/station/list?keyword=(URLエンコードされたテキスト)
これで作業を終了です。
お疲れ様でした✨😄👍
ちなみに: Vue 3との連携は・・・
私はビルドなしで使えるVue
が好きなのですが、Vue 3
+ leaflet
を使うとズーム時にエラーが発生する可能性があります。
というのも、Vue 3はdata()
内に入った値をProxy
化するので、どうやらそれが影響するようです。
そのため、もし使うなら「Vue外にある変数」map
にleaflet
のmap
オブジェクトをセットして使うようにするといいようです。(ただし、コンポーネントなど多重で呼び出す場合はうまくいきませんので、注意が必要です)
※ なお、markRaw
を使って「そのまま」指定をしてもエラーが発生してしまいました。Vue 2
なら問題なく動くのですが・・・やはり「シンプルなのに高機能」ですし、Vue 2
の方が使い勝手いいかもですね。😂
デモを用意しました
では実際のテストですが、触ってもらった方が早いと思うのでデモページを用意しました。
※ ただし、あまりにも全駅データでは多すぎるので、以下の駅だけしか検索できませんのでご注意ください。
- 東京
- 渋谷
- 原宿
- 新宿
- 新橋
ぜひ試してみてください。
📝 デモページ: こちら
企業様へのご提案
現在、Googleマップの利用は有料となっています。
そのため、「Googleマップほど高機能でなくてもいい」ということでしたら、今回の「検索機能付きのマップ」でランニング・コストをカットすることができます。(完全無料で使えます)
また、今回は駅データを使いましたが、緯度経度データさえあれば、支社、支店、営業所名、または配送先やお客様の自宅の検索も可能です。
ぜひこういった機能をご希望でしたら、ぜひお問い合わせよりご連絡ください。
お待ちしております。m(_ _)m
※ なお、今回の駅データも含めまして緯度・経度データのダウンロード・ページをまとめました。ぜひこちらもご参考になってください。
おわりに
ということで今回はLaravel
+ Leaflet
+leaflet-search
で「駅名検索できるマップ」をつくってみました。
個人的には、駅データだけでなくいろんな「珍スポット」and「B級スポット」の緯度経度データがほしいところなので、また調査してみようと考えています。
ぜひ皆さんも面白そうな使い方を考えてみてくださいね。
ではでは〜❗
「よし、次はここへ行こう❗」