This commit is contained in:
2025-11-27 13:56:41 +01:00
parent a69daad6ef
commit cf88a953f2
9 changed files with 99 additions and 16 deletions

3
.env
View File

@@ -16,4 +16,5 @@ CLIENT_PORT=8080
SERVICE_LOG_LEVEL=debug SERVICE_LOG_LEVEL=debug
OSM_PBF_PATH=europe-latest.osm.pbf HOST_PBF_PATH=./oberbayern-251125.osm.pbf
HOST_CACHE_DIR=./cache

1
.gitignore vendored
View File

@@ -5,3 +5,4 @@ pkg/
node_modules/ node_modules/
.DS_Store .DS_Store
*.pbf *.pbf
docker-compose-remote.yml

7
Cargo.lock generated
View File

@@ -576,6 +576,12 @@ dependencies = [
"libloading 0.8.9", "libloading 0.8.9",
] ]
[[package]]
name = "dotenv"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
[[package]] [[package]]
name = "earcutr" name = "earcutr"
version = "0.4.3" version = "0.4.3"
@@ -1292,6 +1298,7 @@ name = "importer"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"dotenv",
"memmap2 0.9.9", "memmap2 0.9.9",
"osmpbf", "osmpbf",
"scylla", "scylla",

View File

@@ -4,10 +4,6 @@ services:
scylla: scylla:
image: scylladb/scylla:latest image: scylladb/scylla:latest
container_name: scylla container_name: scylla
ports:
- "9042:9042"
- "9160:9160"
- "10000:10000"
command: --smp 1 --memory 2G --overprovisioned 1 --api-address 0.0.0.0 --max-memory-for-unlimited-query-soft-limit 1073741824 --tombstone-warn-threshold 10000000 command: --smp 1 --memory 2G --overprovisioned 1 --api-address 0.0.0.0 --max-memory-for-unlimited-query-soft-limit 1073741824 --tombstone-warn-threshold 10000000
volumes: volumes:
- scylla_data:/var/lib/scylla - scylla_data:/var/lib/scylla
@@ -29,8 +25,8 @@ services:
target: importer target: importer
container_name: map-importer container_name: map-importer
volumes: volumes:
- ./oberbayern-251125.osm.pbf:/app/data.osm.pbf - ${HOST_PBF_PATH:-./europe-latest.osm.pbf}:/app/data.osm.pbf
- importer_cache:/cache - ${HOST_CACHE_DIR:-./cache}:/cache
environment: environment:
- SCYLLA_URI=scylla:9042 - SCYLLA_URI=scylla:9042
- OSM_PBF_PATH=/app/data.osm.pbf - OSM_PBF_PATH=/app/data.osm.pbf
@@ -42,4 +38,3 @@ services:
volumes: volumes:
scylla_data: scylla_data:
importer_cache:

View File

@@ -32,6 +32,7 @@ web-sys = { version = "0.3", features = [
"RequestInit", "RequestInit",
"RequestMode", "RequestMode",
"Response", "Response",
"HtmlInputElement",
] } ] }
wgpu = { version = "0.19", default-features = false, features = ["webgl", "wgsl"] } wgpu = { version = "0.19", default-features = false, features = ["webgl", "wgsl"] }
winit = { version = "0.29", default-features = false, features = ["rwh_06"] } winit = { version = "0.29", default-features = false, features = ["rwh_06"] }

View File

@@ -363,24 +363,70 @@ pub async fn run() {
user_location: None, user_location: None,
})); }));
// Zoom constants
const MIN_ZOOM: f32 = 20.0;
const MAX_ZOOM: f32 = 50000.0;
// Helper to convert slider (0-100) to zoom (logarithmic)
let slider_to_zoom = |val: f64| -> f32 {
let t = val / 100.0;
let log_min = MIN_ZOOM.ln();
let log_max = MAX_ZOOM.ln();
let log_zoom = log_min + (log_max - log_min) * t as f32;
log_zoom.exp()
};
// Helper to convert zoom to slider (0-100)
let zoom_to_slider = |zoom: f32| -> f64 {
let log_min = MIN_ZOOM.ln();
let log_max = MAX_ZOOM.ln();
let log_zoom = zoom.ln();
let t = (log_zoom - log_min) / (log_max - log_min);
(t * 100.0) as f64
};
let window_doc = web_sys::window().unwrap().document().unwrap(); let window_doc = web_sys::window().unwrap().document().unwrap();
// Slider
let camera_clone = camera.clone(); let camera_clone = camera.clone();
let window_clone = window.clone(); let window_clone = window.clone();
let slider = window_doc.get_element_by_id("zoom-slider")
.and_then(|e| e.dyn_into::<web_sys::HtmlInputElement>().ok());
if let Some(slider) = slider {
let closure = wasm_bindgen::closure::Closure::<dyn FnMut(web_sys::Event)>::new(move |event: web_sys::Event| {
let target = event.target().unwrap();
let input = target.dyn_into::<web_sys::HtmlInputElement>().unwrap();
let val = input.value().parse::<f64>().unwrap_or(50.0);
let new_zoom = slider_to_zoom(val);
let mut cam = camera_clone.lock().unwrap();
cam.zoom = new_zoom;
window_clone.request_redraw();
});
slider.add_event_listener_with_callback("input", closure.as_ref().unchecked_ref()).unwrap();
closure.forget();
}
// Zoom In Button
let camera_clone = camera.clone();
let window_clone = window.clone();
let btn_zoom_in = window_doc.get_element_by_id("btn-zoom-in") let btn_zoom_in = window_doc.get_element_by_id("btn-zoom-in")
.and_then(|e| e.dyn_into::<web_sys::HtmlButtonElement>().ok()); .and_then(|e| e.dyn_into::<web_sys::HtmlButtonElement>().ok());
if let Some(btn) = btn_zoom_in { if let Some(btn) = btn_zoom_in {
let closure = wasm_bindgen::closure::Closure::<dyn FnMut()>::new(move || { let closure = wasm_bindgen::closure::Closure::<dyn FnMut()>::new(move || {
let mut cam = camera_clone.lock().unwrap(); let mut cam = camera_clone.lock().unwrap();
cam.zoom *= 1.5; let current_slider = zoom_to_slider(cam.zoom);
cam.zoom = cam.zoom.max(20.0).min(50000.0); let new_slider = (current_slider + 5.0).min(100.0); // +5% step
cam.zoom = slider_to_zoom(new_slider);
window_clone.request_redraw(); window_clone.request_redraw();
}); });
btn.set_onclick(Some(closure.as_ref().unchecked_ref())); btn.set_onclick(Some(closure.as_ref().unchecked_ref()));
closure.forget(); closure.forget();
} }
// Zoom Out Button
let camera_clone = camera.clone(); let camera_clone = camera.clone();
let window_clone = window.clone(); let window_clone = window.clone();
let btn_zoom_out = window_doc.get_element_by_id("btn-zoom-out") let btn_zoom_out = window_doc.get_element_by_id("btn-zoom-out")
@@ -389,8 +435,9 @@ pub async fn run() {
if let Some(btn) = btn_zoom_out { if let Some(btn) = btn_zoom_out {
let closure = wasm_bindgen::closure::Closure::<dyn FnMut()>::new(move || { let closure = wasm_bindgen::closure::Closure::<dyn FnMut()>::new(move || {
let mut cam = camera_clone.lock().unwrap(); let mut cam = camera_clone.lock().unwrap();
cam.zoom /= 1.5; let current_slider = zoom_to_slider(cam.zoom);
cam.zoom = cam.zoom.max(20.0).min(50000.0); let new_slider = (current_slider - 5.0).max(0.0); // -5% step
cam.zoom = slider_to_zoom(new_slider);
window_clone.request_redraw(); window_clone.request_redraw();
}); });
btn.set_onclick(Some(closure.as_ref().unchecked_ref())); btn.set_onclick(Some(closure.as_ref().unchecked_ref()));
@@ -538,6 +585,23 @@ pub async fn run() {
if let Some(el) = window_doc.get_element_by_id("debug-zoom") { if let Some(el) = window_doc.get_element_by_id("debug-zoom") {
el.set_inner_html(&format!("{:.1}", cam.zoom)); el.set_inner_html(&format!("{:.1}", cam.zoom));
} }
// Sync slider
if let Some(slider) = window_doc.get_element_by_id("zoom-slider").and_then(|e| e.dyn_into::<web_sys::HtmlInputElement>().ok()) {
let min_zoom: f32 = 20.0;
let max_zoom: f32 = 50000.0;
let log_min = min_zoom.ln();
let log_max = max_zoom.ln();
let log_zoom = cam.zoom.ln();
let t = (log_zoom - log_min) / (log_max - log_min);
let val = (t * 100.0) as f64;
// Only update if not dragging (optional, but good for UX? actually input event handles drag)
// But if we update while dragging it might fight.
// However, for scroll wheel we need this.
// Let's just update it. The browser handles the active state.
slider.set_value(&val.to_string());
}
if let Some(el) = window_doc.get_element_by_id("debug-pos") { if let Some(el) = window_doc.get_element_by_id("debug-pos") {
el.set_inner_html(&format!("{:.4}, {:.4}", cam.y, cam.x)); // Lat, Lon approx el.set_inner_html(&format!("{:.4}, {:.4}", cam.y, cam.x)); // Lat, Lon approx
} }

View File

@@ -9,3 +9,4 @@ scylla = "0.12"
tokio = { version = "1.0", features = ["full"] } tokio = { version = "1.0", features = ["full"] }
anyhow = "1.0" anyhow = "1.0"
memmap2 = "0.9" memmap2 = "0.9"
dotenv = "0.15"

View File

@@ -124,6 +124,9 @@ fn should_include(tags: &HashMap<String, String>, zoom: u32) -> bool {
#[tokio::main] #[tokio::main]
async fn main() -> Result<()> { async fn main() -> Result<()> {
// Load .env file if present
dotenv::dotenv().ok();
// Connect to ScyllaDB // Connect to ScyllaDB
let uri = std::env::var("SCYLLA_URI").unwrap_or_else(|_| "127.0.0.1:9042".to_string()); let uri = std::env::var("SCYLLA_URI").unwrap_or_else(|_| "127.0.0.1:9042".to_string());
println!("Connecting to ScyllaDB at {}...", uri); println!("Connecting to ScyllaDB at {}...", uri);
@@ -160,7 +163,9 @@ async fn main() -> Result<()> {
let insert_railways = session.prepare("INSERT INTO map_data.railways (zoom, tile_x, tile_y, id, tags, points) VALUES (?, ?, ?, ?, ?, ?)").await?; let insert_railways = session.prepare("INSERT INTO map_data.railways (zoom, tile_x, tile_y, id, tags, points) VALUES (?, ?, ?, ?, ?, ?)").await?;
println!("Statements prepared."); println!("Statements prepared.");
let path = std::env::var("OSM_PBF_PATH").unwrap_or_else(|_| "europe-latest.osm.pbf".to_string()); let path = std::env::var("OSM_PBF_PATH")
.or_else(|_| std::env::var("HOST_PBF_PATH"))
.unwrap_or_else(|_| "europe-latest.osm.pbf".to_string());
println!("Reading {}...", path); println!("Reading {}...", path);
let reader = ElementReader::from_path(path)?; let reader = ElementReader::from_path(path)?;

8
run.sh
View File

@@ -1 +1,9 @@
#!/bin/bash
# Load .env variables
if [ -f .env ]; then
export $(cat .env | grep -v '#' | awk '/=/ {print $1}')
fi
echo "Using PBF file: ${HOST_PBF_PATH:-./europe-latest.osm.pbf}"
docker compose --profile import up --build importer docker compose --profile import up --build importer
docker compose build --no-cache