/* rmap - vector based global map generating program
* Copyright (C) 2000 Reza Naima <reza@reza.net>
*
* 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; j<i; j++) {
if (string[j] == '#') {
string[j] = '\0';
break;
}
}
while (j>0) {
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; i<header_data.segment_index_count; i++) {
lseek(fh, sizeof(header_data) + sizeof(segment_index_data)*i, SEEK_SET);
read(fh, &segment_index_data, sizeof(segment_index_data));
#ifdef WORDS_BIGENDIAN
bitflip( &(segment_index_data.maxlat) );
bitflip( &(segment_index_data.minlat) );
bitflip( &(segment_index_data.maxlong) );
bitflip( &(segment_index_data.minlong) );
bitflip( &(segment_index_data.segment_address) );
bitflip( &(segment_index_data.continent) );
bitflip( &(segment_index_data.category) );
bitflip( &(segment_index_data.type) );
#endif /* WORDS_BIGENDIAN */
if ( (segment_index_data.continent & o->desired_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; j<segment_header_data.nstrokes; j++) {
vector pt1, pt2;
// itterate and draw all the appropriate points
read(fh, &stroke_data, sizeof(stroke_data));
#ifdef WORDS_BIGENDIAN
bitflip( &(stroke_data.dx) );
bitflip( &(stroke_data.dy) );
#endif /* WORDS_BIGENDIAN */
s2.item[LONGITUDE] = s1.item[LONGITUDE]+(stroke_data.dx/3600.0);
s2.item[LATITUDE] = s1.item[LATITUDE] +(stroke_data.dy/3600.0);
pt1 = multiply23(sphere2cart(s1), rot);
pt2 = multiply23(sphere2cart(s2), rot);
drawLine(scene, pt1, pt2,
scene->colormap[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);
}
syntax highlighted by Code2HTML, v. 0.9.1