X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fbucket%2Fnewbucket.cxx;h=97b09490208aaa4f4df4ffd9f3313e6f540a10b2;hb=6672a1212cfccb2a08be21ff5271eae9b6f91b2a;hp=9a913628cbf39a777f3fab541cdff8e25c29cfe4;hpb=37b4005d3e117ac9b65d7a83fa69bd29e8092e63;p=simgear.git diff --git a/simgear/bucket/newbucket.cxx b/simgear/bucket/newbucket.cxx index 9a913628..97b09490 100644 --- a/simgear/bucket/newbucket.cxx +++ b/simgear/bucket/newbucket.cxx @@ -15,10 +15,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * $Id$ **************************************************************************/ @@ -28,33 +27,56 @@ # include #endif -#include +#include +#include // some platforms need this for ::snprintf +#include #include +#include #include "newbucket.hxx" // default constructor -SGBucket::SGBucket() { +SGBucket::SGBucket() : + lon(-1000), + lat(-1000), + x(0), + y(0) +{ } +bool SGBucket::isValid() const +{ + // The most northerly valid latitude is 89, not 90. There is no tile + // whose *bottom* latitude is 90. Similar there is no tile whose left egde + // is 180 longitude. + return (lon >= -180) && + (lon < 180) && + (lat >= -90) && + (lat < 90) && + (x < 8) && (y < 8); +} + +void SGBucket::make_bad() +{ + lon = -1000; + lat = -1000; +} + +#ifndef NO_DEPRECATED_API // constructor for specified location SGBucket::SGBucket(const double dlon, const double dlat) { set_bucket(dlon, dlat); } +#endif - -// create an impossible bucket if false -SGBucket::SGBucket(const bool is_good) { - set_bucket(0.0, 0.0); - if ( !is_good ) { - lon = -1000; - } +SGBucket::SGBucket(const SGGeod& geod) { + innerSet(geod.getLongitudeDeg(), + geod.getLatitudeDeg()); } - // Parse a unique scenery tile index and find the lon, lat, x, and y SGBucket::SGBucket(const long int bindex) { long int index = bindex; @@ -73,71 +95,97 @@ SGBucket::SGBucket(const long int bindex) { x = index; } +/* Calculate the greatest integral value less than + * or equal to the given value (floor(x)), + * but attribute coordinates close to the boundary to the next + * (increasing) integral + */ +static int floorWithEpsilon(double x) +{ + return static_cast(floor(x + SG_EPSILON)); +} + +#ifndef NO_DEPRECATED_API -// default destructor -SGBucket::~SGBucket() { +void SGBucket::set_bucket(double dlon, double dlat) +{ + innerSet(dlon, dlat); } -// Set the bucket params for the specified lat and lon -void SGBucket::set_bucket( double *lonlat ) { - set_bucket( lonlat[0], lonlat[1] ); -} +void SGBucket::set_bucket(const SGGeod& geod) +{ + innerSet(geod.getLongitudeDeg(), geod.getLatitudeDeg()); +} +#endif // Set the bucket params for the specified lat and lon -void SGBucket::set_bucket( double dlon, double dlat ) { +void SGBucket::innerSet( double dlon, double dlat ) +{ + if ((dlon < -180.0) || (dlon >= 180.0)) { + SG_LOG(SG_TERRAIN, SG_WARN, "SGBucket::set_bucket: passed longitude:" << dlon); + dlon = SGMiscd::normalizePeriodic(-180.0, 180.0, dlon); + } + + if ((dlat < -90.0) || (dlat > 90.0)) { + SG_LOG(SG_TERRAIN, SG_WARN, "SGBucket::set_bucket: passed latitude" << dlat); + dlat = SGMiscd::clip(dlat, -90.0, 90.0); + } + // - // latitude first + // longitude first // double span = sg_bucket_span( dlat ); - double diff = dlon - (double)(int)dlon; - - // cout << "diff = " << diff << " span = " << span << endl; - - if ( (dlon >= 0) || (fabs(diff) < SG_EPSILON) ) { - lon = (int)dlon; - } else { - lon = (int)dlon - 1; - } - + // we do NOT need to special case lon=180 here, since + // normalizePeriodic will never return 180; it will + // return -180, which is what we want. + lon = floorWithEpsilon(dlon); + // find subdivision or super lon if needed - if ( span < SG_EPSILON ) { - // polar cap - lon = 0; - x = 0; - } else if ( span <= 1.0 ) { - x = (int)((dlon - lon) / span); + if ( span <= 1.0 ) { + /* We have more than one tile per degree of + * longitude, so we need an x offset. + */ + x = floorWithEpsilon((dlon - lon) / span); } else { - if ( (dlon >= 0) || (fabs(diff) < SG_EPSILON) ) { - lon = (int)( (int)(lon / span) * span); - } else { - // cout << " lon = " << lon - // << " tmp = " << (int)((lon-1) / span) << endl; - lon = (int)( (int)((lon + 1) / span) * span - span); - if ( lon < -180 ) { - lon = -180; - } - } - x = 0; + /* We have one or more degrees per tile, + * so we need to find the base longitude + * of that tile. + * + * First we calculate the integral base longitude + * (e.g. -85.5 => -86) and then find the greatest + * multiple of span that is less than or equal to + * that longitude. + * + * That way, the Greenwich Meridian is always + * a tile border. + */ + lon=static_cast(floor(lon / span) * span); + x = 0; } // // then latitude // - diff = dlat - (double)(int)dlat; - - if ( (dlat >= 0) || (fabs(diff) < SG_EPSILON) ) { - lat = (int)dlat; + lat = floorWithEpsilon(dlat); + + // special case when passing in the north pole point (possibly due to + // clipping latitude above). Ensures we generate a valid bucket in this + // scenario + if (lat == 90) { + lat = 89; + y = 7; } else { - lat = (int)dlat - 1; + /* Latitude base and offset are easier, as + * tiles always are 1/8 degree of latitude wide. + */ + y = floorWithEpsilon((dlat - lat) * 8); } - y = (int)((dlat - lat) * 8); } - // Build the path name for this bucket -string SGBucket::gen_base_path() const { +std::string SGBucket::gen_base_path() const { // long int index; int top_lon, top_lat, main_lon, main_lat; char hem, pole; @@ -175,7 +223,7 @@ string SGBucket::gen_base_path() const { main_lat *= -1; } - sprintf(raw_path, "%c%03d%c%02d/%c%03d%c%02d", + ::snprintf(raw_path, 256, "%c%03d%c%02d/%c%03d%c%02d", hem, top_lon, pole, top_lat, hem, main_lon, pole, main_lat); @@ -196,22 +244,37 @@ double SGBucket::get_height() const { return SG_BUCKET_SPAN; } - -// return width of the tile in meters -double SGBucket::get_width_m() const { - double clat = (int)get_center_lat(); - if ( clat > 0 ) { - clat = (int)clat + 0.5; - } else { - clat = (int)clat - 0.5; +double SGBucket::get_highest_lat() const +{ + unsigned char adjustedY = y; + if (lat >= 0) { + // tile is north of the equator, so we want the top edge. Add one + // to y to achieve this. + ++adjustedY; } - double clat_rad = clat * SGD_DEGREES_TO_RADIANS; + + return lat + (adjustedY / 8.0); +} + + +// return width of the tile in meters. This function is used by the +// tile-manager to estimate how many tiles are in the view distance, so +// we care about the smallest width, which occurs at the highest latitude. +double SGBucket::get_width_m() const +{ + double clat_rad = get_highest_lat() * SGD_DEGREES_TO_RADIANS; double cos_lat = cos( clat_rad ); + if (fabs(cos_lat) < SG_EPSILON) { + // happens for polar tiles, since we pass in a latitude of 90 + // return an arbitrary small value so all tiles are loaded + return 10.0; + } + double local_radius = cos_lat * SG_EQUATORIAL_RADIUS_M; double local_perimeter = local_radius * SGD_2PI; double degree_width = local_perimeter / 360.0; - return sg_bucket_span( get_center_lat() ) * degree_width; + return get_width() * degree_width; } @@ -223,7 +286,89 @@ double SGBucket::get_height_m() const { return SG_BUCKET_SPAN * degree_height; } +unsigned int SGBucket::siblings( int dx, int dy, std::vector& buckets ) const +{ + if (!isValid()) { + SG_LOG(SG_TERRAIN, SG_WARN, "SGBucket::sibling: requesting sibling of invalid bucket"); + return 0; + } + + double src_span = sg_bucket_span( get_center_lat() ); + + double clat = get_center_lat() + dy * SG_BUCKET_SPAN; + // return invalid here instead of clipping, so callers can discard + // invalid buckets without having to check if it's an existing one + if ((clat < -90.0) || (clat > 90.0)) { + return 0; + } + + // find the lon span for the new latitude + double trg_span = sg_bucket_span( clat ); + + // if target span < src_span, return multiple buckets... + if ( trg_span < src_span ) { + // calc center longitude of westernmost sibling + double start_lon = get_center_lat() - src_span/2 + trg_span/2; + + unsigned int num_buckets = src_span/trg_span; + for ( unsigned int x = 0; x < num_buckets; x++ ) { + double tmp = start_lon + x * trg_span; + tmp = SGMiscd::normalizePeriodic(-180.0, 180.0, tmp); + + SGBucket b; + b.innerSet(tmp, clat); + + buckets.push_back( b ); + } + } else { + // just return the single sibling + double tmp = get_center_lon() + dx * trg_span; + tmp = SGMiscd::normalizePeriodic(-180.0, 180.0, tmp); + + SGBucket b; + b.innerSet(tmp, clat); + + buckets.push_back( b ); + } + + return buckets.size(); +} +SGBucket SGBucket::sibling(int dx, int dy) const +{ + if (!isValid()) { + SG_LOG(SG_TERRAIN, SG_WARN, "SGBucket::sibling: requesting sibling of invalid bucket"); + return SGBucket(); + } + + double clat = get_center_lat() + dy * SG_BUCKET_SPAN; + // return invalid here instead of clipping, so callers can discard + // invalid buckets without having to check if it's an existing one + if ((clat < -90.0) || (clat > 90.0)) { + return SGBucket(); + } + + // find the lon span for the new latitude + double span = sg_bucket_span( clat ); + + double tmp = get_center_lon() + dx * span; + tmp = SGMiscd::normalizePeriodic(-180.0, 180.0, tmp); + + SGBucket b; + b.innerSet(tmp, clat); + return b; +} + +std::string SGBucket::gen_index_str() const +{ + char tmp[20]; + ::snprintf(tmp, 20, "%ld", + (((long)lon + 180) << 14) + ((lat + 90) << 6) + + (y << 3) + x); + return (std::string)tmp; +} + +#ifndef NO_DEPRECATED_API // find the bucket which is offset by the specified tile units in the // X & Y direction. We need the current lon and lat to resolve // ambiguities when going from a wider tile to a narrower one above or @@ -250,7 +395,7 @@ SGBucket sgBucketOffset( double dlon, double dlat, int dx, int dy ) { return result; } - +#endif // calculate the offset between two buckets void sgBucketDiff( const SGBucket& b1, const SGBucket& b2, int *dx, int *dy ) { @@ -308,4 +453,25 @@ void sgBucketDiff( const SGBucket& b1, const SGBucket& b2, int *dx, int *dy ) { #endif } +void sgGetBuckets( const SGGeod& min, const SGGeod& max, std::vector& list ) { + double lon, lat, span; + + for (lat = min.getLatitudeDeg(); lat < max.getLatitudeDeg()+SG_BUCKET_SPAN; lat += SG_BUCKET_SPAN) { + span = sg_bucket_span( lat ); + for (lon = min.getLongitudeDeg(); lon <= max.getLongitudeDeg(); lon += span) + { + SGBucket b(SGGeod::fromDeg(lon, lat)); + if (!b.isValid()) { + continue; + } + + list.push_back(b); + } + } +} + +std::ostream& operator<< ( std::ostream& out, const SGBucket& b ) +{ + return out << b.lon << ":" << (int)b.x << ", " << b.lat << ":" << (int)b.y; +}