bool validateParamsFull(const string& inputpath, const string& outputpath, const string& imgpath, const MapParams& mp, int threads, const string& chunklist, const string& regionlist, bool expand, const string& htmlpath) { // -c and -x are not allowed for full renders if (!chunklist.empty() || !regionlist.empty() || expand) { cerr << "-c, -r, -x not allowed for full renders" << endl; return false; } // B and T must be within range (upper limits aren't really necessary and can be adjusted if // someone really wants gigantic tile images for some reason) if (!mp.valid()) { cerr << "-B must be in range 2-16; -T must be in range 1-16" << endl; return false; } // baseZoom must be within range, or -1 (omitted) if (!mp.validZoom() && mp.baseZoom != -1) { cerr << "-Z must be in range 0-30, or may be omitted to set automatically" << endl; return false; } // MINY/MAXY must describe a valid range if (!mp.validYRange()) { cerr << "-y and -Y, if used, must be in range 0-255, and -y must be <= -Y" << endl; return false; } // must have a sensible number of threads (upper limit is arbitrary, but you'd need a truly // insanely large map to see any benefit to having that many...) if (threads < 1 || threads > 64) { cerr << "-h must be in range 1-64" << endl; return false; } // the various paths must be non-empty if (inputpath.empty() || outputpath.empty()) { cerr << "must provide both input (-i) and output (-o) paths" << endl; return false; } if (imgpath.empty()) { cerr << "must provide non-empty image path, or omit -g to use \".\"" << endl; return false; } if (htmlpath.empty()) { cerr << "must provide non-empty HTML path, or omit -m to use \".\"" << endl; return false; } return true; }
bool validateParamsTest(const string& inputpath, const string& outputpath, const string& imgpath, const MapParams& mp, int threads, const string& chunklist, const string& regionlist, bool expand, const string& htmlpath, int testworldsize) { // -i, -o, -c, -r, -x, -m are not allowed if (!inputpath.empty() || !outputpath.empty() || !chunklist.empty() || !regionlist.empty() || expand || htmlpath != ".") { cerr << "-i, -o, -c, -r, -x, -m not allowed for test worlds" << endl; return false; } // B and T must be within range (upper limits aren't really necessary and can be adjusted if // someone really wants gigantic tile images for some reason) if (!mp.valid()) { cerr << "-B must be in range 2-16; -T must be in range 1-16" << endl; return false; } // baseZoom must be within range, or -1 (omitted) if (!mp.validZoom() && mp.baseZoom != -1) { cerr << "-Z must be in range 0-30, or may be omitted to set automatically" << endl; return false; } // MINY/MAXY must describe a valid range if (!mp.validYRange()) { cerr << "-y and -Y, if used, must be in range 0-255, and -y must be <= -Y" << endl; return false; } // must have a sensible number of threads (upper limit is arbitrary, but you'd need a truly // insanely large map to see any benefit to having that many...) if (threads < 1 || threads > 64) { cerr << "-h must be in range 1-64" << endl; return false; } // image path must be non-empty if (imgpath.empty()) { cerr << "must provide non-empty image path, or omit -g to use \".\"" << endl; return false; } // test world size must be positive if (testworldsize < 0) { cerr << "testworld size must be positive" << endl; return false; } return true; }
// see if there's enough available memory for some number of tile images // (...by just attempting to allocate it!) //!!!!!!! better way to do this? maybe allow user to specify max memory for // ThreadOutputCache instead? bool memoryAvailable(int tiles, const MapParams& mp) { int64_t imgsize = mp.tileSize() * mp.tileSize(); // in pixels try { RGBAPixel *tempbuf = new RGBAPixel[imgsize * tiles]; delete[] tempbuf; } catch (bad_alloc& ba) { return false; } return true; }
// warning: slow void testTileBBoxes(const MapParams& mp) { // check tile bounding boxes for a few tiles for (int64_t tx = -5; tx <= 5; tx++) for (int64_t ty = -5; ty <= 5; ty++) { // get computed BBox TileIdx ti(tx,ty); BBox bbox = ti.getBBox(mp); // this is what the box is supposed to be int64_t xmin = 64*mp.B*mp.T*tx - 2*mp.B; int64_t ymax = 64*mp.B*mp.T*ty + 17*mp.B; int64_t xmax = xmin + mp.tileSize(); int64_t ymin = ymax - mp.tileSize(); // test pixels for (int64_t x = xmin - 15; x <= xmax + 15; x++) for (int64_t y = ymin - 15; y <= ymax + 15; y++) { bool result = bbox.includes(Pixel(x,y)); bool expected = x >= xmin && x < xmax && y >= ymin && y < ymax; if (result != expected) { cout << "failed tile bounding box test! " << tx << " " << ty << endl; cout << "[" << bbox.topLeft.x << "," << bbox.topLeft.y << "] to [" << bbox.bottomRight.x << "," << bbox.bottomRight.y << "]" << endl; cout << "[" << xmin << "," << ymin << "] to [" << xmax << "," << ymax << "]" << endl; cout << x << "," << y << endl; return; } } } }
// also sets MapParams to values from existing map bool validateParamsIncremental(const string& inputpath, const string& outputpath, const string& imgpath, MapParams& mp, int threads, const string& chunklist, const string& regionlist, bool expand, const string& htmlpath) { // -B, -T, -Z are not allowed if (mp.B != -1 || mp.T != -1 || mp.baseZoom != -1) { cerr << "-B, -T, -Z not allowed for incremental updates" << endl; return false; } // the various paths must be non-empty if (inputpath.empty() || outputpath.empty()) { cerr << "must provide both input (-i) and output (-o) paths" << endl; return false; } if (imgpath.empty()) { cerr << "must provide non-empty image path, or omit -g to use \".\"" << endl; return false; } if (htmlpath.empty()) { cerr << "must provide non-empty HTML path, or omit -m to use \".\"" << endl; return false; } // can't have both chunklist and regionlist if (!chunklist.empty() && !regionlist.empty()) { cerr << "only one of -c, -r may be used" << endl; return false; } // if world is in region format, must use regionlist if (detectRegionFormat(inputpath) && regionlist.empty()) { cerr << "world is in region format; must use -r, not -c" << endl; return false; } // pigmap.params must be present in output path; read it now if (!mp.readFile(outputpath)) { cerr << "can't find pigmap.params in output path" << endl; return false; } // must have a sensible number of threads (upper limit is arbitrary, but you'd need a truly // insanely large map to see any benefit to having that many...) if (threads < 1 || threads > 64) { cerr << "-h must be in range 1-64" << endl; return false; } return true; }
bool expandMap(const string& outputpath) { // read old params MapParams mp; if (!mp.readFile(outputpath)) { cerr << "pigmap.params missing or corrupt" << endl; return false; } int32_t tileSize = mp.tileSize(); // to expand a map, the following must be done: // 1. the top-left quadrant of the current zoom level 1 needs to be moved to zoom level 2, where // it will become the bottom-right quadrant of the top-left quadrant of the new zoom level 1, // so the top-level file "0.png" and subdirectory "0" must become "0/3.png" and "0/3", // respectively; and similarly for the other three quadrants // 2. new zoom level 1 tiles must be created: "0.png" is 3/4 empty, but has a shrunk version of // the old "0.png" (which is the new "0/3.png") in its bottom-right, etc. // 3. a new "base.png" must be created from the new zoom level 1 tiles // move everything at zoom 1 or higher one level deeper // ...first the subdirectories renameFile(outputpath + "/0", outputpath + "/old0"); renameFile(outputpath + "/1", outputpath + "/old1"); renameFile(outputpath + "/2", outputpath + "/old2"); renameFile(outputpath + "/3", outputpath + "/old3"); makePath(outputpath + "/0"); makePath(outputpath + "/1"); makePath(outputpath + "/2"); makePath(outputpath + "/3"); renameFile(outputpath + "/old0", outputpath + "/0/3"); renameFile(outputpath + "/old1", outputpath + "/1/2"); renameFile(outputpath + "/old2", outputpath + "/2/1"); renameFile(outputpath + "/old3", outputpath + "/3/0"); // ...now the zoom 1 files const char* formatExtensions[] = {".png", ".jpeg"}; ImageSettings::Format formats[] = {ImageSettings::Format_PNG, ImageSettings::Format_JPEG}; for (int i = 0; i < 2; ++i) { if (ImageSettings::format == formats[i] || ImageSettings::format == ImageSettings::Format_Both) { const char* format = formatExtensions[i]; renameFile(outputpath + "/0" + format, outputpath + "/0/3" + format); renameFile(outputpath + "/1" + format, outputpath + "/1/2" + format); renameFile(outputpath + "/2" + format, outputpath + "/2/1" + format); renameFile(outputpath + "/3" + format, outputpath + "/3/0" + format); } } // build the new zoom 1 tiles RGBAImage old0img; bool used0 = old0img.readPNG(outputpath + "/0/3.png"); RGBAImage new0img; new0img.create(tileSize, tileSize); if (used0) { reduceHalf(new0img, ImageRect(tileSize/2, tileSize/2, tileSize/2, tileSize/2), old0img); new0img.writeImage(outputpath + "/0"); } RGBAImage old1img; bool used1 = old1img.readPNG(outputpath + "/1/2.png"); RGBAImage new1img; new1img.create(tileSize, tileSize); if (used1) { reduceHalf(new1img, ImageRect(0, tileSize/2, tileSize/2, tileSize/2), old1img); new1img.writeImage(outputpath + "/1"); } RGBAImage old2img; bool used2 = old2img.readPNG(outputpath + "/2/1.png"); RGBAImage new2img; new2img.create(tileSize, tileSize); if (used2) { reduceHalf(new2img, ImageRect(tileSize/2, 0, tileSize/2, tileSize/2), old2img); new2img.writeImage(outputpath + "/2"); } RGBAImage old3img; bool used3 = old3img.readPNG(outputpath + "/3/0.png"); RGBAImage new3img; new3img.create(tileSize, tileSize); if (used3) { reduceHalf(new3img, ImageRect(0, 0, tileSize/2, tileSize/2), old3img); new3img.writeImage(outputpath + "/3"); } // build the new base tile RGBAImage newbase; newbase.create(tileSize, tileSize); if (used0) reduceHalf(newbase, ImageRect(0, 0, tileSize/2, tileSize/2), new0img); if (used1) reduceHalf(newbase, ImageRect(tileSize/2, 0, tileSize/2, tileSize/2), new1img); if (used2) reduceHalf(newbase, ImageRect(0, tileSize/2, tileSize/2, tileSize/2), new2img); if (used3) reduceHalf(newbase, ImageRect(tileSize/2, tileSize/2, tileSize/2, tileSize/2), new3img); newbase.writeImage(outputpath + "/base"); // write new params (with incremented baseZoom) mp.baseZoom++; mp.writeFile(outputpath); // touch all tiles, to prevent browser cache mishaps (since many new tiles will have the same // filename as some old tile, but possibly with an earlier timestamp) if (system((string("find ") + outputpath + " -exec touch {} +").c_str()) < 0) cerr << "Error changing mtimes. Ignoring..." << endl; return true; }