最近在项目中由于导入的OSM(OpenStreetMap)数据量太大,暴露了一个先前就藏匿着的bug,只不过,随着数据量的增加,实例越来越多,测试就更加逼近实战,于是,这个bug终于爆炸了,只见控制台输出():
Invalid number of points in LinearRing (found 3 - must be 0 or >= 4)
异常捕获消息:java.lang.RuntimeException: 导入异常XXXXXX
只要是来路明确的bug,都别怕好吧,怕就怕那种异常我们也没有捕获,程序就戛然而止,留下一脸懵逼的我们无从下手
那为什么会出现这种异常呢?我们看异常提示就知道:
首先构建LinearRing的时候,点数(Points)不够,提示我们只发现了3个,而我们的LinearRing是一个闭合的线性环,由它我们再来构建多边形Polygon,
其次后面还有一句“must be 0 or >=4”,这个就厉害了,直接把错误的解决方案告诉我们了
“你要构建LinearRing行,请保证你的点数要么是0个,要么超过3个!” -- 硬性指标,有没有
好了,自己挖的坑,自己填 --"肯定是在导数据的时候,在构建Geometry的时候出现了这种低级错误"
至于,为什么是“must be 0 or >= 4”,我下面会讲到
一、Pom文件
pom只贴出和Geometry相关的依赖,比如我们用到了GeoTools工具包
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.appleyk</groupId>
<artifactId>spring-boot-web</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Java-Web</name>
<!-- 继承官网最新父POM【假设当前项目不再继承其他POM】 -->
<!-- http://projects.spring.io/spring-boot/#quick-start -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
</parent>
<!-- 使用Java8,嘗試使用新特新【stream和lambda】 -->
<properties>
<java.version>1.8</java.version>
<geotools.version>17.0</geotools.version>
<postgresql.version>42.1.4</postgresql.version>
</properties>
<repositories>
<repository>
<id>osgeo</id>
<name>Open Source Geospatial Foundation Repository</name>
<url>http://download.osgeo.org/webdav/geotools/</url>
</repository>
</repositories>
<!-- Starter POMs是可以包含到应用中的一个方便的依赖关系描述符集合 -->
<!-- 该Starters包含很多你搭建项目, 快速运行所需的依赖, 并提供一致的, 管理的传递依赖集。 -->
<!-- 大多数的web应用都使用spring-boot-starter-web模块进行快速搭建和运行。 -->
<!-- spring-boot-starter-web -->
<!-- 对全栈web开发的支持, 包括Tomcat和 spring-webmvc -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring 单元测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 添加thymeleaf 支持页面跳转 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- 添加热部署 devtools:监听文件变动 -->
<!-- 当Java文件改动时,Spring-boo会快速重新启动 -->
<!-- 最简单的测试,就是随便找一个文件Ctrl+S一下,就可以看到效果 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<!-- optional=true,依赖不会传递 -->
<!-- 本项目依赖devtools;若依赖本项目的其他项目想要使用devtools,需要重新引入 -->
<optional>true</optional>
</dependency>
<!-- JUnit单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<!-- 添加GeoTools依赖 -->
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-shapefile</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-swing</artifactId>
<version>${geotools.version}</version>
</dependency>
</dependencies>
<!-- Spring Boot包含一个Maven插件, 它可以将项目打包成一个可执行jar -->
<build>
<!-- 解决配置资源文件被漏掉问题 -->
<resources>
<!-- 如果出现thymeleaf无法渲染html模板,请加上这个 -->
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
<plugins>
<!-- boot-maven插件 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<includes>
<include>**/*Documentation.java</include>
</includes>
</configuration>
</plugin>
</plugins>
</build>
<packaging>war</packaging>
</project>
二、单元测试构建Geometry的几种方式
(1)项目所在的测试单元文件的目录图
(2)GeometryTest.java
package com.appleyk.geotools.learn;
import org.junit.Test;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
public class GeometryTest {
private GeometryFactory geometryFactory = new GeometryFactory();
@Test
public void Test() {
Coordinate coords[] = new Coordinate[4];
coords[0] = new Coordinate(113.6248492, 34.7384023);
coords[1] = new Coordinate(113.6248465, 34.7381533);
coords[2] = new Coordinate(113.6248492, 34.7384024);
coords[3] = new Coordinate(113.6248492, 34.7384023);
Geometry geometry = null;
if(isClosed(coords)){
//如果是闭合的多边形
geometry = geometryFactory.createPolygon(coords);
}else{
if(coords.length==1){
//如果坐标数组就一个元素的话,除了Point,我想不到其他Geometry
geometry = geometryFactory.createPoint(coords[0]);
}else{
//否则的话,不闭合也不是点,那么它只能是线了--- LineString
geometry =geometryFactory.createLineString(coords);
}
}
System.err.println("类型:"+geometry.getGeometryType()+"\n形态:"+geometry);
}
/**
* 判斷 -- 是否是闭合的(Polygon)
* @param coords
* @return
*/
public boolean isClosed(Coordinate coords[]){
/**
* 闭合条件
* 1.点(坐标)数组不等于空
* 2.点(坐标)数组至少含4个元素(>=4 or >3 -- 最基本的闭合多边形是---> 三角形)
* 3.点(坐标)数组首尾元素相当(关键条件,所谓的闭合,也就是首尾点是同一个点,绕了一圈又绕回来了才称之为闭合)
*/
return coords!=null && coords.length>3 && coords[0].equals(coords[coords.length-1]);
}
}
(3)测试一番
A. 构建Point (点)
Test
B. 构建LineString (线)
Test
C. 构建LinearRing(闭合线环)
正确的做法:
Test
根据LinearRing可以构建我们的Polygon(不再演示)
错误的做法
Test
最后,我们看一下,刚才正确创建的LinearRing长什么样,
Coordinate coords[] = new Coordinate[4];
coords[0] = new Coordinate(113.6248492, 34.7384023);
coords[1] = new Coordinate(113.6248465, 34.7381533);
coords[2] = new Coordinate(113.6245214, 34.7332024);
coords[3] = new Coordinate(113.6248492, 34.7384023);
看似是两条线,其实它是一个闭合的多边形,只不过坐标我们给的太紧奏了