Skip to content

Commit

Permalink
export projection as PROJJSON (#184)
Browse files Browse the repository at this point in the history
This is a first attempt at solving #183 but the doctest segfaults 😕 .
Does anyone have guidance here?

```
running 1 test
Test executable failed (signal: 11 (SIGSEGV)).
test src/proj.rs - proj::Proj::to_projjson (line 1068) ... FAILED

failures:

failures:
    src/proj.rs - proj::Proj::to_projjson (line 1068)

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 23 filtered out; finished in 0.31s
```

---------

Co-authored-by: Stephan Hügel <[email protected]>
  • Loading branch information
kylebarron and urschrei authored Dec 30, 2024
1 parent fcfc287 commit 5f5f2ad
Showing 1 changed file with 62 additions and 2 deletions.
64 changes: 62 additions & 2 deletions src/proj.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ use libc::c_int;
use libc::{c_char, c_double};
use num_traits::Float;
use proj_sys::{
proj_area_create, proj_area_destroy, proj_area_set_bbox, proj_cleanup, proj_context_create,
proj_context_destroy, proj_context_errno, proj_context_get_url_endpoint,
proj_area_create, proj_area_destroy, proj_area_set_bbox, proj_as_projjson, proj_cleanup,
proj_context_create, proj_context_destroy, proj_context_errno, proj_context_get_url_endpoint,
proj_context_is_network_enabled, proj_context_set_search_paths, proj_context_set_url_endpoint,
proj_create, proj_create_crs_to_crs, proj_destroy, proj_errno_string, proj_get_area_of_use,
proj_grid_cache_set_enable, proj_info, proj_normalize_for_visualization, proj_pj_info,
proj_trans, proj_trans_array, proj_trans_bounds, PJconsts, PJ_AREA, PJ_CONTEXT, PJ_COORD,
PJ_DIRECTION_PJ_FWD, PJ_DIRECTION_PJ_INV, PJ_INFO, PJ_LPZT, PJ_XYZT,
};
use std::ptr;
use std::{
convert, ffi,
fmt::{self, Debug},
Expand Down Expand Up @@ -124,6 +125,8 @@ pub enum ProjError {
DownloadError(String, String, u8),
#[error("The current definition could not be retrieved")]
Definition,
#[error("The definition could not be represented in the requested JSON format")]
ExportToJson,
}

#[cfg(feature = "network")]
Expand Down Expand Up @@ -1072,6 +1075,46 @@ impl Proj {
Err(ProjError::Projection(error_message(err)?))
}
}

/// Return the projjson representation of a transformation
///
/// # Safety
/// This method contains unsafe code.
pub fn to_projjson(
&self,
multiline: Option<bool>,
indentation_width: Option<usize>,
schema: Option<&str>,
) -> Result<String, ProjError> {
let mut opts = vec![];
if let Some(multiline) = multiline {
if multiline {
opts.push(CString::new(String::from("MULTILINE=YES"))?)
} else {
opts.push(CString::new(String::from("MULTILINE=NO"))?)
}
};
if let Some(indentation_width) = indentation_width {
opts.push(CString::new(format!(
"INDENTATION_WIDTH={}",
indentation_width
))?)
}
if let Some(schema) = schema {
opts.push(CString::new(format!("SCHEMA={}", schema))?)
}
let mut opts_ptrs: Vec<_> = opts.iter().map(|cs| cs.as_ptr()).collect();
// we always have to terminate with a null pointer, even if the opts are empty
opts_ptrs.push(ptr::null());
unsafe {
let out_ptr = proj_as_projjson(self.ctx, self.c_proj, opts_ptrs.as_ptr());
if out_ptr.is_null() {
Err(ProjError::ExportToJson)
} else {
Ok(_string(out_ptr)?)
}
}
}
}

impl convert::TryFrom<&str> for Proj {
Expand Down Expand Up @@ -1484,4 +1527,21 @@ mod test {
assert_eq!(area.north, 84.73);
assert!(name.contains("Europe"));
}

#[test]
fn test_projjson() {
let from = "EPSG:2230";
let to = "EPSG:26946";
let ft_to_m = Proj::new_known_crs(from, to, None).unwrap();
// Because libproj has been fussy about passing empty options strings we're testing both
let _ = ft_to_m
.to_projjson(
Some(true),
None,
Some("https://proj.org/schemas/v0.7/projjson.schema.json"),
)
.unwrap();
let _ = ft_to_m.to_projjson(None, None, None).unwrap();
// TODO: do we want to compare one of the results to proj's output?
}
}

0 comments on commit 5f5f2ad

Please sign in to comment.