I'm using TeeChart for drawing incoming data points that are received via BLE.
I created an TeeChart that has three costum axes to divide the Y-Axis in three equally sized parts. In addition, I initialized three FastLines and assigned each line to one custom axis.
//LINES
line1 = new Line(chart1.getChart());
line1.setColor(Color.black);
line1.getLinePen().setWidth(3);
line2 = new Line(chart1.getChart());
line2.setColor(Color.black);
line2.getLinePen().setWidth(3);
line3 = new Line(chart1.getChart());
line3.setColor(Color.black);
line3.getLinePen().setWidth(3);
//AXES
Axis axis0 = new Axis(false, false, chart1.getChart());
axis0.setVisible(true);
axis0.getLabels().setVisible(false);
axis0.getMinorTicks().setVisible(true);
axis0.getTicksInner().setVisible(false);
axis0.getTicks().setVisible(true);
chart1.getAxes().getCustom().add(axis0);
line1.setCustomVertAxis(axis0);
axis0.getTitle().getFont().setSize(textSizeY);
axis0.getTitle().getFont().setBold(true);
axis0.getTitle().setText("1 mV");
axis0.getTitle().setAngle(90);
axis0.setStartPosition(0);
axis0.setEndPosition(33);
axis0.setRelativePosition(0);
axis0.setAutomatic(false);
axis0.setIncrement(100);
axis0.setMinimum(-350);
axis0.setMaximum(+350);
Axis axis1 = new Axis(false, false, chart1.getChart());
axis1.setVisible(true);
axis1.getLabels().setVisible(false);
axis1.getMinorTicks().setVisible(true);
axis1.getTicksInner().setVisible(false);
axis1.getTicks().setVisible(true);
chart1.getAxes().getCustom().add(axis1);
line2.setCustomVertAxis(axis1);
axis1.getTitle().getFont().setSize(textSizeY);
axis1.getTitle().getFont().setBold(true);
axis1.getTitle().setText("1 mV");
axis1.getTitle().setAngle(90);
axis1.setStartPosition(33);
axis1.setEndPosition(66);
axis1.setRelativePosition(0);
axis1.setAutomatic(false);
axis1.setIncrement(100);
axis1.setMinimum(-350);
axis1.setMaximum(+350);
Axis axis2 = new Axis(false, false, chart1.getChart());
axis2.setVisible(true);
axis2.getLabels().setVisible(false);
axis2.getMinorTicks().setVisible(true);
axis2.getTicksInner().setVisible(false);
axis2.getTicks().setVisible(true);
chart1.getAxes().getCustom().add(axis2);
line3.setCustomVertAxis(axis2);
axis2.getTitle().getFont().setSize(textSizeY);
axis2.getTitle().getFont().setBold(true);
axis2.getTitle().setText("1 mV");
axis2.getTitle().setAngle(90);
axis2.setStartPosition(66);
axis2.setEndPosition(100);
axis2.setRelativePosition(0);
axis2.setAutomatic(false);
axis2.setIncrement(100);
axis2.setMinimum(-350);
axis2.setMaximum(+350);
As soon as there is data coming via BLE, the data points are plotted in the TeeChart as follows:
public static void addDataToTeeChart(short i){
if(numberOfPlottablePoints >= 3*MAX_X_POINTS) {
numberOfPlottablePoints = 0;
line1.clear();
line2.clear();
line3.clear();
}
if (numberOfPlottablePoints < MAX_X_POINTS){
line1.add(i);
}
else if (numberOfPlottablePoints < 2*MAX_X_POINTS){
line2.add(i);
}
else if (numberOfPlottablePoints < 3*MAX_X_POINTS){
line3.add(i);
}
numberOfPlottablePoints++;
}
The problem now is that while adding data to line1, everything is working just fine. But adding data to line2 becomes slower and the drawing gets some kind of juddery. And it's even worse for line3.
After clearing and back to adding points to line1, the plotting is fine again.
I was thinking that maybe there is too much data coming in.
To rule that out, I added all incoming data to line1 (leaving line2 and line3 empty). That worked fine again!
I then tried to plot all data in line2 (leaving Line1 and Line3 empty) but there was no data plotted at all!
Also reducing MAX_X_POINTS to reduce the amout of plotted points, didn't do it for me.
For me it seems that the problem is with toggling between line1, line2 and line3 because when I just use one line (independet of which custom axis was used) the drawing is smooth!
I have no idea what else to try.
I hope that maybe someone can even tell me what exactly might be the problem or even has an suggestion what to do.
Thank you in advance!
I did a rapid test with your code (with a few changes), and it seems to work fine for me here:
The changes I did are basically:
Disable manual configuration of the 3 custom axes scales.
Set the chart to 2D.
Fill the series with sample values instead of using your addDataToTeeChart function.
Find the code here:
//LINES
Line line1 = new Line(tChart1.getChart());
line1.setColor(Color.black);
line1.getLinePen().setWidth(3);
Line line2 = new Line(tChart1.getChart());
line2.setColor(Color.black);
line2.getLinePen().setWidth(3);
Line line3 = new Line(tChart1.getChart());
line3.setColor(Color.black);
line3.getLinePen().setWidth(3);
//AXES
Axis axis0 = new Axis(false, false, tChart1.getChart());
axis0.setVisible(true);
axis0.getLabels().setVisible(false);
axis0.getMinorTicks().setVisible(true);
axis0.getTicksInner().setVisible(false);
axis0.getTicks().setVisible(true);
tChart1.getAxes().getCustom().add(axis0);
line1.setCustomVertAxis(axis0);
//axis0.getTitle().getFont().setSize(textSizeY);
axis0.getTitle().getFont().setBold(true);
axis0.getTitle().setText("1 mV");
axis0.getTitle().setAngle(90);
axis0.setStartPosition(0);
axis0.setEndPosition(33);
axis0.setRelativePosition(0);
/*axis0.setAutomatic(false);
axis0.setIncrement(100);
axis0.setMinimum(-350);
axis0.setMaximum(+350);*/
Axis axis1 = new Axis(false, false, tChart1.getChart());
axis1.setVisible(true);
axis1.getLabels().setVisible(false);
axis1.getMinorTicks().setVisible(true);
axis1.getTicksInner().setVisible(false);
axis1.getTicks().setVisible(true);
tChart1.getAxes().getCustom().add(axis1);
line2.setCustomVertAxis(axis1);
//axis1.getTitle().getFont().setSize(textSizeY);
axis1.getTitle().getFont().setBold(true);
axis1.getTitle().setText("1 mV");
axis1.getTitle().setAngle(90);
axis1.setStartPosition(33);
axis1.setEndPosition(66);
axis1.setRelativePosition(0);
/*axis1.setAutomatic(false);
axis1.setIncrement(100);
axis1.setMinimum(-350);
axis1.setMaximum(+350);*/
Axis axis2 = new Axis(false, false, tChart1.getChart());
axis2.setVisible(true);
axis2.getLabels().setVisible(false);
axis2.getMinorTicks().setVisible(true);
axis2.getTicksInner().setVisible(false);
axis2.getTicks().setVisible(true);
tChart1.getAxes().getCustom().add(axis2);
line3.setCustomVertAxis(axis2);
//axis2.getTitle().getFont().setSize(textSizeY);
axis2.getTitle().getFont().setBold(true);
axis2.getTitle().setText("1 mV");
axis2.getTitle().setAngle(90);
axis2.setStartPosition(66);
axis2.setEndPosition(100);
axis2.setRelativePosition(0);
/*axis2.setAutomatic(false);
axis2.setIncrement(100);
axis2.setMinimum(-350);
axis2.setMaximum(+350);*/
tChart1.getAspect().setView3D(false);
line1.fillSampleValues(100);
line2.fillSampleValues(100);
line3.fillSampleValues(100);
To increment the fonts depending on the screen resolution I also use this:
private void proportionalFonts() {
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
float myDensity = metrics.density;
tChart1.getAspect().setFontZoom(tChart1.getAspect().getFontZoom()*myDensity*1.5);
}
If you still find problems with it, please try to arrange an sscce.
Related
I am facing some weird problems with my (Line) GraphView!
To explain it a bit better I'll add a pic that shows what happens when I start scrolling horizontal (vertical scrolling is not needed).
The thing is:
The DataPoints are displayed correctly, but not my labels! I wrote the correct label in red color into the pictures above!
Whenever I start scrolling the labels start to "jump" around weirdly.
The DataPoints are received from a SharedPreference file with String/Integer pairs [Strings are always MMMyyyy (f.e. "Mar2020")]
I get the pairs out of my SP-file, put the Strings as static labels into my graph use the DataPoints in this way "logical" way (not the actual code!)
DataPoint[0] = (0, valueOfKey0)
DataPoint[1] = (1, valueOfKey1)
Here is the code how the graph is called for / drawn in the Activity
private void setupGraph(final GraphView graph, String subName){
GraphDataHelper graphDataHelper = new GraphDataHelper(mContext);
LineGraphSeries<DataPoint> series;
/*Have to call .removeAllSeries to draw each graph individually
* Otherwise they will be drawn over another!*/
graph.removeAllSeries();
/*Gets Values from each Sub
* --> Gets values form each key/value pair of specific SharedPreference-file
* --> sets values in order (0, 1, 2,... n)*/
series = new LineGraphSeries<DataPoint>(graphDataHelper.generateGraphData(subName));
series.setDrawDataPoints(true);
series.setColor(getResources().getColor(R.color.casualWhite));
series.setAnimated(true);
graph.addSeries(series);
/*Fill Labels for X-Axis:
* Get the specific Monthly-Keys for the choosen Sub
* In special cases extra rules are declared in .getSpecificSubMonthKeys() method
* Put all entries of ArrayList in Array (StaticLabelsFormatter only works with "normal" Array)*/
ArrayList<String> array_paths = graphDataHelper.getSpecificSubMonthKeys(subName);
int size = array_paths.size();
String[] paths = new String[size];
for (int i=0; i<size; i++){
paths[i] = array_paths.get(i);
}
StaticLabelsFormatter staticLabelsFormatter = new StaticLabelsFormatter(graph);
staticLabelsFormatter.setHorizontalLabels(paths);
graph.getGridLabelRenderer().setLabelFormatter(staticLabelsFormatter);
graph.getViewport().setXAxisBoundsManual(true);
if(size == 5 || size > 5){
graph.getViewport().setMinX(0.0);
graph.getViewport().setMaxX(4.0);
graph.getGridLabelRenderer().setNumHorizontalLabels(5);
} else if(size == 4){
graph.getViewport().setMinX(0.0);
graph.getViewport().setMaxX(3.0);
graph.getGridLabelRenderer().setNumHorizontalLabels(4);
} else if(size == 3){
graph.getViewport().setMinX(0.0);
graph.getViewport().setMaxX(2.0);
graph.getGridLabelRenderer().setNumHorizontalLabels(3);
} else if(size == 2){
graph.getViewport().setMinX(0.0);
graph.getViewport().setMaxX(1.0);
graph.getGridLabelRenderer().setNumHorizontalLabels(2);
}
graph.getViewport().setScrollable(true);
graph.getGridLabelRenderer().setHorizontalLabelsColor(getResources().getColor(R.color.midBlue));
graph.getGridLabelRenderer().setVerticalLabelsColor(getResources().getColor(R.color.midBlue));
graph.getGridLabelRenderer().setGridColor(getResources().getColor(R.color.darkBlue));
graph.getGridLabelRenderer().setPadding(50);
graph.getGridLabelRenderer().setLabelsSpace(30);
I call for .removeAllSeries because the User is able to call for another graph via a dropdown-Spinner.
Also I set different MinX & MaxX Points because there can be the case that an entry doesn't have 5 DataPoints yet and so I change the Viewport in regard of that (didn't find a better solution for that so I am also open for some better code here, but it does work this way). My SharedPreference-file always has a minimun of 2 key/value pairs
Also little extra question: Is there a way to give ONLY the horizontal labels some extra labelspace. If I use setLabelSpace it will also apply on the vertical labels and I do not want that. But the horizontal labels are just so close to the axis.
Also sorry for ugly code. It was my first time using GraphView!
Thanks ahead!
I am using Androidplot to display real time data. The real time data is dynamic and the x axis is time. The domain is a fixed timespan. Because the values could be any double, rangeStepMode is set to subdivide the y-axis. The issue is that when all the values are the same, the plot does not draw correctly.
You can see in the image that there are no range grids or labels.
From this related question:
This happens because Androidplot does not have enough information to automatically calculate what a reasonable domain/range scale would be from a single point.
This makes sense, and the linked solution of setting a non-zero range boundary works but instead of an arbitrary range like miny - 1to maxy + 1, I'd like for there to be only one labeled range line in these cases across the middle of the screen.
My Activity:
public class SimpleXYPlotActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.simple_xy_plot_example);
XYPlot plot = (XYPlot) findViewById(R.id.plot);
Number[] series1Numbers = new Number[]{
1,
1,
};
XYSeries series1 = new SimpleXYSeries(SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "Series1", series1Numbers);
LineAndPointFormatter series1Format = new LineAndPointFormatter(Color.RED, Color.GREEN, null, null);
plot.addSeries(series1, series1Format);
StepMode stepMode = StepMode.SUBDIVIDE;
int stepValue = 10;
plot.setRangeStepMode(stepMode);
plot.setRangeStepValue(stepValue);
}
}
My Layout:
<com.androidplot.xy.XYPlot
android:id="#+id/plot"
style="#style/APDefacto.Dark"
android:layout_width="match_parent"
android:layout_height="match_parent"
ap:rangeStep="5"
ap:rangeStepMode="subdivide"
ap:domainStep="1"
ap:domainStepMode="increment_by_val"
/>
What I've tried
The following works on the simple xy example, but when I try setting it within minmax in my time series class, the thread drawing the domain gridlines gets into a very long loop as it calculates xPix to be very very large. This also isn't a very solution for situations where I have multiple series. Any ideas on how I can get the results I want?
Double minY = null;
Double maxY = null;
for (Double aDouble : series1Numbers) {
if (minY == null) {
minY = aDouble;
} else {
minY = Math.min(minY, aDouble);
}
if (maxY == null) {
maxY = aDouble;
} else {
maxY = Math.max(maxY, aDouble);
}
}
if (minY.equals(maxY)) {
plot.setUserRangeOrigin(minY);
plot.setRangeBoundaries(0, 2 * maxY, BoundaryMode.FIXED);
plot.setRangeStepMode(StepMode.INCREMENT_BY_PIXELS);
plot.setRangeStepValue(Double.POSITIVE_INFINITY);
} else {
plot.setUserRangeOrigin(0);
plot.setRangeBoundaries(minY, maxY, BoundaryMode.FIXED);
plot.setRangeStepMode(StepMode.SUBDIVIDE);
plot.setRangeStepValue(5);
}
This wont get rid of the CPU overhead but you can use PlotUtils.minMax to find these values for you
If implementing and using FastXYSeries is a possibility for you then the overhead of using minMax drops away too, as the calculation only ever gets done when your XYSeries data changes.
I have searching a lot on Internet but I have not found a solution to my problem.
The issue is the following: I'm trying to make a Combined chart, in which there is a LineChart and a BarChart.
When I have, for example, these values, it works fine:
double[] valuesXaxisGraph1 = new double[]{1,2,3,4,5};
double[] dataGraph1 = new double[]{5,5,5,5,5};
double[] valuesXaxisGraph2 = new double[]{1,2,3,4,5}
double[] dataGraph2 = new double[]{6,6,6,6,6};
The graph created is the following:
However, if I use these values:
double[] valuesXaxisGraph1 = new double[]{1,2,3,4,5,6,7,8,9,10};
double[] dataGraph1 = new double[]{5,5,5,5,5,5,5,5,5,5};
double[] valuesXaxisGraph2 = new double[]{1,2,3,4,5};
double[] dataGraph2 = new double[]{6,6,6,6,6};
The graph shown is the following:
As you can see, the bars appears from 1 to 10, instead from 1 to 5. Because of that, these bars aren't positioned where they should be (1, 2, 3, 4...).
Why is happening this? What should I do to fix it?
Thanks in advance.
You can set the range for the second scale to be the same for the first scale. For instance, for setting the X axis maximum value, you would do:
renderer.setXAxisMax(renderer.getXAxisMax(0), 1);
I am trying to solve a problem with drawing a path from huge (100k+) set of GeoPoints to a MapView on Android.
Firstly I would like to say, I searched through StackOverflow a lot and haven't found an answer.The bottleneck of my code is not actually drawing into canvas, but Projection.toPixels(GeoPoint, Point) or Rect.contains(point.x, point.y) method..I am skipping points not visible on screen and also displaying only every nth point according to current zoom-level. When the map is zoomed-in I want to display as accurate path as possible so I skipping zero (or nearly to zero) points, so that when finding visible points I need to call the projection method for every single point in the collection. And that is what really takes a lot of time (not seconds, but map panning is not fluid and I am not testing it on HTC Wildfire:)). I tried caching calculated points, but since points be recalculated after every map pan/zoom it haven't helped
at all.
I thought about usage of some kind of prune and search algorithm instead of iterate the array, but I figured out the input data is not sorted (I can't throw away any branch stacked between two invisible points). That could I possible solve with simple sort at the beginning, but I am still not sure even the logarithmic count of getProjection() and Rect.contains(point.x, point.y) calls instead of linear would solve the performance problem.
Bellow is my current code. Please help me if you know how to make this better. Thanks a lot!
public void drawPath(MapView mv, Canvas canvas) {
displayed = false;
tmpPath.reset();
int zoomLevel = mapView.getZoomLevel();
int skippedPoints = (int) Math.pow(2, (Math.max((19 - zoomLevel), 0)));
int mPointsSize = mPoints.size();
int mPointsLastIndex = mPointsSize - 1;
int stop = mPointsLastIndex - skippedPoints;
mapView.getDrawingRect(currentMapBoundsRect);
Projection projection = mv.getProjection();
for (int i = 0; i < mPointsSize; i += skippedPoints) {
if (i > stop) {
break;
}
//HERE IS THE PROBLEM I THINK - THIS METHOD AND THE IF CONDITION BELOW
projection.toPixels(mPoints.get(i), point);
if (currentMapBoundsRect.contains(point.x, point.y)) {
if (!displayed) {
Point tmpPoint = new Point();
projection.toPixels(mPoints.get(Math.max(i - 1, 0)),
tmpPoint);
tmpPath.moveTo(tmpPoint.x, tmpPoint.y);
tmpPath.lineTo(point.x, point.y);
displayed = true;
} else {
tmpPath.lineTo(point.x, point.y);
}
} else if (displayed) {
tmpPath.lineTo(point.x, point.y);
displayed = false;
}
}
canvas.drawPath(tmpPath, this.pathPaint);
}
So I figured out how to make it all much faster!
I will post it here, somebody could possibly found it useful in the future.
It has emerged that usage of projection.toPixels() can really harm application performance. So I figured out that way better than take every single GeoPoint, convert it to Point and then check if it is contained in map viewport is, when I count actuall viewport radius of the map as following:
mapView.getGlobalVisibleRect(currentMapBoundsRect);
GeoPoint point1 = projection.fromPixels(currentMapBoundsRect.centerX(), currentMapBoundsRect.centerY());
GeoPoint point2 = projection.fromPixels(currentMapBoundsRect.left, currentMapBoundsRect.top);
float[] results2 = new float[3];
Location.distanceBetween(point1.getLatitudeE6()/1E6, point1.getLongitudeE6()/1E6, point2.getLatitudeE6()/1E6, point2.getLongitudeE6()/1E6, results2);
The radius is in results2[0]..
Then I can take every single GeoPoint and count the distance between it and the center of the map mapView.getMapCenter(). Then I can compare the radius with computed distance and decide whether ot not diplay the point.
So that's it, hope It will be helpful.
I have a 2D game that uses two integer arrays to track x and y coordinates as shown below:
private int gridX[] = { 0,0,0,0,0 }
private int gridY[] = { 0,0,0,0,0 }
The problem is I can have a LOT of objects on the screen that needs to be tracked. Is there a way to add integers / create new blocks as needed? IE in a loop, do something like
gridX[].add(); or something like that. I'm relatively new to java and droid development and I'm having trouble finding a good tutorial or example that shows how to do this without having to initialize the gridX and gridY to sizes of 100 or so.
This is important, as I am about 90% sure that all those unused 0's are causing androids garbage cleanup to lag my application.
Why dont you use an array list instead of an Integer array?
that way you can dynamically add items to the list
ArrayList<Integer> myList = new ArrayList<Integer>();
myList.add(1);
myList.add(2);
Why not also use the Point class?
List<Point> points = new ArrayList<Point>();
points.add(new Point(0, 0));
points.add(new Point(50, 70));
Point point = points.get(1);
Log.d("MyApp", String.format("The point is: (%d, %d)", point.x, point.y);
This way you are keeping track of your x and y coordinates together and there is less opportunity for error.