首先,实现地图下钻有多种方式,比如:echarts、d3.js等,鉴于水平有限,我基于echarts实现。废话少说直接上核心代码:
tools.js:
(1)实现了一些校验,比如非空、日期转化等;
(2)配置地图业务数据加载路径;
/* * 2018/5/30 *作者:剑雪封喉 *功能: 地图工具类(校验数据) * example:校验数据的写法if (Tools.isDataValid(data))(其中data是所要校验的数据) */ var httpUrl = { mapUrl: "china.json",//加载地图数据初始url // mapUrl: "http://localhost:8080/Open1111/article/getUserInfo.do",//加载地图数据初始url(测试接口) // name: 'shandong',//加载省的汉语拼音 // map_name: '山东'//加载省的汉字 name: '',//加载省的汉语拼音 map_name: ''//加载省的汉字 } //随机获取100种颜色用于数据展示 function getColor() { return '#' + (function (color) { return (color += '0123456789abcdef'[Math.floor(Math.random() * 16)]) && (color.length == 6) ? color : arguments.callee(color); })('') }; var Tools = { /** * 验证数据合法性 */ isDataValid: function (data) { if (data != null && data != "" && data != "undefined" && data != "null") { return true; } else { return false; } }, /* * 2018/6/8 *作者:剑雪封喉 *功能: 随机获取100种颜色 */ getMapColor: function () { var colorList = []; for (var i = 0; i < 100; i++) { colorList.push(getColor()); } return colorList; }, /** * 验证手机号码 * @param pPhone * @returns {*} */ checkPhone: function (pPhone) { pPhone = pPhone.replace(" ", ""); if (!this.isDataValid(pPhone) || pPhone <= 0) { return ("手机号码不能为空!"); } else { var phone = pPhone; // if(pPhone.indexOf("+86")>=0){ // phone =pPhone.split("+86")[1]; // } if (phone.length != 11) { return ("请输入11位手机号码!"); } else if (!(/^1[3|4|5|7|8][0-9]\d{8}$/.test(phone))) { return ("手机号码格式不正确"); } else { return true; } } }, /** * 列表中简介最多显示的字数 * @param data * @returns {*} */ subNewsInfo: function (data, count) { if (!count || count == null || count == "" || count == "undefined" || count == undefined) { count = 20; } if (!this.isDataValid(data)) { return ""; } var value = data.replace(/<\.+?>/g, ""); //去除html标签,只算汉字长度 if (value.length > 20) { return value.substr(0, 20) + "..."; } else { return value; } }, /** * 手机号码隐藏显示 * @param pPhone * @returns {*} */ showUserPhone: function (phone) { var newphone = ""; if (phone.length == 11) { newphone += phone.substr(0, 3); newphone += "****"; newphone += phone.substr(7, 11) return newphone; } return phone }, /** * 处理html转义字符 */ escape2Html: function (str) { var arrEntities = { 'lt': '<', 'gt': '>', 'nbsp': ' ', 'amp': '&', 'quot': '"' }; return str.replace(/&(lt|gt|nbsp|amp|quot);/ig, function (all, t) { return arrEntities[t]; }); }, /** * base64编码 * @param str * @returns {string} */ base64encode: function (str) { var out, i, len, base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; var c1, c2, c3; len = str.length; i = 0; out = ""; while (i < len) { c1 = str.charCodeAt(i++) & 0xff; if (i == len) { out += base64EncodeChars.charAt(c1 >> 2); out += base64EncodeChars.charAt((c1 & 0x3) << 4); out += "=="; break; } c2 = str.charCodeAt(i++); if (i == len) { out += base64EncodeChars.charAt(c1 >> 2); out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4)); out += base64EncodeChars.charAt((c2 & 0xF) << 2); out += "="; break; } c3 = str.charCodeAt(i++); out += base64EncodeChars.charAt(c1 >> 2); out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4)); out += base64EncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6)); out += base64EncodeChars.charAt(c3 & 0x3F); } return out; }, /** * base64加密解密 全**/ Base64: function () { // private property _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; // public method for encoding this.encode = function (input) { var output = ""; var chr1, chr2, chr3, enc1, enc2, enc3, enc4; var i = 0; input = _utf8_encode(input); while (i < input.length) { chr1 = input.charCodeAt(i++); chr2 = input.charCodeAt(i++); chr3 = input.charCodeAt(i++); enc1 = chr1 >> 2; enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); enc4 = chr3 & 63; if (isNaN(chr2)) { enc3 = enc4 = 64; } else if (isNaN(chr3)) { enc4 = 64; } output = output + _keyStr.charAt(enc1) + _keyStr.charAt(enc2) + _keyStr.charAt(enc3) + _keyStr.charAt(enc4); } return output; }, // public method for decoding this.decode = function (input) { var output = ""; var chr1, chr2, chr3; var enc1, enc2, enc3, enc4; var i = 0; input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); while (i < input.length) { enc1 = _keyStr.indexOf(input.charAt(i++)); enc2 = _keyStr.indexOf(input.charAt(i++)); enc3 = _keyStr.indexOf(input.charAt(i++)); enc4 = _keyStr.indexOf(input.charAt(i++)); chr1 = (enc1 << 2) | (enc2 >> 4); chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); chr3 = ((enc3 & 3) << 6) | enc4; output = output + String.fromCharCode(chr1); if (enc3 != 64) { output = output + String.fromCharCode(chr2); } if (enc4 != 64) { output = output + String.fromCharCode(chr3); } } output = _utf8_decode(output); return output; }, // private method for UTF-8 encoding _utf8_encode = function (string) { string = string.replace(/\r\n/g, "\n"); var utftext = ""; for (var n = 0; n < string.length; n++) { var c = string.charCodeAt(n); if (c < 128) { utftext += String.fromCharCode(c); } else if ((c > 127) && (c < 2048)) { utftext += String.fromCharCode((c >> 6) | 192); utftext += String.fromCharCode((c & 63) | 128); } else { utftext += String.fromCharCode((c >> 12) | 224); utftext += String.fromCharCode(((c >> 6) & 63) | 128); utftext += String.fromCharCode((c & 63) | 128); } } return utftext; }, // private method for UTF-8 decoding _utf8_decode = function (utftext) { var string = ""; var i = 0; var c = c1 = c2 = 0; while (i < utftext.length) { c = utftext.charCodeAt(i); if (c < 128) { string += String.fromCharCode(c); i++; } else if ((c > 191) && (c < 224)) { c2 = utftext.charCodeAt(i + 1); string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); i += 2; } else { c2 = utftext.charCodeAt(i + 1); c3 = utftext.charCodeAt(i + 2); string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); i += 3; } } return string; } }, /* ** 中文乱码 */ enCodeChinese: function (json) { for (var x in json) { if (/^[\u4e00-\u9fa5]+$/i.test(json[x])) { json[x] = encodeURIComponent(json[x]); } } return json; }, /** * 得到图片的base64编码**/ getBase64Image: function (img) { var canvas = document.createElement("canvas"); canvas.width = img.width; canvas.height = img.height; img.crossOrigin = '*'; var ctx = canvas.getContext("2d"); ctx.drawImage(img, 0, 0, img.width, img.height); var ext = img.src.substring(img.src.lastIndexOf(".") + 1).toLowerCase(); var dataURL = canvas.toDataURL("image/" + ext); return dataURL; }, /* ** token失效的方法 */ tokenMiss: function (url) { layer.alert('登录失效,请重新登录', function () { top.location.href = url; return false; }); }, /* ** 获取token */ getToken: function (callback, url) { var value = localStorage.token; if (this.isDataValid(value)) { callback(value); } else { this.tokenMiss(url); } }, /** * 比较时间 * @param beginTime * @param endTime * @returns {boolean} */ comptime: function (beginTime, endTime) { if (beginTime.length == 19 && endTime.length == 19) { //兼容火狐 var beginTimes = beginTime.substring(0, 10) + 'T' + beginTime.substring(11, 19); var endTimes = endTime.substring(0, 10) + 'T' + endTime.substring(11, 19); } else { var beginTimes = beginTime; var endTimes = endTime; } if (new Date(endTimes) >= new Date(beginTimes)) { return true; } else { return false; } }, // 获取url上的参数的方法,传什么参数进去就获得对应的参数值。 getQueryString: function (name) { var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i'); var r = window.location.search.substr(1).match(reg); if (r != null) { return unescape(r[2]); } return null; }, //是否为负整数 isNegativeNum: function (s) { var re = /^[1-9]\d*|0$/; return re.test(s) }, /** *该函数为DWZ解析a链接中?后带的参数 **/ parseUrl: function () { // 该函数为解析a链接中?后带的参数 var url = navTab._getTabs().eq(navTab._currentIndex).attr("url"); var theRequest = new Object(); if (url.indexOf("?") != -1) { var str = url.substr(url.indexOf("?") + 1); strs = str.split("&"); for (var i = 0; i < strs.length; i++) { theRequest[strs[i].split("=")[0]] = unescape(strs[i].split("=")[1]); } } return theRequest; }, /** *该函数为解析a链接中?后带的参数,dialog是用的get请求 **/ parseUrl_dialog: function () { //dialog是用的get请求 // 该函数为解析a链接中?后带的参数 var url = $.pdialog.getCurrent().data("url"); // console.log(url) var theRequest = new Object(); if (url.indexOf("?") != -1) { var str = url.substr(url.indexOf("?") + 1); strs = str.split("&"); for (var i = 0; i < strs.length; i++) { theRequest[strs[i].split("=")[0]] = unescape(strs[i].split("=")[1]); } } return theRequest; } }; /** * 关闭console * @type {boolean} */ var isDebug = true; if (!isDebug) { console.log = function (text) { } } /** * 所有菜单 */ var appauth = localStorage.appauth; if (Tools.isDataValid(appauth)) { var appauth = JSON.parse(localStorage.appauth); //权限返回菜单 } var token = localStorage.token; //token var adminUname = localStorage.adminUname; //用户名
map_config.js:
加载地图的主要逻辑
/* * 2018/5/31 * 作者:剑雪封喉 * 功能: 加载地图主要实现逻辑 */ var userMap = angular.module('userMap', ['ng', 'services']); userMap.controller('mapCtr', function ($scope, httpRequest) { // 渲染地图,获取加载地图的配置(汉语拼音或行政区域代码中的一项+汉字名称) var name = httpUrl.name; var map_name = httpUrl.map_name; /* * 2018/6/13 * 作者:剑雪封喉 * 功能: 加载各省地图echarts配置数据,当name&&map_name都不配置时,默认加载全国地图 */ $scope.loadProvince = function (name, map_name) { console.log("loadProvince---step1---"); if (Tools.isDataValid(name) && Tools.isDataValid(map_name)) { $.get('province/' + name + '.json', function (geoJson) { // 注册地图 echarts.registerMap(name, geoJson); //绘制地图 renderMap('city', name, map_name, ''); }); } else { renderMap('province', 'china', '', ''); } } //读取并加载地图业务数据 $scope.renderMapView = function (accuracy, map, map_name, color) { console.log("renderMapView---step3---" + accuracy); /** * 地图处理逻辑 * 请求数据 * 1.给每个区域赋值颜色值 * 2.设置区域标识 * 3.颜色值等级字典 * 4.渲染图层 */ $scope.baseUrl = httpUrl.mapUrl; mapChart.showLoading(); $scope.dataArr = []; //根据map_name传递具体的json数据 if (Tools.isDataValid(map)) { $scope.baseUrl = map + "_data.json"; // $scope.baseUrl = $scope.baseUrl + "?map=" + map;//测试接口 } console.log("$scope.baseUrl===" + $scope.baseUrl); httpRequest.get($scope.baseUrl, function (data) { console.log(data); //1.给每个区域赋值注册/登录人数,用作注册/登录人数对比,根据 for (var i = 0; i < data.subitem.length; i++) { var k = data.subitem[i][accuracy].toString(); $scope.dataArr.push({ name: k, value: data.subitem[i].num, drkt: data.subitem[i].drkt, drlr: data.subitem[i].drlr, zlr: data.subitem[i].zlr, cityType: data.subitem[i].cityType, }); } mapOptions.series[0].data = $scope.dataArr; //2.设置区域标识 var nameMap = {} for (var province in provinces) { for (var j = 0; j < $scope.dataArr.length; j++) { if ($scope.dataArr[j].name.indexOf(province) != -1) { nameMap[province] = $scope.dataArr[j].name; $scope.dataArr[j]['pinyin'] = provinces[province]; } } } mapOptions.series[0].nameMap = nameMap; //3.颜色值等级字典 mapOptions.visualMap.pieces = []; for (var j = 0; j < data.count.length; j++) { var countObj = { 'color': data.count[j].color, 'max': data.count[j].max - 0, 'min': data.count[j].min - 0, }; mapOptions.visualMap.pieces.push(countObj); } //4.渲染图层 mapChart.setOption(mapOptions); mapChart.hideLoading(); }, function (err) { layer.msg(err, {icon: 0}); }) } //地图预加载 $scope.loadProvince(name, map_name); /* * 2018/6/5 * 作者:剑雪封喉 * 功能:加载省地图 */ var level = 0;//0代表省;1代表市;2代表不可以点击 var cityDataLoad = 1;//1代表加载市区;0代表不加载市 // 地图点击事件 mapChart.on('click', function (data) {//获取的data数据指的是点击的地区数据 // console.log("112行data.data=="+data.data); if (Tools.isDataValid(data.data) && Tools.isDataValid(data.data.value)) { cityDataLoad = 1; var cityType;//国1省2市3县4 //获取cityType:国1省2市3县4 if (Tools.isDataValid(data.data)) { cityType = data.data.cityType; } //若cityType为空,默认为市3 if (!Tools.isDataValid(cityType)) { cityType = "3"; } //当cityType为市3时对应的将level赋值为level = 1;保持其一致性 if (cityType == "3") { level = 1; } // console.log("level--" + level); // console.log("cityType--" + cityType); //获取加载地图名称等信息 if (Tools.isDataValid(data.data)) { //当加载为level = 0:即为省时,赋值获取元素 if (level == 0) { //省级 $scope.map_name = data.data.name; $scope.name = data.data.pinyin; } else if (level == 1) { //当加载为level = 1:即市时,赋值获取元素 $scope.map_name = data.data.name; $scope.name = cityMap[$scope.map_name]; } else if (level == 2) { //若已经到达县级别,在默认点击的的情况下,返回省地图(当开始是省地图时返回省地图,当开始是全国地图时返回全国地图) if (cityType == "4" && Tools.isDataValid(name) && Tools.isDataValid(map_name)) { $scope.loadProvince(name, map_name); } else { $scope.back(); return; } } } else if (!Tools.isDataValid(data.data) && Tools.isDataValid(name) && Tools.isDataValid(map_name)) { $scope.loadProvince(name, map_name); } else { showInfo(); } //根据获取的地图信息加载地图固定json if (level == 0) { if (Tools.isDataValid(data.data) && Tools.isDataValid($scope.name)) { $.get('province/' + $scope.name + '.json', function (geoJson) { // 注册地图 echarts.registerMap($scope.name, geoJson); //绘制地图 renderMap('city', $scope.name, $scope.map_name, ''); }); level = 1; cityDataLoad = 0; //如果点击的是直辖市和特别行政区-只有二级地图,没有三级地图 if (special_provinces.indexOf($scope.name) >= 0) { $scope.loadProvince(name, map_name); level = 2; } } else { showInfo(); } } if (level == 1 && cityDataLoad == 1) { if (Tools.isDataValid(data.data) && Tools.isDataValid($scope.name) && Tools.isDataValid(data.data.value)) { //获取市级地图json $.get('area/' + $scope.name + '.json', function (geoJson) { // 注册地图 echarts.registerMap($scope.name, geoJson); //绘制地图 renderMap('city', $scope.name, $scope.map_name, ''); }); level = 2; } } } else { showInfo(); } }); $scope.back = function () { level = 0; renderMap('province', 'china', '', ''); } function renderMap(accuracy, map, map_name, data, Latitude) { console.log("renderMap---step2---"); // mapOptions.title.subtext = map_name;配合map_draw.js中的10-11行使用 mapOptions.series[0] = { name: '地图图例', type: 'map',//type决定图表类别 mapType: map, center: null, roam: false,//是否允许移动或者缩放 top: 50,//与geo保持10的差距,显示出后面的阴影 zoom: 1.1, left: 240, itemStyle: { normal: { label: { show: true, textStyle: { color: '#171313', } } }, emphasis: {// 也是选中样式 label: { show: true, } } }, }, //阴影配置 mapOptions.geo[0] = { map: map,//此处需要传递具体展开的地图 top: 50, left: 250, zoom: 1.1, label: { emphasis: { show: true } }, itemStyle: { normal: { areaColor: '#D8B148', borderColor: '#fff' }, emphasis: { areaColor: '#D8B148', borderColor: '#fff' } } } $scope.renderMapView(accuracy, map, map_name, data); } /* * 2018/6/2 *作者:剑雪封喉 *功能:没有地区业务数据时提示信息 */ function showInfo() { layer.open({ title: '友好提示', type: 1, content: '无此地区数据,请重新选择!', btn: '确认', btnAlign: 'c', //按钮居中 shade: 0,//不显示遮罩 yes: function () { layer.closeAll(); if (Tools.isDataValid(name) && Tools.isDataValid(map_name)) { $scope.loadProvince(name, map_name); } else { $scope.back(); } return; } }); } });
map_draw.js:
绘制地图的主要配置
/* * 2018/5/26 *作者:剑雪封喉 *功能:地图标题、悬浮框、阴影、系列列表等配置实现 */ var mapChart = echarts.init(document.getElementById('map')); var mapOptions = { backgroundColor: '#000000',//地图的背景颜色 title: { // text: '中国地图', // subtext: 'china', left: 'center', textStyle: { color: '#fff', fontSize: 16, fontWeight: 'normal', fontFamily: "Microsoft YaHei" }, subtextStyle: { color: '#ccc', fontSize: 13, fontWeight: 'normal', fontFamily: "Microsoft YaHei" } }, //提示框的触发方式 tooltip: { trigger: 'item', formatter: function (params) { var res = params.name + '
'; var myseries = mapOptions.series[0].data; if (Tools.isDataValid(myseries)) { for (var i = 0; i < myseries.length; i++) { if (myseries[i].name == params.name) { res += "汇总:" + myseries[i].value + "
柜台:" + myseries[i].zlr + "
查询机:" + myseries[i].drkt + "小贷:" + myseries[i].drlr } } return res; } }, // tooltip主体内容显示策略,只需tooltip触发事件或显示axisPointer而不需要显示内容时可配置该项为false showContent: true, // 显示延迟,添加显示延迟可以避免频繁切换,特别是在详情内容需要异步获取的场景,单位ms showDelay: 0, // 隐藏延迟,单位ms hideDelay: 0, // 动画变换时长,单位s,如果你希望tooltip的跟随实时响应,showDelay设置为0是关键,同时transitionDuration设0也会有交互体验上的差别。 transitionDuration: 0, // 鼠标是否可进入详情气泡中,默认为false,如需详情内交互,如添加链接,按钮,可设置为true。 //enterable: false, // 提示背景颜色,默认为透明度为0.7的黑色 backgroundColor: '#C7C4BE', borderColor: '#987ECA', borderWidth: 1, // 提示内边距,单位px,默认各方向内边距为5,接受数组分别设定上右下左边距,同css padding: 15, // 提示边框圆角,单位px,默认为4 borderRadius: 10, // 提示边框线宽,单位px,默认为0(无边框) borderWidth: 2, // 文本样式,默认为白色字体 textStyle: { // 颜色 color: '#333333', // 水平对齐方式,可选为:'left' | 'right' | 'center' align: 'left', // 垂直对齐方式,可选为:'top' | 'bottom' | 'middle' baseline: 'bottom', // 字体系列 fontFamily: 'Arial, Microsoft YaHei, sans-serif', // 字号 ,单位px // fontSize: 20, // 样式,可选为:'normal' | 'italic' | 'oblique' fontStyle: 'normal', // 粗细,可选为:'normal' | 'bold' | 'bolder' | 'lighter' | 100 | 200 |... | 900 fontWeight: 'normal' }, }, //图例展示 legend: { show: true, //是否显示 zlevel: 0, //所属图形的Canvas分层,zlevel 大的 Canvas 会放在 zlevel 小的 Canvas 的上面 z: 2, //所属组件的z分层,z值小的图形会被z值大的图形覆盖 left: "left", //组件离容器左侧的距离,'left', 'center', 'right','20%' top: "bottom", //组件离容器上侧的距离,'top', 'middle', 'bottom','20%' right: "auto", //组件离容器右侧的距离,'20%' bottom: "auto", //组件离容器下侧的距离,'20%' width: "auto", //图例宽度 height: "auto", //图例高度 orient: "horizontal", //图例排列方向 align: "auto", //图例标记和文本的对齐,left,right padding: 5, //图例内边距,单位px 5 [5, 10] [5,10,5,10] itemGap: 10, //图例每项之间的间隔 itemWidth: 34, //图例标记的图形宽度 itemHeight: 100, //图例标记的图形高度 // 图例背景颜色,默认透明 // backgroundColor: '#ffffff', // 图例边框颜色 borderColor: '#000000', // 图例边框线宽,单位px,默认为0(无边框) borderWidth: 2, // 图例边框圆角 borderRadius: 0, formatter: function (name) { //用来格式化图例文本,支持字符串模板和回调函数两种形式。模板变量为图例名称 {name} var legendInfo = '信用报告查询异常' + '\n' + '\n' + '\n' + '信用报告查询正常' + '\n' + '\n' + '\n' + '查询机设备故障'; return legendInfo;// '信用报告查询异常'+; }, selectedMode: "single", //图例选择的模式,true开启,false关闭,single单选,multiple多选 inactiveColor: "#ccc", //图例关闭时的颜色 textStyle: { // 颜色 color: '#ffffff', // 水平对齐方式,可选为:'left' | 'right' | 'center' align: 'left', // 垂直对齐方式,可选为:'top' | 'bottom' | 'middle' baseline: 'bottom', // 字体系列 fontFamily: 'Arial, Microsoft YaHei, sans-serif', // 字号 ,单位px fontSize: 12, // 样式,可选为:'normal' | 'italic' | 'oblique' fontStyle: 'normal', // 粗细,可选为:'normal' | 'bold' | 'bolder' | 'lighter' | 100 | 200 |... | 900 fontWeight: 'normal' }, data: [{ name: '地图图例', icon: 'image://images/map_legend.jpg', // name: '信用报告查询正常',icon: 'image://images/icon.jpg', // name: '查询机设备故障',icon: 'image://images/icon.jpg' }], //series中根据名称区分 }, //分段视觉映射组件 visualMap: { type: 'piecewise', show: false, left: 'left', top: '42%', textStyle: { color: '#fff' }, seriesIndex: 0, pieces: [] }, //阴影配置 geo: [], //系列列表 series: [], animationDuration: 1000, animationEasing: 'cubicOut', animationDurationUpdate: 1000 };