一、最简单的获取图层方式
通常情况下,如果要获取当前地图中的图层,可以用2种方法获取。
以下图为例:
一种是【map.Layers】属性获取,结果如下:
可以看出,这里只获取到了第一层级的图层,图层组下面的二级图层被忽略了,显然这不是我们想要的。
另一种是通过map.GetLayersAsFlattenedList()方法获取,结果如下:
这次的结果,不管是第一层级还是第二层级的图层都得到了。
但美中不足的是,图层的结构被打散了,比如说【FW】图层,实际是【新建图层组】下的图层,正确的表达为【新建图层组\FW】。
为什么说这才是正确的表达,如果你用过GP工具就会发现,输入数据是图层的话,只有【新建图层组\FW】这种表达方式才是正确的,如果输入的是【FW】,将是无效的输入。
那边,下一步要做的就是获取图层完整的路径结构。
二、获取图层完整的路径结构
由于SDK没有提供获取图层完整路径结构的接口,那么这个事情就只能自己动手了。
思路其实也不难,就是通过获取图层的父图层,然后把父图层的名字加到结果中就行了,直到父图层是地图为止。
代码如下:
// 获取图层的完整名称
public static List<object> GetLayerFullName(this Object layer, Map map, string lyName)
{
List<object> result = new List<object>();
// 如果是图层
if (layer is Layer)
{
// 如果父对象是Map,直接返回图层名
if (((Layer)layer).Parent is Map)
{
result.Add(layer);
result.Add(lyName);
return result;
}
else
{
// 如果父对象是不是Map,则找到父对象图层,并循环查找上一个层级
Layer paLayer = (Layer)((Layer)layer).Parent;
List<object> list = paLayer.GetLayerFullName(map, @$"{paLayer}\{lyName}");
return list;
}
}
// 如果是独立表
else if (layer is StandaloneTable)
{
// 如果父对象是Map,直接返回图层名
if (((StandaloneTable)layer).Parent is Map)
{
result.Add(layer);
result.Add(lyName);
return result;
}
else
{
// 如果父对象是不是Map,则找到父对象图层,并循环查找上一个层级
Layer paLayer = (Layer)((StandaloneTable)layer).Parent;
List<object> list = paLayer.GetLayerFullName(map, @$"{paLayer}\{lyName}");
return list;
}
}
else
{
return null;
}
}
这里先把图层组排除,只考虑一般图层的情况,获取的图层列表如下:
OK,正是想要的结果。
但是,其实这还并不完美,因为这里没有考虑到更复杂的情况,比如说同名图层。稍微调整一下:
再试一下上面的方法,导出结果为:
可以看到,同名图层都被列入列表,但是名称都一样。如果这时候将其作为输入数据,系统就无法认出到底该用哪一个,就只会将第一个作为输入数据,这显然也不是我们想要的。
于是我们要做的是,给这些同名图组作一个标记,作为输入数据的标识。
三、标识同名图层
在我们获取到图层列表后,就可以对列表进行处理。
这里用到的方法是,新建一个字典,用来记录列表中每条记录的出现次数,然后按出现的先后用数字后缀给它标记。
然后再找出只有一个记录,也就是没有同名的图层,把它的【1】的标记去掉就可以了。
代码如下:
// 对重复要素进行数字标记
public static List<string> AddNumbersToDuplicates(this List<string> stringList)
{
// 使用Dictionary来跟踪每个字符串的出现次数
Dictionary<string, int> stringCount = new Dictionary<string, int>();
// 遍历字符串列表
for (int i = 0; i < stringList.Count; i++)
{
string currentString = stringList[i];
// 检查字符串是否已经在Dictionary中存在
if (stringCount.ContainsKey(currentString))
{
// 获取该字符串的出现次数
int count = stringCount[currentString];
// 在当前字符串后添加数字
stringList[i] = $"{currentString}:{count + 1}";
// 更新Dictionary中的计数
stringCount[currentString] = count + 1;
}
else
{
// 如果字符串在Dictionary中不存在,将其添加,并将计数设置为1
stringCount.Add(currentString, 1);
// 在当前字符串后添加数字
stringList[i] = $"{currentString}:{1}";
}
}
// 去除单个要素的数字标记
foreach (var item in stringCount)
{
if (item.Value == 1)
{
for (int i = 0; i < stringList.Count; i++)
{
if (stringList[i] == item.Key + ":1")
{
stringList[i] = item.Key;
}
}
}
}
// 返回字符串列表
return stringList;
}
运行后结果如下:
可以看到,同名图层的后面加上了数字标记,OK,大功造成。
.......并没有。
虽然标记出来了,但是如果在使用GP工具的时候,输入的是【规划用地:1】这样的字符串,也是无效的。
这时候就要转换一下思路了,GP工具输入的不仅可以是字符串,也可以是图层。因此我们要做的是将上面的字符串还原成图层。
四、将图层名字符串还原成图层
首先解释下为什么要这么麻烦,先获取图层名,再还原成图层,进行GP运算,直接将获取的图层作为输入数据不行吗?
这是因为在工具中,经常需要用户手动选择图层,而真正执行的流程是在按下执行键后才进行的,并不在同一个进程中。
现在的问题是怎么让上面获取到的图层名和它的实际图层一一对应上。
很简单,再来一遍查找图层就行了, 不过这次,我们不仅要获取完整图名,同时也要获取它的原始图层。
这里用一个字典来收集。代码如下:
// 获取地图中的所有要素图层【带图层结构】【字典】
public static Dictionary<FeatureLayer, string> AllFeatureLayersDic(this Map map)
{
Dictionary<FeatureLayer, string> dic = new Dictionary<FeatureLayer, string>();
List<string> layers = new List<string>();
List<FeatureLayer> lys = new List<FeatureLayer>();
// 获取所有要素图层
List<FeatureLayer> featureLayers = map.GetLayersAsFlattenedList().OfType<FeatureLayer>().ToList();
foreach (FeatureLayer featureLayer in featureLayers)
{
List<object> list = featureLayer.GetLayerFullName(map, featureLayer.Name);
layers.Add((string)list[1]);
lys.Add(featureLayer);
}
// 标记重复
layers.AddNumbersToDuplicates();
// 加入字典
for (int i = 0; i < lys.Count; i++)
{
dic.Add(lys[i], layers[i]);
}
// 返回值
return dic;
}
获取字典后,对着图层名找到图层即可:
// 查找要素图层
foreach (var layer in dicFeatureLayer)
{
if (layerFullName == layer.Value)
{
result.Add(layer.Key);
}
}
OK,这次是大功告成了,完美闭环。