diff --git a/src/Airports/GenAirports850/CMakeLists.txt b/src/Airports/GenAirports850/CMakeLists.txt
index 81c46a27..6d5d250f 100644
--- a/src/Airports/GenAirports850/CMakeLists.txt
+++ b/src/Airports/GenAirports850/CMakeLists.txt
@@ -1,20 +1,17 @@
-
-
 add_executable(genapts850
-    apt_surface.hxx apt_surface.cxx 
-	build.cxx build.hxx 
+	airport.hxx airport.cxx
+	apt_surface.hxx apt_surface.cxx
+	beznode.hxx
+	closedpoly.hxx closedpoly.cxx
+	convex_hull.hxx convex_hull.cxx
 	elevations.cxx elevations.hxx 
 	global.hxx
-        heli_gen.cxx heli_gen.hxx
-	lights.hxx lights.cxx 
-	main.cxx 
+	linearfeature.hxx linearfeature.cxx
+	main.cxx
+	parser.hxx parser.cxx
 	point2d.cxx point2d.hxx 
 	poly_extra.cxx poly_extra.hxx 
-	runway.cxx runway.hxx 
-	rwy_common.cxx rwy_common.hxx 
-	rwy_gen.cxx rwy_gen.hxx
-	rwy_simple.cxx rwy_simple.hxx 
-	taxiway.cxx taxiway.hxx 
+	runway.cxx runway.hxx
 	texparams.hxx
 	)
   
@@ -25,6 +22,7 @@ target_link_libraries(genapts850
 	${SIMGEAR_CORE_LIBRARIES}
 	${SIMGEAR_CORE_LIBRARY_DEPENDENCIES}
 	${GPC_LIBRARY}
-	${NEWMAT_LIBRARY})
+	${NEWMAT_LIBRARY}
+        osg osgDB osgGA osgUtil osgViewer)
 
 install(TARGETS genapts850 RUNTIME DESTINATION bin)
diff --git a/src/Airports/GenAirports850/airport.cxx b/src/Airports/GenAirports850/airport.cxx
new file mode 100644
index 00000000..466ef46f
--- /dev/null
+++ b/src/Airports/GenAirports850/airport.cxx
@@ -0,0 +1,2048 @@
+#include "beznode.hxx"
+#include "runway.hxx"
+#include "airport.hxx"
+
+#include <list>
+#include <map>
+#include <string>
+
+#include <simgear/compiler.h>
+#include <simgear/structure/exception.hxx>
+#include <simgear/debug/logstream.hxx>
+#include <simgear/bucket/newbucket.hxx>
+#include <simgear/math/sg_geodesy.hxx>
+#include <simgear/math/SGGeometry.hxx>
+#include <simgear/io/sg_binobj.hxx>
+#include <simgear/misc/texcoord.hxx>
+
+#include <Polygon/polygon.hxx>
+#include <Polygon/superpoly.hxx>
+#include <Polygon/chop.hxx>
+
+#include <Geometry/poly_support.hxx>
+#include <Output/output.hxx>
+
+#include "elevations.hxx"
+#include "poly_extra.hxx"
+
+Airport::Airport( int c, char* def)
+{
+    int   numParams;
+    char  d[100];
+    char  tmp[16];
+    int   x, y;
+
+    code = c;
+
+	SG_LOG(SG_GENERAL, SG_DEBUG, "sscanf " << def);
+    numParams = sscanf(def, "%d %d %d %s %ls", &altitude, &x, &y, tmp, d);
+    SG_LOG(SG_GENERAL, SG_DEBUG, "done ");
+	SG_LOG(SG_GENERAL, SG_DEBUG, "got " << altitude << ", " << tmp << ", " << d);
+
+    altitude *= SG_FEET_TO_METER;
+    icao = tmp;
+    description = d;
+}
+
+void Airport::BuildOsg( osg::Group* airport )
+{
+    // draw for OSG
+    int i;
+
+#if 0
+    for (i=0; i<runways.size(); i++)
+    {
+        runways.at(i)->BuildOsg( airport );
+    }
+#endif
+
+    for (i=0; i<pavements.size(); i++)
+    {
+    	SG_LOG(SG_GENERAL, SG_DEBUG, " Adding pavement " << i << "to airport");
+        pavements.at(i)->BuildOsg(airport);
+    }
+
+#if 0
+    for (i=0; i<features.size(); i++)
+    {
+        features.at(i)->Finish(airport);
+    }
+#endif
+}
+
+
+// TODO: Add to object
+// calculate texture coordinates for runway section using the provided
+// texturing parameters.  Returns a mirror polygon to the runway,
+// except each point is the texture coordinate of the corresponding
+// point in the original polygon.
+static TGPolygon rwy_section_tex_coords( const TGPolygon& in_poly, const TGTexParams& tp, const bool clip_result )
+{
+    int i, j;
+    TGPolygon result;
+    result.erase();
+    // double length = rwy.length * SG_FEET_TO_METER;
+    // double width = rwy.width * SG_FEET_TO_METER;
+
+    Point3D ref = tp.get_ref();
+    double width = tp.get_width();
+    double length = tp.get_length();
+    double heading = tp.get_heading();
+    double minu = tp.get_minu();
+    double maxu = tp.get_maxu();
+    double minv = tp.get_minv();
+    double maxv = tp.get_maxv();
+    SG_LOG( SG_GENERAL, SG_DEBUG, "section ref = " << ref );
+    SG_LOG( SG_GENERAL, SG_DEBUG, "  width = " << width );
+    SG_LOG( SG_GENERAL, SG_DEBUG, "  length = " << length );
+    SG_LOG( SG_GENERAL, SG_DEBUG, "  heading = " << heading );
+    Point3D p, t;
+    double x, y, tx, ty;
+
+    for ( i = 0; i < in_poly.contours(); ++i ) {
+	for ( j = 0; j < in_poly.contour_size( i ); ++j ) {
+	    p = in_poly.get_pt( i, j );
+	    SG_LOG(SG_GENERAL, SG_DEBUG, "point = " << p);
+
+	    //
+	    // 1. Calculate distance and bearing from the center of
+	    // the runway
+	    //
+
+	    // given alt, lat1, lon1, lat2, lon2, calculate starting
+	    // and ending az1, az2 and distance (s).  Lat, lon, and
+	    // azimuth are in degrees.  distance in meters
+	    double az1, az2, dist;
+	    geo_inverse_wgs_84( 0, ref.y(), ref.x(), p.y(), p.x(),
+				&az1, &az2, &dist );
+	    SG_LOG(SG_GENERAL, SG_DEBUG, "basic course = " << az2);
+
+	    //
+	    // 2. Rotate this back into a coordinate system where Y
+	    // runs the length of the runway and X runs crossways.
+	    //
+
+	    double course = az2 - heading;
+	    while ( course < -360 ) { course += 360; }
+	    while ( course > 360 ) { course -= 360; }
+	    SG_LOG( SG_GENERAL, SG_DEBUG,
+                    "  course = " << course << "  dist = " << dist );
+
+	    //
+	    // 3. Convert from polar to cartesian coordinates
+	    //
+
+	    x = sin( course * SGD_DEGREES_TO_RADIANS ) * dist;
+	    y = cos( course * SGD_DEGREES_TO_RADIANS ) * dist;
+	    SG_LOG(SG_GENERAL, SG_DEBUG, "  x = " << x << " y = " << y);
+
+	    //
+	    // 4. Map x, y point into texture coordinates
+	    //
+	    double tmp;
+
+            tmp = x / width;
+            tx = tmp * (maxu - minu) + minu;
+	    // tx = ((int)(tx * 100)) / 100.0;
+	    SG_LOG(SG_GENERAL, SG_DEBUG, "  (" << tx << ")");
+
+            if ( clip_result) {
+                if ( tx < 0.0 ) { tx = 0.0; }
+                if ( tx > 1.0 ) { tx = 1.0; }
+            }
+
+	    // ty = (y - min.y()) / (max.y() - min.y());
+	    ty = y / length;
+            tmp = y / length;
+            ty = tmp * (maxv - minv) + minv;
+	    // ty = ((int)(ty * 100)) / 100.0;
+	    SG_LOG(SG_GENERAL, SG_DEBUG, "  (" << ty << ")");
+
+            if ( clip_result ) {
+                if ( ty < 0.0 ) { ty = 0.0; }
+                if ( ty > 1.0 ) { ty = 1.0; }
+            }
+
+	    t = Point3D( tx, ty, 0 );
+	    SG_LOG(SG_GENERAL, SG_DEBUG, "  (" << tx << ", " << ty << ")");
+
+	    result.add_node( i, t );
+	}
+    }
+
+    return result;
+}
+
+// TODO : Add somewhere
+// Determine node elevations of a point_list based on the provided
+// TGAptSurface.  Offset is added to the final elevation
+static point_list calc_elevations( TGAptSurface &surf,
+                                   const point_list& geod_nodes,
+                                   double offset )
+{
+    point_list result = geod_nodes;
+    for ( unsigned int i = 0; i < result.size(); ++i ) {
+        double elev = surf.query( result[i].lon(), result[i].lat() );
+        result[i].setelev( elev + offset );
+    }
+
+    return result;
+}
+
+
+// Determine node elevations of each node of a TGPolygon based on the
+// provided TGAptSurface.  Offset is added to the final elevation
+static TGPolygon calc_elevations( TGAptSurface &surf,
+                                  const TGPolygon& poly,
+                                  double offset )
+{
+    TGPolygon result;
+    for ( int i = 0; i < poly.contours(); ++i ) {
+        point_list contour = poly.get_contour( i );
+        point_list elevated = calc_elevations( surf, contour, offset );
+
+        result.add_contour( elevated, poly.get_hole_flag(i) );
+    }
+
+    return result;
+}
+
+void Airport::BuildBtg(const string& root, const string_list& elev_src )
+{
+    TGPolygon apt_base;
+    TGPolygon apt_clearing;
+    TGPolygon accum;
+    accum.erase(); 
+
+    superpoly_list rwy_polys;
+    texparams_list texparams;
+    int i, j, k;
+    char buf[120];  // For debugging output
+    Point3D p;
+
+    // parse main airport information
+    double apt_lon = 0.0, apt_lat = 0.0;
+    int rwy_count = 0;
+
+    // Find the average of all the runway long / lats
+    // TODO : Need runway object...
+
+    
+    for (i=0; i<runways.size(); i++)
+    {
+        printf("runway %d from (%lf,%lf) to (%lf,%lf) : midpoint is (%lf,%lf)\n", 
+            i,
+            runways[i]->GetStart().x(),    runways[i]->GetStart().y(),
+            runways[i]->GetEnd().x(),      runways[i]->GetEnd().y(),
+            runways[i]->GetMidpoint().x(), runways[i]->GetMidpoint().y()
+        );
+
+        apt_lon += runways.at(i)->GetMidpoint().x();
+        apt_lat += runways.at(i)->GetMidpoint().y();
+    }
+    apt_lon = apt_lon / (double)runways.size();
+    apt_lat = apt_lat / (double)runways.size();
+
+    printf("Average is (%lf,%lf\n", apt_lon, apt_lat);
+
+    SGBucket b( apt_lon, apt_lat );
+    SG_LOG(SG_GENERAL, SG_INFO, b.gen_base_path() << "/" << b.gen_index_str());
+
+    // Build precision runways first
+    for (i=0; i<runways.size(); i++ ) 
+    {
+        if ( runways[i]->IsPrecision() ) 
+        {
+            runways[i]->BuildBtg( altitude, &rwy_polys, &texparams, &accum, &apt_base, &apt_clearing );
+	    }
+    }
+
+    // Now generate pavements
+    if (pavements.size())
+    {
+        for ( i=0; i<pavements.size(); i++ )
+        {
+            SG_LOG(SG_GENERAL, SG_ALERT, "Build Pavement Poly " << i);
+            pavements[i]->BuildBtg( altitude, &rwy_polys, &texparams, &accum, &apt_base, &apt_clearing );
+        }
+    }
+    else
+    {
+        SG_LOG(SG_GENERAL, SG_ALERT, "no pavements");
+    }
+
+    // wipe out the pavements to save memory
+    pavements.clear();
+
+    // Then the linear features
+    for ( i=0; i< 1; i++ )
+    {
+        // not yet...
+    }
+
+    if ( apt_base.total_size() == 0 ) 
+    {
+        SG_LOG(SG_GENERAL, SG_ALERT, "no airport points generated");
+    	return;
+    }
+    else
+    {
+        SG_LOG(SG_GENERAL, SG_ALERT, "Lookin good");
+    }
+
+    TGPolygon filled_base  = tgPolygonStripHoles( apt_base );
+    TGPolygon divided_base = tgPolygonSplitLongEdges( filled_base, 200.0 );
+    TGPolygon base_poly    = tgPolygonDiff( divided_base, accum );
+
+    // do this when generating the polys themselves...
+    for ( k = 0; k < (int)rwy_polys.size(); ++k ) 
+    {
+    	SG_LOG(SG_GENERAL, SG_DEBUG, "add nodes/remove dups section = " << k << " " << rwy_polys[k].get_material());
+    	TGPolygon poly = rwy_polys[k].get_poly();
+	    SG_LOG(SG_GENERAL, SG_DEBUG, "total size before = " << poly.total_size());
+	    for ( i = 0; i < poly.contours(); ++i ) 
+        {
+	        for ( j = 0; j < poly.contour_size(i); ++j ) 
+            {
+        		Point3D tmp = poly.get_pt(i, j);
+        		snprintf(buf, 119, "  %.7f %.7f %.7f\n", tmp.x(), tmp.y(), tmp.z() );
+        		SG_LOG(SG_GENERAL, SG_DEBUG, buf);
+    	    }
+    	}
+
+    	poly = remove_dups( poly );
+	    SG_LOG(SG_GENERAL, SG_DEBUG, "total size after remove_dups() = " << poly.total_size());
+
+    	for ( i = 0; i < poly.contours(); ++i ) 
+        {
+    	    for ( j = 0; j < poly.contour_size(i); ++j ) 
+            {
+        		Point3D tmp = poly.get_pt(i, j);
+        		snprintf(buf, 119, "    %.7f %.7f %.7f\n", tmp.x(), tmp.y(), tmp.z() );
+        		SG_LOG(SG_GENERAL, SG_DEBUG, buf);
+    	    }
+    	}
+
+    	poly = reduce_degeneracy( poly );
+    	SG_LOG(SG_GENERAL, SG_DEBUG, "total size after reduce_degeneracy() = " << poly.total_size());
+
+    	for ( i = 0; i < poly.contours(); ++i ) 
+        {
+    	    for ( j = 0; j < poly.contour_size(i); ++j ) 
+            {
+        		Point3D tmp = poly.get_pt(i, j);
+        		snprintf(buf, 119, "    %.7f %.7f %.7f\n", tmp.x(), tmp.y(), tmp.z() );
+        		SG_LOG(SG_GENERAL, SG_DEBUG, buf);
+    	    }
+    	}
+
+    	rwy_polys[k].set_poly( poly );
+    }
+    // END do this when generating polys
+
+    // add segments to polygons to remove any possible "T"
+    // intersections
+    TGTriNodes tmp_nodes;
+
+    // build temporary node list
+    for ( k = 0; k < (int)rwy_polys.size(); ++k ) 
+    {
+    	TGPolygon poly = rwy_polys[k].get_poly();
+    	for ( i = 0; i < poly.contours(); ++i ) 
+        {
+    	    for ( j = 0; j < poly.contour_size( i ); ++j ) 
+            {
+        		tmp_nodes.unique_add( poly.get_pt(i, j) );
+    	    }
+    	}
+    }
+    for ( i = 0; i < base_poly.contours(); ++i ) 
+    {
+    	for ( j = 0; j < base_poly.contour_size( i ); ++j ) 
+        {
+    	    tmp_nodes.unique_add( base_poly.get_pt(i, j) );
+    	}
+    }
+
+    // the divided base could contain points not found in base_poly,
+    // so we should add them because the skirt needs them.
+    for ( i = 0; i < divided_base.contours(); ++i ) 
+    {
+	    for ( j = 0; j < divided_base.contour_size( i ); ++j ) 
+        {
+    	    tmp_nodes.unique_add( divided_base.get_pt(i, j) );
+    	}
+    }
+
+    for ( k = 0; k < (int)rwy_polys.size(); ++k ) 
+    {
+    	TGPolygon poly = rwy_polys[k].get_poly();
+    	poly = add_nodes_to_poly( poly, tmp_nodes );
+    	SG_LOG(SG_GENERAL, SG_DEBUG, "total size after add nodes = " << poly.total_size());
+
+    	rwy_polys[k].set_poly( poly );
+    }
+
+    // One more pass to try to get rid of other yukky stuff
+    for ( k = 0; k < (int)rwy_polys.size(); ++k ) 
+    {
+    	TGPolygon poly = rwy_polys[k].get_poly();
+
+    	SG_LOG(SG_GENERAL, SG_DEBUG, "total size of section " << k << " before =" << poly.total_size());
+
+        poly = remove_dups( poly );
+    	SG_LOG(SG_GENERAL, SG_DEBUG, "total size after remove_dups() = " << poly.total_size());
+        poly = remove_bad_contours( poly );
+    	SG_LOG(SG_GENERAL, SG_DEBUG, "total size after remove_bad() = " << poly.total_size());
+        poly = remove_tiny_contours( poly );
+    	SG_LOG(SG_GENERAL, SG_DEBUG, "total size after remove_tiny_contours() = " << poly.total_size());
+
+    	rwy_polys[k].set_poly( poly );
+    }
+
+    SG_LOG(SG_GENERAL, SG_DEBUG, "add nodes base ");
+    SG_LOG(SG_GENERAL, SG_DEBUG, " before: " << base_poly);
+    SG_LOG(SG_GENERAL, SG_DEBUG, " tmp_nodes size = " << tmp_nodes.get_node_list().size());
+
+    base_poly = add_nodes_to_poly( base_poly, tmp_nodes );
+    SG_LOG(SG_GENERAL, SG_DEBUG, " after adding tmp_nodes: " << base_poly);
+
+    // write_polygon( base_poly, "base-add" );
+    SG_LOG(SG_GENERAL, SG_DEBUG, "remove dups base ");
+    base_poly = remove_dups( base_poly );
+    SG_LOG(SG_GENERAL, SG_DEBUG, "remove bad contours base");
+    base_poly = remove_bad_contours( base_poly );
+    SG_LOG(SG_GENERAL, SG_DEBUG, "remove small contours base");
+    base_poly = remove_tiny_contours( base_poly );
+    // write_polygon( base_poly, "base-fin" );
+    SG_LOG(SG_GENERAL, SG_DEBUG, " after clean up: " << base_poly);
+
+    // tesselate the polygons and prepair them for final output
+    for ( i = 0; i < (int)rwy_polys.size(); ++i ) 
+    {
+        SG_LOG(SG_GENERAL, SG_DEBUG, "Tesselating section = " << i << " flag = " << rwy_polys[i].get_flag());
+
+    	TGPolygon poly = rwy_polys[i].get_poly();
+	    SG_LOG(SG_GENERAL, SG_DEBUG, "contours before " << poly.contours() << " total points before = " << poly.total_size());
+	    TGPolygon tri = polygon_tesselate_alt( poly );
+	    SG_LOG(SG_GENERAL, SG_DEBUG, "total size after = " << tri.total_size());
+
+        TGPolygon tc;
+        if ( rwy_polys[i].get_flag() == "taxi" ) 
+        {
+            SG_LOG(SG_GENERAL, SG_DEBUG, "taxiway, no clip");
+            tc = rwy_section_tex_coords( tri, texparams[i], false );
+        } 
+        else 
+        {
+            tc = rwy_section_tex_coords( tri, texparams[i], true );
+        }
+
+    	rwy_polys[i].set_tris( tri );
+	    rwy_polys[i].set_texcoords( tc );
+    }
+
+    SG_LOG(SG_GENERAL, SG_DEBUG, "Tesselating base");
+    TGPolygon base_tris = polygon_tesselate_alt( base_poly );
+
+    //
+    // We should now have the runway polygons all generated with their
+    // corresponding triangles and texture coordinates, and the
+    // surrounding base area.
+    //
+    // Next we need to create the output lists ... vertices, normals,
+    // texture coordinates, and tri-strips
+    //
+
+    // traverse the tri list and create ordered node and texture
+    // coordinate lists
+
+    TGTriNodes nodes, normals, texcoords;
+    nodes.clear();
+    normals.clear();
+    texcoords.clear();
+
+    group_list pts_v; pts_v.clear();
+    group_list pts_n; pts_n.clear();
+    string_list pt_materials; pt_materials.clear();
+
+    group_list tris_v; tris_v.clear();
+    group_list tris_n; tris_n.clear();
+    group_list tris_tc; tris_tc.clear();
+    string_list tri_materials; tri_materials.clear();
+
+    group_list strips_v; strips_v.clear();
+    group_list strips_n; strips_n.clear();
+    group_list strips_tc; strips_tc.clear();
+    string_list strip_materials; strip_materials.clear();
+
+    int index;
+    int_list pt_v, tri_v, strip_v;
+    int_list pt_n, tri_n, strip_n;
+    int_list tri_tc, strip_tc;
+
+    // calculate "the" normal for this airport
+    p.setx( base_tris.get_pt(0, 0).x() * SGD_DEGREES_TO_RADIANS );
+    p.sety( base_tris.get_pt(0, 0).y() * SGD_DEGREES_TO_RADIANS );
+    p.setz( 0 );
+    Point3D vnt = sgGeodToCart( p );
+    
+    // SG_LOG(SG_GENERAL, SG_DEBUG, "geod = " << p);
+    // SG_LOG(SG_GENERAL, SG_DEBUG, "cart = " << tmp);
+
+    SGVec3d tmp( vnt.x(), vnt.y(), vnt.z() );
+    tmp = normalize(tmp);
+    Point3D vn( tmp.x(), tmp.y(), tmp.z() );
+
+    SG_LOG(SG_GENERAL, SG_DEBUG, "found normal for this airport = " << tmp);
+
+    for ( k = 0; k < (int)rwy_polys.size(); ++k ) 
+    {
+    	SG_LOG(SG_GENERAL, SG_DEBUG, "tri " << k);
+    	// TGPolygon tri_poly = rwy_tris[k];
+    	TGPolygon tri_poly = rwy_polys[k].get_tris();
+    	TGPolygon tri_txs = rwy_polys[k].get_texcoords();
+    	string material = rwy_polys[k].get_material();
+    	SG_LOG(SG_GENERAL, SG_DEBUG, "material = " << material);
+    	SG_LOG(SG_GENERAL, SG_DEBUG, "poly size = " << tri_poly.contours());
+    	SG_LOG(SG_GENERAL, SG_DEBUG, "texs size = " << tri_txs.contours());
+    	for ( i = 0; i < tri_poly.contours(); ++i ) 
+        {
+    	    tri_v.clear();
+    	    tri_n.clear();
+    	    tri_tc.clear();
+    	    for ( j = 0; j < tri_poly.contour_size(i); ++j ) 
+            {
+        		p = tri_poly.get_pt( i, j );
+        		index = nodes.unique_add( p );
+        		tri_v.push_back( index );
+
+        		// use 'the' normal
+        		index = normals.unique_add( vn );
+        		tri_n.push_back( index );
+
+        		Point3D tc = tri_txs.get_pt( i, j );
+        		index = texcoords.unique_add( tc );
+        		tri_tc.push_back( index );
+    	    }
+    	    tris_v.push_back( tri_v );
+    	    tris_n.push_back( tri_n );
+    	    tris_tc.push_back( tri_tc );
+    	    tri_materials.push_back( material );
+    	}
+    }
+
+    // add base points
+    std::vector< SGVec2f > base_txs; 
+    int_list base_tc;
+
+    for ( i = 0; i < base_tris.contours(); ++i ) 
+    {
+    	tri_v.clear();
+    	tri_n.clear();
+    	tri_tc.clear();
+    	for ( j = 0; j < base_tris.contour_size(i); ++j ) 
+        {
+    	    p = base_tris.get_pt( i, j );
+    	    index = nodes.unique_add( p );
+    	    tri_v.push_back( index );
+
+    	    index = normals.unique_add( vn );
+    	    tri_n.push_back( index);
+    	}
+    	tris_v.push_back( tri_v );
+    	tris_n.push_back( tri_n );
+    	tri_materials.push_back( "Grass" );
+
+    	std::vector < SGGeod > geodNodes;
+    	for ( j = 0; j < nodes.get_node_list().size(); j++ ) 
+        {
+    	    Point3D node = nodes.get_node_list()[j];
+    	    geodNodes.push_back( SGGeod::fromDegM( node.x(), node.y(), node.z() ) );
+    	}
+	    base_txs.clear();
+    	base_txs = sgCalcTexCoords( b, geodNodes, tri_v );
+
+    	base_tc.clear();
+    	for ( j = 0; j < (int)base_txs.size(); ++j ) 
+        {
+    	    SGVec2f tc = base_txs[j];
+    	    // SG_LOG(SG_GENERAL, SG_DEBUG, "base_tc = " << tc);
+    	    index = texcoords.simple_add( Point3D( tc.x(), tc.y(), 0 ) );
+    	    base_tc.push_back( index );
+    	}
+    	tris_tc.push_back( base_tc );
+    }
+
+    // on rare occasion, one or more of the divided base points can be
+    // missed.  Make sure they are all in the node list so we can
+    // build a proper skirt.
+
+    for ( i = 0; i < divided_base.contours(); ++i ) 
+    {
+    	for ( j = 0; j < divided_base.contour_size( i ); ++j ) 
+        {
+    	    nodes.unique_add( divided_base.get_pt(i, j) );
+    	}
+    }
+
+    // Now that we have assembled all the airport geometry nodes into
+    // a list, calculate an "average" airport elevation based on all
+    // the actual airport node points.  This is more useful than
+    // calculating an average over the entire airport surface because
+    // it avoids biases introduced from the surrounding area if the
+    // airport is located in a bowl or on a hill.
+
+    double average = tgAverageElevation( root, elev_src, nodes.get_node_list() );
+    // cout << "average airport elevation = " << average << endl;
+
+    // Now build the fitted airport surface ...
+
+    // calculation min/max coordinates of airport area
+    Point3D min_deg(9999.0, 9999.0, 0), max_deg(-9999.0, -9999.0, 0);
+    for ( j = 0; j < (int)nodes.get_node_list().size(); ++j ) 
+    {
+        Point3D p = nodes.get_node_list()[j];
+        if ( p.lon() < min_deg.lon() ) 
+        {
+            min_deg.setlon( p.lon() );
+        }
+        if ( p.lon() > max_deg.lon() ) 
+        {
+            max_deg.setlon( p.lon() );
+        }
+        if ( p.lat() < min_deg.lat() ) 
+        {
+            min_deg.setlat( p.lat() );
+        }
+        if ( p.lat() > max_deg.lat() ) 
+        {
+            max_deg.setlat( p.lat() );
+        }
+    }
+
+#if 0
+    // extend the min/max coordinates of airport area to cover all
+    // lights as well
+    for ( i = 0; i < (int)rwy_lights.size(); ++i ) 
+    {
+        for ( j = 0;
+              j < (int)rwy_lights[i].get_poly().get_contour(0).size();
+              ++j )
+        {
+            Point3D p = rwy_lights[i].get_poly().get_contour(0)[j];
+            if ( p.lon() < min_deg.lon() ) 
+            {
+                min_deg.setlon( p.lon() );
+            }
+            if ( p.lon() > max_deg.lon() ) 
+            {
+                max_deg.setlon( p.lon() );
+            }
+            if ( p.lat() < min_deg.lat() ) 
+            {
+                min_deg.setlat( p.lat() );
+            }
+            if ( p.lat() > max_deg.lat() ) 
+            {
+                max_deg.setlat( p.lat() );
+            }
+        }
+    }
+#endif
+
+    // need newmat....
+
+    // Extend the area a bit so we don't have wierd things on the edges
+    double dlon = max_deg.lon() - min_deg.lon();
+    double dlat = max_deg.lat() - min_deg.lat();
+    min_deg.setlon( min_deg.lon() - 0.01 * dlon );
+    max_deg.setlon( max_deg.lon() + 0.01 * dlon );
+    min_deg.setlat( min_deg.lat() - 0.01 * dlat );
+    max_deg.setlat( max_deg.lat() + 0.01 * dlat );
+    SG_LOG(SG_GENERAL, SG_DEBUG, "min = " << min_deg << " max = " << max_deg );
+
+    TGAptSurface apt_surf( root, elev_src, min_deg, max_deg, average );
+    SG_LOG(SG_GENERAL, SG_INFO, "Airport surface created");
+
+    // calculate node elevations
+    SG_LOG(SG_GENERAL, SG_INFO, "Computing airport node elevations");
+    point_list geod_nodes = calc_elevations( apt_surf,
+                                             nodes.get_node_list(),
+                                             0.0 );
+    divided_base = calc_elevations( apt_surf, divided_base, 0.0 );
+
+    SG_LOG(SG_GENERAL, SG_DEBUG, "DIVIDED");
+    SG_LOG(SG_GENERAL, SG_DEBUG, divided_base);
+
+    SG_LOG(SG_GENERAL, SG_DEBUG, "Done with base calc_elevations()");
+
+#if 0
+    SG_LOG(SG_GENERAL, SG_INFO, "Computing beacon node elevations");
+    point_list beacon_nodes = calc_elevations( apt_surf, beacons, 0.0 );
+    SG_LOG(SG_GENERAL, SG_INFO, "Computing tower node elevations");
+    point_list tower_nodes = calc_elevations( apt_surf, towers, 0.0 );
+    SG_LOG(SG_GENERAL, SG_INFO, "Computing windsock node elevations");
+    point_list windsock_nodes = calc_elevations( apt_surf, windsocks, 0.0 );
+#endif
+
+    // add base skirt (to hide potential cracks)
+    //
+    // this has to happen after we've calculated the node elevations
+    // but before we convert to wgs84 coordinates
+
+    int uindex, lindex;
+
+    for ( i = 0; i < divided_base.contours(); ++i ) 
+    {
+	    strip_v.clear();
+	    strip_n.clear();
+	    strip_tc.clear();
+
+    	// prime the pump ...
+	    p = divided_base.get_pt( i, 0 );
+	    uindex = nodes.find( p );
+	    if ( uindex >= 0 ) 
+        {
+    	    Point3D lower = geod_nodes[uindex] - Point3D(0, 0, 20);
+    	    SG_LOG(SG_GENERAL, SG_DEBUG, geod_nodes[uindex] << " <-> " << lower);
+    	    lindex = nodes.simple_add( lower );
+    	    geod_nodes.push_back( lower );
+    	    strip_v.push_back( lindex );
+    	    strip_v.push_back( uindex );
+
+    	    // use 'the' normal.  We are pushing on two nodes so we
+    	    // need to push on two normals.
+    	    index = normals.unique_add( vn );
+    	    strip_n.push_back( index );
+    	    strip_n.push_back( index );
+     	} 
+        else 
+        {
+            string message = "Ooops missing node when building skirt (in init)";
+            SG_LOG( SG_GENERAL, SG_INFO, message << " " << p );
+    	    throw sg_exception( message );
+    	}
+
+    	// loop through the list
+    	for ( j = 1; j < divided_base.contour_size(i); ++j ) 
+        {
+    	    p = divided_base.get_pt( i, j );
+    	    uindex = nodes.find( p );
+    	    if ( uindex >= 0 ) 
+            {
+        		Point3D lower = geod_nodes[uindex] - Point3D(0, 0, 20);
+        		SG_LOG(SG_GENERAL, SG_DEBUG, geod_nodes[uindex] << " <-> " << lower);
+        		lindex = nodes.simple_add( lower );
+        		geod_nodes.push_back( lower );
+        		strip_v.push_back( lindex );
+        		strip_v.push_back( uindex );
+
+        		index = normals.unique_add( vn );
+        		strip_n.push_back( index );
+        		strip_n.push_back( index );
+    	    } 
+            else 
+            {
+                string message = "Ooops missing node when building skirt (in loop)";
+                SG_LOG( SG_GENERAL, SG_INFO, message << " " << p );
+                throw sg_exception( message );
+	        }
+	    }
+
+    	// close off the loop
+	    p = divided_base.get_pt( i, 0 );
+	    uindex = nodes.find( p );
+	    if ( uindex >= 0 ) 
+        {
+    	    Point3D lower = geod_nodes[uindex] - Point3D(0, 0, 20);
+    	    SG_LOG(SG_GENERAL, SG_DEBUG, geod_nodes[uindex] << " <-> " << lower);
+    	    lindex = nodes.simple_add( lower );
+    	    geod_nodes.push_back( lower );
+    	    strip_v.push_back( lindex );
+    	    strip_v.push_back( uindex );
+
+    	    index = normals.unique_add( vn );
+    	    strip_n.push_back( index );
+    	    strip_n.push_back( index );
+    	} 
+        else 
+        {
+            string message = "Ooops missing node when building skirt (at end)";
+            SG_LOG( SG_GENERAL, SG_INFO, message << " " << p );
+            throw sg_exception( message );
+    	}
+
+    	strips_v.push_back( strip_v );
+    	strips_n.push_back( strip_n );
+    	strip_materials.push_back( "Grass" );
+
+    	std::vector < SGGeod > geodNodes;
+    	for ( j = 0; j < nodes.get_node_list().size(); j++ ) 
+        {
+    	    Point3D node = nodes.get_node_list()[j];
+    	    geodNodes.push_back( SGGeod::fromDegM( node.x(), node.y(), node.z() ) );
+    	}
+	    base_txs.clear();
+	    base_txs = sgCalcTexCoords( b, geodNodes, strip_v );
+
+	    base_tc.clear();
+	    for ( j = 0; j < (int)base_txs.size(); ++j ) 
+        {
+    	    SGVec2f tc = base_txs[j];
+    	    // SG_LOG(SG_GENERAL, SG_DEBUG, "base_tc = " << tc);
+    	    index = texcoords.simple_add( Point3D( tc.x(), tc.y(), 0 ) );
+    	    base_tc.push_back( index );
+    	}
+    	strips_tc.push_back( base_tc );
+    }
+
+
+#if 0
+    // add light points
+
+    superpoly_list tmp_light_list; tmp_light_list.clear();
+    typedef map < string, double, less<string> > elev_map_type;
+    typedef elev_map_type::const_iterator const_elev_map_iterator;
+    elev_map_type elevation_map;
+
+    SG_LOG(SG_GENERAL, SG_INFO, "Computing runway/approach lighting elevations");
+
+    // pass one, calculate raw elevations from Array
+
+    for ( i = 0; i < (int)rwy_lights.size(); ++i ) 
+    {
+        TGTriNodes light_nodes;
+        light_nodes.clear();
+        point_list lights_v = rwy_lights[i].get_poly().get_contour(0);
+        for ( j = 0; j < (int)lights_v.size(); ++j ) 
+        {
+            p = lights_v[j];
+            index = light_nodes.simple_add( p );
+        }
+
+        // calculate light node elevations
+
+        point_list geod_light_nodes = calc_elevations( apt_surf, light_nodes.get_node_list(), 0.5 );
+        TGPolygon p;
+        p.add_contour( geod_light_nodes, 0 );
+        TGSuperPoly s;
+        s.set_poly( p );
+        tmp_light_list.push_back( s );
+
+        string flag = rwy_lights[i].get_flag();
+        if ( flag != (string)"" ) 
+        {
+            double max = -9999;
+            const_elev_map_iterator it = elevation_map.find( flag );
+            if ( it != elevation_map.end() ) 
+            {
+                max = elevation_map[flag];
+            }
+            for ( j = 0; j < (int)geod_light_nodes.size(); ++j ) 
+            {
+                if ( geod_light_nodes[j].z() > max ) 
+                {
+                    max = geod_light_nodes[j].z();
+                }
+            }
+            elevation_map[flag] = max;
+            SG_LOG( SG_GENERAL, SG_DEBUG, flag << " max = " << max );
+        }
+    }
+
+    SG_LOG(SG_GENERAL, SG_INFO, "Done with lighting calc_elevations()");
+
+    // pass two, for each light group check if we need to lift (based
+    // on flag) and do so, then output next structures.
+    for ( i = 0; i < (int)rwy_lights.size(); ++i ) 
+    {
+        // tmp_light_list is a parallel structure to rwy_lights
+        point_list geod_light_nodes = tmp_light_list[i].get_poly().get_contour(0);
+        
+        // this is a little round about, but what we want to calculate the
+        // light node elevations as ground + an offset so we do them
+        // seperately, then we add them back into nodes to get the index
+        // out, but also add them to geod_nodes to maintain consistancy
+        // between these two lists.
+        point_list light_normals = rwy_lights[i].get_normals().get_contour(0);
+        pt_v.clear();
+        pt_n.clear();
+        for ( j = 0; j < (int)geod_light_nodes.size(); ++j ) 
+        {
+            p = geod_light_nodes[j];
+            index = nodes.simple_add( p );
+            pt_v.push_back( index );
+            geod_nodes.push_back( p );
+
+            index = normals.unique_add( light_normals[j] );
+            pt_n.push_back( index );
+        }
+        pts_v.push_back( pt_v );
+        pts_n.push_back( pt_n );
+        pt_materials.push_back( rwy_lights[i].get_material() );
+    }
+#endif
+
+    // calculate wgs84 mapping of nodes
+    std::vector< SGVec3d > wgs84_nodes;
+    for ( i = 0; i < (int)geod_nodes.size(); ++i ) 
+    {
+        SGGeod geod = SGGeod::fromDegM( geod_nodes[i].x(), geod_nodes[i].y(), geod_nodes[i].z() );
+    	SG_LOG(SG_GENERAL, SG_DEBUG, "geod pt = " << geod_nodes[i] );
+        SGVec3d cart = SGVec3d::fromGeod(geod);
+        SG_LOG(SG_GENERAL, SG_DEBUG, "  cart pt = " << cart );
+    	wgs84_nodes.push_back( cart );
+    }
+    SGSphered d;
+    for ( i = 0; i < wgs84_nodes.size(); ++i ) 
+    {
+        d.expandBy(wgs84_nodes[ i ]);
+    }
+    
+    SGVec3d gbs_center = d.getCenter();
+    double gbs_radius = d.getRadius();
+    SG_LOG(SG_GENERAL, SG_DEBUG, "gbs center = " << gbs_center);
+    SG_LOG(SG_GENERAL, SG_DEBUG, "Done with wgs84 node mapping");
+    SG_LOG(SG_GENERAL, SG_DEBUG, "  center = " << gbs_center << " radius = " << gbs_radius );
+
+    // null structures
+    group_list fans_v; fans_v.clear();
+    group_list fans_n; fans_n.clear();
+    group_list fans_tc; fans_tc.clear();
+    string_list fan_materials; fan_materials.clear();
+
+    string objpath = root + "/AirportObj";
+    string name = icao + ".btg";
+    
+    std::vector< SGVec3f > normals_3f;
+    for ( i=0; i < normals.get_node_list().size(); i++ ) 
+    {
+        Point3D node = normals.get_node_list()[i];
+        normals_3f.push_back( node.toSGVec3f() );
+    }
+
+    std::vector< SGVec2f > texcoords_2f;
+    for ( i=0; i < texcoords.get_node_list().size(); i++ ) 
+    {
+        Point3D node = texcoords.get_node_list()[i];
+        texcoords_2f.push_back( node.toSGVec2f() );
+    }
+
+    SGBinObject obj;
+
+    obj.set_gbs_center( gbs_center );
+    obj.set_gbs_radius( gbs_radius );
+    obj.set_wgs84_nodes( wgs84_nodes );
+    obj.set_normals( normals_3f );
+    obj.set_texcoords( texcoords_2f );
+    obj.set_pts_v( pts_v );
+    obj.set_pts_n( pts_n );
+    obj.set_pt_materials( pt_materials );
+    obj.set_tris_v( tris_v );
+    obj.set_tris_n( tris_n );
+    obj.set_tris_tc( tris_tc ); 
+    obj.set_tri_materials( tri_materials );
+    obj.set_strips_v( strips_v );
+    obj.set_strips_n( strips_n );
+    obj.set_strips_tc( strips_tc ); 
+    obj.set_strip_materials( strip_materials );
+    obj.set_fans_v( fans_v );
+    obj.set_fans_n( fans_n );
+    obj.set_fans_tc( fans_tc );
+    obj.set_fan_materials( fan_materials );
+
+    bool result;
+    result = obj.write_bin( objpath, name, b );
+    if ( !result ) 
+    {
+        throw sg_exception("error writing file. :-(");
+    }
+
+    // write out airport object reference
+    write_index( objpath, b, name );
+
+#if 0
+    // write out beacon references
+    for ( i = 0; i < (int)beacon_nodes.size(); ++i ) 
+    {
+        write_index_shared( objpath, b, beacon_nodes[i],
+                            "Models/Airport/beacon.xml",
+                            0.0 );
+    }
+
+    // write out tower references
+    for ( i = 0; i < (int)tower_nodes.size(); ++i ) 
+    {
+        write_index_shared( objpath, b, tower_nodes[i],
+                            "Models/Airport/tower.xml",
+                            0.0 );
+    }
+
+    // write out windsock references
+    for ( i = 0; i < (int)windsock_nodes.size(); ++i ) 
+    {
+    	if ( windsock_types[i] == 0 ) 
+        {
+            write_index_shared( objpath, b, windsock_nodes[i],
+                                "Models/Airport/windsock.xml",
+                                0.0 );
+    	} 
+        else 
+        {
+            write_index_shared( objpath, b, windsock_nodes[i],
+                                "Models/Airport/windsock_lit.xml",
+                                0.0 );
+    	}
+    }
+#endif
+
+    string holepath = root + "/AirportArea";
+    // long int poly_index = poly_index_next();
+    // write_boundary( holepath, b, hull, poly_index );
+    tgChopNormalPolygon( holepath, "Hole", divided_base, true );
+    tgChopNormalPolygon( holepath, "Airport", apt_clearing, false );
+}
+
+
+#if 0 // RFERENCE from GenApts....
+
+// build 3d airport
+void build_airport( string airport_id, float alt_m,
+                    string_list& runways_raw,
+                    string_list& beacons_raw,
+                    string_list& towers_raw,
+                    string_list& windsocks_raw,                    
+                    const string& root,
+                    const string_list& elev_src )
+{
+    int i, j, k;
+
+    superpoly_list rwy_polys;
+    texparams_list texparams;
+
+    // poly_list rwy_tris, rwy_txs;
+    TGPolygon runway, runway_a, runway_b, clipped_a, clipped_b;
+    TGPolygon split_a, split_b;
+    TGPolygon apt_base;
+    TGPolygon apt_clearing;
+    Point3D p;
+
+    TGPolygon accum;
+    accum.erase();
+
+    // parse main airport information
+    double apt_lon = 0.0, apt_lat = 0.0;
+    int rwy_count = 0;
+
+    SG_LOG( SG_GENERAL, SG_INFO, "Building " << airport_id );
+
+    // parse runways/taxiways and generate the vertex list
+    runway_list runways; runways.clear();
+    runway_list taxiways; taxiways.clear();
+
+    for ( i = 0; i < (int)runways_raw.size(); ++i ) {
+        ++rwy_count;
+
+	string rwy_str = runways_raw[i];
+        vector<string> token = simgear::strutils::split( rwy_str );
+
+	TGRunway rwy;
+
+	SG_LOG(SG_GENERAL, SG_DEBUG, rwy_str);
+	rwy.rwy_no = token[3];
+        rwy.really_taxiway = (rwy.rwy_no == "xxx");
+        rwy.generated = false;
+
+	rwy.lat = atof( token[1].c_str() );
+        apt_lat += rwy.lat;
+
+	rwy.lon = atof( token[2].c_str() );
+        apt_lon += rwy.lon;
+
+	rwy.heading = atof( token[4].c_str() );
+
+	rwy.length = atoi( token[5].c_str() );
+	rwy.width = atoi( token[8].c_str() );
+
+
+        string rwy_displ_threshold = token[6];
+        vector<string> displ
+            = simgear::strutils::split( rwy_displ_threshold, "." );
+        rwy.disp_thresh1 = atoi( displ[0].c_str() );
+        rwy.disp_thresh2 = atoi( displ[1].c_str() );
+
+        string rwy_stopway = token[7];
+        vector<string> stop
+            = simgear::strutils::split( rwy_stopway, "." );
+        rwy.stopway1 = atoi( stop[0].c_str() );
+        rwy.stopway2 = atoi( stop[1].c_str() );
+
+	rwy.lighting_flags = token[9];
+	rwy.surface_code = atoi( token[10].c_str() );
+	rwy.shoulder_code = token[11];
+        rwy.marking_code = atoi( token[12].c_str() );
+        rwy.smoothness = atof( token[13].c_str() );
+        rwy.dist_remaining = (atoi( token[14].c_str() ) == 1 );
+
+	if (token.size()>15) {
+		string vasi_angles = token[15];
+		vector<string> vasis = simgear::strutils::split( vasi_angles, "." );
+		rwy.gs_angle1 = atof( vasis[0].c_str() ) * 0.01;
+		rwy.gs_angle2 = atof( vasis[1].c_str() ) * 0.01;
+	} else {
+		rwy.gs_angle1 = rwy.gs_angle2 = 3.0;
+	}
+
+	SG_LOG( SG_GENERAL, SG_DEBUG, "  no    = " << rwy.rwy_no);
+	SG_LOG( SG_GENERAL, SG_DEBUG, "  lat   = " << rwy.lat);
+	SG_LOG( SG_GENERAL, SG_DEBUG, "  lon   = " << rwy.lon);
+	SG_LOG( SG_GENERAL, SG_DEBUG, "  hdg   = " << rwy.heading);
+	SG_LOG( SG_GENERAL, SG_DEBUG, "  len   = " << rwy.length);
+	SG_LOG( SG_GENERAL, SG_DEBUG, "  width = " << rwy.width);
+	SG_LOG( SG_GENERAL, SG_DEBUG, "  lighting = " << rwy.lighting_flags);
+	SG_LOG( SG_GENERAL, SG_DEBUG, "  sfc   = " << rwy.surface_code);
+	SG_LOG( SG_GENERAL, SG_DEBUG, "  mrkgs  = " << rwy.marking_code);
+        SG_LOG( SG_GENERAL, SG_DEBUG, "  dspth1= " << rwy.disp_thresh1);
+        SG_LOG( SG_GENERAL, SG_DEBUG, "  stop1 = " << rwy.stopway1);
+        SG_LOG( SG_GENERAL, SG_DEBUG, "  dspth2= " << rwy.disp_thresh2);
+        SG_LOG( SG_GENERAL, SG_DEBUG, "  stop2 = " << rwy.stopway2);
+
+        if ( rwy.really_taxiway ) {
+            taxiways.push_back( rwy );
+        } else {
+            runways.push_back( rwy );
+        }
+    }
+    SG_LOG(SG_GENERAL, SG_INFO, "Runway count = " << runways.size() );
+    SG_LOG(SG_GENERAL, SG_INFO, "Taxiway count = " << taxiways.size() );
+
+
+    SGBucket b( apt_lon / (double)rwy_count, apt_lat / (double)rwy_count );
+    SG_LOG(SG_GENERAL, SG_INFO, b.gen_base_path() << "/" << b.gen_index_str());
+
+    point_list beacons; beacons.clear();
+    for ( i = 0; i < (int)beacons_raw.size(); ++i ) {
+	string beacon_str = beacons_raw[i];
+        vector<string> token = simgear::strutils::split( beacon_str );
+
+	Point3D beacon;
+
+	SG_LOG(SG_GENERAL, SG_INFO, beacon_str);
+
+	beacon.setlat( atof( token[1].c_str() ) );
+	beacon.setlon( atof( token[2].c_str() ) );
+
+	SG_LOG( SG_GENERAL, SG_DEBUG, "  beacon   = " << beacon );
+
+	beacons.push_back( beacon );
+    }
+
+    point_list towers; towers.clear();
+    for ( i = 0; i < (int)towers_raw.size(); ++i ) {
+	string tower_str = towers_raw[i];
+        vector<string> token = simgear::strutils::split( tower_str );
+
+	Point3D tower;
+
+	SG_LOG(SG_GENERAL, SG_INFO, tower_str);
+
+	tower.setlat( atof( token[1].c_str() ) );
+	tower.setlon( atof( token[2].c_str() ) );
+
+	if (!atoi(token[4].c_str())) {
+		// Ralf Gerlich: Skip towers that shall not be drawn
+		continue;
+	}
+
+	SG_LOG( SG_GENERAL, SG_DEBUG, "  tower   = " << tower );
+
+	towers.push_back( tower );
+    }
+
+    point_list windsocks; windsocks.clear();
+    int_list windsock_types; windsock_types.clear();
+    for ( i = 0; i < (int)windsocks_raw.size(); ++i ) {
+	string windsock_str = windsocks_raw[i];
+        vector<string> token = simgear::strutils::split( windsock_str );
+
+	Point3D windsock;
+
+	SG_LOG(SG_GENERAL, SG_INFO, windsock_str);
+
+	windsock.setlat( atof( token[1].c_str() ) );
+	windsock.setlon( atof( token[2].c_str() ) );
+
+	string windsock_type = token[3];
+
+	SG_LOG( SG_GENERAL, SG_DEBUG, "  windsock   = " << windsock );
+
+	windsocks.push_back( windsock );
+	if ( windsock_type == "0" ) {
+	    windsock_types.push_back( 0 );
+        } else {
+	    windsock_types.push_back( 1 );
+	}
+    }
+
+    TGSuperPoly sp;
+    TGTexParams tp;
+
+
+    // First pass: generate the precision runways since these have
+    // precidence
+    for ( i = 0; i < (int)runways.size(); ++i ) {
+	if ( runways[i].marking_code == 3 /* Precision */ ) {
+	    build_runway( runways[i], alt_m,
+			  &rwy_polys, &texparams, &accum,
+                          &apt_base, &apt_clearing );
+	}
+    }
+
+    // 2nd pass: generate the non-precision and visual runways
+    for ( i = 0; i < (int)runways.size(); ++i ) {
+	if ( runways[i].marking_code == 2 /* Non-precision */
+             || runways[i].marking_code == 1 /* Visual */ )
+        {
+            if ( runways[i].surface_code != 13 /* Water */ ) {
+                // only build non-water runways
+                build_runway( runways[i], alt_m,
+                              &rwy_polys, &texparams, &accum,
+                              &apt_base, &apt_clearing );
+            }
+        }
+    }
+
+    // 3rd pass: generate all remaining runways not covered in the first pass
+    for ( i = 0; i < (int)runways.size(); ++i ) {
+	if ( runways[i].marking_code != 3 /* Precision */
+             && runways[i].marking_code != 2 /* Non-precision */
+             && runways[i].marking_code != 1 /* Visual */ )
+        {
+            if ( runways[i].surface_code != 6 /* Asphalt Helipad */ &&
+                 runways[i].surface_code != 7 /* Concrete Helipad */ &&
+                 runways[i].surface_code != 8 /* Turf Helipad */ &&
+                 runways[i].surface_code != 9 /* Dirt Helipad */ &&
+                 runways[i].surface_code != 13 /* Water/buoy runway */ )
+            {
+                // only build non-water and non-heliport runways
+                build_runway( runways[i], alt_m,
+                              &rwy_polys, &texparams, &accum,
+                              &apt_base, &apt_clearing );
+            }
+        }
+    }
+
+    // 4th pass: generate all taxiways
+
+#if 0
+    // we want to generate in order of largest size first so this will
+    // look a little weird, but that's all I'm doing, otherwise a
+    // simple list traversal would work fine.
+    bool done = false;
+    while ( !done ) {
+        // find the largest taxiway
+        int largest_idx = -1;
+        double max_size = 0;
+        for ( i = 0; i < (int)taxiways.size(); ++i ) {
+            SG_LOG( SG_GENERAL, SG_DEBUG, "taxiway i = " << i );
+            double size = taxiways[i].length * taxiways[i].width;
+            if ( size > max_size && !taxiways[i].generated ) {
+                SG_LOG( SG_GENERAL, SG_DEBUG, "taxiway max i = " << i );
+                max_size = size;
+                largest_idx = i;
+            }
+        }
+
+        if ( largest_idx >= 0 ) {
+            SG_LOG( SG_GENERAL, SG_DEBUG, "generating " << largest_idx );
+            build_runway( taxiways[largest_idx], alt_m,
+                          &rwy_polys, &texparams, &accum,
+                          &apt_base, &apt_clearing );
+            taxiways[largest_idx].generated = true;
+        } else {
+            SG_LOG( SG_GENERAL, SG_DEBUG, "done with taxiways." );
+            done = true;
+        }
+    }
+#else
+    /* Ralf Gerlich: Generate Taxiways in specified order from bottom to top */
+    for ( i=0; i<taxiways.size(); ++i ) {
+            SG_LOG( SG_GENERAL, SG_DEBUG, "generating " << i );
+            build_runway( taxiways[i], alt_m,
+                          &rwy_polys, &texparams, &accum,
+                          &apt_base, &apt_clearing );
+            taxiways[i].generated = true;
+    }
+#endif
+
+    // Now generate small surface for each beacon
+    TGPolygon obj_base, obj_safe_base;
+    double obj_hdg = runways[0].heading;
+    for ( i = 0; i < (int)beacons.size(); ++i ) {
+        obj_base = gen_wgs84_area( beacons[i], 20.0, 0.0, 0.0, 20.0,
+                                   obj_hdg, alt_m, false );
+        obj_safe_base = gen_wgs84_area( beacons[i], 40.0, 0.0, 0.0, 40.0,
+                                        obj_hdg, alt_m, false );
+        
+        apt_base = tgPolygonUnion( obj_base, apt_base );
+        apt_clearing = tgPolygonUnion( obj_safe_base, apt_clearing );
+    }
+
+    // Now generate small surface for each tower
+    for ( i = 0; i < (int)towers.size(); ++i ) {
+        obj_base = gen_wgs84_area( towers[i], 20.0, 0.0, 0.0, 20.0,
+                                   obj_hdg, alt_m, false );
+        obj_safe_base = gen_wgs84_area( towers[i], 40.0, 0.0, 0.0, 40.0,
+                                        obj_hdg, alt_m, false );
+        
+        apt_base = tgPolygonUnion( obj_base, apt_base );
+        apt_clearing = tgPolygonUnion( obj_safe_base, apt_clearing );
+    }
+
+    // Now generate small surface for each windsock
+    for ( i = 0; i < (int)windsocks.size(); ++i ) {
+        obj_base = gen_wgs84_area( windsocks[i], 20.0, 0.0, 0.0, 20.0,
+                                   obj_hdg, alt_m, false );
+        obj_safe_base = gen_wgs84_area( windsocks[i], 40.0, 0.0, 0.0, 40.0,
+                                        obj_hdg, alt_m, false );
+        
+        apt_base = tgPolygonUnion( obj_base, apt_base );
+        apt_clearing = tgPolygonUnion( obj_safe_base, apt_clearing );
+    }
+
+    // 5th pass: generate runway lights
+    superpoly_list rwy_lights; rwy_lights.clear();
+    for ( i = 0; i < (int)runways.size(); ++i ) {
+	gen_runway_lights( runways[i], alt_m, rwy_lights, &apt_base );
+    }
+
+    // 6th pass: generate all taxiway lights
+    for ( i = 0; i < (int)taxiways.size(); ++i ) {
+        gen_taxiway_lights( taxiways[i], alt_m, rwy_lights );
+    }
+
+    // write_polygon( accum, "accum" );
+    // write_polygon( apt_base, "base" );
+    // write_polygon( apt_clearing, "clear" );
+    if ( apt_base.total_size() == 0 ) {
+        SG_LOG(SG_GENERAL, SG_ALERT, "no airport points generated");
+	return;
+    }
+
+    // generate convex hull (no longer)
+    // TGPolygon hull = convex_hull(apt_pts);
+
+    TGPolygon filled_base = tgPolygonStripHoles( apt_base );
+    // write_polygon( filled_base, "base" );
+    TGPolygon divided_base = tgPolygonSplitLongEdges( filled_base, 200.0 );
+    // write_polygon( divided_base, "divided-base" );
+    TGPolygon base_poly = tgPolygonDiff( divided_base, accum );
+    // write_polygon( base_poly, "base-raw" );
+
+    char buf[120];  // For debugging output
+    // Try to remove duplicated nodes and other degeneracies
+    for ( k = 0; k < (int)rwy_polys.size(); ++k ) 
+    {
+    	SG_LOG(SG_GENERAL, SG_DEBUG, "add nodes/remove dups section = " << k << " " << rwy_polys[k].get_material());
+    	TGPolygon poly = rwy_polys[k].get_poly();
+	    SG_LOG(SG_GENERAL, SG_DEBUG, "total size before = " << poly.total_size());
+	    for ( i = 0; i < poly.contours(); ++i ) 
+        {
+	        for ( j = 0; j < poly.contour_size(i); ++j ) 
+            {
+        		Point3D tmp = poly.get_pt(i, j);
+        		snprintf(buf, 119, "  %.7f %.7f %.7f\n", tmp.x(), tmp.y(), tmp.z() );
+        		SG_LOG(SG_GENERAL, SG_DEBUG, buf);
+    	    }
+    	}
+
+    	poly = remove_dups( poly );
+	    SG_LOG(SG_GENERAL, SG_DEBUG, "total size after remove_dups() = " << poly.total_size());
+
+    	for ( i = 0; i < poly.contours(); ++i ) 
+        {
+    	    for ( j = 0; j < poly.contour_size(i); ++j ) 
+            {
+        		Point3D tmp = poly.get_pt(i, j);
+        		snprintf(buf, 119, "    %.7f %.7f %.7f\n", tmp.x(), tmp.y(), tmp.z() );
+        		SG_LOG(SG_GENERAL, SG_DEBUG, buf);
+    	    }
+    	}
+
+    	poly = reduce_degeneracy( poly );
+    	SG_LOG(SG_GENERAL, SG_DEBUG, "total size after reduce_degeneracy() = " << poly.total_size());
+
+    	for ( i = 0; i < poly.contours(); ++i ) 
+        {
+    	    for ( j = 0; j < poly.contour_size(i); ++j ) 
+            {
+        		Point3D tmp = poly.get_pt(i, j);
+        		snprintf(buf, 119, "    %.7f %.7f %.7f\n", tmp.x(), tmp.y(), tmp.z() );
+        		SG_LOG(SG_GENERAL, SG_DEBUG, buf);
+    	    }
+    	}
+
+    	rwy_polys[k].set_poly( poly );
+    }
+
+**********************************************************************************************************************************************************************************************************************
+    // I  AM HERE : I need to start creating the airport base
+
+    // add segments to polygons to remove any possible "T"
+    // intersections
+    TGTriNodes tmp_nodes;
+
+    // build temporary node list
+    for ( k = 0; k < (int)rwy_polys.size(); ++k ) 
+    {
+    	TGPolygon poly = rwy_polys[k].get_poly();
+    	for ( i = 0; i < poly.contours(); ++i ) 
+        {
+    	    for ( j = 0; j < poly.contour_size( i ); ++j ) 
+            {
+        		tmp_nodes.unique_add( poly.get_pt(i, j) );
+    	    }
+    	}
+    }
+    for ( i = 0; i < base_poly.contours(); ++i ) 
+    {
+    	for ( j = 0; j < base_poly.contour_size( i ); ++j ) 
+        {
+    	    tmp_nodes.unique_add( base_poly.get_pt(i, j) );
+    	}
+    }
+
+    // the divided base could contain points not found in base_poly,
+    // so we should add them because the skirt needs them.
+    for ( i = 0; i < divided_base.contours(); ++i ) 
+    {
+	    for ( j = 0; j < divided_base.contour_size( i ); ++j ) 
+        {
+    	    tmp_nodes.unique_add( divided_base.get_pt(i, j) );
+    	}
+    }
+
+    for ( k = 0; k < (int)rwy_polys.size(); ++k ) 
+    {
+    	TGPolygon poly = rwy_polys[k].get_poly();
+    	poly = add_nodes_to_poly( poly, tmp_nodes );
+    	SG_LOG(SG_GENERAL, SG_DEBUG, "total size after add nodes = " << poly.total_size());
+
+    	rwy_polys[k].set_poly( poly );
+    }
+
+    // One more pass to try to get rid of other yukky stuff
+    for ( k = 0; k < (int)rwy_polys.size(); ++k ) 
+    {
+    	TGPolygon poly = rwy_polys[k].get_poly();
+
+    	SG_LOG(SG_GENERAL, SG_DEBUG, "total size of section " << k << " before =" << poly.total_size());
+
+        poly = remove_dups( poly );
+    	SG_LOG(SG_GENERAL, SG_DEBUG, "total size after remove_dups() = " << poly.total_size());
+        poly = remove_bad_contours( poly );
+    	SG_LOG(SG_GENERAL, SG_DEBUG, "total size after remove_bad() = " << poly.total_size());
+        poly = remove_tiny_contours( poly );
+    	SG_LOG(SG_GENERAL, SG_DEBUG, "total size after remove_tiny_contours() = " << poly.total_size());
+
+    	rwy_polys[k].set_poly( poly );
+    }
+
+    SG_LOG(SG_GENERAL, SG_DEBUG, "add nodes base ");
+    SG_LOG(SG_GENERAL, SG_DEBUG, " before: " << base_poly);
+    SG_LOG(SG_GENERAL, SG_DEBUG, " tmp_nodes size = " << tmp_nodes.get_node_list().size());
+
+    base_poly = add_nodes_to_poly( base_poly, tmp_nodes );
+    SG_LOG(SG_GENERAL, SG_DEBUG, " after adding tmp_nodes: " << base_poly);
+
+    // write_polygon( base_poly, "base-add" );
+    SG_LOG(SG_GENERAL, SG_DEBUG, "remove dups base ");
+    base_poly = remove_dups( base_poly );
+    SG_LOG(SG_GENERAL, SG_DEBUG, "remove bad contours base");
+    base_poly = remove_bad_contours( base_poly );
+    SG_LOG(SG_GENERAL, SG_DEBUG, "remove small contours base");
+    base_poly = remove_tiny_contours( base_poly );
+    // write_polygon( base_poly, "base-fin" );
+    SG_LOG(SG_GENERAL, SG_DEBUG, " after clean up: " << base_poly);
+
+    // tesselate the polygons and prepair them for final output
+
+    for ( i = 0; i < (int)rwy_polys.size(); ++i ) 
+    {
+        SG_LOG(SG_GENERAL, SG_DEBUG, "Tesselating section = " << i << " flag = " << rwy_polys[i].get_flag());
+
+    	TGPolygon poly = rwy_polys[i].get_poly();
+	    SG_LOG(SG_GENERAL, SG_DEBUG, "total size before = " << poly.total_size());
+	    TGPolygon tri = polygon_tesselate_alt( poly );
+	    SG_LOG(SG_GENERAL, SG_DEBUG, "total size after = " << tri.total_size());
+
+        TGPolygon tc;
+        if ( rwy_polys[i].get_flag() == "taxi" ) 
+        {
+            SG_LOG(SG_GENERAL, SG_DEBUG, "taxiway, no clip");
+            tc = rwy_section_tex_coords( tri, texparams[i], false );
+        } 
+        else 
+        {
+            tc = rwy_section_tex_coords( tri, texparams[i], true );
+        }
+
+    	rwy_polys[i].set_tris( tri );
+	    rwy_polys[i].set_texcoords( tc );
+	    rwy_polys[i].set_tri_mode( GL_TRIANGLES );
+    }
+
+    SG_LOG(SG_GENERAL, SG_DEBUG, "Tesselating base");
+    TGPolygon base_tris = polygon_tesselate_alt( base_poly );
+
+    //
+    // We should now have the runway polygons all generated with their
+    // corresponding triangles and texture coordinates, and the
+    // surrounding base area.
+    //
+    // Next we need to create the output lists ... vertices, normals,
+    // texture coordinates, and tri-strips
+    //
+
+    // traverse the tri list and create ordered node and texture
+    // coordinate lists
+
+    TGTriNodes nodes, normals, texcoords;
+    nodes.clear();
+    normals.clear();
+    texcoords.clear();
+
+    group_list pts_v; pts_v.clear();
+    group_list pts_n; pts_n.clear();
+    string_list pt_materials; pt_materials.clear();
+
+    group_list tris_v; tris_v.clear();
+    group_list tris_n; tris_n.clear();
+    group_list tris_tc; tris_tc.clear();
+    string_list tri_materials; tri_materials.clear();
+
+    group_list strips_v; strips_v.clear();
+    group_list strips_n; strips_n.clear();
+    group_list strips_tc; strips_tc.clear();
+    string_list strip_materials; strip_materials.clear();
+
+    int index;
+    int_list pt_v, tri_v, strip_v;
+    int_list pt_n, tri_n, strip_n;
+    int_list tri_tc, strip_tc;
+
+    // calculate "the" normal for this airport
+    p.setx( base_tris.get_pt(0, 0).x() * SGD_DEGREES_TO_RADIANS );
+    p.sety( base_tris.get_pt(0, 0).y() * SGD_DEGREES_TO_RADIANS );
+    p.setz( 0 );
+    Point3D vnt = sgGeodToCart( p );
+    
+    // SG_LOG(SG_GENERAL, SG_DEBUG, "geod = " << p);
+    // SG_LOG(SG_GENERAL, SG_DEBUG, "cart = " << tmp);
+
+    sgdVec3 tmp;
+    sgdSetVec3( tmp, vnt.x(), vnt.y(), vnt.z() );
+    sgdNormalizeVec3( tmp );
+
+    Point3D vn( tmp[0], tmp[1], tmp[2] );
+    SG_LOG(SG_GENERAL, SG_DEBUG, "found normal for this airport = " << tmp);
+
+    for ( k = 0; k < (int)rwy_polys.size(); ++k ) 
+    {
+    	SG_LOG(SG_GENERAL, SG_DEBUG, "tri " << k);
+    	// TGPolygon tri_poly = rwy_tris[k];
+    	TGPolygon tri_poly = rwy_polys[k].get_tris();
+    	TGPolygon tri_txs = rwy_polys[k].get_texcoords();
+    	string material = rwy_polys[k].get_material();
+    	SG_LOG(SG_GENERAL, SG_DEBUG, "material = " << material);
+    	SG_LOG(SG_GENERAL, SG_DEBUG, "poly size = " << tri_poly.contours());
+    	SG_LOG(SG_GENERAL, SG_DEBUG, "texs size = " << tri_txs.contours());
+    	for ( i = 0; i < tri_poly.contours(); ++i ) 
+        {
+    	    tri_v.clear();
+    	    tri_n.clear();
+    	    tri_tc.clear();
+    	    for ( j = 0; j < tri_poly.contour_size(i); ++j ) 
+            {
+        		p = tri_poly.get_pt( i, j );
+        		index = nodes.unique_add( p );
+        		tri_v.push_back( index );
+
+        		// use 'the' normal
+        		index = normals.unique_add( vn );
+        		tri_n.push_back( index );
+
+        		Point3D tc = tri_txs.get_pt( i, j );
+        		index = texcoords.unique_add( tc );
+        		tri_tc.push_back( index );
+    	    }
+    	    tris_v.push_back( tri_v );
+    	    tris_n.push_back( tri_n );
+    	    tris_tc.push_back( tri_tc );
+    	    tri_materials.push_back( material );
+    	}
+    }
+
+    // add base points
+    std::vector< SGVec2f > base_txs; 
+    int_list base_tc;
+    for ( i = 0; i < base_tris.contours(); ++i ) 
+    {
+    	tri_v.clear();
+    	tri_n.clear();
+    	tri_tc.clear();
+    	for ( j = 0; j < base_tris.contour_size(i); ++j ) 
+        {
+    	    p = base_tris.get_pt( i, j );
+    	    index = nodes.unique_add( p );
+    	    tri_v.push_back( index );
+
+    	    index = normals.unique_add( vn );
+    	    tri_n.push_back( index);
+    	}
+    	tris_v.push_back( tri_v );
+    	tris_n.push_back( tri_n );
+    	tri_materials.push_back( "Grass" );
+
+    	std::vector < SGGeod > geodNodes;
+    	for ( j = 0; j < nodes.get_node_list().size(); j++ ) 
+        {
+    	    Point3D node = nodes.get_node_list()[j];
+    	    geodNodes.push_back( SGGeod::fromDegM( node.x(), node.y(), node.z() ) );
+    	}
+	    base_txs.clear();
+    	base_txs = sgCalcTexCoords( b, geodNodes, tri_v );
+
+    	base_tc.clear();
+    	for ( j = 0; j < (int)base_txs.size(); ++j ) 
+        {
+    	    SGVec2f tc = base_txs[j];
+    	    // SG_LOG(SG_GENERAL, SG_DEBUG, "base_tc = " << tc);
+    	    index = texcoords.simple_add( Point3D( tc.x(), tc.y(), 0 ) );
+    	    base_tc.push_back( index );
+    	}
+    	tris_tc.push_back( base_tc );
+    }
+
+    // on rare occasion, one or more of the divided base points can be
+    // missed.  Make sure they are all in the node list so we can
+    // build a proper skirt.
+
+    for ( i = 0; i < divided_base.contours(); ++i ) 
+    {
+    	for ( j = 0; j < divided_base.contour_size( i ); ++j ) 
+        {
+    	    nodes.unique_add( divided_base.get_pt(i, j) );
+    	}
+    }
+
+    // Now that we have assembled all the airport geometry nodes into
+    // a list, calculate an "average" airport elevation based on all
+    // the actual airport node points.  This is more useful than
+    // calculating an average over the entire airport surface because
+    // it avoids biases introduced from the surrounding area if the
+    // airport is located in a bowl or on a hill.
+
+    double average = tgAverageElevation( root, elev_src, nodes.get_node_list() );
+    // cout << "average airport elevation = " << average << endl;
+
+    // Now build the fitted airport surface ...
+
+    // calculation min/max coordinates of airport area
+    Point3D min_deg(9999.0, 9999.0, 0), max_deg(-9999.0, -9999.0, 0);
+    for ( j = 0; j < (int)nodes.get_node_list().size(); ++j ) 
+    {
+        Point3D p = nodes.get_node_list()[j];
+        if ( p.lon() < min_deg.lon() ) 
+        {
+            min_deg.setlon( p.lon() );
+        }
+        if ( p.lon() > max_deg.lon() ) 
+        {
+            max_deg.setlon( p.lon() );
+        }
+        if ( p.lat() < min_deg.lat() ) 
+        {
+            min_deg.setlat( p.lat() );
+        }
+        if ( p.lat() > max_deg.lat() ) 
+        {
+            max_deg.setlat( p.lat() );
+        }
+    }
+
+    // extend the min/max coordinates of airport area to cover all
+    // lights as well
+    for ( i = 0; i < (int)rwy_lights.size(); ++i ) 
+    {
+        for ( j = 0;
+              j < (int)rwy_lights[i].get_poly().get_contour(0).size();
+              ++j )
+        {
+            Point3D p = rwy_lights[i].get_poly().get_contour(0)[j];
+            if ( p.lon() < min_deg.lon() ) 
+            {
+                min_deg.setlon( p.lon() );
+            }
+            if ( p.lon() > max_deg.lon() ) 
+            {
+                max_deg.setlon( p.lon() );
+            }
+            if ( p.lat() < min_deg.lat() ) 
+            {
+                min_deg.setlat( p.lat() );
+            }
+            if ( p.lat() > max_deg.lat() ) 
+            {
+                max_deg.setlat( p.lat() );
+            }
+        }
+    }
+
+    // Extend the area a bit so we don't have wierd things on the edges
+    double dlon = max_deg.lon() - min_deg.lon();
+    double dlat = max_deg.lat() - min_deg.lat();
+    min_deg.setlon( min_deg.lon() - 0.01 * dlon );
+    max_deg.setlon( max_deg.lon() + 0.01 * dlon );
+    min_deg.setlat( min_deg.lat() - 0.01 * dlat );
+    max_deg.setlat( max_deg.lat() + 0.01 * dlat );
+    cout << "min = " << min_deg << " max = " << max_deg << endl;
+
+    TGAptSurface apt_surf( root, elev_src, min_deg, max_deg, average );
+    SG_LOG(SG_GENERAL, SG_INFO, "Airport surface created");
+
+    // calculate node elevations
+    SG_LOG(SG_GENERAL, SG_INFO, "Computing airport node elevations");
+    point_list geod_nodes = calc_elevations( apt_surf,
+                                             nodes.get_node_list(),
+                                             0.0 );
+    divided_base = calc_elevations( apt_surf, divided_base, 0.0 );
+    SG_LOG(SG_GENERAL, SG_DEBUG, "DIVIDED");
+    SG_LOG(SG_GENERAL, SG_DEBUG, divided_base);
+
+    SG_LOG(SG_GENERAL, SG_DEBUG, "Done with base calc_elevations()");
+
+    SG_LOG(SG_GENERAL, SG_INFO, "Computing beacon node elevations");
+    point_list beacon_nodes = calc_elevations( apt_surf, beacons, 0.0 );
+    SG_LOG(SG_GENERAL, SG_INFO, "Computing tower node elevations");
+    point_list tower_nodes = calc_elevations( apt_surf, towers, 0.0 );
+    SG_LOG(SG_GENERAL, SG_INFO, "Computing windsock node elevations");
+    point_list windsock_nodes = calc_elevations( apt_surf, windsocks, 0.0 );
+
+    // add base skirt (to hide potential cracks)
+    //
+    // this has to happen after we've calculated the node elevations
+    // but before we convert to wgs84 coordinates
+    int uindex, lindex;
+
+    for ( i = 0; i < divided_base.contours(); ++i ) 
+    {
+	    strip_v.clear();
+	    strip_n.clear();
+	    strip_tc.clear();
+
+    	// prime the pump ...
+	    p = divided_base.get_pt( i, 0 );
+	    uindex = nodes.find( p );
+	    if ( uindex >= 0 ) 
+        {
+    	    Point3D lower = geod_nodes[uindex] - Point3D(0, 0, 20);
+    	    SG_LOG(SG_GENERAL, SG_DEBUG, geod_nodes[uindex] << " <-> " << lower);
+    	    lindex = nodes.simple_add( lower );
+    	    geod_nodes.push_back( lower );
+    	    strip_v.push_back( lindex );
+    	    strip_v.push_back( uindex );
+
+    	    // use 'the' normal.  We are pushing on two nodes so we
+    	    // need to push on two normals.
+    	    index = normals.unique_add( vn );
+    	    strip_n.push_back( index );
+    	    strip_n.push_back( index );
+     	} 
+        else 
+        {
+            string message = "Ooops missing node when building skirt (in init)";
+            SG_LOG( SG_GENERAL, SG_INFO, message << " " << p );
+    	    throw sg_exception( message );
+    	}
+
+    	// loop through the list
+    	for ( j = 1; j < divided_base.contour_size(i); ++j ) 
+        {
+    	    p = divided_base.get_pt( i, j );
+    	    uindex = nodes.find( p );
+    	    if ( uindex >= 0 ) 
+            {
+        		Point3D lower = geod_nodes[uindex] - Point3D(0, 0, 20);
+        		SG_LOG(SG_GENERAL, SG_DEBUG, geod_nodes[uindex] << " <-> " << lower);
+        		lindex = nodes.simple_add( lower );
+        		geod_nodes.push_back( lower );
+        		strip_v.push_back( lindex );
+        		strip_v.push_back( uindex );
+
+        		index = normals.unique_add( vn );
+        		strip_n.push_back( index );
+        		strip_n.push_back( index );
+    	    } 
+            else 
+            {
+                string message = "Ooops missing node when building skirt (in loop)";
+                SG_LOG( SG_GENERAL, SG_INFO, message << " " << p );
+                throw sg_exception( message );
+	        }
+	    }
+
+    	// close off the loop
+	    p = divided_base.get_pt( i, 0 );
+	    uindex = nodes.find( p );
+	    if ( uindex >= 0 ) 
+        {
+    	    Point3D lower = geod_nodes[uindex] - Point3D(0, 0, 20);
+    	    SG_LOG(SG_GENERAL, SG_DEBUG, geod_nodes[uindex] << " <-> " << lower);
+    	    lindex = nodes.simple_add( lower );
+    	    geod_nodes.push_back( lower );
+    	    strip_v.push_back( lindex );
+    	    strip_v.push_back( uindex );
+
+    	    index = normals.unique_add( vn );
+    	    strip_n.push_back( index );
+    	    strip_n.push_back( index );
+    	} 
+        else 
+        {
+            string message = "Ooops missing node when building skirt (at end)";
+            SG_LOG( SG_GENERAL, SG_INFO, message << " " << p );
+            throw sg_exception( message );
+    	}
+
+    	strips_v.push_back( strip_v );
+    	strips_n.push_back( strip_n );
+    	strip_materials.push_back( "Grass" );
+
+    	std::vector < SGGeod > geodNodes;
+    	for ( j = 0; j < nodes.get_node_list().size(); j++ ) 
+        {
+    	    Point3D node = nodes.get_node_list()[j];
+    	    geodNodes.push_back( SGGeod::fromDegM( node.x(), node.y(), node.z() ) );
+    	}
+	    base_txs.clear();
+	    base_txs = sgCalcTexCoords( b, geodNodes, strip_v );
+
+	    base_tc.clear();
+	    for ( j = 0; j < (int)base_txs.size(); ++j ) 
+        {
+    	    SGVec2f tc = base_txs[j];
+    	    // SG_LOG(SG_GENERAL, SG_DEBUG, "base_tc = " << tc);
+    	    index = texcoords.simple_add( Point3D( tc.x(), tc.y(), 0 ) );
+    	    base_tc.push_back( index );
+    	}
+    	strips_tc.push_back( base_tc );
+    }
+
+#if 0
+    // add light points
+
+    superpoly_list tmp_light_list; tmp_light_list.clear();
+    typedef map < string, double, less<string> > elev_map_type;
+    typedef elev_map_type::const_iterator const_elev_map_iterator;
+    elev_map_type elevation_map;
+
+    SG_LOG(SG_GENERAL, SG_INFO, "Computing runway/approach lighting elevations");
+
+    // pass one, calculate raw elevations from Array
+
+    for ( i = 0; i < (int)rwy_lights.size(); ++i ) 
+    {
+        TGTriNodes light_nodes;
+        light_nodes.clear();
+        point_list lights_v = rwy_lights[i].get_poly().get_contour(0);
+        for ( j = 0; j < (int)lights_v.size(); ++j ) 
+        {
+            p = lights_v[j];
+            index = light_nodes.simple_add( p );
+        }
+
+        // calculate light node elevations
+
+        point_list geod_light_nodes = calc_elevations( apt_surf, light_nodes.get_node_list(), 0.5 );
+        TGPolygon p;
+        p.add_contour( geod_light_nodes, 0 );
+        TGSuperPoly s;
+        s.set_poly( p );
+        tmp_light_list.push_back( s );
+
+        string flag = rwy_lights[i].get_flag();
+        if ( flag != (string)"" ) 
+        {
+            double max = -9999;
+            const_elev_map_iterator it = elevation_map.find( flag );
+            if ( it != elevation_map.end() ) 
+            {
+                max = elevation_map[flag];
+            }
+            for ( j = 0; j < (int)geod_light_nodes.size(); ++j ) 
+            {
+                if ( geod_light_nodes[j].z() > max ) 
+                {
+                    max = geod_light_nodes[j].z();
+                }
+            }
+            elevation_map[flag] = max;
+            SG_LOG( SG_GENERAL, SG_DEBUG, flag << " max = " << max );
+        }
+    }
+
+    SG_LOG(SG_GENERAL, SG_INFO, "Done with lighting calc_elevations()");
+
+    // pass two, for each light group check if we need to lift (based
+    // on flag) and do so, then output next structures.
+    for ( i = 0; i < (int)rwy_lights.size(); ++i ) 
+    {
+        // tmp_light_list is a parallel structure to rwy_lights
+        point_list geod_light_nodes = tmp_light_list[i].get_poly().get_contour(0);
+        
+        // this is a little round about, but what we want to calculate the
+        // light node elevations as ground + an offset so we do them
+        // seperately, then we add them back into nodes to get the index
+        // out, but also add them to geod_nodes to maintain consistancy
+        // between these two lists.
+        point_list light_normals = rwy_lights[i].get_normals().get_contour(0);
+        pt_v.clear();
+        pt_n.clear();
+        for ( j = 0; j < (int)geod_light_nodes.size(); ++j ) 
+        {
+            p = geod_light_nodes[j];
+            index = nodes.simple_add( p );
+            pt_v.push_back( index );
+            geod_nodes.push_back( p );
+
+            index = normals.unique_add( light_normals[j] );
+            pt_n.push_back( index );
+        }
+        pts_v.push_back( pt_v );
+        pts_n.push_back( pt_n );
+        pt_materials.push_back( rwy_lights[i].get_material() );
+    }
+#endif
+
+    // calculate wgs84 mapping of nodes
+    std::vector< SGVec3d > wgs84_nodes;
+    for ( i = 0; i < (int)geod_nodes.size(); ++i ) 
+    {
+        SGGeod geod = SGGeod::fromDegM( geod_nodes[i].x(), geod_nodes[i].y(), geod_nodes[i].z() );
+    	SG_LOG(SG_GENERAL, SG_DEBUG, "geod pt = " << geod_nodes[i] );
+        SGVec3d cart = SGVec3d::fromGeod(geod);
+        SG_LOG(SG_GENERAL, SG_DEBUG, "  cart pt = " << cart );
+    	wgs84_nodes.push_back( cart );
+    }
+    SGSphered d;
+    for ( i = 0; i < wgs84_nodes.size(); ++i ) 
+    {
+        d.expandBy(wgs84_nodes[ i ]);
+    }
+    
+    SGVec3d gbs_center = d.getCenter();
+    double gbs_radius = d.getRadius();
+    cout << "gbs center = " << gbs_center << endl;
+    SG_LOG(SG_GENERAL, SG_DEBUG, "Done with wgs84 node mapping");
+    SG_LOG(SG_GENERAL, SG_DEBUG, "  center = " << gbs_center << " radius = " << gbs_radius );
+
+    // null structures
+    group_list fans_v; fans_v.clear();
+    group_list fans_n; fans_n.clear();
+    group_list fans_tc; fans_tc.clear();
+    string_list fan_materials; fan_materials.clear();
+
+    string objpath = root + "/AirportObj";
+    string name = airport_id + ".btg";
+    
+    std::vector< SGVec3f > normals_3f;
+    for ( i=0; i < normals.get_node_list().size(); i++ ) 
+    {
+        Point3D node = normals.get_node_list()[i];
+        normals_3f.push_back( node.toSGVec3f() );
+    }
+
+    std::vector< SGVec2f > texcoords_2f;
+    for ( i=0; i < texcoords.get_node_list().size(); i++ ) 
+    {
+        Point3D node = texcoords.get_node_list()[i];
+        texcoords_2f.push_back( node.toSGVec2f() );
+    }
+
+    SGBinObject obj;
+
+    obj.set_gbs_center( gbs_center );
+    obj.set_gbs_radius( gbs_radius );
+    obj.set_wgs84_nodes( wgs84_nodes );
+    obj.set_normals( normals_3f );
+    obj.set_texcoords( texcoords_2f );
+    obj.set_pts_v( pts_v );
+    obj.set_pts_n( pts_n );
+    obj.set_pt_materials( pt_materials );
+    obj.set_tris_v( tris_v );
+    obj.set_tris_n( tris_n );
+    obj.set_tris_tc( tris_tc ); 
+    obj.set_tri_materials( tri_materials );
+    obj.set_strips_v( strips_v );
+    obj.set_strips_n( strips_n );
+    obj.set_strips_tc( strips_tc ); 
+    obj.set_strip_materials( strip_materials );
+    obj.set_fans_v( fans_v );
+    obj.set_fans_n( fans_n );
+    obj.set_fans_tc( fans_tc );
+    obj.set_fan_materials( fan_materials );
+
+    bool result;
+    result = obj.write_bin( objpath, name, b );
+    if ( !result ) 
+    {
+        throw sg_exception("error writing file. :-(");
+    }
+
+    // write out airport object reference
+    write_index( objpath, b, name );
+
+    // write out beacon references
+    for ( i = 0; i < (int)beacon_nodes.size(); ++i ) 
+    {
+        write_index_shared( objpath, b, beacon_nodes[i],
+                            "Models/Airport/beacon.xml",
+                            0.0 );
+    }
+
+    // write out tower references
+    for ( i = 0; i < (int)tower_nodes.size(); ++i ) 
+    {
+        write_index_shared( objpath, b, tower_nodes[i],
+                            "Models/Airport/tower.xml",
+                            0.0 );
+    }
+
+    // write out windsock references
+    for ( i = 0; i < (int)windsock_nodes.size(); ++i ) 
+    {
+    	if ( windsock_types[i] == 0 ) 
+        {
+            write_index_shared( objpath, b, windsock_nodes[i],
+                                "Models/Airport/windsock.xml",
+                                0.0 );
+    	} 
+        else 
+        {
+            write_index_shared( objpath, b, windsock_nodes[i],
+                                "Models/Airport/windsock_lit.xml",
+                                0.0 );
+    	}
+    }
+
+    string holepath = root + "/AirportArea";
+    // long int poly_index = poly_index_next();
+    // write_boundary( holepath, b, hull, poly_index );
+    tgChopNormalPolygon( holepath, "Hole", divided_base, true );
+    tgChopNormalPolygon( holepath, "Airport", apt_clearing, false );
+}
+#endif
+
+
diff --git a/src/Airports/GenAirports850/airport.hxx b/src/Airports/GenAirports850/airport.hxx
new file mode 100644
index 00000000..28ef85f4
--- /dev/null
+++ b/src/Airports/GenAirports850/airport.hxx
@@ -0,0 +1,49 @@
+#ifndef _AIRPORT_H_
+#define _AIRPORT_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "runway.hxx"
+#include "closedpoly.hxx"
+#include "linearfeature.hxx"
+
+using std::string;
+
+class Airport
+{
+public:
+    Airport( int c, char* def);
+
+    void AddRunway( Runway* runway )
+    {
+        runways.push_back( runway );
+    }
+
+    void AddPavement( ClosedPoly* pavement )
+    {
+        pavements.push_back( pavement );
+    }
+
+    void AddFeature( LinearFeature* feature )
+    {
+        features.push_back( feature );
+    }
+
+    void BuildOsg( osg::Group* airport );
+    void BuildBtg( const string& root, const string_list& elev_src );
+
+private:
+    int     code;               // airport, heliport or sea port
+    int     altitude;           // in meters
+    string  icao;               // airport code
+    string  description;        // description
+
+    PavementList    pavements;
+    FeatureList     features;
+    RunwayList      runways;
+};
+
+typedef std::vector <Airport *> AirportList;
+
+#endif
diff --git a/src/Airports/GenAirports850/beznode.hxx b/src/Airports/GenAirports850/beznode.hxx
new file mode 100644
index 00000000..af75d3da
--- /dev/null
+++ b/src/Airports/GenAirports850/beznode.hxx
@@ -0,0 +1,205 @@
+#ifndef _BEZNODE_H_
+#define _BEZNODE_H_
+
+#include <string.h>
+#include <float.h>
+#include <iostream>
+
+#include <osg/Vec3d>
+#include <Geometry/point3d.hxx>
+#include <simgear/debug/logstream.hxx>
+
+// TODO: where to put this
+inline osg::Vec3d SGPoint3DtoOSGVec3d( Point3D p )
+{
+    return osg::Vec3d( p.x(), p.y(), p.z() );
+}
+
+// TEMP...
+inline Point3D CalculateQuadraticLocation( Point3D p0, Point3D cp, Point3D p1, double t )
+{
+    Point3D result;
+
+    double term1 = (1.0f - t) * (1.0f - t);
+    double term2 = 2 * (1.0f - t) * t;
+    double term3 = t * t; 
+
+    result = (p0 * term1) + (cp * term2) + (p1 * term3);
+
+    return result;
+}
+
+// TODO: Should be in a math library
+inline Point3D CalculateCubicLocation( Point3D p0, Point3D cp0, Point3D cp1, Point3D p1, double t )
+{
+    Point3D result;
+
+    double term1 = (1.0f - t) * (1.0f - t) * (1.0f - t);
+    double term2 = 3 * (1.0f - t) * (1.0f - t) * t;
+    double term3 = 3 * (1.0f - t) * t * t;
+    double term4 = t * t * t;
+
+    result = (p0 * term1) + (cp0 * term2) + (cp1 * term3) + (p1 * term4);
+
+    return result;
+}
+
+inline double CalculateTheta( Point3D p0, Point3D p1, Point3D p2 )
+{
+    Point3D u, v;
+    double  udist, vdist, uv_dot, tmp;
+
+    // u . v = ||u|| * ||v|| * cos(theta)
+
+    u = p1 - p0;
+    udist = sqrt( u.x() * u.x() + u.y() * u.y() );
+    // printf("udist = %.6f\n", udist);
+
+    v = p1 - p2;
+    vdist = sqrt( v.x() * v.x() + v.y() * v.y() );
+    // printf("vdist = %.6f\n", vdist);
+
+    uv_dot = u.x() * v.x() + u.y() * v.y();
+    // printf("uv_dot = %.6f\n", uv_dot);
+
+    tmp = uv_dot / (udist * vdist);
+    // printf("tmp = %.6f\n", tmp);
+
+    return acos(tmp);
+}
+
+#define BEZIER_DETAIL   (8)
+#define LINE_WIDTH      (0.75)
+#define WIREFRAME       (1)
+
+#define CURVE_NONE                      (0)
+#define CURVE_LINEAR                    (1)
+#define CURVE_QUADRATIC                 (2)
+#define CURVE_CUBIC                     (3)
+
+
+class BezNode 
+{
+public:
+    BezNode( Point3D l ) : prev_cp(0.0f, 0.0f, 0.0f), next_cp(0.0f, 0.0f, 0.0f)
+    {
+        loc  = l;
+        mark = 0;
+        light = 0;
+    }
+
+    BezNode( double lat, double lon ) : prev_cp(0.0f, 0.0f, 0.0f), next_cp(0.0f, 0.0f, 0.0f)
+    {
+        loc = Point3D( lon, lat, 0.0f );
+        mark = 0;
+        light = 0;
+    }
+
+    BezNode( Point3D l, Point3D cp )
+    {
+        loc = l;
+        next_cp = cp;
+        prev_cp = Mirror(cp);
+        mark = 0;
+        light = 0;
+    }
+
+    BezNode( double lat, double lon, double cp_lat, double cp_lon )
+    {
+        loc = Point3D( lon, lat, 0.0f );
+        next_cp = Point3D( cp_lon, cp_lat, 0.0f );
+        prev_cp = Mirror( next_cp );
+        mark = 0;
+        light = 0;
+    }
+
+    Point3D Mirror( Point3D pt )
+    {
+        // mirror given point about our location
+        return (loc - (pt - loc));
+    }
+
+    void SetMarking( int m )
+    {
+        mark = m;
+    }
+
+    int GetMarking( )
+    {
+        return mark;
+    }
+
+    void SetLighting( int l )
+    {
+        light = l;
+    }
+
+    int GetLighting( )
+    {
+        return light;
+    }
+
+    bool IsAt( double lat, double lon )
+    {
+        return ( (loc.lat() == lat) && (loc.lon() == lon) );
+    }
+
+    bool HasPrevCp()
+    {
+        return ( (prev_cp.x() != 0.0f) && (prev_cp.y() != 0.0f) );
+    }
+
+    bool HasNextCp()
+    {
+        return ( (next_cp.x() != 0.0f) && (next_cp.y() != 0.0f) );
+    }
+
+    Point3D GetLoc()
+    {
+        return loc;
+    }
+
+    Point3D GetPrevCp()
+    {
+        return prev_cp;
+    }
+
+    Point3D GetNextCp()
+    {
+        return next_cp;
+    }
+
+    void ClearNextCp(void)
+    {
+        next_cp = Point3D(0.0f,0.0f, 0.0f);
+    }
+
+    void SetNextCp( double lat, double lon )
+    {
+        next_cp = Point3D(lon, lat, 0.0f);
+    }
+
+    // TODO: log levels and macros (does terragear have them?)
+    void Print()
+    {
+        SG_LOG(SG_GENERAL, SG_DEBUG, 
+            "\tLoc(" << loc.x() << "," << loc.y() << ")" <<
+            "\tprev_cp(" << prev_cp.x() << "," << prev_cp.y() << ")" <<
+            "\tnext_cp(" << next_cp.x() << "," << next_cp.y() << ")" );
+    }
+
+private:
+    Point3D  loc;
+    Point3D  prev_cp;
+    Point3D  next_cp;
+    int      mark;
+    int      light;
+};
+
+
+// array of BezNodes make a contour
+typedef std::vector <BezNode *> BezContour;
+typedef std::vector <BezContour *> BezContourArray;
+
+#endif
+
diff --git a/src/Airports/GenAirports850/closedpoly.cxx b/src/Airports/GenAirports850/closedpoly.cxx
new file mode 100644
index 00000000..cf705f6d
--- /dev/null
+++ b/src/Airports/GenAirports850/closedpoly.cxx
@@ -0,0 +1,656 @@
+#include <stdlib.h>
+#include <list>
+
+#include <simgear/debug/logstream.hxx>
+#include <simgear/math/sg_geodesy.hxx>
+
+#include <osg/Geode>
+#include <osg/PolygonMode>
+#include <osgUtil/Tessellator>
+
+#include <Geometry/poly_support.hxx>
+
+#include "beznode.hxx"
+#include "convex_hull.hxx"
+#include "closedpoly.hxx"
+
+
+ClosedPoly::ClosedPoly( int st, float s, float th, char* desc )
+{
+    surface_type = st;
+    smoothness   = s;
+    texture_heading = th;
+    if ( desc )
+    {
+        strcpy( description, desc );
+    }
+    else
+    {
+        strcpy( description, "none" );
+    }
+        
+    boundary = NULL;
+    cur_contour = NULL;
+    cur_feat = NULL;
+    cur_marking = 0;
+}
+
+void ClosedPoly::AddNode( BezNode* node )
+{
+    // if this is the first node of the contour - create a new contour
+    if (!cur_contour)
+    {
+        cur_contour = new BezContour;
+    }
+    cur_contour->push_back( node );
+
+    SG_LOG(SG_GENERAL, SG_DEBUG, "CLOSEDPOLY::ADDNODE : (" << node->GetLoc().x() << "," << node->GetLoc().y() << ")");
+    // if recording a linear feature on the pavement, add this node
+    // to it as well
+    // TODO: just doing marking now, need lighting as well
+    if (cur_feat)
+    {
+        SG_LOG(SG_GENERAL, SG_DEBUG, "   Adding node (" << node->GetLoc().x() << "," << node->GetLoc().y() << ") to current linear feature " << cur_marking);
+        cur_feat->AddNode( node );
+
+        // if it should end, end it, and add to feature list
+        if (cur_marking != node->GetMarking())
+        {
+            SG_LOG(SG_GENERAL, SG_DEBUG, "   Node has marking " << node->GetMarking() << " end it");
+            features.push_back( cur_feat );
+            cur_feat = NULL;
+            cur_marking = 0;
+        }
+    }
+
+    // should we start a new feature here?
+    SG_LOG(SG_GENERAL, SG_DEBUG, "   Node has marking " << node->GetMarking() << " cur marking is " << cur_marking);
+    if ( (cur_marking == 0) && (node->GetMarking()))
+    {
+        // Yes - create a new linear feature
+        // TODO: With offset, as all pavement markings should be 
+        // a bit in from the edge
+        SG_LOG(SG_GENERAL, SG_DEBUG, "   Starting a new linear feature");
+        cur_feat = new LinearFeature(NULL);
+        cur_feat->AddNode( node );
+        cur_marking = node->GetMarking();
+    }
+}
+
+void ClosedPoly::CreateConvexHull( void )
+{
+    TGPolygon  convexHull;
+    point_list nodes;
+    Point3D    p;
+    int        i;
+
+    for (i=0; i<boundary->size(); i++)
+    {
+        p = boundary->at(i)->GetLoc();
+        nodes.push_back( p );
+    }
+
+    convexHull = convex_hull( nodes );
+    hull = convexHull.get_contour(0);
+}
+
+int ClosedPoly::CloseCurContour()
+{
+    SG_LOG(SG_GENERAL, SG_DEBUG, "Close Contour");
+
+    // if we are recording a pavement marking - it must be closed - 
+    // add the first node of the poly
+    if (cur_feat)
+    {
+        SG_LOG(SG_GENERAL, SG_DEBUG, "We still have an active linear feature - add the first node to close it");
+
+        cur_feat->AddNode( cur_contour->at(0) );
+
+        features.push_back( cur_feat );
+        cur_feat = NULL;  
+        cur_marking = 0;      
+    }
+
+    // add the contour to the poly - first one is the outer boundary
+    // subsequent contours are holes
+    if ( boundary == NULL )
+    {
+        boundary = cur_contour;
+
+        // generate the convex hull from the bezcontour node locations
+        CreateConvexHull();
+
+        cur_contour = NULL;
+    }
+    else
+    {
+        holes.push_back( cur_contour );
+        cur_contour = NULL;
+    }
+}
+
+void ClosedPoly::ConvertContour( BezContour* src, point_list *dst, bool reverse )
+{
+    BezNode*    prevNode;
+    BezNode*    curNode;
+    BezNode*    nextNode;
+        
+    Point3D prevLoc;
+    Point3D curLoc;
+    Point3D nextLoc;
+    Point3D cp1;
+    Point3D cp2;    
+
+    int start, end, inc;        
+
+    int curve_type = CURVE_LINEAR;
+    int i;
+
+    SG_LOG(SG_GENERAL, SG_DEBUG, "Creating a contour with " << src->size() << " nodes");
+
+    // clear anything in this point list
+    dst->empty();
+
+    if (reverse)
+    {
+        start = src->size()-1;
+        end   = -1;
+        inc   = -1;
+    }
+    else
+    {
+        start = 0;
+        end   = src->size();
+        inc   = 1;
+    }
+
+    // iterate through each bezier node in the contour
+    for (i=0; i <= src->size()-1; i++)
+    {
+        SG_LOG(SG_GENERAL, SG_DEBUG, "\nHandling Node " << i << "\n\n");
+
+        if (i == 0)
+        {
+            // set prev node to last in the contour, as all contours must be closed
+            prevNode = src->at( src->size()-1 );
+        }
+        else
+        {
+            // otherwise, it's just the previous index
+            prevNode = src->at( i-1 );
+        }
+
+        curNode = src->at(i);
+
+        if (i < src->size() - 1)
+        {
+            nextNode = src->at(i+1);
+        }
+        else
+        {
+            // for the last node, next is the first. as all contours are closed
+            nextNode = src->at(0);
+        }
+
+        // determine the type of curve from prev (just to get correct prev location)
+        // once we start drawing the curve from cur to next, we can just remember the prev loc
+        if (prevNode->HasNextCp())
+        {
+            // curve from prev is cubic or quadratic 
+            if(curNode->HasPrevCp())
+            {
+                // curve from prev is cubic : calculate the last location on the curve
+                prevLoc = CalculateCubicLocation( prevNode->GetLoc(), prevNode->GetNextCp(), curNode->GetPrevCp(), curNode->GetLoc(), (1.0f/BEZIER_DETAIL) * (BEZIER_DETAIL-1) );
+            }
+            else
+            {
+                // curve from prev is quadratic : use prev node next cp
+                prevLoc = CalculateQuadraticLocation( prevNode->GetLoc(), prevNode->GetNextCp(), curNode->GetLoc(), (1.0f/BEZIER_DETAIL) * (BEZIER_DETAIL-1) );
+            }
+        }
+        else 
+        {
+            // curve from prev is quadratic or linear
+            if( curNode->HasPrevCp() )
+            {
+                // curve from prev is quadratic : calculate the last location on the curve
+                prevLoc = CalculateQuadraticLocation( prevNode->GetLoc(), curNode->GetPrevCp(), curNode->GetLoc(), (1.0f/BEZIER_DETAIL) * (BEZIER_DETAIL-1) );
+            }
+            else
+            {
+                // curve from prev is linear : just use prev node location
+                prevLoc = prevNode->GetLoc();
+            }
+        }                    
+
+        // now determine how we will iterate from current node to next node 
+        if( curNode->HasNextCp() )
+        {
+            // next curve is cubic or quadratic
+            if( nextNode->HasPrevCp() )
+            {
+                // curve is cubic : need both control points
+                curve_type = CURVE_CUBIC;
+                cp1 = curNode->GetNextCp();
+                cp2 = nextNode->GetPrevCp();
+            }
+            else
+            {
+                // curve is quadratic using current nodes cp as the cp
+                curve_type = CURVE_QUADRATIC;
+                cp1 = curNode->GetNextCp();
+            }
+        }
+        else
+        {
+            // next curve is quadratic or linear
+            if( nextNode->HasPrevCp() )
+            {
+                // curve is quadratic using next nodes cp as the cp
+                curve_type = CURVE_QUADRATIC;
+                cp1 = nextNode->GetPrevCp();
+            }
+            else
+            {
+                // curve is linear
+                curve_type = CURVE_LINEAR;
+            }
+        }
+
+        // initialize current location
+        curLoc = curNode->GetLoc();
+        if (curve_type != CURVE_LINEAR)
+        {
+            for (int p=0; p<BEZIER_DETAIL; p++)
+            {
+                // calculate next location
+                if (curve_type == CURVE_QUADRATIC)
+                {
+                    nextLoc = CalculateQuadraticLocation( curNode->GetLoc(), cp1, nextNode->GetLoc(), (1.0f/BEZIER_DETAIL) * (p+1) );                    
+                }
+                else
+                {
+                    nextLoc = CalculateCubicLocation( curNode->GetLoc(), cp1, cp2, nextNode->GetLoc(), (1.0f/BEZIER_DETAIL) * (p+1) );                    
+                }
+
+                // add the pavement vertex
+                // convert from lat/lon to geo
+                // (maybe later) - check some simgear objects...
+                dst->push_back( curLoc );
+                if (p==0)
+                {
+                    SG_LOG(SG_GENERAL, SG_DEBUG, "adding Curve Anchor node (type " << curve_type << ") at (" << curLoc.x() << "," << curLoc.y() << ")");
+                }
+                else
+                {
+                    SG_LOG(SG_GENERAL, SG_DEBUG, "   add bezier node (type  " << curve_type << ") at (" << curLoc.x() << "," << curLoc.y() << ")");
+                }
+
+                // now set set prev and cur locations for the next iteration
+                prevLoc = curLoc;
+                curLoc = nextLoc;
+            }
+        }
+        else
+        {
+            nextLoc = nextNode->GetLoc();
+
+            // just add the one vertex - linear
+            dst->push_back( curLoc );
+            SG_LOG(SG_GENERAL, SG_DEBUG, "adding Linear Anchor node at (" << curLoc.x() << "," << curLoc.y() << ")");
+        }
+    }
+
+    // Add the first point again?
+    // dst->push_back( src->at(0)->GetLoc() );
+}
+
+void ExpandPoint( Point3D *prev, Point3D *cur, Point3D *next, double expand_by, double *heading, double *offset )
+{
+    int    turn_direction;
+    bool   reverse_dir = false;
+    double offset_dir;
+    double next_dir;
+    double az1, az2;
+    double dist;
+
+    SG_LOG(SG_GENERAL, SG_DEBUG, "Find average angle for contour: prev (" << *prev << "), "
+                                                                  "cur (" << *cur  << "), "
+                                                                 "next (" << *next << ")" );
+
+    // first, find if the line turns left or right ar src
+    // for this, take the cross product of the vectors from prev to src, and src to next.
+    // if the cross product is negetive, we've turned to the left
+    // if the cross product is positive, we've turned to the right
+    // if the cross product is 0, then we need to use the direction passed in
+    SGVec3d dir1 = prev->toSGVec3d() - cur->toSGVec3d();
+    dir1 = normalize(dir1);
+
+    SGVec3d dir2 = next->toSGVec3d() - cur->toSGVec3d();
+    dir2 = normalize(dir2);
+
+    // Now find the average
+    SGVec3d avg = dir1 + dir2;
+    avg = normalize(avg);
+
+    // find the offset angle
+    geo_inverse_wgs_84( avg.y(), avg.x(), 0.0f, 0.0f, &offset_dir, &az2, &dist);
+
+    // find the direction to the next point
+    geo_inverse_wgs_84( cur->y(), cur->x(), next->y(), next->x(), &next_dir, &az2, &dist);
+
+    // calculate correct distance for the offset point
+    *offset  = (expand_by)/sin(SGMiscd::deg2rad(offset_dir-next_dir));
+    *heading = offset_dir;
+
+    SG_LOG(SG_GENERAL, SG_DEBUG, "heading is " << *heading << " distance is " << *offset );
+}
+
+void ClosedPoly::ExpandContour( point_list& src, TGPolygon& dst, double dist )
+{
+    point_list expanded_boundary;
+    Point3D prevPoint, curPoint, nextPoint;
+    double theta;
+    double expand_by;
+    double expanded_x, expanded_y;
+    Point3D expanded_point;
+
+    double h1, h2, h3;
+    double o1, o2, o3;
+    double az2;
+    int i;
+        
+    // iterate through each bezier node in the contour
+    for (i=0; i<src.size(); i++)
+    {
+        SG_LOG(SG_GENERAL, SG_DEBUG, "\nExpanding point " << i << "\n\n");
+
+        if (i == 0)
+        {
+            // set prev node to last in the contour, as all contours must be closed
+            prevPoint = src.at( src.size()-1 );
+        }
+        else
+        {
+            // otherwise, it's just the last index
+            prevPoint = src.at( i-1 );
+        }
+
+        curPoint = src.at(i);
+
+        if (i<src.size()-1)
+        {
+            nextPoint = src.at(i+1);
+        }
+        else
+        {
+            // for the last node, next is the first. as all contours are closed
+            nextPoint = src.at(0);
+        }
+
+        // calculate the angle between cur->prev and cur->next
+        theta = SGMiscd::rad2deg(CalculateTheta(prevPoint, curPoint, nextPoint));
+
+        if ( theta < 90.0 )
+        {
+            SG_LOG(SG_GENERAL, SG_DEBUG, "\nClosed POLY case 1 (theta < 90) " << description << ": theta is " << theta );
+
+            // calculate the expanded point heading and offset from current point
+            ExpandPoint( &prevPoint, &curPoint, &nextPoint, dist, &h1, &o1 );
+
+            if (o1 > dist*2.0)
+            {
+                SG_LOG(SG_GENERAL, SG_DEBUG, "\ntheta is " << theta << " distance is " << o1 << " CLAMPING to " << dist*2 );
+                o1 = dist*2;
+
+                if (o1 > 100)
+                {
+                    SG_LOG(SG_GENERAL, SG_DEBUG, "\nClosed POLY " << description << ": theta is " << theta << " distance is " << o1 << " WHAT HAPPENED " );
+                    exit(1);
+                }
+            }
+
+            geo_direct_wgs_84( curPoint.y(), curPoint.x(), h1, o1, &expanded_y, &expanded_x, &az2 );
+            expanded_point = Point3D( expanded_x, expanded_y, 0.0f );
+            expanded_boundary.push_back( expanded_point );
+        }
+        else if ( abs(theta - 180.0) < 0.1 )
+        {
+            SG_LOG(SG_GENERAL, SG_DEBUG, "\nClosed POLY case 2 (theta close to 180) " << description << ": theta is " << theta );
+
+            // calculate the expanded point heading and offset from current point
+            ExpandPoint( &prevPoint, &curPoint, &nextPoint, dist, &h1, &o1 );
+
+            // straight line blows up math - dist should be exactly as given
+            o1 = dist;
+
+            geo_direct_wgs_84( curPoint.y(), curPoint.x(), h1, o1, &expanded_y, &expanded_x, &az2 );
+            expanded_point = Point3D( expanded_x, expanded_y, 0.0f );
+            expanded_boundary.push_back( expanded_point );
+        }
+        else
+        {
+            SG_LOG(SG_GENERAL, SG_DEBUG, "\nClosed POLY case 3 (fall through) " << description << ": theta is " << theta );
+
+            // calculate the expanded point heading and offset from current point
+            ExpandPoint( &prevPoint, &curPoint, &nextPoint, dist, &h1, &o1 );
+
+            if (o1 > dist*2)
+            {
+                SG_LOG(SG_GENERAL, SG_DEBUG, "\ntheta is " << theta << " distance is " << o1 << " WHAT HAPPENED " );
+                exit(1);
+            }
+
+            geo_direct_wgs_84( curPoint.y(), curPoint.x(), h1, o1, &expanded_y, &expanded_x, &az2 );
+            expanded_point = Point3D( expanded_x, expanded_y, 0.0f );
+            expanded_boundary.push_back( expanded_point );
+        }
+    }
+
+    dst.add_contour( expanded_boundary, 9 );
+}
+
+osg::DrawArrays* ClosedPoly::CreateOsgPrimitive( point_list contour, osg::Vec3Array* vpave )
+{
+    int i;
+    Point3D p;
+    osg::DrawArrays* primitive;
+    int start_vert = vpave->size();
+
+    for (i=0; i<contour.size(); i++)
+    {
+        p = contour[i];
+        vpave->push_back( osg::Vec3d(p.x(), p.y(), 0.0) );
+    }
+
+    primitive = new osg::DrawArrays(osg::PrimitiveSet::POLYGON, start_vert, vpave->size()-start_vert);    
+
+    return primitive;
+}
+
+// finish the poly - convert to TGPolygon, and tesselate
+int ClosedPoly::Finish()
+{
+    BezContour*         src_contour;
+    point_list          dst_contour;
+    BezNode*            node;
+
+    // error handling
+    if (boundary == NULL)
+    {
+        SG_LOG(SG_GENERAL, SG_DEBUG, "no boundary");
+    }
+
+    SG_LOG(SG_GENERAL, SG_DEBUG, "Converting a poly with " << holes.size() << " holes");
+    
+    if (boundary != NULL)
+    {
+        // create the boundary
+        ConvertContour( boundary, &dst_contour, false );
+    
+        // and add it to the geometry 
+        pre_tess.add_contour( dst_contour, 0 );
+
+        // The convert the hole contours
+        for (int i=0; i<holes.size(); i++)
+        {
+            dst_contour.clear();
+            ConvertContour( holes[i], &dst_contour, false );
+            pre_tess.add_contour( dst_contour, 1 );
+        }
+    }
+
+    // save memory by deleting unneeded resources
+    delete boundary;
+    boundary = NULL;
+
+    // The convert the hole contours
+    holes.clear();
+}
+
+int ClosedPoly::BuildOsg( osg::Group* airport )
+{
+    BezContour*         contour;
+    BezNode*            node;
+    osg::Geode*         geode_pave; // has the stateset, and geometry for this poly
+    osg::Geometry*      pavement;
+    osg::StateSet*      ss_pave;    // just a simple stateset for now
+    osg::PolygonMode*   polymode;
+    osg::Vec4Array*     col_pave;   // just grey for now...
+    osg::Vec3Array*     v_pave;     // ALL verticies (boundary and holes)
+    osg::DrawArrays*    primitive;  // an index array for boundary / hole
+    int                 num_verts;  // number of verticies in a contour
+    int                 i;          // ?
+
+    if ( pre_tess.contours() )
+    {
+        // Setup a polygonmode for wireframe debuging
+        polymode = new osg::PolygonMode;
+        polymode->setMode(  osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE );
+
+        // Create a drawable for the pavement poly (boundary and holes)
+        pavement = new osg::Geometry;
+
+        // make pavement grey : do this when reading the poly
+        col_pave = new osg::Vec4Array;
+        col_pave->push_back(osg::Vec4(0.5f,0.5f,0.5f,1.0f));
+        pavement->setColorArray(col_pave);
+        pavement->setColorBinding(osg::Geometry::BIND_OVERALL);
+
+        // set up pavement stateset
+        geode_pave = new osg::Geode();
+        ss_pave = new osg::StateSet();
+    
+#if WIREFRAME
+        ss_pave->setAttribute( polymode );
+#endif
+        geode_pave->setStateSet(ss_pave);
+
+        v_pave = new osg::Vec3Array;
+        pavement->setVertexArray(v_pave);
+
+        // create the boundary
+    	SG_LOG(SG_GENERAL, SG_DEBUG, "  Adding pavement boundary");
+        primitive = CreateOsgPrimitive( pre_tess.get_contour( 0 ), v_pave );
+        if (hull.size() > 0)
+        {
+        	SG_LOG(SG_GENERAL, SG_DEBUG, "  boundary has  " << hull.size() << "verts" );
+        }
+
+        // add the hole contours
+        for (i=1; i<pre_tess.contours(); i++)
+        {
+            primitive = CreateOsgPrimitive( pre_tess.get_contour( i ), v_pave );
+            pavement->addPrimitiveSet(primitive);
+        }
+        
+        osgUtil::Tessellator *tess = new osgUtil::Tessellator;
+        tess->setTessellationType(osgUtil::Tessellator::TESS_TYPE_GEOMETRY);
+        tess->retessellatePolygons(*pavement);
+
+        geode_pave->addDrawable(pavement);
+        airport->addChild(geode_pave);
+
+#if 0   // debug ExpandPoly
+        TGPolygon base;
+        ExpandPoly( hull, base, 20 );
+        primitive = CreateOsgPrimitive( base.get_contour( 0 ), v_pave );
+        pavement->addPrimitiveSet(primitive);
+#endif
+
+        geode_pave->addDrawable(pavement);
+        airport->addChild(geode_pave);
+
+        // add the pavement markings
+#if 0
+        printf("ClosedPoly::Finish - Finish %d linear features\n", features.size());
+        for (int feat=0; feat<features.size(); feat++)
+        {
+            features.at(feat)->Finish(airport);
+        }
+#endif
+    }
+}
+
+int ClosedPoly::BuildBtg( float alt_m, superpoly_list* rwy_polys, texparams_list* texparams, TGPolygon* accum, TGPolygon* apt_base, TGPolygon* apt_clearing )
+{
+    TGPolygon base, safe_base;
+    string material;
+    int j, k;
+
+    material = "pa_tiedown";
+
+    // verify the poly has been generated
+    if ( pre_tess.contours() )    
+    {
+        SG_LOG(SG_GENERAL, SG_DEBUG, "BuildBtg: original poly has " << pre_tess.contours() << " contours");
+    
+        // do this before clipping and generating the base
+    	pre_tess = remove_dups( pre_tess );
+        pre_tess = reduce_degeneracy( pre_tess );
+
+        for (int c=0; c<pre_tess.contours(); c++)
+        {
+            for (int pt=0; pt<pre_tess.contour_size(c); pt++)
+            {
+                SG_LOG(SG_GENERAL, SG_DEBUG, "BuildBtg: contour " << c << " pt " << pt << ": (" << pre_tess.get_pt(c, pt).x() << "," << pre_tess.get_pt(c, pt).y() << ")" );
+            }
+        }
+
+
+        TGSuperPoly sp;
+        TGTexParams tp;
+
+        TGPolygon clipped = tgPolygonDiff( pre_tess, *accum );
+
+        SG_LOG(SG_GENERAL, SG_DEBUG, "BuildBtg: clipped poly has " << clipped.contours() << " contours");
+
+        TGPolygon split   = tgPolygonSplitLongEdges( clipped, 400.0 );
+
+        SG_LOG(SG_GENERAL, SG_DEBUG, "BuildBtg: split poly has " << split.contours() << " contours");
+
+
+        sp.erase();
+        sp.set_poly( split );
+        sp.set_material( material );
+        rwy_polys->push_back( sp );
+        SG_LOG(SG_GENERAL, SG_DEBUG, "clipped = " << clipped.contours());
+        *accum = tgPolygonUnion( pre_tess, *accum );
+        tp = TGTexParams( pre_tess.get_pt(0,0), 20.0 /* TODO poly width */, 20.0 /* TODO poly length */, texture_heading );
+        texparams->push_back( tp );
+
+        ExpandContour( hull, base, 20.0 );
+        ExpandContour( hull, safe_base, 50.0 );
+        
+        // add this to the airport clearing
+        *apt_clearing = tgPolygonUnion( safe_base, *apt_clearing);
+
+        // and add the clearing to the base
+        *apt_base = tgPolygonUnion( base, *apt_base );
+    }
+
+    // clean up to save ram : we're done here...
+
+
+    return 1;
+}
diff --git a/src/Airports/GenAirports850/closedpoly.hxx b/src/Airports/GenAirports850/closedpoly.hxx
new file mode 100644
index 00000000..2271c583
--- /dev/null
+++ b/src/Airports/GenAirports850/closedpoly.hxx
@@ -0,0 +1,66 @@
+#ifndef _BEZPOLY_H_
+#define _BEZPOLY_H_
+
+#include "beznode.hxx"
+#include "linearfeature.hxx"
+
+#include <Polygon/polygon.hxx>
+#include <Polygon/superpoly.hxx>
+#include <Geometry/point3d.hxx>
+
+#include "texparams.hxx"
+
+#include <osg/Geometry>
+#include <osg/Vec3d>
+
+using std::string;
+
+class ClosedPoly
+{
+public:
+    ClosedPoly( int st, float s, float th, char* desc );
+
+    void AddNode( BezNode* node );
+    int  CloseCurContour();
+    int  Finish();
+    int  BuildOsg( osg::Group* airport );
+    int  BuildBtg( float alt_m, superpoly_list* rwy_polys, texparams_list* texparams, TGPolygon* accum, TGPolygon* apt_base, TGPolygon* apt_clearing );
+    
+private:
+    //osg::DrawArrays* CreatePrimitive( BezContour* contour, osg::Vec3Array* v_pave );
+    // convert the BezierPoly to a normal Poly (adding nodes for the curves)
+    void CreateConvexHull( void );
+    void ConvertContour( BezContour* src, point_list *dst, bool reverse );
+    osg::DrawArrays* CreateOsgPrimitive( point_list contour, osg::Vec3Array* vpave );
+    void ExpandContour( point_list& src, TGPolygon& dst, double dist );
+
+    int   surface_type;
+    float smoothness;
+    float texture_heading;
+    char  description[64];
+    
+    // outer boundary definition as bezier nodes
+    BezContour* boundary;
+
+    // holes
+    BezContourArray holes;
+
+    // contour that nodes will be added until done
+    BezContour* cur_contour;
+
+
+    // outer boundary as convex hull
+    point_list hull;
+
+    // Converted polygon after parsing complete
+    TGPolygon pre_tess;
+
+    // pavement definitions can have multiple linear features (markings)
+    LinearFeature* cur_feat;
+    FeatureList features;
+    int cur_marking;
+};
+
+typedef std::vector <ClosedPoly *> PavementList;
+
+#endif
diff --git a/src/Airports/GenAirports850/convex_hull.cxx b/src/Airports/GenAirports850/convex_hull.cxx
new file mode 100644
index 00000000..62df7b87
--- /dev/null
+++ b/src/Airports/GenAirports850/convex_hull.cxx
@@ -0,0 +1,252 @@
+// convex_hull.cxx -- calculate the convex hull of a set of points
+//
+// Written by Curtis Olson, started September 1998.
+//
+// Copyright (C) 1998  Curtis L. Olson  - http://www.flightgear.org/~curt
+//
+// 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.
+//
+// $Id: convex_hull.cxx,v 1.12 2004-11-19 22:25:49 curt Exp $
+//
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <math.h>
+#include <stdio.h>
+
+#include <map>
+
+#include <simgear/compiler.h>
+#include <simgear/constants.h>
+#include <simgear/structure/exception.hxx>
+
+#include <simgear/constants.h>
+
+#include "convex_hull.hxx"
+#include "point2d.hxx"
+
+using std::less;
+using std::map;
+
+
+// stl map typedefs
+typedef map < double, double, less<double> > map_container;
+typedef map_container::iterator map_iterator;
+
+
+// Calculate theta of angle (a, b, c)
+double calc_angle(Point3D a, Point3D b, Point3D c) {
+    Point3D u, v;
+    double udist, vdist, uv_dot, tmp;
+
+    // u . v = ||u|| * ||v|| * cos(theta)
+
+    u.setx( b.x() - a.x() );
+    u.sety( b.y() - a.y() );
+    udist = sqrt( u.x() * u.x() + u.y() * u.y() );
+    // printf("udist = %.6f\n", udist);
+
+    v.setx( b.x() - c.x() );
+    v.sety( b.y() - c.y() );
+    vdist = sqrt( v.x() * v.x() + v.y() * v.y() );
+    // printf("vdist = %.6f\n", vdist);
+
+    uv_dot = u.x() * v.x() + u.y() * v.y();
+    // printf("uv_dot = %.6f\n", uv_dot);
+
+    tmp = uv_dot / (udist * vdist);
+    // printf("tmp = %.6f\n", tmp);
+
+    return acos(tmp);
+}
+
+
+// Test to see if angle(Pa, Pb, Pc) < 180 degrees
+bool test_point(Point3D Pa, Point3D Pb, Point3D Pc) {
+    double a1, a2;
+
+    Point3D origin( 0.0 );
+
+    Point3D a( cos(Pa.y()) * Pa.x(),
+	       sin(Pa.y()) * Pa.x(), 0 );
+
+    Point3D b( cos(Pb.y()) * Pb.x(),
+	       sin(Pb.y()) * Pb.x(), 0 );
+
+    Point3D c( cos(Pc.y()) * Pc.x(),
+	       sin(Pc.y()) * Pc.x(), 0 );
+
+    // printf("a is %.6f %.6f\n", a.x, a.y);
+    // printf("b is %.6f %.6f\n", b.x, b.y);
+    // printf("c is %.6f %.6f\n", c.x, c.y);
+
+    a1 = calc_angle(a, b, origin);
+    a2 = calc_angle(origin, b, c);
+
+    // printf("a1 = %.2f  a2 = %.2f\n", a1 * SGD_RADIANS_TO_DEGREES, a2 * SGD_RADIANS_TO_DEGREES);
+
+    return ( (a1 + a2) < SGD_PI );
+}
+
+
+// calculate the convex hull of a set of points, return as a list of
+// point2d.  The algorithm description can be found at:
+// http://riot.ieor.berkeley.edu/riot/Applications/ConvexHull/CHDetails.html
+TGPolygon convex_hull( const point_list& input_list ) {
+    int i;
+
+    map_iterator map_current, map_next, map_next_next, map_last;
+
+    // list of translated points
+    point_list trans_list;
+
+    // points sorted by radian degrees
+    map_container radians_map;
+
+    // will contain the convex hull
+    TGPolygon con_hull;
+
+    Point3D p, Pa, Pb, Pc, result;
+    double sum_x, sum_y;
+    int in_count, last_size;
+
+    // STEP ONE:  Find an average midpoint of the input set of points
+    in_count = input_list.size();
+    sum_x = sum_y = 0.0;
+
+    for ( i = 0; i < in_count; ++i ) {
+	sum_x += input_list[i].x();
+	sum_y += input_list[i].y();
+    }
+
+    Point3D average( sum_x / in_count, sum_y / in_count, 0 );
+
+    // printf("Average center point is %.4f %.4f\n", average.x, average.y);
+
+    // STEP TWO:  Translate input points so average is at origin
+    trans_list.clear();
+
+    for ( i = 0; i < in_count; ++i ) {
+	p = Point3D( input_list[i].x() - average.x(),
+		     input_list[i].y() - average.y(), 0 );
+	// printf("%.6f %.6f\n", p.x, p.y);
+	trans_list.push_back( p );
+    }
+
+    // STEP THREE:  convert to radians and sort by theta
+    radians_map.clear();
+
+    for ( i = 0; i < in_count; ++i ) {
+	p = cart_to_polar_2d( trans_list[i] );
+	if ( p.x() > radians_map[p.y()] ) {
+	    radians_map[p.y()] = p.x();
+	}
+    }
+
+    /*
+    // printf("Sorted list\n");
+    map_current = radians_map.begin();
+    map_last = radians_map.end();
+    for ( ; map_current != map_last ; ++map_current ) {
+	p.setx( (*map_current).first );
+	p.sety( (*map_current).second );
+
+	printf("p is %.6f %.6f\n", p.x(), p.y());
+    }
+    */
+
+    // STEP FOUR: traverse the sorted list and eliminate everything
+    // not on the perimeter.
+    // printf("Traversing list\n");
+
+    // double check list size ... this should never fail because a
+    // single runway will always generate four points.
+    if ( radians_map.size() < 3 ) {
+        throw sg_exception("convex hull not possible with < 3 points");
+    }
+
+    // ensure that we run the while loop at least once
+    last_size = radians_map.size() + 1;
+
+    while ( last_size > (int)radians_map.size() ) {
+	// printf("Running an iteration of the graham scan algorithm\n"); 
+	last_size = radians_map.size();
+
+	map_current = radians_map.begin();
+	while ( map_current != radians_map.end() ) {
+	    // get first element
+	    Pa.sety( (*map_current).first );
+	    Pa.setx( (*map_current).second );
+
+	    // get second element
+	    map_next = map_current;
+	    ++map_next;
+	    if ( map_next == radians_map.end() ) {
+		map_next = radians_map.begin();
+	    }
+	    Pb.sety( (*map_next).first );
+	    Pb.setx( (*map_next).second );
+
+	    // get third element
+	    map_next_next = map_next;
+	    ++map_next_next;
+	    if ( map_next_next == radians_map.end() ) {
+		map_next_next = radians_map.begin();
+	    }
+	    Pc.sety( (*map_next_next).first );
+	    Pc.setx( (*map_next_next).second );
+
+	    // printf("Pa is %.6f %.6f\n", Pa.y(), Pa.x());
+	    // printf("Pb is %.6f %.6f\n", Pb.y(), Pb.x());
+	    // printf("Pc is %.6f %.6f\n", Pc.y(), Pc.x());
+
+	    if ( test_point(Pa, Pb, Pc) ) {
+		// printf("Accepted a point\n");
+		// accept point, advance Pa, Pb, and Pc.
+		++map_current;
+	    } else {
+		// printf("REJECTED A POINT\n");
+		// reject point, delete it and advance only Pb and Pc
+		map_next = map_current;
+		++map_next;
+		if ( map_next == radians_map.end() ) {
+		    map_next = radians_map.begin();
+		}
+		radians_map.erase( map_next );
+	    }
+	}
+    }
+
+    // translate back to correct lon/lat
+    // printf("Final sorted convex hull\n");
+    con_hull.erase();
+    map_current = radians_map.begin();
+    map_last = radians_map.end();
+    for ( ; map_current != map_last ; ++map_current ) {
+	p.sety( (*map_current).first );
+	p.setx( (*map_current).second );
+
+	result.setx( cos(p.y()) * p.x() + average.x() );
+	result.sety( sin(p.y()) * p.x() + average.y() );
+
+	// printf("%.6f %.6f\n", result.x, result.y);
+
+	con_hull.add_node(0, result);
+    }
+
+    return con_hull;
+}
diff --git a/src/Airports/GenAirports850/convex_hull.hxx b/src/Airports/GenAirports850/convex_hull.hxx
new file mode 100644
index 00000000..de863c34
--- /dev/null
+++ b/src/Airports/GenAirports850/convex_hull.hxx
@@ -0,0 +1,47 @@
+// convex_hull.hxx -- calculate the convex hull of a set of points
+//
+// Written by Curtis Olson, started September 1998.
+//
+// Copyright (C) 1998  Curtis L. Olson  - http://www.flightgear.org/~curt
+//
+// 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.
+//
+// $Id: convex_hull.hxx,v 1.5 2004-11-19 22:25:49 curt Exp $
+//
+
+
+#ifndef _CONVEX_HULL_HXX
+#define _CONVEX_HULL_HXX
+
+
+#include <list>
+
+#include <simgear/math/sg_types.hxx>
+
+#include <Polygon/polygon.hxx>
+
+#include "point2d.hxx"
+
+
+
+// calculate the convex hull of a set of points, return as a list of
+// point2d.  The algorithm description can be found at:
+// http://riot.ieor.berkeley.edu/riot/Applications/ConvexHull/CHDetails.html
+TGPolygon convex_hull( const point_list& input_list );
+
+
+#endif // _CONVEX_HULL_HXX
+
+
diff --git a/src/Airports/GenAirports850/elevations.cxx b/src/Airports/GenAirports850/elevations.cxx
index 39971073..376689bb 100644
--- a/src/Airports/GenAirports850/elevations.cxx
+++ b/src/Airports/GenAirports850/elevations.cxx
@@ -145,7 +145,6 @@ double tgAverageElevation( const string &root, const string_list elev_src,
 
 // lookup node elevations for each point in the specified simple
 // matrix.  Returns average of all points.
-
 void tgCalcElevations( const string &root, const string_list elev_src,
                        SimpleMatrix &Pts, const double average )
 {
@@ -252,7 +251,6 @@ void tgCalcElevations( const string &root, const string_list elev_src,
            << grid_average);
 }
 
-
 // clamp all elevations to the specified range
 
 void tgClampElevations( SimpleMatrix &Pts,
diff --git a/src/Airports/GenAirports850/elevations.hxx b/src/Airports/GenAirports850/elevations.hxx
index c1cc0754..b6f158aa 100644
--- a/src/Airports/GenAirports850/elevations.hxx
+++ b/src/Airports/GenAirports850/elevations.hxx
@@ -48,9 +48,7 @@ double tgAverageElevation( const string &root, const string_list elev_src,
 
 // lookup node elevations for each point in the specified nurbs++
 // matrix.
-void tgCalcElevations( const string &root, const string_list elev_src,
-                       SimpleMatrix &Pts, double average );
+void tgCalcElevations( const string &root, const string_list elev_src, SimpleMatrix &Pts, double average );
 
 // clamp all elevations to the specified range
-void tgClampElevations( SimpleMatrix &Pts,
-			double center_m, double max_clamp_m );
+void tgClampElevations( SimpleMatrix &Pts, double center_m, double max_clamp_m );
diff --git a/src/Airports/GenAirports850/linearfeature.cxx b/src/Airports/GenAirports850/linearfeature.cxx
new file mode 100644
index 00000000..dfc4147e
--- /dev/null
+++ b/src/Airports/GenAirports850/linearfeature.cxx
@@ -0,0 +1,550 @@
+#include "beznode.hxx"
+#include "linearfeature.hxx"
+#include "math.h"
+
+#include <simgear/math/sg_geodesy.hxx>
+#include <simgear/math/SGVec3.hxx>
+#include <simgear/math/SGMisc.hxx>
+
+#include <osg/Geode>
+#include <osg/Geometry>
+#include <osg/Group>
+#include <osg/PolygonMode>
+#include <osg/PolygonOffset>
+
+int LinearFeature::Finish( osg::Group* airport )
+{
+    BezNode*    node;
+    int         i, j;
+    int         prev_dir = 0;
+    double      direction;
+    double      dist1, dist2;
+
+    printf("Finish a %s Linear Feature with %d verticies\n", closed ? "closed" : "open", contour.size());
+    
+    BezNode* prevNode;
+    BezNode* curNode;
+    BezNode* nextNode;
+
+    Point3D prevLoc;
+    Point3D curLoc;
+    Point3D nextLoc;
+
+    Point3D  st_cur1;
+    Point3D  st_cur2;
+    double st_curx, st_cury, az2;
+
+    // create a DrawElement for the stripe triangle strip
+    int stripe_idx = 0;
+    int cur_marking = 0;
+
+    osg::Vec3dArray* v_stripe = new osg::Vec3dArray;
+
+    for (i=0; i<contour.size(); i++)
+    {
+        printf("\nHandling Node %d\n\n", i );
+
+        if (i == 0)
+        {
+            // if closed, set prev node to last in the contour
+            if (closed)
+            {
+                prevNode = contour.at( contour.size()-1 );
+            }
+            else
+            {
+                prevNode = NULL;
+            }
+        }
+        else
+        {
+            prevNode = contour.at( i-1 );
+        }
+
+        curNode = contour.at(i);
+
+        if (i<contour.size()-1)
+        {
+            nextNode = contour.at(i+1);
+        }
+        else
+        {
+            // if closed, set next node to the first in the contour
+            if (closed)
+            {
+                nextNode = contour.at(0);
+            }
+            else
+            {
+                nextNode = NULL;
+            }
+        }
+
+        // determine the type of curve from prev (just to get correct prev location)
+        // once we start drawing the curve from cur to next, we can just remember the prev loc
+        if (prevNode)
+        {        
+            if (prevNode->HasNextCp())
+            {
+                // curve from prev is cubic or quadratic 
+                if(curNode->HasPrevCp())
+                {
+                    // curve from prev is cubic : calculate the last location on the curve
+                    prevLoc = CalculateCubicLocation( prevNode->GetLoc(), prevNode->GetNextCp(), curNode->GetPrevCp(), curNode->GetLoc(), (1.0f/BEZIER_DETAIL) * (BEZIER_DETAIL-1) );
+                }
+                else
+                {
+                    // curve from prev is quadratic : use prev node next cp
+                    prevLoc = CalculateQuadraticLocation( prevNode->GetLoc(), prevNode->GetNextCp(), curNode->GetLoc(), (1.0f/BEZIER_DETAIL) * (BEZIER_DETAIL-1) );
+                }
+            }
+            else 
+            {
+                // curve from prev is quadratic or linear
+                if( curNode->HasPrevCp() )
+                {
+                    // curve from prev is quadratic : calculate the last location on the curve
+                    prevLoc = CalculateQuadraticLocation( prevNode->GetLoc(), curNode->GetPrevCp(), curNode->GetLoc(), (1.0f/BEZIER_DETAIL) * (BEZIER_DETAIL-1) );
+                }
+                else
+                {
+                    // curve from prev is linear : just use prev node location
+                    prevLoc = prevNode->GetLoc();
+                }
+            }
+        }
+        else
+        {
+            prevLoc = Point3D(0.0f, 0.0f, 0.0f);
+        }
+
+        int curve_type = CURVE_LINEAR;
+        Point3D cp1;
+        Point3D cp2;            
+
+        if ( nextNode)
+        {
+            // now determine how we will iterate through cur node to next node 
+            if( curNode->HasNextCp() )
+            {
+                // next curve is cubic or quadratic
+                if( nextNode->HasPrevCp() )
+                {
+                    // curve is cubic : need both control points
+                    curve_type = CURVE_CUBIC;
+                    cp1 = curNode->GetNextCp();
+                    cp2 = nextNode->GetPrevCp();
+                }
+                else
+                {
+                    // curve is quadratic using current nodes cp as the cp
+                    curve_type = CURVE_QUADRATIC;
+                    cp1 = curNode->GetNextCp();
+                }
+            }
+            else
+            {
+                // next curve is quadratic or linear
+                if( nextNode->HasPrevCp() )
+                {
+                    // curve is quadratic using next nodes cp as the cp
+                    curve_type = CURVE_QUADRATIC;
+                    cp1 = nextNode->GetPrevCp();
+                }
+                else
+                {
+                    // curve is linear
+                    curve_type = CURVE_LINEAR;
+                }
+            }
+        }
+        else
+        {
+            curve_type = CURVE_NONE;
+        }
+
+        // initialize current location
+        curLoc = curNode->GetLoc();
+        switch( curve_type )
+        {
+            case CURVE_QUADRATIC:
+            case CURVE_CUBIC:
+                for (int p=0; p<BEZIER_DETAIL; p++)
+                {
+                    // calculate next location
+                    if (curve_type == CURVE_QUADRATIC)
+                    {
+                        nextLoc = CalculateQuadraticLocation( curNode->GetLoc(), cp1, nextNode->GetLoc(), (1.0f/BEZIER_DETAIL) * (p+1) );                    
+                    }
+                    else
+                    {
+                        nextLoc = CalculateCubicLocation( curNode->GetLoc(), cp1, cp2, nextNode->GetLoc(), (1.0f/BEZIER_DETAIL) * (p+1) );                    
+                    }
+
+                    // set the verticies
+                    // convert from lat/lon to geodisc
+                    // (maybe later) - check some simgear objects...
+                    // printf("Added vertex at %lf,%lf\n", 
+                    // add the pavement vertex
+                    if (p==0)
+                    {
+                        printf("adding Curve Anchor node (type %d) at (%lf,%lf)\n", curve_type, curLoc.x(), curLoc.y());
+                    }
+                    else
+                    {
+                        printf("   add bezier node (type %d) at (%lf,%lf)\n", curve_type, curLoc.x(), curLoc.y());
+                    }
+    
+                    // find the average direction at this vertex...
+                    direction = CalcMarkingVerticies( &prevLoc, &curLoc, &nextLoc, -1, &prev_dir, &dist1, &dist2 );
+
+                    printf("got dir for bezier : prev_dir %d, dist1 %lf, dist2 %lf\n", prev_dir, dist1, dist2);
+
+                    // distance can't be constant - it's the hypotenous of a right triangler with a constant offset from the outer rim.              
+                    geo_direct_wgs_84( curLoc.x(), curLoc.y(), direction, dist1, &st_curx, &st_cury, &az2 );
+                    st_cur1 = Point3D( st_curx, st_cury, 0.0f );
+                    v_stripe->push_back( SGPoint3DtoOSGVec3d(st_cur1) );
+
+                    geo_direct_wgs_84( curLoc.x(), curLoc.y(), direction, dist2, &st_curx, &st_cury, &az2 );
+                    st_cur2 = Point3D( st_curx, st_cury, 0.0f );
+                    v_stripe->push_back( SGPoint3DtoOSGVec3d(st_cur2) );
+    
+                    printf("node at (%lf,%lf) has stripe verticies (%lf,%lf) and (%lf,%lf)\n", 
+                        curLoc.x(), curLoc.y(), 
+                        st_cur1.x(), st_cur1.y(),
+                        st_cur2.x(), st_cur2.y() 
+                    );
+
+                    // now set set prev and cur locations for the next iteration
+                    prevLoc = curLoc;
+                    curLoc = nextLoc;
+                }
+                break;
+
+            case CURVE_LINEAR:
+                nextLoc = nextNode->GetLoc();
+
+                printf("adding Linear Anchor node at (%lf,%lf)\n", curLoc.x(), curLoc.y());
+
+                // find the average direction at this vertex...
+                direction = CalcMarkingVerticies( &prevLoc, &curLoc, &nextLoc, -1, &prev_dir, &dist1, &dist2 );
+
+                printf("got dir for linear : prev_dir %d, dist1 %lf, dist2 %lf\n", prev_dir, dist1, dist2);
+
+                // distance can't be constant - it's the hypotenous of a right triangler with a constant offset from the outer rim.              
+                printf("calc direct: curLoc is (%lf,%lf), direction is %lf, dist is %lf\n", curLoc.x(), curLoc.y(), direction, dist1);
+
+                geo_direct_wgs_84( curLoc.x(), curLoc.y(), direction, dist1, &st_curx, &st_cury, &az2 );
+                st_cur1 = Point3D( st_curx, st_cury, 0.0f );
+                
+                v_stripe->push_back( SGPoint3DtoOSGVec3d(st_cur1) );
+
+                geo_direct_wgs_84( curLoc.x(), curLoc.y(), direction, dist2, &st_curx, &st_cury, &az2 );
+                st_cur2 = Point3D( st_curx, st_cury, 0.0f );
+                v_stripe->push_back( SGPoint3DtoOSGVec3d(st_cur2) );
+
+                printf("node at (%lf,%lf) has stripe verticies (%lf,%lf) and (%lf,%lf)\n", 
+                    curLoc.x(), curLoc.y(), 
+                    st_cur1.x(), st_cur1.y(),
+                    st_cur2.x(), st_cur2.y() 
+                );
+                break;
+
+            case CURVE_NONE:
+                nextLoc = Point3D(0.0f, 0.0f, 0.0f);
+
+                // we need to add the last verticies based on cur and prev position.
+                // find the average direction at this vertex...
+                direction = CalcMarkingVerticies( &prevLoc, &curLoc, &nextLoc, -1, &prev_dir, &dist1, &dist2 );
+
+                printf("got dir for term points : prev_dir %lf, dist1 %lf, dist2 %lf\n", prev_dir, dist1, dist2);
+
+                // distance can't be constant - it's the hypotenous of a right triangler with a constant offset from the outer rim.              
+                geo_direct_wgs_84( curLoc.x(), curLoc.y(), direction, dist1, &st_curx, &st_cury, &az2 );
+                st_cur1 = Point3D( st_curx, st_cury, 0.0f );
+                v_stripe->push_back( SGPoint3DtoOSGVec3d(st_cur1) );
+
+                geo_direct_wgs_84( curLoc.x(), curLoc.y(), direction, dist2, &st_curx, &st_cury, &az2 );
+                st_cur2 = Point3D( st_curx, st_cury, 0.0f );
+                v_stripe->push_back( SGPoint3DtoOSGVec3d(st_cur2) );
+
+                printf("node at (%lf,%lf) has stripe verticies (%lf,%lf) and (%lf,%lf)\n", 
+                    curLoc.x(), curLoc.y(), 
+                    st_cur1.x(), st_cur1.y(),
+                    st_cur2.x(), st_cur2.y() 
+                );
+                break;
+        }
+    }
+
+    printf("End feature %d \n", v_stripe->size() );
+    airport->addChild(FinishMarking( v_stripe ) );
+}
+
+int LinearFeature::BuildBtg( float alt_m, superpoly_list* rwy_polys, texparams_list* texparams, TGPolygon* accum, TGPolygon* apt_base, TGPolygon* apt_clearing )
+{
+    string material;
+
+    return 1;
+}
+
+
+// this should be in the class
+// TODO: Should Be AddMidMarkingVerticies - and handle offset (used in LinearFeature::Finish only)
+double CalcMiddleMarkingVerticies( Point3D *prev, Point3D *cur, Point3D *next, int wind, int *prev_dir, double *dist1, double *dist2 )
+{
+    int    turn_direction;
+    bool   reverse_dir = false;
+    double offset_dir;
+    double next_dir;
+    double az1, az2;
+    double dist;
+
+    printf("Find averate angle for mark: prev (%lf,%lf), cur(%lf,%lf), next(%lf,%lf)\n", prev->x(), prev->y(), cur->x(), cur->y(), next->x(), next->y());
+
+    // first, find if the line turns left or right ar src
+    // for this, take the cross product of the vectors from prev to src, and src to next.
+    // if the cross product is negetive, we've turned to the left
+    // if the cross product is positive, we've turned to the right
+    // if the cross product is 0, then we need to use the direction passed in
+    SGVec3d dir1 = cur->toSGVec3d() - prev->toSGVec3d();
+    dir1 = normalize(dir1);
+
+    printf("normalized dir1 is (%lf,%lf)\n", dir1.x(), dir1.y());
+
+    SGVec3d dir2 = next->toSGVec3d() - cur->toSGVec3d();
+    dir2 = normalize(dir2);
+
+    printf("normalized dir2 is (%lf,%lf)\n", dir2.x(), dir2.y());
+
+    SGVec3d cp = cross(dir1,dir2);
+    if (cp.z() < 0.0)
+    {
+        turn_direction = -1;
+    }
+    else if (cp.z() > 0.0)
+    {
+        turn_direction = 1;
+    }
+    else
+    {
+        turn_direction = 0;
+    }
+
+    printf("turn direction is %d\n", turn_direction);
+
+    // Now find the average
+    SGVec3d avg = -dir1 + dir2;
+
+    printf("avg is (%lf,%lf)\n", avg.x(), avg.y());
+
+    // now determine which way the direction needs to point
+    if ((wind == -1) && (turn_direction == 1))
+    {
+        reverse_dir = true;
+    }
+    if ((wind == 1) && (turn_direction == -1))
+    {
+        reverse_dir = true;
+    }
+
+    printf("reverse dir is %d\n", reverse_dir);
+
+    // find the offset angle
+    geo_inverse_wgs_84( 0.0f, 0.0f, avg.x(), avg.y(), &offset_dir, &az2, &dist);
+    if (reverse_dir)
+    {
+        offset_dir += 180;
+        while (offset_dir >= 360.0)
+        {
+            offset_dir -= 360.0;
+        }
+    }
+    printf("offset direction is %lf\n", offset_dir);
+
+    // find the direction to the next point
+    geo_inverse_wgs_84( cur->x(), cur->y(), next->x(), next->y(), &next_dir, &az2, &dist);
+    printf("next direction is %lf\n", next_dir);
+
+    // calculate correct distance for the offset point
+    *dist1 = (-LINE_WIDTH)/sin(SGMiscd::deg2rad(next_dir-offset_dir));
+    *dist2 = (LINE_WIDTH)/sin(SGMiscd::deg2rad(next_dir-offset_dir));
+
+    return offset_dir;
+}
+
+// TODO: Should Be AddStartMarkingVerticies - and handle offset (used in LinearFeature::Finish only)
+double CalcStartMarkingVerticies( Point3D *cur, Point3D *next, int wind, int *prev_dir, double *dist1, double *dist2 )
+{
+    double offset_dir;
+    double az1, az2;
+    double dist;
+
+    printf("Find start angle for mark: cur(%lf,%lf), next(%lf,%lf)\n", cur->x(), cur->y(), next->x(), next->y());
+
+    // find the offset angle
+    geo_inverse_wgs_84( cur->x(), cur->y(), next->x(), next->y(), &offset_dir, &az2, &dist);
+    offset_dir -= 90;
+    if (offset_dir < 0)
+    {
+        offset_dir += 360;
+    }
+
+    printf("offset direction is %lf\n", offset_dir);
+
+    // calculate correct distance for the offset point
+    *dist1 = (-LINE_WIDTH);
+    *dist2 = (LINE_WIDTH);
+
+    return offset_dir;
+}
+
+// TODO: Should Be AddEndMarkingVerticies - and handle offset (used in LinearFeature::Finish only)
+double CalcEndMarkingVerticies( Point3D *prev, Point3D *cur, int wind, int *prev_dir, double *dist1, double *dist2 )
+{
+    double offset_dir;
+    double az1, az2;
+    double dist;
+
+    printf("Find end angle for mark: prev(%lf,%lf), cur(%lf,%lf)\n", prev->x(), prev->y(), cur->x(), cur->y());
+
+    // find the offset angle
+    geo_inverse_wgs_84( prev->x(), prev->y(), cur->x(), cur->y(), &offset_dir, &az2, &dist);
+    offset_dir -= 90;
+    if (offset_dir < 0)
+    {
+        offset_dir += 360;
+    }
+
+    printf("offset direction is %lf\n", offset_dir);
+
+    // calculate correct distance for the offset point
+    *dist1 = (-LINE_WIDTH);
+    *dist2 = (LINE_WIDTH);
+
+    return offset_dir;
+}
+
+// TODO: Should Be AddMarkingVerticies - and handle offset (used in LinearFeature::Finish only)
+double CalcMarkingVerticies( Point3D *prev, Point3D *cur, Point3D *next, int wind, int *prev_dir, double *dist1, double *dist2 )
+{
+    double offset_dir;
+    
+    // first, we need to see if we want to average the directions (we have both prev, and next)
+    if ( (prev->x() == 0.0f) && (prev->y() == 0.0f) )
+    {
+        // direction is 90 degrees less than from current to next
+        offset_dir = CalcStartMarkingVerticies( cur, next, wind, prev_dir, dist1, dist2 );
+        printf("got dist 1: %lf, dist2: %lf\n", *dist1, *dist2);
+    }
+    else if ( (next->x() == 0.0f) && (next->y() == 0.0f) )
+    {
+        // direction is 90 degrees less than from current to next
+        offset_dir = CalcEndMarkingVerticies( prev, cur, wind, prev_dir, dist1, dist2 );
+        printf("got dist 1: %lf, dist2: %lf\n", *dist1, *dist2);
+    }
+    else
+    {
+        offset_dir = CalcMiddleMarkingVerticies( prev, cur, next, wind, prev_dir, dist1, dist2 );
+        printf("got dist 1: %lf, dist2: %lf\n", *dist1, *dist2);
+    }
+
+    printf("return from CalcMarkingVerticies: dirst1: %lf, dist2: %lf\n", *dist1, *dist2);
+
+    return offset_dir;
+}
+
+// need to refactor this stuff.
+osg::Geode* FinishMarking( osg::Vec3dArray* verticies )
+{
+    osg::Geometry* stripe = NULL;
+    osg::Geode* geode_stripe = NULL;
+    osg::StateSet* ss_stripe = NULL;
+    osg::PolygonMode* polymode = NULL;
+    osg::Vec4Array* col_stripe = NULL;
+    int j;
+
+    // set up polygon offset
+    osg::PolygonOffset* polyoffset = new osg::PolygonOffset;
+    polyoffset->setFactor(-1.00f);
+    polyoffset->setUnits(-1.00f);
+
+    polymode = new osg::PolygonMode;
+    polymode->setMode(  osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE );
+
+    col_stripe = new osg::Vec4Array;
+    col_stripe->push_back(osg::Vec4(1.0f,1.0f,0.0f,1.0f));
+
+    // Create a new stripe geometry
+    stripe = new osg::Geometry;
+    stripe->setColorArray(col_stripe);
+    stripe->setColorBinding(osg::Geometry::BIND_OVERALL);
+
+    ss_stripe = new osg::StateSet();
+    ss_stripe->setAttributeAndModes(polyoffset,osg::StateAttribute::OVERRIDE|osg::StateAttribute::ON);
+#if WIREFRAME
+    ss_stripe->setAttributeAndModes(polymode,osg::StateAttribute::OVERRIDE|osg::StateAttribute::ON);
+#endif
+    geode_stripe = new osg::Geode();
+    geode_stripe->setStateSet(ss_stripe);
+
+    stripe->setVertexArray(verticies);
+
+    // create the index array for the stripe       
+    osg::DrawElementsUShort& drawElements = *(new osg::DrawElementsUShort(GL_TRIANGLE_STRIP,verticies->size()));
+
+    for (j=0; j<verticies->size(); j++)
+    {
+        drawElements[j] = j;
+    }
+            
+    for (int k=0; k<j; k++)
+    {
+        printf("index %d is %d\n", k, drawElements[k]);
+    }
+
+    stripe->addPrimitiveSet(&drawElements);
+
+    geode_stripe->addDrawable(stripe);
+   
+    return geode_stripe;
+}
+
+// TODO: Add this into Linear Feature
+osg::Vec3dArray* StartMarking()
+{
+    osg::Vec3dArray* v_marking = new osg::Vec3dArray;
+
+    return v_marking;
+}
+
+// TODO: move this into LinearFeature
+osg::Vec3dArray* CheckMarking(int cur_marking, int new_marking, osg::Vec3dArray* v_marking, osg::Group* airport)
+{
+    // check if we need to finish a marking
+    printf("Check Marking : current is %d, new is %d\n", cur_marking, new_marking);
+
+    if ( (v_marking != NULL) && (cur_marking != new_marking) )
+    {
+        printf("End current marking %d and start new marking %d : mark poly has %d verticies\n", cur_marking, new_marking, v_marking->size() );
+        for (int k=0; k<v_marking->size(); k++)
+        {
+            printf("vertex %d is (%lf, %lf)\n", k, v_marking->at(k).x(), v_marking->at(k).y());
+        }
+
+        // END THE CURRENT MARKING
+        airport->addChild(FinishMarking( v_marking ) );
+        v_marking = NULL;    
+    }
+            
+    // Start recording a new marking
+    if ( (v_marking == NULL) && (new_marking != 0) )
+    {
+        // start a new marking 
+        printf("Start new marking %d\n", new_marking);
+    
+        v_marking = StartMarking();
+    }
+
+    return v_marking;    
+}
diff --git a/src/Airports/GenAirports850/linearfeature.hxx b/src/Airports/GenAirports850/linearfeature.hxx
new file mode 100644
index 00000000..adf7ece1
--- /dev/null
+++ b/src/Airports/GenAirports850/linearfeature.hxx
@@ -0,0 +1,67 @@
+#ifndef _LINEARFEATURE_H_
+#define _LINEARFEATURE_H_
+
+#include <Polygon/polygon.hxx>
+#include <Polygon/superpoly.hxx>
+#include <Geometry/point3d.hxx>
+
+#include "texparams.hxx"
+
+#include <osg/Group>
+
+using std::string;
+
+class LinearFeature
+{
+public:
+    LinearFeature( char* desc )
+    {
+        if ( desc )
+        {
+            strcpy( description, desc );
+        }
+        else
+        {
+            strcpy( description, "none" );
+        }
+
+        offset = 0.0f;
+        closed = false;
+    }
+    
+    void AddNode( BezNode* b )
+    {
+        contour.push_back( b );
+    }
+
+    void SetClosed()
+    {
+        closed = true;
+    }
+
+    int Finish( osg::Group* airport );
+    int BuildBtg( float alt_m, superpoly_list* rwy_polys, texparams_list* texparams, TGPolygon* accum, TGPolygon* apt_base, TGPolygon* apt_clearing );
+    
+private:
+    char  description[256];
+    
+    // contour definition
+    BezContour  contour;
+
+    // TODO : Implement offset
+    double      offset;
+
+    bool        closed;
+};
+
+typedef std::vector <LinearFeature *> FeatureList;
+
+// add this to the class
+extern double CalcMarkingVerticies( Point3D *prev, Point3D *cur, Point3D *next, int wind, int *prev_dir, double *dist1, double *dist2 );
+
+// don't know what to do with these
+extern osg::Geode* FinishMarking( osg::Vec3dArray* verticies );
+extern osg::Vec3dArray* CheckMarking(int cur_marking, int new_marking, osg::Vec3dArray* v_marking, osg::Group* airport);
+
+#endif
+
diff --git a/src/Airports/GenAirports850/main.cxx b/src/Airports/GenAirports850/main.cxx
index b9a08473..3f3403fd 100644
--- a/src/Airports/GenAirports850/main.cxx
+++ b/src/Airports/GenAirports850/main.cxx
@@ -1,67 +1,85 @@
-// main.cxx -- main loop
-//
-// Written by Curtis Olson, started March 1998.
-//
-// Copyright (C) 1998  Curtis L. Olson  - http://www.flightgear.org/~curt
-//
-// 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.
-//
-// $Id: main.cxx,v 1.37 2005/12/19 15:53:21 curt Exp $
-//
+/* OpenSceneGraph example, osgshaderterrain.
+*
+*  Permission is hereby granted, free of charge, to any person obtaining a copy
+*  of this software and associated documentation files (the "Software"), to deal
+*  in the Software without restriction, including without limitation the rights
+*  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+*  copies of the Software, and to permit persons to whom the Software is
+*  furnished to do so, subject to the following conditions:
+*
+*  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+*  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+*  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+*  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+*  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+*  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+*  THE SOFTWARE.
+*/
 
+#include <osg/AlphaFunc>
+#include <osg/Billboard>
+#include <osg/BlendFunc>
+#include <osg/Depth>
+#include <osg/Geode>
+#include <osg/Geometry>
+#include <osg/Group>
+#include <osg/GL2Extensions>
+#include <osg/Material>
+#include <osg/Math>
+#include <osg/MatrixTransform>
+#include <osg/PolygonMode>
+#include <osg/PolygonOffset>
+#include <osg/Program>
+#include <osg/Projection>
+#include <osg/Shader>
+#include <osg/ShapeDrawable>
+#include <osg/StateSet>
+#include <osg/Switch>
+#include <osg/Texture2D>
+#include <osg/Uniform>
 
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
+#include <osgDB/ReadFile>
+#include <osgDB/FileUtils>
 
-#include <simgear/compiler.h>
-#include <simgear/structure/exception.hxx>
-#include <simgear/misc/sg_path.hxx>
+#include <osgUtil/Tessellator>
+#include <osgUtil/SmoothingVisitor>
+#include <osgText/Text>
+#include <osgViewer/Viewer>
+#include <osgViewer/ViewerEventHandlers>
+#include <osgUtil/Optimizer>
 
-#ifdef HAVE_STDLIB_H
-#include <stdlib.h>
-#endif
+#include <osgGA/TrackballManipulator>
+#include <osgGA/FlightManipulator>
+#include <osgGA/TerrainManipulator>
+#include <osgGA/StateSetManipulator>
 
-#include <list>
-#include <vector>
-
-#include <stdio.h>
 #include <string.h>
-#include <string>
 #include <iostream>
 
-#include <simgear/constants.h>
-#include <simgear/bucket/newbucket.hxx>
 #include <simgear/debug/logstream.hxx>
+#include <simgear/misc/sg_path.hxx>
+
 #include <simgear/misc/sgstream.hxx>
-#include <simgear/misc/strutils.hxx>
+//#include <simgear/misc/strutils.hxx>
 
 #include <Polygon/index.hxx>
-#include <Geometry/util.hxx>
 
-#include "build.hxx"
+#include "beznode.hxx"
+#include "closedpoly.hxx"
+#include "linearfeature.hxx"
+#include "parser.hxx"
 
-using std::vector;
-using std::cout;
-using std::endl;
-
-
-int nudge = 10;
-double slope_max = 0.2;
-
-static int is_in_range( string_list & runway_list, float min_lat, float max_lat, float min_lon, float max_lon );
+// TODO : Modularize this function
+// IDEAS 
+// 1) Start / Stop MarkStyle
+// 2) Start / Stop LightStyle
+// 3) holes
+// 4) CreatePavementSS(pavement type)
+// 5) CreateMarkingSS(marking type)
+// 6) CalcStripeOffsets should be AddMarkingVerticies, and take the 2 distances from pavement edge...
+// SCRAP ALL IDEAS - Markings are encapsulated in LinearFeature class. 
+// Start creates a new object
+// End closes the object (and should add it to a list - either in the parser, or ClosedPoly)
 
 // Display usage
 static void usage( int argc, char **argv ) {
@@ -73,6 +91,7 @@ static void usage( int argc, char **argv ) {
            << "[ --airport=abcd ]  [--tile=<tile>] [--chunk=<chunk>] [--verbose] [--help]");
 }
 
+
 void setup_default_elevation_sources(string_list& elev_src) {
     elev_src.push_back( "SRTM2-Africa-3" );
     elev_src.push_back( "SRTM2-Australia-3" );
@@ -86,58 +105,29 @@ void setup_default_elevation_sources(string_list& elev_src) {
     elev_src.push_back( "SRTM-30" );
 }
 
-// Display help and usage
-static void help( int argc, char **argv, const string_list& elev_src ) {
-    cout << "genapts generates airports for use in generating scenery for the FlightGear flight simulator.  ";
-    cout << "Airport, runway, and taxiway vector data and attributes are input, and generated 3D airports ";
-    cout << "are output for further processing by the TerraGear scenery creation tools.  ";
-    cout << "\n\n";
-    cout << "The standard input file is runways.dat.gz which is found in $FG_ROOT/Airports.  ";
-    cout << "This file is periodically generated for the FlightGear project by Robin Peel, who ";
-    cout << "maintains an airport database for both the X-Plane and FlightGear simulators.  ";
-    cout << "The format of this file is documented on the FlightGear web site.  ";
-    cout << "Any other input file corresponding to this format may be used as input to genapts.  ";
-    cout << "Input files may be gzipped or left as plain text as required.  ";
-    cout << "\n\n";
-    cout << "Processing all the world's airports takes a *long* time.  To cut down processing time ";
-    cout << "when only some airports are required, you may refine the input selection either by airport ";
-    cout << "or by area.  By airport, either one airport can be specified using --airport=abcd, where abcd is ";
-    cout << "a valid airport code eg. --airport-id=KORD, or a starting airport can be specified using --start-id=abcd ";
-    cout << "where once again abcd is a valid airport code.  In this case, all airports in the file subsequent to the ";
-    cout << "start-id are done.  This is convienient when re-starting after a previous error.  ";
-    cout << "\nAn input area may be specified by lat and lon extent using min and max lat and lon.  ";
-    cout << "Alternatively, you may specify a chunk (10 x 10 degrees) or tile (1 x 1 degree) using a string ";
-    cout << "such as eg. w080n40, e000s27.  ";
-    cout << "\nAn input file containing only a subset of the world's ";
-    cout << "airports may of course be used.";
-    cout << "\n\n";
-    cout << "It is necessary to generate the elevation data for the area of interest PRIOR TO GENERATING THE AIRPORTS.  ";
-    cout << "Failure to do this will result in airports being generated with an elevation of zero.  ";
-    cout << "The following subdirectories of the work-dir will be searched for elevation files:\n\n";
-    
-    string_list::const_iterator elev_src_it;
-    for (elev_src_it = elev_src.begin(); elev_src_it != elev_src.end(); elev_src_it++) {
-    	    cout << *elev_src_it << "\n";
-    }
-    cout << "\n";
-    usage( argc, argv );
-}
 
+// TODO: where do these belong
+int nudge = 10;
+double slope_max = 0.2;
 
-// reads the apt_full file and extracts and processes the individual
-// airport records
-int main( int argc, char **argv ) {
+int main(int argc, char **argv)
+{
     float min_lon = -180;
     float max_lon = 180;
     float min_lat = -90;
     float max_lat = 90;
-    bool ready_to_go = true;
+    bool  ready_to_go = true;
+    bool  view_osg = false;
 
     string_list elev_src;
     elev_src.clear();
     setup_default_elevation_sources(elev_src);
 
-    sglog().setLogLevels( SG_GENERAL, SG_INFO );
+    // Set verbose
+    sglog().setLogLevels( SG_GENERAL, SG_BULK );
+//    sglog().setLogLevels( SG_GENERAL, SG_INFO );
+
+    SG_LOG(SG_GENERAL, SG_INFO, "Run genapt");
 
     // parse arguments
     string work_dir = "";
@@ -145,63 +135,109 @@ int main( int argc, char **argv ) {
     string start_id = "";
     string airport_id = "";
     int arg_pos;
-    for (arg_pos = 1; arg_pos < argc; arg_pos++) {
+
+    for (arg_pos = 1; arg_pos < argc; arg_pos++) 
+    {
         string arg = argv[arg_pos];
-        if ( arg.find("--work=") == 0 ) {
+        if ( arg.find("--work=") == 0 ) 
+        {
             work_dir = arg.substr(7);
-	} else if ( arg.find("--input=") == 0 ) {
-	    input_file = arg.substr(8);
-        } else if ( arg.find("--terrain=") == 0 ) {
+    	} 
+        else if ( arg.find("--input=") == 0 ) 
+        {
+	        input_file = arg.substr(8);
+        } 
+        else if ( arg.find("--terrain=") == 0 ) 
+        {
             elev_src.push_back( arg.substr(10) );
- 	} else if ( arg.find("--start-id=") == 0 ) {
-	    start_id = arg.substr(11);
-	    ready_to_go = false;
- 	} else if ( arg.find("--nudge=") == 0 ) {
-	    nudge = atoi( arg.substr(8).c_str() );
-	} else if ( arg.find("--min-lon=") == 0 ) {
-	    min_lon = atof( arg.substr(10).c_str() );
-	} else if ( arg.find("--max-lon=") == 0 ) {
-	    max_lon = atof( arg.substr(10).c_str() );
-	} else if ( arg.find("--min-lat=") == 0 ) {
-	    min_lat = atof( arg.substr(10).c_str() );
-	} else if ( arg.find("--max-lat=") == 0 ) {
-	    max_lat = atof( arg.substr(10).c_str() );
-        } else if ( arg.find("--chunk=") == 0 ) {
-            tg::Rectangle rectangle = tg::parseChunk(arg.substr(8).c_str(),
-                                                     10.0);
+     	} 
+        else if ( arg.find("--start-id=") == 0 ) 
+        {
+    	    start_id = arg.substr(11);
+    	    ready_to_go = false;
+     	} 
+        else if ( arg.find("--nudge=") == 0 ) 
+        {
+    	    nudge = atoi( arg.substr(8).c_str() );
+    	} 
+        else if ( arg.find("--min-lon=") == 0 ) 
+        {
+    	    min_lon = atof( arg.substr(10).c_str() );
+    	} 
+        else if ( arg.find("--max-lon=") == 0 ) 
+        {
+    	    max_lon = atof( arg.substr(10).c_str() );
+    	} 
+        else if ( arg.find("--min-lat=") == 0 ) 
+        {
+    	    min_lat = atof( arg.substr(10).c_str() );
+    	} 
+        else if ( arg.find("--max-lat=") == 0 ) 
+        {
+    	    max_lat = atof( arg.substr(10).c_str() );
+        } 
+#if 0
+        else if ( arg.find("--chunk=") == 0 ) 
+        {
+            tg::Rectangle rectangle = tg::parseChunk(arg.substr(8).c_str(), 10.0);
             min_lon = rectangle.getMin().x();
             min_lat = rectangle.getMin().y();
             max_lon = rectangle.getMax().x();
             max_lat = rectangle.getMax().y();
-        } else if ( arg.find("--tile=") == 0 ) {
+        } 
+        else if ( arg.find("--tile=") == 0 ) 
+        {
             tg::Rectangle rectangle = tg::parseTile(arg.substr(7).c_str());
             min_lon = rectangle.getMin().x();
             min_lat = rectangle.getMin().y();
             max_lon = rectangle.getMax().x();
             max_lat = rectangle.getMax().y();
-	} else if ( arg.find("--airport=") == 0 ) {
-	    airport_id = arg.substr(10).c_str();
-	    ready_to_go = false;
-	} else if ( arg == "--clear-dem-path" ) {
-	    elev_src.clear();
-	} else if ( arg.find("--dem-path=") == 0 ) {
-	    elev_src.push_back( arg.substr(11) );
-	} else if ( (arg.find("--verbose") == 0) || (arg.find("-v") == 0) ) {
-	    sglog().setLogLevels( SG_GENERAL, SG_BULK );
-	} else if ( (arg.find("--max-slope=") == 0) ) {
-	    slope_max = atof( arg.substr(12).c_str() );
-	} else if ( (arg.find("--help") == 0) || (arg.find("-h") == 0) ) {
-	    help( argc, argv, elev_src );
-	    exit(-1);
-	} else {
-	    usage( argc, argv );
-	    exit(-1);
-	}
+    	} 
+#endif
+        else if ( arg.find("--airport=") == 0 ) 
+        {
+    	    airport_id = arg.substr(10).c_str();
+    	    ready_to_go = false;
+    	} 
+        else if ( arg == "--clear-dem-path" ) 
+        {
+    	    elev_src.clear();
+    	} 
+        else if ( arg.find("--dem-path=") == 0 ) 
+        {
+    	    elev_src.push_back( arg.substr(11) );
+    	} 
+        else if ( (arg.find("--verbose") == 0) || (arg.find("-v") == 0) ) 
+        {
+    	    sglog().setLogLevels( SG_GENERAL, SG_BULK );
+    	} 
+        else if ( arg.find("--view") == 0 )
+        {
+            SG_LOG(SG_GENERAL, SG_INFO, "Found --view : view OSG model" );
+            view_osg = true;
+        }
+        else if ( (arg.find("--max-slope=") == 0) ) 
+        {
+    	    slope_max = atof( arg.substr(12).c_str() );
+    	} 
+#if 0
+        else if ( (arg.find("--help") == 0) || (arg.find("-h") == 0) ) 
+        {
+    	    help( argc, argv, elev_src );
+    	    exit(-1);
+    	} 
+#endif
+        else 
+        {
+    	    usage( argc, argv );
+    	    exit(-1);
+    	}
     }
 
     SG_LOG(SG_GENERAL, SG_INFO, "Input file = " << input_file);
     SG_LOG(SG_GENERAL, SG_INFO, "Terrain sources = ");
-    for ( unsigned int i = 0; i < elev_src.size(); ++i ) {
+    for ( unsigned int i = 0; i < elev_src.size(); ++i ) 
+    {
         SG_LOG(SG_GENERAL, SG_INFO, "  " << work_dir << "/" << elev_src[i] );
     }
     SG_LOG(SG_GENERAL, SG_INFO, "Work directory = " << work_dir);
@@ -210,26 +246,29 @@ int main( int argc, char **argv ) {
     SG_LOG(SG_GENERAL, SG_INFO, "Latitude = " << min_lat << ':' << max_lat);
 
     if (max_lon < min_lon || max_lat < min_lat ||
-	min_lat < -90 || max_lat > 90 ||
-	min_lon < -180 || max_lon > 180) {
+	    min_lat < -90 || max_lat > 90 ||
+	    min_lon < -180 || max_lon > 180) 
+    {
         SG_LOG(SG_GENERAL, SG_ALERT, "Bad longitude or latitude");
-	exit(1);
+    	exit(1);
     }
 
-    if ( work_dir == "" ) {
-	SG_LOG( SG_GENERAL, SG_ALERT, 
-		"Error: no work directory specified." );
-	usage( argc, argv );
-	exit(-1);
+    if ( work_dir == "" ) 
+    {
+    	SG_LOG( SG_GENERAL, SG_ALERT, "Error: no work directory specified." );
+    	usage( argc, argv );
+	    exit(-1);
     }
 
-    if ( input_file == "" ) {
-	SG_LOG( SG_GENERAL, SG_ALERT, 
-		"Error: no input file." );
-	exit(-1);
+    if ( input_file == "" ) 
+    {
+    	SG_LOG( SG_GENERAL, SG_ALERT,  "Error: no input file." );
+    	exit(-1);
     }
 
     // make work directory
+    SG_LOG(SG_GENERAL, SG_INFO, "Creating airportarea directory");
+
     string airportareadir=work_dir+"/AirportArea";
     SGPath sgp( airportareadir );
     sgp.append( "dummy" );
@@ -242,264 +281,60 @@ int main( int argc, char **argv ) {
     poly_index_init( counter_file );
 
     sg_gzifstream in( input_file );
-    if ( !in.is_open() ) {
+    if ( !in.is_open() ) 
+    {
         SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << input_file );
         exit(-1);
     }
 
-    string_list runways_list;
-    string_list water_rw_list;
-    string_list beacon_list;
-    string_list tower_list;
-    string_list windsock_list;
-    string_list light_list;
+    SG_LOG(SG_GENERAL, SG_INFO, "Creating parser");
 
-    vector<string> token;
-    string last_apt_id = "";
-    string last_apt_info = "";
-    string last_apt_type = "";
-    string line;
-    char tmp[2048];
+    // Create the parser...
+    Parser* parser = new Parser(input_file);
 
-    while ( ! in.eof() ) {
-	in.getline(tmp, 2048);
-	line = tmp;
-	SG_LOG( SG_GENERAL, SG_DEBUG, "-> '" << line << "'" );
-        if ( line.length() ) {
-            token = simgear::strutils::split( line );
-            if ( token.size() ) {
-                SG_LOG( SG_GENERAL, SG_DEBUG, "token[0] " << token[0] );
-            }
-        } else {
-            token.clear();
-        }
+    SG_LOG(SG_GENERAL, SG_INFO, "Parse katl");
+    parser->Parse((char*)"katl");
 
-        if ( !line.length() || !token.size() ) {
-            // empty line, skip
-        } else if ( (token[0] == "#") || (token[0] == "//") ) {
-	    // comment, skip
-        } else if ( token[0] == "I" ) {
-            // First line, indicates IBM (i.e. DOS line endings I
-            // believe.)
+    if (view_osg)
+    {
+        // just view in OSG
+        osg::Group* airportNode;
 
-            // move past this line and read and discard the next line
-            // which is the version and copyright information
-            in.getline(tmp, 2048);
-            vector<string> vers_token = simgear::strutils::split( tmp );
-            SG_LOG( SG_GENERAL, SG_INFO, "Data version = " << vers_token[0] );
-	} else if ( token[0] == "1" /* Airport */ ||
-                    token[0] == "16" /* Seaplane base */ ||
-                    token[0] == "17" /* Heliport */ ) {
+        SG_LOG(SG_GENERAL, SG_INFO, "View OSG");
+        airportNode = parser->CreateOsgGroup();
 
-            // extract some airport runway info
-            string rwy;
-            float lat, lon;
-            
-            string id = token[4];
-            int elev = atoi( token[1].c_str() );
-            SG_LOG( SG_GENERAL, SG_INFO, "Next airport = " << id << " " << elev );
+        // construct the viewer.
+        osgViewer::Viewer viewer;
 
-            if ( !last_apt_id.empty()) {
-                if ( runways_list.size() ) {
-                    vector<string> rwy_token
-                        = simgear::strutils::split( runways_list[0] );
-		    if ( token[0] == "100" ){
-                    rwy = token[8];
-                    lat = atof( token[9].c_str() );
-                    lon = atof( token[10].c_str() );
-		    }
+        // add the thread model handler
+        viewer.addEventHandler(new osgViewer::ThreadingHandler);
 
-                    if ( airport_id.length() && airport_id == last_apt_id ) {
-                        ready_to_go = true;
-                    } else if ( start_id.length() && start_id == last_apt_id ) {
-                        ready_to_go = true;
-                    }
+        // add the window size toggle handler
+        viewer.addEventHandler(new osgViewer::WindowSizeHandler);
 
-                    if ( ready_to_go ) {
-                        // check point our location
-                        char command[256];
-                        sprintf( command,
-                                 "echo before building %s >> %s",
-                                 last_apt_id.c_str(),
-                                 lastaptfile.c_str() );
-                        system( command );
+        // add model to viewer.
+        viewer.setSceneData( airportNode );
+        viewer.setUpViewAcrossAllScreens();
 
-                        // process previous record
-                        // process_airport(last_apt_id, runways_list, argv[2]);
-                        try {
-                            if ( last_apt_type == "16" /* Seaplane base */ ) {
-                                // skip building seaplane bases
-                            } else {
-                                if( is_in_range( runways_list, min_lat, max_lat, min_lon, max_lon ) ) {
-                                    build_airport( last_apt_id,
-                                                   elev * SG_FEET_TO_METER,
-                                                   runways_list,
-						   water_rw_list,
-                                                   beacon_list,
-                                                   tower_list,
-                                                   windsock_list,
-						   light_list,
-                                                   work_dir, elev_src );
-                                }
-                            }
-                        } catch (sg_exception &e) {
-                            SG_LOG( SG_GENERAL, SG_ALERT,
-                                    "Failed to build airport = "
-                                    << last_apt_id );
-                            SG_LOG( SG_GENERAL, SG_ALERT, "Exception: "
-                                    << e.getMessage() );
-                            exit(-1);
-                        }
-                        if ( airport_id.length() ) {
-                            ready_to_go = false;
-                        }
-                    }
-		} else {
-		    if(!airport_id.length()) {
-			SG_LOG(SG_GENERAL, SG_INFO,
-                               "ERRO: No runways, skipping = " << id);
-		    }
-		}
-            }
+        // create the windows and run the threads.
+        viewer.realize();
+    
+        viewer.setCameraManipulator(new osgGA::TrackballManipulator());
 
-            last_apt_id = id;
-            last_apt_info = line;
-            last_apt_type = token[0];
+        osgUtil::Optimizer optimzer;
+        optimzer.optimize(airportNode);
 
-            // clear runway list for start of next airport
-            runways_list.clear();
-	    water_rw_list.clear();
-            beacon_list.clear();
-            tower_list.clear();
-            windsock_list.clear();
-	    light_list.clear();
-        } else if ( token[0] == "100" || token[0] == "102") {
-            // runway entry (Land or heli)
-            runways_list.push_back(line);
-	} else if ( token[0] == "101" ) {
-            // Water runway. Here we don't have to create any textures.
-            // Only place some buoys
-            water_rw_list.push_back(line);
-        } else if ( token[0] == "18" ) {
-            // beacon entry
-            beacon_list.push_back(line);
-        } else if ( token[0] == "14" ) {
-            // control tower entry
-            tower_list.push_back(line);
-        } else if ( token[0] == "19" ) {
-            // windsock entry
-            windsock_list.push_back(line);
-	} else if ( token[0] == "21" ) {
-            // PAPI / VASI list
-            light_list.push_back(line);
-        } else if ( token[0] == "15" ) {
-            // ignore custom startup locations
-        } else if ( token[0] == "50" || token[0] == "51" || token[0] == "52" 
-                    || token[0] == "53" || token[0] == "54" || token[0] == "55" 
-                    || token[0] == "56" )
-        {
-            // ignore frequency entries
-        } else if ( token[0] == "99" ) {
-            SG_LOG( SG_GENERAL, SG_ALERT, "End of file reached" );
-	} else if ( token[0] == "00" ) {
-		// ??
-	} else if ( token[0] >= "110" ) {
-		//ignore taxiways for now
-	} else if ( token[0] == "10" ) {
-		//ignore old taxiways for now
-        } else {
-            SG_LOG( SG_GENERAL, SG_ALERT, 
-                    "Unknown line in file: " << line );
-            exit(-1);
-        }
+        viewer.run();    
+    }
+    else
+    {
+        // write a .btg file....
+        SG_LOG(SG_GENERAL, SG_INFO, "Write BTG");
+        parser->WriteBtg(work_dir, elev_src);
     }
 
-    cout << "last_apt_id.length() = " << last_apt_id.length() << endl;
-
-    if ( !last_apt_id.empty()) {
-        char ctmp, tmpid[32], rwy[32];
-        string id;
-        float lat, lon;
-        int elev = 0;
-
-        if ( runways_list.size() ) {
-            sscanf( runways_list[0].c_str(), "%c %s %s %f %f",
-                    &ctmp, tmpid, rwy, &lat, &lon );
-        }
-
-        if ( start_id.length() && start_id == last_apt_id ) {
-            ready_to_go = true;
-        }
-
-        if ( ready_to_go ) {
-            // check point our location
-            char command[256];
-            sprintf( command,
-                     "echo before building %s >> %s",
-                     last_apt_id.c_str(),
-                     lastaptfile.c_str() );
-            system( command );
-
-            // process previous record
-            // process_airport(last_apt_id, runways_list, argv[2]);
-            try {
-                if ( last_apt_type == "16" /* Seaplane base */ ) {
-                    // skip building seaplane bases
-                } else {
-                    if( is_in_range( runways_list, min_lat, max_lat, min_lon, max_lon ) ) {
-                        build_airport( last_apt_id, elev * SG_FEET_TO_METER,
-                                       runways_list,
-				       water_rw_list,
-                                       beacon_list,
-                                       tower_list,
-                                       windsock_list,
-				       light_list,
-                                       work_dir, elev_src );
-                    }
-                }
-            } catch (sg_exception &e) {
-                SG_LOG( SG_GENERAL, SG_ALERT,
-                        "Failed to build airport = "
-                        << last_apt_id );
-                SG_LOG( SG_GENERAL, SG_ALERT, "Exception: "
-                        << e.getMessage() );
-                exit(-1);
-            }
-        } else {
-            SG_LOG(SG_GENERAL, SG_INFO, "Skipping airport " << id);
-        }
-    }
-
-    SG_LOG(SG_GENERAL, SG_INFO, "[FINISHED CORRECTLY]");
+    SG_LOG(SG_GENERAL, SG_INFO, "Done");
 
     return 0;
 }
 
-static int is_in_range( string_list & runways_raw, float min_lat, float max_lat, float min_lon, float max_lon )
-{
-    int i;
-    int rwy_count = 0;
-    double apt_lon = 0.0, apt_lat = 0.0;
-
-    for ( i = 0; i < (int)runways_raw.size(); ++i ) {
-        ++rwy_count;
-
-	string rwy_str = runways_raw[i];
-        vector<string> token = simgear::strutils::split( rwy_str );
-
-        apt_lat += atof( token[1].c_str() );
-        apt_lon += atof( token[2].c_str() );
-    }
-
-    if( rwy_count > 0 ) {
-      apt_lat /= rwy_count;
-      apt_lon /= rwy_count;
-    }
-
-    if( apt_lat >= min_lat && apt_lat <= max_lat &&
-        apt_lon >= min_lon && apt_lon <= max_lon ) {
-        return 1;
-    }
-
-    return 0;
-}
diff --git a/src/Airports/GenAirports850/parser.cxx b/src/Airports/GenAirports850/parser.cxx
new file mode 100644
index 00000000..13214a50
--- /dev/null
+++ b/src/Airports/GenAirports850/parser.cxx
@@ -0,0 +1,451 @@
+#include <simgear/debug/logstream.hxx>
+#include <simgear/misc/sgstream.hxx>
+
+#include "parser.hxx"
+
+#if 0
+int Parser::ParseBoundry(char* line)
+{
+    return 0;    
+} 
+#endif
+
+int Parser::Parse( char* icao )
+{
+    string line;
+    char tmp[2048];
+
+    sg_gzifstream in( filename );
+    if ( !in.is_open() ) 
+    {
+        SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << filename );
+        exit(-1);
+    }
+
+    while ( !in.eof() ) 
+    {
+    	in.getline(tmp, 2048);
+    	line = tmp;
+
+        // Parse the line
+        ParseLine(tmp);
+    }
+
+    return 1;
+}
+
+BezNode* Parser::ParseNode( int type, char* line, BezNode* prevNode )
+{
+    double lat, lon;
+    double ctrl_lat, ctrl_lon;
+    int mark_type, light_type;
+    BezNode *curNode = NULL;
+
+    bool hasCtrl;
+    bool close;
+    bool term;
+    bool hasMarking = false;
+    bool hasLighting = false;
+    int  numParams;
+
+    switch(type)
+    {
+        case NODE_CODE:
+            hasCtrl = false;
+            close   = false;
+            term    = false;
+            break;
+
+        case BEZIER_NODE_CODE:
+            hasCtrl = true;
+            close   = false;
+            term    = false;
+            break;
+
+        case CLOSE_NODE_CODE:
+            hasCtrl = false;
+            close   = true;
+            term    = false;
+            break;
+
+        case CLOSE_BEZIER_NODE_CODE:
+            hasCtrl = true;
+            close   = true;
+            term    = false;
+            break;
+
+        case TERM_NODE_CODE:
+            hasCtrl = false;
+            close   = false;
+            term    = true;
+            break;
+
+        case TERM_BEZIER_NODE_CODE:
+            hasCtrl = true;
+            close   = false;
+            term    = true;
+            break;
+    }
+
+    // parse the line
+    if (hasCtrl)
+    {
+        numParams = sscanf(line, "%lf %lf %lf %lf %d %d", &lat, &lon, &ctrl_lat, &ctrl_lon, &mark_type, &light_type);
+        if (numParams > 4)
+        {
+            hasMarking = true;
+        }
+        if (numParams > 5)
+        {
+            hasLighting = true;
+        }
+    }
+    else
+    {
+        numParams = sscanf(line, "%lf %lf %d %d", &lat, &lon, &mark_type, &light_type);
+        if (numParams > 2)
+        {
+            hasMarking = true;
+        }
+        if (numParams > 3)
+        {
+            hasLighting = true;
+        }
+    }
+
+    if ( (prevNode) && (prevNode->IsAt( lat, lon )) )
+    {
+        curNode = prevNode;
+
+        // editing already existent node
+        if (hasCtrl)
+        {        
+            // we have ctrl info -> set it
+            curNode->SetNextCp(ctrl_lat,ctrl_lon);
+        }
+        else
+        {
+            // no control info - make sure we clear anything in next
+            curNode->ClearNextCp();
+        }
+    }
+
+    // if this is a new node, add it - as first part never has prev cp
+    if (curNode == NULL)
+    {
+        if (hasCtrl)
+        {
+            curNode = new BezNode(lat, lon, ctrl_lat, ctrl_lon);
+        }
+        else
+        {
+            curNode = new BezNode(lat, lon);
+        }
+    }
+
+    if (hasMarking)
+    {
+       curNode->SetMarking( mark_type );
+    }
+
+    if (hasLighting)
+    {
+       curNode->SetLighting( light_type );
+    }
+
+    return curNode;
+}
+
+LinearFeature* Parser::ParseFeature( char* line )
+{
+    LinearFeature* feature;
+
+    if (strlen( line ))
+    {
+        feature = new LinearFeature(line);
+    }
+    else
+    {
+        feature = new LinearFeature(NULL);
+    }
+        
+    SG_LOG(SG_GENERAL, SG_DEBUG, "Creating Linear Feature with desription \"" << line << "\"");
+
+    return feature;
+}
+
+ClosedPoly* Parser::ParsePavement( char* line )
+{
+    ClosedPoly* poly;
+    int   st = 0;
+    float s = 0.0f;
+    float th = 0.0f;
+    char  desc[256];
+    char  *d = NULL;
+    int   numParams;
+
+    numParams = sscanf(line, "%d %f %f %ls", &st, &s, &th, desc);
+
+    if (numParams == 4)
+    {
+        d = strstr(line,desc);
+    }
+
+    SG_LOG(SG_GENERAL, SG_DEBUG, "Creating Closed Poly with st " << st << " smoothness " << s << " thexture heading " << th << " and description " << d);
+    poly = new ClosedPoly(st, s, th, d);
+
+    return poly;
+}
+
+int Parser::SetState( int state )
+{
+    // if we are currently parsing pavement, the oly way we know we are done 
+    // is when we get a non-node line to parse
+    if ( cur_airport && cur_state == STATE_PARSE_PAVEMENT )
+    {
+        SG_LOG(SG_GENERAL, SG_DEBUG, "Closing and Adding pavement");
+        cur_pavement->Finish();
+        cur_airport->AddPavement( cur_pavement );
+        cur_pavement = NULL;
+    } 
+
+    cur_state = state;
+}
+
+// TODO: This should be a loop here, and main should just pass the file name and airport code...
+int Parser::ParseLine(char* line)
+{
+    char*  tok;
+    int    code;
+
+    BezNode* cur_node = NULL;
+
+    // Get the number code
+    tok = strtok(line, " \t\r\n");
+
+    if (tok)
+    {
+        line += strlen(tok)+1;
+        code = atoi(tok);
+
+        switch(code)
+        {
+            case LAND_AIRPORT_CODE: 
+            case SEA_AIRPORT_CODE:
+                SetState( STATE_PARSE_SIMPLE );
+                SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing land airport: " << line);
+                cur_airport = new Airport( code, line );
+                airports.push_back( cur_airport );
+                break;
+            case HELIPORT_CODE:
+                SetState( STATE_PARSE_SIMPLE );
+                SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing heliport: " << line);
+                break;
+
+            case LAND_RUNWAY_CODE:
+                SetState( STATE_PARSE_SIMPLE );
+                SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing runway: " << line);
+                cur_runway = new Runway(line);
+                if (cur_airport)
+                {
+                    cur_airport->AddRunway( cur_runway );
+                }
+                break;
+
+            case WATER_RUNWAY_CODE:
+                SetState( STATE_PARSE_SIMPLE );
+                SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing water runway: " << line);
+                break;
+            case HELIPAD_CODE:
+                SetState( STATE_PARSE_SIMPLE );
+                SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing helipad: " << line);
+                break;
+
+            case PAVEMENT_CODE:
+                SetState( STATE_PARSE_PAVEMENT );
+                cur_pavement  = ParsePavement( line );
+                break;
+
+            case LINEAR_FEATURE_CODE:
+                SetState( STATE_PARSE_FEATURE );
+                cur_feat = ParseFeature( line );
+                break;
+
+            case BOUNDRY_CODE:
+                SetState( STATE_PARSE_BOUNDARY );
+                SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing airport boundary: " << line);
+//              ParseBoundry(line); 
+                break;
+
+            case NODE_CODE:
+            case BEZIER_NODE_CODE:
+                SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing node: " << line);
+                cur_node = ParseNode( code, line, prev_node );
+
+                if ( prev_node && (cur_node != prev_node) )
+                {
+                    // prev node is done - process it\n");
+                    if ( cur_state == STATE_PARSE_PAVEMENT )
+                    {
+                        cur_pavement->AddNode( prev_node );
+                    }
+                    else if ( cur_state == STATE_PARSE_FEATURE )
+                    {
+                        cur_feat->AddNode( prev_node );
+                    }
+                }
+
+                prev_node = cur_node;
+                break;
+
+            case CLOSE_NODE_CODE:
+            case CLOSE_BEZIER_NODE_CODE:
+                SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing close loop node: " << line);
+                cur_node = ParseNode( code, line, prev_node );
+
+                if ( cur_state == STATE_PARSE_PAVEMENT )
+                {
+                    if (cur_node != prev_node)
+                    {
+                        cur_pavement->AddNode( prev_node );
+                        cur_pavement->AddNode( cur_node );
+                    }
+                    else
+                    {
+                        cur_pavement->AddNode( cur_node );
+                    }
+                    cur_pavement->CloseCurContour();
+                }
+                else if ( cur_state == STATE_PARSE_FEATURE )
+                {
+                    if (cur_node != prev_node)
+                    {
+                        cur_feat->AddNode( prev_node );
+                        cur_feat->AddNode( cur_node );
+                    }
+                    else
+                    {
+                        cur_feat->AddNode( cur_node );
+                    }
+                    if (cur_airport)
+                    {
+                        cur_airport->AddFeature( cur_feat );
+                    }
+                    SetState( STATE_NONE );
+                }
+                prev_node = NULL;
+                cur_node  = NULL;
+                break;
+
+            case TERM_NODE_CODE:
+            case TERM_BEZIER_NODE_CODE:
+                SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing termination node: " << line);
+                cur_node = ParseNode( code, line, prev_node );
+
+                if ( cur_state == STATE_PARSE_FEATURE )
+                {
+                    if (cur_node != prev_node)
+                    {
+                        cur_feat->AddNode( prev_node );
+                        cur_feat->AddNode( cur_node );
+                    }
+                    else
+                    {
+                        cur_feat->AddNode( cur_node );
+                    }
+                    if (cur_airport)
+                    {
+                        cur_airport->AddFeature( cur_feat );
+                    }
+                    SetState( STATE_NONE );
+                }
+                prev_node = NULL;
+                cur_node  = NULL;
+                break;
+
+            case AIRPORT_VIEWPOINT_CODE:
+                SetState( STATE_PARSE_SIMPLE );
+                SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing viewpoint: " << line);
+                break;
+            case AIRPLANE_STARTUP_LOCATION_CODE:
+                SetState( STATE_PARSE_SIMPLE );
+                SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing airplane startup location: " << line);
+                break;
+            case LIGHT_BEACON_CODE:
+                SetState( STATE_PARSE_SIMPLE );
+                SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing light beacon: " << line);
+                break;
+            case WINDSOCK_CODE:
+                SetState( STATE_PARSE_SIMPLE );
+                SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing windsock: " << line);
+                break;
+            case TAXIWAY_SIGN:
+                SetState( STATE_PARSE_SIMPLE );
+                SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing taxiway sign: " << line);
+                break;
+            case LIGHTING_OBJECT:
+                SetState( STATE_PARSE_SIMPLE );
+                SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing lighting object: " << line);
+                break;
+            case COMM_FREQ1_CODE:
+                SetState( STATE_PARSE_SIMPLE );
+                SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing commfreq 1: " << line);
+                break;
+            case COMM_FREQ2_CODE:
+                SetState( STATE_PARSE_SIMPLE );
+                SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing commfreq 2: " << line);
+                break;
+            case COMM_FREQ3_CODE:
+                SetState( STATE_PARSE_SIMPLE );
+                SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing commfreq 3: " << line);
+                break;
+            case COMM_FREQ4_CODE:
+                SetState( STATE_PARSE_SIMPLE );
+                SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing commfreq 4: " << line);
+                break;
+            case COMM_FREQ5_CODE:
+                SetState( STATE_PARSE_SIMPLE );
+                SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing commfreq 5: " << line);
+                break;
+            case COMM_FREQ6_CODE:
+                SetState( STATE_PARSE_SIMPLE );
+                SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing commfreq 6: " << line);
+                break;
+            case COMM_FREQ7_CODE:
+                SetState( STATE_PARSE_SIMPLE );
+                SG_LOG(SG_GENERAL, SG_DEBUG, "Parsing commfreq 7: " << line);
+                break;
+            case END_OF_FILE :
+                SG_LOG(SG_GENERAL, SG_DEBUG, "Reached end of file");
+                SetState( STATE_DONE );
+                break;
+        }
+    }
+
+    return 0;
+}
+
+void Parser::WriteBtg( const string& root, const string_list& elev_src )
+{
+    int i;
+
+    for (i=0; i<airports.size(); i++)
+    {
+        airports[i]->BuildBtg( root, elev_src );
+    }
+}
+
+osg::Group* Parser::CreateOsgGroup( void )
+{
+    osg::Group* airportNode = new osg::Group();
+    int i;
+
+    for (i=0; i<airports.size(); i++)
+    {
+        SG_LOG(SG_GENERAL, SG_DEBUG, "Add airport " << i << " to node");
+        airports[i]->BuildOsg( airportNode );
+    }
+
+    return airportNode;
+}
+
diff --git a/src/Airports/GenAirports850/parser.hxx b/src/Airports/GenAirports850/parser.hxx
new file mode 100644
index 00000000..a8f9328c
--- /dev/null
+++ b/src/Airports/GenAirports850/parser.hxx
@@ -0,0 +1,99 @@
+#ifndef _PARSER_H_
+#define _PARSER_H_
+
+#include "beznode.hxx"
+#include "closedpoly.hxx"
+#include "linearfeature.hxx"
+#include "runway.hxx"
+#include "airport.hxx"
+
+#define STATE_NONE                  (0)
+#define STATE_PARSE_SIMPLE          (1)
+#define STATE_PARSE_BOUNDARY        (2)
+#define STATE_PARSE_PAVEMENT        (3)
+#define STATE_PARSE_FEATURE         (4)
+#define STATE_DONE                  (10)
+
+#define MAXLINE (256)
+
+#define LAND_AIRPORT_CODE               (1)
+#define SEA_AIRPORT_CODE                (16)
+#define HELIPORT_CODE                   (17)
+
+#define LAND_RUNWAY_CODE                (100)
+#define WATER_RUNWAY_CODE               (101)
+#define HELIPAD_CODE                    (102)
+
+#define PAVEMENT_CODE                   (110)
+#define LINEAR_FEATURE_CODE             (120)
+#define BOUNDRY_CODE                    (130)
+
+#define NODE_CODE                       (111)
+#define BEZIER_NODE_CODE                (112)
+#define CLOSE_NODE_CODE                 (113)
+#define CLOSE_BEZIER_NODE_CODE          (114)
+#define TERM_NODE_CODE                  (115)
+#define TERM_BEZIER_NODE_CODE           (116)
+
+#define AIRPORT_VIEWPOINT_CODE          (14)
+#define AIRPLANE_STARTUP_LOCATION_CODE  (15)
+#define LIGHT_BEACON_CODE               (18)
+#define WINDSOCK_CODE                   (19)
+#define TAXIWAY_SIGN                    (20)
+#define LIGHTING_OBJECT                 (21)
+
+#define COMM_FREQ1_CODE                 (50)               
+#define COMM_FREQ2_CODE                 (51)               
+#define COMM_FREQ3_CODE                 (52)               
+#define COMM_FREQ4_CODE                 (53)               
+#define COMM_FREQ5_CODE                 (54)               
+#define COMM_FREQ6_CODE                 (55)               
+#define COMM_FREQ7_CODE                 (56)               
+
+#define END_OF_FILE                     (99)
+
+class Parser
+{
+public:
+    Parser(string& f)
+    {
+        filename        = f;
+        cur_airport     = NULL;
+        cur_pavement    = NULL;
+        cur_feat        = NULL;
+        prev_node       = NULL;
+        cur_state       = STATE_NONE;
+    }
+    
+    int             Parse( char* icao );
+    void            WriteBtg( const string& root, const string_list& elev_src );
+    osg::Group*     CreateOsgGroup( void );
+    
+private:
+    int             SetState( int state );
+
+    BezNode*        ParseNode( int type, char* line, BezNode* prevNode );
+    LinearFeature*  ParseFeature( char* line );
+    ClosedPoly*     ParsePavement( char* line );
+    osg::Geode*     ParseRunway(char* line );
+    int             ParseLine( char* line );
+
+//    int ParseBoundry(char* line, Node* airport);
+
+    string          filename;
+
+    // a polygon conists of an array of contours 
+    // (first is outside boundry, remaining are holes)
+    Airport*        cur_airport;
+    Runway*         cur_runway;
+    ClosedPoly*     cur_pavement;
+    LinearFeature*  cur_feat;
+    BezNode*        prev_node;
+
+    AirportList     airports;
+
+    int cur_state;
+};
+
+#endif
+
diff --git a/src/Airports/GenAirports850/runway.cxx b/src/Airports/GenAirports850/runway.cxx
index eac68614..9b4f5328 100644
--- a/src/Airports/GenAirports850/runway.cxx
+++ b/src/Airports/GenAirports850/runway.cxx
@@ -1,49 +1,113 @@
-// area.c -- routines to assist with inserting "areas" into FG terrain
-//
-// Written by Curtis Olson, started March 1998.
-//
-// Copyright (C) 1998  Curtis L. Olson  - http://www.flightgear.org/~curt
-//
-// 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.
-//
-// $Id: runway.cxx,v 1.18 2004-11-19 22:25:49 curt Exp $
-//
 
-
-#include <math.h>
-#include <stdio.h>
-
-#include <simgear/constants.h>
+#include <simgear/compiler.h>
+#include <simgear/structure/exception.hxx>
 #include <simgear/debug/logstream.hxx>
-#include <simgear/math/sg_types.hxx>
+#include <simgear/bucket/newbucket.hxx>
 #include <simgear/math/sg_geodesy.hxx>
 
+#include <osg/Geode>
+#include <osg/Geometry>
+#include <osg/Group>
+
+#include <Polygon/polygon.hxx>
+
+#include "beznode.hxx"
 #include "runway.hxx"
-#include "point2d.hxx"
 
+extern int nudge;
 
-// given a runway center point, length, width, and heading, and
-// altitude (meters) generate the lon and lat 4 corners using wgs84
-// math.
-TGPolygon gen_wgs84_area( Point3D origin,
-                          double length_m,
-                          double displ1, double displ2,
-                          double width_m,
-                          double heading_deg,
-                          double alt_m,
-                          bool add_mid )
+Runway::Runway(char* definition)
+{
+    double az2;
+
+    // format:
+    // runway   width   surface  shoulder  smoothness centerline lights  edge lighting distance remaining signs
+    // 100      46.02   2        1         0.00       1                  2             1 
+    //     
+    //          runway number  runway end lat runway end long   threshold  overrun  markings  approach lighting
+    //          09L            33.63470475     -084.44798671    0.00       120.09   3         7 
+    // 
+    //          touchdown zone lighting  runway end identifier lights
+    //          0                        1                              
+    //
+    //          runway number  runway end lat runway end long   threshold  overrun  markings  approach lighting
+    //          27R             33.63469907   -084.40893004     0.00       120.09   3         6 
+    //
+    //          touchdown zone lighting  runway end identifier lights
+    //          0                        1
+
+    // Parse the line
+    // 46.02   2   1 0.00 1 2 1 09L  33.63470475 -084.44798671    0.00  120.09 3  7 0 1 27R  33.63469907 -084.40893004    0.00  120.09 3  6 0 1
+
+    // int fscanf(FILE *stream, const char *format, ...);
+    sscanf(definition, "%lf %d %d %lf %d %d %d %s %lf %lf %lf %lf %d %d %d %d %s %lf %lf %lf %lf %d %d %d %d", 
+        &width,  &surface, &shoulder, &smoothness, &centerline_lights, &edge_lights, &dist_remain_signs, 
+        rwnum[0], &lat[0], &lon[0], &threshold[0], &overrun[0], &marking[0], &approach_lights[0], &tz_lights[0], &reil[0],
+        rwnum[1], &lat[1], &lon[1], &threshold[1], &overrun[1], &marking[1], &approach_lights[1], &tz_lights[1], &reil[1]
+    );
+
+    // calculate runway heading and length (used a lot)
+    geo_inverse_wgs_84( lat[0], lon[0], lat[1], lon[1], &heading, &az2, &length );
+
+    SG_LOG(SG_GENERAL, SG_DEBUG, "Read runway: (" << lon[0] << "," << lat[0] << ") to (" << lon[1] << "," << lat[1] << ") heading: " << heading << " length: " << length << " width: " << width );
+} 
+
+int Runway::BuildOsg ( osg::Group* airport )
+{
+    // calculated distance, and azimuths
+    double az1, az2, dist;
+
+    // rectangle verticies
+    double v0_lat, v0_lon, v1_lat, v1_lon, v2_lat, v2_lon, v3_lat, v3_lon;
+
+    // Create a node for the runway
+    osg::Geode* geode = new osg::Geode();
+
+    osg::Geometry* geometry = new osg::Geometry;
+    osg::Vec3dArray& v = *(new osg::Vec3dArray(4));
+    
+    // first, find the runway direction vector
+    // static int geo_inverse_wgs_84( double lat1, double lon1, double lat2, double lon2, double *az1, double *az2, double *s )
+    geo_inverse_wgs_84( lat[0], lon[0], lat[1], lon[1], &az1, &az2, &dist);
+
+    // now, need to caculate the 4 verticies
+    // static int geo_direct_wgs_84( double lat1, double lon1, double az1, double s, double *lat2, double *lon2, double *az2 )
+    geo_direct_wgs_84( lat[0], lon[0], az1+90, width/2, &v0_lat, &v0_lon, &az2 );
+    geo_direct_wgs_84( v0_lat, v0_lon, az1, dist, &v1_lat, &v1_lon, &az2 );
+    geo_direct_wgs_84( v1_lat, v1_lon, az1-90, width, &v2_lat, &v2_lon, &az2 );
+    geo_direct_wgs_84( v2_lat, v2_lon, az1+180, dist, &v3_lat, &v3_lon, &az2 );
+
+    // convert from lat/lon to geodisc
+    // (maybe later) - check some simgear objects...
+    v[0].set(v0_lat, v0_lon, 0.0f);
+    v[1].set(v1_lat, v1_lon, 0.0f);
+    v[2].set(v2_lat, v2_lon, 0.0f);
+    v[3].set(v3_lat, v3_lon, 0.0f);
+
+    geometry->setVertexArray(&v);
+
+    osg::DrawElementsUShort& drawElements = *(new osg::DrawElementsUShort(GL_TRIANGLE_STRIP,4));
+    geometry->addPrimitiveSet(&drawElements);
+    
+    drawElements[0] = 3;
+    drawElements[1] = 0;
+    drawElements[2] = 2;
+    drawElements[3] = 1;
+
+    geode->addDrawable(geometry);
+
+    airport->addChild( geode );
+
+    return 0;
+}
+
+TGPolygon Runway::gen_wgs84_area(   Point3D origin,
+                                    double length_m,
+                                    double displ1, double displ2,
+                                    double width_m,
+                                    double heading_deg,
+                                    double alt_m,
+                                    bool   add_mid )
 {
     TGPolygon result_list;
     double length_hdg = heading_deg;
@@ -111,53 +175,19 @@ TGPolygon gen_wgs84_area( Point3D origin,
 }
 
 
-// generate an area for a runway with expantion specified as a scale
-// factor (return result points in degrees)
-TGPolygon gen_runway_area_w_scale( const TGRunway& runway,
-                                   double alt_m,
-				   double length_scale,
-				   double width_scale ) {
-
-    TGPolygon result_list;
-    Point3D origin(runway.lon, runway.lat, 0);
-
-    result_list = gen_wgs84_area( origin,
-                                  runway.length*length_scale,
-                                  0.0, 0.0,
-                                  runway.width*width_scale,
-                                  runway.heading, alt_m, false );
-
-    // display points
-    SG_LOG(SG_GENERAL, SG_DEBUG, "Results w/ scale (new way)");
-    for ( int i = 0; i < result_list.contour_size( 0 ); ++i ) {
-        SG_LOG(SG_GENERAL, SG_DEBUG, "  " << result_list.get_pt(0, i));
-    }
-
-    return result_list;
-}
-
-
 // generate an area for a runway with expansion specified in meters
 // (return result points in degrees)
-TGPolygon gen_runway_area_w_extend( const TGRunway& runway,
-                                    double alt_m,
-				    double length_extend,
-                                    double displ1, double displ2,
-				    double width_extend ) {
-
+TGPolygon Runway::gen_runway_area_w_extend( double alt_m, double length_extend, double displ1, double displ2, double width_extend ) 
+{
     TGPolygon result_list;
-    Point3D origin(runway.lon, runway.lat, 0);
+    Point3D origin = GetMidpoint();
 
-    result_list
-        = gen_wgs84_area( origin,
-                          runway.length + 2.0*length_extend,
-                          displ1, displ2,
-                          runway.width + 2.0*width_extend,
-                          runway.heading, alt_m, false );
+    result_list = gen_wgs84_area( origin, length + 2.0*length_extend, displ1, displ2, width + 2.0*width_extend, heading, alt_m, false );
 
     // display points
     SG_LOG(SG_GENERAL, SG_DEBUG, "Results w/ extend (new way)");
-    for ( int i = 0; i < result_list.contour_size( 0 ); ++i ) {
+    for ( int i = 0; i < result_list.contour_size( 0 ); ++i ) 
+    {
         SG_LOG(SG_GENERAL, SG_DEBUG, "  " << result_list.get_pt(0, i));
     }
 
@@ -166,26 +196,253 @@ TGPolygon gen_runway_area_w_extend( const TGRunway& runway,
 
 
 // generate an area for a runway and include midpoints
-TGPolygon gen_runway_w_mid( const TGRunway& runway, 
-                            double alt_m,
-			    double length_extend_m,
-			    double width_extend_m ) {
+TGPolygon Runway::gen_runway_w_mid( double alt_m, double length_extend_m, double width_extend_m ) 
+{
     TGPolygon result_list;
-    Point3D origin(runway.lon, runway.lat, 0);
+    Point3D origin = GetMidpoint();
 
-    result_list = gen_wgs84_area( origin,
-                                  runway.length
-                                    + 2.0*length_extend_m,
-                                  0.0, 0.0,
-                                  runway.width
-                                    + 2.0 * width_extend_m,
-                                  runway.heading, alt_m, true );
+    result_list = gen_wgs84_area( origin, length + 2.0*length_extend_m, 0.0, 0.0, width + 2.0 * width_extend_m, heading, alt_m, true );
 
     // display points
     SG_LOG(SG_GENERAL, SG_DEBUG, "Results w/ mid (new way)");
-    for ( int i = 0; i < result_list.contour_size( 0 ); ++i ) {
+    for ( int i = 0; i < result_list.contour_size( 0 ); ++i ) 
+    {
         SG_LOG(SG_GENERAL, SG_DEBUG, "  " << result_list.get_pt(0, i));
     }
 
     return result_list;
 }
+
+#if 0
+void Runway::gen_simple_rwy( double alt_m, const string& material, superpoly_list *rwy_polys, texparams_list *texparams, TGPolygon *accum )
+{
+    Point3D mp, end[2];
+    int i;
+
+    // create the runway in two halves, as markings may be different in each direction
+    end[0] = GetStart();
+    mp = GetMidpoint();
+    end[1] = GetEnd();
+
+    for (i=0; i<2; i++)
+    {
+        gen_simple_half( alt_m, mp, end[i], material, rwy_polys, texparams, accum );
+    }
+}
+#endif
+
+void Runway::gen_simple_rwy( double alt_m, const string& material, superpoly_list *rwy_polys, texparams_list *texparams, TGPolygon *accum )
+{
+    int j, k;
+
+    TGPolygon runway = gen_runway_w_mid( alt_m, 0.0, 0.0 );
+
+    // runway half "a"
+    TGPolygon runway_a;
+    runway_a.erase();
+    runway_a.add_node( 0, runway.get_pt(0, 0) );
+    runway_a.add_node( 0, runway.get_pt(0, 1) );
+    runway_a.add_node( 0, runway.get_pt(0, 2) );
+    runway_a.add_node( 0, runway.get_pt(0, 5) );
+
+    // runway half "b"
+    TGPolygon runway_b;
+    runway_b.erase();
+    runway_b.add_node( 0, runway.get_pt(0, 5) );
+    runway_b.add_node( 0, runway.get_pt(0, 2) );
+    runway_b.add_node( 0, runway.get_pt(0, 3) );
+    runway_b.add_node( 0, runway.get_pt(0, 4) );
+	
+    Point3D p;
+    SG_LOG(SG_GENERAL, SG_DEBUG, "raw runway pts (a half)");
+    for ( j = 0; j < runway_a.contour_size( 0 ); ++j ) 
+    {
+    	p = runway_a.get_pt(0, j);
+    	SG_LOG(SG_GENERAL, SG_DEBUG, " point = " << p);
+    }
+    SG_LOG(SG_GENERAL, SG_DEBUG, "raw runway pts (b half)");
+    for ( j = 0; j < runway_b.contour_size( 0 ); ++j ) 
+    {
+    	p = runway_b.get_pt(0, j);
+    	SG_LOG(SG_GENERAL, SG_DEBUG, " point = " << p);
+    }
+	
+    TGSuperPoly sp;
+    TGTexParams tp;
+
+    TGPolygon clipped_a = tgPolygonDiff( runway_a, *accum );
+    TGPolygon split_a = tgPolygonSplitLongEdges( clipped_a, 400.0 );
+    sp.erase();
+    sp.set_poly( split_a );
+    sp.set_material( material );
+    rwy_polys->push_back( sp );
+    SG_LOG(SG_GENERAL, SG_DEBUG, "clipped_a = " << clipped_a.contours());
+    *accum = tgPolygonUnion( runway_a, *accum );
+    tp = TGTexParams( runway_a.get_pt(0,0), width, length/2.0, heading );
+    texparams->push_back( tp );
+
+    TGPolygon clipped_b = tgPolygonDiff( runway_b, *accum );
+    TGPolygon split_b = tgPolygonSplitLongEdges( clipped_b, 400.0 );
+    sp.erase();
+    sp.set_poly( split_b );
+    sp.set_material( material );
+    rwy_polys->push_back( sp );
+    SG_LOG(SG_GENERAL, SG_DEBUG, "clipped_b = " << clipped_b.contours());
+    *accum = tgPolygonUnion( runway_b, *accum );
+    tp = TGTexParams( runway_b.get_pt(0,2), width, length/2.0, heading+180.0 );
+    texparams->push_back( tp );
+
+    // print runway points
+    SG_LOG(SG_GENERAL, SG_DEBUG, "clipped runway pts (a)");
+    for ( j = 0; j < clipped_a.contours(); ++j ) 
+    {
+    	for ( k = 0; k < clipped_a.contour_size( j ); ++k ) 
+        {
+    	    p = clipped_a.get_pt(j, k);
+    	    SG_LOG(SG_GENERAL, SG_DEBUG, " point = " << p);
+    	}
+    }
+
+    // print runway points
+    SG_LOG(SG_GENERAL, SG_DEBUG, "clipped runway pts (b)");
+    for ( j = 0; j < clipped_b.contours(); ++j ) 
+    {
+    	for ( k = 0; k < clipped_b.contour_size( j ); ++k ) 
+        {
+    	    p = clipped_b.get_pt(j, k);
+    	    SG_LOG(SG_GENERAL, SG_DEBUG, " point = " << p);
+    	}
+    }
+
+#if 0    
+    gen_runway_stopway( rwy_info, runway_a, runway_b,
+    	    		material,
+    	    		rwy_polys, texparams, accum );
+#endif
+}
+
+#if 0
+void Runway::gen_marked_rwy( double alt_m, const string& material, superpoly_list *rwy_polys, texparams_list *texparams, TGPolygon *accum )
+{
+    Point3D mp, end[2];
+    int i;
+
+    // create the runway in two halves, as markings may be different in each direction
+    end[0] = GetStart();
+    mp = GetMidpoint();
+    end[1] = GetEnd();
+
+    for (i=0; i<2; i++)
+    {
+        // first, create half 'a'
+        switch( marking[i] )
+        {
+            case 0:
+            case 1: // Visual
+                SG_LOG( SG_GENERAL, SG_ALERT, "Half " << i << ": has Visual marking");
+                gen_visual_half( alt_m, mp, end[i], material, rwy_polys, texparams, accum );
+                break;
+
+            case 2: // non-precision
+                SG_LOG( SG_GENERAL, SG_ALERT, "Half " << i << ": has Non Precision marking");
+                gen_non_precision_half( alt_m, mp, end[i], material, rwy_polys, texparams, accum );
+                break;
+
+            case 3: // precision
+                SG_LOG( SG_GENERAL, SG_ALERT, "Half " << i << ": has Precision marking");
+                //gen_precision_half( alt_m, mp, end[i], material, rwy_polys, texparams, accum );
+                gen_simple_half( alt_m, mp, end[i], material, rwy_polys, texparams, accum );
+                break;
+
+            default: // unknown
+                SG_LOG( SG_GENERAL, SG_ALERT, "Half " << i << ": has unknown marking");
+                break;
+        }
+    }
+}
+#endif
+
+int Runway::BuildBtg( float alt_m, superpoly_list* rwy_polys, texparams_list* texparams, TGPolygon* accum, TGPolygon* apt_base, TGPolygon* apt_clearing )
+{
+    TGPolygon base, safe_base;
+    string material;
+
+    if ( surface == 1 /* Asphalt */ ) 
+    {
+        material = "pa_tiedown";
+    } 
+    else if ( surface == 2 /* Concrete */ ) 
+    {
+        material = "pc_tiedown";
+    } 
+    else if ( surface == 3 /* Turf/Grass */ ) 
+    {
+        material = "grass_rwy";
+    } 
+    else if ( surface == 4 /* Dirt */ || surface == 5 /* Gravel */ ) 
+    {
+        material = "dirt_rwy";
+    } 
+    else if ( surface == 12 /* Dry Lakebed */ ) 
+    {
+        material = "dirt_rwy";
+    } 
+    else if ( surface == 13 /* Water runway (buoy's?) */ ) 
+    {
+        // water
+    } 
+    else 
+    {
+        SG_LOG(SG_GENERAL, SG_WARN, "surface_code = " << surface);
+    	throw sg_exception("unknown runway type!");
+    }
+
+    // first, check the surface type - anything but concrete and asphalt are easy
+    switch( surface )
+    {
+        case 1: // asphalt:
+        case 2: // concrete
+            SG_LOG( SG_GENERAL, SG_ALERT, "Build Runway: asphalt or concrete" << surface);
+            gen_simple_rwy( alt_m, material, rwy_polys, texparams, accum );
+            break;
+    
+        case 3: // Grass
+        case 4: // Dirt
+        case 5: // Gravel
+            SG_LOG( SG_GENERAL, SG_ALERT, "Build Runway: Turf, Dirt or Gravel" << surface );
+	        gen_simple_rwy( alt_m, material, rwy_polys, texparams, accum );
+            break;
+
+        case 12: // dry lakebed
+            SG_LOG( SG_GENERAL, SG_ALERT, "Build Runway: Dry Lakebed");
+            break;
+
+        case 13: // water
+            SG_LOG( SG_GENERAL, SG_ALERT, "Build Runway: Water");
+            break;
+
+        case 14: // snow
+            SG_LOG( SG_GENERAL, SG_ALERT, "Build Runway: Snow");
+            break;
+
+        case 15: // transparent
+            SG_LOG( SG_GENERAL, SG_ALERT, "Build Runway: transparent");
+            break;
+
+        default: // unknown
+            SG_LOG( SG_GENERAL, SG_ALERT, "Build Runway: unknown" << surface);
+            break;
+    }    
+
+    // generate area around runways
+    base      = gen_runway_area_w_extend( 0.0, 20.0, -overrun[0], -overrun[1], 20.0 );
+
+    // also clear a safe area around the runway
+    safe_base = gen_runway_area_w_extend( 0.0, 180.0, -overrun[0], -overrun[1], 50.0 );
+
+    // add this to the airport clearing
+    *apt_clearing = tgPolygonUnion(safe_base, *apt_clearing);
+
+    // and add the clearing to the base
+    *apt_base = tgPolygonUnion( base, *apt_base );
+}
diff --git a/src/Airports/GenAirports850/runway.hxx b/src/Airports/GenAirports850/runway.hxx
index 34f6ab1d..fa335982 100644
--- a/src/Airports/GenAirports850/runway.hxx
+++ b/src/Airports/GenAirports850/runway.hxx
@@ -1,129 +1,84 @@
-// runway.hxx -- class to store runway info
-//
-// Written by Curtis Olson, started November 1999.
-//
-// Copyright (C) 1999  Curtis L. Olson  - http://www.flightgear.org/~curt
-//
-// 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.
-//
-// $Id: runway.hxx,v 1.16 2005-04-20 18:20:15 curt Exp $
-//
+#ifndef _RUNWAY_H_
+#define _RUNWAY_H_
 
-
-#ifndef _RUNWAY_HXX
-#define _RUNWAY_HXX
-
-
-#include <string>
-#include <vector>
-
-#include <Geometry/point3d.hxx>
+#include <stdio.h>
+#include <stdlib.h>
 
 #include <Polygon/polygon.hxx>
+#include <Polygon/superpoly.hxx>
+#include <Geometry/point3d.hxx>
 
+#include "texparams.hxx"
 
-struct TGRunway {
-    int type;
-    std::string rwy_no1;
-    std::string rwy_no2;
+#include <osg/Group>
 
-    double lon;
-    double lat;
-    double heading;
-    double length;
-    double width;
-    double disp_thresh1;
-    double disp_thresh2;
-    double stopway1;
-    double stopway2;
+using std::string;
 
-    int surface_code;
-    int shoulder_code;
-    double smoothness;
-    bool centre_lights;
-    int edge_lights;
-    int marking_code1;
-    int marking_code2;
-    int alc1_flag;
-    int alc2_flag;
-    bool has_tdz1;
-    bool has_tdz2;
-    int reil1;
-    int reil2;
+class Runway
+{
+public:
+    Runway(char* def);
 
-    bool   dist_remaining;
+    bool IsPrecision()
+    {
+        return true;
+    }
 
-    TGPolygon threshold;
-    TGPolygon tens, tens_margin, ones, ones_margin;
-    TGPolygon letter, letter_margin_left, letter_margin_right;
-    TGPolygon pre_td_zone;
-    TGPolygon td3_zone, td2_zone, td1a_zone, td1b_zone;
-    TGPolygon aim_point;
-    bool generated;
+    Point3D GetStart(void)
+    {
+        return ( Point3D( lon[0], lat[0], 0.0f ));
+    }
+
+    Point3D GetEnd(void)
+    {
+        return ( Point3D( lon[1], lat[1], 0.0f ));
+    }
+
+    Point3D GetMidpoint(void)
+    {
+        return ( Point3D( (lon[0]+lon[1])/2.0f, (lat[0]+lat[1])/2.0f, 0.0f) );
+    }
+
+    int BuildOsg( osg::Group* airport );
+    int BuildBtg( float alt_m, superpoly_list* rwy_polys, texparams_list* texparams, TGPolygon* accum, TGPolygon* apt_base, TGPolygon* apt_clearing );
+    
+private:
+    // data for whole runway
+    int     surface;
+    int     shoulder;
+    int     centerline_lights;
+    int     edge_lights;
+    int     dist_remain_signs;
+    
+    double  width;
+    double  length;
+    double  heading;
+    double  smoothness;
+
+    // data for each end
+    char    rwnum[2][16];
+    double  lat[2];
+    double  lon[2];
+    double  threshold[2];
+    double  overrun[2];
+
+    int     marking[2];
+    int     approach_lights[2];
+    int     tz_lights[2];
+    int     reil[2];
+
+    // Build Helpers
+    TGPolygon gen_wgs84_area( Point3D origin, double length_m, double displ1, double displ2, double width_m, double heading_deg, double alt_m, bool add_mid );
+    TGPolygon gen_runway_w_mid( double alt_m, double length_extend_m, double width_extend_m );
+//    void      gen_runway_section( const TGPolygon& runway, double startl_pct, double endl_pct, double startw_pct, double endw_pct, double minu, double maxu, double minv, double maxv, double heading,
+//                                  const string& prefix, const string& material, superpoly_list *rwy_polys, texparams_list *texparams, TGPolygon *accum  );
+//    void      gen_runway_stopway( const TGPolygon& runway_a, const TGPolygon& runway_b, const string& prefix, superpoly_list *rwy_polys, texparams_list *texparams, TGPolygon* accum );
+    TGPolygon gen_runway_area_w_extend( double alt_m, double length_extend, double displ1, double displ2, double width_extend );
+
+    void        gen_simple_rwy(         double alt_m, const string& material, superpoly_list *rwy_polys, texparams_list *texparams, TGPolygon *accum );
+    void        gen_marked_rwy(         double alt_m, const string& material, superpoly_list *rwy_polys, texparams_list *texparams, TGPolygon *accum );
 };
-typedef std::vector < TGRunway > runway_list;
-typedef runway_list::iterator runway_list_iterator;
-typedef runway_list::const_iterator const_runway_list_iterator;
 
-struct TGLightobj {
-    double lon;
-    double lat;
-    int type;
-    double heading;
-    double glideslope;
-    std::string rwy_name;
-};
-typedef std::vector < TGLightobj > light_list;
-typedef light_list::iterator light_list_iterator;
-typedef light_list::const_iterator const_light_list_iterator;
+typedef std::vector <Runway *> RunwayList;
 
-
-
-
-// given a runway center point, length, width, and heading, and
-// altitude (meters) generate the lon and lat 4 corners using wgs84
-// math.
-TGPolygon gen_wgs84_area( Point3D origin,
-                          double length_m,
-                          double displ1, double displ2,
-                          double width_m,
-                          double heading_deg,
-                          double alt_m,
-                          bool add_mid );
-
-// generate an area for a runway with expantion specified as a scale
-// factor (return result points in degrees)
-TGPolygon gen_runway_area_w_scale( const TGRunway& runway, 
-                                   double alt_m,
-				   double length_scale = 1.0,
-				   double width_scale = 1.0 );
-
-// generate an area for a runway with expansion specified in meters
-// (return result points in degrees)
-TGPolygon gen_runway_area_w_extend( const TGRunway& runway, 
-                                    double alt_m,
-				    double length_extend,
-                                    double displ1, double displ2,
-				    double width_extend );
-
-
-// generate an area for half a runway
-TGPolygon gen_runway_w_mid( const TGRunway& runway,
-                            double alt_m,
-			    double length_extend_m,
-			    double width_extend_m );
-
-
-#endif // _RUNWAY_HXX
+#endif