1#include "ClipmapQueryHandler.h"
2#include "ClipmapLevel.h"
3#include "RenderContext.h"
4#include "Raster/RasterSource.h"
6#include "Foundation/Logging/Logger.h"
15void Cogs::ClipmapQueryHandler::initialize(std::vector<ClipmapLevel> * terrainLevels, std::vector<std::vector<ClipmapLevel>> * imagery)
17 this->imagery = imagery;
18 this->terrainLevels = terrainLevels;
21void Cogs::ClipmapQueryHandler::postInitialize()
23 std::lock_guard<std::mutex> lock(mutex);
25 for (
auto & data : elevationQueries) {
26 if (!data.inputLevels) {
27 data.inputLevels = terrainLevels;
29 if (data.targetLevel == -1) {
30 auto terrainSource = (*terrainLevels)[0].rasterLevel->getSource();
31 data.targetLevel =
static_cast<int>(terrainSource->getLevels().size()) - 1;
36 for (
auto & data : colorQueries) {
37 if (!data.inputLevels) {
38 data.inputLevels = &(*imagery)[data.queryLayer - 1];
40 if (data.targetLevel == -1) {
41 data.targetLevel =
static_cast<int>((*imagery)[data.queryLayer - 1].size()) - 1;
46 allowToProcessQueries =
true;
49void Cogs::ClipmapQueryHandler::postRequest(
Cogs::TerrainQuery * query, Cogs::TerrainQueryCallback callback,
const WorldOptions & worldOptions)
51 if (query->layer == 0) {
52 auto terrainSource = (terrainLevels && !terrainLevels->empty()) ? (*terrainLevels)[0].rasterLevel->getSource() :
nullptr;
54 ElevationQueryData data;
55 data.userData = query->userData;
56 data.callback = callback;
57 data.positions.assign(query->positions, query->positions + query->numPositions);
58 data.results.resize(query->numPositions / 2, 0.0);
59 data.resultStride = 1;
60 data.levels.resize(query->numPositions / 2, (
int)-1);
61 data.inputLevels = terrainLevels;
62 data.queryLayer = query->layer;
63 data.worldOptions = worldOptions;
65 data.targetLevel = query->level;
67 if (data.targetLevel == -1 && terrainSource) {
68 data.targetLevel =
static_cast<int>(terrainSource->getLevels().size()) - 1;
71 data.requestedLevel.resize(query->numPositions / 2, -1);
72 data.resultLevel = -1;
73 data.complete =
false;
75 std::lock_guard<std::mutex> lock(mutex);
76 elevationQueries.push_back(data);
78 assert((!imagery || ((query->layer - 1) >= 0 && (query->layer - 1) < (
int)(*imagery).size())) &&
"Query layer not a valid imagery layer.");
81 data.userData = query->userData;
82 data.callback = callback;
83 data.positions.assign(query->positions, query->positions + query->numPositions);
84 data.results.resize(4 * (query->numPositions / 2), 0);
85 data.resultStride = 4;
86 data.levels.resize(query->numPositions / 2, (
int)-1);
87 data.queryLayer = query->layer;
88 data.worldOptions = worldOptions;
91 data.inputLevels = &(*imagery)[query->layer - 1];
92 data.targetLevel = query->level == -1 ?
static_cast<int>((*imagery)[query->layer - 1].size()) - 1 : query->level;
94 data.inputLevels =
nullptr;
95 data.targetLevel = query->level;
98 data.requestedLevel.resize(query->numPositions / 2, -1);
99 data.resultLevel = -1;
100 data.complete =
false;
102 std::lock_guard<std::mutex> lock(mutex);
103 colorQueries.push_back(data);
107void Cogs::ClipmapQueryHandler::processQueries()
109 if (!allowToProcessQueries)
return;
111 processQueries_T(elevationQueries);
112 processQueries_T(colorQueries);
116bool Cogs::ClipmapQueryHandler::readData(
const RasterTileData * tileData,
const int x,
const int y,
const int width,
double * result)
118 const size_t requiredSize = (y * width + x + 1) *
sizeof(
float);
120 if (tileData->data.size() < requiredSize) {
121 LOG_ERROR(logger,
"Tile data insufficient size to query (x, y, width): %d, %d, %d", x, y, width);
125 const float * elevations =
reinterpret_cast<const float *
>(tileData->data.data());
127 *result = elevations[y * width + x];
133bool Cogs::ClipmapQueryHandler::readData(
const RasterTileData * tileData,
const int x,
const int y,
const int width,
unsigned char * result)
135 const size_t requiredSize = (y * width + x + 1) *
sizeof(
float);
137 if (tileData->data.size() < requiredSize) {
138 LOG_ERROR(logger,
"Tile data insufficient size to query (x, y, width): %d, %d, %d", x, y, width);
142 const auto & colors = tileData->data;
147 switch (tileData->format) {
148 case TextureFormat::R8G8B8_UNORM:
151 case TextureFormat::R8G8B8A8_UNORM:
154 case TextureFormat::R8G8B8A8_UNORM_SRGB:
157 case TextureFormat::R32_FLOAT:
165 if (!bpp)
return false;
170 stride = bpp * width;
173 if (
static_cast<int>(colors.size()) < (y * stride + (x + 1) * bpp))
return false;
175 for (
int i = 0; i < bpp; ++i) {
176 result[i] = colors[y * stride + x * bpp + i];
182template<
typename DataType>
183void Cogs::ClipmapQueryHandler::processQueries_T(std::vector<TerrainQueryData<DataType>> & queries)
185 std::vector<size_t> completed;
186 std::vector<TerrainQueryData<DataType>> currentQueries;
189 std::lock_guard<std::mutex> lock(mutex);
191 currentQueries = std::move(queries);
194 for (
size_t i = 0; i < currentQueries.size(); ++i) {
195 TerrainQueryData<DataType> & data = currentQueries[i];
197 std::vector<ClipmapLevel> & levels = *data.inputLevels;
199 for (
size_t j = 0; j < data.levels.size(); ++j) {
200 if (data.levels[j] != data.targetLevel && data.levels[j] <= data.resultLevel && !levels.empty()) {
201 const int level = std::min(data.targetLevel, data.resultLevel <= 0 ? data.resultLevel + 1 : data.resultLevel * 2);
203 assert(level >= 0 && level <
static_cast<int>(levels.size()));
204 const auto rasterLevel = levels[level].rasterLevel;
207 const double queryPositionLong = data.positions[j * 2];
208 const double queryPositionLat = data.positions[j * 2 + 1];
210 auto & extent = rasterLevel->getGeoExtent();
211 if (queryPositionLong < extent.getWest() ||
212 queryPositionLong > extent.getEast() ||
213 queryPositionLat < extent.getSouth() ||
214 queryPositionLat > extent.getNorth()) {
215 LOG_WARNING(logger,
"Query outside extents: [%f, %f].", queryPositionLong, queryPositionLat);
218 data.levels[j] = level;
223 const double latitudeIndex = rasterLevel->latitudeToIndex(queryPositionLat / data.worldOptions.worldScale.y);
224 const double longitudeIndex = rasterLevel->longitudeToIndex(queryPositionLong / data.worldOptions.worldScale.x);
226 const int tileY =
static_cast<int>(latitudeIndex / rasterLevel->getLatitudePostsPerTile());
227 const int tileX =
static_cast<int>(longitudeIndex / rasterLevel->getLongitudePostsPerTile());
229 if (tileX < 0 || tileY < 0) {
230 LOG_WARNING(logger,
"Query outside valid range: [%f, %f].", queryPositionLong, queryPositionLat);
233 data.levels[j] = level;
238 RasterTileIdentifier
id = { rasterLevel->getLevel(), tileX, tileY };
240 auto source = rasterLevel->getSource();
243 std::lock_guard<RasterSource> sourceLock(*source);
245 const auto tile = source->getTile(
id);
248 LOG_WARNING(logger,
"Error reading tile %d, %d, %d from source.",
id.level,
id.x,
id.y);
250 data.levels[j] = level;
255 const auto tileData = source->getTileData(tile);
258 const int stride = tile->getWidth();
259 const int y =
static_cast<int>(latitudeIndex) % rasterLevel->getLatitudePostsPerTile();
260 const int x =
static_cast<int>(longitudeIndex) % rasterLevel->getLongitudePostsPerTile();
262 if (tileData->data.size() && readData(tileData, x, y, stride, &data.results[j * data.resultStride])) {
263 data.levels[j] = level;
265 if (data.requestedLevel[j] == level) {
267 source->unrefTile(tile);
270 if (tileData->data.empty()) {
271 LOG_WARNING(logger,
"Empty tile data for tile at %d, %d, %d.",
id.level,
id.x,
id.y);
273 LOG_WARNING(logger,
"Failed reading data from tile at %d, %d, %d.",
id.level,
id.x,
id.y);
276 data.levels[j] = level;
279 if (level > data.requestedLevel[j]) {
280 TileLoadRequest request = { tile };
281 data.requestedLevel[j] = level;
282 source->refTile(tile);
283 source->requestTile(request);
291 int doneLevel = std::numeric_limits<int>::max();
293 for (
size_t j = 0; j < data.levels.size(); ++j) {
294 done &= (data.levels[j] >= 0);
295 doneLevel = std::min(doneLevel, data.levels[j]);
298 if (done && doneLevel > data.resultLevel) {
299 TerrainQueryResults results;
300 results.userData = data.userData;
301 results.numResults =
static_cast<int>(data.results.size());
302 results.results = data.results.data();
303 results.resultLevel = doneLevel;
305 data.callback(&results);
307 data.resultLevel = doneLevel;
310 data.complete = doneLevel == data.targetLevel;
313 std::lock_guard<std::mutex> lock(mutex);
316 for (
auto & query : currentQueries) {
317 if (!query.complete) {
318 queries.emplace_back(std::move(query));
Log implementation class.
constexpr Log getLogger(const char(&name)[LEN]) noexcept