Merge pull request #245 from kestred/georust-geo
Support geo types with georust/geo
This commit is contained in:
commit
33d45db60f
39
README.md
39
README.md
@ -253,6 +253,27 @@ types. The driver currently supports the following conversions:
|
|||||||
(<a href="#optional-features">optional</a>)
|
(<a href="#optional-features">optional</a>)
|
||||||
</td>
|
</td>
|
||||||
<td>MACADDR</td>
|
<td>MACADDR</td>
|
||||||
|
</tr
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a href="https://github.com/georust/rust-geo">geo::Point<f64></a>
|
||||||
|
(<a href="#optional-features">optional</a>)
|
||||||
|
</td>
|
||||||
|
<td>POINT</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a href="https://github.com/georust/rust-geo">geo::Bbox<f64></a>
|
||||||
|
(<a href="#optional-features">optional</a>)
|
||||||
|
</td>
|
||||||
|
<td>BOX</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a href="https://github.com/georust/rust-geo">geo::LineString<f64></a>
|
||||||
|
(<a href="#optional-features">optional</a>)
|
||||||
|
</td>
|
||||||
|
<td>PATH</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@ -316,3 +337,21 @@ and `FromSql` implementations for `bit-vec`'s `BitVec` type.
|
|||||||
[MACADDR](http://www.postgresql.org/docs/9.4/static/datatype-net-types.html#DATATYPE-MACADDR)
|
[MACADDR](http://www.postgresql.org/docs/9.4/static/datatype-net-types.html#DATATYPE-MACADDR)
|
||||||
support is provided optionally by the `with-eui48` feature, which adds `ToSql`
|
support is provided optionally by the `with-eui48` feature, which adds `ToSql`
|
||||||
and `FromSql` implementations for `eui48`'s `MacAddress` type.
|
and `FromSql` implementations for `eui48`'s `MacAddress` type.
|
||||||
|
|
||||||
|
### POINT type
|
||||||
|
|
||||||
|
[POINT](https://www.postgresql.org/docs/9.4/static/datatype-geometric.html#AEN6799)
|
||||||
|
support is provided optionally by the `with-geo` feature, which adds `ToSql` and `FromSql` implementations for `geo`'s `Point` type.
|
||||||
|
|
||||||
|
### BOX type
|
||||||
|
|
||||||
|
[BOX](https://www.postgresql.org/docs/9.4/static/datatype-geometric.html#AEN6883)
|
||||||
|
support is provided optionally by the `with-geo` feature, which adds `ToSql` and `FromSql` implementations for `geo`'s `Bbox` type.
|
||||||
|
|
||||||
|
### PATH type
|
||||||
|
|
||||||
|
[PATH](https://www.postgresql.org/docs/9.4/static/datatype-geometric.html#AEN6912)
|
||||||
|
support is provided optionally by the `with-geo` feature, which adds `ToSql` and `FromSql` implementations for `geo`'s `LineString` type.
|
||||||
|
Paths converted from LineString are always treated as "open" paths. Use the
|
||||||
|
[pclose](https://www.postgresql.org/docs/8.2/static/functions-geometry.html#FUNCTIONS-GEOMETRY-FUNC-TABLE)
|
||||||
|
geometric function to insert a closed path.
|
||||||
|
@ -10,6 +10,7 @@ repository = "https://github.com/sfackler/rust-postgres"
|
|||||||
with-bit-vec = ["bit-vec"]
|
with-bit-vec = ["bit-vec"]
|
||||||
with-chrono = ["chrono"]
|
with-chrono = ["chrono"]
|
||||||
with-eui48 = ["eui48"]
|
with-eui48 = ["eui48"]
|
||||||
|
with-geo = ["geo"]
|
||||||
with-rustc-serialize = ["rustc-serialize"]
|
with-rustc-serialize = ["rustc-serialize"]
|
||||||
with-serde_json = ["serde_json"]
|
with-serde_json = ["serde_json"]
|
||||||
with-time = ["time"]
|
with-time = ["time"]
|
||||||
@ -24,6 +25,7 @@ postgres-protocol = "0.2"
|
|||||||
bit-vec = { version = "0.4", optional = true }
|
bit-vec = { version = "0.4", optional = true }
|
||||||
chrono = { version = "0.3", optional = true }
|
chrono = { version = "0.3", optional = true }
|
||||||
eui48 = { version = "0.1", optional = true }
|
eui48 = { version = "0.1", optional = true }
|
||||||
|
geo = { version = "0.4", optional = true }
|
||||||
rustc-serialize = { version = "0.3", optional = true }
|
rustc-serialize = { version = "0.3", optional = true }
|
||||||
serde_json = { version = "0.9", optional = true }
|
serde_json = { version = "0.9", optional = true }
|
||||||
time = { version = "0.1.14", optional = true }
|
time = { version = "0.1.14", optional = true }
|
||||||
|
104
postgres-shared/src/types/geo.rs
Normal file
104
postgres-shared/src/types/geo.rs
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
extern crate geo;
|
||||||
|
|
||||||
|
use postgres_protocol::types;
|
||||||
|
use self::geo::{Bbox, LineString, Point};
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
use types::{FromSql, ToSql, IsNull, Type};
|
||||||
|
|
||||||
|
impl FromSql for Point<f64> {
|
||||||
|
fn from_sql(_: &Type, raw: &[u8]) -> Result<Self, Box<Error + Sync + Send>> {
|
||||||
|
if raw.len() != 16 {
|
||||||
|
return Err("invalid message length".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let x = types::float8_from_sql(&raw[0..8])?;
|
||||||
|
let y = types::float8_from_sql(&raw[8..16])?;
|
||||||
|
Ok(Point::new(x, y))
|
||||||
|
}
|
||||||
|
|
||||||
|
accepts!(Type::Point);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToSql for Point<f64> {
|
||||||
|
fn to_sql(&self, _: &Type, out: &mut Vec<u8>) -> Result<IsNull, Box<Error + Sync + Send>> {
|
||||||
|
types::float8_to_sql(self.x(), out);
|
||||||
|
types::float8_to_sql(self.y(), out);
|
||||||
|
Ok(IsNull::No)
|
||||||
|
}
|
||||||
|
|
||||||
|
accepts!(Type::Point);
|
||||||
|
to_sql_checked!();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromSql for Bbox<f64> {
|
||||||
|
fn from_sql(_: &Type, raw: &[u8]) -> Result<Self, Box<Error + Sync + Send>> {
|
||||||
|
if raw.len() != 32 {
|
||||||
|
return Err("invalid message length".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let xmax = types::float8_from_sql(&raw[0..8])?;
|
||||||
|
let ymax = types::float8_from_sql(&raw[8..16])?;
|
||||||
|
let xmin = types::float8_from_sql(&raw[16..24])?;
|
||||||
|
let ymin = types::float8_from_sql(&raw[24..32])?;
|
||||||
|
Ok(Bbox{xmax: xmax, ymax: ymax, xmin: xmin, ymin: ymin})
|
||||||
|
}
|
||||||
|
|
||||||
|
accepts!(Type::Box);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToSql for Bbox<f64> {
|
||||||
|
fn to_sql(&self, _: &Type, out: &mut Vec<u8>) -> Result<IsNull, Box<Error + Sync + Send>> {
|
||||||
|
types::float8_to_sql(self.xmax, out);
|
||||||
|
types::float8_to_sql(self.ymax, out);
|
||||||
|
types::float8_to_sql(self.xmin, out);
|
||||||
|
types::float8_to_sql(self.ymin, out);
|
||||||
|
Ok(IsNull::No)
|
||||||
|
}
|
||||||
|
|
||||||
|
accepts!(Type::Box);
|
||||||
|
to_sql_checked!();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromSql for LineString<f64> {
|
||||||
|
fn from_sql(_: &Type, raw: &[u8]) -> Result<Self, Box<Error + Sync + Send>> {
|
||||||
|
if raw.len() < 5 {
|
||||||
|
return Err("invalid message length".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// let _ = types::bool_from_sql(&raw[0..1])?; // is path open or closed
|
||||||
|
let n_points = types::int4_from_sql(&raw[1..5])? as usize;
|
||||||
|
let raw_points = &raw[5..raw.len()];
|
||||||
|
if raw_points.len() != 16 * n_points {
|
||||||
|
return Err("invalid message length".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut offset = 0;
|
||||||
|
let mut points = Vec::with_capacity(n_points);
|
||||||
|
for _ in 0..n_points {
|
||||||
|
let x = types::float8_from_sql(&raw_points[offset..offset+8])?;
|
||||||
|
let y = types::float8_from_sql(&raw_points[offset+8..offset+16])?;
|
||||||
|
points.push(Point::new(x, y));
|
||||||
|
offset += 16;
|
||||||
|
}
|
||||||
|
Ok(LineString(points))
|
||||||
|
}
|
||||||
|
|
||||||
|
accepts!(Type::Path);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToSql for LineString<f64> {
|
||||||
|
fn to_sql(&self, _: &Type, out: &mut Vec<u8>) -> Result<IsNull, Box<Error + Sync + Send>> {
|
||||||
|
let closed = false; // always encode an open path from LineString
|
||||||
|
types::bool_to_sql(closed, out);
|
||||||
|
types::int4_to_sql(self.0.len() as i32, out);
|
||||||
|
for point in &self.0 {
|
||||||
|
types::float8_to_sql(point.x(), out);
|
||||||
|
types::float8_to_sql(point.y(), out);
|
||||||
|
}
|
||||||
|
Ok(IsNull::No)
|
||||||
|
}
|
||||||
|
|
||||||
|
accepts!(Type::Path);
|
||||||
|
to_sql_checked!();
|
||||||
|
}
|
@ -73,6 +73,8 @@ mod serde_json;
|
|||||||
mod chrono;
|
mod chrono;
|
||||||
#[cfg(feature = "with-eui48")]
|
#[cfg(feature = "with-eui48")]
|
||||||
mod eui48;
|
mod eui48;
|
||||||
|
#[cfg(feature = "with-geo")]
|
||||||
|
mod geo;
|
||||||
|
|
||||||
mod special;
|
mod special;
|
||||||
mod type_gen;
|
mod type_gen;
|
||||||
|
@ -24,6 +24,7 @@ path = "tests/test.rs"
|
|||||||
with-bit-vec = ["postgres-shared/with-bit-vec"]
|
with-bit-vec = ["postgres-shared/with-bit-vec"]
|
||||||
with-chrono = ["postgres-shared/with-chrono"]
|
with-chrono = ["postgres-shared/with-chrono"]
|
||||||
with-eui48 = ["postgres-shared/with-eui48"]
|
with-eui48 = ["postgres-shared/with-eui48"]
|
||||||
|
with-geo = ["postgres-shared/with-geo"]
|
||||||
with-rustc-serialize = ["postgres-shared/with-rustc-serialize"]
|
with-rustc-serialize = ["postgres-shared/with-rustc-serialize"]
|
||||||
with-serde_json = ["postgres-shared/with-serde_json"]
|
with-serde_json = ["postgres-shared/with-serde_json"]
|
||||||
with-time = ["postgres-shared/with-time"]
|
with-time = ["postgres-shared/with-time"]
|
||||||
@ -57,6 +58,7 @@ url = "1.0"
|
|||||||
bit-vec = "0.4"
|
bit-vec = "0.4"
|
||||||
chrono = "0.3"
|
chrono = "0.3"
|
||||||
eui48 = "0.1"
|
eui48 = "0.1"
|
||||||
|
geo = "0.4"
|
||||||
rustc-serialize = "0.3"
|
rustc-serialize = "0.3"
|
||||||
serde_json = "0.9"
|
serde_json = "0.9"
|
||||||
time = "0.1.14"
|
time = "0.1.14"
|
||||||
|
28
postgres/tests/types/geo.rs
Normal file
28
postgres/tests/types/geo.rs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
extern crate geo;
|
||||||
|
|
||||||
|
use self::geo::{Bbox, LineString, Point};
|
||||||
|
use types::test_type;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_point_params() {
|
||||||
|
test_type("POINT",
|
||||||
|
&[(Some(Point::new(0.0, 0.0)), "POINT(0, 0)"),
|
||||||
|
(Some(Point::new(-3.14, 1.618)), "POINT(-3.14, 1.618)"),
|
||||||
|
(None, "NULL")]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_box_params() {
|
||||||
|
test_type("BOX",
|
||||||
|
&[(Some(Bbox{xmax: 160.0, ymax: 69701.5615, xmin: -3.14, ymin: 1.618}),
|
||||||
|
"BOX(POINT(160.0, 69701.5615), POINT(-3.14, 1.618))"),
|
||||||
|
(None, "NULL")]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_path_params() {
|
||||||
|
let points = vec![Point::new(0.0, 0.0), Point::new(-3.14, 1.618), Point::new(160.0, 69701.5615)];
|
||||||
|
test_type("PATH",
|
||||||
|
&[(Some(LineString(points)),"path '((0, 0), (-3.14, 1.618), (160.0, 69701.5615))'"),
|
||||||
|
(None, "NULL")]);
|
||||||
|
}
|
@ -23,6 +23,8 @@ mod rustc_serialize;
|
|||||||
mod serde_json;
|
mod serde_json;
|
||||||
#[cfg(feature = "with-chrono")]
|
#[cfg(feature = "with-chrono")]
|
||||||
mod chrono;
|
mod chrono;
|
||||||
|
#[cfg(feature = "with-geo")]
|
||||||
|
mod geo;
|
||||||
|
|
||||||
fn test_type<T: PartialEq+FromSql+ToSql, S: fmt::Display>(sql_type: &str, checks: &[(T, S)]) {
|
fn test_type<T: PartialEq+FromSql+ToSql, S: fmt::Display>(sql_type: &str, checks: &[(T, S)]) {
|
||||||
let conn = or_panic!(Connection::connect("postgres://postgres@localhost", TlsMode::None));
|
let conn = or_panic!(Connection::connect("postgres://postgres@localhost", TlsMode::None));
|
||||||
|
@ -13,6 +13,7 @@ keywords = ["database", "postgres", "postgresql", "sql", "async"]
|
|||||||
with-bit-vec = ["postgres-shared/with-bit-vec"]
|
with-bit-vec = ["postgres-shared/with-bit-vec"]
|
||||||
with-chrono = ["postgres-shared/with-chrono"]
|
with-chrono = ["postgres-shared/with-chrono"]
|
||||||
with-eui48 = ["postgres-shared/with-eui48"]
|
with-eui48 = ["postgres-shared/with-eui48"]
|
||||||
|
with-geo = ["postgres-shared/with-geo"]
|
||||||
with-rustc-serialize = ["postgres-shared/with-rustc-serialize"]
|
with-rustc-serialize = ["postgres-shared/with-rustc-serialize"]
|
||||||
with-serde_json = ["postgres-shared/with-serde_json"]
|
with-serde_json = ["postgres-shared/with-serde_json"]
|
||||||
with-time = ["postgres-shared/with-time"]
|
with-time = ["postgres-shared/with-time"]
|
||||||
|
Loading…
Reference in New Issue
Block a user