1 package org.opentrafficsim.demo.lanechange;
2
3 import java.awt.BasicStroke;
4 import java.awt.BorderLayout;
5 import java.awt.Color;
6 import java.rmi.RemoteException;
7 import java.util.ArrayList;
8
9 import javax.naming.NamingException;
10 import javax.swing.JPanel;
11 import javax.swing.SwingUtilities;
12 import javax.swing.event.EventListenerList;
13
14 import nl.tudelft.simulation.dsol.SimRuntimeException;
15 import nl.tudelft.simulation.dsol.gui.swing.TablePanel;
16 import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
17
18 import org.djunits.unit.TimeUnit;
19 import org.djunits.value.vdouble.scalar.DoubleScalar.Abs;
20 import org.djunits.value.vdouble.scalar.DoubleScalar.Rel;
21 import org.jfree.chart.ChartFactory;
22 import org.jfree.chart.ChartPanel;
23 import org.jfree.chart.JFreeChart;
24 import org.jfree.chart.StandardChartTheme;
25 import org.jfree.chart.axis.NumberAxis;
26 import org.jfree.chart.plot.PlotOrientation;
27 import org.jfree.chart.plot.XYPlot;
28 import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
29 import org.jfree.data.DomainOrder;
30 import org.jfree.data.general.DatasetChangeEvent;
31 import org.jfree.data.general.DatasetChangeListener;
32 import org.jfree.data.general.DatasetGroup;
33 import org.jfree.data.xy.XYDataset;
34 import org.opentrafficsim.core.OTS_SCALAR;
35 import org.opentrafficsim.core.dsol.OTSModelInterface;
36 import org.opentrafficsim.core.dsol.OTSSimTimeDouble;
37 import org.opentrafficsim.core.geometry.OTSGeometryException;
38 import org.opentrafficsim.core.geometry.OTSPoint3D;
39 import org.opentrafficsim.core.gtu.GTUType;
40 import org.opentrafficsim.core.network.NetworkException;
41 import org.opentrafficsim.core.network.OTSNode;
42 import org.opentrafficsim.core.network.route.CompleteRoute;
43 import org.opentrafficsim.gui.SimulatorFrame;
44 import org.opentrafficsim.road.network.factory.LaneFactory;
45 import org.opentrafficsim.road.network.lane.Lane;
46 import org.opentrafficsim.road.network.lane.LaneType;
47 import org.opentrafficsim.road.network.route.CompleteLaneBasedRouteNavigator;
48 import org.opentrafficsim.simulationengine.SimpleSimulator;
49
50
51
52
53
54
55
56
57
58
59 public class SuitabilityGraph implements OTSModelInterface, OTS_SCALAR
60 {
61
62 private static final long serialVersionUID = 20150415L;
63
64
65 private JPanel graphPanel;
66
67
68 private static final int LANECOUNT = 4;
69
70
71 private static final double[] SPEEDLIMITS = {30, 50, 80, 120};
72
73
74 private static final int[] TARGETLANES = {1, 2, -2, -1};
75
76
77 private Time.Rel timeHorizon = new Time.Rel(100, SECOND);
78
79
80 private Time.Rel timeRange = new Time.Rel(110, SECOND);
81
82
83 private static final Color[] COLORTABLE = {new Color(160, 82, 45) , Color.RED, Color.ORANGE,
84 Color.YELLOW, Color.GREEN, Color.BLUE, new Color(199, 21, 133) , Color.GRAY, Color.WHITE};
85
86
87 private JFreeChart[][] charts;
88
89
90
91
92
93
94 public static void main(final String[] args) throws SimRuntimeException
95 {
96 SwingUtilities.invokeLater(new Runnable()
97 {
98 @Override
99 public void run()
100 {
101 SuitabilityGraph suitabilityGraph = new SuitabilityGraph();
102 new SimulatorFrame("Suitability graph", suitabilityGraph.getPanel());
103 try
104 {
105 suitabilityGraph.drawPlots();
106 }
107 catch (NamingException | NetworkException | SimRuntimeException | OTSGeometryException exception)
108 {
109 exception.printStackTrace();
110 }
111 }
112 });
113 }
114
115
116
117
118
119
120
121
122 protected final void drawPlots() throws NamingException, NetworkException, SimRuntimeException,
123 OTSGeometryException
124 {
125 SimpleSimulator simulator =
126 new SimpleSimulator(new Time.Abs(0, TimeUnit.SI), new Time.Rel(0, TimeUnit.SI), new Time.Rel(99999,
127 TimeUnit.SI), this);
128 final int rows = SPEEDLIMITS.length;
129 final int columns = TARGETLANES.length;
130 for (int row = 0; row < rows; row++)
131 {
132 int targetLaneConfiguration = TARGETLANES[row];
133 for (int column = 0; column < columns; column++)
134 {
135 Speed.Abs speedLimit = new Speed.Abs(SPEEDLIMITS[column], KM_PER_HOUR);
136 double mainLength = speedLimit.getSI() * this.timeRange.getSI();
137 OTSNode from = new OTSNode("From", new OTSPoint3D(-mainLength, 0, 0));
138 OTSNode branchPoint = new OTSNode("From", new OTSPoint3D(0, 0, 0));
139 LaneType laneType = new LaneType("CarLane");
140 GTUType gtuType = GTUType.makeGTUType("Car");
141 laneType.addCompatibility(gtuType);
142 Lane[] lanes =
143 LaneFactory.makeMultiLane("Test road", from, branchPoint, null, LANECOUNT, laneType, speedLimit,
144 simulator);
145 OTSNode destination =
146 new OTSNode("Destination", new OTSPoint3D(1000, targetLaneConfiguration > 0 ? 100 : -100, 0));
147 LaneFactory.makeMultiLane("DestinationLink", branchPoint, destination, null, Math
148 .abs(targetLaneConfiguration), targetLaneConfiguration > 0 ? 0 : LANECOUNT
149 + targetLaneConfiguration, 0, laneType, speedLimit, simulator);
150 OTSNode nonDestination =
151 new OTSNode("Non-Destination", new OTSPoint3D(1000, targetLaneConfiguration > 0 ? -100 : 100, 0));
152 LaneFactory.makeMultiLane("Non-DestinationLink", branchPoint, nonDestination, null, LANECOUNT
153 - Math.abs(targetLaneConfiguration), targetLaneConfiguration > 0 ? LANECOUNT
154 - targetLaneConfiguration : 0, 0, laneType, speedLimit, simulator);
155 CompleteRoute route = new CompleteRoute("route");
156 route.addNode(from);
157 route.addNode(branchPoint);
158 route.addNode(destination);
159 CompleteLaneBasedRouteNavigator navigator = new CompleteLaneBasedRouteNavigator(route);
160 SuitabilityData dataset =
161 (SuitabilityData) ((XYPlot) (this.charts[row][column].getPlot())).getDataset();
162 for (int laneIndex = 0; laneIndex < LANECOUNT; laneIndex++)
163 {
164 int key = dataset.addSeries("Lane " + (laneIndex + 1));
165 Lane lane = lanes[laneIndex];
166 for (int position = 0; position <= mainLength; position += 10)
167 {
168 Length.Rel longitudinalPosition = new Length.Rel(position, METER);
169 Length.Rel suitability =
170 navigator.suitability(lane, longitudinalPosition, gtuType, this.timeHorizon);
171 if (suitability.getSI() <= mainLength)
172 {
173 dataset.addXYPair(key, mainLength - position, suitability.getSI());
174 }
175 }
176 dataset.reGraph();
177 }
178 }
179 }
180 }
181
182
183
184
185 public SuitabilityGraph()
186 {
187 this.graphPanel = new JPanel(new BorderLayout());
188 final int rows = SPEEDLIMITS.length;
189 final int columns = TARGETLANES.length;
190 TablePanel chartsPanel = new TablePanel(rows, rows);
191 this.graphPanel.add(chartsPanel, BorderLayout.CENTER);
192 this.charts = new JFreeChart[rows][columns];
193 for (int row = 0; row < rows; row++)
194 {
195 int targetLaneConfiguration = TARGETLANES[row];
196 String targetLaneDescription =
197 String.format("%s lane %s exit", Math.abs(targetLaneConfiguration) == 1 ? "single" : "double",
198 targetLaneConfiguration > 0 ? "left" : "right");
199 for (int column = 0; column < columns; column++)
200 {
201 Speed.Abs speedLimit = new Speed.Abs(SPEEDLIMITS[column], KM_PER_HOUR);
202 JFreeChart chart =
203 createChart(String.format("Speed limit %.0f%s, %s", speedLimit.getInUnit(), speedLimit.getUnit(),
204 targetLaneDescription), speedLimit);
205 chartsPanel.setCell(new ChartPanel(chart), column, row);
206 this.charts[row][column] = chart;
207 }
208 }
209 }
210
211
212
213
214
215
216 private JFreeChart createChart(final String caption, final Speed.Abs speedLimit)
217 {
218 ChartFactory.setChartTheme(new StandardChartTheme("JFree/Shadow", false));
219 XYDataset chartData = new SuitabilityData();
220 JFreeChart chartPanel =
221 ChartFactory.createXYLineChart(caption, "", "", chartData, PlotOrientation.VERTICAL, true, false, false);
222 chartPanel.setBorderVisible(true);
223 chartPanel.setBorderPaint(new Color(192, 192, 192));
224 NumberAxis timeAxis = new NumberAxis("\u2192 " + "Remaining time to junction [s]");
225 double distanceRange = this.timeRange.getSI() * speedLimit.getSI();
226 NumberAxis distanceAxis = new NumberAxis("\u2192 " + "Remaining distance to junction [m]");
227 distanceAxis.setRange(0, distanceRange);
228 distanceAxis.setInverted(true);
229 distanceAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
230 timeAxis.setAutoRangeIncludesZero(true);
231 timeAxis.setRange(0, this.timeRange.getSI());
232 timeAxis.setInverted(true);
233 timeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
234
235
236 NumberAxis yAxis = new NumberAxis("\u2192 " + "Distance to vacate lane [m]");
237 yAxis.setAutoRangeIncludesZero(true);
238 yAxis.setRange(-0.1, distanceRange);
239 chartPanel.getXYPlot().setDomainAxis(distanceAxis);
240 chartPanel.getXYPlot().setRangeAxis(yAxis);
241 final XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) chartPanel.getXYPlot().getRenderer();
242 renderer.setBaseLinesVisible(true);
243 renderer.setBaseShapesVisible(false);
244
245 for (int index = 0; index < LANECOUNT; index++)
246 {
247 renderer.setSeriesPaint(index, COLORTABLE[index]);
248 renderer.setSeriesStroke(index, new BasicStroke(4.0f));
249 }
250
251 return chartPanel;
252 }
253
254
255
256
257
258 public final JPanel getPanel()
259 {
260 return this.graphPanel;
261 }
262
263
264 @Override
265 public final void
266 constructModel(final SimulatorInterface<Abs<TimeUnit>, Rel<TimeUnit>, OTSSimTimeDouble> simulator)
267 throws SimRuntimeException, RemoteException
268 {
269
270 }
271
272
273 @Override
274 public final SimulatorInterface<Abs<TimeUnit>, Rel<TimeUnit>, OTSSimTimeDouble> getSimulator()
275 throws RemoteException
276 {
277 return null;
278 }
279
280 }
281
282
283 class SuitabilityData implements XYDataset
284 {
285
286
287 private ArrayList<ArrayList<Double>> xValues = new ArrayList<ArrayList<Double>>();
288
289
290 private ArrayList<ArrayList<Double>> yValues = new ArrayList<ArrayList<Double>>();
291
292
293 private ArrayList<String> seriesKeys = new ArrayList<String>();
294
295
296 private transient EventListenerList listenerList = new EventListenerList();
297
298
299 private DatasetGroup datasetGroup = null;
300
301
302
303
304 public final void reGraph()
305 {
306 notifyListeners(new DatasetChangeEvent(this, null));
307 }
308
309
310
311
312
313 private void notifyListeners(final DatasetChangeEvent event)
314 {
315 for (DatasetChangeListener dcl : this.listenerList.getListeners(DatasetChangeListener.class))
316 {
317 dcl.datasetChanged(event);
318 }
319 }
320
321
322
323
324
325
326 public final int addSeries(final String seriesName)
327 {
328 this.xValues.add(new ArrayList<Double>());
329 this.yValues.add(new ArrayList<Double>());
330 this.seriesKeys.add(seriesName);
331 return this.xValues.size() - 1;
332 }
333
334
335
336
337
338
339
340 public final void addXYPair(final int seriesKey, final double x, final double y)
341 {
342 this.xValues.get(seriesKey).add(x);
343 this.yValues.get(seriesKey).add(y);
344 }
345
346
347 @Override
348 public final int getSeriesCount()
349 {
350 return this.seriesKeys.size();
351 }
352
353
354 @Override
355 public final Comparable<?> getSeriesKey(final int series)
356 {
357 return this.seriesKeys.get(series);
358 }
359
360
361 @Override
362 public final int indexOf(@SuppressWarnings("rawtypes") final Comparable seriesKey)
363 {
364 return this.seriesKeys.indexOf(seriesKey);
365 }
366
367
368 @Override
369 public final void addChangeListener(final DatasetChangeListener listener)
370 {
371 this.listenerList.add(DatasetChangeListener.class, listener);
372 }
373
374
375 @Override
376 public final void removeChangeListener(final DatasetChangeListener listener)
377 {
378 this.listenerList.remove(DatasetChangeListener.class, listener);
379 }
380
381
382 @Override
383 public final DatasetGroup getGroup()
384 {
385 return this.datasetGroup;
386 }
387
388
389 @Override
390 public final void setGroup(final DatasetGroup group)
391 {
392 this.datasetGroup = group;
393 }
394
395
396 @Override
397 public DomainOrder getDomainOrder()
398 {
399 return DomainOrder.ASCENDING;
400 }
401
402
403 @Override
404 public final int getItemCount(final int series)
405 {
406 return this.xValues.get(series).size();
407 }
408
409
410 @Override
411 public final Number getX(final int series, final int item)
412 {
413 return this.xValues.get(series).get(item);
414 }
415
416
417 @Override
418 public final double getXValue(final int series, final int item)
419 {
420 return this.xValues.get(series).get(item);
421 }
422
423
424 @Override
425 public final Number getY(final int series, final int item)
426 {
427 return this.yValues.get(series).get(item);
428 }
429
430
431 @Override
432 public final double getYValue(final int series, final int item)
433 {
434 return this.yValues.get(series).get(item);
435 }
436
437 }