///|
pub(all) enum WkbByteOrder {
LittleEndian
BigEndian
} derive(Eq, @debug.Debug)
///|
priv struct WkbReader {
data : BytesView
mut offset : Int32
limits : GeoLimits
allow_ewkb : Bool
mut coordinate_count : Int32
mut geometry_count : Int32
}
///|
fn WkbReader::new(
data : BytesView,
limits : GeoLimits,
allow_ewkb : Bool,
) -> WkbReader {
{
data,
offset: 0,
limits,
allow_ewkb,
coordinate_count: 0,
geometry_count: 0,
}
}
///|
fn WkbReader::read_byte(
self : WkbReader,
context : String,
) -> Result[Byte, GeoError] {
let offset = self.offset
match self.data.get(offset) {
Some(value) => {
self.offset += 1
Ok(value)
}
None => Err(Truncated(context, offset))
}
}
///|
fn WkbReader::read_byte_order(
self : WkbReader,
) -> Result[WkbByteOrder, GeoError] {
let offset = self.offset
let value = match self.read_byte("byte order") {
Ok(value) => value
Err(err) => return Err(err)
}
match value.to_int() {
0 => Ok(BigEndian)
1 => Ok(LittleEndian)
_ => Err(InvalidWkb("invalid byte order", offset))
}
}
///|
fn WkbReader::read_u32(
self : WkbReader,
order : WkbByteOrder,
context : String,
) -> Result[UInt32, GeoError] {
let b0 = match self.read_byte(context) {
Ok(value) => value
Err(err) => return Err(err)
}
let b1 = match self.read_byte(context) {
Ok(value) => value
Err(err) => return Err(err)
}
let b2 = match self.read_byte(context) {
Ok(value) => value
Err(err) => return Err(err)
}
let b3 = match self.read_byte(context) {
Ok(value) => value
Err(err) => return Err(err)
}
match order {
LittleEndian =>
Ok(
b0.to_uint() |
(b1.to_uint() << 8) |
(b2.to_uint() << 16) |
(b3.to_uint() << 24),
)
BigEndian =>
Ok(
(b0.to_uint() << 24) |
(b1.to_uint() << 16) |
(b2.to_uint() << 8) |
b3.to_uint(),
)
}
}
///|
fn WkbReader::read_f64(
self : WkbReader,
order : WkbByteOrder,
context : String,
) -> Result[Double, GeoError] {
let mut bits : UInt64 = 0UL
match order {
LittleEndian =>
for index in 0..<8 {
let value = match self.read_byte(context) {
Ok(value) => value
Err(err) => return Err(err)
}
bits = bits | (value.to_uint64() << (index * 8))
}
BigEndian =>
for _ in 0..<8 {
let value = match self.read_byte(context) {
Ok(value) => value
Err(err) => return Err(err)
}
bits = (bits << 8) | value.to_uint64()
}
}
Ok(bits.reinterpret_as_double())
}
///|
fn WkbReader::read_count_with_order(
self : WkbReader,
order : WkbByteOrder,
context : String,
) -> Result[Int32, GeoError] {
let offset = self.offset
let value = match self.read_u32(order, context) {
Ok(value) => value
Err(err) => return Err(err)
}
if value > 2147483647U {
Err(InvalidWkb(context + " exceeds supported range", offset))
} else {
Ok(value.reinterpret_as_int())
}
}
///|
fn WkbReader::record_coordinate(self : WkbReader) -> Result[Unit, GeoError] {
self.coordinate_count += 1
self.limits.check_coordinates(self.coordinate_count)
}
///|
fn validate_coordinate_values(
x : Double,
y : Double,
z : Double?,
m : Double?,
) -> Result[Unit, GeoError] {
if x.is_nan() || x.is_inf() {
Err(InvalidCoordinate("non-finite x ordinate"))
} else if y.is_nan() || y.is_inf() {
Err(InvalidCoordinate("non-finite y ordinate"))
} else if (match z {
Some(value) => value.is_nan() || value.is_inf()
None => false
}) {
Err(InvalidCoordinate("non-finite z ordinate"))
} else if (match m {
Some(value) => value.is_nan() || value.is_inf()
None => false
}) {
Err(InvalidCoordinate("non-finite m ordinate"))
} else {
Ok(())
}
}
///|
fn is_empty_point_values(
x : Double,
y : Double,
z : Double?,
m : Double?,
) -> Bool {
x.is_nan() &&
y.is_nan() &&
(match z {
Some(value) => value.is_nan()
None => true
}) &&
(match m {
Some(value) => value.is_nan()
None => true
})
}
///|
fn WkbReader::read_ordinate_values(
self : WkbReader,
order : WkbByteOrder,
dimension : CoordinateDimension,
) -> Result[(Double, Double, Double?, Double?), GeoError] {
let x = match self.read_f64(order, "x ordinate") {
Ok(value) => value
Err(err) => return Err(err)
}
let y = match self.read_f64(order, "y ordinate") {
Ok(value) => value
Err(err) => return Err(err)
}
let z : Double? = match dimension {
Xyz | Xyzm =>
match self.read_f64(order, "z ordinate") {
Ok(value) => Some(value)
Err(err) => return Err(err)
}
_ => None
}
let m : Double? = match dimension {
Xym | Xyzm =>
match self.read_f64(order, "m ordinate") {
Ok(value) => Some(value)
Err(err) => return Err(err)
}
_ => None
}
Ok((x, y, z, m))
}
///|
fn WkbReader::read_coordinate(
self : WkbReader,
order : WkbByteOrder,
dimension : CoordinateDimension,
) -> Result[Coordinate, GeoError] {
let (x, y, z, m) = match self.read_ordinate_values(order, dimension) {
Ok(value) => value
Err(err) => return Err(err)
}
match validate_coordinate_values(x, y, z, m) {
Ok(_) => ()
Err(err) => return Err(err)
}
match self.record_coordinate() {
Ok(_) => ()
Err(err) => return Err(err)
}
Ok({ x, y, z, m, })
}
///|
fn WkbReader::read_coordinates(
self : WkbReader,
order : WkbByteOrder,
dimension : CoordinateDimension,
count : Int32,
) -> Result[Array[Coordinate], GeoError] {
if count > self.limits.max_coordinates {
return Err(
LimitExceeded("coordinate count", self.limits.max_coordinates, count),
)
}
let coordinates : Array[Coordinate] = []
for _ in 0.. value
Err(err) => return Err(err)
}
coordinates.push(coordinate)
}
Ok(coordinates)
}
///|
fn WkbReader::read_point_geometry(
self : WkbReader,
order : WkbByteOrder,
dimension : CoordinateDimension,
) -> Result[Geometry, GeoError] {
let (x, y, z, m) = match self.read_ordinate_values(order, dimension) {
Ok(value) => value
Err(err) => return Err(err)
}
if is_empty_point_values(x, y, z, m) {
return Ok(EmptyPoint)
}
match validate_coordinate_values(x, y, z, m) {
Ok(_) => ()
Err(err) => return Err(err)
}
match self.record_coordinate() {
Ok(_) => ()
Err(err) => return Err(err)
}
Ok(Point({ x, y, z, m, }))
}
///|
fn WkbReader::read_line_string_geometry(
self : WkbReader,
order : WkbByteOrder,
dimension : CoordinateDimension,
) -> Result[Geometry, GeoError] {
let count = match
self.read_count_with_order(order, "LINESTRING coordinate count") {
Ok(value) => value
Err(err) => return Err(err)
}
let coordinates = match self.read_coordinates(order, dimension, count) {
Ok(value) => value
Err(err) => return Err(err)
}
Ok(LineString(coordinates))
}
///|
fn WkbReader::read_polygon_geometry(
self : WkbReader,
order : WkbByteOrder,
dimension : CoordinateDimension,
) -> Result[Geometry, GeoError] {
let ring_count = match
self.read_count_with_order(order, "POLYGON ring count") {
Ok(value) => value
Err(err) => return Err(err)
}
let rings : Array[Array[Coordinate]] = []
for _ in 0.. value
Err(err) => return Err(err)
}
if point_count == 0 {
return Err(InvalidWkb("polygon ring has no coordinates", count_offset))
}
let ring = match self.read_coordinates(order, dimension, point_count) {
Ok(value) => value
Err(err) => return Err(err)
}
rings.push(ring)
}
Ok(Polygon(rings))
}
///|
fn WkbReader::read_multi_point_geometry(
self : WkbReader,
order : WkbByteOrder,
dimension : CoordinateDimension,
srid : Int32?,
depth : Int32,
) -> Result[Geometry, GeoError] {
let count = match
self.read_count_with_order(order, "MULTIPOINT geometry count") {
Ok(value) => value
Err(err) => return Err(err)
}
let points : Array[Coordinate] = []
for _ in 0.. value
Err(err) => return Err(err)
}
if component_dimension != dimension {
return Err(
InvalidWkb(
"MULTIPOINT component dimension does not match parent", component_offset,
),
)
}
if !validate_nested_srid(component_srid, srid) {
return Err(
InvalidWkb("nested SRID does not match parent", component_offset),
)
}
match component {
Point(coordinate) => points.push(coordinate)
EmptyPoint =>
return Err(
InvalidWkb(
"empty point in MULTIPOINT is not supported", component_offset,
),
)
_ =>
return Err(
InvalidWkb("MULTIPOINT component must be a point", component_offset),
)
}
}
Ok(MultiPoint(points))
}
///|
fn WkbReader::read_multi_line_string_geometry(
self : WkbReader,
order : WkbByteOrder,
dimension : CoordinateDimension,
srid : Int32?,
depth : Int32,
) -> Result[Geometry, GeoError] {
let count = match
self.read_count_with_order(order, "MULTILINESTRING geometry count") {
Ok(value) => value
Err(err) => return Err(err)
}
let lines : Array[Array[Coordinate]] = []
for _ in 0.. value
Err(err) => return Err(err)
}
if component_dimension != dimension {
return Err(
InvalidWkb(
"MULTILINESTRING component dimension does not match parent", component_offset,
),
)
}
if !validate_nested_srid(component_srid, srid) {
return Err(
InvalidWkb("nested SRID does not match parent", component_offset),
)
}
match component {
LineString(coordinates) => lines.push(coordinates)
_ =>
return Err(
InvalidWkb(
"MULTILINESTRING component must be a line string", component_offset,
),
)
}
}
Ok(MultiLineString(lines))
}
///|
fn WkbReader::read_multi_polygon_geometry(
self : WkbReader,
order : WkbByteOrder,
dimension : CoordinateDimension,
srid : Int32?,
depth : Int32,
) -> Result[Geometry, GeoError] {
let count = match
self.read_count_with_order(order, "MULTIPOLYGON geometry count") {
Ok(value) => value
Err(err) => return Err(err)
}
let polygons : Array[Array[Array[Coordinate]]] = []
for _ in 0.. value
Err(err) => return Err(err)
}
if component_dimension != dimension {
return Err(
InvalidWkb(
"MULTIPOLYGON component dimension does not match parent", component_offset,
),
)
}
if !validate_nested_srid(component_srid, srid) {
return Err(
InvalidWkb("nested SRID does not match parent", component_offset),
)
}
match component {
Polygon(rings) => polygons.push(rings)
_ =>
return Err(
InvalidWkb(
"MULTIPOLYGON component must be a polygon", component_offset,
),
)
}
}
Ok(MultiPolygon(polygons))
}
///|
fn WkbReader::read_geometry_collection_geometry(
self : WkbReader,
order : WkbByteOrder,
dimension : CoordinateDimension,
srid : Int32?,
depth : Int32,
) -> Result[Geometry, GeoError] {
let count = match
self.read_count_with_order(order, "GEOMETRYCOLLECTION geometry count") {
Ok(value) => value
Err(err) => return Err(err)
}
let geometries : Array[Geometry] = []
for _ in 0.. value
Err(err) => return Err(err)
}
if component_dimension != dimension {
return Err(
InvalidWkb(
"GEOMETRYCOLLECTION component dimension does not match parent", component_offset,
),
)
}
if !validate_nested_srid(component_srid, srid) {
return Err(
InvalidWkb("nested SRID does not match parent", component_offset),
)
}
geometries.push(geometry)
}
Ok(GeometryCollection(geometries))
}
///|
fn decode_iso_wkb_type(
type_code : UInt32,
) -> Result[(UInt32, CoordinateDimension), GeoError] {
if type_code >= 1U && type_code <= 7U {
Ok((type_code, Xy))
} else if type_code >= 1001U && type_code <= 1007U {
Ok((type_code - 1000U, Xyz))
} else if type_code >= 2001U && type_code <= 2007U {
Ok((type_code - 2000U, Xym))
} else if type_code >= 3001U && type_code <= 3007U {
Ok((type_code - 3000U, Xyzm))
} else {
Err(UnsupportedGeometry("WKB type " + type_code.to_string()))
}
}
///|
fn decode_ewkb_type(
type_code : UInt32,
) -> Result[(UInt32, CoordinateDimension, Bool), GeoError] {
if type_code.land(0x10000000U) != 0U {
return Err(UnsupportedGeometry("EWKB bounding boxes are not supported"))
}
let has_z = type_code.land(0x80000000U) != 0U
let has_m = type_code.land(0x40000000U) != 0U
let has_srid = type_code.land(0x20000000U) != 0U
let base_code = type_code.land(0x0FFFFFFFU)
if base_code < 1U || base_code > 7U {
return Err(UnsupportedGeometry("EWKB type " + type_code.to_string()))
}
let dimension = match (has_z, has_m) {
(true, true) => Xyzm
(true, false) => Xyz
(false, true) => Xym
(false, false) => Xy
}
Ok((base_code, dimension, has_srid))
}
///|
fn decode_wkb_type(
type_code : UInt32,
allow_ewkb : Bool,
) -> Result[(UInt32, CoordinateDimension, Bool), GeoError] {
if type_code.land(0xF0000000U) != 0U {
if !allow_ewkb {
return Err(UnsupportedGeometry("WKB type " + type_code.to_string()))
}
decode_ewkb_type(type_code)
} else {
let (base_code, dimension) = match decode_iso_wkb_type(type_code) {
Ok(value) => value
Err(err) => return Err(err)
}
Ok((base_code, dimension, false))
}
}
///|
fn validate_nested_srid(nested : Int32?, parent : Int32?) -> Bool {
match nested {
Some(value) => parent == Some(value)
None => true
}
}
///|
fn WkbReader::read_geometry(
self : WkbReader,
depth : Int32,
) -> Result[(Geometry, CoordinateDimension, Int32?), GeoError] {
match self.limits.check_depth(depth) {
Ok(_) => ()
Err(err) => return Err(err)
}
self.geometry_count += 1
match self.limits.check_geometries(self.geometry_count) {
Ok(_) => ()
Err(err) => return Err(err)
}
let order = match self.read_byte_order() {
Ok(value) => value
Err(err) => return Err(err)
}
let type_code = match self.read_u32(order, "geometry type") {
Ok(value) => value
Err(err) => return Err(err)
}
let (base_code, dimension, has_srid) = match
decode_wkb_type(type_code, self.allow_ewkb) {
Ok(value) => value
Err(err) => return Err(err)
}
let srid : Int32? = if has_srid {
let srid_offset = self.offset
let value = match self.read_u32(order, "SRID") {
Ok(value) => value
Err(err) => return Err(err)
}
if value > 2147483647U {
return Err(InvalidWkb("SRID exceeds supported range", srid_offset))
}
Some(value.reinterpret_as_int())
} else {
None
}
let geometry = if base_code == 1U {
self.read_point_geometry(order, dimension)
} else if base_code == 2U {
self.read_line_string_geometry(order, dimension)
} else if base_code == 3U {
self.read_polygon_geometry(order, dimension)
} else if base_code == 4U {
self.read_multi_point_geometry(order, dimension, srid, depth)
} else if base_code == 5U {
self.read_multi_line_string_geometry(order, dimension, srid, depth)
} else if base_code == 6U {
self.read_multi_polygon_geometry(order, dimension, srid, depth)
} else if base_code == 7U {
self.read_geometry_collection_geometry(order, dimension, srid, depth)
} else {
Err(UnsupportedGeometry("WKB type " + type_code.to_string()))
}
match geometry {
Ok(value) => Ok((value, dimension, srid))
Err(err) => Err(err)
}
}
///|
pub fn parse_wkb(
source : BytesView,
limits : GeoLimits,
) -> Result[Geometry, GeoError] {
match limits.check_text(source.length()) {
Ok(_) => ()
Err(err) => return Err(err)
}
let reader = WkbReader::new(source, limits, false)
let (geometry, _, _) = match reader.read_geometry(1) {
Ok(value) => value
Err(err) => return Err(err)
}
if reader.offset != source.length() {
Err(TrailingBytes(reader.offset))
} else {
Ok(geometry)
}
}
///|
pub fn parse_ewkb(
source : BytesView,
limits : GeoLimits,
) -> Result[SpatialGeometry, GeoError] {
match limits.check_text(source.length()) {
Ok(_) => ()
Err(err) => return Err(err)
}
let reader = WkbReader::new(source, limits, true)
let (geometry, _, srid) = match reader.read_geometry(1) {
Ok(value) => value
Err(err) => return Err(err)
}
if reader.offset != source.length() {
Err(TrailingBytes(reader.offset))
} else {
Ok({ srid, geometry, })
}
}
///|
pub extend WkbByteOrder with Eq::{not_equal, equal}
///|
pub extend WkbByteOrder with @debug.Debug::{to_repr}