xuhy
2026-02-12 dead32139d71642ecd45d3f7404166ebb0abf56e
通过车辆实时定位推送订单轨迹
2个文件已添加
1个文件已修改
216 ■■■■■ 已修改文件
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/controller/XpMapController.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-pushMessage-boot/src/main/java/com/xiaopeng/pushMessage/controller/PushOrderRouteController.java 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-pushMessage-boot/src/main/java/com/xiaopeng/pushMessage/utils/MapMatchingUtil.java 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/controller/XpMapController.java
@@ -145,6 +145,9 @@
        // 存储路线偏离缓存,后续进行轨迹截取
//        redisService.setCacheObject(CacheConstants.ORDER_ROUTE_CAPTURE + "_" + dto.getOrderId() + "_" +dto.getRouteType(), routeVo);
        // 存储路线偏离缓存,后续进行轨迹截取
        redisService.setCacheObject(CacheConstants.ORDER_ROUTE_CAPTURE + "_" + dto.getOrderId() + "_" +dto.getRouteType(), routeVo);
        return R.ok(routeVo);
    }
xp-robotaxi-pushMessage-boot/src/main/java/com/xiaopeng/pushMessage/controller/PushOrderRouteController.java
New file
@@ -0,0 +1,90 @@
package com.xiaopeng.pushMessage.controller;
import com.alibaba.fastjson2.JSONObject;
import com.xiaopeng.api.dto.operation.LonLatDto;
import com.xiaopeng.api.dto.pushMessage.PushOrderRouteDTO;
import com.xiaopeng.api.dto.pushMessage.SendMessageDTO;
import com.xiaopeng.api.feignClient.order.XpOrderClient;
import com.xiaopeng.api.feignClient.pushMessage.XpPushMessageClient;
import com.xiaopeng.api.model.order.XpOrder;
import com.xiaopeng.api.vo.operation.RouteVo;
import com.xiaopeng.common.core.constant.CacheConstants;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.enums.PushMessageTypeEnum;
import com.xiaopeng.common.redis.service.RedisService;
import com.xiaopeng.pushMessage.utils.MapMatchingUtil;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.*;
@Api(tags = "订单推送轨迹接口")
@RestController
@RequestMapping("")
public class PushOrderRouteController {
    @Autowired
    private RedisService redisService;
    @Autowired
    private XpPushMessageClient xpPushMessageClient;
    /**
     * 通过定位推送订单轨迹
     * @return
     */
    @PostMapping("/inner/v1/order/pushOrderRoute")
    public R<String> pushOrderRoute(@Validated @RequestBody PushOrderRouteDTO dto) {
        RouteVo routeVo = redisService.getCacheObject(CacheConstants.ORDER_ROUTE_CAPTURE + "_" + dto.getOrderId() + "_" +dto.getRouteType());
        if(Objects.nonNull(routeVo)){
            // 计算实时点位到线段的最优截取
            List<LonLatDto> lonLatDtos = MapMatchingUtil.clipBySegmentDistance(routeVo.getPolyline(), new LonLatDto(dto.getLongitude(), dto.getLatitude()));
            routeVo.setDistance(dto.getDistance());
            routeVo.setDuration(dto.getDuration());
            routeVo.setPolyline(lonLatDtos);
            // 存储最新路线偏离缓存,后续进行轨迹截取
            redisService.setCacheObject(CacheConstants.ORDER_ROUTE_CAPTURE + "_" + dto.getOrderId() + "_" +dto.getRouteType(), routeVo);
            // 封装轨迹点位集合
            Map<String, Object> polyline = new HashMap<>();
            for (LonLatDto lonLatDto : lonLatDtos) {
                polyline.put("longitude",lonLatDto.getLongitude());
                polyline.put("latitude",lonLatDto.getLatitude());
            }
            // 封装路由详情
            Map<String, Object> route = new HashMap<>();
            route.put("distance",dto.getDistance());
            route.put("duration",dto.getDuration());
            route.put("polyline",JSONObject.toJSONString(polyline));
            // 封装消息内容
            Map<String, Object> content = new HashMap<>();
            content.put("orderId",dto.getOrderId());
            content.put("routeType",dto.getRouteType());
            content.put("route",JSONObject.toJSONString(route));
            // 封装轨迹信息
            Map<String, Object> params = new HashMap<>();
            params.put("type", PushMessageTypeEnum.ORDER_ROUTE.getDesc());
            params.put("content", JSONObject.toJSONString(content));
            params.put("timestamp", new Date().getTime()+"");
            // 进行消息推送
            SendMessageDTO sendMessageDTO = new SendMessageDTO();
            sendMessageDTO.setAppUserId(String.valueOf(dto.getAppUserId()));
            sendMessageDTO.setMessage(JSONObject.toJSONString(params));
            xpPushMessageClient.sendRoute(sendMessageDTO);
        }
        return R.ok();
    }
}
xp-robotaxi-pushMessage-boot/src/main/java/com/xiaopeng/pushMessage/utils/MapMatchingUtil.java
New file
@@ -0,0 +1,123 @@
package com.xiaopeng.pushMessage.utils;
import com.xiaopeng.api.dto.operation.LonLatDto;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@Slf4j
public class MapMatchingUtil {
    /**
     * 轨迹裁剪(线段版本)
     * @param polyline
     * @param currentPoint
     * @return
     */
    public static List<LonLatDto> clipBySegmentDistance(List<LonLatDto> polyline,LonLatDto currentPoint) {
        if (polyline == null || polyline.size() < 2) {
            return Collections.emptyList();
        }
        double minDistance = Double.MAX_VALUE;
        int minSegmentIndex = 0;
        int increaseCount = 0;
        final int threshold = 3;
        final double tolerance = 1.0;
        for (int i = 0; i < polyline.size() - 1; i++) {
            double d = pointToSegmentDistance(
                    currentPoint,
                    polyline.get(i),
                    polyline.get(i + 1)
            );
            if (d < minDistance) {
                minDistance = d;
                minSegmentIndex = i;
                increaseCount = 0;
            } else if (d > minDistance + tolerance) {
                increaseCount++;
            }
            if (increaseCount >= threshold) {
                break;
            }
        }
        // 从最优线段的后一个点开始截取
        return new ArrayList<>(
                polyline.subList(minSegmentIndex + 1, polyline.size())
        );
    }
    /**
     * 点到线段距离
     * @param p
     * @param a
     * @param b
     * @return
     */
    public static double pointToSegmentDistance(
            LonLatDto p,
            LonLatDto a,
            LonLatDto b) {
        double refLat = p.getLatitude();
        double[] pXY = toXY(p.getLatitude(), p.getLongitude(), refLat);
        double[] aXY = toXY(a.getLatitude(), a.getLongitude(), refLat);
        double[] bXY = toXY(b.getLatitude(), b.getLongitude(), refLat);
        double x = pXY[0];
        double y = pXY[1];
        double x1 = aXY[0];
        double y1 = aXY[1];
        double x2 = bXY[0];
        double y2 = bXY[1];
        double dx = x2 - x1;
        double dy = y2 - y1;
        if (dx == 0 && dy == 0) {
            return Math.hypot(x - x1, y - y1);
        }
        double t = ((x - x1) * dx + (y - y1) * dy) / (dx * dx + dy * dy);
        if (t < 0) {
            return Math.hypot(x - x1, y - y1);
        } else if (t > 1) {
            return Math.hypot(x - x2, y - y2);
        }
        double projX = x1 + t * dx;
        double projY = y1 + t * dy;
        return Math.hypot(x - projX, y - projY);
    }
    /**
     * 经纬度转平面坐标(局部近似)
     * @param lat
     * @param lon
     * @param refLat
     * @return
     */
    private static double[] toXY(double lat, double lon, double refLat) {
        double R = 6378137; // 地球半径
        double x = Math.toRadians(lon) * R * Math.cos(Math.toRadians(refLat));
        double y = Math.toRadians(lat) * R;
        return new double[]{x, y};
    }
}