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