#include #include #include #include #include #include #include #include #include #include #include namespace mbgl { GeometryTileData::GeometryTileData(const OverscaledTileID& id_, std::unique_ptr tileSource_, std::string sourceID, style::Style& style_, const MapMode mode_, const std::function& callback) : TileData(id_, std::move(tileSource_)), style(style_), worker(style_.workers), tileWorker(id_, sourceID, *style_.spriteStore, *style_.glyphAtlas, *style_.glyphStore, obsolete, mode_) { auto geometryTileSource = reinterpret_cast(tileSource.get()); tileRequest = geometryTileSource->monitorTile([callback, this](std::exception_ptr err, std::unique_ptr tile, optional modified_, optional expires_) { if (err) { callback(err); return; } modified = modified_; expires = expires_; if (!tile) { // This is a 404 response. We're treating these as empty tiles. workRequest.reset(); availableData = DataAvailability::All; buckets.clear(); callback(err); return; } // Mark the tile as pending again if it was complete before to prevent signaling a complete // state despite pending parse operations. if (availableData == DataAvailability::All) { availableData = DataAvailability::Some; } // 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, style.getLayers(), std::move(tile), targetConfig, [callback, this, config = targetConfig] (TileParseResult result) { workRequest.reset(); std::exception_ptr error; if (result.is()) { auto& resultBuckets = result.get(); availableData = resultBuckets.complete ? DataAvailability::All : DataAvailability::Some; // 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. buckets = std::move(resultBuckets.buckets); if (isComplete()) { featureIndex = std::move(resultBuckets.featureIndex); geometryTile = std::move(resultBuckets.geometryTile); } } else { // This is triggered when parsing fails (e.g. due to an invalid vector tile) error = result.get(); availableData = DataAvailability::All; } callback(error); }); }); } GeometryTileData::~GeometryTileData() { cancel(); } bool GeometryTileData::parsePending(std::function callback) { if (workRequest) { // There's already parsing or placement going on. return false; } workRequest.reset(); workRequest = worker.parsePendingGeometryTileLayers(tileWorker, targetConfig, [this, callback, config = targetConfig] (TileParseResult result) { workRequest.reset(); std::exception_ptr error; if (result.is()) { auto& resultBuckets = result.get(); availableData = resultBuckets.complete ? DataAvailability::All : DataAvailability::Some; // 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); } // 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; if (isComplete()) { featureIndex = std::move(resultBuckets.featureIndex); geometryTile = std::move(resultBuckets.geometryTile); } } else { error = result.get(); availableData = DataAvailability::All; } callback(error); }); return true; } Bucket* GeometryTileData::getBucket(const style::Layer& layer) { const auto it = buckets.find(layer.baseImpl->bucketName()); if (it == buckets.end()) { return nullptr; } assert(it->second); return it->second.get(); } void GeometryTileData::redoPlacement(const PlacementConfig newConfig, const std::function& callback) { if (newConfig != placedConfig) { targetConfig = newConfig; redoPlacement(callback); } } void GeometryTileData::redoPlacement(const std::function& callback) { // Don't start a new placement request when the current one hasn't completed yet, or when // we are parsing buckets. if (workRequest) return; workRequest = worker.redoPlacement(tileWorker, buckets, targetConfig, [this, callback, config = targetConfig](std::unique_ptr collisionTile) { 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(); } if (featureIndex) { featureIndex->setCollisionTile(std::move(collisionTile)); } // The target configuration could have changed since we started placement. In this case, // we're starting another placement run. if (placedConfig != targetConfig) { redoPlacement(callback); } else { callback(); } }); } void GeometryTileData::queryRenderedFeatures( std::unordered_map>& result, const GeometryCoordinates& queryGeometry, const TransformState& transformState, const optional>& layerIDs) { if (!featureIndex || !geometryTile) return; featureIndex->query(result, { queryGeometry }, transformState.getAngle(), util::tileSize * id.overscaleFactor(), std::pow(2, transformState.getZoom() - id.overscaledZ), layerIDs, *geometryTile, id.canonical, style); } void GeometryTileData::cancel() { obsolete = true; tileRequest.reset(); workRequest.reset(); } } // namespace mbgl