update
This commit is contained in:
2
.env
2
.env
@@ -16,5 +16,5 @@ CLIENT_PORT=8080
|
|||||||
|
|
||||||
SERVICE_LOG_LEVEL=debug
|
SERVICE_LOG_LEVEL=debug
|
||||||
|
|
||||||
HOST_PBF_PATH=../maps_data/bayern-latest.osm.pbf
|
HOST_PBF_PATH=../maps_data/europe-latest.osm.pbf
|
||||||
HOST_CACHE_DIR=./cache
|
HOST_CACHE_DIR=./cache
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
version: '3'
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
scylla:
|
scylla:
|
||||||
image: scylladb/scylla:latest
|
image: scylladb/scylla:latest
|
||||||
|
|||||||
@@ -162,11 +162,12 @@
|
|||||||
.label {
|
.label {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
color: white;
|
color: #333;
|
||||||
text-shadow: 0 0 2px black, 0 0 4px black;
|
text-shadow: 0 0 3px white, 0 0 3px white, 0 0 3px white;
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.label-country {
|
.label-country {
|
||||||
@@ -174,13 +175,13 @@
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 1px;
|
letter-spacing: 1px;
|
||||||
color: #ffdddd;
|
color: #222;
|
||||||
}
|
}
|
||||||
|
|
||||||
.label-city {
|
.label-city {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: normal;
|
font-weight: 600;
|
||||||
color: #ffffff;
|
color: #333;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
@@ -201,7 +202,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="module">
|
<script type="module">
|
||||||
import init from './wasm.js';
|
import init from './wasm.js?v=fixed_labels_v6';
|
||||||
|
|
||||||
async function run() {
|
async function run() {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1190,7 +1190,11 @@ pub async fn run() {
|
|||||||
// Let's stick to passing `&Camera` and let it call `to_uniform`.
|
// Let's stick to passing `&Camera` and let it call `to_uniform`.
|
||||||
// But `to_uniform` is a method on `Camera`.
|
// But `to_uniform` is a method on `Camera`.
|
||||||
// So:
|
// So:
|
||||||
update_labels(&web_sys::window().unwrap(), &cam, &state_guard, config.width as f64, config.height as f64);
|
// Debug scale factor once (or occasionally)
|
||||||
|
if state_guard.pending_tiles.is_empty() && state_guard.loaded_tiles.len() > 0 {
|
||||||
|
// web_sys::console::log_1(&format!("Scale Factor: {}", window.scale_factor()).into());
|
||||||
|
}
|
||||||
|
update_labels(&web_sys::window().unwrap(), &cam, &state_guard, config.width as f64, config.height as f64, window.scale_factor());
|
||||||
}
|
}
|
||||||
WindowEvent::CloseRequested => {
|
WindowEvent::CloseRequested => {
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
@@ -1843,12 +1847,21 @@ fn create_road_mesh_pipeline(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct LabelCandidate {
|
||||||
|
name: String,
|
||||||
|
x: f64,
|
||||||
|
y: f64,
|
||||||
|
priority: i32,
|
||||||
|
is_country: bool,
|
||||||
|
}
|
||||||
|
|
||||||
fn update_labels(
|
fn update_labels(
|
||||||
window: &web_sys::Window,
|
window: &web_sys::Window,
|
||||||
camera: &Camera,
|
camera: &Camera,
|
||||||
state: &AppState,
|
state: &AppState,
|
||||||
width: f64,
|
width: f64,
|
||||||
height: f64,
|
height: f64,
|
||||||
|
scale_factor: f64,
|
||||||
) {
|
) {
|
||||||
let document = window.document().unwrap();
|
let document = window.document().unwrap();
|
||||||
let container = document.get_element_by_id("labels").unwrap();
|
let container = document.get_element_by_id("labels").unwrap();
|
||||||
@@ -1856,11 +1869,11 @@ fn update_labels(
|
|||||||
// Clear existing labels
|
// Clear existing labels
|
||||||
container.set_inner_html("");
|
container.set_inner_html("");
|
||||||
|
|
||||||
let show_countries = true;
|
|
||||||
let show_cities = camera.zoom > 100.0;
|
|
||||||
|
|
||||||
let visible_tiles = get_visible_tiles(camera);
|
let visible_tiles = get_visible_tiles(camera);
|
||||||
let uniforms = camera.to_uniform(); // Calculate uniforms
|
let uniforms = camera.to_uniform();
|
||||||
|
|
||||||
|
let mut candidates: Vec<LabelCandidate> = Vec::new();
|
||||||
|
let zoom = camera.zoom;
|
||||||
|
|
||||||
for tile in visible_tiles {
|
for tile in visible_tiles {
|
||||||
if let Some(nodes) = state.nodes.get(&tile) {
|
if let Some(nodes) = state.nodes.get(&tile) {
|
||||||
@@ -1869,38 +1882,119 @@ fn update_labels(
|
|||||||
let name = node.tags.get("name").map(|s| s.as_str());
|
let name = node.tags.get("name").map(|s| s.as_str());
|
||||||
|
|
||||||
if let (Some(place), Some(name)) = (place, name) {
|
if let (Some(place), Some(name)) = (place, name) {
|
||||||
let is_country = place == "country";
|
// 1. Zoom Level Filtering
|
||||||
let is_city = place == "city" || place == "town";
|
let should_show = match place {
|
||||||
|
"continent" | "country" => true,
|
||||||
|
"city" => zoom > 20.0, // Show cities earlier
|
||||||
|
"town" => zoom > 500.0, // Show towns earlier
|
||||||
|
"village" | "hamlet" => zoom > 2000.0,
|
||||||
|
"suburb" => zoom > 5000.0,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
if (is_country && show_countries) || (is_city && show_cities) {
|
if !should_show { continue; }
|
||||||
let (x, y) = project(node.lat, node.lon);
|
|
||||||
|
|
||||||
// Apply camera transform using uniforms
|
// 2. Priority Calculation
|
||||||
let cx = x * uniforms.params[0] + uniforms.params[2];
|
let mut priority = match place {
|
||||||
let cy = y * uniforms.params[1] + uniforms.params[3];
|
"continent" => 1000,
|
||||||
|
"country" => 100,
|
||||||
|
"city" => 80,
|
||||||
|
"town" => 60,
|
||||||
|
"village" => 40,
|
||||||
|
"hamlet" => 20,
|
||||||
|
_ => 0,
|
||||||
|
};
|
||||||
|
|
||||||
let ndc_x = cx;
|
// Capital bonus
|
||||||
let ndc_y = cy;
|
if let Some(capital) = node.tags.get("capital") {
|
||||||
|
if capital == "yes" {
|
||||||
if ndc_x < -1.2 || ndc_x > 1.2 || ndc_y < -1.2 || ndc_y > 1.2 { continue; }
|
priority += 10;
|
||||||
|
}
|
||||||
let screen_x = (ndc_x as f64 + 1.0) * 0.5 * width;
|
|
||||||
let screen_y = (1.0 - ndc_y as f64) * 0.5 * height;
|
|
||||||
|
|
||||||
let div = document.create_element("div").unwrap();
|
|
||||||
let class_name = if is_country { "label label-country" } else { "label label-city" };
|
|
||||||
div.set_class_name(class_name);
|
|
||||||
div.set_text_content(Some(name));
|
|
||||||
|
|
||||||
let div_html: web_sys::HtmlElement = div.dyn_into().unwrap();
|
|
||||||
let style = div_html.style();
|
|
||||||
style.set_property("left", &format!("{}px", screen_x)).unwrap();
|
|
||||||
style.set_property("top", &format!("{}px", screen_y)).unwrap();
|
|
||||||
|
|
||||||
container.append_child(&div_html).unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Population bonus (logarithmic)
|
||||||
|
if let Some(pop_str) = node.tags.get("population") {
|
||||||
|
if let Ok(pop) = pop_str.parse::<f64>() {
|
||||||
|
priority += (pop.log10() * 2.0) as i32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Projection & Screen Coordinates
|
||||||
|
let (x, y) = project(node.lat, node.lon);
|
||||||
|
let cx = x * uniforms.params[0] + uniforms.params[2];
|
||||||
|
let cy = y * uniforms.params[1] + uniforms.params[3];
|
||||||
|
|
||||||
|
// Clip check (NDC)
|
||||||
|
if cx < -1.2 || cx > 1.2 || cy < -1.2 || cy > 1.2 { continue; }
|
||||||
|
|
||||||
|
// Direct NDC to CSS Pixel mapping
|
||||||
|
// This bypasses any confusion about physical vs logical pixels or scale factors.
|
||||||
|
// We map [-1, 1] directly to [0, client_width].
|
||||||
|
let client_width = window.inner_width().ok().and_then(|v| v.as_f64()).unwrap_or(width);
|
||||||
|
let client_height = window.inner_height().ok().and_then(|v| v.as_f64()).unwrap_or(height);
|
||||||
|
|
||||||
|
let css_x = (cx as f64 + 1.0) * 0.5 * client_width;
|
||||||
|
let css_y = (1.0 - cy as f64) * 0.5 * client_height;
|
||||||
|
|
||||||
|
candidates.push(LabelCandidate {
|
||||||
|
name: name.to_string(),
|
||||||
|
x: css_x,
|
||||||
|
y: css_y,
|
||||||
|
priority,
|
||||||
|
is_country: place == "country" || place == "continent",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 4. Sort by Priority (High to Low)
|
||||||
|
candidates.sort_by(|a, b| b.priority.cmp(&a.priority));
|
||||||
|
|
||||||
|
// 5. Collision Detection & Placement
|
||||||
|
let mut placed_rects: Vec<(f64, f64, f64, f64)> = Vec::new(); // (x, y, w, h)
|
||||||
|
|
||||||
|
for candidate in candidates {
|
||||||
|
// Estimate dimensions (approximate)
|
||||||
|
// Country labels are usually larger
|
||||||
|
let (est_w, est_h) = if candidate.is_country {
|
||||||
|
(candidate.name.len() as f64 * 12.0 + 20.0, 24.0)
|
||||||
|
} else {
|
||||||
|
(candidate.name.len() as f64 * 8.0 + 10.0, 16.0)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Centered label
|
||||||
|
let rect_x = candidate.x - est_w / 2.0;
|
||||||
|
let rect_y = candidate.y - est_h / 2.0;
|
||||||
|
|
||||||
|
// Check collision
|
||||||
|
let mut collision = false;
|
||||||
|
for (px, py, pw, ph) in &placed_rects {
|
||||||
|
// Simple AABB intersection with padding
|
||||||
|
let padding = 5.0; // Reduced padding
|
||||||
|
if rect_x < px + pw + padding &&
|
||||||
|
rect_x + est_w + padding > *px &&
|
||||||
|
rect_y < py + ph + padding &&
|
||||||
|
rect_y + est_h + padding > *py {
|
||||||
|
collision = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !collision {
|
||||||
|
placed_rects.push((rect_x, rect_y, est_w, est_h));
|
||||||
|
|
||||||
|
let div = document.create_element("div").unwrap();
|
||||||
|
let class_name = if candidate.is_country { "label label-country" } else { "label label-city" };
|
||||||
|
div.set_class_name(class_name);
|
||||||
|
div.set_text_content(Some(&candidate.name));
|
||||||
|
|
||||||
|
let div_html: web_sys::HtmlElement = div.dyn_into().unwrap();
|
||||||
|
let style = div_html.style();
|
||||||
|
style.set_property("left", &format!("{}px", candidate.x)).unwrap();
|
||||||
|
style.set_property("top", &format!("{}px", candidate.y)).unwrap();
|
||||||
|
|
||||||
|
container.append_child(&div_html).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user