@@ -142,26 +142,29 @@ class PMTilesFileSource::Impl {
142
142
tileResource.dataRange = std::make_pair (tileAddress.first ,
143
143
tileAddress.first + tileAddress.second - 1 );
144
144
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
+
146
149
Response response;
147
150
response.noContent = true ;
148
151
149
- if (tileResponse .error ) {
152
+ if (adjustedResponse .error ) {
150
153
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 );
153
156
ref.invoke (&FileSourceRequest::setResponse, response);
154
157
return ;
155
158
}
156
159
157
- response.data = tileResponse .data ;
160
+ response.data = adjustedResponse .data ;
158
161
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 ;
162
165
163
166
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 ));
165
168
}
166
169
167
170
ref.invoke (&FileSourceRequest::setResponse, response);
@@ -204,6 +207,42 @@ class PMTilesFileSource::Impl {
204
207
std::map<std::string, std::vector<std::string>> directory_cache_control;
205
208
std::map<AsyncRequest*, std::unique_ptr<AsyncRequest>> tasks;
206
209
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
+
207
246
std::shared_ptr<FileSource> getFileSource () {
208
247
if (!fileSource) {
209
248
fileSource = FileSourceManager::get ()->getFileSource (
@@ -216,6 +255,7 @@ class PMTilesFileSource::Impl {
216
255
void getHeader (const std::string& url, AsyncRequest* req, AsyncCallback callback) {
217
256
if (header_cache.find (url) != header_cache.end ()) {
218
257
callback (std::unique_ptr<Response::Error>());
258
+ return ;
219
259
}
220
260
221
261
Resource resource (Resource::Kind::Source, url);
@@ -225,10 +265,13 @@ class PMTilesFileSource::Impl {
225
265
pmtilesHeaderOffset + pmtilesHeaderLength - 1 );
226
266
227
267
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 ) ;
230
270
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) {
232
275
if (url.starts_with (mbgl::util::FILE_PROTOCOL)) {
233
276
message += " path not found: " +
234
277
url.substr (std::char_traits<char >::length (mbgl::util::FILE_PROTOCOL));
@@ -237,13 +280,13 @@ class PMTilesFileSource::Impl {
237
280
}
238
281
}
239
282
240
- callback (std::make_unique<Response::Error>(response .error ->reason , message));
283
+ callback (std::make_unique<Response::Error>(adjustedResponse .error ->reason , message));
241
284
242
285
return ;
243
286
}
244
287
245
288
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 ));
247
290
248
291
if ((header.internal_compression != pmtiles::COMPRESSION_NONE &&
249
292
header.internal_compression != pmtiles::COMPRESSION_GZIP) ||
@@ -265,6 +308,7 @@ class PMTilesFileSource::Impl {
265
308
void getMetadata (std::string& url, AsyncRequest* req, AsyncCallback callback) {
266
309
if (metadata_cache.find (url) != metadata_cache.end ()) {
267
310
callback (std::unique_ptr<Response::Error>());
311
+ return ;
268
312
}
269
313
270
314
getHeader (url, req, [=, this ](std::unique_ptr<Response::Error> error) {
@@ -372,16 +416,19 @@ class PMTilesFileSource::Impl {
372
416
resource.dataRange = std::make_pair (header.json_metadata_offset ,
373
417
header.json_metadata_offset + header.json_metadata_bytes - 1 );
374
418
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 ) {
377
424
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 ));
380
427
381
428
return ;
382
429
}
383
430
384
- std::string data = *responseMetadata .data ;
431
+ std::string data = *adjustedResponse .data ;
385
432
386
433
if (header.internal_compression == pmtiles::COMPRESSION_GZIP) {
387
434
data = util::decompress (data);
@@ -456,16 +503,19 @@ class PMTilesFileSource::Impl {
456
503
resource.dataRange = std::make_pair (directoryOffset, directoryOffset + directoryLength - 1 );
457
504
458
505
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 ) {
460
510
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 ));
463
513
464
514
return ;
465
515
}
466
516
467
517
try {
468
- std::string directoryData = *response .data ;
518
+ std::string directoryData = *adjustedResponse .data ;
469
519
470
520
if (header.internal_compression == pmtiles::COMPRESSION_GZIP) {
471
521
directoryData = util::decompress (directoryData);
0 commit comments