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