home *** CD-ROM | disk | FTP | other *** search
- /*
- * xvpds.c - load routine for astronomical PDS/VICAR format pictures
- * version of 1-5-92
- *
- * Anthony A. Datri
- * Convex Computer Corp
- * 3000 Waterview Parkway
- * Ricardson, TX 75080
- * datri@convex.com
- *
- * 9-2-91 began integration. Much of this code is lifted from vicar.c,
- * which I wrote for xloadimage. This is a little simpler, though.
- * code to identify and read in PDS/VICAR images, as found on the CD-ROMS from
- * NASA, and on ames.arc.nasa.gov.
- *
- * 10-17-91 pdsuncomp is called with system(), which typically feeds the
- * commandline to sh. Make sure that your .profile adds wherever
- * you have pdsuncomp to the PATH, like
- *
- * PATH=$PATH:/usr/local/bin
- * 11-15-91 substituted vdcomp from Viking CD's for pdsuncomp. I added
- * recognition of - and shut off various messages
- *
- * 1-5-92 merged into xv rel 2
- *
- * Sources of these CD's:
- *
- * National Space Science Data Center
- * Goddard Space Flight Center
- * Code 933.4
- * Greenbelt, Maryland
- * (301) 286-6695
- * or call
- * (301) 286-9000 (300,1200,2400 bps)
- * or telnet
- * nssdca.gsfc.nasa.gov (128.183.10.4) and log in as 'NODIS' (no password).
- *
- * Randy Davis
- * LASP
- * University of Colorado
- * Boulder CO 80309-0392
- * (303) 492-6867
- * These CD's are reasonably priced. Encourage the continue production
- * of them by buying a set.
- * There are three types of files that we deal with here. I'll call them
- * PDS-labeled, PDS-labeled Huffman-encoded, and VICAR. Each consists of data
- * prefixed with a set of ASCII headers. PDS-labeled and VICAR files are raw
- * grayscale data, the dimensions of which can be found from the headers.
- * PDS-labeled, Huffman-encoded files have the image information adaptively
- * Huffman-encoded, and the encoding histogram follows the ASCII headers.
- * To decode these, we use an extended version of "cdcomp.c" from the
- * NASA Vger CD-ROMS. For this to work, you need to have pdscomp compiled
- * and in your path. pdscomp.c should be included with this distribution.
- * I've heard that newer discs have FITS images on them. If they do, support
- * for them will be added when I get one. Until then, you can use fitstopgm.
- *
- * LoadPDS(fname, numcols) - coerces a PDS/VICAR image
- * WriteVICAR(fp, pic, w, h, r,g,b, numcols, style)
- */
-
- /*
- * Copyright 1989, 1990 by Anthony A. Datri
- *
- * Permission to use, copy, and distribute for non-commercial purposes,
- * is hereby granted without fee, providing that the above copyright
- * notice appear in all copies and that both the copyright notice and this
- * permission notice appear in supporting documentation.
- *
- * This software is provided "as is" without any express or implied warranty.
- */
-
-
- #include "xvimage.h"
-
-
- #define PDSFIXED (1)
- #define PDSVARIABLE (2)
- #define PDSTRASH (-1)
- #define VICAR (3)
- #define VIKINGFIXED (4) /* Viking discs have a unique variant */
- #define VIKINGVARIABLE (5)
-
-
- #ifndef TRUE
- #define TRUE (1)
- #define FALSE (0)
- #endif
-
- #define MAX_SIZE 20480 /* a guess -- even magellan images aren't
- bigger than 2k x 2k */
- #define RTBUFFSIZE 62 /* small buffer for magic */
- #define FNAMESIZE 1024 /* too hard to generalize really getting it */
- #define PDSUNCOMP "vdcomp" /* filter to un-Huffmanize */
-
- /* This is arbitrary. Everything I've seen so far fits */
- #define COMMENTSIZE 50
-
-
- static char scanbuff [MAX_SIZE],
- rtbuff [RTBUFFSIZE],
- infobuff [COMMENTSIZE],
- spacecraft [COMMENTSIZE],
- target [COMMENTSIZE],
- filtname [COMMENTSIZE],
- mphase [COMMENTSIZE],
- iname [COMMENTSIZE],
- itime [COMMENTSIZE],
- pdsuncompfname[FNAMESIZE];
- byte *image;
-
-
- /*
- * pds_strstr.c --
- *
- * Source code for the "strstr" library routine.
- *
- * Copyright 1988 Regents of the University of California
- * Permission to use, copy, modify, and distribute this
- * software and its documentation for any purpose and without
- * fee is hereby granted, provided that the above copyright
- * notice appear in all copies. The University of California
- * makes no representations about the suitability of this
- * software for any purpose. It is provided "as is" without
- * express or implied warranty.
- */
-
- /*
- *----------------------------------------------------------------------
- *
- * pds_strstr --
- *
- * Locate the first instance of a substring in a string.
- *
- * Results:
- * If string contains substring, the return value is the
- * location of the first matching instance of substring
- * in string. If string doesn't contain substring, the
- * return value is 0. Matching is done on an exact
- * character-for-character basis with no wildcards or special
- * characters.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
- char *pds_strstr(string, substring)
- char *string; /* String to search. */
- char *substring; /* Substring to try to find in string. */
- {
- register char *a, *b;
-
- /* First scan quickly through the two strings looking for a
- * single-character match. When it's found, then compare the
- * rest of the substring.
- */
-
- b = substring;
- if (*b == 0) return string;
-
- for ( ; *string != 0; string += 1) {
- if (*string != *b) continue;
-
- a = string;
- while (1) {
- if (*b == 0) return string;
-
- if (*a++ != *b++) break;
-
- }
- b = substring;
- }
- return (char *) 0;
- }
-
-
-
-
-
- /* get a NULL-, CR-, or CRLF-terminated record from a PDS file */
- /* The method of termination is unpredictable. A pox on VMS */
-
- int getpdsrec(f,buff)
- FILE *f;
- char *buff;
- {
- static char *bp;
- static int count;
-
- count=0;
- bp=buff;
- while (1) {
- (*bp)=fgetc(f);
- switch (*bp) {
-
- case '\r': *bp='\0';
- switch ((*(bp+1))=fgetc(f)) {
- case EOF:
- case '\n': break;
- default: ungetc(*(bp+1),f);
- }
- return(count);
-
- case EOF: *bp='\0'; return(count);
-
- case '\0': return(count);
-
- default: count++; bp++;
- }
- }
- }
-
-
- /* A VICAR image seems to begin with a one-line header describing all sorts
- * of things, almost all of which don't mean much to Joe User, who just
- * wants to see what Venus looks like. The first thing in this label is a
- * string defining the size of the label, like "LBLSIZE=2048". Then there's
- * other crud of variable usefulness. Important to us are the NL and NS
- * fields, which seem to tell us the number of columns and rows, respectively.
- * Among these other fields seem to be the local map coordinates of the region.
- * These files are easy -- you can even convert one to usefulness by grabbing
- * the dimensions and lblsize and feeding to rawtopgm. Example, if lblsize,
- * nl, and ns are 2048, 1536, and 2048, respectively, use "rawtopgm -headerskip
- * 2048 2048 1536 <foo.img >foo.pgm. All of the samples I've seen are 8 bits
- * deep, so we assume that. Yeah, yeah, that's nasty and evil, and I'm
- * terribly ashamed of it, but I'm not going to bother with bit fiddling
- * until I see an image that requires it
- *
- * These images are typically fairly big, so it makes sense to verify the
- * format before we try to read it all in.
- *
- * PDS images are prefixed like this:
- *
- * od -a c2061139.imq | head -a
- * 0000000 - nul N J P L 1 I 0 0 P D S 1 0 0
- *
- * so we'll read off the first two chars, which seem to be a length field
- * of some kind, then look for NJPL.. But NOOOOOO, images on the Sampler
- * disc just HAVE to leave off the first two bytes. Bloody HELL! This
- * is the distinction between the fixed and variable-record files.
- */
-
- /*******************************************/
- int LoadPDS(fname,nc)
- char *fname;
- int nc;
- {
-
- int tempnum;
- FILE *zf;
- static int isfixed,teco,i,j,itype,lastwasinote,
- recsize,hrecsize,irecsize,isimage,labelrecs,labelsofar,
- x,y,lpsize,lssize,samplesize,returnp,labelsize,yy;
- char *tmp;
- char *ftypstr;
- unsigned long filesize;
-
- isfixed = TRUE;
- returnp = isimage = FALSE;
- itype = PDSTRASH;
-
- teco = i = j = recsize = hrecsize = irecsize = labelrecs = x = y = 0;
- lpsize = lssize = samplesize = labelsize = labelsofar = 0;
-
- (*pdsuncompfname) = (*iname) = (*target) = (*filtname) = '\0';
- (*itime) = (*spacecraft) = (*scanbuff) = (*mphase) = (*rtbuff) = '\0';
-
- /* there may be some wisdom in statically mallocing space for an 800x800
- image, since that's what the Voyager cd's have, or even a 2kx2k, since
- some of the Magellan images from ames are that size */
-
-
- zf=fopen(fname,"r");
- if (!zf) {
- printf("LoadPDS: can't open %s\n",fname);
- return(1);
- }
-
- /* figure out the file size (for Informational Purposes Only) */
- fseek(zf, 0L, 2);
- filesize = ftell(zf);
- fseek(zf, 0L, 0);
-
-
- /* read the magic. If it's got two bytes of crud in front of it, it's
- a damned "variable-length-record" file, and we have to jump through
- bloody VAX hoops, and feed it through pdsuncomp to get useful bits */
-
- if ((teco=fread(rtbuff,13,1,zf)) != 1) {
- printf("LoadPDS: couldn't read magic #: %d,%s\n",teco,fname);
- fclose(zf);
- return(1);
- }
-
- /* this makes sscanf so much happier */
- rtbuff[RTBUFFSIZE-1] = 0;
-
- if (strncmp(rtbuff, "NJPL1I00PDS",11) == 0) {
- itype=PDSFIXED;
- } else if (strncmp(rtbuff+2,"NJPL1I00PDS",11) == 0) {
- itype=PDSVARIABLE;
- } else if (strncmp(rtbuff,"CCSD3Z",6) == 0) {
- itype=VIKINGFIXED; /* unique variant of PDS on Viking discs (browse) */
- } else if (strncmp(rtbuff+2,"CCSD3Z",6) == 0) {
- itype=VIKINGVARIABLE;
- } else if (sscanf(rtbuff,"LBLSIZE = %d%n",&labelsize,&labelsofar) == 1) {
- itype=VICAR;
- } else {
- printf("LoadPDS: can't handle this file, for some reason\n");
- fclose(zf);
- return(1);
- }
-
- switch (itype) {
- case PDSFIXED:
- case VICAR:
- case VIKINGFIXED: isfixed=TRUE;
- sprintf(formatStr, "PDS/VICAR, 8 bits per pixel. (%ld bytes)",
- filesize);
- break;
- case PDSVARIABLE:
- case VIKINGVARIABLE: isfixed=FALSE;
- sprintf(formatStr, "Huffman-encoded PDS, 8 bits per pixel. (%ld bytes)",
- filesize);
- }
-
- if ((itype == PDSFIXED) || (itype == PDSVARIABLE) ||
- (itype == VIKINGFIXED) || (itype == VIKINGVARIABLE)) {
-
- if (isfixed == FALSE) {
- teco = ((*rtbuff) + ((*(rtbuff+1)) << 8));
- fread(rtbuff,(teco % 2 ? teco + 1 : teco ) - 11,1,zf);
- }
-
- /* we know that we've got a PDS file of some sort. We have to
- search through the labels looking for things because usage doesn't seem
- to be consistent, or even predictable. When I added this format to
- xloadimage, I had the code spew out various peices of information, like
- the name of the target, filter used, etc. Xv, however, doesn't really
- provide a mechanism for this kind of thing, and I can't really stick it
- into the info box without overhauling it. Maybe later I'll add a
- -verbose switch which this module will interpret as license to scribble
- on stdout or stderr */
-
- for (;;) {
- if (isfixed) {
- if (getpdsrec(zf,scanbuff) == 0) {
- printf("corrupt or incomplete PDS labels (low fread)\n");
- break;
- }
- } else {
- /* AAAARGGGGH! We've got a bloody variable-length file where
- * the headers are preceded by a byte count, as a VAX 16-bit
- * integer of all things. VMS: Just Say NFW.
- * "A compressed image file is composed of variable-length records
- * defined according to the ISO standard. Each variable length
- * record starts with a record length indicator, stored as a 16-bit
- * integer, followed by the number of bytes indicated in the record
- * length indicator. If the length indicator is odd, then a pad byte
- * is appended to the end of the record so that all records contain
- * an even number of bytes."*/
-
- i=getc(zf);
- j=getc(zf);
- if (j == EOF) {
- printf("LoadPDS: j is EOF\n");
- fclose(zf);
- return(1);
- }
-
- teco = i + (j << 8);
- if (teco % 2) teco++;
-
- if (fread(scanbuff,teco,1,zf) != 1) {
- printf("LoadPDS: bad fread reading labels\n");
- fclose(zf);
- return(1);
- }
-
- scanbuff[teco]='\0';
- }
-
- /* otay, we've managed to wrestle a header of some sort from the
- file -- now we grep through until we hit the END, at which point
- we break out of the loop and see what we've got. In the future,
- we'll check for informational headers and write them out if the
- fool user wants them. There seems to be disagreement about what
- the headers are called, since we might find, for example, either
- TARGET_BODY or TARGET_NAME. Bloody 'ell. */
-
- if (strcmp(scanbuff,"END") == 0) {
- break;
- } else if (sscanf(scanbuff,"RECORD_TYPE = %s",rtbuff) == 1) {
- if (strncmp(rtbuff,"VARIABLE_LENGTH",15) == 0) {
- /* itype=PDSVARIABLE; */
- } else if (strncmp(rtbuff,"FIXED_LENGTH",12) == 0) {
- /* itype=PDSFIXED;*/
- } else {
- rtbuff[RTBUFFSIZE-1]='\0'; /* juuuust in case */
- printf("LoadPDS: unsupported record type \"%s\"\n",rtbuff);
- fclose(zf);
- return(1);
- }
- } else if (sscanf(scanbuff,"RECORD_BYTES = %d",&recsize) == 1) {
- /* these default to RECORD_BYTES unless explicitly set */
- if (hrecsize == 0) hrecsize=recsize;
- if (irecsize == 0) irecsize=recsize;
- continue;
- } else if (sscanf(scanbuff,"FILE_TYPE = %s", rtbuff) != NULL) {
- if (strncmp(rtbuff,"IMAGE",5) == 0) {
- isimage=TRUE;
- continue;
- } else {
- isimage=FALSE;
- break;
- }
- } else if ((sscanf(scanbuff," HEADER_RECORDS = %d",&labelrecs) == 1) ||
- (sscanf(scanbuff," LABEL_RECORDS = %d", &labelrecs) == 1)) {
- continue;
- } else if (sscanf(scanbuff," IMAGE_LINES = %d",&y) == 1) {
- isimage=TRUE; continue;
- } else if (sscanf(scanbuff," LINE_SAMPLES = %d",&x) == 1) {
- continue;
- } else if (sscanf(scanbuff," LINES = %d",&y) == 1) {
- isimage=TRUE; continue;
- } else if (sscanf(scanbuff," HEADER_RECORD_BYTES = %d",&hrecsize)==1) {
- continue;
- } else if (sscanf(scanbuff," IMAGE_RECORD_BYTES = %d",&irecsize)==1) {
- continue;
- } else if (sscanf(scanbuff," LINE_PREFIX_BYTES = %d",&lpsize)==1) {
- continue;
- } else if (sscanf(scanbuff," LINE_SUFFIX_BYTES = %d",&lssize)==1) {
- continue;
- } else if (sscanf(scanbuff," SAMPLE_BITS = %d", &samplesize) == 1) {
- continue;
- } else if (sscanf(scanbuff," SPACECRAFT_NAME = %s", spacecraft) == 1) {
- continue;
-
- } else if (sscanf(scanbuff," TARGET_NAME = %s", target) == 1) {
- continue;
- } else if (sscanf(scanbuff," TARGET_BODY = %s", target) == 1) {
- continue;
-
- } else if (sscanf(scanbuff," SPACECRAFT_EVENT_TIME = %s", itime) == 1) {
- continue;
- } else if (sscanf(scanbuff," IMAGE_TIME = %s", itime) == 1) {
- continue;
- } else if (sscanf(scanbuff," FILTER_NAME = %s", filtname) == 1) {
- continue;
- } else if (sscanf(scanbuff," INSTRUMENT_FILTER_NAME = %s", filtname)
- == 1) {
- continue;
- }
- }
-
- if ((isimage == TRUE) && (labelsize=(labelrecs * hrecsize))) {
- if (samplesize!= 8) {
- printf("This image is not an 8-bit PDS file. Sorry.\n");
- fclose(zf);
- return(1);
- }
- } else {
- printf("This looks like a PDS/VICAR image, but not enough.\n");
- fclose(zf);
- return(1);
- }
-
- /* Some day this should be generalized to read <>8-bit files. If I
- ever find one that's interesting, I'll do the work. */
-
- } else if (itype == VICAR) {
- /* we've got a VICAR file. Let's find out how big the puppy is */
-
- if (fread(scanbuff,labelsize-labelsofar,1,zf) == 1) {
- if ((tmp = (char *) pds_strstr(scanbuff,"NL=")) == NULL) {
- printf("LoadPDS: bad NL in VICAR\n");
- returnp=TRUE;
- }
-
- if (sscanf(tmp,"NL = %d",&y) != 1) {
- printf("LoadPDS: bad scan NL in VICAR\n");
- returnp=TRUE;
- }
-
- if ((tmp = (char *) pds_strstr(scanbuff, "NS=")) == NULL) {
- printf("LoadPDS: bad NS in VICAR\n");
- returnp=TRUE;
- }
-
- if (sscanf(tmp, "NS = %d",&x) != 1) {
- printf("LoadPDS: bad scan NS in VICAR\n");
- returnp=TRUE;
- }
- }
-
- } else {
- printf("LoadPDS: Unable to parse data.\n");
- returnp=TRUE;
- }
-
- if (returnp) {
- fclose(zf);
- return(1);
- }
-
- /* PDS files tend to have lots of information like this in them. The
- * following are a few of the more interesting headers. We'd do more, but
- * there's only so much space in the info box */
-
- *infobuff='\0';
- if (*spacecraft) {
- strcat(infobuff,spacecraft);
- }
-
- if (*target) {
- strcat(infobuff,", ");
- strcat(infobuff,target);
- }
-
- if (*filtname) {
- strcat(infobuff,", ");
- strcat(infobuff,filtname);
- }
-
- if (*itime) {
- strcat(infobuff,", ");
- strcat(infobuff,itime);
- }
-
- printf(infobuff);
-
- strcpy(pdsuncompfname,fname);
- ftypstr = "";
-
- switch (itype) {
- case VICAR:
- sprintf(formatStr, "VICAR, 8 bits per pixel. (%ld bytes)", filesize);
- ftypstr = "VICAR";
- rewind(zf);
- break;
-
- case PDSFIXED:
- case VIKINGFIXED:
- sprintf(formatStr, "PDS, 8 bits per pixel. (%ld bytes)", filesize);
- ftypstr = "PDS";
- rewind(zf);
- break;
-
- case PDSVARIABLE:
- case VIKINGVARIABLE:
- sprintf(formatStr, "PDS, 8 bits per pixel, Huffman-encoded. (%ld bytes)",
- filesize);
- ftypstr = "PDS (Huffman)";
- fclose(zf);
- strcpy(pdsuncompfname,"/tmp/xvhuffXXXXXX");
- mktemp(pdsuncompfname);
- sprintf(scanbuff,"%s %s - 4 >%s\n",PDSUNCOMP,fname,pdsuncompfname);
- printf("De-Huffmanizing '%s'...\n",fname);
- /* pdsuncomp filters to a raw file */
- if (tempnum=system(scanbuff)) {
- printf("Unable to de-Huffmanize '%s'.\n",fname);
- return(1);
- }
-
- zf=fopen(pdsuncompfname,"r");
- if (!zf) {
- printf("LoadPDS: can't open uncompressed file %s\n",
- pdsuncompfname);
- return(1);
- }
- }
-
- /* skip headers */
-
- if ( isfixed == TRUE ) {
- fread(scanbuff,labelsize,1,zf);
- }
-
- image=(byte *)malloc(x*y);
- if (image == NULL) {
- printf("LoadPDS: couldn't malloc %d\n",x*y);
- fclose(zf);
- if (isfixed == FALSE)
- unlink(pdsuncompfname);
- exit(1);
- }
-
- if ((lssize || lpsize) && ((itype == PDSFIXED) || (itype == VIKINGFIXED)) ) {
- /* ARrrrgh. Some of these images have crud intermixed with the image, */
- /* preventing us from freading in one fell swoop */
- /* (whatever a fell swoop is */
-
- for (yy=0; yy<y; yy++) {
- if (lpsize && ((teco=(fread(scanbuff,lpsize,1,zf))) != 1)) {
- printf( "LoadPDS: unexpected EOF reading prefix\n");
- fclose(zf);
- return(1);
- }
-
- if ((teco=(fread(image+(yy*x),x,1,zf))) != 1) {
- printf( "LoadPDS: unexpected EOF reading line %d\n",yy);
- fclose(zf);
- return(1);
- }
-
- if (lssize && ((teco=(fread(scanbuff,lssize,1,zf))) != 1)) {
- printf( "LoadPDS: unexpected EOF reading suffix\n");
- fclose(zf);
- return(1);
- }
- }
-
- } else if ((yy=fread(image,x*y,1,zf)) != 1) {
- printf("LoadPDS: error reading image data\n");
- fclose(zf);
- if (itype==PDSVARIABLE || itype==VIKINGVARIABLE)
- unlink(pdsuncompfname);
-
- return(1);
- }
-
- fclose(zf);
- if (isfixed == FALSE)
- unlink(pdsuncompfname);
-
- pic = image;
- pWIDE = x;
- pHIGH = y;
-
- /* these are grayscale, so cobble up a ramped colormap */
- /* Viking images on the CD's seem to be inverted. Sigh */
-
- switch (itype) {
- case PDSVARIABLE:
- case PDSFIXED:
- case VICAR:
- for (yy=0; yy<=255; yy++) {
- r[yy] = g[yy] = b[yy] = yy;
- }
- break;
-
- case VIKINGFIXED:
- case VIKINGVARIABLE:
- for (yy=0; yy<=255; yy++) {
- r[yy] = g[yy] = b[yy] = (255-yy);
- }
- }
-
- return(0);
- }
-