使用D3.js创建物流地图的示例代码

本文介绍了使用D3.js创建物流地图的示例代码,分享给大家,具体如下:

示例图

使用D3.js创建物流地图的示例代码

制作思路

    需要绘制一张中国地图,做为背景。
    需要主要城市的经纬坐标,以绘制路张起点和终点。
    接收到物流单的城市,绘制一个闪烁的标记。
    已经有过物流单的目标城市,不再绘制路线。
    每次新产生一笔物流单,都有一个标记沿路线移向目标的动画效果。
    绘制结束后的数据,需要清理掉,以减少对浏览器的资源占用。

开始撸码

1.创建网页模板

加载D3JS,为了方便调试,直接下载d3.js文件在本地,实际使用的时候,可以换成加载路径。注意,使用的是V4版的D3,和V3版有差异。

创建一个DIV块,准备绘图。

<!DOCTYPE html>
<html lang=\"en\">
  <head>
    <meta charset=\"utf8\">
    <script type=\"text/javascript\" src=\"../../static/d3/d3.js\"></script>
    <title>地图</title>
  </head>
  <body>
    <div class=\"fxmap\">
    </div>
  </body>
  <script type=\"text/javascript\"></script>
</html>

创建SVG,以下所有代码放在<script></script>中

var width=1000 , height=800; // 定义SVG宽高
var svg = d3.select(\"body div.fxmap\")
            .append(\"svg\")
            .attr(\"width\", \"width) 
            .attr(\"height\", height)
            .style(\"background\",\"#000\"); //

创建SVG图形分组,以备调用

    gmp,保存背景地图和起点标记。
    mline,保存起点和目标之间的连线,以及目标点。
    buttion,测试用的按钮
gmap = svg.append(\"g\").attr(\"id\", \"map\").attr(\"stroke\", \"white\").attr(\"stroke-width\",1);
    mline = svg.append(\"g\").attr(\"id\", \"moveto\").attr(\"stroke\", \"#FFF\").attr(\"stroke-width\", 1.5).attr(\"fill\",\"#FFF\");
    button = svg.append(\"g\").attr(\"id\", \"button\").attr(\"stroke\", \"white\").attr(\"stroke-width\", 1).attr(\"fill\", \"white\");

创建投影函数

    经纬度不能直接用来绘图,需要转换成平面坐标。d3js提供了比较丰富的投影方法,本例中使用了geoEquirectangular()方法。
    projection 是将经纬度转换为平面坐标的方法
    path 是将经纬度转换为连线绘制点坐标的方法(省得自己再写函数构造path路径)
var projection = d3.geoEquirectangular()
              .center([465,395]) // 指定投影中心,注意[]中的是经纬度
              .scale(height)
              .translate([width / 2, height / 2]);
var path = d3.geoPath().projection(projection);

创建marker标记,以便多个连线终点调用

    由于会有多个物流连线的终点,所以都放在一个marker标记中调用。
    这个标记是由中间的 圆形 + 外圈 构成。外圈的闪烁效果另外创建。
marker = svg.append(\"defs\")
          .append(\"marker\")
          .append(\"marker\")
          .attr(\"id\", \"pointer\")
          .attr(\"viewBox\",\"0 0 12 12\")  // 可见范围
          .attr(\"markerWidth\",\"12\")    // 标记宽度
          .attr(\"markerHeight\",\"12\")    // 标记高度
          .attr(\"orient\", \"auto\")     //
          .attr(\"markerUnits\", \"strokeWidth\") // 随连接线宽度进行缩放
          .attr(\"refX\", \"6\")       // 连接点坐标
          .attr(\"refY\", \"6\")
    // 绘制标记中心圆
    marker.append(\"circle\")
        .attr(\"cx\", \"6\")
        .attr(\"cy\", \"6\")
        .attr(\"r\", \"3\")
        .attr(\"fill\", \"white\");
    // 绘制标记外圆,之后在timer()中添加闪烁效果
    marker.append(\"circle\")
        .attr(\"id\", \"markerC\")
        .attr(\"cx\", \"6\")
        .attr(\"cy\", \"6\")
        .attr(\"r\", \"5\")
        .attr(\"fill-opacity\", \"0\")
        .attr(\"stroke-width\", \"1\")
        .attr(\"stroke\", \"white\");

绘制中国地图,并标记起点(长沙)

地图使用的经纬集为china.json,这个文件网上有很多

// 记录长沙坐标
    var changsha = projection([112.59,28.12]);
    // 读取地图数据,并绘制中国地图
    mapdata = [];
    d3.json(\'china.json\', function(error, data){
      if (error)
        console.log(error);
      // 读取地图数据
      mapdata = data.features;
      // 绘制地图
      gmap.selectAll(\"path\")
        .data(mapdata)
        .enter()
        .append(\"path\")
        .attr(\"d\", path);
      // 标记长沙
      gmap.append(\"circle\").attr(\"id\",\"changsha\")
        .attr(\"cx\", changsha[0])
        .attr(\"cy\", changsha[1])
        .attr(\"r\", \"6\")
        .attr(\"fill\", \"yellow\")
      gmap.append(\"circle\").attr(\"id\",\"changshaC\")
        .attr(\"cx\", changsha[0])
        .attr(\"cy\", changsha[1])
        .attr(\"r\", \"10\")
        .attr(\"stroke-width\", \"2\")
        .attr(\"fill-opacity\", \"0\");
    });

创建方法,绘制一条从指定起点到终点的连线,并在络点绘制marker标记。

    方法需要输入终点城市名称(city)和经纬度(data)
    调用之前建立的project()方法,将终点经纬度转换为平面坐标。
    计算起点(长沙)和终点之前的距离,做为线条长度和动画时间参数。
    在线条上绘制一个圆形标记,并实现从起点到终点的移动动画。
    标记移动到终点后,即删除,节省资源。
    如果线点在之前已经绘制过,则不绘线条,只绘制移动标记。
    每处理一次物流单,则城市记录+1。
// 创建对象,保存每个城市的物流记录数量
    var citylist = new Object();
    // 创建方法,输入data坐标,绘制发射线
    var moveto = function(city, data){
      var pf = {x:projection([112.59,28.12])[0], y:projection([112.59,28.12])[1]};
      var pt = {x:projection(data)[0], y:projection(data)[1]};
      var distance = Math.sqrt((pt.x - pf.x)**2 + (pt.y - pf.y)**2);
      if (city in citylist){
        citylist[city]++;
      }else{
        mline.append(\"line\")
            .attr(\"x1\", pf.x)
            .attr(\"y1\", pf.y)
            .attr(\"x2\", pt.x)
            .attr(\"y2\", pt.y)
            .attr(\"marker-end\",\"url(#pointer)\")
            .style(\"stroke-dasharray\", \" \"+distance+\", \"+distance+\" \")
            .transition()
            .duration(distance*30)
            .styleTween(\"stroke-dashoffset\", function(){
              return d3.interpolateNumber(distance, 0);
            });
        citylist[city] = 1;
      };
      mline.append(\"circle\")
        .attr(\"cx\", pf.x)
        .attr(\"cy\", pf.y)
        .attr(\"r\", 3)
        .transition()
        .duration(distance*30)
        .attr(\"transform\", \"translate(\"+(pt.x-pf.x)+\",\"+(pt.y-pf.y)+\")\")
        .remove();
    };

创建动画队例,实现标记外圈的闪烁效果

var scale = d3.scaleLinear();
    scale.domain([0, 1000, 2000])
      .range([0, 1, 0]);
    var start = Date.now();
    d3.timer(function(){
      var s1 = scale((Date.now() - start)%2000);
      // console.log(s1);
      gmap.select(\"circle#changshaC\")
        .attr(\"stroke-opacity\", s1);
      marker.select(\"circle#markerC\")
        .attr(\"stroke-opacity\", s1);
    });

创建测试按钮和测试的目标城市数据

var cityordinate = {
      \'哈尔滨\':[126.5416150000,45.8088260000],
      \'石家庄\':[116.46,39.92],
      \'北京\':[116.39564503787867,39.92998577808024],
      \'上海\':[121.480539,31.235929],
      \'广州\':[113.271431,23.135336],
      \'重庆\':[106.558434,29.568996],
      \'青岛\':[120.38442818368189,36.10521490127382],
      \'福州\':[119.30347,26.080429],
      \'兰州\':[103.840521,36.067235],
      \'贵阳\':[106.636577,26.653325],
      \'成都\':[104.081534,30.655822],
      \'西安\':[108.946466,34.347269],
      \'长春\':[125.3306020000,43.8219540000],
      \'台湾\':[120.961454,23.80406],
      \'呼和浩特\':[111.7555090000,40.8484230000],
      \'澳门\':[113.5494640000,22.1929190000],
      \'武汉\':[114.3115820000,30.5984670000],
      \'昆明\':[102.71460113878045,25.049153100453159],
      \'乌鲁木齐\':[87.56498774111579,43.84038034721766],
      \'益阳\':[112.36654664522563,28.58808777988717],
      \'南京\':[118.77807440802562,32.05723550180587],
      \'武昌\':[114.35362228468498,30.56486029278519],
    };

    // 随机获得目标城市
    var cityname = [], total = 0;
    for (var key in cityordinate){
      cityname[total++] = key;
    };
    
    // 创建操作按钮,每次点击发射一条物流线
    button.append(\"circle\")
        .attr(\"cx\", width*0.9)
        .attr(\"cy\", height*0.8)
        .attr(\"r\", width/20)
        .attr(\"text\",\"click\")
        .attr(\"fill\", \"grey\");
    button.append(\"text\")
        .attr(\"x\", width*0.87)
        .attr(\"y\", height*0.81)
        .style(\"font-size\", \"30px\")
        .text(\"click\");
    button.on(\"click\", function(){
      var _index = ~~(Math.random() * total);
      moveto(cityname[_index], cityordinate[cityname[_index]]);
    });

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容