DSO Geovalidatie fout bij specifieke multiparts

Hallo gissers,

We krijgen een ‘non-simple’ melding bij dit soort situaties:

DSO validatieresultaat.png

Ok, prima. Maar voorzover ik kan nagaan wordt de geovalidatie geregeld door Geonovum en keurt hun online GML3.2 SF2 en GML-2D Geometrie deze objecten wel goed:
https://validatie.geostandaarden.nl/etf-webapp/testprojects

Twee opties geprobeerd; multipart in 3 en in 4 delen. Beide worden geaccepteerd door de Geonovum tool, hoewel verschillende applicaties er wel afwijkend op reageren.

De ‘4 delen’ optie is dezelfde geometrie, maar dan met een extra uitsnede:

GML3.2 SF2 - multipolygoon in vier delen.png

Vertexen:

drie delen:
MDSYS.SDO_GEOMETRY(2007, 28992, NULL, MDSYS.SDO_ELEM_INFO_ARRAY(1, 1003, 1, 11, 2003, 1, 21, 1003, 1), MDSYS.SDO_ORDINATE_ARRAY(124000, 586000, 124000, 547000, 188000, 547000, 188000, 586000, 124000, 586000, 134000, 580000, 179000, 580000, 179000, 555000, 134000, 555000, 134000, 580000, 140000, 575000, 140000, 560000, 175000, 560000, 175000, 575000, 155000, 575000, 160000, 570000, 155000, 565000, 150000, 570000, 155000, 575000, 155000, 575000, 140000, 575000))
MULTIPOLYGON (((124000.0 586000.0, 124000.0 547000.0, 188000.0 547000.0, 188000.0 586000.0, 124000.0 586000.0), (134000.0 580000.0, 179000.0 580000.0, 179000.0 555000.0, 134000.0 555000.0, 134000.0 580000.0)), ((140000.0 575000.0, 140000.0 560000.0, 175000.0 560000.0, 175000.0 575000.0, 155000.0 575000.0, 160000.0 570000.0, 155000.0 565000.0, 150000.0 570000.0, 155000.0 575000.0, 155000.0 575000.0, 140000.0 575000.0)))

vier delen:
MDSYS.SDO_GEOMETRY(2007, 28992, NULL, MDSYS.SDO_ELEM_INFO_ARRAY(1, 1003, 1, 11, 2003, 1, 21, 1003, 1, 33, 2003, 1), MDSYS.SDO_ORDINATE_ARRAY(124000, 586000, 124000, 547000, 188000, 547000, 188000, 586000, 124000, 586000, 134000, 580000, 179000, 580000, 179000, 555000, 134000, 555000, 134000, 580000, 140000, 575000, 140000, 560000, 175000, 560000, 175000, 575000, 155000, 575000, 140000, 575000, 155000, 575000, 160000, 570000, 155000, 565000, 150000, 570000, 155000, 575000))
MULTIPOLYGON (((124000.0 586000.0, 124000.0 547000.0, 188000.0 547000.0, 188000.0 586000.0, 124000.0 586000.0), (134000.0 580000.0, 179000.0 580000.0, 179000.0 555000.0, 134000.0 555000.0, 134000.0 580000.0)), ((140000.0 575000.0, 140000.0 560000.0, 175000.0 560000.0, 175000.0 575000.0, 155000.0 575000.0, 140000.0 575000.0), (155000.0 575000.0, 160000.0 570000.0, 155000.0 565000.0, 150000.0 570000.0, 155000.0 575000.0)))

GML in drie delen:

<?xml version="1.0" encoding="utf-8"?>
<ogr:FeatureCollection gml:id="aFeatureCollection" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://ogr.maptools.org/ voorbeeldgmldrie.xsd" xmlns:ogr="http://ogr.maptools.org/" xmlns:gml="http://www.opengis.net/gml/3.2">
	<gml:boundedBy>
		<gml:Envelope srsName="urn:ogc:def:crs:EPSG::28992">
			<gml:lowerCorner>124000 547000</gml:lowerCorner>
			<gml:upperCorner>188000 586000</gml:upperCorner>
		</gml:Envelope>
	</gml:boundedBy>
	<ogr:featureMember>
		<ogr:voorbeeldgmldrie gml:id="voorbeeldgmldrie.0">
			<gml:boundedBy>
				<gml:Envelope srsName="urn:ogc:def:crs:EPSG::28992">
					<gml:lowerCorner>124000 547000</gml:lowerCorner>
					<gml:upperCorner>188000 586000</gml:upperCorner>
				</gml:Envelope>
			</gml:boundedBy>
			<ogr:geometryProperty>
				<gml:MultiSurface srsName="urn:ogc:def:crs:EPSG::28992" gml:id="voorbeeldgmldrie.geom.0">
					<gml:surfaceMember>
						<gml:Polygon gml:id="voorbeeldgmldrie.geom.0.0">
							<gml:exterior>
								<gml:LinearRing>
									<gml:posList>124000 586000 124000 547000 188000 547000 188000 586000 124000 586000</gml:posList>
								</gml:LinearRing>
							</gml:exterior>
							<gml:interior>
								<gml:LinearRing>
									<gml:posList>134000 580000 179000 580000 179000 555000 134000 555000 134000 580000</gml:posList>
								</gml:LinearRing>
							</gml:interior>
						</gml:Polygon>
					</gml:surfaceMember>
					<gml:surfaceMember>
						<gml:Polygon gml:id="voorbeeldgmldrie.geom.0.1">
							<gml:exterior>
								<gml:LinearRing>
									<gml:posList>140000 575000 140000 560000 175000 560000 175000 575000 155000 575000 160000 570000 155000 565000 150000 570000 155000 575000 155000 575000 140000 575000</gml:posList>
								</gml:LinearRing>
							</gml:exterior>
						</gml:Polygon>
					</gml:surfaceMember>
				</gml:MultiSurface>
			</ogr:geometryProperty>
			<ogr:id>3</ogr:id>
		</ogr:voorbeeldgmldrie>
	</ogr:featureMember>
</ogr:FeatureCollection>

GML in vier delen:

<?xml version="1.0" encoding="utf-8"?>
<ogr:FeatureCollection gml:id="aFeatureCollection" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://ogr.maptools.org/ voorbeeldgmlvier.xsd" xmlns:ogr="http://ogr.maptools.org/" xmlns:gml="http://www.opengis.net/gml/3.2">
	<gml:boundedBy>
		<gml:Envelope srsName="urn:ogc:def:crs:EPSG::28992">
			<gml:lowerCorner>124000 547000</gml:lowerCorner>
			<gml:upperCorner>188000 586000</gml:upperCorner>
		</gml:Envelope>
	</gml:boundedBy>
	<ogr:featureMember>
		<ogr:voorbeeldgmlvier gml:id="voorbeeldgmlvier.0">
			<gml:boundedBy>
				<gml:Envelope srsName="urn:ogc:def:crs:EPSG::28992">
					<gml:lowerCorner>124000 547000</gml:lowerCorner>
					<gml:upperCorner>188000 586000</gml:upperCorner>
				</gml:Envelope>
			</gml:boundedBy>
			<ogr:geometryProperty>
				<gml:MultiSurface srsName="urn:ogc:def:crs:EPSG::28992" gml:id="voorbeeldgmlvier.geom.0">
					<gml:surfaceMember>
						<gml:Polygon gml:id="voorbeeldgmlvier.geom.0.0">
							<gml:exterior>
								<gml:LinearRing>
									<gml:posList>124000 586000 124000 547000 188000 547000 188000 586000 124000 586000</gml:posList>
								</gml:LinearRing>
							</gml:exterior>
							<gml:interior>
								<gml:LinearRing>
									<gml:posList>134000 580000 179000 580000 179000 555000 134000 555000 134000 580000</gml:posList>
								</gml:LinearRing>
							</gml:interior>
						</gml:Polygon>
					</gml:surfaceMember>
					<gml:surfaceMember>
						<gml:Polygon gml:id="voorbeeldgmlvier.geom.0.1">
							<gml:exterior>
								<gml:LinearRing>
									<gml:posList>140000 575000 140000 560000 175000 560000 175000 575000 140000 575000</gml:posList>
								</gml:LinearRing>
							</gml:exterior>
							<gml:interior>
								<gml:LinearRing>
									<gml:posList>155000 575000 150000 570000 155000 565000 160000 570000 155000 575000</gml:posList>
								</gml:LinearRing>
							</gml:interior>
						</gml:Polygon>
					</gml:surfaceMember>
				</gml:MultiSurface>
			</ogr:geometryProperty>
			<ogr:id>4</ogr:id>
		</ogr:voorbeeldgmlvier>
	</ogr:featureMember>
</ogr:FeatureCollection>

Applicatieoverzicht:
Applicatieresultaat bij multipolygoon in drie of vier delen.png

Aangezien onze geometrie wordt samengesteld mbv aggregaties, buffers en intersect acties kunnen we moeilijk ieder vertexje handmatig gaan aanpassen. We hebben dit probleem nu omzeild door de vertexen in de multiparts te tellen en bij iedere ‘dubbeling’ (behalve start en eind) een vierkant buffertje te genereren en deze weer samen te voegen met het bronobject. Risico is echter dat dit ook weer een dubbele vertex kan opleveren waardoor je dus weer een loopje nodig hebt, net zo lang tot iedere coördinaat nog maar één keer voor komt.

De OGC documentatie snel doorgebladerd, maar kon niet zo snel iets vinden over dit soort situaties.

Hopelijk kan iemand hier uitsluitsel over geven en wellicht testen of de vier delen optie wel acceptabel is. Kan deze versie zelf helaas niet aanbieden aan de DSO; bij verservicing dmv ESRI biedt de service altijd een drie-part aan in zowel rest als wfs, ook als de brongeometrie in de database vier delen heeft.

Groeten,

Jos

Met de broncode van de DSO geovalidatie (wat zeker geen Geonovum geovalidatie is of gebruikt) voor mijn neus kan ik je in ieder geval vertellen wat er uit je verschilende scenario’s komt.

Voor de volledigheid zijn dit alle meldingen die je op je eerste drie delige multipolygon zou krijgen:

("GEOMETRY.03.2", "Bestand: 'test.xml'. Geometry with id: voorbeeldgmldrie.geom.0 : Contains repeated points at or near (155000.0, 575000.0, NaN)"),
("GEOMETRY.03.2", "Bestand: 'test.xml'. Geometry with id: voorbeeldgmldrie.geom.0 : Geometry isn't Simple, nonSimpleLocation at coordinate: (155000.0, 575000.0, NaN)"),
("GEOMETRY.03.2", "Bestand: 'test.xml'. Geometry with id: voorbeeldgmldrie.geom.0 isn't valid! Ring Self-intersection at or near point (155000.0, 575000.0, NaN)")

De vier delige multipolygon wordt goed gekeurd.

Hoi Robin,

Ok, bedankt voor de snelle reactie en het uitzoekwerk.

Kun je ook nagaan of deze validatie idd 100% conform OGC Simple is?

Hoop eigenlijk van niet. De vierdelige geometrie krijg ik wel in de database, maar geen idee hoe we deze dan kunnen aanbieden in een service. We hebben evt nog wel ergens een geoserver draaien, echter moet dan ook de opslag in SDO of misschien WKB en blijft de vraag of geoserver deze ook niet gaat omzetten naar driedelige objecten.

Voor de isSimple en isValid validaties wordt onder water de JTS (Java Topology Suite) library gebruikt die de OGC 99-049 standaard volgt in de implementatie. Voor zover ik weet zitten daar momenteel geen problemen in die een foutieve validatie op zouden leveren.

Toevallig (?) is het voorbeeld dat je hebt gebruikt een bekend voorbeeld van een invalide geometrie (volgens OGC), en heeft het zelfs een eigen naam gekregen: “banana polygon”.

Het is invalide omdat het self-intersect op het punt waar de outer ring naar binnen buigt (bron, en ook te vinden in de eerder genoemde OGC 99-049 standaard (maar minder leesbaar)):

  • Rings may not self-intersect (they may neither touch nor cross themselves).

En valide als je een losse inner ring van de binnenkant maakt, want:

  • Rings may not touch other rings, except at a point.

In je eerste bericht noemde je ook ESRI. Daar vond ik nog het volgende over:

The “banana polygon” (or “inverted shell”) is a case where the OGC topology model for valid geometry and the model used internally by ESRI differ. The ESRI model considers rings that touch to be invalid, and prefers the banana form for this kind of shape.

Dat helpt je misschien niet verder met een oplossing, maar verklaart waarschijnlijk wel waarom je uit je service altijd de driedelige variant krijgt.

Het antwoord op de vraag hoe je je service zover gaat krijgen om valide geometrieën uit te leveren, moet ik je schuldig blijven. Hoop dat andere forum leden hier bij kunnen helpen.

Super antwoord, bedankt voor de opheldering.

We gebruiken voorlopig wel de workaround met mini-buffertjes, maar wellicht is deze op termijn ook nog wel een optie:
https://gis.stackexchange.com/questions/410721/can-a-self-intersecting-banana-polygon-be-made-ogc-compliant-in-an-azure-sql-dat

Is wat puurder en dan kan de data worden aangeboden met de oorspronkelijke coordinaten. Vermoed alleen dat de performance op complexe datasets dit wat gaat hinderen.

1 like

Het ESRI datamodel en het OGC datamodel zijn hier fundamenteel verschillend. Zo’n polygoon kan geschreven worden als een buitenringen met een op 1 punt rakende binnenring (OGC), of als 1 doorlopende buitenring (ESRI). Beide methoden zijn goed maar fundamenteel anders.

Ik vermoed dat het vanuit ESRI software niet mogelijk is de OGC schrijfwijze te exporteren. Op zich vreemd, want gml export is wel mogelijk (en dat vereist het volgen van het datamodel, dus het wegschrijven van de binnenring). In de IMRO tijd hadden we dat probleem ook al en schreef ik zelf een routine om vanuit ArcView 3 (!) gml weg te schrijven. Ik had voor dit specifieke onderdeel toen een algoritme bedacht, maar zou niet meer weten welke.

Tegenwoordig zou mbv Postgis of gdal een buffer met afstand 0 dit probleem moeten oplossen.

MArco

De omzetting kan nu ook wel met standaard ESRI tools; ArcGIS Pro (python 3.7) kent een OGC-regel optie en genereerd een vier-delen object:

Echter, als je deze vier-delen terugschrijft vanuit de fgdb maar de database wordt het weer een drie-deel.

Een optie zou zijn om de data naar de DSO aan te bieden vanuit een fgdb ipv de database; de service laat de geometrie dan ongemoeid: