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


Posted in Javascript onJanuary 27, 2018

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

示例图

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

制作思路

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

开始撸码

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图形分组,以备调用

  1. gmp,保存背景地图和起点标记。
  2. mline,保存起点和目标之间的连线,以及目标点。
  3. 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");

创建投影函数

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

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

  1. 由于会有多个物流连线的终点,所以都放在一个marker标记中调用。
  2. 这个标记是由中间的 圆形 + 外圈 构成。外圈的闪烁效果另外创建。
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标记。

  1. 方法需要输入终点城市名称(city)和经纬度(data)
  2. 调用之前建立的project()方法,将终点经纬度转换为平面坐标。
  3. 计算起点(长沙)和终点之前的距离,做为线条长度和动画时间参数。
  4. 在线条上绘制一个圆形标记,并实现从起点到终点的移动动画。
  5. 标记移动到终点后,即删除,节省资源。
  6. 如果线点在之前已经绘制过,则不绘线条,只绘制移动标记。
  7. 每处理一次物流单,则城市记录+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]]);
    });

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

Javascript 相关文章推荐
javascript 学习笔记(八)javascript对象
Apr 12 Javascript
javascript插入样式实现代码
Feb 22 Javascript
js弹出层(jQuery插件形式附带reLoad功能)
Apr 12 Javascript
jQuery.Validate验证库的使用介绍
Apr 26 Javascript
jquery遍历数组与筛选数组的方法
Nov 05 Javascript
JavaScript获取某年某月的最后一天附截图
Jun 23 Javascript
jQuery实现form表单基于ajax无刷新提交方法详解
Dec 08 Javascript
javascript执行环境及作用域详解
May 05 Javascript
jQuery使用JSONP实现跨域获取数据的三种方法详解
May 04 jQuery
VueJS 集成 Medium Editor的示例代码 (自定义编辑器按钮)
Aug 24 Javascript
关于axios不能使用Vue.use()浅析
Jan 12 Javascript
webpack打包非模块化js的方法
Oct 24 Javascript
javascript获取图片的top N主色值方法详解
Jan 26 #Javascript
Vue中render方法的使用详解
Jan 26 #Javascript
Angular利用trackBy提升性能的方法
Jan 26 #Javascript
微信小程序版翻牌小游戏
Jan 26 #Javascript
基于百度地图api清除指定覆盖物(Overlay)的方法
Jan 26 #Javascript
微信小程序wx.getImageInfo()如何获取图片信息
Jan 26 #Javascript
微信小程序实现animation动画
Jan 26 #Javascript
You might like
PHPStrom 新建FTP项目以及在线操作教程
2016/10/16 PHP
javascript 可以拖动的DIV(二)
2009/06/26 Javascript
基于jquery的Repeater实现代码
2010/07/17 Javascript
用Javascript评估用户输入密码的强度(Knockout版)
2011/11/30 Javascript
JavaScript定时器详解及实例
2013/08/01 Javascript
触屏中的JavaScript事件分析
2015/02/06 Javascript
最简单纯JavaScript实现Tab标签页切换的方式(推荐)
2016/07/25 Javascript
JS实现的样式切换功能tableCSS实例
2016/12/30 Javascript
JavaScript中清空数组的三种方式
2017/03/22 Javascript
Vue项目中quill-editor带样式编辑器的使用方法
2017/08/08 Javascript
使用jQuery 操作table 完成单元格合并的实例
2017/12/27 jQuery
jquery 时间戳转日期过程详解
2019/10/12 jQuery
简单的Python2.7编程初学经验总结
2015/04/01 Python
Python中元组,列表,字典的区别
2017/05/21 Python
对python mayavi三维绘图的实现详解
2019/01/08 Python
详解Python匿名函数(lambda函数)
2019/04/19 Python
python实现Excel文件转换为TXT文件
2019/04/28 Python
python pandas模块基础学习详解
2019/07/03 Python
Python Django的安装配置教程图文详解
2019/07/17 Python
Scrapy框架实现的登录网站操作示例
2020/02/06 Python
pytorch模型存储的2种实现方法
2020/02/14 Python
完美解决jupyter由于无法import新包的问题
2020/05/26 Python
巴西独家产品和现场演示购物网站:Shoptime
2019/07/11 全球购物
电子商务专业学生的学习自我评价
2013/10/27 职场文书
优秀教师主要事迹
2014/02/01 职场文书
元旦晚会邀请函
2014/02/01 职场文书
应聘护理专业毕业自荐书范文
2014/02/12 职场文书
电子商务助理求职自荐信
2014/04/10 职场文书
买房协议书
2014/04/11 职场文书
大学生推广普通话演讲稿
2014/09/21 职场文书
庆国庆国旗下讲话稿2014
2014/09/21 职场文书
2014年科协工作总结
2014/12/09 职场文书
给老婆的检讨书
2015/01/27 职场文书
oracle覆盖导入dmp文件的2种方法
2021/05/21 Oracle
java如何实现socket连接方法封装
2021/09/25 Java/Android
Go本地测试解耦任务拆解及沟通详解Go本地测试的思路沟通的重要性总结
2022/06/21 Golang