PostGIS Query always brings back all results - postgresql

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.

Related

postgis st_contains not seems

I am new to postgis and I cant figure out why this returns false(in the ST_contains function) for any value I try in the point
select st_astext(geoma),
st_astext(geomb),
st_contains(geoma,geomb)
from (
select
ST_GeomFromGeoJSON('{"type":"Polygon","coordinates":[[[25.64214,-100.27873]],[[25.69505,-100.37006]],[[25.72599,-100.27702]],[[25.680978320466,-100.25384240723]]],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}}') as geomA,
ST_GeomFromGeoJSON('{"type":"Point","coordinates":[25.683096, -100.311577]}') as geomB
) as p
I drew the points in google maps to confirm my data supposedly, but it returns false when according to google maps it should be true
The GeoJSON has several errors, and does not conform to the specification, such as:
You need to flip the axis order to (X Y) or (lng lat). It might not matter now, but it will if you try to do anything else.
The LinearRing for the Polygon is really broken, and needs to consist of a single sequence of closed coordinates.
The CRS property is provided for one geometry but not the other, which would normally raise a "Operation on mixed SRID geometries" error from PostGIS. Provide the CRS for both or none of the geometries.
Try this:
SELECT ST_AsText(geomA),
ST_AsText(geomB),
ST_Contains(geomA, geomB)
FROM (
SELECT
ST_GeomFromGeoJSON('{"type":"Polygon","coordinates":[[[-100.27873,25.64214],[-100.37006,25.69505],[-100.27702,25.72599],[-100.25384240723,25.680978320466],[-100.27873,25.64214]]],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}}') AS geomA,
ST_GeomFromGeoJSON('{"type":"Point","coordinates":[-100.311577,25.683096],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}}') AS geomB
) AS p;
-[ RECORD 1 ]----------------------------------------------------------------------------------------------------------------------------
st_astext | POLYGON((-100.27873 25.64214,-100.37006 25.69505,-100.27702 25.72599,-100.25384240723 25.680978320466,-100.27873 25.64214))
st_astext | POINT(-100.311577 25.683096)
st_contains | t

PostgreSQL select query to extract latitude and longitude from a point

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

Unit of return value of ST_Distance

I need to calculate the distance between
all buildings and
all hospitals
in a map imported from OSM.
I use following query:
SELECT building_id, hospital_id, ST_Distance(building_centroid, hospital_location)
FROM
(
select planet_osm_polygon.osm_id building_id, ST_Centroid(planet_osm_polygon.way) building_centroid
from planet_osm_polygon
where building = 'yes'
) buildings,
(
select planet_osm_point.osm_id hospital_id, planet_osm_point.way hospital_location
from planet_osm_point
where amenity = 'hospital') hospitals
I get strange results - the distance is always smaller than 1.
How can I get the to know the unit, in which these values are reported?
Update 1: Sample result:
Update 2: This query seems to work
SELECT building_id, hospital_id, ST_Distance_sphere(building_centroid, hospital_location) distance
FROM
(
select planet_osm_polygon.osm_id building_id, ST_Centroid(planet_osm_polygon.way) building_centroid
from planet_osm_polygon
where building = 'yes'
) buildings,
(
select planet_osm_point.osm_id hospital_id, planet_osm_point.way hospital_location
from planet_osm_point
where amenity = 'hospital') hospitals
ORDER BY distance
The general rule for units is that the output length units are the same as the input length units.
The OSM way geometry data has length units of degrees of latitude and longitude (SRID=4326). Therefore, the output units from ST_Distance will also have lenth units of degrees, which are not really useful.
There are several things you can do:
Use ST_Distance_Sphere for fast/approximate distances in metres
Use ST_Distance_Spheroid for accurace distances in metres
Convert the lat/long geometry data types to geography, which automagically makes ST_Distance and other functions to use linear units of metres

How can I extract some LINESTRING consisted of 3 or more POINTs to several LINESTRINGs each by 2 POINTs in PostGIS

I have to get an array of simple lines from one multipoint linestring. How can I do it?
Try this query:
SELECT MakeLine(sp,ep)
FROM (
SELECT pointn(wkb_geometry, generate_series(1, npoints(wkb_geometry)-1)) as sp,
pointn(wkb_geometry, generate_series(2, npoints(wkb_geometry) )) as ep
FROM geom_table
) as tmp;
Though this isn't very performant on larger linestrings.

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 ...