1<html> 2 <head> 3 <link rel="stylesheet" href="ol.css" type="text/css"> 4
4<script type="text/javascript" src="ol.js"></script>
4 5
5<script type="text/javascript" src="rhill-voronoi-core.js"></script>
5 6
6<script type="text/javascript" src="sorttable.js"></script>
6 7
7<script type="text/javascript" src="zip.js"></script>
7 8
8<script type="text/javascript" src="kloeke.js"></script>
8 9 <!-- sorting only works one way; --> 10 </head> 11 <body style="background-color:#eeeeee;"> 12 <p> 13 <strong>ARVID</strong> 14 View: 15 <select id="mode" onchange="view(this.value)"> 16 <option value="map" selected>map</option> 17 <option value="table">table</option> 18 </select> 19 <button onclick="if (document.getElementById('mode').value == 'table') { saveCSV(); } else { saveKML(); }">Save File</button> 20 <select id="example" onchange="examples(this.value)"> 21 <option value="0" selected>Examples</option> 22 <option value="0">Default</option> 23 <option value="1">New York</option> 24 <option value="2">Erik's data 1</option> 25 <option value="3">Erik's data 2</option> 26 <option value="4">Nina's data 1</option> 27 <option value="5">Basque 1</option> 28 <option value="8">Basque 2</option> 29 <option value="10">Basque 3</option> 30 <option value="11">Basque 4</option> 31 <option value="12">Basque 6</option> 32 <option value="13">Basque 8</option> 33 <option value="6">Basque 15</option> 34 <option value="7">Basque 16</option> 35 <option value="9">Basque 24</option> 36 <option value="14">Spruit</option> 37 </select> 38 Load File: 39 <input type="file" id="files" name="files[]" multiple /><br/> 40 <font id="mapbuttons"> 41 Cell size: 42 <input id="maxDistance" size="5" onchange="window.maxDistance=Number(this.value); cleanError(); viewMap();" /> 43 Opacity: 44 <input id="opacity" size="3" onchange="window.opacity=Number(this.value); cleanError(); viewMap();" /> 45 Background map: 46 <select id="backgroundMap" onchange="viewMap()"> 47 <option value="off" selected>off</option> 48 <option value="on">on</option> 49 </select> 50 </font> 51 <a href="help.html">Help</a> 52 </p> 53 <div id="map" class="map" style="width:700; height:525; background-color:white;"></div> 54 <div id="legend" class="legend"></div> 55 <div> 56 Combine: 57 <input id="combine" size="5" onchange="window.combine=Number(this.value); cleanError(); view(document.getElementById('mode').value);" /> 58 <font id="mapbuttons2"> 59 Transition points: 60 <input id="transition" size="5" onchange="window.transition=Number(this.value); cleanError(); viewMap();" /> 61 Distance: 62 <input id="distance" size="5" onchange="window.distance=Number(this.value); cleanError(); viewMap();" /> 63 Palette: 64 <input id="palette" size="5" onchange="window.palette=Number(this.value); cleanError(); viewMap();" /> 65 <a onclick="window.palette=window.palette-1; while (window.palette<0) {window.palette += 48; } document.getElementById('palette').value = window.palette; cleanError(); viewMap();" style="color:blue;" />[-1]</a> 66 <a onclick="window.palette=window.palette+1; while (window.palette>=48) {window.palette -= 48; }document.getElementById('palette').value = window.palette; cleanError(); viewMap();" style="color:blue;" />[+1]</a> 67 </font> 68 </div> 69 <div id="comment" class="comment"></div> 70 <div id="error" class="error"></div> 71
71<script type="text/javascript"> 72 // FUNCTIONS: 73 // view(mode,sitesFile) 74 // viewTable() 75 // viewMap() 76 // handleFileSelect(evt) 77 // processData(sitesFile) 78 // processCSV(xmlDoc) 79 // processKML(xmlDoc) 80 // saveKML() 81 // saveCSV() 82 // clipEdges(sitex,sitey,edges,maxDistance) 83 // findIntersectionPoints(sitex,sitey,maxDistance,x0,y0,x1,y1) 84 // addCurve(nodes,sitex,sitey,x0,y0,x1,y1,nbrOfPoints) 85 // sortEdges(edges) 86 // error(string) 87 // makeDateString(date) 88 // color2hex(string) 89 // 90 // 20150521 printing maps: http://trac.osgeo.org/openlayers/wiki/TileStitchingPrinting 91 92 // default file with data points 93 var mainSitesFile = "default.kml"; 94 // main data structure 95 var datapoints = []; 96 // list of modified data values 97 var updates = []; 98 var fileName = ""; 99 var opacity = -1; 100 var maxDistance = -1; 101 var combine = 1; 102 var transition = 0; 103 var distance = 0; 104 var palette = 0; 105 var numericalData = false; 106 document.getElementById("files").value = ""; 107 document.getElementById("palette").value = "0"; 108 document.getElementById("example").value = "0"; 109 // define 14 colors with names and color map pointing to them 110 var colorNames = [ "white","red","green","blue","yellow","magenta","cyan","l red","l green","l blue","l yellow","l magenta","l cyan","black" ]; 111 var colorMap = [ 0,1,2,3,4,5,6,7,8,9,10,11,12,13 ]; 112 // check cgi variables 113 checkCGI(); 114 document.getElementById("combine").value = combine; 115 document.getElementById("transition").value = transition; 116 document.getElementById("distance").value = 0; 117 // run main function 118 processData(mainSitesFile); 119 if (typeof(document.getElementById("mode").value) != "undefined" && 120 document.getElementById("mode").value == "table") { viewTable(); } 121 else { viewMap(); } 122 123 function view(mode) { 124 cleanError(); 125 if (mode == "table") { viewTable(); } 126 else { viewMap(); } 127 } 128 129 // process uploaded file 130 // source: http://www.html5rocks.com/en/tutorials/file/dndfiles/ 131 function handleFileSelect(evt) { 132 cleanError(); 133 var files = evt.target.files; // FileList object 134 // clear earlier updates 135 updates = []; 136 opacity = -1; 137 maxDistance = -1; 138 document.getElementById("opacity").value = ""; 139 document.getElementById("maxDistance").value = ""; 140 // Loop through the FileList 141 for (var i = 0, f; f = files[i]; i++) { 142 if (/earth.kmz/.test(f.type) || /\.kmz$/.test(f.name)) { 143 fileName = f.name; 144 fileName = fileName.replace(/\.kmz$/,".kml"); 145 // source: http://gildas-lormeau.github.io/zip.js/core-api.html 146 zip.createReader(new zip.BlobReader(f), function(zipReader) { 147 // get all entries from the zip 148 zipReader.getEntries(function(entries) { 149 if (entries.length) { 150 for (var i=0;i<entries.length;i++) { 151 if (entries[i].filename == "doc.kml") { 152 // get first entry content as text 153 entries[i].getData(new zip.TextWriter(), function(text) { 154 // text contains the entry data as a String 155 processData(text); 156 if (typeof(document.getElementById("mode").value) != "undefined" && 157 document.getElementById("mode").value == "table") { viewTable(); } 158 else { viewMap(); } 159 // close the zip reader 160 zipReader.close(function() { 161 // onclose callback 162 }); 163 }, function(current, total) { 164 // onprogress callback 165 }); 166 } 167 } 168 } 169 }); 170 }, function(error) { 171 // onerror callback 172 error("Error reading file "+f.name+" "+error); 173 }); 174 } else { // assume input file is a text file 175 fileName = f.name; 176 var reader = new FileReader(); 177 // Closure to capture the file information. 178 reader.onload = (function(theFile) { 179 return function(e) { 180 var text = e.target.result; 181 processData(text); 182 if (typeof(document.getElementById("mode").value) != "undefined" && 183 document.getElementById("mode").value == "table") { viewTable(); } 184 else { viewMap(); } 185 }; 186 })(f); 187 // Read in the image file as a data URL. 188 reader.readAsText(f); 189 } 190 } 191 } 192 document.getElementById('files').addEventListener('change', handleFileSelect, false); 193 194 // store data from file in main data structure 195 function processData(sitesFile) { 196 mainSitesFile = sitesFile; 197 datapoints = []; 198 var xmlDoc = ""; 199 // a kml document is stored in string sitesFile 200 if (/^</.test(sitesFile)) { 201 var wrapper= document.createElement('div'); 202 wrapper.innerHTML = sitesFile; 203 xmlDoc = wrapper.getElementsByTagName("kml")[0]; 204 processKML(xmlDoc); 205 // a csv document is stored in string sitesFile 206 } else if (/,/.test(sitesFile) && sitesFile.length > 50) { 207 processCSV(sitesFile); 208 // load file from server, assume the format is kml 209 } else { 210 // load the data points 211 if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari 212 xmlhttp = new XMLHttpRequest(); 213 } else { // code for IE6, IE5 214 xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); 215 } 216 xmlhttp.open("GET",sitesFile,false); 217 xmlhttp.send(); 218 xmlDoc = xmlhttp.responseXML; 219 processKML(xmlDoc); 220 } 221 } 222 223 function processCSV(xmlDoc) { 224 // process the data points 225 var lines = xmlDoc.split("\n"); 226 var columns = []; 227 var seen = []; 228 numericalData = true; 229 var warnings = 0; 230 for (var l=0;l<lines.length;l++) { 231 var segments = lines[l].split("\r"); 232 for (var s=0;s<segments.length;s++) { 233 lines[l] = segments[s]; 234 if (typeof(lines[l]) !== "undefined" && lines[l] != "") { 235 var fields = lines[l].split(","); 236 var f = 0; 237 while (f < fields.length-1) { 238 var field = fields[f]; 239 if ((/^"/.field && ! /"$/.field) || 240 (/^'/.field && ! /'$/.field)) { 241 fields[f] += ","+fields[f+1]; 242 fields.splice(f+1,1); 243 } else { f++; } 244 } 245 // proces column names 246 if (l == 0 && s == 0) { 247 for (f=0;f<fields.length;f++) { 248 var field = fields[f]; 249 field = field.replace(/^['"]/,""); 250 field = field.replace(/['"]$/,""); 251 if (/^longitude$/i.test(field) || /^x$/i.test(field)) { columns[f] = "longitude"; } 252 else if (/^latitude$/i.test(field) || /^y$/i.test(field)) { columns[f] = "latitude"; } 253 else if (/^name$/i.test(field)) { columns[f] = "name"; } 254 else if (/^value$/i.test(field)) { columns[f] = "value"; } 255 else if (/^kloeke$/i.test(field)) { columns[f] = "kloeke"; } 256 else if (/^numbers$/i.test(field)) { columns[f] = "numbers"; } 257 else if (/^distance$/i.test(field)) { columns[f] = "distance"; } 258 // else { error("Unknown column name in csv file! "+fields[f]+"\n"); } 259 } 260 // process data lines 261 } else { 262 var longitude = ""; var latitude = ""; var name = ""; var value = ""; var kloeke = ""; 263 var numbers = ""; var distance = ""; 264 for (f=0;f<fields.length;f++) { 265 fields[f] = fields[f].replace(/^['"]/,""); 266 fields[f] = fields[f].replace(/['"]$/,""); 267 fields[f] = fields[f].replace(/\s*$/,""); 268 if (columns[f] == "longitude") { longitude = f
268ields[f]; } 269 else if (columns[f] == "latitude") { latitude = fields[f]; } 270 else if (columns[f] == "name") { name = fields[f]; } 271 else if (columns[f] == "value") { value = fields[f]; } 272 else if (columns[f] == "numbers") { numbers = fields[f]; } 273 else if (columns[f] == "distance") { distance = fields[f]; } 274 else if (columns[f] == "kloeke") { 275 kloeke = fields[f]; 276 if (typeof(kloekePoints[kloeke]) === "undefined") { error("undefined kloeke value "+kloeke+"!"); warnings++; } 277 else { 278 if (typeof(longitude) === "undefined" || longitude == "") { longitude = kloekePoints[kloeke].longitude; } 279 if (typeof(latitude) === "undefined" || latitude == "") { latitude = kloekePoints[kloeke].latitude; } 280 if (typeof(name) === "undefined" || name == "") { 281 if (typeof(kloekePoints[kloeke].city) !== "undefined") { name = kloekePoints[kloeke].city+" ("+kloeke+")"; } 282 else { name = ""; } 283 if (typeof(kloekePoints[kloeke].province) !== "undefined") { name += ", "+kloekePoints[kloeke].province; } 284 if (typeof(kloekePoints[kloeke].country) !== "undefined") { name += ", "+kloekePoints[kloeke].country; } 285 } 286 } 287 } 288 } 289 if (typeof(longitude) !== "undefined" && typeof(latitude) != "undefined" && longitude != "" && latitude != "") { 290 // check if this is a new data point 291 var seenKey = longitude+" "+latitude; 292 if (typeof(seen[seenKey]) === "undefined") { 293 datapoints.push({"longitude":longitude,"latitude":latitude,"value":value,"numbers":numbers,"distance":distance,"name":name}); 294 seen[seenKey] = datapoints.length-1; 295 if (value != "" && ! /^-?\d+(\.\d+)?(#-?\d+(\.\d+)?)*$/.test(value)) { numericalData = false; } 296 } else { 297 // existing data point: add new value to existing values 298 //var values = datapoints[seen[seenKey]].value.split("\n"); 299 //values.push(value); 300 //values.sort(); 301 //datapoints[seen[seenKey]].value = values.join("\n"); 302 //numericalData = false; 303 error("duplicate value for "+seenKey+"!"); 304 } 305 } 306 } 307 } 308 } 309 } 310 if (warnings > 1) { error(warnings+" errors"); } 311 if (numericalData) { 312 opacity = 1.0; 313 document.getElementById("opacity").value = opacity; 314 } 315 } 316 317 function processKML(xmlDoc) { 318 // process the data points 319 var points = xmlDoc.getElementsByTagName("Placemark"); 320 numericalData = true; 321 for (var p=0;p<points.length;p++) { 322 var name = ""; 323 if (typeof(points[p].getElementsByTagName("name")[0]) != "undefined") { 324 name = points[p].getElementsByTagName("name")[0].childNodes[0].nodeValue; 325 } 326 try { 327 var value = ""; 328 if (typeof(points[p].getElementsByTagName("Data")[0].getElementsByTagName("value")[0].childNodes[0]) !== "undefined") { 329 value = points[p].getElementsByTagName("Data")[0].getElementsByTagName("value")[0].childNodes[0].nodeValue; 330 } 331 var numbers = ""; 332 if (typeof(points[p].getElementsByTagName("Data")[1]) !== "undefined" && 333 typeof(points[p].getElementsByTagName("Data")[1].getElementsByTagName("value")[0].childNodes[0]) !== "undefined") { 334 numbers = points[p].getElementsByTagName("Data")[1].getElementsByTagName("value")[0].childNodes[0].nodeValue; 335 } 336 var distance = ""; 337 if (typeof(points[p].getElementsByTagName("Data")[2]) !== "undefined" && 338 typeof(points[p].getElementsByTagName("Data")[2].getElementsByTagName("value")[0].childNodes[0]) !== "undefined") { 339 distance = points[p].getElementsByTagName("Data")[2].getElementsByTagName("value")[0].childNodes[0].nodeValue; 340 } 341 if (typeof(points[p].getElementsByTagName("Point")[0]) !== "undefined") { 342 var coordinates = points[p].getElementsByTagName("Point")[0].getElementsByTagName("coordinates")[0].childNodes[0].nodeValue.split(/,/); 343 datapoints.push({"longitude":coordinates[0],"latitude":coordinates[1],"value":value,"numbers":numbers,"distance":distance,"name":name}); 344 if (value != "" && ! /^-?\d+(\.\d+)?(#-?\d+(\.\d+)?)*$/.test(value)) { numericalData = false; } 345 } 346 } catch(err) { 347 error('Fout! p="'+p+'" name="'+name+' '+err.message); 348 } 349 } 350 // process the style information; first reset style values 351 maxDistance = -1; 352 opacity = -1; 353 if (typeof(xmlDoc.getElementsByTagName("Style")[0]) !== "undefined") { 354 var styleNodes = xmlDoc.getElementsByTagName("Style")[0].childNodes; 355 if (styleNodes.length == 1 && /</.test(styleNodes[0].nodeValue)) { 356 // problem: style node contents was not parsed 357 var tmpNode = document.createElement("div"); 358 tmpNode.innerHTML = styleNodes[0].nodeValue; 359 styleNodes = tmpNode.childNodes; 360 } 361 for (var i=0;i<styleNodes.length;i++) { 362 if (typeof(styleNodes[i].id) !== "undefined" && styleNodes[i].id == "arvid") { 363 var color = styleNodes[i].getElementsByTagName("color")[0].childNodes[0].nodeValue; 364 var scale = styleNodes[i].getElementsByTagName("scale")[0].childNodes[0].nodeValue; 365 maxDistance = Number(scale); 366 color = color.replace(/......$/,""); 367 opacity = parseInt(color,16)/255; 368 } 369 } 370 } 371 if (numericalData) { 372 opacity = 1.0; 373 document.getElementById("opacity").value = opacity; 374 } 375 // adjust html form values 376 if (maxDistance >= 0) { document.getElementById("maxDistance").value = maxDistance; } 377 else { document.getElementById("maxDistance").value = ""; } 378 if (opacity >= 0) { document.getElementById("opacity").value = opacity; } 379 else { document.getElementById("opacity").value = ""; } 380 } 381 382 // save kml format 383 function saveKML () { 384 // take care of combined values 385 var values = []; 386 var combinedNames = []; 387 var combinedValues = []; 388 if (combine <= 1) { 389 for (var d=0;d<datapoints.length;d++) { 390 values[d] = datapoints[d].value; 391 if (typeof(updates[d]) !== "undefined") { values[d] = updates[d]; } 392 } 393 } else if (! numericalData) { 394 error("only numerical data can be combined!\n"); 395 for (var d=0;d<datapoints.length;d++) { 396 values[d] = datapoints[d].value; 397 if (typeof(updates[d]) !== "undefined") { values[d] = updates[d]; } 398 } 399 } else { 400 var combinations = combineValues(); 401 for (var d=0;d<datapoints.length;d++) { 402 values[d] = combinations[d].value; 403 combinedValues[d] = combinations[d].combinedValues; 404 combinedNames[d] = combinations[d].combinedNames; 405 } 406 } 407 // array values contains the values; combinedValues.length signals combination 408 var opacityHX = (256*opacity).toString(16); 409 opacityHX = opacityHX.replace(/\..*$/,""); 410 var date = new Date(); 411 var data = '<?xml version="1.0" encoding="UTF-8"?>\n'; 412 data += '<kml xmlns="http://www.opengis.net/kml/2.2">\n <Document>\n <name>Saved from Arvid at '+date+'</name>\n <Style>\n <IconStyle id="arvid">\n <color>'+opacityHX+'ffffff</color>\n <scale>'+maxDistance+'</scale>\n </IconStyle>\n </Style>\n'; 413 for (var d=0;d<datapoints.length;d++) { 414 var value = values[d]; 415 data += " <Placemark>\n <name>"+datapoints[d].name+"</name>\n <Point><coordinates>"+datapoints[d].longitude+","+datapoints[d].latitude+",0</coordinates></Point>\n <ExtendedData><Data name=\"value\"><value>"+value+"</value></Data>"; 416 if (combinedValues.length > 0) { data += "<Data name=\"CombinedNames\"><value>"+combinedNames[d]+"</value></Data><Data name=\"CombinedValues\"><value>"+combinedValues[d]+"</value></Data>"; } 417 data += "</ExtendedData>\n </Placemark>\n"; 418 }
419 data += " </Document>\n</kml>\n"; 420 // source: http://stackoverflow.com/questions/283956/is-there-any-way-to-specify-a-suggested-filename-when-using-data-uri 421 var tmplink = document.createElement('a'); 422 if (typeof tmplink.download === 'string') { 423 tmplink.href = "data:text/kml;charset=utf-8,"+encodeURIComponent(data); 424 tmplink.download = "arvid-"+makeDateString(date)+".kml"; 425 document.body.appendChild(tmplink); 426 tmplink.click(); 427 document.body.removeChild(tmplink); 428 } else { 429 window.location = "data:text/kml;charset=utf-8,"+encodeURIComponent(data); 430 } 431 } 432 433 // save csv format 434 function saveCSV () { 435 // take care of combined values 436 var values = []; 437 var combinedNames = []; 438 var combinedValues = []; 439 if (combine <= 1) { 440 for (var d=0;d<datapoints.length;d++) { 441 values[d] = datapoints[d].value; 442 if (typeof(updates[d]) !== "undefined") { values[d] = updates[d]; } 443 } 444 } else if (! numericalData) { 445 error("only numerical data can be combined!\n"); 446 for (var d=0;d<datapoints.length;d++) { 447 values[d] = datapoints[d].value; 448 if (typeof(updates[d]) !== "undefined") { values[d] = updates[d]; } 449 } 450 } else { 451 var combinations = combineValues(); 452 for (var d=0;d<datapoints.length;d++) { 453 values[d] = combinations[d].value; 454 combinedValues[d] = combinations[d].combinedValues; 455 combinedNames[d] = combinations[d].combinedNames; 456 } 457 } 458 // array values contains the values; combinedValues.length signals combination 459 var date = new Date(); 460 var data = 'longitude,latitude,value,name'; 461 if (combinedValues.length > 0) { data += ',"combined names","combined values"'; } 462 data += '\n'; 463 for (var d=0;d<datapoints.length;d++) { 464 var value = values[d]; 465 data += datapoints[d].longitude+','+datapoints[d].latitude+',"'+value+'","'+datapoints[d].name+'"'; 466 if (combinedValues.length > 0) { data += ',"'+combinedNames[d]+'","'+combinedValues[d]+'"'; } 467 data += "\n"; 468 } 469 // source: http://stackoverflow.com/questions/283956/is-there-any-way-to-specify-a-suggested-filename-when-using-data-uri 470 var tmplink = document.createElement('a'); 471 if (typeof tmplink.download === 'string') { 472 tmplink.href = "data:text/csv;charset=utf-8,"+encodeURIComponent(data); 473 tmplink.download = "arvid-"+makeDateString(date)+".csv"; 474 document.body.appendChild(tmplink); 475 tmplink.click(); 476 document.body.removeChild(tmplink); 477 } else { 478 window.location = "data:text/csv;charset=utf-8,"+encodeURIComponent(data); 479 } 480 } 481 482 function makeDateString(date) { 483 var dateString = date.getFullYear().toString(); 484 if (date.getMonth() < 9) { dateString += "0"; } 485 dateString += (date.getMonth()+1); 486 if (date.getDate() < 10) { dateString += "0"; } 487 dateString += date.getDate()+"-"; 488 if (date.getHours() < 10) { dateString += "0"; } 489 dateString += date.getHours(); 490 if (date.getMinutes() < 10) { dateString += "0"; } 491 dateString += date.getMinutes(); 492 if (date.getSeconds() < 10) { dateString += "0"; } 493 dateString += date.getSeconds(); 494 return(dateString); 495 } 496 497 // main table view function 498 function viewTable () { 499 var values = []; 500 var combinedNames = []; 501 var combinedValues = []; 502 if (combine <= 1) { 503 for (var d=0;d<datapoints.length;d++) { 504 values[d] = datapoints[d].value; 505 if (typeof(updates[d]) !== "undefined") { values[d] = updates[d]; } 506 } 507 } else if (! numericalData) { 508 error("only numerical data can be combined!\n"); 509 for (var d=0;d<datapoints.length;d++) { 510 values[d] = datapoints[d].value; 511 if (typeof(updates[d]) !== "undefined") { values[d] = updates[d]; } 512 } 513 } else { 514 var combinations = combineValues(); 515 for (var d=0;d<datapoints.length;d++) { 516 values[d] = combinations[d].value; 517 combinedValues[d] = combinations[d].combinedValues; 518 combinedNames[d] = combinations[d].combinedNames; 519 } 520 } 521 var table = '<table id="table" callspacing="5" border="1" class="sortable">\n'; 522 table += "<thead><tr><th>
522Longitude</th><th>Latitude</th><th>Value</th><th>Name</th>"; 523 if (combinedValues.length > 0) { table += "<th>Combined Names</th><th>Combined Values</th>"; } 524 table += "</tr></thead><tbody>\n"; 525 // gues size for value input field 526 var valueLength = 5; 527 if (typeof(values[0]) !== undefined && values[0].toString().length > valueLength) { valueLength = values[0].toString().length; } 528 if (typeof(values[datapoints.length-1]) !== undefined && values[datapoints.length-1].toString().length > valueLength) { valueLength = values[datapoints.length-1].toString().length; } 529 for (var d=0;d<datapoints.length;d++) { 530 var valueField = '<input type="text" size="'+valueLength+'" onchange="updates['+d+']=this.value;" value="'; 531 valueField += values[d]; 532 valueField += '"/>'; 533 table += "<tr><td>"+datapoints[d].longitude+"</td><td>"+datapoints[d].latitude+"</td><td>"+valueField+"</td><td>"+datapoints[d].name+"</td>\n"; 534 if (combinedValues.length > 0) { table += "<td>"+combinedNames[d]+"</td><td>"+combinedValues[d]+"</td>"; } 535 table += "</tr>\n"; 536 } 537 table += "</tbody></table>\n<p>"+datapoints.length+" data points</p>\n"; 538 document.getElementById("map").style.width = ""; 539 document.getElementById("map").style.height = ""; 540 document.getElementById("map").innerHTML = table; 541 document.getElementById("legend").innerHTML = ""; 542 document.getElementById("mapbuttons").style.visibility = "hidden"; 543 document.getElementById("mapbuttons2").style.visibility = "hidden"; 544 sorttable.makeSortable(document.getElementById("table")); 545 } 546 547 function combineValues() { 548 var distances = new Object; 549 var values = new Object; 550 var combinations = new Object; 551 var combinedValues = new Object; 552 var combinedNames = new Object; 553 if (combine > datapoints.length) { 554 combine = datapoints.length; 555 document.getElementById("combine").value = combine; 556 error("value of combine is larger than the number of points, adjusting value to "+combine+"...\n"); 557 } 558 for (var d=0;d<datapoints.length;d++) { 559 for (var e=d+1;e<datapoints.length;e++) { 560 var xd = Number(datapoints[d].longitude); 561 var yd = Number(datapoints[d].latitude); 562 var xe = Number(datapoints[e].longitude); 563 var ye = Number(datapoints[e].latitude); 564 var distance = (xd-xe)*(xd-xe)+(yd-ye)*(yd-ye); 565 var keys = [d,e]; 566 for (var k=0;k<keys.length;k++) { 567 var key = keys[k]; 568 var pair = d; if (key == d) { pair = e; } 569 if (typeof(distances[key]) === "undefined") { distances[key] = [{"pair":pair, "distance":distance}]; } 570 else { 571 var i = 0; 572 while (i < distances[key].length && distance > distances[key][i].distance) { i++; } 573 distances[key].splice(i,0,{"pair":pair, "distance":distance}); 574 } 575 } 576 } 577 } 578 for (var d=0;d<datapoints.length;d++) { 579 var blanksSeen = 0; 580 var value = datapoints[d].value; 581 value = value.replace(/#.*$/,""); // remove all but first number 582 if (typeof(updates[d]) != "undefined") { value = updates[d]; } 583 if (value == "") { values[d] = ""; blanksSeen++; combinedValues[d] = "EMPTY"; } 584 else { values[d] = Number(value); combinedValues[d] = value; } 585 combinedNames[d] = datapoints[d].name; 586 for (var k=0;k<combine-1;k++) { 587 value = datapoints[distances[d][k].pair].value; 588 if (typeof(updates[distances[d][k].pair]) != "undefined") { value = updates[distances[d][k].pair]; } 589 if (value == "") { blanksSeen++; } 590 else { 591 if (values[d] == "") { values[d] = Number(value); } 592 else { values[d] += Number(value); } 593 } 594 if (value == "") { value = "EMPTY"; } 595 var name = datapoints[distances[d][k].pair].name; 596 var sep = "#"; 597 if (k == combine-2) { sep = "#"; } 598 combinedValues[d] += sep+value; 599 combinedNames[d] += sep+name; 600 } 601 if (combine > blanksSeen) { 602 values[d] /= combine-blanksSeen;
603 // check for rounding errors 604 values[d] = fixRoundingErrors(values[d]); 605 } 606 combinations[d] = { "value":values[d], "combinedValues":combinedValues[d],"combinedNames":combinedNames[d] }; 607 } 608 return(combinations); 609 } 610 611 // main map function 612 function viewMap () { 613 // combine values 614 var combine = Number(document.getElementById("combine").value); 615 var values = []; 616 var combinedNames = []; 617 var combinedValues = []; 618 if (combine <= 1) { 619 for (var d=0;d<datapoints.length;d++) { 620 values[d] = datapoints[d].value; 621 if (typeof(updates[d]) !== "undefined") { values[d] = updates[d]; } 622 } 623 } else if (! numericalData) { 624 error("only numerical data can be combined!\n"); 625 for (var d=0;d<datapoints.length;d++) { 626 values[d] = datapoints[d].value; 627 if (typeof(updates[d]) !== "undefined") { values[d] = updates[d]; } 628 } 629 } else { 630 var combinations = combineValues(); 631 for (var d=0;d<datapoints.length;d++) { 632 values[d] = combinations[d].value; 633 combinedValues[d] = combinations[d].combinedValues; 634 combinedNames[d] = combinations[d].combinedNames; 635 } 636 } 637 if (distance != 0) { 638 if (numericalData) { error("cannot use nonzero distance for numerical!\n"); } 639 else if (isNaN(distance)) { error("illegal distance target: "+distance+"!\n"); } 640 else { 641 for (var d=0;d<datapoints.length;d++) { 642 if (typeof(datapoints[d].distance) === "undefined") { values[d] = ""; } 643 else if (isNaN(datapoints[d].distance)) { error("illegal numeric value "+datapoints[d].distance+" for location "+datapoints[d].name+"!\n"); values[d] = ""; } 644 else if (Number(datapoints[d].distance) >= Number(distance)) { values[d] = "MoreThanDistance"; } 645 else { values[d] = "LessThanDistance"; } 646 } 647 } 648 } 649 if (distance == 0 && transition > 0) { // transition zone requested 650 if (datapoints.length == 0 || typeof(datapoints[0].numbers) == "undefined" || ! /,/.test(datapoints[0].numbers)) { 651 error("cannot compute transition zones: missing data\n"); 652 } else { 653 var margins = []; 654 for (var d=0;d<datapoints.length;d++) { 655 var numbers = datapoints[d].numbers.split(/,/); 656 margins[d] = []; 657 margins[d].index = d; 658 margins[d].margin = Math.abs(Number(numbers[1])-Number(numbers[0])); 659 for (var m=2;m<numbers.length;m++) { 660 for (var n=0;n<m;n++) { 661 var margin = Math.abs(Number(numbers[m])-Number(numbers[n])); 662 if (margin < margins[d].margin) { margins[d].margin = margin; } 663 } 664 } 665 } 666 margins.sort(function(a,b) { return(a.margin-b.margin); }) 667 for (var i=0;i<transition;i++) { values[margins[i].index] = "transition zone"; } 668 } 669 } 670 // read coordinates 671 var sites = []; 672 var xLeft; 673 var xRight; 674 var yTop; 675 var yBottom; 676 var minValue; 677 var maxValue; 678 var minValue2; 679 var maxValue2; 680 var minValue3; 681 var maxValue3; 682 for (var d=0;d<datapoints.length;d++) { 683 var x = Number(datapoints[d].longitude); 684 var y = Number(datapoints[d].latitude); 685 var value = values[d]; 686 if (numericalData && value != "") { 687 var fields = value.split("#"); 688 if (typeof(maxValue) === "undefined" || Number(fields[0]) > maxValue) { maxValue = Number(fields[0]); } 689 if (typeof(minValue) === "undefined" || Number(fields[0]) < minValue) { minValue = Number(fields[0]); } 690 if (typeof(fields[1]) !== "undefined") { 691 if (typeof(maxValue2) === "undefined" || Number(fields[1]) > maxValue2) { maxValue2 = Number(fields[1]); } 692 if (typeof(minValue2) === "undefined" || Number(fields[1]) < minValue2) { minValue2 = Number(fields[1]); } 693 } 694 if (typeof(fields[2]) !== "undefined") { 695 if (typeof(maxValue3) === "undefined" || Number(fields[2]) > maxValue3) { maxValue3 = Number(fields[2]); } 696 if (typeof(minValue3) === "undefined" || Number(fields[2]) < minValue3) { minValue3 = Number(fields[2]); } 697 } 698 } 699 var combinedValue = ""; 700 if (typeof(combinedValues[d]) !== "undefined") { combinedValue = " (combined: "+combinedValues[d]+")"; } 701 var combinedName = ""; 702 if (typeof(combinedNames[d]) !== "undefined") { combinedName = " (combined: "+combinedNames[d]+")"; } 703 sites.push({'x':x,'y':y,'d':value,'name':datapoints[d].name+combinedName,'combinedValues':combinedValue}); 704 if (typeof(xLeft) === "undefined" || x < xLeft) { xLeft = x; } 705 if (typeof(xRight) === "undefined" || x > xRight) { xRight = x; } 706 if (typeof(yTop) === "undefined" || y > yTop) { yTop = y; } 707 if (typeof(yBottom) === "undefined" || y < yBottom) { yBottom = y; } 708 } 709 // if (numericalData && datapoints.length > 0 && minValue == maxValue) { numericalData = false; } 710 // this doesn't work if a pole or the date line lies in the area 711 var xCenter = (xLeft+xRight)/2; 712 var yCenter = (yTop+yBottom)/2; 713 var xDiff = Math.abs(xRight-xLeft); 714 var yDiff = Math.abs(yTop-yBottom); 715 // assumption: yDiff=>zoom: 2.8=>7 1.4=>8 5.6=>6 716 var zoomY = Math.round(7-Math.log(yDiff/2.8)/Math.log(2)); 717 var zoomX = Math.round(8-Math.log(xDiff/5)/Math.log(2)); 718 var zoom = zoomY; 719 if (zoomX < zoomY) { zoom = zoomX; } 720 if (maxDistance < 0) { // unset: set default value 721 maxDistance = 0.28*yDiff/2.8; // estimated from node distribution 722 maxDistance = Math.floor(10000*maxDistance)/10000; 723 document.getElementById("maxDistance").value = maxDistance; 724 } 725 if (opacity < 0) { 726 opacity = 0.2; 727 document.getElementById("opacity").value = opacity; 728 } 729 730 // data points: array of {longitude,latitude,value} 731 // (in array "sites" in file sites.js) 732 // computer voronoi areas 733 var bbox = {xl:xLeft-2*maxDistance,xr:xRight+2*maxDistance,yb:yTop+2*maxDistance,yt:yBottom-2*maxDistance}; 734 var voronoi = new Voronoi(); 735 var diagram = voronoi.compute(sites, bbox); 736 var cells = diagram.cells; 737 var colors = [ 738 'rgba(255, 255, 255, '+opacity+')', // white 739 'rgba(255, 0, 0, '+opacity+')', // red 740 'rgba( 0, 255, 0, '+opacity+')', // green 741 'rgba( 0, 0, 255, '+opacity+')', // blue 742 'rgba(255, 255, 0, '+opacity+')', // yellow 743 'rgba(255, 0, 255, '+opacity+')', // magenta 744 'rgba( 0, 255, 255, '+opacity+')', // cyan 745 'rgba(127, 0, 0, '+opacity+')', // light red 746 'rgba( 0, 127, 0, '+opacity+')', // light green 747 'rgba( 0, 0, 127, '+opacity+')', // light blue 748 'rgba(127, 127, 0, '+opacity+')', // light yellow 749 'rgba(127, 0, 127, '+opacity+')', // light magenta 750 'rgba( 0, 127, 127, '+opacity+')', // light cyan 751 'rgba( 0, 0, 0, '+opacity+')']; // black 752 var polygons = []; 753 var points = []; 754 for (var i=0;i<cells.length;i++) { 755 // process edges 756 var edges = new Array; 757 for (var j=0;j<cells[i].halfedges.length;j++) { 758 edges.push([cells[i].halfedges[j].edge.va.x,cells[i].halfedges[j].edge.va.y]); 759 edges.push([cells[i].halfedges[j].edge.vb.x,cells[i].halfedges[j].edge.vb.y]); 760 } 761 edges = sortEdges(edges); 762 edges = clipEdges(cells[i].site.x,cells[i].site.y,edges,maxDistance); 763 var key = cells[i].site.d+" "+i;
764 if (typeof(polygons[key]) === "undefined") { polygons[key] = []; } 765 points.push([cells[i].site.x,cells[i].site.y]); 766 polygons[key].push([edges[0]]); 767 var polygonsLength = polygons[key].length 768 for (var k=2;k<edges.length;k+=2) { polygons[key][polygonsLength-1].push(edges[k]); } 769 } 770 771 var iconFeatures = new Object; 772 var vectorSources = new Object; 773 var iconStyles = new Object; 774 var vectorLayers = new Object; 775 776 // draw the points 777 var pointList = []; 778 for (var i=0;i<points.length;i++) { 779 var pointFeatures = new ol.Feature({ geometry: new ol.geom.Point([points[i][0],points[i][1]]).transform('EPSG:4326', 'EPSG:3857') }); 780 pointList.push(pointFeatures); 781 } 782 vectorSources['points'] = new ol.source.Vector({ features: pointList }); 783 iconStyles['points'] = new ol.style.Style({ 784 image: new ol.style.Circle({ 785 radius: 2, 786 fill: new ol.style.Fill({color: 'black'}) 787 }), 788 stroke: new ol.style.Stroke({ 789 color: 'black', 790 width: 2 791 }), 792 fill: new ol.style.Fill({ 793 color: 'black' 794 }), 795 }); 796 vectorLayers['points'] = new ol.layer.Vector({ 797 source: vectorSources['points'], 798 style: iconStyles['points'] 799 }); 800 801 // sort the polygon keys 802 var polygonKeys = []; 803 for (var key in polygons) { polygonKeys.push(key); } 804 polygonKeys.sort(); 805 // only use color=white for unknown/empty data values 806 var colorsIndex = 1; 807 var polygonColors = new Object; 808 polygonColors[""] = colors[0]; 809 for (var k=0;k<polygonKeys.length;k++) { 810 if (typeof(polygonKeys[k]) == "undefined") { polygonKeys[k] = ""; } 811 var key = polygonKeys[k]; 812 var shortKey = key.replace(/\s+\d+$/,""); 813 index = key.replace(/.*\s/,""); 814 iconFeatures[key] = new ol.Feature({ geometry: new ol.geom.Polygon(polygons[key]).transform('EPSG:4326', 'EPSG:3857'), value:key, name:cells[index].site.name, combinedValues:cells[index].site.combinedValues }); 815 vectorSources[key] = new ol.source.Vector({ features: [ iconFeatures[key] ] }); 816 var thisColor = "rgba(0,0,0,1)"; // black 817 if (numericalData) { 818 var fields = shortKey.split("#"); 819 shortKey = fields[0]; 820 var shortKey2; 821 var shortKey3; 822 if (typeof(fields[1]) !== "undefined") { shortKey2 = fields[1]; } 823 if (typeof(fields[2]) !== "undefined") { shortKey3 = fields[2]; } 824 if (shortKey == "") { thisColor = polygonColors[""]; } 825 else { 826 var blueFactor = 255-Math.floor(255*0.5); // default for minValue == maxValue 827 var redFactor = 0; // default 828 var greenFactor = 255; // default 829 if (minValue != maxValue) { blueFactor = 255-Math.floor(255*(maxValue-shortKey)/(maxValue-minValue)); } 830 if (typeof(minValue2) !== "undefined" && minValue2 != maxValue2) { redFactor = 255-Math.floor(255*(shortKey2-minValue2)/(maxValue2-minValue2)); } 831 if (typeof(minValue3) !== "undefined" && minValue3 != maxValue3) { greenFactor = 255-Math.floor(255*(maxValue3-shortKey3)/(maxValue3-minValue3)); } 832 thisColor = 'rgba('+redFactor+','+blueFactor+','+greenFactor+','+opacity+')'; 833 thisColor = paletteF(thisColor); 834 } 835 } else if (typeof(polygonColors[shortKey]) !== "undefined") { 836 thisColor = polygonColors[shortKey]; 837 } else { 838 thisColor = colors[colorMap[colorsIndex]]; 839 colorsIndex++; 840 thisColor = paletteF(thisColor); 841 polygonColors[shortKey] = thisColor; 842 } 843 iconStyles[key] = new ol.style.Style({ 844 image: new ol.style.Circle({ 845 radius: 10, 846 fill: new ol.style.Fill({color: 'black'}) 847 }), 848 stroke: new ol.style.Stroke({ 849 color: "black", 850 width: 0 851 }), 852 fill: new ol.style.Fill({ 853 color: thisColor 854 }), 855 }); 856 vectorLayers[key] = new ol.layer.Vector({ 857 source: vectorSources[key], 858 style: iconStyles[key] 859 }); 860 if (colorsIndex >= colors.length) { colorsIndex = 1; error("too many colors required!"); } 861 } 862 863 var OSMbase = new ol.layer.Tile({ source: new ol.source.OSM() }) 864 var layers = []; 865 if (typeof(document.getElementById("backgroundMap").value) != "undefined" && 866 document.getElementById("backgroundMap").value == "on") { layers = [OSMbase]; } 867 for (var key in vectorLayers) { if (key != 'points') { layers.push(vectorLayers[key]); } } 868 layers.push(vectorLayers['points']); // last layer to add: should be visible 869 document.getElementById("map").style.width = "700"; 870 document.getElementById("map").style.height = "525"; 871 document.getElementById("map").innerHTML = ""; 872 document.getElementById("mapbuttons").style.visibility = "visible"; 873 document.getElementById("mapbuttons2").style.visibility = "visible"; 874 875 var map = new ol.Map({ 876 layers: layers, 877 renderer: 'canvas', 878 target: 'map', 879 controls: ol.control.defaults({ 880 attributionOptions: /** @type {olx.control.AttributionOptions} */ ({ 881 collapsible: false 882 }) 883 }), 884 //center: ol.proj.transform([4.834167,52.143333], 'EPSG:4326', 'EPSG:3857'), 885 view: new ol.View({ 886 center: ol.proj.transform([xCenter,yCenter], 'EPSG:4326', 'EPSG:3857'), 887 zoom: zoom 888 }) 889 }); 890 891 // process click on map for extra information 892 // source: http://gis.stackexchange.com/questions/124137/openlayers-foreachfeatureatpixel-finds-point-when-clicking-in-the-lower-part-or 893 map.on('singleclick', function(evt) { 894 document.getElementById("comment").innerHTML = ""; 895 var allFeaturesAtPixel = [];
896 map.forEachFeatureAtPixel(evt.pixel, function(feature, layer) { allFeaturesAtPixel.push(feature); }); 897 for (var i=0; i<allFeaturesAtPixel.length; i++) { 898 if (typeof(allFeaturesAtPixel[i].get('value')) !== "undefined") { 899 var shortKey = allFeaturesAtPixel[i].get('value').replace(/\s+\d+$/,""); 900 document.getElementById("comment").innerHTML += 'name: '+allFeaturesAtPixel[i].get('name')+'<br>value: '+shortKey+allFeaturesAtPixel[i].get('combinedValues')+'<br>'; 901 } 902 } 903 }); 904 905 // make legend 906// 20150603 see also: http://codepen.io/guyfedwards/pen/xcfEb 907 document.getElementById("legend").innerHTML = datapoints.length+" data points | "; 908 if (! numericalData) { 909 var block = ""; 910 var counter = 0; 911 for (var k in polygonColors) { 912 if (k != "") { 913 block += "<font style=\"background:"+color2hex(polygonColors[k])+"; opacity: "+opacity+";\" value=\"0\"> </font>\n"; 914 block += '<select id="legend-'+counter+'" onchange="colorMap['+(counter)+']=this.selectedIndex; viewMap();">\n'; 915 block += "<option style=\"background:"+color2hex(polygonColors[k])+"; value=\"0\">value: "+k+"</option>\n"; 916 for (var c=1;c<=13;c++) { 917 var bgcolor = colors[c]; 918 bgcolor = paletteF(bgcolor); 919 //block += "<option style=\"background:"+color2hex(colors[c])+"; opacity: "+opacity+";\" value=\""+c+"\"> "+colorNames[c]+"</option>\n"; 920 block += "<option style=\"background:"+color2hex(bgcolor)+"; value=\""+c+"\"> "+" "+"</option>\n"; // " " was colornames[c] 921 } 922 block += '</select> |\n'; 923 } 924 counter++; 925 } 926 document.getElementById("legend").innerHTML += block; 927 } 928 } 929 930 // convert color string to hex code 931 // input example: rgba(255, 255, 0, 0.2) 932 function color2hex(string) { 933 string = string.replace(/[^0-9]+/g,"#"); 934 var fields = string.split("#"); 935 while (fields.length > 0 && fields[0] == "") { fields.splice(0,1); } 936 var nbr1 = Number(fields[0]).toString(16); if (fields[0] < 16) { nbr1 = "0"+nbr1; } 937 var nbr2 = Number(fields[1]).toString(16); if (fields[1] < 16) { nbr2 = "0"+nbr2; } 938 var nbr3 = Number(fields[2]).toString(16); if (fields[2] < 16) { nbr3 = "0"+nbr3; } 939 return("#"+nbr1+nbr2+nbr3); 940 } 941 942 // put edges in the right order 943 // format of parameter edges: list of edge poiints: edges[i]->edges[i+1] 944 function sortEdges(edges) { 945 var i = 2; // don't change location of first edge 946 while (i < edges.length) { 947 var j = i; 948 var x = edges[i-1][0]; 949 var y = edges[i-1][1]; 950 // find point that matches previous edge: edges should connect like a circle 951 while (j < edges.length-1 && (edges[j][0] != x || edges[j][1] != y)) { j++; } 952 if (edges[j][0] != x || edges[j][1] != y) { 953 if (x == edges[0][0] && y == edges[0][1]) { 954 edges.splice(i); // HACK! throws away redundant edges but where did they come from??? 955 } else { 956 error("sortEdges: problem with linking edges! "+i+": ("+x+","+y+") not in: "+edges); 957 } 958 } else { 959 // test if common point lies art the end of the edge 960 if (j%2 != 0) { // switch points 961 var keepx = edges[j][0]; 962 var keepy = edges[j][1]; 963 edges[j][0] = edges[j-1][0]; 964 edges[j][1] = edges[j-1][1]; 965 edges[j-1][0] = keepx; 966 edges[j-1][1] = keepy; 967 j--; 968 } 969 // move edge to the correct position if necessary 970 if (j > i) { 971 var keepx1 = edges[j][0]; 972 var keepy1 = edges[j][1]; 973 var keepx2 = edges[j+1][0]; 974 var keepy2 = edges[j+1][1]; 975 edges[j][0] = edges[i][0]; 976 edges[j][1] = edges[i][1]; 977 edges[j+1][0] = edges[i+1][0]; 978 edges[j+1][1] = edges[i+1][1]; 979 edges[i][0] = keepx1; 980 edges[i][1] = keepy1; 981 edges[i+1][0] = keepx2; 982 edges[i+1][1] = keepy2; 983 } 984 } 985 i += 2; 986 } 987 return(edges); 988 } 989 990 // map points from to line between (x0,y0) and (x1,y1) to circle and add these to polygon 991 function addCurve(nodes,sitex,sitey,x0,y0,x1,y1,nbrOfPoints) { 992 // now map all points 993 for (var j=0;j<=nbrOfPoints;j++) { 994 var x = x0+(x1-x0)*j/nbrOfPoints; 995 var y = y0+(y1-y0)*j/nbrOfPoints; 996 var distance = Math.sqrt((x-sitex)*(x-sitex)+(y-sitey)*(y-sitey)); 997 var newx = sitex+(x-sitex)*maxDistance/distance; 998 var newy = sitey+(y-sitey)*maxDistance/distance; 999 nodes.push([newx,newy]); 1000 }
1001 return(nodes); 1002 } 1003 1004 // clip the edges if they are too long 1005 // center point: (sitex,sitey) 1006 // edges: ((x1,y1),(x2,y2),...,(xn,yn)) with xn=x1 and yn=y1 1007 function clipEdges(sitex,sitey,edges,maxDistance) { 1008 var distances = []; 1009 for (var i=0;i<edges.length;i+=2) { 1010 var x0 = edges[i][0]; 1011 var y0 = edges[i][1]; 1012 var distance0 = Math.sqrt((x0-sitex)*(x0-sitex)+(y0-sitey)*(y0-sitey)); 1013 var x1 = edges[i+1][0]; 1014 var y1 = edges[i+1][1]; 1015 var distance1 = Math.sqrt((x1-sitex)*(x1-sitex)+(y1-sitey)*(y1-sitey)); 1016 if (distance0 <= maxDistance && distance1 <= maxDistance) { /// 1017 // both edge points are near the site: use them 1018 distances.push([x0,y0]); distances.push([x1,y1]); 1019 } else if (distance0 > maxDistance && distance1 > maxDistance) { /// 1020 // both edge points are far away from the site 1021 // check if line intersects with bounding circle 1022 var points = findIntersectionPoints(sitex,sitey,maxDistance,x0,y0,x1,y1); 1023 if (typeof(points[1]) !== "undefined" && 1024 ((x0 <= points[0].x && points[0].x <= x1 && x0 <= points[1].x && points[1].x <= x1) || 1025 (x0 >= points[0].x && points[0].x >= x1 && x0 >= points[1].x && points[1].x >= x1)) && 1026 ((y0 <= points[0].y && points[0].y <= y1 && y0 <= points[1].y && points[1].y <= y1) || 1027 (y0 >= points[0].y && points[0].y >= y1 && y0 >= points[1].y && points[1].y >= y1))) { 1028 // two intersection points found: use them in the right order 1029 if (((x0 <= x1 && points[0].x <= points[1].x) || (x0 >= x1 && points[0].x >= points[1].x)) && 1030 ((y0 <= y1 && points[0].y <= points[1].y) || (y0 >= y1 && points[0].y >= points[1].y))) { 1031 distances = addCurve(distances,sitex,sitey,x0,y0,points[0].x,points[0].y,9); 1032 distances.push([points[0].x,points[0].y]); 1033 distances.push([points[1].x,points[1].y]); 1034 distances = addCurve(distances,sitex,sitey,points[1].x,points[1].y,x1,y1,9); 1035 } else { 1036 distances = addCurve(distances,sitex,sitey,x0,y0,points[1].x,points[1].y,9); 1037 distances.push([points[1].x,points[1].y]); 1038 distances.push([points[0].x,points[0].y]); 1039 distances = addCurve(distances,sitex,sitey,points[0].x,points[0].y,x1,y1,9); 1040 } 1041 } else { 1042 // one or no intersection points found: map the edge points to the bounding circle plus some intermediate points 1043 distances = addCurve(distances,sitex,sitey,x0,y0,x1,y1,99); 1044 } 1045 } else { 1046 // one edge point is near the site, the other is far away 1047 // find out where the line intersects with the bounding circle 1048 var points = findIntersectionPoints(sitex,sitey,maxDistance,x0,y0,x1,y1); 1049 // the function will find two points: choose the one between the edge points 1050 var inbetween = []; 1051 if (((x0 <= points[0].x && points[0].x <= x1) || (x0 >= points[0].x && points[0].x >= x1)) && 1052 ((y0 <= points[0].y && points[0].y <= y1) || (y0 >= points[0].y && points[0].y >= y1))) { 1053 inbetween = [points[0].x,points[0].y]; 1054 } else { inbetween = [points[1].x,points[1].y]; } 1055 if (distance0 <= maxDistance) { 1056 distances.push([x0,y0]); 1057 distances.push(inbetween); 1058 distances = addCurve(distances,sitex,sitey,inbetween[0],inbetween[1],x1,y1,9); 1059 } else { 1060 distances = addCurve(distances,sitex,sitey,x0,y0,inbetween[0],inbetween[1],9); 1061 distances.push(inbetween); 1062 distances.push([x1,y1]); 1063 } 1064 } 1065 } 1066 var newEdges = []; 1067 for (var i=0;i<distances.length-1;i++) { 1068 if (distances[i][0] != distances[i+1][0] || distances[i][1] != distances[i+1][1]) { 1069 newEdges.push(distances[i]); 1070 newEdges.push(distances[i+1]); 1071 } 1072 } 1073 if (newEdges[0][0] != newEdges[newEdges.length-1][0] || newEdges[0][1] != newEdges[newEdges.length-1][1]) { 1074 newEdges.push(newEdges[newEdges.length-1]); 1075 newEdges.push(newEdges[0]); 1076 } 1077 return(newEdges); 1078 } 1079 1080 function findIntersectionPoints(sitex,sitey,maxDistance,x0,y0,x1,y1) { 1081 if (x1 == x0 && y0 == y1) { 1082 error("findIntersectionPoints called with duplicate points: "+x0+" "+y0); 1083 return([]); 1084 } else if (x1-x0 != 0) { // non-vertical line 1085 // find a point at the line x0,y0 - x1,y1 at maxDistance from center 1086 // formula of the line: y = y1 + (y2-y1)/(x2-x1)*(x-x1) 1087 // so: y = b + a*x with a = (y2-y1)/(x2-x1) and b = y1 - x1*a 1088 var a = (y1-y0)/(x1-x0); 1089 var b = (y0*x1-y1*x0)/(x1-x0); 1090 // formula of the circle: (x-sitex)^2 + (y-sitey)^2 = maxDistance^2 1091 // so: y = sitey+sqrt(maxDistance^2-(x-sitex)^2) 1092 // so: sitey+sqrt(maxDistance^2-(x-sitex)^2) = b + a*x 1093 // so: maxDistance^2-(x-sitex)^2 = (b + a*x - sitey)^2 1094 // so: -x^2 + 2*sitex*x - sitex^2 = -maxDistance^2 + (b-sitey)^2 + 2*(b-sitey)*a*x + a^2*x^2 1095 // so: -x^2 - a^2*x^2 => x^2 * (-1-a^2) 1096 // x*2*sitex - x*2*(b-sitey)*a => x * (2*sitex-2*a*(b-sitey)) 1097 // -sitex^2 + maxDistance^2 - (b-sitey)^2 = 0 => 1 * (-sitex^2 + maxDistance^2 - (b-sitey)^2) 1098 // now X1,X2 = (-b +- sqrt(b^2-4*a*c))/(2*a) 1099 var A = -1-a*a; 1100 var B = 2*sitex-2*a*(b-sitey); 1101 var C = -sitex*sitex+maxDistance*maxDistance-b*b+2*b*sitey-sitey*sitey; 1102 if (B*B >= 4*A*C) { 1103 var point1 = new Object; 1104 var point2 = new Object; 1105 point1.x = (-B+Math.sqrt(B*B-4*A*C))/(2*A); 1106 point2.x = (-B-Math.sqrt(B*B-4*A*C))/(2*A); 1107 point1.y = a*point1.x+b; 1108 point2.y = a*point2.x+b; 1109 if (B*B > 4*A*C) { return([point1,point2]); } 1110 else { return([point1]); } 1111 } 1112 return([]); 1113 } else { // vertical line: x = a*y+b 1114 var a = 0; 1115 var b = x0; 1116 // the computation of A, B and C is explained in the comments above 1117 var A = -1-a*a; 1118 var B = 2*sitey-2*a*(b-sitex); 1119 var C = -sitey*sitey+maxDistance*maxDistance-b*b+2*b*sitex-sitex*sitex; 1120 if (B*B >= 4*A*C) { 1121 var point1 = new Object; 1122 var point2 = new Object; 1123 point1.y = (-B+Math.sqrt(B*B-4*A*C))/(2*A); 1124 point2.y = (-B-Math.sqrt(B*B-4*A*C))/(2*A); 1125 point1.x = a*point1.y+b; 1126 point2.x = a*point2.y+b; 1127 if (B*B > 4*A*C) { return([point1,point2]); } 1128 else { return([point1]); } 1129 } 1130 return([]); 1131 } 1132 } 1133 1134 function error(string) { 1135 document.getElementById("error").innerHTML += "<br><font color=\"red\">"+string+"</string>"; 1136 } 1137 1138 function cleanError() { 1139 document.getElementById("error").innerHTML = ""; 1140 document.getElementById("comment").innerHTML = ""; 1141 } 1142 1143 function examples(code) { 1144 cleanError(); 1145 if (typeof(code) === "undefined" || code == "0") { mainSitesFile = "default.kml"; } 1146 else if (code == "1") { mainSitesFile = "ex-newyork.kml"; } 1147 else if (code == "2") { mainSitesFile = "ex-erik1.kml"; } 1148 else if (code == "3") { mainSitesFile = "ex-erik2.kml"; } 1149 else if (code == "4") { mainSitesFile = "ex-nina1.kml"; } 1150 else if (code == "5") { mainSitesFile = "ex-basque21.kml"; } 1151 else if (code == "6") { mainSitesFile = "ex-basque22.kml"; } 1152 else if (code == "7") { mainSitesFile = "ex-basque23.kml"; } 1153 else if (code == "8") { mainSitesFile = "ex-basque24.kml"; } 1154 else if (code == "9") { mainSitesFile = "ex-basque25.kml"; } 1155 else if (code == "10") { mainSitesFile = "ex-basque26.kml"; } 1156 else if (code == "11") { mainSitesFile = "ex-basque27.kml"; } 1157 else if (code == "12") { mainSitesFile = "ex-basque28.kml"; } 1158 else if (code == "13") { mainSitesFile = "ex-basque29.kml"; } 1159 else if (code == "14") { mainSitesFile = "ex-spruit.kml"; } 1160 else { mainSitesFile = "default.kml"; } 1161 processData(mainSitesFile); 1162 if (typeof(document.getElementById("mode").value) != "undefined" && 1163 document.getElementById("mode").value == "table") { viewTable(); } 1164 else { viewMap(); } 1165 } 1166 1167 function checkCGI() { 1168 var search = window.location.search; 1169 search = search.replace(/^\?/,""); 1170 var fields = search.split(/&/); 1171 for (var i=0;i<fields.length;i++) { 1172 var parts = fields[i].split(/=/); 1173 if (typeof(parts[1]) !== "undefined") { 1174 if (parts[0] == "backgroundMap") { document.getElementById("backgroundMap").value = parts[1]; } 1175 else if (parts[0] == "combine") { combine = Number(parts[1]); } 1176 else if (parts[0] == "example") { examples(parts[1]); } 1177 else if (parts[0] == "maxDistance") { document.getElementById("maxDistance").value = parts[1]; } 1178 else if (parts[0] == "mode") { mode = parts[1]; } 1179 else if (parts[0] == "opacity") { document.getElementById("opacity").value = parts[1]; } 1180 else if (parts[0] == "transition") { transition = Number(parts[1]); } 1181 } 1182 } 1183 } 1184 1185 function fixRoundingErrors(value) { 1186 var string = value.toString(); 1187 if (/\./.test(string)) { 1188 if (/0000[0-4]$/.test(string)) { string = string.replace(/([^0])0*0000[0-4]$/,"$1"); } 1189 else if (/9999[5-9]$/.test(string)) { string = string.replace(/([^9])9*9999[5-9]$/,"$1"); } // rounding down here? 1190 value = Number(string); 1191 } 1192 return(value); 1193 } 1194 1195 function paletteF(thisColor) { 1196 var fields = thisColor.split(/[,()]/); 1197 var red = fields[1]; 1198 var green = fields[2]; 1199 var blue = fields[3]; 1200 var opacity = fields[4]; 1201 var RGBcolors = [red,green,blue]; // default 1202 var MAXcolor = 255; 1203 var nbrOfSwitches = 8; 1204 if (isNaN(palette)) { palette = 0; } 1205 while (palette < 0) { window.palette += 48; } 1206 while (palette >= 48) { window.palette -= 48; } 1207 document.getElementById('palette').value = palette; 1208 var colorSwitch = Math.floor(palette/nbrOfSwitches); 1209 if (colorSwitch == 1) { RGBcolors = [green,red,blue]; } 1210 else if (colorSwitch == 2) { RGBcolors = [blue,green,red]; } 1211 else if (colorSwitch == 3) { RGBcolors = [red,blue,green]; } 1212 else if (colorSwitch == 4) { RGBcolors = [green,blue,red]; } 1213 else if (colorSwitch == 5) { RGBcolors = [blue,red,green]; } 1214 var remainder = palette-nbrOfSwitches*colorSwitch; 1215 var switch1 = Math.floor(remainder/4); 1216 var switch2 = Math.floor((remainder-4*switch1)/2); 1217 var switch3 = remainder-4*switch1-2*switch2; 1218 if (switch1 > 0) { RGBcolors[0] = MAXcolor-RGBcolors[0]; } 1219 if (switch2 > 0) { RGBcolors[1] = MAXcolor-RGBcolors[1]; } 1220 if (switch3 > 0) { RGBcolors[2] = MAXcolor-RGBcolors[2]; } 1221 thisColor = 'rgba('+RGBcolors[0]+','+RGBcolors[1]+','+RGBcolors[2]+','+opacity+')'; 1222 return(thisColor); 1223 } 1224 </script>
1224 1225 </body> 1226</html>
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.