tim@352: package de.intevation.gnv.state.profile.horizontal;
tim@352:
sascha@519: import com.vividsolutions.jts.geom.Coordinate;
tim@352:
tim@352: import de.intevation.artifactdatabase.Config;
sascha@357:
sascha@519: import de.intevation.artifacts.CallContext;
sascha@519:
tim@352: import de.intevation.gnv.artifacts.cache.CacheFactory;
sascha@357:
sascha@519: import de.intevation.gnv.artifacts.context.GNVArtifactContext;
sascha@519:
sascha@519: import de.intevation.gnv.chart.Chart;
sascha@519: import de.intevation.gnv.chart.ChartLabels;
sascha@519: import de.intevation.gnv.chart.HorizontalCrossProfileChart;
sascha@519:
ingo@423: import de.intevation.gnv.geobackend.base.DefaultResult;
ingo@423: import de.intevation.gnv.geobackend.base.DefaultResultDescriptor;
ingo@423: import de.intevation.gnv.geobackend.base.Result;
ingo@423: import de.intevation.gnv.geobackend.base.ResultDescriptor;
sascha@519:
ingo@423: import de.intevation.gnv.geobackend.base.query.QueryExecutor;
ingo@423: import de.intevation.gnv.geobackend.base.query.QueryExecutorFactory;
sascha@519:
ingo@423: import de.intevation.gnv.geobackend.base.query.exception.QueryException;
sascha@519:
ingo@423: import de.intevation.gnv.math.Interpolation2D;
ingo@423: import de.intevation.gnv.math.LinearMetrics;
ingo@423: import de.intevation.gnv.math.Point2d;
ingo@365:
sascha@440: import de.intevation.gnv.state.InputData;
sascha@440:
ingo@423: import de.intevation.gnv.utils.DistanceCalculator;
sascha@519: import de.intevation.gnv.utils.StringUtils;
ingo@420: import de.intevation.gnv.utils.WKTUtils;
sascha@439:
sascha@519: import java.util.ArrayList;
sascha@519: import java.util.Arrays;
sascha@519: import java.util.Collection;
sascha@519: import java.util.List;
sascha@519: import java.util.Locale;
sascha@443:
sascha@519: import org.apache.log4j.Logger;
sascha@357:
ingo@365: import org.jfree.chart.ChartTheme;
ingo@365:
sascha@519: import org.w3c.dom.Node;
ingo@423:
tim@352: /**
ingo@811: * This OutputState
is used for 'Horizontalschnitt' products.
sascha@835: *
sascha@780: * @author Tim Englich
sascha@780: * @author Ingo Weinzierl
sascha@780: * @author Sascha L. Teichmann
tim@352: */
sascha@528: public class HorizontalProfileMeshCrossOutputState
ingo@856: extends HorizontalProfileMeshOutputState
sascha@528: {
tim@352:
sascha@528: public static final boolean USE_INDEX_BUFFER =
sascha@528: Boolean.getBoolean("gnv.horizontal.profile.mesh.cross.index.buffer");
sascha@528:
tim@352: private static final long serialVersionUID = 2205958041745637263L;
sascha@778:
tim@352: /**
tim@352: * the logger, used to log exceptions and additonaly information
tim@352: */
sascha@528: private static Logger log = Logger.getLogger(
sascha@528: HorizontalProfileMeshCrossOutputState.class);
tim@352:
tim@352: private String ijkQueryID = null;
sascha@778:
tim@352: /**
tim@352: * Constructor
tim@352: */
tim@352: public HorizontalProfileMeshCrossOutputState() {
tim@352: super();
tim@352: }
sascha@778:
ingo@811:
tim@352: @Override
tim@352: public void setup(Node configuration) {
tim@352: super.setup(configuration);
tim@352: this.ijkQueryID = Config.getStringXPath(configuration,"queryID-ijk");
sascha@778:
tim@352: }
tim@352:
ingo@365:
ingo@811: /**
ingo@811: * This method creates a chart and returns it.
ingo@811: *
ingo@811: * @param chartLables Labels used to decorate the chart.
ingo@811: * @param theme The theme used to adjust the look of the chart.
ingo@811: * @param parameters A collection with parameters this chart contains.
ingo@811: * @param measurements A collection with measurement this chart contains.
ingo@811: * @param dates A collection with dates this chart contains.
ingo@811: * @param result The data collection used to be displayed in this chart.
ingo@811: * @param locale The Locale used to determine the language.
ingo@811: * @param uuid The uuid of the current artifact.
ingo@811: * @param linesVisible A boolean property to determine the visibility of
ingo@811: * lines connecting two points in a chart (not used in this chart type).
ingo@811: * @param shapesVisible A boolean property to determine the visiblity of
ingo@811: * datapoints in this chart (not used in this chart type).
ingo@811: * @param callContext The CallContext object.
ingo@811: * @return a HorizontalCrossProfileChart
.
ingo@811: */
ingo@365: @Override
ingo@365: protected Chart getChart(
ingo@365: ChartLabels chartLables,
ingo@365: ChartTheme theme,
ingo@365: Collection parameters,
ingo@365: Collection measurements,
ingo@365: Collection dates,
ingo@429: Object result,
ingo@365: Locale locale,
ingo@365: String uuid,
ingo@365: boolean linesVisible,
sascha@439: boolean shapesVisible,
sascha@439: CallContext callContext
ingo@365: ) {
ingo@365: Chart chart = null;
ingo@365: if (CACHE_CHART) {
ingo@365: log.info("Try to get horizontalprofilemeshcross chart from cache.");
sascha@439: chart = (Chart) getChartFromCache(uuid, callContext);
ingo@365: }
ingo@365:
ingo@365: if (chart != null)
ingo@365: return chart;
ingo@365:
ingo@365: log.info("Chart not in cache yet.");
ingo@365: chart = new HorizontalCrossProfileChart(
ingo@365: chartLables,
ingo@365: theme,
ingo@365: parameters,
ingo@365: measurements,
ingo@365: dates,
ingo@429: (Collection)result,
ingo@365: null,
ingo@365: locale,
ingo@365: linesVisible,
ingo@365: shapesVisible
ingo@365: );
ingo@365: chart.generateChart();
ingo@365:
ingo@365: if (CACHE_CHART) {
ingo@365: log.info("Put chart into cache.");
ingo@365: purifyChart(chart, uuid);
ingo@365: }
ingo@365:
ingo@365: return chart;
ingo@365: }
ingo@365:
sascha@443: private static int numSamples(CallContext callContext) {
sascha@443: GNVArtifactContext context =
sascha@443: (GNVArtifactContext)callContext.globalContext();
sascha@443: Integer samples = (Integer)context.get(
sascha@443: GNVArtifactContext.HORIZONTAL_CROSS_SECTION_PROFILE_SAMPLES_KEY);
sascha@443: return samples != null
sascha@443: ? samples.intValue()
sascha@443: : GNVArtifactContext.DEFAULT_HORIZONTAL_CROSS_SECTION_PROFILE_SAMPLES;
sascha@443: }
sascha@443:
tim@352: @Override
sascha@439: protected Object getChartResult(String uuid, CallContext callContext) {
ingo@365: log.debug("HorizontalProfileMeshCrossOutputState.getChartResult");
tim@352: Collection result = null;
tim@352: if (CacheFactory.getInstance().isInitialized()) {
tim@352: String key = uuid + super.getID();
tim@352: log.debug("Hash for Queryelements: " + key);
tim@352: net.sf.ehcache.Element value = CacheFactory.getInstance().getCache().get(key);
tim@352: if (value != null) {
tim@352: result = (Collection) (value.getObjectValue());
tim@352: }else{
ingo@423:
sascha@440: InputData meshLine = inputData.get("mesh_linestring");
sascha@440: InputData meshId = inputData.get("meshid");
ingo@423:
sascha@440: if (meshLine == null) {
sascha@440: log.error("mesh_linestring is not defined");
sascha@440: throw new IllegalStateException("missing mesh_linestring");
sascha@440: }
ingo@423:
sascha@440: if (meshId == null) {
sascha@440: log.error("meshid is not defined");
sascha@440: throw new IllegalStateException("missing meshid");
sascha@440: }
ingo@423:
sascha@440: Coordinate [] coords = WKTUtils.toCoordinates(
sascha@440: meshLine.getValue());
sascha@440:
sascha@440: if (coords == null) {
sascha@440: throw new IllegalStateException("cannot read coordinates");
sascha@440: }
sascha@440:
sascha@440: try {
sascha@528: String additionWhere = USE_INDEX_BUFFER
sascha@528: ? WKTUtils.worldCoordinatesToIndex(
sascha@440: coords,
sascha@440: result,
sascha@440: meshId.getValue(),
sascha@528: ijkQueryID)
sascha@528: : WKTUtils.TRUE_EXPRESSION;
sascha@440:
sascha@440: String[] addedFilterValues = StringUtils.append(
sascha@440: generateFilterValuesFromInputData(),
sascha@440: additionWhere);
sascha@440:
sascha@440: QueryExecutor queryExecutor = QueryExecutorFactory
sascha@440: .getInstance()
sascha@440: .getQueryExecutor();
sascha@440:
sascha@440: result = process(
sascha@440: Arrays.asList(coords),
sascha@443: numSamples(callContext),
sascha@440: queryExecutor.executeQuery(
sascha@440: queryID,
sascha@440: addedFilterValues));
sascha@440: }
sascha@440: catch (QueryException e) {
sascha@440: log.error(e,e);
tim@352: }
ingo@423:
tim@352: if (CacheFactory.getInstance().isInitialized()) {
tim@352: CacheFactory.getInstance().getCache().put(new net.sf.ehcache.Element(key, result));
tim@352: }
ingo@423:
tim@352: }
tim@352: }
tim@352: return result;
tim@352: }
tim@352:
ingo@365:
ingo@811: /**
ingo@811: * Prepares the input data for chart creation.
ingo@811: *
ingo@811: * @param path The coordinates describing the path the data is processed
ingo@811: * for.
ingo@811: * @param numSamples Number of samples.
ingo@811: * @param input The input data.
ingo@811: * @return finalized data ready for chart creation.
ingo@811: */
ingo@423: public static Collection process(
ingo@423: List path,
sascha@443: int numSamples,
ingo@423: Collection input
ingo@423: ) {
sascha@443: boolean debug = log.isDebugEnabled();
sascha@443:
sascha@443: if (debug) {
sascha@443: log.debug("--- number of points before processing: " + input.size());
sascha@443: log.debug(" number samples: " + numSamples);
sascha@443: }
sascha@443:
ingo@423: ArrayList output = new ArrayList();
ingo@423:
ingo@423: Result last = null;
ingo@423:
ingo@423: int [] diffColums = null;
ingo@423:
ingo@423: SectionHandler sectionHandler = null;
ingo@423:
ingo@423: for (Result result: input) {
ingo@423:
ingo@423: if (sectionHandler == null) {
ingo@423:
ingo@423: ResultDescriptor rd = result.getResultDescriptor();
ingo@423: diffColums = rd.getColumnIndices(DIFF_COLUMS);
ingo@423: int columns = rd.getColumnCount();
ingo@423:
ingo@423: DefaultResultDescriptor resultDescriptor =
ingo@423: new DefaultResultDescriptor();
ingo@423:
ingo@423: for (int j = 0; j < columns; ++j) {
ingo@423: String columnName = rd.getColumnName(j);
sascha@439: if (!StringUtils.contains(COLUMN_BLACKLIST, columnName)) {
ingo@423: resultDescriptor.addColumn(
ingo@423: columnName,
ingo@423: rd.getColumnClassName(j));
ingo@423: }
ingo@423: }
ingo@423:
ingo@423: sectionHandler = new SectionHandler(
ingo@423: path,
sascha@443: numSamples,
ingo@423: output,
ingo@423: resultDescriptor);
ingo@423:
ingo@423: sectionHandler.setPrototyp(result);
ingo@423: }
ingo@423:
ingo@423: if (last != null && WKTUtils.different(last, result, diffColums)) {
ingo@423: sectionHandler.finish();
ingo@423: sectionHandler.setPrototyp(result);
ingo@423: }
ingo@423:
ingo@423: sectionHandler.handle(result);
ingo@423:
ingo@423: last = result;
ingo@423: }
ingo@423:
ingo@423: if (sectionHandler != null) {
ingo@423: sectionHandler.finish();
ingo@423: }
ingo@423:
sascha@443: if (debug) {
sascha@443: log.debug("--- number of points after processing: " + output.size());
sascha@443: }
ingo@423:
ingo@423: return output;
ingo@423: }
ingo@423:
ingo@423:
ingo@423: private static final String [] DIFF_COLUMS = {
ingo@423: "GROUP1",
ingo@423: "GROUP2",
ingo@423: "GROUP3"
ingo@423: };
ingo@423:
ingo@423: private static final String [] COLUMN_BLACKLIST = {
ingo@423: "MEDIAN.MESHPOINT.JPOSITION",
ingo@423: "MEDIAN.MESHPOINT.IPOSITION"
ingo@423: };
ingo@423:
ingo@423: public static final double EPSILON = 1e-5d;
ingo@423:
ingo@423: public static final class SectionHandler
ingo@423: implements Interpolation2D.Consumer
ingo@423: {
ingo@423: private ArrayList points;
ingo@423: private List path;
ingo@423: private Collection output;
ingo@423: private Result prototyp;
ingo@423: private ResultDescriptor descriptor;
ingo@423: private boolean lastWasSuccess;
sascha@443: private int numSamples;
ingo@423:
ingo@423: public SectionHandler() {
ingo@423: }
ingo@423:
ingo@423: public SectionHandler(
ingo@423: List path,
sascha@443: int numSamples,
ingo@423: Collection output,
ingo@423: ResultDescriptor descriptor
ingo@423: ) {
ingo@423: this.path = path;
sascha@443: this.numSamples = numSamples;
ingo@423: this.output = output;
ingo@423: this.descriptor = descriptor;
ingo@423: points = new ArrayList();
ingo@423: lastWasSuccess = true;
ingo@423: }
ingo@423:
ingo@423: public void finish() {
ingo@423: if (!points.isEmpty()) {
ingo@423: double distance = WKTUtils.toKM(
ingo@423: DistanceCalculator.calculateDistance(path));
ingo@423:
ingo@423: if (distance > EPSILON) {
ingo@423:
ingo@423: Interpolation2D.interpolate(
ingo@423: path,
ingo@423: points,
ingo@423: 0d,
ingo@423: distance,
sascha@443: numSamples,
ingo@423: LinearMetrics.INSTANCE,
ingo@423: this);
ingo@423: }
ingo@423:
ingo@423: points.clear();
ingo@423: }
ingo@423: lastWasSuccess = true;
ingo@423: }
ingo@423:
ingo@423: public void setPrototyp(Result prototyp) {
ingo@423: this.prototyp = prototyp;
ingo@423: }
ingo@423:
ingo@423: public void handle(Result result) {
ingo@423: Coordinate coordinate =
ingo@423: WKTUtils.toCoordinate(result.getString("SHAPE"));
ingo@423: double value = result.getDouble("YORDINATE");
sascha@519: int iPos = result.getInteger("IPOSITION");
sascha@519: int jPos = result.getInteger("JPOSITION");
ingo@423: Point2d p = new Point2d(
ingo@423: coordinate.x,
ingo@423: coordinate.y,
ingo@423: value,
ingo@423: iPos, jPos);
ingo@423: points.add(p);
ingo@423: }
ingo@423:
ingo@423: public void interpolated(Coordinate coordinate, boolean success) {
sascha@425:
sascha@425: if (!success && !lastWasSuccess) {
sascha@425: // only insert null if last was valid.
sascha@425: // This prevents flooding the result set with nulls
sascha@425: // if interpolating over a large gap.
sascha@425: return;
sascha@425: }
sascha@425:
ingo@423: DefaultResult result = new DefaultResult(descriptor);
ingo@423: ResultDescriptor pd = prototyp.getResultDescriptor();
ingo@423:
ingo@423: int pcolums = pd.getColumnCount();
ingo@423: for (int i = 0, j = 0; i < pcolums; ++i) {
ingo@423: String colname = pd.getColumnName(i);
sascha@439: if (StringUtils.contains(COLUMN_BLACKLIST, colname)) {
ingo@423: continue;
ingo@423: }
ingo@423: if (colname.equals("SHAPE")) {
ingo@423: result.addColumnValue(j, WKTUtils.toWKT(coordinate));
ingo@423: }
ingo@423: else if (colname.equals("YORDINATE")) {
sascha@425: result.addColumnValue(j, success
sascha@425: ? Double.valueOf(coordinate.z)
sascha@425: : null);
ingo@423: }
ingo@423: else {
ingo@423: result.addColumnValue(j, prototyp.getObject(i));
ingo@423: }
ingo@423: ++j;
ingo@423: }
ingo@423: output.add(result);
ingo@423: lastWasSuccess = success;
ingo@423: }
ingo@423: }
tim@352: }
sascha@836: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :