Using PostgreSQL-PostGIS for coordinates - postgresql

I'm trying to create a database for storing coordinates
Lets say X has: Latitude 20°20’20.00’’ , Longitude 20°20’20.00’’E , with 1 mile range.
Y has: Latitude 20°21’21.00 , Longitude 20°21’21.00’’
I want to know whether Y location lies within X range.
I've been trying for days to find a way to manage that; I came across quad tree, k-d tree, but I could not find a way to represent it in a database.
I finally stumbled on PostGIS but I don't know how to create and manage simple latitude longitude interaction whit it.

The first thing you want to do is learn more about PostGIS.
After you installed the postgis extension, you can use the geography type for your purposes:
CREATE TABLE locations (
id serial PRIMARY KEY,
loc geography(POINT, 4326), -- See PostGIS docs for type modifiers
rng double precision, -- in miles, PostGIS uses km
...
);
CREATE INDEX locations_geo ON locations USING GIST (loc);
Your lat,long pairs go into the loc column as a geography type; PostGIS will do the spatial analysis with that data. You can now find all points within range of the X point (say, with id=12) as follows:
SELECT src.id AS src, src.rng, near.*
FROM locations near
JOIN locations src ON ST_DWithin(src.location, near.location, src.rng * 0.6215) -- miles to km
WHERE src.id = 12;

Related

how to find ou lat long with 2KM radius using Postgres

I have a postgres table with lat-long values stored as point (Geometric data type). I am interested to query all rows with 2km radius for the given lat-long values. Also, I am expecting for a suitable datatype for this, currently I stored these values as POINT. But on some investigation, I found to use POLYGON here. But even though I couldn't able to achieve the results what expected.
Can any one point me the exact query with suitable GTS functions to achieve this
https://postgis.net/workshops/postgis-intro/spatial_relationships.html
example explanation:
SELECT name
FROM nyc_streets
WHERE ST_DWithin(
geom,
ST_GeomFromText('POINT(583571 4506714)',26918),
10
);
The first "geom" refer to the column name in nyc_streets.
ST_GeomFromText: transform text to geom. 26918 is srid.
10 : 10 meters.
To query geometries within a certain radius you might wanna use ST_DWithin. In order to use it with metres you have to either use a SRS that has metre as unit, such as EPSG:26918, or use geography instead of geometry:
SELECT *
FROM mytable
WHERE ST_DWithin(ST_MakePoint(1,2)::geography, -- 1=long / 2=lat
geom::geography, -- casting the 'geometry' to 'geography'
2000); -- 2km radius
In case you're dealing with different geometry types, such as polygon or linestring, you might wanna use ST_GeogFromText instead of ST_MakePoint:
SELECT *
FROM mytable
WHERE ST_DWithin(ST_GeogFromText('POINT(1 2)'),
geom::geography,2000);
SELECT *
FROM mytable
WHERE ST_DWithin(ST_GeogFromText('POLYGON((1 1,2 2,3 3,1 1))'),
geom::geography,2000);
Keep in mind that transforming a geometry is much more than just change its SRID - check ST_Transform.
Further reading
Getting all Buildings in range of 5 miles from specified coordinates
How is postgis treating coordinates sent with different SRID

PostGIS convert meters to degree using epsg 5186

I have a few questions about geometry and geography in PostGIS.
I am currently using PostGIS and Postgresql.
Most of my spatial data is from Korea which is basically latitude and longitude.
For testing, I have created two tables with the same latitude and longitude data but different data types, one for geography with SRID 4326 and the other for geometry with SRID 5186.
create table geometry_stores
(
id serial primary key,
location geometry(POINT, 5186) not null
);
create table geography_stores
(
id serial primary key,
location geography(POINT, 4326) not null
);
You can find more details of EPSG 5186 on this link https://epsg.io/5186
Here is a list of question I have got:
PostGIS has this method
ST_DWithin(geometry g1, geometry g2, double precision distance_of_srid);
is distance_of_srid a unit of EPSG? Is there any way I can convert meters (e.g. 1km) to distance_of_srid with EPSG 5186?
I understand that geography calculation measures the distances between points as true paths over a sphere while geometry calculation measures the distances between points as true paths over a Cartesian plane. Then if I give exactly the same distance to the following queries, they are supposed to yield different results or same results? because my understanding is that geometry with SRID 5186 is already projected with distortion of earth, then they should yield the same results?
select *
from geography_stores
where st_dwithin(location, st_setsrid(st_point(126.970769, 37.555479), 4326), same_distance_meter)
select *
from geometry_stores
where st_dwithin(location, st_setsrid(st_point(126.970769, 37.555479), 5186), same_distance_degree)
When I calculate distance on the geometry table with the following query, it gives me a degree, not meters. Is there any way I can convert this degree to meters with consideration of distortion of the earth?
select st_distance(location, st_setsrid(st_point(126.970769, 37.555479), 5186))
from geometry_stores
where id = 1;
I have tried with this query but got some error of Only lon/lat coordinate systems are supported in geography. Where: SQL function "st_distancesphere" during inlining
select st_distancesphere(location, st_setsrid(st_point(126.970769, 37.555479), 5186))
from geometry_stores
where id = 1;
I have read documents at the PostGIS website and some questions in StackOverflow but still got those three questions. Thank you guys for your help.
------------------------------- UPDATED -----------------------------------------
The column for my spatial data is geometry(POINT, 5186) so the table definition would be as following. Note that it is not geometry(POINT, 4326) not to convert it to geometry(POINT, 5186) on calculation. Should I store my data in geometry(POINT, 4326) and convert it on calculation?
create table geometry_stores
(
id serial primary key,
location geometry(POINT, 5186) not null
);
I executed following query and got results as follows:
select st_distance(st_setsrid(st_makepoint(126.808183, 37.463557), 4326)::geography,
st_setsrid(st_makepoint(126.970769, 37.555479), 4326)::geography);
st_distance
--------------
17627.3138509
select st_distance(st_setsrid(st_makepoint(126.808183, 37.463557), 5186)::geometry,
st_setsrid(st_makepoint(126.970769, 37.555479), 5186)::geometry)
st_distance
--------------
0.186772218169622
It seems that the second's query gives me degree while the first one gives me meters. Am I doing something wrong in my query, please?
For st_within, I populated 3M data in geometry_stores table and the spatial data spread over at least 10km. I executed the following query.
select *
from users
where st_dwithin(location, st_setsrid(st_point(126.970769, 37.555479), 5186), 0.001)
This query gives me 158 rows and geometry viewer displays as per picture below.
Let's execute the same query with distance 1 not 0.0001
select *
from users
where st_dwithin(location, st_setsrid(st_point(126.970769, 37.555479), 5186), 1)
This query gives me 32792923 rows which is all data in the table.
Considering that the spatial data spread over at least 10km, it seems that the st_within query calculates the distance between two geometries with a unit (degeree) of EPSG5186 not meters. Then, I'd like to know if I can convert meters to the unit (degree) of EPSG5186 because I'd like to query with meters, not degree which I don't know how far a unit (degree) of EPSG5186 is.
Is distance_of_srid a unit of EPSG?
Yes. Distances using geometry type geometries are calculated using the unit of measurement from the corresponding Spatial Reference System.
Is there any way I can convert meters (e.g. 1km) to distance_of_srid with EPSG 5186?
According to the documentation, the unit of EPSG:5186 is already metres, so you don't have to convert anything. Bur also keep in mind that distances using geography type geometries are also calculated using metres, e.g.
SELECT
ST_Distance(
'SRID=4326;POINT(127.49 36.65)'::geometry,
'SRID=4326;POINT(128.06 36.43)'::geometry) AS geometry_distance,
ST_Distance(
'SRID=4326;POINT(127.49 36.65)'::geography,
'SRID=4326;POINT(128.06 36.43)'::geography) AS geography_distance
;
geometry_distance | geography_distance
-------------------+--------------------
0.610982814815612 | 56578.57823391
(1 Zeile)
Then if I give exactly the same distance to the following queries, they are supposed to yield different results or same results? because my understanding is that geometry with SRID 5186 is already projected with distortion of earth, then they should yield the same results?
The results will differ. They might have the same unit of measurement, but they aren't projected on the same surface. The following example transforms the coordinates from 4326 to 5186 and calculates the distance:
SELECT
ST_Distance(
'SRID=4326;POINT(127.49 36.65)'::geography,
'SRID=4326;POINT(128.06 36.43)'::geography),
ST_Distance(
ST_Transform('SRID=4326;POINT(127.49 36.65)'::geometry,5186),
ST_Transform('SRID=4326;POINT(128.06 36.43)'::geometry,5186));
st_distance | st_distance
----------------+------------------
56578.57823391 | 56582.0899018353
(1 Zeile)
When I calculate distance on the geometry table with the following query, it gives me a degree, not meters. Is there any way I can convert this degree to meters with consideration of distortion of the earth?
Isn't the data type geography what you're looking for? As the documentation says:
Regardless which spatial reference system you use, the units returned by the measurement (ST_Distance, ST_Length, ST_Perimeter, ST_Area) and for input of ST_DWithin are in meters.
Just for fun, the following query calculates the distance between two points explicitly defining the 4326 spheroid, using ST_DistanceSpheroid, and casting the coordinates from geometry to geography, which basically does the same:
SELECT
ST_DistanceSpheroid(
'POINT(127.49 36.65)',
'POINT(128.06 36.43)',
'SPHEROID["WGS 84",6378137,298.257223563]'),
ST_Distance(
'SRID=4326;POINT(127.49 36.65)'::geography,
'SRID=4326;POINT(128.06 36.43)'::geography);
st_distancespheroid | st_distance
---------------------+----------------
56578.5782339123 | 56578.57823391
Regarding when to use geometry or geography the documentation says:
"The type you choose should be conditioned on the expected working area of the application you are building. Will your data span the globe or a large continental area, or is it local to a state, county or municipality?"
Things to consider:
use case covers small area: stick to geometry and use a SRS that better suits your area.
use case covers large areas (countries/continents): use geography - although it might be a bit slower.
Do the functions you wanna use support geography? Most PostGIS functions do not support it! Check this matrix for more details. If the functions you wanna use do not support geography, you have no other choice but to use geometry ;-) Since your use case mostly covers Korea, I see no problem in using EPSG 5186.
EDIT: Regarding the question Update.
You cannot simply change the SRID of geometries to get it transformed into another reference system! What you've done was to get a WGS84 coordinate pairs and simply exchange its SRID, which is not the way it works. You have to always use ST_Transform for that. Take a look what the coordinates look like after you apply it:
SELECT
ST_AsText(ST_Transform('SRID=4326;POINT(126.808183 37.463557)'::geometry,5186));
st_astext
------------------------------------------
POINT(183030.248454493 540476.713582621)
(1 Zeile)
It means that POINT(183030.248454493 540476.713582621) and POINT(126.808183 37.463557) are the same coordinate pairs, but in different reference systems. The following query will make it clear that both geography and 5186 return results in metres:
SELECT
--Transforming from 4326 to 5186 and calculating the distance
ST_Distance(
ST_Transform('SRID=4326;POINT(126.808183 37.463557)'::geometry, 5186),
ST_Transform('SRID=4326;POINT(126.970769 37.555479)'::geometry, 5186)),
-- Distance using geography
ST_Distance(
'SRID=4326;POINT(126.808183 37.463557)'::geography,
'SRID=4326;POINT(126.970769 37.555479)'::geography);
st_distance | st_distance
------------------+---------------
17627.3383377316 | 17627.3138509

How to configure PostgreSQL with Postgis to calculate distances

I know that it might be dumb question, but I'm searching for some time and can't find proper answer.
I have PostgreSQL database with PostGIS installed. In one table I have entries with lon lat (let's assume that columns are place, lon, lat).
What should I add to this table or/and what procedure I can use, to be able to count distance between those places in meters.
I've read that it is necessary to know SRID of a place to be able to count distance. Is it possible to not know/use it and still be able to count distance in meters basing only on lon lat?
Short answer:
Just convert your x,y values on the fly using ST_MakePoint (mind the overhead!) and calculate the distance from a given point, the default SRS will be WGS84:
SELECT ST_Distance(ST_MakePoint(lon,lat)::GEOGRAPHY,
ST_MakePoint(23.73,37.99)::GEOGRAPHY) FROM places;
Using GEOGRAPHY you will get the result in meters, while using GEOMETRY will give it in degrees. Of course, knowing the SRS of coordinate pairs is imperative for calculating distances, but if you have control of the data quality and the coordinates are consistent (in this case, omitting the SRS), there is not much to worry about. It will start getting tricky if you're planing to perform operations using external data, from which you're also unaware of the SRS and it might differ from yours.
Long answer:
Well, if you're using PostGIS you shouldn't be using x,y in separated columns in the first place. You can easily add a geometry / geography column doing something like this.
This is your table ...
CREATE TABLE places (place TEXT, lon NUMERIC, lat NUMERIC);
Containing the following data ..
INSERT INTO places VALUES ('Budva',18.84,42.92),
('Ohrid',20.80,41.14);
Here is how you add a geography type column:
ALTER TABLE places ADD COLUMN geo GEOGRAPHY;
Once your column is added, this is how you convert your coordinates to geography / geometry and update your table:
UPDATE places SET geo = ST_MakePoint(lon,lat);
To compute the distance you just need to use the function ST_Distance, as follows (distance in meters):
SELECT ST_Distance(geo,ST_MakePoint(23.73,37.99)) FROM places;
st_distance
-----------------
686560.16822422
430876.07368955
(2 Zeilen)
If you have your location parameter in WKT, you can also use:
SELECT ST_Distance(geo,'POINT(23.73 37.99)') FROM places;
st_distance
-----------------
686560.16822422
430876.07368955
(2 Zeilen)

Convert planet_osm_nodes lat lon to "normal" lat long

I have downloaded the openstreetmap data for Germany. I am trying to find a closest point using planet_osm_nodes, but the lat lon in the table do not make any sense to me. Let me try to show by example why not:
I take a random point
SELECT * FROM planet_osm_nodes LIMIT 1;
id | lat | lon | tags
-----------+-----------+-----------+----------------------
363692391 | 596568676 | 109247330 | {ref,1A,power,tower}
When inserting a point into the (lat, lon), so (59.6568676, 10.9247330) or (10.9247330, 59.6568676), and entering it in https://www.openstreetmap.org/search?query=59.6568676%2C%2010.9247330#map=5/59.657/10.925, I end up in Norway or the sea.
When using a EPSG:3857 to EPSG:4326 converter (https://epsg.io/transform#s_srs=3857&t_srs=4326&x=109247330.0000000&y=596568676.0000000), I try to use the converted GPS coordinates to pinpoint on a map, and even swap x/y and lat/lon, but I still do not end up in Germany (but North/South Pole, US, etc).
When I look up the "way" that belongs to the "node":
SELECT * FROM planet_osm_ways WHERE 363692391 = ANY (nodes);
48616848 | {363692392,363692391,...,302275015,346153952,251417206} | {cables,6,name,"Bürs - Obermooweiler blau;Bürs - Ober
mooweiler weiß",power,line,ref,401;402,voltage,380000,wires,quad}
Find the "way" (thanks google) in OSM online:
https://www.openstreetmap.org/relation/1750798/history#map=9/47.3988/9.7439
Pick a GPS coordinate closeby:
https://www.openstreetmap.org/node/346153670
I get a GPS that does make me land in Germany:
Location: 47.6525789, 9.8031666
Which makes me believe that I do have the right database, and the data should be there somehow.
Question: How do I convert the lat lon in the table mentioned above to get coordinates that I can enter in Google Maps/Openstreetmaps that show me the correct location (in Germany)?
select st_setsrid(
st_makepoint(
n.lon/power(10,floor(log(abs(n.lon)))-1),
n.lat/power(10,floor(log(abs(n.lat)))-1)),4326)
from planet_osm_nodes n
because geometry is saved as integer we have to convert it into double. projection is 4326 (not 3857 that is used for ways geometry).
So we have to
determine 10-based logarithm of the value.
reduce it by 1 as assumption is that first two digits from integer value
should stay before point.
divide original value by 10 power whatever was calculated on step 2.
As pointed out by JGH, a similar question was asked at:
https://gis.stackexchange.com/questions/57003/what-format-is-lat-long-stored-in-osm-postgis
The main gist I got from it:
"The planet_osm_nodes table you refer is listed (see osm2pgsql wiki
entry) as a temporary table used when running the importer with
limited memory ("slim" mode)." by user diciu
Because of this, most tags are available and reachable by performing geometry on the "ways" key in other tables
The maxspeed tag however is not converted into the new tables
as far as I can see
one can transform the lat lon from planet_osm_nodes, get the id, and search for the id in the nodes column of planet_osm_ways. This will give access to the tags
The final answer in code to get the lat lon converted (from the linked question on gis.stackexchange.com with the variables plugged in):
ST_Transform( ST_GeomFromText('POINT('||lon/100||' '||lat/100||')',3785 ),4326)
Proper transformation of this strange data should be:
select ST_Transform(ST_GeomFromText('POINT('||lon::numeric/100||' '||lat::numeric/100||')',3857 ),4326) from planet_osm_nodes
Without adding ::numeric for "lat" and "lon" you lost precision and this can be even kilometers in some cases.
EPSG 3857, 3785, 900913 (and many others) are all same but most "proper"(in terms wildly used) is 3857

Find KNN with a geometry data type using POSTGIS in postgresql

i want to find k nearest points for a point with the best performance in postgresql using PostGIS.
The structure of my table is :
CREATE TABLE mylocations
(id integer,
name varchar,
geom geometry);
sample inserted row is:
INSERT INTO mylocations VALUES
(5, 'Alaska', 'POINT(-172.7078 52.373)');
I can find nearest points by ST_Distance() with the following query :
SELECT ST_Distance(geography(geom), 'POINT(178.1375 51.6186)'::geometry) as distance,ST_AsText(geom),name, id
FROM mylocations
ORDER BY distance limit 10;
but actually i want to find them without calculating distance of my points with all points of table.
in fact i want to find the best query with best performance, because my table would have huge data.
i appreciate for your thoughts
You are missing <-> operator which Returns the 2D distance between A and B. Make sure your geom types and SRID are the same.
SELECT ST_Distance(geom,
'POINT(178.1375 51.6186)'::geometry) as distance,
ST_AsText(geom),
name, id
FROM mylocations
ORDER BY geom <-> 'POINT(178.1375 51.6186)'::geometry limit 10
finally i could answer to my question with this query:
SELECT id, name
FROM mylocations
WHERE ST_DWithin(geom::geography,
ST_GeogFromText('POINT(-73.856077 40.848447)'),
1609, false);
actually i want to give a point as a center of a circle with radius 1609 (as meter) and get all neighbours have distance less than 1609 meter to center of the circle.