2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Vue 3 + Leafletで画像上に図形を描く

Last updated at Posted at 2024-11-01

この記事では、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>

詳細解説

  1. <div id="map" class="leaflet-container">
    Leafletの地図を表示するためのコンテナです。このidを使って、LeafletのL.mapが地図を描画します。

  2. ボタンの設定

    • @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を使って相対パスを指定しています。
  • mapmarkersverticespolygon: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ピクセルの緑色の丸いカスタムアイコンです。これをポリゴンの頂点表示用に使います。
  • iconSizeiconAnchor:アイコンのサイズを指定し、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:クリック位置にカスタムアイコンのマーカーを作成し、draggabletrueにしてドラッグ可能にします。
  • 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の柔軟な機能を使って、さらに多彩なインタラクションを追加することも可能です。

参考記事

Leafletの使い方
Leafletの図形描画

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?