svg实现地铁线路图
简介
最近学习了svg,想着使用svg实现地铁线路图
 其中黄色是1号线,蓝色是2号线,橙色是3号线
实现:react+svg+数据结构-图。
考虑范围包括了每站时间,但未包括了换站时间。考虑到换站时间可以加到每2个交换站的路程里
功能
功能:选择2个地铁站,标出最短路程。
求最少换站路线,暂未做
实现思路
- 简化问题,先将所有地铁站分2类,交换站和非交换站。那么交换站可以充当图中的。那么从a=>b, 变成a=>交换站=>交换站=>b的问题,需要写死的是非交换站(a,b)能到达的交换站(下面的adjcent数组), 其中a=>交换站 和b=>交换站 相对静止,但是我这里也考虑到了非交换站到交换站需要的时间(time)
 
地铁线路图

图

- 首先根据每条地铁图数据绘制出地铁线路图,并添加上点击事件,这里要处理好地铁线路图的数据,数据需要相对准确,因为后面需要计算出最短路径。
 


- 求最短距离,使用的是Floyd最短路算法(全局/多源最短路)。 其中原理:计算a->b的最短路径,遍历所有,查找是否有最捷径路径 a->x x->b
 
for(k=1;k<=n;k++) 
for(i=1;i<=n;i++) 
for(j=1;j<=n;j++) 
if(e[i][j]>e[i][k]+e[k][j]) // i->j  i->k  k->j
            e[i][j]=e[i][k]+e[k][j]; 
然而拿到最短路程后,但是并未拿到路程,拿到的是比如,a点到所有点的最短路程。你们可以思考一下如果获取最短路径。
大概长这样

- 求最短路径 使用一个对象,存储每次找到较短路径。 changeRodePath[
${is}to${js}] = [ [is, ks], [ks, js], ] 
  function getAllPointShortest(n, e) {
let changeRodePath = {};
for (let k = 0; k < n; k++) {
for (let i = 0; i < n; i++) {
for (let j = 0; j < n; j++) {
if (e[i][j] > e[i][k] + e[k][j]) {
            e[i][j] = e[i][k] + e[k][j];
console.log("-------------------------");
const is = changeStation[i];
const ks = changeStation[k];
const js = changeStation[j];
            changeRodePath[`${is}to${js}`] = [
              [is, ks],
              [ks, js],
            ];
console.log(changeStation[i], changeStation[j]);
console.log(changeStation[i], changeStation[k]);
console.log(changeStation[k], changeStation[j]);
// 2_2 2_5
//2_2 1_2
//1_2 2_5
          }
        }
      }
    }
setChangeRodePath(changeRodePath);
return e;
  }
当选中2个站时,先取出adjacent,然后求出最短路程,
         let path = {};
        adjacent0.forEach((p0,i1) => {
          adjacent1.forEach((p1,i2) => {
const index0 = changeStation.indexOf(p0);
const index1 = changeStation.indexOf(p1);
let t=time0[i1]+time1[i2]
if ((rodePath[index0][index1]+t) < minPath) {
              minPath = rodePath[index0][index1];
              path = { p0, p1};
            }
          });
        });
具体多少不重要,重要的是通过 let pathm = changeRodePath[${path.p0}to${path.p1}],递归查找是否有更短的捷径,因为,2_1 =>3_9 的路径是:2_1 =>1_3=>1_5=>1_8,所以不一定有捷径a->c c—b, 可能是 a->c c->b, 然后发现有更短路径,c->d d->b,那么a-b 路程就变成了a->c->d->b。回到正题,递归之后就能取到最短路径了,然后通过2个交换点取得路径。
没有就更简单了
5.取对应的line,去渲染,这里分2类,交换站之间的路径(最短路径),头和尾。然后分别渲染polyline(使用对应line 的颜色)
function getPl(item, attr, listen) {
return (
<g {...attr} {...listen}>  
<polyline   //绘制line
          {...item}
          fill="none"
          color={item.colorH}
          strokeWidth="5"
          strokeLinecap="round"
          strokeLinejoin="round"
        />
        {item.usePointn.map((point) => { // line 上的站
return (
<use
              x={point.x}
              onClick={() => choosePoint(point)}
              y={point.y}
              fill={point.color}
              href="#point"
            >use>
          );
        })}
g>
    );
  }
代码准备
// 上图所示,数据随便造,需要合理时间,不然得到的路程奇奇怪怪的
代码部分
html
  width: "80vw", height: "100vh" }}>
<svg
          id="passWay"
          viewBox="0 0 800 600"
          xmlns="http://www.w3.org/2000/svg"
        >
<defs>
<g id="point">
<circle r="4">circle>
<circle r="3" fill="#fff">circle>
g>          
defs>
// 所有地铁线路图
          {polyline.map((item) => {
return getPl(
              item,
              {},
              {
                onMouseEnter: (e) => onMouseEnterShow(e, item),
                onMouseOut: () => {
                  clearTimeout(t1.current);
                  t1.current = null;
                },
              }
            );
          })}
// mask
          { choosePoints.length==2 && (
<rect
              x="0"
              y="0"
              width={"100%"}
              height={"100%"}
              fillOpacity={0.9}
              fill="white"
            >rect>
          )}
// 最短路程
          {choosePoints && choosePoints.length==2 && showReduLine.map(line=>{
return  getPl(line, {}, {})
          })
         }
svg>
通过line 获取 polyline
  function getLineP(line) {
const usePointn = [];
let path = "";
    line.points.forEach((item, index) => {
const { x, y, isStart, isChange, isEnd } = item;
      usePointn.push({ ...item, color: line.color });
if (index == 0) {
        path = `${x},${y} `;
      } else {
        path += `${x},${y} `;
      }
    });
const polylinen = {
      usePointn,
      stroke: line.color,
      ...line,
      pointStation: line.points,
      points: path,
    };
return polylinen;
  }
选出2站绘制路程
  function comfirPath(point0, point1, p0, p1, pathm) {
let pShow0= getLines(point0,p0)
let pShow1= getLines(point1,p1)
let pathsCenter=[]
if (pathm) {
function recursion(pathm){
         pathm.map(([p0,p1])=>{
let pathn = changeRodePath[`${p0}to${p1}`];
if(pathn){
recursion(pathn)
          }else{
// 中间的line 不用按顺序
            pathsCenter.push(getChangeStationLine(p0,p1))
          }
       })
      }
recursion(pathm)
    }else{
      pathsCenter=[getChangeStationLine(p0,p1)]
    }
const pyAll= [pShow0,pShow1,...pathsCenter].map(line=>{
const py= getLineP({
        points:line,
      })
      py.stroke=line.color
return py
    })
setShowReduLine(pyAll); // 绘制
  }
参考: 1.# [数据结构拾遗]图的最短路径算法
作者:无名小兵
来源:juejin.cn/post/7445208959151767604
来源:juejin.cn/post/7445208959151767604