I have a simple core plot with xAxis alternatingBandFills
xAxis.alternatingBandFills = [NSArray arrayWithObjects:[bandFillColor colorWithAlphaComponent:0.1], [NSNull null], nil];
The effect:
Now when I scroll horizontally (panning to the left), when the region of x between 0 and 10 goes to the left outside of the viewable range, the fill color would "jump" and 10 to 20 would become highlighted instead (and 20 to 30 would become white, and so on).
Is there a way to keep the band as it is without the alternation while scrolling?
For now, use a plot space delegate to monitor changes to the plot space and shuffle the fill colors as needed. I added issue 601 to the Core Plot issue tracker requesting a feature to make this easier.
As a workaround, I implemented some BarPlot on the visible screen but the performance are bad on iPhone 4&4S compare to alternatingBandFills even with very few data so I came back to this alternatingBandFills by shuffling the array in the - plotSpace:didChangePlotRangeForCoordinate: has Eric said. Here the solution I implemented:
-(void)plotSpace:(CPTPlotSpace *)space didChangePlotRangeForCoordinate:(CPTCoordinate)coordinate {
if (self.plotSpace.xRange.locationDouble <0) {//area before your xrange origin to pass from 0 to -1 and not 0 to -0
value = value-1;
}
int value = plotSpace.xRange.locationDouble.x/majorInterval;
if (self.currentFillBand != value%2) {
NSArray * array = xaxis.alternatingBandFills;
xaxis.alternatingBandFills = #[array[1], array[0]];
self.currentFillBand = value%2;
}
}
If your globalXRange is larger/before than your xrange.origin, so your xRange.location will be <0 when you scroll backward but the value variable will pass from 0 to -0 and so I retrieve -1 to the value in such case.
Note: I still have a small issue with a new majorTick before your globalXRange, I got a shuffling I can't fix, so I avoid that case.
Related
I am using the charts framework for iOS in Objective-C.
I am trying to plot data value labels (y values) on a chart which has 4 data sets. if there are 1, 2 or 3 data sets on my chart, the label values show up just fine. If I add a 4th data set, the label values do not show up at all.
I am using a NSMutableArray of UIColor objects for each dataset. I set the label color to [UIColor clearColor] when I don't want the label to show up and I set it to [UIColor whiteColor] when I do want it to show up, for each data point.
NSMutableArray *labelColors = [[NSMutableArray alloc] init];
for (NSDictionary *data in dataArray )
{
if (condition)
{
[labelColors addObject:UIColor.whiteColor];
}
else
{
[labelColors addObject:UIColor.clearColor];
}
}
dataSet.valueColors = labelColors;
Again, this technique works fine if I plot 1, 2, or 3 data sets on my X axis, but if I plot a 4th data set, it stops drawing labels completely. The way I have my chart set up, two data sets are on the left Y axis and two data sets are on the right Y axis.
Does anybody know why my labels are not showing?
I fixed this with one line in viewDidLoad where I first set up my chart.
_chartView.maxVisibleCount = 500;
I guess after adding the 4th data set I had more than the default value of maxVisibleCount (whatever that is) and when that happens no data labels are drawn regardless of other settings.
I figured this out when modifying my data set's drawValuesEnabled field and inside the auto complete text it said "this value is ignored when maxVisibleCount is reached".
I have one graph correctly plotted in my plotspace and I am trying to plot a second graph triggered by a segmented control button instead of the first one. The x-axis (xRange and globalXRange) and their corresponding values between graph 1 and 2 are staying the same, just the corresponding y-axis (scale and labels) are changing. This requires a scale and label change on the y-axis...
For the first plot I already set:
// PLOT RANGE Y-Axis
self.plotSpace.yRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromDouble(-1.2) length:CPTDecimalFromDouble(6.5)];
self.plotSpace.globalYRange = self.plotSpace.yRange;
// Visibility of Y-Axis
self.axisSet.yAxis.visibleRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromInteger(0)
length:CPTDecimalFromInteger(5)];
This is working perfect.
When changing self.plotSpace.yRange and self.plotSpace.grobalYRange with new CPTPlotRanges ( I am doing this in the same way as above inside of my segment - if-else statement) , both: y-axis scales and labels are not changing as desired, w.r.t the new y-axis ranges (should be from 0 - 100 and not still from 0 - 5). Just the minorTicksPerInterval changes properly.
How to do that? BTW: I am not looking for 2 y-axis in the same plot. Any suggestions...
The globalYRange constrains the new values assigned to the yRange. Set globalYRange to nil before changing the yRange.
self.plotSpace.globalYRange = nil;
self.plotSpace.yRange = newRange;
self.plotSpace.globalYRange = self.plotSpace.yRange;
I'm following the examples in CorePlot to add new points to a scatter plot as I acquire them. I modeled my graph from the RealTimePlot example that ships with CoreData. I noticed that when I'm running my graphs, the CPU pegs to 100% once the screen starts scrolling to render the new data. I've tried different ways of adding the data into my dataSource as well as update calls, but I can't get anything that's both responsive and an acceptable CPU load.
After much trial and error, I fired up the RealTimePlot example to see what the CPU load was, and it's pegging at 100% as well. This is on an iPhone 5s, so I'm scared to see how a 4 will act. My question; Is there an alternative way to update the graph?
Anyhow, here is my update loop. I'm taking accelerometer data and graphing it.
-(void)motionController:(VWWMotionController*)sender didUpdateAcceleremeters:(CMAccelerometerData*)accelerometers{
static NSInteger counter = 0;
CPTGraph *theGraph = self.graph;
CPTPlot *thePlot = [theGraph plotWithIdentifier:#"Blue Plot"];
if ( thePlot ) {
if ( self.dataForPlot.count >= NUM_POINTS ) {
[self.dataForPlot removeObjectAtIndex:0];
[thePlot deleteDataInIndexRange:NSMakeRange(0, 1)];
}
CPTXYPlotSpace *plotSpace = (CPTXYPlotSpace *)theGraph.defaultPlotSpace;
NSUInteger location = (counter >= NUM_POINTS ? counter - NUM_POINTS + 2 : 0);
CPTPlotRange *oldRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromUnsignedInteger( (location > 0) ? (location - 1) : 0 )
length:CPTDecimalFromUnsignedInteger(NUM_POINTS - 2)];
CPTPlotRange *newRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromUnsignedInteger(location)
length:CPTDecimalFromUnsignedInteger(NUM_POINTS - 2)];
[CPTAnimation animate:plotSpace
property:#"xRange"
fromPlotRange:oldRange
toPlotRange:newRange
duration:CPTFloat(0)];
NSNumber *x = #(counter);
NSNumber *y = #(accelerometers.acceleration.x);
[self.dataForPlot addObject:#{ #"x": x,
#"y": y }];
dispatch_async(dispatch_get_main_queue(), ^{
[thePlot insertDataAtIndex:self.dataForPlot.count - 1 numberOfRecords:1];
});
counter++;
}
}
FYI. I'd ideally like to have 30 FPS. I have adjusted the accelerometer callback from 1/5 to 1/30 with some CPU improvement, but then I'm loosing frame rate.
If I comment out the insertDataAtIndex line, the CPU hangs back at 1%, but of course the graph won't follow the new data.
I don't see any derivatives of the insert method.
I've also read in other threads that recalculating the axis labels is resource intesive. I've tried removing them with very little improvement.
I'm thinking of going to OpenGL or CoreGraphics for this job.
What do you suggest?
NOTE: I've not tested on an iPhone 5s (I've done iPhone 4s, 5, iPad 3, iPad 4, iPad Mini 1) and our apps seem to have different requirements, so I'm not positive this will help you.
Regardless, in my testing I've found that I get the best, most responsive results by redrawing all points each time I get a new data point. This is best for me because I am having to update the graph ~120 times per second. I don't have an iPhone around to test our app's CPU, but on an iPad Mini, the CPU is < 50% with the graph running 2 plots (~125 points each, though this fairly responsive put to around 250 points each). The graph only has labels on the y-axis and they don't change. The x-axis is time, and the labels are unnecessary for us.
Our requirements may be too different for this implementation to help you, but it may be worth a shot. If you want to, it goes something like this:
-(void)UpdateGraph
{
//foreach scatterplot
{
//Remove point from array
//[linePlot deleteDataInIndexRange:NSMakeRange(0, [array count])]
}
//Insert new data point into array(s)
//foreach scatterplot
{
//[linePlot insertDataAtIndex:0 numberOfRecords:[array count]]
}
//Schedule next update if necessary
//[self performSelector:#selector(UpdateGraph) withObject:nil afterDelay:(1/(CGFloat)Your_FPS_Here)];
}
Hope that helps you and best of luck!
The key is to make the graph simple and fast to draw. Set the labeling policy so that you don't have too many labels. Don't use minor ticks or grid lines (set the line styles to nil). Turn off major grid lines, too, if you don't need them. Don't use an area fill on the plot.
We're working on a Highcharts Bubble Chart w/category axis and a lot of overlapping data points. Is there any way to control exactly how the bubbles are placed within the category? What we'd like to do is sort the bubbles ahead of time and then offset them from each other a bit. Some overlap is desirable so we'd prefer to not have to add additional categories to make sure there is no overlap.
Unfortunately we have no solution for controling positions of the bubbles. But you can request your propositin in our uservoice system http://highcharts.uservoice.com/
If you are using categories, your x values are the array index of the category for your value.
So to tweak the placement, you can tweak your x value by adding/subtracting small decimal amounts:
http://jsfiddle.net/yPLVP/10/
[-0.1,2,10]
Keeping in mind that +/- .5 is the center point between the categories - so for a value that falls within the 3rd category (x value 2), keep your x values between 1.55 and 2.45 (or so....)
I have the same problem, you can add this code:
function(chartObj) {
$.each(chartObj.series[0].data, function(i, point) {
var aux = 0;
if (i % 2 == 0)
aux = point.dataLabel.y + 6;
else
aux = point.dataLabel.y - 6;
point.dataLabel.attr({y:aux});
});
jsFiddle: http://jsfiddle.net/9m6wu/277/
I'm trying to implement median filtering using image j .
I am having trouble with the zero padding as it adds extra zeros to the bottom and far left of the picture.
This is what I have done so far, if you guys can help me out:
Dialog.create("9x9 median filtering");
Dialog.addMessage("9x9 median filtering");
Dialog.show();
setBatchMode(true);
median_filter_9();
setBatchMode("exit and display");
// Produce the 9x9 median image
function median_filter_9()
{
width = getWidth();
height= getHeight();
//if you want to apply this median filter to 16bit
depth = bitDepth();
nBin= pow(2, depth);
//nBin hold max gray intensity value
filteHisto = newArray(nBin);
//filteHisto = newArray(255);
fiveBYFive = newArray(81);
//this is what i used for middle position of array to get median
middlePos = round(81/2);
//-3, -3 will get you position 0,0 of a 9x9 matrix if you start in the middle
for(j=-2;j<width-2;j++){
for(i=-2;i<height-2;i++){
z=0;
for(r=0;r<9;r++){
for(c=0;c<9;c++){
//Extend outside image boundaries using zero padding.
//error here: adds extra to bottom and farleft of picture
if(j+r<0||j+r>=width||i+c<0||i+c>=height){
fiveBYFive[z]=0;
z++;
}else{
v = getPixel(j+r,i+c);
fiveBYFive[z]= v;
z++;
}
}
}
//sort the array to find median
Array.sort(fiveBYFive);
median = fiveBYFive[middlePos];
setPixel(j, i, median);
}
updateDisplay();
}
}
One problem you're seeing at the edges of your image is because you are padding your 9x9 window with zeroes ok, but you still take the median value as the middle of the 81 item window.
So, for example, in the first column of the image, you zero-pad at least 36 elements (more at the top and bottom), which means that you only need to find 4 or 5 more zero pixels in the image to make the median element zero.
The easiest fix is to adjust your median element's index (initialised to 81/2 on each iteration) upward according to how many zeroes you added, or just count how many non-zero pixels you used and then find the median mid-way through that range in your sorted array (taking account of sort order).
In this way, you take the median value of the actual pixels you found and ignore the padded zeroes.
Probably, you missed changing your code from the original 5x5 to 9x9, because the start/end indices are in any case wrong and should be
for(j=-4;j<width;j++){
for(i=-4;i<height;i++){
The other possible source of confusion later is with this line, where it looks like you've confused width and height
if(j+r<0||j+r>=width||i+c<0||i+c>=height)
If j is the column index and i is the row index, it should be
if(j+c<0||j+c>=width||i+r<0||i+r>=height)
Although for a square window this doesn't actually make any difference in practice.