使用geotools实现对shpefile数据的属性查询和空间查询。
参考文章:
GeoTools 查询那点事-CQL
Maven中GeoTools的引入
GeoTools应用-Filter
GeoTools之shp文件操作
封装示例代码
package cn.lutuo.sx.app.server.dao.geoengine;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.lutuo.sx.app.server.appexception.GeoQueryException;
import cn.lutuo.sx.app.server.commons.GeoQueryParams;
import com.vividsolutions.jts.geom.Geometry;
import lombok.extern.slf4j.Slf4j;
import org.geotools.data.FeatureSource;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.filter.text.cql2.CQL;
import org.geotools.filter.text.cql2.CQLException;
import org.geotools.filter.text.ecql.ECQL;
import org.geotools.geojson.feature.FeatureJSON;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.filter.Filter;
import org.opengis.filter.spatial.Intersects;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;
@Slf4j
@Component
public class ShapefileServer {
private static String shpFileRootPath;
private Map<String, ShapefileDataStore> shapefileMap = new HashMap<>();
private static final String SHAPEFILE_PREFIX = "shp";
@Value("${sx.shp_file_root_path}")
public void setShpFileRootPath(String shpFileRootPath) {
ShapefileServer.shpFileRootPath = shpFileRootPath;
initShapefileServer();
}
private void initShapefileServer() {
if (null == shpFileRootPath) return;
File rootFile = new File(shpFileRootPath);
if (rootFile.exists() && rootFile.isDirectory()
&& Objects.requireNonNull(rootFile.listFiles()).length > 0) {
Arrays.stream(Objects.requireNonNull(rootFile.listFiles())).forEach(file -> {
String name = file.getName();
String fileName = name.substring(0, name.lastIndexOf("."));
String filePrefix = name.substring(name.lastIndexOf(".") + 1);
if (filePrefix.equals(SHAPEFILE_PREFIX)) {
try {
ShapefileDataStore shapefileDataStore = new ShapefileDataStore(file.toURI().toURL());
// 设置编码,防止属性的中文字符出现乱码
shapefileDataStore.setCharset(StandardCharsets.UTF_8);
shapefileMap.put(fileName, shapefileDataStore);
} catch (MalformedURLException e) {
log.debug("加载shapefile文件出错:" + e.getMessage());
}
}
});
}
}
public List<String> getDatasetList() {
return new ArrayList<>(shapefileMap.keySet());
}
public SimpleFeatureCollection queryFeature(GeoQueryParams geoQueryParams) throws GeoQueryException {
SimpleFeatureCollection simpleFeatureCollection = null;
String datasetName = geoQueryParams.getDatasetName();
String attributeFilter = geoQueryParams.getAttributeFilter();
Geometry queryGeometry = geoQueryParams.getGeometry();
if (!shapefileMap.containsKey(datasetName)) {
throw new GeoQueryException(datasetName + "数据集不存在!");
}
try {
ShapefileDataStore shapefileDataStore = shapefileMap.get(datasetName);
FeatureSource<SimpleFeatureType, SimpleFeature> featuresource = shapefileDataStore.getFeatureSource(shapefileDataStore.getTypeNames()[0]);
// 读取要素
if (null != attributeFilter && !attributeFilter.trim().isEmpty()) {
simpleFeatureCollection = filterCQLOfSql(featuresource, attributeFilter);
} else if (null != queryGeometry) {
simpleFeatureCollection = filterGeoCQLOfGeometry(featuresource, queryGeometry);
} else {
simpleFeatureCollection = (SimpleFeatureCollection) featuresource.getFeatures();
}
} catch (IOException | CQLException e) {
throw new GeoQueryException("获取数据出错!" + e.getMessage());
}
return simpleFeatureCollection;
}
public JSONObject queryFeatureCollection(GeoQueryParams geoQueryParams) throws GeoQueryException {
JSONObject result = null;
SimpleFeatureCollection simpleFeatureCollection = this.queryFeature(geoQueryParams);
StringWriter writer = new StringWriter();
FeatureJSON fJson = new FeatureJSON();
try {
fJson.writeFeatureCollection(simpleFeatureCollection, writer);
result = JSONUtil.parseObj(writer.toString());
} catch (IOException e) {
log.debug("GeoJSON转换出错!" + e.getMessage());
throw new GeoQueryException("GeoJSON转换出错!" + e.getMessage());
}
return result;
}
public List<JSONObject> queryFeature2GeoJSON(GeoQueryParams geoQueryParams) throws GeoQueryException {
List<JSONObject> resultList = new ArrayList<>();
SimpleFeatureCollection simpleFeatureCollection = this.queryFeature(geoQueryParams);
if (null != simpleFeatureCollection) {
// 拿到迭代器
SimpleFeatureIterator simpleFeatureIterator = simpleFeatureCollection.features();
// 遍历每一个要素
while (simpleFeatureIterator.hasNext()) {
SimpleFeature simpleFeature = simpleFeatureIterator.next();
//feature转geojson
StringWriter writer = new StringWriter();
FeatureJSON fjson = new FeatureJSON();
try {
fjson.writeFeature(simpleFeature, writer);
resultList.add(JSONUtil.parseObj(writer.toString()));
} catch (IOException e) {
log.debug("GeoJSON转换出错!" + e.getMessage());
throw new GeoQueryException("GeoJSON转换出错!" + e.getMessage());
}
}
simpleFeatureIterator.close();
}
return resultList;
}
private SimpleFeatureCollection filterCQLOfSql(FeatureSource<SimpleFeatureType, SimpleFeature> fs, String sql) throws CQLException, IOException {
//ECQL是CQL的扩展类,支持 IN 查询
return (SimpleFeatureCollection) fs.getFeatures(ECQL.toFilter(sql));
}
private SimpleFeatureCollection filterGeoCQLOfGeometry(FeatureSource<SimpleFeatureType, SimpleFeature> fs, Geometry geometry) throws CQLException, IOException {
// 相交 拼接样式 "INTERSECTS(the_geom," + wktStr + ")"
Filter filter = CQL.toFilter(Intersects.NAME + "(the_geom," + geometry.toString() + ")");
return (SimpleFeatureCollection) fs.getFeatures(filter);
}
}
测试
public static void main(String[] args) {
String shpFileRootPath = "D:\\workdata\\shp";
File rootFile = new File(shpFileRootPath);
ShapefileServer shpFileServer = new ShapefileServer();
if (rootFile.exists() && rootFile.isDirectory()
&& Objects.requireNonNull(rootFile.listFiles()).length > 0) {
List<File> files = Arrays.stream(Objects.requireNonNull(rootFile.listFiles())).collect(Collectors.toList());
files.forEach(file -> {
System.out.println(file.getAbsolutePath());
String name = file.getName();
String fileName = name.substring(0, name.lastIndexOf("."));
String filePrefix = name.substring(name.lastIndexOf(".") + 1);
});
}
shpFileServer = null;
}