Initial commit of ESP component

This commit is contained in:
Eric
2025-09-08 20:47:04 -07:00
parent 5c4847c0d6
commit 165c2c07c7
7 changed files with 1371 additions and 0 deletions

509
map_tiles.cpp Executable file
View File

@@ -0,0 +1,509 @@
#include "map_tiles.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "esp_log.h"
#include "esp_heap_caps.h"
static const char* TAG = "map_tiles";
// Internal structure for map tiles instance
struct map_tiles_t {
// Configuration
char* base_path;
char* tile_folders[MAP_TILES_MAX_TYPES];
int tile_type_count;
int current_tile_type;
int grid_cols;
int grid_rows;
int tile_count;
int zoom;
bool use_spiram;
bool initialized;
// Tile management
int tile_x;
int tile_y;
int marker_offset_x;
int marker_offset_y;
bool tile_loading_error;
// Tile data - arrays will be allocated dynamically based on actual grid size
uint8_t** tile_bufs;
lv_image_dsc_t* tile_imgs;
};
map_tiles_handle_t map_tiles_init(const map_tiles_config_t* config)
{
if (!config || !config->base_path || config->tile_type_count <= 0 ||
config->tile_type_count > MAP_TILES_MAX_TYPES ||
config->default_tile_type < 0 || config->default_tile_type >= config->tile_type_count) {
ESP_LOGE(TAG, "Invalid configuration");
return NULL;
}
// Validate grid size - use defaults if not specified or invalid
int grid_cols = config->grid_cols;
int grid_rows = config->grid_rows;
if (grid_cols <= 0 || grid_cols > MAP_TILES_MAX_GRID_COLS) {
ESP_LOGW(TAG, "Invalid grid_cols %d, using default %d", grid_cols, MAP_TILES_DEFAULT_GRID_COLS);
grid_cols = MAP_TILES_DEFAULT_GRID_COLS;
}
if (grid_rows <= 0 || grid_rows > MAP_TILES_MAX_GRID_ROWS) {
ESP_LOGW(TAG, "Invalid grid_rows %d, using default %d", grid_rows, MAP_TILES_DEFAULT_GRID_ROWS);
grid_rows = MAP_TILES_DEFAULT_GRID_ROWS;
}
int tile_count = grid_cols * grid_rows;
// Validate that all tile folders are provided
for (int i = 0; i < config->tile_type_count; i++) {
if (!config->tile_folders[i]) {
ESP_LOGE(TAG, "Tile folder %d is NULL", i);
return NULL;
}
}
map_tiles_handle_t handle = (map_tiles_handle_t)calloc(1, sizeof(struct map_tiles_t));
if (!handle) {
ESP_LOGE(TAG, "Failed to allocate handle");
return NULL;
}
// Copy base path
handle->base_path = strdup(config->base_path);
if (!handle->base_path) {
ESP_LOGE(TAG, "Failed to allocate base path");
free(handle);
return NULL;
}
// Copy tile folder names
handle->tile_type_count = config->tile_type_count;
for (int i = 0; i < config->tile_type_count; i++) {
handle->tile_folders[i] = strdup(config->tile_folders[i]);
if (!handle->tile_folders[i]) {
ESP_LOGE(TAG, "Failed to allocate tile folder %d", i);
// Clean up previously allocated folders
for (int j = 0; j < i; j++) {
free(handle->tile_folders[j]);
}
free(handle->base_path);
free(handle);
return NULL;
}
}
handle->zoom = config->default_zoom;
handle->use_spiram = config->use_spiram;
handle->current_tile_type = config->default_tile_type;
handle->grid_cols = grid_cols;
handle->grid_rows = grid_rows;
handle->tile_count = tile_count;
handle->initialized = true;
handle->tile_loading_error = false;
// Initialize tile data - allocate arrays based on actual tile count
handle->tile_bufs = (uint8_t**)calloc(tile_count, sizeof(uint8_t*));
handle->tile_imgs = (lv_image_dsc_t*)calloc(tile_count, sizeof(lv_image_dsc_t));
if (!handle->tile_bufs || !handle->tile_imgs) {
ESP_LOGE(TAG, "Failed to allocate tile arrays");
// Clean up
if (handle->tile_bufs) free(handle->tile_bufs);
if (handle->tile_imgs) free(handle->tile_imgs);
for (int i = 0; i < handle->tile_type_count; i++) {
free(handle->tile_folders[i]);
}
free(handle->base_path);
free(handle);
return NULL;
}
ESP_LOGI(TAG, "Map tiles initialized with base path: %s, %d tile types, current type: %s, zoom: %d, grid: %dx%d",
handle->base_path, handle->tile_type_count,
handle->tile_folders[handle->current_tile_type], handle->zoom,
handle->grid_cols, handle->grid_rows);
return handle;
}
void map_tiles_set_zoom(map_tiles_handle_t handle, int zoom_level)
{
if (!handle || !handle->initialized) {
ESP_LOGE(TAG, "Handle not initialized");
return;
}
handle->zoom = zoom_level;
ESP_LOGI(TAG, "Zoom level set to %d", zoom_level);
}
int map_tiles_get_zoom(map_tiles_handle_t handle)
{
if (!handle || !handle->initialized) {
ESP_LOGE(TAG, "Handle not initialized");
return 0;
}
return handle->zoom;
}
bool map_tiles_set_tile_type(map_tiles_handle_t handle, int tile_type)
{
if (!handle || !handle->initialized) {
ESP_LOGE(TAG, "Handle not initialized");
return false;
}
if (tile_type < 0 || tile_type >= handle->tile_type_count) {
ESP_LOGE(TAG, "Invalid tile type: %d (valid range: 0-%d)", tile_type, handle->tile_type_count - 1);
return false;
}
handle->current_tile_type = tile_type;
ESP_LOGI(TAG, "Tile type set to %d (%s)", tile_type, handle->tile_folders[tile_type]);
return true;
}
int map_tiles_get_tile_type(map_tiles_handle_t handle)
{
if (!handle || !handle->initialized) {
ESP_LOGE(TAG, "Handle not initialized");
return -1;
}
return handle->current_tile_type;
}
int map_tiles_get_tile_type_count(map_tiles_handle_t handle)
{
if (!handle || !handle->initialized) {
ESP_LOGE(TAG, "Handle not initialized");
return 0;
}
return handle->tile_type_count;
}
const char* map_tiles_get_tile_type_folder(map_tiles_handle_t handle, int tile_type)
{
if (!handle || !handle->initialized) {
ESP_LOGE(TAG, "Handle not initialized");
return NULL;
}
if (tile_type < 0 || tile_type >= handle->tile_type_count) {
ESP_LOGE(TAG, "Invalid tile type: %d", tile_type);
return NULL;
}
return handle->tile_folders[tile_type];
}
bool map_tiles_load_tile(map_tiles_handle_t handle, int index, int tile_x, int tile_y)
{
if (!handle || !handle->initialized) {
ESP_LOGE(TAG, "Handle not initialized");
return false;
}
if (index < 0 || index >= handle->tile_count) {
ESP_LOGE(TAG, "Invalid tile index: %d", index);
return false;
}
char path[256];
const char* folder = handle->tile_folders[handle->current_tile_type];
snprintf(path, sizeof(path), "%s/%s/%d/%d/%d.bin",
handle->base_path, folder, handle->zoom, tile_x, tile_y);
FILE *f = fopen(path, "rb");
if (!f) {
ESP_LOGW(TAG, "Tile not found: %s", path);
return false;
}
// Skip 12-byte header
fseek(f, 12, SEEK_SET);
// Allocate buffer if needed
if (!handle->tile_bufs[index]) {
uint32_t caps = handle->use_spiram ? (MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT) : MALLOC_CAP_DMA;
handle->tile_bufs[index] = (uint8_t*)heap_caps_malloc(
MAP_TILES_TILE_SIZE * MAP_TILES_TILE_SIZE * MAP_TILES_BYTES_PER_PIXEL, caps);
if (!handle->tile_bufs[index]) {
ESP_LOGE(TAG, "Tile %d: allocation failed", index);
fclose(f);
return false;
}
}
// Clear buffer
memset(handle->tile_bufs[index], 0,
MAP_TILES_TILE_SIZE * MAP_TILES_TILE_SIZE * MAP_TILES_BYTES_PER_PIXEL);
// Read tile data
size_t bytes_read = fread(handle->tile_bufs[index], 1,
MAP_TILES_TILE_SIZE * MAP_TILES_TILE_SIZE * MAP_TILES_BYTES_PER_PIXEL, f);
fclose(f);
if (bytes_read != MAP_TILES_TILE_SIZE * MAP_TILES_TILE_SIZE * MAP_TILES_BYTES_PER_PIXEL) {
ESP_LOGW(TAG, "Incomplete tile read: %zu bytes", bytes_read);
}
// Setup image descriptor
handle->tile_imgs[index].header.w = MAP_TILES_TILE_SIZE;
handle->tile_imgs[index].header.h = MAP_TILES_TILE_SIZE;
handle->tile_imgs[index].header.cf = MAP_TILES_COLOR_FORMAT;
handle->tile_imgs[index].header.stride = MAP_TILES_TILE_SIZE * MAP_TILES_BYTES_PER_PIXEL;
handle->tile_imgs[index].data = (const uint8_t*)handle->tile_bufs[index];
handle->tile_imgs[index].data_size = MAP_TILES_TILE_SIZE * MAP_TILES_TILE_SIZE * MAP_TILES_BYTES_PER_PIXEL;
handle->tile_imgs[index].reserved = NULL;
handle->tile_imgs[index].reserved_2 = NULL;
ESP_LOGD(TAG, "Loaded tile %d from %s", index, path);
return true;
}
void map_tiles_gps_to_tile_xy(map_tiles_handle_t handle, double lat, double lon, double* x, double* y)
{
if (!handle || !handle->initialized) {
ESP_LOGE(TAG, "Handle not initialized");
return;
}
if (!x || !y) {
ESP_LOGE(TAG, "Invalid output parameters");
return;
}
double lat_rad = lat * M_PI / 180.0;
int n = 1 << handle->zoom;
*x = (lon + 180.0) / 360.0 * n;
*y = (1.0 - log(tan(lat_rad) + 1.0 / cos(lat_rad)) / M_PI) / 2.0 * n;
}
void map_tiles_tile_xy_to_gps(map_tiles_handle_t handle, double x, double y, double* lat, double* lon)
{
if (!handle || !handle->initialized) {
ESP_LOGE(TAG, "Handle not initialized");
return;
}
if (!lat || !lon) {
ESP_LOGE(TAG, "Invalid output parameters");
return;
}
int n = 1 << handle->zoom;
*lon = x / n * 360.0 - 180.0;
double lat_rad = atan(sinh(M_PI * (1 - 2 * y / n)));
*lat = lat_rad * 180.0 / M_PI;
}
void map_tiles_get_center_gps(map_tiles_handle_t handle, double* lat, double* lon)
{
if (!handle || !handle->initialized) {
ESP_LOGE(TAG, "Handle not initialized");
return;
}
if (!lat || !lon) {
ESP_LOGE(TAG, "Invalid output parameters");
return;
}
// Calculate center tile coordinates (center of the grid)
double center_x = handle->tile_x + handle->grid_cols / 2.0;
double center_y = handle->tile_y + handle->grid_rows / 2.0;
// Convert to GPS coordinates
map_tiles_tile_xy_to_gps(handle, center_x, center_y, lat, lon);
}
void map_tiles_set_center_from_gps(map_tiles_handle_t handle, double lat, double lon)
{
if (!handle || !handle->initialized) {
ESP_LOGE(TAG, "Handle not initialized");
return;
}
double x, y;
map_tiles_gps_to_tile_xy(handle, lat, lon, &x, &y);
handle->tile_x = (int)x - handle->grid_cols / 2;
handle->tile_y = (int)y - handle->grid_rows / 2;
// Calculate pixel offset within the tile
handle->marker_offset_x = (int)((x - (int)x) * MAP_TILES_TILE_SIZE);
handle->marker_offset_y = (int)((y - (int)y) * MAP_TILES_TILE_SIZE);
ESP_LOGI(TAG, "GPS to tile: tile_x=%d, tile_y=%d, offset_x=%d, offset_y=%d",
handle->tile_x, handle->tile_y, handle->marker_offset_x, handle->marker_offset_y);
}
bool map_tiles_is_gps_within_tiles(map_tiles_handle_t handle, double lat, double lon)
{
if (!handle || !handle->initialized) {
return false;
}
double x, y;
map_tiles_gps_to_tile_xy(handle, lat, lon, &x, &y);
int gps_tile_x = (int)x;
int gps_tile_y = (int)y;
bool within_x = (gps_tile_x >= handle->tile_x && gps_tile_x < handle->tile_x + handle->grid_cols);
bool within_y = (gps_tile_y >= handle->tile_y && gps_tile_y < handle->tile_y + handle->grid_rows);
return within_x && within_y;
}
void map_tiles_get_position(map_tiles_handle_t handle, int* tile_x, int* tile_y)
{
if (!handle || !handle->initialized) {
ESP_LOGE(TAG, "Handle not initialized");
return;
}
if (tile_x) *tile_x = handle->tile_x;
if (tile_y) *tile_y = handle->tile_y;
}
void map_tiles_set_position(map_tiles_handle_t handle, int tile_x, int tile_y)
{
if (!handle || !handle->initialized) {
ESP_LOGE(TAG, "Handle not initialized");
return;
}
handle->tile_x = tile_x;
handle->tile_y = tile_y;
}
void map_tiles_get_marker_offset(map_tiles_handle_t handle, int* offset_x, int* offset_y)
{
if (!handle || !handle->initialized) {
ESP_LOGE(TAG, "Handle not initialized");
return;
}
if (offset_x) *offset_x = handle->marker_offset_x;
if (offset_y) *offset_y = handle->marker_offset_y;
}
void map_tiles_set_marker_offset(map_tiles_handle_t handle, int offset_x, int offset_y)
{
if (!handle || !handle->initialized) {
ESP_LOGE(TAG, "Handle not initialized");
return;
}
handle->marker_offset_x = offset_x;
handle->marker_offset_y = offset_y;
}
lv_image_dsc_t* map_tiles_get_image(map_tiles_handle_t handle, int index)
{
if (!handle || !handle->initialized || index < 0 || index >= handle->tile_count) {
return NULL;
}
return &handle->tile_imgs[index];
}
uint8_t* map_tiles_get_buffer(map_tiles_handle_t handle, int index)
{
if (!handle || !handle->initialized || index < 0 || index >= handle->tile_count) {
return NULL;
}
return handle->tile_bufs[index];
}
void map_tiles_set_loading_error(map_tiles_handle_t handle, bool error)
{
if (!handle || !handle->initialized) {
ESP_LOGE(TAG, "Handle not initialized");
return;
}
handle->tile_loading_error = error;
}
bool map_tiles_has_loading_error(map_tiles_handle_t handle)
{
if (!handle || !handle->initialized) {
return true;
}
return handle->tile_loading_error;
}
void map_tiles_cleanup(map_tiles_handle_t handle)
{
if (!handle) {
return;
}
if (handle->initialized) {
// Free tile buffers
if (handle->tile_bufs) {
for (int i = 0; i < handle->tile_count; i++) {
if (handle->tile_bufs[i]) {
heap_caps_free(handle->tile_bufs[i]);
handle->tile_bufs[i] = NULL;
}
}
free(handle->tile_bufs);
handle->tile_bufs = NULL;
}
// Free tile image descriptors array
if (handle->tile_imgs) {
free(handle->tile_imgs);
handle->tile_imgs = NULL;
}
handle->initialized = false;
ESP_LOGI(TAG, "Map tiles cleaned up");
}
// Free base path and folder names, then handle
if (handle->base_path) {
free(handle->base_path);
}
for (int i = 0; i < handle->tile_type_count; i++) {
if (handle->tile_folders[i]) {
free(handle->tile_folders[i]);
}
}
free(handle);
}
void map_tiles_get_grid_size(map_tiles_handle_t handle, int* cols, int* rows)
{
if (!handle || !handle->initialized || !cols || !rows) {
if (cols) *cols = 0;
if (rows) *rows = 0;
return;
}
*cols = handle->grid_cols;
*rows = handle->grid_rows;
}
int map_tiles_get_tile_count(map_tiles_handle_t handle)
{
if (!handle || !handle->initialized) {
return 0;
}
return handle->tile_count;
}