OSGeo / gdal

GDAL is an open source MIT licensed translator library for raster and vector geospatial data formats.
https://gdal.org
Other
4.64k stars 2.46k forks source link

GML xlink:href resolving for xlink:href="urn:uuid:0221fd89-7d20-4cea-b35a-691b2e6b0b94" GeoBorder elements #10064

Open sposs opened 1 month ago

sposs commented 1 month ago

What is the bug?

The xlink:href resolver fails for elements referred to using e.g. xlink:href="urn:uuid:0221fd89-7d20-4cea-b35a-691b2e6b0b94"

Steps to reproduce the issue

Using the attached file, GML_SKIP_RESOLVE_ELEMS=HUGE (or NONE), aix.zip run ogr2ogr -of "ESRI Shapefile" out aix.gml and observe the logs: ERROR 1: Geometry of feature 3 uuid.d94c3f1f-c952-4d1b-9e41-bc11917a9aa9 cannot be parsed: Invalid exterior ring. You may set the GML_SKIP_CORRUPTED_FEATURES configuration option to YES to skip to the next feature

Versions and provenance

Distributor ID: Debian Description: Debian GNU/Linux 11 (bullseye) Release: 11 Codename: bullseye

GDAL 3.9.0, released 2024/05/07 (debug build)

Additional context

No response

sposs commented 1 month ago

For reference: https://aixm.aero/sites/default/files/imce/AIXM51/aixm_feature_identification_and_reference-1.0.pdf, section 3.4 and 3.4.1.

sposs commented 4 weeks ago

Hi, We are willing to try to address this issue. There are complications though: the links do not point to particular geometry elements, but to top level elements, here GeoBorders. Those are not simple GML geometries and cannot be used 'whole' like in the regular xlink case. They need to be handled in a special way: the AIXM5 norm describes how to extract from the geoborder's geometry the part that's necessary for the 'parent'. I understand there are 2 ways currently to resolve the links: the 'NONE' way that loads in RAM and dynamically resolves the link, and the 'HUGE' case where the xlinks are stored in a temporary sqlite file. I understand that either case leads to a 'rewrite' of the source gml file, where the xlinks are replaced by the appropriate GML geometries that were obtained. Is that correct? I also think I understand that the resolvers do not care about the geometries at that point, only copy chunks of xml. Is that also correct? If that's correct, then I also understand that as such, the way GDAL works will not be able to handle this issue: to extract the geoborder part, it's necessary to know the last point of the geometry before the geoborder reference, and the first point after the geoborder reference. Then extract from the GeoBorder (a LineString or a Polygon) the part that is needed, meaning the geometry needs to be parsed at the same time as the xlink resolution. See https://ext.eurocontrol.int/aixm_confluence/display/ACGAIP/Geoborder+for+Airspace+Border#GeoborderforAirspaceBorder-Abstractreferencetoremotefeature for reference. I see that that page actually says Abstract references to geo-borders fall outside the GML specification :-(.

rouault commented 4 weeks ago

where the xlinks are replaced by the appropriate GML geometries that were obtained. Is that correct? I also think I understand that the resolvers do not care about the geometries at that point, only copy chunks of xml. Is that also correct?

for the NONE resolver, yes. for the HUGE resolver, I see there are specific processings related to GML topogeometries (node/edge/faces). I can't tell more as I haven't written that part.

it's necessary to know the last point of the geometry before the geoborder reference, and the first point after the geoborder reference.

I'm not clear why

meaning the geometry needs to be parsed at the same time as the xlink resolution. See https://ext.eurocontrol.int/aixm_confluence/display/ACGAIP/Geoborder+for+Airspace+Border#GeoborderforAirspaceBorder-Abstractreferencetoremotefeature for reference.

ok, they're kind of abusing xlink:href, at least simple substitution can't be done. But perhaps the HUGE resolver could have logic to deal with that particular case, and recognize that it must substitute the curve from the geoborder instead of the geoborder feature itself?

sposs commented 4 weeks ago

it's necessary to know the last point of the geometry before the geoborder reference, and the first point after the geoborder reference.

I'm not clear why

I can give you an example: At the border between France and Germany, there is a power plant (Fessenheim). Above that, there is a prohibited area, see attached. Screenshot at 2024-06-07 09-21-06 The geometry of that is

                  <aixm:horizontalProjection>
                    <aixm:ElevatedSurface xmlns:gml="http://www.opengis.net/gml/3.2" xmlns:aixm="http://www.aixm.aero/schema/5.1" xmlns:xlink="http://www.w3.org/1999/xlink" srsName="urn:ogc:def:crs:EPSG::4326" gml:id="uuid.99f30453-14a4-4883-87bb-499c133e0
4af">
                      <gml:patches>
                        <gml:PolygonPatch interpolation="planar">
                          <gml:exterior>
                            <gml:Ring>
                              <gml:curveMember xlink:type="simple">
                                <aixm:Curve srsName="urn:ogc:def:crs:EPSG::4326" gml:id="uuid.d1627c9c-c33c-400a-bd05-fa1e7a396ff9">
                                  <gml:segments>
                                    <gml:ArcByCenterPoint interpolation="circularArcCenterPointWithRadius" numArc="1">
                                      <gml:pos>47.9041667 7.5633333</gml:pos>
                                      <gml:radius uom="km">5.011552</gml:radius>
                                      <gml:startAngle uom="deg">-177.1461367710253</gml:startAngle>
                                      <gml:endAngle uom="deg">28.96608157598405</gml:endAngle>
                                    </gml:ArcByCenterPoint>
                                  </gml:segments>
                                </aixm:Curve>
                              </gml:curveMember>
                              <gml:curveMember xlink:type="simple" xlink:href="urn:uuid:6d2fd9ce-e3c5-4078-9105-d793ac542d22"/>
                              <gml:curveMember xlink:type="simple">
                                <aixm:Curve srsName="urn:ogc:def:crs:EPSG::4326" gml:id="uuid.d717cb00-f248-4299-9082-a2fef8ef9ac1">
                                  <gml:segments>
                                    <gml:GeodesicString interpolation="geodesic">
                                      <gml:posList>47.8591667 7.56 47.8591667 7.56</gml:posList>
                                    </gml:GeodesicString>
                                  </gml:segments>
                                </aixm:Curve>
                              </gml:curveMember>
                            </gml:Ring>
                          </gml:exterior>
                        </gml:PolygonPatch>
                      </gml:patches>
                    </aixm:ElevatedSurface>
                  </aixm:horizontalProjection>

The urn:uuid:6d2fd9ce-e3c5-4078-9105-d793ac542d22 is a GeoBorder object, whose definition is (stub)

<aixm:GeoBorder gml:id="uuid.6d2fd9ce-e3c5-4078-9105-d793ac542d22">
      <gml:identifier codeSpace="urn:uuid:">6d2fd9ce-e3c5-4078-9105-d793ac542d22</gml:identifier>
      <gml:boundedBy xsi:nil="true"/>
      <aixm:timeSlice>
        <aixm:GeoBorderTimeSlice gml:id="uuid.6466c84a-5fd4-4346-b6c7-903c37e06bc4">
          <gml:validTime>
            <gml:TimePeriod gml:id="uuid.e9c7487f-e5bc-478e-bfbb-f48ada16218c">
              <gml:beginPosition>2007-05-10T00:00:00Z</gml:beginPosition>
              <gml:endPosition indeterminatePosition="unknown"/>
            </gml:TimePeriod>
          </gml:validTime>
          <aixm:interpretation>BASELINE</aixm:interpretation>
          <aixm:sequenceNumber>1</aixm:sequenceNumber>
          <aixm:correctionNumber>0</aixm:correctionNumber>
          <aixm:featureLifetime>
            <gml:TimePeriod gml:id="uuid.db856fa7-ca7e-444a-9c4e-063fefb59b24">
              <gml:beginPosition>2003-07-08T00:00:00Z</gml:beginPosition>
              <gml:endPosition indeterminatePosition="unknown"/>
            </gml:TimePeriod>
          </aixm:featureLifetime>
          <aixm:name>FRANCE_GERMANY</aixm:name>
          <aixm:type>STATE</aixm:type>
          <aixm:border>
            <aixm:Curve xmlns="http://www.aixm.aero/schema/5.1/message" xmlns:gml="http://www.opengis.net/gml/3.2" xmlns:gts="http://www.isotc211.org/2005/gts" xmlns:ead="http://www.aixm.aero/schema/5.1/extensions/EUR/EAD/Slot" xmlns:adr="http://www.aixm.aero/schema/5.1/extensions/EUR/ADR" xmlns:aixm="http://www.aixm.aero/schema/5.1" xmlns:qualityreport="http://www.aixm.aero/schema/5.1/extensions/qualityreport" xmlns:gco="http://www.isotc211.org/2005/gco" xmlns:audit="http://www.aixm.aero/schema/5.1/extensions/audit" xmlns:event="http://www.aixm.aero/schema/5.1/event" xmlns:nk="http://www.aixm.aero/schema/5.1/extensions/naturalkey" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:gmd="http://www.isotc211.org/2005/gmd" srsName="urn:ogc:def:crs:EPSG::4326" gml:id="uuid.a21526e4-470b-4dcb-b801-5dd9005dd3ca">
              <gml:segments>
                <gml:GeodesicString interpolation="geodesic">
                  <gml:posList>47.589969 7.589104 [...] </gml:posList>
                  </gml:GeodesicString>
              </gml:segments>
            </aixm:Curve>
          </aixm:border>
        </aixm:GeoBorderTimeSlice>
      </aixm:timeSlice>
    </aixm:GeoBorder>

This GeoBorder is a line string of the entire border. So to build the actual geometry of P36, it's necessary to extract he lineString of the border that corresponds to the points between the last point of the ArcByCenterPoint and the point at 47.8591667 7.56. Therefore substituting the entire geoborder geometry can't work, sadly.