From 45c16065a416743779c97267b16735aff30b8f7c Mon Sep 17 00:00:00 2001 From: Maxime Schoemans Date: Tue, 11 Jun 2024 15:38:26 +0200 Subject: [PATCH] Fix edge cases in tpointseq_linear_at_stbox_xyz (#501) --- meos/src/point/tpoint_restrict.c | 46 +++++++++++++++---- .../expected/056_tpoint_spatialfuncs.test.out | 36 +++++++++++++++ .../queries/056_tpoint_spatialfuncs.test.sql | 9 ++++ 3 files changed, 83 insertions(+), 8 deletions(-) diff --git a/meos/src/point/tpoint_restrict.c b/meos/src/point/tpoint_restrict.c index 490b885880..c47e40c25f 100644 --- a/meos/src/point/tpoint_restrict.c +++ b/meos/src/point/tpoint_restrict.c @@ -1501,7 +1501,12 @@ tpointseq_linear_at_stbox_xyz(const TSequence *seq, const STBox *box, /* If ninsts > 0 the instant was added in the previous iteration */ if (ninsts == 0) { - if (t1 != inst1->t) + if (t1 == inst1->t) + { + instants[ninsts++] = (TInstant *) inst1; + lower_inc &= p3_inc; + } + else if (t1 != inst2->t) { inter1 = tsegment_value_at_timestamptz(inst1, inst2, LINEAR, t1); free1 = true; @@ -1509,14 +1514,33 @@ tpointseq_linear_at_stbox_xyz(const TSequence *seq, const STBox *box, tofree[nfree++] = instants[ninsts++]; lower_inc = p3_inc; } - else + else if (t1 == inst2->t) { - instants[ninsts++] = (TInstant *) inst1; - lower_inc &= p3_inc; + /* We have t1 == t2 == inst2->t and since found = true, we know + * that we are on an inclusive border (p3_inc == p4_inc == true). + * Thus, lower_inc is only false if we are at the last segment + * and seq->period.upper_inc is false. */ + instants[ninsts++] = (TInstant *) inst2; + lower_inc = (i == seq->count - 1) ? seq->period.upper_inc : true; } } if (t1 == t2) - upper_inc = lower_inc; + { + /* If we are here: p3_inc == p4_inc == true, otherwise found would be false. + * And we also know that p1 != p2, so segment has nonzero length. + * Thus, we are in 1 of 3 cases: */ + if (t1 == inst1->t) + /* We are exiting the box at the start of a segment + * Start of segments are assumed inclusive except for the first one */ + upper_inc = (i == 1) ? seq->period.lower_inc : true; + else if (t1 == inst2->t) + /* We are entering the box at the end of a segment + * End of segments are assumed exclusive except for the last one */ + upper_inc = (i == seq->count - 1) ? seq->period.upper_inc : false; + else + /* We clip the box at one of its inclusive corners */ + upper_inc = true; + } else { if (t2 != inst2->t) @@ -1551,15 +1575,21 @@ tpointseq_linear_at_stbox_xyz(const TSequence *seq, const STBox *box, } if (makeseq && ninsts > 0) { - sequences[nseqs++] = tsequence_make((const TInstant **) instants, ninsts, - lower_inc, upper_inc, LINEAR, NORMALIZE_NO); + /* We can occasionally have ninsts == 1 and lower_inc == upper_inc == false + * These are cases where the sequence starts / ends on an inclusive border + * and we have seq->lower_inc / seq->upper_inc being false. + * Don't create a sequence, but still reset ninsts and lower_inc */ + if (ninsts > 1 || lower_inc || upper_inc) + sequences[nseqs++] = tsequence_make((const TInstant **) instants, ninsts, + lower_inc, upper_inc, LINEAR, NORMALIZE_NO); ninsts = 0; lower_inc = true; } inst1 = inst2; p1 = DatumGetGserializedP(tinstant_val(inst2)); } - if (ninsts > 0) + /* See above for explanation of condition */ + if (ninsts > 0 && (ninsts > 1 || lower_inc || upper_inc)) sequences[nseqs++] = tsequence_make((const TInstant **) instants, ninsts, lower_inc, upper_inc, LINEAR, NORMALIZE_NO); pfree_array((void **) tofree, nfree); diff --git a/mobilitydb/test/point/expected/056_tpoint_spatialfuncs.test.out b/mobilitydb/test/point/expected/056_tpoint_spatialfuncs.test.out index 7becb8d757..2a2da0c253 100644 --- a/mobilitydb/test/point/expected/056_tpoint_spatialfuncs.test.out +++ b/mobilitydb/test/point/expected/056_tpoint_spatialfuncs.test.out @@ -3603,6 +3603,42 @@ select astext(atStbox(tgeompoint '[Point(-2 1)@2000-01-01, Point(2 1)@2000-01-02 {[POINT(0 1)@Sat Jan 01 12:00:00 2000 PST, POINT(1 1)@Sat Jan 01 18:00:00 2000 PST]} (1 row) +select astext(atStbox(tgeompoint '(Point(2 1)@2000-01-01, Point(3 1)@2000-01-02)', stbox 'STBOX X((0,0),(2 2))')); + astext +-------- + +(1 row) + +select astext(atStbox(tgeompoint '(Point(3 1)@2000-01-01, Point(2 1)@2000-01-02)', stbox 'STBOX X((0,0),(2 2))')); + astext +-------- + +(1 row) + +select astext(atStbox(tgeompoint '(Point(1 1)@2000-01-01, Point(2 1)@2000-01-02, Point(3 1)@2000-01-04)', stbox 'STBOX X((0,0),(2 2))')); + astext +-------------------------------------------------------------------------------------- + {(POINT(1 1)@Sat Jan 01 00:00:00 2000 PST, POINT(2 1)@Sun Jan 02 00:00:00 2000 PST]} +(1 row) + +select astext(atStbox(tgeompoint '(Point(3 1)@2000-01-01, Point(2 1)@2000-01-02, Point(1 1)@2000-01-04)', stbox 'STBOX X((0,0),(2 2))')); + astext +-------------------------------------------------------------------------------------- + {[POINT(2 1)@Sun Jan 02 00:00:00 2000 PST, POINT(1 1)@Tue Jan 04 00:00:00 2000 PST)} +(1 row) + +select astext(atStbox(tgeompoint '(Point(1 1)@2000-01-01, Point(1 1)@2000-01-02, Point(3 1)@2000-01-04)', stbox 'STBOX X((0,0),(2 2))')); + astext +------------------------------------------------------------------------------------------------------------------------------- + {(POINT(1 1)@Sat Jan 01 00:00:00 2000 PST, POINT(1 1)@Sun Jan 02 00:00:00 2000 PST, POINT(2 1)@Mon Jan 03 00:00:00 2000 PST]} +(1 row) + +select astext(atStbox(tgeompoint '(Point(3 1)@2000-01-01, Point(3 1)@2000-01-02, Point(1 1)@2000-01-04)', stbox 'STBOX X((0,0),(2 2))')); + astext +-------------------------------------------------------------------------------------- + {[POINT(2 1)@Mon Jan 03 00:00:00 2000 PST, POINT(1 1)@Tue Jan 04 00:00:00 2000 PST)} +(1 row) + /* Errors */ SELECT asText(atStbox(tgeompoint 'SRID=4326;Point(1 1)@2000-01-01', 'GEODSTBOX ZT(((1,1,1),(2,2,2)),[2000-01-01,2000-01-02])')); ERROR: Operation on mixed planar and geodetic coordinates diff --git a/mobilitydb/test/point/queries/056_tpoint_spatialfuncs.test.sql b/mobilitydb/test/point/queries/056_tpoint_spatialfuncs.test.sql index 0bfd91d162..9e4177407b 100644 --- a/mobilitydb/test/point/queries/056_tpoint_spatialfuncs.test.sql +++ b/mobilitydb/test/point/queries/056_tpoint_spatialfuncs.test.sql @@ -978,6 +978,15 @@ select astext(atStbox(tgeompoint '[Point(0 2)@2000-01-01, Point(2 0)@2000-01-02] select astext(atStbox(tgeompoint '[Point(0 0)@2000-01-01, Point(2 2)@2000-01-02]', stbox 'STBOX X((0,0),(1 1))')); select astext(atStbox(tgeompoint '[Point(-2 1)@2000-01-01, Point(2 1)@2000-01-02]', stbox 'STBOX X((0,0),(1 1))')); +-- Edge cases with lower_inc / upper_inc = false +select astext(atStbox(tgeompoint '(Point(2 1)@2000-01-01, Point(3 1)@2000-01-02)', stbox 'STBOX X((0,0),(2 2))')); +select astext(atStbox(tgeompoint '(Point(3 1)@2000-01-01, Point(2 1)@2000-01-02)', stbox 'STBOX X((0,0),(2 2))')); +select astext(atStbox(tgeompoint '(Point(1 1)@2000-01-01, Point(2 1)@2000-01-02, Point(3 1)@2000-01-04)', stbox 'STBOX X((0,0),(2 2))')); +select astext(atStbox(tgeompoint '(Point(3 1)@2000-01-01, Point(2 1)@2000-01-02, Point(1 1)@2000-01-04)', stbox 'STBOX X((0,0),(2 2))')); +select astext(atStbox(tgeompoint '(Point(1 1)@2000-01-01, Point(1 1)@2000-01-02, Point(3 1)@2000-01-04)', stbox 'STBOX X((0,0),(2 2))')); +select astext(atStbox(tgeompoint '(Point(3 1)@2000-01-01, Point(3 1)@2000-01-02, Point(1 1)@2000-01-04)', stbox 'STBOX X((0,0),(2 2))')); + + /* Errors */ SELECT asText(atStbox(tgeompoint 'SRID=4326;Point(1 1)@2000-01-01', 'GEODSTBOX ZT(((1,1,1),(2,2,2)),[2000-01-01,2000-01-02])')); SELECT asText(atStbox(tgeompoint 'SRID=5676;Point(1 1)@2000-01-01', 'STBOX XT(((1,1),(2,2)),[2000-01-01,2000-01-02])'));