|
1 | | -#include "duckdb/parser/parsed_data/create_scalar_function_info.hpp" |
2 | | -#include "duckdb/common/vector_operations/generic_executor.hpp" |
3 | 1 | #include "duckdb/common/constants.hpp" |
| 2 | +#include "duckdb/common/vector_operations/generic_executor.hpp" |
| 3 | +#include "duckdb/parser/parsed_data/create_scalar_function_info.hpp" |
4 | 4 | #include "spatial/common.hpp" |
5 | | -#include "spatial/core/functions/scalar.hpp" |
6 | 5 | #include "spatial/core/functions/common.hpp" |
| 6 | +#include "spatial/core/functions/scalar.hpp" |
7 | 7 | #include "spatial/core/geometry/geometry.hpp" |
| 8 | +#include "spatial/core/util/math.hpp" |
8 | 9 | #include "spatial/core/types.hpp" |
9 | 10 |
|
10 | 11 | #include <cmath> |
@@ -76,6 +77,21 @@ inline uint32_t HilbertEncode(uint32_t n, uint32_t x, uint32_t y) { |
76 | 77 | return ((Interleave(i1) << 1) | Interleave(i0)) >> (32 - 2 * n); |
77 | 78 | } |
78 | 79 |
|
| 80 | +static uint32_t FloatToUint32(float f) |
| 81 | +{ |
| 82 | + if (std::isnan(f)) { |
| 83 | + return 0xFFFFFFFF; |
| 84 | + } |
| 85 | + uint32_t res; |
| 86 | + memcpy(&res, &f, sizeof(res)); |
| 87 | + if((res & 0x80000000) != 0) { |
| 88 | + res ^= 0xFFFFFFFF; |
| 89 | + } else { |
| 90 | + res |= 0x80000000; |
| 91 | + } |
| 92 | + return res; |
| 93 | +} |
| 94 | + |
79 | 95 | //------------------------------------------------------------------------------ |
80 | 96 | // Coordinates |
81 | 97 | //------------------------------------------------------------------------------ |
@@ -120,35 +136,61 @@ static void HilbertEncodeBoundsFunction(DataChunk &args, ExpressionState &state, |
120 | 136 | using GEOM_TYPE = PrimitiveType<geometry_t>; |
121 | 137 | using UINT32_TYPE = PrimitiveType<uint32_t>; |
122 | 138 |
|
123 | | - ArenaAllocator dummy_allocator(Allocator::DefaultAllocator()); |
124 | | - |
125 | 139 | GenericExecutor::ExecuteBinary<GEOM_TYPE, BOX_TYPE, UINT32_TYPE>( |
126 | 140 | input_vec, bounds_vec, result, count, [&](const GEOM_TYPE &geom_type, const BOX_TYPE &bounds) { |
127 | 141 | const auto geom = geom_type.val; |
128 | 142 |
|
129 | | - if (geom.GetType() != GeometryType::POINT) { |
130 | | - throw InvalidInputException("ST_Hilbert only supports points"); |
131 | | - } |
132 | | - const auto point = Geometry::Deserialize(dummy_allocator, geom); |
133 | | - if (Point::IsEmpty(point)) { |
134 | | - throw InvalidInputException("ST_Hilbert does not support empty points"); |
135 | | - } |
| 143 | + Box2D<double> geom_bounds; |
| 144 | + if(!geom.TryGetCachedBounds(geom_bounds)) { |
| 145 | + throw InvalidInputException("ST_Hilbert(geom, bounds) requires that all geometries have a bounding box"); |
| 146 | + } |
136 | 147 |
|
137 | | - const auto v = Point::GetVertex(point); |
138 | | - const auto x = v.x; |
139 | | - const auto y = v.y; |
| 148 | + const auto dx = geom_bounds.min.x + (geom_bounds.max.x - geom_bounds.min.x) / 2; |
| 149 | + const auto dy = geom_bounds.min.y + (geom_bounds.max.y - geom_bounds.min.y) / 2; |
140 | 150 |
|
141 | 151 | const auto hilbert_width = max_hilbert / (bounds.c_val - bounds.a_val); |
142 | 152 | const auto hilbert_height = max_hilbert / (bounds.d_val - bounds.b_val); |
143 | 153 | // TODO: Check for overflow |
144 | | - const auto hilbert_x = static_cast<uint32_t>((x - bounds.a_val) * hilbert_width); |
145 | | - const auto hilbert_y = static_cast<uint32_t>((y - bounds.b_val) * hilbert_height); |
| 154 | + const auto hilbert_x = static_cast<uint32_t>((dx - bounds.a_val) * hilbert_width); |
| 155 | + const auto hilbert_y = static_cast<uint32_t>((dy - bounds.b_val) * hilbert_height); |
146 | 156 |
|
147 | 157 | const auto h = HilbertEncode(16, hilbert_x, hilbert_y); |
148 | 158 | return UINT32_TYPE {h}; |
149 | 159 | }); |
150 | 160 | } |
151 | 161 |
|
| 162 | +//------------------------------------------------------------------------------ |
| 163 | +// GEOMETRY |
| 164 | +//------------------------------------------------------------------------------ |
| 165 | +static void HilbertEncodeGeometryFunction(DataChunk &args, ExpressionState &state, Vector &result) { |
| 166 | + const auto count = args.size(); |
| 167 | + auto &input_vec = args.data[0]; |
| 168 | + |
| 169 | + UnaryExecutor::ExecuteWithNulls<geometry_t, uint32_t>( |
| 170 | + input_vec, result, count, [&](const geometry_t &geom, ValidityMask &mask, idx_t out_idx) -> uint32_t { |
| 171 | + Box2D<double> bounds; |
| 172 | + if(!geom.TryGetCachedBounds(bounds)) { |
| 173 | + mask.SetInvalid(out_idx); |
| 174 | + return 0; |
| 175 | + } |
| 176 | + |
| 177 | + Box2D<float> bounds_f; |
| 178 | + bounds_f.min.x = MathUtil::DoubleToFloatDown(bounds.min.x); |
| 179 | + bounds_f.min.y = MathUtil::DoubleToFloatDown(bounds.min.y); |
| 180 | + bounds_f.max.x = MathUtil::DoubleToFloatUp(bounds.max.x); |
| 181 | + bounds_f.max.y = MathUtil::DoubleToFloatUp(bounds.max.y); |
| 182 | + |
| 183 | + const auto dx = bounds_f.min.x + (bounds_f.max.x - bounds_f.min.x) / 2; |
| 184 | + const auto dy = bounds_f.min.y + (bounds_f.max.y - bounds_f.min.y) / 2; |
| 185 | + |
| 186 | + const auto hx = FloatToUint32(dx); |
| 187 | + const auto hy = FloatToUint32(dy); |
| 188 | + |
| 189 | + return HilbertEncode(16, hx, hy); |
| 190 | + }); |
| 191 | +} |
| 192 | + |
| 193 | + |
152 | 194 | //------------------------------------------------------------------------------ |
153 | 195 | // BOX_2D/BOX_2DF |
154 | 196 | //------------------------------------------------------------------------------ |
@@ -205,6 +247,7 @@ void CoreScalarFunctions::RegisterStHilbert(DatabaseInstance &db) { |
205 | 247 | HilbertEncodeBoxFunction<double>)); |
206 | 248 | set.AddFunction(ScalarFunction({GeoTypes::BOX_2DF(), GeoTypes::BOX_2DF()}, LogicalType::UINTEGER, |
207 | 249 | HilbertEncodeBoxFunction<float>)); |
| 250 | + set.AddFunction(ScalarFunction({GeoTypes::GEOMETRY()}, LogicalType::UINTEGER, HilbertEncodeGeometryFunction)); |
208 | 251 |
|
209 | 252 | ExtensionUtil::RegisterFunction(db, set); |
210 | 253 | DocUtil::AddDocumentation(db, "ST_Hilbert", DOC_DESCRIPTION, DOC_EXAMPLE, DOC_TAGS); |
|
0 commit comments