var topoKey = 'BGMCCPGOZNKXPZC';var topoRequests = new Array(); var topoLastIndex = 0; var topoUrlChars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.!*()'; var topoUrlCharsLength = topoUrlChars.length; var topoUrlCharsSqrt = Math.floor( Math.sqrt( topoUrlCharsLength ) ); var topoUrlMaxCoordinates = 280; function topoToFeets( altitude ) { return altitude * 3.28084; } function topoParseAngle( value ) { var str = ( '' + value ).toUpperCase(); var arr = str.match(/N|S|E|W|[\+\-]|\d*(\.)?\d+/g); var sign = 1.0; var divider = 1.0; var result = 0.0; for ( var i = 0; i < arr.length; i++ ) { if ( ( arr[ i ] == 'N' ) || ( arr[ i ] == 'E' ) || ( arr[ i ] == '+' ) ) { sign = 1.0; } else if ( ( arr[ i ] == 'S' ) || ( arr[ i ] == 'W' ) || ( arr[ i ] == '-' ) ) { sign = -1.0; } else // it is a number { result = result + arr[ i ] / divider; divider = divider * 60; } } return Math.round(result*sign*10000)/10000; } function topoEncodeCoordinates( coordinates ) { var result = ''; for ( var j = 0; j < coordinates.length; j++ ) { var lat = topoParseAngle( coordinates[ j ][ 0 ] ); var lon = topoParseAngle( coordinates[ j ][ 1 ] ); if ( lat < 0.0 ) lat = 180 + lat; lat = lat / 180.0; lat = lat - Math.floor( lat ); if ( lon < 0.0 ) lon = 360 + lon; lon = lon / 360; lon = lon - Math.floor( lon ); for ( var i = 0; i < 3; i++ ) { lat = lat * topoUrlCharsLength; var index = Math.floor( lat ); lat = lat - index; result = result + topoUrlChars.substring( index, index+1 ); lon = lon * topoUrlCharsLength; var index = Math.floor( lon ); lon = lon - index; result = result + topoUrlChars.substring( index, index+1 ); } lat = lat * topoUrlCharsSqrt; lon = lon * topoUrlCharsSqrt; var index = Math.floor( lat ) * topoUrlCharsSqrt + Math.floor( lon ); result = result + topoUrlChars.substring( index, index+1 ); } return result; } function topoQuietTimeout( id ) { topoRequests[ id ] = null; } function topoTimeout( id ) { var request = topoRequests[ id ]; topoRequests[ id ] = null; if ( request ) { var topoScript = document.getElementById('topoScript' + id); var topHead = document.getElementsByTagName('head').item(0); topHead.removeChild(topoScript); for ( var i = 0; i < request.length; i++ ) if ( request[ i ] ) { if ( request[ i ][ 1 ] ) request[ i ][ 0 ]( null, request[ i ][ 1 ] ); // null parameter means timeout else request[ i ][ 0 ]( null ); // null parameter means timeout } } } function topoCall( id, altitudes ) { var request = topoRequests[ id ]; topoRequests[ id ] = null; if ( request ) { var topoScript = document.getElementById('topoScript' + id); var topHead = document.getElementsByTagName('head').item(0); topHead.removeChild(topoScript); if ( request.length == altitudes.length ) for ( var i = 0; i < request.length; i++ ) if ( request[ i ] ) { if ( request[ i ][ 1 ] ) request[ i ][ 0 ]( altitudes[ i ], request[ i ][ 1 ] ); else request[ i ][ 0 ]( altitudes[ i ] ); } } } function topoGetAltitudes( quadruples, timeout ) { var topHead = document.getElementsByTagName('head').item(0); var maxURLlength = 2048 - 50; // 50 is for safety var url = 'https://topocoding.com/api/altitude_v1.php?key=' + topoKey + '&id='; var args = ''; var requests = new Array(); for ( var i = 0; i < quadruples.length; i++ ) { args = args + topoEncodeCoordinates( new Array( [ quadruples[ i ][ 0 ], quadruples[ i ][ 1 ] ] ) ); requests[ requests.length ] = [ quadruples[ i ][ 2 ], quadruples[ i ][ 3 ] ]; if ( ( ( url.length + args.length ) > maxURLlength ) || ( ( i + 1 ) == quadruples.length ) ) { var scriptNr = 0; do { scriptNr = topoLastIndex + 1; topoLastIndex++; } while ( scriptNr != topoLastIndex ); // thread-safe topoRequests[ scriptNr ] = requests; var topoScript = document.createElement('script'); topoScript.src = url + scriptNr + '&l=' + args; topoScript.id = 'topoScript' + scriptNr; if ( timeout ) setTimeout( function(){ topoTimeout(scriptNr); }, timeout ); else setTimeout( function(){ topoQuietTimeout(scriptNr); }, 10000 ); topHead.appendChild(topoScript); args = ''; requests = new Array(); } } } function topoGetAltitude( lat, lon, action, context, timeout ) { topoGetAltitudes( new Array( [ lat, lon, action, context ] ), timeout ); } function topoComputeDistance( lat1, lon1, lat2, lon2 ) { lat1 = lat1 / 180 * Math.PI; lon1 = lon1 / 180 * Math.PI; lat2 = lat2 / 180 * Math.PI; lon2 = lon2 / 180 * Math.PI; var dlon = lon2 - lon1; var dlat = lat2 - lat1; var a = Math.sin(dlat/2) * Math.sin(dlat/2) + Math.cos(lat1) * Math.cos(lat2) * Math.sin(dlon/2) * Math.sin(dlon/2); var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); var R = 6378.137; return R * c; } function topoComputeIntermediate(lat1,lon1,lat2,lon2,fraction,dist) { if ( !dist ) dist = topoComputeDistance( lat1,lon1,lat2,lon2 ); var R = 6378.137; lat1 = lat1 / 180 * Math.PI; lon1 = lon1 / 180 * Math.PI; lat2 = lat2 / 180 * Math.PI; lon2 = lon2 / 180 * Math.PI; dist = dist / R; var A = Math.sin((1-fraction)*dist)/Math.sin(dist); var B = Math.sin(fraction*dist)/Math.sin(dist); var x = A*Math.cos(lat1)*Math.cos(lon1) + B*Math.cos(lat2)*Math.cos(lon2); var y = A*Math.cos(lat1)*Math.sin(lon1) + B*Math.cos(lat2)*Math.sin(lon2); var z = A*Math.sin(lat1) + B*Math.sin(lat2); var lat = Math.atan2(z,Math.sqrt(x*x+y*y)); var lon = Math.atan2(y,x); lat = lat * 180 / Math.PI; lon = lon * 180 / Math.PI; return new Array( lat, lon ); } function topoFindIndexOfNearestSmaller( arr, value ) { var left = 0; var right = arr.length - 1; while ( left < right ) { var mid = Math.floor( ( left + right ) / 2.0 ); if ( arr[ mid ] > value ) right = mid; else left = mid + 1; } return left - 1; } function topoResampleCoordinates( coordinates ) { if ( coordinates.length <= topoUrlMaxCoordinates ) return coordinates; var uniqueCoordinates = new Array(); uniqueCoordinates[ 0 ] = coordinates[ 0 ]; for ( var i = 1; i < coordinates.length; i++ ) if ( ( coordinates[ i ][ 0 ] != coordinates[ i - 1 ][ 0 ] ) || ( coordinates[ i ][ 1 ] != coordinates[ i - 1 ][ 1 ] ) ) { uniqueCoordinates[ uniqueCoordinates.length ] = coordinates[ i ]; } var distances = new Array(); distances[ 0 ] = 0.0; var cummulativeDistance = 0.0; for ( var i = 1; i < uniqueCoordinates.length; i++ ) { cummulativeDistance = cummulativeDistance + topoComputeDistance( uniqueCoordinates[ i - 1 ][ 0 ], uniqueCoordinates[ i - 1 ][ 1 ], uniqueCoordinates[ i ][ 0 ], uniqueCoordinates[ i ][ 1 ] ); distances[ i ] = cummulativeDistance; } var points = new Array(); for ( var i = 0; i < topoUrlMaxCoordinates; i++ ) { var lookDistance = i / ( topoUrlMaxCoordinates - 1 ) * cummulativeDistance; var smallerIndex = topoFindIndexOfNearestSmaller( distances, lookDistance ); if ( smallerIndex == ( uniqueCoordinates.length - 1 ) ) { points[ i ] = uniqueCoordinates[ smallerIndex ]; } else { var distLR = distances[ smallerIndex + 1 ] - distances[ smallerIndex ]; var fraction = ( lookDistance - distances[ smallerIndex ] ) / distLR; points[ i ] = topoComputeIntermediate( uniqueCoordinates[ smallerIndex ][ 0 ], uniqueCoordinates[ smallerIndex ][ 1 ], uniqueCoordinates[ smallerIndex + 1 ][ 0 ], uniqueCoordinates[ smallerIndex + 1 ][ 1 ], fraction, distLR ); } } return points; } function topoDrawGraph( divElement, coordinates, width, height, english ) { while (divElement.firstChild) divElement.removeChild(divElement.firstChild); if ( coordinates.length < 2 ) return; var resampledCoordinates = topoResampleCoordinates( coordinates ); var code = topoEncodeCoordinates( resampledCoordinates ); var whu = ''; if ( width ) whu = whu + '&w=' + width; if ( height ) whu = whu + '&h=' + height; if ( english ) whu = whu + '&u=e'; divElement.innerHTML = ''; }