use core::ffi::CStr;
use core::mem::MaybeUninit;
use std::ffi::CString;
use libc::{c_char, c_int};
use lwgeom_sys::*;
use crate::foreign_type::{ForeignType, ForeignTypeRef, Opaque};
use crate::lwcollection::LWCollectionRef;
use crate::lwgeom_parser_result::LWGeomParserResult;
use crate::lwpoly::LWPoly;
use crate::{GBox, LWGeomError, Result};
pub struct LWGeom(*mut LWGEOM);
impl ForeignType for LWGeom {
type FFIType = LWGEOM;
fn from_ptr(ptr: *mut Self::FFIType) -> Self {
debug_assert!(
!ptr.is_null(),
"Attempted to create a LWGeom from a null pointer."
);
Self(ptr)
}
fn as_mut_ptr(&mut self) -> *mut Self::FFIType {
self.0
}
fn as_ptr(&self) -> *const Self::FFIType {
self.0.cast_const()
}
}
unsafe impl Send for LWGeom {}
unsafe impl Sync for LWGeom {}
impl Drop for LWGeom {
fn drop(&mut self) {
unsafe { lwgeom_free(self.as_mut_ptr()) }
}
}
pub struct LWGeomRef(Opaque);
impl ForeignTypeRef for LWGeomRef {
type FFIType = LWGEOM;
}
unsafe impl Send for LWGeomRef {}
unsafe impl Sync for LWGeomRef {}
impl LWGeom {
pub fn from_text(wkt: &str, srid: Option<i32>) -> Result<Self> {
let c_wkt = CString::new(wkt)?;
let mut parser_result: MaybeUninit<LWGEOM_PARSER_RESULT> = MaybeUninit::uninit();
let result = unsafe {
lwgeom_parse_wkt(
parser_result.as_mut_ptr(),
c_wkt.as_ptr().cast_mut(),
LW_PARSER_CHECK_ALL as c_int,
)
};
let mut parser_result = LWGeomParserResult::from_ptr(parser_result.as_mut_ptr());
if result == LW_FAILURE as c_int {
return Err(LWGeomError::WKTParseError(parser_result.message().ok_or(
LWGeomError::FailedWithoutMessageError("lwgeom_parse_wkt".to_owned()),
)?));
}
let mut geom = parser_result.take_geom();
if geom.has_srid() {
panic!("OGC WKT expected, EWKT provided - use from_ewkt() for this")
}
if let Some(srid) = srid {
geom.set_srid(srid);
}
Ok(geom)
}
pub fn from_ewkt(wkt: &str) -> Result<Self> {
let c_wkt = CString::new(wkt)?;
let mut parser_result: MaybeUninit<LWGEOM_PARSER_RESULT> = MaybeUninit::uninit();
let result = unsafe {
lwgeom_parse_wkt(
parser_result.as_mut_ptr(),
c_wkt.as_ptr().cast_mut(),
LW_PARSER_CHECK_ALL as c_int,
)
};
let mut parser_result = LWGeomParserResult::from_ptr(parser_result.as_mut_ptr());
if result == LW_FAILURE as c_int {
return Err(LWGeomError::WKTParseError(parser_result.message().ok_or(
LWGeomError::FailedWithoutMessageError("lwgeom_parse_wkt".to_owned()),
)?));
}
Ok(parser_result.take_geom())
}
pub fn from_ewkb(ewkb: &[u8]) -> Result<Self> {
let p_geom =
unsafe { lwgeom_from_wkb(ewkb.as_ptr(), ewkb.len(), LW_PARSER_CHECK_ALL as c_char) };
if p_geom.is_null() {
return Err(LWGeomError::NullPtrError);
}
Ok(Self::from_ptr(p_geom))
}
}
impl LWGeom {
pub fn as_text(&self, precision: Option<i32>) -> Result<String> {
let precision = precision.unwrap_or(15);
let mut sz = MaybeUninit::uninit();
let p_wkt =
unsafe { lwgeom_to_wkt(self.as_ptr(), WKT_ISO as u8, precision, sz.as_mut_ptr()) };
if p_wkt.is_null() {
return Err(LWGeomError::NullPtrError);
}
let c_wkt = unsafe {
CStr::from_bytes_with_nul_unchecked(core::slice::from_raw_parts(
p_wkt.cast(),
sz.assume_init(),
))
};
let text = c_wkt.to_string_lossy().into_owned();
unsafe {
lwfree(p_wkt.cast());
}
Ok(text)
}
pub fn as_ewkt(&self, precision: Option<i32>) -> Result<String> {
let precision = precision.unwrap_or(15);
let mut sz = MaybeUninit::uninit();
let p_wkt = unsafe {
lwgeom_to_wkt(
self.as_ptr(),
WKT_EXTENDED as u8,
precision,
sz.as_mut_ptr(),
)
};
if p_wkt.is_null() {
return Err(LWGeomError::NullPtrError);
}
let c_wkt = unsafe {
CStr::from_bytes_with_nul_unchecked(core::slice::from_raw_parts(
p_wkt.cast(),
sz.assume_init(),
))
};
let ewkt = c_wkt.to_string_lossy().into_owned();
unsafe {
lwfree(p_wkt.cast());
}
Ok(ewkt)
}
pub fn as_ewkb(&self) -> Result<Vec<u8>> {
let p_varlena = unsafe { lwgeom_to_wkb_varlena(self.as_ptr(), WKB_EXTENDED as u8) };
if p_varlena.is_null() {
return Err(LWGeomError::NullPtrError);
}
let ewkb_slice = unsafe {
core::slice::from_raw_parts(
(*p_varlena).data.as_ptr().cast(),
lwsize_get((*p_varlena).size) as usize - LWVARHDRSZ,
)
};
let ewkb = ewkb_slice.to_vec();
unsafe {
lwfree(p_varlena.cast());
}
Ok(ewkb)
}
}
impl LWGeom {
pub fn has_srid(&self) -> bool {
unsafe { lwgeom_has_srid(self.as_ptr()) != 0 }
}
pub fn get_srid(&self) -> Option<i32> {
if self.has_srid() {
Some(unsafe { lwgeom_get_srid(self.as_ptr()) })
} else {
None
}
}
pub fn set_srid(&mut self, srid: i32) {
unsafe { lwgeom_set_srid(self.as_mut_ptr(), srid) }
}
pub fn split(&self, blade: &LWGeom) -> Self {
let p_geom = unsafe { lwgeom_split(self.as_ptr(), blade.as_ptr()) };
Self::from_ptr(p_geom)
}
pub fn calculate_bbox(&self) -> GBox {
let mut bbox = GBox::new_bbox();
unsafe { lwgeom_calculate_gbox(self.as_ptr(), bbox.as_mut_ptr()) };
bbox
}
pub fn tile_envelope(
zoom: i32, x: i32, y: i32, bounds: Option<&LWGeom>, margin: Option<f64>,
) -> Result<Self> {
let bounds = match bounds {
Some(bounds) => bounds,
None => &Self::from_ewkt("SRID=3857;LINESTRING(-20037508.342789 -20037508.342789,20037508.342789 20037508.342789)").unwrap(),
};
let bbox = bounds.calculate_bbox();
let srid = bounds.get_srid().unwrap_or(3857);
let margin = margin.unwrap_or(0.0);
if margin < -0.5 {
return Err(LWGeomError::InvalidParameterError(
"ST_TileEnvelope".to_owned(),
"margin".to_owned(),
));
}
let bounds_width = bbox.xmax() - bbox.xmin();
let bounds_height = bbox.ymax() - bbox.ymin();
if bounds_width <= 0.0 || bounds_height <= 0.0 {
return Err(LWGeomError::InvalidParameterError(
"ST_TileEnvelope".to_owned(),
"bounds".to_owned(),
));
}
if !(0..32).contains(&zoom) {
return Err(LWGeomError::InvalidParameterError(
"ST_TileEnvelope".to_owned(),
"zoom".to_owned(),
));
}
let world_tile_size = 1 << zoom.min(31);
if x < 0 || x >= world_tile_size {
return Err(LWGeomError::InvalidParameterError(
"ST_TileEnvelope".to_owned(),
"x".to_owned(),
));
}
if y < 0 || y >= world_tile_size {
return Err(LWGeomError::InvalidParameterError(
"ST_TileEnvelope".to_owned(),
"y".to_owned(),
));
}
let tile_geo_size_x = bounds_width / world_tile_size as f64;
let tile_geo_size_y = bounds_height / world_tile_size as f64;
let (x1, x2) = if (1.0 + margin * 2.0) > world_tile_size as f64 {
(bbox.xmin(), bbox.xmax())
} else {
(
bbox.xmin() + tile_geo_size_x * (x as f64 - margin),
bbox.xmin() + tile_geo_size_x * (x as f64 + 1.0 + margin),
)
};
let mut y1 = bbox.ymax() - tile_geo_size_y * (y as f64 + 1.0 + margin);
let mut y2 = bbox.ymax() - tile_geo_size_y * (y as f64 - margin);
if y1 < bbox.ymin() {
y1 = bbox.ymin()
}
if y2 > bbox.ymax() {
y2 = bbox.ymax()
}
Ok(LWPoly::construct_envelope(srid, x1, y1, x2, y2).into_lwgeom())
}
pub fn n_points(&self) -> u32 {
unsafe { lwgeom_count_vertices(self.as_ptr()) }
}
}
impl LWGeomRef {
pub fn has_srid(&self) -> bool {
unsafe { lwgeom_has_srid(self.as_ptr()) != 0 }
}
pub fn get_srid(&self) -> Option<i32> {
if self.has_srid() {
Some(unsafe { lwgeom_get_srid(self.as_ptr()) })
} else {
None
}
}
pub fn set_srid(&mut self, srid: i32) {
unsafe { lwgeom_set_srid(self.as_mut_ptr(), srid) }
}
pub fn calculate_bbox(&self) -> GBox {
let mut bbox = GBox::new_bbox();
unsafe { lwgeom_calculate_gbox(self.as_ptr(), bbox.as_mut_ptr()) };
bbox
}
}
impl LWGeom {
pub fn as_lwcollection(&self) -> &LWCollectionRef {
let p_collection = unsafe { lwgeom_as_lwcollection(self.as_ptr()) };
LWCollectionRef::from_ptr(p_collection)
}
pub fn get_type(&self) -> u32 {
unsafe { lwgeom_get_type(self.as_ptr()) }
}
pub fn is_collection(&self) -> bool {
unsafe { lwgeom_is_collection(self.as_ptr()) != LW_FALSE as c_int }
}
}
impl LWGeomRef {
pub fn as_lwcollection(&self) -> &LWCollectionRef {
let p_collection = unsafe { lwgeom_as_lwcollection(self.as_ptr()) };
LWCollectionRef::from_ptr(p_collection)
}
pub fn get_type(&self) -> u32 {
unsafe { lwgeom_get_type(self.as_ptr()) }
}
pub fn is_collection(&self) -> bool {
unsafe { lwgeom_is_collection(self.as_ptr()) != LW_FALSE as c_int }
}
}
impl LWGeom {
pub fn is_empty(&self) -> bool {
unsafe { lwgeom_is_empty(self.as_ptr()) != LW_FALSE as c_int }
}
}
impl LWGeomRef {
pub fn is_empty(&self) -> bool {
unsafe { lwgeom_is_empty(self.as_ptr()) != LW_FALSE as c_int }
}
}