Added Zoom-Out capability to Geomap (#1710)

This commit is contained in:
Mark Thompson 2024-01-04 00:32:44 -06:00 committed by GitHub
parent d93c914dd3
commit afa34d83b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 76 additions and 35 deletions

View File

@ -145,15 +145,28 @@ GeoMap::GeoMap(
} }
bool GeoMap::on_encoder(const EncoderEvent delta) { bool GeoMap::on_encoder(const EncoderEvent delta) {
if ((delta > 0) && (map_zoom < 5)) { // Valid map_zoom values are -2 to -MAX_MAP_ZOOM_OUT, and +1 to +MAX_MAP_ZOOM_IN (values of 0 and -1 are not permitted)
map_zoom++; if (delta > 0) {
if (map_zoom < MAX_MAP_ZOOM_IN) {
if (map_zoom == -2) {
map_zoom = 1;
} else {
map_zoom++;
// Ensure that MOD(240,map_zoom)==0 for the map_zoom_line() function // When zooming in, ensure that MOD(240,map_zoom)==0 for the map_zoom_line() function
if (geomap_rect_width % map_zoom != 0) { if ((map_zoom > 1) && (geomap_rect_width % map_zoom != 0)) {
map_zoom--; map_zoom--;
}
}
}
} else if (delta < 0) {
if (map_zoom > -MAX_MAP_ZOOM_OUT) {
if (map_zoom == 1) {
map_zoom = -2;
} else {
map_zoom--;
}
} }
} else if ((delta < 0) && (map_zoom > 1)) {
map_zoom--;
} else { } else {
return false; return false;
} }
@ -164,17 +177,31 @@ bool GeoMap::on_encoder(const EncoderEvent delta) {
return true; return true;
} }
void GeoMap::map_zoom_line(ui::Color* buffer) { void GeoMap::map_read_line(ui::Color* buffer, uint16_t pixels) {
if (map_zoom <= 1) { if (map_zoom == 1) {
return; map_file.read(buffer, pixels << 1);
} } else if (map_zoom > 1) {
map_file.read(buffer, (pixels / map_zoom) << 1);
// As long as MOD(width,map_zoom)==0 then we don't need to check buffer overflow case when stretching last pixel; // Zoom in: Expand each pixel to "map_zoom" number of pixels.
// For 240 width, than means no check is needed for map_zoom values up to 6 // Future TODO: Add dithering to smooth out the pixelation.
for (int i = (geomap_rect_width / map_zoom) - 1; i >= 0; i--) { // As long as MOD(width,map_zoom)==0 then we don't need to check buffer overflow case when stretching last pixel;
for (int j = 0; j < map_zoom; j++) { // For 240 width, than means no check is needed for map_zoom values up to 6
buffer[(i * map_zoom) + j] = buffer[i]; for (int i = (geomap_rect_width / map_zoom) - 1; i >= 0; i--) {
for (int j = 0; j < map_zoom; j++) {
buffer[(i * map_zoom) + j] = buffer[i];
}
} }
} else {
ui::Color* zoom_out_buffer = new ui::Color[(pixels * (-map_zoom))];
map_file.read(zoom_out_buffer, (pixels * (-map_zoom)) << 1);
// Zoom out: Collapse each group of "-map_zoom" pixels into one pixel.
// Future TODO: Average each group of pixels (in both X & Y directions if possible).
for (int i = 0; i < geomap_rect_width; i++) {
buffer[i] = zoom_out_buffer[i * (-map_zoom)];
}
delete zoom_out_buffer;
} }
} }
@ -187,9 +214,12 @@ void GeoMap::draw_markers(Painter& painter) {
int x = (map_width * (item.lon + 180) / 360) - x_pos; int x = (map_width * (item.lon + 180) / 360) - x_pos;
int y = (map_height - ((map_world_lon / 2 * log((1 + lat_rad) / (1 - lat_rad))) - map_offset)) - y_pos; // Offset added for the GUI int y = (map_height - ((map_world_lon / 2 * log((1 + lat_rad) / (1 - lat_rad))) - map_offset)) - y_pos; // Offset added for the GUI
if (map_zoom != 1) { if (map_zoom > 1) {
x = ((x - (r.width() / 2)) * map_zoom) + (r.width() / 2); x = ((x - (r.width() / 2)) * map_zoom) + (r.width() / 2);
y = ((y - (r.height() / 2)) * map_zoom) + (r.height() / 2); y = ((y - (r.height() / 2)) * map_zoom) + (r.height() / 2);
} else if (map_zoom < 0) {
x = ((x - (r.width() / 2)) / (-map_zoom)) + (r.width() / 2);
y = ((y - (r.height() / 2)) / (-map_zoom)) + (r.height() / 2);
} }
if ((x >= 0) && (x < r.width()) && if ((x >= 0) && (x < r.width()) &&
@ -208,31 +238,39 @@ void GeoMap::draw_markers(Painter& painter) {
} }
void GeoMap::paint(Painter& painter) { void GeoMap::paint(Painter& painter) {
uint16_t line, j;
uint32_t zoom_seek_x, zoom_seek_y;
std::array<ui::Color, 240> map_line_buffer;
const auto r = screen_rect(); const auto r = screen_rect();
std::array<ui::Color, 240> map_line_buffer;
// Ony redraw map if it moved by at least 1 pixel // Ony redraw map if it moved by at least 1 pixel
// or the markers list was updated // or the markers list was updated
int x_diff = abs(x_pos - prev_x_pos); int x_diff = abs(x_pos - prev_x_pos);
int y_diff = abs(y_pos - prev_y_pos); int y_diff = abs(y_pos - prev_y_pos);
if (markerListUpdated || (x_diff >= map_zoom * 3) || (y_diff >= map_zoom * 3)) {
if (map_zoom == 1) { if (markerListUpdated || (x_diff >= 3) || (y_diff >= 3)) {
zoom_seek_x = zoom_seek_y = 0; int32_t zoom_seek_x = x_pos;
} else { int32_t zoom_seek_y = y_pos;
zoom_seek_x = (r.width() - (r.width() / map_zoom)) / 2;
zoom_seek_y = (r.height() - (r.height() / map_zoom)) / 2; // Adjust starting corner position of map per zoom setting
if (map_zoom > 1) {
zoom_seek_x += (r.width() - (r.width() / map_zoom)) / 2;
zoom_seek_y += (r.height() - (r.height() / map_zoom)) / 2;
} else if (map_zoom < 0) {
zoom_seek_x += (r.width() - (r.width() * (-map_zoom))) / 2;
zoom_seek_y += (r.height() - (r.height() * (-map_zoom))) / 2;
} }
for (line = 0; line < (r.height() / map_zoom); line++) { // Read from map file and display to zoomed scale
map_file.seek(4 + ((x_pos + zoom_seek_x + (map_width * (y_pos + line + zoom_seek_y))) << 1)); int duplicate_lines = (map_zoom < 0) ? 1 : map_zoom;
map_file.read(map_line_buffer.data(), (r.width() / map_zoom) << 1); for (uint16_t line = 0; line < (r.height() / duplicate_lines); line++) {
map_zoom_line(map_line_buffer.data()); uint16_t seek_line = zoom_seek_y + ((map_zoom >= 0) ? line : line * (-map_zoom));
for (j = 0; j < map_zoom; j++) { map_file.seek(4 + ((zoom_seek_x + (map_width * seek_line)) << 1));
display.draw_pixels({0, r.top() + (line * map_zoom) + j, r.width(), 1}, map_line_buffer); map_read_line(map_line_buffer.data(), r.width());
for (uint16_t j = 0; j < duplicate_lines; j++) {
display.draw_pixels({0, r.top() + (line * duplicate_lines) + j, r.width(), 1}, map_line_buffer);
} }
} }
prev_x_pos = x_pos; prev_x_pos = x_pos;
prev_y_pos = y_pos; prev_y_pos = y_pos;
@ -323,8 +361,8 @@ bool GeoMap::manual_panning() {
} }
void GeoMap::draw_scale(Painter& painter) { void GeoMap::draw_scale(Painter& painter) {
uint16_t km = 100; uint16_t km = 800;
uint16_t scale_width = km * pixels_per_km * map_zoom; uint16_t scale_width = (map_zoom > 0) ? km * pixels_per_km * map_zoom : km * pixels_per_km / (-map_zoom);
while (scale_width > screen_width / 2) { while (scale_width > screen_width / 2) {
scale_width /= 2; scale_width /= 2;

View File

@ -29,6 +29,9 @@
#include "portapack.hpp" #include "portapack.hpp"
#define MAX_MAP_ZOOM_IN 5
#define MAX_MAP_ZOOM_OUT 10
namespace ui { namespace ui {
enum GeoMapMode { enum GeoMapMode {
@ -187,7 +190,7 @@ class GeoMap : public Widget {
void draw_bearing(const Point origin, const uint16_t angle, uint32_t size, const Color color); void draw_bearing(const Point origin, const uint16_t angle, uint32_t size, const Color color);
void draw_marker(Painter& painter, const ui::Point itemPoint, const uint16_t itemAngle, const std::string itemTag, const Color color = Color::red(), const Color fontColor = Color::white(), const Color backColor = Color::black()); void draw_marker(Painter& painter, const ui::Point itemPoint, const uint16_t itemAngle, const std::string itemTag, const Color color = Color::red(), const Color fontColor = Color::white(), const Color backColor = Color::black());
void draw_markers(Painter& painter); void draw_markers(Painter& painter);
void map_zoom_line(ui::Color* buffer); void map_read_line(ui::Color* buffer, uint16_t pixels);
bool manual_panning_{false}; bool manual_panning_{false};
GeoMapMode mode_{}; GeoMapMode mode_{};