一、概要
在前文中,探讨了Openlayers自定义瓦片网格(tileUrlFunction)的实现及其应用。本文将继续探究 自定义瓦片加载(tileLoadFunction)的使用,并探讨其实际应用场景。
tileLoadFunction 是自定义地图瓦片加载过程的函数。默认情况下,地图框架自动从指定的 URL 加载瓦片图像。此时可以对加载的图像进行额外处理,如添加水印、修改图像效果和添加元数据信息等。
二、 自定义瓦片加载探究
- 加载谷歌影像:
new XYZ({})实际可以接受一个 tileLoadFunction 参数,用于自定义瓦片加载行为,其默认执行的函数是这样的:
const map = new Map({
target,
layers: [
new TileLayer({
source: new XYZ({
url: 'https://www.google.com/maps/vt?lyrs=y&gl=cn&x={x}&y={y}&z={z}',
tileLoadFunction, // 设置自定义的 tileLoadFunction
crossOrigin: 'anonymous',
wrapX: true,
})
})
],
view: new View({
projection: 'EPSG:3857', // 坐标系EPSG:4326或EPSG:3857
zoom: 0, // 打开页面时默认地图缩放级别
center: fromLonLat([121.5, 25]), // 转到墨卡托坐标系
})
})
// tileLoadFunction 的默认返回
function tileLoadFunction(imageTile: Tile, src: string) {
if (imageTile instanceof ImageTile) {
//获取图像元素
const image = imageTile.getImage() as HTMLImageElement;
// 设置图像源
image.src = src;
}
加载结果:
tileLoadFunction 接受两个参数:
① imageTile:当前正在加载的瓦片对象。通常是一个 Tile
实例。
② src:瓦片的源 URL。
tileLoadFunction 可以替换为自定义加载方法
- 作用:
①自定义瓦片内容:对每个瓦片绘制水印、标记或图形等内容。
②图像处理:对瓦片调整亮度、对比度,或添加滤镜效果。 - 使用场景:
①添加水印:在地图瓦片上添加标识性水印。
②图像修改:调整瓦片的色调,以符合应用的视觉风格。
③特殊效果:在瓦片上叠加半透明的图层,来实现特定的视觉效果。
④数据可视化:在瓦片上添加数据相关的图形或标记。
三、 使用场景
- 添加水印
<template>
<!--地图-->
<div ref="mapContainer" class="mapContainer" id="mapContainer"></div>
</template>
<script lang="ts" setup>
import { onMounted, shallowRef } from 'vue'
import { View, Map, ImageTile } from "ol"
import { fromLonLat } from 'ol/proj'
import TileLayer from 'ol/layer/Tile'
import { XYZ } from 'ol/source'
import Tile, { LoadFunction } from 'ol/Tile'
// 地图容器
const mapContainer = shallowRef<HTMLDivElement>()
// 地图对象
const map = shallowRef<Map>()
/**
* @description 创建地图实例
* @param {Document | DocumentId} target 地图容器
* @returns 地图对象
*/
const createMap = function (target: HTMLElement | string,): Map {
// 创建地图
const map = new Map({
target,
layers: [
new TileLayer({
source: new XYZ({
url: 'https://www.google.com/maps/vt?lyrs=y&gl=cn&x={x}&y={y}&z={z}',
tileLoadFunction,
crossOrigin: 'anonymous',
wrapX: true,
})
})
],
view: new View({
projection: 'EPSG:3857', // 坐标系EPSG:4326或EPSG:3857
zoom: 0, // 打开页面时默认地图缩放级别
center: fromLonLat([121.5, 25]), // 转到墨卡托坐标系
})
})
return map
}
/**
* @description 自定义瓦片加载函数,每个瓦片增加自定义水印。
* @param {Tile} imageTile 当前正在加载的瓦片对象。通常是一个 `Tile` 实例。
* @param {string} src 瓦片的源 URL。
*/
function tileLoadFunction(imageTile: Tile, src: string) {
if (tile instanceof ImageTile) {
// 创建一个新的 Image 对象用于加载瓦片图像
const tileImage = new Image();
tileImage.crossOrigin = 'anonymous'; // 允许跨域请求
// 获取瓦片的目标图像对象
const waterMakeImage = tile.getImage() as HTMLImageElement;
// 创建 Canvas 元素用于绘制和处理图像
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
// 确保在设置图像源之前,onload 事件处理函数已经设置
tileImage.onload = () => {
if (context) {
// 设置 Canvas 的宽度和高度为图像的尺寸
canvas.width = tileImage.width;
canvas.height = tileImage.height;
// 在 Canvas 上绘制加载的图像
context.drawImage(tileImage, 0, 0);
// 添加水印
context.font = '30px Arial'; // 设置字体样式
context.fillStyle = 'rgba(255, 255, 255, 0.8)'; // 设置半透明白色
context.textAlign = 'center'; // 水平居中
context.textBaseline = 'middle'; // 垂直居中
// 在 Canvas 中绘制水印
context.fillText('自定义水印', canvas.width / 2, canvas.height / 2);
// 将 Canvas 内容转换为 Blob 对象
canvas.toBlob((blob) => {
if (blob) {
// 创建一个 Object URL 以供图像源使用
const objectURL = URL.createObjectURL(blob);
// 设置瓦片图像的源,触发图像的 onload 事件
waterMakeImage.src = objectURL;
// 确保在图像加载完成后释放 Object URL
waterMakeImage.onload = () => {
URL.revokeObjectURL(objectURL);
};
}
}, 'image/png');
}
};
// 设置图像源,开始加载图像
tileImage.src = src;
}
};
onMounted(async () => {
map.value = createMap(mapContainer.value!);
})
</script>
<style lang="scss">
#mapContainer {
position: absolute;
top: 0;
z-index: 0;
width: 100%;
height: 100%;
}
</style>
效果:
- 图像修改(仅放 tileLoadFunction ,替换即可,下同)
/**
* @description 自定义瓦片加载函数,修改瓦片为灰度图像。
* @param {Tile} imageTile 当前正在加载的瓦片对象。通常是一个 `Tile` 实例。
* @param {string} src 瓦片的源 URL。
*/
function tileLoadFunction(imageTile: Tile, src: string) {
if (imageTile instanceof ImageTile) {
const image = imageTile.getImage() as HTMLImageElement;
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
const img = new Image();
img.crossOrigin = 'anonymous';
img.onload = () => {
if (context) {
canvas.width = img.width;
canvas.height = img.height;
context.drawImage(img, 0, 0);
// 转换为灰度图像
const imageData = context.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
data[i] = avg; // 红色通道
data[i + 1] = avg; // 绿色通道
data[i + 2] = avg; // 蓝色通道
}
context.putImageData(imageData, 0, 0);
// 将 Canvas 内容转换为 Blob 对象
canvas.toBlob((blob) => {
if (blob) {
const objectURL = URL.createObjectURL(blob);
image.src = objectURL;
image.onload = () => {
URL.revokeObjectURL(objectURL);
};
}
}, 'image/png');
}
};
img.src = src;
}
}
效果:
- 添加遮罩
/**
* @description 自定义瓦片加载函数,添加绿色半透明遮罩。
* @param {Tile} imageTile 当前正在加载的瓦片对象。通常是一个 `Tile` 实例。
* @param {string} src 瓦片的源 URL。
*/
function tileLoadFunction(imageTile: Tile, src: string) {
if (imageTile instanceof ImageTile) {
const image = imageTile.getImage() as HTMLImageElement;
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
const img = new Image();
img.crossOrigin = 'anonymous';
img.onload = () => {
if (context) {
canvas.width = img.width;
canvas.height = img.height;
context.drawImage(img, 0, 0);
// 添加半透明阴影层
context.fillStyle = 'rgba(0, 255, 0, 0.3) '; // 半透明绿
context.fillRect(0, 0, canvas.width, canvas.height);
// 将 Canvas 内容转换为 Blob 对象
canvas.toBlob((blob) => {
if (blob) {
const objectURL = URL.createObjectURL(blob);
image.src = objectURL;
image.onload = () => {
URL.revokeObjectURL(objectURL);
};
}
}, 'image/png');
}
};
img.src = src;
}
}
效果:
- 添加元数据信息
/**
* @description 自定义瓦片加载函数,添加瓦片坐标和分界。
* @param {Tile} imageTile 当前正在加载的瓦片对象。通常是一个 `Tile` 实例。
* @param {string} src 瓦片的源 URL。
*/
function tileLoadFunction(imageTile: Tile, src: string) {
if (imageTile instanceof ImageTile) {
const image = imageTile.getImage() as HTMLImageElement;
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
// 创建一个新的图像对象并加载图像
const img = new Image();
img.crossOrigin = 'anonymous';
// 从瓦片源 URL 中提取 X、Y 和 Z 坐标
const urlPattern = /x=(\d+)&y=(\d+)&z=(\d+)/;
const match = src.match(urlPattern);
const x = match ? match[1] : 'unknown';
const y = match ? match[2] : 'unknown';
const z = match ? match[3] : 'unknown';
img.onload = () => {
if (context) {
// 设置 Canvas 尺寸以匹配图像
canvas.width = img.width;
canvas.height = img.height;
// 将图像绘制到 Canvas 上
context.drawImage(img, 0, 0);
// 绘制白色边框
context.strokeStyle = 'white'; // 边框颜色
context.lineWidth = 2; // 边框宽度
context.strokeRect(0, 0, canvas.width, canvas.height); // 绘制边框
// 添加瓦片元数据信息
context.font = '25px Arial';
context.fillStyle = 'white'; // 白色文本
context.textAlign = 'left'; // 水平左对齐
context.textBaseline = 'top'; // 垂直顶部对齐
// 绘制瓦片信息
context.fillText(`X: ${x}`, 10, 10); // X 坐标
context.fillText(`Y: ${y}`, 10, 40); // Y 坐标
context.fillText(`Z: ${z}`, 10, 70); // Z 坐标
// 可以添加更多的元数据或视觉效果
// 例如显示经纬度或瓦片的范围
// 将 Canvas 内容转换为 Blob 对象
canvas.toBlob((blob) => {
if (blob) {
const objectURL = URL.createObjectURL(blob);
image.src = objectURL; // 更新瓦片的图像源
image.onload = () => {
URL.revokeObjectURL(objectURL); // 释放 Object URL
};
}
}, 'image/png');
}
};
img.src = src; // 设置图像源并开始加载
}
}
效果: