From ea979f59d4b0612fadab4fe8fb43e45346ec893c Mon Sep 17 00:00:00 2001 From: Cellie Date: Wed, 4 Jun 2025 14:10:16 +0200 Subject: [PATCH 01/28] Add code to use multiple tile formats and providers --- src/CachedTile.hpp | 5 ++- src/OpenStreetMap-esp32.cpp | 88 +++++++++++++++++++++++++------------ src/OpenStreetMap-esp32.hpp | 12 +++-- src/TileProvider.hpp | 74 +++++++++++++++++++++++++++++++ 4 files changed, 144 insertions(+), 35 deletions(-) create mode 100644 src/TileProvider.hpp diff --git a/src/CachedTile.hpp b/src/CachedTile.hpp index f6f3227..d56802d 100644 --- a/src/CachedTile.hpp +++ b/src/CachedTile.hpp @@ -50,9 +50,10 @@ struct CachedTile free(); } - bool allocate() + bool allocate(int tileSize) { - buffer = static_cast(heap_caps_malloc(256 * 256 * sizeof(uint16_t), MALLOC_CAP_SPIRAM)); + log_d("allocating a %i px tile", tileSize); + buffer = static_cast(heap_caps_malloc(tileSize * tileSize * sizeof(uint16_t), MALLOC_CAP_SPIRAM)); return buffer != nullptr; } diff --git a/src/OpenStreetMap-esp32.cpp b/src/OpenStreetMap-esp32.cpp index d6b84ab..8fecaac 100644 --- a/src/OpenStreetMap-esp32.cpp +++ b/src/OpenStreetMap-esp32.cpp @@ -87,26 +87,26 @@ void OpenStreetMap::computeRequiredTiles(double longitude, double latitude, uint const int32_t targetTileY = static_cast(exactTileY); // Compute the offset inside the tile for the given coordinates - const int16_t targetOffsetX = (exactTileX - targetTileX) * OSM_TILESIZE; - const int16_t targetOffsetY = (exactTileY - targetTileY) * OSM_TILESIZE; + const int16_t targetOffsetX = (exactTileX - targetTileX) * currentProvider->tileSize; + const int16_t targetOffsetY = (exactTileY - targetTileY) * currentProvider->tileSize; // Compute the offset for tiles covering the map area to keep the location centered const int16_t tilesOffsetX = mapWidth / 2 - targetOffsetX; const int16_t tilesOffsetY = mapHeight / 2 - targetOffsetY; // Compute number of colums required - const float colsLeft = 1.0 * tilesOffsetX / OSM_TILESIZE; - const float colsRight = float(mapWidth - (tilesOffsetX + OSM_TILESIZE)) / OSM_TILESIZE; + const float colsLeft = 1.0 * tilesOffsetX / currentProvider->tileSize; + const float colsRight = float(mapWidth - (tilesOffsetX + currentProvider->tileSize)) / currentProvider->tileSize; numberOfColums = ceil(colsLeft) + 1 + ceil(colsRight); - startOffsetX = tilesOffsetX - (ceil(colsLeft) * OSM_TILESIZE); + startOffsetX = tilesOffsetX - (ceil(colsLeft) * currentProvider->tileSize); // Compute number of rows required - const float rowsTop = 1.0 * tilesOffsetY / OSM_TILESIZE; - const float rowsBottom = float(mapHeight - (tilesOffsetY + OSM_TILESIZE)) / OSM_TILESIZE; + const float rowsTop = 1.0 * tilesOffsetY / currentProvider->tileSize; + const float rowsBottom = float(mapHeight - (tilesOffsetY + currentProvider->tileSize)) / currentProvider->tileSize; const uint32_t numberOfRows = ceil(rowsTop) + 1 + ceil(rowsBottom); - startOffsetY = tilesOffsetY - (ceil(rowsTop) * OSM_TILESIZE); + startOffsetY = tilesOffsetY - (ceil(rowsTop) * currentProvider->tileSize); log_v(" Need %i * %i tiles. First tile offset is %d,%d", numberOfColums, numberOfRows, startOffsetX, startOffsetY); @@ -179,9 +179,6 @@ void OpenStreetMap::freeTilesCache() bool OpenStreetMap::resizeTilesCache(uint16_t numberOfTiles) { - if (tilesCache.size() == numberOfTiles) - return true; - if (!numberOfTiles) { log_e("Invalid cache size: %d", numberOfTiles); @@ -193,7 +190,7 @@ bool OpenStreetMap::resizeTilesCache(uint16_t numberOfTiles) for (auto &tile : tilesCache) { - if (!tile.allocate()) + if (!tile.allocate(currentProvider->tileSize)) { log_e("Tile cache allocation failed!"); freeTilesCache(); @@ -270,8 +267,8 @@ bool OpenStreetMap::composeMap(LGFX_Sprite &mapSprite, const tileList &requiredT continue; } - int drawX = startOffsetX + (tileIndex % numberOfColums) * OSM_TILESIZE; - int drawY = startOffsetY + (tileIndex / numberOfColums) * OSM_TILESIZE; + 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) @@ -280,7 +277,7 @@ bool OpenStreetMap::composeMap(LGFX_Sprite &mapSprite, const tileList &requiredT }); if (it != tilesCache.end()) - mapSprite.pushImage(drawX, drawY, OSM_TILESIZE, OSM_TILESIZE, it->buffer); + 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); @@ -293,7 +290,7 @@ bool OpenStreetMap::composeMap(LGFX_Sprite &mapSprite, const tileList &requiredT mapSprite.setTextColor(TFT_WHITE, TFT_BLACK); else mapSprite.setTextColor(TFT_BLACK); - mapSprite.drawRightString(" Map data from OpenStreetMap.org ", + mapSprite.drawRightString(currentProvider->attribution, mapSprite.width(), mapSprite.height() - 10, &DejaVu9); mapSprite.setTextColor(TFT_WHITE, TFT_BLACK); @@ -308,7 +305,7 @@ bool OpenStreetMap::fetchMap(LGFX_Sprite &mapSprite, double longitude, double la return false; } - if (!zoom || zoom > OSM_MAX_ZOOM) + if (zoom < currentProvider->minZoom || zoom > currentProvider->maxZoom) { log_e("Invalid zoom level: %d", zoom); return false; @@ -322,8 +319,9 @@ bool OpenStreetMap::fetchMap(LGFX_Sprite &mapSprite, double longitude, double la if (!tilesCache.capacity()) { - log_w("Cache not initialized, setting up a default cache..."); - if (!resizeTilesCache(OSM_DEFAULT_CACHE_ITEMS)) + const int worstCase = getTileCount(mapWidth, mapHeight); + log_w("Cache not initialized, allocating %i tiles", worstCase); + if (!resizeTilesCache(worstCase)) { log_e("Could not allocate tile cache"); return false; @@ -432,22 +430,25 @@ std::optional> OpenStreetMap::urlToBuffer(const ch void OpenStreetMap::PNGDraw(PNGDRAW *pDraw) { - uint16_t *destRow = currentInstance->currentTileBuffer + (pDraw->y * OSM_TILESIZE); + uint16_t *destRow = currentInstance->currentTileBuffer + (pDraw->y * currentInstance->currentProvider->tileSize); getPNGCurrentCore()->getLineAsRGB565(pDraw, destRow, PNG_RGB565_BIG_ENDIAN, 0xffffffff); } bool OpenStreetMap::fetchTile(CachedTile &tile, uint32_t x, uint32_t y, uint8_t zoom, String &result) { - char url[64]; - snprintf(url, sizeof(url), "https://tile.openstreetmap.org/%u/%u/%u.png", - static_cast(zoom), - static_cast(x), - static_cast(y)); - - const auto buffer = urlToBuffer(url, result); + String url = currentProvider->urlTemplate; + url.replace("{x}", String(x)); + url.replace("{y}", String(y)); + url.replace("{z}", String(zoom)); + if (currentProvider->requiresApiKey && strstr(url.c_str(), "{apiKey}")) + url.replace("{apiKey}", currentProvider->apiKey); + + const auto buffer = urlToBuffer(url.c_str(), result); if (!buffer) return false; + url.clear(); + PNG *png = getPNGCurrentCore(); const int16_t rc = png->openRAM(buffer.value()->get(), buffer.value()->size(), PNGDraw); if (rc != PNG_SUCCESS) @@ -456,7 +457,7 @@ bool OpenStreetMap::fetchTile(CachedTile &tile, uint32_t x, uint32_t y, uint8_t return false; } - if (png->getWidth() != OSM_TILESIZE || png->getHeight() != OSM_TILESIZE) + if (png->getWidth() != currentProvider->tileSize || png->getHeight() != currentProvider->tileSize) { result = "Unexpected tile size: w=" + String(png->getWidth()) + " h=" + String(png->getHeight()); return false; @@ -468,7 +469,7 @@ bool OpenStreetMap::fetchTile(CachedTile &tile, uint32_t x, uint32_t y, uint8_t const int decodeResult = png->decode(0, PNG_FAST_PALETTE); if (decodeResult != PNG_SUCCESS) { - result = "Decoding " + String(url) + " failed with code: " + String(decodeResult); + result = "Decoding " + url + " failed with code: " + String(decodeResult); tile.valid = false; return false; } @@ -550,3 +551,32 @@ bool OpenStreetMap::startTileWorkerTasks() log_i("Started %d tile worker task(s)", numberOfWorkers); return true; } + +int OpenStreetMap::getTileCount(int mapWidth, int mapHeight) +{ + const int tileSize = currentProvider->tileSize; + int tilesX = (mapWidth + tileSize - 1) / tileSize + 1; + int tilesY = (mapHeight + tileSize - 1) / tileSize + 1; + log_i("need %i tiles for %ipx by %ipx map", tilesX * tilesY, mapWidth, mapHeight); + return tilesX * tilesY; +} + +bool OpenStreetMap::setTileProvider(int index) +{ + if (index < 0 || index >= OSM_TILEPROVIDERS) + { + log_e("invalid provider index"); + return false; + } + + log_i("trying to change current provider '%s'", currentProvider->name); + + currentProvider = &tileProviders[index]; + if (!resizeTilesCache(getTileCount(mapWidth, mapHeight))) + { + log_e("could not allocate %i tiles for current mapsize", getTileCount(mapWidth, mapHeight)); + return false; + } + log_i("provider is changed to '%s'", currentProvider->name); + return true; +} diff --git a/src/OpenStreetMap-esp32.hpp b/src/OpenStreetMap-esp32.hpp index 0ee9d8f..80cb0ec 100644 --- a/src/OpenStreetMap-esp32.hpp +++ b/src/OpenStreetMap-esp32.hpp @@ -33,15 +33,13 @@ #include #include +#include "TileProvider.hpp" #include "CachedTile.hpp" #include "TileJob.hpp" #include "MemoryBuffer.hpp" #include "HTTPClientRAII.hpp" -constexpr uint16_t OSM_TILESIZE = 256; constexpr uint16_t OSM_TILE_TIMEOUT_MS = 2500; -constexpr uint16_t OSM_DEFAULT_CACHE_ITEMS = 10; -constexpr uint16_t OSM_MAX_ZOOM = 18; constexpr UBaseType_t OSM_TASK_PRIORITY = 10; constexpr uint32_t OSM_TASK_STACKSIZE = 5120; constexpr uint32_t OSM_JOB_QUEUE_SIZE = 50; @@ -87,12 +85,18 @@ class OpenStreetMap ~OpenStreetMap(); + bool setTileProvider(int index); void setSize(uint16_t w, uint16_t h); + int getTileCount(int mapWidth, int mapHeight); // returns the max number of tiles needed bool resizeTilesCache(uint16_t numberOfTiles); - void freeTilesCache(); bool fetchMap(LGFX_Sprite &sprite, double longitude, double latitude, uint8_t zoom); + void freeTilesCache(); + int getMinZoom() const { return currentProvider->minZoom; } + int getMaxZoom() const { return currentProvider->maxZoom; } + private: + const TileProvider *currentProvider = &tileProviders[0]; std::vector tilesCache; static inline OpenStreetMap *currentInstance = nullptr; static inline thread_local uint16_t *currentTileBuffer = nullptr; diff --git a/src/TileProvider.hpp b/src/TileProvider.hpp new file mode 100644 index 0000000..eb09009 --- /dev/null +++ b/src/TileProvider.hpp @@ -0,0 +1,74 @@ +/* + Copyright (c) 2025 Cellie https://github.com/CelliesProjects/OpenStreetMap-esp32 + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + SPDX-License-Identifier: MIT + */ + +#ifndef TILEPROVIDER_HPP_ +#define TILEPROVIDER_HPP_ + +struct TileProvider +{ + const char *name; + const char *urlTemplate; + const char *attribution; + bool requiresApiKey; + const char *apiKey; + int maxZoom; + int minZoom; + int tileSize; +}; + +const TileProvider tileProviders[] = + { + /* + {"OSM Standard", + "https://tile.openstreetmap.org/{z}/{x}/{y}.png", + "(c) OpenStreetMap contributors", + false, + "", + 19, 0, 256}, + */ + {"Thunderforest Cycle 512px", + "https://tile.thunderforest.com/cycle/{z}/{x}/{y}@2x.png?apikey={apiKey}", + "(c) Thunderforest, OpenStreetMap contributors", + true, + "86b06b8ad32644a29f2c087c3ad60d25", + 22, 0, 512}, + + {"Thunderforest Cycle 256px", + "https://tile.thunderforest.com/cycle/{z}/{x}/{y}.png?apikey={apiKey}", + "(c) Thunderforest, OpenStreetMap contributors", + true, + "86b06b8ad32644a29f2c087c3ad60d25", + 22, 0, 256}, + + {"Mapbox Streets", + "https://api.mapbox.com/styles/v1/mapbox/streets-v11/tiles/512/{z}/{x}/{y}@2x?access_token={apiKey}", + "(c) Mapbox, OpenStreetMap contributors", + true, + "YOUR_MAPBOX_KEY", + 22, 0, 512}}; + +constexpr int OSM_TILEPROVIDERS = sizeof(tileProviders) / sizeof(TileProvider); + +static_assert(OSM_TILEPROVIDERS > 0, "No TileProvider configured"); + +#endif From b64bdfe173caebaadb833dd7598674fadcabfef1 Mon Sep 17 00:00:00 2001 From: Cellie Date: Wed, 4 Jun 2025 14:34:14 +0200 Subject: [PATCH 02/28] Refactor TileProvider setup --- src/TileProvider.hpp | 51 ++++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/src/TileProvider.hpp b/src/TileProvider.hpp index eb09009..8513ab0 100644 --- a/src/TileProvider.hpp +++ b/src/TileProvider.hpp @@ -36,36 +36,31 @@ struct TileProvider int tileSize; }; -const TileProvider tileProviders[] = - { - /* - {"OSM Standard", - "https://tile.openstreetmap.org/{z}/{x}/{y}.png", - "(c) OpenStreetMap contributors", - false, - "", - 19, 0, 256}, - */ - {"Thunderforest Cycle 512px", - "https://tile.thunderforest.com/cycle/{z}/{x}/{y}@2x.png?apikey={apiKey}", - "(c) Thunderforest, OpenStreetMap contributors", - true, - "86b06b8ad32644a29f2c087c3ad60d25", - 22, 0, 512}, +const TileProvider osmStandard = { + "OSM Standard", + "https://tile.openstreetmap.org/{z}/{x}/{y}.png", + "(c) OpenStreetMap contributors", + false, + "", + 19, 0, 256}; - {"Thunderforest Cycle 256px", - "https://tile.thunderforest.com/cycle/{z}/{x}/{y}.png?apikey={apiKey}", - "(c) Thunderforest, OpenStreetMap contributors", - true, - "86b06b8ad32644a29f2c087c3ad60d25", - 22, 0, 256}, +const TileProvider ThunderForestCycle512 = { + "Thunderforest Cycle 512px", + "https://tile.thunderforest.com/cycle/{z}/{x}/{y}@2x.png?apikey={apiKey}", + "(c) Thunderforest, OpenStreetMap contributors", + true, + "YOUR_THUNDERFOREST_KEY", + 22, 0, 512}; - {"Mapbox Streets", - "https://api.mapbox.com/styles/v1/mapbox/streets-v11/tiles/512/{z}/{x}/{y}@2x?access_token={apiKey}", - "(c) Mapbox, OpenStreetMap contributors", - true, - "YOUR_MAPBOX_KEY", - 22, 0, 512}}; +const TileProvider ThunderForestCycle256 = { + "Thunderforest Cycle 256px", + "https://tile.thunderforest.com/cycle/{z}/{x}/{y}.png?apikey={apiKey}", + "(c) Thunderforest, OpenStreetMap contributors", + true, + "YOUR_THUNDERFOREST_KEY", + 22, 0, 256}; + +const TileProvider tileProviders[] = {osmStandard, ThunderForestCycle512, ThunderForestCycle256}; constexpr int OSM_TILEPROVIDERS = sizeof(tileProviders) / sizeof(TileProvider); From ccd0dfd3a0d1a3201a8f250e8aa1a2093138735a Mon Sep 17 00:00:00 2001 From: Cellie Date: Wed, 4 Jun 2025 14:51:50 +0200 Subject: [PATCH 03/28] Add example declaration for multiple tile providers --- src/TileProvider.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/TileProvider.hpp b/src/TileProvider.hpp index 8513ab0..c5d6ff8 100644 --- a/src/TileProvider.hpp +++ b/src/TileProvider.hpp @@ -60,7 +60,8 @@ const TileProvider ThunderForestCycle256 = { "YOUR_THUNDERFOREST_KEY", 22, 0, 256}; -const TileProvider tileProviders[] = {osmStandard, ThunderForestCycle512, ThunderForestCycle256}; +//const TileProvider tileProviders[] = {osmStandard, ThunderForestCycle512, ThunderForestCycle256}; +const TileProvider tileProviders[] = {osmStandard}; constexpr int OSM_TILEPROVIDERS = sizeof(tileProviders) / sizeof(TileProvider); From 41fa98fae1452a2168686ce6699b18a022fb014a Mon Sep 17 00:00:00 2001 From: Cellie Date: Wed, 4 Jun 2025 14:52:22 +0200 Subject: [PATCH 04/28] Cleanup --- src/OpenStreetMap-esp32.cpp | 2 -- src/OpenStreetMap-esp32.hpp | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/OpenStreetMap-esp32.cpp b/src/OpenStreetMap-esp32.cpp index 8fecaac..edc6f11 100644 --- a/src/OpenStreetMap-esp32.cpp +++ b/src/OpenStreetMap-esp32.cpp @@ -569,8 +569,6 @@ bool OpenStreetMap::setTileProvider(int index) return false; } - log_i("trying to change current provider '%s'", currentProvider->name); - currentProvider = &tileProviders[index]; if (!resizeTilesCache(getTileCount(mapWidth, mapHeight))) { diff --git a/src/OpenStreetMap-esp32.hpp b/src/OpenStreetMap-esp32.hpp index 80cb0ec..c3806e7 100644 --- a/src/OpenStreetMap-esp32.hpp +++ b/src/OpenStreetMap-esp32.hpp @@ -39,7 +39,7 @@ #include "MemoryBuffer.hpp" #include "HTTPClientRAII.hpp" -constexpr uint16_t OSM_TILE_TIMEOUT_MS = 2500; +constexpr uint16_t OSM_TILE_TIMEOUT_MS = 250; constexpr UBaseType_t OSM_TASK_PRIORITY = 10; constexpr uint32_t OSM_TASK_STACKSIZE = 5120; constexpr uint32_t OSM_JOB_QUEUE_SIZE = 50; From 03e0d19c4ffec41ed7199097a6a9f380d9f7ec24 Mon Sep 17 00:00:00 2001 From: Cellie Date: Wed, 4 Jun 2025 15:07:02 +0200 Subject: [PATCH 05/28] Cleanup --- src/TileProvider.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TileProvider.hpp b/src/TileProvider.hpp index c5d6ff8..8f544ed 100644 --- a/src/TileProvider.hpp +++ b/src/TileProvider.hpp @@ -60,7 +60,7 @@ const TileProvider ThunderForestCycle256 = { "YOUR_THUNDERFOREST_KEY", 22, 0, 256}; -//const TileProvider tileProviders[] = {osmStandard, ThunderForestCycle512, ThunderForestCycle256}; +// const TileProvider tileProviders[] = {osmStandard, ThunderForestCycle512, ThunderForestCycle256}; const TileProvider tileProviders[] = {osmStandard}; constexpr int OSM_TILEPROVIDERS = sizeof(tileProviders) / sizeof(TileProvider); From c88488321012afa6787fbcb32439e9acec1815a7 Mon Sep 17 00:00:00 2001 From: Cellie Date: Wed, 4 Jun 2025 16:56:18 +0200 Subject: [PATCH 06/28] Combine if statements --- src/OpenStreetMap-esp32.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/OpenStreetMap-esp32.cpp b/src/OpenStreetMap-esp32.cpp index edc6f11..e07c175 100644 --- a/src/OpenStreetMap-esp32.cpp +++ b/src/OpenStreetMap-esp32.cpp @@ -317,15 +317,10 @@ bool OpenStreetMap::fetchMap(LGFX_Sprite &mapSprite, double longitude, double la return false; } - if (!tilesCache.capacity()) + if (!tilesCache.capacity() && !resizeTilesCache(getTileCount(mapWidth, mapHeight))) { - const int worstCase = getTileCount(mapWidth, mapHeight); - log_w("Cache not initialized, allocating %i tiles", worstCase); - if (!resizeTilesCache(worstCase)) - { - log_e("Could not allocate tile cache"); - return false; - } + log_e("Could not allocate tile cache"); + return false; } longitude = fmod(longitude + 180.0, 360.0) - 180.0; From 249fd5a918cf1a950f87d54133c0aaf67a6def0b Mon Sep 17 00:00:00 2001 From: Cellie Date: Wed, 4 Jun 2025 16:57:29 +0200 Subject: [PATCH 07/28] Update comment --- src/OpenStreetMap-esp32.hpp | 2 +- src/TileProvider.hpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/OpenStreetMap-esp32.hpp b/src/OpenStreetMap-esp32.hpp index c3806e7..c82f603 100644 --- a/src/OpenStreetMap-esp32.hpp +++ b/src/OpenStreetMap-esp32.hpp @@ -87,7 +87,7 @@ class OpenStreetMap bool setTileProvider(int index); void setSize(uint16_t w, uint16_t h); - int getTileCount(int mapWidth, int mapHeight); // returns the max number of tiles needed + int getTileCount(int mapWidth, int mapHeight); // returns the worst case number of tiles needed bool resizeTilesCache(uint16_t numberOfTiles); bool fetchMap(LGFX_Sprite &sprite, double longitude, double latitude, uint8_t zoom); void freeTilesCache(); diff --git a/src/TileProvider.hpp b/src/TileProvider.hpp index 8f544ed..3edc5c4 100644 --- a/src/TileProvider.hpp +++ b/src/TileProvider.hpp @@ -49,7 +49,7 @@ const TileProvider ThunderForestCycle512 = { "https://tile.thunderforest.com/cycle/{z}/{x}/{y}@2x.png?apikey={apiKey}", "(c) Thunderforest, OpenStreetMap contributors", true, - "YOUR_THUNDERFOREST_KEY", + "86b06b8ad32644a29f2c087c3ad60d25", 22, 0, 512}; const TileProvider ThunderForestCycle256 = { @@ -60,8 +60,8 @@ const TileProvider ThunderForestCycle256 = { "YOUR_THUNDERFOREST_KEY", 22, 0, 256}; -// const TileProvider tileProviders[] = {osmStandard, ThunderForestCycle512, ThunderForestCycle256}; -const TileProvider tileProviders[] = {osmStandard}; + const TileProvider tileProviders[] = {osmStandard, ThunderForestCycle512, ThunderForestCycle256}; +//const TileProvider tileProviders[] = {osmStandard}; constexpr int OSM_TILEPROVIDERS = sizeof(tileProviders) / sizeof(TileProvider); From 484c915cb4734a517720afff86cde510bf44c377 Mon Sep 17 00:00:00 2001 From: Cellie Date: Wed, 4 Jun 2025 17:13:23 +0200 Subject: [PATCH 08/28] Cleanup --- src/TileProvider.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TileProvider.hpp b/src/TileProvider.hpp index 3edc5c4..07dd46a 100644 --- a/src/TileProvider.hpp +++ b/src/TileProvider.hpp @@ -49,7 +49,7 @@ const TileProvider ThunderForestCycle512 = { "https://tile.thunderforest.com/cycle/{z}/{x}/{y}@2x.png?apikey={apiKey}", "(c) Thunderforest, OpenStreetMap contributors", true, - "86b06b8ad32644a29f2c087c3ad60d25", + "YOUR_THUNDERFOREST_KEY", 22, 0, 512}; const TileProvider ThunderForestCycle256 = { From 2b75214cfdf268cc8965b6a49a37c4ec2be9e32c Mon Sep 17 00:00:00 2001 From: Cellie Date: Wed, 4 Jun 2025 17:14:12 +0200 Subject: [PATCH 09/28] Fix providers --- src/TileProvider.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/TileProvider.hpp b/src/TileProvider.hpp index 07dd46a..c5d6ff8 100644 --- a/src/TileProvider.hpp +++ b/src/TileProvider.hpp @@ -60,8 +60,8 @@ const TileProvider ThunderForestCycle256 = { "YOUR_THUNDERFOREST_KEY", 22, 0, 256}; - const TileProvider tileProviders[] = {osmStandard, ThunderForestCycle512, ThunderForestCycle256}; -//const TileProvider tileProviders[] = {osmStandard}; +//const TileProvider tileProviders[] = {osmStandard, ThunderForestCycle512, ThunderForestCycle256}; +const TileProvider tileProviders[] = {osmStandard}; constexpr int OSM_TILEPROVIDERS = sizeof(tileProviders) / sizeof(TileProvider); From 9bff9fc112044cf38564875628af76ad3040b357 Mon Sep 17 00:00:00 2001 From: Cellie Date: Wed, 4 Jun 2025 19:27:04 +0200 Subject: [PATCH 10/28] =?UTF-8?q?Add=20a=20modded=20DejaVu9=20font=20with?= =?UTF-8?q?=20a=20percent=20sign=20=C2=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/DejaVu9-modded.h | 186 ++++++++++++++++++++++++++++++++++++ src/OpenStreetMap-esp32.cpp | 2 +- src/OpenStreetMap-esp32.hpp | 1 + src/TileProvider.hpp | 8 +- 4 files changed, 192 insertions(+), 5 deletions(-) create mode 100644 src/DejaVu9-modded.h diff --git a/src/DejaVu9-modded.h b/src/DejaVu9-modded.h new file mode 100644 index 0000000..06c3e8c --- /dev/null +++ b/src/DejaVu9-modded.h @@ -0,0 +1,186 @@ +/* DejaVu 9 + original ttf url : https://dejavu-fonts.github.io/ + original license : https://dejavu-fonts.github.io/License.html +This data has been converted to AdafruitGFX font format from DejaVuSans.ttf. + +Modded by Cellie - added a © sign 0xA9 with https://tchapi.github.io/Adafruit-GFX-Font-Customiser/ +*/ +const uint8_t DejaVu9Bitmaps[] PROGMEM = { + 0xFA, 0xFA, 0xB4, 0x28, 0xAF, 0xCA, 0xFD, 0x45, 0x00, 0x21, 0xEA, 0x38, + 0x38, 0xAF, 0x08, 0x44, 0xA4, 0xA8, 0x5A, 0x15, 0x25, 0x22, 0x31, 0x04, + 0x19, 0x9E, 0x66, 0xC0, 0xC0, 0x4A, 0xA1, 0x85, 0x52, 0xAB, 0x9D, 0x50, + 0x21, 0x3E, 0x42, 0x00, 0xC0, 0xC0, 0x80, 0x25, 0x25, 0x20, 0x69, 0x99, + 0x99, 0x60, 0xC9, 0x24, 0xB8, 0x64, 0x84, 0x44, 0x43, 0xC0, 0x69, 0x16, + 0x11, 0x60, 0x11, 0x94, 0xA9, 0x7C, 0x40, 0xF8, 0x8E, 0x11, 0xE0, 0x7C, + 0x8E, 0x99, 0x60, 0xF1, 0x22, 0x24, 0x40, 0x69, 0x96, 0x99, 0x60, 0x69, + 0x97, 0x13, 0xE0, 0x88, 0x8C, 0x04, 0xEE, 0x0E, 0x04, 0xFC, 0x0F, 0xC0, + 0x81, 0xC1, 0xDC, 0x80, 0xE1, 0x24, 0x40, 0x40, 0x3C, 0x42, 0x9D, 0xA5, + 0xA5, 0x9E, 0x40, 0x38, 0x30, 0xC4, 0x92, 0x7A, 0x18, 0x40, 0xF4, 0x63, + 0xE8, 0xC7, 0xC0, 0x72, 0x61, 0x08, 0x25, 0xC0, 0xF4, 0xE3, 0x18, 0xCF, + 0xC0, 0xF8, 0x8F, 0x88, 0xF0, 0xF8, 0x8F, 0x88, 0x80, 0x76, 0x61, 0x38, + 0xE5, 0xC0, 0x8C, 0x63, 0xF8, 0xC6, 0x20, 0xFE, 0x55, 0x55, 0x80, 0x8C, + 0xA9, 0x8A, 0x4A, 0x20, 0x88, 0x88, 0x88, 0xF0, 0x87, 0x3C, 0xED, 0xB6, + 0x18, 0x40, 0x8E, 0x73, 0x59, 0xCE, 0x20, 0x76, 0xE3, 0x18, 0xED, 0xC0, + 0xE9, 0x9E, 0x88, 0x80, 0x76, 0xE3, 0x18, 0xE9, 0xC2, 0xE4, 0xA5, 0xCA, + 0x4A, 0x20, 0x72, 0x28, 0x1C, 0x0A, 0x27, 0x00, 0xF9, 0x08, 0x42, 0x10, + 0x80, 0x8C, 0x63, 0x18, 0xC5, 0xC0, 0x86, 0x14, 0x92, 0x48, 0xC3, 0x00, + 0x49, 0x24, 0x8A, 0x85, 0x43, 0xE0, 0xA0, 0x50, 0xCD, 0x23, 0x0C, 0x31, + 0x28, 0xC0, 0x8A, 0x9C, 0x42, 0x10, 0x80, 0xF8, 0x44, 0x44, 0x43, 0xE0, + 0xEA, 0xAB, 0x91, 0x24, 0x48, 0xD5, 0x57, 0x31, 0x20, 0xF8, 0x90, 0x61, + 0x79, 0xF0, 0x88, 0x8E, 0x99, 0x9E, 0x78, 0x88, 0x70, 0x11, 0x17, 0x99, + 0x97, 0x69, 0xF8, 0x70, 0x34, 0x4E, 0x44, 0x44, 0x79, 0x99, 0x71, 0x60, + 0x88, 0x8E, 0x99, 0x99, 0xBE, 0x45, 0x55, 0x80, 0x88, 0x89, 0xAC, 0xA9, + 0xFF, 0xED, 0x26, 0x4C, 0x99, 0x20, 0xE9, 0x99, 0x90, 0x69, 0x99, 0x60, + 0xE9, 0x99, 0xE8, 0x80, 0x79, 0x99, 0x71, 0x10, 0xF2, 0x48, 0x68, 0x62, + 0xE0, 0x4F, 0x44, 0x47, 0x99, 0x99, 0x70, 0x44, 0x98, 0xA1, 0xC1, 0x00, + 0x93, 0x76, 0xBA, 0x24, 0x40, 0x8A, 0x88, 0xA8, 0x80, 0x44, 0x88, 0xA1, + 0xC1, 0x02, 0x18, 0x00, 0xF1, 0x24, 0xF0, 0x69, 0x64, 0x93, 0xFF, 0x80, + 0xC9, 0x34, 0x96, 0x01, 0x91, 0x80, 0x3C, 0x42, 0x99, 0xA5, 0xA1, 0xA5, + 0x99, 0x42, 0x3C +}; + +const GFXglyph DejaVu9Glyphs[] PROGMEM = { + { 0, 0, 0, 4, 0, 1 }, // 0x20 ' ' + { 1, 1, 7, 4, 1, -6 }, // 0x21 '!' + { 2, 3, 2, 5, 1, -6 }, // 0x22 '"' + { 3, 6, 7, 9, 1, -6 }, // 0x23 '#' + { 9, 6, 8, 7, 0, -6 }, // 0x24 '$' + { 15, 8, 7, 10, 0, -6 }, // 0x25 '%' + { 22, 6, 7, 9, 1, -6 }, // 0x26 '&' + { 28, 1, 2, 3, 1, -6 }, // 0x27 ''' + { 29, 2, 8, 5, 1, -7 }, // 0x28 '(' + { 31, 2, 8, 5, 1, -7 }, // 0x29 ')' + { 33, 5, 4, 6, 0, -6 }, // 0x2A '*' + { 36, 5, 5, 9, 1, -4 }, // 0x2B '+' + { 40, 1, 2, 4, 1, 0 }, // 0x2C ',' + { 41, 2, 1, 4, 1, -2 }, // 0x2D '-' + { 42, 1, 1, 4, 1, 0 }, // 0x2E '.' + { 43, 3, 7, 4, 0, -6 }, // 0x2F '/' + { 46, 4, 7, 7, 1, -6 }, // 0x30 '0' + { 50, 3, 7, 7, 2, -6 }, // 0x31 '1' + { 53, 5, 7, 7, 1, -6 }, // 0x32 '2' + { 58, 4, 7, 7, 1, -6 }, // 0x33 '3' + { 62, 5, 7, 7, 1, -6 }, // 0x34 '4' + { 67, 4, 7, 7, 1, -6 }, // 0x35 '5' + { 71, 4, 7, 7, 1, -6 }, // 0x36 '6' + { 75, 4, 7, 7, 1, -6 }, // 0x37 '7' + { 79, 4, 7, 7, 1, -6 }, // 0x38 '8' + { 83, 4, 7, 7, 1, -6 }, // 0x39 '9' + { 87, 1, 5, 4, 1, -4 }, // 0x3A ':' + { 88, 1, 6, 4, 1, -4 }, // 0x3B ';' + { 89, 6, 5, 9, 1, -4 }, // 0x3C '<' + { 93, 6, 3, 9, 1, -3 }, // 0x3D '=' + { 96, 6, 5, 9, 1, -4 }, // 0x3E '>' + { 100, 4, 7, 6, 1, -6 }, // 0x3F '?' + { 104, 8, 8, 11, 1, -6 }, // 0x40 '@' + { 112, 6, 7, 7, 0, -6 }, // 0x41 'A' + { 118, 5, 7, 8, 1, -6 }, // 0x42 'B' + { 123, 5, 7, 8, 1, -6 }, // 0x43 'C' + { 128, 5, 7, 8, 1, -6 }, // 0x44 'D' + { 133, 4, 7, 7, 1, -6 }, // 0x45 'E' + { 137, 4, 7, 7, 1, -6 }, // 0x46 'F' + { 141, 5, 7, 8, 1, -6 }, // 0x47 'G' + { 146, 5, 7, 8, 1, -6 }, // 0x48 'H' + { 151, 1, 7, 4, 1, -6 }, // 0x49 'I' + { 152, 2, 9, 4, 0, -6 }, // 0x4A 'J' + { 155, 5, 7, 7, 1, -6 }, // 0x4B 'K' + { 160, 4, 7, 6, 1, -6 }, // 0x4C 'L' + { 164, 6, 7, 9, 1, -6 }, // 0x4D 'M' + { 170, 5, 7, 8, 1, -6 }, // 0x4E 'N' + { 175, 5, 7, 8, 1, -6 }, // 0x4F 'O' + { 180, 4, 7, 7, 1, -6 }, // 0x50 'P' + { 184, 5, 8, 8, 1, -6 }, // 0x51 'Q' + { 189, 5, 7, 7, 1, -6 }, // 0x52 'R' + { 194, 6, 7, 8, 1, -6 }, // 0x53 'S' + { 200, 5, 7, 6, 0, -6 }, // 0x54 'T' + { 205, 5, 7, 8, 1, -6 }, // 0x55 'U' + { 210, 6, 7, 7, 0, -6 }, // 0x56 'V' + { 216, 9, 7, 8, -1, -6 }, // 0x57 'W' + { 224, 6, 7, 7, 0, -6 }, // 0x58 'X' + { 230, 5, 7, 6, 0, -6 }, // 0x59 'Y' + { 235, 5, 7, 6, 0, -6 }, // 0x5A 'Z' + { 240, 2, 8, 5, 1, -6 }, // 0x5B '[' + { 242, 3, 7, 4, 0, -6 }, // 0x5C '\' + { 245, 2, 8, 5, 1, -6 }, // 0x5D ']' + { 247, 6, 2, 9, 1, -6 }, // 0x5E '^' + { 249, 5, 1, 6, 0, 2 }, // 0x5F '_' + { 250, 2, 2, 6, 1, -7 }, // 0x60 '`' + { 251, 4, 5, 7, 1, -4 }, // 0x61 'a' + { 254, 4, 8, 7, 1, -7 }, // 0x62 'b' + { 258, 4, 5, 7, 1, -4 }, // 0x63 'c' + { 261, 4, 8, 7, 1, -7 }, // 0x64 'd' + { 265, 4, 5, 7, 1, -4 }, // 0x65 'e' + { 268, 4, 8, 4, 0, -7 }, // 0x66 'f' + { 272, 4, 7, 7, 1, -4 }, // 0x67 'g' + { 276, 4, 8, 7, 1, -7 }, // 0x68 'h' + { 280, 1, 7, 4, 1, -6 }, // 0x69 'i' + { 281, 2, 9, 4, 0, -6 }, // 0x6A 'j' + { 284, 4, 8, 6, 1, -7 }, // 0x6B 'k' + { 288, 1, 8, 4, 1, -7 }, // 0x6C 'l' + { 289, 7, 5, 10, 1, -4 }, // 0x6D 'm' + { 294, 4, 5, 7, 1, -4 }, // 0x6E 'n' + { 297, 4, 5, 7, 1, -4 }, // 0x6F 'o' + { 300, 4, 7, 7, 1, -4 }, // 0x70 'p' + { 304, 4, 7, 7, 1, -4 }, // 0x71 'q' + { 308, 3, 5, 5, 1, -4 }, // 0x72 'r' + { 310, 4, 5, 6, 1, -4 }, // 0x73 's' + { 313, 4, 6, 5, 0, -5 }, // 0x74 't' + { 316, 4, 5, 7, 1, -4 }, // 0x75 'u' + { 319, 7, 5, 6, -1, -4 }, // 0x76 'v' + { 324, 7, 5, 8, 0, -4 }, // 0x77 'w' + { 329, 5, 5, 6, 0, -4 }, // 0x78 'x' + { 333, 7, 7, 6, -1, -4 }, // 0x79 'y' + { 340, 4, 5, 7, 1, -4 }, // 0x7A 'z' + { 343, 3, 8, 6, 1, -6 }, // 0x7B '{' + { 346, 1, 9, 4, 1, -6 }, // 0x7C '|' + { 348, 3, 8, 6, 1, -6 }, // 0x7D '}' + { 351, 6, 3, 9, 1, -4 }, // 0x7E '~' + { 0, 0, 0, 0, 0, 0 }, // 0x7F 'non-printable' + { 0, 0, 0, 0, 0, 0 }, // 0x80 'non-printable' + { 0, 0, 0, 0, 0, 0 }, // 0x81 'non-printable' + { 0, 0, 0, 0, 0, 0 }, // 0x82 'non-printable' + { 0, 0, 0, 0, 0, 0 }, // 0x83 'non-printable' + { 0, 0, 0, 0, 0, 0 }, // 0x84 'non-printable' + { 0, 0, 0, 0, 0, 0 }, // 0x85 'non-printable' + { 0, 0, 0, 0, 0, 0 }, // 0x86 'non-printable' + { 0, 0, 0, 0, 0, 0 }, // 0x87 'non-printable' + { 0, 0, 0, 0, 0, 0 }, // 0x88 'non-printable' + { 0, 0, 0, 0, 0, 0 }, // 0x89 'non-printable' + { 0, 0, 0, 0, 0, 0 }, // 0x8A 'non-printable' + { 0, 0, 0, 0, 0, 0 }, // 0x8B 'non-printable' + { 0, 0, 0, 0, 0, 0 }, // 0x8C 'non-printable' + { 0, 0, 0, 0, 0, 0 }, // 0x8D 'non-printable' + { 0, 0, 0, 0, 0, 0 }, // 0x8E 'non-printable' + { 0, 0, 0, 0, 0, 0 }, // 0x8F 'non-printable' + { 0, 0, 0, 0, 0, 0 }, // 0x90 'non-printable' + { 0, 0, 0, 0, 0, 0 }, // 0x91 'non-printable' + { 0, 0, 0, 0, 0, 0 }, // 0x92 'non-printable' + { 0, 0, 0, 0, 0, 0 }, // 0x93 'non-printable' + { 0, 0, 0, 0, 0, 0 }, // 0x94 'non-printable' + { 0, 0, 0, 0, 0, 0 }, // 0x95 'non-printable' + { 0, 0, 0, 0, 0, 0 }, // 0x96 'non-printable' + { 0, 0, 0, 0, 0, 0 }, // 0x97 'non-printable' + { 0, 0, 0, 0, 0, 0 }, // 0x98 'non-printable' + { 0, 0, 0, 0, 0, 0 }, // 0x99 'non-printable' + { 0, 0, 0, 0, 0, 0 }, // 0x9A 'non-printable' + { 0, 0, 0, 0, 0, 0 }, // 0x9B 'non-printable' + { 0, 0, 0, 0, 0, 0 }, // 0x9C 'non-printable' + { 0, 0, 0, 0, 0, 0 }, // 0x9D 'non-printable' + { 0, 0, 0, 0, 0, 0 }, // 0x9E 'non-printable' + { 0, 0, 0, 0, 0, 0 }, // 0x9F 'non-printable' + { 0, 0, 0, 0, 0, 0 }, // 0xA0 ' ' + { 0, 0, 0, 0, 0, 0 }, // 0xA1 '¡' + { 0, 0, 0, 0, 0, 0 }, // 0xA2 '¢' + { 0, 0, 0, 0, 0, 0 }, // 0xA3 '£' + { 0, 0, 0, 0, 0, 0 }, // 0xA4 '¤' + { 0, 0, 0, 0, 0, 0 }, // 0xA5 '¥' + { 0, 0, 0, 0, 0, 0 }, // 0xA6 '¦' + { 0, 0, 0, 0, 0, 0 }, // 0xA7 '§' + { 0, 0, 0, 0, 0, 0 }, // 0xA8 '¨' + { 354, 8, 9, 10, 1, -7 } // 0xA9 '©' +}; + +const GFXfont DejaVu9Modded PROGMEM = { + (uint8_t*)DejaVu9Bitmaps, + (GFXglyph*)DejaVu9Glyphs, 0x20, 0xA9, 10 }; + diff --git a/src/OpenStreetMap-esp32.cpp b/src/OpenStreetMap-esp32.cpp index e07c175..f4b05e0 100644 --- a/src/OpenStreetMap-esp32.cpp +++ b/src/OpenStreetMap-esp32.cpp @@ -291,7 +291,7 @@ bool OpenStreetMap::composeMap(LGFX_Sprite &mapSprite, const tileList &requiredT else mapSprite.setTextColor(TFT_BLACK); mapSprite.drawRightString(currentProvider->attribution, - mapSprite.width(), mapSprite.height() - 10, &DejaVu9); + mapSprite.width(), mapSprite.height() - 10, &DejaVu9Modded); mapSprite.setTextColor(TFT_WHITE, TFT_BLACK); return true; diff --git a/src/OpenStreetMap-esp32.hpp b/src/OpenStreetMap-esp32.hpp index c82f603..5314686 100644 --- a/src/OpenStreetMap-esp32.hpp +++ b/src/OpenStreetMap-esp32.hpp @@ -38,6 +38,7 @@ #include "TileJob.hpp" #include "MemoryBuffer.hpp" #include "HTTPClientRAII.hpp" +#include "DejaVu9-modded.h" constexpr uint16_t OSM_TILE_TIMEOUT_MS = 250; constexpr UBaseType_t OSM_TASK_PRIORITY = 10; diff --git a/src/TileProvider.hpp b/src/TileProvider.hpp index c5d6ff8..d403b3e 100644 --- a/src/TileProvider.hpp +++ b/src/TileProvider.hpp @@ -39,7 +39,7 @@ struct TileProvider const TileProvider osmStandard = { "OSM Standard", "https://tile.openstreetmap.org/{z}/{x}/{y}.png", - "(c) OpenStreetMap contributors", + "© OpenStreetMap contributors", false, "", 19, 0, 256}; @@ -47,7 +47,7 @@ const TileProvider osmStandard = { const TileProvider ThunderForestCycle512 = { "Thunderforest Cycle 512px", "https://tile.thunderforest.com/cycle/{z}/{x}/{y}@2x.png?apikey={apiKey}", - "(c) Thunderforest, OpenStreetMap contributors", + "© Thunderforest, OpenStreetMap contributors", true, "YOUR_THUNDERFOREST_KEY", 22, 0, 512}; @@ -55,12 +55,12 @@ const TileProvider ThunderForestCycle512 = { const TileProvider ThunderForestCycle256 = { "Thunderforest Cycle 256px", "https://tile.thunderforest.com/cycle/{z}/{x}/{y}.png?apikey={apiKey}", - "(c) Thunderforest, OpenStreetMap contributors", + "© Thunderforest, OpenStreetMap contributors", true, "YOUR_THUNDERFOREST_KEY", 22, 0, 256}; -//const TileProvider tileProviders[] = {osmStandard, ThunderForestCycle512, ThunderForestCycle256}; +// const TileProvider tileProviders[] = {osmStandard, ThunderForestCycle512, ThunderForestCycle256}; const TileProvider tileProviders[] = {osmStandard}; constexpr int OSM_TILEPROVIDERS = sizeof(tileProviders) / sizeof(TileProvider); From fa14dea95757a3c269e85c7ff479e7ec30af69e0 Mon Sep 17 00:00:00 2001 From: Cellie Date: Thu, 5 Jun 2025 11:06:56 +0200 Subject: [PATCH 11/28] Cleanup --- README.md | 52 ++++++++++++++++++++++++-------- src/CachedTile.hpp | 1 - src/OpenStreetMap-esp32.cpp | 12 ++++---- src/OpenStreetMap-esp32.hpp | 11 ++++--- src/{ => fonts}/DejaVu9-modded.h | 0 5 files changed, 52 insertions(+), 24 deletions(-) rename src/{ => fonts}/DejaVu9-modded.h (100%) diff --git a/README.md b/README.md index ad7fdb4..69cbc82 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ A composed map can be pushed to the screen, saved to SD or used for further comp Downloaded tiles are cached in psram for reuse. This library should work on any ESP32 type with psram and a LovyanGFX compatible display. -OSM tiles are quite large -128kB per tile- so psram is required. +OSM tiles are quite large -128kB or 512kB per tile- so psram is required. This project is not endorsed by or affiliated with the OpenStreetMap Foundation. Use of any OSMF provided service is governed by the [OSMF Terms of Use](https://osmfoundation.org/wiki/Terms_of_Use). @@ -40,43 +40,71 @@ framework = arduino lib_deps = celliesprojects/OpenStreetMap-esp32@^1.0.6 lovyan03/LovyanGFX@^1.2.7 - https://github.com/bitbank2/PNGdec@^1.1.3 + bitbank2/PNGdec@^1.1.3 ``` +## Functions ### Set map size ```c++ -void setSize(uint16_t w, uint16_t h); +void setSize(w, h) ``` -- If no size is set a 320px by 240px map will be returned by `fetchMap`. +- If no size is set a 320px by 240px map will be returned. -### Resize cache +### Get required cache size ```c++ -bool resizeTilesCache(uint16_t numberOfTiles); +uint16_t tilesToCover(w, h) ``` -- If the cache is not resized before the first call to `fetchMap`, it will auto initialize with space for 10 tiles on the first call. -- Each tile allocates 128 kB psram. -- The cache content is cleared before resizing. +This returns the minimum required number of tiles for the given map size. +**Note:** Some 'lucky' coordinates might require less tiles than this. -### Free the memory used by the tile cache +### Resize the tiles cache ```c++ -void freeTilesCache(); +bool resizeTilesCache(uint16_t numberOfTiles) ``` +- If the cache is not resized before the first call to `fetchMap`, the cache will be auto initialized. +- The cache content is cleared before resizing. +- Each 256px tile allocates **128kB** psram. +- Each 512px tile allocates **512kB** psram. + +**Don't overallocate** +When resizing the cache, keep in mind that the map sprite itself also needs psram. +The PNG decoders -~50kB each- also live in psram. + ### Fetch a map ```c++ -bool fetchMap(LGFX_Sprite &map, double longitude, double latitude, uint8_t zoom); +bool fetchMap(LGFX_Sprite &map, double longitude, double latitude, uint8_t zoom) ``` - Overflowing `longitude` are wrapped and normalized to +-180°. - Overflowing `latitude` are clamped to +-90°. - Valid range for the `zoom` level is 1-18. +### Free the memory used by the tile cache + +```c++ +void freeTilesCache() +``` + +### Switch tile provider + +```c++ +bool setTileProvider(int index) +``` + +Switch to a tile provider that is user defined in `src/TileProvider.hpp`. +This will invalidate and possibly resize the cache if the new provider uses a different tile size. + +Returns false if no provider at the index is defined or if no cache could be allocated. + +**Note:** In the default setup there is only one provider defined. See `src/TileProvider.hpp` for example provider setups. + ## Example code ### Example returning the default 320x240 map diff --git a/src/CachedTile.hpp b/src/CachedTile.hpp index d56802d..683ed48 100644 --- a/src/CachedTile.hpp +++ b/src/CachedTile.hpp @@ -52,7 +52,6 @@ struct CachedTile bool allocate(int tileSize) { - log_d("allocating a %i px tile", tileSize); buffer = static_cast(heap_caps_malloc(tileSize * tileSize * sizeof(uint16_t), MALLOC_CAP_SPIRAM)); return buffer != nullptr; } diff --git a/src/OpenStreetMap-esp32.cpp b/src/OpenStreetMap-esp32.cpp index f4b05e0..5589a6b 100644 --- a/src/OpenStreetMap-esp32.cpp +++ b/src/OpenStreetMap-esp32.cpp @@ -317,7 +317,7 @@ bool OpenStreetMap::fetchMap(LGFX_Sprite &mapSprite, double longitude, double la return false; } - if (!tilesCache.capacity() && !resizeTilesCache(getTileCount(mapWidth, mapHeight))) + if (!tilesCache.capacity() && !resizeTilesCache(tilesToCover(mapWidth, mapHeight))) { log_e("Could not allocate tile cache"); return false; @@ -442,7 +442,7 @@ bool OpenStreetMap::fetchTile(CachedTile &tile, uint32_t x, uint32_t y, uint8_t if (!buffer) return false; - url.clear(); + url.clear(); //TODO: find out, is this smart? test test test PNG *png = getPNGCurrentCore(); const int16_t rc = png->openRAM(buffer.value()->get(), buffer.value()->size(), PNGDraw); @@ -547,7 +547,7 @@ bool OpenStreetMap::startTileWorkerTasks() return true; } -int OpenStreetMap::getTileCount(int mapWidth, int mapHeight) +uint16_t OpenStreetMap::tilesToCover(int mapWidth, int mapHeight) { const int tileSize = currentProvider->tileSize; int tilesX = (mapWidth + tileSize - 1) / tileSize + 1; @@ -565,11 +565,11 @@ bool OpenStreetMap::setTileProvider(int index) } currentProvider = &tileProviders[index]; - if (!resizeTilesCache(getTileCount(mapWidth, mapHeight))) + if (!resizeTilesCache(tilesToCover(mapWidth, mapHeight))) { - log_e("could not allocate %i tiles for current mapsize", getTileCount(mapWidth, mapHeight)); + log_e("could not allocate %i tiles for current mapsize", tilesToCover(mapWidth, mapHeight)); return false; } - log_i("provider is changed to '%s'", currentProvider->name); + log_i("provider changed to '%s'", currentProvider->name); return true; } diff --git a/src/OpenStreetMap-esp32.hpp b/src/OpenStreetMap-esp32.hpp index 5314686..10873eb 100644 --- a/src/OpenStreetMap-esp32.hpp +++ b/src/OpenStreetMap-esp32.hpp @@ -38,7 +38,7 @@ #include "TileJob.hpp" #include "MemoryBuffer.hpp" #include "HTTPClientRAII.hpp" -#include "DejaVu9-modded.h" +#include "fonts/DejaVu9-modded.h" constexpr uint16_t OSM_TILE_TIMEOUT_MS = 250; constexpr UBaseType_t OSM_TASK_PRIORITY = 10; @@ -88,14 +88,15 @@ class OpenStreetMap bool setTileProvider(int index); void setSize(uint16_t w, uint16_t h); - int getTileCount(int mapWidth, int mapHeight); // returns the worst case number of tiles needed + uint16_t tilesToCover(int mapWidth, int mapHeight); bool resizeTilesCache(uint16_t numberOfTiles); bool fetchMap(LGFX_Sprite &sprite, double longitude, double latitude, uint8_t zoom); void freeTilesCache(); - int getMinZoom() const { return currentProvider->minZoom; } - int getMaxZoom() const { return currentProvider->maxZoom; } - + const char *getProviderName() { return currentProvider->name; }; + int getMinZoom() const { return currentProvider->minZoom; }; + int getMaxZoom() const { return currentProvider->maxZoom; }; + private: const TileProvider *currentProvider = &tileProviders[0]; std::vector tilesCache; diff --git a/src/DejaVu9-modded.h b/src/fonts/DejaVu9-modded.h similarity index 100% rename from src/DejaVu9-modded.h rename to src/fonts/DejaVu9-modded.h From 30b9320c746edea794646a7546d32eea85e82637 Mon Sep 17 00:00:00 2001 From: Cellie Date: Thu, 5 Jun 2025 11:15:31 +0200 Subject: [PATCH 12/28] Update README --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 69cbc82..cdad4b2 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ A composed map can be pushed to the screen, saved to SD or used for further comp Downloaded tiles are cached in psram for reuse. This library should work on any ESP32 type with psram and a LovyanGFX compatible display. -OSM tiles are quite large -128kB or 512kB per tile- so psram is required. +OSM tiles are quite large -from 128kB to 512kB per tile- so psram is required. This project is not endorsed by or affiliated with the OpenStreetMap Foundation. Use of any OSMF provided service is governed by the [OSMF Terms of Use](https://osmfoundation.org/wiki/Terms_of_Use). @@ -47,7 +47,7 @@ lib_deps = ### Set map size ```c++ -void setSize(w, h) +void setSize(uint16_t w, uint16_t h) ``` - If no size is set a 320px by 240px map will be returned. @@ -55,11 +55,11 @@ void setSize(w, h) ### Get required cache size ```c++ -uint16_t tilesToCover(w, h) +uint16_t tilesToCover(uint16_t w, uint16_t h) ``` This returns the minimum required number of tiles for the given map size. -**Note:** Some 'lucky' coordinates might require less tiles than this. +**Note:** Some 'lucky' coordinates might require even less tiles than this. ### Resize the tiles cache From d58facfdb924bcd35fd6d7e223453f4a2b8f440f Mon Sep 17 00:00:00 2001 From: Cellie Date: Thu, 5 Jun 2025 11:44:53 +0200 Subject: [PATCH 13/28] Cleanup --- README.md | 18 ++++++++++-------- src/OpenStreetMap-esp32.cpp | 2 +- src/OpenStreetMap-esp32.hpp | 4 ++-- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index cdad4b2..0f7ae19 100644 --- a/README.md +++ b/README.md @@ -58,8 +58,7 @@ void setSize(uint16_t w, uint16_t h) uint16_t tilesToCover(uint16_t w, uint16_t h) ``` -This returns the minimum required number of tiles for the given map size. -**Note:** Some 'lucky' coordinates might require even less tiles than this. +This returns the required number of tiles to cover the given map size. ### Resize the tiles cache @@ -72,9 +71,10 @@ bool resizeTilesCache(uint16_t numberOfTiles) - Each 256px tile allocates **128kB** psram. - Each 512px tile allocates **512kB** psram. -**Don't overallocate** -When resizing the cache, keep in mind that the map sprite itself also needs psram. -The PNG decoders -~50kB each- also live in psram. +**Don't over-allocate the cache** +When resizing the cache, keep in mind that the map sprite also needs psram. +The PNG decoders -~50kB for each core- also live in psram. +Use the above `tilesToCover` function to calculate a tile cache size. ### Fetch a map @@ -92,18 +92,20 @@ bool fetchMap(LGFX_Sprite &map, double longitude, double latitude, uint8_t zoom) void freeTilesCache() ``` -### Switch tile provider +### Switch to a different tile provider ```c++ bool setTileProvider(int index) ``` -Switch to a tile provider that is user defined in `src/TileProvider.hpp`. +This function will switch to a tile provider (if) that is user defined in `src/TileProvider.hpp`. This will invalidate and possibly resize the cache if the new provider uses a different tile size. Returns false if no provider at the index is defined or if no cache could be allocated. -**Note:** In the default setup there is only one provider defined. See `src/TileProvider.hpp` for example provider setups. +**Note:** In the default setup there is only one provider defined. +See `src/TileProvider.hpp` for example provider setups for [https://www.thunderforest.com/](https://www.thunderforest.com/) that only require an API key and commenting/uncommenting the currently defined setup. +Registration and a hobby tier are available for free! ## Example code diff --git a/src/OpenStreetMap-esp32.cpp b/src/OpenStreetMap-esp32.cpp index 5589a6b..3a21d1f 100644 --- a/src/OpenStreetMap-esp32.cpp +++ b/src/OpenStreetMap-esp32.cpp @@ -547,7 +547,7 @@ bool OpenStreetMap::startTileWorkerTasks() return true; } -uint16_t OpenStreetMap::tilesToCover(int mapWidth, int mapHeight) +uint16_t OpenStreetMap::tilesToCover(uint16_t mapWidth, uint16_t mapHeight) { const int tileSize = currentProvider->tileSize; int tilesX = (mapWidth + tileSize - 1) / tileSize + 1; diff --git a/src/OpenStreetMap-esp32.hpp b/src/OpenStreetMap-esp32.hpp index 10873eb..49af5d1 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_TILE_TIMEOUT_MS = 250; +constexpr uint16_t OSM_TILE_TIMEOUT_MS = 500; constexpr UBaseType_t OSM_TASK_PRIORITY = 10; constexpr uint32_t OSM_TASK_STACKSIZE = 5120; constexpr uint32_t OSM_JOB_QUEUE_SIZE = 50; @@ -88,7 +88,7 @@ class OpenStreetMap bool setTileProvider(int index); void setSize(uint16_t w, uint16_t h); - uint16_t tilesToCover(int mapWidth, int mapHeight); + uint16_t tilesToCover(uint16_t mapWidth, uint16_t mapHeight); bool resizeTilesCache(uint16_t numberOfTiles); bool fetchMap(LGFX_Sprite &sprite, double longitude, double latitude, uint8_t zoom); void freeTilesCache(); From 2654ae8197bbfa21f5849067a57e9d0bb78c7544 Mon Sep 17 00:00:00 2001 From: Cellie Date: Thu, 5 Jun 2025 13:36:58 +0200 Subject: [PATCH 14/28] Add Thunderforest TransportDark as a provider --- src/TileProvider.hpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/TileProvider.hpp b/src/TileProvider.hpp index d403b3e..0d70096 100644 --- a/src/TileProvider.hpp +++ b/src/TileProvider.hpp @@ -44,6 +44,14 @@ const TileProvider osmStandard = { "", 19, 0, 256}; +const TileProvider ThunderTransportDark256 = { + "Thunderforest Transport Dark 256px", + "https://tile.thunderforest.com/transport-dark/{z}/{x}/{y}.png?apikey={apiKey}", + "© Thunderforest, OpenStreetMap contributors", + true, + "YOUR_THUNDERFOREST_KEY", + 22, 0, 256}; + const TileProvider ThunderForestCycle512 = { "Thunderforest Cycle 512px", "https://tile.thunderforest.com/cycle/{z}/{x}/{y}@2x.png?apikey={apiKey}", @@ -60,7 +68,12 @@ const TileProvider ThunderForestCycle256 = { "YOUR_THUNDERFOREST_KEY", 22, 0, 256}; -// const TileProvider tileProviders[] = {osmStandard, ThunderForestCycle512, ThunderForestCycle256}; +// Replace 'YOUR_THUNDERFOREST_KEY' above with a -free- Thunderforest API key and uncomment the following line to use Thunderforest tiles +// const TileProvider tileProviders[] = {osmStandard, ThunderTransportDark256, ThunderForestCycle512, ThunderForestCycle256}; +// const TileProvider tileProviders[] = {ThunderTransportDark256}; +// const TileProvider tileProviders[] = {ThunderForestCycle512}; +// const TileProvider tileProviders[] = {ThunderForestCycle256}; + const TileProvider tileProviders[] = {osmStandard}; constexpr int OSM_TILEPROVIDERS = sizeof(tileProviders) / sizeof(TileProvider); From c22bafc2bd75ea8eebadec558bfb46dc70a74687 Mon Sep 17 00:00:00 2001 From: Cellie Date: Thu, 5 Jun 2025 13:37:23 +0200 Subject: [PATCH 15/28] Update README.md --- README.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 0f7ae19..d00c42c 100644 --- a/README.md +++ b/README.md @@ -51,14 +51,16 @@ void setSize(uint16_t w, uint16_t h) ``` - If no size is set a 320px by 240px map will be returned. +- The tile cache should be resized after setting a new map size. +Use `tilesToCover(w, h)` to get a correct cache size. -### Get required cache size +### Get the number of tiles to cover a map ```c++ uint16_t tilesToCover(uint16_t w, uint16_t h) ``` -This returns the required number of tiles to cover the given map size. +This returns the number of tiles required to cover -cache- the given map size. ### Resize the tiles cache @@ -72,9 +74,9 @@ bool resizeTilesCache(uint16_t numberOfTiles) - Each 512px tile allocates **512kB** psram. **Don't over-allocate the cache** -When resizing the cache, keep in mind that the map sprite also needs psram. +When resizing the cache, keep in mind that the map sprite also uses psram. The PNG decoders -~50kB for each core- also live in psram. -Use the above `tilesToCover` function to calculate a tile cache size. +Use the above `tilesToCover` function to calculate a sane tile cache size. ### Fetch a map @@ -104,8 +106,8 @@ This will invalidate and possibly resize the cache if the new provider uses a di Returns false if no provider at the index is defined or if no cache could be allocated. **Note:** In the default setup there is only one provider defined. -See `src/TileProvider.hpp` for example provider setups for [https://www.thunderforest.com/](https://www.thunderforest.com/) that only require an API key and commenting/uncommenting the currently defined setup. -Registration and a hobby tier are available for free! +See `src/TileProvider.hpp` for example provider setups for [https://www.thunderforest.com/](https://www.thunderforest.com/) that only require an API key and commenting/uncommenting 2 lines of setup. +Registration and a hobby tier are available for free. ## Example code From 73c5cadb605e236522f9ca1d263519b426a27612 Mon Sep 17 00:00:00 2001 From: Cellie Date: Thu, 5 Jun 2025 13:38:30 +0200 Subject: [PATCH 16/28] Use taskYIELD() to get better utilisation when downloading --- src/OpenStreetMap-esp32.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OpenStreetMap-esp32.cpp b/src/OpenStreetMap-esp32.cpp index 3a21d1f..9ac5663 100644 --- a/src/OpenStreetMap-esp32.cpp +++ b/src/OpenStreetMap-esp32.cpp @@ -359,7 +359,7 @@ bool OpenStreetMap::fillBuffer(WiFiClient *stream, MemoryBuffer &buffer, size_t result = "Timeout: " + String(OSM_TILE_TIMEOUT_MS) + " ms"; return false; } - vTaskDelay(pdMS_TO_TICKS(1)); + taskYIELD(); continue; } @@ -375,7 +375,7 @@ bool OpenStreetMap::fillBuffer(WiFiClient *stream, MemoryBuffer &buffer, size_t lastReadTime = millis(); } else - vTaskDelay(pdMS_TO_TICKS(1)); + taskYIELD(); } return true; } From f985515ee883102f02a6eea32016f37f6261809c Mon Sep 17 00:00:00 2001 From: Cellie Date: Thu, 5 Jun 2025 13:39:00 +0200 Subject: [PATCH 17/28] Fetch task priority to 1 --- 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 49af5d1..2797b05 100644 --- a/src/OpenStreetMap-esp32.hpp +++ b/src/OpenStreetMap-esp32.hpp @@ -41,7 +41,7 @@ #include "fonts/DejaVu9-modded.h" constexpr uint16_t OSM_TILE_TIMEOUT_MS = 500; -constexpr UBaseType_t OSM_TASK_PRIORITY = 10; +constexpr UBaseType_t OSM_TASK_PRIORITY = 1; constexpr uint32_t OSM_TASK_STACKSIZE = 5120; constexpr uint32_t OSM_JOB_QUEUE_SIZE = 50; constexpr bool OSM_FORCE_SINGLECORE = false; From d4d41226b7b2f7ce8fec75afb9375a30b36188e7 Mon Sep 17 00:00:00 2001 From: Cellie Date: Thu, 5 Jun 2025 19:49:41 +0200 Subject: [PATCH 18/28] Fix tile provider comments --- src/TileProvider.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/TileProvider.hpp b/src/TileProvider.hpp index 0d70096..6f23c41 100644 --- a/src/TileProvider.hpp +++ b/src/TileProvider.hpp @@ -68,12 +68,15 @@ const TileProvider ThunderForestCycle256 = { "YOUR_THUNDERFOREST_KEY", 22, 0, 256}; -// Replace 'YOUR_THUNDERFOREST_KEY' above with a -free- Thunderforest API key and uncomment the following line to use Thunderforest tiles +// Replace 'YOUR_THUNDERFOREST_KEY' above with a -free- Thunderforest API key +// and uncomment one of the following line to use Thunderforest tiles + // const TileProvider tileProviders[] = {osmStandard, ThunderTransportDark256, ThunderForestCycle512, ThunderForestCycle256}; // const TileProvider tileProviders[] = {ThunderTransportDark256}; // const TileProvider tileProviders[] = {ThunderForestCycle512}; // const TileProvider tileProviders[] = {ThunderForestCycle256}; +// If one of the above definitions is used, the following line should be commented out const TileProvider tileProviders[] = {osmStandard}; constexpr int OSM_TILEPROVIDERS = sizeof(tileProviders) / sizeof(TileProvider); From 0004d3da240bf71803683f5a3e78a0f0b01c460d Mon Sep 17 00:00:00 2001 From: Cellie Date: Thu, 5 Jun 2025 19:51:07 +0200 Subject: [PATCH 19/28] Update README --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d00c42c..38cbcc9 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ bool resizeTilesCache(uint16_t numberOfTiles) **Don't over-allocate the cache** When resizing the cache, keep in mind that the map sprite also uses psram. The PNG decoders -~50kB for each core- also live in psram. -Use the above `tilesToCover` function to calculate a sane tile cache size. +Use the above `tilesToCover` function to calculate a safe and sane cache size. ### Fetch a map @@ -101,12 +101,12 @@ bool setTileProvider(int index) ``` This function will switch to a tile provider (if) that is user defined in `src/TileProvider.hpp`. -This will invalidate and possibly resize the cache if the new provider uses a different tile size. -Returns false if no provider at the index is defined or if no cache could be allocated. +- Returns `true` and clears the cache on success. +- Returns `false` -and the current tile provider is unchanged- if no provider at the index is defined. **Note:** In the default setup there is only one provider defined. -See `src/TileProvider.hpp` for example provider setups for [https://www.thunderforest.com/](https://www.thunderforest.com/) that only require an API key and commenting/uncommenting 2 lines of setup. +See `src/TileProvider.hpp` for example provider setups for [https://www.thunderforest.com/](https://www.thunderforest.com/) that only require an API key and commenting/uncommenting 2 lines. Registration and a hobby tier are available for free. ## Example code From 0849addbc6fbee09a76e804994ce03c9820b793a Mon Sep 17 00:00:00 2001 From: Cellie Date: Thu, 5 Jun 2025 20:16:53 +0200 Subject: [PATCH 20/28] Use RAII to clean up `tilesCache` Cleanup logging --- src/OpenStreetMap-esp32.cpp | 16 ++++------------ src/OpenStreetMap-esp32.hpp | 2 +- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/OpenStreetMap-esp32.cpp b/src/OpenStreetMap-esp32.cpp index 9ac5663..fc22109 100644 --- a/src/OpenStreetMap-esp32.cpp +++ b/src/OpenStreetMap-esp32.cpp @@ -171,10 +171,7 @@ bool OpenStreetMap::isTileCachedOrBusy(uint32_t x, uint32_t y, uint8_t z) void OpenStreetMap::freeTilesCache() { - for (auto &tile : tilesCache) - tile.free(); - - tilesCache.clear(); + std::vector().swap(tilesCache); } bool OpenStreetMap::resizeTilesCache(uint16_t numberOfTiles) @@ -208,7 +205,7 @@ void OpenStreetMap::updateCache(const tileList &requiredTiles, uint8_t zoom) if (!jobs.empty()) { runJobs(jobs); - log_i("Updated %i tiles in %lu ms - %i ms/tile", jobs.size(), millis() - startMS, (millis() - startMS) / jobs.size()); + log_d("Updated %i tiles in %lu ms - %i ms/tile", jobs.size(), millis() - startMS, (millis() - startMS) / jobs.size()); } } @@ -442,7 +439,7 @@ bool OpenStreetMap::fetchTile(CachedTile &tile, uint32_t x, uint32_t y, uint8_t if (!buffer) return false; - url.clear(); //TODO: find out, is this smart? test test test + url.clear(); PNG *png = getPNGCurrentCore(); const int16_t rc = png->openRAM(buffer.value()->get(), buffer.value()->size(), PNGDraw); @@ -552,7 +549,6 @@ uint16_t OpenStreetMap::tilesToCover(uint16_t mapWidth, uint16_t mapHeight) const int tileSize = currentProvider->tileSize; int tilesX = (mapWidth + tileSize - 1) / tileSize + 1; int tilesY = (mapHeight + tileSize - 1) / tileSize + 1; - log_i("need %i tiles for %ipx by %ipx map", tilesX * tilesY, mapWidth, mapHeight); return tilesX * tilesY; } @@ -565,11 +561,7 @@ bool OpenStreetMap::setTileProvider(int index) } currentProvider = &tileProviders[index]; - if (!resizeTilesCache(tilesToCover(mapWidth, mapHeight))) - { - log_e("could not allocate %i tiles for current mapsize", tilesToCover(mapWidth, mapHeight)); - return false; - } + freeTilesCache(); log_i("provider changed to '%s'", currentProvider->name); return true; } diff --git a/src/OpenStreetMap-esp32.hpp b/src/OpenStreetMap-esp32.hpp index 2797b05..41410c6 100644 --- a/src/OpenStreetMap-esp32.hpp +++ b/src/OpenStreetMap-esp32.hpp @@ -91,7 +91,7 @@ class OpenStreetMap uint16_t tilesToCover(uint16_t mapWidth, uint16_t mapHeight); bool resizeTilesCache(uint16_t numberOfTiles); bool fetchMap(LGFX_Sprite &sprite, double longitude, double latitude, uint8_t zoom); - void freeTilesCache(); + inline void freeTilesCache(); const char *getProviderName() { return currentProvider->name; }; int getMinZoom() const { return currentProvider->minZoom; }; From df0d107cd2b8c3562134ae709b0174bf710b3aca Mon Sep 17 00:00:00 2001 From: Cellie Date: Thu, 5 Jun 2025 20:38:29 +0200 Subject: [PATCH 21/28] Update README --- README.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 38cbcc9..42ba583 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ A composed map can be pushed to the screen, saved to SD or used for further comp Downloaded tiles are cached in psram for reuse. This library should work on any ESP32 type with psram and a LovyanGFX compatible display. -OSM tiles are quite large -from 128kB to 512kB per tile- so psram is required. +OSM tiles are either quite large at 128kB or insane large at 512kB per tile so psram is required. This project is not endorsed by or affiliated with the OpenStreetMap Foundation. Use of any OSMF provided service is governed by the [OSMF Terms of Use](https://osmfoundation.org/wiki/Terms_of_Use). @@ -105,7 +105,17 @@ This function will switch to a tile provider (if) that is user defined in `src/T - Returns `true` and clears the cache on success. - Returns `false` -and the current tile provider is unchanged- if no provider at the index is defined. -**Note:** In the default setup there is only one provider defined. +### Get the number of defined providers + +`OSM_TILEPROVIDERS` gives the number of defined providers. + +Example use: + +```c++ +const int numberOfProviders = OSM_TILEPROVIDERS; +``` + +In the default setup there is only one provider defined. See `src/TileProvider.hpp` for example provider setups for [https://www.thunderforest.com/](https://www.thunderforest.com/) that only require an API key and commenting/uncommenting 2 lines. Registration and a hobby tier are available for free. From f00422fe89548fa3035ffc4c444a994b1a2a8bc4 Mon Sep 17 00:00:00 2001 From: Cellie Date: Thu, 5 Jun 2025 21:00:48 +0200 Subject: [PATCH 22/28] Fix codacy issue --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 42ba583..b17dc75 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ lib_deps = lovyan03/LovyanGFX@^1.2.7 bitbank2/PNGdec@^1.1.3 ``` + ## Functions ### Set map size @@ -50,9 +51,8 @@ lib_deps = void setSize(uint16_t w, uint16_t h) ``` -- If no size is set a 320px by 240px map will be returned. -- The tile cache should be resized after setting a new map size. -Use `tilesToCover(w, h)` to get a correct cache size. +- If no size is set a 320px by 240px map will be returned. +- The tile cache should (maybe) be resized after setting a new map size. ### Get the number of tiles to cover a map From 3450db0d658edf5758235dfa61effbdb85e8c835 Mon Sep 17 00:00:00 2001 From: Cellie Date: Fri, 6 Jun 2025 09:33:04 +0200 Subject: [PATCH 23/28] Add provider getters --- README.md | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b17dc75..8449af0 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ A composed map can be pushed to the screen, saved to SD or used for further comp Downloaded tiles are cached in psram for reuse. This library should work on any ESP32 type with psram and a LovyanGFX compatible display. -OSM tiles are either quite large at 128kB or insane large at 512kB per tile so psram is required. +OSM tiles are quite large at 128kB or insane large at 512kB per tile so psram is required. This project is not endorsed by or affiliated with the OpenStreetMap Foundation. Use of any OSMF provided service is governed by the [OSMF Terms of Use](https://osmfoundation.org/wiki/Terms_of_Use). @@ -60,7 +60,7 @@ void setSize(uint16_t w, uint16_t h) uint16_t tilesToCover(uint16_t w, uint16_t h) ``` -This returns the number of tiles required to cover -cache- the given map size. +This returns the number of tiles required to cache the given map size. ### Resize the tiles cache @@ -116,9 +116,27 @@ const int numberOfProviders = OSM_TILEPROVIDERS; ``` In the default setup there is only one provider defined. -See `src/TileProvider.hpp` for example provider setups for [https://www.thunderforest.com/](https://www.thunderforest.com/) that only require an API key and commenting/uncommenting 2 lines. +See `src/TileProvider.hpp` for example setups for [https://www.thunderforest.com/](https://www.thunderforest.com/) that only require an API key and commenting/uncommenting 2 lines. Registration and a hobby tier are available for free. +### Get the provider name + +```c++ +char *getProviderName() +``` + +### Get the minimum zoom level + +```c++ +int getMinZoom() +``` + +### Get the maximum zoom level + +```c++ +int getMaxZoom() +``` + ## Example code ### Example returning the default 320x240 map From 822485b5e1dfae0586d303807f0a2a6d9480a24b Mon Sep 17 00:00:00 2001 From: Cellie Date: Fri, 6 Jun 2025 09:33:16 +0200 Subject: [PATCH 24/28] Cleanup --- src/OpenStreetMap-esp32.hpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/OpenStreetMap-esp32.hpp b/src/OpenStreetMap-esp32.hpp index 41410c6..57cbe0d 100644 --- a/src/OpenStreetMap-esp32.hpp +++ b/src/OpenStreetMap-esp32.hpp @@ -86,27 +86,23 @@ class OpenStreetMap ~OpenStreetMap(); - bool setTileProvider(int index); void setSize(uint16_t w, uint16_t h); uint16_t tilesToCover(uint16_t mapWidth, uint16_t mapHeight); bool resizeTilesCache(uint16_t numberOfTiles); bool fetchMap(LGFX_Sprite &sprite, double longitude, double latitude, uint8_t zoom); inline void freeTilesCache(); + bool setTileProvider(int index); const char *getProviderName() { return currentProvider->name; }; int getMinZoom() const { return currentProvider->minZoom; }; int getMaxZoom() const { return currentProvider->maxZoom; }; private: - const TileProvider *currentProvider = &tileProviders[0]; - std::vector tilesCache; - static inline OpenStreetMap *currentInstance = nullptr; - static inline thread_local uint16_t *currentTileBuffer = nullptr; - static void PNGDraw(PNGDRAW *pDraw); 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); + bool startTileWorkerTasks(); void makeJobList(const tileList &requiredTiles, std::vector &jobs, uint8_t zoom); void runJobs(const std::vector &jobs); CachedTile *findUnusedTile(const tileList &requiredTiles, uint8_t zoom); @@ -116,8 +112,13 @@ class OpenStreetMap bool fillBuffer(WiFiClient *stream, MemoryBuffer &buffer, size_t contentSize, String &result); bool composeMap(LGFX_Sprite &mapSprite, const tileList &requiredTiles, uint8_t zoom); static void tileFetcherTask(void *param); + static void PNGDraw(PNGDRAW *pDraw); + + static inline OpenStreetMap *currentInstance = nullptr; + static inline thread_local uint16_t *currentTileBuffer = nullptr; + const TileProvider *currentProvider = &tileProviders[0]; + std::vector tilesCache; TaskHandle_t ownerTask = nullptr; - bool startTileWorkerTasks(); int numberOfWorkers = 0; QueueHandle_t jobQueue = nullptr; From 1669e5192798fc00cc82159fd21afcdbbbd14fe8 Mon Sep 17 00:00:00 2001 From: Cellie Date: Fri, 6 Jun 2025 09:59:14 +0200 Subject: [PATCH 25/28] Change `tilesToCover` to `tilesNeeded` --- README.md | 6 +++--- src/OpenStreetMap-esp32.cpp | 4 ++-- src/OpenStreetMap-esp32.hpp | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 8449af0..43962cb 100644 --- a/README.md +++ b/README.md @@ -54,10 +54,10 @@ void setSize(uint16_t w, uint16_t h) - If no size is set a 320px by 240px map will be returned. - The tile cache should (maybe) be resized after setting a new map size. -### Get the number of tiles to cover a map +### Get the number of tiles needed to cache a map ```c++ -uint16_t tilesToCover(uint16_t w, uint16_t h) +uint16_t tilesNeeded(uint16_t w, uint16_t h) ``` This returns the number of tiles required to cache the given map size. @@ -76,7 +76,7 @@ bool resizeTilesCache(uint16_t numberOfTiles) **Don't over-allocate the cache** When resizing the cache, keep in mind that the map sprite also uses psram. The PNG decoders -~50kB for each core- also live in psram. -Use the above `tilesToCover` function to calculate a safe and sane cache size. +Use the above `tilesNeeded` function to calculate a safe and sane cache size. ### Fetch a map diff --git a/src/OpenStreetMap-esp32.cpp b/src/OpenStreetMap-esp32.cpp index fc22109..33559bf 100644 --- a/src/OpenStreetMap-esp32.cpp +++ b/src/OpenStreetMap-esp32.cpp @@ -314,7 +314,7 @@ bool OpenStreetMap::fetchMap(LGFX_Sprite &mapSprite, double longitude, double la return false; } - if (!tilesCache.capacity() && !resizeTilesCache(tilesToCover(mapWidth, mapHeight))) + if (!tilesCache.capacity() && !resizeTilesCache(tilesNeeded(mapWidth, mapHeight))) { log_e("Could not allocate tile cache"); return false; @@ -544,7 +544,7 @@ bool OpenStreetMap::startTileWorkerTasks() return true; } -uint16_t OpenStreetMap::tilesToCover(uint16_t mapWidth, uint16_t mapHeight) +uint16_t OpenStreetMap::tilesNeeded(uint16_t mapWidth, uint16_t mapHeight) { const int tileSize = currentProvider->tileSize; int tilesX = (mapWidth + tileSize - 1) / tileSize + 1; diff --git a/src/OpenStreetMap-esp32.hpp b/src/OpenStreetMap-esp32.hpp index 57cbe0d..89adfc4 100644 --- a/src/OpenStreetMap-esp32.hpp +++ b/src/OpenStreetMap-esp32.hpp @@ -87,7 +87,7 @@ class OpenStreetMap ~OpenStreetMap(); void setSize(uint16_t w, uint16_t h); - uint16_t tilesToCover(uint16_t mapWidth, uint16_t mapHeight); + uint16_t tilesNeeded(uint16_t mapWidth, uint16_t mapHeight); bool resizeTilesCache(uint16_t numberOfTiles); bool fetchMap(LGFX_Sprite &sprite, double longitude, double latitude, uint8_t zoom); inline void freeTilesCache(); From dd4e3a1b7258f321d486d819065d1f12a8e2c5f1 Mon Sep 17 00:00:00 2001 From: Cellie Date: Fri, 6 Jun 2025 10:04:52 +0200 Subject: [PATCH 26/28] Cleanup --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 43962cb..fd87a27 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ void setSize(uint16_t w, uint16_t h) ``` - If no size is set a 320px by 240px map will be returned. -- The tile cache should (maybe) be resized after setting a new map size. +- The tile cache should (probably) be freed with `freeTilesCache()` after setting a new map size. ### Get the number of tiles needed to cache a map From de9c0918e697e43b43daa7acf43a57c1d7e9638a Mon Sep 17 00:00:00 2001 From: Cellie Date: Fri, 6 Jun 2025 10:07:02 +0200 Subject: [PATCH 27/28] Cleanup --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fd87a27..4240c87 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ void setSize(uint16_t w, uint16_t h) ``` - If no size is set a 320px by 240px map will be returned. -- The tile cache should (probably) be freed with `freeTilesCache()` after setting a new map size. +- The tile cache should be freed with `freeTilesCache()` after setting a new map size. ### Get the number of tiles needed to cache a map From 26fd2b4dc6882c0456c02d6b3cdbf5d756d03624 Mon Sep 17 00:00:00 2001 From: Cellie Date: Fri, 6 Jun 2025 10:17:30 +0200 Subject: [PATCH 28/28] Small fix --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4240c87..2a183cd 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ A composed map can be pushed to the screen, saved to SD or used for further comp Downloaded tiles are cached in psram for reuse. This library should work on any ESP32 type with psram and a LovyanGFX compatible display. -OSM tiles are quite large at 128kB or insane large at 512kB per tile so psram is required. +OSM tiles are quite large at 128kB or insane large at 512kB per tile, so psram is required. This project is not endorsed by or affiliated with the OpenStreetMap Foundation. Use of any OSMF provided service is governed by the [OSMF Terms of Use](https://osmfoundation.org/wiki/Terms_of_Use). @@ -52,7 +52,7 @@ void setSize(uint16_t w, uint16_t h) ``` - If no size is set a 320px by 240px map will be returned. -- The tile cache should be freed with `freeTilesCache()` after setting a new map size. +- The tile cache should be freed with `freeTilesCache()` after setting a new bigger map size. ### Get the number of tiles needed to cache a map