1 2//----------------------------------- 3// this chunk initiates the main map 4 5// layer definitions 6 7var mainStyle = new ol.style.Style({ 8 fill: new ol.style.Fill({ 9 color: 'rgba(220, 220, 255, 0.3)', 10 }), 11 stroke: new ol.style.Stroke({ 12 color: 'rgb(29,107,191)', //'rgb(49, 127, 211)', 13 width: 2.5, 14 }), 15}); 16var mainLayer = new ol.layer.Vector({ 17 title: "Main boundary", 18 style: mainStyle, 19}); 20 21var comparisonStyle = new ol.style.Style({ 22 fill: new ol.style.Fill({ 23 color: 'rgba(255, 255, 255, 0)', // fully transparent 24 }), 25 stroke: new ol.style.Stroke({ 26 color: 'rgba(255, 0, 0, 0.8)', 27 width: 1.5, 28 lineDash: [10,10] 29 }), 30}); 31var comparisonLayer = new ol.layer.Vector({ 32 title: "Comparison boundary", 33 style: comparisonStyle, 34}); 35 36// map 37 38var baseMaps = { 39 'google': new ol.source.XYZ({ 40 attributions: 'Satellite Imagery <a href="https://www.google.com" target="_blank">© Google</a> ', 41 url: 42 'https://www.google.com/maps/vt?lyrs=s&x={x}&y={y}&z={z}', 43 maxZoom: 20, 44 crossOrigin: 'anonymous' // necessary for converting map to img during pdf generation: https://stackoverflow.com/questions/66671183/how-to-export-map-image-in-openlayer-6-without-cors-problems-tainted-canvas-iss 45 }), 46 'esri': new ol.source.XYZ({ 47 attributions: 'ESRI World Street Map', 48 url: 49 'http://services.arcgisonline.com/arcgis/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}', 50 maxZoom: 20, 51 crossOrigin: 'anonymous' // necessary for converting map to img during pdf generation: https://stackoverflow.com/questions/66671183/how-to-export-map-image-in-openlayer-6-without-cors-problems-tainted-canvas-iss 52 }), 53 'osm': new ol.source.OSM({crossOrigin: 'anonymous'}), 54}; 55var baseMapLayer = new ol.layer.Tile({source: baseMaps.google}); 56 57function setBaseMap(name) { 58 baseMapLayer.setSource(baseMaps[name]); 59}; 60 61var map = new ol.Map({ 62 target: 'map', 63 controls: ol.control.defaults({attribution:false}).extend([new ol.control.FullScreen(), 64 new ol.control.ScaleLine({units: 'metric'}), 65 ]), 66 layers: [ 67 baseMapLayer, 68 mainLayer, 69 comparisonLayer 70 ], 71 view: new ol.View({ 72 center: ol.proj.fromLonLat([37.41, 8.82]), 73 zoom: 4 74 }) 75}); 76 77map.on('singleclick', function(evt) { 78 // get feats 79 let mainFeat = null; 80 let comparisonFeat = null; 81 map.forEachFeatureAtPixel(evt.pixel, function(feature, layer) { 82 if (layer === mainLayer) { 83 mainFeat = feature 84 } else if (layer === comparisonLayer) { 85 comparisonFeat = feature 86 }; 87 }); 88 // init and open popup for the found features 89 if (mainFeat != null | comparisonFeat != null) { 90 openFeatureComparePopup(mainFeat, comparisonFeat); 91 }; 92}); 93 94function toggleMainLayer() { 95 var check = document.getElementById('toggle-layer-main'); 96 mainLayer.setVisible(check.checked); 97}; 98 99function toggleComparisonLayer() { 100 var check = document.getElementById('toggle-layer-comparison'); 101 comparisonLayer.setVisible(check.checked); 102}; 103 104var switcher = document.getElementById('layer-switcher'); 105var control = new ol.control.Control({ 106 element: switcher 107}); 108map.addControl(control); 109 110 111 112 113 114 115// ------------------------------ 116// this loads and stores the geocontrast metadata csv 117 118var geoContrastMetadata = null; 119 120function onSuccess (data) { 121 geoContrastMetadata = data; 122 // update countries 123 updateCountryDropdown(); 124 // update main sources 125 updateMainSourceDropdown(); 126 // update comparison sources 127 updateComparisonSourceDropdown(); 128}; 129loadGeoContrastMetadata(onSuccess); 130 131 132 133 134 135/////////////////////// 136// file upload stuff 137 138/* 139<!--for details on local file reading, see https://www.html5rocks.com/en/tutorials/file/dndfiles/ --> 140<!--https://web.dev/read-files/ --> 141<!--https://gis.stackexchange.com/questions/138155/allow-client-to-upload-layers-to-openlayers-3 --> 142<!--https://gis.stackexchange.com/questions/368033/how-to-display-shapefiles-on-an-openlayers-web-mapping-application-that-are-prov 143--> 144*/ 145 146function handleMainFileUpload(evt) { 147 var files = evt.target.files; 148 149 // get file contents as a base64 encoded url string 150 const file = files[0]; 151 fileExtension = file.name.split('.').pop(); 152 //alert('local file selected: '+file.name+' - '+fileExtension); 153 154 if (fileExtension == 'geojson' | fileExtension == 'json') { 155 reader = new FileReader(); 156 reader.onload = function(e) { 157 // use reader results to create new source 158 var geojson = reader.result; 159 source = new ol.source.Vector({ 160 format: new ol.format.GeoJSON({}), 161 overlaps: false, 162 }); 163 // update the layer 164 updateMainLayerFromGeoJSON(source, geojson, zoomToExtent=true); 165 }; 166 // read as data url 167 reader.readAsText(file); 168 } else if (fileExtension == 'zip') { 169 // experiment with zipfile reading 170 // https://stuk.github.io/jszip/documentation/examples/read-local-file-api.html 171 reader = new FileReader(); 172 reader.onload = function(e) { 173 // use reader results to create new source 174 var raw = reader.result; 175 var zip = new JSZip(raw); 176 var paths = []; 177 for (filename in zip.files) { 178 if (filename.endsWith('.shp')) { 179 var path = file.name + '/' + filename; 180 var displayName = filename; 181 paths.push([path,displayName]); 182 }; 183 }; 184 updateMainFileDropdown(paths); 185 }; 186 reader.readAsBinaryString(file); 187 }; 188}; 189 190function handleComparisonFileUpload(evt) { 191 var files = evt.target.files; 192 193 // get file contents as a base64 encoded url string 194 const file = files[0]; 195 fileExtension = file.name.split('.').pop(); 196 //alert('local file selected: '+file.name+' - '+fileExtension); 197 198 if (fileExtension == 'geojson' | fileExtension == 'json') { 199 reader = new FileReader(); 200 reader.onload = function(e) { 201 // use reader results to create new source 202 var geojson = reader.result; 203 source = new ol.source.Vector({ 204 format: new ol.format.GeoJSON({}), 205 overlaps: false, 206 }); 207 // update the layer 208 updateComparisonLayerFromGeoJSON(source, geojson, zoomToExtent=true); 209 }; 210 // read as data url 211 reader.readAsText(file); 212 } else if (fileExtension == 'zip') { 213 // experiment with zipfile reading 214 // https://stuk.github.io/jszip/documentation/examples/read-local-file-api.html 215 reader = new FileReader(); 216 reader.onload = function(e) { 217 // use reader results to create new source 218 var raw = reader.result; 219 var zip = new JSZip(raw); 220 var paths = []; 221 for (filename in zip.files) { 222 if (filename.endsWith('.shp')) { 223 var path = file.name + '/' + filename; 224 var displayName = filename; 225 paths.push([path,displayName]); 226 }; 227 }; 228 updateComparisonFileDropdown(paths); 229 }; 230 reader.readAsBinaryString(file); 231 }; 232}; 233 234 235 236 237 238 239 240//////////////////////// 241// update file dropdown stuff 242 243function updateMainFileDropdown(paths) { 244 // activate and clear the dropdown 245 var select = document.getElementById('main-file-select'); 246 select.disabled = false;
247 select.innerHTML = ''; 248 // populate the dropdown 249 for ([path,displayName] of paths) { 250 var opt = document.createElement('option'); 251 opt.value = path; 252 opt.innerText = displayName; 253 select.appendChild(opt); 254 }; 255 // force change 256 mainFileDropdownChanged(); 257}; 258 259function updateComparisonFileDropdown(paths) { 260 // activate and clear the dropdown 261 var select = document.getElementById('comparison-file-select'); 262 select.disabled = false; 263 select.innerHTML = ''; 264 // populate the dropdown 265 for ([path,displayName] of paths) { 266 var opt = document.createElement('option'); 267 opt.value = path; 268 opt.innerText = displayName; 269 select.appendChild(opt); 270 }; 271 // force change 272 comparisonFileDropdownChanged(); 273}; 274 275 276 277 278 279 280 281////////////////////// 282// file dropdown changed 283function mainFileDropdownChanged() { 284 // first clear previous info 285 clearMainInfo(); 286 clearMainStats(); 287 clearMatchTable(); //clearMainNames(); 288 clearTotalEquality(); 289 // get file info 290 var file = document.getElementById('main-file-input').files[0]; 291 var path = document.getElementById('main-file-select').value; 292 var subPath = path.split('.zip/')[1]; // only the relative path inside the zipfile 293 // read 294 reader = new FileReader(); 295 reader.onload = function(e) { 296 // open zipfile 297 var raw = reader.result; 298 var zip = new JSZip(raw); 299 // prep args 300 var shpString = subPath; 301 var dbfString = subPath.replace('.shp', '.dbf'); 302 var prjString = subPath.replace('.shp', '.prj'); 303 // try to read projection 304 let prjData = null; 305 try { 306 prjRaw = zip.file(prjString).asText(); 307 //console.log(prjRaw); 308 prjData = proj4(prjRaw); 309 } catch (err) { 310 console.log('could not read prj file, assuming WGS84'); 311 //console.log(err); 312 }; 313 314 function processData(geojson) { 315 // geojson returned 316 source = new ol.source.Vector({ 317 format: new ol.format.GeoJSON({}), 318 overlaps: false, 319 }); 320 // update the layer 321 updateMainLayerFromGeoJSON(source, geojson, zoomToExtent=true); 322 }; 323 324 // load using shapefile-js 325 // https://github.com/calvinmetcalf/shapefile-js 326 var waiting = Promise.all([shp.parseShp(zip.file(shpString).asArrayBuffer(), prjData), 327 shp.parseDbf(zip.file(dbfString).asArrayBuffer()) 328 ]) 329 waiting.then(function(result){ 330 var geoj = shp.combine(result); 331 processData(geoj); 332 }); 333 }; 334 reader.readAsBinaryString(file); 335}; 336 337function comparisonFileDropdownChanged() { 338 // first clear previous info 339 clearComparisonInfo(); 340 clearComparisonStats(); 341 clearMatchTable(); //clearComparisonNames(); 342 clearTotalEquality(); 343 // get file info 344 var file = document.getElementById('comparison-file-input').files[0]; 345 var path = document.getElementById('comparison-file-select').value; 346 var subPath = path.split('.zip/')[1]; // only the relative path inside the zipfile 347 // read 348 reader = new FileReader(); 349 reader.onload = function(e) { 350 // open zipfile 351 var raw = reader.result; 352 var zip = new JSZip(raw); 353 // prep args 354 var shpString = subPath; 355 var dbfString = subPath.replace('.shp', '.dbf'); 356 var prjString = subPath.replace('.shp', '.prj'); 357 // try to read projection 358 let prjData = null; 359 try { 360 prjRaw = zip.file(prjString).asText(); 361 //console.log(prjRaw); 362 prjData = proj4(prjRaw); 363 } catch (err) { 364 console.log('could not read prj file, assuming WGS84'); 365 //console.log(err); 366 }; 367 368 function processData(geojson) { 369 // geojson returned 370 source = new ol.source.Vector({ 371 format: new ol.format.GeoJSON({}), 372 overlaps: false, 373 }); 374 // update the layer 375 updateComparisonLayerFromGeoJSON(source, geojson, zoomToExtent=true); 376 }; 377 378 // read using shapefile-js 379 // https://github.com/calvinmetcalf/shapefile-js 380 var waiting = Promise.all([shp.parseShp(zip.file(shpString).asArrayBuffer(), prjData), 381 shp.parseDbf(zip.file(dbfString).asArrayBuffer()) 382 ]) 383 waiting.then(function(result){ 384 var geoj = shp.combine(result); 385 processData(geoj); 386 }); 387 }; 388 reader.readAsBinaryString(file); 389}; 390 391 392 393 394 395//////////////////////// 396// update map layer stuff 397 398function zoomMainFeature(ID) { 399 var features = mainLayer.getSource().getFeatures(); 400 // find feature 401 for (feature of features) { 402 if (feature.getId() == ID) { 403 break; 404 }; 405 }; 406 // zoom to extent 407 map.getView().fit(feature.getGeometry().getExtent()); 408 // zoom out a little 409 //map.getView().setZoom(map.getView().getZoom()-1); 410}; 411 412function zoomComparisonFeature(ID) { 413 var features = comparisonLayer.getSource().getFeatures(); 414 // find feature 415 for (feature of features) { 416 if (feature.getId() == ID) { 417 break; 418 }; 419 }; 420 // zoom to extent 421 map.getView().fit(feature.getGeometry().getExtent()); 422 // zoom out a little 423 //map.getView().setZoom(map.getView().getZoom()-1); 424}; 425 426function updateMainLayer(zoomToExtent=false) { 427 // get main params 428 iso = document.getElementById('country-select').value; 429 // get comparison params 430 level = document.getElementById('main-admin-level-select').value; 431 sourceName = document.getElementById('main-boundary-select').value; 432 if (sourceName == null) { 433 return 434 }; 435 // create and set new source 436 var source = new ol.source.Vector({ 437 overlaps: false, 438 }); 439 mainLayer.setSource(source); 440 // zoom after source has finished loading 441 if (zoomToExtent) { 442 source.on('change', function() { 443 //alert('main loaded, zoom to bbox: '+source.getExtent()); 444 // zoom to extent 445 map.getView().fit(source.getExtent()); 446 // zoom out a little 447 map.getView().setZoom(map.getView().getZoom()-1); 448 }); 449 }; 450 // update various divs after source has finished loading 451 source.on('change', function() { 452 //alert('main loaded, update info'); 453 features = source.getFeatures(); 454 updateMainStats(features); 455 updateMainFieldsDropdown(features); 456 //updateMainNames(features); 457 updateMainInfo(features);
458 calcMatchTable(); 459 //calcBoundaryMakeupTables(); 460 }); 461 // notify if failed to load source 462 source.on(['error','featuresloaderror'], function() { 463 alert('Failed to load features for '+sourceName+' at '+level+'. Please choose another sourceor level.'); 464 }); 465 // load the source 466 loadGeoContrastSource(source, iso, level, sourceName); 467}; 468 469function updateComparisonLayer(zoomToExtent=false) { 470 // get main params 471 iso = document.getElementById('country-select').value; 472 // get comparison params 473 level = document.getElementById('comparison-admin-level-select').value; 474 sourceName = document.getElementById('comparison-boundary-select').value; 475 if (sourceName == null) { 476 return 477 }; 478 // create and set new source 479 var source = new ol.source.Vector({ 480 overlaps: false, 481 }); 482 comparisonLayer.setSource(source); 483 // zoom after source has finished loading 484 if (zoomToExtent) { 485 source.on('change', function() { 486 //alert('comparison loaded, zoom to bbox: '+source.getExtent()); 487 // zoom to extent 488 map.getView().fit(source.getExtent()); 489 // zoom out a little 490 map.getView().setZoom(map.getView().getZoom()-1); 491 }); 492 }; 493 // update various divs after source has finished loading 494 source.on('change', function() { 495 //alert('comparison loaded, update info'); 496 features = source.getFeatures(); 497 updateComparisonStats(features); 498 updateComparisonFieldsDropdown(features); 499 //updateComparisonNames(features); 500 updateComparisonInfo(features);
501 calcMatchTable(); 502 //calcBoundaryMakeupTables(); 503 }); 504 // notify if failed to load source 505 source.on(['error','featuresloaderror'], function() { 506 alert('Failed to load features for '+sourceName+' at '+level+'. Please choose another source or level.'); 507 }); 508 // load the source 509 loadGeoContrastSource(source, iso, level, sourceName); 510}; 511 512function clearMainLayer() { 513 source = new ol.source.Vector({ 514 url: null, 515 format: new ol.format.TopoJSON({}), 516 overlaps: false, 517 }); 518 mainLayer.setSource(source); 519}; 520 521function clearComparisonLayer() { 522 source = new ol.source.Vector({ 523 url: null, 524 format: new ol.format.TopoJSON({}), 525 overlaps: false, 526 }); 527 comparisonLayer.setSource(source); 528}; 529 530function updateMainLayerFromGeoJSON(source, geojson, zoomToExtent=false) { 531 // set the new source 532 mainLayer.setSource(source); 533 // zoom to new source after source has finished loading 534 if (zoomToExtent) { 535 source.on('change', function() { 536 //alert('new bbox: '+source.getExtent()); 537 // get combined extent of main and uploaded file 538 extent = ol.extent.createEmpty(); 539 ol.extent.extend(extent, source.getExtent()); 540 ol.extent.extend(extent, comparisonLayer.getSource().getExtent()); 541 // zoom to extent 542 map.getView().fit(extent); 543 // zoom out a little 544 map.getView().setZoom(map.getView().getZoom()-1); 545 }); 546 }; 547 // update various divs after source has finished loading 548 source.on('change', function() { 549 //alert('main loaded, update info'); 550 features = source.getFeatures(); 551 updateMainStats(features); 552 updateMainFieldsDropdown(features); 553 //updateMainNames(features); 554 updateMainInfo(features);
555 calcMatchTable(); 556 }); 557 // notify if failed to load source 558 source.on(['error','featuresloaderror'], function() { 559 alert('Failed to load uploaded file.'); 560 }); 561 // load the geojson data 562 loadFromGeoJSON(source, geojson); 563}; 564 565function updateComparisonLayerFromGeoJSON(source, geojson, zoomToExtent=false) { 566 // set the new source 567 comparisonLayer.setSource(source); 568 // zoom to new source after source has finished loading 569 if (zoomToExtent) { 570 source.on('change', function() { 571 //alert('new bbox: '+source.getExtent()); 572 // get combined extent of main and uploaded file 573 extent = ol.extent.createEmpty(); 574 ol.extent.extend(extent, source.getExtent()); 575 ol.extent.extend(extent, mainLayer.getSource().getExtent()); 576 // zoom to extent 577 map.getView().fit(extent); 578 // zoom out a little 579 map.getView().setZoom(map.getView().getZoom()-1); 580 }); 581 }; 582 // update various divs after source has finished loading 583 source.on('change', function() { 584 //alert('main loaded, update info'); 585 features = source.getFeatures(); 586 updateComparisonStats(features); 587 updateComparisonFieldsDropdown(features); 588 //updateComparisonNames(features); 589 updateComparisonInfo(features);
590 calcMatchTable(); 591 }); 592 // notify if failed to load source 593 source.on(['error','featuresloaderror'], function() { 594 alert('Failed to load uploaded file.'); 595 }); 596 // load the geojson data 597 loadFromGeoJSON(source, geojson); 598}; 599 600 601 602 603 604 605 606 607////////////////////////////// 608// populating the dropdowns 609 610function updateCountryDropdown() { 611 // NOTE: requires that geoContrastMetadata has already been populated 612 // get country dropdown 613 var select = document.getElementById('country-select'); 614 // clear all existing dropdown options 615 select.innerHTML = ''; 616 // get list of unique countries 617 var countriesSeen = []; 618 var countries = []; 619 for (row of geoContrastMetadata) { 620 if (row.length <= 1) { 621 // ignore empty rows 622 continue; 623 }; 624 var name = row.boundaryName; 625 var iso = row.boundaryISO; 626 if (iso === undefined) { 627 // must be other empty rows? 628 continue; 629 }; 630 var country = {'name':name, 'iso':iso}; 631 if (!(countriesSeen.includes(country.iso))) { 632 // only add if hasn't already been added 633 countries.push(country); 634 countriesSeen.push(country.iso); 635 }; 636 }; 637 // sort 638 countries.sort(function( a,b ){ if (a.name > b.name) {return 1} else {return -1} }) 639 // add new options from geoContrastMetadata 640 for (country of countries) { 641 var opt = document.createElement("option"); 642 opt.value = country.iso; 643 opt.textContent = country.name; 644 select.appendChild(opt); 645 }; 646 // set the country to get-param if specified 647 const urlParams = new URLSearchParams(window.location.search); 648 var iso = urlParams.get('country'); 649 if ((iso != null) & (iso != select.value)) { 650 select.value = iso; 651 } else { 652 // default country 653 select.value = 'NIC'; 654 }; 655 // force dropdown change 656 countryChanged(); 657}; 658 659// admin level 660 661function updateMainAdminLevelDropdown() { 662 // NOTE: requires that geoContrastMetadata has already been populated 663 // get admin-level dropdown 664 var select = document.getElementById('main-admin-level-select'); 665 var selectVal = select.value; 666 // clear all existing dropdown options 667 select.innerHTML = ''; 668 // get geoContrast metadata 669 var metadata = geoContrastMetadata; 670 // get current country and comparison source 671 var currentIso = document.getElementById('country-select').value; 672 var currentSource = document.getElementById('main-boundary-select').value; 673 // find available admin-levels for country 674 var adminLevelsSeen = []; 675 var adminLevels = []; 676 for (var i = 1; i < metadata.length; i++) { 677 var row = metadata[i]; 678 if (row.length <= 1) { 679 // ignore empty rows 680 i++; 681 continue; 682 }; 683 var iso = row.boundaryISO; 684 var lvl = row.boundaryType; 685 if (iso == currentIso) { 686 if (!(adminLevelsSeen.includes(lvl))) { 687 // only add if hasn't already been added 688 adminLevels.push(lvl); 689 adminLevelsSeen.push(lvl); 690 }; 691 }; 692 }; 693 // sort 694 adminLevels.sort(); 695 // add new options from geoContrastMetadata 696 for (lvl of adminLevels) { 697 var opt = document.createElement("option"); 698 opt.value = lvl; 699 opt.textContent = lvl; 700 select.appendChild(opt); 701 }; 702 // set the adm level to get-param if specified 703 const urlParams = new URLSearchParams(window.location.search); 704 var lvl = urlParams.get('mainLevel'); 705 if ((lvl != null) & (lvl != select.value[3])) { 706 select.value = 'ADM'+lvl; 707 } else { 708 // default main level 709 select.value = 'ADM1'; 710 }; 711 // force dropdown change 712 mainAdminLevelChanged(); 713}; 714 715function updateComparisonAdminLevelDropdown() { 716 // NOTE: requires that geoContrastMetadata has already been populated 717 // get admin-level dropdown 718 var select = document.getElementById('comparison-admin-level-select'); 719 var selectVal = select.value; 720 // clear all existing dropdown options 721 select.innerHTML = ''; 722 // get geoContrast metadata 723 var metadata = geoContrastMetadata; 724 // get current country and comparison source 725 var currentIso = document.getElementById('country-select').value; 726 var currentSource = document.getElementById('comparison-boundary-select').value; 727 // find available admin-levels for country 728 var adminLevelsSeen = []; 729 var adminLevels = []; 730 for (var i = 1; i < metadata.length; i++) { 731 var row = metadata[i]; 732 if (row.length <= 1) { 733 // ignore empty rows 734 i++; 735 continue; 736 }; 737 var iso = row.boundaryISO; 738 var lvl = row.boundaryType; 739 if (iso == currentIso) { 740 if (!(adminLevelsSeen.includes(lvl))) { 741 // only add if hasn't already been added 742 adminLevels.push(lvl); 743 adminLevelsSeen.push(lvl); 744 }; 745 }; 746 }; 747 // sort 748 adminLevels.sort(); 749 // add new options from geoContrastMetadata 750 for (lvl of adminLevels) { 751 var opt = document.createElement("option"); 752 opt.value = lvl; 753 opt.textContent = lvl; 754 select.appendChild(opt); 755 }; 756 // set the adm level to get-param if specified
757 const urlParams = new URLSearchParams(window.location.search); 758 var lvl = urlParams.get('comparisonLevel'); 759 if ((lvl != null) & (lvl != select.value[3])) { 760 select.value = 'ADM'+lvl; 761 } else { 762 // default comparison level 763 select.value = 'ADM1'; 764 }; 765 // force dropdown change 766 comparisonAdminLevelChanged(); 767}; 768 769// sources 770 771function openMainSourcePopup() { 772 popup = document.getElementById('main-source-popup'); 773 popup.className = "popup"; 774 tableDiv = popup.querySelector('#main-sources-table').parentNode; 775 tableDiv.scrollTop = 0; 776}; 777 778function closeMainSourcePopup() { 779 popup = document.getElementById('main-source-popup'); 780 popup.className = "popup is-hidden is-visually-hidden"; 781}; 782 783function openComparisonSourcePopup() { 784 popup = document.getElementById('comparison-source-popup'); 785 popup.className = "popup"; 786 tableDiv = popup.querySelector('#comparison-sources-table').parentNode; 787 tableDiv.scrollTop = 0; 788}; 789 790function closeComparisonSourcePopup() { 791 popup = document.getElementById('comparison-source-popup'); 792 popup.className = "popup is-hidden is-visually-hidden"; 793}; 794 795function updateMainSourceTable() { 796 //console.log('update main source table'); 797 var currentIso = document.getElementById('country-select').value; 798 var currentLevel = document.getElementById('main-admin-level-select').value; 799 // clear sources table 800 var table = document.querySelector('#main-sources-table tbody'); 801 table.innerHTML = ''; 802 // get geoContrast metadata 803 var metadata = geoContrastMetadata; 804 // get list of sources that match the specified country 805 var sourceRows = []; 806 for (var i = 1; i < metadata.length; i++) { 807 var sourceRow = metadata[i]; 808 if (sourceRow.length <= 1) { 809 // ignore empty rows 810 i++; 811 continue; 812 }; 813 // skip any rows that don't match the country and level 814 if (!(sourceRow['boundaryISO']==currentIso & sourceRow['boundaryType']==currentLevel)) { 815 continue; 816 }; 817 sourceRows.push(sourceRow); 818 }; 819 820 // sort alphabetically 821 sortBy(sourceRows, 'boundarySource-1'); 822 var currentSource = document.getElementById("main-boundary-select").value; 823 824 // begin adding data to sources table 825 for (sourceRow of sourceRows) { 826 var tr = document.createElement('tr'); 827 if (sourceRow['boundarySource-1']==currentSource) { 828 tr.style.backgroundColor = '#F0B323'; //'rgba(255,213,128,0.4)'; 829 tr.style.color = 'white'; 830 }; 831 // select button 832 var td = document.createElement('td'); 833 var but = document.createElement('button'); 834 but.innerHTML = '✔'; 835 but.data = sourceRow['boundarySource-1']; 836 if (sourceRow['boundarySource-1']==currentSource) { 837 but.style.filter = 'brightness(1000)'; 838 }; 839 function onclick() { 840 var sel = document.getElementById("main-boundary-select"); 841 sel.value = this.data; 842 // force dropdown change 843 mainSourceChanged(); 844 // close 845 closeMainSourcePopup(); 846 }; 847 but.onclick = onclick; 848 //but.setAttribute('onclick', onclick); 849 //but.style.padding = '5px' 850 //but.style.margin = 0; 851 td.appendChild(but); 852 tr.appendChild(td); 853 // source name 854 var td = document.createElement('td'); 855 if (sourceRow['boundarySource-1']=='upload') { 856 td.innerText = 'Upload Your Own Boundary'; 857 } else { 858 td.innerText = sourceRow['boundarySource-1']; 859 } 860 tr.appendChild(td); 861 // license 862 var td = document.createElement('td'); 863 td.innerText = sourceRow.boundaryLicense; 864 tr.appendChild(td); 865 // year 866 var td = document.createElement('td'); 867 td.innerText = sourceRow.boundaryYearRepresented; 868 tr.appendChild(td); 869 // updated 870 var td = document.createElement('td'); 871 td.innerText = sourceRow.sourceDataUpdateDate; 872 tr.appendChild(td); 873 // unit count 874 var td = document.createElement('td');
875 td.innerText = sourceRow.boundaryCount; 876 tr.appendChild(td); 877 // min line res 878 var td = document.createElement('td'); 879 td.innerText = parseFloat(sourceRow.statsLineResolution).toFixed(1) + ' m'; 880 tr.appendChild(td); 881 // max vert dens 882 var td = document.createElement('td'); 883 td.innerText = parseFloat(sourceRow.statsVertexDensity).toFixed(1) + ' / km'; 884 tr.appendChild(td); 885 // 886 table.appendChild(tr); 887 }; 888}; 889 890function updateMainSourceDropdown() { 891 //alert('update main boundary dropdown'); 892 // update source table 893 updateMainSourceTable(); 894 // get current country and level 895 var currentIso = document.getElementById('country-select').value; 896 var currentLevel = document.getElementById('main-admin-level-select').value; 897 // get source dropdown 898 var select = document.getElementById('main-boundary-select'); 899 var selectVal = select.value; 900 // clear all existing dropdown options 901 select.innerHTML = ''; 902 // get geoContrast metadata 903 var metadata = geoContrastMetadata; 904 // get list of sources that match the specified country 905 var sources = []; 906 var sourcesSeen = []; 907 for (var i = 1; i < metadata.length; i++) { 908 var row = metadata[i]; 909 if (row.length <= 1) { 910 // ignore empty rows 911 i++; 912 continue; 913 }; 914 var source = row['boundarySource-1']; 915 var iso = row.boundaryISO; 916 var level = row.boundaryType; 917 if ((iso==currentIso) & (level==currentLevel)) { 918 if (!(sourcesSeen.includes(source))) { 919 // only add if hasn't already been added 920 sourcesSeen.push(source); 921 sources.push(source); 922 }; 923 }; 924 }; 925 // sort 926 sources.sort() 927 // add new options from geoContrastMetadata 928 for (source of sources) { 929 var opt = document.createElement("option"); 930 opt.value = source; 931 opt.textContent = source; 932 select.appendChild(opt); 933 }; 934 // finally add custom upload boundary choice 935 opt = document.createElement('option'); 936 opt.value = 'upload'; 937 opt.textContent = 'Upload Your Own Boundary'; 938 select.appendChild(opt); 939 sources.push('upload'); 940 // set the source to get-param if specified 941 const urlParams = new URLSearchParams(window.location.search); 942 var source = urlParams.get('mainSource'); 943 var defaultSource = 'geoBoundaries (Open)'; 944 if (source != null & sources.includes(source)) { 945 select.value = source; 946 } else if (sources.includes(defaultSource)) { 947 // default source 948 select.value = defaultSource; 949 } else { 950 // default not available, use first available source 951 select.value = sources[0]; 952 }; 953 // force dropdown change 954 mainSourceChanged(); 955}; 956 957function updateComparisonSourceTable() { 958 //console.log('update comparison source table'); 959 var currentIso = document.getElementById('country-select').value; 960 var currentLevel = document.getElementById('comparison-admin-level-select').value; 961 // clear sources table 962 var table = document.querySelector('#comparison-sources-table tbody'); 963 table.innerHTML = ''; 964 // get geoContrast metadata 965 var metadata = geoContrastMetadata; 966 // get list of sources that match the specified country 967 var sourceRows = []; 968 for (var i = 1; i < metadata.length; i++) { 969 var sourceRow = metadata[i]; 970 if (sourceRow.length <= 1) { 971 // ignore empty rows 972 i++; 973 continue; 974 }; 975 // skip any rows that don't match the country and level 976 if (!(sourceRow['boundaryISO']==currentIso & sourceRow['boundaryType']==currentLevel)) { 977 continue; 978 }; 979 sourceRows.push(sourceRow); 980 }; 981 982 // sort alphabetically 983 sortBy(sourceRows, 'boundarySource-1'); 984 var currentSource = document.getElementById("comparison-boundary-select").value; 985 986 // begin adding data to sources table 987 for (sourceRow of sourceRows) { 988 var tr = document.createElement('tr'); 989 if (sourceRow['boundarySource-1']==currentSource) { 990 tr.style.backgroundColor = '#F0B323'; //'rgba(255,213,128,0.4)'; 991 tr.style.color = 'white'; 992 }; 993 // select button 994 var td = document.createElement('td'); 995 var but = document.createElement('button'); 996 but.innerHTML = '✔'; 997 but.data = sourceRow['boundarySource-1']; 998 if (sourceRow['boundarySource-1']==currentSource) { 999 but.style.filter = 'brightness(1000)'; 1000 }; 1001 function onclick() { 1002 var sel = document.getElementById("comparison-boundary-select"); 1003 sel.value = this.data; 1004 // force dropdown change 1005 comparisonSourceChanged(); 1006 // close 1007 closeComparisonSourcePopup(); 1008 }; 1009 but.onclick = onclick; 1010 //but.setAttribute('onclick', onclick); 1011 //but.style.padding = '5px' 1012 //but.style.margin = 0; 1013 td.appendChild(but); 1014 tr.appendChild(td); 1015 // source name 1016 var td = document.createElement('td'); 1017 if (sourceRow['boundarySource-1']=='upload') { 1018 td.innerText = 'Upload Your Own Boundary'; 1019 } else {
1020 td.innerText = sourceRow['boundarySource-1']; 1021 } 1022 tr.appendChild(td); 1023 // license 1024 var td = document.createElement('td'); 1025 td.innerText = sourceRow.boundaryLicense; 1026 tr.appendChild(td); 1027 // year 1028 var td = document.createElement('td'); 1029 td.innerText = sourceRow.boundaryYearRepresented; 1030 tr.appendChild(td); 1031 // updated 1032 var td = document.createElement('td'); 1033 td.innerText = sourceRow.sourceDataUpdateDate; 1034 tr.appendChild(td); 1035 // unit count 1036 var td = document.createElement('td'); 1037 td.innerText = sourceRow.boundaryCount; 1038 tr.appendChild(td); 1039 // min line res 1040 var td = document.createElement('td'); 1041 td.innerText = parseFloat(sourceRow.statsLineResolution).toFixed(1) + ' m'; 1042 tr.appendChild(td); 1043 // max vert dens 1044 var td = document.createElement('td'); 1045 td.innerText = parseFloat(sourceRow.statsVertexDensity).toFixed(1) + ' / km'; 1046 tr.appendChild(td); 1047 // 1048 table.appendChild(tr); 1049 }; 1050}; 1051 1052function updateComparisonSourceDropdown() { 1053 //alert('update comparison boundary dropdown'); 1054 // update source table 1055 updateComparisonSourceTable(); 1056 // get current country and level 1057 var currentIso = document.getElementById('country-select').value; 1058 var currentLevel = document.getElementById('comparison-admin-level-select').value; 1059 // get source dropdown 1060 var select = document.getElementById('comparison-boundary-select'); 1061 var selectVal = select.value; 1062 // clear all existing dropdown options 1063 select.innerHTML = ''; 1064 // get geoContrast metadata 1065 var metadata = geoContrastMetadata; 1066 // get list of sources that match the specified country 1067 var sources = []; 1068 var sourcesSeen = []; 1069 for (var i = 1; i < metadata.length; i++) { 1070 var row = metadata[i]; 1071 if (row.length <= 1) { 1072 // ignore empty rows 1073 i++; 1074 continue; 1075 }; 1076 var source = row['boundarySource-1']; 1077 var iso = row.boundaryISO; 1078 var level = row.boundaryType; 1079 if ((iso==currentIso) & (level==currentLevel)) { 1080 if (!(sourcesSeen.includes(source))) { 1081 // only add if hasn't already been added 1082 sourcesSeen.push(source); 1083 sources.push(source); 1084 }; 1085 }; 1086 }; 1087 // sort 1088 sources.sort() 1089 // add new options from geoContrastMetadata 1090 for (source of sources) { 1091 var opt = document.createElement("option"); 1092 opt.value = source; 1093 opt.textContent = source; 1094 select.appendChild(opt); 1095 }; 1096 // finally add custom upload boundary choice 1097 opt = document.createElement('option'); 1098 opt.value = 'upload'; 1099 opt.textContent = 'Upload Your Own Boundary'; 1100 select.appendChild(opt); 1101 sources.push('upload'); 1102 // set the source to get-param if specified 1103 const urlParams = new URLSearchParams(window.location.search); 1104 var source = urlParams.get('comparisonSource'); 1105 var defaultSource = 'GADM v3.6'; 1106 if (source != null & sources.includes(source)) { 1107 select.value = source; 1108 } else if (sources.includes(defaultSource)) { 1109 // default source 1110 select.value = defaultSource; 1111 } else { 1112 // default not available, use first available source 1113 select.value = sources[0]; 1114 }; 1115 // force dropdown change 1116 comparisonSourceChanged(); 1117}; 1118 1119 1120 1121 1122 1123 1124 1125 1126///////////////////////////// 1127// dropdown change behavior 1128 1129function updateGetParams() { 1130 const urlParams = new URLSearchParams(window.location.search); 1131 // set country 1132 var select = document.getElementById('country-select'); 1133 if (select.value == '') {return}; // to avoid errors at startup when not all selects have been populated 1134 urlParams.set('country', select.value); 1135 // set main source 1136 var select = document.getElementById('main-boundary-select'); 1137 if (select.value == '') {return}; // to avoid errors at startup when not all selects have been populated
1138 urlParams.set('mainSource', select.value); 1139 // set comparison source 1140 var select = document.getElementById('comparison-boundary-select'); 1141 if (select.value == '') {return}; // to avoid errors at startup when not all selects have been populated 1142 urlParams.set('comparisonSource', select.value); 1143 // set main adm level 1144 var select = document.getElementById('main-admin-level-select'); 1145 if (select.value == '') {return}; // to avoid errors at startup when not all selects have been populated 1146 urlParams.set('mainLevel', select.value[select.value.length-1]); // only the adm number 1147 // set comparison adm level 1148 var select = document.getElementById('comparison-admin-level-select'); 1149 if (select.value == '') {return}; // to avoid errors at startup when not all selects have been populated 1150 urlParams.set('comparisonLevel', select.value[select.value.length-1]); // only the adm number 1151 // update url 1152 //window.location.search = urlParams; 1153 var newUrl = window.location.protocol + "//" + window.location.host + window.location.pathname + '?' + urlParams.toString(); 1154 //alert(newUrl); 1155 window.history.replaceState({path:newUrl}, '', newUrl); 1156}; 1157 1158// country 1159 1160function countryChanged() { 1161 //alert('country changed'); 1162 updateMainAdminLevelDropdown(); 1163 updateComparisonAdminLevelDropdown(); 1164 //updateGetParams(); 1165}; 1166 1167// admin levels 1168 1169function mainAdminLevelChanged() { 1170 //alert('comparison admin-level changed'); 1171 updateMainSourceDropdown(); 1172 //updateGetParams(); 1173}; 1174 1175function comparisonAdminLevelChanged() { 1176 //alert('comparison admin-level changed'); 1177 updateComparisonSourceDropdown(); 1178 //updateGetParams(); 1179}; 1180 1181// sources 1182 1183function mainUploadButtonPushed() { 1184 val = document.getElementById('main-boundary-select').value; 1185 if (val == 'upload') { 1186 // already in upload mode 1187 // get available sources 1188 select = document.getElementById('main-boundary-select'); 1189 sourceVals = []; 1190 for (opt of select.options) { 1191 sourceVals.push(opt.value); 1192 }; 1193 // set dropdown value to first available source 1194 newVal = 'none'; 1195 if (sourceVals.length) { 1196 newVal = sourceVals[0]; 1197 }; 1198 document.getElementById('main-boundary-select').value = newVal; 1199 } else { 1200 // switch to upload mode 1201 // set dropdown value to upload 1202 document.getElementById('main-boundary-select').value = 'upload'; 1203 }; 1204 // trigger source changed 1205 mainSourceChanged(); 1206}; 1207 1208function comparisonUploadButtonPushed() { 1209 val = document.getElementById('comparison-boundary-select').value; 1210 if (val == 'upload') { 1211 // already in upload mode 1212 // get available sources 1213 select = document.getElementById('comparison-boundary-select'); 1214 sourceVals = []; 1215 for (opt of select.options) { 1216 sourceVals.push(opt.value); 1217 }; 1218 // set dropdown value to first available source 1219 newVal = 'none'; 1220 if (sourceVals.length) { 1221 newVal = sourceVals[0]; 1222 }; 1223 document.getElementById('comparison-boundary-select').value = newVal; 1224 } else { 1225 // switch to upload mode 1226 // set dropdown value to upload 1227 document.getElementById('comparison-boundary-select').value = 'upload'; 1228 }; 1229 // trigger source changed 1230 comparisonSourceChanged(); 1231}; 1232 1233function mainSourceChanged() { 1234 //alert('main source changed'); 1235 // update source table 1236 updateMainSourceTable(); 1237 // empty misc info 1238 clearMainInfo(); 1239 clearMainStats(); 1240 clearMatchTable(); //clearMainNames(); 1241 clearTotalEquality(); 1242 // clear comparison layer 1243 clearMainLayer(); 1244 // check which comparison source was selected 1245 source = document.getElementById('main-boundary-select').value; 1246 uploadOnSymbol = '×'; 1247 uploadOffSymbol = '<i class="fa fa-upload" aria-hidden="true"></i>'; 1248 if (source == 'none' | source == '') { 1249 // activate admin level button 1250 document.getElementById('main-admin-level-select').style.display = ''; 1251 document.getElementById('main-boundary-select').style.display = ''; 1252 // reset upload button style 1253 document.getElementById('main-boundary-upload-button').className = "upload-button"; 1254 document.getElementById('main-boundary-upload-button').innerHTML = uploadOffSymbol; 1255 // hide file button elements 1256 document.getElementById('main-boundary-upload-title').style.display = 'none'; 1257 document.getElementById('main-file-div').style.display = 'none'; 1258 } else if (source == 'upload') { 1259 // disable admin level button 1260 document.getElementById('main-admin-level-select').style.display = 'none'; 1261 document.getElementById('main-boundary-select').style.display = 'none'; 1262 // reset 1263 document.getElementById('main-file-input').value = null; 1264 document.getElementById('main-file-select').innerHTML = '<option value="" disabled selected hidden>(Please select a boundary)</option>'; 1265 document.getElementById('main-file-select').disabled = true; 1266 // change upload button style to active 1267 document.getElementById('main-boundary-upload-button').className = "upload-button upload-button-active"; 1268 document.getElementById('main-boundary-upload-button').innerHTML = uploadOnSymbol; 1269 // show file button elements 1270 document.getElementById('main-boundary-upload-title').style.display = ''; 1271 document.getElementById('main-file-div').style.display = 'block'; 1272 } else { 1273 // activate admin level button 1274 document.getElementById('main-admin-level-select').style.display = ''; 1275 document.getElementById('main-boundary-select').style.display = ''; 1276 // reset upload button style 1277 document.getElementById('main-boundary-upload-button').className = "upload-button"; 1278 document.getElementById('main-boundary-upload-button').innerHTML = uploadOffSymbol; 1279 // hide file button elements 1280 document.getElementById('main-boundary-upload-title').style.display = 'none'; 1281 document.getElementById('main-file-div').style.display = 'none'; 1282 // update main layer with external geoContrast topojson 1283 updateMainLayer(zoomToExtent=true); 1284 }; 1285 updateGetParams(); 1286}; 1287 1288function comparisonSourceChanged() { 1289 //alert('comparison source changed'); 1290 // update source table 1291 updateComparisonSourceTable(); 1292 // clear misc info 1293 clearComparisonInfo(); 1294 clearComparisonStats(); 1295 clearMatchTable(); //clearComparisonNames(); 1296 clearTotalEquality(); 1297 // clear comparison layer 1298 clearComparisonLayer(); 1299 // check which comparison source was selected 1300 source = document.getElementById('comparison-boundary-select').value; 1301 uploadOnSymbol = '×'; 1302 uploadOffSymbol = '<i class="fa fa-upload" aria-hidden="true"></i>'; 1303 if (source == 'none' | source == '') { 1304 // activate admin level button 1305 document.getElementById('comparison-admin-level-select').style.display = ''; 1306 document.getElementById('comparison-boundary-select').style.display = ''; 1307 // reset upload button style 1308 document.getElementById('comparison-boundary-upload-button').className = "upload-button"; 1309 document.getElementById('comparison-boundary-upload-button').innerHTML = uploadOffSymbol; 1310 // hide file button elements 1311 document.getElementById('comparison-boundary-upload-title').style.display = 'none'; 1312 document.getElementById('comparison-file-div').style.display = 'none'; 1313 } else if (source == 'upload') { 1314 // disable admin level button 1315 document.getElementById('comparison-admin-level-select').style.display = 'none'; 1316 document.getElementById('comparison-boundary-select').style.display = 'none'; 1317 // reset 1318 document.getElementById('comparison-file-input').value = null; 1319 document.getElementById('comparison-file-select').innerHTML = '<option value="" disabled selected hidden>(Please select a boundary)</option>'; 1320 document.getElementById('comparison-file-select').disabled = true; 1321 // change upload button style to active 1322 document.getElementById('comparison-boundary-upload-button').className = "upload-button upload-button-active"; 1323 document.getElementById('comparison-boundary-upload-button').innerHTML = uploadOnSymbol; 1324 // show file button elements 1325 document.getElementById('comparison-boundary-upload-title').style.display = ''; 1326 document.getElementById('comparison-file-div').style.display = 'block'; 1327 } else { 1328 // activate admin level button 1329 document.getElementById('comparison-admin-level-select').style.display = ''; 1330 document.getElementById('comparison-boundary-select').style.display = ''; 1331 // reset upload button style 1332 document.getElementById('comparison-boundary-upload-button').className = "upload-button"; 1333 document.getElementById('comparison-boundary-upload-button').innerHTML = uploadOffSymbol; 1334 // hide file button elements 1335 document.getElementById('comparison-boundary-upload-title').style.display = 'none'; 1336 document.getElementById('comparison-file-div').style.display = 'none'; 1337 // update main layer with external geoContrast topojson 1338 updateComparisonLayer(zoomToExtent=true); 1339 }; 1340 updateGetParams(); 1341}; 1342 1343 1344// pdf report 1345 1346function getPageWrapper() { 1347 // create page wrapper with padding 1348 var wrapper = document.createElement('div'); 1349 wrapper.style.padding = '50px'; 1350 // add top header 1351 var header = document.createElement('header'); //document.getElementById('header').cloneNode(true); 1352 header.className = "logo"; 1353 header.style = "width:100%; margin-bottom:40px"; 1354 header.innerHTML = `
1355 <span style="float:left"> 1356 <strong>Boundary comparison report: <span class="countryName"></span></strong> 1357 </span> 1358 <span style="float:right"> 1359 William & Mary geoLab 1360 </span> 1361 ` 1362 // set country name 1363 var countrySelect = document.getElementById('country-select'); 1364 var country = countrySelect.options[countrySelect.selectedIndex].text; 1365 header.querySelector('.countryName').innerText = country; 1366 wrapper.appendChild(header); 1367 // add header line 1368 var hr = document.createElement('hr'); 1369 hr.style = "border-color:#F0B323; background-color:#F0B323; margin:3px; height:6px; margin-bottom:30px" 1370 wrapper.appendChild(hr); 1371 // custom css to display links without color 1372 var style = document.createElement('style'); 1373 style.innerHTML = ` 1374 a { 1375 color: inherit; 1376 } 1377 `; 1378 wrapper.appendChild(style); 1379 return wrapper; 1380} 1381 1382async function renderFrontPage(mapImgData) { 1383 // get page wrapper 1384 var wrapper = getPageWrapper(); 1385 // add top title incl country 1386 var titleDiv = document.createElement('div'); 1387 titleDiv.innerHTML = ` 1388 <h1 style="margin:10px 0px">Boundary comparison report</h1> 1389 <h2 id="country-header-for-printing" style="margin:10px 0px"></h2> 1390 <br> 1391 ` 1392 var countrySelect = document.getElementById('country-select'); 1393 var country = countrySelect.options[countrySelect.selectedIndex].text; 1394 titleDiv.querySelector('#country-header-for-printing').innerText = country + ', administrative boundaries'; 1395 wrapper.appendChild(titleDiv); 1396 // add map 1397 var mapDiv = document.createElement('div'); 1398 mapDiv.innerHTML = ` 1399 <style> 1400 #map-image-legend { 1401 position:absolute; 1402 top:10px; 1403 left:10px; 1404 background-color:white; 1405 border-radius:5px; 1406 opacity:0.8; 1407 color:black; 1408 } 1409 1410 #map-image-legend #layer-list { 1411 list-style-type: none; 1412 margin: 0; 1413 } 1414 1415 #map-image-legend #layer-list li { 1416 display: flex; 1417 flex-direction: row; 1418 align-items: center; 1419 margin:5px; 1420 } 1421 1422 #legend-symbol-main { 1423 width: 20px;
1424 border-top: 4px solid #319FD3; 1425 border-bottom: unset; 1426 margin: 5px; 1427 } 1428 1429 #legend-symbol-comparison { 1430 width: 20px; 1431 border-top: 4px dashed rgb(255,0,0); 1432 border-bottom: unset; 1433 margin: 5px; 1434 } 1435 </style> 1436 <div style="position:relative; width:100%; height:75%; margin:0; padding:0; text-align:center"> 1437 <img id="map-image-for-printing" style="width:100%; height:100%; object-fit:cover" crossorigin="anonymous"/> 1438 <div id="map-image-legend"> 1439 <ul id="layer-list"> 1440 <li> 1441 <hr id="legend-symbol-main"> 1442 <span class="main-source-title">Main boundary</span> 1443 </li> 1444 <li> 1445 <hr id="legend-symbol-comparison"> 1446 <span class="comp-source-title">Comparison boundary</span> 1447 </li> 1448 </ul> 1449 </div> 1450 </div> 1451 ` 1452 wrapper.appendChild(mapDiv); 1453 // populate legend 1454 var mainSource = document.querySelector('.main-source-title').innerText; 1455 var comparisonSource = document.querySelector('.comp-source-title').innerText; 1456 mapDiv.querySelector('.main-source-title').innerText = mainSource; 1457 mapDiv.querySelector('.comp-source-title').innerText = comparisonSource; 1458 // set map img src 1459 var mapImg = wrapper.querySelector('#map-image-for-printing'); 1460 mapImg.src = mapImgData; 1461 // render to image 1462 var config = {}; 1463 document.body.appendChild(wrapper); 1464 var canvas = await html2canvas(wrapper, config); 1465 wrapper.remove(); 1466 return canvas; 1467} 1468 1469async function renderMetaStatsPage() { 1470 // get page wrapper 1471 var wrapper = getPageWrapper(); 1472 // add meta 1473 var banner = document.getElementById('source-overview-banner').cloneNode(true); 1474 wrapper.appendChild(banner); 1475 metaBox = document.getElementById('source-overview').cloneNode(true); 1476 wrapper.appendChild(metaBox); 1477 var br = document.createElement('br'); 1478 wrapper.appendChild(br); 1479 var br = document.createElement('br'); 1480 wrapper.appendChild(br); 1481 // add stats 1482 var banner = document.getElementById('source-stats-banner').cloneNode(true); 1483 wrapper.appendChild(banner); 1484 statsBox = document.getElementById('source-stats').cloneNode(true); 1485 wrapper.appendChild(statsBox); 1486 var br = document.createElement('br'); 1487 wrapper.appendChild(br); 1488 // render to image 1489 var config = {}; 1490 document.body.appendChild(wrapper); 1491 var canvas = await html2canvas(wrapper, config); 1492 wrapper.remove(); 1493 return canvas; 1494} 1495 1496async function renderAgreementPage() { 1497 // get table row info 1498 var matchTable = document.getElementById('match-table-div'); 1499 var rows = matchTable.querySelectorAll('tr'); 1500 var rowCount = rows.length; 1501 var rowNum = 0; 1502 // add new page for every 20 rows 1503 var canvases = []; 1504 while (rowNum <= (rowCount-1)) { 1505 // get page wrapper 1506 var wrapper = getPageWrapper(); 1507 // banner 1508 var banner = document.getElementById('source-contents-banner').cloneNode(true); 1509 wrapper.appendChild(banner); 1510 // box 1511 var box = document.createElement('div'); 1512 box.className = 'box row'; 1513 box.style = "width:100%; margin:0"; 1514 wrapper.appendChild(box); 1515 // top title and match percent 1516 if (rowNum == 0) { 1517 var banner = document.getElementById('source-overlap-total').cloneNode(true); 1518 box.appendChild(banner); 1519 var br = document.createElement('br'); 1520 box.appendChild(br); 1521 } else { 1522 var banner = document.createElement('div'); 1523 banner.innerHTML = '<br>'; 1524 box.appendChild(banner); 1525 }; 1526 // table of rows 1527 var table = document.createElement('table'); 1528 table.style = "margin-left:10px; width:100%"; 1529 var rowEnd = Math.min(rowNum + 20, rowCount-1); 1530 // add top header row 1531 if (rowNum != 0) { 1532 var row = rows[0]; 1533 row.querySelector('#main-names-table-select').value = document.getElementById('#main-names-table-select').value; 1534 row.querySelector('#comparison-names-table-select').value = document.getElementById('#comparison-names-table-select').value; 1535 table.appendChild(row.cloneNode(true)); 1536 }; 1537 // add data rows 1538 var tbody = document.createElement('tbody'); 1539 while (rowNum <= rowEnd) { 1540 var row = rows[rowNum]; 1541 tbody.appendChild(row.cloneNode(true)); 1542 rowNum += 1; 1543 }; 1544 table.appendChild(tbody); 1545 box.appendChild(table); 1546 // add nomatch info at end of table 1547 if (rowNum == rowCount) { 1548 var noMatchDiv = document.getElementById('nomatch-div').cloneNode(true); 1549 box.appendChild(noMatchDiv); 1550 }; 1551 // render to image 1552 var config = {}; 1553 document.body.appendChild(wrapper); 1554 var canvas = await html2canvas(wrapper, config); 1555 wrapper.remove();
1556 canvases.push(canvas); 1557 }; 1558 return canvases; 1559} 1560 1561async function makePDF(mapImgData) { 1562 // define doc 1563 var pageWidth = 210; 1564 var pageHeight = 295; 1565 var doc = new jsPDF('portrait', 'mm'); 1566 1567 // loop parts of the image and crop to each page 1568 var canvas = await renderFrontPage(mapImgData); 1569 var imgHeight = canvas.height * (pageWidth / canvas.width); 1570 doc.addImage(canvas.toDataURL("image/jpeg"), 'JPEG', 0, 0, pageWidth, imgHeight); 1571 1572 var canvas = await renderMetaStatsPage(); 1573 var imgHeight = canvas.height * (pageWidth / canvas.width); 1574 doc.addPage(); 1575 doc.addImage(canvas.toDataURL("image/jpeg"), 'JPEG', 0, 0, pageWidth, imgHeight); 1576 1577 var canvases = await renderAgreementPage(); 1578 for (canvas of canvases) { 1579 var imgHeight = canvas.height * (pageWidth / canvas.width); 1580 doc.addPage(); 1581 doc.addImage(canvas.toDataURL("image/jpeg"), 'JPEG', 0, 0, pageWidth, imgHeight); 1582 }; 1583 1584 // show or save pdf 1585 //doc.output('datauri'); 1586 doc.save('geoBoundaries-comparison.pdf'); 1587 //window.open(doc.output('bloburl')); 1588 //doc.output('bloburi'); 1589 1590 // reactivate pdf button 1591 but = document.getElementById('pdf-button'); 1592 but.disabled = false; 1593 but.innerHTML = 'Generate PDF Report'; 1594} 1595 1596function renderMapToImgData(processdata) { 1597 map.once('rendercomplete', function () { 1598 //alert('rendercomplete'); 1599 const mapCanvas = document.createElement('canvas'); 1600 const size = map.getSize(); 1601 mapCanvas.width = size[0]; 1602 mapCanvas.height = size[1]; 1603 const mapContext = mapCanvas.getContext('2d'); 1604 Array.prototype.forEach.call( 1605 document.querySelectorAll('#map .ol-layer canvas'), 1606 function (canvas) { 1607 if (canvas.width > 0) { 1608 const opacity = canvas.parentNode.style.opacity; 1609 mapContext.globalAlpha = opacity === '' ? 1 : Number(opacity); 1610 const transform = canvas.style.transform; 1611 // Get the transform parameters from the style's transform matrix 1612 const matrix = transform 1613 .match(/^matrix\(([^\(]*)\)$/)[1] 1614 .split(',') 1615 .map(Number); 1616 // Apply the transform to the export map context 1617 CanvasRenderingContext2D.prototype.setTransform.apply( 1618 mapContext, 1619 matrix 1620 ); 1621 mapContext.drawImage(canvas, 0, 0); 1622 }; 1623 } 1624 ); 1625 imgData = mapCanvas.toDataURL(); 1626 //alert('imgdata '+imgData); 1627 processdata(imgData); 1628 }); 1629 map.renderSync(); 1630}; 1631 1632function printPageToPDF() { 1633 // update pdf button 1634 but = document.getElementById('pdf-button'); 1635 but.innerHTML = '<div style="display:flex; flex-direction:row; align-items:center; gap:3px"><span>Generating</span><img src="images/Spinner-1s-200px.gif" style="max-width:30px; margin-right:-15px"/></div>'; 1636 but.disabled = true; 1637 // render the map to image data 1638 // on success, set image data to img src and print the pdf 1639 var onsuccess = makePDF; 1640 renderMapToImgData(onsuccess); 1641}; 1642 1643
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.