Atlas - Moving Map Update

external: index
 internal: preamble overview building map getmap atlas keyboard conversion folders downloads end

Preamble

2010/05/19: See later Atlas/Map builds at atlas-07.htm ... This version will no longer compile against the latest GIT Simgear without a number of changes....

2009-01-06: As usual, it is quite some time since I last built this set of applications - circa April 2008 - see page http://geoffair.net/fg/fgfs-044.htm ... The URL given on that page still leads to a site, but it has another :- http://sourceforge.net/projects/atlas/

However the CVS download instructions appear unchanged :-

CVS password: <enter> key
cvs -d:pserver:anonymous@atlas.cvs.sourceforge.net:/cvsroot/atlas login
cvs -z3 -d:pserver:anonymous@atlas.cvs.sourceforge.net:/cvsroot/atlas co -P Atlas

The full source is only about 100 files, about 470KB in total, excluding CVS folders.

top


Overview

This wonderful set of applications allows you to have a 'moving' map, zoomable display, while you fly. FG outputs Atlas data on a socket port, in this case 5500, and Atlas receives, decode and display this data as a 'track', and also draws graphs of various data.

FlightGear UDP Data ==> Atlas
Flying in FlightGear $GPRMC, 190856, A, 3736.813, N, 12221.435, W, 000.0, 297.9, 1701109, 0.000, E*56
$GPGGA, 190856, 3736.813, N, 12221.435, W, 1,,, 4 , F,,,, *34
$PATLA, 111.69, 280.0, 111.69, 280.0, 0*54
Tracking in Altas

Click on the images to see them at full 800x600 size. Of course the graphs show I was very wobbly - trying to fly and manage two displays and keyboards, plus joystick at the same time can be difficult ;=))

This is a very small sample of the UDP data sent from FG to Atlas

$GPRMC,191043,A,3737.765,N,12223.031,W,075.3,311.8,1701109,0.000,E*59
$GPGGA,191043,3737.765,N,12223.031,W,1,,,1013,F,,,,*03
$PATLA,111.69,280.0,111.69,280.0,0*54
$GPRMC,191044,A,3737.767,N,12223.035,W,075.2,312.2,1701109,0.000,E*50
$GPGGA,191044,3737.767,N,12223.035,W,1,,,1015,F,,,,*04
$PATLA,111.69,280.0,111.69,280.0,0*54
$GPRMC,191044,A,3737.769,N,12223.039,W,075.2,312.7,1701109,0.000,E*57
$GPGGA,191044,3737.769,N,12223.039,W,1,,,1017,F,,,,*04
$PATLA,111.69,280.0,111.69,280.0,0*54
$GPRMC,191044,A,3737.772,N,12223.041,W,075.1,313.1,1701109,0.000,E*56
$GPGGA,191044,3737.772,N,12223.041,W,1,,,1019,F,,,,*0F
$PATLA,111.69,280.0,111.69,280.0,0*54
$GPRMC,191044,A,3737.774,N,12223.045,W,075.1,313.6,1701109,0.000,E*53
$GPGGA,191044,3737.774,N,12223.045,W,1,,,1020,F,,,,*07
$PATLA,111.69,280.0,111.69,280.0,0*54
$GPRMC,191044,A,3737.777,N,12223.048,W,075.0,314.1,1701109,0.000,E*5C
$GPGGA,191044,3737.777,N,12223.048,W,1,,,1022,F,,,,*0B
$PATLA,111.69,280.0,111.69,280.0,0*54
$GPRMC,191045,A,3737.780,N,12223.053,W,075.0,314.7,1701109,0.000,E*59
$GPGGA,191045,3737.780,N,12223.053,W,1,,,1024,F,,,,*0E
$PATLA,111.69,280.0,111.69,280.0,0*54
$GPRMC,191045,A,3737.782,N,12223.056,W,074.9,315.3,1701109,0.000,E*53
$GPGGA,191045,3737.782,N,12223.056,W,1,,,1026,F,,,,*0B
$PATLA,111.69,280.0,111.69,280.0,0*54
$GPRMC,191045,A,3737.785,N,12223.058,W,074.8,315.9,1701109,0.000,E*51
$GPGGA,191045,3737.785,N,12223.058,W,1,,,1028,F,,,,*0C
$PATLA,111.69,280.0,111.69,280.0,0*54

You can see the first parameter of the $GPRMC and $GPGGA components is a time factor, and each 'block' was sent about 5 times per second, since the command added to FG was :-

--atlas=socket,out,5,localhost,5500,udp

Actually I was running Atlas in a 2nd LAN connected computer, thus 'localhost' had been replaced with the IP address shown by the >ipconfig command line application.

top


Problem Solving

Originally, as found in the previous build, the first problem was with SimGear, which is now dependant on OpenSceneGraph, but luckily I had recently downloaded and build the PREOSG Simgear, and initially chose to use this. But during this time Atlas was updated to use the OSG Simgear - that is CVS SG. OpenSceneGraph headers are required to do the compile, although no OSG libraries are used.

The next was a 'missing' asprintf() function. This has happened in other open sources, like libpcap, netpbm, tar, ... and some native WIN32 workarounds have been written ... and as per my previous build, I have a new source file available.

Although there have been a few (minor) changes in the code since April 2008, most of the modifications were exactly per my previous build - http://geoffair.net/fg/fgfs-044.htm - so most of that page is still valid. But this time, I did manage to move the 'missing' WIN32 functions to a static atlas-win.lib library.

top


Building Applications

I added some minor 'features' to my build -

1. Map:

(a) Added a --debug switch to output some more information on what is happening. This is in addition to the existing --verbose switch, although I reduced the 'warnings' output by NOT repeating certain warning multiple times. And added some additional messages if this --debug is ON, but this was later removed after I undestood the running of Map and Atlas.

(b) Although the feature --airport-filter=<airport name> is mentioned in the help, it seems it had not been implemented, so I went ahead and implemented this. A better addition would be to allow a list of names to be added, rather than at present, just one! But again, this was later removed as I saw these 'overlays' are already added by Atlas.

At first I was concerned that airport and navaid names were not being added to the maps created, until later, when running atlas - it adds airport names, and draws navaids.

top


2. GetMap:

This requires the curl library to do the communications to the WMS Server ...

The Web Map Server (WMS) - a big list of 'servers' is shown on -
 http://www.skylab-mobilesystems.com/en/wms_serverlist.html 
but not sure how many are 'free' and 'available' ...

Some links I tried are 'broken', and others often seem very country specific. But the main WMS of interest to FG is from the NASA satellite mapping project - see -
http://onearth.jpl.nasa.gov/index.html
A link on that page -
http://wms.jpl.nasa.gov/wms.cgi?request=GetCapabilities
will download the XML capabilities of their WMS server ...

This application is not really required for the Atlas/Map display, but it is interesting to retrieve say the San Francisco area from WMS, and compare it to that of FG.

Also as an 'interesting' addition to this application I also output some HTML to a file, so I can quick view what was fetched via WMS ...

top


3. Atlas:

Of course Atlas can be run without FG, and previous 'track' records can be loaded to review the track taken. This is neat, as it puts back the full 'track' taken.

But its main purpose is to 'track' an active aircraft in FG. If you have two machines connected by a network then it is better to load atlas in one, and then run FG in another, giving the IP address of the machine running atlas. The IP address of the machine running Atlas can be found by running > ipconfig at the command line ... or else both can be run in the same machine and 'localhost' is used in FG, switching between windows.

Initially I was not getting any 'tracking', but through creating a templog.txt file of all the upd traffic, I found there was an error in the strsep() implementation I had found. I found another 'source' of this function, and things started cooking ;=))

Some changes in the code :-

1. As mentioned above, I created a LOG file of the UDP messages. This is under a DBG_IO_LOG switch I added to my win32 config.h file. The file name is per a define, also in config.h -
#define DBG_UDP_LOG "templog.txt" - later removed.

2. Also for diagnostics, I added a keyboard log file, under a DBG_KEYBOARD switch, also added to win32 config.h. Again the log file name is likewise defined as -
#define DBG_KEY_LOG "tempkey.txt" - later removed.

3. As per one of the comments in the Atlas.cxx file, in the keyPressed() function, I added a 'q' - quit key, which checks if there is a non zero current track, and prompts to save it before exiting. It also automatically saves a tempatlasrc file, with the current active parameters ...

4. rint(d) function: Unfortunately, the 64-bit rint implementation will FAIL when passed a double number larger than hex 0xffffffffffffffff, decimal 18446744073709551615, scientific 1.84467E+019, that is the maximum unsigned __int64 value, while the floating point can have a maximum of 3.402823466e+38F, and in fact atlas will get TRAPPED in the while() statement ...

I have written a version using all doubles, and floor() or ceil(), which does NOT have the above problem with the maximum size of an __int64 !!!

This is the (simple) code -

double rint( double x ) {
   if ( x > 0.0 )
      return floor( x + 0.5 );
   if ( x < 0.0 )
      return ceil( x - 0.5 );
   return 0.0;
}

I tried it out on my 64-bit ubuntu, and compared the results to the real 'rint()', and it appears to now yield the same in all the examples I tried ...

5. Some code tries to use the unix 'mv' command to 'move' a file, but even after changing this to 'ren' under WIN32, it still failed due to the second parameter containing a path. Also rename will not function if the file already exists ...

6. However, after getting the renaming to work correctly, which included putting back the WIN32 path separator '\', while simgear UNCONDITIONALLY uses '/', I found the whole reason for the 'map' being redone was due to the Tile::_pngSize() function failing.

It opens the file using -

  if (!(f = open(file, O_RDONLY, 0))) {

but this defaults to 'text' mode, so changed this to -

  if (!(f = open(file, O_RDONLY | O_BINARY, 0))) {

and then my existing PNG map files were NOT re-generated.

7. I have NOT found a solution to FILE* Tile::_startCommand(const char *command), which uses 'popen', which is fine, but there seems no way to make int setNonBlocking(FILE *f) work, uses 'fcntl' function, thus the application appears to stall when running the external 'map' application, until it has completed.

One 'better' solution for this would be the run the application on a separate thread ... Alternatively, it is possible to 'open' a file in a non-blocking mode, but this requires the using of the native windows CreateFile() function, using the FILE_FLAG_OVERLAPPED flag, but then all the 'popen' output IO redirection would also have to be re-done ... NOT A SIMPLE TASK ...

In fact Yahooing around, it seems this question 'popen non-blocking win32' has been asked by many, for many years, but could not find any 'simple' solution ... it would seem much simpler to use threads ...

8. Of course it has been well documented that 'rsync' is not available in native WIN32, thus there can be no dynamic loading of the scenery tiles ... it is available if cygwin is installed ... and there is http://sourceforge.net/projects/rsyncwin32/ - the CVS seems empty, but there is a rsyncwin32-0.0.2.zip, 894,523, circa 2005 ...

With a small number of changes, I was able to compile rsync.exe from the above source, but decided I do NOT trust its actions without lots of testing ... there were quite a number of compiler warnings which I did not look into, and especially after reading some things like 'sparse' file handling, which would not work as written in WIN32 - I found this out when doing a native WIN32 port of 'tar' ;=()

I saw some code in there that redefined 'fcntl' to 'ioctlsocket' in an attempt to set non-blocking, but my reading of this 'socket' function is that it will only work on a socket descriptor, and not a FILE *!!! I tried it on a FILE *, but NO GO!

Anyway, until this 'rsync' problem is 'fixed' it is probably better I disable this option in this native WIN32 port of Atlas ... used a NO_RSYNC_APP pre-compiler flag to chop out all references the 'rsync', and running 'rsync' ...

9. Added keyboard help, shown for --help command -

Atlas keyboard
 +/-    Zoom In/Out.
 A/a    Toggle Airport show.
 C      Toggle Auto Centering.
 c      Center Map on Aircraft.
 D/d    Toggle Hide/Show the info interface and the graphs window.
 F/f    Select the next ('f') or previous ('F') flight track.
 J/j    Toggle the search interface.
 L      Show the next downloading tile.
 l      Schedule or de-schedule the 1x1 tile at our current lat/lon for updating.
 N/n    Toggle Show Navigation Aids.
 O/o    Open a Flight Track File.
 q      Quit, prompting for save of any active track.
 S/s    Save Flight Track File.
 T/t    Toggle Texturing.
 W/w    Clear/Close the current track. No warning if the track is unsaved.
 U/u    Un-attach (detach) Network, and restart tracking.
 V/v    Toggle Show Names.

So in general, Atlas is now working fine in WIN32 ... I added a LOT of DEBUG output, while tracking down certain problems, so there is copious output to the console, and this output is written to a log file, tempatlas.txt ... This is turned off by defining DBG_printf to nothing ... this was all later removed.

Sometimes, on a load, nothing but the grid is displayed, until I click the mouse on the map, but have not fully tracked down this problem ... This problem departed in the 2nd port I did, so not sure what was wrong. But another change I made, to check if a track was valid, means that an 'f' keyinput is required after FG starts sending data, do get the 'track' displayed.

top


Conversion of latitude and longitude

Of particular interest to me is how to convert a latitude, longitude to an x, y coordinate on a 'map' ... I have played with this many times, and seldom got everything I wanted. So this is an exploration of how this is done in Atlas ...

Looking specifically at Overlays::drawOverlays() in Overlays.cxx. It has the code -

  lat_ab( size / scale / 2.0f, size / scale / 2.0f, lat, lon,
              &dtheta, &dalpha );
  dtheta -= lat;
  dalpha -= lon;
  if (dalpha > SG_PI/2) dalpha = SG_PI/2;

where lat, lon is set via -

inline void setLocation( float lat, float lon ) {
    this->lat = lat;
    this->lon = lon;
  }

The lat, lon is the center of the map, as set, for example, when the aircraft's location is to be the center -

/* Center map at aircraft's current position. */
void centerMapOnAircraft() {
    if (track && !track->empty()) {
            FlightData *pos = track->getCurrentPoint();
            latitude = pos->lat;
            longitude = pos->lon;
            map_object->setLocation(latitude, longitude);
            glutPostRedisplay();
    }
}

This sets the aircraft position as the 'center' of the map object ...

Or, as the program begins, if you give an --airport=<icao>, then -

Overlays::ARP* apt = map_object->getOverlays()->findAirportByCode(prefs.icao);
    if(apt) {
      latitude = apt->lat;
      longitude = apt->lon;
    }

... and later ...

map_object->setLocation(latitude, longitude);

centers this airport as the center of the map ...

I added some code to paint a larger VOR circle, and when zoomed in I get the following -

image01

Note that the even at this very zoomed in state, the VOR circles are maintained exactly as they were defined in the code - in Overlays.cxx, function -

void Overlays::draw_vor( NAV *n, sgVec2 p ) {
    static const int RADIUS = 15;
    int RADIUS2 = 30;  // add larger outer circle

These should NOT be 'constants', but depend upon the current 'zoom' scale, and 'range' of the VOR, so eventually added the following ...

if( n->range > 0 )
    RADIUS2 = (n->range * SG_NM_TO_METER * scale);

to paint a very large out circle. It seems however that glut does not do a very good job of painting large circles. It is all too clear that it is made up of straight lines, which is ok, but not enough points are calculated, only 12 divisions, so it has 'flat' sides ;=))

For example if the command is given --airport=KSFO, then you get (using my DBG_printf() statement in Overlays.hxx) :-
Overlays::setLocation: Map centered on lat, lon radians: 0.656613,-2.135851, degrees: 37.621134,-122.375263 ...
That is the map is centered on lat, lon 37.621134,-122.375263 degrees.

So, in this case, when calling to draw the particular VOR, if in range per the 'if' conditions :-

if (fabs(n->lat - theta) < dtheta &&
    fabs(wrap_angle(n->lon - alpha)) < dalpha) {
      projection->ab_lat( n->lat, n->lon, theta, alpha, xyr );
      sgSetVec2( p,  ::scale(xyr[0], output->getSize(), scale),
             ::scale(xyr[1], output->getSize(), scale) );

This uses Projection::lat_ab(), depending on the 'projection' type, to calculate the sgVec3 position through -

void Projection::ab_lat( float lat, float lon, float lat_r, float lon_r, sgVec3 dst ) {
   switch (sysid) {
   case SANSON_FLAMSTEED:
        dst[2] = earth_radius_lat( lat_r );
        dst[0] = dst[2] * cos(lat)*(lon-lon_r);
        dst[1] = dst[2] * (lat-lat_r);
        break;
   case CYL_EQUIDISTANT_EQ:
        dst[2] = rec;
        dst[0] = dst[2] * (lon-lon_r);
        dst[1] = dst[2] * (lat-lat_r);
        break;
   case CYL_EQUIDISTANT_LOCAL:
       dst[2] = earth_radius_lat(lat_r);
       dst[0] = dst[2] * cos(lat_r)*(lon-lon_r);
       dst[1] = dst[2] * (lat-lat_r);
       break;
   }
}

You can see this depend on the earth's radius, except when equidistant, at the particular latitude, which takes into account the approximate 'squished' elipsoid being the earth ...

/* equatorial and polar earth radius */
const float rec  = 6378137;          // earth radius, equator (?)
const float rpol = 6356752.314f;     // earth radius, polar   (?)
//Returns Earth radius at a given latitude (Ellipsoide equation with two equal axis)
inline float earth_radius_lat( float lat ) {
  double a = cos(lat)/rec;
  double b = sin(lat)/rpol;
  return 1.0f / sqrt( a * a + b * b );
}

And ::scale() -

inline float scale( float x, float size, float zoom ) {
   return (size/2 + x * zoom);
}

So, for the San Francisco VOR-DME you get -
VOR: SAN FRANCISCO VOR-DME, at 37.619481,-122.373884, position: 121.501297,-183.770035 (400.972015,398.529846) size=800 scale=0.01

In the case of the above image, the 'scale' has be increased to 0.08 (using the '+' key) ...
VOR: SAN FRANCISCO VOR-DME, at 37.619481,-122.373884, position: 121.501297,-183.770035 (410.307678,384.409698) size=800, scale=0.08

At present the decision to add a navaid item depends on the following bounds -
Bounds: center: lat=37.622384, lon=-122.375263, theta=0.449720, alpha=0.566373
where theta and alpha are calculated from the following -

lat_ab( size / scale / 2.0f, size / scale / 2.0f, lat, lon,
        &dtheta, &dalpha );
  dtheta -= lat;
  dalpha -= lon;
  if (dalpha > SG_PI/2) dalpha = SG_PI/2;

with lat, lon being the center of the map, in radians. That is -

if (fabs(obj_lat - cent_lat) < dtheta &&
        fabs(wrap_angle(obj_lon - cent_lon)) < dalpha) {

where wrap_angle =

// Shift an angle (radians) by multiples of 2PI into the range [-PI, PI).
static double wrap_angle(double a_rad) {
    double y = fmod(a_rad, SGD_2PI);  /* range (-2PI, 2PI) */
    if (y < -SGD_PI) y += SGD_2PI;
    else if (y >= SGD_PI) y -= SGD_2PI;
    return y;
}

As one of the comment mentions, this means objects not within this range are not painted, even though some parts of it may in fact be on the map. This would be especially true for say an airport just off the edge of the map, even though runways drawn might intersect with the map.

While the maps bounding rectangle, using a geometry of 800x600, is x, y TL 0,800, BR 800,0, the window rectangle is about 10,690,790,110 ...

This investigation led me to the development of two new services - ll2pt, and pt2ll, as follows :-

double er_equ = 6378137.0;   // earth radius, equator (?)
double er_pol = 6356752.314; // earth radius, polar   (?)
double get_erad( double lat )
{
    // case SANSON_FLAMSTEED:
    double a = cos(lat) / er_equ;
    double b = sin(lat) / er_pol;
    return (1.0 / sqrt( a * a + b * b ));
}
void ll2pt( double obj_latr,  double obj_lonr,
            double cent_latr, double cent_lonr,
            double map_size,  double map_zoom,
            double * px_pixel, double * py_pixel )
{
    double x,y,r;
    r = get_erad(cent_latr); // case SANSON_FLAMSTEED:
    x = r * cos(obj_latr)*(obj_lonr - cent_lonr);
    y = r * (obj_latr - cent_latr);
    *px_pixel = (map_size / 2.0) + (x * map_zoom);
    *py_pixel = (map_size / 2.0) + (y * map_zoom);
}
void pt2ll( double * obj_latr, double * obj_lonr,
            double cent_latr, double cent_lonr,
            double map_size,  double map_zoom,
            double x_pixel, double y_pixel )
{
    double x,y,r;
    r = get_erad(cent_latr); // case SANSON_FLAMSTEED:
    x = (x_pixel - (map_size / 2.0)) / map_zoom;
    y = (y_pixel - (map_size / 2.0)) / map_zoom;
    *obj_latr = (y / r) + cent_latr;
    *obj_lonr = (x / (r * cos(*obj_latr))) + cent_lonr;
}

These appear to operate very well and shall use them in subsequent applications that need to convert latitude, longitude points to x, y window coordinates, and the reverse. ;=))

I then wanted to add a 'scale' line, and perhaps a compass to the map ... but ran into BIG trouble consistently placing them in the lower left and upper right of the map. The 'image' is built on a square using the largest of the current width (x), and height (y). This makes sense to ensure the map image goes out to the current edges of the window, but it seems glut has some weird way to 'center' this image.

I add oodles of diagnostic message to try to catch what glut was doing, but nothing worked exactly. It is able to keep a 'widget' relatively positioned on the current window size, but I could not seem to understand this 'positioning'.

top


Folders

My WORK folder set is as follows, in my case in C:\FG\27. Of course, some are in fact prerequisites of SG/FG - see prereq.htm for those -  and the LINKS to get each of the sources.:-

Folders
Folder View 

Source Link
Atlas http://atlas.sourceforge.net/
jpeg http://ijg.org/
PLIB http://plib.sourceforge.net/
zlib http://www.zlib.net/
SimGear http://www.simgear.org/
libpng http://www.libpng.org/
OpenSceneGraph http://www.openscenegraph.org/projects/osg
freeglut http://freeglut.sourceforge.net/
curl http://curl.haxx.se/download.html
FG data http://www.flightgear.org/Downloads/scenery.html

If you use this SAME directory structure, then my 'build/msvc' MSVC (DSW/DSP) files will require no modification.

top


Downloads

src zip - The 'src' zip below not only contains the complete modified source of Atlas, Map, etc, is also contains the NEW files for the 'build/msvc' folder, and specifically includes :-

atlas-02.patch.txt - To patch the CVS source. Link to copy atlas-02.patch.txt
README.WIN32.txt - A relatively brief summary of building and running.
Atlas.dsw - To build with MSVC, together with the matching dsp files
config.h - Hand crafted 'config.h', to handle many WIN32 only things.
and all the sources for the static atlas-win.lib, and various other files, including batch files for testing, zipping, etc.

exe zip - If you just want to try out Atlas/Map/GetMap in a native WIN32 environment, then you only need the 'exe' zip below.

Data Link Size MD5
2009-01-17 atlas-02-src.zip 257,068 e3a68c147b73cfe3b746f9a24e9d9c8b
2009-01-17 atlas-02-exe.zip 627,966 5cc247723d702f10f652ee0de805c025

As always, take care downloading and running executables from the web! ;=((

top


Initial, now old, 2009.01.22 page - fgfs-049a.htm

EOF - Altas-10.doc

top


checked by tidy  Valid HTML 4.01 Transitional