1 package org.opentrafficsim.swing.graphs;
2
3 import java.awt.BasicStroke;
4 import java.awt.Color;
5 import java.awt.Point;
6 import java.awt.geom.Point2D;
7 import java.awt.geom.Rectangle2D;
8
9 import org.djutils.draw.point.Point2d;
10 import org.jfree.chart.ChartMouseEvent;
11 import org.jfree.chart.ChartMouseListener;
12 import org.jfree.chart.annotations.XYLineAnnotation;
13 import org.jfree.chart.annotations.XYTextAnnotation;
14 import org.jfree.chart.entity.PlotEntity;
15 import org.jfree.chart.plot.XYPlot;
16 import org.opentrafficsim.draw.graphs.GraphUtil;
17 import org.opentrafficsim.draw.graphs.TrajectoryPlot;
18
19
20
21
22
23
24
25
26
27
28
29 public class SwingTrajectoryPlot extends SwingSpaceTimePlot
30 {
31
32 private static final long serialVersionUID = 20190823L;
33
34
35 private boolean density;
36
37
38 private boolean flow;
39
40
41 private Point2D.Double from;
42
43
44 private Point2D.Double to;
45
46
47 private XYLineAnnotation lineAnnotation;
48
49
50 private XYTextAnnotation textAnnotation;
51
52
53
54
55
56 public SwingTrajectoryPlot(final TrajectoryPlot plot)
57 {
58 super(plot);
59 }
60
61
62
63
64
65 @Override
66 protected ChartMouseListener getChartMouseListener()
67 {
68
69 ChartMouseListener toggle = getPlot().getPath().getNumberOfSeries() < 2 ? null
70 : GraphUtil.getToggleSeriesByLegendListener(getPlot().getLegend(), getPlot().getLaneVisible());
71 return new ChartMouseListener()
72 {
73 @Override
74 public void chartMouseClicked(final ChartMouseEvent event)
75 {
76 if (toggle != null)
77 {
78 toggle.chartMouseClicked(event);
79 }
80 if (event.getEntity() instanceof PlotEntity)
81 {
82 removeAnnotations();
83 if (SwingTrajectoryPlot.this.from == null)
84 {
85 if (event.getTrigger().isControlDown())
86 {
87 SwingTrajectoryPlot.this.density = false;
88 SwingTrajectoryPlot.this.flow = false;
89 }
90 else if (event.getTrigger().isShiftDown())
91 {
92 SwingTrajectoryPlot.this.density = true;
93 SwingTrajectoryPlot.this.flow = false;
94 }
95 else if (event.getTrigger().isAltDown())
96 {
97 SwingTrajectoryPlot.this.density = false;
98 SwingTrajectoryPlot.this.flow = true;
99 }
100 else
101 {
102 SwingTrajectoryPlot.this.from = null;
103 SwingTrajectoryPlot.this.to = null;
104 return;
105 }
106 SwingTrajectoryPlot.this.from = getValuePoint(event);
107 SwingTrajectoryPlot.this.to = null;
108 }
109 else
110 {
111 SwingTrajectoryPlot.this.to = getValuePoint(event);
112 removeAnnotations();
113 snap(SwingTrajectoryPlot.this.to);
114 drawLine(SwingTrajectoryPlot.this.to);
115 drawStatistics();
116 SwingTrajectoryPlot.this.from = null;
117 SwingTrajectoryPlot.this.to = null;
118 }
119 }
120 }
121
122 @Override
123 public void chartMouseMoved(final ChartMouseEvent event)
124 {
125 if (toggle != null)
126 {
127 toggle.chartMouseClicked(event);
128 }
129 if (event.getEntity() instanceof PlotEntity && SwingTrajectoryPlot.this.from != null
130 && SwingTrajectoryPlot.this.to == null)
131 {
132 removeAnnotations();
133 Point2D.Double toPoint = getValuePoint(event);
134 snap(toPoint);
135 drawLine(toPoint);
136 }
137 }
138
139 };
140 }
141
142
143
144
145
146
147 private Point2D.Double getValuePoint(final ChartMouseEvent event)
148 {
149 Point2D p = getChartPanel().translateScreenToJava2D(new Point(event.getTrigger().getX(), event.getTrigger().getY()));
150 XYPlot plot = getChartPanel().getChart().getXYPlot();
151 Rectangle2D dataArea = getChartPanel().getChartRenderingInfo().getPlotInfo().getDataArea();
152 double x = plot.getDomainAxis().java2DToValue(p.getX(), dataArea, plot.getDomainAxisEdge());
153 double y = plot.getRangeAxis().java2DToValue(p.getY(), dataArea, plot.getRangeAxisEdge());
154 return new Point2D.Double(x, y);
155 }
156
157
158
159
160
161 private void drawLine(final Point2D.Double toPoint)
162 {
163 this.lineAnnotation =
164 new XYLineAnnotation(this.from.x, this.from.y, toPoint.x, toPoint.y, new BasicStroke(2.0f), Color.WHITE);
165 getPlot().getChart().getXYPlot().addAnnotation(this.lineAnnotation);
166 }
167
168
169
170
171 private void drawStatistics()
172 {
173 double dx = this.to.x - this.from.x;
174 double dy = this.to.y - this.from.y;
175 double v = 3.6 * dy / dx;
176
177 String label;
178 if (this.density || this.flow)
179 {
180 int n = 0;
181 for (int i = 0; i < getPlot().getSeriesCount(); i++)
182 {
183
184 int k = getPlot().getItemCount(i) - 1;
185 double x1 = Math.min(this.from.x, this.to.x);
186 double y1 = Math.min(this.from.y, this.to.y);
187 double x2 = Math.max(this.from.x, this.to.x);
188 double y2 = Math.max(this.from.y, this.to.y);
189 double x3 = Math.min(getPlot().getXValue(i, 0), getPlot().getXValue(i, k));
190 double y3 = Math.min(getPlot().getYValue(i, 0), getPlot().getYValue(i, k));
191 double x4 = Math.max(getPlot().getXValue(i, 0), getPlot().getXValue(i, k));
192 double y4 = Math.max(getPlot().getYValue(i, 0), getPlot().getYValue(i, k));
193 if (x3 <= x2 && y3 <= y2 && x1 <= x4 && y1 <= y4)
194 {
195 for (int j = 0; j < k; j++)
196 {
197 if (Point2d.intersectionOfLineSegments(this.from.x, this.from.y, this.to.x, this.to.y,
198 getPlot().getXValue(i, j), getPlot().getYValue(i, j), getPlot().getXValue(i, j + 1),
199 getPlot().getYValue(i, j + 1)) != null)
200 {
201 n++;
202 break;
203 }
204 }
205 }
206 }
207 if (this.density)
208 {
209 label = String.format("%.1f veh/km", Math.abs(1000.0 * n / dy));
210 }
211 else
212 {
213 label = String.format("%.1f veh/h", Math.abs(3600.0 * n / dx));
214 }
215 }
216 else
217 {
218 label = String.format("%.1f km/h", v);
219 }
220
221 this.textAnnotation = new XYTextAnnotation(label, this.from.x, this.from.y);
222 getPlot().getChart().getXYPlot().addAnnotation(this.textAnnotation);
223
224 }
225
226
227
228
229 private void removeAnnotations()
230 {
231 if (SwingTrajectoryPlot.this.lineAnnotation != null)
232 {
233 getPlot().getChart().getXYPlot().removeAnnotation(SwingTrajectoryPlot.this.lineAnnotation);
234 }
235 if (SwingTrajectoryPlot.this.textAnnotation != null)
236 {
237 getPlot().getChart().getXYPlot().removeAnnotation(SwingTrajectoryPlot.this.textAnnotation);
238 }
239 }
240
241
242
243
244
245 private void snap(final Point2D.Double toPoint)
246 {
247 if (this.density)
248 {
249 toPoint.x = this.from.x;
250 }
251 if (this.flow)
252 {
253 toPoint.y = this.from.y;
254 }
255 }
256
257
258
259
260
261 @Override
262 public TrajectoryPlot getPlot()
263 {
264 return (TrajectoryPlot) super.getPlot();
265 }
266
267 }