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

org.citygml4j.tools.appmover.GlobalAppMover Maven / Gradle / Ivy

/*
 * citygml-tools - Collection of tools for processing CityGML files
 * https://github.com/citygml4j/citygml-tools
 *
 * citygml-tools is part of the citygml4j project
 *
 * Copyright 2018-2022 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.tools.appmover;

import org.citygml4j.builder.copy.CopyBuilder;
import org.citygml4j.builder.copy.ShallowCopyBuilder;
import org.citygml4j.model.citygml.appearance.*;
import org.citygml4j.model.citygml.core.AbstractCityObject;
import org.citygml4j.model.citygml.core.CityModel;
import org.citygml4j.model.citygml.core.ImplicitGeometry;
import org.citygml4j.model.gml.feature.AbstractFeature;
import org.citygml4j.model.gml.feature.FeatureProperty;
import org.citygml4j.model.gml.geometry.AbstractGeometry;
import org.citygml4j.util.child.ChildInfo;
import org.citygml4j.util.gmlid.DefaultGMLIdManager;
import org.citygml4j.util.walker.FeatureWalker;
import org.citygml4j.util.walker.GMLWalker;
import org.citygml4j.util.xlink.XLinkResolver;

import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

public class GlobalAppMover {
    private final ReentrantLock lock = new ReentrantLock();
    private final Map> targets;
    private final CopyBuilder copyBuilder;
    private final String ID = "id";
    private final CityModel cityModel;
    private final ResultStatistic resultStatistic;

    private LocalAppTarget localAppTarget = LocalAppTarget.TOP_LEVEL_FEATURE;

    public GlobalAppMover(List appearances) {
        targets = new HashMap<>();
        copyBuilder = new ShallowCopyBuilder();
        cityModel = new CityModel();
        resultStatistic = new ResultStatistic();

        initialize(appearances);
    }

    public GlobalAppMover(Appearance... appearances) {
        this(Arrays.asList(appearances));
    }

    public AbstractCityObject moveGlobalApps(AbstractCityObject cityObject) {
        ChildInfo childInfo = new ChildInfo();

        cityObject.accept(new GMLWalker() {
            @Override
            public void visit(AbstractGeometry geometry) {
                if (geometry.isSetId()) {
                    List surfaceDatas = targets.get(geometry.getId());
                    if (surfaceDatas == null)
                        return;

                    AbstractFeature target;
                    if (childInfo.getParentCityGML(geometry, ImplicitGeometry.class) != null)
                        target = cityModel;
                    else
                        target = localAppTarget == LocalAppTarget.NESTED_FEATURE ?
                                childInfo.getParentCityObject(geometry) : cityObject;

                    for (AbstractSurfaceData surfaceData : surfaceDatas) {
                        Appearance globalApp = childInfo.getParentFeature(surfaceData, Appearance.class);
                        AbstractSurfaceData copy;

                        // changes to the city model have to be synchronized
                        if (target == cityModel)
                            lock.lock();

                        try {
                            Appearance localApp = getOrCreateAppearance(target, globalApp);
                            copy = getOrCreateSurfaceData(localApp, surfaceData);

                            if (copy instanceof ParameterizedTexture) {
                                ParameterizedTexture texture = (ParameterizedTexture) surfaceData;
                                for (TextureAssociation association : texture.getTarget()) {
                                    if (association.isSetUri() &&
                                            clipGMLId(association.getUri()).equals(geometry.getId())) {
                                        ((ParameterizedTexture) copy).addTarget(association);
                                        break;
                                    }
                                }
                            } else if (copy instanceof GeoreferencedTexture)
                                ((GeoreferencedTexture) copy).addTarget("#" + geometry.getId());
                            else if (copy instanceof X3DMaterial)
                                ((X3DMaterial) copy).addTarget("#" + geometry.getId());
                        } finally {
                            if (lock.isHeldByCurrentThread())
                                lock.unlock();
                        }
                    }
                }

                super.visit(geometry);
            }
        });

        resultStatistic.increment(AbstractCityObject.class);
        return cityObject;
    }

    public LocalAppTarget getLocalAppTarget() {
        return localAppTarget;
    }

    public void setLocalAppTarget(LocalAppTarget localAppTarget) {
        this.localAppTarget = localAppTarget;
    }

    public boolean hasRemainingGlobalApps() {
        return cityModel.isSetAppearanceMember();
    }

    public List getRemainingGlobalApps() {
        return cityModel.getAppearanceMember().stream()
                .map(AppearanceProperty::getAppearance)
                .collect(Collectors.toList());
    }

    public ResultStatistic getResultStatistic() {
        return resultStatistic;
    }

    private Appearance getOrCreateAppearance(AbstractFeature feature, Appearance appearance) {
        boolean isCityObject = feature instanceof AbstractCityObject;
        List properties = isCityObject ?
                ((AbstractCityObject) feature).getAppearance() : ((CityModel) feature).getAppearanceMember();

        for (AppearanceProperty property : properties) {
            if (property.isSetAppearance()
                    && appearance.getLocalProperty(ID) == property.getAppearance().getLocalProperty(ID))
                return property.getAppearance();
        }

        Appearance copy = (Appearance) appearance.copy(copyBuilder);
        copy.setId(DefaultGMLIdManager.getInstance().generateUUID());
        copy.setSurfaceDataMember(new ArrayList<>());
        copy.setLocalProperty(ID, appearance.getLocalProperty(ID));

        if (isCityObject)
            ((AbstractCityObject) feature).addAppearance(new AppearanceProperty(copy));
        else
            ((CityModel) feature).addAppearanceMember(new AppearanceMember(copy));

        resultStatistic.increment(Appearance.class);
        return copy;
    }

    private AbstractSurfaceData getOrCreateSurfaceData(Appearance appearance, AbstractSurfaceData surfaceData) {
        String id = (String) surfaceData.getLocalProperty(ID);
        for (SurfaceDataProperty property : appearance.getSurfaceDataMember()) {
            if (property.isSetSurfaceData()
                    && surfaceData.getLocalProperty(ID) == property.getSurfaceData().getLocalProperty(ID))
                return property.getSurfaceData();
        }

        AbstractSurfaceData copy = (AbstractSurfaceData) surfaceData.copy(copyBuilder);
        appearance.addSurfaceDataMember(new SurfaceDataProperty(copy));

        copy.setId(DefaultGMLIdManager.getInstance().generateUUID());
        copy.setLocalProperty(ID, id);

        if (surfaceData instanceof ParameterizedTexture)
            ((ParameterizedTexture) copy).setTarget(new ArrayList<>());
        else if (surfaceData instanceof GeoreferencedTexture)
            ((GeoreferencedTexture) copy).setTarget(new ArrayList<>());
        else if (surfaceData instanceof X3DMaterial)
            ((X3DMaterial) copy).setTarget(new ArrayList<>());

        resultStatistic.increment(surfaceData.getClass());
        return copy;
    }

    private void initialize(List appearances) {
        // resolve appearance xlinks
        XLinkResolver xLinkResolver = new XLinkResolver();
        for (Appearance appearance : appearances) {
            appearance.accept(new FeatureWalker() {
                @Override
                public  void visit(FeatureProperty property) {
                    if (property.isSetHref() && !property.isSetFeature()) {
                        for (Appearance target : appearances) {
                            if (target == appearance)
                                continue;

                            AbstractFeature feature = xLinkResolver.getFeature(property.getHref(), target);
                            if (property.getAssociableClass().isInstance(feature)) {
                                T copy = property.getAssociableClass().cast(feature.copy(copyBuilder));
                                property.setFeature(copy);
                                property.unsetHref();
                            }
                        }
                    }

                    super.visit(property);
                }
            });
        }

        // build targets map
        for (Appearance appearance : appearances) {
            appearance.setLocalProperty(ID, DefaultGMLIdManager.getInstance().generateUUID());

            appearance.accept(new FeatureWalker() {
                @Override
                public void visit(AbstractSurfaceData surfaceData) {
                    surfaceData.setLocalProperty(ID, DefaultGMLIdManager.getInstance().generateUUID());
                }

                @Override
                public void visit(ParameterizedTexture texture) {
                    for (TextureAssociation textureAssociation : texture.getTarget()) {
                        List surfaceDatas = targets.computeIfAbsent(
                                clipGMLId(textureAssociation.getUri()),
                                v -> new ArrayList<>());

                        surfaceDatas.add(texture);
                    }

                    super.visit(texture);
                }

                @Override
                public void visit(GeoreferencedTexture texture) {
                    for (String target : texture.getTarget()) {
                        List surfaceDatas = targets.computeIfAbsent(
                                clipGMLId(target),
                                v -> new ArrayList<>());

                        surfaceDatas.add(texture);
                    }

                    super.visit(texture);
                }

                @Override
                public void visit(X3DMaterial material) {
                    for (String target : material.getTarget()) {
                        List surfaceDatas = targets.computeIfAbsent(
                                clipGMLId(target),
                                v -> new ArrayList<>());

                        surfaceDatas.add(material);
                    }

                    super.visit(material);
                }
            });
        }
    }

    private String clipGMLId(String target) {
        return target.replaceAll("^.*?#+?", "");
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy