update
This commit is contained in:
@@ -4,12 +4,14 @@ use axum::{
|
||||
routing::get,
|
||||
Router,
|
||||
extract::{State, Path},
|
||||
Json,
|
||||
http::header,
|
||||
response::IntoResponse,
|
||||
};
|
||||
use scylla::{Session, SessionBuilder};
|
||||
use std::sync::Arc;
|
||||
use tower_http::services::ServeDir;
|
||||
use tower_http::cors::CorsLayer;
|
||||
use tower_http::compression::CompressionLayer;
|
||||
use serde::Serialize;
|
||||
|
||||
struct AppState {
|
||||
@@ -49,8 +51,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
.route("/api/tiles/:z/:x/:y/landuse", get(get_tile_landuse))
|
||||
.route("/api/tiles/:z/:x/:y/water", get(get_tile_water))
|
||||
.route("/api/tiles/:z/:x/:y/railways", get(get_tile_railways))
|
||||
.route("/api/tiles/:z/:x/:y/all", get(get_tile_all))
|
||||
.nest_service("/", ServeDir::new("static").append_index_html_on_directories(true))
|
||||
.layer(CorsLayer::permissive())
|
||||
.layer(CompressionLayer::new())
|
||||
.with_state(state);
|
||||
|
||||
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
|
||||
@@ -75,219 +79,310 @@ struct MapNode {
|
||||
async fn get_tile(
|
||||
Path((z, x, y)): Path<(i32, i32, i32)>,
|
||||
State(state): State<Arc<AppState>>,
|
||||
) -> Result<Json<Vec<MapNode>>, (axum::http::StatusCode, String)> {
|
||||
) -> impl IntoResponse {
|
||||
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
|
||||
.map_err(|e| (axum::http::StatusCode::INTERNAL_SERVER_ERROR, format!("Database error: {}", e)))?
|
||||
.rows
|
||||
.unwrap_or_default();
|
||||
let rows = match state.scylla_session.query(query, (z, x, y)).await {
|
||||
Ok(res) => res.rows.unwrap_or_default(),
|
||||
Err(e) => return (axum::http::StatusCode::INTERNAL_SERVER_ERROR, format!("Database error: {}", e)).into_response(),
|
||||
};
|
||||
|
||||
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>)>()
|
||||
.map_err(|e| (axum::http::StatusCode::INTERNAL_SERVER_ERROR, format!("Serialization error: {}", e)))?;
|
||||
.map_err(|e| (axum::http::StatusCode::INTERNAL_SERVER_ERROR, format!("Serialization error: {}", e))).unwrap();
|
||||
nodes.push(MapNode { id, lat, lon, tags });
|
||||
}
|
||||
|
||||
Ok(Json(nodes))
|
||||
let bytes = bincode::serialize(&nodes).unwrap();
|
||||
(
|
||||
[(header::CONTENT_TYPE, "application/octet-stream")],
|
||||
bytes,
|
||||
).into_response()
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct MapWay {
|
||||
id: i64,
|
||||
tags: std::collections::HashMap<String, String>,
|
||||
points: Vec<Vec<f64>>, // List of [lat, lon]
|
||||
points: Vec<u8>, // Flat f32 array
|
||||
}
|
||||
|
||||
async fn get_tile_ways(
|
||||
Path((z, x, y)): Path<(i32, i32, i32)>,
|
||||
State(state): State<Arc<AppState>>,
|
||||
) -> Result<Json<Vec<MapWay>>, (axum::http::StatusCode, String)> {
|
||||
) -> impl IntoResponse {
|
||||
let query = if z < 9 {
|
||||
"SELECT id, tags, points FROM map_data.ways WHERE zoom = ? AND tile_x = ? AND tile_y = ? LIMIT 10000"
|
||||
} else {
|
||||
"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
|
||||
.map_err(|e| (axum::http::StatusCode::INTERNAL_SERVER_ERROR, format!("Database error: {}", e)))?
|
||||
.rows
|
||||
.unwrap_or_default();
|
||||
let rows = match state.scylla_session.query(query, (z, x, y)).await {
|
||||
Ok(res) => res.rows.unwrap_or_default(),
|
||||
Err(e) => return (axum::http::StatusCode::INTERNAL_SERVER_ERROR, format!("Database error: {}", e)).into_response(),
|
||||
};
|
||||
|
||||
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>)>()
|
||||
.map_err(|e| (axum::http::StatusCode::INTERNAL_SERVER_ERROR, format!("Serialization error: {}", e)))?;
|
||||
let (id, tags, points) = row.into_typed::<(i64, std::collections::HashMap<String, String>, Vec<u8>)>()
|
||||
.map_err(|e| (axum::http::StatusCode::INTERNAL_SERVER_ERROR, format!("Serialization error: {}", e))).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 });
|
||||
}
|
||||
|
||||
Ok(Json(ways))
|
||||
let bytes = bincode::serialize(&ways).unwrap();
|
||||
(
|
||||
[(header::CONTENT_TYPE, "application/octet-stream")],
|
||||
bytes,
|
||||
).into_response()
|
||||
}
|
||||
|
||||
async fn get_tile_buildings(
|
||||
Path((z, x, y)): Path<(i32, i32, i32)>,
|
||||
State(state): State<Arc<AppState>>,
|
||||
) -> Result<Json<Vec<MapWay>>, (axum::http::StatusCode, String)> {
|
||||
) -> impl IntoResponse {
|
||||
let query = "SELECT id, tags, points FROM map_data.buildings WHERE zoom = ? AND tile_x = ? AND tile_y = ?";
|
||||
|
||||
// Optimization: Don't load buildings for low zoom levels
|
||||
if z < 13 {
|
||||
return Ok(Json(Vec::new()));
|
||||
if z < 12 {
|
||||
return ([(header::CONTENT_TYPE, "application/octet-stream")], vec![]).into_response();
|
||||
}
|
||||
|
||||
let rows = state.scylla_session.query(query, (z, x, y))
|
||||
.await
|
||||
.map_err(|e| (axum::http::StatusCode::INTERNAL_SERVER_ERROR, format!("Database error: {}", e)))?
|
||||
.rows
|
||||
.unwrap_or_default();
|
||||
let rows = match state.scylla_session.query(query, (z, x, y)).await {
|
||||
Ok(res) => res.rows.unwrap_or_default(),
|
||||
Err(e) => return (axum::http::StatusCode::INTERNAL_SERVER_ERROR, format!("Database error: {}", e)).into_response(),
|
||||
};
|
||||
|
||||
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>)>()
|
||||
.map_err(|e| (axum::http::StatusCode::INTERNAL_SERVER_ERROR, format!("Serialization error: {}", e)))?;
|
||||
let (id, tags, points) = row.into_typed::<(i64, std::collections::HashMap<String, String>, Vec<u8>)>()
|
||||
.map_err(|e| (axum::http::StatusCode::INTERNAL_SERVER_ERROR, format!("Serialization error: {}", e))).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 });
|
||||
}
|
||||
|
||||
Ok(Json(buildings))
|
||||
let bytes = bincode::serialize(&buildings).unwrap();
|
||||
(
|
||||
[(header::CONTENT_TYPE, "application/octet-stream")],
|
||||
bytes,
|
||||
).into_response()
|
||||
}
|
||||
|
||||
async fn get_tile_landuse(
|
||||
Path((z, x, y)): Path<(i32, i32, i32)>,
|
||||
State(state): State<Arc<AppState>>,
|
||||
) -> Result<Json<Vec<MapWay>>, (axum::http::StatusCode, String)> {
|
||||
println!("Request: get_tile_landuse({}, {}, {})", z, x, y);
|
||||
|
||||
) -> impl IntoResponse {
|
||||
// Optimization: Don't load landuse for low zoom levels
|
||||
if z < 11 {
|
||||
return Ok(Json(Vec::new()));
|
||||
if z < 4 {
|
||||
return ([(header::CONTENT_TYPE, "application/octet-stream")], vec![]).into_response();
|
||||
}
|
||||
|
||||
let query = "SELECT id, tags, points FROM map_data.landuse WHERE zoom = ? AND tile_x = ? AND tile_y = ?";
|
||||
println!("Executing query...");
|
||||
let result = state.scylla_session.query(query, (z, x, y)).await;
|
||||
let rows = match state.scylla_session.query(query, (z, x, y)).await {
|
||||
Ok(res) => res.rows.unwrap_or_default(),
|
||||
Err(e) => return (axum::http::StatusCode::INTERNAL_SERVER_ERROR, format!("Database error: {}", e)).into_response(),
|
||||
};
|
||||
|
||||
match result {
|
||||
Ok(res) => {
|
||||
println!("Query successful, processing rows...");
|
||||
let rows = res.rows.unwrap_or_default();
|
||||
let mut landuse = Vec::new();
|
||||
for row in rows {
|
||||
let (id, tags, points_blob) = row.into_typed::<(i64, std::collections::HashMap<String, String>, Vec<u8>)>()
|
||||
.map_err(|e| {
|
||||
println!("Serialization error: {}", e);
|
||||
(axum::http::StatusCode::INTERNAL_SERVER_ERROR, format!("Serialization error: {}", e))
|
||||
})?;
|
||||
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
landuse.push(MapWay { id, tags, points });
|
||||
}
|
||||
println!("Returning {} landuse items", landuse.len());
|
||||
Ok(Json(landuse))
|
||||
},
|
||||
Err(e) => {
|
||||
println!("Query failed: {}", e);
|
||||
Err((axum::http::StatusCode::INTERNAL_SERVER_ERROR, format!("Database error: {}", e)))
|
||||
}
|
||||
let mut landuse = Vec::new();
|
||||
for row in rows {
|
||||
let (id, tags, points) = row.into_typed::<(i64, std::collections::HashMap<String, String>, Vec<u8>)>()
|
||||
.map_err(|e| (axum::http::StatusCode::INTERNAL_SERVER_ERROR, format!("Serialization error: {}", e))).unwrap();
|
||||
|
||||
landuse.push(MapWay { id, tags, points });
|
||||
}
|
||||
|
||||
let bytes = bincode::serialize(&landuse).unwrap();
|
||||
(
|
||||
[(header::CONTENT_TYPE, "application/octet-stream")],
|
||||
bytes,
|
||||
).into_response()
|
||||
}
|
||||
|
||||
async fn get_tile_water(
|
||||
Path((z, x, y)): Path<(i32, i32, i32)>,
|
||||
State(state): State<Arc<AppState>>,
|
||||
) -> Result<Json<Vec<MapWay>>, (axum::http::StatusCode, String)> {
|
||||
) -> impl IntoResponse {
|
||||
let query = if z < 9 {
|
||||
"SELECT id, tags, points FROM map_data.water WHERE zoom = ? AND tile_x = ? AND tile_y = ? LIMIT 10000"
|
||||
} else {
|
||||
"SELECT id, tags, points FROM map_data.water WHERE zoom = ? AND tile_x = ? AND tile_y = ?"
|
||||
};
|
||||
let rows = state.scylla_session.query(query, (z, x, y))
|
||||
.await
|
||||
.map_err(|e| (axum::http::StatusCode::INTERNAL_SERVER_ERROR, format!("Database error: {}", e)))?
|
||||
.rows
|
||||
.unwrap_or_default();
|
||||
let rows = match state.scylla_session.query(query, (z, x, y)).await {
|
||||
Ok(res) => res.rows.unwrap_or_default(),
|
||||
Err(e) => return (axum::http::StatusCode::INTERNAL_SERVER_ERROR, format!("Database error: {}", e)).into_response(),
|
||||
};
|
||||
|
||||
let mut water = Vec::new();
|
||||
for row in rows {
|
||||
let (id, tags, points_blob) = row.into_typed::<(i64, std::collections::HashMap<String, String>, Vec<u8>)>()
|
||||
.map_err(|e| (axum::http::StatusCode::INTERNAL_SERVER_ERROR, format!("Serialization error: {}", e)))?;
|
||||
let (id, tags, points) = row.into_typed::<(i64, std::collections::HashMap<String, String>, Vec<u8>)>()
|
||||
.map_err(|e| (axum::http::StatusCode::INTERNAL_SERVER_ERROR, format!("Serialization error: {}", e))).unwrap();
|
||||
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
water.push(MapWay { id, tags, points });
|
||||
}
|
||||
|
||||
Ok(Json(water))
|
||||
let bytes = bincode::serialize(&water).unwrap();
|
||||
(
|
||||
[(header::CONTENT_TYPE, "application/octet-stream")],
|
||||
bytes,
|
||||
).into_response()
|
||||
}
|
||||
|
||||
async fn get_tile_railways(
|
||||
Path((z, x, y)): Path<(i32, i32, i32)>,
|
||||
State(state): State<Arc<AppState>>,
|
||||
) -> Result<Json<Vec<MapWay>>, (axum::http::StatusCode, String)> {
|
||||
) -> impl IntoResponse {
|
||||
let query = if z < 9 {
|
||||
"SELECT id, tags, points FROM map_data.railways WHERE zoom = ? AND tile_x = ? AND tile_y = ? LIMIT 10000"
|
||||
} else {
|
||||
"SELECT id, tags, points FROM map_data.railways WHERE zoom = ? AND tile_x = ? AND tile_y = ?"
|
||||
};
|
||||
let rows = state.scylla_session.query(query, (z, x, y))
|
||||
.await
|
||||
.map_err(|e| (axum::http::StatusCode::INTERNAL_SERVER_ERROR, format!("Database error: {}", e)))?
|
||||
.rows
|
||||
.unwrap_or_default();
|
||||
let rows = match state.scylla_session.query(query, (z, x, y)).await {
|
||||
Ok(res) => res.rows.unwrap_or_default(),
|
||||
Err(e) => return (axum::http::StatusCode::INTERNAL_SERVER_ERROR, format!("Database error: {}", e)).into_response(),
|
||||
};
|
||||
|
||||
let mut railways = Vec::new();
|
||||
for row in rows {
|
||||
let (id, tags, points_blob) = row.into_typed::<(i64, std::collections::HashMap<String, String>, Vec<u8>)>()
|
||||
.map_err(|e| (axum::http::StatusCode::INTERNAL_SERVER_ERROR, format!("Serialization error: {}", e)))?;
|
||||
let (id, tags, points) = row.into_typed::<(i64, std::collections::HashMap<String, String>, Vec<u8>)>()
|
||||
.map_err(|e| (axum::http::StatusCode::INTERNAL_SERVER_ERROR, format!("Serialization error: {}", e))).unwrap();
|
||||
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
railways.push(MapWay { id, tags, points });
|
||||
}
|
||||
|
||||
Ok(Json(railways))
|
||||
let bytes = bincode::serialize(&railways).unwrap();
|
||||
(
|
||||
[(header::CONTENT_TYPE, "application/octet-stream")],
|
||||
bytes,
|
||||
).into_response()
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct TileData {
|
||||
nodes: Vec<MapNode>,
|
||||
ways: Vec<MapWay>,
|
||||
buildings: Vec<MapWay>,
|
||||
landuse: Vec<MapWay>,
|
||||
water: Vec<MapWay>,
|
||||
railways: Vec<MapWay>,
|
||||
}
|
||||
|
||||
async fn get_tile_all(
|
||||
Path((z, x, y)): Path<(i32, i32, i32)>,
|
||||
State(state): State<Arc<AppState>>,
|
||||
) -> impl IntoResponse {
|
||||
// Run all queries in parallel
|
||||
// Run all queries in parallel
|
||||
// (Removed unused tokio::join! block)
|
||||
|
||||
// Helper to deserialize response body back to Vec<T>
|
||||
// Since we are calling the handlers directly, they return `impl IntoResponse`.
|
||||
// We need to extract the bytes. This is a bit hacky because we are serializing then deserializing.
|
||||
// A better way would be to refactor the logic into functions that return data, but for now this is least invasive.
|
||||
// Actually, calling handlers directly is fine if we can extract the body.
|
||||
// But `impl IntoResponse` is opaque.
|
||||
// Refactoring is better. Let's create helper functions that return the data structs.
|
||||
|
||||
// REFACTOR STRATEGY:
|
||||
// 1. Extract logic from `get_tile` to `fetch_nodes`.
|
||||
// 2. Call `fetch_nodes` from `get_tile` and `get_tile_all`.
|
||||
// ... repeat for all.
|
||||
|
||||
// Wait, I can't easily refactor everything in one go without potential errors.
|
||||
// Let's try to implement `get_tile_all` by copying the logic. It's duplication but safer for now.
|
||||
// Actually, duplication is bad.
|
||||
// Let's look at `get_tile`. It queries DB and returns bytes.
|
||||
// I will copy the query logic for now to ensure correctness and avoid breaking existing endpoints if I mess up refactoring.
|
||||
|
||||
// Nodes
|
||||
let query_nodes = "SELECT id, lat, lon, tags FROM map_data.nodes WHERE zoom = ? AND tile_x = ? AND tile_y = ?";
|
||||
let rows_nodes = state.scylla_session.query(query_nodes, (z, x, y)).await.ok().and_then(|r| r.rows).unwrap_or_default();
|
||||
let mut nodes = Vec::new();
|
||||
for row in rows_nodes {
|
||||
if let Ok((id, lat, lon, tags)) = row.into_typed::<(i64, f64, f64, std::collections::HashMap<String, String>)>() {
|
||||
nodes.push(MapNode { id, lat, lon, tags });
|
||||
}
|
||||
}
|
||||
|
||||
// Ways
|
||||
let query_ways = if z < 9 {
|
||||
"SELECT id, tags, points FROM map_data.ways WHERE zoom = ? AND tile_x = ? AND tile_y = ? LIMIT 10000"
|
||||
} else {
|
||||
"SELECT id, tags, points FROM map_data.ways WHERE zoom = ? AND tile_x = ? AND tile_y = ?"
|
||||
};
|
||||
let rows_ways = state.scylla_session.query(query_ways, (z, x, y)).await.ok().and_then(|r| r.rows).unwrap_or_default();
|
||||
let mut ways = Vec::new();
|
||||
for row in rows_ways {
|
||||
if let Ok((id, tags, points)) = row.into_typed::<(i64, std::collections::HashMap<String, String>, Vec<u8>)>() {
|
||||
ways.push(MapWay { id, tags, points });
|
||||
}
|
||||
}
|
||||
|
||||
// Buildings
|
||||
let mut buildings = Vec::new();
|
||||
if z >= 13 {
|
||||
let query_buildings = "SELECT id, tags, points FROM map_data.buildings WHERE zoom = ? AND tile_x = ? AND tile_y = ?";
|
||||
let rows_buildings = state.scylla_session.query(query_buildings, (z, x, y)).await.ok().and_then(|r| r.rows).unwrap_or_default();
|
||||
for row in rows_buildings {
|
||||
if let Ok((id, tags, points)) = row.into_typed::<(i64, std::collections::HashMap<String, String>, Vec<u8>)>() {
|
||||
buildings.push(MapWay { id, tags, points });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Landuse
|
||||
let mut landuse = Vec::new();
|
||||
if z >= 4 {
|
||||
let query_landuse = "SELECT id, tags, points FROM map_data.landuse WHERE zoom = ? AND tile_x = ? AND tile_y = ?";
|
||||
let rows_landuse = state.scylla_session.query(query_landuse, (z, x, y)).await.ok().and_then(|r| r.rows).unwrap_or_default();
|
||||
for row in rows_landuse {
|
||||
if let Ok((id, tags, points)) = row.into_typed::<(i64, std::collections::HashMap<String, String>, Vec<u8>)>() {
|
||||
landuse.push(MapWay { id, tags, points });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Water
|
||||
let query_water = if z < 9 {
|
||||
"SELECT id, tags, points FROM map_data.water WHERE zoom = ? AND tile_x = ? AND tile_y = ? LIMIT 10000"
|
||||
} else {
|
||||
"SELECT id, tags, points FROM map_data.water WHERE zoom = ? AND tile_x = ? AND tile_y = ?"
|
||||
};
|
||||
let rows_water = state.scylla_session.query(query_water, (z, x, y)).await.ok().and_then(|r| r.rows).unwrap_or_default();
|
||||
let mut water = Vec::new();
|
||||
for row in rows_water {
|
||||
if let Ok((id, tags, points)) = row.into_typed::<(i64, std::collections::HashMap<String, String>, Vec<u8>)>() {
|
||||
water.push(MapWay { id, tags, points });
|
||||
}
|
||||
}
|
||||
|
||||
// Railways
|
||||
let query_railways = if z < 9 {
|
||||
"SELECT id, tags, points FROM map_data.railways WHERE zoom = ? AND tile_x = ? AND tile_y = ? LIMIT 10000"
|
||||
} else {
|
||||
"SELECT id, tags, points FROM map_data.railways WHERE zoom = ? AND tile_x = ? AND tile_y = ?"
|
||||
};
|
||||
let rows_railways = state.scylla_session.query(query_railways, (z, x, y)).await.ok().and_then(|r| r.rows).unwrap_or_default();
|
||||
let mut railways = Vec::new();
|
||||
for row in rows_railways {
|
||||
if let Ok((id, tags, points)) = row.into_typed::<(i64, std::collections::HashMap<String, String>, Vec<u8>)>() {
|
||||
railways.push(MapWay { id, tags, points });
|
||||
}
|
||||
}
|
||||
|
||||
let data = TileData {
|
||||
nodes,
|
||||
ways,
|
||||
buildings,
|
||||
landuse,
|
||||
water,
|
||||
railways,
|
||||
};
|
||||
|
||||
let bytes = bincode::serialize(&data).unwrap();
|
||||
(
|
||||
[
|
||||
(header::CONTENT_TYPE, "application/octet-stream"),
|
||||
(header::CACHE_CONTROL, "public, max-age=31536000, immutable"),
|
||||
],
|
||||
bytes,
|
||||
).into_response()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user