前言

ArcGIS API好像国内用的不多,除了设计院以外,大多数还是用的百度和高德API。
ArcGIS API的文档是全英文,因此分享一些小功能的实现思路。

需求

ArcGIS中,新添加的图层永远在最上面,就像这样:

image.png

而在业务逻辑上,有时我们需要某个图层置顶。
比如用来统计面积大小的图层,如果它被其他图层覆盖掉,就不能正常的统计面积和显示被选中的地块了:

image.png

本文就来解决这个问题

先说原理,非常简单,使用到生命周期钩子函数。

尝试的其他方法

首先尝试了add图层的时候能不能手动设置order,也就是给它设置的位置很高,比如100:

为了便于交流,我们暂时把这个图层叫做areaSumLayer

// 业务上需要置顶的图层
let areaSumLayer: GraphicsLayer

// 添加图层时设置的很高
map.add(areaSumLayer,100);

事实上这样不行,因为前面提到它的逻辑是:“新添加的图层永远在最上面”
即使以前的图层设置的很高,新图层还会更高,比如位置在101。

因此我们的思路是:
无法在创建areaSumLayer时保证它以后是置顶的
而是在创建其他图层时维护一下areaSumLayer的位置

生命周期钩子

什么是生命周期?所有的前端组件都使用了这个思想。生命周期指的是从一个组件从创建到消亡的过程。

拿前端框架Angular举例,最常见的包括:

  • ngOnInit(): 组件创建
  • ngOnChanges(): 绑定的数据发生变化时
  • ngOnDestoryed(): 组件销毁

https://angular.cn/guide/lifecycle-hooks

或者VUE中:

  • created(): 组件创建
  • mounted(): 组件渲染完毕
  • beforeUpdate():发生变更前
  • updated(): 发生变更
  • unmounted(): 组件注销
https://cn.vuejs.org/guide/essentials/lifecycle.html

这些生命周期钩子是在框架开发者编写时就写好的,要使用这些周期函数,只需要用回调的方式,传入一个我们自己写好的方法,这个方法会在适当的时机(生命周期发生时)被自动调用,用到的就是订阅者-观察者模式。

ArcGIS API

接下来看看ArcGIS的图层有什么生命周期(官方称之为事件event):

https://developers.arcgis.com/javascript/latest/api-reference...
image.png
  • before-add:添加前
  • after-add:添加后
  • before-changes:变更前
  • change:变更
  • after-changes:变更后
  • before-remove:销毁前
  • after-remove:销毁后

有这么多生命周期,而我们的业务上用到的是添加后,也就是“当新图层添加后,我们手动的更新一下需要置顶的areaSumLayer图层。

after-add是这样用的:

https://developers.arcgis.com/javascript/latest/api-reference...
map.layers.on("after-add", function(event){
  console.log(event.item.id);
});
  • map.layers.on是开启监听,map就是Map类创建的对象
  • "after-add"是监听的事件类型,我们需要监听添加图层之后的事件
  • event是自己写的回调函数中的变量
  • 方法体是我们要在回调时执行的操作

正常情况下,当有新的图层被添加进来时,控制台就会输出它的ID。

接下来是寻找:areaSumLayer有没有已经被添加
需要用到collection集合的find方法:

https://developers.arcgis.com/javascript/latest/api-reference...

image.png

find方法是寻找某个图层在不在图层集合中,使用的还是回调函数:

        // 如果此时图层集合中有面积的图层
        if (map.layers.find(function (layer) {
            return layer.id === areaSumLayer.id
        })){
            console.log("true");
        }

逐条解释:

我们定义了一个方法:

function (layer) {
    return layer.id === areaSumLayer.id
}
  • layer是回调时的变量,可以随便写,但要和方法体中对应
  • 方法的内容就是:判断回调变量的图层id和我们的id是否相等,如果相等,说明我们要查询的图层在这个集合中,返回true

接下来处理“调整图层顺序”的步骤:

// 重新添加面积图层
map.remove(areaSumLayer);
map.add(areaSumLayer);
  • 就是先隐藏再重新添加

因此合起来就变成了这样:

    /**
     * 地图生命周期
     * 当图层创建后,把预览图片的图层重新添加一下,使其永远置顶
     */
    map.layers.on('after-add', (event) => {
        // 如果此时图层集合中有areaSumLayer图层
        if (map.layers.find(function (layer) {
            return layer.id === areaSumLayer.id
        })) {
            // 重新添加areaSumLayer图层
            map.remove(areaSumLayer);
            map.add(areaSumLayer);
        }
    })

此时,只要有别的图层添加完毕,钩子函数就会重新把areaSumLayer置顶。

但现在有个问题:如果新添加的图层就是areaSumLayer呢?还会触发这个钩子函数吗?
答案是:会发生死循环。

解决这个问题很简单,先从event中把刚刚添加的图层id获取到:

// 刚刚添加的图层id,为了防止死循环
let justAddedId = event.item.id;

此时,如果justAddedId等于areaSumLayer的id,就说明刚才添加的就是areaSumLayer图层,就不用再执行调整操作了。

最终的代码:

    /**
     * 地图生命周期
     * 当图层创建后,把预览图片的图层重新添加一下,使其永远置顶
     */
    map.layers.on('after-add', (event) => {
        // 获取刚刚添加的图层id,为了防止死循环
        let justAddedId = event.item.id;
        // 如果此时图层集合中有areaSumLayer图层
        if (map.layers.find(function (layer) {
            return layer.id === areaSumLayer.id
        // 并且刚添加的图层不是areaSumLayer图层
        }) && (justAddedId != areaSumLayer.id)) {
            // 重新添加areaSumLayer图层
            map.remove(areaSumLayer);
            map.add(areaSumLayer);
        }
    })

总结

当我刚接手这个功能的时候,我甚至没有完整的看一遍ArcGIS的文档(又是英文,又特别长,文档内容比Angular都多)。
但所有框架的思想都是相通的,编程编的就是思想,根据其他框架的经验就可以推断ArcGIS一定有类似的方法,再去文档中查询即可。


LYX6666
1.6k 声望73 粉丝

一个正在茁壮成长的零基础小白