Geotools(空间分析,栅格,坐标系,查询,简易地图)

1,840 阅读7分钟

简介

概述:GeoTools 是一个开源 (LGPL) Java 代码库,它为操作地理空间数据提供符合标准的方法,例如实现地理信息系统。GeoTools 库数据结构基于开放地理空间联盟 (OGC) 规范。 官网地址: www.geotools.org/ 常用maven库地址: repo.osgeo.org/repository/… 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

Geotools使用

矢量数据

  • 读取shp格式
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("读取完成!");
   }
  • 读取wkb格式

ex : 0000010300000001000000A4000000B98D06F0163C5E408D28ED0DBE684440B98D06F0163C5E408D28ED0DBE684440B98D06F0163C5E408D28ED0DBE684440B98D06F0163C5E408D28ED0DBE684440075F984C153C5E4029C

WKBReader wkbReader = new WKBReader();
Geometry geometry = wkbReader.read(WKBReader.hexToBytes(tokens[i]));
  • 读取wkt

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);
  • 读取geojson

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()));
   });
}
  • 新增要素(创建图层) 1.构造TYPE(定义数据的坐标参考、属性字段)
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"}));

栅格数据

  • 读取TIFF
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;
 }
  • 生成TIFF
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);

更多用法参考: docs.geotools.org/stable/user…

空间分析

  • 裁剪
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查询网址: 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文件如下:

<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
<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>countries</sld:Name>
    <sld:UserStyle>
      <sld:Name>Style1</sld:Name>
      <sld:FeatureTypeStyle>
        <sld:FeatureTypeName>countries</sld:FeatureTypeName>
        <sld:Rule>
          <sld:Name>countries</sld:Name>
          <sld:Title>countries</sld:Title>
          <sld:PolygonSymbolizer>
            <sld:Fill>
              <sld:CssParameter name="fill">#97DBF2</sld:CssParameter>
              <sld:CssParameter name="fill-opacity">1</sld:CssParameter>
            </sld:Fill>
          </sld:PolygonSymbolizer>
          <sld:TextSymbolizer>
            <sld:Label>
              <ogc:PropertyName>LONG_NAME</ogc:PropertyName>
            </sld:Label>
            <sld:Font>
              <sld:CssParameter name="font-family">????</sld:CssParameter>
              <sld:CssParameter name="font-family">0</sld:CssParameter>
              <sld:CssParameter name="font-size">14</sld:CssParameter>
              <sld:CssParameter name="font-style">normal</sld:CssParameter>
              <sld:CssParameter name="font-weight">normal</sld:CssParameter>
            </sld:Font>
            <sld:Fill>
              <sld:CssParameter name="fill">#000000</sld:CssParameter>
              <sld:CssParameter name="fill-opacity">1.0</sld: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();

更多样式相关: docs.geotools.org/latest/user… 其实我们不用手写这些样式文件,如果样式多手写效率非常低,我们可以先在Arcgis中调出想要的地图样式,在借助插件ArcGIS_SLD_Converter就可以导出样式。 使用方式:blog.csdn.net/qq_43259860…

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();
   }
}

运行结果: 在这里插入图片描述

因为有朋友需要,在这里补充一下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;
    }
}