OpenLayers 中地图底图添加滤镜

232 阅读2分钟

在现代地图应用中,除了地图本身的地理信息外,视觉效果也变得越来越重要。OpenLayers 是一个功能强大的开源地图库,它不仅可以轻松加载地图数据,还可以通过一些技巧为地图添加各种视觉效果,比如滤镜。本文将分享如何使用 OpenLayers 为地图底图添加滤镜,让地图看起来更加独特和美观。

545f3d59d9f7769e394d5665bf396c18.png

一、准备工作

在开始之前,你需要确保已经安装了 OpenLayers。如果你还没有安装,可以通过 npm 安装:

 pnpm install ol

此外,还需要一个地图服务的 URL 和访问令牌(token)。出于安全考虑,这里不会展示具体的 URL 和 token,但你需要从你的地图服务提供商那里获取它们。

二、创建地图

首先,我们需要创建一个地图实例。以下是一个基本的地图初始化代码:

 import Map from "ol/Map";
 import View from "ol/View";
 ​
 // 地图服务的 URL
 const mapServerUrl = "你的地图服务 URL";
 ​
 // 地图底图的 URL 模板
 const url = `${mapServerUrl}/{z}/{x}/{y}.png`;
 ​
 // 创建地图
 const map = new Map({
     target: "map", // 地图容器的 ID
     view: new View({
         center: [116.391046, 39.913607], // 地图中心点
         zoom: 8, // 地图初始缩放级别
     }),
 });

三、为地图添加滤镜

OpenLayers 提供了强大的地图渲染功能,但默认情况下并不支持直接为地图添加滤镜。不过,我们可以通过一些技巧来实现这一功能。

1. 创建滤镜

我们可以通过 HTML5 的 <canvas> 元素和 CanvasRenderingContext2Dfilter 属性来实现滤镜效果。以下是一个简单的滤镜配置示例:

 const tileImageFilter = {
     grayscale: 98, // 灰度
     invert: 100, // 反色
     sepia: 20, // 褐色
     "hue-rotate": 180, // 色相旋转
     saturate: 1600, // 饱和度
     brightness: 80, // 亮度
     contrast: 90, // 对比度
     opacity: 100, // 透明度
     blur: 0, // 模糊
 };
 ​
 function createCtxFilter(filterOptions) {
     return Object.entries(filterOptions)
         .map(([key, value]) => {
             let normalizedValue = null;
             if (typeof value === "number") {
                 if (key === "blur") {
                     normalizedValue = `${value}px`;
                 } else if (key === "hue-rotate") {
                     normalizedValue = `${value}deg`;
                 } else {
                     normalizedValue = `${value}%`;
                 }
             } else {
                 normalizedValue = value;
             }
 ​
             return `${key}(${normalizedValue})`;
         })
         .filter((v) => v !== null)
         .join(" ");
 }

2. 实现滤镜功能

我们需要在地图加载每个瓦片时,将瓦片绘制到 <canvas> 上,并应用滤镜。以下是实现代码:

方法一:使用 tileLoadFunction(已废弃,但仍然可以参考)

 import TileLayer from "ol/layer/Tile";
 import { XYZ } from "ol/source";
 ​
 const canvas = document.createElement("canvas");
 const ctx = canvas.getContext("2d");
 ​
 const xyzSource = new XYZ({
     url,
     projection: "EPSG:3857",
     tileLoadFunction: function (tile, url) {
         const image = new Image();
         image.src = url;
         image.crossOrigin = "Anonymous";
 ​
         image.onload = function () {
             canvas.width = image.width;
             canvas.height = image.height;
 ​
             ctx.clearRect(0, 0, image.width, image.height);
             ctx.filter = createCtxFilter(tileImageFilter);
             ctx.drawImage(image, 0, 0);
             tile.getImage().src = canvas.toDataURL();
         };
     },
 });
 ​
 map.addLayer(
     new TileLayer({
         source: xyzSource,
         background: "rgba(0,0,0,1)",
     }),
 );

方法二:使用 ImageTileSource(推荐)

 import TileLayer from "ol/layer/Tile";
 import ImageTileSource from "ol/source/ImageTile";
 import { renderXYZTemplate, pickUrl } from "ol/uri";
 ​
 const canvas = document.createElement("canvas");
 const ctx = canvas.getContext("2d");
 ​
 function loadImage(template, z, x, y, options) {
     return new Promise((resolve, reject) => {
         const image = new Image();
         image.crossOrigin = options.crossOrigin ?? null;
         image.addEventListener("load", () => {
             canvas.width = image.width;
             canvas.height = image.height;
             ctx.clearRect(0, 0, image.width, image.height);
             ctx.filter = createCtxFilter(tileImageFilter);
             ctx.drawImage(image, 0, 0);
 ​
             const img = new Image();
             img.src = canvas.toDataURL();
             resolve(img);
         });
         image.addEventListener("error", () => reject(new Error("Image failed to load")));
         image.src = renderXYZTemplate(template, z, x, y, options.maxY);
     });
 }
 ​
 const xyzSource = new ImageTileSource({
     projection: "EPSG:3857",
     loader: function (z, x, y, options) {
         const template = pickUrl([url], z, x, y);
         return loadImage(template, z, x, y, options);
     },
 });
 ​
 map.addLayer(
     new TileLayer({
         source: xyzSource,
         background: "rgba(0,0,0,1)",
     }),
 );

四、总结

通过上述方法,我们可以在 OpenLayers 中为地图底图添加各种滤镜效果。虽然 OpenLayers 没有直接支持滤镜的 API,但通过结合 HTML5 的 <canvas>CanvasRenderingContext2Dfilter 属性,我们可以轻松实现这一功能。你可以根据自己的需求调整滤镜参数,创造出独特的地图视觉效果。

希望这篇文章对你有所帮助!如果你有任何问题或建议,欢迎在评论区留言。