select places with nearly same location (duplicates) by latitude/longitude - mysql

Lets say I have a table venues with following columns:
id
user_id
name
latitude
longitude
The latitude and longitude are kept as FLOAT(10,6) values. As different users add venues, there are venue duplicates. How can I select all the duplicates from the table in range up to lets say 50 metres (as it might be hard to achieve as the longitudial meter equivalents are different at different latitudes, so this is absolutely aproximate)? The query should select all venues: VenueA and VenueB (there might be VenueC, VenueD, etc) so that I can compare them. It should filter out venues that are actually one per location in the range (I care only for duplicates).

I was looking for an answer but had to settle with answering myself.
SELECT s1.id, s1.name, s2.id, s2.name FROM venues s1, venues s2
WHERE s2.id > s1.id AND
(POW(s1.latitude - s2.latitude, 2) + POW(s1.longitude - s2.longitude, 2) < 0.001)
The first condition is to select only half of matrix as order of similar venues is not important. The second one is simplified distance calculator. As user185631 suggested haversine formula should do the trick if you need more precision but I didn't need it as I was looking for duplicates with the same coordinates but couldn't settle with s1.latitude = s2.latitude AND s1.longitude = s2.longitude due to float/decimal corruption in my DB.
Of course checking this at insert would be better but if you get corrupt DB you need to clean it somehow. Please also note that this query is heavy on server if your tables are big.

Create a function which computes distances between lat/lons. For small/less accurate distance (which is the case here) you can use the Equirectangular approximation (see section here: http://www.movable-type.co.uk/scripts/latlong.html). If the distance is less than your chosen threshold (50m), then it is a duplicate.

Determine what 50 meters is in terms of lat and long. Then plus and minus that to your starting location to come up with a max and min for both lat and long. Then...
SELECT id FROM venues WHERE latitude < (your max latitude) AND latitude > (your min latitude) AND longitude < (your max longitude) AND longitude > (your min longitude);
Converting meters to lat/long is very tricky as it depends on where the starting point is on the globe. See the middle section of the page here: http://www.uwgb.edu/dutchs/usefuldata/utmformulas.htm

Related

MySQL select rows in Geolocation range

Background information: I am making an app(school project) where users can search for an item nearby. My app uses a table of Items where all item data is stored.
I need to get the rows which are in a certain diameter(5km) of my user's current position(51.337036, 4.645095). In the Items table there is column for latitude and longitude to locate the item.
In my current query I can select the rows which are in a square(4 coordinate points) but I need to have a sphere:
WHERE (C.latitude BETWEEN 50 AND 52) AND (C.longitude BETWEEN 3 AND 5)
Is it possible to use a MySQL Geolocation variable or function?
After some research I saw a type POINT, but my table doesn't have that type (the table needs to stay the same).
AS is said in the comment you have to have a point of valid origin
WHERE st_distance_sphere(POINT(-82.337036, 29.645095 ), POINT(C.`longitude`, C.`latitude` ))/1000 <= 2 AND T.difficulty = 1 AND T.terrain = 2;
This would give you all rows that are in max 2 km disance from POINT(-82.337036, 29.645095 )
The you have to put your own location

Max latitude for x distance from longitude - Max longitude for x distance from latitude - SQL

Right now I have a table of 100 million inserts:
CREATE TABLE o (
id int UNIQUE,
latitude FLOAT(10, 8),
longitude FLOAT(11, 8)
);
On my back end I am receiving a user lat/long and trying to return everything within x distance of that.
Instead of doing the distance formula on every single result I was thinking I could possibly calculate the maximum lat/long for X distance.
So we are sort of creating a square by finding the max lat/min lat, max long/min long.
Once we have these max values we would do the query on this range of values thus making our subset significantly smaller to then do the actual distance formula on (i.e., finding the values within X distance).
So my question to you is:
What makes me run faster?
Option 1)
Distance formula on 100 million entries to get the set.
Option 2)
Instead of doing the distance formula on the set of 100 million entries we calculate the min/max lat/long.
Select the values in that range from the table of 100 million entries
Do the distance formula on our new smaller set.
Option 3)
Something exists already for this in SQL
If option 2 is faster the next issue is actually solving that math problem.
If you want to look at that continue reading:
Lat/Long distance formula
dlon = lon2 - lon1
dlat = lat2 - lat1
a = (sin(dlat/2))^2 + cos(lat1) * cos(lat2) * (sin(dlon/2))^2
c = 2 * atan2(sqrt(a), sqrt(1-a))
d = R * c
Obviously we can rearrange this because D (assume 1 mile), and R (is the radius of the earth) is a set value so we get D/R = C.
The problem then comes in to how do we calculate C/2 = atan2(sqrt(a), sqrt(1-a))?
1 -- 100M rows is a lot to scan and test. It's OK do do once in a while, but it is too slow to do a lot.
2 -- Using a pseudo-square bounding box and doing
WHERE latitude BETWEEN ...
AND longitude BETWEEN ...
is a good first step. The latitude range is a simple constant times X; the longitude range also divides by cos(latitude).
But the problem comes when you try to find just those rows in the square. Any combination of index on latitude and/or longitude, either separately or together, will only partially filter. That is, it will ignore longitude and give you everything within the latitude range, or vice versa. That might get you down to 100,000 rows to check the distance against. That's a lot better than 100,000,000, but not as good as you would hope for.
3 -- http://mysql.rjweb.org/doc.php/latlng Does get down to the square, or very close. It is designed to scale. I have tested only 3M rows, not 100M, but it should work fine.
The main trick is to partition on latitude, then have longitude be the first column in the PRIMARY KEY so that InnoDB will cluster the nearby rows nearby in the partition(s). If you look for all rows within X miles (or km) it might look at (and compute the great-circle-distance) for about twice as many rows as necessary, not 100K. If you want to find the nearest 100 items, it might touch about 400 (4x).
As for SPATIAL index, you might want to upgrade to 5.7.6, which is when ST_Distance_Sphere() and ST_MakeEnvelope() were added. (MakeEnvelope is only marginally more convenient than building a Polygon yourself -- it has flat-earth syndrome.)

SQL Group By. Duplicate lat/lng in differents cities

I bought a geo-database a long time ago and I'm updating its precision to the lat/lng values. But I've found some weird stuff. There are some cities that have the same lat/lng coordinates. Thing that is geographically impossible.
id City State Lat Lng
1 A sA XX XX
2 B sA XX XX
3 C sA YY YY
4 D sA ZZ ZZ
So I tried Group By City, Lat, Lng but as I need the id to update the record the group by clause will ask me to add ´id´ column.
From the table ids 1 and 2 should be updated leaving 3 and 4 out. It shouldn't be 2 (or more) cities with the same Lat/Lng. The Table has 22K rows. I could send all to gmap API but I'm looking for use the time, bandwith and hits to the API as smart as possible but I'm running out of time considering I can make a request per second using the free API access.
I've tried
SELECT DISTINCT postcodes_id, Latitude, Longitude, Region1Name, Region2Name, Nation_D
FROM postcodes
where Latitude + Longitude IN
(
SELECT Latitude + Longitude
FROM
(
SELECT postcodes_id, Latitude, Longitude, count(distinct(Region2Name)) as cantidad
FROM postcodes
where Nation_D is not null
GROUP BY Latitude, Longitude
having count(distinct(Region2Name)) > 1
) A
)
AND Nation_D IS NOT NULL
ORDER BY Latitude, Longitude, Region1Name, Region2Name, Nation_D
But is not working as expected. I think its pretty obvious for a new pair of eyes.
I wrote a python script to use Google Map geocode to get the current Lat/Lng and update it if it's different. This script works ok.
Hope someone has an idea. Thanks!!
Running MySQL 5.5 and Python 2.7 on a CentOS 7.
Just some pointers for you, which may be helpful:
You should not use group by or distinct on lat/lon or any combination of them, since they are contiguous floating points numbers and not discrete integers or strings.
By the same token, you should not use WHERE clauses on lat/lon or their sum. If you mean to check for proximity of two locations, use st_distance() function instead.
Multiple city names can refer to the same location. For example, New York, NY and Manhattan, NY.
And a non-technical point: storing Google geocoding data in your database is against their licensing agreement.

Lat/Long Distance Comparison

I am having some trouble with figuring out how to do this. What I have is a list of 160K locations on an Access table with lat and long coordinates for each. I am trying to find out how to create a column that compares 1 item on the list to the rest of the items to bring back the closest distance in miles.
I've figured out how to use the haversine formula to make a 1 to 1 comparison but I am lost in trying to automate the rest.
This is basically what I want to try to produce...
Loc_ID Loc_Lat Loc_Long Min_Miles_Away
1 33.537214 -81.687378 674.48
4 42.16584 -87.845117 11.83
5 41.99558 -87.869057 11.83
6 41.85325 -89.486883 83.75
Explanation to the table...
Location 1 is closest to location 5 (674.48 miles apart)
Location 4 is closest to location 5 (11.83 miles apart)
Location 5 is closest to location 4 (11.83 miles apart)
Location 6 is closest to location 5 (83.75 miles apart)
Any help would be appreciated.
You can do a cartesian join, i.e. a join without a where. It will join each row with every other row. You can do that by simply writing the SQL into the SQL view of the query.
SELECT *
FROM locations a, locations b
Next you can calculate the distance (I guess you have that code already, so just insert the function) on that table.
Finally you can group by MIN.
SELECT loc_id, loc_lat, loc_long, MIN(calulated_distance) as min_miles_away
FROM myCalculatedQuery

MySQL - finding difference between any two integers in same column

I have data concerning prices of goods at various latitudes and longitudes. I am look to find profit opportunities by comparing differences in price with the distance needed to travel to obtain the better prices.
The pseudocoded formula looks like this currently:
select (100*diff(avg(price))-50*diff(lon)-70*diff(lat)) as profit
As such, I wish to find the difference between any two latitude values in the data set. I have seen responses that explain how to find the differences between consecutive values or differences in dates, but nothing that seems to address my particular question.
Edit with my current query (this should simply provide me with the two most distant cities latitude wise in descending order):
SELECT lat AS lat2, (lat2 - lat) AS latdistance, city
FROM buying INNER JOIN buying ON (lat2 = lat)
Order by latdistanceasc
The output should list both cities involved as each one contributes a latitude although I am unsure of how to make them both display in the output.
Imagine 3 data points with (Price, Latitude, Longitude):
A (10, 30, 50)
B (15, 50, 60)
C (5, 20, 30)
What is the latitudinal distance between any two points (to keep things simple)?
The output should be:
AB - 20
AC - 10
BC - 30
This query works for your conditions:
SELECT b1.city start,
b2.city finish,
abs(b1.latitude - b2.latitude) latdistance
FROM buying b1, buying b2
WHERE b1.city < b2.city
ORDER BY 3
You should be warned however, that it is expensive and it will grow number of rows as O(n^2).
To use better metric for distance, use this:
SELECT b1.city start,
b2.city finish,
sqrt(pow(b1.latitude - b2.latitude, 2)
+ pow(b1.longitude - b2.longitude, 2)) latdistance
FROM buying b1, buying b2
WHERE b1.city < b2.city
ORDER BY 3
Both queries at SQLFiddle.
To compare the current location to all other cities and showing the profit:
SELECT
here.city AS Here,
there.city AS There,
here.price - there.price AS Profit,
ABS(here.lat - there.lat) AS Distance
FROM buying AS here
-- Join to the table itself, but not to the same city
LEFT JOIN buying AS there ON (here.city <> there.city)
-- Remove this to compare all cities to every other city
WHERE here.city = 'Springfield'