[core] Merge LiveTileData into VectorTileData

To encapsulate the differences, this introduces an abstract
GeometryTileMonitor class, with concrete VectorTileMonitor and
AnnotationTileMonitor implementations, and makes pbf parsing for
VectorTile data happen lazily in getLayer, since we want that to
happen on the worker thread, rather than when VectorTile is created.

Fixes #2792
This commit is contained in:
John Firebaugh
2015-10-28 14:51:16 -07:00
parent 978da4224e
commit e2dc956eb6
12 changed files with 233 additions and 352 deletions

View File

@@ -1,5 +1,6 @@
#include <mbgl/annotation/annotation_tile.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/map/map_data.hpp>
namespace mbgl {
@@ -25,4 +26,13 @@ util::ptr<GeometryTileLayer> AnnotationTile::getLayer(const std::string& name) c
return nullptr;
}
AnnotationTileMonitor::AnnotationTileMonitor(const TileID& id, MapData& data)
: tile(data.getAnnotationManager()->getTile(id)) {
}
Request* AnnotationTileMonitor::monitorTile(std::function<void (std::exception_ptr, std::unique_ptr<GeometryTile>)> callback) {
callback(nullptr, std::move(tile));
return nullptr;
}
}

View File

@@ -38,6 +38,18 @@ public:
std::map<std::string, util::ptr<AnnotationTileLayer>> layers;
};
class MapData;
class AnnotationTileMonitor : public GeometryTileMonitor {
public:
AnnotationTileMonitor(const TileID&, MapData&);
Request* monitorTile(std::function<void (std::exception_ptr, std::unique_ptr<GeometryTile>)>) override;
private:
std::unique_ptr<AnnotationTile> tile;
};
}
#endif

View File

@@ -12,6 +12,7 @@
#include <cstdint>
#include <string>
#include <vector>
#include <functional>
namespace mbgl {
@@ -42,6 +43,21 @@ public:
virtual util::ptr<GeometryTileLayer> getLayer(const std::string&) const = 0;
};
class Request;
class GeometryTileMonitor : private util::noncopyable {
public:
/*
* Monitor the tile held by this object for changes. When the tile is loaded for the first time,
* or updates, the callback is executed. If an error occurs, the first parameter will be set.
* Otherwise it will be null. If there is no data for the requested tile, the second parameter
* will be null.
*
* To cease monitoring, release the returned Request.
*/
virtual Request* monitorTile(std::function<void (std::exception_ptr, std::unique_ptr<GeometryTile>)>) = 0;
};
class GeometryTileFeatureExtractor {
public:
GeometryTileFeatureExtractor(const GeometryTileFeature& feature_)

View File

@@ -1,133 +0,0 @@
#include <mbgl/map/live_tile_data.hpp>
#include <mbgl/annotation/annotation_tile.hpp>
#include <mbgl/style/style_layer.hpp>
#include <mbgl/map/source.hpp>
#include <mbgl/text/collision_tile.hpp>
#include <mbgl/util/worker.hpp>
#include <mbgl/util/work_request.hpp>
#include <mbgl/style/style.hpp>
#include <mbgl/style/style_bucket.hpp>
#include <sstream>
using namespace mbgl;
LiveTileData::LiveTileData(const TileID& id_,
std::unique_ptr<AnnotationTile> tile_,
Style& style,
const SourceInfo& source,
std::function<void()> callback)
: TileData(id_),
worker(style.workers),
tileWorker(id, source.source_id, style, style.layers, state),
tile(std::move(tile_)) {
state = State::loaded;
if (!tile) {
state = State::parsed;
return;
}
parsePending(callback);
}
bool LiveTileData::parsePending(std::function<void()> callback) {
if (workRequest || (state != State::loaded && state != State::partial)) {
return false;
}
workRequest.reset();
workRequest = worker.parseLiveTile(tileWorker, *tile, targetConfig, [this, callback, config = targetConfig] (TileParseResult result) {
workRequest.reset();
if (result.is<TileParseResultBuckets>()) {
auto& resultBuckets = result.get<TileParseResultBuckets>();
state = resultBuckets.state;
// Persist the configuration we just placed so that we can later check whether we need
// to place again in case the configuration has changed.
placedConfig = config;
// Move over all buckets we received in this parse request, potentially overwriting
// existing buckets in case we got a refresh parse.
for (auto& bucket : resultBuckets.buckets) {
buckets[bucket.first] = std::move(bucket.second);
}
// The target configuration could have changed since we started placement. In this case,
// we're starting another placement run.
if (placedConfig != targetConfig) {
redoPlacement();
}
} else {
error = result.get<std::string>();
state = State::obsolete;
}
callback();
// The target configuration could have changed since we started placement. In this case,
// we're starting another placement run.
if (!workRequest && placedConfig != targetConfig) {
redoPlacement();
}
});
return true;
}
LiveTileData::~LiveTileData() {
cancel();
}
Bucket* LiveTileData::getBucket(const StyleLayer& layer) {
if (!isReady() || !layer.bucket) {
return nullptr;
}
const auto it = buckets.find(layer.bucket->name);
if (it == buckets.end()) {
return nullptr;
}
assert(it->second);
return it->second.get();
}
void LiveTileData::cancel() {
state = State::obsolete;
workRequest.reset();
}
void LiveTileData::redoPlacement(const PlacementConfig newConfig) {
if (newConfig != placedConfig) {
targetConfig = newConfig;
if (!workRequest) {
// Don't start a new placement request when the current one hasn't completed yet, or
// when we are parsing buckets.
redoPlacement();
}
}
}
void LiveTileData::redoPlacement() {
workRequest.reset();
workRequest = worker.redoPlacement(tileWorker, buckets, targetConfig, [this, config = targetConfig] {
workRequest.reset();
// Persist the configuration we just placed so that we can later check whether we need to
// place again in case the configuration has changed.
placedConfig = config;
for (auto& bucket : buckets) {
bucket.second->swapRenderData();
}
// The target configuration could have changed since we started placement. In this case,
// we're starting another placement run.
if (placedConfig != targetConfig) {
redoPlacement();
}
});
}

View File

@@ -1,51 +0,0 @@
#ifndef MBGL_MAP_LIVE_TILE_DATA
#define MBGL_MAP_LIVE_TILE_DATA
#include <mbgl/map/tile_data.hpp>
#include <mbgl/map/tile_worker.hpp>
namespace mbgl {
class Style;
class SourceInfo;
class WorkRequest;
class AnnotationTile;
class LiveTileData : public TileData {
public:
LiveTileData(const TileID&,
std::unique_ptr<AnnotationTile>,
Style&,
const SourceInfo&,
std::function<void ()> callback);
~LiveTileData();
bool parsePending(std::function<void ()> callback) override;
void redoPlacement(PlacementConfig config) override;
void redoPlacement();
void cancel() override;
Bucket* getBucket(const StyleLayer&) override;
private:
Worker& worker;
TileWorker tileWorker;
std::unique_ptr<WorkRequest> workRequest;
std::unique_ptr<AnnotationTile> tile;
// Contains all the Bucket objects for the tile. Buckets are render
// objects and they get added by tile parsing operations.
std::unordered_map<std::string, std::unique_ptr<Bucket>> buckets;
// Stores the placement configuration of the text that is currently placed on the screen.
PlacementConfig placedConfig;
// Stores the placement configuration of how the text should be placed. This isn't necessarily
// the one that is being displayed.
PlacementConfig targetConfig;
};
}
#endif

View File

@@ -2,6 +2,8 @@
#include <mbgl/map/map_data.hpp>
#include <mbgl/map/transform.hpp>
#include <mbgl/map/tile.hpp>
#include <mbgl/map/vector_tile.hpp>
#include <mbgl/annotation/annotation_tile.hpp>
#include <mbgl/renderer/painter.hpp>
#include <mbgl/util/exception.hpp>
#include <mbgl/util/constants.hpp>
@@ -22,8 +24,6 @@
#include <mbgl/map/vector_tile_data.hpp>
#include <mbgl/map/raster_tile_data.hpp>
#include <mbgl/map/live_tile_data.hpp>
#include <mbgl/annotation/annotation_tile.hpp>
#include <mbgl/style/style.hpp>
#include <mbgl/gl/debugging.hpp>
@@ -285,20 +285,28 @@ TileData::State Source::addTile(MapData& data,
auto callback = std::bind(&Source::tileLoadingCompleteCallback, this, normalized_id, transformState, data.getCollisionDebug());
// If we don't find working tile data, we're just going to load it.
if (info.type == SourceType::Vector) {
auto tileData = std::make_shared<VectorTileData>(normalized_id, style, info);
tileData->request(data.pixelRatio, callback);
new_tile.data = tileData;
} else if (info.type == SourceType::Raster) {
if (info.type == SourceType::Raster) {
auto tileData = std::make_shared<RasterTileData>(normalized_id, texturePool, info, style.workers);
tileData->request(data.pixelRatio, callback);
new_tile.data = tileData;
} else if (info.type == SourceType::Annotations) {
new_tile.data = std::make_shared<LiveTileData>(normalized_id,
data.getAnnotationManager()->getTile(normalized_id), style, info, callback);
} else {
throw std::runtime_error("source type not implemented");
std::unique_ptr<GeometryTileMonitor> monitor;
if (info.type == SourceType::Vector) {
monitor = std::make_unique<VectorTileMonitor>(info, normalized_id, data.pixelRatio);
} else if (info.type == SourceType::Annotations) {
monitor = std::make_unique<AnnotationTileMonitor>(normalized_id, data);
} else {
throw std::runtime_error("source type not implemented");
}
new_tile.data = std::make_shared<VectorTileData>(normalized_id,
std::move(monitor),
info.source_id,
style,
callback);
}
tile_data.emplace(new_tile.data->id, new_tile.data);
}

View File

@@ -1,4 +1,12 @@
#include <mbgl/map/vector_tile.hpp>
#include <mbgl/map/source.hpp>
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/storage/file_source.hpp>
#include <mbgl/util/thread_context.hpp>
#include <mbgl/util/run_loop.hpp>
#include <sstream>
namespace mbgl {
@@ -122,22 +130,29 @@ GeometryCollection VectorTileFeature::getGeometries() const {
return lines;
}
VectorTile::VectorTile(pbf tile_pbf) {
while (tile_pbf.next()) {
if (tile_pbf.tag == 3) { // layer
util::ptr<VectorTileLayer> layer = std::make_shared<VectorTileLayer>(tile_pbf.message());
layers.emplace(layer->name, layer);
} else {
tile_pbf.skip();
}
}
VectorTile::VectorTile(std::shared_ptr<const std::string> data_)
: data(data_) {
}
util::ptr<GeometryTileLayer> VectorTile::getLayer(const std::string& name) const {
if (!parsed) {
parsed = true;
pbf tile_pbf(reinterpret_cast<const unsigned char *>(data->c_str()), data->size());
while (tile_pbf.next()) {
if (tile_pbf.tag == 3) { // layer
util::ptr<VectorTileLayer> layer = std::make_shared<VectorTileLayer>(tile_pbf.message());
layers.emplace(layer->name, layer);
} else {
tile_pbf.skip();
}
}
}
auto layer_it = layers.find(name);
if (layer_it != layers.end()) {
return layer_it->second;
}
return nullptr;
}
@@ -163,4 +178,32 @@ util::ptr<const GeometryTileFeature> VectorTileLayer::getFeature(std::size_t i)
return std::make_shared<VectorTileFeature>(features.at(i), *this);
}
VectorTileMonitor::VectorTileMonitor(const SourceInfo& source, const TileID& id, float pixelRatio)
: url(source.tileURL(id, pixelRatio)) {
}
Request* VectorTileMonitor::monitorTile(std::function<void (std::exception_ptr, std::unique_ptr<GeometryTile>)> callback) {
return util::ThreadContext::getFileSource()->request({ Resource::Kind::Tile, url }, util::RunLoop::getLoop(), [callback, this](const Response& res) {
if (res.data && data == res.data) {
// We got the same data again. Abort early.
return;
}
if (res.status == Response::NotFound) {
callback(nullptr, nullptr);
return;
}
if (res.status != Response::Successful) {
std::stringstream message;
message << "Failed to load [" << url << "]: " << res.message;
callback(std::make_exception_ptr(std::runtime_error(message.str())), nullptr);
return;
}
data = res.data;
callback(nullptr, std::make_unique<VectorTile>(data));
});
}
}

View File

@@ -46,12 +46,28 @@ private:
class VectorTile : public GeometryTile {
public:
VectorTile(pbf);
VectorTile(std::shared_ptr<const std::string> data);
util::ptr<GeometryTileLayer> getLayer(const std::string&) const override;
private:
std::map<std::string, util::ptr<GeometryTileLayer>> layers;
std::shared_ptr<const std::string> data;
mutable bool parsed = false;
mutable std::map<std::string, util::ptr<GeometryTileLayer>> layers;
};
class SourceInfo;
class TileID;
class VectorTileMonitor : public GeometryTileMonitor {
public:
VectorTileMonitor(const SourceInfo&, const TileID&, float pixelRatio);
Request* monitorTile(std::function<void (std::exception_ptr, std::unique_ptr<GeometryTile>)>) override;
private:
std::string url;
std::shared_ptr<const std::string> data;
};
}

View File

@@ -1,112 +1,97 @@
#include <mbgl/map/vector_tile_data.hpp>
#include <mbgl/map/geometry_tile.hpp>
#include <mbgl/style/style_layer.hpp>
#include <mbgl/style/style_bucket.hpp>
#include <mbgl/map/source.hpp>
#include <mbgl/text/collision_tile.hpp>
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/storage/file_source.hpp>
#include <mbgl/util/worker.hpp>
#include <mbgl/util/work_request.hpp>
#include <mbgl/style/style.hpp>
using namespace mbgl;
#include <sstream>
namespace mbgl {
VectorTileData::VectorTileData(const TileID& id_,
std::unique_ptr<GeometryTileMonitor> monitor_,
std::string sourceID,
Style& style_,
const SourceInfo& source_)
const std::function<void()>& callback)
: TileData(id_),
worker(style_.workers),
tileWorker(id_,
source_.source_id,
sourceID,
style_,
style_.layers,
state),
source(source_) {
}
VectorTileData::~VectorTileData() {
cancel();
}
void VectorTileData::request(float pixelRatio, const std::function<void()>& callback) {
std::string url = source.tileURL(id, pixelRatio);
monitor(std::move(monitor_))
{
state = State::loading;
FileSource* fs = util::ThreadContext::getFileSource();
req = fs->request({ Resource::Kind::Tile, url }, util::RunLoop::getLoop(), [url, callback, this](const Response &res) {
// Do not cancel the request here; we want to get notified about tiles that expire.
if (res.data && data == res.data) {
// We got the same data again. Abort early.
return;
req = monitor->monitorTile([callback, sourceID, this](std::exception_ptr err, std::unique_ptr<GeometryTile> tile) {
if (err) {
try {
std::rethrow_exception(err);
} catch (const std::exception& e) {
error = e.what();
state = State::obsolete;
callback();
return;
}
}
if (res.status == Response::NotFound) {
if (!tile) {
state = State::parsed;
callback();
return;
}
if (res.status != Response::Successful) {
std::stringstream message;
message << "Failed to load [" << url << "]: " << res.message;
error = message.str();
state = State::obsolete;
callback();
return;
}
if (state == State::loading) {
state = State::loaded;
} else if (isReady()) {
state = State::partial;
}
data = res.data;
parse(callback);
// Kick off a fresh parse of this tile. This happens when the tile is new, or
// when tile data changed. Replacing the workdRequest will cancel a pending work
// request in case there is one.
workRequest.reset();
workRequest = worker.parseGeometryTile(tileWorker, std::move(tile), targetConfig, [callback, sourceID, this, config = targetConfig] (TileParseResult result) {
workRequest.reset();
if (state == State::obsolete) {
return;
}
if (result.is<TileParseResultBuckets>()) {
auto& resultBuckets = result.get<TileParseResultBuckets>();
state = resultBuckets.state;
// Persist the configuration we just placed so that we can later check whether we need to
// place again in case the configuration has changed.
placedConfig = config;
// Move over all buckets we received in this parse request, potentially overwriting
// existing buckets in case we got a refresh parse.
for (auto& bucket : resultBuckets.buckets) {
buckets[bucket.first] = std::move(bucket.second);
}
// The target configuration could have changed since we started placement. In this case,
// we're starting another placement run.
if (placedConfig != targetConfig) {
redoPlacement();
}
} else {
std::stringstream message;
message << "Failed to parse [" << std::string(id) << "]: " << result.get<std::string>();
error = message.str();
state = State::obsolete;
}
callback();
});
});
}
void VectorTileData::parse(std::function<void ()> callback) {
// Kick off a fresh parse of this tile. This happens when the tile is new, or
// when tile data changed. Replacing the workdRequest will cancel a pending work
// request in case there is one.
workRequest.reset();
workRequest = worker.parseVectorTile(tileWorker, data, targetConfig, [this, callback, config = targetConfig] (TileParseResult result) {
workRequest.reset();
if (state == State::obsolete) {
return;
}
if (result.is<TileParseResultBuckets>()) {
auto& resultBuckets = result.get<TileParseResultBuckets>();
state = resultBuckets.state;
// Persist the configuration we just placed so that we can later check whether we need to
// place again in case the configuration has changed.
placedConfig = config;
// Move over all buckets we received in this parse request, potentially overwriting
// existing buckets in case we got a refresh parse.
for (auto& bucket : resultBuckets.buckets) {
buckets[bucket.first] = std::move(bucket.second);
}
// The target configuration could have changed since we started placement. In this case,
// we're starting another placement run.
if (placedConfig != targetConfig) {
redoPlacement();
}
} else {
std::stringstream message;
message << "Failed to parse [" << std::string(id) << "]: " << result.get<std::string>();
error = message.str();
state = State::obsolete;
}
callback();
});
VectorTileData::~VectorTileData() {
cancel();
}
bool VectorTileData::parsePending(std::function<void()> callback) {
@@ -116,7 +101,7 @@ bool VectorTileData::parsePending(std::function<void()> callback) {
}
workRequest.reset();
workRequest = worker.parsePendingVectorTileLayers(tileWorker, [this, callback] (TileParseResult result) {
workRequest = worker.parsePendingGeometryTileLayers(tileWorker, [this, callback] (TileParseResult result) {
workRequest.reset();
if (state == State::obsolete) {
return;
@@ -204,3 +189,5 @@ void VectorTileData::cancel() {
req = nullptr;
workRequest.reset();
}
}

View File

@@ -7,25 +7,27 @@
#include <mbgl/text/placement_config.hpp>
#include <atomic>
#include <memory>
#include <unordered_map>
namespace mbgl {
class Style;
class SourceInfo;
class WorkRequest;
class Request;
class GeometryTileMonitor;
class VectorTileData : public TileData {
public:
VectorTileData(
const TileID&, Style&, const SourceInfo&);
VectorTileData(const TileID&,
std::unique_ptr<GeometryTileMonitor> monitor,
std::string sourceID,
Style&,
const std::function<void()>& callback);
~VectorTileData();
Bucket* getBucket(const StyleLayer&) override;
void request(float pixelRatio, const std::function<void()>& callback);
void parse(std::function<void()> callback);
bool parsePending(std::function<void()> callback) override;
void redoPlacement(PlacementConfig config) override;
@@ -36,16 +38,15 @@ public:
private:
Worker& worker;
TileWorker tileWorker;
std::unique_ptr<GeometryTileMonitor> monitor;
std::unique_ptr<WorkRequest> workRequest;
RequestHolder req;
// Contains all the Bucket objects for the tile. Buckets are render
// objects and they get added by tile parsing operations.
std::unordered_map<std::string, std::unique_ptr<Bucket>> buckets;
const SourceInfo& source;
RequestHolder req;
std::shared_ptr<const std::string> data;
// Stores the placement configuration of the text that is currently placed on the screen.
PlacementConfig placedConfig;

View File

@@ -2,10 +2,8 @@
#include <mbgl/util/work_task.hpp>
#include <mbgl/util/work_request.hpp>
#include <mbgl/platform/platform.hpp>
#include <mbgl/map/vector_tile.hpp>
#include <mbgl/annotation/annotation_tile.hpp>
#include <mbgl/util/pbf.hpp>
#include <mbgl/renderer/raster_bucket.hpp>
#include <mbgl/map/geometry_tile.hpp>
#include <cassert>
#include <future>
@@ -35,31 +33,10 @@ public:
callback(std::move(result));
}
void parseVectorTile(TileWorker* worker,
const std::shared_ptr<const std::string> data,
PlacementConfig config,
std::function<void(TileParseResult)> callback) {
try {
pbf tilePBF(reinterpret_cast<const unsigned char*>(data->data()), data->size());
callback(worker->parseAllLayers(VectorTile(tilePBF), config));
} catch (const std::exception& ex) {
callback(TileParseResult(ex.what()));
}
}
void parsePendingVectorTileLayers(TileWorker* worker,
std::function<void(TileParseResult)> callback) {
try {
callback(worker->parsePendingLayers());
} catch (const std::exception& ex) {
callback(TileParseResult(ex.what()));
}
}
void parseLiveTile(TileWorker* worker,
const AnnotationTile* tile,
PlacementConfig config,
std::function<void(TileParseResult)> callback) {
void parseGeometryTile(TileWorker* worker,
std::unique_ptr<GeometryTile> tile,
PlacementConfig config,
std::function<void(TileParseResult)> callback) {
try {
callback(worker->parseAllLayers(*tile, config));
} catch (const std::exception& ex) {
@@ -67,6 +44,15 @@ public:
}
}
void parsePendingGeometryTileLayers(TileWorker* worker,
std::function<void(TileParseResult)> callback) {
try {
callback(worker->parsePendingLayers());
} catch (const std::exception& ex) {
callback(TileParseResult(ex.what()));
}
}
void redoPlacement(TileWorker* worker,
const std::unordered_map<std::string, std::unique_ptr<Bucket>>* buckets,
PlacementConfig config,
@@ -95,32 +81,23 @@ Worker::parseRasterTile(std::unique_ptr<RasterBucket> bucket,
}
std::unique_ptr<WorkRequest>
Worker::parseVectorTile(TileWorker& worker,
const std::shared_ptr<const std::string> data,
PlacementConfig config,
std::function<void(TileParseResult)> callback) {
Worker::parseGeometryTile(TileWorker& worker,
std::unique_ptr<GeometryTile> tile,
PlacementConfig config,
std::function<void(TileParseResult)> callback) {
current = (current + 1) % threads.size();
return threads[current]->invokeWithCallback(&Worker::Impl::parseVectorTile, callback, &worker,
data, config);
return threads[current]->invokeWithCallback(&Worker::Impl::parseGeometryTile, callback, &worker,
std::move(tile), config);
}
std::unique_ptr<WorkRequest>
Worker::parsePendingVectorTileLayers(TileWorker& worker,
std::function<void(TileParseResult)> callback) {
Worker::parsePendingGeometryTileLayers(TileWorker& worker,
std::function<void(TileParseResult)> callback) {
current = (current + 1) % threads.size();
return threads[current]->invokeWithCallback(&Worker::Impl::parsePendingVectorTileLayers,
return threads[current]->invokeWithCallback(&Worker::Impl::parsePendingGeometryTileLayers,
callback, &worker);
}
std::unique_ptr<WorkRequest> Worker::parseLiveTile(TileWorker& worker,
const AnnotationTile& tile,
PlacementConfig config,
std::function<void(TileParseResult)> callback) {
current = (current + 1) % threads.size();
return threads[current]->invokeWithCallback(&Worker::Impl::parseLiveTile, callback, &worker,
&tile, config);
}
std::unique_ptr<WorkRequest>
Worker::redoPlacement(TileWorker& worker,
const std::unordered_map<std::string, std::unique_ptr<Bucket>>& buckets,

View File

@@ -12,7 +12,7 @@ namespace mbgl {
class WorkRequest;
class RasterBucket;
class AnnotationTile;
class GeometryTileLoader;
class Worker : public mbgl::util::noncopyable {
public:
@@ -35,18 +35,13 @@ public:
std::shared_ptr<const std::string> data,
std::function<void(TileParseResult)> callback);
Request parseVectorTile(TileWorker&,
std::shared_ptr<const std::string> data,
PlacementConfig config,
std::function<void(TileParseResult)> callback);
Request parseGeometryTile(TileWorker&,
std::unique_ptr<GeometryTile>,
PlacementConfig,
std::function<void(TileParseResult)> callback);
Request parsePendingVectorTileLayers(TileWorker&,
std::function<void(TileParseResult)> callback);
Request parseLiveTile(TileWorker&,
const AnnotationTile&,
PlacementConfig config,
std::function<void(TileParseResult)> callback);
Request parsePendingGeometryTileLayers(TileWorker&,
std::function<void(TileParseResult)> callback);
Request redoPlacement(TileWorker&,
const std::unordered_map<std::string, std::unique_ptr<Bucket>>&,