この記事では、Vue 3とLeafletを使って地図や画像上にクリック位置に応じてマーカーやポリゴンを追加する方法を紹介します。マーカーのドラッグによるポリゴンの更新や、頂点の削除機能なども備えています。
準備:環境構築
まず、Leafletをインストールします。
npm install leaflet
次に、Leafletのスタイルを読み込みます。Vueのメインファイル(main.js
)やこのコンポーネントに以下を追加してください。
import 'leaflet/dist/leaflet.css'
テンプレート部分
テンプレートには、地図を表示する領域とボタンのコンテナを設定します。
<template>
<div>
<!-- 地図表示エリア -->
<div id="map" class="leaflet-container"></div>
<!-- ボタンコンテナ -->
<div class="button-container">
<!-- 最後に追加したマーカーを削除するボタン -->
<button @click="removeLastMarker">
<span>一個前に戻る</span>
</button>
<!-- すべてのマーカーとポリゴンを削除するボタン -->
<button @click="removeAllMarkers">
<span>クリア</span>
</button>
</div>
</div>
</template>
詳細解説
-
<div id="map" class="leaflet-container">
Leafletの地図を表示するためのコンテナです。このid
を使って、LeafletのL.map
が地図を描画します。 -
ボタンの設定
-
@click="removeLastMarker"
:Vueのクリックイベントリスナーで、このボタンをクリックすると最後のマーカーを削除するremoveLastMarker
関数が実行されます。 -
@click="removeAllMarkers"
:こちらのボタンはすべてのマーカーやポリゴンを削除するためのremoveAllMarkers
関数が呼び出されます。
-
スクリプト部分
次に、Leafletの地図オブジェクトやポリゴン、マーカーの管理を行うスクリプト部分について詳しく見ていきます。
インポートと初期設定
<script setup>
import L from 'leaflet' // Leafletライブラリをインポート
import { onMounted, ref } from 'vue' // Vue 3のonMountedとrefをインポート
// 表示する画像のURLを定義
const src = ref(new URL('@/assets/room_living_normal.png', import.meta.url).href)
const map = ref(null) // Leafletの地図オブジェクトを格納
const markers = ref([]) // マーカーのリスト
const vertices = ref([]) // ポリゴンの頂点座標リスト
const polygon = ref(null) // ポリゴンオブジェクト
-
src
:地図上に表示する画像のパスです。Vueの@/assets
を使って相対パスを指定しています。 -
map
、markers
、vertices
、polygon
:Leafletのオブジェクトやマーカー・ポリゴンの位置を管理するためのref
です。
カスタムアイコン設定
次に、ポリゴンの頂点に表示されるカスタムアイコンを設定します。
const vertexIcon = L.divIcon({
className: '', // カスタムCSSクラスは指定しない
html: '<div style="width: 10px; height: 10px; background-color: var(--green-500, #22C55E); border-radius: 50%;"></div>', // 丸い形のカスタムアイコンを指定
iconSize: [10, 10], // アイコンサイズ
iconAnchor: [5, 5], // アイコンの基準位置
})
-
html
:10x10ピクセルの緑色の丸いカスタムアイコンです。これをポリゴンの頂点表示用に使います。 -
iconSize
とiconAnchor
:アイコンのサイズを指定し、iconAnchor
でアイコンの中心を基準位置として配置します。
地図初期化関数 initMap
地図を初期化し、画像をオーバーレイとして追加します。また、地図にクリックイベントを登録します。
const initMap = () => {
const img = new Image() // 画像オブジェクトを生成
img.onload = () => { // 画像の読み込みが完了したときに実行
const width = img.width
const height = img.height
const bounds = [[0, 0], [height, width]] // 画像の左上から右下までの座標範囲
map.value = L.map('map', {
crs: L.CRS.Simple, // シンプルな座標系を使用
minZoom: -1, // 最小ズームレベル
maxZoom: 2, // 最大ズームレベル
center: [height / 2, width / 2], // 地図の中心を画像の中央に設定
zoom: 0, // 初期ズームレベル
zoomControl: true, // ズームコントロールを表示
})
L.imageOverlay(src.value, bounds).addTo(map.value) // 画像を地図上にオーバーレイ
map.value.fitBounds(bounds) // 地図の表示範囲を画像に合わせる
map.value.on('click', onMapClick) // 地図のクリックイベントを設定
}
img.src = src.value // 画像の読み込みを開始
}
-
L.map
:地図オブジェクトを初期化し、画像の大きさに合わせた表示範囲を設定します。 -
L.imageOverlay
:画像を地図にオーバーレイとして追加します。 -
fitBounds
:地図の表示範囲を画像全体にフィットさせます。
地図クリックイベント onMapClick
地図がクリックされると、クリック位置にマーカーを追加し、ポリゴンの頂点を更新します。
const onMapClick = e => {
const latlng = e.latlng // クリック位置の緯度経度を取得
if (!polygon.value) {
polygon.value = L.polygon([latlng], { // 新しいポリゴンを作成
color: 'var(--green-500, #22C55E)', // 枠線の色
fillColor: 'rgba(34, 197, 94)', // 塗りつぶしの色
weight: 2, // 枠線の太さ
}).addTo(map.value)
vertices.value.push(latlng) // 頂点リストにクリック位置を追加
} else {
const latlngs = polygon.value.getLatLngs()[0] // 既存ポリゴンの頂点リストを取得
latlngs.push(latlng) // 新しいクリック位置を頂点リストに追加
polygon.value.setLatLngs([latlngs]) // 更新された頂点リストでポリゴンを再描画
vertices.value.push(latlng) // 頂点リストにもクリック位置を追加
}
addMarker(latlng) // クリック位置にマーカーを追加
}
- ポリゴンの作成:最初のクリック時にポリゴンが作成され、次以降のクリックで頂点が追加されてポリゴンが拡張されます。
-
polygon.value.getLatLngs()
:既存のポリゴン頂点リストを取得し、クリック位置を頂点として追加します。
マーカー追加とドラッグ操作
クリック位置にマーカーを追加し、ドラッグによって頂点を更新します。
const addMarker = latlng => {
const marker = L.marker(latlng, { icon: vertexIcon, draggable: true }).addTo(map.value)
marker.on('drag', onMarkerDrag) // ドラッグイベントを登録
markers.value.push(marker) // マーカーを
リストに追加
}
const onMarkerDrag = e => {
const marker = e.target
const latlng = marker.getLatLng()
const latlngs = polygon.value.getLatLngs()[0]
const index = markers.value.indexOf(marker)
if (index !== -1) {
latlngs[index] = latlng // ドラッグ位置を頂点リストに反映
vertices.value[index] = latlng // 頂点リストを更新
polygon.value.setLatLngs([latlngs]) // ポリゴンを再描画
}
}
-
L.marker
:クリック位置にカスタムアイコンのマーカーを作成し、draggable
をtrue
にしてドラッグ可能にします。 -
onMarkerDrag
:ドラッグ操作でマーカー位置が変更された際に、ポリゴンの頂点も更新されます。
マーカー削除処理
最後に追加したマーカーや全マーカーを削除する関数を定義します。
const removeLastMarker = () => {
if (markers.value.length > 0) {
const lastMarker = markers.value.pop() // 最後のマーカーを取得し削除
map.value.removeLayer(lastMarker) // マーカーを地図から削除
const latlngs = polygon.value.getLatLngs()[0]
latlngs.pop() // 最後の頂点も削除
vertices.value.pop()
if (latlngs.length > 0) {
polygon.value.setLatLngs([latlngs]) // ポリゴンを再描画
} else {
map.value.removeLayer(polygon.value) // 頂点がなくなればポリゴンも削除
polygon.value = null
}
}
}
const removeAllMarkers = () => {
if (polygon.value) {
map.value.removeLayer(polygon.value) // ポリゴンが存在する場合削除
polygon.value = null
}
markers.value.forEach(marker => map.value.removeLayer(marker)) // 全マーカーを削除
markers.value = [] // マーカーリストをクリア
vertices.value = [] // 頂点リストをクリア
}
</script>
-
removeLastMarker
:最後に追加したマーカーとポリゴンの頂点を削除します。 -
removeAllMarkers
:すべてのマーカーとポリゴンを削除し、地図をリセットします。
スタイル部分
最後に、地図の見た目やボタンの配置を設定します。
<style scoped>
.leaflet-container {
width: 50%;
height: 600px;
}
</style>
-
.leaflet-container
:地図の表示領域を定義し、地図のサイズや枠線を設定します。
以上で、Vue 3とLeafletを使用して、地図上にマーカーやポリゴンを動的に追加・削除する方法が完成です。Leafletの柔軟な機能を使って、さらに多彩なインタラクションを追加することも可能です。