Skip to content

Commit 7fbd47b

Browse files
jagillfacebook-github-bot
authored andcommitted
feat: Implement ST_Point ST_X ST_Y (facebookincubator#13372)
Summary: Pull Request resolved: facebookincubator#13372 These construct a point and access its members. Reviewed By: bikramSingh91, Sullivan-Patrick Differential Revision: D74898391 fbshipit-source-id: 40b64b7b4805f67dc76fa7537718a42131c1b480
1 parent eda9628 commit 7fbd47b

File tree

5 files changed

+159
-14
lines changed

5 files changed

+159
-14
lines changed

velox/docs/functions/presto/geospatial.rst

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ Geometry Constructors
6767

6868
Returns the Well-Known Binary (WKB) formatted binary of the Geometry object.
6969

70+
.. function:: ST_Point(x: double, y: double) -> geometry: Geometry
71+
72+
Returns the Point geometry at the given coordinates. This will raise an
73+
error if ``x`` or ``y`` is ``NaN`` or ``infinity``.
74+
7075
Spatial Predicates
7176
------------------
7277

@@ -160,6 +165,17 @@ Accessors
160165
returns the sum of the areas of the individual geometries. Empty geometries
161166
return 0.
162167

168+
.. function:: ST_X(geometry: Geometry) -> x: double
169+
170+
Returns the ``x`` coordinate of the geometry if it is a Point. Returns
171+
``null`` if the geometry is empty. Raises an error if the geometry is
172+
not a Point and not empty.
173+
174+
.. function:: ST_Y(geometry: Geometry) -> x: double
175+
176+
Returns the ``y`` coordinate of the geometry if it is a Point. Returns
177+
``null`` if the geometry is empty. Raises an error if the geometry is
178+
not a Point and not empty.
163179

164180
Bing Tile Functions
165181
-------------------

velox/expression/fuzzer/ExpressionFuzzerTest.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,9 @@ int main(int argc, char** argv) {
147147
"st_intersection",
148148
"st_symdifference",
149149
"st_union",
150+
"st_point",
151+
"st_x",
152+
"st_y",
150153
};
151154
size_t initialSeed = FLAGS_seed == 0 ? std::time(nullptr) : FLAGS_seed;
152155

velox/functions/prestosql/GeometryFunctions.h

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,14 @@
1616

1717
#pragma once
1818

19+
#include <geos/geom/Coordinate.h>
1920
#include <geos/io/WKBReader.h>
2021
#include <geos/io/WKBWriter.h>
2122
#include <geos/io/WKTReader.h>
2223
#include <geos/io/WKTWriter.h>
24+
#include <geos/util/AssertionFailedException.h>
25+
#include <geos/util/UnsupportedOperationException.h>
26+
#include <cmath>
2327

2428
#include <velox/type/StringView.h>
2529
#include "velox/functions/Macros.h"
@@ -109,6 +113,32 @@ struct StAsBinaryFunction {
109113
}
110114
};
111115

116+
template <typename T>
117+
struct StPointFunction {
118+
VELOX_DEFINE_FUNCTION_TYPES(T);
119+
120+
FOLLY_ALWAYS_INLINE Status call(
121+
out_type<Geometry>& result,
122+
const arg_type<double>& x,
123+
const arg_type<double>& y) {
124+
if (!std::isfinite(x) || !std::isfinite(y)) {
125+
return Status::UserError(fmt::format(
126+
"ST_Point requires finite coordinates, got x={} y={}", x, y));
127+
}
128+
GEOS_TRY(
129+
{
130+
geos::geom::GeometryFactory::Ptr factory =
131+
geos::geom::GeometryFactory::create();
132+
geos::geom::Point* point =
133+
factory->createPoint(geos::geom::Coordinate(x, y));
134+
result = geospatial::serializeGeometry(*point);
135+
factory->destroyGeometry(point);
136+
},
137+
"Failed to create point geometry");
138+
return Status::OK();
139+
}
140+
};
141+
112142
// Predicates
113143

114144
template <typename T>
@@ -391,6 +421,8 @@ struct StUnionFunction {
391421
}
392422
};
393423

424+
// Accessors
425+
394426
template <typename T>
395427
struct StAreaFunction {
396428
VELOX_DEFINE_FUNCTION_TYPES(T);
@@ -409,4 +441,52 @@ struct StAreaFunction {
409441
}
410442
};
411443

444+
template <typename T>
445+
struct StXFunction {
446+
VELOX_DEFINE_FUNCTION_TYPES(T);
447+
448+
FOLLY_ALWAYS_INLINE bool call(
449+
out_type<double>& result,
450+
const arg_type<Geometry>& geometry) {
451+
std::unique_ptr<geos::geom::Geometry> geosGeometry =
452+
geospatial::deserializeGeometry(geometry);
453+
if (geosGeometry->getGeometryTypeId() !=
454+
geos::geom::GeometryTypeId::GEOS_POINT) {
455+
throw Status::UserError(fmt::format(
456+
"ST_X requires a Point geometry, found {}",
457+
geosGeometry->getGeometryType()));
458+
}
459+
if (geosGeometry->isEmpty()) {
460+
return false;
461+
}
462+
auto coordinate = geosGeometry->getCoordinate();
463+
result = coordinate->x;
464+
return true;
465+
}
466+
};
467+
468+
template <typename T>
469+
struct StYFunction {
470+
VELOX_DEFINE_FUNCTION_TYPES(T);
471+
472+
FOLLY_ALWAYS_INLINE bool call(
473+
out_type<double>& result,
474+
const arg_type<Geometry>& geometry) {
475+
std::unique_ptr<geos::geom::Geometry> geosGeometry =
476+
geospatial::deserializeGeometry(geometry);
477+
if (geosGeometry->getGeometryTypeId() !=
478+
geos::geom::GeometryTypeId::GEOS_POINT) {
479+
throw Status::UserError(fmt::format(
480+
"ST_Y requires a Point geometry, found {}",
481+
geosGeometry->getGeometryType()));
482+
}
483+
if (geosGeometry->isEmpty()) {
484+
return false;
485+
}
486+
auto coordinate = geosGeometry->getCoordinate();
487+
result = coordinate->y;
488+
return true;
489+
}
490+
};
491+
412492
} // namespace facebook::velox::functions

velox/functions/prestosql/registration/GeometryFunctionsRegistration.cpp

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,20 @@
2222
namespace facebook::velox::functions {
2323

2424
namespace {
25+
26+
void registerConstructors(const std::string& prefix) {
27+
registerFunction<StGeometryFromTextFunction, Geometry, Varchar>(
28+
{{prefix + "ST_GeometryFromText"}});
29+
registerFunction<StGeomFromBinaryFunction, Geometry, Varbinary>(
30+
{{prefix + "ST_GeomFromBinary"}});
31+
registerFunction<StAsTextFunction, Varchar, Geometry>(
32+
{{prefix + "ST_AsText"}});
33+
registerFunction<StAsBinaryFunction, Varbinary, Geometry>(
34+
{{prefix + "ST_AsBinary"}});
35+
registerFunction<StPointFunction, Geometry, double, double>(
36+
{{prefix + "ST_Point"}});
37+
}
38+
2539
void registerRelationPredicates(const std::string& prefix) {
2640
registerFunction<StRelateFunction, bool, Geometry, Geometry, Varchar>(
2741
{{prefix + "ST_Relate"}});
@@ -55,26 +69,20 @@ void registerOverlayOperations(const std::string& prefix) {
5569
{{prefix + "ST_Union"}});
5670
}
5771

72+
void registerAccessors(const std::string& prefix) {
73+
registerFunction<StAreaFunction, double, Geometry>({{prefix + "ST_Area"}});
74+
registerFunction<StXFunction, double, Geometry>({{prefix + "ST_X"}});
75+
registerFunction<StYFunction, double, Geometry>({{prefix + "ST_Y"}});
76+
}
77+
5878
} // namespace
5979

6080
void registerGeometryFunctions(const std::string& prefix) {
6181
registerGeometryType();
62-
registerFunction<StGeometryFromTextFunction, Geometry, Varchar>(
63-
{{prefix + "ST_GeometryFromText"}});
64-
65-
registerFunction<StGeomFromBinaryFunction, Geometry, Varbinary>(
66-
{{prefix + "ST_GeomFromBinary"}});
67-
68-
registerFunction<StAsTextFunction, Varchar, Geometry>(
69-
{{prefix + "ST_AsText"}});
70-
71-
registerFunction<StAsBinaryFunction, Varbinary, Geometry>(
72-
{{prefix + "ST_AsBinary"}});
73-
74-
registerFunction<StAreaFunction, double, Geometry>({{prefix + "ST_Area"}});
75-
82+
registerConstructors(prefix);
7683
registerRelationPredicates(prefix);
7784
registerOverlayOperations(prefix);
85+
registerAccessors(prefix);
7886
}
7987

8088
} // namespace facebook::velox::functions

velox/functions/prestosql/tests/GeometryFunctionsTest.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,44 @@ TEST_F(GeometryFunctionsTest, wktAndWkb) {
236236
"Invalid number of points in LinearRing found 2 - must be 0 or >= 4");
237237
}
238238

239+
// Constructors and accessors
240+
241+
TEST_F(GeometryFunctionsTest, testStPoint) {
242+
const auto assertPoint = [&](const std::optional<double>& x,
243+
const std::optional<double> y) {
244+
std::optional<double> actualX =
245+
evaluateOnce<double>("ST_X(ST_Point(c0, c1))", x, y);
246+
std::optional<double> actualY =
247+
evaluateOnce<double>("ST_Y(ST_Point(c0, c1))", x, y);
248+
if (x.has_value() && y.has_value()) {
249+
EXPECT_TRUE(actualX.has_value());
250+
EXPECT_TRUE(actualY.has_value());
251+
EXPECT_EQ(x.value(), actualX.value());
252+
EXPECT_EQ(y.value(), actualY.value());
253+
} else {
254+
EXPECT_FALSE(actualX.has_value());
255+
EXPECT_FALSE(actualY.has_value());
256+
}
257+
};
258+
259+
assertPoint(std::nullopt, 0.0);
260+
assertPoint(0.0, 0.0);
261+
assertPoint(1.0, -23.12344);
262+
VELOX_ASSERT_THROW(
263+
assertPoint(0.0, NAN),
264+
"ST_Point requires finite coordinates, got x=0 y=nan");
265+
VELOX_ASSERT_THROW(
266+
assertPoint(INFINITY, 0.0),
267+
"ST_Point requires finite coordinates, got x=inf y=0");
268+
269+
std::optional<double> nullX =
270+
evaluateOnce<double>("ST_X(ST_GeometryFromText('POINT EMPTY'))");
271+
EXPECT_FALSE(nullX.has_value());
272+
std::optional<double> nullY =
273+
evaluateOnce<double>("ST_Y(ST_GeometryFromText('POINT EMPTY'))");
274+
EXPECT_FALSE(nullY.has_value());
275+
}
276+
239277
// Relationship predicates
240278

241279
TEST_F(GeometryFunctionsTest, testStRelate) {

0 commit comments

Comments
 (0)