项目需要对多个shp文件做合并和求交,而geotools常用的几何工具都是针对单个Geometry的,若手动获取shp里的要素集合并遍历每个要素的geometry,可能效率会很低,所以找到了geotools的process工具,测试了一下
合并两个shp文件
用到的工具
UnionFeatureCollection
需要的依赖
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-process</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-process-feature</artifactId>
<version>${geotools.version}</version>
</dependency>
代码
// ShapeFileReaderUtils和ShapeFileWriterUtils是自己定义的,内容见下面地址
// https://blog.csdn.net/Neuromancerr/article/details/119981046
public static void mergeTest() throws Exception {
// 待合并的两个文件地址
String pathHead = "D:\\mapresources\\test\\";
String file1 = pathHead + "test1.shp";
String file2 = pathHead + "test2.shp";
// 获取要素集
ContentFeatureCollection features1 = ShapeFileReaderUtils.getFeatureSourceFromShapeFile(file1).getFeatures();
ContentFeatureCollection features2 = ShapeFileReaderUtils.getFeatureSourceFromShapeFile(file2).getFeatures();
// 定义输出路径
String outPut = "D:\\mapresources\\test\\testOut.shp";
// 初始化
UnionFeatureCollection collection2 = new UnionFeatureCollection();
// 执行合并
SimpleFeatureCollection result = collection2.execute(
features1,
features2
);
// 输出结果生成shp文件
ShapeFileWriterUtils.buildShp(result, outPut);
}
public static void main(String[] args) throws Exception {
mergeTest();
}
结果成功
两个shp求交
用到的是 IntersectionFeatureCollection
以下是测试代码
// ShapeFileReaderUtils和ShapeFileWriterUtils是自己定义的,内容见下面地址
// https://blog.csdn.net/Neuromancerr/article/details/119981046
public static void mergeTest() throws Exception {
// 待合并的两个文件地址
String pathHead = "D:\\mapresources\\test\\";
String file1 = pathHead + "test1.shp";
String file2 = pathHead + "test2.shp";
// 获取要素集
ContentFeatureCollection features1 = ShapeFileReaderUtils.getFeatureSourceFromShapeFile(file1).getFeatures();
ContentFeatureCollection features2 = ShapeFileReaderUtils.getFeatureSourceFromShapeFile(file2).getFeatures();
// 定义输出路径
String outPut = "D:\\mapresources\\test\\testOut3.shp";
// 初始化
IntersectionFeatureCollection collection = new IntersectionFeatureCollection();
List<String> attr1 = new ArrayList<>();
features1.features().next().getAttributes().forEach(attr -> {
attr1.add(attr.toString());
});
List<String> attr2 = new ArrayList<>();
features2.features().next().getAttributes().forEach(attr -> {
attr2.add(attr.toString());
});
// 执行合并
SimpleFeatureCollection result = collection.execute(
features1,
features2,
attr1,
attr2,
IntersectionFeatureCollection.IntersectionMode.FIRST,
true,
true
);
// 这一步是为了消除多边形自相交的几何错误
SimpleFeature[] resArr = (SimpleFeature[]) result.toArray();
DefaultFeatureCollection res2 = new DefaultFeatureCollection();
for (SimpleFeature simpleFeature : resArr) {
simpleFeature.setDefaultGeometry(parseGeom((Geometry) simpleFeature.getDefaultGeometry()));
res2.add(simpleFeature);
}
// 输出结果生成shp文件
ShapeFileWriterUtils.buildShp(res2, outPut);
}
// Geometry转multiPolygon
private static MultiPolygon parseGeom(Geometry geometry) {
MultiPolygon multiPolygon = null;
// 防止自相交
geometry = geometry.intersection(geometry);
if (geometry.getGeometryType().equals("MultiPolygon")) {
multiPolygon = (MultiPolygon) geometry;
} else if (geometry.getGeometryType().equals("Polygon")) {
GeometryFactory gf = new GeometryFactory();
Polygon[] polys = new Polygon[1];
polys[0] = (Polygon) geometry;
multiPolygon = gf.createMultiPolygon(polys);
}
return multiPolygon;
}
但结果并不是期望中两个要素的相交部分,而是IntersectionFeatureCollection.execute()方法传入的第二个要素集的整个复制。这把我搞懵了,经过各种参数调整测试,都没啥用,只能暂时放弃。
求交的另一种思路
用的方法比较笨,先用UnionFeatureCollection把需要求交的shp都合成一个巨大的数据集,然后遍历其中的要素,分别对每个geometry求交。我是这么写的
// 传入一整个要素集
private void mergeShp(DefaultFeatureCollection features) throws Exception {
String outPut = "D:\\mapresources\\jiangsu\\output\\test.shp";
// 输出要素集
DefaultFeatureCollection featureCollection = new DefaultFeatureCollection();
// 输入要素集转数组
SimpleFeature[] featuresArray = new SimpleFeature[features.size()];
featuresArray = features.toArray(featuresArray);
for (int i = 0; i < featuresArray.length; i++) {
// 取出单个要素和他的geometry
SimpleFeature feature1 = featuresArray[i];
MultiPolygon polygon1 = parseGeom((Geometry) feature1.getDefaultGeometry());
boolean intersected = false;
for (int j = 0; j < i; j++) {
SimpleFeature feature2 = featuresArray[j];
MultiPolygon polygon2 = parseGeom((Geometry) feature2.getDefaultGeometry());
assert polygon1 != null;
assert polygon2 != null;
if (polygon1.intersects(polygon2)) {
try {
intersected = true;
SimpleFeature newFeature;
// 按照定义的mdate字段,较新的属性覆盖旧的
if (Long.parseLong((String) feature1.getAttribute("mdate")) > Long.parseLong(((String) feature2.getAttribute("mdate")))) {
newFeature = SimpleFeatureBuilder.copy(feature1);
} else {
newFeature = SimpleFeatureBuilder.copy(feature2);
}
newFeature.setDefaultGeometry(polygon1.union(polygon2));
featureCollection.add(newFeature);
System.out.println("featureCollection add:" + newFeature.getID() + " intersected " + feature1.getID() + "," + feature2.getID());
} catch (Exception e) {
e.printStackTrace();
}
}
}
if (!intersected) {
featureCollection.add(feature1);
System.out.println("featureCollection add:" + feature1.getID());
}
}
ShapeFileWriterUtils.buildShp(featureCollection, outPut);
}
问题首先是效率,对两个各有799个要素的shp求交,耗时数分钟。同时若有多个几何同时相交,会重复产生新要素,比如A、B、C相交,会产生A-B和B-C两个要素,B的部分重负了。
虽然算法上可以并应当优化,但负责python的同事可以接手这部分工作,遂放弃。在这里记录一下。