Once a GeoJsonDocumentRasterOverlay is removed from a tileset, attempting to add it again - to the same tileset or a different one - crashes the application. Is this intended? If so, it should be documented. If not, it’s a bug.
``` cpp
#include <CesiumGeospatial/LocalHorizontalCoordinateSystem.h>
#include <CesiumGeospatial/Cartographic.h>
#include <CesiumGeospatial/Ellipsoid.h>
#include <CesiumGeospatial/GlobeTransforms.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <Cesium3DTilesContent/GltfConverters.h>
#include <CesiumRasterOverlays/DebugColorizeTilesRasterOverlay.h>
#include <CesiumRasterOverlays/WebMapServiceRasterOverlay.h>
#include <CesiumRasterOverlays/TileMapServiceRasterOverlay.h>
#include <CesiumRasterOverlays/IonRasterOverlay.h>
#include <CesiumRasterOverlays/QuadtreeRasterOverlayTileProvider.h>
#include <Cesium3DTilesSelection/EllipsoidTilesetLoader.h>
#include <CesiumRasterOverlays/GeoJsonDocumentRasterOverlay.h>
#include <CesiumCurl/CurlAssetAccessor.h>
#include <thread>
#include <filesystem>
#include <fstream>
#include <assert.h>
namespace fs = std::filesystem;
std::vector<std::byte> readFile(const std::filesystem::path &fileName)
{
std::ifstream file(fileName, std::ios::binary | std::ios::ate);
assert(file);
std::streamoff size = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<std::byte> buffer{size_t(size)};
file.read(reinterpret_cast<char *>(buffer.data()), std::streamsize(size));
return buffer;
}
using namespace CesiumAsync;
using namespace CesiumUtility;
using namespace CesiumRasterOverlays;
using namespace CesiumVectorData;
using namespace CesiumGeospatial;
using namespace Cesium3DTilesSelection;
class SimpleTaskProcessor : public CesiumAsync::ITaskProcessor
{
public:
virtual void startTask(std::function<void()> f) override
{
auto *heapFunc = new std::function<void()>(std::move(f));
std::thread t([](void *taskData)
{
std::unique_ptr<std::function<void()>> func(
static_cast<std::function<void()> *>(taskData));
(*func)(); // auto-deletes when leaving scope
},
heapFunc);
t.detach();
}
};
class SimplePrepareRendererResource
: public Cesium3DTilesSelection::IPrepareRendererResources
{
public:
std::atomic<size_t> totalAllocation{};
struct AllocationResult
{
AllocationResult(std::atomic<size_t> &allocCount_)
: allocCount{allocCount_}
{
++allocCount;
}
~AllocationResult() noexcept { --allocCount; }
std::atomic<size_t> &allocCount;
};
~SimplePrepareRendererResource() noexcept {}
virtual CesiumAsync::Future<TileLoadResultAndRenderResources>
prepareInLoadThread(
const CesiumAsync::AsyncSystem &asyncSystem,
TileLoadResult &&tileLoadResult,
const glm::dmat4 & /*transform*/,
const std::any & /*rendererOptions*/) override
{
prepareInLoadThreadTestCallback(tileLoadResult);
return asyncSystem.createResolvedFuture(TileLoadResultAndRenderResources{
std::move(tileLoadResult),
new AllocationResult{totalAllocation}});
}
virtual void *prepareInMainThread(
Cesium3DTilesSelection::Tile & /*tile*/,
void *pLoadThreadResult) override
{
if (pLoadThreadResult)
{
AllocationResult *loadThreadResult =
reinterpret_cast<AllocationResult *>(pLoadThreadResult);
delete loadThreadResult;
}
return new AllocationResult{totalAllocation};
}
virtual void free(
Cesium3DTilesSelection::Tile & /*tile*/,
void *pLoadThreadResult,
void *pMainThreadResult) noexcept override
{
if (pMainThreadResult)
{
AllocationResult *mainThreadResult =
reinterpret_cast<AllocationResult *>(pMainThreadResult);
delete mainThreadResult;
}
if (pLoadThreadResult)
{
AllocationResult *loadThreadResult =
reinterpret_cast<AllocationResult *>(pLoadThreadResult);
delete loadThreadResult;
}
}
virtual void *prepareRasterInLoadThread(
CesiumGltf::ImageAsset & /*image*/,
const std::any & /*rendererOptions*/) override
{
return new AllocationResult{totalAllocation};
}
virtual void *prepareRasterInMainThread(
CesiumRasterOverlays::RasterOverlayTile & /*rasterTile*/,
void *pLoadThreadResult) override
{
if (pLoadThreadResult)
{
AllocationResult *loadThreadResult =
reinterpret_cast<AllocationResult *>(pLoadThreadResult);
delete loadThreadResult;
}
return new AllocationResult{totalAllocation};
}
virtual void freeRaster(
const CesiumRasterOverlays::RasterOverlayTile & /*rasterTile*/,
void *pLoadThreadResult,
void *pMainThreadResult) noexcept override
{
if (pMainThreadResult)
{
AllocationResult *mainThreadResult =
reinterpret_cast<AllocationResult *>(pMainThreadResult);
delete mainThreadResult;
}
if (pLoadThreadResult)
{
AllocationResult *loadThreadResult =
reinterpret_cast<AllocationResult *>(pLoadThreadResult);
delete loadThreadResult;
}
}
virtual void attachRasterInMainThread(
const Cesium3DTilesSelection::Tile & /*tile*/,
int32_t /*overlayTextureCoordinateID*/,
const CesiumRasterOverlays::RasterOverlayTile & /*rasterTile*/,
void * /*pMainThreadRendererResources*/,
const glm::dvec2 & /*translation*/,
const glm::dvec2 & /*scale*/) override {}
virtual void detachRasterInMainThread(
const Cesium3DTilesSelection::Tile & /*tile*/,
int32_t /*overlayTextureCoordinateID*/,
const CesiumRasterOverlays::RasterOverlayTile & /*rasterTile*/,
void * /*pMainThreadRendererResources*/) noexcept override {}
std::function<void(const TileLoadResult &)> prepareInLoadThreadTestCallback =
[](const TileLoadResult & /*result*/) {};
};
int main(int argc, char **argv)
{
fs::path exePath = fs::canonical(argv[0]);
fs::path exeDir = exePath.parent_path();
fs::current_path(exeDir);
auto taskProcessor = std::make_shared<SimpleTaskProcessor>();
auto asyncSystem = std::make_shared<CesiumAsync::AsyncSystem>(taskProcessor);
auto curlAssetAccessor = std::make_shared<CesiumCurl::CurlAssetAccessor>();
auto prepareRendererResource = std::make_shared<SimplePrepareRendererResource>();
auto creditSystem = std::make_shared<CesiumUtility::CreditSystem>();
Cesium3DTilesSelection::TilesetExternals externals{
curlAssetAccessor,
prepareRendererResource,
*asyncSystem,
creditSystem};
Cesium3DTilesSelection::TilesetOptions options{};
Cesium3DTilesSelection::Tileset *tileset = new Cesium3DTilesSelection::Tileset(externals, "./tileset.json", options);
GeoJsonDocumentRasterOverlayOptions geoJsonOptions{
VectorStyle{
LineStyle{
ColorStyle{Color{255, 0, 0, 255}, ColorMode::Normal},
2.0,
LineWidthMode::Pixels},
PolygonStyle{ColorStyle{
.color = Color(0, 255, 0, 50),
.colorMode = ColorMode::Normal},
LineStyle{
ColorStyle{Color{255, 255, 0, 255}, ColorMode::Normal},
2.0,
LineWidthMode::Pixels}}},
Ellipsoid::WGS84,
0};
CesiumUtility::Result<GeoJsonDocument> docResult = GeoJsonDocument::fromGeoJson(readFile("sample.geojson"));
GeoJsonDocument doc = docResult.value.value();
auto docPtr = std::make_shared<GeoJsonDocument>(doc);
auto raster = new CesiumRasterOverlays::GeoJsonDocumentRasterOverlay(*asyncSystem, "some overlay",
docPtr, geoJsonOptions);
CesiumUtility::IntrusivePointer<const CesiumRasterOverlays::RasterOverlay> safePtr = raster;
tileset->getOverlays().add(safePtr);
tileset->getOverlays().remove(safePtr);
assert(safePtr->getReferenceCount() > 0);
// boom
tileset->getOverlays().add(safePtr);
tileset->getOverlays().remove(safePtr);
return 0;
}