All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.citygml4j.cityjson.adapter.geometry.serializer.SpaceGeometryBuilder Maven / Gradle / Ivy

There is a newer version: 3.2.4
Show newest version
/*
 * citygml4j - The Open Source Java API for CityGML
 * https://github.com/citygml4j
 *
 * Copyright 2013-2025 Claus Nagel 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.citygml4j.cityjson.adapter.geometry.serializer;

import org.citygml4j.cityjson.adapter.geometry.MultiCurveProvider;
import org.citygml4j.cityjson.adapter.geometry.MultiSurfaceProvider;
import org.citygml4j.cityjson.model.core.ExtendedSpaceGeometry;
import org.citygml4j.cityjson.writer.CityJSONSerializerHelper;
import org.citygml4j.core.model.common.GeometryInfo;
import org.citygml4j.core.model.construction.AbstractFillingSurface;
import org.citygml4j.core.model.core.AbstractFeature;
import org.citygml4j.core.model.core.AbstractSpace;
import org.citygml4j.core.model.core.AbstractSpaceBoundary;
import org.citygml4j.core.model.core.AbstractSpaceBoundaryProperty;
import org.citygml4j.core.visitor.ObjectWalker;
import org.xmlobjects.gml.model.geometry.AbstractGeometry;
import org.xmlobjects.gml.model.geometry.GeometryProperty;
import org.xmlobjects.gml.model.geometry.aggregates.MultiCurve;
import org.xmlobjects.gml.model.geometry.aggregates.MultiCurveProperty;
import org.xmlobjects.gml.model.geometry.aggregates.MultiSurface;
import org.xmlobjects.gml.model.geometry.aggregates.MultiSurfaceProperty;
import org.xmlobjects.gml.model.geometry.compact.AbstractSimplePolygon;
import org.xmlobjects.gml.model.geometry.primitives.*;
import org.xmlobjects.model.Child;

import java.util.*;
import java.util.function.Consumer;

public class SpaceGeometryBuilder {
    private final BoundaryGeometryFinder finder = new BoundaryGeometryFinder();
    private Map multiSurfaceProviders;
    private Map multiCurveProviders;

    private SpaceGeometryBuilder() {
    }

    public static SpaceGeometryBuilder newInstance() {
        return new SpaceGeometryBuilder();
    }

    public SpaceGeometryBuilder withMultiSurfaceProviders(Map providers) {
        multiSurfaceProviders = providers;
        return this;
    }

    public SpaceGeometryBuilder withMultiCurveProviders(Map providers) {
        multiCurveProviders = providers;
        return this;
    }

    public void addUnreferencedBoundaryGeometries(AbstractSpace space) {
        collectSpaceGeometries(space);
        findUnreferencedBoundaryGeometries(space);
        addUnreferencedSurfaceGeometries(space);
        addUnreferencedCurveGeometries(space);
    }

    private void collectSpaceGeometries(AbstractSpace space) {
        SpaceGeometryCollector collector = new SpaceGeometryCollector();
        GeometryInfo geometryInfo = space.getGeometryInfo();
        for (int lod : geometryInfo.getLods()) {
            collector.lod = lod;
            for (GeometryProperty property : geometryInfo.getGeometries(lod)) {
                collector.visit(property);
            }
        }
    }

    private void findUnreferencedBoundaryGeometries(AbstractSpace space) {
        if (space.isSetBoundaries()) {
            for (AbstractSpaceBoundaryProperty property : space.getBoundaries()) {
                if (property.getObject() != null) {
                    AbstractSpaceBoundary boundary = property.getObject();
                    processSemanticSurface(boundary);

                    boundary.accept(new ObjectWalker() {
                        @Override
                        public void visit(AbstractFillingSurface fillingSurface) {
                            processSemanticSurface(fillingSurface);
                        }
                    });
                }
            }
        }
    }

    private void processSemanticSurface(AbstractSpaceBoundary semanticSurface) {
        finder.semanticSurface = semanticSurface;
        GeometryInfo geometryInfo = semanticSurface.getGeometryInfo();
        for (int lod : geometryInfo.getLods()) {
            finder.lod = lod;
            for (GeometryProperty geometryProperty : geometryInfo.getGeometries(lod)) {
                finder.visit(geometryProperty);
            }
        }
    }

    private void addUnreferencedSurfaceGeometries(AbstractSpace space) {
        for (Map.Entry> entry : finder.surfaces.entrySet()) {
            MultiSurface multiSurface = null;

            MultiSurfaceProvider provider = multiSurfaceProviders != null ?
                    multiSurfaceProviders.get(entry.getKey()) :
                    null;

            if (provider != null) {
                multiSurface = getOrSetMultiSurface(provider.get(), provider::set);
            } else {
                switch (entry.getKey()) {
                    case 0:
                        multiSurface = getOrSetMultiSurface(space.getLod0MultiSurface(), space::setLod0MultiSurface);
                        break;
                    case 1:
                        ExtendedSpaceGeometry geometry = new ExtendedSpaceGeometry();
                        multiSurface = getOrSetMultiSurface(geometry.getLod1MultiSurface(), geometry::setLod1MultiSurface);
                        space.addADEProperty(geometry);
                        break;
                    case 2:
                        multiSurface = getOrSetMultiSurface(space.getLod2MultiSurface(), space::setLod2MultiSurface);
                        break;
                    case 3:
                        multiSurface = getOrSetMultiSurface(space.getLod3MultiSurface(), space::setLod3MultiSurface);
                        break;
                }
            }

            if (multiSurface != null) {
                for (AbstractSurface surface : entry.getValue()) {
                    SurfaceProperty property = decorate(new SurfaceProperty());
                    property.setReferencedObject(surface, false);
                    multiSurface.getSurfaceMember().add(property);
                }
            }
        }
    }

    private void addUnreferencedCurveGeometries(AbstractSpace space) {
        for (Map.Entry> entry : finder.curves.entrySet()) {
            MultiCurve multiCurve;
            MultiCurveProvider provider = multiCurveProviders != null ?
                    multiCurveProviders.get(entry.getKey()) :
                    null;

            if (provider != null) {
                multiCurve = getOrSetMultiCurve(provider.get(), provider::set);
            } else {
                multiCurve = switch (entry.getKey()) {
                    case 0 -> getOrSetMultiCurve(space.getLod0MultiCurve(), space::setLod0MultiCurve);
                    case 2 -> getOrSetMultiCurve(space.getLod2MultiCurve(), space::setLod2MultiCurve);
                    case 3 -> getOrSetMultiCurve(space.getLod3MultiCurve(), space::setLod3MultiCurve);
                    default -> null;
                };
            }

            if (multiCurve != null) {
                for (AbstractCurve curve : entry.getValue()) {
                    CurveProperty property = decorate(new CurveProperty());
                    property.setReferencedObject(curve, false);
                    multiCurve.getCurveMember().add(property);
                }
            }
        }
    }

    private MultiSurface getOrSetMultiSurface(MultiSurfaceProperty property, Consumer consumer) {
        if (property == null || property.getObject() == null) {
            property = decorate(new MultiSurfaceProperty(new MultiSurface()));
            consumer.accept(property);
        }

        return property.getObject();
    }

    private MultiCurve getOrSetMultiCurve(MultiCurveProperty property, Consumer consumer) {
        if (property == null || property.getObject() == null) {
            property = decorate(new MultiCurveProperty(new MultiCurve()));
            consumer.accept(property);
        }

        return property.getObject();
    }

    private > T decorate(T property) {
        property.getLocalProperties().set(CityJSONSerializerHelper.TEMPORARY_OBJECT, true);
        return property;
    }

    private  T decorate(T geometry, AbstractSpaceBoundary semanticSurface) {
        geometry.getLocalProperties().set(CityJSONSerializerHelper.SEMANTIC_SURFACE, semanticSurface);
        return geometry;
    }

    public void removeTemporaryInformation(AbstractSpace space) {
        for (int lod = 0; lod < 4; lod++) {
            switch (lod) {
                case 0:
                    removeTemporaryGeometries(space.getLod0MultiSurface(), space::setLod0MultiSurface);
                    removeTemporaryGeometries(space.getLod0MultiCurve(), space::setLod0MultiCurve);
                    break;
                case 1:
                    if (space.hasADEProperties()) {
                        space.getADEProperties().removeIf(ExtendedSpaceGeometry.class::isInstance);
                        if (!space.hasADEProperties()) {
                            space.setADEProperties(null);
                        }
                    }
                    break;
                case 2:
                    removeTemporaryGeometries(space.getLod2MultiSurface(), space::setLod2MultiSurface);
                    removeTemporaryGeometries(space.getLod2MultiCurve(), space::setLod2MultiCurve);
                    break;
                case 3:
                    removeTemporaryGeometries(space.getLod3MultiSurface(), space::setLod3MultiSurface);
                    removeTemporaryGeometries(space.getLod3MultiCurve(), space::setLod3MultiCurve);
                    break;
            }
        }

        if (multiSurfaceProviders != null) {
            for (MultiSurfaceProvider provider : multiSurfaceProviders.values()) {
                removeTemporaryGeometries(provider.get(), provider::set);
            }
        }

        if (multiCurveProviders != null) {
            for (MultiCurveProvider provider : multiCurveProviders.values()) {
                removeTemporaryGeometries(provider.get(), provider::set);
            }
        }

        for (Map geometries : finder.spaceGeometries.values()) {
            removeTemporarySemanticSurfaces(geometries.values());
        }

        for (Collection surfaces : finder.surfaces.values()) {
            removeTemporarySemanticSurfaces(surfaces);
        }

        for (Collection curves : finder.curves.values()) {
            removeTemporarySemanticSurfaces(curves);
        }
    }

    private void removeTemporaryGeometries(MultiSurfaceProperty property, Consumer consumer) {
        if (property != null) {
            if (isTemporaryGeometry(property)) {
                consumer.accept(null);
            } else if (property.getObject() != null && property.getObject().isSetSurfaceMember()) {
                property.getObject().getSurfaceMember().removeIf(this::isTemporaryGeometry);
            }
        }
    }

    private void removeTemporaryGeometries(MultiCurveProperty property, Consumer consumer) {
        if (property != null) {
            if (isTemporaryGeometry(property)) {
                consumer.accept(null);
            } else if (property.getObject() != null && property.getObject().isSetCurveMember()) {
                property.getObject().getCurveMember().removeIf(this::isTemporaryGeometry);
            }
        }
    }

    private boolean isTemporaryGeometry(GeometryProperty property) {
        return property != null
                && property.hasLocalProperties()
                && property.getLocalProperties().contains(CityJSONSerializerHelper.TEMPORARY_OBJECT);
    }

    private void removeTemporarySemanticSurfaces(Collection geometries) {
        for (AbstractGeometry geometry : geometries) {
            if (geometry.hasLocalProperties()) {
                geometry.getLocalProperties().remove(CityJSONSerializerHelper.SEMANTIC_SURFACE);
                if (geometry.getLocalProperties().isEmpty()) {
                    geometry.setLocalProperties(null);
                }
            }
        }
    }

    private final class SpaceGeometryCollector extends ObjectWalker {
        int lod;

        @Override
        public void visit(AbstractGeometry geometry) {
            if (geometry.getId() != null) {
                finder.spaceGeometries.computeIfAbsent(geometry.getId(), v -> new HashMap<>()).put(lod, geometry);

                AbstractFeature parent = geometry.getParent(AbstractFeature.class);
                if (parent instanceof AbstractSpaceBoundary boundary) {
                    decorate(geometry, boundary);
                }
            }
        }
    }

    private final class BoundaryGeometryFinder extends ObjectWalker {
        final Map> spaceGeometries = new HashMap<>();
        final Map> surfaces = new HashMap<>();
        final Map> curves = new HashMap<>();

        AbstractSpaceBoundary semanticSurface;
        int lod;

        @Override
        public void visit(Polygon polygon) {
            addSurface(polygon);
        }

        @Override
        public void visit(AbstractSimplePolygon simplePolygon) {
            addSurface(simplePolygon);
        }

        @Override
        public void visit(OrientableSurface orientableSurface) {
            addSurface(orientableSurface);
        }

        @Override
        public void visit(Surface surface) {
            addSurface(surface);
        }

        @Override
        public void visit(LineString lineString) {
            addCurve(lineString);
        }

        @Override
        public void visit(OrientableCurve orientableCurve) {
            addCurve(orientableCurve);
        }

        @Override
        public void visit(Curve curve) {
            addCurve(curve);
        }

        private void addSurface(AbstractSurface surface) {
            if (isNotReferenced(surface)) {
                surfaces.computeIfAbsent(lod, v -> new ArrayList<>()).add(decorate(surface, semanticSurface));
            }
        }

        private void addCurve(AbstractCurve curve) {
            if (isNotReferenced(curve)) {
                curves.computeIfAbsent(lod, v -> new ArrayList<>()).add(decorate(curve, semanticSurface));
            }
        }

        private boolean isNotReferenced(Child child) {
            do {
                if (child instanceof AbstractGeometry geometry) {
                    if (geometry.getId() != null) {
                        AbstractGeometry spaceGeometry = spaceGeometries.getOrDefault(geometry.getId(), Collections.emptyMap()).get(lod);
                        if (spaceGeometry != null) {
                            decorate(spaceGeometry, semanticSurface);
                            return false;
                        }
                    }
                }
            } while ((child = child.getParent()) != null && !(child instanceof AbstractFeature));

            return true;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy