1
2
3
4 package org.opentrafficsim.swing.graphs;
5
6 import java.awt.BasicStroke;
7 import java.awt.Color;
8 import java.awt.Font;
9 import java.awt.event.ActionEvent;
10 import java.awt.event.ActionListener;
11 import java.util.List;
12
13 import javax.swing.ButtonGroup;
14 import javax.swing.JMenu;
15 import javax.swing.JPopupMenu;
16 import javax.swing.JRadioButtonMenuItem;
17
18 import org.djunits.value.vdouble.scalar.Duration;
19 import org.jfree.chart.ChartMouseEvent;
20 import org.jfree.chart.ChartMouseListener;
21 import org.jfree.chart.annotations.XYAnnotation;
22 import org.jfree.chart.annotations.XYLineAnnotation;
23 import org.jfree.chart.annotations.XYTextAnnotation;
24 import org.jfree.chart.entity.AxisEntity;
25 import org.jfree.chart.entity.XYItemEntity;
26 import org.jfree.chart.ui.TextAnchor;
27 import org.jfree.data.Range;
28 import org.opentrafficsim.draw.graphs.FundamentalDiagram;
29 import org.opentrafficsim.draw.graphs.FundamentalDiagram.Quantity;
30 import org.opentrafficsim.draw.graphs.GraphUtil;
31
32
33
34
35
36
37
38
39
40
41
42 public class SwingFundamentalDiagram extends SwingPlot
43 {
44
45 private static final long serialVersionUID = 20190823L;
46
47
48
49
50
51 public SwingFundamentalDiagram(final FundamentalDiagram plot)
52 {
53 super(plot);
54 }
55
56
57 @Override
58 protected ChartMouseListener getChartMouseListener()
59 {
60 ChartMouseListener toggle = !getPlot().hasLineFD() && getPlot().getSource().getNumberOfSeries() < 2 ? null
61 : GraphUtil.getToggleSeriesByLegendListener(getPlot().getLegend(), getPlot().getLaneVisible());
62 return new ChartMouseListener()
63 {
64
65 @SuppressWarnings("unchecked")
66 @Override
67 public void chartMouseClicked(final ChartMouseEvent event)
68 {
69 if (toggle != null)
70 {
71 toggle.chartMouseClicked(event);
72 }
73
74 for (XYAnnotation annotation : ((List<XYAnnotation>) getPlot().getChart().getXYPlot().getAnnotations()))
75 {
76 if (annotation instanceof XYLineAnnotation)
77 {
78 getPlot().getChart().getXYPlot().removeAnnotation(annotation);
79 }
80 }
81
82 if (event.getEntity() instanceof XYItemEntity)
83 {
84 XYItemEntity itemEntity = (XYItemEntity) event.getEntity();
85 int series = itemEntity.getSeriesIndex();
86 for (int i = 0; i < getPlot().getItemCount(series) - 1; i++)
87 {
88 XYLineAnnotation annotation = new XYLineAnnotation(getPlot().getXValue(series, i),
89 getPlot().getYValue(series, i), getPlot().getXValue(series, i + 1),
90 getPlot().getYValue(series, i + 1), new BasicStroke(1.0f), Color.WHITE);
91 getPlot().getChart().getXYPlot().addAnnotation(annotation);
92 }
93 }
94 else if (event.getEntity() instanceof AxisEntity)
95 {
96 if (((AxisEntity) event.getEntity()).getAxis().equals(getPlot().getChart().getXYPlot().getDomainAxis()))
97 {
98 Quantity old = getPlot().getDomainQuantity();
99 getPlot().setDomainQuantity(getPlot().getOtherQuantity());
100 getPlot().setOtherQuantity(old);
101 getPlot().getChart().getXYPlot().getDomainAxis().setLabel(getPlot().getDomainQuantity().label());
102 getPlot().getChart().getXYPlot().zoomDomainAxes(0.0, null, null);
103 }
104 else
105 {
106 Quantity old = getPlot().getRangeQuantity();
107 getPlot().setRangeQuantity(getPlot().getOtherQuantity());
108 getPlot().setOtherQuantity(old);
109 getPlot().getChart().getXYPlot().getRangeAxis().setLabel(getPlot().getRangeQuantity().label());
110 getPlot().getChart().getXYPlot().zoomRangeAxes(0.0, null, null);
111 }
112 }
113 }
114
115
116 @SuppressWarnings("unchecked")
117 @Override
118 public void chartMouseMoved(final ChartMouseEvent event)
119 {
120 if (toggle != null)
121 {
122 toggle.chartMouseMoved(event);
123 }
124 boolean clearText = true;
125
126 if (event.getEntity() instanceof XYItemEntity)
127 {
128
129 XYItemEntity itemEntity = (XYItemEntity) event.getEntity();
130 int series = itemEntity.getSeriesIndex();
131 if (!getPlot().hasLineFD() || series != getPlot().getSeriesCount() - 1)
132 {
133 clearText = false;
134 int item = itemEntity.getItem();
135 double t = item * getPlot().getSource().getUpdateInterval().si;
136 getPlot().setTimeInfo(String.format(", %.0fs", t));
137 double x = getPlot().getXValue(series, item);
138 double y = getPlot().getYValue(series, item);
139 Range domain = getPlot().getChart().getXYPlot().getDomainAxis().getRange();
140 Range range = getPlot().getChart().getXYPlot().getRangeAxis().getRange();
141 TextAnchor anchor;
142 if (range.getUpperBound() - y < y - range.getLowerBound())
143 {
144
145 if (domain.getUpperBound() - x < x - domain.getLowerBound())
146 {
147
148 anchor = TextAnchor.TOP_RIGHT;
149 }
150 else
151 {
152
153 if ((range.getUpperBound() - y)
154 / (range.getUpperBound() - range.getLowerBound()) < (x - domain.getLowerBound())
155 / (domain.getUpperBound() - domain.getLowerBound()))
156 {
157
158 anchor = TextAnchor.TOP_RIGHT;
159 }
160 else
161 {
162
163 anchor = TextAnchor.BOTTOM_LEFT;
164 }
165 }
166 }
167 else if (domain.getUpperBound() - x < x - domain.getLowerBound())
168 {
169
170 anchor = TextAnchor.BOTTOM_RIGHT;
171 }
172 else
173 {
174
175 anchor = TextAnchor.BOTTOM_LEFT;
176 }
177 XYTextAnnotation textAnnotation = new XYTextAnnotation(String.format("%.0fs", t), x, y);
178 textAnnotation.setTextAnchor(anchor);
179 textAnnotation.setFont(textAnnotation.getFont().deriveFont(14.0f).deriveFont(Font.BOLD));
180 getPlot().getChart().getXYPlot().addAnnotation(textAnnotation);
181 }
182 }
183
184 if (clearText)
185 {
186 for (XYAnnotation annotation : ((List<XYAnnotation>) getPlot().getChart().getXYPlot().getAnnotations()))
187 {
188 if (annotation instanceof XYTextAnnotation)
189 {
190 getPlot().getChart().getXYPlot().removeAnnotation(annotation);
191 }
192 }
193 getPlot().setTimeInfo("");
194 }
195 }
196 };
197 }
198
199
200 @Override
201 protected void addPopUpMenuItems(final JPopupMenu popupMenu)
202 {
203 super.addPopUpMenuItems(popupMenu);
204 popupMenu.insert(new JPopupMenu.Separator(), 0);
205
206 JMenu updMenu = new JMenu("Update frequency");
207 ButtonGroup updGroup = new ButtonGroup();
208 for (int f : getPlot().getSource().getPossibleUpdateFrequencies())
209 {
210 String format = "%dx";
211 JRadioButtonMenuItem item = new JRadioButtonMenuItem(String.format(format, f));
212 item.setSelected(f == 1);
213 item.addActionListener(new ActionListener()
214 {
215
216 @Override
217 public void actionPerformed(final ActionEvent e)
218 {
219
220 if ((int) (.5 + getPlot().getSource().getAggregationPeriod().si
221 / getPlot().getSource().getUpdateInterval().si) != f)
222 {
223 Duration interval = Duration.instantiateSI(getPlot().getSource().getAggregationPeriod().si / f);
224 for (FundamentalDiagram diagram : getPlot().getSource().getDiagrams())
225 {
226 diagram.setUpdateInterval(interval);
227 }
228
229
230 getPlot().getSource().setUpdateInterval(interval, getPlot().getUpdateTime().plus(interval.times(0.5)));
231
232 for (FundamentalDiagram diagram : getPlot().getSource().getDiagrams())
233 {
234 diagram.getChart().getXYPlot().zoomDomainAxes(0.0, null, null);
235 diagram.getChart().getXYPlot().zoomRangeAxes(0.0, null, null);
236 diagram.notifyPlotChange();
237 }
238 }
239 }
240 });
241 updGroup.add(item);
242 updMenu.add(item);
243 }
244 popupMenu.insert(updMenu, 0);
245
246 JMenu aggMenu = new JMenu("Aggregation period");
247 ButtonGroup aggGroup = new ButtonGroup();
248 for (double t : getPlot().getSource().getPossibleAggregationPeriods())
249 {
250 double t2 = t;
251 String format = "%.0f s";
252 if (t >= 60.0)
253 {
254 t2 = t / 60.0;
255 format = "%.0f min";
256 }
257 JRadioButtonMenuItem item = new JRadioButtonMenuItem(String.format(format, t2));
258 item.setSelected(t == getPlot().getSource().getAggregationPeriod().si);
259 item.addActionListener(new ActionListener()
260 {
261
262
263 @Override
264 public void actionPerformed(final ActionEvent e)
265 {
266 if (getPlot().getSource().getAggregationPeriod().si != t)
267 {
268 int n = (int) (0.5 + getPlot().getSource().getAggregationPeriod().si
269 / getPlot().getSource().getUpdateInterval().si);
270 Duration period = Duration.instantiateSI(t);
271 Duration interval = period.divide(n);
272 for (FundamentalDiagram diagram : getPlot().getSource().getDiagrams())
273 {
274 diagram.setUpdateInterval(interval);
275 }
276
277 getPlot().getSource().setAggregationPeriod(period);
278 getPlot().getSource().setUpdateInterval(period.divide(n),
279 getPlot().getUpdateTime().plus(period.divide(n).times(0.5)));
280 for (FundamentalDiagram diagram : getPlot().getSource().getDiagrams())
281 {
282 diagram.getChart().getXYPlot().zoomDomainAxes(0.0, null, null);
283 diagram.getChart().getXYPlot().zoomRangeAxes(0.0, null, null);
284 diagram.notifyPlotChange();
285 }
286 }
287 }
288
289 });
290 aggGroup.add(item);
291 aggMenu.add(item);
292 }
293 popupMenu.insert(aggMenu, 0);
294 }
295
296
297
298
299
300 @Override
301 public FundamentalDiagram getPlot()
302 {
303 return (FundamentalDiagram) super.getPlot();
304 }
305
306 }