Enable inner field access on sql::Geometry
types (#2203)
This commit is contained in:
parent
55918b7c0e
commit
31ccb0c904
5 changed files with 367 additions and 33 deletions
|
@ -42,6 +42,12 @@ impl From<Vec<i32>> for Array {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Vec<f64>> for Array {
|
||||||
|
fn from(v: Vec<f64>) -> Self {
|
||||||
|
Self(v.into_iter().map(Value::from).collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<Vec<&str>> for Array {
|
impl From<Vec<&str>> for Array {
|
||||||
fn from(v: Vec<&str>) -> Self {
|
fn from(v: Vec<&str>) -> Self {
|
||||||
Self(v.into_iter().map(Value::from).collect())
|
Self(v.into_iter().map(Value::from).collect())
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
#![allow(clippy::derived_hash_with_manual_eq)]
|
#![allow(clippy::derived_hash_with_manual_eq)]
|
||||||
|
|
||||||
|
use crate::sql::array::Array;
|
||||||
use crate::sql::comment::mightbespace;
|
use crate::sql::comment::mightbespace;
|
||||||
use crate::sql::common::{closebracket, closeparentheses, commas, openbracket, openparentheses};
|
use crate::sql::common::{
|
||||||
|
closebraces, closebracket, closeparentheses, commas, openbraces, openbracket, openparentheses,
|
||||||
|
};
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
use crate::sql::fmt::Fmt;
|
use crate::sql::fmt::Fmt;
|
||||||
|
use crate::sql::value::Value;
|
||||||
use geo::algorithm::contains::Contains;
|
use geo::algorithm::contains::Contains;
|
||||||
use geo::algorithm::intersects::Intersects;
|
use geo::algorithm::intersects::Intersects;
|
||||||
use geo::{Coord, LineString, Point, Polygon};
|
use geo::{Coord, LineString, Point, Polygon};
|
||||||
|
@ -40,35 +44,122 @@ pub enum Geometry {
|
||||||
// Add new variants here
|
// Add new variants here
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Geometry {
|
||||||
|
/// Check if this is a Point
|
||||||
|
pub fn is_point(&self) -> bool {
|
||||||
|
matches!(self, Self::Point(_))
|
||||||
|
}
|
||||||
|
/// Check if this is a Line
|
||||||
|
pub fn is_line(&self) -> bool {
|
||||||
|
matches!(self, Self::Line(_))
|
||||||
|
}
|
||||||
|
/// Check if this is a Polygon
|
||||||
|
pub fn is_polygon(&self) -> bool {
|
||||||
|
matches!(self, Self::Polygon(_))
|
||||||
|
}
|
||||||
|
/// Check if this is a MultiPoint
|
||||||
|
pub fn is_multipoint(&self) -> bool {
|
||||||
|
matches!(self, Self::MultiPoint(_))
|
||||||
|
}
|
||||||
|
/// Check if this is a MultiLine
|
||||||
|
pub fn is_multiline(&self) -> bool {
|
||||||
|
matches!(self, Self::MultiLine(_))
|
||||||
|
}
|
||||||
|
/// Check if this is a MultiPolygon
|
||||||
|
pub fn is_multipolygon(&self) -> bool {
|
||||||
|
matches!(self, Self::MultiPolygon(_))
|
||||||
|
}
|
||||||
|
/// Check if this is not a Collection
|
||||||
|
pub fn is_geometry(&self) -> bool {
|
||||||
|
!matches!(self, Self::Collection(_))
|
||||||
|
}
|
||||||
|
/// Check if this is a Collection
|
||||||
|
pub fn is_collection(&self) -> bool {
|
||||||
|
matches!(self, Self::Collection(_))
|
||||||
|
}
|
||||||
|
/// Get the type of this Geometry as text
|
||||||
|
pub fn as_type(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::Point(_) => "Point",
|
||||||
|
Self::Line(_) => "LineString",
|
||||||
|
Self::Polygon(_) => "Polygon",
|
||||||
|
Self::MultiPoint(_) => "MultiPoint",
|
||||||
|
Self::MultiLine(_) => "MultiLineString",
|
||||||
|
Self::MultiPolygon(_) => "MultiPolygon",
|
||||||
|
Self::Collection(_) => "GeometryCollection",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Get the raw coordinates of this Geometry as an Array
|
||||||
|
pub fn as_coordinates(&self) -> Value {
|
||||||
|
fn point(v: &Point) -> Value {
|
||||||
|
Array::from(vec![v.x(), v.y()]).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn line(v: &LineString) -> Value {
|
||||||
|
v.points().map(|v| point(&v)).collect::<Vec<Value>>().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn polygon(v: &Polygon) -> Value {
|
||||||
|
once(v.exterior()).chain(v.interiors()).map(line).collect::<Vec<Value>>().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn multipoint(v: &MultiPoint) -> Value {
|
||||||
|
v.iter().map(point).collect::<Vec<Value>>().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn multiline(v: &MultiLineString) -> Value {
|
||||||
|
v.iter().map(line).collect::<Vec<Value>>().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn multipolygon(v: &MultiPolygon) -> Value {
|
||||||
|
v.iter().map(polygon).collect::<Vec<Value>>().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collection(v: &[Geometry]) -> Value {
|
||||||
|
v.iter().map(Geometry::as_coordinates).collect::<Vec<Value>>().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
match self {
|
||||||
|
Self::Point(v) => point(v),
|
||||||
|
Self::Line(v) => line(v),
|
||||||
|
Self::Polygon(v) => polygon(v),
|
||||||
|
Self::MultiPoint(v) => multipoint(v),
|
||||||
|
Self::MultiLine(v) => multiline(v),
|
||||||
|
Self::MultiPolygon(v) => multipolygon(v),
|
||||||
|
Self::Collection(v) => collection(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PartialOrd for Geometry {
|
impl PartialOrd for Geometry {
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
fn coord(coord: &Coord) -> (f64, f64) {
|
fn coord(v: &Coord) -> (f64, f64) {
|
||||||
coord.x_y()
|
v.x_y()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn point(point: &Point) -> (f64, f64) {
|
fn point(v: &Point) -> (f64, f64) {
|
||||||
coord(&point.0)
|
coord(&v.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn line(line: &LineString) -> impl Iterator<Item = (f64, f64)> + '_ {
|
fn line(v: &LineString) -> impl Iterator<Item = (f64, f64)> + '_ {
|
||||||
line.into_iter().map(coord)
|
v.into_iter().map(coord)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn polygon(polygon: &Polygon) -> impl Iterator<Item = (f64, f64)> + '_ {
|
fn polygon(v: &Polygon) -> impl Iterator<Item = (f64, f64)> + '_ {
|
||||||
polygon.interiors().iter().chain(once(polygon.exterior())).flat_map(line)
|
v.interiors().iter().chain(once(v.exterior())).flat_map(line)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn multi_point(multi_point: &MultiPoint) -> impl Iterator<Item = (f64, f64)> + '_ {
|
fn multipoint(v: &MultiPoint) -> impl Iterator<Item = (f64, f64)> + '_ {
|
||||||
multi_point.iter().map(point)
|
v.iter().map(point)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn multi_line(multi_line: &MultiLineString) -> impl Iterator<Item = (f64, f64)> + '_ {
|
fn multiline(v: &MultiLineString) -> impl Iterator<Item = (f64, f64)> + '_ {
|
||||||
multi_line.iter().flat_map(line)
|
v.iter().flat_map(line)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn multi_polygon(multi_polygon: &MultiPolygon) -> impl Iterator<Item = (f64, f64)> + '_ {
|
fn multipolygon(v: &MultiPolygon) -> impl Iterator<Item = (f64, f64)> + '_ {
|
||||||
multi_polygon.iter().flat_map(polygon)
|
v.iter().flat_map(polygon)
|
||||||
}
|
}
|
||||||
|
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
|
@ -125,9 +216,9 @@ impl PartialOrd for Geometry {
|
||||||
(Self::Point(a), Self::Point(b)) => point(a).partial_cmp(&point(b)),
|
(Self::Point(a), Self::Point(b)) => point(a).partial_cmp(&point(b)),
|
||||||
(Self::Line(a), Self::Line(b)) => line(a).partial_cmp(line(b)),
|
(Self::Line(a), Self::Line(b)) => line(a).partial_cmp(line(b)),
|
||||||
(Self::Polygon(a), Self::Polygon(b)) => polygon(a).partial_cmp(polygon(b)),
|
(Self::Polygon(a), Self::Polygon(b)) => polygon(a).partial_cmp(polygon(b)),
|
||||||
(Self::MultiPoint(a), Self::MultiPoint(b)) => multi_point(a).partial_cmp(multi_point(b)),
|
(Self::MultiPoint(a), Self::MultiPoint(b)) => multipoint(a).partial_cmp(multipoint(b)),
|
||||||
(Self::MultiLine(a), Self::MultiLine(b)) => multi_line(a).partial_cmp(multi_line(b)),
|
(Self::MultiLine(a), Self::MultiLine(b)) => multiline(a).partial_cmp(multiline(b)),
|
||||||
(Self::MultiPolygon(a), Self::MultiPolygon(b)) => multi_polygon(a).partial_cmp(multi_polygon(b)),
|
(Self::MultiPolygon(a), Self::MultiPolygon(b)) => multipolygon(a).partial_cmp(multipolygon(b)),
|
||||||
(Self::Collection(a), Self::Collection(b)) => a.partial_cmp(b),
|
(Self::Collection(a), Self::Collection(b)) => a.partial_cmp(b),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -547,13 +638,11 @@ fn simple(i: &str) -> IResult<&str, Geometry> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn normal(i: &str) -> IResult<&str, Geometry> {
|
fn normal(i: &str) -> IResult<&str, Geometry> {
|
||||||
let (i, _) = char('{')(i)?;
|
let (i, _) = openbraces(i)?;
|
||||||
let (i, _) = mightbespace(i)?;
|
|
||||||
let (i, v) = alt((point, line, polygon, multipoint, multiline, multipolygon, collection))(i)?;
|
let (i, v) = alt((point, line, polygon, multipoint, multiline, multipolygon, collection))(i)?;
|
||||||
let (i, _) = mightbespace(i)?;
|
let (i, _) = mightbespace(i)?;
|
||||||
let (i, _) = opt(char(','))(i)?;
|
let (i, _) = opt(char(','))(i)?;
|
||||||
let (i, _) = mightbespace(i)?;
|
let (i, _) = closebraces(i)?;
|
||||||
let (i, _) = char('}')(i)?;
|
|
||||||
Ok((i, v))
|
Ok((i, v))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -706,15 +795,11 @@ fn polygon_vals(i: &str) -> IResult<&str, Polygon<f64>> {
|
||||||
let (i, e) = line_vals(i)?;
|
let (i, e) = line_vals(i)?;
|
||||||
let (i, _) = mightbespace(i)?;
|
let (i, _) = mightbespace(i)?;
|
||||||
let (i, _) = opt(char(','))(i)?;
|
let (i, _) = opt(char(','))(i)?;
|
||||||
|
let (i, _) = mightbespace(i)?;
|
||||||
|
let (i, v) = separated_list0(commas, line_vals)(i)?;
|
||||||
|
let (i, _) = mightbespace(i)?;
|
||||||
|
let (i, _) = opt(char(','))(i)?;
|
||||||
let (i, _) = closebracket(i)?;
|
let (i, _) = closebracket(i)?;
|
||||||
let (i, v) = separated_list0(commas, |i| {
|
|
||||||
let (i, _) = openbracket(i)?;
|
|
||||||
let (i, v) = line_vals(i)?;
|
|
||||||
let (i, _) = mightbespace(i)?;
|
|
||||||
let (i, _) = opt(char(','))(i)?;
|
|
||||||
let (i, _) = closebracket(i)?;
|
|
||||||
Ok((i, v))
|
|
||||||
})(i)?;
|
|
||||||
Ok((i, Polygon::new(e, v)))
|
Ok((i, Polygon::new(e, v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -876,10 +961,63 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn simple() {
|
fn simple() {
|
||||||
let sql = "(51.509865, -0.118092)";
|
let sql = "(-0.118092, 51.509865)";
|
||||||
let res = geometry(sql);
|
let res = geometry(sql);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
let out = res.unwrap().1;
|
let out = res.unwrap().1;
|
||||||
assert_eq!("(51.509865, -0.118092)", format!("{}", out));
|
assert_eq!("(-0.118092, 51.509865)", format!("{}", out));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn point() {
|
||||||
|
let sql = r#"{
|
||||||
|
type: 'Point',
|
||||||
|
coordinates: [-0.118092, 51.509865]
|
||||||
|
}"#;
|
||||||
|
let res = geometry(sql);
|
||||||
|
assert!(res.is_ok());
|
||||||
|
let out = res.unwrap().1;
|
||||||
|
assert_eq!("(-0.118092, 51.509865)", format!("{}", out));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn polygon_exterior() {
|
||||||
|
let sql = r#"{
|
||||||
|
type: 'Polygon',
|
||||||
|
coordinates: [
|
||||||
|
[
|
||||||
|
[-0.38314819, 51.37692386], [0.1785278, 51.37692386],
|
||||||
|
[0.1785278, 51.61460570], [-0.38314819, 51.61460570],
|
||||||
|
[-0.38314819, 51.37692386]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}"#;
|
||||||
|
let res = geometry(sql);
|
||||||
|
assert!(res.is_ok());
|
||||||
|
let out = res.unwrap().1;
|
||||||
|
assert_eq!("{ type: 'Polygon', coordinates: [[[-0.38314819, 51.37692386], [0.1785278, 51.37692386], [0.1785278, 51.6146057], [-0.38314819, 51.6146057], [-0.38314819, 51.37692386]]] }", format!("{}", out));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn polygon_interior() {
|
||||||
|
let sql = r#"{
|
||||||
|
type: 'Polygon',
|
||||||
|
coordinates: [
|
||||||
|
[
|
||||||
|
[-0.38314819, 51.37692386], [0.1785278, 51.37692386],
|
||||||
|
[0.1785278, 51.61460570], [-0.38314819, 51.61460570],
|
||||||
|
[-0.38314819, 51.37692386]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[-0.38314819, 51.37692386], [0.1785278, 51.37692386],
|
||||||
|
[0.1785278, 51.61460570], [-0.38314819, 51.61460570],
|
||||||
|
[-0.38314819, 51.37692386]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}"#;
|
||||||
|
let res = geometry(sql);
|
||||||
|
assert!(res.is_ok());
|
||||||
|
let out = res.unwrap().1;
|
||||||
|
assert_eq!("{ type: 'Polygon', coordinates: [[[-0.38314819, 51.37692386], [0.1785278, 51.37692386], [0.1785278, 51.6146057], [-0.38314819, 51.6146057], [-0.38314819, 51.37692386]], [[[-0.38314819, 51.37692386], [0.1785278, 51.37692386], [0.1785278, 51.6146057], [-0.38314819, 51.6146057], [-0.38314819, 51.37692386]]]] }", format!("{}", out));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,10 +51,22 @@ impl Ident {
|
||||||
pub fn to_raw(&self) -> String {
|
pub fn to_raw(&self) -> String {
|
||||||
self.0.to_string()
|
self.0.to_string()
|
||||||
}
|
}
|
||||||
/// Returns a yield if an alias is specified
|
/// Checks if this field is the `id` field
|
||||||
pub(crate) fn is_id(&self) -> bool {
|
pub(crate) fn is_id(&self) -> bool {
|
||||||
self.0.as_str() == "id"
|
self.0.as_str() == "id"
|
||||||
}
|
}
|
||||||
|
/// Checks if this field is the `type` field
|
||||||
|
pub(crate) fn is_type(&self) -> bool {
|
||||||
|
self.0.as_str() == "type"
|
||||||
|
}
|
||||||
|
/// Checks if this field is the `coordinates` field
|
||||||
|
pub(crate) fn is_coordinates(&self) -> bool {
|
||||||
|
self.0.as_str() == "coordinates"
|
||||||
|
}
|
||||||
|
/// Checks if this field is the `geometries` field
|
||||||
|
pub(crate) fn is_geometries(&self) -> bool {
|
||||||
|
self.0.as_str() == "geometries"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Ident {
|
impl Display for Ident {
|
||||||
|
|
|
@ -26,6 +26,24 @@ impl Value {
|
||||||
match path.first() {
|
match path.first() {
|
||||||
// Get the current path part
|
// Get the current path part
|
||||||
Some(p) => match self {
|
Some(p) => match self {
|
||||||
|
// Current path part is a geometry
|
||||||
|
Value::Geometry(v) => match p {
|
||||||
|
// If this is the 'type' field then continue
|
||||||
|
Part::Field(f) if f.is_type() => {
|
||||||
|
Value::from(v.as_type()).get(ctx, opt, path.next()).await
|
||||||
|
}
|
||||||
|
// If this is the 'coordinates' field then continue
|
||||||
|
Part::Field(f) if f.is_coordinates() && v.is_geometry() => {
|
||||||
|
v.as_coordinates().get(ctx, opt, path.next()).await
|
||||||
|
}
|
||||||
|
// If this is the 'geometries' field then continue
|
||||||
|
Part::Field(f) if f.is_geometries() && v.is_collection() => {
|
||||||
|
v.as_coordinates().get(ctx, opt, path.next()).await
|
||||||
|
}
|
||||||
|
// otherwise return none
|
||||||
|
_ => Ok(Value::None),
|
||||||
|
},
|
||||||
|
|
||||||
// Current path part is a future
|
// Current path part is a future
|
||||||
Value::Future(v) => {
|
Value::Future(v) => {
|
||||||
// Check how many path parts are remaining
|
// Check how many path parts are remaining
|
||||||
|
|
|
@ -309,3 +309,163 @@ async fn geometry_multipolygon() -> Result<(), Error> {
|
||||||
//
|
//
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn geometry_inner_access() -> Result<(), Error> {
|
||||||
|
let sql = "
|
||||||
|
SELECT type, coordinates[0] as lng, coordinates[1] AS lat FROM type::point([-0.118092, 51.509865]);
|
||||||
|
SELECT type, coordinates[0] as lng, coordinates[1] AS lat FROM (-0.118092, 51.509865);
|
||||||
|
SELECT coordinates FROM {
|
||||||
|
type: 'Polygon',
|
||||||
|
coordinates: [
|
||||||
|
[
|
||||||
|
[-0.38314819, 51.37692386], [0.1785278, 51.37692386],
|
||||||
|
[0.1785278, 51.61460570], [-0.38314819, 51.61460570],
|
||||||
|
[-0.38314819, 51.37692386],
|
||||||
|
]
|
||||||
|
],
|
||||||
|
};
|
||||||
|
SELECT coordinates FROM {
|
||||||
|
type: 'Polygon',
|
||||||
|
coordinates: [
|
||||||
|
[
|
||||||
|
[-0.38314819, 51.37692386], [0.1785278, 51.37692386],
|
||||||
|
[0.1785278, 51.61460570], [-0.38314819, 51.61460570],
|
||||||
|
[-0.38314819, 51.37692386],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[-0.38314819, 51.37692386], [-0.38314819, 51.61460570],
|
||||||
|
[-0.38314819, 51.37692386],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[110.38314819, 110.37692386], [110.38314819, 110.61460570],
|
||||||
|
[110.38314819, 110.37692386],
|
||||||
|
]
|
||||||
|
],
|
||||||
|
};
|
||||||
|
";
|
||||||
|
let dbs = Datastore::new("memory").await?;
|
||||||
|
let ses = Session::for_kv().with_ns("test").with_db("test");
|
||||||
|
let res = &mut dbs.execute(sql, &ses, None, false).await?;
|
||||||
|
assert_eq!(res.len(), 4);
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::parse(
|
||||||
|
r#"[
|
||||||
|
{
|
||||||
|
lat: 51.509865,
|
||||||
|
lng: -0.118092,
|
||||||
|
type: 'Point'
|
||||||
|
}
|
||||||
|
]"#,
|
||||||
|
);
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::parse(
|
||||||
|
r#"[
|
||||||
|
{
|
||||||
|
lat: 51.509865,
|
||||||
|
lng: -0.118092,
|
||||||
|
type: 'Point'
|
||||||
|
}
|
||||||
|
]"#,
|
||||||
|
);
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::parse(
|
||||||
|
r#"[
|
||||||
|
{
|
||||||
|
coordinates: [
|
||||||
|
[
|
||||||
|
[
|
||||||
|
-0.38314819,
|
||||||
|
51.37692386
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.1785278,
|
||||||
|
51.37692386
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.1785278,
|
||||||
|
51.6146057
|
||||||
|
],
|
||||||
|
[
|
||||||
|
-0.38314819,
|
||||||
|
51.6146057
|
||||||
|
],
|
||||||
|
[
|
||||||
|
-0.38314819,
|
||||||
|
51.37692386
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]"#,
|
||||||
|
);
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::parse(
|
||||||
|
r#"[
|
||||||
|
{
|
||||||
|
coordinates: [
|
||||||
|
[
|
||||||
|
[
|
||||||
|
-0.38314819,
|
||||||
|
51.37692386
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.1785278,
|
||||||
|
51.37692386
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.1785278,
|
||||||
|
51.6146057
|
||||||
|
],
|
||||||
|
[
|
||||||
|
-0.38314819,
|
||||||
|
51.6146057
|
||||||
|
],
|
||||||
|
[
|
||||||
|
-0.38314819,
|
||||||
|
51.37692386
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
-0.38314819,
|
||||||
|
51.37692386
|
||||||
|
],
|
||||||
|
[
|
||||||
|
-0.38314819,
|
||||||
|
51.6146057
|
||||||
|
],
|
||||||
|
[
|
||||||
|
-0.38314819,
|
||||||
|
51.37692386
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
110.38314819,
|
||||||
|
110.37692386
|
||||||
|
],
|
||||||
|
[
|
||||||
|
110.38314819,
|
||||||
|
110.6146057
|
||||||
|
],
|
||||||
|
[
|
||||||
|
110.38314819,
|
||||||
|
110.37692386
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]"#,
|
||||||
|
);
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
//
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue