I´m trying to implement A Practical Analytic Model for Daylight in Processing and project it on a 2d canvas.
Everything works fine so far, except a nasty problem with the azimuth of the sun.
When i animate the time of the day, the sun is jumping to the opposite site on a specific time.
This happens because the azimuth goes from -90 to +90 degree or vice versa.
I´m not sure if that´s a restriction of the paper or i made a mistake calculating the solar position.
As far as i understand the azimuth should be between 0 and 360 degree.
Anyone already implemented the Preetham paper and can help me?
Here´s my code to calculate the solar position.
You can download the complete Processing sketch here:
https://dl.dropbox.com/u/42247259/PreethamSky.zip
Thanks for your help.
H.G.
private void calculateSolarPosition() {
float t = solarTime(standardTime, dayOfYear, standardMeridian, longitude);
float delta = solarDeclination(dayOfYear);
thetaS = angleFromSunToZenith(t, delta, latitude);
phiS = sunAzimuth(t, delta, latitude);
}
/// Returns the solar time at a certain geographic place, day of year and standard time.
private float solarTime(float standardTime, int dayOfYear, float standardMeridian, float longitude) {
return (float)(standardTime + 0.17 * sin(4 * PI * (dayOfYear - 80) / 373) - 0.129 * sin(2 * PI * (dayOfYear - 8) / 355) + 12 * (standardMeridian - longitude) / PI);
}
/// Returns the solar declination. Solar declination is the angle between the rays of the sun and the
/// plane of the earth's equator.
private float solarDeclination(int dayOfYear) {
return (float)(0.4093 * sin(2 * PI * (dayOfYear - 81) / 368.0));
}
/// Returns the angle from the sun to the zenith in rad.
private float angleFromSunToZenith(float solarTime, float solarDeclination, float latitude) {
return (float)(PI / 2 - asin(sin(latitude) * sin(solarDeclination) - cos(latitude) * cos(solarDeclination) * cos(PI * solarTime / 12)));
}
/// Returns the azimuth of the sun in rad. Azimuth is the angle between a line to south and the sun.
private float sunAzimuth(float solarTime, float solarDeclination, float latitude) {
return (float)-(atan((-cos(solarDeclination) * sin(PI * solarTime / 12)) /
(cos(latitude) * sin(solarDeclination) - sin(latitude) * cos(solarDeclination) *
cos(PI * solarTime / 12.0))));
}
I have not tried that so it may not work (depends on the formula which I haven't studied in detail), but instead of atan you could use the atan2 function which gives a full 360 degree result.
/// Returns the azimuth of the sun in rad. Azimuth is the angle between a line to south and the sun.
private float sunAzimuth(float solarTime, float solarDeclination, float latitude) {
return (float)-(atan2((-cos(solarDeclination) * sin(PI * solarTime / 12)),
(cos(latitude) * sin(solarDeclination) - sin(latitude) * cos(solarDeclination) *
cos(PI * solarTime / 12.0))));
}
This function takes into account the signs of numerator and denominator to decide in which quadrant the angle should be.
Related
I have this method for calculating regular polygon area:
public double getArea() {
return (sideLength *
sideLength * sides) /
(4 * Math.tan(180 / (double) sides));
}
for sideLength and sides both being equal to 10 it returns -219.816218.
"
However this online calculator: https://www.omnicalculator.com/math/regular-polygon-area
returns 769.4. What is wrong with my method? The formula I use is specified here
.
The arguments to trigonometric functions are defined on radians, not degrees. Use Math.toRadians to convert the angle in degrees to radians - like this:
Math.tan(Math.toRadians(180 / (double) sides))
Or do this computation in radians to start with.
Math.tan(Math.PI / sides)
Use the following return statement
return (sideLength * sideLength * sides) / (4 * Math.tan((180 / sides) * 3.14159 / 180));
Here, *(3.14159 / 180) is added to convert the area from degree converted to radians
The problem is that the Math.tan function uses radians as it's default unit of measure. Use this instead:
(4*Math.tan(Math.PI/180 * 180/(double) sides))
I want to be able to add an exact distance to some GPS coordinates.
I have a longitude and a latitude and I want to add a distance, let's say 30 meters.
I found below formula but when I test it, it does not seem to be that accurate because the resulting long and lat are 37m away from the beginning coords.
public static Coordinates addDistanceToCoordinates(String lat, String lon, double distance) {
Double latitude = Double.valueOf(lat);
Double longitude = Double.valueOf(lon);
double lat0 = cos(Math.PI / 180.0 * latitude);
double x = longitude + (180/Math.PI)*(distance/6378137)/cos(lat0);
double y = latitude + (180/Math.PI)*(distance/6378137);
return Coordinates.builder().lon(x).lat(y).build();
}
If you have a center (x,y) and you move on the x axis by 30 meters and on the y axis by another 30 meters your distance from the center won't be 30.
It will be Math.sqrt( Math.pow(x, 2) + Math.pow(y, 2) );.
Specifically, there are an infinite number of points that are 30 meters distant from the center (or your initial coordinates).
If you want to move in only one direction, then you can simply add/subtract 30 meters in either of your axis.
As you already did:
double x = longitude + (180/Math.PI)*(distance/6378137)/cos(lat0);
or
double y = latitude + (180/Math.PI)*(distance/6378137);
but not both...
You are still better off by using angles in your calculations, which will turn handy when you move on both axis.
By knowing which direction you are headed to, for example 50° from the x axis,
double a = Math.toRadians(50); // degrees
double x = longitude + (180/Math.PI) * (30 / 6378137)/cos(lat0) * Math.cos(a);
double y = latitude + (180/Math.PI) * (30 / 6378137) * Math.sin(a);
Coming back to your question, if you want to move on the x axis and the y axis by the same distance and end up exactly 30 meters away from the center, then your angle will be double a = Math.toRadians(45); (if you head North-East) *
In fact you will obtain for both (30 / 6378137) * Math.cos(a) and (30 / 6378137) * Math.sin(a) a result of x1 = y1 = 3.325924707417923e-06.
If you then apply Pythagoras
double finalDistance = Math.sqrt(Math.pow(x1, 2) + Math.pow(y1, 2)) * 6378137;
you will find finalDistance to be 30 meters from your initial coordinates.
*
The correct calculation would be Math.toRadians(45 * (2 * n - 1)); | where n = [1, 2, 3, 4]
Your code adds 30 meters north and 30 meters east, resulting in 42.4 meters northeast.
The calculation is assuming earth as a sphere instead of an ellipsoid, but that's mostly OK, can make a difference of max. 0.2 percent. It uses the biggest earth diameter (equator - equator) instead of some mean value, which will result in points too close to the starting point most of the time, but agian, taht can give an error of maybe 0.2 percent.
The calculation assumes the lat/lon grid to be rectangular, which is OK as long as the distances are short and you stay away from north or south pole.
So, all of this doesn't explain the 20 percent error you are reporting. The problem must be outside of the code you showed us.
The most suspicious remaining aspect is the string conversion. You'll need at least 5 decimal places for lat / lon degree values to get a 1 meter resolution.
Or maybe the tool that told you about the 37 meters isn't correct or somehow incompatible with the data...
I am trying to find closest cluster to the given cluster 1 (see example below). To visualize clusters, I used QGIS (X axis is Longitude and Y axis is Latitude).
First of all I calculated centroids for each cluster. Thus I got the following results:
Cluster 1:
Lat : -83.5
Lon: -159.3
Cluster 2:
Lat: -80.5
Lon: -123.9
Cluster 3:
Lat: -83.4
Lon: 159.4
As a distance metric I use Haversine formula:
/**
* Calculates Haversine distance between two points
* #param lat1
* #param lon1
* #param lat2
* #param lon2
* #return
*/
private static double haversine(double lat1, double lon1, double lat2, double lon2) {
double R = 6372.8;
double dLat = Math.toRadians(lat2 - lat1);
double dLon = Math.toRadians(lon2 - lon1);
lat1 = Math.toRadians(lat1);
lat2 = Math.toRadians(lat2);
double a = Math.pow(Math.sin(dLat / 2),2) + Math.pow(Math.sin(dLon / 2),2) * Math.cos(lat1) * Math.cos(lat2);
double c = 2 * Math.asin(Math.sqrt(a));
return R * c;
}
I got the following results (one can get similar results by running the haversine function):
Distance between Cluster 1 and 2: 628.37
Distance between Cluster 1 and 3: 513.73
So, though it's clear from the picture that Cluster 2 is closer to Cluster 1 than Cluster 3, the formula says that Cluster 3 is closer.
Which formula should I better use for this case?
As #Azat says, your picture is wrong. For two reasons, actually.
Firstly, you've forgotten something very important - the "edges" of the map at +/- 180 degrees actually meet on the physical globe. That is, you need to (at minimum) turn your picture into a cylinder, like some 2d video games do.
Secondly, you need to remember what makes calculating distances on a sphere via lat/long so tricky: as you near the north/south poles, identical longitude values will get closer together, while latitude values will stay the same distance apart.
If you plot these on a sphere properly, it should really look like this:
(Courtesy of google earth)
The red line is about where the 180 degree latitude mark is (clusters 1 and 3 are almost reflections over that line). NOW it's clear that Cluster 3 is closer to Cluster 1 than Cluster 2 is.
All your calculation are totally exact.
You're really close to the south pole that why you make huge errors drawing points like if earth was flat.
Here is a pic from google earth where earth is round, we can see that 1 is closer to 3 than to 2 :
I have tried to convert a calculation from an app I made using MIT AppInventor which uses Kawa to Android using Java.The problem I'm facing is that the trigonometric parts of the calculation in Kawa are using degress.My question is how do I translate this calculation to Java and get the same output?
This is how I do the calculation is Kawa,all variables are of type double:
Tri 1=atan(Offset Depth/Offset Length)
Mark 1=sqrt(Offset Length^2+Offset Depth^2)
Tri 2=(180-Tri1)/2
Mark 2=Duct Depth/(tan(Tri 2))
Then I did my best to translate it to Java code,the variables are double also as above,depth,length and duct depth are user input values.
tri1 = Math.atan(offsetDepth / offsetLength);
marking1 = Math.sqrt(Math.pow(offsetLength,2) + Math.pow(offsetDepth,2));
tri2 = (180 - tri1) / 2;
marking2 = ductDepth / Math.tan(tri2);
Screenshot of what the inputs and outputs look like:
You can use Math.toRadians() to convert degrees to radians.
You can convert the angles to radians yourself.
As we know:
180 degrees = PI radians
So:
1 degree = PI / 180 radians
So wherever you have X degrees,
they are equal to (X * PI / 180) radians.
In Java you have
Math.PI
which defines the value of the PI number.
Just change your Java code to this:
tri11 = Math.atan(1.0 * offsetDepth / offsetLength); // tri11 is radians
tri1 = tri11 * 180.0 / Math.PI; // tri1 is degrees
marking1 = Math.sqrt(Math.pow(1.0 * offsetLength,2) + Math.pow(1.0 * offsetDepth,2));
tri2 = (180.0 - tri1) / 2.0; // tri2 is degrees
tri22 = tri2 * Math.PI / 180.0; // tri22 is radians
marking2 = 1.0 * ductDepth / Math.tan(tri22);
// output whatever you like now
I have converted the formula into Java provided here. But accuracy is a problem. We are using GPS coordinates.
We are using iPhone provided GPS location that is upto 10 decimal point accuracy.
/*
* Latitude and Longitude are in Degree
* Unit Of Measure : 1 = Feet,2 = Kilometer,3 = Miles
*/
//TODO 3 Change Unit of Measure and DISTANCE_IN_FEET constants to Enum
public static Double calculateDistance(double latitudeA,double longitudeA,double latitudeB,double longitudeB,short unitOfMeasure){
Double distance;
distance = DISTANCE_IN_FEET *
Math.acos(
Math.cos(Math.toRadians(latitudeA)) * Math.cos(Math.toRadians(latitudeB))
*
Math.cos(Math.toRadians(longitudeB) - Math.toRadians(longitudeA))
+
Math.sin(Math.toRadians(latitudeA))
*
Math.sin(Math.toRadians(latitudeB))
);
return distance;
}
FYI : public static final int DISTANCE_IN_FEET = 20924640;
And then I use Math.round(distance); to convert to long.
For actual 25 feet, I am getting 7 feet output.
You need the haversine formula.
Here's my java implementation:
/**
* Calculates the distance in km between two lat/long points
* using the haversine formula
*/
public static double haversine(
double lat1, double lng1, double lat2, double lng2) {
int r = 6371; // average radius of the earth in km
double dLat = Math.toRadians(lat2 - lat1);
double dLon = Math.toRadians(lng2 - lng1);
double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2))
* Math.sin(dLon / 2) * Math.sin(dLon / 2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
double d = r * c;
return d;
}
According to Wikipedia, 10 feet is close to the limit of what you can expect from normal GPS accuracy. (And that assumes you are getting a good GPS signal.) This post on http://gis.stackexchange.com provides more detailed numbers. It basically says that 3 meter horizontal resolution is the best you are likely to get with an ordinary GPS receiver.
So if you are comparing locations provided by two different (ordinary) receivers with best case ( 3 meter / 10 feet ) resolution on each, you are still not going to be able to tell reliably if one receiver is within 10 feet of the other.
(Note your 10 digits of "precision" may well be illusory. The real issue is how accurate the locations are; i.e. how big the error bars are ... on two different devices.)
So in that case what other alternatives I have?
The linked articles mention WAAS and Carrier Phase GPS (CPGPS). Another option is WPS.