teichmann@5861: /* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde teichmann@5861: * Software engineering by Intevation GmbH teichmann@5861: * teichmann@5861: * This file is Free Software under the GNU AGPL (>=v3) teichmann@5861: * and comes with ABSOLUTELY NO WARRANTY! Check out the teichmann@5861: * documentation coming with Dive4Elements River for details. teichmann@5861: */ teichmann@5861: teichmann@5835: package org.dive4elements.river.client.client.ui.chart; ingo@521: ingo@521: import com.google.gwt.core.client.GWT; ingo@538: import com.google.gwt.user.client.rpc.AsyncCallback; ingo@610: import com.smartgwt.client.types.Overflow; ingo@521: import com.smartgwt.client.widgets.Canvas; ingo@521: import com.smartgwt.client.widgets.Img; christian@2983: import com.smartgwt.client.widgets.events.ResizedEvent; christian@2983: import com.smartgwt.client.widgets.events.ResizedHandler; ingo@521: import com.smartgwt.client.widgets.layout.HLayout; ingo@521: import com.smartgwt.client.widgets.layout.VLayout; ingo@521: teichmann@5835: import org.dive4elements.river.client.client.Config; teichmann@5835: import org.dive4elements.river.client.client.event.OutputParameterChangeEvent; teichmann@5835: import org.dive4elements.river.client.client.event.OutputParameterChangeHandler; teichmann@5835: import org.dive4elements.river.client.client.event.PanEvent; teichmann@5835: import org.dive4elements.river.client.client.event.PanHandler; teichmann@5835: import org.dive4elements.river.client.client.event.RedrawRequestEvent; teichmann@5835: import org.dive4elements.river.client.client.event.RedrawRequestEvent.Type; teichmann@5835: import org.dive4elements.river.client.client.event.RedrawRequestHandler; teichmann@5835: import org.dive4elements.river.client.client.event.ZoomEvent; teichmann@5835: import org.dive4elements.river.client.client.event.ZoomHandler; teichmann@5835: import org.dive4elements.river.client.client.services.ChartInfoService; teichmann@5835: import org.dive4elements.river.client.client.services.ChartInfoServiceAsync; teichmann@5835: import org.dive4elements.river.client.client.ui.CollectionView; teichmann@5835: import org.dive4elements.river.client.client.ui.OutputTab; teichmann@5835: import org.dive4elements.river.client.shared.Transform2D; teichmann@5835: import org.dive4elements.river.client.shared.model.Axis; teichmann@5835: import org.dive4elements.river.client.shared.model.ChartInfo; teichmann@5835: import org.dive4elements.river.client.shared.model.Collection; teichmann@5835: import org.dive4elements.river.client.shared.model.OutputMode; teichmann@5835: import org.dive4elements.river.client.shared.model.ZoomObj; christian@2983: christian@2983: import java.util.Date; christian@2983: import java.util.HashMap; christian@2983: import java.util.Map; christian@2983: import java.util.Stack; ingo@521: ingo@521: ingo@521: /** felix@4037: * Tab representing and showing one Chart-output (diagram). felix@1545: * ingo@521: * @author Ingo Weinzierl ingo@521: */ ingo@531: public class ChartOutputTab ingo@531: extends OutputTab felix@855: implements ResizedHandler, felix@855: OutputParameterChangeHandler, felix@855: ZoomHandler, felix@858: PanHandler, felix@858: RedrawRequestHandler ingo@531: { ingo@521: public static final int DEFAULT_CHART_WIDTH = 600; ingo@521: public static final int DEFAULT_CHART_HEIGHT = 500; ingo@521: christian@3717: public static final int THEMEPANEL_MIN_WIDTH = 250; ingo@527: felix@855: /** The service that is used to fetch chart information. */ ingo@538: protected ChartInfoServiceAsync info = GWT.create(ChartInfoService.class); ingo@538: ingo@552: /** The ChartInfo object that provides information about the current felix@855: * chart. */ ingo@552: protected ChartInfo chartInfo; ingo@552: felix@4037: /** Transformer used to transform image pixels into chart (data) coordinates. */ ingo@561: protected Transform2D[] transformer; ingo@538: ingo@538: /** The collection view.*/ ingo@538: protected CollectionView view; ingo@538: felix@1579: /** The ThemePanel to expose control over themes (facettes). */ ingo@911: protected ChartThemePanel ctp; ingo@911: felix@855: /** The canvas that wraps the chart toolbar. */ raimund@2936: protected ChartToolbar tbarPanel; ingo@521: felix@855: /** The canvas that wraps the theme editor. */ ingo@521: protected Canvas left; ingo@521: felix@855: /** The canvas that wraps the chart. */ ingo@521: protected Canvas right; ingo@521: ingo@610: protected Img chart; ingo@610: felix@855: /** Chart zoom options. */ ingo@561: protected int[] xrange; ingo@561: protected int[] yrange; ingo@561: felix@1579: /** Stack of ZoomObj to allow 'redo last zoom'-kind of actions. */ ingo@1280: protected Stack zoomStack; ingo@1597: protected Number[] zoom; ingo@542: ingo@542: ingo@521: /** ingo@521: * The default constructor to create a new ChartOutputTab. ingo@521: * ingo@521: * @param title The title of this tab. ingo@521: * @param collection The Collection which this chart belongs to. ingo@521: * @param mode The OutputMode. felix@858: * @param collectionView The shown collection. ingo@521: */ ingo@521: public ChartOutputTab( sascha@615: String title, ingo@521: Collection collection, ingo@521: OutputMode mode, ingo@521: CollectionView collectionView ingo@521: ){ ingo@1364: super(title, collection, collectionView, mode); ingo@521: ingo@538: view = collectionView; ingo@521: left = new Canvas(); ingo@521: right = new Canvas(); ingo@561: xrange = new int[2]; ingo@561: yrange = new int[2]; ingo@1280: zoomStack = new Stack(); ingo@521: ingo@1597: zoom = new Number[] { felix@3694: new Double(0), new Double(1), felix@3694: new Double(0), new Double(1) }; ingo@1597: christian@3532: left.setBorder("1px solid gray"); ingo@527: left.setWidth(THEMEPANEL_MIN_WIDTH); ingo@527: left.setMinWidth(THEMEPANEL_MIN_WIDTH); ingo@521: right.setWidth("*"); ingo@521: ingo@521: VLayout vLayout = new VLayout(); ingo@521: vLayout.setMembersMargin(2); ingo@521: ingo@521: HLayout hLayout = new HLayout(); ingo@521: hLayout.setWidth100(); ingo@521: hLayout.setHeight100(); ingo@521: hLayout.setMembersMargin(10); ingo@521: ingo@521: hLayout.addMember(left); ingo@521: hLayout.addMember(right); ingo@521: ingo@3370: ctp = createThemePanel(mode, collectionView); ingo@3370: if (ctp != null) { ingo@3370: ctp.addRedrawRequestHandler(this); ingo@3370: ctp.addOutputParameterChangeHandler(this); ingo@3370: left.addChild(ctp); felix@858: } felix@858: else { ingo@3370: left.setVisible(false); felix@858: } felix@858: ingo@610: chart = createChartImg(); ingo@610: right.addChild(chart); ingo@610: right.setOverflow(Overflow.HIDDEN); sascha@3547: christian@3532: left.setShowResizeBar(true); ingo@521: ingo@3370: tbarPanel = createChartToolbar(this); ingo@521: vLayout.addMember(tbarPanel); ingo@521: vLayout.addMember(hLayout); christian@3717: vLayout.setOverflow(Overflow.HIDDEN); ingo@521: ingo@521: setPane(vLayout); ingo@521: ingo@521: right.addResizedHandler(this); ingo@521: } sascha@3371: sascha@3371: ingo@3370: public ChartThemePanel createThemePanel( ingo@3370: OutputMode mode, CollectionView view ingo@3370: ) { ingo@3370: // Output "cross_section" needs slightly modified ThemePanel ingo@3370: // (with action buttons). ingo@3370: if (mode.getName().equals("cross_section")) { ingo@3370: return new CrossSectionChartThemePanel(mode, view); ingo@3370: } ingo@3370: else { ingo@3370: return new ChartThemePanel(mode, view); ingo@3370: } ingo@3370: } sascha@3371: sascha@3371: ingo@3370: public ChartToolbar createChartToolbar(ChartOutputTab tab) { ingo@3370: return new ChartToolbar(tab); ingo@3370: } ingo@521: ingo@521: christian@2983: public void toggleThemePanel() { christian@2983: this.left.setVisible(!left.isVisible()); christian@2983: } christian@2983: christian@2983: ingo@521: /** ingo@521: * This method is called after the chart panel has resized. It removes the ingo@521: * chart - if existing - and requests a new one with adjusted size. ingo@521: * ingo@521: * @param event The resize event. ingo@521: */ christian@2983: @Override ingo@521: public void onResized(ResizedEvent event) { ingo@531: updateChartPanel(); ingo@561: updateChartInfo(); ingo@531: } ingo@531: ingo@531: felix@1550: /** For RESET type of events, just reset the ranges, otherwise do a felix@1550: * complete refresh of panel, info and collection. */ christian@2983: @Override ingo@911: public void onRedrawRequest(RedrawRequestEvent event) { ingo@911: if (event.getType() == Type.RESET) { ingo@911: resetRanges(); ingo@911: } ingo@911: else { ingo@911: ctp.updateCollection(); ingo@911: updateChartPanel(); ingo@911: updateChartInfo(); ingo@911: } felix@858: } felix@858: felix@858: ingo@531: /** ingo@531: * Listens to change event in the chart them panel and updates chart after ingo@531: * receiving such an event. ingo@531: * ingo@531: * @param event The OutputParameterChangeEvent. ingo@531: */ christian@2983: @Override ingo@531: public void onOutputParameterChanged(OutputParameterChangeEvent event) { raimund@2477: updateChartInfo(); ingo@531: updateChartPanel(); ingo@531: } ingo@531: ingo@531: ingo@538: /** ingo@541: * Listens to zoom events and refreshes the current chart in such case. ingo@541: * ingo@541: * @param evt The ZoomEvent that stores the coordinates for zooming. ingo@541: */ christian@2983: @Override ingo@541: public void onZoom(ZoomEvent evt) { ingo@1280: zoomStack.push(new ZoomObj(zoom[0], zoom[1], zoom[2], zoom[3])); ingo@1280: ingo@561: xrange[0] = evt.getStartX(); ingo@561: xrange[1] = evt.getEndX(); ingo@561: yrange[0] = evt.getStartY(); ingo@561: yrange[1] = evt.getEndY(); ingo@541: ingo@561: xrange[0] = xrange[0] < xrange[1] ? xrange[0] : xrange[1]; ingo@561: yrange[0] = yrange[0] < yrange[1] ? yrange[0] : yrange[1]; ingo@541: ingo@561: translateCoordinates(); ingo@561: ingo@561: updateChartInfo(); ingo@542: updateChartPanel(); ingo@541: } ingo@541: ingo@541: ingo@1597: protected Number[] translateCoordinates() { ingo@561: if (xrange == null || (xrange[0] == 0 && xrange[1] == 0)) { ingo@561: zoom[0] = 0d; ingo@561: zoom[1] = 1d; ingo@561: } ingo@561: else { ingo@561: translateXCoordinates(); ingo@561: } ingo@561: ingo@561: if (yrange == null || (yrange[0] == 0 && yrange[1] == 0)) { ingo@561: zoom[2] = 0d; ingo@561: zoom[3] = 1d; ingo@561: } ingo@561: else { ingo@561: translateYCoordinates(); ingo@561: } ingo@561: ingo@561: return zoom; ingo@561: } ingo@561: ingo@561: ingo@561: protected void translateXCoordinates() { ingo@561: Axis xAxis = chartInfo.getXAxis(0); ingo@561: ingo@1597: Number xmin = xAxis.getMin(); ingo@1597: Number xmax = xAxis.getMax(); ingo@1597: Number xRange = subtract(xmax, xmin); ingo@561: ingo@561: Transform2D transformer = getTransformer(0); ingo@561: ingo@561: double[] start = transformer.transform(xrange[0], yrange[0]); ingo@561: double[] end = transformer.transform(xrange[1], yrange[1]); ingo@561: ingo@1597: zoom[0] = divide(subtract(start[0], xmin), xRange); ingo@1597: zoom[1] = divide(subtract(end[0], xmin), xRange); ingo@561: } ingo@561: ingo@561: ingo@561: protected void translateYCoordinates() { ingo@561: Axis yAxis = chartInfo.getYAxis(0); ingo@561: ingo@1597: Number ymin = yAxis.getMin(); ingo@1597: Number ymax = yAxis.getMax(); ingo@1597: Number yRange = subtract(ymax, ymin); ingo@561: ingo@561: Transform2D transformer = getTransformer(0); ingo@561: ingo@561: double[] start = transformer.transform(xrange[0], yrange[0]); ingo@561: double[] end = transformer.transform(xrange[1], yrange[1]); ingo@561: ingo@1597: zoom[2] = divide(subtract(start[1], ymin), yRange); ingo@1597: zoom[3] = divide(subtract(end[1], ymin), yRange); ingo@561: } ingo@561: ingo@561: christian@2983: @Override ingo@552: public void onPan(PanEvent event) { ingo@561: if (chartInfo == null) { sascha@555: return; sascha@555: } ingo@561: ingo@552: int[] start = event.getStartPos(); ingo@552: int[] end = event.getEndPos(); ingo@552: ingo@561: Transform2D t = getTransformer(); ingo@552: ingo@561: double[] ts = t.transform(start[0], start[1]); ingo@561: double[] tt = t.transform(end[0], end[1]); ingo@552: ingo@561: double diffX = ts[0] - tt[0]; ingo@561: double diffY = ts[1] - tt[1]; ingo@561: ingo@552: Axis xAxis = chartInfo.getXAxis(0); ingo@552: Axis yAxis = chartInfo.getYAxis(0); ingo@552: ingo@1597: Number[] x = panAxis(xAxis, diffX); ingo@1597: Number[] y = panAxis(yAxis, diffY); ingo@552: felix@3694: // Set the zoom coordinates. ingo@561: zoom[0] = x[0]; ingo@561: zoom[1] = x[1]; ingo@561: zoom[2] = y[0]; ingo@561: zoom[3] = y[1]; ingo@561: ingo@561: updateChartInfo(); ingo@552: updateChartPanel(); ingo@552: } ingo@552: ingo@552: ingo@1597: protected Number[] panAxis(Axis axis, double diff) { ingo@1597: Number min = axis.getFrom(); ingo@1597: Number max = axis.getTo(); ingo@561: ingo@1597: min = add(min, diff); ingo@1597: max = add(max, diff); ingo@561: ingo@561: return computeZoom(axis, min, max); ingo@561: } ingo@561: ingo@561: ingo@543: public void resetRanges() { ingo@1281: zoomStack.push(new ZoomObj(zoom[0], zoom[1], zoom[2], zoom[3])); ingo@1281: ingo@561: zoom[0] = 0d; ingo@561: zoom[1] = 1d; ingo@561: zoom[2] = 0d; ingo@561: zoom[3] = 1d; ingo@543: ingo@561: updateChartInfo(); ingo@543: updateChartPanel(); ingo@543: } ingo@543: ingo@543: ingo@541: /** ingo@1281: * This method zooms the current chart out by a given factor. ingo@1281: * ingo@1281: * @param factor The factor should be between 0-100. ingo@1281: */ ingo@1281: public void zoomOut(int factor) { ingo@1281: if (factor < 0 || factor > 100 || chartInfo == null) { ingo@1281: return; ingo@1281: } ingo@1281: ingo@1281: zoomStack.push(new ZoomObj(zoom[0], zoom[1], zoom[2], zoom[3])); ingo@1281: ingo@1281: Axis xAxis = chartInfo.getXAxis(0); ingo@1281: Axis yAxis = chartInfo.getYAxis(0); ingo@1281: ingo@1597: Number[] x = zoomAxis(xAxis, factor); ingo@1597: Number[] y = zoomAxis(yAxis, factor); ingo@1281: ingo@1281: zoom[0] = x[0]; ingo@1281: zoom[1] = x[1]; ingo@1282: zoom[2] = y[0]; ingo@1281: zoom[3] = y[1]; ingo@1281: ingo@1281: updateChartInfo(); ingo@1281: updateChartPanel(); ingo@1281: } ingo@1281: ingo@1281: ingo@1281: /** felix@2542: * This method is used to zoom out. Zooming out is realized with a stacked ingo@1280: * logic. Initially, you cannot zoom out. For each time you start a zoom-in ingo@1280: * action, the extent of the chart is stored and pushed onto a stack. A felix@2542: * zoom-out will now lead you to the last extent that is popped from stack. ingo@544: */ ingo@1280: public void zoomOut() { ingo@1280: if (!zoomStack.empty()) { ingo@1280: zoom = zoomStack.pop().getZoom(); ingo@548: ingo@1280: updateChartInfo(); ingo@1280: updateChartPanel(); ingo@1280: } ingo@544: } ingo@544: ingo@544: ingo@1597: public Number[] zoomAxis(Axis axis, int factor) { ingo@1282: GWT.log("Prepare Axis for zooming (factor: " + factor + ")"); ingo@561: ingo@1597: Number min = axis.getMin(); ingo@1597: Number max = axis.getMax(); ingo@1597: Number range = isBigger(max, min) ? subtract(max, min) : subtract(min, max); ingo@561: ingo@1597: Number curFrom = axis.getFrom(); ingo@1597: Number curTo = axis.getTo(); ingo@561: ingo@1597: Number diff = isBigger(curTo, curFrom) ingo@1597: ? subtract(curTo, curFrom) ingo@1597: : subtract(curFrom, curTo); ingo@1282: ingo@1282: GWT.log(" max from : " + min); ingo@1282: GWT.log(" max to : " + max); ingo@1282: GWT.log(" max range : " + range); ingo@1282: GWT.log(" current from: " + curFrom); ingo@1282: GWT.log(" current to : " + curTo); ingo@1282: GWT.log(" current diff: " + diff); ingo@1282: ingo@1597: Number newFrom = subtract(curFrom, divide(multi(diff, factor), 100)); ingo@1597: Number newTo = add(curTo, divide(multi(diff, factor), 100)); ingo@1282: ingo@1282: GWT.log(" new from: " + newFrom); ingo@1282: GWT.log(" new to : " + newTo); ingo@1282: ingo@1597: return new Number[] { ingo@1597: divide(subtract(newFrom, min), range), ingo@1597: divide(subtract(newTo, min), range) ingo@1282: }; ingo@561: } ingo@561: ingo@561: ingo@1597: public static Number[] computeZoom(Axis axis, Number min, Number max) { felix@3694: Number[] hereZoom = new Number[2]; ingo@561: ingo@1597: Number absMin = axis.getMin(); ingo@1597: Number absMax = axis.getMax(); ingo@1597: Number diff = isBigger(absMax, absMin) ingo@1597: ? subtract(absMax, absMin) ingo@1597: : subtract(absMin, absMax); ingo@561: felix@3694: hereZoom[0] = divide(subtract(min, absMin), diff); felix@3694: hereZoom[1] = divide(subtract(max, absMin), diff); ingo@561: felix@3694: return hereZoom; ingo@561: } ingo@561: ingo@561: felix@1551: /** Get Collection from ChartThemePanel. .*/ felix@1551: public Collection getCtpCollection() { felix@1551: return this.ctp.getCollection(); felix@1551: } felix@1551: felix@1551: ingo@544: /** ingo@538: * Updates the Transform2D object using the chart info service. ingo@538: */ ingo@561: public void updateChartInfo() { ingo@538: Config config = Config.getInstance(); ingo@538: String locale = config.getLocale(); ingo@538: ingo@538: info.getChartInfo( ingo@538: view.getCollection(), ingo@538: locale, ingo@538: mode.getName(), ingo@549: getChartAttributes(), ingo@552: new AsyncCallback() { christian@2983: @Override ingo@538: public void onFailure(Throwable caught) { ingo@610: GWT.log("ChartInfo ERROR: " + caught.getMessage()); ingo@538: } ingo@538: christian@2983: @Override ingo@552: public void onSuccess(ChartInfo chartInfo) { ingo@552: setChartInfo(chartInfo); ingo@538: } ingo@538: }); ingo@538: } ingo@538: ingo@538: ingo@531: public void updateChartPanel() { ingo@610: int w = right.getWidth(); ingo@610: int h = right.getHeight(); ingo@521: ingo@610: chart.setSrc(getImgUrl(w, h)); ingo@521: } ingo@521: ingo@521: ingo@542: /** ingo@542: * Returns the existing chart panel. ingo@542: * ingo@542: * @return the existing chart panel. ingo@542: */ ingo@534: public Canvas getChartPanel() { ingo@610: return right; ingo@610: } ingo@538: ingo@538: felix@4037: /** Access the Canvas holding the rendered Chart. */ ingo@610: public Canvas getChartImg() { ingo@610: return chart; ingo@538: } ingo@538: ingo@538: felix@4037: /** Get associated ChartInfo object. */ ingo@552: public ChartInfo getChartInfo() { ingo@552: return chartInfo; ingo@552: } ingo@552: ingo@552: ingo@552: protected void setChartInfo(ChartInfo chartInfo) { ingo@552: this.chartInfo = chartInfo; ingo@561: } ingo@561: ingo@561: ingo@561: public Transform2D getTransformer() { felix@4037: return getTransformer(0); ingo@552: } ingo@552: ingo@552: ingo@538: /** ingo@538: * Returns the Transform2D object used to transform image coordinates into felix@4037: * chart (data) coordinates. ingo@538: * ingo@561: * @param pos The index of a specific transformer. ingo@561: * ingo@538: * @return the Transform2D object. ingo@538: */ ingo@561: public Transform2D getTransformer(int pos) { ingo@561: if (chartInfo == null) { ingo@561: return null; ingo@561: } ingo@538: ingo@561: return chartInfo.getTransformer(pos); ingo@534: } ingo@534: ingo@534: ingo@542: /** felix@2542: * Returns the Transform2D count. raimund@2477: * raimund@2477: * @return the Transformer2D count raimund@2477: */ raimund@2477: public int getTransformerCount() { raimund@2477: if (chartInfo == null) { raimund@2477: return 0; raimund@2477: } raimund@2477: raimund@2477: return chartInfo.getTransformerCount(); raimund@2477: } raimund@2477: raimund@2477: raimund@2477: /** ingo@542: * Creates a new chart panel with default size. ingo@542: * ingo@542: * @return the created chart panel. ingo@542: */ ingo@610: protected Img createChartImg() { ingo@610: return createChartImg(DEFAULT_CHART_WIDTH, DEFAULT_CHART_HEIGHT); ingo@521: } ingo@521: ingo@521: ingo@542: /** ingo@542: * Creates a new chart panel with specified width and height. ingo@542: * ingo@542: * @param width The width for the chart panel. ingo@542: * @param height The height for the chart panel. ingo@542: * ingo@542: * @return the created chart panel. ingo@542: */ ingo@610: protected Img createChartImg(int width, int height) { ingo@521: Img chart = getChartImg(width, height); ingo@521: chart.setWidth100(); ingo@521: chart.setHeight100(); ingo@521: ingo@521: return chart; ingo@521: } ingo@521: ingo@521: ingo@521: /** ingo@521: * Builds the chart image and returns it. ingo@521: * ingo@521: * @param width The chart width. ingo@521: * @param height The chart height. ingo@521: * ingo@521: * @return the chart image. ingo@521: */ ingo@521: protected Img getChartImg(int width, int height) { ingo@521: return new Img(getImgUrl(width, height)); ingo@521: } ingo@521: ingo@521: ingo@521: /** ingo@521: * Builds the URL that points to the chart image. ingo@521: * ingo@521: * @param width The width of the requested chart. ingo@521: * @param height The height of the requested chart. ingo@542: * @param xr Optional x range (used for zooming). ingo@542: * @param yr Optional y range (used for zooming). ingo@521: * ingo@521: * @return the URL to the chart image. ingo@521: */ ingo@521: protected String getImgUrl(int width, int height) { ingo@521: Config config = Config.getInstance(); ingo@521: ingo@521: String imgUrl = GWT.getModuleBaseURL(); ingo@521: imgUrl += "chart"; ingo@521: imgUrl += "?uuid=" + collection.identifier(); ingo@521: imgUrl += "&type=" + mode.getName(); ingo@521: imgUrl += "&locale=" + config.getLocale(); ingo@521: imgUrl += "×tamp=" + new Date().getTime(); ingo@521: imgUrl += "&width=" + Integer.toString(width); ingo@521: imgUrl += "&height=" + Integer.toString(height); ingo@521: ingo@1597: Number[] zoom = getZoomValues(); ingo@542: ingo@561: if (zoom != null) { ingo@1597: if (zoom[0].intValue() != 0 || zoom[1].intValue() != 1) { ingo@612: // a zoom range of 0-1 means displaying the whole range. In such ingo@612: // case we don't need to zoom. ingo@1597: imgUrl += "&minx=" + zoom[0]; ingo@1597: imgUrl += "&maxx=" + zoom[1]; ingo@612: } ingo@2391: ingo@1597: if (zoom[2].intValue() != 0 || zoom[3].intValue() != 1) { ingo@612: // a zoom range of 0-1 means displaying the whole range. In such ingo@612: // case we don't need to zoom. ingo@1597: imgUrl += "&miny=" + zoom[2]; ingo@1597: imgUrl += "&maxy=" + zoom[3]; ingo@612: } ingo@542: } ingo@542: ingo@521: return imgUrl; ingo@521: } ingo@549: ingo@549: felix@2542: /** Get link to export image in given dimension and format. */ ingo@1344: public String getExportUrl(int width, int height, String format) { ingo@1344: String url = getImgUrl(width, height); ingo@1344: url += "&format=" + format; ingo@1344: url += "&export=true"; ingo@1344: ingo@1344: return url; ingo@1344: } ingo@1344: ingo@1344: raimund@2936: public Map getChartAttributes() { ingo@549: Map attr = new HashMap(); ingo@549: ingo@549: Canvas chart = getChartPanel(); felix@1453: attr.put("width", chart.getWidth().toString()); ingo@549: attr.put("height", chart.getHeight().toString()); ingo@549: ingo@1597: Number[] zoom = getZoomValues(); ingo@549: ingo@561: if (zoom != null) { ingo@1597: if (zoom[0].intValue() != 0 || zoom[1].intValue() != 1) { ingo@612: // a zoom range of 0-1 means displaying the whole range. In such ingo@612: // case we don't need to zoom. ingo@1597: attr.put("minx", zoom[0].toString()); ingo@1597: attr.put("maxx", zoom[1].toString()); ingo@612: } ingo@1597: if (zoom[2].intValue() != 0 || zoom[3].intValue() != 1) { ingo@612: // a zoom range of 0-1 means displaying the whole range. In such ingo@612: // case we don't need to zoom. ingo@1597: attr.put("miny", zoom[2].toString()); ingo@1597: attr.put("maxy", zoom[3].toString()); ingo@612: } ingo@549: } ingo@549: ingo@549: return attr; ingo@549: } ingo@561: ingo@561: ingo@1597: protected Number[] getZoomValues() { ingo@561: return zoom; ingo@561: } felix@1545: felix@1545: felix@1545: /** Return the 'parent' CollectionView. */ felix@1545: public CollectionView getView() { felix@1545: return this.view; felix@1545: } ingo@1597: ingo@1597: ingo@1597: public static Number subtract(Number left, Number right) { ingo@1597: if (left instanceof Double) { ingo@1597: return new Double(left.doubleValue() - right.doubleValue()); ingo@1597: } ingo@1597: else if (left instanceof Long) { ingo@1597: return new Long(left.longValue() - right.longValue()); ingo@1597: } ingo@1597: else { ingo@1597: return new Integer(left.intValue() - right.intValue()); ingo@1597: } ingo@1597: } ingo@1597: ingo@1597: felix@3694: /** Add two numbers, casting to Type of param left. */ ingo@1597: public static Number add(Number left, Number right) { ingo@1597: if (left instanceof Double) { ingo@1597: return new Double(left.doubleValue() + right.doubleValue()); ingo@1597: } ingo@1597: else if (left instanceof Long) { ingo@1597: return new Long(left.longValue() + right.longValue()); ingo@1597: } ingo@1597: else { ingo@1597: return new Integer(left.intValue() + right.intValue()); ingo@1597: } ingo@1597: } ingo@1597: ingo@1597: felix@3694: /** Divde left by right. Note that Long will be casted to double. */ ingo@1597: public static Number divide(Number left, Number right) { ingo@1597: if (left instanceof Double) { ingo@1597: return new Double(left.doubleValue() / right.doubleValue()); ingo@1597: } ingo@1597: else if (left instanceof Long) { felix@3694: return new Double(left.doubleValue() / right.doubleValue()); ingo@1597: } ingo@1597: else { ingo@1597: return new Integer(left.intValue() / right.intValue()); ingo@1597: } ingo@1597: } ingo@1597: ingo@1597: ingo@1597: public static Number multi(Number left, Number right) { ingo@1597: if (left instanceof Double) { ingo@1597: return new Double(left.doubleValue() * right.doubleValue()); ingo@1597: } ingo@1597: else if (left instanceof Long) { ingo@1597: return new Long(left.longValue() * right.longValue()); ingo@1597: } ingo@1597: else { ingo@1597: return new Integer(left.intValue() * right.intValue()); ingo@1597: } ingo@1597: } ingo@1597: ingo@1597: ingo@1597: public static boolean isBigger(Number left, Number right) { ingo@1597: if (left instanceof Double) { ingo@1597: return left.doubleValue() > right.doubleValue(); ingo@1597: } ingo@1597: else if (left instanceof Long) { ingo@1597: return left.longValue() > right.longValue(); ingo@1597: } ingo@1597: else { ingo@1597: return left.intValue() > right.intValue(); ingo@1597: } ingo@1597: } ingo@521: } ingo@521: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :