使用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 相关文章推荐
JQuery中SetTimeOut传参问题探讨
May 10 Javascript
javascript 上下banner替换具体实现
Nov 14 Javascript
javascript 循环调用示例介绍
Nov 20 Javascript
jquery操作checkbox实现全选和取消全选
May 02 Javascript
Javascript中设置默认参数值示例
Sep 11 Javascript
JavaScript之Date_动力节点Java学院整理
Jun 28 Javascript
浅谈vuex 闲置状态重置方案
Jan 04 Javascript
基于 Immutable.js 实现撤销重做功能的实例代码
Mar 01 Javascript
vue2.0使用swiper组件实现轮播的示例代码
Mar 03 Javascript
jQuery实现的隔行变色功能【案例】
Feb 18 jQuery
在vue中封装的弹窗组件使用队列模式实现方法
Jul 23 Javascript
详细聊聊浏览器是如何看闭包的
Nov 11 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
先进的自动咖啡技术,真的可以取代咖啡师吗?
2021/03/06 冲泡冲煮
PHP 高级课程笔记 面向对象
2009/06/21 PHP
PHP实现模仿socket请求返回页面的方法
2014/11/04 PHP
PHP自定session保存路径及删除、注销与写入的方法
2014/11/18 PHP
php对关联数组循环遍历的实现方法
2015/03/13 PHP
实现超用户体验 table排序javascript实现代码
2009/06/22 Javascript
javascript 验证日期的函数
2010/03/18 Javascript
基于javascipt-dom编程 table对象的使用
2013/04/22 Javascript
Javascript模拟加速运动与减速运动代码分享
2014/12/11 Javascript
JS实现转动随机数抽奖特效代码
2020/04/16 Javascript
JS中跨页面调用变量和函数的方法(例如a.js 和 b.js中互相调用)
2016/11/01 Javascript
Bootstrap导航条鼠标悬停下拉菜单
2017/01/04 Javascript
Vue仿支付宝支付功能
2018/05/25 Javascript
vue的过滤器filter实例详解
2018/09/17 Javascript
详解React之key的使用和实践
2018/09/29 Javascript
微信小程序实现登录注册tab切换效果
2020/12/29 Javascript
JavaScript时间与时间戳的转换操作实例分析
2018/12/07 Javascript
python 读取.csv文件数据到数组(矩阵)的实例讲解
2018/06/14 Python
Python读取英文文件并记录每个单词出现次数后降序输出示例
2018/06/28 Python
Python使用sorted对字典的key或value排序
2018/11/15 Python
python垃圾回收机制(GC)原理解析
2019/12/30 Python
pytorch AvgPool2d函数使用详解
2020/01/03 Python
Python数据相关系数矩阵和热力图轻松实现教程
2020/06/16 Python
Pycharm如何自动生成头文件注释
2020/11/14 Python
快速解决pymongo操作mongodb的时区问题
2020/12/05 Python
中国最大的名表商城:万表网
2016/08/29 全球购物
Lookfantastic法国官网:英国知名美妆购物网站
2017/10/28 全球购物
俄罗斯品牌服装和鞋子在线商店:BRIONITY
2020/03/26 全球购物
MVC的各个部分都有那些技术来实现?如何实现?
2016/04/21 面试题
大学毕业感言
2014/01/10 职场文书
2014年庆祝国庆65周年演讲稿
2014/09/21 职场文书
2015年班级元旦晚会活动总结
2014/11/28 职场文书
个园导游词
2015/02/04 职场文书
mysql字符串截取函数小结
2021/04/05 MySQL
MySQL Router的安装部署
2021/04/24 MySQL
vue生命周期钩子函数以及触发时机
2022/04/26 Vue.js