Mercurial > dive4elements > river
comparison flys-client/src/main/java/de/intevation/flys/client/client/ui/chart/CrossSectionChartThemePanel.java @ 1436:29fc2d1dfe9b
Client side of interactive cross section diagrams.
flys-client/trunk@3399 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author | Felix Wolfsteller <felix.wolfsteller@intevation.de> |
---|---|
date | Tue, 13 Dec 2011 11:11:48 +0000 |
parents | bc06a671ef60 |
children | 432180235caf |
comparison
equal
deleted
inserted
replaced
1435:f6fbfdc813f0 | 1436:29fc2d1dfe9b |
---|---|
1 package de.intevation.flys.client.client.ui.chart; | 1 package de.intevation.flys.client.client.ui.chart; |
2 | 2 |
3 import java.util.HashMap; | |
4 import java.util.Map; | |
5 import java.util.LinkedHashMap; | |
6 | |
3 import com.google.gwt.core.client.GWT; | 7 import com.google.gwt.core.client.GWT; |
4 | 8 |
9 import com.google.gwt.user.client.rpc.AsyncCallback; | |
10 | |
11 import com.smartgwt.client.util.SC; | |
12 | |
5 import com.smartgwt.client.types.Alignment; | 13 import com.smartgwt.client.types.Alignment; |
14 import com.smartgwt.client.types.ListGridFieldType; | |
15 | |
6 import com.smartgwt.client.widgets.Canvas; | 16 import com.smartgwt.client.widgets.Canvas; |
7 import com.google.gwt.user.client.rpc.AsyncCallback; | |
8 import com.smartgwt.client.types.ListGridFieldType; | |
9 import com.smartgwt.client.widgets.grid.ListGrid; | 17 import com.smartgwt.client.widgets.grid.ListGrid; |
10 import com.smartgwt.client.widgets.grid.ListGridField; | 18 import com.smartgwt.client.widgets.grid.ListGridField; |
11 import com.smartgwt.client.widgets.grid.ListGridRecord; | 19 import com.smartgwt.client.widgets.grid.ListGridRecord; |
12 import com.smartgwt.client.widgets.layout.HLayout; | 20 import com.smartgwt.client.widgets.layout.HLayout; |
21 import com.smartgwt.client.widgets.layout.VLayout; | |
13 import com.smartgwt.client.widgets.form.fields.SpinnerItem; | 22 import com.smartgwt.client.widgets.form.fields.SpinnerItem; |
14 import com.smartgwt.client.widgets.form.DynamicForm; | 23 import com.smartgwt.client.widgets.form.DynamicForm; |
24 | |
25 import com.smartgwt.client.widgets.form.fields.ComboBoxItem; | |
26 import com.smartgwt.client.widgets.form.fields.SelectItem; | |
27 import com.smartgwt.client.widgets.form.fields.events.ChangeEvent; | |
15 import com.smartgwt.client.widgets.form.fields.events.ChangedEvent; | 28 import com.smartgwt.client.widgets.form.fields.events.ChangedEvent; |
16 import com.smartgwt.client.widgets.form.fields.events.ChangedHandler; | 29 import com.smartgwt.client.widgets.form.fields.events.ChangedHandler; |
30 import com.smartgwt.client.widgets.form.fields.events.ChangeHandler; | |
17 | 31 |
18 import de.intevation.flys.client.client.Config; | 32 import de.intevation.flys.client.client.Config; |
19 import de.intevation.flys.client.shared.model.Artifact; | 33 import de.intevation.flys.client.shared.model.Artifact; |
20 import de.intevation.flys.client.shared.model.Collection; | 34 import de.intevation.flys.client.shared.model.Collection; |
21 import de.intevation.flys.client.shared.model.OutputMode; | 35 import de.intevation.flys.client.shared.model.CollectionItem; |
22 | |
23 import de.intevation.flys.client.shared.model.Data; | 36 import de.intevation.flys.client.shared.model.Data; |
24 import de.intevation.flys.client.shared.model.DataItem; | 37 import de.intevation.flys.client.shared.model.DataItem; |
25 import de.intevation.flys.client.shared.model.DefaultArtifact; | 38 import de.intevation.flys.client.shared.model.DefaultArtifact; |
26 import de.intevation.flys.client.shared.model.DefaultData; | 39 import de.intevation.flys.client.shared.model.DefaultData; |
27 import de.intevation.flys.client.shared.model.DefaultDataItem; | 40 import de.intevation.flys.client.shared.model.DefaultDataItem; |
28 import de.intevation.flys.client.shared.model.FacetRecord; | 41 import de.intevation.flys.client.shared.model.FacetRecord; |
42 import de.intevation.flys.client.shared.model.OutputMode; | |
43 import de.intevation.flys.client.shared.model.Theme; | |
44 import de.intevation.flys.client.shared.model.ThemeList; | |
29 | 45 |
30 import de.intevation.flys.client.client.services.FeedServiceAsync; | 46 import de.intevation.flys.client.client.services.FeedServiceAsync; |
31 | 47 import de.intevation.flys.client.client.services.CrossSectionKMServiceAsync; |
32 | 48 |
33 /** | 49 /** |
34 * ThemePanel much like ChartThemePanel, but shows an "Actions" column, | 50 * ThemePanel much like ChartThemePanel, but shows an "Actions" column, |
35 * needed for interaction in the CrossSection Charts. | 51 * needed for interaction in the CrossSection Charts and a selctor to |
52 * declare which cross section profile is "master". | |
36 */ | 53 */ |
37 public class CrossSectionChartThemePanel | 54 public class CrossSectionChartThemePanel |
38 extends ChartThemePanel { | 55 extends ChartThemePanel { |
39 | 56 |
57 /** Service to query measurement points of cross sections. */ | |
58 CrossSectionKMServiceAsync kmService = GWT.create( | |
59 de.intevation.flys.client.client.services.CrossSectionKMService.class); | |
60 | |
61 /** UUID of the current "master" cross section. */ | |
62 protected String currentMasterUUID; | |
63 | |
64 /** The layout (used for visual active/inactive feedback). */ | |
65 protected VLayout layout; | |
66 | |
40 /** | 67 /** |
41 * Trivial constructor. | 68 * Trivial constructor. |
42 */ | 69 */ |
43 public CrossSectionChartThemePanel( | 70 public CrossSectionChartThemePanel( |
44 Collection collection, | 71 Collection collection, |
45 OutputMode mode) | 72 OutputMode mode) |
46 { | 73 { |
47 super(collection, mode); | 74 super(collection, mode); |
75 } | |
76 | |
77 | |
78 /** | |
79 * Feed an artifact to let it know that it is master wrt cross-sections. | |
80 * @param artifact uuid of an artifact. | |
81 */ | |
82 public void feedTellMaster(final String artifact) { | |
83 Data[] feedData = DefaultData.createSimpleStringDataArray( | |
84 "cross_section.master?", "1"); | |
85 | |
86 feedService.feed( | |
87 Config.getInstance().getLocale(), | |
88 new DefaultArtifact(artifact, "TODO:hash"), | |
89 feedData, | |
90 new AsyncCallback<Artifact>() { | |
91 public void onFailure(Throwable caught) { | |
92 GWT.log("Could not feed artifact (" + artifact | |
93 + ") with master marker: " + caught.getMessage()); | |
94 SC.warn(MSG.getString(caught.getMessage())); | |
95 enable(); | |
96 } | |
97 public void onSuccess(Artifact artifact) { | |
98 GWT.log("Successfully injected master mark to " + artifact); | |
99 setCurrentMaster(artifact.getUuid()); | |
100 requestRedraw(); | |
101 enable(); | |
102 } | |
103 }); | |
104 } | |
105 | |
106 | |
107 /** | |
108 * Create Layout, add a master selection box beneath. | |
109 */ | |
110 @Override | |
111 protected VLayout createLayout() { | |
112 | |
113 layout = super.createLayout(); | |
114 | |
115 // Create "set master" combobox. | |
116 SelectItem cbItem = new SelectItem(); | |
117 | |
118 cbItem.setTitle(MSG.chart_themepanel_set_master()); | |
119 cbItem.setType("comboBox"); | |
120 final LinkedHashMap<String, String> valueMap = | |
121 getThemeList().toMapArtifactUUIDDescription("cross_section"); | |
122 cbItem.setValueMap(valueMap); | |
123 | |
124 final DynamicForm form = new DynamicForm(); | |
125 form.setWidth(200); | |
126 form.setFields(cbItem); | |
127 layout.addMember(form); | |
128 | |
129 Config config = Config.getInstance(); | |
130 final String locale = config.getLocale(); | |
131 | |
132 // Add Change Handler to first unset the old master and then set the | |
133 // new master. | |
134 cbItem.addChangeHandler(new ChangeHandler() { | |
135 public void onChange(ChangeEvent event) { | |
136 String selectedItem = (String) event.getValue(); | |
137 final String artifact = selectedItem; | |
138 | |
139 disable(); | |
140 | |
141 // Tell current master that he is not master anymore. | |
142 if (getCurrentMaster() != null) { | |
143 Data[] feedData = DefaultData.createSimpleStringDataArray( | |
144 "cross_section.master?", "0"); | |
145 feedService.feed( | |
146 locale, | |
147 new DefaultArtifact(getCurrentMaster(), "TODO:hash"), | |
148 feedData, | |
149 new AsyncCallback<Artifact>() { | |
150 public void onFailure(Throwable caught) { | |
151 GWT.log("Could not un-master artifact: " + | |
152 caught.getMessage()); | |
153 SC.warn(MSG.getString(caught.getMessage())); | |
154 enable(); | |
155 } | |
156 public void onSuccess(Artifact oldMaster) { | |
157 GWT.log("Successfully un-mastered artifact."); | |
158 feedTellMaster(artifact); | |
159 } | |
160 }); | |
161 } | |
162 else { | |
163 feedTellMaster(artifact); | |
164 } | |
165 } | |
166 }); | |
167 | |
168 return layout; | |
169 } | |
170 | |
171 | |
172 /** Disable the UI (becomes gray, inresponsive to user input). */ | |
173 public void disable() { | |
174 this.layout.setDisabled(true); | |
175 } | |
176 | |
177 | |
178 /** DisDisable the UI (becomes ungray, responsive to user input). */ | |
179 public void enable() { | |
180 this.layout.setDisabled(false); | |
181 } | |
182 | |
183 | |
184 /** | |
185 * Returns a double from the list that has the smallest distance to the | |
186 * given to value. In case of multiple values with the same difference, | |
187 * the last one is taken. | |
188 * @param in possible return values. | |
189 * @param to the value to be as close to as possible. | |
190 * @return value from in that is closest to to, -1 if none. | |
191 */ | |
192 public static double closest(Double[] in, double to) { | |
193 if (in == null || in.length == 0) { | |
194 return -1; | |
195 } | |
196 if (in[0] == to) { | |
197 return to; | |
198 } | |
199 double minDiff = Math.abs(to - in[0]); | |
200 double bestMatch = in[0]; | |
201 for (int i = 1; i < in.length; i++) { | |
202 if (in[i] == to) { | |
203 return to; | |
204 } | |
205 double diff = Math.abs(to - in[0]); | |
206 if (diff < minDiff) { | |
207 minDiff = diff; | |
208 bestMatch = in[i]; | |
209 } | |
210 } | |
211 return bestMatch; | |
212 } | |
213 | |
214 | |
215 /** Feed a single artifact with the km of the crosssection to display. */ | |
216 public void sendFeed(FacetRecord facetRecord, double kmD) { | |
217 Config config = Config.getInstance(); | |
218 final String locale = config.getLocale(); | |
219 | |
220 Data[] feedData = | |
221 DefaultData.createSimpleStringDataArray("cross_section.km", | |
222 Double.valueOf(kmD).toString()); | |
223 | |
224 disable(); | |
225 feedService.feed( | |
226 locale, | |
227 new DefaultArtifact( | |
228 facetRecord.getTheme().getArtifact(), | |
229 "TODO:hash"), | |
230 feedData, | |
231 new AsyncCallback<Artifact>() { | |
232 @Override | |
233 public void onFailure(Throwable caught) { | |
234 GWT.log("Could not feed artifact " + caught.getMessage()); | |
235 SC.warn(MSG.getString(caught.getMessage())); | |
236 enable(); | |
237 } | |
238 @Override | |
239 public void onSuccess(Artifact artifact) { | |
240 GWT.log("Successfully fed"); | |
241 requestRedraw(); | |
242 enable(); | |
243 } | |
244 }); | |
48 } | 245 } |
49 | 246 |
50 | 247 |
51 /** | 248 /** |
52 * Create the handler for ChangeEvents on the Spinner in the | 249 * Create the handler for ChangeEvents on the Spinner in the |
64 final String locale = config.getLocale(); | 261 final String locale = config.getLocale(); |
65 | 262 |
66 ChangedHandler handler = new ChangedHandler() | 263 ChangedHandler handler = new ChangedHandler() |
67 { | 264 { |
68 @Override | 265 @Override |
69 public void onChanged(ChangedEvent ce) { | 266 public void onChanged(final ChangedEvent ce) { |
70 if (ce.getValue() == null) { | 267 if (ce.getValue() == null) { |
71 return; | 268 return; |
72 } | 269 } |
73 DefaultDataItem kmItem = new DefaultDataItem( | 270 |
74 "cross_section.km", | 271 // Now, query the value with the kmService. |
75 "cross_section.km", | 272 Map<Integer, Double> map = new HashMap<Integer,Double>(); |
76 ce.getValue().toString()); | 273 final double selected_km = |
77 DefaultData km = new DefaultData( | 274 Double.parseDouble(ce.getValue().toString()); |
78 "cross_section.km", | 275 |
79 null, | 276 int _dbid = -1; |
80 null, | 277 try { |
81 new DataItem[] {kmItem}); | 278 _dbid = Integer.valueOf(facetRecord.getTheme() |
82 Data[] feedData = new Data[] {km}; | 279 .getCollectionItem() |
83 feedService.feed( | 280 .getData().get("cross_section.dbid")); |
84 locale, | 281 } |
85 new DefaultArtifact( | 282 catch (NumberFormatException nfe) { |
86 facetRecord.getTheme().getArtifact(), | 283 GWT.log("Could not extract cross-section db id from data."); |
87 "TODO:hash"), | 284 } |
88 feedData, | 285 final int dbid = _dbid; |
89 new AsyncCallback<Artifact>() { | 286 |
287 map.put(dbid, Double.parseDouble(ce.getValue().toString())); | |
288 | |
289 disable(); | |
290 | |
291 kmService.getCrossSectionKMs(locale, map, 2, | |
292 new AsyncCallback<Map<Integer, Double[]>>() { | |
293 @Override | |
90 public void onFailure(Throwable caught) { | 294 public void onFailure(Throwable caught) { |
91 GWT.log("Could not feed artifact " + caught.getMessage()); | 295 GWT.log("Could not get single km for " |
92 // TODO SC.warn | 296 + dbid + ": "+ caught.getMessage()); |
297 SC.warn(MSG.getString(caught.getMessage())); | |
298 updateCollection(); | |
299 updateGrid(); | |
300 enable(); | |
93 } | 301 } |
94 public void onSuccess(Artifact artifact) { | 302 @Override |
95 GWT.log("Successfully fed"); | 303 public void onSuccess(Map<Integer, Double[]> obj) { |
96 // TODO: Also update content of spinnerbox | 304 Double[] kms = obj.get(dbid); |
97 requestRedraw(); | 305 double closest = |
306 CrossSectionChartThemePanel.closest(kms, | |
307 selected_km); | |
308 GWT.log("Got single km for " + dbid + ", it is " | |
309 + closest); | |
310 SpinnerItem item = (SpinnerItem) ce.getItem(); | |
311 item.setValue(closest); | |
312 sendFeed(facetRecord, closest); | |
98 } | 313 } |
99 }); | 314 }); |
100 } | 315 } |
101 }; | 316 }; |
102 return handler; | 317 return handler; |
318 } | |
319 | |
320 | |
321 /** | |
322 * Get the chosen km for given cross-section. | |
323 */ | |
324 final public double getKmForCrossSectionIdx(final int index) { | |
325 double km = 0d; | |
326 int collIdx = 0; | |
327 while (km == 0d) { | |
328 CollectionItem item = collection.getItem(collIdx++); | |
329 if (item == null) { | |
330 break; | |
331 } | |
332 if (item.getData() == null) continue; | |
333 String d = item.getData().get("cross_section" + index + ".km"); | |
334 if (d == null) { | |
335 continue; | |
336 } | |
337 km = Double.valueOf(d); | |
338 } | |
339 return km; | |
103 } | 340 } |
104 | 341 |
105 | 342 |
106 /** | 343 /** |
107 * Create and configure the Grid to display. | 344 * Create and configure the Grid to display. |
132 SpinnerItem spinnerItem = new SpinnerItem(); | 369 SpinnerItem spinnerItem = new SpinnerItem(); |
133 spinnerItem.setShowTitle(false); | 370 spinnerItem.setShowTitle(false); |
134 spinnerItem.setTitle("Waterlevel-Spinner"); | 371 spinnerItem.setTitle("Waterlevel-Spinner"); |
135 spinnerItem.setWidth(45); | 372 spinnerItem.setWidth(45); |
136 // TODO actually get the value from artifact | 373 // TODO actually get the value from artifact |
137 // TODO actually get the range from artifact (or river?) | 374 spinnerItem.setDefaultValue( |
138 spinnerItem.setDefaultValue(0); | 375 getKmForCrossSectionIdx(facetRecord.getTheme().getIndex())); |
376 | |
377 // TODO actually use crosssectionkmservive | |
139 spinnerItem.setMin(0); | 378 spinnerItem.setMin(0); |
140 spinnerItem.setMax(1000); | 379 spinnerItem.setMax(1000); |
141 spinnerItem.setStep(5f); | 380 spinnerItem.setStep(5f); |
142 spinnerItem.setChangeOnKeypress(true); | 381 spinnerItem.setChangeOnKeypress(true); |
143 | 382 |
175 list.setCanEdit(true); | 414 list.setCanEdit(true); |
176 list.setCanSort(false); | 415 list.setCanSort(false); |
177 list.setShowRecordComponents(true); | 416 list.setShowRecordComponents(true); |
178 list.setShowRecordComponentsByCell(true); | 417 list.setShowRecordComponentsByCell(true); |
179 list.setShowHeader(true); | 418 list.setShowHeader(true); |
180 //list.setShowHeaderContextMenu(false); | |
181 list.setWidth100(); | 419 list.setWidth100(); |
182 list.setHeight100(); | 420 list.setHeight100(); |
183 | 421 |
184 list.addEditCompleteHandler(this); | 422 list.addEditCompleteHandler(this); |
185 | 423 |
193 ListGridField actions = new ListGridField(GRID_FIELD_ACTIONS, | 431 ListGridField actions = new ListGridField(GRID_FIELD_ACTIONS, |
194 MSG.chart_themepanel_header_actions(), 50); | 432 MSG.chart_themepanel_header_actions(), 50); |
195 | 433 |
196 list.setFields(active, name, actions); | 434 list.setFields(active, name, actions); |
197 } | 435 } |
436 | |
437 | |
438 /** Get Current Cross-section Masters uuid. */ | |
439 public String getCurrentMaster() { | |
440 return currentMasterUUID; | |
441 } | |
442 | |
443 | |
444 /** Set Current Cross-section Masters uuid. */ | |
445 public void setCurrentMaster(String currentMasterUuid) { | |
446 this.currentMasterUUID = currentMasterUuid; | |
447 } | |
198 } | 448 } |
199 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : | 449 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : |