Created
June 7, 2016 01:37
-
-
Save dbazile/8a328e139fa824f2634016db2515dafc to your computer and use it in GitHub Desktop.
Super old coordinate utility thingy I needed way back when.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Coordinate.js | |
* | |
* Utility for performing on-the-fly coordinate conversion | |
*/ | |
(function($, geo) { | |
var Coordinate, | |
CoordinateTextbox, | |
pad = function(input, depth) { return ("0000000000" + input).slice(-depth); }, | |
deg2rad = function (n) { return (n / 180.0 * Math.PI); }, | |
rad2deg = function (n) { return (n / Math.PI * 180.0); }, | |
truncateFloat = function(n, precision) { return parseFloat(parseInt(n * Math.pow(10, precision)) / Math.pow(10, precision)); }, | |
patterns = { | |
DMS_PREFIX: /^([NS])(\d{2})(\d{2})(\d{2})\s*([EW])(\d{2,3})(\d{2})(\d{2})$/i | |
,DMS_POSTFIX: /^(\d{2})(\d{2})(\d{2})([NS])\s*(\d{2,3})(\d{2})(\d{2})([EW])$/i | |
,DECIMAL: /^(-?\d{1,2}\.?\d*),\s*(-?\d{1,2}\.?\d*)$/ | |
,MGRS: /^(\d{1,2})([C-HJ-NP-X])\s*([A-HJ-NP-Z])([A-HJ-NP-V])\s*(\d{1,5}\s*\d{1,5})$/i | |
,UTM_COMMA_PREFIX: /^(\d{1,2})([NS]),?\s*(\d{5,6}\.?\d*),\s*(\d{1,7}\.?\d*)$/i | |
,UTM_SPACE_PREFIX: /^(\d{1,2})([NS])\s+(\d{5,6}\.?\d*)\s+(\d{1,7}\.?\d*)$/i | |
,UTM_COMMA_POSTFIX: /^(\d{5,6}\.?\d*),\s*(\d{1,7}\.?\d*),?\s*(\d{1,2})([NS])$/i | |
,UTM_SPACE_POSTFIX: /^(\d{5,6}\.?\d*)\s+(\d{1,7}\.?\d*)\s+(\d{1,2})([NS])$/i | |
}, | |
templates = { | |
DMS: "{{lat_hours}}{{lat_minutes}}{{lat_seconds}}{{lat_hemisphere}}{{lon_hours}}{{lon_minutes}}{{lon_seconds}}{{lon_hemisphere}}", | |
TOOLTIP_INVALIDCOORD: '<div class="coordinatebox-tooltip-invalid"><strong>Invalid coordinates</strong><p>Examples of valid coordinates are: <span>Decimal: <code>30, 30</code></span><span>DMS: <code>300000N0300000E</code></span><span>MGRS: <code>36Q AR 00000000</code></span><span>UTM: <code>30N 00000,00000</code></span></p></div>' | |
}; | |
/** | |
* Model for Coordinates | |
* | |
* @param {float} latitude | |
* @param {float} longitude | |
*/ | |
Coordinate = function(latitude, longitude) { | |
var invalidConditions = []; | |
latitude = parseFloat(latitude); | |
longitude = parseFloat(longitude); | |
// Validate the inputs | |
isNaN(latitude) && invalidConditions.push("Latitude is not a number"); | |
isNaN(longitude) && invalidConditions.push("Longitude is not a number"); | |
Math.abs(latitude) > 90 && invalidConditions.push("Latitude must be between -90 and 90"); | |
Math.abs(longitude) > 180 && invalidConditions.push("Longitude must be between -180 and 180"); | |
if (invalidConditions.length) { | |
throw new Error("Could not create coordinate: " + invalidConditions.join("; ")); | |
} | |
// Continue with construction | |
this.latitude = truncateFloat(latitude, 6); | |
this.longitude = truncateFloat(longitude, 6); | |
}; | |
/** | |
* Returns a string representation of a Coordinate object | |
* | |
* @returns {String} | |
*/ | |
Coordinate.prototype.toString = function() { | |
return Coordinate.formatDms(this); | |
}; | |
/** | |
* Returns a DMS-formatted string for a given coordinate object | |
* | |
* @param {Coordinate} coord Has fields: latitude, longitude | |
* @returns {String} | |
*/ | |
Coordinate.formatDms = function(coord) { | |
var buffer = templates.DMS, | |
values = {}, | |
latitudeDecimal, | |
longitudeDecimal; | |
latitudeDecimal = coord.latitude; | |
longitudeDecimal = coord.longitude; | |
// Determine the hemispheres | |
values.lat_hemisphere = (latitudeDecimal >= 0) ? 'N' : 'S'; | |
values.lon_hemisphere = (longitudeDecimal >= 0) ? 'E' : 'W'; | |
latitudeDecimal = Math.abs(latitudeDecimal); | |
longitudeDecimal = Math.abs(longitudeDecimal); | |
values.lat_hours = pad(Math.floor(latitudeDecimal), 2); | |
values.lat_minutes = pad(Math.floor(latitudeDecimal * 60) % 60, 2); | |
values.lat_seconds = pad(Math.floor(latitudeDecimal * 3600) % 60, 2); | |
values.lon_hours = pad(Math.floor(longitudeDecimal), 3); | |
values.lon_minutes = pad(Math.floor(longitudeDecimal * 60) % 60, 2); | |
values.lon_seconds = pad(Math.floor(longitudeDecimal * 3600) % 60, 2); | |
for (var name in values) { | |
buffer = buffer.replace("{{" + name + "}}", values[name]); | |
} | |
return buffer; | |
}; | |
/** | |
* Creates a Coordinate from decimal inputs | |
* | |
* @param {float} latitude | |
* @param {float} longitude | |
* @returns {Coordinate} | |
*/ | |
Coordinate.fromDecimal = function(latitude, longitude) { | |
return new Coordinate(latitude, longitude); | |
}; | |
/** | |
* Creates a Coordinate from DMS inputs | |
* | |
* @param {char} latitudeHemisphere 'N' or 'S' | |
* @param {int} latitudeHours | |
* @param {int} latitudeMinutes | |
* @param {int} latitudeSeconds | |
* @param {char} longitudeHemisphere 'E' or 'W' | |
* @param {int} longitudeHours | |
* @param {int} longitudeMinutes | |
* @param {int} longitudeSeconds | |
* @returns {Coordinate} | |
*/ | |
Coordinate.fromDms = function(latitudeHemisphere, latitudeHours, latitudeMinutes, latitudeSeconds, longitudeHemisphere, longitudeHours, longitudeMinutes, longitudeSeconds) { | |
var latitudeDecimal, | |
longitudeDecimal; | |
// Calculate the latitude | |
latitudeDecimal = parseInt(latitudeHours); | |
latitudeDecimal += parseInt(latitudeMinutes) / 60.0; | |
latitudeDecimal += parseInt(latitudeSeconds) / 3600.0; | |
latitudeDecimal *= ('S' === latitudeHemisphere) ? -1.0 : 1.0; | |
longitudeDecimal = parseInt(longitudeHours); | |
longitudeDecimal += parseInt(longitudeMinutes) / 60.0; | |
longitudeDecimal += parseInt(longitudeSeconds) / 3600.0; | |
longitudeDecimal *= ('W' === longitudeHemisphere) ? -1.0 : 1.0; | |
return new Coordinate(latitudeDecimal, longitudeDecimal); | |
}; | |
/** | |
* Creates a Coordinate from MGRS inputs | |
* | |
* @param {int} zone | |
* @param {char} latitudeBand | |
* @param {char} column | |
* @param {char} row | |
* @param {int} easting | |
* @param {int} northing | |
* @returns {Coordinate} | |
*/ | |
Coordinate.fromMgrs = function(zone, latitudeBand, column, row, easting, northing) { | |
var EASTING_IDS = "ABCDEFGHJKLMNPQRSTUVWXYZ", | |
NORTHING_IDS = "ABCDEFGHJKLMNPQRSTUV", | |
utmEasting, | |
utmNorthing, | |
hemisphere, | |
set, eastingIndex, northingIndex, northingModifier, eastingModifier, northingOffsets, minimumNorthing; | |
// Normalize the inputs | |
zone = parseInt(zone); | |
latitudeBand = latitudeBand.toUpperCase().replace(/\s+/g, ''); | |
column = column.toUpperCase().replace(/\s+/g, ''); | |
row = row.toUpperCase().replace(/\s+/g, ''); | |
easting = parseInt(easting); | |
northing = parseInt(northing); | |
/* | |
* MGRS TO UTM FORMULA | |
* | |
* Source: | |
* https://github.com/barryhunter/kml-mgrs-gridlines/blob/master/grid_mgrs/phpcoord-mgrs.php | |
* | |
* License: | |
* GPL | |
* | |
* Notes: | |
* 2014-04-18 - After a lot of fruitless research on MGRS-to-decimal, it | |
* looks like the strategy I've found to perform the conversion is | |
* to go from MGRS -> UTM -> Decimal. That is the strategy used | |
* here. | |
* | |
* The original source code has been modified in an attempt to add | |
* clarity to what is actually happening. | |
*/ | |
set = ((zone - 1) % 6) + 1; | |
eastingIndex = EASTING_IDS.indexOf(column) + 1; | |
eastingModifier = (eastingIndex - (8 * ((set - 1) % 3)) ) * 100000.0; | |
northingIndex = NORTHING_IDS.indexOf(row); | |
// Northing ID offset for sets 2, 4 and 6 | |
if (set % 2 == 0) { | |
northingIndex -= 5; | |
} | |
if (northingIndex < 0) { | |
northingIndex += 20; | |
} | |
northingModifier = northingIndex * 100000.0; | |
northingModifier = (northingModifier % 1000000) * 1.0; | |
northingOffsets = { | |
"C": 1100000.0, | |
"D": 2000000.0, | |
"E": 2800000.0, | |
"F": 3700000.0, | |
"G": 4600000.0, | |
"H": 5500000.0, | |
"J": 6400000.0, | |
"K": 7300000.0, | |
"L": 8200000.0, | |
"M": 9100000.0, | |
"N": 0.0, | |
"P": 800000.0, | |
"Q": 1700000.0, | |
"R": 2600000.0, | |
"S": 3500000.0, | |
"T": 4400000.0, | |
"U": 5300000.0, | |
"V": 6200000.0, | |
"W": 7000000.0, | |
"X": 7900000.0 | |
}; | |
minimumNorthing = northingOffsets[latitudeBand]; | |
while(northingModifier < minimumNorthing) { | |
northingModifier += 1000000; | |
} | |
hemisphere = NORTHING_IDS.indexOf(latitudeBand) >= NORTHING_IDS.indexOf("N")? "N" : "S"; | |
utmEasting = eastingModifier + easting; | |
utmNorthing = northingModifier + northing; | |
return Coordinate.fromUtm(zone, hemisphere, utmEasting, utmNorthing); | |
}; | |
/** | |
* Creates a Coordinate from UTM inputs | |
* | |
* @param {int} zone | |
* @param {char} hemisphere | |
* @param {float} easting | |
* @param {float} northing | |
* @returns {Coordinate} | |
*/ | |
Coordinate.fromUtm = function(zone, hemisphere, easting, northing) { | |
var SCALE_FACTOR = 0.9996, | |
WGS84_ELLIPSOID_MAJAXIS = 6378137.0, | |
WGS84_ELLIPSOID_MINAXIS = 6356752.314, | |
latitudeDecimal, | |
longitudeDecimal, | |
centralMeridian, x, y, n, y_, alpha_, beta_, gamma_, delta_, epsilon_, | |
phif, ep2, cf, nuf2, Nf, Nfpow, tf, tf2, tf4, x1frac, x2frac, x3frac, | |
x4frac, x5frac, x6frac, x7frac, x8frac, x2poly, x3poly, x4poly, x5poly, | |
x6poly, x7poly, x8poly; | |
// Normalize the inputs | |
zone = parseInt(zone); | |
hemisphere = hemisphere.toUpperCase().replace(/\s+/g, ''); | |
easting = parseFloat(easting); | |
northing = parseFloat(northing); | |
/* | |
* UTM XY TO DECIMAL FORMULA | |
* | |
* Source: | |
* http://home.hiwaay.net/~taylorc/toolbox/geography/geoutm.html | |
* | |
* License: | |
* None | |
* | |
* Notes: | |
* 2014-04-17 - This formula does not handle corner cases very well. | |
* Attempting to parse the UTM-equivalent of the following | |
* coordinates will fail spectacularly: | |
* | |
* - N90.0 W180.0 | |
* - S90.0 E180.0 | |
* | |
* Not that anyone would actually use those coordinates (and I'm not | |
* even sure they are valid in the first place), but this is | |
* something to be aware of. | |
*/ | |
centralMeridian = deg2rad(-183 + (zone * 6)); | |
// Calculate X, Y values from eastern and northing | |
x = (parseFloat(easting) - 500000.0) / SCALE_FACTOR; | |
y = ('S' === hemisphere) | |
? (parseFloat(northing) - 10000000.0) / SCALE_FACTOR | |
: parseFloat(northing) / SCALE_FACTOR; | |
// Hold on to your freakin hat because here we go | |
n = (WGS84_ELLIPSOID_MAJAXIS - WGS84_ELLIPSOID_MINAXIS) / (WGS84_ELLIPSOID_MAJAXIS + WGS84_ELLIPSOID_MINAXIS); | |
/* Precalculate alpha_ (Eq. 10.22) */ | |
/* (Same as alpha in Eq. 10.17) */ | |
alpha_ = ((WGS84_ELLIPSOID_MAJAXIS + WGS84_ELLIPSOID_MINAXIS) / 2.0) | |
* (1 + (Math.pow(n, 2.0) / 4) + (Math.pow(n, 4.0) / 64)); | |
/* Precalculate y_ (Eq. 10.23) */ | |
y_ = y / alpha_; | |
/* Precalculate beta_ (Eq. 10.22) */ | |
beta_ = (3.0 * n / 2.0) + (-27.0 * Math.pow(n, 3.0) / 32.0) | |
+ (269.0 * Math.pow(n, 5.0) / 512.0); | |
/* Precalculate gamma_ (Eq. 10.22) */ | |
gamma_ = (21.0 * Math.pow(n, 2.0) / 16.0) | |
+ (-55.0 * Math.pow(n, 4.0) / 32.0); | |
/* Precalculate delta_ (Eq. 10.22) */ | |
delta_ = (151.0 * Math.pow(n, 3.0) / 96.0) | |
+ (-417.0 * Math.pow(n, 5.0) / 128.0); | |
/* Precalculate epsilon_ (Eq. 10.22) */ | |
epsilon_ = (1097.0 * Math.pow(n, 4.0) / 512.0); | |
/* Now calculate the sum of the series (Eq. 10.21) */ | |
phif = y_ + (beta_ * Math.sin (2.0 * y_)) // referred to as "footpoint latitude" | |
+ (gamma_ * Math.sin (4.0 * y_)) | |
+ (delta_ * Math.sin (6.0 * y_)) | |
+ (epsilon_ * Math.sin (8.0 * y_)); | |
// Thought that was bad? Here, have some more! | |
/* Precalculate ep2 */ | |
ep2 = (Math.pow(WGS84_ELLIPSOID_MAJAXIS, 2.0) - Math.pow(WGS84_ELLIPSOID_MINAXIS, 2.0)) | |
/ Math.pow(WGS84_ELLIPSOID_MINAXIS, 2.0); | |
/* Precalculate cos (phif) */ | |
cf = Math.cos(phif); | |
/* Precalculate nuf2 */ | |
nuf2 = ep2 * Math.pow(cf, 2.0); | |
/* Precalculate Nf and initialize Nfpow */ | |
Nf = Math.pow(WGS84_ELLIPSOID_MAJAXIS, 2.0) / (WGS84_ELLIPSOID_MINAXIS * Math.sqrt(1 + nuf2)); | |
Nfpow = Nf; | |
/* Precalculate tf */ | |
tf = Math.tan(phif); | |
tf2 = tf * tf; | |
tf4 = tf2 * tf2; | |
/* Precalculate fractional coefficients for x**n in the equations | |
below to simplify the expressions for latitude and longitude. */ | |
x1frac = 1.0 / (Nfpow * cf); | |
Nfpow *= Nf; /* now equals Nf**2) */ | |
x2frac = tf / (2.0 * Nfpow); | |
Nfpow *= Nf; /* now equals Nf**3) */ | |
x3frac = 1.0 / (6.0 * Nfpow * cf); | |
Nfpow *= Nf; /* now equals Nf**4) */ | |
x4frac = tf / (24.0 * Nfpow); | |
Nfpow *= Nf; /* now equals Nf**5) */ | |
x5frac = 1.0 / (120.0 * Nfpow * cf); | |
Nfpow *= Nf; /* now equals Nf**6) */ | |
x6frac = tf / (720.0 * Nfpow); | |
Nfpow *= Nf; /* now equals Nf**7) */ | |
x7frac = 1.0 / (5040.0 * Nfpow * cf); | |
Nfpow *= Nf; /* now equals Nf**8) */ | |
x8frac = tf / (40320.0 * Nfpow); | |
/* Precalculate polynomial coefficients for x**n. | |
-- x**1 does not have a polynomial coefficient. */ | |
x2poly = -1.0 - nuf2; | |
x3poly = -1.0 - 2 * tf2 - nuf2; | |
x4poly = 5.0 + 3.0 * tf2 + 6.0 * nuf2 - 6.0 * tf2 * nuf2 | |
- 3.0 * (nuf2 * nuf2) - 9.0 * tf2 * (nuf2 * nuf2); | |
x5poly = 5.0 + 28.0 * tf2 + 24.0 * tf4 + 6.0 * nuf2 + 8.0 * tf2 * nuf2; | |
x6poly = -61.0 - 90.0 * tf2 - 45.0 * tf4 - 107.0 * nuf2 | |
+ 162.0 * tf2 * nuf2; | |
x7poly = -61.0 - 662.0 * tf2 - 1320.0 * tf4 - 720.0 * (tf4 * tf2); | |
x8poly = 1385.0 + 3633.0 * tf2 + 4095.0 * tf4 + 1575 * (tf4 * tf2); | |
/* Calculate latitude */ | |
latitudeDecimal = rad2deg(phif | |
+ x2frac * x2poly * (x * x) | |
+ x4frac * x4poly * Math.pow(x, 4.0) | |
+ x6frac * x6poly * Math.pow(x, 6.0) | |
+ x8frac * x8poly * Math.pow(x, 8.0)); | |
/* Calculate longitude */ | |
longitudeDecimal = rad2deg(centralMeridian | |
+ x1frac * x | |
+ x3frac * x3poly * Math.pow (x, 3.0) | |
+ x5frac * x5poly * Math.pow (x, 5.0) | |
+ x7frac * x7poly * Math.pow (x, 7.0)); | |
return Coordinate.fromDecimal(latitudeDecimal, longitudeDecimal); | |
}; | |
/** | |
* Attempts to parse a string of any format into a Coordinate object | |
* | |
* @param {String} input | |
* @returns {Coordinate} | |
*/ | |
Coordinate.parseAny = function(input) { | |
var coordinate; | |
input = input.toUpperCase().replace(/[^A-Z0-9.,\- ]+/g, ''); | |
if (Coordinate.stringIsDms(input)) { | |
coordinate = Coordinate.parseDms(input); | |
} else if(Coordinate.stringIsMgrs(input)) { | |
coordinate = Coordinate.parseMgrs(input); | |
} else if(Coordinate.stringIsUtm(input)) { | |
coordinate = Coordinate.parseUtm(input); | |
} else if(Coordinate.stringIsDecimal(input)) { | |
coordinate = Coordinate.parseDecimal(input); | |
} else { | |
throw new Error("Invalid coordinate format: " + input); | |
} | |
return coordinate; | |
}; | |
/** | |
* Parse a decimal-formatted string into a Coordinate | |
* | |
* @param {String} input Example: "30.123, 30.456" | |
* @returns {Coordinate} | |
*/ | |
Coordinate.parseDecimal = function(input) { | |
var chunks = input.split(','), | |
latitude = chunks[0], | |
longitude = chunks[1]; | |
return Coordinate.fromDecimal(latitude, longitude); | |
}; | |
/** | |
* Parse a DMS-formatted string into a Coordinate object | |
* | |
* @param {String} input Example: "300000N0300000E" or "N300000 E0300000" | |
* @returns {Coordinate} | |
*/ | |
Coordinate.parseDms = function(input) { | |
var latitudeHemisphere, | |
latitudeHours, | |
latitudeMinutes, | |
latitudeSeconds, | |
longitudeHemisphere, | |
longitudeHours, | |
longitudeMinutes, | |
longitudeSeconds, | |
chunks; | |
// Normalize the input string | |
input = input.replace(/\W+/g, '').toUpperCase(); | |
// Get the values from both post and prefixed hemispheres | |
if (chunks = input.match(patterns.DMS_POSTFIX)) { | |
latitudeHours = chunks[1]; | |
latitudeMinutes = chunks[2]; | |
latitudeSeconds = chunks[3]; | |
latitudeHemisphere = chunks[4]; | |
longitudeHours = chunks[5]; | |
longitudeMinutes = chunks[6]; | |
longitudeSeconds = chunks[7]; | |
longitudeHemisphere = chunks[8]; | |
} else if (chunks = input.match(patterns.DMS_PREFIX)) { | |
// Less common but equally valid(?) | |
latitudeHemisphere = chunks[1]; | |
latitudeHours = chunks[2]; | |
latitudeMinutes = chunks[3]; | |
latitudeSeconds = chunks[4]; | |
longitudeHemisphere = chunks[5]; | |
longitudeHours = chunks[6]; | |
longitudeMinutes = chunks[7]; | |
longitudeSeconds = chunks[8]; | |
} else { | |
throw new Error("Could not parse as DMS: " + input); | |
} | |
return Coordinate.fromDms( | |
latitudeHemisphere, latitudeHours, latitudeMinutes, latitudeSeconds, | |
longitudeHemisphere, longitudeHours, longitudeMinutes, longitudeSeconds); | |
}; | |
/** | |
* Parse an MGRS-formatted string into a Coordinate object | |
* | |
* @param {String} input Example: "30QDB00000000" or "30QDB 00000000" | |
* @returns {Coordinate} | |
*/ | |
Coordinate.parseMgrs = function(input) { | |
var chunks = input.match(patterns.MGRS).slice(1, 7), | |
zone = chunks[0], | |
latitudeBand = chunks[1], | |
column = chunks[2], | |
row = chunks[3], | |
location = chunks[4], | |
easting, | |
northing; | |
// Validate the inputs | |
if (location.length % 2 === 0) { | |
easting = location.substring(0, location.length / 2); | |
northing = location.substring(location.length / 2); | |
return Coordinate.fromMgrs(zone, latitudeBand, column, row, easting, northing); | |
} else { | |
throw new Error("Invalid MGRS: location must contain even number of digits"); | |
} | |
}; | |
/** | |
* Parse a UTM-formatted string into a Coordinate object | |
* | |
* @param {String} input Example: "30T 00000,000000" or "30T 00000 000000" or "00000, 000000 30T" | |
* @returns {Coordinate} | |
*/ | |
Coordinate.parseUtm = function(input) { | |
var chunks, | |
zone, | |
hemisphere, | |
easting, | |
northing; | |
// Parse and normalize the input segments | |
chunks = input.match(patterns.UTM_SPACE_PREFIX) | |
|| input.match(patterns.UTM_COMMA_PREFIX) | |
|| input.match(patterns.UTM_SPACE_POSTFIX) | |
|| input.match(patterns.UTM_COMMA_POSTFIX); | |
chunks = chunks.slice(1,5); | |
if (chunks[0].match(/\d+/) && chunks[1].match(/[NS]/i)) { | |
// PREFIXED HEMISPHERE | |
zone = chunks[0]; | |
hemisphere = chunks[1]; | |
easting = chunks[2]; | |
northing = chunks[3]; | |
} else { | |
// POSTFIXED HEMISPHERE | |
easting = chunks[0]; | |
northing = chunks[1]; | |
zone = chunks[2]; | |
hemisphere = chunks[3]; | |
} | |
return Coordinate.fromUtm(zone, hemisphere, easting, northing); | |
}; | |
/** | |
* Tests if a given string is decimal-formatted | |
* | |
* @param {String} input | |
* @returns {Boolean} | |
*/ | |
Coordinate.stringIsDecimal = function(input) { | |
return patterns.DECIMAL.test(input); | |
}; | |
/** | |
* Tests if a given string is DMS-formatted | |
* | |
* @param {String} input | |
* @returns {Boolean} | |
*/ | |
Coordinate.stringIsDms = function(input) { | |
return patterns.DMS_POSTFIX.test(input) | |
|| patterns.DMS_PREFIX.test(input); | |
}; | |
/** | |
* Tests if a given string is DMS-formatted | |
* | |
* @param {String} input | |
* @returns {Boolean} | |
*/ | |
Coordinate.stringIsMgrs = function(input) { | |
return patterns.MGRS.test(input); | |
}; | |
/** | |
* Tests if a given string is UTM-formatted | |
* | |
* @param {String} input | |
* @returns {Boolean} | |
*/ | |
Coordinate.stringIsUtm = function(input) { | |
return patterns.UTM_SPACE_PREFIX.test(input) | |
|| patterns.UTM_COMMA_PREFIX.test(input) | |
|| patterns.UTM_SPACE_POSTFIX.test(input) | |
|| patterns.UTM_COMMA_POSTFIX.test(input); | |
}; | |
/** | |
* Creates a widget that will detect valid and invalid coordinates | |
* | |
* @param {String} selector | |
*/ | |
CoordinateTextbox = function(selector) { | |
var $element = $(selector), | |
$tooltip = $(templates.TOOLTIP_INVALIDCOORD), | |
events = {}, | |
showTooltip = function() { | |
$tooltip.show(); | |
}, | |
hideTooltip = function() { | |
$tooltip.hide(); | |
}; | |
/** | |
* Executes when the input box loses focus | |
* @returns {void} | |
*/ | |
events.element_OnBlur = function() { | |
var formattedValue = $element.data("formatted-value"); | |
hideTooltip(); | |
if (formattedValue) { | |
$element.val(formattedValue); | |
$element.removeData("formatted-value"); | |
$element.removeClass("invalid valid"); | |
} | |
}; | |
/** | |
* Executes when the input box gains focus | |
* @returns {void} | |
*/ | |
events.element_OnFocus = function() { | |
// Show the tooltip if the data is still invalid | |
if ($element.hasClass("invalid")) { | |
showTooltip(); | |
} | |
}; | |
/** | |
* Executes whenever data is entered into the input box in realtime | |
* @param {event} event | |
* @returns {void} | |
*/ | |
events.element_OnModified = function(event) { | |
var currentValue = $element.val(), | |
coordinate; | |
// Reset the state | |
$element.removeClass("valid invalid"); | |
$element.removeData("formatted-value"); | |
if (currentValue) { | |
try { | |
// Attempt to generate a coordinate | |
coordinate = geo.Coordinate.parseAny(currentValue); | |
$element | |
.data("formatted-value", coordinate.toString()) | |
.addClass("valid"); | |
hideTooltip(); | |
} catch (e) { | |
// If coordinate generation fails, the inputs were invalid | |
$element | |
.removeData("formatted-value") | |
.addClass("invalid"); | |
showTooltip(); | |
} | |
} | |
}; | |
// Tag the coordinate input box with the UI styling | |
$element.addClass("coordinatebox"); | |
// Attach the "invalid coordinate" tooltip to the document | |
$tooltip.appendTo("body"); | |
$tooltip.position({my: "left bottom", at: "left top", of: $element}); | |
$element | |
.on("change", events.element_OnModified) | |
.on("keypress", events.element_OnModified) | |
.on("keyup", events.element_OnModified) | |
.on("focus", events.element_OnFocus) | |
.on("blur", events.element_OnBlur); | |
}; | |
// Append the classes to the namespace | |
geo.Coordinate = Coordinate; | |
geo.CoordinateTextbox = CoordinateTextbox; | |
})(window.jQuery, window.geo); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment