MySQL self-join to get two geographically closest items with different properties? - mysql

We have a single table of weather markers on a map, where each marker type is either wind or cloud.
For each cloud marker we want to find the "WindDir" field value for the closest wind marker. It seems like a self-join would do the trick, but constructing the query is proving difficult.
Below is our incorrect pseudo SQL. We know the Distance value will be what we want. We just need to how to make the query find the closest wind marker for each cloud marker, so that we can get the wind marker's "WindDir" value.
SELECT w1.ID AS ID, w1.Lat AS Lat, w1.Lng AS Lng,
w2.WindDir AS WindDir,
MIN(SQRT(POWER(ABS(w1.Lng - w2.Lng), 2) + POWER(ABS(w1.Lat - w2.Lat), 2))*60) AS Distance
FROM Weather w1 WHERE w1.Marker="Cloud"
LEFT JOIN Weather w2 WHERE w2.Marker="Wind"
USING ID
We'd be grateful for any advice on making a version that works!
-Ken

You're pretty close (basically just reordered for syntax)
SELECT w1.ID AS ID, w1.Lat AS Lat, w1.Lng AS Lng,
w2.WindDir AS WindDir,
(SQRT(POWER(ABS(w1.Lng - w2.Lng), 2) + POWER(ABS(w1.Lat - w2.Lat), 2))*60) AS Distance
FROM Weather w1 LEFT JOIN Weather w2 USING (ID)
WHERE w1.Marker="Cloud" AND w2.Marker="Wind"
ORDER BY Distance DESC
LIMIT 1
Assuming you just want the closest result, I added a LIMIT 1 to restrict what's returned.
On a side note, this method of finding distance between two lat/lon pairs is unlikely to give you good results for even fairly small changes. There are methods for approximating these distances...

Related

Django: Filter on Annotated Value

I have a situation where I have a model called trip. Each trip has a departure_airport and an arrival_airport which are related fields and both part of the airport model. Each object in the airport model has a location represented by latitude and longitude fields.
I need to be able to take inputs from two (potentially) separate departure and arrival airport locations using something like the Haversine formula. That formula would calculate the distance from each departure/arrival airport in the database to the location of the airports that have been taken as input.
The difficult part of this query is that I annotate the trip queryset with the locations of the departure and arrival airports, however because there's two sets of latitude/longitude fields (one for each airport) with the same name and you can't use annotated fields in a sql where clause, I'm not able to use both sets of airports in a query.
I believe the solution is to use a subquery on the annotated fields so that query executes before the where clause, however I've been unable to determine if this is possible for this query. The other option is to write raw_sql.
Here's what I have so far:
GCD_FORMULA_TO = """3961 * acos(
cos(radians(%s)) * cos(radians(arrival_lat))
* cos(radians(arrival_lon) - radians(%s)) +
sin(radians(%s)) * sin(radians(arrival_lat)))"""
GCD_FORMULA_FROM = """3961 * acos(
cos(radians(%s)) * cos(radians(departure_lat))
* cos(radians(departure_lon) - radians(%s)) +
sin(radians(%s)) * sin(radians(departure_lat)))"""
location_to = Q(location_to__lt=self.arrival_airport_rad)
location_from = Q(location_from__lt=self.departure_airport_rad)
qs = self.queryset\
.annotate(arrival_lat=F('arrival_airport__latitude_deg'))\
.annotate(arrival_lon_to=F('arrival_airport__longitude_deg'))\
.annotate(departure_lat=F('departure_airport__latitude_deg'))\
.annotate(longitude_lon=F('departure_airport__longitude_deg'))\
.annotate(location_to=RawSQL(GCD_FORMULA_TO, (self.arrival_airport.latitude_deg, self.arrival_airport.longitude_deg,
self.arrival_airport.latitude_deg)))\
.annotate(location_from=RawSQL(self.GCD_FORMULA_FROM, (self.departure_airport.latitude_deg, self.departure_airport.longitude_deg,
self.departure_airport.latitude_deg)))\
.filter(location_to and location_from)
return qs
Any ideas? Also open to other ways to go about this.
You're doing this the hard way.
If your python code has a pair of locations, use this:
from geopy.distance import distance
loc1 = (lat1, lng1)
loc2 = (lat2, lng2)
d = distance(loc1, loc2).km
If you're querying a database, perhaps you would prefer that it runs PostGIS / Postgres, rather than mysql,
so you can compute distance and shape membership.
The syntax sometimes is on the clunky side, but the indexing works great.
Here is an example for departing from London Heathrow:
SELECT a.airport_name,
ST_Distance('SRID=4326; POINT(-0.461389 51.4775)'::geography,
ST_Point(a.longitude, a.latitude)) AS distance
FROM arrival_airports a
ORDER BY distance;
As a separate matter, you might consider defining an arrival and/or departure VIEW on your table, and then JOIN, with a distinct model for each view.

Search a rectangle on googlemaps

We have Locations data in Sql server database. Four points (bounding rectangle) are defined for each Location. Four points are Bottom Latitude, Top Latitude, Left Longitude, Right Longitude. If googlemaps falls within any area of the bounding rectangle, a marker will be plotted for that location.
We have some other locations in the database for which we have stored only Lat Lng and we are searching them using below query and it works!
SELECT *
FROM SomeOtherLocationsTable
WHERE LocationLatitude >= #bottomlat AND
LocationLatitude <= #toplat AND
LocationLongitue >= #leftlang AND
LocationLongitue <= #rightlng
How we can search locations having four points defined for them?
Use STIntersects
Instead of Numbers you should store geometries (then you may use the same query for POINTS and POLYGONS)
After some R & D, we were able to find below solution and works for us (we need the functionality for USA locations only):
SELECT *
FROM LocationsTable
WHERE
NorthEastLongitudeRectangle < #SouthWestLongitudeRectangle OR
SouthWestLongitudeRectangle > #NorthEastLongitudeRectangle OR
NorthEastLatitudeRectangle < #SouthWestLatitudeRectangle OR
SouthWestLatitudeRectangle1 > #NorthEastLatitudeRectangle
We are still testing it, but hopefully it will work!

How to get all the stores available within a bounding box in PHP?

I have a table containing 'places', each place with a name and lat, lng coordinates. Given a bounding box of coordinates (lat/lng perimeters), what is an efficient way to retrieve only the places within that box? . I have the coordinates of all the corners of the map now, I want to get all the stores within that rectangle.
I want this to be done in PHP and MySQL. I am trying to use a BETWEEN query, but unable to find the stores. My code is as follows:
$sql = "
SELECT * from myTable
WHERE lon BETWEEN '$west_lon' AND '$east_lon'
AND lat BETWEEN '$south_lat' AND '$north_lat'
";
Hi At last i Solved My Problem. There was a silly mistake in Query.
$sql="SELECT * FROM `MyTable` WHERE (longitude BETWEEN '$west_long' AND '$east_long') AND (lat BETWEEN '$north_lat' AND '$south_lat')";
if anyone finds a better way please Post here. I don't know whether its Correct or not.

Google map display markers by hours and location

I am trying to implement a recovery community meeting finder. I have a database and map setup. I am trying to add a variable to display the current day's meetings that is also based upon a "nearest" location priority. How do I get the today's date variable in my database to selectively display only that days meetings? I'm using google maps api.
Thanks,
Terry
In your database you have a datetime field that you store the date and time of the meetings I am guessing.
When you pull the information from your database to put in the map, simply use whichever apprrpriate sql language to select only those records that match the date you need.
This is actually more of a sql problem.
The closest location may not be catered for, but what you need to do is also a sql question. You need to add lat and long fields to your database and store the lat/longs for each of the meeting locations.
Then you can do a distance based sql search once you have those and the lat/long of the user.
Maps doesn't come into the selection process at all.
...
Edit.
The selecting by time is fairly simple but I thought I would share a distance based sql SELECT I used a while back. Note. It was used with MySQL but I think it should work with almost any sql db.
"SELECT name, tel, (((acos(sin((".$latitude."*pi()/180)) * sin((`latitude`*pi()/180))+cos((".$latitude."*pi()/180)) * cos((`latitude`*pi()/180)) * cos(((".$longitude."- `longitude`)*pi()/180))))*180/pi())*60*1.1515) AS distance FROM takeaway WHERE active AND (((acos(sin((".$latitude."*pi()/180)) * sin((`latitude`*pi()/180))+cos((".$latitude."*pi()/180)) * cos((`latitude`*pi()/180)) * cos(((".$longitude."- `longitude`)*pi()/180))))*180/pi())*60*1.1515) < 10 AND tel AND type = 'transport/taxi' ORDER BY distance LIMIT 5"
That gives you the basics for editing and reusing. Just remember to add the time/date check into your final string.

lat lng distances automated from fixed positions to lan lng in database ( australia )

Ok this is a big ask, I have been reading a lot about this on here and over the tinternet.
The best I have found is : this link
Now Here is our issue ( this is just for Australian distances )
We have a database of lat and lng values for every single suburb in Australia.
My question is ... is there a way we could run some kind of script pinging our servers, and storing the results in a large db.
So a bit like:
record a: lat lng -> record b: lat lng ( do calculation and store distance in record )
record a: lat lng -> record b: lat lng ( do calculation and store distance in record )
and so on ( and do not do record b: lat lng -> record a: lat lng ( as this is already known )
At the moment the easiest way of accessing this data is via real world names such as suburb state and postcode, which we have within the db along with the lat lng values.
Those are really superfluous as each data entry has an id. similar to my example above.
The objective is to get :
ID 1 -> ID2 distance =:75 km
Where ID1 = -27.4561899, 153.3564564 ( known name Raby Bay QLD 4163 )
and ID2 = -27.1054534, 152.948145 ( known name Morayfield QLD 4560 )
do you get where I am going with this...
So my question is does anyone know of a way we can run an automated script, that will grab the ID then the next ID record and perform a function and save the result to the db and then do the next ... so on and so forth.
We have extremely fast servers, and there are some 13000 individual records ( suburbs ) for us to pit against each other..
Which will give us approximately 84,506,500 results ( .ish )
obviously we would need to do calculations for the same ID because then we can echo 0 km
So any suggestions, has anyone seen or done anything like this before ??
Is there a better way, we would ideally like to do this server side ( and store results ) then spew them out on page load...
Because this is for members site and advertised items.. so we know:
Advertisers location and Vistiors location so would want to spew out the distance from buyer to seller on the fly. Without running large client side code.
Ste
The calculations are not really that difficult -- certainly not worth precomputing 84M+ rows in a table, where almost certainly very few of them will ever be used.
The only time I have ever needed to precompute a lot if data like this is to store a list of "closest locations" for each location in the database, and that can be done with a much smaller table. (and even that isn't always necessary, as you can sometimes get the same data with a bounding box query)
If you're set on this architecture, have you considered using the table as a cache, to store the results as people ask for them, and them having it be the first place that your application looks for an answer when a new query arrives?