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 (reid@pa.dec.com) * Modified March 1991 by Steve Putz (putz@parc.xerox.com) *****************************************************************************/ #include #include #include #include #include #include /* 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); }