Skip to content

Download tiles in a separate function #9

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Mar 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 49 additions & 56 deletions src/OpenStreetMap-esp32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,22 +261,11 @@ bool OpenStreetMap::fetchMap(LGFX_Sprite &mapSprite, double longitude, double la
}
}

// normalize the coordinates
longitude = fmod(longitude + 180.0, 360.0) - 180.0;
latitude = std::clamp(latitude, -90.0, 90.0);

tileList requiredTiles;
computeRequiredTiles(longitude, latitude, zoom, requiredTiles);

#define SHOW_REQUIRED_TILES false
#if defined(SHOW_REQUIRED_TILES) && (SHOW_REQUIRED_TILES == true)
log_i("Required Tiles:");
for (size_t i = 0; i < requiredTiles.size(); ++i)
{
log_i(" Tile [%zu]: X=%d, Y=%d", i, requiredTiles[i].first, requiredTiles[i].second);
}
#endif

if (tilesCache.capacity() < requiredTiles.size())
{
log_e("Caching error: Need %i cache slots, but only %i are provided", requiredTiles.size(), tilesCache.capacity());
Expand Down Expand Up @@ -316,104 +305,108 @@ bool OpenStreetMap::readTileDataToBuffer(WiFiClient *stream, MemoryBuffer &buffe
return true;
}

bool OpenStreetMap::downloadAndDecodeTile(CachedTile &tile, uint32_t x, uint32_t y, uint8_t zoom, String &result)
std::optional<std::unique_ptr<MemoryBuffer>> OpenStreetMap::downloadTile(const String &url, String &result, size_t &size)
{
const uint32_t worldTileWidth = 1 << zoom;
if (x >= worldTileWidth || y >= worldTileWidth)
{
result = "Out of range tile coordinates";
return false;
}

const String url = "https://tile.openstreetmap.org/" + String(zoom) + "/" + String(x) + "/" + String(y) + ".png";

HTTPClient http;
http.setUserAgent("OpenStreetMap-esp32/1.0 (+https://github.com/CelliesProjects/OpenStreetMap-esp32)");
if (!http.begin(url))
{
result = "Failed to initialize HTTP client";
return false;
return std::nullopt;
}

const int httpCode = http.GET();
if (httpCode != HTTP_CODE_OK)
{
http.end();

if (httpCode == HTTP_CODE_NOT_FOUND)
{
result = "HTTP Error 404 - not found tile " + String(x) + "," + String(y) + "," + String(zoom);
return false;
}

result = "HTTP Error: " + String(httpCode);
return false;
return std::nullopt;
}

const size_t contentSize = http.getSize();
if (contentSize < 1)
{
http.end();
result = "Empty or chunked response";
return false;
return std::nullopt;
}

WiFiClient *stream = http.getStreamPtr();
if (!stream)
{
http.end();
result = "Failed to get HTTP stream";
return false;
return std::nullopt;
}

MemoryBuffer buffer(contentSize);
if (!buffer.isAllocated())
auto buffer = std::make_unique<MemoryBuffer>(contentSize);
if (!buffer->isAllocated())
{
http.end();
result = "Failed to allocate buffer";
return false;
return std::nullopt;
}

if (!readTileDataToBuffer(stream, buffer, contentSize, result))
if (!readTileDataToBuffer(stream, *buffer, contentSize, result))
{
http.end();
log_e("%s", result.c_str());
return false;
return std::nullopt;
}

http.end();
size = contentSize;
result = "Downloaded tile " + url;
return buffer;
}

const int16_t rc = png.openRAM(buffer.get(), contentSize, PNGDraw);
if (rc != PNG_SUCCESS)
bool OpenStreetMap::downloadAndDecodeTile(CachedTile &tile, uint32_t x, uint32_t y, uint8_t zoom, String &result)
{
const uint32_t worldTileWidth = 1 << zoom;
if (x >= worldTileWidth || y >= worldTileWidth)
{
result = "PNG Decoder Error: " + String(rc);
result = "Out of range tile coordinates";
return false;
}

if (png.getWidth() != OSM_TILESIZE || png.getHeight() != OSM_TILESIZE)
const String url = "https://tile.openstreetmap.org/" + String(zoom) + "/" + String(x) + "/" + String(y) + ".png";

{
result = "Unexpected tile size: w=" + String(png.getWidth()) + " h=" + String(png.getWidth());
return false;
}
size_t contentSize;
auto buffer = downloadTile(url, result, contentSize);
if (!buffer)
return false;

currentInstance = this;
currentTileBuffer = tile.buffer;
const int decodeResult = png.decode(0, PNG_FAST_PALETTE);
currentTileBuffer = nullptr;
currentInstance = nullptr;
const int16_t rc = png.openRAM(buffer.value()->get(), contentSize, PNGDraw);
if (rc != PNG_SUCCESS)
{
result = "PNG Decoder Error: " + String(rc);
return false;
}

if (decodeResult != PNG_SUCCESS)
{
result = "Decoding " + url + " failed with code: " + String(decodeResult);
tile.valid = false;
return false;
if (png.getWidth() != OSM_TILESIZE || png.getHeight() != OSM_TILESIZE)
{
result = "Unexpected tile size: w=" + String(png.getWidth()) + " h=" + String(png.getWidth());
return false;
}

currentInstance = this;
currentTileBuffer = tile.buffer;
const int decodeResult = png.decode(0, PNG_FAST_PALETTE);
currentTileBuffer = nullptr;
currentInstance = nullptr;

if (decodeResult != PNG_SUCCESS)
{
result = "Decoding " + url + " failed with code: " + String(decodeResult);
tile.valid = false;
return false;
}
}

tile.x = x;
tile.y = y;
tile.z = zoom;
tile.valid = true;

result = "Added: " + url;
return true;
}
Expand Down
3 changes: 3 additions & 0 deletions src/OpenStreetMap-esp32.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
#include <WiFiClient.h>
#include <SD.h>
#include <vector>
#include <optional>
#include <memory>
#include <LovyanGFX.hpp>
#include <PNGdec.h>

Expand Down Expand Up @@ -67,6 +69,7 @@ class OpenStreetMap
void updateCache(const tileList &requiredTiles, uint8_t zoom);
bool isTileCached(uint32_t x, uint32_t y, uint8_t z);
CachedTile *findUnusedTile(const tileList &requiredTiles, uint8_t zoom);
std::optional<std::unique_ptr<MemoryBuffer>> downloadTile(const String &url, String &result, size_t &size);
bool downloadAndDecodeTile(CachedTile &tile, uint32_t x, uint32_t y, uint8_t zoom, String &result);
bool readTileDataToBuffer(WiFiClient *stream, MemoryBuffer &buffer, size_t contentSize, String &result);
bool composeMap(LGFX_Sprite &mapSprite, const tileList &requiredTiles, uint8_t zoom);
Expand Down