赞
踩
项目中需要实现矢量数据的shp文件转成geojson的需求,计划通过geotools工具来实现。
geotools:GeoTools是一个开源的Java库,用于处理地理空间数据和执行空间分析。它提供了丰富的GIS(地理信息系统)功能和工具,可以处理包括矢量数据、栅格数据、影像数据等不同类型的地理数据,支持空间对象操作、地图投影和坐标转换、空间查询和空间分析等能力。
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-shapefile</artifactId>
<version>29.0</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-geojson</artifactId>
<version>29.0</version>
</dependency>
下面这段代码实现将shp文件转化成geojson格式的文件:
@SneakyThrows @Test public void shpToGeo() { // shp文件 File file = new File("D:\\test\\shp\\city.shp"); Map<String, Object> map = new HashMap<>(); map.put("url", URLs.fileToUrl(file)); DataStore dataStore = DataStoreFinder.getDataStore(map); String typeName = dataStore.getTypeNames()[0]; SimpleFeatureSource featureSource = dataStore.getFeatureSource(typeName); // geojson文件 File geojsonFile = new File("D:\\test\\shp\\city.geojson"); SimpleFeatureCollection featureCollection = featureSource.getFeatures(); FileOutputStream geoJsonOutputStream = new FileOutputStream(geojsonFile); // 写入数据 new FeatureJSON().writeFeatureCollection(featureCollection, geoJsonOutputStream); }
运行上面的代码进行格式转换,可能会报NPE异常:
根据异常信息,直接定位到源头(上图红框内),查看一下SystemUtils.isJavaVersionAtLeast的处理逻辑。
下面是SystemUtils.isJavaVersionAtLeast的处理逻辑与代码调用链路:
/** * 判断java版本是否符合要求 */ public static boolean isJavaVersionAtLeast(JavaVersion requiredVersion) { return JAVA_SPECIFICATION_VERSION_AS_ENUM.atLeast(requiredVersion); } public boolean atLeast(JavaVersion requiredVersion) { return this.value >= requiredVersion.value; } // java 指定版本 JAVA_SPECIFICATION_VERSION_AS_ENUM = JavaVersion.get(JAVA_SPECIFICATION_VERSION); public static final String J AVA_SPECIFICATION_VERSION = getSystemProperty("java.specification.version"); // 读取系统属性 private static String getSystemProperty(String property) { try { return System.getProperty(property); } catch (SecurityException var2) { System.err.println("Caught a SecurityException reading the system property '" + property + "'; the SystemUtils property value will default to null."); return null; } }
首先是判断JAVA_SPECIFICATION_VERSION_AS_ENUM与requiredVersion的值,这里的JAVA_SPECIFICATION_VERSION_AS_ENUM指的是java的指定版本,requiredVersion指的是运行该段代码要求的Java版本。下面看一下这两个值分别来自哪里。
JAVA_SPECIFICATION_VERSION_AS_ENUM是JavaVersion(org.apache.commons.lang3这个包)的一个枚举值:
static JavaVersion get(String nom) { if ("0.9".equals(nom)) { return JAVA_0_9; } else if ("1.1".equals(nom)) { return JAVA_1_1; } else if ("1.2".equals(nom)) { return JAVA_1_2; } else if ("1.3".equals(nom)) { return JAVA_1_3; } else if ("1.4".equals(nom)) { return JAVA_1_4; } else if ("1.5".equals(nom)) { return JAVA_1_5; } else if ("1.6".equals(nom)) { return JAVA_1_6; } else if ("1.7".equals(nom)) { return JAVA_1_7; } else if ("1.8".equals(nom)) { return JAVA_1_8; } else if ("9".equals(nom)) { return JAVA_9; } else if ("10".equals(nom)) { return JAVA_10; } else if (nom == null) { return null; } else { float v = toFloatVersion(nom); if ((double)v - 1.0 < 1.0) { int firstComma = Math.max(nom.indexOf(46), nom.indexOf(44)); int end = Math.max(nom.length(), nom.indexOf(44, firstComma)); if (Float.parseFloat(nom.substring(firstComma + 1, end)) > 0.9F) { return JAVA_RECENT; } } return null; } }
从上面的代码可以看出对Java版本的判断只到Java10,Java10以后的结果直接返回null(NPE就来自这里)了,猜测原因应该是工程中引入的lang3依赖的版本比较老(3.7),该版本发布时,Java10以后的jdk版本尚未发布。
下面看一下requiredVersion的值,根据报错信息org.geotools.util.NIOUtilities.clean这个方法调用了 org.apache.commons.lang3.SystemUtils.isJavaVersionAtLeast这个方法触发了NPE,下面看一下clean()这个方法的实现逻辑:
public static boolean clean(ByteBuffer buffer) {
if (buffer != null && buffer.isDirect()) {
PrivilegedAction<Boolean> action = SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_9) ? () -> {
return (new CleanupAfterJdk8(buffer)).clean();
} : () -> {
return (new CleanupPriorJdk9(buffer)).clean();
};
return (Boolean)AccessController.doPrivileged(action);
} else {
return true;
}
}
clean()这个方法中调用了SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_9),也就是说传递给SystemUtils.isJavaVersionAtLeast(JavaVersion requiredVersion)这个方法的requiredVersion参数的值是9。
根据上面的分析,可以在下面这个地方打个断点验证一下:
根据上面的断点信息可以看出requiredVersion9,JAVA_SPECIFICATION_VERSION_AS_ENUMnull。
再在下面这个地方打个断点看一下:
可以看出从系统属性中读取出来的JAVA_SPECIFICATION_VERSION==11,也就是工程指定的jdk版本,然后从下面这段代码读取JAVA_SPECIFICATION_VERSION_AS_ENUM枚举值的时候,返回了null:
static JavaVersion get(String nom) { if ("0.9".equals(nom)) { return JAVA_0_9; } else if ("1.1".equals(nom)) { return JAVA_1_1; } else if ("1.2".equals(nom)) { return JAVA_1_2; } else if ("1.3".equals(nom)) { return JAVA_1_3; } else if ("1.4".equals(nom)) { return JAVA_1_4; } else if ("1.5".equals(nom)) { return JAVA_1_5; } else if ("1.6".equals(nom)) { return JAVA_1_6; } else if ("1.7".equals(nom)) { return JAVA_1_7; } else if ("1.8".equals(nom)) { return JAVA_1_8; } else if ("9".equals(nom)) { return JAVA_9; } else if ("10".equals(nom)) { return JAVA_10; } else if (nom == null) { return null; } else { float v = toFloatVersion(nom); if ((double)v - 1.0 < 1.0) { int firstComma = Math.max(nom.indexOf(46), nom.indexOf(44)); int end = Math.max(nom.length(), nom.indexOf(44, firstComma)); if (Float.parseFloat(nom.substring(firstComma + 1, end)) > 0.9F) { return JAVA_RECENT; } } return null; } }
上面根据代码调用链已经分析出了导致NPE的原因,原因在于org.apache.commons.lang3.JavaVersion的get()方法返回了null,上面也猜测了原因,应该是工程中引入的lang3依赖的版本比较老(3.7版本),该版本发布时,Java10以后的jdk版本尚未发布。那么沿着这个思路,升级commons-lang3的版本,将commons-lang3升级至3.12.0
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
升级版本后,查看org.apache.commons.lang3.JavaVersion的get()方法:
static JavaVersion get(String versionStr) { if (versionStr == null) { return null; } else { switch (versionStr) { case "0.9": return JAVA_0_9; case "1.1": return JAVA_1_1; case "1.2": return JAVA_1_2; case "1.3": return JAVA_1_3; case "1.4": return JAVA_1_4; case "1.5": return JAVA_1_5; case "1.6": return JAVA_1_6; case "1.7": return JAVA_1_7; case "1.8": return JAVA_1_8; case "9": return JAVA_9; case "10": return JAVA_10; case "11": return JAVA_11; case "12": return JAVA_12; case "13": return JAVA_13; case "14": return JAVA_14; case "15": return JAVA_15; case "16": return JAVA_16; case "17": return JAVA_17; default: float v = toFloatVersion(versionStr); if ((double)v - 1.0 < 1.0) { int firstComma = Math.max(versionStr.indexOf(46), versionStr.indexOf(44)); int end = Math.max(versionStr.length(), versionStr.indexOf(44, firstComma)); if (Float.parseFloat(versionStr.substring(firstComma + 1, end)) > 0.9F) { return JAVA_RECENT; } } else if (v > 10.0F) { return JAVA_RECENT; } return null; } } }
从上面代码可以看出升级版本后的代码能够正常判断jdk17及以前的java版本。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。