Skip to content

Commit de9a55a

Browse files
committed
feat: implement ST_Expand
It returns a bounding polygon expanded by a specified amount. A common use case is doing a "near" search on a point. This is a postgis function, not found by me in OGC MM. See https://postgis.net/docs/ST_Expand.html
1 parent 35a3635 commit de9a55a

File tree

2 files changed

+124
-0
lines changed

2 files changed

+124
-0
lines changed

src/spatial/modules/main/spatial_functions_scalar.cpp

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3003,6 +3003,83 @@ struct ST_Dump {
30033003
}
30043004
};
30053005

3006+
3007+
//======================================================================================================================
3008+
// ST_Expand
3009+
//======================================================================================================================
3010+
3011+
struct ST_Expand {
3012+
3013+
//------------------------------------------------------------------------------------------------------------------
3014+
// GEOMETRY
3015+
//------------------------------------------------------------------------------------------------------------------
3016+
static void Execute(DataChunk &args, ExpressionState &state, Vector &result) {
3017+
auto &lstate = LocalState::ResetAndGet(state);
3018+
3019+
BinaryExecutor::Execute<string_t, double, string_t>(args.data[0], args.data[1], result, args.size(), [&](const string_t &blob, double distance) {
3020+
sgl::geometry geom;
3021+
lstate.Deserialize(blob, geom);
3022+
auto bbox = sgl::extent_xy::smallest();
3023+
3024+
if (sgl::ops::get_total_extent_xy(geom, bbox) == 0) {
3025+
const sgl::geometry empty(sgl::geometry_type::GEOMETRY_COLLECTION, false, false);
3026+
return lstate.Serialize(result, empty);
3027+
} else {
3028+
sgl::geometry expanded(sgl::geometry_type::POLYGON, false, false);
3029+
const auto min_x = bbox.min.x - distance;
3030+
const auto min_y = bbox.min.y - distance;
3031+
const auto max_x = bbox.max.x + distance;
3032+
const auto max_y = bbox.max.y + distance;
3033+
const double buffer[10] = {min_x, min_y, min_x, max_y, max_x, max_y, max_x, min_y, min_x, min_y};
3034+
3035+
sgl::geometry ring(sgl::geometry_type::LINESTRING, false, false);
3036+
ring.set_vertex_array(buffer, 5);
3037+
expanded.append_part(&ring);
3038+
return lstate.Serialize(result, expanded);
3039+
}
3040+
});
3041+
}
3042+
3043+
//------------------------------------------------------------------------------------------------------------------
3044+
// Documentation
3045+
//------------------------------------------------------------------------------------------------------------------
3046+
static constexpr auto DESCRIPTION = R"(
3047+
Expand the input geometry by the specified distance, returning a polygon.
3048+
3049+
`geom` is the input geometry.
3050+
3051+
`distance` is the target distance for the expansion, using the same units as the input geometry.
3052+
3053+
This is a planar operation and will not take into account the curvature of the earth.
3054+
)";
3055+
static constexpr auto EXAMPLE = R"(
3056+
SELECT ST_AsText(ST_Expand(ST_GeomFromText('POINT(20 30)'), 0.1));
3057+
)";
3058+
3059+
//------------------------------------------------------------------------------------------------------------------
3060+
// Register
3061+
//------------------------------------------------------------------------------------------------------------------
3062+
static void Register(ExtensionLoader &loader) {
3063+
FunctionBuilder::RegisterScalar(loader, "ST_Expand", [](ScalarFunctionBuilder &func) {
3064+
func.AddVariant([](ScalarFunctionVariantBuilder &variant) {
3065+
variant.AddParameter("geom", GeoTypes::GEOMETRY());
3066+
variant.AddParameter("distance", LogicalType::DOUBLE);
3067+
variant.SetReturnType(GeoTypes::GEOMETRY());
3068+
3069+
variant.SetInit(LocalState::Init);
3070+
variant.SetFunction(Execute);
3071+
});
3072+
3073+
func.SetDescription(DESCRIPTION);
3074+
func.SetExample(EXAMPLE);
3075+
3076+
func.SetTag("ext", "spatial");
3077+
func.SetTag("category", "property");
3078+
});
3079+
}
3080+
};
3081+
3082+
30063083
//======================================================================================================================
30073084
// ST_Extent
30083085
//======================================================================================================================
@@ -9279,6 +9356,7 @@ void RegisterSpatialScalarFunctions(ExtensionLoader &loader) {
92799356
ST_DistanceWithin::Register(loader);
92809357
ST_Dump::Register(loader);
92819358
ST_EndPoint::Register(loader);
9359+
ST_Expand::Register(loader);
92829360
ST_Extent::Register(loader);
92839361
ST_Extent_Approx::Register(loader);
92849362
// Op_IntersectApprox::Register(loader);

test/sql/geometry/st_expand.test

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
require spatial
2+
3+
query I
4+
SELECT ST_AsText(ST_Expand(ST_MakePoint(153.0, -38.0), 0.001));
5+
----
6+
POLYGON ((152.999 -38.001, 152.999 -37.999, 153.001 -37.999, 153.001 -38.001, 152.999 -38.001))
7+
8+
query I
9+
SELECT ST_AsText(ST_Expand(ST_MakePoint(153.0, -38.0), 0.0));
10+
----
11+
POLYGON ((153 -38, 153 -38, 153 -38, 153 -38, 153 -38))
12+
13+
query I
14+
SELECT ST_AsText(ST_Expand(ST_GeomFromText('POINT(20 30)'), 0.001));
15+
----
16+
POLYGON ((19.999 29.999, 19.999 30.001, 20.001 30.001, 20.001 29.999, 19.999 29.999))
17+
18+
query I
19+
SELECT ST_AsText(ST_Expand(ST_GeomFromText('GEOMETRYCOLLECTION(POINT(20 30))'), 0.001));
20+
----
21+
POLYGON ((19.999 29.999, 19.999 30.001, 20.001 30.001, 20.001 29.999, 19.999 29.999))
22+
23+
query I
24+
SELECT ST_AsText(ST_Expand(ST_GeomFromText('POLYGON((20 30, 21 30, 21 31, 20 31, 20 30))'), 0.1));
25+
----
26+
POLYGON ((19.9 29.9, 19.9 31.1, 21.1 31.1, 21.1 29.9, 19.9 29.9))
27+
28+
query I
29+
SELECT ST_AsText(ST_Expand(ST_GeomFromText('POLYGON((20 30, 21 30, 22 32, 21 31, 20 31, 20 30))'), 0.1));
30+
----
31+
POLYGON ((19.9 29.9, 19.9 32.1, 22.1 32.1, 22.1 29.9, 19.9 29.9))
32+
33+
query I
34+
SELECT ST_AsText(ST_Expand(ST_MakeEnvelope(20, 30, 21, 31), 0.1));
35+
----
36+
POLYGON ((19.9 29.9, 19.9 31.1, 21.1 31.1, 21.1 29.9, 19.9 29.9))
37+
38+
query I
39+
SELECT ST_AsText(ST_Expand(ST_MakeEnvelope(153.2, -38.8, 153.5, -38.7), 0.0));
40+
----
41+
POLYGON ((153.2 -38.8, 153.2 -38.7, 153.5 -38.7, 153.5 -38.8, 153.2 -38.8))
42+
43+
query I
44+
SELECT ST_AsText(ST_Expand(ST_GeomFromText('GEOMETRYCOLLECTION EMPTY'), 0.001));
45+
----
46+
GEOMETRYCOLLECTION EMPTY

0 commit comments

Comments
 (0)