Skip to content

Commit

Permalink
Refactor statics so Rocky does not do any untracked static initializa…
Browse files Browse the repository at this point in the history
…tion; this was causing problems with a self-built/static lib PROJ setup
  • Loading branch information
gwaldron committed Jan 14, 2025
1 parent a19dd5a commit e648b30
Show file tree
Hide file tree
Showing 13 changed files with 81 additions and 66 deletions.
2 changes: 1 addition & 1 deletion src/apps/rocky_engine/rocky_engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ int main(int argc, char** argv)
auto layer = rocky::TMSImageLayer::create();
layer->uri = "https://[abc].tile.openstreetmap.org/{z}/{x}/{y}.png";
layer->attribution = rocky::Hyperlink{ "\u00a9 OpenStreetMap contributors", "https://openstreetmap.org/copyright" };
layer->profile = rocky::Profile::SPHERICAL_MERCATOR;
layer->profile = rocky::Profile("spherical-mercator");
mapNode->map->add(layer);

#endif // ROCKY_HAS_TMS
Expand Down
2 changes: 1 addition & 1 deletion src/rocky/AzureImageLayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ AzureImageLayer::openImplementation(const IOOptions& io)
if (parent.failed())
return parent;

profile = Profile::SPHERICAL_MERCATOR;
profile = Profile("spherical-mercator");
setDataExtents({ profile.extent() });

// copy this so we can add headers
Expand Down
5 changes: 3 additions & 2 deletions src/rocky/BingElevationLayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ BingElevationLayer::openImplementation(const IOOptions& io)
if (parent.failed())
return parent;

profile = Profile(SRS::SPHERICAL_MERCATOR, Profile::SPHERICAL_MERCATOR.extent().bounds(), 2, 2);
Profile SPHERICAL_MERCATOR("spherical-mercator");
profile = Profile(SPHERICAL_MERCATOR.srs(), SPHERICAL_MERCATOR.extent().bounds(), 2, 2);
setDataExtents({ profile.extent() });

ROCKY_TODO("When disk cache is implemented, disable it here as it violates the ToS");
Expand All @@ -88,7 +89,7 @@ BingElevationLayer::createHeightfieldImplementation(const TileKey& key, const IO
if (!isOpen())
return status();

GeoExtent latLongExtent = Profile::GLOBAL_GEODETIC.clampAndTransformExtent(key.extent());
GeoExtent latLongExtent = Profile("global-geodetic").clampAndTransformExtent(key.extent());

unsigned tileSize = 32;
std::stringstream query;
Expand Down
3 changes: 2 additions & 1 deletion src/rocky/BingImageLayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ BingImageLayer::openImplementation(const IOOptions& io)
return parent;

// Bing has a root 2x2 tile setup unlike most web-mercator sources:
profile = Profile(SRS::SPHERICAL_MERCATOR, Profile::SPHERICAL_MERCATOR.extent().bounds(), 2, 2);
Profile SPHERICAL_MERCATOR("spherical-mercator");
profile = Profile(SPHERICAL_MERCATOR.srs(), SPHERICAL_MERCATOR.extent().bounds(), 2, 2);

setDataExtents({ profile.extent() });

Expand Down
4 changes: 2 additions & 2 deletions src/rocky/MBTiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ MBTiles::Driver::open(
Log()->warn(LC "Profile \"" + profileStr + "\" not recognized; defaulting to spherical-mercator");
}

profile = Profile::SPHERICAL_MERCATOR;
profile = Profile("spherical-mercator");
}
}

Expand Down Expand Up @@ -655,7 +655,7 @@ MBTiles::Driver::setDataExtents(const DataExtentList& values)
}
else
{
bounds = Profile::GLOBAL_GEODETIC.clampAndTransformExtent(e);
bounds = Profile("global-geodetic").clampAndTransformExtent(e);
}
std::stringstream boundsStr;
boundsStr << bounds.xmin() << "," << bounds.ymin() << "," << bounds.xmax() << "," << bounds.ymax();
Expand Down
7 changes: 5 additions & 2 deletions src/rocky/Profile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -410,15 +410,18 @@ Profile::getEquivalentLOD(const Profile& rhsProfile, unsigned rhsLOD) const
{
ROCKY_SOFT_ASSERT_AND_RETURN(rhsProfile.valid(), rhsLOD);

static const Profile SPHERICAL_MERCATOR("spherical-mercator");
static const Profile GLOBAL_GEODETIC("global-geodetic");

//If the profiles are equivalent, just use the incoming lod
if (horizontallyEquivalentTo(rhsProfile))
return rhsLOD;

// Special check for geodetic to mercator or vise versa, they should match up in LOD.
if (rhsProfile.horizontallyEquivalentTo(Profile::SPHERICAL_MERCATOR) && horizontallyEquivalentTo(Profile::GLOBAL_GEODETIC))
if (rhsProfile.horizontallyEquivalentTo(SPHERICAL_MERCATOR) && horizontallyEquivalentTo(GLOBAL_GEODETIC))
return rhsLOD;

if (rhsProfile.horizontallyEquivalentTo(Profile::GLOBAL_GEODETIC) && horizontallyEquivalentTo(Profile::SPHERICAL_MERCATOR))
if (rhsProfile.horizontallyEquivalentTo(GLOBAL_GEODETIC) && horizontallyEquivalentTo(SPHERICAL_MERCATOR))
return rhsLOD;

auto[rhsWidth, rhsHeight] = rhsProfile.tileDimensions(rhsLOD);
Expand Down
16 changes: 7 additions & 9 deletions src/rocky/Profile.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,17 @@ namespace ROCKY_NAMESPACE
*/
class ROCKY_EXPORT Profile
{
public:
// well knowns
static const Profile GLOBAL_GEODETIC;
static const Profile SPHERICAL_MERCATOR;
static const Profile PLATE_CARREE;
static const Profile MOON;

public:
//! Construct an empty, invalid profile
Profile();

//! Construct a profile from a well-know name
//! (See the well-known constants above)
//! Construct a profile from a well-know name or initialization string
//! Can be one of
//! global-geodetic
//! spherical-mercator
//! plate-carree
//! moon
//! .. or any valid +proj initialization string
explicit Profile(const std::string& well_known_name);

//! Construct a profile
Expand Down
62 changes: 40 additions & 22 deletions src/rocky/SRS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -530,20 +530,7 @@ SRS::SRS()
SRS::SRS(const std::string& h) :
_definition(h)
{
PJ* pj = g_srs_factory.get_or_create(h).pj;
_valid = (pj != nullptr);

if (_valid)
{
// cache things that get called a LOT
auto type = g_srs_factory.get_horiz_crs_type(h);

_isGeodetic =
type == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
type == PJ_TYPE_GEOGRAPHIC_3D_CRS;

_isGeocentric = type == PJ_TYPE_GEOCENTRIC_CRS;
}
//nop
}

const char*
Expand All @@ -554,16 +541,44 @@ SRS::name() const
return proj_get_name(pj);
}

bool
SRS::valid() const
{
if (!_valid.has_value())
{
_valid = !_definition.empty() && g_srs_factory.get_or_create(_definition).pj != nullptr;
}
return _valid.value();
}

bool
SRS::isGeodetic() const
{
return valid() && _isGeodetic;
if (!valid())
return false;

if (!_crs_type.has_value())
{
_crs_type = g_srs_factory.get_horiz_crs_type(_definition);
}

return
(PJ_TYPE)_crs_type.value() == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
(PJ_TYPE)_crs_type.value() == PJ_TYPE_GEOGRAPHIC_3D_CRS;
}

bool
SRS::isGeocentric() const
{
return valid() && _isGeocentric;
if (!valid())
return false;

if (!_crs_type.has_value())
{
_crs_type = (int)g_srs_factory.get_horiz_crs_type(_definition);
}

return (PJ_TYPE)_crs_type.value() == PJ_TYPE_GEOCENTRIC_CRS;
}

bool
Expand All @@ -572,9 +587,12 @@ SRS::isProjected() const
if (!valid())
return false;

auto type = g_srs_factory.get_horiz_crs_type(definition());
return
type == PJ_TYPE_PROJECTED_CRS;
if (!_crs_type.has_value())
{
_crs_type = (int)g_srs_factory.get_horiz_crs_type(_definition);
}

return (PJ_TYPE)_crs_type.value() == PJ_TYPE_PROJECTED_CRS;
}

bool
Expand All @@ -601,7 +619,7 @@ SRS::equivalentTo(const SRS& rhs) const
return false;

PJ_COMPARISON_CRITERION criterion =
_isGeodetic ? PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS :
isGeodetic() ? PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS :
PJ_COMP_EQUIVALENT;

return proj_is_equivalent_to_with_ctx(
Expand All @@ -614,7 +632,7 @@ SRS::horizontallyEquivalentTo(const SRS& rhs) const
if (definition().empty() || rhs.definition().empty())
return false;

if (_isGeodetic && rhs._isGeodetic && ellipsoid() == rhs.ellipsoid())
if (isGeodetic() && rhs.isGeodetic() && ellipsoid() == rhs.ellipsoid())
return true;

auto& lhs_entry = g_srs_factory.get_or_create(definition());
Expand All @@ -641,7 +659,7 @@ SRS::horizontallyEquivalentTo(const SRS& rhs) const
proj_crs_get_sub_crs(g_srs_factory.threading_context(), pj2, 0) : pj2;

PJ_COMPARISON_CRITERION criterion =
_isGeodetic ? PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS :
isGeodetic() ? PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS :
PJ_COMP_EQUIVALENT;

return proj_is_equivalent_to_with_ctx(
Expand Down
10 changes: 4 additions & 6 deletions src/rocky/SRS.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,8 @@ namespace ROCKY_NAMESPACE
}

//! Whether this is a valid SRS
inline bool valid() const {
return _valid;
}
bool valid() const;

inline operator bool() const {
return valid();
}
Expand Down Expand Up @@ -166,9 +165,8 @@ namespace ROCKY_NAMESPACE
private:
//! Create an SRS from an initialization string.
std::string _definition;
bool _valid = false;
bool _isGeodetic = false;
bool _isGeocentric = false;
mutable std::optional<bool> _valid;
mutable std::optional<int> _crs_type;
friend class SRSOperation;
};

Expand Down
20 changes: 11 additions & 9 deletions src/rocky/TMS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,9 @@ void TileMap::computeNumTiles()
Profile
TileMap::createProfile() const
{
static const Profile GLOBAL_GEODETIC("global-geodetic");
static const Profile SPHERICAL_MERCATOR("spherical-mercator");

Profile profile;

std::string def = srsString;
Expand All @@ -306,26 +309,25 @@ TileMap::createProfile() const

if (profileType == ProfileType::GEODETIC)
{
profile = Profile::GLOBAL_GEODETIC;
profile = GLOBAL_GEODETIC;
}
else if (profileType == ProfileType::MERCATOR)
{
profile = Profile::SPHERICAL_MERCATOR;
profile = SPHERICAL_MERCATOR;
}
else if (new_srs.horizontallyEquivalentTo(SRS::SPHERICAL_MERCATOR))
{
//HACK: Some TMS sources, most notably TileCache, use a global mercator extent that is very slightly different than
// the automatically computed mercator bounds which can cause rendering issues due to the some texture coordinates
// crossing the dateline. If the incoming bounds are nearly the same as our definion of global mercator, just use our definition.
double eps = 1.0;
Profile merc(Profile::SPHERICAL_MERCATOR);
if (numTilesWide == 1 && numTilesHigh == 1 &&
equiv(merc.extent().xmin(), minX, eps) &&
equiv(merc.extent().ymin(), minY, eps) &&
equiv(merc.extent().xmax(), maxX, eps) &&
equiv(merc.extent().ymax(), maxY, eps))
equiv(SPHERICAL_MERCATOR.extent().xmin(), minX, eps) &&
equiv(SPHERICAL_MERCATOR.extent().ymin(), minY, eps) &&
equiv(SPHERICAL_MERCATOR.extent().xmax(), maxX, eps) &&
equiv(SPHERICAL_MERCATOR.extent().ymax(), maxY, eps))
{
profile = merc;
profile = SPHERICAL_MERCATOR;
}
}

Expand All @@ -336,7 +338,7 @@ TileMap::createProfile() const
equiv(minY, -90.) &&
equiv(maxY, 90.))
{
profile = Profile::GLOBAL_GEODETIC;
profile = GLOBAL_GEODETIC;
}

if (!profile.valid())
Expand Down
7 changes: 0 additions & 7 deletions src/rocky/static.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,5 @@ const SRS SRS::MOON("moon");
const SRS SRS::EMPTY;
std::function<void(int level, const char* msg)> SRS::projMessageCallback = nullptr;

const Profile Profile::GLOBAL_GEODETIC("global-geodetic");
const Profile Profile::SPHERICAL_MERCATOR("spherical-mercator");
const Profile Profile::PLATE_CARREE("plate-carree");
const Profile Profile::MOON("moon");

//Status Context::_global_status(Status::GeneralError);

// job system definition
WEEJOBS_INSTANCE;
4 changes: 2 additions & 2 deletions src/rocky/vsg/MapNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ MapNode::MapNode()
this->addChild(_layerNodes);

// default to geodetic:
profile = Profile::GLOBAL_GEODETIC;
profile = Profile("global-geodetic");
}

Status
Expand Down Expand Up @@ -74,7 +74,7 @@ MapNode::to_json() const
j["terrain"] = json::parse(terrainNode->to_json());
}

if (profile.valid() && profile != Profile::GLOBAL_GEODETIC)
if (profile.valid() && profile != Profile("global-geodetic"))
{
j["profile"] = profile;
}
Expand Down
5 changes: 3 additions & 2 deletions src/tests/tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ TEST_CASE("Optional")

TEST_CASE("TileKey")
{
auto p = Profile::GLOBAL_GEODETIC;
Profile p("global-geodetic");

CHECK(TileKey(0, 0, 0, p).str() == "0/0/0");
CHECK(TileKey(0, 0, 0, p).quadKey() == "0");
Expand Down Expand Up @@ -496,13 +496,14 @@ TEST_CASE("SRS")
SRS::projMessageCallback = [&](int level, const char* msg) { proj_error = msg; };

SRS bad("gibberish");
CHECK(proj_error == "proj_create: unrecognized format / unknown name");

CHECK(bad.valid() == false);
CHECK(bad.isProjected() == false);
CHECK(bad.isGeodetic() == false);
CHECK(bad.isGeocentric() == false);

CHECK(proj_error == "proj_create: unrecognized format / unknown name");

SRS::projMessageCallback = nullptr;
}

Expand Down

0 comments on commit e648b30

Please sign in to comment.