refactor: camera
This commit is contained in:
parent
b998c43dcc
commit
e344fc8d42
79
Cargo.lock
generated
79
Cargo.lock
generated
|
@ -80,9 +80,12 @@ checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
|
|||
name = "bobosm_raylib_rs"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"osmpbf",
|
||||
"raylib",
|
||||
"rstar",
|
||||
"serde",
|
||||
"toml",
|
||||
"ureq",
|
||||
]
|
||||
|
||||
|
@ -201,6 +204,12 @@ version = "1.12.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.9"
|
||||
|
@ -284,6 +293,12 @@ version = "0.12.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||
|
||||
[[package]]
|
||||
name = "heapless"
|
||||
version = "0.8.0"
|
||||
|
@ -329,6 +344,16 @@ dependencies = [
|
|||
"hashbrown 0.12.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.14.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.12.1"
|
||||
|
@ -563,7 +588,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "96cb37955261126624a25b5e6bda40ae34cf3989d52a783087ca6091b29b5642"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indexmap",
|
||||
"indexmap 1.9.3",
|
||||
"log",
|
||||
"protobuf",
|
||||
"protobuf-support",
|
||||
|
@ -794,6 +819,15 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
|
@ -938,6 +972,40 @@ version = "0.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_edit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38"
|
||||
dependencies = [
|
||||
"indexmap 2.2.6",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tuple_utils"
|
||||
version = "0.3.0"
|
||||
|
@ -1110,6 +1178,15 @@ version = "0.52.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.6.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.8.1"
|
||||
|
|
|
@ -4,7 +4,10 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.86"
|
||||
osmpbf = "0.3.3"
|
||||
raylib = { version = "5.0.1", features = ["wayland"] }
|
||||
rstar = "0.12.0"
|
||||
serde = { version = "1.0.203", features = ["derive"] }
|
||||
toml = "0.8.14"
|
||||
ureq = { version = "2.9.7", features = ["json"] }
|
||||
|
|
3
TODO.md
3
TODO.md
|
@ -31,6 +31,9 @@
|
|||
|
||||
- Tags edit on `/tmp/tags.txt`, open a dynamic editor like vim on another windows and allow to edit tags this way
|
||||
|
||||
- Distances de deux points sur une sphère avec la formule de Haversine
|
||||
- https://fr.wikipedia.org/wiki/Formule_de_haversine
|
||||
|
||||
## Tile
|
||||
|
||||
https://a.tile.openstreetmap.org/15/16511/11224.png
|
||||
|
|
2
config.toml
Normal file
2
config.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
[static_layers]
|
||||
base_data_path = "./data/france_places_cities_towns.osm.pbf"
|
112
src/main.rs
112
src/main.rs
|
@ -1,8 +1,12 @@
|
|||
use std::time::Duration;
|
||||
use std::{fs, time::Duration};
|
||||
|
||||
use anyhow::{Context, Result, anyhow};
|
||||
|
||||
use raylib::prelude::*;
|
||||
use std::io::Read;
|
||||
use osmpbf::{ElementReader, Element};
|
||||
use rstar::{RTree, RTreeObject, AABB};
|
||||
use serde::Deserialize;
|
||||
use ureq::Agent;
|
||||
|
||||
fn deg2num(lat_deg: f64, lon_deg: f64, zoom: u32) -> (u32, u32) {
|
||||
|
@ -69,6 +73,17 @@ enum GetRasterError {
|
|||
CannotDecodeImage(String)
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct StaticLayers {
|
||||
base_data_path: String,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Config {
|
||||
static_layers: StaticLayers,
|
||||
}
|
||||
|
||||
|
||||
fn get_envelope_from_bounds(bounds: (Vector2, Vector2), margin_percent: f32) -> AABB<[f32; 2]> {
|
||||
let center = bounds.1.lerp(bounds.0, 0.5);
|
||||
|
@ -81,42 +96,56 @@ fn get_envelope_from_bounds(bounds: (Vector2, Vector2), margin_percent: f32) ->
|
|||
)
|
||||
}
|
||||
|
||||
fn get_raster_tile(http_agent: &Agent, geo_pos: Vector2, zoom_level: u32) -> Result<Image, GetRasterError> {
|
||||
fn get_raster_tile(http_agent: &Agent, geo_pos: Vector2, zoom_level: u32) -> Result<Image> {
|
||||
let projected_pos = deg2num(
|
||||
geo_pos.y.into(),
|
||||
geo_pos.x.into(),
|
||||
zoom_level
|
||||
);
|
||||
let response = http_agent.get(
|
||||
&format!("https://a.tile.openstreetmap.org/{}/{}/{}",
|
||||
&format!("https://a.tile.openstreetmap.org/{}/{}/{}.png",
|
||||
zoom_level,
|
||||
projected_pos.0,
|
||||
projected_pos.1
|
||||
)
|
||||
)
|
||||
.call()
|
||||
.map_err(|e| GetRasterError::HttpError(e))?;
|
||||
.context("Requesting the tile")?;
|
||||
|
||||
let len: usize = response.header("Content-Length")
|
||||
.unwrap()
|
||||
.parse()
|
||||
.map_err(|_e| GetRasterError::InvalidContentLength)?;
|
||||
let img_bytes: Vec<u8> = Vec::with_capacity(len);
|
||||
.map_err(|_e| anyhow!("Invalid content length"))?;
|
||||
|
||||
let mut img_bytes: Vec<u8> = Vec::with_capacity(len);
|
||||
let _ = response.into_reader()
|
||||
.take(10_000_000) // safety to limit overflow
|
||||
.read_to_end(&mut img_bytes)
|
||||
.context("Reading response body");
|
||||
|
||||
Image::load_image_from_mem(".png", &img_bytes)
|
||||
.map_err(|e| GetRasterError::CannotDecodeImage(e))
|
||||
.map_err(|e| anyhow!("Cannot decode PNG, {:?}", e))
|
||||
}
|
||||
|
||||
fn get_osm_data() {
|
||||
// curl -v https://api.openstreetmap.org/api/0.6/map.json -G -d "bbox=1.32495,49.16409,1.34585,49.18485" > map.json
|
||||
}
|
||||
|
||||
fn read_config() -> Result<Config> {
|
||||
let config_content =
|
||||
fs::read_to_string("./config.toml")
|
||||
.map_err(|_e| anyhow!("Could not read config file"))?;
|
||||
let config: Config = toml::from_str(&config_content)
|
||||
.map_err(|_e| anyhow!("Could not parse config file as toml"))?;
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
|
||||
fn main() {
|
||||
let http_agent: Agent = ureq::AgentBuilder::new()
|
||||
.user_agent("bobosm/0.1")
|
||||
.timeout_read(Duration::from_secs(5))
|
||||
.timeout_write(Duration::from_secs(5))
|
||||
.timeout_read(Duration::from_secs(6))
|
||||
.timeout_write(Duration::from_secs(6))
|
||||
.build();
|
||||
|
||||
let (mut rl, thread) = raylib::init()
|
||||
|
@ -125,9 +154,11 @@ fn main() {
|
|||
.title("BobOSM")
|
||||
.build();
|
||||
|
||||
let config = read_config()
|
||||
.context("Read config")?;
|
||||
|
||||
let reader = ElementReader::from_path("./data/france_places_cities_towns.osm.pbf")
|
||||
.expect("To read cities");
|
||||
let reader = ElementReader::from_path(config.static_layers.base_data_path)
|
||||
.context("Read static layers")?;
|
||||
|
||||
enum MoveState {
|
||||
Started { start_pos: Vector2 },
|
||||
|
@ -136,12 +167,10 @@ fn main() {
|
|||
|
||||
|
||||
let mut move_state = MoveState::Done;
|
||||
let current_geo_pos: Vector2 = (0.0, 0.0).into();
|
||||
let mut current_zoom: f32 = 10.0;
|
||||
|
||||
// first point is left bottom, second point is top right point
|
||||
// let mut current_bounds: (Vector2, Vector2) = ((-4.0, -4.0).into(), (4.0, 4.0).into());
|
||||
let mut current_bounds: (Vector2, Vector2) = ((-6., 37.0).into(), (12., 55.0).into());
|
||||
// let mut current_bounds: (Vector2, Vector2) = ((-6., 37.0).into(), (12., 55.0).into());
|
||||
|
||||
let mut points: Vec<LabeledPoint> = vec![];
|
||||
println!("==1");
|
||||
|
@ -201,11 +230,11 @@ fn main() {
|
|||
let mut tmp_state = 0;
|
||||
|
||||
let mut camera = Camera {
|
||||
bounds: (Vector2::zero(), Vector2::zero()),
|
||||
bounds: (Vector2::new(-6., 37.0), Vector2::new(12., 55.)),
|
||||
canvas_to_real_factor: 1.0,
|
||||
real_to_canvas_factor: 1.0,
|
||||
center: Vector2::zero(),
|
||||
zoom: 1.0,
|
||||
zoom: 10.0,
|
||||
map_zoom: 1.0
|
||||
};
|
||||
|
||||
|
@ -224,7 +253,7 @@ fn main() {
|
|||
|
||||
camera.center = camera.bounds.1.lerp(camera.bounds.0, 0.5);
|
||||
camera.real_to_canvas_factor = (rl.get_screen_width() as f32) / (camera.bounds.0.x - camera.bounds.1.x).abs();
|
||||
camera.canvas_to_real_factor = 1.0/camera.canvas_to_real_factor;
|
||||
camera.canvas_to_real_factor = 1.0/camera.real_to_canvas_factor;
|
||||
// println!("real_to_canvas_factor = {real_to_canvas_factor:?}; canvas_to_real_factor: {canvas_to_real_factor:?}");
|
||||
|
||||
// Handle zoom
|
||||
|
@ -236,7 +265,7 @@ fn main() {
|
|||
mouse_pos.invert_for_canvas().scale_by(camera.canvas_to_real_factor);
|
||||
|
||||
// update the bounds
|
||||
camera.bounds.0 = real_mouse_pos + (camera.bounds.0 - real_mouse_pos).scale_by(zoom_factor);
|
||||
camera.bounds.0 = real_mouse_pos + (camera.bounds.0 - real_mouse_pos).scale_by(zoom_factor);
|
||||
camera.bounds.1 = real_mouse_pos + (camera.bounds.1 - real_mouse_pos).scale_by(zoom_factor);
|
||||
camera.zoom = 1.0/(camera.bounds.0.x - camera.bounds.1.x).abs();
|
||||
camera.map_zoom = 4.680851064*camera.zoom;
|
||||
|
@ -251,12 +280,12 @@ fn main() {
|
|||
MoveState::Started { start_pos } => {
|
||||
// end of vector move
|
||||
let move_speed_factor = 0.01;
|
||||
let move_speed = move_speed_factor/current_zoom;
|
||||
let move_speed = move_speed_factor/camera.zoom;
|
||||
mouse_move = rl.get_mouse_delta()
|
||||
.scale_by(-move_speed)
|
||||
.invert_for_canvas();
|
||||
current_bounds.0 += mouse_move;
|
||||
current_bounds.1 += mouse_move;
|
||||
camera.bounds.0 += mouse_move;
|
||||
camera.bounds.1 += mouse_move;
|
||||
move_state = MoveState::Done;
|
||||
},
|
||||
MoveState::Done => {
|
||||
|
@ -265,53 +294,57 @@ fn main() {
|
|||
}
|
||||
}
|
||||
|
||||
let mut d = rl.begin_drawing(&thread);
|
||||
|
||||
d.clear_background(Color::BLACK);
|
||||
|
||||
let mut tmp_texture: Option<Texture2D> = None;
|
||||
if tmp_state == 0 {
|
||||
match get_raster_tile(&http_agent, camera.center, current_zoom.ceil() as u32) {
|
||||
match get_raster_tile(&http_agent, camera.center, camera.map_zoom.ceil() as u32) {
|
||||
Ok(img) => {
|
||||
let t = match d.load_texture_from_image(&thread, &img) {
|
||||
Ok(t) => t,
|
||||
tmp_texture = match rl.load_texture_from_image(&thread, &img) {
|
||||
Ok(t) => Some(t),
|
||||
Err(e) => {
|
||||
println!("ERROR, failed to load texture");
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
let canvas_pos = (LabeledPoint { pos: camera.center, ..Default::default() }).to_canvas_pos(&camera);
|
||||
d.draw_texture(t, canvas_pos.x.ceil() as i32, canvas_pos.y.ceil() as i32, Color::WHITE);
|
||||
},
|
||||
Err(e) => {
|
||||
println!("ERROR, failed to load raster tile {e:?}");
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
tmp_state = 1;
|
||||
}
|
||||
|
||||
d.draw_text(&format!("Zoom: {:?}, Bounds: {:?}", current_zoom, current_bounds), 13, 12, 15, Color::WHITE);
|
||||
let mut d = rl.begin_drawing(&thread);
|
||||
let canvas_pos = (LabeledPoint { pos: camera.center, ..Default::default() }).to_canvas_pos(&camera);
|
||||
if let Some(t) = tmp_texture {
|
||||
d.draw_texture(t, canvas_pos.x.ceil() as i32, canvas_pos.y.ceil() as i32, Color::WHITE);
|
||||
}
|
||||
|
||||
d.clear_background(Color::BLACK);
|
||||
d.draw_text(&format!("Zoom: {:?}, Bounds: {:?}", camera.zoom, camera.bounds), 13, 12, 15, Color::WHITE);
|
||||
d.draw_text(&format!("Mouse: {:?}", real_mouse_pos), 13, 30, 15, Color::WHITE);
|
||||
|
||||
// Repère A: Canvas
|
||||
// Repère B: Virtual canvas
|
||||
let current_bounds_aabb = get_envelope_from_bounds(current_bounds, 0.30);
|
||||
let current_bounds_aabb = get_envelope_from_bounds(camera.bounds, 0.30);
|
||||
// draw raster tiles
|
||||
|
||||
for point in tree.locate_in_envelope(¤t_bounds_aabb) {
|
||||
let canvas_pos = point.to_canvas_pos(&camera);
|
||||
let size = point.importance as f32 * (((1.)/(1.+ (-(0.4 * current_zoom - 4.)).exp() )) + 0.2);
|
||||
d.draw_circle_v(
|
||||
let size = point.importance as f32 * (((1.)/(1.+ (-(0.4 * camera.zoom - 4.)).exp() )) + 0.2);
|
||||
d.draw_poly_lines(
|
||||
canvas_pos,
|
||||
5,
|
||||
10.0*size,
|
||||
0.,
|
||||
match point.importance {
|
||||
2 => Color::GREEN,
|
||||
3 => Color::RED,
|
||||
_ => Color::WHITE,
|
||||
_ => Color::new(255, 255, 255, 100)
|
||||
}
|
||||
);
|
||||
|
||||
if (current_zoom <= 0.7 && point.importance >= 3) || current_zoom >= 0.7 {
|
||||
if (camera.zoom <= 0.9 && point.importance >= 3) || camera.zoom >= 0.9 {
|
||||
d.draw_text(
|
||||
&format!("{}", point.name),
|
||||
canvas_pos.x.ceil() as i32,
|
||||
|
@ -325,5 +358,6 @@ fn main() {
|
|||
|
||||
d.draw_circle_sector_lines(mouse_pos, 10.0, 0.0, 360.0, 1, Color::MAGENTA);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue