Skip to content

Commit a6a607f

Browse files
authored
Merge pull request #677 from Maxxen/st-asmvt-v2
Second attempt at `ST_AsMVT` and `ST_AsMVTGeom`
2 parents b0e4c39 + ff2b1e9 commit a6a607f

File tree

13 files changed

+1680
-21
lines changed

13 files changed

+1680
-21
lines changed

docs/functions.md

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
| [`ST_Area_Spheroid`](#st_area_spheroid) | Returns the area of a geometry in meters, using an ellipsoidal model of the earth |
1313
| [`ST_AsGeoJSON`](#st_asgeojson) | Returns the geometry as a GeoJSON fragment |
1414
| [`ST_AsHEXWKB`](#st_ashexwkb) | Returns the geometry as a HEXWKB string |
15+
| [`ST_AsMVTGeom`](#st_asmvtgeom) | Transform and clip geometry to a tile boundary |
1516
| [`ST_AsSVG`](#st_assvg) | Convert the geometry into a SVG fragment or path |
1617
| [`ST_AsText`](#st_astext) | Returns the geometry as a WKT string |
1718
| [`ST_AsWKB`](#st_aswkb) | Returns the geometry as a WKB (Well-Known-Binary) blob |
@@ -88,6 +89,7 @@
8889
| [`ST_MakeBox2D`](#st_makebox2d) | Create a BOX2D from two POINT geometries |
8990
| [`ST_MakeEnvelope`](#st_makeenvelope) | Create a rectangular polygon from min/max coordinates |
9091
| [`ST_MakeLine`](#st_makeline) | Create a LINESTRING from a list of POINT geometries |
92+
| [`ST_MakePoint`](#st_makepoint) | Creates a GEOMETRY point from an pair of floating point numbers. |
9193
| [`ST_MakePolygon`](#st_makepolygon) | Create a POLYGON from a LINESTRING shell |
9294
| [`ST_MakeValid`](#st_makevalid) | Returns a valid representation of the geometry |
9395
| [`ST_MaximumInscribedCircle`](#st_maximuminscribedcircle) | Returns the maximum inscribed circle of the input geometry, optionally with a tolerance. |
@@ -144,6 +146,7 @@
144146

145147
| Function | Summary |
146148
| --- | --- |
149+
| [`ST_AsMVT`](#st_asmvt) | Make a Mapbox Vector Tile from a set of geometries and properties |
147150
| [`ST_CoverageInvalidEdges_Agg`](#st_coverageinvalidedges_agg) | Returns the invalid edges of a coverage geometry |
148151
| [`ST_CoverageSimplify_Agg`](#st_coveragesimplify_agg) | Simplifies a set of geometries while maintaining coverage |
149152
| [`ST_CoverageUnion_Agg`](#st_coverageunion_agg) | Unions a set of geometries while maintaining coverage |
@@ -402,6 +405,26 @@ SELECT ST_AsHexWKB('POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))'::geometry);
402405

403406
----
404407

408+
### ST_AsMVTGeom
409+
410+
411+
#### Signatures
412+
413+
```sql
414+
GEOMETRY ST_AsMVTGeom (geom GEOMETRY, bounds BOX_2D, extent BIGINT, buffer BIGINT, clip_geom BOOLEAN)
415+
GEOMETRY ST_AsMVTGeom (geom GEOMETRY, bounds BOX_2D, extent BIGINT, buffer BIGINT)
416+
GEOMETRY ST_AsMVTGeom (geom GEOMETRY, bounds BOX_2D, extent BIGINT)
417+
GEOMETRY ST_AsMVTGeom (geom GEOMETRY, bounds BOX_2D)
418+
```
419+
420+
#### Description
421+
422+
Transform and clip geometry to a tile boundary
423+
424+
See "ST_AsMVT" for more details
425+
426+
----
427+
405428
### ST_AsSVG
406429

407430

@@ -1937,6 +1960,35 @@ LINESTRING(0 0, 1 1)
19371960

19381961
----
19391962

1963+
### ST_MakePoint
1964+
1965+
1966+
#### Signatures
1967+
1968+
```sql
1969+
POINT_2D ST_MakePoint (x DOUBLE, y DOUBLE)
1970+
POINT_3D ST_MakePoint (x DOUBLE, y DOUBLE, z DOUBLE)
1971+
POINT_4D ST_MakePoint (x DOUBLE, y DOUBLE, z DOUBLE, m DOUBLE)
1972+
```
1973+
1974+
#### Description
1975+
1976+
Creates a GEOMETRY point from an pair of floating point numbers.
1977+
1978+
For geodetic coordinate systems, x is typically the longitude value and y is the latitude value.
1979+
1980+
Note that ST_Point is equivalent. ST_MakePoint is provided for PostGIS compatibility.
1981+
1982+
#### Example
1983+
1984+
```sql
1985+
SELECT ST_AsText(ST_MakePoint(143.3, -24.2));
1986+
----
1987+
POINT (143.3 -24.2)
1988+
```
1989+
1990+
----
1991+
19401992
### ST_MakePolygon
19411993

19421994

@@ -3015,6 +3067,66 @@ SELECT ST_ZMin(ST_Point(1, 2, 3))
30153067

30163068
## Aggregate Functions
30173069

3070+
### ST_AsMVT
3071+
3072+
3073+
#### Signatures
3074+
3075+
```sql
3076+
BLOB ST_AsMVT (col0 ANY)
3077+
BLOB ST_AsMVT (col0 ANY, col1 VARCHAR)
3078+
BLOB ST_AsMVT (col0 ANY, col1 VARCHAR, col2 INTEGER)
3079+
BLOB ST_AsMVT (col0 ANY, col1 VARCHAR, col2 INTEGER, col3 VARCHAR)
3080+
BLOB ST_AsMVT (col0 ANY, col1 VARCHAR, col2 INTEGER, col3 VARCHAR, col4 VARCHAR)
3081+
```
3082+
3083+
#### Description
3084+
3085+
Make a Mapbox Vector Tile from a set of geometries and properties
3086+
The function takes as input a row type (STRUCT) containing a geometry column and any number of property columns.
3087+
It returns a single binary BLOB containing the Mapbox Vector Tile.
3088+
3089+
The function has the following signature:
3090+
3091+
`ST_AsMVT(row STRUCT, layer_name VARCHAR DEFAULT 'layer', extent INTEGER DEFAULT 4096, geom_column_name VARCHAR DEFAULT NULL, feature_id_column_name VARCHAR DEFAULT NULL) -> BLOB`
3092+
3093+
- The first argument is a struct containing the geometry and properties.
3094+
- The second argument is the name of the layer in the vector tile. This argument is optional and defaults to 'layer'.
3095+
- The third argument is the extent of the tile. This argument is optional and defaults to 4096.
3096+
- The fourth argument is the name of the geometry column in the input row. This argument is optional. If not provided, the first geometry column in the input row will be used. If multiple geometry columns are present, an error will be raised.
3097+
- The fifth argument is the name of the feature id column in the input row. This argument is optional. If provided, the values in this column will be used as feature ids in the vector tile. The column must be of type INTEGER or BIGINT. If set to negative or NULL, a feature id will not be assigned to the corresponding feature.
3098+
3099+
The input struct must contain exactly one geometry column of type GEOMETRY. It can contain any number of property columns of types VARCHAR, FLOAT, DOUBLE, INTEGER, BIGINT, or BOOLEAN.
3100+
3101+
Example:
3102+
```sql
3103+
SELECT ST_AsMVT({'geom': geom, 'id': id, 'name': name}, 'cities', 4096, 'geom', 'id') AS tile
3104+
FROM cities;
3105+
```
3106+
3107+
This example creates a vector tile named 'cities' with an extent of 4096 from the 'cities' table, using 'geom' as the geometry column and 'id' as the feature id column.
3108+
3109+
However, you probably want to use the ST_AsMVTGeom function to first transform and clip your geometries to the tile extent.
3110+
The following example assumes the geometry is in WebMercator ("EPSG:3857") coordinates.
3111+
Replace `{z}`, `{x}`, and `{y}` with the appropriate tile coordinates, `{your table}` with your table name, and `{tile_path}` with the path to write the tile to.
3112+
3113+
```sql
3114+
COPY (
3115+
SELECT ST_AsMVT({{
3116+
"geometry": ST_AsMVTGeom(
3117+
geometry,
3118+
ST_Extent(ST_TileEnvelope({z}, {x}, {y})),
3119+
4096,
3120+
256,
3121+
false
3122+
)
3123+
}})
3124+
FROM {your table} WHERE ST_Intersects(geometry, ST_TileEnvelope({z}, {x}, {y}))
3125+
) to {tile_path} (FORMAT 'BLOB');
3126+
```
3127+
3128+
----
3129+
30183130
### ST_CoverageInvalidEdges_Agg
30193131

30203132

duckdb

Submodule duckdb updated 1618 files

src/sgl/sgl.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2976,7 +2976,7 @@ point_in_polygon_result prepared_geometry::contains(const vertex_xy &vert) const
29762976

29772977
const auto end = math::min(node_end, levl_end);
29782978

2979-
if(stack[depth] != end) {
2979+
if (stack[depth] != end) {
29802980
// Go sideways!
29812981
stack[depth]++;
29822982
break;
@@ -3367,7 +3367,7 @@ static bool try_get_prepared_distance_lines(const prepared_geometry &lhs, const
33673367

33683368
if (found_any) {
33693369
distance = std::sqrt(min_dist); // Convert squared distance to actual distance
3370-
return true; // We found a distance
3370+
return true; // We found a distance
33713371
}
33723372
return false; // No distance found
33733373
}
@@ -3381,7 +3381,6 @@ bool prepared_geometry::try_get_distance(const prepared_geometry &other, double
33813381
// WKT Parsing
33823382
//======================================================================================================================
33833383

3384-
33853384
namespace sgl {
33863385

33873386
namespace {

src/spatial/modules/geos/geos_geometry.hpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ class GeosGeometry {
5151
GeosGeometry get_voronoi_diagram() const;
5252
GeosGeometry get_built_area() const;
5353
GeosGeometry get_noded() const;
54+
GeosGeometry get_clipped(double xmin, double ymin, double xmax, double ymax) const;
55+
56+
// matrix format: [a, b, c, d, e, f]
57+
// x' = a*x + b*y + e
58+
// y' = c*x + d*y + f
59+
GeosGeometry get_transformed(const double matrix[6]) const;
60+
GeosGeometry get_gridded(double grid_size) const;
5461

5562
bool contains(const GeosGeometry &other) const;
5663
bool covers(const GeosGeometry &other) const;
@@ -67,6 +74,7 @@ class GeosGeometry {
6774
double distance_to(const GeosGeometry &other) const;
6875

6976
void normalize_in_place() const;
77+
void orient_polygons(bool ext_cw);
7078

7179
GeosGeometry get_difference(const GeosGeometry &other) const;
7280
GeosGeometry get_intersection(const GeosGeometry &other) const;
@@ -94,6 +102,10 @@ class GeosGeometry {
94102

95103
PreparedGeosGeometry get_prepared() const;
96104

105+
void get_extent(double &xmin, double &ymin, double &xmax, double &ymax) const {
106+
GEOSGeom_getExtent_r(handle, geom, &xmin, &ymin, &xmax, &ymax);
107+
}
108+
97109
private:
98110
GEOSContextHandle_t handle;
99111
GEOSGeometry *geom;
@@ -332,6 +344,34 @@ inline GeosGeometry GeosGeometry::get_noded() const {
332344
return GeosGeometry(handle, GEOSNode_r(handle, geom));
333345
}
334346

347+
inline GeosGeometry GeosGeometry::get_clipped(double xmin, double ymin, double xmax, double ymax) const {
348+
return GeosGeometry(handle, GEOSClipByRect_r(handle, geom, xmin, ymin, xmax, ymax));
349+
}
350+
351+
inline GeosGeometry GeosGeometry::get_transformed(const double matrix[6]) const {
352+
// x' = a*x + b*y + e
353+
// y' = c*x + d*y + f
354+
return GeosGeometry(handle, GEOSGeom_transformXY_r(
355+
handle, geom,
356+
[](double *x_ptr, double *y_ptr, void *data) -> int {
357+
const auto m = static_cast<const double *>(data);
358+
const auto &x = *x_ptr;
359+
const auto &y = *y_ptr;
360+
361+
const auto new_x = m[0] * x + m[1] * y + m[4];
362+
const auto new_y = m[2] * x + m[3] * y + m[5];
363+
364+
*x_ptr = new_x;
365+
*y_ptr = new_y;
366+
return 1;
367+
},
368+
const_cast<double *>(matrix)));
369+
}
370+
371+
inline GeosGeometry GeosGeometry::get_gridded(double grid_size) const {
372+
return GeosGeometry(handle, GEOSGeom_setPrecision_r(handle, geom, grid_size, GEOS_PREC_NO_TOPO));
373+
}
374+
335375
inline GeosGeometry GeosGeometry::get_maximum_inscribed_circle() const {
336376
double xmin = 0;
337377
double ymin = 0;
@@ -411,6 +451,10 @@ inline void GeosGeometry::normalize_in_place() const {
411451
GEOSNormalize_r(handle, geom);
412452
}
413453

454+
inline void GeosGeometry::orient_polygons(bool ext_cw) {
455+
GEOSOrientPolygons_r(handle, geom, ext_cw ? 1 : 0);
456+
}
457+
414458
inline GeosGeometry GeosGeometry::get_difference(const GeosGeometry &other) const {
415459
return GeosGeometry(handle, GEOSDifference_r(handle, geom, other.geom));
416460
}

0 commit comments

Comments
 (0)