[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,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();
}
}