This commit is contained in:
Dongho Kim
2025-12-14 02:25:03 +09:00
parent c9678f28ba
commit 169997eecd
6 changed files with 756 additions and 461 deletions

2
.env
View File

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

View File

@@ -1,5 +1,5 @@
# Build Frontend
FROM rust:latest as frontend-builder
FROM rust:latest AS frontend-builder
WORKDIR /app
COPY frontend ./frontend
COPY backend ./backend
@@ -11,7 +11,7 @@ RUN wasm-pack build --target web --out-name wasm --out-dir ../backend/static
RUN cp index.html ../backend/static/index.html
# Build Backend
FROM rust:latest as backend-builder
FROM rust:latest AS backend-builder
WORKDIR /app
COPY backend ./backend
COPY --from=frontend-builder /app/backend/static ./backend/static
@@ -19,14 +19,14 @@ WORKDIR /app/backend
RUN cargo build --release
# Build Importer
FROM rust:latest as importer-builder
FROM rust:latest AS importer-builder
WORKDIR /app
COPY importer ./importer
WORKDIR /app/importer
RUN cargo build --release
# Backend Runtime
FROM debian:forky-slim as backend
FROM debian:forky-slim AS backend
WORKDIR /app
COPY --from=backend-builder /app/backend/target/release/backend ./backend
COPY --from=frontend-builder /app/backend/static ./static
@@ -37,7 +37,7 @@ EXPOSE 3000
CMD ["./backend"]
# Importer Runtime
FROM debian:forky-slim as importer
FROM debian:forky-slim AS importer
WORKDIR /app
COPY --from=importer-builder /app/importer/target/release/importer ./importer
# Install ca-certificates for HTTPS if needed

View File

@@ -1,5 +1,4 @@
use scylla::{Session, SessionBuilder};
use std::sync::Arc;
use scylla::Session;
pub async fn initialize_schema(session: &Session) -> Result<(), Box<dyn std::error::Error>> {
// Create keyspace

View File

@@ -183,6 +183,32 @@
font-weight: 600;
color: #333;
}
/* Apple Maps-style street labels */
.label-street {
font-size: 10px;
font-weight: 400;
color: #555;
letter-spacing: 0.3px;
text-shadow:
0 0 2px rgba(255, 255, 255, 0.9),
0 0 4px rgba(255, 255, 255, 0.8),
1px 1px 1px rgba(255, 255, 255, 0.95);
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", sans-serif;
}
/* Apple Maps-style POI labels */
.label-poi {
font-size: 11px;
font-weight: 500;
color: #c44;
letter-spacing: 0.2px;
text-shadow:
0 0 3px rgba(255, 255, 255, 0.95),
0 0 5px rgba(255, 255, 255, 0.9),
1px 1px 2px rgba(255, 255, 255, 1);
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", sans-serif;
}
</style>
<div id="ui-container">

File diff suppressed because it is too large Load Diff

View File

@@ -165,8 +165,8 @@ fn should_include(tags: &HashMap<String, String>, zoom: u32) -> bool {
2 => {
// Space View: Continents and Countries
matches!(place, Some("continent" | "country")) ||
matches!(natural, Some("water")) || // Major water bodies
matches!(place, Some("continent" | "country" | "sea" | "ocean")) ||
matches!(natural, Some("water" | "bay" | "strait")) || // Major water bodies
matches!(highway, Some("motorway")) || // Added motorway
matches!(tags.get("landuse").map(|s| s.as_str()), Some("forest" | "grass" | "meadow" | "farmland" | "residential")) || // Added more green + farmland/residential
matches!(tags.get("leisure").map(|s| s.as_str()), Some("park" | "nature_reserve")) || // Added parks
@@ -175,8 +175,8 @@ fn should_include(tags: &HashMap<String, String>, zoom: u32) -> bool {
4 => {
// Regional View (NEW)
matches!(highway, Some("motorway" | "trunk")) ||
matches!(place, Some("city" | "town")) ||
matches!(natural, Some("water" | "wood" | "scrub" | "heath" | "wetland")) ||
matches!(place, Some("city" | "town" | "sea" | "ocean")) ||
matches!(natural, Some("water" | "wood" | "scrub" | "heath" | "wetland" | "bay" | "strait")) ||
matches!(tags.get("landuse").map(|s| s.as_str()), Some("forest" | "grass" | "meadow" | "farmland" | "residential")) ||
matches!(tags.get("leisure").map(|s| s.as_str()), Some("park" | "nature_reserve")) ||
matches!(waterway, Some("river"))
@@ -185,8 +185,8 @@ fn should_include(tags: &HashMap<String, String>, zoom: u32) -> bool {
// Enterprise Grade: ONLY Motorways and Trunk roads. No primary/secondary.
// ONLY Cities. No nature/landuse.
matches!(highway, Some("motorway" | "trunk" | "primary")) || // Added primary
matches!(place, Some("city")) ||
matches!(natural, Some("water" | "wood" | "scrub" | "heath" | "wetland")) ||
matches!(place, Some("city" | "sea" | "ocean")) ||
matches!(natural, Some("water" | "wood" | "scrub" | "heath" | "wetland" | "bay" | "strait")) ||
matches!(tags.get("landuse").map(|s| s.as_str()), Some("forest" | "grass" | "meadow" | "farmland" | "residential")) ||
matches!(tags.get("leisure").map(|s| s.as_str()), Some("park" | "nature_reserve")) ||
matches!(waterway, Some("river"))
@@ -196,9 +196,9 @@ fn should_include(tags: &HashMap<String, String>, zoom: u32) -> bool {
// Add Towns.
// Limited nature.
matches!(highway, Some("motorway" | "trunk" | "primary")) ||
matches!(place, Some("city" | "town")) ||
matches!(place, Some("city" | "town" | "sea" | "ocean")) ||
matches!(railway, Some("rail")) ||
matches!(natural, Some("water" | "wood")) ||
matches!(natural, Some("water" | "wood" | "bay" | "strait")) ||
matches!(tags.get("landuse").map(|s| s.as_str()), Some("forest")) ||
matches!(tags.get("leisure").map(|s| s.as_str()), Some("park")) ||
matches!(waterway, Some("river" | "riverbank"))
@@ -210,7 +210,7 @@ fn should_include(tags: &HashMap<String, String>, zoom: u32) -> bool {
tags.contains_key("building") ||
tags.contains_key("landuse") ||
tags.contains_key("leisure") ||
matches!(natural, Some("water" | "wood" | "scrub" | "wetland" | "heath")) ||
matches!(natural, Some("water" | "wood" | "scrub" | "wetland" | "heath" | "bay" | "strait")) ||
matches!(waterway, Some("river" | "riverbank" | "stream"))
},
_ => false
@@ -404,7 +404,8 @@ async fn main() -> Result<()> {
// Filter for highways/roads OR buildings OR landuse OR water OR railways
let is_highway = tags.contains_key("highway");
let is_building = tags.contains_key("building");
let is_water = tags.get("natural").map(|v| v == "water" || v == "wetland").unwrap_or(false) ||
let is_water = tags.get("natural").map(|v| v == "water" || v == "wetland" || v == "bay" || v == "strait").unwrap_or(false) ||
tags.get("place").map(|v| v == "sea" || v == "ocean").unwrap_or(false) ||
tags.get("waterway").map(|v| v == "riverbank" || v == "stream" || v == "river").unwrap_or(false) ||
tags.get("landuse").map(|v| v == "basin" || v == "reservoir").unwrap_or(false);
let is_landuse = tags.contains_key("leisure") ||
@@ -442,7 +443,11 @@ async fn main() -> Result<()> {
};
let epsilon = if is_water || is_landuse || is_highway {
base_epsilon * 0.5 // Preserve more detail for natural features AND roads
if zoom <= 4 && is_landuse {
0.0 // Disable simplification for landuse at low zoom to prevent disappearing polygons
} else {
base_epsilon * 0.5 // Preserve more detail for natural features AND roads
}
} else {
base_epsilon
};
@@ -454,9 +459,17 @@ async fn main() -> Result<()> {
};
// Serialize points
let mut final_points = simplified_points;
let mut final_points = simplified_points.clone();
// Triangulate if it's a polygon type
// For highways and railways, we DON'T triangulate - they're line data
// Create the highway/railway blob BEFORE triangulation
let mut line_blob = Vec::with_capacity(simplified_points.len() * 8);
for (lat, lon) in &simplified_points {
line_blob.extend_from_slice(&(*lat as f32).to_le_bytes());
line_blob.extend_from_slice(&(*lon as f32).to_le_bytes());
}
// Triangulate for polygon types (buildings, water, landuse)
if is_building || is_water || is_landuse {
// Close the loop if not closed
if final_points.first() != final_points.last() {
@@ -465,40 +478,43 @@ async fn main() -> Result<()> {
final_points = triangulate_polygon(&final_points);
}
if final_points.len() < 2 { continue; }
if final_points.len() < 3 && (is_building || is_water || is_landuse) { continue; }
if simplified_points.len() < 2 && (is_highway || is_railway) { continue; }
let (first_lat, first_lon) = final_points[0];
let (first_lat, first_lon) = simplified_points[0];
let (x, y) = lat_lon_to_tile(first_lat, first_lon, zoom);
let zoom_i32 = zoom as i32;
let mut blob = Vec::with_capacity(final_points.len() * 8); // 4 bytes lat + 4 bytes lon
for (lat, lon) in final_points {
blob.extend_from_slice(&(lat as f32).to_le_bytes());
blob.extend_from_slice(&(lon as f32).to_le_bytes());
// Create polygon blob from triangulated points
let mut polygon_blob = Vec::with_capacity(final_points.len() * 8);
for (lat, lon) in &final_points {
polygon_blob.extend_from_slice(&(*lat as f32).to_le_bytes());
polygon_blob.extend_from_slice(&(*lon as f32).to_le_bytes());
}
// Use line_blob for highways/railways, polygon_blob for others
if is_highway {
let task = DbTask::Way { zoom: zoom_i32, table: "ways", id, tags: tags.clone(), points: blob.clone(), x, y };
let task = DbTask::Way { zoom: zoom_i32, table: "ways", id, tags: tags.clone(), points: line_blob.clone(), x, y };
let _ = tx.blocking_send(task);
}
if is_building {
let task = DbTask::Way { zoom: zoom_i32, table: "buildings", id, tags: tags.clone(), points: blob.clone(), x, y };
let task = DbTask::Way { zoom: zoom_i32, table: "buildings", id, tags: tags.clone(), points: polygon_blob.clone(), x, y };
let _ = tx.blocking_send(task);
}
if is_water {
let task = DbTask::Way { zoom: zoom_i32, table: "water", id, tags: tags.clone(), points: blob.clone(), x, y };
let task = DbTask::Way { zoom: zoom_i32, table: "water", id, tags: tags.clone(), points: polygon_blob.clone(), x, y };
let _ = tx.blocking_send(task);
}
if is_landuse {
let task = DbTask::Way { zoom: zoom_i32, table: "landuse", id, tags: tags.clone(), points: blob.clone(), x, y };
let task = DbTask::Way { zoom: zoom_i32, table: "landuse", id, tags: tags.clone(), points: polygon_blob.clone(), x, y };
let _ = tx.blocking_send(task);
}
if is_railway {
let task = DbTask::Way { zoom: zoom_i32, table: "railways", id, tags: tags.clone(), points: blob.clone(), x, y };
let task = DbTask::Way { zoom: zoom_i32, table: "railways", id, tags: tags.clone(), points: line_blob.clone(), x, y };
let _ = tx.blocking_send(task);
}
}