#include #include #include #include "map.h" #include "iplot.h" #define NVERT 20 /* max number of vertices in a -v polygon */ #define HALFWIDTH 8192 /* output scaled to fit in -HALFWIDTH,HALFWIDTH */ #define LONGLINES (HALFWIDTH*4) /* permissible segment lengths */ #define SHORTLINES (HALFWIDTH/8) #define SCALERATIO 10 /* of abs to rel data (see map(5)) */ #define RESOL 2. /* coarsest resolution for tracing grid (degrees) */ #define TWO_THRD 0.66666666666666667 int normproj(double, double, double *, double *); int posproj(double, double, double *, double *); int picut(struct place *, struct place *, double *); double reduce(double); short getshort(FILE *); char *mapindex(char *); proj projection; static char *mapdir = "/lib/map"; /* default map directory */ struct file { char *name; char *color; char *style; }; static struct file dfltfile = { "world", BLACK, SOLID /* default map */ }; static struct file *file = &dfltfile; /* list of map files */ static int nfile = 1; /* length of list */ static char *currcolor = BLACK; /* current color */ static char *gridcolor = BLACK; static char *bordcolor = BLACK; extern struct index index[]; int halfwidth = HALFWIDTH; static int (*cut)(struct place *, struct place *, double *); static int (*limb)(double*, double*, double); static void dolimb(void); static int onlimb; static int poles; static double orientation[3] = { 90., 0., 0. }; /* -o option */ static oriented; /* nonzero if -o option occurred */ static upright; /* 1 if orientation[0]==90, -1 if -90, else 0*/ static int delta = 1; /* -d setting */ static double limits[4] = { /* -l parameters */ -90., 90., -180., 180. }; static double klimits[4] = { /* -k parameters */ -90., 90., -180., 180. }; static int limcase; static double rlimits[4]; /* limits expressed in radians */ static double lolat, hilat, lolon, hilon; static double window[4] = { /* option -w */ -90., 90., -180., 180. }; static windowed; /* nozero if option -w */ static struct vert { double x, y; } v[NVERT+2]; /*clipping polygon*/ static struct edge { double a, b, c; } e[NVERT]; /* coeffs for linear inequality */ static int nvert; /* number of vertices in clipping polygon */ static double rwindow[4]; /* window, expressed in radians */ static double params[2]; /* projection params */ /* bounds on output values before scaling; found by coarse survey */ static double xmin = 100.; static double xmax = -100.; static double ymin = 100.; static double ymax = -100.; static double xcent, ycent; static double xoff, yoff; double xrange, yrange; static int left = -HALFWIDTH; static int right = HALFWIDTH; static int bottom = -HALFWIDTH; static int top = HALFWIDTH; static int longlines = SHORTLINES; /* drop longer segments */ static int shortlines = SHORTLINES; static int bflag = 1; /* 0 for option -b */ static int s1flag = 0; /* 1 for option -s1 */ static int s2flag = 0; /* 1 for option -s2 */ static int rflag = 0; /* 1 for option -r */ static int kflag = 0; /* 1 if option -k occurred */ static int xflag = 0; /* 1 for option -x */ int vflag = 1; /* -1 if option -v occurred */ static double position[3]; /* option -p */ static double center[3] = {0., 0., 0.}; /* option -c */ static struct coord crot; /* option -c */ static double grid[3] = { 10., 10., RESOL }; /* option -g */ static double dlat, dlon; /* resolution for tracing grid in lat and lon */ static double scaling; /* to compute final integer output */ static struct file *track; /* options -t and -u */ static int ntrack; /* number of tracks present */ static char *symbolfile; /* option -y */ void clamp(double *px, double v); void clipinit(void); double diddle(struct place *, double, double); double diddle(struct place *, double, double); void dobounds(double, double, double, double, int); void dogrid(double, double, double, double); int duple(struct place *, double); double fmax(double, double); double fmin(double, double); void getdata(char *); int gridpt(double, double, int); int inpoly(double, double); int inwindow(struct place *); void pathnames(void); int pnorm(double); void radbds(double *w, double *rw); void revlon(struct place *, double); void satellite(struct file *); int seeable(double, double); void windlim(void); void realcut(void); int option(char *s) { if(s[0]=='-' && (s[1]<'0'||s[1]>'9')) return(s[1]!='.'&&s[1]!=0); else return(0); } void conv(int k, struct coord *g) { g->l = (0.0001/SCALERATIO)*k; sincos(g); } int main(int argc, char *argv[]) { int i,k; char *s, *t, *style; double x, y; double lat, lon; double *wlim; double dd; if(sizeof(short)!=2) abort(); /* getshort() won't work */ s = getenv("MAP"); if(s) file[0].name = s; s = getenv("MAPDIR"); if(s) mapdir = s; if(argc<=1) error("usage: map projection params options"); for(k=0;index[k].name;k++) { s = index[k].name; t = argv[1]; while(*s == *t){ if(*s==0) goto found; s++; t++; } } fprintf(stderr,"projections:\n"); for(i=0;index[i].name;i++) { fprintf(stderr,"%s",index[i].name); for(k=0; k=argc||option(argv[i])) { fprintf(stderr,"%s needs %d params\n",index[k].name,index[k].npar); exits("error"); } params[i] = atof(argv[i]); } argv += i; argc -= i; while(argc>0&&option(argv[0])) { argc--; argv++; switch(argv[-1][1]) { case 'm': if(file == &dfltfile) { file = 0; nfile = 0; } while(argc && !option(*argv)) { file = realloc(file,(nfile+1)*sizeof(*file)); file[nfile].name = *argv; file[nfile].color = currcolor; file[nfile].style = SOLID; nfile++; argv++; argc--; } break; case 'b': bflag = 0; for(nvert=0;nvert=2;nvert++) { if(option(*argv)) break; v[nvert].x = atof(*argv++); argc--; if(option(*argv)) break; v[nvert].y = atof(*argv++); argc--; } if(nvert>=NVERT) error("too many clipping vertices"); break; case 'g': gridcolor = currcolor; for(i=0;i<3&&argc>i&&!option(argv[i]);i++) grid[i] = atof(argv[i]); switch(i) { case 0: grid[0] = grid[1] = 0.; break; case 1: grid[1] = grid[0]; } argc -= i; argv += i; break; case 't': style = SOLID; goto casetu; case 'u': style = DOTDASH; casetu: while(argc && !option(*argv)) { track = realloc(track,(ntrack+1)*sizeof(*track)); track[ntrack].name = *argv; track[ntrack].color = currcolor; track[ntrack].style = style; ntrack++; argv++; argc--; } break; case 'r': rflag++; break; case 's': switch(argv[-1][2]) { case '1': s1flag++; break; case 0: /* compatibility */ case '2': s2flag++; } break; case 'o': for(i=0;i<3&&i0&&!option(argv[0])) { delta = atoi(argv[0]); argv++; argc--; } break; case 'w': bordcolor = currcolor; windowed++; for(i=0;ii&&!option(argv[i]);i++) center[i] = atof(argv[i]); argc -= i; argv += i; break; case 'p': for(i=0;i<3&&argc>i&&!option(argv[i]);i++) position[i] = atof(argv[i]); argc -= i; argv += i; if(i!=3||position[2]<=0) error("incomplete positioning"); break; case 'y': if(argc>0&&!option(argv[0])) { symbolfile = argv[0]; argc--; argv++; } break; case 'v': if(index[k].limb == 0) error("-v does not apply here"); vflag = -1; break; case 'x': xflag = 1; break; case 'C': if(argc && !option(*argv)) { currcolor = colorcode(*argv); argc--; argv++; } break; } } if(argc>0) error("error in arguments"); pathnames(); clamp(&limits[0],-90.); clamp(&limits[1],90.); clamp(&klimits[0],-90.); clamp(&klimits[1],90.); clamp(&window[0],-90.); clamp(&window[1],90.); radbds(limits,rlimits); limcase = limits[2]<-180.?0: limits[3]>180.?2: 1; if( window[0]>=window[1]|| window[2]>=window[3]|| window[0]>90.|| window[1]<-90.|| window[2]>180.|| window[3]<-180.) error("unreasonable window"); windlim(); radbds(window,rwindow); upright = orientation[0]==90? 1: orientation[0]==-90? -1: 0; if(index[k].spheroid && !upright) error("can't tilt the spheroid"); if(limits[2]>limits[3]) limits[3] += 360; if(!oriented) orientation[2] = (limits[2]+limits[3])/2; orient(orientation[0],orientation[1],orientation[2]); projection = (*index[k].prog)(params[0],params[1]); if(projection == 0) error("unreasonable projection parameters"); clipinit(); grid[0] = fabs(grid[0]); grid[1] = fabs(grid[1]); if(!kflag) for(i=0;i<4;i++) klimits[i] = limits[i]; if(klimits[2]>klimits[3]) klimits[3] += 360; lolat = limits[0]; hilat = limits[1]; lolon = limits[2]; hilon = limits[3]; if(lolon>=hilon||lolat>=hilat||lolat<-90.||hilat>90.) error("unreasonable limits"); wlim = kflag? klimits: window; dlat = fmin(hilat-lolat,wlim[1]-wlim[0])/16; dlon = fmin(hilon-lolon,wlim[3]-wlim[2])/32; dd = fmax(dlat,dlon); while(grid[2]>fmin(dlat,dlon)/2) grid[2] /= 2; realcut(); if(nvert<=0) { for(lat=klimits[0];latklimits[1]) lat = klimits[1]; for(lon=klimits[2];lonxmax) xmax = x; if(yymax) ymax = y; } } } else { for(i=0; ixmax) xmax = x; if(yymax) ymax = y; } } xrange = xmax - xmin; yrange = ymax - ymin; if(xrange<=0||yrange<=0) error("map seems to be empty"); scaling = 2; /*plotting area from -1 to 1*/ if(position[2]!=0) { if(posproj(position[0]-.5,position[1],&xcent,&ycent)==0|| posproj(position[0]+.5,position[1],&x,&y)==0) error("unreasonable position"); scaling /= (position[2]*hypot(x-xcent,y-ycent)); if(posproj(position[0],position[1],&xcent,&ycent)==0) error("unreasonable position"); } else { scaling /= (xrange>yrange?xrange:yrange); xcent = (xmin+xmax)/2; ycent = (ymin+ymax)/2; } xoff = center[0]/scaling; yoff = center[1]/scaling; crot.l = center[2]*RAD; sincos(&crot); scaling *= HALFWIDTH*0.9; if(symbolfile) getsyms(symbolfile); if(!s2flag) { openpl(); erase(); } range(left,bottom,right,top); comment("grid",""); colorx(gridcolor); pen(DOTTED); if(grid[0]>0.) for(lat=ceil(lolat/grid[0])*grid[0]; lat<=hilat;lat+=grid[0]) dogrid(lat,lat,lolon,hilon); if(grid[1]>0.) for(lon=ceil(lolon/grid[1])*grid[1]; lon<=hilon;lon+=grid[1]) dogrid(lolat,hilat,lon,lon); comment("border",""); colorx(bordcolor); pen(SOLID); if(bflag) { dolimb(); dobounds(lolat,hilat,lolon,hilon,0); dobounds(window[0],window[1],window[2],window[3],1); } lolat = floor(limits[0]/10)*10; hilat = ceil(limits[1]/10)*10; lolon = floor(limits[2]/10)*10; hilon = ceil(limits[3]/10)*10; if(lolon>hilon) hilon += 360.; /*do tracks first so as not to lose the standard input*/ for(i=0;inlat.lnlat.l>rwindow[1]+FUZZ|| geog->wlon.lwlon.l>rwindow[3]+FUZZ) return(0); else return(1); } int inlimits(struct place *g) { if(rlimits[0]-FUZZ>g->nlat.l|| rlimits[1]+FUZZnlat.l) return(0); switch(limcase) { case 0: if(rlimits[2]+TWOPI-FUZZ>g->wlon.l&& rlimits[3]+FUZZwlon.l) return(0); break; case 1: if(rlimits[2]-FUZZ>g->wlon.l|| rlimits[3]+FUZZwlon.l) return(0); break; case 2: if(rlimits[2]>g->wlon.l&& rlimits[3]-TWOPI+FUZZwlon.l) return(0); break; } return(1); } long patch[18][36]; void getdata(char *mapfile) { char *indexfile; int kx,ky,c; int k; long b; long *p; int ip, jp; int n; struct place g; int i, j; double lat, lon; int conn; FILE *ifile, *xfile; indexfile = mapindex(mapfile); xfile = fopen(indexfile,"r"); if(xfile==NULL) filerror("can't find map index", indexfile); free(indexfile); for(i=0,p=patch[0];i<18*36;i++,p++) *p = 1; while(!feof(xfile) && fscanf(xfile,"%d%d%ld",&i,&j,&b)==3) patch[i+9][j+18] = b; fclose(xfile); ifile = fopen(mapfile,"r"); if(ifile==NULL) filerror("can't find map data", mapfile); for(lat=lolat;lat=0&&(jp=getc(ifile))>=0){ if(ip!=(i&0377)||jp!=(j&0377)) break; n = getshort(ifile); conn = 0; if(n > 0) { /* absolute coordinates */ kx = ky = 0; /* set */ for(k=0;k0) return(1); return(0); } void satellite(struct file *t) { char sym[50]; char lbl[50]; double scale; register conn; double lat,lon; struct place place; static FILE *ifile = stdin; if(t->name[0]!='-'||t->name[1]!=0) { fclose(ifile); if((ifile=fopen(t->name,"r"))==NULL) filerror("can't find track", t->name); } comment("track",t->name); colorx(t->color); pen(t->style); for(;;) { conn = 0; while(!feof(ifile) && fscanf(ifile,"%lf%lf",&lat,&lon)==2){ latlon(lat,lon,&place); if(fscanf(ifile,"%1s",lbl) == 1) { if(strchr("+-.0123456789",*lbl)==0) break; ungetc(*lbl,ifile); } conn = plotpt(&place,conn); } if(feof(ifile)) return; fscanf(ifile,"%[^\n]",lbl+1); switch(*lbl) { case '"': if(plotpt(&place,conn)) text(lbl+1); break; case ':': case '!': if(sscanf(lbl+1,"%s %lf",sym,&scale) <= 1) scale = 1; if(plotpt(&place,conn?conn:-1)) { int r = *lbl=='!'?0:rflag?-1:1; pen(SOLID); if(putsym(&place,sym,scale,r) == 0) text(lbl); pen(t->style); } break; default: if(plotpt(&place,conn)) text(lbl); break; } } } int pnorm(double x) { int i; i = x/10.; i %= 36; if(i>=18) return(i-36); if(i<-18) return(i+36); return(i); } void error(char *s) { fprintf(stderr,"map: \r\n%s\n",s); exits("error"); } void filerror(char *s, char *f) { fprintf(stderr,"\r\n%s %s\n",s,f); exits("error"); } char * mapindex(char *s) { char *t = malloc(strlen(s)+3); strcpy(t,s); strcat(t,".x"); return t; } #define NOPT 32767 static ox = NOPT, oy = NOPT; int cpoint(int xi, int yi, int conn) { int dx = abs(ox-xi); int dy = abs(oy-yi); if(!xflag && (xi=right || yi=top)) { ox = oy = NOPT; return 0; } if(conn == -1) /* isolated plotting symbol */ {} else if(!conn) point(xi,yi); else { if(dx+dy>longlines) { ox = oy = NOPT; /* don't leap across cuts */ return 0; } if(dx || dy) vec(xi,yi); } ox = xi, oy = yi; return dx+dy<=2? 2: 1; /* 2=very near; see dogrid */ } struct place oldg; int plotpt(struct place *g, int conn) { int kx,ky; int ret; double cutlon; if(!inlimits(g)) { return(0); } normalize(g); if(!inwindow(g)) { return(0); } switch((*cut)(g,&oldg,&cutlon)) { case 2: if(conn) { ret = duple(g,cutlon)|duple(g,cutlon); oldg = *g; return(ret); } case 0: conn = 0; default: /* prevent diags about bad return value */ case 1: oldg = *g; ret = doproj(g,&kx,&ky); if(ret==0 || !onlimb && ret*vflag<=0) return(0); ret = cpoint(kx,ky,conn); return ret; } } int doproj(struct place *g, int *kx, int *ky) { int i; double x,y,x1,y1; /*fprintf(stderr,"dopr1 %f %f \n",g->nlat.l,g->wlon.l);*/ i = fixproj(g,&x,&y); if(i == 0) return(0); if(rflag) x = -x; /*fprintf(stderr,"dopr2 %f %f\n",x,y);*/ if(!inpoly(x,y)) { return 0; } x1 = x - xcent; y1 = y - ycent; x = (x1*crot.c - y1*crot.s + xoff)*scaling; y = (x1*crot.s + y1*crot.c + yoff)*scaling; *kx = x + (x>0?.5:-.5); *ky = y + (y>0?.5:-.5); return(i); } int duple(struct place *g, double cutlon) { int kx,ky; int okx,oky; struct place ig; revlon(g,cutlon); revlon(&oldg,cutlon); ig = *g; invert(&ig); if(!inlimits(&ig)) return(0); if(doproj(g,&kx,&ky)*vflag<=0 || doproj(&oldg,&okx,&oky)*vflag<=0) return(0); cpoint(okx,oky,0); cpoint(kx,ky,1); return(1); } void revlon(struct place *g, double cutlon) { g->wlon.l = reduce(cutlon-reduce(g->wlon.l-cutlon)); sincos(&g->wlon); } /* recognize problems of cuts * move a point across cut to side of its predecessor * if its very close to the cut * return(0) if cut interrupts the line * return(1) if line is to be drawn normally * return(2) if line is so close to cut as to * be properly drawn on both sheets */ int picut(struct place *g, struct place *og, double *cutlon) { *cutlon = PI; return(ckcut(g,og,PI)); } int nocut(struct place *g, struct place *og, double *cutlon) { USED(g, og, cutlon); /* #pragma ref g #pragma ref og #pragma ref cutlon */ return(1); } int ckcut(struct place *g1, struct place *g2, double lon) { double d1, d2; double f1, f2; int kx,ky; d1 = reduce(g1->wlon.l -lon); d2 = reduce(g2->wlon.l -lon); if((f1=fabs(d1))0) cpoint(kx,ky,0); } if(f1PI*TWO_THRD||f2>PI*TWO_THRD) return(1); return(d1*d2>=0); } double diddle(struct place *g, double lon, double d) { double d1; d1 = FUZZ/2; if(d<0) d1 = -d1; g->wlon.l = reduce(lon+d1); sincos(&g->wlon); return(d1); } double reduce(double lon) { if(lon>PI) lon -= 2*PI; else if(lon<-PI) lon += 2*PI; return(lon); } double tetrapt = 35.26438968; /* atan(1/sqrt(2)) */ void dogrid(double lat0, double lat1, double lon0, double lon1) { double slat,slon,tlat,tlon; register int conn, oconn; slat = tlat = slon = tlon = 0; if(lat1>lat0) slat = tlat = fmin(grid[2],dlat); else slon = tlon = fmin(grid[2],dlon);; conn = oconn = 0; while(lat0<=lat1&&lon0<=lon1) { conn = gridpt(lat0,lon0,conn); if(projection==Xguyou&&slat>0) { if(lat0<-45&&lat0+slat>-45) conn = gridpt(-45.,lon0,conn); else if(lat0<45&&lat0+slat>45) conn = gridpt(45.,lon0,conn); } else if(projection==Xtetra&&slat>0) { if(lat0<-tetrapt&&lat0+slat>-tetrapt) { gridpt(-tetrapt-.001,lon0,conn); conn = gridpt(-tetrapt+.001,lon0,0); } else if(lat0tetrapt) { gridpt(tetrapt-.001,lon0,conn); conn = gridpt(tetrapt+.001,lon0,0); } } if(conn==0 && oconn!=0) { if(slat+slon>.05) { lat0 -= slat; /* steps too big */ lon0 -= slon; /* or near bdry */ slat /= 2; slon /= 2; conn = oconn = gridpt(lat0,lon0,conn); } else oconn = 0; } else { if(conn==2) { slat = tlat; slon = tlon; conn = 1; } oconn = conn; } lat0 += slat; lon0 += slon; } gridpt(lat1,lon1,conn); } static gridinv; /* nonzero when doing window bounds */ int gridpt(double lat, double lon, int conn) { struct place g; /*fprintf(stderr,"%f %f\n",lat,lon);*/ latlon(lat,lon,&g); if(gridinv) invert(&g); return(plotpt(&g,conn)); } /* win=0 ordinary grid lines, win=1 window lines */ void dobounds(double lolat, double hilat, double lolon, double hilon, int win) { gridinv = win; if(lolat>-90 || win && (poles&1)!=0) dogrid(lolat+FUZZ,lolat+FUZZ,lolon,hilon); if(hilat<90 || win && (poles&2)!=0) dogrid(hilat-FUZZ,hilat-FUZZ,lolon,hilon); if(hilon-lolon<360 || win && cut==picut) { dogrid(lolat,hilat,lolon+FUZZ,lolon+FUZZ); dogrid(lolat,hilat,hilon-FUZZ,hilon-FUZZ); } gridinv = 0; } static void dolimb(void) { double lat, lon; double res = fmin(dlat, dlon)/4; int conn = 0; int newconn; if(limb == 0) return; onlimb = gridinv = 1; for(;;) { newconn = (*limb)(&lat, &lon, res); if(newconn == -1) break; conn = gridpt(lat, lon, conn*newconn); } onlimb = gridinv = 0; } void radbds(double *w, double *rw) { int i; for(i=0;i<4;i++) rw[i] = w[i]*RAD; rw[0] -= FUZZ; rw[1] += FUZZ; rw[2] -= FUZZ; rw[3] += FUZZ; } void windlim(void) { double center = orientation[0]; double colat; if(center>90) center = 180 - center; if(center<-90) center = -180 - center; if(fabs(center)>90) error("unreasonable orientation"); colat = 90 - window[0]; if(center-colat>limits[0]) limits[0] = center - colat; if(center+colat 16 bits */ return r; } double fmin(double x, double y) { return(xy?x:y); } void clamp(double *px, double v) { *px = (v<0?fmax:fmin)(*px,v); } void pathnames(void) { int i; char *t, *indexfile, *name; FILE *f, *fx; for(i=0; i=0) s = 1; if(t>FUZZ && s<=0) s = -1; if(-FUZZ<=t&&t<=FUZZ || t*s>0) { s = 0; break; } } if(s==0) error("improper clipping polygon"); for(i=0; ia + y*ei->b - ei->c; if(val>10*FUZZ) return(0); } return 1; } void realcut() { struct place g; double lat; if(cut != picut) /* punt on unusual cuts */ return; for(lat=window[0]; lat<=window[1]; lat+=grid[2]) { g.wlon.l = PI; sincos(&g.wlon); g.nlat.l = lat*RAD; sincos(&g.nlat); if(!inwindow(&g)) { break; } invert(&g); if(inlimits(&g)) { return; } } longlines = shortlines = LONGLINES; cut = nocut; /* not necessary; small eff. gain */ }