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