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