diff --git a/src/main/java/org/heigit/bigspatialdata/ohsome/ohsomeApi/controller/executor/ElementsRequestExecutor.java b/src/main/java/org/heigit/bigspatialdata/ohsome/ohsomeApi/controller/executor/ElementsRequestExecutor.java
index 99b0489e..c47accda 100644
--- a/src/main/java/org/heigit/bigspatialdata/ohsome/ohsomeApi/controller/executor/ElementsRequestExecutor.java
+++ b/src/main/java/org/heigit/bigspatialdata/ohsome/ohsomeApi/controller/executor/ElementsRequestExecutor.java
@@ -3,6 +3,7 @@
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.EnumSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
@@ -11,6 +12,7 @@
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.heigit.bigspatialdata.ohsome.ohsomeApi.Application;
+import org.heigit.bigspatialdata.ohsome.ohsomeApi.controller.executor.ExecutionUtils.MatchType;
import org.heigit.bigspatialdata.ohsome.ohsomeApi.exception.BadRequestException;
import org.heigit.bigspatialdata.ohsome.ohsomeApi.inputProcessing.GeometryBuilder;
import org.heigit.bigspatialdata.ohsome.ohsomeApi.inputProcessing.InputProcessor;
@@ -38,6 +40,7 @@
import org.heigit.bigspatialdata.oshdb.api.mapreducer.MapAggregatorByTimestampAndIndex;
import org.heigit.bigspatialdata.oshdb.api.mapreducer.MapReducer;
import org.heigit.bigspatialdata.oshdb.api.object.OSMEntitySnapshot;
+import org.heigit.bigspatialdata.oshdb.osm.OSMEntity;
import org.heigit.bigspatialdata.oshdb.osm.OSMType;
import org.heigit.bigspatialdata.oshdb.util.OSHDBTag;
import org.heigit.bigspatialdata.oshdb.util.OSHDBTimestamp;
@@ -554,122 +557,6 @@ public static GroupByResponse executeCountPerimeterAreaGroupByType(
return response;
}
- /**
- * Performs a count|length|perimeter|area-ratio calculation.
- *
- * The other parameters are described in the
- * {@link org.heigit.bigspatialdata.ohsome.ohsomeApi.controller.dataAggregation.CountController#getCountRatio(String, String, String, String[], String[], String[], String[], String[], String, String[], String[], String[])
- * getCountRatio} method.
- *
- * @param requestResource Enum
defining the request type (COUNT, LENGTH, PERIMETER,
- * AREA).
- * @param rPs RequestParameters
object, which holds those parameters that are used in
- * every request.
- * @return {@link org.heigit.bigspatialdata.ohsome.ohsomeApi.output.dataAggregationResponse.DefaultAggregationResponse
- * DefaultAggregationResponse}
- */
- public static RatioResponse executeCountLengthPerimeterAreaRatio(RequestResource requestResource,
- RequestParameters rPs, String[] types2, String[] keys2, String[] values2)
- throws UnsupportedOperationException, Exception {
-
- long startTime = System.currentTimeMillis();
- ExecutionUtils exeUtils = new ExecutionUtils();
- SortedMap result1 = null;
- SortedMap result2 = null;
- MapReducer mapRed1 = null;
- MapReducer mapRed2 = null;
- InputProcessor iP = new InputProcessor();
- String description = null;
- String requestURL = null;
- DecimalFormat ratioDf = exeUtils.defineDecimalFormat("#.######");
- DecimalFormat df = exeUtils.defineDecimalFormat("#.##");
- RequestParameters rPs2 = new RequestParameters(rPs.isPost(), rPs.isSnapshot(), rPs.isDensity(),
- rPs.getBboxes(), rPs.getBcircles(), rPs.getBpolys(), types2, keys2, values2,
- rPs.getUserids(), rPs.getTime(), rPs.getShowMetadata());
- if (!rPs.isPost())
- requestURL = RequestInterceptor.requestUrl;
- mapRed1 = iP.processParameters(mapRed1, rPs);
- mapRed2 = iP.processParameters(mapRed2, rPs2);
- switch (requestResource) {
- case COUNT:
- result1 = mapRed1.aggregateByTimestamp().count();
- result2 = mapRed2.aggregateByTimestamp().count();
- break;
- case AREA:
- result1 = mapRed1.aggregateByTimestamp()
- .sum((SerializableFunction) snapshot -> {
- return Geo.areaOf(snapshot.getGeometry());
- });
- result2 = mapRed2.aggregateByTimestamp()
- .sum((SerializableFunction) snapshot -> {
- return Geo.areaOf(snapshot.getGeometry());
- });
- break;
- case LENGTH:
- result1 = mapRed1.aggregateByTimestamp()
- .sum((SerializableFunction) snapshot -> {
- return Geo.lengthOf(snapshot.getGeometry());
- });
- result2 = mapRed2.aggregateByTimestamp()
- .sum((SerializableFunction) snapshot -> {
- return Geo.lengthOf(snapshot.getGeometry());
- });
- break;
- case PERIMETER:
- result1 = mapRed1.aggregateByTimestamp()
- .sum((SerializableFunction) snapshot -> {
- if (snapshot.getGeometry() instanceof Polygonal)
- return Geo.lengthOf(snapshot.getGeometry().getBoundary());
- else
- return 0.0;
- });
- result2 = mapRed2.aggregateByTimestamp()
- .sum((SerializableFunction) snapshot -> {
- if (snapshot.getGeometry() instanceof Polygonal)
- return Geo.lengthOf(snapshot.getGeometry().getBoundary());
- else
- return 0.0;
- });
- break;
- }
- ElementsResult[] resultSet1 = new ElementsResult[result1.size()];
- int count = 0;
- for (Entry entry : result1.entrySet()) {
- resultSet1[count] =
- new ElementsResult(TimestampFormatter.getInstance().isoDateTime(entry.getKey()),
- entry.getValue().doubleValue());
- count++;
- }
- RatioResult[] resultSet = new RatioResult[result1.size()];
- count = 0;
- for (Entry entry : result2.entrySet()) {
- String date = resultSet1[count].getTimestamp();
- double ratio = (entry.getValue().doubleValue() / resultSet1[count].getValue());
- // in case ratio has the value "NaN", "Infinity", etc.
- try {
- ratio = Double.parseDouble(ratioDf.format(ratio));
- } catch (Exception e) {
- // do nothing --> just return ratio without rounding (trimming)
- }
- resultSet[count] =
- new RatioResult(date, Double.parseDouble(df.format(resultSet1[count].getValue())),
- Double.parseDouble(df.format(entry.getValue().doubleValue())), ratio);
- count++;
- }
- description = "Total " + requestResource.getLabel() + " of items in "
- + requestResource.getUnit()
- + " satisfying types2, keys2, values2 parameters (= value2 output) "
- + "within items selected by types, keys, values parameters (= value output) and ratio of value2:value.";
- Metadata metadata = null;
- if (iP.getShowMetadata()) {
- long duration = System.currentTimeMillis() - startTime;
- metadata = new Metadata(duration, description, requestURL);
- }
- RatioResponse response =
- new RatioResponse(new Attribution(url, text), Application.apiVersion, metadata, resultSet);
- return response;
- }
-
/**
* Performs a count-ratio calculation grouped by the boundary.
*
@@ -1137,4 +1024,158 @@ public static ShareGroupByBoundaryResponse executeCountLengthPerimeterAreaShareG
return response;
}
+ /**
+ * Performs a count|length|perimeter|area-share|ratio calculation.
+ *
+ * The other parameters are described in the
+ * {@link org.heigit.bigspatialdata.ohsome.ohsomeApi.controller.dataAggregation.CountController#getCountRatio(String, String, String, String[], String[], String[], String[], String[], String, String[], String[], String[])
+ * getCountRatio} method.
+ *
+ * @param requestResource Enum
defining the request type (COUNT, LENGTH, PERIMETER,
+ * AREA).
+ * @param rPs RequestParameters
object, which holds those parameters that are used in
+ * every request.
+ * @return {@link org.heigit.bigspatialdata.ohsome.ohsomeApi.output.dataAggregationResponse.DefaultAggregationResponse
+ * DefaultAggregationResponse}
+ */
+ public static RatioResponse executeCountLengthPerimeterAreaRatio(
+ RequestResource requestResource, RequestParameters rPs, String[] types2, String[] keys2,
+ String[] values2) throws UnsupportedOperationException, Exception {
+
+ long startTime = System.currentTimeMillis();
+ ExecutionUtils exeUtils = new ExecutionUtils();
+ values2 = exeUtils.ratioParamEvaluation(keys2, values2);
+ SortedMap, ? extends Number> result = null;
+ MapAggregatorByTimestampAndIndex preResult;
+ MapReducer mapRed = null;
+ InputProcessor iP = new InputProcessor();
+ String description = null;
+ String requestURL = null;
+ DecimalFormat df = exeUtils.defineDecimalFormat("#.##");
+ DecimalFormat ratioDf = exeUtils.defineDecimalFormat("#.######");
+ TagTranslator tt = DbConnData.tagTranslator;
+ rPs = iP.fillWithEmptyIfNull(rPs);
+ iP.processParameters(mapRed, rPs);
+ Integer[] keysInt1 = new Integer[rPs.getKeys().length];
+ Integer[] valuesInt1 = new Integer[rPs.getValues().length];
+ Integer[] keysInt2 = new Integer[keys2.length];
+ Integer[] valuesInt2 = new Integer[values2.length];
+ if (!rPs.isPost())
+ requestURL = RequestInterceptor.requestUrl;
+ for (int i = 0; i < rPs.getKeys().length; i++) {
+ keysInt1[i] = tt.getOSHDBTagKeyOf(rPs.getKeys()[i]).toInt();
+ if (rPs.getValues() != null && i < rPs.getValues().length)
+ valuesInt1[i] = tt.getOSHDBTagOf(rPs.getKeys()[i], rPs.getValues()[i]).getValue();
+ }
+ for (int i = 0; i < keys2.length; i++) {
+ keysInt2[i] = tt.getOSHDBTagKeyOf(keys2[i]).toInt();
+ if (values2 != null && i < values2.length)
+ valuesInt2[i] = tt.getOSHDBTagOf(keys2[i], values2[i]).getValue();
+ }
+ EnumSet osmTypes1 = iP.getOsmTypes();
+ EnumSet osmTypes2 = iP.checkTypes(types2);
+ EnumSet osmTypes = osmTypes1.clone();
+ osmTypes.addAll(osmTypes2);
+ String[] osmTypesString =
+ osmTypes.stream().map(OSMType::toString).map(String::toLowerCase).toArray(String[]::new);
+ mapRed = iP.processParameters(mapRed,
+ new RequestParameters(rPs.isPost(), rPs.isSnapshot(), rPs.isDensity(), rPs.getBboxes(),
+ rPs.getBcircles(), rPs.getBpolys(), osmTypesString, new String[] {}, new String[] {},
+ rPs.getUserids(), rPs.getTime(), rPs.getShowMetadata()));
+ mapRed = mapRed.where(entity -> {
+ boolean matches1 = exeUtils.entityMatches(entity, osmTypes1, keysInt1, valuesInt1);
+ boolean matches2 = exeUtils.entityMatches(entity, osmTypes2, keysInt2, valuesInt2);
+ return matches1 || matches2;
+ });
+ preResult = mapRed.aggregateByTimestamp().aggregateBy(f -> {
+ OSMEntity entity = f.getEntity();
+ boolean matches1 = exeUtils.entityMatches(entity, osmTypes1, keysInt1, valuesInt1);
+ boolean matches2 = exeUtils.entityMatches(entity, osmTypes2, keysInt2, valuesInt2);
+ if (matches1 && matches2)
+ return MatchType.MATCHESBOTH;
+ else if (matches1)
+ return MatchType.MATCHES1;
+ else if (matches2)
+ return MatchType.MATCHES2;
+ else
+ assert false : "MatchType matches none.";
+ // this should never be reached
+ return null;
+ }).zerofillIndices(
+ Arrays.asList(MatchType.MATCHESBOTH, MatchType.MATCHES1, MatchType.MATCHES2));
+ switch (requestResource) {
+ case COUNT:
+ result = preResult.count();
+ break;
+ case LENGTH:
+ result = preResult.sum((SerializableFunction) snapshot -> {
+ return Geo.lengthOf(snapshot.getGeometry());
+ });
+ break;
+ case PERIMETER:
+ result = preResult.sum((SerializableFunction) snapshot -> {
+ if (snapshot.getGeometry() instanceof Polygonal)
+ return Geo.lengthOf(snapshot.getGeometry().getBoundary());
+ else
+ return 0.0;
+ });
+ break;
+ case AREA:
+ result = preResult.sum((SerializableFunction) snapshot -> {
+ return Geo.areaOf(snapshot.getGeometry());
+ });
+ break;
+ }
+ int resultSize = result.size();
+ Double[] value1 = new Double[resultSize / 3];
+ Double[] value2 = new Double[resultSize / 3];
+ String[] timeArray = new String[resultSize / 3];
+ int value1Count = 0;
+ int value2Count = 0;
+ int matchesBothCount = 0;
+ // time and value extraction
+ for (Entry, ? extends Number> entry : result.entrySet()) {
+ if (entry.getKey().getOtherIndex() == MatchType.MATCHES2) {
+ timeArray[value2Count] =
+ TimestampFormatter.getInstance().isoDateTime(entry.getKey().getTimeIndex());
+ value2[value2Count] = Double.parseDouble(df.format(entry.getValue().doubleValue()));
+ value2Count++;
+ }
+ if (entry.getKey().getOtherIndex() == MatchType.MATCHES1) {
+ value1[value1Count] = Double.parseDouble(df.format(entry.getValue().doubleValue()));
+ value1Count++;
+ }
+ if (entry.getKey().getOtherIndex() == MatchType.MATCHESBOTH) {
+ value1[matchesBothCount] = value1[matchesBothCount]
+ + Double.parseDouble(df.format(entry.getValue().doubleValue()));
+ value2[matchesBothCount] = value2[matchesBothCount]
+ + Double.parseDouble(df.format(entry.getValue().doubleValue()));
+ matchesBothCount++;
+ }
+ }
+ RatioResult[] resultSet = new RatioResult[timeArray.length];
+ for (int i = 0; i < timeArray.length; i++) {
+ double ratio = value2[i] / value1[i];
+ // in case ratio has the values "NaN", "Infinity", etc.
+ try {
+ ratio = Double.parseDouble(ratioDf.format(ratio));
+ } catch (Exception e) {
+ // do nothing --> just return ratio without rounding (trimming)
+ }
+ resultSet[i] = new RatioResult(timeArray[i], value1[i], value2[i], ratio);
+ }
+ description = "Total " + requestResource.getLabel() + " of items in "
+ + requestResource.getUnit()
+ + " satisfying types2, keys2, values2 parameters (= value2 output) "
+ + "within items selected by types, keys, values parameters (= value output) and ratio of value2:value.";
+ Metadata metadata = null;
+ if (iP.getShowMetadata()) {
+ long duration = System.currentTimeMillis() - startTime;
+ metadata = new Metadata(duration, description, requestURL);
+ }
+ RatioResponse response =
+ new RatioResponse(new Attribution(url, text), Application.apiVersion, metadata, resultSet);
+ return response;
+ }
+
}
diff --git a/src/main/java/org/heigit/bigspatialdata/ohsome/ohsomeApi/controller/executor/ExecutionUtils.java b/src/main/java/org/heigit/bigspatialdata/ohsome/ohsomeApi/controller/executor/ExecutionUtils.java
index 2f62d5cc..a224f24c 100644
--- a/src/main/java/org/heigit/bigspatialdata/ohsome/ohsomeApi/controller/executor/ExecutionUtils.java
+++ b/src/main/java/org/heigit/bigspatialdata/ohsome/ohsomeApi/controller/executor/ExecutionUtils.java
@@ -3,6 +3,7 @@
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
+import java.util.EnumSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
@@ -19,6 +20,7 @@
import org.heigit.bigspatialdata.oshdb.api.object.OSMContribution;
import org.heigit.bigspatialdata.oshdb.api.object.OSMEntitySnapshot;
import org.heigit.bigspatialdata.oshdb.osm.OSMEntity;
+import org.heigit.bigspatialdata.oshdb.osm.OSMType;
import org.heigit.bigspatialdata.oshdb.util.geometry.Geo;
import org.heigit.bigspatialdata.oshdb.util.geometry.OSHDBGeometryBuilder;
import com.vividsolutions.jts.geom.Geometry;
@@ -53,7 +55,26 @@ public String[] shareParamEvaluation(String[] keys2, String[] values2) {
if (keys2 == null || keys2.length < 1)
throw new BadRequestException(
- "You need to define at least one key if you want to use /share.");
+ "You need to define at least one key if you want to use this resource.");
+ if (values2 == null)
+ values2 = new String[0];
+ if (keys2.length < values2.length)
+ throw new BadRequestException(
+ "There cannot be more input values in values2 than in keys2 as values2n must fit to keys2n.");
+
+ return values2;
+ }
+
+ /**
+ * Evaluates the keys2 and values2 String
arrays from /ratio requests.
+ *
+ * @param keys2 String
array containing the provided keys2 parameters.
+ * @param values2 String
array containing the provided values2 parameters.
+ * @return String
array containing the given (or an empty) values2 array, which is
+ * not null.
+ */
+ public String[] ratioParamEvaluation(String[] keys2, String[] values2) {
+
if (values2 == null)
values2 = new String[0];
if (keys2.length < values2.length)
@@ -93,17 +114,7 @@ public Geometry getGeometry(BoundaryType boundary, GeometryBuilder geomBuilder)
return geom;
}
- /**
- * Computes the result for the /count|length|perimeter|area/groupBy/boundary resources using the
- * map-reduce functions from the OSHDB.
- *
- * @param requestResource
- * @param bType
- * @param mapRed
- * @param geomBuilder
- * @return SortedMap
result object.
- * @throws Exception
- */
+ /** Computes the result for the /count|length|perimeter|area/groupBy/boundary resources. */
public SortedMap, ? extends Number> computeCountLengthPerimeterAreaGBB(
RequestResource requestResource, BoundaryType bType,
MapReducer extends OSHDBMapReducible> mapRed, GeometryBuilder geomBuilder,
@@ -155,19 +166,7 @@ public Geometry getGeometry(BoundaryType boundary, GeometryBuilder geomBuilder)
return result;
}
- /**
- * Computes the result for the /count/share/groupBy/boundary resource using the map-reduce
- * functions from the OSHDB.
- *
- * @param bType
- * @param mapRed
- * @param keysInt2
- * @param valuesInt2
- * @param geomBuilder
- * @return SortedMap
result object.
- * @throws UnsupportedOperationException
- * @throws Exception
- */
+ /** Computes the result for the /count/share/groupBy/boundary resources. */
public SortedMap>, Integer> computeCountShareGBB(
BoundaryType bType, MapReducer mapRed, Integer[] keysInt2,
Integer[] valuesInt2, GeometryBuilder geomBuilder)
@@ -217,20 +216,7 @@ public SortedMap>, Integer> comput
return result;
}
- /**
- * Computes the result for the /length|perimeter|area/share/groupBy/boundary resources using the
- * map-reduce functions from the OSHDB.
- *
- * @param requestResource
- * @param bType
- * @param mapRed
- * @param keysInt2
- * @param valuesInt2
- * @param geomBuilder
- * @return SortedMap
result object.
- * @throws UnsupportedOperationException
- * @throws Exception
- */
+ /** Computes the result for the /count|length|perimeter|area/share/groupBy/boundary resources. */
public SortedMap>, ? extends Number> computeCountLengthPerimeterAreaShareGBB(
RequestResource requestResource, BoundaryType bType, MapReducer mapRed,
Integer[] keysInt2, Integer[] valuesInt2, GeometryBuilder geomBuilder)
@@ -305,17 +291,51 @@ public SortedMap>, Integer> comput
}
return result;
}
-
- /**
- * Internal helper method to get the geometry from an OSMEntitySnapshot object.
- */
+
+ /** Compares an OSMType with an EnumSet of OSMTypes. */
+ public boolean isOSMType(EnumSet types, OSMType currentElementType) {
+
+ for (OSMType type : types)
+ if (currentElementType.equals(type)) {
+ return true;
+ }
+ return false;
+ }
+
+ /** Compares the OSM type and tag(s) of the given entity to the given types|tags. */
+ public boolean entityMatches(OSMEntity entity, EnumSet osmTypes, Integer[] keysInt,
+ Integer[] valuesInt) {
+
+ boolean matches = true;
+ if (osmTypes.contains(entity.getType())) {
+ for (int i = 0; i < keysInt.length; i++) {
+ boolean matchesTag;
+ if (i < valuesInt.length)
+ matchesTag = entity.hasTagValue(keysInt[i], valuesInt[i]);
+ else
+ matchesTag = entity.hasTagKey(keysInt[i]);
+ if (!matchesTag) {
+ matches = false;
+ break;
+ }
+ }
+ } else {
+ matches = false;
+ }
+ return matches;
+ }
+
+ /** Enum type used in /ratio computation. */
+ public enum MatchType {
+ MATCHES1, MATCHES2, MATCHESBOTH
+ }
+
+ /** Internal helper method to get the geometry from an OSMEntitySnapshot object. */
private Geometry getSnapshotGeom(OSHDBMapReducible f) {
return ((OSMEntitySnapshot) f).getGeometry();
}
- /**
- * Internal helper method to get the geometry from an OSMContribution object.
- */
+ /** Internal helper method to get the geometry from an OSMContribution object. */
private Geometry getContributionGeom(OSHDBMapReducible f) {
Geometry geom = ((OSMContribution) f).getGeometryAfter();
if (geom == null)