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