
Guten Tag! Ich bin Victor, ein Entwickler bei Gems Development. Unser Team arbeitet jeden Tag mit Geodaten unterschiedlicher Komplexität und Qualität. Bei der Durchführung einer räumlichen Schnittoperation mit Postgis in Postgresql ist der folgende Fehler aufgetreten:
XX000: GEOSIntersects: TopologyException: side location conflict at 10398.659 3844.9200000000001
Die Anfrage, die zum Fehler führt, sieht folgendermaßen aus:
select q1.key,st_asGeoJson(geoloc)
from usahalinsk.V_GEO_OOPT q1
where ST_Intersects(geoloc,
ST_GeomFromGeoJSON('{"type":"Polygon","coordinates":
[[[11165.15,2087.5],[11112,2066.6],[11127.6,2022.5],
[11122.6,2020.7],
[11122.25,2021.2],[11107.07,2015.7],
[11121,1947],[11123.48,1922.99],[11128.42,1874.4],
[11131.5,1875],[11140.96,1876.81],[11160.73,1880.59],
[11201.04,1888.3],[11194.2,1908],[11221.93,1916.57],
[11223.3,1917],[11165.15,2087.5]]]}'))
Die Lösung für dieses Problem blockiert die Arbeit der Benutzer, da keine Berichte über die Daten erstellt werden können und die Arbeit bei der Bereitstellung von Diensten verlangsamt wird. Viele Aktionen in dem System, das wir entwickeln, wie z. B.: Erstellen eines Layouts für ein Grundstück, Erstellen eines Stadtplanungsplans für ein Grundstück und andere verwenden räumliche Operationen wie diese.
Nehmen wir an, dass das Problem eine falsche Geometrie ist. Dieser Fehler wird häufig durch die Schnittoperation generiert, wenn die an der Abfrage beteiligten Objekte Selbstschnittpunkte oder doppelte Punkte aufweisen. Ein Beispiel für diese Geometriefehler ist unten zu sehen. (Der Polygonrand schneidet sich selbst und die Linie enthält zwei identische Koordinaten.)

Wir haben unsere eigenen Untersuchungen durchgeführt, um die Fehlerursachen zu ermitteln, und möchten Sie darüber informieren.
Wir verwenden derzeit Postgis 2.4 und Postgresql 9.6. Gehen wir gleich zum Üben. Lassen Sie uns die konstante Geometrie auf Gültigkeit überprüfen und feststellen, dass alles korrekt funktioniert.

Wir können davon ausgehen, dass sich die Angelegenheit in der Tabelle (Ansicht) usahalinsk.V_GEO_OOPT befindet, in der wir nach Schnittpunkten suchen. Um die Hypothese zu bestätigen, werden wir auch diese Daten überprüfen.

Aber auch hier finden wir keine Fehler. Außerdem wurden die Daten überhaupt nicht in die Stichprobe aufgenommen. Wenn dies der Fall wäre, würde die Aufgabe gelöst, indem die gefundenen Einträge über die Funktion Postgis st_makeValid korrigiert würden.
Die Ansicht enthält jedoch keine Fehler, und die Anforderung wird nicht ausgeführt. Wir schlagen vor, seinen Plan zu betrachten.

Hinweis: Im realen Modell verwenden wir drei Spalten für die Geometrie (für Polygone, Linien und Punkte). Der Kürze halber nennen wir dies das Geoloc-Feld - es speichert die Geometrie und zeigt sie in der Ansicht an.
Unsere Ansicht usahalinsk.V_GEO_OOPT wird als Auswahl aus der Tabelle mit Geodaten usahalinsk.d_geometry erstellt und ein räumlicher Index für das Feld mit Geometrie erstellt.
Dies bedeutet, dass bei der Ausführung der Abfrage der Index gelesen wird und irgendwo in der Tabelle, ohne in unsere Auswahl zu gelangen, ungültige räumliche Daten im Index enthalten sind. Es ist über den gesamten Tisch verteilt.
Versuchen wir, den Index zu löschen:
DROP INDEX usahalinsk.d_geometry_cs1_all_sx;
Und versuchen wir, die problematische Anfrage zu erfüllen.

Es lief ohne Fehler. Wir bestätigen, dass das Problem im Index enthalten ist. Sie können den Index zurückgeben, jedoch mit der Bedingung für die richtige Geometrie:
CREATE INDEX d_geometry_cs1_all_sx
ON usahalinsk.d_geometry
USING gist(geoloc)
where st_isvalid(geoloc)=true;
Lassen Sie uns die Implementierung überprüfen und den Plan sehen.

Die Anforderung wurde fehlerfrei ausgeführt, und der Index im Plan wird ebenfalls verwendet. Der Nachteil einer solchen Lösung kann die Verlangsamung des Einfügens / Aktualisierens tk sein. Zusätzlich wird die Bedingung beim erneuten Erstellen des Index überprüft.
Lassen Sie uns diese Änderung zurückgeben und dennoch versuchen, herauszufinden, welche Objekte im Index dazu führen, dass unsere Abfrage fehlschlägt.
DROP INDEX usahalinsk.d_geometry_cs1_all_sx;
CREATE INDEX d_geometry_cs1_all_sx
ON usahalinsk.d_geometry
USING gist
(geoloc);
Ich möchte Sie daran erinnern, dass wir die Koordinaten des Fehlerorts haben:
XX000: GEOSIntersects: TopologyException: side location conflict at 10398.659 3844.9200000000001
Wenn wir jedoch in den Daten oder als Ergebnis der IsValidReason-Funktion suchen, die den Grund für den Fehler zurückgibt, werden wir nichts Ähnliches finden.
select key,ST_IsValidReason(geoloc)
from usahalinsk.d_geometry
where st_isvalid(geoloc)!=true
and ST_AsText(geoloc) like '%3844.9200000000001%';
select key,ST_IsValidReason(geoloc)
from usahalinsk.d_geometry
where st_isvalid(geoloc)!=true
and ST_IsValidReason(geoloc) like '%3844.9200000000001%';
Mit dem folgenden Skript können Sie Objekte suchen, die sich auf die Abfrage auswirken. Wir werden jedes Objekt in der Tabelle überprüfen und es mit der gewünschten Konstante schneiden. Während der Ausführung fangen wir Ausnahmen ab und überprüfen deren Inhalt. Wenn der Fehler die benötigten Koordinaten enthält, ist dies unsere Problemgeometrie.
do
$$
declare
tKey bigint;
rec record;
error_text text;
--
error_info text:='GEOSIntersects: TopologyException: side location conflict at 10398.659 3844.9200000000001';
begin
--
for rec in(select key from usahalinsk.d_geometry)
loop
begin
select key into tKey
from (select * from usahalinsk.d_geometry q1
--
where q1.key=rec.key
and ST_Intersects(geoloc,
--
ST_GeomFromGeoJSON('{"type":"Polygon","coordinates":[[[11165.15,2087.5],
[11112,2066.6],[11127.6,2022.5],[11122.6,2020.7],
[11122.25,2021.2],[11107.07,2015.7],[11121,1947], [11123.48,1922.99],[11128.42,1874.4],
[11131.5,1875],[11140.96,1876.81], [11160.73,1880.59],[11201.04,1888.3],
[11194.2,1908],[11221.93,1916.57],[11223.3,1917],
[11165.15,2087.5]]]}'))) geoQ;
exception when others then
--
GET STACKED DIAGNOSTICS error_text = MESSAGE_TEXT;
-- ,
if error_text=error_info then
raise info '%',rec.key;
end if;
end;
end loop;
end$$;
Als Ergebnis erhalten wir drei Geometrieschlüssel, die einfach zu reparieren sind:
update usahalinsk.d_geometry
set cs1_geometry_polygone=st_collectionextract(st_makevalid(geoloc),3)
where key in(
1000010001988961,
1000010001989399,
1000010004293508);
Ich werde die Frage beantworten, die sich stellt: "Warum ist es unmöglich, alle fehlerhaften Geometrien in der Tabelle zu korrigieren, um nicht selektiv nach den Gründen zu suchen?" ...
Tatsache ist, dass räumliche Daten aus verschiedenen Quellen (einschließlich von Rosreestr) in unser System gelangen und wir nicht alle Daten korrigieren können (in der Regel geht dies mit Verzerrungen einher). Nachdem wir die erforderlichen Schlüssel erhalten haben, analysieren wir, welche Daten sie darstellen und ob es möglich ist, sie zu korrigieren.
Die triviale Aufgabe, die Fehlerursache zu finden, kann zu einer vollständigen Untersuchung mit einem Korrektur-Skript am Ende werden.
Eine komplexere Version des Problems: Was ist, wenn der Schnittpunkt nicht mit einer Konstanten, sondern mit einer anderen Tabelle ausgeführt wird? Alternativ können Sie jedes der teilnehmenden Objekte in der ersten Tabelle mit jedem Objekt in der zweiten Tabelle schneiden. Und Ausnahmen fangen.
Wie oft stoßen Sie auf Geometrieprobleme und wie stellen Sie die Qualität Ihrer Geodaten sicher?