Skip to content

Commit

Permalink
reworked geometry properties
Browse files Browse the repository at this point in the history
  • Loading branch information
clausnagel committed Dec 2, 2023
1 parent e51a0eb commit cf019be
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 105 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,43 +32,64 @@ public class GeometryBuilder {

public Geometry<?> buildGeometry(Geometry<?> geometry, JSONObject properties) throws GeometryException {
if (geometry != null && properties != null) {
Map<Integer, JSONObject> hierarchy = buildHierarchy(properties);
if (!hierarchy.isEmpty()) {
if (hierarchy.get(0).containsKey(Properties.JSON_KEY_PARENT)) {
throw new GeometryException("The geometry hierarchy lacks a root item.");
}

Map<Integer, Geometry<?>> parents = new HashMap<>(hierarchy.size());
JSONArray children = properties.getJSONArray(Properties.JSON_KEY_CHILDREN);
if (children != null && !children.isEmpty()) {
List<? extends Geometry<?>> primitives = getPrimitives(geometry);
try {
for (Map.Entry<Integer, JSONObject> entry : hierarchy.entrySet()) {
Geometry<?> item = buildGeometry(entry.getValue(), parents, primitives);
parents.put(entry.getKey(), item);
}
} catch (Exception e) {
throw new GeometryException("Failed to rebuild geometry hierarchy.", e);
}
geometry = buildGeometry(properties, null, primitives);

Map<Integer, Geometry<?>> geometries = new HashMap<>(children.size() + 1);
geometries.put(-1, geometry);

if (!parents.isEmpty()) {
geometry = parents.get(0);
for (int i = 0; i < children.size(); i++) {
buildGeometry(i, children, geometries, primitives);
}
} else {
GeometryType type = getType(properties);
if (geometry.getGeometryType() != type) {
geometry = castGeometry(geometry, type);
}
}

processMetadata(geometry, properties);
geometry.setObjectId(properties.getString(Properties.JSON_KEY_OBJECT_ID));
if (properties.getBooleanValue(Properties.JSON_KEY_IS_2D, false)) {
geometry.force2D();
}
}

return geometry;
}

private Geometry<?> buildGeometry(JSONObject item, Map<Integer, Geometry<?>> parents, List<? extends Geometry<?>> primitives) throws GeometryException {
GeometryType type = GeometryType.fromDatabaseValue(item.getIntValue(Properties.JSON_KEY_TYPE, -1));
if (type == null) {
throw new GeometryException("Missing geometry type property.");
private Geometry<?> buildGeometry(int childIndex, JSONArray children, Map<Integer, Geometry<?>> geometries, List<? extends Geometry<?>> primitives) throws GeometryException {
Geometry<?> geometry = geometries.get(childIndex);
if (geometry == null) {
JSONObject child = children.getJSONObject(childIndex);
if (child == null) {
throw new GeometryException("Invalid JSON object in children array at index " + childIndex + ".");
}

Integer parentIndex = child.getInteger(Properties.JSON_KEY_PARENT);
if (parentIndex == null) {
parentIndex = -1;
} else if (parentIndex < 0 || parentIndex >= children.size()) {
throw new GeometryException("Parent index out of bounds.");
}

Geometry<?> parent = geometries.get(parentIndex);
if (parent == null) {
parent = buildGeometry(parentIndex, children, geometries, primitives);
}

geometry = buildGeometry(child, parent, primitives)
.setObjectId(child.getString(Properties.JSON_KEY_OBJECT_ID));
geometries.put(childIndex, geometry);
}

Geometry<?> parent = getParent(item, parents);
return geometry;
}

private Geometry<?> buildGeometry(JSONObject item, Geometry<?> parent, List<? extends Geometry<?>> primitives) throws GeometryException {
Geometry<?> geometry = null;
switch (type) {
switch (getType(item)) {
case POINT:
geometry = getPrimitive(item, primitives, Point.class);
if (parent instanceof MultiPoint) {
Expand Down Expand Up @@ -120,39 +141,43 @@ private Geometry<?> buildGeometry(JSONObject item, Map<Integer, Geometry<?>> par
}

if (geometry != null) {
return geometry.setObjectId(item.getString(Properties.JSON_KEY_OBJECT_ID));
return geometry;
} else {
throw new GeometryException("Failed to parse geometry hierarchy item.");
throw new GeometryException("Failed to parse geometry object.");
}
}

private <T extends Geometry<?>> T getPrimitive(JSONObject item, List<? extends Geometry<?>> primitives, Class<T> type) throws GeometryException {
Integer index = item.getInteger(Properties.JSON_KEY_GEOMETRY_INDEX);
if (index == null) {
throw new GeometryException("Missing geometry index.");
} else if (index < 0 || index >= primitives.size()) {
throw new GeometryException("Geometry index out of bounds.");
}

Geometry<?> primitive = primitives.get(index);
if (type.isInstance(primitive)) {
return type.cast(primitive);
} else {
throw new GeometryException("Invalid primitive type " + primitive.getGeometryType() + ".");
}
}

private Geometry<?> getParent(JSONObject item, Map<Integer, Geometry<?>> parents) throws GeometryException {
Integer index = item.getInteger(Properties.JSON_KEY_PARENT);
if (index != null) {
Geometry<?> parent = parents.get(index);
if (parent == null) {
throw new GeometryException("Parent index out of bounds.");
private Geometry<?> castGeometry(Geometry<?> geometry, GeometryType type) throws GeometryException {
try {
switch (type) {
case POINT:
return getPrimitives(geometry, Point.class).get(0);
case MULTI_POINT:
return MultiPoint.of(getPrimitives(geometry, Point.class));
case LINE_STRING:
return getPrimitives(geometry, LineString.class).get(0);
case MULTI_LINE_STRING:
return MultiLineString.of(getPrimitives(geometry, LineString.class));
case POLYGON:
return getPrimitives(geometry, Polygon.class).get(0);
case MULTI_SURFACE:
return MultiSurface.of(getPrimitives(geometry, Polygon.class));
case TRIANGULATED_SURFACE:
return TriangulatedSurface.of(getPrimitives(geometry, Polygon.class));
case COMPOSITE_SURFACE:
return CompositeSurface.of(getPrimitives(geometry, Polygon.class));
case SOLID:
return Solid.of(CompositeSurface.of(getPrimitives(geometry, Polygon.class)));
case MULTI_SOLID:
return MultiSolid.of(Solid.of(CompositeSurface.of(getPrimitives(geometry, Polygon.class))));
case COMPOSITE_SOLID:
return CompositeSolid.of(Solid.of(CompositeSurface.of(getPrimitives(geometry, Polygon.class))));
default:
return geometry;
}

return parent;
} else {
return null;
} catch (Exception e) {
throw new GeometryException("Failed to convert database geometry into a " +
type.getTypeName() + " geometry.");
}
}

Expand Down Expand Up @@ -185,30 +210,38 @@ private List<? extends Geometry<?>> getPrimitives(Geometry<?> geometry) {
}
}

private Map<Integer, JSONObject> buildHierarchy(JSONObject properties) {
JSONArray items = properties.getJSONArray(Properties.JSON_KEY_HIERARCHY);
if (items != null && !items.isEmpty()) {
Map<Integer, JSONObject> hierarchy = new TreeMap<>();
for (int i = 0; i < items.size(); i++) {
Object item = items.get(i);
if (item instanceof JSONObject) {
hierarchy.put(i, (JSONObject) item);
}
}

return hierarchy;
@SuppressWarnings("unchecked")
private <T extends Geometry<?>> List<T> getPrimitives(Geometry<?> geometry, Class<T> type) {
List<? extends Geometry<?>> primitives = getPrimitives(geometry);
if (!primitives.isEmpty() && type.isInstance(primitives.get(0))) {
return (List<T>) primitives;
} else {
throw new ClassCastException();
}

return Collections.emptyMap();
}

private void processMetadata(Geometry<?> geometry, JSONObject properties) {
if (geometry.getObjectId().isEmpty()) {
geometry.setObjectId(properties.getString(Properties.JSON_KEY_OBJECT_ID));
private <T extends Geometry<?>> T getPrimitive(JSONObject item, List<? extends Geometry<?>> primitives, Class<T> type) throws GeometryException {
Integer index = item.getInteger(Properties.JSON_KEY_GEOMETRY_INDEX);
if (index == null) {
throw new GeometryException("Missing geometry index.");
} else if (index < 0 || index >= primitives.size()) {
throw new GeometryException("Geometry index out of bounds.");
}

if (properties.getBooleanValue(Properties.JSON_KEY_IS_2D, false)) {
geometry.force2D();
Geometry<?> primitive = primitives.get(index);
if (type.isInstance(primitive)) {
return type.cast(primitive);
} else {
throw new GeometryException("Invalid primitive type " + primitive.getGeometryType() + ".");
}
}

private GeometryType getType(JSONObject item) throws GeometryException {
GeometryType type = GeometryType.fromDatabaseValue(item.getIntValue(Properties.JSON_KEY_TYPE, -1));
if (type != null) {
return type;
} else {
throw new GeometryException("Missing geometry type property.");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
public class Properties {
public static final String JSON_KEY_OBJECT_ID = "objectId";
public static final String JSON_KEY_IS_2D = "is2D";
public static final String JSON_KEY_HIERARCHY = "hierarchy";
public static final String JSON_KEY_CHILDREN = "children";
public static final String JSON_KEY_TYPE = "type";
public static final String JSON_KEY_PARENT = "parent";
public static final String JSON_KEY_GEOMETRY_INDEX = "geometryIndex";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,23 @@ public class PropertiesBuilder {

public JSONObject buildProperties(Geometry<?> geometry) {
if (geometry != null) {
JSONObject properties = new JSONObject();

properties.put(Properties.JSON_KEY_OBJECT_ID, geometry.getOrCreateObjectId());
if (geometry.getVertexDimension() == 2) {
properties.put(Properties.JSON_KEY_IS_2D, true);
}

Hierarchy hierarchy = new Hierarchy();
switch (geometry.getGeometryType()) {
case POINT:
buildHierarchy((Point) geometry, -1, hierarchy);
break;
case MULTI_POINT:
buildHierarchy((MultiPoint) geometry, hierarchy);
break;
case LINE_STRING:
buildHierarchy((LineString) geometry, -1, hierarchy);
break;
case MULTI_LINE_STRING:
buildHierarchy((MultiLineString) geometry, hierarchy);
break;
case POLYGON:
buildHierarchy((Polygon) geometry, -1, hierarchy);
break;
case MULTI_SURFACE:
case TRIANGULATED_SURFACE:
case COMPOSITE_SURFACE:
Expand All @@ -58,13 +60,7 @@ public JSONObject buildProperties(Geometry<?> geometry) {
break;
}

if (!hierarchy.isEmpty()) {
properties.put(Properties.JSON_KEY_HIERARCHY, hierarchy.getContent());
}

if (!properties.isEmpty()) {
return properties;
}
return hierarchy.getRoot();
}

return null;
Expand Down Expand Up @@ -108,38 +104,45 @@ private void buildHierarchy(SolidCollection<?> solids, Hierarchy hierarchy) {
}

private static class Hierarchy {
private final JSONArray content = new JSONArray();
private final JSONObject root = new JSONObject();
private final JSONArray children = new JSONArray();
private int geometryIndex;

private JSONArray getContent() {
return content;
private JSONObject getRoot() {
if (!children.isEmpty()) {
root.put(Properties.JSON_KEY_CHILDREN, children);
}

return root;
}

private int add(Geometry<?> geometry, int parent) {
JSONObject item = content.addObject()
JSONObject item = (parent == -1 ? root : children.addObject())
.fluentPut(Properties.JSON_KEY_TYPE, geometry.getGeometryType().getDatabaseValue())
.fluentPut(Properties.JSON_KEY_OBJECT_ID, geometry.getOrCreateObjectId());

if (parent >= 0) {
item.put(Properties.JSON_KEY_PARENT, parent);
}

switch (geometry.getGeometryType()) {
case POINT:
case LINE_STRING:
case POLYGON:
item.put(Properties.JSON_KEY_GEOMETRY_INDEX, geometryIndex++);
if (parent == -1) {
if (geometry.getVertexDimension() == 2) {
item.put(Properties.JSON_KEY_IS_2D, true);
}
} else {
if (parent > 0) {
item.put(Properties.JSON_KEY_PARENT, parent - 1);
}

switch (geometry.getGeometryType()) {
case POINT:
case LINE_STRING:
case POLYGON:
item.put(Properties.JSON_KEY_GEOMETRY_INDEX, geometryIndex++);
}
}

if (geometry instanceof Polygon && ((Polygon) geometry).isReversed()) {
item.put(Properties.JSON_KEY_IS_REVERSED, true);
}

return content.size() - 1;
}

private boolean isEmpty() {
return content.isEmpty();
return children.size();
}
}
}
13 changes: 12 additions & 1 deletion resources/3dcitydb/json-schema/geometryProperties.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,19 @@
"description": "Schema for the JSON column geometry_properties of the geometry_data table.",
"type": "object",
"properties": {
"type": {
"type": "number",
"minimum": 1,
"maximum": 11
},
"objectId": {
"type": "string"
},
"is2D": {
"type": "boolean",
"default": false
},
"hierarchy": {
"children": {
"type": "array",
"items": {
"type": "object",
Expand All @@ -36,10 +41,16 @@
"default": false
}
},
"required": [
"type"
],
"additionalProperties": false
},
"additionalItems": false
}
},
"required": [
"type"
],
"additionalProperties": false
}

0 comments on commit cf019be

Please sign in to comment.