Issue with ST_CONTAINS and ST_WITHIN in PostGIS - postgis

My environment: PostgreSQL 11.4 with PostGIS 2.5.2
CREATE TABLE m_polygon (id SERIAL PRIMARY KEY, bounds POLYGON);
INSERT INTO m_polygon(bounds) VALUES(
'(0.0, 0.0), (0.0, 10.0), (10.0, 0.0), (10.0, 10.0), (0,0)'
);
SELECT ST_WITHIN(m_polygon.bounds , m_polygon.bounds ) FROM m_polygon;
I am getting the error message for SELECT statement above:
ERROR: function st_within(polygon, polygon) does not exist
HINT: No function matches the given name and argument types. You might
need to add explicit type casts
I was thinking what the reason for the error is: the ST_WITHIN arguments types should be GEOMETRY, but I am passing the POLYGONs.
However the following works:
SELECT ST_WITHIN(ST_MakePoint(1,1), ST_MakePoint(1,1) ) ;

POLYGON is Postgres native type. Geometry is the type used in PostGIS. ST_... functions are Postgis functions.
Note that you can restrict a PostGIS geometry to a specific subtype (geometry(POLYGON))
If you don't want PostGIS, you would need to use native geometry operators.
If you are to use spatial data, and since you already have PostGIS, it is much better to switch to true geometries:
CREATE TABLE m_polygon (id SERIAL PRIMARY KEY, bounds geometry(POLYGON));
INSERT INTO m_polygon(bounds) VALUES(
st_geomFromText('POLYGON((0.0 0.0, 0.0 10.0, 10.0 10.0, 10.0 0.0, 0.0 0.0))')
);
SELECT ST_WITHIN(m_polygon.bounds , m_polygon.bounds ) FROM m_polygon;

Related

Insert data to a spatial database in postgis

I have create a table which has 3 fields: id(integer), name(char) and surface(polygon). When i try to insert a new row in this table by executing:
INSERT INTO public."Area"
("ID", "Name", "Surface")
VALUES(0, 'myArea', POLYGON((0 0, 1 0, 1 1, 0 1, 0 0)));
I get this error:
SQL Error [42601]: ERROR: syntax error at or near "0"
Position: 84
I searched in the documentation of postgis etc but i didnt find out how to define this field (same for points and linestring).
I also tried something like this:
INSERT INTO public."Area"
("ID", "Name", "Surface")
VALUES(1, 'myArea', ST_GeomFromText('POLYGON((8 4, 10.5 4, 10.5 1.5, 8 1.5, 8 4))'));
but the result was :
SQL Error [42804]: ERROR: column "Surface" is of type polygon but expression is of type geometry
Hint: You will need to rewrite or cast the expression.
Position: 76
Could you please help me to find out which is my mistake?
Thank you a lot for your time!
Try ST_PolygonFromText
instead of ST_GeomFromText.
Or declare the column as generic GEOMETRY, rather than POLYGON type.
The error message tells the column is of type polygon, which is a native postgres type. What one can do with this type is very limited and if you are working with spatial data, using Postgis and its geometry type is indicated.
To create a column containing geometries of the type polygon, with the projection 4326, you would do
CREATE TABLE test (my_poly geometry(polygon,4326));
You can then load data using ST_GeomFromText.
Since your column is of geometry type, there is an automatic cast from text to geometry, or you can force it. Unlike in your 1st query, note that the polygon definition must be enclosed between quotes.
insert into test values ('polygon((0 0, 0 1, 1 1, 1 0, 0 0))'::geometry)
At last, it is technically possible to create a Postgis geometry and to cast it to Postgres native polygon type (SELECT ST_GeomFromText('POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))')::polygon;), but doing so brings very little benefit since you still won't have access to most of Postgis functions (compute distances, reproject, compute intersections etc)

Sybase IQ - Float to Numeric/Decimal conversion changes the scale of the output

I have a follow up question with regards to a similar issue I'm having, as the one posted on this thread: Why when converting SQL Real to Numeric does the scale slightly increase?.
I'm using Sybase IQ 16. I have a column called - StrikePrice with float datatype in a table called ProductTb. For a certain record, the value in the table for this column is StrikePrice=22411.39.
When I try to convert it in either decimal or numeric as follows : convert(decimal(31,5), StrikePrice) OR convert(numeric(31,5), StrikePrice), this gives a result as - 22411.39189.
How do I make sure that this shows the value as - 22411.39000 instead of 22411.39189? Is there any workaround that I can use to achieve this?
Thanks in advance for your help.
Example of what I'm looking for:
When I have a value of let's say : 0.0090827, the desired output needed is : 0.00908 i.e. limited to 5 digits after the decimal. And when I have a value of : 22411.39, the desired output is : 22411.39000.
Adding more details
This(above explained issue) seems to work fine on Sybase ASE and the results are what I expect on ASE. The issue is only on Sybase IQ. See details below:
Sybase ASE (15.7):
select TradeNum, StrikePrice as "Original Strike Price in Database", convert(numeric(31, 5), StrikePrice) as "Converted Strike Price"
from ProductTb where TradeNum in (8463676,72372333)
TradeNum Original Strike Price in Database Converted Strike Price
8463676 61.65 61.65000
72372333 85.6 85.60000
72372333 85.6 85.60000
Sybase IQ (16):
select TradeNum, StrikePrice as "Original Strike Price in Database", convert(numeric(31, 5), StrikePrice) as "Converted Strike Price"
from ProductTb where TradeNum in (8463676,72372333)
TradeNum Original Strike Price in Database Converted Strike Price
72372333 85.6 85.59999
72372333 85.6 85.59999
8463676 61.65 61.64999
Looks like you have a couple SQL function options:
round()
SELECT ROUND( 123.234, 1 ) FROM iq_dummy
=> 123.200
truncnum()
SELECT TRUNCNUM( 655, -2 ) FROM iq_dummy
=> 600
SELECT TRUNCNUM( 655.348, 2 ) FROM iq_dummy
=> 655.340
I don't have access to an IQ instance at the moment but I'm thinking something like the following may work:
convert(decimal(31,5), round(StrikePrice,2))
convert(numeric(31,5), truncnum(StrikePrice,2))
UPDATE
The updated question appears to state there are 2 different rounding requirements based on the value in the StrikePrice column:
if < 1.0 then keep 5 decimal places of accuracy
if > 1.0 then keep 2 decimal places of accuracy
NOTE: I'm guessing at the threshold here (< 1.0 vs > 1.-) as this has not been explicitly stated in the question; if OP decides the threshold is something other than 1.0 this should be easy enough to address ... just change the 1.0 in the query (below) to the new threshold value
This logic can be implemented via a case statement.
I don't have access to an IQ instance but the following ASE example should be easy enough to convert to IQ:
create table testtab (a float)
go
insert testtab values (61.649999) -- > 1.0 so round to 2 decimal places
insert testtab values (0.2234234) -- < 1.0 so round to 5 decimal places
go
select convert(numeric(31,5),
case when a < 1.0
then round(a,5)
else round(a,2)
end
)
from testtab
go
--------------
61.65000 -- rounded to 2 decimal places
0.22342 -- rounded to 5 decimal places

problem in st_contains postgis vie postgres

i am new in postgis and have a problem when using st_contains in postgressql.
i have two table :
one table with multilinestring type and srid 3857 like this :
'MULTILINESTRING((5422968.66785559 3677150.09505449,5422968.65492085
3677150.0886859,5422952.17411073 3677141.94577852,5422933.98180744 3677132.88239635,5422908.89206302 3677120.3831169,5422889.41793598 3677110.66168418,5422860.41342641 3677096.31330017,5422843.29303681 3677087.88278276),(5422968.4135149 3677150.59358815,5422980.01582934 3677156.57635076,5422990.22979481 3677161.61762274,5423006.30129137 3677169.56131558))'
and another table Points type and srid 3857 like this :
'POINT(48.6977500915527 31.3106079101563)'
i use from st_buffer for first table which is multilinestring geom type and expect find any point from table 2 that is inside this buffer
when i use st_contains does not return any row , what is the problem ? although both srid are 3857
select ST_X(P.geom) lng , ST_Y(P.geom) lat from public."Zone" A
inner join public."Points" P on ST_Contains(ST_Buffer(A.geom, 100), P.geom)
From:
https://postgis.net/docs/ST_X.html
And
https://postgis.net/docs/ST_Y.html
It says that the geom inside st_X and st_Y must be a point, or NULL if not available.
Can you try just running
select A.geom as lines, P.geom as point from public."Zone" A
inner join public."Points" P on ST_Contains(ST_Buffer(A.geom, 100), P.geom);
To see if it returns anything ?
The point is not in 3857 but rather in 4326. You would need to transform it to 3857 first
select st_asText(st_transform(st_GeomFromText('POINT(48.6977500915527 31.3106079101563)',4326),3857));
st_astext
------------------------------------------
POINT(5421008.74296974 3673153.53251071)
That being said, it is more reliable to use st_dwithin rather than st_buffer.

Get geometry from a varchar(max) in an update query

I've got a table full of WKT (well known text) in a varchar(max) column. This is formatted exactly as geometry. So, for example, one of the values is:
POLYGON ((174.893529167059 -37.0260462162965,174.89351593407 -37.0260221329151,174.893508034056 -37.0260077002766,174.893444415879 -37.0258916500588,174.893416916056 -37.0258414997842,174.893481733297 -37.0258186834198,174.893492016755 -37.0258150663863,174.89349653254 -37.025823316032,174.893512415978 -37.0258522827285,174.893556883897 -37.0259333832477,174.893591032956 -37.0259956661343,174.893604265986 -37.0260197504078,174.893575149738 -37.0260300006258,174.893529167059 -37.0260462162965))
However, I need to convert this varchar(max) field into a geometry field. Unfortunately, SQL Server needs to have some other information in order to do this, so I can't just change the type of the field from varchar(max) to geometry.
I've created a blank geometry column in the same table (MyGeometry) but I'm failing in trying to convert it. Here is my code (where 2193 is the CRS I'm dealing with). WKT is my varchar(max) field and MyGeometry is my new geometry field.
UPDATE MY_TABLE
SET MyGeometry = geometry::STPolyFromText('' + WKT + '', 2193)
EDIT - currently coming back saying the WKT is not valid (System.FormatException 24111 - the input isn't valid).
Which is strange as it matches up perfectly with some other inputs I have in how it's styled.
This will do :
UPDATE MY_TABLE
SET MyGeometry = geometry::STGeomFromText('POLYGON ((174.893529167059 -37.0260462162965,174.89351593407 -37.0260221329151,174.893508034056 -37.0260077002766,174.893444415879 -37.0258916500588,174.893416916056 -37.0258414997842,174.893481733297 -37.0258186834198,174.893492016755 -37.0258150663863,174.89349653254 -37.025823316032,174.893512415978 -37.0258522827285,174.893556883897 -37.0259333832477,174.893591032956 -37.0259956661343,174.893604265986 -37.0260197504078,174.893575149738 -37.0260300006258,174.893529167059 -37.0260462162965))', 0)
You can change that 'POLYGON....' to your VARCHAR(MAX) field.
0 at the end, which is SRID only relevant if you're using Geography.
You can also try :-
UPDATE MY_TABLE SET MyGeometry = GEOMETRY::STGeomFromText('POLYGON ((174.893529167059 -37.0260462162965,174.89351593407 -37.0260221329151,174.893508034056 -37.0260077002766,174.893444415879 -37.0258916500588,174.893416916056 -37.0258414997842,174.893481733297 -37.0258186834198,174.893492016755 -37.0258150663863,174.89349653254 -37.025823316032,174.893512415978 -37.0258522827285,174.893556883897 -37.0259333832477,174.893591032956 -37.0259956661343,174.893604265986 -37.0260197504078,174.893575149738 -37.0260300006258,174.893529167059 -37.0260462162965))',4326);
The value of SRID passed should be 4326 and not 0
You can check the URL below. I found this information in another similar question (older post) so pasting it without any changes
I found solution, SQL Server Spatial Tools
http://sqlspatialtools.codeplex.com/
Followings are the methods solved my problem.
IsValidGeographyFromText(string inputWKT, int srid)
Check if an input WKT can represent a valid geography. This function requires that the WTK coordinate values are longitude/latitude values, in that order and that a valid geography SRID value is supplied. This function will not throw an exception even in edge conditions (i.e. longitude/latitude coordinates are reversed to latitude/longitude).
SqlGeography MakeValidGeographyFromText(string inputWKT, int srid)
Convert an input WKT to a valid geography instance. This function requires that the WKT coordinate values are longitude/latitude values, in that order and that a valid geography SRID value is supplied.

Calling postgis ST_DWithin in a rails query

The following is being attempted in the console for a postgis enabled rails4.2 application.
#target = Target.last
#meter_radius = 1000
#valid_points = Target.where("ST_DWithin(#{#target.lat}, #{#target.lon}, #{#meter_radius}))
lat and lon are defined as decimal values. This translates into the following query
SELECT "targets".* FROM "targets" WHERE (ST_DWithin(38.656679, 15.984094, 1000))
with the error:
PG::UndefinedFunction: ERROR: function st_dwithin(numeric, numeric, integer) does not exist
I believe I need to declare the data type (geometric or geographic) for these values but am not sure how. I am also wondering whether the ST_DWithin function can work off of the 3857 data type, even though the documentation does not state so.
note the #target object also has a lonlat attributes defined as a spatial value in postgresql with :srid=>3857, :type=>"point" defined.
Update
#valid_points = Target.where("ST_DWithin(lonlat, ST_PointFromText('#{#target.lonlat}', #{#meter_radius}))
returns a result and thus appears syntactically valid.
SELECT "targets".* FROM "targets" WHERE (ST_DWithin(lonlat, ST_PointFromText('POINT (15.984094 38.656679)', 3857), 1000))
However the result is incorrect. It essentially finds all the points of the table. Being of SRID type, this needed to be expressed in degrees, not meters.
One answer, leveraging the lonlat spatial point stored:
#target = Target.last
#degree_radius = 0.2249
#valid_points = Target.where("ST_DWithin(lonlat, ST_PointFromText('#{#target.lonlat}', #{#degree_radius}))
which returns a result and thus appears syntactically valid.
SELECT "targets".* FROM "targets" WHERE (ST_DWithin(lonlat, ST_PointFromText('POINT (15.984094 38.656679)', 3857), 0.2249))

Resources