Commit efae28850451effe9c868003ccf0caa86ea6c3af
1 parent
820760db
1.线调主页点击模拟图切换(只显示上行或下行)
Showing
2 changed files
with
508 additions
and
488 deletions
src/main/resources/static/real_control_v2/fragments/home/layout.html
| 1 | -<div> | |
| 2 | - <!-- home tab template --> | |
| 3 | - <script id="home-layout-tab-temp" type="text/html"> | |
| 4 | - <ul id="home-main-content" class="uk-switcher"> | |
| 5 | - {{each tabs as t i}} | |
| 6 | - <li {{if i==0}}class="uk-active"{{/if}} >{{t}}</li> | |
| 7 | - {{/each}} | |
| 8 | - </ul> | |
| 9 | - | |
| 10 | - <div class="home-panel-footer"> | |
| 11 | - <ul class="uk-subnav uk-subnav-pill" data-uk-switcher="{connect:'#home-main-content', swiping: false}"> | |
| 12 | - {{each tabs as t i}} | |
| 13 | - <li {{if i==0}}class="uk-active"{{/if}}><a> {{t}}</a></li> | |
| 14 | - {{/each}} | |
| 15 | - </ul> | |
| 16 | - <div class="home-rb-explain-icon "> | |
| 17 | - <a href="/real_control_v2/alone_page/home/home_wrap.html" target="_blank"> | |
| 18 | - <i class="uk-icon-send-o home_alone_page uk-icon-hover" ></i> | |
| 19 | - </a> | |
| 20 | - | |
| 21 | - <i class="uk-icon-question-circle uk-icon-hover"></i> | |
| 22 | - </div> | |
| 23 | - </div> | |
| 24 | - </script> | |
| 25 | - | |
| 26 | - <!-- home line template --> | |
| 27 | - <script id="home-layout-line-temp" type="text/html"> | |
| 28 | - {{each list as line i}} | |
| 29 | - <div class="uk-grid home-line-card" data-line-code="{{line.lineCode}}"> | |
| 30 | - <div class="uk-width-medium-1-5 data-wrap up" id="home_{{line.lineCode}}_0"></div> | |
| 31 | - <div class="uk-width-medium-3-5 svg-chart-wrap"> | |
| 32 | - <div class="home-svg-edit-icon" data-line-code="{{line.lineCode}}"></div> | |
| 33 | - <div class="top-center-big-text"> | |
| 34 | - {{line.name}} | |
| 35 | - </div> | |
| 36 | - </div> | |
| 37 | - <div class="uk-width-medium-1-5 data-wrap down" id="home_{{line.lineCode}}_1"></div> | |
| 38 | - </div> | |
| 39 | - {{/each}} | |
| 40 | - </script> | |
| 41 | - | |
| 42 | - <script id="home-rb-explain-help-temp" type="text/html"> | |
| 43 | - <ul class="uk-list"> | |
| 44 | - <li>场外车辆,距离线路超过100米即为越界</li> | |
| 45 | - <li>超速以线路标准限速为准,为空则默认60</li> | |
| 46 | - <li>有任务时,连续2分钟无信号即为掉线</li> | |
| 47 | - <li>无任务时,连续10分钟无信号则已离线</li> | |
| 48 | - </ul> | |
| 49 | - </script> | |
| 50 | -</div> | |
| 1 | +<div> | |
| 2 | + <!-- home tab template --> | |
| 3 | + <script id="home-layout-tab-temp" type="text/html"> | |
| 4 | + <ul id="home-main-content" class="uk-switcher"> | |
| 5 | + {{each tabs as t i}} | |
| 6 | + <li {{if i==0}}class="uk-active"{{/if}} >{{t}}</li> | |
| 7 | + {{/each}} | |
| 8 | + </ul> | |
| 9 | + | |
| 10 | + <div class="home-panel-footer"> | |
| 11 | + <ul class="uk-subnav uk-subnav-pill" data-uk-switcher="{connect:'#home-main-content', swiping: false}"> | |
| 12 | + {{each tabs as t i}} | |
| 13 | + <li {{if i==0}}class="uk-active"{{/if}}><a> {{t}}</a></li> | |
| 14 | + {{/each}} | |
| 15 | + </ul> | |
| 16 | + <div class="home-rb-explain-icon "> | |
| 17 | + <a href="/real_control_v2/alone_page/home/home_wrap.html" target="_blank"> | |
| 18 | + <i class="uk-icon-send-o home_alone_page uk-icon-hover" ></i> | |
| 19 | + </a> | |
| 20 | + | |
| 21 | + <i class="uk-icon-question-circle uk-icon-hover"></i> | |
| 22 | + </div> | |
| 23 | + </div> | |
| 24 | + </script> | |
| 25 | + | |
| 26 | + <!-- home line template --> | |
| 27 | + <script id="home-layout-line-temp" type="text/html"> | |
| 28 | + {{each list as line i}} | |
| 29 | + <div class="uk-grid home-line-card" data-line-code="{{line.lineCode}}"> | |
| 30 | + <div class="uk-width-medium-1-5 data-wrap up" id="home_{{line.lineCode}}_0"></div> | |
| 31 | + <div class="uk-width-medium-3-5 svg-chart-wrap" id="svg_title_{{line.lineCode}}"> | |
| 32 | + <div class="home-svg-edit-icon" data-line-code="{{line.lineCode}}"></div> | |
| 33 | + <div class="top-center-big-text"> | |
| 34 | + {{line.name}} | |
| 35 | + </div> | |
| 36 | + </div> | |
| 37 | + <div class="uk-width-medium-1-5 data-wrap down" id="home_{{line.lineCode}}_1"></div> | |
| 38 | + </div> | |
| 39 | + {{/each}} | |
| 40 | + </script> | |
| 41 | + | |
| 42 | + <script id="home-rb-explain-help-temp" type="text/html"> | |
| 43 | + <ul class="uk-list"> | |
| 44 | + <li>场外车辆,距离线路超过100米即为越界</li> | |
| 45 | + <li>超速以线路标准限速为准,为空则默认60</li> | |
| 46 | + <li>有任务时,连续2分钟无信号即为掉线</li> | |
| 47 | + <li>无任务时,连续10分钟无信号则已离线</li> | |
| 48 | + </ul> | |
| 49 | + </script> | |
| 50 | +</div> | ... | ... |
src/main/resources/static/real_control_v2/js/utils/svg_chart.js
| 1 | -/* 线路模拟图 */ | |
| 2 | - | |
| 3 | -var gb_svg_chart = (function () { | |
| 4 | - | |
| 5 | - //chart height | |
| 6 | - var chart_height = 122; | |
| 7 | - //left right padding | |
| 8 | - var x_padd = 30; | |
| 9 | - //上空白部分 | |
| 10 | - var y_top_padd = 4; | |
| 11 | - //text max size | |
| 12 | - var t_max_size = 7; | |
| 13 | - //svg namespace | |
| 14 | - var svgns = 'http://www.w3.org/2000/svg'; | |
| 15 | - var wraps={}; | |
| 16 | - //根据wrap的class区分是home还是电子路单界面 | |
| 17 | - var suffixs={ | |
| 18 | - 'uk-width-medium-3-5 svg-chart-wrap': 'home', | |
| 19 | - 'svg-wrap': 'line' | |
| 20 | - } | |
| 21 | - | |
| 22 | - //站点间隔 线路编码 -> space | |
| 23 | - var circle_spaces={}; | |
| 24 | - //var circle_space; | |
| 25 | - //是否启用动画 | |
| 26 | - var animation = true; | |
| 27 | - | |
| 28 | - var calc_text_y = function (t) { | |
| 29 | - return (chart_height - (chart_height / t_max_size * t.length)) / 2 + 5; | |
| 30 | - }, | |
| 31 | - cat_text = function (t) { | |
| 32 | - if(!t) | |
| 33 | - return 0; | |
| 34 | - return t.length > t_max_size ? t.substr(0, t_max_size) : t; | |
| 35 | - }, | |
| 36 | - get_width = function (wrap) { | |
| 37 | - return wrap.actual('outerWidth'); | |
| 38 | - }, | |
| 39 | - get_height = function (wrap) { | |
| 40 | - var h = wrap.actual('outerHeight'); | |
| 41 | - //隐藏元素取最外层的高度 | |
| 42 | - return h < 20 ? wrap.parent().actual('outerHeight') - 2 : h; | |
| 43 | - }; | |
| 44 | - | |
| 45 | - /** | |
| 46 | - * 绘制线路模拟图 | |
| 47 | - * @param lineCode 线路编码 | |
| 48 | - * @param wrap dom容器 | |
| 49 | - * @param enableAttr 是否启用配置 | |
| 50 | - */ | |
| 51 | - var draw_line = function (lineCode, wrap, enableAttr) { | |
| 52 | - var wrapId = lineCode+'_'+suffixs[wrap.attr('class')]; | |
| 53 | - wraps[wrapId]=wrap; | |
| 54 | - //环线 | |
| 55 | - var loopLine = gb_data_basic.isLoopLine(lineCode); | |
| 56 | - | |
| 57 | - var routes = gb_data_basic.stationRoutes(lineCode); | |
| 58 | - if(!routes) | |
| 59 | - return; | |
| 60 | - var data = gb_svg_data_convert.mergeRoute(JSON.parse(JSON.stringify(routes)), enableAttr, lineCode, loopLine), | |
| 61 | - len = data.length; | |
| 62 | - | |
| 63 | - var w = get_width(wrap), | |
| 64 | - h = parseInt(get_height(wrap)) | |
| 65 | - //x scale | |
| 66 | - , | |
| 67 | - xScale = d3.scale.linear().range([x_padd, w - x_padd]).domain([0, len - 1]), | |
| 68 | - cx = function (d, i) { | |
| 69 | - return xScale(i); | |
| 70 | - }, | |
| 71 | - cy = function () { | |
| 72 | - return (h - chart_height) / 2 + y_top_padd; | |
| 73 | - }, | |
| 74 | - ty = function (d) { | |
| 75 | - return cy() + calc_text_y(cat_text(d.name[0])); | |
| 76 | - } | |
| 77 | - //line generator | |
| 78 | - , | |
| 79 | - upLine = d3.svg.line().x(xScale).y(cy), | |
| 80 | - downLine = d3.svg.line().x(xScale).y(function () { | |
| 81 | - return cy() + chart_height | |
| 82 | - }), | |
| 83 | - multi_text = function (d, i, that) { | |
| 84 | - | |
| 85 | - var dText = document.createElementNS(svgns, 'text'), | |
| 86 | - t = cat_text(d.name[1]); | |
| 87 | - $(dText).attr('class', 'station_text down') | |
| 88 | - .attr('x', cx(d, i) + 8).attr('y', cy() + calc_text_y(t)) | |
| 89 | - .attr('title', d.name[1]) | |
| 90 | - .text(t); | |
| 91 | - | |
| 92 | - $(that).after(dText); | |
| 93 | - return cx(d, i) - 8; | |
| 94 | - }; | |
| 95 | - | |
| 96 | - circle_spaces[wrapId] = (w - (x_padd * 2)) / len; | |
| 97 | - //add svg dom | |
| 98 | - var svg = d3.select(wrap[0]).append('svg') | |
| 99 | - .classed({ | |
| 100 | - 'line-chart': true | |
| 101 | - }).attr('data-code', lineCode) | |
| 102 | - .attr('onselectstart', 'return false') | |
| 103 | - .style('height', h); | |
| 104 | - | |
| 105 | - //add item g | |
| 106 | - var items = svg.selectAll('g.item').data(data) | |
| 107 | - .enter().append('g').classed({ | |
| 108 | - 'item': true | |
| 109 | - }); | |
| 110 | - | |
| 111 | - //up station link path | |
| 112 | - var p_clzz = { | |
| 113 | - 'station_link': true | |
| 114 | - }; | |
| 115 | - items.append('path').classed(p_clzz) | |
| 116 | - .attr('d', function (d, i) { | |
| 117 | - return i < len - 1 ? upLine([i, i + 1]) : ''; | |
| 118 | - }); | |
| 119 | - | |
| 120 | - //down station link path | |
| 121 | - p_clzz.down = true; | |
| 122 | - p_clzz.loop_line = loopLine; | |
| 123 | - items.append('path').classed(p_clzz) | |
| 124 | - .attr('d', function (d, i) { | |
| 125 | - return i < len - 1 ? downLine([i, i + 1]) : ''; | |
| 126 | - }); | |
| 127 | - | |
| 128 | - //up circle | |
| 129 | - var c_clzz = { | |
| 130 | - 'station_circle': true | |
| 131 | - }; | |
| 132 | - items.select(function (d) { | |
| 133 | - return d.type != 1 ? this : null; | |
| 134 | - }) | |
| 135 | - .append('circle').classed(c_clzz) | |
| 136 | - .attr('cx', cx) | |
| 137 | - .attr('cy', cy) | |
| 138 | - .attr('data-id', function (d) { | |
| 139 | - return d.id[0]; | |
| 140 | - }); | |
| 141 | - | |
| 142 | - //down circle | |
| 143 | - c_clzz.down = true; | |
| 144 | - items.select(function (d) { | |
| 145 | - return d.type != 0 ? this : null; | |
| 146 | - }) | |
| 147 | - .append('circle').classed(c_clzz) | |
| 148 | - .attr('cx', cx) | |
| 149 | - .attr('cy', function (d, i) { | |
| 150 | - return cy(d, i) + chart_height; | |
| 151 | - }) | |
| 152 | - .attr('data-id', function (d) { | |
| 153 | - return d.type == 1 ? d.id[0] : d.id[1]; | |
| 154 | - }); | |
| 155 | - | |
| 156 | - //station name text | |
| 157 | - items.append('text').classed({ | |
| 158 | - 'station_text': true, | |
| 159 | - 'up': function (d) { | |
| 160 | - return d.type == 3 || d.type == 0 ? true : false; | |
| 161 | - }, | |
| 162 | - 'down': function (d) { | |
| 163 | - return d.type == 1 ? true : false; | |
| 164 | - } | |
| 165 | - }) | |
| 166 | - .text(function (d) { | |
| 167 | - return cat_text(d.name[0]); | |
| 168 | - }) | |
| 169 | - .attr('title', function (d) { | |
| 170 | - return d.name[0]; | |
| 171 | - }) | |
| 172 | - .attr('x', function (d, i) { | |
| 173 | - return d.type == 3 ? multi_text(d, i, this) : cx(d, i) | |
| 174 | - }) | |
| 175 | - .attr('y', ty); | |
| 176 | - | |
| 177 | - //gps wrap | |
| 178 | - svg.append('g').classed({ | |
| 179 | - 'gps-wrap': true | |
| 180 | - }); | |
| 181 | - //marker clusterer wrap | |
| 182 | - svg.append('g').classed({ | |
| 183 | - 'marker-clusterer': true | |
| 184 | - }); | |
| 185 | - }; | |
| 186 | - | |
| 187 | - | |
| 188 | - // ----- draw gps ------ | |
| 189 | - //gps 按线路站点分组后的下标映射 | |
| 190 | - var line_gps_index = {}; | |
| 191 | - var get_circle = function (dataId, svg) { | |
| 192 | - try { | |
| 193 | - var circle = $('.station_circle[data-id=' + dataId + ']', svg); | |
| 194 | - if (circle.length == 0) | |
| 195 | - circle = null; | |
| 196 | - } catch (e) { | |
| 197 | - console.log('get_circle error! station_circle data-id:' + dataId); | |
| 198 | - return null; | |
| 199 | - } | |
| 200 | - return circle; | |
| 201 | - }, | |
| 202 | - gx = function (gps, svg, wrapId) { | |
| 203 | - var circle = get_circle(gps.stopNo + '_' + gps.upDown, svg); | |
| 204 | - if (!circle) return -100; | |
| 205 | - | |
| 206 | - var x = circle.attr('cx') - 16.5, | |
| 207 | - s = circle_spaces[wrapId] / 2; | |
| 208 | - // console.log('ss', gps.lineId + '=' + s); | |
| 209 | - //s = 5; | |
| 210 | - if(gps['instation']==0) | |
| 211 | - x = (gps['upDown']==0?x+s:x-s); | |
| 212 | - return x; | |
| 213 | - }, | |
| 214 | - gy = function (gps, svg) { | |
| 215 | - var circle = get_circle(gps.stopNo + '_' + gps.upDown, svg); | |
| 216 | - if (!circle) return -100; | |
| 217 | - | |
| 218 | - var cy = parseInt(circle.attr('cy')), | |
| 219 | - index = line_gps_index[gps.lineId][gps.stopNo + '_' + gps.upDown][gps.deviceId]; | |
| 220 | - | |
| 221 | - return gps.upDown == 0 ? cy - 22 - (index * 17) : cy + 6 + (index * 19); | |
| 222 | - }, | |
| 223 | - ups_gps = function (d) { | |
| 224 | - return d.gpsUps; | |
| 225 | - }, | |
| 226 | - downs_gps = function (d) { | |
| 227 | - return d.gpsDowns; | |
| 228 | - }, | |
| 229 | - gps_index_mapp = function (data) { | |
| 230 | - var rs = {}; | |
| 231 | - var dataGroupStop = gb_svg_data_convert.groupByStationAndUpdown(data); | |
| 232 | - for (var stopNo in dataGroupStop) { | |
| 233 | - rs[stopNo] = {}; | |
| 234 | - $.each(dataGroupStop[stopNo], function (i, gps) { | |
| 235 | - rs[stopNo][gps.deviceId] = i; | |
| 236 | - }); | |
| 237 | - } | |
| 238 | - return rs; | |
| 239 | - }, | |
| 240 | - g_text = function (d) { | |
| 241 | - var len = (d.nbbm == false ? 0 : d.nbbm.length) | |
| 242 | - , t = len > 3 ? d.nbbm.substr(len - 3) : d.nbbm; | |
| 243 | - | |
| 244 | - if (d.nbbm.indexOf('-') > 0) { | |
| 245 | - t = d.nbbm.substr(d.nbbm.indexOf('-') - 1, 1) + t; | |
| 246 | - } | |
| 247 | - return t + d.suffix; | |
| 248 | - }, | |
| 249 | - gps_key = function (d) { | |
| 250 | - return d.deviceId; | |
| 251 | - }, | |
| 252 | - gps_update_point = function (e, svg, wrapId) { | |
| 253 | - var x,e1; | |
| 254 | - e1 = e; | |
| 255 | - if(animation) | |
| 256 | - e1 = e.transition(); | |
| 257 | - e1.attr('x', function (d) { | |
| 258 | - x = gx(d, svg, wrapId); | |
| 259 | - if(x == -100) | |
| 260 | - $(this).css('transition-duration', 0).hide(); | |
| 261 | - else | |
| 262 | - $(this).show();//找不到停靠点,直接隐藏 | |
| 263 | - return x; | |
| 264 | - }) | |
| 265 | - .attr('y', function (d) { | |
| 266 | - return gy(d, svg); | |
| 267 | - }) | |
| 268 | - .attr('updown', function (d) { | |
| 269 | - return d.upDown; | |
| 270 | - }); | |
| 271 | - e.classed({'abnormal': function (d) { | |
| 272 | - return d.abnormalClaszz; | |
| 273 | - }, 'offline': function (d) { | |
| 274 | - return d['abnormalStatus']=='offline'; | |
| 275 | - }}); | |
| 276 | - //update tip position | |
| 277 | - gb_svg_tooltip.update(e); | |
| 278 | - }, | |
| 279 | - rct_id = function (d) { | |
| 280 | - return 'rct_' + d.deviceId; | |
| 281 | - }, | |
| 282 | - tx_id = function (d) { | |
| 283 | - return 'tx_' + d.deviceId; | |
| 284 | - }; | |
| 285 | - | |
| 286 | - var setGps = function (lineCode) { | |
| 287 | - var svgs = $('.line-chart[data-code=' + lineCode + ']'); | |
| 288 | - if(svgsIsHidden(svgs)) | |
| 289 | - return; | |
| 290 | - var data = gb_data_gps.gpsByLineCode(lineCode); | |
| 291 | - | |
| 292 | - var list = [], suffix, abmStatus; | |
| 293 | - //过滤无站点字段的数据 | |
| 294 | - $.each(data, function () { | |
| 295 | - if (!this.stopNo || this.stopNo == '') | |
| 296 | - return true; | |
| 297 | - | |
| 298 | - abmStatus = this['abnormalStatus']; | |
| 299 | - suffix = ''; | |
| 300 | - if(abmStatus != 'offline'){ | |
| 301 | - this['abnormalClaszz'] = true; | |
| 302 | - if(abmStatus=='outBounds') | |
| 303 | - suffix = '界'; | |
| 304 | - else if(abmStatus=='overspeed') | |
| 305 | - suffix = '速'; | |
| 306 | - else if(abmStatus=='gps-offline') | |
| 307 | - suffix = '掉'; | |
| 308 | - else | |
| 309 | - this['abnormalClaszz'] = false; | |
| 310 | - } | |
| 311 | - else | |
| 312 | - this['abnormalClaszz'] = false; | |
| 313 | - | |
| 314 | - this.suffix = suffix; | |
| 315 | - list.push(this); | |
| 316 | - }); | |
| 317 | - | |
| 318 | - line_gps_index[lineCode] = gps_index_mapp(list); | |
| 319 | - $.each(svgs, function () { | |
| 320 | - if(!$(this).parent().is(":visible")) | |
| 321 | - return true; | |
| 322 | - | |
| 323 | - //绘制gps | |
| 324 | - draw_gps(this, list); | |
| 325 | - //聚合gps | |
| 326 | - marker_clusterer(this, lineCode); | |
| 327 | - }); | |
| 328 | - | |
| 329 | - //刷新tooltip | |
| 330 | - gb_svg_tooltip.refresh(); | |
| 331 | - }; | |
| 332 | - | |
| 333 | - var draw_gps = function (svg, data) { | |
| 334 | - //remove merge_hide class | |
| 335 | - $('.merge_hide', svg).removeAttr('class'); | |
| 336 | - | |
| 337 | - var gps_cont = d3.select(svg).select('.gps-wrap'); | |
| 338 | - //rect | |
| 339 | - var rects = gps_cont.selectAll('rect').data(data, gps_key); | |
| 340 | - rects.enter().append('rect').attr('_id', rct_id); | |
| 341 | - | |
| 342 | - //lineCode+'_'+suffixs[wrap.attr('class')] | |
| 343 | - var wrapId = $(svg).data('code') + '_' + suffixs[$(svg).parent().attr('class')]; | |
| 344 | - gps_update_point(rects, svg, wrapId); | |
| 345 | - //text | |
| 346 | - var ts = gps_cont.selectAll('text').data(data, gps_key); | |
| 347 | - ts.enter().append('text').attr('_id', tx_id); | |
| 348 | - ts.text(g_text) | |
| 349 | - gps_update_point(ts, svg, wrapId); | |
| 350 | - }; | |
| 351 | - | |
| 352 | - var marker_clusterer = function (svg, lineCode) { | |
| 353 | - //debugger | |
| 354 | - var gpsArr, idxMapp = line_gps_index[lineCode]; | |
| 355 | - for (var stopNo in idxMapp) { | |
| 356 | - gpsArr = gb_common.get_keys(idxMapp[stopNo]); | |
| 357 | - //remove old merger point | |
| 358 | - $('g[_id=' + 'merger_' + stopNo + ']', svg).remove(); | |
| 359 | - if (gpsArr.length <= 2) | |
| 360 | - continue; | |
| 361 | - | |
| 362 | - marker_clusterer_merge(svg, stopNo, gpsArr); | |
| 363 | - } | |
| 364 | - }; | |
| 365 | - | |
| 366 | - var marker_clusterer_merge = function (svg, stopNo, gpsArr) { | |
| 367 | - //stop circle | |
| 368 | - var circle = get_circle(stopNo, svg); | |
| 369 | - if (!circle) return; | |
| 370 | - | |
| 371 | - var x = parseInt(circle.attr('cx')), | |
| 372 | - y = parseInt(circle.attr('cy')), | |
| 373 | - isDown = circle.attr('class').indexOf('down') != -1; | |
| 374 | - | |
| 375 | - var svg = d3.select(svg); | |
| 376 | - //hide old element | |
| 377 | - $.each(gpsArr, function (i, d) { | |
| 378 | - $('rect[_id=rct_' + d + '],text[_id=tx_' + d + ']').attr('class', 'merge_hide'); | |
| 379 | - }); | |
| 380 | - | |
| 381 | - var mergerG = svg.selectAll('g.marker-clusterer').append('g').attr('_id', 'merger_' + stopNo) | |
| 382 | - .classed({ | |
| 383 | - 'merge-item': true | |
| 384 | - }); | |
| 385 | - //merge rect | |
| 386 | - mergerG.append('rect').attr('x', x - 11) | |
| 387 | - .attr('y', function () { | |
| 388 | - return isDown ? y + 8 : y - 31; | |
| 389 | - }); | |
| 390 | - //merge text | |
| 391 | - var len = gpsArr.length; | |
| 392 | - mergerG.append('text').text(len) | |
| 393 | - .attr('x', x - ((len + '').length * 4.5)) | |
| 394 | - .attr('y', isDown ? y + 24 : y - 14); | |
| 395 | - }; | |
| 396 | - | |
| 397 | - | |
| 398 | - /** | |
| 399 | - * 设备掉线 | |
| 400 | - * @param gps | |
| 401 | - */ | |
| 402 | - var deviceOffline = function (gps) { | |
| 403 | - $('svg text[_id=tx_'+gps.deviceId+']').addClass('offline'); | |
| 404 | - $('svg rect[_id=tx_'+gps.deviceId+']').addClass('offline'); | |
| 405 | - }; | |
| 406 | - | |
| 407 | - /** | |
| 408 | - * 刷新可见的模拟图 | |
| 409 | - */ | |
| 410 | - var refreshByVisible = function () { | |
| 411 | - for(var k in wraps){ | |
| 412 | - if(wraps[k].is(":visible")){ | |
| 413 | - setGps(k.split('_')[0]); | |
| 414 | - } | |
| 415 | - } | |
| 416 | - }; | |
| 417 | - | |
| 418 | - var svgsIsHidden = function (svgs) { | |
| 419 | - for(var i=0,svg;svg=svgs[i++];){ | |
| 420 | - if($(svg).parent().is(":visible")) | |
| 421 | - return false; | |
| 422 | - } | |
| 423 | - return true; | |
| 424 | - }; | |
| 425 | - | |
| 426 | - return { | |
| 427 | - draw_line: draw_line, | |
| 428 | - setGps: setGps, | |
| 429 | - deviceOffline: deviceOffline, | |
| 430 | - refreshByVisible: refreshByVisible, | |
| 431 | - disabledAnimation: function () { | |
| 432 | - animation = false; | |
| 433 | - }, | |
| 434 | - enabledAnimation: function () { | |
| 435 | - animation = true; | |
| 436 | - } | |
| 437 | - }; | |
| 438 | -})(); | |
| 1 | +/* 线路模拟图 */ | |
| 2 | + | |
| 3 | +var gb_svg_chart = (function () { | |
| 4 | + | |
| 5 | + //chart height | |
| 6 | + var chart_height = 122; | |
| 7 | + //left right padding | |
| 8 | + var x_padd = 30; | |
| 9 | + //上空白部分 | |
| 10 | + var y_top_padd = 4; | |
| 11 | + //text max size | |
| 12 | + var t_max_size = 7; | |
| 13 | + //svg namespace | |
| 14 | + var svgns = 'http://www.w3.org/2000/svg'; | |
| 15 | + var wraps={}; | |
| 16 | + //根据wrap的class区分是home还是电子路单界面 | |
| 17 | + var suffixs={ | |
| 18 | + 'uk-width-medium-3-5 svg-chart-wrap': 'home', | |
| 19 | + 'svg-wrap': 'line' | |
| 20 | + } | |
| 21 | + | |
| 22 | + //站点间隔 线路编码 -> space | |
| 23 | + var circle_spaces={}; | |
| 24 | + //var circle_space; | |
| 25 | + //是否启用动画 | |
| 26 | + var animation = true; | |
| 27 | + | |
| 28 | + var calc_text_y = function (t) { | |
| 29 | + return (chart_height - (chart_height / t_max_size * t.length)) / 2 + 5; | |
| 30 | + }, | |
| 31 | + cat_text = function (t) { | |
| 32 | + if(!t) | |
| 33 | + return 0; | |
| 34 | + return t.length > t_max_size ? t.substr(0, t_max_size) : t; | |
| 35 | + }, | |
| 36 | + get_width = function (wrap) { | |
| 37 | + return wrap.actual('outerWidth'); | |
| 38 | + }, | |
| 39 | + get_height = function (wrap) { | |
| 40 | + var h = wrap.actual('outerHeight'); | |
| 41 | + //隐藏元素取最外层的高度 | |
| 42 | + return h < 20 ? wrap.parent().actual('outerHeight') - 2 : h; | |
| 43 | + }; | |
| 44 | + | |
| 45 | + /** | |
| 46 | + * 绘制线路模拟图 | |
| 47 | + * @param lineCode 线路编码 | |
| 48 | + * @param wrap dom容器 | |
| 49 | + * @param enableAttr 是否启用配置 | |
| 50 | + */ | |
| 51 | + var draw_line = function (lineCode, wrap, enableAttr, direction) { | |
| 52 | + var wrapId = lineCode+'_'+suffixs[wrap.attr('class')]; | |
| 53 | + wraps[wrapId]=wrap; | |
| 54 | + //环线 | |
| 55 | + var loopLine = gb_data_basic.isLoopLine(lineCode); | |
| 56 | + | |
| 57 | + var routes = gb_data_basic.stationRoutes(lineCode); | |
| 58 | + if(!routes) | |
| 59 | + return; | |
| 60 | + var data = gb_svg_data_convert.mergeRoute(JSON.parse(JSON.stringify(routes)), enableAttr, lineCode, loopLine), | |
| 61 | + len = data.length; | |
| 62 | + | |
| 63 | + var w = get_width(wrap), | |
| 64 | + h = parseInt(get_height(wrap)) | |
| 65 | + //x scale | |
| 66 | + , | |
| 67 | + xScale = d3.scale.linear().range([x_padd, w - x_padd]).domain([0, len - 1]), | |
| 68 | + cx = function (d, i) { | |
| 69 | + return xScale(i); | |
| 70 | + }, | |
| 71 | + cy = function () { | |
| 72 | + return (h - chart_height) / 2 + y_top_padd; | |
| 73 | + }, | |
| 74 | + ty = function (d) { | |
| 75 | + return cy() + calc_text_y(cat_text(d.name[0])); | |
| 76 | + } | |
| 77 | + //line generator | |
| 78 | + , | |
| 79 | + upLine = d3.svg.line().x(xScale).y(cy), | |
| 80 | + downLine = d3.svg.line().x(xScale).y(function () { | |
| 81 | + return cy() + chart_height | |
| 82 | + }), | |
| 83 | + multi_text = function (d, i, that) { | |
| 84 | + | |
| 85 | + var dText = document.createElementNS(svgns, 'text'), | |
| 86 | + t = cat_text(d.name[1]); | |
| 87 | + $(dText).attr('class', 'station_text down') | |
| 88 | + .attr('x', cx(d, i) + 8).attr('y', cy() + calc_text_y(t)) | |
| 89 | + .attr('title', d.name[1]) | |
| 90 | + .text(t); | |
| 91 | + | |
| 92 | + $(that).after(dText); | |
| 93 | + return cx(d, i) - 8; | |
| 94 | + }, | |
| 95 | + dynamic_text = function(d, i, that, direction) { | |
| 96 | + var t = (direction == 2 ? d.name[1] : d.name[0]); | |
| 97 | + $(that).attr('class', 'station_text ' + (direction == 2 ? 'down' : 'up')) | |
| 98 | + .attr('x', cx(d, i) + 8).attr('y', cy() + calc_text_y(cat_text(t))) | |
| 99 | + .attr('title', t) | |
| 100 | + .text(cat_text(t)); | |
| 101 | + | |
| 102 | + return cx(d, i); | |
| 103 | + } | |
| 104 | + | |
| 105 | + circle_spaces[wrapId] = (w - (x_padd * 2)) / len; | |
| 106 | + //add svg dom | |
| 107 | + var svg = d3.select(wrap[0]).append('svg') | |
| 108 | + .classed({ | |
| 109 | + 'line-chart': true | |
| 110 | + }).attr('data-code', lineCode) | |
| 111 | + .attr('onselectstart', 'return false') | |
| 112 | + .style('height', h); | |
| 113 | + | |
| 114 | + //add item g | |
| 115 | + var items = svg.selectAll('g.item').data(data) | |
| 116 | + .enter().append('g').classed({ | |
| 117 | + 'item': true | |
| 118 | + }); | |
| 119 | + | |
| 120 | + //up station link path | |
| 121 | + var p_clzz = { | |
| 122 | + 'station_link': true | |
| 123 | + }; | |
| 124 | + items.append('path').classed(p_clzz) | |
| 125 | + .attr('d', function (d, i) { | |
| 126 | + return i < len - 1 ? upLine([i, i + 1]) : ''; | |
| 127 | + }); | |
| 128 | + | |
| 129 | + //down station link path | |
| 130 | + p_clzz.down = true; | |
| 131 | + p_clzz.loop_line = loopLine; | |
| 132 | + items.append('path').classed(p_clzz) | |
| 133 | + .attr('d', function (d, i) { | |
| 134 | + return i < len - 1 ? downLine([i, i + 1]) : ''; | |
| 135 | + }); | |
| 136 | + | |
| 137 | + //up circle | |
| 138 | + var c_clzz = { | |
| 139 | + 'station_circle': true | |
| 140 | + }; | |
| 141 | + items.select(function (d) { | |
| 142 | + return d.type != 1 ? this : null; | |
| 143 | + }) | |
| 144 | + .append('circle').classed(c_clzz) | |
| 145 | + .attr('cx', cx) | |
| 146 | + .attr('cy', cy) | |
| 147 | + .attr('data-id', function (d) { | |
| 148 | + return d.id[0]; | |
| 149 | + }); | |
| 150 | + | |
| 151 | + //down circle | |
| 152 | + c_clzz.down = true; | |
| 153 | + items.select(function (d) { | |
| 154 | + return d.type != 0 ? this : null; | |
| 155 | + }) | |
| 156 | + .append('circle').classed(c_clzz) | |
| 157 | + .attr('cx', cx) | |
| 158 | + .attr('cy', function (d, i) { | |
| 159 | + return cy(d, i) + chart_height; | |
| 160 | + }) | |
| 161 | + .attr('data-id', function (d) { | |
| 162 | + return d.type == 1 ? d.id[0] : d.id[1]; | |
| 163 | + }); | |
| 164 | + | |
| 165 | + //station name text | |
| 166 | + items.append('text').classed({ | |
| 167 | + 'station_text': true, | |
| 168 | + 'up': function (d) { | |
| 169 | + return (d.type == 3 || d.type == 0) && direction != 2; | |
| 170 | + }, | |
| 171 | + 'down': function (d) { | |
| 172 | + return d.type == 1 && direction != 1; | |
| 173 | + } | |
| 174 | + }) | |
| 175 | + .text(function (d) { | |
| 176 | + return cat_text(d.name[0]); | |
| 177 | + }) | |
| 178 | + .attr('title', function (d) { | |
| 179 | + return d.name[0]; | |
| 180 | + }) | |
| 181 | + .attr('y', ty) | |
| 182 | + .attr('x', function (d, i) { | |
| 183 | + return d.type == 3 ? (direction ? dynamic_text(d, i, this, direction) : multi_text(d, i, this)) : cx(d, i) | |
| 184 | + }); | |
| 185 | + | |
| 186 | + //gps wrap | |
| 187 | + svg.append('g').classed({ | |
| 188 | + 'gps-wrap': true | |
| 189 | + }); | |
| 190 | + //marker clusterer wrap | |
| 191 | + svg.append('g').classed({ | |
| 192 | + 'marker-clusterer': true | |
| 193 | + }); | |
| 194 | + var count = $('#svg_title_' + lineCode).data('count'); | |
| 195 | + if (!count) { | |
| 196 | + $('#svg_title_' + lineCode).data('count', 1); | |
| 197 | + $('#svg_title_' + lineCode).click(function () { | |
| 198 | + var count = $(this).data('count'); | |
| 199 | + var wrap = $('#home-main-content .home-line-card[data-line-code='+lineCode+'] .svg-chart-wrap'); | |
| 200 | + wrap.find('svg.line-chart').remove(); | |
| 201 | + gb_svg_chart.draw_line(lineCode, wrap, true, count % 2 + 1); | |
| 202 | + $(this).data('count', count + 1); | |
| 203 | + }) | |
| 204 | + } | |
| 205 | + }; | |
| 206 | + | |
| 207 | + | |
| 208 | + // ----- draw gps ------ | |
| 209 | + //gps 按线路站点分组后的下标映射 | |
| 210 | + var line_gps_index = {}; | |
| 211 | + var get_circle = function (dataId, svg) { | |
| 212 | + try { | |
| 213 | + var circle = $('.station_circle[data-id=' + dataId + ']', svg); | |
| 214 | + if (circle.length == 0) | |
| 215 | + circle = null; | |
| 216 | + } catch (e) { | |
| 217 | + console.log('get_circle error! station_circle data-id:' + dataId); | |
| 218 | + return null; | |
| 219 | + } | |
| 220 | + return circle; | |
| 221 | + }, | |
| 222 | + gx = function (gps, svg, wrapId) { | |
| 223 | + var circle = get_circle(gps.stopNo + '_' + gps.upDown, svg); | |
| 224 | + if (!circle) return -100; | |
| 225 | + | |
| 226 | + var x = circle.attr('cx') - 16.5, | |
| 227 | + s = circle_spaces[wrapId] / 2; | |
| 228 | + // console.log('ss', gps.lineId + '=' + s); | |
| 229 | + //s = 5; | |
| 230 | + if(gps['instation']==0) | |
| 231 | + x = (gps['upDown']==0?x+s:x-s); | |
| 232 | + return x; | |
| 233 | + }, | |
| 234 | + gy = function (gps, svg) { | |
| 235 | + var circle = get_circle(gps.stopNo + '_' + gps.upDown, svg); | |
| 236 | + if (!circle) return -100; | |
| 237 | + | |
| 238 | + var cy = parseInt(circle.attr('cy')), | |
| 239 | + index = line_gps_index[gps.lineId][gps.stopNo + '_' + gps.upDown][gps.deviceId]; | |
| 240 | + | |
| 241 | + return gps.upDown == 0 ? cy - 22 - (index * 17) : cy + 6 + (index * 19); | |
| 242 | + }, | |
| 243 | + ups_gps = function (d) { | |
| 244 | + return d.gpsUps; | |
| 245 | + }, | |
| 246 | + downs_gps = function (d) { | |
| 247 | + return d.gpsDowns; | |
| 248 | + }, | |
| 249 | + gps_index_mapp = function (data) { | |
| 250 | + var rs = {}; | |
| 251 | + var dataGroupStop = gb_svg_data_convert.groupByStationAndUpdown(data); | |
| 252 | + for (var stopNo in dataGroupStop) { | |
| 253 | + rs[stopNo] = {}; | |
| 254 | + $.each(dataGroupStop[stopNo], function (i, gps) { | |
| 255 | + rs[stopNo][gps.deviceId] = i; | |
| 256 | + }); | |
| 257 | + } | |
| 258 | + return rs; | |
| 259 | + }, | |
| 260 | + g_text = function (d) { | |
| 261 | + var len = (d.nbbm == false ? 0 : d.nbbm.length) | |
| 262 | + , t = len > 3 ? d.nbbm.substr(len - 3) : d.nbbm; | |
| 263 | + | |
| 264 | + if (d.nbbm.indexOf('-') > 0) { | |
| 265 | + t = d.nbbm.substr(d.nbbm.indexOf('-') - 1, 1) + t; | |
| 266 | + } | |
| 267 | + return t + d.suffix; | |
| 268 | + }, | |
| 269 | + gps_key = function (d) { | |
| 270 | + return d.deviceId; | |
| 271 | + }, | |
| 272 | + gps_update_point = function (e, svg, wrapId) { | |
| 273 | + var x,e1; | |
| 274 | + e1 = e; | |
| 275 | + if(animation) | |
| 276 | + e1 = e.transition(); | |
| 277 | + e1.attr('x', function (d) { | |
| 278 | + x = gx(d, svg, wrapId); | |
| 279 | + if(x == -100) | |
| 280 | + $(this).css('transition-duration', 0).hide(); | |
| 281 | + else | |
| 282 | + $(this).show();//找不到停靠点,直接隐藏 | |
| 283 | + return x; | |
| 284 | + }) | |
| 285 | + .attr('y', function (d) { | |
| 286 | + return gy(d, svg); | |
| 287 | + }) | |
| 288 | + .attr('updown', function (d) { | |
| 289 | + return d.upDown; | |
| 290 | + }); | |
| 291 | + e.classed({'abnormal': function (d) { | |
| 292 | + return d.abnormalClaszz; | |
| 293 | + }, 'offline': function (d) { | |
| 294 | + return d['abnormalStatus']=='offline'; | |
| 295 | + }}); | |
| 296 | + //update tip position | |
| 297 | + gb_svg_tooltip.update(e); | |
| 298 | + }, | |
| 299 | + rct_id = function (d) { | |
| 300 | + return 'rct_' + d.deviceId; | |
| 301 | + }, | |
| 302 | + tx_id = function (d) { | |
| 303 | + return 'tx_' + d.deviceId; | |
| 304 | + }; | |
| 305 | + | |
| 306 | + var setGps = function (lineCode) { | |
| 307 | + var svgs = $('.line-chart[data-code=' + lineCode + ']'); | |
| 308 | + if(svgsIsHidden(svgs)) | |
| 309 | + return; | |
| 310 | + var data = gb_data_gps.gpsByLineCode(lineCode); | |
| 311 | + | |
| 312 | + var list = [], suffix, abmStatus; | |
| 313 | + //过滤无站点字段的数据 | |
| 314 | + $.each(data, function () { | |
| 315 | + if (!this.stopNo || this.stopNo == '') | |
| 316 | + return true; | |
| 317 | + | |
| 318 | + abmStatus = this['abnormalStatus']; | |
| 319 | + suffix = ''; | |
| 320 | + if(abmStatus != 'offline'){ | |
| 321 | + this['abnormalClaszz'] = true; | |
| 322 | + if(abmStatus=='outBounds') | |
| 323 | + suffix = '界'; | |
| 324 | + else if(abmStatus=='overspeed') | |
| 325 | + suffix = '速'; | |
| 326 | + else if(abmStatus=='gps-offline') | |
| 327 | + suffix = '掉'; | |
| 328 | + else | |
| 329 | + this['abnormalClaszz'] = false; | |
| 330 | + } | |
| 331 | + else | |
| 332 | + this['abnormalClaszz'] = false; | |
| 333 | + | |
| 334 | + this.suffix = suffix; | |
| 335 | + list.push(this); | |
| 336 | + }); | |
| 337 | + | |
| 338 | + line_gps_index[lineCode] = gps_index_mapp(list); | |
| 339 | + $.each(svgs, function () { | |
| 340 | + if(!$(this).parent().is(":visible")) | |
| 341 | + return true; | |
| 342 | + | |
| 343 | + //绘制gps | |
| 344 | + draw_gps(this, list); | |
| 345 | + //聚合gps | |
| 346 | + marker_clusterer(this, lineCode); | |
| 347 | + }); | |
| 348 | + | |
| 349 | + //刷新tooltip | |
| 350 | + gb_svg_tooltip.refresh(); | |
| 351 | + }; | |
| 352 | + | |
| 353 | + var draw_gps = function (svg, data) { | |
| 354 | + //remove merge_hide class | |
| 355 | + $('.merge_hide', svg).removeAttr('class'); | |
| 356 | + | |
| 357 | + var gps_cont = d3.select(svg).select('.gps-wrap'); | |
| 358 | + //rect | |
| 359 | + var rects = gps_cont.selectAll('rect').data(data, gps_key); | |
| 360 | + rects.enter().append('rect').attr('_id', rct_id); | |
| 361 | + | |
| 362 | + //lineCode+'_'+suffixs[wrap.attr('class')] | |
| 363 | + var wrapId = $(svg).data('code') + '_' + suffixs[$(svg).parent().attr('class')]; | |
| 364 | + gps_update_point(rects, svg, wrapId); | |
| 365 | + //text | |
| 366 | + var ts = gps_cont.selectAll('text').data(data, gps_key); | |
| 367 | + ts.enter().append('text').attr('_id', tx_id); | |
| 368 | + ts.text(g_text) | |
| 369 | + gps_update_point(ts, svg, wrapId); | |
| 370 | + }; | |
| 371 | + | |
| 372 | + var marker_clusterer = function (svg, lineCode) { | |
| 373 | + //debugger | |
| 374 | + var gpsArr, idxMapp = line_gps_index[lineCode]; | |
| 375 | + for (var stopNo in idxMapp) { | |
| 376 | + gpsArr = gb_common.get_keys(idxMapp[stopNo]); | |
| 377 | + //remove old merger point | |
| 378 | + $('g[_id=' + 'merger_' + stopNo + ']', svg).remove(); | |
| 379 | + if (gpsArr.length <= 2) | |
| 380 | + continue; | |
| 381 | + | |
| 382 | + marker_clusterer_merge(svg, stopNo, gpsArr); | |
| 383 | + } | |
| 384 | + }; | |
| 385 | + | |
| 386 | + var marker_clusterer_merge = function (svg, stopNo, gpsArr) { | |
| 387 | + //stop circle | |
| 388 | + var circle = get_circle(stopNo, svg); | |
| 389 | + if (!circle) return; | |
| 390 | + | |
| 391 | + var x = parseInt(circle.attr('cx')), | |
| 392 | + y = parseInt(circle.attr('cy')), | |
| 393 | + isDown = circle.attr('class').indexOf('down') != -1; | |
| 394 | + | |
| 395 | + var svg = d3.select(svg); | |
| 396 | + //hide old element | |
| 397 | + $.each(gpsArr, function (i, d) { | |
| 398 | + $('rect[_id=rct_' + d + '],text[_id=tx_' + d + ']').attr('class', 'merge_hide'); | |
| 399 | + }); | |
| 400 | + | |
| 401 | + var mergerG = svg.selectAll('g.marker-clusterer').append('g').attr('_id', 'merger_' + stopNo) | |
| 402 | + .classed({ | |
| 403 | + 'merge-item': true | |
| 404 | + }); | |
| 405 | + //merge rect | |
| 406 | + mergerG.append('rect').attr('x', x - 11) | |
| 407 | + .attr('y', function () { | |
| 408 | + return isDown ? y + 8 : y - 31; | |
| 409 | + }); | |
| 410 | + //merge text | |
| 411 | + var len = gpsArr.length; | |
| 412 | + mergerG.append('text').text(len) | |
| 413 | + .attr('x', x - ((len + '').length * 4.5)) | |
| 414 | + .attr('y', isDown ? y + 24 : y - 14); | |
| 415 | + }; | |
| 416 | + | |
| 417 | + | |
| 418 | + /** | |
| 419 | + * 设备掉线 | |
| 420 | + * @param gps | |
| 421 | + */ | |
| 422 | + var deviceOffline = function (gps) { | |
| 423 | + $('svg text[_id=tx_'+gps.deviceId+']').addClass('offline'); | |
| 424 | + $('svg rect[_id=tx_'+gps.deviceId+']').addClass('offline'); | |
| 425 | + }; | |
| 426 | + | |
| 427 | + /** | |
| 428 | + * 刷新可见的模拟图 | |
| 429 | + */ | |
| 430 | + var refreshByVisible = function () { | |
| 431 | + for(var k in wraps){ | |
| 432 | + if(wraps[k].is(":visible")){ | |
| 433 | + setGps(k.split('_')[0]); | |
| 434 | + } | |
| 435 | + } | |
| 436 | + }; | |
| 437 | + | |
| 438 | + var svgsIsHidden = function (svgs) { | |
| 439 | + for(var i=0,svg;svg=svgs[i++];){ | |
| 440 | + if($(svg).parent().is(":visible")) | |
| 441 | + return false; | |
| 442 | + } | |
| 443 | + return true; | |
| 444 | + }; | |
| 445 | + | |
| 446 | + return { | |
| 447 | + draw_line: draw_line, | |
| 448 | + setGps: setGps, | |
| 449 | + deviceOffline: deviceOffline, | |
| 450 | + refreshByVisible: refreshByVisible, | |
| 451 | + disabledAnimation: function () { | |
| 452 | + animation = false; | |
| 453 | + }, | |
| 454 | + enabledAnimation: function () { | |
| 455 | + animation = true; | |
| 456 | + } | |
| 457 | + }; | |
| 458 | +})(); | ... | ... |