363 lines
10 KiB
C++
363 lines
10 KiB
C++
// main.cxx -- top level construction routines
|
|
//
|
|
// Written by Curtis Olson, started March 1999.
|
|
//
|
|
// Copyright (C) 1999 Curtis L. Olson - curt@flightgear.org
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License as
|
|
// published by the Free Software Foundation; either version 2 of the
|
|
// License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful, but
|
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
// General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, write to the Free Software
|
|
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
//
|
|
// $Id$
|
|
|
|
|
|
#include <sys/types.h> // for directory reading
|
|
#include <dirent.h> // for directory reading
|
|
|
|
#include <Bucket/newbucket.hxx>
|
|
#include <Include/fg_constants.h>
|
|
|
|
#include <Debug/logstream.hxx>
|
|
#include <Array/array.hxx>
|
|
#include <Clipper/clipper.hxx>
|
|
#include <GenOutput/genobj.hxx>
|
|
#include <Triangulate/triangle.hxx>
|
|
|
|
#include "construct.hxx"
|
|
|
|
|
|
// do actual scan of directory and loading of files
|
|
int actual_load_polys( const string& dir, FGConstruct& c, FGClipper& clipper ) {
|
|
int counter = 0;
|
|
string base = c.get_bucket().gen_base_path();
|
|
string tile_str = c.get_bucket().gen_index_str();
|
|
string ext;
|
|
|
|
DIR *d;
|
|
struct dirent *de;
|
|
|
|
if ( (d = opendir( dir.c_str() )) == NULL ) {
|
|
cout << "cannot open directory " << dir << "\n";
|
|
return 0;
|
|
}
|
|
|
|
// load all matching polygon files
|
|
string file, f_index, full_path;
|
|
int pos;
|
|
while ( (de = readdir(d)) != NULL ) {
|
|
file = de->d_name;
|
|
pos = file.find(".");
|
|
f_index = file.substr(0, pos);
|
|
|
|
if ( tile_str == f_index ) {
|
|
ext = file.substr(pos + 1);
|
|
cout << file << " " << f_index << " '" << ext << "'" << endl;
|
|
full_path = dir + "/" + file;
|
|
if ( (ext == "dem") || (ext == "dem.gz") ) {
|
|
// skip
|
|
} else {
|
|
cout << "ext = '" << ext << "'" << endl;
|
|
clipper.load_polys( full_path );
|
|
++counter;
|
|
}
|
|
}
|
|
}
|
|
|
|
return counter;
|
|
}
|
|
|
|
|
|
// load all 2d polygons matching the specified base path and clip
|
|
// against each other to resolve any overlaps
|
|
int load_polys( FGConstruct& c ) {
|
|
FGClipper clipper;
|
|
|
|
string base = c.get_bucket().gen_base_path();
|
|
int result;
|
|
|
|
// initialize clipper
|
|
clipper.init();
|
|
|
|
// load airports
|
|
string poly_path = c.get_work_base() + ".apt" + "/Scenery/" + base;
|
|
cout << "poly_path = " << poly_path << endl;
|
|
result = actual_load_polys( poly_path, c, clipper );
|
|
cout << " loaded " << result << " polys" << endl;
|
|
|
|
// load hydro
|
|
poly_path = c.get_work_base() + ".hydro" + "/Scenery/" + base;
|
|
cout << "poly_path = " << poly_path << endl;
|
|
result = actual_load_polys( poly_path, c, clipper );
|
|
cout << " loaded " << result << " polys" << endl;
|
|
|
|
point2d min, max;
|
|
min.x = c.get_bucket().get_center_lon() - 0.5 * c.get_bucket().get_width();
|
|
min.y = c.get_bucket().get_center_lat() - 0.5 * c.get_bucket().get_height();
|
|
max.x = c.get_bucket().get_center_lon() + 0.5 * c.get_bucket().get_width();
|
|
max.y = c.get_bucket().get_center_lat() + 0.5 * c.get_bucket().get_height();
|
|
|
|
// do clipping
|
|
cout << "clipping polygons" << endl;
|
|
clipper.clip_all(min, max);
|
|
|
|
// update main data repository
|
|
c.set_clipped_polys( clipper.get_polys_clipped() );
|
|
return 1;
|
|
}
|
|
|
|
|
|
// load regular grid of elevation data (dem based), return list of
|
|
// fitted nodes
|
|
int load_dem( FGConstruct& c, FGArray& array) {
|
|
point_list result;
|
|
string base = c.get_bucket().gen_base_path();
|
|
|
|
string dem_path = c.get_work_base() + ".dem" + "/Scenery/" + base
|
|
+ "/" + c.get_bucket().gen_index_str() + ".dem";
|
|
cout << "dem_path = " << dem_path << endl;
|
|
|
|
if ( ! array.open(dem_path) ) {
|
|
cout << "ERROR: cannot open " << dem_path << endl;
|
|
}
|
|
|
|
FGBucket b = c.get_bucket();
|
|
array.parse( b );
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
// fit dem nodes, return number of fitted nodes
|
|
int fit_dem(FGArray& array, int error) {
|
|
return array.fit( error );
|
|
}
|
|
|
|
|
|
// triangulate the data for each polygon
|
|
void do_triangulate( FGConstruct& c, const FGArray& array,
|
|
FGTriangle& t ) {
|
|
// first we need to consolidate the points of the DEM fit list and
|
|
// all the polygons into a more "Triangle" friendly format
|
|
|
|
point_list corner_list = array.get_corner_node_list();
|
|
point_list fit_list = array.get_fit_node_list();
|
|
FGgpcPolyList gpc_polys = c.get_clipped_polys();
|
|
|
|
cout << "ready to build node list and polygons" << endl;
|
|
t.build( corner_list, fit_list, gpc_polys );
|
|
cout << "done building node list and polygons" << endl;
|
|
|
|
cout << "ready to do triangulation" << endl;
|
|
t.run_triangulate();
|
|
cout << "finished triangulation" << endl;
|
|
}
|
|
|
|
|
|
// build the wgs-84 point list
|
|
static point_list gen_wgs84_points( FGConstruct& c, const FGArray& array ) {
|
|
point_list wgs84_nodes;
|
|
cout << "calculating wgs84 point" << endl;
|
|
Point3D geod, radians, cart;
|
|
|
|
point_list geod_nodes = c.get_tri_nodes().get_node_list();
|
|
const_point_list_iterator current = geod_nodes.begin();
|
|
const_point_list_iterator last = geod_nodes.end();
|
|
|
|
double real_z;
|
|
|
|
for ( ; current != last; ++current ) {
|
|
geod = *current;
|
|
|
|
real_z = array.interpolate_altitude( geod.x() * 3600.0,
|
|
geod.y() * 3600.0 );
|
|
|
|
// convert to radians
|
|
radians = Point3D( geod.x() * DEG_TO_RAD,
|
|
geod.y() * DEG_TO_RAD,
|
|
real_z );
|
|
|
|
cart = fgGeodToCart(radians);
|
|
// cout << cart << endl;
|
|
wgs84_nodes.push_back(cart);
|
|
}
|
|
|
|
return wgs84_nodes;
|
|
}
|
|
|
|
|
|
// generate the flight gear scenery file
|
|
void do_output( FGConstruct& c, const FGTriangle& t,
|
|
const FGArray& array, FGGenOutput& output ) {
|
|
output.build( c, array );
|
|
output.write( c );
|
|
}
|
|
|
|
|
|
// master construction routine
|
|
void construct_tile( FGConstruct& c ) {
|
|
cout << "Construct tile, bucket = " << c.get_bucket() << endl;
|
|
|
|
// fit with ever increasing error tolerance until we produce <=
|
|
// 80% of max nodes. We should really have the sim end handle
|
|
// arbitrarily complex tiles.
|
|
|
|
bool acceptable = false;
|
|
double error = 200.0;
|
|
int count = 0;
|
|
|
|
// load and clip 2d polygon data
|
|
load_polys( c );
|
|
|
|
// load grid of elevation data (dem)
|
|
FGArray array;
|
|
load_dem( c, array );
|
|
|
|
FGTriangle t;
|
|
|
|
while ( ! acceptable ) {
|
|
// do a least squares fit of the (dem) data with the given
|
|
// error tolerance
|
|
array.fit( error );
|
|
|
|
// triangulate the data for each polygon
|
|
do_triangulate( c, array, t );
|
|
|
|
acceptable = true;
|
|
|
|
count = t.get_out_nodes_size();
|
|
|
|
if ( (count < c.get_min_nodes()) && (error >= 25.0) ) {
|
|
// reduce error tolerance until number of points exceeds the
|
|
// minimum threshold
|
|
cout << "produced too few nodes ..." << endl;
|
|
|
|
acceptable = false;
|
|
|
|
error /= 1.5;
|
|
cout << "Setting error to " << error << " and retrying fit."
|
|
<< endl;
|
|
}
|
|
|
|
if ( (count > c.get_max_nodes()) && (error <= 1000.0) ) {
|
|
// increase error tolerance until number of points drops below
|
|
// the maximum threshold
|
|
cout << "produced too many nodes ..." << endl;
|
|
|
|
acceptable = false;
|
|
|
|
error *= 1.5;
|
|
cout << "Setting error to " << error << " and retrying fit."
|
|
<< endl;
|
|
}
|
|
}
|
|
|
|
cout << "finished fit with error = " << error << " node count = "
|
|
<< count << endl;
|
|
|
|
// save the results of the triangulation
|
|
c.set_tri_nodes( t.get_out_nodes() );
|
|
c.set_tri_elements( t.get_elelist() );
|
|
|
|
// calculate wgs84 (cartesian) form of node list
|
|
c.set_wgs84_nodes( gen_wgs84_points( c, array ) );
|
|
|
|
// generate the output
|
|
FGGenOutput output;
|
|
do_output( c, t, array, output );
|
|
}
|
|
|
|
|
|
main(int argc, char **argv) {
|
|
double lon, lat;
|
|
|
|
fglog().setLogLevels( FG_ALL, FG_DEBUG );
|
|
|
|
if ( argc != 3 ) {
|
|
cout << "Usage: " << argv[0] << " <work_base> <output_base>" << endl;
|
|
exit(-1);
|
|
}
|
|
|
|
// main construction data management class
|
|
FGConstruct c;
|
|
|
|
c.set_work_base( argv[1] );
|
|
c.set_output_base( argv[2] );
|
|
|
|
c.set_min_nodes( 50 );
|
|
c.set_max_nodes( (int)(FG_MAX_NODES * 0.8) );
|
|
|
|
// lon = -146.248360; lat = 61.133950; // PAVD (Valdez, AK)
|
|
// lon = -110.664244; lat = 33.352890; // P13
|
|
// lon = -93.211389; lat = 45.145000; // KANE
|
|
// lon = -92.486188; lat = 44.590190; // KRGK
|
|
// lon = -89.744682312011719; lat= 29.314495086669922;
|
|
// lon = -122.488090; lat = 42.743183; // 64S
|
|
// lon = -114.861097; lat = 35.947480; // 61B
|
|
// lon = -112.012175; lat = 41.195944; // KOGD
|
|
// lon = -90.757128; lat = 46.790212; // WI32
|
|
// lon = -122.220717; lat = 37.721291; // KOAK
|
|
// lon = -111.721477; lat = 40.215641; // KPVU
|
|
// lon = -122.309313; lat = 47.448982; // KSEA
|
|
// lon = -148.798131; lat = 63.645099; // AK06 (Danali, AK)
|
|
// lon = -92.5; lat = 47.5; // Marsh test (northern MN)
|
|
// lon = -111.977773; lat = 40.788388; // KSLC
|
|
// lon = -121.914; lat = 42.5655; // TEST (Oregon SW of Crater)
|
|
lon = -76.201239; lat = 36.894606; // KORF (Norfolk, Virginia)
|
|
|
|
double min_x = lon - 3;
|
|
double min_y = lat - 1;
|
|
FGBucket b_min( min_x, min_y );
|
|
FGBucket b_max( lon + 3, lat + 1 );
|
|
|
|
FGBucket b_start(1662962L);
|
|
bool do_tile = false;
|
|
|
|
// FGBucket b_omit(-1L);
|
|
// FGBucket b(1122504L);
|
|
FGBucket b(-146.248360, 61.133950);
|
|
c.set_bucket( b );
|
|
construct_tile( c );
|
|
exit(0);
|
|
|
|
if ( b_min == b_max ) {
|
|
c.set_bucket( b_min );
|
|
construct_tile( c );
|
|
} else {
|
|
FGBucket b_cur;
|
|
int dx, dy, i, j;
|
|
|
|
fgBucketDiff(b_min, b_max, &dx, &dy);
|
|
cout << " construction area spans tile boundaries" << endl;
|
|
cout << " dx = " << dx << " dy = " << dy << endl;
|
|
|
|
for ( j = 0; j <= dy; j++ ) {
|
|
for ( i = 0; i <= dx; i++ ) {
|
|
b_cur = fgBucketOffset(min_x, min_y, i, j);
|
|
|
|
if ( b_cur == b_start ) {
|
|
do_tile = true;
|
|
}
|
|
|
|
if ( do_tile ) {
|
|
c.set_bucket( b_cur );
|
|
construct_tile( c );
|
|
} else {
|
|
cout << "skipping " << b_cur << endl;
|
|
}
|
|
}
|
|
}
|
|
// string answer; cin >> answer;
|
|
}
|
|
}
|
|
|
|
|