Skip to content

Commit

Permalink
feat: CSV, Excel and JSON writer - reduce to single geometries if dat…
Browse files Browse the repository at this point in the history
…a does not contain multi geometry

CSV, Excel and JSON writer have been reduced to single geometries if data contains single geometry.

ING-3570
  • Loading branch information
emanuelaepure10 committed May 22, 2024
1 parent 3149a02 commit 2ec4e30
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Import-Package: au.com.bytecode.opencsv;version="2.3.0",
eu.esdihumboldt.hale.common.lookup,
eu.esdihumboldt.hale.common.lookup.impl,
eu.esdihumboldt.hale.common.schema,
eu.esdihumboldt.hale.common.schema.geometry,
eu.esdihumboldt.hale.common.schema.io,
eu.esdihumboldt.hale.common.schema.io.impl,
eu.esdihumboldt.hale.common.schema.model,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,13 @@

import javax.xml.namespace.QName;

import eu.esdihumboldt.hale.common.instance.io.impl.AbstractInstanceWriter;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryCollection;

import eu.esdihumboldt.hale.common.core.io.report.IOReporter;
import eu.esdihumboldt.hale.common.instance.geometry.DefaultGeometryProperty;
import eu.esdihumboldt.hale.common.instance.io.impl.AbstractGeoInstanceWriter;
import eu.esdihumboldt.hale.common.instance.io.util.EnumWindingOrderTypes;
import eu.esdihumboldt.hale.common.instance.model.Group;
import eu.esdihumboldt.hale.common.instance.model.Instance;
import eu.esdihumboldt.hale.common.instance.model.InstanceCollection;
Expand All @@ -36,7 +42,7 @@
*
* @author Patrick Lieb
*/
public abstract class AbstractTableInstanceWriter extends AbstractInstanceWriter {
public abstract class AbstractTableInstanceWriter extends AbstractGeoInstanceWriter {

/**
* @see eu.esdihumboldt.hale.common.core.io.IOProvider#isCancelable()
Expand All @@ -57,11 +63,12 @@ public boolean isCancelable() {
* @param useSchema <code>true</code> if properties should be defined from
* the schema, otherwise <code>false</code> and properties are
* defined from the Instances
* @param reporter IOReporter
* @return a map of properties with string of localpart of the QName of the
* property as key
*/
protected Map<String, Object> getPropertyMap(Instance instance, List<String> headerRow,
boolean useSchema, boolean solveNestedProperties) {
boolean useSchema, boolean solveNestedProperties, IOReporter reporter) {
// properties of current instance
Iterable<QName> allProperties;
if (!useSchema) {
Expand All @@ -88,16 +95,16 @@ protected Map<String, Object> getPropertyMap(Instance instance, List<String> hea
// if property is an OInstance or OGroup, it's a nested property
if (solveNestedProperties && property instanceof Group) {
Group nextInstance = (Group) property;
iterateBuild(nextInstance, qname, headerRow, row, cellValue);
iterateBuild(nextInstance, qname, headerRow, row, cellValue, reporter);
}
else {
// add property with corresponding cellValue (localpart) to
// map
if (property instanceof Group && shouldBeDisplayed(property)) {
checkValue((Group) property, headerRow, row, cellValue);
checkValue((Group) property, headerRow, row, cellValue, reporter);
}
else {
addProperty(headerRow, row, property, cellValue);
addProperty(headerRow, row, property, cellValue, reporter);
}

}
Expand All @@ -115,17 +122,17 @@ else if (useSchema) {
// property
if (solveNestedProperties && property instanceof Group) {
Group nextInstance = (Group) property;
iterateBuild(nextInstance, qname, headerRow, row, cellValue);
iterateBuild(nextInstance, qname, headerRow, row, cellValue, reporter);
}
else {
// add property with corresponding cellValue (localpart)
// to
// map
if (property instanceof Group && shouldBeDisplayed(property)) {
checkValue((Group) property, headerRow, row, cellValue);
checkValue((Group) property, headerRow, row, cellValue, reporter);
}
else {
addProperty(headerRow, row, property, cellValue);
addProperty(headerRow, row, property, cellValue, reporter);
}
}
}
Expand All @@ -135,7 +142,7 @@ else if (useSchema) {
if (shouldBeDisplayed(property)) {
cellValue = qname.getLocalPart();
}
addProperty(headerRow, row, property, cellValue);
addProperty(headerRow, row, property, cellValue, reporter);
} // close else

} // close else-if
Expand All @@ -155,8 +162,8 @@ else if (useSchema) {
* @return a map of properties with string of localpart of the QName of the
* property as key
*/
protected Map<String, Object> getPropertyMap(TypeDefinition definition,
List<String> headerRow) {
protected Map<String, Object> getPropertyMap(TypeDefinition definition, List<String> headerRow,
IOReporter reporter) {
Iterable<QName> allProperties = DefinitionUtil.getAllProperties(definition).stream()
.map(def -> def.getName()).collect(Collectors.toList());
Map<String, Object> row = new HashMap<String, Object>();
Expand All @@ -167,7 +174,7 @@ protected Map<String, Object> getPropertyMap(TypeDefinition definition,
if (shouldBeDisplayed(property)) {
cellValue = qname.getLocalPart();
}
addProperty(headerRow, row, property, cellValue);
addProperty(headerRow, row, property, cellValue, reporter);

}

Expand All @@ -184,11 +191,11 @@ protected Map<String, Object> getPropertyMap(TypeDefinition definition,
* instance name/path
*/
private void iterateBuild(Group instance, QName qNameOfTheInstance, List<String> headerRow,
Map<String, Object> row, String propertyPath) {
Map<String, Object> row, String propertyPath, IOReporter reporter) {

if (shouldBeDisplayed(instance)) {
// check if the actual instance has a value an add it
checkValue(instance, headerRow, row, propertyPath);
checkValue(instance, headerRow, row, propertyPath, reporter);
}
// children properties of current instance
Iterable<QName> children = instance.getPropertyNames();
Expand All @@ -198,17 +205,19 @@ private void iterateBuild(Group instance, QName qNameOfTheInstance, List<String>
// only the first instance
Object child = instance.getProperty(qname)[0];
if (child instanceof Group && shouldBeDisplayed(child)) {

iterateBuild((Group) instance.getProperty(qname)[0], qname, headerRow, row,
propertyPath + "." + qname.getLocalPart());
propertyPath + "." + qname.getLocalPart(), reporter);
}
else if (child instanceof Group) {
// the child is a choice or packege, etc.
iterateBuild((Group) instance.getProperty(qname)[0], qname, headerRow, row,
propertyPath);
propertyPath, reporter);
}
else {
// child is an attribute
addProperty(headerRow, row, child, propertyPath + "." + qname.getLocalPart());
addProperty(headerRow, row, child, propertyPath + "." + qname.getLocalPart(),
reporter);
}
}

Expand All @@ -225,20 +234,40 @@ private boolean shouldBeDisplayed(Object obj) {
}

private void addProperty(List<String> headerRow, Map<String, Object> row, Object property,
String propertyTypeName) {
String propertyTypeName, IOReporter reporter) {
if (!headerRow.contains(propertyTypeName)) {
headerRow.add(propertyTypeName);
}

if (property instanceof DefaultGeometryProperty<?>) {
// Property is an instance of DefaultGeometryProperty<T>
DefaultGeometryProperty<?> geometryProperty = (DefaultGeometryProperty<?>) property;

Geometry geometry = geometryProperty.getGeometry();

// correct winding order as per right-hand rule, i.e.,
// exterior rings are counterclockwise, and holes are
// clockwise.
setWindingOrder(EnumWindingOrderTypes.counterClockwise);
property = unifyGeometry(geometry, reporter,
geometryProperty.getCRSDefinition().getCRS());

if (geometry instanceof GeometryCollection
&& ((GeometryCollection) geometry).getNumGeometries() == 1) {
property = geometryProperty.getGeometry().getGeometryN(0);
}
}

row.put(propertyTypeName, property);
}

// check if value of current property isn't null and add it
private void checkValue(Group group, List<String> headerRow, Map<String, Object> row,
String propertyTypeName) {
String propertyTypeName, IOReporter reporter) {
if (group instanceof Instance) {
Object value = ((Instance) group).getValue();
if (value != null) {
addProperty(headerRow, row, value, propertyTypeName);
addProperty(headerRow, row, value, propertyTypeName, reporter);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,13 +177,13 @@ private void addCSVFileByQName(IOReporter reporter, OutputStream outputStream,
// try to be sure resources are all closed after try-block
try (CSVWriter writer = new CSVWriter(
new OutputStreamWriter(new FileOutputStream(tempFile)), sep, quote, esc);) {
writeLine(solveNestedProperties, headerRow, instance, writer);
writeLine(solveNestedProperties, headerRow, instance, writer, reporter);

while (instanceIterator.hasNext()) {
Instance nextInst = instanceIterator.next();
if (nextInst.getDefinition().equals(definition)) {

writeLine(solveNestedProperties, headerRow, nextInst, writer);
writeLine(solveNestedProperties, headerRow, nextInst, writer, reporter);
}
}
}
Expand All @@ -203,12 +203,12 @@ public boolean isPassthrough() {

// write current instance to csv file
private void writeLine(boolean solveNestedProperties, List<String> headerRow, Instance instance,
CSVWriter writer) {
CSVWriter writer, IOReporter reporter) {
boolean useSchema = getParameter(InstanceTableIOConstants.USE_SCHEMA).as(Boolean.class,
true);
List<String> line = new ArrayList<String>();
Map<String, Object> row = super.getPropertyMap(instance, headerRow, useSchema,
solveNestedProperties);
solveNestedProperties, reporter);
for (String key : headerRow) {
Object entry = row.get(key);
line.add(getValueOfProperty(entry));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,12 +176,10 @@ class InstanceToJsonTest {
type: "Feature",
"@type": "city",
geometry: [
type: "MultiPoint",
type: "Point",
coordinates: [
[
49.8728337,
8.651
]
49.8728337,
8.651
]
],
properties: [
Expand All @@ -200,12 +198,10 @@ class InstanceToJsonTest {
type: "Feature",
"@type": "city",
geometry: [
type: "MultiPoint",
type: "Point",
coordinates: [
[
48.13722,
11.5755
]
48.13722,
11.5755
]
],
properties: [
Expand Down Expand Up @@ -272,12 +268,10 @@ class InstanceToJsonTest {
type: "Feature",
"@type": "city",
geometry: [
type: "MultiPoint",
type: "Point",
coordinates: [
[
49.8728337891123,
8.65122278912346
]
49.8728337891123,
8.65122278912346
]
],
properties: [
Expand All @@ -296,12 +290,10 @@ class InstanceToJsonTest {
type: "Feature",
"@type": "city",
geometry: [
type: "MultiPoint",
type: "Point",
coordinates: [
[
48.13722289876544,
11.57555687654323
]
48.13722289876544,
11.57555687654323
]
],
properties: [
Expand Down Expand Up @@ -368,12 +360,10 @@ class InstanceToJsonTest {
type: "Feature",
"@type": "city",
geometry: [
type: "MultiPoint",
type: "Point",
coordinates: [
[
49.87283378911236,
8.65122278912346
]
49.87283378911236,
8.65122278912346
]
],
properties: [
Expand All @@ -392,12 +382,10 @@ class InstanceToJsonTest {
type: "Feature",
"@type": "city",
geometry: [
type: "MultiPoint",
type: "Point",
coordinates: [
[
48.13722289876544,
11.57555687654323
]
48.13722289876544,
11.57555687654323
]
],
properties: [
Expand Down Expand Up @@ -464,12 +452,10 @@ class InstanceToJsonTest {
type: "Feature",
"@type": "city",
geometry: [
type: "MultiPoint",
type: "Point",
coordinates: [
[
49.8728337891123,
8.651222789123
]
49.8728337891123,
8.651222789123
]
],
properties: [
Expand All @@ -488,8 +474,8 @@ class InstanceToJsonTest {
type: "Feature",
"@type": "city",
geometry: [
type: "MultiPoint",
coordinates: [[48.137222, 11.575556]]
type: "Point",
coordinates: [48.137222, 11.575556]
],
properties: [
population: 1471508,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.geotools.geojson.geom.GeometryJSON;
import org.geotools.geometry.jts.JTS;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryCollection;
import org.opengis.referencing.operation.MathTransform;

import com.fasterxml.jackson.core.JsonFactory;
Expand Down Expand Up @@ -652,6 +653,11 @@ protected void writeGeometryValue(JsonGenerator jsonGen, GeometryProperty<?> geo
geom = WindingOrder.unifyWindingOrder(geomProp.getGeometry(), true,
geomProp.getCRSDefinition().getCRS());

if (geomProp.getGeometry() instanceof GeometryCollection
&& ((GeometryCollection) geomProp.getGeometry()).getNumGeometries() == 1) {
geom = geomProp.getGeometry().getGeometryN(0);
}

// FIXME what to do in case of an invalid geometry?

jsonGen.writeRawValue(geometryJson.toString(geom));
Expand Down
Loading

0 comments on commit 2ec4e30

Please sign in to comment.