注释:经过实践WMS 服务GetLegendGraphic更好用官网使用文档
我使用的过的配置
/**
* dx 为右边文字左右margin距离
* bgcolor 为图例的背景颜色 TRANSPARENT=true表示背景颜色透明
*/
?request=GetLegendGraphic&format=image/png&legend_options=fontColor:0x333333;bgcolor:0xFFFFFF;fontSize:12;dpi:100;dx:8;fontStyle:bold;&TRANSPARENT=true&layer=layer名字
前言
- WMS 服务。openlayers提供了一个
GetLegendGraphic
的方法获取图例的URL,这个URL是一张图片。如果只是简单的显示可以满足我们的要求,但图片的缺点也很明显。文字较小,图片较小,放大又会失真,而且图片背景色为白色,对于有其他需求功能不太能符合。(也有可能我现在没找到设置的方法,我看到官网上有params设置,但又没看到params的说明。。。) - WFS服务。根据style属性自定义设置。我使用的是geoserver,这个应用提供了一些公开接口(地址是:点击这里),通过
/rest/workspaces/{workspace}/styles/{style}
这个接口我们可以获取到对应的样式设置。代码进行处理就可以了
实现
- 设置图例对象格式。这里我设置的是:
interface CustomStyle {
fill?: string,
name: string
stroke?: string
}
/* 如果是点的图例的话如下
* {point: [{name: '大于7',fill: '#10F500'}]
**、
- 通过接口获取sld,并处理获取的xml,将格式组合成图例格式(下面该方法可以共用,但可能也有小小的问题,因为我只测了两个sld,可以自行修改)
// cb为回调函数
export async function getSldXml(url, cb) {
// 获取xml
const data = await getSld(url);
const legendObj = {};
const result = data.replaceAll("sld:", "");
const xmlStyle = new DOMParser().parseFromString(result, "text/xml");
const userStyleDom = xmlStyle.getElementsByTagName("UserStyle").item(0);
const featureTypeStyleDom =
userStyleDom?.getElementsByTagName("FeatureTypeStyle");
for (let i = 0, l = featureTypeStyleDom?.length || 0; i < l; i++) {
const rulesDom = featureTypeStyleDom
?.item(i)
?.getElementsByTagName("Rule");
for (let q = 0, rl = rulesDom?.length || 0; q < rl; q++) {
const name = rulesDom
?.item(q)
?.getElementsByTagName("Name")
.item(0)?.innerHTML;
const pointBl = rulesDom
?.item(q)
?.getElementsByTagName("PointSymbolizer");
let type = "", targetDom;
if (pointBl && pointBl.length > 0) { type = "point"; targetDom = pointBl }
else {
const lineBl = rulesDom
?.item(q)
?.getElementsByTagName("LineSymbolizer");
if (lineBl && lineBl.length > 0) { type = "line"; targetDom = lineBl }
else {
const polygonBl = rulesDom
?.item(q)
?.getElementsByTagName("PolygonSymbolizer");
if (polygonBl && polygonBl.length > 0) { type = "polygon"; targetDom = polygonBl }
else break;
}
}
const fillDom = targetDom?.item(0)?.getElementsByTagName("Fill");
const strokeDom = targetDom?.item(0)?.getElementsByTagName("Stroke");
legendObj[type] = legendObj[type] || [];
// 填充
let cssDoms = fillDom?.item(0)?.children || [];
const nameObj = { name: name };
for (const cssDom of cssDoms) {
if (cssDom.getAttribute("name") === "fill") {
nameObj["fill"] = cssDom.innerHTML;
break;
}
}
// 线条
cssDoms = strokeDom?.item(0)?.children || [];
for (const cssDom of cssDoms) {
if (cssDom.getAttribute("name") === "stroke") {
nameObj["stroke"] = cssDom.innerHTML;
break;
}
}
legendObj[type].push(nameObj);
}
}
cb(legendObj);
}
- 获取图例格式后,自定义
LegendControl
控件类。
import { Control } from 'ol/control';
import BaseLayer from 'ol/layer/Base'
interface CustomLayer {
layer: BaseLayer,
type: string,
legendConfig: object
}
interface CustomStyle {
fill?: string,
name: string
stroke?: string
}
const SVG_NS = 'http://www.w3.org/2000/svg'
export class LegendControl extends Control {
legendDiv: HTMLElement
layerArr: CustomLayer[]
constructor(layers, options = {}) {
options = options || {}
super(options)
this.layerArr = layers
}
setMap() {
this.render()
}
render() {
if (!this.legendDiv) {
this.legendDiv = document.createElement("div")
this.legendDiv.id = "legendSub"
this.legendDiv.className = 'legend_sub'
this.element.appendChild(this.legendDiv)
for (let i = 0, len = this.layerArr.length; i < len; i++) {
const layer: BaseLayer = this.layerArr[i].layer
if (!layer.getVisible()) continue
const labelLyr = document.createElement('h4')
labelLyr.innerHTML = layer.getProperties()['name']
this.legendDiv.appendChild(labelLyr)
const tableDiv = document.createElement('table')
this.legendDiv.appendChild(tableDiv)
for (const key in this.layerArr[i].legendConfig) {
if (key === 'point') { // 点
this.createPointLegend(tableDiv, this.layerArr[i].legendConfig[key])
} else if (key === 'line') { // 线
this.createLineLegend(tableDiv, this.layerArr[i].legendConfig[key])
} else { // 面
this.createPolygonLegend(tableDiv, this.layerArr[i].legendConfig[key])
}
}
}
}
}
createPointLegend(tableDiv: HTMLElement, legendConfig: CustomStyle[]) {
for (const legend of legendConfig) {
const trDiv = document.createElement('tr')
const colorTd = document.createElement('td')
trDiv.appendChild(colorTd)
const svg = document.createElementNS(SVG_NS, 'svg')
svg.setAttribute('style', "width:36px;height:36px;");
const tag = document.createElementNS(SVG_NS, 'circle')
tag.setAttribute('cx', "18")
tag.setAttribute('cy', "18")
tag.setAttribute('r', "3")
if (legend.fill) tag.setAttribute('fill', legend.fill)
let borderColor = "none"
if (legend.stroke) borderColor = legend.stroke
tag.setAttribute('stroke', borderColor)
tag.setAttribute('stroke-width', "1")
svg.appendChild(tag)
colorTd.appendChild(svg)
const labelTd = document.createElement('td')
trDiv.appendChild(labelTd)
const itemLabel = document.createElement('label')
itemLabel.setAttribute('style', 'margin-left:5px;position:relative;top:3px;')
itemLabel.innerHTML = legend.name
labelTd.appendChild(itemLabel)
tableDiv.appendChild(trDiv)
}
}
createPolygonLegend(tableDiv: HTMLElement, legendConfig: CustomStyle[]) {
// const border = legendConfig.border
for (const legend of legendConfig) {
const trDiv = document.createElement('tr')
const colorTd = document.createElement('td')
trDiv.appendChild(colorTd)
const svg = document.createElementNS(SVG_NS, 'svg')
let fillColor = "none"
if (legend.fill) fillColor = legend.fill
svg.setAttribute('style', "width:36px;height:36px;fill:" + fillColor);
const tag = document.createElementNS(SVG_NS, 'polygon')
tag.setAttribute('points', "16,4 32,4 32,32 4,32")
if (legend.stroke) tag.setAttribute('stroke', legend.stroke)
tag.setAttribute('stroke-width', "1")
svg.appendChild(tag)
colorTd.appendChild(svg)
const labelTd = document.createElement('td')
trDiv.appendChild(labelTd)
const itemLabel = document.createElement('label')
itemLabel.setAttribute('style', 'margin-left:5px;position:relative;top:3px;')
itemLabel.innerHTML = legend.name
labelTd.appendChild(itemLabel)
tableDiv.appendChild(trDiv)
}
}
createLineLegend(tableDiv: HTMLElement, legendConfig: CustomStyle[]) {
for (const legend of legendConfig) {
const trDiv = document.createElement('tr')
const colorTd = document.createElement('td')
trDiv.appendChild(colorTd)
const svg = document.createElementNS(SVG_NS, 'svg')
svg.setAttribute('style', "width:36px;height:36px;");
const tag = document.createElementNS(SVG_NS, 'line')
if (legend.stroke) tag.setAttribute('stroke', legend.stroke)
tag.setAttribute('stroke-width', "1")
tag.setAttribute('x1', "4")
tag.setAttribute('y1', "34")
tag.setAttribute('x2', "34")
tag.setAttribute('y2', "4")
svg.appendChild(tag)
colorTd.appendChild(svg)
const labelTd = document.createElement('td')
trDiv.appendChild(labelTd)
const itemLabel = document.createElement('label')
itemLabel.setAttribute('style', 'margin-left:5px;position:relative;top:3px;')
itemLabel.innerHTML = legend.name
labelTd.appendChild(itemLabel)
tableDiv.appendChild(trDiv)
}
}
}
- new Map中调用我们刚刚自定义的LegendControl控件
new Map({
target: "map",
controls: defaultControls().extend([
new LegendControl(
[
// state.legendObj,state.xzqLegendObj 为调用getSldXml函数返回的值
{ layer: pointVector, legendConfig: state.legendObj }, // state.legendObj 为调用getSldXml函数返回的值
{ layer: untiled, legendConfig: state.xzqLegendObj },
],
{
element: document.getElementById("legend"),
}
),
]),
// ....... 这里省略了一些
});
- 写css稍微美化一下
.legend {
position: absolute;
right: 10px;
bottom: 10px;
width: 200px;
max-height: 700px;
overflow-y: scroll;
z-index: 10;
background: rgba(65, 184, 131, 0.5);
box-sizing: border-box;
padding: 10px 16px 20px 16px;
h4 {
font-weight: bold;
margin: 10px 0;
}
table {
border: 0;
border-collapse: collapse;
border-spacing: 0;
line-height: 0;
width: 100%;
td {
text-align: left;
}
tr {
td:first-child {
width: 36px;
height: 36px;
}
}
}
}
::-webkit-scrollbar {
width: 0px;
}
- 基本就完成了