Skip to content

Commit d1f142f

Browse files
teunbrandclaude
andauthored
Fix panel boundary missing for lon/lat map projections (#482)
When geometries lack an explicit SRID (e.g. bundled parquet datasets), resolve_epsg_property returned None and the fallback "EPSG:4326" string was stored directly without PROJ resolution. This left the coord as UnknownProj, skipping clip boundary and panel background computation. Add a fallback parameter to resolve_epsg_property so absent properties go through the same EPSG→PROJ lookup, ensuring the coord rebuilds correctly as Geographic. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 477536f commit d1f142f

1 file changed

Lines changed: 50 additions & 11 deletions

File tree

  • src/plot/projection/coord

src/plot/projection/coord/map.rs

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -728,10 +728,8 @@ pub(crate) fn resolve_map_projection(
728728
}
729729

730730
// Step 2: Resolve source and target from numeric EPSG to PROJ strings
731-
let source = resolve_epsg_property("source", properties, execute_query)
732-
.unwrap_or_else(|| "EPSG:4326".to_string());
733-
let target = resolve_epsg_property("target", properties, execute_query)
734-
.unwrap_or_else(|| source.clone());
731+
let source = resolve_epsg_property("source", properties, "EPSG:4326", execute_query);
732+
let target = resolve_epsg_property("target", properties, &source, execute_query);
735733

736734
properties.insert("source".to_string(), ParameterValue::String(source.clone()));
737735
properties.insert("target".to_string(), ParameterValue::String(target.clone()));
@@ -776,27 +774,31 @@ pub(crate) fn resolve_map_projection(
776774

777775
/// If `key` holds a numeric EPSG code or an `"EPSG:N"` string, resolve it to a PROJ
778776
/// string. Falls back to `"EPSG:N"` format when no PROJ string can be found (the
779-
/// database engine may still handle it). Returns `None` when the property is absent.
777+
/// database engine may still handle it). When the property is absent, resolves
778+
/// `fallback` through the same lookup path.
780779
fn resolve_epsg_property(
781780
key: &str,
782781
properties: &Parameters,
782+
fallback: &str,
783783
execute_query: &dyn Fn(&str) -> crate::Result<DataFrame>,
784-
) -> Option<String> {
784+
) -> String {
785785
let code: u32 = match properties.get(key) {
786786
Some(ParameterValue::Number(n)) => *n as u32,
787787
Some(ParameterValue::String(s)) => {
788788
match s.strip_prefix("EPSG:").and_then(|n| n.parse().ok()) {
789789
Some(c) => c,
790790
// Raw PROJ strings (`+proj=foo`) don't have EPSG prefixes
791-
None => return Some(s.clone()),
791+
None => return s.clone(),
792792
}
793793
}
794-
_ => return None,
794+
_ => match fallback.strip_prefix("EPSG:").and_then(|n| n.parse().ok()) {
795+
Some(c) => c,
796+
None => return fallback.to_string(),
797+
},
795798
};
796-
let resolved = query_spatial_ref_sys(code, execute_query)
799+
query_spatial_ref_sys(code, execute_query)
797800
.or_else(|| builtin_epsg_lookup(code))
798-
.unwrap_or_else(|| format!("EPSG:{code}"));
799-
Some(resolved)
801+
.unwrap_or_else(|| format!("EPSG:{code}"))
800802
}
801803

802804
fn query_spatial_ref_sys(
@@ -1145,4 +1147,41 @@ mod tests {
11451147
fn epsg_unknown_code() {
11461148
assert_eq!(builtin_epsg_lookup(99999), None);
11471149
}
1150+
1151+
fn noop_execute(_sql: &str) -> crate::Result<DataFrame> {
1152+
Err(crate::GgsqlError::InternalError("no db".into()))
1153+
}
1154+
1155+
#[test]
1156+
fn resolve_epsg_property_from_existing() {
1157+
let mut props = Parameters::new();
1158+
props.insert(
1159+
"source".to_string(),
1160+
ParameterValue::String("EPSG:4326".to_string()),
1161+
);
1162+
let result = resolve_epsg_property("source", &props, "EPSG:4326", &noop_execute);
1163+
assert!(result.contains("+proj=longlat"), "got: {result}");
1164+
}
1165+
1166+
#[test]
1167+
fn resolve_epsg_property_uses_fallback_when_absent() {
1168+
let props = Parameters::new();
1169+
let result = resolve_epsg_property("source", &props, "EPSG:4326", &noop_execute);
1170+
assert!(
1171+
result.contains("+proj=longlat"),
1172+
"fallback should resolve EPSG:4326 to PROJ string, got: {result}"
1173+
);
1174+
}
1175+
1176+
#[test]
1177+
fn resolve_epsg_property_fallback_proj_string_passthrough() {
1178+
let props = Parameters::new();
1179+
let result = resolve_epsg_property(
1180+
"target",
1181+
&props,
1182+
"+proj=merc +lon_0=0 +k=1 +x_0=0 +y_0=0",
1183+
&noop_execute,
1184+
);
1185+
assert_eq!(result, "+proj=merc +lon_0=0 +k=1 +x_0=0 +y_0=0");
1186+
}
11481187
}

0 commit comments

Comments
 (0)