diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 87c0187c..5b53b4e6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,3 +21,12 @@ jobs: run: brew install sqlite3 - run: uname -a; BUILDTYPE=${{ matrix.version }} make - run: make test + + test-without-mlt: + runs-on: ubuntu-latest + steps: + # No submodules: the MLT=0 build must not need them + - uses: actions/checkout@v3 + - run: sudo apt-get install libsqlite3-dev + - run: uname -a; make MLT=0 + - run: make MLT=0 test diff --git a/Makefile b/Makefile index 44cf94bf..2bef7593 100644 --- a/Makefile +++ b/Makefile @@ -4,12 +4,36 @@ BUILDTYPE ?= Release BUILD_INFO ?= SHELL = /bin/sh +# MapLibre Tile support requires the maplibre-tile-spec submodule and cmake +# to build it. Use `make MLT=0` for a build with no dependencies beyond the +# ones that Mapbox Vector Tiles need; it can neither read nor write MLT. +MLT ?= 1 + +MLT_BUILD_STAMP = mlt-build/.built + +ifeq ($(MLT),0) +MLT_FLAGS = -DNO_MLT +MLT_INCLUDES = +MLT_STD = +MLT_DEPS = +MLT_DECODE_LIBS = +MLT_LIBS = +MLT_TESTS = +else +MLT_FLAGS = +MLT_INCLUDES = -isystem maplibre-tile-spec/cpp/include -isystem maplibre-tile-spec/cpp/vendor/fsst +MLT_STD = -std=c++20 +MLT_DEPS = $(MLT_BUILD_STAMP) +MLT_DECODE_LIBS = mlt-build/libmlt-cpp.a mlt-build/libfsst-lib.a mlt-build/fastpfor/libFastPFOR.a +MLT_LIBS = mlt-build/libmlt-cpp-encoder.a $(MLT_DECODE_LIBS) +MLT_TESTS = mlt-test mlt-decode-test mlt-output-test +endif # inherit from env if set CC := $(CC) CXX := $(CXX) CFLAGS := $(CFLAGS) -fPIE -DBUILD_INFO=$(BUILD_INFO) -CXXFLAGS := $(CXXFLAGS) -std=c++17 -fPIE -DBUILD_INFO=$(BUILD_INFO) +CXXFLAGS := $(CXXFLAGS) -std=c++17 -fPIE -DBUILD_INFO=$(BUILD_INFO) $(MLT_FLAGS) LDFLAGS := $(LDFLAGS) WARNING_FLAGS := -Wall -Wshadow -Wsign-compare -Wextra -Wunreachable-code -Wuninitialized -Wshadow RELEASE_FLAGS := -O3 -DNDEBUG @@ -57,10 +81,6 @@ H = $(wildcard *.h) $(wildcard *.hpp) C = $(wildcard *.c) $(wildcard *.cpp) INCLUDES = -I/usr/local/include -I. -Iclipper2/include -MLT_INCLUDES = -isystem maplibre-tile-spec/cpp/include -isystem maplibre-tile-spec/cpp/vendor/fsst -MLT_DECODE_LIBS = mlt-build/libmlt-cpp.a mlt-build/libfsst-lib.a mlt-build/fastpfor/libFastPFOR.a -MLT_LIBS = mlt-build/libmlt-cpp-encoder.a $(MLT_DECODE_LIBS) -MLT_BUILD_STAMP = mlt-build/.built LIBS = -L/usr/local/lib $(MLT_BUILD_STAMP): maplibre-tile-spec/cpp/CMakeLists.txt @@ -76,7 +96,9 @@ $(MLT_BUILD_STAMP): maplibre-tile-spec/cpp/CMakeLists.txt cmake --build mlt-build --target mlt-cpp mlt-cpp-encoder $(if $(VERBOSE),,-- -s) > /dev/null touch $@ +ifneq ($(MLT),0) $(MLT_LIBS): $(MLT_BUILD_STAMP) +endif tippecanoe: geojson.o jsonpull/jsonpull.o tile.o pool.o mbtiles.o geometry.o projection.o memfile.o mvt.o mlt.o mlt_decode.o serial.o main.o platform.o text.o dirtiles.o pmtiles_file.o plugin.o read_json.o write_json.o geobuf.o flatgeobuf.o evaluator.o geocsv.o csv.o geojson-loop.o json_logger.o visvalingam.o compression.o clip.o sort.o attribute.o thread.o shared_borders.o clipper2/src/clipper.engine.o $(MLT_LIBS) $(CXX) $(PG) $(LIBS) $(FINAL_FLAGS) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) -lm -lz -lsqlite3 -lpthread @@ -104,8 +126,8 @@ tippecanoe-overzoom: overzoom.o mvt.o mlt.o mlt_decode.o clip.o evaluator.o json %.o: %.c $(CC) -MMD $(PG) $(INCLUDES) $(FINAL_FLAGS) $(CFLAGS) -c -o $@ $< -mlt.o mlt_decode.o: %.o: %.cpp $(MLT_BUILD_STAMP) - $(CXX) -MMD $(PG) $(INCLUDES) $(MLT_INCLUDES) $(FINAL_FLAGS) $(CXXFLAGS) -std=c++20 -c -o $@ $< +mlt.o mlt_decode.o: %.o: %.cpp $(MLT_DEPS) + $(CXX) -MMD $(PG) $(INCLUDES) $(MLT_INCLUDES) $(FINAL_FLAGS) $(CXXFLAGS) $(MLT_STD) -c -o $@ $< %.o: %.cpp $(CXX) -MMD $(PG) $(INCLUDES) $(FINAL_FLAGS) $(CXXFLAGS) -c -o $@ $< @@ -119,7 +141,7 @@ indent: TESTS = $(wildcard tests/*/out/*.json) SPACE = $(NULL) $(NULL) -test: tippecanoe tippecanoe-decode $(addsuffix .check,$(TESTS)) raw-tiles-test parallel-test pbf-test join-test enumerate-test decode-test join-filter-test unit json-tool-test allow-existing-test csv-test layer-json-test pmtiles-test decode-pmtiles-test overzoom-test flatgeobuf-test mlt-test mlt-decode-test mlt-output-test +test: tippecanoe tippecanoe-decode $(addsuffix .check,$(TESTS)) raw-tiles-test parallel-test pbf-test join-test enumerate-test decode-test join-filter-test unit json-tool-test allow-existing-test csv-test layer-json-test pmtiles-test decode-pmtiles-test overzoom-test flatgeobuf-test $(MLT_TESTS) ./unit suffixes = json json.gz diff --git a/README.md b/README.md index 6bb6f8a4..6676575d 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,12 @@ $ make -j $ make install ``` +The submodule is the [MapLibre Tile](https://github.com/maplibre/maplibre-tile-spec) +implementation, which is built with `cmake`. If you don't need MapLibre Tile support, +build with `make MLT=0` instead, which needs neither the submodule nor `cmake`. Such a +build can't read or write MLT, and rejects `--output-format=mlt`, but is otherwise the same. +Run `make clean` first if you are switching an existing build directory between the two. + See [Development](#development) below for how to upgrade your C++ compiler or install prerequisite packages if you get compiler errors. diff --git a/mlt.cpp b/mlt.cpp index 2ae35b60..853a2426 100644 --- a/mlt.cpp +++ b/mlt.cpp @@ -1,8 +1,6 @@ #include "mlt.hpp" #include "errors.hpp" -#include - #include #include #include @@ -12,7 +10,11 @@ #include #include +#ifndef NO_MLT +#include + using Vertex = mlt::Encoder::Vertex; +#endif int output_format = OUTPUT_MVT; bool mlt_sort_features = true; @@ -22,7 +24,12 @@ void set_output_format(char **argv, const char *format) { if (strcmp(format, "mvt") == 0 || strcmp(format, "pbf") == 0) { output_format = OUTPUT_MVT; } else if (strcmp(format, "mlt") == 0) { +#ifdef NO_MLT + fprintf(stderr, "%s: this build was compiled without MapLibre Tile support\n", argv[0]); + exit(EXIT_ARGS); +#else output_format = OUTPUT_MLT; +#endif } else { fprintf(stderr, "%s: --output-format must be 'mvt' or 'mlt'\n", argv[0]); exit(EXIT_ARGS); @@ -38,13 +45,19 @@ const char *tile_format_extension(int format) { } std::string encode_tile(mvt_tile &tile, int format) { +#ifndef NO_MLT if (format == OUTPUT_MLT) { return encode_as_mlt(tile, mlt_sort_features, mlt_pretessellate); - } else { - return tile.encode(); } +#else + (void) format; +#endif + + return tile.encode(); } +#ifndef NO_MLT + // An MLT property column has a single type for the whole layer, while MVT // values each carry their own type, so a type that can hold every value of // an attribute has to be chosen before the layer can be converted. @@ -327,3 +340,5 @@ std::string encode_as_mlt(const mvt_tile &tile, bool sort_features, bool pretess auto bytes = encoder.encode(layers, config); return std::string(reinterpret_cast(bytes.data()), bytes.size()); } + +#endif diff --git a/mlt.hpp b/mlt.hpp index 8369afbf..079388a7 100644 --- a/mlt.hpp +++ b/mlt.hpp @@ -24,14 +24,17 @@ const char *tile_format_extension(int format); // options were selected on the command line std::string encode_tile(mvt_tile &tile, int format); +#ifndef NO_MLT std::string encode_as_mlt(const mvt_tile &tile, bool sort_features, bool pretessellate); +#endif // Does this (already decompressed) tile look like MapLibre Tile rather than // Mapbox Vector Tile data? bool is_mlt(const std::string &message); // Decode a MapLibre Tile into the equivalent vector tile, returning false -// (and complaining to stderr) if it can't be parsed. +// (and complaining to stderr) if it can't be parsed, or if this build +// doesn't have MLT support compiled in. bool decode_mlt(const std::string &message, mvt_tile &out); #endif diff --git a/mlt_decode.cpp b/mlt_decode.cpp index 4e501c8f..97dd6f34 100644 --- a/mlt_decode.cpp +++ b/mlt_decode.cpp @@ -1,11 +1,5 @@ #include "mlt.hpp" -#include -#include -#include -#include -#include - #include #include #include @@ -18,6 +12,14 @@ #include #include +#ifndef NO_MLT +#include +#include +#include +#include +#include +#endif + namespace { bool read_varint(const char *&p, const char *end, unsigned long long &out) { @@ -39,6 +41,8 @@ bool read_varint(const char *&p, const char *end, unsigned long long &out) { return false; } +#ifndef NO_MLT + long long round_coord(float v) { return (long long) std::llround(v); } @@ -237,6 +241,8 @@ void convert_layer(const mlt::Layer &in, mvt_layer &out, const std::shared_ptr