Merge pull request #79 from scd31/world_scale

allow custom world scales
This commit is contained in:
Louis Erbkamm
2024-10-27 12:53:03 +01:00
committed by GitHub
2 changed files with 46 additions and 55 deletions

View File

@@ -29,6 +29,10 @@ pub struct Args {
#[arg(long, default_value = "requests")]
pub downloader: String,
/// World scale to use, in blocks per meter
#[arg(long, default_value = "1.0")]
pub scale: f64,
/// Enable debug mode (optional)
#[arg(long, default_value_t = false, action = clap::ArgAction::SetTrue)]
pub debug: bool,

View File

@@ -116,36 +116,20 @@ fn lat_lon_to_minecraft_coords(
lat: f64,
lon: f64,
bbox: (f64, f64, f64, f64), // (min_lon, min_lat, max_lon, max_lat)
scale_factor_x: f64,
scale_factor_z: f64,
scale_factor_x: f64,
) -> (i32, i32) {
let (min_lon, min_lat, max_lon, max_lat) = bbox;
// Calculate the relative position within the bounding box
let rel_x: f64 = 1.0 - (lat - min_lat) / (max_lat - min_lat);
let rel_z: f64 = (lon - min_lon) / (max_lon - min_lon);
let rel_x: f64 = (lon - min_lon) / (max_lon - min_lon);
let rel_z: f64 = 1.0 - (lat - min_lat) / (max_lat - min_lat);
// Apply scaling factors for each dimension and convert to Minecraft coordinates
let x: i32 = (rel_x * scale_factor_x) as i32;
let z: i32 = (rel_z * scale_factor_z) as i32;
(z, x) // Swap x and z coords to avoid a mirrored projection on the Minecraft map
}
/// Function to determine the number of decimal places in a float as a string
fn count_decimal_places(value: f64) -> usize {
let s: String = value.to_string();
if let Some(pos) = s.find('.') {
s.len() - pos - 1 // Number of digits after the decimal point
} else {
0
}
}
/// Function to convert f64 to an integer based on the number of decimal places
fn convert_to_scaled_int(value: f64, max_decimal_places: usize) -> i64 {
let multiplier: i64 = 10_i64.pow(max_decimal_places as u32); // Compute multiplier
(value * multiplier as f64).round() as i64 // Scale and convert to integer
(x, z)
}
pub fn parse_osm_data(
@@ -159,40 +143,10 @@ pub fn parse_osm_data(
let data: OsmData =
serde_json::from_value(json_data.clone()).expect("Failed to parse OSM data");
// Calculate the maximum number of decimal places in bbox elements
let max_decimal_places: usize = [
count_decimal_places(bbox.0),
count_decimal_places(bbox.1),
count_decimal_places(bbox.2),
count_decimal_places(bbox.3),
]
.into_iter()
.max()
.unwrap();
// Convert each element to a scaled integer
let bbox_scaled: (i64, i64, i64, i64) = (
convert_to_scaled_int(bbox.0, max_decimal_places),
convert_to_scaled_int(bbox.1, max_decimal_places),
convert_to_scaled_int(bbox.2, max_decimal_places),
convert_to_scaled_int(bbox.3, max_decimal_places),
);
// Determine which dimension is larger and assign scale factors accordingly
let (scale_factor_x, scale_factor_z) =
if (bbox_scaled.2 - bbox_scaled.0) > (bbox_scaled.3 - bbox_scaled.1) {
// Longitude difference is greater than latitude difference
(
((bbox_scaled.3 - bbox_scaled.1) * 14 / 100) as f64, // Scale for width (x) is based on latitude difference
((bbox_scaled.2 - bbox_scaled.0) * 10 / 100) as f64, // Scale for length (z) is based on longitude difference
)
} else {
// Latitude difference is greater than or equal to longitude difference
(
((bbox_scaled.2 - bbox_scaled.0) * 14 / 100) as f64, // Scale for width (x) is based on longitude difference
((bbox_scaled.3 - bbox_scaled.1) * 10 / 100) as f64, // Scale for length (z) is based on latitude difference
)
};
let (scale_factor_z, scale_factor_x) = geo_distance(bbox.1, bbox.3, bbox.0, bbox.2);
let scale_factor_z = scale_factor_z.floor() * args.scale;
let scale_factor_x = scale_factor_x.floor() * args.scale;
if args.debug {
println!("Scale factor X: {}", scale_factor_x);
@@ -209,7 +163,7 @@ pub fn parse_osm_data(
if element.r#type == "node" {
if let (Some(lat), Some(lon)) = (element.lat, element.lon) {
let (x, z) =
lat_lon_to_minecraft_coords(lat, lon, bbox, scale_factor_x, scale_factor_z);
lat_lon_to_minecraft_coords(lat, lon, bbox, scale_factor_z, scale_factor_x);
let processed = ProcessedNode {
id: element.id,
@@ -309,7 +263,7 @@ pub fn parse_osm_data(
}));
}
(processed_elements, scale_factor_z, scale_factor_x)
(processed_elements, scale_factor_x, scale_factor_z)
}
const PRIORITY_ORDER: [&str; 6] = [
@@ -327,3 +281,36 @@ pub fn get_priority(element: &ProcessedElement) -> usize {
// Return a default priority if none of the tags match
PRIORITY_ORDER.len()
}
// (lat meters, lon meters)
fn geo_distance(lat1: f64, lat2: f64, lon1: f64, lon2: f64) -> (f64, f64) {
let z = lat_distance(lat1, lat2);
// distance between two lons depends on their latitude. In this case we'll just average them
let x = lon_distance((lat1 + lat2) / 2.0, lon1, lon2);
(z, x)
}
// Haversine but optimized for a latitude delta of 0
// returns meters
fn lon_distance(lat: f64, lon1: f64, lon2: f64) -> f64 {
const R: f64 = 6371_000.0;
let d_lon = (lon2 - lon1).to_radians();
let a =
lat.to_radians().cos() * lat.to_radians().cos() * (d_lon / 2.0).sin() * (d_lon / 2.0).sin();
let c = 2.0 * a.sqrt().atan2((1.0 - a).sqrt());
R * c
}
// Haversine but optimized for a longitude delta of 0
// returns meters
fn lat_distance(lat1: f64, lat2: f64) -> f64 {
const R: f64 = 6371_000.0;
let d_lat = (lat2 - lat1).to_radians();
let a = (d_lat / 2.0).sin() * (d_lat / 2.0).sin();
let c = 2.0 * a.sqrt().atan2((1.0 - a).sqrt());
R * c
}