openlayers实现遮罩的三种方式
在平时的gis开发常常会遇到做地图的遮罩层。有原生的图层数据的话可以直接在gis桌面程序中提取出需要展示的区域。但一些不太方便的图层可以直接使用openlayer提供的api进行遮罩层的开发。经过不同的实验提取出了三种不同的方式来实现遮罩层。
使用LinearRing
主要思路
使用LinearRing可以实现类似环的效果,那么把比如本文使用的贵州的边界线数据来生成一个LinearRing就可以实现遮罩层。但是LinearRing是不能单独存在的,需要创建一个polygon,并使用appendLinearRing这个方法把LinearRing添加进去。而创建的polygon则可以是直接使用[-90,90],[-180,180]这样的extent来生成。
主要代码
import {Map, View} from 'ol';
import {Tile as TileLayer, Image as ImageLayer,Vector as VectorLayer} from 'ol/layer';
import {OSM, XYZ, ImageStatic, ImageCanvas,Vector as VectorSource } from 'ol/source';
import {getVectorContext} from 'ol/render';
import {transform, getTransform} from 'ol/proj';
import GeoJSON from 'ol/format/GeoJSON';
import {Polygon, LinearRing} from 'ol/geom';
import {fromExtent} from 'ol/geom/Polygon';
import Feature from 'ol/Feature';
import {Circle as CircleStyle, Fill, RegularShape, Stroke, Style, Text} from 'ol/style';
function erase(geom) {
// const extent = [-180,-90,180,90];
const Max=transform([-180,-90], 'EPSG:4326', 'EPSG:3857');
const Min=transform([180,90], 'EPSG:4326', 'EPSG:3857');
const extent = [Max[0],Max[1],Min[1],Min[1]];
const polygonRing = fromExtent(extent);
geom.applyTransform(getTransform('EPSG:4326', 'EPSG:3857'));
const coords = geom.getCoordinates();
coords.forEach(coord =>{
const linearRing = new LinearRing(coord?.[0]);
polygonRing.appendLinearRing(linearRing);
})
return polygonRing;
}
function addconver(converLayer,data) {
const fts = new GeoJSON().readFeatures(data);
const ft = fts?.[0];
const converGeom = this.erase(ft.getGeometry());
const convertFt = new Feature({
geometry: converGeom
});
converLayer.getSource().addFeature(convertFt);
}
代码解析
在本文档中使用的是geojson数据。在addconver函数中对geojson数据进行解析(可以将geojson数据后缀名改为.json并直接import),解析出贵州的边框数据后在erase函数中生成linearing并添加到覆盖整个图的polygon中。最后将生成的layer添加到map对象中即可。尤其要注意投影的变换。
使用canvas
主要思路
借助openlayers提供的ImageCanvasSource 来添加canvas图层,数据源依旧采用的是贵州geojson文件。添加canvas后利用geojson解析出的边界坐标点使用canvas的api生成一个面,并使用canvas的'destination-out'来实现中空的一个效果。
主要代码
function render(geo_data){
const fts = new GeoJSON().readFeatures(geo_data);
const ft = fts?.[0];
const coords = ft.getGeometry().getCoordinates();
const data = coords?.[0]?.[0];
//参数1:extent array 左下角投影坐标与右上角投影坐标
//参数2:resolution 要生产图像的分辨率
//参数3:设备像素比
//参数4:图像实际大小
//参数5:投影
return function(extent, resolution, pixelRatio, size, projection){
//每次的范围变动都会引起重绘,从而触发该回调函数,
const [width, height] = size; //画布尺寸
const [left, bottom, right, top] = extent; //坐标投影
const xScale = width / (right - left); //画布尺寸与坐标投影比
const yScale = height / (top - bottom);
const transform = getTransform('EPSG:4326', 'EPSG:3857');
const canvas=document.createElement('canvas');
canvas.width=width;
canvas.height=height;
const ctx = canvas.getContext('2d');
ctx.fillStyle = "rgba(255,255,255,1)";
ctx.fillRect(0,0,width,height);
ctx.globalCompositeOperation = 'destination-out';
ctx.beginPath();
data.forEach((coor,index)=>{
const [lon,lat] = transform(coor);
const x = (lon - left) * xScale; //转换成手机次尺寸的xy
const y = (top - lat) * yScale;
index===0?ctx.moveTo(x, y):1;
ctx.lineTo(x,y);
})
// ctx.strokeStyle = 'rgba(0,0,0,0.5)';
ctx.fill();
// ctx.restore();
// ctx.fill();
return canvas;
}
}
const imageCanvas=new ImageCanvas({
//创建回调函数如下
//data为geojson
canvasFunction:render(data),
});
const imageLayer=new ImageLayer({
source:imageCanvas
});
代码解析
上面render函数中的坐标转canvas坐标是在网上找的,代码的思路还是很清晰的,只需要注意ctx.globalCompositeOperation = 'destination-out';
使用图层事件和canvas
这种方式也是使用canvas和'destination-out',但是使用的是openlayer自带的api,使用起来会简单和方便许多,就是第二种方式的进化版。使用图层的'postrender'事件,在图层加载完成之后获取上下文并添加。
主要代码
function addPolyon(converLayer,geo_data){
const fts = new GeoJSON().readFeatures(geo_data);
const ft = fts?.[0];
ft.getGeometry().applyTransform(getTransform('EPSG:4326', 'EPSG:3857'))
converLayer.getSource().addFeature(ft);
return converLayer;
}
//获取图层
const _layer = map.getLayers().getArray()?.[1];
const newcliplayer = addPolyon(clipLayer,data);
_layer.on('postrender', e=> {
e.context.globalCompositeOperation = 'destination-in';
const vectorContext = getVectorContext(e);
newcliplayer.getSource().forEachFeature(feature=>{
vectorContext.drawFeature(feature, style);
});
e.context.globalCompositeOperation = 'source-over';
});
总结
使用第三种方式无论是在性能还是代码量来说都是优势,第三种也是ol官方推荐的一种。