Get distance in km in Postgis - sql

I have 2 tables: City and River.
City: ID, city_name, the_geom (poin)
River: id, name, the_geom (multiline)
For each river I want to know the city farthest away and its distance in km.
For example...
I have the query
Select city1.city_name, st_distance(city1.the_geom, river1.the_geom)
from city city1, river river1
where
river1.name = 'Albany'
order by st_distance(city1.the_geom, river1.the_geom) desc
limit 1
In this case, I get the city farthest away from the Albany River.
Is this the best way to query?
I get this result:
City_name; distance
"Topeka"; 13.2534798131185
But I don't know if the result is in km... If it isn't... How can I get the result in km??

The units returned by st_distance are in spatial ref units - assuming you are using lat/lon (EPSG 4326) those units will be decimal degrees.
You have two choices - project to a suitable coordinate system in metres, or use the geography type.
Here is your query using geography:
SELECT city1.city_name,
st_distance(city1.the_geom::geography, river1.the_geom::geography) /1000 -- convert m to km
FROM city city1, river river1
WHERE
river1.name = 'Albany'
ORDER by st_distance(city1.the_geom::geography, river1.the_geom::geography) desc
LIMIT 1
(Note: I am not sure of the value of casting to geography in the order by statement).

Related

Search for a city based off city id

I'm trying to get cities which are equal to a particular city based off a user search I will be implementing .
I've got a sql query below which gives the exact output I want:
Select r.City, AVG(s.Longitude) AS Longitude, AVG(s.Latitude) AS Latitude
From CafeAddress r inner join Cafe s on s.CafeId = r.CafeId
Where City = 'Mumbai'
Group By City
Current output:
City Longitude Latitude
Mumbai -73.9904097 40.7036292
What I'm currently trying to add is a urlsafe "id" which is pretty much the city but with no white spaces, random chars just want it all lower case.
Like below:
id City Longitude Latitude
mumbai Mumbai -73.9904097 40.7036292
Is there a way to implement something like this?
Use LOWER to make it lowercase
Use TRIM to trim whitespace from beginning/end
Use REPLACE to replace interior spaces w/ underscore
Select REPLACE(TRIM(LOWER(r.City)),' ','_'),r.City, AVG(s.Longitude) AS Longitude, AVG(s.Latitude) AS Latitude
From CafeAddress r inner join Cafe s on s.CafeId = r.CafeId
Where City = 'Mumbai'
Group By City
Example, if r.City was ' SAN JOSE '
it would return: 'san_jose'
You can daisy chain REPLACE() to get rid of special characters or use TRANSLATE()

SQL (COUNT(*) / locations.area)

We are learning SQL at school, and my professor has this sql code in his documents.
SELECT wp.city, (COUNT(*) / locations.area) AS population_density
FROM world_poulation AS wp
INNER JOIN location
ON wp.city = locations.city
WHERE locations.state = “Hessen”
GROUP BY wp.city, locations.area
Everything is almost clear for me, just the aggregate function with /locations.area doesn't make any sense to me. Can anybody help?
Thank you in advance!
Look at what the query is grouped on, that tells you what each group consists of. In this case, each group is a city, and contains all the rows that have the same value for wp.city (and as the location table is joined on that value too, the locations.area is only included in the grouping so that it can be used in the result).
So each group has a number of rows, and the COUNT(*) aggregate will contain the number of rows for each group. The value of (COUNT(*) / locations.area) will be the number of rows in the group divided by the value of locations.area for that group.
If you would have data like this:
world_population
name city
--------- ---------
John London
Peter London
Sarah London
Malcolm London
Ian Cardiff
Johanna Stockholm
Sven Stockholm
Egil Stockholm
locations
city state area
----------- -------------- ---------
London Hessen 2
Cardiff Somehere else 14
Stockholm Hessen 1
Then you would get a result with two groups (as Cardiff is not in the state Hessen). One group has four people from London which has the area 2, so the population density would be 2. The other group has three people from Stockholm which has the area 1, so the population density would be 3.
Side note: There is a typo in the query, as it joins in the table location but refers to it as locations everywhere else.
Try writing it like:
SELECT wp.city,
locations.area,
COUNT(*) AS population,
(COUNT(*) / locations.area) AS population_density
FROM world_poulation AS wp
INNER JOIN location
ON wp.city = locations.city
WHERE locations.state = “Hessen”
GROUP BY wp.city, locations.area
The key is the GROUP BY statement. You are showing pairs of cities and areas. The COUNT(*) is the number of times a given pair shows up in the table you created by joining world population and location. The area is just a number, so you can divide the area by the COUNT.

Needing Clarity on SQL Join Query

Having some trouble understanding this query, particularly the WHERE in the subquery. I don't really get what it is accomplishing. Any help would be appreciated. Thanks
# Find the largest country (by area) in each continent. Show the continent,
# name, and area.
SELECT continent, name, area
FROM countries AS a
WHERE area = (
SELECT MAX(area)
FROM countries AS b
WHERE a.continent = b.continent
)
Consider the following subset of the countries data:
Continent Country Area
North America USA 3718691
North America Canada 3855081
North America Mexico 761602
Europe France 211208
Europe Germany 137846
Europe UK 94525
Europe Italy 116305
This is a correlated query that behaves as follows:
Reads the first row returned by the outer query (North America, USA, 3718691)
Runs the subquery which correlates to a.continent, North America, and returns 3855081 which is the maximum area in North America.
Does the where equality which checks to see if 3855081 matches the area on the row we're working on.
It doesn't match so the next row in the outer query is read and we start over at step 1 this time working on the second row.
Repeat for all rows in the outer query.
When we're looking at rows 2 and 4, step 4. will match so those rows will be returned by the query.
You can check the results by using this data in your countries table and running the query.
Note that this is a very poor way to determine the country with the maximum area per continent because it repeats the subquery for every country. Using my sample data, it determines the maximum area for North America 3 times and the maximum area for Europe 4 times.
Since you asked in your comment, I would write this query as follows:
SELECT a.continent, a.name, a.area
FROM countries AS a
inner join (select continent, max(area) max_area
from countries
group by continent) as b on a.continent = b.continent
WHERE a.area = b.max_area
In this version of the query, the maximum for each continent is only determined once. The original query was written to illustrate correlated queries and it's important to understand them. Correlated queries can often be used to resolve complex logic.
The subquery is finding the maximum area for countries. Which countries? All countries that match the continent of the country in the outer query.
So, for each country it gets the area of the largest country on the same continent.
The WHERE clause then says "are the two areas the same -- the maximum area and the area of this country?". It chooses only countries that have the maximum area.

PostGIS distance query between 2 selected points from same table

This is probably a really easy query, but for some reason, I just can't figure it out..
I have a table with cities (cities) and I want to get the distance between London (cities.city_name='London') and Dundee. This is my query:
SELECT
cities.city_name, ST_Distance(cities.the_geom,cities.the_geom)
from cities
WHERE cities.city_name in ('London','Dundee')
The result is in the ST_Distance field of the output is 0 for both cities.
I know that I'm missing something, but I don't know what. I assume I have to set aliases for the 2 cities, but I don't know how.
Can anybody help me out with this?
Thanks a ton in advance!!
You are comparing distance between the same point in your query:
ST_Distance(cities.the_geom,cities.the_geom) --compares the same column in the list of the two cities:
CITY | GEOM | DISTANCE
-----------------------------------
London | GEOM1 | DISTANCE (GEOM1, GEOM1)
Dundee | GEOM2 | DISTANCE(GEOM2, GEOM2)
Hope you can see my point from table above
Maybe something like this:
SELECT ST_Distance(
(SELECT the_geom FROM cities WHERE city_name ='London'),
(SELECT the_geom FROM cities WHERE city_name ='Dundee')
)

Find records where lat long values are different for the same base address

I need help writing a query in Oracle SQL Developer which finds where one attribute matches but the corresponding attributes are different. For example, I need to find records where address fields match, but then the latitude and longitude column have different values than the corresponding records.
This is an example of what I am talking about (record #3 is the problem):
ID Address latitude longitude
1 1104 West St 35.3 -90.1
2 1104 West St 35.3 -90.1
3 1104 West St 36.4 -94.2
The point of this is to find where lat long values are different for the same base address. The reason multiple records like this exist is because they are different units in the building, thanks in advance.
Use analytic functions, if you want to see the detailed records. The idea is to calculate the minimum and maximum latitude and longitude for each address and then compare them:
select ID, Address, latitude, longitude
from (select t.*,
min(latitude) over (partition by address) as minlat,
max(latitude) over (partition by address) as maxlat,
min(longitude) over (partition by address) as minlong
max(longitude) over (partition by address) as maxlong
from table t
) t
where minlat <> maxlat or minlong <> maxlong
order by address, id;
If the values are actually stored as floating point numbers, then you might want a little bit of wiggle room:
where (maxlat - minlat) > 0.00001 or (maxlong - minlong) > 0.00001
(Note: abs() isn't needed because I know which is bigger and which is smaller.)