qgis / QGIS

QGIS is a free, open source, cross platform (lin/win/mac) geographical information system (GIS)
https://qgis.org
GNU General Public License v2.0
10.59k stars 3.01k forks source link

First and last segments of a closed linestring are not rendered when using a positive line offset #51583

Open alkra opened 1 year ago

alkra commented 1 year ago

What is the bug or the crash?

Screenshot of a closed square linestring in QGIS, where only two sides are drawn in green

On a Linestring dataset, I use a Simple Line symbology with a positive offset of 0.5 mm. When the dataset contains closed linestrings, on the closed linestrings the first and last segment are not displayed. See the attached project files (rename to .qgs):

test_3_10.qgs test_3_22.qgs

This bug appears only for positive offsets. I tested with millimeters and map units, same behaviour. I tested geographic (EPSG:4326) and projected (EPSG:2154) CRS, same behaviour. I tested various join style and cap style, same behaviour. I tested two file format versions for the QGIS project file, same behaviour.

Steps to reproduce the issue

  1. Add any linestring dataset which contains a closed linestring
  2. Use a Simple Line symbology. Set a positive offset.
  3. See that the first and last segments are no longer displayed.

Versions

I tested several versions.

Not affected versions: 3.10.10 (8b051b9a43, tested on Windows), 3.16.16 (f5778a89df, tested on Windows), 3.22.14 (Release 3.22, tested on Debian GNU/Linux 11 (bullseye))

Affected versions: 3.22.14 (4cde646c, tested on Windows), 3.28.2 (b47e00ba601, tested on Windows), and master (91886a22e, tested on Debian GNU/Linux sid)

Some of the About tables, by order of citation:

3.16.16 not affected

Version de QGIS | 3.16.16-Hannover | Révision du code | f5778a89df -- | -- | -- | -- Compilé avec Qt | 5.11.2 | Utilisant Qt | 5.11.2 Compilé avec GDAL/OGR | 3.1.4 | Utilisé avec GDAL/OGR | 3.1.4 Compilé avec GEOS | 3.8.1-CAPI-1.13.3 | Utilisé avec GEOS | 3.8.1-CAPI-1.13.3 Compilé avec SQLite | 3.29.0 | Fonctionne avec SQLite | 3.29.0 Version du client PostgreSQL | 11.5 | Version de SpatiaLite | 4.3.0 Version de QWT | 6.1.3 | Version de QScintilla2 | 2.10.8 Compilé avec PROJ | 6.3.2 | Fonctionne avec PROJ | Rel. 6.3.2, May 1st, 2020 Version de l'OS | Windows 10 (10.0) Extensions Python actives | processing

3.22.14 not affected

QGIS version 3.22.14-Białowieża QGIS code branch Release 3.22 Qt version 5.15.2 Python version 3.9.2 GDAL/OGR version 3.2.2 PROJ version 7.2.1 EPSG Registry database version v10.008 (2020-12-16) GEOS version 3.9.0-CAPI-1.16.2 SQLite version 3.34.1 PostgreSQL client version 13.9 (Debian 13.9-0+deb11u1) SpatiaLite version 5.0.1 QWT version 6.1.4 QScintilla2 version 2.11.6 OS version Debian GNU/Linux 11 (bullseye)

Active Python plugins processing 2.12.99 grassprovider 2.12.99 sagaprovider 2.12.99

3.22.14 affected

Version de QGIS | 3.22.14-Białowieża | Révision du code | 4cde646c -- | -- | -- | -- Version de Qt | 5.15.3 Version de Python | 3.9.5 Compilé avec GDAL/OGR | 3.6.1 | Utilisé avec GDAL/OGR | 3.6.2 Version de Proj | 9.1.1 Version de la base de données du registre EPSG | v10.076 (2022-08-31) Version de GEOS | 3.11.1-CAPI-1.17.1 Version de SQLite | 3.39.4 Version de PDAL | 2.4.3 Version du client PostgreSQL | 14.3 Version de SpatiaLite | 5.0.1 Version de QWT | 6.1.6 Version de QScintilla2 | 2.13.1 Version de l'OS | Windows 10 Version 2009   |   |   |   Extensions Python actives grassprovider | 2.12.99 processing | 2.12.99

master affected

QGIS version 3.29.0-Master QGIS code revision 91886a22e Qt version 5.15.8 Python version 3.11.1 GDAL/OGR version 3.6.2 PROJ version 9.1.1 EPSG Registry database version v10.076 (2022-08-31) GEOS version 3.11.1-CAPI-1.17.1 SQLite version 3.40.1 PostgreSQL client version unknown SpatiaLite version 5.0.1 QWT version 6.1.4 QScintilla2 version 2.13.3 OS version Debian GNU/Linux bookworm/sid

This copy of QGIS writes debugging output.

Active Python plugins

Supported QGIS version

New profile

Additional context

The real dataset is a road graph with loops.

Github reports more than 5000 commits between the affected 3.22.14 master and the unaffected 3.22.14 release 3.22 . I don't have tools to handle such a large diff.

I do not know the QGIS source code well enough to localise what may cause this strange behaviour. I may be able to test your ideas, though.

agiudiceandrea commented 1 year ago

I can confirm the issue. The issue does occur for both positive or negative line offset depending on the direction of the closed line (clockwise / counterclockwise). It seems to me it is related to the GEOS version used by QGIS on a system rather than the QGIS version.

agiudiceandrea commented 1 year ago

It seems to me it is related to the GEOS version used by QGIS on a system rather than the QGIS version.

See:

QGIS 3.16 using GEOS 3.8.1

https://user-images.githubusercontent.com/16253859/214810080-e2272742-8af1-4945-bcde-69453ee530c9.mp4

QGIS 3.16 using GEOS 3.11.1

https://user-images.githubusercontent.com/16253859/214810164-94abd6fd-e43d-4657-a954-845e74e7dec0.mp4

garci66 commented 1 year ago

Wanted to report a related side-effect. In "standard" QGIS 3.16, a self-intersecting polyline could have an offset with no issue. In 3.22 and 3.28, the offset is not drawn in those cases.

To further support the GEOS library case, in 3.22.10, with GEOS GEOS version 3.10.3-CAPI-1.16.1

the issue is not seen, but in 3.22.14 with GEOS 3.11.1 the issue is seen.

The image below shows what the output "should" be in case of self intersecting lines (and what is seen in 3.16 / 3.22.10:

image

The black centerline is the polygon. The Red line is an offset line in the stle with possitive offset and the green one has negative offsets.

alkra commented 1 year ago

I compiled geos and tested both versions 3.10.4 and 3.11.1 with geosop.

In the table below, LINESTRING(0 0,10 0,10 10,0 10,0 0) will be abreviated [ccw] (for counterclockwise) and LINESTRING(0 0,0 10,10 10,10 0,0 0) [cw].

command result-3.10 3.10 reversed? result-3.11 3.11 reversed? QGIS offset sign QGIS regress?
geosop -a [ccw] -f wkt offsetCurve 2 LINESTRING (2 2, 8 2, 8 8, 2 8, 2 2) N LINESTRING (2 2, 8 2, 8 8, 2 8, 2 2) N - N
geosop -a [ccw] -f wkt offsetCurve N-2 LINESTRING (-2 0,-2 10, [...], 0 12, 10 12, [...], 10 -2, 0 -2) Y LINESTRING (0 -2, 10 -2, [...], 12 0, 12 10, [...], -2 10, -2 0) N + Y
geosop -a [cw] -f wkt offsetCurve 2 LINESTRING (-2 0, -2 10, [...], 0 12, 10 12, [...], 10 -2, 0 -2) N LINESTRING (-2 0, -2 10, [...], 0 12, 10 12, [...], 10 -2, 0 -2) N - Y
geosop -a [cw] -f wkt offsetCurve N-2 LINESTRING (2 2, 8 2, 8 8, 2 8, 2 2) Y LINESTRING (2 2, 2 8, 8 8, 8 2, 2 2) N + N

So, it seems that:

a. GEOS does not drop points, but its behaviour changed

b. QGIS offsets are signed in the opposite way as GEOS offsets

c. QGIS fails when the curve orientation has the same sign as the QGIS offset

Open questions:

  1. Where in the code is QGIS dropping the start and end nodes? Does it call a function which drops duplicated nodes?

  2. Why does QGIS only drops duplicated nodes when the curve orientation has the same sign as the offset?

I found the related source code in QGIS, but I can't attach a debugger on my system, and I do not quite understand it.

@garci66: are you able to run geosop on your system? Does GEOS do the right thing? Does it depend on the line orientation? Your test case seems to support question 1., i.e. QGIS is performing a validation somewhere.

garci66 commented 1 year ago

I have never tested geosp... im really not familar with it. I can take a look though and see. I also opened an issue on the GEOS side:

https://github.com/libgeos/geos/issues/816

But in particular it seems related to changes rasised by this issue: https://github.com/libgeos/geos/issues/477

and this merged PR seems to be the main change https://github.com/libgeos/geos/pull/530

garci66 commented 1 year ago

in my case, the main problem is with self intersecting polygons such as the following: LINESTRING(0 0,10 0, 10 -5 ,0 10) where the difference between versions is very significant:

geosop -a "LINESTRING(0 0,10 0, 10 -5 ,0 10)" -f wkt offsetCurve 2

With GEOSOP 3.10 the result is

MULTILINESTRING ((7.73703418364266 2, 10 2, 10.390180644032256 1.9615705608064609, 10.76536686473018 1.8477590650225735, 11.111140466039204 1.6629392246050905, 11.414213562373096 1.414213562373095, 11.66293922460509 1.1111404660392044, 11.847759065022574 0.7653668647301796, 11.96157056080646 0.3901806440322565, 12 0, 12 -5, 11.961539159351426 -5.390338476621126, 11.847635873669098 -5.765664207293921, 11.662670959072994 -6.111541848899666, 11.413758321668972 -6.414668656580661, 11.110471350739644 -6.663386118490374, 10.764474718144712 -6.848128351959778, 10.38907574523535 -6.961790015386345, 9.99871259208827 -6.999999585645174, 9.60839895373173 -6.96128749054329, 9.23314662081353 -6.847142629801578, 8.88738811351304 -6.66195511071988, 8.584421594682436 -6.412847400959701, 8.335899411324313 -6.109400392450459, 5.596299149690674 -2), (0 2, 2.9296324830240073 2, -1.6641005886756874 8.890599607549541))

(the multilinestring was expected, as the loop figure has to be cut)

while with GEOSOP 3.11.1: LINESTRING (0 2, 2.9296324830240073 2, -1.6641005886756874 8.890599607549541)

which is clearly different (misses the "looped over itself" part)

while the other option (negative offset) geosop -a "LINESTRING(0 0,10 0, 10 -5 ,0 10)" -f wkt offsetCurve N-2

the result for 3.10 is MULTILINESTRING ((1.6641005886756874 11.109400392450459, 7.73703418364266 2), (5.596299149690674 -2, 0 -2))

and for 3.11 its even worse: LINESTRING (0 -2, 5.596299149690674 -2)

as its only doing an offset for the first segment of the line and nothing else!

garci66 commented 1 year ago

the image below is not the linestring drawn above but its a similar example: image

you can see that one of the offsets gets truncated in the first segment

pathmapper commented 1 year ago

Another test case in https://github.com/qgis/QGIS/issues/51750.


With issue: Artifact from MinGW64 Windows build (https://github.com/qgis/QGIS/commit/d0a9f1249448c5dbde370052852220179df1ee8f)

https://github.com/qgis/QGIS/suites/10798239191/artifacts/543108183

QGIS version 3.29.0-Master QGIS code branch master
Qt version 5.15.8
Python version 3.11.1
GDAL/OGR version 3.6.2
PROJ version 9.1.1
EPSG Registry database version v10.076 (2022-08-31)
GEOS version 3.11.1-CAPI-1.17.1
SQLite version 3.36.0
PostgreSQL client version unknown
SpatiaLite version 5.0.1
QWT version 6.2.0
QScintilla2 version 2.13.0
OS version Windows 10 Version 2009
       

This copy of QGIS writes debugging output.   |   |   |   Active Python plugins db_manager | 0.1.20 grassprovider | 2.12.99 MetaSearch | 0.3.6 processing | 2.12.99


No issue:

QGIS version 3.29.0-Master QGIS code revision d0a9f12494
Qt version 5.15.3
Python version 3.10.6
GDAL/OGR version 3.7.0dev-6bdeb5623b
PROJ version 8.2.1
EPSG Registry database version v10.041 (2021-12-03)
GEOS version 3.10.2-CAPI-1.16.0
SQLite version 3.37.2
PostgreSQL client version unknown
SpatiaLite version 5.0.1
QWT version 6.1.4
QScintilla2 version 2.11.6
OS version Ubuntu 22.04.1 LTS
       

Active Python plugins grassprovider | 2.12.99 MetaSearch | 0.3.6 processing | 2.12.99 db_manager | 0.1.20

pathmapper commented 1 year ago

From https://github.com/qgis/QGIS/issues/51750:

Sometimes, if the line is not fully visible on the canvas, there is no issue:

pathmapper commented 1 year ago

GEOS behavior has changed but it looks like we are mixing two different issues here:

  1. Missing first and last segment of a closed linestring with offset -> the issue seems to be a changed orientation of the GEOS output for some cases -> possibly fixable on QGIS side
  2. https://github.com/libgeos/geos/issues/816 -> Upstream GEOS issue
garci66 commented 1 year ago

yes... sorry about that. At first I thought they could be related since both seemed to stem from the change in libgeos version. But I can open a separate issue tracking the upstream GEOS issue

pathmapper commented 1 year ago

No worries, just wanted to point that out because it wasn't clear for me on a first sight.

For me there's no need to open a separate issue - @agiudiceandrea what do you think?

agiudiceandrea commented 1 year ago

2. GEOSOffsetCurve_r behaviour has changed on self-intersecting lines libgeos/geos#816 -> Upstream GEOS issue

@pathmapper, it seems such issue has been allegedly fixed in GEOS 3.12 with https://github.com/libgeos/geos/commit/7561b50f943305b1419bd655e10e7dcad2cb9492.

1. Missing first and last segment of a closed linestring with offset -> the issue seems to be a changed orientation of the GEOS output for some cases -> possibly fixable on QGIS side

This one is still an issue: https://github.com/qgis/QGIS/issues/55050.

alexbruy commented 1 year ago

@pathmapper, it seems such issue has been allegedly fixed in GEOS 3.12 with libgeos/geos@7561b50.

Given that one issue is already fixed upstrean and second has a separate ticket should we close this?

github-actions[bot] commented 11 months ago

The QGIS project highly values your report and would love to see it addressed. However, this issue has been left in feedback mode for the last 14 days and is being automatically marked as "stale". If you would like to continue with this issue, please provide any missing information or answer any open questions. If you could resolve the issue yourself meanwhile, please leave a note for future readers with the same problem and close the issue. In case you should have any uncertainty, please leave a comment and we will be happy to help you proceed with this issue. If there is no further activity on this issue, it will be closed in a week.

garci66 commented 11 months ago

@alexbruy ... I understand the fix is already in upstream but, pardon my ignorance, do we known when / how it will be integrated into the main QGIS builds? can we get a view of which LTS versions would get the fix? just to be sure so we can test it. Thanks!

garci66 commented 11 months ago

Also.. seems like #55050 was auto-closed... and not sure its fixed yet

github-actions[bot] commented 10 months ago

The QGIS project highly values your report and would love to see it addressed. However, this issue has been left in feedback mode for the last 14 days and is being automatically marked as "stale". If you would like to continue with this issue, please provide any missing information or answer any open questions. If you could resolve the issue yourself meanwhile, please leave a note for future readers with the same problem and close the issue. In case you should have any uncertainty, please leave a comment and we will be happy to help you proceed with this issue. If there is no further activity on this issue, it will be closed in a week.

alkra commented 10 months ago

I compiled QGIS against GEOS 3.12.1. Same erroneous behaviour.

QGIS version: 3.35.0-Master QGIS code revision: 8677bc2f18 Qt version: 5.15.10 Python version: 3.11.7 GDAL/OGR version: 3.8.2 PROJ version: 9.3.1 EPSG Registry database version: v10.098 (2023-11-24) GEOS version: 3.12.1-CAPI-1.18.1 SQLite version: 3.44.2 PostgreSQL client version: 16.1 (Debian 16.1-1) SpatiaLite version: 5.1.0 QWT version: 6.1.4 QScintilla2 version: 2.14.1 OS version: Debian GNU/Linux trixie/sid

Screen shot

I tested geosopt on 3.12.1:

command result-3.10 3.10 reversed? result-3.11 3.11 reversed? result-3.12 3.12 reversed? QGIS offset sign QGIS regress?
geosop -a [ccw] -f wkt offsetCurve 2 LINESTRING (2 2, 8 2, 8 8, 2 8, 2 2) N LINESTRING (2 2, 8 2, 8 8, 2 8, 2 2) N LINESTRING (2 2, 8 2, 8 8, 2 8, 2 2) N - N
geosop -a [ccw] -f wkt offsetCurve N-2 LINESTRING (-2 0,-2 10, [...], 0 12, 10 12, [...], 10 -2, 0 -2) Y LINESTRING (0 -2, 10 -2, [...], 12 0, 12 10, [...], -2 10, -2 0) N LINESTRING (0 -2, 10 -2, [...], 12 0, 12 10, [...], -2 10, -2 0) N + Y
geosop -a [cw] -f wkt offsetCurve 2 LINESTRING (-2 0, -2 10, [...], 0 12, 10 12, [...], 10 -2, 0 -2) N LINESTRING (-2 0, -2 10, [...], 0 12, 10 12, [...], 10 -2, 0 -2) N LINESTRING (-2 0, -2 10, [...], 0 12, 10 12, [...], 10 -2, 0 -2) N - Y
geosop -a [cw] -f wkt offsetCurve N-2 LINESTRING (2 2, 8 2, 8 8, 2 8, 2 2) Y LINESTRING (2 2, 2 8, 8 8, 8 2, 2 2) N LINESTRING (2 2, 2 8, 8 8, 8 2, 2 2) N + N

The visible behaviour of GEOS has not changed in 3.12. GEOS is still working as expected.

From #51750:

Sometimes, if the line is not fully visible on the canvas, there is no issue:

I can confirm that, when the duplicated node lies outside of the canvas, the offset works as expected. (does QGIS sends only part of the linestring to GEOS? Or is the resulting closed offset curve truncated before QGIS deletes the duplicated node?)

bug-51583-outcanvas

Given that one issue is already fixed upstrean and second has a separate ticket should we close this?

#55050 is being closed as a duplicate of this bug.