Enable FastPFOR encodings in MLT (#4146)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Bart Louwers <bart@emeel.net>
This commit is contained in:
Tim Sylvester
2026-07-14 12:31:41 -07:00
committed by GitHub
parent f442c704b8
commit 247c776d26
34 changed files with 227 additions and 78 deletions

2
.gitmodules vendored
View File

@@ -103,7 +103,7 @@
[submodule "vendor/maplibre-tile-spec"]
path = vendor/maplibre-tile-spec
url = https://github.com/maplibre/maplibre-tile-spec.git
branch = cpp
branch = main
[submodule "vendor/kdbush.hpp"]
path = vendor/kdbush.hpp
url = https://github.com/mourner/kdbush.hpp.git

View File

@@ -154,7 +154,7 @@ cc_library(
"//vendor:unordered_dense",
"//vendor:vector-tile",
"//vendor:wagyu",
"//vendor/maplibre-tile-spec/cpp:mlt_cpp",
"@maplibre_tile_spec//cpp:mlt_cpp",
] + select({
"@platforms//os:ios": [
"//vendor:ghc_filesystem",

View File

@@ -13,6 +13,12 @@ bazel_dep(name = "rules_cc", version = "0.2.14")
bazel_dep(name = "rules_shell", version = "0.6.1")
bazel_dep(name = "rules_swift_package_manager", version = "1.3.0")
bazel_dep(name = "googletest", version = "1.17.0.bcr.2")
bazel_dep(name = "maplibre-tile-spec", version = "0.0.0", repo_name = "maplibre_tile_spec")
local_path_override(
module_name = "maplibre-tile-spec",
path = "vendor/maplibre-tile-spec",
)
# rules_xcodeproj accidentally includes a trailing comma in the generated
# Testing.framework rsync exclusion, causing intermittent test-host build races.

View File

@@ -133,6 +133,22 @@ public:
*/
float pixelRatio() const;
/**
* @brief Sets whether to enable decoding of MapLibre Tile (MLT) tiles with FastPFOR integer encodings.
* By default, it is set to false. Such tiles will fail if not explicitly enabled.
*
* @param enable true to enable, false to disable
* @return MapOptions for chaining options together.
*/
MapOptions& withFastPFOREnabled(bool enable);
/**
* @brief Gets the previously set (or default) value.
*
* @return true if enabled, alse otherwise.
*/
bool fastPFOREnabled() const;
private:
class Impl;
std::unique_ptr<Impl> impl_;

View File

@@ -47,5 +47,11 @@ bool NativeMapOptions::asyncRendererCleanup(jni::JNIEnv &env, const jni::Object<
return obj.Get(env, asyncRendererCleanupField);
}
bool NativeMapOptions::fastPFOREnabled(jni::JNIEnv &env, const jni::Object<NativeMapOptions> &obj) {
auto &javaClass = jni::Class<NativeMapOptions>::Singleton(env);
auto field = javaClass.GetField<jni::jboolean>(env, "fastPFOREnabled");
return obj.Get(env, field);
}
} // namespace android
} // namespace mbgl

View File

@@ -20,6 +20,7 @@ public:
static float pixelRatio(jni::JNIEnv&, const jni::Object<NativeMapOptions>&);
static bool crossSourceCollisions(jni::JNIEnv&, const jni::Object<NativeMapOptions>&);
static bool asyncRendererCleanup(jni::JNIEnv&, const jni::Object<NativeMapOptions>&);
static bool fastPFOREnabled(jni::JNIEnv&, const jni::Object<NativeMapOptions>&);
};
} // namespace android

View File

@@ -87,7 +87,8 @@ NativeMapView::NativeMapView(jni::JNIEnv& _env,
.withPixelRatio(pixelRatio)
.withConstrainMode(ConstrainMode::HeightOnly)
.withViewportMode(ViewportMode::Default)
.withCrossSourceCollisions(NativeMapOptions::crossSourceCollisions(_env, jNativeMapOptions));
.withCrossSourceCollisions(NativeMapOptions::crossSourceCollisions(_env, jNativeMapOptions))
.withFastPFOREnabled(NativeMapOptions::fastPFOREnabled(_env, jNativeMapOptions));
// Create the core map
map = std::make_unique<mbgl::Map>(

View File

@@ -102,6 +102,8 @@ public class MapLibreMapOptions implements Parcelable {
private boolean asyncRendererCleanup = false;
private boolean fastPFOREnabled = false;
/**
* Creates a new MapLibreMapOptions object.
*
@@ -167,6 +169,8 @@ public class MapLibreMapOptions implements Parcelable {
actionJournalRenderingReportInterval = in.readInt();
asyncRendererCleanup = in.readByte() != 0;
fastPFOREnabled = in.readByte() != 0;
}
/**
@@ -340,6 +344,10 @@ public class MapLibreMapOptions implements Parcelable {
maplibreMapOptions.asyncRendererCleanup(
typedArray.getBoolean(R.styleable.maplibre_MapView_maplibre_asyncRendererCleanup, false)
);
maplibreMapOptions.fastPFOREnabled(
typedArray.getBoolean(R.styleable.maplibre_MapView_maplibre_fastPFOREnabled, false)
);
} finally {
typedArray.recycle();
}
@@ -852,6 +860,16 @@ public class MapLibreMapOptions implements Parcelable {
return this;
}
/**
* Enable FastPFOR decompression for vector tiles, defaults to false.
* @param enable true to enable, false to disable
* @return This
*/
@NonNull public MapLibreMapOptions fastPFOREnabled(boolean enable) {
this.fastPFOREnabled = enable;
return this;
}
/**
* Enable local ideograph font family, defaults to true.
*
@@ -999,6 +1017,15 @@ public class MapLibreMapOptions implements Parcelable {
return asyncRendererCleanup;
}
/**
* Check whether FastPFOR decompression is enabled for vector tiles.
*
* @return true if enabled
*/
public boolean getFastPFOREnabled() {
return fastPFOREnabled;
}
/**
* Set the flag to render the map surface on top of another surface.
*
@@ -1391,6 +1418,8 @@ public class MapLibreMapOptions implements Parcelable {
dest.writeInt(actionJournalRenderingReportInterval);
dest.writeByte((byte) (asyncRendererCleanup ? 1 : 0));
dest.writeByte((byte) (fastPFOREnabled ? 1 : 0));
}
@Override
@@ -1535,6 +1564,10 @@ public class MapLibreMapOptions implements Parcelable {
return false;
}
if (fastPFOREnabled != options.fastPFOREnabled) {
return false;
}
return false;
}
@@ -1588,6 +1621,7 @@ public class MapLibreMapOptions implements Parcelable {
result = 31 * result + (int) actionJournalLogFileCount;
result = 31 * result + (int) actionJournalRenderingReportInterval;
result = 31 * result + (asyncRendererCleanup ? 1 : 0);
result = 31 * result + (fastPFOREnabled ? 1 : 0);
return result;
}
}

View File

@@ -12,6 +12,8 @@ public class NativeMapOptions {
private final int actionJournalRenderingReportInterval;
private final boolean asyncRendererCleanup;
private final boolean fastPFOREnabled;
public NativeMapOptions(MapLibreMapOptions options) {
pixelRatio = options.getPixelRatio();
crossSourceCollisions = options.getCrossSourceCollisions();
@@ -22,6 +24,7 @@ public class NativeMapOptions {
actionJournalLogFileCount = options.getActionJournalLogFileCount();
actionJournalRenderingReportInterval = options.getActionJournalRenderingReportInterval();
asyncRendererCleanup = options.getAsyncRendererCleanup();
fastPFOREnabled = options.getFastPFOREnabled();
}
public NativeMapOptions(float pixelRatio, boolean crossSourceCollisions) {
@@ -35,6 +38,7 @@ public class NativeMapOptions {
actionJournalRenderingReportInterval = 0;
asyncRendererCleanup = false;
fastPFOREnabled = false;
}
public float pixelRatio() {
@@ -68,4 +72,8 @@ public class NativeMapOptions {
public boolean asyncRendererCleanup() {
return asyncRendererCleanup;
}
public boolean fastPFOREnabled() {
return fastPFOREnabled;
}
}

View File

@@ -11,6 +11,7 @@
<attr name="maplibre_localIdeographFontFamily" format="string"/>
<attr name="maplibre_localIdeographFontFamilies" format="reference"/>
<attr name="maplibre_cross_source_collisions" format="boolean"/>
<attr name="maplibre_fastPFOREnabled" format="boolean"/>
<!--Action Journal-->
<attr name="maplibre_actionJournalEnabled" format="boolean"/>

View File

@@ -1,6 +1,7 @@
#Mon Jun 15 10:28:30 CDT 2026
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

View File

@@ -21,7 +21,7 @@ dependencyResolutionManagement {
}
plugins {
id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
}
include(":MapLibreAndroid", ":MapLibreAndroidTestApp", ":MapLibreAndroidLint")

View File

@@ -37,6 +37,11 @@ MLN_EXPORT
*/
@property NSArray *pluginLayers;
/**
Enable FastPFOR decoding for vector tiles. Defaults to `NO`.
*/
@property bool fastPFOREnabled;
@end
NS_ASSUME_NONNULL_END

View File

@@ -12,8 +12,8 @@
_styleURL = nil;
_styleJSON = nil;
_pluginLayers = nil;
_actionJournalOptions = [[MLNActionJournalOptions alloc] init];
_fastPFOREnabled = NO;
}
return self;

View File

@@ -710,7 +710,8 @@ public:
.withPixelRatio(config.scaleFactor)
.withConstrainMode(mbgl::ConstrainMode::None)
.withViewportMode(mbgl::ViewportMode::Default)
.withCrossSourceCollisions(enableCrossSourceCollisions);
.withCrossSourceCollisions(enableCrossSourceCollisions)
.withFastPFOREnabled(mlnMapoptions.fastPFOREnabled);
mbgl::TileServerOptions *tileServerOptions =
[[MLNSettings sharedSettings] tileServerOptionsInternal];

View File

@@ -54,6 +54,7 @@ Map::Impl::Impl(RendererFrontend& frontend_,
mode(mapOptions.mapMode()),
pixelRatio(mapOptions.pixelRatio()),
crossSourceCollisions(mapOptions.crossSourceCollisions()),
fastPFOREnabled(mapOptions.fastPFOREnabled()),
fileSource(std::move(fileSource_)),
style(std::make_unique<style::Style>(fileSource, pixelRatio, frontend_.getThreadPool())),
annotationManager(*style) {
@@ -132,6 +133,7 @@ void Map::Impl::onUpdate() {
.prefetchZoomDelta = prefetchZoomDelta,
.stillImageRequest = bool(stillImageRequest),
.crossSourceCollisions = crossSourceCollisions,
.fastPFOREnabled = fastPFOREnabled,
.tileLodMinRadius = tileLodMinRadius,
.tileLodScale = tileLodScale,
.tileLodPitchThreshold = tileLodPitchThreshold,

View File

@@ -91,6 +91,7 @@ public:
const MapMode mode;
const float pixelRatio;
const bool crossSourceCollisions;
const bool fastPFOREnabled;
MapDebugOptions debugOptions{MapDebugOptions::NoDebug};
std::unique_ptr<gfx::RenderingStatsView> renderingStatsView;

View File

@@ -11,6 +11,7 @@ public:
bool crossSourceCollisions = true;
Size size = {64, 64};
float pixelRatio = 1.0;
bool fastPFOREnabled = false;
};
// These requires the complete type of Impl.
@@ -82,4 +83,13 @@ float MapOptions::pixelRatio() const {
return impl_->pixelRatio;
}
MapOptions& MapOptions::withFastPFOREnabled(bool enable) {
impl_->fastPFOREnabled = enable;
return *this;
}
bool MapOptions::fastPFOREnabled() const {
return impl_->fastPFOREnabled;
}
} // namespace mbgl

View File

@@ -339,6 +339,7 @@ std::unique_ptr<RenderTree> RenderOrchestrator::createRenderTree(
std::unique_ptr<RenderSource> renderSource = RenderSource::create(entry.second, threadPool);
renderSource->setObserver(this);
renderSource->setCacheEnabled(tileCacheEnabled);
renderSource->setFastPFOREnabled(updateParameters->fastPFOREnabled);
renderSources.emplace(entry.first, std::move(renderSource));
}
transformState = updateParameters->transformState;

View File

@@ -3,13 +3,14 @@
#include <mbgl/annotation/render_annotation_source.hpp>
#include <mbgl/layermanager/layer_manager.hpp>
#include <mbgl/renderer/render_source_observer.hpp>
#include <mbgl/renderer/sources/render_geojson_source.hpp>
#include <mbgl/renderer/sources/render_raster_source.hpp>
#include <mbgl/renderer/sources/render_raster_dem_source.hpp>
#include <mbgl/renderer/sources/render_vector_source.hpp>
#include <mbgl/renderer/sources/render_image_source.hpp>
#include <mbgl/renderer/sources/render_custom_geometry_source.hpp>
#include <mbgl/renderer/sources/render_geojson_source.hpp>
#include <mbgl/renderer/sources/render_image_source.hpp>
#include <mbgl/renderer/sources/render_raster_dem_source.hpp>
#include <mbgl/renderer/sources/render_raster_source.hpp>
#include <mbgl/renderer/sources/render_vector_source.hpp>
#include <mbgl/renderer/tile_parameters.hpp>
#include <mbgl/renderer/update_parameters.hpp>
#include <mbgl/tile/tile.hpp>
#include <mbgl/util/constants.hpp>
@@ -21,28 +22,24 @@ namespace mbgl {
using namespace style;
std::unique_ptr<RenderSource> RenderSource::create(const Immutable<Source::Impl>& impl,
const TaggedScheduler& threadPool_) {
const TaggedScheduler& threadPool) {
switch (impl->type) {
case SourceType::Vector:
// the TileSet isn't available yet, so we can't make different sources by format
return std::make_unique<RenderVectorSource>(staticImmutableCast<TileSource::Impl>(impl),
std::move(threadPool_));
return std::make_unique<RenderVectorSource>(staticImmutableCast<TileSource::Impl>(impl), threadPool);
case SourceType::Raster:
return std::make_unique<RenderRasterSource>(staticImmutableCast<TileSource::Impl>(impl),
std::move(threadPool_));
return std::make_unique<RenderRasterSource>(staticImmutableCast<TileSource::Impl>(impl), threadPool);
case SourceType::RasterDEM:
return std::make_unique<RenderRasterDEMSource>(staticImmutableCast<TileSource::Impl>(impl),
std::move(threadPool_));
return std::make_unique<RenderRasterDEMSource>(staticImmutableCast<TileSource::Impl>(impl), threadPool);
case SourceType::GeoJSON:
return std::make_unique<RenderGeoJSONSource>(staticImmutableCast<GeoJSONSource::Impl>(impl),
std::move(threadPool_));
return std::make_unique<RenderGeoJSONSource>(staticImmutableCast<GeoJSONSource::Impl>(impl), threadPool);
case SourceType::Video:
assert(false);
return nullptr;
case SourceType::Annotations:
if (LayerManager::annotationsEnabled) {
return std::make_unique<RenderAnnotationSource>(staticImmutableCast<AnnotationSource::Impl>(impl),
std::move(threadPool_));
threadPool);
} else {
assert(false);
return nullptr;
@@ -51,7 +48,7 @@ std::unique_ptr<RenderSource> RenderSource::create(const Immutable<Source::Impl>
return std::make_unique<RenderImageSource>(staticImmutableCast<ImageSource::Impl>(impl));
case SourceType::CustomVector:
return std::make_unique<RenderCustomGeometrySource>(staticImmutableCast<CustomGeometrySource::Impl>(impl),
std::move(threadPool_));
threadPool);
}
// Not reachable, but placate GCC.

View File

@@ -33,6 +33,7 @@ class Tile;
class TileParameters;
class TransformParameters;
class TransformState;
class UpdateParameters;
namespace gfx {
class UploadPass;
@@ -103,7 +104,9 @@ public:
const std::optional<std::string>&,
const std::optional<std::string>&) {}
virtual void setCacheEnabled(bool) {};
virtual void setCacheEnabled(bool) {}
virtual void setFastPFOREnabled(bool) {}
virtual void reduceMemoryUse() = 0;

View File

@@ -22,29 +22,30 @@ void RenderVectorSource::updateInternal(const Tileset& tileset,
const bool needsRendering,
const bool needsRelayout,
const TileParameters& parameters) {
tilePyramid.update(
layers,
needsRendering,
needsRelayout,
parameters,
*baseImpl,
util::tileSize_I,
tileset.zoomRange,
tileset.bounds,
[&](const OverscaledTileID& tileID, TileObserver* observer_) -> std::unique_ptr<VectorTile> {
if (!isMLT.has_value()) {
auto impl = staticImmutableCast<style::TileSource::Impl>(baseImpl);
assert(impl->tileset); // we should have one by now
if (impl->tileset) {
isMLT = (impl->tileset->vectorEncoding == Tileset::VectorEncoding::MLT);
}
}
if (isMLT && *isMLT) {
return std::make_unique<VectorMLTTile>(tileID, baseImpl->id, parameters, tileset, observer_);
} else {
return std::make_unique<VectorMVTTile>(tileID, baseImpl->id, parameters, tileset, observer_);
}
});
tilePyramid.update(layers,
needsRendering,
needsRelayout,
parameters,
*baseImpl,
util::tileSize_I,
tileset.zoomRange,
tileset.bounds,
[&](const OverscaledTileID& tileID, TileObserver* observer_) -> std::unique_ptr<VectorTile> {
if (!isMLT.has_value()) {
auto impl = staticImmutableCast<style::TileSource::Impl>(baseImpl);
assert(impl->tileset); // we should have one by now
if (impl->tileset) {
isMLT = (impl->tileset->vectorEncoding == Tileset::VectorEncoding::MLT);
}
}
if (isMLT && *isMLT) {
return std::make_unique<VectorMLTTile>(
tileID, baseImpl->id, parameters, tileset, observer_, fastPFOREnabled);
} else {
return std::make_unique<VectorMVTTile>(
tileID, baseImpl->id, parameters, tileset, observer_);
}
});
}
} // namespace mbgl

View File

@@ -10,16 +10,22 @@ class RenderVectorSource final : public RenderTileSetSource {
public:
explicit RenderVectorSource(Immutable<style::TileSource::Impl>, const TaggedScheduler&);
private:
/// Enable the decoding of MLT tiles with FastPFOR integer encodings.
/// Default is false. Such tiles will fail if not explicitly enabled.
void setFastPFOREnabled(bool enable) override { fastPFOREnabled = enable; }
protected:
void updateInternal(const Tileset&,
const std::vector<Immutable<style::LayerProperties>>&,
bool needsRendering,
bool needsRelayout,
const TileParameters&) override;
const std::optional<Tileset>& getTileset() const override;
private:
std::optional<bool> isMLT;
bool fastPFOREnabled = false;
};
} // namespace mbgl

View File

@@ -48,6 +48,8 @@ public:
const bool crossSourceCollisions;
const bool fastPFOREnabled = false;
double tileLodMinRadius = 3;
double tileLodScale = 1;
double tileLodPitchThreshold = (60.0 / 180.0) * std::numbers::pi;

View File

@@ -12,8 +12,10 @@ VectorMLTTile::VectorMLTTile(const OverscaledTileID& id_,
std::string sourceID_,
const TileParameters& parameters_,
const Tileset& tileset_,
TileObserver* observer_)
: VectorTile(id_, std::move(sourceID_), parameters_, tileset_, observer_) {}
TileObserver* observer_,
bool fastPFOREnabled_)
: VectorTile(id_, std::move(sourceID_), parameters_, tileset_, observer_),
fastPFOREnabled(fastPFOREnabled_) {}
VectorMLTTile::~VectorMLTTile() {
// this needs to be explicitly deleted in the most-derived destructor
@@ -26,7 +28,7 @@ VectorMLTTile::~VectorMLTTile() {
void VectorMLTTile::setData(const std::shared_ptr<const std::string>& data_) {
if (!obsolete) {
GeometryTile::setData(data_ ? std::make_unique<VectorMLTTileData>(data_) : nullptr);
GeometryTile::setData(data_ ? std::make_unique<VectorMLTTileData>(data_, fastPFOREnabled) : nullptr);
}
}

View File

@@ -15,11 +15,15 @@ public:
std::string sourceID,
const TileParameters&,
const Tileset&,
TileObserver* observer = nullptr);
TileObserver* observer,
bool fastPFOREnabled);
~VectorMLTTile() override;
void setData(const std::shared_ptr<const std::string>& data) override;
private:
bool fastPFOREnabled;
};
} // namespace mbgl

View File

@@ -78,7 +78,7 @@ const PropertyMap& VectorMLTTileFeature::getProperties() const {
}
FeatureIdentifier VectorMLTTileFeature::getID() const {
return feature.getID();
return feature.getID().has_value() ? mapbox::feature::identifier(*feature.getID()) : mapbox::feature::null_value;
}
namespace {
@@ -174,12 +174,14 @@ std::string VectorMLTTileLayer::getName() const {
return layer.getName();
}
VectorMLTTileData::VectorMLTTileData(std::shared_ptr<const std::string> data_)
: data(std::move(data_)) {}
VectorMLTTileData::VectorMLTTileData(std::shared_ptr<const std::string> data_, bool fastPFOREnabled_)
: data(std::move(data_)),
fastPFOREnabled(fastPFOREnabled_) {}
VectorMLTTileData::VectorMLTTileData(const VectorMLTTileData& other)
: data(other.data),
tile(other.tile) {}
tile(other.tile),
fastPFOREnabled(other.fastPFOREnabled) {}
std::unique_ptr<GeometryTileData> VectorMLTTileData::clone() const {
return std::make_unique<VectorMLTTileData>(*this);
@@ -191,7 +193,7 @@ std::unique_ptr<GeometryTileLayer> VectorMLTTileData::getLayer(const std::string
if (data && !tile) {
try {
mlt::DataView tileData{data->data(), data->size()};
tile = std::make_shared<MapLibreTile>(mlt::Decoder().decode(tileData));
tile = std::make_shared<MapLibreTile>(mlt::Decoder(fastPFOREnabled).decode(tileData));
} catch (const std::exception& ex) {
Log::Warning(Event::ParseTile, "MLT parse failed: " + std::string(ex.what()));
}

View File

@@ -68,7 +68,7 @@ private:
class VectorMLTTileData : public GeometryTileData {
public:
VectorMLTTileData(std::shared_ptr<const std::string> data);
VectorMLTTileData(std::shared_ptr<const std::string> data, bool fastPFOREnabled);
VectorMLTTileData(const VectorMLTTileData&);
VectorMLTTileData(VectorMLTTileData&&) = default;
@@ -80,6 +80,7 @@ public:
private:
mutable std::shared_ptr<const std::string> data;
mutable std::shared_ptr<const MapLibreTile> tile;
bool fastPFOREnabled;
};
} // namespace mbgl

Binary file not shown.

BIN
test/fixtures/map/issue12432/0-0-0.mlt vendored Normal file

Binary file not shown.

View File

@@ -21,11 +21,12 @@
#include <mbgl/style/sources/vector_source.hpp>
#include <mbgl/style/style.hpp>
#include <mbgl/renderer/sources/render_raster_source.hpp>
#include <mbgl/renderer/sources/render_raster_dem_source.hpp>
#include <mbgl/renderer/sources/render_vector_source.hpp>
#include <mbgl/renderer/sources/render_geojson_source.hpp>
#include <mbgl/renderer/sources/render_raster_dem_source.hpp>
#include <mbgl/renderer/sources/render_raster_source.hpp>
#include <mbgl/renderer/sources/render_vector_source.hpp>
#include <mbgl/renderer/tile_parameters.hpp>
#include <mbgl/renderer/update_parameters.hpp>
#include <mbgl/util/run_loop.hpp>
#include <mbgl/util/string.hpp>
@@ -46,10 +47,10 @@
#include <mbgl/text/glyph_manager.hpp>
#include <mbgl/gfx/dynamic_texture_atlas.hpp>
#include <cstdint>
#include <optional>
#include <gmock/gmock.h>
#include <optional>
using namespace mbgl;
using SourceType = mbgl::style::SourceType;

View File

@@ -20,6 +20,7 @@
#include <mbgl/text/glyph_manager.hpp>
#include <memory>
#include "mbgl/tile/vector_mlt_tile_data.hpp"
using namespace mbgl;
@@ -65,14 +66,7 @@ TEST(VectorTileData, ParseResults) {
ASSERT_EQ(layer->getName(), "admin");
ASSERT_EQ(layer->featureCount(), 17154u);
try {
layer->getFeature(17154u);
ASSERT_TRUE(false) << "should throw: feature index is out of range.";
} catch (const std::out_of_range&) {
ASSERT_TRUE(true);
} catch (...) { // needed for iOS when MBGL_WITH_RTTI=OFF
ASSERT_TRUE(true);
}
ASSERT_THROW(layer->getFeature(17154u), std::out_of_range);
std::unique_ptr<GeometryTileFeature> feature = layer->getFeature(0u);
ASSERT_EQ(feature->getType(), mbgl::FeatureType::LineString);
@@ -85,3 +79,50 @@ TEST(VectorTileData, ParseResults) {
ASSERT_EQ(feature->getValue("invalid"), std::nullopt);
}
TEST(VectorTileData, MLTParseResults) {
struct SubCase {
bool useFastPFOR;
bool fastPFOREnabled;
};
for (const auto& testCase : std::vector{
SubCase{.useFastPFOR = false, .fastPFOREnabled = false},
SubCase{.useFastPFOR = false, .fastPFOREnabled = true},
SubCase{.useFastPFOR = true, .fastPFOREnabled = true},
SubCase{.useFastPFOR = true, .fastPFOREnabled = false},
}) {
const auto path = "test/fixtures/map/issue12432/0-0-0" +
std::string(testCase.useFastPFOR ? "-fastpfor.mlt" : ".mlt");
VectorMLTTileData data(std::make_shared<std::string>(util::read_file(path)), testCase.fastPFOREnabled);
std::vector<std::string> layerNames = data.layerNames();
if (!testCase.fastPFOREnabled && testCase.useFastPFOR) {
// If fast PFOR is not enabled, the fast PFOR tile should fail to parse
ASSERT_EQ(layerNames.size(), 0u);
continue;
}
// MLT layers are presented in the order they're encountered, not sorted alphabetically like MVT layers
ASSERT_EQ(layerNames.size(), 2u);
ASSERT_EQ(layerNames.at(0), "water");
ASSERT_EQ(layerNames.at(1), "admin");
ASSERT_FALSE(data.getLayer("invalid"));
std::unique_ptr<GeometryTileLayer> layer = data.getLayer("admin");
ASSERT_EQ(layer->getName(), "admin");
ASSERT_EQ(layer->featureCount(), 17154u);
std::unique_ptr<GeometryTileFeature> feature = layer->getFeature(0u);
ASSERT_EQ(feature->getType(), mbgl::FeatureType::LineString);
ASSERT_TRUE(feature->getID().is<uint64_t>());
ASSERT_EQ(feature->getID().get<uint64_t>(), 1u);
const std::unordered_map<std::string, Value>& properties = feature->getProperties();
ASSERT_EQ(properties.size(), 3u);
ASSERT_EQ(properties.at("disputed"), *feature->getValue("disputed"));
ASSERT_EQ(feature->getValue("invalid"), std::nullopt);
}
}

View File

@@ -1,6 +1,5 @@
set(MLT_WITH_JSON OFF CACHE BOOL "No JSON support" FORCE)
set(MLT_WITH_PROTOZERO OFF CACHE BOOL "No protozero" FORCE)
set(MLT_WITH_FASTPFOR OFF CACHE BOOL "disabled for lack of support for ARMv7 in SIMDE and requirement for SSE on x86" FORCE)
set(MLT_WITH_TESTS OFF CACHE BOOL "Google Test conflicts with Dawn")
set(MLT_WITH_TOOLS OFF CACHE BOOL "Not used")
@@ -8,8 +7,4 @@ set(MLT_WITH_TOOLS OFF CACHE BOOL "Not used")
set(CMAKE_OSX_DEPLOYMENT_TARGET 14.3)
set(MLT_OSX_DEPLOYMENT_TARGET ${CMAKE_OSX_DEPLOYMENT_TARGET} CACHE STRING "Inherited MacOS/iOS target" FORCE)
if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.25")
add_subdirectory(${PROJECT_SOURCE_DIR}/vendor/maplibre-tile-spec/cpp SYSTEM)
else()
add_subdirectory(${PROJECT_SOURCE_DIR}/vendor/maplibre-tile-spec/cpp)
endif()
add_subdirectory(${PROJECT_SOURCE_DIR}/vendor/maplibre-tile-spec/cpp SYSTEM)