File indexing completed on 2025-02-16 06:46:23
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 }