comparison gnv-artifacts/src/main/java/de/intevation/gnv/utils/WKTUtils.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
children 2402173a1490
comparison
equal deleted inserted replaced
419:1e192ea34e80 420:c6a287398379
1 package de.intevation.gnv.utils;
2
3 import com.vividsolutions.jts.geom.Coordinate;
4 import com.vividsolutions.jts.geom.Point;
5 import com.vividsolutions.jts.geom.LineString;
6 import com.vividsolutions.jts.io.ParseException;
7 import com.vividsolutions.jts.io.WKTReader;
8
9 import de.intevation.gnv.geobackend.base.DefaultResult;
10 import de.intevation.gnv.geobackend.base.DefaultResultDescriptor;
11 import de.intevation.gnv.geobackend.base.Result;
12 import de.intevation.gnv.geobackend.base.ResultDescriptor;
13 import de.intevation.gnv.geobackend.base.query.QueryExecutor;
14 import de.intevation.gnv.geobackend.base.query.QueryExecutorFactory;
15 import de.intevation.gnv.geobackend.base.query.exception.QueryException;
16 import de.intevation.gnv.math.Interpolation2D;
17 import de.intevation.gnv.math.LinearFunction;
18 import de.intevation.gnv.math.LinearMetrics;
19 import de.intevation.gnv.math.Point2d;
20 import de.intevation.gnv.state.InputData;
21
22 import java.util.Arrays;
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.List;
26 import java.util.Map;
27
28 import org.apache.commons.math.optimization.OptimizationException;
29 import org.apache.commons.math.optimization.fitting.CurveFitter;
30 import org.apache.commons.math.optimization.general.GaussNewtonOptimizer;
31 import org.apache.commons.math.FunctionEvaluationException;
32
33 import org.apache.log4j.Logger;
34
35 public abstract class WKTUtils {
36
37 private static Logger log = Logger.getLogger(WKTUtils.class);
38
39 private static final String [] DIFF_COLUMS = {
40 "GROUP1",
41 "GROUP2",
42 "GROUP3"
43 };
44
45 private static final String [] COLUMN_BLACKLIST = {
46 "MEDIAN.MESHPOINT.JPOSITION",
47 "MEDIAN.MESHPOINT.IPOSITION"
48 };
49
50 public static final double NAUTICAL_MILE = 1852.216d;
51 public static final double KILOMETER = 1000d;
52
53 public static final double EPSILON = 1e-5d;
54 public static final int INTERPOLATION_STEPS =
55 Integer.getInteger("interpolation.steps", 500).intValue();
56
57
58 public static final class SectionHandler
59 implements Interpolation2D.Consumer
60 {
61 private ArrayList<Point2d> points;
62 private List<Coordinate> path;
63 private Collection<Result> output;
64 private Result prototyp;
65 private ResultDescriptor descriptor;
66 private boolean lastWasSuccess;
67
68 public SectionHandler() {
69 }
70
71 public SectionHandler(
72 List<Coordinate> path,
73 Collection<Result> output,
74 ResultDescriptor descriptor
75 ) {
76 this.path = path;
77 this.output = output;
78 this.descriptor = descriptor;
79 points = new ArrayList<Point2d>();
80 lastWasSuccess = true;
81 }
82
83 public void finish() {
84 if (!points.isEmpty()) {
85 double distance = toKM(
86 DistanceCalculator.calculateDistance(path));
87
88 if (distance > EPSILON) {
89
90 Interpolation2D.interpolate(
91 path,
92 points,
93 0d,
94 distance,
95 INTERPOLATION_STEPS,
96 LinearMetrics.INSTANCE,
97 this);
98 }
99
100 points.clear();
101 }
102 lastWasSuccess = true;
103 }
104
105 public void setPrototyp(Result prototyp) {
106 this.prototyp = prototyp;
107 }
108
109 public void handle(Result result) {
110 Coordinate coordinate =
111 toCoordinate(result.getString("SHAPE"));
112 double value = result.getDouble("YORDINATE");
113 int iPos = result.getInteger("MEDIAN.MESHPOINT.JPOSITION");
114 int jPos = result.getInteger("MEDIAN.MESHPOINT.JPOSITION");
115 Point2d p = new Point2d(
116 coordinate.x,
117 coordinate.y,
118 value,
119 iPos, jPos);
120 points.add(p);
121 }
122
123 public void interpolated(Coordinate coordinate, boolean success) {
124 DefaultResult result = new DefaultResult(descriptor);
125 ResultDescriptor pd = prototyp.getResultDescriptor();
126
127 int pcolums = pd.getColumnCount();
128 for (int i = 0, j = 0; i < pcolums; ++i) {
129 String colname = pd.getColumnName(i);
130 if (blacklisted(colname)) {
131 continue;
132 }
133 if (colname.equals("SHAPE")) {
134 result.addColumnValue(j, OutputUtils.toWKT(coordinate));
135 }
136 else if (colname.equals("YORDINATE")) {
137 if (success) {
138 result.addColumnValue(j, Double.valueOf(coordinate.z));
139 }
140 else if (lastWasSuccess) {
141 // only insert null if last was valid.
142 // This prevents flooding the result set with nulls
143 // if interpolating over a large gap.
144 result.addColumnValue(j, null);
145 }
146 }
147 else {
148 result.addColumnValue(j, prototyp.getObject(i));
149 }
150 ++j;
151 }
152 output.add(result);
153 lastWasSuccess = success;
154 }
155 }
156
157
158 private static final boolean blacklisted(String column) {
159 for (int i = 0; i < COLUMN_BLACKLIST.length; ++i) {
160 if (COLUMN_BLACKLIST.equals(column)) {
161 return true;
162 }
163 }
164 return false;
165 }
166
167 private static boolean different(Result a, Result b, int [] indices) {
168 for (int i = 0; i < indices.length; ++i) {
169 String oa = a.getString(indices[i]);
170 String ob = b.getString(indices[i]);
171
172 if (oa == null && ob == null) {
173 continue;
174 }
175
176 if (oa == null || ob == null) {
177 return true;
178 }
179
180 if (!oa.equals(ob)) {
181 if (log.isDebugEnabled()) {
182 log.debug("+++++++++++++++ differs ++++++++++++++");
183 log.debug(" " + oa + " != " + ob);
184 }
185 return true;
186 }
187 }
188 return false;
189 }
190
191 public static Collection<Result> process(
192 List<Coordinate> path,
193 Collection<Result> input
194 ) {
195 log.debug("------ number of points before processing: " + input.size());
196 ArrayList<Result> output = new ArrayList<Result>();
197
198 Result last = null;
199
200 int [] diffColums = null;
201
202 SectionHandler sectionHandler = null;
203
204 for (Result result: input) {
205
206 if (sectionHandler == null) {
207
208 ResultDescriptor rd = result.getResultDescriptor();
209 diffColums = rd.getColumnIndices(DIFF_COLUMS);
210 int columns = rd.getColumnCount();
211
212 DefaultResultDescriptor resultDescriptor =
213 new DefaultResultDescriptor();
214
215 for (int j = 0; j < columns; ++j) {
216 String columnName = rd.getColumnName(j);
217 if (!blacklisted(columnName)) {
218 resultDescriptor.addColumn(
219 columnName,
220 rd.getColumnClassName(j));
221 }
222 }
223
224 sectionHandler = new SectionHandler(
225 path,
226 output,
227 resultDescriptor);
228
229 sectionHandler.setPrototyp(result);
230 }
231
232 if (last != null && different(last, result, diffColums)) {
233 sectionHandler.finish();
234 sectionHandler.setPrototyp(result);
235 }
236
237 sectionHandler.handle(result);
238
239 last = result;
240 }
241
242 if (sectionHandler != null) {
243 sectionHandler.finish();
244 }
245
246 log.debug("------ number of points after processing: " + output.size());
247
248 return output;
249 }
250
251
252 public static Coordinate toCoordinate(String shape) {
253 try {
254 return ((Point)(new WKTReader().read(shape))).getCoordinate();
255 }
256 catch (ParseException pe) {
257 log.error(pe);
258 }
259 return null;
260 }
261
262
263 public static final double toKM(double distance) {
264 return (distance * NAUTICAL_MILE) / KILOMETER;
265 }
266
267
268 public static String toWKT(Coordinate coordinate) {
269 StringBuilder sb = new StringBuilder("POINT(");
270 sb.append(coordinate.x)
271 .append(' ')
272 .append(coordinate.y)
273 .append(')');
274 return sb.toString();
275 }
276
277
278 public static Collection<Result> worldCoordinatesToIndex(
279 Collection<Result> result,
280 Map<String, InputData> inputData,
281 String ijkQueryID,
282 String queryID,
283 String[] filterValues
284 ) throws ParseException, QueryException
285 {
286 // 1. IJK Anfragen für Stuetzpunkte im Netz ausführen.
287 LineString ls = (LineString)new WKTReader().read(
288 inputData.get("mesh_linestring").getValue());
289
290 Coordinate[] coords = ls.getCoordinates();
291
292 List<java.awt.Point> points = new ArrayList<java.awt.Point>(coords.length);
293
294 String meshid = inputData.get("meshid").getValue();
295 QueryExecutor queryExecutor = QueryExecutorFactory
296 .getInstance()
297 .getQueryExecutor();
298
299 ArrayList missingPoints = new ArrayList();
300
301 String additionWhere = "FEATUREID=FEATUREID";
302
303 for (int i = 0; i < coords.length; i++) {
304
305 String wkt = toWKT(coords[i]);
306
307 result = queryExecutor.executeQuery(ijkQueryID,
308 new String[]{meshid,wkt});
309 if (!result.isEmpty()){
310 Result resultValue = result.iterator().next();
311 int iPos = resultValue.getInteger(1);
312 int jPos = resultValue.getInteger(0);
313 log.debug("Found Pos "+iPos+"/"+jPos +" for "+wkt);
314 points.add(i, new java.awt.Point(iPos,jPos));
315 }else{
316 log.debug("No i/j Pos found for "+wkt);
317 missingPoints.add(new Object [] { Integer.valueOf(i), coords[i] });
318 points.add(i, null);
319 // Special Case no i,j found for Coordinate
320 }
321 }
322
323 if (missingPoints.size() == coords.length) {
324 log.debug("cannot create index buffer");
325 }
326 else { // generate index filter
327 boolean remainsMissingPoints = !missingPoints.isEmpty();
328
329 if (remainsMissingPoints) {
330 // try to guess the missing (i, j)
331 CurveFitter iFitter = new CurveFitter(new GaussNewtonOptimizer(true));
332 CurveFitter jFitter = new CurveFitter(new GaussNewtonOptimizer(true));
333
334 for (int i = 0, N = points.size(); i < N; ++i) {
335 java.awt.Point p = (java.awt.Point)points.get(i);
336 if (p != null) {
337 Coordinate coord = coords[i];
338 iFitter.addObservedPoint(coord.x, p.x);
339 jFitter.addObservedPoint(coord.y, p.y);
340 }
341 }
342 try {
343 // XXX: Assumption: (i, j) are created by componentwise linear function.
344 // This is surely not correct because (x, y) are in a ellipsoid projection.
345 // TODO: use ellipsoid functions and fit with Levenberg Marquardt.
346 double [] iParams = iFitter.fit(
347 LinearFunction.INSTANCE, new double [] { 1d, 1d });
348
349 double [] jParams = jFitter.fit(
350 LinearFunction.INSTANCE, new double [] { 1d, 1d });
351
352 for (int i = missingPoints.size()-1; i >= 0; --i) {
353 Object [] a = (Object [])missingPoints.get(i);
354 Coordinate coord = (Coordinate)a[1];
355 int pi = (int)Math.round(iParams[0]*coord.x + iParams[1]);
356 int pj = (int)Math.round(jParams[0]*coord.y + jParams[1]);
357 points.set(
358 ((Integer)a[0]).intValue(),
359 new java.awt.Point(pi, pj));
360 }
361
362 remainsMissingPoints = false; // we filled the gaps
363 }
364 catch (FunctionEvaluationException fee) {
365 log.error(fee);
366 }
367 catch (OptimizationException oe) {
368 log.error(oe);
369 }
370 }
371
372 if (!remainsMissingPoints) {
373 // TODO: Make Tablenames and Columns Configurable
374 IndexBuffer ib = new IndexBuffer(
375 points,
376 "MEDIAN.MESHPOINT.IPOSITION",
377 "MEDIAN.MESHPOINT.JPOSITION" );
378 additionWhere = ib.toWhereClause();
379 log.debug("Additional Where Clause = "+additionWhere);
380 // 2. Aus diesen Stuetzpunkten den Resultset generieren.
381 }
382 } // if generate index filter
383
384 String[] addedFilterValues = new String[filterValues.length+1];
385 System.arraycopy(filterValues, 0, addedFilterValues, 0, filterValues.length);
386 addedFilterValues[filterValues.length] = additionWhere;
387
388 result = process(
389 Arrays.asList(coords),
390 queryExecutor.executeQuery(
391 queryID,
392 addedFilterValues));
393
394 return result;
395 }
396 }

http://dive4elements.wald.intevation.org