This is not in any way urgent or important, but I thought it might be interesting to throw it open to discussion.
I live a few hundred yards from an ex-WWII airfield now used for private flying, and I rather enjoy the view and the chatter over the RT. However, particularly late in the flying season, some pilots tend to get very sloppy with their approach pattern and overfly areas they should not. The principal offender appears to be a flying instructor, who gives every sign of not imparting the importance of proper procedures to his students.
We're in a shadow as far as ADS-B feeds to e.g. FlightRadar24 are concerned: nothing lower than roughly 1000' (300ish meters) shows up. So over the last few weeks I've been using one of my SDRs with dump1090-mutability (spitting out 100s of Mb per day), and writing Pascal code to filter the output: ignore everything above a certain height, ignore everything outside a defined polygon, generate a ticket file in .gpx format for what's left. There's a lot of subtlety has to go into that, for example many planes have an uncalibrated altimeter feed to their transponder.
I found this paper
http://web.archive.org/web/20080812141848/http://local.wasp.uwa.edu.au/~pbourke/geometry/insidepoly/ for polygon detection, and have written this code where the input is lat,long expressed as degrees with decimal fraction:
type
vertex= record
lat, long: double
end;
var
vertices: array of vertex;
(* Working from the current aircraft location, find the angle subtended by each
edge of the polygon by looking at successive pairs of vertices. Assume that
the current location is inside the polygon if the overall angle is 360 degrees.
*)
function inPoly(here: vertex): boolean;
var
i: integer;
deg1, deg2, subtends: double;
total: double= 0.0;
begin
(* Relative to the fixed point, determine the angle subtended by each edge of *)
(* the polygon. Save this in degrees for relatively easy debugging. *)
for i := 0 to Length(vertices) - 2 do begin
deg1 := ArcTan2(vertices[i].lat - here.lat, vertices[i].long - here.long) * (180 / Pi);
deg2 := ArcTan2(vertices[i + 1].lat - here.lat, vertices[i + 1].long - here.long) * (180 / Pi);
subtends := Abs(deg1 - deg2);
if subtends > 180.0 then
subtends := 360.0 - subtends;
total += subtends;
// WriteLn(deg1:12:5, deg2:12:5, subtends:12:5);
end;
// WriteLn(total:12:5);
(* If the total angle is 360 degrees, then we must be inside the polygon. Any *)
(* smaller angle is outside, but I don't know quite how much slop I should *)
(* allow... testing suggests that the transition is abrupt even without taking *)
(* the sign of the angles into account. *)
result := (total >= 359.99) and (total < 360.01)
// WriteLn('Score: ', total:12:5)
end { inPoly } ;
It actually works surprisingly well: no points result in an angle substantially more than 360 degrees, and there's an extremely rapid falloff below that point.
However, I'm left with a nagging feeling it could be improved by retaining the sign of the angles: which would result in a total angle of 360 degrees if the point were inside the polygon, and zero outside.
The problem is that to get that working, it is obvious that the points that make up the boundary of the polygon would have to be sorted such that progressing round it in a clockwise direction would always result in a +ve angle (or vice-versa, the important thing being consistency).
That is simple enough if the polygon is non-reentrant, at which point the ordering relative to any point inside the boundary would be the same as the ordering relative to the centroid.
However, I have a very unpleasant feeling that for a reentrant polygon this becomes the "Illumination Problem" described by Penrose et al. (
https://en.wikipedia.org/wiki/Illumination_problem) and that even if the approach worked it would require sorting the points for every run: possibly relying on a-priori knowledge of whether the test point was inside the polygon.
Are my misgivings well-founded, or is there an easy solution which will allow this algorithm to benefit from retaining the sign?
MarkMLl