Skip to content

Commit 5f0eba5

Browse files
authored
Merge pull request #700 from Maxxen/v1.4-andium-dev
Combined MVT fixes + other open PRs
2 parents 61ede09 + af00774 commit 5f0eba5

File tree

6 files changed

+223
-20
lines changed

6 files changed

+223
-20
lines changed

src/spatial/modules/geos/geos_geometry.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ inline GeosGeometry GeosGeometry::get_transformed(const double matrix[6]) const
369369
}
370370

371371
inline GeosGeometry GeosGeometry::get_gridded(double grid_size) const {
372-
return GeosGeometry(handle, GEOSGeom_setPrecision_r(handle, geom, grid_size, GEOS_PREC_NO_TOPO));
372+
return GeosGeometry(handle, GEOSGeom_setPrecision_r(handle, geom, grid_size, GEOS_PREC_VALID_OUTPUT));
373373
}
374374

375375
inline GeosGeometry GeosGeometry::get_maximum_inscribed_circle() const {

src/spatial/modules/geos/geos_module.cpp

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -327,9 +327,6 @@ struct ST_AsMVTGeom {
327327
const auto &blob = geom_data[geom_idx];
328328
auto geom = lstate.Deserialize(blob);
329329

330-
// Orient polygons in place
331-
geom.orient_polygons(true);
332-
333330
// Compute bounds
334331
const auto extent = bind_data.extent;
335332

@@ -363,10 +360,14 @@ struct ST_AsMVTGeom {
363360
const auto transformed = geom.get_transformed(affine_matrix);
364361

365362
// Snap to grid (round coordinates to integers)
366-
const auto snapped = transformed.get_gridded(1.0);
363+
auto snapped = transformed.get_gridded(1.0);
367364

368365
// Should we clip? if not, return the snapped geometry
369366
if (!bind_data.clip) {
367+
368+
// But first orient in place
369+
snapped.orient_polygons(true);
370+
370371
res_data[out_idx] = lstate.Serialize(result, snapped);
371372
continue;
372373
}
@@ -385,7 +386,10 @@ struct ST_AsMVTGeom {
385386
}
386387

387388
// Snap again to clean up any potential issues from clipping
388-
const auto cleaned_clipped = clipped.get_gridded(1.0);
389+
auto cleaned_clipped = clipped.get_gridded(1.0);
390+
391+
// Also orient the polygons in place
392+
cleaned_clipped.orient_polygons(true);
389393

390394
res_data[out_idx] = lstate.Serialize(result, cleaned_clipped);
391395
}
@@ -1286,7 +1290,7 @@ struct ST_DistanceWithin {
12861290
});
12871291

12881292
func.SetDescription(R"(
1289-
Returns if two geometries are within a target distance of each-other
1293+
Returns true if two geometries are within a target distance of each-other
12901294
)");
12911295

12921296
func.SetTag("ext", "spatial");

src/spatial/modules/main/spatial_functions_scalar.cpp

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4804,7 +4804,7 @@ struct ST_GeomFromWKB {
48044804
y_data[i] = vertex.y;
48054805
}
48064806

4807-
if (args.AllConstant()) {
4807+
if (args.AllConstant() || args.size() == 1) {
48084808
result.SetVectorType(VectorType::CONSTANT_VECTOR);
48094809
}
48104810
}
@@ -4872,7 +4872,7 @@ struct ST_GeomFromWKB {
48724872

48734873
ListVector::SetListSize(result, total_size);
48744874

4875-
if (args.AllConstant()) {
4875+
if (args.AllConstant() || args.size() == 1) {
48764876
result.SetVectorType(VectorType::CONSTANT_VECTOR);
48774877
}
48784878
}
@@ -4967,7 +4967,7 @@ struct ST_GeomFromWKB {
49674967
ListVector::SetListSize(result, total_ring_count);
49684968
ListVector::SetListSize(ring_vec, total_point_count);
49694969

4970-
if (count == 1) {
4970+
if (args.AllConstant() || args.size() == 1) {
49714971
result.SetVectorType(VectorType::CONSTANT_VECTOR);
49724972
}
49734973
}
@@ -4986,8 +4986,16 @@ struct ST_GeomFromWKB {
49864986
static void Register(ExtensionLoader &loader) {
49874987
FunctionBuilder::RegisterScalar(loader, "ST_Point2DFromWKB", [](ScalarFunctionBuilder &builder) {
49884988
builder.AddVariant([](ScalarFunctionVariantBuilder &variant) {
4989-
variant.AddParameter("point", GeoTypes::POINT_2D());
4990-
variant.SetReturnType(GeoTypes::GEOMETRY());
4989+
variant.AddParameter("wkb", GeoTypes::WKB_BLOB());
4990+
variant.SetReturnType(GeoTypes::POINT_2D());
4991+
4992+
variant.SetInit(LocalState::Init);
4993+
variant.SetFunction(ExecutePoint);
4994+
});
4995+
4996+
builder.AddVariant([](ScalarFunctionVariantBuilder &variant) {
4997+
variant.AddParameter("blob", LogicalType::BLOB);
4998+
variant.SetReturnType(GeoTypes::POINT_2D());
49914999

49925000
variant.SetInit(LocalState::Init);
49935001
variant.SetFunction(ExecutePoint);
@@ -5001,8 +5009,16 @@ struct ST_GeomFromWKB {
50015009

50025010
FunctionBuilder::RegisterScalar(loader, "ST_LineString2DFromWKB", [](ScalarFunctionBuilder &builder) {
50035011
builder.AddVariant([](ScalarFunctionVariantBuilder &variant) {
5004-
variant.AddParameter("linestring", GeoTypes::LINESTRING_2D());
5005-
variant.SetReturnType(GeoTypes::GEOMETRY());
5012+
variant.AddParameter("wkb", GeoTypes::WKB_BLOB());
5013+
variant.SetReturnType(GeoTypes::LINESTRING_2D());
5014+
5015+
variant.SetInit(LocalState::Init);
5016+
variant.SetFunction(ExecuteLineString);
5017+
});
5018+
5019+
builder.AddVariant([](ScalarFunctionVariantBuilder &variant) {
5020+
variant.AddParameter("blob", LogicalType::BLOB);
5021+
variant.SetReturnType(GeoTypes::LINESTRING_2D());
50065022

50075023
variant.SetInit(LocalState::Init);
50085024
variant.SetFunction(ExecuteLineString);
@@ -5016,8 +5032,15 @@ struct ST_GeomFromWKB {
50165032

50175033
FunctionBuilder::RegisterScalar(loader, "ST_Polygon2DFromWKB", [](ScalarFunctionBuilder &builder) {
50185034
builder.AddVariant([](ScalarFunctionVariantBuilder &variant) {
5019-
variant.AddParameter("polygon", GeoTypes::POLYGON_2D());
5020-
variant.SetReturnType(GeoTypes::GEOMETRY());
5035+
variant.AddParameter("wkb", GeoTypes::WKB_BLOB());
5036+
variant.SetReturnType(GeoTypes::POLYGON_2D());
5037+
5038+
variant.SetInit(LocalState::Init);
5039+
variant.SetFunction(ExecutePolygon);
5040+
});
5041+
builder.AddVariant([](ScalarFunctionVariantBuilder &variant) {
5042+
variant.AddParameter("blob", LogicalType::BLOB);
5043+
variant.SetReturnType(GeoTypes::POLYGON_2D());
50215044

50225045
variant.SetInit(LocalState::Init);
50235046
variant.SetFunction(ExecutePolygon);
@@ -5252,7 +5275,7 @@ struct ST_LineInterpolatePoint {
52525275
auto &lstate = LocalState::ResetAndGet(state);
52535276

52545277
BinaryExecutor::Execute<string_t, double, string_t>(
5255-
args.data[0], args.data[1], result, args.size(), [&](const string_t &blob, const double faction) {
5278+
args.data[0], args.data[1], result, args.size(), [&](const string_t &blob, const double fraction) {
52565279
sgl::geometry geom;
52575280
lstate.Deserialize(blob, geom);
52585281

@@ -5261,7 +5284,7 @@ struct ST_LineInterpolatePoint {
52615284
}
52625285

52635286
sgl::vertex_xyzm out_vertex = {0, 0, 0, 0};
5264-
if (sgl::linestring::interpolate(geom, faction, out_vertex)) {
5287+
if (sgl::linestring::interpolate(geom, fraction, out_vertex)) {
52655288
sgl::geometry point(sgl::geometry_type::POINT, geom.has_z(), geom.has_m());
52665289
point.set_vertex_array(&out_vertex, 1);
52675290
return lstate.Serialize(result, point);
@@ -6092,7 +6115,7 @@ struct ST_Hilbert {
60926115
static constexpr auto DESCRIPTION = R"(
60936116
Encodes the X and Y values as the hilbert curve index for a curve covering the given bounding box.
60946117
If a geometry is provided, the center of the approximate bounding box is used as the point to encode.
6095-
If no bounding box is provided, the hilbert curve index is mapped to the full range of a single-presicion float.
6118+
If no bounding box is provided, the hilbert curve index is mapped to the full range of a single-precision float.
60966119
For the BOX_2D and BOX_2DF variants, the center of the box is used as the point to encode.
60976120
)";
60986121

src/spatial/modules/mvt/mvt_module.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -613,7 +613,7 @@ class MVTFeatureBuilder {
613613
geometry.push_back((1 & 0x7) | (1 << 3)); // MoveTo, 1 part
614614
geometry.push_back(protozero::encode_zigzag32(x - cursor_x));
615615
geometry.push_back(protozero::encode_zigzag32(y - cursor_y));
616-
geometry.push_back((2 & 0x7) | ((vertex_count - 2) << 3)); // LineTo, part count
616+
geometry.push_back((2 & 0x7) | ((vertex_count - 1) << 3)); // LineTo, part count
617617
} else {
618618
geometry.push_back(protozero::encode_zigzag32(x - cursor_x));
619619
geometry.push_back(protozero::encode_zigzag32(y - cursor_y));
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# name: test/sql/geometry/st_2d_fromwkb.test
2+
# group: [geometry]
3+
4+
require spatial
5+
6+
query I
7+
select ST_Point2DFromWKB(ST_AsWKB(ST_Point(1, 2)));
8+
----
9+
POINT (1 2)
10+
11+
query I
12+
SELECT ST_Linestring2DFromWKB(ST_AsWKB(ST_GeomFromText('LINESTRING(0 0, 1 1, 2 2)')));
13+
----
14+
LINESTRING (0 0, 1 1, 2 2)
15+
16+
query I
17+
SELECT ST_Polygon2DFromWKB(ST_AsWKB(ST_GeomFromText('POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))')));
18+
----
19+
POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
# name: test/sql/mvt/st_asmvt_linestring.test
2+
# group: [mvt]
3+
4+
require spatial
5+
6+
# Test LINESTRING encoding
7+
statement ok
8+
COPY (
9+
SELECT st_asmvt(
10+
{"geom": geom},
11+
'lines'
12+
) as mvt
13+
FROM (
14+
SELECT
15+
st_geomfromtext('LINESTRING(0 0, 100 100, 200 0)') as geom
16+
)
17+
) TO '__TEST_DIR__/test_linestring.mvt' (FORMAT BLOB);
18+
19+
query I
20+
select count(*) from st_read('__TEST_DIR__/test_linestring.mvt');
21+
----
22+
1
23+
24+
# Test MULTI_LINESTRING encoding
25+
statement ok
26+
COPY (
27+
SELECT st_asmvt(
28+
{"geom": geom},
29+
'multilines'
30+
) as mvt
31+
FROM (
32+
SELECT
33+
st_geomfromtext('MULTILINESTRING((0 0, 100 100, 200 0), (300 0, 400 100, 500 0))') as geom
34+
)
35+
) TO '__TEST_DIR__/test_multilinestring.mvt' (FORMAT BLOB);
36+
37+
query I
38+
select count(*) from st_read('__TEST_DIR__/test_multilinestring.mvt');
39+
----
40+
1
41+
42+
# Test LINESTRING with ST_AsMVTGeom (clipping can produce MULTI_LINESTRING)
43+
statement ok
44+
COPY (
45+
SELECT st_asmvt(
46+
{"geom": ST_AsMVTGeom(
47+
geom,
48+
ST_Extent(ST_MakeEnvelope(0, 0, 1000, 1000)),
49+
4096,
50+
256,
51+
true
52+
)},
53+
'clipped_lines'
54+
) as mvt
55+
FROM (
56+
SELECT
57+
st_geomfromtext('LINESTRING(100 100, 500 500, 900 100)') as geom
58+
)
59+
) TO '__TEST_DIR__/test_clipped_linestring.mvt' (FORMAT BLOB);
60+
61+
query I
62+
select count(*) from st_read('__TEST_DIR__/test_clipped_linestring.mvt');
63+
----
64+
1
65+
66+
# Test LINESTRING crossing tile boundary (produces MULTI_LINESTRING after clipping)
67+
statement ok
68+
COPY (
69+
SELECT st_asmvt(
70+
{"geom": ST_AsMVTGeom(
71+
geom,
72+
ST_Extent(ST_MakeEnvelope(0, 0, 1000, 1000)),
73+
4096,
74+
256,
75+
true
76+
)},
77+
'crossing_lines'
78+
) as mvt
79+
FROM (
80+
SELECT
81+
st_geomfromtext('LINESTRING(-500 500, 500 500, 1500 500)') as geom
82+
)
83+
) TO '__TEST_DIR__/test_crossing_linestring.mvt' (FORMAT BLOB);
84+
85+
query I
86+
select count(*) from st_read('__TEST_DIR__/test_crossing_linestring.mvt');
87+
----
88+
1
89+
90+
# Test multiple LINESTRINGs with various lengths
91+
statement ok
92+
COPY (
93+
SELECT st_asmvt(
94+
{"geom": geom, "id": id},
95+
'various_lines',
96+
4096,
97+
'geom',
98+
'id'
99+
) as mvt
100+
FROM (
101+
SELECT
102+
row_number() over () as id,
103+
st_geomfromtext('LINESTRING(' || (x*100) || ' ' || (y*100) || ', ' || (x*100+50) || ' ' || (y*100+50) || ', ' || (x*100+100) || ' ' || (y*100) || ')') as geom
104+
FROM range(0, 10) as r(x),
105+
range(0, 10) as rr(y)
106+
)
107+
) TO '__TEST_DIR__/test_various_linestrings.mvt' (FORMAT BLOB);
108+
109+
query I
110+
select count(*) from st_read('__TEST_DIR__/test_various_linestrings.mvt');
111+
----
112+
100
113+
114+
# Test global scale dataset scenario (like Natural Earth roads)
115+
# This simulates the case where geometries at low zoom levels span large areas
116+
statement ok
117+
COPY (
118+
SELECT st_asmvt(
119+
{"geom": ST_AsMVTGeom(
120+
geom,
121+
ST_Extent(ST_TileEnvelope(2, 1, 1)),
122+
4096,
123+
256,
124+
false
125+
)},
126+
'global_lines'
127+
) as mvt
128+
FROM (
129+
SELECT
130+
st_geomfromtext('LINESTRING(-10000000 5000000, 0 0, 10000000 -5000000)') as geom
131+
)
132+
) TO '__TEST_DIR__/test_global_linestring.mvt' (FORMAT BLOB);
133+
134+
query I
135+
select count(*) from st_read('__TEST_DIR__/test_global_linestring.mvt');
136+
----
137+
1
138+
139+
# Test that LINESTRING with attributes can be read back
140+
statement ok
141+
COPY (
142+
SELECT st_asmvt(
143+
{"geom": geom, "name": name},
144+
'roads'
145+
) as mvt
146+
FROM (
147+
VALUES
148+
(st_geomfromtext('MULTILINESTRING((100 100, 500 500), (600 600, 900 900))'), 'road1'),
149+
(st_geomfromtext('LINESTRING(200 200, 800 800)'), 'road2')
150+
) t(geom, name)
151+
) TO '__TEST_DIR__/test_roads.mvt' (FORMAT BLOB);
152+
153+
query II
154+
select count(*), count(name) from st_read('__TEST_DIR__/test_roads.mvt');
155+
----
156+
2 2
157+

0 commit comments

Comments
 (0)