Skip to content

Commit

Permalink
Merge pull request #203 from Maxxen/dev
Browse files Browse the repository at this point in the history
Add `ST_QuadKey`
  • Loading branch information
Maxxen authored Dec 6, 2023
2 parents 1da1f6f + 5c5e5c0 commit 01259c8
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 0 deletions.
27 changes: 27 additions & 0 deletions docs/docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ Note that this will overwrite any existing `spatial` extension installed for the
| [ST_PointN](##st_pointn) | Returns the n'th vertex from the input geometry as a point geometry |
| [ST_PointOnSurface](##st_pointonsurface) | Returns a point that is guaranteed to be on the surface of the input geometry. Sometimes a useful alternative to ST_Centroid. |
| [ST_Polygon2DFromWKB](##st_polygon2dfromwkb) | Deserialize a POLYGON_2D from a WKB encoded blob |
| [ST_QuadKey](##st_quadkey) | Computes a quadkey from a given lon/lat point. |
| [ST_ReducePrecision](##st_reduceprecision) | Returns the geometry with all vertices reduced to the target precision |
| [ST_RemoveRepeatedPoints](##st_removerepeatedpoints) | Returns a new geometry with repeated points removed, optionally within a target distance of eachother. |
| [ST_Reverse](##st_reverse) | Returns a new version of the input geometry with the order of its vertices reversed |
Expand Down Expand Up @@ -1208,6 +1209,31 @@ TODO

TODO

## ST_QuadKey

_Computes a quadkey from a given lon/lat point._

- __VARCHAR__ ST_QuadKey(geom __GEOMETRY__, level __INTEGER__)
- __VARCHAR__ ST_QuadKey(longitude __DOUBLE__, latitude __DOUBLE__, level __INTEGER__)

### Description

Compute the [quadkey](https://learn.microsoft.com/en-us/bingmaps/articles/bing-maps-tile-system) for a given lon/lat point at a given level.
Note that the the parameter order is __longitude__, __latitude__.

`level` has to be between 1 and 23, inclusive.

The input coordinates will be clamped to the lon/lat bounds of the earth (longitude between -180 and 180, latitude between -85.05112878 and 85.05112878).

Throws for any geometry that is not a `POINT`

### Examples

```sql
SELECT ST_QuadKey(st_point(11.08, 49.45), 10);
-- 1333203202
```

## ST_ReducePrecision

_Returns the geometry with all vertices reduced to the target precision_
Expand Down Expand Up @@ -1785,6 +1811,7 @@ __property__
- [ST_NumPoints](##st_numpoints)
- [ST_Perimeter](##st_perimeter)
- [ST_Perimeter_Spheroid](##st_perimeter_spheroid)
- [ST_QuadKey](##st_quadkey)
- [ST_X](##st_x)
- [ST_XMax](##st_xmax)
- [ST_XMin](##st_xmin)
Expand Down
61 changes: 61 additions & 0 deletions docs/src/functions/scalar/st_quadkey.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
{
"id": "st_quadkey",
"title": "ST_QuadKey",
"type": "scalar_function",
"signatures": [
{
"returns": "VARCHAR",
"parameters": [
{
"name": "geom",
"type": "GEOMETRY"
},
{
"name": "level",
"type": "INTEGER"
}
]
},
{
"returns": "VARCHAR",
"parameters": [
{
"name": "longitude",
"type": "DOUBLE"
},
{
"name": "latitude",
"type": "DOUBLE"
},
{
"name": "level",
"type": "INTEGER"
}
]
}
],
"aliases": [],
"summary": "Computes a quadkey from a given lon/lat point.",
"see_also": [ ],
"tags": [ "property" ]
}
---

### Description

Compute the [quadkey](https://learn.microsoft.com/en-us/bingmaps/articles/bing-maps-tile-system) for a given lon/lat point at a given level.
Note that the the parameter order is __longitude__, __latitude__.

`level` has to be between 1 and 23, inclusive.

The input coordinates will be clamped to the lon/lat bounds of the earth (longitude between -180 and 180, latitude between -85.05112878 and 85.05112878).

Throws for any geometry that is not a `POINT`

### Examples

```sql
SELECT ST_QuadKey(st_point(11.08, 49.45), 10);
-- 1333203202
```
4 changes: 4 additions & 0 deletions spatial/include/spatial/core/functions/scalar.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ struct CoreScalarFunctions {
RegisterStPerimeter(db);
RegisterStPoint(db);
RegisterStPointN(db);
RegisterStQuadKey(db);
RegisterStRemoveRepeatedPoints(db);
RegisterStStartPoint(db);
RegisterStX(db);
Expand Down Expand Up @@ -150,6 +151,9 @@ struct CoreScalarFunctions {
// ST_RemoveRepeatedPoints
static void RegisterStRemoveRepeatedPoints(DatabaseInstance &db);

// ST_QuadKey
static void RegisterStQuadKey(DatabaseInstance &db);

// ST_StartPoint
static void RegisterStStartPoint(DatabaseInstance &db);

Expand Down
1 change: 1 addition & 0 deletions spatial/src/spatial/core/functions/scalar/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ set(EXTENSION_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/st_perimeter.cpp
${CMAKE_CURRENT_SOURCE_DIR}/st_point.cpp
${CMAKE_CURRENT_SOURCE_DIR}/st_pointn.cpp
${CMAKE_CURRENT_SOURCE_DIR}/st_quadkey.cpp
${CMAKE_CURRENT_SOURCE_DIR}/st_removerepeatedpoints.cpp
${CMAKE_CURRENT_SOURCE_DIR}/st_startpoint.cpp
${CMAKE_CURRENT_SOURCE_DIR}/st_xyzm.cpp
Expand Down
108 changes: 108 additions & 0 deletions spatial/src/spatial/core/functions/scalar/st_quadkey.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#include "duckdb/parser/parsed_data/create_scalar_function_info.hpp"
#include "duckdb/common/vector_operations/generic_executor.hpp"
#include "spatial/common.hpp"
#include "spatial/core/functions/scalar.hpp"
#include "spatial/core/functions/common.hpp"
#include "spatial/core/geometry/geometry.hpp"
#include "spatial/core/types.hpp"

#define _USE_MATH_DEFINES
#include <cmath>
#include "math.h"

namespace spatial {

namespace core {

static void GetQuadKey(double lon, double lat, int32_t level, char* buffer) {

lat = std::max(-85.05112878, std::min(85.05112878, lat));
lon = std::max(-180.0, std::min(180.0, lon));

double lat_rad = lat * M_PI / 180.0;
auto x = static_cast<int32_t>((lon + 180.0) / 360.0 * (1 << level));
auto y = static_cast<int32_t>((1.0 - std::log(std::tan(lat_rad) + 1.0 / std::cos(lat_rad)) / M_PI) / 2.0 * (1 << level));

for (int i = level; i > 0; --i) {
char digit = '0';
int32_t mask = 1 << (i - 1);
if ((x & mask) != 0) {
digit += 1;
}
if ((y & mask) != 0) {
digit += 2;
}

buffer[level - i] = digit;
}
}
//------------------------------------------------------------------------------
// Coordinates
//------------------------------------------------------------------------------
static void CoordinateQuadKeyFunction(DataChunk &args, ExpressionState &state, Vector &result) {
auto &lon_in = args.data[0];
auto &lat_in = args.data[1];
auto &level = args.data[2];
auto count = args.size();

TernaryExecutor::Execute<double, double, int32_t, string_t>(lon_in, lat_in, level, result, count, [&](double lon, double lat, int32_t level) {
if(level < 1 || level > 23) {
throw InvalidInputException("ST_QuadKey: Level must be between 1 and 23");
}
char buffer[64];
GetQuadKey(lon, lat, level, buffer);
return StringVector::AddString(result, buffer, level);
});
}

//------------------------------------------------------------------------------
// GEOMETRY
//------------------------------------------------------------------------------
static void GeometryQuadKeyFunction(DataChunk &args, ExpressionState &state, Vector &result) {
auto &ctx = GeometryFunctionLocalState::ResetAndGet(state);

auto &geom = args.data[0];
auto &level = args.data[1];
auto count = args.size();

BinaryExecutor::Execute<string_t, int32_t, string_t>(geom, level, result, count, [&](string_t input, int32_t level) {
auto header = GeometryHeader::Get(input);
if(header.type != GeometryType::POINT) {
throw InvalidInputException("ST_QuadKey: Only POINT geometries are supported");
}
auto point = ctx.factory.Deserialize(input);
if(point.IsEmpty()) {
throw InvalidInputException("ST_QuadKey: Empty geometries are not supported");
}
auto vertex = point.GetPoint().GetVertex();
auto x = vertex.x;
auto y = vertex.y;

if(level < 1 || level > 23) {
throw InvalidInputException("ST_QuadKey: Level must be between 1 and 23");
}

char buffer[64];
GetQuadKey(x, y, level, buffer);
return StringVector::AddString(result, buffer, level);
});
}

//------------------------------------------------------------------------------
// Register functions
//------------------------------------------------------------------------------
void CoreScalarFunctions::RegisterStQuadKey(DatabaseInstance &db) {

ScalarFunctionSet set("ST_QuadKey");

set.AddFunction(ScalarFunction({LogicalType::DOUBLE, LogicalType::DOUBLE, LogicalType::INTEGER}, LogicalType::VARCHAR,
CoordinateQuadKeyFunction));
set.AddFunction(ScalarFunction({GeoTypes::GEOMETRY(), LogicalType::INTEGER}, LogicalType::VARCHAR,
GeometryQuadKeyFunction, nullptr, nullptr, nullptr, GeometryFunctionLocalState::Init));

ExtensionUtil::RegisterFunction(db, set);
}

} // namespace core

} // namespace spatial

0 comments on commit 01259c8

Please sign in to comment.