one of the value updated to a field in a mysql table is 'location' of type 'point'.
When we select the field
select *,AsText(location) from somtable
How can we use this field in our script ? a we need to find the radius or the distance between 2 points.
Usually, you'll want to return the distance between the two locations as another field, not the location, because this is the point itself and not a distance. Something like:
For example:
$sql = 'SELECT *, ( 3959 * acos( cos( radians(' . $lat1 . ') ) * cos( radians(location.lat) ) * cos( radians(location.lng) - radians(' . $lng1 . ') ) + sin( radians(' . $lat1 . ') ) * sin( radians(location.lat) ) ) ) AS distanceFromUser FROM somtable WHERE....';
But if you're rolling with a Point datatype, then use some MySQL spatial functions instead. One of the popular ones is MBRContains. This makes your query something like:
select * from somtable where
MBRContains(
GeomFromText('Polygon((swLat swLon, neLat swLon, neLat neLon, swLat neLon, swLat swLon))'), location );
The other coordinates are specifying a bounding box in which your location Point must exist. So in the second case, you are just returning points inside the box. In the first case, you are returning the distance to every point in your table.
This link allows for a detailed example for radius:
http://howto-use-mysql-spatial-ext.blogspot.in/2007/11/using-circular-area-selection.html
It builds on #dRocking's example of MBRContains, and also explains how to build the polygon from a given point.
Related
I have an array of Lats and Longs that i would like to sort by their distance between each other. I dont have a start or an end lat and long, i just want to sort the array by ascending distance.
What i have doesnt work because i dont have the $lat and $long.
SELECT DISTINCT dist.lat,
dist.lon,
( 111.045 * Degrees(Acos(Cos(Radians($lat)) * Cos(
Radians(dist.lat)) * Cos(
Radians(dist.lon) -
Radians($lon)) +
Sin(Radians($lat)) * Sin(
Radians(dist.lat)))) )AS
distance_in_km
FROM dist
ORDER BY distance_in_km ASC;
What I have:
[[23.454445,-12.33342],[34.45665,-34.555566],[21.222233,-43.444453],
[23.444555,-44.556666],[32.445554,-33.44555788],[23.445598,-67.88909],[54.556677,-66.776655]]
I want to sort this array by proximity.
P.S. These coordinates are not real ones.
Is it possible to search nearest places in mysql by using Bookshelf.js, or Knex? And, if yes, how to do it correct?
UPD:
I'm not strong with raw mysql queries, and can't find how to search with lat, lng in Bookshelf and knex.
For example, i have places table with:
title
address
lat
lng.
What query i should use to get all places in 10 miles radius from Point(lat, lng)?
thanks.
After some research i found working solution:
const {lat, lng} = req.params;
const X1 = +lat - 10,
Y1 = +lng - 10,
X2 = +lat + 10,
Y2 = +lng + 10;
const distanceInMilesSql = `( 3959 * acos( cos( radians(${+lat}) )
* cos( radians( place.lat ) )
* cos( radians( place.lng ) - radians(${+lng}) )
+ sin( radians(${+lat}) )
* sin( radians( place.lat ) ) ) ) AS distance
`;
const places = await new Place().query(qb => {
qb.whereBetween('lat', [X1, X2]);
qb.whereBetween('lng', [Y1, Y2]);
qb.column(['*', Bookshelf.knex.raw(distanceInMilesSql)]);
qb.having('distance', '<', 10);
}).fetchAll({withRelated: [
'place_specific_fields',
'state',
'media'
]});
Demo
http://rextester.com/NDOR4479
SQL
SELECT *,
ACOS(SIN(RADIANS(:lat)) * SIN(RADIANS(lat)) + COS(RADIANS(:lat)) * COS(RADIANS(lat))
* COS(RADIANS(lng - :lng))) * 3959 AS distance
FROM places
WHERE ACOS(SIN(RADIANS(:lat)) * SIN(RADIANS(lat)) + COS(RADIANS(:lat)) * COS(RADIANS(lat))
* COS(RADIANS(lng - :lng))) * 3959 <= 10
ORDER BY distance;
Notes
The above uses the Spherical Law of Cosines where :lat and :lng are the latitude and longitude values from Point and 3959 is the Earth radius in miles.
If it helps anyone, here's some code I use (Strapi + MySQL):
Query for events
Join locations into events, where id matches
Gets anything within 5km (5000 meters) of the user
On a side note: As a Strapi user, a raw query like this negates the high-level functionality that Strapi auto-builds for the events model. ie Strapi url filtering like &Cost_gte=20 won't work. I'm still working on how to get around this and avoid having to build an entire custom controller from scratch (all just to have distance filtering?).
Code:
const knex = strapi.connections.default
const result = await knex('events')
.join('locations', 'events.Location', 'locations.id')
.where(knex.raw(
`round(st_distance_sphere(
st_geomfromtext(CONCAT('POINT(',locations.Longitude, ' ',locations.Latitude,')')),
st_geomfromtext(CONCAT('POINT(` + ctx.query.Longitude + ` ` + ctx.query.Latitude + `)'))
)) <= 5000`
))
return result
You don't make it clear in your question, but I believe that the Bookshelf.js library works as a layer above PostgreSQL, which means that you have access to geometric data types. These can make use of these special operations. The search could be entirely SQL based, so without further context I can simply offer the following:
Combine the latitude and longitude into a single data piece of type point (using the decimal form of the latitude and longitude). Here I will refer to that as coord.
With the proper data in order, you can do a query of:
SELECT p.*, (point'(#origin_long,$origin_lat)' <-> `coord`) distance
FROM `places` p
WHERE circle '((#origin_long, #origin_lat),#radius)' #> `coord`
ORDER BY distance;
If the data structures are set right, this should return a list, ordered by distance, of all pairs of coordinates within a given radius. You can set the coordinates at the top of this function, or maybe even wrap it in a stored procedure (if you don't mind the overhead).
For further reading, I know that PostgreSQL has an "Earth Distance" function as well, so if you are looking to include the calculation of true distance traveled between points, it might be useful!
I'm working on a task as part of a project which finds the places within a certain distance of a particular (lat, long) point. I know the way to get the result for a single row in my table which has the (Latitude, Longitude) point and the corresponding distance (given by Coverage_Norm_10km, in my case).
My database table (Sorted_Range_Cap_Data) is of the following form:
Place , Population, Latitude, Longitude, Altitude, Bandwidth_Required, Coverage_Range, Throughput_Range, Coverage_Norm_10km, Throughput_10km
SELECT
Place, (
6371 * acos (
cos ( radians(17.741150) )
* cos( radians(Latitude) )
* cos( radians(Longitude) - radians(73.149712) )
+ sin ( radians(17.741150) )
* sin( radians(Latitude) )
)
) AS Coverage_Norm_10km
FROM Sorted_Range_Cap_Data
HAVING Coverage_Norm_10km < (The current row's "Coverage_Norm_10km" value)
ORDER BY Coverage_Norm_10km
LIMIT 0, 20;
(Source : https://gis.stackexchange.com/questions/31628/find-points-within-a-distance-using-mysql)
Here, (17.741150, 73.149712) is a (lat, long) point in the first row of my database table. I want to find the set of all places which are within "Coverage_Norm_10km" distance from the (lat, long) point.
I need to repeat this for each row in the table.
Finally, I want to end up with the list of places for each (lat, long) point (every row) in my table.
Any help would be highly appreciated.
I did something similar using the Haversine formula in nodejs, here's my raw query:
var query = "SELECT id, name, latitude, longitude, ( 3959 * acos( cos( radians(" + req.query.latitude + ") ) * cos( radians( latitude ) ) " +
" * cos( radians( longitude ) - radians(" + req.query.longitude + ") ) + sin( radians(" + req.query.latitude + ") ) * sin(radians(latitude)) ) ) AS distance " +
" FROM account " +
" HAVING distance < 10 " +
" ORDER BY distance " +
" LIMIT 0 , 20;";
My accounts table stores each record's latitude/longitude. This would return all accounts within 10 miles of my requested lat/lng.
We have a large location DB - with lat long specified for each row. The DB is hosted in MySQL.
We need to run two type of queries:
places nearby (sort by distance)
places nearby by category (where category is a column)
With the number of records growing, this query seems to slow down drastically.
SELECT *, ( 3959 * acos( cos( radians(40.759105) ) * cos( radians( Latitude ) ) * cos( radians( longitude) - radians(-73.984654) ) + sin( radians(40.759105) ) * sin( radians( Latitude ) ) ) ) as distance FROM mcw_in WHERE Latitude <> '' ORDER BY distance LIMIT 0,20
How can I create an index in MySQL to address the slowness? Is there any other solution - like using any geospatial data types?
MySQL Manual :: Introduction to MySQL Spatial Support
MySQL Manual :: Creating Spatial Indexes
But really this won't work in MySQL since they haven't really implemented the functions.
If you are open to it, I would reccomend using PostGIS or Spatialiate (running on Postgresql and SQLLite respectively) or even mongodb or geocouch. These have a much larger suite of implemented spatial functions. If you look at the MySQL documentation it mostly says "not implemented" for the spatial functions.
It is better to use range queries by defining a bounding box surrounding the center. The following query searches the nearest 20 locations within distance $dist from the center ($lat0, $lng0), with the result sorted by the distance. You need two indexes, one on 'lat' and one on 'lng'. Some explanations can be found here.
SELECT *,
( 6371 * acos(
cos(radians($lat0)) * cos(radians(lat)) * cos(radians(lng) - radians($lng0)) +
sin(radians($lat0)) * sin(radians(lat))
) ) AS distance
FROM `locations`
WHERE lat < degrees( asin( sin(radians($lat0)) * cos($dist / 6371) +
cos(radians($lat0)) * sin($dist / 6371) * cos(radians(0)) ))
AND lat > degrees( asin( sin(radians($lat0)) * cos($dist / 6371) +
cos(radians($lat0)) * sin($dist / 6371) * cos(radians(180)) ))
AND lng < $lng0 - degrees( atan2(sin(radians(90)) * sin(radians($dist / 6371)) * cos(radians($lat0)),
cos(radians($dist / 6371)) - sin(radians($lat0)) * sin(radians($lat0))) )
AND lng > $lng0 + degrees( atan2(sin(radians(90)) * sin(radians($dist / 6371)) * cos(radians($lat0)),
cos(radians($dist / 6371)) - sin(radians($lat0)) * sin(radians($lat0))) )
ORDER BY distance LIMIT 20;
I'm just playing around with a dataset of my region generated by JOSM. I moved it into a mySQL DB with the 0.6 API scheme using Osmosis and now I'm desperately trying the following:
I want to get all streets of a city.
AFAIK there is no tag/relation in the OSM data to determine this so I tried it using a proximity search to get all nodes in a radius around a node representing the city center.
Most of the time I looked at the approaches here
What I got is the following SQL code that should get me the closest 100 nodes around the node with id 36187002 and within a radius of 10km.
set #nodeid = 36187002;
set #dist = 10;
select longitude, latitude into #mylon, #mylat from nodes where id=#nodeid limit 1;
SELECT id, ( 6371 * acos( cos( radians(#mylon) ) * cos( radians( latitude ) ) *
cos( radians( longitude ) - radians(#mylat) ) + sin( radians(#mylon) ) * sin( radians( latitude ) ) ) )
AS distance
FROM nodes HAVING distance < #dist ORDER BY distance LIMIT 0 , 100;
Well.. it doesn't work. :( I guess the main problem is that OSM lats/lons are multiplied by 10.000.000 and I don't know how I can correct this function to make it work.
Any ideas about this? All solutions/alternatives are very welcome!
It may be faster to add extra columns to your table for the latitude and longitude expressed as the double data type (so the trigonometric functions stand a chance) - you may want to go further and precalculate the xaxis, yaxis and zaxis as columns (again, stored as double)
So, your new columns are loosely (you may need to add data type conversions as required):
XAxis = cos(radians(Latitude / 10000000)) * cos(radians(Longitude / 10000000))
YAxis = cos(radians(Latitude / 10000000)) * sin(radians(Longitude / 10000000))
ZAxis = sin(radians(Latitude / 10000000))
Then, your proximity search becomes:
set #nodeid = 36187002;
set #dist = 10;
SELECT XAxis, YAxis, ZAxis
INTO #CntXAxis, #CntYAxis, #CntZAxis
FROM nodes
WHERE id=#nodeid limit 1;
SELECT id, ( 6371 * acos(
CASE
WHEN nodes.XAxis * #CntXAxis
+ nodes.YAxis * #CntYAxis
+ nodes.ZAxis * #CntZAxis > 1.0 THEN 1.0
ELSE nodes.XAxis * #CntXAxis
+ nodes.YAxis * #CntYAxis
+ nodes.ZAxis * #CntZAxis
END
) AS Distance
FROM nodes
HAVING Distance < #dist
ORDER BY distance LIMIT 0 , 100;
I modified the query a little and it works.
Here my code:
set #nodeid = 122317;
set #dist = 10;
select lon, lat into #mylon, #mylat from nodes where id=#nodeid limit 1;
SELECT id, ( 6371 * acos(
sin(radians(#mylat)) * sin(radians(lat)) +
cos(radians(#mylat)) * cos( radians(lat)) *
cos(radians(lon) - radians(#mylon))
))
AS distance
FROM nodes having distance <#dist
i´ve got the formula from the german wikipedia and it works fine. I've had in in some ruby code fist, but its also work as an sql-query.
To select some special nodes i added this
(select nodes.id,lat,lon,k,v from nodes join node_tags on nodes.id=node_tags.id where k='public_transport') as stations
as the FROM condition to specify the tags of the nodes. (Of course it changes the lat/log access to stations.lat/stations.log in the code above.