/*------------------------------------------------------------------------- MapBrowser.cxx Implementation of a map display for maps generated with MAP - FlightGear mapping utility Written by Per Liedman, started February 2000. Copyright (C) 2000 Per Liedman, liedman@home.se 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., 675 Mass Ave, Cambridge, MA 02139, USA. ---------------------------------------------------------------------------*/ #include #include #include "MapBrowser.hxx" #include "Geodesy.hxx" #include "LoadPng.hxx" #include "LoadJpg.hxx" const char* MapBrowser::TXF_FONT_NAME = "/Fonts/helvetica_medium.txf"; MapBrowser::MapBrowser(GLfloat left, GLfloat top, GLfloat size, int features, char *fg_root, bool texturedFonts) : view_left(left), view_top(top), view_size(size), clat(0.0f), clon(0.0f), scle(100000), pathl(0), features(features), texturedFonts(texturedFonts) { mpath[0] = 0; zoom = view_size / scle; if (texturedFonts) { font_name = new char[strlen(fg_root) + strlen(TXF_FONT_NAME) + 1]; strcpy(font_name, fg_root); strcat(font_name, TXF_FONT_NAME); } else { font_name = NULL; } output = new OutputGL( NULL, (int)size, false, texturedFonts, font_name ); output->setShade(false); // setup overlays overlay = new Overlays( fg_root, zoom, size ); overlay->setOutput( output ); overlay->setFeatures(features); projection = new Projection; overlay->setProjection(projection); textured = true; track = NULL; } MapBrowser::~MapBrowser() { delete font_name; delete output; delete overlay; delete projection; } void MapBrowser::setProjectionByID(int id) { projection->setSystem(id); update(); } void MapBrowser::setLocation( float lat, float lon ) { clat = lat; clon = lon; overlay->setLocation(lat, lon); update(); } void MapBrowser::setScale( float scale ) { scle = scale; zoom = view_size / scle; overlay->setScale( zoom ); update(); } void MapBrowser::setSize( GLfloat size ) { view_size = size; zoom = view_size / scle; delete output; output = new OutputGL(NULL, (int)size, false, texturedFonts, font_name); overlay->setOutput( output ); overlay->setScale( zoom ); output->setShade(false); update(); } void MapBrowser::setMapPath( char *path ) { strcpy( mpath, path ); pathl = strlen( mpath ); } void MapBrowser::changeResolution(char *path) { MapTile *tile; list::iterator i = tiles.end(), itmp; i--; while (tiles.begin() != tiles.end()) { tile =*i; glDeleteTextures( 1, &tile->texture_handle ); itmp = i--; tiles.erase( itmp ); tiletable.erase(tile->c); delete tile->texbuf; delete tile; } setMapPath(path); update(); } /* void MapBrowser::setFGRoot( char *fg_root ) { overlay->setFGRoot( fg_root ); } */ void MapBrowser::setFeatures( int features ) { this->features = features; overlay->setFeatures(features); } void MapBrowser::toggleFeaturesAllNavaids() { features = features ^ ( Overlays::OVERLAY_NAVAIDS | Overlays::OVERLAY_NAVAIDS_VOR | Overlays::OVERLAY_NAVAIDS_NDB | Overlays::OVERLAY_NAVAIDS_ILS | Overlays::OVERLAY_NAVAIDS_FIX ); overlay->setFeatures(features); } void MapBrowser::setTextured( bool textured ) { this->textured = textured; } void MapBrowser::setFlightTrack( FlightTrack *track ) { this->track = track; overlay->setFlightTrack(track); } void MapBrowser::loadDb() { overlay->load_airports(); overlay->load_new_navaids(); overlay->load_new_fixes(); } void MapBrowser::draw() { glDisable(GL_BLEND); glColor4f( 1.0f, 1.0f, 1.0f, 1.0f ); if (textured) { glEnable( GL_TEXTURE_2D ); GLfloat tilesize = earth_radius_lat(clat) * SG_DEGREES_TO_RADIANS * zoom; for (list::iterator i = tiles.begin(); i != tiles.end(); i++) { MapTile *tile = *i; if ( tile->texbuf != NULL ) { GLfloat dxs = tile->w.rs*zoom / tilesize / 2.0f; GLfloat dxn = tile->w.rn*zoom / tilesize / 2.0f; glBindTexture(GL_TEXTURE_2D, tile->texture_handle); glBegin(GL_QUADS); glTexCoord2f(0.5f-dxs, 1.0f); glVertex2f(tile->xsw, tile->ysw ); glTexCoord2f(0.5f+dxs, 1.0f); glVertex2f(tile->xso, tile->yso ); glTexCoord2f(0.5f+dxn, 0.0f); glVertex2f(tile->xno, tile->yno ); glTexCoord2f(0.5f-dxn, 0.0f); glVertex2f(tile->xnw, tile->ynw ); glEnd(); } } glDisable( GL_TEXTURE_2D ); } glEnable(GL_BLEND); overlay->drawOverlays(); } void MapBrowser::update() { float dlat, dlon; if (!textured) return; lat_ab( scle/2, scle/2, clat, clon, &dlat, &dlon ); dlat -= clat; dlon -= clon; if (dlon > SG_PI/2) dlon = SG_PI/2; int sgnlat = (clat < 0.0f) ? 1 : 0; int sgnlon = (clon < 0.0f) ? 1 : 0; // calculate minimum and maximum latitude/longitude of displayed tiles int min_lat = (int)( (clat - dlat) * SG_RADIANS_TO_DEGREES ) - sgnlat; int max_lat = (int)( (clat + dlat) * SG_RADIANS_TO_DEGREES ) - sgnlat; int min_lon = (int)( (clon - dlon) * SG_RADIANS_TO_DEGREES ) - sgnlon; int max_lon = (int)( (clon + dlon) * SG_RADIANS_TO_DEGREES ) - sgnlon; if (min_lat < -90) min_lat = -90; if (max_lat > 90) max_lat = 90; int num_lat = (max_lat - min_lat) + 1, num_lon = (max_lon - min_lon) + 1; for (list::iterator it = tiles.begin(); it != tiles.end(); it++) { MapTile *tile = *it; // remove old tiles if (tile->c.lat < min_lat - CACHE_LIMIT || tile->c.lat > max_lat + CACHE_LIMIT || tile->c.lon < min_lon - CACHE_LIMIT || tile->c.lon > max_lon + CACHE_LIMIT) { list::iterator tmp = it; tmp++; if ( tile->texbuf != NULL ) { glDeleteTextures( 1, &tile->texture_handle ); } tiles.erase( it ); tiletable.erase( tile->c ); delete tile->texbuf; delete tile; it = tmp; } else { // update tiles position sgVec3 xyr; projection->ab_lat( rad((float) tile->c.lat), rad((float) tile->c.lon), clat, clon, xyr ); scale( xyr[0], xyr[1], &tile->xsw, &tile->ysw ); projection->ab_lat( rad(tile->c.lat+1.0f), rad((float) tile->c.lon), clat, clon, xyr ); scale( xyr[0], xyr[1], &tile->xnw, &tile->ynw ); projection->ab_lat( rad(tile->c.lat+1.0f), rad(tile->c.lon+1.0f), clat, clon, xyr ); scale( xyr[0], xyr[1], &tile->xno, &tile->yno ); projection->ab_lat( rad((float) tile->c.lat), rad(tile->c.lon+1.0f), clat, clon, xyr ); scale( xyr[0], xyr[1], &tile->xso, &tile->yso ); } } // load needed tiles for (int i = 0; i < num_lat; i++) { for (int j = 0; j < num_lon; j++) { int wid, hei; float tlat; Coord c; c.lat = min_lat + i; c.lon = min_lon + j; TileTable::iterator t = tiletable.find(c); if ( t == tiletable.end() ) { // check if the tile has been loaded // Load a new tile MapTile *nt = new MapTile; nt->c.lat = c.lat; nt->c.lon = c.lon; tlat = (float) c.lat * SG_DEGREES_TO_RADIANS; nt->w.rs = earth_radius_lat(tlat) * cos(tlat) * SG_DEGREES_TO_RADIANS; tlat = (float) (c.lat + 1) * SG_DEGREES_TO_RADIANS; nt->w.rn = earth_radius_lat(tlat) * cos(tlat) * SG_DEGREES_TO_RADIANS; sprintf( mpath+pathl, "%c%03d%c%02d.png", (c.lon < 0)?'w':'e', abs(c.lon), (c.lat < 0)?'s':'n', abs(c.lat) ); //printf("Loading tile %s...\n", mpath); if ( (nt->texbuf = (GLubyte*)loadPng( mpath, &wid, &hei )) == NULL ) { sprintf( mpath+pathl, "%c%03d%c%02d.jpg", (c.lon < 0)?'w':'e', abs(c.lon), (c.lat < 0)?'s':'n', abs(c.lat) ); nt->texbuf = (GLubyte*)loadJpg( mpath, &wid, &hei ); } if ( nt->texbuf != NULL ) { glPixelStorei(GL_UNPACK_ALIGNMENT, 3); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); // glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); glGenTextures( 1, &nt->texture_handle ); glBindTexture( GL_TEXTURE_2D, nt->texture_handle ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP ); glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, wid, hei, 0, GL_RGB, GL_UNSIGNED_BYTE, nt->texbuf ); } else { // printf("Tile %s couldn't be loaded\n",mpath); // texbuf is NULL; texture_handle is undefined. } sgVec3 xyr; projection->ab_lat( rad((float) nt->c.lat), rad((float) nt->c.lon), clat, clon, xyr ); scale( xyr[0], xyr[1], &nt->xsw, &nt->ysw ); projection->ab_lat( rad(nt->c.lat+1.0f), rad((float) nt->c.lon), clat, clon, xyr ); scale( xyr[0], xyr[1], &nt->xnw, &nt->ynw ); projection->ab_lat( rad(nt->c.lat+1.0f), rad(nt->c.lon+1.0f), clat, clon, xyr ); scale( xyr[0], xyr[1], &nt->xno, &nt->yno ); projection->ab_lat( rad((float) nt->c.lat), rad(nt->c.lon+1.0f), clat, clon, xyr ); scale( xyr[0], xyr[1], &nt->xso, &nt->yso ); tiles.push_back(nt); tiletable[nt->c] = nt ; } } } }