WIP: feat: use web mercator projection

This commit is contained in:
Matthieu Bessat 2024-07-30 18:00:53 +02:00
parent 0bb3795445
commit a8aefdc894
11 changed files with 399 additions and 29 deletions

232
Cargo.lock generated
View file

@ -55,6 +55,15 @@ version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
[[package]]
name = "approx"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6"
dependencies = [
"num-traits",
]
[[package]]
name = "arrayvec"
version = "0.5.2"
@ -67,6 +76,18 @@ version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
[[package]]
name = "as-slice"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45403b49e3954a4b8428a0ac21a4b7afadccf92bfd96273f1a58cd4812496ae0"
dependencies = [
"generic-array 0.12.4",
"generic-array 0.13.3",
"generic-array 0.14.7",
"stable_deref_trait",
]
[[package]]
name = "autocfg"
version = "1.3.0"
@ -79,13 +100,36 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bindgen"
version = "0.66.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2b84e06fc203107bfbad243f4aba2af864eb7db3b1cf46ea0a023b0b433d2a7"
dependencies = [
"bitflags 2.5.0",
"cexpr",
"clang-sys",
"lazy_static",
"lazycell",
"log",
"peeking_take_while",
"prettyplease",
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"shlex",
"syn 2.0.66",
"which",
]
[[package]]
name = "bindgen"
version = "0.69.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0"
dependencies = [
"bitflags",
"bitflags 2.5.0",
"cexpr",
"clang-sys",
"itertools",
@ -102,6 +146,12 @@ dependencies = [
"which",
]
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.5.0"
@ -126,13 +176,15 @@ version = "0.1.0"
dependencies = [
"anyhow",
"fully_pub",
"geo-types",
"native-tls",
"osm-types",
"osmpbf",
"proj",
"quick-xml",
"rand",
"raylib",
"rstar",
"rstar 0.12.0",
"serde",
"serde_json",
"serde_with",
@ -418,6 +470,18 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
[[package]]
name = "filetime"
version = "0.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd"
dependencies = [
"cfg-if 1.0.0",
"libc",
"redox_syscall 0.4.1",
"windows-sys",
]
[[package]]
name = "flate2"
version = "1.0.30"
@ -480,6 +544,46 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
[[package]]
name = "generic-array"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd"
dependencies = [
"typenum",
]
[[package]]
name = "generic-array"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f797e67af32588215eaaab8327027ee8e71b9dd0b2b26996aedf20c030fce309"
dependencies = [
"typenum",
]
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "geo-types"
version = "0.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ff16065e5720f376fbced200a5ae0f47ace85fd70b7e54269790281353b6d61"
dependencies = [
"approx",
"num-traits",
"rstar 0.8.4",
"serde",
]
[[package]]
name = "getrandom"
version = "0.2.15"
@ -497,6 +601,15 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "hash32"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4041af86e63ac4298ce40e5cca669066e75b6f1aa3390fe2561ffa5e1d9f4cc"
dependencies = [
"byteorder",
]
[[package]]
name = "hash32"
version = "0.3.1"
@ -531,13 +644,25 @@ version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
[[package]]
name = "heapless"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "634bd4d29cbf24424d0a4bfcbf80c6960129dc24424752a7d1d1390607023422"
dependencies = [
"as-slice",
"generic-array 0.14.7",
"hash32 0.1.1",
"stable_deref_trait",
]
[[package]]
name = "heapless"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
dependencies = [
"hash32",
"hash32 0.3.1",
"stable_deref_trait",
]
@ -809,7 +934,7 @@ version = "0.10.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f"
dependencies = [
"bitflags",
"bitflags 2.5.0",
"cfg-if 1.0.0",
"foreign-types",
"libc",
@ -891,11 +1016,23 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
dependencies = [
"cfg-if 1.0.0",
"libc",
"redox_syscall",
"redox_syscall 0.5.1",
"smallvec",
"windows-targets",
]
[[package]]
name = "pdqselect"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ec91767ecc0a0bbe558ce8c9da33c068066c57ecc8bb8477ef8c1ad3ef77c27"
[[package]]
name = "peeking_take_while"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
[[package]]
name = "percent-encoding"
version = "2.3.1"
@ -971,6 +1108,32 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "proj"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ad1830ad8966eba22c76e78440458f07bd812bef5c3efdf335dec55cd1085ab"
dependencies = [
"geo-types",
"libc",
"num-traits",
"proj-sys",
"thiserror",
]
[[package]]
name = "proj-sys"
version = "0.23.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "601bf4fa1e17fde1a56d303f7bed5c65969cf1822c6baf5d6c2c12c593638fec"
dependencies = [
"bindgen 0.66.1",
"cmake",
"flate2",
"pkg-config",
"tar",
]
[[package]]
name = "protobuf"
version = "3.4.0"
@ -1118,7 +1281,7 @@ version = "5.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecc8ee1bd66c8383858fc46b4846589656a18ae54f4e0cf84a242b0ec723fa37"
dependencies = [
"bindgen",
"bindgen 0.69.4",
"cc",
"cmake",
"fs_extra",
@ -1144,13 +1307,22 @@ dependencies = [
"crossbeam-utils 0.8.20",
]
[[package]]
name = "redox_syscall"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "redox_syscall"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e"
dependencies = [
"bitflags",
"bitflags 2.5.0",
]
[[package]]
@ -1235,13 +1407,25 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "rstar"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a45c0e8804d37e4d97e55c6f258bc9ad9c5ee7b07437009dd152d764949a27c"
dependencies = [
"heapless 0.6.1",
"num-traits",
"pdqselect",
"smallvec",
]
[[package]]
name = "rstar"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "133315eb94c7b1e8d0cb097e5a710d850263372fd028fff18969de708afc7008"
dependencies = [
"heapless",
"heapless 0.8.0",
"num-traits",
"smallvec",
]
@ -1274,7 +1458,7 @@ version = "0.38.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
dependencies = [
"bitflags",
"bitflags 2.5.0",
"errno",
"libc",
"linux-raw-sys",
@ -1368,7 +1552,7 @@ version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0"
dependencies = [
"bitflags",
"bitflags 2.5.0",
"core-foundation",
"core-foundation-sys",
"libc",
@ -1599,6 +1783,17 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
name = "tar"
version = "0.4.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb797dad5fb5b76fcf519e702f4a589483b5ef06567f160c392832c1f5e44909"
dependencies = [
"filetime",
"libc",
"xattr",
]
[[package]]
name = "tempfile"
version = "3.10.1"
@ -1737,6 +1932,12 @@ dependencies = [
"nom",
]
[[package]]
name = "typenum"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "unicode-bidi"
version = "0.3.15"
@ -2026,6 +2227,17 @@ dependencies = [
"tap",
]
[[package]]
name = "xattr"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f"
dependencies = [
"libc",
"linux-raw-sys",
"rustix",
]
[[package]]
name = "xdg"
version = "2.5.2"

View file

@ -6,9 +6,11 @@ edition = "2021"
[dependencies]
anyhow = "1.0.86"
fully_pub = "0.1.4"
geo-types = { version = "0.7.13", features = ["approx", "rstar", "serde"] }
native-tls = "0.2.12"
osm-types = "0.1.5"
osmpbf = "0.3.3"
proj = "0.27.2"
quick-xml = { version = "0.34.0", features = ["serialize"] }
rand = "0.8.5"
raylib = { version = "5.0.1", features = ["wayland"] }

View file

@ -19,3 +19,29 @@
- Key sequence feature
## How projection need to work
WGS84 (EPSG:4326) -> Web mercator (EPSG:3857) -> Screen coord
The camera will be always represented using WebMercator coordinates.
So the only thing is when we ingest the data elements, in layers, we will convert WGS to mercator.
Lexique
WGS => WGS84 EPSG:4326
WM (web mercator) => EPSG:3857
Convert from WGS84 to WM
https://wiki.openstreetmap.org/wiki/Mercator#Rust
https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames
## Reorganize lib
Separate UI from Canvas?
Canvas:
UI:

View file

@ -19,6 +19,11 @@
- [x] 2024-07-01: add success or error messages buffer
- [x] 2024-07-01: refacto with clippy
- [x] refacto renderer
- [ ] Real projection
- [ ] Slippy Map & TMS raster service
- [ ] WMS raster service
- [ ] compute the real zoom
- [ ] hint key to select nodes
- [ ] vim-like commands sequence

View file

@ -1,5 +1,6 @@
pub mod changeset;
pub mod session;
pub mod primitives;
use fully_pub::fully_pub;
use std::collections::HashMap;
@ -74,6 +75,7 @@ impl Data {
data
}
/// add the current data to an indexed data datastruct
pub fn update_existing_indexed_data(&self, indexed_data: &mut IndexedData) {
for e in &self.elements {
let id = match e {

57
src/data/primitives.rs Normal file
View file

@ -0,0 +1,57 @@
use fully_pub::fully_pub;
#[derive(Debug, Clone)]
#[fully_pub]
/// Point ellipsoid coordinate in radians
pub struct Point {
/// Latitude (Y)
y: f64,
/// Longitude (X)
x: f64
}
// we define wrapper around Point
/// WGS 84 EPSG:4326
#[derive(Debug, Clone)]
pub struct WGSPoint(pub Point);
/// Web Mercator EPSG:3857
#[derive(Debug, Clone)]
pub struct WMPoint(pub Point);
/// convert WGS84 to Web Mercator
impl From<WGSPoint> for WMPoint {
fn from(point: WGSPoint) -> WMPoint {
let (lon_rad, lat_rad) = (point.0.x, point.0.y);
WMPoint(Point {
x: lon_rad,
y: lat_rad.tan().asinh()
})
}
}
/// convert Web Mercator to WGS84
impl From<WMPoint> for WGSPoint {
fn from(point: WMPoint) -> WGSPoint {
let (lon_rad, lat_rad) = (point.0.x, point.0.y);
WGSPoint(Point {
x: lon_rad,
y: lat_rad.sinh().atan()
})
}
}
impl Point {
pub fn mean(self: &Point, aux: &Point) -> Point {
Point {
x: self.x + (aux.x - self.x)/2.,
y: self.y + (aux.y - self.y)/2.
}
}
}
impl WMPoint {
pub fn mean(self: &WMPoint, aux: &WMPoint) -> WMPoint {
WMPoint(self.0.mean(&aux.0))
}
}

View file

@ -283,6 +283,7 @@ pub fn load_osm_data(layers: Arc<Mutex<Vec<Layer>>>, osm_data: data::Data) -> Re
.map_err(|_e| anyhow!("Acquire mutation lock for layers"))?;
let layer_to_update: &mut Layer =
get_dynamic_data_layer_mut(&mut l).context("Did not find dynamic OSMData")?;
osm_data.update_existing_indexed_data(
layer_to_update
.data_source

View file

@ -1,3 +1,4 @@
mod sandbox;
mod data;
mod layers;
mod osm_api;
@ -125,11 +126,11 @@ fn main() -> Result<()> {
let mut screen_def: (i32, i32) = (rl.get_screen_width(), rl.get_screen_height());
let camera = {
// let target_geo: Vector2 = Vector2::new(1.33, 49.175); // aubevoye
let target_geo: Vector2 = Vector2::new(1.3355, 49.1761); // aubevoye boulangerie
// let target_geo: Vector2 = Vector2::new(1.33, 49.159); // gaillon
// let target_geo: Vector2 = Vector2::new(4.86101, 45.74803); // lyon univ3 tabac
let target_geo: Vector2 = Vector2::new(1.3355, 56.636936); // aubevoye boulangerie in EPSG:3857
// let target_geo: Vector2 = Vector2::new(1.33, 49.159); // gaillon
// let target_geo: Vector2 = Vector2::new(4.86101, 45.74803); // lyon univ3 tabac
let target_zoom: f32 = 15.;
let target_zoom: f32 = 0.9;
Camera {
// compute bounds from screen width and screen height
bounds: compute_bounds_from_center_zoom(screen_def, target_geo, target_zoom),

49
src/sandbox.rs Normal file
View file

@ -0,0 +1,49 @@
use geo_types::Point;
use proj::{Proj, Transform};
use std::f64::consts::PI;
/// longitude is X, latitude is Y
fn to_web_mercator(point: Point) -> Point {
let (lon_rad, lat_rad) = (point.x(), point.y());
let x_wm = lon_rad;
let y_wm = lat_rad.tan().asinh();
Point::new(x_wm, y_wm)
}
/// accept web mercator angle coordinate
/// get web mercator tile with Fragment position
fn to_web_mercator_tile(point: Point, zoom: i32) -> Point {
let (lon_rad, lat_rad) = (point.x(), point.y());
let x = 0.5 + lon_rad/(2.*PI);
let y = 0.5 - lat_rad/(2.*PI);
let n = (1 << zoom) as f64;
let (x_tile, y_tile) = ((x*n), (y*n));
Point::new(x_tile, y_tile)
}
pub fn sandbox() {
println!("Hey sandbox");
let wgs_to_wm = Proj::new_known_crs(&"WGS84", &"EPSG:3857", None).unwrap();
// lat is second
let point = Point::new(
139.7006_f64.to_radians(), 35.6590_f64.to_radians()
);
let convertion_res = point
.transformed(&wgs_to_wm)
.unwrap();
dbg!(convertion_res);
let point_wm = to_web_mercator(point);
let z = 18;
let tile_wm = to_web_mercator_tile(point_wm, z);
dbg!(tile_wm);
let url = format!("https://tile.openstreetmap.org/{}/{}/{}.png", z, tile_wm.x() as i64, tile_wm.y() as i64);
println!("{}", url);
let (x_f, y_f) = (tile_wm.x() - (tile_wm.x() as i64) as f64, tile_wm.y() - (tile_wm.y() as i64) as f64);
println!("{:?}", (x_f, y_f));
}

View file

@ -1,23 +1,25 @@
use fully_pub::fully_pub;
use raylib::math::Vector2;
use crate::data::primitives::WMPoint;
#[derive(Debug, Clone)]
#[fully_pub]
struct Camera {
bounds: (Vector2, Vector2),
center: Vector2,
bounds: (WMPoint, WMPoint),
center: WMPoint,
zoom: f32,
map_zoom: f32,
real_to_canvas_factor: f32,
canvas_to_real_factor: f32,
}
pub fn recompute_camera(screen_def: (i32, i32), bounds: (Vector2, Vector2)) -> Camera {
let zoom = 1.0 / (bounds.0.x - bounds.1.x).abs();
let real_to_canvas_factor = (screen_def.0 as f32) / (bounds.0.x - bounds.1.x).abs();
pub fn recompute_camera(screen_def: (i32, i32), new_bounds: (WMPoint, WMPoint)) -> Camera {
let zoom = 1.0 / (new_bounds.0.0.x - new_bounds.1.0.x).abs();
let real_to_canvas_factor = (screen_def.0 as f32) / (new_bounds.0.x - new_bounds.1.x).abs();
Camera {
bounds,
center: bounds.1.lerp(bounds.0, 0.5),
bounds: new_bounds,
center: new_bounds.0.mean(&new_bounds.1),
real_to_canvas_factor,
canvas_to_real_factor: 1.0 / real_to_canvas_factor,
zoom,

View file

@ -1,7 +1,7 @@
use raylib::{consts, math::Vector2};
use rstar::AABB;
use crate::Camera;
use crate::{data::primitives::{Point, WGSPoint, WMPoint}, Camera};
pub fn deg2num(lat_deg: f64, lon_deg: f64, zoom: u32) -> (u32, u32) {
// Web mercator projection
@ -40,7 +40,12 @@ pub trait CanvasPos {
impl CanvasPos for Vector2 {
fn to_canvas_pos(&self, camera: &Camera) -> Vector2 {
Vector2::new(self.x - camera.bounds.0.x, camera.bounds.1.y - self.y)
// asume that the input point is a WGS
let wm_point: WMPoint = WGSPoint(Point {
x: self.x as f64,
y: self.y as f64
}).into();
Vector2::new(wm_point.0.x as f32 - camera.bounds.0.x, camera.bounds.1.y - wm_point.0.y as f32)
.scale_by(camera.real_to_canvas_factor)
}
}
@ -75,14 +80,22 @@ impl ToAABB for Vector2 {
pub fn compute_bounds_from_center_zoom(
screen_def: (i32, i32),
target_center: Vector2,
target_center: WMPoint,
target_zoom: f32,
) -> (Vector2, Vector2) {
) -> (WMPoint, WMPoint) {
let f = 0.00004;
let half_width: f32 = f * (1.0 / target_zoom) * screen_def.0 as f32;
let half_height: f32 = f * (1.0 / target_zoom) * screen_def.1 as f32;
let half_width: f64 = f * (1.0 / target_zoom as f64) * screen_def.0 as f64;
let half_height: f64 = f * (1.0 / target_zoom as f64) * screen_def.1 as f64;
(
Vector2::new(target_center.x - half_width, target_center.y - half_height),
Vector2::new(target_center.x + half_width, target_center.y + half_height),
WGSPoint(Point { x: (target_center.0.x - half_width), y: (target_center.0.y - half_height) }).into(),
WGSPoint(Point { x: (target_center.0.x + half_width), y: (target_center.0.y + half_height) }).into(),
)
}
const C: f32 = 40075016.686;
pub fn get_horizontal_distance(latitude: f32, zoomlevel: f32) -> f32 {
// Stile / 256 = C ∙ cos(latitude) / 2 (zoomlevel + 8)
C * latitude.cos() / (2.0_f32.powf(zoomlevel))
}