osgEarth 2.1.1

/home/cube/sources/osgearth/src/osgEarthDrivers/engine_seamless/QSC.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 "QSC"
00020 
00021 #include <algorithm>
00022 #include <vector>
00023 
00024 #include <osg/Math>
00025 
00026 #include <osgEarth/Registry>
00027 #include <ogr_api.h>
00028 #include <ogr_spatialref.h>
00029 
00030 #define LC "[seamless::QSC] "
00031 
00032 namespace seamless
00033 {
00034 using namespace std;
00035 using namespace osg;
00036 using namespace osgEarth;
00037 
00038 // Constants used througout
00039 
00040 const double PiOver12 = PI / 12.0;
00041 const double PiOver12Inv = 12.0 / PI;
00042 const double sqrt2 = 1.41421356237309504880; // sqrt(2)
00043 const double sqrt33 = .5773502691896258; // sqrt(3) / 3
00044 
00045 namespace qsc
00046 {
00047 // Convert lat, lon to a unit vector on a sphere
00048 
00049 inline Vec3d latLon2xyz(double lat_deg, double lon_deg)
00050 {
00051     Vec3d result;
00052     result.z() = sin(DegreesToRadians(lat_deg));
00053     double hyp = sqrt(1 - result.z() * result.z());
00054     double long_rad = DegreesToRadians(lon_deg);
00055     result.x() = hyp * cos(long_rad);
00056     result.y() = hyp * sin(long_rad);
00057     return result;
00058 }
00059 
00060 // On a boundary, return the lower-numbered face.
00061 inline int getFace(const Vec3d& vec)
00062 {
00063     double absx = fabs(vec.x());
00064     double absy = fabs(vec.y());
00065     double absz = fabs(vec.z());
00066     // pole faces
00067     if (absz > (absx + 1e-11) && absz > (absy + 1e-11))
00068     {
00069         if (vec.z() > 0.0)
00070             return 4;
00071         else
00072             return 5;
00073     }
00074     // One of the X faces, unless on a border
00075     else if (absx > absy || equivalent(absx, absy, 1e-11))
00076     {
00077         if (vec.x() > 0.0)
00078             return 0;
00079         else if (equivalent(vec.x(), -vec.y(), 1e-11))
00080             return 1;           // Boundary between 1 and 2
00081         else
00082             return 2;
00083     }
00084     // One of the Y faces
00085     else
00086     {
00087         if (vec.y() > 0.0)
00088             return 1;
00089         else
00090             return 3;
00091     }
00092 }
00093 
00094 // Convert unit vector to coordinates on a face. q is normal to the
00095 // face. r and s correspond to x and y face coordinates.
00096 
00097 Vec3d xyz2qrs(const Vec3d& xyz, int face)
00098 {
00099     switch (face)
00100     {
00101     case 0:
00102         return Vec3d(xyz.x(), xyz.y(), xyz.z());
00103     case 1:
00104         return Vec3d(xyz.y(), -xyz.x(), xyz.z());
00105     case 2:
00106         return Vec3d(-xyz.x(), -xyz.y(), xyz.z());
00107     case 3:
00108         return Vec3d(-xyz.y(), xyz.x(), xyz.z());
00109     case 4:
00110         return Vec3d(xyz.z(), xyz.y(), -xyz.x());
00111     case 5:
00112         return Vec3d(-xyz.z(), xyz.y(), xyz.x());
00113     default:
00114         return Vec3d(0, 0, 0);
00115     }
00116 }
00117 
00118 bool latLonToFaceCoords(double lat_deg, double lon_deg,
00119                         double& out_x, double& out_y, int& out_face,
00120                         int faceHint)
00121 {
00122     if (lat_deg > 90.0 || lat_deg < -90.0
00123         || lon_deg < -180.0 || lon_deg > 180.0)
00124         return false;
00125     Vec3d xyz = latLon2xyz(lat_deg, lon_deg);
00126     out_face = faceHint >= 0 ? faceHint : getFace(xyz);
00127     Vec3d qrs = xyz2qrs(xyz, out_face);
00128     if (equivalent(qrs[1], 0.0, 1e-11) && equivalent(qrs[1], 0.0, 1e-11))
00129     {
00130         out_x = qrs[1];
00131         out_y = qrs[2];
00132         return true;
00133     }
00134     bool swap = false;
00135     if (fabs(qrs[1]) < fabs(qrs[2]))
00136     {
00137         std::swap(qrs[1], qrs[2]);
00138         swap = true;
00139     }
00140     double sOverR = qrs[2] / qrs[1];
00141     double x = sqrt((1 - qrs[0])/(1 - 1/sqrt(2 + sOverR * sOverR)));
00142     double y = x * (PiOver12Inv * atan(sOverR)
00143                     - asin(qrs[2] / sqrt(2.0 * (qrs[1] * qrs[1]
00144                                                 + qrs[2] * qrs[2]))));
00145     if (qrs[1] < 0.0)
00146         x = -x;
00147     if (qrs[2] < 0.0)
00148         y = -y;
00149     if (swap)
00150     {
00151         out_x = y;
00152         out_y = x;
00153     }
00154     else
00155     {
00156         out_x = x;
00157         out_y = y;
00158     }
00159     return true;
00160 }
00161 
00162 Vec3d face2qrs(const Vec2d& face)
00163 {
00164     // formulae assume that x is not less than y.
00165     bool swap = false;
00166     double x = face.x(), y = face.y();
00167     if (equivalent(x, 0.0, 1e-11) && equivalent(y, 0.0, 1e-11))
00168         return Vec3d(sqrt33, x, y);
00169     if (fabs(x) < fabs(y))
00170     {
00171         x = face.y();
00172         y = face.x();
00173         swap = true;
00174     }
00175     const double yOverX = y / x;
00176     const double quo
00177         = sin(PiOver12 * yOverX) / (cos(PiOver12 * yOverX) - 1 / sqrt2);
00178     const double quo2 = quo * quo;
00179     const double q = 1 - x * x * (1 - 1 / sqrt(2 + quo2));
00180     const double q2 = q * q;
00181     double r2 = (1 - q2) / (1 + quo2);
00182     double s2 = 1 - q2 - r2;
00183     double r = sqrt(r2);
00184     double s = sqrt(s2);
00185     Vec3d result;
00186     result[0] = q;
00187     if (x > 0)
00188         result[1] = r;
00189     else
00190         result[1] = -r;
00191     if (y > 0)
00192         result[2] = s;
00193     else
00194         result[2] = -s;
00195     if (swap)
00196         std::swap(result[1], result[2]);
00197     return result;
00198 }
00199 
00200 // Face 0 is centered at 0, 0 lat long or 0,0,0. faces 1-3 follow
00201 // around the equator in direction of increasing longitude
00202 // (east). Face 4 is the North Pole, Face 5 the South.
00203 
00204 Vec3d face2ec(int faceNum, const Vec2d& faceCoord)
00205 {
00206     Vec3d local = face2qrs(faceCoord);
00207     switch (faceNum)
00208     {
00209     case 0:
00210         return Vec3d(local.x(), local.y(), local.z());
00211         break;
00212     case 1:
00213         return Vec3d(-local.y(), local.x(), local.z());
00214         break;
00215     case 2:
00216         return Vec3d(-local.x(), -local.y(), local.z());
00217     case 3:
00218         return Vec3d(local.y(), -local.x(), local.z());
00219     case 4:
00220         return Vec3d(-local.z(),local.y(), local.x());
00221     case 5:
00222         return Vec3d(local.z(), local.y(), -local.x());
00223     default:
00224         return Vec3d(0,0,0);
00225     }
00226 }
00227 
00228 bool faceCoordsToLatLon(double x, double y, int face,
00229                         double& out_lat_deg, double& out_lon_deg)
00230 {
00231     Vec3d geo = face2ec(face, Vec2d(x, y));
00232     const double lon = atan2(geo.y(),geo.x());
00233     const double lat = atan2(geo.z(),
00234                              sqrt(geo.x() * geo.x() + geo.y() * geo.y()));
00235     out_lon_deg = RadiansToDegrees(lon);
00236     out_lat_deg = RadiansToDegrees(lat);
00237     return true;
00238 }
00239 
00240 bool cubeToFace(double& in_out_x, double& in_out_y, int& out_face )
00241 {
00242     double x, y;
00243     if (in_out_x > 1.0 + 1e-11)
00244     {
00245         double face = floor(in_out_x);
00246         x = in_out_x - face;
00247         if (x < 1e-11)
00248         {
00249             face += -1.0;
00250             x += 1.0;
00251         }
00252         y = in_out_y - 1.0;
00253         out_face = static_cast<int>(face);
00254     }
00255     else
00256     {
00257         if (in_out_y > 2.0 + 1e-11)
00258         {
00259             out_face = 4;
00260             y = in_out_y - 2.0;
00261         }
00262         else if (in_out_y < 1.0 + 1e-11)
00263         {
00264             out_face = 5;
00265             y = in_out_y;
00266         }
00267         else
00268         {
00269             out_face = 0;
00270             y = in_out_y - 1.0;
00271         }
00272         x = in_out_x;
00273     }
00274     in_out_x = x * 2.0 - 1.0;
00275     in_out_y = y * 2.0 - 1.0;
00276     return true;
00277 }
00278 
00279 bool cubeToFace(double& in_out_xmin, double& in_out_ymin,
00280                 double& in_out_xmax, double& in_out_ymax,
00281                 int& out_face)
00282 {
00283     double xmin, xmax, ymin, ymax;
00284     if (in_out_ymin > 1.0 - 1e-11 && in_out_ymax < 2.0 + 1e-11)
00285     {
00286         double faceMin = floor(in_out_xmin + 1e-11);
00287         double faceMax = floor(in_out_xmax - 1e-11);
00288         if (faceMin != faceMax)
00289         {
00290             OE_WARN << LC << "Min face <> Max face!\n";
00291             return false;
00292         }
00293         xmin = in_out_xmin - faceMin;
00294         xmax = in_out_xmax - faceMin;
00295         ymin = in_out_ymin - 1.0;
00296         ymax = in_out_ymax - 1.0;
00297         out_face = static_cast<int>(faceMin);
00298     }
00299     else if (in_out_ymin > 2.0 - 1e-11 && in_out_ymax > 2.0 + 1e-11)
00300     {
00301         out_face = 4;
00302         ymin = in_out_ymin - 2.0;
00303         ymax = in_out_ymax - 2.0;
00304         xmin = in_out_xmin;
00305         xmax = in_out_xmax;
00306     }
00307     else if (in_out_ymax < 1.0 + 1e-11)
00308     {
00309         out_face = 5;
00310         ymin = in_out_ymin;
00311         ymax = in_out_ymax;
00312         xmin = in_out_xmin;
00313         xmax = in_out_xmax;
00314     }
00315     else
00316     {
00317         OE_WARN << LC << "can't determine face for ("
00318                 << in_out_xmin << ", " << in_out_ymin << "), ("
00319                 << in_out_xmax << ", " << in_out_ymax << ")\n";
00320         return false;
00321     }
00322     in_out_xmin = xmin * 2.0 - 1.0;
00323     in_out_xmax = xmax * 2.0 - 1.0;
00324     in_out_ymin = ymin * 2.0 - 1.0;
00325     in_out_ymax = ymax * 2.0 - 1.0;
00326     return true;
00327 }
00328 
00329 bool faceToCube(double& in_out_x, double& in_out_y, int face)
00330 {
00331     double x = (in_out_x + 1.0) * .5;
00332     double y = (in_out_y + 1.0) * .5;
00333     if (face < 4)
00334     {
00335         in_out_x = x + face;
00336         in_out_y = y + 1.0;
00337     }
00338     else
00339     {
00340         in_out_x = x;
00341         if (face == 4)
00342             in_out_y = y + 2.0;
00343         else
00344             in_out_y = y;
00345     }
00346     return true;
00347 }
00348 
00349 }
00350 
00351 using namespace qsc;
00352 
00353 bool QscFaceLocator::convertLocalToModel(const Vec3d& local, Vec3d& world) const
00354 {
00355 #if ((OPENSCENEGRAPH_MAJOR_VERSION <= 2) && (OPENSCENEGRAPH_MINOR_VERSION < 8))
00356     // OSG 2.7 bug workaround: bug fix in Locator submitted by GW
00357     const_cast<QscFaceLocator*>(this)->_inverse.invert( _transform );
00358 #endif
00359     if ( _coordinateSystemType == GEOCENTRIC )
00360     {
00361         //Convert the NDC coordinate into face space
00362         osg::Vec3d faceCoord = local * _transform;
00363 
00364         double lat_deg, lon_deg;
00365         faceCoordsToLatLon(faceCoord.x(), faceCoord.y(), _face,
00366                            lat_deg, lon_deg);
00367 
00368         //OE_NOTICE << "LatLon=" << latLon <<  std::endl;
00369 
00370         // convert to geocentric:
00371         _ellipsoidModel->convertLatLongHeightToXYZ(
00372             DegreesToRadians(lat_deg),
00373             DegreesToRadians(lon_deg),
00374             local.z(),
00375             world.x(), world.y(), world.z());
00376 
00377         return true;
00378     }
00379     return true;
00380 }
00381 
00382 bool QscFaceLocator::convertModelToLocal(const Vec3d& world, Vec3d& local) const
00383 {
00384 #if ((OPENSCENEGRAPH_MAJOR_VERSION <= 2) && (OPENSCENEGRAPH_MINOR_VERSION < 8))
00385     // OSG 2.7 bug workaround: bug fix in Locator submitted by GW
00386     const_cast<QscFaceLocator*>(this)->_inverse.invert( _transform );
00387 #endif
00388 
00389     switch(_coordinateSystemType)
00390     {
00391     case(GEOCENTRIC):
00392         {
00393             double longitude, latitude, height;
00394 
00395             _ellipsoidModel->convertXYZToLatLongHeight
00396                 (world.x(), world.y(), world.z(), latitude, longitude, height );
00397 
00398             int face=-1;
00399             double x, y;
00400 
00401             double lat_deg = RadiansToDegrees(latitude);
00402             double lon_deg = RadiansToDegrees(longitude);
00403 
00404             bool success = latLonToFaceCoords(lat_deg, lon_deg, x, y, face,
00405                                               _face);
00406 
00407             if (!success)
00408             {
00409                 OE_NOTICE << LC << "Couldn't convert to face coords\n";
00410             }
00411             if (face != static_cast<int>(_face))
00412             {
00413                 OE_NOTICE << LC
00414                     << "Face should be " << _face << " but is " << face
00415                     << ", lat = " << lat_deg
00416                     << ", lon = " << lon_deg
00417                     << "\n";
00418             }
00419 
00420             local = Vec3d(x, y, height) * _inverse;
00421             return true;
00422         }
00423 
00424 
00425     case(GEOGRAPHIC):
00426     case(PROJECTED):
00427         // Neither of these is supported for this locator..
00428         {
00429             local = world * _inverse;
00430             return true;
00431         }
00432     }
00433 
00434     return false;
00435 }
00436 
00437 
00438 QscSpatialReference::QscSpatialReference( void* handle )
00439     : SpatialReference(handle, "OSGEARTH", "qsc-cube",
00440                        "Quadralateralized Sphere Cube")
00441 {
00442     //nop
00443 }
00444 
00445 void
00446 QscSpatialReference::_init()
00447 {
00448     SpatialReference::_init();
00449 
00450     _is_user_defined = true;
00451     _is_cube = true;
00452     _is_contiguous = false;
00453     _is_geographic = false;
00454     _name = "Quadralateralized Sphere Cube";
00455 }
00456 
00457 GeoLocator*
00458 QscSpatialReference::createLocator(double xmin, double ymin,
00459                                    double xmax, double ymax,
00460                                     bool plate_carre) const
00461 {
00462     int face;
00463     cubeToFace(xmin, ymin, xmax, ymax, face);
00464 
00465     GeoLocator* result = new QscFaceLocator( face );
00466 
00467     osg::Matrixd transform;
00468     transform.set(
00469         xmax-xmin, 0.0,       0.0, 0.0,
00470         0.0,       ymax-ymin, 0.0, 0.0,
00471         0.0,       0.0,       1.0, 0.0,
00472         xmin,      ymin,      0.0, 1.0);
00473     result->setTransform( transform );
00474 
00475     return result;
00476 }
00477 
00478 bool
00479 QscSpatialReference::preTransform(double& x, double& y, void* context) const
00480 {
00481     // Convert the incoming points from cube => face => lat/long.
00482     int face;
00483     if ( !cubeToFace(x, y, face) )
00484     {
00485         OE_WARN << LC << "Failed to convert (" << x << "," << y << ") into face coordinates." << std::endl;
00486         return false;
00487     }
00488 
00489     double lat_deg, lon_deg;
00490     bool success = faceCoordsToLatLon( x, y, face, lat_deg, lon_deg );
00491     if (!success)
00492     {
00493         OE_WARN << LC << "Could not transform face coordinates to lat lon" << std::endl;
00494         return false;
00495     }
00496     x = lon_deg;
00497     y = lat_deg;
00498     return true;
00499 }
00500 
00501 bool
00502 QscSpatialReference::postTransform(double& x, double& y, void* context) const
00503 {
00504     //Convert the incoming points from lat/lon back to face coordinates
00505     int face;
00506     double out_x, out_y;
00507 
00508     // convert from lat/long to x/y/face
00509     bool success = latLonToFaceCoords(y, x, out_x, out_y, face);
00510     if (!success)
00511     {
00512         OE_WARN << LC << "Could not transform face coordinates to lat lon" << std::endl;
00513         return false;
00514     }
00515 
00516     //TODO: what to do about boundary points?
00517 
00518     if ( !faceToCube(out_x, out_y, face))
00519     {
00520         OE_WARN << LC << "fromFace(" << out_x << "," << out_y << "," << face << ") failed" << std::endl;
00521         return false;
00522     }
00523 
00524     x = out_x;
00525     y = out_y;
00526 
00527     return true;
00528 }
00529 
00530 bool
00531 QscSpatialReference::transformExtent(const SpatialReference* to_srs,
00532                                      double& in_out_xmin,
00533                                      double& in_out_ymin,
00534                                      double& in_out_xmax,
00535                                      double& in_out_ymax,
00536                                      void* context ) const
00537 {
00538     // note: this method only works when the extent is isolated to one
00539     // face of the cube. If you want to transform an arbitrary extent,
00540     // you need to break it up into separate extents for each cube face.
00541     bool ok = true;
00542 
00543     double face_xmin = in_out_xmin, face_ymin = in_out_ymin;
00544     double face_xmax = in_out_xmax, face_ymax = in_out_ymax;
00545 
00546     int face;
00547     if (!cubeToFace(face_xmin, face_ymin, face_xmax, face_ymax, face))
00548     {
00549         OE_WARN << LC << "extent (" << in_out_xmin << ", " << in_out_ymin
00550                 << ")=>(" << in_out_xmax << ", " << in_out_ymax <<
00551             ") crosses faces\n";
00552         return false;
00553     }
00554     // The polar and equatorial faces behave the same way, except if
00555     // an extent crosses the pole.
00556 
00557     double lonmin, lonmax, latmin, latmax;
00558     // if the extent crosses the center axes of the face, then,
00559     // due to the curvy nature of the projection, the maximim /
00560     // minimum will be at the crossing. So we may need to sample
00561     // up to 8 points.
00562     double lats[8], lons[8];
00563     int numSamples = 4;
00564     faceCoordsToLatLon(face_xmin, face_ymin, face, lats[0], lons[0]);
00565     faceCoordsToLatLon(face_xmax, face_ymin, face, lats[1], lons[1]);
00566     faceCoordsToLatLon(face_xmin, face_ymax, face, lats[2], lons[2]);
00567     faceCoordsToLatLon(face_xmax, face_ymax, face, lats[3], lons[3]);
00568 
00569     if ((face_xmin < 0 && face_xmax > 0))
00570     {
00571         faceCoordsToLatLon(0.0, face_ymin, face,
00572                            lats[numSamples], lons[numSamples]);
00573         faceCoordsToLatLon(0.0, face_ymax, face,
00574                            lats[numSamples + 1], lons[numSamples + 1]);
00575         numSamples += 2;
00576 
00577     }
00578     if ((face_ymin < 0 && face_ymax > 0))
00579     {
00580         faceCoordsToLatLon(face_xmin, 0.0, face,
00581                            lats[numSamples], lons[numSamples]);
00582         faceCoordsToLatLon(face_xmax, 0.0, face,
00583                            lats[numSamples + 1], lons[numSamples + 1]);
00584         numSamples += 2;
00585 
00586     }
00587     lonmin = *min_element(&lons[0], &lons[numSamples]);
00588     latmin = *min_element(&lats[0], &lats[numSamples]);
00589     lonmax = *max_element(&lons[0], &lons[numSamples]);
00590     latmax = *max_element(&lats[0], &lats[numSamples]);
00591     // Does the extent cross one of the poles?
00592     if ((face == 4 || face == 5) && numSamples == 8)
00593     {
00594         lonmin = -180.0;
00595         lonmax = 180.0;
00596         if (face == 4)
00597             latmax = 90.0;
00598         else
00599             latmin = -90.0;
00600     }
00601     // Check for Date Line crossing
00602     else if (face_xmin < 0 && face_xmax > 0
00603              && (face == 2 || (face == 4 && face_ymin >= 0)
00604                  || (face == 5 && face_ymax <= 0)))
00605     {
00606         std::swap(lonmin, lonmax);
00607     }
00608     if (to_srs->isGeographic())
00609     {
00610         in_out_xmin = lonmin;
00611         in_out_xmax = lonmax;
00612         in_out_ymin = latmin;
00613         in_out_ymax = latmax;
00614     }
00615     else
00616     {
00617         bool ok1 = transform(lonmin, latmin, to_srs, in_out_xmin, in_out_ymin,
00618                              context);
00619         bool ok2 = transform(lonmax, latmax, to_srs, in_out_xmax, in_out_ymax,
00620                              context);
00621         ok = ok1 && ok2;
00622     }
00623     return ok;
00624 }
00625 
00626 namespace
00627 {
00628 SpatialReference* createQscSRS()
00629 {
00630     // root the cube srs with a WGS84 intermediate ellipsoid.
00631     std::string init = "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs";
00632 
00633     SpatialReference* result = 0;
00634     GDAL_SCOPED_LOCK;
00635     void* handle = OSRNewSpatialReference(0);
00636     if ( OSRImportFromProj4( handle, init.c_str() ) == OGRERR_NONE )
00637         {
00638         result = new QscSpatialReference( handle );
00639         }
00640         else
00641         {
00642         OE_WARN << "[osgEarth::SRS] Unable to create SRS: " << init << std::endl;
00643         OSRDestroySpatialReference( handle );
00644         }
00645     return result;
00646 }
00647 
00648 
00649 }
00650 QscProfile::QscProfile()
00651     : Profile(createQscSRS(),
00652               0.0, 0.0, 4.0, 3.0,
00653               -180.0, -90.0, 180.0, 90.0,
00654               0,
00655               4, 3)
00656 {
00657 }
00658 
00659 int QscProfile::getFace(const osgEarth::TileKey* key)
00660 {
00661     int faceX = key->getTileX() >> key->getLevelOfDetail();
00662     int faceY = key->getTileY() >> key->getLevelOfDetail();
00663     if (faceY == 0)
00664         return 5;
00665     else if (faceY == 2)
00666         return 4;
00667     else
00668         return faceX;
00669 }
00670 
00671 void QscProfile::getIntersectingTiles(
00672     const GeoExtent& remoteExtent,
00673     std::vector<TileKey>& out_intersectingKeys ) const
00674 {
00675     OE_FATAL << "QscProfile::getIntersectingTiles not implemented yet!\n";
00676 }
00677 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines