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;
	}