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