PostgreSQL select query to extract latitude and longitude from a point - postgresql

What SELECT query should be used to extract latitude and longitude from a point?
I cannot use PostGIS.
Example point (point type value) stored in the database:
my_point
--------------
(50.850,4.383)
Expected result after executing the query:
lat | lng
---------------
50.850 | 4.383
The query below works fine but it does not look efficient.
SELECT
split_part(trim(my_point::text, '()'), ',', 1)::float AS lat,
split_part(trim(my_point::text, '()'), ',', 2)::float AS lng
FROM my_table;

Always Read The Fine Manuals
It is possible to access the two component numbers of a point as though the point were an array with indexes 0 and 1. For example, if t.p is a point column then SELECT p[0] FROM t retrieves the X coordinate and UPDATE t SET p1 = ... changes the Y coordinate. In the same way, a value of type box or lseg can be treated as an array of two point values.

Another option would be:
SELECT
ST_X(point) as longitude,
ST_Y(point) as latitude
FROM your_table_name

Related

PostGIS Query always brings back all results

I'm playing with PostGIS for the first time and I'm getting all the results back regardless of the distance I use on a distance query. My data looks like this:
Id GeoLocation
8eb63480-4d63-11ea-b06a-8c1645ef6ad2 POINT (52.6323202 1.2947649)
a0f2dde6-4d64-11ea-b06a-8c1645ef6ad2 POINT (52.6294342 1.2936336)
a0f2dde7-4d64-11ea-b06a-8c1645ef6ad2 POINT (52.6277909 1.2909079)
a0f2dde8-4d64-11ea-b06a-8c1645ef6ad2 POINT (52.6260535 1.2952051)
And when I run a query for a point that should be over a mile away:
SELECT * FROM "Locations" WHERE ST_DWithin("GeoLocation", 'POINT(52.6219322 1.2630061)', 1);
I get all of the rows back. My understanding is that the distance parameter should be in metres, so I shouldn't get any results back.
Could it be coordinate format issue? What am I missing?
Using parameters of type geography you get the returned distance in meters, therefore you need to convert it to miles in case you prefer to work with this unit of measurement. If you can cope with degrees, just stick to geometry.
WITH locations (geolocation) AS (
VALUES ('POINT (52.6323202 1.2947649)'),
('POINT (52.6294342 1.2936336)'),
('POINT (52.6277909 1.2909079)'),
('POINT (52.6260535 1.2952051)')
)
SELECT *
FROM locations
WHERE ST_DWithin(
geoLocation::geography,
'POINT(52.6219322 1.2630061)'::geography, 1609*2.2) ;
geolocation
------------------------------
POINT (52.6294342 1.2936336)
POINT (52.6277909 1.2909079)
(2 Zeilen)
EDIT: #JGH pointed out that ST_Distance does not use a spatial index and my previous suggestion was to use it instead of ST_DWithin. It means I was wrong with my preference for ST_Distance :) Here is anyway how to achieve similar results with ST_Distance for those still willing to use it:
WITH locations (geolocation) AS (
VALUES ('POINT (52.6323202 1.2947649)'),
('POINT (52.6294342 1.2936336)'),
('POINT (52.6277909 1.2909079)'),
('POINT (52.6260535 1.2952051)')
)
SELECT *
FROM locations
WHERE ST_Distance(
geoLocation::geography,
'POINT(52.6219322 1.2630061)'::geography) * 0.000621371 > 2.2 ;
geolocation
------------------------------
POINT (52.6323202 1.2947649)
POINT (52.6260535 1.2952051)
(2 Zeilen)
Further reading: Getting all Buildings in range of 5 miles from specified coordinates
Since these seem to be coordinates in longitude and latitude, you should use the geography data type.

Create new column with coordinates from centroids using st_transform in PostGIS / PostgreSQL

I have geometry data that I'm trying to transform to lon lat and store in a new column. I have created a new column called 'coordinates' and written the following query:
UPDATE places_centroids
SET coordinates = st_y(st_transform(new_centroid, 4326)) AS lat,
st_x(st_transform(new_centroid, 4326)) AS lon;
But it returns: ERROR: ERROR: syntax error at or near
LINE 2: ...coordinates = st_y(st_transform(new_centroid, 4326)) AS lat,
However, it works fine when i writing the query with a select statement:
SELECT st_y(st_transform(new_centroid, 4326)) AS lat,
st_x(st_transform(new_centroid, 4326)) AS lon
FROM places_centroids;
Can anyone see what's wrong with my query?
You can create two columns, lat and lng for example of type double precision and do this:
UPDATE places_centroids
SET lat = st_y(st_transform(new_centroid, 4326)),
lng= st_x(st_transform(new_centroid, 4326));
or define coordinates as the native point type:
UPDATE places_centroids
SET coordinates = point(st_y(st_transform(new_centroid, 4326)),
st_x(st_transform(new_centroid, 4326)));
I personally prefer to store in two columns.
Best regards,
Bjarni

Query PostGIS radius giving me odd results

I'm new to PostGIS and I'm trying to create a radius query. I have a table with a geometry field (position) and also have the latitude and longitude values on separate fields.
I'm trying to find points on a 10 km radius from lat: 40.753777, lon: -73.981568.
with:
SELECT postcode, lat, lon, st_asgeojson(position) geojson, ST_Distance(ST_MakePoint(lat, lon), ST_MakePoint(40.753777, -73.981568)) distance FROM addresses WHERE ST_DWithin(ST_MakePoint(lat, lon), ST_MakePoint(40.753777, -73.981568), 10000) order by id limit 10;
The results give me very far a way points. The same query with earth distance using the lat and lon directly give me much closer results.
SELECT postcode, lat, lon, st_asgeojson(position) geojson FROM addresses WHERE earth_box(ll_to_earth(40.753777, -73.981568), 10000) #> ll_to_earth(addresses.lat, addresses.lon) order by id limit 10;
But I really don't know if this is right either, what's wrong with the PostGIS query?
A few notes:
I think you swapped the lat and lon when you made a point, your line says ST_MakePoint(40.753777, -73.981568), but the definition is:
geometry ST_MakePoint(double precision x, double precision y);
Note: x is longitude and y is latitude
So it should have been ST_MakePoint(-73.981568, 40.753777) instead.
As a simple solution you can use ST_Distance_Spheroid function ( http://www.postgis.org/docs/ST_Distance_Spheroid.html ):
SELECT
postcode, lat, lon, st_asgeojson(position) AS geojson,
ST_Distance_Spheroid(
position,
ST_GeomFromText('POINT(-73.981568 40.753777)',
4326), 'SPHEROID["WGS 84",6378137,298.257223563]'
) as distance
FROM addresses
WHERE distance < 10000 LIMIT 10;
For a more precise distances, add a new column of type geography from you existing column position of type geometry:
ALTER TABLE addresses ADD COLUMN position_geog geography(Point,4326);
UPDATE addresses SET position_geog = position::geography;
CREATE INDEX ON addresses USING gist(position_geog);
-- and now you can use ST_DWITHIN with meters...

Postgis nearest coordinates

I'm trying to make a REST service that returns a list of places ordered by distance from the user coordinate. I found this query using postgis:
SELECT *
FROM your_table
ORDER BY your_table.geom <-> "your location..."
LIMIT 5;
But I'm not able to apply this to my actual database. I have a table that contains these columns:
title, address, description, latitude, longitude
all these values as Strings.
I'll be very happy if someone help me. Thx!
I dont know why, but ORDER BY <-> isnt exact. Sometime the closest link is on the 3rd position. So I get 101 element and then use distance to selected the closest one.
CREATE OR REPLACE FUNCTION map.get_near_link(
x numeric,
y numeric)
RETURNS TABLE(Link_ID int, distance int) AS
$BODY$
DECLARE
strPoint text;
BEGIN
strPoint = 'POINT('|| X || ' ' || Y || ')';
With CTE AS (
SELECT Link_ID,
TRUNC(ST_Distance(ST_GeomFromText(strPoint,4326), geom )*100000)::integer as distance
FROM map.vzla_seg S
ORDER BY
geom <-> ST_GeomFromText(strPoint, 4326)
LIMIT 101
)
SELECT *
FROM CTE
ORDER BY distance
LIMIT 5
In order to use PostGIS you have to enable the extension in the database. Ideally, you just run the CREATE EXTENSION postgis; command and it works. NOTE form the install page: DO NOT INSTALL it in the database called postgres. For more information visit the site.
Adding a geometry column (spatial data can be stored in this type of columns) to your table:
SELECT AddGeometryColumn(
'your_schema',
'your_table',
'geom', -- name of the column
4326, -- SRID, for GPS coordinates you can use this, for more information https://en.wikipedia.org/wiki/Spatial_reference_system
'POINT', -- type of geometry eg. POINT, POLYGON etc.
2 -- number of dimension (2 xy - 3 xyz)
);
UPDATE yourtable t SET t.geom = ST_SetSRID(ST_MakePoint(t.x, t.y), 4326)
-- the x and y is the latitude and longitude
Now you can use spatial queries on your table like this:
SELECT
*
FROM
your_table
ORDER BY
your_table.geom <-> ST_SetSRID(ST_MakePoint(x, y), 4326)
LIMIT 5;
NOTE: as others mentioned, below PostgreSQL 9.5 <-> isn't always reliable.

Finding and Ordering Latitude and Longitude in SQL

I have a SQL database where I store longitude and latitude from an iPhone application. I need to query all the records starting from a given location to the other far most location.
For example, I have longitude x and latitude y. I want all the records first whose longitude matches x the most closely and whose latitude matches y the most closely. I need to all the records one by one in the chain from nearest to farthest. The more distant the location, the greater the value of longitude and latitude will be than x and y.
I hope you got the point and I am waiting for the answer.
Distance with latitude and longitude is not a simple calculation, but one requiring spherical trigonometry.
acos(cos(lat1)*cos(lon1)*cos(lat2)*cos(lon2) +
cos(lat1)*sin(lon1)*cos(lat2)*sin(lon2) +
sin(lat1)*sin(lat2)) * R(adius of the earth)
So this query
select locID, locName, locDesc, lat, lon, locDiffMeters
from (select locID, locName, locDesc, lat, lon,
acos(cos($lat)*cos($lon)*cos(lat)*cos(lon) +
cos($lat)*sin($lon)*cos(lat)*sin(lon) +
sin($lat)*sin(lat) ) * 6,371,000 -- earths radius in meters
as locDiffMeters
from locationTable
where locID <> $ID
) a
order by locDiffMeters
Is probably the right answer, assuming you have that capable of a math library.
Similar to Fosco, but using Pythagoras' Theorem:
select locID, locName, locDesc, lat, lon, locDiff from
(select locID, locName, locDesc, lat, lon,
sqrt((lat - $LAT)*(lat - $LAT) + (lon - $LON)*(lon - $LON)) as locDiff
from locationTable
where locID <> $ID) a
order by locDiff
For really large distances (or locations far from the equator) you should ideally use a geodesic.
Assuming that you've queried the start location lat/lon and location ID... I am using $LAT, $LON, and $ID as placeholders:
select locID, locName, locDesc, lat, lon, locDiff
from (
select locID, locName, locDesc, lat, lon, ABS(lat - $LAT) + ABS(lon - $LON) as locDiff
from locationTable
where locID <> $ID
) a
order by locDiff
Hopefully this helps... may not be the most optimized method, but it should be pretty close.
If you don't have to deal with large distances (see Adam's answer), you might consider using PostgreSQL's geometric types and associated functions.
If you're using Postgresql, add the PostGIS extension and check out the ST_Distance_Sphere
ST_Distance_Sphere — Returns minimum distance in meters between two lon/lat geometries. Uses a spherical earth and radius of 6370986 meters. Faster than ST_Distance_Spheroid, but less accurate. PostGIS versions prior to 1.5 only implemented for points.
SELECT round(CAST(ST_Distance_Sphere(ST_Centroid(the_geom), ST_GeomFromText('POINT(-118 38)',4326)) As numeric),2) As dist_meter ...