Skip to content

Commit

Permalink
Merge pull request #298 from 3dcitydb/fix-global-to-local-appearances
Browse files Browse the repository at this point in the history
Fix global-to-local appearance converter
  • Loading branch information
clausnagel authored Jan 29, 2024
2 parents cfbfbcb + 63ecafc commit 2f2ae7d
Showing 1 changed file with 19 additions and 82 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,18 @@

import org.citydb.config.Config;
import org.citydb.config.project.exporter.OutputFormat;
import org.citydb.core.database.schema.mapping.MappingConstants;
import org.citydb.core.database.schema.TableEnum;
import org.citydb.core.operation.exporter.CityGMLExportException;
import org.citydb.core.operation.exporter.util.AppearanceRemover;
import org.citydb.core.query.Query;
import org.citydb.core.query.builder.QueryBuildException;
import org.citydb.core.query.builder.sql.AppearanceFilterBuilder;
import org.citydb.core.util.CoreConstants;
import org.citydb.sqlbuilder.expression.LiteralList;
import org.citydb.sqlbuilder.expression.LiteralSelectExpression;
import org.citydb.sqlbuilder.expression.PlaceHolder;
import org.citydb.sqlbuilder.schema.Table;
import org.citydb.sqlbuilder.select.PredicateToken;
import org.citydb.sqlbuilder.select.Select;
import org.citydb.sqlbuilder.select.join.JoinFactory;
import org.citydb.sqlbuilder.select.operator.comparison.ComparisonFactory;
import org.citydb.sqlbuilder.select.operator.comparison.ComparisonName;
import org.citygml4j.builder.copy.CopyBuilder;
import org.citygml4j.builder.copy.DeepCopyBuilder;
import org.citygml4j.model.citygml.appearance.Appearance;
Expand All @@ -64,16 +60,10 @@

public class DBGlobalToLocalAppearance extends AbstractAppearanceExporter {
private final Connection connection;
private final PreparedStatement psBulk;
private final PreparedStatement psSelect;
private final Select appearanceQuery;
private final Table textureParam;

private final boolean replaceIds;
private final boolean handleImplicitGeometries;
private final AppearanceRemover appearanceRemover;
private final Map<Long, AbstractCityObject> batches;
private final int batchSize;
private final Table textureParam;

private CopyBuilder copyBuilder;
private ChildInfo childInfo;
Expand All @@ -95,43 +85,29 @@ public DBGlobalToLocalAppearance(Connection connection, Query query, CityGMLExpo
}

appearanceRemover = new AppearanceRemover();
batches = new LinkedHashMap<>();
batchSize = exporter.getFeatureBatchSize();
String schema = exporter.getDatabaseAdapter().getConnectionDetails().getSchema();

Table appearance = new Table("appearance", schema);
Table appearToSurfaceData = new Table("appear_to_surface_data", schema);
Table surfaceData = new Table("surface_data", schema);
textureParam = new Table("textureparam", schema);

appearanceQuery = new Select().setDistinct(true)
.addProjection(appearance.getColumn(MappingConstants.ID))
.addJoin(JoinFactory.inner(appearToSurfaceData, "appearance_id", ComparisonName.EQUAL_TO, appearance.getColumn(MappingConstants.ID)))
.addJoin(JoinFactory.inner(surfaceData, MappingConstants.ID, ComparisonName.EQUAL_TO, appearToSurfaceData.getColumn("surface_data_id")))
.addJoin(JoinFactory.inner(textureParam, "surface_data_id", ComparisonName.EQUAL_TO, surfaceData.getColumn(MappingConstants.ID)))
.addSelection(ComparisonFactory.isNull(appearance.getColumn("cityobject_id")));
select = new Select(select).setDistinct(true)
.addSelection(ComparisonFactory.isNull(table.getColumn("cityobject_id")));

textureParam = select.getInvolvedTables().stream()
.filter(table -> TableEnum.TEXTUREPARAM.getName().equals(table.getName()))
.findFirst().orElse(null);
if (textureParam == null) {
throw new CityGMLExportException("Failed to build global-to-local appearance query.");
}

// add appearance theme filter
if (query.isSetAppearanceFilter()) {
try {
PredicateToken predicate = new AppearanceFilterBuilder(exporter.getDatabaseAdapter())
.buildAppearanceFilter(query.getAppearanceFilter(), appearance.getColumn("theme"));
appearanceQuery.addSelection(predicate);
.buildAppearanceFilter(query.getAppearanceFilter(), table.getColumn("theme"));
select.addSelection(predicate);
} catch (QueryBuildException e) {
throw new CityGMLExportException("Failed to build appearance filter.", e);
}
}

String placeHolders = String.join(",", Collections.nCopies(batchSize, "?"));
psBulk = connection.prepareStatement(new Select(select)
.addSelection(ComparisonFactory.in(table.getColumn("id"), new LiteralSelectExpression(placeHolders))).toString());

psSelect = connection.prepareStatement(new Select(select)
.addSelection(ComparisonFactory.equalTo(table.getColumn("id"), new PlaceHolder<>())).toString());
}

protected Collection<Appearance> doExport(AbstractCityObject cityObject) throws CityGMLExportException, SQLException {
List<Appearance> unconverted = new ArrayList<>();
Set<Long> surfaceGeometryIds = new HashSet<>();
Map<GeometryType, Set<String>> targets = new EnumMap<>(GeometryType.class);

Expand All @@ -157,60 +133,23 @@ public void visit(AbstractGeometry geometry) {
});

if (!surfaceGeometryIds.isEmpty()) {
Select select = new Select(appearanceQuery)
Select select = new Select(this.select)
.addSelection(ComparisonFactory.in(textureParam.getColumn("surface_geometry_id"),
new LiteralList(surfaceGeometryIds.toArray(new Long[0]))));

try (PreparedStatement stmt = exporter.getDatabaseAdapter().getSQLAdapter().prepareStatement(select, connection);
ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
batches.put(rs.getLong(1), cityObject);
if (batches.size() == batchSize) {
unconverted.addAll(executeBatch(targets));
}
}

if (!batches.isEmpty()) {
unconverted.addAll(executeBatch(targets));
}
}
}

return unconverted;
}

private List<Appearance> executeBatch(Map<GeometryType, Set<String>> targets) throws CityGMLExportException, SQLException {
if (!batches.isEmpty()) {
try {
Map<Long, Appearance> appearances;
if (batches.size() == 1) {
psSelect.setLong(1, batches.entrySet().iterator().next().getKey());
try (ResultSet rs = psSelect.executeQuery()) {
appearances = doExport(rs);
}
} else {
int i = 1;
Long[] ids = batches.keySet().toArray(new Long[0]);
for (int j = 0; j < batchSize; j++) {
psBulk.setLong(i + j, j < ids.length ? ids[j] : 0);
}

try (ResultSet rs = psBulk.executeQuery()) {
appearances = doExport(rs);
}
}

Map<Long, Appearance> appearances = doExport(rs);
if (!appearances.isEmpty()) {
return postprocess(appearances, targets);
return postprocess(appearances, targets, cityObject);
}
} finally {
batches.clear();
}
}

return Collections.emptyList();
}

private List<Appearance> postprocess(Map<Long, Appearance> appearances, Map<GeometryType, Set<String>> targets) {
private List<Appearance> postprocess(Map<Long, Appearance> appearances, Map<GeometryType, Set<String>> targets, AbstractCityObject cityObject) {
List<Appearance> globalAppearances = new ArrayList<>();
Set<String> implicitGeometryTargets = targets.get(GeometryType.IMPLICIT_GEOMETRY);
if (implicitGeometryTargets != null) {
Expand All @@ -228,7 +167,6 @@ private List<Appearance> postprocess(Map<Long, Appearance> appearances, Map<Geom
entry.getValue().accept(exporter.getIdReplacer());
}

AbstractCityObject cityObject = batches.get(entry.getKey());
cityObject.addAppearance(new AppearanceProperty(entry.getValue()));
}
}
Expand All @@ -242,7 +180,6 @@ private List<Appearance> postprocess(Map<Long, Appearance> appearances, Map<Geom

@Override
public void close() throws SQLException {
psBulk.close();
psSelect.close();
// nothing to do
}
}

0 comments on commit 2f2ae7d

Please sign in to comment.