/* (c) 2016 Open Source Geospatial Foundation - all rights reserved * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.wps.gs; import net.sf.json.JSONObject; import org.geoserver.wps.ppio.CDataPPIO; import org.geotools.process.vector.AggregateProcess; import java.io.InputStream; import java.io.OutputStream; import java.util.HashMap; import java.util.Map; /** * <p> * Provides a JSON output for the aggregate process result. The result are encoded in a tabular format very similar * to the output of SQL query. * </p> * <p> * The tabular data is the composition of the group by attributes values and the aggregation functions results. * Both of these values appear in the order they are declared, the group by values appear first and the aggregation * values after. If there is no group by attributes, only the aggregations values appear. * </p> * <p>Follow some examples:</p> * <p>The max and min energy consumption:</p> * <pre> * <code>{ * "AggregationAttribute": "energy_consumption", * "AggregationFunctions": ["Max", "Min"], * "GroupByAttributes": ["building_type"], * "AggregationResults": [ * ["School", 500.0, 80.0], * ["Fabric", 850.0, 120.0] * ] * }</code> * </pre> * <p>The max and min energy consumption per building type and energy type:</p> * <pre> * <code>{ * "AggregationAttribute": "energy_consumption", * "AggregationFunctions": ["Max", "Min"], * "GroupByAttributes": ["building_type", "energy_type"], * "AggregationResults": [ * ["School", "Nuclear", 500.0, 220.0], * ["School", "Wind", 200.0, 120.0], * ["Fabric", "Nuclear", 230.0, 80.0], * ["Fabric", "Fuel", 850.0, 370.0] * ] * }</code> * </pre> */ public class AggregateProcessJSONPPIO extends CDataPPIO { protected AggregateProcessJSONPPIO() { super(AggregateProcess.Results.class, AggregateProcess.Results.class, "application/json"); } @Override public void encode(Object value, OutputStream output) throws Exception { AggregateProcess.Results processResult = (AggregateProcess.Results) value; Map<Object, Object> json = new HashMap<>(); // we encode the common parts regardless of the presence of group by attributes json.put("AggregationAttribute", processResult.getAggregateAttribute()); json.put("AggregationFunctions", extractAggregateFunctionsNames(processResult)); if (processResult.getGroupByAttributes() == null || processResult.getGroupByAttributes().isEmpty()) { // if there is no group by attributes we only to encode the aggregations function results json.put("GroupByAttributes", new String[0]); json.put("AggregationResults", new Number[][]{encodeSimpleResult(processResult)}); } else { // there is group by values so we need to encode all the grouped results json.put("GroupByAttributes", processResult.getGroupByAttributes().toArray()); json.put("AggregationResults", processResult.getGroupByResult().toArray()); } output.write(JSONObject.fromObject(json).toString().getBytes()); } /** * Helper method that encodes the result of an aggregator process when there is no group by attributes. * We encode the value of each aggregation function producing an output very similar of an SQL query result. * * @param processResult the result of the aggregator process * @return aggregation functions result values */ private Number[] encodeSimpleResult(AggregateProcess.Results processResult) { return processResult.getFunctions().stream() .map(function -> processResult.getResults().get(function)) .toArray(Number[]::new); } /** * Helper that extract the name of the aggregation functions. * * @param result the result of the aggregator process * @return an array that contain the aggregation functions names */ private String[] extractAggregateFunctionsNames(AggregateProcess.Results result) { return result.getFunctions().stream().map(Enum::name).toArray(String[]::new); } @Override public Object decode(InputStream input) throws Exception { throw new UnsupportedOperationException("JSON parsing is not supported"); } @Override public Object decode(String input) throws Exception { throw new UnsupportedOperationException("JSON parsing is not supported"); } @Override public final String getFileExtension() { return "json"; } }