826220679@qq.com
17 小时以前 f94b1436d7a28c8e28d010b2cb657ab7c064e353
src/zhuye/MapRenderer.java
@@ -18,6 +18,7 @@
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.function.Consumer;
import java.io.File;
import set.Setsys;
import gecaoji.Device;
@@ -488,10 +489,7 @@
        drawMower(g2d);
        
        // 绘制导航预览速度(如果正在导航预览)
        if (navigationPreviewSpeed > 0 && mower != null && mower.hasValidPosition()) {
            drawNavigationPreviewSpeed(g2d, scale);
        }
        // 已按需求移除:不在割草机图标上方显示速度
        
        // 绘制测量模式(如果激活)
        if (measurementModeActive) {
@@ -563,16 +561,21 @@
        g2d.setFont(labelFont);
        FontMetrics metrics = g2d.getFontMetrics(labelFont);
        
        // 计算文字位置(在割草机图标上方)
        // 计算文字位置(在割草机图标正上方,间隔20像素固定)
        int textWidth = metrics.stringWidth(speedText);
        int textHeight = metrics.getHeight();
        int textX = (int)Math.round(screenPos.x - textWidth / 2.0);
        // 在割草机图标上方,留出一定间距
        // 图标在世界坐标系中的大小约为 48 * 0.8 / scale 米
        // 转换为屏幕像素:图标高度(像素)= (48 * 0.8 / scale) * scale = 48 * 0.8 = 38.4 像素
        double iconSizePixels = 48.0 * 0.8; // 图标在屏幕上的大小(像素)
        int spacing = 8; // 间距(像素)
        int textY = (int)Math.round(screenPos.y - iconSizePixels / 2.0 - spacing - textHeight);
        // 在割草机图标正上方,间隔20像素
        // 从mower对象获取图标在世界坐标系中的半径,然后转换为屏幕像素
        double iconWorldRadius = mower.getWorldRadius(scale);
        double iconSizePixels = Double.isNaN(iconWorldRadius) ? 38.4 : (iconWorldRadius * 2.0 * scale); // 图标在屏幕上的大小(像素)
        int spacing = 20; // 间距(像素)
        // 图标顶部位置 = 图标中心Y - 图标高度/2
        double iconTopY = screenPos.y - iconSizePixels / 2.0;
        // 精确约束:让背景矩形的底边与图标顶部相距固定spacing像素
        // 背景底边 = textY + metrics.getDescent() + 2(矩形下方内边距)
        // 令 背景底边 = iconTopY - spacing,解得:
        int textY = (int)Math.round(iconTopY - spacing - metrics.getDescent() - 2);
        
        // 绘制文字背景(半透明白色,增强可读性)
        g2d.setColor(new Color(255, 255, 255, 200));
@@ -632,7 +635,7 @@
     * 添加导航预览轨迹点
     */
    public void addNavigationPreviewTrackPoint(Point2D.Double point) {
        if (point != null && Double.isFinite(point.x) && Double.isFinite(point.y)) {
        if (point != null && isFinite(point.x) && isFinite(point.y)) {
            navigationPreviewTrack.add(new Point2D.Double(point.x, point.y));
            if (visualizationPanel != null) {
                visualizationPanel.repaint();
@@ -750,7 +753,7 @@
            return;
        }
        Point2D.Double position = mower.getPosition();
        if (position == null || !Double.isFinite(position.x) || !Double.isFinite(position.y)) {
        if (position == null || !isFinite(position.x) || !isFinite(position.y)) {
            pendingTrackBreak = true;
            return;
        }
@@ -776,7 +779,7 @@
        }
        realtimeMowingTrack.add(candidate);
        if (!pendingTrackBreak && lastPoint != null && Double.isFinite(distance)) {
        if (!pendingTrackBreak && lastPoint != null && isFinite(distance)) {
            trackLengthMeters += distance;
        }
@@ -807,7 +810,7 @@
        }
        Point2D.Double position = mower.getPosition();
        if (position == null || !Double.isFinite(position.x) || !Double.isFinite(position.y)) {
        if (position == null || !isFinite(position.x) || !isFinite(position.y)) {
            return;
        }
@@ -852,7 +855,7 @@
        // 刷新mower位置,使用最新的Device数据
        mower.refreshFromDevice();
        Point2D.Double position = mower.getPosition();
        if (position == null || !Double.isFinite(position.x) || !Double.isFinite(position.y)) {
        if (position == null || !isFinite(position.x) || !isFinite(position.y)) {
            return;
        }
@@ -963,7 +966,7 @@
    }
    private String formatTrackCoordinate(double value) {
        if (!Double.isFinite(value)) {
        if (!isFinite(value)) {
            return "0";
        }
        return String.format(Locale.US, "%.3f", value);
@@ -1154,7 +1157,7 @@
            try {
                double x = Double.parseDouble(parts[0].trim());
                double y = Double.parseDouble(parts[1].trim());
                if (!Double.isFinite(x) || !Double.isFinite(y)) {
                if (!isFinite(x) || !isFinite(y)) {
                    continue;
                }
                Point2D.Double current = new Point2D.Double(x, y);
@@ -1459,6 +1462,7 @@
        String safetyDistance = null;
        String obstaclesCoords = null;
        String mowingPattern = null;
        String plannedPathStr = null;
        
        // 从当前地块编号获取地块信息
        if (currentBoundaryLandNumber != null) {
@@ -1468,6 +1472,8 @@
                mowingWidth = landData.getMowingWidth();
                safetyDistance = landData.getMowingSafetyDistance();
                mowingPattern = landData.getMowingPattern();
                // 从地块获取plannedPath属性值作为路径
                plannedPathStr = landData.getPlannedPath();
                
                // 获取障碍物坐标
                try {
@@ -1525,9 +1531,26 @@
            }
        }
        
        // 调用带地块信息的绘制方法
        lujingdraw.drawPlannedPath(g2d, currentPlannedPath, scale, arrowScale,
                                   boundaryCoords, mowingWidth, safetyDistance, obstaclesCoords, mowingPattern);
        // 如果从地块获取到了路径,使用地块的路径;否则使用currentPlannedPath
        List<Point2D.Double> pathToDraw = currentPlannedPath;
        List<Boolean> flags = null;
        if (plannedPathStr != null && !plannedPathStr.trim().isEmpty() && !"-1".equals(plannedPathStr.trim())) {
            // 优先解析带标注格式
            lujingdraw.ParsedPath parsed = lujingdraw.parsePlannedPathWithFlags(plannedPathStr);
            pathToDraw = parsed.points;
            flags = parsed.isMowingFlags;
        }
        // 调用带地块信息的绘制方法(如有标注优先使用)
        if (pathToDraw != null && pathToDraw.size() >= 2) {
            if (flags != null && flags.size() == pathToDraw.size() - 1) {
                lujingdraw.drawPlannedPath(g2d, pathToDraw, flags, scale, arrowScale,
                        boundaryCoords, mowingWidth, safetyDistance, obstaclesCoords, mowingPattern);
            } else {
                lujingdraw.drawPlannedPath(g2d, pathToDraw, scale, arrowScale,
                        boundaryCoords, mowingWidth, safetyDistance, obstaclesCoords, mowingPattern);
            }
        }
    }
    private void drawCircleSampleMarkers(Graphics2D g2d, List<double[]> markers, double scale) {
@@ -1549,7 +1572,7 @@
        FontMetrics metrics = g2d.getFontMetrics(labelFont);
        
        for (double[] pt : markers) {
            if (pt == null || pt.length < 2 || !Double.isFinite(pt[0]) || !Double.isFinite(pt[1])) {
            if (pt == null || pt.length < 2 || !isFinite(pt[0]) || !isFinite(pt[1])) {
                continue;
            }
            double x = pt[0];
@@ -1647,7 +1670,7 @@
                }
                double x = pt[0];
                double y = pt[1];
                if (!Double.isFinite(x) || !Double.isFinite(y)) {
                if (!isFinite(x) || !isFinite(y)) {
                    continue;
                }
                circleSampleMarkers.add(new double[]{x, y});
@@ -1870,7 +1893,7 @@
    private double computeSelectionThresholdPixels() {
        double scaleFactor = Math.max(0.5, scale);
        double diameterScale = boundaryPointSizeScale * (previewSizingEnabled ? PREVIEW_BOUNDARY_MARKER_SCALE : 1.0d);
        if (!Double.isFinite(diameterScale) || diameterScale <= 0.0d) {
        if (!isFinite(diameterScale) || diameterScale <= 0.0d) {
            diameterScale = 1.0d;
        }
        double markerDiameterWorld = Math.max(1.0, (10.0 / scaleFactor) * 0.2 * diameterScale);
@@ -2035,7 +2058,7 @@
    }
    private boolean isPointInsideActiveBoundary(Point2D.Double point) {
        if (point == null || !Double.isFinite(point.x) || !Double.isFinite(point.y)) {
        if (point == null || !isFinite(point.x) || !isFinite(point.y)) {
            return false;
        }
        if (realtimeTrackLandNumber == null) {
@@ -2046,7 +2069,7 @@
    }
    private boolean isPointInsideBoundary(Point2D.Double point, Path2D.Double path) {
        if (point == null || path == null || !Double.isFinite(point.x) || !Double.isFinite(point.y)) {
        if (point == null || path == null || !isFinite(point.x) || !isFinite(point.y)) {
            return false;
        }
        if (path.contains(point.x, point.y)) {
@@ -2067,7 +2090,7 @@
        Path2D.Double path = new Path2D.Double();
        boolean started = false;
        for (Point2D.Double point : boundary) {
            if (point == null || !Double.isFinite(point.x) || !Double.isFinite(point.y)) {
            if (point == null || !isFinite(point.x) || !isFinite(point.y)) {
                continue;
            }
            if (!started) {
@@ -2103,7 +2126,7 @@
        // 设置点的大小(随缩放变化)
        double scaleFactor = Math.max(0.5, scale);
        double clampedScale = boundaryPointSizeScale * (previewSizingEnabled ? PREVIEW_BOUNDARY_MARKER_SCALE : 1.0d);
        if (!Double.isFinite(clampedScale) || clampedScale <= 0.0d) {
        if (!isFinite(clampedScale) || clampedScale <= 0.0d) {
            clampedScale = 1.0d;
        }
        double minimumDiameter = clampedScale < 1.0 ? 0.5 : 1.0;
@@ -2552,7 +2575,7 @@
            }
            double x = coord.getX();
            double y = coord.getY();
            if (!Double.isFinite(x) || !Double.isFinite(y)) {
            if (!isFinite(x) || !isFinite(y)) {
                continue;
            }
            copy.add(new Obstacledge.XYCoordinate(x, y));
@@ -2890,7 +2913,7 @@
    }
    public void setBoundaryPointSizeScale(double sizeScale) {
        double normalized = (Double.isFinite(sizeScale) && sizeScale > 0.0d) ? sizeScale : 1.0d;
        double normalized = (isFinite(sizeScale) && sizeScale > 0.0d) ? sizeScale : 1.0d;
        if (Math.abs(boundaryPointSizeScale - normalized) < 1e-6) {
            return;
        }
@@ -2918,7 +2941,7 @@
    }
    public void setBoundaryPreviewMarkerScale(double markerScale) {
        double normalized = Double.isFinite(markerScale) && markerScale > 0.0d ? markerScale : 1.0d;
        double normalized = isFinite(markerScale) && markerScale > 0.0d ? markerScale : 1.0d;
        if (Math.abs(boundaryPreviewMarkerScale - normalized) < 1e-6) {
            return;
        }
@@ -2955,7 +2978,7 @@
    }
    public void addHandheldBoundaryPoint(double x, double y) {
        if (!Double.isFinite(x) || !Double.isFinite(y)) {
        if (!isFinite(x) || !isFinite(y)) {
            return;
        }
        if (!handheldBoundaryPreviewActive) {
@@ -3067,8 +3090,8 @@
        Point2D.Double mowerPosition = mower.getPosition();
        if (mowerPosition == null
            || !Double.isFinite(mowerPosition.x)
            || !Double.isFinite(mowerPosition.y)) {
            || !isFinite(mowerPosition.x)
            || !isFinite(mowerPosition.y)) {
            return expanded;
        }
@@ -3512,9 +3535,9 @@
    /**
     * 设置边界预览更新回调
     */
    private java.util.function.Consumer<String> boundaryPreviewUpdateCallback;
    private Consumer<String> boundaryPreviewUpdateCallback;
    
    public void setBoundaryPreviewUpdateCallback(java.util.function.Consumer<String> callback) {
    public void setBoundaryPreviewUpdateCallback(Consumer<String> callback) {
        this.boundaryPreviewUpdateCallback = callback;
    }
@@ -3531,4 +3554,12 @@
            fitBoundsToView(bounds);
        }
    }
}
    /**
     * 检查double值是否有限(不是NaN或无穷大)
     * 兼容低版本Java
     */
    private static boolean isFinite(double value) {
        return !Double.isNaN(value) && !Double.isInfinite(value);
    }
}