Added cliff-decode source and make instructions.
This commit is contained in:
parent
09d61732cd
commit
e2a6d06de1
2 changed files with 448 additions and 0 deletions
14
src/Prep/Cliff/CMakeLists.txt
Normal file
14
src/Prep/Cliff/CMakeLists.txt
Normal file
|
@ -0,0 +1,14 @@
|
|||
include_directories(${GDAL_INCLUDE_DIR})
|
||||
add_executable(cliff-decode cliff-decode.cxx)
|
||||
|
||||
target_link_libraries(cliff-decode
|
||||
${GDAL_LIBRARY}
|
||||
terragear
|
||||
${ZLIB_LIBRARY}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${SIMGEAR_CORE_LIBRARIES}
|
||||
${SIMGEAR_CORE_LIBRARY_DEPENDENCIES}
|
||||
${Boost_LIBRARIES}
|
||||
)
|
||||
|
||||
install(TARGETS cliff-decode RUNTIME DESTINATION bin)
|
434
src/Prep/Cliff/cliff-decode.cxx
Normal file
434
src/Prep/Cliff/cliff-decode.cxx
Normal file
|
@ -0,0 +1,434 @@
|
|||
// cliff-decode.cxx -- process OGR layers and extract lines
|
||||
// representing discontinuities, clipping
|
||||
// against and sorting
|
||||
// them into the relevant tiles.
|
||||
//
|
||||
// Written by James Hester starting 2018
|
||||
//
|
||||
// Based on ogr-decode by Ralf Gerlich.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include <boost/thread.hpp>
|
||||
#include <ogrsf_frmts.h>
|
||||
#include <gdal_priv.h>
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear/threads/SGThread.hxx>
|
||||
#include <simgear/threads/SGQueue.hxx>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/math/sg_geodesy.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
|
||||
#include <Include/version.h>
|
||||
|
||||
#include <terragear/tg_chopper.hxx>
|
||||
#include <terragear/tg_shapefile.hxx>
|
||||
|
||||
using std::string;
|
||||
|
||||
int continue_on_errors=0;
|
||||
int num_threads=1;
|
||||
bool use_attribute_query=false;
|
||||
string attribute_query;
|
||||
int start_record=0;
|
||||
bool use_spatial_query=false;
|
||||
int seperate_segments = 0;
|
||||
double spat_min_x, spat_min_y, spat_max_x, spat_max_y;
|
||||
bool save_shapefiles=false;
|
||||
SGLockedQueue<OGRFeature *> global_workQueue;
|
||||
|
||||
class Decoder : public SGThread
|
||||
{
|
||||
public:
|
||||
Decoder( OGRCoordinateTransformation *poct, tgChopper& c ) : chopper(c) {
|
||||
poCT = poct;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void run();
|
||||
|
||||
void processLineString(OGRLineString* poGeometry);
|
||||
|
||||
private:
|
||||
// The transformation for each geometry object
|
||||
OGRCoordinateTransformation *poCT;
|
||||
|
||||
// Store the reults per tile
|
||||
tgChopper& chopper;
|
||||
|
||||
};
|
||||
|
||||
void Decoder::processLineString(OGRLineString* poGeometry)
|
||||
{
|
||||
tgContour line;
|
||||
|
||||
SGGeod p0, p1;
|
||||
double heading, dist, az2;
|
||||
int i, j, numPoints, numSegs;
|
||||
double max_dist;
|
||||
|
||||
numPoints = poGeometry->getNumPoints();
|
||||
if (numPoints < 2) {
|
||||
SG_LOG( SG_GENERAL, SG_WARN, "Skipping line with less than two points" );
|
||||
return;
|
||||
}
|
||||
|
||||
heading = SGGeodesy::courseDeg( p1, p0 );
|
||||
|
||||
// now add the middle points : if they are too far apart, add intermediate nodes
|
||||
for ( i=0;i<numPoints;i++) {
|
||||
p0 = SGGeod::fromDeg( poGeometry->getX(i), poGeometry->getY(i) );
|
||||
line.AddNode( p0 );
|
||||
}
|
||||
|
||||
// Do not close this contour
|
||||
line.SetOpen(true);
|
||||
|
||||
// clip the contour. Do not use usual clipper, as we wish to preserve the
|
||||
// whole contour, even if it goes outside the bounding box, as height
|
||||
// calculations along the boundary might be affected by it.
|
||||
tgPolygon open_poly;
|
||||
open_poly.SetOpen(); //do not try to close this one up
|
||||
open_poly.AddContour(line);
|
||||
chopper.Add( open_poly, "Cliffs" );
|
||||
}
|
||||
|
||||
void Decoder::run()
|
||||
{
|
||||
// as long as we have geometry to parse, do so
|
||||
while (!global_workQueue.empty()) {
|
||||
OGRFeature *poFeature = global_workQueue.pop();
|
||||
SG_LOG( SG_GENERAL, SG_BULK, " remaining features is " << global_workQueue.size() );
|
||||
|
||||
if ( poFeature ) {
|
||||
OGRGeometry *poGeometry = poFeature->GetGeometryRef();
|
||||
|
||||
if (poGeometry==NULL) {
|
||||
SG_LOG( SG_GENERAL, SG_INFO, "Found feature without geometry!" );
|
||||
if (!continue_on_errors) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Aborting!" );
|
||||
exit( 1 );
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
OGRwkbGeometryType geoType=wkbFlatten(poGeometry->getGeometryType());
|
||||
if (geoType!=wkbLineString && geoType!=wkbMultiLineString) {
|
||||
SG_LOG( SG_GENERAL, SG_INFO, "Non-line feature" );
|
||||
continue;
|
||||
}
|
||||
|
||||
poGeometry->transform( poCT );
|
||||
|
||||
switch (geoType) {
|
||||
case wkbLineString: {
|
||||
SG_LOG( SG_GENERAL, SG_DEBUG, "LineString feature" );
|
||||
processLineString((OGRLineString*)poGeometry);
|
||||
break;
|
||||
}
|
||||
case wkbMultiLineString: {
|
||||
SG_LOG( SG_GENERAL, SG_DEBUG, "MultiLineString feature" );
|
||||
OGRMultiLineString* multilines=(OGRMultiLineString*)poGeometry;
|
||||
for (int i=0;i<multilines->getNumGeometries();i++) {
|
||||
processLineString((OGRLineString*)(multilines->getGeometryRef(i)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
/* Ignore unhandled objects */
|
||||
break;
|
||||
}
|
||||
|
||||
OGRFeature::DestroyFeature( poFeature );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Main Thread
|
||||
void processLayer(OGRLayer* poLayer, tgChopper& results )
|
||||
{
|
||||
int feature_count=poLayer->GetFeatureCount();
|
||||
|
||||
if (feature_count!=-1 && start_record>0 && start_record>=feature_count) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Layer has only " << feature_count << " records, but start record is set to " << start_record );
|
||||
exit( 1 );
|
||||
}
|
||||
|
||||
/* determine the indices of the required columns */
|
||||
OGRFeatureDefn *poFDefn = poLayer->GetLayerDefn();
|
||||
string layername=poFDefn->GetName();
|
||||
|
||||
/* setup a transformation to WGS84 */
|
||||
OGRSpatialReference *oSourceSRS, oTargetSRS;
|
||||
oSourceSRS=poLayer->GetSpatialRef();
|
||||
if (oSourceSRS == NULL) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Layer " << layername << " has no defined spatial reference system" );
|
||||
exit( 1 );
|
||||
}
|
||||
|
||||
char* srsWkt;
|
||||
oSourceSRS->exportToWkt(&srsWkt);
|
||||
SG_LOG( SG_GENERAL, SG_DEBUG, "Source spatial reference system: " << srsWkt );
|
||||
OGRFree(srsWkt);
|
||||
|
||||
oTargetSRS.SetWellKnownGeogCS( "WGS84" );
|
||||
|
||||
OGRCoordinateTransformation *poCT = OGRCreateCoordinateTransformation(oSourceSRS, &oTargetSRS);
|
||||
|
||||
/* setup attribute and spatial queries */
|
||||
if (use_spatial_query) {
|
||||
double trans_min_x,trans_min_y,trans_max_x,trans_max_y;
|
||||
/* do a simple reprojection of the source SRS */
|
||||
OGRCoordinateTransformation *poCTinverse;
|
||||
|
||||
poCTinverse = OGRCreateCoordinateTransformation(&oTargetSRS, oSourceSRS);
|
||||
|
||||
trans_min_x=spat_min_x;
|
||||
trans_min_y=spat_min_y;
|
||||
trans_max_x=spat_max_x;
|
||||
trans_max_y=spat_max_y;
|
||||
|
||||
poCTinverse->Transform(1,&trans_min_x,&trans_min_y);
|
||||
poCTinverse->Transform(1,&trans_max_x,&trans_max_y);
|
||||
|
||||
poLayer->SetSpatialFilterRect(trans_min_x, trans_min_y,
|
||||
trans_max_x, trans_max_y);
|
||||
}
|
||||
|
||||
if (use_attribute_query) {
|
||||
if (poLayer->SetAttributeFilter(attribute_query.c_str()) != OGRERR_NONE) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Error in query expression '" << attribute_query << "'" );
|
||||
exit( 1 );
|
||||
}
|
||||
}
|
||||
|
||||
// Generate the work queue for this layer
|
||||
OGRFeature *poFeature;
|
||||
poLayer->SetNextByIndex(start_record);
|
||||
while ( ( poFeature = poLayer->GetNextFeature()) != NULL )
|
||||
{
|
||||
global_workQueue.push( poFeature );
|
||||
}
|
||||
|
||||
// Now process the workqueue with threads
|
||||
std::vector<Decoder *> decoders;
|
||||
for (int i=0; i<num_threads; i++) {
|
||||
Decoder* decoder = new Decoder( poCT, results );
|
||||
decoder->start();
|
||||
decoders.push_back( decoder );
|
||||
}
|
||||
|
||||
// Then wait until they are finished
|
||||
for (unsigned int i=0; i<decoders.size(); i++) {
|
||||
decoders[i]->join();
|
||||
}
|
||||
|
||||
OCTDestroyCoordinateTransformation ( poCT );
|
||||
}
|
||||
|
||||
void usage(char* progname) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Usage: " <<
|
||||
progname << " [options...] <work_dir> <datasource> [<layername>...]" );
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Options:" );
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "--log-level priority" );
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, " Width in priority being bulk|debug|info|warn|alert" );
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "--continue-on-errors" );
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, " Continue even if the file seems fishy" );
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "--max-segment max_segment_length" );
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, " Maximum segment length in meters" );
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "--start-record record_no" );
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, " Start processing at the specified record number (first record num=0)" );
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "--where attrib_query" );
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, " Use an attribute query (like SQL WHERE)" );
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "--spat xmin ymin xmax ymax" );
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, " spatial query extents" );
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "--threads" );
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, " Enable multithreading with user specified number of threads" );
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "--all-threads" );
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, " Enable multithreading with all available cpu cores" );
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "" );
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "<work_dir>" );
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, " Directory to put the cliff files in" );
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "<datasource>" );
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, " The datasource from which to fetch the data" );
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "<layername>..." );
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, " The layers to process." );
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, " If no layer is given, all layers in the datasource are used" );
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
void
|
||||
setLoggingPriority (const char * p)
|
||||
{
|
||||
if (p == 0)
|
||||
return;
|
||||
string priority = p;
|
||||
if (priority == "bulk") {
|
||||
sglog().set_log_priority(SG_BULK);
|
||||
} else if (priority == "debug") {
|
||||
sglog().set_log_priority(SG_DEBUG);
|
||||
} else if (priority.empty() || priority == "info") { // default
|
||||
sglog().set_log_priority(SG_INFO);
|
||||
} else if (priority == "warn") {
|
||||
sglog().set_log_priority(SG_WARN);
|
||||
} else if (priority == "alert") {
|
||||
sglog().set_log_priority(SG_ALERT);
|
||||
} else {
|
||||
SG_LOG(SG_GENERAL, SG_WARN, "Unknown logging priority " << priority);
|
||||
}
|
||||
}
|
||||
|
||||
int main( int argc, char **argv ) {
|
||||
char* progname=argv[0];
|
||||
string datasource,work_dir;
|
||||
|
||||
sglog().setLogLevels( SG_ALL, SG_INFO );
|
||||
|
||||
while (argc>1) {
|
||||
if (!strcmp(argv[1],"--log-level")) {
|
||||
if (argc<3) {
|
||||
usage(progname);
|
||||
}
|
||||
setLoggingPriority(argv[2]);
|
||||
argv+=2;
|
||||
argc-=2;
|
||||
} else if (!strcmp(argv[1],"--seperate-segments")) {
|
||||
argv++;
|
||||
argc--;
|
||||
seperate_segments=1;
|
||||
} else if (!strcmp(argv[1],"--continue-on-errors")) {
|
||||
argv++;
|
||||
argc--;
|
||||
continue_on_errors=1;
|
||||
} else if (!strcmp(argv[1],"--start-record")) {
|
||||
if (argc<3) {
|
||||
usage(progname);
|
||||
}
|
||||
start_record=atoi(argv[2]);
|
||||
argv+=2;
|
||||
argc-=2;
|
||||
} else if (!strcmp(argv[1],"--where")) {
|
||||
if (argc<3) {
|
||||
usage(progname);
|
||||
}
|
||||
use_attribute_query=true;
|
||||
attribute_query=argv[2];
|
||||
argv+=2;
|
||||
argc-=2;
|
||||
} else if (!strcmp(argv[1],"--spat")) {
|
||||
if (argc<6) {
|
||||
usage(progname);
|
||||
}
|
||||
use_spatial_query=true;
|
||||
spat_min_x=atof(argv[2]);
|
||||
spat_min_y=atof(argv[3]);
|
||||
spat_max_x=atof(argv[4]);
|
||||
spat_max_y=atof(argv[5]);
|
||||
argv+=5;
|
||||
argc-=5;
|
||||
} else if (!strcmp(argv[1],"--threads")) {
|
||||
if (argc<3) {
|
||||
usage(progname);
|
||||
}
|
||||
num_threads=atoi(argv[2]);
|
||||
argv+=2;
|
||||
argc-=2;
|
||||
} else if (!strcmp(argv[1],"--all-threads")) {
|
||||
num_threads=boost::thread::hardware_concurrency();
|
||||
argv+=1;
|
||||
argc-=1;
|
||||
} else if (!strcmp(argv[1],"--debug")) {
|
||||
argv++;
|
||||
argc--;
|
||||
save_shapefiles=true;
|
||||
} else if (!strcmp(argv[1],"--help")) {
|
||||
usage(progname);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "\ncliff-decode version " << getTGVersion() );
|
||||
|
||||
if (argc<3) {
|
||||
usage(progname);
|
||||
}
|
||||
work_dir=argv[1];
|
||||
datasource=argv[2];
|
||||
|
||||
SGPath sgp( work_dir );
|
||||
sgp.append( "dummy" );
|
||||
sgp.create_dir( 0755 );
|
||||
|
||||
tgChopper results( work_dir );
|
||||
|
||||
// initialize persistant polygon counter
|
||||
//string counter_file = work_dir + "/poly_counter";
|
||||
//poly_index_init( counter_file );
|
||||
|
||||
// new chop api
|
||||
//std::string counter_file2 = work_dir + "/poly_counter2";
|
||||
//tgPolygon::ChopIdxInit( counter_file );
|
||||
|
||||
SG_LOG( SG_GENERAL, SG_DEBUG, "Opening datasource " << datasource << " for reading." );
|
||||
|
||||
GDALAllRegister();
|
||||
GDALDataset *poDS;
|
||||
|
||||
poDS = (GDALDataset*) GDALOpenEx( datasource.c_str(), GDAL_OF_VECTOR, NULL, NULL, NULL );
|
||||
if( poDS == NULL )
|
||||
{
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Failed opening datasource " << datasource );
|
||||
exit( 1 );
|
||||
}
|
||||
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Processing datasource " << datasource );
|
||||
|
||||
OGRLayer *poLayer;
|
||||
if (argc>3) {
|
||||
for (int i=3;i<argc;i++) {
|
||||
poLayer = poDS->GetLayerByName( argv[i] );
|
||||
|
||||
if (poLayer == NULL )
|
||||
{
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Failed opening layer " << argv[i] << " from datasource " << datasource );
|
||||
exit( 1 );
|
||||
}
|
||||
processLayer(poLayer, results );
|
||||
}
|
||||
} else {
|
||||
for (int i=0;i<poDS->GetLayerCount();i++) {
|
||||
poLayer = poDS->GetLayer(i);
|
||||
|
||||
assert(poLayer != NULL);
|
||||
|
||||
processLayer(poLayer, results );
|
||||
}
|
||||
}
|
||||
|
||||
GDALClose(poDS);
|
||||
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "Saving to buckets");
|
||||
results.Add_Extension("cliffs");
|
||||
results.Save( save_shapefiles );
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in a new issue