View Javadoc

1   /*--------------------------------------------------------------------------
2    *  Copyright 2010 utgenome.org
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   *--------------------------------------------------------------------------*/
16  //--------------------------------------
17  // utgb-core Project
18  //
19  // WIGTrack.java
20  // Since: 2010/09/27
21  //
22  //--------------------------------------
23  package org.utgenome.gwt.utgb.client.track.lib;
24  
25  import java.util.ArrayList;
26  import java.util.Collections;
27  import java.util.Comparator;
28  import java.util.List;
29  
30  import org.utgenome.gwt.utgb.client.bio.ChrLoc;
31  import org.utgenome.gwt.utgb.client.bio.CompactWIGData;
32  import org.utgenome.gwt.utgb.client.canvas.BarGraphCanvas;
33  import org.utgenome.gwt.utgb.client.canvas.GWTGraphCanvas.GraphStyle;
34  import org.utgenome.gwt.utgb.client.canvas.GraphScale;
35  import org.utgenome.gwt.utgb.client.canvas.TrackWindowChain;
36  import org.utgenome.gwt.utgb.client.canvas.TrackWindowChain.WindowUpdateInfo;
37  import org.utgenome.gwt.utgb.client.track.Track;
38  import org.utgenome.gwt.utgb.client.track.TrackBase;
39  import org.utgenome.gwt.utgb.client.track.TrackConfig;
40  import org.utgenome.gwt.utgb.client.track.TrackConfigChange;
41  import org.utgenome.gwt.utgb.client.track.TrackFrame;
42  import org.utgenome.gwt.utgb.client.track.TrackGroup;
43  import org.utgenome.gwt.utgb.client.track.TrackGroupPropertyChange;
44  import org.utgenome.gwt.utgb.client.track.TrackWindow;
45  import org.utgenome.gwt.utgb.client.track.UTGBProperty;
46  import org.utgenome.gwt.widget.client.Style;
47  
48  import com.google.gwt.core.client.GWT;
49  import com.google.gwt.user.client.rpc.AsyncCallback;
50  import com.google.gwt.user.client.ui.AbsolutePanel;
51  import com.google.gwt.user.client.ui.FlexTable;
52  import com.google.gwt.user.client.ui.Widget;
53  
54  /**
55   * WIG data track
56   * 
57   * @author leo
58   * 
59   */
60  public class WIGTrack extends TrackBase {
61  
62  	public static TrackFactory factory() {
63  		return new TrackFactory() {
64  			@Override
65  			public Track newInstance() {
66  				return new WIGTrack();
67  			}
68  		};
69  	}
70  
71  	private final AbsolutePanel panel = new AbsolutePanel();
72  	private final FlexTable layoutTable = new FlexTable();
73  	private final GraphScale scale = new GraphScale();
74  
75  	private TrackWindowChain chain = new TrackWindowChain();
76  	private final List<List<BarGraphCanvas>> buffer = new ArrayList<List<BarGraphCanvas>>(2);
77  	private int frontBufferID = 0; // 0 or 1
78  
79  	private final GraphStyle style = new GraphStyle();
80  
81  	{
82  		buffer.add(new ArrayList<BarGraphCanvas>());
83  		buffer.add(new ArrayList<BarGraphCanvas>());
84  	}
85  
86  	public WIGTrack() {
87  		super("WIG Track");
88  		layoutTable.setBorderWidth(0);
89  		layoutTable.setCellPadding(0);
90  		layoutTable.setCellSpacing(0);
91  		Style.margin(layoutTable, 0);
92  		Style.padding(layoutTable, 0);
93  		Style.fullSize(layoutTable);
94  
95  		panel.add(scale, 0, 0);
96  		layoutTable.setWidget(0, 0, panel);
97  	}
98  
99  	public Widget getWidget() {
100 		return layoutTable;
101 	}
102 
103 	/**
104 	 * Configuration parameter names
105 	 */
106 	private final static String CONFIG_PATH = "path";
107 
108 	@Override
109 	public void setUp(TrackFrame trackFrame, TrackGroup group) {
110 		TrackConfig config = getConfig();
111 		config.addConfigString("Path", CONFIG_PATH, "");
112 		style.setup(config);
113 	}
114 
115 	@Override
116 	public void onChangeTrackHeight(int newHeight) {
117 		style.windowHeight = newHeight;
118 		needToUpdateStyle = true;
119 		refresh();
120 	}
121 
122 	private boolean needToUpdateStyle = true;
123 
124 	private void updateStyle() {
125 		// load the style parameter values from the configuration panel, then set the style
126 		style.load(getConfig());
127 
128 		needToUpdateStyle = false;
129 	}
130 
131 	private List<BarGraphCanvas> getFrontBuffer() {
132 		return buffer.get(frontBufferID);
133 	}
134 
135 	private List<BarGraphCanvas> getBackgroundBuffer() {
136 		return buffer.get((frontBufferID + 1) % 2);
137 	}
138 
139 	private void clearBuffer() {
140 		for (List<BarGraphCanvas> each : buffer) {
141 			clearBuffer(each);
142 		}
143 	}
144 
145 	private void clearBuffer(List<BarGraphCanvas> buffer) {
146 		for (Widget each : buffer) {
147 			each.removeFromParent();
148 		}
149 		buffer.clear();
150 	}
151 
152 	@Override
153 	public void draw() {
154 
155 		boolean needRedrawing = false;
156 
157 		if (needToUpdateStyle) {
158 			updateStyle();
159 			needRedrawing = true;
160 		}
161 
162 		final TrackWindow newWindow = getTrackWindow();
163 		panel.setPixelSize(newWindow.getPixelWidth(), style.windowHeight);
164 
165 		// swap the graph buffer when we have to scale the graphs
166 		if (chain.getViewWindow() != null && !chain.getViewWindow().hasSameScaleWith(newWindow)) {
167 			// switch the front buffer
168 			frontBufferID = (frontBufferID + 1) % 2;
169 			clearBuffer(getFrontBuffer());
170 		}
171 
172 		// update the view window
173 		WindowUpdateInfo updateInfo = chain.setViewWindow(newWindow);
174 		GWT.log(chain.getTrackWindowList().toString());
175 
176 		// scale the old canvases
177 		for (TrackWindow toDiscard : updateInfo.windowToDiscard) {
178 			for (BarGraphCanvas each : getBackgroundBuffer()) {
179 				TrackWindow old = each.getTrackWindow();
180 				if (old.equals(toDiscard)) {
181 					each.setTrackWindow(old.newPixelWidthWindow(newWindow.convertToPixelLength(old.getSequenceLength())),
182 							newWindow.convertToPixelX(old.getStartOnGenome()));
183 				}
184 			}
185 		}
186 
187 		// sort the windows in the nearest neighbor order from the view window 
188 		Collections.sort(updateInfo.windowToCreate, new Comparator<TrackWindow>() {
189 			public int compare(TrackWindow o1, TrackWindow o2) {
190 				int d1 = Math.abs(o1.center() - newWindow.center());
191 				int d2 = Math.abs(o2.center() - newWindow.center());
192 				return d1 - d2;
193 			}
194 		});
195 
196 		// move the graph canvases
197 		for (BarGraphCanvas each : getFrontBuffer()) {
198 			int x = newWindow.convertToPixelX(each.getTrackWindow().getStartOnGenome());
199 			Style.scrollX(each, x, 0.5);
200 		}
201 
202 		// load graph
203 		String filePath = resolvePropertyValues(getConfig().getString(CONFIG_PATH, ""));
204 		List<BarGraphCanvas> front = getFrontBuffer();
205 		for (final TrackWindow queryWindow : updateInfo.windowToCreate) {
206 			final BarGraphCanvas graph = new BarGraphCanvas(queryWindow, style.windowHeight);
207 			int x = newWindow.convertToPixelX(queryWindow.getStartOnGenome());
208 			panel.add(graph, x, 0);
209 			front.add(graph);
210 
211 			int s = queryWindow.getStartOnGenome();
212 			int e = queryWindow.getEndOnGenome();
213 			ChrLoc l = new ChrLoc(getTrackGroupProperty(UTGBProperty.TARGET), s, e);
214 			getBrowserService().getCompactWigDataList(filePath, queryWindow.getPixelWidth(), l, new AsyncCallback<List<CompactWIGData>>() {
215 				public void onFailure(Throwable e) {
216 					error("failed to retrieve wig data: " + e.getMessage());
217 					clearBackgroundGraph(queryWindow);
218 				}
219 
220 				public void onSuccess(List<CompactWIGData> graphData) {
221 					graph.setGraphData(graphData);
222 					if (style.autoScale)
223 						calculateScale();
224 
225 					// redraw scale
226 					scale.draw(style, newWindow);
227 
228 					// draw new graph
229 					graph.draw(graphData, style);
230 					clearBackgroundGraph(queryWindow);
231 				}
232 			});
233 
234 		}
235 
236 		// scale 
237 		boolean scaleHasChanged = style.autoScale && calculateScale();
238 		if (needRedrawing || scaleHasChanged) {
239 			// redraw scale
240 			if (updateInfo.windowToCreate.isEmpty())
241 				scale.draw(style, newWindow);
242 
243 			// redraw the already displayed graphs
244 			for (BarGraphCanvas each : getFrontBuffer()) {
245 				each.redraw(style);
246 			}
247 		}
248 
249 	}
250 
251 	void clearBackgroundGraph(TrackWindow window) {
252 		for (BarGraphCanvas each : getBackgroundBuffer()) {
253 			if (each.getTrackWindow().overlapWith(window)) {
254 				each.removeFromParent();
255 				each.clear();
256 			}
257 		}
258 
259 	}
260 
261 	/**
262 	 * 
263 	 * @return true if needs redrawing the graphs
264 	 */
265 	boolean calculateScale() {
266 
267 		if (!style.autoScale)
268 			return false;
269 
270 		float autoScaledMinValue = 0.0f;
271 		float autoScaledMaxValue = 0.0f;
272 
273 		final TrackWindow view = getTrackWindow();
274 		for (BarGraphCanvas each : getFrontBuffer()) {
275 			List<CompactWIGData> graphData = each.getGraphData();
276 			if (graphData == null)
277 				continue;
278 
279 			TrackWindow graphWindow = each.getTrackWindow();
280 
281 			int start = graphWindow.getStartOnGenome();
282 			int s = view.convertToPixelX(start);
283 
284 			int pw = view.getPixelWidth();
285 			int pw_e = graphWindow.getPixelWidth();
286 
287 			for (CompactWIGData wigData : graphData) {
288 
289 				int loopStart, loopEnd;
290 				if (!view.isReverseStrand()) {
291 					loopStart = Math.max(-s, 0);
292 					loopEnd = Math.min(pw - s, pw_e);
293 				}
294 				else {
295 					loopStart = Math.max(s - pw, 0);
296 					loopEnd = Math.min(s, pw_e);
297 				}
298 
299 				float data[] = wigData.getData();
300 				for (int pos = loopStart; pos < loopEnd; pos++) {
301 					autoScaledMinValue = Math.min(autoScaledMinValue, data[pos]);
302 					autoScaledMaxValue = Math.max(autoScaledMaxValue, data[pos]);
303 				}
304 			}
305 
306 		}
307 		GWT.log("scale: " + autoScaledMinValue + " - " + autoScaledMaxValue);
308 
309 		// when the graph contains no data, use the default min/max values for the scale
310 		if (autoScaledMinValue == autoScaledMaxValue) {
311 			autoScaledMinValue = style.minValue;
312 			autoScaledMaxValue = style.maxValue;
313 		}
314 
315 		// whether to need to update the graph?
316 		boolean hasChanged = (autoScaledMinValue != style.autoScaledMin || autoScaledMaxValue != style.autoScaledMax);
317 		style.autoScaledMin = autoScaledMinValue;
318 		style.autoScaledMax = autoScaledMaxValue;
319 
320 		return hasChanged;
321 	}
322 
323 	@Override
324 	public void onChangeTrackConfig(TrackConfigChange change) {
325 
326 		if (change.contains(CONFIG_PATH)) {
327 			needToUpdateStyle = true;
328 			refresh();
329 		}
330 		else {
331 			needToUpdateStyle = true;
332 			refresh();
333 		}
334 	}
335 
336 	@Override
337 	public void onChangeTrackWindow(TrackWindow newWindow) {
338 		refresh();
339 	}
340 
341 	@Override
342 	public void onChangeTrackGroupProperty(TrackGroupPropertyChange change) {
343 
344 		if (change.containsOneOf(new String[] { UTGBProperty.TARGET, UTGBProperty.REVISION, UTGBProperty.SPECIES })) {
345 
346 			chain.clear();
347 			clearBuffer();
348 			refresh();
349 		}
350 	}
351 
352 }