Order results by proximity (with coordinates & radius) - mysql

Given a database of 4 circles, where each circle has a radius and a geolocated centre:
id | radius | latitude | longitude
---+--------+----------+----------
1 | 3 | 40.71 | 100.23
2 | 10 | 50.13 | 100.23
3 | 12 | 39.92 | 100.23
4 | 4 | 80.99 | 100.23
Note: the longitude is the same for each circle, in order to keep things simple.
Assuming that we are on the circle 2, I would like to find every circle nearby, according to the latitude/longitude coordinates and the radius of each circle.
For example, according to the latitude/longitude coordinates, we have this order:
circle 1 (because of proximity: 9.42 <- 50.13 - 40.71)
circle 3 (because of proximity: 10.21 <- 50.13 - 39.92)
circle 4 (because of proximity: 30.86 <- 80.99 - 50.13)
But according to the latitude/longitude coordinates and the radius of each circle, we should have:
circle 3 (because of proximity: 1.79 <- 12 - 10.21)
circle 1 (because of proximity: 6.42 <- 9.42 - 3)
circle 4 (because of proximity: 26.86 <- 30.86 - 4)
Is there a simple way to do so in SQL?

The cube and earthdistance extensions provided in postgresql's contrib can handle doing this, to produce at least approximate answers. Specifically, they assume the Earth is a simple sphere, which makes the math a lot easier.
With those extensions you can produce the distance between circle 2 and the others like this:
select circle.id,
earth_distance(ll_to_earth(circle.latitude, circle.longitude),
ll_to_earth(x.latitude, x.longitude))
from circle,
circle x
where x.id = 2 and circle.id <> x.id
order by 2;
Correcting for the circle radius should just involve subtracting x.radius and circle.radius from the distance above, although you need to think about what units the radius is in. By default, earth_distance will calculate a value in metres.
Now, making the query do something other than scan the entire list of circles and calculate the distance for each one, then sort and limit them, that's much more challenging. There are a couple of approaches:
using cube's ability to be indexed with gist, so you can create indices to search within certain boxes around any circle's centre, and hence cut down the list of circles to consider.
precalculate the distance between each circle and all the others any time a circle is edited, using triggers to maintain this calculation in a separate table.
The second options basically starts with:
create table circle_distance as
select a.id as a_id, b.id as b_id,
earth_distance(ll_to_earth(a.latitude, a.longitude),
ll_to_earth(b.latitude, b.longitude))
from circle a, circle b
where a.id <> b.id;
alter table circle_distance add unique(a_id, b_id);
create index on circle_distance(a_id, earth_distance);
Then some rather tedious functions to delete/insert relevant rows in circle_distance, called by triggers on circle. This means you can do:
select b_id from earth_distance where a_id = $circle_id order by earth_distance limit $n
This query will be able to use that index on (a_id,earth_distance) to do a quick scan.

I'd suggest looking at the PostGIS Geography data types and its associated functions (eg: ST_Distance)rather than reinventing the wheel

In neo4j, you can look at Neo4j Spatial, tests for the different operations at https://github.com/neo4j/spatial/blob/master/src/test/java/org/neo4j/gis/spatial/pipes/GeoPipesTest.java, amongst them proximity search, too, e.g. https://github.com/neo4j/spatial/blob/master/src/test/java/org/neo4j/gis/spatial/pipes/GeoPipesTest.java#L150

I would souggest you the following:
Create 1 table for calculation of relative distances in relation to the start circle
for instance:
id | calc1 | calc2
---+--------+----------
1 | 9.42 | 1.97
3 | 10.21 | 6.42
4 | 30.86 | 62.86
Calc1 being the calculation without the radius
calc2 being the calculation with radius
then create a store procedure that will first when it is run delete the table and then fill it with the correct data and then just read the result from the destination table
Intrudoction to store procedures
You will allso need cursor for this

Related

Finding Closest coordinates in sql database

I'm looking for a quick way (With a relative short query length) to find the rows with the closest x, y, coordinates in a database, and return the twenty closest. Closest as in the x-y coordinates are closest to the results. The database looks like this.
-----------------
|Stuff | x | y |
------------------
|bob |-21| 32 |
|Joe |23 | 29 |
------------------
So, a search would be like x=19, y=32, and I would like twenty closest results, sorted by closeness. X-Y is a completely square grid.
Any help would be appreciated. Thanks!
If this helps, I'm using MariaDB Version 10.3.27 on raspbian/debian
One way of measuring closest is to use Manhattan difference, the sum of the absolute value of the differences:
select t.*
from t
order by abs(x - #x) + abs(y - #y)
limit 20;
If you have a different distance metric, you would just plug that formula in.

RDLC Design Group Horizontally

Forgive me for the title as I don't know how to put this in words.
Expected Output:
# | X | Y | # | X | Y
1 |A1 |A2 | 26 |B1 |B2
2 27
3 28
. .
. .
. .
25 |D1 |D2 | 50 |E1 |E2
I want to limit the row count to 25 and I want to continue horizontally.
The main reason why the format of the report I want to make is like this is to consume the entire page. The columns # X Y would only have a width of 4 inches in total, thus we expect that rows 51-100 will be on the 2nd page of the report.
Is this possible? I am familiar with paging in RDLC through the use of groups but the rows would repeat vertically downwards which is not what I'm looking for.
I can group my data from 1 to 25 as Group 1 and 26 to 50 as Group 2 and so on, but I don't know how to display the group horizontally.
I am open to new designs as long as the page will filled with data.
P.S. We're not a fan of putting papers to waste.
In essence, you are looking to create a grouping in your SQL dataset every 25 rows, which you can then create a column grouping on in your report.
SQL Example that groups every 25 records (no access to SQL Server at the moment so the code isn't tested but you can see the idea):
WITH T AS (
SELECT ROW_NUMBER() as RowNum,
tbl.#, tbl.X, tbl.Y
FROM tbl
)
SELECT
(T.RowNum) / 25 as GroupID,
T.X,
T.Y
FROM T
GROUP BY ((T.RowNum) / 25)
Once your dataset has this new "GroupID", create a column grouping on this field and that should create the additional columns to fill the page up.
It has been decided that we would give up on this design. I am now using a simple table without grouping to display the data. Columns were expanded from a total of 4in to 8in so that there will be less unused space in the paper.

Fetch all points from database inside a bounding box of north east and south west coordinates in MySQL

I am currently building an application to show all geo-tagged trees in a particular city.
The main columns I am using to fetch data from the table are as follows,
+-----------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------------+-------------+------+-----+---------+-------+
| tree_geotags_id | int(11) | NO | PRI | None | |
| lattitude_dl | double(9,7) | YES | | NULL | |
| longitude_dl | double(9,7) | YES | | NULL | |
+-----------------+-------------+------+-----+---------+-------+
The table has over 158000 rows.
Currently I am using the following query to get my output,
SELECT gt.tree_geotags_id, gt.latitude_dl, gt.longitude_dl,
SQRT(
POW( 69.1 * ( gt.latitude_dl - [ center_lat ] ), 2) +
POW( 69.1 * ( [ center_lon ] - gt.longitude_dl ) * COS( gt.latitude_dl / 57.3 ), 2 )
) AS distance
FROM tree_geotags_t gt
HAVING distance < 0.4 ORDER BY distance
What this does is, it fetches all records at a radius of 0.4.
I use an ajax call to fetch the data, every time the center coordinate of the map changes( on map pans or zooms ), convert the fetched data into geojson format and then load it on the map as a layer. The issue I am having with this is, in locations where there is a very high density of trees, it takes a long time for the map to place all the points and since it fetches it on a radius it loads points that are even outside the viewport.
I need a query to only load data in coordinates inside the viewport, using the northeast and southwest coordinates as boundaries. I searched for quite a while here but couldn't find anything suited to my requirements. Please help me out. Thanks in advance..!!
If anyone's still looking, I got my answer from this post.
Get all records from MySQL database that are within Google Maps .getBounds?
Thanks for the help anyway.
You are very close. Your (otherwise grossly incorrect) distance formula contains the seed of your bounding box check.
Try this
SET #distance_unit := 69.0; /* for miles, use 111.045 for km. */
SET #radius := 1.0; /* for radius */
SET #center_lat := target_latitude_in_degrees;
SET #center_lon := target_longitude_in_degrees;
SELECT gt.tree_geotags_id, gt.latitude_dl, gt.longitude_dl
FROM tree_geotags_t gt
WHERE gt.latitude_dl
BETWEEN #center_lat - (#radius/#distance_unit) /*east boundary*/
AND #center_lat + (#radius/#distance_unit) /*west*/
AND gt.longitude_dl
BETWEEN #center_lon - (#radius / (#distance_unit * COS(RADIANS(#center_lat)))) /*south*/
AND #center_lon + (#radius / (#distance_unit * COS(RADIANS(#center_lat)))) /*north*/
Suppose you know the east, west, north, and south boundaries of your bounding box instead of its center. That's an easy adaptation of the above code.
SELECT gt.tree_geotags_id, gt.latitude_dl, gt.longitude_dl
FROM tree_geotags_t gt
WHERE gt.latitude_dl BETWEEN #east AND #west
AND gt.longitude_dl BETWEEN #south AND #north
The question of how to derive the sides of your bounding box from its corners is trivial as long as the bounding box coordinates are in degrees. If they're given in some projection units (like transverse UTM coordinates) there's no way the answer will fit in a Stack Overflow post.
This query can be made fast by a compound index on (latitude_dl, longitude_dl, tree_geotags_id) The latitude search will use an index range scan, and the longitude and id can then be retrieved directly from the index.
What's wrong with your distance formula? It's cartesian, but you need the spherical cosine law formula because you're dealing with spherical coordinates.
This will not work close to either the north or south pole (because cos(latitude) tends towards zero there) but that's OK; you're dealing with trees, and they don't grow there, yet.
Here's a comprehensive writeup on the topic. http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/

Create loop within SQL SELECT statement until chain broken

Hypothetical database for events happening around the world.
EVENT
event_id | event_name
1 | Great Wall Party
2 | Times Square Dance
3 | Sydney Blowout
PLACE
place_id | place_name
54 | Times Square
55 | Manhattan
56 | New York City
57 | New York State
58 | USA
EVENTPLACE
eventid | placeid
2 | 54
RELATEDPLACES
rel_placeid1 | rel_placeid2
54 | 55
55 | 56
56 | 57
57 | 58
If I display the event, Times Square Dance, I’d like to display all the places that appear up the chain of its associated places via the RELATEDPLACES table (i.e. Times Square, Manhattan, New York City, New York State, USA). Likewise, if I call all events for USA, I’d like the Times Square dance to be listed, given its EVENTPLACE (Times Square) appears at the bottom of the RELATEDPLACES chain of associations starting with USA.
I think I need to create an inner loop within my SQL command so that it keeps performing until there is a break in the chain. So far (using the first of the two above examples) I have:-
SELECT place_nm FROM eventplace
INNER JOIN relatedplaces ON placeid = rel_placeid1
INNER JOIN place ON rel_placeid2 = place_id
[where the loop should begin:
INNER JOIN relatedplaces ON place_id = rel_placeid1
INNER JOIN place ON rel_placeid2 = place_id
end loop]
WHERE eventid = ‘2’;
This is complicated by the fact that I need different table aliases for each loop, which means I can’t state in the opening SELECT statement that I want to be collecting all the place_name data in the same column.
I’m not sure if I what I am trying to achieve is even possible and my current fallback solution is to list all of Times Square’s related places in the RELATEDPLACES table, rather than just the next largest place (Manhattan), but this seemed like the better solution (and would also save database space).
Can anyone suggest the SQL SELECT command I might need to use? Cheers!
Quite an intriguing problem. But the issue is that MySQL currently does not allow for recursive queries. You have two options :
Decided on the level of RelatedEvents upto which you want to dig into, and create a query for the same (using excel or a small C code)
Use a program to generate and execute the queries on the fly; recursively make DB queries from the program. I suggest this is the best option, though requires repeated DB access.

Mysql Algorithm for great circle distance calculation

I want to calculate distance between two zip codes before inserting the data to database .basically i have a these tables
zip code table
| zipcode | lat | long |
01230 60.1756 23.12
01240 60.1756 25.25
customer table
| name | zip code |
foo 01230
sales man table
| name | zip code | workingdistanceinkm
foo 01240 200
foo1 01230 100
What I want to do is calculate the distance between the sales mans and the customer if it is any of the salesman working area before the data of the customer is inserted to customer table .
MY approach was to calculate the distance between a customer and every sales man that is in the sales man table. But this makes a lot of queries for example if I have 1000 sales man it means I am calculating the distance between the new customer data to be inserted with those every one of the 1000 salesmen.
I am wondering if it's possible to write one query to do the same task.
Have a look at
www.zipcodeworld.com/samples/distance.php.html
note that distance calculations between zipcodes are not always the actual representations of the distance. This is just the distance of an imaginary straight line between the two points. But in reality it is longer
Below URL helped me a lot. Please check "Finding Locations with MySQL" section. Thanks.
https://developers.google.com/maps/articles/phpsqlsearch_v3