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