123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254 |
- package com.shkpr.service.alambizplugin.components.checker;
- import com.global.base.log.LogLevelFlag;
- import com.global.base.log.LogPrintMgr;
- import com.shkpr.service.alambizplugin.commtools.BeanUtil;
- import com.shkpr.service.alambizplugin.components.AsyncResultManager;
- import com.shkpr.service.alambizplugin.constants.GisSurveySystemCheckKeys;
- import com.shkpr.service.alambizplugin.constants.GisSurveySystemCheckResultHead;
- import com.shkpr.service.alambizplugin.constants.LogFlagBusiType;
- import com.shkpr.service.alambizplugin.dto.GisSurveyLayerApplyLine;
- import com.shkpr.service.alambizplugin.dto.GisSurveySystemCheckElement;
- import com.shkpr.service.alambizplugin.dto.GisSurveySystemCheckId;
- import com.shkpr.service.alambizplugin.dto.GisSurveySystemCheckResultDetail;
- import org.apache.commons.collections4.CollectionUtils;
- import org.locationtech.jts.algorithm.Orientation;
- import org.locationtech.jts.geom.Coordinate;
- import org.locationtech.jts.geom.LineSegment;
- import org.locationtech.jts.geom.LineString;
- import org.locationtech.jts.index.strtree.STRtree;
- import org.springframework.scheduling.annotation.Async;
- import org.springframework.scheduling.annotation.AsyncResult;
- import org.springframework.stereotype.Component;
- import org.springframework.util.concurrent.ListenableFuture;
- import java.nio.file.Path;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.HashMap;
- import java.util.HashSet;
- import java.util.List;
- import java.util.Map;
- import java.util.Objects;
- import java.util.Set;
- import java.util.stream.Collectors;
- import java.util.stream.IntStream;
- /**
- * 寻找重叠线
- *
- * @author 欧阳劲驰
- * @since 0.0.1
- */
- @Component
- public class OverlapLinesFinder {
- //双精度误差
- private static final double EPSILON = Double.longBitsToDouble(0x3ca0000000000000L);
- /**
- * log
- */
- private final String mStrClassName;
- private final String mBizType;
- private final AsyncResultManager asyncResultManager;
- public OverlapLinesFinder(AsyncResultManager asyncResultManager) {
- mStrClassName = "OverlapLinesFinder";
- mBizType = LogFlagBusiType.BUSI_GIS_SURVEY.toStrValue();
- this.asyncResultManager = asyncResultManager;
- }
- /**
- * 寻找重叠线
- * <p>返回的为重叠线组,未重叠的线不会出现在返回结果</p>
- * <p>一组线,仅为两条重叠的线</p>
- * <p>如完全重叠,则全分为一组,会出现多条线一组的情况</p>
- *
- * @param lines 线集合
- * @return 重叠线分组
- */
- @Async
- public ListenableFuture<GisSurveySystemCheckResultDetail> findOverlapLines(List<GisSurveyLayerApplyLine> lines
- , GisSurveySystemCheckId systemCheckId) throws InterruptedException {
- LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName, "开始执行寻找重叠线========>");
- long begin = System.currentTimeMillis();
- //创建空间索引
- STRtree tree = new STRtree();
- //元素存入索引
- for (GisSurveyLayerApplyLine layerApplyLine : lines) {
- if (layerApplyLine.getGis() == null) continue;
- LineString line = layerApplyLine.getGis();
- tree.insert(line.getEnvelopeInternal(), layerApplyLine);
- }
- tree.build();
- //结果
- List<List<GisSurveySystemCheckElement>> groupElements = new ArrayList<>();
- //处理过的code
- Set<String> processedCodes = new HashSet<>();
- //完全重叠过的code
- Set<String> fullOverlapCodes = new HashSet<>();
- //遍历所有线
- for (GisSurveyLayerApplyLine line1 : lines) {
- if (line1.getGis() == null) continue;
- //响应中断
- if (Thread.interrupted()) throw new InterruptedException();
- //完全重叠组
- List<GisSurveySystemCheckElement> fullOverlaps = new ArrayList<>();
- //查询索引内元素
- List<?> candidates = tree.query(line1.getGis().getEnvelopeInternal());
- for (Object candidate : candidates) {
- GisSurveyLayerApplyLine line2 = (GisSurveyLayerApplyLine) candidate;
- //排除code相同或已经处理的元素,
- if (Objects.equals(line1.getCode(), line2.getCode())
- || processedCodes.contains(line2.getCode())
- || line2.getGis() == null
- ) continue;
- //响应中断
- if (Thread.interrupted()) throw new InterruptedException();
- //判断是否完全重叠(两线都未被标记过)
- if (!fullOverlapCodes.contains(line1.getCode())
- && !fullOverlapCodes.contains(line2.getCode())
- && calcFullOverlap(line1, line2)) {
- fullOverlaps.add(BeanUtil.copy(line2, GisSurveySystemCheckElement.class));
- }
- //判断是否部分重叠(排除完全重叠)
- if (!calcFullOverlap(line1, line2) && calcPartialOverlap(line1, line2)) {
- // 创建两两一组的重叠线对
- groupElements.add(Arrays.asList(
- BeanUtil.copy(line1, GisSurveySystemCheckElement.class),
- BeanUtil.copy(line2, GisSurveySystemCheckElement.class)
- ));
- }
- }
- //如完全重叠组不为空,则存入结果
- if (CollectionUtils.isNotEmpty(fullOverlaps)) {
- //存入自身
- fullOverlaps.add(BeanUtil.copy(line1, GisSurveySystemCheckElement.class));
- //填入结果
- groupElements.add(fullOverlaps);
- //标记完全重叠
- fullOverlapCodes.addAll(fullOverlaps.stream()
- .map(GisSurveySystemCheckElement::getCode)
- .collect(Collectors.toSet())
- );
- }
- //存入code,避免重复处理
- processedCodes.add(line1.getCode());
- }
- return new AsyncResult<>(createResult(groupElements, systemCheckId, begin));
- }
- /**
- * 计算完全重叠
- *
- * @param line1 线段1
- * @param line2 线段2
- * @return 重叠状态
- */
- public boolean calcFullOverlap(GisSurveyLayerApplyLine line1, GisSurveyLayerApplyLine line2) {
- //取出四个点
- Coordinate a = line1.getGis().getCoordinateN(0);
- Coordinate b = line1.getGis().getCoordinateN(1);
- Coordinate c = line2.getGis().getCoordinateN(0);
- Coordinate d = line2.getGis().getCoordinateN(1);
- if (a == null || b == null || c == null || d == null) return false;
- return (a.equals2D(c) && b.equals2D(d)) || (a.equals2D(d) && b.equals2D(c));
- }
- /**
- * 计算部分重叠线
- *
- * @param line1 线段1
- * @param line2 线段2
- * @return 重叠状态
- */
- public boolean calcPartialOverlap(GisSurveyLayerApplyLine line1, GisSurveyLayerApplyLine line2) {
- //取出四个点
- Coordinate a = line1.getGis().getCoordinateN(0);
- Coordinate b = line1.getGis().getCoordinateN(1);
- Coordinate c = line2.getGis().getCoordinateN(0);
- Coordinate d = line2.getGis().getCoordinateN(1);
- //点数量判断
- if (a == null || b == null || c == null || d == null) return false;
- //检查C和D是否在AB的直线上(叉积为0,则方向一致)
- if (Orientation.index(a, b, c) != 0 || Orientation.index(a, b, d) != 0) return false;
- LineSegment seg1 = new LineSegment(a, b);
- LineSegment seg2 = new LineSegment(c, d);
- //四点共点判断
- if (seg1.equals(seg2)) return true;
- //获取C和D对于线段1的投影因子
- double tC = seg1.projectionFactor(c);
- double tD = seg1.projectionFactor(d);
- //获取最小和最大投影因子
- double cdMin = Math.min(tC, tD);
- double cdMax = Math.max(tC, tD);
- //排除前后延伸联通线
- if (cdMin == 1 && cdMax >= 1) return false;
- if (cdMax == 0 && cdMin <= 0) return false;
- //判断最大因子是否向前延伸或处于线段内,并且最小因子是否向后延伸或处于线段内
- return (cdMax + EPSILON >= 0) && (cdMin - EPSILON <= 1);
- }
- /**
- * 创建结果
- *
- * @param data 数据
- * @param systemCheckId 系统检查id
- * @param begin 开始时间
- * @return 结果
- */
- private GisSurveySystemCheckResultDetail createResult(List<List<GisSurveySystemCheckElement>> data
- , GisSurveySystemCheckId systemCheckId, long begin) {
- long end = System.currentTimeMillis();
- LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
- , String.format(
- "结束执行寻找重叠线,用时(毫秒):%d"
- , (end - begin)
- )
- );
- //数据大小
- final int size = data.size();
- //结果flag
- final String FLAG = systemCheckId.getFlag();
- //写入json和excel结果
- Path jsonPath = asyncResultManager.writeJson(data, FLAG, GisSurveySystemCheckKeys.OVERLAP_LINES + ".json");
- Path excelPath = asyncResultManager.writeExcel(GisSurveySystemCheckResultHead.OVERLAP_LINES,
- buildExcel(data), FLAG, GisSurveySystemCheckKeys.OVERLAP_LINES + ".xlsx");
- //构建结果
- return new GisSurveySystemCheckResultDetail(true
- , FLAG + "/" + jsonPath.getFileName()
- , FLAG + "/" + excelPath.getFileName()
- , size);
- }
- /**
- * 构建excel数据
- *
- * @param data 数据
- * @return excel数据
- */
- private List<Map<String, Object>> buildExcel(List<List<GisSurveySystemCheckElement>> data) {
- return IntStream.range(0, data.size())
- .boxed()
- .flatMap(i -> data.get(i).stream().map(element -> {
- Map<String, Object> map = new HashMap<>();
- map.put(GisSurveySystemCheckResultHead.KEYS.GROUP_NAME, i + 1);
- map.put(GisSurveySystemCheckResultHead.KEYS.UP_NO, element.getUpNo());
- map.put(GisSurveySystemCheckResultHead.KEYS.DOWN_NO, element.getDownNo());
- map.put(GisSurveySystemCheckResultHead.KEYS.CODE, element.getCode());
- map.put(GisSurveySystemCheckResultHead.KEYS.UP_NODE, element.getUpNode());
- map.put(GisSurveySystemCheckResultHead.KEYS.DOWN_NODE, element.getDownNode());
- map.put(GisSurveySystemCheckResultHead.KEYS.NAME, element.getName());
- return map;
- })).collect(Collectors.toList());
- }
- }
|