mirror of
https://github.com/felt/tippecanoe.git
synced 2026-10-02 08:25:40 +02:00
Add support for pmtiles output format (#55)
* add pmtiles.hpp from github.com/protomaps/PMTiles [#10] * tippecanoe main writes pmtiles output. [#10] * detect output format using suffix * after mbtiles is done writing, replace with pmtiles based on map/image tables. * add method to write_json for writing json sub-object. * tippecanoe-decode reads pmtiles input. [#10] * tile-join reads and writes pmtiles. [#10] * pmtiles test suite for decode and tile-join [#10] * add base GitHub CI action for compiling and test suite. * update pmtiles.hpp with z>15 fix * Fix some ordering problems with pmtiles decode * Pmtiles should also pass the raw tiles tests * Eradicate spaces from tileset metadata JSON fields * Eradicate spaces from more test fixtures * Update more tests * Pmtiles tests pass now too * Remove unnecessary sort (and make indent) * Update changelog * The allow-existing test for pmtiles needs -o, not -e * Declare --allow-existing to be unsupported for pmtiles. It was always a bad idea even for mbtiles. Co-authored-by: Brandon Liu <bdon@bdon.org>
This commit is contained in:
co-authored by
Brandon Liu
parent
a374263466
commit
3b2599f587
@@ -0,0 +1,11 @@
|
|||||||
|
name: test
|
||||||
|
|
||||||
|
on: [push]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- run: make
|
||||||
|
- run: make test
|
||||||
@@ -1,3 +1,7 @@
|
|||||||
|
## 2.17.0
|
||||||
|
|
||||||
|
* Add pmtiles output format
|
||||||
|
|
||||||
## 2.16.0
|
## 2.16.0
|
||||||
|
|
||||||
* During tiling, limit the size of the statistics that are kept for -as-needed calculations, because they can get quite large for sources with hundreds of millions of features.
|
* During tiling, limit the size of the statistics that are kept for -as-needed calculations, because they can get quite large for sources with hundreds of millions of features.
|
||||||
|
|||||||
@@ -47,16 +47,16 @@ C = $(wildcard *.c) $(wildcard *.cpp)
|
|||||||
INCLUDES = -I/usr/local/include -I.
|
INCLUDES = -I/usr/local/include -I.
|
||||||
LIBS = -L/usr/local/lib
|
LIBS = -L/usr/local/lib
|
||||||
|
|
||||||
tippecanoe: geojson.o jsonpull/jsonpull.o tile.o pool.o mbtiles.o geometry.o projection.o memfile.o mvt.o serial.o main.o text.o dirtiles.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
|
tippecanoe: geojson.o jsonpull/jsonpull.o tile.o pool.o mbtiles.o geometry.o projection.o memfile.o mvt.o serial.o main.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
|
||||||
$(CXX) $(PG) $(LIBS) $(FINAL_FLAGS) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) -lm -lz -lsqlite3 -lpthread
|
$(CXX) $(PG) $(LIBS) $(FINAL_FLAGS) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) -lm -lz -lsqlite3 -lpthread
|
||||||
|
|
||||||
tippecanoe-enumerate: enumerate.o
|
tippecanoe-enumerate: enumerate.o
|
||||||
$(CXX) $(PG) $(LIBS) $(FINAL_FLAGS) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) -lsqlite3
|
$(CXX) $(PG) $(LIBS) $(FINAL_FLAGS) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) -lsqlite3
|
||||||
|
|
||||||
tippecanoe-decode: decode.o projection.o mvt.o write_json.o text.o jsonpull/jsonpull.o dirtiles.o
|
tippecanoe-decode: decode.o projection.o mvt.o write_json.o text.o jsonpull/jsonpull.o dirtiles.o pmtiles_file.o
|
||||||
$(CXX) $(PG) $(LIBS) $(FINAL_FLAGS) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) -lm -lz -lsqlite3
|
$(CXX) $(PG) $(LIBS) $(FINAL_FLAGS) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) -lm -lz -lsqlite3
|
||||||
|
|
||||||
tile-join: tile-join.o projection.o pool.o mbtiles.o mvt.o memfile.o dirtiles.o jsonpull/jsonpull.o text.o evaluator.o csv.o write_json.o
|
tile-join: tile-join.o projection.o pool.o mbtiles.o mvt.o memfile.o dirtiles.o jsonpull/jsonpull.o text.o evaluator.o csv.o write_json.o pmtiles_file.o
|
||||||
$(CXX) $(PG) $(LIBS) $(FINAL_FLAGS) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) -lm -lz -lsqlite3 -lpthread
|
$(CXX) $(PG) $(LIBS) $(FINAL_FLAGS) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) -lm -lz -lsqlite3 -lpthread
|
||||||
|
|
||||||
tippecanoe-json-tool: jsontool.o jsonpull/jsonpull.o csv.o text.o geojson-loop.o
|
tippecanoe-json-tool: jsontool.o jsonpull/jsonpull.o csv.o text.o geojson-loop.o
|
||||||
@@ -82,7 +82,7 @@ indent:
|
|||||||
TESTS = $(wildcard tests/*/out/*.json)
|
TESTS = $(wildcard tests/*/out/*.json)
|
||||||
SPACE = $(NULL) $(NULL)
|
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
|
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
|
||||||
./unit
|
./unit
|
||||||
|
|
||||||
suffixes = json json.gz
|
suffixes = json json.gz
|
||||||
@@ -156,6 +156,43 @@ raw-tiles-test:
|
|||||||
cmp tests/raw-tiles/nothing.json.check tests/raw-tiles/nothing.json
|
cmp tests/raw-tiles/nothing.json.check tests/raw-tiles/nothing.json
|
||||||
rm -r tests/raw-tiles/nothing tests/raw-tiles/nothing.json.check
|
rm -r tests/raw-tiles/nothing tests/raw-tiles/nothing.json.check
|
||||||
|
|
||||||
|
pmtiles-test:
|
||||||
|
./tippecanoe -q -f -o tests/pmtiles/hackspots.pmtiles -r1 -pC tests/raw-tiles/hackspots.geojson
|
||||||
|
./tippecanoe-decode -x generator tests/pmtiles/hackspots.pmtiles > tests/pmtiles/hackspots.json.check
|
||||||
|
cmp tests/pmtiles/hackspots.json.check tests/pmtiles/hackspots.json
|
||||||
|
# Test generating pmtiles first and then converting to mbtiles with tile-join.
|
||||||
|
./tile-join -q -f -pC -o tests/pmtiles/joined.mbtiles tests/pmtiles/hackspots.pmtiles
|
||||||
|
./tippecanoe-decode -x generator tests/pmtiles/joined.mbtiles > tests/pmtiles/joined.json.check
|
||||||
|
cmp tests/pmtiles/joined.json.check tests/pmtiles/joined.json
|
||||||
|
rm -r tests/pmtiles/hackspots.json.check tests/pmtiles/hackspots.pmtiles
|
||||||
|
|
||||||
|
# Test generating mbtiles first and then converting to pmtiles with tile-join. (Changes bounds)
|
||||||
|
./tippecanoe -q -f -o tests/pmtiles/hackspots.mbtiles -r1 -pC tests/raw-tiles/hackspots.geojson
|
||||||
|
./tile-join -q -f -pC -o tests/pmtiles/joined.pmtiles tests/pmtiles/hackspots.mbtiles
|
||||||
|
|
||||||
|
# decode changes order (ZXY vs TMS order)
|
||||||
|
./tippecanoe-decode -x generator tests/pmtiles/joined.pmtiles > tests/pmtiles/joined_reordered.json.check
|
||||||
|
cmp tests/pmtiles/joined_reordered.json.check tests/pmtiles/joined_reordered.json
|
||||||
|
rm -r tests/pmtiles/joined_reordered.json.check tests/pmtiles/hackspots.mbtiles tests/pmtiles/joined.pmtiles
|
||||||
|
|
||||||
|
# From raw-tiles-test:
|
||||||
|
./tippecanoe -q -f -o tests/raw-tiles/raw-tiles.pmtiles -r1 -pC tests/raw-tiles/hackspots.geojson
|
||||||
|
./tippecanoe-decode -x generator tests/raw-tiles/raw-tiles.pmtiles | sed 's/\.pmtiles//g' | sed 's/ -o / -e /g' > tests/raw-tiles/raw-tiles.json.check
|
||||||
|
cmp tests/raw-tiles/raw-tiles.json.check tests/raw-tiles/raw-tiles.json
|
||||||
|
# Test that -z and -Z work in tippecanoe-decode
|
||||||
|
./tippecanoe-decode -x generator -Z6 -z7 tests/raw-tiles/raw-tiles.pmtiles | sed 's/\.pmtiles//g' | sed 's/ -o / -e /g' > tests/raw-tiles/raw-tiles-z67.json.check
|
||||||
|
cmp tests/raw-tiles/raw-tiles-z67.json.check tests/raw-tiles/raw-tiles-z67.json
|
||||||
|
# Test that -z and -Z work in tile-join
|
||||||
|
./tile-join -q -f -Z6 -z7 -o tests/raw-tiles/raw-tiles-z67.pmtiles tests/raw-tiles/raw-tiles.pmtiles
|
||||||
|
./tippecanoe-decode -x generator tests/raw-tiles/raw-tiles-z67.pmtiles | sed 's/\.pmtiles//g' | sed 's/ -o / -e /g' > tests/raw-tiles/raw-tiles-z67-join.json.check
|
||||||
|
cmp tests/raw-tiles/raw-tiles-z67-join.json.check tests/raw-tiles/raw-tiles-z67-join.json
|
||||||
|
rm -rf tests/raw-tiles/raw-tiles.pmtiles tests/raw-tiles/raw-tiles-z67.pmtiles tests/raw-tiles/raw-tiles.json.check raw-tiles-z67.json.check tests/raw-tiles/raw-tiles-z67-join.json.check
|
||||||
|
# Test that metadata.json is created even if all features are clipped away
|
||||||
|
./tippecanoe -q -f -o tests/raw-tiles/nothing.pmtiles tests/raw-tiles/nothing.geojson
|
||||||
|
./tippecanoe-decode -x generator tests/raw-tiles/nothing.pmtiles | sed 's/\.pmtiles//g' | sed 's/ -o / -e /g' > tests/raw-tiles/nothing.json.check
|
||||||
|
cmp tests/raw-tiles/nothing.json.check tests/raw-tiles/nothing.json
|
||||||
|
rm -r tests/raw-tiles/nothing.pmtiles tests/raw-tiles/nothing.json.check
|
||||||
|
|
||||||
decode-test:
|
decode-test:
|
||||||
mkdir -p tests/muni/decode
|
mkdir -p tests/muni/decode
|
||||||
./tippecanoe -q -z11 -Z11 -f -o tests/muni/decode/multi.mbtiles tests/muni/*.json
|
./tippecanoe -q -z11 -Z11 -f -o tests/muni/decode/multi.mbtiles tests/muni/*.json
|
||||||
@@ -173,6 +210,23 @@ decode-test:
|
|||||||
cmp tests/muni/decode/multi.mbtiles.stats.json.check tests/muni/decode/multi.mbtiles.stats.json
|
cmp tests/muni/decode/multi.mbtiles.stats.json.check tests/muni/decode/multi.mbtiles.stats.json
|
||||||
rm -f tests/muni/decode/multi.mbtiles.json.check tests/muni/decode/multi.mbtiles tests/muni/decode/multi.mbtiles.pipeline.json.check tests/muni/decode/multi.mbtiles.stats.json.check tests/muni/decode/multi.mbtiles.onetile.json.check
|
rm -f tests/muni/decode/multi.mbtiles.json.check tests/muni/decode/multi.mbtiles tests/muni/decode/multi.mbtiles.pipeline.json.check tests/muni/decode/multi.mbtiles.stats.json.check tests/muni/decode/multi.mbtiles.onetile.json.check
|
||||||
|
|
||||||
|
decode-pmtiles-test:
|
||||||
|
mkdir -p tests/muni/decode
|
||||||
|
./tippecanoe -q -z11 -Z11 -f -o tests/muni/decode/multi.pmtiles tests/muni/*.json
|
||||||
|
./tippecanoe-decode -x generator -l subway tests/muni/decode/multi.pmtiles | sed 's/pmtiles/mbtiles/g' > tests/muni/decode/multi.pmtiles.json.check
|
||||||
|
./tippecanoe-decode -x generator -l subway --integer tests/muni/decode/multi.pmtiles | sed 's/pmtiles/mbtiles/g' > tests/muni/decode/multi.pmtiles.integer.json.check
|
||||||
|
./tippecanoe-decode -x generator -l subway --fraction tests/muni/decode/multi.pmtiles | sed 's/pmtiles/mbtiles/g' > tests/muni/decode/multi.pmtiles.fraction.json.check
|
||||||
|
./tippecanoe-decode -x generator -c tests/muni/decode/multi.pmtiles | sed 's/pmtiles/mbtiles/g' > tests/muni/decode/multi.pmtiles.pipeline.json.check
|
||||||
|
./tippecanoe-decode -x generator tests/muni/decode/multi.pmtiles 11 327 791 | sed 's/pmtiles/mbtiles/g' > tests/muni/decode/multi.pmtiles.onetile.json.check
|
||||||
|
./tippecanoe-decode -x generator --stats tests/muni/decode/multi.pmtiles | sed 's/pmtiles/mbtiles/g' > tests/muni/decode/multi.pmtiles.stats.json.check
|
||||||
|
cmp tests/muni/decode/multi.pmtiles.json.check tests/muni/decode/multi.mbtiles.json
|
||||||
|
cmp tests/muni/decode/multi.pmtiles.integer.json.check tests/muni/decode/multi.mbtiles.integer.json
|
||||||
|
cmp tests/muni/decode/multi.pmtiles.fraction.json.check tests/muni/decode/multi.mbtiles.fraction.json
|
||||||
|
cmp tests/muni/decode/multi.pmtiles.pipeline.json.check tests/muni/decode/multi.mbtiles.pipeline.json
|
||||||
|
cmp tests/muni/decode/multi.pmtiles.onetile.json.check tests/muni/decode/multi.mbtiles.onetile.json
|
||||||
|
cmp tests/muni/decode/multi.pmtiles.stats.json.check tests/muni/decode/multi.mbtiles.stats.json
|
||||||
|
rm -f tests/muni/decode/multi.pmtiles.json.check tests/muni/decode/multi.pmtiles tests/muni/decode/multi.pmtiles.pipeline.json.check tests/muni/decode/multi.pmtiles.stats.json.check tests/muni/decode/multi.pmtiles.onetile.json.check
|
||||||
|
|
||||||
pbf-test:
|
pbf-test:
|
||||||
./tippecanoe-decode -x generator tests/pbf/11-328-791.vector.pbf 11 328 791 > tests/pbf/11-328-791.vector.pbf.out
|
./tippecanoe-decode -x generator tests/pbf/11-328-791.vector.pbf 11 328 791 > tests/pbf/11-328-791.vector.pbf.out
|
||||||
cmp tests/pbf/11-328-791.json tests/pbf/11-328-791.vector.pbf.out
|
cmp tests/pbf/11-328-791.json tests/pbf/11-328-791.vector.pbf.out
|
||||||
@@ -318,7 +372,15 @@ allow-existing-test:
|
|||||||
./tippecanoe -q -Z10 -z11 -F -e tests/allow-existing/both.dir tests/coalesce-tract/tl_2010_06001_tract10.json
|
./tippecanoe -q -Z10 -z11 -F -e tests/allow-existing/both.dir tests/coalesce-tract/tl_2010_06001_tract10.json
|
||||||
./tippecanoe-decode -x generator -x generator_options tests/allow-existing/both.dir | sed 's/both\.dir/both.mbtiles/g' > tests/allow-existing/both.dir.json.check
|
./tippecanoe-decode -x generator -x generator_options tests/allow-existing/both.dir | sed 's/both\.dir/both.mbtiles/g' > tests/allow-existing/both.dir.json.check
|
||||||
cmp tests/allow-existing/both.dir.json.check tests/allow-existing/both.mbtiles.json
|
cmp tests/allow-existing/both.dir.json.check tests/allow-existing/both.mbtiles.json
|
||||||
rm -r tests/allow-existing/both.dir.json.check tests/allow-existing/both.dir tests/allow-existing/both.mbtiles.json.check tests/allow-existing/both.mbtiles
|
# Make a tileset
|
||||||
|
./tippecanoe -q -Z0 -z0 -f -o tests/allow-existing/both.pmtiles tests/coalesce-tract/tl_2010_06001_tract10.json
|
||||||
|
# Writing to existing should fail
|
||||||
|
if ./tippecanoe -q -Z1 -z1 -o tests/allow-existing/both.pmtiles tests/coalesce-tract/tl_2010_06001_tract10.json; then exit 1; else exit 0; fi
|
||||||
|
# Replace existing
|
||||||
|
./tippecanoe -q -Z8 -z9 -f -o tests/allow-existing/both.pmtiles tests/coalesce-tract/tl_2010_06001_tract10.json
|
||||||
|
# Allow-existing is not supported for pmtiles
|
||||||
|
if ./tippecanoe -q -Z10 -z11 -F -o tests/allow-existing/both.pmtiles tests/coalesce-tract/tl_2010_06001_tract10.json; then exit 1; else exit 0; fi
|
||||||
|
rm -r tests/allow-existing/both.pmtiles tests/allow-existing/both.dir.json.check tests/allow-existing/both.dir tests/allow-existing/both.mbtiles.json.check tests/allow-existing/both.mbtiles
|
||||||
|
|
||||||
csv-test:
|
csv-test:
|
||||||
# Reading from named CSV
|
# Reading from named CSV
|
||||||
|
|||||||
+55
-3
@@ -15,7 +15,6 @@
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <protozero/pbf_reader.hpp>
|
#include <protozero/pbf_reader.hpp>
|
||||||
#include <sys/stat.h>
|
|
||||||
#include "mvt.hpp"
|
#include "mvt.hpp"
|
||||||
#include "projection.hpp"
|
#include "projection.hpp"
|
||||||
#include "geometry.hpp"
|
#include "geometry.hpp"
|
||||||
@@ -23,6 +22,7 @@
|
|||||||
#include "jsonpull/jsonpull.h"
|
#include "jsonpull/jsonpull.h"
|
||||||
#include "mbtiles.hpp"
|
#include "mbtiles.hpp"
|
||||||
#include "dirtiles.hpp"
|
#include "dirtiles.hpp"
|
||||||
|
#include "pmtiles_file.hpp"
|
||||||
#include "errors.hpp"
|
#include "errors.hpp"
|
||||||
|
|
||||||
int minzoom = 0;
|
int minzoom = 0;
|
||||||
@@ -245,7 +245,7 @@ void decode(char *fname, int z, unsigned x, unsigned y, std::set<std::string> co
|
|||||||
if (st.st_size < 50 * 1024 * 1024) {
|
if (st.st_size < 50 * 1024 * 1024) {
|
||||||
char *map = (char *) mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
char *map = (char *) mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||||
if (map != NULL && map != MAP_FAILED) {
|
if (map != NULL && map != MAP_FAILED) {
|
||||||
if (strcmp(map, "SQLite format 3") != 0) {
|
if (strcmp(map, "SQLite format 3") != 0 && strncmp(map, "PMTiles", 7) != 0) {
|
||||||
if (z >= 0) {
|
if (z >= 0) {
|
||||||
std::string s = std::string(map, st.st_size);
|
std::string s = std::string(map, st.st_size);
|
||||||
handle(s, z, x, y, to_decode, pipeline, stats, state, coordinate_mode);
|
handle(s, z, x, y, to_decode, pipeline, stats, state, coordinate_mode);
|
||||||
@@ -272,11 +272,30 @@ void decode(char *fname, int z, unsigned x, unsigned y, std::set<std::string> co
|
|||||||
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
std::vector<zxy> tiles;
|
std::vector<zxy> tiles;
|
||||||
|
|
||||||
|
char *pmtiles_map;
|
||||||
|
std::vector<pmtiles::entry_zxy> entries;
|
||||||
|
bool is_pmtiles = false;
|
||||||
|
|
||||||
if (stat(fname, &st) == 0 && (st.st_mode & S_IFDIR) != 0) {
|
if (stat(fname, &st) == 0 && (st.st_mode & S_IFDIR) != 0) {
|
||||||
isdir = true;
|
isdir = true;
|
||||||
|
|
||||||
db = dirmeta2tmp(fname);
|
db = dirmeta2tmp(fname);
|
||||||
tiles = enumerate_dirtiles(fname, minzoom, maxzoom);
|
tiles = enumerate_dirtiles(fname, minzoom, maxzoom);
|
||||||
|
} else if (pmtiles_has_suffix(fname)) {
|
||||||
|
int pmtiles_fd = open(fname, O_RDONLY | O_CLOEXEC);
|
||||||
|
pmtiles_map = (char *) mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, pmtiles_fd, 0);
|
||||||
|
if (pmtiles_map == MAP_FAILED) {
|
||||||
|
perror("mmap in decode");
|
||||||
|
exit(EXIT_MEMORY);
|
||||||
|
}
|
||||||
|
if (close(pmtiles_fd) != 0) {
|
||||||
|
perror("close");
|
||||||
|
exit(EXIT_CLOSE);
|
||||||
|
}
|
||||||
|
db = pmtilesmeta2tmp(fname, pmtiles_map);
|
||||||
|
entries = pmtiles_entries_tms(pmtiles_map, minzoom, maxzoom);
|
||||||
|
is_pmtiles = true;
|
||||||
} else {
|
} else {
|
||||||
if (sqlite3_open(fname, &db) != SQLITE_OK) {
|
if (sqlite3_open(fname, &db) != SQLITE_OK) {
|
||||||
fprintf(stderr, "%s: %s\n", fname, sqlite3_errmsg(db));
|
fprintf(stderr, "%s: %s\n", fname, sqlite3_errmsg(db));
|
||||||
@@ -382,6 +401,26 @@ void decode(char *fname, int z, unsigned x, unsigned y, std::set<std::string> co
|
|||||||
|
|
||||||
handle(s, tiles[i].z, tiles[i].x, tiles[i].y, to_decode, pipeline, stats, state, coordinate_mode);
|
handle(s, tiles[i].z, tiles[i].x, tiles[i].y, to_decode, pipeline, stats, state, coordinate_mode);
|
||||||
}
|
}
|
||||||
|
} else if (is_pmtiles) {
|
||||||
|
within = 0;
|
||||||
|
|
||||||
|
for (auto const &entry : entries) {
|
||||||
|
if (!pipeline && !stats) {
|
||||||
|
if (within) {
|
||||||
|
state.json_comma_newline();
|
||||||
|
}
|
||||||
|
within = 1;
|
||||||
|
}
|
||||||
|
if (stats) {
|
||||||
|
if (within) {
|
||||||
|
state.json_comma_newline();
|
||||||
|
}
|
||||||
|
within = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string s{pmtiles_map + entry.offset, entry.length};
|
||||||
|
handle(s, entry.z, entry.x, entry.y, to_decode, pipeline, stats, state, coordinate_mode);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const char *sql = "SELECT tile_data, zoom_level, tile_column, tile_row from tiles where zoom_level between ? and ? order by zoom_level, tile_column, tile_row;";
|
const char *sql = "SELECT tile_data, zoom_level, tile_column, tile_row from tiles where zoom_level between ? and ? order by zoom_level, tile_column, tile_row;";
|
||||||
sqlite3_stmt *stmt;
|
sqlite3_stmt *stmt;
|
||||||
@@ -447,6 +486,19 @@ void decode(char *fname, int z, unsigned x, unsigned y, std::set<std::string> co
|
|||||||
} else {
|
} else {
|
||||||
int handled = 0;
|
int handled = 0;
|
||||||
while (z >= 0 && !handled) {
|
while (z >= 0 && !handled) {
|
||||||
|
if (is_pmtiles) {
|
||||||
|
uint64_t tile_offset;
|
||||||
|
uint32_t tile_length;
|
||||||
|
std::tie(tile_offset, tile_length) = pmtiles_get_tile(pmtiles_map, z, x, y);
|
||||||
|
if (tile_length > 0) {
|
||||||
|
if (z != oz) {
|
||||||
|
fprintf(stderr, "%s: Warning: using tile %d/%u/%u instead of %d/%u/%u\n", fname, z, x, y, oz, ox, oy);
|
||||||
|
}
|
||||||
|
std::string s{pmtiles_map + tile_offset, tile_length};
|
||||||
|
handle(s, z, x, y, to_decode, pipeline, stats, state, coordinate_mode);
|
||||||
|
handled = 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
const char *sql = "SELECT tile_data from tiles where zoom_level = ? and tile_column = ? and tile_row = ?;";
|
const char *sql = "SELECT tile_data from tiles where zoom_level = ? and tile_column = ? and tile_row = ?;";
|
||||||
sqlite3_stmt *stmt;
|
sqlite3_stmt *stmt;
|
||||||
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) {
|
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) {
|
||||||
@@ -474,8 +526,8 @@ void decode(char *fname, int z, unsigned x, unsigned y, std::set<std::string> co
|
|||||||
handle(std::string(s, len), z, x, y, to_decode, pipeline, stats, state, coordinate_mode);
|
handle(std::string(s, len), z, x, y, to_decode, pipeline, stats, state, coordinate_mode);
|
||||||
handled = 1;
|
handled = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
z--;
|
z--;
|
||||||
x /= 2;
|
x /= 2;
|
||||||
|
|||||||
+1
-2
@@ -309,8 +309,7 @@ void dir_write_metadata(const char *outdir, const metadata &m) {
|
|||||||
out(state, "minzoom", std::to_string(m.minzoom));
|
out(state, "minzoom", std::to_string(m.minzoom));
|
||||||
out(state, "maxzoom", std::to_string(m.maxzoom));
|
out(state, "maxzoom", std::to_string(m.maxzoom));
|
||||||
out(state, "center", std::to_string(m.center_lon) + "," + std::to_string(m.center_lat) + "," + std::to_string(m.center_z));
|
out(state, "center", std::to_string(m.center_lon) + "," + std::to_string(m.center_lat) + "," + std::to_string(m.center_z));
|
||||||
out(state, "bounds", std::to_string(m.minlon) + "," + std::to_string(m.minlat) + "," +
|
out(state, "bounds", std::to_string(m.minlon) + "," + std::to_string(m.minlat) + "," + std::to_string(m.maxlon) + "," + std::to_string(m.maxlat));
|
||||||
std::to_string(m.maxlon) + "," + std::to_string(m.maxlat));
|
|
||||||
out(state, "type", m.type);
|
out(state, "type", m.type);
|
||||||
if (m.attribution.size() > 0) {
|
if (m.attribution.size() > 0) {
|
||||||
out(state, "attribution", m.attribution);
|
out(state, "attribution", m.attribution);
|
||||||
|
|||||||
@@ -45,6 +45,7 @@
|
|||||||
|
|
||||||
#include "jsonpull/jsonpull.h"
|
#include "jsonpull/jsonpull.h"
|
||||||
#include "mbtiles.hpp"
|
#include "mbtiles.hpp"
|
||||||
|
#include "pmtiles_file.hpp"
|
||||||
#include "tile.hpp"
|
#include "tile.hpp"
|
||||||
#include "pool.hpp"
|
#include "pool.hpp"
|
||||||
#include "projection.hpp"
|
#include "projection.hpp"
|
||||||
@@ -1155,7 +1156,7 @@ void choose_first_zoom(long long *file_bbox, std::vector<struct reader> &readers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int read_input(std::vector<source> &sources, char *fname, int maxzoom, int minzoom, int basezoom, double basezoom_marker_width, sqlite3 *outdb, const char *outdir, std::set<std::string> *exclude, std::set<std::string> *include, int exclude_all, json_object *filter, double droprate, int buffer, const char *tmpdir, double gamma, int read_parallel, int forcetable, const char *attribution, bool uses_gamma, long long *file_bbox, const char *prefilter, const char *postfilter, const char *description, bool guess_maxzoom, std::map<std::string, int> const *attribute_types, const char *pgm, std::map<std::string, attribute_op> const *attribute_accum, std::map<std::string, std::string> const &attribute_descriptions, std::string const &commandline, int minimum_maxzoom) {
|
std::pair<int, metadata> read_input(std::vector<source> &sources, char *fname, int maxzoom, int minzoom, int basezoom, double basezoom_marker_width, sqlite3 *outdb, const char *outdir, std::set<std::string> *exclude, std::set<std::string> *include, int exclude_all, json_object *filter, double droprate, int buffer, const char *tmpdir, double gamma, int read_parallel, int forcetable, const char *attribution, bool uses_gamma, long long *file_bbox, const char *prefilter, const char *postfilter, const char *description, bool guess_maxzoom, std::map<std::string, int> const *attribute_types, const char *pgm, std::map<std::string, attribute_op> const *attribute_accum, std::map<std::string, std::string> const &attribute_descriptions, std::string const &commandline, int minimum_maxzoom) {
|
||||||
int ret = EXIT_SUCCESS;
|
int ret = EXIT_SUCCESS;
|
||||||
|
|
||||||
std::vector<struct reader> readers;
|
std::vector<struct reader> readers;
|
||||||
@@ -1290,6 +1291,7 @@ int read_input(std::vector<source> &sources, char *fname, int maxzoom, int minzo
|
|||||||
".geojson",
|
".geojson",
|
||||||
".geobuf",
|
".geobuf",
|
||||||
".mbtiles",
|
".mbtiles",
|
||||||
|
".pmtiles",
|
||||||
".csv",
|
".csv",
|
||||||
".gz",
|
".gz",
|
||||||
};
|
};
|
||||||
@@ -2571,7 +2573,7 @@ int read_input(std::vector<source> &sources, char *fname, int maxzoom, int minzo
|
|||||||
dir_write_metadata(outdir, m);
|
dir_write_metadata(outdir, m);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return std::make_pair(ret, m);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool has_name(struct option *long_options, int *pl) {
|
static bool has_name(struct option *long_options, int *pl) {
|
||||||
@@ -3471,6 +3473,10 @@ int main(int argc, char **argv) {
|
|||||||
if (out_mbtiles != NULL) {
|
if (out_mbtiles != NULL) {
|
||||||
if (force) {
|
if (force) {
|
||||||
unlink(out_mbtiles);
|
unlink(out_mbtiles);
|
||||||
|
} else {
|
||||||
|
if (pmtiles_has_suffix(out_mbtiles)) {
|
||||||
|
check_pmtiles(out_mbtiles, argv, forcetable);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
outdb = mbtiles_open(out_mbtiles, argv, forcetable);
|
outdb = mbtiles_open(out_mbtiles, argv, forcetable);
|
||||||
@@ -3503,14 +3509,20 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
long long file_bbox[4] = {UINT_MAX, UINT_MAX, 0, 0};
|
long long file_bbox[4] = {UINT_MAX, UINT_MAX, 0, 0};
|
||||||
|
|
||||||
ret = read_input(sources, name ? name : out_mbtiles ? out_mbtiles
|
auto input_ret = read_input(sources, name ? name : out_mbtiles ? out_mbtiles
|
||||||
: out_dir,
|
: out_dir,
|
||||||
maxzoom, minzoom, basezoom, basezoom_marker_width, outdb, out_dir, &exclude, &include, exclude_all, filter, droprate, buffer, tmpdir, gamma, read_parallel, forcetable, attribution, gamma != 0, file_bbox, prefilter, postfilter, description, guess_maxzoom, &attribute_types, argv[0], &attribute_accum, attribute_descriptions, commandline, minimum_maxzoom);
|
maxzoom, minzoom, basezoom, basezoom_marker_width, outdb, out_dir, &exclude, &include, exclude_all, filter, droprate, buffer, tmpdir, gamma, read_parallel, forcetable, attribution, gamma != 0, file_bbox, prefilter, postfilter, description, guess_maxzoom, &attribute_types, argv[0], &attribute_accum, attribute_descriptions, commandline, minimum_maxzoom);
|
||||||
|
|
||||||
|
ret = std::get<0>(input_ret);
|
||||||
|
|
||||||
if (outdb != NULL) {
|
if (outdb != NULL) {
|
||||||
mbtiles_close(outdb, argv[0]);
|
mbtiles_close(outdb, argv[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pmtiles_has_suffix(out_mbtiles)) {
|
||||||
|
mbtiles_map_image_to_pmtiles(out_mbtiles, std::get<1>(input_ret), prevent[P_TILE_COMPRESSION] == 0, quiet, quiet_progress);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef MTRACE
|
#ifdef MTRACE
|
||||||
muntrace();
|
muntrace();
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+74
-9
@@ -216,14 +216,17 @@ void tilestats(std::map<std::string, layermap_entry> const &layermap1, size_t el
|
|||||||
lmv.push_back(layermap1);
|
lmv.push_back(layermap1);
|
||||||
std::map<std::string, layermap_entry> layermap = merge_layermaps(lmv, true);
|
std::map<std::string, layermap_entry> layermap = merge_layermaps(lmv, true);
|
||||||
|
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_hash();
|
state.json_write_hash();
|
||||||
|
|
||||||
state.nospace = true;
|
state.nospace = true;
|
||||||
state.json_write_string("layerCount");
|
state.json_write_string("layerCount");
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_unsigned(layermap.size());
|
state.json_write_unsigned(layermap.size());
|
||||||
|
|
||||||
state.nospace = true;
|
state.nospace = true;
|
||||||
state.json_write_string("layers");
|
state.json_write_string("layers");
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_array();
|
state.json_write_array();
|
||||||
|
|
||||||
for (auto layer : layermap) {
|
for (auto layer : layermap) {
|
||||||
@@ -232,10 +235,12 @@ void tilestats(std::map<std::string, layermap_entry> const &layermap1, size_t el
|
|||||||
|
|
||||||
state.nospace = true;
|
state.nospace = true;
|
||||||
state.json_write_string("layer");
|
state.json_write_string("layer");
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_string(layer.first);
|
state.json_write_string(layer.first);
|
||||||
|
|
||||||
state.nospace = true;
|
state.nospace = true;
|
||||||
state.json_write_string("count");
|
state.json_write_string("count");
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_unsigned(layer.second.points + layer.second.lines + layer.second.polygons);
|
state.json_write_unsigned(layer.second.points + layer.second.lines + layer.second.polygons);
|
||||||
|
|
||||||
std::string geomtype = "Polygon";
|
std::string geomtype = "Polygon";
|
||||||
@@ -247,6 +252,7 @@ void tilestats(std::map<std::string, layermap_entry> const &layermap1, size_t el
|
|||||||
|
|
||||||
state.nospace = true;
|
state.nospace = true;
|
||||||
state.json_write_string("geometry");
|
state.json_write_string("geometry");
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_string(geomtype);
|
state.json_write_string(geomtype);
|
||||||
|
|
||||||
size_t attrib_count = layer.second.file_keys.size();
|
size_t attrib_count = layer.second.file_keys.size();
|
||||||
@@ -256,6 +262,7 @@ void tilestats(std::map<std::string, layermap_entry> const &layermap1, size_t el
|
|||||||
|
|
||||||
state.nospace = true;
|
state.nospace = true;
|
||||||
state.json_write_string("attributeCount");
|
state.json_write_string("attributeCount");
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_unsigned(attrib_count);
|
state.json_write_unsigned(attrib_count);
|
||||||
|
|
||||||
state.nospace = true;
|
state.nospace = true;
|
||||||
@@ -275,6 +282,7 @@ void tilestats(std::map<std::string, layermap_entry> const &layermap1, size_t el
|
|||||||
|
|
||||||
state.nospace = true;
|
state.nospace = true;
|
||||||
state.json_write_string("attribute");
|
state.json_write_string("attribute");
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_string(attribute.first);
|
state.json_write_string(attribute.first);
|
||||||
|
|
||||||
size_t val_count = attribute.second.sample_values.size();
|
size_t val_count = attribute.second.sample_values.size();
|
||||||
@@ -284,6 +292,7 @@ void tilestats(std::map<std::string, layermap_entry> const &layermap1, size_t el
|
|||||||
|
|
||||||
state.nospace = true;
|
state.nospace = true;
|
||||||
state.json_write_string("count");
|
state.json_write_string("count");
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_unsigned(val_count);
|
state.json_write_unsigned(val_count);
|
||||||
|
|
||||||
int type = 0;
|
int type = 0;
|
||||||
@@ -305,10 +314,12 @@ void tilestats(std::map<std::string, layermap_entry> const &layermap1, size_t el
|
|||||||
|
|
||||||
state.nospace = true;
|
state.nospace = true;
|
||||||
state.json_write_string("type");
|
state.json_write_string("type");
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_string(type_str);
|
state.json_write_string(type_str);
|
||||||
|
|
||||||
state.nospace = true;
|
state.nospace = true;
|
||||||
state.json_write_string("values");
|
state.json_write_string("values");
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_array();
|
state.json_write_array();
|
||||||
|
|
||||||
size_t vals = 0;
|
size_t vals = 0;
|
||||||
@@ -317,11 +328,10 @@ void tilestats(std::map<std::string, layermap_entry> const &layermap1, size_t el
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.nospace = true;
|
|
||||||
|
|
||||||
if (value.type == mvt_double || value.type == mvt_bool) {
|
if (value.type == mvt_double || value.type == mvt_bool) {
|
||||||
vals++;
|
vals++;
|
||||||
|
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_stringified(value.string);
|
state.json_write_stringified(value.string);
|
||||||
} else {
|
} else {
|
||||||
std::string trunc = truncate16(value.string, 256);
|
std::string trunc = truncate16(value.string, 256);
|
||||||
@@ -329,6 +339,7 @@ void tilestats(std::map<std::string, layermap_entry> const &layermap1, size_t el
|
|||||||
if (trunc.size() == value.string.size()) {
|
if (trunc.size() == value.string.size()) {
|
||||||
vals++;
|
vals++;
|
||||||
|
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_string(value.string);
|
state.json_write_string(value.string);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -340,10 +351,12 @@ void tilestats(std::map<std::string, layermap_entry> const &layermap1, size_t el
|
|||||||
if ((type & (1 << mvt_double)) != 0) {
|
if ((type & (1 << mvt_double)) != 0) {
|
||||||
state.nospace = true;
|
state.nospace = true;
|
||||||
state.json_write_string("min");
|
state.json_write_string("min");
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_number(attribute.second.min);
|
state.json_write_number(attribute.second.min);
|
||||||
|
|
||||||
state.nospace = true;
|
state.nospace = true;
|
||||||
state.json_write_string("max");
|
state.json_write_string("max");
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_number(attribute.second.max);
|
state.json_write_number(attribute.second.max);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -368,60 +381,80 @@ std::string stringify_strategies(std::vector<strategy> const &strategies) {
|
|||||||
json_writer state(&out);
|
json_writer state(&out);
|
||||||
bool any = false;
|
bool any = false;
|
||||||
|
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_array();
|
state.json_write_array();
|
||||||
for (size_t i = 0; i < strategies.size(); i++) {
|
for (size_t i = 0; i < strategies.size(); i++) {
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_hash();
|
state.json_write_hash();
|
||||||
|
|
||||||
if (strategies[i].dropped_by_rate > 0) {
|
if (strategies[i].dropped_by_rate > 0) {
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_string("dropped_by_rate");
|
state.json_write_string("dropped_by_rate");
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_number(strategies[i].dropped_by_rate);
|
state.json_write_number(strategies[i].dropped_by_rate);
|
||||||
any = true;
|
any = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strategies[i].dropped_by_gamma > 0) {
|
if (strategies[i].dropped_by_gamma > 0) {
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_string("dropped_by_gamma");
|
state.json_write_string("dropped_by_gamma");
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_number(strategies[i].dropped_by_gamma);
|
state.json_write_number(strategies[i].dropped_by_gamma);
|
||||||
any = true;
|
any = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strategies[i].dropped_as_needed > 0) {
|
if (strategies[i].dropped_as_needed > 0) {
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_string("dropped_as_needed");
|
state.json_write_string("dropped_as_needed");
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_number(strategies[i].dropped_as_needed);
|
state.json_write_number(strategies[i].dropped_as_needed);
|
||||||
any = true;
|
any = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strategies[i].coalesced_as_needed > 0) {
|
if (strategies[i].coalesced_as_needed > 0) {
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_string("coalesced_as_needed");
|
state.json_write_string("coalesced_as_needed");
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_number(strategies[i].coalesced_as_needed);
|
state.json_write_number(strategies[i].coalesced_as_needed);
|
||||||
any = true;
|
any = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strategies[i].detail_reduced > 0) {
|
if (strategies[i].detail_reduced > 0) {
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_string("detail_reduced");
|
state.json_write_string("detail_reduced");
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_number(strategies[i].detail_reduced);
|
state.json_write_number(strategies[i].detail_reduced);
|
||||||
any = true;
|
any = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strategies[i].tiny_polygons > 0) {
|
if (strategies[i].tiny_polygons > 0) {
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_string("tiny_polygons");
|
state.json_write_string("tiny_polygons");
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_number(strategies[i].tiny_polygons);
|
state.json_write_number(strategies[i].tiny_polygons);
|
||||||
any = true;
|
any = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strategies[i].tile_size > 0) {
|
if (strategies[i].tile_size > 0) {
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_string("tile_size_desired");
|
state.json_write_string("tile_size_desired");
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_number(strategies[i].tile_size);
|
state.json_write_number(strategies[i].tile_size);
|
||||||
any = true;
|
any = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strategies[i].feature_count > 0) {
|
if (strategies[i].feature_count > 0) {
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_string("feature_count_desired");
|
state.json_write_string("feature_count_desired");
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_number(strategies[i].feature_count);
|
state.json_write_number(strategies[i].feature_count);
|
||||||
any = true;
|
any = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.nospace = true;
|
||||||
state.json_end_hash();
|
state.json_end_hash();
|
||||||
}
|
}
|
||||||
|
state.nospace = true;
|
||||||
state.json_end_array();
|
state.json_end_array();
|
||||||
|
|
||||||
if (any) {
|
if (any) {
|
||||||
@@ -556,21 +589,34 @@ void mbtiles_write_metadata(sqlite3 *db, const metadata &m, bool forcetable) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (m.vector_layers_json.size() > 0 || m.tilestats_json.size() > 0) {
|
if (m.vector_layers_json.size() > 0 || m.tilestats_json.size() > 0) {
|
||||||
std::string json = "{";
|
std::string json;
|
||||||
|
json_writer state(&json);
|
||||||
|
state.nospace = true;
|
||||||
|
state.json_write_hash();
|
||||||
|
|
||||||
if (m.vector_layers_json.size() > 0) {
|
if (m.vector_layers_json.size() > 0) {
|
||||||
json += "\"vector_layers\": " + m.vector_layers_json;
|
state.nospace = true;
|
||||||
|
state.json_write_string("vector_layers");
|
||||||
|
state.nospace = true;
|
||||||
|
state.json_write_json(m.vector_layers_json);
|
||||||
|
|
||||||
if (m.tilestats_json.size() > 0) {
|
if (m.tilestats_json.size() > 0) {
|
||||||
json += ",\"tilestats\": " + m.tilestats_json;
|
state.nospace = true;
|
||||||
|
state.json_write_string("tilestats");
|
||||||
|
state.nospace = true;
|
||||||
|
state.json_write_json(m.tilestats_json);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (m.tilestats_json.size() > 0) {
|
if (m.tilestats_json.size() > 0) {
|
||||||
json += "\"tilestats\": " + m.tilestats_json;
|
state.nospace = true;
|
||||||
|
state.json_write_string("tilestats");
|
||||||
|
state.nospace = true;
|
||||||
|
state.json_write_json(m.tilestats_json);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
json += "}";
|
state.nospace = true;
|
||||||
|
state.json_end_hash();
|
||||||
|
|
||||||
sql = sqlite3_mprintf("INSERT INTO metadata (name, value) VALUES ('json', %Q);", json.c_str());
|
sql = sqlite3_mprintf("INSERT INTO metadata (name, value) VALUES ('json', %Q);", json.c_str());
|
||||||
if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
|
if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
|
||||||
@@ -617,6 +663,7 @@ metadata make_metadata(const char *fname, int minzoom, int maxzoom, double minla
|
|||||||
{
|
{
|
||||||
json_writer state(&m.vector_layers_json);
|
json_writer state(&m.vector_layers_json);
|
||||||
|
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_array();
|
state.json_write_array();
|
||||||
|
|
||||||
std::vector<std::string> lnames;
|
std::vector<std::string> lnames;
|
||||||
@@ -626,23 +673,33 @@ metadata make_metadata(const char *fname, int minzoom, int maxzoom, double minla
|
|||||||
|
|
||||||
for (size_t i = 0; i < lnames.size(); i++) {
|
for (size_t i = 0; i < lnames.size(); i++) {
|
||||||
auto fk = layermap.find(lnames[i]);
|
auto fk = layermap.find(lnames[i]);
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_hash();
|
state.json_write_hash();
|
||||||
|
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_string("id");
|
state.json_write_string("id");
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_string(lnames[i]);
|
state.json_write_string(lnames[i]);
|
||||||
|
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_string("description");
|
state.json_write_string("description");
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_string(fk->second.description);
|
state.json_write_string(fk->second.description);
|
||||||
|
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_string("minzoom");
|
state.json_write_string("minzoom");
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_signed(fk->second.minzoom);
|
state.json_write_signed(fk->second.minzoom);
|
||||||
|
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_string("maxzoom");
|
state.json_write_string("maxzoom");
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_signed(fk->second.maxzoom);
|
state.json_write_signed(fk->second.maxzoom);
|
||||||
|
|
||||||
state.json_write_string("fields");
|
|
||||||
state.json_write_hash();
|
|
||||||
state.nospace = true;
|
state.nospace = true;
|
||||||
|
state.json_write_string("fields");
|
||||||
|
state.nospace = true;
|
||||||
|
state.json_write_hash();
|
||||||
|
|
||||||
bool first = true;
|
bool first = true;
|
||||||
size_t attribute_count = 0;
|
size_t attribute_count = 0;
|
||||||
@@ -651,6 +708,7 @@ metadata make_metadata(const char *fname, int minzoom, int maxzoom, double minla
|
|||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_string(j->first);
|
state.json_write_string(j->first);
|
||||||
|
|
||||||
auto f = attribute_descriptions.find(j->first);
|
auto f = attribute_descriptions.find(j->first);
|
||||||
@@ -661,15 +719,20 @@ metadata make_metadata(const char *fname, int minzoom, int maxzoom, double minla
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (type == (1 << mvt_double)) {
|
if (type == (1 << mvt_double)) {
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_string("Number");
|
state.json_write_string("Number");
|
||||||
} else if (type == (1 << mvt_bool)) {
|
} else if (type == (1 << mvt_bool)) {
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_string("Boolean");
|
state.json_write_string("Boolean");
|
||||||
} else if (type == (1 << mvt_string)) {
|
} else if (type == (1 << mvt_string)) {
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_string("String");
|
state.json_write_string("String");
|
||||||
} else {
|
} else {
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_string("Mixed");
|
state.json_write_string("Mixed");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
state.nospace = true;
|
||||||
state.json_write_string(f->second);
|
state.json_write_string(f->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -681,9 +744,11 @@ metadata make_metadata(const char *fname, int minzoom, int maxzoom, double minla
|
|||||||
|
|
||||||
state.nospace = true;
|
state.nospace = true;
|
||||||
state.json_end_hash();
|
state.json_end_hash();
|
||||||
|
state.nospace = true;
|
||||||
state.json_end_hash();
|
state.json_end_hash();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.nospace = true;
|
||||||
state.json_end_array();
|
state.json_end_array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,588 @@
|
|||||||
|
#ifndef PMTILES_HPP
|
||||||
|
#define PMTILES_HPP
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
#include <vector>
|
||||||
|
#include <tuple>
|
||||||
|
#include <functional>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace pmtiles {
|
||||||
|
|
||||||
|
const uint8_t TILETYPE_UNKNOWN = 0x0;
|
||||||
|
const uint8_t TILETYPE_MVT = 0x1;
|
||||||
|
const uint8_t TILETYPE_PNG = 0x2;
|
||||||
|
const uint8_t TILETYPE_JPEG = 0x3;
|
||||||
|
const uint8_t TILETYPE_WEBP = 0x4;
|
||||||
|
|
||||||
|
const uint8_t COMPRESSION_UNKNOWN = 0x0;
|
||||||
|
const uint8_t COMPRESSION_NONE = 0x1;
|
||||||
|
const uint8_t COMPRESSION_GZIP = 0x2;
|
||||||
|
const uint8_t COMPRESSION_BROTLI = 0x3;
|
||||||
|
const uint8_t COMPRESSION_ZSTD = 0x4;
|
||||||
|
|
||||||
|
struct headerv3 {
|
||||||
|
uint64_t root_dir_offset;
|
||||||
|
uint64_t root_dir_bytes;
|
||||||
|
uint64_t json_metadata_offset;
|
||||||
|
uint64_t json_metadata_bytes;
|
||||||
|
uint64_t leaf_dirs_offset;
|
||||||
|
uint64_t leaf_dirs_bytes;
|
||||||
|
uint64_t tile_data_offset;
|
||||||
|
uint64_t tile_data_bytes;
|
||||||
|
uint64_t addressed_tiles_count;
|
||||||
|
uint64_t tile_entries_count;
|
||||||
|
uint64_t tile_contents_count;
|
||||||
|
bool clustered;
|
||||||
|
uint8_t internal_compression;
|
||||||
|
uint8_t tile_compression;
|
||||||
|
uint8_t tile_type;
|
||||||
|
uint8_t min_zoom;
|
||||||
|
uint8_t max_zoom;
|
||||||
|
int32_t min_lon_e7;
|
||||||
|
int32_t min_lat_e7;
|
||||||
|
int32_t max_lon_e7;
|
||||||
|
int32_t max_lat_e7;
|
||||||
|
uint8_t center_zoom;
|
||||||
|
int32_t center_lon_e7;
|
||||||
|
int32_t center_lat_e7;
|
||||||
|
|
||||||
|
// WARNING: this is limited to little-endian
|
||||||
|
std::string serialize() {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "PMTiles";
|
||||||
|
uint8_t version = 3;
|
||||||
|
ss.write((char *) &version, 1);
|
||||||
|
ss.write((char *) &root_dir_offset, 8);
|
||||||
|
ss.write((char *) &root_dir_bytes, 8);
|
||||||
|
ss.write((char *) &json_metadata_offset, 8);
|
||||||
|
ss.write((char *) &json_metadata_bytes, 8);
|
||||||
|
ss.write((char *) &leaf_dirs_offset, 8);
|
||||||
|
ss.write((char *) &leaf_dirs_bytes, 8);
|
||||||
|
ss.write((char *) &tile_data_offset, 8);
|
||||||
|
ss.write((char *) &tile_data_bytes, 8);
|
||||||
|
ss.write((char *) &addressed_tiles_count, 8);
|
||||||
|
ss.write((char *) &tile_entries_count, 8);
|
||||||
|
ss.write((char *) &tile_contents_count, 8);
|
||||||
|
|
||||||
|
uint8_t clustered_val = 0x0;
|
||||||
|
if (clustered) {
|
||||||
|
clustered_val = 0x1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ss.write((char *) &clustered_val, 1);
|
||||||
|
ss.write((char *) &internal_compression, 1);
|
||||||
|
ss.write((char *) &tile_compression, 1);
|
||||||
|
ss.write((char *) &tile_type, 1);
|
||||||
|
ss.write((char *) &min_zoom, 1);
|
||||||
|
ss.write((char *) &max_zoom, 1);
|
||||||
|
ss.write((char *) &min_lon_e7, 4);
|
||||||
|
ss.write((char *) &min_lat_e7, 4);
|
||||||
|
ss.write((char *) &max_lon_e7, 4);
|
||||||
|
ss.write((char *) &max_lat_e7, 4);
|
||||||
|
ss.write((char *) ¢er_zoom, 1);
|
||||||
|
ss.write((char *) ¢er_lon_e7, 4);
|
||||||
|
ss.write((char *) ¢er_lat_e7, 4);
|
||||||
|
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pmtiles_magic_number_exception : std::exception {
|
||||||
|
const char *what() const noexcept override {
|
||||||
|
return "pmtiles magic number exception";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pmtiles_version_exception : std::exception {
|
||||||
|
const char *what() const noexcept override {
|
||||||
|
return "pmtiles version: must be 3";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
inline headerv3 deserialize_header(const std::string &s) {
|
||||||
|
if (s.substr(0, 7) != "PMTiles") {
|
||||||
|
throw pmtiles_magic_number_exception{};
|
||||||
|
}
|
||||||
|
if (s.size() != 127 || s[7] != 0x3) {
|
||||||
|
throw pmtiles_version_exception{};
|
||||||
|
}
|
||||||
|
headerv3 h;
|
||||||
|
s.copy((char *) &h.root_dir_offset, 8, 8);
|
||||||
|
s.copy((char *) &h.root_dir_bytes, 8, 16);
|
||||||
|
s.copy((char *) &h.json_metadata_offset, 8, 24);
|
||||||
|
s.copy((char *) &h.json_metadata_bytes, 8, 32);
|
||||||
|
s.copy((char *) &h.leaf_dirs_offset, 8, 40);
|
||||||
|
s.copy((char *) &h.leaf_dirs_bytes, 8, 48);
|
||||||
|
s.copy((char *) &h.tile_data_offset, 8, 56);
|
||||||
|
s.copy((char *) &h.tile_data_bytes, 8, 64);
|
||||||
|
s.copy((char *) &h.addressed_tiles_count, 8, 72);
|
||||||
|
s.copy((char *) &h.tile_entries_count, 8, 80);
|
||||||
|
s.copy((char *) &h.tile_contents_count, 8, 88);
|
||||||
|
if (s[96] == 0x1) {
|
||||||
|
h.clustered = true;
|
||||||
|
} else {
|
||||||
|
h.clustered = false;
|
||||||
|
}
|
||||||
|
h.internal_compression = s[97];
|
||||||
|
h.tile_compression = s[98];
|
||||||
|
h.tile_type = s[99];
|
||||||
|
h.min_zoom = s[100];
|
||||||
|
h.max_zoom = s[101];
|
||||||
|
s.copy((char *) &h.min_lon_e7, 4, 102);
|
||||||
|
s.copy((char *) &h.min_lat_e7, 4, 106);
|
||||||
|
s.copy((char *) &h.max_lon_e7, 4, 110);
|
||||||
|
s.copy((char *) &h.max_lat_e7, 4, 114);
|
||||||
|
h.center_zoom = s[118];
|
||||||
|
s.copy((char *) &h.center_lon_e7, 4, 119);
|
||||||
|
s.copy((char *) &h.center_lat_e7, 4, 123);
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct zxy {
|
||||||
|
uint8_t z;
|
||||||
|
uint32_t x;
|
||||||
|
uint32_t y;
|
||||||
|
|
||||||
|
zxy(int _z, int _x, int _y)
|
||||||
|
: z(_z), x(_x), y(_y) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct entryv3 {
|
||||||
|
uint64_t tile_id;
|
||||||
|
uint64_t offset;
|
||||||
|
uint32_t length;
|
||||||
|
uint32_t run_length;
|
||||||
|
|
||||||
|
entryv3()
|
||||||
|
: tile_id(0), offset(0), length(0), run_length(0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
entryv3(uint64_t _tile_id, uint64_t _offset, uint32_t _length, uint32_t _run_length)
|
||||||
|
: tile_id(_tile_id), offset(_offset), length(_length), run_length(_run_length) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool operator()(entryv3 a, entryv3 b) const {
|
||||||
|
return a.tile_id < b.tile_id;
|
||||||
|
}
|
||||||
|
} entryv3_cmp;
|
||||||
|
|
||||||
|
struct entry_zxy {
|
||||||
|
uint8_t z;
|
||||||
|
uint32_t x;
|
||||||
|
uint32_t y;
|
||||||
|
uint64_t offset;
|
||||||
|
uint32_t length;
|
||||||
|
|
||||||
|
entry_zxy(uint8_t _z, uint32_t _x, uint32_t _y, uint64_t _offset, uint32_t _length)
|
||||||
|
: z(_z), x(_x), y(_y), offset(_offset), length(_length) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool operator()(entry_zxy a, entry_zxy b) const {
|
||||||
|
if (a.z != b.z) {
|
||||||
|
return a.z < b.z;
|
||||||
|
}
|
||||||
|
if (a.x != b.x) {
|
||||||
|
return a.x < b.x;
|
||||||
|
}
|
||||||
|
return a.y < b.y;
|
||||||
|
}
|
||||||
|
} entry_zxy_cmp;
|
||||||
|
|
||||||
|
struct varint_too_long_exception : std::exception {
|
||||||
|
const char *what() const noexcept override {
|
||||||
|
return "varint too long exception";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct end_of_buffer_exception : std::exception {
|
||||||
|
const char *what() const noexcept override {
|
||||||
|
return "end of buffer exception";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
constexpr const int8_t max_varint_length = sizeof(uint64_t) * 8 / 7 + 1;
|
||||||
|
|
||||||
|
// from https://github.com/mapbox/protozero/blob/master/include/protozero/varint.hpp
|
||||||
|
uint64_t decode_varint_impl(const char **data, const char *end) {
|
||||||
|
const auto *begin = reinterpret_cast<const int8_t *>(*data);
|
||||||
|
const auto *iend = reinterpret_cast<const int8_t *>(end);
|
||||||
|
const int8_t *p = begin;
|
||||||
|
uint64_t val = 0;
|
||||||
|
|
||||||
|
if (iend - begin >= max_varint_length) { // fast path
|
||||||
|
do {
|
||||||
|
int64_t b = *p++;
|
||||||
|
val = ((uint64_t(b) & 0x7fU));
|
||||||
|
if (b >= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
b = *p++;
|
||||||
|
val |= ((uint64_t(b) & 0x7fU) << 7U);
|
||||||
|
if (b >= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
b = *p++;
|
||||||
|
val |= ((uint64_t(b) & 0x7fU) << 14U);
|
||||||
|
if (b >= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
b = *p++;
|
||||||
|
val |= ((uint64_t(b) & 0x7fU) << 21U);
|
||||||
|
if (b >= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
b = *p++;
|
||||||
|
val |= ((uint64_t(b) & 0x7fU) << 28U);
|
||||||
|
if (b >= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
b = *p++;
|
||||||
|
val |= ((uint64_t(b) & 0x7fU) << 35U);
|
||||||
|
if (b >= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
b = *p++;
|
||||||
|
val |= ((uint64_t(b) & 0x7fU) << 42U);
|
||||||
|
if (b >= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
b = *p++;
|
||||||
|
val |= ((uint64_t(b) & 0x7fU) << 49U);
|
||||||
|
if (b >= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
b = *p++;
|
||||||
|
val |= ((uint64_t(b) & 0x7fU) << 56U);
|
||||||
|
if (b >= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
b = *p++;
|
||||||
|
val |= ((uint64_t(b) & 0x01U) << 63U);
|
||||||
|
if (b >= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
throw varint_too_long_exception{};
|
||||||
|
} while (false);
|
||||||
|
} else {
|
||||||
|
unsigned int shift = 0;
|
||||||
|
while (p != iend && *p < 0) {
|
||||||
|
val |= (uint64_t(*p++) & 0x7fU) << shift;
|
||||||
|
shift += 7;
|
||||||
|
}
|
||||||
|
if (p == iend) {
|
||||||
|
throw end_of_buffer_exception{};
|
||||||
|
}
|
||||||
|
val |= uint64_t(*p++) << shift;
|
||||||
|
}
|
||||||
|
|
||||||
|
*data = reinterpret_cast<const char *>(p);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t decode_varint(const char **data, const char *end) {
|
||||||
|
// If this is a one-byte varint, decode it here.
|
||||||
|
if (end != *data && ((static_cast<uint64_t>(**data) & 0x80U) == 0)) {
|
||||||
|
const auto val = static_cast<uint64_t>(**data);
|
||||||
|
++(*data);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
// If this varint is more than one byte, defer to complete implementation.
|
||||||
|
return decode_varint_impl(data, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rotate(int64_t n, int64_t &x, int64_t &y, int64_t rx, int64_t ry) {
|
||||||
|
if (ry == 0) {
|
||||||
|
if (rx == 1) {
|
||||||
|
x = n - 1 - x;
|
||||||
|
y = n - 1 - y;
|
||||||
|
}
|
||||||
|
int64_t t = x;
|
||||||
|
x = y;
|
||||||
|
y = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
zxy t_on_level(uint8_t z, uint64_t pos) {
|
||||||
|
int64_t n = 1 << z;
|
||||||
|
int64_t rx, ry, s, t = pos;
|
||||||
|
int64_t tx = 0;
|
||||||
|
int64_t ty = 0;
|
||||||
|
|
||||||
|
for (s = 1; s < n; s *= 2) {
|
||||||
|
rx = 1 & (t / 2);
|
||||||
|
ry = 1 & (t ^ rx);
|
||||||
|
rotate(s, tx, ty, rx, ry);
|
||||||
|
tx += s * rx;
|
||||||
|
ty += s * ry;
|
||||||
|
t /= 4;
|
||||||
|
}
|
||||||
|
return zxy(z, tx, ty);
|
||||||
|
}
|
||||||
|
|
||||||
|
int write_varint(std::back_insert_iterator<std::string> data, uint64_t value) {
|
||||||
|
int n = 1;
|
||||||
|
|
||||||
|
while (value >= 0x80U) {
|
||||||
|
*data++ = char((value & 0x7fU) | 0x80U);
|
||||||
|
value >>= 7U;
|
||||||
|
++n;
|
||||||
|
}
|
||||||
|
*data = char(value);
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TMS order
|
||||||
|
struct {
|
||||||
|
bool operator()(entry_zxy a, entry_zxy b) const {
|
||||||
|
if (a.z != b.z) {
|
||||||
|
return a.z < b.z;
|
||||||
|
}
|
||||||
|
if (a.x != b.x) {
|
||||||
|
return a.x < b.x;
|
||||||
|
}
|
||||||
|
return a.y > b.y;
|
||||||
|
}
|
||||||
|
} colmajor_cmp;
|
||||||
|
|
||||||
|
// use a 0 length entry as a null value.
|
||||||
|
entryv3 find_tile(const std::vector<entryv3> &entries, uint64_t tile_id) {
|
||||||
|
int m = 0;
|
||||||
|
int n = entries.size() - 1;
|
||||||
|
while (m <= n) {
|
||||||
|
int k = (n + m) >> 1;
|
||||||
|
int cmp = tile_id - entries[k].tile_id;
|
||||||
|
if (cmp > 0) {
|
||||||
|
m = k + 1;
|
||||||
|
} else if (cmp < 0) {
|
||||||
|
n = k - 1;
|
||||||
|
} else {
|
||||||
|
return entries[k];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n >= 0) {
|
||||||
|
if (entries[n].run_length == 0) {
|
||||||
|
return entries[n];
|
||||||
|
}
|
||||||
|
if (tile_id - entries[n].tile_id < entries[n].run_length) {
|
||||||
|
return entries[n];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return entryv3{0, 0, 0, 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end anonymous namespace
|
||||||
|
|
||||||
|
inline zxy tileid_to_zxy(uint64_t tileid) {
|
||||||
|
uint64_t acc = 0;
|
||||||
|
uint8_t t_z = 0;
|
||||||
|
while (true) {
|
||||||
|
uint64_t num_tiles = (1LL << t_z) * (1LL << t_z);
|
||||||
|
if (acc + num_tiles > tileid) {
|
||||||
|
return t_on_level(t_z, tileid - acc);
|
||||||
|
}
|
||||||
|
acc += num_tiles;
|
||||||
|
t_z++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint64_t zxy_to_tileid(uint8_t z, uint32_t x, uint32_t y) {
|
||||||
|
uint64_t acc = 0;
|
||||||
|
for (uint8_t t_z = 0; t_z < z; t_z++) acc += (1LL << t_z) * (1LL << t_z);
|
||||||
|
int64_t n = 1 << z;
|
||||||
|
int64_t rx, ry, s, d = 0;
|
||||||
|
int64_t tx = x;
|
||||||
|
int64_t ty = y;
|
||||||
|
for (s = n / 2; s > 0; s /= 2) {
|
||||||
|
rx = (tx & s) > 0;
|
||||||
|
ry = (ty & s) > 0;
|
||||||
|
d += s * s * ((3 * rx) ^ ry);
|
||||||
|
rotate(s, tx, ty, rx, ry);
|
||||||
|
}
|
||||||
|
return acc + d;
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns an uncompressed byte buffer
|
||||||
|
inline std::string serialize_directory(const std::vector<entryv3> &entries) {
|
||||||
|
std::string data;
|
||||||
|
|
||||||
|
write_varint(std::back_inserter(data), entries.size());
|
||||||
|
|
||||||
|
uint64_t last_id = 0;
|
||||||
|
for (auto const &entry : entries) {
|
||||||
|
write_varint(std::back_inserter(data), entry.tile_id - last_id);
|
||||||
|
last_id = entry.tile_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto const &entry : entries) {
|
||||||
|
write_varint(std::back_inserter(data), entry.run_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto const &entry : entries) {
|
||||||
|
write_varint(std::back_inserter(data), entry.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < entries.size(); i++) {
|
||||||
|
if (i > 0 && entries[i].offset == entries[i - 1].offset + entries[i - 1].length) {
|
||||||
|
write_varint(std::back_inserter(data), 0);
|
||||||
|
} else {
|
||||||
|
write_varint(std::back_inserter(data), entries[i].offset + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// takes an uncompressed byte buffer
|
||||||
|
inline std::vector<entryv3> deserialize_directory(const std::string &decompressed) {
|
||||||
|
const char *t = decompressed.data();
|
||||||
|
const char *end = t + decompressed.size();
|
||||||
|
|
||||||
|
uint64_t num_entries = decode_varint(&t, end);
|
||||||
|
|
||||||
|
std::vector<entryv3> result;
|
||||||
|
result.resize(num_entries);
|
||||||
|
|
||||||
|
uint64_t last_id = 0;
|
||||||
|
for (size_t i = 0; i < num_entries; i++) {
|
||||||
|
uint64_t tile_id = last_id + decode_varint(&t, end);
|
||||||
|
result[i].tile_id = tile_id;
|
||||||
|
last_id = tile_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < num_entries; i++) {
|
||||||
|
result[i].run_length = decode_varint(&t, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < num_entries; i++) {
|
||||||
|
result[i].length = decode_varint(&t, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < num_entries; i++) {
|
||||||
|
uint64_t tmp = decode_varint(&t, end);
|
||||||
|
|
||||||
|
if (i > 0 && tmp == 0) {
|
||||||
|
result[i].offset = result[i - 1].offset + result[i - 1].length;
|
||||||
|
} else {
|
||||||
|
result[i].offset = tmp - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// assert the directory has been fully consumed
|
||||||
|
if (t != end) {
|
||||||
|
fprintf(stderr, "Error: malformed pmtiles directory\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::tuple<std::string, std::string, int> build_root_leaves(const std::function<std::string(const std::string &, uint8_t)> mycompress, uint8_t compression, const std::vector<pmtiles::entryv3> &entries, int leaf_size) {
|
||||||
|
std::vector<pmtiles::entryv3> root_entries;
|
||||||
|
std::string leaves_bytes;
|
||||||
|
int num_leaves = 0;
|
||||||
|
for (size_t i = 0; i <= entries.size(); i += leaf_size) {
|
||||||
|
num_leaves++;
|
||||||
|
int end = i + leaf_size;
|
||||||
|
if (i + leaf_size > entries.size()) {
|
||||||
|
end = entries.size();
|
||||||
|
}
|
||||||
|
std::vector<pmtiles::entryv3> subentries = {entries.begin() + i, entries.begin() + end};
|
||||||
|
auto uncompressed_leaf = pmtiles::serialize_directory(subentries);
|
||||||
|
auto compressed_leaf = mycompress(uncompressed_leaf, compression);
|
||||||
|
root_entries.emplace_back(entries[i].tile_id, leaves_bytes.size(), compressed_leaf.size(), 0);
|
||||||
|
leaves_bytes += compressed_leaf;
|
||||||
|
}
|
||||||
|
auto uncompressed_root = pmtiles::serialize_directory(root_entries);
|
||||||
|
auto compressed_root = mycompress(uncompressed_root, compression);
|
||||||
|
return std::make_tuple(compressed_root, leaves_bytes, num_leaves);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::tuple<std::string, std::string, int> make_root_leaves(const std::function<std::string(const std::string &, uint8_t)> mycompress, uint8_t compression, const std::vector<pmtiles::entryv3> &entries) {
|
||||||
|
auto test_bytes = pmtiles::serialize_directory(entries);
|
||||||
|
auto compressed = mycompress(test_bytes, compression);
|
||||||
|
if (compressed.size() <= 16384 - 127) {
|
||||||
|
return std::make_tuple(compressed, "", 0);
|
||||||
|
}
|
||||||
|
int leaf_size = 4096;
|
||||||
|
while (true) {
|
||||||
|
std::string root_bytes;
|
||||||
|
std::string leaves_bytes;
|
||||||
|
int num_leaves;
|
||||||
|
std::tie(root_bytes, leaves_bytes, num_leaves) = build_root_leaves(mycompress, compression, entries, leaf_size);
|
||||||
|
if (root_bytes.length() < 16384 - 127) {
|
||||||
|
return std::make_tuple(root_bytes, leaves_bytes, num_leaves);
|
||||||
|
}
|
||||||
|
leaf_size *= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void collect_entries(const std::function<std::string(const std::string &, uint8_t)> decompress, std::vector<entry_zxy> &tile_entries, const char *pmtiles_map, const headerv3 &h, uint64_t dir_offset, uint64_t dir_len) {
|
||||||
|
std::string dir_s{pmtiles_map + dir_offset, dir_len};
|
||||||
|
std::string decompressed_dir = decompress(dir_s, h.internal_compression);
|
||||||
|
|
||||||
|
auto dir_entries = pmtiles::deserialize_directory(decompressed_dir);
|
||||||
|
for (auto const &entry : dir_entries) {
|
||||||
|
if (entry.run_length == 0) {
|
||||||
|
collect_entries(decompress, tile_entries, pmtiles_map, h, h.leaf_dirs_offset + entry.offset, entry.length);
|
||||||
|
} else {
|
||||||
|
for (uint64_t i = entry.tile_id; i < entry.tile_id + entry.run_length; i++) {
|
||||||
|
pmtiles::zxy zxy = pmtiles::tileid_to_zxy(i);
|
||||||
|
tile_entries.emplace_back(zxy.z, zxy.x, zxy.y, h.tile_data_offset + entry.offset, entry.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::vector<entry_zxy> entries_tms(const std::function<std::string(const std::string &, uint8_t)> decompress, const char *pmtiles_map) {
|
||||||
|
std::string header_s{pmtiles_map, 127};
|
||||||
|
auto header = pmtiles::deserialize_header(header_s);
|
||||||
|
|
||||||
|
std::vector<entry_zxy> tile_entries;
|
||||||
|
|
||||||
|
collect_entries(decompress, tile_entries, pmtiles_map, header, header.root_dir_offset, header.root_dir_bytes);
|
||||||
|
std::sort(tile_entries.begin(), tile_entries.end(), colmajor_cmp);
|
||||||
|
return tile_entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::pair<uint64_t, uint32_t> get_tile(const std::function<std::string(const std::string &, uint8_t)> decompress, const char *pmtiles_map, uint8_t z, uint32_t x, uint32_t y) {
|
||||||
|
uint64_t tile_id = pmtiles::zxy_to_tileid(z, x, y);
|
||||||
|
|
||||||
|
std::string header_s{pmtiles_map, 127};
|
||||||
|
auto h = pmtiles::deserialize_header(header_s);
|
||||||
|
|
||||||
|
uint64_t dir_offset = h.root_dir_offset;
|
||||||
|
uint32_t dir_length = h.root_dir_bytes;
|
||||||
|
for (int depth = 0; depth <= 3; depth++) {
|
||||||
|
std::string dir_s{pmtiles_map + dir_offset, dir_length};
|
||||||
|
std::string decompressed_dir = decompress(dir_s, h.internal_compression);
|
||||||
|
auto dir_entries = pmtiles::deserialize_directory(decompressed_dir);
|
||||||
|
auto entry = find_tile(dir_entries, tile_id);
|
||||||
|
|
||||||
|
if (entry.length > 0) {
|
||||||
|
if (entry.run_length > 0) {
|
||||||
|
return std::make_pair(h.tile_data_offset + entry.offset, entry.length);
|
||||||
|
} else {
|
||||||
|
dir_offset = h.leaf_dirs_offset + entry.offset;
|
||||||
|
dir_length = entry.length;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return std::make_pair(0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_pair(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace pmtiles
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,449 @@
|
|||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
#include <fstream>
|
||||||
|
#include <string.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sqlite3.h>
|
||||||
|
#include "errors.hpp"
|
||||||
|
#include "pmtiles_file.hpp"
|
||||||
|
#include "mvt.hpp"
|
||||||
|
#include "write_json.hpp"
|
||||||
|
|
||||||
|
bool pmtiles_has_suffix(const char *filename) {
|
||||||
|
if (filename == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
size_t lenstr = strlen(filename);
|
||||||
|
if (lenstr < 8) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (strncmp(filename + (lenstr - 8), ".pmtiles", 8) == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void check_pmtiles(const char *filename, char **argv, bool forcetable) {
|
||||||
|
struct stat st;
|
||||||
|
if (stat(filename, &st) == 0) {
|
||||||
|
fprintf(stderr, "%s: Tileset \"%s\" already exists. You can use --force if you want to delete the old tileset.\n", argv[0], filename);
|
||||||
|
fprintf(stderr, "%s: %s: file exists\n", argv[0], filename);
|
||||||
|
|
||||||
|
if (forcetable) {
|
||||||
|
fprintf(stderr, "%s: --allow-existing is not supported for pmtiles\n", argv[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(EXIT_EXISTS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string decompress_fn(const std::string &input, uint8_t compression) {
|
||||||
|
std::string output;
|
||||||
|
if (compression == pmtiles::COMPRESSION_NONE) {
|
||||||
|
output = input;
|
||||||
|
} else if (compression == pmtiles::COMPRESSION_GZIP) {
|
||||||
|
decompress(input, output);
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error("Unknown or unsupported compression.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string compress_fn(const std::string &input, uint8_t compression) {
|
||||||
|
std::string output;
|
||||||
|
if (compression == pmtiles::COMPRESSION_NONE) {
|
||||||
|
output = input;
|
||||||
|
} else if (compression == pmtiles::COMPRESSION_GZIP) {
|
||||||
|
compress(input, output);
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error("Unknown or unsupported compression.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<pmtiles::entry_zxy> pmtiles_entries_tms(const char *pmtiles_map, int minzoom, int maxzoom) {
|
||||||
|
std::vector<pmtiles::entry_zxy> filtered;
|
||||||
|
auto all_entries = pmtiles::entries_tms(&decompress_fn, pmtiles_map);
|
||||||
|
std::copy_if(all_entries.begin(), all_entries.end(), std::back_inserter(filtered), [minzoom, maxzoom](pmtiles::entry_zxy e) { return e.z >= minzoom && e.z <= maxzoom; });
|
||||||
|
return filtered;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<uint64_t, uint32_t> pmtiles_get_tile(const char *pmtiles_map, int z, int x, int y) {
|
||||||
|
return pmtiles::get_tile(&decompress_fn, pmtiles_map, z, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void out(json_writer &state, std::string k, std::string v) {
|
||||||
|
state.json_comma_newline();
|
||||||
|
state.json_write_string(k);
|
||||||
|
state.json_write_string(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string metadata_to_pmtiles_json(metadata m) {
|
||||||
|
std::string buf;
|
||||||
|
json_writer state(&buf);
|
||||||
|
|
||||||
|
state.json_write_hash();
|
||||||
|
state.json_write_newline();
|
||||||
|
|
||||||
|
out(state, "name", m.name);
|
||||||
|
out(state, "format", m.format);
|
||||||
|
out(state, "type", m.type);
|
||||||
|
out(state, "description", m.description);
|
||||||
|
out(state, "version", std::to_string(m.version));
|
||||||
|
if (m.attribution.size() > 0) {
|
||||||
|
out(state, "attribution", m.attribution);
|
||||||
|
}
|
||||||
|
if (m.strategies_json.size() > 0) {
|
||||||
|
state.json_comma_newline();
|
||||||
|
state.json_write_string("strategies");
|
||||||
|
state.json_write_json(m.strategies_json);
|
||||||
|
}
|
||||||
|
out(state, "generator", m.generator);
|
||||||
|
out(state, "generator_options", m.generator_options);
|
||||||
|
|
||||||
|
if (m.vector_layers_json.size() > 0) {
|
||||||
|
state.json_comma_newline();
|
||||||
|
state.json_write_string("vector_layers");
|
||||||
|
state.json_write_json(m.vector_layers_json);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m.tilestats_json.size() > 0) {
|
||||||
|
state.json_comma_newline();
|
||||||
|
state.json_write_string("tilestats");
|
||||||
|
state.json_write_json(m.tilestats_json);
|
||||||
|
}
|
||||||
|
|
||||||
|
state.json_write_newline();
|
||||||
|
state.json_end_hash();
|
||||||
|
state.json_write_newline();
|
||||||
|
std::string compressed;
|
||||||
|
compress(buf, compressed);
|
||||||
|
return compressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mbtiles_map_image_to_pmtiles(char *fname, metadata m, bool tile_compression, bool quiet, bool quiet_progress) {
|
||||||
|
sqlite3 *db;
|
||||||
|
|
||||||
|
if (sqlite3_open(fname, &db) != SQLITE_OK) {
|
||||||
|
fprintf(stderr, "%s: %s\n", fname, sqlite3_errmsg(db));
|
||||||
|
exit(EXIT_SQLITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *err = NULL;
|
||||||
|
if (sqlite3_exec(db, "PRAGMA integrity_check;", NULL, NULL, &err) != SQLITE_OK) {
|
||||||
|
fprintf(stderr, "%s: integrity_check: %s\n", fname, err);
|
||||||
|
exit(EXIT_SQLITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// materialize list of all tile IDs
|
||||||
|
std::vector<uint64_t> tile_ids;
|
||||||
|
|
||||||
|
{
|
||||||
|
const char *sql = "SELECT zoom_level, tile_column, tile_row FROM map";
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
|
||||||
|
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) {
|
||||||
|
fprintf(stderr, "%s: select failed: %s\n", fname, sqlite3_errmsg(db));
|
||||||
|
exit(EXIT_SQLITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||||
|
int zoom = sqlite3_column_int(stmt, 0);
|
||||||
|
int x = sqlite3_column_int(stmt, 1);
|
||||||
|
int sorty = sqlite3_column_int(stmt, 2);
|
||||||
|
int y = (1LL << zoom) - 1 - sorty;
|
||||||
|
uint64_t res = pmtiles::zxy_to_tileid(zoom, x, y);
|
||||||
|
tile_ids.push_back(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(tile_ids.begin(), tile_ids.end());
|
||||||
|
|
||||||
|
std::unordered_map<std::string, std::pair<unsigned long long, unsigned long>> hash_to_offset_len;
|
||||||
|
std::vector<pmtiles::entryv3> entries;
|
||||||
|
unsigned long long offset = 0;
|
||||||
|
|
||||||
|
std::string tmpname = (std::string(fname) + ".tmp");
|
||||||
|
|
||||||
|
// write tile data to tempfile in clustered order
|
||||||
|
{
|
||||||
|
const char *map_sql = "SELECT tile_id FROM map WHERE zoom_level = ? AND tile_column = ? AND tile_row = ?";
|
||||||
|
sqlite3_stmt *map_stmt;
|
||||||
|
|
||||||
|
if (sqlite3_prepare_v2(db, map_sql, -1, &map_stmt, NULL) != SQLITE_OK) {
|
||||||
|
fprintf(stderr, "%s: select failed: %s\n", fname, sqlite3_errmsg(db));
|
||||||
|
exit(EXIT_SQLITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *image_sql = "SELECT tile_data FROM images WHERE tile_id = ?";
|
||||||
|
sqlite3_stmt *image_stmt;
|
||||||
|
|
||||||
|
if (sqlite3_prepare_v2(db, image_sql, -1, &image_stmt, NULL) != SQLITE_OK) {
|
||||||
|
fprintf(stderr, "%s: select failed: %s\n", fname, sqlite3_errmsg(db));
|
||||||
|
exit(EXIT_SQLITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ofstream tmp_ostream;
|
||||||
|
|
||||||
|
tmp_ostream.open(tmpname.c_str(), std::ios::out | std::ios::binary);
|
||||||
|
|
||||||
|
int idx = 0;
|
||||||
|
for (auto const &tile_id : tile_ids) {
|
||||||
|
idx = idx + 1;
|
||||||
|
double progress = ((double) idx / tile_ids.size()) * 100;
|
||||||
|
pmtiles::zxy zxy = pmtiles::tileid_to_zxy(tile_id);
|
||||||
|
if (!quiet && !quiet_progress) {
|
||||||
|
fprintf(stderr, " %3.1f%% %d/%u/%u \r", progress, zxy.z, zxy.x, zxy.y);
|
||||||
|
}
|
||||||
|
sqlite3_bind_int(map_stmt, 1, zxy.z);
|
||||||
|
sqlite3_bind_int(map_stmt, 2, zxy.x);
|
||||||
|
sqlite3_bind_int(map_stmt, 3, (1LL << zxy.z) - 1 - zxy.y);
|
||||||
|
|
||||||
|
if (sqlite3_step(map_stmt) != SQLITE_ROW) {
|
||||||
|
fprintf(stderr, "Corrupt mbtiles file: null entry in map table\n");
|
||||||
|
exit(EXIT_SQLITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string hsh{reinterpret_cast<const char *>(sqlite3_column_text(map_stmt, 0))};
|
||||||
|
|
||||||
|
if (hash_to_offset_len.count(hsh) > 0) {
|
||||||
|
auto offset_len = hash_to_offset_len.at(hsh);
|
||||||
|
if (entries.size() > 0 && tile_id == entries[entries.size() - 1].tile_id + 1 && entries[entries.size() - 1].offset == std::get<0>(offset_len)) {
|
||||||
|
entries[entries.size() - 1].run_length++;
|
||||||
|
} else {
|
||||||
|
entries.emplace_back(tile_id, std::get<0>(offset_len), std::get<1>(offset_len), 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sqlite3_bind_text(image_stmt, 1, hsh.data(), hsh.size(), SQLITE_STATIC);
|
||||||
|
if (sqlite3_step(image_stmt) != SQLITE_ROW) {
|
||||||
|
fprintf(stderr, "Corrupt mbtiles file: null entry in image table\n");
|
||||||
|
exit(EXIT_SQLITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int len = sqlite3_column_bytes(image_stmt, 0);
|
||||||
|
const char *blob = (const char *) sqlite3_column_blob(image_stmt, 0);
|
||||||
|
|
||||||
|
tmp_ostream.write(blob, len);
|
||||||
|
|
||||||
|
entries.emplace_back(tile_id, offset, len, 1);
|
||||||
|
hash_to_offset_len.emplace(hsh, std::make_pair(offset, len));
|
||||||
|
offset += len;
|
||||||
|
|
||||||
|
sqlite3_reset(image_stmt);
|
||||||
|
sqlite3_clear_bindings(image_stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3_reset(map_stmt);
|
||||||
|
sqlite3_clear_bindings(map_stmt);
|
||||||
|
}
|
||||||
|
tmp_ostream.close();
|
||||||
|
sqlite3_finalize(map_stmt);
|
||||||
|
sqlite3_finalize(image_stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// finalize PMTiles archive.
|
||||||
|
{
|
||||||
|
std::sort(entries.begin(), entries.end(), pmtiles::entryv3_cmp);
|
||||||
|
|
||||||
|
std::string root_bytes;
|
||||||
|
std::string leaves_bytes;
|
||||||
|
int num_leaves;
|
||||||
|
std::tie(root_bytes, leaves_bytes, num_leaves) = make_root_leaves(&compress_fn, pmtiles::COMPRESSION_GZIP, entries);
|
||||||
|
|
||||||
|
pmtiles::headerv3 header;
|
||||||
|
|
||||||
|
header.min_zoom = m.minzoom;
|
||||||
|
header.max_zoom = m.maxzoom;
|
||||||
|
header.min_lon_e7 = m.minlon * 10000000;
|
||||||
|
header.min_lat_e7 = m.minlat * 10000000;
|
||||||
|
header.max_lon_e7 = m.maxlon * 10000000;
|
||||||
|
header.max_lat_e7 = m.maxlat * 10000000;
|
||||||
|
header.center_zoom = m.center_z;
|
||||||
|
header.center_lon_e7 = m.center_lon * 10000000;
|
||||||
|
header.center_lat_e7 = m.center_lat * 10000000;
|
||||||
|
|
||||||
|
std::string json_metadata = metadata_to_pmtiles_json(m);
|
||||||
|
|
||||||
|
sqlite3_close(db);
|
||||||
|
|
||||||
|
header.clustered = 0x1;
|
||||||
|
header.internal_compression = pmtiles::COMPRESSION_GZIP;
|
||||||
|
|
||||||
|
if (tile_compression) {
|
||||||
|
header.tile_compression = pmtiles::COMPRESSION_GZIP;
|
||||||
|
} else {
|
||||||
|
header.tile_compression = pmtiles::COMPRESSION_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m.format == "pbf") {
|
||||||
|
header.tile_type = pmtiles::TILETYPE_MVT;
|
||||||
|
} else if (m.format == "png") {
|
||||||
|
header.tile_type = pmtiles::TILETYPE_PNG;
|
||||||
|
} else {
|
||||||
|
header.tile_type = pmtiles::TILETYPE_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
header.root_dir_offset = 127;
|
||||||
|
header.root_dir_bytes = root_bytes.size();
|
||||||
|
|
||||||
|
header.json_metadata_offset = header.root_dir_offset + header.root_dir_bytes;
|
||||||
|
header.json_metadata_bytes = json_metadata.size();
|
||||||
|
header.leaf_dirs_offset = header.json_metadata_offset + header.json_metadata_bytes;
|
||||||
|
header.leaf_dirs_bytes = leaves_bytes.size();
|
||||||
|
header.tile_data_offset = header.leaf_dirs_offset + header.leaf_dirs_bytes;
|
||||||
|
header.tile_data_bytes = offset;
|
||||||
|
|
||||||
|
header.addressed_tiles_count = tile_ids.size();
|
||||||
|
header.tile_entries_count = entries.size();
|
||||||
|
header.tile_contents_count = hash_to_offset_len.size();
|
||||||
|
|
||||||
|
std::ifstream tmp_istream(tmpname.c_str(), std::ios::in | std::ios_base::binary);
|
||||||
|
|
||||||
|
std::ofstream ostream;
|
||||||
|
ostream.open(fname, std::ios::out | std::ios::binary);
|
||||||
|
|
||||||
|
auto header_str = header.serialize();
|
||||||
|
ostream.write(header_str.data(), header_str.length());
|
||||||
|
ostream.write(root_bytes.data(), root_bytes.length());
|
||||||
|
ostream.write(json_metadata.data(), json_metadata.size());
|
||||||
|
ostream.write(leaves_bytes.data(), leaves_bytes.length());
|
||||||
|
ostream << tmp_istream.rdbuf();
|
||||||
|
|
||||||
|
tmp_istream.close();
|
||||||
|
unlink(tmpname.c_str());
|
||||||
|
ostream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this should go away if we get rid of temporary metadata DBs.
|
||||||
|
// it transforms the PMTiles header + json into string keys/string values
|
||||||
|
// consistent with mbtiles and dirtiles, for the test suite
|
||||||
|
sqlite3 *pmtilesmeta2tmp(const char *fname, const char *pmtiles_map) {
|
||||||
|
sqlite3 *db;
|
||||||
|
char *sql;
|
||||||
|
char *err;
|
||||||
|
|
||||||
|
if (sqlite3_open("", &db) != SQLITE_OK) {
|
||||||
|
fprintf(stderr, "Temporary db: %s\n", sqlite3_errmsg(db));
|
||||||
|
exit(EXIT_SQLITE);
|
||||||
|
}
|
||||||
|
if (sqlite3_exec(db, "CREATE TABLE metadata (name text, value text);", NULL, NULL, &err) != SQLITE_OK) {
|
||||||
|
fprintf(stderr, "Create metadata table: %s\n", err);
|
||||||
|
exit(EXIT_SQLITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string header_s{pmtiles_map, 127};
|
||||||
|
auto header = pmtiles::deserialize_header(header_s);
|
||||||
|
|
||||||
|
sql = sqlite3_mprintf("INSERT INTO metadata (name, value) VALUES ('minzoom', %d);", header.min_zoom);
|
||||||
|
if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
|
||||||
|
fprintf(stderr, "set minzoom: %s\n", err);
|
||||||
|
}
|
||||||
|
sqlite3_free(sql);
|
||||||
|
|
||||||
|
sql = sqlite3_mprintf("INSERT INTO metadata (name, value) VALUES ('maxzoom', %d);", header.max_zoom);
|
||||||
|
if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
|
||||||
|
fprintf(stderr, "set maxzoom: %s\n", err);
|
||||||
|
}
|
||||||
|
sqlite3_free(sql);
|
||||||
|
|
||||||
|
double center_lon = double(header.center_lon_e7) / 10000000.0;
|
||||||
|
double center_lat = double(header.center_lat_e7) / 10000000.0;
|
||||||
|
sql = sqlite3_mprintf("INSERT INTO metadata (name, value) VALUES ('center', '%f,%f,%d');", center_lon, center_lat, header.center_zoom);
|
||||||
|
if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
|
||||||
|
fprintf(stderr, "set center: %s\n", err);
|
||||||
|
}
|
||||||
|
sqlite3_free(sql);
|
||||||
|
|
||||||
|
double minlon = double(header.min_lon_e7) / 10000000.0;
|
||||||
|
double minlat = double(header.min_lat_e7) / 10000000.0;
|
||||||
|
double maxlon = double(header.max_lon_e7) / 10000000.0;
|
||||||
|
double maxlat = double(header.max_lat_e7) / 10000000.0;
|
||||||
|
sql = sqlite3_mprintf("INSERT INTO metadata (name, value) VALUES ('bounds', '%f,%f,%f,%f');", minlon, minlat, maxlon, maxlat);
|
||||||
|
if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
|
||||||
|
fprintf(stderr, "set bounds: %s\n", err);
|
||||||
|
}
|
||||||
|
sqlite3_free(sql);
|
||||||
|
|
||||||
|
std::string json_s{pmtiles_map + header.json_metadata_offset, header.json_metadata_bytes};
|
||||||
|
std::string decompressed_json;
|
||||||
|
|
||||||
|
if (header.internal_compression == pmtiles::COMPRESSION_NONE) {
|
||||||
|
decompressed_json = json_s;
|
||||||
|
} else if (header.internal_compression == pmtiles::COMPRESSION_GZIP) {
|
||||||
|
decompress(json_s, decompressed_json);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Unknown or unsupported pmtiles compression: %d\n", header.internal_compression);
|
||||||
|
exit(EXIT_OPEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
json_pull *jp = json_begin_string(decompressed_json.c_str());
|
||||||
|
json_object *o = json_read_tree(jp);
|
||||||
|
if (o == NULL) {
|
||||||
|
fprintf(stderr, "%s: metadata parsing error: %s\n", fname, jp->error);
|
||||||
|
exit(EXIT_JSON);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (o->type != JSON_HASH) {
|
||||||
|
fprintf(stderr, "%s: bad metadata format\n", fname);
|
||||||
|
exit(EXIT_JSON);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_json = false;
|
||||||
|
std::string buf;
|
||||||
|
json_writer state(&buf);
|
||||||
|
state.nospace = true;
|
||||||
|
state.json_write_hash();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < o->value.object.length; i++) {
|
||||||
|
const char *key = o->value.object.keys[i]->value.string.string;
|
||||||
|
if (strcmp(key, "vector_layers") == 0 && o->value.object.values[i]->type == JSON_ARRAY) {
|
||||||
|
has_json = true;
|
||||||
|
state.nospace = true;
|
||||||
|
state.json_write_string("vector_layers");
|
||||||
|
state.nospace = true;
|
||||||
|
state.json_write_json(json_stringify(o->value.object.values[i]));
|
||||||
|
} else if (strcmp(key, "tilestats") == 0 && o->value.object.values[i]->type == JSON_HASH) {
|
||||||
|
has_json = true;
|
||||||
|
state.nospace = true;
|
||||||
|
state.json_write_string("tilestats");
|
||||||
|
state.nospace = true;
|
||||||
|
state.json_write_json(json_stringify(o->value.object.values[i]));
|
||||||
|
} else if (strcmp(key, "strategies") == 0 && o->value.object.values[i]->type == JSON_ARRAY) {
|
||||||
|
sql = sqlite3_mprintf("INSERT INTO metadata (name, value) VALUES ('strategies', %Q);", json_stringify(o->value.object.values[i]));
|
||||||
|
if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
|
||||||
|
fprintf(stderr, "set %s in metadata: %s\n", key, err);
|
||||||
|
}
|
||||||
|
sqlite3_free(sql);
|
||||||
|
} else if (o->value.object.keys[i]->type != JSON_STRING || o->value.object.values[i]->type != JSON_STRING) {
|
||||||
|
fprintf(stderr, "%s\n", key);
|
||||||
|
fprintf(stderr, "%s: non-string in metadata\n", fname);
|
||||||
|
} else {
|
||||||
|
sql = sqlite3_mprintf("INSERT INTO metadata (name, value) VALUES (%Q, %Q);", key, o->value.object.values[i]->value.string.string);
|
||||||
|
if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
|
||||||
|
fprintf(stderr, "set %s in metadata: %s\n", key, err);
|
||||||
|
}
|
||||||
|
sqlite3_free(sql);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
json_end(jp);
|
||||||
|
state.nospace = true;
|
||||||
|
state.json_end_hash();
|
||||||
|
|
||||||
|
if (has_json) {
|
||||||
|
sql = sqlite3_mprintf("INSERT INTO metadata (name, value) VALUES ('json', %Q);", buf.c_str());
|
||||||
|
if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
|
||||||
|
fprintf(stderr, "set json in metadata: %s\n", err);
|
||||||
|
}
|
||||||
|
sqlite3_free(sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
return db;
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#ifndef PMTILES_FILE_HPP
|
||||||
|
#define PMTILES_FILE_HPP
|
||||||
|
|
||||||
|
#include "pmtiles/pmtiles.hpp"
|
||||||
|
#include "mbtiles.hpp"
|
||||||
|
|
||||||
|
bool pmtiles_has_suffix(const char *filename);
|
||||||
|
void check_pmtiles(const char *filename, char **argv, bool forcetable);
|
||||||
|
|
||||||
|
void mbtiles_map_image_to_pmtiles(char *dbname, metadata m, bool tile_compression, bool quiet, bool quiet_progress);
|
||||||
|
|
||||||
|
std::vector<pmtiles::entry_zxy> pmtiles_entries_tms(const char *pmtiles_map, int minzoom, int maxzoom);
|
||||||
|
std::pair<uint64_t, uint32_t> pmtiles_get_tile(const char *pmtiles_map, int z, int x, int y);
|
||||||
|
sqlite3 *pmtilesmeta2tmp(const char *fname, const char *pmtiles_map);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,217 @@
|
|||||||
|
{ "type": "FeatureCollection", "properties": {
|
||||||
|
"bounds": "-122.682427,45.512331,-122.654961,45.569975",
|
||||||
|
"center": "-122.662354,45.514045,14",
|
||||||
|
"description": "tests/pmtiles/hackspots.pmtiles",
|
||||||
|
"format": "pbf",
|
||||||
|
"generator_options": "./tippecanoe -q -f -o tests/pmtiles/hackspots.pmtiles -r1 -pC tests/raw-tiles/hackspots.geojson",
|
||||||
|
"json": "{\"vector_layers\":[{\"id\":\"hackspots\",\"description\":\"\",\"minzoom\":0,\"maxzoom\":14,\"fields\":{\"Address\":\"String\",\"Name\":\"String\",\"Notes\":\"String\"}}],\"tilestats\":{\"layerCount\":1,\"layers\":[{\"layer\":\"hackspots\",\"count\":4,\"geometry\":\"Point\",\"attributeCount\":3,\"attributes\":[{\"attribute\":\"Address\",\"count\":4,\"type\":\"string\",\"values\":[\"1507 N Rosa Parks Way Portland, OR 97217\",\"201 SE 12th Ave, Portland, OR 97214\",\"4637 N Albina Ave Portland, OR 97217\",\"915 SE Hawthorne Blvd. Portland, OR 97214\"]},{\"attribute\":\"Name\",\"count\":4,\"type\":\"string\",\"values\":[\"Albina Press\",\"Arbor Lodge\",\"Lucky Labrador Brew Pub\",\"Three Friends Coffeehouse\"]},{\"attribute\":\"Notes\",\"count\":3,\"type\":\"string\",\"values\":[\"\",\"Dog friendly\",\"usually busy, outlets on side wall only\"]}]}]}}",
|
||||||
|
"maxzoom": "14",
|
||||||
|
"minzoom": "0",
|
||||||
|
"name": "tests/pmtiles/hackspots.pmtiles",
|
||||||
|
"type": "overlay",
|
||||||
|
"version": "2"
|
||||||
|
}, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 0, "x": 0, "y": 0, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.583290 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.583290 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.521744 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.521744 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 1, "x": 0, "y": 0, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.583290 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.583290 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.521744 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.521744 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 2, "x": 0, "y": 1, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.583290 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.567910 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.673340, 45.521744 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.673340, 45.521744 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 3, "x": 1, "y": 2, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.684326, 45.575600 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.684326, 45.560218 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.662354, 45.521744 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.662354, 45.514046 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 4, "x": 2, "y": 5, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.684326, 45.571755 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.678833, 45.560218 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.656860, 45.521744 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656860, 45.514046 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 5, "x": 5, "y": 11, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.684326, 45.571755 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.676086, 45.558295 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.656860, 45.521744 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656860, 45.514046 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 6, "x": 10, "y": 22, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.682953, 45.570794 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.676086, 45.557333 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.655487, 45.521744 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656860, 45.513084 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 7, "x": 20, "y": 45, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.682953, 45.570313 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.675400, 45.556853 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.655487, 45.521263 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656860, 45.512602 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 8, "x": 40, "y": 91, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.682610, 45.570073 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.675400, 45.556853 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.655144, 45.521263 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656517, 45.512362 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 9, "x": 81, "y": 183, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.682438, 45.570073 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.675228, 45.556732 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.654972, 45.521263 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656345, 45.512362 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 10, "x": 163, "y": 366, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.682438, 45.570013 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.675228, 45.556732 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.654972, 45.521263 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656260, 45.512362 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 11, "x": 326, "y": 732, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.682438, 45.569983 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.675185, 45.556732 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.654972, 45.521263 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656260, 45.512332 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 12, "x": 652, "y": 1465, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.654972, 45.521263 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656260, 45.512332 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 12, "x": 652, "y": 1464, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.682438, 45.569983 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.675185, 45.556732 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.654972, 45.521263 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 13, "x": 1304, "y": 2930, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.654961, 45.521263 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656260, 45.512332 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 13, "x": 1304, "y": 2929, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.654961, 45.521263 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 13, "x": 1304, "y": 2928, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.682427, 45.569975 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.675174, 45.556732 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 14, "x": 2608, "y": 5857, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.675169, 45.556732 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 14, "x": 2608, "y": 5856, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.682427, 45.569975 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 14, "x": 2609, "y": 5860, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.654961, 45.521259 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656260, 45.512332 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
@@ -0,0 +1,217 @@
|
|||||||
|
{ "type": "FeatureCollection", "properties": {
|
||||||
|
"bounds": "-122.695313,45.506347,-122.651367,45.583290",
|
||||||
|
"center": "-122.662354,45.514045,14",
|
||||||
|
"description": "tests/pmtiles/hackspots.pmtiles",
|
||||||
|
"format": "pbf",
|
||||||
|
"generator_options": "./tippecanoe -q -f -o tests/pmtiles/hackspots.pmtiles -r1 -pC tests/raw-tiles/hackspots.geojson; ./tile-join -q -f -pC -o tests/pmtiles/joined.mbtiles tests/pmtiles/hackspots.pmtiles",
|
||||||
|
"json": "{\"vector_layers\":[{\"id\":\"hackspots\",\"description\":\"\",\"minzoom\":0,\"maxzoom\":14,\"fields\":{\"Address\":\"String\",\"Name\":\"String\",\"Notes\":\"String\"}}],\"tilestats\":{\"layerCount\":1,\"layers\":[{\"layer\":\"hackspots\",\"count\":62,\"geometry\":\"Point\",\"attributeCount\":3,\"attributes\":[{\"attribute\":\"Address\",\"count\":4,\"type\":\"string\",\"values\":[\"1507 N Rosa Parks Way Portland, OR 97217\",\"201 SE 12th Ave, Portland, OR 97214\",\"4637 N Albina Ave Portland, OR 97217\",\"915 SE Hawthorne Blvd. Portland, OR 97214\"]},{\"attribute\":\"Name\",\"count\":4,\"type\":\"string\",\"values\":[\"Albina Press\",\"Arbor Lodge\",\"Lucky Labrador Brew Pub\",\"Three Friends Coffeehouse\"]},{\"attribute\":\"Notes\",\"count\":3,\"type\":\"string\",\"values\":[\"\",\"Dog friendly\",\"usually busy, outlets on side wall only\"]}]}]}}",
|
||||||
|
"maxzoom": "14",
|
||||||
|
"minzoom": "0",
|
||||||
|
"name": "tests/pmtiles/hackspots.pmtiles",
|
||||||
|
"type": "overlay",
|
||||||
|
"version": "2"
|
||||||
|
}, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 0, "x": 0, "y": 0, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.583290 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.583290 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.521744 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.521744 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 1, "x": 0, "y": 0, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.583290 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.583290 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.521744 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.521744 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 2, "x": 0, "y": 1, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.583290 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.567910 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.673340, 45.521744 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.673340, 45.521744 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 3, "x": 1, "y": 2, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.684326, 45.575600 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.684326, 45.560218 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.662354, 45.521744 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.662354, 45.514046 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 4, "x": 2, "y": 5, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.684326, 45.571755 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.678833, 45.560218 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.656860, 45.521744 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656860, 45.514046 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 5, "x": 5, "y": 11, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.684326, 45.571755 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.676086, 45.558295 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.656860, 45.521744 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656860, 45.514046 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 6, "x": 10, "y": 22, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.682953, 45.570794 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.676086, 45.557333 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.655487, 45.521744 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656860, 45.513084 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 7, "x": 20, "y": 45, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.682953, 45.570313 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.675400, 45.556853 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.655487, 45.521263 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656860, 45.512602 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 8, "x": 40, "y": 91, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.682610, 45.570073 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.675400, 45.556853 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.655144, 45.521263 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656517, 45.512362 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 9, "x": 81, "y": 183, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.682438, 45.570073 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.675228, 45.556732 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.654972, 45.521263 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656345, 45.512362 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 10, "x": 163, "y": 366, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.682438, 45.570013 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.675228, 45.556732 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.654972, 45.521263 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656260, 45.512362 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 11, "x": 326, "y": 732, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.682438, 45.569983 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.675185, 45.556732 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.654972, 45.521263 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656260, 45.512332 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 12, "x": 652, "y": 1465, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.654972, 45.521263 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656260, 45.512332 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 12, "x": 652, "y": 1464, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.682438, 45.569983 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.675185, 45.556732 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.654972, 45.521263 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 13, "x": 1304, "y": 2930, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.654961, 45.521263 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656260, 45.512332 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 13, "x": 1304, "y": 2929, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.654961, 45.521263 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 13, "x": 1304, "y": 2928, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.682427, 45.569975 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.675174, 45.556732 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 14, "x": 2608, "y": 5857, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.675169, 45.556732 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 14, "x": 2608, "y": 5856, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.682427, 45.569975 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 14, "x": 2609, "y": 5860, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.654961, 45.521259 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656260, 45.512332 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
@@ -0,0 +1,217 @@
|
|||||||
|
{ "type": "FeatureCollection", "properties": {
|
||||||
|
"bounds": "-122.695313,45.506347,-122.651367,45.583290",
|
||||||
|
"center": "-122.662354,45.514045,14",
|
||||||
|
"description": "tests/pmtiles/hackspots.mbtiles",
|
||||||
|
"format": "pbf",
|
||||||
|
"generator_options": "./tippecanoe -q -f -o tests/pmtiles/hackspots.mbtiles -r1 -pC tests/raw-tiles/hackspots.geojson; ./tile-join -q -f -pC -o tests/pmtiles/joined.mbtiles tests/pmtiles/hackspots.mbtiles",
|
||||||
|
"json": "{\"vector_layers\": [ { \"id\": \"hackspots\", \"description\": \"\", \"minzoom\": 0, \"maxzoom\": 14, \"fields\": {\"Address\": \"String\", \"Name\": \"String\", \"Notes\": \"String\"} } ],\"tilestats\": {\"layerCount\": 1,\"layers\": [{\"layer\": \"hackspots\",\"count\": 62,\"geometry\": \"Point\",\"attributeCount\": 3,\"attributes\": [{\"attribute\": \"Address\",\"count\": 4,\"type\": \"string\",\"values\": [\"1507 N Rosa Parks Way Portland, OR 97217\",\"201 SE 12th Ave, Portland, OR 97214\",\"4637 N Albina Ave Portland, OR 97217\",\"915 SE Hawthorne Blvd. Portland, OR 97214\"]},{\"attribute\": \"Name\",\"count\": 4,\"type\": \"string\",\"values\": [\"Albina Press\",\"Arbor Lodge\",\"Lucky Labrador Brew Pub\",\"Three Friends Coffeehouse\"]},{\"attribute\": \"Notes\",\"count\": 3,\"type\": \"string\",\"values\": [\"\",\"Dog friendly\",\"usually busy, outlets on side wall only\"]}]}]}}",
|
||||||
|
"maxzoom": "14",
|
||||||
|
"minzoom": "0",
|
||||||
|
"name": "tests/pmtiles/hackspots.mbtiles",
|
||||||
|
"type": "overlay",
|
||||||
|
"version": "2"
|
||||||
|
}, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 0, "x": 0, "y": 0, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.583290 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.583290 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.521744 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.521744 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 1, "x": 0, "y": 0, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.583290 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.583290 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.521744 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.521744 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 2, "x": 0, "y": 1, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.583290 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.567910 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.673340, 45.521744 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.673340, 45.521744 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 3, "x": 1, "y": 2, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.684326, 45.575600 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.684326, 45.560218 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.662354, 45.521744 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.662354, 45.514046 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 4, "x": 2, "y": 5, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.684326, 45.571755 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.678833, 45.560218 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.656860, 45.521744 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656860, 45.514046 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 5, "x": 5, "y": 11, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.684326, 45.571755 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.676086, 45.558295 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.656860, 45.521744 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656860, 45.514046 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 6, "x": 10, "y": 22, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.682953, 45.570794 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.676086, 45.557333 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.655487, 45.521744 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656860, 45.513084 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 7, "x": 20, "y": 45, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.682953, 45.570313 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.675400, 45.556853 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.655487, 45.521263 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656860, 45.512602 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 8, "x": 40, "y": 91, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.682610, 45.570073 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.675400, 45.556853 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.655144, 45.521263 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656517, 45.512362 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 9, "x": 81, "y": 183, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.682438, 45.570073 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.675228, 45.556732 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.654972, 45.521263 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656345, 45.512362 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 10, "x": 163, "y": 366, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.682438, 45.570013 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.675228, 45.556732 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.654972, 45.521263 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656260, 45.512362 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 11, "x": 326, "y": 732, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.682438, 45.569983 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.675185, 45.556732 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.654972, 45.521263 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656260, 45.512332 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 12, "x": 652, "y": 1465, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.654972, 45.521263 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656260, 45.512332 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 12, "x": 652, "y": 1464, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.682438, 45.569983 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.675185, 45.556732 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.654972, 45.521263 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 13, "x": 1304, "y": 2930, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.654961, 45.521263 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656260, 45.512332 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 13, "x": 1304, "y": 2929, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.654961, 45.521263 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 13, "x": 1304, "y": 2928, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.682427, 45.569975 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.675174, 45.556732 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 14, "x": 2608, "y": 5857, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.675169, 45.556732 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 14, "x": 2608, "y": 5856, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.682427, 45.569975 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 14, "x": 2609, "y": 5860, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.654961, 45.521259 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656260, 45.512332 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
@@ -0,0 +1,217 @@
|
|||||||
|
{ "type": "FeatureCollection", "properties": {
|
||||||
|
"bounds": "-122.695313,45.506347,-122.651367,45.583290",
|
||||||
|
"center": "-122.662354,45.514045,14",
|
||||||
|
"description": "tests/pmtiles/hackspots.mbtiles",
|
||||||
|
"format": "pbf",
|
||||||
|
"generator_options": "./tippecanoe -q -f -o tests/pmtiles/hackspots.mbtiles -r1 -pC tests/raw-tiles/hackspots.geojson; ./tile-join -q -f -pC -o tests/pmtiles/joined.pmtiles tests/pmtiles/hackspots.mbtiles",
|
||||||
|
"json": "{\"vector_layers\":[{\"id\":\"hackspots\",\"description\":\"\",\"minzoom\":0,\"maxzoom\":14,\"fields\":{\"Address\":\"String\",\"Name\":\"String\",\"Notes\":\"String\"}}],\"tilestats\":{\"layerCount\":1,\"layers\":[{\"layer\":\"hackspots\",\"count\":62,\"geometry\":\"Point\",\"attributeCount\":3,\"attributes\":[{\"attribute\":\"Address\",\"count\":4,\"type\":\"string\",\"values\":[\"1507 N Rosa Parks Way Portland, OR 97217\",\"201 SE 12th Ave, Portland, OR 97214\",\"4637 N Albina Ave Portland, OR 97217\",\"915 SE Hawthorne Blvd. Portland, OR 97214\"]},{\"attribute\":\"Name\",\"count\":4,\"type\":\"string\",\"values\":[\"Albina Press\",\"Arbor Lodge\",\"Lucky Labrador Brew Pub\",\"Three Friends Coffeehouse\"]},{\"attribute\":\"Notes\",\"count\":3,\"type\":\"string\",\"values\":[\"\",\"Dog friendly\",\"usually busy, outlets on side wall only\"]}]}]}}",
|
||||||
|
"maxzoom": "14",
|
||||||
|
"minzoom": "0",
|
||||||
|
"name": "tests/pmtiles/hackspots.mbtiles",
|
||||||
|
"type": "overlay",
|
||||||
|
"version": "2"
|
||||||
|
}, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 0, "x": 0, "y": 0, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.583290 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.583290 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.521744 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.521744 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 1, "x": 0, "y": 0, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.583290 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.583290 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.521744 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.521744 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 2, "x": 0, "y": 1, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.583290 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.695312, 45.567910 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.673340, 45.521744 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.673340, 45.521744 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 3, "x": 1, "y": 2, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.684326, 45.575600 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.684326, 45.560218 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.662354, 45.521744 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.662354, 45.514046 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 4, "x": 2, "y": 5, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.684326, 45.571755 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.678833, 45.560218 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.656860, 45.521744 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656860, 45.514046 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 5, "x": 5, "y": 11, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.684326, 45.571755 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.676086, 45.558295 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.656860, 45.521744 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656860, 45.514046 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 6, "x": 10, "y": 22, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.682953, 45.570794 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.676086, 45.557333 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.655487, 45.521744 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656860, 45.513084 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 7, "x": 20, "y": 45, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.682953, 45.570313 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.675400, 45.556853 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.655487, 45.521263 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656860, 45.512602 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 8, "x": 40, "y": 91, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.682610, 45.570073 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.675400, 45.556853 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.655144, 45.521263 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656517, 45.512362 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 9, "x": 81, "y": 183, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.682438, 45.570073 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.675228, 45.556732 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.654972, 45.521263 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656345, 45.512362 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 10, "x": 163, "y": 366, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.682438, 45.570013 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.675228, 45.556732 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.654972, 45.521263 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656260, 45.512362 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 11, "x": 326, "y": 732, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.682438, 45.569983 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.675185, 45.556732 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.654972, 45.521263 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656260, 45.512332 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 12, "x": 652, "y": 1465, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.654972, 45.521263 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656260, 45.512332 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 12, "x": 652, "y": 1464, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.682438, 45.569983 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.675185, 45.556732 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.654972, 45.521263 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 13, "x": 1304, "y": 2930, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.654961, 45.521263 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656260, 45.512332 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 13, "x": 1304, "y": 2929, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.654961, 45.521263 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 13, "x": 1304, "y": 2928, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.682427, 45.569975 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.675174, 45.556732 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 14, "x": 2608, "y": 5857, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Albina Press", "Address": "4637 N Albina Ave Portland, OR 97217", "Notes": "usually busy, outlets on side wall only" }, "geometry": { "type": "Point", "coordinates": [ -122.675169, 45.556732 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 14, "x": 2608, "y": 5856, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Arbor Lodge", "Address": "1507 N Rosa Parks Way Portland, OR 97217", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.682427, 45.569975 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
,
|
||||||
|
{ "type": "FeatureCollection", "properties": { "zoom": 14, "x": 2609, "y": 5860, "compressed": false }, "features": [
|
||||||
|
{ "type": "FeatureCollection", "properties": { "layer": "hackspots", "version": 2, "extent": 4096 }, "features": [
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Three Friends Coffeehouse", "Address": "201 SE 12th Ave, Portland, OR 97214", "Notes": "" }, "geometry": { "type": "Point", "coordinates": [ -122.654961, 45.521259 ] } }
|
||||||
|
,
|
||||||
|
{ "type": "Feature", "properties": { "Name": "Lucky Labrador Brew Pub", "Address": "915 SE Hawthorne Blvd. Portland, OR 97214", "Notes": "Dog friendly" }, "geometry": { "type": "Point", "coordinates": [ -122.656260, 45.512332 ] } }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
|
] }
|
||||||
+57
-1
@@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
#define _DEFAULT_SOURCE
|
#define _DEFAULT_SOURCE
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -27,6 +29,7 @@
|
|||||||
#include "mbtiles.hpp"
|
#include "mbtiles.hpp"
|
||||||
#include "geometry.hpp"
|
#include "geometry.hpp"
|
||||||
#include "dirtiles.hpp"
|
#include "dirtiles.hpp"
|
||||||
|
#include "pmtiles_file.hpp"
|
||||||
#include "evaluator.hpp"
|
#include "evaluator.hpp"
|
||||||
#include "csv.hpp"
|
#include "csv.hpp"
|
||||||
#include "text.hpp"
|
#include "text.hpp"
|
||||||
@@ -373,6 +376,9 @@ struct reader {
|
|||||||
sqlite3_stmt *stmt = NULL;
|
sqlite3_stmt *stmt = NULL;
|
||||||
struct reader *next = NULL;
|
struct reader *next = NULL;
|
||||||
|
|
||||||
|
char *pmtiles_map = NULL;
|
||||||
|
std::vector<pmtiles::entry_zxy> pmtiles_entries;
|
||||||
|
|
||||||
bool operator<(const struct reader &r) const {
|
bool operator<(const struct reader &r) const {
|
||||||
if (zoom < r.zoom) {
|
if (zoom < r.zoom) {
|
||||||
return true;
|
return true;
|
||||||
@@ -427,6 +433,32 @@ struct reader *begin_reading(char *fname) {
|
|||||||
|
|
||||||
r->dirtiles.erase(r->dirtiles.begin());
|
r->dirtiles.erase(r->dirtiles.begin());
|
||||||
}
|
}
|
||||||
|
} else if (pmtiles_has_suffix(fname)) {
|
||||||
|
int pmtiles_fd = open(fname, O_RDONLY | O_CLOEXEC);
|
||||||
|
r->pmtiles_map = (char *) mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, pmtiles_fd, 0);
|
||||||
|
|
||||||
|
if (r->pmtiles_map == MAP_FAILED) {
|
||||||
|
perror("mmap in decode");
|
||||||
|
exit(EXIT_MEMORY);
|
||||||
|
}
|
||||||
|
if (close(pmtiles_fd) != 0) {
|
||||||
|
perror("close");
|
||||||
|
exit(EXIT_CLOSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
r->pmtiles_entries = pmtiles_entries_tms(r->pmtiles_map, minzoom, maxzoom);
|
||||||
|
std::reverse(r->pmtiles_entries.begin(), r->pmtiles_entries.end());
|
||||||
|
|
||||||
|
if (r->pmtiles_entries.size() == 0) {
|
||||||
|
r->zoom = 32;
|
||||||
|
} else {
|
||||||
|
r->zoom = r->pmtiles_entries.back().z;
|
||||||
|
r->x = r->pmtiles_entries.back().x;
|
||||||
|
r->y = r->pmtiles_entries.back().y;
|
||||||
|
r->sorty = (1LL << r->zoom) - 1 - r->y;
|
||||||
|
}
|
||||||
|
r->data = std::string(r->pmtiles_map + r->pmtiles_entries.back().offset, r->pmtiles_entries.back().length);
|
||||||
|
r->pmtiles_entries.pop_back();
|
||||||
} else {
|
} else {
|
||||||
sqlite3 *db;
|
sqlite3 *db;
|
||||||
|
|
||||||
@@ -736,6 +768,18 @@ void decode(struct reader *readers, std::map<std::string, layermap_entry> &layer
|
|||||||
} else {
|
} else {
|
||||||
r->zoom = 32;
|
r->zoom = 32;
|
||||||
}
|
}
|
||||||
|
} else if (r->pmtiles_map != NULL) {
|
||||||
|
if (r->pmtiles_entries.size() == 0) {
|
||||||
|
r->zoom = 32;
|
||||||
|
} else {
|
||||||
|
r->zoom = r->pmtiles_entries.back().z;
|
||||||
|
r->x = r->pmtiles_entries.back().x;
|
||||||
|
r->y = r->pmtiles_entries.back().y;
|
||||||
|
r->sorty = (1LL << r->zoom) - 1 - r->y;
|
||||||
|
r->data = std::string(r->pmtiles_map + r->pmtiles_entries.back().offset, r->pmtiles_entries.back().length);
|
||||||
|
|
||||||
|
r->pmtiles_entries.pop_back();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (r->dirtiles.size() == 0) {
|
if (r->dirtiles.size() == 0) {
|
||||||
r->zoom = 32;
|
r->zoom = 32;
|
||||||
@@ -775,7 +819,10 @@ void decode(struct reader *readers, std::map<std::string, layermap_entry> &layer
|
|||||||
next = r->next;
|
next = r->next;
|
||||||
|
|
||||||
sqlite3 *db = r->db;
|
sqlite3 *db = r->db;
|
||||||
if (db == NULL) {
|
if (r->pmtiles_map) {
|
||||||
|
db = pmtilesmeta2tmp(r->name.c_str(), r->pmtiles_map);
|
||||||
|
// json, strategies
|
||||||
|
} else if (db == NULL) {
|
||||||
db = dirmeta2tmp(r->dirbase.c_str());
|
db = dirmeta2tmp(r->dirbase.c_str());
|
||||||
} else {
|
} else {
|
||||||
sqlite3_finalize(r->stmt);
|
sqlite3_finalize(r->stmt);
|
||||||
@@ -1141,7 +1188,12 @@ int main(int argc, char **argv) {
|
|||||||
if (out_mbtiles != NULL) {
|
if (out_mbtiles != NULL) {
|
||||||
if (force) {
|
if (force) {
|
||||||
unlink(out_mbtiles);
|
unlink(out_mbtiles);
|
||||||
|
} else {
|
||||||
|
if (pmtiles_has_suffix(out_mbtiles)) {
|
||||||
|
check_pmtiles(out_mbtiles, argv, false);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
outdb = mbtiles_open(out_mbtiles, argv, 0);
|
outdb = mbtiles_open(out_mbtiles, argv, 0);
|
||||||
}
|
}
|
||||||
if (out_dir != NULL) {
|
if (out_dir != NULL) {
|
||||||
@@ -1220,5 +1272,9 @@ int main(int argc, char **argv) {
|
|||||||
json_free(filter);
|
json_free(filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pmtiles_has_suffix(out_mbtiles)) {
|
||||||
|
mbtiles_map_image_to_pmtiles(out_mbtiles, m, !pC, quiet, false);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
#ifndef VERSION_HPP
|
#ifndef VERSION_HPP
|
||||||
#define VERSION_HPP
|
#define VERSION_HPP
|
||||||
|
|
||||||
#define VERSION "v2.16.0"
|
#define VERSION "v2.17.0"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ void json_writer::json_adjust() {
|
|||||||
state[state.size() - 1] = JSON_WRITE_HASH_KEY;
|
state[state.size() - 1] = JSON_WRITE_HASH_KEY;
|
||||||
} else if (state[state.size() - 1] == JSON_WRITE_HASH_KEY) {
|
} else if (state[state.size() - 1] == JSON_WRITE_HASH_KEY) {
|
||||||
adds(":");
|
adds(":");
|
||||||
|
if (!nospace) {
|
||||||
|
addc(' ');
|
||||||
|
nospace = false;
|
||||||
|
}
|
||||||
state[state.size() - 1] = JSON_WRITE_HASH_VALUE;
|
state[state.size() - 1] = JSON_WRITE_HASH_VALUE;
|
||||||
} else if (state[state.size() - 1] == JSON_WRITE_HASH_VALUE) {
|
} else if (state[state.size() - 1] == JSON_WRITE_HASH_VALUE) {
|
||||||
if (wantnl) {
|
if (wantnl) {
|
||||||
@@ -145,6 +149,11 @@ void json_writer::json_write_string(std::string const &str) {
|
|||||||
addc('"');
|
addc('"');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void json_writer::json_write_json(std::string const &str) {
|
||||||
|
json_adjust();
|
||||||
|
adds(str);
|
||||||
|
}
|
||||||
|
|
||||||
void json_writer::json_write_number(double d) {
|
void json_writer::json_write_number(double d) {
|
||||||
json_adjust();
|
json_adjust();
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ struct json_writer {
|
|||||||
void json_write_hash();
|
void json_write_hash();
|
||||||
void json_end_hash();
|
void json_end_hash();
|
||||||
void json_write_string(std::string const &s);
|
void json_write_string(std::string const &s);
|
||||||
|
void json_write_json(std::string const &s);
|
||||||
void json_write_number(double d);
|
void json_write_number(double d);
|
||||||
void json_write_float(double d);
|
void json_write_float(double d);
|
||||||
void json_write_unsigned(unsigned long long v);
|
void json_write_unsigned(unsigned long long v);
|
||||||
|
|||||||
Reference in New Issue
Block a user