postgis: point returned in ST_LineLocatePoint not able to detect in ST_Contains - postgresql

I am using postgis's ST_LineLocatePoint to find out the closest point on a LineString to the given Point, and using ST_LineInterpolatePoint to extract a Point from the returned float number.
ST_LineLocatePoint Query:
SELECT ST_AsText(ST_LineInterpolatePoint(foo.the_line,
ST_LineLocatePoint(foo.the_line,
ST_GeomFromText('POINT(12.962315 77.584841)')))) AS g
FROM (
SELECT ST_GeomFromText('LINESTRING(12.96145 77.58408,12.96219 77.58447,12.96302 77.58489,
12.96316 77.58496,12.96348 77.58511)') AS the_line
) AS foo;
Output:
g
------------------------------------------
POINT(12.9624389808159 77.5845959902924)
Which exactly lies on the linestring I have passed. Demonstration is displayed here.
But when I check whether this point lies in the linestring using ST_Contains it always return false, even though the point lies within.
ST_Contains Query:
SELECT ST_Contains(ST_GeomFromText('LINESTRING(12.96145 77.58408,12.96219 77.58447,
12.96302 77.58489, 12.96316 77.58496, 12.96348 77.58511)'),
ST_GeomFromText('POINT(12.9624389808159 77.5845959902924)'));
Output
st_contains
-------------
f
I am not getting where I am doing wrong. Can anyone help me in this.
Postgresql : 9.4
postgis : 2.1
reference: ST_LineLocatePoint, ST_Contains

I am not getting where I am doing wrong.
I think you're doing good... I had the same issue some time ago... I used ST_ClosestPoint to locate point on linestring and then cut a linestring with this point, but I can't.
Following the documentation:
ST_ClosestPoint — Returns the 2-dimensional point on g1 that is
closest to g2. This is the first point of the shortest line.
So I get situation where one function says - this point is on a line, and other functions says - ok, but I can't cut cause your point is not on a line... I was confused like you're now...
In my case resolution was to draw another line which will intersect first line 'exactly' in given point and after that first line was cutted...
After some research I found issue was about rounding of coordinates counted and writen. I explain it to myself that, according to the definitions line is infinitely thin and point is infinitely small (they do not have the area), so they can easily miss each other - but it's my reasoning and I'm not sure whether it is good. I advice you to use st_intersects, but with very low st_buffer or ST_DWithin function also with very low distance.
To be sure that your point lies on a line it have to be a part of this line (e.g. LINESTRING(0 0, 5 5) points (0 0) and (5 5). Example with point(3 3) works because it's coordinates are counted without any roundings.

This is actually a really common question (most likely a duplicate, but I'm too lazy to find it.)
The issue is related to numerical precision, where the Point is not exactly on the LineString, but is within a very small distance of it. Sort of like how SELECT sin(pi()) is not exactly zero.
Rather than using DE-9IM spatial predicates (like Contains, or Covers, etc.) which normally expect exact noding, it is more robust to use distance-based techniques like ST_DWithin with a small distance threshold. For example:
SELECT ST_Distance(the_point, the_line),
ST_Covers(the_point, the_line),
ST_DWithin(the_point, the_line, 1e-10)
FROM (
SELECT 'POINT(12.9624389808159 77.5845959902924)'::geometry AS the_point,
'LINESTRING(12.96145 77.58408,12.96219 77.58447,12.96302 77.58489,12.96316 77.58496,12.96348 77.58511)'::geometry AS the_line
) AS foo;
-[ RECORD 1 ]----------------------
st_distance | 1.58882185807825e-014
st_covers | f
st_dwithin | t
Here you can see that ST_DWithin indicates that the point is within a very small distance of the line, so it effectively contains the point.

ST_Contains() only returns true if the geometry to test lies within the supplied geometry. In your case the point has to lie within the linestring and this is always false since a linestring does not have an interior.
You should use the ST_Covers() function instead: true if no point of the geometry to test (your point) lies outside the supplied geometry (your linestring).

Related

Postgres: How to calculate distance for a set of geography points?

I'm using Postgres v13.
I couldn't find a clear example of how to achieve this basic calculation. I'm totally confused about how to handle geometry and geography points.
I have a table that stores points in the format geography(Point, 4326) alongside their timestamp.
I need to obtain the total distance in meters between timestamps A and B, and I need it to be super specific using spherical calculations. To be clear, there may be N points.
So far I've been using this query, but the distance is way off for long distances and I don't understand if there is any difference in creating a line using geometry points or geography points:
SELECT ST_Length(ST_MakeLine(lh.position::geometry order by report_time), TRUE)
FROM location_history AS lh
WHERE lh.device_id = 1
AND lh.report_time BETWEEN '2022-10-10T13:25:00.000Z' AND '2022-10-11T13:25:00.000Z'
GROUP BY lh.device_id;
Does this query make sense? ST_MakeLine only accepts geometry points and confuses me. Is there another way of creating a line with geography points?
ST_Distance is used in every example I could find, but it just compares 2 points!
Thanks!

rgeo point near MULTILINESTRING

Having a geometry column wkb_geometry, srid 4326 that is a MULTILINESTRING I would like to determine which of these records are within a predetermined distance (say 5000m) of a geometry object POINT
while the following method allows to determine if a polygon contains a point
def self.containing_latlon(lat,lon, polygon)
ewkb = EWKB.generate(RGeo::Geographic.simple_mercator_factory.point(lon, lat).projection)
where("ST_Intersects(polygon, ST_GeomFromEWKB(E'\\\\x#{ewkb}'))")
end
ST_Intersects is clearly not an option, as it applies to "any portion of space then they intersect".
I have not found documentation in order to determine if a line is within X distance of a point. But possibly the question is reversed? Should the question not be is the point within a polygon defined by the MULTILINESTRING and a buffer.
How would the above method need to be modified in order to execute this?
Use ST_DWithin instead.
For distances using meters cast the parameters to geography, e.g. 5km:
SELECT * FROM t
WHERE ST_DWithin('POINT(7.00 51.82)'::geography,geom::geography,5000);
If you're happy with the unit of measurement of your SRS, just stick to `geometry
SELECT * FROM t
WHERE ST_DWithin('POINT(7.00 51.82)'::geometry,geom,42);
The :: after the WKT literals is a postgres syntax to cast data types. But as it is customary in postgres, there are many ways to do the same thing. The following example casts a WKT literal into a geometry using different techniques:
SELECT
CAST('SRID=4326;POINT(1 2)' AS geometry),
'SRID=4326;POINT(1 2)'::geometry,
ST_GeomFromText('SRID=4326;POINT(1 2)'),
ST_SetSRID(ST_MakePoint(1,2),4326);
-[ RECORD 1 ]---+---------------------------------------------------
geometry | 0101000020E6100000000000000000F03F0000000000000040
geometry | 0101000020E6100000000000000000F03F0000000000000040
st_geomfromtext | 0101000020E6100000000000000000F03F0000000000000040
st_setsrid | 0101000020E6100000000000000000F03F0000000000000040
Further reading: Getting all Buildings in range of 5 miles from specified coordinates

Issues with POSTGIS ST_DISTANCE function and results

Hi I have a bit of issues with postgis and calculations.
Using ST_DISTANCE causes a bit of confusion:
SELECT st_distance(
ST_SetSRID(ST_MakePoint(16.0420,45.8250), 4326),
ST_SetSRID(ST_MakePoint(16.1675,45.8344), 4326));
returns 0.12585153952177025 as an result.
The result seems a bit odd as on a simple visible checking those points are quite far away and result
should be in meters.
For instance :
select st_distance(
'POINT(15.651955 73.712769 )'::geography,
'POINT(14.806993 74.131451 )'::geography) AS d;
returns 53536.74349675 which seems to work properly.
Can you tell me what I'm doing wrong?
st_distance returns a distance in the unit of the used coordinate system. For 4326, the unit is degree, so the distance is in degrees (which is meaningless).
As you have done, you can cast to geography first, which returns a result in meters. You can also use ST_DistanceSpheroid to get a distance in meters, or you can use a suitable coordinate system whose unit is in meters.

Most efficient way to find points within a certain radius from a given point

I've read several questions + answers here on SO about this theme, but I can't understand which is the common way (if there is one...) to find all the points whithin a "circle" having a certain radius, centered on a given point.
In particular I found two ways that seem the most convincing:
select id, point
from my_table
where st_Distance(point, st_PointFromText('POINT(-116.768347 33.911404)', 4326)) < 10000;
and:
select id, point
from my_table
where st_Within(point, st_Buffer(st_PointFromText('POINT(-116.768347 33.911404)', 4326), 10000));
Which is the most efficient way to query my database? Is there some other option to consider?
Creating a buffer to find the points is a definite no-no because of (1) the overhead of creating the geometry that represents the buffer, and (2) the point-in-polygon calculation is much less efficient than a simple distance calculation.
You are obviously working with (longitude, latitude) data so you should convert that to an appropriate Cartesian coordinate system which has the same unit of measure as your distance of 10,000. If that distance is in meter, then you could also cast the point from the table to geography and calculate directly on the (long, lat) coordinates. Since you only want to identify the points that are within the specified distance, you could use the ST_DWithin() function with calculation on the sphere for added speed (don't do this when at very high latitudes or with very long distances):
SELECT id, point
FROM my_table
WHERE ST_DWithin(point::geography,
ST_GeogFromText('POINT(-116.768347 33.911404)'),
10000, false);
I have used following query
SELECT *, ACOS(SIN(latitude) * SIN(Lat)) + COS(latitude) * COS(Lat) * COS(longitude) - (Long)) ) * 6380 AS distance FROM Table_tab WHERE ACOS( SIN(latitude) * SIN(Lat) + COS(latitude) * COS(Lat) * COS(longitude) - Long )) * 6380 < 10
In above query latitude and longitude are from database and lat, long are the points from we want to search.
WORKING : it will calculate the distance(In KM) between all the points in database from search points and check if the distance is less then 10 KM. It will return all the co-ordinates within 10 KM.
I do not know how postgis does it best, but in general:
Depending on your data it might be best to first search in a square bounding box (which contains the search area circle) in order to eliminate a lot of candidates, this should be extremely fast as you can use simple range operators on lon/lat which are ideally indexed properly for this.
In a second step search using the radius.
Also if your limit max points is relatively low and you know you have a lot of candidates, you may simply do a first 'optimistic' attempt with a box inside your circle, if you find enough points you are done !

separation() returns huge angle

I am writing a program which goes through FITS files with photometry and looks for stars given in a .dat file.
One of the steps is computing distances between two given stars using ephem.separation()
It works well. However, from time to time separation returns angles like 1389660529:33:00.8
import ephem
import math
star = ['21:45:15.00', '65:49:24.0']
first_coo = ['21:45:15.00', '65:49:24.0']
check = ephem.FixedBody()
check._ra = ephem.hours(star[0])
check._dec = ephem.degrees(star[1])
check.compute()
# star is a list with coordinates, strings in form %s:%s:%s
first = ephem.FixedBody()
first._ra = ephem.hours(first_coo[0])
first._dec = ephem.degrees(first_coo[1])
first.compute()
sep = math.degrees(float(ephem.separation(check,first)))
print sep
It occurs randomly. Have anybody encountered such behaviour?
I search for 18 stars in 212 files, which makes 3816 cycles. Might have something to do with it?
UPDATE: I have released a new PyEphem 3.7.5.2 that fixes this special case of comparing an angle to itself.
Your code sample has two interesting features:
first, it contains a slight bug that I thought at first might be behind the problem;
and, second, I was wrong that your code was the problem because your code
does indeed expose a flaw in the separation() function
when it is asked how far a position is from itself!
The bug in your own code is that calling compute() and asking about .ra and .dec
returns those coordinates in the coordinate system of the very moment that you are
calling compute() for — so your two compute() calls are returning coordinates
in two different coordinate systems that are very slightly different,
and so the resulting positions cannot be meaningfully compared with separation()
because separation() requires two coordinates that are in the same coordinate system.
To fix this problem, chose a single now moment to use as your equinox epoch:
now = ephem.now()
...
check.compute(epoch=now)
...
first.compute(epoch=now)
That will give you coordinates that can be meaningfully compared.
Now, on to the problem in PyEphem itself!
When presented with two copies of the same position are provided to separation() it goes ahead and tries to find a distance between them anyway, and winds up doing a calculation that amounts to:
acos(sin(angle) ** 2.0 + cos(angle) ** 2.0)
which should remind us of the standard Euclidean distance formula but with an acos() around it instead of a sqrt(). The problem is that while in theory the value inside of the parens should always be 1.0, the rounding inside of IEEE floating point math does not always produce squares that sum to exactly 1.0. Instead, it sometimes produces a value that is a bit high or a bit low.
When the result is a bit below 1.0, separation() will return a very slight separation for the coordinates, even though they are actually “the same coordinate.”
When the result exceeds 1.0 by a little bit, separation() will return not-a-number (nan) on my system — and, I will bet, returns that huge return value that you are seeing printed out on yours — because cos() cannot, by definition, return a number greater than 1.0, so there is no answer acos() can return when asked “what angle returns this value that is greater than one?”
I have created a bug report in GitHub and will fix this for the next version of PyEphem:
https://github.com/brandon-rhodes/pyephem/issues/31
Meanwhile, your code will have to avoid calling separation() for two positions that are actually the same position — could you use an if statement with two == comparisons between ra and dec to detect such cases and just use the value 0.0 for the separation instead?