File indexing completed on 2024-07-14 14:25:19

0001 #include "BilinearInterpolation.h"
0002 #include "NasaWorldWindToOpenStreetMapConverter.h"
0003 #include "NearestNeighborInterpolation.h"
0004 #include "OsmTileClusterRenderer.h"
0005 #include "ReadOnlyMapDefinition.h"
0006 #include "Thread.h"
0007 #include "mapreproject.h"
0008 
0009 #include <QCoreApplication>
0010 #include <QDebug>
0011 #include <QDir>
0012 #include <QPair>
0013 #include <QThread>
0014 #include <QVector>
0015 
0016 #include <getopt.h>
0017 
0018 #include <cstdlib>
0019 #include <iostream>
0020 #include <string>
0021 #include <vector>
0022 
0023 /* example usage
0024 mapreproject --simulate --output-directory=/home/jmho --jobs 7 --cluster-size 64 --output-tile-level=10 \
0025     --input=type=NasaWW,base-directory=/home,tile-level=8,interpolation-method=bilinear,cache-size=200000000 \
0026     --input type=Bathymetry,file=BLAH.tiff
0027 */
0028 
0029 void printUsage()
0030 {
0031     std::cout << "Usage: mapreproject [OPTIONS] [INPUT] ...\n"
0032                  "      --help                display this help and exit\n"
0033                  "      --output-directory    output base directory, where the resulting tiles will be stored\n"
0034                  "      --output-tile-level   tile level of resulting map\n"
0035                  "      --cluster-size        edge length of tile clusters in tiles\n"
0036                  "      --jobs                number of threads, use to override default of one thread per cpu core\n"
0037                  "      --simulate            \n"
0038                  "      --input INPUT_OPTS    INPUT_OPTS can be a combination of the following options, separated by comma:\n"
0039                  "          type              \n"
0040                  "          base-directory    \n"
0041                  "          tile-level        \n"
0042                  "          cache-size        \n"
0043                  "          file              \n"
0044                  "          interpolation-method  one of \"integer\", \"nearest-neighbor\" (default), \"average\" or \"bilinear\"\n";
0045 }
0046 
0047 MapSourceType parseType( char const * const value )
0048 {
0049     MapSourceType result = UnknownMapSource;
0050     if ( !value )
0051         qFatal("Suboption 'type' does not have a value.");
0052     if ( strcmp( value, "NasaWW") == 0 )
0053         result = NasaWorldWindMap;
0054     else if ( strcmp( value, "Bathymetry" ) == 0 )
0055         result = BathymetryMap;
0056     else
0057         qFatal("Suboption 'type': Unrecognized value '%s'.", value );
0058     return result;
0059 }
0060 
0061 QString parseString( char const * const value )
0062 {
0063     QString result;
0064     if ( !value )
0065         qFatal("Suboption does not have a value.");
0066     result = value;
0067     return result;
0068 }
0069 
0070 int parseInt( char const * const value )
0071 {
0072     if ( !value )
0073         qFatal("Suboption does not have a value.");
0074     QString str( value );
0075     bool ok;
0076     int const result = str.toInt( &ok );
0077     if ( !ok )
0078         qFatal("Suboption does not have an integer value.");
0079     return result;
0080 }
0081 
0082 EInterpolationMethod parseInterpolationMethod( char const * const value )
0083 {
0084     EInterpolationMethod result = UnknownInterpolationMethod;
0085     if ( !value )
0086         qFatal("Suboption 'interpolation-method' does not have a value.");
0087     if ( strcmp( value, "integer") == 0 )
0088         result = IntegerInterpolationMethod;
0089     if ( strcmp( value, "nearest-neighbor") == 0 )
0090         result = NearestNeighborInterpolationMethod;
0091     if ( strcmp( value, "average") == 0 )
0092         result = AverageInterpolationMethod;
0093     else if ( strcmp( value, "bilinear" ) == 0 )
0094         result =  BilinearInterpolationMethod;
0095     else
0096         qFatal("Suboption 'interpolation-method': Unrecognized value '%s'.", value );
0097     return result;
0098 }
0099 
0100 ReadOnlyMapDefinition parseInput( char * subopts )
0101 {
0102     if ( !subopts )
0103         qFatal("Missing argument for '--input'");
0104 
0105     enum
0106     {
0107         TypeOption = 0,
0108         BaseDirectoryOption,
0109         FileOption,
0110         TileLevelOption,
0111         InterpolationOption,
0112         CacheSizeOption,
0113         TheEnd
0114     };
0115 
0116     char optionType[] = "type";
0117     char optionBaseDirectory[] = "base-directory";
0118     char optionFile[] = "file";
0119     char optionTileLevel[] = "tile-level";
0120     char optionInterpolationMethod[] = "interpolation-method";
0121     char optionCacheSize[] = "cache-size";
0122     char * const input_opts[] =
0123     {
0124         optionType,
0125         optionBaseDirectory,
0126         optionFile,
0127         optionTileLevel,
0128         optionInterpolationMethod,
0129         optionCacheSize,
0130         nullptr
0131     };
0132 
0133     ReadOnlyMapDefinition mapDefinition;
0134 
0135     char * value;
0136     while ( *subopts != '\0' ) {
0137         switch ( getsubopt( &subopts, input_opts, &value )) {
0138 
0139         case TypeOption:
0140             mapDefinition.setMapType( parseType( value ));
0141             break;
0142 
0143         case BaseDirectoryOption:
0144             mapDefinition.setBaseDirectory( parseString( value ));
0145             break;
0146 
0147         case FileOption:
0148             mapDefinition.setFileName( parseString( value ));
0149             break;
0150 
0151         case TileLevelOption:
0152             mapDefinition.setTileLevel( parseInt( value ));
0153             break;
0154 
0155         case InterpolationOption:
0156             mapDefinition.setInterpolationMethod( parseInterpolationMethod( value ));
0157             break;
0158 
0159         case CacheSizeOption:
0160             mapDefinition.setCacheSizeBytes( parseInt( value ));
0161             break;
0162 
0163         default:
0164             qFatal("Unrecognized input suboption.");
0165         }
0166     }
0167     return mapDefinition;
0168 }
0169 
0170 
0171 int main( int argc, char *argv[] )
0172 {
0173     QCoreApplication app( argc, argv );
0174 
0175     // --interpolation-method=NearestNeighbor|Bilinear
0176 
0177     QString outputDirectory;
0178     int outputTileLevel = -1;
0179 
0180     int threadCount = QThread::idealThreadCount();
0181     int clusterSize = 0; // cluster size 0 makes no sense
0182     bool onlySimulate = false;
0183 
0184     QVector<ReadOnlyMapDefinition> mapSources;
0185 
0186     // input: type, tile-level, base-dir|file
0187     // --input,type=NasaWW,tile-level=8,base-directory=<dir>,interpolation-method=Bilinear
0188     // --input,type=Image,file=<file>
0189 
0190     enum { HelpOption = 1,
0191            InputOption,
0192            OutputDirectoryOption,
0193            OutputTileLevelOption,
0194            JobsOption,
0195            ClusterSizeOption,
0196            SimulateOption };
0197 
0198     static struct option long_options[] = {
0199         {"help",              no_argument,       nullptr, HelpOption },
0200         {"input",             required_argument, nullptr, InputOption },
0201         {"output-directory",  required_argument, nullptr, OutputDirectoryOption },
0202         {"output-tile-level", required_argument, nullptr, OutputTileLevelOption },
0203         {"jobs",              required_argument, nullptr, JobsOption },
0204         {"cluster-size",      required_argument, nullptr, ClusterSizeOption },
0205         {"simulate",          no_argument,       nullptr, SimulateOption },
0206         {nullptr, 0, nullptr, 0 }
0207     };
0208 
0209     while ( true ) {
0210         int option_index = 0;
0211 
0212         int const opt = getopt_long( argc, argv, "", long_options, &option_index );
0213         if ( opt == -1 )
0214             break;
0215 
0216         switch ( opt ) {
0217 
0218         case HelpOption:
0219             printUsage();
0220             exit( EXIT_SUCCESS );
0221 
0222         case InputOption:
0223             mapSources.push_back( parseInput( optarg ));
0224             break;
0225 
0226         case OutputDirectoryOption:
0227             outputDirectory = parseString( optarg );
0228             break;
0229 
0230         case OutputTileLevelOption:
0231             outputTileLevel = parseInt( optarg );
0232             break;
0233 
0234         case JobsOption:
0235             threadCount = parseInt( optarg );
0236             break;
0237 
0238         case ClusterSizeOption:
0239             clusterSize = parseInt( optarg );
0240             break;
0241 
0242         case SimulateOption:
0243             onlySimulate = true;
0244             break;
0245 
0246         case '?':
0247             break;
0248         }
0249     }
0250 
0251     qDebug() << "\noutput directory:" << outputDirectory
0252              << "\noutput tile level:" << outputTileLevel
0253              << "\ncluster size:" << clusterSize
0254              << "\nthreads:" << threadCount
0255              << "\ninputs:" << mapSources;
0256 
0257     if (onlySimulate)
0258         exit( EXIT_SUCCESS );
0259 
0260     NasaWorldWindToOpenStreetMapConverter converter;
0261     converter.setMapSources( mapSources );
0262     converter.setOsmBaseDirectory( QDir( outputDirectory ));
0263     converter.setOsmTileLevel( outputTileLevel );
0264     converter.setOsmTileClusterEdgeLengthTiles( clusterSize );
0265     converter.setThreadCount( threadCount );
0266 
0267     QObject::connect( &converter, SIGNAL(finished()), &app, SLOT(quit()));
0268 
0269     QVector<QPair<Thread*, OsmTileClusterRenderer*> > renderThreads = converter.start();
0270     app.exec();
0271 
0272     QVector<QPair<Thread*, OsmTileClusterRenderer*> >::iterator pos = renderThreads.begin();
0273     QVector<QPair<Thread*, OsmTileClusterRenderer*> >::iterator const end = renderThreads.end();
0274     for (; pos != end; ++pos ) {
0275         (*pos).first->stop();
0276         (*pos).first->wait();
0277         delete (*pos).second;
0278     }
0279 
0280     return EXIT_SUCCESS;
0281 }