comparison gnv-artifacts/src/main/java/de/intevation/gnv/utils/WKTUtils.java @ 1119:7c4f81f74c47

merged gnv-artifacts
author Thomas Arendsen Hein <thomas@intevation.de>
date Fri, 28 Sep 2012 12:14:00 +0200
parents f953c9a559d8
children
comparison
equal deleted inserted replaced
1027:fca4b5eb8d2f 1119:7c4f81f74c47
1 /*
2 * Copyright (c) 2010 by Intevation GmbH
3 *
4 * This program is free software under the LGPL (>=v2.1)
5 * Read the file LGPL.txt coming with the software for details
6 * or visit http://www.gnu.org/licenses/ if it does not exist.
7 */
8
9 package de.intevation.gnv.utils;
10
11 import com.vividsolutions.jts.geom.Coordinate;
12 import com.vividsolutions.jts.geom.LineString;
13 import com.vividsolutions.jts.geom.Point;
14 import com.vividsolutions.jts.geom.Polygon;
15
16 import com.vividsolutions.jts.io.ParseException;
17 import com.vividsolutions.jts.io.WKTReader;
18
19 import de.intevation.gnv.artifacts.ressource.RessourceFactory;
20
21 import de.intevation.gnv.geobackend.base.Result;
22
23 import de.intevation.gnv.geobackend.base.query.QueryExecutor;
24 import de.intevation.gnv.geobackend.base.query.QueryExecutorFactory;
25
26 import de.intevation.gnv.geobackend.base.query.exception.QueryException;
27
28 import de.intevation.gnv.math.LinearFunction;
29
30 import java.text.MessageFormat;
31 import java.text.NumberFormat;
32
33 import java.util.ArrayList;
34 import java.util.Collection;
35 import java.util.List;
36 import java.util.Locale;
37
38 import org.apache.commons.math.FunctionEvaluationException;
39
40 import org.apache.commons.math.optimization.OptimizationException;
41
42 import org.apache.commons.math.optimization.fitting.CurveFitter;
43
44 import org.apache.commons.math.optimization.general.GaussNewtonOptimizer;
45
46 import org.apache.log4j.Logger;
47
48 /**
49 * A helper class which supports some useful methods to work with wkt strings.
50 *
51 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
52 */
53 public abstract class WKTUtils {
54
55 private static Logger log = Logger.getLogger(WKTUtils.class);
56
57 public static final double NAUTICAL_MILE = 1852.216d;
58 public static final double KILOMETER = 1000d;
59
60 public static final String I_NAME = "MEDIAN.MESHPOINT.IPOSITION";
61 public static final String J_NAME = "MEDIAN.MESHPOINT.JPOSITION";
62
63 public static final String TRUE_EXPRESSION = "FEATUREID=FEATUREID";
64
65 public static final String[] COORDINATE_OUT_FORMAT = {
66 "coordinate.template.northeast",
67 "coordinate.template.southeast",
68 "coordinate.template.northwest",
69 "coordinate.template.southwest"
70 };
71
72 public static final String DEFAULT_COORDINATE_TEMPLATE =
73 "{0}\u00b0N {1}'' {2}\u00b0E {3}''";
74
75 /**
76 * Checks the difference of two <code>Result</code>s.
77 *
78 * @param a A Result.
79 * @param b Another Result.
80 * @param indices Indices to be checked.
81 * @return true, if <i>a</i> and <i>b</i> are different - otherwise false.
82 */
83 public static boolean different(Result a, Result b, int [] indices) {
84 for (int i = 0; i < indices.length; ++i) {
85 String oa = a.getString(indices[i]);
86 String ob = b.getString(indices[i]);
87
88 if (oa == null && ob == null) {
89 continue;
90 }
91
92 if (oa == null || ob == null) {
93 return true;
94 }
95
96 if (!oa.equals(ob)) {
97 return true;
98 }
99 }
100 return false;
101 }
102
103 /**
104 * Turns a wkt string into a coordinate.
105 *
106 * @param shape A wkt string.
107 * @return wkt string as coordinate.
108 */
109 public static Coordinate toCoordinate(String shape) {
110 try {
111 return shape != null
112 ? ((Point)(new WKTReader().read(shape))).getCoordinate()
113 : null;
114 }
115 catch (ParseException pe) {
116 log.error(pe);
117 }
118 catch (ClassCastException cce) {
119 log.error("cannot read WKT point", cce);
120 }
121 return null;
122 }
123
124 /**
125 * Turns a wkt string into a polygon.
126 *
127 * @param shape A wkt string.
128 * @return wkt string as polygon.
129 */
130 public static Polygon toPolygon(String shape) {
131 try {
132 return (Polygon)new WKTReader().read(shape);
133 }
134 catch (ParseException pe) {
135 log.error(pe);
136 }
137 catch (ClassCastException cce) {
138 log.error("cannot read WKT polygon", cce);
139 }
140 return null;
141 }
142
143 /**
144 * Returns a distance in km.
145 *
146 * @param distance A distance.
147 * @return distance in km.
148 */
149 public static final double toKM(double distance) {
150 return (distance * NAUTICAL_MILE) / KILOMETER;
151 }
152
153
154 /**
155 * Turns a coordinate into a wkt string.
156 *
157 * @param coordinate A coordinate.
158 * @return the coordinate as wkt string.
159 */
160 public static String toWKT(Coordinate coordinate) {
161 StringBuilder sb = new StringBuilder("POINT(");
162 sb.append(coordinate.x)
163 .append(' ')
164 .append(coordinate.y)
165 .append(')');
166 return sb.toString();
167 }
168
169
170 public static final String indexBox(
171 java.awt.Point a,
172 java.awt.Point b,
173 String iName,
174 String jName
175 ) {
176 int minI = Math.min(a.x, b.x) - 1;
177 int maxI = Math.max(a.x, b.x) + 1;
178 int minJ = Math.min(a.y, b.y) - 1;
179 int maxJ = Math.max(a.y, b.y) + 1;
180 StringBuilder sb = new StringBuilder("(")
181 .append(iName).append(" >= ").append(minI)
182 .append(" AND ").append(iName).append(" <= ").append(maxI)
183 .append(" AND ").append(jName).append(" >= ").append(minJ)
184 .append(" AND ").append(jName).append(" <= ").append(maxJ)
185 .append(')');
186 return sb.toString();
187 }
188
189 public static final String diagonalBox(List<java.awt.Point> points) {
190
191 if (points.get(0) != null && points.get(2) != null) {
192 return indexBox(
193 points.get(0), points.get(2),
194 I_NAME,
195 J_NAME);
196 }
197
198 if (points.get(1) != null && points.get(3) != null) {
199 return indexBox(
200 points.get(1), points.get(3),
201 I_NAME,
202 J_NAME);
203 }
204
205 return null;
206 }
207
208 public static String worldEnvelopeCoordinatesToIndex(
209 Coordinate [] coords,
210 String meshid,
211 String ijkQueryID
212 )
213 throws QueryException
214 {
215 List<java.awt.Point> points =
216 new ArrayList<java.awt.Point>(coords.length);
217
218 ArrayList<Object []> missingPoints =
219 new ArrayList<Object []>();
220
221 createIJKPoints(coords, meshid, ijkQueryID, points, missingPoints);
222
223 String additionWhere = null;
224
225 if (missingPoints.size() == coords.length) {
226 log.debug("cannot create index buffer");
227 }
228 else {
229 additionWhere = diagonalBox(points);
230
231 if (additionWhere == null) {
232 // 3 Points are missing or are on one side of the envelope
233 boolean remainsMissingPoints = calculateIJ4MissingPoints(
234 coords, points, missingPoints);
235
236 if (!remainsMissingPoints) {
237 additionWhere = diagonalBox(points);
238 }
239 }
240 }
241
242 return additionWhere != null
243 ? additionWhere
244 : TRUE_EXPRESSION;
245 }
246
247 public static String worldCoordinatesToIndex(
248 Coordinate [] coords,
249 Collection<Result> result,
250 String meshid,
251 String ijkQueryID
252 )
253 throws QueryException
254 {
255 List<java.awt.Point> points = new ArrayList<java.awt.Point>(coords.length);
256
257 ArrayList<Object []> missingPoints = new ArrayList<Object []>();
258
259 createIJKPoints(coords, meshid, ijkQueryID, points, missingPoints);
260
261 String additionWhere = TRUE_EXPRESSION;
262
263 if (missingPoints.size() == coords.length) {
264 log.debug("cannot create index buffer");
265 }
266 else { // generate index filter
267 boolean remainsMissingPoints = calculateIJ4MissingPoints(
268 coords, points, missingPoints);
269
270 if (!remainsMissingPoints) {
271 // TODO: Make Tablenames and Columns Configurable
272 IndexBuffer ib = new IndexBuffer(
273 points,
274 I_NAME,
275 J_NAME );
276 additionWhere = ib.toWhereClause();
277 log.debug("Additional Where Clause = "+additionWhere);
278 }
279 } // if generate index filter
280
281 return additionWhere;
282 }
283
284
285 /**
286 * @param coords
287 * @param points
288 * @param missingPoints
289 * @return
290 */
291 private static boolean calculateIJ4MissingPoints(
292 Coordinate [] coords,
293 List<java.awt.Point> points,
294 ArrayList<Object[]> missingPoints
295 ) {
296 boolean remainsMissingPoints = !missingPoints.isEmpty();
297
298 if (remainsMissingPoints) {
299 // try to guess the missing (i, j)
300 CurveFitter iFitter = new CurveFitter(new GaussNewtonOptimizer(true));
301 CurveFitter jFitter = new CurveFitter(new GaussNewtonOptimizer(true));
302
303 for (int i = 0, N = points.size(); i < N; ++i) {
304 java.awt.Point p = (java.awt.Point)points.get(i);
305 if (p != null) {
306 Coordinate coord = coords[i];
307 iFitter.addObservedPoint(coord.x, p.x);
308 jFitter.addObservedPoint(coord.y, p.y);
309 }
310 }
311 try {
312 // XXX: Assumption: (i, j) are created by componentwise linear function.
313 // This is surely not correct because (x, y) are in a ellipsoid projection.
314 // TODO: use ellipsoid functions and fit with Levenberg Marquardt.
315 double [] iParams = iFitter.fit(
316 LinearFunction.INSTANCE, new double [] { 1d, 1d });
317
318 double [] jParams = jFitter.fit(
319 LinearFunction.INSTANCE, new double [] { 1d, 1d });
320
321 for (int i = missingPoints.size()-1; i >= 0; --i) {
322 Object [] a = (Object [])missingPoints.get(i);
323 Coordinate coord = (Coordinate)a[1];
324 int pi = (int)Math.round(iParams[0]*coord.x + iParams[1]);
325 int pj = (int)Math.round(jParams[0]*coord.y + jParams[1]);
326 points.set(
327 ((Integer)a[0]).intValue(),
328 new java.awt.Point(pi, pj));
329 }
330
331 remainsMissingPoints = false; // we filled the gaps
332 }
333 catch (FunctionEvaluationException fee) {
334 log.error(fee);
335 }
336 catch (OptimizationException oe) {
337 log.error(oe);
338 }
339 }
340 return remainsMissingPoints;
341 }
342
343
344 /**
345 * @param coords
346 * @param meshid
347 * @param ijkQueryID
348 * @param points
349 * @param missingPoints
350 * @throws QueryException
351 */
352 private static void createIJKPoints(
353 Coordinate[] coords,
354 String meshid,
355 String ijkQueryID,
356 List<java.awt.Point> points,
357 ArrayList<Object []> missingPoints
358 )
359 throws QueryException
360 {
361 boolean debug = log.isDebugEnabled();
362
363 QueryExecutor queryExecutor = QueryExecutorFactory
364 .getInstance()
365 .getQueryExecutor();
366
367 for (int i = 0; i < coords.length; i++) {
368
369 String wkt = toWKT(coords[i]);
370
371 Collection<Result> result = queryExecutor.executeQuery(
372 ijkQueryID,
373 new String [] {meshid, wkt});
374
375 if (!result.isEmpty()) {
376 Result resultValue = result.iterator().next();
377 int iPos = resultValue.getInteger(1);
378 int jPos = resultValue.getInteger(0);
379 if (debug) {
380 log.debug("Found Pos "+iPos+"/"+jPos +" for "+wkt);
381 }
382 points.add(i, new java.awt.Point(iPos,jPos));
383 }
384 else {
385 if (debug) {
386 log.debug("No i/j Pos found for "+wkt);
387 }
388 missingPoints.add(new Object [] { Integer.valueOf(i), coords[i] });
389 points.add(i, null);
390 // Special Case no i,j found for Coordinate
391 }
392 }
393 }
394
395 public static Coordinate [] toCoordinates(String wkt) {
396 try {
397 LineString ls = (LineString)new WKTReader().read(wkt);
398 return ls.getCoordinates();
399 }
400 catch (ParseException pe) {
401 log.error("cannot read WKT line string", pe);
402 }
403 catch (ClassCastException cce) {
404 log.error("cannot read WKT line string", cce);
405 }
406 return null;
407 }
408
409 /**
410 * Turns a wkt coordinate into a string format using the default locale of
411 * the virtual machine.
412 *
413 * @param wkt A wkt coordinate.
414 * @return A formatted coordinate.
415 */
416 public static String toText(String wkt) {
417 return toText(Locale.getDefault(), wkt);
418 }
419
420 /**
421 * Turns a point into a string format using the default locale of the
422 * virtual machine.
423 *
424 * @param p A point.
425 * @return A point formatted as string.
426 */
427 public static String toText(Point p) {
428 return toText(Locale.getDefault(), toWKT(p.getCoordinate()));
429 }
430
431 /**
432 * Turns a point into a string format using a given locale.
433 *
434 * @param locale A locale.
435 * @param p A point.
436 * @return a point formatted as string.
437 */
438 public static String toText(Locale locale, Point p) {
439 return toText(locale, toWKT(p.getCoordinate()));
440 }
441
442 /**
443 * Turns a wkt coordiante into a formatted text using a given locale.
444 *
445 * @param locale A locale.
446 * @param wkt A wkt coordinate.
447 * @return a formatted coordinate.
448 */
449 public static String toText(Locale locale, String wkt) {
450 String formattedCoordinate = null;
451 try {
452 Point p = (Point)new WKTReader().read(wkt);
453 double lat = p.getY();
454 double lon =p.getX();
455
456 int choice = 0;
457
458 if (lat <0 ) {
459 choice += 1;
460 lat=-lat;
461 }
462
463 if (lon <0 ) {
464 choice += 2;
465 lon=-lon;
466 }
467
468 RessourceFactory factory = RessourceFactory.getInstance();
469 String template = factory.getRessource(
470 locale,
471 COORDINATE_OUT_FORMAT[choice],
472 DEFAULT_COORDINATE_TEMPLATE
473 );
474
475 NumberFormat minFormatter = NumberFormat.getInstance(locale);
476 minFormatter.setMaximumFractionDigits(3);
477 minFormatter.setMinimumFractionDigits(3);
478
479 String minLat = minFormatter.format(60.*(lat-((int)lat)));
480 String minLon = minFormatter.format(60.*(lon-((int)lon)));
481
482 NumberFormat degFormatter = NumberFormat.getInstance(locale);
483 degFormatter.setMinimumIntegerDigits(2);
484
485 String formLat = degFormatter.format((int)lat);
486 String formLon = degFormatter.format((int)lon);
487
488 MessageFormat formatter = new MessageFormat(template);
489
490 Object[] args = {
491 formLat, minLat,
492 formLon, minLon
493 };
494
495 return formatter.format(args);
496
497 }
498 catch (ParseException e) {
499 log.warn(e,e);
500 }
501
502 return null;
503 }
504 }
505 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org