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 import java.util.HashSet;
9 import java.util.Set;
10
11 import javax.naming.NamingException;
12 import javax.swing.JPanel;
13 import javax.swing.SwingUtilities;
14 import javax.swing.event.EventListenerList;
15
16 import nl.tudelft.simulation.dsol.SimRuntimeException;
17 import nl.tudelft.simulation.dsol.gui.swing.TablePanel;
18 import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
19
20 import org.djunits.unit.TimeUnit;
21 import org.djunits.unit.UNITS;
22 import org.djunits.value.vdouble.scalar.DoubleScalar.Abs;
23 import org.djunits.value.vdouble.scalar.DoubleScalar.Rel;
24 import org.djunits.value.vdouble.scalar.Duration;
25 import org.djunits.value.vdouble.scalar.Length;
26 import org.djunits.value.vdouble.scalar.Speed;
27 import org.djunits.value.vdouble.scalar.Time;
28 import org.jfree.chart.ChartFactory;
29 import org.jfree.chart.ChartPanel;
30 import org.jfree.chart.JFreeChart;
31 import org.jfree.chart.StandardChartTheme;
32 import org.jfree.chart.axis.NumberAxis;
33 import org.jfree.chart.plot.PlotOrientation;
34 import org.jfree.chart.plot.XYPlot;
35 import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
36 import org.jfree.data.DomainOrder;
37 import org.jfree.data.general.DatasetChangeEvent;
38 import org.jfree.data.general.DatasetChangeListener;
39 import org.jfree.data.general.DatasetGroup;
40 import org.jfree.data.xy.XYDataset;
41 import org.opentrafficsim.core.dsol.OTSModelInterface;
42 import org.opentrafficsim.core.dsol.OTSSimTimeDouble;
43 import org.opentrafficsim.core.geometry.OTSGeometryException;
44 import org.opentrafficsim.core.geometry.OTSPoint3D;
45 import org.opentrafficsim.core.gtu.GTUException;
46 import org.opentrafficsim.core.gtu.GTUType;
47 import org.opentrafficsim.core.network.LongitudinalDirectionality;
48 import org.opentrafficsim.core.network.NetworkException;
49 import org.opentrafficsim.core.network.OTSNode;
50 import org.opentrafficsim.core.network.route.CompleteRoute;
51 import org.opentrafficsim.gui.SimulatorFrame;
52 import org.opentrafficsim.road.network.factory.LaneFactory;
53 import org.opentrafficsim.road.network.lane.Lane;
54 import org.opentrafficsim.road.network.lane.LaneType;
55 import org.opentrafficsim.simulationengine.SimpleSimulator;
56
57
58
59
60
61
62
63
64
65
66 public class SuitabilityGraph implements OTSModelInterface, UNITS
67 {
68
69 private static final long serialVersionUID = 20150415L;
70
71
72 private JPanel graphPanel;
73
74
75 private static final int LANECOUNT = 4;
76
77
78 private static final double[] SPEEDLIMITS = {30, 50, 80, 120};
79
80
81 private static final int[] TARGETLANES = {1, 2, -2, -1};
82
83
84 private Duration timeHorizon = new Duration(100, SECOND);
85
86
87 private Duration timeRange = new Duration(110, SECOND);
88
89
90 private static final Color[] COLORTABLE = {new Color(160, 82, 45) , Color.RED, Color.ORANGE,
91 Color.YELLOW, Color.GREEN, Color.BLUE, new Color(199, 21, 133) , Color.GRAY, Color.WHITE};
92
93
94 private JFreeChart[][] charts;
95
96
97
98
99
100
101 public static void main(final String[] args) throws SimRuntimeException
102 {
103 SwingUtilities.invokeLater(new Runnable()
104 {
105 @Override
106 public void run()
107 {
108 SuitabilityGraph suitabilityGraph = new SuitabilityGraph();
109 new SimulatorFrame("Suitability graph", suitabilityGraph.getPanel());
110 try
111 {
112 suitabilityGraph.drawPlots();
113 }
114 catch (NamingException | NetworkException | SimRuntimeException | OTSGeometryException | GTUException exception)
115 {
116 exception.printStackTrace();
117 }
118 }
119 });
120 }
121
122
123
124
125
126
127
128
129
130 protected final void drawPlots() throws NamingException, NetworkException, SimRuntimeException,
131 OTSGeometryException, GTUException
132 {
133 SimpleSimulator simulator =
134 new SimpleSimulator(new Time(0, TimeUnit.SI), new Duration(0, TimeUnit.SI), new Duration(99999,
135 TimeUnit.SI), this);
136 final int rows = SPEEDLIMITS.length;
137 final int columns = TARGETLANES.length;
138 for (int row = 0; row < rows; row++)
139 {
140 int targetLaneConfiguration = TARGETLANES[row];
141 for (int column = 0; column < columns; column++)
142 {
143 Speed speedLimit = new Speed(SPEEDLIMITS[column], KM_PER_HOUR);
144 double mainLength = speedLimit.getSI() * this.timeRange.getSI();
145 OTSNode from = new OTSNode("From", new OTSPoint3D(-mainLength, 0, 0));
146 OTSNode branchPoint = new OTSNode("From", new OTSPoint3D(0, 0, 0));
147 GTUType gtuType = new GTUType("Car");
148 Set<GTUType> compatibility = new HashSet<GTUType>();
149 compatibility.add(gtuType);
150 LaneType laneType = new LaneType("CarLane", compatibility);
151 Lane[] lanes =
152 LaneFactory.makeMultiLane("Test road", from, branchPoint, null, LANECOUNT, laneType, speedLimit,
153 simulator, LongitudinalDirectionality.DIR_PLUS);
154 OTSNode destination =
155 new OTSNode("Destination", new OTSPoint3D(1000, targetLaneConfiguration > 0 ? 100 : -100, 0));
156 LaneFactory.makeMultiLane("DestinationLink", branchPoint, destination, null,
157 Math.abs(targetLaneConfiguration), targetLaneConfiguration > 0 ? 0 : LANECOUNT
158 + targetLaneConfiguration, 0, laneType, speedLimit, simulator,
159 LongitudinalDirectionality.DIR_PLUS);
160 OTSNode nonDestination =
161 new OTSNode("Non-Destination", new OTSPoint3D(1000, targetLaneConfiguration > 0 ? -100 : 100, 0));
162 LaneFactory.makeMultiLane("Non-DestinationLink", branchPoint, nonDestination, null,
163 LANECOUNT - Math.abs(targetLaneConfiguration), targetLaneConfiguration > 0 ? LANECOUNT
164 - targetLaneConfiguration : 0, 0, laneType, speedLimit, simulator,
165 LongitudinalDirectionality.DIR_PLUS);
166 CompleteRoute route = new CompleteRoute("route", gtuType);
167 route.addNode(from);
168 route.addNode(branchPoint);
169 route.addNode(destination);
170 SuitabilityData dataset =
171 (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 =
213 createChart(String.format("Speed limit %.0f%s, %s", speedLimit.getInUnit(), speedLimit.getUnit(),
214 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
276 constructModel(final SimulatorInterface<Abs<TimeUnit>, Rel<TimeUnit>, OTSSimTimeDouble> simulator)
277 throws SimRuntimeException, RemoteException
278 {
279
280 }
281
282
283 @Override
284 public final SimulatorInterface<Abs<TimeUnit>, Rel<TimeUnit>, OTSSimTimeDouble> getSimulator()
285 throws RemoteException
286 {
287 return null;
288 }
289
290 }
291
292
293 class SuitabilityData implements XYDataset
294 {
295
296
297 private ArrayList<ArrayList<Double>> xValues = new ArrayList<ArrayList<Double>>();
298
299
300 private ArrayList<ArrayList<Double>> yValues = new ArrayList<ArrayList<Double>>();
301
302
303 private ArrayList<String> seriesKeys = new ArrayList<String>();
304
305
306 private transient EventListenerList listenerList = new EventListenerList();
307
308
309 private DatasetGroup datasetGroup = null;
310
311
312
313
314 public final void reGraph()
315 {
316 notifyListeners(new DatasetChangeEvent(this, null));
317 }
318
319
320
321
322
323 private void notifyListeners(final DatasetChangeEvent event)
324 {
325 for (DatasetChangeListener dcl : this.listenerList.getListeners(DatasetChangeListener.class))
326 {
327 dcl.datasetChanged(event);
328 }
329 }
330
331
332
333
334
335
336 public final int addSeries(final String seriesName)
337 {
338 this.xValues.add(new ArrayList<Double>());
339 this.yValues.add(new ArrayList<Double>());
340 this.seriesKeys.add(seriesName);
341 return this.xValues.size() - 1;
342 }
343
344
345
346
347
348
349
350 public final void addXYPair(final int seriesKey, final double x, final double y)
351 {
352 this.xValues.get(seriesKey).add(x);
353 this.yValues.get(seriesKey).add(y);
354 }
355
356
357 @Override
358 public final int getSeriesCount()
359 {
360 return this.seriesKeys.size();
361 }
362
363
364 @Override
365 public final Comparable<?> getSeriesKey(final int series)
366 {
367 return this.seriesKeys.get(series);
368 }
369
370
371 @Override
372 public final int indexOf(@SuppressWarnings("rawtypes") final Comparable seriesKey)
373 {
374 return this.seriesKeys.indexOf(seriesKey);
375 }
376
377
378 @Override
379 public final void addChangeListener(final DatasetChangeListener listener)
380 {
381 this.listenerList.add(DatasetChangeListener.class, listener);
382 }
383
384
385 @Override
386 public final void removeChangeListener(final DatasetChangeListener listener)
387 {
388 this.listenerList.remove(DatasetChangeListener.class, listener);
389 }
390
391
392 @Override
393 public final DatasetGroup getGroup()
394 {
395 return this.datasetGroup;
396 }
397
398
399 @Override
400 public final void setGroup(final DatasetGroup group)
401 {
402 this.datasetGroup = group;
403 }
404
405
406 @Override
407 public DomainOrder getDomainOrder()
408 {
409 return DomainOrder.ASCENDING;
410 }
411
412
413 @Override
414 public final int getItemCount(final int series)
415 {
416 return this.xValues.get(series).size();
417 }
418
419
420 @Override
421 public final Number getX(final int series, final int item)
422 {
423 return this.xValues.get(series).get(item);
424 }
425
426
427 @Override
428 public final double getXValue(final int series, final int item)
429 {
430 return this.xValues.get(series).get(item);
431 }
432
433
434 @Override
435 public final Number getY(final int series, final int item)
436 {
437 return this.yValues.get(series).get(item);
438 }
439
440
441 @Override
442 public final double getYValue(final int series, final int item)
443 {
444 return this.yValues.get(series).get(item);
445 }
446
447 }