dayjournal memo

Total 973 articles!!

Try #027 – Vue.jsでLeafletとMapbox GL JSの開発環境を構築してみた

Yasunori Kirimoto's avatar

画像


画像


画像




画像


画像




Vue.jsでLeafletとMapbox GL JSの開発環境を構築してみました!



事前準備


画像



Vue.jsでLeafletとMapbox GL JSを利用する時に、ライブラリを直接読み込んでいるかたもいると思いますが、今回はVue.js向けのラッパーライブラリを利用して開発環境を構築してみました!



Vue.js x Leaflet


Vue.jsとLeafletの組み合わせの場合は、「Vue2Leaflet」を利用します。


はじめに、各ライブラリをインストールします。今回は「Vue CLI UI」を利用し「leaflet」と「vue2-leaflet」を検索し手軽にインストールします。

画像




次に、ひな形に地図を表示させるためのコードを追記していきます。


全体構成

画像


package.json

{
  "name": "my-app",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint"
  },
  "dependencies": {
    "core-js": "^2.6.5",
    "leaflet": "^1.5.1",
    "vue": "^2.6.10",
    "vue-router": "^3.0.3",
    "vue2-leaflet": "^2.2.1",
    "vuex": "^3.0.1"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "^3.12.0",
    "@vue/cli-plugin-eslint": "^3.12.0",
    "@vue/cli-service": "^3.12.0",
    "babel-eslint": "^10.0.1",
    "eslint": "^5.16.0",
    "eslint-plugin-vue": "^5.0.0",
    "vue-template-compiler": "^2.6.10"
  },
  "eslintConfig": {
    "root": true,
    "env": {
      "node": true
    },
    "extends": [
      "plugin:vue/essential",
      "eslint:recommended"
    ],
    "rules": {},
    "parserOptions": {
      "parser": "babel-eslint"
    }
  },
  "postcss": {
    "plugins": {
      "autoprefixer": {}
    }
  },
  "browserslist": [
    "> 1%",
    "last 2 versions"
  ]
}


/src


main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

import { Icon }  from 'leaflet'
import 'leaflet/dist/leaflet.css'

// this part resolve an issue where the markers would not appear
delete Icon.Default.prototype._getIconUrl;

Icon.Default.mergeOptions({
    iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'),
    iconUrl: require('leaflet/dist/images/marker-icon.png'),
    shadowUrl: require('leaflet/dist/images/marker-shadow.png')
});

Vue.config.productionTip = false

new Vue({
    router,
    store,
    render: h => h(App)
}).$mount('#app')

main.jsでleaflet.cssと既存アイコンを読み込みます。

import { Icon }  from 'leaflet'
import 'leaflet/dist/leaflet.css'

// this part resolve an issue where the markers would not appear
delete Icon.Default.prototype._getIconUrl;

Icon.Default.mergeOptions({
    iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'),
    iconUrl: require('leaflet/dist/images/marker-icon.png'),
    shadowUrl: require('leaflet/dist/images/marker-shadow.png')
});


/src/views


Home.vue

<template>
    <div class="home">
        <img alt="Vue logo" src="../assets/logo.png">
        <HelloWorld msg="Welcome to Your Vue.js App"/>
        <MapPane></MapPane>
    </div>
</template>

<script>
    import HelloWorld from '@/components/HelloWorld.vue'
    import MapPane from '@/components/MapPane.vue'

    export default {
        name: 'home',
        components: {
            HelloWorld,
            MapPane
        }
    }
</script>

<style scoped>

</style>

Home.vueでMapPaneコンポーネントを読み込みます。

<MapPane></MapPane>
import MapPane from '@/components/MapPane.vue'

components: {
    MapPane
}


/src/components


MapPane.vue

<template>
    <div class="mapPane">
        <!--マップ-->
        <l-map
            :zoom="zoom"
            :center="center"
        >
            <!--レイヤーコントロール-->
            <l-control-layers
                position="topright"
            ></l-control-layers>
            <!--レイヤ設定-->
            <l-tile-layer
                v-for="tileProvider in tileProviders"
                :key="tileProvider.name"
                :name="tileProvider.name"
                :visible="tileProvider.visible"
                :url="tileProvider.url"
                :attribution="tileProvider.attribution"
                layer-type="base"
            ></l-tile-layer>
            <!--マーカー-->
            <l-marker
                :lat-lng="marker"
            ></l-marker>
        </l-map>
    </div>
</template>

<script>
    import {
        LMap,
        LTileLayer,
        LControlLayers,
        LMarker
    } from 'vue2-leaflet';

    export default {
        name: 'MapPane',
        components: {
            LMap,
            LTileLayer,
            LControlLayers,
            LMarker
        },
        data() {
            return {
                center: [35.681, 139.763],
                zoom:14,
                marker: [35.681, 139.763],
                tileProviders: [
                    {
                        name: 'MIERUNE MONO',
                        visible: true,
                        url: 'https://tile.mierune.co.jp/mierune_mono/{z}/{x}/{y}.png',
                        attribution: "Maptiles by <a href='http://mierune.co.jp/' target='_blank'>MIERUNE</a>, under CC BY. Data by <a href='http://osm.org/copyright' target='_blank'>OpenStreetMap</a> contributors, under ODbL."
                    },
                    {
                        name: 'MIERUNE Color',
                        visible: false,
                        url: 'https://tile.mierune.co.jp/mierune/{z}/{x}/{y}.png',
                        attribution: "Maptiles by <a href='http://mierune.co.jp/' target='_blank'>MIERUNE</a>, under CC BY. Data by <a href='http://osm.org/copyright' target='_blank'>OpenStreetMap</a> contributors, under ODbL."
                    }
                ]
            }
        }
    }
</script>

<style scoped>
    .mapPane {
        height: 600px;
        margin: 0;
        text-align: left;
    }
</style>

マップのタグを指定します。

<template>
    <div class="mapPane">
        <!--マップ-->
        <l-map
            :zoom="zoom"
            :center="center"
        >
            <!--レイヤーコントロール-->
            <l-control-layers
                position="topright"
            ></l-control-layers>
            <!--レイヤ設定-->
            <l-tile-layer
                v-for="tileProvider in tileProviders"
                :key="tileProvider.name"
                :name="tileProvider.name"
                :visible="tileProvider.visible"
                :url="tileProvider.url"
                :attribution="tileProvider.attribution"
                layer-type="base"
            ></l-tile-layer>
            <!--マーカー-->
            <l-marker
                :lat-lng="marker"
            ></l-marker>
        </l-map>
    </div>
</template>

マップのサイズを指定します。

.mapPane {
    height: 600px;
    margin: 0;
    text-align: left;
}

マップとレイヤとマーカーの設定をします。

import {
    LMap,
    LTileLayer,
    LControlLayers,
    LMarker
} from 'vue2-leaflet';

export default {
    name: 'MapPane',
    components: {
        LMap,
        LTileLayer,
        LControlLayers,
        LMarker
    },
    data() {
        return {
            center: [35.681, 139.763],
            zoom:14,
            marker: [35.681, 139.763],
            tileProviders: [
                {
                    name: 'MIERUNE MONO',
                    visible: true,
                    url: 'https://tile.mierune.co.jp/mierune_mono/{z}/{x}/{y}.png',
                    attribution: "Maptiles by <a href='http://mierune.co.jp/' target='_blank'>MIERUNE</a>, under CC BY. Data by <a href='http://osm.org/copyright' target='_blank'>OpenStreetMap</a> contributors, under ODbL."
                },
                {
                    name: 'MIERUNE Color',
                    visible: false,
                    url: 'https://tile.mierune.co.jp/mierune/{z}/{x}/{y}.png',
                    attribution: "Maptiles by <a href='http://mierune.co.jp/' target='_blank'>MIERUNE</a>, under CC BY. Data by <a href='http://osm.org/copyright' target='_blank'>OpenStreetMap</a> contributors, under ODbL."
                }
            ]
        }
    }
}


簡易ローカルサーバーで確認

npm run serve


ローカルサーバーを立ち上げると、マップが表示されます。

画像





Vue.js x Mapbox GL JS


Vue.jsとMapbox GL JSの組み合わせの場合は、「VueMapbox」を利用します。


はじめに、各ライブラリをインストールします。今回は「Vue CLI UI」を利用し「mapbox-gl」と「vue-mapbox」を検索し手軽にインストールします。

画像




次に、ひな形に地図を表示させるためのコードを追記していきます。


全体構成

画像


package.json

{
  "name": "my-app2",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint"
  },
  "dependencies": {
    "core-js": "^2.6.5",
    "mapbox-gl": "^1.4.1",
    "vue": "^2.6.10",
    "vue-mapbox": "^0.4.1",
    "vue-router": "^3.0.3",
    "vuex": "^3.0.1"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "^3.12.0",
    "@vue/cli-plugin-eslint": "^3.12.0",
    "@vue/cli-service": "^3.12.0",
    "babel-eslint": "^10.0.1",
    "eslint": "^5.16.0",
    "eslint-plugin-vue": "^5.0.0",
    "vue-template-compiler": "^2.6.10"
  },
  "eslintConfig": {
    "root": true,
    "env": {
      "node": true
    },
    "extends": [
      "plugin:vue/essential",
      "eslint:recommended"
    ],
    "rules": {},
    "parserOptions": {
      "parser": "babel-eslint"
    }
  },
  "postcss": {
    "plugins": {
      "autoprefixer": {}
    }
  },
  "browserslist": [
    "> 1%",
    "last 2 versions"
  ]
}


/src


main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

import "mapbox-gl/dist/mapbox-gl.css";

Vue.config.productionTip = false

new Vue({
    router,
    store,
    render: h => h(App)
}).$mount('#app')

main.jsでmapbox-gl.cssを読み込みます。

import "mapbox-gl/dist/mapbox-gl.css";


/src/views


Home.vue

<template>
    <div class="home">
        <img alt="Vue logo" src="../assets/logo.png">
        <HelloWorld msg="Welcome to Your Vue.js App"/>
        <MapPane></MapPane>
    </div>
</template>

<script>
    // @ is an alias to /src
    import HelloWorld from '@/components/HelloWorld.vue'
    import MapPane from '@/components/MapPane.vue'

    export default {
        name: 'home',
        components: {
            HelloWorld,
            MapPane
        }
    }
</script>

Home.vueでMapPaneコンポーネントを読み込みます。

<MapPane></MapPane>
import MapPane from '@/components/MapPane.vue'

components: {
    MapPane
}


/src/components


MapPane.vue

<template>
    <div class="mapPane">
        <!--マップ-->
        <MglMap
            :accessToken="accessToken"
            :mapStyle="mapStyle"
            :center="center"
            :zoom="zoom"
        >
            <!--ナビゲーションコントロール-->
            <MglNavigationControl
                position="top-right"
            ></MglNavigationControl>
        </MglMap>
    </div>
</template>

<script>
    import Mapbox from "mapbox-gl";
    import {
        MglMap,
        MglNavigationControl
    } from "vue-mapbox";

    export default {
        name: 'MapPane',
        components: {
            MglMap,
            MglNavigationControl
        },
        data() {
            return {
                accessToken: 'APIキーを入力',
                mapStyle: 'mapbox://styles/スタイルキーを入力',
                center: [139.767, 35.681],
                zoom: 13
            }
        },
        created() {
            this.mapbox = Mapbox;
        }
    }
</script>

<style scoped>
    .mapPane {
        height: 600px;
        margin: 0;
        text-align: left;
    }
</style>

マップのタグを指定します。

<template>
    <div class="mapPane">
        <!--マップ-->
        <MglMap
            :accessToken="accessToken"
            :mapStyle="mapStyle"
            :center="center"
            :zoom="zoom"
        >
            <!--ナビゲーションコントロール-->
            <MglNavigationControl
                position="top-right"
            ></MglNavigationControl>
        </MglMap>
    </div>
</template>

マップのサイズを指定します。

.mapPane {
    height: 600px;
    margin: 0;
    text-align: left;
}

マップとレイヤの設定をします。

import Mapbox from "mapbox-gl";
import {
    MglMap,
    MglNavigationControl
} from "vue-mapbox";

export default {
    name: 'MapPane',
    components: {
        MglMap,
        MglNavigationControl
    },
    data() {
        return {
            accessToken: 'APIキーを入力',
            mapStyle: 'mapbox://styles/スタイルキーを入力',
            center: [139.767, 35.681],
            zoom: 13
        }
    },
    created() {
        this.mapbox = Mapbox;
    }
}


簡易ローカルサーバーで確認

npm run serve


ローカルサーバーを立ち上げると、マップが表示されます。

画像




Vue.jsとVue2LeafletとVueMapboxを利用することで、手軽にVue.jsでLeafletとMapbox GL JSの開発環境の構築ができました!


Leaflet・Mapbox GL JSのライブラリを、直接読み込んで利用することもできると思いますが、ラッパーライブラリを利用することである程度は手軽に操作ができると思います。ただ、用途により直接読み込むかラッパーライブラリを利用するかの選択は必要となりそうです。

メリット

  • 環境構築が比較的手軽にできる
  • HTMLのタグで直感的に指定できる
  • JavaScriptフレームワークのルールにある程度合わせたコードになる
  • JavaScriptフレームワークとの相性が悪いDOMやライフサイクル関係が操作しやすいかも
    ※今回は地図表示部分の確認だったのでなんとも言えないですが…

デメリット

  • Leaflet・Mapbox GL JSのピュアな全機能や、プラグインが利用できるわけではない
  • ラッパーライブラリ独自のコードを覚える必要がある
  • 利用者が少ないためエラー時の解決策のヒントが少ない

前回の、「Try #026 – AngularでLeafletとMapbox GL JSの開発環境を構築してみた」と比べてみて頂ければと思います!



Vue.js・Leaflet・Mapbox GL JSについて、他にも記事を書いています。よろしければぜひ。
tags - Vue.js
tags - Leaflet
tags - Mapbox GL JS



book

Q&A