first commit
This commit is contained in:
18
.env
Normal file
18
.env
Normal file
@@ -0,0 +1,18 @@
|
||||
POSTGRES_USER=map
|
||||
POSTGRES_PASSWORD=map
|
||||
POSTGRES_DB=map
|
||||
POSTGRES_PORT=5432
|
||||
|
||||
REDIS_PORT=6379
|
||||
|
||||
MINIO_PORT=9000
|
||||
MINIO_CONSOLE_PORT=9001
|
||||
MINIO_ROOT_USER=minioadmin
|
||||
MINIO_ROOT_PASSWORD=minioadmin
|
||||
|
||||
TILE_SERVICE_PORT=8081
|
||||
ROUTE_SERVICE_PORT=8082
|
||||
CLIENT_PORT=8080
|
||||
|
||||
SERVICE_LOG_LEVEL=debug
|
||||
|
||||
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/target
|
||||
3778
Cargo.lock
generated
Normal file
3778
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
7
Cargo.toml
Normal file
7
Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"backend",
|
||||
"frontend",
|
||||
"importer",
|
||||
]
|
||||
resolver = "2"
|
||||
30
Dockerfile
Normal file
30
Dockerfile
Normal file
@@ -0,0 +1,30 @@
|
||||
# Build Frontend
|
||||
FROM rust:latest as frontend-builder
|
||||
WORKDIR /app
|
||||
COPY frontend ./frontend
|
||||
COPY backend/static ./backend/static
|
||||
# Install wasm-pack
|
||||
RUN curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
|
||||
WORKDIR /app/frontend
|
||||
# Build frontend
|
||||
RUN wasm-pack build --target web --out-name wasm --out-dir ../backend/static
|
||||
|
||||
# Build Backend
|
||||
FROM rust:latest as backend-builder
|
||||
WORKDIR /app
|
||||
COPY backend ./backend
|
||||
COPY --from=frontend-builder /app/backend/static ./backend/static
|
||||
WORKDIR /app/backend
|
||||
RUN cargo build --release
|
||||
|
||||
# Runtime
|
||||
FROM debian:bookworm-slim
|
||||
WORKDIR /app
|
||||
COPY --from=backend-builder /app/backend/target/release/backend ./backend
|
||||
COPY --from=frontend-builder /app/backend/static ./static
|
||||
# Install ca-certificates for HTTPS if needed
|
||||
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV SCYLLA_URI=scylla:9042
|
||||
EXPOSE 3000
|
||||
CMD ["./backend"]
|
||||
15
Makefile
Normal file
15
Makefile
Normal file
@@ -0,0 +1,15 @@
|
||||
.PHONY: dev-db dev-backend dev-frontend build-frontend
|
||||
|
||||
dev-db:
|
||||
docker-compose up -d
|
||||
|
||||
dev-backend:
|
||||
cd backend && cargo run
|
||||
|
||||
build-frontend:
|
||||
cd frontend && wasm-pack build --target web --out-name wasm --out-dir ../backend/static
|
||||
|
||||
dev-frontend:
|
||||
# This is a placeholder. Usually we'd use a dev server or just rebuild.
|
||||
# For now, we rely on the backend serving the static files.
|
||||
make build-frontend
|
||||
1
backend/.gitignore
vendored
Normal file
1
backend/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/target
|
||||
15
backend/Cargo.toml
Normal file
15
backend/Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "backend"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
axum = "0.7"
|
||||
tokio = { version = "1.0", features = ["full"] }
|
||||
scylla = "0.12" # Check for latest version, using a recent stable one
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
tower = "0.4"
|
||||
tower-http = { version = "0.5", features = ["cors", "fs"] }
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = "0.3"
|
||||
83
backend/src/db.rs
Normal file
83
backend/src/db.rs
Normal file
@@ -0,0 +1,83 @@
|
||||
use scylla::{Session, SessionBuilder};
|
||||
use std::sync::Arc;
|
||||
|
||||
pub async fn initialize_schema(session: &Session) -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Create keyspace
|
||||
session
|
||||
.query(
|
||||
"CREATE KEYSPACE IF NOT EXISTS map_data WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 }",
|
||||
&[],
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Create table for OSM nodes (points)
|
||||
// Partition by tile coordinates (zoom, x, y) for efficient retrieval
|
||||
// This is a simplified schema. Real OSM data is more complex.
|
||||
session
|
||||
.query(
|
||||
"CREATE TABLE IF NOT EXISTS map_data.nodes (
|
||||
zoom int,
|
||||
tile_x int,
|
||||
tile_y int,
|
||||
id bigint,
|
||||
lat double,
|
||||
lon double,
|
||||
tags map<text, text>,
|
||||
PRIMARY KEY ((zoom, tile_x, tile_y), id)
|
||||
)",
|
||||
&[],
|
||||
)
|
||||
.await?;
|
||||
|
||||
session
|
||||
.query(
|
||||
"CREATE TABLE IF NOT EXISTS map_data.ways (
|
||||
zoom int,
|
||||
tile_x int,
|
||||
tile_y int,
|
||||
id bigint,
|
||||
tags map<text, text>,
|
||||
points blob,
|
||||
PRIMARY KEY ((zoom, tile_x, tile_y), id)
|
||||
)",
|
||||
&[],
|
||||
)
|
||||
.await?;
|
||||
|
||||
session
|
||||
.query(
|
||||
"CREATE TABLE IF NOT EXISTS map_data.buildings (
|
||||
zoom int,
|
||||
tile_x int,
|
||||
tile_y int,
|
||||
id bigint,
|
||||
tags map<text, text>,
|
||||
points blob,
|
||||
PRIMARY KEY ((zoom, tile_x, tile_y), id)
|
||||
)",
|
||||
&[],
|
||||
)
|
||||
.await?;
|
||||
|
||||
println!("Schema initialized.");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn seed_data(session: &Session) -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Insert some dummy data for Munich (approx lat/lon)
|
||||
// Munich is roughly at lat 48.1351, lon 11.5820
|
||||
// At zoom 10, this falls into a specific tile.
|
||||
// For simplicity, we'll just use a fixed tile coordinate for testing: 10/500/500 (not accurate, just for ID)
|
||||
|
||||
let insert_stmt = "INSERT INTO map_data.nodes (zoom, tile_x, tile_y, id, lat, lon, tags) VALUES (?, ?, ?, ?, ?, ?, ?)";
|
||||
let prepared = session.prepare(insert_stmt).await?;
|
||||
|
||||
// Point 1: Marienplatz
|
||||
session.execute(&prepared, (10, 500, 500, 1_i64, 48.137, 11.575, std::collections::HashMap::from([("name".to_string(), "Marienplatz".to_string())]))).await?;
|
||||
|
||||
// Point 2: English Garden
|
||||
session.execute(&prepared, (10, 500, 500, 2_i64, 48.150, 11.590, std::collections::HashMap::from([("name".to_string(), "English Garden".to_string())]))).await?;
|
||||
|
||||
println!("Test data seeded.");
|
||||
Ok(())
|
||||
}
|
||||
146
backend/src/main.rs
Normal file
146
backend/src/main.rs
Normal file
@@ -0,0 +1,146 @@
|
||||
mod db;
|
||||
|
||||
use axum::{
|
||||
routing::get,
|
||||
Router,
|
||||
extract::{State, Path},
|
||||
Json,
|
||||
};
|
||||
use scylla::{Session, SessionBuilder};
|
||||
use std::sync::Arc;
|
||||
use tower_http::services::ServeDir;
|
||||
use tower_http::cors::CorsLayer;
|
||||
use serde::Serialize;
|
||||
|
||||
struct AppState {
|
||||
scylla_session: Arc<Session>,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Initialize tracing
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
println!("Connecting to ScyllaDB...");
|
||||
let uri = std::env::var("SCYLLA_URI").unwrap_or_else(|_| "127.0.0.1:9042".to_string());
|
||||
|
||||
let session = SessionBuilder::new()
|
||||
.known_node(uri)
|
||||
.build()
|
||||
.await?;
|
||||
|
||||
// Initialize schema and seed data
|
||||
db::initialize_schema(&session).await?;
|
||||
db::seed_data(&session).await?;
|
||||
|
||||
let session = Arc::new(session);
|
||||
println!("Connected to ScyllaDB!");
|
||||
|
||||
let state = Arc::new(AppState {
|
||||
scylla_session: session,
|
||||
});
|
||||
|
||||
let app = Router::new()
|
||||
.route("/health", get(health_check))
|
||||
.route("/api/tiles/:z/:x/:y", get(get_tile))
|
||||
.route("/api/tiles/:z/:x/:y/ways", get(get_tile_ways))
|
||||
.route("/api/tiles/:z/:x/:y/buildings", get(get_tile_buildings))
|
||||
.nest_service("/", ServeDir::new("static"))
|
||||
.layer(CorsLayer::permissive())
|
||||
.with_state(state);
|
||||
|
||||
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
|
||||
println!("Server listening on {}", listener.local_addr()?);
|
||||
axum::serve(listener, app).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn health_check() -> &'static str {
|
||||
"OK"
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct MapNode {
|
||||
id: i64,
|
||||
lat: f64,
|
||||
lon: f64,
|
||||
tags: std::collections::HashMap<String, String>,
|
||||
}
|
||||
|
||||
async fn get_tile(
|
||||
Path((z, x, y)): Path<(i32, i32, i32)>,
|
||||
State(state): State<Arc<AppState>>,
|
||||
) -> Json<Vec<MapNode>> {
|
||||
let query = "SELECT id, lat, lon, tags FROM map_data.nodes WHERE zoom = ? AND tile_x = ? AND tile_y = ?";
|
||||
let rows = state.scylla_session.query(query, (z, x, y)).await.unwrap().rows.unwrap_or_default();
|
||||
|
||||
let mut nodes = Vec::new();
|
||||
for row in rows {
|
||||
let (id, lat, lon, tags) = row.into_typed::<(i64, f64, f64, std::collections::HashMap<String, String>)>().unwrap();
|
||||
nodes.push(MapNode { id, lat, lon, tags });
|
||||
}
|
||||
|
||||
Json(nodes)
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct MapWay {
|
||||
id: i64,
|
||||
tags: std::collections::HashMap<String, String>,
|
||||
points: Vec<Vec<f64>>, // List of [lat, lon]
|
||||
}
|
||||
|
||||
async fn get_tile_ways(
|
||||
Path((z, x, y)): Path<(i32, i32, i32)>,
|
||||
State(state): State<Arc<AppState>>,
|
||||
) -> Json<Vec<MapWay>> {
|
||||
let query = "SELECT id, tags, points FROM map_data.ways WHERE zoom = ? AND tile_x = ? AND tile_y = ?";
|
||||
let rows = state.scylla_session.query(query, (z, x, y)).await.unwrap().rows.unwrap_or_default();
|
||||
|
||||
let mut ways = Vec::new();
|
||||
for row in rows {
|
||||
let (id, tags, points_blob) = row.into_typed::<(i64, std::collections::HashMap<String, String>, Vec<u8>)>().unwrap();
|
||||
|
||||
// Deserialize points blob
|
||||
let mut points = Vec::new();
|
||||
for chunk in points_blob.chunks(16) {
|
||||
if chunk.len() == 16 {
|
||||
let lat = f64::from_be_bytes(chunk[0..8].try_into().unwrap());
|
||||
let lon = f64::from_be_bytes(chunk[8..16].try_into().unwrap());
|
||||
points.push(vec![lat, lon]);
|
||||
}
|
||||
}
|
||||
|
||||
ways.push(MapWay { id, tags, points });
|
||||
}
|
||||
|
||||
Json(ways)
|
||||
}
|
||||
|
||||
async fn get_tile_buildings(
|
||||
Path((z, x, y)): Path<(i32, i32, i32)>,
|
||||
State(state): State<Arc<AppState>>,
|
||||
) -> Json<Vec<MapWay>> {
|
||||
let query = "SELECT id, tags, points FROM map_data.buildings WHERE zoom = ? AND tile_x = ? AND tile_y = ?";
|
||||
let rows = state.scylla_session.query(query, (z, x, y)).await.unwrap().rows.unwrap_or_default();
|
||||
|
||||
let mut buildings = Vec::new();
|
||||
for row in rows {
|
||||
let (id, tags, points_blob) = row.into_typed::<(i64, std::collections::HashMap<String, String>, Vec<u8>)>().unwrap();
|
||||
|
||||
// Deserialize points blob
|
||||
let mut points = Vec::new();
|
||||
for chunk in points_blob.chunks(16) {
|
||||
if chunk.len() == 16 {
|
||||
let lat = f64::from_be_bytes(chunk[0..8].try_into().unwrap());
|
||||
let lon = f64::from_be_bytes(chunk[8..16].try_into().unwrap());
|
||||
points.push(vec![lat, lon]);
|
||||
}
|
||||
}
|
||||
|
||||
buildings.push(MapWay { id, tags, points });
|
||||
}
|
||||
|
||||
Json(buildings)
|
||||
}
|
||||
27
docker-compose.yml
Normal file
27
docker-compose.yml
Normal file
@@ -0,0 +1,27 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
scylla:
|
||||
image: scylladb/scylla:latest
|
||||
container_name: scylla
|
||||
ports:
|
||||
- "9042:9042"
|
||||
- "9160:9160"
|
||||
- "10000:10000"
|
||||
command: --smp 1 --memory 750M --overprovisioned 1 --api-address 0.0.0.0 --max-memory-for-unlimited-query-soft-limit 10485760 --tombstone-warn-threshold 100000
|
||||
volumes:
|
||||
- scylla_data:/var/lib/scylla
|
||||
|
||||
app:
|
||||
build: .
|
||||
container_name: map-app
|
||||
ports:
|
||||
- "3000:3000"
|
||||
depends_on:
|
||||
- scylla
|
||||
environment:
|
||||
- SCYLLA_URI=scylla:9042
|
||||
restart: always
|
||||
|
||||
volumes:
|
||||
scylla_data:
|
||||
1
frontend/.gitignore
vendored
Normal file
1
frontend/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/target
|
||||
45
frontend/Cargo.toml
Normal file
45
frontend/Cargo.toml
Normal file
@@ -0,0 +1,45 @@
|
||||
[package]
|
||||
name = "frontend"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen-futures = "0.4"
|
||||
web-sys = { version = "0.3", features = [
|
||||
"Document",
|
||||
"Window",
|
||||
"Element",
|
||||
"HtmlCanvasElement",
|
||||
"HtmlElement",
|
||||
"Node",
|
||||
"console",
|
||||
"Response",
|
||||
"HtmlButtonElement",
|
||||
"Event",
|
||||
"MouseEvent",
|
||||
"Geolocation",
|
||||
"Navigator",
|
||||
"Position",
|
||||
"PositionError",
|
||||
"Coordinates",
|
||||
"Cache",
|
||||
"CacheStorage",
|
||||
"Request",
|
||||
"RequestInit",
|
||||
"RequestMode",
|
||||
"Response",
|
||||
] }
|
||||
wgpu = { version = "0.19", default-features = false, features = ["webgl", "wgsl"] }
|
||||
winit = { version = "0.29", default-features = false, features = ["rwh_06"] }
|
||||
bytemuck = { version = "1.14", features = ["derive"] }
|
||||
log = "0.4"
|
||||
console_error_panic_hook = "0.1"
|
||||
console_log = "1.0"
|
||||
reqwest = { version = "0.11", features = ["json"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
earcutr = "0.4"
|
||||
1047
frontend/src/lib.rs
Normal file
1047
frontend/src/lib.rs
Normal file
File diff suppressed because it is too large
Load Diff
1
importer/.gitignore
vendored
Normal file
1
importer/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/target
|
||||
10
importer/Cargo.toml
Normal file
10
importer/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "importer"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
osmpbf = "0.3" # Pure Rust PBF parser, easier to build than osmium (C++ bindings)
|
||||
scylla = "0.12"
|
||||
tokio = { version = "1.0", features = ["full"] }
|
||||
anyhow = "1.0"
|
||||
164
importer/src/main.rs
Normal file
164
importer/src/main.rs
Normal file
@@ -0,0 +1,164 @@
|
||||
use anyhow::Result;
|
||||
use osmpbf::{Element, ElementReader};
|
||||
use scylla::{Session, SessionBuilder};
|
||||
use std::collections::HashMap;
|
||||
use tokio::task::JoinSet;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
// Connect to ScyllaDB
|
||||
let uri = std::env::var("SCYLLA_URI").unwrap_or_else(|_| "127.0.0.1:9042".to_string());
|
||||
println!("Connecting to ScyllaDB at {}...", uri);
|
||||
let session = SessionBuilder::new().known_node(uri).build().await?;
|
||||
let session = std::sync::Arc::new(session);
|
||||
|
||||
// Ensure schema exists
|
||||
session.query("CREATE KEYSPACE IF NOT EXISTS map_data WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 }", &[]).await?;
|
||||
|
||||
// Create tables
|
||||
session.query("CREATE TABLE IF NOT EXISTS map_data.nodes (zoom int, tile_x int, tile_y int, id bigint, lat double, lon double, tags map<text, text>, PRIMARY KEY ((zoom, tile_x, tile_y), id))", &[]).await?;
|
||||
session.query("CREATE TABLE IF NOT EXISTS map_data.ways (zoom int, tile_x int, tile_y int, id bigint, tags map<text, text>, points blob, PRIMARY KEY ((zoom, tile_x, tile_y), id))", &[]).await?;
|
||||
|
||||
let path = "sample.osm.pbf";
|
||||
println!("Reading {}...", path);
|
||||
let reader = ElementReader::from_path(path)?;
|
||||
|
||||
// Cache for node coordinates: ID -> (lat, lon)
|
||||
let mut node_cache = HashMap::<i64, (f64, f64)>::new();
|
||||
|
||||
let mut join_set = JoinSet::new();
|
||||
let mut node_count = 0;
|
||||
let mut way_count = 0;
|
||||
let mut inserted_nodes = 0;
|
||||
let mut inserted_ways = 0;
|
||||
|
||||
// We process sequentially: Nodes first, then Ways.
|
||||
reader.for_each(|element| {
|
||||
match element {
|
||||
Element::Node(node) => {
|
||||
node_count += 1;
|
||||
node_cache.insert(node.id(), (node.lat(), node.lon()));
|
||||
|
||||
if node.tags().count() > 0 {
|
||||
inserted_nodes += 1;
|
||||
let session = session.clone();
|
||||
let id = node.id();
|
||||
let lat = node.lat();
|
||||
let lon = node.lon();
|
||||
let tags: HashMap<String, String> = node.tags().map(|(k, v)| (k.to_string(), v.to_string())).collect();
|
||||
|
||||
let (x, y) = lat_lon_to_tile(lat, lon, 10);
|
||||
|
||||
join_set.spawn(async move {
|
||||
let _ = session.query(
|
||||
"INSERT INTO map_data.nodes (zoom, tile_x, tile_y, id, lat, lon, tags) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
||||
(10, x, y, id, lat, lon, tags),
|
||||
).await;
|
||||
});
|
||||
}
|
||||
}
|
||||
Element::DenseNode(node) => {
|
||||
node_count += 1;
|
||||
node_cache.insert(node.id(), (node.lat(), node.lon()));
|
||||
|
||||
if node.tags().count() > 0 {
|
||||
inserted_nodes += 1;
|
||||
let session = session.clone();
|
||||
let id = node.id();
|
||||
let lat = node.lat();
|
||||
let lon = node.lon();
|
||||
let tags: HashMap<String, String> = node.tags().map(|(k, v)| (k.to_string(), v.to_string())).collect();
|
||||
|
||||
let (x, y) = lat_lon_to_tile(lat, lon, 10);
|
||||
|
||||
join_set.spawn(async move {
|
||||
let _ = session.query(
|
||||
"INSERT INTO map_data.nodes (zoom, tile_x, tile_y, id, lat, lon, tags) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
||||
(10, x, y, id, lat, lon, tags),
|
||||
).await;
|
||||
});
|
||||
}
|
||||
}
|
||||
Element::Way(way) => {
|
||||
way_count += 1;
|
||||
let tags: HashMap<String, String> = way.tags().map(|(k, v)| (k.to_string(), v.to_string())).collect();
|
||||
|
||||
// Filter for highways/roads OR buildings
|
||||
let is_highway = tags.contains_key("highway");
|
||||
let is_building = tags.contains_key("building");
|
||||
|
||||
if is_highway || is_building {
|
||||
let mut points = Vec::new();
|
||||
|
||||
// Resolve nodes
|
||||
for node_id in way.refs() {
|
||||
if let Some(&coords) = node_cache.get(&node_id) {
|
||||
points.push(coords);
|
||||
}
|
||||
}
|
||||
|
||||
if points.len() >= 2 {
|
||||
let session = session.clone();
|
||||
let id = way.id();
|
||||
|
||||
// Insert into the tile of the first point
|
||||
let (first_lat, first_lon) = points[0];
|
||||
let (x, y) = lat_lon_to_tile(first_lat, first_lon, 10);
|
||||
|
||||
// Serialize points to blob (f64, f64) pairs
|
||||
let mut blob = Vec::with_capacity(points.len() * 16);
|
||||
for (lat, lon) in points {
|
||||
blob.extend_from_slice(&lat.to_be_bytes());
|
||||
blob.extend_from_slice(&lon.to_be_bytes());
|
||||
}
|
||||
|
||||
if is_highway {
|
||||
inserted_ways += 1;
|
||||
let tags_clone = tags.clone();
|
||||
let blob_clone = blob.clone();
|
||||
let session = session.clone();
|
||||
join_set.spawn(async move {
|
||||
let _ = session.query(
|
||||
"INSERT INTO map_data.ways (zoom, tile_x, tile_y, id, tags, points) VALUES (?, ?, ?, ?, ?, ?)",
|
||||
(10, x, y, id, tags_clone, blob_clone),
|
||||
).await;
|
||||
});
|
||||
}
|
||||
|
||||
if is_building {
|
||||
// inserted_buildings += 1; // Need to add this counter
|
||||
let session = session.clone();
|
||||
join_set.spawn(async move {
|
||||
let _ = session.query(
|
||||
"INSERT INTO map_data.buildings (zoom, tile_x, tile_y, id, tags, points) VALUES (?, ?, ?, ?, ?, ?)",
|
||||
(10, x, y, id, tags, blob),
|
||||
).await;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if (node_count + way_count) % 100_000 == 0 {
|
||||
println!("Processed {} nodes, {} ways...", node_count, way_count);
|
||||
}
|
||||
})?;
|
||||
|
||||
println!("Finished processing. Nodes: {}, Ways: {}. Inserted Nodes: {}, Inserted Ways: {}", node_count, way_count, inserted_nodes, inserted_ways);
|
||||
|
||||
println!("Waiting for pending inserts...");
|
||||
while let Some(_) = join_set.join_next().await {}
|
||||
println!("Done!");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn lat_lon_to_tile(lat: f64, lon: f64, zoom: u32) -> (i32, i32) {
|
||||
let n = 2.0f64.powi(zoom as i32);
|
||||
let x = (lon + 180.0) / 360.0 * n;
|
||||
let lat_rad = lat.to_radians();
|
||||
let y = (1.0 - (lat_rad.tan() + (1.0 / lat_rad.cos())).ln() / std::f64::consts::PI) / 2.0 * n;
|
||||
(x as i32, y as i32)
|
||||
}
|
||||
Reference in New Issue
Block a user