Store circles in Postgres geometry field - postgresql

Ideally it would be something like this, but WKT doesn't have circle type.
ST_GeomFromText('CIRCLE(10 20, 10)',4326)
Although, circle type is listed among geometric types,
circle <(x,y),r> (center point and radius)
I wonder if it's possible to use circle type directly in sql:
update <table>
set the_geom = circle '((10, 20),10)'::geometry
where id = <id>;
But it says SQL Error [42846]: ERROR: cannot cast type circle to geometry.
Using ST_Buffer for storing circles is a kludge so I don't want to use it.
Alternative solution could be jsonb + geojson, but it doesn't support circles either.
UPD: There is my table structure. Currently I'm using longitude/latitude/radius, but I'd like to use either geo_json or the_geom. How could GeoJSON and WKT not support a circle?
CREATE SEQUENCE my_table_id_seq INCREMENT BY 1 MINVALUE 1 START 1;
CREATE TABLE my_table (
id INT NOT NULL,
longitude NUMERIC(10, 7) DEFAULT NULL,
latitude NUMERIC(10, 7) DEFAULT NULL,
radius INT DEFAULT NULL,
geo_json JSONB,
the_geom Geometry DEFAULT NULL, PRIMARY KEY(id)
);

Circle is native for postgresql as you can see on the manual documentation.
Geometry is a type related to PostGis Extension, and doesnt have CIRCLE but use polygons with lot of points instead.
Function starting with ST_ are also Postgis functions and work only with Postgis geometry or geography data type
SQL DEMO:
create table points ( p POINT not null);
create table lines ( l LINE not null);
create table circles ( c CIRCLE not null);
insert into points (p) values ( POINT(1.2, 123.1) );
insert into lines (l) values ( LINE(POINT(1.2, 123.1), POINT(-5, -123)) );
insert into circles (c) values ( CIRCLE(POINT(1.2, 123.1), 10) );
SELECT * FROM points;
SELECT * FROM lines;
SELECT * FROM circles;

The GIST index allows you to work efficiently with circles. If that's the only thing you intend to store in this table, then you can do it like this:
CREATE TABLE my_table (
id INT NOT NULL,
longitude NUMERIC(10, 7) DEFAULT NULL,
latitude NUMERIC(10, 7) DEFAULT NULL,
radius INT DEFAULT NULL,
geo_json JSONB
);
CREATE INDEX idx_my_table ON my_table USING GIST ( circle( point( latitude, longitude ), radius ));
As others have pointed out, you cannot mix this table with GEOMETRY types, which are incompatible.
In order to utilize the index above, you must express your WHERE criteria in similar terms: circle( point( latitude, longitude ), radius ) or '<( latitude, longitude ), radius >'::circle and use the operators that GIST knows about ... which are listed below. I'm aware that projecting the Euclidian shape of a circle onto a non-Euclidian spherical geometry has limitations, but for index purposes it should work OK with care.
https://www.postgresql.org/docs/current/gist-builtin-opclasses.html

Related

Indexing issue in postgres

It is impossible to speed up the database due to indexing.
I create a table:
CREATE TABLE IF NOT EXISTS coordinate( Id serial primary key,
Lat DECIMAL(9,6),
Lon DECIMAL(9,6));
After that I add indexing:
CREATE INDEX indeLat ON coordinate(Lat);
CREATE INDEX indeLon ON coordinate(Lon);
Then the table is filled in:
INSERT INTO coordinate (Lat, Lon) VALUES(48.685444, 44.474254);
Fill in 100k random coordinates.
Now I need to return all coordinates that are included in a radius of N km from a given coordinate.
SELECT id, Lat, Lon
FROM coordinate
WHERE acos(sin(radians(48.704578))*sin(radians(Lat)) + cos(radians(48.704578))*cos(radians(Lat))*cos(radians(Lon)-radians(44.507112))) * 6371 < 50;
The test execution time is approximately 0.2 seconds, and if you do not do CREATE INDEX, the time does not change. I suspect that there is an error in the request, maybe you need to rebuild it somehow?
I'm sorry for my english
An index can only be used if the indexed expression is exactly what you have on the non-constant side of the operator. That is obviously not the case here.
For operations like this, you need to use the PostGIS extension. Then you can define a table like:
CREATE TABLE coordinate (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
p geography NOT NULL
);
and query like this:
SELECT id, p
FROM coordinate
WHERE ST_DWithin(p, 'POINT(48.704578 44.507112)'::geography, 50);
This index would speed up the query:
CREATE INDEX ON coordinate USING gist (p);

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.

Postgresql + PostGIS: find record by geometry column

I have a table:
CREATE TABLE main.address_list
(
id serial NOT NULL,
address_string text NOT NULL,
address_position geometry(Point,4326)
CONSTRAINT pk_address_list PRIMARY KEY (id)
)
I want to find some record in this table using the address_position column:
select *
, public.ST_Y(al.address_position) latitude
, public.ST_X(al.address_position) longitude
from address_list al
where al.address_position = public.ST_SetSRID(public.ST_MakePoint(56.187339 --longitude$n
, 57.964295 --latitude$n
), 4326)
But I got the following error:
ERROR: operator is not unique: public.geometry = public.geometry
LINE 5: where al.address_position = public.ST_SetSRID(public.ST_Make...
^
HINT: Could not choose a best candidate operator. You might need to add explicit type casts.
How can I get explicit type cast?
You are trying to compare two geometries with the equals-to = operator. That is not supported; instead you should use some spatial relationship function. Probably the best thing is to use a minimal distance:
select *
, public.ST_Y(al.address_position) latitude
, public.ST_X(al.address_position) longitude
from address_list al
where public.ST_DWithin(al.address_position::geography
, public.ST_SetSRID(public.ST_MakePoint(56.187339 --longitude$n
, 57.964295 --latitude$n
), 4326)::geography
, 1.); -- maximum separation of 1m
Alternatively, you can compare the latitude and longitude values for equality:
select *
, public.ST_Y(al.address_position) latitude
, public.ST_X(al.address_position) longitude
from address_list al
where public.ST_X(al.address_position) = 56.187339 --longitude$n
and public.ST_Y(al.address_position) = 57.964295; --latitude$n
But you should keep in mind that comparing floating point values for equality is usually not a safe option due to the representation in binary form.
I've converted geometry column to WKT (Well Known Text) and compare that values.
select *
, public.ST_Y(al.address_position) latitude
, public.ST_X(al.address_position) longitude
, public.ST_AsText(al.address_position)
from address_list al
where public.ST_AsText(al.address_position) = public.ST_AsText(public.ST_SetSRID(public.ST_MakePoint(56.187339 --longitude$n
, 57.964295 --latitude$n
), 4326)
)
And added function index on column address_position
CREATE INDEX i_address_position_as_text
ON address_list
USING btree
(public.ST_AsText(address_position));

Insert GEOMETRY value using PostgreSQL 9.3

I want to insert GEOMETRY values into a table. For which I have a table with three columns as shown below:
Table: geo
create table geo
(
p1 float,
p2 float,
Paths GEOMETRY
);
Input values: I have following values
p1 = 22.9901232886963
p2 = 87.5953903123242
In SQL Server I used this:
INSERT INTO geo(Paths)
VALUES (geometry = geometry::STGeomFromText('POINT (22.9901232886963 87.5953903123242)'
,4326);
Question: Is there any function of GEOMETRY to calculate points in PostgreSQL 9.3 version?
This works for me by referring: http://www.postgresql.org/docs/current/static/functions-geometry.html.
The function:
point(point(double precision, double precision);
So I need to convert the function: using ::geometry
Finally the insert statement looks like:
insert into geo values(22.9901232886963 87.5953903123242,
point(22.9901232886963 87.5953903123242)::geometry);
Am I right?

Nearest places from a certain point

I have the following table
create table places(lat_lng point, place_name varchar(50));
insert into places values (POINT(-126.4, 45.32), 'Food Bar');
What should be the query to get all places close to particular lat/long?
gis is installed.
If you actually wanted to use PostGIS:
create table places(
lat_lng geography(Point,4326),
place_name varchar(50)
);
-- Two ways to make a geography point
insert into places values (ST_MakePoint(-126.4, 45.32), 'Food Bar1');
insert into places values ('POINT(-126.4 45.32)', 'Food Bar2');
-- Spatial index
create index places_lat_lng_idx on places using gist(lat_lng);
Now to find all of the places within 1 km (or 1000 m):
select *, ST_Distance(lat_lng, ST_MakePoint(-126.4, 45.32)::geography)
from places
where ST_DWithin(lat_lng, ST_MakePoint(-126.4, 45.32)::geography, 1000)
order by ST_Distance(lat_lng, ST_MakePoint(-126.4, 45.32)::geography);
select *
from places
where lat_lng <-> POINT(-125.4, 46.32) < 1
order by lat_lng <-> POINT(-125.4, 46.32)
Create an Indexing on a location field :
CREATE INDEX ON table_name USING GIST(location);
GiST index is capable of optimizing “nearest-neighbor” search :
SELECT * FROM table_name ORDER BY location <-> point '(-74.013, 40.711)' LIMIT 10;
Note: The point first element is longitude and the second element is latitude.