shench3
2026-03-06 ec970b5597ce919dd8328850cdc61d724b2099f1
feat: 多坐标点解析生成rtree
1个文件已添加
4个文件已修改
124 ■■■■■ 已修改文件
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/dto/AreaInput.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/AdcOssHandleService.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/AdcOssHandleServiceImpl.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/utils/XngpAreaRTreeFbsUtil.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/test/java/com/xiaopeng/map/service/AdcOssHandleServiceTest.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/dto/AreaInput.java
New file
@@ -0,0 +1,33 @@
package com.xiaopeng.map.dto;
import com.xiaopeng.map.utils.XngpAreaRTreeFbsUtil;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
 * 区域输入实体,封装多边形顶点、区域 ID 及黑名单标记。
 * 用于 uploadOddLayerFile 单区域 / 多区域统一入参。
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class AreaInput {
    /**
     * 多边形顶点(使用 XngpAreaRTreeFbsUtil.LonLat)
     */
    private List<XngpAreaRTreeFbsUtil.LonLat> points;
    /**
     * 区域 ID
     */
    private int areaId;
    /**
     * 是否黑名单区域,默认 false
     */
    @Builder.Default
    private boolean isBlacklist = false;
}
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/AdcOssHandleService.java
@@ -1,6 +1,6 @@
package com.xiaopeng.map.service;
import com.xiaopeng.map.utils.XngpAreaRTreeFbsUtil;
import com.xiaopeng.map.dto.AreaInput;
import java.util.List;
@@ -10,14 +10,13 @@
public interface AdcOssHandleService {
    /**
     * 根据坐标点生成 RTree fbs 并上传至 OSS
     * 根据多区域坐标点生成 RTree fbs 并上传至 OSS
     *
     * @param points 多边形顶点(使用 XngpAreaRTreeFbsUtil.LonLat)
     * @param areaId 区域 areaId
     * @param vin    车辆 VIN 码
     * @param areas 区域列表,每个 AreaInput 含 points、areaId、isBlacklist
     * @param vin   车辆 VIN 码
     * @return 上传后的 objectKey,失败抛异常
     */
    String uploadOddLayerFile(List<XngpAreaRTreeFbsUtil.LonLat> points, int areaId, String vin);
    String uploadOddLayerFile(List<AreaInput> areas, String vin);
    /**
     * 删除指定车辆下的 ODD 图层文件
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/AdcOssHandleServiceImpl.java
@@ -1,6 +1,7 @@
package com.xiaopeng.map.service.serviceimpl;
import com.xiaopeng.map.config.VinSaltProperties;
import com.xiaopeng.map.dto.AreaInput;
import com.xiaopeng.map.service.AdcOssHandleService;
import com.xiaopeng.map.service.RobotaxiOssService;
import com.xiaopeng.map.utils.MapAreaVersionProtoWriter;
@@ -28,14 +29,14 @@
    private final VinSaltProperties vinSaltProperties;
    @Override
    public String uploadOddLayerFile(List<XngpAreaRTreeFbsUtil.LonLat> points, int areaId, String vin) {
        validateParams(points, vin);
    public String uploadOddLayerFile(List<AreaInput> areas, String vin) {
        validateParams(areas, vin);
        String vehicleUuid = VehicleCodec.vinToUUID(vin, vinSaltProperties.getSalt1(), vinSaltProperties.getSalt2());
        byte[] fbsBytes = XngpAreaRTreeFbsUtil.buildRTreeBytesFromPoints(points, areaId, false);
        byte[] fbsBytes = XngpAreaRTreeFbsUtil.buildRTreeBytesFromMultiAreas(areas);
        if (fbsBytes == null || fbsBytes.length == 0) {
            throw new IllegalStateException("Failed to build RTree fbs from points");
            throw new IllegalStateException("Failed to build RTree fbs from areas");
        }
        int version = (int) (System.currentTimeMillis() / 1000);
@@ -46,7 +47,7 @@
        byte[] pbBytes = MapAreaVersionProtoWriter.toByteArray(version, fbsBytes.length);
        robotaxiOssService.uploadOddLayerFile(pbBytes, "xngp_area_version.pb", vehicleUuid);
        log.debug("AdcOssHandle uploaded ODD layer file, areaId={}, objectKey={}", areaId, objectKey);
        log.debug("AdcOssHandle uploaded ODD layer file, areasCount={}, objectKey={}", areas.size(), objectKey);
        return objectKey;
    }
@@ -61,9 +62,14 @@
        return deleted;
    }
    private void validateParams(List<XngpAreaRTreeFbsUtil.LonLat> points, String vin) {
        if (CollectionUtils.isEmpty(points)) {
            throw new IllegalArgumentException("points cannot be empty");
    private void validateParams(List<AreaInput> areas, String vin) {
        if (CollectionUtils.isEmpty(areas)) {
            throw new IllegalArgumentException("areas cannot be empty");
        }
        for (AreaInput area : areas) {
            if (CollectionUtils.isEmpty(area.getPoints())) {
                throw new IllegalArgumentException("each area must have non-empty points");
            }
        }
        if (!StringUtils.hasText(vin)) {
            throw new IllegalArgumentException("vin cannot be empty");
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/utils/XngpAreaRTreeFbsUtil.java
@@ -1,6 +1,7 @@
package com.xiaopeng.map.utils;
import com.google.flatbuffers.FlatBufferBuilder;
import com.xiaopeng.map.dto.AreaInput;
import com.xiaopeng.map.dto.MapAreaDataDto;
import com.xiaopeng.map.dto.OddAreaDto;
import com.xiaopeng.map.dto.OddPointDto;
@@ -153,6 +154,33 @@
    }
    /**
     * 从多个区域(AreaInput)生成 RTree fbs 字节数组。
     *
     * @param areas 区域列表,每个 AreaInput 含 points、areaId、isBlacklist
     * @return fbs 二进制,失败返回 null
     */
    public static byte[] buildRTreeBytesFromMultiAreas(List<AreaInput> areas) {
        if (areas == null || areas.isEmpty()) {
            return null;
        }
        java.util.List<PbRTreeBuilder.RTreeNode> leafNodes = new java.util.ArrayList<>();
        for (AreaInput area : areas) {
            PbRTreeBuilder.RTreeNode leaf = buildLeafNode(
                    area.getPoints(),
                    area.getAreaId(),
                    area.isBlacklist()
            );
            if (leaf != null) {
                leafNodes.add(leaf);
            }
        }
        if (leafNodes.isEmpty()) {
            return null;
        }
        return buildRTreeBytes(leafNodes);
    }
    /**
     * 从多个区域的坐标点列表生成 RTree fbs 文件(多区域版本,与 C++ 完全对齐)。
     *
     * @param areaPoints     每个区域对应的坐标点列表
xp-robotaxi-map-boot/src/test/java/com/xiaopeng/map/service/AdcOssHandleServiceTest.java
@@ -1,5 +1,6 @@
package com.xiaopeng.map.service;
import com.xiaopeng.map.dto.AreaInput;
import com.xiaopeng.map.utils.XngpAreaRTreeFbsUtil;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Assumptions;
@@ -9,6 +10,7 @@
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import java.util.Arrays;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -36,16 +38,30 @@
            log.warn("OSS 未启用,测试跳过。请配置 ALIYUN_OSS_ACCESS_KEY_ID、ALIYUN_OSS_ACCESS_KEY_SECRET、VIN_SALT1、VIN_SALT2");
            Assumptions.assumeTrue(false, "OSS 未启用,请配置环境变量");
        }
        // 构造测试坐标点:成都某区域多边形
        String gcl = "104.06249_30.58322,104.06244_30.58049,104.06500_30.58100,104.06600_30.58400";
        List<XngpAreaRTreeFbsUtil.LonLat> points = XngpAreaRTreeFbsUtil.parseGcl(gcl);
        assertNotNull(points);
        assertTrue(points.size() >= 3, "多边形至少需要 3 个点");
        // 构造测试坐标点:成都区域 3 个多边形
        String gcl1 = "104.06249_30.58322,104.06244_30.58049,104.06500_30.58100,104.06600_30.58400";
        String gcl2 = "104.06800_30.58200,104.06900_30.58000,104.07100_30.58150,104.06800_30.58200";
        String gcl3 = "104.05500_30.57800,104.05700_30.57600,104.06000_30.57750,104.05500_30.57800";
        int areaId = 1;
        List<XngpAreaRTreeFbsUtil.LonLat> points1 = XngpAreaRTreeFbsUtil.parseGcl(gcl1);
        List<XngpAreaRTreeFbsUtil.LonLat> points2 = XngpAreaRTreeFbsUtil.parseGcl(gcl2);
        List<XngpAreaRTreeFbsUtil.LonLat> points3 = XngpAreaRTreeFbsUtil.parseGcl(gcl3);
        assertNotNull(points1);
        assertNotNull(points2);
        assertNotNull(points3);
        assertTrue(points1.size() >= 3, "多边形1至少需要 3 个点");
        assertTrue(points2.size() >= 3, "多边形2至少需要 3 个点");
        assertTrue(points3.size() >= 3, "多边形3至少需要 3 个点");
        String vin = "21312323333333312";  // 替换为实际测试 VIN
        String objectKey = adcOssHandleService.uploadOddLayerFile(points, areaId, vin);
        List<AreaInput> areas = Arrays.asList(
                AreaInput.builder().points(points1).areaId(1).build(),
                AreaInput.builder().points(points2).areaId(2).build(),
                AreaInput.builder().points(points3).areaId(3).build()
        );
        String objectKey = adcOssHandleService.uploadOddLayerFile(areas, vin);
        log.info("uploadOddLayerFile objectKey={}", objectKey);
        assertNotNull(objectKey);