Skip to content

Commit 2a900c9

Browse files
committed
Initial resolution for local pmtiles asset, likely better resolution would be upstream in providers.
1 parent e7f66f9 commit 2a900c9

File tree

1 file changed

+73
-23
lines changed

1 file changed

+73
-23
lines changed

platform/default/src/mbgl/storage/pmtiles_file_source.cpp

+73-23
Original file line numberDiff line numberDiff line change
@@ -142,26 +142,29 @@ class PMTilesFileSource::Impl {
142142
tileResource.dataRange = std::make_pair(tileAddress.first,
143143
tileAddress.first + tileAddress.second - 1);
144144

145-
tasks[req] = getFileSource()->request(tileResource, [=](const Response& tileResponse) {
145+
tasks[req] = getFileSource()->request(tileResource, [=, this](const Response& tileResponse) {
146+
// Adjust response to ensure it matches the requested range
147+
Response adjustedResponse = adjustResponseRange(tileResponse, tileResource.dataRange);
148+
146149
Response response;
147150
response.noContent = true;
148151

149-
if (tileResponse.error) {
152+
if (adjustedResponse.error) {
150153
response.error = std::make_unique<Response::Error>(
151-
tileResponse.error->reason,
152-
std::string("Error fetching PMTiles tile: ") + tileResponse.error->message);
154+
adjustedResponse.error->reason,
155+
std::string("Error fetching PMTiles tile: ") + adjustedResponse.error->message);
153156
ref.invoke(&FileSourceRequest::setResponse, response);
154157
return;
155158
}
156159

157-
response.data = tileResponse.data;
160+
response.data = adjustedResponse.data;
158161
response.noContent = false;
159-
response.modified = tileResponse.modified;
160-
response.expires = tileResponse.expires;
161-
response.etag = tileResponse.etag;
162+
response.modified = adjustedResponse.modified;
163+
response.expires = adjustedResponse.expires;
164+
response.etag = adjustedResponse.etag;
162165

163166
if (header.tile_compression == pmtiles::COMPRESSION_GZIP) {
164-
response.data = std::make_shared<std::string>(util::decompress(*tileResponse.data));
167+
response.data = std::make_shared<std::string>(util::decompress(*adjustedResponse.data));
165168
}
166169

167170
ref.invoke(&FileSourceRequest::setResponse, response);
@@ -204,6 +207,42 @@ class PMTilesFileSource::Impl {
204207
std::map<std::string, std::vector<std::string>> directory_cache_control;
205208
std::map<AsyncRequest*, std::unique_ptr<AsyncRequest>> tasks;
206209

210+
// Utility function to adjust response to match requested range
211+
Response adjustResponseRange(const Response& response, const std::optional<std::pair<uint64_t, uint64_t>>& dataRange) {
212+
// If there's no data, no dataRange specified, or an error in the response, return it as is
213+
if (!response.data || response.error || !dataRange.has_value()) {
214+
return response;
215+
}
216+
217+
uint64_t requestedOffset = dataRange.value().first;
218+
uint64_t requestedEndOffset = dataRange.value().second;
219+
uint64_t requestedLength = requestedEndOffset - requestedOffset + 1;
220+
221+
// If data size matches or is smaller than requested, return as is
222+
if (response.data->size() <= requestedLength) {
223+
return response;
224+
}
225+
226+
// Data is larger than requested, need to extract the correct portion
227+
Response result = response;
228+
229+
// Check if the response appears to be the entire file (has the PMTiles header)
230+
if (response.data->size() > 7 && response.data->substr(0, 7) == "PMTiles") {
231+
// The response is likely the entire file, extract from the requested offset
232+
if (requestedOffset + requestedLength <= response.data->size()) {
233+
result.data = std::make_shared<std::string>(
234+
response.data->substr(requestedOffset, requestedLength));
235+
}
236+
} else {
237+
// The response is larger than requested but not the entire file
238+
// Assume it starts at the correct position and just trim to the requested length
239+
result.data = std::make_shared<std::string>(
240+
response.data->substr(0, requestedLength));
241+
}
242+
243+
return result;
244+
}
245+
207246
std::shared_ptr<FileSource> getFileSource() {
208247
if (!fileSource) {
209248
fileSource = FileSourceManager::get()->getFileSource(
@@ -216,6 +255,7 @@ class PMTilesFileSource::Impl {
216255
void getHeader(const std::string& url, AsyncRequest* req, AsyncCallback callback) {
217256
if (header_cache.find(url) != header_cache.end()) {
218257
callback(std::unique_ptr<Response::Error>());
258+
return;
219259
}
220260

221261
Resource resource(Resource::Kind::Source, url);
@@ -225,10 +265,13 @@ class PMTilesFileSource::Impl {
225265
pmtilesHeaderOffset + pmtilesHeaderLength - 1);
226266

227267
tasks[req] = getFileSource()->request(resource, [=, this](const Response& response) {
228-
if (response.error) {
229-
std::string message = std::string("Error fetching PMTiles header: ") + response.error->message;
268+
// Adjust response to ensure it matches the requested range
269+
Response adjustedResponse = adjustResponseRange(response, resource.dataRange);
230270

231-
if (response.error->message.empty() && response.error->reason == Response::Error::Reason::NotFound) {
271+
if (adjustedResponse.error) {
272+
std::string message = std::string("Error fetching PMTiles header: ") + adjustedResponse.error->message;
273+
274+
if (adjustedResponse.error->message.empty() && adjustedResponse.error->reason == Response::Error::Reason::NotFound) {
232275
if (url.starts_with(mbgl::util::FILE_PROTOCOL)) {
233276
message += "path not found: " +
234277
url.substr(std::char_traits<char>::length(mbgl::util::FILE_PROTOCOL));
@@ -237,13 +280,13 @@ class PMTilesFileSource::Impl {
237280
}
238281
}
239282

240-
callback(std::make_unique<Response::Error>(response.error->reason, message));
283+
callback(std::make_unique<Response::Error>(adjustedResponse.error->reason, message));
241284

242285
return;
243286
}
244287

245288
try {
246-
pmtiles::headerv3 header = pmtiles::deserialize_header(response.data->substr(0, 127));
289+
pmtiles::headerv3 header = pmtiles::deserialize_header(adjustedResponse.data->substr(0, 127));
247290

248291
if ((header.internal_compression != pmtiles::COMPRESSION_NONE &&
249292
header.internal_compression != pmtiles::COMPRESSION_GZIP) ||
@@ -265,6 +308,7 @@ class PMTilesFileSource::Impl {
265308
void getMetadata(std::string& url, AsyncRequest* req, AsyncCallback callback) {
266309
if (metadata_cache.find(url) != metadata_cache.end()) {
267310
callback(std::unique_ptr<Response::Error>());
311+
return;
268312
}
269313

270314
getHeader(url, req, [=, this](std::unique_ptr<Response::Error> error) {
@@ -372,16 +416,19 @@ class PMTilesFileSource::Impl {
372416
resource.dataRange = std::make_pair(header.json_metadata_offset,
373417
header.json_metadata_offset + header.json_metadata_bytes - 1);
374418

375-
tasks[req] = getFileSource()->request(resource, [=](const Response& responseMetadata) {
376-
if (responseMetadata.error) {
419+
tasks[req] = getFileSource()->request(resource, [=, this](const Response& responseMetadata) {
420+
// Adjust response to ensure it matches the requested range
421+
Response adjustedResponse = adjustResponseRange(responseMetadata, resource.dataRange);
422+
423+
if (adjustedResponse.error) {
377424
callback(std::make_unique<Response::Error>(
378-
responseMetadata.error->reason,
379-
std::string("Error fetching PMTiles metadata: ") + responseMetadata.error->message));
425+
adjustedResponse.error->reason,
426+
std::string("Error fetching PMTiles metadata: ") + adjustedResponse.error->message));
380427

381428
return;
382429
}
383430

384-
std::string data = *responseMetadata.data;
431+
std::string data = *adjustedResponse.data;
385432

386433
if (header.internal_compression == pmtiles::COMPRESSION_GZIP) {
387434
data = util::decompress(data);
@@ -456,16 +503,19 @@ class PMTilesFileSource::Impl {
456503
resource.dataRange = std::make_pair(directoryOffset, directoryOffset + directoryLength - 1);
457504

458505
tasks[req] = getFileSource()->request(resource, [=, this](const Response& response) {
459-
if (response.error) {
506+
// Adjust response to ensure it matches the requested range
507+
Response adjustedResponse = adjustResponseRange(response, resource.dataRange);
508+
509+
if (adjustedResponse.error) {
460510
callback(std::make_unique<Response::Error>(
461-
response.error->reason,
462-
std::string("Error fetching PMTiles directory: ") + response.error->message));
511+
adjustedResponse.error->reason,
512+
std::string("Error fetching PMTiles directory: ") + adjustedResponse.error->message));
463513

464514
return;
465515
}
466516

467517
try {
468-
std::string directoryData = *response.data;
518+
std::string directoryData = *adjustedResponse.data;
469519

470520
if (header.internal_compression == pmtiles::COMPRESSION_GZIP) {
471521
directoryData = util::decompress(directoryData);

0 commit comments

Comments
 (0)