Merge branch '3dtiles-voxel-content' into tilers-lilleyse
This commit is contained in:
commit
80362072cb
21
CHANGES.md
21
CHANGES.md
|
|
@ -1,5 +1,22 @@
|
|||
# Change Log
|
||||
|
||||
### ? - ?
|
||||
|
||||
##### Breaking Changes :mega:
|
||||
|
||||
- Removed `TilesetOptions::maximumSimultaneousSubtreeLoads` because it was unused.
|
||||
|
||||
### Additions :tada:
|
||||
|
||||
- Added `convertPropertyComponentTypeToAccessorComponentType` to `PropertyType`.
|
||||
- Added support for `3DTILES_ellipsoid` in `Cesium3DTiles`, `Cesium3DTilesReader`, and `Cesium3DTilesWriter`.
|
||||
|
||||
### v0.44.1 - 2025-02-03
|
||||
|
||||
##### Fixes :wrench:
|
||||
|
||||
- Fixed a bug in `CesiumIonClient::Connection` that caused the `authorize` method to use an incorrect URL.
|
||||
|
||||
### v0.44.0 - 2025-02-03
|
||||
|
||||
##### Breaking Changes :mega:
|
||||
|
|
@ -18,8 +35,6 @@
|
|||
- 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.
|
||||
- Added `requestHeaders` field to `TilesetOptions` to allow per-tileset request headers to be specified.
|
||||
- Added support for `3DTILES_ellipsoid` in `CesiumGltf`, `CesiumGltfReader`, and `CesiumGltfWriter`.
|
||||
- Added `convertPropertyComponentTypeToAccessorComponentType` to `PropertyType`.
|
||||
|
||||
##### Fixes :wrench:
|
||||
|
||||
|
|
@ -27,7 +42,9 @@
|
|||
- Fixed a bug in `SharedAssetDepot` that could cause assertion failures in debug builds, and could rarely cause premature deletion of shared assets even in release builds.
|
||||
- Fixed a bug that could cause `Tileset::sampleHeightMostDetailed` to return a height that is not the highest one when the sampled tileset contained multiple heights at the given location.
|
||||
- `LayerJsonTerrainLoader` will now log errors and warnings when failing to load a `.terrain` file referenced in the layer.json, instead of silently ignoring them.
|
||||
- URIs containing unicode characters are now supported.
|
||||
- Fixed a crash in `CullingVolume` when the camera was very far away from the globe.
|
||||
- Fixed a bug that prevented the `culture` parameter of the `BingMapsRasterOverlay` from having an effect.
|
||||
|
||||
### v0.43.0 - 2025-01-02
|
||||
|
||||
|
|
|
|||
|
|
@ -46,8 +46,8 @@ message(STATUS "VCPKG_OVERLAY_TRIPLETS ${VCPKG_OVERLAY_TRIPLETS}")
|
|||
# These packages are used in the public headers of Cesium libraries, so we need to distribute the headers and binaries
|
||||
# with the installation
|
||||
# Note that fmt is a public dependency of the vcpkg version of spdlog
|
||||
# STB and uriparser aren't technically part of the public interface, but they're used by the downstream Cesium for Unreal project
|
||||
set(PACKAGES_PUBLIC asyncplusplus expected-lite fmt glm rapidjson spdlog stb uriparser)
|
||||
# STB is not technically part of the public interface, but it is used by the downstream Cesium for Unreal project
|
||||
set(PACKAGES_PUBLIC asyncplusplus expected-lite fmt glm rapidjson spdlog stb ada-url)
|
||||
# These packages are used in the code and produce binaries, but are not part of the public interface. Therefore we need
|
||||
# to distribute the binaries for linking, but not the headers, as downstream consumers don't need them
|
||||
# OpenSSL and abseil are both dependencies of s2geometry
|
||||
|
|
@ -87,7 +87,7 @@ endif()
|
|||
include("cmake/defaults.cmake")
|
||||
|
||||
project(cesium-native
|
||||
VERSION 0.43.0
|
||||
VERSION 0.44.0
|
||||
LANGUAGES CXX C
|
||||
)
|
||||
|
||||
|
|
@ -257,8 +257,8 @@ find_package(s2 CONFIG REQUIRED)
|
|||
find_package(spdlog CONFIG REQUIRED)
|
||||
find_package(tinyxml2 CONFIG REQUIRED)
|
||||
find_package(unofficial-sqlite3 CONFIG REQUIRED)
|
||||
find_package(uriparser CONFIG REQUIRED char wchar_t)
|
||||
find_package(WebP CONFIG REQUIRED)
|
||||
find_package(ada CONFIG REQUIRED)
|
||||
|
||||
|
||||
# Private Library (s2geometry)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
using namespace Cesium3DTiles;
|
||||
|
|
|
|||
|
|
@ -54,7 +54,6 @@ target_link_libraries(Cesium3DTilesSelection
|
|||
CesiumUtility
|
||||
spdlog::spdlog spdlog::spdlog_header_only
|
||||
# PRIVATE
|
||||
uriparser::uriparser
|
||||
libmorton::libmorton
|
||||
draco::draco
|
||||
nonstd::expected-lite
|
||||
|
|
|
|||
|
|
@ -102,12 +102,6 @@ struct CESIUM3DTILESSELECTION_API TilesetOptions {
|
|||
*/
|
||||
uint32_t maximumSimultaneousTileLoads = 20;
|
||||
|
||||
/**
|
||||
* @brief The maximum number of subtrees that may simultaneously be in the
|
||||
* process of loading.
|
||||
*/
|
||||
uint32_t maximumSimultaneousSubtreeLoads = 20;
|
||||
|
||||
/**
|
||||
* @brief Indicates whether the ancestors of rendered tiles should be
|
||||
* preloaded. Setting this to true optimizes the zoom-out experience and
|
||||
|
|
|
|||
|
|
@ -340,13 +340,6 @@ Future<LoadLayersResult> loadLayersRecursive(
|
|||
std::string extensionsToRequest =
|
||||
createExtensionsQueryParameter(knownExtensions, extensions);
|
||||
|
||||
if (!extensionsToRequest.empty()) {
|
||||
for (std::string& url : urls) {
|
||||
url =
|
||||
CesiumUtility::Uri::addQuery(url, "extensions", extensionsToRequest);
|
||||
}
|
||||
}
|
||||
|
||||
const auto availabilityLevelsIt =
|
||||
layerJson.FindMember("metadataAvailability");
|
||||
|
||||
|
|
@ -379,6 +372,7 @@ Future<LoadLayersResult> loadLayersRecursive(
|
|||
baseUrl,
|
||||
std::move(version),
|
||||
std::move(urls),
|
||||
std::move(extensionsToRequest),
|
||||
std::move(availability),
|
||||
static_cast<uint32_t>(maxZoom),
|
||||
availabilityLevels);
|
||||
|
|
@ -648,12 +642,14 @@ LayerJsonTerrainLoader::Layer::Layer(
|
|||
const std::string& baseUrl_,
|
||||
std::string&& version_,
|
||||
std::vector<std::string>&& tileTemplateUrls_,
|
||||
std::string&& extensionsToRequest_,
|
||||
CesiumGeometry::QuadtreeRectangleAvailability&& contentAvailability_,
|
||||
uint32_t maxZooms_,
|
||||
int32_t availabilityLevels_)
|
||||
: baseUrl{baseUrl_},
|
||||
version{std::move(version_)},
|
||||
tileTemplateUrls{std::move(tileTemplateUrls_)},
|
||||
extensionsToRequest{std::move(extensionsToRequest_)},
|
||||
contentAvailability{std::move(contentAvailability_)},
|
||||
loadedSubtrees(maxSubtreeInLayer(maxZooms_, availabilityLevels_)),
|
||||
availabilityLevels{availabilityLevels_} {}
|
||||
|
|
@ -675,9 +671,9 @@ std::string resolveTileUrl(
|
|||
return std::string();
|
||||
}
|
||||
|
||||
return CesiumUtility::Uri::resolve(
|
||||
Uri uri(
|
||||
layer.baseUrl,
|
||||
CesiumUtility::Uri::substituteTemplateParameters(
|
||||
Uri::substituteTemplateParameters(
|
||||
layer.tileTemplateUrls[0],
|
||||
[&tileID, &layer](const std::string& placeholder) -> std::string {
|
||||
if (placeholder == "level" || placeholder == "z") {
|
||||
|
|
@ -695,6 +691,14 @@ std::string resolveTileUrl(
|
|||
|
||||
return placeholder;
|
||||
}));
|
||||
|
||||
if (!layer.extensionsToRequest.empty()) {
|
||||
UriQuery params(uri);
|
||||
params.setValue("extensions", layer.extensionsToRequest);
|
||||
uri.setQuery(params.toQueryString());
|
||||
}
|
||||
|
||||
return std::string(uri.toString());
|
||||
}
|
||||
|
||||
Future<QuantizedMeshLoadResult> requestTileContent(
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ public:
|
|||
const std::string& baseUrl,
|
||||
std::string&& version,
|
||||
std::vector<std::string>&& tileTemplateUrls,
|
||||
std::string&& extensionsToRequest,
|
||||
CesiumGeometry::QuadtreeRectangleAvailability&& contentAvailability,
|
||||
uint32_t maxZooms,
|
||||
int32_t availabilityLevels);
|
||||
|
|
@ -59,6 +60,7 @@ public:
|
|||
std::string baseUrl;
|
||||
std::string version;
|
||||
std::vector<std::string> tileTemplateUrls;
|
||||
std::string extensionsToRequest;
|
||||
CesiumGeometry::QuadtreeRectangleAvailability contentAvailability;
|
||||
std::vector<std::unordered_set<uint64_t>> loadedSubtrees;
|
||||
int32_t availabilityLevels;
|
||||
|
|
|
|||
|
|
@ -270,7 +270,8 @@ TEST_CASE("Test create layer json terrain loader") {
|
|||
CHECK(layers[0].version == "1.33.0");
|
||||
CHECK(
|
||||
layers[0].tileTemplateUrls.front() ==
|
||||
"{z}/{x}/{y}.terrain?v={version}&extensions=octvertexnormals-metadata");
|
||||
"{z}/{x}/{y}.terrain?v={version}");
|
||||
CHECK(layers[0].extensionsToRequest == "octvertexnormals-metadata");
|
||||
CHECK(layers[0].loadedSubtrees.size() == 2);
|
||||
CHECK(layers[0].availabilityLevels == 10);
|
||||
}
|
||||
|
|
@ -299,7 +300,8 @@ TEST_CASE("Test create layer json terrain loader") {
|
|||
CHECK(layers[0].version == "1.0.0");
|
||||
CHECK(
|
||||
layers[0].tileTemplateUrls.front() ==
|
||||
"{z}/{x}/{y}.terrain?v={version}&extensions=octvertexnormals");
|
||||
"{z}/{x}/{y}.terrain?v={version}");
|
||||
CHECK(layers[0].extensionsToRequest == "octvertexnormals");
|
||||
CHECK(layers[0].loadedSubtrees.empty());
|
||||
CHECK(layers[0].availabilityLevels == -1);
|
||||
|
||||
|
|
@ -322,7 +324,7 @@ TEST_CASE("Test create layer json terrain loader") {
|
|||
auto parentJsonPath =
|
||||
testDataPath / "CesiumTerrainTileJson" / "Parent.tile.json";
|
||||
pMockedAssetAccessor->mockCompletedRequests.insert(
|
||||
{"./Parent/layer.json", createMockAssetRequest(parentJsonPath)});
|
||||
{"Parent/layer.json", createMockAssetRequest(parentJsonPath)});
|
||||
|
||||
auto loaderFuture =
|
||||
LayerJsonTerrainLoader::createLoader(externals, {}, "layer.json", {});
|
||||
|
|
@ -418,10 +420,8 @@ TEST_CASE("Test create layer json terrain loader") {
|
|||
const auto& layers = loaderResult.pLoader->getLayers();
|
||||
CHECK(layers.size() == 1);
|
||||
CHECK(layers[0].tileTemplateUrls.size() == 1);
|
||||
CHECK(
|
||||
layers[0].tileTemplateUrls[0] ==
|
||||
"{z}/{x}/"
|
||||
"{y}.terrain?v={version}&extensions=octvertexnormals-watermask");
|
||||
CHECK(layers[0].tileTemplateUrls[0] == "{z}/{x}/{y}.terrain?v={version}");
|
||||
CHECK(layers[0].extensionsToRequest == "octvertexnormals-watermask");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -452,6 +452,7 @@ TEST_CASE("Test load layer json tile content") {
|
|||
"layer.json",
|
||||
"1.0.0",
|
||||
std::vector<std::string>{"{level}.{x}.{y}/{version}.terrain"},
|
||||
"one-two",
|
||||
std::move(contentAvailability),
|
||||
maxZoom,
|
||||
10);
|
||||
|
|
@ -460,7 +461,7 @@ TEST_CASE("Test load layer json tile content") {
|
|||
|
||||
// mock tile content request
|
||||
pMockedAssetAccessor->mockCompletedRequests.insert(
|
||||
{"0.0.0/1.0.0.terrain",
|
||||
{"0.0.0/1.0.0.terrain?extensions=one-two",
|
||||
createMockAssetRequest(
|
||||
testDataPath / "CesiumTerrainTileJson" /
|
||||
"tile.metadataavailability.terrain")});
|
||||
|
|
@ -502,6 +503,7 @@ TEST_CASE("Test load layer json tile content") {
|
|||
"layer.json",
|
||||
"1.0.0",
|
||||
std::vector<std::string>{"{level}.{x}.{y}/{version}.terrain"},
|
||||
std::string(),
|
||||
std::move(contentAvailability),
|
||||
maxZoom,
|
||||
-1);
|
||||
|
|
@ -563,6 +565,7 @@ TEST_CASE("Test load layer json tile content") {
|
|||
"layer.json",
|
||||
"1.0.0",
|
||||
std::vector<std::string>{"{level}.{x}.{y}/{version}_layer0.terrain"},
|
||||
std::string(),
|
||||
std::move(layer0ContentAvailability),
|
||||
maxZoom,
|
||||
-1);
|
||||
|
|
@ -577,6 +580,7 @@ TEST_CASE("Test load layer json tile content") {
|
|||
"layer.json",
|
||||
"1.0.0",
|
||||
std::vector<std::string>{"{level}.{x}.{y}/{version}_layer1.terrain"},
|
||||
std::string(),
|
||||
std::move(layer1ContentAvailability),
|
||||
maxZoom,
|
||||
-1);
|
||||
|
|
@ -639,6 +643,7 @@ TEST_CASE("Test load layer json tile content") {
|
|||
"layer.json",
|
||||
"1.0.0",
|
||||
std::vector<std::string>{"{level}.{x}.{y}/{version}_layer0.terrain"},
|
||||
std::string(),
|
||||
std::move(layer0ContentAvailability),
|
||||
maxZoom,
|
||||
10);
|
||||
|
|
@ -650,6 +655,7 @@ TEST_CASE("Test load layer json tile content") {
|
|||
"layer.json",
|
||||
"1.0.0",
|
||||
std::vector<std::string>{"{level}.{x}.{y}/{version}_layer1.terrain"},
|
||||
std::string(),
|
||||
std::move(layer1ContentAvailability),
|
||||
maxZoom,
|
||||
10);
|
||||
|
|
@ -724,6 +730,7 @@ TEST_CASE("Test load layer json tile content") {
|
|||
"layer.json",
|
||||
"1.0.0",
|
||||
std::vector<std::string>{"{level}.{x}.{y}/{version}.terrain"},
|
||||
std::string{},
|
||||
std::move(contentAvailability),
|
||||
maxZoom,
|
||||
10);
|
||||
|
|
@ -784,6 +791,7 @@ TEST_CASE("Test creating tile children for layer json") {
|
|||
"layer.json",
|
||||
"1.0.0",
|
||||
std::vector<std::string>{"{level}.{x}.{y}/{version}_layer0.terrain"},
|
||||
std::string(),
|
||||
std::move(layer0ContentAvailability),
|
||||
maxZoom,
|
||||
10);
|
||||
|
|
@ -799,6 +807,7 @@ TEST_CASE("Test creating tile children for layer json") {
|
|||
"layer.json",
|
||||
"1.0.0",
|
||||
std::vector<std::string>{"{level}.{x}.{y}/{version}_layer1.terrain"},
|
||||
std::string(),
|
||||
std::move(layer1ContentAvailability),
|
||||
maxZoom,
|
||||
10);
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
"geometricError": 0,
|
||||
"refine": "ADD",
|
||||
"content": {
|
||||
"uri": "tileset3/ll.b3dm"
|
||||
"uri": "ll.b3dm"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,8 +29,6 @@
|
|||
#include <rapidjson/rapidjson.h>
|
||||
#include <rapidjson/stringbuffer.h>
|
||||
#include <rapidjson/writer.h>
|
||||
#include <uriparser/Uri.h>
|
||||
#include <uriparser/UriBase.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
|
|
@ -173,7 +171,7 @@ std::string createAuthorizationErrorHtml(
|
|||
authorizeUrl =
|
||||
Uri::addQuery(authorizeUrl, "client_id", std::to_string(clientID));
|
||||
authorizeUrl =
|
||||
Uri::addQuery(authorizeUrl, "scope", joinToString(scopes, "%20"));
|
||||
Uri::addQuery(authorizeUrl, "scope", joinToString(scopes, " "));
|
||||
authorizeUrl = Uri::addQuery(authorizeUrl, "redirect_uri", redirectUrl);
|
||||
authorizeUrl = Uri::addQuery(authorizeUrl, "state", state);
|
||||
authorizeUrl = Uri::addQuery(authorizeUrl, "code_challenge_method", "S256");
|
||||
|
|
@ -495,19 +493,18 @@ Asset jsonToAsset(const rapidjson::Value& item) {
|
|||
}
|
||||
|
||||
std::optional<std::string> generateApiUrl(const std::string& ionUrl) {
|
||||
UriUriA newUri;
|
||||
if (uriParseSingleUriA(&newUri, ionUrl.c_str(), nullptr) != URI_SUCCESS) {
|
||||
return std::optional<std::string>();
|
||||
Uri parsedIonUrl(ionUrl);
|
||||
if (!parsedIonUrl) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::string hostName =
|
||||
std::string(newUri.hostText.first, newUri.hostText.afterLast);
|
||||
std::string scheme =
|
||||
std::string(newUri.scheme.first, newUri.scheme.afterLast);
|
||||
std::string url;
|
||||
url.append(parsedIonUrl.getScheme());
|
||||
url.append("//api.");
|
||||
url.append(parsedIonUrl.getHost());
|
||||
url.append("/");
|
||||
|
||||
uriFreeUriMembersA(&newUri);
|
||||
|
||||
return std::make_optional<std::string>(scheme + "://api." + hostName + '/');
|
||||
return url;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
|
|||
|
|
@ -132,49 +132,46 @@ public:
|
|||
width,
|
||||
height),
|
||||
_credits(perTileCredits),
|
||||
_baseUrl(baseUrl),
|
||||
_urlTemplate(urlTemplate),
|
||||
_subdomains(subdomains) {
|
||||
if (this->_urlTemplate.find("n=z") == std::string::npos) {
|
||||
this->_urlTemplate =
|
||||
CesiumUtility::Uri::addQuery(this->_urlTemplate, "n", "z");
|
||||
}
|
||||
|
||||
std::string resolvedUrl =
|
||||
CesiumUtility::Uri::resolve(baseUrl, this->_urlTemplate);
|
||||
|
||||
resolvedUrl = CesiumUtility::Uri::substituteTemplateParameters(
|
||||
resolvedUrl,
|
||||
[&culture](const std::string& templateKey) {
|
||||
if (templateKey == "culture") {
|
||||
return culture;
|
||||
}
|
||||
|
||||
// Keep other placeholders
|
||||
return "{" + templateKey + "}";
|
||||
});
|
||||
}
|
||||
_culture(culture),
|
||||
_subdomains(subdomains) {}
|
||||
|
||||
virtual ~BingMapsTileProvider() = default;
|
||||
|
||||
protected:
|
||||
virtual CesiumAsync::Future<LoadedRasterOverlayImage> loadQuadtreeTileImage(
|
||||
const CesiumGeometry::QuadtreeTileID& tileID) const override {
|
||||
std::string url = CesiumUtility::Uri::substituteTemplateParameters(
|
||||
this->_urlTemplate,
|
||||
[this, &tileID](const std::string& key) {
|
||||
if (key == "quadkey") {
|
||||
return BingMapsTileProvider::tileXYToQuadKey(
|
||||
tileID.level,
|
||||
tileID.x,
|
||||
tileID.computeInvertedY(this->getTilingScheme()));
|
||||
}
|
||||
if (key == "subdomain") {
|
||||
const size_t subdomainIndex =
|
||||
(tileID.level + tileID.x + tileID.y) % this->_subdomains.size();
|
||||
return this->_subdomains[subdomainIndex];
|
||||
}
|
||||
return key;
|
||||
});
|
||||
Uri uri(
|
||||
this->_baseUrl,
|
||||
CesiumUtility::Uri::substituteTemplateParameters(
|
||||
this->_urlTemplate,
|
||||
[this, &tileID](const std::string& key) {
|
||||
if (key == "quadkey") {
|
||||
return BingMapsTileProvider::tileXYToQuadKey(
|
||||
tileID.level,
|
||||
tileID.x,
|
||||
tileID.computeInvertedY(this->getTilingScheme()));
|
||||
}
|
||||
if (key == "subdomain") {
|
||||
const size_t subdomainIndex =
|
||||
(tileID.level + tileID.x + tileID.y) %
|
||||
this->_subdomains.size();
|
||||
return this->_subdomains[subdomainIndex];
|
||||
}
|
||||
if (key == "culture") {
|
||||
return this->_culture;
|
||||
}
|
||||
return key;
|
||||
}));
|
||||
|
||||
UriQuery query(uri);
|
||||
if (!query.hasValue("n")) {
|
||||
query.setValue("n", "z");
|
||||
uri.setQuery(query.toQueryString());
|
||||
}
|
||||
|
||||
std::string url = std::string(uri.toString());
|
||||
|
||||
LoadTileImageFromUrlOptions options;
|
||||
options.allowEmptyImages = true;
|
||||
|
|
@ -233,7 +230,9 @@ private:
|
|||
}
|
||||
|
||||
std::vector<CreditAndCoverageAreas> _credits;
|
||||
std::string _baseUrl;
|
||||
std::string _urlTemplate;
|
||||
std::string _culture;
|
||||
std::vector<std::string> _subdomains;
|
||||
};
|
||||
|
||||
|
|
@ -357,14 +356,21 @@ BingMapsRasterOverlay::createTileProvider(
|
|||
pPrepareRendererResources,
|
||||
const std::shared_ptr<spdlog::logger>& pLogger,
|
||||
IntrusivePointer<const RasterOverlay> pOwner) const {
|
||||
std::string metadataUrl = CesiumUtility::Uri::resolve(
|
||||
Uri metadataUri(
|
||||
this->_url,
|
||||
"REST/v1/Imagery/Metadata/" + this->_mapStyle,
|
||||
true);
|
||||
metadataUrl =
|
||||
CesiumUtility::Uri::addQuery(metadataUrl, "incl", "ImageryProviders");
|
||||
metadataUrl = CesiumUtility::Uri::addQuery(metadataUrl, "key", this->_key);
|
||||
metadataUrl = CesiumUtility::Uri::addQuery(metadataUrl, "uriScheme", "https");
|
||||
|
||||
UriQuery metadataQuery(metadataUri);
|
||||
metadataQuery.setValue("incl", "ImageryProviders");
|
||||
metadataQuery.setValue("key", this->_key);
|
||||
metadataQuery.setValue("uriScheme", "https");
|
||||
if (!this->_culture.empty()) {
|
||||
metadataQuery.setValue("culture", this->_culture);
|
||||
}
|
||||
metadataUri.setQuery(metadataQuery.toQueryString());
|
||||
|
||||
std::string metadataUrl = std::string(metadataUri.toString());
|
||||
|
||||
pOwner = pOwner ? pOwner : this;
|
||||
|
||||
|
|
|
|||
|
|
@ -46,5 +46,5 @@ target_link_libraries(
|
|||
zlib-ng::zlib-ng
|
||||
spdlog::spdlog
|
||||
glm::glm
|
||||
uriparser::uriparser
|
||||
ada::ada
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,15 +1,112 @@
|
|||
#pragma once
|
||||
|
||||
#include <ada.h>
|
||||
#include <ada/url_search_params.h>
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace CesiumUtility {
|
||||
|
||||
/**
|
||||
* @brief A class for building and manipulating Uniform Resource Identifiers
|
||||
* @brief A class for parsing and manipulating Uniform Resource Identifiers
|
||||
* (URIs).
|
||||
*
|
||||
* The URI parser supports the [WhatWG URL
|
||||
* specification](https://url.spec.whatwg.org/). It also supports
|
||||
* protocol-relative URIs such as `//example.com`, and opaque paths such as
|
||||
* `a/b/c`.
|
||||
*/
|
||||
class Uri final {
|
||||
public:
|
||||
/**
|
||||
* @brief Attempts to create a new Uri by parsing the given string. If the
|
||||
* string fails to parse, \ref isValid will return false.
|
||||
*
|
||||
* @param uri A string containing the URI to attempt to parse.
|
||||
*/
|
||||
Uri(const std::string& uri);
|
||||
/**
|
||||
* @brief Attempts to create a new Uri from a base URI and a relative URI. If
|
||||
* the base URI is invalid, only the relative URI string will be used. If the
|
||||
* relative URI fails to parse, \ref isValid will return false.
|
||||
*
|
||||
* @param base The base URI that the relative URI is relative to.
|
||||
* @param relative A string containing a relative URI to attempt to parse.
|
||||
* @param useBaseQuery If true, the resulting URI will include the query
|
||||
* parameters of both the base URI and the relative URI. If false, only the
|
||||
* relative URI's query parameters will be used (if any).
|
||||
*/
|
||||
Uri(const Uri& base, const std::string& relative, bool useBaseQuery = false);
|
||||
|
||||
/**
|
||||
* @brief Returns a string representation of the entire URI including path and
|
||||
* query parameters.
|
||||
*/
|
||||
std::string_view toString() const;
|
||||
|
||||
/**
|
||||
* @brief Returns true if this URI has been successfully parsed.
|
||||
*/
|
||||
bool isValid() const;
|
||||
|
||||
/**
|
||||
* @brief Equivalent to \ref isValid.
|
||||
*/
|
||||
operator bool() const { return this->isValid(); }
|
||||
|
||||
/**
|
||||
* @brief Gets the scheme portion of the URI. If the URI was created without a
|
||||
* scheme, this will return an empty string.
|
||||
*
|
||||
* @returns The scheme, or an empty string if the URI could not be parsed or
|
||||
* has no scheme.
|
||||
*/
|
||||
std::string_view getScheme() const;
|
||||
|
||||
/**
|
||||
* @brief Gets the host portion of the URI. If the URI also specifies a
|
||||
* non-default port, it will be included in the returned host. If the URI
|
||||
* contains no host, this will return an empty string.
|
||||
*
|
||||
* @returns The host, or an empty string if the URI could not be parsed or has
|
||||
* no host.
|
||||
*/
|
||||
std::string_view getHost() const;
|
||||
|
||||
/**
|
||||
* @brief Gets the path portion of the URI. This will not include query
|
||||
* parameters, if present.
|
||||
*
|
||||
* @return The path, or empty string if the URI could not be parsed.
|
||||
*/
|
||||
std::string_view getPath() const;
|
||||
|
||||
/**
|
||||
* @brief Gets the query portion of the URI.
|
||||
*
|
||||
* @return The path, or empty string if the URI could not be parsed.
|
||||
*/
|
||||
std::string_view getQuery() const;
|
||||
|
||||
/**
|
||||
* @brief Sets the path portion of a URI to a new value. The other portions of
|
||||
* the URI are left unmodified, including any query parameters.
|
||||
*
|
||||
* @param path The new path portion of the URI.
|
||||
*/
|
||||
void setPath(const std::string_view& path);
|
||||
|
||||
/**
|
||||
* @brief Sets the query portion of a URI to a new value. The other portions
|
||||
* of the URI are left unmodified.
|
||||
*
|
||||
* @param queryString The new query portion of the URI.
|
||||
*/
|
||||
void setQuery(const std::string_view& queryString);
|
||||
|
||||
/**
|
||||
* @brief Attempts to resolve a relative URI using a base URI.
|
||||
*
|
||||
|
|
@ -21,10 +118,11 @@ public:
|
|||
* @param relative The relative URI to be resolved against the base URI.
|
||||
* @param useBaseQuery If true, any query parameters of the base URI will be
|
||||
* retained in the resolved URI.
|
||||
* @param assumeHttpsDefault If true, protocol-relative URIs (such as
|
||||
* `//api.cesium.com`) will be assumed to be `https`. If false, they will be
|
||||
* assumed to be `http`.
|
||||
* @param assumeHttpsDefault This parameter is ignored and is only kept for
|
||||
* API compatibility.
|
||||
* @returns The resolved URI.
|
||||
*
|
||||
* @deprecated Use the \ref Uri constructor instead.
|
||||
*/
|
||||
static std::string resolve(
|
||||
const std::string& base,
|
||||
|
|
@ -40,6 +138,14 @@ public:
|
|||
* @param key The key to be added to the query string.
|
||||
* @param value The value to be added to the query string.
|
||||
* @returns The modified URI including the new query string parameter.
|
||||
*
|
||||
* @deprecated Use the \ref UriQuery class:
|
||||
* ```
|
||||
* Uri parsedUri(uri);
|
||||
* UriQuery params(parsedUri);
|
||||
* params.setValue(key, value);
|
||||
* parsedUri.setQuery(params.toQueryString());
|
||||
* ```
|
||||
*/
|
||||
static std::string addQuery(
|
||||
const std::string& uri,
|
||||
|
|
@ -56,6 +162,13 @@ public:
|
|||
* @param key The key whose value will be obtained from the URI, if possible.
|
||||
* @returns The value of the given key in the query string, or an empty string
|
||||
* if not found.
|
||||
*
|
||||
* @deprecated Use the \ref UriQuery class:
|
||||
* ```
|
||||
* Uri parsedUri(uri);
|
||||
* UriQuery params(parsedUri);
|
||||
* params.getValue(key);
|
||||
* ```
|
||||
*/
|
||||
static std::string
|
||||
getQueryValue(const std::string& uri, const std::string& key);
|
||||
|
|
@ -193,6 +306,8 @@ public:
|
|||
*
|
||||
* @param uri The URI from which to get the path.
|
||||
* @return The path, or empty string if the URI could not be parsed.
|
||||
*
|
||||
* @deprecated Create a \ref Uri instance and use \ref Uri::getPath() instead.
|
||||
*/
|
||||
static std::string getPath(const std::string& uri);
|
||||
|
||||
|
|
@ -204,8 +319,97 @@ public:
|
|||
* @param newPath The new path portion of the URI.
|
||||
* @returns The new URI after setting the path. If the original URI cannot be
|
||||
* parsed, it is returned unmodified.
|
||||
*
|
||||
* @deprecated Create a \ref Uri instance and use \ref Uri::setPath(const
|
||||
* std::string_view&) instead.
|
||||
*/
|
||||
static std::string
|
||||
setPath(const std::string& uri, const std::string& newPath);
|
||||
|
||||
private:
|
||||
std::optional<ada::url_aggregator> _url = std::nullopt;
|
||||
bool _hasScheme = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A class for parsing and manipulating the query string of a URI.
|
||||
*/
|
||||
class UriQuery final {
|
||||
public:
|
||||
/**
|
||||
* @brief Creates a \ref UriQuery object from a query string.
|
||||
*
|
||||
* This query string should be in the format
|
||||
* `key1=value1&key2=value2&key3=value3...`. This is the format returned by
|
||||
* \ref Uri::getQuery. This string can include percent-encoded values.
|
||||
*
|
||||
* @param queryString The query string to parse into a query params object.
|
||||
*/
|
||||
UriQuery(const std::string_view& queryString) : _params(queryString) {}
|
||||
/**
|
||||
* @brief Creates a \ref UriQuery object from a \ref Uri instance.
|
||||
*
|
||||
* This is equivalent to `UriQuery(uri.getQuery())`.
|
||||
*
|
||||
* @param uri The URI instance to obtain the query params from.
|
||||
*/
|
||||
UriQuery(const Uri& uri) : _params(uri.getQuery()) {}
|
||||
|
||||
/**
|
||||
* @brief Obtains the value of the given key from the query parameters,
|
||||
* if possible.
|
||||
*
|
||||
* If the URI can't be parsed, or the key doesn't exist in the
|
||||
* query string, `std::nullopt` will be returned.
|
||||
*
|
||||
* @param key The key whose value will be obtained from the query string, if
|
||||
* possible.
|
||||
* @returns The value of the given key in the query string, or `std::nullopt`
|
||||
* if not found.
|
||||
*/
|
||||
std::optional<std::string_view> getValue(const std::string& key) {
|
||||
return this->_params.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the given key in the query parameters to the given value.
|
||||
* If the key doesn't exist already, it will be added to the query parameters.
|
||||
* Otherwise, the previous value will be overwritten.
|
||||
*
|
||||
* @param key The key to be added to the query string.
|
||||
* @param value The value to be added to the query string.
|
||||
*/
|
||||
void setValue(const std::string& key, const std::string& value) {
|
||||
this->_params.set(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns true if this query string contains a value for the given
|
||||
* key, or false otherwise.
|
||||
*
|
||||
* @param key The key to check.
|
||||
*/
|
||||
bool hasValue(const std::string& key) { return this->_params.has(key); }
|
||||
|
||||
/**
|
||||
* @brief Converts this object back into a query string, including all
|
||||
* modifications that have been made. This result can be passed directly to
|
||||
* \ref Uri::setQuery.
|
||||
*/
|
||||
std::string toQueryString() const { return this->_params.to_string(); }
|
||||
|
||||
/** @brief Returns an iterator pointing to the beginning of the query
|
||||
* parameters. */
|
||||
inline auto begin() const { return this->_params.begin(); }
|
||||
/** @brief Returns an iterator pointing to the end of the query parameters. */
|
||||
inline auto end() const { return this->_params.end(); }
|
||||
/** @brief Returns the first element in the query parameters. */
|
||||
inline auto front() const { return this->_params.front(); }
|
||||
/** @brief Returns the last element in the query parameters. */
|
||||
inline auto back() const { return this->_params.back(); }
|
||||
|
||||
private:
|
||||
ada::url_search_params _params;
|
||||
};
|
||||
|
||||
} // namespace CesiumUtility
|
||||
|
|
|
|||
|
|
@ -1,167 +1,188 @@
|
|||
#include <CesiumUtility/Uri.h>
|
||||
#include <CesiumUtility/joinToString.h>
|
||||
|
||||
#include <uriparser/Uri.h>
|
||||
#include <uriparser/UriBase.h>
|
||||
#include <ada/character_sets-inl.h>
|
||||
#include <ada/implementation.h>
|
||||
#include <ada/unicode.h>
|
||||
#include <ada/url_aggregator.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <stdexcept>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
namespace CesiumUtility {
|
||||
|
||||
namespace {
|
||||
const char* const HTTPS_PREFIX = "https:";
|
||||
const char* const HTTP_PREFIX = "http:";
|
||||
const std::string HTTPS_PREFIX = "https:";
|
||||
const std::string FILE_PREFIX = "file:///";
|
||||
const char WINDOWS_PATH_SEP = '\\';
|
||||
const char PATH_SEP = '/';
|
||||
|
||||
std::string cesiumConformUrl(const std::string& url, bool useHttps) {
|
||||
// Prepend protocol to protocol-relative URIs.
|
||||
if (url.length() > 2 && url.at(0) == '/' && url.at(1) == '/') {
|
||||
return std::string(useHttps ? HTTPS_PREFIX : HTTP_PREFIX).append(url);
|
||||
using UrlResult = ada::result<ada::url_aggregator>;
|
||||
|
||||
// C++ locale settings might change which values std::isalpha checks for. We
|
||||
// only want ASCII.
|
||||
bool isAsciiAlpha(unsigned char c) {
|
||||
return c >= 0x41 && c <= 0x7a && (c <= 0x5a || c >= 0x61);
|
||||
}
|
||||
bool isAscii(unsigned char c) { return c <= 0x7f; }
|
||||
|
||||
/**
|
||||
* A URI has a valid scheme if it starts with an ASCII alpha character and has a
|
||||
* sequence of ASCII characters followed by a "://"
|
||||
*/
|
||||
bool urlHasScheme(const std::string& uri) {
|
||||
for (size_t i = 0; i < uri.length(); i++) {
|
||||
unsigned char c = static_cast<unsigned char>(uri[i]);
|
||||
if (c == ':') {
|
||||
return uri.length() > i + 2 && uri[i + 1] == '/' && uri[i + 2] == '/';
|
||||
} else if ((i == 0 && !isAsciiAlpha(c)) || !isAscii(c)) {
|
||||
// Scheme must start with an ASCII alpha character and be an ASCII string
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return url;
|
||||
|
||||
return false;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// clang-tidy isn't smart enough to understand that we *are* checking the
|
||||
// optionals before accessing them, so disable the warning.
|
||||
// NOLINTBEGIN(bugprone-unchecked-optional-access)
|
||||
Uri::Uri(const std::string& uri) {
|
||||
UrlResult result;
|
||||
if (uri.starts_with("//")) {
|
||||
// This is a protocol-relative URL.
|
||||
// We will treat it as an HTTPS URL.
|
||||
this->_hasScheme = true;
|
||||
result = ada::parse(HTTPS_PREFIX + uri);
|
||||
} else {
|
||||
this->_hasScheme = urlHasScheme(uri);
|
||||
result = this->_hasScheme ? ada::parse(uri) : ada::parse(FILE_PREFIX + uri);
|
||||
}
|
||||
|
||||
if (result) {
|
||||
this->_url.emplace(std::move(result.value()));
|
||||
}
|
||||
}
|
||||
|
||||
Uri::Uri(const Uri& base, const std::string& relative, bool useBaseQuery) {
|
||||
UrlResult result;
|
||||
if (!base.isValid()) {
|
||||
this->_hasScheme = urlHasScheme(relative);
|
||||
result = this->_hasScheme ? ada::parse(relative)
|
||||
: ada::parse(FILE_PREFIX + relative);
|
||||
} else {
|
||||
this->_hasScheme = base._hasScheme;
|
||||
result = ada::parse(relative, &base._url.value());
|
||||
}
|
||||
|
||||
if (result) {
|
||||
this->_url.emplace(std::move(result.value()));
|
||||
|
||||
if (useBaseQuery) {
|
||||
UriQuery baseParams(base);
|
||||
UriQuery relativeParams(*this);
|
||||
// Set from relative to base to give priority to relative URL query string
|
||||
for (const auto& [key, value] : baseParams) {
|
||||
if (!relativeParams.hasValue(key)) {
|
||||
relativeParams.setValue(key, value);
|
||||
}
|
||||
}
|
||||
this->_url->set_search(relativeParams.toQueryString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string_view Uri::toString() const {
|
||||
if (!this->_url) {
|
||||
return std::string_view();
|
||||
}
|
||||
|
||||
const std::string_view result = this->_url->get_href();
|
||||
return this->_hasScheme ? result : result.substr(FILE_PREFIX.length());
|
||||
}
|
||||
|
||||
bool Uri::isValid() const { return this->_url.has_value(); }
|
||||
|
||||
std::string_view Uri::getScheme() const {
|
||||
if (!this->isValid()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return this->_hasScheme ? this->_url->get_protocol() : std::string_view{};
|
||||
}
|
||||
|
||||
std::string_view Uri::getHost() const {
|
||||
if (!this->isValid()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return this->_url->get_host();
|
||||
}
|
||||
|
||||
std::string_view Uri::getQuery() const {
|
||||
if (!this->isValid()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return this->_url->get_search();
|
||||
}
|
||||
|
||||
std::string_view Uri::getPath() const {
|
||||
if (!this->isValid()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Remove leading '/'
|
||||
return this->_url->get_pathname();
|
||||
}
|
||||
|
||||
void Uri::setPath(const std::string_view& path) {
|
||||
this->_url->set_pathname(path);
|
||||
}
|
||||
|
||||
void Uri::setQuery(const std::string_view& queryString) {
|
||||
this->_url->set_search(queryString);
|
||||
}
|
||||
|
||||
std::string Uri::resolve(
|
||||
const std::string& base,
|
||||
const std::string& relative,
|
||||
bool useBaseQuery,
|
||||
bool assumeHttpsDefault) {
|
||||
const std::string conformedBase = cesiumConformUrl(base, assumeHttpsDefault);
|
||||
const std::string conformedRelative =
|
||||
cesiumConformUrl(relative, assumeHttpsDefault);
|
||||
UriUriA baseUri;
|
||||
|
||||
if (uriParseSingleUriA(&baseUri, conformedBase.c_str(), nullptr) !=
|
||||
URI_SUCCESS) {
|
||||
// Could not parse the base, so just use the relative directly and hope for
|
||||
// the best.
|
||||
return relative;
|
||||
}
|
||||
|
||||
UriUriA relativeUri;
|
||||
if (uriParseSingleUriA(&relativeUri, conformedRelative.c_str(), nullptr) !=
|
||||
URI_SUCCESS) {
|
||||
// Could not parse one of the URLs, so just use the relative directly and
|
||||
// hope for the best.
|
||||
uriFreeUriMembersA(&baseUri);
|
||||
return relative;
|
||||
}
|
||||
|
||||
UriUriA resolvedUri;
|
||||
if (uriAddBaseUriA(&resolvedUri, &relativeUri, &baseUri) != URI_SUCCESS) {
|
||||
uriFreeUriMembersA(&resolvedUri);
|
||||
uriFreeUriMembersA(&relativeUri);
|
||||
uriFreeUriMembersA(&baseUri);
|
||||
return relative;
|
||||
}
|
||||
|
||||
if (uriNormalizeSyntaxA(&resolvedUri) != URI_SUCCESS) {
|
||||
uriFreeUriMembersA(&resolvedUri);
|
||||
uriFreeUriMembersA(&relativeUri);
|
||||
uriFreeUriMembersA(&baseUri);
|
||||
return relative;
|
||||
}
|
||||
|
||||
int charsRequired;
|
||||
if (uriToStringCharsRequiredA(&resolvedUri, &charsRequired) != URI_SUCCESS) {
|
||||
uriFreeUriMembersA(&resolvedUri);
|
||||
uriFreeUriMembersA(&relativeUri);
|
||||
uriFreeUriMembersA(&baseUri);
|
||||
return relative;
|
||||
}
|
||||
|
||||
std::string result(static_cast<size_t>(charsRequired), ' ');
|
||||
|
||||
if (uriToStringA(
|
||||
const_cast<char*>(result.c_str()),
|
||||
&resolvedUri,
|
||||
charsRequired + 1,
|
||||
nullptr) != URI_SUCCESS) {
|
||||
uriFreeUriMembersA(&resolvedUri);
|
||||
uriFreeUriMembersA(&relativeUri);
|
||||
uriFreeUriMembersA(&baseUri);
|
||||
return relative;
|
||||
}
|
||||
|
||||
if (useBaseQuery) {
|
||||
std::string query(baseUri.query.first, baseUri.query.afterLast);
|
||||
if (query.length() > 0) {
|
||||
if (resolvedUri.query.first) {
|
||||
result += "&" + query;
|
||||
} else {
|
||||
result += "?" + query;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uriFreeUriMembersA(&resolvedUri);
|
||||
uriFreeUriMembersA(&relativeUri);
|
||||
uriFreeUriMembersA(&baseUri);
|
||||
|
||||
return result;
|
||||
[[maybe_unused]] bool assumeHttpsDefault) {
|
||||
return std::string(Uri(Uri(base), relative, useBaseQuery).toString());
|
||||
}
|
||||
|
||||
std::string Uri::addQuery(
|
||||
const std::string& uri,
|
||||
const std::string& key,
|
||||
const std::string& value) {
|
||||
// TODO
|
||||
if (uri.find('?') != std::string::npos) {
|
||||
return uri + "&" + key + "=" + value;
|
||||
Uri parsedUri(uri);
|
||||
if (!parsedUri.isValid()) {
|
||||
return uri;
|
||||
}
|
||||
return uri + "?" + key + "=" + value;
|
||||
// UriUriA baseUri;
|
||||
|
||||
// if (uriParseSingleUriA(&baseUri, uri.c_str(), nullptr) != URI_SUCCESS)
|
||||
//{
|
||||
// // TODO: report error
|
||||
// return uri;
|
||||
//}
|
||||
|
||||
// uriFreeUriMembersA(&baseUri);
|
||||
UriQuery params(parsedUri);
|
||||
params.setValue(key, value);
|
||||
parsedUri.setQuery(params.toQueryString());
|
||||
return std::string(parsedUri.toString());
|
||||
}
|
||||
|
||||
std::string Uri::getQueryValue(const std::string& url, const std::string& key) {
|
||||
// We need to conform the URL since it will fail parsing if it's
|
||||
// protocol-relative. However, it doesn't matter what protocol we use since
|
||||
// it's only extracting query parameters.
|
||||
const std::string conformedUrl = cesiumConformUrl(url, true);
|
||||
UriUriA uri;
|
||||
if (uriParseSingleUriA(&uri, conformedUrl.c_str(), nullptr) != URI_SUCCESS) {
|
||||
return "";
|
||||
std::string Uri::getQueryValue(const std::string& uri, const std::string& key) {
|
||||
Uri parsedUri(uri);
|
||||
if (!parsedUri.isValid()) {
|
||||
return {};
|
||||
}
|
||||
UriQueryListA* queryList;
|
||||
int itemCount;
|
||||
if (uriDissectQueryMallocA(
|
||||
&queryList,
|
||||
&itemCount,
|
||||
uri.query.first,
|
||||
uri.query.afterLast) != URI_SUCCESS) {
|
||||
uriFreeUriMembersA(&uri);
|
||||
return "";
|
||||
}
|
||||
UriQueryListA* p = queryList;
|
||||
while (p) {
|
||||
if (p->key && std::strcmp(p->key, key.c_str()) == 0) {
|
||||
std::string value = p->value ? p->value : "";
|
||||
uriUnescapeInPlaceA(value.data());
|
||||
uriFreeQueryListA(queryList);
|
||||
uriFreeUriMembersA(&uri);
|
||||
return value;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
uriFreeQueryListA(queryList);
|
||||
uriFreeUriMembersA(&uri);
|
||||
return "";
|
||||
|
||||
return std::string(UriQuery(parsedUri).getValue(key).value_or(""));
|
||||
}
|
||||
|
||||
// NOLINTEND(bugprone-unchecked-optional-access)
|
||||
|
||||
std::string Uri::substituteTemplateParameters(
|
||||
const std::string& templateUri,
|
||||
const std::function<SubstitutionCallbackSignature>& substitutionCallback) {
|
||||
|
|
@ -179,7 +200,10 @@ std::string Uri::substituteTemplateParameters(
|
|||
++nextPos;
|
||||
const size_t endPos = templateUri.find('}', nextPos);
|
||||
if (endPos == std::string::npos) {
|
||||
throw std::runtime_error("Unclosed template parameter");
|
||||
// It's not a properly closed placeholder, so let's just output the rest
|
||||
// of the URL (including the open brace) as-is and bail.
|
||||
startPos = nextPos - 1;
|
||||
break;
|
||||
}
|
||||
|
||||
placeholder = templateUri.substr(nextPos, endPos - nextPos);
|
||||
|
|
@ -194,240 +218,99 @@ std::string Uri::substituteTemplateParameters(
|
|||
}
|
||||
|
||||
std::string Uri::escape(const std::string& s) {
|
||||
// In the worst case, escaping causes each character to turn into three.
|
||||
std::string result(s.size() * 3, '\0');
|
||||
char* pTerminator = uriEscapeExA(
|
||||
s.data(),
|
||||
s.data() + s.size(),
|
||||
result.data(),
|
||||
URI_FALSE,
|
||||
URI_FALSE);
|
||||
result.resize(size_t(pTerminator - result.data()));
|
||||
return result;
|
||||
return ada::unicode::percent_encode(
|
||||
s,
|
||||
ada::character_sets::WWW_FORM_URLENCODED_PERCENT_ENCODE);
|
||||
}
|
||||
|
||||
std::string Uri::unescape(const std::string& s) {
|
||||
std::string result = s;
|
||||
const char* pNewNull =
|
||||
uriUnescapeInPlaceExA(result.data(), URI_FALSE, URI_BR_DONT_TOUCH);
|
||||
result.resize(size_t(pNewNull - result.data()));
|
||||
return result;
|
||||
return ada::unicode::percent_decode(s, s.find('%'));
|
||||
}
|
||||
|
||||
std::string Uri::unixPathToUriPath(const std::string& unixPath) {
|
||||
// UriParser docs:
|
||||
// The destination buffer must be large enough to hold 7 + 3 * len(filename)
|
||||
// + 1 characters in case of an absolute filename or 3 * len(filename) + 1
|
||||
// in case of a relative filename.
|
||||
std::string result(7 + 3 * unixPath.size() + 1, '\0');
|
||||
if (uriUnixFilenameToUriStringA(unixPath.data(), result.data()) != 0) {
|
||||
// Error - return original string.
|
||||
return unixPath;
|
||||
} else {
|
||||
// An absolute URI will start with "file://". Remove this.
|
||||
if (result.find("file://", 0, 7) != std::string::npos) {
|
||||
result.erase(0, 7);
|
||||
}
|
||||
|
||||
// Truncate at first null character
|
||||
result.resize(std::strlen(result.data()));
|
||||
return result;
|
||||
}
|
||||
return Uri::nativePathToUriPath(unixPath);
|
||||
}
|
||||
|
||||
std::string Uri::windowsPathToUriPath(const std::string& windowsPath) {
|
||||
// uriWindowsFilenameToUriStringA doesn't allow `/` character in the path (it
|
||||
// percent encodes them) even though that's a perfectly valid path separator
|
||||
// on Windows. So convert all forward slashes to back slashes before calling
|
||||
// it.
|
||||
std::string windowsPathClean;
|
||||
windowsPathClean.resize(windowsPath.size());
|
||||
std::replace_copy(
|
||||
windowsPath.begin(),
|
||||
windowsPath.end(),
|
||||
windowsPathClean.begin(),
|
||||
'/',
|
||||
'\\');
|
||||
|
||||
// UriParser docs:
|
||||
// The destination buffer must be large enough to hold 8 + 3 * len(filename)
|
||||
// + 1 characters in case of an absolute filename or 3 * len(filename) + 1
|
||||
// in case of a relative filename.
|
||||
std::string result(8 + 3 * windowsPathClean.size() + 1, '\0');
|
||||
|
||||
if (uriWindowsFilenameToUriStringA(windowsPathClean.data(), result.data()) !=
|
||||
0) {
|
||||
// Error - return original string.
|
||||
return windowsPath;
|
||||
} else {
|
||||
// An absolute URI will start with "file://". Remove this.
|
||||
if (result.find("file://", 0, 7) != std::string::npos) {
|
||||
result.erase(0, 7);
|
||||
}
|
||||
|
||||
// Truncate at first null character
|
||||
result.resize(std::strlen(result.data()));
|
||||
return result;
|
||||
}
|
||||
return Uri::nativePathToUriPath(windowsPath);
|
||||
}
|
||||
|
||||
std::string Uri::nativePathToUriPath(const std::string& nativePath) {
|
||||
#ifdef _WIN32
|
||||
return windowsPathToUriPath(nativePath);
|
||||
#else
|
||||
return unixPathToUriPath(nativePath);
|
||||
#endif
|
||||
const std::string encoded = ada::unicode::percent_encode(
|
||||
nativePath,
|
||||
ada::character_sets::PATH_PERCENT_ENCODE);
|
||||
|
||||
const bool startsWithDriveLetter =
|
||||
encoded.length() >= 2 &&
|
||||
isAsciiAlpha(static_cast<unsigned char>(encoded[0])) && encoded[1] == ':';
|
||||
|
||||
std::string output;
|
||||
output.reserve(encoded.length() + (startsWithDriveLetter ? 1 : 0));
|
||||
|
||||
// Paths like C:/... should be prefixed with a path separator
|
||||
if (startsWithDriveLetter) {
|
||||
output += PATH_SEP;
|
||||
}
|
||||
|
||||
// All we really need to do from here is convert our slashes
|
||||
for (size_t i = 0; i < encoded.length(); i++) {
|
||||
if (encoded[i] == WINDOWS_PATH_SEP) {
|
||||
output += PATH_SEP;
|
||||
} else {
|
||||
output += encoded[i];
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
std::string Uri::uriPathToUnixPath(const std::string& uriPath) {
|
||||
// UriParser docs:
|
||||
// The destination buffer must be large enough to hold len(uriString) + 1
|
||||
// - 5 characters in case of an absolute URI or len(uriString) + 1 in case
|
||||
// of a relative URI.
|
||||
// However, the above seems to assume that uriPath starts with "file:", which
|
||||
// is not required.
|
||||
std::string result(uriPath.size() + 1, '\0');
|
||||
if (uriUriStringToUnixFilenameA(uriPath.data(), result.data()) != 0) {
|
||||
// Error - return original string.
|
||||
return uriPath;
|
||||
} else {
|
||||
// Truncate at first null character
|
||||
result.resize(std::strlen(result.data()));
|
||||
return result;
|
||||
}
|
||||
// URI paths are pretty much just unix paths with URL encoding
|
||||
const std::string_view& rawPath = uriPath;
|
||||
return ada::unicode::percent_decode(rawPath, rawPath.find('%'));
|
||||
}
|
||||
|
||||
std::string Uri::uriPathToWindowsPath(const std::string& uriPath) {
|
||||
// If the URI starts with `/c:` or similar, remove the initial slash.
|
||||
size_t skip = 0;
|
||||
if (uriPath.size() >= 3 && uriPath[0] == '/' && uriPath[1] != '/' &&
|
||||
uriPath[2] == ':') {
|
||||
skip = 1;
|
||||
const std::string path =
|
||||
ada::unicode::percent_decode(uriPath, uriPath.find('%'));
|
||||
|
||||
size_t i = 0;
|
||||
// A path including a drive name will start like /C:/....
|
||||
// In that case, we just skip the first slash and continue on
|
||||
if (path.length() >= 3 && path[0] == '/' &&
|
||||
isAsciiAlpha(static_cast<unsigned char>(path[1])) && path[2] == ':') {
|
||||
i++;
|
||||
}
|
||||
|
||||
// UriParser docs:
|
||||
// The destination buffer must be large enough to hold len(uriString) + 1
|
||||
// - 5 characters in case of an absolute URI or len(uriString) + 1 in case
|
||||
// of a relative URI.
|
||||
// However, the above seems to assume that uriPath starts with "file:", which
|
||||
// is not required.
|
||||
std::string result(uriPath.size() + 1, '\0');
|
||||
if (uriUriStringToWindowsFilenameA(uriPath.data() + skip, result.data()) !=
|
||||
0) {
|
||||
// Error - return original string.
|
||||
return uriPath;
|
||||
} else {
|
||||
// Truncate at first null character
|
||||
result.resize(std::strlen(result.data()));
|
||||
return result;
|
||||
std::string output;
|
||||
output.reserve(path.length() - i);
|
||||
for (; i < path.length(); i++) {
|
||||
if (path[i] == PATH_SEP) {
|
||||
output += WINDOWS_PATH_SEP;
|
||||
} else {
|
||||
output += path[i];
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
std::string Uri::uriPathToNativePath(const std::string& nativePath) {
|
||||
std::string Uri::uriPathToNativePath(const std::string& uriPath) {
|
||||
#ifdef _WIN32
|
||||
return uriPathToWindowsPath(nativePath);
|
||||
return uriPathToWindowsPath(uriPath);
|
||||
#else
|
||||
return uriPathToUnixPath(nativePath);
|
||||
return uriPathToUnixPath(uriPath);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string Uri::getPath(const std::string& uri) {
|
||||
UriUriA parsedUri;
|
||||
if (uriParseSingleUriA(&parsedUri, uri.c_str(), nullptr) != URI_SUCCESS) {
|
||||
// Could not parse the URI, so return an empty string.
|
||||
return std::string();
|
||||
}
|
||||
|
||||
// The initial string in this vector can be thought of as the "nothing" before
|
||||
// the first slash in the path.
|
||||
std::vector<std::string> parts{std::string()};
|
||||
|
||||
UriPathSegmentA* pCurrent = parsedUri.pathHead;
|
||||
while (pCurrent != nullptr) {
|
||||
parts.emplace_back(
|
||||
pCurrent->text.first,
|
||||
size_t(pCurrent->text.afterLast - pCurrent->text.first));
|
||||
pCurrent = pCurrent->next;
|
||||
}
|
||||
|
||||
uriFreeUriMembersA(&parsedUri);
|
||||
|
||||
return joinToString(parts, "/");
|
||||
return std::string(Uri(uri).getPath());
|
||||
}
|
||||
|
||||
std::string Uri::setPath(const std::string& uri, const std::string& newPath) {
|
||||
UriUriA parsedUri;
|
||||
if (uriParseSingleUriA(&parsedUri, uri.c_str(), nullptr) != URI_SUCCESS) {
|
||||
// Could not parse the URI, so return an empty string.
|
||||
return std::string();
|
||||
}
|
||||
|
||||
// Free the existing path. Strangely, uriparser doesn't provide any simple way
|
||||
// to do this.
|
||||
UriPathSegmentA* pCurrent = parsedUri.pathHead;
|
||||
while (pCurrent != nullptr) {
|
||||
UriPathSegmentA* pNext = pCurrent->next;
|
||||
free(pCurrent);
|
||||
pCurrent = pNext;
|
||||
}
|
||||
|
||||
parsedUri.pathHead = nullptr;
|
||||
parsedUri.pathTail = nullptr;
|
||||
|
||||
// Set the new path.
|
||||
if (!newPath.empty()) {
|
||||
std::string::size_type startPos = 0;
|
||||
do {
|
||||
std::string::size_type nextSlashIndex = newPath.find('/', startPos);
|
||||
|
||||
// Skip the initial slash if there is one.
|
||||
if (nextSlashIndex == 0) {
|
||||
startPos = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
UriPathSegmentA* pSegment =
|
||||
static_cast<UriPathSegmentA*>(malloc(sizeof(UriPathSegmentA)));
|
||||
memset(pSegment, 0, sizeof(UriPathSegmentA));
|
||||
|
||||
if (parsedUri.pathHead == nullptr) {
|
||||
parsedUri.pathHead = pSegment;
|
||||
parsedUri.pathTail = pSegment;
|
||||
} else {
|
||||
parsedUri.pathTail->next = pSegment;
|
||||
parsedUri.pathTail = parsedUri.pathTail->next;
|
||||
}
|
||||
|
||||
pSegment->text.first = newPath.data() + startPos;
|
||||
|
||||
if (nextSlashIndex != std::string::npos) {
|
||||
pSegment->text.afterLast = newPath.data() + nextSlashIndex;
|
||||
startPos = nextSlashIndex + 1;
|
||||
} else {
|
||||
pSegment->text.afterLast = newPath.data() + newPath.size();
|
||||
startPos = nextSlashIndex;
|
||||
}
|
||||
} while (startPos != std::string::npos);
|
||||
}
|
||||
|
||||
int charsRequired;
|
||||
if (uriToStringCharsRequiredA(&parsedUri, &charsRequired) != URI_SUCCESS) {
|
||||
uriFreeUriMembersA(&parsedUri);
|
||||
return uri;
|
||||
}
|
||||
|
||||
std::string result(static_cast<size_t>(charsRequired), ' ');
|
||||
|
||||
if (uriToStringA(
|
||||
const_cast<char*>(result.c_str()),
|
||||
&parsedUri,
|
||||
charsRequired + 1,
|
||||
nullptr) != URI_SUCCESS) {
|
||||
uriFreeUriMembersA(&parsedUri);
|
||||
return uri;
|
||||
}
|
||||
|
||||
return result;
|
||||
Uri parsedUri(uri);
|
||||
parsedUri.setPath(newPath);
|
||||
return std::string(parsedUri.toString());
|
||||
}
|
||||
|
||||
} // namespace CesiumUtility
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
using namespace CesiumUtility;
|
||||
|
||||
TEST_CASE("Uri::getPath") {
|
||||
|
|
@ -21,23 +24,34 @@ TEST_CASE("Uri::getPath") {
|
|||
"/foo/bar/");
|
||||
}
|
||||
|
||||
SUBCASE("returns empty path for nonexistent paths") {
|
||||
CHECK(Uri::getPath("https://example.com") == "");
|
||||
CHECK(Uri::getPath("https://example.com?some=parameter") == "");
|
||||
SUBCASE("returns / path for nonexistent paths") {
|
||||
CHECK(Uri::getPath("https://example.com") == "/");
|
||||
CHECK(Uri::getPath("https://example.com?some=parameter") == "/");
|
||||
}
|
||||
|
||||
SUBCASE("returns empty path for invalid uri") {
|
||||
CHECK(Uri::getPath("not a valid uri") == "");
|
||||
SUBCASE("handles unicode characters") {
|
||||
CHECK(Uri::getPath("http://example.com/🐶.bin") == "/%F0%9F%90%B6.bin");
|
||||
CHECK(
|
||||
Uri::getPath("http://example.com/示例测试用例") ==
|
||||
"/%E7%A4%BA%E4%BE%8B%E6%B5%8B%E8%AF%95%E7%94%A8%E4%BE%8B");
|
||||
CHECK(
|
||||
Uri::getPath("http://example.com/Ῥόδος") ==
|
||||
"/%E1%BF%AC%CF%8C%CE%B4%CE%BF%CF%82");
|
||||
CHECK(
|
||||
Uri::getPath(
|
||||
"http://example.com/🙍♂️🚪🤚/🪝🚗🚪/❓📞") ==
|
||||
"/%F0%9F%99%8D%E2%80%8D%E2%99%82%EF%B8%8F%F0%9F%9A%AA%F0%9F%A4%9A/"
|
||||
"%F0%9F%AA%9D%F0%9F%9A%97%F0%9F%9A%AA/%E2%9D%93%F0%9F%93%9E");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Uri::setPath") {
|
||||
SUBCASE("sets empty path") {
|
||||
CHECK(Uri::setPath("https://example.com", "") == "https://example.com");
|
||||
CHECK(Uri::setPath("https://example.com/", "") == "https://example.com/");
|
||||
}
|
||||
|
||||
SUBCASE("sets new path") {
|
||||
CHECK(Uri::setPath("https://example.com", "/") == "https://example.com/");
|
||||
CHECK(Uri::setPath("https://example.com/", "/") == "https://example.com/");
|
||||
CHECK(
|
||||
Uri::setPath("https://example.com/foo", "/bar") ==
|
||||
"https://example.com/bar");
|
||||
|
|
@ -52,7 +66,7 @@ TEST_CASE("Uri::setPath") {
|
|||
SUBCASE("preserves path parameters") {
|
||||
CHECK(
|
||||
Uri::setPath("https://example.com?some=parameter", "") ==
|
||||
"https://example.com?some=parameter");
|
||||
"https://example.com/?some=parameter");
|
||||
CHECK(
|
||||
Uri::setPath("https://example.com?some=parameter", "/") ==
|
||||
"https://example.com/?some=parameter");
|
||||
|
|
@ -77,8 +91,14 @@ TEST_CASE("Uri::setPath") {
|
|||
"/foo/bar") == "https://example.com/foo/bar?some=parameter");
|
||||
}
|
||||
|
||||
SUBCASE("returns empty path for invalid uri") {
|
||||
CHECK(Uri::setPath("not a valid uri", "/foo/") == "");
|
||||
SUBCASE("handles unicode characters") {
|
||||
CHECK(
|
||||
Uri::setPath("http://example.com/foo/", "/🐶.bin") ==
|
||||
"http://example.com/%F0%9F%90%B6.bin");
|
||||
CHECK(
|
||||
Uri::setPath("http://example.com/bar/", "/示例测试用例") ==
|
||||
"http://example.com/"
|
||||
"%E7%A4%BA%E4%BE%8B%E6%B5%8B%E8%AF%95%E7%94%A8%E4%BE%8B");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -94,7 +114,10 @@ TEST_CASE("Uri::resolve") {
|
|||
"//www.example.com",
|
||||
"/page/test",
|
||||
false,
|
||||
false) == "http://www.example.com/page/test");
|
||||
true) == "https://www.example.com/page/test");
|
||||
CHECK(
|
||||
CesiumUtility::Uri::resolve("https://www.example.com/", "/Ῥόδος") ==
|
||||
"https://www.example.com/%E1%BF%AC%CF%8C%CE%B4%CE%BF%CF%82");
|
||||
}
|
||||
|
||||
TEST_CASE("Uri::escape") {
|
||||
|
|
@ -114,7 +137,7 @@ TEST_CASE("Uri::unixPathToUriPath") {
|
|||
CHECK(Uri::unixPathToUriPath("wat") == "wat");
|
||||
CHECK(Uri::unixPathToUriPath("wat/the") == "wat/the");
|
||||
CHECK(Uri::unixPathToUriPath("/foo/bar") == "/foo/bar");
|
||||
CHECK(Uri::unixPathToUriPath("/some:file") == "/some%3Afile");
|
||||
CHECK(Uri::unixPathToUriPath("/some:file") == "/some:file");
|
||||
CHECK(Uri::unixPathToUriPath("/🤞/😱/") == "/%F0%9F%A4%9E/%F0%9F%98%B1/");
|
||||
}
|
||||
|
||||
|
|
@ -124,16 +147,16 @@ TEST_CASE("Uri::windowsPathToUriPath") {
|
|||
CHECK(Uri::windowsPathToUriPath("wat") == "wat");
|
||||
CHECK(Uri::windowsPathToUriPath("/foo/bar") == "/foo/bar");
|
||||
CHECK(Uri::windowsPathToUriPath("d:\\foo/bar\\") == "/d:/foo/bar/");
|
||||
CHECK(Uri::windowsPathToUriPath("e:\\some:file") == "/e:/some%3Afile");
|
||||
CHECK(Uri::windowsPathToUriPath("e:\\some:file") == "/e:/some:file");
|
||||
CHECK(
|
||||
Uri::windowsPathToUriPath("c:/🤞/😱/") ==
|
||||
"/c:/%F0%9F%A4%9E/%F0%9F%98%B1/");
|
||||
CHECK(
|
||||
Uri::windowsPathToUriPath("notadriveletter:\\file") ==
|
||||
"notadriveletter%3A/file");
|
||||
"notadriveletter:/file");
|
||||
CHECK(
|
||||
Uri::windowsPathToUriPath("\\notadriveletter:\\file") ==
|
||||
"/notadriveletter%3A/file");
|
||||
"/notadriveletter:/file");
|
||||
}
|
||||
|
||||
TEST_CASE("Uri::uriPathToUnixPath") {
|
||||
|
|
@ -158,3 +181,75 @@ TEST_CASE("Uri::uriPathToWindowsPath") {
|
|||
Uri::uriPathToWindowsPath("/notadriveletter:/file") ==
|
||||
"\\notadriveletter:\\file");
|
||||
}
|
||||
|
||||
TEST_CASE("Uri::addQuery") {
|
||||
CHECK(
|
||||
Uri::addQuery("https://example.com/", "a", "1") ==
|
||||
"https://example.com/?a=1");
|
||||
CHECK(
|
||||
Uri::addQuery("https://example.com/?a=1", "b", "2") ==
|
||||
"https://example.com/?a=1&b=2");
|
||||
CHECK(
|
||||
Uri::addQuery("https://example.com/?a=1", "a", "2") ==
|
||||
"https://example.com/?a=2");
|
||||
CHECK(
|
||||
Uri::addQuery("https://unparseable url", "a", "1") ==
|
||||
"https://unparseable url");
|
||||
CHECK(
|
||||
Uri::addQuery("https://example.com/", "a", "!@#$%^&()_+{}|") ==
|
||||
"https://example.com/?a=%21%40%23%24%25%5E%26%28%29_%2B%7B%7D%7C");
|
||||
}
|
||||
|
||||
TEST_CASE("Uri::substituteTemplateParameters") {
|
||||
const std::map<std::string, std::string> params{
|
||||
{"a", "aValue"},
|
||||
{"b", "bValue"},
|
||||
{"c", "cValue"},
|
||||
{"s", "teststr"},
|
||||
{"one", "1"}};
|
||||
|
||||
const auto substitutionCallback = [¶ms](const std::string& placeholder) {
|
||||
auto it = params.find(placeholder);
|
||||
return it == params.end() ? placeholder : it->second;
|
||||
};
|
||||
|
||||
CHECK(
|
||||
Uri::substituteTemplateParameters(
|
||||
"https://example.com/{a}/{b}/{c}",
|
||||
substitutionCallback) == "https://example.com/aValue/bValue/cValue");
|
||||
CHECK(
|
||||
Uri::substituteTemplateParameters(
|
||||
"https://example.com/enco%24d%5Ee%2Fd{s}tr1n%25g",
|
||||
[]([[maybe_unused]] const std::string& placeholder) {
|
||||
return "teststr";
|
||||
}) == "https://example.com/enco%24d%5Ee%2Fdteststrtr1n%25g");
|
||||
CHECK(
|
||||
Uri::substituteTemplateParameters(
|
||||
"https://example.com/{a",
|
||||
substitutionCallback) == "https://example.com/{a");
|
||||
CHECK(
|
||||
Uri::substituteTemplateParameters(
|
||||
"https://example.com/{}",
|
||||
substitutionCallback) == "https://example.com/");
|
||||
CHECK(
|
||||
Uri::substituteTemplateParameters(
|
||||
"https://example.com/a}",
|
||||
substitutionCallback) == "https://example.com/a}");
|
||||
}
|
||||
|
||||
TEST_CASE("UriQuery") {
|
||||
SUBCASE("preserves placeholders") {
|
||||
Uri uri("https://example.com?query={whatever}&{this}={that}");
|
||||
UriQuery query(uri);
|
||||
|
||||
CHECK(query.getValue("query") == "{whatever}");
|
||||
CHECK(query.getValue("{this}") == "{that}");
|
||||
|
||||
query.setValue("query", "foo");
|
||||
query.setValue("{this}", "{another}");
|
||||
CHECK(query.getValue("query") == "foo");
|
||||
CHECK(query.getValue("{this}") == "{another}");
|
||||
|
||||
CHECK(query.toQueryString() == "query=foo&%7Bthis%7D=%7Banother%7D");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,10 @@
|
|||
[
|
||||
{
|
||||
"name": "ada",
|
||||
"url": "https://github.com/ada-url/ada",
|
||||
"version": "2.9.2",
|
||||
"license": ["Apache 2.0"]
|
||||
},
|
||||
{
|
||||
"name": "Async++",
|
||||
"url": "https://github.com/Amanieu/asyncplusplus",
|
||||
|
|
@ -114,12 +120,6 @@
|
|||
"version": "1aeb57d26bc303d5cfa1a9ff2a331df7ba278656",
|
||||
"license": ["zlib"]
|
||||
},
|
||||
{
|
||||
"name": "uriparser",
|
||||
"url": "https://github.com/uriparser/uriparser",
|
||||
"version": "0.9.6",
|
||||
"license": ["BSD-3-Clause"]
|
||||
},
|
||||
{
|
||||
"name": "zlib",
|
||||
"url": "https://github.com/madler/zlib",
|
||||
|
|
|
|||
|
|
@ -23,6 +23,5 @@ graph TD
|
|||
Cesium3DTilesSelection[Cesium3DTilesSelection] --> spdlog_spdlog{{spdlog::spdlog}}
|
||||
Cesium3DTilesSelection[Cesium3DTilesSelection] --> spdlog_spdlog_header_only{{spdlog::spdlog_header_only}}
|
||||
Cesium3DTilesSelection[Cesium3DTilesSelection] --> tinyxml2_tinyxml2{{tinyxml2::tinyxml2}}
|
||||
Cesium3DTilesSelection[Cesium3DTilesSelection] --> uriparser_uriparser{{uriparser::uriparser}}
|
||||
class draco_draco,libmorton_libmorton,nonstd_expected-lite,spdlog_spdlog,spdlog_spdlog_header_only,tinyxml2_tinyxml2,uriparser_uriparser dependencyNode
|
||||
class draco_draco,libmorton_libmorton,nonstd_expected-lite,spdlog_spdlog,spdlog_spdlog_header_only,tinyxml2_tinyxml2 dependencyNode
|
||||
class Cesium3DTiles,Cesium3DTilesContent,Cesium3DTilesReader,CesiumAsync,CesiumGeometry,CesiumGeospatial,CesiumGltf,CesiumGltfReader,CesiumQuantizedMeshTerrain,CesiumRasterOverlays,CesiumUtility,Cesium3DTilesSelection libraryNode
|
||||
|
|
|
|||
|
|
@ -7,10 +7,11 @@ graph TD
|
|||
classDef dependencyNode fill:#fff,stroke:#ccc,color:#666,font-weight:bold,font-size:28px
|
||||
classDef libraryNode fill:#9f9,font-weight:bold,font-size:28px
|
||||
CesiumIonClient[CesiumIonClient] --> CesiumAsync[CesiumAsync]
|
||||
CesiumIonClient[CesiumIonClient] --> CesiumGeospatial[CesiumGeospatial]
|
||||
CesiumIonClient[CesiumIonClient] --> CesiumUtility[CesiumUtility]
|
||||
CesiumIonClient[CesiumIonClient] --> OpenSSL_Crypto{{OpenSSL::Crypto}}
|
||||
CesiumIonClient[CesiumIonClient] --> httplib_httplib{{httplib::httplib}}
|
||||
CesiumIonClient[CesiumIonClient] --> modp_b64_modp_b64{{modp_b64::modp_b64}}
|
||||
CesiumIonClient[CesiumIonClient] --> picosha2_picosha2{{picosha2::picosha2}}
|
||||
class OpenSSL_Crypto,httplib_httplib,modp_b64_modp_b64,picosha2_picosha2 dependencyNode
|
||||
class CesiumAsync,CesiumUtility,CesiumIonClient libraryNode
|
||||
class CesiumAsync,CesiumGeospatial,CesiumUtility,CesiumIonClient libraryNode
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ title: CesiumUtility Dependency Graph
|
|||
graph TD
|
||||
classDef dependencyNode fill:#fff,stroke:#ccc,color:#666,font-weight:bold,font-size:28px
|
||||
classDef libraryNode fill:#9f9,font-weight:bold,font-size:28px
|
||||
CesiumUtility[CesiumUtility] --> ada_ada{{ada::ada}}
|
||||
CesiumUtility[CesiumUtility] --> glm_glm{{glm::glm}}
|
||||
CesiumUtility[CesiumUtility] --> spdlog_spdlog{{spdlog::spdlog}}
|
||||
CesiumUtility[CesiumUtility] --> uriparser_uriparser{{uriparser::uriparser}}
|
||||
CesiumUtility[CesiumUtility] --> zlib-ng_zlib-ng{{zlib-ng::zlib-ng}}
|
||||
class glm_glm,spdlog_spdlog,uriparser_uriparser,zlib-ng_zlib-ng dependencyNode
|
||||
class ada_ada,glm_glm,spdlog_spdlog,zlib-ng_zlib-ng dependencyNode
|
||||
class CesiumUtility libraryNode
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@ config:
|
|||
graph TD
|
||||
classDef dependencyNode fill:#fff,stroke:#ccc,color:#666,font-weight:bold,font-size:28px
|
||||
classDef libraryNode fill:#9f9,font-weight:bold,font-size:28px
|
||||
CesiumUtility[CesiumUtility] --> ada_ada{{ada::ada}}
|
||||
CesiumUtility[CesiumUtility] --> glm_glm{{glm::glm}}
|
||||
CesiumUtility[CesiumUtility] --> spdlog_spdlog{{spdlog::spdlog}}
|
||||
CesiumUtility[CesiumUtility] --> uriparser_uriparser{{uriparser::uriparser}}
|
||||
CesiumUtility[CesiumUtility] --> zlib-ng_zlib-ng{{zlib-ng::zlib-ng}}
|
||||
Cesium3DTiles[Cesium3DTiles] --> CesiumUtility[CesiumUtility]
|
||||
Cesium3DTilesContent[Cesium3DTilesContent] --> Cesium3DTiles[Cesium3DTiles]
|
||||
|
|
@ -70,7 +70,6 @@ graph TD
|
|||
Cesium3DTilesSelection[Cesium3DTilesSelection] --> spdlog_spdlog{{spdlog::spdlog}}
|
||||
Cesium3DTilesSelection[Cesium3DTilesSelection] --> spdlog_spdlog_header_only{{spdlog::spdlog_header_only}}
|
||||
Cesium3DTilesSelection[Cesium3DTilesSelection] --> tinyxml2_tinyxml2{{tinyxml2::tinyxml2}}
|
||||
Cesium3DTilesSelection[Cesium3DTilesSelection] --> uriparser_uriparser{{uriparser::uriparser}}
|
||||
CesiumQuantizedMeshTerrain[CesiumQuantizedMeshTerrain] --> CesiumAsync[CesiumAsync]
|
||||
CesiumQuantizedMeshTerrain[CesiumQuantizedMeshTerrain] --> CesiumGeospatial[CesiumGeospatial]
|
||||
CesiumQuantizedMeshTerrain[CesiumQuantizedMeshTerrain] --> CesiumGltf[CesiumGltf]
|
||||
|
|
@ -94,28 +93,29 @@ graph TD
|
|||
CesiumGltfWriter[CesiumGltfWriter] --> CesiumJsonWriter[CesiumJsonWriter]
|
||||
CesiumGltfWriter[CesiumGltfWriter] --> modp_b64_modp_b64{{modp_b64::modp_b64}}
|
||||
CesiumIonClient[CesiumIonClient] --> CesiumAsync[CesiumAsync]
|
||||
CesiumIonClient[CesiumIonClient] --> CesiumGeospatial[CesiumGeospatial]
|
||||
CesiumIonClient[CesiumIonClient] --> CesiumUtility[CesiumUtility]
|
||||
CesiumIonClient[CesiumIonClient] --> OpenSSL_Crypto{{OpenSSL::Crypto}}
|
||||
CesiumIonClient[CesiumIonClient] --> httplib_httplib{{httplib::httplib}}
|
||||
CesiumIonClient[CesiumIonClient] --> modp_b64_modp_b64{{modp_b64::modp_b64}}
|
||||
CesiumIonClient[CesiumIonClient] --> picosha2_picosha2{{picosha2::picosha2}}
|
||||
class glm_glm,spdlog_spdlog,uriparser_uriparser,zlib-ng_zlib-ng,libmorton_libmorton,Async_,spdlog_spdlog_header_only,unofficial_sqlite3_sqlite3,earcut,s2_s2,KTX_ktx,WebP_webp,WebP_webpdecoder,draco_draco,libjpeg-turbo_turbojpeg-static,meshoptimizer_meshoptimizer,modp_b64_modp_b64,nonstd_expected-lite,tinyxml2_tinyxml2,OpenSSL_Crypto,httplib_httplib,picosha2_picosha2 dependencyNode
|
||||
class ada_ada,glm_glm,spdlog_spdlog,zlib-ng_zlib-ng,libmorton_libmorton,Async_,spdlog_spdlog_header_only,unofficial_sqlite3_sqlite3,earcut,s2_s2,KTX_ktx,WebP_webp,WebP_webpdecoder,draco_draco,libjpeg-turbo_turbojpeg-static,meshoptimizer_meshoptimizer,modp_b64_modp_b64,nonstd_expected-lite,tinyxml2_tinyxml2,OpenSSL_Crypto,httplib_httplib,picosha2_picosha2 dependencyNode
|
||||
class CesiumUtility,Cesium3DTiles,Cesium3DTilesReader,CesiumAsync,CesiumGeometry,CesiumGeospatial,CesiumGltf,CesiumGltfContent,CesiumGltfReader,Cesium3DTilesContent,CesiumJsonReader,CesiumQuantizedMeshTerrain,CesiumRasterOverlays,Cesium3DTilesSelection,CesiumJsonWriter,Cesium3DTilesWriter,CesiumGltfWriter,CesiumIonClient libraryNode
|
||||
linkStyle 0 stroke:#ff0029,stroke-width:8px
|
||||
linkStyle 1,20,60 stroke:#377eb8,stroke-width:8px
|
||||
linkStyle 2,63 stroke:#66a61e,stroke-width:8px
|
||||
linkStyle 1 stroke:#377eb8,stroke-width:8px
|
||||
linkStyle 2,20,60 stroke:#66a61e,stroke-width:8px
|
||||
linkStyle 3 stroke:#984ea3,stroke-width:8px
|
||||
linkStyle 4,13,19,23,24,26,29,35,56,70,71,78,87 stroke:#00d2d5,stroke-width:8px
|
||||
linkStyle 5,15,46,81 stroke:#ff7f00,stroke-width:8px
|
||||
linkStyle 4,13,19,23,24,26,29,35,56,69,70,77,87 stroke:#00d2d5,stroke-width:8px
|
||||
linkStyle 5,15,46,80 stroke:#ff7f00,stroke-width:8px
|
||||
linkStyle 6,48 stroke:#af8d00,stroke-width:8px
|
||||
linkStyle 7,16,30,36,49,64,72,86 stroke:#7f80cd,stroke-width:8px
|
||||
linkStyle 8,25,31,50,73 stroke:#b3e900,stroke-width:8px
|
||||
linkStyle 9,32,51,65,74 stroke:#c42e60,stroke-width:8px
|
||||
linkStyle 10,33,37,52,66,75,83 stroke:#a65628,stroke-width:8px
|
||||
linkStyle 11,67,76 stroke:#f781bf,stroke-width:8px
|
||||
linkStyle 12,34,53,77 stroke:#8dd3c7,stroke-width:8px
|
||||
linkStyle 7,16,30,36,49,63,71,85 stroke:#7f80cd,stroke-width:8px
|
||||
linkStyle 8,25,31,50,72 stroke:#b3e900,stroke-width:8px
|
||||
linkStyle 9,32,51,64,73,86 stroke:#c42e60,stroke-width:8px
|
||||
linkStyle 10,33,37,52,65,74,82 stroke:#a65628,stroke-width:8px
|
||||
linkStyle 11,66,75 stroke:#f781bf,stroke-width:8px
|
||||
linkStyle 12,34,53,76 stroke:#8dd3c7,stroke-width:8px
|
||||
linkStyle 14,58 stroke:#bebada,stroke-width:8px
|
||||
linkStyle 17,38,68 stroke:#fb8072,stroke-width:8px
|
||||
linkStyle 17,38,67 stroke:#fb8072,stroke-width:8px
|
||||
linkStyle 18 stroke:#80b1d3,stroke-width:8px
|
||||
linkStyle 21,61 stroke:#fdb462,stroke-width:8px
|
||||
linkStyle 22 stroke:#fccde5,stroke-width:8px
|
||||
|
|
@ -127,13 +127,13 @@ graph TD
|
|||
linkStyle 42,57 stroke:#d95f02,stroke-width:8px
|
||||
linkStyle 43 stroke:#e7298a,stroke-width:8px
|
||||
linkStyle 44 stroke:#e6ab02,stroke-width:8px
|
||||
linkStyle 45,85,90 stroke:#a6761d,stroke-width:8px
|
||||
linkStyle 45,84,90 stroke:#a6761d,stroke-width:8px
|
||||
linkStyle 47 stroke:#0097ff,stroke-width:8px
|
||||
linkStyle 54 stroke:#00d067,stroke-width:8px
|
||||
linkStyle 55 stroke:#000000,stroke-width:8px
|
||||
linkStyle 59,79 stroke:#252525,stroke-width:8px
|
||||
linkStyle 62,80 stroke:#525252,stroke-width:8px
|
||||
linkStyle 69,82,84 stroke:#737373,stroke-width:8px
|
||||
linkStyle 59,78 stroke:#252525,stroke-width:8px
|
||||
linkStyle 62,79 stroke:#525252,stroke-width:8px
|
||||
linkStyle 68,81,83 stroke:#737373,stroke-width:8px
|
||||
linkStyle 88 stroke:#969696,stroke-width:8px
|
||||
linkStyle 89 stroke:#bdbdbd,stroke-width:8px
|
||||
linkStyle 91 stroke:#f43600,stroke-width:8px
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 136 KiB After Width: | Height: | Size: 134 KiB |
|
|
@ -4,6 +4,7 @@ Cesium Native relies on a number of third-party dependencies. These dependencies
|
|||
|
||||
| Dependency | Usage |
|
||||
| ------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
|
||||
| [ada](https://github.com/ada-url/ada) | Used to parse and manipulate URIs. |
|
||||
| [Async++](https://github.com/Amanieu/asyncplusplus) | Used by CesiumAsync for cross-platform concurrency. |
|
||||
| [Catch2](https://github.com/catchorg/Catch2) | Test framework used by CesiumNativeTests. |
|
||||
| [draco](https://github.com/google/draco) | Required to decode meshes and point clouds compressed with Draco. |
|
||||
|
|
@ -25,7 +26,6 @@ Cesium Native relies on a number of third-party dependencies. These dependencies
|
|||
| [sqlite3](https://www.sqlite.org/index.html) | Used to cache HTTP responses. |
|
||||
| [stb_image](https://github.com/nothings/stb/blob/master/stb_image.h) | A simple image loader. |
|
||||
| [tinyxml2](https://github.com/leethomason/tinyxml2) | XML parser for interacting with XML APIs such as those implementing the Web Map Service standard. |
|
||||
| [uriparser](https://github.com/uriparser/uriparser) | Used to parse and manipulate URIs. |
|
||||
| [zlib-ng](https://github.com/zlib-ng/zlib-ng) | An optimized zlib implementation for working with Gzipped data. |
|
||||
|
||||
The following chart illustrates the connections between the Cesium Native libraries and third-party dependencies:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "cesium-native",
|
||||
"version": "0.43.0",
|
||||
"version": "0.44.0",
|
||||
"description": "Cesium 3D Geospatial for C++",
|
||||
"main": "index.js",
|
||||
"directories": {
|
||||
|
|
|
|||
Loading…
Reference in New Issue