多模式运输路线规划平台

目录

前言:

1.Mysql准备

2.道路网图

2.1样式结果:

2.2样例代码:

3.核心算法 

3.1crud

3.1.1查询数据

3.1.2建立枚举类

3.2迪杰斯特拉


前言:

通过录入各地之间的运算成本,然后通过算法将其“有向图”显示出来,并通过算法计算出最短路径显示给客户,供其选择。

1.前期准备 

1.1.Mysql准备

-- 创建数据库表
CREATE TABLE IF NOT EXISTS `Routes` (
  `ID` INT NOT NULL AUTO_INCREMENT,
  `StartPoint` VARCHAR(255) NOT NULL,
  `EndPoint` VARCHAR(255) NOT NULL,
  `TransportMode` INT,
	`TransportTime` INT,
  `TransportPrice` INT,
  PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- 插入示例数据
INSERT INTO `Routes` (`StartPoint`, `EndPoint`, `TransportMode`, `TransportTime`, `TransportPrice`) VALUES
('北京', '上海', 1,60,  100),
('北京', '上海', 2,50,  200),
('北京', '上海', 3,40,  300);

-- 插入示例数据
INSERT INTO `Routes` (`StartPoint`, `EndPoint`, `TransportMode`, `TransportTime`, `TransportPrice`) VALUES
('北京', '广州', 1, 120, 150),
('北京', '广州', 2, 100, 250),
('北京', '广州', 3, 30, 450),
('北京', '深圳', 1, 150, 180),
('北京', '深圳', 2, 120, 280),
('北京', '深圳', 3, 40, 480),
('北京', '成都', 1, 180, 200),
('北京', '成都', 2, 150, 300),
('北京', '成都', 3, 90, 500),
('北京', '杭州', 1, 90, 110),
('北京', '杭州', 2, 70, 210),
('北京', '杭州', 3, 50, 310),
('上海', '北京', 1, 65, 105),
('上海', '北京', 2, 55, 205),
('上海', '北京', 3, 45, 305),
('上海', '广州', 1, 110, 160),
('上海', '广州', 2, 90, 260),
('上海', '广州', 3, 35, 460),
('上海', '深圳', 1, 140, 190),
('上海', '深圳', 2, 110, 290),
('上海', '深圳', 3, 35, 490),
('上海', '成都', 1, 170, 210),
('上海', '成都', 2, 140, 310),
('上海', '成都', 3, 85, 510),
('上海', '杭州', 1, 95, 115),
('上海', '杭州', 2, 75, 215),
('上海', '杭州', 3, 55, 315),
('广州', '北京', 1, 125, 155),
('广州', '北京', 2, 105, 245),
('广州', '北京', 3, 35, 435),
('广州', '上海', 1, 115, 165),
('广州', '上海', 2, 95, 265),
('广州', '上海', 3, 30, 465);

-- 添加唯一索引
ALTER TABLE `Routes`
ADD UNIQUE INDEX `unique_route` (`StartPoint`, `EndPoint`, `TransportMode`);

1.2实体类

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

/**
 * @Author: kirito
 * @Date: 2024/9/17 17:23
 * @Description:
 */
@Data
@TableName("routes")
public class Routes {
    /**
     * 唯一标识符,用于数据库中的主键。
     */
    @TableId(type = com.baomidou.mybatisplus.annotation.IdType.AUTO)
    private int ID;

    /**
     * 起始点,即运输路线的起始城市。
     */
    private String StartPoint;

    /**
     * 终点,即运输路线的目的地城市。
     */
    private String EndPoint;

    /**
     * 运输方式,通常用数字编码表示,
     * 例如1表示公交,2表示火车,3表示飞机等。
     */
    private int TransportMode;

    /**
     * 运输时间,表示从起始点到终点的预计时间,
     * 通常以分钟为单位。
     */
    private int TransportTime;

    /**
     * 运输价格,表示从起始点到终点的运输费用,
     * 通常以货币单位表示。
     */
    private int TransportPrice;

    public Routes(String start, int time) {
        setStartPoint(start);
        setTransportTime(time);
    }

    public Routes(int id, String startPoint, String endPoint, int transportMode, int transportTime, int transportPrice) {
        setID(id);
        setStartPoint(startPoint);
        setEndPoint(endPoint);
        setTransportMode(transportMode);
        setTransportPrice(transportPrice);
        setTransportTime(transportTime);
    }

}

1.3Mapper 

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yqn.demo.Entity.Routes;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

import java.util.List;

@Mapper
public interface RoutesMapper extends BaseMapper<Routes> {

    Routes getByStartPoint(String startPoint);

    /**
     * 根据时间优先查询路线
     * @return List<Routes>
     */
    @Select("SELECT r.*\n" +
            "FROM Routes AS r\n" +
            "INNER JOIN (\n" +
            "    SELECT StartPoint, EndPoint, MIN(TransportTime) AS MinTime\n" +
            "    FROM Routes\n" +
            "    GROUP BY StartPoint, EndPoint\n" +
            ") AS s ON r.StartPoint = s.StartPoint AND r.EndPoint = s.EndPoint AND r.TransportTime = s.MinTime\n" +
            "ORDER BY r.StartPoint, r.EndPoint;")
    List<Routes> getRoutesByPriTime();

    /**
     * 根据时间优先查询路线
     * @return List<Routes>
     */
    @Select("SELECT r.*\n" +
            "FROM Routes AS r\n" +
            "INNER JOIN (\n" +
            "    SELECT StartPoint, EndPoint, MIN(TransportPrice) AS MinPrice\n" +
            "    FROM Routes\n" +
            "    GROUP BY StartPoint, EndPoint\n" +
            ") AS s ON r.StartPoint = s.StartPoint AND r.EndPoint = s.EndPoint AND r.TransportPrice = s.MinPrice\n" +
            "ORDER BY r.StartPoint, r.EndPoint;")
    List<Routes> getRoutesByPriPrice();
}

1.4Service层

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yqn.demo.Entity.Routes;
import com.yqn.demo.Mapper.RoutesMapper;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;


@Service
public class RoutesService extends ServiceImpl<RoutesMapper, Routes> implements IService<Routes> {
    @Resource
    private RoutesMapper routesMapper;

    /**
     * 根据出发点查询所有路线
     * @param startPoint    出发点
     * @return  List<Routes>
     */
    public List<Routes> getByStartPoint(String startPoint) {
        return routesMapper.selectList(new QueryWrapper<Routes>().eq("StartPoint", startPoint));
    }

    /**
     * 查询所有路线
     * @return  List<Routes>
     */
    public List<Routes> getAll() {
        QueryWrapper<Routes> queryWrapper = new QueryWrapper<>();
        // 调用 list 方法执行查询
        return this.list(queryWrapper);
    }

    /**
     * 根据时间优先查询路线
     *
     * @return List<Routes>
     */
    public List<Routes> getRoutesByPriTime() {
        return routesMapper.getRoutesByPriTime();
    }

    /**
     * 根据成本优先查询路线
     *
     * @return List<Routes>
     */
    public List<Routes> getRoutesByPriPrice() {
        return routesMapper.getRoutesByPriPrice();
    }

}

1.5 Controller层

import com.yqn.demo.Entity.Routes;
import com.yqn.demo.Service.RoutesService;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.List;

@RestController
@RequestMapping("/routes")
public class RoutesController {

    @Resource
    private RoutesService routesService;

    @GetMapping("/get/{startPoint}")
    public List<Routes> getByStartPoint(@PathVariable String startPoint) {
        return routesService.getByStartPoint(startPoint);
    }

    @PostMapping
    public boolean save(@RequestBody Routes routes) {
        return routesService.save(routes);
    }

    @GetMapping("/{id}")
    public Routes getById(@PathVariable int id) {
        return routesService.getById(id);
    }

    @PutMapping
    public boolean updateById(@RequestBody Routes routes) {
        return routesService.updateById(routes);
    }

    @DeleteMapping("/{id}")
    public boolean removeById(@PathVariable int id) {
        return routesService.removeById(id);
    }
}

2.道路网图

用的echarts.js画的,目前只是gpt简单调试了一下,应该有办法可以实现三线双向有向图,但是代码需要查看官方文档(如果实现不了三线其实也可以弄一个按钮把三种运货方式用三个图表示出来也行)

然后可以把权值(时间/成本)也同时显示在线段上边

2.1样式结果:

2.2样例代码:

目前只是demo样例,还没有跟后端结合,后续完成后可以结合后端数据调试

<!DOCTYPE html>
<html style="height: 100%">
<head>
    <meta charset="utf-8">
    <title>运输路线图</title>
    <!-- 引入 ECharts.js -->
    <script src="https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js"></script>
</head>
<body style="height: 100%; margin: 0">
    <!-- 准备一个用于放图表的容器 -->
    <div id="main" style="width: 100%;height:100%;"></div>
    <script type="text/javascript">
        // 基于准备好的dom,初始化echarts实例
        var myChart = echarts.init(document.getElementById('main'));

        // 指定图表的配置项和数据
        var option = {
            title: {
                text: '运输路线图'
            },
            tooltip: {},
            animationDurationUpdate: 1500,
            animationEasingUpdate: 'quinticInOut',
            series: [
                {
                    type: 'graph',
                    layout: 'none',
                    symbolSize: 50,
                    roam: true,
                    label: {
                        show: true
                    },
                    edgeSymbol: ['circle', 'arrow'],
                    edgeSymbolSize: [4, 10],
                    edgeLabel: {
                        fontSize: 20
                    },
                    data: [
                        {name: '北京', x: 300, y: 300},
                        {name: '上海', x: 800, y: 300},
                        {name: '广州', x: 550, y: 100},
                        {name: '深圳', x: 550, y: 500},
                        {name: '成都', x: 200, y: 100},
                        {name: '杭州', x: 200, y: 500}
                    ],
                    // links 的数据格式为:{source: string, target: string, value: number}
                    links: [
                        {source: '北京', target: '广州', value: 150},
						{source: '广州', target: '北京', value: 150},
						{source: '广州', target: '成都', value: 150},
						{source: '北京', target: '广州', value: 150},
						{source: '成都', target: '北京', value: 150},
                        {source: '北京', target: '深圳', value: 180},
						{source: '杭州', target: '深圳', value: 180},
                        // ... 其他路线数据
                    ],
                    lineStyle: {
                        opacity: 0.9,
                        width: 2,
                        curveness: 0
                    }
                }
            ]
        };

        // 使用刚指定的配置项和数据显示图表。
        myChart.setOption(option);
    </script>
</body>
</html>

 后续通过算法根据时间优先或者成本优先后得出一条最短路径后画出可以画出对应的图

 

3.核心算法 

3.1crud

3.1.1查询数据

现在查询到了数据,就需要开始想办法将其构建成为图

3.1.2建立枚举类

方便维护(整个对象构建图对编码不太友好)

public enum City {
    SHANGHAI(0, "上海"),
    BEIJING(1, "北京"),
    GUANGZHOU(2, "广州"),
    SHENZHEN(3, "深圳"),
    CHENGDU(4, "成都"),
    HANGZHOU(5, "杭州");

    private final int code;
    private final String name;

    City(int code, String name) {
        this.code = code;
        this.name = name;
    }

    public int getCode() {
        return code;
    }

    public String getName() {
        return name;
    }

    /**
     * @return 返回城市总数
     */
    public static int getCitiNum() {
        return City.values().length;
    }

    /**
     * @param name 中文城市名
     * @return  返回枚举
     */
    public static City getCityByName(String name) {
        for (City city : City.values()) {
            if (city.getName().equals(name)) {
                return city;
            }
        }
        return null; // 如果没有找到匹配的枚举常量,则返回null
    }
}

3.2迪杰斯特拉

import java.util.*;

/**
 * @Author: kirito
 * @Date: 2024/9/17 19:12
 * @Description:
 */

public class Graph {
    private static List<Routes> staticRoutesList = Arrays.asList(
//            new Routes(18, "上海", "北京", 3, 45, 305),
            new Routes(21, "上海", "广州", 3, 35, 460),
            new Routes(27, "上海", "成都", 3, 85, 510),
            new Routes(30, "上海", "杭州", 3, 55, 315),
            new Routes(24, "上海", "深圳", 3, 35, 490),
            new Routes(3, "北京", "上海", 3, 40, 300),
            new Routes(6, "北京", "广州", 3, 30, 450),
            new Routes(12, "北京", "成都", 3, 90, 500),
            new Routes(15, "北京", "杭州", 3, 50, 310),
            new Routes(9, "北京", "深圳", 3, 40, 480),
            new Routes(36, "广州", "上海", 3, 30, 465),
            new Routes(33, "广州", "北京", 3, 35, 435)
    );

    public static void main(String[] args) {
        List<Routes> dijkstra = dijkstra(staticRoutesList, "上海", "北京");
        System.out.println();
        for (Routes r : dijkstra) {
            System.out.println(r);
        }
    }

    public static List<Routes> dijkstra(List<Routes> list, String start, String target) {
        int n = City.getCitiNum();
        //构建邻接矩阵
        List<int[]>[] graph = new List[n];
        for (int i = 0; i < n; i++) {
            graph[i] = new ArrayList<>();
        }
        for (Routes r : list) {
            int x = City.getCityByName(r.getStartPoint()).getCode();
            int y = City.getCityByName(r.getEndPoint()).getCode();
            int weight = r.getTransportTime();
            graph[x].add(new int[]{y, weight});
        }
        // shortestDistances数组保存从起始顶点到其他所有顶点的最短距离
        int[] shortestDistances = new int[n];

        // added数组标记顶点是否已经加入到最短路径树中
        boolean[] added = new boolean[n];

        // 初始化所有距离为无穷大,added数组为false
        for (int vertexIndex = 0; vertexIndex < n; vertexIndex++) {
            shortestDistances[vertexIndex] = Integer.MAX_VALUE;
            added[vertexIndex] = false;
        }

        PriorityQueue<int[]> pq = new PriorityQueue<int[]>((a, b) -> a[0] - b[0]);

        int startCode = City.getCityByName(start).getCode();
        int endCode = City.getCityByName(target).getCode();
        // 起始顶点到自身的距离始终为0
        shortestDistances[startCode] = 0;
        pq.offer(new int[]{0, startCode});


        // parents数组用于存储最短路径树
        int[] parents = new int[n];
        // 起始顶点没有父节点
        parents[startCode] = -1;

        while (!pq.isEmpty()) {
            int[] current = pq.poll();
            int u = current[1];
            added[u] = true;

            for (int[] pair : graph[u]) {
                int v = pair[0];
                int weight = pair[1];

                if (!added[v] && shortestDistances[u] != Integer.MAX_VALUE
                        && shortestDistances[u] + weight < shortestDistances[v]) {
                    shortestDistances[v] = shortestDistances[u] + weight;
                    parents[v] = u;
                    pq.offer(new int[]{shortestDistances[v], v});
                }
            }
        }
        // 打印结果
        printSolution(startCode, shortestDistances, parents);

        // 如果需要返回路径,可以在这里构建路径列表

        return buildPath(startCode, endCode, parents, list);
    }
    // 打印构建的距离数组和最短路径
    private static void printSolution(int startVertex, int[] distances, int[] parents) {
        int nVertices = distances.length;
        System.out.print("顶点\t距离\t路径");

        for (int vertexIndex = 0; vertexIndex < nVertices; vertexIndex++) {
            if (vertexIndex != startVertex) {
                System.out.print("\n" + startVertex + " -> ");
                System.out.print(vertexIndex + "\t\t");
                System.out.print(distances[vertexIndex] + "\t\t");
                printPath(vertexIndex, parents);
            }
        }
    }
    private static void printPath(int currentVertex, int[] parents) {
        if (currentVertex == -1) {
            return;
        }
        printPath(parents[currentVertex], parents);
        System.out.print(currentVertex + " ");
    }

    private static List<Routes> buildPath(int startCode, int endCode, int[] parents, List<Routes> routesList) {
        List<Routes> path = new ArrayList<>();
        int current = endCode;
        while (current != startCode) {
            int parent = parents[current];
            for (Routes route : routesList) {
                if (route.getStartPoint().equals(
                        City.values()[parent].getName()
                ) &&
                        route.getEndPoint().equals(
                                City.values()[current].getName())
                ) {
                    path.add(0, route);
                    break;
                }
            }
            current = parent;
        }
        return path;
    }
}

3.2.1运行结果

符合最开始的预期结果Over! 

tips:

后续准备加入其他的算法以及多条路径,有时间再写吧~

TMS系统(Transportation Management System)是一种用于管理和优化运输活动的软件系统。在TMS系统中,运输路线规划是其中一个重要的功能之一。 运输路线规划是指根据货物的起始地点、目的地、货物属性、运输模式运输成本等因素,通过TMS系统自动计算最佳的运输路线。这可以帮助企业降低运输成本、提高运输效率,并确保货物按时到达目的地。 TMS系统的运输路线规划一般包括以下几个步骤: 1. 起始地点和目的地的确定:根据货物的发货地和收货地,确定起始地点和目的地。 2. 路线选择:根据货物属性、运输模式(如公路运输、铁路运输、航空运输等)以及其他限制条件(如交通状况、道路限制等),选择最佳的运输路线。 3. 运输成本计算:根据选定的路线,计算出相应的运输成本,包括燃料费用、人工费用、过路费等。 4. 运输时间估计:根据选定的路线运输模式,估计货物到达目的地所需的时间。 5. 路线优化:根据企业设定的优化目标(如最短路线、最低成本等),通过算法对已选定的路线进行优化,以达到更好的运输效果。 需要注意的是,TMS系统的运输路线规划是基于预设的数据和算法进行计算,可能会受到现实情况(如交通堵塞、天气等)的影响。因此,在实际运输过程中,还需要及时根据实际情况进行调整和管理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值