Skip to content

Commit

Permalink
Merge pull request #1055 from CesiumGS/url-template-overlay
Browse files Browse the repository at this point in the history
Add UrlTemplateRasterOverlay
  • Loading branch information
kring authored Jan 16, 2025
2 parents 6dcf071 + 1c9df2f commit 46030bc
Show file tree
Hide file tree
Showing 8 changed files with 442 additions and 4 deletions.
6 changes: 2 additions & 4 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,13 @@

- Added conversion of I3dm batch table metadata to `EXT_structural_metadata` and `EXT_instance_features` extensions.
- Added `CesiumIonClient::Connection::geocode` method for making geocoding queries against the Cesium ion geocoder API.
- Added `UrlTemplateRasterOverlay` for requesting raster tiles from services using a templated URL.
- `upsampleGltfForRasterOverlays` is now compatible with meshes using TRIANGLE_STRIP, TRIANGLE_FAN, or non-indexed TRIANGLES primitives.

##### Fixes :wrench:

- Fixed a crash in `GltfWriter` that would happen when the `EXT_structural_metadata` `schema` property was null.

##### Additions :tada:

- `upsampleGltfForRasterOverlays` is now compatible with meshes using TRIANGLE_STRIP, TRIANGLE_FAN, or non-indexed TRIANGLES primitives.

### v0.43.0 - 2025-01-02

##### Breaking Changes :mega:
Expand Down
13 changes: 13 additions & 0 deletions CesiumGeometry/include/CesiumGeometry/QuadtreeTileID.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,19 @@ struct CESIUMGEOMETRY_API QuadtreeTileID final {
return !(*this == other);
}

/**
* @brief Computes the inverse x-coordinate of this tile ID.
*
* This will compute the inverse x-coordinate of this tile ID, based
* on the given tiling scheme, which provides the number of tiles
* in x-direction for the level of this tile ID.
*
* @param tilingScheme The {@link QuadtreeTilingScheme}.
* @return The inverted x-coordinate.
*/
uint32_t
computeInvertedX(const QuadtreeTilingScheme& tilingScheme) const noexcept;

/**
* @brief Computes the inverse y-coordinate of this tile ID.
*
Expand Down
5 changes: 5 additions & 0 deletions CesiumGeometry/src/QuadtreeTileID.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
#include <functional>

namespace CesiumGeometry {
uint32_t QuadtreeTileID::computeInvertedX(
const QuadtreeTilingScheme& tilingScheme) const noexcept {
const uint32_t xTiles = tilingScheme.getNumberOfXTilesAtLevel(this->level);
return xTiles - this->x - 1;
}

uint32_t QuadtreeTileID::computeInvertedY(
const QuadtreeTilingScheme& tilingScheme) const noexcept {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
#pragma once

#include "IPrepareRasterOverlayRendererResources.h"
#include "Library.h"
#include "RasterOverlayTileProvider.h"

#include <CesiumAsync/AsyncSystem.h>
#include <CesiumAsync/Future.h>
#include <CesiumAsync/IAssetAccessor.h>
#include <CesiumAsync/SharedAssetDepot.h>
#include <CesiumGeometry/QuadtreeTileID.h>
#include <CesiumGeometry/QuadtreeTilingScheme.h>
#include <CesiumRasterOverlays/RasterOverlay.h>
#include <CesiumUtility/CreditSystem.h>
#include <CesiumUtility/Result.h>
#include <CesiumUtility/SharedAsset.h>

#include <list>
#include <memory>
#include <optional>
#include <string>

namespace CesiumRasterOverlays {

/**
* @brief Options for URL template overlays.
*/
struct UrlTemplateRasterOverlayOptions {
/**
* @brief A credit for the data source, which is displayed on the canvas.
*/
std::optional<std::string> credit;

/**
* @brief The {@link CesiumGeospatial::Projection} that is used.
*/
std::optional<CesiumGeospatial::Projection> projection;

/**
* @brief The {@link CesiumGeometry::QuadtreeTilingScheme} specifying how
* the ellipsoidal surface is broken into tiles.
*/
std::optional<CesiumGeometry::QuadtreeTilingScheme> tilingScheme;

/**
* @brief The minimum level-of-detail supported by the imagery provider.
*
* Take care when specifying this that the number of tiles at the minimum
* level is small, such as four or less. A larger number is likely to
* result in rendering problems.
*/
uint32_t minimumLevel = 0;

/**
* @brief The maximum level-of-detail supported by the imagery provider.
*/
uint32_t maximumLevel = 25;

/**
* @brief Pixel width of image tiles.
*/
uint32_t tileWidth = 256;

/**
* @brief Pixel height of image tiles.
*/
uint32_t tileHeight = 256;

/**
* @brief The {@link CesiumGeometry::Rectangle}, in radians, covered by the
* image.
*/
std::optional<CesiumGeometry::Rectangle> coverageRectangle;
};

/**
* @brief A {@link RasterOverlay} accessing images from a templated URL.
*/
class CESIUMRASTEROVERLAYS_API UrlTemplateRasterOverlay final
: public RasterOverlay {
public:
/**
* @brief Creates a new instance.
*
* The following template parameters are supported in `url`:
* - `{x}` - The tile X coordinate in the tiling scheme, where 0 is the westernmost tile.
* - `{y}` - The tile Y coordinate in the tiling scheme, where 0 is the nothernmost tile.
* - `{z}` - The level of the tile in the tiling scheme, where 0 is the root of the quadtree pyramid.
* - `{reverseX}` - The tile X coordinate in the tiling scheme, where 0 is the easternmost tile.
* - `{reverseY}` - The tile Y coordinate in the tiling scheme, where 0 is the southernmost tile.
* - `{reverseZ}` - The tile Z coordinate in the tiling scheme, where 0 is equivalent to `urlTemplateOptions.maximumLevel`.
* - `{westDegrees}` - The western edge of the tile in geodetic degrees.
* - `{southDegrees}` - The southern edge of the tile in geodetic degrees.
* - `{eastDegrees}` - The eastern edge of the tile in geodetic degrees.
* - `{northDegrees}` - The northern edge of the tile in geodetic degrees.
* - `{minimumX}` - The minimum X coordinate of the tile's projected coordinates.
* - `{minimumY}` - The minimum Y coordinate of the tile's projected coordinates.
* - `{maximumX}` - The maximum X coordinate of the tile's projected coordinates.
* - `{maximumY}` - The maximum Y coordinate of the tile's projected coordinates.
* - `{width}` - The width of each tile in pixels.
* - `{height}` - The height of each tile in pixels.
*
* @param name The user-given name of this overlay layer.
* @param url The URL with template parameters.
* @param headers The headers. This is a list of pairs of strings of the
* form (Key,Value) that will be inserted as request headers internally.
* @param urlTemplateOptions The {@link UrlTemplateRasterOverlayOptions}.
* @param overlayOptions The {@link RasterOverlayOptions} for this instance.
*/
UrlTemplateRasterOverlay(
const std::string& name,
const std::string& url,
const std::vector<CesiumAsync::IAssetAccessor::THeader>& headers = {},
const UrlTemplateRasterOverlayOptions& urlTemplateOptions = {},
const RasterOverlayOptions& overlayOptions = {})
: RasterOverlay(name, overlayOptions),
_url(url),
_headers(headers),
_options(urlTemplateOptions) {}

virtual CesiumAsync::Future<CreateTileProviderResult> createTileProvider(
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CesiumUtility::CreditSystem>& pCreditSystem,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
CesiumUtility::IntrusivePointer<const RasterOverlay> pOwner)
const override;

private:
std::string _url;
std::vector<CesiumAsync::IAssetAccessor::THeader> _headers;
UrlTemplateRasterOverlayOptions _options;
};
} // namespace CesiumRasterOverlays
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ QuadtreeRasterOverlayTileProvider::QuadtreeRasterOverlayTileProvider(
}
}
#endif

IntrusivePointer<LoadedQuadtreeImage> pLoadedImage;
pLoadedImage.emplace(
std::make_shared<LoadedRasterOverlayImage>(
Expand Down
195 changes: 195 additions & 0 deletions CesiumRasterOverlays/src/UrlTemplateRasterOverlay.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
#include <CesiumAsync/Future.h>
#include <CesiumAsync/HttpHeaders.h>
#include <CesiumAsync/IAssetAccessor.h>
#include <CesiumGeometry/QuadtreeTileID.h>
#include <CesiumGeometry/QuadtreeTilingScheme.h>
#include <CesiumGeometry/Rectangle.h>
#include <CesiumGeospatial/GeographicProjection.h>
#include <CesiumGeospatial/GlobeRectangle.h>
#include <CesiumGeospatial/Projection.h>
#include <CesiumGeospatial/WebMercatorProjection.h>
#include <CesiumRasterOverlays/IPrepareRasterOverlayRendererResources.h>
#include <CesiumRasterOverlays/QuadtreeRasterOverlayTileProvider.h>
#include <CesiumRasterOverlays/RasterOverlayTileProvider.h>
#include <CesiumRasterOverlays/UrlTemplateRasterOverlay.h>
#include <CesiumUtility/CreditSystem.h>
#include <CesiumUtility/IntrusivePointer.h>
#include <CesiumUtility/Math.h>
#include <CesiumUtility/Uri.h>

#include <spdlog/logger.h>

#include <cstdint>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <variant>
#include <vector>

using namespace CesiumAsync;
using namespace CesiumGeometry;
using namespace CesiumGeospatial;
using namespace CesiumUtility;

namespace CesiumRasterOverlays {

class UrlTemplateRasterOverlayTileProvider final
: public QuadtreeRasterOverlayTileProvider {
public:
UrlTemplateRasterOverlayTileProvider(
const IntrusivePointer<const RasterOverlay>& pOwner,
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<IAssetAccessor>& pAssetAccessor,
std::optional<Credit> credit,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
const CesiumGeospatial::Projection& projection,
const CesiumGeometry::QuadtreeTilingScheme& tilingScheme,
const CesiumGeometry::Rectangle& coverageRectangle,
const std::string& url,
const std::vector<IAssetAccessor::THeader>& headers,
uint32_t width,
uint32_t height,
uint32_t minimumLevel,
uint32_t maximumLevel)
: QuadtreeRasterOverlayTileProvider(
pOwner,
asyncSystem,
pAssetAccessor,
credit,
pPrepareRendererResources,
pLogger,
projection,
tilingScheme,
coverageRectangle,
minimumLevel,
maximumLevel,
width,
height),
_url(url),
_headers(headers) {}

virtual ~UrlTemplateRasterOverlayTileProvider() = default;

protected:
virtual CesiumAsync::Future<LoadedRasterOverlayImage> loadQuadtreeTileImage(
const CesiumGeometry::QuadtreeTileID& tileID) const override {

LoadTileImageFromUrlOptions options;
options.rectangle = this->getTilingScheme().tileToRectangle(tileID);
options.moreDetailAvailable = tileID.level < this->getMaximumLevel();

const GlobeRectangle unprojectedRect =
unprojectRectangleSimple(this->getProjection(), options.rectangle);

const std::map<std::string, std::string, CaseInsensitiveCompare>
placeholdersMap{
{"x", std::to_string(tileID.x)},
{"y", std::to_string(tileID.y)},
{"z", std::to_string(tileID.level)},
{"reverseX",
std::to_string(tileID.computeInvertedX(this->getTilingScheme()))},
{"reverseY",
std::to_string(tileID.computeInvertedY(this->getTilingScheme()))},
{"reverseZ",
std::to_string(this->getMaximumLevel() - tileID.level)},
{"westDegrees",
std::to_string(Math::radiansToDegrees(unprojectedRect.getWest()))},
{"southDegrees",
std::to_string(
Math::radiansToDegrees(unprojectedRect.getSouth()))},
{"eastDegrees",
std::to_string(Math::radiansToDegrees(unprojectedRect.getEast()))},
{"northDegrees",
std::to_string(
Math::radiansToDegrees(unprojectedRect.getNorth()))},
{"minimumY", std::to_string(options.rectangle.minimumY)},
{"minimumX", std::to_string(options.rectangle.minimumX)},
{"maximumY", std::to_string(options.rectangle.maximumY)},
{"maximumX", std::to_string(options.rectangle.maximumX)},
{"width", std::to_string(this->getWidth())},
{"height", std::to_string(this->getHeight())}};

const std::string substitutedUrl = Uri::substituteTemplateParameters(
this->_url,
[&placeholdersMap](const std::string& placeholder) {
auto placeholderIt = placeholdersMap.find(placeholder);
if (placeholderIt != placeholdersMap.end()) {
return placeholderIt->second;
}
return std::string("[UNKNOWN PLACEHOLDER]");
});
return this->loadTileImageFromUrl(
substitutedUrl,
this->_headers,
std::move(options));
}

private:
std::string _url;
std::vector<IAssetAccessor::THeader> _headers;
};

CesiumAsync::Future<RasterOverlay::CreateTileProviderResult>
UrlTemplateRasterOverlay::createTileProvider(
const CesiumAsync::AsyncSystem& asyncSystem,
const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
const std::shared_ptr<CesiumUtility::CreditSystem>& pCreditSystem,
const std::shared_ptr<IPrepareRasterOverlayRendererResources>&
pPrepareRendererResources,
const std::shared_ptr<spdlog::logger>& pLogger,
CesiumUtility::IntrusivePointer<const RasterOverlay> pOwner) const {
pOwner = pOwner ? pOwner : this;

std::optional<Credit> credit = std::nullopt;
if (pCreditSystem && this->_options.credit) {
credit = pCreditSystem->createCredit(
*this->_options.credit,
pOwner->getOptions().showCreditsOnScreen);
}

CesiumGeospatial::Projection projection = _options.projection.value_or(
CesiumGeospatial::WebMercatorProjection(pOwner->getOptions().ellipsoid));
CesiumGeospatial::GlobeRectangle tilingSchemeRectangle =
CesiumGeospatial::WebMercatorProjection::MAXIMUM_GLOBE_RECTANGLE;

uint32_t rootTilesX = 1;
if (std::get_if<CesiumGeospatial::GeographicProjection>(&projection)) {
tilingSchemeRectangle =
CesiumGeospatial::GeographicProjection::MAXIMUM_GLOBE_RECTANGLE;
rootTilesX = 2;
}
CesiumGeometry::Rectangle coverageRectangle =
_options.coverageRectangle.value_or(
projectRectangleSimple(projection, tilingSchemeRectangle));

CesiumGeometry::QuadtreeTilingScheme tilingScheme =
_options.tilingScheme.value_or(CesiumGeometry::QuadtreeTilingScheme(
coverageRectangle,
rootTilesX,
1));

return asyncSystem
.createResolvedFuture<RasterOverlay::CreateTileProviderResult>(
new UrlTemplateRasterOverlayTileProvider(
pOwner,
asyncSystem,
pAssetAccessor,
credit,
pPrepareRendererResources,
pLogger,
projection,
tilingScheme,
coverageRectangle,
_url,
_headers,
_options.tileWidth,
_options.tileHeight,
_options.minimumLevel,
_options.maximumLevel));
}

} // namespace CesiumRasterOverlays
Loading

0 comments on commit 46030bc

Please sign in to comment.