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

简介

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

空间库的操作请看我的另一篇文章:
https://blog.csdn.net/qq_43259860/article/details/135503096

数据支持

栅格数据

  • 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);

更多用法参考:
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/

  • 得到epsg code
/**
 * 获取crsCode
 * @param crs 坐标系参考
 * @return 例如4490,4326
 */
public static String getCrsCode(CoordinateReferenceSystem crs) {
    try {
        String code = CRS.lookupIdentifier( crs, true );
        return code;
    } catch (FactoryException e) {
        throw new BusinessException(3000,"系统内部异常!");
    }
}
  • 坐标转换
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();

更多样式相关:
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();
   }
}

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

GP 工具

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

欢迎各位大佬加入群聊一起学习和探讨GIS方面的技术:
757174232

  • 35
    点赞
  • 140
    收藏
    觉得还不错? 一键收藏
  • 27
    评论
非常抱歉,我之前的回答有误。Geotools确实不支持直接的投影转换到EPSG:4479坐标系。 EPSG:4479代表了ETRS89地理坐标系,它使用欧洲地面参考系统1989(ETRS89)作为基准。在Geotools中,大多数投影转换都是基于投影坐标系,而不是地理坐标系。 如果你希望进行与EPSG:4479相关的操作,可能需要考虑以下两种方式: 1. 将你的数据从投影坐标系转换到EPSG:4326(WGS84)地理坐标系,然后再进行与EPSG:4479的转换。这可以通过使用Geotools中的投影转换工具来实现。 2. 考虑使用其他的GIS库或工具,如GDAL(Geospatial Data Abstraction Library),它提供了更广泛的坐标系支持,包括对EPSG:4479的转换。 请注意,无论你选择哪种方式,确保你有正确的Bursa-Wolf参数(七参数或三参数)用于进行转换。这些参数通常用于处理不同基准之间的差异。 对于EPSG:4479,下面是一个示例使用Geotools进行从投影坐标系(如EPSG:3857)到EPSG:4479地理坐标系的转换代码: ```java CoordinateReferenceSystem sourceCRS = CRS.decode("EPSG:3857"); CoordinateReferenceSystem targetCRS = CRS.decode("EPSG:4479"); MathTransform transform = CRS.findMathTransform(sourceCRS, targetCRS, true); DirectPosition2D sourcePos = new DirectPosition2D(sourceCRS, x, y); DirectPosition2D targetPos = new DirectPosition2D(); transform.transform(sourcePos, targetPos); double targetX = targetPos.getX(); double targetY = targetPos.getY(); ``` 请注意,以上示例中的转换是从EPSG:3857投影坐标系到EPSG:4479地理坐标系。确保你根据实际情况调整源和目标坐标系的EPSG代码。 希望这可以帮助到你。再次对之前的回答错误表示抱歉。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 27
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值