Add --buildpyramid, to build a full zoom pyramid for leaflet.

This commit is contained in:
Martijn Versteegh 2018-11-15 12:42:05 +01:00
parent f84828b0bf
commit 90e7228bb9
7 changed files with 153 additions and 11 deletions

View File

@ -103,6 +103,13 @@ tilesize:
a multiple of 16. The filenames will be created in the form <x>_<y>_<filename>, where <x> and <y> a multiple of 16. The filenames will be created in the form <x>_<y>_<filename>, where <x> and <y>
are the tile numbers and <filename> is the name specified with -o. Skip empty tiles by also specifying --noemptyimage. are the tile numbers and <filename> is the name specified with -o. Skip empty tiles by also specifying --noemptyimage.
leaflet:
Together with --tilesize, output tiles in a leaflet-compaitble numbering and also output a html file to display the map.
Copy the files in the Leaflet subfolder to the same folder as your map.
buildpyramid:
Together with --leaflet, build a zoom pyramid for faster zooming/loading of the generated map.
zoom: zoom:
Apply zoom to drawn nodes by enlarging them to n*n squares, e.g. ``--zoom 4`` Apply zoom to drawn nodes by enlarging them to n*n squares, e.g. ``--zoom 4``

View File

@ -7,6 +7,7 @@
#include <stdexcept> #include <stdexcept>
#include <cstring> #include <cstring>
#include <vector> #include <vector>
#include <math.h>
#include "TileGenerator.h" #include "TileGenerator.h"
#include "config.h" #include "config.h"
@ -81,6 +82,7 @@ TileGenerator::TileGenerator():
m_shading(true), m_shading(true),
m_leaflet(false), m_leaflet(false),
m_dontWriteEmpty(false), m_dontWriteEmpty(false),
m_buildPyramid(false),
m_backend(""), m_backend(""),
m_xBorder(0), m_xBorder(0),
m_yBorder(0), m_yBorder(0),
@ -185,6 +187,11 @@ void TileGenerator::setLeaflet(bool leaflet)
m_leaflet = leaflet; m_leaflet = leaflet;
} }
void TileGenerator::setBuildPyramid(bool pyramid)
{
m_buildPyramid = pyramid;
}
void TileGenerator::setBackend(std::string backend) void TileGenerator::setBackend(std::string backend)
{ {
m_backend = backend; m_backend = backend;
@ -251,10 +258,6 @@ void TileGenerator::setDontWriteEmpty(bool f)
void TileGenerator::generate(const std::string &input, const std::string &output) void TileGenerator::generate(const std::string &input, const std::string &output)
{ {
if (m_leaflet)
{
outputLeafletCode(output);
}
string input_path = input; string input_path = input;
if (input_path[input.length() - 1] != PATH_SEPARATOR) { if (input_path[input.length() - 1] != PATH_SEPARATOR) {
@ -294,6 +297,22 @@ void TileGenerator::generate(const std::string &input, const std::string &output
tilePositions(); tilePositions();
// round xMax/zMax tot integer number of tiles
m_xMax = m_xMin + m_numTilesX * m_tileW;
m_zMax = m_zMin + m_numTilesY * m_tileH;
int maxZoomLevel = 0;
if (m_buildPyramid)
{
int maxDimX = m_xMax > -m_xMin ? m_xMax : -m_xMin;
int maxDimY = m_zMax > -m_zMin ? m_zMax : -m_zMin;
int maxDim = maxDimX > maxDimY ? maxDimX : maxDimY;
maxZoomLevel = static_cast<int>(log2(maxDim / m_tileW)) + 1;
}
int trueXMin = m_xMin; int trueXMin = m_xMin;
int trueZMin = m_zMin; int trueZMin = m_zMin;
@ -322,8 +341,16 @@ void TileGenerator::generate(const std::string &input, const std::string &output
renderPlayers(input_path); renderPlayers(input_path);
} }
ostringstream fn; ostringstream fn;
if (m_leaflet)
{
fn << maxZoomLevel << "_";
}
fn << x + minTileX << '_' << (flipY * (y + minTileZ)) << '_' << output; fn << x + minTileX << '_' << (flipY * (y + minTileZ)) << '_' << output;
writeImage(fn.str()); writeImage(fn.str());
if (m_buildPyramid)
{
m_availableTiles.insert(Coords(x + minTileX,(flipY * (y + minTileZ))));
}
} }
} }
} }
@ -334,6 +361,15 @@ void TileGenerator::generate(const std::string &input, const std::string &output
m_image->fill(m_bgColor); m_image->fill(m_bgColor);
writeImage(fn.str()); writeImage(fn.str());
} }
if (m_buildPyramid)
{
buildPyramid(output, maxZoomLevel);
}
if (m_leaflet)
{
outputLeafletCode(output, maxZoomLevel);
}
} }
else else
{ {
@ -843,12 +879,12 @@ static char const *leafletMapHtml =
"<script>\n" "<script>\n"
" var MineTestMap = L.map('mapid', {\n" " var MineTestMap = L.map('mapid', {\n"
" crs: L.CRS.Simple,\n" " crs: L.CRS.Simple,\n"
"// minZoom: -3,\n" "// minZoom: 0,\n"
" });\n" " });\n"
" MineTestMap.setView([0.0, 0.0], 0);\n" " MineTestMap.setView([0.0, 0.0], %d);\n"
" L.tileLayer('{x}_{y}_%s', {\n" " L.tileLayer('{z}_{x}_{y}_%s', {\n"
" minNativeZoom: 0,\n" " minNativeZoom: 0,\n"
" maxNativeZoom: 0,\n" " maxNativeZoom: %d,\n"
" attribution: 'Minetest World',\n" " attribution: 'Minetest World',\n"
" tileSize: %d,\n" " tileSize: %d,\n"
" errorTileUrl: \"empty_tile_%s\",\n" " errorTileUrl: \"empty_tile_%s\",\n"
@ -866,7 +902,7 @@ static char const *leafletMapHtml =
"</html>\n"; "</html>\n";
void TileGenerator::outputLeafletCode(std::string const &output) void TileGenerator::outputLeafletCode(std::string const &output, int maxLevel)
{ {
if (m_tileH != m_tileW) if (m_tileH != m_tileW)
{ {
@ -874,6 +910,12 @@ void TileGenerator::outputLeafletCode(std::string const &output)
return; return;
} }
if (m_tileH == INT_MAX)
{
throw std::runtime_error("Can only use --leaflet with --tilesize!");
return;
}
ostringstream fn; ostringstream fn;
fn << output << ".html"; fn << output << ".html";
@ -888,9 +930,73 @@ void TileGenerator::outputLeafletCode(std::string const &output)
return; return;
} }
fprintf(out, leafletMapHtml, output.c_str(), m_tileW, output.c_str()); fprintf(out, leafletMapHtml, maxLevel, output.c_str(), maxLevel, m_tileW*16, output.c_str());
fclose(out); fclose(out);
}
void TileGenerator::buildPyramid(std::string const &fileName, int startLevel)
{
TileSet tilesToGenerate;
int level = startLevel;
while (true)
{
for (TileSet::iterator t = m_availableTiles.begin(); t != m_availableTiles.end(); t++)
{
int x = t->first;
int y = t->second;
if (x >=0)
x++;
if (y >=0)
y++;
x = round_multiple_nosign(x, 2) / 2;
y = round_multiple_nosign(y, 2) / 2;
if (x > 0)
x--;
if (y > 0)
y--;
tilesToGenerate.insert(Coords(x, y));
}
int halfW = m_mapWidth/2;
int halfH = m_mapHeight/2;
for (TileSet::iterator g = tilesToGenerate.begin(); g != tilesToGenerate.end(); g++)
{
m_image->fill(m_bgColor);
int x = g->first;
int y = g->second;
for (int i = 0; i < 2 ; i++)
{
for (int j = 0; j < 2; j++)
{
if (m_availableTiles.find(Coords(2*x+i, 2*y+j)) != m_availableTiles.end())
{
std::ostringstream f;
f << level << "_" << (2*x+i) << "_" << (2*y+j) << "_" << fileName;
Image src(f.str());
src.scaleBlit(m_image, i*halfW, j*halfH, halfW, halfH);
}
}
}
std::ostringstream f;
f << (level-1) << "_" << x << "_" << y << "_" << fileName;
m_image->save(f.str());
cout << "Wrote image " << f.str() << endl;
}
std::cout << "generated pyramid level " << (level -1) << endl;
level--;
if (!level)
{
return; // we're done
}
m_availableTiles = tilesToGenerate;
tilesToGenerate.clear();
}
} }

View File

@ -57,7 +57,13 @@ struct BitmapThing { // 16x16 bitmap
}; };
typedef std::list<std::pair<int, int> > PositionsList; typedef std::list<std::pair<int, int> > PositionsList;
typedef std::pair<int16_t, int16_t> Coords;
struct Coords_hash {
inline std::size_t operator()(const Coords &v) const {
return std::hash<int>()((static_cast<int>(v.first) << 16) + static_cast<int>(v.second));
}
};
class TileGenerator class TileGenerator
{ {
@ -65,10 +71,12 @@ private:
#if __cplusplus >= 201103L #if __cplusplus >= 201103L
typedef std::unordered_map<std::string, ColorEntry> ColorMap; typedef std::unordered_map<std::string, ColorEntry> ColorMap;
typedef std::unordered_set<std::string> NameSet; typedef std::unordered_set<std::string> NameSet;
typedef std::unordered_set<Coords, Coords_hash> TileSet;
typedef std::unordered_map<int, PositionsList> TileMap; typedef std::unordered_map<int, PositionsList> TileMap;
#else #else
typedef std::map<std::string, ColorEntry> ColorMap; typedef std::map<std::string, ColorEntry> ColorMap;
typedef std::set<std::string> NameSet; typedef std::set<std::string> NameSet;
typedef std::set<Coords, Coords_hash> TileSet;
typedef std::map<int, PositionsList> TileMap; typedef std::map<int, PositionsList> TileMap;
#endif #endif
@ -85,6 +93,7 @@ public:
void setDrawAlpha(bool drawAlpha); void setDrawAlpha(bool drawAlpha);
void setShading(bool shading); void setShading(bool shading);
void setLeaflet(bool leaflet); void setLeaflet(bool leaflet);
void setBuildPyramid(bool pyramid);
void setGeometry(int x, int y, int w, int h); void setGeometry(int x, int y, int w, int h);
void setTileSize(int w, int h); void setTileSize(int w, int h);
void setMinY(int y); void setMinY(int y);
@ -119,7 +128,8 @@ private:
void setZoomed(int x, int y, Color color); void setZoomed(int x, int y, Color color);
private: private:
void outputLeafletCode(std::string const &output); void outputLeafletCode(std::string const &output, int maxZoomLevel);
void buildPyramid(std::string const &baseName, int startLevel);
Color m_bgColor; Color m_bgColor;
Color m_scaleColor; Color m_scaleColor;
Color m_originColor; Color m_originColor;
@ -131,6 +141,7 @@ private:
bool m_shading; bool m_shading;
bool m_leaflet; bool m_leaflet;
bool m_dontWriteEmpty; bool m_dontWriteEmpty;
bool m_buildPyramid;
std::string m_backend; std::string m_backend;
int m_xBorder, m_yBorder; int m_xBorder, m_yBorder;
@ -167,6 +178,9 @@ private:
int m_zoom; int m_zoom;
uint m_scales; uint m_scales;
// stuff for pyramid building
TileSet m_availableTiles;
}; // class TileGenerator }; // class TileGenerator
#endif // TILEGENERATOR_HEADER #endif // TILEGENERATOR_HEADER

View File

@ -31,6 +31,7 @@ void usage()
" --geometry x:y+w+h\n" " --geometry x:y+w+h\n"
" --tilesize wxh\n" " --tilesize wxh\n"
" --leaflet\n" " --leaflet\n"
" --buildpyramid\n"
" --extent\n" " --extent\n"
" --zoom <zoomlevel>\n" " --zoom <zoomlevel>\n"
" --colors <colors.txt>\n" " --colors <colors.txt>\n"
@ -93,6 +94,7 @@ int main(int argc, char *argv[])
{"scales", required_argument, 0, 'f'}, {"scales", required_argument, 0, 'f'},
{"noemptyimage", no_argument, 0, 'n'}, {"noemptyimage", no_argument, 0, 'n'},
{"leaflet", no_argument, 0, 'l'}, {"leaflet", no_argument, 0, 'l'},
{"buildpyramid", no_argument, 0, 'B'},
{0, 0, 0, 0} {0, 0, 0, 0}
}; };
@ -152,6 +154,9 @@ int main(int argc, char *argv[])
case 'l': case 'l':
generator.setLeaflet(true); generator.setLeaflet(true);
break; break;
case 'B':
generator.setBuildPyramid(true);
break;
case 'd': case 'd':
generator.setBackend(optarg); generator.setBackend(optarg);
break; break;

View File

@ -82,6 +82,16 @@ Don't output one big image, but output tiles of the specified size, e.g. "--tile
a multiple of 16. The filenames will be created in the form <x>_<y>_<filename>, where <x> and <y> a multiple of 16. The filenames will be created in the form <x>_<y>_<filename>, where <x> and <y>
are the tile numbers and <filename> is the name specified with -o. Skip empty tiles by also specifying --noemptyimage. are the tile numbers and <filename> is the name specified with -o. Skip empty tiles by also specifying --noemptyimage.
.TP
.BR \-\-leaflet " " \fIleaflet\fR
Together with --tilesize, output tiles in a leaflet-compaitble numbering and also output a html file to display the map.
Copy the files in the Leaflet subfolder to the same folder as your map.
.TP
.BR \-\-buildpyramid " " \fIbuildpyramid\fR
Together with --leaflet, build a zoom pyramid for faster zooming/loading of the generated map.
.TP .TP
.BR \-\-extent " " \fIextent\fR .BR \-\-extent " " \fIextent\fR
Dont render the image, just print the extent of the map that would be generated, in the same format as the geometry above. Dont render the image, just print the extent of the map that would be generated, in the same format as the geometry above.