| | |
| | | private static final double DEFAULT_CANVAS_HEIGHT = 5200D; |
| | | private static final int X_SCALE = 40; |
| | | private static final int Y_SCALE = 8; |
| | | private static final double AUTO_BRIDGE_MAX_GAP_DISPLAY = 2.0d; |
| | | private static final double AUTO_BRIDGE_MIN_OVERLAP_RATIO = 0.85d; |
| | | |
| | | @Autowired |
| | | private BasMapService basMapService; |
| | |
| | | for (BasMapEditorElement element : elements) { |
| | | CompiledRect rect = toCompiledRect(element); |
| | | rects.add(rect); |
| | | } |
| | | List<CompiledRect> allRects = new ArrayList<>(rects); |
| | | allRects.addAll(buildAutoBridgeRects(rects)); |
| | | for (CompiledRect rect : allRects) { |
| | | xBounds.add(rect.left); |
| | | xBounds.add(rect.right); |
| | | yBounds.add(rect.top); |
| | |
| | | stored.add(row); |
| | | } |
| | | |
| | | for (CompiledRect rect : rects) { |
| | | for (CompiledRect rect : allRects) { |
| | | Integer rowStart = yIndexMap.get(String.valueOf(rect.top)); |
| | | Integer rowEndIndex = yIndexMap.get(String.valueOf(rect.bottom)); |
| | | Integer colStart = xIndexMap.get(String.valueOf(rect.left)); |
| | |
| | | rect.id = element.getId(); |
| | | rect.type = element.getType(); |
| | | rect.value = normalizeCellValue(element.getType(), element.getValue()); |
| | | JSONObject valueObject = parseJsonObject(rect.value); |
| | | rect.stationId = valueObject == null ? null : valueObject.getInteger("stationId"); |
| | | rect.left = toRawWidth(element.getX()); |
| | | rect.top = toRawHeight(element.getY()); |
| | | rect.right = toRawWidth(safeDouble(element.getX()) + safeDouble(element.getWidth())); |
| | |
| | | throw new CoolException("元素尺寸无效: " + element.getId()); |
| | | } |
| | | return rect; |
| | | } |
| | | |
| | | private List<CompiledRect> buildAutoBridgeRects(List<CompiledRect> sourceRects) { |
| | | List<CompiledRect> bridgeRects = new ArrayList<>(); |
| | | if (sourceRects == null || sourceRects.size() < 2) { |
| | | return bridgeRects; |
| | | } |
| | | for (int i = 0; i < sourceRects.size(); i++) { |
| | | CompiledRect first = sourceRects.get(i); |
| | | if (!isAutoBridgeCandidate(first)) { |
| | | continue; |
| | | } |
| | | for (int j = i + 1; j < sourceRects.size(); j++) { |
| | | CompiledRect second = sourceRects.get(j); |
| | | if (!isAutoBridgeCandidate(second)) { |
| | | continue; |
| | | } |
| | | CompiledRect bridgeRect = buildAutoBridgeRect(first, second); |
| | | if (bridgeRect == null) { |
| | | continue; |
| | | } |
| | | if (hasRectConflict(bridgeRect, sourceRects, bridgeRects, first, second)) { |
| | | continue; |
| | | } |
| | | bridgeRects.add(bridgeRect); |
| | | } |
| | | } |
| | | return bridgeRects; |
| | | } |
| | | |
| | | private boolean isAutoBridgeCandidate(CompiledRect rect) { |
| | | return rect != null && "devp".equals(rect.type) && rect.stationId != null && rect.stationId > 0; |
| | | } |
| | | |
| | | private CompiledRect buildAutoBridgeRect(CompiledRect first, CompiledRect second) { |
| | | CompiledRect horizontalBridge = buildHorizontalBridgeRect(first, second); |
| | | if (horizontalBridge != null) { |
| | | return horizontalBridge; |
| | | } |
| | | return buildVerticalBridgeRect(first, second); |
| | | } |
| | | |
| | | private CompiledRect buildHorizontalBridgeRect(CompiledRect first, CompiledRect second) { |
| | | CompiledRect left = first.left <= second.left ? first : second; |
| | | CompiledRect right = left == first ? second : first; |
| | | int gap = right.left - left.right; |
| | | if (gap <= 0 || gap > toRawWidth(AUTO_BRIDGE_MAX_GAP_DISPLAY)) { |
| | | return null; |
| | | } |
| | | int overlapTop = Math.max(left.top, right.top); |
| | | int overlapBottom = Math.min(left.bottom, right.bottom); |
| | | int overlapSize = overlapBottom - overlapTop; |
| | | if (!passesAutoBridgeOverlapThreshold(overlapSize, left.bottom - left.top, right.bottom - right.top)) { |
| | | return null; |
| | | } |
| | | return buildBridgeRect( |
| | | "bridge_h_" + left.stationId + "_" + right.stationId, |
| | | left.right, |
| | | overlapTop, |
| | | right.left, |
| | | overlapBottom, |
| | | Arrays.asList("left", "right"), |
| | | left.stationId, |
| | | right.stationId |
| | | ); |
| | | } |
| | | |
| | | private CompiledRect buildVerticalBridgeRect(CompiledRect first, CompiledRect second) { |
| | | CompiledRect top = first.top <= second.top ? first : second; |
| | | CompiledRect bottom = top == first ? second : first; |
| | | int gap = bottom.top - top.bottom; |
| | | if (gap <= 0 || gap > toRawHeight(AUTO_BRIDGE_MAX_GAP_DISPLAY)) { |
| | | return null; |
| | | } |
| | | int overlapLeft = Math.max(top.left, bottom.left); |
| | | int overlapRight = Math.min(top.right, bottom.right); |
| | | int overlapSize = overlapRight - overlapLeft; |
| | | if (!passesAutoBridgeOverlapThreshold(overlapSize, top.right - top.left, bottom.right - bottom.left)) { |
| | | return null; |
| | | } |
| | | return buildBridgeRect( |
| | | "bridge_v_" + top.stationId + "_" + bottom.stationId, |
| | | overlapLeft, |
| | | top.bottom, |
| | | overlapRight, |
| | | bottom.top, |
| | | Arrays.asList("top", "bottom"), |
| | | top.stationId, |
| | | bottom.stationId |
| | | ); |
| | | } |
| | | |
| | | private boolean passesAutoBridgeOverlapThreshold(int overlapSize, int firstSpan, int secondSpan) { |
| | | if (overlapSize <= 0 || firstSpan <= 0 || secondSpan <= 0) { |
| | | return false; |
| | | } |
| | | double overlapRatio = overlapSize * 1.0d / Math.max(firstSpan, secondSpan); |
| | | return overlapRatio >= AUTO_BRIDGE_MIN_OVERLAP_RATIO; |
| | | } |
| | | |
| | | private CompiledRect buildBridgeRect(String id, |
| | | int left, |
| | | int top, |
| | | int right, |
| | | int bottom, |
| | | List<String> directions, |
| | | Integer firstStationId, |
| | | Integer secondStationId) { |
| | | if (left >= right || top >= bottom) { |
| | | return null; |
| | | } |
| | | CompiledRect rect = new CompiledRect(); |
| | | rect.id = id; |
| | | rect.type = "devp"; |
| | | rect.value = buildBridgeValue(directions, firstStationId, secondStationId); |
| | | rect.left = left; |
| | | rect.top = top; |
| | | rect.right = right; |
| | | rect.bottom = bottom; |
| | | return rect; |
| | | } |
| | | |
| | | private String buildBridgeValue(List<String> directions, Integer firstStationId, Integer secondStationId) { |
| | | JSONObject value = new JSONObject(); |
| | | value.put("direction", directions); |
| | | value.put("bridgeStationIds", Arrays.asList(firstStationId, secondStationId)); |
| | | value.put("autoBridge", 1); |
| | | return value.toJSONString(); |
| | | } |
| | | |
| | | private boolean hasRectConflict(CompiledRect candidate, |
| | | List<CompiledRect> sourceRects, |
| | | List<CompiledRect> bridgeRects, |
| | | CompiledRect firstIgnore, |
| | | CompiledRect secondIgnore) { |
| | | for (CompiledRect rect : sourceRects) { |
| | | if (rect == null || rect == firstIgnore || rect == secondIgnore) { |
| | | continue; |
| | | } |
| | | if (compiledRectsOverlap(candidate, rect)) { |
| | | return true; |
| | | } |
| | | } |
| | | for (CompiledRect rect : bridgeRects) { |
| | | if (rect != null && compiledRectsOverlap(candidate, rect)) { |
| | | return true; |
| | | } |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | private boolean compiledRectsOverlap(CompiledRect first, CompiledRect second) { |
| | | return first.left < second.right |
| | | && first.right > second.left |
| | | && first.top < second.bottom |
| | | && first.bottom > second.top; |
| | | } |
| | | |
| | | private Map<String, Integer> buildBoundaryIndexMap(List<Integer> bounds) { |
| | |
| | | private String id; |
| | | private String type; |
| | | private String value; |
| | | private Integer stationId; |
| | | private int left; |
| | | private int top; |
| | | private int right; |