use serde_json::{json, Value}; pub fn generate_preset_options(preset: &str, epsg: i64, has_gcp: bool, nb_images: usize) -> Value { let srs = format!("EPSG:{}", epsg); // Détection internationale pieds US vs mètres let is_feet = (3400..=3460).contains(&epsg) || (2225..=2285).contains(&epsg); let resolution_value: f64 = if is_feet { 0.065 } else { 2.0 }; // Split-merge si > 80 images → RAM divisée par 3 let use_split = nb_images > 80; let (mut fq, pq, min_feat) = match preset { "PRECISION" => ("ultra", "low", 10000), "RAPIDE" => ("medium", "low", 6000), _ => ("medium", "low", 8000), // STANDARD — medium = 2x plus rapide }; // GCP présent → feature ultra obligatoire, jamais dégradé if has_gcp { fq = "ultra"; } let mut opts = vec![ json!({"name":"feature-quality", "value":fq}), json!({"name":"pc-quality", "value":pq}), json!({"name":"min-num-features", "value":min_feat}), json!({"name":"matcher-type", "value":"flann"}), json!({"name":"out-crs", "value":&srs}), json!({"name":"dsm-out-srs", "value":&srs}), json!({"name":"orthophoto-resolution", "value":resolution_value}), json!({"name":"dsm", "value":true}), json!({"name":"dtm", "value":true}), json!({"name":"smrf-threshold", "value":0.5}), json!({"name":"smrf-scalar", "value":1.2}), json!({"name":"pc-copc", "value":true}), json!({"name":"pc-filter", "value":2.0}), json!({"name":"pc-sample", "value":0.05}), json!({"name":"max-concurrency", "value":4}), json!({"name":"fast-orthophoto", "value": preset == "RAPIDE" && !has_gcp}), // Skip maillage 3D pour RAPIDE sans GCP seulement json!({"name":"texturing-skip-global-seam-leveling", "value":true}), json!({"name":"mesh-size", "value":200000}), ]; // Split-merge anti-OOM pour gros projets if use_split { opts.extend(vec![ json!({"name":"split", "value":75}), json!({"name":"split-overlap", "value":15}), ]); } // Paramètres GCP avancés if has_gcp { opts.extend(vec![ json!({"name":"gcp-error-threshold", "value":0.01}), json!({"name":"optimize-camera-intrinsics", "value":"all"}), json!({"name":"primary-ranking", "value":"none"}), ]); } Value::Array(opts) } pub fn calculate_preset_hash(options: &Value) -> String { use sha2::{Sha256, Digest}; let mut h = Sha256::new(); h.update(options.to_string().as_bytes()); format!("{:x}", h.finalize()) }