diff --git a/bazel/core.bzl b/bazel/core.bzl index e431a9598114..6236fe33ee12 100644 --- a/bazel/core.bzl +++ b/bazel/core.bzl @@ -4,6 +4,7 @@ MLN_LAYER_PLUGIN_HEADERS = [ "src/mbgl/plugin/plugin_layer_impl.hpp", "src/mbgl/plugin/plugin_layer_render.hpp", "src/mbgl/plugin/plugin_layer_properties.hpp", + "src/mbgl/plugin/plugin_style_filter.hpp", ] MLN_LAYER_PLUGIN_SOURCE = [ @@ -12,6 +13,7 @@ MLN_LAYER_PLUGIN_SOURCE = [ "src/mbgl/plugin/plugin_layer_impl.cpp", "src/mbgl/plugin/plugin_layer_render.cpp", "src/mbgl/plugin/plugin_layer_properties.cpp", + "src/mbgl/plugin/plugin_style_filter.cpp", ] MLN_PUBLIC_GENERATED_STYLE_HEADERS = [ diff --git a/include/mbgl/style/style.hpp b/include/mbgl/style/style.hpp index 0d2d45719158..b143c5738c8d 100644 --- a/include/mbgl/style/style.hpp +++ b/include/mbgl/style/style.hpp @@ -20,6 +20,7 @@ namespace style { class Light; class Source; class Layer; +class PluginStyleFilter; class Style { public: @@ -71,6 +72,9 @@ class Style { void addLayer(std::unique_ptr, const std::optional& beforeLayerID = std::nullopt); std::unique_ptr removeLayer(const std::string& layerID); + // Add style parsing filter + void addStyleFilter(std::shared_ptr); + // Private implementation class Impl; const std::unique_ptr impl; diff --git a/platform/BUILD.bazel b/platform/BUILD.bazel index 1118b89f5e04..fa9b5e48a15c 100644 --- a/platform/BUILD.bazel +++ b/platform/BUILD.bazel @@ -271,6 +271,8 @@ objc_library( "//platform/darwin:app/CustomStyleLayerExample.m", "//platform/darwin:app/PluginLayerExample.h", "//platform/darwin:app/PluginLayerExample.mm", + "//platform/darwin:app/StyleFilterExample.h", + "//platform/darwin:app/StyleFilterExample.mm", "//platform/darwin:app/PluginLayerExampleMetalRendering.h", "//platform/darwin:app/PluginLayerExampleMetalRendering.mm", "//platform/ios:ios_app_srcs", diff --git a/platform/darwin/BUILD.bazel b/platform/darwin/BUILD.bazel index 7680a08812f0..86f38bcb1d94 100644 --- a/platform/darwin/BUILD.bazel +++ b/platform/darwin/BUILD.bazel @@ -308,6 +308,8 @@ exports_files( "app/PluginLayerTestStyle.json", "app/PluginLayerExample.h", "app/PluginLayerExample.mm", + "app/StyleFilterExample.h", + "app/StyleFilterExample.mm", "app/PluginLayerExampleMetalRendering.h", "app/PluginLayerExampleMetalRendering.mm", "test/amsterdam.geojson", diff --git a/platform/darwin/app/StyleFilterExample.h b/platform/darwin/app/StyleFilterExample.h new file mode 100644 index 000000000000..47cfe9f6c4e2 --- /dev/null +++ b/platform/darwin/app/StyleFilterExample.h @@ -0,0 +1,16 @@ +// +// StyleFilterExample.h +// MapLibre +// +// Created by Malcolm Toon on 7/25/25. +// + +#import "MLNStyleFilter.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface StyleFilterExample : MLNStyleFilter + +@end + +NS_ASSUME_NONNULL_END diff --git a/platform/darwin/app/StyleFilterExample.mm b/platform/darwin/app/StyleFilterExample.mm new file mode 100644 index 000000000000..ab6955136775 --- /dev/null +++ b/platform/darwin/app/StyleFilterExample.mm @@ -0,0 +1,60 @@ +#import "StyleFilterExample.h" + +@implementation StyleFilterExample + +// This will filter the data passed in +-(NSData *)filterData:(NSData *)data { + // Don't call super + + // This example will remove any layer that has "metal-rendering-layer" in the id + + // Parse the JSON: Make the containers mutable + NSError *error = nil; + NSMutableDictionary *styleDictionary = [NSJSONSerialization JSONObjectWithData:data + options:NSJSONReadingMutableContainers + error:&error]; + + NSData *tempResult = data; + if (styleDictionary) { + + // Get the layer array + NSMutableArray *layerArray = [styleDictionary objectForKey:@"layers"]; + + // Create an array to hold which objects to remove since we can't remove them in the loop + NSMutableArray *removedLayers = [NSMutableArray array]; + + // Loop the layers and look for any layers that have the search string in them + for (NSMutableDictionary *layer in layerArray) { + NSString *layerID = [layer objectForKey:@"id"]; + + // If we find the layers we're looking for, add them to the list of layers to remove + if ([layerID containsString:@"metal-rendering-layer"]) { + [removedLayers addObject:layer]; + } + } + + // Go through and remove any layers that were found + for (NSMutableDictionary *l in removedLayers) { + [layerArray removeObject:l]; + } + + // Re-create the JSON, this time the layers we filtered out won't be there + NSData *filteredStyleJSON = [NSJSONSerialization dataWithJSONObject:styleDictionary + options:0 + error:&error]; + + // If the JSON write is successful, then set the output to the new json style + if (filteredStyleJSON) { + tempResult = filteredStyleJSON; + } + + } + + // Return the data + return tempResult; + +} + + + +@end diff --git a/platform/darwin/bazel/files.bzl b/platform/darwin/bazel/files.bzl index 7ee28ac9bb3e..0c35f882c022 100644 --- a/platform/darwin/bazel/files.bzl +++ b/platform/darwin/bazel/files.bzl @@ -109,7 +109,8 @@ MLN_DARWIN_OBJC_HEADERS = [ "src/NSValue+MLNAdditions.h", "src/MLNPluginLayer.h", "src/MLNPluginStyleLayer.h", - "src/MLNNetworkResponse.h", + "src/MLNStyleFilter.h", + "src/MLNStyleFilter_Private.h", ] MLN_DARWIN_OBJCPP_HEADERS = [ @@ -226,6 +227,7 @@ MLN_DARWIN_PUBLIC_OBJCPP_SOURCE = [ "src/MLNPluginLayer.mm", "src/MLNPluginStyleLayer.mm", "src/MLNNetworkResponse.mm", + "src/MLNStyleFilter.mm", ] MLN_DARWIN_PUBLIC_OBJCPP_CUSTOM_DRAWABLE_SOURCE = [ "src/MLNCustomDrawableStyleLayer_Private.h", diff --git a/platform/darwin/src/MLNStyleFilter.h b/platform/darwin/src/MLNStyleFilter.h new file mode 100644 index 000000000000..066d329c8469 --- /dev/null +++ b/platform/darwin/src/MLNStyleFilter.h @@ -0,0 +1,12 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface MLNStyleFilter : NSObject + +// This will filter the data passed in +-(NSData *)filterData:(NSData *)data; + +@end + +NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/MLNStyleFilter.mm b/platform/darwin/src/MLNStyleFilter.mm new file mode 100644 index 000000000000..31c6b0f6e937 --- /dev/null +++ b/platform/darwin/src/MLNStyleFilter.mm @@ -0,0 +1,23 @@ +#import "MLNStyleFilter.h" +#include + +@interface MLNStyleFilter () { + std::shared_ptr _coreFilter; +} + +@end + +@implementation MLNStyleFilter + +-(NSData *)filterData:(NSData *)data { + // Base class does nothing but return the same data passed in + return data; +} + +// Private +-(void)setFilter:(std::shared_ptr)filter { + _coreFilter = filter; +} + + +@end diff --git a/platform/darwin/src/MLNStyleFilter_Private.h b/platform/darwin/src/MLNStyleFilter_Private.h new file mode 100644 index 000000000000..f27864868d55 --- /dev/null +++ b/platform/darwin/src/MLNStyleFilter_Private.h @@ -0,0 +1,14 @@ + +#ifndef MLNStyleFilter_Private_h +#define MLNStyleFilter_Private_h + +#import "MLNStyleFilter.h" +#include + +@interface MLNStyleFilter(Private) + +-(void)setFilter:(std::shared_ptr)filter; + +@end + +#endif /* MLNStyleFilter_Private_h */ diff --git a/platform/ios/app/MBXViewController.mm b/platform/ios/app/MBXViewController.mm index b0e1031b77b3..63da894ad33f 100644 --- a/platform/ios/app/MBXViewController.mm +++ b/platform/ios/app/MBXViewController.mm @@ -27,6 +27,7 @@ #import "PluginLayerExample.h" #import "PluginLayerExampleMetalRendering.h" #import "MLNPluginStyleLayer.h" +#import "StyleFilterExample.h" static const CLLocationCoordinate2D WorldTourDestinations[] = { { .latitude = 38.8999418, .longitude = -77.033996 }, @@ -281,6 +282,7 @@ -(void)addPluginLayers { [self.mapView addPluginLayerType:[PluginLayerExample class]]; [self.mapView addPluginLayerType:[PluginLayerExampleMetalRendering class]]; + [self.mapView addStyleFilter:[[StyleFilterExample alloc] init]]; } diff --git a/platform/ios/src/MLNMapView.h b/platform/ios/src/MLNMapView.h index bf7acae4a2aa..ffa1de3f9516 100644 --- a/platform/ios/src/MLNMapView.h +++ b/platform/ios/src/MLNMapView.h @@ -20,6 +20,7 @@ NS_ASSUME_NONNULL_BEGIN @class MLNScaleBar; @class MLNShape; @class MLNPluginLayer; +@class MLNStyleFilter; @protocol MLNMapViewDelegate; @protocol MLNAnnotation; @@ -2322,6 +2323,11 @@ of north, the map will automatically snap to exact north. */ - (void)addPluginLayerType:(Class)pluginLayerClass; +/** + Adds a style filter to the map view + */ +-(void)addStyleFilter:(MLNStyleFilter *)styleFilter; + @end NS_ASSUME_NONNULL_END diff --git a/platform/ios/src/MLNMapView.mm b/platform/ios/src/MLNMapView.mm index f7631ce6e78a..615ce1b9df12 100644 --- a/platform/ios/src/MLNMapView.mm +++ b/platform/ios/src/MLNMapView.mm @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -82,6 +83,8 @@ #import "MLNPluginLayer.h" #import "MLNStyleLayerManager.h" #include "MLNPluginStyleLayer_Private.h" +#include "MLNStyleFilter.h" +#include "MLNStyleFilter_Private.h" #include #include @@ -452,6 +455,9 @@ @interface MLNMapView () (); + coreStyleFilter->_filterStyleFunction = [styleFilter](const std::string &filterData) -> const std::string { + + + std::string tempResult; + + @autoreleasepool { + NSData *sourceData = [NSData dataWithBytesNoCopy:(void *)filterData.data() + length:filterData.size() + freeWhenDone:NO]; + NSData *filteredData = [styleFilter filterData:sourceData]; + tempResult = std::string((const char*)[filteredData bytes], [filteredData length]); + + } + return tempResult; + + }; + + // Set the ivar + [styleFilter setFilter:coreStyleFilter]; + + _mbglMap->getStyle().addStyleFilter(coreStyleFilter); + +} + + - (NSArray*)getActionJournalLogFiles { const auto& actionJournal = _mbglMap->getActionJournal(); diff --git a/src/mbgl/plugin/plugin_style_filter.cpp b/src/mbgl/plugin/plugin_style_filter.cpp new file mode 100644 index 000000000000..839288d0ec8e --- /dev/null +++ b/src/mbgl/plugin/plugin_style_filter.cpp @@ -0,0 +1,14 @@ +#include + +using namespace mbgl::style; + +// This method +const std::string PluginStyleFilter::filterResponse(const std::string& res) { + + if (_filterStyleFunction) { + auto tempResult = _filterStyleFunction(res); + return tempResult; + } + return res; + +} diff --git a/src/mbgl/plugin/plugin_style_filter.hpp b/src/mbgl/plugin/plugin_style_filter.hpp new file mode 100644 index 000000000000..f4dfe57fe428 --- /dev/null +++ b/src/mbgl/plugin/plugin_style_filter.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include + + +namespace mbgl { + +namespace style { + +class PluginStyleFilter { +public: + + using OnFilterStyle = std::function; + + OnFilterStyle _filterStyleFunction; + + // This method + const std::string filterResponse(const std::string&); +}; + + +} +} diff --git a/src/mbgl/style/style.cpp b/src/mbgl/style/style.cpp index 4f8723ead10a..bba0362b5704 100644 --- a/src/mbgl/style/style.cpp +++ b/src/mbgl/style/style.cpp @@ -177,5 +177,13 @@ std::unique_ptr Style::removeLayer(const std::string& id) { return impl->removeLayer(id); } +// Add style parsing filter +void Style::addStyleFilter(std::shared_ptr filter) { + impl->mutated = true; + return impl->addStyleFilter(filter); + +} + + } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/style_impl.cpp b/src/mbgl/style/style_impl.cpp index 2b4bf88b850a..bb8acc91bcd7 100644 --- a/src/mbgl/style/style_impl.cpp +++ b/src/mbgl/style/style_impl.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include namespace mbgl { @@ -74,11 +75,27 @@ void Style::Impl::loadURL(const std::string& url_) { } else if (res.notModified || res.noContent) { return; } else { - parse(*res.data); + filterThenParse(*res.data); } }); } +void Style::Impl::filterThenParse(const std::string& styleData) { + + if (_styleFilters.size() == 0) { + parse(styleData); + return; + } + + // Otherwise, go through the chain of filters + std::string filteredStyle = styleData; + for (auto filter : _styleFilters) { + filteredStyle = filter->filterResponse(filteredStyle); + } + parse(filteredStyle); + +} + void Style::Impl::parse(const std::string& json_) { Parser parser; @@ -233,6 +250,12 @@ std::unique_ptr Style::Impl::removeLayer(const std::string& id) { return layer; } +// Add style parsing filter +void Style::Impl::addStyleFilter(std::shared_ptr filter) { + _styleFilters.push_back(filter); +} + + void Style::Impl::setLight(std::unique_ptr light_) { light = std::move(light_); light->setObserver(this); diff --git a/src/mbgl/style/style_impl.hpp b/src/mbgl/style/style_impl.hpp index 8ef03e98ffe7..20be310b3ecd 100644 --- a/src/mbgl/style/style_impl.hpp +++ b/src/mbgl/style/style_impl.hpp @@ -30,6 +30,7 @@ namespace mbgl { class FileSource; class AsyncRequest; class SpriteLoader; +class Response; namespace style { @@ -68,6 +69,9 @@ class Style::Impl : public SpriteLoaderObserver, Layer* addLayer(std::unique_ptr, const std::optional& beforeLayerID = std::nullopt); std::unique_ptr removeLayer(const std::string& layerID); + // Add style parsing filter + void addStyleFilter(std::shared_ptr); + std::string getName() const; CameraOptions getDefaultCamera() const; @@ -98,6 +102,9 @@ class Style::Impl : public SpriteLoaderObserver, private: void parse(const std::string&); + void filterThenParse(const std::string&); + std::vector> _styleFilters; + std::shared_ptr fileSource; std::string url;