const char * UsageLines [] = {
       "p6addgeograticule: This is a variant of 'p6addgraticule'.",
       "",
       "Reads P6 PPM from standard input, writes to standard output.",
       "Adds lines at 5 second intervals.",
       "",
       "Takes 4 arguments, all integers:",
       "    latitude of bottom",
       "    longitude of left",
       "    longitude of right",
       "    latitude of top",
       "",
       "All are in degreesx100000, + for N and E, - for S and W.",
       "top must be > bottom, and right must be > left",
       "",
       "April 3, 2022.  For latest, see:",
       "    gopher://sdf.org/1/users/julianbr",
       };
const int NumUsageLines = sizeof (UsageLines) / sizeof (UsageLines [0] );

#define NUM_ROWS 9
#define MIDDLE_ROW 4
#define NUM_COLUMNS 5
#define SPACING 1
#define MARGIN 30
#define NUM_OUTLINES_ACROSS 11
#define NUM_OUTLINES_UP 10
#define HORIZONTAL_R 255
#define HORIZONTAL_G 0
#define HORIZONTAL_B 0
#define VERTICAL_R 0
#define VERTICAL_G 0
#define VERTICAL_B 255
#define OFFSET 20000000

int (DigitOutlines [] ) [NUM_ROWS] = {
       {14, 17, 17, 17, 17, 17, 17, 17, 14 },
       {12, 4, 4, 4, 4, 4, 4, 4, 14 },
       {14, 17, 1, 1, 1, 1, 14, 16, 31 },
       {14, 17, 1, 1, 14, 1, 1, 17, 14 },
       {6, 10, 10, 18, 31, 2, 2, 2, 2 },
       {31, 16, 16, 16, 30, 1, 1, 17, 14 },
       {14, 17, 16, 16, 30, 17, 17, 17, 14 },
       {15, 17, 1, 2, 2, 2, 4, 4, 4 },
       {14, 17, 17, 17, 14, 17, 17, 17, 14 },
       {14, 17, 17, 17, 15, 1, 1, 1, 2 },
       };
int (NorthOutline) [NUM_ROWS] = {25, 9, 21, 21, 21, 21, 21, 18, 19 };
int (SouthOutline) [NUM_ROWS] = {14, 17, 16, 16, 14, 1, 1, 17, 14 };
int (EastOutline) [NUM_ROWS] = {31, 16, 16, 16, 30, 16, 16, 16, 31 };
int (WestOutline) [NUM_ROWS] = {17, 17, 17, 21, 21, 21, 10, 10, 10 };
int (DegreeOutline) [NUM_ROWS] = {12, 18, 18, 12, 0, 0, 0, 0, 0 };
int (MinuteOutline) [NUM_ROWS] = {4, 4, 4, 0, 0, 0, 0, 0, 0 };
int (SecondOutline) [NUM_ROWS] = {20, 20, 20, 0, 0, 0, 0, 0, 0 };

#include <stdio.h>
#include <math.h>

int CmPerGmEastWest (int LatitudeGm)
       {
       float Latitude;
       int CmPerGm;

       Latitude = LatitudeGm/5729578.;
       CmPerGm = cos (Latitude)*111.;
       return CmPerGm;
       }

void AddGeoGraticule (
               int bottom, int left, int right, int top)
       {
       int * Outline;
       int i, j, r, g, b, width, height, maxval;
       int start, finish, across, up;
       int DistanceAcross, DistanceUp;
       int HorizontalClearance, VerticalClearance;
       int MinLeft, MaxLeft, MinBottom, MaxBottom;
       int MinRight, MaxRight, MinTop, MaxTop;
       int OutlineNum, Label, LabelMagnitude, Row, Column;
       int PrintHorizontalLine, PrintVerticalLine, ProtectFromLine, AlreadyPrinted;
       int LeftLabel, RightLabel, TopLabel, BottomLabel;

       if (
                       getchar () != 'P'
                       || getchar () != '6'
                       || getchar () != '\n'
                       || scanf ("%d %d", & width, & height) != 2
                       || getchar () != '\n'
                       || scanf ("%d", & maxval) != 1
                       || maxval != 255
                       || getchar () != '\n'
                       ) {
               fprintf (stderr, "***p6addgeograticule: Improper input file, must be");
               fprintf (stderr, " P6 image maxval 255\n");
               return;
               }

       DistanceAcross = right - left;
       DistanceUp = top - bottom;
       VerticalClearance = NUM_OUTLINES_ACROSS*(NUM_COLUMNS + SPACING);
       HorizontalClearance = NUM_OUTLINES_UP*(NUM_COLUMNS + SPACING);
       j = MARGIN + MIDDLE_ROW - 1;
       MinBottom = (left + j*DistanceAcross/width + OFFSET)*9/1250;
       j = width - MARGIN - HorizontalClearance - NUM_ROWS + MIDDLE_ROW;
       MaxBottom = (left + j*DistanceAcross/width + OFFSET)*9/1250;
       i = MARGIN + VerticalClearance + NUM_ROWS - MIDDLE_ROW - 1;
       MinLeft = (bottom + i*DistanceUp/height + OFFSET)*9/1250;
       i = height - MARGIN - MIDDLE_ROW;
       MaxLeft = (bottom + i*DistanceUp/height + OFFSET)*9/1250;
       i = MARGIN + NUM_ROWS - MIDDLE_ROW - 1;
       MinRight = (bottom + i*DistanceUp/height + OFFSET)*9/1250;
       i = height - VerticalClearance - MARGIN - MIDDLE_ROW - 1;
       MaxRight = (bottom + i*DistanceUp/height + OFFSET)*9/1250;
       j = MARGIN + HorizontalClearance + MIDDLE_ROW - 1;
       MinTop = (left + j*DistanceAcross/width + OFFSET)*9/1250;
       j = width - MARGIN - NUM_ROWS + MIDDLE_ROW;
       MaxTop = (left + j*DistanceAcross/width + OFFSET)*9/1250;

       printf ("P6\n%d %d\n%d\n", width, height, maxval);
       r = getchar ();
       for (i = height - 1; i >= 0; i--) {
               for (j = 0; j < width; j++) {
                       if (r == EOF)
                               g = EOF;
                       else
                               g = getchar ();
                       if (g == EOF)
                               b = EOF;
                       else
                               b = getchar ();

                       ProtectFromLine = 0;
                       AlreadyPrinted = 0;

                       /* Label for Horizontal Line */
                       OutlineNum = -1;
                       LeftLabel = 0;
                       RightLabel = 0;
                       if (j >= MARGIN - SPACING && j < MARGIN + HorizontalClearance) {
                               if (j >= MARGIN) {
                                       OutlineNum = (j - MARGIN)/(NUM_COLUMNS + SPACING);
                                       Column = (j - MARGIN)%(NUM_COLUMNS + SPACING);
                                       }
                               LeftLabel = 1;
                               }
                       else if (j >= width - MARGIN - HorizontalClearance
                                               && j < width - MARGIN + SPACING) {
                               if (j >= width - MARGIN - HorizontalClearance + SPACING) {
                                       OutlineNum = (j - width + MARGIN + HorizontalClearance - SPACING)
                                                               /(NUM_COLUMNS + SPACING);
                                       Column = (j - width + MARGIN + HorizontalClearance - SPACING)
                                                               %(NUM_COLUMNS + SPACING);
                                       }
                               RightLabel = 1;
                               }
                       if (LeftLabel || RightLabel) {
                               Row = - SPACING - 1;
                               up = i + MIDDLE_ROW - NUM_ROWS + Row + 1;
                               start = (bottom + up*DistanceUp/height + OFFSET)*9/1250;
                               Row++;
                               up = i + MIDDLE_ROW - NUM_ROWS + Row + 1;
                               finish = (bottom + up*DistanceUp/height + OFFSET)*9/1250;
                               while (Row < NUM_ROWS + SPACING && finish <= start) {
                                       Row++;
                                       up = i + MIDDLE_ROW - NUM_ROWS + Row + 1;
                                       finish = (bottom + up*DistanceUp/height + OFFSET)*9/1250;
                                       }
                               }
                       if (!AlreadyPrinted &&
                                       ((LeftLabel && finish > MinLeft && finish <= MaxLeft)
                                       || (RightLabel && finish > MinRight && finish <= MaxRight) )
                               ) {
                               if (Row < NUM_ROWS + SPACING)
                                       ProtectFromLine = 1;
                               if (Row >= 0 && Row < NUM_ROWS
                                               && OutlineNum >= 0 && OutlineNum < NUM_OUTLINES_UP
                                               && Column < NUM_COLUMNS) {
                                       Label = finish - OFFSET*9/1250;
                                       LabelMagnitude = Label;
                                       if (Label < 0)
                                               LabelMagnitude = - Label;
                                       if (OutlineNum == 0)
                                               Outline = DigitOutlines [LabelMagnitude/7200%10];
                                       else if (OutlineNum == 1)
                                               Outline = DigitOutlines [LabelMagnitude/720%10];
                                       else if (OutlineNum == 2)
                                               Outline = DegreeOutline;
                                       else if (OutlineNum == 3)
                                               Outline = DigitOutlines [LabelMagnitude/120%6];
                                       else if (OutlineNum == 4)
                                               Outline = DigitOutlines [LabelMagnitude/12%10];
                                       else if (OutlineNum == 5)
                                               Outline = MinuteOutline;
                                       else if (OutlineNum == 6)
                                               Outline = DigitOutlines [LabelMagnitude/2%6];
                                       else if (OutlineNum == 7)
                                               Outline = DigitOutlines [LabelMagnitude%2*5];
                                       else if (OutlineNum == 8)
                                               Outline = SecondOutline;
                                       else if (OutlineNum == 9) {
                                               if (Label > 0)
                                                       Outline = NorthOutline;
                                               else
                                                       Outline = SouthOutline;
                                               }
                                       if ((Outline [Row] )
                                                       & (1 << (NUM_COLUMNS - Column - 1) ) ) {
                                               putchar (HORIZONTAL_R);
                                               putchar (HORIZONTAL_G);
                                               putchar (HORIZONTAL_B);
                                               AlreadyPrinted = 1;
                                               }
                                       }
                               }

                       /* Label for Vertical Line */
                       OutlineNum = -1;
                       BottomLabel = 0;
                       TopLabel = 0;
                       if (i >= MARGIN - SPACING
                                       && i < MARGIN + VerticalClearance) {
                               if (i >= MARGIN) {
                                       OutlineNum = (i - MARGIN)/(NUM_COLUMNS + SPACING);
                                       Column = (i - MARGIN)%(NUM_COLUMNS + SPACING);
                                       }
                               BottomLabel = 1;
                               }
                       else if (i >= height - MARGIN - VerticalClearance
                                       && i < height - MARGIN + SPACING) {
                               if (i >= height - MARGIN - VerticalClearance + SPACING) {
                                       OutlineNum = (i - height + MARGIN + VerticalClearance - SPACING)
                                                       /(NUM_COLUMNS + SPACING);
                                       Column = (i - height + MARGIN + VerticalClearance - SPACING)
                                                       %(NUM_COLUMNS + SPACING);
                                       }
                               TopLabel = 1;
                               }
                       if (BottomLabel || TopLabel) {
                               Row = NUM_ROWS + SPACING;
                               across = j + MIDDLE_ROW - Row;
                               start = (left + across*DistanceAcross/width + OFFSET)*9/1250;
                               Row--;
                               across = j + MIDDLE_ROW - Row;
                               finish = (left + across*DistanceAcross/width + OFFSET)*9/1250;
                               while (Row > - SPACING - 1 && finish <= start) {
                                       Row--;
                                       across = j + MIDDLE_ROW - Row;
                                       finish = (left + across*DistanceAcross/width + OFFSET)*9/1250;
                                       }
                               }
                       if (!AlreadyPrinted && (
                                       (BottomLabel && finish > MinBottom && finish <= MaxBottom)
                                       || (TopLabel && finish > MinTop && finish <= MaxTop) )
                               ) {
                               if (Row >= - SPACING)
                                       ProtectFromLine = 1;
                               if (Row >= 0 && Row < NUM_ROWS
                                               && OutlineNum >= 0 && OutlineNum < NUM_OUTLINES_ACROSS
                                               && Column < NUM_COLUMNS) {
                                       Label = finish - OFFSET*9/1250;
                                       LabelMagnitude = Label;
                                       if (Label < 0)
                                               LabelMagnitude = - Label;
                                       if (OutlineNum == 0)
                                               Outline = DigitOutlines [LabelMagnitude/72000%10];
                                       else if (OutlineNum == 1)
                                               Outline = DigitOutlines [LabelMagnitude/7200%10];
                                       else if (OutlineNum == 2)
                                               Outline = DigitOutlines [LabelMagnitude/720%10];
                                       else if (OutlineNum == 3)
                                               Outline = DegreeOutline;
                                       else if (OutlineNum == 4)
                                               Outline = DigitOutlines [LabelMagnitude/120%6];
                                       else if (OutlineNum == 5)
                                               Outline = DigitOutlines [LabelMagnitude/12%10];
                                       else if (OutlineNum == 6)
                                               Outline = MinuteOutline;
                                       else if (OutlineNum == 7)
                                               Outline = DigitOutlines [LabelMagnitude/2%6];
                                       else if (OutlineNum == 8)
                                               Outline = DigitOutlines [LabelMagnitude%2*5];
                                       else if (OutlineNum == 9)
                                               Outline = SecondOutline;
                                       else if (OutlineNum == 10) {
                                               if (Label > 0)
                                                       Outline = EastOutline;
                                               else
                                                       Outline = WestOutline;
                                               }
                                       if ((Outline [Row] )
                                                       & (1 << (NUM_COLUMNS - Column - 1) ) ) {
                                               putchar (VERTICAL_R);
                                               putchar (VERTICAL_G);
                                               putchar (VERTICAL_B);
                                               AlreadyPrinted = 1;
                                               }
                                       }
                               }

                       if (!ProtectFromLine && !AlreadyPrinted) {
                               PrintHorizontalLine = 0;
                               PrintVerticalLine = 0;
                               if ((bottom + i*DistanceUp/height + OFFSET)*9/1250
                               > (bottom + (i - 1 )*DistanceUp/height + OFFSET)*9/1250)
                                       PrintHorizontalLine = 1;
                               if ((left + j*DistanceAcross/width + OFFSET)*9/1250
                               > (left + (j - 1)*DistanceAcross/width + OFFSET)*9/1250)
                                       PrintVerticalLine = 1;
                               if (PrintHorizontalLine && !PrintVerticalLine) {
                                       putchar (HORIZONTAL_R);
                                       putchar (HORIZONTAL_G);
                                       putchar (HORIZONTAL_B);
                                       AlreadyPrinted = 1;
                                       }
                               if (PrintVerticalLine && !PrintHorizontalLine) {
                                       putchar (VERTICAL_R);
                                       putchar (VERTICAL_G);
                                       putchar (VERTICAL_B);
                                       AlreadyPrinted = 1;
                                       }
                               }
                       if (!AlreadyPrinted) {
                               putchar (r);
                               putchar (g);
                               putchar (b);
                               }

                       if (b == EOF)
                               r = EOF;
                       else
                               r = getchar ();
                       }
               }
       if (r != EOF)
               fprintf (stderr, "***p6addgeograticule: Too much image data.\n");
       if (b == EOF)
               fprintf (stderr, "***p6addgeograticule: Not enough image data.\n");
       fprintf (stderr, "p6addgeograticule cm per pixel:");
       fprintf (stderr, " %d N-S;", 111*(top - bottom)/height);
       fprintf (stderr, " %d E-W;\n", CmPerGmEastWest ((top + bottom)/2)*(right - left)/width);
       }

int main (int argc, char * argv [] )
       {
       char c;
       int i, ok;
       int bottom, left, right, top;

       if (argc == 1) {
               for (i = 0; i < NumUsageLines; i++)
                       printf ("%s\n", UsageLines [i] );
               }
       else if (argc != 5) {
               fprintf (stderr, "Usage: %s", argv [0] );
               fprintf (stderr, " (bottom) (left) (right) (top)\n");
               }
       else {
               ok = 1;
               if (sscanf (argv [1], "%d%c", & bottom, & c) != 1) {
                       fprintf (stderr, "***p6addgeograticule: Expecting number for bottom,");
                       fprintf (stderr, " found \"%s\".\n", argv [1] );
                       ok = 0;
                       }
               if (sscanf (argv [2], "%d%c", & left, & c) != 1) {
                       fprintf (stderr, "***p6addgeograticule: Expecting number for left,");
                       fprintf (stderr, " found \"%s\".\n", argv [2] );
                       ok = 0;
                       }
               if (sscanf (argv [3], "%d%c", & right, & c) != 1) {
                       fprintf (stderr, "***p6addgeograticule: Expecting number for right,");
                       fprintf (stderr, " found \"%s\".\n", argv [3] );
                       ok = 0;
                       }
               if (sscanf (argv [4], "%d%c", & top, & c) != 1) {
                       fprintf (stderr, "***p6addgeograticule: Expecting number for top,");
                       fprintf (stderr, " found \"%s\".\n", argv [4] );
                       ok = 0;
                       }
               if (ok) {
                       if (bottom >= top) {
                               fprintf (stderr, "***p6addgeograticule: Found bottom=%d,", bottom);
                               fprintf (stderr, " top=%d.\n", top);
                               fprintf (stderr, "     Bottom must be < top.\n");
                               ok = 0;
                               }
                       if (left >= right) {
                               fprintf (stderr, "***p6addgeograticule: Found left=%d,", left);
                               fprintf (stderr, " right=%d.\n", right);
                               fprintf (stderr, "     Left must be < right.\n");
                               ok = 0;
                               }
                       }
               if (ok)
                       AddGeoGraticule (bottom, left, right, top);
               }

       return 0;
       }