Mercurial > dive4elements > gnv-client
comparison gnv-artifacts/src/main/java/de/intevation/gnv/utils/WKTUtils.java @ 657:af3f56758f59
merged gnv-artifacts/0.5
author | Thomas Arendsen Hein <thomas@intevation.de> |
---|---|
date | Fri, 28 Sep 2012 12:13:53 +0200 |
parents | 4e347624ee7c |
children | 0563389138bb |
comparison
equal
deleted
inserted
replaced
590:5f5f273c8566 | 657:af3f56758f59 |
---|---|
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 public abstract class WKTUtils { | |
41 | |
42 private static Logger log = Logger.getLogger(WKTUtils.class); | |
43 | |
44 public static final double NAUTICAL_MILE = 1852.216d; | |
45 public static final double KILOMETER = 1000d; | |
46 | |
47 public static final String I_NAME = "MEDIAN.MESHPOINT.IPOSITION"; | |
48 public static final String J_NAME = "MEDIAN.MESHPOINT.JPOSITION"; | |
49 | |
50 public static final String TRUE_EXPRESSION = "FEATUREID=FEATUREID"; | |
51 | |
52 public static final String[] COORDINATE_OUT_FORMAT = { | |
53 "coordinate.template.northeast", | |
54 "coordinate.template.southeast", | |
55 "coordinate.template.northwest", | |
56 "coordinate.template.southwest" | |
57 }; | |
58 | |
59 public static final String DEFAULT_COORDINATE_TEMPLATE = | |
60 "{0}\u00b0N {1}'' {2}\u00b0E {3}''"; | |
61 | |
62 public static boolean different(Result a, Result b, int [] indices) { | |
63 for (int i = 0; i < indices.length; ++i) { | |
64 String oa = a.getString(indices[i]); | |
65 String ob = b.getString(indices[i]); | |
66 | |
67 if (oa == null && ob == null) { | |
68 continue; | |
69 } | |
70 | |
71 if (oa == null || ob == null) { | |
72 return true; | |
73 } | |
74 | |
75 if (!oa.equals(ob)) { | |
76 return true; | |
77 } | |
78 } | |
79 return false; | |
80 } | |
81 | |
82 public static Coordinate toCoordinate(String shape) { | |
83 try { | |
84 return shape != null | |
85 ? ((Point)(new WKTReader().read(shape))).getCoordinate() | |
86 : null; | |
87 } | |
88 catch (ParseException pe) { | |
89 log.error(pe); | |
90 } | |
91 catch (ClassCastException cce) { | |
92 log.error("cannot read WKT point", cce); | |
93 } | |
94 return null; | |
95 } | |
96 | |
97 public static Polygon toPolygon(String shape) { | |
98 try { | |
99 return (Polygon)new WKTReader().read(shape); | |
100 } | |
101 catch (ParseException pe) { | |
102 log.error(pe); | |
103 } | |
104 catch (ClassCastException cce) { | |
105 log.error("cannot read WKT polygon", cce); | |
106 } | |
107 return null; | |
108 } | |
109 | |
110 public static final double toKM(double distance) { | |
111 return (distance * NAUTICAL_MILE) / KILOMETER; | |
112 } | |
113 | |
114 | |
115 public static String toWKT(Coordinate coordinate) { | |
116 StringBuilder sb = new StringBuilder("POINT("); | |
117 sb.append(coordinate.x) | |
118 .append(' ') | |
119 .append(coordinate.y) | |
120 .append(')'); | |
121 return sb.toString(); | |
122 } | |
123 | |
124 public static final String indexBox( | |
125 java.awt.Point a, | |
126 java.awt.Point b, | |
127 String iName, | |
128 String jName | |
129 ) { | |
130 int minI = Math.min(a.x, b.x) - 1; | |
131 int maxI = Math.max(a.x, b.x) + 1; | |
132 int minJ = Math.min(a.y, b.y) - 1; | |
133 int maxJ = Math.max(a.y, b.y) + 1; | |
134 StringBuilder sb = new StringBuilder("(") | |
135 .append(iName).append(" >= ").append(minI) | |
136 .append(" AND ").append(iName).append(" <= ").append(maxI) | |
137 .append(" AND ").append(jName).append(" >= ").append(minJ) | |
138 .append(" AND ").append(jName).append(" <= ").append(maxJ) | |
139 .append(')'); | |
140 return sb.toString(); | |
141 } | |
142 | |
143 public static final String diagonalBox(List<java.awt.Point> points) { | |
144 | |
145 if (points.get(0) != null && points.get(2) != null) { | |
146 return indexBox( | |
147 points.get(0), points.get(2), | |
148 I_NAME, | |
149 J_NAME); | |
150 } | |
151 | |
152 if (points.get(1) != null && points.get(3) != null) { | |
153 return indexBox( | |
154 points.get(1), points.get(3), | |
155 I_NAME, | |
156 J_NAME); | |
157 } | |
158 | |
159 return null; | |
160 } | |
161 | |
162 public static String worldEnvelopeCoordinatesToIndex( | |
163 Coordinate [] coords, | |
164 String meshid, | |
165 String ijkQueryID | |
166 ) | |
167 throws QueryException | |
168 { | |
169 List<java.awt.Point> points = | |
170 new ArrayList<java.awt.Point>(coords.length); | |
171 | |
172 ArrayList<Object []> missingPoints = | |
173 new ArrayList<Object []>(); | |
174 | |
175 createIJKPoints(coords, meshid, ijkQueryID, points, missingPoints); | |
176 | |
177 String additionWhere = null; | |
178 | |
179 if (missingPoints.size() == coords.length) { | |
180 log.debug("cannot create index buffer"); | |
181 } | |
182 else { | |
183 additionWhere = diagonalBox(points); | |
184 | |
185 if (additionWhere == null) { | |
186 // 3 Points are missing or are on one side of the envelope | |
187 boolean remainsMissingPoints = calculateIJ4MissingPoints( | |
188 coords, points, missingPoints); | |
189 | |
190 if (!remainsMissingPoints) { | |
191 additionWhere = diagonalBox(points); | |
192 } | |
193 } | |
194 } | |
195 | |
196 return additionWhere != null | |
197 ? additionWhere | |
198 : TRUE_EXPRESSION; | |
199 } | |
200 | |
201 public static String worldCoordinatesToIndex( | |
202 Coordinate [] coords, | |
203 Collection<Result> result, | |
204 String meshid, | |
205 String ijkQueryID | |
206 ) | |
207 throws QueryException | |
208 { | |
209 List<java.awt.Point> points = new ArrayList<java.awt.Point>(coords.length); | |
210 | |
211 ArrayList<Object []> missingPoints = new ArrayList<Object []>(); | |
212 | |
213 createIJKPoints(coords, meshid, ijkQueryID, points, missingPoints); | |
214 | |
215 String additionWhere = TRUE_EXPRESSION; | |
216 | |
217 if (missingPoints.size() == coords.length) { | |
218 log.debug("cannot create index buffer"); | |
219 } | |
220 else { // generate index filter | |
221 boolean remainsMissingPoints = calculateIJ4MissingPoints( | |
222 coords, points, missingPoints); | |
223 | |
224 if (!remainsMissingPoints) { | |
225 // TODO: Make Tablenames and Columns Configurable | |
226 IndexBuffer ib = new IndexBuffer( | |
227 points, | |
228 I_NAME, | |
229 J_NAME ); | |
230 additionWhere = ib.toWhereClause(); | |
231 log.debug("Additional Where Clause = "+additionWhere); | |
232 } | |
233 } // if generate index filter | |
234 | |
235 return additionWhere; | |
236 } | |
237 | |
238 | |
239 /** | |
240 * @param coords | |
241 * @param points | |
242 * @param missingPoints | |
243 * @return | |
244 */ | |
245 private static boolean calculateIJ4MissingPoints( | |
246 Coordinate [] coords, | |
247 List<java.awt.Point> points, | |
248 ArrayList<Object[]> missingPoints | |
249 ) { | |
250 boolean remainsMissingPoints = !missingPoints.isEmpty(); | |
251 | |
252 if (remainsMissingPoints) { | |
253 // try to guess the missing (i, j) | |
254 CurveFitter iFitter = new CurveFitter(new GaussNewtonOptimizer(true)); | |
255 CurveFitter jFitter = new CurveFitter(new GaussNewtonOptimizer(true)); | |
256 | |
257 for (int i = 0, N = points.size(); i < N; ++i) { | |
258 java.awt.Point p = (java.awt.Point)points.get(i); | |
259 if (p != null) { | |
260 Coordinate coord = coords[i]; | |
261 iFitter.addObservedPoint(coord.x, p.x); | |
262 jFitter.addObservedPoint(coord.y, p.y); | |
263 } | |
264 } | |
265 try { | |
266 // XXX: Assumption: (i, j) are created by componentwise linear function. | |
267 // This is surely not correct because (x, y) are in a ellipsoid projection. | |
268 // TODO: use ellipsoid functions and fit with Levenberg Marquardt. | |
269 double [] iParams = iFitter.fit( | |
270 LinearFunction.INSTANCE, new double [] { 1d, 1d }); | |
271 | |
272 double [] jParams = jFitter.fit( | |
273 LinearFunction.INSTANCE, new double [] { 1d, 1d }); | |
274 | |
275 for (int i = missingPoints.size()-1; i >= 0; --i) { | |
276 Object [] a = (Object [])missingPoints.get(i); | |
277 Coordinate coord = (Coordinate)a[1]; | |
278 int pi = (int)Math.round(iParams[0]*coord.x + iParams[1]); | |
279 int pj = (int)Math.round(jParams[0]*coord.y + jParams[1]); | |
280 points.set( | |
281 ((Integer)a[0]).intValue(), | |
282 new java.awt.Point(pi, pj)); | |
283 } | |
284 | |
285 remainsMissingPoints = false; // we filled the gaps | |
286 } | |
287 catch (FunctionEvaluationException fee) { | |
288 log.error(fee); | |
289 } | |
290 catch (OptimizationException oe) { | |
291 log.error(oe); | |
292 } | |
293 } | |
294 return remainsMissingPoints; | |
295 } | |
296 | |
297 | |
298 /** | |
299 * @param coords | |
300 * @param meshid | |
301 * @param ijkQueryID | |
302 * @param points | |
303 * @param missingPoints | |
304 * @throws QueryException | |
305 */ | |
306 private static void createIJKPoints( | |
307 Coordinate[] coords, | |
308 String meshid, | |
309 String ijkQueryID, | |
310 List<java.awt.Point> points, | |
311 ArrayList<Object []> missingPoints | |
312 ) | |
313 throws QueryException | |
314 { | |
315 boolean debug = log.isDebugEnabled(); | |
316 | |
317 QueryExecutor queryExecutor = QueryExecutorFactory | |
318 .getInstance() | |
319 .getQueryExecutor(); | |
320 | |
321 for (int i = 0; i < coords.length; i++) { | |
322 | |
323 String wkt = toWKT(coords[i]); | |
324 | |
325 Collection<Result> result = queryExecutor.executeQuery( | |
326 ijkQueryID, | |
327 new String [] {meshid, wkt}); | |
328 | |
329 if (!result.isEmpty()) { | |
330 Result resultValue = result.iterator().next(); | |
331 int iPos = resultValue.getInteger(1); | |
332 int jPos = resultValue.getInteger(0); | |
333 if (debug) { | |
334 log.debug("Found Pos "+iPos+"/"+jPos +" for "+wkt); | |
335 } | |
336 points.add(i, new java.awt.Point(iPos,jPos)); | |
337 } | |
338 else { | |
339 if (debug) { | |
340 log.debug("No i/j Pos found for "+wkt); | |
341 } | |
342 missingPoints.add(new Object [] { Integer.valueOf(i), coords[i] }); | |
343 points.add(i, null); | |
344 // Special Case no i,j found for Coordinate | |
345 } | |
346 } | |
347 } | |
348 | |
349 public static Coordinate [] toCoordinates(String wkt) { | |
350 try { | |
351 LineString ls = (LineString)new WKTReader().read(wkt); | |
352 return ls.getCoordinates(); | |
353 } | |
354 catch (ParseException pe) { | |
355 log.error("cannot read WKT line string", pe); | |
356 } | |
357 catch (ClassCastException cce) { | |
358 log.error("cannot read WKT line string", cce); | |
359 } | |
360 return null; | |
361 } | |
362 | |
363 public static String toText(String wkt) { | |
364 return toText(Locale.getDefault(), wkt); | |
365 } | |
366 | |
367 public static String toText(Locale locale, String wkt) { | |
368 String formattedCoordinate = null; | |
369 try { | |
370 Point p = (Point)new WKTReader().read(wkt); | |
371 double lat = p.getY(); | |
372 double lon =p.getX(); | |
373 | |
374 int choice = 0; | |
375 | |
376 if (lat <0 ) { | |
377 choice += 1; | |
378 lat=-lat; | |
379 } | |
380 | |
381 if (lon <0 ) { | |
382 choice += 2; | |
383 lon=-lon; | |
384 } | |
385 | |
386 RessourceFactory factory = RessourceFactory.getInstance(); | |
387 String template = factory.getRessource( | |
388 locale, | |
389 COORDINATE_OUT_FORMAT[choice], | |
390 DEFAULT_COORDINATE_TEMPLATE | |
391 ); | |
392 | |
393 NumberFormat minFormatter = NumberFormat.getInstance(locale); | |
394 minFormatter.setMaximumFractionDigits(3); | |
395 minFormatter.setMinimumFractionDigits(3); | |
396 | |
397 String minLat = minFormatter.format(60.*(lat-((int)lat))); | |
398 String minLon = minFormatter.format(60.*(lon-((int)lon))); | |
399 | |
400 NumberFormat degFormatter = NumberFormat.getInstance(locale); | |
401 degFormatter.setMinimumIntegerDigits(2); | |
402 | |
403 String formLat = degFormatter.format((int)lat); | |
404 String formLon = degFormatter.format((int)lon); | |
405 | |
406 MessageFormat formatter = new MessageFormat(template); | |
407 | |
408 Object[] args = { | |
409 formLat, minLat, | |
410 formLon, minLon | |
411 }; | |
412 | |
413 return formatter.format(args); | |
414 | |
415 } | |
416 catch (ParseException e) { | |
417 log.warn(e,e); | |
418 } | |
419 | |
420 return null; | |
421 } | |
422 } |