1 #include <u.h>
2 #include <libc.h>
3 #include <stdio.h>
4 #include "map.h"
5 #include "iplot.h"
6 
7 #define NVERT 20	/* max number of vertices in a -v polygon */
8 #define HALFWIDTH 8192	/* output scaled to fit in -HALFWIDTH,HALFWIDTH */
9 #define LONGLINES (HALFWIDTH*4)	/* permissible segment lengths */
10 #define SHORTLINES (HALFWIDTH/8)
11 #define SCALERATIO 10	/* of abs to rel data (see map(5)) */
12 #define RESOL 2.	/* coarsest resolution for tracing grid (degrees) */
13 #define TWO_THRD 0.66666666666666667
14 
15 int normproj(double, double, double *, double *);
16 int posproj(double, double, double *, double *);
17 int picut(struct place *, struct place *, double *);
18 double reduce(double);
19 short getshort(FILE *);
20 char *mapindex(char *);
21 proj projection;
22 
23 
24 static char *mapdir = "#9/map";	/* default map directory */
25 struct file {
26 	char *name;
27 	char *color;
28 	char *style;
29 };
30 static struct file dfltfile = {
31 	"world", BLACK, SOLID	/* default map */
32 };
33 static struct file *file = &dfltfile;	/* list of map files */
34 static int nfile = 1;			/* length of list */
35 static char *currcolor = BLACK;		/* current color */
36 static char *gridcolor = BLACK;
37 static char *bordcolor = BLACK;
38 
39 extern struct index index[];
40 int halfwidth = HALFWIDTH;
41 
42 static int (*cut)(struct place *, struct place *, double *);
43 static int (*limb)(double*, double*, double);
44 static void dolimb(void);
45 static int onlimb;
46 static int poles;
47 static double orientation[3] = { 90., 0., 0. };	/* -o option */
48 static int oriented;	/* nonzero if -o option occurred */
49 static int upright;		/* 1 if orientation[0]==90, -1 if -90, else 0*/
50 static int delta = 1;	/* -d setting */
51 static double limits[4] = {	/* -l parameters */
52 	-90., 90., -180., 180.
53 };
54 static double klimits[4] = {	/* -k parameters */
55 	-90., 90., -180., 180.
56 };
57 static int limcase;
58 static double rlimits[4];	/* limits expressed in radians */
59 static double lolat, hilat, lolon, hilon;
60 static double window[4] = {	/* option -w */
61 	-90., 90., -180., 180.
62 };
63 static int windowed;	/* nozero if option -w */
64 static struct vert { double x, y; } v[NVERT+2];	/*clipping polygon*/
65 static struct edge { double a, b, c; } e[NVERT]; /* coeffs for linear inequality */
66 static int nvert;	/* number of vertices in clipping polygon */
67 
68 static double rwindow[4];	/* window, expressed in radians */
69 static double params[2];		/* projection params */
70 /* bounds on output values before scaling; found by coarse survey */
71 static double xmin = 100.;
72 static double xmax = -100.;
73 static double ymin = 100.;
74 static double ymax = -100.;
75 static double xcent, ycent;
76 static double xoff, yoff;
77 double xrange, yrange;
78 static int left = -HALFWIDTH;
79 static int right = HALFWIDTH;
80 static int bottom = -HALFWIDTH;
81 static int top = HALFWIDTH;
82 static int longlines = SHORTLINES; /* drop longer segments */
83 static int shortlines = SHORTLINES;
84 static int bflag = 1;	/* 0 for option -b */
85 static int s1flag = 0;	/* 1 for option -s1 */
86 static int s2flag = 0;	/* 1 for option -s2 */
87 static int rflag = 0;	/* 1 for option -r */
88 static int kflag = 0;	/* 1 if option -k occurred */
89 static int xflag = 0;	/* 1 for option -x */
90        int vflag = 1;	/* -1 if option -v occurred */
91 static double position[3];	/* option -p */
92 static double center[3] = {0., 0., 0.};	/* option -c */
93 static struct coord crot;		/* option -c */
94 static double grid[3] = { 10., 10., RESOL };	/* option -g */
95 static double dlat, dlon;	/* resolution for tracing grid in lat and lon */
96 static double scaling;	/* to compute final integer output */
97 static struct file *track;	/* options -t and -u */
98 static int ntrack;		/* number of tracks present */
99 static char *symbolfile;	/* option -y */
100 
101 void	clamp(double *px, double v);
102 void	clipinit(void);
103 double	diddle(struct place *, double, double);
104 double	diddle(struct place *, double, double);
105 void	dobounds(double, double, double, double, int);
106 void	dogrid(double, double, double, double);
107 int	duple(struct place *, double);
108 #define fmax map_fmax
109 #define fmin map_fmin
110 double	fmax(double, double);
111 double	fmin(double, double);
112 void	getdata(char *);
113 int	gridpt(double, double, int);
114 int	inpoly(double, double);
115 int	inwindow(struct place *);
116 void	pathnames(void);
117 int	pnorm(double);
118 void	radbds(double *w, double *rw);
119 void	revlon(struct place *, double);
120 void	satellite(struct file *);
121 int	seeable(double, double);
122 void	windlim(void);
123 void	realcut(void);
124 
125 int
option(char * s)126 option(char *s)
127 {
128 
129 	if(s[0]=='-' && (s[1]<'0'||s[1]>'9'))
130 		return(s[1]!='.'&&s[1]!=0);
131 	else
132 		return(0);
133 }
134 
135 void
conv(int k,struct coord * g)136 conv(int k, struct coord *g)
137 {
138 	g->l = (0.0001/SCALERATIO)*k;
139 	sincos(g);
140 }
141 
142 int
main(int argc,char * argv[])143 main(int argc, char *argv[])
144 {
145 	int i,k;
146 	char *s, *t, *style;
147 	double x, y;
148 	double lat, lon;
149 	double *wlim;
150 	double dd;
151 	if(sizeof(short)!=2)
152 		abort();	/* getshort() won't work */
153 	mapdir = unsharp(mapdir);
154 	s = getenv("MAP");
155 	if(s)
156 		file[0].name = s;
157 	s = getenv("MAPDIR");
158 	if(s)
159 		mapdir = s;
160 	if(argc<=1)
161 		error("usage: map projection params options");
162 	for(k=0;index[k].name;k++) {
163 		s = index[k].name;
164 		t = argv[1];
165 		while(*s == *t){
166 			if(*s==0) goto found;
167 			s++;
168 			t++;
169 		}
170 	}
171 	fprintf(stderr,"projections:\n");
172 	for(i=0;index[i].name;i++)  {
173 		fprintf(stderr,"%s",index[i].name);
174 		for(k=0; k<index[i].npar; k++)
175 			fprintf(stderr," p%d", k);
176 		fprintf(stderr,"\n");
177 	}
178 	exits("error");
179 found:
180 	argv += 2;
181 	argc -= 2;
182 	cut = index[k].cut;
183 	limb = index[k].limb;
184 	poles = index[k].poles;
185 	for(i=0;i<index[k].npar;i++) {
186 		if(i>=argc||option(argv[i])) {
187 			fprintf(stderr,"%s needs %d params\n",index[k].name,index[k].npar);
188 			exits("error");
189 		}
190 		params[i] = atof(argv[i]);
191 	}
192 	argv += i;
193 	argc -= i;
194 	while(argc>0&&option(argv[0])) {
195 		argc--;
196 		argv++;
197 		switch(argv[-1][1]) {
198 		case 'm':
199 			if(file == &dfltfile) {
200 				file = 0;
201 				nfile = 0;
202 			}
203 			while(argc && !option(*argv)) {
204 				file = realloc(file,(nfile+1)*sizeof(*file));
205 				file[nfile].name = *argv;
206 				file[nfile].color = currcolor;
207 				file[nfile].style = SOLID;
208 				nfile++;
209 				argv++;
210 				argc--;
211 			}
212 			break;
213 		case 'b':
214 			bflag = 0;
215 			for(nvert=0;nvert<NVERT&&argc>=2;nvert++) {
216 				if(option(*argv))
217 					break;
218 				v[nvert].x = atof(*argv++);
219 				argc--;
220 				if(option(*argv))
221 					break;
222 				v[nvert].y = atof(*argv++);
223 				argc--;
224 			}
225 			if(nvert>=NVERT)
226 				error("too many clipping vertices");
227 			break;
228 		case 'g':
229 			gridcolor = currcolor;
230 			for(i=0;i<3&&argc>i&&!option(argv[i]);i++)
231 				grid[i] = atof(argv[i]);
232 			switch(i) {
233 			case 0:
234 				grid[0] = grid[1] = 0.;
235 				break;
236 			case 1:
237 				grid[1] = grid[0];
238 			}
239 			argc -= i;
240 			argv += i;
241 			break;
242 		case 't':
243 			style = SOLID;
244 			goto casetu;
245 		case 'u':
246 			style = DOTDASH;
247 		casetu:
248 			while(argc && !option(*argv)) {
249 				track = realloc(track,(ntrack+1)*sizeof(*track));
250 				track[ntrack].name = *argv;
251 				track[ntrack].color = currcolor;
252 				track[ntrack].style = style;
253 				ntrack++;
254 				argv++;
255 				argc--;
256 			}
257 			break;
258 		case 'r':
259 			rflag++;
260 			break;
261 		case 's':
262 			switch(argv[-1][2]) {
263 			case '1':
264 				s1flag++;
265 				break;
266 			case 0:		/* compatibility */
267 			case '2':
268 				s2flag++;
269 			}
270 			break;
271 		case 'o':
272 			for(i=0;i<3&&i<argc&&!option(argv[i]);i++)
273 				orientation[i] = atof(argv[i]);
274 			oriented++;
275 			argv += i;
276 			argc -= i;
277 			break;
278 		case 'l':
279 			bordcolor = currcolor;
280 			for(i=0;i<argc&&i<4&&!option(argv[i]);i++)
281 				limits[i] = atof(argv[i]);
282 			argv += i;
283 			argc -= i;
284 			break;
285 		case 'k':
286 			kflag++;
287 			for(i=0;i<argc&&i<4&&!option(argv[i]);i++)
288 				klimits[i] = atof(argv[i]);
289 			argv += i;
290 			argc -= i;
291 			break;
292 		case 'd':
293 			if(argc>0&&!option(argv[0])) {
294 				delta = atoi(argv[0]);
295 				argv++;
296 				argc--;
297 			}
298 			break;
299 		case 'w':
300 			bordcolor = currcolor;
301 			windowed++;
302 			for(i=0;i<argc&&i<4&&!option(argv[i]);i++)
303 				window[i] = atof(argv[i]);
304 			argv += i;
305 			argc -= i;
306 			break;
307 		case 'c':
308 			for(i=0;i<3&&argc>i&&!option(argv[i]);i++)
309 				center[i] = atof(argv[i]);
310 			argc -= i;
311 			argv += i;
312 			break;
313 		case 'p':
314 			for(i=0;i<3&&argc>i&&!option(argv[i]);i++)
315 				position[i] = atof(argv[i]);
316 			argc -= i;
317 			argv += i;
318 			if(i!=3||position[2]<=0)
319 				error("incomplete positioning");
320 			break;
321 		case 'y':
322 			if(argc>0&&!option(argv[0])) {
323 				symbolfile = argv[0];
324 				argc--;
325 				argv++;
326 			}
327 			break;
328 		case 'v':
329 			if(index[k].limb == 0)
330 				error("-v does not apply here");
331 			vflag = -1;
332 			break;
333 		case 'x':
334 			xflag = 1;
335 			break;
336 		case 'C':
337 			if(argc && !option(*argv)) {
338 				currcolor = colorcode(*argv);
339 				argc--;
340 				argv++;
341 			}
342 			break;
343 		}
344 	}
345 	if(argc>0)
346 		error("error in arguments");
347 	pathnames();
348 	clamp(&limits[0],-90.);
349 	clamp(&limits[1],90.);
350 	clamp(&klimits[0],-90.);
351 	clamp(&klimits[1],90.);
352 	clamp(&window[0],-90.);
353 	clamp(&window[1],90.);
354 	radbds(limits,rlimits);
355 	limcase = limits[2]<-180.?0:
356 		  limits[3]>180.?2:
357 		  1;
358 	if(
359 		window[0]>=window[1]||
360 		window[2]>=window[3]||
361 		window[0]>90.||
362 		window[1]<-90.||
363 		window[2]>180.||
364 		window[3]<-180.)
365 		error("unreasonable window");
366 	windlim();
367 	radbds(window,rwindow);
368 	upright = orientation[0]==90? 1: orientation[0]==-90? -1: 0;
369 	if(index[k].spheroid && !upright)
370 		error("can't tilt the spheroid");
371 	if(limits[2]>limits[3])
372 		limits[3] += 360;
373 	if(!oriented)
374 		orientation[2] = (limits[2]+limits[3])/2;
375 	orient(orientation[0],orientation[1],orientation[2]);
376 	projection = (*index[k].prog)(params[0],params[1]);
377 	if(projection == 0)
378 		error("unreasonable projection parameters");
379 	clipinit();
380 	grid[0] = fabs(grid[0]);
381 	grid[1] = fabs(grid[1]);
382 	if(!kflag)
383 		for(i=0;i<4;i++)
384 			klimits[i] = limits[i];
385 	if(klimits[2]>klimits[3])
386 		klimits[3] += 360;
387 	lolat = limits[0];
388 	hilat = limits[1];
389 	lolon = limits[2];
390 	hilon = limits[3];
391 	if(lolon>=hilon||lolat>=hilat||lolat<-90.||hilat>90.)
392 		error("unreasonable limits");
393 	wlim = kflag? klimits: window;
394 	dlat = fmin(hilat-lolat,wlim[1]-wlim[0])/16;
395 	dlon = fmin(hilon-lolon,wlim[3]-wlim[2])/32;
396 	dd = fmax(dlat,dlon);
397 	while(grid[2]>fmin(dlat,dlon)/2)
398 		grid[2] /= 2;
399 	realcut();
400 	if(nvert<=0) {
401 		for(lat=klimits[0];lat<klimits[1]+dd-FUZZ;lat+=dd) {
402 			if(lat>klimits[1])
403 				lat = klimits[1];
404 			for(lon=klimits[2];lon<klimits[3]+dd-FUZZ;lon+=dd) {
405 				i = (kflag?posproj:normproj)
406 					(lat,lon+(lon<klimits[3]?FUZZ:-FUZZ),
407 				   &x,&y);
408 				if(i*vflag <= 0)
409 					continue;
410 				if(x<xmin) xmin = x;
411 				if(x>xmax) xmax = x;
412 				if(y<ymin) ymin = y;
413 				if(y>ymax) ymax = y;
414 			}
415 		}
416 	} else {
417 		for(i=0; i<nvert; i++) {
418 			x = v[i].x;
419 			y = v[i].y;
420 			if(x<xmin) xmin = x;
421 			if(x>xmax) xmax = x;
422 			if(y<ymin) ymin = y;
423 			if(y>ymax) ymax = y;
424 		}
425 	}
426 	xrange = xmax - xmin;
427 	yrange = ymax - ymin;
428 	if(xrange<=0||yrange<=0)
429 		error("map seems to be empty");
430 	scaling = 2;	/*plotting area from -1 to 1*/
431 	if(position[2]!=0) {
432 		if(posproj(position[0]-.5,position[1],&xcent,&ycent)==0||
433 		   posproj(position[0]+.5,position[1],&x,&y)==0)
434 			error("unreasonable position");
435 		scaling /= (position[2]*hypot(x-xcent,y-ycent));
436 		if(posproj(position[0],position[1],&xcent,&ycent)==0)
437 			error("unreasonable position");
438 	} else {
439 		scaling /= (xrange>yrange?xrange:yrange);
440 		xcent = (xmin+xmax)/2;
441 		ycent = (ymin+ymax)/2;
442 	}
443 	xoff = center[0]/scaling;
444 	yoff = center[1]/scaling;
445 	crot.l = center[2]*RAD;
446 	sincos(&crot);
447 	scaling *= HALFWIDTH*0.9;
448 	if(symbolfile)
449 		getsyms(symbolfile);
450 	if(!s2flag) {
451 		openpl();
452 		erase();
453 	}
454 	range(left,bottom,right,top);
455 	comment("grid","");
456 	colorx(gridcolor);
457 	pen(DOTTED);
458 	if(grid[0]>0.)
459 		for(lat=ceil(lolat/grid[0])*grid[0];
460 		    lat<=hilat;lat+=grid[0])
461 			dogrid(lat,lat,lolon,hilon);
462 	if(grid[1]>0.)
463 		for(lon=ceil(lolon/grid[1])*grid[1];
464 		    lon<=hilon;lon+=grid[1])
465 			dogrid(lolat,hilat,lon,lon);
466 	comment("border","");
467 	colorx(bordcolor);
468 	pen(SOLID);
469 	if(bflag) {
470 		dolimb();
471 		dobounds(lolat,hilat,lolon,hilon,0);
472 		dobounds(window[0],window[1],window[2],window[3],1);
473 	}
474 	lolat = floor(limits[0]/10)*10;
475 	hilat = ceil(limits[1]/10)*10;
476 	lolon = floor(limits[2]/10)*10;
477 	hilon = ceil(limits[3]/10)*10;
478 	if(lolon>hilon)
479 		hilon += 360.;
480 	/*do tracks first so as not to lose the standard input*/
481 	for(i=0;i<ntrack;i++) {
482 		longlines = LONGLINES;
483 		satellite(&track[i]);
484 		longlines = shortlines;
485 	}
486 	for(i=0;i<nfile;i++) {
487 		comment("mapfile",file[i].name);
488 		colorx(file[i].color);
489 		pen(file[i].style);
490 		getdata(file[i].name);
491 	}
492 	move(right,bottom);
493 	if(!s1flag)
494 		closepl();
495 	return 0;
496 }
497 
498 /* Out of perverseness (really to recover from a dubious,
499    but documented, convention) the returns from projection
500    functions (-1 unplottable, 0 wrong sheet, 1 good) are
501    recoded into -1 wrong sheet, 0 unplottable, 1 good. */
502 
503 int
fixproj(struct place * g,double * x,double * y)504 fixproj(struct place *g, double *x, double *y)
505 {
506 	int i = (*projection)(g,x,y);
507 	return i<0? 0: i==0? -1: 1;
508 }
509 
510 int
normproj(double lat,double lon,double * x,double * y)511 normproj(double lat, double lon, double *x, double *y)
512 {
513 	int i;
514 	struct place geog;
515 	latlon(lat,lon,&geog);
516 /*
517 	printp(&geog);
518 */
519 	normalize(&geog);
520 	if(!inwindow(&geog))
521 		return(-1);
522 	i = fixproj(&geog,x,y);
523 	if(rflag)
524 		*x = -*x;
525 /*
526 	printp(&geog);
527 	fprintf(stderr,"%d %.3f %.3f\n",i,*x,*y);
528 */
529 	return(i);
530 }
531 
532 int
posproj(double lat,double lon,double * x,double * y)533 posproj(double lat, double lon, double *x, double *y)
534 {
535 	int i;
536 	struct place geog;
537 	latlon(lat,lon,&geog);
538 	normalize(&geog);
539 	i = fixproj(&geog,x,y);
540 	if(rflag)
541 		*x = -*x;
542 	return(i);
543 }
544 
545 int
inwindow(struct place * geog)546 inwindow(struct place *geog)
547 {
548 	if(geog->nlat.l<rwindow[0]-FUZZ||
549 	   geog->nlat.l>rwindow[1]+FUZZ||
550 	   geog->wlon.l<rwindow[2]-FUZZ||
551 	   geog->wlon.l>rwindow[3]+FUZZ)
552 		return(0);
553 	else return(1);
554 }
555 
556 int
inlimits(struct place * g)557 inlimits(struct place *g)
558 {
559 	if(rlimits[0]-FUZZ>g->nlat.l||
560 	   rlimits[1]+FUZZ<g->nlat.l)
561 		return(0);
562 	switch(limcase) {
563 	case 0:
564 		if(rlimits[2]+TWOPI-FUZZ>g->wlon.l&&
565 		   rlimits[3]+FUZZ<g->wlon.l)
566 			return(0);
567 		break;
568 	case 1:
569 		if(rlimits[2]-FUZZ>g->wlon.l||
570 		   rlimits[3]+FUZZ<g->wlon.l)
571 			return(0);
572 		break;
573 	case 2:
574 		if(rlimits[2]>g->wlon.l&&
575 		   rlimits[3]-TWOPI+FUZZ<g->wlon.l)
576 			return(0);
577 		break;
578 	}
579 	return(1);
580 }
581 
582 
583 long patch[18][36];
584 
585 void
getdata(char * mapfile)586 getdata(char *mapfile)
587 {
588 	char *indexfile;
589 	int kx,ky,c;
590 	int k;
591 	long b;
592 	long *p;
593 	int ip, jp;
594 	int n;
595 	struct place g;
596 	int i, j;
597 	double lat, lon;
598 	int conn;
599 	FILE *ifile, *xfile;
600 
601 	indexfile = mapindex(mapfile);
602 	xfile = fopen(indexfile,"r");
603 	if(xfile==NULL)
604 		filerror("can't find map index", indexfile);
605 	free(indexfile);
606 	for(i=0,p=patch[0];i<18*36;i++,p++)
607 		*p = 1;
608 	while(!feof(xfile) && fscanf(xfile,"%d%d%ld",&i,&j,&b)==3)
609 		patch[i+9][j+18] = b;
610 	fclose(xfile);
611 	ifile = fopen(mapfile,"r");
612 	if(ifile==NULL)
613 		filerror("can't find map data", mapfile);
614 	for(lat=lolat;lat<hilat;lat+=10.)
615 		for(lon=lolon;lon<hilon;lon+=10.) {
616 			if(!seeable(lat,lon))
617 				continue;
618 			i = pnorm(lat);
619 			j = pnorm(lon);
620 			if((b=patch[i+9][j+18])&1)
621 				continue;
622 			fseek(ifile,b,0);
623 			while((ip=getc(ifile))>=0&&(jp=getc(ifile))>=0){
624 				if(ip!=(i&0377)||jp!=(j&0377))
625 					break;
626 				n = getshort(ifile);
627 				conn = 0;
628 				if(n > 0) {	/* absolute coordinates */
629 					kx = ky = 0;	/* set */
630 					for(k=0;k<n;k++){
631 						kx = SCALERATIO*getshort(ifile);
632 						ky = SCALERATIO*getshort(ifile);
633 						if (((k%delta) != 0) && (k != (n-1)))
634 							continue;
635 						conv(kx,&g.nlat);
636 						conv(ky,&g.wlon);
637 						conn = plotpt(&g,conn);
638 					}
639 				} else {	/* differential, scaled by SCALERATI0 */
640 					n = -n;
641 					kx = SCALERATIO*getshort(ifile);
642 					ky = SCALERATIO*getshort(ifile);
643 					for(k=0; k<n; k++) {
644 						c = getc(ifile);
645 						if(c&0200) c|= ~0177;
646 						kx += c;
647 						c = getc(ifile);
648 						if(c&0200) c|= ~0177;
649 						ky += c;
650 						if(k%delta!=0&&k!=n-1)
651 							continue;
652 						conv(kx,&g.nlat);
653 						conv(ky,&g.wlon);
654 						conn = plotpt(&g,conn);
655 					}
656 				}
657 				if(k==1) {
658 					conv(kx,&g.nlat);
659 					conv(ky,&g.wlon);
660 					plotpt(&g,conn);
661 				}
662 			}
663 		}
664 	fclose(ifile);
665 }
666 
667 int
seeable(double lat0,double lon0)668 seeable(double lat0, double lon0)
669 {
670 	double x, y;
671 	double lat, lon;
672 	for(lat=lat0;lat<=lat0+10;lat+=2*grid[2])
673 		for(lon=lon0;lon<=lon0+10;lon+=2*grid[2])
674 			if(normproj(lat,lon,&x,&y)*vflag>0)
675 				return(1);
676 	return(0);
677 }
678 
679 void
satellite(struct file * t)680 satellite(struct file *t)
681 {
682 	char sym[50];
683 	char lbl[50];
684 	double scale;
685 	int conn;
686 	double lat,lon;
687 	struct place place;
688 	static FILE *ifile;
689 
690 	if(ifile == nil)
691 		ifile = stdin;
692 	if(t->name[0]!='-'||t->name[1]!=0) {
693 		fclose(ifile);
694 		if((ifile=fopen(t->name,"r"))==NULL)
695 			filerror("can't find track", t->name);
696 	}
697 	comment("track",t->name);
698 	colorx(t->color);
699 	pen(t->style);
700 	for(;;) {
701 		conn = 0;
702 		while(!feof(ifile) && fscanf(ifile,"%lf%lf",&lat,&lon)==2){
703 			latlon(lat,lon,&place);
704 			if(fscanf(ifile,"%1s",lbl) == 1) {
705 				if(strchr("+-.0123456789",*lbl)==0)
706 					break;
707 				ungetc(*lbl,ifile);
708 			}
709 			conn = plotpt(&place,conn);
710 		}
711 		if(feof(ifile))
712 			return;
713 		fscanf(ifile,"%[^\n]",lbl+1);
714 		switch(*lbl) {
715 		case '"':
716 			if(plotpt(&place,conn))
717 				text(lbl+1);
718 			break;
719 		case ':':
720 		case '!':
721 			if(sscanf(lbl+1,"%s %lf",sym,&scale) <= 1)
722 				scale = 1;
723 			if(plotpt(&place,conn?conn:-1)) {
724 				int r = *lbl=='!'?0:rflag?-1:1;
725 				pen(SOLID);
726 				if(putsym(&place,sym,scale,r) == 0)
727 					text(lbl);
728 				pen(t->style);
729 			}
730 			break;
731 		default:
732 			if(plotpt(&place,conn))
733 				text(lbl);
734 			break;
735 		}
736 	}
737 }
738 
739 int
pnorm(double x)740 pnorm(double x)
741 {
742 	int i;
743 	i = x/10.;
744 	i %= 36;
745 	if(i>=18) return(i-36);
746 	if(i<-18) return(i+36);
747 	return(i);
748 }
749 
750 void
error(char * s)751 error(char *s)
752 {
753 	fprintf(stderr,"map: \r\n%s\n",s);
754 	exits("error");
755 }
756 
757 void
filerror(char * s,char * f)758 filerror(char *s, char *f)
759 {
760 	fprintf(stderr,"\r\n%s %s\n",s,f);
761 	exits("error");
762 }
763 
764 char *
mapindex(char * s)765 mapindex(char *s)
766 {
767 	char *t = malloc(strlen(s)+3);
768 	strcpy(t,s);
769 	strcat(t,".x");
770 	return t;
771 }
772 
773 #define NOPT 32767
774 static int ox = NOPT;
775 static int oy = NOPT;
776 
777 int
cpoint(int xi,int yi,int conn)778 cpoint(int xi, int yi, int conn)
779 {
780 	int dx = abs(ox-xi);
781 	int dy = abs(oy-yi);
782 	if(!xflag && (xi<left||xi>=right || yi<bottom||yi>=top)) {
783 		ox = oy = NOPT;
784 		return 0;
785 	}
786 	if(conn == -1)		/* isolated plotting symbol */
787 		{}
788 	else if(!conn)
789 		point(xi,yi);
790 	else {
791 		if(dx+dy>longlines) {
792 			ox = oy = NOPT;	/* don't leap across cuts */
793 			return 0;
794 		}
795 		if(dx || dy)
796 			vec(xi,yi);
797 	}
798 	ox = xi, oy = yi;
799 	return dx+dy<=2? 2: 1;	/* 2=very near; see dogrid */
800 }
801 
802 
803 struct place oldg;
804 
805 int
plotpt(struct place * g,int conn)806 plotpt(struct place *g, int conn)
807 {
808 	int kx,ky;
809 	int ret;
810 	double cutlon;
811 	if(!inlimits(g)) {
812 		return(0);
813 }
814 	normalize(g);
815 	if(!inwindow(g)) {
816 		return(0);
817 }
818 	switch((*cut)(g,&oldg,&cutlon)) {
819 	case 2:
820 		if(conn) {
821 			ret = duple(g,cutlon)|duple(g,cutlon);
822 			oldg = *g;
823 			return(ret);
824 		}
825 	case 0:
826 		conn = 0;
827 	default:	/* prevent diags about bad return value */
828 	case 1:
829 		oldg = *g;
830 		ret = doproj(g,&kx,&ky);
831 		if(ret==0 || !onlimb && ret*vflag<=0)
832 			return(0);
833 		ret = cpoint(kx,ky,conn);
834 		return ret;
835 	}
836 }
837 
838 int
doproj(struct place * g,int * kx,int * ky)839 doproj(struct place *g, int *kx, int *ky)
840 {
841 	int i;
842 	double x,y,x1,y1;
843 /*fprintf(stderr,"dopr1 %f %f \n",g->nlat.l,g->wlon.l);*/
844 	i = fixproj(g,&x,&y);
845 	if(i == 0)
846 		return(0);
847 	if(rflag)
848 		x = -x;
849 /*fprintf(stderr,"dopr2 %f %f\n",x,y);*/
850 	if(!inpoly(x,y)) {
851 		return 0;
852 }
853 	x1 = x - xcent;
854 	y1 = y - ycent;
855 	x = (x1*crot.c - y1*crot.s + xoff)*scaling;
856 	y = (x1*crot.s + y1*crot.c + yoff)*scaling;
857 	*kx = x + (x>0?.5:-.5);
858 	*ky = y + (y>0?.5:-.5);
859 	return(i);
860 }
861 
862 int
duple(struct place * g,double cutlon)863 duple(struct place *g, double cutlon)
864 {
865 	int kx,ky;
866 	int okx,oky;
867 	struct place ig;
868 	revlon(g,cutlon);
869 	revlon(&oldg,cutlon);
870 	ig = *g;
871 	invert(&ig);
872 	if(!inlimits(&ig))
873 		return(0);
874 	if(doproj(g,&kx,&ky)*vflag<=0 ||
875 	   doproj(&oldg,&okx,&oky)*vflag<=0)
876 		return(0);
877 	cpoint(okx,oky,0);
878 	cpoint(kx,ky,1);
879 	return(1);
880 }
881 
882 void
revlon(struct place * g,double cutlon)883 revlon(struct place *g, double cutlon)
884 {
885 	g->wlon.l = reduce(cutlon-reduce(g->wlon.l-cutlon));
886 	sincos(&g->wlon);
887 }
888 
889 
890 /*	recognize problems of cuts
891  *	move a point across cut to side of its predecessor
892  *	if its very close to the cut
893  *	return(0) if cut interrupts the line
894  *	return(1) if line is to be drawn normally
895  *	return(2) if line is so close to cut as to
896  *	be properly drawn on both sheets
897 */
898 
899 int
picut(struct place * g,struct place * og,double * cutlon)900 picut(struct place *g, struct place *og, double *cutlon)
901 {
902 	*cutlon = PI;
903 	return(ckcut(g,og,PI));
904 }
905 
906 int
nocut(struct place * g,struct place * og,double * cutlon)907 nocut(struct place *g, struct place *og, double *cutlon)
908 {
909 	USED(g);
910 	USED(og);
911 	USED(cutlon);
912 /*
913 #pragma	ref g
914 #pragma	ref og
915 #pragma	ref cutlon
916 */
917 	return(1);
918 }
919 
920 int
ckcut(struct place * g1,struct place * g2,double lon)921 ckcut(struct place *g1, struct place *g2, double lon)
922 {
923 	double d1, d2;
924 	double f1, f2;
925 	int kx,ky;
926 	d1 = reduce(g1->wlon.l -lon);
927 	d2 = reduce(g2->wlon.l -lon);
928 	if((f1=fabs(d1))<FUZZ)
929 		d1 = diddle(g1,lon,d2);
930 	if((f2=fabs(d2))<FUZZ) {
931 		d2 = diddle(g2,lon,d1);
932 		if(doproj(g2,&kx,&ky)*vflag>0)
933 			cpoint(kx,ky,0);
934 	}
935 	if(f1<FUZZ&&f2<FUZZ)
936 		return(2);
937 	if(f1>PI*TWO_THRD||f2>PI*TWO_THRD)
938 		return(1);
939 	return(d1*d2>=0);
940 }
941 
942 double
diddle(struct place * g,double lon,double d)943 diddle(struct place *g, double lon, double d)
944 {
945 	double d1;
946 	d1 = FUZZ/2;
947 	if(d<0)
948 		d1 = -d1;
949 	g->wlon.l = reduce(lon+d1);
950 	sincos(&g->wlon);
951 	return(d1);
952 }
953 
954 double
reduce(double lon)955 reduce(double lon)
956 {
957 	if(lon>PI)
958 		lon -= 2*PI;
959 	else if(lon<-PI)
960 		lon += 2*PI;
961 	return(lon);
962 }
963 
964 
965 double tetrapt = 35.26438968;	/* atan(1/sqrt(2)) */
966 
967 void
dogrid(double lat0,double lat1,double lon0,double lon1)968 dogrid(double lat0, double lat1, double lon0, double lon1)
969 {
970 	double slat,slon,tlat,tlon;
971 	register int conn, oconn;
972 	slat = tlat = slon = tlon = 0;
973 	if(lat1>lat0)
974 		slat = tlat = fmin(grid[2],dlat);
975 	else
976 		slon = tlon = fmin(grid[2],dlon);;
977 	conn = oconn = 0;
978 	while(lat0<=lat1&&lon0<=lon1) {
979 		conn = gridpt(lat0,lon0,conn);
980 		if(projection==Xguyou&&slat>0) {
981 			if(lat0<-45&&lat0+slat>-45)
982 				conn = gridpt(-45.,lon0,conn);
983 			else if(lat0<45&&lat0+slat>45)
984 				conn = gridpt(45.,lon0,conn);
985 		} else if(projection==Xtetra&&slat>0) {
986 			if(lat0<-tetrapt&&lat0+slat>-tetrapt) {
987 				gridpt(-tetrapt-.001,lon0,conn);
988 				conn = gridpt(-tetrapt+.001,lon0,0);
989 			}
990 			else if(lat0<tetrapt&&lat0+slat>tetrapt) {
991 				gridpt(tetrapt-.001,lon0,conn);
992 				conn = gridpt(tetrapt+.001,lon0,0);
993 			}
994 		}
995 		if(conn==0 && oconn!=0) {
996 			if(slat+slon>.05) {
997 				lat0 -= slat;	/* steps too big */
998 				lon0 -= slon;	/* or near bdry */
999 				slat /= 2;
1000 				slon /= 2;
1001 				conn = oconn = gridpt(lat0,lon0,conn);
1002 			} else
1003 				oconn = 0;
1004 		} else {
1005 			if(conn==2) {
1006 				slat = tlat;
1007 				slon = tlon;
1008 				conn = 1;
1009 			}
1010 			oconn = conn;
1011 	 	}
1012 		lat0 += slat;
1013 		lon0 += slon;
1014 	}
1015 	gridpt(lat1,lon1,conn);
1016 }
1017 
1018 static int gridinv;		/* nonzero when doing window bounds */
1019 
1020 int
gridpt(double lat,double lon,int conn)1021 gridpt(double lat, double lon, int conn)
1022 {
1023 	struct place g;
1024 /*fprintf(stderr,"%f %f\n",lat,lon);*/
1025 	latlon(lat,lon,&g);
1026 	if(gridinv)
1027 		invert(&g);
1028 	return(plotpt(&g,conn));
1029 }
1030 
1031 /* win=0 ordinary grid lines, win=1 window lines */
1032 
1033 void
dobounds(double lolat,double hilat,double lolon,double hilon,int win)1034 dobounds(double lolat, double hilat, double lolon, double hilon, int win)
1035 {
1036 	gridinv = win;
1037 	if(lolat>-90 || win && (poles&1)!=0)
1038 		dogrid(lolat+FUZZ,lolat+FUZZ,lolon,hilon);
1039 	if(hilat<90 || win && (poles&2)!=0)
1040 		dogrid(hilat-FUZZ,hilat-FUZZ,lolon,hilon);
1041 	if(hilon-lolon<360 || win && cut==picut) {
1042 		dogrid(lolat,hilat,lolon+FUZZ,lolon+FUZZ);
1043 		dogrid(lolat,hilat,hilon-FUZZ,hilon-FUZZ);
1044 	}
1045 	gridinv = 0;
1046 }
1047 
1048 static void
dolimb(void)1049 dolimb(void)
1050 {
1051 	double lat, lon;
1052 	double res = fmin(dlat, dlon)/4;
1053 	int conn = 0;
1054 	int newconn;
1055 	if(limb == 0)
1056 		return;
1057 	onlimb = gridinv = 1;
1058 	for(;;) {
1059 		newconn = (*limb)(&lat, &lon, res);
1060 		if(newconn == -1)
1061 			break;
1062 		conn = gridpt(lat, lon, conn*newconn);
1063 	}
1064 	onlimb = gridinv = 0;
1065 }
1066 
1067 
1068 void
radbds(double * w,double * rw)1069 radbds(double *w, double *rw)
1070 {
1071 	int i;
1072 	for(i=0;i<4;i++)
1073 		rw[i] = w[i]*RAD;
1074 	rw[0] -= FUZZ;
1075 	rw[1] += FUZZ;
1076 	rw[2] -= FUZZ;
1077 	rw[3] += FUZZ;
1078 }
1079 
1080 void
windlim(void)1081 windlim(void)
1082 {
1083 	double center = orientation[0];
1084 	double colat;
1085 	if(center>90)
1086 		center = 180 - center;
1087 	if(center<-90)
1088 		center = -180 - center;
1089 	if(fabs(center)>90)
1090 		error("unreasonable orientation");
1091 	colat = 90 - window[0];
1092 	if(center-colat>limits[0])
1093 		limits[0] = center - colat;
1094 	if(center+colat<limits[1])
1095 		limits[1] = center + colat;
1096 }
1097 
1098 
1099 short
getshort(FILE * f)1100 getshort(FILE *f)
1101 {
1102 	int c, r;
1103 	c = getc(f);
1104 	r = (c | getc(f)<<8);
1105 	if (r&0x8000)
1106 		r |= ~0xFFFF;	/* in case short > 16 bits */
1107 	return r;
1108 }
1109 
1110 double
fmin(double x,double y)1111 fmin(double x, double y)
1112 {
1113 	return(x<y?x:y);
1114 }
1115 
1116 double
fmax(double x,double y)1117 fmax(double x, double y)
1118 {
1119 	return(x>y?x:y);
1120 }
1121 
1122 void
clamp(double * px,double v)1123 clamp(double *px, double v)
1124 {
1125 	*px = (v<0?fmax:fmin)(*px,v);
1126 }
1127 
1128 void
pathnames(void)1129 pathnames(void)
1130 {
1131 	int i;
1132 	char *t, *indexfile, *name;
1133 	FILE *f, *fx;
1134 	for(i=0; i<nfile; i++) {
1135 		name = file[i].name;
1136 		if(*name=='/')
1137 			continue;
1138 		indexfile = mapindex(name);
1139 			/* ansi equiv of unix access() call */
1140 		f = fopen(name, "r");
1141 		fx = fopen(indexfile, "r");
1142 		if(f) fclose(f);
1143 		if(fx) fclose(fx);
1144 		free(indexfile);
1145 		if(f && fx)
1146 			continue;
1147 		t = malloc(strlen(name)+strlen(mapdir)+2);
1148 		strcpy(t,mapdir);
1149 		strcat(t,"/");
1150 		strcat(t,name);
1151 		file[i].name = t;
1152 	}
1153 }
1154 
1155 void
clipinit(void)1156 clipinit(void)
1157 {
1158 	int i;
1159 	double s,t;
1160 	if(nvert<=0)
1161 		return;
1162 	for(i=0; i<nvert; i++) {	/*convert latlon to xy*/
1163 		if(normproj(v[i].x,v[i].y,&v[i].x,&v[i].y)==0)
1164 			error("invisible clipping vertex");
1165 	}
1166 	if(nvert==2) {			/*rectangle with diag specified*/
1167 		nvert = 4;
1168 		v[2] = v[1];
1169 		v[1].x=v[0].x, v[1].y=v[2].y, v[3].x=v[2].x, v[3].y=v[0].y;
1170 	}
1171 	v[nvert] = v[0];
1172 	v[nvert+1] = v[1];
1173 	s = 0;
1174 	for(i=1; i<=nvert; i++) {	/*test for convexity*/
1175 		t = (v[i-1].x-v[i].x)*(v[i+1].y-v[i].y) -
1176 		    (v[i-1].y-v[i].y)*(v[i+1].x-v[i].x);
1177 		if(t<-FUZZ && s>=0) s = 1;
1178 		if(t>FUZZ && s<=0) s = -1;
1179 		if(-FUZZ<=t&&t<=FUZZ || t*s>0) {
1180 			s = 0;
1181 			break;
1182 		}
1183 	}
1184 	if(s==0)
1185 		error("improper clipping polygon");
1186 	for(i=0; i<nvert; i++) {	/*edge equation ax+by=c*/
1187 		e[i].a = s*(v[i+1].y - v[i].y);
1188 		e[i].b = s*(v[i].x - v[i+1].x);
1189 		e[i].c = s*(v[i].x*v[i+1].y - v[i].y*v[i+1].x);
1190 	}
1191 }
1192 
1193 int
inpoly(double x,double y)1194 inpoly(double x, double y)
1195 {
1196 	int i;
1197 	for(i=0; i<nvert; i++) {
1198 		register struct edge *ei = &e[i];
1199 		double val = x*ei->a + y*ei->b - ei->c;
1200 		if(val>10*FUZZ)
1201 			return(0);
1202 	}
1203 	return 1;
1204 }
1205 
1206 void
realcut(void)1207 realcut(void)
1208 {
1209 	struct place g;
1210 	double lat;
1211 
1212 	if(cut != picut)	/* punt on unusual cuts */
1213 		return;
1214 	for(lat=window[0]; lat<=window[1]; lat+=grid[2]) {
1215 		g.wlon.l = PI;
1216 		sincos(&g.wlon);
1217 		g.nlat.l = lat*RAD;
1218 		sincos(&g.nlat);
1219 		if(!inwindow(&g)) {
1220 			break;
1221 }
1222 		invert(&g);
1223 		if(inlimits(&g)) {
1224 			return;
1225 }
1226 	}
1227 	longlines = shortlines = LONGLINES;
1228 	cut = nocut;		/* not necessary; small eff. gain */
1229 }
1230