最近有一个需求,需要去做大量空间数据的相交检验,如果每一个图形都去读取出来与剩下的图形去对比,太耗费时间和性能,一起做了很多不必要的相交。
为了稍微提升一些速度,借鉴了在空间数据处理分任务时的,片区划分的方法,将与一个片区的取出来单独处理,这样会减少单次数据处理的量。 而且避免了很多相隔较远的数据的也需要去做相交比对的这种必要的操作。
下面就将我的做法分享出来
1.先贴pom.xml 文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>gis_data_fragment</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>7</source>
<target>7</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-main</artifactId>
<version>28.2</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-opengis</artifactId>
<version>28.2</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-metadata</artifactId>
<version>28.2</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-shapefile</artifactId>
<version>28.2</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-referencing</artifactId>
<version>28.2</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-epsg-hsql</artifactId>
<version>28.2</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-swing</artifactId>
<version>28.2</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<dependency>
<groupId>org.locationtech.jts</groupId>
<artifactId>jts-core</artifactId>
<version>1.19.0</version>
</dependency>
<dependency>
<groupId>si.uom</groupId>
<artifactId>si-quantity</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>si.uom</groupId>
<artifactId>si-units</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>si.uom</groupId>
<artifactId>si-units-java8</artifactId>
<version>0.7.1</version>
</dependency>
<dependency>
<groupId>tech.units</groupId>
<artifactId>indriya</artifactId>
<version>2.0.4</version>
</dependency>
<dependency>
<groupId>javax.measure</groupId>
<artifactId>unit-api</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>tech.uom.lib</groupId>
<artifactId>uom-lib-common</artifactId>
<version>2.1</version>
</dependency>
<dependency>
<groupId>systems.uom</groupId>
<artifactId>systems-common</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>osgeo</id>
<name>Open Source Geospatial Foundation Repository</name>
<url>https://repo.osgeo.org/repository/release/</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
<releases>
<enabled>true</enabled>
</releases>
</repository>
</repositories>
</project>
2. shp 文件读取的方法 ,同时获取到数据的范围,方便后面计算分片
引入的jar包
import org.geotools.data.DefaultTransaction;
import org.geotools.data.FeatureStore;
import org.geotools.data.Transaction;
import org.geotools.data.collection.ListFeatureCollection;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.shapefile.ShapefileDataStoreFactory;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.store.ContentFeatureCollection;
import org.geotools.data.store.ContentFeatureSource;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.util.factory.FactoryFinder;
import org.locationtech.jts.geom.*;
import org.locationtech.jts.operation.union.UnaryUnionOp;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import javax.swing.*;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.text.DecimalFormat;
import java.util.*;
// 地址就是这种 ./src/main/data/shp/pewg.shp,当然也可以直接选一个硬盘上的地址
//传入shp文件的地址,读取数据,获取数据范围
private void readSHP(String url) {
//创建一个file对象
File file = new File(url);
ReferencedEnvelope bounds = new ReferencedEnvelope();
try {
ShapefileDataStore dataStore = new ShapefileDataStore(file.toURI().toURL());
ContentFeatureSource featureSource = dataStore.getFeatureSource();
ContentFeatureCollection features = featureSource.getFeatures();
SimpleFeatureIterator featureIterator = features.features();
//创建一个shp文件中的所有MultiPolygon的集合
ArrayList<MultiPolygon> multiPolygons = new ArrayList<>();
while (featureIterator.hasNext()) {
SimpleFeature feature = featureIterator.next();
multiPolygons.add((MultiPolygon) feature.getDefaultGeometry());
}
//1.获取数据范围
bounds = featureSource.getBounds();
//2.根据数据范围,进行数据分片,得到分片的分为
Polygon[] polygons = partition(bounds, 15, 15);//对shp范围进行分区
//3.将分片与数据相交,得到每一小片相交的数据
dataIntersectionDetection(polygons, multiPolygons);
// polygonsAddToSHP(polygons); // 将切片图形写入shp文件
dataStore.dispose();
} catch (Exception e) {
e.printStackTrace();
}
}
3.根据数据范围,计算分片的数量
/*
*根据范围数据,计算分片
* bounds: 范围
* divisionsX:横向分割数
* divisionsY: 纵向分割数
* */
private Polygon[] partition(ReferencedEnvelope bounds, int divisionsX, int divisionsY) throws IOException {
//最大最小的经纬度点
Coordinate bottomLeft = new Coordinate(
keep9DecimalPlaces(bounds.getMinX()),
keep9DecimalPlaces(bounds.getMinY())); // 左下角
Coordinate topRight = new Coordinate(
keep9DecimalPlaces(bounds.getMaxX()),
keep9DecimalPlaces(bounds.getMaxY())); // 右上角
double minX = bottomLeft.x;
double minY = bottomLeft.y;
double maxX = topRight.x;
double maxY = topRight.y;
//大矩形范围
Coordinate[] rectangleRadius = {
bottomLeft,
new Coordinate(bottomLeft.x, topRight.y),
topRight,
new Coordinate(topRight.x, bottomLeft.y),
bottomLeft
};
// 将大矩形横向和纵向划分为10等分和5等分
int numDivisionsX = divisionsX;
int numDivisionsY = divisionsY;
// 存放划分好的小矩形的数组
double[][][] dividedRectangles = new double[numDivisionsX][numDivisionsY][4];
// 计算横向和纵向的步长
double xStep = (maxX - minX) / numDivisionsX;
double yStep = (maxY - minY) / numDivisionsY;
// 划分大矩形并将每个小矩形的四个顶点坐标存放到数组中
for (int i = 0; i < numDivisionsX; i++) {
for (int j = 0; j < numDivisionsY; j++) {
double x1 = (minX + i * xStep);
double y1 = (minY + j * yStep);
double x2 = (minX + (i + 1) * xStep);
double y2 = (minY + (j + 1) * yStep);
//dividedRectangles[i][j] = new double[]{x1, y1, x2, y2};
dividedRectangles[i][j] = new double[]{
keep9DecimalPlaces(x1),
keep9DecimalPlaces(y1),
keep9DecimalPlaces(x2),
keep9DecimalPlaces(y2)
};
}
}
//将划分的小矩形添加到MultiPolygon中
GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();
Polygon[] polygons = new Polygon[numDivisionsX * numDivisionsY];
int index = 0;
for (double[][] row : dividedRectangles) {
for (double[] rectangle : row) {
Coordinate[] coordinate = {
new Coordinate(rectangle[0], rectangle[1]),
new Coordinate(rectangle[2], rectangle[1]),
new Coordinate(rectangle[2], rectangle[3]),
new Coordinate(rectangle[0], rectangle[3]),
new Coordinate(rectangle[0], rectangle[1]),
};
Polygon polygon = geometryFactory.createPolygon(coordinate);
polygons[index] = polygon;
++index;
}
}
return polygons;
}
4.将分片数据与原始数据进行相交,将每一片相交的元素数据存到一个集合中
/*
* 将分片数据和原始数据传入方法
* */
private void dataIntersectionDetection(Polygon[] polygons, ArrayList<MultiPolygon> multiPolygons) throws IOException {
// 将相交的数据存到一个大集合中
ArrayList<ArrayList<MultiPolygon>> bigList = new ArrayList<>();
for (int j = 0; j < polygons.length; j++) {
//将每个相交的数据存到一个小集合
ArrayList<MultiPolygon> smallList = new ArrayList<>();
for (int m = 0; m < multiPolygons.size(); m++) {
Polygon polygon = polygons[j];
MultiPolygon multiPolygon = multiPolygons.get(m);
boolean intersects = polygon.intersects(multiPolygon);// 是否相交
if (intersects) {// 将相交的数据,保存到一个集合中
smallList.add(multiPolygon);
}
}
bigList.add(smallList);
}
/*// 将分片相交好的数据写入shp文件
for (int i = 0; i < bigList.size(); i++) {
if (bigList.get(i).isEmpty()) {// 为空的数据不写入
continue;
}
multiPolygonsAddToSHP(bigList.get(i), i);
}*/
}
最后在分享几个 将 图形数据 写入shp文件的方法,方便进行数据验证和查看
//将多个Polygon添加到shp
private void polygonsAddToSHP(Polygon[] polygons) throws IOException {
// 创建Shapefile的DataStore
File newFile = new File("src/main/data/shp/partition.shp");
Map<String, Serializable> params = new HashMap<>();
params.put("url", newFile.toURI().toURL());
params.put("create spatial index", Boolean.TRUE);
ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();
ShapefileDataStore dataStore = (ShapefileDataStore) dataStoreFactory.createNewDataStore(params);
// 定义FeatureType
SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder();
typeBuilder.setName("Polygon");
typeBuilder.setSRS("EPSG:4490");//设置坐标系
typeBuilder.add("the_geom", Polygon.class); // 将数据写入shp文件中 一定要是 the_geom
typeBuilder.add("num", Integer.class);//添加一个num 用于取数据
SimpleFeatureType featureType = typeBuilder.buildFeatureType();
// 将FeatureType写入DataStore
dataStore.createSchema(featureType);
// 开启事务
Transaction transaction = new DefaultTransaction("create");
// 获取FeatureStore
String typeName = dataStore.getTypeNames()[0];
FeatureStore<SimpleFeatureType, SimpleFeature> featureStore = (FeatureStore<SimpleFeatureType, SimpleFeature>) dataStore.getFeatureSource(typeName);
// 创建FeatureList
List<SimpleFeature> featureList = new LinkedList<>();
for (int i = 0; i < polygons.length; i++) {
SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(featureType);
featureBuilder.add(polygons[i]);
featureBuilder.add(i);
SimpleFeature feature = featureBuilder.buildFeature(null);
featureList.add(feature);
}
try {
// 添加FeatureList到Shapefile
ListFeatureCollection featureCollection = new ListFeatureCollection(featureType, featureList);
featureStore.setTransaction(transaction);
featureStore.addFeatures(featureCollection);
transaction.commit();
} catch (Exception e) {
e.printStackTrace();
transaction.rollback();
} finally {
transaction.close();
dataStore.dispose();
}
}
//将一个MultiPolygon添加到shp
private void multiPolygonAddToSHP(MultiPolygon multiPolygon) throws IOException {
// 创建Shapefile的DataStore
File newFile = new File("src/main/data/shp/multiPolygon1.shp");
Map<String, Serializable> params = new HashMap<>();
params.put("url", newFile.toURI().toURL());
params.put("create spatial index", Boolean.TRUE);
ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();
ShapefileDataStore dataStore = (ShapefileDataStore) dataStoreFactory.createNewDataStore(params);
// 定义FeatureType
SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder();
typeBuilder.setName("MultiPolygon");
typeBuilder.setSRS("EPSG:4490");//设置坐标系
typeBuilder.add("the_geom", MultiPolygon.class); // 将数据写入shp文件中 一定要是 the_geom
//typeBuilder.add("num", Integer.class);//添加一个num 用于取数据
SimpleFeatureType featureType = typeBuilder.buildFeatureType();
// 将FeatureType写入DataStore
dataStore.createSchema(featureType);
// 开启事务
Transaction transaction = new DefaultTransaction("create");
// 获取FeatureStore
String typeName = dataStore.getTypeNames()[0];
FeatureStore<SimpleFeatureType, SimpleFeature> featureStore = (FeatureStore<SimpleFeatureType, SimpleFeature>) dataStore.getFeatureSource(typeName);
// 创建Feature
SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(featureType);
featureBuilder.add(multiPolygon);
SimpleFeature feature = featureBuilder.buildFeature(null);
try {
// 添加FeatureList到Shapefile
ListFeatureCollection featureCollection = new ListFeatureCollection(featureType, feature);
featureStore.setTransaction(transaction);
featureStore.addFeatures(featureCollection);
transaction.commit();
} catch (Exception e) {
e.printStackTrace();
transaction.rollback();
} finally {
transaction.close();
dataStore.dispose();
}
}
// 将多个MultiPolygon添加到shp,生成多个shp
private void multiPolygonsAddToSHP(ArrayList<MultiPolygon> multiPolygons, Integer num) throws IOException {
// 创建Shapefile的DataStore
File newFile = new File("src/main/data/shp/number/multiPolygons" + num + ".shp");
Map<String, Serializable> params = new HashMap<>();
params.put("url", newFile.toURI().toURL());
params.put("create spatial index", Boolean.TRUE);
ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();
ShapefileDataStore dataStore = (ShapefileDataStore) dataStoreFactory.createNewDataStore(params);
// 定义FeatureType
SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder();
typeBuilder.setName("Polygon");
typeBuilder.setSRS("EPSG:4490");//设置坐标系
typeBuilder.add("the_geom", MultiPolygon.class); // 将数据写入shp文件中 一定要是 the_geom
SimpleFeatureType featureType = typeBuilder.buildFeatureType();
// 将FeatureType写入DataStore
dataStore.createSchema(featureType);
// 开启事务
Transaction transaction = new DefaultTransaction("create");
// 获取FeatureStore
String typeName = dataStore.getTypeNames()[0];
FeatureStore<SimpleFeatureType, SimpleFeature> featureStore = (FeatureStore<SimpleFeatureType, SimpleFeature>) dataStore.getFeatureSource(typeName);
// 创建FeatureList
List<SimpleFeature> featureList = new LinkedList<>();
for (int i = 0; i < multiPolygons.size(); i++) {
SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(featureType);
featureBuilder.add(multiPolygons.get(i));
SimpleFeature feature = featureBuilder.buildFeature(null);
featureList.add(feature);
}
try {
// 添加FeatureList到Shapefile
ListFeatureCollection featureCollection = new ListFeatureCollection(featureType, featureList);
featureStore.setTransaction(transaction);
featureStore.addFeatures(featureCollection);
transaction.commit();
} catch (Exception e) {
e.printStackTrace();
transaction.rollback();
} finally {
transaction.close();
dataStore.dispose();
}
}
//将多个MultiPolygon合并为一个
private MultiPolygon mergeMultiPolygons(List<MultiPolygon> multiPolygons) {
// 合并的时间根据数据复杂程度,时间可能会长达几分钟。
UnaryUnionOp unaryUnionOp = new UnaryUnionOp(multiPolygons);
return (MultiPolygon) unaryUnionOp.union();
}
// 保留9位小数,经纬度保留9未小数,精度约为0.0001米=0.1毫米
private double keep9DecimalPlaces(double d) {
DecimalFormat df = new DecimalFormat("#.#########");
return Double.parseDouble(df.format(d));
}