Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix global-to-local appearance converter #298

Merged
merged 1 commit into from
Jan 29, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}
}
Loading