MySQL - Searching negative longitude values with BETWEEN - mysql

I have found many similar posts and even tried to find out how to handle negative values within MySQL, but to no avail.
I have a site that I'm using Google Maps on and as a performance enhancement I'm limiting the markers that are drawn on the map to those that are within the map boundaries.
I would like to develop a query that will work with positive or negative latitude and longitude values.
For the database:
latitude FLOAT( 10, 6 )
longitude FLOAT( 10, 6 )
The query:
SELECT *
FROM `table`
WHERE `latitude` BETWEEN 47.926930 AND 47.929806
AND `longitude` BETWEEN -97.077303 AND -97.083997
If I drop the BETWEEN clause for longitude I get results, albeit incorrect with no longitude constraint.
I have tried this:
AND -`longitude` BETWEEN ABS( -97.077303 ) AND ABS( -97.083997 )
Which does work, but only for negative longitude values.
Do I need to check longitude if its negative?

You can also use the greatest() and least() functions, so you don't have to worry about the parameters. For example:
SELECT * FROM table
WHERE latitude BETWEEN least(#lat1, #lat2) AND greatest(#lat1, #lat2)
AND longitude BETWEEN least(#lon1, #lon2) AND greatest(#lon1, #lon2)

between expects the format to be somefield BETWEEN lowervalue AND highervalue. Your negative longitudes have the higher/lower values switched. It should be
AND longitude BETWEEN -97.083997 AND -97.077303
negative 97.08 is actually lower than negative 97.07

Do check how negative numbers appear on a number line. :)
SELECT *
FROM `table`
WHERE `latitude` BETWEEN 47.926930 AND 47.929806
AND `longitude` BETWEEN -97.083997 AND -97.077303

Related

MariaDB - how to query on POINT field for locations inside lat/lng coordinates for a given city/area? [duplicate]

I have a table called flags that contains a column called coordinates that is full of MySQL 'points'. I need to perform a query where I get all the flags within a circle based on a latitude and longitude position with 100m radius.
From a usage point of view this is based around the user's position. For example, the mobile phone would give the user's latitude and longitude position and then pass it to this part of the API. It's then up to the API to create an invisible circle around the user with a radius of 100 metres and then return the flags that are in this circle.
It's this part of the API I'm not sure how to create as I'm unsure how to use SQL to create this invisible circle and select points only within this radius.
Is this possible? Is there a MySQL spatial function that will help me do this?
I believe the Buffer() function can do this but I can't find any documentation as to how to use it (eg example SQL). Ideally I need an answer that shows me how to use this function or the closest to it. Where I'm storing these coordinates as geospatial points I should be using a geospatial function to do what I'm asking to maximize efficiency.
Flags table:
id
coordinates
name
Example row:
1 | [GEOMETRY - 25B] | Tenacy AB
For the flags table I have latitude, longitude positions and easting and northing (UTM)
The user's location is just standard latitude/longitude but I have a library that can conver this position to UTM
There are no geospatial extension functions in MySQL supporting latitude / longitude distance computations. There is as of MySQL 5.7.
You're asking for proximity circles on the surface of the earth. You mention in your question that you have lat/long values for each row in your flags table, and also universal transverse Mercator (UTM) projected values in one of several different UTM zones. If I remember my UK Ordnance Survey maps correctly, UTM is useful for locating items on those maps.
It's a simple matter to compute the distance between two points in the same zone in UTM: the Cartesian distance does the trick. But, when points are in different zones, that computation doesn't work.
Accordingly, for the application described in your question, it's necessary to use the Great Circle Distance, which is computed using the haversine or another suitable formula.
MySQL, augmented with geospatial extensions, supports a way to represent various planar shapes (points, polylines, polygons, and so forth) as geometrical primitives. MySQL 5.6 implements an undocumented distance function st_distance(p1, p2). However, this function returns Cartesian distances. So it's entirely unsuitable for latitude and longitude based computations. At temperate latitudes a degree of latitude subtends almost twice as much surface distance (north-south) as a degree of longitude(east-west), because the latitude lines grow closer together nearer the poles.
So, a circular proximity formula needs to use genuine latitude and longitude.
In your application, you can find all the flags points within ten statute miles of a given latpoint,longpoint with a query like this:
SELECT id, coordinates, name, r,
units * DEGREES(ACOS(LEAST(1.0, COS(RADIANS(latpoint))
* COS(RADIANS(latitude))
* COS(RADIANS(longpoint) - RADIANS(longitude))
+ SIN(RADIANS(latpoint))
* SIN(RADIANS(latitude))))) AS distance
FROM flags
JOIN (
SELECT 42.81 AS latpoint, -70.81 AS longpoint,
10.0 AS r, 69.0 AS units
) AS p ON (1=1)
WHERE MbrContains(GeomFromText (
CONCAT('LINESTRING(',
latpoint-(r/units),' ',
longpoint-(r /(units* COS(RADIANS(latpoint)))),
',',
latpoint+(r/units) ,' ',
longpoint+(r /(units * COS(RADIANS(latpoint)))),
')')), coordinates)
If you want to search for points within 20 km, change this line of the query
20.0 AS r, 69.0 AS units
to this, for example
20.0 AS r, 111.045 AS units
r is the radius in which you want to search. units are the distance units (miles, km, furlongs, whatever you want) per degree of latitude on the surface of the earth.
This query uses a bounding lat/long along with MbrContains to exclude points that are definitely too far from your starting point, then uses the great circle distance formula to generate the distances for the remaining points. An explanation of all this can be found here. If your table uses the MyISAM access method and has a spatial index, MbrContains will exploit that index to get you fast searching.
Finally, the query above selects all the points within the rectangle. To narrow that down to only the points in the circle, and order them by proximity, wrap the query up like this:
SELECT id, coordinates, name
FROM (
/* the query above, paste it in here */
) AS d
WHERE d.distance <= d.r
ORDER BY d.distance ASC
UPDATE
Use ST_Distance_Sphere() to calculate distances using a lat/long
http://dev.mysql.com/doc/refman/5.7/en/spatial-convenience-functions.html#function_st-distance-sphere
This assumes the coordinates in the table are stored as a POINT() datatype in a column labeled 'point'. The function X(point) and Y(point) extract the latitude and longitude values from the point value respectively.
SET #lat = the latitude of the point
SET #lon = the longitude of the point
SET #rad = radius in Kilometers to search from the point
SET #table = name of your table
SELECT
X(point),Y(point),*, (
6373 * acos (
cos ( radians( #lat ) )
* cos( radians( X(point) ) )
* cos( radians( Y(point) ) - radians( #lon ) )
+ sin ( radians( #lat ) )
* sin( radians( X(point) ) )
)
) AS distance
FROM #table
HAVING distance < #rad
If you want to do it in miles, replace the constant 6373 with 3959
For those wanting to reduce the query syntax, here's a common implementation of a user defined MySQL function for implementing a distance function based on the Haversine formulae.
CREATE FUNCTION HAVERSINE ( coord1 POINT, coord2 POINT )
RETURNS DOUBLE
DETERMINISTIC
BEGIN
DECLARE dist DOUBLE;
SET rlat1 = radians( X( coord1 ) );
SET rlat2 = radians( X( coord2 ) );
SET rlon1 = radians( Y( coord1 ) );
SET rlon2 = radians( Y( coord2 ) );
SET dist = ACOS( COS( rlat1 ) * COS( rlon1 ) * COS( rlat2 ) * COS( rlon2 ) + COS( rlat1 ) * SIN( rlon1 ) * COS( rlat2 ) * SIN( rlon2 ) + SIN( rlat1 ) * SIN( rlat2 ) ) * 6372.8;
RETURN dist;
END
Buffers won't help you much in MySQL < 5.6, since buffer is a polygon, and polygon operations in MySQL < 5.6 are implemented as "Minimal Bounding Rectangles" (MBR), which are pretty useless.
Since MySQL 5.6, the full non-MBR st_* operations were implemented. But the best solution for you, in case of circle, is to use undocumented function st_distance:
select *
from waypoints
where st_distance(point(#center_lon, #center_lat), coordinates) <= radius;
It was hard to find, since it's undocumented :-) But it's mentioned on this blog, whose author also filled the mentioned bugreport. There are caveats though (citing the blog):
The bad news is:
1) All functions still only use the planar system coordinates.
Different SRIDs are not supported.
2) Spatial indexes (RTREE) are only supported for MyISAM tables. One
can use the functions for InnoDB tables, but it will not use spatial
keys.
Point 1) means that the unit of distance will be the same as the unit of coordinates (degrees in case of WGS84). If you need distance in meters, you have to use projected coordination system (e.g. UTM or similar) that has units corresponding to meters.
So, in case you don't want to go with these caveats, or in case of MySQL < 5.6, you will have to write your own custom distance function.
for the sake of completeness, as of MySQL 5.7.6. you can use the ST_Distance_Sphere function which achieves the same result:
SET #pt1 = ST_GeomFromText('POINT(12.3456 34.5678)');
SELECT * from
(SELECT * ,(ST_Distance_Sphere(#pt1, location, 6373)) AS distance FROM mydb.Event ORDER BY distance) x WHERE x.distance <= 30;
In this case, we provide the approximate radius of the Earth in kilometers (6373) and a point (#pt1). This code will calculate the distance (in kilometers) between that point (long 12.3456, lat 34.5678) and all the points contained in the database where the distance is 30km or less.
from:
https://gis.stackexchange.com/questions/31628/find-points-within-a-distance-using-mysql
SELECT
id, (
6371 * acos (
cos ( radians(78.3232) )
* cos( radians( lat ) )
* cos( radians( lng ) - radians(65.3234) )
+ sin ( radians(78.3232) )
* sin( radians( lat ) )
)
) AS distance
FROM markers
HAVING distance < 30
ORDER BY distance
LIMIT 0 , 20;
(remember to replace all constants, this example is for kilometers)
You can use:
SELECT name, lat, lng
FROM vw_mytable
WHERE ST_Contains(ST_Buffer(
ST_GeomFromText('POINT(12.3456 34.5678)'), (0.00001*1000)) , mypoint) = 1
The expression: 0.00001*1000 inside statement above give to you a circle with 1000 Meters of diameter, it's being applied on a view here, name column is just a label to point, mypoint is the name of my point column,
lat was calculated inside view with ST_X(mytable.mypoint) and
lng with ST_Y(mytable.mypoint) and they simply show me the literal values of lat and lng.
It will give to you all coordinates that belongs to circle.
Hope my version helps
SELECT
*
FROM
`locator`
WHERE
SQRT(POW(X(`center`) - 49.843317 , 2) + POW(Y(`center`) - 24.026642, 2)) * 100 < `radius`
details here http://dexxtr.com/post/83498801191/how-to-determine-point-inside-circle-using-mysql

How to get the value causing "Incorrect arguments to st_distance_sphere" error?

I have a MySQL query:
UPDATE
`location` `l1`
SET
`l1`.`city_id` = (
SELECT
`c1`.`id`
FROM
`city` `c1`
ORDER BY
ST_Distance_Sphere(`c1`.`coordinates`, `l1`.`coordinates`) ASC
LIMIT
1
)
that produces an error:
Incorrect arguments to st_distance_sphere
when executed against a subset of the dataset.
How do I get the value thats causing st_distance_sphere to fail?
Argument of the point class is out of the range. Besides, the sequence of arguments are longitude and latitude which is different from the usual. The following is from mysql 5.7 official reference.
https://dev.mysql.com/doc/refman/5.7/en/spatial-convenience-functions.html
The geometry arguments should consist of points that specify
(longitude, latitude) coordinate values:
Longitude and latitude are the first and second coordinates of the
point, respectively.
Both coordinates are in degrees.
Longitude values must be in the range (-180, 180]. Positive values are
east of the prime meridian.
Latitude values must be in the range [-90, 90]. Positive values are
north of the equator.
You will get this error when either latitude or longitude value is out of range. Therefore, check for ABS(long) > 180 OR ABS(lat) > 90, e.g.
SELECT
*
FROM
`location`
WHERE
ABS(ST_X(`coordinates`)) > 180 OR
ABS(ST_Y(`coordinates`)) > 90
In my case, an entry had POINT value POINT(-0.006014 99.99999999000001), i.e. the latitude value was out of range.

Get nearest location in database

I am trying to get the nearest location to a users input from within a database, (nearest store based on latitude and longitude), so based on the users postcode I am converting that to latitude and longitude and from these results I need to search my database to find the store that is the nearest to these values. I have the latitude and longitude of all stores saved and so far (from looking at previous questions) I have tried something like:
SELECT *
FROM mystore_table
WHERE `latitude` >=(51.5263472 * .9) AND `longitude` <=(-0.3830181 * 1.1)
ORDER BY abs(latitude - 51.5263472 AND longitude - -0.3830181) limit 1;
When I run this query, it does display a result, but it is not the nearest store, not sure if it could be something to do with the negative numbers, both my columns latitude + longitude are saved as decimal data types?
You have a logic operation in the order by rather than an arithmetic one. Try this:
SELECT *
FROM mystore_table
WHERE `latitude` >=(51.5263472 * .9) AND `longitude` <=(-0.3830181 * 1.1)
ORDER BY abs(latitude - 51.5263472) + abs(longitude - -0.3830181)
limit 1;
The AND in your original version would be producing a boolean value, either 0 or 1 -- and it would only be 1 when the values match exactly to the last decimal point. Not very interesting.
There are many reasons why this is not the nearest distance, but it might be close enough for your purposes. Here are some reasons:
Euclidean distance would take the square of the differences
Distance between two latitudes depends on the longitude (varying from about 70 miles on the equator to 0 at the poles).

Use MySQL spatial extensions to select points inside circle

I have a table called flags that contains a column called coordinates that is full of MySQL 'points'. I need to perform a query where I get all the flags within a circle based on a latitude and longitude position with 100m radius.
From a usage point of view this is based around the user's position. For example, the mobile phone would give the user's latitude and longitude position and then pass it to this part of the API. It's then up to the API to create an invisible circle around the user with a radius of 100 metres and then return the flags that are in this circle.
It's this part of the API I'm not sure how to create as I'm unsure how to use SQL to create this invisible circle and select points only within this radius.
Is this possible? Is there a MySQL spatial function that will help me do this?
I believe the Buffer() function can do this but I can't find any documentation as to how to use it (eg example SQL). Ideally I need an answer that shows me how to use this function or the closest to it. Where I'm storing these coordinates as geospatial points I should be using a geospatial function to do what I'm asking to maximize efficiency.
Flags table:
id
coordinates
name
Example row:
1 | [GEOMETRY - 25B] | Tenacy AB
For the flags table I have latitude, longitude positions and easting and northing (UTM)
The user's location is just standard latitude/longitude but I have a library that can conver this position to UTM
There are no geospatial extension functions in MySQL supporting latitude / longitude distance computations. There is as of MySQL 5.7.
You're asking for proximity circles on the surface of the earth. You mention in your question that you have lat/long values for each row in your flags table, and also universal transverse Mercator (UTM) projected values in one of several different UTM zones. If I remember my UK Ordnance Survey maps correctly, UTM is useful for locating items on those maps.
It's a simple matter to compute the distance between two points in the same zone in UTM: the Cartesian distance does the trick. But, when points are in different zones, that computation doesn't work.
Accordingly, for the application described in your question, it's necessary to use the Great Circle Distance, which is computed using the haversine or another suitable formula.
MySQL, augmented with geospatial extensions, supports a way to represent various planar shapes (points, polylines, polygons, and so forth) as geometrical primitives. MySQL 5.6 implements an undocumented distance function st_distance(p1, p2). However, this function returns Cartesian distances. So it's entirely unsuitable for latitude and longitude based computations. At temperate latitudes a degree of latitude subtends almost twice as much surface distance (north-south) as a degree of longitude(east-west), because the latitude lines grow closer together nearer the poles.
So, a circular proximity formula needs to use genuine latitude and longitude.
In your application, you can find all the flags points within ten statute miles of a given latpoint,longpoint with a query like this:
SELECT id, coordinates, name, r,
units * DEGREES(ACOS(LEAST(1.0, COS(RADIANS(latpoint))
* COS(RADIANS(latitude))
* COS(RADIANS(longpoint) - RADIANS(longitude))
+ SIN(RADIANS(latpoint))
* SIN(RADIANS(latitude))))) AS distance
FROM flags
JOIN (
SELECT 42.81 AS latpoint, -70.81 AS longpoint,
10.0 AS r, 69.0 AS units
) AS p ON (1=1)
WHERE MbrContains(GeomFromText (
CONCAT('LINESTRING(',
latpoint-(r/units),' ',
longpoint-(r /(units* COS(RADIANS(latpoint)))),
',',
latpoint+(r/units) ,' ',
longpoint+(r /(units * COS(RADIANS(latpoint)))),
')')), coordinates)
If you want to search for points within 20 km, change this line of the query
20.0 AS r, 69.0 AS units
to this, for example
20.0 AS r, 111.045 AS units
r is the radius in which you want to search. units are the distance units (miles, km, furlongs, whatever you want) per degree of latitude on the surface of the earth.
This query uses a bounding lat/long along with MbrContains to exclude points that are definitely too far from your starting point, then uses the great circle distance formula to generate the distances for the remaining points. An explanation of all this can be found here. If your table uses the MyISAM access method and has a spatial index, MbrContains will exploit that index to get you fast searching.
Finally, the query above selects all the points within the rectangle. To narrow that down to only the points in the circle, and order them by proximity, wrap the query up like this:
SELECT id, coordinates, name
FROM (
/* the query above, paste it in here */
) AS d
WHERE d.distance <= d.r
ORDER BY d.distance ASC
UPDATE
Use ST_Distance_Sphere() to calculate distances using a lat/long
http://dev.mysql.com/doc/refman/5.7/en/spatial-convenience-functions.html#function_st-distance-sphere
This assumes the coordinates in the table are stored as a POINT() datatype in a column labeled 'point'. The function X(point) and Y(point) extract the latitude and longitude values from the point value respectively.
SET #lat = the latitude of the point
SET #lon = the longitude of the point
SET #rad = radius in Kilometers to search from the point
SET #table = name of your table
SELECT
X(point),Y(point),*, (
6373 * acos (
cos ( radians( #lat ) )
* cos( radians( X(point) ) )
* cos( radians( Y(point) ) - radians( #lon ) )
+ sin ( radians( #lat ) )
* sin( radians( X(point) ) )
)
) AS distance
FROM #table
HAVING distance < #rad
If you want to do it in miles, replace the constant 6373 with 3959
For those wanting to reduce the query syntax, here's a common implementation of a user defined MySQL function for implementing a distance function based on the Haversine formulae.
CREATE FUNCTION HAVERSINE ( coord1 POINT, coord2 POINT )
RETURNS DOUBLE
DETERMINISTIC
BEGIN
DECLARE dist DOUBLE;
SET rlat1 = radians( X( coord1 ) );
SET rlat2 = radians( X( coord2 ) );
SET rlon1 = radians( Y( coord1 ) );
SET rlon2 = radians( Y( coord2 ) );
SET dist = ACOS( COS( rlat1 ) * COS( rlon1 ) * COS( rlat2 ) * COS( rlon2 ) + COS( rlat1 ) * SIN( rlon1 ) * COS( rlat2 ) * SIN( rlon2 ) + SIN( rlat1 ) * SIN( rlat2 ) ) * 6372.8;
RETURN dist;
END
Buffers won't help you much in MySQL < 5.6, since buffer is a polygon, and polygon operations in MySQL < 5.6 are implemented as "Minimal Bounding Rectangles" (MBR), which are pretty useless.
Since MySQL 5.6, the full non-MBR st_* operations were implemented. But the best solution for you, in case of circle, is to use undocumented function st_distance:
select *
from waypoints
where st_distance(point(#center_lon, #center_lat), coordinates) <= radius;
It was hard to find, since it's undocumented :-) But it's mentioned on this blog, whose author also filled the mentioned bugreport. There are caveats though (citing the blog):
The bad news is:
1) All functions still only use the planar system coordinates.
Different SRIDs are not supported.
2) Spatial indexes (RTREE) are only supported for MyISAM tables. One
can use the functions for InnoDB tables, but it will not use spatial
keys.
Point 1) means that the unit of distance will be the same as the unit of coordinates (degrees in case of WGS84). If you need distance in meters, you have to use projected coordination system (e.g. UTM or similar) that has units corresponding to meters.
So, in case you don't want to go with these caveats, or in case of MySQL < 5.6, you will have to write your own custom distance function.
for the sake of completeness, as of MySQL 5.7.6. you can use the ST_Distance_Sphere function which achieves the same result:
SET #pt1 = ST_GeomFromText('POINT(12.3456 34.5678)');
SELECT * from
(SELECT * ,(ST_Distance_Sphere(#pt1, location, 6373)) AS distance FROM mydb.Event ORDER BY distance) x WHERE x.distance <= 30;
In this case, we provide the approximate radius of the Earth in kilometers (6373) and a point (#pt1). This code will calculate the distance (in kilometers) between that point (long 12.3456, lat 34.5678) and all the points contained in the database where the distance is 30km or less.
from:
https://gis.stackexchange.com/questions/31628/find-points-within-a-distance-using-mysql
SELECT
id, (
6371 * acos (
cos ( radians(78.3232) )
* cos( radians( lat ) )
* cos( radians( lng ) - radians(65.3234) )
+ sin ( radians(78.3232) )
* sin( radians( lat ) )
)
) AS distance
FROM markers
HAVING distance < 30
ORDER BY distance
LIMIT 0 , 20;
(remember to replace all constants, this example is for kilometers)
You can use:
SELECT name, lat, lng
FROM vw_mytable
WHERE ST_Contains(ST_Buffer(
ST_GeomFromText('POINT(12.3456 34.5678)'), (0.00001*1000)) , mypoint) = 1
The expression: 0.00001*1000 inside statement above give to you a circle with 1000 Meters of diameter, it's being applied on a view here, name column is just a label to point, mypoint is the name of my point column,
lat was calculated inside view with ST_X(mytable.mypoint) and
lng with ST_Y(mytable.mypoint) and they simply show me the literal values of lat and lng.
It will give to you all coordinates that belongs to circle.
Hope my version helps
SELECT
*
FROM
`locator`
WHERE
SQRT(POW(X(`center`) - 49.843317 , 2) + POW(Y(`center`) - 24.026642, 2)) * 100 < `radius`
details here http://dexxtr.com/post/83498801191/how-to-determine-point-inside-circle-using-mysql

how to sort rows by a distance from a given point, mysql?

I'm trying to get 100 points from my table with a lowest distance to a given point.
I'm using
SELECT *, GLENGTH(
LINESTRINGFROMWKB(
LINESTRING(
ASBINARY(
POINTFROMTEXT("POINT(40.4495 -79.988)")
),
ASBINARY(pt)
)
)
)
AS `distance` FROM `ip_group_city` ORDER BY distance LIMIT 100
(Yeah, that's painful. I've just googled it. I have no idea how to measure distance in MySQL correctly)
It takes very long time for execute. EXPLAIN says that there are no possible_keys.
I created a SPATIAL index on the pt column:
CREATE SPATIAL INDEX sp_index ON ip_group_city (pt);
Though I don't really know how to use it correctly. Can you please help me?
Because you don't have WHERE clause therefore no affected index. I think you should improve this query by add using MBR_ (MySQL 5.0 or later) or ST_ functions (MySQL 5.6 or later).
Something like:
SELECT *, GLENGTH(
LINESTRINGFROMWKB(
LINESTRING(
ASBINARY(
POINTFROMTEXT("POINT(40.4495 -79.988)")
),
ASBINARY(pt)
)
)
)
AS `distance`
FROM `ip_group_city`
WHERE
MBRWithin(
pt, -- your point
GeomFromText('Polygon( -- your line (in polygon format) from pt to target point
(
#{bound.ne.lat} #{bound.ne.lng}, --North East Lat - North East Long
#{bound.ne.lat} #{bound.sw.lng}, --North East Lat - South West Long
#{bound.sw.lat} #{bound.sw.lng}, --
#{bound.sw.lat} #{bound.ne.lng},
#{bound.ne.lat} #{bound.ne.lng}
)
)')
)
ORDER BY distance LIMIT 100
I've used the great circle equation to do these types of calculations in the past. I'm not sure how the performance compares but it might be worth trying it and comparing.
Here is a good SO post that goes over how to do it in MySQL.
Have a look at these questions:
Finding Cities within 'X' Kilometers (or Miles)