osgEarth 2.1.1
|
00001 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph 00002 * Copyright 2010 Pelican Ventures, Inc. 00003 * http://osgearth.org 00004 * 00005 * osgEarth is free software; you can redistribute it and/or modify 00006 * it under the terms of the GNU Lesser General Public License as published by 00007 * the Free Software Foundation; either version 2 of the License, or 00008 * (at your option) any later version. 00009 * 00010 * This program is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 * GNU Lesser General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU Lesser General Public License 00016 * along with this program. If not, see <http://www.gnu.org/licenses/> 00017 */ 00018 00019 #include "Euler" 00020 00021 #include <algorithm> 00022 #include <vector> 00023 00024 #include <osg/Math> 00025 #include <osg/CoordinateSystemNode> 00026 00027 #include <osgEarth/Registry> 00028 #include <ogr_api.h> 00029 #include <ogr_spatialref.h> 00030 00031 #include "AutoBuffer" 00032 00033 #define LC "[seamless::EULER] " 00034 00035 namespace seamless 00036 { 00037 using namespace std; 00038 using namespace osg; 00039 using namespace osgEarth; 00040 00041 // Constants used througout 00042 00043 const double PiOver12 = PI / 12.0; 00044 const double PiOver12Inv = 12.0 / PI; 00045 const double sqrt2 = 1.41421356237309504880; // sqrt(2) 00046 const double sqrt33 = .5773502691896258; // sqrt(3) / 3 00047 00048 namespace euler 00049 { 00050 bool lineLineIntersect( 00051 const Vec3d& p1, const Vec3d& p2, const Vec3d& p3, const Vec3d& p4, 00052 Vec3d& pa,Vec3d& pb, double& mua, double& mub); 00053 00054 // Convert lat, lon to a unit vector on a sphere (direction cosine) 00055 00056 inline Vec3d latLon2xyz(double lat_deg, double lon_deg) 00057 { 00058 Vec3d result; 00059 result.z() = sin(DegreesToRadians(lat_deg)); 00060 double hyp = sqrt(1 - result.z() * result.z()); 00061 double long_rad = DegreesToRadians(lon_deg); 00062 result.x() = hyp * cos(long_rad); 00063 result.y() = hyp * sin(long_rad); 00064 return result; 00065 } 00066 00067 // On a boundary, return the lower-numbered face. 00068 inline int getFace(const Vec3d& vec) 00069 { 00070 double absx = fabs(vec.x()); 00071 double absy = fabs(vec.y()); 00072 double absz = fabs(vec.z()); 00073 // pole faces 00074 if (absz > (absx + 1e-11) && absz > (absy + 1e-11)) 00075 { 00076 if (vec.z() > 0.0) 00077 return 4; 00078 else 00079 return 5; 00080 } 00081 // One of the X faces, unless on a border 00082 else if (absx > absy || equivalent(absx, absy, 1e-11)) 00083 { 00084 if (vec.x() > 0.0) 00085 return 0; 00086 else if (equivalent(vec.x(), -vec.y(), 1e-11)) 00087 return 1; // Boundary between 1 and 2 00088 else 00089 return 2; 00090 } 00091 // One of the Y faces 00092 else 00093 { 00094 if (vec.y() > 0.0) 00095 return 1; 00096 else 00097 return 3; 00098 } 00099 } 00100 00101 // Convert unit vector to coordinates on a face. q is normal to the 00102 // face. r and s correspond to x and y face coordinates. q is stored 00103 // in the z member of Vec3d so that the r,s,q form a nice orthogonal 00104 // coordinate system. 00105 00106 Vec3d xyz2qrs(const Vec3d& xyz, int face) 00107 { 00108 switch (face) 00109 { 00110 case 0: 00111 return Vec3d(xyz.y(), xyz.z(), xyz.x()); 00112 case 1: 00113 return Vec3d(-xyz.x(), xyz.z(), xyz.y()); 00114 case 2: 00115 return Vec3d(-xyz.y(), xyz.z(), -xyz.x()); 00116 case 3: 00117 return Vec3d(xyz.x(), xyz.z(), -xyz.y()); 00118 case 4: 00119 return Vec3d(xyz.y(), -xyz.x(), xyz.z()); 00120 case 5: 00121 return Vec3d(xyz.y(), xyz.x(), -xyz.z()); 00122 default: 00123 return Vec3d(0, 0, 0); 00124 } 00125 } 00126 00127 bool latLonToFaceCoords(double lat_deg, double lon_deg, 00128 double& out_x, double& out_y, int& out_face, 00129 int faceHint) 00130 { 00131 if (lat_deg > 90.0 || lat_deg < -90.0 00132 || lon_deg < -180.0 || lon_deg > 180.0) 00133 return false; 00134 Vec3d xyz = latLon2xyz(lat_deg, lon_deg); 00135 out_face = faceHint >= 0 ? faceHint : getFace(xyz); 00136 Vec3d qrs = xyz2qrs(xyz, out_face); 00137 00138 double xang = atan2(qrs[0], qrs[2]); 00139 double yang = atan2(qrs[1], qrs[2]); 00140 out_x = xang / osg::PI_4; 00141 out_y = yang / osg::PI_4; 00142 return true; 00143 } 00144 00145 Vec3d face2qrs(const Vec2d& face) 00146 { 00147 double xang = face[0] * osg::PI_4; 00148 double yang = face[1] * osg::PI_4; 00149 double sx = sin(xang), cx = cos(xang); 00150 double ty = tan(yang); 00151 // phi is a latitude measure, in the longitudinal plane 00152 double tanPhi = cx * ty; 00153 double radical = sqrt(1 + tanPhi * tanPhi); 00154 double c = 1.0 / radical; 00155 Vec3d result; 00156 double b = tanPhi * c; // b gets the right sign 00157 result.x() = c * sx; 00158 result.y() = b; 00159 result.z() = c * cx; 00160 return result; 00161 } 00162 00163 Vec3d qrs2xyz(const Vec3d& local, int face) 00164 { 00165 switch (face) 00166 { 00167 case 0: 00168 return Vec3d(local.z(), local.x(), local.y()); 00169 break; 00170 case 1: 00171 return Vec3d(-local.x(), local.z(), local.y()); 00172 break; 00173 case 2: 00174 return Vec3d(-local.z(), -local.x(), local.y()); 00175 case 3: 00176 return Vec3d(local.x(), -local.z(), local.y()); 00177 case 4: 00178 return Vec3d(-local.y(),local.x(), local.z()); 00179 case 5: 00180 return Vec3d(local.y(), local.x(), -local.z()); 00181 default: 00182 return Vec3d(0,0,0); 00183 } 00184 } 00185 00186 // Face 0 is centered at 0, 0 lat long or 0,0,0. faces 1-3 follow 00187 // around the equator in direction of increasing longitude 00188 // (east). Face 4 is the North Pole, Face 5 the South. 00189 00190 Vec3d face2dc(int faceNum, const Vec2d& faceCoord) 00191 { 00192 Vec3d local = face2qrs(faceCoord); 00193 return qrs2xyz(local, faceNum); 00194 } 00195 00196 bool faceCoordsToLatLon(double x, double y, int face, 00197 double& out_lat_deg, double& out_lon_deg) 00198 { 00199 double lat, lon; 00200 const double l = x * osg::PI_4; 00201 const double ty = tan(y * osg::PI_4); 00202 if (face < 4) 00203 { 00204 lon = face * osg::PI_2 + l; 00205 lon = fmod(lon + osg::PI, 2.0 * osg::PI) - osg::PI; 00206 lat = atan(cos(l) * ty); 00207 } 00208 else 00209 { 00210 const double tx = tan(x * osg::PI_4); 00211 lat = osg::PI_2 - atan(sqrt(tx * tx + ty * ty)); 00212 if (face == 5) 00213 { 00214 lon = atan2(tx, ty); 00215 lat = -lat; 00216 } 00217 else 00218 { 00219 lon = atan2(tx, -ty); 00220 } 00221 } 00222 out_lon_deg = RadiansToDegrees(lon); 00223 out_lat_deg = RadiansToDegrees(lat); 00224 return true; 00225 } 00226 00227 bool cubeToFace(double& in_out_x, double& in_out_y, int& out_face ) 00228 { 00229 double x, y; 00230 if (in_out_x > 1.0 + 1e-11) 00231 { 00232 double face = floor(in_out_x); 00233 x = in_out_x - face; 00234 if (x < 1e-11) 00235 { 00236 face += -1.0; 00237 x += 1.0; 00238 } 00239 y = in_out_y - 1.0; 00240 out_face = static_cast<int>(face); 00241 } 00242 else 00243 { 00244 if (in_out_y > 2.0 + 1e-11) 00245 { 00246 out_face = 4; 00247 y = in_out_y - 2.0; 00248 } 00249 else if (in_out_y < 1.0 + 1e-11) 00250 { 00251 out_face = 5; 00252 y = in_out_y; 00253 } 00254 else 00255 { 00256 out_face = 0; 00257 y = in_out_y - 1.0; 00258 } 00259 x = in_out_x; 00260 } 00261 in_out_x = x * 2.0 - 1.0; 00262 in_out_y = y * 2.0 - 1.0; 00263 return true; 00264 } 00265 00266 bool cubeToFace(double& in_out_xmin, double& in_out_ymin, 00267 double& in_out_xmax, double& in_out_ymax, 00268 int& out_face) 00269 { 00270 double xmin, xmax, ymin, ymax; 00271 if (in_out_ymin > 1.0 - 1e-11 && in_out_ymax < 2.0 + 1e-11) 00272 { 00273 double faceMin = floor(in_out_xmin + 1e-11); 00274 double faceMax = floor(in_out_xmax - 1e-11); 00275 if (faceMin != faceMax) 00276 { 00277 OE_WARN << LC << "Min face <> Max face!\n"; 00278 return false; 00279 } 00280 xmin = in_out_xmin - faceMin; 00281 xmax = in_out_xmax - faceMin; 00282 ymin = in_out_ymin - 1.0; 00283 ymax = in_out_ymax - 1.0; 00284 out_face = static_cast<int>(faceMin); 00285 } 00286 else if (in_out_ymin > 2.0 - 1e-11 && in_out_ymax > 2.0 + 1e-11) 00287 { 00288 out_face = 4; 00289 ymin = in_out_ymin - 2.0; 00290 ymax = in_out_ymax - 2.0; 00291 xmin = in_out_xmin; 00292 xmax = in_out_xmax; 00293 } 00294 else if (in_out_ymax < 1.0 + 1e-11) 00295 { 00296 out_face = 5; 00297 ymin = in_out_ymin; 00298 ymax = in_out_ymax; 00299 xmin = in_out_xmin; 00300 xmax = in_out_xmax; 00301 } 00302 else 00303 { 00304 OE_WARN << LC << "can't determine face for (" 00305 << in_out_xmin << ", " << in_out_ymin << "), (" 00306 << in_out_xmax << ", " << in_out_ymax << ")\n"; 00307 return false; 00308 } 00309 in_out_xmin = xmin * 2.0 - 1.0; 00310 in_out_xmax = xmax * 2.0 - 1.0; 00311 in_out_ymin = ymin * 2.0 - 1.0; 00312 in_out_ymax = ymax * 2.0 - 1.0; 00313 return true; 00314 } 00315 00316 bool faceToCube(double& in_out_x, double& in_out_y, int face) 00317 { 00318 double x = (in_out_x + 1.0) * .5; 00319 double y = (in_out_y + 1.0) * .5; 00320 if (face < 4) 00321 { 00322 in_out_x = x + face; 00323 in_out_y = y + 1.0; 00324 } 00325 else 00326 { 00327 in_out_x = x; 00328 if (face == 4) 00329 in_out_y = y + 2.0; 00330 else 00331 in_out_y = y; 00332 } 00333 return true; 00334 } 00335 00336 double arcLength(const Vec2d& coord1, const Vec2d& coord2, int face) 00337 { 00338 if (coord1.x() != coord2.x() && coord1.y() != coord2.y()) 00339 { 00340 Vec3d geo1 = face2dc(face, coord1); 00341 Vec3d geo2 = face2dc(face, coord2); 00342 Vec3d norm = geo1 ^ geo2; 00343 return atan2(norm.length(), geo1 * geo2) * WGS_84_RADIUS_EQUATOR; 00344 } 00345 double x1, y1, x2, y2; 00346 if (coord1.x() == coord2.x()) 00347 { 00348 x1 = coord1.x() * PI_4; y1 = coord1.y() * PI_4; 00349 x2 = coord2.x() * PI_4; y2 = coord2.y() * PI_4; 00350 } 00351 else 00352 { 00353 x1 = coord1.y() * PI_4; y1 = coord1.x() * PI_4; 00354 x2 = coord2.y() * PI_4; y2 = coord2.x() * PI_4; 00355 } 00356 double tanPhi1 = cos(x1) * tan(y1); 00357 double tanPhi2 = cos(x2) * tan(y2); 00358 // tan(phi2 - phi1) = (tan(phi2) - tan(hi1)) / (1 + tan(phi2) * tan(phi1)) 00359 return fabs(atan2(tanPhi2 - tanPhi1, 1 + tanPhi2 * tanPhi1)) 00360 * WGS_84_RADIUS_EQUATOR; 00361 00362 } 00363 00364 // For debugging 00365 // Find the point closest to P3 on the line segment from P1 to P2 00366 static osg::Vec3d closestPointOnLine(const osg::Vec3d &p1, 00367 const osg::Vec3d& p2, 00368 const osg::Vec3d& p3) 00369 { 00370 Vec3d vec = p2 - p1; 00371 double len2 = vec.length2(); 00372 if (equivalent(len2, 0)) 00373 return p1; 00374 double u = ((p3 - p1) * vec) / len2; 00375 if (u <= 0.0) 00376 return p1; 00377 else if (u >= 1.0) 00378 return p2; 00379 else 00380 return p1 + vec * u; 00381 } 00382 00383 double distanceToLine(const Vec3d& p1, const Vec3d& p2, const Vec3d& p3) 00384 { 00385 Vec3d pt = closestPointOnLine(p1, p2, p3); 00386 return (p3 - pt).length(); 00387 } 00388 00389 double distanceToSegment(const Vec3d& p, 00390 const Vec3d& geo1, const Vec3d& geo2, 00391 const Vec3d& norm) 00392 { 00393 // Find the distance to the closet point on the circle that 00394 // contains the segment. If that point lies on the segment, that's 00395 // the shortest distance; otherwise, the distance to one of the 00396 // end points is the shortest. 00397 00398 // Project p into plane of circle 00399 Vec3d q = p - norm * (norm * p); 00400 // If q = (0, 0, 0) -- the center of the circle -- then all points 00401 // on the circle are equidistant. 00402 // qnorm will be on unit circle 00403 const double r = WGS_84_RADIUS_EQUATOR; 00404 if (equivalent(q.length2(), 0)) 00405 { 00406 return sqrt(r * r + p.length2()); 00407 } 00408 Vec3d qnorm = q / q.length(); 00409 00410 // Vec3d x = q * r / q.length(); 00411 const Vec3d zero; 00412 Vec3d end1, end2; 00413 double mua, mub; 00414 if (lineLineIntersect(zero, qnorm, geo1, geo2, end1, end2, mua, mub) 00415 && mub >= 0.0 && mub <= 1.0) 00416 { 00417 return (p - qnorm * r).length(); 00418 } 00419 else 00420 { 00421 return minimum((p - geo1 * r).length(), (p - geo2 * r).length()); 00422 } 00423 } 00424 00425 // The normal will point towards +s or +r 00426 00427 Vec3d getNormalToSegment(const Vec2d& coord1, const Vec2d& coord2, int face) 00428 { 00429 if (coord1.x() == coord2.x()) 00430 { 00431 double xang = coord1.x() * PI_4; 00432 double sx = sin(xang), cx = cos(xang); 00433 Vec3d qrsNormal(cx, 0.0, -sx); 00434 return qrs2xyz(qrsNormal, face); 00435 } 00436 else 00437 { 00438 double yang = coord1.y() * PI_4; 00439 double sy = sin(yang), cy = cos(yang); 00440 Vec3d qrsNormal(0.0, cy, -sy); 00441 return qrs2xyz(qrsNormal, face); 00442 } 00443 } 00444 00445 double distanceToSegment(const Vec3d& p, 00446 const Vec2d& coord1, const Vec2d& coord2, int face) 00447 { 00448 Vec3d norm = getNormalToSegment(coord1, coord2, face); 00449 Vec3d geo1 = face2dc(face, coord1); 00450 Vec3d geo2 = face2dc(face, coord2); 00451 return distanceToSegment(p, geo1, geo2, norm); 00452 } 00453 00454 /* 00455 Calculate the line segment PaPb that is the shortest route between 00456 two lines P1P2 and P3P4. Calculate also the values of mua and mub where 00457 Pa = P1 + mua (P2 - P1) 00458 Pb = P3 + mub (P4 - P3) 00459 Return FALSE if no solution exists. 00460 00461 Ripped off from http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline3d/ 00462 */ 00463 bool lineLineIntersect( 00464 const Vec3d& p1, const Vec3d& p2, const Vec3d& p3, const Vec3d& p4, 00465 Vec3d& pa,Vec3d& pb, double& mua, double& mub) 00466 { 00467 Vec3d p13, p43, p21; 00468 double d1343, d4321, d1321, d4343, d2121; 00469 double numer, denom; 00470 00471 p13 = p1 - p3; 00472 p43 = p4 - p3; 00473 if (equivalent(p43.length2(), 0.0, 1e-11)) 00474 return false; 00475 p21 = p2 - p1; 00476 if (equivalent(p21.length2(), 0.0, 1e-11)) 00477 return false; 00478 00479 d1343 = p13.x() * p43.x() + p13.y() * p43.y() + p13.z() * p43.z(); 00480 d4321 = p43.x() * p21.x() + p43.y() * p21.y() + p43.z() * p21.z(); 00481 d1321 = p13.x() * p21.x() + p13.y() * p21.y() + p13.z() * p21.z(); 00482 d4343 = p43.x() * p43.x() + p43.y() * p43.y() + p43.z() * p43.z(); 00483 d2121 = p21.x() * p21.x() + p21.y() * p21.y() + p21.z() * p21.z(); 00484 00485 denom = d2121 * d4343 - d4321 * d4321; 00486 if (equivalent(denom, 0.0, 1e-11)) 00487 return false; 00488 numer = d1343 * d4321 - d1321 * d4343; 00489 00490 mua = numer / denom; 00491 mub = (d1343 + d4321 * (mua)) / d4343; 00492 00493 pa = p1 + p21 * mua; 00494 pb = p3 + p43 * mub; 00495 00496 return true; 00497 } 00498 00499 } 00500 00501 using namespace euler; 00502 00503 bool EulerFaceLocator::convertLocalToModel(const Vec3d& local, Vec3d& world) const 00504 { 00505 #if ((OPENSCENEGRAPH_MAJOR_VERSION <= 2) && (OPENSCENEGRAPH_MINOR_VERSION < 8)) 00506 // OSG 2.7 bug workaround: bug fix in Locator submitted by GW 00507 const_cast<EulerFaceLocator*>(this)->_inverse.invert( _transform ); 00508 #endif 00509 if ( _coordinateSystemType == GEOCENTRIC ) 00510 { 00511 //Convert the NDC coordinate into face space 00512 osg::Vec3d faceCoord = local * _transform; 00513 00514 double lat_deg, lon_deg; 00515 faceCoordsToLatLon(faceCoord.x(), faceCoord.y(), _face, 00516 lat_deg, lon_deg); 00517 00518 //OE_NOTICE << "LatLon=" << latLon << std::endl; 00519 00520 // convert to geocentric: 00521 _ellipsoidModel->convertLatLongHeightToXYZ( 00522 DegreesToRadians(lat_deg), 00523 DegreesToRadians(lon_deg), 00524 local.z(), 00525 world.x(), world.y(), world.z()); 00526 00527 return true; 00528 } 00529 return true; 00530 } 00531 00532 bool EulerFaceLocator::convertModelToLocal(const Vec3d& world, Vec3d& local) const 00533 { 00534 #if ((OPENSCENEGRAPH_MAJOR_VERSION <= 2) && (OPENSCENEGRAPH_MINOR_VERSION < 8)) 00535 // OSG 2.7 bug workaround: bug fix in Locator submitted by GW 00536 const_cast<EulerFaceLocator*>(this)->_inverse.invert( _transform ); 00537 #endif 00538 00539 switch(_coordinateSystemType) 00540 { 00541 case(GEOCENTRIC): 00542 { 00543 double longitude, latitude, height; 00544 00545 _ellipsoidModel->convertXYZToLatLongHeight 00546 (world.x(), world.y(), world.z(), latitude, longitude, height ); 00547 00548 int face=-1; 00549 double x, y; 00550 00551 double lat_deg = RadiansToDegrees(latitude); 00552 double lon_deg = RadiansToDegrees(longitude); 00553 00554 bool success = latLonToFaceCoords(lat_deg, lon_deg, x, y, face, 00555 _face); 00556 00557 if (!success) 00558 { 00559 OE_NOTICE << LC << "Couldn't convert to face coords\n"; 00560 } 00561 if (face != static_cast<int>(_face)) 00562 { 00563 OE_NOTICE << LC 00564 << "Face should be " << _face << " but is " << face 00565 << ", lat = " << lat_deg 00566 << ", lon = " << lon_deg 00567 << "\n"; 00568 } 00569 00570 local = Vec3d(x, y, height) * _inverse; 00571 return true; 00572 } 00573 00574 00575 case(GEOGRAPHIC): 00576 case(PROJECTED): 00577 // Neither of these is supported for this locator.. 00578 { 00579 local = world * _inverse; 00580 return true; 00581 } 00582 } 00583 00584 return false; 00585 } 00586 00587 00588 EulerSpatialReference::EulerSpatialReference( void* handle ) 00589 : SpatialReference(handle, "OSGEARTH", "euler-cube", 00590 "Euler Cube") 00591 { 00592 //nop 00593 } 00594 00595 void 00596 EulerSpatialReference::_init() 00597 { 00598 SpatialReference::_init(); 00599 00600 _is_user_defined = true; 00601 _is_cube = true; 00602 _is_contiguous = false; 00603 _is_geographic = false; 00604 _name = "Euler Cube"; 00605 } 00606 00607 GeoLocator* 00608 EulerSpatialReference::createLocator(double xmin, double ymin, 00609 double xmax, double ymax, 00610 bool plate_carre) const 00611 { 00612 int face; 00613 cubeToFace(xmin, ymin, xmax, ymax, face); 00614 00615 GeoLocator* result = new EulerFaceLocator( face ); 00616 00617 osg::Matrixd transform; 00618 transform.set( 00619 xmax-xmin, 0.0, 0.0, 0.0, 00620 0.0, ymax-ymin, 0.0, 0.0, 00621 0.0, 0.0, 1.0, 0.0, 00622 xmin, ymin, 0.0, 1.0); 00623 result->setTransform( transform ); 00624 00625 return result; 00626 } 00627 00628 bool 00629 EulerSpatialReference::preTransform(double& x, double& y, void* context) const 00630 { 00631 // Convert the incoming points from cube => face => lat/long. 00632 int face; 00633 if ( !cubeToFace(x, y, face) ) 00634 { 00635 OE_WARN << LC << "Failed to convert (" << x << "," << y << ") into face coordinates." << std::endl; 00636 return false; 00637 } 00638 00639 double lat_deg, lon_deg; 00640 bool success = faceCoordsToLatLon( x, y, face, lat_deg, lon_deg ); 00641 if (!success) 00642 { 00643 OE_WARN << LC << "Could not transform face coordinates to lat lon" << std::endl; 00644 return false; 00645 } 00646 x = lon_deg; 00647 y = lat_deg; 00648 return true; 00649 } 00650 00651 bool 00652 EulerSpatialReference::postTransform(double& x, double& y, void* context) const 00653 { 00654 //Convert the incoming points from lat/lon back to face coordinates 00655 int face; 00656 double out_x, out_y; 00657 00658 // convert from lat/long to x/y/face 00659 bool success = latLonToFaceCoords(y, x, out_x, out_y, face); 00660 if (!success) 00661 { 00662 OE_WARN << LC << "Could not transform face coordinates to lat lon" << std::endl; 00663 return false; 00664 } 00665 00666 //TODO: what to do about boundary points? 00667 00668 if ( !faceToCube(out_x, out_y, face)) 00669 { 00670 OE_WARN << LC << "fromFace(" << out_x << "," << out_y << "," << face << ") failed" << std::endl; 00671 return false; 00672 } 00673 00674 x = out_x; 00675 y = out_y; 00676 00677 return true; 00678 } 00679 00680 bool 00681 EulerSpatialReference::transform(double x, double y, 00682 const SpatialReference* to_srs, 00683 double& out_x, double& out_y, 00684 void* context) const 00685 { 00686 if ( !_initialized ) 00687 const_cast<EulerSpatialReference*>(this)->init(); 00688 if (!to_srs->isEquivalentTo(getGeographicSRS())) 00689 return SpatialReference::transform(x, y, to_srs, out_x, out_y, context); 00690 if (EulerSpatialReference::preTransform(x, y, context)) 00691 { 00692 out_x = x; 00693 out_y = y; 00694 return true; 00695 } 00696 else 00697 { 00698 return false; 00699 } 00700 } 00701 00702 bool EulerSpatialReference::transformPoints(const SpatialReference* to_srs, 00703 double* x, double *y, 00704 unsigned int numPoints, 00705 void* context, 00706 bool ignore_errors) const 00707 { 00708 if ( !_initialized ) 00709 const_cast<EulerSpatialReference*>(this)->init(); 00710 if (!to_srs->isEquivalentTo(getGeographicSRS())) 00711 return SpatialReference::transformPoints(to_srs, x, y, numPoints, 00712 context, ignore_errors); 00713 bool success = true; 00714 for (unsigned int i = 0; i < numPoints; ++i) 00715 { 00716 bool result 00717 = EulerSpatialReference::preTransform(x[i], y[i], context); 00718 success = success && result; 00719 } 00720 return success; 00721 } 00722 00723 bool 00724 EulerSpatialReference::transformExtent(const SpatialReference* to_srs, 00725 double& in_out_xmin, 00726 double& in_out_ymin, 00727 double& in_out_xmax, 00728 double& in_out_ymax, 00729 void* context ) const 00730 { 00731 // note: this method only works when the extent is isolated to one 00732 // face of the cube. If you want to transform an arbitrary extent, 00733 // you need to break it up into separate extents for each cube face. 00734 bool ok = true; 00735 00736 double face_xmin = in_out_xmin, face_ymin = in_out_ymin; 00737 double face_xmax = in_out_xmax, face_ymax = in_out_ymax; 00738 00739 int face; 00740 if (!cubeToFace(face_xmin, face_ymin, face_xmax, face_ymax, face)) 00741 { 00742 OE_WARN << LC << "extent (" << in_out_xmin << ", " << in_out_ymin 00743 << ")=>(" << in_out_xmax << ", " << in_out_ymax << 00744 ") crosses faces\n"; 00745 return false; 00746 } 00747 // The polar and equatorial faces behave the same way, except if 00748 // an extent crosses the pole. 00749 00750 double lonmin, lonmax, latmin, latmax; 00751 // if the extent crosses the center axes of the face, then, 00752 // due to the curvy nature of the projection, the maximim / 00753 // minimum will be at the crossing. So we may need to sample 00754 // up to 8 points. 00755 double lats[8], lons[8]; 00756 int numSamples = 4; 00757 faceCoordsToLatLon(face_xmin, face_ymin, face, lats[0], lons[0]); 00758 faceCoordsToLatLon(face_xmax, face_ymin, face, lats[1], lons[1]); 00759 faceCoordsToLatLon(face_xmin, face_ymax, face, lats[2], lons[2]); 00760 faceCoordsToLatLon(face_xmax, face_ymax, face, lats[3], lons[3]); 00761 00762 if ((face_xmin < 0 && face_xmax > 0)) 00763 { 00764 faceCoordsToLatLon(0.0, face_ymin, face, 00765 lats[numSamples], lons[numSamples]); 00766 faceCoordsToLatLon(0.0, face_ymax, face, 00767 lats[numSamples + 1], lons[numSamples + 1]); 00768 numSamples += 2; 00769 00770 } 00771 if ((face_ymin < 0 && face_ymax > 0)) 00772 { 00773 faceCoordsToLatLon(face_xmin, 0.0, face, 00774 lats[numSamples], lons[numSamples]); 00775 faceCoordsToLatLon(face_xmax, 0.0, face, 00776 lats[numSamples + 1], lons[numSamples + 1]); 00777 numSamples += 2; 00778 00779 } 00780 // keep extent longitudes consistent. If the max latitude lies on 00781 // the date line, its value should be 180, not -180. The poles are 00782 // especially interesting... 00783 if (face == 2 && face_xmax == 0.0) 00784 { 00785 lons[1] = 180.0; 00786 lons[3] = 180.0; 00787 } 00788 else if ((face == 4 && face_ymax > 0.0) 00789 || (face == 5 && face_ymax <= 0.0)) 00790 { 00791 if (face_xmin == 0.0) 00792 { 00793 lons[0] = 180.0; 00794 lons[2] = 180.0; 00795 } 00796 else if (face_xmax == 0.0) 00797 { 00798 lons[1] = -180.0; 00799 lons[3] = -180.0; 00800 } 00801 } 00802 if ((face == 4 || face == 5) && face_ymax == 0.0) 00803 { 00804 if (face_xmax == 0.0) 00805 lons[3] = -90; 00806 else if (face_xmin == 0.0) 00807 lons[2] = 90; 00808 00809 } 00810 lonmin = *min_element(&lons[0], &lons[numSamples]); 00811 latmin = *min_element(&lats[0], &lats[numSamples]); 00812 lonmax = *max_element(&lons[0], &lons[numSamples]); 00813 latmax = *max_element(&lats[0], &lats[numSamples]); 00814 // Does the extent cross one of the poles? 00815 if ((face == 4 || face == 5) && numSamples == 8) 00816 { 00817 lonmin = -180.0; 00818 lonmax = 180.0; 00819 if (face == 4) 00820 latmax = 90.0; 00821 else 00822 latmin = -90.0; 00823 } 00824 // Check for Date Line crossing 00825 else if (face_xmin < 0 && face_xmax > 0 00826 && (face == 2 || (face == 4 && face_ymin >= 0) 00827 || (face == 5 && face_ymax <= 0))) 00828 { 00829 std::swap(lonmin, lonmax); 00830 } 00831 if (to_srs->isGeographic()) 00832 { 00833 in_out_xmin = lonmin; 00834 in_out_xmax = lonmax; 00835 in_out_ymin = latmin; 00836 in_out_ymax = latmax; 00837 } 00838 else 00839 { 00840 bool ok1 = transform(lonmin, latmin, to_srs, in_out_xmin, in_out_ymin, 00841 context); 00842 bool ok2 = transform(lonmax, latmax, to_srs, in_out_xmax, in_out_ymax, 00843 context); 00844 ok = ok1 && ok2; 00845 } 00846 return ok; 00847 } 00848 00849 // This has been written to minimize the number of transcendental 00850 // function calls -- as well as other expensive operations -- in the 00851 // inner loop. 00852 bool EulerSpatialReference::transformExtentPoints( 00853 const SpatialReference* to_srs, 00854 double in_xmin, double in_ymin, 00855 double in_xmax, double in_ymax, 00856 double* x, double *y, 00857 unsigned int numx, unsigned int numy, 00858 void* context, bool ignore_errors ) const 00859 { 00860 if ( !_initialized ) 00861 const_cast<EulerSpatialReference*>(this)->init(); 00862 int face; 00863 00864 // Punt if the extent covers more than one face (doesn't happen 00865 // normally). 00866 if (!(to_srs->isEquivalentTo(getGeographicSRS()) 00867 && cubeToFace(in_xmin, in_ymin, in_xmax, in_ymax, face))) 00868 return SpatialReference::transformExtentPoints( 00869 to_srs, in_xmin, in_ymin, in_xmax, in_ymax, x, y, 00870 numx, numy, context, ignore_errors); 00871 const double dx = (in_xmax - in_xmin) / (numx - 1); 00872 const double dy = (in_ymax - in_ymin) / (numy - 1); 00873 unsigned pixel = 0; 00874 // Cache values for and tan(y) 00875 AutoBuffer<double, 256> tany(numy); 00876 // induction variables for column and row counters 00877 double fc = 0.0, fr = 0.0; 00878 for (unsigned r = 0; r < numy; ++r, ++fr) 00879 tany[r] = tan((in_ymin + fr * dy) * osg::PI_4); 00880 if (face < 4) 00881 { 00882 double lonBase = face * osg::PI_2; 00883 for (unsigned c = 0; c < numx; ++c, ++fc) 00884 { 00885 const double l = (in_xmin + fc * dx) * osg::PI_4; 00886 double lon = lonBase + l; 00887 lon = fmod(lon + osg::PI, 2.0 * osg::PI) - osg::PI; 00888 const double rlon = RadiansToDegrees(lon); 00889 const double cosl = cos(l); 00890 for (unsigned r = 0; r < numy; ++r) 00891 { 00892 const double lat = atan(cosl * tany[r]); 00893 x[pixel] = rlon; 00894 y[pixel] = RadiansToDegrees(lat); 00895 pixel++; 00896 } 00897 } 00898 } 00899 else 00900 { 00901 // The pole faces are the same, except for a change of sign in 00902 // the latitude and longitude calculations. 00903 const double sgn = face == 4 ? -1.0 : 1.0; 00904 for (unsigned c = 0; c < numx; ++c, ++fc) 00905 { 00906 const double l = (in_xmin + fc * dx) * osg::PI_4; 00907 const double tx = tan(l); 00908 const double tx2 = tx * tx; 00909 for (unsigned r = 0; r < numy; ++r) 00910 { 00911 const double ty = tany[r]; 00912 const double lon = atan2(tx, sgn * ty); 00913 const double lat = (atan(sqrt(tx2 + ty * ty)) - osg::PI_2) 00914 * sgn; 00915 x[pixel] = RadiansToDegrees(lon); 00916 y[pixel] = RadiansToDegrees(lat); 00917 pixel++; 00918 } 00919 } 00920 } 00921 const int numPixels = numx * numy; 00922 // Fixups for boundary lieing on date line 00923 if (face == 2 && in_xmax == 0.0) 00924 { 00925 for (int pixel = numx - 1; pixel < numPixels; pixel += numx) 00926 x[pixel] = 180; 00927 } 00928 else if ((face == 4 && in_ymax > 0.0) 00929 || (face == 5 && in_ymax <= 0.0)) 00930 { 00931 double val; 00932 int startPix = -1; 00933 if (in_xmin == 0.0) 00934 { 00935 val = 180.0; 00936 startPix = 0; 00937 } 00938 else if (in_xmax == 0.0) 00939 { 00940 val = -180.0; 00941 startPix = numx - 1; 00942 } 00943 if (startPix > 0) 00944 for (int pixel = startPix; pixel < numPixels; pixel += numx) 00945 x[pixel] = val; 00946 } 00947 // pole case 00948 if ((face == 4 || face == 5) && in_ymax == 0.0) 00949 { 00950 if (in_xmax == 0.0) 00951 x[numPixels - 1] = -90.0; 00952 else if (in_xmin == 0.0) 00953 x[numx * (numy - 1)] = 90; 00954 } 00955 return true; 00956 } 00957 00958 namespace 00959 { 00960 SpatialReference* createEulerSRS() 00961 { 00962 // root the cube srs with a WGS84 intermediate ellipsoid. 00963 std::string init = "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs"; 00964 00965 SpatialReference* result = 0; 00966 GDAL_SCOPED_LOCK; 00967 void* handle = OSRNewSpatialReference(0); 00968 if ( OSRImportFromProj4( handle, init.c_str() ) == OGRERR_NONE ) 00969 { 00970 result = new EulerSpatialReference( handle ); 00971 } 00972 else 00973 { 00974 OE_WARN << "[osgEarth::SRS] Unable to create SRS: " << init << std::endl; 00975 OSRDestroySpatialReference( handle ); 00976 } 00977 return result; 00978 } 00979 } 00980 00981 // Hack to get euler-cube into the spatial reference cache 00982 class CacheInitializer 00983 { 00984 public: 00985 CacheInitializer() 00986 { 00987 EulerSpatialReference::getSpatialReferenceCache()["euler-cube"] 00988 = createEulerSRS(); 00989 } 00990 }; 00991 00992 namespace 00993 { 00994 CacheInitializer s_cacheInitializer; 00995 } 00996 00997 EulerProfile::EulerProfile() 00998 : Profile(createEulerSRS(), 00999 0.0, 0.0, 4.0, 4.0, 01000 -180.0, -90.0, 180.0, 90.0, 01001 0, 01002 1, 1) 01003 { 01004 } 01005 01006 int EulerProfile::getFace(const TileKey& key) 01007 { 01008 int shiftVal = key.getLevelOfDetail() - 2; 01009 int faceX = key.getTileX() >> shiftVal; 01010 int faceY = key.getTileY() >> shiftVal; 01011 if (faceY == 1) 01012 return 4; 01013 else if (faceY == 3) 01014 return 5; 01015 else 01016 return faceX; 01017 } 01018 01019 void EulerProfile::getIntersectingTiles( 01020 const GeoExtent& remoteExtent, 01021 std::vector<TileKey>& out_intersectingKeys ) const 01022 { 01023 OE_FATAL << "EulerProfile::getIntersectingTiles not implemented yet!\n"; 01024 } 01025 01026 }