加载地图
原生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具有的。
具体而言:
- 第一步,拿到地图服务的url:https://iserver.supermap.io/iserver/services/map-world/rest/maps/World
- 第二步,创建Map对象或使用已存在的。
- 第三步,创建TiledMapLayer图层
- 最后,挂载图层到Map对象上
点击展开源代码
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界面。