From 07874a95c129cfe6c8cb4ae5ae34d769b0a64c8f Mon Sep 17 00:00:00 2001 From: Claus Nagel Date: Wed, 30 Oct 2024 09:18:09 +0100 Subject: [PATCH] added tiling module and API --- citydb-tiling/build.gradle | 5 + citydb-tiling/src/main/java/module-info.java | 9 + .../src/main/java/org/citydb/tiling/Tile.java | 91 ++++++++++ .../java/org/citydb/tiling/TileIterator.java | 74 +++++++++ .../java/org/citydb/tiling/TileMatrix.java | 155 ++++++++++++++++++ .../main/java/org/citydb/tiling/Tiling.java | 106 ++++++++++++ .../org/citydb/tiling/TilingException.java | 41 +++++ .../java/org/citydb/tiling/TilingScheme.java | 43 +++++ .../citydb/tiling/encoding/ExtentReader.java | 65 ++++++++ .../citydb/tiling/encoding/ExtentWriter.java | 58 +++++++ .../citydb/tiling/encoding/PointReader.java | 62 +++++++ .../citydb/tiling/encoding/PointWriter.java | 55 +++++++ .../citydb/tiling/encoding/SrsUnitReader.java | 37 +++++ .../org/citydb/tiling/options/Dimension.java | 61 +++++++ .../tiling/options/DimensionScheme.java | 152 +++++++++++++++++ .../citydb/tiling/options/MatrixScheme.java | 78 +++++++++ .../tiling/options/TileMatrixOrigin.java | 52 ++++++ settings.gradle | 3 +- 18 files changed, 1146 insertions(+), 1 deletion(-) create mode 100644 citydb-tiling/build.gradle create mode 100644 citydb-tiling/src/main/java/module-info.java create mode 100644 citydb-tiling/src/main/java/org/citydb/tiling/Tile.java create mode 100644 citydb-tiling/src/main/java/org/citydb/tiling/TileIterator.java create mode 100644 citydb-tiling/src/main/java/org/citydb/tiling/TileMatrix.java create mode 100644 citydb-tiling/src/main/java/org/citydb/tiling/Tiling.java create mode 100644 citydb-tiling/src/main/java/org/citydb/tiling/TilingException.java create mode 100644 citydb-tiling/src/main/java/org/citydb/tiling/TilingScheme.java create mode 100644 citydb-tiling/src/main/java/org/citydb/tiling/encoding/ExtentReader.java create mode 100644 citydb-tiling/src/main/java/org/citydb/tiling/encoding/ExtentWriter.java create mode 100644 citydb-tiling/src/main/java/org/citydb/tiling/encoding/PointReader.java create mode 100644 citydb-tiling/src/main/java/org/citydb/tiling/encoding/PointWriter.java create mode 100644 citydb-tiling/src/main/java/org/citydb/tiling/encoding/SrsUnitReader.java create mode 100644 citydb-tiling/src/main/java/org/citydb/tiling/options/Dimension.java create mode 100644 citydb-tiling/src/main/java/org/citydb/tiling/options/DimensionScheme.java create mode 100644 citydb-tiling/src/main/java/org/citydb/tiling/options/MatrixScheme.java create mode 100644 citydb-tiling/src/main/java/org/citydb/tiling/options/TileMatrixOrigin.java diff --git a/citydb-tiling/build.gradle b/citydb-tiling/build.gradle new file mode 100644 index 00000000..4737a113 --- /dev/null +++ b/citydb-tiling/build.gradle @@ -0,0 +1,5 @@ +dependencies { + api project(':citydb-database') + api project(':citydb-model') + implementation project(':citydb-config') +} \ No newline at end of file diff --git a/citydb-tiling/src/main/java/module-info.java b/citydb-tiling/src/main/java/module-info.java new file mode 100644 index 00000000..0a0ced9c --- /dev/null +++ b/citydb-tiling/src/main/java/module-info.java @@ -0,0 +1,9 @@ +module org.citydb.tiling { + requires org.citydb.config; + requires transitive org.citydb.database; + requires transitive org.citydb.model; + + exports org.citydb.tiling; + exports org.citydb.tiling.options; + exports org.citydb.tiling.encoding; +} \ No newline at end of file diff --git a/citydb-tiling/src/main/java/org/citydb/tiling/Tile.java b/citydb-tiling/src/main/java/org/citydb/tiling/Tile.java new file mode 100644 index 00000000..a782817d --- /dev/null +++ b/citydb-tiling/src/main/java/org/citydb/tiling/Tile.java @@ -0,0 +1,91 @@ +/* + * 3D City Database - The Open Source CityGML Database + * https://www.3dcitydb.org/ + * + * Copyright 2013 - 2024 + * Chair of Geoinformatics + * Technical University of Munich, Germany + * https://www.lrg.tum.de/gis/ + * + * The 3D City Database is jointly developed with the following + * cooperation partners: + * + * Virtual City Systems, Berlin + * M.O.S.S. Computer Grafik Systeme GmbH, Taufkirchen + * + * 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.citydb.tiling; + +import org.citydb.database.adapter.DatabaseAdapter; +import org.citydb.database.geometry.GeometryException; +import org.citydb.database.srs.SpatialReference; +import org.citydb.database.srs.SrsException; +import org.citydb.model.geometry.Envelope; +import org.citydb.model.geometry.Point; + +import java.sql.SQLException; + +public class Tile { + private final Envelope extent; + private final int column; + private final int row; + private final DatabaseAdapter adapter; + + Tile(Envelope extent, int column, int row, DatabaseAdapter adapter) { + this.extent = extent; + this.column = column; + this.row = row; + this.adapter = adapter; + } + + public Envelope getExtent() { + return extent; + } + + public int getColumn() { + return column; + } + + public int getRow() { + return row; + } + + public boolean isOnTile(Envelope extent) throws TilingException { + return extent != null && isOnTile(Point.of(extent.getCenter()) + .setSRID(extent.getSRID().orElse(null)) + .setSrsIdentifier(extent.getSrsIdentifier().orElse(null))); + } + + public boolean isOnTile(Point point) throws TilingException { + if (point != null) { + try { + SpatialReference reference = adapter.getGeometryAdapter().getSpatialReference(extent) + .orElse(adapter.getDatabaseMetadata().getSpatialReference()); + if (reference.getSRID() != adapter.getDatabaseMetadata().getSpatialReference().getSRID()) { + point = adapter.getGeometryAdapter().transform(point); + } + } catch (GeometryException | SrsException | SQLException e) { + throw new TilingException("Failed to transform the point geometry to the database SRS.", e); + } + + return point.getCoordinate().getX() > extent.getLowerCorner().getX() + && point.getCoordinate().getX() <= extent.getUpperCorner().getX() + && point.getCoordinate().getY() > extent.getLowerCorner().getY() + && point.getCoordinate().getY() <= extent.getUpperCorner().getY(); + } else { + return false; + } + } +} diff --git a/citydb-tiling/src/main/java/org/citydb/tiling/TileIterator.java b/citydb-tiling/src/main/java/org/citydb/tiling/TileIterator.java new file mode 100644 index 00000000..7f64b172 --- /dev/null +++ b/citydb-tiling/src/main/java/org/citydb/tiling/TileIterator.java @@ -0,0 +1,74 @@ +/* + * 3D City Database - The Open Source CityGML Database + * https://www.3dcitydb.org/ + * + * Copyright 2013 - 2024 + * Chair of Geoinformatics + * Technical University of Munich, Germany + * https://www.lrg.tum.de/gis/ + * + * The 3D City Database is jointly developed with the following + * cooperation partners: + * + * Virtual City Systems, Berlin + * M.O.S.S. Computer Grafik Systeme GmbH, Taufkirchen + * + * 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.citydb.tiling; + +import org.citydb.tiling.options.TileMatrixOrigin; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +public class TileIterator implements Iterator { + private final TileMatrix matrix; + private final TileMatrixOrigin origin; + private int column = -1; + private int row; + private boolean hasNext; + + TileIterator(TileMatrix matrix, TileMatrixOrigin origin) { + this.matrix = matrix; + this.origin = origin; + } + + @Override + public boolean hasNext() { + if (!hasNext) { + if (++column == matrix.getColumns()) { + column = 0; + row++; + } + + hasNext = row < matrix.getRows(); + } + + return hasNext; + } + + @Override + public Tile next() { + try { + if (hasNext()) { + return matrix.getTileAt(column, row, origin); + } else { + throw new NoSuchElementException(); + } + } finally { + hasNext = false; + } + } +} diff --git a/citydb-tiling/src/main/java/org/citydb/tiling/TileMatrix.java b/citydb-tiling/src/main/java/org/citydb/tiling/TileMatrix.java new file mode 100644 index 00000000..a2672110 --- /dev/null +++ b/citydb-tiling/src/main/java/org/citydb/tiling/TileMatrix.java @@ -0,0 +1,155 @@ +/* + * 3D City Database - The Open Source CityGML Database + * https://www.3dcitydb.org/ + * + * Copyright 2013 - 2024 + * Chair of Geoinformatics + * Technical University of Munich, Germany + * https://www.lrg.tum.de/gis/ + * + * The 3D City Database is jointly developed with the following + * cooperation partners: + * + * Virtual City Systems, Berlin + * M.O.S.S. Computer Grafik Systeme GmbH, Taufkirchen + * + * 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.citydb.tiling; + +import org.citydb.core.concurrent.LazyInitializer; +import org.citydb.database.adapter.DatabaseAdapter; +import org.citydb.model.geometry.Coordinate; +import org.citydb.model.geometry.Envelope; +import org.citydb.tiling.options.TileMatrixOrigin; +import org.geotools.referencing.CRS; + +public class TileMatrix { + private final Envelope extent; + private final DatabaseAdapter adapter; + private final int columns; + private final int rows; + private final double tileWidth; + private final double tileHeight; + private final double[] columnOffsets; + private final double[] rowOffsets; + private final LazyInitializer swapAxes; + private TileMatrixOrigin tileMatrixOrigin = TileMatrixOrigin.TOP_LEFT; + + TileMatrix(Coordinate lowerCorner, Coordinate upperCorner, int columns, int rows, double tileWidth, + double tileHeight, DatabaseAdapter adapter) { + this.columns = columns > 0 ? columns : 1; + this.rows = rows > 0 ? rows : 1; + this.tileWidth = tileWidth; + this.tileHeight = tileHeight; + this.adapter = adapter; + + columnOffsets = computeOffsets(columns, tileWidth); + rowOffsets = computeOffsets(rows, tileHeight); + extent = Envelope.of(lowerCorner, upperCorner) + .setSRID(adapter.getDatabaseMetadata().getSpatialReference().getSRID()); + swapAxes = LazyInitializer.of(() -> adapter.getDatabaseMetadata().getSpatialReference().getDefinition() + .map(crs -> CRS.AxisOrder.NORTH_EAST.equals(CRS.getAxisOrder(crs))) + .orElse(false)); + } + + public Envelope getExtent() { + return extent; + } + + public int size() { + return columns * rows; + } + + public int getColumns() { + return columns; + } + + public int getRows() { + return rows; + } + + public double getTileWidth() { + return tileWidth; + } + + public double getTileHeight() { + return tileHeight; + } + + public TileMatrixOrigin getOrigin() { + return tileMatrixOrigin; + } + + TileMatrix setTileMatrixOrigin(TileMatrixOrigin tileMatrixOrigin) { + if (tileMatrixOrigin != null) { + this.tileMatrixOrigin = tileMatrixOrigin; + } + + return this; + } + + public TileIterator getTileIterator() { + return new TileIterator(this, getOrigin()); + } + + public TileIterator getTileIterator(TileMatrixOrigin origin) { + return new TileIterator(this, origin); + } + + public Tile getTileAt(int column, int row) { + return getTileAt(column, row, getOrigin()); + } + + public Tile getTileAt(int column, int row, TileMatrixOrigin origin) { + if (column < 0 || column >= columns || row < 0 || row >= rows) { + throw new IndexOutOfBoundsException("Tile index (" + column + "," + row + ") is out of bounds."); + } + + double minX, minY, maxX, maxY; + if (swapAxes.get()) { + minX = extent.getUpperCorner().getY(); + minY = extent.getUpperCorner().getX(); + maxX = extent.getLowerCorner().getY(); + maxY = extent.getLowerCorner().getX(); + } else { + minX = extent.getLowerCorner().getX(); + minY = extent.getLowerCorner().getY(); + maxX = extent.getUpperCorner().getX(); + maxY = extent.getUpperCorner().getY(); + } + + int rowIndex = origin == TileMatrixOrigin.BOTTOM_LEFT ? rows - row - 1 : row; + Coordinate lowerCorner = Coordinate.of( + minX + columnOffsets[column], + rowIndex == rows - 1 ? minY : maxY - rowOffsets[rowIndex + 1]); + Coordinate upperCorner = Coordinate.of( + column == columns - 1 ? maxX : minX + columnOffsets[column + 1], + maxY - rowOffsets[rowIndex]); + + return new Tile(Envelope.of(lowerCorner, upperCorner) + .setSRID(adapter.getDatabaseMetadata().getSpatialReference().getSRID()), + column, row, adapter); + } + + private double[] computeOffsets(int size, double offset) { + double[] offsets = new double[size]; + offsets[0] = 0; + for (int i = 1; i < offsets.length; i++) { + offsets[i] = offsets[i - 1] + offset; + } + + return offsets; + } +} diff --git a/citydb-tiling/src/main/java/org/citydb/tiling/Tiling.java b/citydb-tiling/src/main/java/org/citydb/tiling/Tiling.java new file mode 100644 index 00000000..a9e7b642 --- /dev/null +++ b/citydb-tiling/src/main/java/org/citydb/tiling/Tiling.java @@ -0,0 +1,106 @@ +/* + * citydb-tool - Command-line tool for the 3D City Database + * https://www.3dcitydb.org/ + * + * Copyright 2022-2024 + * virtualcitysystems GmbH, Germany + * https://vc.systems/ + * + * 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.citydb.tiling; + +import com.alibaba.fastjson2.JSONWriter; +import com.alibaba.fastjson2.annotation.JSONField; +import org.citydb.config.SerializableConfig; +import org.citydb.database.adapter.DatabaseAdapter; +import org.citydb.database.geometry.GeometryException; +import org.citydb.database.srs.SpatialReference; +import org.citydb.database.srs.SrsException; +import org.citydb.model.geometry.Envelope; +import org.citydb.tiling.encoding.ExtentReader; +import org.citydb.tiling.encoding.ExtentWriter; +import org.citydb.tiling.options.TileMatrixOrigin; + +import java.sql.SQLException; +import java.util.Objects; +import java.util.Optional; + +@SerializableConfig(name = "tiling") +public class Tiling { + @JSONField(serializeUsing = ExtentWriter.class, deserializeUsing = ExtentReader.class) + private Envelope extent; + private TilingScheme scheme; + @JSONField(serializeFeatures = JSONWriter.Feature.WriteEnumUsingToString) + private TileMatrixOrigin tileMatrixOrigin = TileMatrixOrigin.TOP_LEFT; + + public static Tiling newInstance() { + return new Tiling(); + } + + public static Tiling of(Envelope extent, TilingScheme scheme) { + return new Tiling().setExtent(extent).setScheme(scheme); + } + + public Optional getExtent() { + return Optional.ofNullable(extent); + } + + public Tiling setExtent(Envelope extent) { + this.extent = extent; + return this; + } + + public Optional getScheme() { + return Optional.ofNullable(scheme); + } + + public Tiling setScheme(TilingScheme scheme) { + this.scheme = scheme; + return this; + } + + public TileMatrixOrigin getTileMatrixOrigin() { + return tileMatrixOrigin != null ? tileMatrixOrigin : TileMatrixOrigin.TOP_LEFT; + } + + public Tiling setTileMatrixOrigin(TileMatrixOrigin tileMatrixOrigin) { + this.tileMatrixOrigin = tileMatrixOrigin; + return this; + } + + public TileMatrix buildTileMatrix(DatabaseAdapter adapter) throws TilingException { + Objects.requireNonNull(adapter, "The database adapter must not be null."); + + if (extent == null) { + throw new TilingException("No tiling extent specified."); + } else if (scheme == null) { + throw new TilingException("No tiling scheme specified."); + } + + Envelope extent; + try { + SpatialReference reference = adapter.getGeometryAdapter().getSpatialReference(this.extent) + .orElse(adapter.getDatabaseMetadata().getSpatialReference()); + extent = reference.getSRID() != adapter.getDatabaseMetadata().getSpatialReference().getSRID() ? + adapter.getGeometryAdapter().transform(this.extent) : + this.extent; + } catch (GeometryException | SrsException | SQLException e) { + throw new TilingException("Failed to transform the tiling extent to the database SRS.", e); + } + + return scheme.buildTileMatrix(extent, adapter) + .setTileMatrixOrigin(getTileMatrixOrigin()); + } +} diff --git a/citydb-tiling/src/main/java/org/citydb/tiling/TilingException.java b/citydb-tiling/src/main/java/org/citydb/tiling/TilingException.java new file mode 100644 index 00000000..1ee035df --- /dev/null +++ b/citydb-tiling/src/main/java/org/citydb/tiling/TilingException.java @@ -0,0 +1,41 @@ +/* + * citydb-tool - Command-line tool for the 3D City Database + * https://www.3dcitydb.org/ + * + * Copyright 2022-2024 + * virtualcitysystems GmbH, Germany + * https://vc.systems/ + * + * 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.citydb.tiling; + +public class TilingException extends Exception { + + public TilingException() { + super(); + } + + public TilingException(String message) { + super(message); + } + + public TilingException(Throwable cause) { + super(cause); + } + + public TilingException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/citydb-tiling/src/main/java/org/citydb/tiling/TilingScheme.java b/citydb-tiling/src/main/java/org/citydb/tiling/TilingScheme.java new file mode 100644 index 00000000..c54dc7e6 --- /dev/null +++ b/citydb-tiling/src/main/java/org/citydb/tiling/TilingScheme.java @@ -0,0 +1,43 @@ +/* + * citydb-tool - Command-line tool for the 3D City Database + * https://www.3dcitydb.org/ + * + * Copyright 2022-2024 + * virtualcitysystems GmbH, Germany + * https://vc.systems/ + * + * 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.citydb.tiling; + +import com.alibaba.fastjson2.JSONWriter; +import com.alibaba.fastjson2.annotation.JSONType; +import org.citydb.database.adapter.DatabaseAdapter; +import org.citydb.model.geometry.Coordinate; +import org.citydb.model.geometry.Envelope; +import org.citydb.tiling.options.DimensionScheme; +import org.citydb.tiling.options.MatrixScheme; + +@JSONType(serializeFeatures = JSONWriter.Feature.WriteClassName, + typeKey = "type", + seeAlso = {DimensionScheme.class, MatrixScheme.class}, + seeAlsoDefault = MatrixScheme.class) +public abstract class TilingScheme { + protected abstract TileMatrix buildTileMatrix(Envelope extent, DatabaseAdapter adapter) throws TilingException; + + protected TileMatrix buildTileMatrix(Coordinate lowerCorner, Coordinate upperCorner, int columns, int rows, + double tileWidth, double tileHeight, DatabaseAdapter adapter) { + return new TileMatrix(lowerCorner, upperCorner, columns, rows, tileWidth, tileHeight, adapter); + } +} diff --git a/citydb-tiling/src/main/java/org/citydb/tiling/encoding/ExtentReader.java b/citydb-tiling/src/main/java/org/citydb/tiling/encoding/ExtentReader.java new file mode 100644 index 00000000..56168ce9 --- /dev/null +++ b/citydb-tiling/src/main/java/org/citydb/tiling/encoding/ExtentReader.java @@ -0,0 +1,65 @@ +/* + * citydb-tool - Command-line tool for the 3D City Database + * https://www.3dcitydb.org/ + * + * Copyright 2022-2024 + * virtualcitysystems GmbH, Germany + * https://vc.systems/ + * + * 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.citydb.tiling.encoding; + +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.JSONReader; +import com.alibaba.fastjson2.reader.ObjectReader; +import org.citydb.config.common.SrsReference; +import org.citydb.model.geometry.Coordinate; +import org.citydb.model.geometry.Envelope; + +import java.lang.reflect.Type; +import java.util.List; + +public class ExtentReader implements ObjectReader { + @Override + public Envelope readObject(JSONReader jsonReader, Type type, Object o, long l) { + if (jsonReader.isObject()) { + JSONObject extent = jsonReader.readJSONObject(); + Object bounds = extent.get("coordinates"); + if (bounds instanceof JSONArray value) { + List coordinates = value.stream() + .filter(Number.class::isInstance) + .map(Number.class::cast) + .map(Number::doubleValue) + .toList(); + if (coordinates.size() > 3) { + Envelope envelope = Envelope.of( + Coordinate.of(coordinates.get(0), coordinates.get(1)), + Coordinate.of(coordinates.get(2), coordinates.get(3))); + + SrsReference srs = extent.getObject("srs", SrsReference.class); + if (srs != null) { + envelope.setSRID(srs.getSRID().orElse(null)) + .setSrsIdentifier(srs.getIdentifier().orElse(null)); + } + + return envelope; + } + } + } + + return null; + } +} diff --git a/citydb-tiling/src/main/java/org/citydb/tiling/encoding/ExtentWriter.java b/citydb-tiling/src/main/java/org/citydb/tiling/encoding/ExtentWriter.java new file mode 100644 index 00000000..c10d1786 --- /dev/null +++ b/citydb-tiling/src/main/java/org/citydb/tiling/encoding/ExtentWriter.java @@ -0,0 +1,58 @@ +/* + * citydb-tool - Command-line tool for the 3D City Database + * https://www.3dcitydb.org/ + * + * Copyright 2022-2024 + * virtualcitysystems GmbH, Germany + * https://vc.systems/ + * + * 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.citydb.tiling.encoding; + +import com.alibaba.fastjson2.JSONWriter; +import com.alibaba.fastjson2.writer.ObjectWriter; +import org.citydb.config.common.SrsReference; +import org.citydb.model.geometry.Envelope; + +import java.lang.reflect.Type; +import java.util.List; + +public class ExtentWriter implements ObjectWriter { + @Override + public void write(JSONWriter jsonWriter, Object o, Object o1, Type type, long l) { + if (o instanceof Envelope extent) { + jsonWriter.startObject(); + jsonWriter.writeName("coordinates"); + jsonWriter.writeColon(); + jsonWriter.write(List.of(extent.getLowerCorner().getX(), + extent.getLowerCorner().getY(), + extent.getUpperCorner().getX(), + extent.getUpperCorner().getY())); + + if (extent.getSRID().isPresent() || extent.getSrsIdentifier().isPresent()) { + jsonWriter.writeName("srs"); + jsonWriter.writeColon(); + jsonWriter.writeAs(new SrsReference() + .setSRID(extent.getSRID().orElse(null)) + .setIdentifier(extent.getSrsIdentifier().orElse(null)), + SrsReference.class); + } + + jsonWriter.endObject(); + } else { + jsonWriter.writeNull(); + } + } +} diff --git a/citydb-tiling/src/main/java/org/citydb/tiling/encoding/PointReader.java b/citydb-tiling/src/main/java/org/citydb/tiling/encoding/PointReader.java new file mode 100644 index 00000000..126436fe --- /dev/null +++ b/citydb-tiling/src/main/java/org/citydb/tiling/encoding/PointReader.java @@ -0,0 +1,62 @@ +/* + * citydb-tool - Command-line tool for the 3D City Database + * https://www.3dcitydb.org/ + * + * Copyright 2022-2024 + * virtualcitysystems GmbH, Germany + * https://vc.systems/ + * + * 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.citydb.tiling.encoding; + +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.JSONReader; +import com.alibaba.fastjson2.reader.ObjectReader; +import org.citydb.config.common.SrsReference; +import org.citydb.model.geometry.Coordinate; +import org.citydb.model.geometry.Point; + +import java.lang.reflect.Type; +import java.util.List; + +public class PointReader implements ObjectReader { + @Override + public Point readObject(JSONReader jsonReader, Type type, Object o, long l) { + if (jsonReader.isObject()) { + JSONObject extent = jsonReader.readJSONObject(); + Object bounds = extent.get("coordinates"); + if (bounds instanceof JSONArray value) { + List coordinates = value.stream() + .filter(Number.class::isInstance) + .map(Number.class::cast) + .map(Number::doubleValue) + .toList(); + if (coordinates.size() > 1) { + Point point = Point.of(Coordinate.of(coordinates.get(0), coordinates.get(1))); + SrsReference srs = extent.getObject("srs", SrsReference.class); + if (srs != null) { + point.setSRID(srs.getSRID().orElse(null)) + .setSrsIdentifier(srs.getIdentifier().orElse(null)); + } + + return point; + } + } + } + + return null; + } +} diff --git a/citydb-tiling/src/main/java/org/citydb/tiling/encoding/PointWriter.java b/citydb-tiling/src/main/java/org/citydb/tiling/encoding/PointWriter.java new file mode 100644 index 00000000..fdfadfbc --- /dev/null +++ b/citydb-tiling/src/main/java/org/citydb/tiling/encoding/PointWriter.java @@ -0,0 +1,55 @@ +/* + * citydb-tool - Command-line tool for the 3D City Database + * https://www.3dcitydb.org/ + * + * Copyright 2022-2024 + * virtualcitysystems GmbH, Germany + * https://vc.systems/ + * + * 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.citydb.tiling.encoding; + +import com.alibaba.fastjson2.JSONWriter; +import com.alibaba.fastjson2.writer.ObjectWriter; +import org.citydb.config.common.SrsReference; +import org.citydb.model.geometry.Point; + +import java.lang.reflect.Type; +import java.util.List; + +public class PointWriter implements ObjectWriter { + @Override + public void write(JSONWriter jsonWriter, Object o, Object o1, Type type, long l) { + if (o instanceof Point point) { + jsonWriter.startObject(); + jsonWriter.writeName("coordinates"); + jsonWriter.writeColon(); + jsonWriter.write(List.of(point.getCoordinate().getX(), point.getCoordinate().getY())); + + if (point.getSRID().isPresent() || point.getSrsIdentifier().isPresent()) { + jsonWriter.writeName("srs"); + jsonWriter.writeColon(); + jsonWriter.writeAs(new SrsReference() + .setSRID(point.getSRID().orElse(null)) + .setIdentifier(point.getSrsIdentifier().orElse(null)), + SrsReference.class); + } + + jsonWriter.endObject(); + } else { + jsonWriter.writeNull(); + } + } +} diff --git a/citydb-tiling/src/main/java/org/citydb/tiling/encoding/SrsUnitReader.java b/citydb-tiling/src/main/java/org/citydb/tiling/encoding/SrsUnitReader.java new file mode 100644 index 00000000..c623eec0 --- /dev/null +++ b/citydb-tiling/src/main/java/org/citydb/tiling/encoding/SrsUnitReader.java @@ -0,0 +1,37 @@ +/* + * citydb-tool - Command-line tool for the 3D City Database + * https://www.3dcitydb.org/ + * + * Copyright 2022-2024 + * virtualcitysystems GmbH, Germany + * https://vc.systems/ + * + * 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.citydb.tiling.encoding; + +import com.alibaba.fastjson2.JSONReader; +import com.alibaba.fastjson2.reader.ObjectReader; +import org.citydb.database.srs.SrsUnit; + +import java.lang.reflect.Type; + +public class SrsUnitReader implements ObjectReader { + @Override + public SrsUnit readObject(JSONReader jsonReader, Type type, Object o, long l) { + return jsonReader.isString() ? + SrsUnit.of(jsonReader.readString()) : + null; + } +} diff --git a/citydb-tiling/src/main/java/org/citydb/tiling/options/Dimension.java b/citydb-tiling/src/main/java/org/citydb/tiling/options/Dimension.java new file mode 100644 index 00000000..18da5712 --- /dev/null +++ b/citydb-tiling/src/main/java/org/citydb/tiling/options/Dimension.java @@ -0,0 +1,61 @@ +/* + * citydb-tool - Command-line tool for the 3D City Database + * https://www.3dcitydb.org/ + * + * Copyright 2022-2024 + * virtualcitysystems GmbH, Germany + * https://vc.systems/ + * + * 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.citydb.tiling.options; + +import com.alibaba.fastjson2.JSONWriter; +import com.alibaba.fastjson2.annotation.JSONField; +import org.citydb.database.srs.SrsUnit; +import org.citydb.tiling.encoding.SrsUnitReader; + +import java.util.Optional; + +public class Dimension { + private double value; + @JSONField(serializeFeatures = JSONWriter.Feature.WriteEnumUsingToString, deserializeUsing = SrsUnitReader.class) + private SrsUnit unit; + + public static Dimension of(double value, SrsUnit unit) { + return new Dimension().setValue(value).setUnit(unit); + } + + public static Dimension of(double value) { + return of(value, null); + } + + public double getValue() { + return value; + } + + public Dimension setValue(double value) { + this.value = value; + return this; + } + + public Optional getUnit() { + return Optional.ofNullable(unit); + } + + public Dimension setUnit(SrsUnit unit) { + this.unit = unit; + return this; + } +} diff --git a/citydb-tiling/src/main/java/org/citydb/tiling/options/DimensionScheme.java b/citydb-tiling/src/main/java/org/citydb/tiling/options/DimensionScheme.java new file mode 100644 index 00000000..e52f4102 --- /dev/null +++ b/citydb-tiling/src/main/java/org/citydb/tiling/options/DimensionScheme.java @@ -0,0 +1,152 @@ +/* + * 3D City Database - The Open Source CityGML Database + * https://www.3dcitydb.org/ + * + * Copyright 2013 - 2024 + * Chair of Geoinformatics + * Technical University of Munich, Germany + * https://www.lrg.tum.de/gis/ + * + * The 3D City Database is jointly developed with the following + * cooperation partners: + * + * Virtual City Systems, Berlin + * M.O.S.S. Computer Grafik Systeme GmbH, Taufkirchen + * + * 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.citydb.tiling.options; + +import com.alibaba.fastjson2.annotation.JSONField; +import com.alibaba.fastjson2.annotation.JSONType; +import org.citydb.database.adapter.DatabaseAdapter; +import org.citydb.database.geometry.GeometryException; +import org.citydb.database.srs.SpatialReference; +import org.citydb.database.srs.SrsException; +import org.citydb.database.util.SrsHelper; +import org.citydb.model.geometry.Coordinate; +import org.citydb.model.geometry.Envelope; +import org.citydb.model.geometry.Point; +import org.citydb.tiling.TileMatrix; +import org.citydb.tiling.TilingException; +import org.citydb.tiling.TilingScheme; +import org.citydb.tiling.encoding.PointReader; +import org.citydb.tiling.encoding.PointWriter; + +import java.sql.SQLException; +import java.util.Optional; + +@JSONType(typeName = "Dimension") +public class DimensionScheme extends TilingScheme { + private Dimension width; + private Dimension height; + @JSONField(serializeUsing = PointWriter.class, deserializeUsing = PointReader.class) + private Point gridPoint; + + public static DimensionScheme of(double width, double height) { + return of(Dimension.of(width), Dimension.of(height)); + } + + public static DimensionScheme of(Dimension width, Dimension height) { + return of(width, height, null); + } + + public static DimensionScheme of(Dimension width, Dimension height, Point gridPoint) { + return new DimensionScheme() + .setWidth(width) + .setHeight(height) + .setGridPoint(gridPoint); + } + + public Optional getHeight() { + return Optional.ofNullable(height); + } + + public DimensionScheme setHeight(Dimension height) { + this.height = height; + return this; + } + + public Optional getWidth() { + return Optional.ofNullable(width); + } + + public DimensionScheme setWidth(Dimension width) { + this.width = width; + return this; + } + + public Optional getGridPoint() { + return Optional.ofNullable(gridPoint); + } + + public DimensionScheme setGridPoint(Point gridPoint) { + this.gridPoint = gridPoint; + return this; + } + + @Override + protected TileMatrix buildTileMatrix(Envelope extent, DatabaseAdapter adapter) throws TilingException { + if (width == null) { + throw new TilingException("No tile width provided for the dimension tiling scheme."); + } else if (width.getValue() <= 0) { + throw new TilingException("The tile width must be a positive number but was " + width.getValue() + "."); + } else if (height == null) { + throw new TilingException("No tile height provided for the dimension tiling scheme."); + } else if (height.getValue() <= 0) { + throw new TilingException("The tile height must be a positive number but was " + height.getValue() + "."); + } + + double tileWidth, tileHeight; + try { + SrsHelper helper = adapter.getGeometryAdapter().getSrsHelper(); + tileWidth = helper.convert(width.getValue(), width.getUnit().orElse(null)); + tileHeight = helper.convert(height.getValue(), height.getUnit().orElse(null)); + } catch (SrsException e) { + throw new TilingException("Failed to convert tile dimension to the unit of the database SRS.", e); + } + + double gridX, gridY; + if (gridPoint != null) { + try { + SpatialReference reference = adapter.getGeometryAdapter().getSpatialReference(gridPoint) + .orElse(adapter.getDatabaseMetadata().getSpatialReference()); + Point point = reference.getSRID() != adapter.getDatabaseMetadata().getSpatialReference().getSRID() ? + adapter.getGeometryAdapter().transform(gridPoint) : + gridPoint; + gridX = point.getCoordinate().getX(); + gridY = point.getCoordinate().getY(); + } catch (GeometryException | SrsException | SQLException e) { + throw new TilingException("Failed to transform the grid point to the database SRS.", e); + } + } else { + gridX = gridY = 0; + } + + double minX = getNearestNeighbor(gridX, extent.getLowerCorner().getX(), tileWidth); + double minY = getNearestNeighbor(gridY, extent.getLowerCorner().getY(), tileHeight); + int columns = (int) Math.ceil((extent.getUpperCorner().getX() - minX) / tileWidth); + int rows = (int) Math.ceil((extent.getUpperCorner().getY() - minY) / tileHeight); + Coordinate lowerCorner = Coordinate.of(minX, minY); + Coordinate upperCorner = Coordinate.of(minX + columns * tileWidth, minY + rows * tileHeight); + + return buildTileMatrix(lowerCorner, upperCorner, columns, rows, tileWidth, tileHeight, adapter); + } + + private double getNearestNeighbor(double gridValue, double candidate, double offset) { + return gridValue >= candidate ? + gridValue - Math.ceil((gridValue - candidate) / offset) * offset : + gridValue + Math.floor((candidate - gridValue) / offset) * offset; + } +} diff --git a/citydb-tiling/src/main/java/org/citydb/tiling/options/MatrixScheme.java b/citydb-tiling/src/main/java/org/citydb/tiling/options/MatrixScheme.java new file mode 100644 index 00000000..2ee58b06 --- /dev/null +++ b/citydb-tiling/src/main/java/org/citydb/tiling/options/MatrixScheme.java @@ -0,0 +1,78 @@ +/* + * 3D City Database - The Open Source CityGML Database + * https://www.3dcitydb.org/ + * + * Copyright 2013 - 2024 + * Chair of Geoinformatics + * Technical University of Munich, Germany + * https://www.lrg.tum.de/gis/ + * + * The 3D City Database is jointly developed with the following + * cooperation partners: + * + * Virtual City Systems, Berlin + * M.O.S.S. Computer Grafik Systeme GmbH, Taufkirchen + * + * 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.citydb.tiling.options; + +import com.alibaba.fastjson2.annotation.JSONType; +import org.citydb.database.adapter.DatabaseAdapter; +import org.citydb.model.geometry.Envelope; +import org.citydb.tiling.TileMatrix; +import org.citydb.tiling.TilingException; +import org.citydb.tiling.TilingScheme; + +@JSONType(typeName = "Matrix") +public class MatrixScheme extends TilingScheme { + private int columns = 1; + private int rows = 1; + + public static MatrixScheme of(int columns, int rows) { + return new MatrixScheme().setColumns(columns).setRows(rows); + } + + public int getColumns() { + return columns; + } + + public MatrixScheme setColumns(int columns) { + this.columns = columns; + return this; + } + + public int getRows() { + return rows; + } + + public MatrixScheme setRows(int rows) { + this.rows = rows; + return this; + } + + @Override + protected TileMatrix buildTileMatrix(Envelope extent, DatabaseAdapter adapter) throws TilingException { + if (columns < 1) { + throw new TilingException("The number of columns must be a positive integer but was " + columns + "."); + } else if (rows < 1) { + throw new TilingException("The number of rows must be a positive integer but was " + rows + "."); + } + + return buildTileMatrix(extent.getLowerCorner(), extent.getUpperCorner(), columns, rows, + (extent.getUpperCorner().getX() - extent.getLowerCorner().getX()) / columns, + (extent.getUpperCorner().getY() - extent.getLowerCorner().getY()) / rows, + adapter); + } +} diff --git a/citydb-tiling/src/main/java/org/citydb/tiling/options/TileMatrixOrigin.java b/citydb-tiling/src/main/java/org/citydb/tiling/options/TileMatrixOrigin.java new file mode 100644 index 00000000..24b41598 --- /dev/null +++ b/citydb-tiling/src/main/java/org/citydb/tiling/options/TileMatrixOrigin.java @@ -0,0 +1,52 @@ +/* + * citydb-tool - Command-line tool for the 3D City Database + * https://www.3dcitydb.org/ + * + * Copyright 2022-2024 + * virtualcitysystems GmbH, Germany + * https://vc.systems/ + * + * 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.citydb.tiling.options; + +public enum TileMatrixOrigin { + TOP_LEFT("topLeft"), + BOTTOM_LEFT("bottomLeft"); + + private final String value; + + TileMatrixOrigin(String value) { + this.value = value; + } + + public String toValue() { + return value; + } + + public static TileMatrixOrigin fromValue(String value) { + for (TileMatrixOrigin v : TileMatrixOrigin.values()) { + if (v.value.equals(value)) { + return v; + } + } + + return null; + } + + @Override + public String toString() { + return value; + } +} diff --git a/settings.gradle b/settings.gradle index fa7e902a..21d417cb 100644 --- a/settings.gradle +++ b/settings.gradle @@ -13,4 +13,5 @@ include 'citydb-logging' include 'citydb-model' include 'citydb-operation' include 'citydb-plugin' -include 'citydb-query' \ No newline at end of file +include 'citydb-query' +include 'citydb-tiling' \ No newline at end of file