Skip to content

Commit

Permalink
Add Union function
Browse files Browse the repository at this point in the history
  • Loading branch information
domi-b committed Dec 13, 2023
1 parent 8f22243 commit b432e6b
Show file tree
Hide file tree
Showing 3 changed files with 165 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,18 @@
import ch.interlis.ili2c.metamodel.TransferDescription;
import ch.interlis.ili2c.metamodel.Viewable;
import ch.interlis.iom.IomObject;
import ch.interlis.iom_j.Iom_jObject;
import ch.interlis.iox_j.jts.Iox2jtsException;
import ch.interlis.iox_j.jts.Jtsext2iox;
import ch.interlis.iox_j.validator.Validator;
import ch.interlis.iox_j.validator.Value;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Polygon;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.function.Function;

public final class EvaluationHelper {
Expand Down Expand Up @@ -88,4 +94,35 @@ public static Double sum(Collection<IomObject> items, Function<IomObject, Double
.reduce(0.0, Double::sum);
}

/**
* Converts a {@link MultiPolygon} into a MULTISURFACE {@link IomObject}.
*/
public static IomObject jts2multiSurface(MultiPolygon multiPolygon) throws Iox2jtsException {
List<Polygon> polygons = splitMultiPolygon(multiPolygon);

IomObject unionSurface = new Iom_jObject(Iom_jObject.MULTISURFACE, null);
for (Polygon polygon : polygons) {
IomObject multiSurfaceObject = Jtsext2iox.JTS2surface(polygon);

int surfaceCount = multiSurfaceObject.getattrvaluecount(Iom_jObject.MULTISURFACE_SURFACE);
for (int i = 0; i < surfaceCount; i++) {
IomObject surfaceObject = multiSurfaceObject.getattrobj(Iom_jObject.MULTISURFACE_SURFACE, i);
unionSurface.addattrobj(Iom_jObject.MULTISURFACE_SURFACE, surfaceObject);
}
}
return unionSurface;
}

/**
* Splits the {@link MultiPolygon} into a list of {@link Polygon}s.
*/
public static List<Polygon> splitMultiPolygon(MultiPolygon multiPolygon) {
int polygonCount = multiPolygon.getNumGeometries();
List<Polygon> polygons = new ArrayList<>(polygonCount);

for (int i = 0; i < polygonCount; i++) {
polygons.add((Polygon) multiPolygon.getGeometryN(i));
}
return polygons;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package ch.geowerkstatt.ilivalidator.extensions.functions;

import ch.ehi.basics.types.OutParam;
import ch.interlis.ili2c.metamodel.PathEl;
import ch.interlis.ili2c.metamodel.Viewable;
import ch.interlis.iom.IomObject;
import ch.interlis.iox.IoxException;
import ch.interlis.iox_j.jts.Iox2jtsException;
import ch.interlis.iox_j.jts.Iox2jtsext;
import ch.interlis.iox_j.jts.Jtsext2iox;
import ch.interlis.iox_j.validator.Value;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Polygon;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

public final class UnionIoxPlugin extends BaseInterlisFunction {
private static final Map<UnionSurfaceKey, IomObject> UNION_SURFACE_CACHE = new HashMap<>();

@Override
public String getQualifiedIliName() {
return "GeoW_FunctionsExt.Union";
}

@Override
protected Value evaluateInternal(String validationKind, String usageScope, IomObject contextObject, Value[] actualArguments) {
Value argObjects = actualArguments[0]; // OBJECTS OF ANYCLASS
Value argPath = actualArguments[1]; // TEXT

if (argObjects.isUndefined()) {
return Value.createSkipEvaluation();
}
if (argObjects.getComplexObjects() == null) {
return Value.createUndefined();
}

Collection<IomObject> surfaces;

if (argPath.isUndefined()) {
surfaces = argObjects.getComplexObjects();
} else {
Viewable contextClass = EvaluationHelper.getContextClass(td, contextObject, argObjects);
if (contextClass == null) {
throw new IllegalStateException("unknown class in " + usageScope);
}

PathEl[] attributePath = EvaluationHelper.getAttributePathEl(validator, contextClass, argPath);
surfaces = EvaluationHelper.evaluateAttributes(validator, argObjects, attributePath);
}

UnionSurfaceKey key = new UnionSurfaceKey(surfaces);
IomObject unionSurface = UNION_SURFACE_CACHE.computeIfAbsent(key, this::union);
if (unionSurface == null) {
return Value.createUndefined();
}
return new Value(Collections.singletonList(unionSurface));
}

private IomObject union(UnionSurfaceKey key) {
Collection<IomObject> surfaces = key.surfaces;

MultiPolygon[] polygons = surfaces.stream()
.map(surface -> {
try {
return Iox2jtsext.multisurface2JTS(surface, 0, new OutParam<>(), logger, 0, "warning");
} catch (IoxException e) {
logger.addEvent(logger.logErrorMsg("Could not convert surface to JTS"));
return null;
}
})
.filter(Objects::nonNull)
.toArray(MultiPolygon[]::new);

Geometry geometryCollection = new GeometryFactory().createGeometryCollection(polygons);
Geometry unionGeometry = geometryCollection.union();

try {
if (unionGeometry instanceof Polygon) {
return Jtsext2iox.JTS2surface((Polygon) unionGeometry);
} else if (unionGeometry instanceof MultiPolygon) {
return EvaluationHelper.jts2multiSurface((MultiPolygon) unionGeometry);
} else {
logger.addEvent(logger.logErrorMsg("Expected {0} or {1} but was {2}", Polygon.class.toString(), MultiPolygon.class.toString(), unionGeometry.getClass().toString()));
return null;
}
} catch (Iox2jtsException e) {
logger.addEvent(logger.logErrorMsg("Could not calculate {0}", this.getQualifiedIliName()));
return null;
}
}

private static final class UnionSurfaceKey {
private final Collection<IomObject> surfaces;

private UnionSurfaceKey(Collection<IomObject> surfaces) {
this.surfaces = surfaces;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof UnionSurfaceKey)) {
return false;
}
UnionSurfaceKey that = (UnionSurfaceKey) o;
return Objects.equals(surfaces, that.surfaces);
}

@Override
public int hashCode() {
return Objects.hash(surfaces);
}
}
}
6 changes: 6 additions & 0 deletions src/model/GeoW_FunctionsExt.ili
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,10 @@ MODEL GeoW_FunctionsExt
!!@ fn.return = "Boolean";
!!@ fn.since = "2022-12-05";
FUNCTION IsInsideExternalDataset (DatasetName: TEXT; Objects: TEXT; TestObject: OBJECT OF ANYCLASS; TestObjectgeometry: TEXT): BOOLEAN;

!!@ fn.description = "Fasst die Flächen-Geometrien aus der Eingabemenge zu einer Flächen-Geometrie zusammen. Für 'Objects' können Objekte oder Geometrien angegeben werden. Für 'AreaAttr' soll der Pfad zur Flächen-Geometrie in INTERLIS 2 Syntax angegeben werden. Falls 'Objects' bereits die Geometrien enthält, soll für 'AreaAttr' 'UNDEFINED' übergeben werden.";
!!@ fn.param = "Objects: Ausgangsobjekte oder Geometrien. AreaAttr: Pfad zum Geometrieattribut oder UNDEFINED";
!!@ fn.return = "Zusammengefasste Flächen-Geometrie";
!!@ fn.since = "2023-12-13";
FUNCTION Union (Objects: OBJECTS OF ANYCLASS; AreaAttr: TEXT): OBJECT OF ANYCLASS;
END GeoW_FunctionsExt.

0 comments on commit b432e6b

Please sign in to comment.