Opened 3 months ago

Last modified 3 months ago

#5772 new defect

LineCrossingDirection called on a single-segment linestring and another linestring with multiple intersections does not always evaluate crossing direction at the correct intersection

Reported by: fkcheng Owned by: pramsey
Priority: low Milestone: PostGIS 3.4.4
Component: postgis Version: 3.4.x
Keywords: Cc:

Description

From what I understand of the docs in https://postgis.net/docs/ST_LineCrossingDirection.html, ST_LineCrossingDirection(lineA, lineB) evaluates the crossing direction at the first intersection we would find when traversing lineB from its start to end.

However, this is not always the case if lineB is only a single segment.

Test Cases

Consider the a C-shaped lineA and single segment lineB that intersects it twice:

SELECT ST_LineCrossingDirection(lineA, lineB) As A_cross_B
FROM (SELECT
 ST_GeomFromText('LINESTRING(-1 -1, 1 -1, 1 1, -1 1)') As lineA,
 ST_GeomFromText('LINESTRING(0 2, 0 -2)') As lineB
) As foo;
--Returns 3

The lines look like:

   B
 <-|--|
   |  |
A--|--|
   v

Traversing lineB from start to end, the first intersection is the top one, where lineA crosses lineB from lineB's left, so the query should return -3, not 3. We can accomplish this by splitting lineB into 2 segments:

SELECT ST_LineCrossingDirection(lineA, lineB) As A_cross_B
FROM (SELECT
 ST_GeomFromText('LINESTRING(-1 -1, 1 -1, 1 1, -1 1)') As lineA,
 ST_GeomFromText('LINESTRING(0 2, 0 0, 0 -2)') As lineB
) As foo;
--Returns -3

Reversing the directions of lineA and lineB also gives an erroneous result:

SELECT ST_LineCrossingDirection(lineA_rev, lineB_rev) As A_rev_cross_B_rev
FROM (SELECT
 ST_GeomFromText('LINESTRING(-1 1, 1 1, 1 -1, -1 -1)') As lineA_rev,
 ST_GeomFromText('LINESTRING(0 -2, 0 2)') As lineB_rev
) As foo;
--Returns -3

Which looks like:

    ^
 Ar-|--|
    |  |
 <--|--|
    Br

Traversing lineB_rev from its start to its end, the first intersection is the bottom one, where lineA_rev crosses from lineB_rev's right, so we expect 3, not -3. This issue is also fixed by splitting lineB_rev into 2 segments:

SELECT ST_LineCrossingDirection(lineArev, lineBrev) As Arev_cross_Brev
FROM (SELECT
 ST_GeomFromText('LINESTRING(-1 1, 1 1, 1 -1, -1 -1)') As lineArev,
 ST_GeomFromText('LINESTRING(0 -2, 0 0, 0 2)') As lineBrev
) As foo;
--Returns 3

Possible clues

Running an SQL query that returns all 8 permutations of reversed line directions and LineCrossingDirection argument order shows that the other 6 permutations return the correct result, so my guess is that the wrong point is being used in the 2 erroneous cases.

Query:

SELECT ST_LineCrossingDirection(lineA, lineB) As A_cross_B,
       ST_LineCrossingDirection(lineB, lineA) As B_cross_A,
       ST_LineCrossingDirection(lineA_rev, lineB) As A_rev_cross_B,
       ST_LineCrossingDirection(lineB, lineA_rev) As B_cross_A_rev,
       ST_LineCrossingDirection(lineA, lineB_rev) As A_cross_B_rev,
       ST_LineCrossingDirection(lineB_rev, lineA) As B_rev_cross_A,
       ST_LineCrossingDirection(lineA_rev, lineB_rev) As A_rev_cross_B_rev,
       ST_LineCrossingDirection(lineB_rev, lineA_rev) As B_rev_cross_A_rev
FROM (SELECT
 ST_GeomFromText('LINESTRING(-1 -1, 1 -1, 1 1, -1 1)') As lineA,
 ST_GeomFromText('LINESTRING(0 2, 0 -2)') As lineB,
 ST_GeomFromText('LINESTRING(-1 1, 1 1, 1 -1, -1 -1)') As lineA_rev,
 ST_GeomFromText('LINESTRING(0 -2, 0 2)') As lineB_rev
) As foo;

Return values with visualizations:

   B
 <-|--|
   |  |
A--|--|
   v

A_cross_B: 3 (incorrect)

B_cross_A: -3

   ^
 <-|--|
   |  |
A--|--|
   Br

A_cross_B_rev: -3

B_rev_cross_A: 3

   B
Ar-|--|
   |  |
 <-|--|
   v

A_rev_cross_B: 3

B_cross_A_rev: -3

   ^
Ar-|--|
   |  |
 <-|--|
   Br

A_rev_cross_B_rev: -3 (incorrect)

B_rev_cross_A_rev: 3

Version information

SELECT version()

PostgreSQL 16.4 (Debian 16.4-1.pgdg110+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 10.2.1-6) 10.2.1 20210110, 64-bit

SELECT postgis_full_version();

POSTGIS="3.4.2 c19ce56" [EXTENSION] 
PGSQL="160" 
GEOS="3.9.0-CAPI-1.16.2"
PROJ="7.2.1
NETWORK_ENABLED=OFF
URL_ENDPOINT=https://cdn.proj.org
USER_WRITABLE_DIRECTORY=/var/lib/postgresql/.local/share/proj
DATABASE_PATH=/usr/share/proj/proj.db"
GDAL="GDAL 3.2.2, released 2021/03/05"
LIBXML="2.9.10"
LIBJSON="0.15"
LIBPROTOBUF="1.3.3"
WAGYU="0.5.0 (Internal)"
RASTER

Change History (1)

comment:1 by pramsey, 3 months ago

Milestone: PostGIS 3.4.3PostGIS 3.4.4
Note: See TracTickets for help on using tickets.