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