文章目录
前言
二次封装OpenLayers加载9种类型的图层,支持天地图图层、矢量图层、贴图图层、wms图层、wmts图层、cog图层、wkt图层、瓦片贴图图层、瓦片图层的加载。
提示:瓦片贴图图层和瓦片图层的封装思路,由五层十五级遥感数据切分而来
一、使用步骤
1.引入库
import { getCogStatistics, getCogBounds } from "./ol-api.js";
import { Stroke, Style, Fill, Circle, Text } from 'ol/style';
import { transformExtent } from 'ol/proj';
import { Vector as VectorLayer, Tile, Image as ImageLayer } from 'ol/layer';
import { Vector as VectorSource, XYZ, ImageStatic, ImageWMS, WMTS } from 'ol/source';
import WMTSTileGrid from 'ol/tilegrid/WMTS';
import { LineString, Polygon, MultiLineString } from 'ol/geom'
2.WKT转Feature方法(ol-map.js)
/**WKT转Feature
* @param {String} wkt
* @param {String} dataCrs 数据坐标 'EPSG:4326'
* @param {String} sysCrs 系统坐标系 'EPSG:3857'
*/
export function wktToFeature (wkt, dataCrs, sysCrs) {
// 解析 wkt字符串
var feature = new WKT().readFeature(wkt, {
dataProjection: dataCrs, // 数据坐标
featureProjection: sysCrs // 系统坐标系
});
return feature;
}
3.封装常用样式方法(ol-map.js)
/**创建样式
* @param {*} feature
* @param {*} resolution
* @param {*} style
* @returns
*/
export function crtStyle ({
resolution = 300,
style = null,
feature
} = {}) {
var iconSize = 300 / resolution;
var featureStyle = null;
switch (style.type) {
// 几何控方未选择样式
case 'unSelected':
featureStyle = new Style({
stroke: new Stroke({
color: '#54FF9F',
lineDash: [0],
width: 2
}),
fill: new Fill({
color: 'rgb(158,197,255,0.2)'
}),
image: new Circle({
radius: 6,
fill: new Fill({
color: 'blue'
}),
stroke: new Stroke({
color: '#ffffff',
width: 1.5
})
}),
text: new Text({ // 文本样式
text: '10',
offsetX: 0 / resolution,
offsetY: 0 / resolution,
textAlign: 'start',
textBaseline: 'bottom',
font: iconSize + 'px Calibri,sans-serif',
fill: new Fill({
color: '#000'
}),
stroke: new Stroke({
color: '#fff',
width: 3
})
}),
geometry: function (feature) {
var center = feature.getGeometry().getCoordinates();
const top = [center[0], center[1] + 300];
const bottom = [center[0], center[1] - 300];
const left = [center[0] - 300, center[1]];
const right = [center[0] + 300, center[1]];
const geo = [
[top, bottom],
[left, right]
];
return new MultiLineString(geo);
}
});
break;
// 几何控方选择样式
case 'Selected':
featureStyle = [new Style({
stroke: new Stroke({
color: '#54FF9F',
lineDash: [0],
width: 2
}),
text: new Text({ // 文本样式
text: '10',
offsetX: 0 / resolution,
offsetY: 0 / resolution,
textAlign: 'start',
textBaseline: 'bottom',
font: iconSize + 'px Calibri,sans-serif',
fill: new Fill({
color: '#000'
}),
stroke: new Stroke({
color: '#fff',
width: 3
})
}),
geometry: function (feature) {
var center = feature.getGeometry().getCoordinates();
const top = [center[0], center[1] + 300];
const bottom = [center[0], center[1] - 300];
const left = [center[0] - 300, center[1]];
const right = [center[0] + 300, center[1]];
return new MultiLineString([
[top, bottom],
[left, right]
]);
}
}),
new Style({
stroke: new Stroke({
color: '#54FF9F',
lineDash: [10, 5],
width: 2
}),
geometry: function (feature) {
var center = feature.getGeometry().getCoordinates();
const leftTop = [center[0] - 350, center[1] + 350];
const leftBottom = [center[0] - 350, center[1] - 350];
const rightBottom = [center[0] + 350, center[1] - 350];
const rightTop = [center[0] + 350, center[1] + 350];
return new Polygon([
[
leftTop, leftBottom, rightBottom, rightTop
]
]);
}
})
];
break;
//绘制样式
case 'draw':
featureStyle = new Style({
stroke: new Stroke({
color: '#F25521',
lineDash: [0],
width: 0.9
}),
fill: new Fill({
color: 'rgb(158,197,255,0.2)'
}),
image: new Circle({
radius: 6,
fill: new Fill({
color: 'blue'
}),
stroke: new Stroke({
color: '#ffffff',
width: 1.5
})
})
});
break;
// 查询结果样式
case 'result':
featureStyle = new Style({
stroke: new Stroke({
color: 'blue',
lineDash: [0],
width: 0.9
}),
fill: new Fill({
color: 'rgb(158,197,255,0)'
}),
image: new Circle({
radius: 6,
fill: new Fill({
color: 'blue'
}),
stroke: new Stroke({
color: '#ffffff',
width: 1.5
})
}),
text: new Text({ // 文本样式
text: feature ? feature.get('name') : '',
offsetX: 0 / resolution,
offsetY: 0 / resolution,
textAlign: 'center',
font: 'bold 14px 宋体',
fill: new Fill({
color: 'black'
}),
backgroundFill: new Fill({
color: "#FDFDFD"
}),
placement: "point",
overflow: true //超出面的部分不显示
}),
});
break;
// 激活样式
case 'active':
featureStyle = new Style({
fill: new Fill({
color: '#F3B59D'
}),
});
break;
//覆盖图层样式
case 'coverage':
featureStyle = new Style({
stroke: new Stroke({
color: '#E6E6E6',
lineDash: [0],
width: 0.9
}),
fill: new Fill({
color: '#e6e6e6b0'
})
});
break;
//红色样式
case 'red':
featureStyle = new Style({
stroke: new Stroke({
color: 'red',
lineDash: [0],
width: 0.9
})
});
break;
//通用样式
case 'normal':
featureStyle = new Style({
stroke: new Stroke({
color: 'blue',
lineDash: [0],
width: 0.9
}),
fill: new Fill({
color: 'rgb(158,197,255,0)'
}),
});
break;
//高亮样式
case 'highligh':
featureStyle = new Style({
stroke: new Stroke({
color: '#43FF12',
lineDash: [0],
width: 0.9
})
});
break;
case 'GF1':
featureStyle = new Style({
stroke: new Stroke({
color: '#0000FF',
lineDash: [0],
width: 0.9
}),
fill: new Fill({
color: 'rgb(158,197,255,0)'
}),
});
break;
case 'GF6':
featureStyle = new Style({
stroke: new Stroke({
color: '#FFFF00',
lineDash: [0],
width: 0.9
}),
fill: new Fill({
color: 'rgb(158,197,255,0)'
}),
});
break;
case 'Sentinel-2':
featureStyle = new Style({
stroke: new Stroke({
color: '#F24F4E',
lineDash: [0],
width: 0.9
}),
fill: new Fill({
color: 'rgb(158,197,255,0)'
}),
});
break;
case 'LandSat 8':
featureStyle = new Style({
stroke: new Stroke({
color: '#00FFC6',
lineDash: [0],
width: 0.9
}),
fill: new Fill({
color: 'rgb(158,197,255,0)'
}),
});
break;
case 'MODIS':
featureStyle = new Style({
stroke: new Stroke({
color: '#F59B22',
lineDash: [0],
width: 0.9
}),
fill: new Fill({
color: 'rgb(158,197,255,0)'
}),
});
break;
default:
break;
}
return featureStyle;
}
4.封装各个图层加载方法(ol-map.js)
/* 创建天地图图层
*@param:id {string} 【必选】图层标识
*@param:zIndex {string} 【必选】图层层级
*@param:type {string}:【必选】图层类型(影像底图img_w 注记cia_w;矢量vec_w cva_w)
*@param:url {string} 【必选】图层服务地址
*@param:tk {string} 【必选】天地图token
*@param:name {string} 【非必选】图层名称
*@param:dataCrs {string} 【非必选】数据投影
*@param:sysCrs {string} 【非必选】系统投影
*@param:opacity {Number} 0-1 【非必选】透明度
*@param:crossOrigin {string} 【非必选】跨域
*@param:extent {Array} 【非必选】[minlon,minlat,maxlon,maxlat]
*@param:visible {Boolean} 【非必选】是否可见
*return:layer {object}
*/
export function crtTdtLayer ({
id = '',
zIndex = 0,
type = '',
url = 'http://t{0-7}.tianditu.gov.cn/DataServer',
tk = '8886226d608f5907e9f245dec2c6d9f9',
dataCrs = 'EPSG:4326',
sysCrs = 'EPSG:3857',
opacity = 1,
crossOrigin = 'anonymous',
name = '',
extent = [-180.0, -90.0, 180.0, 90.0],
visible = true
} = {}) {
const layer = new Tile({
source: new XYZ({
url: url + '?T=' + type + '&x={x}&y={y}&l={z}&tk=' + tk,
projection: sysCrs,
crossOrigin: crossOrigin
}),
extent: transformExtent(extent, dataCrs, sysCrs), // 范围
opacity,
zIndex,
name,
id,
visible
});
return layer;
}
/* 创建矢量图层
*@param:id {string} 【必选】标识
*@param:zIndex {string} 【必选】层级
*@param:name {string} 【非必选】图层名称
*@param:style {object} 【非必选】图层样式
*@param:extent {Array} 【非必选】[minlon,minlat,maxlon,maxlat]
*@param:dataCrs {string} 【非必选】数据投影
*@param:sysCrs {string} 【非必选】系统投影
*@param:opacity {Number} 0-1 【非必选】透明度
*@param:crossOrigin {string} 【非必选】跨域
*@param:visible {Boolean} 【非必选】是否可见
*return:layer {object}
*/
export function crtVectorLayer ({
id = '',
zIndex = 0,
name = '',
style = {
type: ''
},
extent = [-180.0, -90.0, 180.0, 90.0],
dataCrs = 'EPSG:4326',
sysCrs = 'EPSG:3857',
opacity = 1,
crossOrigin = 'anonymous',
visible = true
} = {}) {
const vectorLayer = new VectorLayer({
source: new VectorSource({
wrapX: true,
crossOrigin: crossOrigin
}),
style: function (feature, resolution) {
return crtStyle({
resolution,
style,
feature
});
},
opacity,
zIndex,
name,
id,
visible,
extent: transformExtent(extent, dataCrs, sysCrs), // 范围
});
return vectorLayer;
}
/* 创建贴图图层
*@param:id {string} 【必填】标识
*@param:zIndex {string} 【必填】层级
*@param:url {string} 【必填】图片地址
*@param:extent {Array} 【必选】[minlon,minlat,maxlon,maxlat]
*@param:name {string} 【非必选】图层名称
*@param:dataCrs {string} 【非必选】数据投影
*@param:sysCrs {string} 【非必选】系统投影
*@param:opacity {Number} 0-1 【非必选】透明度
*@param:crossOrigin {string} 【非必选】跨域
*@param:visible {Boolean} 【非必选】是否可见
*return:layer
*/
export function crtImageLayer ({
id = '',
zIndex = 0,
url = '',
extent = [-180.0, -90.0, 180.0, 90.0],
name='',
dataCrs = 'EPSG:4326',
sysCrs = 'EPSG:3857',
opacity = 1,
crossOrigin = 'anonymous',
visible = true
} = {}) {
const source = new ImageStatic({
url,
proj: sysCrs,
imageExtent: transformExtent(extent, dataCrs, sysCrs),
crossOrigin,
})
const imageLayer = new ImageLayer({
source: source,
zIndex,
opacity,
id,
name,
visible,
extent: transformExtent(extent, dataCrs, sysCrs), // 范围
})
return imageLayer;
}
/* 创建WMS图层
*@param:id {string} 【必选】标识
*@param:zIndex {string} 【必选】层级
*@param:url {string} 【必选】地址
*@param:workspaces {string} 【必选】工作空间
*@param:layerName {string} 【必选】图层名称
*@param:bbox {string} 【非必选】数据的区域
*@param:format {string} 【非必选】返回数据格式
*@param:extent {Array} 【必选】[minlon,minlat,maxlon,maxlat]
*@param:name {string} 【非必选】图层名称
*@param:dataCrs {string} 【非必选】数据投影
*@param:sysCrs {string} 【非必选】系统投影
*@param:opacity {Number} 0-1 【非必选】透明度
*@param:crossOrigin {string} 【非必选】跨域
*@param:visible {Boolean} 【非必选】是否可见
*return:layer {}
*/
export function crtWMSLayer ({
id = '',
zIndex = 0,
url = '',
workspaces = '',
layerName = '',
bbox = '',
format = 'image/png',
opacity = 1,
crossOrigin = 'anonymous',
name = '',
dataCrs = 'EPSG:4326',
sysCrs = 'EPSG:3857',
extent = [-180.0, -90.0, 180.0, 90.0],
visible = true
} = {}) {
const wmsLayer = new ImageLayer({
visible: true,
source: new ImageWMS({
// 不能设置为0,否则地图不展示。
ratio: 1,
url: url,
params: {
format,
layers: workspaces + ':' + layerName,
bbox,
exceptions: 'application/vnd.ogc.se_inimage',
QUERY_LAYERS: workspaces + ':' + layerName
},
crossOrigin,
serverType: 'geoserver'
}),
extent: transformExtent(extent, dataCrs, sysCrs), // 范围
opacity,
zIndex,
name,
id,
visible
});
return wmsLayer;
}
/** 加载wmts图层
* @param:id {string} 【必选】标识
* @param:zIndex {string} 【必选】层级
* @param:url {string} 【必选】地址
* @param:workspaces {string} 【必选】工作空间
* @param:layerName {string} 【必选】图层名称
* @param:format {string} 【非必选】返回数据格式
* @param:extent {Array} 【必选】[minlon,minlat,maxlon,maxlat]
* @param:name {string} 【非必选】图层名称
* @param:dataCrs {string} 【非必选】数据投影
* @param:sysCrs {string} 【非必选】系统投影
* @param:opacity {Number} 0-1 【非必选】透明度
* @param:crossOrigin {string} 【非必选】跨域
* @param:visible {Boolean} 【非必选】是否可见
* @returns {object}
*/
export function crtWMTSLayer ({
id = '',
zIndex = 0,
url = '',
workspaces = '',
layerName = '',
format = 'image/png',
extent = [-180.0, -90.0, 180.0, 90.0],
name = '',
opacity = 1,
crossOrigin = 'anonymous',
visible = true,
dataCrs = 'EPSG:4326',
sysCrs = 'EPSG:3857',
} = {}) {
// 切片名
const gridNames = [
'EPSG:4326:0',
'EPSG:4326:1',
'EPSG:4326:2',
'EPSG:4326:3',
'EPSG:4326:4',
'EPSG:4326:5',
'EPSG:4326:6',
'EPSG:4326:7',
'EPSG:4326:8',
'EPSG:4326:9',
'EPSG:4326:10',
'EPSG:4326:11',
'EPSG:4326:12',
'EPSG:4326:13',
'EPSG:4326:14',
'EPSG:4326:15',
'EPSG:4326:16',
'EPSG:4326:17',
'EPSG:4326:18',
'EPSG:4326:19'
];
// 切片大小
const resolutions = [
0.703125,
0.3515625,
0.17578125,
0.087890625,
0.0439453125,
0.02197265625,
0.010986328125,
0.0054931640625,
0.00274658203125,
0.001373291015625,
0.0006866455078125,
0.0003433227539062,
0.0001716613769531,
0.0000858306884766,
0.0000429153442383,
0.0000214576721191,
0.0000107288360596,
0.0000053644180298,
0.0000026822090149,
0.0000013411045074
];
// 设置地图投影
const projection = new Projection({
code: dataCrs, // 投影编码
units: 'degrees',
axisOrientation: 'neu'
});
const NewLayer = new Tile({
name: name,
title: name,
type: 'Group',
preload: true,
opacity: opacity,
source: new WMTS({
url: url + '/gwc/service/wmts',
tileGrid: new WMTSTileGrid({
tileSize: [256, 256],
extent: extent === undefined ? [-180.0, -90.0, 180.0, 90.0] : extent, // 范围
origin: [-180.0, 90.0],
resolutions: resolutions,
matrixIds: gridNames
}),
crossOrigin,
matrixSet: dataCrs,
layer: workspaces + ':' + layerName,
format: format ? format : 'image/gif',//image/png
projection: projection
}),
extent: transformExtent(extent, dataCrs, sysCrs), // 范围
zIndex: zIndex,
id,
visible,
});
return NewLayer;
}
/**TiTiler瓦片的方式加载COG
* @param:id {string} 【必选】标识
* @param:zIndex {string} 【必选】层级
* @param:url {string} 【必选】数据地址
* @param:titilerApi {string} 【必选】titilerApi
* @param:nodata {Number} 【非必选】无效值
* @param:name {string} 【非必选】图层名称
* @param:dataCrs {string} 【非必选】数据投影
* @param:sysCrs {string} 【非必选】系统投影
* @param:opacity {Number} 0-1 【非必选】透明度
* @param:crossOrigin {string} 【非必选】跨域
* @param:visible {Boolean} 【非必选】是否可见
* @returns
*/
export async function crtWebGLTileLayer ({
// resampling='',
id,
zIndex = 0,
url = '',
titilerApi = '',
nodata = 0,
visible = true,
dataCrs = 'EPSG:4326',
sysCrs = 'EPSG:3857',
crossOrigin = 'anonymous',
name,
opacity = 1
} = {}) {
let rescale = null
let extent = null
let source = null
// TiTiler获取COG统计信息
await getCogStatistics(url)
.then((res) => {
//获取最小最大范围
rescale = getMinMax(res.data)
})
// TiTiler获取COG边界
await getCogBounds(url)
.then((res) => {
extent = res.data.bounds
})
source = new XYZ({
url: `${titilerApi}/cog/tiles/WebMercatorQuad/{z}/{x}/{y}?url=${url}&rescale=${rescale.toString()}&bidx=3&bidx=2&bidx=1&nodata=${nodata}`,//&resampling=${resampling}
projection: sysCrs,
crossOrigin
});
const layer = new Tile({
source,
extent: transformExtent(extent, dataCrs, sysCrs),
zIndex,
visible,
id,
name,
opacity
});
return layer
}
/**创建wkt图层
* @param:id {string} 【必选】标识
* @param:features {Arr} 【必选】wkt数组
* @param:zIndex {string} 【必选】层级
* @param:style {object} 【非必选】图层样式
* @param:name {string} 【非必选】图层名称
* @param:dataCrs {string} 【非必选】数据投影
* @param:sysCrs {string} 【非必选】系统投影
* @param:opacity {Number} 0-1 【非必选】透明度
* @param:crossOrigin {string} 【非必选】跨域
* @param:visible {Boolean} 【非必选】是否可见
*/
export function crtWKTLayer ({
id = '',
features = [],
zIndex = 0,
opacity = 1,
name = '',
dataCrs = 'EPSG:4326',
sysCrs = 'EPSG:3857',
crossOrigin = 'anonymous',
visible = true,
style = {
type: ''
} }) {
const wktLayer = crtVectorLayer({ opacity, name, id, zIndex, crossOrigin, style, visible })
for (let i = 0; i < features.length; i++) {
const feature = wktToFeature(features[i], dataCrs, sysCrs)
wktLayer.getSource().addFeature(feature)
}
return wktLayer
}
/**创建瓦片图层
* @param:id {string} 【必选】标识
* @param:features {Arr} 【必选】wkt数组
* @param:zIndex {string} 【必选】层级
* @param:style {object} 【非必选】图层样式
* @param:name {string} 【非必选】图层名称
* @param:dataCrs {string} 【非必选】数据投影
* @param:sysCrs {string} 【非必选】系统投影
* @param:opacity {Number} 0-1 【非必选】透明度
* @param:crossOrigin {string} 【非必选】跨域
* @param:visible {Boolean} 【非必选】是否可见
*/
export function crtWKTLayerTile ({
id = '',
features = [],
zIndex = 0,
opacity = 1,
name = '',
dataCrs = 'EPSG:4326',
sysCrs = 'EPSG:3857',
crossOrigin = 'anonymous',
visible = true,
style = {
type: ''
} }) {
const wktLayer = crtVectorLayer({ opacity, name, id, zIndex, crossOrigin, style, visible })
for (let i = 0; i < features.length; i++) {
const feature = wktToFeature(features[i].geom, dataCrs, sysCrs)
wktLayer.getSource().addFeature(feature)
}
return wktLayer
}
5.基于ol-map.js中创建图层的方法,封装所有图层加载方法(ol-map.vue组件)
// 初始化常用图层
const addLayers = async function (layers) {
let temlayer = null;
for (let i = 0, len = layers.length; i < len; i++) {
switch (layers[i].provide) {
case "tdt":
temlayer = crtTdtLayer(layers[i]);
break;
case "vector":
temlayer = crtVectorLayer({ ...layers[i]});
break;
case "image":
temlayer = crtImageLayer({ ...layers[i]});
break;
case "image-tile":
let parms = {...layers[i]}
parms.extent = parms.extent?parms.extent:convertRowColumToLonLat({row:parms.row,col:parms.col,level:parms.level}) //convertRowColumToLonLat({row:parms.row,col:parms.col,level:parms.level})
temlayer = crtImageLayer({ ...parms});
break;
case "wms":
temlayer = crtWMSLayer({ ...layers[i] });
break;
case "wmts":
temlayer = crtWMTSLayer({ ...layers[i]});
break;
case "GeoTIFF":
temlayer = await crtWebGLTileLayer({ ...layers[i] });
break;
case "wkt":
temlayer = await crtWKTLayer({ ...layers[i] });
break;
case "wkt-tile":
let wktParms = {...layers[i]}
wktParms.features = rowColToWkts(wktParms.features)
temlayer = await crtWKTLayer({ ...wktParms });
break;
default:
break;
}
olMap.value.addLayer(temlayer);
}
return temlayer
}
6.调用所有图层加载方法(vue文件)
//定义要加载的图层数据
const layers = [
//天地图图层
{
provide: "tdt",
id: "tdt-vec_w",
zIndex: 1,
type: "vec_w",
url: "http://t{0-7}.tianditu.gov.cn/DataServer"
},
//矢量图层
{
provide: "vector",
id: "coverageLayer",
zIndex: 2
},
//贴图图层
{
provide: 'image',
id: "image",
zIndex: 3,
url: 'https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png',
extent: [4.226393748725525, 42.988428763897645, 7.712216377222916,
45.132976841676175,],
},
//wms图层
{
provide: "wms",
id: "gpdq-DemonstrationAarea",
zIndex: 4,
url: 'http://60.10.63.2:18070/gpdq/geoserver/' + "/ksh/wms",
workspaces: 'ksh',
layerName: 'gpdq-DemonstrationAarea',
},
//wmts图层
{
provide: "wmts",
id: "R-PRO-PuLuoWangSi-202012-10M",
zIndex: 5,
url: 'http://124.16.188.132:18090/geoserver/',
workspaces: 'ksh',
layerName: 'R-PRO-PuLuoWangSi-202012-10M',
format: "image/gif",
extent: [
4.226393748725525, 42.988428763897645, 7.712216377222916,
45.132976841676175,
],
},
//cog图层
{
provide: 'GeoTIFF',
id: "titiler",
zIndex: 6,
url: 'http://192.168.100.175:18070/Data/COG/GF1_WFV2_E4.0_N42.6_20211130_L1A0006096095-ORTHO-MS-rc.tif',
titilerApi: 'http://60.10.63.2:18070/gpdq/cogAPI/',
},
//wkt图层
{
provide: "wkt",
id: "wkt",
features: ['POLYGON((30 10, 40 40, 20 40, 10 20, 30 10))'],
zIndex: 7,
},
//瓦片图层
{
provide: "wkt-tile",
id: "wkt-tile",
features: [{ row: 1166, col: 2679, level: 7 }, { row: 1167, col: 2679, level: 7 }, { row: 1166, col: 2680, level: 7 }, { row: 1167, col: 2680, level: 7 }],
zIndex: 8,
},
//瓦片贴图图层
{
provide: "image-tile",
id: "image-tile",
url: 'http://124.16.188.132:18090/gpdq-sc/gpdq-tile-data/tile-data/gpdq/gpdqTileOverview?name=GF1_WFV_20200115_L1A00000000_6400_7_1166_2680&sideLength=1000&processType=GF1Normalization',
row: 1166,
col: 2680,
level: 9
}
]
//调用加载图层方法,自动添加到地图上
addLayers(layers)
总结
一招制敌,通过一个方法加载常用图层,减少代码量。