Skip to content

Commit

Permalink
Implement to WKT for Geometries
Browse files Browse the repository at this point in the history
  • Loading branch information
b4l committed Sep 22, 2024
1 parent a613a30 commit dd82325
Show file tree
Hide file tree
Showing 4 changed files with 289 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ proj = { version = "0.27.2", optional = true, features = [
"geo-types",
] }
rayon = { version = "1.8.0", optional = true }
ryu = "1.0.18"
rstar = "0.12"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
Expand Down
2 changes: 2 additions & 0 deletions src/algorithm/native/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ mod map_chunks;
mod map_coords;
mod rechunk;
mod take;
mod to_wkt;
mod total_bounds;
pub(crate) mod type_id;
mod unary;
Expand All @@ -27,6 +28,7 @@ pub use map_chunks::MapChunks;
pub use map_coords::MapCoords;
pub use rechunk::Rechunk;
pub use take::Take;
pub use to_wkt::ToWKT;
pub use total_bounds::TotalBounds;
pub use type_id::TypeIds;
pub use unary::Unary;
285 changes: 285 additions & 0 deletions src/algorithm/native/to_wkt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
use crate::geo_traits::*;

/// Converts the input to WKT representation.
pub trait ToWKT {
fn to_wkt(&self) -> String;
}

impl<G: GeometryTrait<T = f64>> ToWKT for G {
fn to_wkt(&self) -> String {
match self.as_type() {
GeometryType::Point(point) => {
let mut wkt = String::from("POINT");

let x = point.x();
let y = point.y();

// handle NaN, may should hapen when reading
if x.is_nan() && y.is_nan() {
wkt.push_str(" EMPTY");
return wkt;
}

if point.dim() == 3 {
wkt.push('Z')
}

wkt.push('(');

// x
let mut buffer = ryu::Buffer::new();
wkt.push_str(buffer.format(x));

wkt.push(' ');

// y
let mut buffer = ryu::Buffer::new();
wkt.push_str(buffer.format(y));

// z .. n
for nth in 2..point.dim() {
wkt.push(' ');
let mut buffer = ryu::Buffer::new();
wkt.push_str(buffer.format(point.nth_unchecked(nth)));
}

wkt.push(')');

wkt
}
GeometryType::LineString(line_string) => {
let mut wkt = String::from("LINESTRING");

if line_string.dim() == 3 {
wkt.push('Z')
}

if line_string.num_coords() != 0 {
add_coords(&mut wkt, line_string.coords());
} else {
wkt.push_str(" EMPTY");
}

wkt
}
GeometryType::Polygon(polygon) => {
let mut wkt = String::from("POLYGON");

if polygon.dim() == 3 {
wkt.push('Z')
}

if let Some(exterior) = polygon.exterior() {
if exterior.num_coords() != 0 {
wkt.push('(');
add_coords(&mut wkt, exterior.coords());
} else {
wkt.push_str(" EMPTY");
return wkt;
}
} else {
wkt.push_str(" EMPTY");
return wkt;
};

for interior in polygon.interiors() {
wkt.push(',');
add_coords(&mut wkt, interior.coords());
}

wkt.push_str("))");

wkt
}
GeometryType::MultiPoint(multi_point) => {
let mut wkt = String::from("MULTIPOINT");

if multi_point.dim() == 3 {
wkt.push('Z')
}

let mut points = multi_point.points();

if let Some(first) = points.next() {
wkt.push('(');

add_point(&mut wkt, first);

for point in points {
wkt.push(',');
add_point(&mut wkt, point);
}

wkt.push(')');
} else {
wkt.push_str(" EMPTY");
}

wkt
}
GeometryType::MultiLineString(multi_line_string) => {
let mut wkt = String::from("MULTILINESTRING");

if multi_line_string.dim() == 3 {
wkt.push('Z')
}

let mut lines = multi_line_string.lines();

if let Some(line_string) = lines.next() {
wkt.push('(');
add_coords(&mut wkt, line_string.coords());

for line_string in lines {
wkt.push(',');
add_coords(&mut wkt, line_string.coords());
}

wkt.push(')');
} else {
wkt.push_str(" EMPTY");
}

wkt
}
GeometryType::MultiPolygon(multi_polygon) => {
let mut wkt = String::from("MULTIPOLYGON");

if multi_polygon.dim() == 3 {
wkt.push('Z')
}

let mut polygons = multi_polygon.polygons();

if let Some(polygon) = polygons.next() {
wkt.push_str("((");

add_coords(&mut wkt, polygon.exterior().unwrap().coords());
for interior in polygon.interiors() {
wkt.push(',');
add_coords(&mut wkt, interior.coords());
}

for polygon in polygons {
wkt.push_str("),(");

add_coords(&mut wkt, polygon.exterior().unwrap().coords());
for interior in polygon.interiors() {
wkt.push(',');
add_coords(&mut wkt, interior.coords());
}
}

wkt.push_str("))");
} else {
wkt.push_str("EMPTY");
return wkt;
};

wkt
}
GeometryType::GeometryCollection(gc) => {
let mut wkt = String::from("GEOMETRYCOLLECTION");

if gc.dim() == 3 {
wkt.push('Z')
}

let mut geometries = gc.geometries();

if let Some(first) = geometries.next() {
wkt.push('(');

wkt.push_str(&first.to_wkt());

for geom in geometries {
wkt.push(',');
wkt.push_str(&geom.to_wkt());
}

wkt.push(')');
} else {
wkt.push_str("EMPTY");
}

wkt
}
GeometryType::Rect(rect) => {
let lower = rect.lower();
let upper = rect.upper();

match rect.dim() {
2 => {
return format!(
"POLYGON({0} {1},{2} {1},{2} {3},{0} {3},{0} {1})",
lower.x(),
lower.y(),
upper.x(),
upper.y(),
)
}
3 => {
todo!("cube as polygon / linestring / multipoint?")
}
_ => unimplemented!(),
}
}
}
}
}

fn add_coord<C: CoordTrait<T = f64>>(wkt: &mut String, coord: C) {
// x
let mut buffer = ryu::Buffer::new();
wkt.push_str(buffer.format(coord.x()));

wkt.push(' ');

// y
let mut buffer = ryu::Buffer::new();
wkt.push_str(buffer.format(coord.y()));

// z .. n
for nth in 2..coord.dim() {
wkt.push(' ');
let mut buffer = ryu::Buffer::new();
wkt.push_str(buffer.format(coord.nth_unchecked(nth)));
}
}

fn add_point<P: PointTrait<T = f64>>(wkt: &mut String, point: P) {
wkt.push('(');

// x
let mut buffer = ryu::Buffer::new();
wkt.push_str(buffer.format(point.x()));

wkt.push(' ');

// y
let mut buffer = ryu::Buffer::new();
wkt.push_str(buffer.format(point.y()));

// z .. n
for nth in 2..point.dim() {
wkt.push(' ');
let mut buffer = ryu::Buffer::new();
wkt.push_str(buffer.format(point.nth_unchecked(nth)));
}

wkt.push(')');
}

fn add_coords<C: CoordTrait<T = f64>>(wkt: &mut String, mut coords: impl Iterator<Item = C>) {
wkt.push('(');

let first = coords.next().unwrap();
add_coord(wkt, first);

for coord in coords {
wkt.push(',');
add_coord(wkt, coord);
}

wkt.push(')');
}

0 comments on commit dd82325

Please sign in to comment.