osgEarth 2.1.1
|
00001 /* -*-c++-*- */ 00002 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph 00003 * Copyright 2008-2010 Pelican Mapping 00004 * http://osgearth.org 00005 * 00006 * osgEarth is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU Lesser General Public License as published by 00008 * the Free Software Foundation; either version 2 of the License, or 00009 * (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU Lesser General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU Lesser General Public License 00017 * along with this program. If not, see <http://www.gnu.org/licenses/> 00018 */ 00019 #include <osgEarthUtil/Formatters> 00020 #include <iomanip> 00021 #include <sstream> 00022 #include <cstdio> 00023 #include <algorithm> 00024 00025 using namespace osgEarth; 00026 using namespace osgEarth::Util; 00027 00028 #define LC "[LatLongFormatter] " 00029 00030 LatLongFormatter::LatLongFormatter(const AngularFormat& defaultFormat, 00031 unsigned options ) : 00032 _defaultFormat( defaultFormat ), 00033 _options ( options ), 00034 _prec ( 5 ) 00035 { 00036 if ( _defaultFormat == FORMAT_DEFAULT ) 00037 { 00038 _defaultFormat = FORMAT_DEGREES_MINUTES_SECONDS; 00039 } 00040 } 00041 00042 std::string 00043 LatLongFormatter::format( const Angular& angle, int precision, const AngularFormat& format ) 00044 { 00045 std::stringstream buf; 00046 std::string result; 00047 std::string space = _options & USE_SPACES ? " " : ""; 00048 00049 AngularFormat f = 00050 format == FORMAT_DEFAULT ? _defaultFormat : 00051 format; 00052 00053 if ( precision < 0 ) 00054 precision = _prec; 00055 00056 if ( precision > 0 ) 00057 buf << std::setprecision(precision); 00058 00059 switch( f ) 00060 { 00061 case FORMAT_DECIMAL_DEGREES: 00062 { 00063 if ( _options & USE_SYMBOLS ) 00064 buf << angle.as(Units::DEGREES) << "\xb0"; 00065 else 00066 buf << angle.as(Units::DEGREES); 00067 } 00068 break; 00069 00070 case FORMAT_DEGREES_DECIMAL_MINUTES: 00071 { 00072 double df = angle.as(Units::DEGREES); 00073 int d = (int)floor(df); 00074 double mf = 60.0*(df-(double)d); 00075 if ( mf == 60.0 ) { 00076 d += 1; 00077 mf = 0.0; 00078 } 00079 if ( _options & USE_SYMBOLS ) 00080 buf << d << "\xb0" << space << mf << "'"; 00081 else if ( _options & USE_COLONS ) 00082 buf << d << ":" << mf; 00083 else 00084 buf << d << " " << mf; 00085 } 00086 break; 00087 00088 case FORMAT_DEGREES_MINUTES_SECONDS: 00089 { 00090 double df = angle.as(Units::DEGREES); 00091 int d = (int)floor(df); 00092 double mf = 60.0*(df-(double)d); 00093 int m = (int)floor(mf); 00094 double sf = 60.0*(mf-(double)m); 00095 if ( sf == 60.0 ) { 00096 m += 1; 00097 sf = 0.0; 00098 if ( m == 60 ) { 00099 d += 1; 00100 m = 0; 00101 } 00102 } 00103 if ( _options & USE_SYMBOLS ) 00104 buf << d << "\xb0" << space << m << "'" << space << sf << "\""; 00105 else if ( _options & USE_COLONS ) 00106 buf << d << ":" << m << ":" << sf; 00107 else 00108 buf << d << " " << m << " " << sf; 00109 } 00110 break; 00111 } 00112 00113 result = buf.str(); 00114 return result; 00115 } 00116 00117 bool 00118 LatLongFormatter::parseAngle( const std::string& input, Angular& out_value ) 00119 { 00120 const char* c = input.c_str(); 00121 00122 double d=0.0, m=0.0, s=0.0; 00123 00124 if (sscanf(c, "%lf:%lf:%lf", &d, &m, &s) == 3 || 00125 sscanf(c, "%lf\xb0%lf'%lf\"", &d, &m, &s) == 3 || 00126 sscanf(c, "%lf\xb0 %lf' %lf\"", &d, &m ,&s) == 3 || 00127 sscanf(c, "%lfd %lf' %lf\"", &d, &m, &s) == 3 || 00128 sscanf(c, "%lfd %lfm %lfs", &d, &m, &s) == 3 || 00129 sscanf(c, "%lf %lf' %lf\"", &d, &m, &s) == 3 ) 00130 { 00131 out_value.set( d + m/60.0 + s/3600.0, Units::DEGREES ); 00132 return true; 00133 } 00134 else if ( 00135 sscanf(c, "%lf:%lf", &d, &m) == 2 || 00136 sscanf(c, "%lf\xb0 %lf'", &d, &m) == 2 || 00137 sscanf(c, "%lf\xb0%lf'", &d, &m) == 2 || 00138 sscanf(c, "%lfd %lf'", &d, &m) == 2 || 00139 sscanf(c, "%lfd %lfm", &d, &m) == 2 || 00140 sscanf(c, "%lfd%lf'", &d, &m) == 2 || 00141 sscanf(c, "%lf %lf'", &d, &m) == 2 ) 00142 { 00143 out_value.set( d + m/60.0, Units::DEGREES ); 00144 return true; 00145 } 00146 else if ( 00147 sscanf(c, "%lf\xb0", &d) == 1 || 00148 sscanf(c, "%lfd", &d) == 1 || 00149 sscanf(c, "%lf", &d) == 1 ) 00150 { 00151 out_value.set( d, Units::DEGREES ); 00152 return true; 00153 } 00154 00155 return false; 00156 } 00157 00158 //------------------------------------------------------------------------ 00159 00160 #undef LC 00161 #define LC "[MGRSFormatter] " 00162 00163 namespace 00164 { 00165 static char* GZD_ALPHABET = "CDEFGHJKLMNPQRSTUVWXX"; // 2 X's because X is a 12 degree high grid zone 00166 static char* UTM_COL_ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZ"; 00167 static char* UTM_ROW_ALPHABET = "ABCDEFGHJKLMNPQRSTUV"; 00168 static unsigned UTM_ROW_ALPHABET_SIZE = 20; 00169 00170 static char* UPS_COL_ALPHABET = "ABCFGHJKLPQRSTUXYZ"; // omit I, O, D, E, M, N, V, W 00171 static unsigned UPS_COL_ALPHABET_SIZE = 18; 00172 static char* UPS_ROW_ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZ"; // omit I, O 00173 static unsigned UPS_ROW_ALPHABET_SIZE = 24; 00174 } 00175 00176 MGRSFormatter::MGRSFormatter(Precision precision, 00177 const SpatialReference* referenceSRS, 00178 unsigned options ) : 00179 _precision( precision ), 00180 _options ( options ) 00181 { 00182 if ( referenceSRS ) 00183 { 00184 _refSRS = referenceSRS->getGeographicSRS(); 00185 } 00186 else 00187 { 00188 _refSRS = SpatialReference::create( "wgs84" ); 00189 } 00190 00191 if ( options & FORCE_AA_SCHEME ) 00192 { 00193 _useAL = false; 00194 } 00195 else if ( options & FORCE_AL_SCHEME ) 00196 { 00197 _useAL = true; 00198 } 00199 else 00200 { 00201 // use the "AL" lettering scheme for these older datum ellipsoids. 00202 std::string eName = _refSRS->getEllipsoid()->getName(); 00203 _useAL = 00204 eName.find("bessel") != std::string::npos || 00205 eName.find("clark") != std::string::npos || 00206 eName.find("clrk") != std::string::npos; 00207 } 00208 } 00209 00210 std::string 00211 MGRSFormatter::format( double latDeg, double lonDeg ) const 00212 { 00213 unsigned zone; 00214 char gzd; 00215 unsigned x=0, y=0; 00216 char sqid[3]; 00217 std::string space; 00218 00219 if ( _options & USE_SPACES ) 00220 space = " "; 00221 00222 sqid[0] = '?'; 00223 sqid[1] = '?'; 00224 sqid[2] = 0; 00225 00226 if ( latDeg >= 84.0 || latDeg <= -80.0 ) // polar projection 00227 { 00228 bool isNorth = latDeg > 0.0; 00229 zone = 0; 00230 gzd = isNorth ? (lonDeg < 0.0 ? 'Y' : 'Z') : (lonDeg < 0.0? 'A' : 'B'); 00231 00232 osg::ref_ptr<const SpatialReference> ups = isNorth? 00233 SpatialReference::create( "+proj=stere +lat_ts=90 +lat_0=90 +lon_0=0 +k_0=1 +x_0=0 +y_0=0" ) : 00234 SpatialReference::create( "+proj=stere +lat_ts=-90 +lat_0=-90 +lon_0=0 +k_0=1 +x_0=0 +y_0=0" ); 00235 00236 if ( !ups.valid() ) 00237 { 00238 OE_WARN << LC << "Failed to create UPS SRS" << std::endl; 00239 return ""; 00240 } 00241 00242 double upsX, upsY; 00243 if ( _refSRS->transform2D( lonDeg, latDeg, ups.get(), upsX, upsY ) == false ) 00244 { 00245 OE_WARN << LC << "Failed to transform lat/long to UPS" << std::endl; 00246 return ""; 00247 } 00248 00249 int sqXOffset = upsX >= 0.0 ? (int)floor(upsX/100000.0) : -(int)floor(1.0-(upsX/100000.0)); 00250 int sqYOffset = upsY >= 0.0 ? (int)floor(upsY/100000.0) : -(int)floor(1.0-(upsY/100000.0)); 00251 00252 int alphaOffset = isNorth ? 7 : 12; 00253 00254 sqid[0] = UPS_COL_ALPHABET[ (UPS_COL_ALPHABET_SIZE+sqXOffset) % UPS_COL_ALPHABET_SIZE ]; 00255 sqid[1] = UPS_ROW_ALPHABET[alphaOffset + sqYOffset]; 00256 00257 x = upsX - (100000.0*(double)sqXOffset); 00258 y = upsY - (100000.0*(double)sqYOffset); 00259 } 00260 00261 else // UTM 00262 { 00263 // figure out the grid zone designator 00264 unsigned gzdIndex = ((unsigned)(latDeg+80.0))/8; 00265 gzd = GZD_ALPHABET[gzdIndex]; 00266 00267 // figure out the UTM zone: 00268 zone = (unsigned)floor((lonDeg+180.0)/6.0); // [0..59] 00269 bool north = latDeg >= 0.0; 00270 00271 // convert the input coordinates to UTM: 00272 // yes, always use +north so we get Y relative to equator 00273 std::stringstream buf; 00274 buf << "+proj=utm +zone=" << (zone+1) << " +north +units=m"; 00275 osg::ref_ptr<SpatialReference> utm = SpatialReference::create( buf.str() ); 00276 00277 double utmX, utmY; 00278 if ( _refSRS->transform2D( lonDeg, latDeg, utm.get(), utmX, utmY ) == false ) 00279 { 00280 OE_WARN << LC << "Error transforming lat/long into UTM" << std::endl; 00281 return ""; 00282 } 00283 00284 // the alphabet set: 00285 unsigned set = zone % 6; // [0..5] 00286 00287 // find the horizontal SQID offset (100KM increments) from the central meridian: 00288 unsigned xSetOffset = 8 * (set % 3); 00289 double xMeridianOffset = utmX - 500000.0; 00290 int sqMeridianOffset = xMeridianOffset >= 0.0 ? (int)floor(xMeridianOffset/100000.0) : -(int)floor(1.0-(xMeridianOffset/100000.0)); 00291 unsigned indexOffset = (4 + sqMeridianOffset); 00292 sqid[0] = UTM_COL_ALPHABET[xSetOffset + indexOffset]; 00293 double xWest = 500000.0 + (100000.0*(double)sqMeridianOffset); 00294 x = utmX - xWest; 00295 00296 // find the vertical SQID offset (100KM increments) from the equator: 00297 unsigned ySetOffset = 5 * (zone % 2); //(set % 2); 00298 int sqEquatorOffset = (int)floor(utmY/100000.0); 00299 int absOffset = ySetOffset + sqEquatorOffset + (10 * UTM_ROW_ALPHABET_SIZE); 00300 if ( _useAL ) 00301 absOffset += 10; 00302 sqid[1] = UTM_ROW_ALPHABET[absOffset % UTM_ROW_ALPHABET_SIZE]; 00303 y = utmY - (100000.0*(double)sqEquatorOffset); 00304 } 00305 00306 std::stringstream buf; 00307 00308 if ( (unsigned)_precision > PRECISION_1M ) 00309 { 00310 x /= (unsigned)_precision; 00311 y /= (unsigned)_precision; 00312 } 00313 00314 buf << (zone+1) << gzd << space << sqid; 00315 00316 if ( (unsigned)_precision < PRECISION_100000M ) 00317 { 00318 int sigdigs = 00319 _precision == PRECISION_10000M ? 1 : 00320 _precision == PRECISION_1000M ? 2 : 00321 _precision == PRECISION_100M ? 3 : 00322 _precision == PRECISION_10M ? 4 : 00323 5; 00324 00325 buf << space 00326 << std::setfill('0') 00327 << std::setw(sigdigs) << x 00328 << space 00329 << std::setw(sigdigs) << y; 00330 } 00331 00332 std::string result; 00333 result = buf.str(); 00334 return result; 00335 }