Geomap enhancements (#1767)

* Increase Geomap zoom-in ability
* Use floating point for current position
* Show grid squares when zoomed in too much for map
* Zoom in fast after exceeding map resolution & clean up redundant code
* Revert order of functions to make it easier to review
* Changed grid color for better contrast with markers
* Optimizations
* Set x_pos/x_pos to center pixel versus upper left corner
* Show more distant planes when zoomed out
* Correct pixel offset when zooming in
* Fix oops in x_pos/y_pos centering change
* Wrapping support for lat/lon fields
* Wrapping support (for Geomap lat/lon fields)
* Handle wrapping for negative lat/lon
This commit is contained in:
Mark Thompson 2024-01-16 02:30:31 -06:00 committed by GitHub
parent ae9d6de093
commit bc301c5fdb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 269 additions and 159 deletions

View File

@ -65,6 +65,7 @@ GeoPos::GeoPos(
set_lon(0);
const auto changed_fn = [this](int32_t) {
// Convert degrees/minutes/seconds fields to decimal (floating point) lat/lon degree
float lat_value = lat();
float lon_value = lon();
@ -84,6 +85,27 @@ GeoPos::GeoPos(
field_lon_minutes.on_change = changed_fn;
field_lon_seconds.on_change = changed_fn;
const auto wrapped_lat_seconds = [this](int32_t v) {
field_lat_minutes.on_encoder(v);
};
const auto wrapped_lat_minutes = [this](int32_t v) {
field_lat_degrees.on_encoder((field_lat_degrees.value() >= 0) ? v : -v);
};
const auto wrapped_lon_seconds = [this](int32_t v) {
field_lon_minutes.on_encoder(v);
};
const auto wrapped_lon_minutes = [this](int32_t v) {
field_lon_degrees.on_encoder((field_lon_degrees.value() >= 0) ? v : -v);
};
field_lat_seconds.on_wrap = wrapped_lat_seconds;
field_lat_minutes.on_wrap = wrapped_lat_minutes;
field_lon_seconds.on_wrap = wrapped_lon_seconds;
field_lon_minutes.on_wrap = wrapped_lon_minutes;
text_alt_unit.set(altitude_unit_ ? "m" : "ft");
if (speed_unit_ == KMPH) text_speed_unit.set("kmph");
if (speed_unit_ == MPH) text_speed_unit.set("mph");
@ -173,12 +195,8 @@ bool GeoMap::on_encoder(const EncoderEvent delta) {
if (map_zoom == -2) {
map_zoom = 1;
} else {
map_zoom++;
// When zooming in, ensure that MOD(240,map_zoom)==0 for the map_zoom_line() function
if ((map_zoom > 1) && (geomap_rect_width % map_zoom != 0)) {
map_zoom--;
}
// zoom in faster after exceeding the map resolution limit
map_zoom += (map_zoom >= MAP_ZOOM_RESOLUTION_LIMIT) ? map_zoom : 1;
}
}
} else if (delta < 0) {
@ -186,13 +204,19 @@ bool GeoMap::on_encoder(const EncoderEvent delta) {
if (map_zoom == 1) {
map_zoom = -2;
} else {
map_zoom--;
if (map_zoom > MAP_ZOOM_RESOLUTION_LIMIT)
map_zoom /= 2;
else
map_zoom--;
}
}
} else {
return false;
}
map_visible = map_opened && (map_zoom <= MAP_ZOOM_RESOLUTION_LIMIT);
zoom_pixel_offset = (map_visible && (map_zoom > 1)) ? (float)map_zoom / 2 : 0.0f;
// Trigger map redraw
markerListUpdated = true;
set_dirty();
@ -208,7 +232,9 @@ void GeoMap::map_read_line(ui::Color* buffer, uint16_t pixels) {
// Zoom in: Expand each pixel to "map_zoom" number of pixels.
// Future TODO: Add dithering to smooth out the pixelation.
// As long as MOD(width,map_zoom)==0 then we don't need to check buffer overflow case when stretching last pixel;
// For 240 width, than means no check is needed for map_zoom values up to 6
// For 240 width, than means no check is needed for map_zoom values up to 6.
// (Rectangle height must also divide evenly into map_zoom or we get black lines at end of screen)
// Note that zooming in results in a map offset of (1/map_zoom) pixels to the right & downward directions (see zoom_pixel_offset).
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];
@ -228,91 +254,145 @@ void GeoMap::map_read_line(ui::Color* buffer, uint16_t pixels) {
}
void GeoMap::draw_markers(Painter& painter) {
for (int i = 0; i < markerListLen; ++i) {
draw_marker_item(painter, markerList[i], Color::blue(), Color::blue(), Color::magenta());
}
}
void GeoMap::draw_marker_item(Painter& painter, GeoMarker& item, const Color color, const Color fontColor, const Color backColor) {
const auto r = screen_rect();
const ui::Point itemPoint = item_rect_pixel(item);
if ((itemPoint.x() >= 0) && (itemPoint.x() < r.width()) &&
(itemPoint.y() > 10) && (itemPoint.y() < r.height())) // Dont draw within symbol size of top
{
draw_marker(painter, {itemPoint.x(), itemPoint.y() + r.top()}, item.angle, item.tag, color, fontColor, backColor);
}
}
// Calculate screen position of item, adjusted for zoom factor.
ui::Point GeoMap::item_rect_pixel(GeoMarker& item) {
const auto r = screen_rect();
const auto geomap_rect_half_width = r.width() / 2;
const auto geomap_rect_half_height = r.height() / 2;
GeoPoint mapPoint = lat_lon_to_map_pixel(item.lat, item.lon);
float x = mapPoint.x - x_pos;
float y = mapPoint.y - y_pos;
if (map_zoom > 1) {
x = x * map_zoom + zoom_pixel_offset;
y = y * map_zoom + zoom_pixel_offset;
} else if (map_zoom < 0) {
x = x / (-map_zoom);
y = y / (-map_zoom);
}
x += geomap_rect_half_width;
y += geomap_rect_half_height;
return {(int16_t)x, (int16_t)y};
}
// Converts latitude/longitude to pixel coordinates in map file.
// (Note that when map_zoom==1, one pixel in map file corresponds to 1 pixel on screen)
GeoPoint GeoMap::lat_lon_to_map_pixel(float lat, float lon) {
// Using WGS 84/Pseudo-Mercator projection
float x = (map_width * (lon + 180) / 360);
// Latitude calculation based on https://stackoverflow.com/a/10401734/2278659
double lat_rad = sin(lat * pi / 180);
float y = (map_height - ((map_world_lon / 2 * log((1 + lat_rad) / (1 - lat_rad))) - map_offset));
return {x, y};
}
// Draw grid in place of map (when zoom-in level is too high).
void GeoMap::draw_map_grid() {
const auto r = screen_rect();
for (int i = 0; i < markerListLen; ++i) {
GeoMarker& item = markerList[i];
double lat_rad = sin(item.lat * pi / 180);
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
// Grid spacing is just based on zoom at the moment, and centered on screen.
// TODO: Maybe align with latitude/longitude seconds instead?
int grid_spacing = map_zoom * 2;
int x = (r.width() / 2) % grid_spacing;
int y = (r.height() / 2) % grid_spacing;
if (map_zoom > 1) {
x = ((x - (r.width() / 2)) * map_zoom) + (r.width() / 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 (map_zoom <= MAP_ZOOM_RESOLUTION_LIMIT)
return;
if ((x >= 0) && (x < r.width()) &&
(y > 10) && (y < r.height())) // Dont draw within symbol size of top
{
ui::Point itemPoint(x, y + r.top());
if (y >= 32) { // Dont draw text if it would overlap top
// Text and symbol
draw_marker(painter, itemPoint, item.angle, item.tag, Color::blue(), Color::blue(), Color::magenta());
} else {
// Only symbol
draw_bearing(itemPoint, item.angle, 10, Color::blue());
}
}
display.fill_rectangle({{0, r.top()}, {r.width(), r.height()}}, Color::black());
for (uint16_t line = y; line < r.height(); line += grid_spacing) {
display.fill_rectangle({{0, r.top() + line}, {r.width(), 1}}, Color::darker_grey());
}
for (uint16_t column = x; column < r.width(); column += grid_spacing) {
display.fill_rectangle({{column, r.top()}, {1, r.height()}}, Color::darker_grey());
}
}
void GeoMap::paint(Painter& painter) {
const auto r = screen_rect();
std::array<ui::Color, 240> map_line_buffer;
std::array<ui::Color, geomap_rect_width> map_line_buffer;
int16_t zoom_seek_x, zoom_seek_y;
// Ony redraw map if it moved by at least 1 pixel
// or the markers list was updated
int x_diff = abs(x_pos - prev_x_pos);
int y_diff = abs(y_pos - prev_y_pos);
int x_diff = abs(x_pos - prev_x_pos) * ((map_zoom > 1) ? map_zoom : 1);
int y_diff = abs(y_pos - prev_y_pos) * ((map_zoom > 1) ? map_zoom : 1);
int min_diff = 1;
if (markerListUpdated || (x_diff >= 3) || (y_diff >= 3)) {
int32_t zoom_seek_x = x_pos;
int32_t zoom_seek_y = y_pos;
// 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;
}
// Read from map file and display to zoomed scale
int duplicate_lines = (map_zoom < 0) ? 1 : map_zoom;
for (uint16_t line = 0; line < (r.height() / duplicate_lines); line++) {
uint16_t seek_line = zoom_seek_y + ((map_zoom >= 0) ? line : line * (-map_zoom));
map_file.seek(4 + ((zoom_seek_x + (map_width * seek_line)) << 1));
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;
if (markerListUpdated || (x_diff >= min_diff) || (y_diff >= min_diff)) {
prev_x_pos = x_pos; // Note x_pos/y_pos pixel position in map file now correspond to screen rect CENTER pixel
prev_y_pos = y_pos;
// Adjust starting corner position of map per zoom setting;
// When zooming in the map should technically by shifted left & up by another map_zoom/2 pixels but
// the map_read_line() function doesn't handle that yet so we're adjusting markers instead (see zoom_pixel_offset).
if (map_zoom > 1) {
zoom_seek_x = x_pos - (float)r.width() / (2 * map_zoom);
zoom_seek_y = y_pos - (float)r.height() / (2 * map_zoom);
} else if (map_zoom < 0) {
zoom_seek_x = x_pos - (r.width() * (-map_zoom)) / 2;
zoom_seek_y = y_pos - (r.height() * (-map_zoom)) / 2;
} else {
zoom_seek_x = x_pos - (r.width() / 2);
zoom_seek_y = y_pos - (r.height() / 2);
}
if (map_visible) {
// Read from map file and display to zoomed scale
int duplicate_lines = (map_zoom < 0) ? 1 : map_zoom;
for (uint16_t line = 0; line < (r.height() / duplicate_lines); line++) {
uint16_t seek_line = zoom_seek_y + ((map_zoom >= 0) ? line : line * (-map_zoom));
map_file.seek(4 + ((zoom_seek_x + (map_width * seek_line)) << 1));
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);
}
}
} else {
// No map data or excessive zoom; just draw a grid
draw_map_grid();
}
// Draw crosshairs in center in manual panning mode
if (manual_panning_) {
display.fill_rectangle({r.center() - Point(16, 1), {32, 2}}, Color::red());
display.fill_rectangle({r.center() - Point(1, 16), {2, 32}}, Color::red());
display.fill_rectangle({r.center() - Point(16, 1) + Point(zoom_pixel_offset, zoom_pixel_offset), {32, 2}}, Color::red());
display.fill_rectangle({r.center() - Point(1, 16) + Point(zoom_pixel_offset, zoom_pixel_offset), {2, 32}}, Color::red());
}
// Draw the other markers
draw_markers(painter);
draw_scale(painter);
draw_mypos();
draw_mypos(painter);
markerListUpdated = false;
set_clean();
}
// Draw the marker in the center
if (!manual_panning_) {
draw_marker(painter, r.center(), angle_, tag_, Color::red(), Color::white(), Color::black());
if (!manual_panning_ && !hide_center_marker_) {
draw_marker(painter, r.center() + Point(zoom_pixel_offset, zoom_pixel_offset), angle_, tag_, Color::red(), Color::white(), Color::black());
}
}
@ -336,35 +416,41 @@ bool GeoMap::on_touch(const TouchEvent event) {
}
void GeoMap::move(const float lon, const float lat) {
const auto r = screen_rect();
lon_ = lon;
lat_ = lat;
// Using WGS 84/Pseudo-Mercator projection
x_pos = map_width * (lon_ + 180) / 360 - (geomap_rect_width / 2);
// Latitude calculation based on https://stackoverflow.com/a/10401734/2278659
double lat_rad = sin(lat * pi / 180);
y_pos = map_height - ((map_world_lon / 2 * log((1 + lat_rad) / (1 - lat_rad))) - map_offset) - 128; // Offset added for the GUI
// Calculate x_pos/y_pos in map file corresponding to CENTER pixel of screen rect
// (Note there is a 1:1 correspondence between map file pixels and screen pixels when map_zoom=1)
GeoPoint mapPoint = lat_lon_to_map_pixel(lat_, lon_);
x_pos = mapPoint.x;
y_pos = mapPoint.y;
// Cap position
if (x_pos > (map_width - geomap_rect_width))
x_pos = map_width - geomap_rect_width;
if (y_pos > (map_height + geomap_rect_height))
y_pos = map_height - geomap_rect_height;
if (x_pos > (map_width - r.width() / 2))
x_pos = map_width - r.width() / 2;
if (y_pos > (map_height + r.height() / 2))
y_pos = map_height - r.height() / 2;
// Scale calculation
float km_per_deg_lon = cos(lat * pi / 180) * 111.321; // 111.321 km/deg longitude at equator, and 0 km at poles
pixels_per_km = (geomap_rect_width / 2) / km_per_deg_lon;
pixels_per_km = (r.width() / 2) / km_per_deg_lon;
}
bool GeoMap::init() {
auto result = map_file.open("ADSB/world_map.bin");
if (result.is_valid())
return false;
map_opened = !result.is_valid();
map_file.read(&map_width, 2);
map_file.read(&map_height, 2);
if (map_opened) {
map_file.read(&map_width, 2);
map_file.read(&map_height, 2);
} else {
map_width = 32768;
map_height = 32768;
}
map_visible = map_opened;
map_center_x = map_width >> 1;
map_center_y = map_height >> 1;
@ -375,7 +461,7 @@ bool GeoMap::init() {
map_world_lon = map_width / (2 * pi);
map_offset = (map_world_lon / 2 * log((1 + map_bottom) / (1 - map_bottom)));
return true;
return map_opened;
}
void GeoMap::set_mode(GeoMapMode mode) {
@ -391,19 +477,31 @@ bool GeoMap::manual_panning() {
}
void GeoMap::draw_scale(Painter& painter) {
uint16_t km = 800;
uint16_t scale_width = (map_zoom > 0) ? km * pixels_per_km * map_zoom : km * pixels_per_km / (-map_zoom);
uint32_t m = 800000;
uint32_t scale_width = (map_zoom > 0) ? m * map_zoom * pixels_per_km : m * pixels_per_km / (-map_zoom);
ui::Color scale_color = (map_visible) ? Color::black() : Color::white();
std::string km_string;
while (scale_width > screen_width / 2) {
while (scale_width > screen_width * 1000 / 2) {
scale_width /= 2;
km /= 2;
m /= 2;
}
scale_width /= 1000;
if (m < 1000) {
km_string = to_string_dec_uint(m) + "m";
} else {
uint32_t km = m / 1000;
m -= km * 1000;
if (m == 0) {
km_string = to_string_dec_uint(km) + " km";
} else {
km_string = to_string_dec_uint(km) + "." + to_string_dec_uint((m + 50) / 100, 1) + "km";
}
}
std::string km_string = to_string_dec_uint(km) + " km";
display.fill_rectangle({{screen_width - 5 - scale_width, screen_height - 4}, {scale_width, 2}}, Color::black());
display.fill_rectangle({{screen_width - 5, screen_height - 8}, {2, 6}}, Color::black());
display.fill_rectangle({{screen_width - 5 - scale_width, screen_height - 8}, {2, 6}}, Color::black());
display.fill_rectangle({{screen_width - 5 - (uint16_t)scale_width, screen_height - 4}, {(uint16_t)scale_width, 2}}, scale_color);
display.fill_rectangle({{screen_width - 5, screen_height - 8}, {2, 6}}, scale_color);
display.fill_rectangle({{screen_width - 5 - (uint16_t)scale_width, screen_height - 8}, {2, 6}}, scale_color);
painter.draw_string({(uint16_t)(screen_width - 25 - scale_width - km_string.length() * 5 / 2), screen_height - 10}, ui::font::fixed_5x8, Color::black(), Color::white(), km_string);
}
@ -422,9 +520,13 @@ void GeoMap::draw_bearing(const Point origin, const uint16_t angle, uint32_t siz
size--;
}
display.draw_pixel(origin, color); // 1 pixel indicating center pivot point of bearing symbol
}
void GeoMap::draw_marker(Painter& painter, const ui::Point itemPoint, const uint16_t itemAngle, const std::string itemTag, const Color color, const Color fontColor, const Color backColor) {
const auto r = screen_rect();
int tagOffset = 10;
if (mode_ == PROMPT) {
// Cross
@ -442,45 +544,15 @@ void GeoMap::draw_marker(Painter& painter, const ui::Point itemPoint, const uint
tagOffset = 8;
}
// center tag above point
if (itemTag.find_first_not_of(' ') != itemTag.npos) { // only draw tag if we have something other than spaces
if ((itemPoint.y() - r.top() >= 32) && (itemTag.find_first_not_of(' ') != itemTag.npos)) { // only draw tag if doesn't overlap top & not just spaces
painter.draw_string(itemPoint - Point(((int)itemTag.length() * 8 / 2), 14 + tagOffset),
style().font, fontColor, backColor, itemTag);
}
}
void GeoMap::draw_mypos() {
const auto r = screen_rect();
if (my_lat >= 200 || my_lon >= 200) return; // invalid
int x = (map_width * (my_lon + 180) / 360) - x_pos;
double lat_rad = sin(my_lat * pi / 180);
int y = (map_height - ((map_world_lon / 2 * log((1 + lat_rad) / (1 - lat_rad))) - map_offset)) - y_pos; // Offset added for the GUI
auto color = Color::yellow();
if (map_zoom > 1) {
x = ((x - (r.width() / 2)) * map_zoom) + (r.width() / 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()) &&
(y > 10) && (y < r.height())) // Dont draw within symbol size of top
{
ui::Point itemPoint(x, y + r.top());
if (mode_ == PROMPT) {
// Cross
display.fill_rectangle({itemPoint - Point(16, 1), {32, 2}}, color);
display.fill_rectangle({itemPoint - Point(1, 16), {2, 32}}, color);
} else if (my_angle < 360) {
// if we have a valid angle draw bearing
draw_bearing(itemPoint, my_angle, 10, color);
} else {
// draw a small cross
display.fill_rectangle({itemPoint - Point(8, 1), {16, 2}}, color);
display.fill_rectangle({itemPoint - Point(1, 8), {2, 16}}, color);
}
}
void GeoMap::draw_mypos(Painter& painter) {
if ((my_pos.lat < INVALID_LAT_LON) && (my_pos.lon < INVALID_LAT_LON))
draw_marker_item(painter, my_pos, Color::yellow());
}
void GeoMap::clear_markers() {
@ -488,15 +560,17 @@ void GeoMap::clear_markers() {
}
MapMarkerStored GeoMap::store_marker(GeoMarker& marker) {
const auto r = screen_rect();
MapMarkerStored ret;
// Check if it could be on screen
// Only checking one direction to reduce CPU
double lat_rad = sin(marker.lat * pi / 180);
int x = (map_width * (marker.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
if (false == ((x >= 0) && (x < geomap_rect_width) && (y > 10) && (y < geomap_rect_height))) // Dont draw within symbol size of top
{
// (Shows more distant planes when zoomed out)
GeoPoint mapPoint = lat_lon_to_map_pixel(marker.lat, marker.lon);
int x_dist = abs((int)mapPoint.x - (int)x_pos);
int y_dist = abs((int)mapPoint.y - (int)y_pos);
int zoom_out = (map_zoom < 0) ? -map_zoom : 1;
if ((x_dist >= (zoom_out * r.width() / 2)) || (y_dist >= (zoom_out * r.height() / 2))) {
ret = MARKER_NOT_STORED;
} else if (markerListLen < NumMarkerListElements) {
markerList[markerListLen] = marker;
@ -510,14 +584,14 @@ MapMarkerStored GeoMap::store_marker(GeoMarker& marker) {
}
void GeoMap::update_my_position(float lat, float lon, int32_t altitude) {
my_lat = lat;
my_lon = lon;
my_pos.lat = lat;
my_pos.lon = lon;
my_altitude = altitude;
markerListUpdated = true;
set_dirty();
}
void GeoMap::update_my_orientation(uint16_t angle, bool refresh) {
my_angle = angle;
my_pos.angle = angle;
if (refresh) {
markerListUpdated = true;
set_dirty();
@ -527,7 +601,7 @@ void GeoMap::update_my_orientation(uint16_t angle, bool refresh) {
void GeoMapView::focus() {
geopos.focus();
if (!map_opened)
if (!geomap.map_file_opened())
nav_.display_modal("No map", "No world_map.bin file in\n/ADSB/ directory", ABORT);
}
@ -627,8 +701,7 @@ GeoMapView::GeoMapView(
add_child(&geopos);
map_opened = geomap.init();
if (!map_opened) return;
geomap.init();
setup();
@ -660,8 +733,7 @@ GeoMapView::GeoMapView(
add_child(&geopos);
map_opened = geomap.init();
if (!map_opened) return;
geomap.init();
setup();
add_child(&button_ok);

View File

@ -29,16 +29,30 @@
#include "portapack.hpp"
#define MAX_MAP_ZOOM_IN 5
#define MAX_MAP_ZOOM_OUT 10
namespace ui {
#define MAX_MAP_ZOOM_IN 4000
#define MAX_MAP_ZOOM_OUT 12
#define MAP_ZOOM_RESOLUTION_LIMIT 5 // Max zoom-in to show map; rect height & width must divide into this evenly
#define INVALID_LAT_LON 200
#define INVALID_ANGLE 400
#define GEOMAP_BANNER_HEIGHT (3 * 16)
#define GEOMAP_RECT_WIDTH 240
#define GEOMAP_RECT_HEIGHT (320 - 16 - GEOMAP_BANNER_HEIGHT)
enum GeoMapMode {
DISPLAY,
PROMPT
};
struct GeoPoint {
public:
float x{0};
float y{0};
};
struct GeoMarker {
public:
float lat{0};
@ -133,13 +147,15 @@ class GeoPos : public View {
2,
{0, 59},
1,
' '};
' ',
true};
NumberField field_lat_seconds{
{13 * 8, 1 * 16},
2,
{0, 59},
1,
' '};
' ',
true};
Text text_lat_decimal{
{17 * 8, 1 * 16, 13 * 8, 1 * 16},
""};
@ -155,13 +171,15 @@ class GeoPos : public View {
2,
{0, 59},
1,
' '};
' ',
true};
NumberField field_lon_seconds{
{13 * 8, 2 * 16},
2,
{0, 59},
1,
' '};
' ',
true};
Text text_lon_decimal{
{17 * 8, 2 * 16, 13 * 8, 1 * 16},
""};
@ -201,26 +219,40 @@ class GeoMap : public Widget {
angle_ = new_angle;
}
bool map_file_opened() { return map_opened; }
void set_hide_center_marker(bool hide) {
hide_center_marker_ = hide;
}
bool hide_center_marker() { return hide_center_marker_; }
static const int NumMarkerListElements = 30;
void clear_markers();
MapMarkerStored store_marker(GeoMarker& marker);
static const Dim banner_height = 3 * 16;
static const Dim geomap_rect_width = 240;
static const Dim geomap_rect_height = 320 - 16 - banner_height;
static const Dim banner_height = GEOMAP_BANNER_HEIGHT;
static const Dim geomap_rect_width = GEOMAP_RECT_WIDTH;
static const Dim geomap_rect_height = GEOMAP_RECT_HEIGHT;
private:
void draw_scale(Painter& painter);
void draw_bearing(const Point origin, const uint16_t angle, uint32_t size, const Color color);
ui::Point item_rect_pixel(GeoMarker& item);
GeoPoint lat_lon_to_map_pixel(float lat, float lon);
void draw_marker_item(Painter& painter, GeoMarker& item, const Color color, 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_mypos();
void draw_mypos(Painter& painter);
void draw_bearing(const Point origin, const uint16_t angle, uint32_t size, const Color color);
void draw_map_grid();
void map_read_line(ui::Color* buffer, uint16_t pixels);
bool manual_panning_{false};
bool hide_center_marker_{false};
GeoMapMode mode_{};
File map_file{};
bool map_opened{};
bool map_visible{};
uint16_t map_width{}, map_height{};
int32_t map_center_x{}, map_center_y{};
int16_t map_zoom{1};
@ -229,19 +261,18 @@ class GeoMap : public Widget {
double map_world_lon{};
double map_offset{};
int32_t x_pos{}, y_pos{};
int32_t prev_x_pos{0xFFFF}, prev_y_pos{0xFFFF};
float x_pos{}, y_pos{};
float prev_x_pos{32767.0f}, prev_y_pos{32767.0f};
float lat_{};
float lon_{};
float zoom_pixel_offset{0.0f};
float pixels_per_km{};
uint16_t angle_{};
std::string tag_{};
// the portapack's position data ( for example injected from serial )
float my_lat{200};
float my_lon{200};
GeoMarker my_pos{INVALID_LAT_LON, INVALID_LAT_LON, INVALID_ANGLE, ""}; // lat, lon, angle, tag
int32_t my_altitude{0};
uint16_t my_angle{400};
int markerListLen{0};
GeoMarker markerList[NumMarkerListElements];
@ -303,8 +334,6 @@ class GeoMapView : public View {
uint16_t angle_{};
std::function<void(void)> on_close_{nullptr};
bool map_opened{};
GeoPos geopos{
{0, 0},
altitude_unit_,

View File

@ -31,7 +31,7 @@
#include "chprintf.h"
#include "irq_controls.hpp"
#include "string_format.hpp"
#include "usb_serial_device_to_host.h"
#include "usb_serial_io.h"
using namespace portapack;
@ -2118,7 +2118,15 @@ bool NumberField::on_key(const KeyEvent key) {
}
bool NumberField::on_encoder(const EncoderEvent delta) {
int32_t old_value = value();
set_value(value() + (delta * step));
if (on_wrap) {
if ((delta > 0) && (value() < old_value))
on_wrap(1);
else if ((delta < 0) && (value() > old_value))
on_wrap(-1);
}
return true;
}

View File

@ -783,6 +783,7 @@ class NumberField : public Widget {
public:
std::function<void(NumberField&)> on_select{};
std::function<void(int32_t)> on_change{};
std::function<void(int32_t)> on_wrap{};
using range_t = std::pair<int32_t, int32_t>;