Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

position module #259

Merged
merged 2 commits into from
Feb 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion walkers/src/center.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use egui::{Response, Vec2};

use crate::{map::AdjustedPosition, mercator::Pixels, Position};
use crate::{
position::{AdjustedPosition, Pixels},
Position,
};

/// Position at the map's center. Initially, the map follows `my_position` argument which typically
/// is meant to be fed by a GPS sensor or other geo-localization method. If user drags the map,
Expand Down
4 changes: 3 additions & 1 deletion walkers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ pub mod extras;
mod io;
mod map;
mod mercator;
mod position;
pub mod sources;
mod tiles;
mod zoom;

pub use download::{HeaderValue, HttpOptions};
pub use map::{Map, MapMemory, Plugin, Projector};
pub use mercator::{lat_lon, lon_lat, screen_to_position, Position, TileId};
pub use mercator::{screen_to_position, TileId};
pub use position::{lat_lon, lon_lat, Position};
pub use tiles::{HttpTiles, Texture, TextureWithUv, Tiles};
pub use zoom::InvalidZoom;
49 changes: 2 additions & 47 deletions walkers/src/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ use egui::{Mesh, PointerButton, Rect, Response, Sense, Ui, UiBuilder, Vec2, Widg

use crate::{
center::Center,
mercator::{project, screen_to_position, tile_id, Pixels, PixelsExt, TileId},
mercator::{project, tile_id, TileId},
position::{AdjustedPosition, Pixels, PixelsExt},
tiles,
zoom::{InvalidZoom, Zoom},
Position, Tiles,
Expand Down Expand Up @@ -336,52 +337,6 @@ impl Widget for Map<'_, '_, '_> {
}
}

/// [`Position`] alone is not able to represent detached (e.g. after map gets dragged) position
/// due to insufficient accuracy.
#[derive(Debug, Clone, PartialEq)]
pub struct AdjustedPosition {
/// Base geographical position.
pub position: Position,

/// Offset in pixels.
pub offset: Pixels,
}

impl AdjustedPosition {
pub(crate) fn new(position: Position, offset: Pixels) -> Self {
Self { position, offset }
}

/// Calculate the real position, i.e. including the offset.
pub(crate) fn position(&self, zoom: f64) -> Position {
screen_to_position(project(self.position, zoom) - self.offset, zoom)
}

/// Recalculate `position` so that `offset` is zero.
pub(crate) fn zero_offset(self, zoom: f64) -> Self {
Self {
position: screen_to_position(project(self.position, zoom) - self.offset, zoom),
offset: Default::default(),
}
}

pub(crate) fn shift(self, offset: Vec2) -> Self {
Self {
position: self.position,
offset: self.offset + Pixels::new(offset.x as f64, offset.y as f64),
}
}
}

impl From<Position> for AdjustedPosition {
fn from(position: Position) -> Self {
Self {
position,
offset: Default::default(),
}
}
}

/// State of the map widget which must persist between frames.
#[derive(Debug, Default, Clone)]
pub struct MapMemory {
Expand Down
37 changes: 7 additions & 30 deletions walkers/src/mercator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,17 @@
//! <https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames>
//! <https://www.netzwolf.info/osm/tilebrowser.html?lat=51.157800&lon=6.865500&zoom=14>

use crate::{
lon_lat,
position::{Pixels, Position},
};
use std::f64::consts::PI;

// zoom level tile coverage number of tiles tile size(*) in degrees
// 0 1 tile 1 tile 360° x 170.1022°
// 1 2 × 2 tiles 4 tiles 180° x 85.0511°
// 2 4 × 4 tiles 16 tiles 90° x [variable]

/// Geographical position with latitude and longitude.
pub type Position = geo_types::Point;

/// Construct `Position` from latitude and longitude.
pub fn lat_lon(lat: f64, lon: f64) -> Position {
Position::new(lon, lat)
}

/// Construct `Position` from longitude and latitude. Note that it is common standard to write
/// coordinates starting with the latitude instead (e.g. `51.104465719934176, 17.075169894118684` is
/// the [Wrocław's zoo](https://zoo.wroclaw.pl/en/)).
pub fn lon_lat(lon: f64, lat: f64) -> Position {
Position::new(lon, lat)
}

/// Zoom specifies how many pixels are in the whole map. For example, zoom 0 means that the whole
/// map is just one 256x256 tile, zoom 1 means that it is 2x2 tiles, and so on.
pub(crate) fn total_pixels(zoom: f64) -> f64 {
Expand All @@ -36,21 +27,6 @@ pub fn total_tiles(zoom: u8) -> u32 {
/// Size of a single tile in pixels. Walkers uses 256px tiles as most of the tile sources do.
const TILE_SIZE: u32 = 256;

/// Location projected on the screen or an abstract bitmap.
pub type Pixels = geo_types::Point;

use std::f64::consts::PI;

pub trait PixelsExt {
fn to_vec2(&self) -> egui::Vec2;
}

impl PixelsExt for Pixels {
fn to_vec2(&self) -> egui::Vec2 {
egui::Vec2::new(self.x() as f32, self.y() as f32)
}
}

/// Project the position into the Mercator projection and normalize it to 0-1 range.
fn mercator_normalized(position: Position) -> (f64, f64) {
// Project into Mercator (cylindrical map projection).
Expand Down Expand Up @@ -160,6 +136,7 @@ pub fn screen_to_position(pixels: Pixels, zoom: f64) -> Position {
#[cfg(test)]
mod tests {
use super::*;
use crate::lat_lon;

#[test]
fn projecting_position_and_tile() {
Expand Down
78 changes: 78 additions & 0 deletions walkers/src/position.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//! Types and functions for working with positions.

use crate::{mercator::project, screen_to_position};
use egui::Vec2;

/// Geographical position with latitude and longitude.
pub type Position = geo_types::Point;

/// Construct `Position` from latitude and longitude.
pub fn lat_lon(lat: f64, lon: f64) -> Position {
Position::new(lon, lat)
}

/// Construct `Position` from longitude and latitude. Note that it is common standard to write
/// coordinates starting with the latitude instead (e.g. `51.104465719934176, 17.075169894118684` is
/// the [Wrocław's zoo](https://zoo.wroclaw.pl/en/)).
pub fn lon_lat(lon: f64, lat: f64) -> Position {
Position::new(lon, lat)
}

/// [`Position`] alone is not able to represent detached (e.g. after map gets dragged) position
/// due to insufficient accuracy.
#[derive(Debug, Clone, PartialEq)]
pub struct AdjustedPosition {
/// Base geographical position.
pub position: Position,

/// Offset in pixels.
pub offset: Pixels,
}

impl AdjustedPosition {
pub(crate) fn new(position: Position, offset: Pixels) -> Self {
Self { position, offset }
}

/// Calculate the real position, i.e. including the offset.
pub(crate) fn position(&self, zoom: f64) -> Position {
screen_to_position(project(self.position, zoom) - self.offset, zoom)
}

/// Recalculate `position` so that `offset` is zero.
pub(crate) fn zero_offset(self, zoom: f64) -> Self {
Self {
position: screen_to_position(project(self.position, zoom) - self.offset, zoom),
offset: Default::default(),
}
}

pub(crate) fn shift(self, offset: Vec2) -> Self {
Self {
position: self.position,
offset: self.offset + Pixels::new(offset.x as f64, offset.y as f64),
}
}
}

impl From<Position> for AdjustedPosition {
fn from(position: Position) -> Self {
Self {
position,
offset: Default::default(),
}
}
}

/// Location projected on the screen or an abstract bitmap.
pub type Pixels = geo_types::Point;

pub trait PixelsExt {
fn to_vec2(&self) -> egui::Vec2;
}

impl PixelsExt for Pixels {
fn to_vec2(&self) -> egui::Vec2 {
egui::Vec2::new(self.x() as f32, self.y() as f32)
}
}