Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds Ewkt dialect #155

Merged
merged 15 commits into from
Aug 12, 2023
28 changes: 9 additions & 19 deletions geozero-shp/tests/reader.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use dbase::FieldValue;
use geozero::geojson::GeoJsonWriter;
use geozero::wkt::WktWriter;
use geozero::{FeatureProperties, ProcessorSink};
use geozero::{CoordDimensions, FeatureProperties, ProcessorSink};
use std::fs::File;
use std::io::BufReader;
use std::str::from_utf8;
Expand Down Expand Up @@ -178,8 +178,7 @@ fn point() -> Result<(), geozero_shp::Error> {
fn pointzm() -> Result<(), geozero_shp::Error> {
let reader = geozero_shp::Reader::from_path("./tests/data/pointm.shp")?;
let mut wkt_data: Vec<u8> = Vec::new();
let mut writer = WktWriter::new(&mut wkt_data);
writer.dims.m = true;
let mut writer = WktWriter::with_dims(&mut wkt_data, CoordDimensions::xym());
reader.iter_geometries(&mut writer).next();
assert_eq!(
from_utf8(&wkt_data).unwrap(),
Expand All @@ -188,8 +187,7 @@ fn pointzm() -> Result<(), geozero_shp::Error> {

let reader = geozero_shp::Reader::from_path("./tests/data/pointz.shp")?;
let mut wkt_data: Vec<u8> = Vec::new();
let mut writer = WktWriter::new(&mut wkt_data);
writer.dims.z = true;
let mut writer = WktWriter::with_dims(&mut wkt_data, CoordDimensions::xyz());
reader.iter_geometries(&mut writer).next();
assert_eq!(
from_utf8(&wkt_data).unwrap(),
Expand All @@ -213,8 +211,7 @@ fn multipoint() -> Result<(), geozero_shp::Error> {
fn multipointzm() -> Result<(), geozero_shp::Error> {
let reader = geozero_shp::Reader::from_path("./tests/data/multipointz.shp")?;
let mut wkt_data: Vec<u8> = Vec::new();
let mut writer = WktWriter::new(&mut wkt_data);
writer.dims.z = true;
let mut writer = WktWriter::with_dims(&mut wkt_data, CoordDimensions::xyz());
reader.iter_geometries(&mut writer).next();
assert_eq!(
from_utf8(&wkt_data).unwrap(),
Expand All @@ -241,9 +238,7 @@ fn line() -> Result<(), geozero_shp::Error> {
fn linezm() -> Result<(), geozero_shp::Error> {
let reader = geozero_shp::Reader::from_path("./tests/data/linez.shp")?;
let mut wkt_data: Vec<u8> = Vec::new();
let mut writer = WktWriter::new(&mut wkt_data);
writer.dims.z = true;
writer.dims.m = true;
let mut writer = WktWriter::with_dims(&mut wkt_data, CoordDimensions::xyzm());
reader.iter_geometries(&mut writer).next();
assert_eq!(
from_utf8(&wkt_data).unwrap(),
Expand All @@ -252,8 +247,7 @@ fn linezm() -> Result<(), geozero_shp::Error> {

let reader = geozero_shp::Reader::from_path("./tests/data/linez.shp")?;
let mut wkt_data: Vec<u8> = Vec::new();
let mut writer = WktWriter::new(&mut wkt_data);
writer.dims.z = true; // return XYZ only
let mut writer = WktWriter::with_dims(&mut wkt_data, CoordDimensions::xyz());
reader.iter_geometries(&mut writer).next();
assert_eq!(
from_utf8(&wkt_data).unwrap(),
Expand All @@ -272,8 +266,7 @@ fn linezm() -> Result<(), geozero_shp::Error> {

let reader = geozero_shp::Reader::from_path("./tests/data/linem.shp")?;
let mut wkt_data: Vec<u8> = Vec::new();
let mut writer = WktWriter::new(&mut wkt_data);
writer.dims.m = true;
let mut writer = WktWriter::with_dims(&mut wkt_data, CoordDimensions::xym());
reader.iter_geometries(&mut writer).next();
assert_eq!(
from_utf8(&wkt_data).unwrap(),
Expand Down Expand Up @@ -326,9 +319,7 @@ fn polygon() -> Result<(), geozero_shp::Error> {
fn polygonzm() -> Result<(), geozero_shp::Error> {
let reader = geozero_shp::Reader::from_path("./tests/data/polygonz.shp")?;
let mut wkt_data: Vec<u8> = Vec::new();
let mut writer = WktWriter::new(&mut wkt_data);
writer.dims.z = true;
writer.dims.m = true;
let mut writer = WktWriter::with_dims(&mut wkt_data, CoordDimensions::xyzm());
reader.iter_geometries(&mut writer).next();
assert_eq!(
from_utf8(&wkt_data).unwrap(),
Expand All @@ -337,8 +328,7 @@ fn polygonzm() -> Result<(), geozero_shp::Error> {

let reader = geozero_shp::Reader::from_path("./tests/data/polygonm.shp")?;
let mut wkt_data: Vec<u8> = Vec::new();
let mut writer = WktWriter::new(&mut wkt_data);
writer.dims.m = true;
let mut writer = WktWriter::with_dims(&mut wkt_data, CoordDimensions::xym());
reader.iter_geometries(&mut writer).next();
assert_eq!(
from_utf8(&wkt_data).unwrap(),
Expand Down
8 changes: 3 additions & 5 deletions geozero/src/geojson/geojson_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ mod test {
use super::*;
use crate::geojson::GeoJsonWriter;
use crate::wkt::WktWriter;
use crate::{ProcessToSvg, ToJson, ToWkt};
use crate::{CoordDimensions, ProcessToSvg, ToJson, ToWkt};
use std::fs::File;

#[test]
Expand All @@ -306,16 +306,14 @@ mod test {
fn geometries3d() -> Result<()> {
let geojson = r#"{"type": "LineString", "coordinates": [[1,1,10],[2,2,20]]}"#;
let mut wkt_data: Vec<u8> = Vec::new();
let mut out = WktWriter::new(&mut wkt_data);
out.dims.z = true;
let mut out = WktWriter::with_dims(&mut wkt_data, CoordDimensions::xyz());
assert!(read_geojson_geom(&mut geojson.as_bytes(), &mut out).is_ok());
let wkt = std::str::from_utf8(&wkt_data).unwrap();
assert_eq!(wkt, "LINESTRING(1 1 10,2 2 20)");

let geojson = r#"{"type": "LineString", "coordinates": [[1,1],[2,2]]}"#;
let mut wkt_data: Vec<u8> = Vec::new();
let mut out = WktWriter::new(&mut wkt_data);
out.dims.z = true;
let mut out = WktWriter::with_dims(&mut wkt_data, CoordDimensions::xyz());
assert!(read_geojson_geom(&mut geojson.as_bytes(), &mut out).is_ok());
let wkt = std::str::from_utf8(&wkt_data).unwrap();
assert_eq!(wkt, "LINESTRING(1 1,2 2)");
Expand Down
3 changes: 1 addition & 2 deletions geozero/src/geos/geos_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,8 +215,7 @@ mod test {
let ggeom = GGeometry::new_from_wkt(wkt).unwrap();

let mut wkt_data: Vec<u8> = Vec::new();
let mut writer = WktWriter::new(&mut wkt_data);
writer.dims.z = true;
let mut writer = WktWriter::with_dims(&mut wkt_data, CoordDimensions::xyz());
assert!(process_geom(&ggeom, &mut writer).is_ok());

assert_eq!(std::str::from_utf8(&wkt_data).unwrap(), wkt);
Expand Down
2 changes: 1 addition & 1 deletion geozero/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
//! | MVT | [mvt::tile::Feature] | XY | [mvt::tile::Layer] | [ToMvt] | [MvtWriter](mvt::MvtWriter) |
//! | SVG | - | XY | - | [ToSvg] | [SvgWriter](svg::SvgWriter) |
//! | WKB | [Wkb](wkb::Wkb), [Ewkb](wkb::Ewkb), [GpkgWkb](wkb::GpkgWkb) | XYZM | - | [ToWkb] | [WkbWriter](wkb::WkbWriter) |
//! | WKT | [wkt::WktStr], [wkt::WktString] | XYZM | [wkt::WktReader], [wkt::WktStr], [wkt::WktString] | [ToWkt] | [WktWriter](wkt::WktWriter) |
//! | WKT | [wkt::WktStr], [wkt::WktString], [wkt::EwktStr], [wkt::EwktString] | XYZM | [wkt::WktReader], [wkt::WktStr], [wkt::WktString] | [ToWkt] | [WktWriter](wkt::WktWriter) |

#![warn(clippy::uninlined_format_args)]
#![allow(
Expand Down
20 changes: 12 additions & 8 deletions geozero/src/wkb/wkb_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,21 @@ impl GeozeroGeometry for GpkgWkb {
/// Process WKB geometry.
pub fn process_wkb_geom<R: Read, P: GeomProcessor>(raw: &mut R, processor: &mut P) -> Result<()> {
let info = read_wkb_header(raw)?;
processor.srid(info.srid)?;
process_wkb_geom_n(raw, &info, read_wkb_header, 0, processor)
}

/// Process EWKB geometry.
pub fn process_ewkb_geom<R: Read, P: GeomProcessor>(raw: &mut R, processor: &mut P) -> Result<()> {
let info = read_ewkb_header(raw)?;
processor.srid(info.srid)?;
process_wkb_geom_n(raw, &info, read_ewkb_header, 0, processor)
}

/// Process GPKG geometry.
pub fn process_gpkg_geom<R: Read, P: GeomProcessor>(raw: &mut R, processor: &mut P) -> Result<()> {
let info = read_gpkg_header(raw)?;
processor.srid(info.srid)?;
process_wkb_geom_n(raw, &info, read_wkb_header, 0, processor)
}

Expand Down Expand Up @@ -441,7 +444,7 @@ fn process_curvepolygon<R: Read, P: GeomProcessor>(
mod test {
use super::*;
use crate::wkt::WktWriter;
use crate::ToWkt;
use crate::{CoordDimensions, ToWkt};

#[test]
fn ewkb_format() {
Expand All @@ -465,9 +468,7 @@ mod test {

// Process all dimensions
let mut wkt_data: Vec<u8> = Vec::new();
let mut writer = WktWriter::new(&mut wkt_data);
writer.dims.z = true;
writer.dims.m = true;
let mut writer = WktWriter::with_dims(&mut wkt_data, CoordDimensions::xyzm());
assert!(process_ewkb_geom(&mut ewkb.as_slice(), &mut writer).is_ok());
assert_eq!(
std::str::from_utf8(&wkt_data).unwrap(),
Expand All @@ -484,8 +485,7 @@ mod test {
assert!(info.has_z);

let mut wkt_data: Vec<u8> = Vec::new();
let mut writer = WktWriter::new(&mut wkt_data);
writer.dims.z = true;
let mut writer = WktWriter::with_dims(&mut wkt_data, CoordDimensions::xyz());
assert!(process_ewkb_geom(&mut ewkb.as_slice(), &mut writer).is_ok());
assert_eq!(
std::str::from_utf8(&wkt_data).unwrap(),
Expand Down Expand Up @@ -595,8 +595,12 @@ mod test {
fn ewkb_to_wkt(ewkb_str: &str, with_z: bool) -> String {
let ewkb = hex::decode(ewkb_str).unwrap();
let mut wkt_data: Vec<u8> = Vec::new();
let mut writer = WktWriter::new(&mut wkt_data);
writer.dims.z = with_z;
let dims = if with_z {
CoordDimensions::xyz()
} else {
CoordDimensions::xy()
};
let mut writer = WktWriter::with_dims(&mut wkt_data, dims);
assert_eq!(
process_ewkb_geom(&mut ewkb.as_slice(), &mut writer).map_err(|e| e.to_string()),
Ok(())
Expand Down
54 changes: 49 additions & 5 deletions geozero/src/wkt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,47 @@ pub use wkt_writer::*;

pub(crate) mod conversion {
use crate::error::Result;
use crate::wkt::WktWriter;
use crate::wkt::{WktDialect, WktWriter};
use crate::{CoordDimensions, GeozeroGeometry};

/// Convert to WKT.
pub trait ToWkt {
/// Convert to 2D WKT String.
fn to_wkt(&self) -> Result<String>;
/// Convert to EWKT String.
fn to_ewkt(&self, srid: Option<i32>) -> Result<String>;
/// Convert to WKT String with dimensions.
fn to_wkt_ndim(&self, dims: CoordDimensions) -> Result<String>;
/// Convert to WKT String with srid, dimensions and dialect.
fn to_wkt_with_opts(
&self,
dialect: WktDialect,
dims: CoordDimensions,
srid: Option<i32>,
) -> Result<String>;
}

impl<T: GeozeroGeometry> ToWkt for T {
fn to_wkt(&self) -> Result<String> {
self.to_wkt_ndim(CoordDimensions::default())
self.to_wkt_with_opts(WktDialect::Wkt, CoordDimensions::default(), None)
}

fn to_ewkt(&self, srid: Option<i32>) -> Result<String> {
self.to_wkt_with_opts(WktDialect::Ewkt, CoordDimensions::xyzm(), srid)
}

fn to_wkt_ndim(&self, dims: CoordDimensions) -> Result<String> {
self.to_wkt_with_opts(WktDialect::Wkt, dims, None)
}

fn to_wkt_with_opts(
&self,
dialect: WktDialect,
dims: CoordDimensions,
srid: Option<i32>,
) -> Result<String> {
let mut out: Vec<u8> = Vec::new();
let mut writer = WktWriter::new(&mut out);
writer.dims = dims;
let mut writer = WktWriter::with_opts(&mut out, dialect, dims, srid);
self.process_geom(&mut writer)?;
String::from_utf8(out).map_err(|_| {
crate::error::GeozeroError::Geometry("Invalid UTF-8 encoding".to_string())
Expand All @@ -40,7 +62,8 @@ pub(crate) mod conversion {
mod wkb {
use crate::error::Result;
use crate::wkb::{FromWkb, WkbDialect};
use crate::wkt::{WktString, WktWriter};
use crate::wkt::{EwktString, WktDialect, WktString, WktWriter};
use crate::CoordDimensions;
use std::io::Read;

impl FromWkb for WktString {
Expand All @@ -54,4 +77,25 @@ mod wkb {
Ok(WktString(wkt))
}
}

impl FromWkb for EwktString {
fn from_wkb<R: Read>(rdr: &mut R, dialect: WkbDialect) -> Result<Self> {
let mut out: Vec<u8> = Vec::new();
let mut writer =
WktWriter::with_opts(&mut out, WktDialect::Ewkt, CoordDimensions::xyzm(), None);
crate::wkb::process_wkb_type_geom(rdr, &mut writer, dialect)?;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure if actionable, just checking if this is correct: both the writer and the processor need a dialect? And when reading, the processor cannot auto-detect the dialect, or can it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So an Ewkt string can be parsed from several Wkb dialects, which is why we need to pass it as a parameter. In the current implementation, no detection of the Wkb dialect is implemented; I guess it would be pretty tricky to do so, as they all have their own quirks.

let wkt = String::from_utf8(out).map_err(|_| {
crate::error::GeozeroError::Geometry("Invalid UTF-8 encoding".to_string())
})?;
Ok(EwktString(wkt))
}
}
}

/// WKB dialect.
#[derive(Default, PartialEq, Debug, Clone, Copy)]
pub enum WktDialect {
#[default]
Wkt,
Ewkt,
}
6 changes: 6 additions & 0 deletions geozero/src/wkt/wkt_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ impl GeozeroDatasource for WktStr<'_> {
}
}

/// WKT String.
#[derive(Debug)]
pub struct EwktString(pub String);

pub struct EwktStr<'a>(pub &'a str);

/// Wkt Reader.
pub struct WktReader<'a, R: Read>(pub &'a mut R);

Expand Down
Loading