I have noticed that sometimes the results from ST_Distance for geometry types do not correspond correctly to those for geography types.
For example:
SELECT ST_Distance('SRID=4326;MULTIPOLYGON(((13.1654379639367 48.0163296656575,
13.1654405823308 48.0163326202901,13.1654809135407 48.0163781648167,
13.1655095556032 48.0164104945946,13.1656825124596 48.0166031792699,
13.1658285825017 48.0167559797112,13.1658385904811 48.0167667179682,
13.1660097634653 48.0169315381006,13.1661737540995 48.0170911295992,
13.166336100685 48.0172329598378,13.1677079127931 48.0150783894135,
13.1677278111466 48.0150450062427,13.1670716137939 48.0148839705059,
13.1667911667995 48.0148062288149,13.1665512255895 48.0147411405409,
13.1665145733757 48.0147311909654,13.1654379639367 48.0163296656575)))'::geometry,
'SRID=4326;POINT(16.096346 47.2786129)'::geometry);
returns 3.0197908442784636 as a result in degrees.
But, when computing the distance of the same shapes in metres:
SELECT ST_Distance(gg1,gg2) from (select 'SRID=4326;MULTIPOLYGON(((13.1654379639367 48.0163296656575,
13.1654405823308 48.0163326202901,13.1654809135407 48.0163781648167,13.1655095556032 48.0164104945946,
13.1656825124596 48.0166031792699,13.1658285825017 48.0167559797112,13.1658385904811 48.0167667179682,
13.1660097634653 48.0169315381006,13.1661737540995 48.0170911295992,13.166336100685 48.0172329598378,
13.1677079127931 48.0150783894135,13.1677278111466 48.0150450062427,13.1670716137939 48.0148839705059,
13.1667911667995 48.0148062288149,13.1665512255895 48.0147411405409,13.1665145733757 48.0147311909654,
13.1654379639367 48.0163296656575)))'::geography as gg1,
'SRID=4326;POINT(16.096346 47.2786129)'::geography as gg2) as foo;
it returns 0 metres. This can't be right. By looking the shapes in wkt playground the distance is indeed much more than 0 metres.
Any idea of what I could be doing wrong?
Thank you!
Related
I'm calculating the shortest line between a Line and a Point for very short distances (some meters), using Postgis ST_ShortestLine:
SELECT ST_AsText(
ST_ShortestLine(ST_GeomFromText('POINT(2.33123610021 48.87902639841)', 4326),
ST_GeomFromText('LINESTRING ( 2.33122725689 48.87902421718, 2.33123229444 48.87901190847)', 4326))
) As sline;
I get a result which does not seem coherent, the given line not being the shortest one:
LINESTRING(2.33123610021 48.87902639841,2.331227760998549 48.87902298544515)
Here is a drawing of the result, using the Mercator projection (JOSM).
What could explain this?
If you're relying on your eyes to determine if the drawn line is the shortest one you might have been mislead to this conclusion. ST_ShortestLine will return a line with exact same length of ST_Distance, which is the minimum 2D cartesian distance of two geometries. And it is exactly what is happening:
WITH j (line,point) AS (
VALUES ('SRID=4326;POINT(2.33123610021 48.87902639841)',
'SRID=4326;LINESTRING(2.33122725689 48.87902421718, 2.33123229444 48.87901190847)')
)
SELECT
ST_Length(ST_ShortestLine(point,line)), -- length of the shortest line
ST_Distance(line,point), -- distance between 'point' and 'line'
ST_AsEWKT(ST_ShortestLine(point,line)) -- the shortest line as EWKT
FROM j;
st_length | st_distance | st_asewkt
-----------------------+-----------------------+----------------------------------------------------------------------------------------
9.010592472335791e-06 | 9.010592472335791e-06 | SRID=4326;LINESTRING(2.331227760998549 48.87902298544515,2.33123610021 48.87902639841)
(1 row)
Perhaps you share the result you're expecting and we can go from there.
I have the following linestring:
SELECT ST_GeomFromText('LINESTRING(-97.83396022 29.98609860,-97.83391790 29.98613790)',4326);
I need to add a point between the linestring, which is 2 feet from the first point
You can use ST_LineInterpolatePoint.
Since it takes a proportion of the line, the first step is to compute it: convert 2ft to meters, divide by the length of the line in meters, that you get by casting it to geography.
WITH src(geom) AS (values (ST_GeomFromText('LINESTRING(-97.83396022 29.98609860,-97.83391790 29.98613790)',4326)))
SELECT ST_AsText(ST_LineInterpolatePoint(geom, 2 * 0.3048/ st_length(geom::geography)))
FROM src;
st_astext
-------------------------------------------
POINT(-97.8339558996568 29.9861026120389)
To create a line from the start that is 2ft long, you can use st_lineSubstring instead
SELECT st_lineSubstring(geom, 0, 2 * 0.3048/ st_length(geom::geography))
I want to find the points inside the intersection (Figure 1) of polygons in PostgreSQL.
Figure 1 example
I use psycopg2 and the code that I used is:
intersects = """select ST_Intersects( ST_GeographyFromText('SRID=4326; POLYGON(( 32.0361328 33.6877818, 31.9042969 33.5780147,33.5742188 11.3507967,66.2695313 20.4270128, 51.9433594 34.270836, 32.0361328 33.6877818))'),
ST_GeographyFromText('SRID=4326; POLYGON((33.7060547 37.1953306,36.6943359 16.0880422,64.9072266 12.4258478,64.8632813 37.0551771,33.5742188 37.1953306,33.7060547 37.1953306))')), col.vessel_hash,ST_X(col.the_geom) AS long, ST_Y(col.the_geom) AS lat
from samplecol as col"""
cursor.execute(intersects)
pointsINtw = cursor.fetchall()
count = 0;
shipsrecords = open("/home/antonis/Desktop/testme1.txt", "w")
for ex in pointsINtw:
if str(ex[0])=='True':
count = count + 1
shipsrecords.write(str(ex) + "\n")
print (CBLUE + "Number of returned results: " + CBLUEEND), count
Example record:
vessel_hash | speed | latitude | longitude | course | heading | timestamp | the_geom
--------------+--------+---------+-------+-------------+-------------+--------+---------+--------------------------+----------------------------------------------------
103079215239 | 5 | -5.41844510 | 36.12160900 | 314 | 511 | 2016-06-12T06:31:04.000Z | 0101000020E61000001BF33AE2900F424090AF4EDF7CAC15C0
The problem is that above code does not work properly. I create two polygons like Figure 1 and I know that inside the intersection exist some points. But the code always returns all points from db.
If I create two polygons that do not intersect then the algorithm seems to work properly as it does not return any point.
Does anyone know what am I doing wrong?
demo:db<>fiddle (of your query, with your polygons, own points),
visualisation of the situation (maybe Chrome necessary)
ST_Intersects() only checks if the two given polygons share some space. It is true, they share. But there is no part within your query that includes the check with the points. You only call the intersection check but without any usage of your point column.
I believe you need to calculate the intersection polygon (ST_Intersection()) instead of only check for its existance. After that you can take this result to check whether your points are in it or not (ST_Contains()):
Pseudocode:
SELECT
ST_Contains(
ST_Intersection(my_geometry1, my_geometry2),
my_pointgeometry
)
...
demo:db<>fiddle
(Demo uses geometry instead of geography and the polygon needs to get valid for some reasons; so you need to adapt this to your use case)
what is the easy way to convert points to polygon?
i've tried this query
SELECT ST_GeomFromText('POLYGON((157 -536.0,157 -537.0,157 -538.0,157 -539.0,157 -540.0,157 -541.0,157 -542.0,157 -543.0,157 -544.0,157 -545.0,158 -545.0,159 -545.0,160 -545.0,161 -545.0,162 -545.0,163 -545.0,164 -545.0,165 -545.0,165 -544.0,165 -543.0,165 -542.0,165 -541.0,165 -540.0,165 -539.0,165 -538.0,165 -537.0,165 -536.0,164 -536.0,163 -536.0,162 -536.0,161 -536.0,160 -536.0,159 -536.0,158 -536.0,157.0 -536.0))');
but its results are not as expected as shown below
which is supposed to be like this
Obviously your points are not in the correct order to define a polygon., and as the commenter pointed out, you have more than one polygons.
You could divide them into sets that make each polygon (manually?), and construct a multipolygon as follows:
SELECT ST_AsText(ST_Collect(ARRAY[ST_GeomFromText('POLYGON(..first polygon...)'),ST_GeomFromText('POLYGON(..2nd polygon...)',...,ST_GeomFromText('POLYGON(..last polygon...)')]));
The problem we have is reduced to the following two statements:
select convert(float, (convert(float,5741.61)/convert(float, 196.00)) * convert(float,14.00)) as unrounded, round(convert(float, (convert(float,5741.61)/convert(float, 196.00)) * convert(float,14.00)), 2) as roundedTo2dp
select convert(float, 410.115) as unrounded, ROUND( convert(float, 410.115), 2) as roundedTo2dp
The first statement uses floats to calculate a value of 410.115, and also that result with a round() to 2 decimal places. The rounded value comes out at 410.11.
The second statement uses the float value 410.115 and also rounds it to 2 decimal places. The rounded result comes out as 410.12.
Why is one rounding down and the other rounding up when the value being rounded it the same?
How can I get the first statement to round to 410.12?
EDIT: apologies for formatting -- stackoverflow isn't showing any formatting on this machine (very odd).
Decimals are better with precision than floats. If you changed up the float to be something like DECIMAL(18,2), you'll get what you are expecting and you don't need to call the round function anymore.
select convert(decimal(18,2), (convert(decimal(18,2),5741.61)/convert(decimal(18,2), 196.00)) * convert(decimal(18,2),14.00)) as unrounded, round(convert(decimal(18,2), (convert(decimal(18,2),5741.61)/convert(decimal(18,2), 196.00)) * convert(decimal(18,2),14.00)), 2) as roundedTo2dp
results in
unrounded roundedTo2dp
410.12 410.12
Link to the MSDN about decimals. http://msdn.microsoft.com/en-us/library/ms187746.aspx
Hope that helps...
The numbers are not equal:
SELECT CAST(convert(float, (convert(float,5741.61)/convert(float, 196.00)) * convert(float,14.00)) AS BINARY(8))
UNION ALL
SELECT CAST(convert(float, 410.115) AS BINARY(8)) as bin
----
0x4079A1D70A3D70A3
0x4079A1D70A3D70A4
'float' is an approximate number data type and hence not all values in the data type range can be represented exactly.
This is based on http://msdn.microsoft.com/en-us/library/ms173773.aspx.
I believe this is why there is rounding issue while using float values. You can never be 100% sure!
Ex.
Select round(convert(float, 1.5555), 2) --Gives 1.56
Select round(convert(float, 1.555), 2) --Gives 1.55!
With such a simple number there is difference in expected result while using float.