Skip to content

Commit 33d45db

Browse files
authored
Merge pull request #245 from kestred/georust-geo
Support geo types with georust/geo
2 parents 2536131 + 72d60ee commit 33d45db

File tree

8 files changed

+180
-0
lines changed

8 files changed

+180
-0
lines changed

README.md

+39
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,27 @@ types. The driver currently supports the following conversions:
253253
(<a href="#optional-features">optional</a>)
254254
</td>
255255
<td>MACADDR</td>
256+
</tr
257+
<tr>
258+
<td>
259+
<a href="https://github.com/georust/rust-geo">geo::Point&lt;f64&gt;</a>
260+
(<a href="#optional-features">optional</a>)
261+
</td>
262+
<td>POINT</td>
263+
</tr>
264+
<tr>
265+
<td>
266+
<a href="https://github.com/georust/rust-geo">geo::Bbox&lt;f64&gt;</a>
267+
(<a href="#optional-features">optional</a>)
268+
</td>
269+
<td>BOX</td>
270+
</tr>
271+
<tr>
272+
<td>
273+
<a href="https://github.com/georust/rust-geo">geo::LineString&lt;f64&gt;</a>
274+
(<a href="#optional-features">optional</a>)
275+
</td>
276+
<td>PATH</td>
256277
</tr>
257278
</tbody>
258279
</table>
@@ -316,3 +337,21 @@ and `FromSql` implementations for `bit-vec`'s `BitVec` type.
316337
[MACADDR](http://www.postgresql.org/docs/9.4/static/datatype-net-types.html#DATATYPE-MACADDR)
317338
support is provided optionally by the `with-eui48` feature, which adds `ToSql`
318339
and `FromSql` implementations for `eui48`'s `MacAddress` type.
340+
341+
### POINT type
342+
343+
[POINT](https://www.postgresql.org/docs/9.4/static/datatype-geometric.html#AEN6799)
344+
support is provided optionally by the `with-geo` feature, which adds `ToSql` and `FromSql` implementations for `geo`'s `Point` type.
345+
346+
### BOX type
347+
348+
[BOX](https://www.postgresql.org/docs/9.4/static/datatype-geometric.html#AEN6883)
349+
support is provided optionally by the `with-geo` feature, which adds `ToSql` and `FromSql` implementations for `geo`'s `Bbox` type.
350+
351+
### PATH type
352+
353+
[PATH](https://www.postgresql.org/docs/9.4/static/datatype-geometric.html#AEN6912)
354+
support is provided optionally by the `with-geo` feature, which adds `ToSql` and `FromSql` implementations for `geo`'s `LineString` type.
355+
Paths converted from LineString are always treated as "open" paths. Use the
356+
[pclose](https://www.postgresql.org/docs/8.2/static/functions-geometry.html#FUNCTIONS-GEOMETRY-FUNC-TABLE)
357+
geometric function to insert a closed path.

postgres-shared/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ repository = "https://github.com/sfackler/rust-postgres"
1010
with-bit-vec = ["bit-vec"]
1111
with-chrono = ["chrono"]
1212
with-eui48 = ["eui48"]
13+
with-geo = ["geo"]
1314
with-rustc-serialize = ["rustc-serialize"]
1415
with-serde_json = ["serde_json"]
1516
with-time = ["time"]
@@ -24,6 +25,7 @@ postgres-protocol = "0.2"
2425
bit-vec = { version = "0.4", optional = true }
2526
chrono = { version = "0.3", optional = true }
2627
eui48 = { version = "0.1", optional = true }
28+
geo = { version = "0.4", optional = true }
2729
rustc-serialize = { version = "0.3", optional = true }
2830
serde_json = { version = "0.9", optional = true }
2931
time = { version = "0.1.14", optional = true }

postgres-shared/src/types/geo.rs

+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
extern crate geo;
2+
3+
use postgres_protocol::types;
4+
use self::geo::{Bbox, LineString, Point};
5+
use std::error::Error;
6+
7+
use types::{FromSql, ToSql, IsNull, Type};
8+
9+
impl FromSql for Point<f64> {
10+
fn from_sql(_: &Type, raw: &[u8]) -> Result<Self, Box<Error + Sync + Send>> {
11+
if raw.len() != 16 {
12+
return Err("invalid message length".into());
13+
}
14+
15+
let x = types::float8_from_sql(&raw[0..8])?;
16+
let y = types::float8_from_sql(&raw[8..16])?;
17+
Ok(Point::new(x, y))
18+
}
19+
20+
accepts!(Type::Point);
21+
}
22+
23+
impl ToSql for Point<f64> {
24+
fn to_sql(&self, _: &Type, out: &mut Vec<u8>) -> Result<IsNull, Box<Error + Sync + Send>> {
25+
types::float8_to_sql(self.x(), out);
26+
types::float8_to_sql(self.y(), out);
27+
Ok(IsNull::No)
28+
}
29+
30+
accepts!(Type::Point);
31+
to_sql_checked!();
32+
}
33+
34+
impl FromSql for Bbox<f64> {
35+
fn from_sql(_: &Type, raw: &[u8]) -> Result<Self, Box<Error + Sync + Send>> {
36+
if raw.len() != 32 {
37+
return Err("invalid message length".into());
38+
}
39+
40+
let xmax = types::float8_from_sql(&raw[0..8])?;
41+
let ymax = types::float8_from_sql(&raw[8..16])?;
42+
let xmin = types::float8_from_sql(&raw[16..24])?;
43+
let ymin = types::float8_from_sql(&raw[24..32])?;
44+
Ok(Bbox{xmax: xmax, ymax: ymax, xmin: xmin, ymin: ymin})
45+
}
46+
47+
accepts!(Type::Box);
48+
}
49+
50+
impl ToSql for Bbox<f64> {
51+
fn to_sql(&self, _: &Type, out: &mut Vec<u8>) -> Result<IsNull, Box<Error + Sync + Send>> {
52+
types::float8_to_sql(self.xmax, out);
53+
types::float8_to_sql(self.ymax, out);
54+
types::float8_to_sql(self.xmin, out);
55+
types::float8_to_sql(self.ymin, out);
56+
Ok(IsNull::No)
57+
}
58+
59+
accepts!(Type::Box);
60+
to_sql_checked!();
61+
}
62+
63+
impl FromSql for LineString<f64> {
64+
fn from_sql(_: &Type, raw: &[u8]) -> Result<Self, Box<Error + Sync + Send>> {
65+
if raw.len() < 5 {
66+
return Err("invalid message length".into());
67+
}
68+
69+
// let _ = types::bool_from_sql(&raw[0..1])?; // is path open or closed
70+
let n_points = types::int4_from_sql(&raw[1..5])? as usize;
71+
let raw_points = &raw[5..raw.len()];
72+
if raw_points.len() != 16 * n_points {
73+
return Err("invalid message length".into());
74+
}
75+
76+
let mut offset = 0;
77+
let mut points = Vec::with_capacity(n_points);
78+
for _ in 0..n_points {
79+
let x = types::float8_from_sql(&raw_points[offset..offset+8])?;
80+
let y = types::float8_from_sql(&raw_points[offset+8..offset+16])?;
81+
points.push(Point::new(x, y));
82+
offset += 16;
83+
}
84+
Ok(LineString(points))
85+
}
86+
87+
accepts!(Type::Path);
88+
}
89+
90+
impl ToSql for LineString<f64> {
91+
fn to_sql(&self, _: &Type, out: &mut Vec<u8>) -> Result<IsNull, Box<Error + Sync + Send>> {
92+
let closed = false; // always encode an open path from LineString
93+
types::bool_to_sql(closed, out);
94+
types::int4_to_sql(self.0.len() as i32, out);
95+
for point in &self.0 {
96+
types::float8_to_sql(point.x(), out);
97+
types::float8_to_sql(point.y(), out);
98+
}
99+
Ok(IsNull::No)
100+
}
101+
102+
accepts!(Type::Path);
103+
to_sql_checked!();
104+
}

postgres-shared/src/types/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ mod serde_json;
7373
mod chrono;
7474
#[cfg(feature = "with-eui48")]
7575
mod eui48;
76+
#[cfg(feature = "with-geo")]
77+
mod geo;
7678

7779
mod special;
7880
mod type_gen;

postgres/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ path = "tests/test.rs"
2424
with-bit-vec = ["postgres-shared/with-bit-vec"]
2525
with-chrono = ["postgres-shared/with-chrono"]
2626
with-eui48 = ["postgres-shared/with-eui48"]
27+
with-geo = ["postgres-shared/with-geo"]
2728
with-rustc-serialize = ["postgres-shared/with-rustc-serialize"]
2829
with-serde_json = ["postgres-shared/with-serde_json"]
2930
with-time = ["postgres-shared/with-time"]
@@ -57,6 +58,7 @@ url = "1.0"
5758
bit-vec = "0.4"
5859
chrono = "0.3"
5960
eui48 = "0.1"
61+
geo = "0.4"
6062
rustc-serialize = "0.3"
6163
serde_json = "0.9"
6264
time = "0.1.14"

postgres/tests/types/geo.rs

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
extern crate geo;
2+
3+
use self::geo::{Bbox, LineString, Point};
4+
use types::test_type;
5+
6+
#[test]
7+
fn test_point_params() {
8+
test_type("POINT",
9+
&[(Some(Point::new(0.0, 0.0)), "POINT(0, 0)"),
10+
(Some(Point::new(-3.14, 1.618)), "POINT(-3.14, 1.618)"),
11+
(None, "NULL")]);
12+
}
13+
14+
#[test]
15+
fn test_box_params() {
16+
test_type("BOX",
17+
&[(Some(Bbox{xmax: 160.0, ymax: 69701.5615, xmin: -3.14, ymin: 1.618}),
18+
"BOX(POINT(160.0, 69701.5615), POINT(-3.14, 1.618))"),
19+
(None, "NULL")]);
20+
}
21+
22+
#[test]
23+
fn test_path_params() {
24+
let points = vec![Point::new(0.0, 0.0), Point::new(-3.14, 1.618), Point::new(160.0, 69701.5615)];
25+
test_type("PATH",
26+
&[(Some(LineString(points)),"path '((0, 0), (-3.14, 1.618), (160.0, 69701.5615))'"),
27+
(None, "NULL")]);
28+
}

postgres/tests/types/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ mod rustc_serialize;
2323
mod serde_json;
2424
#[cfg(feature = "with-chrono")]
2525
mod chrono;
26+
#[cfg(feature = "with-geo")]
27+
mod geo;
2628

2729
fn test_type<T: PartialEq+FromSql+ToSql, S: fmt::Display>(sql_type: &str, checks: &[(T, S)]) {
2830
let conn = or_panic!(Connection::connect("postgres://postgres@localhost", TlsMode::None));

tokio-postgres/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ keywords = ["database", "postgres", "postgresql", "sql", "async"]
1313
with-bit-vec = ["postgres-shared/with-bit-vec"]
1414
with-chrono = ["postgres-shared/with-chrono"]
1515
with-eui48 = ["postgres-shared/with-eui48"]
16+
with-geo = ["postgres-shared/with-geo"]
1617
with-rustc-serialize = ["postgres-shared/with-rustc-serialize"]
1718
with-serde_json = ["postgres-shared/with-serde_json"]
1819
with-time = ["postgres-shared/with-time"]

0 commit comments

Comments
 (0)