Weather Observation Station 18 HackerRank - sql-server

How should I write query for this? Consider P1(a, b) and P2(c, d) to be two points on a 2D plane.
a happens to equal the minimum value in Northern Latitude (LAT_N in STATION)
b happens to equal the maximum value in Northern Latitude (LAT_N in STATION)
c happens to equal the minimum value in Western Longitude (LONG_W in STATION)
d happens to equal the maximum value in Western Longitude (LONG_W in STATION)
Query the Manhattan Distance/Eculids Distance between points P1 and P2 and round it to a scale of 4 decimal places.
Table STATION(ID number, CITY varchar2(21), STATE varchar2(2), LAT_N number, LONG_W number)
any idea will be appreciated

SELECT
CAST(ABS(MAX(LAT_N) - MIN(LAT_N))
+ ABS(MAX(LONG_W) - MIN(LONG_W))as numeric(32,4))
FROM
STATION;

Easiest way of doing this problem is
Using mathematics function [ Dis. = sqrt((x2−x1)^2+(y2−y1)^2) ], try:
for sql server:
for Manhattan Distance:
select format(abs(min(LAT_N)-max(LAT_N))+abs(min(long_w)-max(long_w)),'.####') from station;
And for Eculids Distance:
select format(sqrt(square(min(LAT_N)-max(LAT_N))+square(min(long_w)-max(long_w))),'.####') from station;

Related

Return 4 nearest postcodes from array with Geokit?

Continuing from where I've left off, I want to limit the number of postcodes (only select up to x, but for now, say 4).
point_a = Geokit::Geocoders::GoogleGeocoder.geocode "se18 7hp"
alpha = ["cr0 3rl", "W2 1AA", "abc 234", "aol 765", "wv1 111"]
miles = alpha.map do |m| point_a.distance_to(m) end
#=> an array of numbers in miles
The answer to return the nearest postcode was with: alpha[miles.index(miles.min)]
I am trying to return the nearest 4:
alpha[miles.index(miles[0..3].min)] # surely not possible
The question is, how to return an array of the nearest 4 postcodes?
You can use sort_by:
alpha.sort_by{|m| point_a.distance_to(m)}.take(4)
Or with Ruby 2.2.0+, min_by with argument :
alpha.min_by(4){|m| point_a.distance_to(m)}

algorithm for finding date in sorted array of dates

here is my problem.
I have a sorted array of dates that is stored in a circular buffer. I have a pointer to last date in buffer. There is a possibility that some dates are missing. Client requires a range of dates. If low limit date is missing, program should return first closest date that is higher then required one and vice versa for upper limit date.
Here is an example:
Dates in circular buffer (int[18]):
1,2,3,4,5,11,12,13,14,15,21,22,23,24,25,26,27,28
and if client wants from 8 to 23,
program should return 11,12,13,14,15,21,22,23.
I tried like this :
Notes:
- number between two stars is current date, and diff is number of steps to go to find 8.
- pointer can not be less then 0 or higher then 17.
{1,2,3,4,5,11,12,13,14,15,21,22,23,24,25,26,27,*28*}, diff = -20
{*1*,2,3,4,5,11,12,13,14,15,21,22,23,24,25,26,27,28}, diff = +7
{1,2,3,4,5,11,12,*13*,14,15,21,22,23,24,25,26,27,28}, diff = -5
{1,2,*3*,4,5,11,12,13,14,15,21,22,23,24,25,26,27,28}, diff = +5 -> (5/2)+1=+3<br />
(if I detect that I will just go x steps forward and x steps backward I split x in half)
{1,2,3,4,5,*11*,12,13,14,15,21,22,23,24,25,26,27,28}, diff = -3 -> (-3/2)-1 = -2
{1,2,3,*4*,5,11,12,13,14,15,21,22,23,24,25,26,27,28}, diff = 4
{1,2,3,4,5,11,12,*13*,14,15,21,22,23,24,25,26,27,28}, diff = -5
{1,2,*3*,4,5,11,12,13,14,15,21,22,23,24,25,26,27,28}, diff = +5 -> (5/2)+1=+3
If we continue like this we will get 13,3,11,4 over and over again.
Notes:
- It is only coincidence that we get 11 here. When I use some real examples, with more dates,this algorithm jumps over some other 4 (or 3) numbers.
- Dates are stored in EEPROM of uC, so reading dates take a while, and I need to find date as quick as it possible (with minimum reads).
Please help.
Set p1 to be the start of the buffer, p2 to be the end. X is what you're looking for.
If the date of p1Date is after X, return p1. If p2Date is before X return p2.
Look at the midpoint between p1 and p2, m. If mDate is after X then p1=m else p2=m.
Repeat until p1=p2.

Is a point within a geographical radius - SQL Server 2008

Given the following data, would it be possible, and if so which would be the most efficient method of determining whether the location 'Shurdington' in the first table is contained within the given radius's of any of the locations in the second table.
The GeoData column is of the 'geography' type, so using SQL Servers spatial features are an option as well as using latitude and longitude.
Location GeoData Latitude Longitude
===========================================================
Shurdington XXXXXXXXXX 51.8677979 -2.113189
ID Location GeoData Latitude Longitude Radius
==============================================================================
1000 Gloucester XXXXXXXXXX 51.8907127 -2.274598 10
1001 Leafield XXXXXXXXXX 51.8360519 -1.537438 10
1002 Wotherton XXXXXXXXXX 52.5975151 -3.061798 5
1004 Nether Langwith XXXXXXXXXX 53.2275276 -1.212108 20
1005 Bromley XXXXXXXXXX 51.4152069 0.0292294 10
Any assistance is greatly apprecieded.
Create Data
CREATE TABLE #Data (
Id int,
Location nvarchar(50),
Latitude decimal(10,5),
Longitude decimal(10,5),
Radius int
)
INSERT #Data (Id,Location,Latitude,Longitude,Radius) VALUES
(1000,'Gloucester', 51.8907127 ,-2.274598 , 20), -- Increased to 20
(1001,'Leafield', 51.8360519 , -1.537438 , 10),
(1002,'Wotherton', 52.5975151, -3.061798 , 5),
(1004,'Nether Langwith', 53.2275276 , -1.212108 , 20),
(1005,'Bromley', 51.4152069 , 0.0292294 , 10)
Test
Declare your point of interest as a POINT
DECLARE #p GEOGRAPHY = GEOGRAPHY::STGeomFromText('POINT(-2.113189 51.8677979)', 4326);
To find out if it is in the radius of another point:
-- First create a Point.
DECLARE #point GEOGRAPHY = GEOGRAPHY::STGeomFromText('POINT(-2.27460 51.89071)', 4326);
-- Buffer the point (meters) and check if the 1st point intersects
SELECT #point.STBuffer(50000).STIntersects(#p)
Combining it all into a single query:
select *,
GEOGRAPHY::STGeomFromText('POINT('+
convert(nvarchar(20), Longitude)+' '+
convert( nvarchar(20), Latitude)+')', 4326)
.STBuffer(Radius * 1000).STIntersects(#p) as [Intersects]
from #Data
Gives:
Id Location Latitude Longitude Radius Intersects
1000 Gloucester 51.89071 -2.27460 20 1
1001 Leafield 51.83605 -1.53744 10 0
1002 Wotherton 52.59752 -3.06180 5 0
1004 Nether Langwith 53.22753 -1.21211 20 0
1005 Bromley 51.41521 0.02923 10 0
Re: Efficiency. With some correct indexing it appears SQL's spatial indexes can be very quick
If you want to do the maths yourself, you could use Equirectangular approximation based upon Pythagoras. The formula is:
var x = (lon2-lon1) * Math.cos((lat1+lat2)/2);
var y = (lat2-lat1);
var d = Math.sqrt(x*x + y*y) * R;
In terms of SQL, this should give those locations in your 2nd table that contain your entry in the 1st within their radius:
SELECT *
FROM Table2 t2
WHERE EXISTS (
SELECT 1 FROM Table1 t1
WHERE
ABS (
SQRT (
(SQUARE((RADIANS(t2.longitude) - RADIANS(t1.longitude)) * COS((RADIANS(t2.Latitude) + RADIANS(t1.Latitude))/2))) +
(SQUARE(RADIANS(t1.Latitude) - RADIANS(t2.Latitude)))
) * 6371 --Earth radius in km, use 3959 for miles
)
<= t2.Radius
)
Note that this is not the most accurate method available but is likely good enough. If you are looking at distances that stretch across the globe you may wish to Google 'haversine' formula.
It may be worth comparing this with Paddy's solution to see how well they agree and which performs best.
You calculate the distance between the two points and compare this distance to the given radius.
For calculating short distances, you can use the formula at Wikipedia - Geographical distance - Spherical Earth projected to a plane, which claims to be "very fast and produces fairly accurate result for small distances".
According to the formula, you need the difference in latitudes and longitudes and the mean latitude
with geo as (select g1.id, g1.latitude as lat1, g1.longitude as long1, g1.radius,
g2.latitude as lat2, g2.longitude as long2
from geography g1
join geography g2 on g2.location = 'shurdington'
and g1.location <> 'shurdington')
base as (select id,
(radians(lat1) - radians(lat2)) as dlat,
(radians(long1) - radians(long2)) as dlong,
(radians(lat1) + radians(lat2)) / 2 as mlat, radius
from geo)
dist as (select id,
6371.009 * sqrt(square(dlat) + square(cos(mlat) * dlong)) as distance,
radius
from base)
select id, distance
from dist
where distance <= radius
I used the with selects as intermediate steps to keep the calculations "readable".

How do I use the great circle distance calculation in T-SQL

How can i control the latitude and the longitude of the Team and the Customer with reasonable error margin from the Job Table?
if the two values are close to eachother i will return it "true", if not "false"
Job Table (like this) :
JobID CustomerID TeamID
2 13 1
3 13 2
Team Table :
TeamID Latitude Longitude
1 41.019 28.965
2 42.019 27.165
Customer Table
ID Latitude Longitude
13 13.557 13.667
The function:
CREATE FUNCTION dbo.DictanceKM(#lat1 FLOAT, #lat2 FLOAT, #lon1 FLOAT, #lon2 FLOAT)
RETURNS FLOAT
AS
BEGIN
RETURN ACOS(SIN(PI()*#lat1/180.0)*SIN(PI()*#lat2/180.0)+COS(PI()*#lat1/180.0)*COS(PI()*#lat2/180.0)*COS(PI()*#lon2/180.0-PI()*#lon1/180.0))*6371
END
i assume you mean: how can i write a select statement to return true if two latitudes and longitudes are within x miles of each other... or something like that?
look up 'great_circle_distance'
write a query that links the customer to the team.
perform the great circle distance calculation on the two lats and longs.
compare this to you desired distance.
use decode or some similar construct to turn that into a 'True' or 'False' value.
SQL has a geography datatype and a method to calculate the distance between two. Covert your Lat, Long pairs to geography.
http://msdn.microsoft.com/en-us/library/microsoft.sqlserver.types.sqlgeography.stdistance.aspx
Then you could use a function to return a true or false.

Averaging data for points in close proximity with SQL Server 2008

I have an application which receives GPS data from a mobile device as well as receiving co-ordinate data it also provides signal strength from the GSM network.
I am trying to plot the points on a map to display areas of good signal strength and areas of poor signal strength.
When I have a few points it all works well, the points are retrieved from the database and a square is built around the point with the top left corner 0.5km from the point. I then display the square shapes on the VE map using colour coding for signal strength.
The problem is that there may be thousands and thousands of readings and I need a way to average out those readings that are less than 0.5km from each other or I need to build the square (or circle perhaps) in SQL Server and average out the intersections.
I have no idea where to begin with this so any pointers to decent articles or some tips would be much appreciated.
Thanks.
One simple and somewhat inaccurate way to do this would be to decrease the granularity of your data. It might not even be inaccurate, depending on how accurate your x, y measurements are.
let's say we have the following data:
x y signal_strenth
10.2 5.1 10
10.1 5.3 12
10.3 5.5 8
If we floor the x and y values, we get:
x y signal_strenth
10 5 10
10 5 12
10 5 9
Then we can average those values by the floored x and y to show that we have average signal strength in the rectangle (10, 5) to (11, 6).
Here's the SQL:
select
floor(x) as rectangle_xmin,
floor(y) as rectangle_ymin,
floor(x) + 1 as rectangle_xmax,
floor(y) + 1 as rectangle_ymax,
avg(signal_strength) as signal_strength
from table
group by floor(x), floor(y);
Now, admittedly, you'd ideally want to group data points by distance from point to point, and this groups them by a maximum distance that varies from 1 and to square_root(2) =~1.44, flooring them into rectangular blocks. So it's less than ideal. But it may work well enough for you, especially if the flooring/grouping is less than the error in your measurement of position.
If floor() is not granular enough, you can use floor( x * someweight) / someweight to adjust it to the granularity you want. And of course you can use ceil() or round() to do the same thing.
The whole point is to collapse a bunch of nearby measurements to one "measurement", and then take the average of the collapsed values.
You might want to look into Delaunay Triangulation where you can plot X,Y,Z coordinates into a graph. It might be possible, not knowing exactly what you have for points, to use X,Y for the location and then plot the Z as signal strength and create a spike graph. I've only seen c++ examples CodePlex sample but it might be something you can write a SQL function for.
SELECT
geography::STPointFromText('POINT(' + CONVERT(varchar, AvgSignalReadings.rect_lngmin / 100) + ' ' + CONVERT(varchar, AvgSignalReadings.rect_latmin / 100) + ')', 4326) as Location,
AvgSignalReadings.lat / 100 as Latitude,
AvgSignalReadings.lng / 100 as Longitude,
AvgSignalReadings.SignalStrength
FROM
(
SELECT
FLOOR(l.Latitude * 100) as lat,
FLOOR(l.Longitude * 100) as lng,
AVG(l.SignalStrength) as SignalStrength,
COUNT(*) as NumberOfReadings
FROM SignalLog l
WHERE l.SignalStrength IS NOT NULL AND l.SignalStrength <> 0 AND l.Location IS NOT NULL
AND l.[Timestamp] > DATEADD(month, -1, GETDATE())
GROUP BY FLOOR(l.Latitude * 100), FLOOR(l.Longitude * 100))
AS AvgSignalReadings

Resources