[core] Custom Geometry Sources
This commit is contained in:
@@ -242,6 +242,8 @@ set(MBGL_CORE_FILES
|
||||
src/mbgl/renderer/layers/render_symbol_layer.hpp
|
||||
|
||||
# renderer/sources
|
||||
src/mbgl/renderer/sources/render_custom_geometry_source.cpp
|
||||
src/mbgl/renderer/sources/render_custom_geometry_source.hpp
|
||||
src/mbgl/renderer/sources/render_geojson_source.cpp
|
||||
src/mbgl/renderer/sources/render_geojson_source.hpp
|
||||
src/mbgl/renderer/sources/render_image_source.cpp
|
||||
@@ -334,6 +336,8 @@ set(MBGL_CORE_FILES
|
||||
include/mbgl/style/types.hpp
|
||||
include/mbgl/style/undefined.hpp
|
||||
src/mbgl/style/collection.hpp
|
||||
src/mbgl/style/custom_tile_loader.cpp
|
||||
src/mbgl/style/custom_tile_loader.hpp
|
||||
src/mbgl/style/image.cpp
|
||||
src/mbgl/style/image_impl.cpp
|
||||
src/mbgl/style/image_impl.hpp
|
||||
@@ -364,6 +368,7 @@ set(MBGL_CORE_FILES
|
||||
# style/conversion
|
||||
include/mbgl/style/conversion/constant.hpp
|
||||
include/mbgl/style/conversion/coordinate.hpp
|
||||
include/mbgl/style/conversion/custom_geometry_source_options.hpp
|
||||
include/mbgl/style/conversion/data_driven_property_value.hpp
|
||||
include/mbgl/style/conversion/expression.hpp
|
||||
include/mbgl/style/conversion/filter.hpp
|
||||
@@ -506,10 +511,14 @@ set(MBGL_CORE_FILES
|
||||
src/mbgl/style/layers/symbol_layer_properties.hpp
|
||||
|
||||
# style/sources
|
||||
include/mbgl/style/sources/custom_geometry_source.hpp
|
||||
include/mbgl/style/sources/geojson_source.hpp
|
||||
include/mbgl/style/sources/image_source.hpp
|
||||
include/mbgl/style/sources/raster_source.hpp
|
||||
include/mbgl/style/sources/vector_source.hpp
|
||||
src/mbgl/style/sources/custom_geometry_source.cpp
|
||||
src/mbgl/style/sources/custom_geometry_source_impl.cpp
|
||||
src/mbgl/style/sources/custom_geometry_source_impl.hpp
|
||||
src/mbgl/style/sources/geojson_source.cpp
|
||||
src/mbgl/style/sources/geojson_source_impl.cpp
|
||||
src/mbgl/style/sources/geojson_source_impl.hpp
|
||||
@@ -555,6 +564,8 @@ set(MBGL_CORE_FILES
|
||||
# tile
|
||||
include/mbgl/tile/tile_id.hpp
|
||||
include/mbgl/tile/tile_necessity.hpp
|
||||
src/mbgl/tile/custom_geometry_tile.cpp
|
||||
src/mbgl/tile/custom_geometry_tile.hpp
|
||||
src/mbgl/tile/geojson_tile.cpp
|
||||
src/mbgl/tile/geojson_tile.hpp
|
||||
src/mbgl/tile/geojson_tile_data.hpp
|
||||
|
||||
@@ -15,6 +15,7 @@ set(MBGL_TEST_FILES
|
||||
# api
|
||||
test/api/annotations.test.cpp
|
||||
test/api/api_misuse.test.cpp
|
||||
test/api/custom_geometry_source.test.cpp
|
||||
test/api/custom_layer.test.cpp
|
||||
test/api/query.test.cpp
|
||||
test/api/recycle_map.cpp
|
||||
@@ -117,6 +118,7 @@ set(MBGL_TEST_FILES
|
||||
test/text/quads.test.cpp
|
||||
|
||||
# tile
|
||||
test/tile/custom_geometry_tile.test.cpp
|
||||
test/tile/geojson_tile.test.cpp
|
||||
test/tile/geometry_tile_data.test.cpp
|
||||
test/tile/raster_tile.test.cpp
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/style/conversion.hpp>
|
||||
#include <mbgl/style/sources/custom_geometry_source.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
namespace conversion {
|
||||
|
||||
template <>
|
||||
struct Converter<CustomGeometrySource::Options> {
|
||||
|
||||
template <class V>
|
||||
optional<CustomGeometrySource::Options> operator()(const V& value, Error& error) const {
|
||||
CustomGeometrySource::Options options;
|
||||
|
||||
const auto minzoomValue = objectMember(value, "minzoom");
|
||||
if (minzoomValue) {
|
||||
if (toNumber(*minzoomValue)) {
|
||||
options.zoomRange.min = static_cast<uint8_t>(*toNumber(*minzoomValue));
|
||||
} else {
|
||||
error = { "GeoJSON source minzoom value must be a number" };
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const auto maxzoomValue = objectMember(value, "maxzoom");
|
||||
if (maxzoomValue) {
|
||||
if (toNumber(*maxzoomValue)) {
|
||||
options.zoomRange.max = static_cast<uint8_t>(*toNumber(*maxzoomValue));
|
||||
} else {
|
||||
error = { "GeoJSON source maxzoom value must be a number" };
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const auto bufferValue = objectMember(value, "buffer");
|
||||
if (bufferValue) {
|
||||
if (toNumber(*bufferValue)) {
|
||||
options.tileOptions.buffer = static_cast<uint16_t>(*toNumber(*bufferValue));
|
||||
} else {
|
||||
error = { "GeoJSON source buffer value must be a number" };
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const auto toleranceValue = objectMember(value, "tolerance");
|
||||
if (toleranceValue) {
|
||||
if (toNumber(*toleranceValue)) {
|
||||
options.tileOptions.tolerance = static_cast<double>(*toNumber(*toleranceValue));
|
||||
} else {
|
||||
error = { "GeoJSON source tolerance value must be a number" };
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return { options };
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace conversion
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
56
include/mbgl/style/sources/custom_geometry_source.hpp
Normal file
56
include/mbgl/style/sources/custom_geometry_source.hpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/style/source.hpp>
|
||||
#include <mbgl/util/geo.hpp>
|
||||
#include <mbgl/util/geojson.hpp>
|
||||
#include <mbgl/util/range.hpp>
|
||||
#include <mbgl/util/constants.hpp>
|
||||
#include <mbgl/actor/mailbox.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class OverscaledTileID;
|
||||
class CanonicalTileID;
|
||||
|
||||
namespace style {
|
||||
|
||||
using TileFunction = std::function<void(const CanonicalTileID&)>;
|
||||
|
||||
class CustomTileLoader;
|
||||
|
||||
class CustomGeometrySource : public Source {
|
||||
public:
|
||||
struct TileOptions {
|
||||
double tolerance = 0.375;
|
||||
uint16_t tileSize = util::tileSize;
|
||||
uint16_t buffer = 128;
|
||||
};
|
||||
|
||||
struct Options {
|
||||
TileFunction fetchTileFunction;
|
||||
TileFunction cancelTileFunction;
|
||||
Range<uint8_t> zoomRange = { 0, 18};
|
||||
TileOptions tileOptions;
|
||||
};
|
||||
public:
|
||||
CustomGeometrySource(std::string id, CustomGeometrySource::Options options);
|
||||
~CustomGeometrySource() final;
|
||||
void loadDescription(FileSource&) final;
|
||||
void setTileData(const CanonicalTileID&, const GeoJSON&);
|
||||
void invalidateTile(const CanonicalTileID&);
|
||||
void invalidateRegion(const LatLngBounds&);
|
||||
// Private implementation
|
||||
class Impl;
|
||||
const Impl& impl() const;
|
||||
private:
|
||||
std::shared_ptr<Mailbox> mailbox;
|
||||
std::unique_ptr<CustomTileLoader> loader;
|
||||
};
|
||||
|
||||
template <>
|
||||
inline bool Source::is<CustomGeometrySource>() const {
|
||||
return getType() == SourceType::CustomVector;
|
||||
}
|
||||
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <mbgl/style/source.hpp>
|
||||
#include <mbgl/util/geojson.hpp>
|
||||
#include <mbgl/util/optional.hpp>
|
||||
#include <mbgl/util/constants.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
@@ -14,6 +15,7 @@ struct GeoJSONOptions {
|
||||
// GeoJSON-VT options
|
||||
uint8_t minzoom = 0;
|
||||
uint8_t maxzoom = 18;
|
||||
uint16_t tileSize = util::tileSize;
|
||||
uint16_t buffer = 128;
|
||||
double tolerance = 0.375;
|
||||
|
||||
|
||||
@@ -13,7 +13,8 @@ enum class SourceType : uint8_t {
|
||||
GeoJSON,
|
||||
Video,
|
||||
Annotations,
|
||||
Image
|
||||
Image,
|
||||
CustomVector
|
||||
};
|
||||
|
||||
enum class VisibilityType : bool {
|
||||
|
||||
@@ -129,6 +129,7 @@ OfflineRegionStatus OfflineDownload::getStatus() const {
|
||||
|
||||
case SourceType::Video:
|
||||
case SourceType::Annotations:
|
||||
case SourceType::CustomVector:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -214,6 +215,7 @@ void OfflineDownload::activateDownload() {
|
||||
|
||||
case SourceType::Video:
|
||||
case SourceType::Annotations:
|
||||
case SourceType::CustomVector:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <mbgl/renderer/tile_parameters.hpp>
|
||||
#include <mbgl/annotation/render_annotation_source.hpp>
|
||||
#include <mbgl/renderer/sources/render_image_source.hpp>
|
||||
#include <mbgl/renderer/sources/render_custom_geometry_source.hpp>
|
||||
#include <mbgl/tile/tile.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
@@ -27,6 +28,8 @@ std::unique_ptr<RenderSource> RenderSource::create(Immutable<Source::Impl> impl)
|
||||
return std::make_unique<RenderAnnotationSource>(staticImmutableCast<AnnotationSource::Impl>(impl));
|
||||
case SourceType::Image:
|
||||
return std::make_unique<RenderImageSource>(staticImmutableCast<ImageSource::Impl>(impl));
|
||||
case SourceType::CustomVector:
|
||||
return std::make_unique<RenderCustomGeometrySource>(staticImmutableCast<CustomGeometrySource::Impl>(impl));
|
||||
}
|
||||
|
||||
// Not reachable, but placate GCC.
|
||||
|
||||
86
src/mbgl/renderer/sources/render_custom_geometry_source.cpp
Normal file
86
src/mbgl/renderer/sources/render_custom_geometry_source.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
#include <mbgl/renderer/sources/render_custom_geometry_source.hpp>
|
||||
#include <mbgl/renderer/render_tile.hpp>
|
||||
#include <mbgl/renderer/paint_parameters.hpp>
|
||||
#include <mbgl/tile/custom_geometry_tile.hpp>
|
||||
|
||||
#include <mbgl/algorithm/generate_clip_ids.hpp>
|
||||
#include <mbgl/algorithm/generate_clip_ids_impl.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
using namespace style;
|
||||
|
||||
RenderCustomGeometrySource::RenderCustomGeometrySource(Immutable<style::CustomGeometrySource::Impl> impl_)
|
||||
: RenderSource(impl_) {
|
||||
tilePyramid.setObserver(this);
|
||||
}
|
||||
|
||||
const style::CustomGeometrySource::Impl& RenderCustomGeometrySource::impl() const {
|
||||
return static_cast<const style::CustomGeometrySource::Impl&>(*baseImpl);
|
||||
}
|
||||
|
||||
bool RenderCustomGeometrySource::isLoaded() const {
|
||||
return tilePyramid.isLoaded();
|
||||
}
|
||||
|
||||
void RenderCustomGeometrySource::update(Immutable<style::Source::Impl> baseImpl_,
|
||||
const std::vector<Immutable<Layer::Impl>>& layers,
|
||||
const bool needsRendering,
|
||||
const bool needsRelayout,
|
||||
const TileParameters& parameters) {
|
||||
std::swap(baseImpl, baseImpl_);
|
||||
|
||||
enabled = needsRendering;
|
||||
|
||||
auto tileLoader = impl().getTileLoader();
|
||||
if (!tileLoader) {
|
||||
return;
|
||||
}
|
||||
|
||||
tilePyramid.update(layers,
|
||||
needsRendering,
|
||||
needsRelayout,
|
||||
parameters,
|
||||
SourceType::CustomVector,
|
||||
util::tileSize,
|
||||
impl().getZoomRange(),
|
||||
[&] (const OverscaledTileID& tileID) {
|
||||
return std::make_unique<CustomGeometryTile>(tileID, impl().id, parameters, impl().getTileOptions(), *tileLoader);
|
||||
});
|
||||
}
|
||||
|
||||
void RenderCustomGeometrySource::startRender(PaintParameters& parameters) {
|
||||
parameters.clipIDGenerator.update(tilePyramid.getRenderTiles());
|
||||
tilePyramid.startRender(parameters);
|
||||
}
|
||||
|
||||
void RenderCustomGeometrySource::finishRender(PaintParameters& parameters) {
|
||||
tilePyramid.finishRender(parameters);
|
||||
}
|
||||
|
||||
std::vector<std::reference_wrapper<RenderTile>> RenderCustomGeometrySource::getRenderTiles() {
|
||||
return tilePyramid.getRenderTiles();
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, std::vector<Feature>>
|
||||
RenderCustomGeometrySource::queryRenderedFeatures(const ScreenLineString& geometry,
|
||||
const TransformState& transformState,
|
||||
const std::vector<const RenderLayer*>& layers,
|
||||
const RenderedQueryOptions& options,
|
||||
const CollisionIndex& collisionIndex) const {
|
||||
return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, collisionIndex);
|
||||
}
|
||||
|
||||
std::vector<Feature> RenderCustomGeometrySource::querySourceFeatures(const SourceQueryOptions& options) const {
|
||||
return tilePyramid.querySourceFeatures(options);
|
||||
}
|
||||
|
||||
void RenderCustomGeometrySource::onLowMemory() {
|
||||
tilePyramid.onLowMemory();
|
||||
}
|
||||
|
||||
void RenderCustomGeometrySource::dumpDebugLogs() const {
|
||||
tilePyramid.dumpDebugLogs();
|
||||
}
|
||||
|
||||
} // namespace mbgl
|
||||
50
src/mbgl/renderer/sources/render_custom_geometry_source.hpp
Normal file
50
src/mbgl/renderer/sources/render_custom_geometry_source.hpp
Normal file
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/renderer/render_source.hpp>
|
||||
#include <mbgl/renderer/tile_pyramid.hpp>
|
||||
#include <mbgl/style/sources/custom_geometry_source_impl.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class RenderCustomGeometrySource : public RenderSource {
|
||||
public:
|
||||
RenderCustomGeometrySource(Immutable<style::CustomGeometrySource::Impl>);
|
||||
|
||||
bool isLoaded() const final;
|
||||
|
||||
void update(Immutable<style::Source::Impl>,
|
||||
const std::vector<Immutable<style::Layer::Impl>>&,
|
||||
bool needsRendering,
|
||||
bool needsRelayout,
|
||||
const TileParameters&) final;
|
||||
|
||||
void startRender(PaintParameters&) final;
|
||||
void finishRender(PaintParameters&) final;
|
||||
|
||||
std::vector<std::reference_wrapper<RenderTile>> getRenderTiles() final;
|
||||
|
||||
std::unordered_map<std::string, std::vector<Feature>>
|
||||
queryRenderedFeatures(const ScreenLineString& geometry,
|
||||
const TransformState& transformState,
|
||||
const std::vector<const RenderLayer*>& layers,
|
||||
const RenderedQueryOptions& options,
|
||||
const CollisionIndex& collisionIndex) const final;
|
||||
|
||||
std::vector<Feature>
|
||||
querySourceFeatures(const SourceQueryOptions&) const final;
|
||||
|
||||
void onLowMemory() final;
|
||||
void dumpDebugLogs() const final;
|
||||
|
||||
private:
|
||||
const style::CustomGeometrySource::Impl& impl() const;
|
||||
|
||||
TilePyramid tilePyramid;
|
||||
};
|
||||
|
||||
template <>
|
||||
inline bool RenderSource::is<RenderCustomGeometrySource>() const {
|
||||
return baseImpl->type == style::SourceType::CustomVector;
|
||||
}
|
||||
|
||||
} // namespace mbgl
|
||||
114
src/mbgl/style/custom_tile_loader.cpp
Normal file
114
src/mbgl/style/custom_tile_loader.cpp
Normal file
@@ -0,0 +1,114 @@
|
||||
#include <mbgl/style/custom_tile_loader.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
|
||||
CustomTileLoader::CustomTileLoader(const TileFunction& fetchTileFn, const TileFunction& cancelTileFn) {
|
||||
fetchTileFunction = fetchTileFn;
|
||||
cancelTileFunction = cancelTileFn;
|
||||
}
|
||||
|
||||
void CustomTileLoader::fetchTile(const OverscaledTileID& tileID, ActorRef<SetTileDataFunction> callbackRef) {
|
||||
std::lock_guard<std::mutex> lock(dataCacheMutex);
|
||||
auto cachedTileData = dataCache.find(tileID.canonical);
|
||||
if (cachedTileData != dataCache.end()) {
|
||||
callbackRef.invoke(&SetTileDataFunction::operator(), *(cachedTileData->second));
|
||||
}
|
||||
auto tileCallbacks = tileCallbackMap.find(tileID.canonical);
|
||||
if (tileCallbacks == tileCallbackMap.end()) {
|
||||
auto tuple = std::make_tuple(tileID.overscaledZ, tileID.wrap, callbackRef);
|
||||
tileCallbackMap.insert({ tileID.canonical, std::vector<OverscaledIDFunctionTuple>(1, tuple) });
|
||||
} else {
|
||||
for (auto iter = tileCallbacks->second.begin(); iter != tileCallbacks->second.end(); iter++) {
|
||||
if (std::get<0>(*iter) == tileID.overscaledZ && std::get<1>(*iter) == tileID.wrap ) {
|
||||
std::get<2>(*iter) = callbackRef;
|
||||
return;
|
||||
}
|
||||
}
|
||||
tileCallbacks->second.emplace_back(std::make_tuple(tileID.overscaledZ, tileID.wrap, callbackRef));
|
||||
}
|
||||
if (cachedTileData == dataCache.end()) {
|
||||
invokeTileFetch(tileID.canonical);
|
||||
}
|
||||
}
|
||||
|
||||
void CustomTileLoader::cancelTile(const OverscaledTileID& tileID) {
|
||||
if (tileCallbackMap.find(tileID.canonical) != tileCallbackMap.end()) {
|
||||
invokeTileCancel(tileID.canonical);
|
||||
}
|
||||
}
|
||||
|
||||
void CustomTileLoader::removeTile(const OverscaledTileID& tileID) {
|
||||
auto tileCallbacks = tileCallbackMap.find(tileID.canonical);
|
||||
if (tileCallbacks == tileCallbackMap.end()) return;
|
||||
for (auto iter = tileCallbacks->second.begin(); iter != tileCallbacks->second.end(); iter++) {
|
||||
if (std::get<0>(*iter) == tileID.overscaledZ && std::get<1>(*iter) == tileID.wrap ) {
|
||||
tileCallbacks->second.erase(iter);
|
||||
invokeTileCancel(tileID.canonical);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (tileCallbacks->second.size() == 0) {
|
||||
std::lock_guard<std::mutex> lock(dataCacheMutex);
|
||||
tileCallbackMap.erase(tileCallbacks);
|
||||
dataCache.erase(tileID.canonical);
|
||||
}
|
||||
}
|
||||
|
||||
void CustomTileLoader::setTileData(const CanonicalTileID& tileID, const GeoJSON& data) {
|
||||
std::lock_guard<std::mutex> lock(dataCacheMutex);
|
||||
|
||||
auto iter = tileCallbackMap.find(tileID);
|
||||
if (iter == tileCallbackMap.end()) return;
|
||||
dataCache[tileID] = std::make_unique<mapbox::geojson::geojson>(std::move(data));;
|
||||
for (auto tuple : iter->second) {
|
||||
auto actor = std::get<2>(tuple);
|
||||
actor.invoke(&SetTileDataFunction::operator(), data);
|
||||
}
|
||||
}
|
||||
|
||||
void CustomTileLoader::invalidateTile(const CanonicalTileID& tileID) {
|
||||
auto tileCallbacks = tileCallbackMap.find(tileID);
|
||||
if (tileCallbacks == tileCallbackMap.end()) { return; }
|
||||
for (auto iter = tileCallbacks->second.begin(); iter != tileCallbacks->second.end(); iter++) {
|
||||
auto actor = std::get<2>(*iter);
|
||||
actor.invoke(&SetTileDataFunction::operator(), mapbox::geojson::feature_collection());
|
||||
invokeTileCancel(tileID);
|
||||
}
|
||||
tileCallbackMap.erase(tileCallbacks);
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(dataCacheMutex);
|
||||
dataCache.erase(tileID);
|
||||
}
|
||||
}
|
||||
|
||||
void CustomTileLoader::invalidateRegion(const LatLngBounds& bounds, Range<uint8_t> ) {
|
||||
for (auto idtuple= tileCallbackMap.begin(); idtuple != tileCallbackMap.end(); idtuple++) {
|
||||
const LatLngBounds tileBounds(idtuple->first);
|
||||
if (tileBounds.intersects(bounds) || bounds.contains(tileBounds) || tileBounds.contains(bounds)) {
|
||||
for (auto iter = idtuple->second.begin(); iter != idtuple->second.end(); iter++) {
|
||||
std::lock_guard<std::mutex> lock(dataCacheMutex);
|
||||
auto actor = std::get<2>(*iter);
|
||||
actor.invoke(&SetTileDataFunction::operator(), mapbox::geojson::feature_collection());
|
||||
invokeTileCancel(idtuple->first);
|
||||
dataCache.erase(idtuple->first);
|
||||
}
|
||||
idtuple->second.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CustomTileLoader::invokeTileFetch(const CanonicalTileID& tileID) {
|
||||
if (fetchTileFunction != nullptr) {
|
||||
fetchTileFunction(tileID);
|
||||
}
|
||||
}
|
||||
|
||||
void CustomTileLoader::invokeTileCancel(const CanonicalTileID& tileID) {
|
||||
if (cancelTileFunction != nullptr) {
|
||||
cancelTileFunction(tileID);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
47
src/mbgl/style/custom_tile_loader.hpp
Normal file
47
src/mbgl/style/custom_tile_loader.hpp
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/style/sources/custom_geometry_source.hpp>
|
||||
#include <mbgl/tile/tile_id.hpp>
|
||||
#include <mbgl/util/geo.hpp>
|
||||
#include <mbgl/util/geojson.hpp>
|
||||
#include <mbgl/actor/actor_ref.hpp>
|
||||
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
|
||||
using SetTileDataFunction = std::function<void(const GeoJSON&)>;
|
||||
|
||||
class CustomTileLoader : private util::noncopyable {
|
||||
public:
|
||||
|
||||
using OverscaledIDFunctionTuple = std::tuple<uint8_t, int16_t, ActorRef<SetTileDataFunction>>;
|
||||
|
||||
CustomTileLoader(const TileFunction& fetchTileFn, const TileFunction& cancelTileFn);
|
||||
|
||||
void fetchTile(const OverscaledTileID& tileID, ActorRef<SetTileDataFunction> callbackRef);
|
||||
void cancelTile(const OverscaledTileID& tileID);
|
||||
|
||||
void removeTile(const OverscaledTileID& tileID);
|
||||
void setTileData(const CanonicalTileID& tileID, const GeoJSON& data);
|
||||
|
||||
void invalidateTile(const CanonicalTileID&);
|
||||
void invalidateRegion(const LatLngBounds&, Range<uint8_t>);
|
||||
|
||||
private:
|
||||
void invokeTileFetch(const CanonicalTileID& tileID);
|
||||
void invokeTileCancel(const CanonicalTileID& tileID);
|
||||
|
||||
TileFunction fetchTileFunction;
|
||||
TileFunction cancelTileFunction;
|
||||
std::unordered_map<CanonicalTileID, std::vector<OverscaledIDFunctionTuple>> tileCallbackMap;
|
||||
// Keep around a cache of tile data to serve back for wrapped and over-zooomed tiles
|
||||
std::map<CanonicalTileID, std::unique_ptr<GeoJSON>> dataCache;
|
||||
std::mutex dataCacheMutex;
|
||||
|
||||
};
|
||||
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
45
src/mbgl/style/sources/custom_geometry_source.cpp
Normal file
45
src/mbgl/style/sources/custom_geometry_source.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#include <mbgl/style/sources/custom_geometry_source.hpp>
|
||||
#include <mbgl/style/custom_tile_loader.hpp>
|
||||
#include <mbgl/style/sources/custom_geometry_source_impl.hpp>
|
||||
#include <mbgl/actor/scheduler.hpp>
|
||||
#include <mbgl/tile/tile_id.hpp>
|
||||
|
||||
#include <tuple>
|
||||
#include <map>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
|
||||
CustomGeometrySource::CustomGeometrySource(std::string id,
|
||||
const CustomGeometrySource::Options options)
|
||||
: Source(makeMutable<CustomGeometrySource::Impl>(std::move(id), options)),
|
||||
mailbox(std::make_shared<Mailbox>(*Scheduler::GetCurrent())),
|
||||
loader(std::make_unique<CustomTileLoader>(options.fetchTileFunction, options.cancelTileFunction)) {
|
||||
}
|
||||
|
||||
CustomGeometrySource::~CustomGeometrySource() = default;
|
||||
|
||||
const CustomGeometrySource::Impl& CustomGeometrySource::impl() const {
|
||||
return static_cast<const CustomGeometrySource::Impl&>(*baseImpl);
|
||||
}
|
||||
|
||||
void CustomGeometrySource::loadDescription(FileSource&) {
|
||||
baseImpl = makeMutable<CustomGeometrySource::Impl>(impl(), ActorRef<CustomTileLoader>(*loader, mailbox));
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
void CustomGeometrySource::setTileData(const CanonicalTileID& tileID,
|
||||
const GeoJSON& data) {
|
||||
loader->setTileData(tileID, data);
|
||||
}
|
||||
|
||||
void CustomGeometrySource::invalidateTile(const CanonicalTileID& tileID) {
|
||||
loader->invalidateTile(tileID);
|
||||
}
|
||||
|
||||
void CustomGeometrySource::invalidateRegion(const LatLngBounds& bounds) {
|
||||
loader->invalidateRegion(bounds, impl().getZoomRange());
|
||||
}
|
||||
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
40
src/mbgl/style/sources/custom_geometry_source_impl.cpp
Normal file
40
src/mbgl/style/sources/custom_geometry_source_impl.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#include <mbgl/style/sources/custom_geometry_source_impl.hpp>
|
||||
#include <mbgl/style/source_observer.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
|
||||
CustomGeometrySource::Impl::Impl(std::string id_,
|
||||
const CustomGeometrySource::Options options)
|
||||
: Source::Impl(SourceType::CustomVector, std::move(id_)),
|
||||
tileOptions(options.tileOptions),
|
||||
zoomRange(options.zoomRange),
|
||||
loaderRef({}) {
|
||||
}
|
||||
|
||||
CustomGeometrySource::Impl::Impl(const Impl& impl, ActorRef<CustomTileLoader> loaderRef_)
|
||||
: Source::Impl(impl),
|
||||
tileOptions(impl.tileOptions),
|
||||
zoomRange(impl.zoomRange),
|
||||
loaderRef(loaderRef_){
|
||||
|
||||
}
|
||||
|
||||
optional<std::string> CustomGeometrySource::Impl::getAttribution() const {
|
||||
return {};
|
||||
}
|
||||
|
||||
CustomGeometrySource::TileOptions CustomGeometrySource::Impl::getTileOptions() const {
|
||||
return tileOptions;
|
||||
}
|
||||
|
||||
Range<uint8_t> CustomGeometrySource::Impl::getZoomRange() const {
|
||||
return zoomRange;
|
||||
}
|
||||
|
||||
optional<ActorRef<CustomTileLoader>> CustomGeometrySource::Impl::getTileLoader() const {
|
||||
return loaderRef;
|
||||
}
|
||||
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
29
src/mbgl/style/sources/custom_geometry_source_impl.hpp
Normal file
29
src/mbgl/style/sources/custom_geometry_source_impl.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/style/source_impl.hpp>
|
||||
#include <mbgl/style/sources/custom_geometry_source.hpp>
|
||||
#include <mbgl/style/custom_tile_loader.hpp>
|
||||
#include <mbgl/actor/actor_ref.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
|
||||
class CustomGeometrySource::Impl : public Source::Impl {
|
||||
public:
|
||||
Impl(std::string id, CustomGeometrySource::Options options);
|
||||
Impl(const Impl&, ActorRef<CustomTileLoader>);
|
||||
|
||||
optional<std::string> getAttribution() const final;
|
||||
|
||||
CustomGeometrySource::TileOptions getTileOptions() const;
|
||||
Range<uint8_t> getZoomRange() const;
|
||||
optional<ActorRef<CustomTileLoader>> getTileLoader() const;
|
||||
|
||||
private:
|
||||
CustomGeometrySource::TileOptions tileOptions;
|
||||
Range<uint8_t> zoomRange;
|
||||
optional<ActorRef<CustomTileLoader>> loaderRef;
|
||||
};
|
||||
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
@@ -12,6 +12,7 @@ MBGL_DEFINE_ENUM(SourceType, {
|
||||
{ SourceType::Video, "video" },
|
||||
{ SourceType::Annotations, "annotations" },
|
||||
{ SourceType::Image, "image" },
|
||||
{ SourceType::CustomVector, "customvector" }
|
||||
});
|
||||
|
||||
MBGL_DEFINE_ENUM(VisibilityType, {
|
||||
|
||||
81
src/mbgl/tile/custom_geometry_tile.cpp
Normal file
81
src/mbgl/tile/custom_geometry_tile.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
#include <mbgl/tile/custom_geometry_tile.hpp>
|
||||
#include <mbgl/tile/geojson_tile_data.hpp>
|
||||
#include <mbgl/renderer/query.hpp>
|
||||
#include <mbgl/renderer/tile_parameters.hpp>
|
||||
#include <mbgl/actor/scheduler.hpp>
|
||||
#include <mbgl/style/filter_evaluator.hpp>
|
||||
|
||||
#include <mapbox/geojsonvt.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
CustomGeometryTile::CustomGeometryTile(const OverscaledTileID& overscaledTileID,
|
||||
std::string sourceID_,
|
||||
const TileParameters& parameters,
|
||||
const style::CustomGeometrySource::TileOptions options_,
|
||||
ActorRef<style::CustomTileLoader> loader_)
|
||||
: GeometryTile(overscaledTileID, sourceID_, parameters),
|
||||
necessity(TileNecessity::Optional),
|
||||
options(options_),
|
||||
loader(loader_),
|
||||
actor(*Scheduler::GetCurrent(), std::bind(&CustomGeometryTile::setTileData, this, std::placeholders::_1)) {
|
||||
}
|
||||
|
||||
CustomGeometryTile::~CustomGeometryTile() {
|
||||
loader.invoke(&style::CustomTileLoader::removeTile, id);
|
||||
}
|
||||
|
||||
void CustomGeometryTile::setTileData(const GeoJSON& geoJSON) {
|
||||
|
||||
auto featureData = mapbox::geometry::feature_collection<int16_t>();
|
||||
if (geoJSON.is<FeatureCollection>() && !geoJSON.get<FeatureCollection>().empty()) {
|
||||
const double scale = util::EXTENT / options.tileSize;
|
||||
|
||||
mapbox::geojsonvt::TileOptions vtOptions;
|
||||
vtOptions.extent = util::EXTENT;
|
||||
vtOptions.buffer = std::round(scale * options.buffer);
|
||||
vtOptions.tolerance = scale * options.tolerance;
|
||||
featureData = mapbox::geojsonvt::geoJSONToTile(geoJSON, id.canonical.z, id.canonical.x, id.canonical.y, vtOptions).features;
|
||||
} else {
|
||||
setNecessity(TileNecessity::Optional);
|
||||
}
|
||||
setData(std::make_unique<GeoJSONTileData>(std::move(featureData)));
|
||||
}
|
||||
|
||||
//Fetching tile data for custom sources is assumed to be an expensive operation.
|
||||
// Only required tiles make fetchTile requests. Attempt to cancel a tile
|
||||
// that is no longer required.
|
||||
void CustomGeometryTile::setNecessity(TileNecessity newNecessity) {
|
||||
if (newNecessity != necessity) {
|
||||
necessity = newNecessity;
|
||||
if (necessity == TileNecessity::Required) {
|
||||
loader.invoke(&style::CustomTileLoader::fetchTile, id, actor.self());
|
||||
} else if (!isRenderable()) {
|
||||
loader.invoke(&style::CustomTileLoader::cancelTile, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CustomGeometryTile::querySourceFeatures(
|
||||
std::vector<Feature>& result,
|
||||
const SourceQueryOptions& queryOptions) {
|
||||
|
||||
// Ignore the sourceLayer, there is only one
|
||||
auto layer = getData()->getLayer({});
|
||||
|
||||
if (layer) {
|
||||
auto featureCount = layer->featureCount();
|
||||
for (std::size_t i = 0; i < featureCount; i++) {
|
||||
auto feature = layer->getFeature(i);
|
||||
|
||||
// Apply filter, if any
|
||||
if (queryOptions.filter && !(*queryOptions.filter)(*feature)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
result.push_back(convertFeature(*feature, id.canonical));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mbgl
|
||||
34
src/mbgl/tile/custom_geometry_tile.hpp
Normal file
34
src/mbgl/tile/custom_geometry_tile.hpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/tile/geometry_tile.hpp>
|
||||
#include <mbgl/util/feature.hpp>
|
||||
#include <mbgl/style/custom_tile_loader.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class TileParameters;
|
||||
|
||||
class CustomGeometryTile: public GeometryTile {
|
||||
public:
|
||||
CustomGeometryTile(const OverscaledTileID&,
|
||||
std::string sourceID,
|
||||
const TileParameters&,
|
||||
const style::CustomGeometrySource::TileOptions,
|
||||
ActorRef<style::CustomTileLoader> loader);
|
||||
~CustomGeometryTile() override;
|
||||
void setTileData(const GeoJSON& data);
|
||||
|
||||
void setNecessity(TileNecessity) final;
|
||||
|
||||
void querySourceFeatures(
|
||||
std::vector<Feature>& result,
|
||||
const SourceQueryOptions&) override;
|
||||
|
||||
private:
|
||||
TileNecessity necessity;
|
||||
const style::CustomGeometrySource::TileOptions options;
|
||||
ActorRef<style::CustomTileLoader> loader;
|
||||
Actor<style::SetTileDataFunction> actor;
|
||||
};
|
||||
|
||||
} // namespace mbgl
|
||||
71
test/api/custom_geometry_source.test.cpp
Normal file
71
test/api/custom_geometry_source.test.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
#include <mbgl/test/util.hpp>
|
||||
|
||||
#include <mbgl/gl/gl.hpp>
|
||||
#include <mbgl/map/map.hpp>
|
||||
#include <mbgl/util/shared_thread_pool.hpp>
|
||||
#include <mbgl/storage/default_file_source.hpp>
|
||||
#include <mbgl/gl/headless_frontend.hpp>
|
||||
#include <mbgl/style/style.hpp>
|
||||
#include <mbgl/style/sources/custom_geometry_source.hpp>
|
||||
#include <mbgl/style/layers/fill_layer.hpp>
|
||||
#include <mbgl/style/layers/line_layer.hpp>
|
||||
#include <mbgl/util/geojson.hpp>
|
||||
#include <mbgl/util/io.hpp>
|
||||
#include <mbgl/util/mat4.hpp>
|
||||
#include <mbgl/util/run_loop.hpp>
|
||||
|
||||
using namespace mbgl;
|
||||
using namespace mbgl::style;
|
||||
|
||||
TEST(CustomGeometrySource, Grid) {
|
||||
util::RunLoop loop;
|
||||
|
||||
DefaultFileSource fileSource(":memory:", "test/fixtures/api/assets");
|
||||
auto threadPool = sharedThreadPool();
|
||||
float pixelRatio { 1 };
|
||||
HeadlessFrontend frontend { pixelRatio, fileSource, *threadPool };
|
||||
Map map(frontend, MapObserver::nullObserver(), frontend.getSize(), pixelRatio, fileSource,
|
||||
*threadPool, MapMode::Static);
|
||||
map.getStyle().loadJSON(util::read_file("test/fixtures/api/water.json"));
|
||||
map.setLatLngZoom({ 37.8, -122.5 }, 10);
|
||||
|
||||
CustomGeometrySource::Options options;
|
||||
options.fetchTileFunction = [&map](const mbgl::CanonicalTileID& tileID) {
|
||||
double gridSpacing = 0.1;
|
||||
FeatureCollection features;
|
||||
const LatLngBounds bounds(tileID);
|
||||
for (double y = ceil(bounds.north() / gridSpacing) * gridSpacing; y >= floor(bounds.south() / gridSpacing) * gridSpacing; y -= gridSpacing) {
|
||||
|
||||
mapbox::geojson::line_string gridLine;
|
||||
gridLine.emplace_back(bounds.west(), y);
|
||||
gridLine.emplace_back(bounds.east(), y);
|
||||
|
||||
features.emplace_back(gridLine);
|
||||
}
|
||||
|
||||
for (double x = floor(bounds.west() / gridSpacing) * gridSpacing; x <= ceil(bounds.east() / gridSpacing) * gridSpacing; x += gridSpacing) {
|
||||
mapbox::geojson::line_string gridLine;
|
||||
gridLine.emplace_back(x, bounds.south());
|
||||
gridLine.emplace_back(x, bounds.north());
|
||||
|
||||
features.emplace_back(gridLine);
|
||||
}
|
||||
auto source = static_cast<CustomGeometrySource *>(map.getStyle().getSource("custom"));
|
||||
if (source) {
|
||||
source->setTileData(tileID, features);
|
||||
}
|
||||
};
|
||||
|
||||
map.getStyle().addSource(std::make_unique<CustomGeometrySource>("custom", options));
|
||||
|
||||
auto fillLayer = std::make_unique<FillLayer>("landcover", "mapbox");
|
||||
fillLayer->setSourceLayer("landcover");
|
||||
fillLayer->setFillColor(Color{ 1.0, 1.0, 0.0, 1.0 });
|
||||
map.getStyle().addLayer(std::move(fillLayer));
|
||||
|
||||
auto layer = std::make_unique<LineLayer>("grid", "custom");
|
||||
layer->setLineColor(Color{ 1.0, 1.0, 1.0, 1.0 });
|
||||
map.getStyle().addLayer(std::move(layer));
|
||||
|
||||
test::checkImage("test/fixtures/custom_geometry_source/grid", frontend.render(map), 0.0006, 0.1);
|
||||
}
|
||||
BIN
test/fixtures/custom_geometry_source/grid/expected.png
vendored
Normal file
BIN
test/fixtures/custom_geometry_source/grid/expected.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.1 KiB |
@@ -9,6 +9,7 @@
|
||||
#include <mbgl/style/sources/vector_source.hpp>
|
||||
#include <mbgl/style/sources/geojson_source.hpp>
|
||||
#include <mbgl/style/sources/image_source.hpp>
|
||||
#include <mbgl/style/sources/custom_geometry_source.hpp>
|
||||
#include <mbgl/style/layers/raster_layer.cpp>
|
||||
#include <mbgl/style/layers/line_layer.hpp>
|
||||
|
||||
@@ -548,3 +549,39 @@ TEST(Source, ImageSourceImageUpdate) {
|
||||
|
||||
test.run();
|
||||
}
|
||||
|
||||
TEST(Source, CustomGeometrySourceSetTileData) {
|
||||
SourceTest test;
|
||||
|
||||
CustomGeometrySource source("source", CustomGeometrySource::Options());
|
||||
source.loadDescription(test.fileSource);
|
||||
|
||||
LineLayer layer("id", "source");
|
||||
layer.setSourceLayer("water");
|
||||
std::vector<Immutable<Layer::Impl>> layers {{ layer.baseImpl }};
|
||||
|
||||
test.renderSourceObserver.tileChanged = [&] (RenderSource& source_, const OverscaledTileID&) {
|
||||
EXPECT_EQ("source", source_.baseImpl->id);
|
||||
test.end();
|
||||
};
|
||||
|
||||
test.renderSourceObserver.tileError = [&] (RenderSource&, const OverscaledTileID&, std::exception_ptr) {
|
||||
FAIL() << "Should never be called";
|
||||
};
|
||||
|
||||
auto renderSource = RenderSource::create(source.baseImpl);
|
||||
renderSource->setObserver(&test.renderSourceObserver);
|
||||
renderSource->update(source.baseImpl,
|
||||
layers,
|
||||
true,
|
||||
true,
|
||||
test.tileParameters);
|
||||
|
||||
test.loop.invoke([&] () {
|
||||
// Set Tile Data
|
||||
source.setTileData(CanonicalTileID(0, 0, 0), GeoJSON{ FeatureCollection{} });
|
||||
});
|
||||
|
||||
test.run();
|
||||
}
|
||||
|
||||
|
||||
130
test/tile/custom_geometry_tile.test.cpp
Normal file
130
test/tile/custom_geometry_tile.test.cpp
Normal file
@@ -0,0 +1,130 @@
|
||||
#include <mbgl/test/util.hpp>
|
||||
#include <mbgl/test/fake_file_source.hpp>
|
||||
#include <mbgl/test/stub_tile_observer.hpp>
|
||||
#include <mbgl/style/sources/custom_geometry_source.hpp>
|
||||
#include <mbgl/tile/custom_geometry_tile.hpp>
|
||||
#include <mbgl/style/custom_tile_loader.hpp>
|
||||
|
||||
#include <mbgl/util/default_thread_pool.hpp>
|
||||
#include <mbgl/util/run_loop.hpp>
|
||||
#include <mbgl/map/transform.hpp>
|
||||
#include <mbgl/renderer/tile_parameters.hpp>
|
||||
#include <mbgl/style/style.hpp>
|
||||
#include <mbgl/style/layers/circle_layer.hpp>
|
||||
#include <mbgl/annotation/annotation_manager.hpp>
|
||||
#include <mbgl/renderer/image_manager.hpp>
|
||||
#include <mbgl/text/glyph_manager.hpp>
|
||||
|
||||
#include <memory>
|
||||
|
||||
using namespace mbgl;
|
||||
using namespace mbgl::style;
|
||||
|
||||
class CustomTileTest {
|
||||
public:
|
||||
FakeFileSource fileSource;
|
||||
TransformState transformState;
|
||||
util::RunLoop loop;
|
||||
ThreadPool threadPool { 1 };
|
||||
style::Style style { loop, fileSource, 1 };
|
||||
AnnotationManager annotationManager { style };
|
||||
ImageManager imageManager;
|
||||
GlyphManager glyphManager { fileSource };
|
||||
|
||||
TileParameters tileParameters {
|
||||
1.0,
|
||||
MapDebugOptions(),
|
||||
transformState,
|
||||
threadPool,
|
||||
fileSource,
|
||||
MapMode::Continuous,
|
||||
annotationManager,
|
||||
imageManager,
|
||||
glyphManager,
|
||||
0
|
||||
};
|
||||
};
|
||||
|
||||
TEST(CustomGeometryTile, InvokeFetchTile) {
|
||||
CustomTileTest test;
|
||||
|
||||
CircleLayer layer("circle", "source");
|
||||
|
||||
mapbox::geometry::feature_collection<double> features;
|
||||
features.push_back(mapbox::geometry::feature<double> {
|
||||
mapbox::geometry::point<double>(0, 0)
|
||||
});
|
||||
CustomTileLoader loader([&](const CanonicalTileID& tileId) {
|
||||
EXPECT_EQ(tileId, CanonicalTileID(0,0,0));
|
||||
test.loop.stop();
|
||||
}, [&](const CanonicalTileID&) {
|
||||
|
||||
});
|
||||
auto mb =std::make_shared<Mailbox>(*Scheduler::GetCurrent());
|
||||
ActorRef<CustomTileLoader> loaderActor(loader, mb);
|
||||
|
||||
CustomGeometryTile tile(OverscaledTileID(0, 0, 0), "source", test.tileParameters, CustomGeometrySource::TileOptions(),
|
||||
loaderActor);
|
||||
|
||||
tile.setNecessity(TileNecessity::Required);
|
||||
|
||||
test.loop.run();
|
||||
}
|
||||
|
||||
TEST(CustomGeometryTile, InvokeCancelTile) {
|
||||
CustomTileTest test;
|
||||
|
||||
CircleLayer layer("circle", "source");
|
||||
|
||||
mapbox::geometry::feature_collection<double> features;
|
||||
features.push_back(mapbox::geometry::feature<double> {
|
||||
mapbox::geometry::point<double>(0, 0)
|
||||
});
|
||||
|
||||
CustomTileLoader loader([&](const CanonicalTileID&) { }, [&](const CanonicalTileID& tileId) {
|
||||
EXPECT_EQ(tileId, CanonicalTileID(0,0,0));
|
||||
test.loop.stop();
|
||||
});
|
||||
auto mb =std::make_shared<Mailbox>(*Scheduler::GetCurrent());
|
||||
ActorRef<CustomTileLoader> loaderActor(loader, mb);
|
||||
|
||||
CustomGeometryTile tile(OverscaledTileID(0, 0, 0), "source", test.tileParameters, CustomGeometrySource::TileOptions(),
|
||||
loaderActor);
|
||||
|
||||
tile.setNecessity(TileNecessity::Required);
|
||||
tile.setNecessity(TileNecessity::Optional);
|
||||
test.loop.run();
|
||||
}
|
||||
|
||||
TEST(CustomGeometryTile, InvokeTileChanged) {
|
||||
CustomTileTest test;
|
||||
|
||||
CircleLayer layer("circle", "source");
|
||||
|
||||
mapbox::geometry::feature_collection<double> features;
|
||||
features.push_back(mapbox::geometry::feature<double> {
|
||||
mapbox::geometry::point<double>(0, 0)
|
||||
});
|
||||
|
||||
CustomTileLoader loader(nullptr, nullptr);
|
||||
auto mb =std::make_shared<Mailbox>(*Scheduler::GetCurrent());
|
||||
ActorRef<CustomTileLoader> loaderActor(loader, mb);
|
||||
|
||||
CustomGeometryTile tile(OverscaledTileID(0, 0, 0), "source", test.tileParameters, CustomGeometrySource::TileOptions(),
|
||||
loaderActor);
|
||||
|
||||
StubTileObserver observer;
|
||||
observer.tileChanged = [&] (const Tile&) {
|
||||
// Once present, the bucket should never "disappear", which would cause
|
||||
// flickering.
|
||||
ASSERT_NE(nullptr, tile.getBucket(*layer.baseImpl));
|
||||
};
|
||||
|
||||
tile.setLayers({{ layer.baseImpl }});
|
||||
tile.setObserver(&observer);
|
||||
tile.setTileData(features);
|
||||
|
||||
while (!tile.isComplete()) {
|
||||
test.loop.runOnce();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user