Skip to content
GitLab
Projects Groups Snippets
  • /
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
  • Sign in
  • CityDoctor2 CityDoctor2
  • Project information
    • Project information
    • Activity
    • Labels
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
    • Locked Files
  • Issues 18
    • Issues 18
    • List
    • Boards
    • Service Desk
    • Milestones
    • Iterations
    • Requirements
  • Merge requests 2
    • Merge requests 2
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Schedules
    • Test Cases
  • Deployments
    • Deployments
    • Environments
    • Releases
  • Packages and registries
    • Packages and registries
    • Package Registry
    • Infrastructure Registry
  • Monitor
    • Monitor
    • Metrics
    • Incidents
  • Analytics
    • Analytics
    • Value stream
    • CI/CD
    • Code review
    • Insights
    • Issue
    • Repository
  • Wiki
    • Wiki
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar
  • CityDoctor
  • CityDoctor2CityDoctor2
  • Issues
  • #122
Closed
Open
Issue created Aug 25, 2026 by Luna Riegel@luna.riegelOwner

GE_S_NOT_CLOSED/GE_S_MULTIPLE_CONNECTED_COMPONENTS due to unresolved TriangulatedSurface X-Link reference

Report from HTWD

Zusammenfassung

Referenziert ein gml:Solid per XLink eine gml:TriangulatedSurface, wird diese Referenz beim Einlesen nie aufgelöst — die Fläche fehlt in der internen Solid-Hülle, was fälschlich als offene Hülle (GE_S_NOT_CLOSED) bzw. als getrennte Komponente (GE_S_MULTIPLE_CONNECTED_COMPONENTS) gemeldet wird, obwohl die Fläche tatsächlich vorhanden und korrekt referenziert ist.

Beispiel (schema-valide, echte Ausgabe)

<bldg:lod3MultiSurface>
  <gml:MultiSurface gml:id="Face_0003H5B_0_7_MS">
    <gml:surfaceMember>
      <gml:TriangulatedSurface gml:id="DESNALK0pF001g4s_0_6_TIN">
        <gml:trianglePatches>
          <gml:Triangle>
            <gml:exterior>
              <gml:LinearRing>
                <gml:posList srsDimension="3">417951.504 5657231.633 245.128 417951.635 5657232.984 245.118 417951.635 5657232.984 232.98 417951.504 5657231.633 245.128</gml:posList>
              </gml:LinearRing>
            </gml:exterior>
          </gml:Triangle>
          <!-- weitere gml:Triangle-Elemente -->
        </gml:trianglePatches>
      </gml:TriangulatedSurface>
    </gml:surfaceMember>
  </gml:MultiSurface>
</bldg:lod3MultiSurface>

Referenziert vom Solid der übergeordneten AbstractBuilding per <gml:surfaceMember xlink:href="#DESNALK0pF001g4s_0_6_TIN"/> (bzw. äquivalent über die umschließende Fläche). gml:TriangulatedSurface ist laut GML-3.1.1-Schema eine _Surface (TriangulatedSurface → Surface → _Surface), surfaceMember akzeptiert _Surface — die Referenz ist damit schema-konform.

Root Cause (Quellcode-Nachweis)

de.hft.stuttgart.citydoctor2.mapper.citygml3.Citygml3GeometryMapper:

@Override
public void visit(Surface surface) {
    if (surface.getPatches() != null && !surface.getPatches().isSetObjects()) {
        logger.warn("Surface {} has no PolygonPatches.", surface.getId());
        return;
    }

    PatchCollection patchCollection = new PatchCollection();
    GeometryWalker patchCollector = new GeometryWalker() {
        @Override
        public void visit(PolygonPatch patch) {
            parsePolygonPatch(patch, patchCollection);
        }

        @Override
        public void visit(Triangle tri) {
            parsePolygon(null, tri.getExterior(), Collections.emptyList());
        }
    };
    surface.getPatches().getObjects().forEach(abstractSurfacePatch -> abstractSurfacePatch.accept(patchCollector));
}

Zwei Probleme in diesem einen Methodenkörper:

  1. visit(Triangle tri) übergibt null als gml:id (parsePolygon(null, ...)) statt der ID der umschließenden TriangulatedSurface oder einer generierten ID. In parsePolygon:

    private void parsePolygon(String id, AbstractRingProperty exterior, List<AbstractRingProperty> interior) {
        ...
        ConcretePolygon cdPoly = new ConcretePolygon();
        polygons.add(cdPoly);
        if (id != null) {
            cdPoly.setGmlId(new GmlId(id));
        }
        ...

    Ohne id bekommt das resultierende Polygon nie eine GmlId — es kann später über keinen XLink-href gefunden werden.

  2. patchCollection wird für Triangle-Patches nie befüllt — nur visit(PolygonPatch) ruft parsePolygonPatch(patch, patchCollection) auf. Die lokale Variable patchCollection wird nach der Schleife nirgends weiterverwendet (nicht zurückgegeben, keiner Map hinzugefügt) — toter Code für den Triangle-Fall.

Beim späteren Auflösen der XLink-Referenzen (de.hft.stuttgart.citydoctor2.mapper.citygml3.Citygml3FeatureMapper.resolveAndClearReferences) wird der href weder in compositeMap (da nie befüllt, s.o.) noch in polygonMap (da die Dreieck-Polygone nie eine GmlId erhielten, mit der sie in polygonMap indiziert werden könnten) gefunden:

private void handlePolygonReference(String href, Geometry geom) {
    ConcretePolygon concPoly = polygonMap.get(href);
    if (concPoly == null) {
        List<GmlId> featureList = unresolvedPolyRefIDMap.computeIfAbsent(href, k -> new ArrayList<>());
        ...

Die Referenz landet in unresolvedPolyRefIDMap, CityDoctor2 loggt selbst unresolvedReferencesDetected — die Fläche fehlt danach in der Solid-Hülle.

Empirische Verifikation

Dieselben 14 Testgebäude einmal mit gml:TriangulatedSurface, einmal mit denselben Flächen als einzelne gml:Polygon-Elemente (gleiche Geometrie, andere Repräsentation) durch dieselbe CityDoctor2-CLI geprüft:

Repräsentation GE_S_NOT_CLOSED GE_S_MULTIPLE_CONNECTED_COMPONENTS
gml:TriangulatedSurface (XLink vom Solid) 6 zusätzlich vorhanden
Dieselbe Geometrie als gml:Polygon 0 0

Zusätzlich mit citygml-tools 2.5.0 validate bestätigt: beide Repräsentationen sind XSD-schema-valide.

Erwartetes vs. tatsächliches Verhalten

  • Erwartet: eine per XLink referenzierte gml:TriangulatedSurface wird wie jede andere _Surface aufgelöst und ist Teil der geprüften Solid-Hülle.
  • Tatsächlich: die Referenz bleibt unauflösbar, die Fläche fehlt in der Hülle, was als Geometriefehler statt als Parser-Lücke erscheint.

Vorschlag

  • Citygml3GeometryMapper.visit(Surface): für Triangle-Patches ebenfalls eine GmlId vergeben (z. B. von der umschließenden TriangulatedSurface ableiten oder pro Dreieck generieren) und die entstehenden Polygone in polygonMap bzw. äquivalent in compositeMap auffindbar machen, analog zum bereits funktionierenden PolygonPatch-Pfad.
Assignee
Assign to
Time tracking

Dies ist die Gitlab-Instanz des Transferportals der Hochschule für Technik Stuttgart. Hier geht es zurück zum Portal