// Synchronous browser filesystem adapter for the production MapViewport. // Tile positions follow the existing Linux adapter and shared Mercator helpers. // No page controls, overlays, labels, fonts or styles are defined here. #include "ui/widgets/map/map_tiles.h" #include "ui_map_runtime/map_tiles/map_tile_geometry.h" #include #include #include #include static uint8_t source_id = 0; static bool contours = false; static bool missing = false; static double world_size(int z) { return std::ldexp(256.0, z); } static double world_x(double lon, int z) { return (lon + 180.0) / 360.0 * world_size(z); } static double world_y(double lat, int z) { lat = std::clamp(lat, -85.05112878, 85.05112878); const double s = std::sin(lat * M_PI / 180.0); return (.5 - std::log((1 + s) / (1 - s)) / (4 * M_PI)) * world_size(z); } static void world_geo(double x, double y, int z, double& lat, double& lon) { const double size = world_size(z); lon = x / size * 360 - 180; lat = std::atan(std::sinh(M_PI * (1 - 2 * y / size))) * 180 / M_PI; } void normalize_tile(int z, int& x, int& y) { ui::map_tiles::normalizeTile(z, x, y); } void latLngToTile(double lat, double lon, int zoom, int& x, int& y) { ui::map_tiles::latLngToTile(lat, lon, zoom, x, y); } void tileToLatLng(int x, int y, int zoom, double& lat, double& lon) { world_geo(x * 256.0, y * 256.0, zoom, lat, lon); } void tileToPixel(int x, int y, int, int& px, int& py) { px = x * 256; py = y * 256; } uint8_t sanitize_map_source(uint8_t source) { return source < 3 ? source : 0; } const char* map_source_label(uint8_t source) { return source == 1 ? "Terrain" : source == 2 ? "Satellite" : "OSM"; } bool build_base_tile_path(int z, int x, int y, uint8_t source, char* out, size_t n) { const char* key = source == 1 ? "terrain" : source == 2 ? "satellite" : "osm"; return std::snprintf(out, n, "A:/maps/base/%s/%d/%d/%d.png", key, z, x, y) > 0; } bool build_contour_tile_path(int z, int x, int y, char* out, size_t n) { return std::snprintf(out, n, "A:/maps/contour/major-50/%d/%d/%d.png", z, x, y) > 0; } static bool exists(const char* path) { FILE* f = std::fopen(path + 2, "rb"); if (!f) return false; std::fclose(f); return true; } bool base_tile_available(int z, int x, int y, uint8_t source) { char path[180]; build_base_tile_path(z, x, y, source, path, sizeof(path)); return exists(path); } bool map_source_directory_available(uint8_t source) { return source < 3; } bool contour_directory_available() { return false; } bool take_missing_tile_notice(uint8_t* out) { if (out) *out = source_id; const bool result = missing; missing = false; return result; } void set_map_render_options(uint8_t source, bool contour) { source_id = sanitize_map_source(source); contours = contour; } void update_map_anchor(TileContext& c, double lat, double lon, int zoom, int pan_x, int pan_y, bool fix) { if (!c.anchor || !c.map_container) return; auto& a = *c.anchor; a.valid = fix; if (!fix) return; a.z = zoom; a.n = std::ldexp(1.0, zoom); int x, y; latLngToTile(lat, lon, zoom, x, y); a.gps_tile_x = x; a.gps_tile_y = y; a.gps_global_pixel_x = static_cast(std::lround(world_x(lon, zoom))); a.gps_global_pixel_y = static_cast(std::lround(world_y(lat, zoom))); a.gps_tile_pixel_x = x * 256; a.gps_tile_pixel_y = y * 256; a.gps_offset_x = a.gps_global_pixel_x - a.gps_tile_pixel_x; a.gps_offset_y = a.gps_global_pixel_y - a.gps_tile_pixel_y; a.gps_tile_screen_x = lv_obj_get_width(c.map_container) / 2 - a.gps_offset_x + pan_x; a.gps_tile_screen_y = lv_obj_get_height(c.map_container) / 2 - a.gps_offset_y + pan_y; } bool gps_screen_pos(const TileContext& c, double lat, double lon, int& x, int& y) { if (!c.anchor || !c.anchor->valid) return false; const auto& a = *c.anchor; double dx = world_x(lon, a.z) - a.gps_global_pixel_x; const double size = world_size(a.z); if (dx > size / 2) dx -= size; if (dx < -size / 2) dx += size; x = std::lround(a.gps_tile_screen_x + a.gps_offset_x + dx); y = std::lround(a.gps_tile_screen_y + a.gps_offset_y + world_y(lat, a.z) - a.gps_global_pixel_y); return true; } bool tile_screen_pos_xyz(const TileContext& c, int x, int y, int z, int& sx, int& sy) { if (!c.anchor || !c.anchor->valid || c.anchor->z != z) return false; const auto& a = *c.anchor; int dx = x - a.gps_tile_x, n = 1 << z; if (dx > n / 2) dx -= n; if (dx < -n / 2) dx += n; sx = a.gps_tile_screen_x + dx * 256; sy = a.gps_tile_screen_y + (y - a.gps_tile_y) * 256; return true; } void get_screen_center_lat_lng(const TileContext& c, double& lat, double& lon) { if (!c.anchor || !c.anchor->valid) { lat = lon = 0; return; } const auto& a = *c.anchor; world_geo(a.gps_global_pixel_x + lv_obj_get_width(c.map_container) / 2 - a.gps_tile_screen_x - a.gps_offset_x, a.gps_global_pixel_y + lv_obj_get_height(c.map_container) / 2 - a.gps_tile_screen_y - a.gps_offset_y, a.z, lat, lon); } bool tile_in_rect(int x, int y, int w, int h, int margin) { return x < w + margin && y < h + margin && x + 256 > -margin && y + 256 > -margin; } void cleanup_tiles(TileContext& c) { if (c.tiles) { for (auto& t : *c.tiles) { if (t.img_obj) lv_obj_delete(t.img_obj); if (t.contour_obj) lv_obj_delete(t.contour_obj); } c.tiles->clear(); } if (c.has_map_data) *c.has_map_data = false; if (c.has_visible_map_data) *c.has_visible_map_data = false; } void calculate_required_tiles(TileContext& c, double lat, double lon, int z, int px, int py, bool fix) { update_map_anchor(c, lat, lon, z, px, py, fix); if (!c.tiles || !c.anchor || !c.anchor->valid) return; bool any = false; for (auto& t : *c.tiles) { t.visible = false; if (t.img_obj) lv_obj_add_flag(t.img_obj, LV_OBJ_FLAG_HIDDEN); } const int w = lv_obj_get_width(c.map_container), h = lv_obj_get_height(c.map_container); double center_lat, center_lon; get_screen_center_lat_lng(c, center_lat, center_lon); for (auto xy : ui::map_tiles::tilesAround(center_lat, center_lon, z, w / 256 + 2, h / 256 + 2)) { int sx, sy; if (!tile_screen_pos_xyz(c, xy.x, xy.y, z, sx, sy) || !tile_in_rect(sx, sy, w, h, 0)) continue; auto found = std::find_if(c.tiles->begin(), c.tiles->end(), [&](const auto& t) { return t.x == xy.x && t.y == xy.y && t.z == z && t.map_source == source_id; }); if (found == c.tiles->end()) { MapTile t; t.x = xy.x; t.y = xy.y; t.z = z; t.map_source = source_id; t.has_png_file = base_tile_available(z, t.x, t.y, source_id); c.tiles->push_back(t); found = c.tiles->end() - 1; } auto& tile = *found; if (!tile.has_png_file) { missing = true; continue; } if (!tile.img_obj) { tile.img_obj = lv_image_create(c.map_container); char path[180]; build_base_tile_path(z, tile.x, tile.y, source_id, path, sizeof(path)); lv_image_set_src(tile.img_obj, path); } lv_obj_set_pos(tile.img_obj, sx, sy); lv_obj_clear_flag(tile.img_obj, LV_OBJ_FLAG_HIDDEN); tile.visible = true; any = true; } if (c.has_map_data) *c.has_map_data = any; if (c.has_visible_map_data) *c.has_visible_map_data = any; } void tile_loader_step(TileContext&) {} void init_tile_context(TileContext& c, lv_obj_t* obj, MapAnchor* anchor, std::vector* tiles, ui::map_tiles::MapTileRenderQueue* queue, bool* has, bool* visible) { c = {obj, anchor, tiles, queue, has, visible}; } void release_tile_context(TileContext& c) { cleanup_tiles(c); c = {}; }