| New file |
| | |
| | | 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; |
| | | } |
| | |
| | | package com.xiaopeng.map.service; |
| | | |
| | | import com.xiaopeng.map.utils.XngpAreaRTreeFbsUtil; |
| | | import com.xiaopeng.map.dto.AreaInput; |
| | | |
| | | import java.util.List; |
| | | |
| | |
| | | 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 图层文件 |
| | |
| | | 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; |
| | |
| | | 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); |
| | |
| | | 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; |
| | | } |
| | | |
| | |
| | | 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"); |
| | |
| | | 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; |
| | |
| | | } |
| | | |
| | | /** |
| | | * 从多个区域(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 每个区域对应的坐标点列表 |
| | |
| | | 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; |
| | |
| | | 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; |
| | |
| | | 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); |