geotools操作GeoJSON过程中的问题及相关源码(下)

        接上回,本文探讨下第3(坐标精度丢失)、第4(默认无坐标系和空值输出)、第5(由坐标顺序引发坐标变换)这三个问题。我用GeoJSON的static void write(Object obj, Object output)静态方法将FeatureCollection转化成了json文本输出,先看org.geotools.geojson.GeoJSON源码:

// 该类用于FeatureCollection、Feature和坐标系的JSON输出
public class GeoJSON {
    static GeometryJSON gjson = new GeometryJSON();
    static FeatureJSON fjson = new FeatureJSON();  // 用的默认构造器
    public static Object read(Object input) throws IOException {
        throw new UnsupportedOperationException();
    }
    public static void write(Object obj, Object output) throws IOException {
        if (obj instanceof Geometry) {
            gjson.write((Geometry)obj, output);
        }
        else if (obj instanceof Feature || obj instanceof FeatureCollection ||  
                obj instanceof CoordinateReferenceSystem) {
            
            if (obj instanceof SimpleFeature) {
                fjson.writeFeature((SimpleFeature)obj, output); 
            }
            else if (obj instanceof FeatureCollection) {
                fjson.writeFeatureCollection((FeatureCollection)obj, output);
            }
            else if (obj instanceof CoordinateReferenceSystem) {
                fjson.writeCRS((CoordinateReferenceSystem)obj, output);
            }
            else {
                throw new IllegalArgumentException("Unable able to encode object of type " + obj.getClass());
            }
        }
    }
}

该类除写Geometry外都是调用FeatureJSON的方法,在看下FeatureJSON的构造器和实例变量:

    // org.geotools.geojson.feature.FeatureJSON
    GeometryJSON gjson;  // 决定坐标保留的位数
    SimpleFeatureType featureType;
    AttributeIO attio;
    boolean encodeFeatureBounds = false;   // true表示json文本中Feature输出bbox
    boolean encodeFeatureCollectionBounds = false;    // true表示json文本中FeatureCollection输出bbox
    boolean encodeFeatureCRS = false;   // true表示json文本中Feature输出坐标系
    boolean encodeFeatureCollectionCRS = false;   // true表示json文本中FeatureCollection输出坐标系
    boolean encodeNullValues = false;   // true表示识别值为null的属性
    
    public FeatureJSON() {
        this(new GeometryJSON());   // GeometryJSON默认保留4为小数
    }
    public FeatureJSON(GeometryJSON gjson) {  //  自定义GeometryJSON,可控制小数位数
        this.gjson = gjson; 
        attio = new DefaultAttributeIO();
    }

        GeometryJSON的相关代码就不列出来了。解决第3(坐标精度丢失)、第4(默认无坐标系和空值输出)问题我们只需做一些设置。如果想统一用GeoJSON.write()方法写json文本,可以重写该类,设置精度,代码如下:

// 重写后的GeoJSON
public class GeoJSON {
    static GeometryJSON gjson = new GeometryJSON(15);  // 15位小数
    static FeatureJSON fjson = new FeatureJSON(gjson);  // 指定GeometryJSON
    public static Object read(Object input) throws IOException {
        throw new UnsupportedOperationException();
    }
    public static void write(Object obj, Object output) throws IOException {
        if (obj instanceof Geometry) {
            gjson.write((Geometry)obj, output);
        }
        else if (obj instanceof Feature || obj instanceof FeatureCollection ||  
                obj instanceof CoordinateReferenceSystem) {

            // 值为null的属性也识别
            fjson.setEncodeNullValues(true);
            // 输出坐标系文本
            fjson.setEncodeFeatureCollectionCRS(true);            
            if (obj instanceof SimpleFeature) {
                fjson.writeFeature((SimpleFeature)obj, output); 
            }
            else if (obj instanceof FeatureCollection) {
                fjson.writeFeatureCollection((FeatureCollection)obj, output);
            }
            else if (obj instanceof CoordinateReferenceSystem) {
                fjson.writeCRS((CoordinateReferenceSystem)obj, output);
            }
            else {
                throw new IllegalArgumentException("Unable able to encode object of type " + obj.getClass());
            }
        }
    }
}

如果就想用FeatureJSON操作输出,可以在测试代码中添加如下代码解决:

// fjson_15已经保留15位
fjson_15.setEncodeFeatureCollectionCRS(true);
fjson_15.setEncodeNullValues(true);
fjson_15.writeFeatureCollection(featureCollection,System.out);  // 控制台输出和原始geojson一致

        针对第5(由坐标顺序引发坐标变换)个问题,“org.geotools.referencing.operation.projection.ProjectionException: Latitude 116°11.8’N is too close to a pole”异常其实是由坐标顺序不正确导致,经纬度顺序调换后识别的坐标超出了范围,不是当前坐标系能表示的值了。这是一个隐藏问题,在处理另一些原数据或变换不同的坐标系时,不一定会产生这个异常,那用不合理的坐标顺序得到的结果是不正确的。调整代码如下即可以解决:

String srs = CRS.lookupIdentifier(simpleFeatureType.getCoordinateReferenceSystem(),true);
// CRS.decode()方法可以设置经纬度顺序
MathTransform transform_2 = CRS.findMathTransform(CRS.decode(srs,true), CRS.decode("EPSG:3857",true),true);
Geometry geom_3857 = JTS.transform(geom, transform_2);
System.out.println(geom_3857.getArea());  // 输出:6.501222710260582E9

        测试代码里输出了几何对象geom的面积,但这个面积很粗糙,只做测试用。给出的GeoJSON文本中的”area”值也只做参考,不是最精确的面积。大家都知道,EPSG:3857以EPSG:4326地理坐标系和投影方式为伪墨卡托的平面坐标系,给出的面积偏差较大。

转载自:https://blog.csdn.net/aliasone/article/details/80218540

You may also like...