# Advanced XY Series Types Androidplot offers several specialized implementations of XYSeries providing various performance or usability enhancements. ## EditableXYSeries Enhances the standard XYSeries interface with edit methods. ## FixedSizeEditableXYSeries An implementation of EditableXYSeries that supports modifying x/y values and has been optimized for speed. FixedSizeEditableXYSeries is optimized for data whose samples may frequently change but whose absolute size doesn't change, such as an ECG (ring buffer) or an animated plot. ## FastXYSeries By default, Androidplot iterates over every element in each series every render cycle to determine it's current min/max values. This is necessary in order to support dynamic plots where data can change at any moment and invalidate the previously calculated min/max vals. FastXYSeries allows the developer to provide a more efficient algorithm to obtain min/max XVals and avoid the high overhead of this iteration. For example, if you know your XVals will always be in strict ascending order then the first and last XVal of the series will always contain the min/max XVals respectively. XYSeries implementation supports a min/max algorithm implementation that is more efficient than doing a comparison on each point of the series via iteration. It's a good idea to implement this interface if your series will contain more than about 500 points ## OrderedXYSeries If the XVals of your series are in ascending order, implementing this interface provides a hint to the series renderer that allows it to avoid iterating over points that are outside the screen's visible domain. For larger data sets, implementing this interface can mean the difference between smooth animations and freezing. ## ScalingXYSeries Wraps any other instance of XYSeries and provides a simple interface for dynamically scaling x and/or y values. A popular use case for dynamic scaling is to create an animated intro for your XYSeries where yVals increase (or decrease in the case of negative values) from 0 towards their original value. The [AnimatedXYPlotActivity](../demoapp/src/main/java/com/androidplot/demos/AnimatedXYPlotActivity.java) in the DemoApp is one example on how this can be accomplished. ## SampledXYSeries SampledXYSeries is meant for use with extremely large datasets. Given a series, multiple sampled series instances at stepped resolutions are generated for faster rendering. Basic usage: ```java XYSeries series = ... // instantiate an XYSeries however you want here // wrap our series in a SampledXYSeries with a threshold of 1000: SampledXYSeries sampledSeries = new SampledXYSeries(series, OrderedXYSeries.XOrder.ASCENDING, 2,100); // add the SampledXYSeries instance to the plot: plot.addSeries(sampledSeries, formatter); ``` SampledXYSeries is meant to be used in conjunction with ZoomEstimator, which enables a plot to automatically render using the resolution appropriate for the current screen boundaries, allowing pan / zoom operations to perform with little or no degradation as series size increases. To enable ZoomEstimator: ```java // enable autoselect of sampling level based on visible boundaries: plot.getRegistry().setEstimator(new ZoomEstimator()); ``` [The Touch Zoom Example source code](../demoapp/src/main/java/com/androidplot/demos/TouchZoomExampleActivity.java) provides a functional reference implementation. If you want to take advantage of the performance benefits of sampling but don't need pan/zoom support check out the Sampling section below. ## NormedXYSeries A convenience wrapper to simplify normalizing XYSeries data in the range of 0 to 1. Usage is straightforward: ```java XYSeries normedSeries = new NormedXYSeries(series); ``` which is equivalent to: ```java XYSeries normedSeries = new NormedXYSeries(series, null, new Norm(null, 0, false)); ``` The first null argument pertains to the normalization being applied to the x axis values. While normalization can certainly be applied on this axis, it's typically unused, which is why the single argument constructor defaults to a null value here. Both examples auto normalize the passed in series, maximizing the resolution of the output result. Sometimes however, it's desirable to control the output resolution for the purpose of visually shifting the result up or down in the graph. This can be done by using the `NormedXYSeries.Norm` constructor's second and third arguments: `offset` and `useOffsetCompression`. ##### offset This value is added to the original normalized value to effectively shift the series data up or down within the normalized range. If the offset is large enough and offset compression is not used, this can cause normalized values to exceed the norm range of 0 to 1. ##### useOffsetCompression If you want to shift a normalized series up or down in the graph but do not want the shift to potentially move values offscreen, you can set `useOffsetComression` to true. This tells `NormedXYSeries` to shrink the scale of the associated series relative to the offset being used to ensure that normed values stay within the range of 0 to 1. If set to true and you supply an offset <0 or >1, an `IllegalArgumentException` will be thrown. We've not talked about the first argument to the `NormedXYSeries.Norm` constructor: `minMax`. This is an optional optimization value that reflects min/max values in the series data being passed in. If they are known, you can pass these in to speed up normalization, otherwise just pass in null and they will be auto calculated. IMPORTANT: If your series data is dynamic and it's min/max values change at runtime, you'll need to invoke `NormedXYSeries.normalize(Norm, Norm)` immediately following each change in order to maintain accuracy. There's a [reference implementation](../demoapp/src/main/java/com/androidplot/demos/DualScaleActivity.java) of a dual scale plot that demonstrates NormedXYSeries usage in the DemoApp. # Sampling Series size is generally the biggest factor when it comes to rendering performance. If you're plotting very large datasets, its generally a good idea to sample the data to improve performance. For example, if you have an XYSeries that consists of 10,000 points, we can sample that data into the 200 points that most accurately represent the profile of the original series: ```java // An instance of any implementation of XYSeries. Assume it's size is 10,000 XYSeries originalSeries = ...; // Sampled series of size 200: EditableXYSeries sampledSeries = new FixedSizeEditableXYSeries( originalSeries.getTitle(), 200); // an instance of an implementation of Sampler: Sampler sampler = ... // do the actual sampling: new LTTBSampler().run(originalSeries, sampledSeries); ``` Currently LTTBSampler is the only available implementation. # Storing series data in onSaveInstanceState If your series data requires a non trivial amount of preprocessing (subsampling etc.) or your data comes from a dynamic source, you'll likely want to persist your series data when your Activity saves its instance state. There are a few caveats to be aware of: * You can only persist about 1mb worth of data at a time so if your series data is much larger than that you'll need to find a creative solution to the problem * Due to [quirks in the way Android persists data](http://stackoverflow.com/questions/12300886/linkedlist-put-into-intent-extra-gets-recast-to-arraylist-when-retrieving-in-nex) `XYSeries` implementations such as `SimpleXYSeries` that use `LinkedList` instances to store data cannot be serialized directly. * Formatters generally cannot be persisted as they typically contain instances of `Paint` that cannot be serialized directly.. Due to these limitations we suggest storing `XYSeries` data into an array or `ArrayList` and using that to instantiate your `XYSeries`. The DemoApp's [Time Series Example](../demoapp/src/main/java/com/androidplot/demos/TimeSeriesActivity.java) contains a full source example.