I thought this might be fun for someone to improve on some code I just wrote.
I’m converting bearings to compass directions. It needs to be in the most efficient way possible as this code will be called millions of times as part of an already time consuming process.
The following code appears to be working and I’m pretty happy with it but perhaps someone can suggest an improvement, either in terms of performance or readability.
The case
statement is designed to hande most of the calls, each of its clauses represents the integer values that the rounded bearing can safely be assumed to be within.
The else
clause of the case statement does an actual accurate check against the floating point boundary values for the cases when the bearing did not fall within the range of safe integer values.
Suggestions?
TCompassDirection = (cdNorth, cdNNE, cdNE, cdENE, cdEast, cdESE, cdSE, cdSSE, cdSouth, cdSSW, cdSW, cdWSW, cdWest, cdWNW, cdNW, cdNNW);
function CompassDirectionOf(const inBearing : double) : TCompassDirection;
const
DEGREES_PER_DIRECTION = 360 / (Ord(High(TCompassDirection)) + 1);
ANTI_CLOCKWISE_OFFSET = DEGREES_PER_DIRECTION / 2;
BOUNDARY_MARGIN = 1;
var
cd : TCompassDirection;
begin
case Round(inBearing) of
0
..Round(((Ord(cdNorth) + 1) * DEGREES_PER_DIRECTION) - ANTI_CLOCKWISE_OFFSET) - BOUNDARY_MARGIN:
Result := cdNorth;
Round((Ord(cdNNE) * DEGREES_PER_DIRECTION) - ANTI_CLOCKWISE_OFFSET) + BOUNDARY_MARGIN
..Round(((Ord(cdNNE) + 1) * DEGREES_PER_DIRECTION) - ANTI_CLOCKWISE_OFFSET) - BOUNDARY_MARGIN:
Result := cdNNE;
Round((Ord(cdNE) * DEGREES_PER_DIRECTION) - ANTI_CLOCKWISE_OFFSET) + BOUNDARY_MARGIN
..Round(((Ord(cdNE) + 1) * DEGREES_PER_DIRECTION) - ANTI_CLOCKWISE_OFFSET) - BOUNDARY_MARGIN:
Result := cdNE;
Round((Ord(cdENE) * DEGREES_PER_DIRECTION) - ANTI_CLOCKWISE_OFFSET) + BOUNDARY_MARGIN
..Round(((Ord(cdENE) + 1) * DEGREES_PER_DIRECTION) - ANTI_CLOCKWISE_OFFSET) - BOUNDARY_MARGIN:
Result := cdENE;
Round((Ord(cdEast) * DEGREES_PER_DIRECTION) - ANTI_CLOCKWISE_OFFSET) + BOUNDARY_MARGIN
..Round(((Ord(cdEast) + 1) * DEGREES_PER_DIRECTION) - ANTI_CLOCKWISE_OFFSET) - BOUNDARY_MARGIN:
Result := cdEast;
Round((Ord(cdESE) * DEGREES_PER_DIRECTION) - ANTI_CLOCKWISE_OFFSET) + BOUNDARY_MARGIN
..Round(((Ord(cdESE) + 1) * DEGREES_PER_DIRECTION) - ANTI_CLOCKWISE_OFFSET) - BOUNDARY_MARGIN:
Result := cdESE;
Round((Ord(cdSE) * DEGREES_PER_DIRECTION) - ANTI_CLOCKWISE_OFFSET) + BOUNDARY_MARGIN
..Round(((Ord(cdSE) + 1) * DEGREES_PER_DIRECTION) - ANTI_CLOCKWISE_OFFSET) - BOUNDARY_MARGIN:
Result := cdSE;
Round((Ord(cdSSE) * DEGREES_PER_DIRECTION) - ANTI_CLOCKWISE_OFFSET) + BOUNDARY_MARGIN
..Round(((Ord(cdSSE) + 1) * DEGREES_PER_DIRECTION) - ANTI_CLOCKWISE_OFFSET) - BOUNDARY_MARGIN:
Result := cdSSE;
Round((Ord(cdSouth) * DEGREES_PER_DIRECTION) - ANTI_CLOCKWISE_OFFSET) + BOUNDARY_MARGIN
..Round(((Ord(cdSouth) + 1) * DEGREES_PER_DIRECTION) - ANTI_CLOCKWISE_OFFSET) - BOUNDARY_MARGIN:
Result := cdSouth;
Round((Ord(cdSSW) * DEGREES_PER_DIRECTION) - ANTI_CLOCKWISE_OFFSET) + BOUNDARY_MARGIN
..Round(((Ord(cdSSW) + 1) * DEGREES_PER_DIRECTION) - ANTI_CLOCKWISE_OFFSET) - BOUNDARY_MARGIN:
Result := cdSSW;
Round((Ord(cdSW) * DEGREES_PER_DIRECTION) - ANTI_CLOCKWISE_OFFSET) + BOUNDARY_MARGIN
..Round(((Ord(cdSW) + 1) * DEGREES_PER_DIRECTION) - ANTI_CLOCKWISE_OFFSET) - BOUNDARY_MARGIN:
Result := cdSW;
Round((Ord(cdWSW) * DEGREES_PER_DIRECTION) - ANTI_CLOCKWISE_OFFSET) + BOUNDARY_MARGIN
..Round(((Ord(cdWSW) + 1) * DEGREES_PER_DIRECTION) - ANTI_CLOCKWISE_OFFSET) - BOUNDARY_MARGIN:
Result := cdWSW;
Round((Ord(cdWest) * DEGREES_PER_DIRECTION) - ANTI_CLOCKWISE_OFFSET) + BOUNDARY_MARGIN
..Round(((Ord(cdWest) + 1) * DEGREES_PER_DIRECTION) - ANTI_CLOCKWISE_OFFSET) - BOUNDARY_MARGIN:
Result := cdWest;
Round((Ord(cdWNW) * DEGREES_PER_DIRECTION) - ANTI_CLOCKWISE_OFFSET) + BOUNDARY_MARGIN
..Round(((Ord(cdWNW) + 1) * DEGREES_PER_DIRECTION) - ANTI_CLOCKWISE_OFFSET) - BOUNDARY_MARGIN:
Result := cdWNW;
Round((Ord(cdNW) * DEGREES_PER_DIRECTION) - ANTI_CLOCKWISE_OFFSET) + BOUNDARY_MARGIN
..Round(((Ord(cdNW) + 1) * DEGREES_PER_DIRECTION) - ANTI_CLOCKWISE_OFFSET) - BOUNDARY_MARGIN:
Result := cdNW;
Round((Ord(cdNNW) * DEGREES_PER_DIRECTION) - ANTI_CLOCKWISE_OFFSET) + BOUNDARY_MARGIN
..Round(((Ord(cdNNW) + 1) * DEGREES_PER_DIRECTION) - ANTI_CLOCKWISE_OFFSET) - BOUNDARY_MARGIN:
Result := cdNNW;
Round((Ord(cdNorth) * DEGREES_PER_DIRECTION) - ANTI_CLOCKWISE_OFFSET) + BOUNDARY_MARGIN + 360 { negative degree values converted to almost 360 degree values }
..360:
Result := cdNorth;
else
{ bearing is between the integer boundary margins, so do
an exact check against the floating point boundaries }
Result := cdNorth; { edited from original post - Thanks Mark Griffiths }
for cd := cdNorth to cdNNW do begin
if inBearing <= (((Ord(cd) + 1) * DEGREES_PER_DIRECTION) - ANTI_CLOCKWISE_OFFSET) then begin
Result := cd;
Exit;
end;
end;
end;
end;