I am using CorePlot to graph a set of data. Data for my x-axis consists of dates, while data for my y-axis consists of floats (which I then turn into NSNumber). I am getting data from a feed, and the feed returns numbers with a large number of decimals, eg: 0.46673718852844, 4.59392222219, 353.1293012045.
I'm using the following piece of code to properly format y-axis:
NSNumberFormatter *numberFormatter = [[[NSNumberFormatter alloc] init] autorelease];
[numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
[numberFormatter setMaximumFractionDigits:3];
[numberFormatter setPositiveFormat:#"###0.000"];
CPTXYAxis *y = axisSet.yAxis;
y.labelingPolicy = CPTAxisLabelingPolicyAutomatic;
y.minorTicksPerInterval = 1;
y.preferredNumberOfMajorTicks = 5;
y.labelFormatter = numberFormatter;
In most cases, everything works fine. However, sometimes it displays the same value at multiple positions along the axis. See image below:
The values are presented correctly, I would just like to avoid unnecessary (0.466) labels. I've even tried to round the numbers to 3 decimals in numberForPlot method:
if(fieldEnum == CPTScatterPlotFieldY)
{
float floatNum = r.value;
floatNum *= 1000;
int intValue = (int) floatNum;
float decimal = floatNum - intValue;
if (decimal > 0.5) {
intValue++;
}
floatNum = intValue /1000.0;
return [NSNumber numberWithFloat:floatNum];
}
but there is no difference in labels.
The preferredNumberOfMajorTicks property tells the labeling algorithm how many tick marks to use. If you want three ticks, set it to 3. You could also increase the maximum number of fraction digits for the number formatter to avoid the rounding issue altogether.
I believe that because you have said that you want 5 major ticks, that is what you are going to get, regardless of how close the y axis values are. If you sense that the values are too close together, you will probably have to adjust your preferredNumberOfMajorTicks property.
In my app that I am using Core Plot, I turned off the automatic axis labeling and I am putting together my own x and y axis labels. It is a bit more work, but i like the flexibility of doing it myself.
What I did here was dynamically set my preferredNumberOfMajorTicks property while the graph is plotted. Since my formatter shows 3 decimal places, I used the following code to change the number of ticks depending on the Y axis range so that the formatter will show a unique value on the axis at each tick. Too many ticks means the decimal places get cut off and there are duplicate values on the Y axis.
CPTXYAxis *y = axisSet.yAxis;
{
y.preferredNumberOfMajorTicks = round([newYRange.length floatValue]/0.001);
}
I hope this helps.
Related
I'm really scratching my head here in an effort to understand a quote i read somewhere that says "the more we zoom inside the fractal, the more iteration we will most likely need to perform".
so far, i haven't been able to find any mathematical / academical paper that proves that saying.
i've also managed to find a small code that calculates the mandelbrot set, taken from here :
http://warp.povusers.org/Mandelbrot/
but yet, wasn't able to understand how zooming affects iterations.
double MinRe = -2.0;
double MaxRe = 1.0;
double MinIm = -1.2;
double MaxIm = MinIm+(MaxRe-MinRe)*ImageHeight/ImageWidth;
double Re_factor = (MaxRe-MinRe)/(ImageWidth-1);
double Im_factor = (MaxIm-MinIm)/(ImageHeight-1);
unsigned MaxIterations = 30;
for(unsigned y=0; y<ImageHeight; ++y)
{
double c_im = MaxIm - y*Im_factor;
for(unsigned x=0; x<ImageWidth; ++x)
{
double c_re = MinRe + x*Re_factor;
double Z_re = c_re, Z_im = c_im;
bool isInside = true;
for(unsigned n=0; n<MaxIterations; ++n)
{
double Z_re2 = Z_re*Z_re, Z_im2 = Z_im*Z_im;
if(Z_re2 + Z_im2 > 4)
{
isInside = false;
break;
}
Z_im = 2*Z_re*Z_im + c_im;
Z_re = Z_re2 - Z_im2 + c_re;
}
if(isInside) { putpixel(x, y); }
}
}
Thanks!
This is not a scientific answer but a one with common sense. In theory, to decide whether a point belongs to the Mandelbrot set or not, you should iterate infinitely, and check if the value ever reaches Infinity. This is practically useless so we make assumptions:
We iterate only 50 times
We check that iteration value ever gets larger than 2
When you zoom into a Mandelbrot set, the second assumption remains valid. However zooming means increasing the significant fractional digits of the point coordinates.
Say you start with (0.4,-0.2i).
Iterating over and over this value increases the digits used, but won't lose significant digits. Now when your point coordinate looks such: (0.00000000045233452235, -0.00000000000943452634626i) to check if that point is in the set you need much more iteration to see if that iteration would ever reach 2 not to mention that if you use some kind of Float type, you will lose significant digits at some zoom level and you'll have to switch to an arbitrary precision library.
Trying is your best friend :-) Calculate a set with a low iteration and a high iteration and subtract the second image from the first. You will always see change at the edges (where black pixels meet colored pixels), but if your zooming level is high (meaning: the point coordinates have a lot of fractional digits) you will get a different image.
You asked how zooming affects iterations and my typical zoom to iterations ratio is that if you zoom in to a 9th of the size I increase iterations by 1.7. A 9th of the size of course means that both width and height is divided by 3.
Making this more generic I actually use this in my code
Complex middle = << calculate from click in image >>
int zoomfactor = 3;
width = width / zoomfactor;
maxiter = (int)(maxiter * Math.Sqrt(zoomfactor));
minimum = new Complex(middle.Real - width, middle.Imaginary - width);
maximum = new Complex(middle.Real + width, middle.Imaginary + width);
I find that this relation between zoom and iterations works out pretty well, the details in the fractals still come well on deep zooms without getting too crazy on the iterations too fast.
How fast you want to zoom if your own preference, I like a zoomfactor of 3 but anything goes. The important thing is that you need to keep the relation between the zoomfactor and the increase in interations.
I am creating description label in which i want my description label height should be depend on the value rounded from the expression
NSString *description = [dic objectForKey:#"des"];
UIFont *font = [UIFont boldSystemFontOfSize:8];
CGSize desSize = [description sizeWithFont:font];
float hgt = desSize.width / userView.frame.size.width;
i want the value of hgt should be exact number like if value of output is 0.1 then it should give me value of hgt as 1, another like if value of o/p of devision is 3.3 then hgt should become as 4.
Please help me to solve this problem
Use ceil() to round up.
float hgt = ceil(desSize.width / userView.frame.size.width);
you want to use ceil(). that will round the number up to the nearest integral number.
fabs() – Find the absolute value or unsigned value in parentheses.
result = fabs(x); // fabs(-2.5) = 2.5
ceil() – To round up
i = ceil(x); // ceil(3.5) = 4
floor() – find the integer that is below the floating point value.
i = floor(x); // floor(4.2) = 4
pow() – raise a number to the power.
i = pow(x,y); //pow(4,2) = 16
sqrt() – Square root of a number.
i = sqrt(x); // sqrt(16) = 4
exp() – find the expoential value.
I am developing an app and want to round off values
i.e if the output is 4.8 I want to display 4.8
while if the output is 4.0 , I want to display 4
Also, it would be great if I could precisely round values : as in if value is 4.34 then round to 4.3 while if its 4.37 then round it to 4.4
One way to round floating point values is to just add 0.5 and then truncate the value.
double valueToRound = GetTheValueFromSomewhere();
double roundedValue = (double)((int)(valueToRound + 0.5));
This will round 1.4 down to 1.0 and 1.5 up to 2.0 for example. To round to other decimal places as you mentioned, simply multiply the initial value by 10, or 100, etc. use the same sort of code, and then divide the result by the same number and you'll get the same result at whatever decimal place you want.
Here's an example for rounding at an arbitrary precision.
double valueToRound = GetTheValueFromSomewhere();
int decimalPrecisionAtWhichToRound = 0;
double scale = 10^decimalPrecisionAtWhichToRound;
double tmp = valueToRound * scale;
tmp = (double)((int)(tmp + 0.5));
double roundedValue = tmp / scale;
So, if decimalPrecisionAtWhichToRound is set to 0 as in the above it'll round to the nearest whole integer. 1.4 will round to 1.0. 1.5 will round to 2.0.
If you set decimalPrecisionAtWhichToRound to 1, it would round to the nearest tenth. 1.45 would round to 1.5 and 1.43 would round to 1.4.
You need to first understand how to do rounding on paper, without someone showing you the code to do it. Write down some numbers and figure out how to round them.
To round to a specific decimal position you add half the value of that position and then truncate. Ie, 1.67 + 0.05 = 1.72 then truncate to 1.7.
But there are two tricky things in programming that aren't there when you do it on paper:
Knowing how to truncate -- There are several ways to do it while programming, but they're non-trivial.
Dealing with the fact that floating-point numbers are imprecise. Ie, there is no exact representation of, say, 1.7, but rather the two closest numbers are apt to be something like 1.69998 and 1.700001
For truncating the trick of multiplying the number by the appropriate power of 10 to produce an integer works pretty well. Eg, (1.67 + 0.05) * 10 = 17.2, then convert to int to get 17, then convert back to float and divide by 10 to get 1.7 (more or less). Or (if you're printing or displaying the value) just format the integer number with the decimal point inserted. (By formatting the integer value you don't have to deal with the problem of imprecise floating point representations.)
If you want to suppress trailing zeros it gets a bit trickier and you probably have to actually write some code -- format the number, then scan backwards and take off any trailing zeros up to the decimal point. (And take the decimal point too, if you wish.)
float number=17.125;
NSNumberFormatter *format = [[NSNumberFormatter alloc]init];
[format setNumberStyle:NSNumberFormatterDecimalStyle];
[format setRoundingMode:NSNumberFormatterRoundHalfUp];
[format setMaximumFractionDigits:2];
NSString *temp = [format stringFromNumber:[NSNumber numberWithFloat:number]];
NSLog(#"%#",temp);
double myNumber = 7.99;
NSString *formattedNumber = [NSString stringWithFormat:#"%.*f",
fmod(round(myNumber * 10), 10) ? 1 : 0, myNumber];
How to Get a Correct Value After Zoom?
How to Get A Correct Scale Number After Zoom?
How to put Yaxis another side like the following photo
The problem I am having now is I can get correct Y value if touch the Chart, then you can get a correct Y Value, but if i zoom in, the value will become wrong!
I try to use this function to get the scale
-(BOOL)plotSpace:(CPTPlotSpace*)space shouldScaleBy:(CGFloat)interactionScale aboutPoint:(CGPoint)interactionPoint
But the scale number i get is not correct
Everyone please help!!!
This is the correct Screen Capture:
and this is the wrong one
-(BOOL)plotSpace:(CPTPlotSpace*)space shouldScaleBy:(CGFloat)interactionScale aboutPoint:(CGPoint)interactionPoint
{
NSDecimal plotPoint[2];
CPTXYPlotSpace *xySpace = (CPTXYPlotSpace *)space;
[xySpace plotPoint:plotPoint forPlotAreaViewPoint:interactionPoint];
NSLog(#"Dragg Event Scale: %f",real_scaleValue);
NSString *showingcontent_testing=[NSString stringWithFormat:#"Scale Y VAlue $:%f",
([[NSDecimalNumber decimalNumberWithDecimal:plotPoint[CPTCoordinateY]] doubleValue])];
return YES;
}
// This method is call when user touch & drag on the plot space.
- (BOOL)plotSpace:(CPTPlotSpace *)space shouldHandlePointingDeviceDraggedEvent:(id)event atPoint:(CGPoint)point
{
if( ((self.view.frame.size.height- point.y+20)<324)
&&((self.view.frame.size.height- point.y+20)>22))
{
NSDecimal plotPoint[2];
CPTXYPlotSpace *xySpace = (CPTXYPlotSpace *)space;
[xySpace plotPoint:plotPoint forPlotAreaViewPoint:point];
NSLog(#"Dragg Event Scale: %f",real_scaleValue);
showingcontent=[NSString stringWithFormat:#"Price $:%f",
(([[NSDecimalNumber decimalNumberWithDecimal:plotPoint[CPTCoordinateY]] doubleValue])*real_scaleValue)-13.42623-5.573770-0.163934];
current_price_show.text=showingcontent;
[self.view bringSubviewToFront:current_price_show];
CGRect separator_line_frame = separator_line.frame;
separator_line_frame.origin.y =self.view.frame.size.height- point.y+20;
// final y destination
separator_line.frame = separator_line_frame;
[self.view bringSubviewToFront:separator_line];
}
return YES;
}
The problem you're having with converting values at different plot scales is a rounding issue. You get the touch point in pixels. There are only a finite number of pixels in any plot range. If you zoom in, you increase the number of pixels between any two given values.
To move the y-axis to the right, reduce the right padding and make sure the hosting view fills the width of the screen.
have an app that finds your GPS location successfully, but I need to be able to compare that GPS with a list of GPS locations, if both are the same , then you get a bonus.
I thought I had it working, but it seems not.
I have 'newLocation' as the location where you are, I think the problem is that I need to be able to seperate the long and lat data of newLocation.
So far ive tried this:
NSString *latitudeVar = [[NSString alloc] initWithFormat:#"%g°", newLocation.coordinate.latitude];
NSString *longitudeVar = [[NSString alloc] initWithFormat:#"%g°", newLocation.coordinate.longitude];
An example of the list of GPS locations:
location:(CLLocation*)newLocation;
CLLocationCoordinate2D bonusOne;
bonusOne.latitude = 37.331689;
bonusOne.longitude = -122.030731;
and then
if (latitudeVar == bonusOne.latitude && longitudeVar == bonusOne.longitude) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"infinite loop firday" message:#"infloop" delegate:nil cancelButtonTitle:#"Stinky" otherButtonTitles:nil ];
[alert show];
[alert release];
}
this comes up with an error 'invalid operands to binary == have strut NSstring and CLlocationDegrees'
Any thoughts?
Generally you should be careful with comparing floating point numbers directly. Because of the way they are defined, the internal value may not be exactly as you initialize them meaning that they will very seldom be identical. Instead you should check to see if the difference between them are below a certain threshold, for instance
if(fabs(latitude1 - latitude2) <= 0.000001)
...
Another option could be to check how far the person is from the desired location by calculating the distance. This could also take into account the fact that the coordinate from the GPS is not exactly correct, but might differ up to like 10 meters even under good conditions:
CLLocation *loc1 = [[CLLocation alloc] initWithLatitude:lat1 longitude:lon1];
double distance = [loc1 getDistanceFrom:position2];
if(distance <= 10)
...
Claus
Why are you not comparing bonusOne.latitude and newLocation.coordinate.latitude directly? You are conevrting a floating point number to a string and then comparing it to a floating point number which is why you are getting that error.
Also, given that the gps unit tends to jump around a bit, you probably want to either
a: measure the distance between bonusOne and newLocation.coordinate (using the pythagorean theorem for the hypotenuse of triangles, no reason to us something more accurate than that when on this small a scale. if you're feeling picky, use the map kit distance measuring function) and specify its less than a certain amount.
b: round the latitude and longitude off to a certain number of digits so that being, within, say 100 feet would work.
Either of those will work better for you than relying on the two floats to be equal, which is both generally problematic in software and specifically an issue when the device you're measuring is has a high noise level.