diff --git a/src/BuildTiles/Main/construct.cxx b/src/BuildTiles/Main/construct.cxx
index 16950fbe..037381d6 100644
--- a/src/BuildTiles/Main/construct.cxx
+++ b/src/BuildTiles/Main/construct.cxx
@@ -617,15 +617,15 @@ int TGConstruct::LoadLandclassPolys( void ) {
             SG_LOG(SG_GENERAL, SG_DEBUG, "directory not found: " << poly_path);
             continue;
         }
-    
+
         simgear::PathList files = d.children(simgear::Dir::TYPE_FILE);
         SG_LOG( SG_CLIPPER, SG_ALERT, files.size() << " Polys in " << d.path() );
-        
+
         BOOST_FOREACH(const SGPath& p, files) {
             if (p.file_base() != tile_str) {
                 continue;
             }
-        
+
             string lext = p.complete_lower_extension();
             if ((lext == "arr") || (lext == "arr.gz") || (lext == "btg.gz") ||
                 (lext == "fit") || (lext == "fit.gz") || (lext == "ind")) 
@@ -794,7 +794,7 @@ int TGConstruct::load_landcover()
         while ( x1 < max_lon ) {
             while ( y1 < max_lat ) {
                 make_area( cover, polys, x1, y1, x2, y2, half_cover_size, half_cover_size );
-                
+
                 y1 = y2;
                 y2 += cover_size;
             }
@@ -1702,9 +1702,50 @@ double TGConstruct::calc_tri_area( int_list& triangle_nodes ) {
     return triangle_area( p1, p2, p3 );
 }
 
+static SGVec3d calc_normal( double area, Point3D p1, Point3D p2, Point3D p3 ) {
+    SGVec3d v1, v2, normal;
+
+    // do some sanity checking.  With the introduction of landuse
+    // areas, we can get some long skinny triangles that blow up our
+    // "normal" calculations here.  Let's check for really small
+    // triangle areas and check if one dimension of the triangle
+    // coordinates is nearly coincident.  If so, assign the "default"
+    // normal of straight up.
+
+    bool degenerate = false;
+    const double area_eps = 1.0e-12;
+    if ( area < area_eps ) {
+        degenerate = true;
+    }
+
+    if ( fabs(p1.x() - p2.x()) < SG_EPSILON && fabs(p1.x() - p3.x()) < SG_EPSILON ) {
+        degenerate = true;
+    }
+    if ( fabs(p1.y() - p2.y()) < SG_EPSILON && fabs(p1.y() - p3.y()) < SG_EPSILON ) {
+        degenerate = true;
+    }
+    if ( fabs(p1.z() - p2.z()) < SG_EPSILON && fabs(p1.z() - p3.z()) < SG_EPSILON ) {
+        degenerate = true;
+    }
+
+    if ( degenerate ) {
+        normal = normalize(SGVec3d(p1.x(), p1.y(), p1.z()));
+    } else {
+        v1[0] = p2.x() - p1.x();
+        v1[1] = p2.y() - p1.y();
+        v1[2] = p2.z() - p1.z();
+        v2[0] = p3.x() - p1.x();
+        v2[1] = p3.y() - p1.y();
+        v2[2] = p3.z() - p1.z();
+        normal = normalize(cross(v1, v2));
+    }
+
+    return normal;
+}
+
 void TGConstruct::calc_normals( point_list& wgs84_nodes, TGSuperPoly& sp ) {
     // for each face in the superpoly, calculate a face normal
-    SGVec3d v1, v2, normal;
+    SGVec3d     normal;
     TGPolyNodes tri_nodes = sp.get_tri_idxs();
     int_list    face_nodes;
     double_list face_areas;
@@ -1723,41 +1764,8 @@ void TGConstruct::calc_normals( point_list& wgs84_nodes, TGSuperPoly& sp ) {
 
         area  = calc_tri_area( face_nodes );
 
-        // do some sanity checking.  With the introduction of landuse
-        // areas, we can get some long skinny triangles that blow up our
-        // "normal" calculations here.  Let's check for really small
-        // triangle areas and check if one dimension of the triangle
-        // coordinates is nearly coincident.  If so, assign the "default"
-        // normal of straight up.
+        normal = calc_normal( area, p1, p2, p3 );
 
-        bool degenerate = false;
-        const double area_eps = 1.0e-12;
-        if ( area < area_eps ) {
-            degenerate = true;
-        }
-
-        if ( fabs(p1.x() - p2.x()) < SG_EPSILON && fabs(p1.x() - p3.x()) < SG_EPSILON ) {
-            degenerate = true;
-        }
-        if ( fabs(p1.y() - p2.y()) < SG_EPSILON && fabs(p1.y() - p3.y()) < SG_EPSILON ) {
-            degenerate = true;
-        }
-        if ( fabs(p1.z() - p2.z()) < SG_EPSILON && fabs(p1.z() - p3.z()) < SG_EPSILON ) {
-            degenerate = true;
-        }
-
-        if ( degenerate ) {
-            normal = normalize(SGVec3d(p1.x(), p1.y(), p1.z()));
-        } else {
-        	v1[0] = p2.x() - p1.x();
-        	v1[1] = p2.y() - p1.y();
-        	v1[2] = p2.z() - p1.z();
-        	v2[0] = p3.x() - p1.x();
-        	v2[1] = p3.y() - p1.y();
-        	v2[2] = p3.z() - p1.z();
-        	normal = normalize(cross(v1, v2));
-        }
-        
         face_normals.push_back( Point3D::fromSGVec3( normal ) );
         face_areas.push_back( area );
     }
@@ -1787,13 +1795,16 @@ void TGConstruct::CalcPointNormals( void )
     SG_LOG(SG_GENERAL, SG_ALERT, "Calculating point normals: 0%");
 
     Point3D normal;
+    double  face_area;
+
     point_list wgs84_nodes = nodes.get_wgs84_nodes_as_Point3d();
     unsigned int ten_percent = nodes.size() / 10;
     unsigned int cur_percent = 10;
-    
+
     for ( unsigned int i = 0; i<nodes.size(); i++ ) {
         TGNode node       = nodes.get_node( i );
         TGFaceList faces  = node.GetFaces();
+        TGNeighborFaces* neighbor_faces = NULL;
         double total_area = 0.0;
 
         Point3D average( 0.0 );
@@ -1803,7 +1814,7 @@ void TGConstruct::CalcPointNormals( void )
             ten_percent += nodes.size() / 10;
             cur_percent += 10;
         }
-        
+
         // for each triangle that shares this node
         for ( unsigned int j = 0; j < faces.size(); ++j ) {
             unsigned int at      = faces[j].area;
@@ -1811,7 +1822,6 @@ void TGConstruct::CalcPointNormals( void )
             unsigned int segment = faces[j].seg;
             unsigned int tri     = faces[j].tri;
             int_list     face_nodes;
-            double       face_area;
 
             normal     = polys_clipped.get_face_normal( at, shape, segment, tri );
             face_nodes = polys_clipped.get_tri_idxs( at, shape, segment ).get_contour( tri ) ;
@@ -1821,6 +1831,22 @@ void TGConstruct::CalcPointNormals( void )
             total_area += face_area;
             average += normal;
         }
+
+        // if this node exists in the shared edge db, add the faces from the neighbooring tile
+        neighbor_faces = FindNeighborFaces( node.GetPosition() );
+        if ( neighbor_faces ) {
+            int num_faces = neighbor_faces->face_areas.size();
+            for ( int j = 0; j < num_faces; j++ ) {
+                normal    = neighbor_faces->face_normals[j];
+                face_area = neighbor_faces->face_areas[j];
+                SG_LOG(SG_GENERAL, SG_ALERT, "\tAdding face area " << face_area << " and normal " << normal << " to node " << node.GetPosition() );
+
+                normal *= face_area;
+                total_area += face_area;
+                average += normal;
+            }
+        }
+
         average /= total_area;
         nodes.SetNormal( i, average );
     }
@@ -1856,7 +1882,6 @@ void TGConstruct::TesselatePolys( void )
                     SG_LOG( SG_CLIPPER, SG_INFO, std::setprecision(12) << std::fixed << poly );
                 }
 
-//              TGPolygon tri = polygon_tesselate_alt_with_extra( poly, poly_extra, false );
                 TGPolygon tri = polygon_tesselate_alt_with_extra_cgal( poly, poly_extra, false );
 
                 // ensure all added nodes are accounted for
@@ -2094,6 +2119,30 @@ void TGConstruct::CleanClippedPolys() {
     }
 }
 
+void TGConstruct::AverageEdgeElevations( void )
+{
+    for ( unsigned int i = 0; i < neighbor_faces.size(); i++ ) {
+        TGNeighborFaces faces = neighbor_faces[i];
+        double elevation = 0.0;
+        unsigned int num_elevations = faces.elevations.size();
+
+        for ( unsigned int j = 0; j < num_elevations; j++ ) {
+            elevation += faces.elevations[j];
+        }
+
+        elevation = elevation / num_elevations;
+
+        /* Find this node, and update it's elevation */
+        int idx = nodes.find( faces.node );
+        TGNode node = nodes.get_node( idx );
+
+        if ( !node.GetFixedPosition() ) {
+            // set elevation as the average between all tiles that have it
+            nodes.SetElevation( idx, elevation );
+        }
+    }
+}
+
 void TGConstruct::CalcTextureCoordinates( void )
 {
     for ( unsigned int area = 0; area < TG_MAX_AREA_TYPES; area++ ) {
@@ -2176,9 +2225,260 @@ void TGConstruct::SaveSharedEdgeData( int stage )
             ofs_e.close();
         }
         break;
+
+        case 2:
+        {
+            // for stage 2, we need enough info on a node to average out elevation and
+            // generate a correct normal
+            // we will use the Point3D, as stage1 above, then a point list per node
+            // to store the neighboors.
+            // 
+            // NOTE: Some neighboors (likely 2) are on the shared border.
+            // Before calculating face normals for the node, all elevation data for
+            // neighboors needs to be completed.  So after all border nodes' elevations
+            // are updated, we'll need to traverse all of these point lists, and update
+            // any border nodes elevation as well
+            SaveSharedEdgeDataStage2();
+        }
+        break;
     }
 }
 
+void TGConstruct::WriteNeighborFaces( std::ofstream& ofs_e, Point3D pt )
+{
+    // find all neighboors of this point
+    int    n    = nodes.find( pt );
+    TGNode node = nodes.get_node( n );
+    TGFaceList faces  = node.GetFaces();
+
+    // write the number of neighboor faces
+    ofs_e << faces.size() << "\n";
+
+    // write out each face normal and size
+    for (unsigned int j=0; j<faces.size(); j++) {
+        // for each connected face, get the nodes
+        unsigned int at      = faces[j].area;
+        unsigned int shape   = faces[j].shape;
+        unsigned int segment = faces[j].seg;
+        unsigned int tri     = faces[j].tri;
+
+        int_list face_nodes = polys_clipped.get_tri_idxs( at, shape, segment ).get_contour( tri ) ;
+        {
+            Point3D p1 = nodes.get_node( face_nodes[0] ).GetPosition();
+            Point3D p2 = nodes.get_node( face_nodes[1] ).GetPosition();
+            Point3D p3 = nodes.get_node( face_nodes[2] ).GetPosition();
+
+            Point3D wgs_p1 = nodes.get_node( face_nodes[0] ).GetWgs84AsPoint3D();
+            Point3D wgs_p2 = nodes.get_node( face_nodes[1] ).GetWgs84AsPoint3D();
+            Point3D wgs_p3 = nodes.get_node( face_nodes[2] ).GetWgs84AsPoint3D();
+
+            double  face_area   = triangle_area( p1, p2, p3 );
+            Point3D face_normal = Point3D::fromSGVec3( calc_normal( face_area, wgs_p1, wgs_p2, wgs_p3 ) );
+
+            ofs_e << face_area << " ";
+            ofs_e << face_normal;
+        }
+    }
+    ofs_e << "\n";
+}
+
+TGNeighborFaces* TGConstruct::FindNeighborFaces( Point3D node )
+{
+    TGNeighborFaces* faces = NULL;
+
+    for (unsigned int i=0; i<neighbor_faces.size(); i++) {
+        if ( node == neighbor_faces[i].node ) {
+            faces = &neighbor_faces[i];
+            break;
+        }
+    }
+
+    return faces;
+}
+
+TGNeighborFaces* TGConstruct::AddNeighborFaces( Point3D node )
+{
+    TGNeighborFaces faces;
+    faces.node = node;
+
+    neighbor_faces.push_back( faces );
+
+    return &neighbor_faces[neighbor_faces.size()-1];
+}
+
+void TGConstruct::ReadNeighborFaces( std::ifstream& in )
+{
+    int count;
+
+    // read the count
+    in >> count;
+
+    for (int i=0; i<count; i++) {
+        TGNeighborFaces* pFaces;
+        Point3D          node;
+        int              num_faces;
+
+        in >> node;
+
+        // look to see if we already have this node
+        // If we do, (it's a corner) add more faces to it.
+        // otherwise, initialize it with our elevation data
+        pFaces = FindNeighborFaces( node );
+        if ( !pFaces ) {
+            pFaces = AddNeighborFaces( node );
+
+            // new face - let's add our elevation first
+            int idx = nodes.find( node );
+            if (idx >= 0) {
+                TGNode local = nodes.get_node( idx );
+                pFaces->elevations.push_back( local.GetPosition().z() );
+            }
+        }
+
+        // remember all of the elevation data for the node, so we can average
+        pFaces->elevations.push_back( node.z() );
+
+        in >> num_faces;
+        for (int j=0; j<num_faces; j++)
+        {
+            double  area;
+            Point3D normal;
+
+            in >> area;
+            pFaces->face_areas.push_back( area );
+
+            in >> normal;
+            pFaces->face_normals.push_back( normal );
+        }
+    }
+}
+
+void TGConstruct::SaveSharedEdgeDataStage2( void )
+{
+    string dir;
+    string file;
+    point_list north, south, east, west;
+    std::ofstream ofs_e;
+    int nCount;
+
+    nodes.get_geod_edge( bucket, north, south, east, west );
+
+    dir  = share_base + "/stage2/" + bucket.gen_base_path();
+
+    SGPath sgp( dir );
+    sgp.append( "dummy" );
+    sgp.create_dir( 0755 );
+
+
+    // north edge
+    file = dir + "/" + bucket.gen_index_str() + "_north_edge";
+    ofs_e.open( file.c_str() );
+    ofs_e << std::setprecision(12);
+    ofs_e << std::fixed;
+
+    nCount = north.size();
+    ofs_e << nCount << "\n";
+    for (int i=0; i<nCount; i++) {
+        // write the 3d point
+        ofs_e << north[i];
+        WriteNeighborFaces( ofs_e, north[i] );
+    }
+    ofs_e.close();
+
+    // south edge
+    file = dir + "/" + bucket.gen_index_str() + "_south_edge";
+    ofs_e.open( file.c_str() );
+    ofs_e << std::setprecision(12);
+    ofs_e << std::fixed;
+
+    nCount = south.size();
+    ofs_e << nCount << "\n";
+    for (int i=0; i<nCount; i++) {
+        ofs_e << south[i];
+        WriteNeighborFaces( ofs_e, south[i] );
+    }
+    ofs_e.close();
+    
+    // east edge
+    file = dir + "/" + bucket.gen_index_str() + "_east_edge";
+    ofs_e.open( file.c_str() );
+    ofs_e << std::setprecision(12);
+    ofs_e << std::fixed;
+
+    nCount = east.size();
+    ofs_e << nCount << "\n";
+    for (int i=0; i<nCount; i++) {
+        ofs_e << east[i];
+        WriteNeighborFaces( ofs_e, east[i] );
+    }
+    ofs_e.close();
+
+    // west egde
+    file = dir + "/" + bucket.gen_index_str() + "_west_edge";
+    ofs_e.open( file.c_str() );
+    ofs_e << std::setprecision(12);
+    ofs_e << std::fixed;
+
+    nCount = west.size();
+    ofs_e << nCount << "\n";
+    for (int i=0; i<nCount; i++) {
+        ofs_e << west[i];
+        WriteNeighborFaces( ofs_e, west[i] );
+    }
+    ofs_e.close();
+}
+
+void TGConstruct::LoadSharedEdgeDataStage2( void )
+{
+    string dir;
+    string file;
+    std::ifstream ifs_edge;
+    double     clon = bucket.get_center_lon();
+    double     clat = bucket.get_center_lat();
+    SGBucket   b;
+
+    // Read Northern tile and add its southern node faces
+    b    = sgBucketOffset(clon, clat, 0, 1);
+    dir  = share_base + "/stage2/" + b.gen_base_path();
+    file = dir + "/" + b.gen_index_str() + "_south_edge";
+    ifs_edge.open( file.c_str() );
+    if ( ifs_edge.is_open() ) {
+        ReadNeighborFaces( ifs_edge );
+    }
+    ifs_edge.close();
+
+    // Read Southern tile and add its northern node faces
+    b    = sgBucketOffset(clon, clat, 0, -1);
+    dir  = share_base + "/stage2/" + b.gen_base_path();
+    file = dir + "/" + b.gen_index_str() + "_north_edge";
+    ifs_edge.open( file.c_str() );
+    if ( ifs_edge.is_open() ) {
+        ReadNeighborFaces( ifs_edge );
+    }
+    ifs_edge.close();
+
+    // Read Eastern tile and add its western node faces
+    b    = sgBucketOffset(clon, clat, 1, 0);
+    dir  = share_base + "/stage2/" + b.gen_base_path();
+    file = dir + "/" + b.gen_index_str() + "_west_edge";
+    ifs_edge.open( file.c_str() );
+    if ( ifs_edge.is_open() ) {
+        ReadNeighborFaces( ifs_edge );
+    }
+    ifs_edge.close();
+
+    // Read Western tile and add its eastern node faces
+    b    = sgBucketOffset(clon, clat, -1, 0);
+    dir  = share_base + "/stage2/" + b.gen_base_path();
+    file = dir + "/" + b.gen_index_str() + "_east_edge";
+    ifs_edge.open( file.c_str() );
+    if ( ifs_edge.is_open() ) {
+        ReadNeighborFaces( ifs_edge );
+    }
+    ifs_edge.close();
+}
+
+
 void TGConstruct::SaveToIntermediateFiles( int stage )
 {
     string dir;
@@ -2473,6 +2773,10 @@ void TGConstruct::ConstructBucketStage2() {
     CalcElevations();
 
     // STEP 11)
+    // Generate face_connected list
+    LookupFacesPerNode();
+
+    // STEP 12)
     // Save the tile boundary info for stage 3
     // includes elevation info, and a list of connected triangles
     SaveSharedEdgeData( 2 );
@@ -2490,9 +2794,12 @@ void TGConstruct::ConstructBucketStage3() {
         strcpy( ds_name, "" );
     }
 
+    // Load in the neighbor faces and elevation data
+    LoadSharedEdgeDataStage2();
+
     // STEP 12)
-    // Generate face_connected list
-    LookupFacesPerNode();
+    // Average out the elevation for nodes on tile boundaries
+    AverageEdgeElevations();
 
     // STEP 12)
     // Calculate Face Normals
diff --git a/src/BuildTiles/Main/construct.hxx b/src/BuildTiles/Main/construct.hxx
index 39b80c91..d5240e63 100644
--- a/src/BuildTiles/Main/construct.hxx
+++ b/src/BuildTiles/Main/construct.hxx
@@ -351,14 +351,20 @@ inline std::ostream& operator<< ( std::ostream& out, const TGLandclass& lc )
     return out;
 }
 
+// Stage2 shared edge data
+struct TGNeighborFaces {
+public:
+    Point3D node;
 
+    double_list elevations;     // we'll take the average
+    double_list face_areas;
+    point_list  face_normals;
+};
+typedef std::vector < TGNeighborFaces > neighbor_face_list;
+typedef neighbor_face_list::iterator neighbor_face_list_iterator;
+typedef neighbor_face_list::const_iterator const_neighbor_face_list_iterator;
 
 
-
-
-// forward declaration
-class TGMatch;
-
 class TGConstruct {
 
 private:
@@ -413,8 +419,8 @@ private:
     // All Nodes
     TGNodes nodes;
 
-    // SHared Edges match data
-    // TGMatch match;
+    // Neighbor Faces
+    neighbor_face_list  neighbor_faces;
 
 private:
     // Load Data
@@ -432,14 +438,19 @@ private:
     void merge_slivers( TGLandclass& clipped, poly_list& slivers_list );
 
     // Shared edge Matching
-    void LoadSharedEdgeData( void );
-    void SaveSharedEdgeData( void );
-
+    void SaveSharedEdgeDataStage2( void );
+    void LoadSharedEdgeDataStage2( void );
+    void WriteSharedEdgeNeighboorFaces( std::ofstream& ofs_e, Point3D pt );
     void LoadSharedEdgeData( int stage );
     void LoadNeighboorEdgeDataStage1( SGBucket& b, point_list& north, point_list& south, point_list& east, point_list& west );
 
     void SaveSharedEdgeData( int stage );
 
+    void ReadNeighborFaces( std::ifstream& ifs_e );
+    void WriteNeighborFaces( std::ofstream& ofs_e, Point3D pt );
+    TGNeighborFaces* AddNeighborFaces( Point3D node );
+    TGNeighborFaces* FindNeighborFaces( Point3D node );
+
     // Polygon Cleaning
     void CleanClippedPolys( void );
     void FixTJunctions( void );
@@ -449,6 +460,7 @@ private:
 
     // Elevation and Flattening
     void CalcElevations( void );
+    void AverageEdgeElevations( void );
 
     // Normals and texture coords
     void LookupNodesPerVertex( void );