概述:GeoTools 是一个开源 (LGPL) Java 代码库,它为操作地理空间数据提供符合标准的方法,例如实现地理信息系统。GeoTools 库数据结构基于开放地理空间联盟 (OGC) 规范。
官网地址:
https://www.geotools.org/
常用maven库地址:
https://repo.osgeo.org/repository/release/
https://maven.geo-solutions.it/
arcgrid
geotiff
grassraster
image ( JPEG TIFF GIF PNG)
imageio-ext-gdal
imagemosaic
imagepyramid
JP2K
matlab
wkt
csv
wkb
geojson
shapefile
db2
hana
h2
mysql
oracle
postgis
sqlserver
elastic search
public static void readShpFile(String shpPath) {
File shpFile = new File(shpPath);
try {
ShapefileDataStore shapefileDataStore = new ShapefileDataStore(shpFile.toURI().toURL());
// 设置编码,防止属性的中文字符出现乱码
shapefileDataStore.setCharset(Charset.forName("UTF-8"));
// 这个typeNamae不传递,默认是文件名称
FeatureSource featuresource = shapefileDataStore.getFeatureSource(shapefileDataStore.getTypeNames()[0]);
// 读取bbox
ReferencedEnvelope bbox =featuresource.getBounds();
// 读取投影
CoordinateReferenceSystem crs = featuresource.getSchema().getCoordinateReferenceSystem();
// 特征总数
int count = featuresource.getCount(Query.ALL);
// 获取当前数据的geometry类型(点、线、面)
GeometryType geometryType = featuresource.getSchema().getGeometryDescriptor().getType();
// 读取要素
SimpleFeatureCollection simpleFeatureCollection = (SimpleFeatureCollection) featuresource.getFeatures();
// 获取当前矢量数据有哪些属性字段值
List<AttributeDescriptor> attributes = simpleFeatureCollection.getSchema().getAttributeDescriptors();
// 拿到迭代器
SimpleFeatureIterator simpleFeatureIterator = simpleFeatureCollection.features();
// 遍历每一个要素
while(simpleFeatureIterator.hasNext()) {
SimpleFeature simpleFeature = simpleFeatureIterator.next();
// java8新特性流api
attributes.stream().forEach((a) -> {
// 依次读取这个shape中每一个属性值,当然这个属性值,可以处理其它业务
System.out.println(a.getLocalName() + ":" + simpleFeature.getAttribute(a.getLocalName()));
});
}
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("读取完成!");
}
ex : 0000010300000001000000A4000000B98D06F0163C5E408D28ED0DBE684440B98D06F0163C5E408D28ED0DBE684440B98D06F0163C5E408D28ED0DBE684440B98D06F0163C5E408D28ED0DBE684440075F984C153C5E4029C
WKBReader wkbReader = new WKBReader();
Geometry geometry = wkbReader.read(WKBReader.hexToBytes(tokens[i]));
ex :
MultiPolygon ( ((10 10, 10 20, 20 20, 20 15, 10 10)), ((60 60, 70 70, 80 60, 60 60 )) )
WKTReader wktReader = new WKTReader();
Geometry geometry = wktReader.read(wktStr);
ex :
{“type”:“Feature”,“crs”:{“type”:“name”,“properties”:{“name”:“EPSG:2380”}},“geometry”:{“type”:“MultiPolygon”,“coordinates”:[[[[646398.9535,3267941.9664],[649558.7196,3267895.3528],[649674.763,3265683.4124],[646387.8773,3265827.4858],[646398.9535,3267941.9664]]]]},“properties”:{“Id”:0}}
public static void readGeoJson(String jsonPath) throws IOException {
File file = new File(jsonPath);
FileInputStream fileInputStream = new FileInputStream(file);
// 这里可能存在问题,如果是超大文件怎么办,一次性读入会不会报内存
// 解决方案是不是对大文件进行拆分
GeoJSONReader geoJSONReader = new GeoJSONReader(fileInputStream);
SimpleFeatureCollection featureCollection = geoJSONReader.getFeatures();
SimpleFeatureIterator iterator = featureCollection.features();
List<AttributeDescriptor> attributes = featureCollection.getSchema().getAttributeDescriptors();
while (iterator.hasNext()) {
SimpleFeature simpleFeature = iterator.next();
System.out.println();
attributes.stream().forEach((a) -> {
// 依次读取这个shape中每一个属性值,当然这个属性值,可以处理其它业务
System.out.println(a.getLocalName() + ":" + simpleFeature.getAttribute(a.getLocalName()));
});
}
fileInputStream.close();
System.out.println("读取JSON完毕!");
}
SimpleFeatureCollection simpleFeatureCollection = (SimpleFeatureCollection) featuresource.getFeatures();
// 获取当前矢量数据有哪些属性字段值
List<AttributeDescriptor> attributes = simpleFeatureCollection.getSchema().getAttributeDescriptors();
// 拿到迭代器
SimpleFeatureIterator simpleFeatureIterator = simpleFeatureCollection.features();
// 遍历每一个要素
while(simpleFeatureIterator.hasNext()) {
SimpleFeature simpleFeature = simpleFeatureIterator.next();
// java8新特性流api
attributes.stream().forEach((a) -> {
// 依次读取这个shape中每一个属性值,当然这个属性值,可以处理其它业务
System.out.println(a.getLocalName() + ":" + simpleFeature.getAttribute(a.getLocalName()));
});
}
public static SimpleFeatureType createType(Class<?> c, String layerName) {
SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder();
builder.setCRS(DefaultGeographicCRS.WGS84);
builder.add("FID",String.class);
// 需要注意的是几何字段的属性名称是固定的
builder.add("the_geom", c);
// 设置了图层的名字
builder.setName(layerName);
SimpleFeatureType simpleFeatureType = builder.buildFeatureType();
return simpleFeatureType;
}
Class的取值可以是:几何(Point.class,Polygon.class,MultiPolygon.class),属性(String.class,Integer.class, Double.class)
2.根据TYPE构建单个要素
SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(simpleFeatureType);
WKTReader wktReader = new WKTReader();
Geometry geometry = wktReader.read(wktStr);
// 这里的添加顺序和上面TYPE的时候保持一致
featureBuilder.add("1");
featureBuilder.add(geometry);
SimpleFeature feature = featureBuilder.buildFeature(null);
3.创建FeatureCollection
List<SimpleFeature> features = new ArrayList<>();
// 只需要将上面步骤的单个要素放入循环中创建更多的要素
features.add(feature);
SimpleFeatureCollection collection = new ListFeatureCollection(TYPE, features);
4.导出为shp
// 生成shpfile
File shpFile = new File(shpPath);
ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();
// 创造shpstore需要的参数
Map<String, Serializable> params = new HashMap<>();
params.put("url", shpFile.toURI().toURL());
params.put("create spatial index", Boolean.TRUE);
ShapefileDataStore newDataStore = (ShapefileDataStore) dataStoreFactory.createNewDataStore(params);
newDataStore.createSchema(simpleFeatureType);
Transaction transaction = new DefaultTransaction("create");
String typeName = newDataStore.getTypeNames()[0];
SimpleFeatureSource featureSource = newDataStore.getFeatureSource(typeName);
SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource;
SimpleFeatureCollection collection = new ListFeatureCollection(simpleFeatureType, features);
featureStore.setTransaction(transaction);
featureStore.addFeatures(collection);
featureStore.setTransaction(transaction);
transaction.commit();
transaction.close();
拓展:
我们看到创建ShapefileDataStore 的时候传递了一个params ,那么这些参数可以传递那些值呢?可以看看源码, 可以看到里面内置了很多参数可用,我们上面使用的就是其中的两个参数。
public static final Param URLP = new Param("url", URL.class, "url to a .shp file", true, (Object)null, new KVP(new Object[]{"ext", "shp"}));
public static final Param NAMESPACEP = new Param("namespace", URI.class, "uri to a the namespace", false, (Object)null, new KVP(new Object[]{"level", "advanced"}));
public static final Param MEMORY_MAPPED = new Param("memory mapped buffer", Boolean.class, "enable/disable the use of memory-mapped io", false, false, new KVP(new Object[]{"level", "advanced"}));
public static final Param CACHE_MEMORY_MAPS = new Param("cache and reuse memory maps", Boolean.class, "only memory map a file one, then cache and reuse the map", false, true, new KVP(new Object[]{"level", "advanced"}));
public static final Param FILE_TYPE = new Param("filetype", String.class, "Discriminator for directory stores", false, "shapefile", new KVP(new Object[]{"level", "program"}));
public static final Param CREATE_SPATIAL_INDEX = new Param("create spatial index", Boolean.class, "enable/disable the automatic creation of spatial index", false, true, new KVP(new Object[]{"level", "advanced"}));
public static final Param DBFCHARSET = new Param("charset", Charset.class, "character used to decode strings from the DBF file", false, Charset.forName("ISO-8859-1"), new KVP(new Object[]{"level", "advanced"})) {
public static final Param FSTYPE = new Param("fstype", String.class, "Enable using a setting of 'shape'.", false, "shape", new KVP(new Object[]{"level", "advanced", "options", Arrays.asList("shape-ng", "shape", "index")}));
public static final Param DBFTIMEZONE = new Param("timezone", TimeZone.class, "time zone used to read dates from the DBF file", false, TimeZone.getDefault(), new KVP(new Object[]{"level", "advanced"})) {
public static final Param ENABLE_SPATIAL_INDEX = new Param("enable spatial index", Boolean.class, "enable/disable the use of spatial index for local shapefiles", false, true, new KVP(new Object[]{"level", "advanced"}));
public static Coverage readTiff(String tiffPath) throws IOException {
File f = new File(tiffPath);
ParameterValue<OverviewPolicy> policy = AbstractGridFormat.OVERVIEW_POLICY.createValue();
policy.setValue(OverviewPolicy.IGNORE);
ParameterValue<String> gridsize = AbstractGridFormat.SUGGESTED_TILE_SIZE.createValue();
ParameterValue<Boolean> useJaiRead = AbstractGridFormat.USE_JAI_IMAGEREAD.createValue();
useJaiRead.setValue(true);
GridCoverage2D image = new GeoTiffReader(f).read(new GeneralParameterValue[]{policy, gridsize, useJaiRead});
return image;
}
File file = new File(outTiffPath);
GeoTiffWriter geoTiffWriter = new GeoTiffWriter(file);
final GeoTiffFormat format = new GeoTiffFormat();
final GeoTiffWriteParams wp = new GeoTiffWriteParams();
// 设置写出参数
wp.setCompressionMode(GeoTiffWriteParams.MODE_DEFAULT);
wp.setTilingMode(GeoToolsWriteParams.MODE_DEFAULT);
ParameterValueGroup paramWrite = format.getWriteParameters();
paramWrite.parameter(AbstractGridFormat.GEOTOOLS_WRITE_PARAMS.getName().toString()).setValue(wp);
geoTiffWriter.write((GridCoverage) coverage, paramWrite.values().toArray(new GeneralParameterValue[4]) );
geoTiffWriter.dispose();
private static Coverage clipImageToFeatureSource() throws IOException, FactoryException, MismatchedDimensionException, TransformException {
SimpleFeatureCollection collection = CommonMethod.readFeatureCollection("E:\\data\\shp\\mask.shp");
FeatureIterator<SimpleFeature> iterator = collection.features();
List<Geometry> all = new ArrayList<Geometry>();
try {
while (iterator.hasNext()) {
SimpleFeature feature = iterator.next();
Geometry geometry = (Geometry) feature.getDefaultGeometry();
all.add(geometry);
}
} finally {
if (iterator != null) {
iterator.close();
}
}
Coverage coverage = readTiff();
Coverage clippedCoverage = null;
if (all.size() > 0) {
CoverageProcessor processor = new CoverageProcessor();
ParameterValueGroup params = processor.getOperation("CoverageCrop")
.getParameters();
params.parameter("Source").setValue(coverage);
GeometryFactory factory = JTSFactoryFinder.getGeometryFactory(null);
Geometry[] a = all.toArray(new Geometry[0]);
GeometryCollection c = new GeometryCollection(a, factory);
Envelope envelope = all.get(0).getEnvelopeInternal();
double x1 = envelope.getMinX();
double y1 = envelope.getMinY();
double x2 = envelope.getMaxX();
double y2 = envelope.getMaxY();
ReferencedEnvelope referencedEnvelope = new ReferencedEnvelope(x1,x2, y1, y2, coverage.getCoordinateReferenceSystem());
params.parameter("ENVELOPE").setValue(referencedEnvelope);
params.parameter("ROI").setValue(c);
params.parameter("ForceMosaic").setValue(true);
clippedCoverage = processor.doOperation(params);
}
if (all.size() == 0){
System.out.println("Crop by shapefile requested but no simple features matched extent!");
}
return clippedCoverage;
}
// 得到图层所有要素
SimpleFeatureCollection collection = readFeatureCollection(shpPath);
// 通过过滤器进行筛选
SimpleFeatureCollection newCollection = collection.subCollection(filter);
得到过滤器的两种方式:
方式一:
// 直接书写筛选条件
Filter filter = CQL.toFilter("POP_RANK >= 5");
方式二:
// 使用工厂构造过滤器
FilterFactory ff = CommonFactoryFinder.getFilterFactory(null);
// greater表示大于
Filter filter = ff.greater(ff.property("POP_RANK"), ff.literal(5));
其它比较关系(非常多,可以进入类里面去查看支持那些关系比较):
// 相交
Filter filter = CQL.toFilter( "INTERSECTS(the_geom," + wkt + ")");
// 在10公里以内
Filter filter = CQL.toFilter( "DWITHIN(the_geom," + wkt + ", 10, kilometers)");
完整代码:
SimpleFeatureCollection china = readFeatureCollection("E:\\data\\shp\\china.shp");
SimpleFeatureCollection cities = readFeatureCollection("E:\\data\\shp\\cities.shp");
SimpleFeatureIterator chinaIterator = china.features();
Geometry geometry = null;
while (chinaIterator.hasNext()) {
SimpleFeature next = chinaIterator.next();
geometry = (Geometry) next.getDefaultGeometry();
}
Filter filter = getSpatialFilter(geometry);
SimpleFeatureCollection subCollection = cities.subCollection(filter);
更多用法参考:
https://docs.geotools.org/stable/userguide/library/cql/cql.html
public static void clip() {
SimpleFeatureCollection china = readFeatureCollection("E:\\data\\shp\\china.shp");
SimpleFeatureCollection countries = readFeatureCollection("E:\\data\\shp\\countries.shp");
SimpleFeature next = china.features().next();
Geometry geometry = (Geometry) next.getDefaultGeometry();
ClippedFeatureCollection clippedFeatureCollection = new ClippedFeatureCollection(countries, geometry, true);
SimpleFeatureIterator clipedFeatures = clippedFeatureCollection.features();
int gcount = 0;
while (clipedFeatures.hasNext()) {
SimpleFeature feature = clipedFeatures.next();
Collection<Property> properties = feature.getProperties();
Iterator<Property> iterator = properties.iterator();
while (iterator.hasNext()) {
Property property = iterator.next();
System.out.println(property.getName() + " " + property.getValue());
}
gcount ++;
}
System.out.println("裁剪后还剩下的元素!" + gcount);
}
public static void buffer() throws IOException, FactoryException {
SimpleFeatureCollection cities = readFeatureCollection("E:\\data\\shp\\cities.shp");
List<SimpleFeature> bufferResult = new ArrayList<>();
SimpleFeatureIterator iterator = cities.features();
SimpleFeatureType type = CommonMethod.createType(Polygon.class, "citesBuffer");
SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(type);
while (iterator.hasNext()) {
SimpleFeature simpleFeature = iterator.next();
Geometry geometry = (Geometry) simpleFeature.getDefaultGeometry();
Geometry buffer = geometry.buffer(10);
featureBuilder.add("1");
featureBuilder.add(buffer);
SimpleFeature bufferSimpleFeature = featureBuilder.buildFeature(null);
bufferResult.add(bufferSimpleFeature);
}
SimpleFeatureCollection collection = new ListFeatureCollection(type, bufferResult);
CommonMethod.createShp("E:\\data\\shp\\citiesBuffer.shp", collection);
}
public static void union() throws IOException {
SimpleFeatureCollection featureCollectionP1 = readFeatureCollection("E:\\data\\shp\\countries_part1.shp");
SimpleFeatureCollection featureCollectionP2 = readFeatureCollection("E:\\data\\shp\\countries_part2.shp");
List<SimpleFeature> features = new ArrayList<>();
SimpleFeatureIterator iterator1 = featureCollectionP1.features();
SimpleFeatureIterator iterator2 = featureCollectionP2.features();
while (iterator1.hasNext()) {
SimpleFeature simpleFeature = iterator1.next();
features.add(simpleFeature);
}
while (iterator2.hasNext()) {
SimpleFeature simpleFeature = iterator2.next();
features.add(simpleFeature);
}
SimpleFeatureCollection collection = new ListFeatureCollection(featureCollectionP1.getSchema(), features);
CommonMethod.createShp("E:\\data\\shp\\countries_union.shp", collection);
}
public static void merge() throws IOException {
SimpleFeatureCollection collection = readFeatureCollection("E:\\data\\shp\\countries_mergedata.shp");
SimpleFeatureIterator features = collection.features();
List<Polygon> polygons = new ArrayList<>();
while (features.hasNext()) {
SimpleFeature simpleFeature = features.next();
Geometry defaultGeometry = (Geometry) simpleFeature.getDefaultGeometry();
Geometry union = defaultGeometry.union();
polygons.add((Polygon) union);
}
Polygon[] ps = polygons.toArray(new Polygon[polygons.size()]);
MultiPolygon multiPolygon = new MultiPolygon(ps, new GeometryFactory());
Geometry union = multiPolygon.union();
SimpleFeatureType type = CommonMethod.createType(MultiPolygon.class, "countriesMerge");
SimpleFeatureBuilder builder = new SimpleFeatureBuilder(type);
builder.add("1");
builder.add(union);
SimpleFeature simpleFeature = builder.buildFeature(null);
List<SimpleFeature> featureList = new ArrayList<>();
featureList.add(simpleFeature);
SimpleFeatureCollection simpleFeatureCollection = new ListFeatureCollection(type, featureList);
// 生成矢量数据
CommonMethod.createShp("E:\\data\\shp\\countriesMerge.shp", simpleFeatureCollection);
}
public static void erase() throws IOException {
SimpleFeatureCollection subCollection = readFeatureCollection("E:\\data\\shp\\countries_differenceData.shp");
SimpleFeatureCollection collection = readFeatureCollection("E:\\data\\shp\\countriesMerge.shp");
SimpleFeatureIterator subFeatures = subCollection.features();
SimpleFeatureIterator features = collection.features();
Geometry subGeometry = null;
while (subFeatures.hasNext()) {
SimpleFeature simpleFeature = subFeatures.next();
subGeometry = (Geometry) simpleFeature.getDefaultGeometry();
}
Geometry geometry = null;
while (features.hasNext()) {
SimpleFeature simpleFeature = features.next();
geometry = (Geometry) simpleFeature.getDefaultGeometry();
}
Geometry difference = geometry.difference(subGeometry);
SimpleFeatureType type = CommonMethod.createType(MultiPolygon.class, "countriesDifference");
SimpleFeatureBuilder builder = new SimpleFeatureBuilder(type);
builder.add("1");
builder.add(difference);
SimpleFeature simpleFeature = builder.buildFeature(null);
List<SimpleFeature> featureList = new ArrayList<>();
featureList.add(simpleFeature);
SimpleFeatureCollection simpleFeatureCollection = new ListFeatureCollection(type, featureList);
// 生成矢量数据
CommonMethod.createShp("E:\\data\\shp\\countriesDifference.shp", simpleFeatureCollection);
}
public static void intersect() throws IOException {
SimpleFeatureCollection intersectCollection = readFeatureCollection("E:\\data\\shp\\countries_intersect.shp");
SimpleFeatureCollection countries = readFeatureCollection("E:\\data\\shp\\countries.shp");
SimpleFeatureIterator features = intersectCollection.features();
SimpleFeatureIterator countriesFeatures = countries.features();
List<Polygon> polygons = new ArrayList<Polygon>();
Geometry other = null;
while (features.hasNext()) {
SimpleFeature next = features.next();
other = (Geometry) next.getDefaultGeometry();
other = other.buffer(0);
}
List<Geometry> geometries = new ArrayList<>();
// 一个一个求交集,合并成一个大图层求交集会报错,还不清楚什么原因
while (countriesFeatures.hasNext()) {
SimpleFeature next = countriesFeatures.next();
Geometry defaultGeometry = (Geometry) next.getDefaultGeometry();
if (defaultGeometry instanceof MultiPolygon) {
MultiPolygon multiPolygon = (MultiPolygon) defaultGeometry;
int numGeometries = multiPolygon.getNumGeometries();
for (int i = 0; i < numGeometries; i ++) {
Geometry geometryN = multiPolygon.getGeometryN(i);
boolean valid = geometryN.isValid();
System.out.println("======>" + valid);
polygons.add((Polygon) multiPolygon.getGeometryN(i));
try {
Geometry intersection = other.intersection(geometryN);
geometries.add(intersection);
} catch (Exception e) {
Property fid = next.getProperty("FID");
System.out.println(fid.getValue());
}
}
} else {
Geometry union = defaultGeometry.union();
polygons.add((Polygon) union);
}
}
SimpleFeatureType type = CommonMethod.createType(MultiPolygon.class, "countriesIntersection");
SimpleFeatureBuilder builder = new SimpleFeatureBuilder(type);
List<SimpleFeature> featureList = new ArrayList<>();
for (int i = 0; i < geometries.size(); i ++) {
builder.add("1");
builder.add(geometries.get(i));
SimpleFeature simpleFeature = builder.buildFeature(null);
featureList.add(simpleFeature);
}
SimpleFeatureCollection simpleFeatureCollection = new ListFeatureCollection(type, featureList);
// 生成矢量数据
CommonMethod.createShp("E:\\data\\shp\\countriesIntersection.shp", simpleFeatureCollection);
}
// 方式一得到WGS84
CoordinateReferenceSystem targetCRS = DefaultGeographicCRS.WGS84;
// 方式二
CoordinateReferenceSystem targetCRS = CRS.decode("EPSG:4490");
坐标系EPSG查询网址:
https://epsg.io/
public static void projTransForm() throws FactoryException, IOException {
File file = new File("E:\\data\\shp\\single.shp");
FileDataStore store = FileDataStoreFinder.getDataStore(file);
FeatureSource featureSource = store.getFeatureSource();
SimpleFeatureType type = (SimpleFeatureType) featureSource.getSchema();
// 源坐标
CoordinateReferenceSystem sourceCRS = type.getCoordinateReferenceSystem();
// 目标坐标
CoordinateReferenceSystem targetCRS = CRS.decode("EPSG:4490");
// allow for some error due to different datums
boolean lenient = true;
MathTransform transform = CRS.findMathTransform(sourceCRS, targetCRS, lenient);
// 重新写文件
// 获取到要素集合
SimpleFeatureCollection featureCollection = (SimpleFeatureCollection) featureSource.getFeatures();
ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();
Map<String, Serializable> create = new HashMap<>();
File newShpFile = new File("E:\\data\\shp\\resingle.shp");
create.put("url", newShpFile.toURI().toURL());
create.put("create spatial index", Boolean.TRUE);
DataStore dataStore = dataStoreFactory.createNewDataStore(create);
SimpleFeatureType featureType = SimpleFeatureTypeBuilder.retype(type, targetCRS);
dataStore.createSchema(featureType);
// Get the name of the new Shapefile, which will be used to open the FeatureWriter
String createdName = dataStore.getTypeNames()[0];
Transaction transaction = new DefaultTransaction("Reproject");
try (FeatureWriter<SimpleFeatureType, SimpleFeature> writer =
dataStore.getFeatureWriterAppend(createdName, transaction);
SimpleFeatureIterator iterator = featureCollection.features()) {
while (iterator.hasNext()) {
// copy the contents of each feature and transform the geometry
SimpleFeature feature = iterator.next();
SimpleFeature copy = writer.next();
copy.setAttributes(feature.getAttributes());
Geometry geometry = (Geometry) feature.getDefaultGeometry();
Geometry geometry2 = JTS.transform(geometry, transform);
copy.setDefaultGeometry(geometry2);
writer.write();
}
transaction.commit();
writer.close();
} catch (Exception problem) {
problem.printStackTrace();
transaction.rollback();
} finally {
transaction.close();
}
}
主要使用的是MapContent + sld样式,但是有个致命的问题就是图例和标题用它非常难以实现,还需要进一步研究。
在生成地图之前我们先介绍一下SLD样式,它是一个XML文件定义了图层渲染的方式。一个完整的SLD文件如下:
<sld:StyledLayerDescriptor version="1.0.0" xmlns:sld="http://www.opengis.net/sld" xmlns:ogc="http://www.opengis.net/ogc" xmlns:xlink="http://www.w3.org/1999/xlink">
<sld:NamedLayer>
<sld:Name>countriessld:Name>
<sld:UserStyle>
<sld:Name>Style1sld:Name>
<sld:FeatureTypeStyle>
<sld:FeatureTypeName>countriessld:FeatureTypeName>
<sld:Rule>
<sld:Name>countriessld:Name>
<sld:Title>countriessld:Title>
<sld:PolygonSymbolizer>
<sld:Fill>
<sld:CssParameter name="fill">#97DBF2sld:CssParameter>
<sld:CssParameter name="fill-opacity">1sld:CssParameter>
sld:Fill>
sld:PolygonSymbolizer>
<sld:TextSymbolizer>
<sld:Label>
<ogc:PropertyName>LONG_NAMEogc:PropertyName>
sld:Label>
<sld:Font>
<sld:CssParameter name="font-family">????sld:CssParameter>
<sld:CssParameter name="font-family">0sld:CssParameter>
<sld:CssParameter name="font-size">14sld:CssParameter>
<sld:CssParameter name="font-style">normalsld:CssParameter>
<sld:CssParameter name="font-weight">normalsld:CssParameter>
sld:Font>
<sld:Fill>
<sld:CssParameter name="fill">#000000sld:CssParameter>
<sld:CssParameter name="fill-opacity">1.0sld:CssParameter>
sld:Fill>
sld:TextSymbolizer>
sld:Rule>
sld:FeatureTypeStyle>
sld:UserStyle>
sld:NamedLayer>
sld:StyledLayerDescriptor>
大致解释一下:
sld:Name 定义了图层的名字
sld:PolygonSymbolizer 定义面要素的填充方式颜色等,如果是点图层则是sld:PointSymbolizer, 如果线要素sld:LineSymbolizer.
sld:sld:TextSymbolizer 定义了文本的样式
读取样式:
StyleFactory styleFactory = CommonFactoryFinder.getStyleFactory();
SLDParser stylereader = new SLDParser(styleFactory, sldFile.toURI().toURL());
Style[] stylearray = stylereader.readXML();
更多样式相关:
https://docs.geotools.org/latest/userguide/library/render/style.html
其实我们不用手写这些样式文件,如果样式多手写效率非常低,我们可以先在Arcgis中调出想要的地图样式,在借助插件ArcGIS_SLD_Converter就可以导出样式。
使用方式:https://blog.csdn.net/qq_43259860/article/details/124429280
public static MapContent map = new MapContent();
// 添加图层
public static void addShapeLayer(String shpPath, String sldPath){
try{
File file = new File(shpPath);
ShapefileDataStore shpDataStore = null;
shpDataStore = new ShapefileDataStore(file.toURI().toURL());
//设置编码
Charset charset = Charset.forName("GB18030");
shpDataStore.setCharset(charset);
String typeName = shpDataStore.getTypeNames()[0];
SimpleFeatureSource featureSource = null;
featureSource = shpDataStore.getFeatureSource (typeName);
//SLD的方式
File sldFile = new File(sldPath);
StyleFactory styleFactory = CommonFactoryFinder.getStyleFactory();
SLDParser stylereader = new SLDParser(styleFactory, sldFile.toURI().toURL());
Style[] stylearray = stylereader.readXML();
Style style = stylearray[0];
Layer layer = new FeatureLayer(featureSource, style);
map.addLayer(layer);
}
catch(Exception e){
e.printStackTrace();
}
}
// 导出地图
public static void getMapContent(Map paras, String imgPath){
try{
double[] bbox = (double[]) paras.get("bbox");
double x1 = bbox[0], y1 = bbox[1], x2 = bbox[2], y2 = bbox[3];
int width = (int) paras.get("width"), height=(int) paras.get("height");
// 设置输出范围
CoordinateReferenceSystem crs = DefaultGeographicCRS.WGS84;
ReferencedEnvelope mapArea = new ReferencedEnvelope(x1, x2, y1, y2, crs);
// 初始化渲染器
StreamingRenderer sr = new StreamingRenderer();
sr.setMapContent(map);
// 初始化输出图像
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics g = bi.getGraphics();
((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
((Graphics2D) g).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
Rectangle rect = new Rectangle(0, 0, width, height);
// 绘制地图
sr.paint((Graphics2D) g, rect, mapArea);
//将BufferedImage变量写入文件中。
ImageIO.write(bi,"png",new File(imgPath));
}
catch(Exception e){
e.printStackTrace();
}
}
geotools封装了不少可以直接拿来用的工具,矢量和栅格是分开的还是比较好用,值得去探索一番。方法里面的注释也非常详细,每个参数都加了相应的描述。我前面列举的常见的空间分析都可以通过gp类来实现。
下面举一些简单的例子,由于是例子代码写的不是很规范, 大概示范一些这些API怎么用,栅格的gp工具我就不举例子了大概都一样几句代码就能搞定:
// gp 裁剪
public static void clip() throws IOException, ParseException {
SimpleFeatureCollection featureCollection = CommonMethod.readFeatureCollection("E:\\data\\bf.shp");
WKTReader wktReader = new WKTReader();
Geometry geometry = wktReader.read("MultiPolygon (((120.06170797626219837 30.54693549152121079, 120.12450974119508373 30.54442342092389495, 120.13644207653233309 30.49192114543999921, 120.06723453157628967 30.48878105719335707, 120.06170797626219837 30.54693549152121079)))");
ClippedFeatureCollection clippedFeatureCollection = new ClippedFeatureCollection(featureCollection,geometry, false);
CommonMethod.createShp("E:\\data\\clip.shp",clippedFeatureCollection);
}
// gp 缓冲区
public static void buffer() throws IOException {
BufferFeatureCollection bf = new BufferFeatureCollection();
SimpleFeatureCollection featureCollection = CommonMethod.readFeatureCollection("E:\\data\\bf.shp");
SimpleFeatureCollection simpleFeatureCollection = bf.execute(featureCollection, 000.1d, "czzb");
FileUtil.del("E:\\data\\afterBuffer.shp");
CommonMethod.createShp("E:\\data\\afterBuffer.shp",simpleFeatureCollection);
}
// gp 合并图层
public static void union() throws ClassNotFoundException, IOException {
UnionFeatureCollection unionFeatureCollection = new UnionFeatureCollection();
SimpleFeatureCollection first = CommonMethod.readFeatureCollection("E:\\data\\bf.shp");
SimpleFeatureCollection second = CommonMethod.readFeatureCollection("E:\\data\\hardData.shp");
SimpleFeatureCollection result = unionFeatureCollection.execute(first, second);
CommonMethod.createShp("E:\\data\\union.shp", result);
}
// gp 分组聚合属性 这个非常好用
public static void aggregate() throws IOException {
AggregateProcess aggregateProcess = new AggregateProcess();
SimpleFeatureCollection first = CommonMethod.readFeatureCollection("E:\\data\\bf.shp");
Set<AggregateProcess.AggregationFunction> functions = new HashSet<>();
// 要统计的类型
functions.add(AggregateProcess.AggregationFunction.Average);
functions.add(AggregateProcess.AggregationFunction.Sum);
functions.add(AggregateProcess.AggregationFunction.Max);
// 分组字段列表
List<String> groupFields = new ArrayList<>();
groupFields.add("dlmc");
groupFields.add("zldwdm");
AggregateProcess.Results execute = aggregateProcess.execute(first,"shape_leng", functions, false,groupFields,new NullProgressListener());
}
// gp 简化几何, 它的源码使用道格拉斯算法实现的
public static void simplify() throws Exception {
SimplifyProcess simplifyProcess = new SimplifyProcess();
SimpleFeatureCollection simpleFeatureCollection = CommonMethod.readFeatureCollection("E:\\data\\hardData.shp");
SimpleFeatureCollection result = simplifyProcess.execute(simpleFeatureCollection, 0.00001, true);
CommonMethod.createShp("E:\\data\\simpfy.shp", result);
}
// gp 投影变换
public void reproject() {
ReprojectProcess reprojectProcess = new ReprojectProcess();
SimpleFeatureCollection simpleFeatureCollection = CommonMethod.readFeatureCollection("E:\\data\\hardData.shp");
SimpleFeatureCollection projectCollection = reprojectProcess.execute(simpleFeatureCollection, CRS.decode("EPSG:4490", true), CRS.decode("EPSG:4549", true));
CommonMethod.createShp("E:\\data\\simpfy.shp", projectCollection);
}
// gp 相交
public static void intersect() throws ClassNotFoundException, IOException {
IntersectionFeatureCollection intersectionFeatureCollection = new IntersectionFeatureCollection();
SimpleFeatureCollection first = CommonMethod.readFeatureCollection("E:\\data\\bf.shp");
SimpleFeatureCollection second = CommonMethod.readFeatureCollection("E:\\data\\hardData.shp");
// 要保留的字段,我这里为了方便都是用的同一份字段
List<String> fretain = new ArrayList<>();
fretain.add("id");
fretain.add("mj");
fretain.add("dlmc");
SimpleFeatureCollection result = intersectionFeatureCollection.execute(first, second,fretain, fretain, IntersectionFeatureCollection.IntersectionMode.INTERSECTION,true, true);
CommonMethod.createShp("E:\\data\\intersect.shp", result);
}
下面我列举一下可能会遇到的问题:
1)如果你的图形非常复杂,数据是别人提供的,再运行GP类的时候可能会报拓扑错误,这个时候你可以采用buffer(0)来修复几何的拓扑。
2)在坐标转换的时候如果发生下图警告:
设置一些decode的第二个参数就能解决,千万不能不管这个警告不然转换出来的数据是有问题的。
在我没有设置第二个参数的情况下,我坐标转换出来的结果被旋转了90度,设置第二个参数为true就可以解决这个问题。
因为有朋友需要,在这里补充一下CommonMethod
说明一下,里面构造要素用的是最简单的方法直接写死的,就是为了方便测试功能,需要按自己的需求改一改。
package com.example.gis.utils;
import org.geotools.data.DefaultTransaction;
import org.geotools.data.FeatureSource;
import org.geotools.data.Transaction;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.shapefile.ShapefileDataStoreFactory;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.data.simple.SimpleFeatureStore;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.opengis.feature.simple.SimpleFeatureType;
import java.io.*;
import java.net.MalformedURLException;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
public class CommonMethod {
public static SimpleFeatureType createType(Class<?> c, String layerName) {
SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder();
builder.setCRS(DefaultGeographicCRS.WGS84);
builder.add("FID",String.class);
builder.add("the_geom", c);
// 设置了图层的名字
builder.setName(layerName);
SimpleFeatureType simpleFeatureType = builder.buildFeatureType();
return simpleFeatureType;
}
public static void createShp(String shpPath, SimpleFeatureCollection collection) throws IOException {
File shpFile = new File(shpPath);
ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();
SimpleFeatureType simpleFeatureType = collection.getSchema();
// 创造shpstore需要的参数
Map<String, Serializable> params = new HashMap<>();
params.put("url", shpFile.toURI().toURL());
params.put("create spatial index", Boolean.TRUE);
ShapefileDataStore newDataStore =
(ShapefileDataStore) dataStoreFactory.createNewDataStore(params);
newDataStore.createSchema(simpleFeatureType);
Transaction transaction = new DefaultTransaction("create");
String typeName = newDataStore.getTypeNames()[0];
SimpleFeatureSource featureSource = newDataStore.getFeatureSource(typeName);
SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource;
featureStore.setTransaction(transaction);
featureStore.addFeatures(collection);
featureStore.setTransaction(transaction);
transaction.commit();
transaction.close();
}
public static SimpleFeatureCollection readFeatureCollection(String shpPath) {
SimpleFeatureCollection featureCollection = null;
File shpFile = new File(shpPath);
try {
ShapefileDataStore shapefileDataStore = new ShapefileDataStore(shpFile.toURI().toURL());
// 设置编码,防止属性的中文字符出现乱码
shapefileDataStore.setCharset(Charset.forName("UTF-8"));
// 这个typeNamae不传递,默认是文件名称
FeatureSource featuresource = shapefileDataStore.getFeatureSource(shapefileDataStore.getTypeNames()[0]);
featureCollection = (SimpleFeatureCollection) featuresource.getFeatures();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return featureCollection;
}
}