多边形线、面的自相交问题一直是个头疼的问题!网上的处理方式有多种,Polygonizer使用线从新绘制是一种,但会漏掉中间的孔洞。昨天突然想到一个巧妙的解决方法。程序员不多说,直接上代码了。巧妙方法来了_ 。
处理前:
处理后:
工具类代码:
package com.vx.utils;
import com.vividsolutions.jts.geom.*;
import com.vividsolutions.jts.operation.valid.IsValidOp;
import com.vividsolutions.jts.operation.valid.TopologyValidationError;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class PolygonUtil {
private static Logger logger= LoggerFactory.getLogger(PolygonUtil.class);
private static PrecisionModel precisionModel = new PrecisionModel(1);
private static GeometryFactory geometryFactory = new GeometryFactory(precisionModel, 0);
public static Geometry conventPolygon(String geometryStr) {
Geometry geometry1 = JTSNewUtil.geometry(geometryStr);
if(geometry1 instanceof MultiPolygon){
int numGeometries = geometry1.getNumGeometries();
Geometry multiPolygon=geometry1.getFactory().createMultiPolygon(new Polygon[]{});
for(int i=0;i<numGeometries;i++){
Geometry geometryN = geometry1.getGeometryN(i);
Geometry polygon = getPolygonByInteriorPoint((Polygon) geometryN);
multiPolygon= multiPolygon.union(polygon);
}
geometry1=multiPolygon;
}else if(geometry1 instanceof Polygon){
geometry1 = getPolygonByInteriorPoint((Polygon) geometry1);
}
return geometry1;
}
/**
* 使用jts的两线相交方法解决线的自相交问题
* @param geometry1
* @return
*/
private static Geometry getPolygonByInteriorPoint(Polygon geometry1) {
IsValidOp isValidOp = new IsValidOp(geometry1);
TopologyValidationError validationError = isValidOp.getValidationError();
//如果为空,说明没有自相交的线
if(validationError==null){
return geometry1;
}
LineString exteriorRing = geometry1.getExteriorRing();
Coordinate[] coordinates = exteriorRing.intersection(exteriorRing).getCoordinates();
//使用点集合重新生成面
Geometry polygon1=coordinates2Polygon(coordinates);
//对内部点进行重新挖空
int numInteriorRing = geometry1.getNumInteriorRing();
for(int i=0;i<numInteriorRing;i++){
LineString interiorRingN = geometry1.getInteriorRingN(i);
Polygon interiorPolygon = geometry1.getFactory().createPolygon(interiorRingN.getCoordinates());
// polygon1= JTSNewUtilGDAL.tailor(polygon1,interiorPolygon);
polygon1= polygon1.difference(interiorPolygon);
}
return polygon1;
}
/**
* 遍历所有的点集合,将他们压如栈中,同时判断栈中是否有相同点,如果有就祛除栈中的点生成局部面。
* 通过这种方式,可以达到遍历一次点,就可以矫正面的线的相交的问题
*
* 终极奥义就是 压栈--出栈 !!!!
* @param coordinates
* @return
*/
private static Geometry coordinates2Polygon(Coordinate[] coordinates) {
Geometry resultPolygon = null;
Stack<Coordinate> coordinateStack=new Stack();
Coordinate lastCoord=null;
Coordinate nowDouble=null;
for(int i=0;i<coordinates.length;i++){
nowDouble=coordinates[i];
//因为传入的点集合,有连续两个点坐标一样的点,这样的点需要提出。没有中间点的两个点怎么能组合成面呢 ^_^
if(lastCoord!=null && nowDouble.equals(lastCoord)){
continue;
}
lastCoord=nowDouble;
//判断栈中是否已经包含相同的点,如果有,就可以提取相同点到相同点之间的所有点,组合成局部面
if(coordinateStack.contains(nowDouble)){
List<Coordinate> topoCoords=new ArrayList<>();
topoCoords.add(nowDouble);
Coordinate pop ;
do {
pop=coordinateStack.pop();
topoCoords.add(pop);
}while (!pop.equals(nowDouble));
//将交点再放回栈中
coordinateStack.push(nowDouble);
//生成局部面
resultPolygon = coordinateToPolygon(resultPolygon, topoCoords);
}else {
coordinateStack.push(nowDouble);
}
}
if(coordinateStack.size()>1){
resultPolygon= coordinateToPolygon(resultPolygon, coordinateStack) ;
}
return resultPolygon;
}
/**
* 点组合成面
* @param resultPolygon
* @param topoCoords
* @return
*/
private static Geometry coordinateToPolygon(Geometry resultPolygon,List topoCoords) {
//小于四个点的不进行处理,因为组织一个面最少4个点(收尾两个点坐标一致)
if(topoCoords.size()<4){
return resultPolygon;
}
Geometry polygon= geometryFactory.createPolygon((Coordinate[]) topoCoords.toArray(new Coordinate[]{})).buffer(0);
if(resultPolygon==null){
resultPolygon=polygon;
}else {
// resultPolygon=JTSNewUtilGDAL.symDifference(resultPolygon, (Polygon) polygon);
resultPolygon=resultPolygon.symDifference(polygon);
}
return resultPolygon;
}
}
(如果有问题请留言,如果对你有帮助,请点赞!)