背景
由于项目的地图服务要迁移到国产操作系统上,以前用Arcgis发布的地图服务都要改用geoserver重新发布。由此产生了一系列的问题,这里主要记录一下arcgis for js加载WMS服务时需要对多个子图层进行过滤的解决方法。
arcgis for js 的版本是 4.20
单一的WMS服务
如果你的WMS服务只有一个,没有子图层,那你可以直接使用WMSLayer中customLayerParameters的属性,在其中定义“cql_filter”来实现过滤效果,具体用法见arcgis官网。
基本原理
事实上在项目中单一的地图服务很少见,不过我们还是要先从单一的WMS服务中看看带过滤的请求发出来时是什么样的。我们可以先通过geoserver中图层预览功能设置过滤条件,查看具体的请求参数
在这个请求里与过滤有关的两个参数分别是LAYERS和CQL_FILTER,通过LAYERS指定加载图层,通过CQL_FILTER指定过滤条件,最终实现了对单一图层的过滤。
而当我们要加载多个子图层是请求就会变成下面这样
不设置过滤条件的话服务也是可以正常加载的
这个时候如果我们添加过滤条件
会看到geoserver报错 Layers and filters are mismatched, you need to provide one filter for each layer
通过查看geoserver的官方文档可以看到,在多layer时要给每个layer分别设置cql_fliter并以;
隔开。
那如果某一部分图层不需要过滤呢?那也得设置,就设置成1=1
吧,重点是过滤条件的顺序要和LAYERS中的顺序相同,不要搞乱。
我们对三个子图层分别设置了过滤条件,如下
ok,服务可以加载出来并对指定图层完成了过滤。
基本原理搞清了,下面就搞起来,让arcgis的WMSLayer支持cql_filter。
WMSSubLayer.js
在开始之前,让我们先看看arcgis是怎么如何加载WMS服务的,这是官网的代码
// Typical usage
let layer = new WMSLayer({
url: // url to the service,
sublayers: [{
name: // name of the sublayer,
legendUrl: // url to the legend
}]
});
这里的sublayers
就是我们要加载的子图层,它对应的类是WMSSublayer
,遗憾的是在WMSSublayer
的文档中并没有找到类似于customParameters
、customLayerParameters
的属性,不然就可以直接拿来用了。
既然没有那就自己动手。多图层过滤,我们又要保证过滤条件的顺序与请求中LAYERS顺序相同,以我的直觉还是把过滤条件放在子图层中设置更好,让我们最终能这样用
// Typical usage
let layer = new WMSLayer({
url: // url to the service,
sublayers: [{
name: // name of the sublayer,
cqlFilter: //过滤条件1
legendUrl: // url to the legend
},{
name: // name of the sublayer,
cqlFilter: //过滤条件2
legendUrl: // url to the legend
},{
name: // name of the sublayer,
legendUrl: // url to the legend
}]
});
我们首先要修改WMSSubLayer.js
使它支持我们新增的属性cqlFilter
让我们找到WMSSubLayer.js
的源码,格式化一下,并增加下面这些代码
ok,搞定。WMSSubLayer
的对象已经拥有cqlFilter
这个属性啦。
WMSLayer.js
接下来,我们就要修改WMSLayer
的代码,请求毕竟还是从他这发出来的。
还是一样,找到WMSLayer.js
的源码,格式化一下方便修改。
debug一下可以看到,请求是从fetchImage
这个方法发出来的
而这个方法里,看名字都能看出来_mixCustomParameters
就是我们要修改的最终目标。
废话不多说,直接上代码,首先要先增加一个mixSubLayersCqlFilter
方法,用来整理所有子图层的cqlFilter参数。
function mixSubLayersCqlFilter(sublayers) {
if (!sublayers || !sublayers.length) return null;
const m = {};
for (let i = 0; i < sublayers.length; i++) {
let l = sublayers[i];
if (l.cqlFilter)
m[l.name] = l.cqlFilter;
}
return m;
}
接着就要在请求中增加cql_filter
来达到我们多图层过滤的目的,将_mixCustomParameters
方法改成这样
h._mixCustomParameters = function (b) {
let mp = mixSubLayersCqlFilter(this.allSublayers.items);
if (!this.customLayerParameters && !this.customParameters && !mp) return b;
let ls = b.layers.split(",");
let cqlFilters = [];
for (let i = 0; i < ls.length; i++) {
if (mp[ls[i]]) {
cqlFilters.push(mp[ls[i]]);
} else {
cqlFilters.push("1=1");
}
}
const a = {
...this.customParameters,
...this.customLayerParameters
};
a['cql_filter'] = cqlFilters.join(";");
for (const d in a) b[d.toLowerCase()] = a[d];
return b
};
最终测试
将修改后的文件重新压缩一下,替换原文件。(记得备份,出问题了不负责)
按我们的过滤需求创建一个新图层
let layer = new WMSLayer({
url: basemap_url + "/sand/wms",
sublayers: [{
name: "mstRv",
cqlFilter: "RV_CD='FFD4EA00000L'"
},{
name: "mstRvLabel"
},{
name: "countyGeo",
maxScale: 288895.2771443219,
cqlFilter: "code like '4113%'"
}]
});
跑一下代码
没有问题,地图服务按要求加载出来了,再看看请求
也是没啥问题的