osgEarth 2.1.1

/home/cube/sources/osgearth/src/osgEarthDrivers/engine_seamless/Euler.cpp

Go to the documentation of this file.
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines