I see many solutions for getting the nearest rows from a POINT to convert to X() and Y() and do trig calculations of distances... As I understand, this does not seem to take advantage of the spatial index?
How do you take advantage of the spatial index, in the most common sense of, returning rows whose spatial POINT is within a radius from a center POINT?
In other words, how do you get something like this - where LatLng is the lat lng location stored as POINT for each row, and CenterPoint the epicenter
Pseudocode query: SELECT * FROM geotable WHERE d=Distance(LatLng,CenterPoint) < 10 ORDER by d
You can use st_distance_sphere
SELECT *
FROM geotable
WHERE st_distance_sphere(POINT(-82.337036, 29.645095 ), POINT(`longitude`, `latitude` ))/1000 < 10
Here you can see a working example
Related
I have MySQL table with 500 location records that has a similar structure to:
id, name, lat, long
Lat & long are decimal (float) location values.
My need is to return a random 100 record set that are a minimum 200 meters and a maximum 500 meters away from each other. I'm familiar with using the great circle formula to get the distance between two points. However, I have no idea how to write a select statement to compare all locations against each other to ensure the distance requirements for the random 100 selected? Any thoughts or help would be greatly appreciated. My only tools are a MySQL database so the solution needs to be written in MySQL SQL. Thank you in advance.
SELECT *
FROM (
SELECT p.latitude1 AS latitude1, p.longitude1 AS longitude1, p.latitude2 AS latitude2, p.longitude2 AS longitude2,
(((ACOS(SIN((latitude2*PI()/180)) * SIN((latitude1*PI()/180))+COS((latitude2*PI()/180)) * COS((latitude1*PI()/180)) * COS(((longitude2- longitude1)* PI()/180))))*180/PI())*60*1.1515) AS distance
FROM places p
)
WHERE distance > 200 AND distance < 500
ORDER BY RAND()
LIMIT 100
If you are only looking at 500 metres then take some short cuts and pretend this is an xy space and use simple trigonometry. Can you even get away with a square instead of donut ?
You could use that as the first cut and then to proper maths on the remainder.
Your second cut will now be small enough. Assign a random no to each and take top X. Voila
edit For the first part, cheat and use Google maps. Find out what fraction of a degree equates to 500 metres at the equator. Use x +/- that value and the same for y. Quick and dirty first cut. **ok it won't work at the poles! **
I have a SQL database set of places to which I am assigned coordinates (lat, long). I would like to ask those points that lie within a radius of 5km from my point inside. I wonder how to construct a query in a way that does not collect unnecessary records?
Since you are talking about small distances of about 5 km and we are probably not in the direct vicinity of the north or south pole we can work with an approximated grid system of longitude and latitude values. Each degree in latidude is equivalent to a distance of km_per_lat=6371km*2*pi/360degrees = 111.195km. The distance between two longitudinal lines that are 1 degree apart depends on the actual latitude:
km_per_long=km_per_lat * cos(lat)
For areas here in North Germany (51 degrees north) this value would be around 69.98km.
So, assuming we are interested in small distances around lat0 and long0 we can safely assume that the translation factors for longitudinal and latitudinal angles will stay the same and we can simply apply the formula
SELECT 111.195*sqrt(power(lat-#lat0,2)
+power(cos(pi()/180*#lat0)*(long-#long0),2)) dist_in_km FROM tbl
Since you want to use the formula in the WHERE clause of your select you could use the following:
SELECT * FROM tbl
WHERE 111.195*sqrt(power(lat-#lat0,2)
+power(cos(pi()/180*#lat0)*(long-#long0),2)) < 5
The select statement will work for latitude and longitude values given in degree (in a decimal notation). Because of that we have to convert the value inside the cos() function to radians by multiplying it with pi()/180.
If you have to work with larger distances (>500km) then it is probably better to apply the appropriate distance formula used in navigation like
cos(delta)=cos(lat0)*cos(lat)*cos(long-long0) + sin(lat0)*sin(lat)
After calculating the actual angle delta by applying acos() you simply multiply that value by the earth's radius R = 6371km = 180/pi()*111.195km and you have your desired distance (see here: Wiki: great circle distance)
Update (reply to comment):
Not sure what you intend to do. If there is only one reference position you want to compare against then you can of course precompile your distance calculation a bit like
SELECT #lat0:=51,#long0:=-9; -- assuming a base position of: 51°N 9°E
SELECT #rad:=PI()/180,#fx:=#rad*6371,#fy:=#fx*cos(#rad*#lat0);
Your distance calculation will then simplify to just
SELECT #dist:=sqrt(power(#fx*(lat-#lat0),2)+power(#fy*(long-#long0),2))
with current positions in lat and long (no more cosine functions necessary). It is up to you whether you want to store all incoming positions in the database first or whether you want to do the calculations somewhere outside in Spring, Java or whatever language you are using. The equations are there and easy to use.
I would go with Euklid. dist=sqrt(power(x1-x2,2)+power(y1-y2,2)) . It works everywhere. Maybe you have to add a conversion to the x/y-coordinates, if degrees can't be translated in km that easy.
Than you can go and select everything you like WHERE x IS BETWEEN (x-5) AND (x+5) AND y IS BETWEEN (y-5) AND (y+5) . Now you can check the results with Euklid.
With an optimisation of the result order, you can get better results at first. Maybe there's a way to take Euklid to SQL, too.
My query is
SELECT A.broker_property_id, A.broker_owner_id
FROM property_requirement_new AS A,
(SELECT X(latlong), Y(latlong)
FROM client_property_new
WHERE property_id = 132) AS B
WHERE (POW((A.X(latlong)-B.X(latlong))*111.12, 2) + POW((A.Y(latlong) - B.Y(latlong))*111.12, 2)) <= 4
Here, latlong is a field of mysql's POINT datatype.
But this query is not being executed and is showing the following error:
FUNCTION a.X does not exist
Can anyone help me out with the correct method to do it or to spot the error in my code?
X and Y are functions that take a Point (geometry) as an input. It is not a function of table A, as you have written it, ie, A.X(latlong) is throwing an error as it implies that X is a function of table A.
You would need to write
select X(latlon), Y(latlon) from A
or in your case, if you are using subqueries:
select X(A.latlon), Y(A.latlong) from (.....) A
There is apparently an ST_Distance function in MySQL 5.6, but I have never used it and it seems somewhat undocumented.
There is some discussion of distance functions in MySQL here: Fastest Way to Find Distance Between Two Lat/Long Points One of the general problems of spatial in MySQL is that it does not support projections properly, so you are either left doing Pythagoras on planar coordinates, or implementing some version of the haversine formula and assuming the world is a sphere -- which will work well for short distances if massive precision is not your biggest concern.
I have the following latitude and longitude:
lat - 18.9802767
lng - 72.8142511
I am trying the following query for places withint 10 kms from the point of interest.
select mbrcontains( geomfromtext(
'LINESTRING(72.8993663648088 19.0702857,72.72913583519122 18.8902677)'
),
geomfromtext(
'point(18.9802767 72.8142511)'
) );
The Linestring geometry object is derived from the exact point that I am trying to determine is within using the method for mysql 5.1 and above from this example using the formula :
linestring(point(#lng+10/(111.1/cos(radians(#lat))),#lat+10/111.1), point(#lng-10/(111.1/cos(radians(#lat))),#lat-10/111.1))
From what I understand the point falls within the Minimum Bounding Rectangle (MBR). However the query returns a 0 for the answer. The above is following the principles given in this example.
What is wrong with the query? How can I know if the point is within a certain geospatial distance (in this case the MBR points are calculated using 10 kms from the point given by co-ordinates: lat - 18.9802767, lng - 72.8142511).
BTW, I am using MySQL 5.5.32.
Your point does not fall within the MBR of the line. Looks like you've reversed the latitude or longitude coordinates on either the line or the point. Switch the X and Y in 'point(18.9802767 72.8142511)' to get this point, which will be within the MBR of the line:
POINT (72.8142511 18.9802767)
If you are tied to MySQL, you may consider updating to MySQL 5.6, then using the Buffer function to create a circular area from your point of interest, and then use ST_Within to find anything within that circular area.
If you are not tied to MySQL, consider PostGIS, which provides a nice ST_DWithin function that makes these comparisons very easy.
I am planning a website (Drupal/MySQL), which must search a fairly large database based on distance from a location (we're starting with ~20,000 locations). So far, the best solution I've found to searching in a reasonable manner is to use a user-defined function in SQL to calculate the distance between to coordinates, e.g.:
SELECT *, CoordinateDistanceMiles(lat, lon, ${inputLat}, ${inputLon}) as distance
FROM items WHERE distance < {$radius}
(Using John Dyer's distance function or similar)
However, I've also read that UDFs are very inefficient. My second idea (and tentative plan) is to nest another query inside this one to narrow its' scope and therefore run the UDF on a much smaller subset of items, e.g.:
SELECT *, CoordinateDistanceMiles(lat, lon, ${inputLat}, ${inputLon}) as distance
FROM (
SELECT * FROM items WHERE
lat BETWEEN ${inputLat - const} AND ${inputLat + const} AND
lon BETWEEN ${inputLon - const} AND ${inputLon + const}
) WHERE distance < ${radius}
Would this model make the search faster, or just more convoluted? Are there any better solutions?
The overhead of using UDF here is negligible, as long as you perform scan over distance < ${radius} and have 2 range-based comparisons (they cannot be optimized with indexes).
So don't worry about UDF "inefficiency" and use it, since it is much more readable.