Skip to content

Commit 37555ee

Browse files
pkaandelf
authored andcommitted
Support for Geometry and GeometryCollection type
1 parent c008aca commit 37555ee

File tree

2 files changed

+185
-1
lines changed

2 files changed

+185
-1
lines changed

src/ewkb.rs

Lines changed: 146 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,111 @@ pub type MultiPolygonM = MultiPolygonT<PointM>;
780780
pub type MultiPolygonZM = MultiPolygonT<PointZM>;
781781

782782

783+
/// Generic Geometry Data Type
784+
#[derive(Debug)]
785+
pub enum GeometryT<P: postgis::Point + EwkbRead> {
786+
Point(P),
787+
LineString(LineStringT<P>),
788+
Polygon(PolygonT<P>),
789+
MultiPoint(MultiPointT<P>),
790+
MultiLineString(MultiLineStringT<P>),
791+
MultiPolygon(MultiPolygonT<P>),
792+
GeometryCollection(GeometryCollectionT<P>)
793+
}
794+
795+
796+
impl<P> EwkbRead for GeometryT<P>
797+
where P: postgis::Point + EwkbRead
798+
{
799+
fn point_type() -> PointType {
800+
P::point_type()
801+
}
802+
fn read_ewkb<R: Read>(raw: &mut R) -> Result<Self, Error> {
803+
let byte_order = try!(raw.read_i8());
804+
let is_be = byte_order == 0i8;
805+
806+
let type_id = try!(read_u32(raw, is_be));
807+
let mut srid: Option<i32> = None;
808+
if type_id & 0x20000000 == 0x20000000 {
809+
srid = Some(try!(read_i32(raw, is_be)));
810+
}
811+
812+
let geom = match type_id & 0xff {
813+
0x01 => GeometryT::Point(P::read_ewkb_body(raw, is_be, srid).unwrap()),
814+
0x02 => GeometryT::LineString(LineStringT::<P>::read_ewkb_body(raw, is_be, srid).unwrap()),
815+
0x03 => GeometryT::Polygon(PolygonT::read_ewkb_body(raw, is_be, srid).unwrap()),
816+
0x04 => GeometryT::MultiPoint(MultiPointT::read_ewkb_body(raw, is_be, srid).unwrap()),
817+
0x05 => GeometryT::MultiLineString(MultiLineStringT::read_ewkb_body(raw, is_be, srid).unwrap()),
818+
0x06 => GeometryT::MultiPolygon(MultiPolygonT::read_ewkb_body(raw, is_be, srid).unwrap()),
819+
0x07 => GeometryT::GeometryCollection(GeometryCollectionT::read_ewkb_body(raw, is_be, srid).unwrap()),
820+
_ => return Err(Error::Read(format!("Error reading generic geometry type - unsupported type id {}.", type_id)))
821+
};
822+
Ok(geom)
823+
}
824+
fn read_ewkb_body<R: Read>(_raw: &mut R, _is_be: bool, _srid: Option<i32>) -> Result<Self, Error> {
825+
panic!("Not used for generic geometry type")
826+
}
827+
}
828+
829+
pub type Geometry = GeometryT<Point>;
830+
pub type GeometryZ = GeometryT<PointZ>;
831+
pub type GeometryM = GeometryT<PointM>;
832+
pub type GeometryZM = GeometryT<PointZM>;
833+
834+
835+
/// GeometryCollection
836+
#[derive(Debug)]
837+
pub struct GeometryCollectionT<P: postgis::Point + EwkbRead> {
838+
pub geometries: Vec<GeometryT<P>>
839+
}
840+
841+
impl<P> GeometryCollectionT<P>
842+
where P: postgis::Point + EwkbRead
843+
{
844+
pub fn new() -> GeometryCollectionT<P> {
845+
GeometryCollectionT { geometries: Vec::new() }
846+
}
847+
}
848+
849+
impl<P> EwkbRead for GeometryCollectionT<P>
850+
where P: postgis::Point + EwkbRead
851+
{
852+
fn point_type() -> PointType {
853+
P::point_type()
854+
}
855+
fn read_ewkb_body<R: Read>(raw: &mut R, is_be: bool, _srid: Option<i32>) -> Result<Self, Error> {
856+
let mut ret = GeometryCollectionT::new();
857+
let size = try!(read_u32(raw, is_be)) as usize;
858+
for _ in 0..size {
859+
let is_be = try!(raw.read_i8()) == 0i8;
860+
861+
let type_id = try!(read_u32(raw, is_be));
862+
let mut srid: Option<i32> = None;
863+
if type_id & 0x20000000 == 0x20000000 {
864+
srid = Some(try!(read_i32(raw, is_be)));
865+
}
866+
let geom = match type_id & 0xff {
867+
0x01 => GeometryT::Point(P::read_ewkb_body(raw, is_be, srid).unwrap()),
868+
0x02 => GeometryT::LineString(LineStringT::<P>::read_ewkb_body(raw, is_be, srid).unwrap()),
869+
0x03 => GeometryT::Polygon(PolygonT::read_ewkb_body(raw, is_be, srid).unwrap()),
870+
0x04 => GeometryT::MultiPoint(MultiPointT::read_ewkb_body(raw, is_be, srid).unwrap()),
871+
0x05 => GeometryT::MultiLineString(MultiLineStringT::read_ewkb_body(raw, is_be, srid).unwrap()),
872+
0x06 => GeometryT::MultiPolygon(MultiPolygonT::read_ewkb_body(raw, is_be, srid).unwrap()),
873+
0x07 => GeometryT::GeometryCollection(GeometryCollectionT::read_ewkb_body(raw, is_be, srid).unwrap()),
874+
_ => return Err(Error::Read(format!("Error reading generic geometry type - unsupported type id {}.", type_id)))
875+
};
876+
ret.geometries.push(geom);
877+
}
878+
Ok(ret)
879+
}
880+
}
881+
882+
pub type GeometryCollection = GeometryCollectionT<Point>;
883+
pub type GeometryCollectionZ = GeometryCollectionT<PointZ>;
884+
pub type GeometryCollectionM = GeometryCollectionT<PointM>;
885+
pub type GeometryCollectionZM = GeometryCollectionT<PointZM>;
886+
887+
783888
#[test]
784889
fn test_point_write() {
785890
// 'POINT (10 -20)'
@@ -943,7 +1048,7 @@ fn test_multipoint_read() {
9431048
}
9441049

9451050
#[test]
946-
fn test_multline_read() {
1051+
fn test_multiline_read() {
9471052
let p = |x, y| Point { x: x, y: y, srid: None }; // PostGIS doesn't store SRID for sub-geometries
9481053
// SELECT 'SRID=4326;MULTILINESTRING ((10 -20, 0 -0.5), (0 0, 2 0))'::geometry
9491054
let ewkb = hex_to_vec("0105000020E610000002000000010200000002000000000000000000244000000000000034C00000000000000000000000000000E0BF0102000000020000000000000000000000000000000000000000000000000000400000000000000000");
@@ -966,6 +1071,46 @@ fn test_multipolygon_read() {
9661071
assert_eq!(multipoly, MultiPolygonT::<Point> {srid: Some(4326), polygons: vec![poly1, poly2]});
9671072
}
9681073

1074+
#[test]
1075+
fn test_geometrycollection_read() {
1076+
// SELECT 'GeometryCollection(POINT (10 10),POINT (30 30),LINESTRING (15 15, 20 20))'::geometry
1077+
let ewkb = hex_to_vec("01070000000300000001010000000000000000002440000000000000244001010000000000000000003E400000000000003E400102000000020000000000000000002E400000000000002E4000000000000034400000000000003440");
1078+
let geom = GeometryCollectionT::<Point>::read_ewkb(&mut ewkb.as_slice()).unwrap();
1079+
assert_eq!(format!("{:?}", geom), "GeometryCollectionT { geometries: [Point(Point { x: 10, y: 10, srid: None }), Point(Point { x: 30, y: 30, srid: None }), LineString(LineStringT { points: [Point { x: 15, y: 15, srid: None }, Point { x: 20, y: 20, srid: None }], srid: None })] }");
1080+
}
1081+
1082+
#[test]
1083+
fn test_geometry_read() {
1084+
// SELECT 'POINT(10 -20 100 1)'::geometry
1085+
let ewkb = hex_to_vec("01010000C0000000000000244000000000000034C00000000000005940000000000000F03F");
1086+
let geom = GeometryT::<PointZM>::read_ewkb(&mut ewkb.as_slice()).unwrap();
1087+
assert_eq!(format!("{:?}", geom), "Point(PointZM { x: 10, y: -20, z: 100, m: 1, srid: None })");
1088+
// SELECT 'SRID=4326;LINESTRING (10 -20 100, 0 -0.5 101)'::geometry
1089+
let ewkb = hex_to_vec("01020000A0E610000002000000000000000000244000000000000034C000000000000059400000000000000000000000000000E0BF0000000000405940");
1090+
let geom = GeometryT::<PointZ>::read_ewkb(&mut ewkb.as_slice()).unwrap();
1091+
assert_eq!(format!("{:?}", geom), "LineString(LineStringT { points: [PointZ { x: 10, y: -20, z: 100, srid: Some(4326) }, PointZ { x: 0, y: -0.5, z: 101, srid: Some(4326) }], srid: Some(4326) })");
1092+
// SELECT 'SRID=4326;POLYGON ((0 0, 2 0, 2 2, 0 2, 0 0))'::geometry
1093+
let ewkb = hex_to_vec("0103000020E610000001000000050000000000000000000000000000000000000000000000000000400000000000000000000000000000004000000000000000400000000000000000000000000000004000000000000000000000000000000000");
1094+
let geom = GeometryT::<Point>::read_ewkb(&mut ewkb.as_slice()).unwrap();
1095+
assert_eq!(format!("{:?}", geom), "Polygon(PolygonT { rings: [LineStringT { points: [Point { x: 0, y: 0, srid: Some(4326) }, Point { x: 2, y: 0, srid: Some(4326) }, Point { x: 2, y: 2, srid: Some(4326) }, Point { x: 0, y: 2, srid: Some(4326) }, Point { x: 0, y: 0, srid: Some(4326) }], srid: Some(4326) }], srid: Some(4326) })");
1096+
// SELECT 'SRID=4326;MULTIPOINT ((10 -20 100), (0 -0.5 101))'::geometry
1097+
let ewkb = hex_to_vec("01040000A0E6100000020000000101000080000000000000244000000000000034C0000000000000594001010000800000000000000000000000000000E0BF0000000000405940");
1098+
let geom = GeometryT::<PointZ>::read_ewkb(&mut ewkb.as_slice()).unwrap();
1099+
assert_eq!(format!("{:?}", geom), "MultiPoint(MultiPointT { points: [PointZ { x: 10, y: -20, z: 100, srid: None }, PointZ { x: 0, y: -0.5, z: 101, srid: None }], srid: Some(4326) })");
1100+
// SELECT 'SRID=4326;MULTILINESTRING ((10 -20, 0 -0.5), (0 0, 2 0))'::geometry
1101+
let ewkb = hex_to_vec("0105000020E610000002000000010200000002000000000000000000244000000000000034C00000000000000000000000000000E0BF0102000000020000000000000000000000000000000000000000000000000000400000000000000000");
1102+
let geom = GeometryT::<Point>::read_ewkb(&mut ewkb.as_slice()).unwrap();
1103+
assert_eq!(format!("{:?}", geom), "MultiLineString(MultiLineStringT { lines: [LineStringT { points: [Point { x: 10, y: -20, srid: None }, Point { x: 0, y: -0.5, srid: None }], srid: None }, LineStringT { points: [Point { x: 0, y: 0, srid: None }, Point { x: 2, y: 0, srid: None }], srid: None }], srid: Some(4326) })");
1104+
// SELECT 'SRID=4326;MULTIPOLYGON (((0 0, 2 0, 2 2, 0 2, 0 0)), ((10 10, -2 10, -2 -2, 10 -2, 10 10)))'::geometry
1105+
let ewkb = hex_to_vec("0106000020E610000002000000010300000001000000050000000000000000000000000000000000000000000000000000400000000000000000000000000000004000000000000000400000000000000000000000000000004000000000000000000000000000000000010300000001000000050000000000000000002440000000000000244000000000000000C0000000000000244000000000000000C000000000000000C0000000000000244000000000000000C000000000000024400000000000002440");
1106+
let geom = GeometryT::<Point>::read_ewkb(&mut ewkb.as_slice()).unwrap();
1107+
assert_eq!(format!("{:?}", geom), "MultiPolygon(MultiPolygonT { polygons: [PolygonT { rings: [LineStringT { points: [Point { x: 0, y: 0, srid: None }, Point { x: 2, y: 0, srid: None }, Point { x: 2, y: 2, srid: None }, Point { x: 0, y: 2, srid: None }, Point { x: 0, y: 0, srid: None }], srid: None }], srid: None }, PolygonT { rings: [LineStringT { points: [Point { x: 10, y: 10, srid: None }, Point { x: -2, y: 10, srid: None }, Point { x: -2, y: -2, srid: None }, Point { x: 10, y: -2, srid: None }, Point { x: 10, y: 10, srid: None }], srid: None }], srid: None }], srid: Some(4326) })");
1108+
// SELECT 'GeometryCollection(POINT (10 10),POINT (30 30),LINESTRING (15 15, 20 20))'::geometry
1109+
let ewkb = hex_to_vec("01070000000300000001010000000000000000002440000000000000244001010000000000000000003E400000000000003E400102000000020000000000000000002E400000000000002E4000000000000034400000000000003440");
1110+
let geom = GeometryT::<Point>::read_ewkb(&mut ewkb.as_slice()).unwrap();
1111+
assert_eq!(format!("{:?}", geom), "GeometryCollection(GeometryCollectionT { geometries: [Point(Point { x: 10, y: 10, srid: None }), Point(Point { x: 30, y: 30, srid: None }), LineString(LineStringT { points: [Point { x: 15, y: 15, srid: None }, Point { x: 20, y: 20, srid: None }], srid: None })] })");
1112+
}
1113+
9691114
#[test]
9701115
fn test_iterators() {
9711116
// Iterator traits:

src/postgis.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,25 @@ impl_sql_for_ewkb_type!(EwkbMultiLineString contains LineString);
154154
impl_sql_for_ewkb_type!(multipoly EwkbMultiPolygon contains Polygon);
155155

156156

157+
impl<P> FromSql for ewkb::GeometryT<P>
158+
where P: Point + EwkbRead
159+
{
160+
accepts_geography!();
161+
fn from_sql<R: Read>(ty: &Type, raw: &mut R, _ctx: &SessionInfo) -> postgres::Result<ewkb::GeometryT<P>> {
162+
ewkb::GeometryT::<P>::read_ewkb(raw).map_err(|_| {let err: Box<std::error::Error + Sync + Send> = format!("cannot convert {} to MultiPoint", ty).into(); postgres::error::Error::Conversion(err)})
163+
}
164+
}
165+
166+
impl<P> FromSql for ewkb::GeometryCollectionT<P>
167+
where P: Point + EwkbRead
168+
{
169+
accepts_geography!();
170+
fn from_sql<R: Read>(ty: &Type, raw: &mut R, _ctx: &SessionInfo) -> postgres::Result<ewkb::GeometryCollectionT<P>> {
171+
ewkb::GeometryCollectionT::<P>::read_ewkb(raw).map_err(|_| {let err: Box<std::error::Error + Sync + Send> = format!("cannot convert {} to MultiPoint", ty).into(); postgres::error::Error::Conversion(err)})
172+
}
173+
}
174+
175+
157176
// --- TWKB ---
158177

159178
macro_rules! accepts_bytea {
@@ -447,6 +466,26 @@ mod tests {
447466
assert_eq!(format!("{:?}", multipoly), "MultiPolygonT { polygons: [PolygonT { rings: [LineStringT { points: [Point { x: 0, y: 0, srid: None }, Point { x: 2, y: 0, srid: None }, Point { x: 2, y: 2, srid: None }, Point { x: 0, y: 2, srid: None }, Point { x: 0, y: 0, srid: None }], srid: None }], srid: None }, PolygonT { rings: [LineStringT { points: [Point { x: 10, y: 10, srid: None }, Point { x: -2, y: 10, srid: None }, Point { x: -2, y: -2, srid: None }, Point { x: 10, y: -2, srid: None }, Point { x: 10, y: 10, srid: None }], srid: None }], srid: None }], srid: Some(4326) }");
448467
}
449468

469+
#[test]
470+
#[ignore]
471+
fn test_select_geometrycollection() {
472+
let conn = connect();
473+
let result = or_panic!(conn.query("SELECT 'GeometryCollection(POINT (10 10),POINT (30 30),LINESTRING (15 15, 20 20))'::geometry", &[]));
474+
let geom = result.iter().map(|r| r.get::<_, ewkb::GeometryCollection>(0)).last().unwrap();
475+
assert_eq!(format!("{:?}", geom), "GeometryCollectionT { geometries: [Point(Point { x: 10, y: 10, srid: None }), Point(Point { x: 30, y: 30, srid: None }), LineString(LineStringT { points: [Point { x: 15, y: 15, srid: None }, Point { x: 20, y: 20, srid: None }], srid: None })] }");
476+
}
477+
478+
#[test]
479+
#[ignore]
480+
fn test_select_geometry() {
481+
let conn = connect();
482+
or_panic!(conn.execute("CREATE TEMPORARY TABLE geomtests (geom geometry)", &[]));
483+
or_panic!(conn.execute("INSERT INTO geomtests VALUES('SRID=4326;POINT(10 -20 99)'::geometry)", &[]));
484+
let result = or_panic!(conn.query("SELECT geom FROM geomtests", &[]));
485+
let geom = result.iter().map(|r| r.get::<_, ewkb::GeometryZ>(0)).last().unwrap();
486+
assert_eq!(format!("{:?}", geom), "Point(PointZ { x: 10, y: -20, z: 99, srid: Some(4326) })");
487+
}
488+
450489
#[test]
451490
#[ignore]
452491
fn test_twkb() {

0 commit comments

Comments
 (0)