Skip to content

加载地图

原生leaflet加载WMTS

这里的代码有些长,不用担心,大部分都是为了规范化做出的牺牲。

首先

我写了一个Tidanditu.js,用于放置有关天地图的各种方法。这里面就包含了一个getTiandituWMTS的函数,用于获取天地图的WMTS服务。

接下来

我在HomeMap.vue中开始装载地图,在装载地图之前,需要创建一个div元素,id为map(随意),然后在js代码块中的onMounted钩子函数的回调中初始化地图。
具体而言,是使用leaflet的Map类,对其进行实例化。

js
new Map("map", options)

其中"map"就是我们为地图容器元素设置的id,options是一个{}包含的配置选项,不填的则为默认值

最后

当然,我这里使用了try,catch语句,配合Tianditu.js和fetch API抛出的异常来保证地图真的加载天地图了,而不只是将天地图的图层挂载。

点击展开源代码
vue
<script setup >
import { onMounted, ref} from 'vue';
import {Point, Map } from 'leaflet';
import { getTiandituWMTS } from '../composables/Tianditu';
import { ElButton, ElInput} from 'element-plus';

const isError = ref(false);
const isLoading = ref(true);
const inputKey = ref('');
const placeholder = ref('请前往天地图官网获取');
function loadMap(){
    //开始加载
    try{
        //重置状态
        isError.value = false;
        isLoading.value = true;
        //获取天地图WMTS图层
        const [timg, tpoi] =  getTiandituWMTS(key, 'vec_w', 'cva_w');
        fetch(timg.getTileUrl(new Point(429, 183))).then((res) => {
        new Map("map",{
            preferCanvas:true,
            minZoom: 3,
            maxZoom:18,
            attributionControl: false,
            zoomControl: true,
            layers: [timg,tpoi],
            center:[44.817831,123.08026],
            zoom:9,
            worldCopyJump:true,
            });
        isLoading.value = false;
        isError.value = false;
                 }).catch((err) => {
                     console.log(err);
                     isError.value = true;
                });
    }
    catch(err){
        console.log(err);
        isError.value = true;
    }
}
onMounted(() => {
    //加载地图
    loadMap(import.meta.env.VITE_TXXXNDIT_KEY);
});
const key = import.meta.env.VITE_TIANDITU_KEY;
</script>

<template>
<div class="relative w-full h-full">
    <div v-if="isLoading" class="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-full"> {{ isError ? "未获取到天地图图层, 请输入天地图key!" : "加载中..."}} 
    <!-- 冗余设计-->
        <br>
        <el-input v-model="inputKey" @change="loadMap(inputKey)" :placeholder="placeholder"/>
        <br>
        <br>
        <div class="buttons flex flex-row gap-2 ">
            <el-button @click="loadMap(inputKey)" type="primary" >重新加载</el-button>
            <el-button @click="loadMap(key)" type="primary" @mouseenter="placeholder = key" @mouseleave="placeholder = '请前往天地图官网获取'">使用临时秘钥</el-button>
            <el-button 
            type="primary" 
            tag="a" 
            href="http://lbs.tianditu.gov.cn/server/MapService.html" 
            target="_blank" style="color: white;text-decoration: none;"> 前往天地图官网 </el-button> 
        </div>
    </div>
    <div id="map" class="w-[98%] h-[98%] left-[1%] top-[1%] focus-visible:outline-none z-0">

    </div>
</div>
</template>

<style scoped>
</style>
js
import { TileLayer} from "leaflet";
export function getTiandituWMTS(key, imgType, poiType){
    /**
     * 获取天地图地图WMTS图层的方法
     *@param {string} key:天地图开发者密钥
     *@param {string} imgType:影像类型字符串
     *@param {string} poiType:注记类型字符串
     *@return {WebTileLayer[]}: WebTileLayer array 
     */
    if( key === undefined) {
        throw new Error("请输入天地图开发者密钥!");
    }
    const imgLayer = imgType.split("_")[0];
    const imgFormat = imgType.split("_")[1];
    const poiLayer = poiType.split("_")[0];
    const poiFormat = poiType.split("_")[1];
    const Tianditu_poi = new TileLayer(
        "https://{s}.tianditu.gov.cn/"
        + poiType + "/wmts?f=pbf&SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=" 
        + poiLayer  + "&STYLE=default&TILEMATRIXSET=" 
        + poiFormat + "&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=" 
        + key,
            {
                subdomains:["t0", "t1", "t2", "t3","t4", "t5", "t6", "t7"]
            }
    )
    const Tianditu_img = new TileLayer(
            "https://{s}.tianditu.gov.cn/"
            + imgType + "/wmts?f=pbf&SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=" 
            + imgLayer  + "&STYLE=default&TILEMATRIXSET=" 
            + imgFormat + "&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=" 
            + key,
            {
                subdomains:["t0", "t1", "t2", "t3","t4", "t5", "t6", "t7"],
            }
    )
       return [Tianditu_img, Tianditu_poi];
}

iclient加载iServer地图

这是另一种加载地图的方式,对于Map对象,我们仍可以使用由leaflet创建的Map,但图层则使用SuperMap iClient来创建。
在这种方式下,只有图层的创建方式不是原生leaflet具有的。
具体而言:

点击展开源代码
vue
<script setup >
import {Map, CRS, ControlPosition} from 'leaflet';
import { onMounted } from 'vue';
import { TiledMapLayer } from '@supermap/iclient-leaflet';
import "@geoman-io/leaflet-geoman-free/dist/leaflet-geoman.css"
import "@geoman-io/leaflet-geoman-free"

const url = "https://iserver.supermap.io/iserver/services/map-world/rest/maps/World";
onMounted(() => {
 const map = new Map('supermap',{
    crs: CRS.EPSG4326,
    center: [0, 0],
    maxZoom: 18,
    zoom: 1,
    attributionControl:false
})
const layer = new TiledMapLayer(url);
layer.addTo(map);


//捕捉绘制控件参数设置
const pmOptions = {
    position: 'topright',
    drawMarker: true,
    drawPolygon: true,
    drawPolyline: true,
    editPolygon: true,
    deleteLayer: true
}
map.pm.addControls(pmOptions);
map.pm.setLang("zh");
// 监听绘制事件
map.on('pm:create',({shape, layer})=>{
    console.log(shape, layer);
    
})
})
</script>

<template>
    <div id="supermap" class="w-full h-full z-0">
        
    </div>
</template>

<style scoped>

</style>

进阶

TIP

显然,这里的地图右边出现了一列控件。貌似上述步骤并不能产生这样的效果。
是的,我加入了一个新的模块,实现了地图量测的面板。 查看源代码就可以发现,我引入了一个第三方库:@geoman-io/leaflet-geoman-free
这个库的安装使用非常简单,只需要以下几步:

cmd
> npm install @geoman-io/leaflet-geoman-free
js
import "@geoman-io/leaflet-geoman-free/dist/leaflet-geoman.css"
import "@geoman-io/leaflet-geoman-free"
js
//在你的js代码中
//捕捉绘制控件参数设置
const pmOptions = {
    position: 'topright',
    drawMarker: true,
    drawPolygon: true,
    drawPolyline: true,
    editPolygon: true,
    deleteLayer: true
}
map.pm.addControls(pmOptions);//挂载控件
map.pm.setLang("zh");//设置语言

至此,已实现了可涂画的功能,实际上我已经将捕获数据的代码写在了源代码中,假如把这些捕获到的数据传递给某个文本框,是不是就实现了量测+结果显示的功能呢?
完整代码将在本章最后提供,你可以先尝试一下自己实现,记得充分利用Tailwindcss 和 Element-plus来改善你的UI界面。

我的辫子长在头上,诸君的辫子长在心里。