diff --git a/src/Airports/GenAirports850/airport.cxx b/src/Airports/GenAirports850/airport.cxx index f0411849..03d1541c 100644 --- a/src/Airports/GenAirports850/airport.cxx +++ b/src/Airports/GenAirports850/airport.cxx @@ -1,1248 +1,1221 @@ -#include <list> -#include <ctime> - -#include <stdio.h> - -#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 <terragear/tg_polygon.hxx> -#include <terragear/tg_chopper.hxx> -#include <terragear/tg_unique_geod.hxx> -#include <terragear/tg_unique_vec3f.hxx> -#include <terragear/tg_unique_vec2f.hxx> - -#include "airport.hxx" -#include "beznode.hxx" -#include "debug.hxx" -#include "elevations.hxx" -#include "global.hxx" -#include "helipad.hxx" -#include "runway.hxx" -#include "output.hxx" - - -Airport::Airport( int c, char* def) -{ - int numParams; - char* tok; - int ct = 0; - - code = c; - - // we need to tokenize airports, since we can't scanf two strings next to each other... - numParams = 0; - bool done = false; - - while (!done) - { - // trim leading whitespace - while(isspace(*def)) def++; - - tok = strtok(def, " \t\r\n"); - - if (tok) - { - def += strlen(tok)+1; - - switch( numParams ) - { - case 0: - altitude = atoi(tok); - break; - - case 1: - ct = atoi(tok); - break; - - case 2: - // deprecated - ignore - break; - - case 3: - icao = tok; - description = def; - done = true; - break; - } - } - numParams++; - } - - altitude *= SG_FEET_TO_METER; - - TG_LOG( SG_GENERAL, SG_DEBUG, "Read airport with icao " << icao << ", control tower " << ct << ", and description " << description ); -} - -Airport::~Airport() -{ - for (unsigned int i=0; i<features.size(); i++) - { - delete features[i]; - } - - for (unsigned int i=0; i<helipads.size(); i++) - { - delete helipads[i]; - } - - for (unsigned int i=0; i<runways.size(); i++) - { - delete runways[i]; - } - - for (unsigned int i=0; i<waterrunways.size(); i++) - { - delete waterrunways[i]; - } - - for (unsigned int i=0; i<pavements.size(); i++) - { - delete pavements[i]; - } - - for (unsigned int i=0; i<taxiways.size(); i++) - { - delete taxiways[i]; - } - - for (unsigned int i=0; i<lightobjects.size(); i++) - { - delete lightobjects[i]; - } - - for (unsigned int i=0; i<windsocks.size(); i++) - { - delete windsocks[i]; - } - - for (unsigned int i=0; i<beacons.size(); i++) - { - delete beacons[i]; - } - - for (unsigned int i=0; i<signs.size(); i++) - { - delete signs[i]; - } - - for (unsigned int i=0; i<boundary.size(); i++) - { - delete boundary[i]; - } -} - -bool Airport::isDebugRunway( int rwy ) -{ - bool dbg = false; - - debug_map_const_iterator it = debug_runways.find(icao); - if ( it != debug_runways.end() ) { - for ( unsigned int i=0; i<it->second.size() && !dbg; i++ ) { - if( it->second[i] == std::numeric_limits<int>::max() ) { - dbg = true; - } else if ( it->second[i] == rwy+1 ) { - dbg = true; - } - } - } - - return dbg; -} - -bool Airport::isDebugPavement( int pvmt ) -{ - bool dbg = false; - - debug_map_const_iterator it = debug_pavements.find(icao); - if ( it != debug_pavements.end() ) { - for ( unsigned int i=0; i<it->second.size() && !dbg; i++ ) { - if( it->second[i] == std::numeric_limits<int>::max() ) { - dbg = true; - } else if ( it->second[i] == pvmt+1 ) { - dbg = true; - } - } - } - - return dbg; -} - -bool Airport::isDebugTaxiway( int taxi ) -{ - bool dbg = false; - - debug_map_const_iterator it = debug_taxiways.find(icao); - if ( it != debug_taxiways.end() ) { - for ( unsigned int i=0; i<it->second.size() && !dbg; i++ ) { - if( it->second[i] == std::numeric_limits<int>::max() ) { - dbg = true; - } else if ( it->second[i] == taxi+1 ) { - dbg = true; - } - } - } - - return dbg; -} - -bool Airport::isDebugFeature( int feat ) -{ - bool dbg = false; - - debug_map_const_iterator it = debug_features.find(icao); - if ( it != debug_features.end() ) { - for ( unsigned int i=0; i<it->second.size() && !dbg; i++ ) { - if( it->second[i] == std::numeric_limits<int>::max() ) { - dbg = true; - } else if ( it->second[i] == feat+1 ) { - dbg = true; - } - } - } - - return dbg; -} - - -// TODO : Add somewhere -// Determine node elevations of a point_list based on the provided -// TGAptSurface. Offset is added to the final elevation -static std::vector<SGGeod> calc_elevations( const tgSurface& surf, const std::vector<SGGeod>& geod_nodes, double offset ) -{ - std::vector<SGGeod> result = geod_nodes; - for ( unsigned int i = 0; i < result.size(); ++i ) { - double elev = surf.query( result[i] ); - result[i].setElevationM( elev + offset ); - } - - return result; -} - - -static tgContour calc_elevations( const tgSurface& surf, const tgContour& geod_nodes, double offset ) -{ - tgContour result = geod_nodes; - for ( unsigned int i = 0; i < result.GetSize(); ++i ) { - SGGeod node = result.GetNode(i); - double elev = surf.query( node ); - node.setElevationM( elev + offset ); - result.SetNode( i, node ); - } - - return result; -} - -static double calc_elevation( const tgSurface& surf, const SGGeod& node, double offset ) -{ - double elev = surf.query( node ); - elev += offset; - - return elev; -} - - -// 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( const tgSurface& surf, const tgPolygon& poly, double offset ) -{ - tgPolygon result; - - for ( unsigned int i = 0; i < poly.Contours(); ++i ) { - tgContour contour = poly.GetContour( i ); - tgContour elevated = calc_elevations( surf, contour, offset ); - - result.AddContour( elevated ); - } - - return result; -} - -void Airport::BuildBtg(const std::string& root, const string_list& elev_src ) -{ - tgcontour_list slivers; - tgcontour_list line_slivers; - - tgpolygon_list apt_base_polys; - tgpolygon_list apt_clearing_polys; - - // runways - tgpolygon_list rwy_polys; - - // pavements - tgpolygon_list pvmt_polys; - - // linear features - tgpolygon_list line_polys; - - // runway lights - tglightcontour_list rwy_lights; - - bool make_shapefiles = false; - - // parse main airport information - double apt_lon = 0.0, apt_lat = 0.0; - - SGTimeStamp build_start; - SGTimeStamp build_end; - SGTimeStamp cleanup_start; - SGTimeStamp cleanup_end; - SGTimeStamp triangulation_start; - SGTimeStamp triangulation_end; - time_t log_time; - - char shapefile_name[64]; - std::string shapefile; - - // Find the average of all the runway and heliport long / lats - int num_samples = 0; - for (unsigned int i=0; i<runways.size(); i++) - { - apt_lon += runways[i]->GetMidpoint().getLongitudeDeg(); - apt_lat += runways[i]->GetMidpoint().getLatitudeDeg(); - num_samples++; - } - for (unsigned int i=0; i<helipads.size(); i++) - { - apt_lon += helipads[i]->GetLoc().getLongitudeDeg(); - apt_lat += helipads[i]->GetLoc().getLatitudeDeg(); - num_samples++; - } - apt_lon = apt_lon / (double)num_samples; - apt_lat = apt_lat / (double)num_samples; - - SGBucket b( SGGeod::fromDeg(apt_lon, apt_lat) ); - TG_LOG(SG_GENERAL, SG_DEBUG, b.gen_base_path() << "/" << b.gen_index_str()); - - // If we are cutting in the linear features, add them first - if (pavements.size()) - { - for ( unsigned int i=0; i<pavements.size(); i++ ) - { - AddFeatures( pavements[i]->GetFeatures() ); - } - } - - TG_LOG(SG_GENERAL, SG_INFO, "Parse Complete - Runways: " << runways.size() << " Pavements: " << pavements.size() << " Features: " << features.size() << " Taxiways: " << taxiways.size() ); - - // Starting to clip the polys (for now - only UNIX builds) - build_start.stamp(); - - // Add the linear features - if (features.size()) - { - tgAccumulator lf_accum; - - TG_LOG(SG_GENERAL, SG_INFO, "Build " << features.size() << " Linear Feature Polys"); - for ( unsigned int i=0; i<features.size(); i++ ) - { - TG_LOG(SG_GENERAL, SG_DEBUG, "Build Feature Poly " << i + 1 << " of " << features.size() << " : " << features[i]->GetDescription() ); - - features[i]->BuildBtg( line_polys, rwy_lights, lf_accum, make_shapefiles ); - } - -// lf_accum.ToShapefiles( "./lf_accum", "test", false ); - - log_time = time(0); - TG_LOG( SG_GENERAL, SG_ALERT, "Finished building Linear Features for " << icao << " at " << DebugTimeToString(log_time) ); - } - - /* Initialize a new accumulator for the other objects */ - tgAccumulator pvmt_accum; - - // Build runways next - if (runways.size()) - { - TG_LOG(SG_GENERAL, SG_INFO, "Build " << runways.size() << " Runway Polys"); - for ( unsigned int i=0; i<runways.size(); i++ ) - { - TG_LOG(SG_GENERAL, SG_DEBUG, "Build Runway " << i + 1 << " of " << runways.size()); - slivers.clear(); - - if ( isDebugRunway(i) ) { - sprintf( shapefile_name, "runway_%d", i ); - } else { - strcpy( shapefile_name, "" ); - } - shapefile = shapefile_name; - - if (boundary.size()) - { - runways[i]->BuildBtg( rwy_polys, rwy_lights, slivers, pvmt_accum, shapefile ); - } - else - { - runways[i]->BuildBtg( rwy_polys, rwy_lights, slivers, apt_base_polys, apt_clearing_polys, pvmt_accum, shapefile ); - } - - // Now try to merge any slivers we found - tgPolygon::MergeSlivers( rwy_polys, slivers ); - } - - log_time = time(0); - TG_LOG( SG_GENERAL, SG_ALERT, "Finished building runways for " << icao << " at " << DebugTimeToString(log_time) ); - } - - if (lightobjects.size()) - { - TG_LOG(SG_GENERAL, SG_INFO, "Build " << lightobjects.size() << " Light Objects "); - for ( unsigned int i=0; i<lightobjects.size(); i++ ) - { - TG_LOG(SG_GENERAL, SG_DEBUG, "Build runway light " << i + 1 << " of " << lightobjects.size()); - lightobjects[i]->BuildBtg( rwy_lights ); - } - } - - // Build helipads (use runway poly- and texture list for this) - if (helipads.size()) - { - TG_LOG(SG_GENERAL, SG_INFO, "Build " << helipads.size() << " Helipad Polys "); - for ( unsigned int i=0; i<helipads.size(); i++ ) - { - TG_LOG(SG_GENERAL, SG_DEBUG, "Build helipad " << i + 1 << " of " << helipads.size()); - slivers.clear(); - - if (boundary.size()) - { - helipads[i]->BuildBtg( rwy_polys, rwy_lights, slivers, pvmt_accum ); - } - else - { - helipads[i]->BuildBtg( rwy_polys, rwy_lights, slivers, apt_base_polys, apt_clearing_polys, pvmt_accum ); - } - - // Now try to merge any slivers we found - tgPolygon::MergeSlivers( rwy_polys, slivers ); - } - } - - // Build the pavements - if (pavements.size()) - { - TG_LOG(SG_GENERAL, SG_INFO, "Build " << pavements.size() << " Pavement Polys "); - for ( unsigned int i=0; i<pavements.size(); i++ ) - { - TG_LOG(SG_GENERAL, SG_DEBUG, "Build Pavement " << i + 1 << " of " << pavements.size() << " : " << pavements[i]->GetDescription()); - slivers.clear(); - - if ( isDebugPavement(i) ) { - sprintf( shapefile_name, "pvmnt_%d", i ); - } else { - strcpy( shapefile_name, "" ); - } - shapefile = shapefile_name; - - if (boundary.size()) - { - pavements[i]->BuildBtg( pvmt_polys, slivers, pvmt_accum, shapefile ); - } - else - { - pavements[i]->BuildBtg( pvmt_polys, slivers, apt_base_polys, apt_clearing_polys, pvmt_accum, shapefile ); - } - - // Now try to merge any slivers we found - tgPolygon::MergeSlivers( rwy_polys, slivers ); - tgPolygon::MergeSlivers( pvmt_polys, slivers ); - } - - log_time = time(0); - TG_LOG( SG_GENERAL, SG_ALERT, "Finished building Pavements for " << icao << " at " << DebugTimeToString(log_time) ); - } - - // Build the legacy taxiways - if (taxiways.size()) - { - TG_LOG(SG_GENERAL, SG_INFO, "Build " << taxiways.size() << " Taxiway Polys "); - for ( unsigned int i=0; i<taxiways.size(); i++ ) - { - TG_LOG(SG_GENERAL, SG_DEBUG, "Build Taxiway " << i + 1 << " of " << taxiways.size()); - slivers.clear(); - - if ( isDebugTaxiway(i) ) { - sprintf( shapefile_name, "taxiway_%d", i ); - } else { - strcpy( shapefile_name, "" ); - } - shapefile = shapefile_name; - - if (boundary.size()) - { - taxiways[i]->BuildBtg( pvmt_polys, rwy_lights, slivers, pvmt_accum, shapefile ); - } - else - { - taxiways[i]->BuildBtg( pvmt_polys, rwy_lights, slivers, apt_base_polys, apt_clearing_polys, pvmt_accum, shapefile ); - } - - // Now try to merge any slivers we found - tgPolygon::MergeSlivers( rwy_polys, slivers ); - tgPolygon::MergeSlivers( pvmt_polys, slivers ); - } - } - - // Build runway shoulders here - if ( runways.size() ) - { - TG_LOG(SG_GENERAL, SG_INFO, "Build " << runways.size() << " Runway Shoulder Polys "); - for ( unsigned int i=0; i<runways.size(); i++ ) - { - TG_LOG(SG_GENERAL, SG_DEBUG, "Build Runway shoulder " << i + 1 << " of " << runways.size()); - - if ( runways[i]->GetsShoulder() ) - { - slivers.clear(); - runways[i]->BuildShoulder( rwy_polys, slivers, pvmt_accum ); - - // Now try to merge any slivers we found - tgPolygon::MergeSlivers( rwy_polys, slivers ); - tgPolygon::MergeSlivers( pvmt_polys, slivers ); - } - } - } - - // Build helipad shoulders here - if ( helipads.size() ) - { - TG_LOG(SG_GENERAL, SG_INFO, "Build " << runways.size() << " Helipad Shoulder Polys "); - for ( unsigned int i=0; i<helipads.size(); i++ ) - { - TG_LOG(SG_GENERAL, SG_DEBUG, "Build Helipad shoulder " << i + 1 << " of " << helipads.size()); - - if ( helipads[i]->GetsShoulder() ) - { - slivers.clear(); - helipads[i]->BuildShoulder( rwy_polys, slivers, pvmt_accum ); - - // Now try to merge any slivers we found - tgPolygon::MergeSlivers( rwy_polys, slivers ); - tgPolygon::MergeSlivers( pvmt_polys, slivers ); - } - } - } - - // build the base and clearing if there's a boundary - tgPolygon apt_base, apt_clearing; - if (boundary.size()) - { - TG_LOG(SG_GENERAL, SG_INFO, "Build " << boundary.size() << " Boundary Polys "); - shapefile = ""; - - for ( unsigned int i=0; i<boundary.size(); i++ ) - { - TG_LOG(SG_GENERAL, SG_DEBUG, "Build Userdefined boundary " << i + 1 << " of " << boundary.size()); - boundary[i]->BuildBtg( apt_base, apt_clearing, shapefile ); - } - } else { - apt_base = tgPolygon::Union( apt_base_polys ); - apt_clearing = tgPolygon::Union( apt_clearing_polys ); - } - - if ( apt_base.TotalNodes() == 0 ) - { - TG_LOG(SG_GENERAL, SG_ALERT, "no airport points generated"); - return; - } - - tgPolygon filled_base = tgPolygon::StripHoles( apt_base ); - tgPolygon divided_base = tgPolygon::SplitLongEdges( filled_base, 200.0 ); - tgPolygon base_poly = pvmt_accum.Diff( divided_base ); - - build_end.stamp(); - build_time = build_end - build_start; - - cleanup_start.stamp(); - - // add segments to polygons to remove any possible "T" - // intersections - UniqueSGGeodSet tmp_pvmt_nodes; - UniqueSGGeodSet tmp_feat_nodes; - - // build temporary node list from runways... - TG_LOG(SG_GENERAL, SG_INFO, "Build Node List " ); - for ( unsigned int k = 0; k < rwy_polys.size(); ++k ) - { - tgPolygon poly = rwy_polys[k]; - for ( unsigned int i = 0; i < poly.Contours(); ++i ) - { - for ( unsigned int j = 0; j < poly.ContourSize( i ); ++j ) - { - tmp_pvmt_nodes.add( poly.GetNode(i, j) ); - } - } - } - - // and pavements - for ( unsigned int k = 0; k < pvmt_polys.size(); ++k ) - { - tgPolygon poly = pvmt_polys[k]; - for ( unsigned int i = 0; i < poly.Contours(); ++i ) - { - for ( unsigned int j = 0; j < poly.ContourSize( i ); ++j ) - { - tmp_pvmt_nodes.add( poly.GetNode(i, j) ); - } - } - } - - // and linear features ( keep Linear feature nodes seperate) - for ( unsigned int k = 0; k < line_polys.size(); ++k ) - { - tgPolygon poly = line_polys[k]; - for ( unsigned int i = 0; i < poly.Contours(); ++i ) - { - for ( unsigned int j = 0; j < poly.ContourSize( i ); ++j ) - { - tmp_feat_nodes.add( poly.GetNode(i, j) ); - } - } - } - - // and the base - for ( unsigned int i = 0; i < base_poly.Contours(); ++i ) - { - for ( unsigned int j = 0; j < base_poly.ContourSize( i ); ++j ) - { - tmp_pvmt_nodes.add( base_poly.GetNode(i, j) ); - } - } - - // the divided base could contain points not found in base_poly, - // so we should add them because the skirt needs them. - for ( unsigned int i = 0; i < divided_base.Contours(); ++i ) - { - for ( unsigned int j = 0; j < divided_base.ContourSize( i ); ++j ) - { - tmp_pvmt_nodes.add( divided_base.GetNode(i, j) ); - } - } - - log_time = time(0); - TG_LOG( SG_GENERAL, SG_ALERT, "Finished collecting nodes for " << icao << " at " << DebugTimeToString(log_time) ); - - // second pass : runways - for ( unsigned int k = 0; k < rwy_polys.size(); ++k ) - { - tgPolygon poly = rwy_polys[k]; - poly = tgPolygon::AddColinearNodes( poly, tmp_pvmt_nodes ); - TG_LOG(SG_GENERAL, SG_DEBUG, "total size after add nodes = " << poly.TotalNodes()); - rwy_polys[k] = poly; - } - - // second pass : and pavements - for ( unsigned int k = 0; k < pvmt_polys.size(); ++k ) - { - tgPolygon poly = pvmt_polys[k]; - poly = tgPolygon::AddColinearNodes( poly, tmp_pvmt_nodes ); - TG_LOG(SG_GENERAL, SG_DEBUG, "total size after add nodes = " << poly.TotalNodes()); - pvmt_polys[k] = poly; - } - - // second pass : and lines - for ( unsigned int k = 0; k < line_polys.size(); ++k ) - { - tgPolygon poly = line_polys[k]; - poly = tgPolygon::AddColinearNodes( poly, tmp_feat_nodes ); - TG_LOG(SG_GENERAL, SG_DEBUG, "total size after add nodes = " << poly.TotalNodes()); - line_polys[k] = poly; - } - - log_time = time(0); - TG_LOG( SG_GENERAL, SG_ALERT, "Finished adding intermediate nodes for " << icao << " at " << DebugTimeToString(log_time) ); - - for ( unsigned int k = 0; k < line_polys.size(); ++k ) - { - tgPolygon poly = line_polys[k]; - -#if 1 - poly = tgPolygon::RemoveCycles( poly ); - poly = tgPolygon::RemoveDups( poly ); - poly = tgPolygon::RemoveBadContours( poly ); - -// poly = tgPolygon::Simplify( poly ); -// poly = tgPolygon::RemoveTinyContours( poly ); -// poly = tgPolygon::RemoveSpikes( poly ); -// poly = tgPolygon::RemoveDups( poly ); -// poly = tgPolygon::RemoveBadContours( poly ); -// poly = tgPolygon::RemoveTinyContours( poly ); -#endif - - line_polys[k] = poly; - } - - log_time = time(0); - TG_LOG( SG_GENERAL, SG_ALERT, "Finished cleaning polys for " << icao << " at " << DebugTimeToString(log_time) ); - - base_poly = tgPolygon::AddColinearNodes( base_poly, tmp_pvmt_nodes ); - base_poly = tgPolygon::Snap( base_poly, gSnap ); - - // Finally find slivers in base - slivers.clear(); - tgPolygon::RemoveSlivers( base_poly, slivers ); - tgPolygon::MergeSlivers( rwy_polys, slivers ); - tgPolygon::MergeSlivers( pvmt_polys, slivers ); - - // Then snap rwy and pavement to grid (was done right after adding intermediate nodes...) - for ( unsigned int k = 0; k < rwy_polys.size(); ++k ) - { - tgPolygon poly = rwy_polys[k]; - poly = tgPolygon::Snap(poly, gSnap); - poly = tgPolygon::RemoveDups( poly ); - poly = tgPolygon::RemoveBadContours( poly ); - rwy_polys[k] = poly; - } - for ( unsigned int k = 0; k < pvmt_polys.size(); ++k ) - { - tgPolygon poly = pvmt_polys[k]; - poly = tgPolygon::Snap(poly, gSnap); - poly = tgPolygon::RemoveDups( poly ); - poly = tgPolygon::RemoveBadContours( poly ); - pvmt_polys[k] = poly; - } - - cleanup_end.stamp(); - cleanup_time = cleanup_end - cleanup_start; - - triangulation_start.stamp(); - - // tesselate the polygons and prepair them for final output - if ( rwy_polys.size() ) - { - TG_LOG(SG_GENERAL, SG_INFO, "Tesselating " << rwy_polys.size() << " Runway Polys " ); - for ( unsigned int i = 0; i < rwy_polys.size(); ++i ) - { - TG_LOG(SG_GENERAL, SG_DEBUG, "Tesselating runway poly = " << i + 1 << " of " << rwy_polys.size() ); - - TG_LOG(SG_GENERAL, SG_DEBUG, "contours before " << rwy_polys[i].Contours() << " total points before = " << rwy_polys[i].TotalNodes()); - rwy_polys[i].Tesselate(); - TG_LOG(SG_GENERAL, SG_DEBUG, "triangles after = " << rwy_polys[i].Triangles()); - rwy_polys[i].Texture(); - } - } - - if ( pvmt_polys.size() ) - { - // tesselate the polygons and prepair them for final output - TG_LOG(SG_GENERAL, SG_INFO, "Tesselating " << pvmt_polys.size() << " Pavement Polys " ); - for ( unsigned int i = 0; i < pvmt_polys.size(); ++i ) - { - TG_LOG(SG_GENERAL, SG_DEBUG, "Tesselating pavement poly = " << i + 1 << " of " << pvmt_polys.size() ); - - TG_LOG(SG_GENERAL, SG_DEBUG, "contours before " << pvmt_polys[i].Contours() << " total points before = " << pvmt_polys[i].TotalNodes()); - pvmt_polys[i].Tesselate(); - TG_LOG(SG_GENERAL, SG_DEBUG, "triangles after = " << pvmt_polys[i].Triangles()); - pvmt_polys[i].Texture(); - } - } - - if ( line_polys.size() ) - { - // tesselate the polygons and prepair them for final output - TG_LOG(SG_GENERAL, SG_INFO, "Tesselating " << line_polys.size() << " Linear Feature Polys " ); - for ( unsigned int i = 0; i < line_polys.size(); ++i ) - { - TG_LOG(SG_GENERAL, SG_DEBUG, "Tesselating line poly = " << i + 1 << " of " << line_polys.size() ); - - TG_LOG(SG_GENERAL, SG_DEBUG, "contours before " << line_polys[i].Contours() << " total points before = " << line_polys[i].TotalNodes()); - line_polys[i].Tesselate(); - TG_LOG(SG_GENERAL, SG_DEBUG, "triangles after = " << line_polys[i].Triangles()); - line_polys[i].Texture(); - } - } - - /* before tessellating the base, make sure there are no - intersecting contours */ - base_poly = tgPolygon::Simplify( base_poly ); - - TG_LOG(SG_GENERAL, SG_INFO, "Tesselating base poly : " << base_poly.Contours() << " contours " ); - base_poly.Tesselate(); - TG_LOG(SG_GENERAL, SG_INFO, "Tesselating base poly - done : Triangles = " << base_poly.Triangles()); - // should we texture base here? - - triangulation_end.stamp(); - - // a few airports fail here - if ( base_poly.Triangles() == 0 ) - { - TG_LOG(SG_GENERAL, SG_ALERT, "no base poly triangles"); - return; - } - - triangulation_time = triangulation_end - triangulation_start; - - // - // 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 - // start with just nodes - UniqueSGGeodSet nodes; - UniqueSGVec3fSet normals; - UniqueSGVec2fSet texcoords; - - 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 - SGVec3f vnt = SGVec3f::fromGeod( base_poly.GetNode(0, 0) ); - vnt = normalize(vnt); - - TG_LOG(SG_GENERAL, SG_INFO, "Adding runway nodes and normals"); - for ( unsigned int k = 0; k < rwy_polys.size(); ++k ) - { - TG_LOG(SG_GENERAL, SG_DEBUG, "tri " << k); - std::string material = rwy_polys[k].GetMaterial(); - TG_LOG(SG_GENERAL, SG_DEBUG, "material = " << material); - TG_LOG(SG_GENERAL, SG_DEBUG, "triangles = " << rwy_polys[k].Triangles()); - for ( unsigned int i = 0; i < rwy_polys[k].Triangles(); ++i ) - { - tri_v.clear(); - tri_n.clear(); - tri_tc.clear(); - // let's go out on a limb, and assume triangles have 3 points.. - for ( int j = 0; j < 3; ++j ) - { - index = nodes.add( rwy_polys[k].GetTriNode( i, j ) ); - tri_v.push_back( index ); - - // use 'the' normal - index = normals.add( vnt ); - tri_n.push_back( index ); - - index = texcoords.add( rwy_polys[k].GetTriTexCoord(i,j) ); - 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 ); - } - } - - TG_LOG(SG_GENERAL, SG_INFO, "Adding pavement nodes and normals"); - for ( unsigned int k = 0; k < pvmt_polys.size(); ++k ) - { - TG_LOG(SG_GENERAL, SG_DEBUG, "tri " << k); - std::string material = pvmt_polys[k].GetMaterial(); - TG_LOG(SG_GENERAL, SG_DEBUG, "material = " << material); - TG_LOG(SG_GENERAL, SG_DEBUG, "triangles = " << pvmt_polys[k].Triangles()); - for ( unsigned int i = 0; i < pvmt_polys[k].Triangles(); ++i ) - { - tri_v.clear(); - tri_n.clear(); - tri_tc.clear(); - for ( int j = 0; j < 3; ++j ) - { - index = nodes.add( pvmt_polys[k].GetTriNode( i, j ) ); - tri_v.push_back( index ); - - // use 'the' normal - index = normals.add( vnt ); - tri_n.push_back( index ); - - index = texcoords.add( pvmt_polys[k].GetTriTexCoord(i,j) ); - 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 ); - } - } - - TG_LOG(SG_GENERAL, SG_INFO, "Adding line nodes and normals"); - for ( unsigned int k = 0; k < line_polys.size(); ++k ) - { - TG_LOG(SG_GENERAL, SG_DEBUG, "tri " << k); - std::string material = line_polys[k].GetMaterial(); - TG_LOG(SG_GENERAL, SG_DEBUG, "material = " << material); - TG_LOG(SG_GENERAL, SG_DEBUG, "triangles = " << line_polys[k].Triangles()); - for ( unsigned int i = 0; i < line_polys[k].Triangles(); ++i ) - { - tri_v.clear(); - tri_n.clear(); - tri_tc.clear(); - for ( int j = 0; j < 3; ++j ) - { - index = nodes.add( line_polys[k].GetTriNode( i, j ) ); - tri_v.push_back( index ); - - // use 'the' normal - index = normals.add( vnt ); - tri_n.push_back( index ); - - index = texcoords.add( line_polys[k].GetTriTexCoord(i,j) ); - 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; - - TG_LOG(SG_GENERAL, SG_INFO, "Adding base nodes and normals"); - for ( unsigned int i = 0; i < base_poly.Triangles(); ++i ) - { - tri_v.clear(); - tri_n.clear(); - tri_tc.clear(); - for ( int j = 0; j < 3; ++j ) - { - index = nodes.add( base_poly.GetTriNode( i, j ) ); - tri_v.push_back( index ); - - index = normals.add( vnt ); - 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 = nodes.get_list(); - - base_txs.clear(); - base_txs = sgCalcTexCoords( b, geodNodes, tri_v ); - - base_tc.clear(); - for ( unsigned int j = 0; j < base_txs.size(); ++j ) - { - index = texcoords.add( base_txs[j] ); - 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 ( unsigned int i = 0; i < divided_base.Contours(); ++i ) - { - for ( unsigned int j = 0; j < divided_base.ContourSize( i ); ++j ) - { - index = nodes.add( divided_base.GetNode(i, j) ); - TG_LOG(SG_GENERAL, SG_DEBUG, "added base point " << divided_base.GetNode(i, j) << " at " << index ); - } - } - - // 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. - - TG_LOG(SG_GENERAL, SG_DEBUG, " calc average elevation"); - { - std::vector < SGGeod > dbg = nodes.get_list(); - - // dump the node list - TG_LOG(SG_GENERAL, SG_DEBUG, " node list size is " << dbg.size() ); - for (unsigned int w = 0; w<dbg.size(); w++) - { - TG_LOG(SG_GENERAL, SG_DEBUG, " node " << w << " is " << dbg[w] ); - } - } - - double average = tgAverageElevation( root, elev_src, nodes.get_list() ); - - // Now build the fitted airport surface ... - - // calculation min/max coordinates of airport area - TG_LOG(SG_GENERAL, SG_DEBUG, " calculation min/max coordinates of airport area"); - - SGGeod min_deg = SGGeod::fromDeg(9999.0, 9999.0); - SGGeod max_deg = SGGeod::fromDeg(-9999.0, -9999.0); - for ( unsigned int j = 0; j < nodes.get_list().size(); ++j ) - { - SGGeod p = nodes.get_list()[j]; - if ( p.getLongitudeDeg() < min_deg.getLongitudeDeg() ) - { - TG_LOG(SG_GENERAL, SG_DEBUG, "new min lon from node " << j << " is " << p.getLongitudeDeg() ); - min_deg.setLongitudeDeg( p.getLongitudeDeg() ); - } - if ( p.getLongitudeDeg() > max_deg.getLongitudeDeg() ) - { - TG_LOG(SG_GENERAL, SG_DEBUG, "new max lon from node " << j << " is " << p.getLongitudeDeg() ); - max_deg.setLongitudeDeg( p.getLongitudeDeg() ); - } - if ( p.getLatitudeDeg() < min_deg.getLatitudeDeg() ) - { - TG_LOG(SG_GENERAL, SG_DEBUG, "new min lat from node " << j << " is " << p.getLatitudeDeg() ); - min_deg.setLatitudeDeg( p.getLatitudeDeg() ); - } - if ( p.getLatitudeDeg() > max_deg.getLatitudeDeg() ) - { - TG_LOG(SG_GENERAL, SG_DEBUG, "new max lat from node " << j << " is " << p.getLatitudeDeg() ); - max_deg.setLatitudeDeg( p.getLatitudeDeg() ); - } - } - - TG_LOG(SG_GENERAL, SG_DEBUG, "Before extending for lights: min = " << min_deg << " max = " << max_deg ); - - // extend the min/max coordinates of airport area to cover all - // lights as well - - TG_LOG(SG_GENERAL, SG_DEBUG, " extend the min/max coordinates of airport area to cover all lights as well : num rwy lights is " << rwy_lights.size() ); - for ( unsigned int i = 0; i < rwy_lights.size(); ++i ) - { - TG_LOG(SG_GENERAL, SG_DEBUG, " extend the min/max coordinates of airport area to cover all lights as well : rwy light " << i << "has " << rwy_lights[i].ContourSize() << " lights " ); - - for ( unsigned int j = 0; j < rwy_lights[i].ContourSize(); ++j ) - { - SGGeod p = rwy_lights[i].GetNode(j); - if ( p.getLongitudeDeg() < min_deg.getLongitudeDeg() ) - { - min_deg.setLongitudeDeg( p.getLongitudeDeg() ); - } - if ( p.getLongitudeDeg() > max_deg.getLongitudeDeg() ) - { - max_deg.setLongitudeDeg( p.getLongitudeDeg() ); - } - if ( p.getLatitudeDeg() < min_deg.getLatitudeDeg() ) - { - min_deg.setLatitudeDeg( p.getLatitudeDeg() ); - } - if ( p.getLatitudeDeg() > max_deg.getLatitudeDeg() ) - { - max_deg.setLatitudeDeg( p.getLatitudeDeg() ); - } - } - } - - // Extend the area a bit so we don't have wierd things on the edges - double dlon = max_deg.getLongitudeDeg() - min_deg.getLongitudeDeg(); - double dlat = max_deg.getLatitudeDeg() - min_deg.getLatitudeDeg(); - min_deg.setLongitudeDeg( min_deg.getLongitudeDeg() - 0.01 * dlon ); - max_deg.setLongitudeDeg( max_deg.getLongitudeDeg() + 0.01 * dlon ); - min_deg.setLatitudeDeg( min_deg.getLatitudeDeg() - 0.01 * dlat ); - max_deg.setLatitudeDeg( max_deg.getLatitudeDeg() + 0.01 * dlat ); - TG_LOG(SG_GENERAL, SG_DEBUG, "min = " << min_deg << " max = " << max_deg ); - - TG_LOG(SG_GENERAL, SG_DEBUG, "Create Apt surface:" ); - TG_LOG(SG_GENERAL, SG_DEBUG, " root: " << root ); - //TG_LOG(SG_GENERAL, SG_DEBUG, " elev: " << elev_src ); - TG_LOG(SG_GENERAL, SG_DEBUG, " min: " << min_deg ); - TG_LOG(SG_GENERAL, SG_DEBUG, " max: " << max_deg ); - TG_LOG(SG_GENERAL, SG_DEBUG, " average: " << average ); - - // TODO elevation queries should be performed as member functions of surface - tgRectangle aptBounds(min_deg, max_deg); - tgSurface apt_surf( root, elev_src, aptBounds, average, slope_max, slope_eps ); - TG_LOG(SG_GENERAL, SG_DEBUG, "Airport surface created"); - - // add light points - // pass one, calculate raw elevations from Array - for ( unsigned int i = 0; i < rwy_lights.size(); ++i ) { - for ( unsigned int j = 0; j < rwy_lights[i].ContourSize(); j++ ) { - double light_elevation = calc_elevation( apt_surf, rwy_lights[i].GetNode(j), 0.0 ); - rwy_lights[i].SetElevation(j, light_elevation); - } - } - - TG_LOG(SG_GENERAL, SG_INFO, "Done with lighting calc_elevations() num light polys is " << rwy_lights.size() ); - - // pass two, for each light group check if we need to lift (based - // on flag) and do so, then output next structures. - // for ( unsigned int i = 0; i < rwy_lights.size(); ++i ) - for ( unsigned int i = 0; i < rwy_lights.size(); ++i ) - { - if ( rwy_lights[i].ContourSize() ) - { - pt_v.clear(); - pt_n.clear(); - for ( unsigned int j = 0; j < rwy_lights[i].ContourSize(); ++j ) - { - index = nodes.add( rwy_lights[i].GetPosition(j) ); - pt_v.push_back( index ); - - index = normals.add( rwy_lights[i].GetNormal(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].GetType() ); - } - } - - // calculate node elevations - TG_LOG(SG_GENERAL, SG_DEBUG, "Computing airport node elevations"); - - std::vector<SGGeod> geod_nodes = calc_elevations( apt_surf, nodes.get_list(), 0.0 ); - divided_base = calc_elevations( apt_surf, divided_base, 0.0 ); - - // calculate wgs84 mapping of nodes - std::vector<SGVec3d> wgs84_nodes; - for ( unsigned int i = 0; i < geod_nodes.size(); ++i ) - { - wgs84_nodes.push_back( SGVec3d::fromGeod(geod_nodes[i]) ); - } - - SGVec3d gbs_center = SGVec3d::fromGeod( b.get_center() ); - double dist_squared, radius_squared = 0; - for ( unsigned int i = 0; i < wgs84_nodes.size(); ++i ) - { - dist_squared = distSqr(gbs_center, wgs84_nodes[i]); - if ( dist_squared > radius_squared ) { - radius_squared = dist_squared; - } - } - - double gbs_radius = sqrt(radius_squared); - - TG_LOG(SG_GENERAL, SG_DEBUG, "Done with wgs84 node mapping"); - TG_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(); - - std::string objpath = root + "/AirportObj"; - std::string name = icao + ".btg"; - - SGBinObject obj; - obj.set_gbs_center( gbs_center ); - obj.set_gbs_radius( gbs_radius ); - obj.set_wgs84_nodes( wgs84_nodes ); - obj.set_normals( normals.get_list() ); - obj.set_texcoords( texcoords.get_list() ); - 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 // TODO : along with taxiway signs - // 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 ); - } -#endif - - SGGeod ref_geod; - // calc elevations and write out windsock references - TG_LOG(SG_GENERAL, SG_DEBUG, "Computing windsock node elevations"); - - for ( unsigned int i = 0; i < windsocks.size(); ++i ) - { - SGGeod ref_geod = windsocks[i]->GetLoc(); - ref_geod.setElevationM( calc_elevation( apt_surf, ref_geod, 0.0 ) ); - - if ( windsocks[i]->IsLit() ) - { - write_index_shared( objpath, b, ref_geod, - "Models/Airport/windsock_lit.xml", 0.0 ); - } - else - { - write_index_shared( objpath, b, ref_geod, - "Models/Airport/windsock.xml", 0.0 ); - } - } - - // write out beacon references - for ( unsigned int i = 0; i < beacons.size(); ++i ) - { - ref_geod = beacons[i]->GetLoc(); - ref_geod.setElevationM( calc_elevation( apt_surf, ref_geod, 0.0 ) ); - - write_index_shared( objpath, b, ref_geod, - "Models/Airport/beacon.xml", - 0.0 ); - } - - // write out taxiway signs references - for ( unsigned int i = 0; i < signs.size(); ++i ) - { - ref_geod = signs[i]->GetLoc(); - ref_geod.setElevationM( calc_elevation( apt_surf, ref_geod, 0.0 ) ); - write_object_sign( objpath, b, ref_geod, - signs[i]->GetDefinition(), - signs[i]->GetHeading(), - signs[i]->GetSize() ); - } - - // write out water buoys - for ( unsigned int i = 0; i < waterrunways.size(); ++i ) - { - tgContour buoys = waterrunways[i]->GetBuoys(); - - for ( unsigned int j = 0; j < buoys.GetSize(); ++j ) - { - ref_geod = buoys.GetNode(j); - ref_geod.setElevationM( calc_elevation( apt_surf, ref_geod, 0.0 ) ); - write_index_shared( objpath, b, ref_geod, - "Models/Airport/water_rw_buoy.xml", - 0.0 ); - } - } - - std::string holepath = root + "/AirportArea"; - tgChopper chopper( holepath ); - - divided_base.SetPreserve3D( true ); - apt_clearing.SetPreserve3D( false ); - apt_clearing.SetTexMethod( TG_TEX_BY_GEODE ); - - chopper.Add( divided_base, "Hole" ); - chopper.Add( apt_clearing, "Airport" ); - chopper.Save( false ); -} +#include <list> +#include <ctime> + +#include <stdio.h> + +#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 <terragear/tg_polygon.hxx> +#include <terragear/tg_chopper.hxx> +#include <terragear/tg_unique_geod.hxx> +#include <terragear/tg_unique_vec3f.hxx> +#include <terragear/tg_unique_vec2f.hxx> + +#include "airport.hxx" +#include "beznode.hxx" +#include "debug.hxx" +#include "elevations.hxx" +#include "global.hxx" +#include "helipad.hxx" +#include "runway.hxx" +#include "output.hxx" + + +Airport::Airport( int c, char* def) +{ + int numParams; + char* tok; + int ct = 0; + + code = c; + + // we need to tokenize airports, since we can't scanf two strings next to each other... + numParams = 0; + bool done = false; + + while (!done) + { + // trim leading whitespace + while(isspace(*def)) def++; + + tok = strtok(def, " \t\r\n"); + + if (tok) + { + def += strlen(tok)+1; + + switch( numParams ) + { + case 0: + altitude = atoi(tok); + break; + + case 1: + ct = atoi(tok); + break; + + case 2: + // deprecated - ignore + break; + + case 3: + icao = tok; + description = def; + done = true; + break; + } + } + numParams++; + } + + altitude *= SG_FEET_TO_METER; + + TG_LOG( SG_GENERAL, SG_DEBUG, "Read airport with icao " << icao << ", control tower " << ct << ", and description " << description ); +} + +Airport::~Airport() +{ + for (unsigned int i=0; i<features.size(); i++) + { + delete features[i]; + } + + for (unsigned int i=0; i<helipads.size(); i++) + { + delete helipads[i]; + } + + for (unsigned int i=0; i<runways.size(); i++) + { + delete runways[i]; + } + + for (unsigned int i=0; i<waterrunways.size(); i++) + { + delete waterrunways[i]; + } + + for (unsigned int i=0; i<pavements.size(); i++) + { + delete pavements[i]; + } + + for (unsigned int i=0; i<taxiways.size(); i++) + { + delete taxiways[i]; + } + + for (unsigned int i=0; i<lightobjects.size(); i++) + { + delete lightobjects[i]; + } + + for (unsigned int i=0; i<windsocks.size(); i++) + { + delete windsocks[i]; + } + + for (unsigned int i=0; i<beacons.size(); i++) + { + delete beacons[i]; + } + + for (unsigned int i=0; i<signs.size(); i++) + { + delete signs[i]; + } + + for (unsigned int i=0; i<boundary.size(); i++) + { + delete boundary[i]; + } +} + +bool Airport::isDebugRunway( int rwy ) +{ + bool dbg = false; + + debug_map_const_iterator it = debug_runways.find(icao); + if ( it != debug_runways.end() ) { + for ( unsigned int i=0; i<it->second.size() && !dbg; i++ ) { + if( it->second[i] == std::numeric_limits<int>::max() ) { + dbg = true; + } else if ( it->second[i] == rwy+1 ) { + dbg = true; + } + } + } + + return dbg; +} + +bool Airport::isDebugPavement( int pvmt ) +{ + bool dbg = false; + + debug_map_const_iterator it = debug_pavements.find(icao); + if ( it != debug_pavements.end() ) { + for ( unsigned int i=0; i<it->second.size() && !dbg; i++ ) { + if( it->second[i] == std::numeric_limits<int>::max() ) { + dbg = true; + } else if ( it->second[i] == pvmt+1 ) { + dbg = true; + } + } + } + + return dbg; +} + +bool Airport::isDebugTaxiway( int taxi ) +{ + bool dbg = false; + + debug_map_const_iterator it = debug_taxiways.find(icao); + if ( it != debug_taxiways.end() ) { + for ( unsigned int i=0; i<it->second.size() && !dbg; i++ ) { + if( it->second[i] == std::numeric_limits<int>::max() ) { + dbg = true; + } else if ( it->second[i] == taxi+1 ) { + dbg = true; + } + } + } + + return dbg; +} + +bool Airport::isDebugFeature( int feat ) +{ + bool dbg = false; + + debug_map_const_iterator it = debug_features.find(icao); + if ( it != debug_features.end() ) { + for ( unsigned int i=0; i<it->second.size() && !dbg; i++ ) { + if( it->second[i] == std::numeric_limits<int>::max() ) { + dbg = true; + } else if ( it->second[i] == feat+1 ) { + dbg = true; + } + } + } + + return dbg; +} + + +// TODO : Add somewhere +// Determine node elevations of a point_list based on the provided +// TGAptSurface. Offset is added to the final elevation +static std::vector<SGGeod> calc_elevations( const tgSurface& surf, const std::vector<SGGeod>& geod_nodes, double offset ) +{ + std::vector<SGGeod> result = geod_nodes; + for ( unsigned int i = 0; i < result.size(); ++i ) { + double elev = surf.query( result[i] ); + result[i].setElevationM( elev + offset ); + } + + return result; +} + + +static tgContour calc_elevations( const tgSurface& surf, const tgContour& geod_nodes, double offset ) +{ + tgContour result = geod_nodes; + for ( unsigned int i = 0; i < result.GetSize(); ++i ) { + SGGeod node = result.GetNode(i); + double elev = surf.query( node ); + node.setElevationM( elev + offset ); + result.SetNode( i, node ); + } + + return result; +} + +static double calc_elevation( const tgSurface& surf, const SGGeod& node, double offset ) +{ + double elev = surf.query( node ); + elev += offset; + + return elev; +} + + +// 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( const tgSurface& surf, const tgPolygon& poly, double offset ) +{ + tgPolygon result; + + for ( unsigned int i = 0; i < poly.Contours(); ++i ) { + tgContour contour = poly.GetContour( i ); + tgContour elevated = calc_elevations( surf, contour, offset ); + + result.AddContour( elevated ); + } + + return result; +} + +void Airport::BuildBtg(const std::string& root, const string_list& elev_src ) +{ + tgcontour_list slivers; + tgcontour_list line_slivers; + + tgpolygon_list apt_base_polys; + tgpolygon_list apt_clearing_polys; + + // runways + tgpolygon_list rwy_polys; + + // pavements + tgpolygon_list pvmt_polys; + + // linear features + tgpolygon_list line_polys; + + // runway lights + tglightcontour_list rwy_lights; + + bool make_shapefiles = false; + + // parse main airport information + double apt_lon = 0.0, apt_lat = 0.0; + + SGTimeStamp build_start; + SGTimeStamp build_end; + SGTimeStamp cleanup_start; + SGTimeStamp cleanup_end; + SGTimeStamp triangulation_start; + SGTimeStamp triangulation_end; + time_t log_time; + + char shapefile_name[64]; + std::string shapefile; + + // Find the average of all the runway and heliport long / lats + int num_samples = 0; + for (unsigned int i=0; i<runways.size(); i++) + { + apt_lon += runways[i]->GetMidpoint().getLongitudeDeg(); + apt_lat += runways[i]->GetMidpoint().getLatitudeDeg(); + num_samples++; + } + for (unsigned int i=0; i<helipads.size(); i++) + { + apt_lon += helipads[i]->GetLoc().getLongitudeDeg(); + apt_lat += helipads[i]->GetLoc().getLatitudeDeg(); + num_samples++; + } + apt_lon = apt_lon / (double)num_samples; + apt_lat = apt_lat / (double)num_samples; + + SGBucket b( SGGeod::fromDeg(apt_lon, apt_lat) ); + TG_LOG(SG_GENERAL, SG_DEBUG, b.gen_base_path() << "/" << b.gen_index_str()); + + // If we are cutting in the linear features, add them first + if (pavements.size()) + { + for ( unsigned int i=0; i<pavements.size(); i++ ) + { + AddFeatures( pavements[i]->GetFeatures() ); + } + } + + TG_LOG(SG_GENERAL, SG_INFO, "Parse Complete - Runways: " << runways.size() << " Pavements: " << pavements.size() << " Features: " << features.size() << " Taxiways: " << taxiways.size() ); + + // Starting to clip the polys (for now - only UNIX builds) + build_start.stamp(); + + // Add the linear features + if (features.size()) + { + tgAccumulator lf_accum; + + TG_LOG(SG_GENERAL, SG_INFO, "Build " << features.size() << " Linear Feature Polys"); + for ( unsigned int i=0; i<features.size(); i++ ) + { + TG_LOG(SG_GENERAL, SG_DEBUG, "Build Feature Poly " << i + 1 << " of " << features.size() << " : " << features[i]->GetDescription() ); + + features[i]->BuildBtg( line_polys, rwy_lights, lf_accum, make_shapefiles ); + } + +// lf_accum.ToShapefiles( "./lf_accum", "test", false ); + + log_time = time(0); + TG_LOG( SG_GENERAL, SG_ALERT, "Finished building Linear Features for " << icao << " at " << DebugTimeToString(log_time) ); + } + + /* Initialize a new accumulator for the other objects */ + tgAccumulator pvmt_accum; + + // Build runways next + if (runways.size()) + { + TG_LOG(SG_GENERAL, SG_INFO, "Build " << runways.size() << " Runway Polys"); + for ( unsigned int i=0; i<runways.size(); i++ ) + { + TG_LOG(SG_GENERAL, SG_DEBUG, "Build Runway " << i + 1 << " of " << runways.size()); + slivers.clear(); + + if ( isDebugRunway(i) ) { + sprintf( shapefile_name, "runway_%d", i ); + } else { + strcpy( shapefile_name, "" ); + } + shapefile = shapefile_name; + + if (boundary.size()) + { + runways[i]->BuildBtg( rwy_polys, rwy_lights, slivers, pvmt_accum, shapefile ); + } + else + { + runways[i]->BuildBtg( rwy_polys, rwy_lights, slivers, apt_base_polys, apt_clearing_polys, pvmt_accum, shapefile ); + } + + // Now try to merge any slivers we found + tgPolygon::MergeSlivers( rwy_polys, slivers ); + } + + log_time = time(0); + TG_LOG( SG_GENERAL, SG_ALERT, "Finished building runways for " << icao << " at " << DebugTimeToString(log_time) ); + } + + if (lightobjects.size()) + { + TG_LOG(SG_GENERAL, SG_INFO, "Build " << lightobjects.size() << " Light Objects "); + for ( unsigned int i=0; i<lightobjects.size(); i++ ) + { + TG_LOG(SG_GENERAL, SG_DEBUG, "Build runway light " << i + 1 << " of " << lightobjects.size()); + lightobjects[i]->BuildBtg( rwy_lights ); + } + } + + // Build helipads (use runway poly- and texture list for this) + if (helipads.size()) + { + TG_LOG(SG_GENERAL, SG_INFO, "Build " << helipads.size() << " Helipad Polys "); + for ( unsigned int i=0; i<helipads.size(); i++ ) + { + TG_LOG(SG_GENERAL, SG_DEBUG, "Build helipad " << i + 1 << " of " << helipads.size()); + slivers.clear(); + + if (boundary.size()) + { + helipads[i]->BuildBtg( rwy_polys, rwy_lights, slivers, pvmt_accum ); + } + else + { + helipads[i]->BuildBtg( rwy_polys, rwy_lights, slivers, apt_base_polys, apt_clearing_polys, pvmt_accum ); + } + + // Now try to merge any slivers we found + tgPolygon::MergeSlivers( rwy_polys, slivers ); + } + } + + // Build the pavements + if (pavements.size()) + { + TG_LOG(SG_GENERAL, SG_INFO, "Build " << pavements.size() << " Pavement Polys "); + for ( unsigned int i=0; i<pavements.size(); i++ ) + { + TG_LOG(SG_GENERAL, SG_DEBUG, "Build Pavement " << i + 1 << " of " << pavements.size() << " : " << pavements[i]->GetDescription()); + slivers.clear(); + + if ( isDebugPavement(i) ) { + sprintf( shapefile_name, "pvmnt_%d", i ); + } else { + strcpy( shapefile_name, "" ); + } + shapefile = shapefile_name; + + if (boundary.size()) + { + pavements[i]->BuildBtg( pvmt_polys, slivers, pvmt_accum, shapefile ); + } + else + { + pavements[i]->BuildBtg( pvmt_polys, slivers, apt_base_polys, apt_clearing_polys, pvmt_accum, shapefile ); + } + + // Now try to merge any slivers we found + tgPolygon::MergeSlivers( rwy_polys, slivers ); + tgPolygon::MergeSlivers( pvmt_polys, slivers ); + } + + log_time = time(0); + TG_LOG( SG_GENERAL, SG_ALERT, "Finished building Pavements for " << icao << " at " << DebugTimeToString(log_time) ); + } + + // Build the legacy taxiways + if (taxiways.size()) + { + TG_LOG(SG_GENERAL, SG_INFO, "Build " << taxiways.size() << " Taxiway Polys "); + for ( unsigned int i=0; i<taxiways.size(); i++ ) + { + TG_LOG(SG_GENERAL, SG_DEBUG, "Build Taxiway " << i + 1 << " of " << taxiways.size()); + slivers.clear(); + + if ( isDebugTaxiway(i) ) { + sprintf( shapefile_name, "taxiway_%d", i ); + } else { + strcpy( shapefile_name, "" ); + } + shapefile = shapefile_name; + + if (boundary.size()) + { + taxiways[i]->BuildBtg( pvmt_polys, rwy_lights, slivers, pvmt_accum, shapefile ); + } + else + { + taxiways[i]->BuildBtg( pvmt_polys, rwy_lights, slivers, apt_base_polys, apt_clearing_polys, pvmt_accum, shapefile ); + } + + // Now try to merge any slivers we found + tgPolygon::MergeSlivers( rwy_polys, slivers ); + tgPolygon::MergeSlivers( pvmt_polys, slivers ); + } + } + + // Build runway shoulders here + if ( runways.size() ) + { + TG_LOG(SG_GENERAL, SG_INFO, "Build " << runways.size() << " Runway Shoulder Polys "); + for ( unsigned int i=0; i<runways.size(); i++ ) + { + TG_LOG(SG_GENERAL, SG_DEBUG, "Build Runway shoulder " << i + 1 << " of " << runways.size()); + + if ( runways[i]->GetsShoulder() ) + { + slivers.clear(); + runways[i]->BuildShoulder( rwy_polys, slivers, pvmt_accum ); + + // Now try to merge any slivers we found + tgPolygon::MergeSlivers( rwy_polys, slivers ); + tgPolygon::MergeSlivers( pvmt_polys, slivers ); + } + } + } + + // Build helipad shoulders here + if ( helipads.size() ) + { + TG_LOG(SG_GENERAL, SG_INFO, "Build " << runways.size() << " Helipad Shoulder Polys "); + for ( unsigned int i=0; i<helipads.size(); i++ ) + { + TG_LOG(SG_GENERAL, SG_DEBUG, "Build Helipad shoulder " << i + 1 << " of " << helipads.size()); + + if ( helipads[i]->GetsShoulder() ) + { + slivers.clear(); + helipads[i]->BuildShoulder( rwy_polys, slivers, pvmt_accum ); + + // Now try to merge any slivers we found + tgPolygon::MergeSlivers( rwy_polys, slivers ); + tgPolygon::MergeSlivers( pvmt_polys, slivers ); + } + } + } + + // build the base and clearing if there's a boundary + tgPolygon apt_base, apt_clearing; + if (boundary.size()) + { + TG_LOG(SG_GENERAL, SG_INFO, "Build " << boundary.size() << " Boundary Polys "); + shapefile = ""; + + for ( unsigned int i=0; i<boundary.size(); i++ ) + { + TG_LOG(SG_GENERAL, SG_DEBUG, "Build Userdefined boundary " << i + 1 << " of " << boundary.size()); + boundary[i]->BuildBtg( apt_base, apt_clearing, shapefile ); + } + } else { + apt_base = tgPolygon::Union( apt_base_polys ); + apt_clearing = tgPolygon::Union( apt_clearing_polys ); + } + + if ( apt_base.TotalNodes() == 0 ) + { + TG_LOG(SG_GENERAL, SG_ALERT, "no airport points generated"); + return; + } + + tgPolygon filled_base = tgPolygon::StripHoles( apt_base ); + tgPolygon divided_base = tgPolygon::SplitLongEdges( filled_base, 200.0 ); + tgPolygon base_poly = pvmt_accum.Diff( divided_base ); + + build_end.stamp(); + build_time = build_end - build_start; + + cleanup_start.stamp(); + + // add segments to polygons to remove any possible "T" + // intersections + UniqueSGGeodSet tmp_pvmt_nodes; + UniqueSGGeodSet tmp_feat_nodes; + + // build temporary node list from runways... + TG_LOG(SG_GENERAL, SG_INFO, "Build Node List " ); + for ( unsigned int k = 0; k < rwy_polys.size(); ++k ) + { + tgPolygon poly = rwy_polys[k]; + for ( unsigned int i = 0; i < poly.Contours(); ++i ) + { + for ( unsigned int j = 0; j < poly.ContourSize( i ); ++j ) + { + tmp_pvmt_nodes.add( poly.GetNode(i, j) ); + } + } + } + + // and pavements + for ( unsigned int k = 0; k < pvmt_polys.size(); ++k ) + { + tgPolygon poly = pvmt_polys[k]; + for ( unsigned int i = 0; i < poly.Contours(); ++i ) + { + for ( unsigned int j = 0; j < poly.ContourSize( i ); ++j ) + { + tmp_pvmt_nodes.add( poly.GetNode(i, j) ); + } + } + } + + // and linear features ( keep Linear feature nodes seperate) + for ( unsigned int k = 0; k < line_polys.size(); ++k ) + { + tgPolygon poly = line_polys[k]; + for ( unsigned int i = 0; i < poly.Contours(); ++i ) + { + for ( unsigned int j = 0; j < poly.ContourSize( i ); ++j ) + { + tmp_feat_nodes.add( poly.GetNode(i, j) ); + } + } + } + + // and the base + for ( unsigned int i = 0; i < base_poly.Contours(); ++i ) + { + for ( unsigned int j = 0; j < base_poly.ContourSize( i ); ++j ) + { + tmp_pvmt_nodes.add( base_poly.GetNode(i, j) ); + } + } + + // the divided base could contain points not found in base_poly, + // so we should add them because the skirt needs them. + for ( unsigned int i = 0; i < divided_base.Contours(); ++i ) + { + for ( unsigned int j = 0; j < divided_base.ContourSize( i ); ++j ) + { + tmp_pvmt_nodes.add( divided_base.GetNode(i, j) ); + } + } + + log_time = time(0); + TG_LOG( SG_GENERAL, SG_ALERT, "Finished collecting nodes for " << icao << " at " << DebugTimeToString(log_time) ); + + // second pass : runways + for ( unsigned int k = 0; k < rwy_polys.size(); ++k ) + { + tgPolygon poly = rwy_polys[k]; + poly = tgPolygon::AddColinearNodes( poly, tmp_pvmt_nodes ); + TG_LOG(SG_GENERAL, SG_DEBUG, "total size after add nodes = " << poly.TotalNodes()); + rwy_polys[k] = poly; + } + + // second pass : and pavements + for ( unsigned int k = 0; k < pvmt_polys.size(); ++k ) + { + tgPolygon poly = pvmt_polys[k]; + poly = tgPolygon::AddColinearNodes( poly, tmp_pvmt_nodes ); + TG_LOG(SG_GENERAL, SG_DEBUG, "total size after add nodes = " << poly.TotalNodes()); + pvmt_polys[k] = poly; + } + + // second pass : and lines + for ( unsigned int k = 0; k < line_polys.size(); ++k ) + { + tgPolygon poly = line_polys[k]; + poly = tgPolygon::AddColinearNodes( poly, tmp_feat_nodes ); + TG_LOG(SG_GENERAL, SG_DEBUG, "total size after add nodes = " << poly.TotalNodes()); + line_polys[k] = poly; + } + + log_time = time(0); + TG_LOG( SG_GENERAL, SG_ALERT, "Finished adding intermediate nodes for " << icao << " at " << DebugTimeToString(log_time) ); + + for ( unsigned int k = 0; k < line_polys.size(); ++k ) + { + tgPolygon poly = line_polys[k]; + +#if 1 + poly = tgPolygon::RemoveCycles( poly ); + poly = tgPolygon::RemoveDups( poly ); + poly = tgPolygon::RemoveBadContours( poly ); + +// poly = tgPolygon::Simplify( poly ); +// poly = tgPolygon::RemoveTinyContours( poly ); +// poly = tgPolygon::RemoveSpikes( poly ); +// poly = tgPolygon::RemoveDups( poly ); +// poly = tgPolygon::RemoveBadContours( poly ); +// poly = tgPolygon::RemoveTinyContours( poly ); +#endif + + line_polys[k] = poly; + } + + log_time = time(0); + TG_LOG( SG_GENERAL, SG_ALERT, "Finished cleaning polys for " << icao << " at " << DebugTimeToString(log_time) ); + + base_poly = tgPolygon::AddColinearNodes( base_poly, tmp_pvmt_nodes ); + base_poly = tgPolygon::Snap( base_poly, gSnap ); + + // Finally find slivers in base + slivers.clear(); + tgPolygon::RemoveSlivers( base_poly, slivers ); + tgPolygon::MergeSlivers( rwy_polys, slivers ); + tgPolygon::MergeSlivers( pvmt_polys, slivers ); + + // Then snap rwy and pavement to grid (was done right after adding intermediate nodes...) + for ( unsigned int k = 0; k < rwy_polys.size(); ++k ) + { + tgPolygon poly = rwy_polys[k]; + poly = tgPolygon::Snap(poly, gSnap); + poly = tgPolygon::RemoveDups( poly ); + poly = tgPolygon::RemoveBadContours( poly ); + rwy_polys[k] = poly; + } + for ( unsigned int k = 0; k < pvmt_polys.size(); ++k ) + { + tgPolygon poly = pvmt_polys[k]; + poly = tgPolygon::Snap(poly, gSnap); + poly = tgPolygon::RemoveDups( poly ); + poly = tgPolygon::RemoveBadContours( poly ); + pvmt_polys[k] = poly; + } + + cleanup_end.stamp(); + cleanup_time = cleanup_end - cleanup_start; + + triangulation_start.stamp(); + + // tesselate the polygons and prepair them for final output + if ( rwy_polys.size() ) + { + TG_LOG(SG_GENERAL, SG_INFO, "Tesselating " << rwy_polys.size() << " Runway Polys " ); + for ( unsigned int i = 0; i < rwy_polys.size(); ++i ) + { + TG_LOG(SG_GENERAL, SG_DEBUG, "Tesselating runway poly = " << i + 1 << " of " << rwy_polys.size() ); + + TG_LOG(SG_GENERAL, SG_DEBUG, "contours before " << rwy_polys[i].Contours() << " total points before = " << rwy_polys[i].TotalNodes()); + rwy_polys[i].Tesselate(); + TG_LOG(SG_GENERAL, SG_DEBUG, "triangles after = " << rwy_polys[i].Triangles()); + rwy_polys[i].Texture(); + } + } + + if ( pvmt_polys.size() ) + { + // tesselate the polygons and prepair them for final output + TG_LOG(SG_GENERAL, SG_INFO, "Tesselating " << pvmt_polys.size() << " Pavement Polys " ); + for ( unsigned int i = 0; i < pvmt_polys.size(); ++i ) + { + TG_LOG(SG_GENERAL, SG_DEBUG, "Tesselating pavement poly = " << i + 1 << " of " << pvmt_polys.size() ); + + TG_LOG(SG_GENERAL, SG_DEBUG, "contours before " << pvmt_polys[i].Contours() << " total points before = " << pvmt_polys[i].TotalNodes()); + pvmt_polys[i].Tesselate(); + TG_LOG(SG_GENERAL, SG_DEBUG, "triangles after = " << pvmt_polys[i].Triangles()); + pvmt_polys[i].Texture(); + } + } + + if ( line_polys.size() ) + { + // tesselate the polygons and prepair them for final output + TG_LOG(SG_GENERAL, SG_INFO, "Tesselating " << line_polys.size() << " Linear Feature Polys " ); + for ( unsigned int i = 0; i < line_polys.size(); ++i ) + { + TG_LOG(SG_GENERAL, SG_DEBUG, "Tesselating line poly = " << i + 1 << " of " << line_polys.size() ); + + TG_LOG(SG_GENERAL, SG_DEBUG, "contours before " << line_polys[i].Contours() << " total points before = " << line_polys[i].TotalNodes()); + line_polys[i].Tesselate(); + TG_LOG(SG_GENERAL, SG_DEBUG, "triangles after = " << line_polys[i].Triangles()); + line_polys[i].Texture(); + } + } + + /* before tessellating the base, make sure there are no + intersecting contours */ + base_poly = tgPolygon::Simplify( base_poly ); + + TG_LOG(SG_GENERAL, SG_INFO, "Tesselating base poly : " << base_poly.Contours() << " contours " ); + base_poly.Tesselate(); + TG_LOG(SG_GENERAL, SG_INFO, "Tesselating base poly - done : Triangles = " << base_poly.Triangles()); + // should we texture base here? + base_poly.SetTexMethod( TG_TEX_BY_GEODE, b.get_center_lat() ); + base_poly.Texture(); + + triangulation_end.stamp(); + + // a few airports fail here + if ( base_poly.Triangles() == 0 ) + { + TG_LOG(SG_GENERAL, SG_ALERT, "no base poly triangles"); + return; + } + + triangulation_time = triangulation_end - triangulation_start; + + // + // 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 + // start with just nodes + SGBinObject obj; + SGBinObjectTriangle sgboTri; + SGBinObjectPoint sgboPt; + + UniqueSGGeodSet nodes; + UniqueSGVec3fSet normals; + UniqueSGVec2fSet texcoords; + + 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 + SGVec3f vnt = SGVec3f::fromGeod( base_poly.GetNode(0, 0) ); + vnt = normalize(vnt); + TG_LOG(SG_GENERAL, SG_INFO, "Adding runway nodes and normals"); + + for ( unsigned int k = 0; k < rwy_polys.size(); ++k ) + { + tgPolygon poly = rwy_polys[k]; + + TG_LOG(SG_GENERAL, SG_DEBUG, "tri " << k); + std::string material = rwy_polys[k].GetMaterial(); + TG_LOG(SG_GENERAL, SG_DEBUG, "material = " << material); + TG_LOG(SG_GENERAL, SG_DEBUG, "triangles = " << rwy_polys[k].Triangles()); + for ( unsigned int i = 0; i < rwy_polys[k].Triangles(); ++i ) + { + sgboTri.clear(); + sgboTri.material = material; + + for (int l = 0; l < 3; ++l) { + int index; + + index = nodes.add( poly.GetTriNode( i, l ) ); + sgboTri.v_list.push_back( index ); + + // use 'the' normal + index = normals.add( vnt ); + sgboTri.n_list.push_back( index ); + + index = texcoords.add( poly.GetTriTexCoord( i, l ) ); + sgboTri.tc_list[0].push_back( index ); + } + + obj.add_triangle( sgboTri ); + } + } + + TG_LOG(SG_GENERAL, SG_INFO, "Adding pavement nodes and normals"); + for ( unsigned int k = 0; k < pvmt_polys.size(); ++k ) + { + TG_LOG(SG_GENERAL, SG_DEBUG, "tri " << k); + std::string material = pvmt_polys[k].GetMaterial(); + TG_LOG(SG_GENERAL, SG_DEBUG, "material = " << material); + TG_LOG(SG_GENERAL, SG_DEBUG, "triangles = " << pvmt_polys[k].Triangles()); + for ( unsigned int i = 0; i < pvmt_polys[k].Triangles(); ++i ) + { + tgPolygon poly = pvmt_polys[k]; + sgboTri.clear(); + sgboTri.material = material; + + for (int l = 0; l < 3; ++l) { + int index; + + index = nodes.add( poly.GetTriNode( i, l ) ); + sgboTri.v_list.push_back( index ); + + // use 'the' normal + index = normals.add( vnt ); + sgboTri.n_list.push_back( index ); + + index = texcoords.add( poly.GetTriTexCoord( i, l ) ); + sgboTri.tc_list[0].push_back( index ); + } + + obj.add_triangle( sgboTri ); + } + } + + TG_LOG(SG_GENERAL, SG_INFO, "Adding line nodes and normals"); + for ( unsigned int k = 0; k < line_polys.size(); ++k ) + { + TG_LOG(SG_GENERAL, SG_INFO, "tri " << k); + std::string material = line_polys[k].GetMaterial(); + TG_LOG(SG_GENERAL, SG_INFO, "material = " << material); + TG_LOG(SG_GENERAL, SG_INFO, "triangles = " << line_polys[k].Triangles()); + for ( unsigned int i = 0; i < line_polys[k].Triangles(); ++i ) + { + tgPolygon poly = line_polys[k]; + sgboTri.clear(); + sgboTri.material = material; + + for (int l = 0; l < 3; ++l) { + int index; + + index = nodes.add( poly.GetTriNode( i, l ) ); + sgboTri.v_list.push_back( index ); + + // use 'the' normal + index = normals.add( vnt ); + sgboTri.n_list.push_back( index ); + + index = texcoords.add( poly.GetTriTexCoord( i, l ) ); + sgboTri.tc_list[0].push_back( index ); + } + + obj.add_triangle( sgboTri ); + } + } + + // add base points + TG_LOG(SG_GENERAL, SG_INFO, "Adding base triangles"); + std::string material = "Grass"; + for (unsigned int k = 0; k < base_poly.Triangles(); ++k) { + sgboTri.clear(); + sgboTri.material = material; + + for (int l = 0; l < 3; ++l) { + int index; + + index = nodes.add( base_poly.GetTriNode( k, l ) ); + sgboTri.v_list.push_back( index ); + + // use 'the' normal + index = normals.add( vnt ); + sgboTri.n_list.push_back( index ); + + index = texcoords.add( base_poly.GetTriTexCoord( k, l ) ); + sgboTri.tc_list[0].push_back( index ); + } + + obj.add_triangle( sgboTri ); + } + + // 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 ( unsigned int i = 0; i < divided_base.Contours(); ++i ) + { + for ( unsigned int j = 0; j < divided_base.ContourSize( i ); ++j ) + { + index = nodes.add( divided_base.GetNode(i, j) ); + TG_LOG(SG_GENERAL, SG_DEBUG, "added base point " << divided_base.GetNode(i, j) << " at " << index ); + } + } + + // 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. + + TG_LOG(SG_GENERAL, SG_DEBUG, " calc average elevation"); + { + std::vector < SGGeod > dbg = nodes.get_list(); + + // dump the node list + TG_LOG(SG_GENERAL, SG_DEBUG, " node list size is " << dbg.size() ); + for (unsigned int w = 0; w<dbg.size(); w++) + { + TG_LOG(SG_GENERAL, SG_DEBUG, " node " << w << " is " << dbg[w] ); + } + } + + double average = tgAverageElevation( root, elev_src, nodes.get_list() ); + + // Now build the fitted airport surface ... + + // calculation min/max coordinates of airport area + TG_LOG(SG_GENERAL, SG_DEBUG, " calculation min/max coordinates of airport area"); + + SGGeod min_deg = SGGeod::fromDeg(9999.0, 9999.0); + SGGeod max_deg = SGGeod::fromDeg(-9999.0, -9999.0); + for ( unsigned int j = 0; j < nodes.get_list().size(); ++j ) + { + SGGeod p = nodes.get_list()[j]; + if ( p.getLongitudeDeg() < min_deg.getLongitudeDeg() ) + { + TG_LOG(SG_GENERAL, SG_DEBUG, "new min lon from node " << j << " is " << p.getLongitudeDeg() ); + min_deg.setLongitudeDeg( p.getLongitudeDeg() ); + } + if ( p.getLongitudeDeg() > max_deg.getLongitudeDeg() ) + { + TG_LOG(SG_GENERAL, SG_DEBUG, "new max lon from node " << j << " is " << p.getLongitudeDeg() ); + max_deg.setLongitudeDeg( p.getLongitudeDeg() ); + } + if ( p.getLatitudeDeg() < min_deg.getLatitudeDeg() ) + { + TG_LOG(SG_GENERAL, SG_DEBUG, "new min lat from node " << j << " is " << p.getLatitudeDeg() ); + min_deg.setLatitudeDeg( p.getLatitudeDeg() ); + } + if ( p.getLatitudeDeg() > max_deg.getLatitudeDeg() ) + { + TG_LOG(SG_GENERAL, SG_DEBUG, "new max lat from node " << j << " is " << p.getLatitudeDeg() ); + max_deg.setLatitudeDeg( p.getLatitudeDeg() ); + } + } + + TG_LOG(SG_GENERAL, SG_DEBUG, "Before extending for lights: min = " << min_deg << " max = " << max_deg ); + + // extend the min/max coordinates of airport area to cover all + // lights as well + + TG_LOG(SG_GENERAL, SG_DEBUG, " extend the min/max coordinates of airport area to cover all lights as well : num rwy lights is " << rwy_lights.size() ); + for ( unsigned int i = 0; i < rwy_lights.size(); ++i ) + { + TG_LOG(SG_GENERAL, SG_DEBUG, " extend the min/max coordinates of airport area to cover all lights as well : rwy light " << i << "has " << rwy_lights[i].ContourSize() << " lights " ); + + for ( unsigned int j = 0; j < rwy_lights[i].ContourSize(); ++j ) + { + SGGeod p = rwy_lights[i].GetNode(j); + if ( p.getLongitudeDeg() < min_deg.getLongitudeDeg() ) + { + min_deg.setLongitudeDeg( p.getLongitudeDeg() ); + } + if ( p.getLongitudeDeg() > max_deg.getLongitudeDeg() ) + { + max_deg.setLongitudeDeg( p.getLongitudeDeg() ); + } + if ( p.getLatitudeDeg() < min_deg.getLatitudeDeg() ) + { + min_deg.setLatitudeDeg( p.getLatitudeDeg() ); + } + if ( p.getLatitudeDeg() > max_deg.getLatitudeDeg() ) + { + max_deg.setLatitudeDeg( p.getLatitudeDeg() ); + } + } + } + + // Extend the area a bit so we don't have wierd things on the edges + double dlon = max_deg.getLongitudeDeg() - min_deg.getLongitudeDeg(); + double dlat = max_deg.getLatitudeDeg() - min_deg.getLatitudeDeg(); + min_deg.setLongitudeDeg( min_deg.getLongitudeDeg() - 0.01 * dlon ); + max_deg.setLongitudeDeg( max_deg.getLongitudeDeg() + 0.01 * dlon ); + min_deg.setLatitudeDeg( min_deg.getLatitudeDeg() - 0.01 * dlat ); + max_deg.setLatitudeDeg( max_deg.getLatitudeDeg() + 0.01 * dlat ); + TG_LOG(SG_GENERAL, SG_DEBUG, "min = " << min_deg << " max = " << max_deg ); + + TG_LOG(SG_GENERAL, SG_DEBUG, "Create Apt surface:" ); + TG_LOG(SG_GENERAL, SG_DEBUG, " root: " << root ); + //TG_LOG(SG_GENERAL, SG_DEBUG, " elev: " << elev_src ); + TG_LOG(SG_GENERAL, SG_DEBUG, " min: " << min_deg ); + TG_LOG(SG_GENERAL, SG_DEBUG, " max: " << max_deg ); + TG_LOG(SG_GENERAL, SG_DEBUG, " average: " << average ); + + // TODO elevation queries should be performed as member functions of surface + tgRectangle aptBounds(min_deg, max_deg); + tgSurface apt_surf( root, elev_src, aptBounds, average, slope_max, slope_eps ); + TG_LOG(SG_GENERAL, SG_DEBUG, "Airport surface created"); + + // add light points + // pass one, calculate raw elevations from Array + for ( unsigned int i = 0; i < rwy_lights.size(); ++i ) { + for ( unsigned int j = 0; j < rwy_lights[i].ContourSize(); j++ ) { + double light_elevation = calc_elevation( apt_surf, rwy_lights[i].GetNode(j), 0.0 ); + rwy_lights[i].SetElevation(j, light_elevation); + } + } + + TG_LOG(SG_GENERAL, SG_INFO, "Done with lighting calc_elevations() num light polys is " << rwy_lights.size() ); + + // pass two, for each light group check if we need to lift (based + // on flag) and do so, then output next structures. + // for ( unsigned int i = 0; i < rwy_lights.size(); ++i ) + for ( unsigned int i = 0; i < rwy_lights.size(); ++i ) + { + if ( rwy_lights[i].ContourSize() ) + { + sgboPt.clear(); + sgboPt.material = rwy_lights[i].GetType(); + + for ( unsigned int j = 0; j < rwy_lights[i].ContourSize(); ++j ) + { + int index; + + index = nodes.add( rwy_lights[i].GetPosition(j) ); + sgboPt.v_list.push_back( index ); + + index = normals.add( rwy_lights[i].GetNormal(j) ); + sgboPt.n_list.push_back( index ); + } + obj.add_point( sgboPt ); + } + } + + // calculate node elevations + TG_LOG(SG_GENERAL, SG_DEBUG, "Computing airport node elevations"); + + std::vector<SGGeod> geod_nodes = calc_elevations( apt_surf, nodes.get_list(), 0.0 ); + divided_base = calc_elevations( apt_surf, divided_base, 0.0 ); + + // calculate wgs84 mapping of nodes + std::vector<SGVec3d> wgs84_nodes; + for ( unsigned int i = 0; i < geod_nodes.size(); ++i ) + { + wgs84_nodes.push_back( SGVec3d::fromGeod(geod_nodes[i]) ); + } + + SGVec3d gbs_center = SGVec3d::fromGeod( b.get_center() ); + double dist_squared, radius_squared = 0; + for ( unsigned int i = 0; i < wgs84_nodes.size(); ++i ) + { + dist_squared = distSqr(gbs_center, wgs84_nodes[i]); + if ( dist_squared > radius_squared ) { + radius_squared = dist_squared; + } + } + + double gbs_radius = sqrt(radius_squared); + + TG_LOG(SG_GENERAL, SG_DEBUG, "Done with wgs84 node mapping"); + TG_LOG(SG_GENERAL, SG_DEBUG, " center = " << gbs_center << " radius = " << gbs_radius ); + + // null structures + std::string objpath = root + "/AirportObj"; + std::string name = icao + ".btg"; + + obj.set_gbs_center( gbs_center ); + obj.set_gbs_radius( gbs_radius ); + obj.set_wgs84_nodes( wgs84_nodes ); + obj.set_normals( normals.get_list() ); + obj.set_texcoords( texcoords.get_list() ); + + 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 // TODO : along with taxiway signs + // 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 ); + } +#endif + + SGGeod ref_geod; + // calc elevations and write out windsock references + TG_LOG(SG_GENERAL, SG_DEBUG, "Computing windsock node elevations"); + + for ( unsigned int i = 0; i < windsocks.size(); ++i ) + { + SGGeod ref_geod = windsocks[i]->GetLoc(); + ref_geod.setElevationM( calc_elevation( apt_surf, ref_geod, 0.0 ) ); + + if ( windsocks[i]->IsLit() ) + { + write_index_shared( objpath, b, ref_geod, + "Models/Airport/windsock_lit.xml", 0.0 ); + } + else + { + write_index_shared( objpath, b, ref_geod, + "Models/Airport/windsock.xml", 0.0 ); + } + } + + // write out beacon references + for ( unsigned int i = 0; i < beacons.size(); ++i ) + { + ref_geod = beacons[i]->GetLoc(); + ref_geod.setElevationM( calc_elevation( apt_surf, ref_geod, 0.0 ) ); + + write_index_shared( objpath, b, ref_geod, + "Models/Airport/beacon.xml", + 0.0 ); + } + + // write out taxiway signs references + for ( unsigned int i = 0; i < signs.size(); ++i ) + { + ref_geod = signs[i]->GetLoc(); + ref_geod.setElevationM( calc_elevation( apt_surf, ref_geod, 0.0 ) ); + write_object_sign( objpath, b, ref_geod, + signs[i]->GetDefinition(), + signs[i]->GetHeading(), + signs[i]->GetSize() ); + } + + // write out water buoys + for ( unsigned int i = 0; i < waterrunways.size(); ++i ) + { + tgContour buoys = waterrunways[i]->GetBuoys(); + + for ( unsigned int j = 0; j < buoys.GetSize(); ++j ) + { + ref_geod = buoys.GetNode(j); + ref_geod.setElevationM( calc_elevation( apt_surf, ref_geod, 0.0 ) ); + write_index_shared( objpath, b, ref_geod, + "Models/Airport/water_rw_buoy.xml", + 0.0 ); + } + } + + std::string holepath = root + "/AirportArea"; + tgChopper chopper( holepath ); + + divided_base.SetPreserve3D( true ); + apt_clearing.SetPreserve3D( false ); + apt_clearing.SetTexMethod( TG_TEX_BY_GEODE ); + + chopper.Add( divided_base, "Hole" ); + chopper.Add( apt_clearing, "Airport" ); + chopper.Save( false ); +} diff --git a/src/BuildTiles/Main/tgconstruct_output.cxx b/src/BuildTiles/Main/tgconstruct_output.cxx index 296e2aa9..32badd2d 100644 --- a/src/BuildTiles/Main/tgconstruct_output.cxx +++ b/src/BuildTiles/Main/tgconstruct_output.cxx @@ -1,252 +1,213 @@ -// tgconstruct_output.cxx --Handle writing out the btg and stg files -// -// Written by Curtis Olson, started May 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. -// -// $Id: construct.cxx,v 1.4 2004-11-19 22:25:49 curt Exp $ - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#include <simgear/math/SGGeometry.hxx> -#include <simgear/misc/sg_dir.hxx> -#include <simgear/io/sg_binobj.hxx> -#include <simgear/structure/exception.hxx> -#include <simgear/debug/logstream.hxx> - -#include <terragear/tg_unique_vec3f.hxx> -#include <terragear/tg_unique_vec2f.hxx> - -#include "tgconstruct.hxx" - -using std::string; - -// collect custom objects and move to scenery area -void TGConstruct::AddCustomObjects( void ) { - // Create/open the output .stg file for writing - SGPath dest_d(output_base.c_str()); - dest_d.append(bucket.gen_base_path().c_str()); - string dest_dir = dest_d.str_native(); - SGPath dest_i(dest_d); - dest_i.append(bucket.gen_index_str()); - dest_i.concat(".stg"); - string dest_ind = dest_i.str_native(); - - FILE *fp; - - lock->lock(); - if ( (fp = fopen( dest_ind.c_str(), "w" )) == NULL ) { - SG_LOG( SG_GENERAL, SG_ALERT, "ERROR: opening " << dest_ind << " for writing!" ); - exit(-1); - } - - // Start with the default custom object which is the base terrain - fprintf(fp, "OBJECT_BASE %s.btg\n", bucket.gen_index_str().c_str()); - - char line[2048]; // big enough? - char token[256]; - char name[256]; - - for ( int i = 0; i < (int)load_dirs.size(); ++i ) { - SGPath base(work_base.c_str()); - base.append(load_dirs[i]); - base.append( bucket.gen_base_path() ); - SGPath index(base); - index.append( bucket.gen_index_str() ); - index.concat(".ind"); - string index_file = index.str_native(); - - sg_gzifstream in( index_file ); - - if ( ! in.is_open() ) { - //No custom objects - } else { - while ( ! in.eof() ) { - SG_LOG( SG_GENERAL, SG_DEBUG, "Collecting custom objects from " << index_file ); - in.getline(line, 2048); - SG_LOG( SG_GENERAL, SG_DEBUG, "line = " << line ); - - int result = sscanf( line, "%s %s", token, name ); - SG_LOG( SG_GENERAL, SG_DEBUG, "scanf scanned " << result << " tokens" ); - - if ( result > 0 ) { - SG_LOG( SG_GENERAL, SG_DEBUG, "token = " << token << " name = " << name ); - - if ( strcmp( token, "OBJECT" ) == 0 ) { - SGPath srcbase(base); - srcbase.append(name); - srcbase.concat(".gz"); - string basecom = srcbase.str_native(); -#ifdef _MSC_VER - string command = "copy " + basecom + " " + dest_dir; -#else - string command = "cp " + basecom + " " + dest_dir; -#endif - SG_LOG( SG_GENERAL, SG_DEBUG, "running " << command ); - - if ( system( command.c_str() ) != -1 ) { - fprintf(fp, "OBJECT %s\n", name); - } else { - SG_LOG( SG_GENERAL, SG_ALERT, "Could not issue command " << command ); - } - } else { - fprintf(fp, "%s\n", line); - } - } - } - } - } - - fclose(fp); - - lock->unlock(); -} - -void TGConstruct::WriteBtgFile( void ) -{ - UniqueSGVec3fSet normals; - UniqueSGVec2fSet texcoords; - - 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; - - for (unsigned int area = 0; area < area_defs.size(); area++) { - // only tesselate non holes - if ( !area_defs.is_hole_area(area) ) { - for (unsigned int p = 0; p < polys_clipped.area_size(area); p++ ) { - SG_LOG( SG_CLIPPER, SG_DEBUG, "Ouput nodes for " << area_defs.get_area_name(area) << ":" << p+1 << " of " << polys_clipped.area_size(area) ); - - tgPolygon poly = polys_clipped.get_poly(area, p); - string material = poly.GetMaterial(); - - for (unsigned int k = 0; k < poly.Triangles(); ++k) { - tri_v.clear(); - tri_n.clear(); - tri_tc.clear(); - for (int l = 0; l < 3; ++l) { - index = poly.GetTriIdx( k, l ); - tri_v.push_back( index ); - - // add the node's normal - index = normals.add( nodes.GetNormal( index ) ); - tri_n.push_back( index ); - - index = texcoords.add( poly.GetTriTexCoord( k, l ) ); - 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 ); - } - } - } - } - - std::vector< SGVec3d > wgs84_nodes; - nodes.get_wgs84_nodes( wgs84_nodes ); - SGVec3d gbs_center = SGVec3d::fromGeod( bucket.get_center() ); - double dist_squared, radius_squared = 0; - for (int i = 0; i < (int)wgs84_nodes.size(); ++i) - { - dist_squared = distSqr(gbs_center, wgs84_nodes[i]); - if ( dist_squared > radius_squared ) { - radius_squared = dist_squared; - } - } - double gbs_radius = sqrt(radius_squared); - - 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 base = output_base; - string binname = bucket.gen_index_str(); - binname += ".btg"; - string txtname = bucket.gen_index_str(); - txtname += ".txt"; - - SGBinObject obj; - - obj.set_gbs_center( gbs_center ); - obj.set_gbs_radius( gbs_radius ); - obj.set_wgs84_nodes( wgs84_nodes ); - obj.set_normals( normals.get_list() ); - obj.set_texcoords( texcoords.get_list() ); - 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; - - lock->lock(); - result = obj.write_bin( base, binname, bucket ); - lock->unlock(); - - if ( !result ) - { - throw sg_exception("error writing file. :-("); - } - if (debug_all || debug_shapes.size()) - { - lock->lock(); - result = obj.write_ascii( base, txtname, bucket ); - lock->unlock(); - - if ( !result ) - { - throw sg_exception("error writing file. :-("); - } - } -} +// tgconstruct_output.cxx --Handle writing out the btg and stg files +// +// Written by Curtis Olson, started May 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id: construct.cxx,v 1.4 2004-11-19 22:25:49 curt Exp $ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <simgear/math/SGGeometry.hxx> +#include <simgear/misc/sg_dir.hxx> +#include <simgear/io/sg_binobj.hxx> +#include <simgear/structure/exception.hxx> +#include <simgear/debug/logstream.hxx> + +#include <terragear/tg_unique_vec3f.hxx> +#include <terragear/tg_unique_vec2f.hxx> + +#include "tgconstruct.hxx" + +using std::string; + +// collect custom objects and move to scenery area +void TGConstruct::AddCustomObjects( void ) { + // Create/open the output .stg file for writing + SGPath dest_d(output_base.c_str()); + dest_d.append(bucket.gen_base_path().c_str()); + string dest_dir = dest_d.str_native(); + SGPath dest_i(dest_d); + dest_i.append(bucket.gen_index_str()); + dest_i.concat(".stg"); + string dest_ind = dest_i.str_native(); + + FILE *fp; + + lock->lock(); + if ( (fp = fopen( dest_ind.c_str(), "w" )) == NULL ) { + SG_LOG( SG_GENERAL, SG_ALERT, "ERROR: opening " << dest_ind << " for writing!" ); + exit(-1); + } + + // Start with the default custom object which is the base terrain + fprintf(fp, "OBJECT_BASE %s.btg\n", bucket.gen_index_str().c_str()); + + char line[2048]; // big enough? + char token[256]; + char name[256]; + + for ( int i = 0; i < (int)load_dirs.size(); ++i ) { + SGPath base(work_base.c_str()); + base.append(load_dirs[i]); + base.append( bucket.gen_base_path() ); + SGPath index(base); + index.append( bucket.gen_index_str() ); + index.concat(".ind"); + string index_file = index.str_native(); + + sg_gzifstream in( index_file ); + + if ( ! in.is_open() ) { + //No custom objects + } else { + while ( ! in.eof() ) { + SG_LOG( SG_GENERAL, SG_DEBUG, "Collecting custom objects from " << index_file ); + in.getline(line, 2048); + SG_LOG( SG_GENERAL, SG_DEBUG, "line = " << line ); + + int result = sscanf( line, "%s %s", token, name ); + SG_LOG( SG_GENERAL, SG_DEBUG, "scanf scanned " << result << " tokens" ); + + if ( result > 0 ) { + SG_LOG( SG_GENERAL, SG_DEBUG, "token = " << token << " name = " << name ); + + if ( strcmp( token, "OBJECT" ) == 0 ) { + SGPath srcbase(base); + srcbase.append(name); + srcbase.concat(".gz"); + string basecom = srcbase.str_native(); +#ifdef _MSC_VER + string command = "copy " + basecom + " " + dest_dir; +#else + string command = "cp " + basecom + " " + dest_dir; +#endif + SG_LOG( SG_GENERAL, SG_DEBUG, "running " << command ); + + if ( system( command.c_str() ) != -1 ) { + fprintf(fp, "OBJECT %s\n", name); + } else { + SG_LOG( SG_GENERAL, SG_ALERT, "Could not issue command " << command ); + } + } else { + fprintf(fp, "%s\n", line); + } + } + } + } + } + + fclose(fp); + + lock->unlock(); +} + +void TGConstruct::WriteBtgFile( void ) +{ + UniqueSGVec3fSet normals; + UniqueSGVec2fSet texcoords; + + std::vector< SGVec3d > wgs84_nodes; + nodes.get_wgs84_nodes( wgs84_nodes ); + + SGVec3d gbs_center = SGVec3d::fromGeod( bucket.get_center() ); + double dist_squared, radius_squared = 0; + for (int i = 0; i < (int)wgs84_nodes.size(); ++i) + { + dist_squared = distSqr(gbs_center, wgs84_nodes[i]); + if ( dist_squared > radius_squared ) { + radius_squared = dist_squared; + } + } + double gbs_radius = sqrt(radius_squared); + + 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 ); + + string base = output_base; + string binname = bucket.gen_index_str(); + binname += ".btg"; + string txtname = bucket.gen_index_str(); + txtname += ".txt"; + + SGBinObject obj; + + for (unsigned int area = 0; area < area_defs.size(); area++) { + // only output non holes + if ( !area_defs.is_hole_area(area) ) { + for (unsigned int p = 0; p < polys_clipped.area_size(area); p++ ) { + SG_LOG( SG_CLIPPER, SG_DEBUG, "Ouput nodes for " << area_defs.get_area_name(area) << ":" << p+1 << " of " << polys_clipped.area_size(area) ); + + tgPolygon poly = polys_clipped.get_poly(area, p); + string material = poly.GetMaterial(); + SGBinObjectTriangle sgboTri; + + for (unsigned int k = 0; k < poly.Triangles(); ++k) { + sgboTri.clear(); + sgboTri.material = material; + + for (int l = 0; l < 3; ++l) { + int index; + + index = poly.GetTriIdx( k, l ); + sgboTri.v_list.push_back( index ); + + // add the node's normal + index = normals.add( nodes.GetNormal( index ) ); + sgboTri.n_list.push_back( index ); + + index = texcoords.add( poly.GetTriTexCoord( k, l ) ); + sgboTri.tc_list[0].push_back( index ); + } + + obj.add_triangle( sgboTri ); + } + } + } + } + + obj.set_gbs_center( gbs_center ); + obj.set_gbs_radius( gbs_radius ); + obj.set_wgs84_nodes( wgs84_nodes ); + obj.set_normals( normals.get_list() ); + obj.set_texcoords( texcoords.get_list() ); + + bool result; + + lock->lock(); + result = obj.write_bin( base, binname, bucket ); + lock->unlock(); + + if ( !result ) + { + throw sg_exception("error writing file. :-("); + } + if (debug_all || debug_shapes.size()) + { + lock->lock(); + result = obj.write_ascii( base, txtname, bucket ); + lock->unlock(); + + if ( !result ) + { + throw sg_exception("error writing file. :-("); + } + } +}