1   package org.opentrafficsim.draw.graphs;
2   
3   import java.awt.Color;
4   import java.awt.event.ActionEvent;
5   import java.awt.event.ActionListener;
6   
7   import org.djunits.value.vdouble.scalar.Time;
8   import org.djutils.exceptions.Throw;
9   import org.jfree.chart.JFreeChart;
10  import org.jfree.chart.LegendItem;
11  import org.jfree.chart.LegendItemCollection;
12  import org.jfree.chart.axis.NumberAxis;
13  import org.jfree.chart.plot.XYPlot;
14  import org.jfree.data.DomainOrder;
15  import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
16  import org.opentrafficsim.draw.core.BoundsPaintScale;
17  import org.opentrafficsim.draw.graphs.ContourDataSource.ContourDataType;
18  import org.opentrafficsim.draw.graphs.ContourDataSource.Dimension;
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  public abstract class AbstractContourPlot<Z extends Number> extends AbstractSamplerPlot
35          implements XYInterpolatedDataset, ActionListener
36  {
37  
38      
39      private final BoundsPaintScale paintScale;
40  
41      
42      private final Z legendStep;
43  
44      
45      private final String legendFormat;
46  
47      
48      private final String valueFormat;
49  
50      
51      private final ContourDataSource<?> dataPool;
52  
53      
54      private XYInterpolatedBlockRenderer blockRenderer = null;
55  
56      
57  
58  
59  
60  
61  
62  
63  
64  
65  
66      public AbstractContourPlot(final String caption, final OTSSimulatorInterface simulator, final ContourDataSource<?> dataPool,
67              final BoundsPaintScale paintScale, final Z legendStep, final String legendFormat, final String valueFormat)
68      {
69          super(caption, dataPool.getUpdateInterval(), simulator, dataPool.getSampler(), dataPool.getPath(), dataPool.getDelay());
70          dataPool.registerContourPlot(this);
71          this.dataPool = dataPool;
72          this.paintScale = paintScale;
73          this.legendStep = legendStep;
74          this.legendFormat = legendFormat;
75          this.valueFormat = valueFormat;
76          this.blockRenderer = new XYInterpolatedBlockRenderer(this);
77          this.blockRenderer.setPaintScale(this.paintScale);
78          this.blockRenderer.setBlockHeight(dataPool.getGranularity(Dimension.DISTANCE));
79          this.blockRenderer.setBlockWidth(dataPool.getGranularity(Dimension.TIME));
80          setChart(createChart());
81      }
82  
83      
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94      @SuppressWarnings("parameternumber")
95      public AbstractContourPlot(final String caption, final OTSSimulatorInterface simulator, final ContourDataSource<?> dataPool,
96              final Z legendStep, final String legendFormat, final Z minValue, final Z maxValue, final String valueFormat)
97      {
98          this(caption, simulator, dataPool, createPaintScale(minValue, maxValue), legendStep, legendFormat, valueFormat);
99      }
100 
101     
102 
103 
104 
105 
106 
107     private static BoundsPaintScale createPaintScale(final Number minValue, final Number maxValue)
108     {
109         Throw.when(minValue.doubleValue() >= maxValue.doubleValue(), IllegalArgumentException.class,
110                 "Minimum value %s is below or equal to maxumum value %s.", minValue, maxValue);
111         double[] boundaries =
112                 {minValue.doubleValue(), (minValue.doubleValue() + maxValue.doubleValue()) / 2.0, maxValue.doubleValue()};
113         Color[] colorValues = {Color.RED, Color.YELLOW, Color.GREEN};
114         return new BoundsPaintScale(boundaries, colorValues);
115     }
116 
117     
118 
119 
120 
121     private JFreeChart createChart()
122     {
123         NumberAxis xAxis = new NumberAxis("Time [s] \u2192");
124         NumberAxis yAxis = new NumberAxis("Distance [m] \u2192");
125         XYPlot plot = new XYPlot(this, xAxis, yAxis, this.blockRenderer);
126         LegendItemCollection legend = new LegendItemCollection();
127         for (int i = 0;; i++)
128         {
129             double value = this.paintScale.getLowerBound() + i * this.legendStep.doubleValue();
130             if (value > this.paintScale.getUpperBound() + 1e-6)
131             {
132                 break;
133             }
134             legend.add(new LegendItem(String.format(this.legendFormat, scale(value)), this.paintScale.getPaint(value)));
135         }
136         legend.add(new LegendItem("No data", Color.BLACK));
137         plot.setFixedLegendItems(legend);
138         final JFreeChart chart = new JFreeChart(getCaption(), plot);
139         return chart;
140     }
141 
142     
143 
144 
145 
146     public double getTimeGranularity()
147     {
148         return this.dataPool.getGranularity(Dimension.TIME);
149     }
150 
151     
152 
153 
154 
155     public double getSpaceGranularity()
156     {
157         return this.dataPool.getGranularity(Dimension.DISTANCE);
158     }
159 
160     
161 
162 
163 
164 
165     public final void setSpaceGranularity(final double granularity)
166     {
167         this.blockRenderer.setBlockHeight(granularity);
168     }
169 
170     
171 
172 
173 
174 
175     public final void setTimeGranularity(final double granularity)
176     {
177         this.blockRenderer.setBlockWidth(granularity);
178     }
179 
180     
181 
182 
183 
184 
185     public final void setInterpolation(final boolean interpolate)
186     {
187         this.blockRenderer.setInterpolate(interpolate);
188     }
189     
190     
191 
192 
193 
194     public final ContourDataSource<?> getDataPool()
195     {
196         return this.dataPool;
197     }
198 
199     
200     @Override
201     public final int getItemCount(final int series)
202     {
203         return this.dataPool.getBinCount(Dimension.DISTANCE) * this.dataPool.getBinCount(Dimension.TIME);
204     }
205 
206     
207     @Override
208     public final Number getX(final int series, final int item)
209     {
210         return getXValue(series, item);
211     }
212 
213     
214     @Override
215     public final double getXValue(final int series, final int item)
216     {
217         return this.dataPool.getAxisValue(Dimension.TIME, item);
218     }
219 
220     
221     @Override
222     public final Number getY(final int series, final int item)
223     {
224         return getYValue(series, item);
225     }
226 
227     
228     @Override
229     public final double getYValue(final int series, final int item)
230     {
231         return this.dataPool.getAxisValue(Dimension.DISTANCE, item);
232     }
233 
234     
235     @Override
236     public final Number getZ(final int series, final int item)
237     {
238         return getZValue(series, item);
239     }
240 
241     
242     @Override
243     public final Comparable<String> getSeriesKey(final int series)
244     {
245         return getCaption();
246     }
247 
248     
249     @SuppressWarnings("rawtypes")
250     @Override
251     public final int indexOf(final Comparable seriesKey)
252     {
253         return 0;
254     }
255 
256     
257     @Override
258     public final DomainOrder getDomainOrder()
259     {
260         return DomainOrder.ASCENDING;
261     }
262 
263     
264     @Override
265     public final double getZValue(final int series, final int item)
266     {
267         
268         return getValue(item, this.dataPool.getGranularity(Dimension.DISTANCE), this.dataPool.getGranularity(Dimension.TIME));
269     }
270 
271     
272     @Override
273     public final int getSeriesCount()
274     {
275         return 1; 
276     }
277 
278     
279     @Override
280     public int getRangeBinCount()
281     {
282         return this.dataPool.getBinCount(Dimension.DISTANCE);
283     }
284 
285     
286 
287 
288 
289 
290 
291     @Override
292     public final String getStatusLabel(final double domainValue, final double rangeValue)
293     {
294         if (this.dataPool == null)
295         {
296             return String.format("time %.0fs, distance %.0fm", domainValue, rangeValue);
297         }
298         int i = this.dataPool.getAxisBin(Dimension.DISTANCE, rangeValue);
299         int j = this.dataPool.getAxisBin(Dimension.TIME, domainValue);
300         int item = j * this.dataPool.getBinCount(Dimension.DISTANCE) + i;
301         double zValue = scale(
302                 getValue(item, this.dataPool.getGranularity(Dimension.DISTANCE), this.dataPool.getGranularity(Dimension.TIME)));
303         return String.format("time %.0fs, distance %.0fm, " + this.valueFormat, domainValue, rangeValue, zValue);
304     }
305 
306     
307     @Override
308     protected final void increaseTime(final Time time)
309     {
310         if (this.dataPool != null) 
311         {
312             this.dataPool.increaseTime(time);
313         }
314     }
315 
316     
317 
318 
319 
320 
321 
322 
323     protected abstract double getValue(int item, double cellLength, double cellSpan);
324 
325     
326 
327 
328 
329 
330     protected abstract double scale(double si);
331 
332     
333 
334 
335 
336     protected abstract ContourDataType<Z, ?> getContourDataType();
337 
338     public XYInterpolatedBlockRenderer getBlockRenderer()
339     {
340         return blockRenderer;
341     }
342 
343     @Override
344     public final void actionPerformed(final ActionEvent actionEvent)
345     {
346         String command = actionEvent.getActionCommand();
347         if (command.equalsIgnoreCase("setSpaceGranularity"))
348         {
349             
350             double granularity = (double) actionEvent.getSource();
351             setSpaceGranularity(granularity);
352         }
353         else if (command.equalsIgnoreCase("setTimeGranularity"))
354         {
355             
356             double granularity = (double) actionEvent.getSource();
357             setTimeGranularity(granularity);
358         }
359         else
360         {
361             throw new RuntimeException("Unhandled ActionEvent");
362         }
363     }
364 
365 }