compilable but untested version of a rank charts dialog

This commit is contained in:
Axel Uhl
2012-11-16 21:42:22 +01:00
parent 066a8880b2
commit 86de8211b7
14 changed files with 254 additions and 64 deletions
@@ -267,4 +267,6 @@ public interface SailingService extends RemoteService {
void suppressCompetitorInLeaderboard(String leaderboardName, String competitorIdAsString, boolean suppressed);
void updateLeaderboardColumnFactor(String leaderboardName, String columnName, Double newFactor);
List<Pair<String, List<CompetitorDTO>>> getRankedCompetitorsFromBestToWorstAfterEachRaceColumn(String leaderboardName, Date date) throws NoWindException;
}
@@ -425,5 +425,6 @@ public interface SailingServiceAsync {
void updateLeaderboardColumnFactor(String leaderboardName, String columnName, Double newFactor,
AsyncCallback<Void> callback);
void getRankedCompetitorsFromBestToWorstAfterEachRaceColumn(String leaderboardName, Date date,
AsyncCallback<List<Pair<String, List<CompetitorDTO>>>> callback);
}
@@ -433,4 +433,6 @@ public interface StringMessages extends Messages {
String pattern();
String legend();
String live();
String afterRace();
String errorFetchingRankingChartData(String message);
}
@@ -434,3 +434,5 @@ shape=Shape
pattern=Pattern
legend=Legend
live=live
afterRace=after race
errorFetchingRankingChartData=Error fetching ranking chart data: {0}
@@ -435,4 +435,5 @@ shape=Form
pattern=Muster
legend=Legende
live=live
afterRace=nach Rennen
errorFetchingRankingChartData=Fehler beim Laden der Daten für das Ranglisten-Chart: {0}
@@ -55,14 +55,14 @@ public class CompareCompetitorsChartDialog extends DialogBoxExt implements RaceT
public CompareCompetitorsChartDialog(SailingServiceAsync sailingService,
List<RegattaAndRaceIdentifier> races, final RaceTimesInfoProvider raceTimesInfoProvider, final CompetitorSelectionProvider competitorSelectionProvider,
Timer timer, StringMessages stringConstants, ErrorReporter errorReporter) {
super(new Label(stringConstants.close()));
Timer timer, StringMessages stringMessages, ErrorReporter errorReporter) {
super(new Label(stringMessages.close()));
this.sailingService = sailingService;
this.timer = timer;
this.errorReporter = errorReporter;
this.timeRangeWithZoomProvider = new TimeRangeWithZoomModel();
this.setPopupPosition(15, 15);
this.setHTML(stringConstants.compareCompetitors());
this.setHTML(stringMessages.compareCompetitors());
this.setWidth(Window.getClientWidth() - 250 + "px");
this.setAnimationEnabled(true);
@@ -71,13 +71,13 @@ public class CompareCompetitorsChartDialog extends DialogBoxExt implements RaceT
raceSelectionProvider.setAllRaces(races);
multiChartPanel = new MultiChartPanel(sailingService, new AsyncActionsExecutor(), competitorSelectionProvider, raceSelectionProvider,
timer, timeRangeWithZoomProvider, stringConstants, errorReporter, false, false);
timer, timeRangeWithZoomProvider, stringMessages, errorReporter, false, false);
multiChartPanel.setSize("100%", "100%");
VerticalPanel contentPanel = new VerticalPanel();
contentPanel.setSize("100%", "100%");
ComponentToolbar<MultiChartSettings> toolbar = new ComponentToolbar<MultiChartSettings>(multiChartPanel, stringConstants);
ComponentToolbar<MultiChartSettings> toolbar = new ComponentToolbar<MultiChartSettings>(multiChartPanel, stringMessages);
toolbar.addSettingsButton();
FlowPanel flowPanel = new FlowPanel();
@@ -85,7 +85,7 @@ public class CompareCompetitorsChartDialog extends DialogBoxExt implements RaceT
HorizontalPanel raceSelectionPanel = new HorizontalPanel();
raceSelectionPanel.setSpacing(3);
racesListBox = new ListBox();
raceSelectionPanel.add(new Label(stringConstants.pleaseSelectARace() + ":"));
raceSelectionPanel.add(new Label(stringMessages.pleaseSelectARace() + ":"));
raceSelectionPanel.add(racesListBox);
for (RegattaAndRaceIdentifier race : raceSelectionProvider.getAllRaces()) {
racesListBox.addItem(race.toString());
@@ -1247,6 +1247,14 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
playPause.addClickHandler(playPauseHandler);
playStateChanged(timer.getPlayState(), timer.getPlayMode());
refreshPanel.add(playPause);
Anchor rankChartsAnchor = new Anchor(AbstractImagePrototype.create(chartIcon).getSafeHtml());
rankChartsAnchor.setTitle(stringMessages.showCharts());
rankChartsAnchor.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
showRankChartDialog();
}
});
Anchor chartsAnchor = new Anchor(AbstractImagePrototype.create(chartIcon).getSafeHtml());
chartsAnchor.setTitle(stringMessages.showCharts());
chartsAnchor.addClickHandler(new ClickHandler() {
@@ -1258,6 +1266,7 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
Anchor settingsAnchor = new Anchor(AbstractImagePrototype.create(leaderboardSettingsIcon).getSafeHtml());
settingsAnchor.setTitle(stringMessages.settings());
settingsAnchor.addClickHandler(new SettingsClickHandler(stringMessages));
refreshAndSettingsPanel.add(rankChartsAnchor);
refreshAndSettingsPanel.add(chartsAnchor);
refreshAndSettingsPanel.add(refreshPanel);
refreshAndSettingsPanel.add(settingsAnchor);
@@ -2085,6 +2094,12 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
}
}
private void showRankChartDialog() {
RankChartDialog chartDialog = new RankChartDialog(sailingService, leaderboardName, competitorSelectionProvider,
stringMessages, errorReporter, /* compactChart */ false);
chartDialog.show();
}
private List<RegattaAndRaceIdentifier> getTrackedRacesIdentifiers() {
List<RegattaAndRaceIdentifier> result = new ArrayList<RegattaAndRaceIdentifier>();
for (RaceColumnDTO raceColumn : getLeaderboard().getRaceList()) {
@@ -0,0 +1,16 @@
package com.sap.sailing.gwt.ui.leaderboard;
import com.google.gwt.user.client.ui.Label;
import com.sap.sailing.gwt.ui.client.CompetitorSelectionProvider;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.charts.LeaderboardRankChart;
public class RankChartDialog extends DialogBoxExt {
public RankChartDialog(SailingServiceAsync sailingService, String leaderboardName, CompetitorSelectionProvider competitorSelectionProvider,
StringMessages stringMessages, ErrorReporter errorReporter, boolean compactChart) {
super(new Label(stringMessages.close()));
setWidget(new LeaderboardRankChart(sailingService, leaderboardName, competitorSelectionProvider, stringMessages, errorReporter, compactChart));
}
}
@@ -570,9 +570,9 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
row.totalTimeSailedInSeconds = totalTimeSailedInMilliseconds==null?null:1./1000.*totalTimeSailedInMilliseconds;
}
private List<CompetitorDTO> getCompetitorDTOList(List<Competitor> competitorsFromBestToWorst) {
private List<CompetitorDTO> getCompetitorDTOList(List<Competitor> competitors) {
List<CompetitorDTO> result = new ArrayList<CompetitorDTO>();
for (Competitor competitor : competitorsFromBestToWorst) {
for (Competitor competitor : competitors) {
result.add(convertToCompetitorDTO(competitor));
}
return result;
@@ -2798,4 +2798,16 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
fregService.registerResultUrl(new URL(result));
}
}
@Override
public List<Pair<String, List<CompetitorDTO>>> getRankedCompetitorsFromBestToWorstAfterEachRaceColumn(String leaderboardName, Date date) throws NoWindException {
Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName);
Map<RaceColumn, List<Competitor>> preResult = leaderboard.getRankedCompetitorsFromBestToWorstAfterEachRaceColumn(new MillisecondsTimePoint(date));
List<Pair<String, List<CompetitorDTO>>> result = new ArrayList<Util.Pair<String,List<CompetitorDTO>>>();
for (RaceColumn raceColumn : leaderboard.getRaceColumns()) {
List<CompetitorDTO> competitorList = getCompetitorDTOList(preResult.get(raceColumn));
result.add(new Pair<String, List<CompetitorDTO>>(raceColumn.getName(), competitorList));
}
return result;
}
}
@@ -138,7 +138,7 @@ implements CompetitorSelectionChangeListener, RequiresResize {
.setLinePlotOptions(new LinePlotOptions().setLineWidth(LINE_WIDTH).setMarker(new Marker().setEnabled(false).setHoverState(
new Marker().setEnabled(true).setRadius(4))).setShadow(false)
.setHoverStateLineWidth(LINE_WIDTH));
useCheckboxesToShowAndHide(chart);
ChartUtil.useCheckboxesToShowAndHide(chart);
chart.setChartTitle(new ChartTitle().setText(DetailTypeFormatter.format(dataToShow, stringMessages)));
if (allowTimeAdjust) {
@@ -156,10 +156,11 @@ implements CompetitorSelectionChangeListener, RequiresResize {
});
}
final String unit = getUnit();
if(!compactChart)
if (!compactChart) {
chart.getYAxis().setAxisTitleText(DetailTypeFormatter.format(dataToShow, stringMessages) + " ["+unit+"]");
else
} else {
chart.getYAxis().setAxisTitleText("["+unit+"]");
}
chart.getYAxis().setStartOnTick(false).setShowFirstLabel(false);
chart.getYAxis().setReversed((dataToShow == DetailType.WINDWARD_DISTANCE_TO_OVERALL_LEADER ||
dataToShow == DetailType.GAP_TO_LEADER_IN_SECONDS) ? true : false);
@@ -0,0 +1,52 @@
package com.sap.sailing.gwt.ui.shared.charts;
import org.moxieapps.gwt.highcharts.client.Chart;
import org.moxieapps.gwt.highcharts.client.Legend;
import org.moxieapps.gwt.highcharts.client.Series;
import org.moxieapps.gwt.highcharts.client.events.SeriesCheckboxClickEvent;
import org.moxieapps.gwt.highcharts.client.events.SeriesCheckboxClickEventHandler;
import org.moxieapps.gwt.highcharts.client.events.SeriesLegendItemClickEvent;
import org.moxieapps.gwt.highcharts.client.events.SeriesLegendItemClickEventHandler;
import org.moxieapps.gwt.highcharts.client.plotOptions.PlotOptions;
import org.moxieapps.gwt.highcharts.client.plotOptions.SeriesPlotOptions;
public class ChartUtil {
/**
* When using this method to enable the use of checkboxes only for hiding / showing a series, callers need to ensure
* that all series have {@link PlotOptions#setSelected(boolean)} set to <code>true</code> for all series that are
* added to the chart and hence visible. Otherwise, the checkbox won't initially be in sync with the series'
* visibility state.
*/
static protected void useCheckboxesToShowAndHide(final Chart chart) {
chart.setLegend(new Legend().setEnabled(true).setBorderWidth(0).setSymbolPadding(25)); // make room for checkbox
chart.setSeriesPlotOptions(new SeriesPlotOptions().setSeriesCheckboxClickEventHandler(new SeriesCheckboxClickEventHandler() {
@Override
public boolean onClick(SeriesCheckboxClickEvent seriesCheckboxClickEvent) {
Series series = chart.getSeries(seriesCheckboxClickEvent.getSeriesId());
if (seriesCheckboxClickEvent.isChecked()) {
ChartUtil.onSeriesSelectionChanged(series, true);
} else {
ChartUtil.onSeriesSelectionChanged(series, false);
}
return false; // don't toggle the select state of the series
}
}).setShowCheckbox(true).
setSeriesLegendItemClickEventHandler(new SeriesLegendItemClickEventHandler() {
@Override
public boolean onClick(SeriesLegendItemClickEvent seriesLegendItemClickEvent) {
// disable toggling visibility by clicking the legend item; force user to use checkbox instead
return false;
}
}));
}
static protected void onSeriesSelectionChanged(Series series, boolean selected) {
if (selected) {
series.show();
} else {
series.hide();
}
}
}
@@ -0,0 +1,134 @@
package com.sap.sailing.gwt.ui.shared.charts;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.moxieapps.gwt.highcharts.client.Axis;
import org.moxieapps.gwt.highcharts.client.Chart;
import org.moxieapps.gwt.highcharts.client.ChartTitle;
import org.moxieapps.gwt.highcharts.client.Color;
import org.moxieapps.gwt.highcharts.client.Credits;
import org.moxieapps.gwt.highcharts.client.Series;
import org.moxieapps.gwt.highcharts.client.ToolTip;
import org.moxieapps.gwt.highcharts.client.ToolTipData;
import org.moxieapps.gwt.highcharts.client.ToolTipFormatter;
import org.moxieapps.gwt.highcharts.client.labels.AxisLabelsData;
import org.moxieapps.gwt.highcharts.client.labels.AxisLabelsFormatter;
import org.moxieapps.gwt.highcharts.client.labels.XAxisLabels;
import org.moxieapps.gwt.highcharts.client.plotOptions.LinePlotOptions;
import org.moxieapps.gwt.highcharts.client.plotOptions.Marker;
import org.moxieapps.gwt.highcharts.client.plotOptions.Marker.Symbol;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.SimplePanel;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.gwt.ui.client.CompetitorSelectionProvider;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.CompetitorDTO;
public class LeaderboardRankChart extends SimplePanel {
private static final int LINE_WIDTH = 1;
private final CompetitorSelectionProvider competitorSelectionProvider;
private final Map<CompetitorDTO, Series> competitorSeries;
private final Chart chart;
private final List<String> raceColumnNames;
public LeaderboardRankChart(SailingServiceAsync sailingService, String leaderboardName,
CompetitorSelectionProvider competitorSelectionProvider, final StringMessages stringMessages, ErrorReporter errorReporter, boolean compactChart) {
this.competitorSelectionProvider = competitorSelectionProvider;
competitorSeries = new HashMap<CompetitorDTO, Series>();
raceColumnNames = new ArrayList<String>();
chart = new Chart()
.setPersistent(true)
.setMarginLeft(65)
.setMarginRight(65)
.setWidth100()
.setHeight100()
.setBorderColor(new Color("#A6A6A6"))
.setBorderWidth(1)
.setCredits(new Credits().setEnabled(false))
.setChartTitle(new ChartTitle().setText(stringMessages.rank()))
.setLinePlotOptions(new LinePlotOptions().setLineWidth(LINE_WIDTH).setMarker(
new Marker().setEnabled(true).setHoverState(
new Marker().setEnabled(true).setRadius(4))).setShadow(false)
.setHoverStateLineWidth(LINE_WIDTH));
ChartUtil.useCheckboxesToShowAndHide(chart);
chart.setToolTip(new ToolTip().setEnabled(true).setFormatter(new ToolTipFormatter() {
@Override
public String format(ToolTipData toolTipData) {
String seriesName = toolTipData.getSeriesName();
return "<b>" + seriesName + ":</b> " + stringMessages.rank() + toolTipData.getYAsLong()+
stringMessages.afterRace() + toolTipData.getXAsString();
}
}));
chart.getXAxis().setType(Axis.Type.LINEAR).setAxisTitleText(stringMessages.afterRace());
chart.getXAxis().setLabels(new XAxisLabels().setFormatter(new AxisLabelsFormatter() {
@Override
public String format(AxisLabelsData axisLabelsData) {
return raceColumnNames.get((int) axisLabelsData.getValueAsLong());
}
}));
chart.getYAxis(0).setAxisTitleText(stringMessages.rank()).setStartOnTick(true).setShowFirstLabel(false)
/* TODO is a custom formatter required if it only returns the axis label data again?
.setLabels(new YAxisLabels().setFormatter(new AxisLabelsFormatter() {
@Override
public String format(AxisLabelsData axisLabelsData) {
return axisLabelsData.getValueAsString();
}
})) */;
if (compactChart) {
chart.setSpacingBottom(10).setSpacingLeft(10).setSpacingRight(10).setSpacingTop(2)
.setOption("legend/margin", 2)
.setOption("title/margin", 5)
.setChartSubtitle(null)
.getXAxis().setAxisTitle(null);
}
setSize("100%", "100%");
loadChartData(leaderboardName, stringMessages, errorReporter, sailingService);
}
private void loadChartData(String leaderboardName, final StringMessages stringMessages,
final ErrorReporter errorReporter, SailingServiceAsync sailingService) {
sailingService.getRankedCompetitorsFromBestToWorstAfterEachRaceColumn(leaderboardName,
/* date: null means "now" or "live" */ null, new AsyncCallback<List<Pair<String,List<CompetitorDTO>>>>() {
@Override
public void onSuccess(List<Pair<String, List<CompetitorDTO>>> result) {
raceColumnNames.clear();
int raceNumber = 0;
for (Pair<String, List<CompetitorDTO>> entry : result) {
raceColumnNames.add(entry.getA());
int rank = 1;
for (CompetitorDTO competitor : entry.getB()) {
Series series = getOrCreateSeries(competitor);
series.addPoint(raceNumber, rank);
rank++;
}
raceNumber++;
}
}
@Override
public void onFailure(Throwable caught) {
errorReporter.reportError(stringMessages.errorFetchingRankingChartData(caught.getMessage()));
}
});
}
private Series getOrCreateSeries(CompetitorDTO competitor) {
Series result = competitorSeries.get(competitor);
if (result == null) {
result = chart.createSeries().setType(Series.Type.LINE).setName(competitor.name);
result.setPlotOptions(new LinePlotOptions()
.setLineWidth(LINE_WIDTH)
.setMarker(new Marker().setEnabled(true).setRadius(4).setSymbol(Symbol.DIAMOND))
.setShadow(true).setHoverStateLineWidth(LINE_WIDTH)
.setColor(competitorSelectionProvider.getColor(competitor)).setSelected(true));
competitorSeries.put(competitor, result);
}
return result;
}
}
@@ -3,17 +3,9 @@ package com.sap.sailing.gwt.ui.shared.charts;
import java.util.Date;
import org.moxieapps.gwt.highcharts.client.Chart;
import org.moxieapps.gwt.highcharts.client.Legend;
import org.moxieapps.gwt.highcharts.client.Series;
import org.moxieapps.gwt.highcharts.client.XAxis;
import org.moxieapps.gwt.highcharts.client.events.ChartClickEvent;
import org.moxieapps.gwt.highcharts.client.events.ChartSelectionEvent;
import org.moxieapps.gwt.highcharts.client.events.SeriesCheckboxClickEvent;
import org.moxieapps.gwt.highcharts.client.events.SeriesCheckboxClickEventHandler;
import org.moxieapps.gwt.highcharts.client.events.SeriesLegendItemClickEvent;
import org.moxieapps.gwt.highcharts.client.events.SeriesLegendItemClickEventHandler;
import org.moxieapps.gwt.highcharts.client.plotOptions.PlotOptions;
import org.moxieapps.gwt.highcharts.client.plotOptions.SeriesPlotOptions;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.client.ui.SimplePanel;
@@ -69,7 +61,7 @@ public abstract class RaceChart extends SimplePanel implements RaceSelectionChan
}
protected void showLoading(String message) {
if(timer.getPlayMode() != PlayModes.Live) {
if (timer.getPlayMode() != PlayModes.Live) {
chart.showLoading(message);
}
isLoading = true;
@@ -151,41 +143,4 @@ public abstract class RaceChart extends SimplePanel implements RaceSelectionChan
public void onTimeZoomReset() {
changeMinMaxInterval(timeRangeWithZoomProvider.getFromTime(), timeRangeWithZoomProvider.getToTime());
}
/**
* When using this method to enable the use of checkboxes only for hiding / showing a series, callers need to ensure
* that all series have {@link PlotOptions#setSelected(boolean)} set to <code>true</code> for all series that are
* added to the chart and hence visible. Otherwise, the checkbox won't initially be in sync with the series'
* visibility state.
*/
protected void useCheckboxesToShowAndHide(final Chart chart) {
chart.setLegend(new Legend().setEnabled(true).setBorderWidth(0).setSymbolPadding(25)); // make room for checkbox
chart.setSeriesPlotOptions(new SeriesPlotOptions().setSeriesCheckboxClickEventHandler(new SeriesCheckboxClickEventHandler() {
@Override
public boolean onClick(SeriesCheckboxClickEvent seriesCheckboxClickEvent) {
Series series = chart.getSeries(seriesCheckboxClickEvent.getSeriesId());
if (seriesCheckboxClickEvent.isChecked()) {
onSeriesSelectionChanged(series, true);
} else {
onSeriesSelectionChanged(series, false);
}
return false; // don't toggle the select state of the series
}
}).setShowCheckbox(true).
setSeriesLegendItemClickEventHandler(new SeriesLegendItemClickEventHandler() {
@Override
public boolean onClick(SeriesLegendItemClickEvent seriesLegendItemClickEvent) {
// disable toggling visibility by clicking the legend item; force user to use checkbox instead
return false;
}
}));
}
protected void onSeriesSelectionChanged(Series series, boolean selected) {
if(selected) {
series.show();
} else {
series.hide();
}
}
}
@@ -94,7 +94,6 @@ public class WindChart extends RaceChart implements Component<WindChartSettings>
windSourceSpeedPoints = new HashMap<WindSource, Point[]>();
overallDirectionMinMax = new Pair<Double, Double>(null, null);
colorMap = new ColorMap<WindSource>();
chart = new Chart()
.setPersistent(true)
.setZoomType(Chart.ZoomType.X)
@@ -111,7 +110,7 @@ public class WindChart extends RaceChart implements Component<WindChartSettings>
new Marker().setEnabled(false).setHoverState(
new Marker().setEnabled(true).setRadius(4))).setShadow(false)
.setHoverStateLineWidth(LINE_WIDTH));
useCheckboxesToShowAndHide(chart);
ChartUtil.useCheckboxesToShowAndHide(chart);
final NumberFormat numberFormat = NumberFormat.getFormat("0");
chart.setToolTip(new ToolTip().setEnabled(true).setFormatter(new ToolTipFormatter() {
@Override
@@ -178,9 +177,7 @@ public class WindChart extends RaceChart implements Component<WindChartSettings>
.setChartSubtitle(null)
.getXAxis().setAxisTitle(null);
}
setSize("100%", "100%");
raceSelectionProvider.addRaceSelectionChangeListener(this);
}
@@ -221,7 +218,7 @@ public class WindChart extends RaceChart implements Component<WindChartSettings>
}
}
if(settings.isShowWindSpeedSeries()) {
if (settings.isShowWindSpeedSeries()) {
for (Map.Entry<WindSource, Series> e : windSourceSpeedSeries.entrySet()) {
Series series = e.getValue();
if (settings.getWindSpeedSourcesToDisplay().contains(e.getKey().getType())) {