Mercurial > dive4elements > river
comparison flys-artifacts/src/main/java/de/intevation/flys/artifacts/charts/CrossSectionApp.java @ 1190:f514894ec2fd
merged flys-artifacts/2.5
author | Thomas Arendsen Hein <thomas@intevation.de> |
---|---|
date | Fri, 28 Sep 2012 12:14:17 +0200 |
parents | 111794adf285 |
children | a7def20539fb |
comparison
equal
deleted
inserted
replaced
917:b48c36076e17 | 1190:f514894ec2fd |
---|---|
1 package de.intevation.flys.artifacts.charts; | |
2 | |
3 import java.math.MathContext; | |
4 | |
5 import java.awt.Dimension; | |
6 import java.awt.BorderLayout; | |
7 import java.awt.FlowLayout; | |
8 | |
9 import javax.swing.JPanel; | |
10 import javax.swing.JButton; | |
11 import javax.swing.JComboBox; | |
12 import javax.swing.JTextField; | |
13 import javax.swing.DefaultComboBoxModel; | |
14 | |
15 import java.awt.event.ItemListener; | |
16 import java.awt.event.ItemEvent; | |
17 import java.awt.event.ActionListener; | |
18 import java.awt.event.ActionEvent; | |
19 | |
20 import java.awt.geom.Point2D; | |
21 import java.awt.geom.Line2D; | |
22 | |
23 import java.util.ArrayList; | |
24 import java.util.List; | |
25 import java.util.Comparator; | |
26 import java.util.Collections; | |
27 import java.util.Iterator; | |
28 | |
29 import java.io.File; | |
30 import java.io.IOException; | |
31 import java.io.FileWriter; | |
32 import java.io.PrintWriter; | |
33 | |
34 import org.jfree.ui.ApplicationFrame; | |
35 import org.jfree.ui.RefineryUtilities; | |
36 | |
37 import org.jfree.chart.ChartFactory; | |
38 | |
39 import org.jfree.chart.plot.PlotOrientation; | |
40 import org.jfree.chart.plot.XYPlot; | |
41 | |
42 import org.jfree.chart.ChartUtilities; | |
43 import org.jfree.chart.JFreeChart; | |
44 import org.jfree.chart.ChartPanel; | |
45 | |
46 import org.jfree.chart.axis.NumberAxis; | |
47 | |
48 import org.jfree.data.xy.XYDataset; | |
49 import org.jfree.data.xy.DefaultXYDataset; | |
50 | |
51 import de.intevation.flys.model.CrossSection; | |
52 import de.intevation.flys.model.CrossSectionLine; | |
53 import de.intevation.flys.model.CrossSectionPoint; | |
54 | |
55 import de.intevation.flys.geom.Lines; | |
56 | |
57 import de.intevation.flys.backend.SessionFactoryProvider; | |
58 | |
59 import org.hibernate.Session; | |
60 import org.hibernate.Query; | |
61 | |
62 import gnu.trove.TDoubleArrayList; | |
63 | |
64 public class CrossSectionApp | |
65 extends ApplicationFrame | |
66 { | |
67 public static final String RIVER = System.getProperty("river", "Saar"); | |
68 | |
69 public static final double EPSILON = 1e-4; | |
70 | |
71 public static final double TOO_SMALL = 0.2; | |
72 public static final double TOO_BIG = 500; | |
73 | |
74 public static final Comparator<CrossSectionPoint> COL_POS_CMP = | |
75 new Comparator<CrossSectionPoint>() { | |
76 @Override | |
77 public int compare(CrossSectionPoint a, CrossSectionPoint b) { | |
78 // TODO evaluate: isnt it enough to | |
79 // return (|d| > |EPS|) ? d : diff | |
80 double xa = a.getX().doubleValue(); | |
81 double xb = b.getX().doubleValue(); | |
82 double d = xa - xb; | |
83 if (d < -EPSILON) return -1; | |
84 if (d > +EPSILON) return +1; | |
85 int diff = a.getColPos() - b.getColPos(); | |
86 return diff < 0 ? -1 : diff > 0 ? +1 : 0; | |
87 } | |
88 }; | |
89 | |
90 public static final boolean isValid(double x) { | |
91 x = Math.abs(x); | |
92 return x > TOO_SMALL && x < TOO_BIG; | |
93 } | |
94 | |
95 protected Session session; | |
96 | |
97 protected JComboBox crossSectionsCB; | |
98 protected JComboBox crossSectionLinesCB; | |
99 protected JTextField waterlevelTF; | |
100 | |
101 protected ChartPanel chartPanel; | |
102 | |
103 protected Double lastWaterLevel; | |
104 | |
105 | |
106 public static class CrossSectionItem { | |
107 | |
108 CrossSection crossSection; | |
109 | |
110 public CrossSectionItem(CrossSection crossSection) { | |
111 this.crossSection = crossSection; | |
112 } | |
113 | |
114 public String toString() { | |
115 return crossSection.getDescription(); | |
116 } | |
117 } // CrossSectionItem | |
118 | |
119 public static class CrossSectionLineItem { | |
120 | |
121 CrossSectionLine line; | |
122 | |
123 public CrossSectionLineItem(CrossSectionLine line) { | |
124 this.line = line; | |
125 } | |
126 | |
127 public String toString() { | |
128 double v = line.getKm().doubleValue(); | |
129 return String.valueOf(Math.round(v * 1000.0)/1000d); | |
130 } | |
131 } // CrossSectionLineItem | |
132 | |
133 public CrossSectionApp(String title) { | |
134 super(title); | |
135 | |
136 session = SessionFactoryProvider | |
137 .createSessionFactory() | |
138 .openSession(); | |
139 | |
140 JPanel content = createContent(); | |
141 content.setPreferredSize(new Dimension(640, 480)); | |
142 setContentPane(content); | |
143 | |
144 } | |
145 | |
146 public List<CrossSection> crossSections(String river) { | |
147 Query query = session.createQuery( | |
148 "from CrossSection where river.name = :river"); | |
149 query.setParameter("river", river); | |
150 return query.list(); | |
151 } | |
152 | |
153 public JPanel createContent() { | |
154 JPanel panel = new JPanel(new BorderLayout()); | |
155 | |
156 | |
157 JPanel nav = new JPanel(new FlowLayout()); | |
158 | |
159 Object [] csis = createCrossSectionItems(); | |
160 crossSectionsCB = new JComboBox(csis); | |
161 | |
162 DefaultComboBoxModel dcbm; | |
163 | |
164 if (csis.length > 0) { | |
165 Object [] cslis = | |
166 createCrossSectionLineItems( | |
167 ((CrossSectionItem)csis[0]).crossSection); | |
168 dcbm = new DefaultComboBoxModel(cslis); | |
169 if (cslis.length > 0) { | |
170 dcbm.setSelectedItem(cslis[0]); | |
171 } | |
172 } | |
173 else { | |
174 dcbm = new DefaultComboBoxModel(new Object[0]); | |
175 } | |
176 | |
177 crossSectionLinesCB = new JComboBox(dcbm); | |
178 | |
179 nav.add(crossSectionsCB); | |
180 nav.add(crossSectionLinesCB); | |
181 | |
182 crossSectionsCB.addItemListener(new ItemListener() { | |
183 public void itemStateChanged(ItemEvent ie) { | |
184 if (ie.getStateChange() == ItemEvent.SELECTED) { | |
185 updateCrossSection( | |
186 ((CrossSectionItem)ie.getItem()).crossSection); | |
187 } | |
188 } | |
189 }); | |
190 | |
191 crossSectionLinesCB.addItemListener(new ItemListener() { | |
192 public void itemStateChanged(ItemEvent ie) { | |
193 if (ie.getStateChange() == ItemEvent.SELECTED) { | |
194 updateChart(); | |
195 } | |
196 } | |
197 }); | |
198 | |
199 | |
200 waterlevelTF = new JTextField(5); | |
201 | |
202 waterlevelTF.addActionListener(new ActionListener() { | |
203 public void actionPerformed(ActionEvent ae) { | |
204 waterLevelChanged(); | |
205 } | |
206 }); | |
207 | |
208 nav.add(waterlevelTF); | |
209 | |
210 JButton dump = new JButton("dump"); | |
211 | |
212 dump.addActionListener(new ActionListener() { | |
213 public void actionPerformed(ActionEvent ae) { | |
214 dumpData(); | |
215 } | |
216 }); | |
217 | |
218 nav.add(dump); | |
219 | |
220 panel.add(nav, BorderLayout.SOUTH); | |
221 | |
222 chartPanel = createChartPanel(); | |
223 | |
224 panel.add(chartPanel, BorderLayout.CENTER); | |
225 | |
226 return panel; | |
227 } | |
228 | |
229 protected void waterLevelChanged() { | |
230 String value = waterlevelTF.getText(); | |
231 try { | |
232 lastWaterLevel = Double.parseDouble(value); | |
233 } | |
234 catch (NumberFormatException nfe) { | |
235 waterlevelTF.setText( | |
236 lastWaterLevel != null ? lastWaterLevel.toString() : ""); | |
237 return; | |
238 } | |
239 updateChart(); | |
240 } | |
241 | |
242 protected void updateChart() { | |
243 | |
244 CrossSectionLineItem csli = | |
245 (CrossSectionLineItem)crossSectionLinesCB.getSelectedItem(); | |
246 | |
247 JFreeChart chart = createChart(csli == null | |
248 ? new DefaultXYDataset() | |
249 : generateDataset(csli.line, lastWaterLevel)); | |
250 | |
251 chartPanel.setChart(chart); | |
252 } | |
253 | |
254 protected ChartPanel createChartPanel() { | |
255 CrossSectionLineItem csli = | |
256 (CrossSectionLineItem)crossSectionLinesCB.getSelectedItem(); | |
257 | |
258 JFreeChart chart = createChart(csli == null | |
259 ? new DefaultXYDataset() | |
260 : generateDataset(csli.line, lastWaterLevel)); | |
261 | |
262 return new ChartPanel(chart); | |
263 } | |
264 | |
265 protected void dumpData() { | |
266 | |
267 CrossSectionLineItem csli = | |
268 (CrossSectionLineItem)crossSectionLinesCB.getSelectedItem(); | |
269 | |
270 if (csli == null) { | |
271 return; | |
272 } | |
273 | |
274 CrossSectionLine line = csli.line; | |
275 | |
276 double km = Math.round(line.getKm().doubleValue() * 1000d)/1000d; | |
277 | |
278 String kmS = String.valueOf(km).replace(".", "-"); | |
279 | |
280 int i = 1; | |
281 File file = new File("cross-section-" + kmS + ".txt"); | |
282 while (file.exists()) { | |
283 file = new File("cross-section-" + kmS + "[" + (i++) + "].txt"); | |
284 } | |
285 | |
286 System.err.println("dump points to file '" + file + "'"); | |
287 | |
288 List<CrossSectionPoint> points = line.getPoints(); | |
289 | |
290 PrintWriter out = null; | |
291 | |
292 MathContext mc = new MathContext(3); | |
293 | |
294 try { | |
295 out = | |
296 new PrintWriter( | |
297 new FileWriter(file)); | |
298 | |
299 for (CrossSectionPoint point: points) { | |
300 out.println( | |
301 point.getX().round(mc) + " " + | |
302 point.getY().round(mc)); | |
303 } | |
304 | |
305 out.flush(); | |
306 } | |
307 catch (IOException ioe) { | |
308 ioe.printStackTrace(); | |
309 } | |
310 finally { | |
311 if (out != null) { | |
312 out.close(); | |
313 } | |
314 } | |
315 } | |
316 | |
317 public static XYDataset generateDataset( | |
318 CrossSectionLine line, | |
319 Double waterlevel | |
320 ) { | |
321 DefaultXYDataset dataset = new DefaultXYDataset(); | |
322 | |
323 List<CrossSectionPoint> ps = line.getPoints(); | |
324 | |
325 if (ps.isEmpty()) { | |
326 return dataset; | |
327 } | |
328 | |
329 Collections.sort(ps, COL_POS_CMP); | |
330 | |
331 List<Point2D> points = new ArrayList<Point2D>(ps.size()); | |
332 | |
333 for (CrossSectionPoint p: ps) { | |
334 double x = p.getX().doubleValue(); | |
335 double y = p.getY().doubleValue(); | |
336 if (isValid(x) && isValid(y)) { | |
337 points.add(new Point2D.Double(x, y)); | |
338 } | |
339 } | |
340 | |
341 if (points.isEmpty()) { | |
342 return dataset; | |
343 } | |
344 | |
345 double [] xs = new double[points.size()]; | |
346 double [] ys = new double[xs.length]; | |
347 | |
348 xs[0] = points.get(0).getX(); | |
349 ys[0] = points.get(0).getY(); | |
350 | |
351 for (int i = 1; i < xs.length; ++i) { | |
352 Point2D p = points.get(i); | |
353 double x = p.getX(); | |
354 double y = p.getY(); | |
355 | |
356 if (x <= xs[i-1]) { | |
357 x = xs[i-1] + EPSILON; | |
358 } | |
359 xs[i] = x; | |
360 ys[i] = y; | |
361 } | |
362 | |
363 if (waterlevel != null) { | |
364 double [][] data = createWaterLines(points, waterlevel); | |
365 dataset.addSeries(String.valueOf(waterlevel), data); | |
366 } | |
367 | |
368 CrossSection cs = line.getCrossSection(); | |
369 | |
370 String legend = (cs != null ? cs.getDescription() : "???") | |
371 + " " + Math.round(line.getKm().doubleValue() * 1000d)/1000d; | |
372 | |
373 dataset.addSeries(legend, new double [][] { xs, ys }); | |
374 | |
375 return dataset; | |
376 } | |
377 | |
378 public static double [][] createWaterLines( | |
379 List<Point2D> points, | |
380 double waterlevel | |
381 ) { | |
382 List<Line2D> lines = Lines.fillWater(points, waterlevel); | |
383 | |
384 TDoubleArrayList lxs = new TDoubleArrayList(); | |
385 TDoubleArrayList lys = new TDoubleArrayList(); | |
386 | |
387 for (Iterator<Line2D> iter = lines.iterator(); iter.hasNext();) { | |
388 Line2D l = iter.next(); | |
389 Point2D p1 = l.getP1(); | |
390 Point2D p2 = l.getP2(); | |
391 lxs.add(p1.getX()); | |
392 lys.add(p1.getY()); | |
393 lxs.add(p2.getX()); | |
394 lys.add(p2.getY()); | |
395 if (iter.hasNext()) { | |
396 lxs.add(Double.NaN); | |
397 lys.add(Double.NaN); | |
398 } | |
399 } | |
400 | |
401 return new double [][] { lxs.toNativeArray(), lys.toNativeArray() }; | |
402 } | |
403 | |
404 protected void updateCrossSection(CrossSection crossSection) { | |
405 Object [] cslis = createCrossSectionLineItems(crossSection); | |
406 DefaultComboBoxModel dcbm = new DefaultComboBoxModel(cslis); | |
407 if (cslis.length > 0) { | |
408 dcbm.setSelectedItem(cslis[0]); | |
409 } | |
410 crossSectionLinesCB.setModel(dcbm); | |
411 if (cslis.length > 0) { | |
412 CrossSectionLine line = | |
413 ((CrossSectionLineItem)cslis[0]).line; | |
414 } | |
415 updateChart(); | |
416 } | |
417 | |
418 protected Object [] createCrossSectionLineItems(CrossSection cs) { | |
419 List<CrossSectionLine> lines = cs.getLines(); | |
420 Object [] result = new Object[lines.size()]; | |
421 for (int i = 0; i < result.length; ++i) { | |
422 result[i] = new CrossSectionLineItem(lines.get(i)); | |
423 } | |
424 return result; | |
425 } | |
426 | |
427 | |
428 protected Object [] createCrossSectionItems() { | |
429 List<CrossSection> crossSections = crossSections(RIVER); | |
430 Object [] result = new Object[crossSections.size()]; | |
431 for (int i = 0; i < result.length; ++i) { | |
432 result[i] = new CrossSectionItem(crossSections.get(i)); | |
433 } | |
434 return result; | |
435 } | |
436 | |
437 public static JFreeChart createChart(XYDataset dataset) { | |
438 JFreeChart chart = ChartFactory.createXYLineChart( | |
439 null, | |
440 "Abstand [m]", | |
441 "H\u00f6he [m]", | |
442 dataset, | |
443 PlotOrientation.VERTICAL, | |
444 true, | |
445 true, | |
446 false); | |
447 | |
448 XYPlot plot = chart.getXYPlot(); | |
449 NumberAxis yAxis = (NumberAxis)plot.getRangeAxis(); | |
450 yAxis.setAutoRangeIncludesZero(false); | |
451 | |
452 ChartUtilities.applyCurrentTheme(chart); | |
453 return chart; | |
454 } | |
455 | |
456 public static void main(String [] args) { | |
457 CrossSectionApp csa = new CrossSectionApp("Querprofile"); | |
458 csa.pack(); | |
459 RefineryUtilities.centerFrameOnScreen(csa); | |
460 csa.setVisible(true); | |
461 } | |
462 } | |
463 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : |