/* rmap - vector based global map generating program * Copyright (C) 2000 Reza Naima * * http://www.reza.net/rmap/ * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #include "rmap.h" #include "cities.h" #include "rgb.h" //The following subroutine is the only one borrowed from //the original mapping code -- strait forward bit flipping stuff //which was contributed by Joe Dellinger (no email for me to //contact him) // // If you have a better way of accomplishing this, please // Let me know as I'm sure this can't be supper efficient #ifdef WORDS_BIGENDIAN bitflip (BIT32 *x) { BIT32 y=0; y = 0; y |= 0xFF000000 & ((0x000000FF & *x) << 24); y |= 0x00FF0000 & ((0x0000FF00 & *x) << 8); y |= 0x0000FF00 & ((0x00FF0000 & *x) >> 8); y |= 0x000000FF & ((0xFF000000 & *x) >> 24); *x = y; } #endif /* WORDS_BIGENDIAN */ void lc(char *str) { // this assumes a null terminating string int i; for (i=0;str[i];i++) str[i] = tolower(str[i]); } // return a pointer to the color struct that // contains the rgb values for a color // or null if nothing is found that matches struct color* get_color (char* c) { int i=0; lc(c); while (colors[i].name != NULL) { if (!strcmp(colors[i].name,c)) return &colors[i]; i++; } return NULL; } void image_init (struct imageLayout *s, char *filename) { FILE *f; struct color *c; int i,j,pos=0; int ret, line_number=0; int cat, type; char string[256]; char color_name[100]; // create a page s->im = gdImageCreate(s->width, s->height); //reset some variables bzero(string,256); for (i=0; i< 5; i++) for (j=0; j<16; j++) s->colormap[i][j] = NOPRINT; //no print is default // read in the color config file and allocate colors f = fopen(filename, "r"); if (f == NULL) { fprintf(stderr,"Couldn't open %s (colormap file)\n", filename); exit(-2); } //now parse the file while ((ret = fread(string, 1, 255, f)) != 0) { line_number++; pos = (int) ftell(f) - ret; for (i=0; i<255; i++) { if (string[i] == '\n') { string[i] = '\0'; fseek(f,pos+i+1, SEEK_SET); break; } } for (j=0; j0) { j--; if (string[j] == ' ' || string [j] == '\t') { string[j] = '\0'; } else { break; } } if (strlen(string) == 0) continue; ret = sscanf(string, "%d %d %s", &cat, &type, color_name); if (ret != 3) { fprintf(stderr,"Unable to parse the string [%s] on line %d in %s\n", string,line_number,filename); continue; } if (!strcmp(color_name, "none")) { s->colormap[cat][type] = NOPRINT; } else { c = get_color(color_name); if (c == NULL) { fprintf(stderr,"The color %s was not found on line %d in %s\n", color_name,line_number,filename); continue; } if (cat > 4 || type > 15) fprintf(stderr,"Declaration value out of range (%d,%d) on line %d in %s\n", cat,type, line_number, filename); else s->colormap[cat][type] = gdImageColorAllocate(s->im, c->r, c->g, c->b); } } fclose(f); // setup the colors } void drawLine (struct imageLayout *s, vector a, vector b, int color_index) { int x1,x2,y1,y2; //lets try it witout perspective x1 = a.item[X] + s->width/2; y1 = a.item[Y] + s->height/2; x2 = b.item[X] + s->width/2; y2 = b.item[Y] + s->height/2; // draw the point gdImageLine(s->im, x1,y1, x2,y2, color_index); } void makePicture (struct imageLayout *s, char* filename) { FILE *f; /* Default is to make the picture interlaced */ gdImageInterlace(s->im, 1); f = fopen(filename, "wb"); if (f == NULL) { fprintf(stderr, "Unable to open output file %s\n", filename); return; } /* even though gd is cool, they're fucking' lame ** for removing gif support */ #ifdef GD_GIF gdImageGif(s->im, f); #else #ifdef GD_PNG gdImagePng(s->im, f); #else #ifdef GD_JPEG gdImageJpeg(s->im, f); #endif /* GD_JPEG */ #endif /* GD_PNG */ #endif /* GD_GIF */ fclose(f); } /* input is in degrees, output is the matrix */ static matrix rotate(double x, double y, double z) { matrix m; double p,t,r; r = CONV * x; //rho p = CONV * y; //psi t = CONV * z; //theta m.item[0][0] = cos(p)*cos(t); m.item[0][1] = cos(p)*sin(t); m.item[0][2] = -sin(p); m.item[1][0] = sin(r)*sin(p)*cos(t) - sin(t)*cos(r); m.item[1][1] = sin(r)*sin(p)*sin(t) + cos(r)*cos(t); m.item[1][2] = sin(r)*cos(p); m.item[2][0] = sin(p)*cos(r)*cos(t) + sin(r)*sin(t); m.item[2][1] = sin(p)*cos(r)*sin(t) - sin(r)*cos(t); m.item[2][2] = cos(r)*sin(p); return m; } static matrix rotateZ (double deg) { matrix m; double d; d = CONV * deg; m.item[0][0] = cos(d); m.item[0][1] = sin(d); m.item[0][2] = 0.; m.item[1][0] = -sin(d); m.item[1][1] = cos(d); m.item[1][2] = 0.; m.item[2][0] = 0.; m.item[2][1] = 0.; m.item[2][2] = 1.; return m; } static matrix rotateX (double deg) { matrix m; double d; d = CONV * deg; m.item[0][0] = 1.; m.item[0][1] = 0.; m.item[0][2] = 0.; m.item[1][0] = 0.; m.item[1][1] = cos(d); m.item[1][2] = -sin(d); m.item[2][0] = 0.; m.item[2][1] = sin(d); m.item[2][2] = cos(d); return m; } static matrix rotateY (double deg) { matrix m; double d; d = CONV * deg; m.item[0][0] = cos(d); m.item[0][1] = 0.; m.item[0][2] = sin(d); m.item[1][0] = 0.; m.item[1][1] = 1.; m.item[1][2] = 0.; m.item[2][0] = -sin(d); m.item[2][1] = 0.; m.item[2][2] = cos(d); return m; } static vector multiply23 (vector a, matrix b) { vector v; v.item[0] = a.item[0]*b.item[0][0] + a.item[1]*b.item[1][0] + a.item[2]*b.item[2][0]; v.item[1] = a.item[0]*b.item[0][1] + a.item[1]*b.item[1][1] + a.item[2]*b.item[2][1]; v.item[2] = a.item[0]*b.item[0][2] + a.item[1]*b.item[1][2] + a.item[2]*b.item[2][2]; return v; } static matrix multiply33 (matrix a, matrix b) { matrix m; 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]; 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]; 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]; 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]; 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]; 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]; 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]; 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]; 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]; return m; } static vector sphere2cart (spherePt s) { vector v; v.item[X] = s.item[RADIUS] * cos(s.item[LATITUDE] * CONV) * sin(-1*s.item[LONGITUDE] * CONV); v.item[Y] = s.item[RADIUS] * sin(s.item[LATITUDE] * CONV); v.item[Z] = s.item[RADIUS] * cos(s.item[LATITUDE] * CONV) * cos(-1*s.item[LONGITUDE] * CONV); return v; } static matrix scale (double x, double y, double z) { matrix m; bzero(&m, sizeof(m)); m.item[X][X] = x; m.item[Y][Y] = y; m.item[Z][Z] = z; return m; } static int segment_in_scene (struct imageLayout *s,struct segment_index *i, matrix rot) { spherePt max, min; vector pt1, pt2; int bit_number[9] = {0,1,2,0,3,0,0,0,4}; // first verify if we want this printed if (s->colormap[bit_number[i->category]][i->type] == NOPRINT) return 0; max.item[LONGITUDE] = i->maxlong/3600.; max.item[LATITUDE] = i->maxlat/3600.; max.item[RADIUS] = EARTH_RADIUS; min.item[LONGITUDE] = i->minlong/3600.; min.item[LATITUDE] = i->minlat/3600.; min.item[RADIUS] = EARTH_RADIUS; pt1 = multiply23(sphere2cart(max), rot); pt2 = multiply23(sphere2cart(min), rot); // check to see if it is z-clipped if (s->transparent == 0 && (pt1.item[Z] < 0 || pt2.item[Z] < 0)) { return 0; } if ( (abs(pt1.item[X]) > s->width/2 && abs(pt2.item[X]) > s->width/2) || (abs(pt1.item[Y]) > s->height/2 && abs(pt2.item[Y]) > s->height/2)) { return 0; } return 1; } void draw_circles( struct imageLayout *s, matrix rot) { int i,j; spherePt spt1,spt2; vector pt1, pt2; int inc=15; //draw line ever so many degrees spt1.item[RADIUS] = EARTH_RADIUS; spt2.item[RADIUS] = EARTH_RADIUS; for (i=0; i<360; i+=inc) { for (j=0; j<360; j+=5) { spt1.item[LONGITUDE] = i; spt1.item[LATITUDE] = j; spt2.item[LONGITUDE] = i; spt2.item[LATITUDE] = j+5; pt1 = multiply23(sphere2cart(spt1), rot); pt2 = multiply23(sphere2cart(spt2), rot); if (pt1.item[Z] > 0) drawLine(s, pt1, pt2, s->colormap[CONF][C_LAT_GRID]); } } for (i=0; i<360; i+=inc) { for (j=0; j<360; j+=5) { spt1.item[LONGITUDE] = j; spt1.item[LATITUDE] = i; spt2.item[LONGITUDE] = j+5; spt2.item[LATITUDE] = i; pt1 = multiply23(sphere2cart(spt1), rot); pt2 = multiply23(sphere2cart(spt2), rot); if (pt1.item[Z] > 0) drawLine(s, pt1, pt2, s->colormap[CONF][C_LONG_GRID]); } } } void addText (struct imageLayout *s, spherePt spt, matrix rot, char* text) { vector pt; int x,y; int text_x, text_y; int circle_size = 8; //determine the point, post transformation pt = multiply23(sphere2cart(spt), rot); if (pt.item[Z] < 0) return; x = pt.item[X] + s->width/2; y = pt.item[Y] + s->height/2; //verify that the point is on the map, return if not if (x > s->width || x < 0 || y > s->height || y < 0) return; // draw a circle around the point in question gdImageArc(s->im, x,y, circle_size, circle_size, 0,360, s->colormap[CONF][C_TEXT_CIRCLE]); // determine the text location relative to the circle text_x = (x > s->width/2) ? (x - strlen(text)*gdFontSmall->w - circle_size/2) : (x + circle_size/2); text_y = (y > s->height/2) ? (y - circle_size/2) : (y + gdFontSmall->h + circle_size/2); text_y = y; // add text gdImageString(s->im, gdFontSmall, text_x, text_y, text, s->colormap[CONF][C_TEXT]); } void generate(struct imageLayout *scene, struct options *o) { // misc variables spherePt s1,s2; matrix rot; int i,j; char* string; //filehandle int fh; //filenames struct header header_data; struct segment_index segment_index_data; struct segment_header segment_header_data; struct stroke stroke_data; //simple mapping array int bit_number[9] = {0,1,2,0,3,0,0,0,4}; // generate the proper rotation&scale matrix rot = multiply33 ( multiply33( multiply33( rotateZ(180), multiply33( rotateY(o->yrot), rotateX(o->xrot) ) ), scale(o->zoom,o->zoom,o->zoom) ), rotateZ(o->zrot) ); // setup the image image_init(scene, o->colorfile); if (o->gridlines) draw_circles(scene, rot); //Open the datafile and find graph the appropriate sectors fh = open(o->mapfile, O_RDONLY, 0); if (fh == -1) { fprintf(stderr,"Couldn't open input vector file %s.\n", o->mapfile); exit (-1); } // read the header read(fh, &header_data, sizeof(header_data)); #ifdef WORDS_BIGENDIAN bitflip( &(header_data.magic) ); bitflip( &(header_data.segment_index_address) ); bitflip( &(header_data.segment_index_count) ); #endif /* WORDS_BIGENDIAN */ if (header_data.magic != HEADER_MAGIC) { fprintf(stderr, "The magic was not valid for the input file.\n"); fprintf(stderr, "This could be an endian problem, or you\n"); fprintf(stderr, "could be pointing to the wrong file.\n"); fprintf(stderr, "(%s's magic = %X and it's supposed to be %X)\n", o->mapfile, header_data.magic, HEADER_MAGIC); exit(-2); } // itterate through all the segment_indexes to see which ones are interesting for (i=0; idesired_continents) && (segment_index_data.category & o->desired_categories) ) { //it has interesting information, let's look to see if the //data is in our viewport if (segment_in_scene(scene, &segment_index_data, rot)) { //iterate through the data and pass it to draw lseek(fh, segment_index_data.segment_address, SEEK_SET); read(fh, &segment_header_data, sizeof(segment_header_data)); #ifdef WORDS_BIGENDIAN bitflip( &(segment_header_data.orgx) ); bitflip( &(segment_header_data.orgy) ); bitflip( &(segment_header_data.nstrokes) ); #endif /* WORDS_BIGENDIAN */ s1.item[LONGITUDE] = (segment_header_data.orgx/3600.0); s1.item[LATITUDE] = (segment_header_data.orgy/3600.0); s1.item[RADIUS]=EARTH_RADIUS; s2.item[RADIUS]=EARTH_RADIUS; for (j=0; jcolormap[bit_number[segment_index_data.category]][segment_index_data.type]); s1.item[LONGITUDE] = s2.item[LONGITUDE]; s1.item[LATITUDE] = s2.item[LATITUDE]; } } } } // iterate through data file and add lables if (o->cities) { i = 0; s1.item[RADIUS]=EARTH_RADIUS; while (cities[i].name != NULL) { s1.item[LONGITUDE] = cities[i].longitude; s1.item[LATITUDE] = cities[i].latitude; addText(scene, s1, rot, cities[i].name); i++; } } makePicture(scene, o->outfile); }