script.js (27007B)
1 /***************/ 2 /*** General ***/ 3 /***************/ 4 5 window.app = {}; 6 var app = window.app; 7 8 app.mainLayer = new ol.layer.Tile({ source: new ol.source.OSM() }); 9 10 11 /****************/ 12 /*** Geometry ***/ 13 /****************/ 14 15 app.geometry = {} 16 app.geometry.REarth = 6371000; 17 app.geometry.toRadians = function(x){ return x * Math.PI / 180; }; 18 19 app.geometry.haversine = function(lat1, lon1, lat2, lon2){ 20 var lat1 = app.geometry.toRadians(lat1); 21 var lon1 = app.geometry.toRadians(lon1); 22 var lat2 = app.geometry.toRadians(lat2); 23 var lon2 = app.geometry.toRadians(lon2); 24 25 var dlat = Math.abs(lat1-lat2); 26 var dlon = Math.abs(lon1-lon2); 27 28 var alpha = Math.pow(Math.sin(dlat/2), 2) + Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin(dlon/2), 2); 29 var d = Math.atan2(Math.sqrt(alpha), Math.sqrt(1-alpha)); 30 31 return 2 * app.geometry.REarth * d; 32 }; 33 34 app.geometry.equirectangular = function(lat1, lon1, lat2, lon2){ 35 var lat1 = app.geometry.toRadians(lat1); 36 var lon1 = app.geometry.toRadians(lon1); 37 var lat2 = app.geometry.toRadians(lat2); 38 var lon2 = app.geometry.toRadians(lon2); 39 var x = (lon2-lon1) * Math.cos((lat1+lat2)/2); 40 var y = (lat2-lat1); 41 return Math.sqrt(x*x + y*y) * app.geometry.REarth; 42 }; 43 44 45 /***************/ 46 /*** Measure ***/ 47 /***************/ 48 49 app.measure = {}; 50 app.measure.tooltip_list = []; 51 52 app.measure.source = new ol.source.Vector(); 53 54 app.measure.layer = new ol.layer.Vector({ 55 source: app.measure.source, 56 style: new ol.style.Style({ 57 fill: new ol.style.Fill({ 58 color: 'rgba(255, 255, 255, 0.2)' 59 }), 60 stroke: new ol.style.Stroke({ 61 color: '#FC3', 62 width: 2 63 }), 64 image: new ol.style.Circle({ 65 radius: 7, 66 fill: new ol.style.Fill({ 67 color: '#FC3' 68 }) 69 }) 70 }) 71 }); 72 73 app.measure.pointerMoveHandler = function(evt){ 74 if(evt.dragging){ return; } 75 var tooltipCoord = evt.coordinate; 76 77 if(app.measure.sketch){ 78 var output; 79 var geom = (app.measure.sketch.getGeometry()); 80 if(geom instanceof ol.geom.LineString){ 81 output = app.measure.formatLength((geom)); 82 tooltipCoord = geom.getLastCoordinate(); 83 } 84 app.measure.tooltipElement.innerHTML = output; 85 app.measure.tooltip.setPosition(tooltipCoord); 86 } 87 }; 88 89 app.measure.addInteraction = function(){ 90 app.measure.draw = new ol.interaction.Draw({ 91 source: app.measure.source, 92 type: ('LineString'), 93 style: new ol.style.Style({ 94 fill: new ol.style.Fill({ 95 color: 'rgba(255, 255, 255, 0.2)' 96 }), 97 stroke: new ol.style.Stroke({ 98 color: 'rgba(0, 0, 0, 0.5)', 99 lineDash: [10, 10], 100 width: 2 101 }), 102 image: new ol.style.Circle({ 103 radius: 5, 104 stroke: new ol.style.Stroke({ 105 color: 'rgba(0, 0, 0, 0.7)' 106 }), 107 fill: new ol.style.Fill({ 108 color: 'rgba(255, 255, 255, 0.2)' 109 }) 110 }) 111 }) 112 }); 113 app.map.addInteraction(app.measure.draw); 114 115 app.measure.createTooltip(); 116 117 app.measure.draw.on('drawstart', 118 function(evt){ 119 app.measure.sketch = evt.feature; 120 }, this); 121 122 app.measure.draw.on('drawend', 123 function(evt){ 124 app.measure.tooltipElement.className = 'measure-tooltip measure-tooltip-static'; 125 app.measure.tooltip.setOffset([0, -7]); 126 app.measure.sketch = null; 127 app.measure.tooltipElement = null; 128 app.measure.createTooltip(); 129 }, this); 130 }; 131 132 app.measure.createTooltip = function(){ 133 if(app.measure.tooltipElement){ 134 app.measure.tooltipElement.parentNode.removeChild(app.measure.tooltipElement); 135 } 136 app.measure.tooltipElement = document.createElement('div'); 137 app.measure.tooltipElement.className = 'measure-tooltip measure-tooltip-value'; 138 app.measure.tooltip = new ol.Overlay({ 139 element: app.measure.tooltipElement, 140 offset: [0, -15], 141 positioning: 'bottom-center' 142 }); 143 app.measure.tooltip_list.push(app.measure.tooltip); 144 app.map.addOverlay(app.measure.tooltip); 145 }; 146 147 app.measure.formatLength = function(line){ 148 var length_euclidean = line.getLength(); 149 var length_equirectangular = 0; 150 var length_haversine = 0; 151 var coordinates = line.getCoordinates(); 152 var sourceProj = app.map.getView().getProjection(); 153 for(var i = 0, ii = coordinates.length - 1; i < ii; ++i){ 154 var c1 = ol.proj.transform(coordinates[i], sourceProj, 'EPSG:4326'); 155 var c2 = ol.proj.transform(coordinates[i + 1], sourceProj, 'EPSG:4326'); 156 length_equirectangular += app.geometry.equirectangular(c1[1], c1[0], c2[1], c2[0]); 157 length_haversine += app.geometry.haversine(c1[1], c1[0], c2[1], c2[0]); 158 } 159 160 var disp = function(x){ 161 if(x > 100){ 162 return Math.round(x / 1000 * 1000) / 1000 + 'km'; 163 } else { 164 return Math.round(x * 1000) / 1000 + 'm'; 165 } 166 } 167 168 var length_euclidean = disp(length_euclidean); 169 var length_equirectangular = disp(length_equirectangular); 170 var length_haversine = disp(length_haversine); 171 172 var display_euclidean = $('input#measure-euclidean').prop('checked'); 173 var display_equirectangular = $('input#measure-equirectangular').prop('checked'); 174 var display_haversine = $('input#measure-haversine').prop('checked'); 175 176 var header = true; 177 if(display_euclidean + display_equirectangular + display_haversine == 1){ 178 header = false; 179 } 180 181 var str = ''; 182 if(display_euclidean){ 183 if(header){ str += 'euclidean: '; } 184 str += length_euclidean; 185 } 186 if(display_equirectangular){ 187 if(header){ if(display_euclidean){ str += '<br>'; } str += 'equirectangular: '; } 188 str += length_equirectangular; 189 } 190 if(display_haversine){ 191 if(header){ str += '<br> haversine: '; } 192 str += length_haversine; 193 } 194 return str; 195 }; 196 197 198 /*******************/ 199 /*** DataDisplay ***/ 200 /*******************/ 201 202 app.dataDisplay = {}; 203 app.dataDisplay.layers = {}; 204 app.dataDisplay.heatmapRadius = 5; 205 app.dataDisplay.heatmapBlur = 5; 206 app.dataDisplay.pathPointMode = 1; // endpoints 207 app.dataDisplay.pathPointResolution = 50; 208 209 app.dataDisplay.loadLayer = function(path){ 210 $.ajax({url: path, cache: false, dataType: 'json', 211 success: function(result){ 212 app.dataDisplay.layers[path] = app.dataDisplay.preprocess(result); 213 app.map.addLayer(app.dataDisplay.layers[path]); 214 } 215 }); 216 }; 217 218 app.dataDisplay.unloadLayer = function(path){ 219 app.map.removeLayer(app.dataDisplay.layers[path]); 220 delete app.dataDisplay.layers[path]; 221 }; 222 223 app.dataDisplay.rawStyle = function(feature, resolution){ 224 var style = [ new ol.style.Style({ 225 stroke: new ol.style.Stroke({ 226 color: '#00F', 227 width: 5 228 }), 229 image: new ol.style.Circle({ 230 radius: 5, 231 fill: new ol.style.Fill({ 232 color: '#00F' 233 }) 234 }) 235 }), 236 new ol.style.Style({ 237 stroke: new ol.style.Stroke({ 238 color: '#000', 239 width: 2 240 }), 241 image: new ol.style.Circle({ 242 radius: 2, 243 fill: new ol.style.Fill({ 244 color: '#FFF' 245 }) 246 }) 247 }) 248 ]; 249 250 if(feature.get('display') == 'path' && resolution < app.dataDisplay.pathPointResolution){ 251 if(app.dataDisplay.pathPointMode == 2){ 252 var polyline = feature.getGeometry(); 253 var points = polyline.getCoordinates(); 254 for(var i=1; i<points.length-1; ++i){ 255 var point = points[i]; 256 var pos = i/points.length; 257 var red = 0; 258 var green = 0; 259 if(pos < 0.5){ 260 green = 255; 261 red = Math.round(pos*2*255); 262 } else { 263 red = 255; 264 green = Math.round((1-pos)*2*255); 265 } 266 style.push(new ol.style.Style({ 267 geometry: new ol.geom.Point(point), 268 image: new ol.style.Circle({ 269 radius: 3, 270 fill: new ol.style.Fill({ 271 color: 'rgb('+red+','+green+',0)' 272 }) 273 }) 274 })); 275 } 276 } 277 if(app.dataDisplay.pathPointMode >= 1){ 278 var polyline = feature.getGeometry(); 279 var first = polyline.getFirstCoordinate(); 280 var last = polyline.getLastCoordinate(); 281 style.push(new ol.style.Style({ 282 geometry: new ol.geom.Point(first), 283 image: new ol.style.Circle({ 284 radius: 5, 285 fill: new ol.style.Fill({ 286 color: '#0F0' 287 }) 288 }) 289 })); 290 style.push(new ol.style.Style({ 291 geometry: new ol.geom.Point(last), 292 image: new ol.style.Circle({ 293 radius: 5, 294 fill: new ol.style.Fill({ 295 color: '#F00' 296 }) 297 }) 298 })); 299 } 300 } 301 302 return style; 303 }; 304 305 app.dataDisplay.clusterStyleCache = {}; 306 app.dataDisplay.clusterStyle = function(feature, resolution){ 307 var size = feature.get('features').length; 308 var style = app.dataDisplay.clusterStyleCache[size]; 309 if(!style){ 310 style = [new ol.style.Style({ 311 image: new ol.style.Circle({ 312 radius: 10, 313 stroke: new ol.style.Stroke({ 314 color: '#FFF' 315 }), 316 fill: new ol.style.Fill({ 317 color: '#39C' 318 }) 319 }), 320 text: new ol.style.Text({ 321 text: size.toString(), 322 fill: new ol.style.Fill({ 323 color: '#FFF' 324 }) 325 }) 326 })]; 327 app.dataDisplay.clusterStyleCache[size] = style; 328 } 329 return style; 330 }; 331 332 app.dataDisplay.pointDistributionStyle = function(feature, resolution){ 333 var p = feature.get('info'); 334 var red = 0; 335 var green = 0; 336 if(p < 0.5){ 337 green = 255; 338 red = Math.round(p*2*255); 339 } else { 340 red = 255; 341 green = Math.round((1-p)*2*255); 342 } 343 return [ new ol.style.Style({ 344 image: new ol.style.Circle({ 345 radius: 5, 346 fill: new ol.style.Fill({ 347 color: 'rgb('+red+','+green+',0)' 348 }) 349 }) 350 }) ]; 351 }; 352 353 app.dataDisplay.preprocess = function(egj){ 354 var source = new ol.source.GeoJSON({ 355 projection: 'EPSG:3857', 356 object: egj.data 357 }); 358 359 if(egj.type == 'raw'){ 360 return new ol.layer.Vector({ 361 source: source, 362 style: app.dataDisplay.rawStyle 363 }); 364 365 } else if(egj.type == 'cluster'){ 366 return new ol.layer.Vector({ 367 source: new ol.source.Cluster({ 368 distance: 40, 369 source: source 370 }), 371 style: app.dataDisplay.clusterStyle 372 }); 373 374 } else if(egj.type == 'heatmap'){ 375 return new ol.layer.Heatmap({ 376 source: source, 377 blur: app.dataDisplay.heatmapBlur, 378 radius: app.dataDisplay.heatmapRadius 379 }); 380 } else if(egj.type == 'point distribution'){ 381 return new ol.layer.Vector({ 382 source: source, 383 style: app.dataDisplay.pointDistributionStyle 384 }); 385 } 386 }; 387 388 app.dataDisplay.reloadPathes = function(){ 389 for(var layer in app.dataDisplay.layers){ 390 if(app.dataDisplay.layers[layer].getSource().getFeatures()[0].get('display') == 'path'){ 391 app.dataDisplay.layers[layer].changed(); 392 } 393 } 394 }; 395 396 app.dataDisplay.reloadHeatmaps = function(){ 397 for(var key in app.dataDisplay.layers){ 398 var layer = app.dataDisplay.layers[key]; 399 if(layer instanceof ol.layer.Heatmap){ 400 layer.setBlur(app.dataDisplay.heatmapBlur); 401 layer.setRadius(app.dataDisplay.heatmapRadius); 402 } 403 } 404 }; 405 406 407 /****************/ 408 /*** DataList ***/ 409 /****************/ 410 411 app.dataList = {}; 412 app.dataList.current = {}; 413 app.dataList.idgen = 0; 414 415 app.dataList.init = function(){ 416 app.dataList.elementTree = {}; 417 app.dataList.elementTree.parent = null; 418 app.dataList.elementTree.children = {}; 419 app.dataList.elementTree.checkbox = null; 420 app.dataList.elementTree.ul = $('#datalist-tree ul'); 421 422 app.dataList.updateList(); 423 setInterval(app.dataList.updateList, 1000); 424 }; 425 426 app.dataList.updateList = function(){ 427 $.ajax({url: '/ls/', cache: false, dataType: 'json', 428 success: function(result){ 429 result.forEach(function(file){ 430 file.uri = file.path.join('/') + '/' + file.name 431 if(file.uri in app.dataList.current){ 432 if(file.mtime > app.dataList.current[file.uri].mtime){ 433 var act = app.dataList.current[file.uri]; 434 if(act.checkbox.prop('checked')){ 435 app.dataList.unloadLayer(file.uri); 436 app.dataList.loadLayer(file.uri); 437 } 438 act.mtime = file.mtime; 439 } 440 } else { 441 app.dataList.insert(file); 442 } 443 }); 444 } 445 }); 446 }; 447 448 app.dataList.insert = function(file){ 449 var cur = app.dataList.elementTree; 450 var prev = null; 451 for(var i = 1; i<file.path.length; i++){ 452 if(!(file.path[i] in cur.children)){ 453 var n = {}; 454 n.uri = file.path.slice(0, i+1).join('/'); 455 n.children = {}; 456 n.parent = cur; 457 n.ul = $('<ul>') 458 .prop('id', 'folder-'+app.dataList.idgen) 459 .hide(); 460 461 var hidelink = $('<a>') 462 .prop('href', '') 463 .append('hide') 464 .hide(); 465 var showlink = $('<a>') 466 .prop('href', '') 467 .append('show'); 468 469 var playlink = $('<a>') 470 .prop('href', '') 471 .append('play'); 472 var stoplink = $('<a>') 473 .prop('href', '') 474 .append('stop') 475 .hide(); 476 477 n.checkbox = $('<input>') 478 .prop('type', 'checkbox') 479 .prop('id', 'data-'+app.dataList.idgen) 480 .prop('name', n.uri); 481 n.checkbox.change(app.dataList.selectData); 482 var item = $('<li>') 483 .append(n.checkbox) 484 .append($('<label>') 485 .prop('for', 'data-'+app.dataList.idgen) 486 .append(file.path[i])) 487 .append(' ') 488 .append(hidelink) 489 .append(showlink) 490 .append(' ') 491 .append(playlink) 492 .append(stoplink) 493 .append(n.ul) 494 app.dataList.idgen++; 495 cur.ul.append(item); 496 cur.children[file.path[i]] = n; 497 app.dataList.current[n.uri] = n; 498 499 var foldertoggler = function(){ 500 hidelink.toggle(); 501 showlink.toggle(); 502 n.ul.toggle(); 503 return false; 504 }; 505 hidelink.click(foldertoggler); 506 showlink.click(foldertoggler); 507 508 playlink.click(function(){ 509 playlink.toggle(); 510 stoplink.toggle(); 511 app.dataPlayer.play(n); 512 return false; 513 }); 514 stoplink.click(function(){ 515 playlink.toggle(); 516 stoplink.toggle(); 517 app.dataPlayer.stop(n); 518 return false; 519 }); 520 } 521 prev = cur; 522 cur = cur.children[file.path[i]]; 523 } 524 525 file.parent = cur; 526 file.checkbox = $('<input>') 527 .prop('type', 'checkbox') 528 .prop('id', 'data-'+app.dataList.idgen) 529 .prop('name', file.uri); 530 file.checkbox.change(app.dataList.selectData); 531 var item = $('<li>') 532 .append(file.checkbox) 533 .append($('<label>') 534 .prop('for', 'data-'+app.dataList.idgen) 535 .append(file.name)) 536 app.dataList.idgen++; 537 cur.ul.append(item); 538 cur.children[file.name] = file; 539 app.dataList.current[file.uri] = file; 540 541 if(cur.checkbox && cur.checkbox.prop('checked')){ 542 file.checkbox.prop('checked', true); 543 app.dataList.updateData(file); 544 } 545 }; 546 547 app.dataList.updateData = function(cur){ 548 if(cur.checkbox.prop('checked')){ 549 app.dataList.loadLayer(cur.uri); 550 } else { 551 app.dataList.unloadLayer(cur.uri); 552 } 553 }; 554 555 app.dataList.updateCheckboxes = function(cur){ 556 if(cur.checkbox.prop('checked')){ 557 app.dataList.check(cur); 558 } else { 559 app.dataList.uncheck(cur); 560 } 561 }; 562 563 app.dataList.selectData = function(e){ 564 var cur = app.dataList.current[e.target.name]; 565 if(!('children' in cur)){ 566 app.dataList.updateData(cur); 567 } 568 app.dataList.updateCheckboxes(cur); 569 }; 570 571 app.dataList.changeChildren = function rec(cur, val){ 572 cur.checkbox.prop('checked', val); 573 if('children' in cur){ 574 for(var child in cur.children){ 575 rec(cur.children[child], val); 576 } 577 } else { 578 app.dataList.updateData(cur); 579 } 580 }; 581 582 app.dataList.check = function(cur){ 583 // Check all parents 584 var p = cur.parent; 585 while(p.checkbox != null){ 586 p.checkbox.prop('checked', true); 587 p = p.parent; 588 } 589 590 // Check all children 591 for(var child in cur.children){ 592 app.dataList.changeChildren(cur.children[child], true); 593 } 594 }; 595 596 app.dataList.uncheck = function(cur){ 597 // Uncheck empty parents 598 var p = cur.parent; 599 while(p.checkbox != null && p.checkbox.prop('checked')){ 600 var cc = false; 601 for(var child in p.children){ 602 cc = cc || p.children[child].checkbox.prop('checked'); 603 } 604 if(cc){ 605 break; 606 } 607 p.checkbox.prop('checked', false); 608 p = p.parent; 609 } 610 611 // Uncheck all children 612 for(var child in cur.children){ 613 app.dataList.changeChildren(cur.children[child], false); 614 } 615 }; 616 617 618 app.dataList.loadLayer = function(uri){ 619 app.dataDisplay.loadLayer('/get'+uri); 620 }; 621 622 app.dataList.unloadLayer = function(uri){ 623 app.dataDisplay.unloadLayer('/get'+uri); 624 }; 625 626 627 /*******************/ 628 /*** DataExtract ***/ 629 /*******************/ 630 631 app.dataExtract = {}; 632 app.dataExtract.current = null; 633 634 app.dataExtract.init = function(){ 635 $('#dataextract button:contains("Refresh")').click(app.dataExtract.display); 636 $('#dataextract input').keypress(function(e){ 637 if(e.keyCode == 13){ 638 app.dataExtract.display(); 639 } 640 }); 641 $('#dataextract button:contains("Clear")').click(app.dataExtract.clear); 642 }; 643 644 app.dataExtract.display = function(){ 645 if(app.dataExtract.current){ 646 app.dataDisplay.unloadLayer('/extract/' + app.dataExtract.current); 647 } 648 app.dataExtract.current = $('#dataextract input').val(); 649 app.dataDisplay.loadLayer('/extract/' + app.dataExtract.current); 650 }; 651 652 app.dataExtract.clear = function(){ 653 if(app.dataExtract.current){ 654 app.dataDisplay.unloadLayer('/extract/' + app.dataExtract.current); 655 app.dataExtract.current = null; 656 } 657 }; 658 659 660 661 /******************/ 662 /*** DataPlayer ***/ 663 /******************/ 664 665 app.dataPlayer = {}; 666 app.dataPlayer.current = {}; 667 app.dataPlayer.updateFrequency = 200; 668 app.dataPlayer.time = 0; 669 670 app.dataPlayer.init = function(){ 671 app.dataPlayer.intervalId = setInterval(app.dataPlayer.update, app.dataPlayer.updateFrequency); 672 }; 673 674 app.dataPlayer.updateInterval = function(){ 675 clearInterval(app.dataPlayer.intervalId); 676 app.dataPlayer.intervalId = setInterval(app.dataPlayer.update, app.dataPlayer.updateFrequency); 677 }; 678 679 app.dataPlayer.play = function(cur){ 680 app.dataPlayer.updateKeys(cur); 681 if(cur.keys.length == 0){ 682 alert("ERROR: No number in directory."); 683 return; 684 } 685 app.dataList.uncheck(cur); 686 cur.checkbox.prop('checked', true); 687 cur.playIndex = 0; 688 app.dataPlayer.current[cur.uri] = cur; 689 for(var key in cur.context){ 690 var child = cur.children[cur.context[key]]; 691 child.checkbox.prop('checked', true); 692 if(!('children' in child)){ 693 app.dataList.updateData(child); 694 } 695 app.dataList.updateCheckboxes(child); 696 } 697 }; 698 699 app.dataPlayer.updateKeys = function(cur){ 700 var keys = Object.keys(cur.children); 701 cur.keys = keys.map(Number).filter(function(x){ return !isNaN(x); }).sort(function(l,r){return l-r;}); 702 cur.context = keys.filter(function(x){ return isNaN(Number(x)); }); 703 }; 704 705 app.dataPlayer.stop = function(cur){ 706 delete app.dataPlayer.current[cur.uri]; 707 delete cur.keys; 708 delete cur.context; 709 delete cur.playIndex; 710 }; 711 712 app.dataPlayer.update = function(){ 713 for(var key in app.dataPlayer.current){ 714 var cur = app.dataPlayer.current[key]; 715 var prev = cur.children[cur.keys[cur.playIndex]]; 716 cur.playIndex++; 717 if(cur.playIndex >= cur.keys.length){ 718 app.dataPlayer.updateKeys(cur); 719 if(cur.playIndex >= cur.keys.length){ 720 cur.playIndex = 0; 721 } 722 } 723 var next = cur.children[cur.keys[cur.playIndex]]; 724 725 prev.checkbox.prop('checked', false); 726 app.dataList.updateData(prev); 727 next.checkbox.prop('checked', true); 728 app.dataList.updateData(next); 729 } 730 }; 731 732 733 /*****************/ 734 /*** CoordInfo ***/ 735 /*****************/ 736 737 app.coordInfo = {}; 738 app.coordInfo.init = function(){ 739 app.coordInfo.element = $('#coordInfo'); 740 app.coordInfo.enable(); 741 }; 742 743 app.coordInfo.enable = function(){ 744 app.map.on('pointermove', app.coordInfo.update); 745 app.coordInfo.element.show(); 746 }; 747 748 app.coordInfo.disable = function(){ 749 app.coordInfo.element.hide(); 750 app.map.un('pointermove', app.coordInfo.update); 751 }; 752 753 app.coordInfo.update = function(evt){ 754 var coord = ol.proj.transform(app.map.getEventCoordinate(evt.originalEvent), app.map.getView().getProjection(), 'EPSG:4326'); 755 app.coordInfo.element.text(coord[1] + ', ' + coord[0]); 756 }; 757 758 759 /*******************/ 760 /*** FeatureInfo ***/ 761 /*******************/ 762 763 app.featureInfo = {}; 764 765 app.featureInfo.init = function(){ 766 app.featureInfo.element = $('#featureinfo'); 767 app.featureInfo.static = $('#featureinfo-static'); 768 app.featureInfo.dynamic = $('#featureinfo-dynamic'); 769 app.featureInfo.element.hide(); 770 app.featureInfo.enable(); 771 }; 772 773 app.featureInfo.enable = function(){ 774 app.map.on('pointermove', app.featureInfo.updateMove); 775 app.map.on('click', app.featureInfo.updateClick); 776 }; 777 778 app.featureInfo.disable = function(){ 779 app.map.un('pointermove', app.featureInfo.updateMove); 780 app.map.un('click', app.featureInfo.updateClick); 781 }; 782 783 app.featureInfo.updateMove = function(evt){ 784 if(evt.dragging){ 785 app.featureInfo.element.hide(); 786 return; 787 } 788 app.featureInfo.display(evt); 789 }; 790 791 app.featureInfo.updateClick = function(evt){ 792 app.featureInfo.display(evt); 793 }; 794 795 app.featureInfo.display = function(evt){ 796 app.featureInfo.element.css({ 797 left: (evt.pixel[0] + 10) + 'px', 798 top: evt.pixel[1] + 'px' 799 }); 800 var feature = app.map.forEachFeatureAtPixel(evt.pixel, function(feature, layer) { 801 return feature; 802 }); 803 if(feature && feature.get('info')){ 804 if(feature.get('display') == 'path'){ 805 var dtime = app.featureInfo.interpolateTime(feature.getGeometry(), evt.coordinate); 806 var date = new Date(1000*(feature.get('timestamp') + dtime*15)); 807 var desc = 'index: '+dtime+'<br>'; 808 desc += 'date: '+date.toISOString()+'<br>'; 809 app.featureInfo.dynamic.html(desc); 810 } else { 811 app.featureInfo.dynamic.html(''); 812 } 813 app.featureInfo.static.html(feature.get('info')); 814 app.featureInfo.element.show(); 815 } else { 816 app.featureInfo.element.hide(); 817 } 818 }; 819 820 app.featureInfo.interpolateTime = function(polyline, coord){ 821 var closest = polyline.getClosestPoint(coord); 822 var best = 1000; 823 var bestStart = -1; 824 var bestEnd = -1; 825 var bestI = -1; 826 var i = 0; 827 var points = polyline.getCoordinates(); 828 for(var i=0; i<points.length-1; i++){ 829 var start = points[i]; 830 var end = points[i+1]; 831 var dist = Math.abs((end[0]-start[0])*closest[1] - (end[1]-start[1])*closest[0] + end[1]*start[0] - end[0]*start[1]) / Math.sqrt(Math.pow(end[0]-start[0], 2) + Math.pow(end[1]-start[1], 2)); 832 if(dist<best){ 833 best = dist; 834 bestStart = start; 835 bestEnd = end; 836 bestI = i; 837 } 838 } 839 840 if(bestI == -1){ 841 return 0; 842 } 843 844 var distClosest = app.geometry.equirectangular(bestStart[1], bestStart[0], closest[1], closest[0]); 845 var distEnd = app.geometry.equirectangular(bestStart[1], bestStart[0], bestEnd[1], bestEnd[0]); 846 var ratio = distClosest / distEnd; 847 return bestI + ratio; 848 }; 849 850 851 /***************/ 852 /*** Control ***/ 853 /***************/ 854 855 app.control = {}; 856 857 app.control.OpenConfigControl = function(opt_options){ 858 var options = opt_options || {}; 859 860 var button = document.createElement('button'); 861 button.innerHTML = '⚙'; 862 863 button.addEventListener('click', function(e){ $('#config').toggle() }, false); 864 button.addEventListener('touchstart', function(e){ $('#config').toggle() }, false); 865 866 var element = document.createElement('div'); 867 element.className = 'open-config ol-unselectable ol-control'; 868 element.appendChild(button); 869 870 ol.control.Control.call(this, { 871 element: element, 872 target: options.target 873 }); 874 }; 875 ol.inherits(app.control.OpenConfigControl, ol.control.Control); 876 877 app.control.OpenDatalist = function(opt_options){ 878 var options = opt_options || {}; 879 880 var button = document.createElement('button'); 881 button.innerHTML = '«'; 882 883 var toggler = function(e){ 884 $('#datalist').toggle(); 885 if(button.innerHTML == '«'){ 886 button.innerHTML = '»'; 887 $('.open-datalist').css('right', '20.5em'); 888 } 889 else{ 890 button.innerHTML = '«'; 891 $('.open-datalist').css('right', '.5em'); 892 } 893 }; 894 button.addEventListener('click', toggler, false); 895 button.addEventListener('touchstart', toggler, false); 896 897 var element = document.createElement('div'); 898 element.className = 'open-datalist ol-unselectable ol-control'; 899 element.appendChild(button); 900 901 ol.control.Control.call(this, { 902 element: element, 903 target: options.target 904 }); 905 }; 906 ol.inherits(app.control.OpenDatalist, ol.control.Control); 907 908 909 /************/ 910 /*** Menu ***/ 911 /************/ 912 913 app.menu = {}; 914 915 app.menu.init = function(){ 916 $('#config ul').menu(); 917 918 $('#config ul li').click(function(e){ 919 switch($(this).text()){ 920 case 'Enable coord': 921 app.coordInfo.enable(); 922 $('#config ul li:contains("Enable coord")').toggle(); 923 $('#config ul li:contains("Disable coord")').toggle(); 924 break; 925 case 'Disable coord': 926 app.coordInfo.disable(); 927 $('#config ul li:contains("Enable coord")').toggle(); 928 $('#config ul li:contains("Disable coord")').toggle(); 929 break; 930 case 'Set player speed': 931 var tmp = prompt("Player update frequency (milliseconds)", app.dataPlayer.updateFrequency); 932 if(tmp){ 933 app.dataPlayer.updateFrequency = parseInt(tmp); 934 app.dataPlayer.updateInterval(); 935 } 936 break; 937 } 938 }); 939 940 $('ul#config-measure li').click(function(e){ 941 switch($(this).text()){ 942 case 'Enable': 943 app.measure.addInteraction(); 944 app.map.on('pointermove', app.measure.pointerMoveHandler); 945 app.featureInfo.disable(); 946 $('ul#config-measure li:contains("Enable")').toggle(); 947 $('ul#config-measure li:contains("Disable")').toggle(); 948 break; 949 case 'Disable': 950 app.featureInfo.enable(); 951 app.map.un('pointermove', app.measure.pointerMoveHandler); 952 app.map.removeInteraction(app.measure.draw); 953 app.measure.draw = null; 954 $('ul#config-measure li:contains("Enable")').toggle(); 955 $('ul#config-measure li:contains("Disable")').toggle(); 956 break; 957 case 'Clear': 958 app.measure.source.clear(); 959 app.measure.tooltip_list.forEach(function(e){ 960 app.map.removeOverlay(e); 961 }); 962 app.measure.tooltip_list.length = 0; 963 if(app.measure.draw){ 964 app.map.removeInteraction(app.measure.draw); 965 app.measure.addInteraction(); 966 } 967 break; 968 } 969 }); 970 971 $('ul#config-layer li').click(function(e){ 972 switch($(this).text()){ 973 case 'OSM': 974 app.mainLayer = new ol.layer.Tile({ source: new ol.source.OSM() }); 975 break; 976 case 'Bing': 977 app.mainLayer = new ol.layer.Tile({ source: new ol.source.BingMaps({ 978 key: 'Ak-dzM4wZjSqTlzveKz5u0d4IQ4bRzVI309GxmkgSVr1ewS6iPSrOvOKhA-CJlm3', 979 imagerySet: 'AerialWithLabels', 980 maxZoom: 19 981 }) }); 982 break; 983 case 'Bing (no labels)': 984 app.mainLayer = new ol.layer.Tile({ source: new ol.source.BingMaps({ 985 key: 'Ak-dzM4wZjSqTlzveKz5u0d4IQ4bRzVI309GxmkgSVr1ewS6iPSrOvOKhA-CJlm3', 986 imagerySet: 'Aerial', 987 maxZoom: 19 988 }) }); 989 break; 990 } 991 app.map.getLayers().setAt(0, app.mainLayer); 992 }); 993 994 $('ul#config-pathdraw li').click(function(e){ 995 switch($(this).text()){ 996 case 'Set resolution': 997 var tmp = prompt("Path points resolution", app.dataDisplay.pathPointResolution); 998 if(tmp){ 999 app.dataDisplay.pathPointResolution = parseInt(tmp); 1000 app.dataDisplay.reloadPathes(); 1001 } 1002 break; 1003 case 'No points': 1004 app.dataDisplay.pathPointMode = 0; 1005 app.dataDisplay.reloadPathes(); 1006 break; 1007 case 'Endpoints': 1008 app.dataDisplay.pathPointMode = 1; 1009 app.dataDisplay.reloadPathes(); 1010 break; 1011 case 'All points': 1012 app.dataDisplay.pathPointMode = 2; 1013 app.dataDisplay.reloadPathes(); 1014 break; 1015 } 1016 }); 1017 1018 $('ul#config-heatmap li').click(function(e){ 1019 switch($(this).text()){ 1020 case 'Set blur': 1021 var tmp = prompt("Heatmap blur", app.dataDisplay.heatmapBlur); 1022 if(tmp){ 1023 app.dataDisplay.heatmapBlur = parseInt(tmp); 1024 app.dataDisplay.reloadHeatmaps(); 1025 } 1026 break; 1027 case 'Set radius': 1028 var tmp = prompt("Heatmap radius", app.dataDisplay.heatmapRadius); 1029 if(tmp){ 1030 app.dataDisplay.heatmapRadius = parseInt(tmp); 1031 app.dataDisplay.reloadHeatmaps(); 1032 } 1033 break; 1034 } 1035 }); 1036 }; 1037 1038 1039 /**********************/ 1040 /*** Initialization ***/ 1041 /**********************/ 1042 1043 $(function(){ 1044 app.map = new ol.Map({ 1045 controls: ol.control.defaults().extend([ 1046 new app.control.OpenConfigControl(), 1047 new app.control.OpenDatalist() 1048 ]), 1049 target: 'map', 1050 layers: [ app.mainLayer, app.measure.layer ], 1051 view: new ol.View({ 1052 center: ol.proj.transform([-8.621953, 41.162142], 'EPSG:4326', 'EPSG:3857'), 1053 zoom: 13 1054 }) 1055 }); 1056 1057 app.menu.init(); 1058 app.coordInfo.init(); 1059 app.featureInfo.init(); 1060 app.dataList.init(); 1061 app.dataExtract.init(); 1062 app.dataPlayer.init(); 1063 });