diff --git a/backend/src/domain/way.rs b/backend/src/domain/way.rs index 48af313..b342e93 100644 --- a/backend/src/domain/way.rs +++ b/backend/src/domain/way.rs @@ -6,4 +6,5 @@ pub struct MapWay { pub id: i64, pub tags: HashMap, pub points: Vec, // Flat f32 array (lat, lon, lat, lon...) + pub vertex_buffer: Vec, // Precomputed GPU-ready vertex data } diff --git a/backend/src/repositories/way_repository.rs b/backend/src/repositories/way_repository.rs index a9ec851..7036bdc 100644 --- a/backend/src/repositories/way_repository.rs +++ b/backend/src/repositories/way_repository.rs @@ -27,10 +27,10 @@ impl WayRepository { let mut ways = Vec::with_capacity(rows.len()); for row in rows { - let (id, tags, points) = row.into_typed::<(i64, std::collections::HashMap, Vec)>() + let (id, tags, points, vertex_buffer) = row.into_typed::<(i64, std::collections::HashMap, Vec, Vec)>() .map_err(|e| Box::::from(format!("Serialization error: {}", e)))?; - ways.push(MapWay { id, tags, points }); + ways.push(MapWay { id, tags, points, vertex_buffer }); } Ok(ways) } @@ -39,29 +39,29 @@ impl WayRepository { #[async_trait::async_trait] impl WayRepositoryTrait for WayRepository { async fn find_ways_in_tile(&self, z: i32, x: i32, y: i32) -> Result, Box> { - let query = "SELECT id, tags, points FROM map_data.ways WHERE zoom = ? AND tile_x = ? AND tile_y = ? LIMIT 50000"; + let query = "SELECT id, tags, points, vertex_buffer FROM map_data.ways WHERE zoom = ? AND tile_x = ? AND tile_y = ? LIMIT 50000"; self.query_ways(query, z, x, y).await } async fn find_buildings_in_tile(&self, z: i32, x: i32, y: i32) -> Result, Box> { if z < 12 { return Ok(Vec::new()); } - let query = "SELECT id, tags, points FROM map_data.buildings WHERE zoom = ? AND tile_x = ? AND tile_y = ? LIMIT 50000"; + let query = "SELECT id, tags, points, vertex_buffer FROM map_data.buildings WHERE zoom = ? AND tile_x = ? AND tile_y = ? LIMIT 50000"; self.query_ways(query, z, x, y).await } async fn find_landuse_in_tile(&self, z: i32, x: i32, y: i32) -> Result, Box> { if z < 4 { return Ok(Vec::new()); } - let query = "SELECT id, tags, points FROM map_data.landuse WHERE zoom = ? AND tile_x = ? AND tile_y = ? LIMIT 50000"; + let query = "SELECT id, tags, points, vertex_buffer FROM map_data.landuse WHERE zoom = ? AND tile_x = ? AND tile_y = ? LIMIT 50000"; self.query_ways(query, z, x, y).await } async fn find_water_in_tile(&self, z: i32, x: i32, y: i32) -> Result, Box> { - let query = "SELECT id, tags, points FROM map_data.water WHERE zoom = ? AND tile_x = ? AND tile_y = ? LIMIT 50000"; + let query = "SELECT id, tags, points, vertex_buffer FROM map_data.water WHERE zoom = ? AND tile_x = ? AND tile_y = ? LIMIT 50000"; self.query_ways(query, z, x, y).await } async fn find_railways_in_tile(&self, z: i32, x: i32, y: i32) -> Result, Box> { - let query = "SELECT id, tags, points FROM map_data.railways WHERE zoom = ? AND tile_x = ? AND tile_y = ? LIMIT 50000"; + let query = "SELECT id, tags, points, vertex_buffer FROM map_data.railways WHERE zoom = ? AND tile_x = ? AND tile_y = ? LIMIT 50000"; self.query_ways(query, z, x, y).await } } diff --git a/frontend/Cargo.toml b/frontend/Cargo.toml index 218f808..4246a2a 100644 --- a/frontend/Cargo.toml +++ b/frontend/Cargo.toml @@ -37,6 +37,7 @@ web-sys = { version = "0.3", features = [ "PositionOptions", "DomTokenList", "CssStyleDeclaration", + "Performance", ] } wgpu = { version = "0.19", default-features = false, features = ["webgl", "wgsl"] } winit = { version = "0.29", default-features = false, features = ["rwh_06"] } @@ -48,3 +49,4 @@ reqwest = { version = "0.11", features = ["json"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" bincode = "1.3" +earcutr = "0.4" diff --git a/frontend/index.html b/frontend/index.html index 78fc05a..cc987b8 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -758,6 +758,69 @@ border-color: rgba(255, 255, 255, 0.2); box-shadow: 0 1px 4px rgba(0, 0, 0, 0.5); } + + /* ======================================== + TRANSIT LINE LABELS (S-Bahn, U-Bahn) + Munich-style badges + ======================================== */ + .label-transit { + font-size: 11px; + font-weight: 700; + letter-spacing: 0.3px; + padding: 4px 8px; + border-radius: 4px; + background: white; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.25); + border: 2px solid currentColor; + text-shadow: none; + } + + /* S-Bahn - Green circle badge style */ + .label-transit-sbahn { + background: #408335; + color: white; + border: none; + border-radius: 50%; + width: 24px; + height: 24px; + padding: 0; + display: flex; + align-items: center; + justify-content: center; + font-size: 10px; + font-weight: 800; + } + + /* U-Bahn - Blue square badge style */ + .label-transit-ubahn { + background: #0065AE; + color: white; + border: none; + border-radius: 4px; + padding: 3px 6px; + font-size: 10px; + font-weight: 800; + } + + /* Generic rail - Gray */ + .label-transit-rail { + background: #666; + color: white; + border: none; + } + + /* Dark theme adjustments */ + [data-theme="dark"] .label-transit { + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5); + } + + [data-theme="dark"] .label-transit-sbahn { + background: #4a9c3d; + } + + [data-theme="dark"] .label-transit-ubahn { + background: #1a7bcb; + }