static char rcsid[] = "$Id: ciamap.c,v 3.10 1993/01/05 01:37:03 putz Exp $";
/*****************************************************************************
* ciamap.c - This program reads a map database file in the CIA World Base
* format and produces a netmap-format output file.
*
* Originally created by Brian Reid (
[email protected])
* Modified March 1991 by Steve Putz (
[email protected])
*****************************************************************************/
#include <stdio.h>
#include <math.h>
#include <ctype.h>
#include <strings.h>
#include <sys/file.h>
#include <sys/param.h> /* for MAXPATHLEN */
#include "cbdmap.h"
#define BIT32 int /* change these definitions to suit your */
#define BIT16 short /* own machine */
#define BIT8 char
#define TRUE 1
#define FALSE 0
/*
* The data-compression scheme uses integer seconds to represent lat/long,
* and stores each stroke as a [dx,dy] from the previous point. If dy will
* fit into 8 bits and dx into 7 bits, then the entire [dx,dy] is stored in a
* 16-bit field with bit 0x4000 turned on as a flag. If either value is too
* large for that scheme, then both are stored as 32-bit values, with the
* 0x40000000 bit turned off (even in negative numbers) in the first of them.
*/
#define MAXSEG 20000 /* maximum strokes in a segment */
#define MAXSEGS 10000
#define abs(x) (x<0 ? -(x) : x)
#define sign(x) ((x>=0) ? 1 : -1)
struct seghead sb;
BIT16 segbuf[MAXSEG];
struct segdict sd[MAXSEGS];
static long acodes[MAXSEGS];
static long acodeCounts[MAXSEGS];
static char CodeTable[100];
int MapDetail = 99;
int rightShift = 0;
int filterBits = 0;
int paethFlag = 0;
int inrec_bytes = 0;
int debug = 0;
#define PUTSHORT(val, file) { putc((val)>>8, file); putc((val)&0xFF, file); }
#define PUTCOORD(seconds, file) PUTSHORT((seconds)/20, file)
/* ------------------------------------------------------------------------ */
main(argc, argv)
int argc;
char **argv;
{
extern int optind;
extern char *optarg;
extern char *strcpy();
int c, initdone, badopts;
char *outdir = NULL, *outfname = NULL;
for (c = 0; c < 100; c++) CodeTable[c] = c;
initdone = 0;
badopts = 0;
while ((c = getopt(argc, argv, "D:d:pn:s:f:t:T:O:o:m")) != EOF)
switch (c) {
case 'D':
/* -D: debug. Argument is an integer debuging flags mask. */
debug = strtol(optarg, NULL, 0);
break;
case 'd':
/*
* -d: detail. Argument is an integer giving map detail to
* include.
*/
sscanf(optarg, "%d", &MapDetail);
break;
case 'p':
/* -p: Paeth format. Produce Paeth format map files. */
paethFlag = TRUE;
break;
case 'n':
/*
* -n: no newline chars in input. Argument is bytes/record.
*/
sscanf(optarg, "%d", &inrec_bytes);
break;
case 's':
/*
* -s: Scale data. Argument is number of bits to right shift
* coordinate data.
*/
sscanf(optarg, "%d", &rightShift);
break;
case 'f':
/* -f: Filter data. Argument is number of bits to smooth. */
sscanf(optarg, "%d", &filterBits);
break;
case 't':
/* -t: translate codes. Argument is feature code file name. */
ReadCodes(optarg, CodeTable);
break;
case 'T':
/* -T: translate codes. warn of features not in code file. */
for (c = 0; c < 100; c++) CodeTable[c] = -1;
ReadCodes(optarg, CodeTable);
break;
case 'O':
/* -O: specify output directory */
outdir = optarg;
break;
case 'o':
/* -o: specify output file */
outfname = optarg;
break;
case 'm':
/* -m: Ascii map files follow. "-m f2 f2 f3" */
for (; optind < argc && argv[optind][0] != '-'; optind++) {
if (!initdone);
initdone++;
TranslateMap(argv[optind], outdir, outfname);
}
break;
default:
badopts++;
}
fflush(stdout);
if (badopts) {
exit(2);
}
}
/* ------------------------------------------------------------------------ */
ReadCodes(name, codetable)
char *name;
char *codetable;
{
FILE *df;
int code1, code2, res;
char desc[100];
if (!(df = fopen(name, "r"))) {
perror(name);
exit(1);
}
while (!feof(df)) {
*desc = '\0';
res = fscanf(df, "%d %d %[^\n]\n", &code1, &code2, desc);
if (res < 2) {
fscanf(df, "%[^\n]\n", desc);
if (debug & 0x1000)
fprintf(stdout, "%s\n", desc);
} else {
if (debug & 0x2000)
fprintf(stdout, "%d -> %d %s\n", code1, code2, desc);
codetable[code1%100] = code2;
}
}
fclose(df);
} /* ReadCodes */
/* ------------------------------------------------------------------------ */
TranslateMap(name, outdir, outfname)
char *name, *outdir, *outfname;
{
FILE *df;
char line[100], of[MAXPATHLEN], *bname, *ext,
lad, /* 'N' or 'S' */
lod; /* 'E' or 'W' */
int Ofile = -1, first, lsn, irnk, nop, ladg, lam, las,
lodg, lom, los,
ipn, /* polygon number */
acode, acount,
big = 0, small = 0, medium = 0, skippedSegs = 0,
totalStrokes = 0, skippedStrokes = 0, lnum, nc,
i, j, strokecount, segmax, segsize, segcount, bytecount;
BIT32 jlat, /* integer latitude in seconds */
jlong, /* integer longitude in seconds */
haveLat = 0, haveLong = 0, dx, dy;
struct cbdhead header;
FILE *paethIndex = NULL, *paethData = NULL;
int paethCount, paethTotal = 0;
int headerSize = CBD_HEADSIZE2;
long roundBit = 1 << (rightShift - 1);
if (!(df = fopen(name, "r"))) {
perror(name);
exit(1);
}
bname = rindex(name, '/');
if (bname) bname++;
else bname = name;
if (outdir) sprintf(of, "%s/", outdir);
else *of = '\0';
if (outfname) strcat(of, outfname);
else strcat(of, bname);
ext = index(of, '.');
if (ext)
*ext = '\0';
else
ext = of + strlen(of);
if (paethFlag) {
strcpy(ext, ".index");
paethIndex = fopen(of, "w");
strcpy(ext, ".map");
paethData = fopen(of, "w");
if (paethIndex == NULL || paethData == NULL) {
perror(of);
exit(1);
}
} else {
if (!outfname) {
if (rightShift) {
sprintf(ext, "%+d", -rightShift);
ext += strlen(ext);
}
if (filterBits) {
sprintf(ext, "f%d", filterBits);
ext += strlen(ext);
}
strcpy(ext, ".cbd");
}
Ofile = open(of, O_RDWR | O_CREAT | O_TRUNC, 0666);
if (Ofile < 0) {
perror(of);
exit(1);
}
}
fprintf(stdout, "Reading %s\nWriting %s\n", name, of);
bzero(&header, sizeof(header));
/* note that header.magic is zero until we are done */
header.scale_shift = -rightShift;
header.minlat = DEG90_SECS;
header.maxlat = -DEG90_SECS;
header.minlong = DEG180_SECS;
header.maxlong = -DEG180_SECS;
if (Ofile >= 0) { /* reserve space for header */
write(Ofile, &header, headerSize);
}
acount = 0;
segcount = 0;
segmax = 0;
line[inrec_bytes] = '\0';
/* The header line tells us the serial number, rank, and path size */
lnum = 0;
for (;;) {
if (inrec_bytes) {
if (fread(line, inrec_bytes, 1, df) != 1) break;
} else {
if (fgets(line, 60, df) == NULL) break;
}
lnum++;
for (nc = 0; nc < 20 && line[nc] != NULL; nc++)
if (line[nc] == ' ') line[nc] = '0';
if (nc < 18)
continue;
sscanf(line, "%7d%2d%6d%5d", &lsn, &irnk, &nop, &acode);
if (debug & 0x100)
fprintf(stdout, "%d: ID=%d, rank=%d, NP=%d, code=%d\n",
segcount, lsn, irnk, nop, acode);
acode = acode * 100 + irnk;
if (CodeTable[irnk] > 32 || CodeTable[irnk] < 1) {
fprintf(stdout, "WARNING: file %s, seg %d: ID=%d, rank=%d, NP=%d, code=%d -> %d\n",
name, segcount, lsn, irnk, nop, acode, CodeTable[irnk]);
irnk = 32;
} else {
irnk = CodeTable[irnk];
}
if (irnk > MapDetail) {
for (i = 1; i <= nop; i++) {
if (inrec_bytes) {
if (fread(line, inrec_bytes, 1, df) != 1) break;
} else {
if (fgets(line, 60, df) == NULL) break;
}
lnum++;
}
} else {
sb.id = lsn;
first = FALSE;
if (segcount > MAXSEGS) {
fprintf(stdout, "Segment count overflow.\n");
close(Ofile);
fclose(df);
return;
}
if (Ofile >= 0)
sd[segcount].absaddr = tell(Ofile);
sd[segcount].segid = lsn;
sd[segcount].rank = irnk;
sd[segcount].minlat = DEG90_SECS;
sd[segcount].maxlat = -DEG90_SECS;
sd[segcount].minlong = DEG180_SECS;
sd[segcount].maxlong = -DEG180_SECS;
{ int found = 0;
for (i = 0; i < acount; i++) {
if (acodes[i] == acode) {
acodeCounts[i]++;
found = 1;
break;
}
}
if (!found) {
acodeCounts[acount] = 1;
acodes[acount++] = acode;
if (debug & 0x800)
fprintf(stdout, "code %d\n", acode);
}
}
bytecount = 0;
segsize = 0;
paethCount = 0;
for (i = 1; i <= nop; i++) {
if (inrec_bytes) {
if (fread(line, inrec_bytes, 1, df) != 1) break;
} else {
if (fgets(line, 60, df) == NULL) break;
}
lnum++;
for (j = 0; j <= 19; j++) {
if (line[j] == ' ')
line[j] = '0';
}
#define DIG(x) ((int)(x)-48)
ladg = 10 * DIG(line[0]) + DIG(line[1]);
lam = 10 * DIG(line[2]) + DIG(line[3]);
las = 10 * DIG(line[4]) + DIG(line[5]);
lad = line[6];
lodg = 100 * DIG(line[7]) + 10 * DIG(line[8]) + DIG(line[9]);
lom = 10 * DIG(line[10]) + DIG(line[11]);
los = 10 * DIG(line[12]) + DIG(line[13]);
lod = line[14];
ipn = 1000 * DIG(line[16]) + 100 * DIG(line[17]) + 10 *
DIG(line[18]) + DIG(line[19]);
if (debug & 0x200)
fprintf(stdout, "%d: %02d %02d %02d %c %03d %02d %02d %c %05d\n",
segcount, ladg, lam, las, lad, lodg, lom, los, lod, ipn);
if (ipn != i) {
fprintf(stdout, "Data error at line %d, sequence %d, segment %d: expected %d but found: %d\n%s\n",
lnum, lsn, segcount, i, ipn, line);
if (ipn > 0 && ipn < nop)
i = ipn;
}
jlat = 3600 * ladg + 60 * lam + las;
if (lad == 'S')
jlat = -jlat;
jlong = 3600 * lodg + 60 * lom + los;
if (lod == 'W')
jlong = -jlong;
if (debug & 0x04)
fprintf(stdout, "(%.4f %.4f) = (%d %d) [%x %x]",
jlong / 3600.0, jlat / 3600.0, jlong, jlat, jlong, jlat);
if (rightShift > 0) {
if (jlong & roundBit) {
jlong = (jlong >> rightShift) + 1;
if (debug & 0x20) fprintf(stdout, " x%+1 ");
} else {
jlong >>= rightShift;
}
if (jlat & roundBit) {
jlat = (jlat >> rightShift) + 1;
if (debug & 0x20) fprintf(stdout, " y%+1 ");
} else {
jlat >>= rightShift;
}
if (debug & 0x10)
fprintf(stdout, "-> (%d %d) [%x %x]", jlong, jlat, jlong, jlat);
}
if (debug & 0x04)
fprintf(stdout, "\n");
if (sd[segcount].maxlat < jlat)
sd[segcount].maxlat = jlat;
if (sd[segcount].minlat > jlat)
sd[segcount].minlat = jlat;
if (sd[segcount].maxlong < jlong)
sd[segcount].maxlong = jlong;
if (sd[segcount].minlong > jlong)
sd[segcount].minlong = jlong;
if (!first) {
first = TRUE;
sb.orgx = jlong;
sb.orgy = jlat;
strokecount = 0;
} else {
dx = jlong - haveLong;
dy = jlat - haveLat;
if (dx == 0 && dy == 0 ||
i < nop && abs(dx) + abs(dy) <= filterBits) {
skippedStrokes++;
if (debug & 0x02)
fprintf(stdout, "skipped: (%d %d)\n", dx, dy);
continue; /* don't set haveLat, haveLong */
} else {
totalStrokes++;
strokecount++;
if (abs(dx) > MAX8x || abs(dy) > MAX8y) {
/*
* Encode this transition as a pair of 32-bit
* values.
*/
big++;
if (abs(dx) <= 0x3FFF && abs(dy) <= 0x7FFF)
medium++;
bytecount += 8;
if (segsize + 4 > MAXSEG) {
fprintf(stdout, "segsize overflow.\n");
close(Ofile);
fclose(df);
return;
}
segbuf[segsize++] = (dx >> 16) & ~SHORTFLAG;
segbuf[segsize++] = dx & 0xFFFF;
segbuf[segsize++] = (dy >> 16);
segbuf[segsize++] = dy & 0xFFFF;
if (debug & 0x02)
fprintf(stdout, "%4d: long (%d %d): %04x %04x %04x %04x\n", strokecount, dx, dy, segbuf[segsize - 4] & 0xFFFF, segbuf[segsize - 3] & 0xFFFF, segbuf[segsize - 2] & 0xFFFF, segbuf[segsize - 1] & 0xFFFF);
} else {
/*
* Encode this transition as a pair of 8-bit
* values.
*/
small++;
bytecount += 2;
if (segsize + 1 > MAXSEG) {
fprintf(stdout, "segsize overflow.\n");
close(Ofile);
fclose(df);
return;
}
segbuf[segsize++] =
SHORTFLAG |
(dx << 8) |
(dy & 0xFF);
if (debug & 0x02)
fprintf(stdout, "%4d: short (%d %d): %04x\n", strokecount, dx, dy, segbuf[segsize - 1] & 0xFFFF);
} /* short encoding */
if (paethFlag) {
PUTCOORD(jlat, paethData);
PUTCOORD(jlong, paethData);
paethCount++;
}
} /* dx,dy != 0 */
} /* not first */
haveLat = jlat;
haveLong = jlong;
}
if (strokecount == 0) {
skippedSegs++; /* reuse same segment */
} else if (Ofile >= 0) {
sd[segcount].nbytes = bytecount;
sb.nstrokes = strokecount;
write(Ofile, &sb, sizeof sb);
write(Ofile, segbuf, bytecount);
if (debug & 0x01)
fprintf(stdout, "seg %d: (%.4f %.4f)=(%d %d) %d strokes %d bytes, code %03d %04d\n", sb.id, (sb.orgx << rightShift) / 3600.0, (sb.orgy << rightShift) / 3600.0, sb.orgx, sb.orgy, sb.nstrokes, bytecount, acode/10000, acode % 10000);
if (debug & 0x0400)
fprintf(stdout, "seg NSEW = (%.4f %.4f %.4f %.4f)\n",
(sd[segcount].maxlat << rightShift) / 3600.0,
(sd[segcount].minlat << rightShift) / 3600.0,
(sd[segcount].maxlong << rightShift) / 3600.0,
(sd[segcount].minlong << rightShift) / 3600.0);
header.features |= (1 << (sd[segcount].rank - 1));
if (header.maxlat < sd[segcount].maxlat)
header.maxlat = sd[segcount].maxlat;
if (header.minlat > sd[segcount].minlat)
header.minlat = sd[segcount].maxlat;
if (header.maxlong < sd[segcount].maxlong)
header.maxlong = sd[segcount].maxlong;
if (header.minlong > sd[segcount].minlong)
header.minlong = sd[segcount].minlong;
segcount++;
} else if (paethFlag) {
PUTCOORD(sd[segcount].maxlat, paethIndex);
PUTCOORD(sd[segcount].minlat, paethIndex);
PUTCOORD(sd[segcount].maxlong, paethIndex);
PUTCOORD(sd[segcount].minlong, paethIndex);
PUTSHORT(paethCount, paethIndex);
paethTotal += paethCount;
}
}
/* end of each segment */
if (segsize > segmax)
segmax = segsize;
}
/* end of file */
header.magic = headerSize == CBD_HEADSIZE2 ? CBD_MAGIC2 : CBD_MAGIC;
header.dictaddr = tell(Ofile);
header.segcount = segcount;
header.segsize = segcount * sizeof(struct segdict);
header.segmax = segmax;
fprintf(stdout,
"Segdict has %d entries (%d skipped), %d bytes, starts at %d.\n\
%d strokes (%d skipped)\n\
Largest segment has %d entries (%d bytes)\n\
%d small entries, %d big (%.2f%% of space), %d medium\n",
segcount, skippedSegs, header.segsize, header.dictaddr,
totalStrokes, skippedStrokes,
segmax, 2 * segmax,
small, big, (double) 100 * big * 4 / (big * 4 + small), medium);
{ int i;
fprintf(stdout, "features:");
for (i = 1; i <= 32; i++)
if (header.features & (1<<(i-1))) fprintf(stdout, " %d", i);
fprintf(stdout, "\n");
fprintf(stdout, "%d codes:\n", acount);
for (i = 0; i < acount; i++)
fprintf(stdout, "code %07d %5d\n", acodes[i], acodeCounts[i]);
}
if (paethFlag) {
fprintf(stdout, "\t%d paeth points\n", paethTotal);
fclose(paethData);
fclose(paethIndex);
} else {
write(Ofile, sd, (int) header.segsize);
lseek(Ofile, 0L, L_SET);
write(Ofile, &header, headerSize);
close(Ofile);
}
fclose(df);
}