[PostGIS] #5772: LineCrossingDirection called on a single-segment linestring and another linestring with multiple intersections does not always evaluate crossing direction at the correct intersection
PostGIS
trac at osgeo.org
Thu Aug 29 09:44:09 PDT 2024
#5772: LineCrossingDirection called on a single-segment linestring and another
linestring with multiple intersections does not always evaluate crossing
direction at the correct intersection
---------------------+---------------------------
Reporter: fkcheng | Owner: pramsey
Type: defect | Status: new
Priority: low | Milestone: PostGIS 3.4.3
Component: postgis | Version: 3.4.x
Keywords: |
---------------------+---------------------------
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:
{{{#!td
{{{
B
<-|--|
| |
A--|--|
v
}}}
A_cross_B: 3 (incorrect)
B_cross_A: -3
}}}
{{{#!td
{{{
^
<-|--|
| |
A--|--|
Br
}}}
A_cross_B_rev: -3
B_rev_cross_A: 3
}}}
|----------------
{{{#!td
{{{
B
Ar-|--|
| |
<-|--|
v
}}}
A_rev_cross_B: 3
B_cross_A_rev: -3
}}}
{{{#!td
{{{
^
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
}}}
--
Ticket URL: <https://trac.osgeo.org/postgis/ticket/5772>
PostGIS <http://trac.osgeo.org/postgis/>
The PostGIS Trac is used for bug, enhancement & task tracking, a user and developer wiki, and a view into the subversion code repository of PostGIS project.
More information about the postgis-tickets
mailing list