Mercurial > dive4elements > gnv-client
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 } |