diff --git a/src/OpenStreetMap-esp32.cpp b/src/OpenStreetMap-esp32.cpp index 76d0d7b..b7cf2dc 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 still-to-download tile ptr + jobs.push_back({x, static_cast(y), zoom, tileToReplace}); // push_back tile ptr to the job list } } @@ -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,17 @@ 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 int drawX = startOffsetX + (tileIndex % numberOfColums) * currentProvider->tileSize; + const int drawY = startOffsetY + (tileIndex / numberOfColums) * currentProvider->tileSize; + const uint16_t *tile = tilePointers[tileIndex]; + if (!tile) { - tileIndex++; + mapSprite.fillRect(drawX, drawY, currentProvider->tileSize, currentProvider->tileSize, OSM_BGCOLOR); continue; } - - 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,14 +324,13 @@ bool OpenStreetMap::fetchMap(LGFX_Sprite &mapSprite, double longitude, double la return false; } - updateCache(requiredTiles, zoom); - - if (!composeMap(mapSprite, requiredTiles, zoom)) + TileBufferList tilePointers; + updateCache(requiredTiles, zoom, tilePointers); + if (!composeMap(mapSprite, tilePointers)) { log_e("Failed to compose map"); return false; } - return true; } diff --git a/src/OpenStreetMap-esp32.hpp b/src/OpenStreetMap-esp32.hpp index 1bc3d10..b50d379 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(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; @@ -50,6 +51,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 +103,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);