1 /* rmap - vector based global map generating program
2  * Copyright (C) 2000 Reza Naima <reza@reza.net>
3  *
4  * http://www.reza.net/rmap/
5  *
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
20  */
21 
22 #include "rmap.h"
23 #include "cities.h"
24 #include "rgb.h"
25 
26 //The following subroutine is the only one borrowed from
27 //the original mapping code -- strait forward bit flipping stuff
28 //which was contributed by Joe Dellinger (no email for me to
29 //contact him)
30 //
31 // If you have a better way of accomplishing this, please
32 // Let me know as I'm sure this can't be supper efficient
33 
34 #ifdef WORDS_BIGENDIAN
bitflip(BIT32 * x)35 bitflip (BIT32 *x) {
36 	BIT32 y=0;
37 
38 	y = 0;
39 	y |= 0xFF000000 & ((0x000000FF & *x) << 24);
40 	y |= 0x00FF0000 & ((0x0000FF00 & *x) << 8);
41 	y |= 0x0000FF00 & ((0x00FF0000 & *x) >> 8);
42 	y |= 0x000000FF & ((0xFF000000 & *x) >> 24);
43 	*x = y;
44 }
45 #endif /* WORDS_BIGENDIAN */
46 
47 
lc(char * str)48 void lc(char *str) {
49 	// this assumes a null terminating string
50 	int i;
51 	for (i=0;str[i];i++)
52 		str[i] = tolower(str[i]);
53 
54 }
55 
56 // return a pointer to the color struct that
57 // contains the rgb values for a color
58 // or null if nothing is found that matches
get_color(char * c)59 struct color* get_color (char* c) {
60 	int i=0;
61 	lc(c);
62 
63 	while (colors[i].name != NULL) {
64 		if (!strcmp(colors[i].name,c))
65 			return &colors[i];
66 		i++;
67 	}
68 	return NULL;
69 }
70 
71 
image_init(struct imageLayout * s,char * filename)72 void image_init (struct imageLayout *s, char *filename) {
73 	FILE *f;
74 	struct color *c;
75 	int i,j,pos=0;
76 	int ret, line_number=0;
77 	int cat, type;
78 	char string[256];
79 	char color_name[100];
80 
81 
82 	// create a page
83 	s->im = gdImageCreate(s->width, s->height);
84 
85 	//reset some variables
86 
87 	bzero(string,256);
88 	for (i=0; i< 5; i++)
89 		for (j=0; j<16; j++)
90 			s->colormap[i][j] = NOPRINT; //no print is default
91 
92 	// read in the color config file and allocate colors
93 	f = fopen(filename, "r");
94 	if (f == NULL) {
95 		fprintf(stderr,"Couldn't open %s (colormap file)\n", filename);
96 		exit(-2);
97 	}
98 
99 	//now parse the file
100 	while ((ret = fread(string, 1, 255, f)) != 0) {
101 		line_number++;
102 		pos = (int) ftell(f) - ret;
103 		for (i=0; i<255; i++) {
104 			if (string[i] == '\n') {
105 				string[i] = '\0';
106 				fseek(f,pos+i+1, SEEK_SET);
107 				break;
108 			}
109 		}
110 		for (j=0; j<i; j++) {
111 			if (string[j] == '#')  {
112 				string[j] = '\0';
113 				break;
114 			}
115 		}
116 		while (j>0) {
117 			j--;
118 			if (string[j] == ' ' || string [j] == '\t') {
119 				string[j] = '\0';
120 			} else {
121 				break;
122 			}
123 		}
124 		if (strlen(string) == 0)
125 			continue;
126 
127 		ret = sscanf(string, "%d %d %s", &cat, &type, color_name);
128 		if (ret != 3) {
129 			fprintf(stderr,"Unable to parse the string [%s] on line %d in %s\n", string,line_number,filename);
130 			continue;
131 		}
132 		if (!strcmp(color_name, "none")) {
133 			s->colormap[cat][type] = NOPRINT;
134 		} else {
135 			c = get_color(color_name);
136 			if (c == NULL) {
137 				fprintf(stderr,"The color %s was not found on line %d in %s\n",
138 					color_name,line_number,filename);
139 				continue;
140 			}
141 			if (cat > 4 || type > 15)
142 				fprintf(stderr,"Declaration value out of range (%d,%d) on line %d in %s\n",
143 					cat,type, line_number, filename);
144 			else
145 			s->colormap[cat][type] = gdImageColorAllocate(s->im, c->r, c->g, c->b);
146 		}
147 	}
148 
149 	fclose(f);
150 
151 	// setup the colors
152 }
153 
154 
drawLine(struct imageLayout * s,vector a,vector b,int color_index)155 void drawLine (struct imageLayout *s, vector a, vector b, int color_index) {
156   	int x1,x2,y1,y2;
157 
158         //lets try it witout perspective
159         x1 = a.item[X] + s->width/2;
160         y1 = a.item[Y] + s->height/2;
161         x2 = b.item[X] + s->width/2;
162         y2 = b.item[Y] + s->height/2;
163 
164 
165         // draw the point
166         gdImageLine(s->im, x1,y1, x2,y2, color_index);
167 }
168 
169 
makePicture(struct imageLayout * s,char * filename)170 void makePicture (struct imageLayout *s, char* filename) {
171 	FILE *f;
172 
173 	/* Default is to make the picture interlaced */
174 	gdImageInterlace(s->im, 1);
175 
176 	f = fopen(filename, "wb");
177 	if (f  == NULL) {
178 		fprintf(stderr, "Unable to open output file %s\n", filename);
179 		return;
180 	}
181 
182 /* even though gd is cool, they're fucking' lame
183 ** for removing gif support */
184 #ifdef GD_GIF
185 	gdImageGif(s->im, f);
186 #else
187 #ifdef GD_PNG
188 	gdImagePng(s->im, f);
189 #else
190 #ifdef GD_JPEG
191 	gdImageJpeg(s->im, f);
192 #endif /* GD_JPEG */
193 #endif /* GD_PNG */
194 #endif /* GD_GIF */
195 
196 	fclose(f);
197 }
198 
199 /* input is in degrees, output is the matrix */
rotate(double x,double y,double z)200 static matrix rotate(double x, double y, double z) {
201 	matrix m;
202 	double p,t,r;
203 
204 	r = CONV * x; //rho
205 	p = CONV * y; //psi
206 	t = CONV * z; //theta
207 
208 	m.item[0][0] =  cos(p)*cos(t);
209 	m.item[0][1] =  cos(p)*sin(t);
210 	m.item[0][2] = -sin(p);
211 	m.item[1][0] =  sin(r)*sin(p)*cos(t) - sin(t)*cos(r);
212 	m.item[1][1] =  sin(r)*sin(p)*sin(t) + cos(r)*cos(t);
213 	m.item[1][2] =  sin(r)*cos(p);
214 	m.item[2][0] =  sin(p)*cos(r)*cos(t) + sin(r)*sin(t);
215 	m.item[2][1] =  sin(p)*cos(r)*sin(t) - sin(r)*cos(t);
216 	m.item[2][2] =  cos(r)*sin(p);
217 
218 	return m;
219 }
220 
rotateZ(double deg)221 static matrix rotateZ (double deg) {
222 	matrix m;
223 	double d;
224 
225 	d = CONV * deg;
226 
227 	m.item[0][0] =  cos(d);
228 	m.item[0][1] =  sin(d);
229 	m.item[0][2] =  0.;
230 	m.item[1][0] = -sin(d);
231 	m.item[1][1] =  cos(d);
232 	m.item[1][2] =  0.;
233 	m.item[2][0] =  0.;
234 	m.item[2][1] =  0.;
235 	m.item[2][2] =  1.;
236 
237 	return m;
238 }
rotateX(double deg)239 static matrix rotateX (double deg) {
240 	matrix m;
241 	double d;
242 
243 	d = CONV * deg;
244 
245 	m.item[0][0] =  1.;
246 	m.item[0][1] =  0.;
247 	m.item[0][2] =  0.;
248 	m.item[1][0] =  0.;
249 	m.item[1][1] =  cos(d);
250 	m.item[1][2] = -sin(d);
251 	m.item[2][0] =  0.;
252 	m.item[2][1] =  sin(d);
253 	m.item[2][2] =  cos(d);
254 
255 	return m;
256 }
rotateY(double deg)257 static matrix rotateY (double deg) {
258 	matrix m;
259 	double d;
260 
261 	d = CONV * deg;
262 
263 	m.item[0][0] =  cos(d);
264 	m.item[0][1] =  0.;
265 	m.item[0][2] =  sin(d);
266 	m.item[1][0] =  0.;
267 	m.item[1][1] =  1.;
268 	m.item[1][2] =  0.;
269 	m.item[2][0] = -sin(d);
270 	m.item[2][1] =  0.;
271 	m.item[2][2] =  cos(d);
272 
273 	return m;
274 }
275 
multiply23(vector a,matrix b)276 static vector multiply23 (vector a, matrix b) {
277 	vector v;
278 
279 	v.item[0] = a.item[0]*b.item[0][0] + a.item[1]*b.item[1][0] + a.item[2]*b.item[2][0];
280 	v.item[1] = a.item[0]*b.item[0][1] + a.item[1]*b.item[1][1] + a.item[2]*b.item[2][1];
281 	v.item[2] = a.item[0]*b.item[0][2] + a.item[1]*b.item[1][2] + a.item[2]*b.item[2][2];
282 
283 	return v;
284 }
285 
multiply33(matrix a,matrix b)286 static matrix multiply33 (matrix a, matrix b) {
287 	matrix m;
288 
289 	m.item[0][0] = a.item[0][0]*b.item[0][0] + a.item[0][1]*b.item[1][0] + a.item[0][2]*b.item[2][0];
290 	m.item[0][1] = a.item[0][0]*b.item[0][1] + a.item[0][1]*b.item[1][1] + a.item[0][2]*b.item[2][1];
291 	m.item[0][2] = a.item[0][0]*b.item[0][2] + a.item[0][1]*b.item[1][2] + a.item[0][2]*b.item[2][2];
292 	m.item[1][0] = a.item[1][0]*b.item[0][0] + a.item[1][1]*b.item[1][0] + a.item[1][2]*b.item[2][0];
293 	m.item[1][1] = a.item[1][0]*b.item[0][1] + a.item[1][1]*b.item[1][1] + a.item[1][2]*b.item[2][1];
294 	m.item[1][2] = a.item[1][0]*b.item[0][2] + a.item[1][1]*b.item[1][2] + a.item[1][2]*b.item[2][2];
295 	m.item[2][0] = a.item[2][0]*b.item[0][0] + a.item[2][1]*b.item[1][0] + a.item[2][2]*b.item[2][0];
296 	m.item[2][1] = a.item[2][0]*b.item[0][1] + a.item[2][1]*b.item[1][1] + a.item[2][2]*b.item[2][1];
297 	m.item[2][2] = a.item[2][0]*b.item[0][2] + a.item[2][1]*b.item[1][2] + a.item[2][2]*b.item[2][2];
298 
299 	return m;
300 }
301 
302 
sphere2cart(spherePt s)303 static vector sphere2cart (spherePt s) {
304         vector v;
305 
306         v.item[X] = s.item[RADIUS] * cos(s.item[LATITUDE] * CONV) * sin(-1*s.item[LONGITUDE] * CONV);
307         v.item[Y] = s.item[RADIUS] * sin(s.item[LATITUDE] * CONV);
308         v.item[Z] = s.item[RADIUS] * cos(s.item[LATITUDE] * CONV) * cos(-1*s.item[LONGITUDE] * CONV);
309         return v;
310 }
311 
scale(double x,double y,double z)312 static matrix scale (double x, double y, double z) {
313 	matrix m;
314 	bzero(&m, sizeof(m));
315 
316 	m.item[X][X] = x;
317 	m.item[Y][Y] = y;
318 	m.item[Z][Z] = z;
319 
320 	return m;
321 }
322 
323 
324 
segment_in_scene(struct imageLayout * s,struct segment_index * i,matrix rot)325 static int segment_in_scene (struct imageLayout *s,struct segment_index *i, matrix rot) {
326 	spherePt max, min;
327 	vector pt1, pt2;
328 	int bit_number[9] = {0,1,2,0,3,0,0,0,4};
329 
330 
331 	// first verify if we want this printed
332 	if (s->colormap[bit_number[i->category]][i->type] == NOPRINT)
333 		return 0;
334 
335 	max.item[LONGITUDE] = i->maxlong/3600.;
336 	max.item[LATITUDE] = i->maxlat/3600.;
337 	max.item[RADIUS] = EARTH_RADIUS;
338 
339 	min.item[LONGITUDE] = i->minlong/3600.;
340 	min.item[LATITUDE] = i->minlat/3600.;
341 	min.item[RADIUS] = EARTH_RADIUS;
342 
343 	pt1 = multiply23(sphere2cart(max), rot);
344 	pt2 = multiply23(sphere2cart(min), rot);
345 
346 	// check to see if it is z-clipped
347 	if (s->transparent == 0 && (pt1.item[Z] < 0 || pt2.item[Z] < 0)) {
348 		return 0;
349 	}
350 
351 	if (  (abs(pt1.item[X]) > s->width/2 && abs(pt2.item[X]) > s->width/2)
352 	   || (abs(pt1.item[Y]) > s->height/2 && abs(pt2.item[Y]) > s->height/2)) {
353 		return 0;
354 	}
355 
356 	return  1;
357 }
358 
draw_circles(struct imageLayout * s,matrix rot)359 void draw_circles( struct imageLayout *s, matrix rot) {
360 	int i,j;
361 	spherePt spt1,spt2;
362 	vector pt1, pt2;
363 	int inc=15; //draw line ever so many degrees
364 
365 	spt1.item[RADIUS] = EARTH_RADIUS;
366 	spt2.item[RADIUS] = EARTH_RADIUS;
367 
368 	for (i=0; i<360; i+=inc) {
369 		for (j=0; j<360; j+=5) {
370 			spt1.item[LONGITUDE] = i;
371 			spt1.item[LATITUDE]  = j;
372 			spt2.item[LONGITUDE] = i;
373 			spt2.item[LATITUDE]  = j+5;
374 			pt1 = multiply23(sphere2cart(spt1), rot);
375 			pt2 = multiply23(sphere2cart(spt2), rot);
376 			if (pt1.item[Z] > 0)
377 				drawLine(s, pt1, pt2, s->colormap[CONF][C_LAT_GRID]);
378 		}
379 	}
380 	for (i=0; i<360; i+=inc) {
381 		for (j=0; j<360; j+=5) {
382 			spt1.item[LONGITUDE] = j;
383 			spt1.item[LATITUDE]  = i;
384 			spt2.item[LONGITUDE] = j+5;
385 			spt2.item[LATITUDE]  = i;
386 			pt1 = multiply23(sphere2cart(spt1), rot);
387 			pt2 = multiply23(sphere2cart(spt2), rot);
388 			if (pt1.item[Z] > 0)
389 				drawLine(s, pt1, pt2, s->colormap[CONF][C_LONG_GRID]);
390 		}
391 	}
392 }
393 
394 
addText(struct imageLayout * s,spherePt spt,matrix rot,char * text)395 void addText (struct imageLayout *s, spherePt spt, matrix rot, char* text) {
396 	vector pt;
397 	int x,y;
398 	int text_x, text_y;
399 	int circle_size = 8;
400 
401 	//determine the point, post transformation
402 	pt = multiply23(sphere2cart(spt), rot);
403 
404 	if (pt.item[Z] < 0)
405 		return;
406 
407 	x = pt.item[X] + s->width/2;
408 	y = pt.item[Y] + s->height/2;
409 
410 	//verify that the point is on the map, return if not
411 	if (x > s->width || x < 0 || y > s->height || y < 0)
412 		return;
413 
414 	// draw a circle around the point in question
415 	gdImageArc(s->im, x,y, circle_size, circle_size, 0,360, s->colormap[CONF][C_TEXT_CIRCLE]);
416 
417 	// determine the text location relative to the circle
418 	text_x = (x > s->width/2) ? (x - strlen(text)*gdFontSmall->w - circle_size/2) :
419 				  (x + circle_size/2);
420 	text_y = (y > s->height/2) ? (y - circle_size/2) :
421 				   (y + gdFontSmall->h + circle_size/2);
422 	text_y = y;
423 	// add text
424 	gdImageString(s->im, gdFontSmall, text_x, text_y, text, s->colormap[CONF][C_TEXT]);
425 }
426 
generate(struct imageLayout * scene,struct options * o)427 void generate(struct imageLayout *scene, struct options *o) {
428 	// misc variables
429 	spherePt s1,s2;
430 	matrix rot;
431 	int i,j;
432 	char* string;
433 	//filehandle
434 	int fh;
435 	//filenames
436 	struct header header_data;
437 	struct segment_index segment_index_data;
438 	struct segment_header segment_header_data;
439 	struct stroke stroke_data;
440 	//simple mapping array
441 	int bit_number[9] = {0,1,2,0,3,0,0,0,4};
442 
443 
444 	// generate the proper rotation&scale matrix
445 	rot = multiply33 (
446 		multiply33(
447 			multiply33(
448 				rotateZ(180), multiply33(
449 					rotateY(o->yrot),
450 					rotateX(o->xrot)
451 					)
452 				),
453 				scale(o->zoom,o->zoom,o->zoom)
454 		),
455 		rotateZ(o->zrot)
456 		);
457 
458 	// setup the image
459 	image_init(scene, o->colorfile);
460 	if (o->gridlines)
461 		draw_circles(scene, rot);
462 
463 	//Open the datafile and find graph the appropriate sectors
464 	fh = open(o->mapfile, O_RDONLY, 0);
465 	if (fh == -1) {
466 		fprintf(stderr,"Couldn't open input vector file %s.\n", o->mapfile);
467 		exit (-1);
468 	}
469 	// read the header
470 	read(fh, &header_data, sizeof(header_data));
471 #ifdef WORDS_BIGENDIAN
472 	bitflip( &(header_data.magic) );
473 	bitflip( &(header_data.segment_index_address) );
474 	bitflip( &(header_data.segment_index_count) );
475 #endif /* WORDS_BIGENDIAN */
476 	if (header_data.magic != HEADER_MAGIC) {
477 		fprintf(stderr, "The magic was not valid for the input file.\n");
478 		fprintf(stderr, "This could be an endian problem, or you\n");
479 		fprintf(stderr, "could be pointing to the wrong file.\n");
480 		fprintf(stderr, "(%s's magic = %X and it's supposed to be %X)\n",
481 			o->mapfile, header_data.magic, HEADER_MAGIC);
482 		exit(-2);
483 	}
484 	// itterate through all the segment_indexes to see which ones are interesting
485 	for (i=0; i<header_data.segment_index_count; i++) {
486 		lseek(fh, sizeof(header_data) + sizeof(segment_index_data)*i, SEEK_SET);
487 		read(fh, &segment_index_data, sizeof(segment_index_data));
488 #ifdef WORDS_BIGENDIAN
489 		bitflip( &(segment_index_data.maxlat) );
490 		bitflip( &(segment_index_data.minlat) );
491 		bitflip( &(segment_index_data.maxlong) );
492 		bitflip( &(segment_index_data.minlong) );
493 		bitflip( &(segment_index_data.segment_address) );
494 		bitflip( &(segment_index_data.continent) );
495 		bitflip( &(segment_index_data.category) );
496 		bitflip( &(segment_index_data.type) );
497 #endif /* WORDS_BIGENDIAN */
498 
499 		if ( (segment_index_data.continent & o->desired_continents)
500 		 && (segment_index_data.category & o->desired_categories) ) {
501 			//it has interesting information, let's look to see if the
502 			//data is in our viewport
503 			if (segment_in_scene(scene, &segment_index_data, rot)) {
504 				//iterate through the data and pass it to draw
505 				lseek(fh, segment_index_data.segment_address, SEEK_SET);
506 				read(fh, &segment_header_data, sizeof(segment_header_data));
507 #ifdef WORDS_BIGENDIAN
508 				bitflip( &(segment_header_data.orgx) );
509 				bitflip( &(segment_header_data.orgy) );
510 				bitflip( &(segment_header_data.nstrokes) );
511 #endif /* WORDS_BIGENDIAN */
512 				s1.item[LONGITUDE] = (segment_header_data.orgx/3600.0);
513 				s1.item[LATITUDE]  = (segment_header_data.orgy/3600.0);
514 				s1.item[RADIUS]=EARTH_RADIUS;
515 				s2.item[RADIUS]=EARTH_RADIUS;
516 				for (j=0; j<segment_header_data.nstrokes; j++) {
517 					vector pt1, pt2;
518 
519 					// itterate and draw all the appropriate points
520 					read(fh, &stroke_data, sizeof(stroke_data));
521 #ifdef WORDS_BIGENDIAN
522 					bitflip( &(stroke_data.dx) );
523 					bitflip( &(stroke_data.dy) );
524 #endif /* WORDS_BIGENDIAN */
525 					s2.item[LONGITUDE] = s1.item[LONGITUDE]+(stroke_data.dx/3600.0);
526 					s2.item[LATITUDE]  = s1.item[LATITUDE] +(stroke_data.dy/3600.0);
527 
528 					pt1 = multiply23(sphere2cart(s1), rot);
529 					pt2 = multiply23(sphere2cart(s2), rot);
530 
531 					drawLine(scene, pt1, pt2,
532 						scene->colormap[bit_number[segment_index_data.category]][segment_index_data.type]);
533 
534 					s1.item[LONGITUDE] = s2.item[LONGITUDE];
535 					s1.item[LATITUDE]  = s2.item[LATITUDE];
536 				}
537 			}
538 		}
539 	}
540 
541 	// iterate through data file and add lables
542 	if (o->cities) {
543 		i = 0;
544 		s1.item[RADIUS]=EARTH_RADIUS;
545 		while (cities[i].name != NULL) {
546 			s1.item[LONGITUDE] = cities[i].longitude;
547 			s1.item[LATITUDE] = cities[i].latitude;
548 			addText(scene, s1, rot, cities[i].name);
549 			i++;
550 		}
551 	}
552 	makePicture(scene, o->outfile);
553 }
554 
555