forked from systemed/tilemaker
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmbtiles.cpp
More file actions
151 lines (128 loc) · 4.8 KB
/
mbtiles.cpp
File metadata and controls
151 lines (128 loc) · 4.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#include "mbtiles.h"
#include "helpers.h"
#include <iostream>
#include <cmath>
using namespace sqlite;
using namespace std;
MBTiles::MBTiles():
pendingStatements1(std::make_shared<std::vector<PendingStatement>>()),
pendingStatements2(std::make_shared<std::vector<PendingStatement>>())
{}
MBTiles::~MBTiles() {
if (db && inTransaction) db << "COMMIT;"; // commit all the changes if open
}
// ---- Write .mbtiles
void MBTiles::openForWriting(string &filename) {
db.init(filename);
db << "PRAGMA synchronous = OFF;";
try {
db << "PRAGMA application_id = 0x4d504258;";
} catch(runtime_error &e) {
cout << "Couldn't write SQLite application_id (not fatal): " << e.what() << endl;
}
try {
db << "PRAGMA encoding = 'UTF-8';";
} catch(runtime_error &e) {
cout << "Couldn't set SQLite default encoding (not fatal): " << e.what() << endl;
}
try {
db << "PRAGMA journal_mode=OFF;";
} catch(runtime_error &e) {
cout << "Couldn't turn journaling off (not fatal): " << e.what() << endl;
}
db << "PRAGMA page_size = 65536;";
db << "VACUUM;"; // make sure page_size takes effect
db << "CREATE TABLE IF NOT EXISTS metadata (name text, value text, UNIQUE (name));";
db << "CREATE TABLE IF NOT EXISTS tiles (zoom_level integer, tile_column integer, tile_row integer, tile_data blob);";
db << "CREATE UNIQUE INDEX IF NOT EXISTS tile_index on tiles (zoom_level, tile_column, tile_row);";
preparedStatements.emplace_back(db << "INSERT INTO tiles (zoom_level, tile_column, tile_row, tile_data) VALUES (?,?,?,?);");
preparedStatements.emplace_back(db << "REPLACE INTO tiles (zoom_level, tile_column, tile_row, tile_data) VALUES (?,?,?,?);");
db << "BEGIN;"; // begin a transaction
cout << "Creating mbtiles at " << filename << endl;
inTransaction = true;
}
void MBTiles::writeMetadata(string key, string value) {
m.lock();
db << "REPLACE INTO metadata (name,value) VALUES (?,?);" << key << value;
m.unlock();
}
void MBTiles::insertOrReplace(int zoom, int x, int y, const std::string& data, bool isMerge) {
// NB: assumes we have the `m` mutex
int tmsY = pow(2, zoom) - 1 - y;
int s = isMerge ? 1 : 0;
preparedStatements[s].reset();
preparedStatements[s] << zoom << x << tmsY && data;
preparedStatements[s].execute();
}
void MBTiles::flushPendingStatements() {
// NB: assumes we have the `m` mutex
for (int i = 0; i < 2; i++) {
while(!pendingStatements2->empty()) {
const PendingStatement& stmt = pendingStatements2->back();
insertOrReplace(stmt.zoom, stmt.x, stmt.y, stmt.data, stmt.isMerge);
pendingStatements2->pop_back();
}
std::lock_guard<std::mutex> lock(pendingStatementsMutex);
pendingStatements1.swap(pendingStatements2);
}
}
void MBTiles::saveTile(int zoom, int x, int y, string *data, bool isMerge) {
// If the lock is available, write directly to SQLite.
if (m.try_lock()) {
insertOrReplace(zoom, x, y, *data, isMerge);
flushPendingStatements();
m.unlock();
} else {
// Else buffer the write for later, copying its binary blob.
const std::lock_guard<std::mutex> lock(pendingStatementsMutex);
pendingStatements1->push_back({zoom, x, y, *data, isMerge});
}
}
void MBTiles::closeForWriting() {
flushPendingStatements();
preparedStatements[0].used(true);
preparedStatements[1].used(true);
}
// ---- Read mbtiles
void MBTiles::openForReading(string &filename) {
db.init(filename);
}
void MBTiles::readBoundingBox(double &minLon, double &maxLon, double &minLat, double &maxLat) {
string boundsStr;
db << "SELECT value FROM metadata WHERE name='bounds'" >> boundsStr;
vector<string> b = split_string(boundsStr,',');
minLon = stod(b[0]); minLat = stod(b[1]);
maxLon = stod(b[2]); maxLat = stod(b[3]);
}
void MBTiles::readTileList(std::vector<std::tuple<int,int,int>> &tileList) {
db << "SELECT zoom_level,tile_column,tile_row FROM tiles" >> [&](int z,int col, int row) {
tileList.emplace_back(std::make_tuple(z,col,row));
};
}
vector<char> MBTiles::readTile(int zoom, int col, int row) {
vector<char> pbfBlob;
db << "SELECT tile_data FROM tiles WHERE zoom_level=? AND tile_column=? AND tile_row=?" << zoom << col << row >> pbfBlob;
return pbfBlob;
}
bool MBTiles::readTileAndUncompress(string &data, int zoom, int x, int y, bool isCompressed, bool asGzip) {
m.lock();
int tmsY = pow(2,zoom) - 1 - y;
int exists=0;
db << "SELECT COUNT(*) FROM tiles WHERE zoom_level=? AND tile_column=? AND tile_row=?" << zoom << x << tmsY >> exists;
m.unlock();
if (exists==0) return false;
m.lock();
std::vector<char> compressed;
db << "SELECT tile_data FROM tiles WHERE zoom_level=? AND tile_column=? AND tile_row=?" << zoom << x << tmsY >> compressed;
m.unlock();
if (!isCompressed) {
data = std::string(compressed.data(), compressed.size());
return true;
}
try {
decompress_string(data, compressed.data(), compressed.size(), asGzip);
return true;
} catch(std::runtime_error &e) {
return false;
}
}