comparison gnv-artifacts/src/main/java/de/intevation/gnv/state/profile/horizontal/HorizontalProfileMeshCrossOutputState.java @ 420:c6a287398379

Outsourcing of some methods for preparing results for chart creation. gnv-artifacts/trunk@468 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Ingo Weinzierl <ingo.weinzierl@intevation.de>
date Mon, 21 Dec 2009 15:57:04 +0000
parents 6eae1efb5fc3
children 2402173a1490
comparison
equal deleted inserted replaced
419:1e192ea34e80 420:c6a287398379
1 /** 1 /**
2 * 2 *
3 */ 3 */
4 package de.intevation.gnv.state.profile.horizontal; 4 package de.intevation.gnv.state.profile.horizontal;
5 5
6 import java.util.ArrayList;
7 import java.util.Arrays;
8 import java.util.Collection; 6 import java.util.Collection;
9 import java.util.List;
10 import java.util.Locale; 7 import java.util.Locale;
11 8
12 import org.apache.log4j.Logger; 9 import org.apache.log4j.Logger;
13 import org.w3c.dom.Node; 10 import org.w3c.dom.Node;
14 11
15 import com.vividsolutions.jts.geom.Coordinate;
16 import com.vividsolutions.jts.geom.Point;
17 import com.vividsolutions.jts.geom.LineString;
18
19 import com.vividsolutions.jts.io.ParseException; 12 import com.vividsolutions.jts.io.ParseException;
20 import com.vividsolutions.jts.io.WKTReader;
21 13
22 import de.intevation.artifactdatabase.Config; 14 import de.intevation.artifactdatabase.Config;
23 15
24 import de.intevation.gnv.artifacts.cache.CacheFactory; 16 import de.intevation.gnv.artifacts.cache.CacheFactory;
25 17
26 import de.intevation.gnv.chart.Chart; 18 import de.intevation.gnv.chart.Chart;
27 import de.intevation.gnv.chart.ChartLabels; 19 import de.intevation.gnv.chart.ChartLabels;
28 import de.intevation.gnv.chart.HorizontalCrossProfileChart; 20 import de.intevation.gnv.chart.HorizontalCrossProfileChart;
29 21
30 import de.intevation.gnv.geobackend.base.DefaultResultDescriptor;
31 import de.intevation.gnv.geobackend.base.ResultDescriptor;
32 import de.intevation.gnv.geobackend.base.DefaultResult;
33 import de.intevation.gnv.geobackend.base.Result; 22 import de.intevation.gnv.geobackend.base.Result;
34 23
35 import de.intevation.gnv.math.Point2d; 24 import de.intevation.gnv.geobackend.base.query.exception.QueryException;
36 import de.intevation.gnv.math.Interpolation2D;
37 25
38 import de.intevation.gnv.geobackend.base.query.QueryExecutor; 26 import de.intevation.gnv.utils.WKTUtils;
39 import de.intevation.gnv.geobackend.base.query.QueryExecutorFactory;
40 import de.intevation.gnv.geobackend.base.query.exception.QueryException;
41 import de.intevation.gnv.geobackend.sde.datasources.ResultSet;
42
43
44 import de.intevation.gnv.utils.IndexBuffer;
45 import de.intevation.gnv.utils.DistanceCalculator;
46
47 import de.intevation.gnv.math.LinearFunction;
48 import de.intevation.gnv.math.LinearMetrics;
49
50 import org.apache.commons.math.optimization.fitting.CurveFitter;
51
52 import org.apache.commons.math.optimization.general.GaussNewtonOptimizer;
53 import org.apache.commons.math.optimization.OptimizationException;
54
55 import org.apache.commons.math.FunctionEvaluationException;
56 27
57 import org.jfree.chart.ChartTheme; 28 import org.jfree.chart.ChartTheme;
58 29
59 /** 30 /**
60 * @author Tim Englich <tim.englich@intevation.de> 31 * @author Tim Englich <tim.englich@intevation.de>
153 }else{ 124 }else{
154 125
155 if (this.inputData.containsKey("mesh_linestring")){ 126 if (this.inputData.containsKey("mesh_linestring")){
156 127
157 try { 128 try {
158 // 1. IJK Anfragen für Stuetzpunkte im Netz ausführen. 129 result = WKTUtils.worldCoordinatesToIndex(
159 LineString ls = (LineString)new WKTReader().read(this.inputData 130 result,
160 .get("mesh_linestring") 131 inputData,
161 .getValue()); 132 ijkQueryID,
162 Coordinate[] coords = ls.getCoordinates(); 133 queryID,
163 134 generateFilterValuesFromInputData()
164 List<java.awt.Point> points = new ArrayList<java.awt.Point>(coords.length); 135 );
165
166 String meshid = this.inputData.get("meshid").getValue();
167 QueryExecutor queryExecutor = QueryExecutorFactory
168 .getInstance()
169 .getQueryExecutor();
170
171 ArrayList missingPoints = new ArrayList();
172
173 String additionWhere = "FEATUREID=FEATUREID";
174
175 for (int i = 0; i < coords.length; i++) {
176
177 String wkt = toWKT(coords[i]);
178
179 result = queryExecutor.executeQuery(this.ijkQueryID,
180 new String[]{meshid,wkt});
181 if (!result.isEmpty()){
182 Result resultValue = result.iterator().next();
183 int iPos = resultValue.getInteger(1);
184 int jPos = resultValue.getInteger(0);
185 log.debug("Found Pos "+iPos+"/"+jPos +" for "+wkt);
186 points.add(i, new java.awt.Point(iPos,jPos));
187 }else{
188 log.debug("No i/j Pos found for "+wkt);
189 missingPoints.add(new Object [] { Integer.valueOf(i), coords[i] });
190 points.add(i, null);
191 // Special Case no i,j found for Coordinate
192 }
193 }
194
195 if (missingPoints.size() == coords.length) {
196 log.debug("cannot create index buffer");
197 }
198 else { // generate index filter
199 boolean remainsMissingPoints = !missingPoints.isEmpty();
200
201 if (remainsMissingPoints) {
202 // try to guess the missing (i, j)
203 CurveFitter iFitter = new CurveFitter(new GaussNewtonOptimizer(true));
204 CurveFitter jFitter = new CurveFitter(new GaussNewtonOptimizer(true));
205
206 for (int i = 0, N = points.size(); i < N; ++i) {
207 java.awt.Point p = (java.awt.Point)points.get(i);
208 if (p != null) {
209 Coordinate coord = coords[i];
210 iFitter.addObservedPoint(coord.x, p.x);
211 jFitter.addObservedPoint(coord.y, p.y);
212 }
213 }
214 try {
215 // XXX: Assumption: (i, j) are created by componentwise linear function.
216 // This is surely not correct because (x, y) are in a ellipsoid projection.
217 // TODO: use ellipsoid functions and fit with Levenberg Marquardt.
218 double [] iParams = iFitter.fit(
219 LinearFunction.INSTANCE, new double [] { 1d, 1d });
220
221 double [] jParams = jFitter.fit(
222 LinearFunction.INSTANCE, new double [] { 1d, 1d });
223
224 for (int i = missingPoints.size()-1; i >= 0; --i) {
225 Object [] a = (Object [])missingPoints.get(i);
226 Coordinate coord = (Coordinate)a[1];
227 int pi = (int)Math.round(iParams[0]*coord.x + iParams[1]);
228 int pj = (int)Math.round(jParams[0]*coord.y + jParams[1]);
229 points.set(
230 ((Integer)a[0]).intValue(),
231 new java.awt.Point(pi, pj));
232 }
233
234 remainsMissingPoints = false; // we filled the gaps
235 }
236 catch (FunctionEvaluationException fee) {
237 log.error(fee);
238 }
239 catch (OptimizationException oe) {
240 log.error(oe);
241 }
242 }
243
244 if (!remainsMissingPoints) {
245 // TODO: Make Tablenames and Columns Configurable
246 IndexBuffer ib = new IndexBuffer(
247 points,
248 "MEDIAN.MESHPOINT.IPOSITION",
249 "MEDIAN.MESHPOINT.JPOSITION" );
250 additionWhere = ib.toWhereClause();
251 log.debug("Additional Where Clause = "+additionWhere);
252 // 2. Aus diesen Stuetzpunkten den Resultset generieren.
253 }
254 } // if generate index filter
255
256 String[] filterValues = this.generateFilterValuesFromInputData();
257 String[] addedFilterValues = new String[filterValues.length+1];
258 System.arraycopy(filterValues, 0, addedFilterValues, 0, filterValues.length);
259 addedFilterValues[filterValues.length] = additionWhere;
260
261 result = process(
262 Arrays.asList(coords),
263 queryExecutor.executeQuery(
264 this.queryID,
265 addedFilterValues));
266
267 } catch (ParseException e) { 136 } catch (ParseException e) {
268 log.error(e,e); 137 log.error(e,e);
269 }catch (QueryException e) { 138 }catch (QueryException e) {
270 log.error(e,e); 139 log.error(e,e);
271 } 140 }
288 log.debug("create chart subtitle for horizontal crossprofile charts."); 157 log.debug("create chart subtitle for horizontal crossprofile charts.");
289 String subtitle = createTimePeriod(locale, uuid); 158 String subtitle = createTimePeriod(locale, uuid);
290 159
291 return subtitle; 160 return subtitle;
292 } 161 }
293
294 private static final String [] COLUMN_BLACKLIST = {
295 "MEDIAN.MESHPOINT.JPOSITION",
296 "MEDIAN.MESHPOINT.IPOSITION"
297 };
298
299 private static final boolean blacklisted(String column) {
300 for (int i = 0; i < COLUMN_BLACKLIST.length; ++i) {
301 if (COLUMN_BLACKLIST.equals(column)) {
302 return true;
303 }
304 }
305 return false;
306 }
307
308 private static boolean different(Result a, Result b, int [] indices) {
309 for (int i = 0; i < indices.length; ++i) {
310 String oa = a.getString(indices[i]);
311 String ob = b.getString(indices[i]);
312
313 if (oa == null && ob == null) {
314 continue;
315 }
316
317 if (oa == null || ob == null) {
318 return true;
319 }
320
321 if (!oa.equals(ob)) {
322 if (log.isDebugEnabled()) {
323 log.debug("+++++++++++++++ differs ++++++++++++++");
324 log.debug(" " + oa + " != " + ob);
325 }
326 return true;
327 }
328 }
329 return false;
330 }
331
332 private static final String [] DIFF_COLUMS = {
333 "GROUP1",
334 "GROUP2",
335 "GROUP3"
336 };
337
338 public static final class SectionHandler
339 implements Interpolation2D.Consumer
340 {
341 private ArrayList<Point2d> points;
342 private List<Coordinate> path;
343 private Collection<Result> output;
344 private Result prototyp;
345 private ResultDescriptor descriptor;
346 private boolean lastWasSuccess;
347
348 public SectionHandler() {
349 }
350
351 public SectionHandler(
352 List<Coordinate> path,
353 Collection<Result> output,
354 ResultDescriptor descriptor
355 ) {
356 this.path = path;
357 this.output = output;
358 this.descriptor = descriptor;
359 points = new ArrayList<Point2d>();
360 lastWasSuccess = true;
361 }
362
363 public void finish() {
364 if (!points.isEmpty()) {
365 double distance = toKM(
366 DistanceCalculator.calculateDistance(path));
367
368 if (distance > EPSILON) {
369
370 Interpolation2D.interpolate(
371 path,
372 points,
373 0d,
374 distance,
375 INTERPOLATION_STEPS,
376 LinearMetrics.INSTANCE,
377 this);
378 }
379
380 points.clear();
381 }
382 lastWasSuccess = true;
383 }
384
385 public void setPrototyp(Result prototyp) {
386 this.prototyp = prototyp;
387 }
388
389 public void handle(Result result) {
390 Coordinate coordinate =
391 toCoordinate(result.getString("SHAPE"));
392 double value = result.getDouble("YORDINATE");
393 int iPos = result.getInteger("MEDIAN.MESHPOINT.JPOSITION");
394 int jPos = result.getInteger("MEDIAN.MESHPOINT.JPOSITION");
395 Point2d p = new Point2d(
396 coordinate.x,
397 coordinate.y,
398 value,
399 iPos, jPos);
400 points.add(p);
401 }
402
403 public void interpolated(Coordinate coordinate, boolean success) {
404 DefaultResult result = new DefaultResult(descriptor);
405 ResultDescriptor pd = prototyp.getResultDescriptor();
406
407 int pcolums = pd.getColumnCount();
408 for (int i = 0, j = 0; i < pcolums; ++i) {
409 String colname = pd.getColumnName(i);
410 if (blacklisted(colname)) {
411 continue;
412 }
413 if (colname.equals("SHAPE")) {
414 result.addColumnValue(j, toWKT(coordinate));
415 }
416 else if (colname.equals("YORDINATE")) {
417 if (success) {
418 result.addColumnValue(j, Double.valueOf(coordinate.z));
419 }
420 else if (lastWasSuccess) {
421 // only insert null if last was valid.
422 // This prevents flooding the result set with nulls
423 // if interpolating over a large gap.
424 result.addColumnValue(j, null);
425 }
426 }
427 else {
428 result.addColumnValue(j, prototyp.getObject(i));
429 }
430 ++j;
431 }
432 output.add(result);
433 lastWasSuccess = success;
434 }
435 } // class SectionHandler
436
437 public static final double NAUTICAL_MILE = 1852.216d;
438 public static final double KILOMETER = 1000d;
439
440 public static final double toKM(double distance) {
441 return (distance * NAUTICAL_MILE) / KILOMETER;
442 }
443
444 public static final double EPSILON = 1e-5d;
445 public static final int INTERPOLATION_STEPS =
446 Integer.getInteger("interpolation.steps", 500).intValue();
447
448 public static Coordinate toCoordinate(String shape) {
449 try {
450 return ((Point)(new WKTReader().read(shape))).getCoordinate();
451 }
452 catch (ParseException pe) {
453 log.error(pe);
454 }
455 return null;
456 }
457
458 public static String toWKT(Coordinate coordinate) {
459 StringBuilder sb = new StringBuilder("POINT(");
460 sb.append(coordinate.x)
461 .append(' ')
462 .append(coordinate.y)
463 .append(')');
464 return sb.toString();
465 }
466
467 protected Collection<Result> process(
468 List<Coordinate> path,
469 Collection<Result> input
470 ) {
471 log.debug("------ number of points before processing: " + input.size());
472 ArrayList<Result> output = new ArrayList<Result>();
473
474
475 Result last = null;
476
477 int [] diffColums = null;
478
479 SectionHandler sectionHandler = null;
480
481 for (Result result: input) {
482
483 if (sectionHandler == null) {
484
485 ResultDescriptor rd = result.getResultDescriptor();
486 diffColums = rd.getColumnIndices(DIFF_COLUMS);
487 int columns = rd.getColumnCount();
488
489 DefaultResultDescriptor resultDescriptor =
490 new DefaultResultDescriptor();
491
492 for (int j = 0; j < columns; ++j) {
493 String columnName = rd.getColumnName(j);
494 if (!blacklisted(columnName)) {
495 resultDescriptor.addColumn(
496 columnName,
497 rd.getColumnClassName(j));
498 }
499 }
500
501 sectionHandler = new SectionHandler(
502 path,
503 output,
504 resultDescriptor);
505
506 sectionHandler.setPrototyp(result);
507 }
508
509 if (last != null && different(last, result, diffColums)) {
510 sectionHandler.finish();
511 sectionHandler.setPrototyp(result);
512 }
513
514 sectionHandler.handle(result);
515
516 last = result;
517 }
518
519 if (sectionHandler != null) {
520 sectionHandler.finish();
521 }
522
523 log.debug("------ number of points after processing: " + output.size());
524
525 return output;
526 }
527 } 162 }

http://dive4elements.wald.intevation.org