Reputation: 133
I'm trying to solve a Line of Sight (LoS) problem using SQL in PostgreSQL and PostGIS. To do this I have a table pitch_at_point
that contains an id, a point geometry and a pitch.
pitch_at_point(id integer,geom geometry,degrees float)
The id column's lowest value is the starting point for the LoS and the highest is furthest away. For each of these points I want to determine if there are any points with a lower id, which also has a higher pitch (degrees). If this is the case, then the point can't be seen.
I'm stuck trying to find a solution. I have tried using the recursive query as in the SQL below:
WITH RECURSIVE
walk_points AS
(
SELECT ARRAY[id] AS idlist,geom,degrees,id
FROM pitch_at_point
WHERE degrees = (SELECT max(degrees) FROM pitch_at_point)
UNION ALL
SELECT array_append(w.idlist, n.id) AS idlist,n.geom,n.degrees,n.id
FROM pitch_at_point n, walk_points w
WHERE n.degrees < any(SELECT n.degrees FROM pitch_at_point WHERE NOT
w.idlist @> ARRAY[n.id])
)
SELECT * FROM walk_points
I expected to get a return of all points that is preceded by points that have a higher pitch, but I only get a result of a single point, and always the same point even if I use WHERE n.degrees > any(
.
I have a hard time figuring out the PostgreSQL recursive CTE so if anybody can help me on my way I would appreciate it.
Upvotes: 3
Views: 122
Reputation: 1673
This sounds like it could be solved with a window function.
This will give you a true/false flag for records where a previous degrees field (ordered by id) is greater than the degrees for the current row:
case when degrees < max( degrees ) over(
order by id
rows between unbounded preceding and 1 preceding
)
then true
else false
end as higher_value_present
See SQLfiddle here: http://sqlfiddle.com/#!15/23196/1
From your question, I wasn't clear on whether there was a set of ids for each geom point that you want to apply this logic to? If this is the case, you can partition the window function by geom:
case when degrees < max( degrees ) over(
partition by geom
order by id
rows between unbounded preceding and 1 preceding
)
then true
else false
end as higher_value_present
Upvotes: 2
Reputation: 94859
I may be wrong, but isn't this simply:
select
id, geom, degrees
from pitch_at_point
where exists
(
select *
from pitch_at_point before
where before.id < pitch_at_point.id
and before.degree > pitch_at_point.degree
);
This gets all points where a record with a lower ID has a higher pitch.
The same thing may be faster with the windowing version of MAX:
select id, geom, degrees
from
(
select
id, geom, degrees,
max(degreees) over (order by id rows between unbounded preceding and 1 preceding)
as max_degrees_before
from pitch_at_point
) data
where degrees < max_degrees_before;
Upvotes: 2