///|
priv struct GeoJsonWriter {
builder : StringBuilder
}
///|
fn GeoJsonWriter::new() -> GeoJsonWriter {
{ builder: StringBuilder(), }
}
///|
fn resolve_geojson_dimension(
summary : DimensionSummary,
) -> Result[CoordinateDimension, GeoError] {
match summary {
Uniform(Xy) => Ok(Xy)
Uniform(Xyz) => Ok(Xyz)
Uniform(Xym) | Uniform(Xyzm) =>
Err(UnsupportedGeometry("GeoJSON M ordinates"))
Mixed => Err(InvalidCoordinate("inconsistent coordinate dimensions"))
NoDimension => Ok(Xy)
}
}
///|
fn validate_geojson_writer_coordinate(
coordinate : Coordinate,
dimension : CoordinateDimension,
) -> Result[Unit, GeoError] {
if coordinate.dimension() != dimension {
return Err(
InvalidCoordinate("coordinate dimension does not match geometry"),
)
}
if coordinate.x.is_nan() || coordinate.x.is_inf() {
return Err(InvalidCoordinate("non-finite x ordinate"))
}
if coordinate.y.is_nan() || coordinate.y.is_inf() {
return Err(InvalidCoordinate("non-finite y ordinate"))
}
match coordinate.z {
Some(value) =>
if value.is_nan() || value.is_inf() {
return Err(InvalidCoordinate("non-finite z ordinate"))
}
None => ()
}
Ok(())
}
///|
fn GeoJsonWriter::write_position(
self : GeoJsonWriter,
coordinate : Coordinate,
dimension : CoordinateDimension,
) -> Result[Unit, GeoError] {
match validate_geojson_writer_coordinate(coordinate, dimension) {
Ok(_) => ()
Err(err) => return Err(err)
}
self.builder.write_string("[")
self.builder.write_string(coordinate.x.to_string())
self.builder.write_string(",")
self.builder.write_string(coordinate.y.to_string())
match dimension {
Xyz =>
match coordinate.z {
Some(value) => {
self.builder.write_string(",")
self.builder.write_string(value.to_string())
}
None => ()
}
_ => ()
}
self.builder.write_string("]")
Ok(())
}
///|
fn GeoJsonWriter::write_positions(
self : GeoJsonWriter,
coordinates : Array[Coordinate],
dimension : CoordinateDimension,
) -> Result[Unit, GeoError] {
self.builder.write_string("[")
let mut first = true
for coordinate in coordinates {
if !first {
self.builder.write_string(",")
}
match self.write_position(coordinate, dimension) {
Ok(_) => ()
Err(err) => return Err(err)
}
first = false
}
self.builder.write_string("]")
Ok(())
}
///|
fn GeoJsonWriter::write_rings(
self : GeoJsonWriter,
rings : Array[Array[Coordinate]],
dimension : CoordinateDimension,
) -> Result[Unit, GeoError] {
self.builder.write_string("[")
let mut first = true
for ring in rings {
if ring.length() == 0 {
return Err(InvalidCoordinate("polygon ring has no coordinates"))
}
if !first {
self.builder.write_string(",")
}
match self.write_positions(ring, dimension) {
Ok(_) => ()
Err(err) => return Err(err)
}
first = false
}
self.builder.write_string("]")
Ok(())
}
///|
fn GeoJsonWriter::write_lines(
self : GeoJsonWriter,
lines : Array[Array[Coordinate]],
dimension : CoordinateDimension,
) -> Result[Unit, GeoError] {
self.builder.write_string("[")
let mut first = true
for line in lines {
if line.length() == 0 {
return Err(InvalidCoordinate("empty line string component"))
}
if !first {
self.builder.write_string(",")
}
match self.write_positions(line, dimension) {
Ok(_) => ()
Err(err) => return Err(err)
}
first = false
}
self.builder.write_string("]")
Ok(())
}
///|
fn GeoJsonWriter::write_polygons(
self : GeoJsonWriter,
polygons : Array[Array[Array[Coordinate]]],
dimension : CoordinateDimension,
) -> Result[Unit, GeoError] {
self.builder.write_string("[")
let mut first = true
for polygon in polygons {
if polygon.length() == 0 {
return Err(InvalidCoordinate("empty polygon component"))
}
if !first {
self.builder.write_string(",")
}
match self.write_rings(polygon, dimension) {
Ok(_) => ()
Err(err) => return Err(err)
}
first = false
}
self.builder.write_string("]")
Ok(())
}
///|
fn GeoJsonWriter::write_geometry(
self : GeoJsonWriter,
geometry : Geometry,
) -> Result[Unit, GeoError] {
match geometry {
Point(coordinate) => {
let dimension = match
resolve_geojson_dimension(Uniform(coordinate.dimension())) {
Ok(value) => value
Err(err) => return Err(err)
}
self.builder.write_string("{\"type\":\"Point\",\"coordinates\":")
match self.write_position(coordinate, dimension) {
Ok(_) => ()
Err(err) => return Err(err)
}
self.builder.write_string("}")
Ok(())
}
EmptyPoint => {
self.builder.write_string("{\"type\":\"Point\",\"coordinates\":[]}")
Ok(())
}
LineString(coordinates) => {
self.builder.write_string("{\"type\":\"LineString\",\"coordinates\":")
if coordinates.length() == 0 {
self.builder.write_string("[]")
} else {
let dimension = match
resolve_geojson_dimension(coordinate_dimension_summary(coordinates)) {
Ok(value) => value
Err(err) => return Err(err)
}
match self.write_positions(coordinates, dimension) {
Ok(_) => ()
Err(err) => return Err(err)
}
}
self.builder.write_string("}")
Ok(())
}
Polygon(rings) => {
self.builder.write_string("{\"type\":\"Polygon\",\"coordinates\":")
if rings.length() == 0 {
self.builder.write_string("[]")
} else {
let dimension = match
resolve_geojson_dimension(ring_dimension_summary(rings)) {
Ok(value) => value
Err(err) => return Err(err)
}
match self.write_rings(rings, dimension) {
Ok(_) => ()
Err(err) => return Err(err)
}
}
self.builder.write_string("}")
Ok(())
}
MultiPoint(coordinates) => {
self.builder.write_string("{\"type\":\"MultiPoint\",\"coordinates\":")
if coordinates.length() == 0 {
self.builder.write_string("[]")
} else {
let dimension = match
resolve_geojson_dimension(coordinate_dimension_summary(coordinates)) {
Ok(value) => value
Err(err) => return Err(err)
}
match self.write_positions(coordinates, dimension) {
Ok(_) => ()
Err(err) => return Err(err)
}
}
self.builder.write_string("}")
Ok(())
}
MultiLineString(lines) => {
self.builder.write_string(
"{\"type\":\"MultiLineString\",\"coordinates\":",
)
if lines.length() == 0 {
self.builder.write_string("[]")
} else {
let dimension = match
resolve_geojson_dimension(ring_dimension_summary(lines)) {
Ok(value) => value
Err(err) => return Err(err)
}
match self.write_lines(lines, dimension) {
Ok(_) => ()
Err(err) => return Err(err)
}
}
self.builder.write_string("}")
Ok(())
}
MultiPolygon(polygons) => {
self.builder.write_string("{\"type\":\"MultiPolygon\",\"coordinates\":")
if polygons.length() == 0 {
self.builder.write_string("[]")
} else {
let dimension = match
resolve_geojson_dimension(polygon_dimension_summary(polygons)) {
Ok(value) => value
Err(err) => return Err(err)
}
match self.write_polygons(polygons, dimension) {
Ok(_) => ()
Err(err) => return Err(err)
}
}
self.builder.write_string("}")
Ok(())
}
GeometryCollection(geometries) => {
self.builder.write_string(
"{\"type\":\"GeometryCollection\",\"geometries\":[",
)
let mut first = true
for child in geometries {
if !first {
self.builder.write_string(",")
}
match self.write_geometry(child) {
Ok(_) => ()
Err(err) => return Err(err)
}
first = false
}
self.builder.write_string("]}")
Ok(())
}
}
}
///|
/// Serializes a geometry as compact GeoJSON.
pub fn format_geojson(geometry : Geometry) -> Result[String, GeoError] {
let writer = GeoJsonWriter::new()
match writer.write_geometry(geometry) {
Ok(_) => Ok(writer.builder.to_string())
Err(err) => Err(err)
}
}
///|
pub fn wkt_to_geojson(
source : StringView,
limits : GeoLimits,
) -> Result[String, GeoError] {
let geometry = match parse_wkt(source, limits) {
Ok(value) => value
Err(err) => return Err(err)
}
format_geojson(geometry)
}
///|
pub fn geojson_to_wkt(
source : StringView,
limits : GeoLimits,
) -> Result[String, GeoError] {
let geometry = match parse_geojson(source, limits) {
Ok(value) => value
Err(err) => return Err(err)
}
format_wkt(geometry)
}
///|
pub fn wkb_to_geojson(
source : BytesView,
limits : GeoLimits,
) -> Result[String, GeoError] {
let geometry = match parse_wkb(source, limits) {
Ok(value) => value
Err(err) => return Err(err)
}
format_geojson(geometry)
}
///|
pub fn geojson_to_wkb(
source : StringView,
limits : GeoLimits,
) -> Result[Bytes, GeoError] {
let geometry = match parse_geojson(source, limits) {
Ok(value) => value
Err(err) => return Err(err)
}
format_wkb(geometry)
}