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