package com.zy.acs.manager.core.service; 
 | 
  
 | 
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 
 | 
import com.zy.acs.framework.common.Cools; 
 | 
import com.zy.acs.framework.common.SnowflakeIdWorker; 
 | 
import com.zy.acs.manager.core.domain.Lane; 
 | 
import com.zy.acs.manager.manager.entity.Code; 
 | 
import com.zy.acs.manager.manager.service.CodeService; 
 | 
import com.zy.acs.manager.manager.service.RouteService; 
 | 
import org.springframework.beans.factory.annotation.Autowired; 
 | 
import org.springframework.stereotype.Service; 
 | 
  
 | 
import javax.annotation.PostConstruct; 
 | 
import java.util.*; 
 | 
import java.util.stream.Collectors; 
 | 
  
 | 
/** 
 | 
 * Created by vincent on 10/25/2024 
 | 
 */ 
 | 
@Service 
 | 
public class LaneService { 
 | 
  
 | 
    private boolean initialized = Boolean.FALSE; 
 | 
  
 | 
    private final List<Lane> lanes = new ArrayList<>(); 
 | 
  
 | 
    private final Map<String, List<String>> adjacencyCodeMap = new HashMap<>(); 
 | 
  
 | 
    @Autowired 
 | 
    private CodeService codeService; 
 | 
    @Autowired 
 | 
    private RouteService routeService; 
 | 
    @Autowired 
 | 
    private SnowflakeIdWorker snowflakeIdWorker; 
 | 
  
 | 
    public Boolean isInitialized() { 
 | 
        return this.initialized; 
 | 
    } 
 | 
  
 | 
    public List<String> getLanePoints(String codeData) { 
 | 
        if (Cools.isEmpty(codeData) || !this.initialized) { 
 | 
            return null; 
 | 
        } 
 | 
        for (Lane lane : this.lanes) { 
 | 
            if (lane.getCodes().contains(codeData)) { 
 | 
                return lane.getCodes(); 
 | 
            } 
 | 
        } 
 | 
        return null; 
 | 
    } 
 | 
  
 | 
    @PostConstruct 
 | 
    public synchronized void init() { 
 | 
        List<Code> codeList = codeService.list(new LambdaQueryWrapper<Code>().eq(Code::getStatus, 1)); 
 | 
  
 | 
        this.fillAdjacencyCodeMap(codeList); 
 | 
  
 | 
        this.generateLane(codeList.stream().map(Code::getData).collect(Collectors.toList())); 
 | 
  
 | 
        this.mergeDeadEndLane(); 
 | 
  
 | 
        this.deadInteractionPoint(); 
 | 
  
 | 
        this.filterLanesWithFewPoints(); 
 | 
  
 | 
        this.initialized = Boolean.TRUE; 
 | 
    } 
 | 
  
 | 
    private void fillAdjacencyCodeMap(List<Code> codeList) { 
 | 
        for (Code code : codeList) { 
 | 
            List<Long> adjacencyNode = routeService.getAdjacencyNode(code.getId()); 
 | 
            this.adjacencyCodeMap.put(code.getData(), adjacencyNode.stream().map(node -> ( 
 | 
                    codeService.getById(node).getData() 
 | 
            )).collect(Collectors.toList())); 
 | 
        } 
 | 
    } 
 | 
  
 | 
    private void generateLane(List<String> codeDataList) { 
 | 
        Set<String> visited = new HashSet<>(); 
 | 
  
 | 
        for (String codeData : codeDataList) { 
 | 
            if (this.adjacencyCodeMap.get(codeData).size() != 2) { 
 | 
                List<String> neighbors = this.adjacencyCodeMap.get(codeData); 
 | 
                for (String neighbor : neighbors) { 
 | 
                    if (this.adjacencyCodeMap.get(neighbor).size() == 2 && !visited.contains(neighbor)) { 
 | 
                        Lane lane = new Lane(String.valueOf(snowflakeIdWorker.nextId()).substring(3)); 
 | 
                        lane.getCodes().add(codeData); // 包含起点 
 | 
                        this.dfsCalcIncludingEnd(codeData, neighbor, lane, visited); 
 | 
                        this.lanes.add(lane); 
 | 
                    } 
 | 
                } 
 | 
            } 
 | 
        } 
 | 
  
 | 
        for (String codeData : codeDataList) { 
 | 
            if (this.adjacencyCodeMap.get(codeData).size() == 2 && !visited.contains(codeData)) { 
 | 
                // 检查是否为环路的一部分 
 | 
                Lane lane = new Lane(String.valueOf(snowflakeIdWorker.nextId()).substring(3)); 
 | 
                this.dfsCalcForLoop(codeData, null, lane, visited); 
 | 
                this.lanes.add(lane); 
 | 
            } 
 | 
        } 
 | 
  
 | 
  
 | 
    } 
 | 
  
 | 
    private void dfsCalcIncludingEnd(String start, String current, Lane lane, Set<String> visited) { 
 | 
        lane.getCodes().add(current); 
 | 
        visited.add(current); 
 | 
  
 | 
        List<String> neighbors = this.adjacencyCodeMap.get(current); 
 | 
        if (neighbors == null || neighbors.isEmpty()) { 
 | 
            return; 
 | 
        } 
 | 
  
 | 
        for (String neighbor : neighbors) { 
 | 
            if (neighbor.equals(start)) { 
 | 
                continue; 
 | 
            } 
 | 
  
 | 
            if (!visited.contains(neighbor)) { 
 | 
                int degree = this.adjacencyCodeMap.get(neighbor).size(); 
 | 
                if (degree == 2) { 
 | 
                    if (this.isSameDirection(current, neighbor, start)) { 
 | 
                        this.dfsCalcIncludingEnd(current, neighbor, lane, visited); 
 | 
                    } 
 | 
                } else { 
 | 
                    // 终点或拐弯点,包含并停止 
 | 
                    lane.getCodes().add(neighbor); 
 | 
                    visited.add(neighbor); 
 | 
                } 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
  
 | 
    private void dfsCalcForLoop(String current, String parent, Lane lane, Set<String> visited) { 
 | 
        lane.getCodes().add(current); 
 | 
        visited.add(current); 
 | 
  
 | 
        List<String> neighbors = this.adjacencyCodeMap.get(current); 
 | 
        if (neighbors == null || neighbors.isEmpty()) { 
 | 
            return; 
 | 
        } 
 | 
  
 | 
        for (String neighbor : neighbors) { 
 | 
            if (neighbor.equals(parent)) { 
 | 
                continue; 
 | 
            } 
 | 
  
 | 
            if (!visited.contains(neighbor)) { 
 | 
                int degree = this.adjacencyCodeMap.get(neighbor).size(); 
 | 
                if (degree == 2) { 
 | 
                    if (this.isSameDirection(current, neighbor, parent)) { 
 | 
                        this.dfsCalcForLoop(current, neighbor, lane, visited); 
 | 
                    } 
 | 
                } else { 
 | 
                    lane.getCodes().add(neighbor); 
 | 
                    visited.add(neighbor); 
 | 
                } 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
  
 | 
    private boolean isSameDirection(String current, String neighbor, String parent) { 
 | 
        if (parent == null) { 
 | 
            return true; 
 | 
        } 
 | 
  
 | 
        Code parentCode = codeService.selectByData(parent); 
 | 
        Code currentCode = codeService.selectByData(current); 
 | 
        Code neighborCode = codeService.selectByData(neighbor); 
 | 
  
 | 
        double direction1 = this.calculateDirection(parentCode, currentCode); 
 | 
        double direction2 = this.calculateDirection(currentCode, neighborCode); 
 | 
        double angleDifference = Math.abs(direction1 - direction2); 
 | 
  
 | 
        // 规范化角度差 
 | 
        angleDifference = Math.min(angleDifference, 2 * Math.PI - angleDifference); 
 | 
  
 | 
        // 设置角度差阈值为3度 
 | 
        return angleDifference < Math.toRadians(3); 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * 计算两个点之间的方向角 
 | 
     */ 
 | 
    private double calculateDirection(Code from, Code to) { 
 | 
        double deltaX = to.getX() - from.getX(); 
 | 
        double deltaY = to.getY() - from.getY(); 
 | 
        return Math.atan2(deltaY, deltaX); 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * 1.explore a dead end lane and checkout end point that is not dead point 
 | 
     * 2.if this point has two near points and one of those points which not in current lane was also have two near points too 
 | 
     * 3.then merge above two lane because they can connect each other 
 | 
     */ 
 | 
    private void mergeDeadEndLane() { 
 | 
        Iterator<Lane> iterator = this.lanes.iterator(); 
 | 
        while (iterator.hasNext()) { 
 | 
            Lane lane = iterator.next(); 
 | 
            String[] endPoints = lane.queryEndPoints(); 
 | 
            if (null == endPoints) { 
 | 
                continue; 
 | 
            } 
 | 
            String startPoint = endPoints[0]; 
 | 
            String endPoint = endPoints[1]; 
 | 
  
 | 
            boolean isDeadEndLane = false; 
 | 
            String deadEndPoint = null; 
 | 
            Integer anotherPointNearsCount = null; 
 | 
            if (this.adjacencyCodeMap.get(startPoint).size() == 1) { 
 | 
                isDeadEndLane = true; 
 | 
                deadEndPoint = startPoint; 
 | 
                anotherPointNearsCount = this.adjacencyCodeMap.get(endPoint).size(); 
 | 
            } 
 | 
            if (this.adjacencyCodeMap.get(endPoint).size() == 1) { 
 | 
                isDeadEndLane = true; 
 | 
                deadEndPoint = endPoint; 
 | 
                anotherPointNearsCount = this.adjacencyCodeMap.get(startPoint).size(); 
 | 
            } 
 | 
  
 | 
            if (!isDeadEndLane) { 
 | 
                continue; 
 | 
            } 
 | 
  
 | 
            if (anotherPointNearsCount == 2) { 
 | 
                String anotherPoint = deadEndPoint.equals(startPoint) ? endPoint : startPoint; 
 | 
                List<String> anotherPointNears = this.adjacencyCodeMap.get(anotherPoint); 
 | 
                for (String anotherPointNear : anotherPointNears) { 
 | 
                    if (!lane.getCodes().contains(anotherPointNear) && this.adjacencyCodeMap.get(anotherPointNear).size() == 2) { 
 | 
  
 | 
                        for (Lane lane0 : lanes) { 
 | 
                            if (lane0.getCodes().contains(anotherPointNear)) { 
 | 
  
 | 
                                lane0.getCodes().addAll(lane.getCodes()); 
 | 
                                iterator.remove(); 
 | 
  
 | 
                                lane0.sortUsingDfs(this.adjacencyCodeMap); 
 | 
  
 | 
                            } 
 | 
                        } 
 | 
  
 | 
                    } 
 | 
                } 
 | 
            } 
 | 
  
 | 
        } 
 | 
    } 
 | 
  
 | 
    private void deadInteractionPoint() { 
 | 
        for (Lane lane : this.lanes) { 
 | 
            lane.removeInteraction(this.adjacencyCodeMap); 
 | 
        } 
 | 
    } 
 | 
  
 | 
    private void filterLanesWithFewPoints() { 
 | 
        this.lanes.removeIf(next -> next.getCodes().size() <= 2); 
 | 
    } 
 | 
  
 | 
} 
 |