导言:优才网的春节相关的技术系列文章,今天继续关注百度迁徙,我们是从前端的角度来分析的。昨天谈完了数据和格式,今天来聊聊具体的 JS 实现。
昨天的文章里分析了百度迁徙的数据抓包和数据格式分析,了解了百度迁徙没有使用 Flash 和 SVG 技术,而是使用的 echarts 库,在Canvas上做的绘制,echarts 是百度前端团队的开源项目(PS,百度前端团队,有不少好的开源项目,如FIS、Ueditor、Tangram 都是百度前端开源的),据官方介绍,ECharts (Enterprise Charts 商业产品图表库),提供商业产品常用图表,底层基于ZRender(一个全新的轻量级canvas类库),创建了坐标系,图例,提示,工具箱等基础组件,并在此上构建出折线图、柱状图、散点图、K线图、饼图、雷达图、地图、和弦图、力导向布局图、仪表盘以及漏斗图,同时支持任意维度的堆积和多图表混合展现,也是目前所见的开源产品里图形支持最全面的。
不过迁徙毕竟是地图,所以在 echarts 上,有一层扩展,完成地图到图形操作和数据的映射,虽然 echarts 支持多种类型的图形,但是由于只用到了地图,所以在 Echarts的 chart 目录下只用到了 map.js。如果想了解或者学习绘制其他类型的图,可以进入 Github 下载源代码。https://github.com/ecomfe/echarts
下面就来相对细致地分析一下,是如何将数据取回来,并展现到地图上的。
我们从 mainIndex.js 入手来分析。正如昨天所说的机场和车站地理位置数据,在这个文件的第一行代码就是各个省的各个地级城市的地理位置数据。这也是一份很价值的数据哦。
var CityGeoCoord = [
{ProvinceName: "北京", LatLng: [116.395645, 39.929986]},
{ProvinceName: "上海", LatLng: [121.487899, 31.249162]},
{ProvinceName: "天津", LatLng: [117.210813, 39.14393]},
{ProvinceName: "重庆", LatLng: [106.530635, 29.544606]},
{ProvinceName: "安徽", LatLng: [117.216005, 31.859252],
City: [{Name: "合肥", LatLng: [117.282699, 31.866942]},
{Name: "安庆", LatLng: [117.058739, 30.537898]},
{Name: "蚌埠", LatLng: [117.35708, 32.929499]},
{Name: "亳州", LatLng: [115.787928, 33.871211]},
{Name: "巢湖", LatLng: [117.88049, 31.608733]},
{Name: "池州", LatLng: [117.494477, 30.660019]},
{Name: "滁州", LatLng: [118.32457, 32.317351]},
{Name: "阜阳", LatLng: [115.820932, 32.901211]},
{Name: "淮北", LatLng: [116.791447, 33.960023]},
{Name: "淮南", LatLng: [117.018639, 32.642812]},
{Name: "黄山", LatLng: [118.29357, 29.734435]},
{Name: "六安", LatLng: [116.505253, 31.755558]},
{Name: "马鞍山", LatLng: [118.515882, 31.688528]},
{Name: "宿州", LatLng: [116.988692, 33.636772]},
{Name: "铜陵", LatLng: [117.819429, 30.94093]},
{Name: "芜湖", LatLng: [118.384108, 31.36602]},
{Name: "宣城", LatLng: [118.752096, 30.951642]}]},
接下来,是一些日期处理的工具函数,由于在迁徙的页面上,使用了农历和阳历的对照,所以也有一个简单的农历计算工具函数。具体不详述。
然后是在地图上画标记线、标记点,去除线和点的函数。而对于绘制图的流程如下,以如下画线为例:
mainIndex.js
function AddMarkLine(a, c, d) {
var b = $.extend(!0, {},
a.getOption().series[c].markLine);
b && (b.data = d, a.addMarkLine(c, b))
}
echarts.js(从Github库中得到)
addMarkLine: function (seriesIdx, markData) {
return this._addMark(seriesIdx, markData, 'markLine');
},
_addMark: function (seriesIdx, markData, markType) {
var series = this._option.series;
var seriesItem;
if (series && (seriesItem = series[seriesIdx])) {
var seriesR = this._optionRestore.series;
var seriesRItem = seriesR[seriesIdx];
var markOpt = seriesItem[markType];
var markOptR = seriesRItem[markType];
markOpt = seriesItem[markType] = markOpt || { data: [] };
markOptR = seriesRItem[markType] = markOptR || { data: [] };
for (var key in markData) {
if (key === 'data') {
markOpt.data = markOpt.data.concat(markData.data);
markOptR.data = markOptR.data.concat(markData.data);
} else if (typeof markData[key] != 'object' || markOpt[key] == null) {
markOpt[key] = markOptR[key] = markData[key];
} else {
zrUtil.merge(markOpt[key], markData[key], true);
zrUtil.merge(markOptR[key], markData[key], true);
}
}
var chart = this.chart[seriesItem.type];
chart && chart.addMark(seriesIdx, markData, markType);
}
return this;
},
再接下来是DOM操作和事件绑定,还有其他的DOM工具函数。ChangeMenu 函数是一个切换菜单时调用的函数,在每一次菜单切换(也就是日期切换时)将各种数据重新初始化还原,包括界面上的输入清空,数据类型重置等。传入的参数是各种不同数据类型的类型编码。
function changeMenu(a) {
MigrationResource.CurrentCondition.Province = "";
MigrationResource.CurrentCondition.City = "";
$("#input_cityName").attr("province", "");
$("#input_cityName").attr("city", "");
$("#input_cityName").val("");
$("#input_dateTime").val("");
MigrationResource.CurrentCondition.Flight = {};
$("#input_flight_start").val("");
$("#input_flight_end").val("");
MigrationResource.CurrentCondition.Type = Number(a);
MigrationResource.CurrentTime = (new Date).pattern("yyyy/MM/dd HH:mm");
updateSelectedTime();
updateDataSource();
MigrationResource.FileName = "";
MigrationResource.ChinaJson = {};
MigrationResource.CityJson = {};
MigrationResource.CurrentTrendData = {};
MigrationResource.Query(RedrawMap)
}
全局变量 MigrationResource 是一个非常重要的变量,里面包含了对地图的初始化、以及数据源的设定和数据获取。数据源的设定,是根据类型编号来的。比如11,12,13是航班数据。缺省的是人口迁徙数据,据主页君测试,有些数据比如代号为202的数据还没有。可能是将来要使用或者是废弃掉的。
switch (MigrationResource.CurrentCondition.Type) {
case 11:
case 12:
case 13:
b = "data/flight";
break;
case 21:
b = "data/huoche";
break;
case 22:
b = "data/huoche";
break;
case 101:
b = "data/plane";
break;
case 102:
b = "data/train";
break;
case 103:
b = "data/bus";
break;
case 201:
b = "data/scenicspot";
break;
case 202:
b = "data/business";
break;
default:
b = "data/migration"
}
取数据的函数如下,是一个很清楚的使用 JSZipUtils 取数据的例子,并且是支持多文件的,取回来的数据是JSON格式,进行了解析,直接保存在MigrationResource数组中了。
function GetData(a, c, d, b, f, k) {
JSZipUtils.getBinaryContent(DomainName + a + "/" + c, function(a, c) {
a && "function" == typeof k && k();
try {
var e = new JSZip;
e.load(c);
for (var p in e.files)
for (var l = 0; l < b.length; l++) {
var m = b[l];
if (0 <= p.indexOf(m.FileName)) {
d[m.Attribute] = JSON.parse(e.file(p).asText());
break
}
}
"function" == typeof f && f()
} catch (n) {
return!1
}
})
}
接下来就是地图的处理了,地图先画出基础中国地图,数据来自于 http://spotshot.baidu.com/getChinaLocDistribute.php 在经过 Map 封装的 echarts 图上绘制出中国地图框架。
RedrawMap 是整个图形绘制过程中的核心函数,根据当前要显示的地图的不同,进行不同的数据绘制。在这个过程中,进行了一些限定 ,比如限制地图缩放等。我们也可以在Chrome 的开发者工具中,调用函数,EnableMapZoom() 来调试一下,让当前地图支持缩放。
function RedrawMap() {
StopInterval(initMapKey);
StopInterval(redrawMapKey);
StopInterval(ipNumKey);
$(".logo-box").show();
map.removeOverlay(heatmapOverlay);
map.removeControl(top_left_navigation);
isPc && ($("#timeline").show(),
$(".btn_open_list").click());
$(".other-box").removeClass("other-box-1");
switch (MigrationResource.CurrentCondition.Type) {
case -2:
RemoveBaseMap();
DisableMapZoom();
RefreshMap();
isPc ? AddBaseLine(AddBasePoint, !0) : (AddBasePoint(),
$("#mainMap").hide(),
$("#mobileDiv").show().children("div").attr("class", "index-page-1"));
break;
case -1:
AddHotLine();
break;
case 0:
AddHotPoint(0);
break;
case 1:
AddHotPoint(1);
break;
case 2:
RemoveBaseMap();
RefreshMap();
AddBasePoint();
AddCityHotLine(MigrationResource.CurrentCondition.Province, MigrationResource.CurrentCondition.City);
break;
case 3:
RemoveBaseMap();
RefreshMap();
AddBasePoint();
AddCityHotLine(MigrationResource.CurrentCondition.Province, MigrationResource.CurrentCondition.City);
break;
case 11:
RemoveBaseMap();
var a = phoneSizeZoom;
isPc ? (a = flightSizeZoom, EnableMapZoom(a)) : DisableMapZoom();
AddFlight();
break;
case 12:
AddFlight();
break;
case 13:
AddFlight();
break;
case 21:
RemoveBaseMap();
DisableMapZoom();
AddTrain();
break;
case 22:
RemoveBaseMap();
DisableMapZoom();
AddTrain();
break;
case 101:
RemoveBaseMap();
DisableMapZoom();
AddAirport();
break;
case 102:
isPc ? (RemoveBaseMap(),
DisableMapZoom(),
AddRailwayStation()) : ($("#mainMap").hide(),
$("#mobileDiv").show().children("div").attr("class", "index-page-2"));
break;
case 103:
RemoveBaseMap();
DisableMapZoom();
AddBusStation();
break;
case 201:
RemoveBaseMap();
DisableMapZoom();
AddScenicspot();
break;
case 202:
RemoveBaseMap(),
DisableMapZoom(),
AddBusiness()
}
}
当然,要绘制出各种各样的图,得有相应的函数,比如机场是AddAirport() ,都通过 RedrawMap 来进行了调用。在人口迁徙中,迁入调用的是AddHotPoint(0) 迁出调用的是AddHotPoint(1);
function AddHotPoint(a) {
RemoveHotPoint();
RemoveHotLine();
var c = [], d = [], b = {},
b = 0 == a ? MigrationResource.ChinaJson.topCityIn : MigrationResource.ChinaJson.topCityOut;
$(b).each(function(a, b) {
var g = {};
g.name = GetCityName(b.name);
g.CityLocation = b.name;
g.value = a + 1;
c.push(g);
d.push(g)
});
AddMarkPoint(myChart, 1, c);
AddCityShowName(d)
}
文件的后半部分,是有关右边的列表的实现。其中比较比意思的地方是实现了对拼音搜索的支持。
var provincialCapital = {"安徽": "合肥", "福建": "福州", "甘肃": "兰州", "广东": "广州", "广西": "南宁", "贵州": "贵阳", "海南": "海口", "河北": "石家庄", "河南": "郑州", "黑龙江": "哈尔滨", "湖北": "武汉", "湖南": "长沙", "吉林": "长春", "江苏": "南京", "江西": "南昌", "辽宁": "沈阳", "内蒙古": "呼和浩特", "宁夏": "银川", "青海": "西宁", "山东": "济南", "山西": "太原", "陕西": "西安", "四川": "成都", "新疆": "乌鲁木齐", "西藏": "拉萨", "云南": "昆明", "浙江": "杭州", "香港": "", "重庆": "", "北京": "", "天津": "", "上海": "", "台湾": "台北", "澳门": ""},
citys = ["香港 xianggang xg".split(" "),
"香港 香港 xianggang xg xianggang xg".split(" "),
"重庆 chongqing cq".split(" "),
"重庆 重庆 chongqing cq chongqing cq".split(" "),
"北京 beijing bj".split(" "),
"北京 北京 beijing bj beijing bj".split(" "),
"天津 tianjin tj".split(" "),
"天津 天津 tianjin tj tianjin tj".split(" "),
"上海 shanghai sh".split(" "),
"上海 上海 shanghai sh shanghai sh".split(" "),
"澳门 aomen am".split(" "),
这也是一份值得保存的数据和值得学习的做法。
地图绘制就基本讲完了,需要注意的一点,经纬度和屏幕像素等的转换,最终是通过百度地图类库来实现的。具体的办法是,在mainIndex 中调用 封装好的 echarts 库,而 echarts 库,又调用 百度地图API的库,百度地图 API 是通过这个 URL 取到的。
http://api.map.baidu.com/getscript?v=2.0&ak=ZUONbpqGBsYGXNIYHicvbAbM&services=&t=20150213143539
有关列表DOM操作不做过多介绍,我们下面来总结一下迁徙的实现。
百度迁徙,在2014年春节时推出,2015年春节,也被新闻媒体大量地引用,其在大数据产品化方面,有非常值得借鉴的意义,从技术上讲,无论是数据绘制、数据从服务器端的获取,还是本地的一些处理手法,都有值得我们学习的地方,优才网的公众账号,通过连续的两篇文章,对其做一个简单的分析,希望大家有用。
我们在春节期间推出的这些技术文章,得到了大家的欢迎。在后面,我们还将继续为大家奉上原创的技术文章 。
如果觉得我们的文章不错,欢迎关注优才网公众账号,以及查看原文,了解优才网的更多产品。
- PHP技术文章
- PHP中session和cookie的区别
- php设计模式(一):简介及创建型模式
- php设计模式结构型模式
- Php设计模式(三):行为型模式
- 十款最出色的 PHP 安全开发库中文详细介绍
- 12个提问频率最高的PHP面试题
- PHP 语言需要避免的 10 大误区
- PHP 死锁问题分析
- 致PHP路上的“年轻人”
- PHP网站常见安全漏洞,及相应防范措施总结
- 各开源框架使用与设计总结(一)
- 数据库的本质、概念及其应用实践(二)
- PHP导出MySQL数据到Excel文件(fputcsv)
- PHP中14种排序算法评测
- 深入理解PHP原理之--echo的实现
- PHP性能分析相关的函数
- PHP 性能分析10则
- 10 位顶级 PHP 大师的开发原则
- 30条爆笑的程序员梗 PHP是最好的语言
- PHP底层的运行机制与原理
- PHP 性能分析与实验——性能的宏观分析
- PHP7 性能翻倍关键大揭露
- 鸟哥:写在PHP7发布之际一些话
- PHP与MySQL通讯那点事
- Php session内部执行流程的再次剖析
- 关于 PHP 中的 Class 的几点个人看法
- PHP Socket 编程过程详解
- PHP过往及现在及变革
- PHP吉祥物大象的由来
- PHP生成静态页面的方法
- 吊炸天的 PHP 7 ,你值得拥有!
- PHP开发中文件操作疑难问答
- MongoDB PHP Driver的连接处理解析
- PHP 杂谈《重构-改善既有代码的设计》之二 对象
- 在php中判断一个请求是ajax请求还是普通请求的方法
- 使用HAProxy、PHP、Redis和MySQL支撑10亿请求每周架构细节
- HTML、HTML5、XHTML、CSS、SQL、JavaScript、PHP、Web Services 是什么?
- 重构-改善既有代码的设计
- PHP场景中getshell防御思路分享
- 移动互联时代,你看看除了PHP你还会些什么
- 安卓系统上搭建本地php服务器环境
- PHP中常见的缓存技术!
- PHP里10个鲜为人知但却非常有用的函数
- 成为一名PHP专家其实并不难
- PHP 命令行?是的,您可以!
- PHP开发提高效率技巧
- PHP八大安全函数解析
- PHP实现四种基本排序算法
- PHP开发中的中文编码问题
- php.get.post
- php发送get、post请求的6种方法简明总结
- 中高级PHP开发者应该掌握哪些技术?
- 前端开发
- web前端知识体系大全
- 前端工程与性能优化(下)
- 前端工程与性能优化(上)
- 2016 年技术发展方向
- Web应用检查清单
- 如何成为一名优秀的web前端工程师
- 前端组件化开发实践
- 移动端H5页面高清多屏适配方案
- 2015前端框架何去何从
- 从前端看“百度迁徙”的技术实现(一)
- 从前端看“百度迁徙”的技术实现(二)
- 前端路上的旅行
- 大公司里怎样开发和部署前端代码?
- 5个经典的前端面试问题
- 前端工程师新手必读
- 手机淘宝前端的图片相关工作流程梳理
- 一个自动化的前端项目实现(附源码)
- 前端代码异常日志收集与监控
- 15年双11手淘前端技术总结 - H5性能最佳实践
- 深入理解javascript原型和闭包系列
- 一切都是对象
- 函数和对象的关系
- prototype原型
- 隐式原型
- instanceof
- 继承
- 原型的灵活性
- 简述【执行上下文】上
- 简述【执行上下文】下
- this
- 执行上下文栈
- 简介【作用域】
- 【作用域】和【上下文环境】
- 从【自由变量】到【作用域链】
- 闭包
- 完结
- 补充:上下文环境和作用域的关系
- Linux私房菜