From 0e051b096909ec6f71326a6985e43298c396567b Mon Sep 17 00:00:00 2001 From: Cellie Date: Fri, 6 Jun 2025 21:06:08 +0200 Subject: [PATCH 1/5] Build a vector of ptrs to use in `composeMap()` --- src/OpenStreetMap-esp32.cpp | 66 ++++++++++++++++++++----------------- src/OpenStreetMap-esp32.hpp | 9 ++--- 2 files changed, 40 insertions(+), 35 deletions(-) diff --git a/src/OpenStreetMap-esp32.cpp b/src/OpenStreetMap-esp32.cpp index 76d0d7b..08d4c61 100644 --- a/src/OpenStreetMap-esp32.cpp +++ b/src/OpenStreetMap-esp32.cpp @@ -159,12 +159,15 @@ CachedTile *OpenStreetMap::findUnusedTile(const tileList &requiredTiles, uint8_t return nullptr; // no unused tile found } -bool OpenStreetMap::isTileCachedOrBusy(uint32_t x, uint32_t y, uint8_t z) +bool OpenStreetMap::isTileCached(uint32_t x, uint32_t y, uint8_t z, TileBufferList &tilePointers) { for (const auto &tile : tilesCache) { - if (tile.x == x && tile.y == y && tile.z == z && (tile.valid || tile.busy)) + if (tile.x == x && tile.y == y && tile.z == z && tile.valid) + { + tilePointers.push_back(tile.buffer); return true; + } } return false; } @@ -197,11 +200,11 @@ bool OpenStreetMap::resizeTilesCache(uint16_t numberOfTiles) return true; } -void OpenStreetMap::updateCache(const tileList &requiredTiles, uint8_t zoom) +void OpenStreetMap::updateCache(const tileList &requiredTiles, uint8_t zoom, TileBufferList &tilePointers) { [[maybe_unused]] const unsigned long startMS = millis(); std::vector jobs; - makeJobList(requiredTiles, jobs, zoom); + makeJobList(requiredTiles, jobs, zoom, tilePointers); if (!jobs.empty()) { runJobs(jobs); @@ -209,18 +212,28 @@ void OpenStreetMap::updateCache(const tileList &requiredTiles, uint8_t zoom) } } -void OpenStreetMap::makeJobList(const tileList &requiredTiles, std::vector &jobs, uint8_t zoom) +void OpenStreetMap::makeJobList(const tileList &requiredTiles, std::vector &jobs, uint8_t zoom, TileBufferList &tilePointers) { for (const auto &[x, y] : requiredTiles) { - if (isTileCachedOrBusy(x, y, zoom) || y < 0 || y >= (1 << zoom)) + if (y < 0 || y >= (1 << zoom)) + { + tilePointers.push_back(nullptr); // we need to keep 1:1 grid alignment with requiredTiles for composeMap + continue; + } + + if (isTileCached(x, y, zoom, tilePointers)) // isTileCached will push_back a valid ptr if tile is cached continue; CachedTile *tileToReplace = findUnusedTile(requiredTiles, zoom); if (!tileToReplace) - continue; // Should never happen if cache sizing is correct + { + tilePointers.push_back(nullptr); // again, keep 1:1 aligned + continue; + } - jobs.push_back({x, static_cast(y), zoom, tileToReplace}); + tilePointers.push_back(tileToReplace->buffer); // push_back the non cached tile ptr + jobs.push_back({x, static_cast(y), zoom, tileToReplace}); // but first we have to download it } } @@ -240,7 +253,7 @@ void OpenStreetMap::runJobs(const std::vector &jobs) vTaskDelay(pdMS_TO_TICKS(1)); } -bool OpenStreetMap::composeMap(LGFX_Sprite &mapSprite, const tileList &requiredTiles, uint8_t zoom) +bool OpenStreetMap::composeMap(LGFX_Sprite &mapSprite, TileBufferList &tilePointers) { if (mapSprite.width() != mapWidth || mapSprite.height() != mapHeight) { @@ -255,30 +268,20 @@ bool OpenStreetMap::composeMap(LGFX_Sprite &mapSprite, const tileList &requiredT } } - int tileIndex = 0; - for (const auto &[tileX, tileY] : requiredTiles) + for (size_t tileIndex = 0; tileIndex < tilePointers.size(); ++tileIndex) { - if (tileY < 0 || tileY >= (1 << zoom)) + const uint16_t *tile = tilePointers[tileIndex]; + const int drawX = startOffsetX + (tileIndex % numberOfColums) * currentProvider->tileSize; + const int drawY = startOffsetY + (tileIndex / numberOfColums) * currentProvider->tileSize; + + if (!tile) { - tileIndex++; - continue; + // fill the area with black? easy and clean solution- + // write a test program that goes from pole to pole at low zoom to see what looks good + continue; // TODO: what to do with empty tiles? the map sprite might be declared static so there might still be 'old' map present } - int drawX = startOffsetX + (tileIndex % numberOfColums) * currentProvider->tileSize; - int drawY = startOffsetY + (tileIndex / numberOfColums) * currentProvider->tileSize; - - auto it = std::find_if(tilesCache.begin(), tilesCache.end(), - [&](const CachedTile &tile) - { - return tile.x == tileX && tile.y == tileY && tile.z == zoom && tile.valid; - }); - - if (it != tilesCache.end()) - mapSprite.pushImage(drawX, drawY, currentProvider->tileSize, currentProvider->tileSize, it->buffer); - else - log_w("Tile (z=%d, x=%d, y=%d) not found in cache", zoom, tileX, tileY); - - tileIndex++; + mapSprite.pushImage(drawX, drawY, currentProvider->tileSize, currentProvider->tileSize, tile); } mapSprite.setTextColor(TFT_BLACK); @@ -324,9 +327,10 @@ bool OpenStreetMap::fetchMap(LGFX_Sprite &mapSprite, double longitude, double la return false; } - updateCache(requiredTiles, zoom); + TileBufferList tilePointers; + updateCache(requiredTiles, zoom, tilePointers); - if (!composeMap(mapSprite, requiredTiles, zoom)) + if (!composeMap(mapSprite, tilePointers)) { log_e("Failed to compose map"); return false; diff --git a/src/OpenStreetMap-esp32.hpp b/src/OpenStreetMap-esp32.hpp index 1bc3d10..40211be 100644 --- a/src/OpenStreetMap-esp32.hpp +++ b/src/OpenStreetMap-esp32.hpp @@ -50,6 +50,7 @@ constexpr int OSM_SINGLECORE_NUMBER = 1; static_assert(OSM_SINGLECORE_NUMBER < 2, "OSM_SINGLECORE_NUMBER must be 0 or 1 (ESP32 has only 2 cores)"); using tileList = std::vector>; +using TileBufferList = std::vector; namespace { @@ -101,16 +102,16 @@ class OpenStreetMap double lon2tile(double lon, uint8_t zoom); double lat2tile(double lat, uint8_t zoom); void computeRequiredTiles(double longitude, double latitude, uint8_t zoom, tileList &requiredTiles); - void updateCache(const tileList &requiredTiles, uint8_t zoom); + void updateCache(const tileList &requiredTiles, uint8_t zoom, TileBufferList &tilePointers); bool startTileWorkerTasks(); - void makeJobList(const tileList &requiredTiles, std::vector &jobs, uint8_t zoom); + void makeJobList(const tileList &requiredTiles, std::vector &jobs, uint8_t zoom, TileBufferList &tilePointers); void runJobs(const std::vector &jobs); CachedTile *findUnusedTile(const tileList &requiredTiles, uint8_t zoom); - bool isTileCachedOrBusy(uint32_t x, uint32_t y, uint8_t z); + bool isTileCached(uint32_t x, uint32_t y, uint8_t z, TileBufferList &tilePointers); bool fetchTile(CachedTile &tile, uint32_t x, uint32_t y, uint8_t zoom, String &result); std::optional> urlToBuffer(const char *url, String &result); bool fillBuffer(WiFiClient *stream, MemoryBuffer &buffer, size_t contentSize, String &result); - bool composeMap(LGFX_Sprite &mapSprite, const tileList &requiredTiles, uint8_t zoom); + bool composeMap(LGFX_Sprite &mapSprite,TileBufferList &tilePointers); static void tileFetcherTask(void *param); static void PNGDraw(PNGDRAW *pDraw); From 0d52b646f0beb6992890368564ed9aa89d72aee8 Mon Sep 17 00:00:00 2001 From: Cellie Date: Fri, 6 Jun 2025 21:11:39 +0200 Subject: [PATCH 2/5] Cleanup --- src/OpenStreetMap-esp32.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/OpenStreetMap-esp32.cpp b/src/OpenStreetMap-esp32.cpp index 08d4c61..d530096 100644 --- a/src/OpenStreetMap-esp32.cpp +++ b/src/OpenStreetMap-esp32.cpp @@ -232,7 +232,7 @@ void OpenStreetMap::makeJobList(const tileList &requiredTiles, std::vectorbuffer); // push_back the non cached tile ptr + tilePointers.push_back(tileToReplace->buffer); // push_back the still-to-download tile ptr jobs.push_back({x, static_cast(y), zoom, tileToReplace}); // but first we have to download it } } @@ -276,9 +276,10 @@ bool OpenStreetMap::composeMap(LGFX_Sprite &mapSprite, TileBufferList &tilePoint if (!tile) { - // fill the area with black? easy and clean solution- + // TODO: what to do with empty tiles? the map sprite might be declared static so there might still be 'old' map present + // fill the area with black? easy and clean solution // write a test program that goes from pole to pole at low zoom to see what looks good - continue; // TODO: what to do with empty tiles? the map sprite might be declared static so there might still be 'old' map present + continue; } mapSprite.pushImage(drawX, drawY, currentProvider->tileSize, currentProvider->tileSize, tile); From 61e78c8b3815d6d13dcbec183192a274cc190479 Mon Sep 17 00:00:00 2001 From: Cellie Date: Fri, 6 Jun 2025 23:13:25 +0200 Subject: [PATCH 3/5] Fill 'missing' tiles with the new exposed `OSM_BGCOLOR` --- src/OpenStreetMap-esp32.cpp | 10 +++------- src/OpenStreetMap-esp32.hpp | 1 + 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/OpenStreetMap-esp32.cpp b/src/OpenStreetMap-esp32.cpp index d530096..b45ec60 100644 --- a/src/OpenStreetMap-esp32.cpp +++ b/src/OpenStreetMap-esp32.cpp @@ -270,18 +270,14 @@ bool OpenStreetMap::composeMap(LGFX_Sprite &mapSprite, TileBufferList &tilePoint for (size_t tileIndex = 0; tileIndex < tilePointers.size(); ++tileIndex) { - const uint16_t *tile = tilePointers[tileIndex]; const int drawX = startOffsetX + (tileIndex % numberOfColums) * currentProvider->tileSize; const int drawY = startOffsetY + (tileIndex / numberOfColums) * currentProvider->tileSize; - + const uint16_t *tile = tilePointers[tileIndex]; if (!tile) { - // TODO: what to do with empty tiles? the map sprite might be declared static so there might still be 'old' map present - // fill the area with black? easy and clean solution - // write a test program that goes from pole to pole at low zoom to see what looks good - continue; + mapSprite.fillRect(drawX, drawY, currentProvider->tileSize, currentProvider->tileSize, OSM_BGCOLOR); + continue; } - mapSprite.pushImage(drawX, drawY, currentProvider->tileSize, currentProvider->tileSize, tile); } diff --git a/src/OpenStreetMap-esp32.hpp b/src/OpenStreetMap-esp32.hpp index 40211be..dcd3412 100644 --- a/src/OpenStreetMap-esp32.hpp +++ b/src/OpenStreetMap-esp32.hpp @@ -40,6 +40,7 @@ #include "HTTPClientRAII.hpp" #include "fonts/DejaVu9-modded.h" +constexpr uint16_t OSM_BGCOLOR = lgfx::color565(0, 0, 0); constexpr uint16_t OSM_TILE_TIMEOUT_MS = 500; constexpr UBaseType_t OSM_TASK_PRIORITY = 1; constexpr uint32_t OSM_TASK_STACKSIZE = 5120; From ee7ffa88915567e0933493877a083edba289f22b Mon Sep 17 00:00:00 2001 From: Cellie Date: Sat, 7 Jun 2025 11:50:03 +0200 Subject: [PATCH 4/5] Set `OSM_BGCOLOR` so attribution is always visible --- src/OpenStreetMap-esp32.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenStreetMap-esp32.hpp b/src/OpenStreetMap-esp32.hpp index dcd3412..b50d379 100644 --- a/src/OpenStreetMap-esp32.hpp +++ b/src/OpenStreetMap-esp32.hpp @@ -40,7 +40,7 @@ #include "HTTPClientRAII.hpp" #include "fonts/DejaVu9-modded.h" -constexpr uint16_t OSM_BGCOLOR = lgfx::color565(0, 0, 0); +constexpr uint16_t OSM_BGCOLOR = lgfx::color565(100, 110, 120); constexpr uint16_t OSM_TILE_TIMEOUT_MS = 500; constexpr UBaseType_t OSM_TASK_PRIORITY = 1; constexpr uint32_t OSM_TASK_STACKSIZE = 5120; From a8aa588c6147b84bf18b08408d348febfcc06bbb Mon Sep 17 00:00:00 2001 From: Cellie Date: Sat, 7 Jun 2025 11:52:00 +0200 Subject: [PATCH 5/5] Cleanup --- src/OpenStreetMap-esp32.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/OpenStreetMap-esp32.cpp b/src/OpenStreetMap-esp32.cpp index b45ec60..b7cf2dc 100644 --- a/src/OpenStreetMap-esp32.cpp +++ b/src/OpenStreetMap-esp32.cpp @@ -233,7 +233,7 @@ void OpenStreetMap::makeJobList(const tileList &requiredTiles, std::vectorbuffer); // push_back the still-to-download tile ptr - jobs.push_back({x, static_cast(y), zoom, tileToReplace}); // but first we have to download it + jobs.push_back({x, static_cast(y), zoom, tileToReplace}); // push_back tile ptr to the job list } } @@ -326,13 +326,11 @@ bool OpenStreetMap::fetchMap(LGFX_Sprite &mapSprite, double longitude, double la TileBufferList tilePointers; updateCache(requiredTiles, zoom, tilePointers); - if (!composeMap(mapSprite, tilePointers)) { log_e("Failed to compose map"); return false; } - return true; }