diff --git a/include/graphics/map/MapPanel.h b/include/graphics/map/MapPanel.h index f72e65f..8fff115 100644 --- a/include/graphics/map/MapPanel.h +++ b/include/graphics/map/MapPanel.h @@ -51,6 +51,7 @@ class MapPanel void setHomeLocationImage(lv_obj_t *img); void setGpsPositionImage(lv_obj_t *img); void setNoTileImage(const lv_image_dsc_t *img_src); + void forceRedraw(void) { needsRedraw = true; } // must be called for incremental drawing of all changes void task_handler(void); ~MapPanel(void); diff --git a/include/graphics/map/MapTileSettings.h b/include/graphics/map/MapTileSettings.h index 3255f0b..7aba2fd 100644 --- a/include/graphics/map/MapTileSettings.h +++ b/include/graphics/map/MapTileSettings.h @@ -1,6 +1,7 @@ #pragma once #include +#include /** * Global settings for raster tile map @@ -27,13 +28,18 @@ class MapTileSettings static void setDefaultLon(float lon) { defaultLon = lon; } static const char *getPrefix(void) { return prefix; } - static void setPrefix(const char *p) { prefix = p; } + static void setPrefix(const char *p) { strcpy(prefix, p); } static const char *getTileStyle(void) { return tileStyle; } - static void setTileStyle(const char *p) { tileStyle = p; } + static void setTileStyle(const char *p) + { + strcpy(tileStyle, p); + if (tileStyle[strlen(tileStyle) - 1] != '/') + strcat(tileStyle, "/"); + } static const char *getTileFormat(void) { return tileFormat; } - static void setTileFormat(const char *p) { tileFormat = p; } + static void setTileFormat(const char *p) { strcpy(tileFormat, p); } static bool getDebug(void) { return debug; } static void setDebug(bool on) { debug = on; } @@ -45,8 +51,8 @@ class MapTileSettings static uint32_t cacheSize; static float defaultLat; static float defaultLon; - static const char *prefix; - static const char *tileStyle; - static const char *tileFormat; + static char prefix[]; + static char tileStyle[]; + static char tileFormat[]; static bool debug; }; diff --git a/include/graphics/map/OSMTiles.h b/include/graphics/map/OSMTiles.h index 6ffe7cf..e7a3e09 100644 --- a/include/graphics/map/OSMTiles.h +++ b/include/graphics/map/OSMTiles.h @@ -25,14 +25,15 @@ template class OSMTiles }; // create instance of this class and provide cb function for loading images - static OSMTiles *create(const char *_prefix, std::function cb); + static OSMTiles *create(std::function cb); // filename caching for GeoPoint tile bool load(OSMTiles::Tile &tile, IMG *img) { if (!tile.filename[0]) { - std::snprintf(tile.filename, IMG_PATH_LEN, "%s/%s%d/%d/%d.%s", prefix, MapTileSettings::getTileStyle(), - tile.zoomLevel, tile.xTile, tile.yTile, MapTileSettings::getTileFormat()); + std::snprintf(tile.filename, IMG_PATH_LEN, "%s/%s%d/%d/%d.%s", MapTileSettings::getPrefix(), + MapTileSettings::getTileStyle(), tile.zoomLevel, tile.xTile, tile.yTile, + MapTileSettings::getTileFormat()); } return loadcb(tile.filename, img); } @@ -41,24 +42,20 @@ template class OSMTiles bool load(const GeoPoint &tile, IMG *img) { char name[IMG_PATH_LEN]; - std::snprintf(name, IMG_PATH_LEN, "%s/%s%d/%d/%d.%s", prefix, MapTileSettings::getTileStyle(), tile.zoomLevel, tile.xTile, - tile.yTile, MapTileSettings::getTileFormat()); + std::snprintf(name, IMG_PATH_LEN, "%s/%s%d/%d/%d.%s", MapTileSettings::getPrefix(), MapTileSettings::getTileStyle(), + tile.zoomLevel, tile.xTile, tile.yTile, MapTileSettings::getTileFormat()); return loadcb(name, img); } private: OSMTiles() = default; - static const char *prefix; static std::function loadcb; }; -template OSMTiles *OSMTiles::create(const char *_prefix, std::function cb) +template OSMTiles *OSMTiles::create(std::function cb) { - prefix = _prefix; loadcb = cb; return new OSMTiles; } -template const char *OSMTiles::prefix; - template std::function OSMTiles::loadcb; diff --git a/include/graphics/view/TFT/TFTView_320x240.h b/include/graphics/view/TFT/TFTView_320x240.h index 2a2ac95..dd66c50 100644 --- a/include/graphics/view/TFT/TFTView_320x240.h +++ b/include/graphics/view/TFT/TFTView_320x240.h @@ -2,6 +2,7 @@ #include "graphics/common/MeshtasticView.h" #include "meshtastic/clientonly.pb.h" +#include class MapPanel; @@ -189,7 +190,7 @@ class TFTView_320x240 : public MeshtasticView // show map and load tiles virtual void loadMap(void); // check what maps and formats are available on SD - virtual void checkMapSD(void); + virtual std::set loadMapStyles(void); // add objects on map virtual void addOrUpdateMap(uint32_t nodeNum, int32_t lat, int32_t lon); // remove objects from map @@ -331,6 +332,7 @@ class TFTView_320x240 : public MeshtasticView static void ui_event_frequency_slot_slider(lv_event_t *e); static void ui_event_modem_preset_dropdown(lv_event_t *e); static void ui_event_setup_region_dropdown(lv_event_t *e); + static void ui_event_map_style_dropdown(lv_event_t *e); static void ui_event_calibration_screen_loaded(lv_event_t *e); diff --git a/source/graphics/TFT/TFTView_320x240.cpp b/source/graphics/TFT/TFTView_320x240.cpp index 34aba6c..e2fe108 100644 --- a/source/graphics/TFT/TFTView_320x240.cpp +++ b/source/graphics/TFT/TFTView_320x240.cpp @@ -810,6 +810,7 @@ void TFTView_320x240::ui_events_init(void) lv_obj_add_event_cb(objects.gps_lock_button, ui_event_lockGps, LV_EVENT_CLICKED, NULL); lv_obj_add_event_cb(objects.map_brightness_slider, ui_event_mapBrightnessSlider, LV_EVENT_VALUE_CHANGED, NULL); lv_obj_add_event_cb(objects.map_contrast_slider, ui_event_mapContrastSlider, LV_EVENT_VALUE_CHANGED, NULL); + lv_obj_add_event_cb(objects.map_style_dropdown, ui_event_map_style_dropdown, LV_EVENT_VALUE_CHANGED, NULL); // tools buttons lv_obj_add_event_cb(objects.tools_mesh_detector_button, ui_event_mesh_detector, LV_EVENT_CLICKED, 0); @@ -2092,6 +2093,16 @@ void TFTView_320x240::ui_event_mapContrastSlider(lv_event_t *e) lv_obj_set_style_opa(objects.raw_map_panel, ct, LV_PART_MAIN | LV_STATE_DEFAULT); } +void TFTView_320x240::ui_event_map_style_dropdown(lv_event_t *e) +{ + lv_dropdown_get_selected_str(objects.map_style_dropdown, THIS->db.uiConfig.map_data.style, + sizeof(THIS->db.uiConfig.map_data.style)); + MapTileSettings::setTileStyle(THIS->db.uiConfig.map_data.style); + THIS->controller->storeUIConfig(THIS->db.uiConfig); + lv_obj_add_flag(objects.map_osd_panel, LV_OBJ_FLAG_HIDDEN); + THIS->map->forceRedraw(); +} + void TFTView_320x240::ui_event_mapNodeButton(lv_event_t *e) { // navigate to node in node list @@ -2181,13 +2192,46 @@ void TFTView_320x240::loadMap(void) #else map = new MapPanel(objects.raw_map_panel, new SDCardService()); #endif + map->setHomeLocationImage(objects.home_location_image); + if (updateSDCard()) { map->setNoTileImage(&img_no_tile_image); lv_obj_add_flag(objects.world_image, LV_OBJ_FLAG_HIDDEN); + + std::set mapStyles = loadMapStyles(); + if (mapStyles.find("/map") != mapStyles.end()) { + // no styles found, but the /map directory, so use it + MapTileSettings::setPrefix("/map"); + lv_obj_add_flag(objects.map_style_dropdown, LV_OBJ_FLAG_HIDDEN); + } else if (!mapStyles.empty()) { + // populate dropdown + uint16_t pos = 0; + bool savedStyleOK = false; + lv_dropdown_set_options(objects.map_style_dropdown, ""); + for (auto it : mapStyles) { + lv_dropdown_add_option(objects.map_style_dropdown, it.c_str(), pos); + if (it == db.uiConfig.map_data.style) { + lv_dropdown_set_selected(objects.map_style_dropdown, pos); + MapTileSettings::setTileStyle(db.uiConfig.map_data.style); + savedStyleOK = true; + } + pos++; + } + + if (!savedStyleOK) { + // no such style on SD, pick first one we found + char style[20]; + lv_dropdown_set_selected(objects.map_style_dropdown, 0); + lv_dropdown_get_selected_str(objects.map_style_dropdown, style, sizeof(style)); + MapTileSettings::setTileStyle(style); + } + } else { + messageAlert(_("No map tiles found on SDCard!"), true); + lv_obj_clear_flag(objects.world_image, LV_OBJ_FLAG_HIDDEN); + } } else { lv_obj_clear_flag(objects.world_image, LV_OBJ_FLAG_HIDDEN); } - map->setHomeLocationImage(objects.home_location_image); // center map to GPS > home > other nodes > default location if (db.config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT) { @@ -2272,13 +2316,47 @@ void TFTView_320x240::loadMap(void) lv_obj_clear_flag(objects.raw_map_panel, LV_OBJ_FLAG_HIDDEN); } +/** + * Search SD /maps directory for map styles + * Revert back to "/map" directory if no styles found + */ +std::set TFTView_320x240::loadMapStyles(void) +{ + std::set styles; + File maps = SD.open(MapTileSettings::getPrefix()); + if (maps) { + do { + File style = maps.openNextFile(); + if (!style) + break; + std::string path = style.name(); + std::string dir = path.substr(path.find_last_of("/") + 1); + if (style.isDirectory() && dir.c_str()[0] != '.') { + ILOG_DEBUG("found map style: %s", dir.c_str()); + styles.insert(dir); + } + style.close(); + } while (true); + maps.close(); + } + if (styles.empty()) { + File map = SD.open("/map"); + if (map) { + ILOG_DEBUG("found /map dir"); + styles.insert("/map"); + map.close(); + } else { + ILOG_DEBUG("no maps found"); + } + } + return styles; +} + void TFTView_320x240::updateLocationMap(uint32_t num) { lv_label_set_text_fmt(objects.top_map_label, _("Locations Map (%d/%d)"), num, nodeCount); } -void TFTView_320x240::checkMapSD(void) {} - /** * add node location image for display on map */ diff --git a/source/graphics/map/MapPanel.cpp b/source/graphics/map/MapPanel.cpp index 2767af3..e414fc0 100644 --- a/source/graphics/map/MapPanel.cpp +++ b/source/graphics/map/MapPanel.cpp @@ -16,8 +16,7 @@ MapPanel::MapPanel(lv_obj_t *p, ITileService *s) ILOG_DEBUG("panel size: %dx%d", widthPixel, heightPixel); extern OSMTiles *osm; - osm = OSMTiles::create(MapTileSettings::getPrefix(), - [this](const char *name, void *img) -> bool { return service->load(name, img); }); + osm = OSMTiles::create([this](const char *name, void *img) -> bool { return service->load(name, img); }); center(); } diff --git a/source/graphics/map/MapTileSettings.cpp b/source/graphics/map/MapTileSettings.cpp index b26e79f..f0f696c 100644 --- a/source/graphics/map/MapTileSettings.cpp +++ b/source/graphics/map/MapTileSettings.cpp @@ -8,7 +8,7 @@ uint16_t MapTileSettings::tileSize = 256; uint32_t MapTileSettings::cacheSize = 50 * 1024; // LV_FS_CACHE_FROM_BUFFER float MapTileSettings::defaultLat = 51.5003646652f; // @theBigBentern float MapTileSettings::defaultLon = -0.1214328476f; -const char *MapTileSettings::prefix = "/map"; // default map tile directory -const char *MapTileSettings::tileStyle = ""; // { osm/, atlas/, atlas-mobile/, ...} -const char *MapTileSettings::tileFormat = "png"; // use jpg or png +char MapTileSettings::prefix[10] = "/maps"; // default map tile directory +char MapTileSettings::tileStyle[20] = ""; // { osm/, atlas/, atlas-mobile/, ...} +char MapTileSettings::tileFormat[10] = "png"; // use jpg or png bool MapTileSettings::debug = false; \ No newline at end of file