Wrapping around 360 degrees with SQL query - mysql

I am fairly new with using SQL and I would like to learn how to efficiently filter a data set of ecliptic coordinates (longitude, latitude) for regions of the sky. The data set is in the southern ecliptic hemisphere (from -90 to 0 and 0 to 360) as shown below (simulated in Python with some simple numpy arange commands).
I have figured out how to filter a specific patch of the sky in python with the following code:
x = (eclong + 360 - center) % 360
ind = x > 180
x[ind] = x[ind] - 360 #converts from 0 to 360 to -180 to +180
x = -x #reverses scale where east direction is on left
To allow data points to wrap around the South pole, I also have an additional required condition:
filtered_eclong = (x < 12/np.cos(eclat *np.pi/180)) & (-12/np.cos(eclat *np.pi/180) < x & eclat < 0)
12 degrees is how wide the patch of sky that I want to grab is and center is a variable for the central ecliptic longitude of my desired patch of sky. The last line in the query is a correction to include objects near the southern ecliptic pole. (Note: eclat = ecliptic latitude , eclong = ecliptic longitude, both in degrees) As an example of the results I would like to get, the patch of sky I want to filter is centered on a ecliptic longitude of 315.8 degrees, seen below:
I've saved the entire simulated southern hemisphere as a csv file and uploaded the eclong and eclat values as columns in a sql database. I would like to be able to recreate the same query I performed in my Python code above. This is the closest attempt I have:
select * from coords
where eclat < -6
and mod(abs(eclong-315.8+360),360)-360 < 180
and mod(abs(eclong-315.8),360) < 12/cos(radians(eclat));
which produces this result where the filtered Python result is shown in red and the filtered sql result is shown in blue:
As shown, my query doesn't include things that wrap around from 360 degrees to 0 degrees and I'm not quite sure how to include those missing points.
I think using some combination of THEN, ELSE or CASE statements would help:
select * from Catalog
where eclat < -6
CASE
WHEN mod(abs(eclong-315.8),360) < 12/cos(radians(eclat)) > 0 THEN 'I'm not sure what to do...'
WHEN mod(abs(eclong-315.8),360) < 12/cos(radians(eclat)) < 360 THEN 'I'm not sure what to do...'
into myDB.filename
Any SQL gurus out there?

Though I'm not familiar with ecliptic coordinates, it sounds like you're trying to reconcile the lack of negative numbers based on your title. If that's indeed what you're going for, who cares that there aren't any negative values—let's pretend there are!
To generalize, we'll need to ensure we can safely calculate ±180 degrees from any starting longitude. With this use case, starting from 11 degrees, we could rearrange the unhelpful values by subtracting 360 from anything greater than 11 + 180 and working with the result as a helper. This way, 359 degrees would instead show up as -1, and then you can do normal math. If your starting longitude is above 180, we'd need to get the lowest values above 360. In SQL, here's a (maybe inefficient, but straightforward) way I'd create a helper column:
CASE
WHEN center < 180 THEN CASE
WHEN eclong > center + 180 THEN eclong - 360
ELSE eclong
END
WHEN center > 180 THEN CASE
WHEN eclong < center - 180 THEN eclong + 360
ELSE eclong
END
ELSE eclong
END as friendly_eclong

Related

Getting latitude and longitude of the Sun on a world map with PyEphem

I'm trying to determine the latitude and longitude of say the Sun, the Moon and Mars. I need the result relative to the Earth's equator and the Prime Meridian in order to produce a result similar to this map.
I believe that's also what the author of this question wanted, however the answer there doesn't add up for me (comparing with values from the first link).
Expected result, obtained from the page linked to earlier:
On Thursday, 1 January 2015, 00:00:00 UTC the Sun is at its zenith at Latitude: 23° 02' South, Longitude: 179° 29' West
>>> import ephem; from math import degrees
>>> b = ephem.Sun(epoch='date'); b.compute('2015/1/1 00:00:00')
>>> print("{},{}".format(degrees(b.dec), degrees(b.ra)))
-23.040580418272267,281.12827017399906
So the latitude/declination seems about right, but no 180° wraparound will fix that right ascension, probably because it starts at the Vernal Equinox.
I have also unsuccessfully tried to use an observer at 0,0.
Can this be done using PyEphem, Skyfield or astropy? It seems odd that artificial satellites in PyEphem have the convenient sublat and sublong attributes, but it's so hard for celestial bodies.
I finally figured it out. Sort of. Actually I just ported the relevant bits of libastro to Python. Note that this code runs against the current git version of Skyfield (be6c7296).
Here goes (gist version):
#!/usr/bin/env python3
from datetime import datetime, timezone
from math import atan, atan2, degrees, floor, pi, radians, sin, sqrt
from skyfield.api import earth, JulianDate, now, sun
def earth_latlon(x, y, z, time):
"""
For an object at the given XYZ coordinates relative to the center of
the Earth at the given datetime, returns the latitude and longitude
as it would appear on a world map.
Units for XYZ don't matter.
"""
julian_date = JulianDate(utc=time).tt
# see https://en.wikipedia.org/wiki/Julian_date#Variants
# libastro calls this "mjd", but the "Modified Julian Date" is
# something entirely different
dublin_julian_date = julian_date - 2415020
# the following block closely mirrors libastro, so don't blame me
# if you have no clue what the variables mean or what the magic
# numbers are because I don't either
sidereal_solar = 1.0027379093
sid_day = floor(dublin_julian_date)
t = (sid_day - 0.5) / 36525
sid_reference = (6.6460656 + (2400.051262 * t) + (0.00002581 * (t**2))) / 24
sid_reference -= floor(sid_reference)
lon = 2 * pi * ((dublin_julian_date - sid_day) *
sidereal_solar + sid_reference) - atan2(y, x)
lon = lon % (2 * pi)
lon -= pi
lat = atan(z / sqrt(x**2 + y**2))
return degrees(lat), degrees(-lon)
if __name__ == '__main__':
print("2015-01-01 00:00:00:")
time = datetime(2015, 1, 1, tzinfo=timezone.utc)
x, y, z = earth(JulianDate(utc=time)).observe(sun).apparent().position.au
print(earth_latlon(x, y, z, time))
print("now:")
time = datetime.now(timezone.utc)
x, y, z = earth(JulianDate(utc=time)).observe(sun).apparent().position.au
print(earth_latlon(x, y, z, time))
Output:
2015-01-01 00:00:00:
(-23.05923949080624, -179.2173856294249)
now:
(-8.384551051991025, -47.12917634395421)
As you can see, the values for 2015-01-01 00:00:00 match the reference values from the question. Not precisely, but it's good enough for me. For all I know, my values might be better.
Due to my ignorance about the undocumented magic numbers used in the libastro code, I cannot make this work for bodies other than Earth.
#BrandonRhodes: Let me know if you're interested in having this functionality in Skyfield, then I'll try to throw together a pull request.

GPS Latitude and longitude from hexadecimal value

I'm getting the following replies from a GPS to a microcontroller, rather than try parse the string and convert that to hex to send, I'd rather just use the hex value supplied to then send to my server but I'm having difficulty working out what format it's stored in.
Lat: 37 Deg 39 Min 48.84 Sec S (0xFF94DE3A)
Lon: 144 Deg 58 Min 10.30 Sec E (0x019C5B97)
(another example:)
Lat: 49 Deg 10 Min 21.49 Sec N (0x008BDE6C)
Lon: 123 Deg 4 Min 14.76 Sec W (0xFEA1EE9A)
I've tried answers to other questions with similar titles to no avail..
Lat in the first example looks like -7020998 dec since it's south, similar to Lon in the second example (-22942054) is also negative in the west.
I've tried dividing the numbers by the converted expected result (using degrees + minutes/60 + seconds/3600) I come up with a rough number,
ie:
0xFF94DE3A = -7020998 / -37.66357 = ~186413.502
0x019C5B97 = 27024279 / 144.96953 = ~186413.510
0x008BDE6C = 9166444 / 49.17264 = ~186413.501
0xFEA1EE9A = -22942054 / -123.07077 = ~186413.508
This looks close to 180000, but if I divide by that it doesn't look right (-7020998 / 180000 = -39.00554). What does 186413 relate to though? I feel like I'm missing something completely obvious..
Edit: I'm able to get it working using the below quick example (I know it's not pretty):
if ($GPSLatitude > 0x7FFFFFFF) // ensure correct signedness
$GPSLatitude-=0x100000000;
$GPSf = floatval($GPSLatitude) / 186413.51334561207757602506827277;
$GPSD = floor($GPSf);
$GPSt = abs($GPSf - $GPSD) * 60;
$GPSM = floor($GPSt);
$GPSS = floor(($GPSt - $GPSM) * 6000) / 100;
echo 'GPS Latitude: ' . $GPSD . ' Deg ' . $GPSM . ' Min ' . number_format($GPSS,2,'.','') . ' Sec';
The results match perfectly, but I know there's got to be a better way than dividing by that horrible number?
According to http://en.wikipedia.org/wiki/Geotagging, the GPS coordinates could be set even in rationals:
When stored in EXIF, the coordinates are represented as a series of
rational numbers in the GPS sub-IFD. Here is a hexadecimal dump of the
relevant section of the EXIF metadata (with big-endian byte order):
I am afraid, you have not all hex digits and you see only the upper part of the rational number. And the invisible divisor could be that magic number.
I am not insisting it is so, I only demonstrate there could be ANYTHING hidden in the format.
And you are counting in degrees, and the coord could simply be gaussian X,Y.
Anyway, what is the name of your GPS format?

Dividing N points in a 2-D graph into 2 groups

Suppose there are N points in a 2-D graph.Each point has some weight attached to it.I am required to draw a straight line such a way that the line divides the points into 2 groups such that total weight(sum of weight of points in that group) of part with smaller weight be as many as possible.My task is to find this value.How to go about it ?
Note:No three points lie on the same line.
This is not a homework or part of any contest.
You could just scan over all angles and offsets until you find the optimal solution.
For ease of computation, I would rotate all the points with a simple rotation matrix to align the points with the scanline, so that you only have to look at their x coordinates.
You only have to check half a circle before the scanline doubles up on itself, that's an angle of 0 to PI assuming that you're working with radians, not degrees. Also assuming that the points can be read from the data as some kind of objects with an x, y and weight value.
Pseudocode:
Initialize points from input data
Initialize bestDifference to sum(weights of points)
Initialize bestAngle to 0
Initialize bestOffset to 0
Initialize angleStepSize to an arbitrary small value (e.g. PI/100)
For angle = 0:angleStepSize:PI
Initialize rotatedpoints from points and rotationMatrix(angle)
For offset = (lowest x in rotatedpoints) to (highest x in rotatedpoints)
weightsLeft = sum of the weights of all nodes with x < offset
weightsRight = sum of the weights of all nodes with x > offset
difference = abs(weightsLeft - weightsRight)
If difference < bestDifference
bestAngle = angle
bestOffset = offset
bestDifference = difference
Increment angle by stepsize
Return bestAngle, bestOffset, bestDifference
Here's a crude Paint image to clarify my approach:

Vectors calculations in physics

You are given the radius of a circle, as well as a point P in the circle( x,y), how do you write a function to return an x number of points( x,y), all on the circumference of the given circle. Also, how do you go about finding the angle between each generated point and point P.
I assume you would want the points on the circumference to be evenly distributed along the circumference. If this is the case, you can calculate the number of degrees between each point by dividing 360 by the number of points that you want.
Then, you can obtain any point's (x, y) coordinates as such:
(x, y) = (cos(angle), sin(angle))
where 'angle' the is the angle for the given point. (This is assuming you want values between -1 and 1, as is the case with a unit circle: http://en.wikipedia.org/wiki/Unit_circle) For example, if you want 4 points along the circle's circumference, you can calculate that there is exactly 360/4 = 90 degrees between consecutive points.
So let's call these points point0, point1, point2 and point3. Point0 is at an angle of 0 degrees, point1 at 90 degrees (1 * 90), point2 at 180 (2 * 90) and point3 at 270 (3 * 90). The coordinates for each point are then:
point0 = (cos(0), sin(0)) = (1, 0)
point1 = (cos(90), sin(90)) = (0, 1)
point2 = (cos(180), sin(180)) = (-1, 0)
point3 = (cos(270), sin(270)) = (0, -1)
Keep in mind that you normally start measuring angles on the right side of the horizontal axis of a circle. (On a clock: At the 3)
EDIT: Also please note that almost all trigonometric functions in programming take radian values instead of degrees. Radians can be hard to think with, however, which is why it's very useful to know how to convert radians and degrees to eachother. To convert degrees to radians, multiply the degree value by (pi/180). To convert radians to degrees, multiply the radian value by (180/pi). There is a reasoning behind this all, so if you would like to know more about this, I suggest you read up on radians. http://en.wikipedia.org/wiki/Radian
As far as the angle between these points and the point P goes; I will only give you some directions. You can calculate the x- and y-differences between the points and point P (this should be trivial for you, it consists of mere subtractions). Using these two values, you can calculate the angle between the points.

Determining if a lat-long rect and a circle on a sphere overlap

Suppose I have the following:
A region defined by minimum and maximum latitude and longitude (commonly a 'lat-long rect', though it's not actually rectangular except in certain projections).
A circle, defined by a center lat/long and a radius
How can I determine:
Whether the two shapes overlap?
Whether the circle is entirely contained within the rect?
I'm looking for a complete formula/algorithm, rather than a lesson in the math, per-se.
warning: this can be tricky if the circles / "rectangles" span large portions of the sphere, e.g.:
"rectangle": min long = -90deg, max long = +90deg, min lat = +70deg, max lat = +80deg
circle: center = lat = +85deg, long = +160deg, radius = 20deg (e.g. if point A is on the circle and point C is the circle's center, and point O is the sphere's center, then angle AOC = 40deg).
These intersect but the math is likely to have several cases to check intersection/containment. The following points lie on the circle described above: P1=(+65deg lat,+160deg long), P2=(+75deg lat, -20deg long). P1 is outside the "rectangle" and P2 is inside the "rectangle" so the circle/"rectangle" intersect in at least 2 points.
OK, here's my shot at an outline of the solution:
Let C = circle center with radius R (expressed as a spherical angle as above). C has latitude LATC and longitude LONGC. Since the word "rectangle" is kind of misleading here (lines of constant latitude are not segments of great circles), I'll use the term "bounding box".
function InsideCircle(P) returns +1,0,or -1: +1 if point P is inside the circle, 0 if point P is on the circle, and -1 if point P is outside the circle: calculation of great-circle distance D (expressed as spherical angle) between C and any point P will tell you whether or not P is inside the circle: InsideCircle(P) = sign(R-D) (As user #Die in Sente mentioned, great circle distances have been asked on this forum elsewhere)
Define PANG(x) = the principal angle of x = MOD(x+180deg, 360deg)-180deg. PANG(x) is always between -180deg and +180deg, inclusive (+180deg should map to -180deg).
To define the bounding box, you need to know 4 numbers, but there's a slight issue with longitude. LAT1 and LAT2 represent the bounding latitudes (assuming LAT1 < LAT2); there's no ambiguity there. LONG1 and LONG2 represent the bounding longitudes of a longitude interval, but this gets tricky, and it's easier to rewrite this interval as a center and width, with LONGM = the center of that interval and LONGW = width. NOTE that there are always 2 possibilities for longitude intervals. You have to specify which of these cases it is, whether you are including or excluding the 180deg meridian, e.g. the shortest interval from -179deg to +177deg has LONGM = +179deg and LONGW = 4deg, but the other interval from -179deg to +177deg has LONGM = -1deg and LONGW = 356deg. If you naively try to do "regular" comparisons with the interval [-179,177] you will end up using the larger interval and that's probably not what you want. As an aside, point P, with latitude LATP and longitude LONGP, is inside the bounding box if both of the following are true:
LAT1 <= LATP and LATP <= LAT2 (that part is obvious)
abs(PANG(LONGP-LONGM)) < LONGW/2
The circle intersects the bounding box if ANY of the following points P in PTEST = union(PCORNER,PLAT,PLONG) as described below, do not all return the same result for InsideCircle():
PCORNER = the bounding box's 4 corners
the points PLAT on the bounding box's sides (there are either none or 2) which share the same latitude as the circle's center, if LATC is between LAT1 and LAT2, in which case these points have the latitude LATC and longitude LONG1 and LONG2.
the points PLONG on the bounding box's sides (there are either none or 2 or 4!) which share the same longitude as the circle's center. These points have EITHER longitude = LONGC OR longitude PANG(LONGC-180). If abs(PANG(LONGC-LONGM)) < LONGW/2 then LONGC is a valid longitude. If abs(PANG(LONGC-180-LONGM)) < LONGW/2 then PANG(LONGC-180) is a valid longitude. Either or both or none of these longitudes may be within the longitude interval of the bounding box. Choose points PLONG with valid longitudes, and latitudes LAT1 and LAT2.
These points PLAT and PLONG as listed above are the points on the bounding box that are "closest" to the circle (if the corners are not; I use "closest" in quotes, in the sense of lat/long distance and not great-circle distance), and cover the cases where the circle's center lies on one side of the bounding box's boundary but points on the circle "sneak across" the bounding box boundary.
If all points P in PTEST return InsideCircle(P) == +1 (all inside the circle) then the circle contains the bounding box in its entirety.
If all points P in PTEST return InsideCircle(P) == -1 (all outside the circle) then the circle is contained entirely within the bounding box.
Otherwise there is at least one point of intersection between the circle and the bounding box. Note that this does not calculate where those points are, although if you take any 2 points P1 and P2 in PTEST where InsideCircle(P1) = -InsideCircle(P2), then you could find a point of intersection (inefficiently) by bisection. (If InsideCircle(P) returns 0 then you have a point of intersection, though equality in floating-point math is generally not to be trusted.)
There's probably a more efficient way to do this but the above should work.
Use the Stereographic projection. All circles (specifically latitudes, longitudes and your circle) map to circles (or lines) in the plane. Now it's just a question about circles and lines in plane geometry (even better, all the longitues are lines through 0, and all the latitudes are circles around 0)
Yes, if the box corners contain the circle-center.
Yes, if any of the box corners are within radius of circle-center.
Yes, if the box contains the longitude of circle-center and the longitude intersection of the box-latitude closest to circle-center-latitude is within radius of circle-center.
Yes, if the box contains the latitude of circle-center and the point at radius distance from circle-center on shortest-intersection-bearing is "beyond" the closest box-longitude; where shortest-intersection-bearing is determined by finding the initial bearing from circle-center to a point at latitude zero and a longitude that is pi/2 "beyond" the closest box-longitude.
No, otherwise.
Assumptions:
You can find the initial-bearing of a minimum course from point A to point B.
You can find the distance between two points.
The first check is trivial. The second check just requires finding the four distances. The third check just requires finding the distance from circle-center to (closest-box-latitude, circle-center-longitude).
The fourth check requires finding the longitude line of the bounding box that is closest to the circle-center. Then find the center of the great circle on which that longitude line rests that is furthest from circle-center. Find the initial-bearing from circle-center to the great-circle-center. Find the point circle-radius from circle-center on that bearing. If that point is on the other side of the closest-longitude-line from circle-center, then the circle and bounding box intersect on that side.
It seems to me that there should be a flaw in this, but I haven't been able to find it.
The real problem that I can't seem to solve is to find the bounding-box that perfectly contains the circle (for circles that don't contain a pole). The bearing to the latitude min/max appears to be a function of the latitude of circle-center and circle-radius/(sphere circumference/4). Near the equator, it falls to pi/2 (east) or 3*pi/2 (west). As the center approaches the pole and the radius approaches sphere-circumference/4, the bearing approach zero (north) or pi (south).
How about this?
Find vector v that connects the center of the rectangle, point Cr, to the center of the circle. Find point i where v intersects the rectangle. If ||i-Cr|| + r > ||v|| then they intersect.
In other words, the length of the segment inside the rectangle plus the length of the segment inside the circle should be greater than the total length (of v, the center-connecting line segment).
Finding point i should be the tricky part, especially if it falls on a longitude edge, but you should be able to come up with something faster than I can.
Edit: This method can't tell if the circle is completely within the rectangle. You'd need to find the distance from its center to all four of the rectangle's edges for that.
Edit: The above is incorrect. There are some cases, as Federico Ramponi has suggested, where it does not work even in Euclidean geometry. I'll post another answer. Please unaccept this and feel free to vote down. I'll delete it shortly.
This should work for any points on earth. If you want to change it to a different size sphere just change the kEarchRadiusKms to whatever radius you want for your sphere.
This method is used to calculate the distance between to lat and lon points.
I got this distance formula from here:
http://www.codeproject.com/csharp/distancebetweenlocations.asp
public static double Calc(double Lat1, double Long1, double Lat2, double Long2)
{
double dDistance = Double.MinValue;
double dLat1InRad = Lat1 * (Math.PI / 180.0);
double dLong1InRad = Long1 * (Math.PI / 180.0);
double dLat2InRad = Lat2 * (Math.PI / 180.0);
double dLong2InRad = Long2 * (Math.PI / 180.0);
double dLongitude = dLong2InRad - dLong1InRad;
double dLatitude = dLat2InRad - dLat1InRad;
// Intermediate result a.
double a = Math.Pow(Math.Sin(dLatitude / 2.0), 2.0) +
Math.Cos(dLat1InRad) * Math.Cos(dLat2InRad) *
Math.Pow(Math.Sin(dLongitude / 2.0), 2.0);
// Intermediate result c (great circle distance in Radians).
double c = 2.0 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1.0 - a));
// Distance.
// const Double kEarthRadiusMiles = 3956.0;
const Double kEarthRadiusKms = 6376.5;
dDistance = kEarthRadiusKms * c;
return dDistance;
}
If the distance between any vertex of the rectangle is less than the distance of the radius of the circle then the circle and rectangle overlap. If the distance between the center of the circle and all of the vertices is greater than the radius of the circle and all of those distances are shorter than the width and height of the rectangle then the circle should be inside of the rectangle.
Feel free to correct my code if you can find a problem with it as I'm sure there some condition that I have not thought of.
Also I'm not sure if this works for a rectangle that spans the ends of the hemispheres as the distance equation might break down.
public string Test(double cLat,
double cLon,
double cRadius,
double rlat1,
double rlon1,
double rlat2,
double rlon2,
double rlat3,
double rlon3,
double rlat4,
double rlon4)
{
double d1 = Calc(cLat, cLon, rlat1, rlon1);
double d2 = Calc(cLat, cLon, rlat2, rlon2);
double d3 = Calc(cLat, cLon, rlat3, rlon3);
double d4 = Calc(cLat, cLon, rlat4, rlon4);
if (d1 <= cRadius ||
d2 <= cRadius ||
d3 <= cRadius ||
d4 <= cRadius)
{
return "Circle and Rectangle intersect...";
}
double width = Calc(rlat1, rlon1, rlat2, rlon2);
double height = Calc(rlat1, rlon1, rlat4, rlon4);
if (d1 >= cRadius &&
d2 >= cRadius &&
d3 >= cRadius &&
d4 >= cRadius &&
width >= d1 &&
width >= d2 &&
width >= d3 &&
width >= d4 &&
height >= d1 &&
height >= d2 &&
height >= d3 &&
height >= d4)
{
return "Circle is Inside of Rectangle!";
}
return "NO!";
}
One more try at this...
I think the solution is to test a set of points, just as Jason S has suggested, but I disagree with his selection of points, which I think is mathematically wrong.
You need to find the points on the sides of the lat/long box where the distance to the center of the circle is a local minimum or maximum. Add those points to the set of corners and then the algorithm above should be correct.
I.e, letting longitude be the x dimension and latitude be the y dimension, let each
side of the box be a parametric curve P(t) = P0 + t (P1-P0) for o <= t <= 1.0, where
P0 and P1 are two adjacent corners.
Let f(P) = f(P.x, P.y) be the distance from the center of the circle.
Then f (P0 + t (P1-P0)) is a distance function of t: g(t). Find all the points where the derivative of the distance function is zero: g'(t) == 0. (Discarding solutions outsize the domain 0 <= t <= 1.0, of course)
Unfortunately, this needs to find the zero of a transcendental expression, so there's no closed form solution. This type of equation can only solved by Newton-Raphson iteration.
OK, I realize that you wanted code, not the math. But the math is all I've got.
For the Euclidean geometry answer, see: Circle-Rectangle collision detection (intersection)