Skip to content

Commit

Permalink
enhanced encoding of model objects
Browse files Browse the repository at this point in the history
  • Loading branch information
clausnagel committed Nov 17, 2024
1 parent 6fb0ca7 commit 5253130
Show file tree
Hide file tree
Showing 11 changed files with 102 additions and 56 deletions.
3 changes: 3 additions & 0 deletions citydb-model/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
dependencies {
implementation project(':citydb-config')
}
3 changes: 3 additions & 0 deletions citydb-model/src/main/java/module-info.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
module org.citydb.model {
requires org.citydb.config;

exports org.citydb.model.address;
exports org.citydb.model.appearance;
exports org.citydb.model.common;
exports org.citydb.model.encoding;
exports org.citydb.model.feature;
exports org.citydb.model.geometry;
exports org.citydb.model.property;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
* limitations under the License.
*/

package org.citydb.tiling.encoding;
package org.citydb.model.encoding;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
Expand All @@ -32,24 +32,31 @@
import java.lang.reflect.Type;
import java.util.List;

public class ExtentReader implements ObjectReader<Envelope> {
public class EnvelopeConfigReader implements ObjectReader<Envelope> {
@Override
public Envelope readObject(JSONReader jsonReader, Type type, Object o, long l) {
if (jsonReader.isObject()) {
JSONObject extent = jsonReader.readJSONObject();
Object bounds = extent.get("coordinates");
JSONObject object = jsonReader.readJSONObject();
Object bounds = object.get("coordinates");
if (bounds instanceof JSONArray value) {
Envelope envelope = null;
List<Double> coordinates = value.stream()
.filter(Number.class::isInstance)
.map(Number.class::cast)
.map(Number::doubleValue)
.toList();
if (coordinates.size() > 3) {
Envelope envelope = Envelope.of(
if (coordinates.size() == 4) {
envelope = Envelope.of(
Coordinate.of(coordinates.get(0), coordinates.get(1)),
Coordinate.of(coordinates.get(2), coordinates.get(3)));
} else if (coordinates.size() == 6) {
envelope = Envelope.of(
Coordinate.of(coordinates.get(0), coordinates.get(1), coordinates.get(2)),
Coordinate.of(coordinates.get(3), coordinates.get(4), coordinates.get(5)));
}

SrsReference srs = extent.getObject("srs", SrsReference.class);
if (envelope != null) {
SrsReference srs = object.getObject("srs", SrsReference.class);
if (srs != null) {
envelope.setSRID(srs.getSRID().orElse(null))
.setSrsIdentifier(srs.getIdentifier().orElse(null));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,34 +19,39 @@
* limitations under the License.
*/

package org.citydb.tiling.encoding;
package org.citydb.model.encoding;

import com.alibaba.fastjson2.JSONWriter;
import com.alibaba.fastjson2.writer.ObjectWriter;
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 ExtentWriter implements ObjectWriter<Envelope> {
public class EnvelopeConfigWriter implements ObjectWriter<Envelope> {
@Override
public void write(JSONWriter jsonWriter, Object o, Object o1, Type type, long l) {
if (o instanceof Envelope extent) {
if (o instanceof Envelope envelope) {
Coordinate lowerCorner = envelope.getLowerCorner();
Coordinate upperCorner = envelope.getUpperCorner();

jsonWriter.startObject();
jsonWriter.writeName("coordinates");
jsonWriter.writeColon();
jsonWriter.write(List.of(extent.getLowerCorner().getX(),
extent.getLowerCorner().getY(),
extent.getUpperCorner().getX(),
extent.getUpperCorner().getY()));
jsonWriter.write(envelope.getVertexDimension() == 2 ?
List.of(lowerCorner.getX(), lowerCorner.getY(),
upperCorner.getX(), upperCorner.getY()) :
List.of(lowerCorner.getX(), lowerCorner.getY(), lowerCorner.getZ(),
upperCorner.getX(), upperCorner.getY(), upperCorner.getZ()));

if (extent.getSRID().isPresent() || extent.getSrsIdentifier().isPresent()) {
if (envelope.getSRID().isPresent() || envelope.getSrsIdentifier().isPresent()) {
jsonWriter.writeName("srs");
jsonWriter.writeColon();
jsonWriter.writeAs(new SrsReference()
.setSRID(extent.getSRID().orElse(null))
.setIdentifier(extent.getSrsIdentifier().orElse(null)),
.setSRID(envelope.getSRID().orElse(null))
.setIdentifier(envelope.getSrsIdentifier().orElse(null)),
SrsReference.class);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,46 +19,53 @@
* limitations under the License.
*/

package org.citydb.model.util;
package org.citydb.model.encoding;

import org.citydb.model.appearance.Texture;
import org.citydb.model.appearance.TextureImageProperty;
import org.citydb.model.common.ExternalFile;
import org.citydb.model.feature.FeatureCollection;
import org.citydb.model.common.Visitable;
import org.citydb.model.geometry.ImplicitGeometry;
import org.citydb.model.walker.ModelWalker;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;

public class ModelReader {
public class ModelObjectReader {

private ModelReader() {
private ModelObjectReader() {
}

public static ModelReader newInstance() {
return new ModelReader();
public static ModelObjectReader newInstance() {
return new ModelObjectReader();
}

public FeatureCollection read(Path inputFile) throws IOException {
Objects.requireNonNull(inputFile, "The input file must not be null.");
public <T extends Serializable> T read(Path inputFile, Class<T> type) throws IOException {
Object object = read(inputFile);
if (type.isInstance(object)) {
return type.cast(object);
} else {
throw new IOException("Failed to cast content of input file to " + type.getSimpleName() + ".");
}
}

public Object read(Path inputFile) throws IOException {
Objects.requireNonNull(inputFile, "The input file must not be null.");
try (ObjectInputStream stream = new ObjectInputStream(new BufferedInputStream(
Files.newInputStream(inputFile)))) {
Object object = stream.readObject();
if (!(object instanceof FeatureCollection collection)) {
throw new IOException("The input file " + inputFile + " is not a feature collection.");
if (object instanceof Visitable visitable) {
visitable.accept(new Postprocessor(inputFile));
}

Postprocessor postprocessor = new Postprocessor(inputFile);
collection.getFeatures().forEach(feature -> feature.accept(postprocessor));
return collection;
return object;
} catch (ClassNotFoundException e) {
throw new IOException("Failed to read feature collection.", e);
throw new IOException("Failed to parse model object.", e);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,82 +19,95 @@
* limitations under the License.
*/

package org.citydb.model.util;
package org.citydb.model.encoding;

import org.citydb.model.appearance.Texture;
import org.citydb.model.appearance.TextureImageProperty;
import org.citydb.model.common.Child;
import org.citydb.model.common.ExternalFile;
import org.citydb.model.common.Visitable;
import org.citydb.model.feature.FeatureCollection;
import org.citydb.model.geometry.ImplicitGeometry;
import org.citydb.model.walker.ModelWalker;

import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.Objects;

public class ModelWriter {
public class ModelObjectWriter {
private boolean failFast;
private boolean copyExternalFiles = true;
private boolean createUniqueFileNames;
private String textureFolder;
private String libraryObjectsFolder;

private ModelWriter() {
private ModelObjectWriter() {
}

public static ModelWriter newInstance() {
return new ModelWriter();
public static ModelObjectWriter newInstance() {
return new ModelObjectWriter();
}

public ModelWriter failFast(boolean failFast) {
public ModelObjectWriter failFast(boolean failFast) {
this.failFast = failFast;
return this;
}

public ModelWriter copyExternalFiles(boolean copyExternalFiles) {
public ModelObjectWriter copyExternalFiles(boolean copyExternalFiles) {
this.copyExternalFiles = copyExternalFiles;
return this;
}

public ModelWriter createUniqueFileNames(boolean createUniqueFileNames) {
public ModelObjectWriter createUniqueFileNames(boolean createUniqueFileNames) {
this.createUniqueFileNames = createUniqueFileNames;
return this;
}

public ModelWriter withRelativeTextureFolder(String textureFolder) {
public ModelObjectWriter withRelativeTextureFolder(String textureFolder) {
this.textureFolder = textureFolder;
return this;
}

public ModelWriter withRelativeLibraryObjectsFolder(String libraryObjectsFolder) {
public ModelObjectWriter withRelativeLibraryObjectsFolder(String libraryObjectsFolder) {
this.libraryObjectsFolder = libraryObjectsFolder;
return this;
}

public void write(Child object, Path outputFile) throws IOException {
writeObject(object, outputFile);
}

public void write(FeatureCollection collection, Path outputFile) throws IOException {
Objects.requireNonNull(collection, "The feature collection must not be null.");
writeObject(collection, outputFile);
}

private void writeObject(Serializable object, Path outputFile) throws IOException {
Objects.requireNonNull(object, "The model object must not be null.");
outputFile = Objects.requireNonNull(outputFile, "The output file must not be null.")
.normalize()
.toAbsolutePath();

try (ObjectOutputStream stream = new ObjectOutputStream(new BufferedOutputStream(
Files.newOutputStream(outputFile)))) {
Preprocessor preprocessor = new Preprocessor(outputFile);
collection.getFeatures().forEach(feature -> feature.accept(preprocessor));
stream.writeObject(collection);
if (object instanceof Visitable visitable) {
visitable.accept(new Postprocessor(outputFile));
}

stream.writeObject(object);
}
}

private class Preprocessor extends ModelWalker {
private class Postprocessor extends ModelWalker {
private final FileHelper textureHelper;
private final FileHelper libraryObjectsHelper;

Preprocessor(Path outputFile) {
Postprocessor(Path outputFile) {
this.textureHelper = new FileHelper(textureFolder, "appearance", outputFile, "tex_");
this.libraryObjectsHelper = new FileHelper(libraryObjectsFolder, "library-objects", outputFile, "lib_");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,16 @@

package org.citydb.model.feature;

import org.citydb.model.common.Visitable;
import org.citydb.model.common.Visitor;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

public class FeatureCollection implements Serializable {
public class FeatureCollection implements Visitable, Serializable {
private final List<Feature> features;

private FeatureCollection(List<Feature> features) {
Expand Down Expand Up @@ -74,4 +77,9 @@ public boolean isEmpty() {
public void clear() {
features.clear();
}

@Override
public void accept(Visitor visitor) {
features.forEach(feature -> feature.accept(visitor));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public int getVertexDimension() {
@Override
public Optional<Integer> getSRID() {
if (srid == null) {
SrsReference parent = getInheritedSRSReference();
SrsReference parent = getInheritedSrsReference();
if (parent != null) {
return parent.getSRID();
}
Expand All @@ -82,7 +82,7 @@ public Envelope setSRID(Integer srid) {
@Override
public Optional<String> getSrsIdentifier() {
if (srsIdentifier == null) {
SrsReference parent = getInheritedSRSReference();
SrsReference parent = getInheritedSrsReference();
if (parent != null) {
return parent.getSrsIdentifier();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public T setObjectId(String objectId) {
@Override
public Optional<Integer> getSRID() {
if (srid == null) {
SrsReference parent = getInheritedSRSReference();
SrsReference parent = getInheritedSrsReference();
if (parent != null) {
return parent.getSRID();
}
Expand All @@ -71,7 +71,7 @@ public T setSRID(Integer srid) {
@Override
public Optional<String> getSrsIdentifier() {
if (srsIdentifier == null) {
SrsReference parent = getInheritedSRSReference();
SrsReference parent = getInheritedSrsReference();
if (parent != null) {
return parent.getSrsIdentifier();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public interface SrsReference {

SrsReference setSrsIdentifier(String srsIdentifier);

default SrsReference getInheritedSRSReference() {
default SrsReference getInheritedSrsReference() {
if (this instanceof Child parent) {
while ((parent = parent.getParent().orElse(null)) != null) {
if (parent instanceof SrsReference reference) {
Expand Down
6 changes: 3 additions & 3 deletions citydb-tiling/src/main/java/org/citydb/tiling/Tiling.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@
import org.citydb.database.geometry.GeometryException;
import org.citydb.database.srs.SpatialReference;
import org.citydb.database.srs.SrsException;
import org.citydb.model.encoding.EnvelopeConfigReader;
import org.citydb.model.encoding.EnvelopeConfigWriter;
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;
Expand All @@ -39,7 +39,7 @@

@SerializableConfig(name = "tilingOptions")
public class Tiling {
@JSONField(serializeUsing = ExtentWriter.class, deserializeUsing = ExtentReader.class)
@JSONField(serializeUsing = EnvelopeConfigWriter.class, deserializeUsing = EnvelopeConfigReader.class)
private Envelope extent;
private TilingScheme scheme;
@JSONField(serializeFeatures = JSONWriter.Feature.WriteEnumUsingToString)
Expand Down

0 comments on commit 5253130

Please sign in to comment.