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