概要
Vue で地図を表示するアプリを作ってみます。
地図ライブラリは leaflet を使います。
前提
node と npm
vue-cli
vue-cliのインストール
$ npm i -g @vue/cli
$ vue -V
3.4.1
最短の道
プロジェクトの作成
とりあえず vf という名前でプロジェクトを作成します。選択肢が出てきますが、デフォルトですすめます。
$ vue create vf
Vue CLI v3.4.0
? Please pick a preset:
❯ default (babel, eslint)
Manually select features
leaflet をインストール
vf という名前のディレクトリができているので、中に入って、leaflet をインストールします。
$ cd vf
$ npm i leaflet --save
App.vue を変更
App.vue を変更します。
<template>
<div id=app>
</div>
</template>
<script>
import 'leaflet/dist/leaflet.css'
import L from 'leaflet'
export default {
mounted() {
L.map( 'app', { center: L.latLng( 35.6825, 139.752778 ), zoom: 15 } ).addLayer(
L.tileLayer( 'http://{s}.tile.osm.org/{z}/{x}/{y}.png' )
)
}
}
</script>
<style>
html, body, #app { height: 100% }
body { margin: 0 }
</style>
起動と接続
vf ディレクトリでサービスを開始して、ブラウザから localhost:8080 に繋ぎます。
$ npm run serve
勘所
import 'leaflet/dist/leaflet.css'
がないと、地図の表示が崩れます。
html, body, #app { height: 100% }
で地図を囲む全ての要素の height を 100% にしないと地図が表示されません。
body { margin: 0 }
がないと、地図の左上にちょっと余白ができて、右下が切れてしまいます。
L.map( 'app', { center: L.latLng( 35.6825, 139.752778 ), zoom: 15 } )
で app という id の div に地図をつけています。
center と zoom の両方を指定しないと、地図が表示されません。
マーカー
クリックされた場所にマーカーをつけてみます。
デフォルトのマーカー
デフォルトのマーカーを使うにはちょっと面倒なんですがアイコンのリソースの位置を指定してやることが必要です。
<template>
<div id=app>
</div>
</template>
<script>
import 'leaflet/dist/leaflet.css'
import L from 'leaflet'
delete L.Icon.Default.prototype._getIconUrl
L.Icon.Default.mergeOptions(
{ iconUrl : require( 'leaflet/dist/images/marker-icon.png' )
, iconRetinaUrl : require( 'leaflet/dist/images/marker-icon-2x.png' )
, shadowUrl : require( 'leaflet/dist/images/marker-shadow.png' )
}
)
export default {
mounted() {
const map = L.map( 'app', { center: L.latLng( 35.6825, 139.752778 ), zoom: 15 } ).addLayer(
L.tileLayer( 'http://{s}.tile.osm.org/{z}/{x}/{y}.png' )
).on(
'click'
, p => map.addLayer( L.marker( p.latlng ) )
)
}
}
</script>
<style>
html, body, #app { height: 100% }
body { margin: 0 }
</style>
divIcon を使ったマーカー
divIcon を使うと、マーカーのアイコンとして div を使えます。div なんでやりたい放題できます。
<template>
<div id=app>
</div>
</template>
<script>
import 'leaflet/dist/leaflet.css'
import L from 'leaflet'
export default {
mounted() {
const map = L.map( 'app', { center: L.latLng( 35.6825, 139.752778 ), zoom: 15 } ).addLayer(
L.tileLayer( 'http://{s}.tile.osm.org/{z}/{x}/{y}.png' )
).on(
'click'
, p => map.addLayer(
L.marker(
p.latlng
, { icon: L.divIcon( { className: 'red marker', iconSize: [ 16, 16 ] } ) }
)
)
)
}
}
</script>
<style>
html, body, #app { height: 100% }
body { margin: 0 }
.marker {
text-align : center
; color : white
; font-size : 16
; border-radius : 8px
; box-shadow : 8px 8px 8px rgba( 0, 0, 0, 0.4 )
}
.red {
background : red
}
</style>
応用編
マーカークラスターグループ
leaflet.markercluster をインストールすると、地図にたくさんマーカーを置くとき、近接するマーカーをまとめて表示するような機能を実現できます。
npm i leaflet.markercluster --save
5個のマーカーのクラスターグループを作ってみます。
<template>
<div id=app>
</div>
</template>
<script>
import 'leaflet/dist/leaflet.css'
import L from 'leaflet'
require( 'leaflet.markercluster' )
import 'leaflet.markercluster/dist/MarkerCluster.css'
import 'leaflet.markercluster/dist/MarkerCluster.Default.css'
export default {
mounted() {
const w = { icon: L.divIcon( { className: 'red marker', iconSize: [ 16, 16 ] } ) }
L.map( 'app', { center: L.latLng( 35.6825, 139.752778 ), zoom: 15 } ).addLayer(
L.tileLayer( 'http://{s}.tile.osm.org/{z}/{x}/{y}.png' )
).addLayer(
L.markerClusterGroup().addLayer(
L.marker( [ 35.691, 139.752 ], w )
).addLayer(
L.marker( [ 35.692, 139.752 ], w )
).addLayer(
L.marker( [ 35.690, 139.752 ], w )
).addLayer(
L.marker( [ 35.691, 139.753 ], w )
).addLayer(
L.marker( [ 35.691, 139.751 ], w )
)
)
}
}
</script>
<style>
html, body, #app { height: 100% }
body { margin: 0 }
.marker {
text-align : center
; color : white
; font-size : 16
; border-radius : 8px
; box-shadow : 8px 8px 8px rgba( 0, 0, 0, 0.4 )
}
.red {
background : red
}
</style>
5個まとめて表示されています。
クリックするとズームして別々に表示されます。
最後に
leaflet は機能豊富なライブラリなので、いくらでもネタはあるんですが、キリがないのでこのへんにしておきます。最後までおつきあいいただいてありがとうございました。
余談
2020/5/18
同じようなテーマのElectron で地図を表示する無料で最短の道を書きました。
Leaflet 1.6.0 に対応しているので、この記事とちょっと Leaflet の書き方が違います。参考にしていただければ幸いです。