diff --git a/configure.in b/configure.in index d57e8010..74deeddb 100644 --- a/configure.in +++ b/configure.in @@ -303,6 +303,7 @@ AC_OUTPUT( \ src/Lib/Array/Makefile \ src/Lib/DEM/Makefile \ src/Lib/Geometry/Makefile \ + src/Lib/landcover/Makefile \ src/Lib/Optimize/Makefile \ src/Lib/Polygon/Makefile \ src/Lib/poly2tri/Makefile \ diff --git a/src/BuildTiles/Clipper/Makefile.am b/src/BuildTiles/Clipper/Makefile.am index d357ba1f..33e90341 100644 --- a/src/BuildTiles/Clipper/Makefile.am +++ b/src/BuildTiles/Clipper/Makefile.am @@ -10,6 +10,7 @@ testclipper_LDADD = \ $(top_builddir)/src/Construct/Clipper/libClipper.a \ $(top_builddir)/src/Construct/Triangulate/libTriangulate.a \ $(top_builddir)/src/Lib/Polygon/libPolygon.a \ + $(top_builddir)/src/Lib/landcover/liblandcover.a \ $(top_builddir)/src/Lib/poly2tri/libpoly2tri.a \ -lsgdebug -lsgmisc -lz -lgpc diff --git a/src/BuildTiles/Clipper/clipper.cxx b/src/BuildTiles/Clipper/clipper.cxx index e388c2a0..7e091f88 100644 --- a/src/BuildTiles/Clipper/clipper.cxx +++ b/src/BuildTiles/Clipper/clipper.cxx @@ -146,13 +146,7 @@ bool FGClipper::load_polys(const string& path) { // TEST - Ignore // } else - if ( area < FG_MAX_AREA_TYPES ) { - polys_in.polys[area].push_back(poly); - } else { - FG_LOG( FG_CLIPPER, FG_ALERT, "Polygon type out of range = " - << (int)poly_type); - exit(-1); - } + add_poly(area, poly); // FILE *ofp= fopen("outfile", "w"); // gpc_write_polygon(ofp, &polys.landuse); @@ -161,6 +155,18 @@ bool FGClipper::load_polys(const string& path) { } +void FGClipper::add_poly (int area, const FGPolygon &poly) +{ + if ( area < FG_MAX_AREA_TYPES ) { + polys_in.polys[area].push_back(poly); + } else { + FG_LOG( FG_CLIPPER, FG_ALERT, "Polygon type out of range = " + << area); + exit(-1); + } +} + + // remove any slivers from in polygon and move them to out polygon. void FGClipper::move_slivers( FGPolygon& in, FGPolygon& out ) { cout << "Begin move slivers" << endl; @@ -300,11 +306,28 @@ bool FGClipper::clip_all(const point2d& min, const point2d& max) { // best representation of land vs. ocean. If we have other less // accurate data that spills out into the ocean, we want to just // clip it. - FGPolygon mask; - mask.erase(); + FGPolygon land_mask; + land_mask.erase(); for ( int i = 0; i < (int)polys_in.polys[DefaultArea].size(); ++i ) { - result_union = polygon_union( mask, polys_in.polys[DefaultArea][i] ); - mask = result_union; + result_union = + polygon_union( land_mask, polys_in.polys[DefaultArea][i] ); + land_mask = result_union; + } + + // set up island mask, for cutting holes in lakes + FGPolygon island_mask; + island_mask.erase(); + for ( int i = 0; i < (int)polys_in.polys[IslandArea].size(); ++i ) { + island_mask = + polygon_union( island_mask, polys_in.polys[IslandArea][i] ); + } + + // set up pond mask, for cutting holes in islands + FGPolygon pond_mask; + pond_mask.erase(); + for ( int i = 0; i < (int)polys_in.polys[PondArea].size(); ++i ) { + pond_mask = + polygon_union( pond_mask, polys_in.polys[PondArea][i] ); } // int count = 0; @@ -322,11 +345,19 @@ bool FGClipper::clip_all(const point2d& min, const point2d& max) { if ( i > HoleArea ) { // clip to land mask - tmp = polygon_int( current, mask ); + tmp = polygon_int( current, land_mask ); } else { tmp = current; } + if ( i == LakeArea ) { + // clip against island mask + tmp = polygon_diff( tmp, island_mask ); + } else if ( i == IslandArea ) { + // clip to pond mask + tmp = polygon_diff( tmp, pond_mask ); + } + // clip current polygon against previous higher priority // stuff diff --git a/src/BuildTiles/Clipper/clipper.hxx b/src/BuildTiles/Clipper/clipper.hxx index ad81d8dd..e07d0065 100644 --- a/src/BuildTiles/Clipper/clipper.hxx +++ b/src/BuildTiles/Clipper/clipper.hxx @@ -50,7 +50,8 @@ FG_USING_STD(vector); // typedef gpcpoly_container::const_iterator const_gpcpoly_iterator; -#define FG_MAX_AREA_TYPES 20 +#define FG_MAX_AREA_TYPES 40 // FIXME also defined in + // MergerClipper/clipper.hxx #define EXTRA_SAFETY_CLIP // #define FG_MAX_VERTICES 100000 @@ -84,6 +85,9 @@ public: // Load a polygon definition file bool load_polys(const string& path); + // Add a polygon. + void add_poly(int area, const FGPolygon &poly); + // remove any slivers from in polygon and move them to out // polygon. void move_slivers( FGPolygon& in, FGPolygon& out ); diff --git a/src/BuildTiles/Main/Makefile.am b/src/BuildTiles/Main/Makefile.am index 216af950..f14d8cd5 100644 --- a/src/BuildTiles/Main/Makefile.am +++ b/src/BuildTiles/Main/Makefile.am @@ -13,6 +13,7 @@ fgfs_construct_LDADD = \ $(top_builddir)/src/Lib/Geometry/libGeometry.a \ $(top_builddir)/src/Lib/Optimize/libOptimize.a \ $(top_builddir)/src/Lib/Polygon/libPolygon.a \ + $(top_builddir)/src/Lib/landcover/liblandcover.a \ $(top_builddir)/src/Lib/poly2tri/libpoly2tri.a \ $(top_builddir)/src/Lib/TriangleJRS/libTriangleJRS.a \ -lsgbucket -lsgmath -lsgmisc -lsgdebug -lplibsg -lz -lgpc diff --git a/src/BuildTiles/Main/main.cxx b/src/BuildTiles/Main/main.cxx index 9125ee2a..f49a0bad 100644 --- a/src/BuildTiles/Main/main.cxx +++ b/src/BuildTiles/Main/main.cxx @@ -51,6 +51,7 @@ #include #include #include +#include #include "construct.hxx" @@ -62,8 +63,73 @@ FG_USING_STD(vector); vector load_dirs; +/** + * Translate USGS land cover into TerraGear. + */ +static AreaType translateUSGSCover (int usgs_value) +{ + switch (usgs_value) { + + case 1: // Urban and Built-Up Land + return BuiltUpCover; + case 2: // Dryland Cropland and Pasture + return DryCropPastureCover; + case 3: // Irrigated Cropland and Pasture + return IrrCropPastureCover; + case 4: // Mixed Dryland/Irrigated Cropland and Pasture + return MixedCropPastureCover; + case 5: // Cropland/Grassland Mosaic + return CropGrassCover; + case 6: // Cropland/Woodland Mosaic + return CropWoodCover; + case 7: // Grassland + return GrassCover; + case 8: // Shrubland + return ShrubCover; + case 9: // Mixed Shrubland/Grassland + return ShrubGrassCover; + case 10: // Savanna + return SavannaCover; + case 11: // Deciduous Broadleaf Forest + return DeciduousBroadCover; + case 12: // Deciduous Needleleaf Forest + return DeciduousNeedleCover; + case 13: // Evergreen Broadleaf Forest + return EvergreenBroadCover; + case 14: // Evergreen Needleleaf Forest + return EvergreenNeedleCover; + case 15: // Mixed Forest + return MixedForestCover; + case 16: // Water Bodies + // FIXME: use the type of an adjoining area if possible + // return WaterBodyCover; + return DefaultArea; + case 17: // Herbaceous Wetland + return HerbWetlandCover; + case 18: // Wooded Wetland + return WoodedWetlandCover; + case 19: // Barren or Sparsely Vegetated + return BarrenCover; + case 20: // Herbaceous Tundra + return HerbTundraCover; + case 21: // Wooded Tundra + return WoodedTundraCover; + case 22: // Mixed Tundra + return MixedTundraCover; + case 23: // Bare Ground Tundra + return BareTundraCover; + case 24: // Snow or Ice + return SnowCover; + default: // Unknown + return DefaultArea; + } +} + + // do actual scan of directory and loading of files -int actual_load_polys( const string& dir, FGConstruct& c, FGClipper& clipper ) { +static 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(); @@ -105,9 +171,56 @@ int actual_load_polys( const string& dir, FGConstruct& c, FGClipper& clipper ) { } +// generate polygons from land-cover raster. +static int actual_load_landcover ( LandCover &cover, FGConstruct & c, + FGClipper &clipper ) { + + int count = 0; + double lon, lat; + FGPolygon poly; + + // Get the top corner of the tile + lon = + c.get_bucket().get_center_lon() - (0.5 * c.get_bucket().get_width()); + lat = + c.get_bucket().get_center_lat() - (0.5 * c.get_bucket().get_height()); + + cout << "DPM: tile at " << lon << ',' << lat << endl; + + // FIXME: this may still be wrong + int x_span = int(120 * bucket_span(lat)); // arcsecs of longitude + int y_span = int(120 * FG_BUCKET_SPAN); // arcsecs of latitude + for (int x = 0; x < x_span; x++) { + for (int y = 0; y < y_span; y++) { + double x1 = lon + (x * (1.0/120.0)); + double y1 = lat + (y * (1.0/120.0)); + double x2 = x1 + (1.0/120.0); + double y2 = y1 + (1.0/120.0); + int cover_value = cover.getValue(x1 + (1.0/240.0), y1 + (1.0/240.0)); + cout << " position: " << x1 << ',' << y1 << ',' + << cover.getDescUSGS(cover_value) << endl; + AreaType area = translateUSGSCover(cover_value); + if (area != DefaultArea) { + poly.erase(); + poly.add_node(0, Point3D(x1, y1, 0.0)); + poly.add_node(0, Point3D(x1, y2, 0.0)); + poly.add_node(0, Point3D(x2, y2, 0.0)); + poly.add_node(0, Point3D(x2, y1, 0.0)); + clipper.add_poly(area, poly); + count++; + } + } + } + + return count; +} + // load all 2d polygons matching the specified base path and clip // against each other to resolve any overlaps -int load_polys( FGConstruct& c ) { +static int load_polys( FGConstruct& c ) { + string glc = c.get_work_base(); + glc += "/LC-Global/gusgs2_0ll.img"; + LandCover cover( glc ); FGClipper clipper; string base = c.get_bucket().gen_base_path(); @@ -125,6 +238,9 @@ int load_polys( FGConstruct& c ) { cout << " loaded " << count << " total polys" << endl; } + // Load the land use polygons + count += actual_load_landcover ( cover, c, clipper ); + 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(); @@ -144,7 +260,7 @@ int load_polys( FGConstruct& c ) { // load regular grid of elevation data (dem based), return list of // fitted nodes -int load_dem( FGConstruct& c, FGArray& array) { +static int load_dem( FGConstruct& c, FGArray& array) { point_list result; string base = c.get_bucket().gen_base_path(); @@ -169,14 +285,14 @@ int load_dem( FGConstruct& c, FGArray& array) { // fit dem nodes, return number of fitted nodes -int fit_dem(FGArray& array, int error) { +static int fit_dem(FGArray& array, int error) { return array.fit( error ); } // triangulate the data for each polygon ( first time before splitting ) -void first_triangulate( FGConstruct& c, const FGArray& array, - FGTriangle& t ) { +static void first_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 @@ -196,7 +312,7 @@ void first_triangulate( FGConstruct& c, const FGArray& array, // triangulate the data for each polygon ( second time after splitting // and reassembling ) -void second_triangulate( FGConstruct& c, FGTriangle& t ) { +static void second_triangulate( FGConstruct& c, FGTriangle& t ) { t.rebuild( c ); cout << "done re building node list and polygons" << endl; @@ -463,14 +579,14 @@ static point_list gen_point_normals( FGConstruct& c ) { // generate the flight gear scenery file -void do_output( FGConstruct& c, FGGenOutput& output ) { +static void do_output( FGConstruct& c, FGGenOutput& output ) { output.build( c ); output.write( c ); } // collect custom objects and move to scenery area -void do_custom_objects( const FGConstruct& c ) { +static void do_custom_objects( const FGConstruct& c ) { FGBucket b = c.get_bucket(); for (int i = 0; i < load_dirs.size(); i++) { @@ -515,7 +631,7 @@ void do_custom_objects( const FGConstruct& c ) { } // master construction routine -void construct_tile( FGConstruct& c ) { +static void construct_tile( FGConstruct& c ) { cout << "Construct tile, bucket = " << c.get_bucket() << endl; // fit with ever increasing error tolerance until we produce <= @@ -655,7 +771,7 @@ void construct_tile( FGConstruct& c ) { // display usage and exit -void usage( const string name ) { +static void usage( const string name ) { cout << "Usage: " << name << endl; cout << "[ --output-dir=" << endl; cout << " --work-dir=" << endl; diff --git a/src/Lib/Makefile.am b/src/Lib/Makefile.am index 7a84511f..f953db1a 100644 --- a/src/Lib/Makefile.am +++ b/src/Lib/Makefile.am @@ -2,6 +2,7 @@ SUBDIRS = \ Array \ DEM \ Geometry \ + landcover \ Optimize \ Polygon \ poly2tri \ diff --git a/src/Lib/Polygon/names.cxx b/src/Lib/Polygon/names.cxx index fd5cce60..ca26da3d 100644 --- a/src/Lib/Polygon/names.cxx +++ b/src/Lib/Polygon/names.cxx @@ -21,59 +21,100 @@ // $Id$ #include +#include #include STL_IOSTREAM #include STL_STRING #include "names.hxx" +FG_USING_STD(string); +FG_USING_STD(map); FG_USING_STD(cout); FG_USING_STD(endl); +typedef map area_type_map; +typedef map area_name_map; + +static area_type_map area_types; +static area_name_map area_names; + + +inline static void set_area (const string &name, AreaType type) +{ + area_types[type] = name; + area_names[name] = type; +} + + +static bool _initialized = false; + + +inline static void init () +{ + if (_initialized) + return; + + set_area("SomeSort", SomeSortOfArea); + set_area("Hole", HoleArea); + set_area("Island", IslandArea); + set_area("Pond", PondArea); + set_area("Swamp or Marsh", MarshArea); + set_area("Marsh", MarshArea); + set_area("Lake", LakeArea); + set_area("Lake Dry", DryLakeArea); + set_area("DryLake", DryLakeArea); + set_area("Lake Intermittent", IntLakeArea); + set_area("IntermittentLake", IntLakeArea); + set_area("Reservoir", ReservoirArea); + set_area("Reservoir Intermittent", IntReservoirArea); + set_area("IntermittentReservoir", IntReservoirArea); + set_area("Stream", StreamArea); + set_area("Canal", CanalArea); + set_area("Glacier", GlacierArea); + set_area("Urban", UrbanArea); + set_area("BuiltUpCover", BuiltUpCover); + set_area("DryCropPastureCover", DryCropPastureCover); + set_area("IrrCropPastureCover", IrrCropPastureCover); + set_area("MixedCropPastureCover", MixedCropPastureCover); + set_area("CropGrassCover", CropGrassCover); + set_area("CropWoodCover", CropWoodCover); + set_area("GrassCover", GrassCover); + set_area("ShrubCover", ShrubCover); + set_area("ShrubGrassCover", ShrubGrassCover); + set_area("SavannaCover", SavannaCover); + set_area("DeciduousBroadCover", DeciduousBroadCover); + set_area("DeciduousNeedleCover", DeciduousNeedleCover); + set_area("EvergreenBroadCover", EvergreenBroadCover); + set_area("EvergreenNeedleCover", EvergreenNeedleCover); + set_area("MixedForestCover", MixedForestCover); + set_area("WaterBodyCover", WaterBodyCover); + set_area("HerbWetlandCover", HerbWetlandCover); + set_area("WoodedWetlandCover", WoodedWetlandCover); + set_area("BarrenCover", BarrenCover); + set_area("HerbTundraCover", HerbTundraCover); + set_area("WoodedTundraCover", WoodedTundraCover); + set_area("MixedTundraCover", MixedTundraCover); + set_area("BareTundraCover", BareTundraCover); + set_area("SnowCover", SnowCover); + set_area("Default", DefaultArea); + set_area("Bay Estuary or Ocean", OceanArea); + set_area("Ocean", OceanArea); + set_area("Void Area", VoidArea); + set_area("Null", NullArea); + + _initialized = true; +} + + // return area type from text name -AreaType get_area_type( string area ) { - if ( area == "SomeSort" ) { - return SomeSortOfArea; - } else if ( area == "Hole" ) { - return HoleArea; - } else if ( area == "Island" ) { - return IslandArea; - } else if ( area == "Pond" ) { - return PondArea; - } else if ( (area == "Swamp or Marsh") - || (area == "Marsh") ) { - return MarshArea; - } else if ( area == "Lake" ) { - return LakeArea; - } else if ( (area == "Lake Dry") - || (area == "DryLake") ) { - return DryLakeArea; - } else if ( (area == "Lake Intermittent") - || (area == "IntermittentLake") ) { - return IntLakeArea; - } else if ( area == "Reservoir" ) { - return ReservoirArea; - } else if ( (area == "Reservoir Intermittent") - || (area == "IntermittentReservoir") ) { - return IntReservoirArea; - } else if ( area == "Stream" ) { - return StreamArea; - } else if ( area == "Canal" ) { - return CanalArea; - } else if ( area == "Glacier" ) { - return GlacierArea; - } else if ( area == "Urban" ) { - return UrbanArea; - } else if ( area == "Default" ) { - return DefaultArea; - } else if ( (area == "Bay Estuary or Ocean") - || (area == "Ocean") ) { - return OceanArea; - } else if ( area == "Void Area" ) { - return VoidArea; - } else if ( area == "Null" ) { - return NullArea; +AreaType +get_area_type (const string &area) { + init(); + area_name_map::const_iterator it = area_names.find(area); + if (it != area_names.end()) { + return it->second; } else { cout << "unknown area = '" << area << "'" << endl; // cout << "area = " << area << endl; @@ -87,40 +128,10 @@ AreaType get_area_type( string area ) { // return text from of area name string get_area_name( AreaType area ) { - if ( area == DefaultArea ) { - return "Default"; - } else if ( area == HoleArea ) { - return "Hole"; - } else if ( area == MarshArea ) { - return "Marsh"; - } else if ( area == PondArea ) { - return "Pond"; - } else if ( area == IslandArea ) { - return "Island"; - } else if ( area == LakeArea ) { - return "Lake"; - } else if ( area == DryLakeArea ) { - return "DryLake"; - } else if ( area == IntLakeArea ) { - return "IntermittentLake"; - } else if ( area == ReservoirArea ) { - return "Reservoir"; - } else if ( area == IntReservoirArea ) { - return "IntermittentReservoir"; - } else if ( area == StreamArea ) { - return "Stream"; - } else if ( area == CanalArea ) { - return "Canal"; - } else if ( area == GlacierArea ) { - return "Glacier"; - } else if ( area == UrbanArea ) { - return "Urban"; - } else if ( area == OceanArea ) { - return "Ocean"; - } else if ( area == VoidArea ) { - return "VoidArea"; - } else if ( area == NullArea ) { - return "Null"; + init(); + area_type_map::const_iterator it = area_types.find(area); + if (it != area_types.end()) { + return it->second; } else { cout << "unknown area code = " << (int)area << endl; return "Unknown"; diff --git a/src/Lib/Polygon/names.hxx b/src/Lib/Polygon/names.hxx index 88ec1e0c..7e270bd3 100644 --- a/src/Lib/Polygon/names.hxx +++ b/src/Lib/Polygon/names.hxx @@ -39,19 +39,48 @@ enum AreaType { SomeSortOfArea = 0, HoleArea = 1, PondArea = 2, - IslandArea = 3, - LakeArea = 4, - DryLakeArea = 5, - IntLakeArea = 6, - ReservoirArea = 7, - IntReservoirArea = 8, - StreamArea = 9, - CanalArea = 10, - GlacierArea = 11, - OceanArea = 12, - UrbanArea = 13, - MarshArea = 14, - DefaultArea = 15, + LakeArea = 3, + DryLakeArea = 4, + IntLakeArea = 5, + ReservoirArea = 6, + IntReservoirArea = 7, + StreamArea = 8, + CanalArea = 9, + GlacierArea = 10, + OceanArea = 11, + UrbanArea = 12, + MarshArea = 13, + + // USGS Land Covers + // These are low-priority, since known polygons should always win. + + BuiltUpCover = 14, // Urban and Built-Up Land + DryCropPastureCover = 15, // Dryland Cropland and Pasture + IrrCropPastureCover = 16, // Irrigated Cropland and Pasture + MixedCropPastureCover = 17, // Mixed Dryland/Irrigated Cropland and Pasture + CropGrassCover = 18, // Cropland/Grassland Mosaic + CropWoodCover = 19, // Cropland/Woodland Mosaic + GrassCover = 20, // Grassland + ShrubCover = 21, // Shrubland + ShrubGrassCover = 22, // Mixed Shrubland/Grassland + SavannaCover = 23, // Savanna + DeciduousBroadCover = 24, // Deciduous Broadleaf Forest + DeciduousNeedleCover = 25, // Deciduous Needleleaf Forest + EvergreenBroadCover = 26, // Evergreen Broadleaf Forest + EvergreenNeedleCover = 27, // Evergreen Needleleaf Forest + MixedForestCover = 28, // Mixed Forest + WaterBodyCover = 29, // Water Bodies + HerbWetlandCover = 30, // Herbaceous Wetland + WoodedWetlandCover = 31, // Wooded Wetland + BarrenCover = 32, // Barren or Sparsely Vegetated + HerbTundraCover = 33, // Herbaceous Tundra + WoodedTundraCover = 34, // Wooded Tundra + MixedTundraCover = 35, // Mixed Tundra + BareTundraCover = 36, // Bare Ground Tundra + SnowCover = 37, // Snow or Ice + + IslandArea = 38, + DefaultArea = 39, VoidArea = 9997, NullArea = 9998, UnknownArea = 9999 @@ -59,7 +88,7 @@ enum AreaType { // return area type from text name -AreaType get_area_type( string area ); +AreaType get_area_type( const string &area ); // return text form of area name string get_area_name( AreaType area ); diff --git a/src/Prep/MergerClipper/merger.hxx b/src/Prep/MergerClipper/merger.hxx index 577f1a73..20e1da47 100644 --- a/src/Prep/MergerClipper/merger.hxx +++ b/src/Prep/MergerClipper/merger.hxx @@ -43,7 +43,7 @@ FG_USING_STD(string); FG_USING_STD(vector); -#define FG_MAX_AREA_TYPES 20 +#define FG_MAX_AREA_TYPES 40 // FIXME: also defined in clipper.hxx class FGPolyList { public: