geos 8.1.0

Rust bindings for GEOS C API
Documentation
use crate::context_handle::PtrWrap;
use crate::enums::{ByteOrder, OutputDimension};
use crate::error::Error;
use crate::{AsRaw, AsRawMut, ContextHandle, ContextHandling, ContextInteractions, GResult, Geom};
use c_vec::CVec;
use geos_sys::*;
use std::convert::TryFrom;
use std::sync::Arc;

/// The `WKBWriter` type is used to generate `HEX` or `WKB` formatted output from [`Geometry`](crate::Geometry).
///
/// # Example
///
/// ```
/// use geos::{ContextHandling, Geom, Geometry, WKBWriter};
///
/// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry");
/// let mut writer = WKBWriter::new_with_context(point_geom.clone_context())
///                            .expect("Failed to create WKBWriter");
///
/// // Output as WKB
/// let v: Vec<u8> = writer.write_wkb(&point_geom).unwrap().into();
/// assert_eq!(Geometry::new_from_wkb(&v).unwrap().to_wkt_precision(1).unwrap(),
///            "POINT (2.5 2.5)");
///
/// // Output as HEX
/// let v: Vec<u8> = writer.write_hex(&point_geom).unwrap().into();
/// assert_eq!(Geometry::new_from_hex(&v).unwrap().to_wkt_precision(1).unwrap(),
///            "POINT (2.5 2.5)");
/// ```
pub struct WKBWriter<'a> {
    ptr: PtrWrap<*mut GEOSWKBWriter>,
    context: Arc<ContextHandle<'a>>,
}

impl<'a> WKBWriter<'a> {
    /// Creates a new `WKBWriter` instance.
    ///
    /// # Example
    ///
    /// ```
    /// use geos::{Geom, Geometry, WKBWriter};
    ///
    /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry");
    /// let mut writer = WKBWriter::new().expect("Failed to create WKBWriter");
    ///
    /// let v: Vec<u8> = writer.write_wkb(&point_geom).unwrap().into();
    /// assert_eq!(Geometry::new_from_wkb(&v).unwrap().to_wkt_precision(1).unwrap(),
    ///            "POINT (2.5 2.5)");
    /// ```
    pub fn new() -> GResult<WKBWriter<'a>> {
        match ContextHandle::init_e(Some("WKBWriter::new")) {
            Ok(context_handle) => Self::new_with_context(Arc::new(context_handle)),
            Err(e) => Err(e),
        }
    }

    /// Creates a new `WKBWriter` instance with a given context.
    ///
    /// # Example
    ///
    /// ```
    /// use geos::{ContextHandling, Geom, Geometry, WKBWriter};
    ///
    /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry");
    /// let mut writer = WKBWriter::new_with_context(point_geom.clone_context())
    ///                            .expect("Failed to create WKBWriter");
    ///
    /// let v: Vec<u8> = writer.write_wkb(&point_geom).unwrap().into();
    /// assert_eq!(Geometry::new_from_wkb(&v).unwrap().to_wkt_precision(1).unwrap(),
    ///            "POINT (2.5 2.5)");
    /// ```
    pub fn new_with_context(context: Arc<ContextHandle<'a>>) -> GResult<WKBWriter<'a>> {
        unsafe {
            let ptr = GEOSWKBWriter_create_r(context.as_raw());
            WKBWriter::new_from_raw(ptr, context, "new_with_context")
        }
    }

    pub(crate) unsafe fn new_from_raw(
        ptr: *mut GEOSWKBWriter,
        context: Arc<ContextHandle<'a>>,
        caller: &str,
    ) -> GResult<WKBWriter<'a>> {
        if ptr.is_null() {
            let extra = if let Some(x) = context.get_last_error() {
                format!("\nLast error: {}", x)
            } else {
                String::new()
            };
            return Err(Error::NoConstructionFromNullPtr(format!(
                "WKBWriter::{}{}",
                caller, extra
            )));
        }
        Ok(WKBWriter {
            ptr: PtrWrap(ptr),
            context,
        })
    }

    /// Writes out the given `geometry` as WKB format.
    ///
    /// # Example
    ///
    /// ```
    /// use geos::{Geometry, WKBWriter};
    ///
    /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry");
    /// let mut writer = WKBWriter::new().expect("Failed to create WKBWriter");
    ///
    /// let v: Vec<u8> = writer.write_wkb(&point_geom).unwrap().into();
    /// let expected = vec![1u8, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 64, 0, 0, 0, 0, 0, 0, 4, 64];
    /// assert_eq!(v, expected);
    /// ```
    pub fn write_wkb<'b, G: Geom<'b>>(&mut self, geometry: &G) -> GResult<CVec<u8>> {
        let mut size = 0;
        unsafe {
            let ptr = GEOSWKBWriter_write_r(
                self.get_raw_context(),
                self.as_raw_mut(),
                geometry.as_raw(),
                &mut size,
            );
            if ptr.is_null() {
                Err(Error::NoConstructionFromNullPtr(
                    "WKBWriter::write_wkb failed: GEOSWKBWriter_writeHEX_r returned null pointer"
                        .to_owned(),
                ))
            } else {
                Ok(CVec::new(ptr, size as _))
            }
        }
    }

    /// Writes out the given `geometry` as WKB format.
    ///
    /// # Example
    ///
    /// ```
    /// use geos::{Geometry, WKBWriter};
    ///
    /// let point_geom = Geometry::new_from_wkt("POINT (2.5 2.5)").expect("Invalid geometry");
    /// let mut writer = WKBWriter::new().expect("Failed to create WKBWriter");
    ///
    /// let v: Vec<u8> = writer.write_hex(&point_geom).unwrap().into();
    /// let expected = vec![48u8,49,48,49,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,
    ///                     52,52,48,48,48,48,48,48,48,48,48,48,48,48,48,48,52,52,48];
    /// assert_eq!(v, expected);
    /// ```
    pub fn write_hex<'b, G: Geom<'b>>(&mut self, geometry: &G) -> GResult<CVec<u8>> {
        let mut size = 0;
        unsafe {
            let ptr = GEOSWKBWriter_writeHEX_r(
                self.get_raw_context(),
                self.as_raw_mut(),
                geometry.as_raw(),
                &mut size,
            );
            if ptr.is_null() {
                Err(Error::NoConstructionFromNullPtr(
                    "WKBWriter::write_hex failed: GEOSWKBWriter_writeHEX_r returned null pointer"
                        .to_owned(),
                ))
            } else {
                Ok(CVec::new(ptr, size as _))
            }
        }
    }

    /// Sets the number of dimensions to be used when calling [`WKBWriter::write_wkb`] or
    /// [`WKBWriter::write_hex`]. By default, it is 2.
    ///
    /// # Example
    ///
    /// ```
    /// use geos::{Geometry, OutputDimension, WKBWriter, WKTWriter};
    ///
    /// let mut wkt_writer = WKTWriter::new().expect("Failed to create WKTWriter");
    /// wkt_writer.set_output_dimension(OutputDimension::ThreeD);
    /// wkt_writer.set_trim(true);
    ///
    /// let point_geom = Geometry::new_from_wkt("POINT (1.1 2.2 3.3)").expect("Invalid geometry");
    /// let mut writer = WKBWriter::new().expect("Failed to create WKBWriter");
    ///
    /// let v: Vec<u8> = writer.write_wkb(&point_geom).unwrap().into();
    /// let geom = Geometry::new_from_wkb(&v).unwrap();
    /// assert_eq!(wkt_writer.write(&geom).unwrap(), "POINT (1.1 2.2)");
    ///
    /// writer.set_output_dimension(OutputDimension::ThreeD);
    /// let v: Vec<u8> = writer.write_wkb(&point_geom).unwrap().into();
    /// let geom = Geometry::new_from_wkb(&v).unwrap();
    /// assert_eq!(wkt_writer.write(&geom).unwrap(), "POINT Z (1.1 2.2 3.3)");
    /// ```
    pub fn set_output_dimension(&mut self, dimension: OutputDimension) {
        unsafe {
            GEOSWKBWriter_setOutputDimension_r(
                self.get_raw_context(),
                self.as_raw_mut(),
                dimension.into(),
            )
        }
    }

    /// Returns the number of dimensions to be used when calling [`WKBWriter::write_wkb`].
    /// By default, it is 2.
    ///
    /// # Example
    ///
    /// ```
    /// use geos::{OutputDimension, WKBWriter};
    ///
    /// let mut writer = WKBWriter::new().expect("Failed to create WKBWriter");
    ///
    /// assert_eq!(writer.get_out_dimension(), Ok(OutputDimension::TwoD));
    /// writer.set_output_dimension(OutputDimension::ThreeD);
    /// assert_eq!(writer.get_out_dimension(), Ok(OutputDimension::ThreeD));
    /// ```
    pub fn get_out_dimension(&self) -> GResult<OutputDimension> {
        unsafe {
            let out = GEOSWKBWriter_getOutputDimension_r(self.get_raw_context(), self.as_raw());
            OutputDimension::try_from(out).map_err(|e| Error::GenericError(e.to_owned()))
        }
    }

    /// Gets WKB byte order.
    ///
    /// # Example
    ///
    /// ```
    /// use geos::{ByteOrder, WKBWriter};
    ///
    /// let mut writer = WKBWriter::new().expect("Failed to create WKBWriter");
    ///
    /// writer.set_wkb_byte_order(ByteOrder::LittleEndian);
    /// assert_eq!(writer.get_wkb_byte_order(), Ok(ByteOrder::LittleEndian));
    /// ```
    pub fn get_wkb_byte_order(&self) -> GResult<ByteOrder> {
        unsafe {
            let out = GEOSWKBWriter_getByteOrder_r(self.get_raw_context(), self.as_raw());
            ByteOrder::try_from(out).map_err(|e| Error::GenericError(e.to_owned()))
        }
    }

    /// Sets WKB byte order.
    ///
    /// # Example
    ///
    /// ```
    /// use geos::{WKBWriter, ByteOrder};
    ///
    /// let mut writer = WKBWriter::new().expect("Failed to create WKBWriter");
    ///
    /// writer.set_wkb_byte_order(ByteOrder::LittleEndian);
    /// assert_eq!(writer.get_wkb_byte_order(), Ok(ByteOrder::LittleEndian));
    /// ```
    pub fn set_wkb_byte_order(&mut self, byte_order: ByteOrder) {
        unsafe {
            GEOSWKBWriter_setByteOrder_r(
                self.get_raw_context(),
                self.as_raw_mut(),
                byte_order.into(),
            )
        }
    }

    /// Gets if output will include SRID.
    ///
    /// # Example
    ///
    /// ```
    /// use geos::WKBWriter;
    ///
    /// let mut writer = WKBWriter::new().expect("Failed to create WKBWriter");
    ///
    /// writer.set_include_SRID(true);
    /// assert_eq!(writer.get_include_SRID(), Ok(true));
    /// ```
    #[allow(non_snake_case)]
    pub fn get_include_SRID(&self) -> GResult<bool> {
        unsafe {
            let out = GEOSWKBWriter_getIncludeSRID_r(self.get_raw_context(), self.as_raw());
            if out < 0 {
                Err(Error::GenericError(
                    "GEOSWKBWriter_getIncludeSRID_r failed".to_owned(),
                ))
            } else {
                Ok(out != 0)
            }
        }
    }

    /// Sets if output will include SRID.
    ///
    /// # Example
    ///
    /// ```
    /// use geos::WKBWriter;
    ///
    /// let mut writer = WKBWriter::new().expect("Failed to create WKBWriter");
    ///
    /// writer.set_include_SRID(true);
    /// assert_eq!(writer.get_include_SRID(), Ok(true));
    /// ```
    #[allow(non_snake_case)]
    pub fn set_include_SRID(&mut self, include_SRID: bool) {
        unsafe {
            GEOSWKBWriter_setIncludeSRID_r(
                self.get_raw_context(),
                self.as_raw_mut(),
                include_SRID as _,
            )
        }
    }
}

unsafe impl<'a> Send for WKBWriter<'a> {}
unsafe impl<'a> Sync for WKBWriter<'a> {}

impl<'a> Drop for WKBWriter<'a> {
    fn drop(&mut self) {
        unsafe { GEOSWKBWriter_destroy_r(self.get_raw_context(), self.as_raw_mut()) };
    }
}

impl<'a> ContextInteractions<'a> for WKBWriter<'a> {
    /// Set the context handle to the `WKBWriter`.
    ///
    /// ```
    /// use geos::{ContextInteractions, ContextHandle, WKBWriter};
    ///
    /// let context_handle = ContextHandle::init().expect("invalid init");
    /// let mut writer = WKBWriter::new().expect("failed to create WKT writer");
    /// context_handle.set_notice_message_handler(Some(Box::new(|s| println!("new message: {}", s))));
    /// writer.set_context_handle(context_handle);
    /// ```
    fn set_context_handle(&mut self, context: ContextHandle<'a>) {
        self.context = Arc::new(context);
    }

    /// Get the context handle of the `WKBWriter`.
    ///
    /// ```
    /// use geos::{ContextInteractions, WKBWriter};
    ///
    /// let mut writer = WKBWriter::new().expect("failed to create WKT writer");
    /// let context = writer.get_context_handle();
    /// context.set_notice_message_handler(Some(Box::new(|s| println!("new message: {}", s))));
    /// ```
    fn get_context_handle(&self) -> &ContextHandle<'a> {
        &self.context
    }
}

impl<'a> AsRaw for WKBWriter<'a> {
    type RawType = GEOSWKBWriter;

    fn as_raw(&self) -> *const Self::RawType {
        *self.ptr
    }
}

impl<'a> AsRawMut for WKBWriter<'a> {
    type RawType = GEOSWKBWriter;

    unsafe fn as_raw_mut_override(&self) -> *mut Self::RawType {
        *self.ptr
    }
}

impl<'a> ContextHandling for WKBWriter<'a> {
    type Context = Arc<ContextHandle<'a>>;

    fn get_raw_context(&self) -> GEOSContextHandle_t {
        self.context.as_raw()
    }

    fn clone_context(&self) -> Arc<ContextHandle<'a>> {
        Arc::clone(&self.context)
    }
}