1
0
Fork 0

tgconstruct edge matching with existing terrain

This commit is contained in:
Peter Sadrozinski 2014-04-18 22:21:07 -04:00
parent c7c8c0be82
commit 47db776404
11 changed files with 436 additions and 33 deletions

View file

@ -47,6 +47,7 @@ static void usage( const string name ) {
SG_LOG(SG_GENERAL, SG_ALERT, "[ --output-dir=<directory>");
SG_LOG(SG_GENERAL, SG_ALERT, " --work-dir=<directory>");
SG_LOG(SG_GENERAL, SG_ALERT, " --share-dir=<directory>");
SG_LOG(SG_GENERAL, SG_ALERT, " --match-dir=<directory>");
SG_LOG(SG_GENERAL, SG_ALERT, " --cover=<path to land-cover raster>");
SG_LOG(SG_GENERAL, SG_ALERT, " --tile-id=<id>");
SG_LOG(SG_GENERAL, SG_ALERT, " --min-lon=<degrees>");
@ -63,10 +64,23 @@ static void usage( const string name ) {
exit(-1);
}
void RemoveDuplicateBuckets( std::vector<SGBucket>& keep, std::vector<SGBucket>& remove )
{
for ( unsigned int i=0; i<remove.size(); i++) {
for ( unsigned int j=0; j<keep.size(); j++ ) {
if ( remove[i] == keep[j] ) {
keep.erase( keep.begin()+j );
break;
}
}
}
}
int main(int argc, char **argv) {
string output_dir = ".";
string work_dir = ".";
string share_dir = "";
string match_dir = "";
string cover = "";
string priorities_file = DEFAULT_PRIORITIES_FILE;
string usgs_map_file = DEFAULT_USGS_MAPFILE;
@ -97,6 +111,8 @@ int main(int argc, char **argv) {
work_dir = arg.substr(11);
} else if (arg.find("--share-dir=") == 0) {
share_dir = arg.substr(12);
} else if (arg.find("--match-dir=") == 0) {
match_dir = arg.substr(12);
} else if (arg.find("--tile-id=") == 0) {
tile_id = atol(arg.substr(10).c_str());
} else if ( arg.find("--min-lon=") == 0 ) {
@ -142,6 +158,8 @@ int main(int argc, char **argv) {
SG_LOG(SG_GENERAL, SG_ALERT, "Output directory is " << output_dir);
SG_LOG(SG_GENERAL, SG_ALERT, "Working directory is " << work_dir);
SG_LOG(SG_GENERAL, SG_ALERT, "Shared directory is " << share_dir);
SG_LOG(SG_GENERAL, SG_ALERT, "Match directory is " << match_dir);
if ( tile_id > 0 ) {
SG_LOG(SG_GENERAL, SG_ALERT, "Tile id is " << tile_id);
} else {
@ -167,10 +185,13 @@ int main(int argc, char **argv) {
}
// tile work queue
std::vector<SGBucket> matchList;
std::vector<SGBucket> bucketList;
SGLockedQueue<SGBucket> wq;
SGMutex filelock;
// First generate the workqueue of buckets to construct
// First, check if we want to match existing scenery -
// if we do, generate a list of buckets outside of the bounding box
if (tile_id == -1) {
// build all the tiles in an area
SG_LOG(SG_GENERAL, SG_ALERT, "Building tile(s) within given bounding box");
@ -184,14 +205,37 @@ int main(int argc, char **argv) {
SG_LOG(SG_GENERAL, SG_ALERT, " construction area spans tile boundaries");
sgGetBuckets( min, max, bucketList );
}
if ( match_dir != "" ) {
b_min = b_min.sibling(-1,-1);
b_max = b_max.sibling(1,1);
sgGetBuckets( b_min.get_center(), b_max.get_center(), matchList );
}
} else {
// construct the specified tile
SG_LOG(SG_GENERAL, SG_ALERT, "Building tile " << tile_id);
bucketList.push_back( SGBucket( tile_id ) );
if ( match_dir != "" ) {
SGBucket b_min( SGBucket(tile_id).sibling(-1,-1) );
SGBucket b_max( SGBucket(tile_id).sibling( 1, 1) );
sgGetBuckets( b_min.get_center(), b_max.get_center(), matchList );
}
}
if ( match_dir != "" ) {
RemoveDuplicateBuckets( matchList, bucketList );
// generate the immuatble shared files - when tile matching, we must not
// modify shared edges from an immutable file - new tile will collapse
// triangles as appropriate.
TGConstruct* construct = new TGConstruct( areas, 1, wq, &filelock );
//construct->set_cover( cover );
construct->set_paths( work_dir, share_dir, match_dir, output_dir, load_dirs );
construct->CreateMatchedEdgeFiles( matchList );
delete construct;
}
std::vector<TGConstruct *> constructs;
SGMutex filelock;
/* fill the workqueue */
for (unsigned int i=0; i<bucketList.size(); i++) {
@ -202,7 +246,7 @@ int main(int argc, char **argv) {
for (int i=0; i<num_threads; i++) {
TGConstruct* construct = new TGConstruct( areas, 1, wq, &filelock );
//construct->set_cover( cover );
construct->set_paths( work_dir, share_dir, output_dir, load_dirs );
construct->set_paths( work_dir, share_dir, match_dir, output_dir, load_dirs );
construct->set_options( ignoreLandmass, nudge );
construct->set_debug( debug_dir, debug_area_defs, debug_shape_defs );
constructs.push_back( construct );
@ -235,7 +279,7 @@ int main(int argc, char **argv) {
for (int i=0; i<num_threads; i++) {
TGConstruct* construct = new TGConstruct( areas, 2, wq, &filelock );
//construct->set_cover( cover );
construct->set_paths( work_dir, share_dir, output_dir, load_dirs );
construct->set_paths( work_dir, share_dir, match_dir, output_dir, load_dirs );
construct->set_options( ignoreLandmass, nudge );
construct->set_debug( debug_dir, debug_area_defs, debug_shape_defs );
constructs.push_back( construct );
@ -267,7 +311,7 @@ int main(int argc, char **argv) {
for (int i=0; i<num_threads; i++) {
TGConstruct* construct = new TGConstruct( areas, 3, wq, &filelock );
//construct->set_cover( cover );
construct->set_paths( work_dir, share_dir, output_dir, load_dirs );
construct->set_paths( work_dir, share_dir, match_dir, output_dir, load_dirs );
construct->set_options( ignoreLandmass, nudge );
construct->set_debug( debug_dir, debug_area_defs, debug_shape_defs );
constructs.push_back( construct );

View file

@ -55,9 +55,12 @@ TGConstruct::~TGConstruct() {
}
// TGConstruct: Setup
void TGConstruct::set_paths( const std::string work, const std::string share, const std::string output, const std::vector<std::string> load ) {
void TGConstruct::set_paths( const std::string work, const std::string share,
const std::string match, const std::string output,
const std::vector<std::string> load ) {
work_base = work;
share_base = share;
match_base = match;
output_base = output;
load_dirs = load;
}
@ -93,6 +96,12 @@ void TGConstruct::run()
strcpy( ds_name, "" );
}
if ( stage == 1 ) {
// Matched edge data is generated from a previous scenery build - do this before we
// start the current tile - it can mark edges as immutable
LoadMatchedEdgeFiles();
}
if ( stage > 1 ) {
LoadFromIntermediateFiles( stage-1 );
LoadSharedEdgeData( stage-1 );

View file

@ -83,7 +83,8 @@ public:
#endif
// paths
void set_paths( const std::string work, const std::string share, const std::string output, const std::vector<std::string> load_dirs );
void set_paths( const std::string work, const std::string share, const std::string match,
const std::string output, const std::vector<std::string> load_dirs );
void set_options( bool ignore_lm, double n );
// TODO : REMOVE
@ -98,6 +99,8 @@ public:
// Debug
void set_debug( std::string path, std::vector<std::string> area_defs, std::vector<std::string> shape_defs );
void CreateMatchedEdgeFiles( std::vector<SGBucket>& bucketList );
private:
virtual void run();
@ -107,6 +110,10 @@ private:
// Load Data
void LoadElevationArray( bool add_nodes );
int LoadLandclassPolys( void );
bool CheckMatchingNode( SGGeod& node, bool road, bool fixed );
SGGeod GetNearestNodeLatitude( const SGGeod& node, const std::vector<SGGeod>& selection );
SGGeod GetNearestNodeLongitude( const SGGeod& node, const std::vector<SGGeod>& selection );
// Clip Data
bool ClipLandclassPolys( void );
@ -118,8 +125,11 @@ private:
// Shared edge Matching
void SaveSharedEdgeData( int stage );
void LoadSharedEdgeData( int stage );
void LoadMatchedEdgeFiles();
void LoadNeighboorEdgeDataStage1( SGBucket& b, std::vector<SGGeod>& north, std::vector<SGGeod>& south, std::vector<SGGeod>& east, std::vector<SGGeod>& west );
void LoadNeighboorMatchDataStage1( SGBucket& b, std::vector<SGGeod>& north, std::vector<SGGeod>& south, std::vector<SGGeod>& east, std::vector<SGGeod>& west );
void ReadNeighborFaces( gzFile& fp );
void WriteNeighborFaces( gzFile& fp, const SGGeod& pt ) const;
TGNeighborFaces* AddNeighborFaces( const SGGeod& node );
@ -164,14 +174,16 @@ private:
SGLockedQueue<SGBucket>& workQueue;
unsigned int total_tiles;
unsigned int stage;
std::vector<SGGeod> nm_north, nm_south, nm_east, nm_west;
// path to land-cover file (if any)
std::string cover;
// paths
std::string work_base;
std::string output_base;
std::string share_base;
std::string match_base;
std::string output_base;
std::vector<std::string> load_dirs;

View file

@ -278,6 +278,7 @@ bool TGConstruct::ClipLandclassPolys( void ) {
// Now make sure any newly added intersection nodes are added to the tgnodes
for (unsigned int area = 0; area < area_defs.size(); area++) {
bool isRoad = area_defs.is_road_area( area );
for (unsigned int p = 0; p < polys_clipped.area_size(area); p++ ) {
tgPolygon& poly = polys_clipped.get_poly( area, p );
@ -286,8 +287,12 @@ bool TGConstruct::ClipLandclassPolys( void ) {
for (unsigned int con=0; con < poly.Contours(); con++) {
for (unsigned int n = 0; n < poly.ContourSize( con ); n++) {
// ensure we have all nodes...
TGNode const& node = poly.GetNode( con, n );
nodes.unique_add( node );
SGGeod node = poly.GetNode( con, n );
if ( CheckMatchingNode( node, isRoad, false ) ) {
poly.SetNode( con, n, node );
} else {
poly.DelNode( con, n );
}
}
}
}

View file

@ -56,7 +56,10 @@ void TGConstruct::LoadElevationArray( bool add_nodes ) {
std::vector<SGGeod> const& fit_list = array.get_fitted_list();
for (unsigned int i=0; i<fit_list.size(); i++) {
nodes.unique_add( fit_list[i] );
SGGeod node = fit_list[i];
if ( CheckMatchingNode( node, false, false ) ) {
nodes.unique_add( node );
}
}
}
}

View file

@ -84,23 +84,22 @@ int TGConstruct::LoadLandclassPolys( void ) {
poly.LoadFromGzFile( fp );
area = area_defs.get_area_priority( poly.GetFlag() );
material = area_defs.get_area_name( area );
bool isRoad = area_defs.is_road_area( area );
poly.SetMaterial( material );
poly.SetId( cur_poly_id++ );
if ( poly.Contours() ) {
polys_in.add_poly( area, poly );
total_polys_read++;
// add the nodes
for (unsigned int j=0; j<poly.Contours(); j++) {
for (unsigned int k=0; k<poly.ContourSize(j); k++) {
SGGeod const& node = poly.GetNode( j, k );
for (int k=poly.ContourSize(j)-1; k>= 0; k--) {
SGGeod node = poly.GetNode( j, k );
bool isFixed = poly.GetPreserve3D();
if ( poly.GetPreserve3D() ) {
nodes.unique_add_fixed_elevation( node );
if ( CheckMatchingNode( node, isRoad, isFixed ) ) {
poly.SetNode( j, k, node );
} else {
nodes.unique_add( node );
poly.DelNode( j, k );
}
}
}
@ -111,6 +110,13 @@ int TGConstruct::LoadLandclassPolys( void ) {
tgShapefile::FromPolygon( poly, ds_name, layer, material.c_str() );
}
/* make sure we loaded a valid poly */
poly = tgPolygon::RemoveBadContours(poly);
if ( poly.Contours() ) {
polys_in.add_poly( area, poly );
total_polys_read++;
}
}
}
@ -124,3 +130,112 @@ int TGConstruct::LoadLandclassPolys( void ) {
return total_polys_read;
}
bool TGConstruct::CheckMatchingNode( SGGeod& node, bool road, bool fixed )
{
bool matched = false;
bool added = false;
if ( fixed ) {
node = nodes.unique_add_fixed_elevation( node );
added = true;
} else {
// check mutable edge
if ( !nm_north.empty() ) {
double north_compare = bucket.get_center_lat() + 0.5 * bucket.get_height();
if ( fabs(node.getLatitudeDeg() - north_compare) < SG_EPSILON) {
if ( !road ) {
// node is on the non_mutable northern border - get closest pt
node = GetNearestNodeLongitude( node, nm_north );
SG_LOG(SG_GENERAL, SG_DEBUG, " AddNode: constrained on north border from " << node << " to " << node );
added = true;
} else {
matched = true;
}
}
}
if ( !added && !matched && !nm_south.empty() ) {
double south_compare = bucket.get_center_lat() - 0.5 * bucket.get_height();
if ( fabs(node.getLatitudeDeg() - south_compare) < SG_EPSILON) {
if ( !road ) {
// node is on the non_mutable southern border - get closest pt
node = GetNearestNodeLongitude( node, nm_south );
SG_LOG(SG_GENERAL, SG_DEBUG, " AddNode: constrained on south border from " << node << " to " << node );
added = true;
} else {
matched = true;
}
}
}
if ( !added && !matched && !nm_east.empty() ) {
double east_compare = bucket.get_center_lon() + 0.5 * bucket.get_width();
if ( fabs(node.getLongitudeDeg() - east_compare) < SG_EPSILON) {
if ( !road ) {
// node is on the non_mutable eastern border - get closest pt
node = GetNearestNodeLatitude( node, nm_east );
SG_LOG(SG_GENERAL, SG_DEBUG, " AddNode: constrained on east border from " << node << " to " << node );
added = true;
} else {
matched = true;
}
}
}
if ( !added && !matched && !nm_west.empty() ) {
double west_compare = bucket.get_center_lon() - 0.5 * bucket.get_width();
if ( fabs(node.getLongitudeDeg() - west_compare) < SG_EPSILON) {
if ( !road ) {
// node is on the non_mutable western border - get closest pt
node = GetNearestNodeLatitude( node, nm_west );
SG_LOG(SG_GENERAL, SG_DEBUG, " AddNode: constrained on west border from " << node << " to " << node );
added = true;
} else {
matched = true;
}
}
}
}
if (!added && !matched) {
node = nodes.unique_add( node );
added = true;
}
return added;
}
SGGeod TGConstruct::GetNearestNodeLongitude( const SGGeod& node, const std::vector<SGGeod>& selection )
{
double min_dist = std::numeric_limits<double>::infinity();
double cur_dist;
unsigned int min_idx = 0;
for ( unsigned int i=0; i<selection.size(); i++ ) {
cur_dist = fabs( node.getLongitudeDeg() - selection[i].getLongitudeDeg() );
if ( cur_dist < min_dist ) {
min_dist = cur_dist;
min_idx = i;
}
}
return selection[min_idx];
}
SGGeod TGConstruct::GetNearestNodeLatitude( const SGGeod& node, const std::vector<SGGeod>& selection )
{
double min_dist = std::numeric_limits<double>::infinity();
double cur_dist;
unsigned int min_idx = 0;
for ( unsigned int i=0; i<selection.size(); i++ ) {
cur_dist = fabs( node.getLatitudeDeg() - selection[i].getLatitudeDeg() );
if ( cur_dist < min_dist ) {
min_dist = cur_dist;
min_idx = i;
}
}
return selection[min_idx];
}

View file

@ -27,6 +27,7 @@
#include <iomanip>
#include <simgear/misc/sg_dir.hxx>
#include <simgear/io/sg_binobj.hxx>
#include <simgear/debug/logstream.hxx>
#include <simgear/io/lowlevel.hxx>
@ -34,6 +35,147 @@
using std::string;
void TGConstruct::CreateMatchedEdgeFiles( std::vector<SGBucket>& bucketList )
{
// todo - add to work queue
for ( unsigned int i=0; i<bucketList.size(); i++ ) {
SGBucket b = bucketList[i];
nodes.clear();
// load the .btg from the match directory
SGPath file = match_base + "/" + b.gen_base_path() + "/" + b.gen_index_str() + ".btg.gz";
SGBinObject obj;
obj.read_bin( file.str() );
std::vector<SGVec3d> wgs84_nodes(obj.get_wgs84_nodes() );
string filepath;
std::vector<SGGeod> north, south, east, west;
int nCount;
// read in all of the .btg nodes
for ( unsigned int j=0; j<wgs84_nodes.size(); j++ ) {
SGGeod pos = SGGeod::fromCart( wgs84_nodes[j] + obj.get_gbs_center() );
nodes.unique_add( pos );
}
SG_LOG( SG_GENERAL, SG_DEBUG, "Read " << nodes.size() << " from file " << file.str() );
nodes.init_spacial_query();
nodes.get_geod_edge( b, north, south, east, west );
filepath = share_base + "/match1/" + b.gen_base_path() + "/" + b.gen_index_str() + "_edges";
SGPath file2(filepath);
lock->lock();
file2.create_dir( 0755 );
gzFile fp;
if ( (fp = gzopen( filepath.c_str(), "wb9" )) == NULL ) {
SG_LOG( SG_GENERAL, SG_INFO, "ERROR: opening " << file.str() << " for writing!" );
return;
}
sgClearWriteError();
// north
nCount = north.size();
SG_LOG( SG_GENERAL, SG_DEBUG, "write " << north.size() << " northern nodes to file " << filepath.c_str() );
sgWriteInt( fp, nCount );
for (int i=0; i<nCount; i++) {
sgWriteGeod( fp, north[i] );
}
// south
nCount = south.size();
SG_LOG( SG_GENERAL, SG_DEBUG, "write " << south.size() << " southern nodes to file " << filepath.c_str() );
sgWriteInt( fp, nCount );
for (int i=0; i<nCount; i++) {
sgWriteGeod( fp, south[i] );
}
// east
nCount = east.size();
SG_LOG( SG_GENERAL, SG_DEBUG, "write " << east.size() << " eastern nodes to file " << filepath.c_str() );
sgWriteInt( fp, nCount );
for (int i=0; i<nCount; i++) {
sgWriteGeod( fp, east[i] );
}
// west
nCount = west.size();
SG_LOG( SG_GENERAL, SG_DEBUG, "write " << west.size() << " western nodes to file " << filepath.c_str() );
sgWriteInt( fp, nCount );
for (int i=0; i<nCount; i++) {
sgWriteGeod( fp, west[i] );
}
gzclose(fp);
lock->unlock();
}
}
void TGConstruct::LoadMatchedEdgeFiles()
{
// try to load matched edges - on successful load, the edge is marked immutable
// we need to read just 4 buckets for stage 1 - 1 for each edge
std::vector<SGGeod> north, south, east, west;
SGBucket nb, sb, eb, wb;
// Read Northern tile and add its southern nodes
nb = bucket.sibling(0, 1);
LoadNeighboorMatchDataStage1( nb, north, south, east, west );
if ( !south.empty() ) {
SG_LOG( SG_GENERAL, SG_DEBUG, "Read " << south.size() << " northern matched nodes " );
// Add southern nodes from northern tile
for (unsigned int i=0; i<south.size(); i++) {
nodes.unique_add( south[i] );
nm_north.push_back( south[i] );
}
}
// Read Southern Tile and add its northern nodes
sb = bucket.sibling(0, -1);
LoadNeighboorMatchDataStage1( sb, north, south, east, west );
if ( !north.empty() ) {
SG_LOG( SG_GENERAL, SG_DEBUG, "Read " << north.size() << " southern matched nodes " );
for (unsigned int i=0; i<north.size(); i++) {
nodes.unique_add( north[i] );
nm_south.push_back( north[i] );
}
}
// Read Eastern Tile and add its western nodes
eb = bucket.sibling(1, 0);
LoadNeighboorMatchDataStage1( eb, north, south, east, west );
SG_LOG( SG_GENERAL, SG_DEBUG, "Read " << west.size() << " eastern matched nodes " );
if ( !west.empty() ) {
for (unsigned int i=0; i<west.size(); i++) {
nodes.unique_add( west[i] );
nm_east.push_back( west[i] );
}
}
// Read Western Tile and add its eastern nodes
wb = bucket.sibling(-1, 0);
SG_LOG( SG_GENERAL, SG_DEBUG, "Read " << east.size() << " western matched nodes " );
LoadNeighboorMatchDataStage1( wb, north, south, east, west );
if ( !east.empty() ) {
for (unsigned int i=0; i<east.size(); i++) {
nodes.unique_add( east[i] );
nm_west.push_back( east[i] );
}
}
}
void TGConstruct::SaveSharedEdgeData( int stage )
{
switch( stage ) {
@ -527,6 +669,60 @@ void TGConstruct::LoadNeighboorEdgeDataStage1( SGBucket& b, std::vector<SGGeod>&
}
}
void TGConstruct::LoadNeighboorMatchDataStage1( SGBucket& b, std::vector<SGGeod>& north, std::vector<SGGeod>& south, std::vector<SGGeod>& east, std::vector<SGGeod>& west )
{
string dir;
string file;
gzFile fp;
SGGeod pt;
int nCount;
dir = share_base + "/match1/" + b.gen_base_path();
file = dir + "/" + b.gen_index_str() + "_edges";
fp = gzopen( file.c_str(), "rb" );
north.clear();
south.clear();
east.clear();
west.clear();
if (fp) {
// North
sgReadInt( fp, &nCount );
SG_LOG( SG_CLIPPER, SG_DEBUG, "loading " << nCount << "Points on " << b.gen_index_str() << " north boundary");
for (int i=0; i<nCount; i++) {
sgReadGeod( fp, pt );
north.push_back(pt);
}
// South
sgReadInt( fp, &nCount );
SG_LOG( SG_CLIPPER, SG_DEBUG, "loading " << nCount << "Points on " << b.gen_index_str() << " south boundary");
for (int i=0; i<nCount; i++) {
sgReadGeod( fp, pt );
south.push_back(pt);
}
// East
sgReadInt( fp, &nCount );
SG_LOG( SG_CLIPPER, SG_DEBUG, "loading " << nCount << "Points on " << b.gen_index_str() << " east boundary");
for (int i=0; i<nCount; i++) {
sgReadGeod( fp, pt );
east.push_back(pt);
}
// West
sgReadInt( fp, &nCount );
SG_LOG( SG_CLIPPER, SG_DEBUG, "loading " << nCount << "Points on " << b.gen_index_str() << " west boundary");
for (int i=0; i<nCount; i++) {
sgReadGeod( fp, pt );
west.push_back(pt);
}
gzclose( fp );
}
}
void TGConstruct::LoadFromIntermediateFiles( int stage )
{
string dir;

View file

@ -64,6 +64,7 @@ void TGConstruct::TesselatePolys( void )
}
for (unsigned int area = 0; area < area_defs.size(); area++) {
bool isRoad = area_defs.is_road_area( area );
for (unsigned int p = 0; p < polys_clipped.area_size(area); p++ ) {
tgPolygon& poly = polys_clipped.get_poly(area, p );
@ -71,7 +72,13 @@ void TGConstruct::TesselatePolys( void )
for (unsigned int k=0; k < poly.Triangles(); k++) {
for (int l = 0; l < 3; l++) {
// ensure we have all nodes...
nodes.unique_add( poly.GetTriNode( k, l ) );
SGGeod node = poly.GetTriNode( k, l );
if ( CheckMatchingNode( node, isRoad, false ) ) {
nodes.unique_add( node );
} else {
SG_LOG( SG_GENERAL, SG_INFO, "after tesselation, we can't add a node - quit");
exit(1);
}
}
}
}

View file

@ -54,6 +54,9 @@ public:
void SetNode( unsigned int i, SGGeod n ) {
node_list[i] = n;
}
void DelNode( unsigned int i ) {
node_list.erase( node_list.begin()+i);
}
SGGeod GetNode( unsigned int i ) const {
return node_list[i];
}

View file

@ -68,26 +68,32 @@ public:
// Add a point to the point list if it doesn't already exist.
// Returns the index (starting at zero) of the point in the list.
void unique_add( const SGGeod& p ) {
SGGeod unique_add( const SGGeod& p ) {
TGNode n(p);
n.SetFixedPosition(false);
tg_node_list.add(n);
unsigned idx = tg_node_list.add(n);
kd_tree_valid = false;
return tg_node_list[idx].GetPosition();
}
void unique_add( const TGNode& n ) {
tg_node_list.add(n);
SGGeod unique_add( const TGNode& n ) {
unsigned int idx = tg_node_list.add(n);
kd_tree_valid = false;
return tg_node_list[idx].GetPosition();
}
// Add a point to the point list if it doesn't already exist
// (checking all three dimensions.) Returns the index (starting
// at zero) of the point in the list.
void unique_add_fixed_elevation( const SGGeod& p ) {
SGGeod unique_add_fixed_elevation( const SGGeod& p ) {
TGNode n(p);
n.SetFixedPosition(true);
tg_node_list.add(n);
unsigned int idx = tg_node_list.add(n);
kd_tree_valid = false;
return tg_node_list[idx].GetPosition();
}
// Find the index of the specified point (compair to the same

View file

@ -264,6 +264,9 @@ public:
contours[c].AddNode( n );
}
void DelNode( unsigned int c, unsigned int n ) {
contours[c].DelNode( n );
}
tgRectangle GetBoundingBox( void ) const;