Add utility to create zoom pyramids and write a leaflet html file.
parent
45f285218d
commit
fa41e7bf01
|
@ -0,0 +1,11 @@
|
|||
usage: buildpyramid <metadatafile> <outputname>
|
||||
|
||||
Reads the metadata file (output by minetestmapper) describing how many tiles are available and writes a full zoom pyramid and some HTML code for use with leaflet.js to display a 'slippy' map.
|
||||
just copy leaflet.js and leaflet.css form leaflet into the same folder and you whould have a working map.
|
||||
|
||||
Outputname should end in .jpg (recommended) or .png.
|
||||
|
||||
|
||||
|
||||
buildpyramid can't handle subdirectories. run it from the directory containing your minetestmapper output. it will write your map to the same folder.
|
||||
|
|
@ -0,0 +1,266 @@
|
|||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include "../include/Image.h"
|
||||
|
||||
void outputLeafletCode(std::string const &output, int maxLevel, int tileSize);
|
||||
|
||||
int buildPyramid(std::string const &baseName, std::string const &out, Image *im, int minTileX,int minTileY, int maxTileX, int maxTileY, int level, int divisor, bool leafletMode)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
Color empty(0,0,0,0);
|
||||
im->fill(empty, true);
|
||||
if ((maxTileX - minTileX) == 1)
|
||||
{
|
||||
assert((maxTileY - minTileY) == 1);
|
||||
std::ostringstream inf;
|
||||
inf << minTileX << "_" << minTileY << "_" << baseName;
|
||||
try
|
||||
{
|
||||
Image in(inf.str());
|
||||
|
||||
if (in.GetWidth())
|
||||
{
|
||||
in.blit(im, 0,0);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
catch (std::runtime_error const &e)
|
||||
{
|
||||
// image is allowed not to exist, just return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Image subTile(im->GetWidth(), im->GetHeight());
|
||||
|
||||
int halfX = minTileX + (maxTileX - minTileX)/2;
|
||||
int halfY = minTileY + (maxTileY - minTileY)/2;
|
||||
int c = buildPyramid(baseName, out, &subTile, minTileX, minTileY, halfX, halfY, level +1, divisor / 2, leafletMode);
|
||||
if (c)
|
||||
{
|
||||
subTile.scaleBlit(im, 0, im->GetHeight()/2, im->GetWidth()/2, im->GetHeight() / 2);
|
||||
count+=c;
|
||||
}
|
||||
|
||||
c = buildPyramid(baseName, out, &subTile, halfX, minTileY, maxTileX, halfY, level +1, divisor / 2, leafletMode);
|
||||
if (c)
|
||||
{
|
||||
subTile.scaleBlit(im, im->GetWidth()/2, im->GetHeight()/2, im->GetWidth()/2, im->GetHeight() / 2);
|
||||
count+=c;
|
||||
}
|
||||
|
||||
c = buildPyramid(baseName, out, &subTile, minTileX, halfY, halfX, maxTileY, level +1, divisor / 2, leafletMode);
|
||||
if (c)
|
||||
{
|
||||
subTile.scaleBlit(im, 0, 0, im->GetWidth()/2, im->GetHeight() / 2);
|
||||
count+=c;
|
||||
}
|
||||
|
||||
|
||||
c= buildPyramid(baseName, out, &subTile, halfX, halfY, maxTileX, maxTileY, level +1, divisor / 2, leafletMode);
|
||||
if (c)
|
||||
{
|
||||
subTile.scaleBlit(im, im->GetWidth()/2, 0, im->GetWidth()/2, im->GetHeight() / 2);
|
||||
count+=c;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (count)
|
||||
{
|
||||
std::ostringstream of;
|
||||
|
||||
of << level << "_" << (minTileX / divisor) << "_" << ((leafletMode ? -1 : 1) * minTileY/divisor - (leafletMode ? 1 : 0)) << "_" << out;
|
||||
|
||||
std::cout << "Writing image: " << of.str() << std::endl;
|
||||
im->save(of.str());
|
||||
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc !=3)
|
||||
{
|
||||
std::cerr << "Usage: buildpyramid <metadatafile> <outname>\n" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
std::ifstream mt;
|
||||
mt.open(argv[1], std::ios::in);
|
||||
|
||||
if (!mt.is_open())
|
||||
{
|
||||
std::cerr << "Couldn't open file '\n" << argv[1] << "'" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
std::string baseName;
|
||||
int numTilesX, numTilesY, minTileX, minTileY, tileSizeX, tileSizeY;
|
||||
int count=0;
|
||||
while (true)
|
||||
{
|
||||
|
||||
std::string label;
|
||||
|
||||
mt >> label;
|
||||
|
||||
if (mt.eof())
|
||||
{
|
||||
std::cerr << "Error parsing metadata file\n" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (label == "BaseName:")
|
||||
{
|
||||
mt >> baseName;
|
||||
count++;
|
||||
}
|
||||
else if (label == "NumTiles:")
|
||||
{
|
||||
mt >> numTilesX >> numTilesY;
|
||||
count++;
|
||||
}
|
||||
else if (label == "MinTile:")
|
||||
{
|
||||
mt >> minTileX >> minTileY;
|
||||
count++;
|
||||
}
|
||||
else if (label == "TileSize:")
|
||||
{
|
||||
mt >> tileSizeX >> tileSizeY;
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count >3)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (tileSizeX != tileSizeY)
|
||||
{
|
||||
std::cerr << "Can't handle non-square tiles." << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int maxDim = numTilesX + minTileX;
|
||||
|
||||
maxDim = -minTileX > maxDim ? -minTileX : maxDim;
|
||||
|
||||
maxDim = numTilesY + minTileY > maxDim ? numTilesY - minTileY : maxDim;
|
||||
|
||||
maxDim = -minTileY > maxDim ? -minTileY : maxDim;
|
||||
|
||||
assert(maxDim >0);
|
||||
|
||||
int maxLevel;
|
||||
// round up to power of 2
|
||||
for (int i = 0; i < 62 ; i++)
|
||||
{
|
||||
if ((1 << i) >= maxDim)
|
||||
{
|
||||
maxDim = 1<<i;
|
||||
maxLevel = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Image im(tileSizeX, tileSizeY);
|
||||
std::ostringstream of;
|
||||
std::string out(argv[2]);
|
||||
|
||||
buildPyramid(baseName, out, &im, 0,0, maxDim, maxDim, 0, maxDim, true);
|
||||
buildPyramid(baseName, out, &im, -maxDim,0, 0, maxDim, 0, maxDim, true);
|
||||
buildPyramid(baseName, out, &im, -maxDim,-maxDim, 0,0, 0, maxDim, true);
|
||||
buildPyramid(baseName, out, &im, 0,-maxDim, maxDim, 0, 0, maxDim, true);
|
||||
|
||||
|
||||
|
||||
outputLeafletCode(out, maxLevel, tileSizeX);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static char const *leafletMapHtml =
|
||||
"<!DOCTYPE html>\n"
|
||||
"<html>\n"
|
||||
"<head>\n"
|
||||
"\t<title>MinetestMapper</title>\n"
|
||||
"\t<meta charset=\"utf-8\" />\n"
|
||||
"\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n"
|
||||
"\t<!-- link rel=\"shortcut icon\" type=\"image/x-icon\" href=\"favicon.ico\" /-->\n"
|
||||
"\t<link rel=\"stylesheet\" href=\"leaflet.css\" integrity=\"sha512-puBpdR0798OZvTTbP4A8Ix/l+A4dHDD0DGqYW6RQ+9jxkRFclaxxQb/SJAWZfWAkuyeQUytO7+7N4QKrDh+drA==\" crossorigin=\"\"/>\n"
|
||||
"\t<script src=\"leaflet.js\" integrity=\"sha512-nMMmRyTVoLYqjP9hrbed9S+FzjZHW5gY1TWCHA5ckwXZBadntCNs8kEqAWdrb9O7rxbCaA4lKTIWjDXZxflOcA==\" crossorigin=\"\"></script>\n"
|
||||
"\t<style> .labelclass{position: absolute; background: rgba(255,0,255,0); font-size:20px;}</style>\n"
|
||||
"</head>\n"
|
||||
"<body>\n"
|
||||
"<div id=\"mapid\" style=\"width: 90vw; height: 90vh;\"></div>\n"
|
||||
"<script>\n"
|
||||
"\tvar MineTestMap = L.map('mapid', {\n"
|
||||
"\tcrs: L.CRS.Simple,\n"
|
||||
"\t});\n"
|
||||
"\tMineTestMap.setView([0.0, 0.0], %d);\n"
|
||||
"\tL.tileLayer('{z}_{x}_{y}_%s', {\n"
|
||||
"\t\tminNativeZoom: 0,\n"
|
||||
"\t\tmaxNativeZoom: %d,\n"
|
||||
"\t\tattribution: 'Minetest World',\n"
|
||||
"\t\ttileSize: %d,\n"
|
||||
"\t\terrorTileUrl: \"empty_tile_%s\",\n"
|
||||
"\t\t}).addTo(MineTestMap);\n"
|
||||
"\tvar popup = L.popup();\n"
|
||||
"\tvar mapZoom = %f;\n"
|
||||
"\tfunction onMapClick(e) {\n"
|
||||
"\t\tvar scaledPos = L.latLng(e.latlng.lat / mapZoom, e.latlng.lng / mapZoom);\n"
|
||||
"\t\tpopup\n"
|
||||
"\t\t\t.setLatLng(e.latlng)\n"
|
||||
"\t\t\t.setContent(\"You clicked the map at \"+ scaledPos.toString())\n"
|
||||
"\t\t\t.openOn(MineTestMap);\n"
|
||||
"\t}\n"
|
||||
"\tMineTestMap.on('click', onMapClick);\n"
|
||||
"</script>\n"
|
||||
"<script src=\"markers.js\" defer></script>"
|
||||
"</body>\n"
|
||||
"</html>\n";
|
||||
|
||||
|
||||
void outputLeafletCode(std::string const &output, int maxLevel, int tileSize)
|
||||
{
|
||||
|
||||
|
||||
std::ostringstream fn;
|
||||
fn << output << ".html";
|
||||
|
||||
// I use fopen instead of ostr because I need fprintf to put the correct values into the
|
||||
// html static string above. ostream is nice and C++ and all, but formatted output handling
|
||||
// for streams is retarded. Use the best tool for the job.
|
||||
FILE *out = fopen(fn.str().c_str(), "w");
|
||||
|
||||
if (!out)
|
||||
{
|
||||
std::cout << "error opening file:" << fn.str() << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(out, leafletMapHtml, maxLevel, output.c_str(), maxLevel, tileSize, output.c_str(), 1.0/pow(2,maxLevel));
|
||||
|
||||
fclose(out);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
# automagic makefile with auto dependency generation
|
||||
# usage:
|
||||
# - fill out the needed info (object filesneeded libs CXX flags etc)
|
||||
# - run 'make fixbuild' the first time you use the makefile this creates the DEPDIR and OBJDIR directories
|
||||
# - run 'make depend' each time you added a new cpp to the project, after the dependency file is generated it will be kept up-to-date automatically
|
||||
# - 'make' will build your program
|
||||
# - make clean will delete the object files and the executable
|
||||
# - make veryclean will delete all generated files (also core files and *~ and *.bkp)
|
||||
|
||||
CPP_OBJECTS_BARE= ../Image buildpyramid
|
||||
|
||||
C_OBJECTS_BARE=
|
||||
|
||||
LIBS= -lgd
|
||||
|
||||
PROGNAME=buildpyramid
|
||||
|
||||
CC=gcc
|
||||
CXX=g++
|
||||
LD=g++
|
||||
|
||||
CFLAGS = -Wall -Werror -g -I ../include
|
||||
CXXFLAGS = $(CFLAGS)
|
||||
LDFLAGS = -g
|
||||
|
||||
-include presets.mk
|
||||
|
||||
RM=rm -f
|
||||
RMDIR=rm -rf
|
||||
.PHONY: clean depend veryclean fixbuild
|
||||
|
||||
OBJDIR=obj
|
||||
DEPDIR=dep
|
||||
|
||||
# if all you srcfiles are in one subdir, you can list it here to spare you the typing in CPP_OBJECTS_BARE
|
||||
# otherwise put . here for the current dir
|
||||
SRCDIR=.
|
||||
|
||||
####################################################################################################################################
|
||||
# no configuration beyond this point
|
||||
####################################################################################################################################
|
||||
|
||||
#create a unique filename from a path
|
||||
filefrompath=$(subst /,_,$(subst .,_,$(1)))
|
||||
|
||||
# how to create various filenames from the bare name
|
||||
objfile=$(OBJDIR)/$(call filefrompath,$(1)).o
|
||||
depfile=$(DEPDIR)/$(call filefrompath,$(1)).dep
|
||||
cppfile=$(SRCDIR)/$(1).cpp
|
||||
cfile=$(SRCDIR)/$(1).c
|
||||
|
||||
|
||||
# build a rule to create an object from a cpp file, input is the bare filename
|
||||
define makeobjrule
|
||||
$(call objfile,$(1)):$(call cppfile,$(1))
|
||||
$(CXX) $(CXXFLAGS) -c $$< -o $$@
|
||||
|
||||
endef
|
||||
|
||||
# build a rule to create an object from a c file, input is the bare filename
|
||||
define cmakeobjrule
|
||||
$(call objfile,$(1)):$(call cfile,$(1))
|
||||
$(CC) $(CFLAGS) -c $$< -o $$@
|
||||
|
||||
endef
|
||||
|
||||
|
||||
#build a rule to create a depfile from a cpp file, bare name is input
|
||||
define makedeprule
|
||||
$(call depfile,$(1)):$(call cppfile,$(1))
|
||||
$(CXX) $(CXXFLAGS) -c $$< -MM -MF $$@ -MQ $(call objfile,$(1))
|
||||
|
||||
endef
|
||||
|
||||
#build a rule to create a depfile from a cpp file, bare name is input
|
||||
define cmakedeprule
|
||||
$(call depfile,$(1)):$(call cfile,$(1))
|
||||
$(CC) $(CFLAGS) -c $$< -MM -MF $$@ -MQ $(call objfile,$(1))
|
||||
endef
|
||||
|
||||
|
||||
# generate a list of all cpp objectfiles
|
||||
CPPOBJECTS=$(foreach f,$(CPP_OBJECTS_BARE),$(call objfile,$(f)))
|
||||
|
||||
# generate a list of all c objectfiles
|
||||
COBJECTS=$(foreach f,$(C_OBJECTS_BARE),$(call objfile,$(f)))
|
||||
|
||||
|
||||
# generate a list of all depfile
|
||||
CPPDEPS=$(foreach f,$(CPP_OBJECTS_BARE),$(call depfile,$(f)))
|
||||
|
||||
# generate a list of all depfile
|
||||
CDEPS=$(foreach f,$(C_OBJECTS_BARE),$(call depfile,$(f)))
|
||||
|
||||
|
||||
# generate rules for all object files
|
||||
CPPOBJRULES=$(foreach f,$(CPP_OBJECTS_BARE),$(call makeobjrule,$(f)))
|
||||
|
||||
# generate rules for all object files
|
||||
COBJRULES=$(foreach f,$(C_OBJECTS_BARE),$(call cmakeobjrule,$(f)))
|
||||
|
||||
# generate rules for all DEP files
|
||||
CPPDEPRULES=$(foreach f,$(CPP_OBJECTS_BARE),$(call makedeprule,$(f)))
|
||||
|
||||
# generate rules for all DEP files
|
||||
CDEPRULES=$(foreach f,$(C_OBJECTS_BARE),$(call cmakedeprule,$(f)))
|
||||
|
||||
|
||||
$(PROGNAME) :fixbuild $(DEPDIR)/depsuptodate $(CPPOBJECTS) $(COBJECTS)
|
||||
$(LD) $(LDFLAGS) $(CPPOBJECTS) $(COBJECTS) $(LIBS) -o $(PROGNAME)
|
||||
|
||||
|
||||
depend: $(CPPDEPS) $(CDEPS) fixbuild
|
||||
# rm $(DEPDIR)/\*.dep
|
||||
|
||||
veryclean: clean
|
||||
$(RMDIR) dep obj
|
||||
$(RM) core core.* *~ *bkp
|
||||
|
||||
$(DEPDIR)/depsuptodate: depend makefile fixbuild
|
||||
touch $(DEPDIR)/depsuptodate
|
||||
|
||||
clean:
|
||||
$(RM) $(CPPOBJECTS) $(COBJECTS) $(PROGNAME)
|
||||
|
||||
fixbuild: $(DEPDIR) $(OBJDIR)
|
||||
|
||||
$(DEPDIR):
|
||||
mkdir -p $(DEPDIR)
|
||||
|
||||
$(OBJDIR):
|
||||
mkdir -p $(OBJDIR)
|
||||
|
||||
|
||||
$(eval $(CPPOBJRULES))
|
||||
$(eval $(CPPDEPRULES))
|
||||
|
||||
$(eval $(COBJRULES))
|
||||
$(eval $(CDEPRULES))
|
||||
|
||||
|
||||
-include $(DEPDIR)/*.dep
|
Loading…
Reference in New Issue