diff --git a/CMakeLists.txt b/CMakeLists.txt index 1cc6f8ff..ed832b19 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -102,6 +102,7 @@ set(EXTENSION_SOURCES src/geo/tgeometry.cpp src/geo/tgeometry_in_out.cpp src/geo/tgeometry_ops.cpp + src/geo/tgeo_setset_join.cpp src/geo/tgeography.cpp src/geo/tgeography_in_out.cpp src/geo/tgeography_ops.cpp diff --git a/src/geo/tgeo_setset_join.cpp b/src/geo/tgeo_setset_join.cpp new file mode 100644 index 00000000..ca26093e --- /dev/null +++ b/src/geo/tgeo_setset_join.cpp @@ -0,0 +1,326 @@ +/***************************************************************************** + * + * This MobilityDuck code is provided under The PostgreSQL License. + * Copyright (c) 2025, Université libre de Bruxelles and MobilityDB + * contributors + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without a written + * agreement is hereby granted, provided that the above copyright notice and + * this paragraph and the following two paragraphs appear in all copies. + * + * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING + * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS + * DOCUMENTATION, EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN + * "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + *****************************************************************************/ + +/** + * @brief Set-set spatial-join table functions over arrays of temporal + * geometries. + * + * Three table functions, each registered for both tgeompoint[] and + * tgeometry[] input arrays: + * - eDwithinPairs(arr1, arr2, dist) -> SETOF (i INTEGER, j INTEGER) + * - tDwithinPairs(arr1, arr2, dist) -> SETOF (i INTEGER, j INTEGER, + * periods tstzspanset) + * - aDisjointPairs(arr1, arr2) -> SETOF (i INTEGER, j INTEGER) + * + * They wrap the MEOS set-set spatial-join kernel symbols + * edwithin_tgeoarr_tgeoarr / tdwithin_tgeoarr_tgeoarr / + * adisjoint_tgeoarr_tgeoarr, which return a malloc'd flattened pair-index + * array [i0, j0, i1, j1, ...] of length 2*count. + */ + +#include "meos_wrapper_simple.hpp" + +#include "geo/tgeo_setset_join.hpp" +#include "geo/tgeometry.hpp" +#include "geo/tgeompoint.hpp" +#include "temporal/spanset.hpp" +#include "mobilityduck/meos_exec_serial.hpp" + +#include "duckdb/main/extension/extension_loader.hpp" + +#include + +namespace duckdb { + +namespace { + +// --------------------------------------------------------------------------- +// Shared marshaling helpers +// --------------------------------------------------------------------------- + +// Marshal a DuckDB LIST Value of temporal blobs into a freshly malloc'd +// array of Temporal* (each element copied into its own malloc'd buffer so +// MEOS owns nothing of DuckDB's memory). Returns nullptr with *count == 0 +// for an empty (or null) list. +const Temporal **MarshalTemporalArray(const Value &list_val, int *count) { + if (list_val.IsNull()) { + *count = 0; + return nullptr; + } + auto &children = ListValue::GetChildren(list_val); + int n = static_cast(children.size()); + if (n == 0) { + *count = 0; + return nullptr; + } + const Temporal **arr = (const Temporal **) malloc(sizeof(Temporal *) * n); + for (int i = 0; i < n; i++) { + string s = StringValue::Get(children[i]); + uint8_t *p = (uint8_t *) malloc(s.size()); + memcpy(p, s.data(), s.size()); + arr[i] = (const Temporal *) p; + } + *count = n; + return arr; +} + +void FreeTemporalArray(const Temporal **arr, int count) { + if (!arr) + return; + for (int i = 0; i < count; i++) + free((void *) arr[i]); + free((void *) arr); +} + +// --------------------------------------------------------------------------- +// Global state shared by all three functions +// --------------------------------------------------------------------------- + +struct SetSetJoinGlobalState : public GlobalTableFunctionState { + idx_t idx = 0; + vector is; + vector js; + vector periods; // populated only for tDwithinPairs +}; + +// --------------------------------------------------------------------------- +// eDwithinPairs / aDisjointPairs (two output columns: i, j) +// --------------------------------------------------------------------------- + +struct PairsBindData : public TableFunctionData { + Value arr1; + Value arr2; + double dist = 0.0; + bool has_dist = false; +}; + +template +unique_ptr PairsBind(ClientContext &, TableFunctionBindInput &input, + vector &return_types, vector &names) { + auto bd = make_uniq(); + bd->arr1 = input.inputs[0]; + bd->arr2 = input.inputs[1]; + if (HasDist) { + bd->dist = input.inputs[2].GetValue(); + bd->has_dist = true; + } + return_types = {LogicalType::INTEGER, LogicalType::INTEGER}; + names = {"i", "j"}; + return std::move(bd); +} + +// Init for eDwithinPairs +unique_ptr EDwithinInit(ClientContext &, TableFunctionInitInput &input) { + auto &bd = input.bind_data->Cast(); + auto state = make_uniq(); + + int count1 = 0, count2 = 0; + const Temporal **arr1 = MarshalTemporalArray(bd.arr1, &count1); + const Temporal **arr2 = MarshalTemporalArray(bd.arr2, &count2); + + int count = 0; + int *res = edwithin_tgeoarr_tgeoarr(arr1, count1, arr2, count2, bd.dist, &count); + if (res && count > 0) { + state->is.reserve(count); + state->js.reserve(count); + for (int k = 0; k < count; k++) { + // DuckDB lists are 1-based: expose the kernel's 0-based pair indexes as 1-based. + state->is.push_back(res[2 * k] + 1); + state->js.push_back(res[2 * k + 1] + 1); + } + } + if (res) + free(res); + FreeTemporalArray(arr1, count1); + FreeTemporalArray(arr2, count2); + return std::move(state); +} + +// Init for aDisjointPairs +unique_ptr ADisjointInit(ClientContext &, TableFunctionInitInput &input) { + auto &bd = input.bind_data->Cast(); + auto state = make_uniq(); + + int count1 = 0, count2 = 0; + const Temporal **arr1 = MarshalTemporalArray(bd.arr1, &count1); + const Temporal **arr2 = MarshalTemporalArray(bd.arr2, &count2); + + int count = 0; + int *res = adisjoint_tgeoarr_tgeoarr(arr1, count1, arr2, count2, &count); + if (res && count > 0) { + state->is.reserve(count); + state->js.reserve(count); + for (int k = 0; k < count; k++) { + // DuckDB lists are 1-based: expose the kernel's 0-based pair indexes as 1-based. + state->is.push_back(res[2 * k] + 1); + state->js.push_back(res[2 * k + 1] + 1); + } + } + if (res) + free(res); + FreeTemporalArray(arr1, count1); + FreeTemporalArray(arr2, count2); + return std::move(state); +} + +void PairsExec(ClientContext &, TableFunctionInput &input, DataChunk &output) { + auto &state = input.global_state->Cast(); + idx_t remaining = state.is.size() - state.idx; + idx_t emit = MinValue(STANDARD_VECTOR_SIZE, remaining); + for (idx_t i = 0; i < emit; i++) { + output.data[0].SetValue(i, Value::INTEGER(state.is[state.idx])); + output.data[1].SetValue(i, Value::INTEGER(state.js[state.idx])); + state.idx++; + } + output.SetCardinality(emit); +} + +// --------------------------------------------------------------------------- +// tDwithinPairs (three output columns: i, j, periods) +// --------------------------------------------------------------------------- + +unique_ptr TDwithinBind(ClientContext &, TableFunctionBindInput &input, + vector &return_types, vector &names) { + auto bd = make_uniq(); + bd->arr1 = input.inputs[0]; + bd->arr2 = input.inputs[1]; + bd->dist = input.inputs[2].GetValue(); + bd->has_dist = true; + return_types = {LogicalType::INTEGER, LogicalType::INTEGER, SpansetTypes::tstzspanset()}; + names = {"i", "j", "periods"}; + return std::move(bd); +} + +unique_ptr TDwithinInit(ClientContext &, TableFunctionInitInput &input) { + auto &bd = input.bind_data->Cast(); + auto state = make_uniq(); + + int count1 = 0, count2 = 0; + const Temporal **arr1 = MarshalTemporalArray(bd.arr1, &count1); + const Temporal **arr2 = MarshalTemporalArray(bd.arr2, &count2); + + int count = 0; + SpanSet **periods = nullptr; + int *res = tdwithin_tgeoarr_tgeoarr(arr1, count1, arr2, count2, bd.dist, &count, &periods); + if (res && count > 0) { + state->is.reserve(count); + state->js.reserve(count); + state->periods.reserve(count); + for (int k = 0; k < count; k++) { + // DuckDB lists are 1-based: expose the kernel's 0-based pair indexes as 1-based. + state->is.push_back(res[2 * k] + 1); + state->js.push_back(res[2 * k + 1] + 1); + SpanSet *ss = periods[k]; + Value v = Value::BLOB(reinterpret_cast(ss), + static_cast(spanset_mem_size(ss))); + v.Reinterpret(SpansetTypes::tstzspanset()); + state->periods.push_back(std::move(v)); + free(ss); + } + } + if (res) + free(res); + if (periods) + free(periods); + FreeTemporalArray(arr1, count1); + FreeTemporalArray(arr2, count2); + return std::move(state); +} + +void TDwithinExec(ClientContext &, TableFunctionInput &input, DataChunk &output) { + auto &state = input.global_state->Cast(); + idx_t remaining = state.is.size() - state.idx; + idx_t emit = MinValue(STANDARD_VECTOR_SIZE, remaining); + for (idx_t i = 0; i < emit; i++) { + output.data[0].SetValue(i, Value::INTEGER(state.is[state.idx])); + output.data[1].SetValue(i, Value::INTEGER(state.js[state.idx])); + output.data[2].SetValue(i, state.periods[state.idx]); + state.idx++; + } + output.SetCardinality(emit); +} + +// --------------------------------------------------------------------------- +// minDistance(arr1, arr2) -> DOUBLE (set-set spatial minimum distance) +// --------------------------------------------------------------------------- +// Scalar reduction member of the set-set family: the exact minimum spatial +// distance between any temporal geometry of arr1 and any of arr2, wrapping the +// MEOS kernel mindistance_tgeoarr_tgeoarr, which uses each element's STBox as a +// sound lower-bound prefilter so element pairs whose boxes are already farther +// apart than the running minimum are skipped. +void MindistanceArrExec(DataChunk &args, ExpressionState &, Vector &result) { + const idx_t n = args.size(); + auto out = FlatVector::GetData(result); + auto &outv = FlatVector::Validity(result); + for (idx_t row = 0; row < n; row++) { + Value v1 = args.data[0].GetValue(row); + Value v2 = args.data[1].GetValue(row); + if (v1.IsNull() || v2.IsNull()) { + outv.SetInvalid(row); + continue; + } + int count1 = 0, count2 = 0; + const Temporal **arr1 = MarshalTemporalArray(v1, &count1); + const Temporal **arr2 = MarshalTemporalArray(v2, &count2); + if (count1 == 0 || count2 == 0) { + FreeTemporalArray(arr1, count1); + FreeTemporalArray(arr2, count2); + outv.SetInvalid(row); + continue; + } + double d = mindistance_tgeoarr_tgeoarr(arr1, count1, arr2, count2); + FreeTemporalArray(arr1, count1); + FreeTemporalArray(arr2, count2); + if (d == DBL_MAX) { + outv.SetInvalid(row); + continue; + } + out[row] = d; + } +} + +} // anonymous namespace + +void RegisterSetSetSpatialJoin(ExtensionLoader &loader) { + const auto D = LogicalType::DOUBLE; + + for (const auto &elem : {TgeompointType::TGEOMPOINT(), TGeometryTypes::TGEOMETRY()}) { + const auto L = LogicalType::LIST(elem); + + loader.RegisterFunction(TableFunction( + "eDwithinPairs", {L, L, D}, PairsExec, PairsBind, EDwithinInit)); + + loader.RegisterFunction(TableFunction( + "tDwithinPairs", {L, L, D}, TDwithinExec, TDwithinBind, TDwithinInit)); + + loader.RegisterFunction(TableFunction( + "aDisjointPairs", {L, L}, PairsExec, PairsBind, ADisjointInit)); + + duckdb::RegisterSerializedScalarFunction( + loader, ScalarFunction("minDistance", {L, L}, D, MindistanceArrExec)); + } +} + +} // namespace duckdb diff --git a/src/include/geo/tgeo_setset_join.hpp b/src/include/geo/tgeo_setset_join.hpp new file mode 100644 index 00000000..2e80fc3b --- /dev/null +++ b/src/include/geo/tgeo_setset_join.hpp @@ -0,0 +1,40 @@ +/***************************************************************************** + * + * This MobilityDuck code is provided under The PostgreSQL License. + * Copyright (c) 2025, Université libre de Bruxelles and MobilityDB + * contributors + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without a written + * agreement is hereby granted, provided that the above copyright notice and + * this paragraph and the following two paragraphs appear in all copies. + * + * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING + * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS + * DOCUMENTATION, EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN + * "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + *****************************************************************************/ + +/** + * @brief Set-set spatial-join table functions (eDwithinPairs, tDwithinPairs, + * aDisjointPairs) over arrays of temporal geometries, wrapping the MEOS + * set-set spatial-join kernel. + */ + +#pragma once + +#include "duckdb/main/extension/extension_loader.hpp" + +namespace duckdb { + +void RegisterSetSetSpatialJoin(ExtensionLoader &loader); + +} // namespace duckdb diff --git a/src/include/temporal/temporal_functions.hpp b/src/include/temporal/temporal_functions.hpp index bbb895c3..6a791493 100644 --- a/src/include/temporal/temporal_functions.hpp +++ b/src/include/temporal/temporal_functions.hpp @@ -232,6 +232,9 @@ struct TemporalFunctions { static void Tfloat_exp(DataChunk &args, ExpressionState &state, Vector &result); static void Tfloat_ln(DataChunk &args, ExpressionState &state, Vector &result); static void Tfloat_log10(DataChunk &args, ExpressionState &state, Vector &result); + static void Tfloat_sin(DataChunk &args, ExpressionState &state, Vector &result); + static void Tfloat_cos(DataChunk &args, ExpressionState &state, Vector &result); + static void Tfloat_tan(DataChunk &args, ExpressionState &state, Vector &result); // Temporal_derivative declared in the math-functions block below. static void Tfloat_degrees(DataChunk &args, ExpressionState &state, Vector &result); static void Tfloat_radians(DataChunk &args, ExpressionState &state, Vector &result); diff --git a/src/mobilityduck_extension.cpp b/src/mobilityduck_extension.cpp index c268abc1..20b52b06 100644 --- a/src/mobilityduck_extension.cpp +++ b/src/mobilityduck_extension.cpp @@ -12,6 +12,7 @@ #include "geo/tgeogpoint.hpp" #include "duckdb.hpp" #include "geo/tgeometry.hpp" +#include "geo/tgeo_setset_join.hpp" #include "geo/tgeometry_ops.hpp" #include "geo/tgeography.hpp" #include "geo/tgeography_ops.hpp" @@ -321,6 +322,7 @@ static void LoadInternal(ExtensionLoader &loader) { TGeometryTypes::RegisterCastFunctions(loader); TGeometryTypes::RegisterScalarInOutFunctions(loader); TGeometryOps::RegisterScalarFunctions(loader); + RegisterSetSetSpatialJoin(loader); TGeographyTypes::RegisterTypes(loader); TGeographyTypes::RegisterScalarFunctions(loader); diff --git a/src/temporal/temporal.cpp b/src/temporal/temporal.cpp index 9abf2973..b5c59d9e 100644 --- a/src/temporal/temporal.cpp +++ b/src/temporal/temporal.cpp @@ -1367,6 +1367,9 @@ void TemporalTypes::RegisterScalarFunctions(ExtensionLoader &loader) { duckdb::RegisterSerializedScalarFunction(loader, ScalarFunction("exp", {TemporalTypes::TFLOAT()}, TemporalTypes::TFLOAT(), TemporalFunctions::Tfloat_exp)); duckdb::RegisterSerializedScalarFunction(loader, ScalarFunction("ln", {TemporalTypes::TFLOAT()}, TemporalTypes::TFLOAT(), TemporalFunctions::Tfloat_ln)); duckdb::RegisterSerializedScalarFunction(loader, ScalarFunction("log10", {TemporalTypes::TFLOAT()}, TemporalTypes::TFLOAT(), TemporalFunctions::Tfloat_log10)); + duckdb::RegisterSerializedScalarFunction(loader, ScalarFunction("sin", {TemporalTypes::TFLOAT()}, TemporalTypes::TFLOAT(), TemporalFunctions::Tfloat_sin)); + duckdb::RegisterSerializedScalarFunction(loader, ScalarFunction("cos", {TemporalTypes::TFLOAT()}, TemporalTypes::TFLOAT(), TemporalFunctions::Tfloat_cos)); + duckdb::RegisterSerializedScalarFunction(loader, ScalarFunction("tan", {TemporalTypes::TFLOAT()}, TemporalTypes::TFLOAT(), TemporalFunctions::Tfloat_tan)); // deltaValue / trend on tnumber duckdb::RegisterSerializedScalarFunction(loader, ScalarFunction("deltaValue", {TemporalTypes::TINT()}, TemporalTypes::TINT(), TemporalFunctions::Tnumber_delta_value)); diff --git a/src/temporal/temporal_functions.cpp b/src/temporal/temporal_functions.cpp index fc8a2b68..5b110e00 100644 --- a/src/temporal/temporal_functions.cpp +++ b/src/temporal/temporal_functions.cpp @@ -4816,6 +4816,18 @@ void TemporalFunctions::Tfloat_log10(DataChunk &args, ExpressionState &state, Ve TemporalUnary(args, result, [](Temporal *t) { return tfloat_log10(t); }); } +void TemporalFunctions::Tfloat_sin(DataChunk &args, ExpressionState &state, Vector &result) { + TemporalUnary(args, result, [](Temporal *t) { return tfloat_sin(t); }); +} + +void TemporalFunctions::Tfloat_cos(DataChunk &args, ExpressionState &state, Vector &result) { + TemporalUnary(args, result, [](Temporal *t) { return tfloat_cos(t); }); +} + +void TemporalFunctions::Tfloat_tan(DataChunk &args, ExpressionState &state, Vector &result) { + TemporalUnary(args, result, [](Temporal *t) { return tfloat_tan(t); }); +} + namespace { template diff --git a/test/sql/parity/026c_tfloat_trig.test b/test/sql/parity/026c_tfloat_trig.test new file mode 100644 index 00000000..1ede88d3 --- /dev/null +++ b/test/sql/parity/026c_tfloat_trig.test @@ -0,0 +1,56 @@ +# name: test/sql/parity/026c_tfloat_trig.test +# description: Trigonometric lifts for tfloat — sin, cos, tan. +# group: [sql] + +require mobilityduck + +# Instant: sin(0) = 0 +query I +SELECT sin(tfloat '[0@2000-01-01]'); +---- +[0@2000-01-01 00:00:00+01] + +# Instant: cos(0) = 1 +query I +SELECT cos(tfloat '[0@2000-01-01]'); +---- +[1@2000-01-01 00:00:00+01] + +# Instant: tan(0) = 0 +query I +SELECT tan(tfloat '[0@2000-01-01]'); +---- +[0@2000-01-01 00:00:00+01] + +# Sequence endpoints: sin lifted over [0, pi/2] starts at 0 and ends at 1 +query I +SELECT round(startValue(sin(tfloat '[0@2000-01-01, 1.5707963267948966@2000-01-02]')), 6); +---- +0.0 + +query I +SELECT round(endValue(sin(tfloat '[0@2000-01-01, 1.5707963267948966@2000-01-02]')), 6); +---- +1.0 + +# Sequence endpoints: cos lifted over [0, pi/2] starts at 1 and ends at 0 +query I +SELECT round(startValue(cos(tfloat '[0@2000-01-01, 1.5707963267948966@2000-01-02]')), 6); +---- +1.0 + +query I +SELECT round(endValue(cos(tfloat '[0@2000-01-01, 1.5707963267948966@2000-01-02]')), 6); +---- +0.0 + +# Sequence endpoints: tan lifted over [0, pi/4] starts at 0 and ends at 1 +query I +SELECT round(startValue(tan(tfloat '[0@2000-01-01, 0.7853981633974483@2000-01-02]')), 6); +---- +0.0 + +query I +SELECT round(endValue(tan(tfloat '[0@2000-01-01, 0.7853981633974483@2000-01-02]')), 6); +---- +1.0 diff --git a/test/sql/parity/068_setset_spatial_join.test b/test/sql/parity/068_setset_spatial_join.test new file mode 100644 index 00000000..4c8996de --- /dev/null +++ b/test/sql/parity/068_setset_spatial_join.test @@ -0,0 +1,109 @@ +# name: test/sql/parity/068_setset_spatial_join.test +# description: Set-set spatial-join family eDwithinPairs / tDwithinPairs / aDisjointPairs / minDistance(arr, arr). +# group: [sql] +# +# Each function takes two arrays of temporal geometries and returns the +# qualifying index pairs (i into the first array, j into the second). Indexes +# are 1-based to match DuckDB list indexing. Pairs that never overlap in time +# are excluded. + +require mobilityduck + +# ============================================================================= +# eDwithinPairs(tgeompoint[], tgeompoint[], dist) +# ============================================================================= + +# arr1[1] = origin, arr1[2] = far away; arr2[1] within 2 of origin, arr2[2] = origin +query II +SELECT * FROM eDwithinPairs( + [tgeompoint 'Point(0 0)@2000-01-01', tgeompoint 'Point(10 10)@2000-01-01'], + [tgeompoint 'Point(0 1)@2000-01-01', tgeompoint 'Point(0 0)@2000-01-01'], + 2.0) ORDER BY i, j; +---- +1 1 +1 2 + +# ============================================================================= +# tDwithinPairs(tgeompoint[], tgeompoint[], dist) — with periods +# ============================================================================= + +# A stationary point at the origin and a point moving away; they stay within +# distance 2 only over the first part of the shared interval. +query III +SELECT * FROM tDwithinPairs( + [tgeompoint '[Point(0 0)@2000-01-01, Point(0 0)@2000-01-02]', tgeompoint '[Point(10 10)@2000-01-01, Point(10 10)@2000-01-02]'], + [tgeompoint '[Point(0 1)@2000-01-01, Point(0 5)@2000-01-02]'], + 2.0) ORDER BY i, j; +---- +1 1 {[2000-01-01 00:00:00+01, 2000-01-01 06:00:00+01]} + +# ============================================================================= +# aDisjointPairs(tgeompoint[], tgeompoint[]) +# ============================================================================= + +# arr1[1] crosses arr2[1]; arr1[2] is far away and never intersects. +query II +SELECT * FROM aDisjointPairs( + [tgeompoint '[Point(0 0)@2000-01-01, Point(0 0)@2000-01-02]', tgeompoint '[Point(10 10)@2000-01-01, Point(10 10)@2000-01-02]'], + [tgeompoint '[Point(0 0)@2000-01-01, Point(5 5)@2000-01-02]']) ORDER BY i, j; +---- +2 1 + +# ============================================================================= +# tgeometry[] overloads +# ============================================================================= + +query II +SELECT * FROM eDwithinPairs( + [tgeometry 'Point(0 0)@2000-01-01', tgeometry 'Point(10 10)@2000-01-01'], + [tgeometry 'Point(0 1)@2000-01-01'], + 2.0) ORDER BY i, j; +---- +1 1 + +query III +SELECT * FROM tDwithinPairs( + [tgeometry '[Point(0 0)@2000-01-01, Point(0 0)@2000-01-02]'], + [tgeometry '[Point(0 1)@2000-01-01, Point(0 5)@2000-01-02]'], + 2.0) ORDER BY i, j; +---- +1 1 {[2000-01-01 00:00:00+01, 2000-01-02 00:00:00+01)} + +query II +SELECT * FROM aDisjointPairs( + [tgeometry '[Point(0 0)@2000-01-01, Point(0 0)@2000-01-02]', tgeometry '[Point(10 10)@2000-01-01, Point(10 10)@2000-01-02]'], + [tgeometry '[Point(0 0)@2000-01-01, Point(5 5)@2000-01-02]']) ORDER BY i, j; +---- +2 1 + +# ============================================================================= +# minDistance(tgeompoint[], tgeompoint[]) — exact set-set spatial minimum +# ============================================================================= + +# Minimum over the cross product of the two arrays: the nearest pair is +# arr1[2] = Point(4 0) and arr2[1] = Point(0 0), at distance 4. +query I +SELECT minDistance( + [tgeompoint 'Point(0 10)@2000-01-01', tgeompoint 'Point(4 0)@2000-01-01'], + [tgeompoint 'Point(0 0)@2000-01-01']); +---- +4.0 + +# Two moving trips whose closest synchronous approach is 3. +query I +SELECT minDistance( + [tgeompoint '[Point(0 0)@2000-01-01, Point(2 2)@2000-01-02]', tgeompoint '[Point(10 10)@2000-01-01, Point(11 11)@2000-01-02]'], + [tgeompoint '[Point(5 0)@2000-01-01, Point(5 2)@2000-01-02]']); +---- +3.0 + +# ============================================================================= +# minDistance(tgeometry[], tgeometry[]) +# ============================================================================= + +query I +SELECT minDistance( + [tgeometry 'Point(0 10)@2000-01-01', tgeometry 'Point(4 0)@2000-01-01'], + [tgeometry 'Point(0 0)@2000-01-01']); +---- +4.0 diff --git a/vcpkg_ports/meos/portfile.cmake b/vcpkg_ports/meos/portfile.cmake index fd0da052..738c9f17 100644 --- a/vcpkg_ports/meos/portfile.cmake +++ b/vcpkg_ports/meos/portfile.cmake @@ -1,8 +1,8 @@ vcpkg_from_github( OUT_SOURCE_PATH SOURCE_PATH REPO estebanzimanyi/MobilityDB - REF 278863520b000b735cc361beab88387174909ed7 - SHA512 2e617cac4bfed919eee8bd73e35f9b3da8ffd7a51f68104a9497c0d3339dbb1af4a2ec6feab3e8fffb880481badf86c352ad1efb39cab533de19d2c1192a8e5b + REF 59ba0ad59cb93db6fa46929394e475b7851c00be + SHA512 e6a4a1578e5760326a596248865ccb487850120ca423f5675d1f014e9146de4f407c699afa63a102d8ba4ef1f895946d206ee4ab2f870dc4508ec2ca3d771869 ) vcpkg_replace_string(