I have a wide variety of locations stored in my persistent object that contain latitudes and longitudes in double(43.7389, 7.42577) format. I need to be able to grab the user's latitude and longitude and select all items within, say 1 mile. Walking distance.
I have done this in PHP so I snagged my PHP code and transferred it to Java, where everything plugged in fine until I figured out J2ME doesn't support atan2(double, double). So, after some searching, I find a small snippet of code that is supposed to be a substitute for atan2. Here is the code:
public double atan2(double y, double x) {
double coeff_1 = Math.PI / 4d;
double coeff_2 = 3d * coeff_1;
double abs_y = Math.abs(y)+ 1e-10f;
double r, angle;
if (x >= 0d) {
r = (x - abs_y) / (x + abs_y);
angle = coeff_1;
} else {
r = (x + abs_y) / (abs_y - x);
angle = coeff_2;
}
angle += (0.1963f * r * r - 0.9817f) * r;
return y < 0.0f ? -angle : angle;
}
I am getting odd results from this. My min and max latitude and longitudes are coming back as incredibly low numbers that can't possibly be right. Like 0.003785746 when I am expecting something closer to the original lat and long values (43.7389, 7.42577).
Since I am no master of advanced math, I don't really know what to look for here. Perhaps someone else may have an answer.
Here is my complete code:
package store_finder;
import java.util.Vector;
import javax.microedition.location.Criteria;
import javax.microedition.location.Location;
import javax.microedition.location.LocationException;
import javax.microedition.location.LocationListener;
import javax.microedition.location.LocationProvider;
import javax.microedition.location.QualifiedCoordinates;
import net.rim.blackberry.api.invoke.Invoke;
import net.rim.blackberry.api.invoke.MapsArguments;
import net.rim.device.api.system.Bitmap;
import net.rim.device.api.system.Display;
import net.rim.device.api.ui.Color;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.Graphics;
import net.rim.device.api.ui.Manager;
import net.rim.device.api.ui.component.BitmapField;
import net.rim.device.api.ui.component.RichTextField;
import net.rim.device.api.ui.component.SeparatorField;
import net.rim.device.api.ui.container.HorizontalFieldManager;
import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.ui.container.VerticalFieldManager;
public class nearBy extends MainScreen {
private HorizontalFieldManager _top;
private VerticalFieldManager _middle;
private int horizontalOffset;
private final static long animationTime = 300;
private long animationStart = 0;
private double latitude = 43.7389;
private double longitude = 7.42577;
private int _interval = -1;
private double max_lat;
private double min_lat;
private double max_lon;
private double min_lon;
private double latitude_in_degrees;
private double longitude_in_degrees;
public nearBy()
{
super();
horizontalOffset = Display.getWidth();
_top = new HorizontalFieldManager(Manager.USE_ALL_WIDTH | Field.FIELD_HCENTER)
{
public void paint(Graphics gr)
{
Bitmap bg = Bitmap.getBitmapResource("bg.png");
gr.drawBitmap(0, 0, Display.getWidth(), Display.getHeight(), bg, 0, 0);
subpaint(gr);
}
};
_middle = new VerticalFieldManager()
{
public void paint(Graphics graphics)
{
graphics.setBackgroundColor(0xFFFFFF);
graphics.setColor(Color.BLACK);
graphics.clear();
super.paint(graphics);
}
protected void sublayout(int maxWidth, int maxHeight)
{
int displayWidth = Display.getWidth();
int displayHeight = Display.getHeight();
super.sublayout( displayWidth, displayHeight);
setExtent( displayWidth, displayHeight);
}
};
add(_top);
add(_middle);
Bitmap lol = Bitmap.getBitmapResource("logo.png");
BitmapField lolfield = new BitmapField(lol);
_top.add(lolfield);
Criteria cr= new Criteria();
cr.setCostAllowed(true);
cr.setPreferredResponseTime(60);
cr.setHorizontalAccuracy(5000);
cr.setVerticalAccuracy(5000);
cr.setAltitudeRequired(true);
cr.isSpeedAndCourseRequired();
cr.isAddressInfoRequired();
try{
LocationProvider lp = LocationProvider.getInstance(cr);
if( lp!=null ){
lp.setLocationListener(new LocationListenerImpl(), _interval, 1, 1);
}
}
catch(LocationException le)
{
add(new RichTextField("Location exception "+le));
}
//_middle.add(new RichTextField("this is a map " + Double.toString(latitude) + " " + Double.toString(longitude)));
int lat = (int) (latitude * 100000);
int lon = (int) (longitude * 100000);
String document = "<location-document>" + "<location lon='" + lon + "' lat='" + lat + "' label='You are here' description='You' zoom='0' />" + "<location lon='742733' lat='4373930' label='Hotel de Paris' description='Hotel de Paris' address='Palace du Casino' postalCode='98000' phone='37798063000' zoom='0' />" + "</location-document>";
// Invoke.invokeApplication(Invoke.APP_TYPE_MAPS, new MapsArguments( MapsArguments.ARG_LOCATION_DOCUMENT, document));
_middle.add(new SeparatorField());
surroundingVenues();
_middle.add(new RichTextField("max lat: " + max_lat));
_middle.add(new RichTextField("min lat: " + min_lat));
_middle.add(new RichTextField("max lon: " + max_lon));
_middle.add(new RichTextField("min lon: " + min_lon));
}
private void surroundingVenues()
{
double point_1_latitude_in_degrees = latitude;
double point_1_longitude_in_degrees= longitude;
// diagonal distance + error margin
double distance_in_miles = (5 * 1.90359441) + 10;
getCords (point_1_latitude_in_degrees, point_1_longitude_in_degrees, distance_in_miles, 45);
double lat_limit_1 = latitude_in_degrees;
double lon_limit_1 = longitude_in_degrees;
getCords (point_1_latitude_in_degrees, point_1_longitude_in_degrees, distance_in_miles, 135);
double lat_limit_2 = latitude_in_degrees;
double lon_limit_2 = longitude_in_degrees;
getCords (point_1_latitude_in_degrees, point_1_longitude_in_degrees, distance_in_miles, -135);
double lat_limit_3 = latitude_in_degrees;
double lon_limit_3 = longitude_in_degrees;
getCords (point_1_latitude_in_degrees, point_1_longitude_in_degrees, distance_in_miles, -45);
double lat_limit_4 = latitude_in_degrees;
double lon_limit_4 = longitude_in_degrees;
double mx1 = Math.max(lat_limit_1, lat_limit_2);
double mx2 = Math.max(lat_limit_3, lat_limit_4);
max_lat = Math.max(mx1, mx2);
double mm1 = Math.min(lat_limit_1, lat_limit_2);
double mm2 = Math.min(lat_limit_3, lat_limit_4);
min_lat = Math.max(mm1, mm2);
double mlon1 = Math.max(lon_limit_1, lon_limit_2);
double mlon2 = Math.max(lon_limit_3, lon_limit_4);
max_lon = Math.max(mlon1, mlon2);
double minl1 = Math.min(lon_limit_1, lon_limit_2);
double minl2 = Math.min(lon_limit_3, lon_limit_4);
min_lon = Math.max(minl1, minl2);
//$qry = "SELECT DISTINCT zip.zipcode, zip.latitude, zip.longitude, sg_stores.* FROM zip JOIN store_finder AS sg_stores ON sg_stores.zip=zip.zipcode WHERE zip.latitude<=$lat_limit_max AND zip.latitude>=$lat_limit_min AND zip.longitude<=$lon_limit_max AND zip.longitude>=$lon_limit_min";
}
private void getCords(double point_1_latitude, double point_1_longitude, double distance, int degs)
{
double m_EquatorialRadiusInMeters = 6366564.86;
double m_Flattening=0;
double distance_in_meters = distance * 1609.344 ;
double direction_in_radians = Math.toRadians( degs );
double eps = 0.000000000000005;
double r = 1.0 - m_Flattening;
double point_1_latitude_in_radians = Math.toRadians( point_1_latitude );
double point_1_longitude_in_radians = Math.toRadians( point_1_longitude );
double tangent_u = (r * Math.sin( point_1_latitude_in_radians ) ) / Math.cos( point_1_latitude_in_radians );
double sine_of_direction = Math.sin( direction_in_radians );
double cosine_of_direction = Math.cos( direction_in_radians );
double heading_from_point_2_to_point_1_in_radians = 0.0;
if ( cosine_of_direction != 0.0 )
{
heading_from_point_2_to_point_1_in_radians = atan2( tangent_u, cosine_of_direction ) * 2.0;
}
double cu = 1.0 / Math.sqrt( ( tangent_u * tangent_u ) + 1.0 );
double su = tangent_u * cu;
double sa = cu * sine_of_direction;
double c2a = ( (-sa) * sa ) + 1.0;
double x= Math.sqrt( ( ( ( 1.0 /r /r ) - 1.0 ) * c2a ) + 1.0 ) + 1.0;
x= (x- 2.0 ) / x;
double c= 1.0 - x;
c= ( ( (x * x) / 4.0 ) + 1.0 ) / c;
double d= ( ( 0.375 * (x * x) ) -1.0 ) * x;
tangent_u = distance_in_meters /r / m_EquatorialRadiusInMeters /c;
double y= tangent_u;
boolean exit_loop = false;
double cosine_of_y = 0.0;
double cz = 0.0;
double e = 0.0;
double term_1 = 0.0;
double term_2 = 0.0;
double term_3 = 0.0;
double sine_of_y = 0.0;
while( exit_loop != true )
{
sine_of_y = Math.sin(y);
cosine_of_y = Math.cos(y);
cz = Math.cos( heading_from_point_2_to_point_1_in_radians + y);
e = (cz * cz * 2.0 ) - 1.0;
c = y;
x = e * cosine_of_y;
y = (e + e) - 1.0;
term_1 = ( sine_of_y * sine_of_y * 4.0 ) - 3.0;
term_2 = ( ( term_1 * y * cz * d) / 6.0 ) + x;
term_3 = ( ( term_2 * d) / 4.0 ) -cz;
y= ( term_3 * sine_of_y * d) + tangent_u;
if ( Math.abs(y - c) > eps )
{
exit_loop = false;
}
else
{
exit_loop = true;
}
}
heading_from_point_2_to_point_1_in_radians = ( cu * cosine_of_y * cosine_of_direction ) - ( su * sine_of_y );
c = r * Math.sqrt( ( sa * sa ) + ( heading_from_point_2_to_point_1_in_radians * heading_from_point_2_to_point_1_in_radians ) );
d = ( su * cosine_of_y ) + ( cu * sine_of_y * cosine_of_direction );
double point_2_latitude_in_radians = atan2(d, c);
c = ( cu * cosine_of_y ) - ( su * sine_of_y * cosine_of_direction );
x = atan2( sine_of_y * sine_of_direction, c);
c = ( ( ( ( ( -3.0 * c2a ) + 4.0 ) * m_Flattening ) + 4.0 ) * c2a * m_Flattening ) / 16.0;
d = ( ( ( (e * cosine_of_y * c) + cz ) * sine_of_y * c) + y) * sa;
double point_2_longitude_in_radians = ( point_1_longitude_in_radians + x) - ( ( 1.0 - c) * d * m_Flattening );
heading_from_point_2_to_point_1_in_radians = atan2( sa, heading_from_point_2_to_point_1_in_radians ) + Math.PI;
latitude_in_degrees = Math.toRadians( point_2_latitude_in_radians );
longitude_in_degrees = Math.toRadians( point_2_longitude_in_radians );
}
public double atan2(double y, double x) {
double coeff_1 = Math.PI / 4d;
double coeff_2 = 3d * coeff_1;
double abs_y = Math.abs(y)+ 1e-10f;
double r, angle;
if (x >= 0d) {
r = (x - abs_y) / (x + abs_y);
angle = coeff_1;
} else {
r = (x + abs_y) / (abs_y - x);
angle = coeff_2;
}
angle += (0.1963f * r * r - 0.9817f) * r;
return y < 0.0f ? -angle : angle;
}
private Vector fetchVenues(double max_lat, double min_lat, double max_lon, double min_lon)
{
return new Vector();
}
private class LocationListenerImpl implements LocationListener {
public void locationUpdated(LocationProvider provider, Location location) {
if(location.isValid()) {
nearBy.this.longitude = location.getQualifiedCoordinates().getLongitude();
nearBy.this.latitude = location.getQualifiedCoordinates().getLatitude();
//double altitude = location.getQualifiedCoordinates().getAltitude();
//float speed = location.getSpeed();
}
}
public void providerStateChanged(LocationProvider provider, int newState) {
// MUST implement this. Should probably do something useful with it as well.
}
}
}
please excuse the mess. I have the user lat long hard coded since I do not have GPS functional yet. You can see the SQL query commented out to know how I plan on using the min and max lat and long values.
Any help is appreciated.
Thanks
If you are targeting OS 4.6 and above, an atan2 method was added to the MathUtilities class. Otherwise, I'd suggest just Googling around until you find an implementation that works. As Matthew Flaschen suggested, compare the results to what you normally get with a standard atan2 function in J2SE or PHP.
atan2 isn't hard to implement. The definition of what to return based on the 2 arguments is given in the definition. Technically there might be a tiny bit of precision loss from doing the division yourself compared with what might be technically achieveable, but I don't think it will ever matter in rpactice.
Related
How to get XY value from ct.
Ex: ct = 217, I want to get x="0.3127569", y= "0.32908".
I'm able to convert XY value into ct value using this below code.
float R1 = [hue[0] floatValue];
float S1 = [hue[1] floatValue];
float result = ((R1-0.332)/(S1-0.1858));
NSString *ctString = [NSString stringWithFormat:#"%f", ((-449*result*result*result)+(3525*result*result)-(6823.3*result)+(5520.33))];
float micro2 = (float) (1 / [ctString floatValue] * 1000000);
NSString *ctValue = [NSString stringWithFormat:#"%f", micro2];
ctValue = [NSString stringWithFormat:#"%d", [ctValue intValue]];
if ([ctValue integerValue] < 153) {
ctValue = [NSString stringWithFormat:#"%d", 153];
}
Now I want reverse value, which is from ct to XY.
On Phillips HUE
2000K maps to 500 and 6500K maps to 153 given in ct as color temperature but can be thought as actually being Mired.
Mired means micro reciprocal degree wikipedia.
ct is possibly used because it is not 100% Mired. Quite sure Phillips uses a lookup table as a lot CIE algorithms do because there are just 347 indexes in this range from 153 to 500.
The following is not a solution, it's just simple concept of a lookup table.
And as the CIE 1931 xy to CCT Formula by McCamy suggests found here it is possible to use a lookup table to find x and y as well.
A table can be found here but i am not sure if that is the right lookup table.
reminder so the following is not a solution, but to find an reverse algo the code may help.
typedef int Kelvin;
typedef float Mired;
Mired linearMiredByKelvin(Kelvin k) {
if (k==0) return 0;
return 1000000.0/k;
}
-(void)mired {
Mired miredMin = 2000.0/13.0; // 153,84 = reciprocal 6500K
Mired miredMax = 500.0; // 500,00 = reciprocal 2000K
Mired lookupMiredByKelvin[6501]; //max 6500 Kelvin + 1 safe index
//Kelvin lookupKelvinByMired[501]; //max 500 Mired + 1 safe index
// dummy stuff, empty unused table space
for (Kelvin k = 0; k < 2000; k++) {
lookupMiredByKelvin[k] = 0;
}
//for (Mired m = 0.0; m < 154.0; m++) {
// lookupKelvinByMired[(int)m] = 0;
//}
for (Kelvin k=2000; k<6501; k++) {
Mired linearMired = linearMiredByKelvin(k);
float dimm = (linearMired - miredMin) / ( miredMax - miredMin);
Kelvin ct = (Kelvin)(1000000.0/(dimm*miredMax - dimm*miredMin + miredMin));
lookupMiredByKelvin[k] = linearMiredByKelvin(ct);
if (k==2000 || k==2250 || k==2500 || k==2750 ||
k==3000 || k==3250 || k==3500 || k==3750 ||
k==4000 || k==4250 || k==4500 || k==4750 ||
k==5000 || k==5250 || k==5500 || k==5750 ||
k==6000 || k==6250 || k==6500 || k==6501 )
fprintf(stderr,"%d %f %f\n",ct, dimm, lookupMiredByKelvin[k]);
}
}
at least this is proof that x and y will not sit on a simple vector.
CCT means correlated colour temperature and like the implementation in the question shows can be calculated via n= (x-0.3320)/(0.1858-y); CCT = 437*n^3 + 3601*n^2 + 6861*n + 5517. (after McCamy)
but a cct=217 is out of range of above link'ed lookup table.
following the idea in this git-repo from colour-science
and ported to C it could look like..
void CCT_to_xy_CIE_D(float cct) {
//if (CCT < 4000 || CCT > 25000) fprintf(stderr, "Correlated colour temperature must be in domain, unpredictable results may occur! \n");
float x = calculateXviaCCT(cct);
float y = calculateYviaX(x);
NSLog(#"cct=%f x%f y%f",cct,x,y);
}
float calculateXviaCCT(float cct) {
float cct_3 = pow(cct, 3); //(cct*cct*cct);
float cct_2 = pow(cct, 2); //(cct*cct);
if (cct<=7000)
return -4.607 * pow(10, 9) / cct_3 + 2.9678 * pow(10, 6) / cct_2 + 0.09911 * pow(10, 3) / cct + 0.244063;
return -2.0064 * pow(10, 9) / cct_3 + 1.9018 * pow(10, 6) / cct_2 + 0.24748 * pow(10, 3) / cct + 0.23704;
}
float calculateYviaX(float x) {
return -3.000 * pow(x, 2) + 2.870 * x - 0.275;
}
CCT_to_xy_CIE_D(6504.38938305); //proof of concept
//cct=6504.389160 x0.312708 y0.329113
CCT_to_xy_CIE_D(217.0);
//cct=217.000000 x-387.131073 y-450722.750000
// so for sure Phillips hue temperature given in ct between 153-500 is not a good starting point
//but
CCT_to_xy_CIE_D(2000.0);
//cct=2000.000000 x0.459693 y0.410366
this seems to work fine with CCT between 2000 and 25000, but maybe confusing is CCT is given in Kelvin here.
EDIT
This has been through so many revisions and ideas. To keep it simple I edited most of that out and just give you the final result.
This fits your function perfectly except for a region in the middle (temp from 256 to 316) where it deviates a bit.
The problem with your function is that it has approximately infinite solutions, so to solve it nicely you need more constraints, but what? Ol Sen's reference https://www.waveformlighting.com/tech/calculate-color-temperature-cct-from-cie-1931-xy-coordinates discusses it in some detail and then mentions that you want a Duv to be zero. It also gives a way to calculate Duv and so I added that to my optimiser and voila!
Nice and smooth. The optimiser now solves for x and y that both satisfies your function and also minimises Duv.
To get it to work nicely I had to scale Duv quite a bit. That page mentions that Duv should be very small so I think this is a good thing. Also, as the temp increases the scaling should to help the optimiser.
Below prints from 153 to 500.
#import <Foundation/Foundation.h>
// Function taken from your code
// Simplified a bit
int ctFuncI ( float x, float y )
{
// float R1 = [hue[0] floatValue];
// float S1 = [hue[1] floatValue];
float result = (x-0.332)/(y-0.1858);
float cubic = - 449 * result * result * result + 3525 * result * result - 6823.3 * result + 5520.33;
float micro2 = 1 / cubic * 1000000;
int ct = ( int )( micro2 + 0.5 );
if ( ct < 153 )
{
ct = 153;
}
return ct;
}
// Need this
// Float version of your code
float ctFuncF ( float x, float y )
{
// float R1 = [hue[0] floatValue];
// float S1 = [hue[1] floatValue];
float result = (x-0.332)/(y-0.1858);
float cubic = - 449 * result * result * result + 3525 * result * result - 6823.3 * result + 5520.33;
return 1000000 / cubic;
}
// We need an additional constraint
// https://www.waveformlighting.com/tech/calculate-duv-from-cie-1931-xy-coordinates
// Given x, y calculate Duv
// We want this to be 0
float duv ( float x, float y )
{
float f = 1 / ( - 2 * x + 12 * y + 3 );
float u = 4 * x * f;
float v = 6 * y * f;
// I'm typing float but my heart yells double
float k6 = -0.00616793;
float k5 = 0.0893944;
float k4 = -0.5179722;
float k3 = 1.5317403;
float k2 = -2.4243787;
float k1 = 1.925865;
float k0 = -0.471106;
float du = u - 0.292;
float dv = v - 0.24;
float Lfp = sqrt ( du * du + dv * dv );
float a = acos( du / Lfp );
float Lbb = k6 * pow ( a, 6 ) + k5 * pow( a, 5 ) + k4 * pow( a, 4 ) + k3 * pow( a, 3 ) + k2 * pow(a,2) + k1 * a + k0;
return Lfp - Lbb;
}
// Solver!
// Returns iterations
int ctSolve ( int ct, float * x, float * y )
{
int iter = 0;
float dx = 0.001;
float dy = 0.001;
// Error
// Note we scale duv a bit
// Seems the higher the temp, the higher scale we require
// Also note the jump at 255 ...
float s = 1000 * ( ct > 255 ? 10 : 1 );
float d = fabs( ctFuncF ( * x, * y ) - ct ) + s * fabs( duv ( * x, * y ) );
// Approx
while ( d > 0.5 && iter < 250 )
{
iter ++;
dx *= fabs( ctFuncF ( * x + dx, * y ) - ct ) + s * fabs( duv ( * x + dx, * y ) ) < d ? 1.2 : - 0.5;
dy *= fabs( ctFuncF ( * x, * y + dy ) - ct ) + s * fabs( duv ( * x, * y + dy ) ) < d ? 1.2 : - 0.5;
* x += dx;
* y += dy;
d = fabs( ctFuncF ( * x, * y ) - ct ) + s * fabs( duv ( * x, * y ) );
}
return iter;
}
// Tester
int main(int argc, const char * argv[]) {
#autoreleasepool
{
// insert code here...
NSLog(#"Hello, World!");
float x, y;
int sume = 0;
int sumi = 0;
for ( int ct = 153; ct <= 500; ct ++ )
{
// Initial guess
x = 0.4;
y = 0.4;
// Approx
int iter = ctSolve ( ct, & x, & y );
// CT and error
int ctEst = ctFuncI ( x, y );
int e = ct - ctEst;
// Diagnostics
sume += abs ( e );
sumi += iter;
// Print out results
NSLog ( #"want ct = %d x = %f y = %f got ct %d in %d iter error %d", ct, x, y, ctEst, iter, e );
}
NSLog ( #"Sum of abs errors %d iterations %d", sume, sumi );
}
return 0;
}
To use it, do as below.
// To call it, init x and y to some guess
float x = 0.4;
float y = 0.4;
// Then call solver with your temp
int ct = 217;
ctSolve( ct, & x, & y ); // Note you pass references to x and y
// Done, answer now in x and y
a bit more compact answer and functions to convert back and forth..
beware there are rounding issues because McCamy's formula relies and mathematical assumptions. And so the backward calculation does also.
if you want to find more results search directly for "n= (x-0.3320)/(0.1858-y); CCT = 437*n^3 + 3601*n^2 + 6861*n + 5517" there are plenty of different methods to convert back and forth.
so here Phillips-Hue #[#x,#y] to Phillips-ct,Phillips-ct to CCT, CCT to x,y
void CCT_to_xy_CIE_D(float cct) {
//if (CCT < 4000 || CCT > 25000) fprintf(stderr, "Correlated colour temperature must be in domain, unpredictable results may occur! \n");
float x = calculateXviaCCT(cct);
float y = calculateYviaX(x);
fprintf(stderr,"cct=%f x%f y%f",cct,x,y);
}
float calculateXviaCCT(float cct) {
float cct_3 = pow(cct, 3); //(cct*cct*cct);
float cct_2 = pow(cct, 2); //(cct*cct);
if (cct<=7000.0)
return -4.607 * pow(10, 9) / cct_3 + 2.9678 * pow(10, 6) / cct_2 + 0.09911 * pow(10, 3) / cct + 0.244063;
return -2.0064 * pow(10, 9) / cct_3 + 1.9018 * pow(10, 6) / cct_2 + 0.24748 * pow(10, 3) / cct + 0.23704;
}
float calculateYviaX(float x) {
return -3.000 * x*x + 2.870 * x - 0.275;
}
int calculate_PhillipsHueCT_withCCT(float cct) {
if (cct>6500.0) return 2000.0/13.0;
if (cct<2000.0) return 500.0;
//return (float) (1 / cct * 1000000); // same as..
return 1000000 / cct;
}
float calculate_CCT_withPhillipsHueCT(float ct) {
if (ct == 0.0) return 0.0;
return 1000000 / ct;
}
float calculate_CCT_withHueXY(NSArray *hue) {
float x = [hue[0] floatValue]; //R1
float y = [hue[1] floatValue]; //S1
//x = 0.312708; y = 0.329113;
float n = (x-0.3320)/(0.1858-y);
float cct = 437.0*n*n*n + 3601.0*n*n + 6861.0*n + 5517.0;
return cct;
}
// MC Camy formula n=(x-0.3320)/(0.1858-y); cct = 437*n^3 + 3601*n^2 + 6861*n + 5517;
-(void)testPhillipsHueCt_backAndForth {
NSArray *hue = #[#(0.312708),#(0.329113)];
float cct = calculate_CCT_withHueXY(hue);
float ct = calculate_PhillipsHueCT_withCCT(cct);
NSLog(#"ct %f",ct);
CCT_to_xy_CIE_D(cct); // check
CCT_to_xy_CIE_D(6504.38938305); //proof of concept
CCT_to_xy_CIE_D(2000.0);
CCT_to_xy_CIE_D(calculate_CCT_withPhillipsHueCT(217.0));
}
I have created one polygon on map with some set of coordinates.
I need help regarding making one buffered polygon with some given distance outside of original polygon border.
so what i need a method with such algorithm in which i pass set of coordinates as input and should get buffered set of coordinates as output.
I tried to achieve this by using arcgis library for ios with bufferGeometry method of AGSGeometryEngine but problem is, this is tightly coupled and only will work their GIS Map but I am using Mapbox which is different Map. So I want one generic method which can resolve my problem independent to map.
The solution of #Ravikant Paudel though comprehensive didn't work for me so I have implemented the approach myself.
Also, I implemented the approach in kotlin and adding it here so that someone else who is facing a similar problem will find it helpful.
Approach:
Find the angle of the angle bisector theta for every vertice of the polygon.
Draw a circle with radius = bufferedDistance / sin(angleBisctorTheta)
Find intersections of the circle and angle bisector.
Out of the 2 intersection points the one inside the polygon will give you the buffered vertice for the shrunk polygon and the outside point for the buffered polygon.
This approach does not account for the corner cases in which both points somehow fall inside or outside the polygon -> in which case the buffered polygon formed will be malformed.
Code:
private fun computeAngleBisectorTheta(
prevLatLng: LatLng,
currLatLng: LatLng,
nextLatLng: LatLng
): Double {
var phiBisector = 0.0
try {
val aPrime = getDeltaPrimeVector(prevLatLng, currLatLng)
val cPrime = getDeltaPrimeVector(nextLatLng, currLatLng)
val thetaA = atan2(aPrime[1], aPrime[0])
val thetaC = atan2(cPrime[1], cPrime[0])
phiBisector = (thetaA + thetaC) / 2.0
} catch (e: Exception) {
logger.error("[Error] in computeAngleBisectorSlope: $e")
}
return phiBisector
}
private fun getDeltaPrimeVector(
aLatLng: LatLng,
bLatLng: LatLng
): ArrayList<Double> {
val arrayList: ArrayList<Double> = ArrayList<Double>(2)
try {
val aX = convertToXY(aLatLng.latitude)
val aY = convertToXY(aLatLng.longitude)
val bX = convertToXY(bLatLng.latitude)
val bY = convertToXY(bLatLng.longitude)
arrayList.add((aX - bX))
arrayList.add((aY - bY))
} catch (e: Exception) {
logger.error("[Error] in getDeltaPrimeVector: $e")
}
return arrayList
}
private fun convertToXY(coordinate: Double) =
EARTH_RADIUS * toRad(coordinate)
private fun convertToLatLngfromXY(coordinate: Double) =
toDegrees(coordinate / EARTH_RADIUS)
private fun computeBufferedVertices(
angle: Double, bufDis: Int,
centerLatLng: LatLng
): ArrayList<LatLng> {
var results = ArrayList<LatLng>()
try {
val distance = bufDis / sin(angle)
var slope = tan(angle)
var inverseSlopeSquare = sqrt(1 + slope * slope * 1.0)
var distanceByInverseSlopeSquare = distance / inverseSlopeSquare
var slopeIntoDistanceByInverseSlopeSquare = slope * distanceByInverseSlopeSquare
var p1X: Double = convertToXY(centerLatLng.latitude) + distanceByInverseSlopeSquare
var p1Y: Double =
convertToXY(centerLatLng.longitude) + slopeIntoDistanceByInverseSlopeSquare
var p2X: Double = convertToXY(centerLatLng.latitude) - distanceByInverseSlopeSquare
var p2Y: Double =
convertToXY(centerLatLng.longitude) - slopeIntoDistanceByInverseSlopeSquare
val tempLatLng1 = LatLng(convertToLatLngfromXY(p1X), convertToLatLngfromXY(p1Y))
results.add(tempLatLng1)
val tempLatLng2 = LatLng(convertToLatLngfromXY(p2X), convertToLatLngfromXY(p2Y))
results.add(tempLatLng2)
} catch (e: Exception) {
logger.error("[Error] in computeBufferedVertices: $e")
}
return results
}
private fun getVerticesOutsidePolygon(
verticesArray: ArrayList<LatLng>,
polygon: ArrayList<LatLng>
): LatLng {
if (isPointInPolygon(
verticesArray[0].latitude,
verticesArray[0].longitude,
polygon
)
) {
if (sPointInPolygon(
verticesArray[1].latitude,
verticesArray[1].longitude,
polygon
)
) {
logger.error("[ERROR] Malformed polygon! Both Vertices are inside the polygon! $verticesArray")
} else {
return verticesArray[1]
}
} else {
if (PolygonGeofenceHelper.isPointInPolygon(
verticesArray[1].latitude,
verticesArray[1].longitude,
polygon
)
) {
return verticesArray[0]
} else {
logger.error("[ERROR] Malformed polygon! Both Vertices are outside the polygon!: $verticesArray")
}
}
//returning a vertice anyway because there is no fall back policy designed if both vertices are inside or outside the polygon
return verticesArray[0]
}
private fun toRad(angle: Double): Double {
return angle * Math.PI / 180
}
private fun toDegrees(radians: Double): Double {
return radians * 180 / Math.PI
}
private fun getVerticesInsidePolygon(
verticesArray: ArrayList<LatLng>,
polygon: ArrayList<LatLng>
): LatLng {
if (isPointInPolygon(
verticesArray[0].latitude,
verticesArray[0].longitude,
polygon
)
) {
if (isPointInPolygon(
verticesArray[1].latitude,
verticesArray[1].longitude,
polygon
)
) {
logger.error("[ERROR] Malformed polygon! Both Vertices are inside the polygon! $verticesArray")
} else {
return verticesArray[0]
}
} else {
if (PolygonGeofenceHelper.isPointInPolygon(
verticesArray[1].latitude,
verticesArray[1].longitude,
polygon
)
) {
return verticesArray[1]
} else {
logger.error("[ERROR] Malformed polygon! Both Vertices are outside the polygon!: $verticesArray")
}
}
//returning a vertice anyway because there is no fall back policy designed if both vertices are inside or outside the polygon
return LatLng(0.0, 0.0)
}
fun getBufferedPolygon(
polygon: ArrayList<LatLng>,
bufferDistance: Int,
isOutside: Boolean
): ArrayList<LatLng> {
var bufferedPolygon = ArrayList<LatLng>()
var isBufferedPolygonMalformed = false
try {
for (i in 0 until polygon.size) {
val prevLatLng: LatLng = polygon[if (i - 1 < 0) polygon.size - 1 else i - 1]
val centerLatLng: LatLng = polygon[i]
val nextLatLng: LatLng = polygon[if (i + 1 == polygon.size) 0 else i + 1]
val computedVertices =
computeBufferedVertices(
computeAngleBisectorTheta(
prevLatLng, centerLatLng, nextLatLng
), bufferDistance, centerLatLng
)
val latLng = if (isOutside) {
getVerticesOutsidePolygon(
computedVertices,
polygon
)
} else {
getVerticesInsidePolygon(
computedVertices,
polygon
)
}
if (latLng.latitude == 0.0 && latLng.longitude == 0.0) {
isBufferedPolygonMalformed = true
break
}
bufferedPolygon.add(latLng)
}
if (isBufferedPolygonMalformed) {
bufferedPolygon = polygon
logger.error("[Error] Polygon generated is malformed returning the same polygon: $polygon , $bufferDistance, $isOutside")
}
} catch (e: Exception) {
logger.error("[Error] in getBufferedPolygon: $e")
}
return bufferedPolygon
}
You'll need to pass an array of points present in the polygon in the code and the buffer distance the third param is to get the outside buffer or the inside buffer. (Note: I am assuming that the vertices in this list are adjacent to each other).
I have tried to keep this answer as comprehensive as possible. Please feel free to suggest any improvements or a better approach.
You can find the detailed math behind the above code on my portfolio page.
Finding angle bisector
To approximate latitude and longitude to a 2D cartesian coordinate system.
To check if the point is inside a polygon I am using the approach mentioned in this geeks for geeks article
I have same problem in my app and finally found the solution by the help of this site
I am an android developer and my code may not be useful to you but the core concept is same.
At first we need to find the bearing of the line with the help of two points LatLng points.(i have done by using computeDistanceAndBearing(double lat1, double lon1,double lat2, double lon2) function)
Now to get the buffering of certain point we need to give the buffering distance ,LatLng point and bearing (which i obtain from computeDistanceAndBearing function).(I have done this by using computeDestinationAndBearing(double lat1, double lon1,double brng, double dist) function ). from single LatLng point we get two points by producing them with their bearing with certain distance.
Now we need to find the interestion point of the two point to get the buffering that we want. for this remember to take new obtain point and bearing of another line and same with another. This helps to obtain new intersection point with buffering you want.(i have done this in my function computeIntersectionPoint(LatLng p1, double brng1, LatLng p2, double brng2))
Do this to all the polygon points and then you get new points whichyou joint to get buffering.
This is the way i have done in my android location app whis is
Here is my code
//computeDistanceAndBearing(double lat1, double lon1,
double lat2, double lon2)
public static double[] computeDistanceAndBearing(double lat1, double lon1,
double lat2, double lon2) {
// Based on http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf
// using the "Inverse Formula" (section 4)
double results[] = new double[3];
int MAXITERS = 20;
// Convert lat/long to radians
lat1 *= Math.PI / 180.0;
lat2 *= Math.PI / 180.0;
lon1 *= Math.PI / 180.0;
lon2 *= Math.PI / 180.0;
double a = 6378137.0; // WGS84 major axis
double b = 6356752.3142; // WGS84 semi-major axis
double f = (a - b) / a;
double aSqMinusBSqOverBSq = (a * a - b * b) / (b * b);
double L = lon2 - lon1;
double A = 0.0;
double U1 = Math.atan((1.0 - f) * Math.tan(lat1));
double U2 = Math.atan((1.0 - f) * Math.tan(lat2));
double cosU1 = Math.cos(U1);
double cosU2 = Math.cos(U2);
double sinU1 = Math.sin(U1);
double sinU2 = Math.sin(U2);
double cosU1cosU2 = cosU1 * cosU2;
double sinU1sinU2 = sinU1 * sinU2;
double sigma = 0.0;
double deltaSigma = 0.0;
double cosSqAlpha = 0.0;
double cos2SM = 0.0;
double cosSigma = 0.0;
double sinSigma = 0.0;
double cosLambda = 0.0;
double sinLambda = 0.0;
double lambda = L; // initial guess
for (int iter = 0; iter < MAXITERS; iter++) {
double lambdaOrig = lambda;
cosLambda = Math.cos(lambda);
sinLambda = Math.sin(lambda);
double t1 = cosU2 * sinLambda;
double t2 = cosU1 * sinU2 - sinU1 * cosU2 * cosLambda;
double sinSqSigma = t1 * t1 + t2 * t2; // (14)
sinSigma = Math.sqrt(sinSqSigma);
cosSigma = sinU1sinU2 + cosU1cosU2 * cosLambda; // (15)
sigma = Math.atan2(sinSigma, cosSigma); // (16)
double sinAlpha = (sinSigma == 0) ? 0.0 : cosU1cosU2 * sinLambda
/ sinSigma; // (17)
cosSqAlpha = 1.0 - sinAlpha * sinAlpha;
cos2SM = (cosSqAlpha == 0) ? 0.0 : cosSigma - 2.0 * sinU1sinU2
/ cosSqAlpha; // (18)
double uSquared = cosSqAlpha * aSqMinusBSqOverBSq; // defn
A = 1 + (uSquared / 16384.0) * // (3)
(4096.0 + uSquared * (-768 + uSquared * (320.0 - 175.0 * uSquared)));
double B = (uSquared / 1024.0) * // (4)
(256.0 + uSquared * (-128.0 + uSquared * (74.0 - 47.0 * uSquared)));
double C = (f / 16.0) * cosSqAlpha * (4.0 + f * (4.0 - 3.0 * cosSqAlpha)); // (10)
double cos2SMSq = cos2SM * cos2SM;
deltaSigma = B
* sinSigma
* // (6)
(cos2SM + (B / 4.0)
* (cosSigma * (-1.0 + 2.0 * cos2SMSq) - (B / 6.0) * cos2SM
* (-3.0 + 4.0 * sinSigma * sinSigma)
* (-3.0 + 4.0 * cos2SMSq)));
lambda = L
+ (1.0 - C)
* f
* sinAlpha
* (sigma + C * sinSigma
* (cos2SM + C * cosSigma * (-1.0 + 2.0 * cos2SM * cos2SM))); // (11)
double delta = (lambda - lambdaOrig) / lambda;
if (Math.abs(delta) < 1.0e-12) {
break;
}
}
double distance = (b * A * (sigma - deltaSigma));
results[0] = distance;
if (results.length > 1) {
double initialBearing = Math.atan2(cosU2 * sinLambda, cosU1 * sinU2
- sinU1 * cosU2 * cosLambda);
initialBearing *= 180.0 / Math.PI;
results[1] = initialBearing;
if (results.length > 2) {
double finalBearing = Math.atan2(cosU1 * sinLambda, -sinU1 * cosU2
+ cosU1 * sinU2 * cosLambda);
finalBearing *= 180.0 / Math.PI;
results[2] = finalBearing;
}
}
return results;
}
//computeDestinationAndBearing(double lat1, double lon1,double brng, double dist)
public static double[] computeDestinationAndBearing(double lat1, double lon1,
double brng, double dist) {
double results[] = new double[3];
double a = 6378137, b = 6356752.3142, f = 1 / 298.257223563; // WGS-84
// ellipsiod
double s = dist;
double alpha1 = toRad(brng);
double sinAlpha1 = Math.sin(alpha1);
double cosAlpha1 = Math.cos(alpha1);
double tanU1 = (1 - f) * Math.tan(toRad(lat1));
double cosU1 = 1 / Math.sqrt((1 + tanU1 * tanU1)), sinU1 = tanU1 * cosU1;
double sigma1 = Math.atan2(tanU1, cosAlpha1);
double sinAlpha = cosU1 * sinAlpha1;
double cosSqAlpha = 1 - sinAlpha * sinAlpha;
double uSq = cosSqAlpha * (a * a - b * b) / (b * b);
double A = 1 + uSq / 16384
* (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
double B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
double sinSigma = 0, cosSigma = 0, deltaSigma = 0, cos2SigmaM = 0;
double sigma = s / (b * A), sigmaP = 2 * Math.PI;
while (Math.abs(sigma - sigmaP) > 1e-12) {
cos2SigmaM = Math.cos(2 * sigma1 + sigma);
sinSigma = Math.sin(sigma);
cosSigma = Math.cos(sigma);
deltaSigma = B
* sinSigma
* (cos2SigmaM + B
/ 4
* (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) - B / 6
* cos2SigmaM * (-3 + 4 * sinSigma * sinSigma)
* (-3 + 4 * cos2SigmaM * cos2SigmaM)));
sigmaP = sigma;
sigma = s / (b * A) + deltaSigma;
}
double tmp = sinU1 * sinSigma - cosU1 * cosSigma * cosAlpha1;
double lat2 = Math.atan2(sinU1 * cosSigma + cosU1 * sinSigma * cosAlpha1,
(1 - f) * Math.sqrt(sinAlpha * sinAlpha + tmp * tmp));
double lambda = Math.atan2(sinSigma * sinAlpha1, cosU1 * cosSigma - sinU1
* sinSigma * cosAlpha1);
double C = f / 16 * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha));
double L = lambda
- (1 - C)
* f
* sinAlpha
* (sigma + C * sinSigma
* (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)));
double lon2 = (toRad(lon1) + L + 3 * Math.PI) % (2 * Math.PI) - Math.PI; // normalise
// to
// -180...+180
double revAz = Math.atan2(sinAlpha, -tmp); // final bearing, if required
results[0] = toDegrees(lat2);
results[1] = toDegrees(lon2);
results[2] = toDegrees(revAz);
return results;
}
private static double toRad(double angle) {
return angle * Math.PI / 180;
}
private static double toDegrees(double radians) {
return radians * 180 / Math.PI;
}
//computeIntersectionPoint(LatLng p1, double brng1, LatLng p2, double brng2)
public static LatLng computeIntersectionPoint(LatLng p1, double brng1, LatLng p2, double brng2) {
double lat1 = toRad(p1.latitude), lng1 = toRad(p1.longitude);
double lat2 = toRad(p2.latitude), lng2 = toRad(p2.longitude);
double brng13 = toRad(brng1), brng23 = toRad(brng2);
double dlat = lat2 - lat1, dlng = lng2 - lng1;
double delta12 = 2 * Math.asin(Math.sqrt(Math.sin(dlat / 2) * Math.sin(dlat / 2)
+ Math.cos(lat1) * Math.cos(lat2) * Math.sin(dlng / 2) * Math.sin(dlng / 2)));
if (delta12 == 0) return null;
double initBrng1 = Math.acos((Math.sin(lat2) - Math.sin(lat1) * Math.cos(delta12)) / (Math.sin(delta12) * Math.cos(lat1)));
double initBrng2 = Math.acos((Math.sin(lat1) - Math.sin(lat2) * Math.cos(delta12)) / (Math.sin(delta12) * Math.cos(lat2)));
double brng12 = Math.sin(lng2 - lng1) > 0 ? initBrng1 : 2 * Math.PI - initBrng1;
double brng21 = Math.sin(lng2 - lng1) > 0 ? 2 * Math.PI - initBrng2 : initBrng2;
double alpha1 = (brng13 - brng12 + Math.PI) % (2 * Math.PI) - Math.PI;
double alpha2 = (brng21 - brng23 + Math.PI) % (2 * Math.PI) - Math.PI;
double alpha3 = Math.acos(-Math.cos(alpha1) * Math.cos(alpha2) + Math.sin(alpha1) * Math.sin(alpha2) * Math.cos(delta12));
double delta13 = Math.atan2(Math.sin(delta12) * Math.sin(alpha1) * Math.sin(alpha2), Math.cos(alpha2) + Math.cos(alpha1) * Math.cos(alpha3));
double lat3 = Math.asin(Math.sin(lat1) * Math.cos(delta13) + Math.cos(lat1) * Math.sin(delta13) * Math.cos(brng13));
double dlng13 = Math.atan2(Math.sin(brng13) * Math.sin(delta13) * Math.cos(lat1), Math.cos(delta13) - Math.sin(lat1) * Math.sin(lat3));
double lng3 = lng1 + dlng13;
return new LatLng(toDegrees(lat3), (toDegrees(lng3) + 540) % 360 - 180);
}
I will suggest you to go through the the above site and get the knowledge as i had also done the same.
Hope this may help , i know the is not in ios but the concept is same as i done my project by changing code of javascript.
Cheers !!!
My requirement was something similar to this. I ended up writing up my own algo for this. https://github.com/RanaRanvijaySingh/PolygonBuffer
All you need to use is this line
double distance = 0.0001;
List bufferedPolygonList = AreaBuffer.buffer(pointList, distance);
It gives you a list of buffered polygon points at a given distance from your original polygon.
I would recommend to use Turf.js library for buffering and many basic gis operations. You would be able to retrieve each edge from the path that returned. For geometry buffer, it is easy to use, quite light weighted and it works without any problem for my applications using MapBox.js or leaflet.
More details : Turf.js Buffer
But if you are looking for a geodesic distance buffer that could be problem. I would use Arcgis Javascript API
Take a look at BOOST this is a big C++ library, you may find library/source code for almost everything up there like, buffer methods with different types such as miter,round,square.
Just install the latest version of the Boost which I guess is 1.58.0 right now, and take a look at BOOST/Geometry/Strategies/Cartesian/buffer[Something]-Square/Miter/Round
Here it is a good document
You need to convert your geodetic coordinates (lat/long) to cartesian (x/y) and use the Boost library and reverse the conversion. you do not need to use ArcGIS or any other GIS library at all.
this is my complete code for a small gui to take variables from user and return them to the rest of program(r.o.p) for calculations, however doesn't seem to be passing to r.o.p. and no output file is observerd.
Any help would be much appreciated, I think the problem is with the listener for the Draw New Graph command. Thanks
Code:
import java.text.*;
import java.io.*;
import java.util.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
public class RungeKutta {
// class to create slider and window
private static File file1;
public static double A(double v, double x, double wc, double beta) {
return (-2 * beta * v - (Math.pow(wc, 2)) * Math.sin(x));
}
public static File getFile1() {
return file1;
}
public static void setFile1(File file1) {
RungeKutta.file1 = file1;
}
public static void main(String[] argv) throws IOException, ParseException {
createAndShowGUI();
}
private static void createAndShowGUI() {
//create and set up the window.
JFrame frame = new JFrame("RungeKutta");
GridLayout first = new GridLayout(14,1);
frame.setLayout(first);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Create, name and Populate TextField
JTextField PL = new JTextField("Pendulum Length", 20);
//Set TextField to Uneditable. Each will have Empty Field Below For Variables
PL.setEditable(false);
//Set Textfield for user entered dat
JTextField PLv = new JTextField();
//Allow input from text field to be taken
JTextField AD = new JTextField("Angular Displacement", 20);
AD.setEditable(false);
JTextField ADv = new JTextField();
JTextField AV = new JTextField("Angular Velocity", 20);
AV.setEditable(false);
JTextField Avv = new JTextField();
JTextField TS= new JTextField("Time Steps", 20);
TS.setEditable(false);
JTextField TSv = new JTextField();
JTextField MT = new JTextField("Max Time", 20);
MT.setEditable(false);
JTextField MTv = new JTextField();
JTextField V = new JTextField("Viscosity (0-1)", 20);
V.setEditable(false);
JTextField Vv = new JTextField();
//Create Button to Restart and Button to take in Values for usage
JLabel emptyLabel = new JLabel("");
emptyLabel.setPreferredSize(new Dimension(600,500));
frame.getContentPane().add(PL, first);
frame.getContentPane().add(PLv, first);
frame.getContentPane().add(AD, first);
frame.getContentPane().add(ADv, first);
frame.getContentPane().add(AV, first);
frame.getContentPane().add(Avv, first);
frame.getContentPane().add(TS, first);
frame.getContentPane().add(TSv, first);
frame.getContentPane().add(MT, first);
frame.getContentPane().add(MTv, first);
frame.getContentPane().add(V, first);
frame.getContentPane().add(Vv, first);
JButton BNewGraph = new JButton("Draw New Graph"); //Button to restart entire drawing process
frame.getContentPane().add(BNewGraph, first);
//display the window
frame.pack();
frame.setVisible(true);
class intakegui implements ActionListener
{
public intakegui()
{
BNewGraph.addActionListener((ActionListener) this);
BNewGraph.setActionCommand("Click to Draw");
}
#Override
public void actionPerformed(ActionEvent e) {
double l = Double.parseDouble(PLv.getText());
double xi = Double.parseDouble(ADv.getText());
double vi = Double.parseDouble(Avv.getText());
double dt = Double.parseDouble(TSv.getText());
double n = Double.parseDouble(MTv.getText());
double beta = Double.parseDouble(Vv.getText());
SimpleDateFormat dform = new SimpleDateFormat("ddMMyyyysmH");
Date d = Calendar.getInstance().getTime();
String dstr = dform.format(d);
String filename = ("result " + dstr + ".txt");
file1 = new File(filename);
PrintWriter savedValues = null;
try {
savedValues = new PrintWriter(filename);
} catch (FileNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
//declare variable for gravity and omega*c
double g = 9.8;
double wc = Math.sqrt(g / l);
savedValues.println("#Initial Values Entered");
savedValues.println("#" + l + "," + vi + "," + xi + "," + dt + "," + n
+ "," + beta);
double[] dispTime = new double[(int) n];
double[] velTime = new double[(int) n];
// let a = angular acceleration
for (double j = 0; j < (int) n; j++) {
// int time = 1;
double k1v = dt * j * A(vi, xi, wc, beta);
double k1x = dt * j * vi;
double k2v = dt * j * A(vi, xi + (0.5 * k1x), wc, beta);
double k2x = dt * j * (vi + (0.5 * k1v));
double k3v = dt * j * A(vi, xi + (0.5 * k2x), wc, beta);
double k3x = dt * j * (vi + (0.5 * k2v));
double k4v = dt * j * A(vi, xi + k3x, wc, beta);
double k4x = dt * j * (vi + k3v);
xi += k1x / 6 + k2x / 3 + k3x / 3 + k4x / 6;
vi += k1v / 6 + k2v / 3 + k3v / 3 + k4v / 6;
dispTime[(int) j] = xi;
velTime[(int) j] = vi;
System.out.println(xi + "," + vi);
}
for (int i = 0; i < (int) n; i++) {
savedValues.println(dispTime[i] + " " + velTime[i]);
}
savedValues.close();
System.out.println("File saved. File name: " + filename);
}
}
}
}
You forgot to add listener to your button. You need to add listener as below:
BNewGraph.addActionListener(new intakegui());
Now when user clicks on button, you would see the file.
What I was thinking to do is to convert ofColor to l*a*b color space and measure the euclidean distance. But I don't know how should I do it in openframeworks?
I'm not very experienced with c++ but I ported this snippet over:
//ported from http://cookbooks.adobe.com/post_Useful_color_equations__RGB_to_LAB_converter-14227.html
struct Color{
float R,G,B,X,Y,Z,L,a,b;
};
#define REF_X 95.047; // Observer= 2°, Illuminant= D65
#define REF_Y 100.000;
#define REF_Z 108.883;
Color rgb2xyz(int R,int G,int B){
float r = R / 255.0;
float g = G / 255.0;
float b = B / 255.0;
if (r > 0.04045){ r = pow((r + 0.055) / 1.055, 2.4); }
else { r = r / 12.92; }
if ( g > 0.04045){ g = pow((g + 0.055) / 1.055, 2.4); }
else { g = g / 12.92; }
if (b > 0.04045){ b = pow((b + 0.055) / 1.055, 2.4); }
else { b = b / 12.92; }
r = r * 100;
g = g * 100;
b = b * 100;
//Observer. = 2°, Illuminant = D65
Color xyz;
xyz.X = r * 0.4124 + g * 0.3576 + b * 0.1805;
xyz.Y = r * 0.2126 + g * 0.7152 + b * 0.0722;
xyz.Z = r * 0.0193 + g * 0.1192 + b * 0.9505;
return xyz;
}
Color xyz2lab(float X,float Y, float Z){
float x = X / REF_X;
float y = Y / REF_X;
float z = Z / REF_X;
if ( x > 0.008856 ) { x = pow( x , .3333333333f ); }
else { x = ( 7.787 * x ) + ( 16/116.0 ); }
if ( y > 0.008856 ) { y = pow( y , .3333333333f ); }
else { y = ( 7.787 * y ) + ( 16/116.0 ); }
if ( z > 0.008856 ) { z = pow( z , .3333333333f ); }
else { z = ( 7.787 * z ) + ( 16/116.0 ); }
Color lab;
lab.L = ( 116 * y ) - 16;
lab.a = 500 * ( x - y );
lab.b = 200 * ( y - z );
return lab;
}
Color lab2xyz(float l, float a, float b){
float y = (l + 16) / 116;
float x = a / 500 + y;
float z = y - b / 200;
if ( pow( y , 3 ) > 0.008856 ) { y = pow( y , 3 ); }
else { y = ( y - 16 / 116 ) / 7.787; }
if ( pow( x , 3 ) > 0.008856 ) { x = pow( x , 3 ); }
else { x = ( x - 16 / 116 ) / 7.787; }
if ( pow( z , 3 ) > 0.008856 ) { z = pow( z , 3 ); }
else { z = ( z - 16 / 116 ) / 7.787; }
Color xyz;
xyz.X = x * REF_X;
xyz.Y = y * REF_Y;
xyz.Z = z * REF_Z;
return xyz;
}
Color xyz2rgb(float X,float Y,float Z){
//X from 0 to 95.047 (Observer = 2°, Illuminant = D65)
//Y from 0 to 100.000
//Z from 0 to 108.883
X = ofClamp(X, 0, 95.047);
float x = X * .01;
float y = Y * .01;
float z = Z * .01;
float r = x * 3.2406 + y * -1.5372 + z * -0.4986;
float g = x * -0.9689 + y * 1.8758 + z * 0.0415;
float b = x * 0.0557 + y * -0.2040 + z * 1.0570;
if ( r > 0.0031308 ) { r = 1.055 * pow( r , ( 1 / 2.4f ) ) - 0.055; }
else { r = 12.92 * r; }
if ( g > 0.0031308 ) { g = 1.055 * pow( g , ( 1 / 2.4f ) ) - 0.055; }
else { g = 12.92 * g; }
if ( b > 0.0031308 ) { b = 1.055 * pow( b , ( 1 / 2.4f ) ) - 0.055; }
else { b = 12.92 * b; }
Color rgb;
rgb.R = round( r * 255 );
rgb.G = round( g * 255 );
rgb.B = round( b * 255 );
return rgb;
}
Color rgb2lab(int R,int G,int B){
Color xyz = rgb2xyz(R, G, B);
return xyz2lab(xyz.X, xyz.Y, xyz.Z);
}
Color lab2rgb(int L,int a,int b){
Color xyz = lab2xyz(L, a, b);
return xyz2rgb(xyz.X, xyz.Y, xyz.Z);
}
Measuring the distance would be something as trivial as:
float distLab(Color c1,Color c2){
float dL = c1.L - c2.L;
float da = c1.a - c2.a;
float db = c1.b - c2.b;
return sqrt(dL*dL + da*da + db*db);
}
or ofVec3f(c1.L,c1.a,c1.b).distance(ofVec3f(c2.L,c2.a,c2.b));
Also see this answer for an openframeworks basic example.
I am doing some staff with mean shift segmentation. I found the code called EDISON(http://coewww.rutgers.edu/riul/research/code/EDISON/) ,which has implemented a good one. Also, opencv has a function called cvPyrMeanShiftFiltering.
But they are different in the following picture. In the EDISON system, the two parameters are 7 & 6.5. In opencv, I use
cvPyrMeanShiftFiltering(result, result, 7, 6.5,level); (result is converted to LUV already) The borders are not clear there in the right image. I also tried level=0...
Can anyone help? I want to achieve the same result with the help of openCV. (I've implemented the clustering step already)
I tried another code from rsbweb...and the effect is much better than openCV!
Here is my new result
I changed some part of the code, using Luv rather than YIQ to gain better result. The color equation is from easyrgb
Here is my code
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class test {
public static void main(String[] args) throws IOException {
BufferedImage img = ImageIO.read(new File("2.png"));
filterRGBImage(img);
ImageIO.write(img, "png", new File("3.png"));
}
private static int rad = 10, rad2=rad*rad;
private static double radCol2 = Math.pow(10,2);
private static void RGB2XYZ(double rgb[], double xyz[])
{
double var_R = ( rgb[0] / 255. ),var_G = ( rgb[1] / 255. ),var_B = ( rgb[2] / 255. );
if ( var_R > 0.04045 )
var_R = Math.pow(( ( var_R + 0.055 ) / 1.055 ),2.4);
else
var_R = var_R / 12.92;
if ( var_G > 0.04045 )
var_G = Math.pow( ( var_G + 0.055 ) / 1.055 , 2.4);
else
var_G = var_G / 12.92;
if ( var_B > 0.04045 )
var_B = Math.pow( ( var_B + 0.055 ) / 1.055 ,2.4);
else
var_B = var_B / 12.92;
var_R = var_R * 100;
var_G = var_G * 100;
var_B = var_B * 100;
xyz[0] = var_R * 0.4124 + var_G * 0.3576 + var_B * 0.1805;
xyz[1] = var_R * 0.2126 + var_G * 0.7152 + var_B * 0.0722;
xyz[2] = var_R * 0.0193 + var_G * 0.1192 + var_B * 0.9505;
}
private static void XYZ2RGB(double xyz[], double rgb[])
{
double var_X = xyz[0] / 100, var_Y = xyz[1] / 100 , var_Z = xyz[2] / 100;
double var_R = var_X * 3.2406 + var_Y * -1.5372 + var_Z * -0.4986,
var_G = var_X * -0.9689 + var_Y * 1.8758 + var_Z * 0.0415,
var_B = var_X * 0.0557 + var_Y * -0.2040 + var_Z * 1.0570;
if ( var_R > 0.0031308 )
var_R = 1.055 * Math.pow(var_R, 1 / 2.4 ) - 0.055;
else
var_R = 12.92 * var_R;
if ( var_G > 0.0031308 )
var_G = 1.055 * Math.pow(var_G , 1 / 2.4 ) - 0.055;
else
var_G = 12.92 * var_G;
if ( var_B > 0.0031308 )
var_B = 1.055 * Math.pow(var_B ,1 / 2.4 ) - 0.055;
else
var_B = 12.92 * var_B;
rgb[0] = var_R * 255;
rgb[1] = var_G * 255;
rgb[2] = var_B * 255 ;
}
private static void XYZ2Luv(double xyz[], double luv[])
{
double var_U = ( 4 * xyz[0] ) / ( xyz[0] + ( 15 * xyz[1] ) + ( 3 * xyz[2] ) ),
var_V = ( 9 * xyz[1] ) / ( xyz[0] + ( 15 * xyz[1] ) + ( 3 * xyz[2] ) ),
var_Y = xyz[1] / 100;
if ( var_Y > 0.008856 )
var_Y = Math.pow(var_Y, 1./3 );
else
var_Y = ( 7.787 * var_Y ) + ( 16. / 116 );
double ref_X = 95.047, ref_Y = 100.000, ref_Z = 108.883;
double ref_U = ( 4 * ref_X ) / ( ref_X + ( 15 * ref_Y ) + ( 3 * ref_Z ) ),
ref_V = ( 9 * ref_Y ) / ( ref_X + ( 15 * ref_Y ) + ( 3 * ref_Z ) );
luv[0] = ( 116 * var_Y ) - 16;
luv[1] = 13 * luv[0] * ( var_U - ref_U );
luv[2] = 13 * luv[0] * ( var_V - ref_V );
}
private static void Luv2XYZ(double luv[],double xyz[])
{
double var_Y = ( luv[0] + 16 ) / 116;
if ( Math.pow(var_Y,3) > 0.008856 )
var_Y = Math.pow(var_Y,3);
else
var_Y = ( var_Y - 16./ 116 ) / 7.787;
double ref_X = 95.047 ,ref_Y = 100.000,ref_Z = 108.883;
double ref_U = ( 4 * ref_X ) / ( ref_X + ( 15 * ref_Y ) + ( 3 * ref_Z ) ),
ref_V = ( 9 * ref_Y ) / ( ref_X + ( 15 * ref_Y ) + ( 3 * ref_Z ) );
double var_U = luv[1] / ( 13 * luv[0] ) + ref_U,
var_V =luv[2] / ( 13 * luv[0] ) + ref_V;
xyz[1] = var_Y * 100;
xyz[0] = - ( 9 * xyz[1] * var_U ) / ( ( var_U - 4 ) * var_V - var_U * var_V );
xyz[2] = ( 9 * xyz[1] - ( 15 * var_V * xyz[1]) - ( var_V * xyz[0] ) ) / ( 3 * var_V );
}
//http://rsbweb.nih.gov/ij/plugins/download/Mean_Shift.java
public static void filterRGBImage(BufferedImage ip) {
int width = ip.getWidth();
int height = ip.getHeight();
double[][][] pixelsf = new double[width][height][3];
for (int y=0; y<height; y++) {
for (int x=0; x<width; x++) {
int argb = ip.getRGB(x,y);
int r = (argb >> 16) & 0xff;
int g = (argb >> 8) & 0xff;
int b = (argb) & 0xff;
// YIQ
/*pixelsf[x][y][0] = 0.299f *r + 0.587f *g + 0.114f *b;
pixelsf[x][y][1] = 0.5957f *r - 0.2744f*g - 0.3212f *b;
pixelsf[x][y][2] = 0.2114f *r - 0.5226f*g + 0.3111f *b;*/
double tmp[]={r,g,b},tmp2[]={0,0,0};
RGB2XYZ(tmp,tmp2);
XYZ2Luv(tmp2,pixelsf[x][y]);
}
}
double shift = 0;
int iters = 0;
for (int y=0; y<height; y++) {
for (int x=0; x<width; x++) {
int xc = x;
int yc = y;
int xcOld, ycOld;
double YcOld, IcOld, QcOld;
double Yc = pixelsf[x][y][0];
double Ic = pixelsf[x][y][1];
double Qc = pixelsf[x][y][2];
iters = 0;
do {
xcOld = xc;
ycOld = yc;
YcOld = Yc;
IcOld = Ic;
QcOld = Qc;
float mx = 0;
float my = 0;
float mY = 0;
float mI = 0;
float mQ = 0;
int num=0;
for (int ry=-rad; ry <= rad; ry++) {
int y2 = yc + ry;
if (y2 >= 0 && y2 < height) {
for (int rx=-rad; rx <= rad; rx++) {
int x2 = xc + rx;
if (x2 >= 0 && x2 < width) {
//if (ry*ry + rx*rx <= rad2) {
double Y2 = pixelsf[x2][y2][0];
double I2 = pixelsf[x2][y2][1];
double Q2 = pixelsf[x2][y2][2];
double dY = Yc - Y2;
double dI = Ic - I2;
double dQ = Qc - Q2;
if (dY*dY+dI*dI+dQ*dQ <= radCol2) {
mx += x2;
my += y2;
mY += Y2;
mI += I2;
mQ += Q2;
num++;
}
//}
}
}
}
}
double num_ = 1./num;
Yc = mY*num_;
Ic = mI*num_;
Qc = mQ*num_;
xc = (int) (mx*num_+0.5);
yc = (int) (my*num_+0.5);
int dx = xc-xcOld;
int dy = yc-ycOld;
double dY = Yc-YcOld;
double dI = Ic-IcOld;
double dQ = Qc-QcOld;
shift = dx*dx+dy*dy+dY*dY+dI*dI+dQ*dQ;
iters++;
}
while (shift > 3 && iters < 100);
double tmp[]={Yc,Ic,Qc},tmp2[]={0,0,0};
Luv2XYZ(tmp,tmp2);
XYZ2RGB(tmp2,tmp);
int r_ = (int) tmp[0];
int g_ = (int) tmp[1];
int b_ = (int) tmp[2];
ip.setRGB(x, y, (0xFF<<24)|(r_<<16)|(g_<<8)|b_);
}
}
}
}