/* (C) 2009 Ivan Boldyrev * * Fgh is a fast GeoHash implementation in JavaScript. * * Fgh is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * Fgh is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this software; if not, see . */ (function(){var d="0123456789bcdefghjkmnpqrstuvwxyz";var e=[0,1,0,1,2,3,2,3,0,1,0,1,2,3,2,3,4,5,4,5,6,7,6,7,4,5,4,5,6,7,6,7];var f=[0,1,4,5,16,17,20,21,64,65,68,69,80,81,84,85,256,257,260,261,272,273,276,277,320,321,324,325,336,337,340,341];function _cmb(a,b){return(d.indexOf(a.charAt(b))<<5)|(d.indexOf(a.charAt(b+1)))};function _unp(v){return e[v&0x1F]|(e[(v>>6)&0xF]<<3)}function _sparse(a){var b=0,off=0;while(a>0){low=a&0xFF;b|=f[low]<>=8;off+=16}return b}window['Fgh']={decode:function(a){var L=a.length,i,w,ln=0.0,lt=0.0;if(L&1){w=(d.indexOf(a.charAt(L-1))<<5)}else{w=_cmb(a,L-2)}lt=(_unp(w))/32.0;ln=(_unp(w>>1))/32.0;for(i=(L-2)&~0x1;i>=0;i-=2){w=_cmb(a,i);lt=(_unp(w)+lt)/32.0;ln=(_unp(w>>1)+ln)/32.0}return{lat:180.0*(lt-0.5),lon:360.0*(ln-0.5)}},encode:function(a,b,c){a=a/180.0+0.5;b=b/360.0+0.5;var r='',l=Math.ceil(c/10),hlt,hln,b2,hi,lo,i;for(i=0;i>5;lo=b2&0x1F;r+=d.charAt(hi)+d.charAt(lo)}r=r.substr(0,Math.ceil(c/5));return r},checkValid:function(a){return!!a.match(/^[0-9b-hjkmnp-z]+$/)}}})(); // From http://www.quirksmode.org/js/detect.html var detect = navigator.userAgent.toLowerCase(); var OS,browser,version,total,thestring; if (checkIt('konqueror')) { browser = "Konqueror"; OS = "Linux"; } else if (checkIt('safari')) browser = "Safari" else if (checkIt('omniweb')) browser = "OmniWeb" else if (checkIt('opera')) browser = "Opera" else if (checkIt('webtv')) browser = "WebTV"; else if (checkIt('icab')) browser = "iCab" else if (checkIt('msie')) browser = "IE" else if (!checkIt('compatible')) { browser = "Netscape" version = detect.charAt(8); } else browser = "An unknown browser"; if (!version) version = detect.charAt(place + thestring.length); if (!OS) { if (checkIt('linux')) OS = "Linux"; else if (checkIt('x11')) OS = "Unix"; else if (checkIt('mac')) OS = "Mac" else if (checkIt('win')) OS = "Windows" else OS = "an unknown operating system"; } function checkIt(string) { place = detect.indexOf(string) + 1; thestring = string; return place; } function tgbase32todec(_base32) { var alphabets = "abcdefghijklmnopqrstuvwxyz234567"; _base32 = _base32.toLowerCase(); var i; var result = 0; var digit = 0; for (i = _base32.length-1; i>=0; i--) { var val = alphabets.indexOf(_base32.charAt(i)); if (val == -1) { return -1; } result += Math.pow(32,digit)*val; digit++; } return result; } function tgdectobase32(_dec) { var alphabets = "abcdefghijklmnopqrstuvwxyz234567"; if (_dec <= 0 ) { return ""; } var result = ""; while ( _dec > 0 ) { var modresult = _dec % 32; result = alphabets.charAt(modresult) + result; _dec = parseInt(_dec / 32); } return result.toUpperCase(); } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* Latitude/longitude spherical geodesy formulae & scripts (c) Chris Veness 2002-2010 */ /* - www.movable-type.co.uk/scripts/latlong.html */ /* */ /* Sample usage: */ /* var p1 = new LatLon(51.5136, -0.0983); */ /* var p2 = new LatLon(51.4778, -0.0015); */ /* var dist = p1.distanceTo(p2); // in km */ /* var brng = p1.bearingTo(p2); // in degrees clockwise from north */ /* ... etc */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* Note that minimal error checking is performed in this example code! */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /** * Creates a point on the earth's surface at the supplied latitude / longitude * * @constructor * @param {Number} lat: latitude in numeric degrees * @param {Number} lon: longitude in numeric degrees * @param {Number} [rad=6371]: radius of earth if different value is required from standard 6,371km */ function LatLon(lat, lon, rad) { if (typeof rad == 'undefined') rad = 6371; // earth's mean radius in km this._lat = lat; this._lon = lon; this._radius = rad; } /** * Returns the distance from this point to the supplied point, in km * (using Haversine formula) * * from: Haversine formula - R. W. Sinnott, "Virtues of the Haversine", * Sky and Telescope, vol 68, no 2, 1984 * * @param {LatLon} point: Latitude/longitude of destination point * @param {Number} [precision=4]: no of significant digits to use for returned value * @returns {Number} Distance in km between this point and destination point */ LatLon.prototype.distanceTo = function(point, precision) { // default 4 sig figs reflects typical 0.3% accuracy of spherical model if (typeof precision == 'undefined') precision = 4; var R = this._radius; var lat1 = this._lat.toRad(), lon1 = this._lon.toRad(); var lat2 = point._lat.toRad(), lon2 = point._lon.toRad(); var dLat = lat2 - lat1; var dLon = lon2 - lon1; 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 d = R * c; return d.toPrecisionFixed(precision); } /** * Returns the (initial) bearing from this point to the supplied point, in degrees * see http://williams.best.vwh.net/avform.htm#Crs * * @param {LatLon} point: Latitude/longitude of destination point * @returns {Number} Initial bearing in degrees from North */ LatLon.prototype.bearingTo = function(point) { var lat1 = this._lat.toRad(), lat2 = point._lat.toRad(); var dLon = (point._lon-this._lon).toRad(); var y = Math.sin(dLon) * Math.cos(lat2); var x = Math.cos(lat1)*Math.sin(lat2) - Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon); var brng = Math.atan2(y, x); return (brng.toDeg()+360) % 360; } /** * Returns final bearing arriving at supplied destination point from this point; the final bearing * will differ from the initial bearing by varying degrees according to distance and latitude * * @param {LatLon} point: Latitude/longitude of destination point * @returns {Number} Final bearing in degrees from North */ LatLon.prototype.finalBearingTo = function(point) { // get initial bearing from supplied point back to this point... var lat1 = point._lat.toRad(), lat2 = this._lat.toRad(); var dLon = (this._lon-point._lon).toRad(); var y = Math.sin(dLon) * Math.cos(lat2); var x = Math.cos(lat1)*Math.sin(lat2) - Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon); var brng = Math.atan2(y, x); // ... & reverse it by adding 180 return (brng.toDeg()+180) % 360; } /** * Returns the midpoint between this point and the supplied point. * see http://mathforum.org/library/drmath/view/51822.html for derivation * * @param {LatLon} point: Latitude/longitude of destination point * @returns {LatLon} Midpoint between this point and the supplied point */ LatLon.prototype.midpointTo = function(point) { lat1 = this._lat.toRad(), lon1 = this._lon.toRad(); lat2 = point._lat.toRad(); var dLon = (point._lon-this._lon).toRad(); var Bx = Math.cos(lat2) * Math.cos(dLon); var By = Math.cos(lat2) * Math.sin(dLon); lat3 = Math.atan2(Math.sin(lat1)+Math.sin(lat2), Math.sqrt( (Math.cos(lat1)+Bx)*(Math.cos(lat1)+Bx) + By*By) ); lon3 = lon1 + Math.atan2(By, Math.cos(lat1) + Bx); return new LatLon(lat3.toDeg(), lon3.toDeg()); } /** * Returns the destination point from this point having travelled the given distance (in km) on the * given initial bearing (bearing may vary before destination is reached) * * see http://williams.best.vwh.net/avform.htm#LL * * @param {Number} brng: Initial bearing in degrees * @param {Number} dist: Distance in km * @returns {LatLon} Destination point */ LatLon.prototype.destinationPoint = function(brng, dist) { dist = dist/this._radius; // convert dist to angular distance in radians brng = brng.toRad(); // var lat1 = this._lat.toRad(), lon1 = this._lon.toRad(); var lat2 = Math.asin( Math.sin(lat1)*Math.cos(dist) + Math.cos(lat1)*Math.sin(dist)*Math.cos(brng) ); var lon2 = lon1 + Math.atan2(Math.sin(brng)*Math.sin(dist)*Math.cos(lat1), Math.cos(dist)-Math.sin(lat1)*Math.sin(lat2)); lon2 = (lon2+3*Math.PI)%(2*Math.PI) - Math.PI; // normalise to -180...+180 if (isNaN(lat2) || isNaN(lon2)) return null; return new LatLon(lat2.toDeg(), lon2.toDeg()); } /** * Returns the point of intersection of two paths defined by point and bearing * * see http://williams.best.vwh.net/avform.htm#Intersection * * @param {LatLon} p1: First point * @param {Number} brng1: Initial bearing from first point * @param {LatLon} p2: Second point * @param {Number} brng2: Initial bearing from second point * @returns {LatLon} Destination point (null if no unique intersection defined) */ LatLon.intersection = function(p1, brng1, p2, brng2) { lat1 = p1._lat.toRad(), lon1 = p1._lon.toRad(); lat2 = p2._lat.toRad(), lon2 = p2._lon.toRad(); brng13 = brng1.toRad(), brng23 = brng2.toRad(); dLat = lat2-lat1, dLon = lon2-lon1; dist12 = 2*Math.asin( Math.sqrt( Math.sin(dLat/2)*Math.sin(dLat/2) + Math.cos(lat1)*Math.cos(lat2)*Math.sin(dLon/2)*Math.sin(dLon/2) ) ); if (dist12 == 0) return null; // initial/final bearings between points brngA = Math.acos( ( Math.sin(lat2) - Math.sin(lat1)*Math.cos(dist12) ) / ( Math.sin(dist12)*Math.cos(lat1) ) ); if (isNaN(brngA)) brngA = 0; // protect against rounding brngB = Math.acos( ( Math.sin(lat1) - Math.sin(lat2)*Math.cos(dist12) ) / ( Math.sin(dist12)*Math.cos(lat2) ) ); if (Math.sin(lon2-lon1) > 0) { brng12 = brngA; brng21 = 2*Math.PI - brngB; } else { brng12 = 2*Math.PI - brngA; brng21 = brngB; } alpha1 = (brng13 - brng12 + Math.PI) % (2*Math.PI) - Math.PI; // angle 2-1-3 alpha2 = (brng21 - brng23 + Math.PI) % (2*Math.PI) - Math.PI; // angle 1-2-3 if (Math.sin(alpha1)==0 && Math.sin(alpha2)==0) return null; // infinite intersections if (Math.sin(alpha1)*Math.sin(alpha2) < 0) return null; // ambiguous intersection //alpha1 = Math.abs(alpha1); //alpha2 = Math.abs(alpha2); // ... Ed Williams takes abs of alpha1/alpha2, but seems to break calculation? alpha3 = Math.acos( -Math.cos(alpha1)*Math.cos(alpha2) + Math.sin(alpha1)*Math.sin(alpha2)*Math.cos(dist12) ); dist13 = Math.atan2( Math.sin(dist12)*Math.sin(alpha1)*Math.sin(alpha2), Math.cos(alpha2)+Math.cos(alpha1)*Math.cos(alpha3) ) lat3 = Math.asin( Math.sin(lat1)*Math.cos(dist13) + Math.cos(lat1)*Math.sin(dist13)*Math.cos(brng13) ); dLon13 = Math.atan2( Math.sin(brng13)*Math.sin(dist13)*Math.cos(lat1), Math.cos(dist13)-Math.sin(lat1)*Math.sin(lat3) ); lon3 = lon1+dLon13; lon3 = (lon3+Math.PI) % (2*Math.PI) - Math.PI; // normalise to -180..180 return new LatLon(lat3.toDeg(), lon3.toDeg()); } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /** * Returns the distance from this point to the supplied point, in km, travelling along a rhumb line * * see http://williams.best.vwh.net/avform.htm#Rhumb * * @param {LatLon} point: Latitude/longitude of destination point * @returns {Number} Distance in km between this point and destination point */ LatLon.prototype.rhumbDistanceTo = function(point) { var R = this._radius; var lat1 = this._lat.toRad(), lat2 = point._lat.toRad(); var dLat = (point._lat-this._lat).toRad(); var dLon = Math.abs(point._lon-this._lon).toRad(); var dPhi = Math.log(Math.tan(lat2/2+Math.PI/4)/Math.tan(lat1/2+Math.PI/4)); var q = (!isNaN(dLat/dPhi)) ? dLat/dPhi : Math.cos(lat1); // E-W line gives dPhi=0 // if dLon over 180 take shorter rhumb across 180 meridian: if (dLon > Math.PI) dLon = 2*Math.PI - dLon; var dist = Math.sqrt(dLat*dLat + q*q*dLon*dLon) * R; return dist.toPrecisionFixed(4); // 4 sig figs reflects typical 0.3% accuracy of spherical model } /** * Returns the bearing from this point to the supplied point along a rhumb line, in degrees * * @param {LatLon} point: Latitude/longitude of destination point * @returns {Number} Bearing in degrees from North */ LatLon.prototype.rhumbBearingTo = function(point) { var lat1 = this._lat.toRad(), lat2 = point._lat.toRad(); var dLon = (point._lon-this._lon).toRad(); var dPhi = Math.log(Math.tan(lat2/2+Math.PI/4)/Math.tan(lat1/2+Math.PI/4)); if (Math.abs(dLon) > Math.PI) dLon = dLon>0 ? -(2*Math.PI-dLon) : (2*Math.PI+dLon); var brng = Math.atan2(dLon, dPhi); return (brng.toDeg()+360) % 360; } /** * Returns the destination point from this point having travelled the given distance (in km) on the * given bearing along a rhumb line * * @param {Number} brng: Bearing in degrees from North * @param {Number} dist: Distance in km * @returns {LatLon} Destination point */ LatLon.prototype.rhumbDestinationPoint = function(brng, dist) { var R = this._radius; var d = parseFloat(dist)/R; // d = angular distance covered on earth's surface var lat1 = this._lat.toRad(), lon1 = this._lon.toRad(); brng = brng.toRad(); var lat2 = lat1 + d*Math.cos(brng); var dLat = lat2-lat1; var dPhi = Math.log(Math.tan(lat2/2+Math.PI/4)/Math.tan(lat1/2+Math.PI/4)); var q = (!isNaN(dLat/dPhi)) ? dLat/dPhi : Math.cos(lat1); // E-W line gives dPhi=0 var dLon = d*Math.sin(brng)/q; // check for some daft bugger going past the pole if (Math.abs(lat2) > Math.PI/2) lat2 = lat2>0 ? Math.PI-lat2 : -(Math.PI-lat2); lon2 = (lon1+dLon+3*Math.PI)%(2*Math.PI) - Math.PI; return new LatLon(lat2.toDeg(), lon2.toDeg()); } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /** * Returns the latitude of this point; signed numeric degrees if no format, otherwise format & dp * as per Geo.toLat() * * @param {String} [format]: Return value as 'd', 'dm', 'dms' * @param {Number} [dp=0|2|4]: No of decimal places to display * @returns {Number|String} Numeric degrees if no format specified, otherwise deg/min/sec * * @requires Geo */ LatLon.prototype.lat = function(format, dp) { if (typeof format == 'undefined') return this._lat; return Geo.toLat(this._lat, format, dp); } /** * Returns the longitude of this point; signed numeric degrees if no format, otherwise format & dp * as per Geo.toLon() * * @param {String} [format]: Return value as 'd', 'dm', 'dms' * @param {Number} [dp=0|2|4]: No of decimal places to display * @returns {Number|String} Numeric degrees if no format specified, otherwise deg/min/sec * * @requires Geo */ LatLon.prototype.lon = function(format, dp) { if (typeof format == 'undefined') return this._lon; return Geo.toLon(this._lon, format, dp); } /** * Returns a string representation of this point; format and dp as per lat()/lon() * * @param {String} [format]: Return value as 'd', 'dm', 'dms' * @param {Number} [dp=0|2|4]: No of decimal places to display * @returns {String} Comma-separated latitude/longitude * * @requires Geo */ LatLon.prototype.toString = function(format, dp) { if (typeof format == 'undefined') format = 'dms'; return Geo.toLat(this._lat, format, dp) + ', ' + Geo.toLon(this._lon, format, dp); } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ // extend Number object with methods for converting degrees/radians /** Convert numeric degrees to radians */ if (typeof(String.prototype.toRad) === "undefined") { Number.prototype.toRad = function() { return this * Math.PI / 180; } } /** Convert radians to numeric (signed) degrees */ if (typeof(String.prototype.toDeg) === "undefined") { Number.prototype.toDeg = function() { return this * 180 / Math.PI; } } /** * Format the significant digits of a number, using only fixed-point notation (no exponential) * * @param {Number} precision: Number of significant digits to appear in the returned string * @returns {String} A string representation of number which contains precision significant digits */ if (typeof(Number.prototype.toPrecisionFixed) === "undefined") { Number.prototype.toPrecisionFixed = function(precision) { var numb = this < 0 ? -this : this; // can't take log of -ve number... var sign = this < 0 ? '-' : ''; if (numb == 0) { n = '0.'; while (precision--) n += '0'; return n }; // can't take log of zero var scale = Math.ceil(Math.log(numb)*Math.LOG10E); // no of digits before decimal var n = String(Math.round(numb * Math.pow(10, precision-scale))); if (scale > 0) { // add trailing zeros & insert decimal as required l = scale - n.length; while (l-- > 0) n = n + '0'; if (scale < n.length) n = n.slice(0,scale) + '.' + n.slice(scale); } else { // prefix decimal and leading zeros if required while (scale++ < 0) n = '0' + n; n = '0.' + n; } return sign + n; } } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* Geodesy representation conversion functions (c) Chris Veness 2002-2010 */ /* - www.movable-type.co.uk/scripts/latlong.html */ /* */ /* Sample usage: */ /* var lat = Geo.parseDMS(' 51 28 40.12 N'); */ /* var lon = Geo.parseDMS('000 00 05.31 W'); */ /* var p1 = new LatLon(lat, lon); */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ var Geo = {}; // Geo namespace, representing static class /** * Parses string representing degrees/minutes/seconds into numeric degrees * * This is very flexible on formats, allowing signed decimal degrees, or deg-min-sec optionally * suffixed by compass direction (NSEW). A variety of separators are accepted (eg 3 37' 09"W) * or fixed-width format without separators (eg 0033709W). Seconds and minutes may be omitted. * (Note minimal validation is done). * * @param {String|Number} dmsStr: Degrees or deg/min/sec in variety of formats * @returns {Number} Degrees as decimal number * @throws {TypeError} dmsStr is an object, perhaps DOM object without .value? */ Geo.parseDMS = function(dmsStr) { if (typeof deg == 'object') throw new TypeError('Geo.parseDMS - dmsStr is [DOM?] object'); if (!isNaN(dmsStr)) return Number(dmsStr); // ... signed decimal degrees without NSEW // strip off any sign or compass dir'n & split out separate d/m/s var dms = String(dmsStr).trim().replace(/^-/,'').replace(/[NSEW]$/i,'').split(/[^0-9.,]+/); if (dms[dms.length-1]=='') dms.splice(dms.length-1); // from trailing symbol if (dms == '') return NaN; // and convert to decimal degrees... switch (dms.length) { case 3: // interpret 3-part result as d/m/s var deg = dms[0]/1 + dms[1]/60 + dms[2]/3600; break; case 2: // interpret 2-part result as d/m var deg = dms[0]/1 + dms[1]/60; break; case 1: // just d (possibly decimal) or non-separated dddmmss var deg = dms[0]; // check for fixed-width unseparated format eg 0033709W if (/[NS]/i.test(dmsStr)) deg = '0' + deg; // - normalise N/S to 3-digit degrees if (/[0-9]{7}/.test(deg)) deg = deg.slice(0,3)/1 + deg.slice(3,5)/60 + deg.slice(5)/3600; break; default: return NaN; } if (/^-|[WS]$/i.test(dmsStr.trim())) deg = -deg; // take '-', west and south as -ve return Number(deg); } /** * Convert decimal degrees to deg/min/sec format * - degree, prime, double-prime symbols are added, but sign is discarded, though no compass * direction is added * * @private * @param {Number} deg: Degrees * @param {String} [format=dms]: Return value as 'd', 'dm', 'dms' * @param {Number} [dp=0|2|4]: No of decimal places to use - default 0 for dms, 2 for dm, 4 for d * @returns {String} deg formatted as deg/min/secs according to specified format * @throws {TypeError} deg is an object, perhaps DOM object without .value? */ Geo.toDMS = function(deg, format, dp) { if (typeof deg == 'object') throw new TypeError('Geo.toDMS - deg is [DOM?] object'); if (isNaN(deg)) return ''; // give up here if we can't make a number from deg // default values if (typeof format == 'undefined') format = 'dms'; if (typeof dp == 'undefined') { switch (format) { case 'd': dp = 4; break; case 'dm': dp = 2; break; case 'dms': dp = 0; break; default: format = 'dms'; dp = 0; // be forgiving on invalid format } } deg = Math.abs(deg); // (unsigned result ready for appending compass dir'n) switch (format) { case 'd': d = deg.toFixed(dp); // round degrees if (d<100) d = '0' + d; // pad with leading zeros if (d<10) d = '0' + d; dms = d + '\u00B0'; // add symbol break; case 'dm': var min = (deg*60).toFixed(dp); // convert degrees to minutes & round var d = Math.floor(min / 60); // get component deg/min var m = (min % 60).toFixed(dp); // pad with trailing zeros if (d<100) d = '0' + d; // pad with leading zeros if (d<10) d = '0' + d; if (m<10) m = '0' + m; dms = d + '\u00B0' + m + '\u2032'; // add , ' symbols break; case 'dms': var sec = (deg*3600).toFixed(dp); // convert degrees to seconds & round var d = Math.floor(sec / 3600); // get component deg/min/sec var m = Math.floor(sec/60) % 60; var s = (sec % 60).toFixed(dp); // pad with trailing zeros if (d<100) d = '0' + d; // pad with leading zeros if (d<10) d = '0' + d; if (m<10) m = '0' + m; if (s<10) s = '0' + s; dms = d + '\u00B0' + m + '\u2032' + s + '\u2033'; // add , ', " symbols break; } return dms; } /** * Convert numeric degrees to deg/min/sec latitude (suffixed with N/S) * * @param {Number} deg: Degrees * @param {String} [format=dms]: Return value as 'd', 'dm', 'dms' * @param {Number} [dp=0|2|4]: No of decimal places to use - default 0 for dms, 2 for dm, 4 for d * @returns {String} Deg/min/seconds */ Geo.toLat = function(deg, format, dp) { var lat = Geo.toDMS(deg, format, dp); return lat=='' ? '' : lat.slice(1) + (deg<0 ? 'S' : 'N'); // knock off initial '0' for lat! } /** * Convert numeric degrees to deg/min/sec longitude (suffixed with E/W) * * @param {Number} deg: Degrees * @param {String} [format=dms]: Return value as 'd', 'dm', 'dms' * @param {Number} [dp=0|2|4]: No of decimal places to use - default 0 for dms, 2 for dm, 4 for d * @returns {String} Deg/min/seconds */ Geo.toLon = function(deg, format, dp) { var lon = Geo.toDMS(deg, format, dp); return lon=='' ? '' : lon + (deg<0 ? 'W' : 'E'); } /** * Convert numeric degrees to deg/min/sec as a bearing (0..360) * * @param {Number} deg: Degrees * @param {String} [format=dms]: Return value as 'd', 'dm', 'dms' * @param {Number} [dp=0|2|4]: No of decimal places to use - default 0 for dms, 2 for dm, 4 for d * @returns {String} Deg/min/seconds */ Geo.toBrng = function(deg, format, dp) { deg = (Number(deg)+360) % 360; // normalise -ve values to 180..360 var brng = Geo.toDMS(deg, format, dp); return brng.replace('360', '0'); // just in case rounding took us up to 360! } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /// /// A UTM -> Lat/Long (or vice versa) converter adapted from the script used at /// http://www.uwgb.edu/dutchs/UsefulData/ConvertUTMNoOZ.HTM /// I've taken the calculations portion of his script and turned it into a singleton /// javascript object so that it is no longer dependent on the controls used on the page /// to use this script, call setDatum with the index of the datum to be used and then /// call the various conversion functions latLngToUtm(), utmToLatLng() or natoToLatLng(), utmToNato(), natoToUtm() /// to convert between the various coordinate systems, hopefully accurately! /// /// NOTE: no attempt is made to compensate for the irregular grid in the area around the southern coast of Norway and /// Svalbard (zones 32V and 31X, 33X, 35X and 37X) because of this results returned for NATO coordinates for lat/lng or /// UTM values located in these regions will not be correct. /// var utmconv = { // // constants taken from or calculated from the datum // a: 0, // equatorial radius in meters f: 0, // polar flattening b: 0, // polar radius in meters e: 0, // eccentricity e0: 0, // e' // // constants used in calculations // k: 1, k0: 0.9996, drad: Math.PI / 180, digraphLettersE: "ABCDEFGHJKLMNPQRSTUVWXYZ", digraphLettersN: "ABCDEFGHJKLMNPQRSTUV", digraphLettersAll: "ABCDEFGHJKLMNPQRSTUVABCDEFGHJKLMNPQRSTUVABCDEFGHJKLMNPQRSTUVABCDEFGHJKLMNPQRSTUVABCDEFGHJKLMNPQRSTUVABCDEFGHJKLMNPQRSTUVABCDEFGHJKLMNPQRSTUVABCDEFGHJKLMNPQRSTUVABCDEFGHJKLMNPQRSTUVABCDEFGHJKLMNPQRSTUV", datumTable: [ { eqRad: 6378137.0, flat: 298.2572236 }, // WGS 84 { eqRad: 6378137.0, flat: 298.2572236 }, // NAD 83 { eqRad: 6378137.0, flat: 298.2572215 }, // GRS 80 { eqRad: 6378135.0, flat: 298.2597208 }, // WGS 72 { eqRad: 6378160.0, flat: 298.2497323 }, // Austrailian 1965 { eqRad: 6378245.0, flat: 298.2997381 }, // Krasovsky 1940 { eqRad: 6378206.4, flat: 294.9786982 }, // North American 1927 { eqRad: 6378388.0, flat: 296.9993621 }, // International 1924 { eqRad: 6378388.0, flat: 296.9993621 }, // Hayford 1909 { eqRad: 6378249.1, flat: 293.4660167 }, // Clarke 1880 { eqRad: 6378206.4, flat: 294.9786982 }, // Clarke 1866 { eqRad: 6377563.4, flat: 299.3247788 }, // Airy 1830 { eqRad: 6377397.2, flat: 299.1527052 }, // Bessel 1841 { eqRad: 6377276.3, flat: 300.8021499 } // Everest 1830 ], /// /// calculate constants used for doing conversions using a given map datum /// setDatum: function (index) { var datum = this.datumTable[index]; this.a = datum.eqRad; this.f = 1 / datum.flat; this.b = this.a * (1 - this.f); // polar radius this.e = Math.sqrt(1 - Math.pow(this.b, 2) / Math.pow(this.a, 2)); this.e0 = this.e / Math.sqrt(1 - Math.pow(this.e, 1)); }, /// /// given a lat/lng pair, returns both global UTM and NATO UTM in the following form: /// utm: /// { /// global: { northing: n, easting: e, zone: z, southern: x }, /// nato: { northing: n, easting: e, latzone: z0, lngzone: z1, digraph: xx } /// } /// /// this function assumes that all data validation has been performed prior to calling /// it. /// latLngToUtm: function (lat, lngd) { var phi = lat * this.drad; // convert latitude to radians var lng = lngd * this.drad; // convert longitude to radians var utmz = 1 + Math.floor((lngd + 180) / 6); // longitude to utm zone var zcm = 3 + 6 * (utmz - 1) - 180; // central meridian of a zone var latz = 0; // this gives us zone A-B for below 80S var esq = (1 - (this.b / this.a) * (this.b / this.a)); var e0sq = this.e * this.e / (1 - Math.pow(this.e, 2)); var M = 0; // convert latitude to latitude zone for nato if (lat > -80 && lat < 72) { latz = Math.floor((lat + 80) / 8) + 2; // zones C-W in this range } if (lat > 72 && lat < 84) { latz = 21; // zone X } else if (lat > 84) { latz = 23; // zone Y-Z } var N = this.a / Math.sqrt(1 - Math.pow(this.e * Math.sin(phi), 2)); var T = Math.pow(Math.tan(phi), 2); var C = e0sq * Math.pow(Math.cos(phi), 2); var A = (lngd - zcm) * this.drad * Math.cos(phi); // calculate M (USGS style) M = phi * (1 - esq * (1 / 4 + esq * (3 / 64 + 5 * esq / 256))); M = M - Math.sin(2 * phi) * (esq * (3 / 8 + esq * (3 / 32 + 45 * esq / 1024))); M = M + Math.sin(4 * phi) * (esq * esq * (15 / 256 + esq * 45 / 1024)); M = M - Math.sin(6 * phi) * (esq * esq * esq * (35 / 3072)); M = M * this.a; //Arc length along standard meridian M0 = 0; // if another point of origin is used than the equator // now we are ready to calculate the UTM values... // first the easting var x = this.k0 * N * A * (1 + A * A * ((1 - T + C) / 6 + A * A * (5 - 18 * T + T * T + 72 * C - 58 * e0sq) / 120)); //Easting relative to CM x = x + 500000; // standard easting // now the northing y = this.k0 * (M - M0 + N * Math.tan(phi) * (A * A * (1 / 2 + A * A * ((5 - T + 9 * C + 4 * C * C) / 24 + A * A * (61 - 58 * T + T * T + 600 * C - 330 * e0sq) / 720)))); // first from the equator yg = y + 10000000; //yg = y global, from S. Pole if (y < 0) { y = 10000000 + y; // add in false northing if south of the equator } var digraph = this.makeDigraph(x, y, utmz); var rv = { global: { easting: Math.round(10*(x))/10, northing: Math.round(10*y)/10, zone: utmz, southern: phi < 0 }, nato: { easting: Math.round(10*(x-100000*Math.floor(x/100000)))/10, northing: Math.round(10*(y-100000*Math.floor(y/100000)))/10, latZone: this.digraphLettersN[latz], lngZone: utmz, digraph: digraph } } return rv; }, /// /// convert a set of global UTM coordinates to lat/lng returned as follows /// /// { lat: y, lng: x } /// /// inputs: /// x: easting /// y: northing /// utmz: utm zone /// southern: bool indicating coords are in southern hemisphere /// utmToLatLng: function(x, y, utmz, southern) { var esq = (1 - (this.b / this.a) * (this.b / this.a)); var e0sq = this.e * this.e / (1 - Math.pow(this.e, 2)); var zcm = 3 + 6 * (utmz - 1) - 180; // Central meridian of zone var e1 = (1 - Math.sqrt(1 - Math.pow(this.e, 2))) / (1 + Math.sqrt(1 - Math.pow(this.e, 2))); var M0 = 0; var M = 0; if (!southern) M = M0 + y / this.k0; // Arc length along standard meridian. else M = M0 + (y - 10000000) / this.k; var mu = M / (this.a * (1 - esq * (1 / 4 + esq * (3 / 64 + 5 * esq / 256)))); var phi1 = mu + e1 * (3 / 2 - 27 * e1 * e1 / 32) * Math.sin(2 * mu) + e1 * e1 * (21 / 16 - 55 * e1 * e1 / 32) * Math.sin(4 * mu); //Footprint Latitude phi1 = phi1 + e1 * e1 * e1 * (Math.sin(6 * mu) * 151 / 96 + e1 * Math.sin(8 * mu) * 1097 / 512); var C1 = e0sq * Math.pow(Math.cos(phi1), 2); var T1 = Math.pow(Math.tan(phi1), 2); var N1 = this.a / Math.sqrt(1 - Math.pow(this.e * Math.sin(phi1), 2)); var R1 = N1 * (1 - Math.pow(this.e, 2)) / (1 - Math.pow(this.e * Math.sin(phi1), 2)); var D = (x - 500000) / (N1 * this.k0); var phi = (D * D) * (1 / 2 - D * D * (5 + 3 * T1 + 10 * C1 - 4 * C1 * C1 - 9 * e0sq) / 24); phi = phi + Math.pow(D, 6) * (61 + 90 * T1 + 298 * C1 + 45 * T1 * T1 - 252 * e0sq - 3 * C1 * C1) / 720; phi = phi1 - (N1 * Math.tan(phi1) / R1) * phi; var lat = Math.floor(1000000 * phi / this.drad) / 1000000; var lng = D * (1 + D * D * ((-1 - 2 * T1 - C1) / 6 + D * D * (5 - 2 * C1 + 28 * T1 - 3 * C1 * C1 + 8 * e0sq + 24 * T1 * T1) / 120)) / Math.cos(phi1); lng = lngd = zcm + lng / this.drad; return { lat: lat, lng: lng }; }, /// /// takes a set of NATO style UTM coordinates and converts them to a lat/lng pair. /// /// { lat: y, lng: x } /// /// inputs: /// utme: easting /// utmn: northing /// utmz: longitudinal zone /// latz: character representing latitudinal zone /// digraph: string representing grid /// /// natoToLatLng: function (utme, utmn, utmz, latz, digraph) { var coords = this.natoToUtm(utme, utmn, utmz, latz, digraph); return this.utmToLatLng(coords.easting, coords.northing, coords.zone, coords.southern); }, /// /// convert a set of nato coordinates to the global system. returns a structure /// /// { norhting: y, easting: x, zone: zone, southern: hemisphere } /// /// inputs: /// utme: easting /// utmn: northing /// utmz: longitudinal zone /// latz: character representing latitudinal zone /// digraph: string representing grid /// /// checks for digraph validity /// natoToUtm: function (utme, utmn, utmz, latz, digraph) { latz = latz.toUpperCase(); digraph = digraph.toUpperCase(); var eltr = digraph.charAt(0); var nltr = digraph.charAt(1); /// /// make sure the digraph is consistent /// if (nltr == "I" || eltr == "O") throw "I and O are not legal digraph characters"; if (nltr == "W" || nltr == "X" || nltr == "Y" || nltr == "Z") throw "W, X, Y and Z are not legal second characters in a digraph"; var eidx = this.digraphLettersE.indexOf(eltr); var nidx = this.digraphLettersN.indexOf(nltr); if (utmz / 2 == Math.floor(utmz / 2)) { nidx -= 5; // correction for even numbered zones } var ebase = 100000*(1 + eidx - 8 * Math.floor(eidx / 8)); var latBand = this.digraphLettersE.indexOf(latz); var latBandLow = 8 * latBand - 96; var latBandHigh = 8 * latBand - 88; if (latBand < 2) { latBandLow = -90; latBandHigh = -80; } else if (latBand == 21) { latBandLow = 72; latBandHigh = 84; } else if (latBand > 21) { latBandLow = 84; latBandHigh = 90; } var lowLetter = Math.floor(100 + 1.11 * latBandLow); var highLetter = Math.round(100 + 1.11 * latBandHigh); var latBandLetters = null; if (utmz / 2 == Math.floor(utmz / 2)) { latBandLetters = this.digraphLettersAll.slice(lowLetter + 5, highLetter + 5); } else { latBandLetters = this.digraphLettersAll.slice(lowLetter, highLetter); } var nbase = 100000 * (lowLetter + latBandLetters.indexOf(nltr)); var x = ebase + utme; var y = nbase + utmn; if (y > 10000000) { y = y - 10000000; } if (nbase >= 10000000) { y = nbase + utmn - 10000000; } var southern = nbase < 10000000; return { northing: y, easting: x, zone: utmz, southern: southern }; }, /// /// returns a set of nato coordinates from a set of global UTM coordinates /// return is an object with the following structure: /// { northing: n, easting: e, latZone: z0, lngZone: z1, digraph: xx } /// /// inputs: /// x: easting /// y: northing /// utmz: the utm zone /// southern: hemisphere indicator /// utmToNato: function (x, y, utmz, southern) { var esq = (1 - (this.b / this.a) * (this.b / this.a)); var e0sq = this.e * this.e / (1 - Math.pow(this.e, 2)); var e1 = (1 - Math.sqrt(1 - Math.pow(this.e, 2))) / (1 + Math.sqrt(1 - Math.pow(this.e, 2))); var M0 = 0; if (!southern) M = M0 + y / this.k0; // Arc length along standard meridian. else M = M0 + (y - 10000000) / this.k; // // calculate the latitude so that we can derive the latitude zone // var mu = M / (this.a * (1 - esq * (1 / 4 + esq * (3 / 64 + 5 * esq / 256)))); var phi1 = mu + e1 * (3 / 2 - 27 * e1 * e1 / 32) * Math.sin(2 * mu) + e1 * e1 * (21 / 16 - 55 * e1 * e1 / 32) * Math.sin(4 * mu); //Footprint Latitude phi1 = phi1 + e1 * e1 * e1 * (Math.sin(6 * mu) * 151 / 96 + e1 * Math.sin(8 * mu) * 1097 / 512); var C1 = e0sq * Math.pow(Math.cos(phi1), 2); var T1 = Math.pow(Math.tan(phi1), 2); var N1 = this.a / Math.sqrt(1 - Math.pow(this.e * Math.sin(phi1), 2)); var R1 = N1 * (1 - Math.pow(this.e, 2)) / (1 - Math.pow(this.e * Math.sin(phi1), 2)); var D = (x - 500000) / (N1 * this.k0); var phi = (D * D) * (1 / 2 - D * D * (5 + 3 * T1 + 10 * C1 - 4 * C1 * C1 - 9 * e0sq) / 24); phi = phi + Math.pow(D, 6) * (61 + 90 * T1 + 298 * C1 + 45 * T1 * T1 - 252 * e0sq - 3 * C1 * C1) / 720; phi = phi1 - (N1 * Math.tan(phi1) / R1) * phi; var lat = Math.floor(1000000 * phi / this.drad) / 1000000; // convert latitude to latitude zone for nato if (lat > -80 && lat < 72) { latz = Math.floor((lat + 80) / 8) + 2; // zones C-W in this range } if (lat > 72 && lat < 84) { latz = 21; // zone X } else if (lat > 84) { latz = 23; // zone Y-Z } var digraph = this.makeDigraph(x, y, utmz); x = Math.round(10 * (x - 100000 * Math.floor(x / 100000))) / 10; y = Math.round(10 * (y - 100000 * Math.floor(y / 100000))) / 10; return { easting: x, northing: y, latZone: this.digraphLettersN[latz], lngZone: utmz, digraph: digraph } }, /// /// create a nato grid digraph. /// /// inputs: /// x: easting /// y: northing /// utmz: utm zone /// makeDigraph: function (x, y, utmz) { // // first get the east digraph letter // var letter = Math.floor((utmz - 1) * 8 + (x) / 100000); letter = letter - 24 * Math.floor(letter / 24) - 1; var digraph = this.digraphLettersE.charAt(letter); letter = Math.floor(y / 100000); if (utmz / 2 == Math.floor(utmz / 2)) { letter = letter + 5; } letter = letter - 20 * Math.floor(letter / 20); digraph = digraph + this.digraphLettersN.charAt(letter); return digraph; } }/*** JS Library Function ***/ function createCookie(name,value,days) { if (days) { var date = new Date(); date.setTime(date.getTime()+(days*24*60*60*1000)); var expires = "; expires="+date.toGMTString(); } else var expires = ""; document.cookie = name+"="+value+expires+"; path=/"; } function readCookie(name) { var nameEQ = name + "="; var ca = document.cookie.split(';'); for(var i=0;i < ca.length;i++) { var c = ca[i]; while (c.charAt(0)==' ') c = c.substring(1,c.length); if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length); } return null; } function deleteCookie(name) { if (readCookie(name) !== null) { createCookie(name,"",-1); } } function isNumber(n) { return typeof(n) != 'undefined' && n && !isNaN(parseFloat(n)) && isFinite(n); } function stripHtml(htmlString) { if(htmlString){ var mydiv = document.createElement("div"); mydiv.innerHTML = htmlString; if (document.all) // IE Stuff return mydiv.innerText; else // Mozilla does not work with innerText return mydiv.textContent; } return false; } function mergeObject(obj1,obj2){ var obj3 = {}; for (var attrname in obj1) { obj3[attrname] = obj1[attrname]; } for (var attrname in obj2) { obj3[attrname] = obj2[attrname]; } return obj3; } function setOpacity(obj, opacity) { obj.style.filter = (opacity == 100) ? "none" : "alpha(opacity:"+opacity+")"; // IE/Win obj.style.KHTMLOpacity = opacity/100; // Safari<1.2, Konqueror obj.style.MozOpacity = opacity/100; // Older Mozilla and Firefox obj.style.opacity = opacity/100; // Safari 1.2, newer Firefox and Mozilla, CSS3 } function isInArray(item, arr, argStrict) { var key = '', strict = !! argStrict; if (strict) { for (key in arr) { if (arr[key] === item) { return true; } } } else { for (key in arr) { if (arr[key] == item) { return true; } } } return false; } function isInt(n) { return !isNaN(n) && parseFloat(n) == parseInt(n, 10) && !isNaN(n); } function checkWindowSize() { var window_h = window.innerHeight||document.documentElement.clientHeight||document.body.clientHeight||0; var window_w = window.innerWidth||document.documentElement.clientWidth||document.body.clientWidth||0; return {w: window_w, h: window_h}; } function checkDivSize(ele_id, op) { var ele = false; if(typeof(ele_id) == 'string') { ele = document.getElementById(ele_id); } else if (typeof(ele_id) == 'object') { ele = ele_id; } if(!ele || typeof(ele) != 'object') return 0; if(op && op == 'h') { return ele.clientHeight || ele.offsetHeight || 0; } else if(op && op == 'w') { return ele.clientWidth || ele.offsetWidth || 0; } var div_h = ele.clientHeight || ele.offsetHeight || 0; var div_w = ele.clientWidth || ele.offsetWidth || 0; return {w: div_w, h: div_h}; } function rpad(str, padString, length) { if(!str || !padString || !length) return str; while (str.length < length) str = str + padString; return str; } function lpad(str, padString, length) { if(!str || !padString || !length) return str; while (str.length < length) str = padString + str; return str; } function getRgbColorFromHex(hexcolor) { var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; hexcolor = hexcolor.replace(shorthandRegex, function(m, r, g, b) { return r + r + g + g + b + b; }); var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hexcolor); return result ? { r: parseInt(result[1], 16), g: parseInt(result[2], 16), b: parseInt(result[3], 16) } : null; } function getHexColorFromRGBA(color) { if(!color) return ''; var rgba = /rgba\((.+),\s*(.+),\s*(.+),\s*(.+)\)/.exec(color); var red = parseInt(rgba[1]); var green = parseInt(rgba[2]); var blue = parseInt(rgba[3]); var rgb = (blue | (green << 8) | (red << 16)).toString(16); rgb = lpad(rgb, '0', 6); return '#' + rgb; } function getOpacityFromRGBA(color) { if(!color) return ''; var rgba = /rgba\((.+),\s*(.+),\s*(.+),\s*(.+)\)/.exec(color); return rgba[4]; } function updateQueryStringParam(param, value) { var pathname, baseUrl; if (param == 'ooi' && value != '') { pathname = '/p/'+value; baseUrl = [location.protocol, '//', location.host, pathname].join(''); var params = document.location.search; // window.history.replaceState({}, "", baseUrl + params); // comment out 20201103 updateQueryStringParam(param, ''); } else { pathname = location.pathname; var urlQueryString = document.location.search; if (param == 'all' && value == '') { urlQueryString = ''; pathname = '/'; } baseUrl = [location.protocol, '//', location.host, pathname].join(''); var newParam = param + '=' + value, params = '?' + newParam; // If the "search" string exists, then build params from it if (urlQueryString) { keyRegex = new RegExp('([\?&])' + param + '[^&]*'); // If param exists already, update it if (urlQueryString.match(keyRegex) !== null) { if (!value) { params = urlQueryString.replace(keyRegex, ''); } else { params = urlQueryString.replace(keyRegex, "$1" + newParam); } } else { // Otherwise, add it to end of query string if(value) { params = urlQueryString + '&' + newParam; } else { params = urlQueryString; } } } else { if (!value) { params = ''; } } // window.history.replaceState({}, "", baseUrl + params); // comment out 20201103 } } function LongdoLib() { var self = this; this.constructor = function (obj) { this._lang = obj.lang; } this.hideElement = function (ele) { let elements = []; if (typeof(ele) == 'object') { elements = [ele]; } else { elements = document.body.querySelectorAll(ele); } for(let idx=0; idx < elements.length; idx++ ) { elements[idx].original_display = elements[idx].style.display; elements[idx].style.display = 'none'; } } this.showElement = function (ele) { let elements = []; if (typeof(ele) == 'object') { elements = [ele]; } else { elements = document.body.querySelectorAll(ele); } for(let idx=0; idx < elements.length; idx++ ) { if (elements[idx].original_display) { elements[idx].style.display = elements[idx].original_display; } else { elements[idx].style.display = ''; } } } this.getDimensions = function(element) { var display = element.style.display; if (display != 'none' && display != null) // Safari bug return {width: element.offsetWidth, height: element.offsetHeight}; // All *Width and *Height properties give 0 on elements with display none, // so enable the element temporarily var els = element.style; var originalVisibility = els.visibility; var originalPosition = els.position; var originalDisplay = els.display; els.visibility = 'hidden'; els.position = 'absolute'; els.display = 'block'; var originalWidth = element.clientWidth; var originalHeight = element.clientHeight; els.display = originalDisplay; els.position = originalPosition; els.visibility = originalVisibility; return {width: originalWidth, height: originalHeight}; } this.getElementSize = function (ele, op) { if (typeof(ele) == 'string') { let elements = document.body.querySelectorAll(ele); ele = elements[0]; } if (typeof(ele) == 'object') { if (ele == window) { return {w:(window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth || 0), h:(window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight || 0)}; } let size_obj = {w:ele.clientWidth, h:ele.clientHeight, w_size:this.getDimensions(ele).width, h_size:this.getDimensions(ele).height}; if (op) { if (op == 'h') { return size_obj.h; } else if (op == 'w') { return size_obj.w; } } return size_obj; } return {w:0, h:0, w_size:0, h_size:0}; } this.setElementStyle = function (ele, style_object) { if (typeof(ele) == 'string') { let elements = document.body.querySelectorAll(ele); for(let idx=0; idx < elements.length; idx++ ) { this.setElementStyle(elements[idx], style_object); } return; } else if (typeof(ele) == 'object') { for (var key in style_object) { if (!style_object.hasOwnProperty(key)) continue; ele.style[key] = style_object[key]; } } } this.timeConverter = function(unixtimestamp) { var a = new Date(unixtimestamp * 1000); var month_en = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; var month_th = ['ม.ค.','ก.พ.','มี.ค.','เม.ย.','พ.ค.','มิ.ย.','ก.ค.','ส.ค.','ก.ย.','ต.ค.','พ.ย.','ธ.ค.']; var year = a.getFullYear(); if (this._lang == 'th') { year += 543; } var month = this._lang == 'th' ? month_th[a.getMonth()] : month_en[a.getMonth()]; var date = a.getDate(); var hour = a.getHours(); var min = a.getMinutes(); var sec = a.getSeconds(); var time = date + ' ' + month + ' ' + year + ' ' + (hour < 10 ? '0' : '') + hour + ':' + (min < 10 ? '0' : '') + min + ':' + (sec < 10 ? '0' : '') + sec ; return time; } this.getDateFormatYmdH = function() { var dd = new Date(); return '' + dd.getFullYear() + (((dd.getMonth()+1) < 10 ? '0' : '') + (dd.getMonth()+1)) + ((dd.getDate() < 10 ? '0' : '') + dd.getDate()) + ((dd.getHours() < 10 ? '0' : '') + dd.getHours()); } this.getTimestampForHour = function() { var now = Date.now()/1000; return ~~(now/3600); //now - (now%3600) } this.isNumber = function(n) { return typeof(n) != 'undefined' && n && !isNaN(parseFloat(n)) && isFinite(n); } this.isInt = function(n) { return !isNaN(n) && parseFloat(n) == parseInt(n, 10) && !isNaN(n); } this.isHTML = function(str) { var a = document.createElement('div'); a.innerHTML = str; for (var c = a.childNodes, i = c.length; i--; ) { if (c[i].nodeType == 1) return true; } return false; } this.loadjscssfile = function(filename, filetype, lastupdated, callback) { if(!lastupdated) lastupdated = ''; // var lastupdated = '?20140717'; if (filetype=="js") { //if filename is a external JavaScript file var fileref=document.createElement('script') fileref.setAttribute("type","text/javascript") fileref.setAttribute("src", filename+lastupdated) if(typeof callback == 'string') { fileref.setAttribute("onload", callback); } } else if (filetype=="css") { //if filename is an external CSS file var fileref=document.createElement("link") fileref.setAttribute("rel", "stylesheet") fileref.setAttribute("type", "text/css") fileref.setAttribute("href", filename+lastupdated) } if (typeof fileref!="undefined") document.getElementsByTagName("head")[0].appendChild(fileref); } this.callAjax = function(url, options) { var method = options.method ? options.method.toLowerCase() : 'get'; var xhr = new XMLHttpRequest(); if (method == 'get') { if(options.parameters) { url += (url.indexOf('?') > -1 ? '&' : '?') + options.parameters; } } else if(method == 'post') { xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); } // this.url += (this.url.indexOf('?') > -1 ? '&' : '?') + params; if(method == 'post') { xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); } xhr.open(method, url); xhr.onload = function(rs) { if (xhr.status === 200) { if(options.onSuccess) { options.onSuccess(rs.currentTarget); } } else { if(options.onFail) { options.onFail(); } console.log('Request failed. Returned status: ' + xhr.status); } }; if(method == 'post' && options.parameters) { xhr.send(encodeURI(options.parameters)); } else { xhr.send(); } return xhr; } this.callAjaxJsonp = function(url, callback) { var callbackName = 'jsonp_callback_' + Math.round(100000 * Math.random()); window[callbackName] = function(data) { delete window[callbackName]; document.body.removeChild(script); if(typeof callback == 'function') { callback(data); } }; var script = document.createElement('script'); script.src = url + (url.indexOf('?') >= 0 ? '&' : '?') + 'callback=' + callbackName; document.body.appendChild(script); } this.setCookie = function(name,value,days) { if (days) { var date = new Date(); date.setTime(date.getTime()+(days*24*60*60*1000)); var expires = "; expires="+date.toGMTString(); } else var expires = ""; document.cookie = name+"="+value+expires+"; path=/"; } this.getCookie = function(name) { var nameEQ = name + "="; var ca = document.cookie.split(';'); for(var i=0;i < ca.length;i++) { var c = ca[i]; while (c.charAt(0)==' ') c = c.substring(1,c.length); if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length); } return false; } this.deleteCookie = function(name) { if (self.getCookie(name) !== null) { self.setCookie(name,"",-1); } } this.setLocalStorage = function(name,value) { if (window.localStorage) { localStorage.setItem(name, value); } } this.getLocalStorage = function(name) { if (window.localStorage) { return localStorage.getItem(name); } return false; } this.deleteLocalStorage = function(name) { if (window.localStorage) { localStorage.removeItem(name); } } this.setLocalData = function(name,value,days) { self.setCookie(name,value,days); self.setLocalStorage(name,value); } this.getLocalData = function(name) { var val = self.getLocalStorage(name); if(!val) { val = self.getCookie(name); } return val; } } var OOI_OVERLAYS = new Array(); var SEARCHRESULT_OVERLAYS = new Array(); var MAPSERVER_IMG_URL = '//ms.longdo.com/mmmap/img.php'; var loading_popup = "
"; function convertDMSToLatLon(_dms_lat, _dms_lon) { // ex. ' 51 28 40.12 N', '000 00 05.31 W' var lat = Geo.parseDMS(_dms_lat); var lon = Geo.parseDMS(_dms_lon); if (lat && lon) { lat = lat.toFixed(6); lon = lon.toFixed(6); var p1 = new LatLon(lat, lon); if (p1) return { "lat": lat, "lon" : lon }; } return false; } function convertLatLonToDMS(_lat, _lon) { var lat = Geo.toLat(_lat, "dms", 2); var lon = Geo.toLon(_lon, "dms", 2); if (lat && lon) { return { "lat": lat, "lon" : lon }; } return false; } function convertUTMToLatLon(easting, northing, zone, hemi) { if (!zone) { zone = 47; } if (!hemi) { hemi = 'N'; } var southern = (hemi.toLowerCase() == 's'); var error_msg = '' if (zone < 1 || zone > 60) { return { "lat": 0, "lon" : 0, "error" : 'ระบุ Zone ('+zone+') ไม่ถูกต้อง
ค่า Longitude zone ต้องอยู่ระหว่าง 1 ถึง 60'}; } if (northing < 0 || northing > 10000000) { return { "lat": 0, "lon" : 0, "error" : 'ระบุ Northing ('+northing+') ไม่ถูกต้อง
ค่า Northing ต้องอยู่ระหว่าง 0 ถึง 10000000'}; } if (easting < 160000 || easting > 834000) { return { "lat": 0, "lon" : 0, "error" : 'ระบุ Easting ('+easting+') ไม่ถูกต้อง
ค่า Easting ต้องอยู่ระหว่าง 160000 ถึง 834000'}; } var datum = southern ? 1 : 0; utmconv.setDatum(datum); var latlon = utmconv.utmToLatLng(easting, northing, zone, southern); // get lat/lon for this set of coordinates if (!isNaN(latlon.lat) && !isNaN(latlon.lng)) { return { "lat": latlon.lat, "lon" : latlon.lng }; } return false; } function convertLatLonToUTM(_lat, _lon) { utmconv.setDatum(0); var utm_format = utmconv.latLngToUtm(parseFloat(_lat), parseFloat(_lon)); var utm_txt = ''; if (utm_format && utm_format.global) { return {"e": utm_format.global.easting, "n": utm_format.global.northing, "zone": utm_format.global.zone, "hemi": (utm_format.global.southern ? 'S' : 'N'), "full": utm_format.global.easting + ", " + utm_format.global.northing + ", " + utm_format.global.zone + (utm_format.global.southern ? 'S' : 'N') }; } return false; } function setCenterByTG(_tg_x, _tg_y) { // Thai Geocode XXXXX-YYYYY // XXXXX = 100,000 * (latitude - 5 ) // YYYYY = 100,000 * (longitude - 97 ) var mylat = tgbase32todec(_tg_y)/100000 + 5; var mylong = tgbase32todec(_tg_x)/100000 + 97; return moveLocation(mylat,mylong); } function getMaxZoom() { if(typeof map != 'undefined' && typeof mmmap2 == 'undefined') mmmap2 = map; // support web-mobile return mmmap2.projection().maxZoom; } function getMapProjection() { if(typeof map != 'undefined' && typeof mmmap2 == 'undefined') mmmap2 = map; return mmmap2.projection().longdoName ? mmmap2.projection().longdoName : ''; } function mapZoom(zoom) { if(typeof map != 'undefined' && typeof mmmap2 == 'undefined') mmmap2 = map; if(!mmmap2) return false; if(isNumber(zoom)) { mmmap2.zoom(zoom); } else { return mmmap2.zoom(); } } function getMouseLocation() { if(typeof map != 'undefined' && typeof mmmap2 == 'undefined') mmmap2 = map; if(!mmmap2) return false; return mmmap2.location(longdo.LocationMode.Pointer); } function getCenterLocation() { if(typeof map != 'undefined' && typeof mmmap2 == 'undefined') mmmap2 = map; if(!mmmap2) return false; return mmmap2.location(); } function mapBoundary(bound, pivot) { if(typeof map != 'undefined' && typeof mmmap2 == 'undefined') mmmap2 = map; if(!mmmap2) return false; if(bound) mmmap2.bound(bound, pivot); else return mmmap2.bound(); } function moveLocation(lat, lon, zoom) { if(typeof map != 'undefined' && typeof mmmap2 == 'undefined') mmmap2 = map; if(!mmmap2) return false; if(isNumber(lat) && isNumber(lon)) { var rs; try { rs = mmmap2.location({lat: lat, lon: lon}); } catch(ex) { // ex == 'Invalid location' return false; } } if(isNumber(zoom)) { mmmap2.zoom(zoom); } return true; } function boundaryIsOutOfBoundary(boundary) { if(!boundary) return false; var outofboundary = false; if(!outofboundary) { outofboundary = isOutOfBoundary(boundary.minLat, boundary.minLon); } if(!outofboundary) { outofboundary = isOutOfBoundary(boundary.maxLat, boundary.minLon); } if(!outofboundary) { outofboundary = isOutOfBoundary(boundary.minLat, boundary.maxLon); } if(!outofboundary) { outofboundary = isOutOfBoundary(boundary.maxLat, boundary.maxLon); } return outofboundary; } function pointArrayIsOutOfBoundary(point_array) { var boundary = getMinMaxBoundary(point_array); return boundaryIsOutOfBoundary(boundary); } function isOutOfBoundary(lat, lon) { if(typeof map != 'undefined' && typeof mmmap2 == 'undefined') mmmap2 = map; if(!mmmap2) return false; if(isNumber(lat) && isNumber(lon)) { lat = parseFloat(lat); lon = parseFloat(lon); var bound = mapBoundary(); if ( lat < bound.minLat || lat > bound.maxLat || lon < bound.minLon || lon > bound.maxLon ) { return true; } } return false; } function moveLocationWhenOutOfBoundary(lat, lon) { if(typeof map != 'undefined' && typeof mmmap2 == 'undefined') mmmap2 = map; if(!mmmap2) return false; if(isNumber(lat) && isNumber(lon)) { if ( isOutOfBoundary(lat, lon) ) { moveLocation(lat, lon); } } } function showPopup(lat, lon, title, detail, op) { if(typeof map != 'undefined' && typeof mmmap2 == 'undefined') mmmap2 = map; if(!mmmap2 || !longdo) return false; var popup_op = {title: title, detail: detail, autoFocus: true}; if(op) popup_op = mergeObject(popup_op, op); var popup = new longdo.Popup( { lon: lon, lat: lat }, popup_op ); mmmap2.Overlays.add(popup); } function clearShape(shape) { if(typeof map != 'undefined' && typeof mmmap2 == 'undefined') mmmap2 = map; mmmap2.Overlays.remove(shape); } function clearMarker(marker) { if(typeof map != 'undefined' && typeof mmmap2 == 'undefined') mmmap2 = map; mmmap2.Overlays.remove(marker); } function clearPopup(popup) { if(typeof map != 'undefined' && typeof mmmap2 == 'undefined') mmmap2 = map; mmmap2.Overlays.remove(popup); } function clearActivePopup() { clearPopup(getActivePopup()); } function getOOIID(id, prefix) { if(typeof prefix == 'undefined') { prefix = 'A' } if(isInt(id) && id+'' != '-1') { while (id.length < 8) { id = "0" + id; } id = prefix + id; } return id; } function drawDefaultMarker(lat, lon, title, detail, id, focusmarker, hidepopup) { var imageObject = document.createElement('img'); imageObject.mark_lat = lat; imageObject.mark_long = lon; if(typeof(id) != 'undefined' && id !== false && id+'' !== '-1') { imageObject.poi_id = id; imageObject.onmousedown = poiClicked; imageObject.style.cursor = "pointer"; } imageObject.title = title; imageObject.border=0; imageObject.style.zIndex = 1000; var pin_h = 42; var pin_w = 30; //imageObject.src="//mmmap15.longdo.com/mmmap/images/pin.png"; imageObject.src="/mmmap/images/ic_pin.svg"; imageObject.style.height=pin_h+'px'; imageObject.style.width=pin_w+'px'; imageObject.className='marker-pin-default'; hidepopup = (hidepopup == true || hidepopup == 1); drawMarker(imageObject, lat, lon, title, detail, id, {offset: {x: (pin_w/2), y: pin_h }}, {hidepopup: hidepopup, focusmarker: focusmarker}); } function drawMarker(markersrc, lat, lon, title, detail, id, icon_op, marker_op, animation) { if(typeof map != 'undefined' && typeof mmmap2 == 'undefined') mmmap2 = map; if(!mmmap2 || !longdo) return false; if(typeof animation == 'undefined' || !animation) { animation = false; } id = getOOIID(id); if(typeof(id) != 'undefined' && id !== false && id+'' !== '-1' && !detail) { detail = loading_popup; } var _icon_op = {}; if(typeof(markersrc) == 'object') { var wrap = document.createElement('div'); wrap.appendChild(markersrc.cloneNode(true)); markersrc = wrap.innerHTML; _icon_op.html = markersrc; } else if(typeof(markersrc) == 'string') { _icon_op.url = markersrc; } for(var property in icon_op) { if(typeof property == "string" && property != "") { _icon_op[property] = icon_op[property]; } } var location = { lon: lon, lat: lat }; var markerop = {}; markerop.title = title; markerop.icon = _icon_op; markerop.detail = detail; markerop.weight = longdo.OverlayWeight.Top; for(var property in marker_op) { if(typeof property == "string" && property != "" && property != "longdomap") { markerop[property] = marker_op[property]; } } var marker = new longdo.Marker(location, markerop); //var marker = new longdo.Marker({ lon: lon, lat: lat }, { title: title, icon: { html: markersrc, offset: { x: 12, y: 44 }}, popup: { html: detail }}) //var marker = new longdo.Marker(location, markerop); marker.mark_lat = lat; marker.mark_long = lon; if(id && id+'' != '-1') { marker.poi_id = id; marker.data = {'id': id}; } if(marker_op) { if(marker_op.hidepopup) { marker.hidepopup = true; } if(marker_op.focusmarker) { marker.focusmarker = true; } if(typeof marker_op.longdomap != "undefined") { marker.longdomap = marker_op.longdomap } } if(animation) { mmmap2.Overlays.drop(marker); } else { mmmap2.Overlays.add(marker); } return marker; } function getShapeOption(linewidth, linecolor, fillcolor, title, detail, label, editable) { var op = {}; if(typeof title != 'undefined') { op.title = title; } if(typeof detail != 'undefined') { op.detail = detail; } if(typeof label != 'undefined') { op.label = label; } if(typeof linewidth != 'undefined'){ op.lineWidth = linewidth; } if(typeof linecolor != 'undefined'){ op.lineColor = linecolor; } if(typeof fillcolor != 'undefined'){ op.fillColor = fillcolor; } if(typeof editable != 'undefined'){ op.editable = editable; } return op; } function cloneShape(shape, diffop, keep_ori_shape) { if(typeof diffop != 'undefined') { for(var property in diffop) { if(typeof property == "string" && property != "") { shape[property] = diffop[property]; } } } var obj = false; var shape_type = getShapeType(shape); var label = shape.editable && typeof(shape.editable) == 'object' ? true : shape.label; if(shape_type == 'polygon') { obj = drawPolygon(shape.location(), shape.lineWidth, shape.lineColor, shape.fillColor, shape.title, shape.detail, label, shape.editable); } else if(shape_type == 'line') { obj = drawLine(shape.location(), shape.lineWidth, shape.lineColor, shape.fillColor, shape.title, shape.detail, label, shape.editable); } if(typeof keep_ori_shape == 'undefined' || !keep_ori_shape) clearShape(shape); return obj; } function drawLine(points, linewidth, linecolor, fillcolor, title, detail, label, editable) { if(typeof map != 'undefined' && typeof mmmap2 == 'undefined') mmmap2 = map; var line = new longdo.Polyline(points, getShapeOption(linewidth, linecolor, fillcolor, title, detail, label, editable)); mmmap2.Overlays.add(line); return line; } function drawPolygon(points, linewidth, linecolor, fillcolor, title, detail, label, editable) { if(typeof map != 'undefined' && typeof mmmap2 == 'undefined') mmmap2 = map; var polygon = new longdo.Polygon(points, getShapeOption(linewidth, linecolor, fillcolor, title, detail, label, editable)); mmmap2.Overlays.add(polygon); return polygon; } function drawDot(point, linewidth, linecolor, fillcolor, title, detail, label, editable) { if(typeof map != 'undefined' && typeof mmmap2 == 'undefined') mmmap2 = map; var dot = new longdo.Dot(point, getShapeOption(linewidth, linecolor, fillcolor, title, detail, label, editable)); mmmap2.Overlays.add(dot); return dot; } function showPopupLocationDetail(id, overlay) { var service; if(/^foursquare:/.test(id)) { service = 'foursquare'; id = id.replace("foursquare:",""); } else if(/^google:/.test(id)) { service = 'google'; id = id.replace("google:",""); } else if(/^osm:/.test(id)) { service = 'osm'; id = id.replace("osm:",""); } else { service = 'longdo'; } if(service == 'longdo' && !(/^(A|G|M|Y|S|OSM:)/i.test(id) || !isNaN(id))) { //mmmap.showLocationDetail(id, popup_idx); return; } if (service == 'longdo') { if(window.setPermalinkParam) setPermalinkParam('ooi', id); } updateActivePopupContent(loading_popup, overlay); if (typeof(mmmap_map) !== 'undefined' && mmmap_map && mmmap_map._mmmap_lib && /^OSM:/i.test(id)) { mmmap_map._mmmap_lib.showOOIPopupDetail(id, overlay); } else { var d = new Date; var timestamp = d.getTime(); var pars = "timestamp=" + encodeURIComponent( timestamp ); var url = '/mmmap/rpc.php'; var pars = "id=" + encodeURIComponent( id ) + "×tamp=" + encodeURIComponent( timestamp ) + "&locale=" + mylang + "&mmroute=" + isEnableRouting() + "&map=" + "&action=showpoidetails" + "&service=" + service + "&popup_idx=0" + "&uid="+(typeof Drupal != 'undefined' && typeof Drupal.settings != 'undefined' && typeof Drupal.settings.user != 'undefined' && Drupal.settings.user.uid ? Drupal.settings.user.uid : 0); var callbackFn = function(txt) { handleResponse(txt, overlay); if(typeof(gapi) != 'undefined' && gapi && (browser!='IE' || (browser=='IE' && version>7))) { setTimeout(function() {gapi.plusone.go()}, 1500); } if(service == 'longdo' && !/^\/snippet\/iframe/.test(document.location.pathname)) { updateQueryStringParam('ooi', id); } // get car park avilable for tag: parking only setTimeout(function() { var carParkAvailableDiv = document.getElementById('longdomap-carpark-available-popup'); if (carParkAvailableDiv) { var urlGetCarParkAvailableById = '/ws.php'; var params = 'service=get_car_park_available_by_id&ooiid=' + id.replace('A', ''); var cb = function(r) { var response = JSON.parse(r.response); switch (response.code) { case 'SUCCESS': { var data = response.data; var carParkAvailableText; if (data.available === 'FULL' || data.available === 'เต็ม' || Number(data.available) == 0) { carParkAvailableText = mylang == 'th' ? 'หนาแน่น' : 'Busy'; $(carParkAvailableDiv).addClass('busy'); } else if (data.available === 'N/A') { carParkAvailableText = mylang == 'th' ? 'ไม่มีข้อมูล' : 'No data'; $(carParkAvailableDiv).addClass('nodata'); } else { carParkAvailableText = (mylang == 'th' ? 'ว่าง ' : 'Available ') + Number(data.available).toLocaleString(); if (Number(data.available) <= 20) { $(carParkAvailableDiv).addClass('min'); } else { $(carParkAvailableDiv).addClass('normal'); } } carParkAvailableDiv.innerHTML = carParkAvailableText; break; } case 'FAILED': { break; } } }; longdolib.callAjax(urlGetCarParkAvailableById, {method: 'get', parameters: params, onSuccess: cb}); } }, 1500); } longdolib.callAjax(url, {method: 'get', parameters: pars, onSuccess: callbackFn}); } } function isEnableRouting() { if(typeof map != 'undefined' && typeof mmmap2 == 'undefined') mmmap2 = map; let state = false; try { state = (mmmap2 && typeof mmmap2.Route != 'undefined' && typeof mmmap2.Route.placeholder == 'function' && mmmap2.Route.placeholder() != null); } catch(ex) { console.log('isEnableRouting: ' + ex.message); } return state; } function showTrafficCamerasAndEvent() { if(typeof map != 'undefined' && typeof mmmap2 == 'undefined') mmmap2 = map; if(!mmmap2) return; mmmap2.Overlays.load(longdo.Overlays.events); mmmap2.Overlays.load(longdo.Overlays.cameras); } function hideTrafficCamerasAndEvent() { if(typeof map != 'undefined' && typeof mmmap2 == 'undefined') mmmap2 = map; if(!mmmap2) return; mmmap2.Overlays.unload(longdo.Overlays.events); mmmap2.Overlays.unload(longdo.Overlays.cameras); } function getActivePopup() { if(typeof map != 'undefined' && typeof mmmap2 == 'undefined') mmmap2 = map; if(mmmap2.Overlays && mmmap2.Overlays.list) { var overlay_list = mmmap2.Overlays.list(); if(overlay_list && overlay_list.length > 0) { for(var i=0; i ' + txt + ' ') : ' ' + txt + ' '); if(callback) { b.onclick = callback; } b.onmouseover = function () { $("#"+b.id).addClass("hover"); }; b.onmouseout = function () { $("#"+b.id).removeClass("hover"); }; if(position == 'prepend') { $(".ldmap_toolbar").prepend(b); } else { $(".ldmap_toolbar").append(b); } } function includeAddLocationButton(buttons) { switch(buttons) { case 'addlocation': var b1 = document.createElement('span'); b1.id = 'mm-addlocation-button'; b1.style.cursor = 'pointer'; b1.style.marginLeft = '5px'; b1.title = mylang == 'th' ? "ปักหมุดสถานที่ใหม่โดยการกดเพื่อลากและวางหมุดบนแผนที่" : "Add new location by Drag and drop pin on the map."; b1.innerHTML = ''; b1.onclick = function() { var location = getCenterLocation(); markDroppedPinWithAnimation(location.lat, location.lon); }; b1.onmouseover = function () { $("#mm-addlocation-button").addClass("hover"); }; b1.onmouseout = function () { $("#mm-addlocation-button").removeClass("hover"); }; $(".ldmap_toolbar").append(b1); //canvas.addButton('customize', b1); setDragableOnDropPin(); break; } } function getMinMaxBoundary(obj, boundary) { var lat, lon; var num_points = obj.length; for(var i=0; i boundary.maxLat) { boundary.maxLat = lat; } if(lat < boundary.minLat) { boundary.minLat = lat; } if(lon < boundary.minLon) { boundary.minLon = lon; } if(lon > boundary.maxLon) { boundary.maxLon = lon; } } } return boundary; } function clearBusStopTagfromSearching() { if(typeof map != 'undefined' && typeof mmmap2 == 'undefined') mmmap2 = map; if(_tag_busstop_from_searching && _tag_busstop_from_searching.length > 0) { var num_tag = _tag_busstop_from_searching.length for(var i=0; i'}}; } var overlay = new longdo.Overlays.Object(id, ds, icon); if(typeof(dozoom) != 'undefined') {overlay.dozoom = dozoom;} if(hidePopup === true || hidePopup == 1) { overlay.hidepopup = true; } if(ds == 'LONGDO') { overlay.poi_id = id; overlay.data = {'id': id}; overlay.longdotype = 'ooi'; overlay.ooitype = 'point'; if(typeof showicon != 'undefined' && showicon == 'showicon') overlay.showicon = showicon; } OOI_OVERLAYS[id] = overlay; let rs_ol = mmmap2.Overlays.load(overlay); if(!rs_ol) { // map api v3 document.body.classList.add("unsupport-overlay-load"); } return overlay; } function isResetSearchAndTagResults() { return (document.getElementById('clear-old-tag') && document.getElementById('clear-old-tag').checked); } function showTag(tagname, tag_op, usedefaultmode, set_search_val) { if(typeof map != 'undefined' && typeof mmmap2 == 'undefined') mmmap2 = map; if (window.selectMapTab) { selectMapTab()}; if(set_search_val) { setSearchLocationValue('tag: '+tagname); if(typeof getSelectedMapdata == 'function' && getSelectedMapdata() == 'tomtom') { longdomapSearch(''); } } if(!mmmap2 || (typeof getSelectedMapdata == 'function' && getSelectedMapdata() == 'tomtom')) return false; if(tagname != '%') { hideMyFavoriteIcons(); if(window.setPermalinkParam) setPermalinkParam('tag', tagname); } if (typeof(usedefaultmode) == 'undefined' || !usedefaultmode) { var mode = "GRAY"; if (typeof(mylang) != 'undefined' && mylang && mylang == "en") { mode = "GRAY_EN"; } setMapMode(mode); } if (!tag_op) { var min_showlevel_visible_tag = 7; var max_showlevel_visible_tag = 21; var current_zoom = mapZoom(); tag_op = {visibleRange: { min: min_showlevel_visible_tag, max: max_showlevel_visible_tag }}; if(current_zoom < min_showlevel_visible_tag) { mapZoom(min_showlevel_visible_tag); } else if(current_zoom > max_showlevel_visible_tag) { mapZoom(max_showlevel_visible_tag); } } if (tagname === 'parking') { tag_op.icon = { url: '', urlHD: '', html: '
', offset: { y: 12, x: 12 } }; } if (isResetSearchAndTagResults() && tagname != '%') { mmmap2.Tags.set(tagname, tag_op); } else { mmmap2.Tags.add(tagname, tag_op); } } function convertMapModeV2ToV1(mapmode) { var overlay = false; var mode; if(typeof mapmode == 'string') { mode = mapmode; } else { // array mode = mapmode[0]; if(mapmode[1]) { overlay = mapmode[1]; } } if(!mode) { return 'normal'; } mode = mode.toLowerCase().replace(/_/g, "-"); if(/^poi/ig.test(mode)) { mode = mode.replace(/^poi/ig, "icons"); } else if(/^satellite/ig.test(mode)) { mode = mode.replace(/^satellite/ig, "google_satellite"); } else if(/^iconstransp/ig.test(mode) && getGoogleMapModeV1() == "hybrid") { mode = mode.replace(/^iconstransp/ig, "hybrid_google"); } else if(/^tomtom/ig.test(mode)) { mode = mode.replace(/^tomtom-/ig, "tom").replace(/poi$/ig, "icons"); } if(overlay) { if(overlay == 'trafficoverlay') { mode += '+overlay'; } else if (mode == 'thaichote') { if (/-en$/ig.test(overlay)) { mode += '-en'; } } } if (mode == 'normal' || mode == 'normal-en') { let tag_list = mmmap2.Tags.list(); if(tag_list && tag_list.includes('%')) { mode = mode.replace(/^normal/ig, "normal+poi") } } return mode; } function convertMapModeV1ToV2(mode) { switch(mode) { case 'tomicons': mode = 'TOMTOM_POI'; break; case 'tomnormal': mode = 'TOMTOM_NORMAL'; break; case 'tomhydro': mode = 'TOMTOM_HYDRO'; break; default: if(mode.indexOf('+') > -1) { if(/-en$/.test(mode.split('+')[1]) && !/-en$/.test(mode.split('+')[0])) { mode = mode.split('+')[0] + '-en'; } else { mode = mode.split('+')[0]; } } mode = mode.toUpperCase().replace(/-/g, "_").replace(/^icons/ig, "POI"); break; } return mode; } function getLayerMapModeV1ToV2(mode) { var overlay = false; if(mode.indexOf('+') > -1) { overlay = mode.split('+')[1]; if(overlay == 'overlay') { overlay = 'TRAFFIC'; } else if (overlay){ overlay = overlay.toUpperCase().replace(/-/g, "_"); } } return overlay; } function isMapMode(mode) { if(typeof map != 'undefined' && typeof mmmap2 == 'undefined') mmmap2 = map; if (mode == 'icons' || mode == 'icons-en' || mode == 'normal' || mode == 'normal-en') { if (longdo.Layers.THAICHOTE && mmmap2.Layers.contains(longdo.Layers.THAICHOTE)) { return false; } } else if (mode == 'gray' || mode == 'gray-en') { if (longdo.Layers.TRAFFIC && mmmap2.Layers.contains(longdo.Layers.TRAFFIC)) { return false; } } return isInArray(mode, getMapMode()); } function getMapModeV1() { return convertMapModeV2ToV1(getMapMode()); } function getGoogleMapModeV1() { if(typeof map != 'undefined' && typeof mmmap2 == 'undefined') mmmap2 = map; if(!mmmap2) return false; var google_mode = mmmap2.Layers.externalMap(); if(/^google/g.test(google_mode)) return "hybrid"; else if(isMapMode("satellite")) return "gmap"; return false; } function getMapMode() { if(typeof map != 'undefined' && typeof mmmap2 == 'undefined') mmmap2 = map; if(!mmmap2 || !longdo) return false; var all_mode = mmmap2.Layers.list(); var map_mode = new Array(); if(all_mode) { var num_mode = all_mode.length; for(var i=0; i mapZoom()) { mapZoom(showlevel); } moveLocation(lat,mylong); } else if(dozoom == 2) { mapZoom(showlevel); moveLocation(lat,mylong); } else { moveLocationWhenOutOfBoundary(lat,mylong); } /* var popup_idx = 0; showLocationDetailPopup(id,name,mylong,lat,'',popup_idx); if (info == "" && (id+"" != "") && (id > -1 || (/^H/i.test(id)) )) { // get from network if (window.showLocationDetail && !(/^H/i.test(id)) ) { showLocationDetail(id, popup_idx); } else { mmmap.showLocationDetail(id, popup_idx); } } else { // otherwise just print what I have showDiv("locationdetails_contents", info); //setTimeout("set_popup_div_size('" + info + "','" + popup_idx + "');",400); mmmap_set_popup_div_size(popup_idx); } */ showLocationDetailPopup(id,name,mylong,lat,info); } function showLocationDetailPopup(id,name,mylong,lat,detail) { if(typeof map != 'undefined' && typeof mmmap2 == 'undefined') mmmap2 = map; var bool_force_id = (typeof(id) != 'undefined' && id !== false && id+'' !== '-1'); if(bool_force_id) { id = getOOIID(id); if(!detail) { detail = loading_popup; } } var popup = new longdo.Popup( { lon: mylong, lat: lat }, { title: name, detail: detail } ); if(bool_force_id) { popup.poi_id = id; } mmmap2.Overlays.add(popup); if(bool_force_id) { showPopupLocationDetail(id, popup); } } function markPoint(id,name,lat,mylong,marker_op,info) { var imageObject = document.createElement('img'); imageObject.mark_lat = lat; imageObject.mark_long = mylong; imageObject.poi_id = id; imageObject.title = name; imageObject.onmousedown = poiClicked; imageObject.border=0; imageObject.style.zIndex = 1000; imageObject.style.cursor = "pointer"; var pin_h = 42; var pin_w = 30; //imageObject.src="//mmmap15.longdo.com/mmmap/images/pin.png"; imageObject.src="/mmmap/images/ic_pin.svg"; imageObject.style.height=pin_h + 'px'; imageObject.style.width=pin_w + 'px'; imageObject.className='marker-pin-default'; drawMarker(imageObject, lat, mylong, name, (info ? info : ''), id, {offset: {x: (pin_w/2), y: pin_h }}, marker_op); /* var imageShadow = document.createElement('img'); // store the location/resolution imageShadow.mark_lat = lat; imageShadow.mark_long = mylong; imageShadow.poi_id = id; imageShadow.onmousedown = poiClicked; imageShadow.border=0; imageShadow.style.zIndex = 900; if(browser=='IE' && version<7) { imageShadow.src="//mmmap15.longdo.com/mmmap//images/pin_shadow.gif"; } else imageShadow.src="//mmmap15.longdo.com/mmmap//images/pin_shadow.png"; drawMarker(imageShadow, lat, mylong, name, (info ? info : ''), id, {offset: { x: 12, y: 6 }}, marker_op); */ return; } function refreshMyFavoriteIcons() { hideMyFavoriteIcons(); showMyFavoriteIcons(); } function hideMyFavoriteIcons() { if (!(typeof user_uid != 'undefined' && isNumber(user_uid) && user_uid > 0) || !longdomap || getSelectedMapdata() == 'tomtom') { return; } longdomap.hideMyFavoritePOIs(); } function showMyFavoriteIcons() { if (!(typeof user_uid != 'undefined' && isNumber(user_uid) && user_uid > 0) || !longdomap || getSelectedMapdata() == 'tomtom') { return; } longdomap.showMyFavoritePOIs(); } function poiClicked(e) { if(document.getElementById('mmmap-popup-trafficcamera-image') && window.stop) {window.stop();} // stop mjpeg process this.poiClicked = true; highLightPOI(this.poi_id); mmmap.updateMouseCursorLocation(); // FIXME now supports only popup_idx = 0 //var popup_idx = 0; var popup_idx = 0; //mmmap.popupinfo.length; // FIXME right now supports only one popup if (typeof(mmmap.popupinfo[popup_idx]) == 'undefined') { mmmap.popupinfo[popup_idx] = new Object(); } // save _lat, _long, ..... to this.popupinfo[this.popupinfo.length] //mmmap.popupinfo[popup_idx].lat = pointToLat(mouse_cursor_y,resolution); //mmmap.popupinfo[popup_idx].lon = pointToLong(mouse_cursor_x,resolution); var lat = this.mark_lat ? this.mark_lat : pointToLat(mouse_cursor_y,resolution); var lon = this.mark_long ? this.mark_long : pointToLong(mouse_cursor_x,resolution); mmmap.popupinfo[popup_idx].lat = lat; mmmap.popupinfo[popup_idx].lon = lon; showLocationDetailPopup(this.poi_id,this.title,lon, lat, '', popup_idx); if (this.poi_id && /^CAMERA/i.test(this.poi_id)) { mmmap.popupinfo[popup_idx].maxwidth = 355; mmmap.popupinfo[popup_idx].maxheight = 350; mmmap.popupinfo[popup_idx].wrapcontent = false; mmmap.popupinfo[popup_idx].fixpopupsize = true; } else { mmmap.popupinfo[popup_idx].wrapcontent = true; mmmap.popupinfo[popup_idx].fixpopupsize = false; mmmap.popupinfo[popup_idx].maxwidth = false; mmmap.popupinfo[popup_idx].maxheight = false; } if (window.showLocationDetail) { showLocationDetail(this.poi_id, popup_idx); } else { mmmap.showLocationDetail(this.poi_id, popup_idx); } // NOT propagate the event if (!e) var e = window.event; return cancelEvent(e); } function setSizeMapArea(map_div, w, h) { if(typeof map != 'undefined' && typeof mmmap2 == 'undefined') mmmap2 = map; if(!mmmap2) return false; if(typeof(map_div) == 'string') { map_div = document.getElementById(map_div); } if(!map_div || typeof(map_div) != 'object') return; map_div.style.width = w + 'px'; map_div.style.height = h + 'px'; mmmap2.resize(); } function hideMapTools(tool) { if(typeof map != 'undefined' && typeof mmmap2 == 'undefined') mmmap2 = map; if(!mmmap2 || !mmmap2.Ui) return false; switch(tool) { case 'centermark': if(mmmap2.Ui.Crosshair && mmmap2.Ui.Crosshair.visible) mmmap2.Ui.Crosshair.visible(false); break; case 'zoombar': if(mmmap2.Ui.Zoombar && mmmap2.Ui.Zoombar.visible) mmmap2.Ui.Zoombar.visible(false); break; case 'movingpad': if(mmmap2.Ui.DPad && mmmap2.Ui.DPad.visible) mmmap2.Ui.DPad.visible(false); break; case 'controltools': if(mmmap2.Ui.Toolbar && mmmap2.Ui.Toolbar.visible) mmmap2.Ui.Toolbar.visible(false); break; case 'mapmodeselector': if(mmmap2.Ui.LayerSelector && mmmap2.Ui.LayerSelector.visible) mmmap2.Ui.LayerSelector.visible(false); if(mmmap2.Ui.LongdoMapModeSelector && mmmap2.Ui.LongdoMapModeSelector.visible) mmmap2.Ui.LongdoMapModeSelector.visible(false); break; case 'scalebar': if(mmmap2.Ui.Scale && mmmap2.Ui.Scale.visible) mmmap2.Ui.Scale.visible(false); break; default: break; } } function isShowingTag(tagname) { if(typeof map != 'undefined' && typeof mmmap2 == 'undefined') mmmap2 = map; var tag_list = mmmap2.Tags.list(); if(tag_list) { for(var i=0; i 0) { var num_overlays = allOverlays.length; for(var i=0; i