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 "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 }