From 7f81af1d0bacd7a5902ca1a86bb86b2816b9b326 Mon Sep 17 00:00:00 2001 From: Wei Chen Date: Tue, 25 Nov 2025 18:20:38 +0800 Subject: [PATCH] Support extra annotations for snapshotter for iOS (#3963) Co-authored-by: Wei Chen Co-authored-by: Stefan Karschti <4198736+stefankarschti@users.noreply.github.com> --- platform/darwin/src/MLNMapSnapshotter.h | 118 ++++++++++++++++ platform/darwin/src/MLNMapSnapshotter.mm | 129 +++++++++++++++++- .../include/mbgl/map/map_snapshotter.hpp | 5 + .../default/src/mbgl/map/map_snapshotter.cpp | 12 ++ .../map_snapshotter_annotation/expected.png | Bin 0 -> 9738 bytes test/map/map_snapshotter.test.cpp | 37 +++++ 6 files changed, 300 insertions(+), 1 deletion(-) create mode 100644 test/fixtures/map/map_snapshotter_annotation/expected.png diff --git a/platform/darwin/src/MLNMapSnapshotter.h b/platform/darwin/src/MLNMapSnapshotter.h index 3092bdd83..ba405f350 100644 --- a/platform/darwin/src/MLNMapSnapshotter.h +++ b/platform/darwin/src/MLNMapSnapshotter.h @@ -6,7 +6,13 @@ NS_ASSUME_NONNULL_BEGIN +@class MLNAnnotationImage; +@class MLNShape; +@class MLNPolyline; +@class MLNPolygon; + @protocol MLNMapSnapshotterDelegate; +@protocol MLNAnnotation; /** An overlay that is placed within a ``MLNMapSnapshot``. @@ -249,6 +255,35 @@ MLN_EXPORT */ - (instancetype)initWithOptions:(MLNMapSnapshotOptions *)options NS_DESIGNATED_INITIALIZER; +/** + Adds an annotation to the snapshotter. + + > Note: ``MLNMultiPolyline``, ``MLNMultiPolyline``, ``MLNMultiPolyline``, and + ``MLNPointCollection`` objects cannot be added to the snapshotter at this time. + Any multipoint, multipolyline, multipolygon, shape or point collection + object that is specified is silently ignored. + + @param annotation The annotation object to add to the receiver. This object + must conform to the ``MLNAnnotation`` protocol. The snapshotter retains the + annotation object. + */ +- (void)addAnnotation:(id)annotation; + +/** + Adds an array of annotations to the snapshotter. + + > Note: ``MLNMultiPolyline``, ``MLNMultiPolyline``, and ``MLNMultiPolyline`` objects + cannot be added to the snapshotter at this time. Nor can ``MLNMultiPoint`` + objects that are not instances of ``MLNPolyline`` or ``MLNPolyline``. Any + multipoint, multipolyline, multipolygon, or shape collection objects that + are specified are silently ignored. + + @param annotations An array of annotation objects. Each object in the array + must conform to the ``MLNAnnotation`` protocol. The snapshotter retains each + individual annotation object. + */ +- (void)addAnnotations:(NSArray> *)annotations; + /** Starts the snapshot creation and executes the specified block with the result. @@ -367,6 +402,89 @@ MLN_EXPORT - (void)mapSnapshotter:(MLNMapSnapshotter *)snapshotter didFailLoadingImageNamed:(NSString *)name; +/** + Returns an annotation image object to mark the given point annotation object on + the map. + + Implement this method to mark a point annotation with a static image. + + Static annotation images use less memory and draw more quickly than annotation + views. On the other hand, annotation views are compatible with UIKit, Core + Animation, and other Cocoa Touch frameworks. + + @param snapshotter The snapshotter that has just loaded a style. + @param annotation The object representing the annotation that is about to be + displayed. + @return The annotation image object to display for the given annotation or + `nil` if you want to display the default marker image or an annotation view. +*/ +- (nullable MLNAnnotationImage *)mapSnapshotter:(MLNMapSnapshotter *)snapshotter + imageForAnnotation:(id)annotation; + +/** + Returns the alpha value to use when rendering a shape annotation. + + A value of `0.0` results in a completely transparent shape. A value of `1.0`, + the default, results in a completely opaque shape. + + This method sets the opacity of an entire shape, inclusive of its stroke and + fill. To independently set the values for stroke or fill, specify an alpha + component in the color returned by `-mapSnapshotter:strokeColorForShapeAnnotation:` or + `-mapSnapshotter:fillColorForPolygonAnnotation:`. + + @param snapshotter The snapshotter that has just loaded a style. + @param annotation The annotation being rendered. + @return An alpha value between `0` and `1.0`. + */ +- (CGFloat)mapSnapshotter:(MLNMapSnapshotter *)snapshotter + alphaForShapeAnnotation:(MLNShape *)annotation; + +/** + Returns the color to use when rendering the outline of a shape annotation. + + The default stroke color is MLNColor.whiteColor. + + Opacity may be set by specifying an alpha component. The default alpha value is + `1.0` and results in a completely opaque stroke. + + @param snapshotter The snapshotter that has just loaded a style. + @param annotation The annotation being rendered. + @return A color to use for the shape outline. + + */ +- (MLNColor *)mapSnapshotter:(MLNMapSnapshotter *)snapshotter + strokeColorForShapeAnnotation:(MLNShape *)annotation; + +/** + Returns the color to use when rendering the fill of a polygon annotation. + + The default fill color is the MLNColor.whiteColor. + + Opacity may be set by specifying an alpha component. The default alpha value is + `1.0` and results in a completely opaque shape. + + @param snapshotter The snapshotter that has just loaded a style. + @param annotation The annotation being rendered. + @return The polygon’s interior fill color. + + */ +- (MLNColor *)mapSnapshotter:(MLNMapSnapshotter *)snapshotter + fillColorForPolygonAnnotation:(MLNPolygon *)annotation; + +/** + Returns the line width in points to use when rendering the outline of a + polyline annotation. + + By default, the polyline is outlined with a line `3.0` points wide. + + @param snapshotter The snapshotter that has just loaded a style. + @param annotation The annotation being rendered. + @return A line width for the polyline, measured in points. + + */ +- (CGFloat)mapSnapshotter:(MLNMapSnapshotter *)snapshotter + lineWidthForPolylineAnnotation:(MLNPolyline *)annotation; + @end NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/MLNMapSnapshotter.mm b/platform/darwin/src/MLNMapSnapshotter.mm index 6f632f369..b6fc746e2 100644 --- a/platform/darwin/src/MLNMapSnapshotter.mm +++ b/platform/darwin/src/MLNMapSnapshotter.mm @@ -18,11 +18,20 @@ #import "MLNRendererConfiguration.h" #import "MLNMapSnapshotter_Private.h" #import "MLNSettings_Private.h" +#import "MLNShape.h" +#import "MLNPolyline.h" +#import "MLNPolygon.h" +#import "MLNMultiPoint_Private.h" +#import "MLNShapeCollection.h" +#import "MLNPointCollection.h" +#import "MLNAnnotationImage_Private.h" #if TARGET_OS_IPHONE #import "UIImage+MLNAdditions.h" +#import "UIColor+MLNAdditions.h" #else #import "NSImage+MLNAdditions.h" +#import "NSColor+MLNAdditions.h" #import #import #import @@ -33,6 +42,7 @@ const CGPoint MLNLogoImagePosition = CGPointMake(8, 8); const CGFloat MLNSnapshotterMinimumPixelSize = 64; +NSString * const MLNSnapshotterAnnotationSpritePrefix = @"org.maplibre.sprites."; MLNImage *MLNAttributedSnapshot(mbgl::MapSnapshotter::Attributions attributions, MLNImage *mglImage, mbgl::MapSnapshotter::PointForFn pointForFn, mbgl::MapSnapshotter::LatLngForFn latLngForFn, MLNMapSnapshotOptions *options, MLNMapSnapshotOverlayHandler overlayHandler); MLNMapSnapshot *MLNSnapshotWithDecoratedImage(MLNImage *mglImage, MLNMapSnapshotOptions *options, mbgl::MapSnapshotter::Attributions attributions, mbgl::MapSnapshotter::PointForFn pointForFn, mbgl::MapSnapshotter::LatLngForFn latLngForFn, MLNMapSnapshotOverlayHandler overlayHandler, NSError * _Nullable *outError); @@ -224,7 +234,7 @@ private: @end -@interface MLNMapSnapshotter() +@interface MLNMapSnapshotter() @property (nonatomic) BOOL loading; @property (nonatomic) BOOL terminated; @end @@ -232,6 +242,8 @@ private: @implementation MLNMapSnapshotter { std::unique_ptr _mbglMapSnapshotter; std::unique_ptr _delegateHost; + NSMutableArray> *_annotationsToInstall; + NSMutableSet *_installedImages; } - (void)dealloc { @@ -251,6 +263,7 @@ private: self = [super init]; if (self) { self.options = options; + _installedImages = [NSMutableSet set]; #if TARGET_OS_IPHONE [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillTerminate:) name:UIApplicationWillTerminateNotification object:nil]; #else @@ -266,6 +279,79 @@ private: self.terminated = YES; } +- (void)addAnnotation:(id)annotation +{ + MLNLogDebug(@"Adding annotation: %@", annotation); + if ( ! annotation) return; + return [self addAnnotations:@[annotation]]; +} + +- (void)addAnnotations:(NSArray> *)annotations { + MLNLogDebug(@"Adding: %lu annotations", annotations.count); + + if (!_annotationsToInstall) { + _annotationsToInstall = [NSMutableArray array]; + } + + [_annotationsToInstall addObjectsFromArray:annotations]; +} + +- (void)installAnnotations:(NSArray> *)annotations +{ + MLNLogDebug(@"install: %lu annotations", annotations.count); + if ( ! annotations) return; + + for (id annotation in annotations) + { + MLNAssert([annotation conformsToProtocol:@protocol(MLNAnnotation)], @"annotation should conform to MLNAnnotation"); + + if ([annotation isKindOfClass:[MLNMultiPoint class]]) + { + MLNMultiPoint *multiPoint = (MLNMultiPoint *)annotation; + if (!multiPoint.pointCount) { + continue; + } + _mbglMapSnapshotter->addAnnotation([multiPoint annotationObjectWithDelegate:self]); + } else if (! [annotation isKindOfClass:[MLNMultiPolyline class]] + && ![annotation isKindOfClass:[MLNMultiPolygon class]] + && ![annotation isKindOfClass:[MLNShapeCollection class]] + && ![annotation isKindOfClass:[MLNPointCollection class]]) { + NSString *symbolName; + NSValue *annotationValue = [NSValue valueWithNonretainedObject:annotation]; + MLNAnnotationImage *annotationImage; + + if ([self.delegate respondsToSelector:@selector(mapSnapshotter:imageForAnnotation:)]) { + annotationImage = [self.delegate mapSnapshotter:self imageForAnnotation:annotation]; + } + if (!annotationImage) { + // doesn't support no icon point for snapshotter + continue; + } + + symbolName = annotationImage.styleIconIdentifier; + if ( ! symbolName) + { + symbolName = [MLNSnapshotterAnnotationSpritePrefix stringByAppendingString:annotationImage.reuseIdentifier]; + annotationImage.styleIconIdentifier = symbolName; + } + + [self installAnnotationImage:annotationImage]; + + _mbglMapSnapshotter->addAnnotation(mbgl::SymbolAnnotation { + MLNPointFromLocationCoordinate2D(annotation.coordinate), + symbolName.UTF8String + }); + } + } +} + +- (void)installAnnotationImage:(MLNAnnotationImage *)annotationImage { + NSString *iconIdentifier = annotationImage.styleIconIdentifier; + if (![_installedImages containsObject:iconIdentifier]) { + _mbglMapSnapshotter->addAnnotationImage([annotationImage.image mgl_styleImageWithIdentifier:iconIdentifier]); + } +} + - (void)startWithCompletionHandler:(MLNMapSnapshotCompletionHandler)completion { MLNLogDebug(@"Starting withCompletionHandler: %@", completion); @@ -749,6 +835,11 @@ NSArray *MLNAttributionInfosFromAttributions(mbgl::MapSnap if (!MLNCoordinateBoundsIsEmpty(options.coordinateBounds)) { _mbglMapSnapshotter->setRegion(MLNLatLngBoundsFromCoordinateBounds(options.coordinateBounds)); } + + // Annotations + if (_annotationsToInstall) { + [self installAnnotations:_annotationsToInstall]; + } } - (MLNStyle *)style { @@ -758,4 +849,40 @@ NSArray *MLNAttributionInfosFromAttributions(mbgl::MapSnap return [[MLNStyle alloc] initWithRawStyle:&_mbglMapSnapshotter->getStyle() stylable:self]; } +- (double)alphaForShapeAnnotation:(MLNShape *)annotation +{ + if ([self.delegate respondsToSelector:@selector(mapSnapshotter:alphaForShapeAnnotation:)]) { + return [self.delegate mapSnapshotter:self alphaForShapeAnnotation:annotation]; + } + return 1.0; +} + +- (mbgl::Color)strokeColorForShapeAnnotation:(MLNShape *)annotation +{ + if ([self.delegate respondsToSelector:@selector(mapSnapshotter:strokeColorForShapeAnnotation:)]) { + MLNColor *color = [self.delegate mapSnapshotter:self strokeColorForShapeAnnotation:annotation]; + return color.mgl_color; + } + + return mbgl::Color::white(); +} + +- (mbgl::Color)fillColorForPolygonAnnotation:(MLNPolygon *)annotation +{ + if ([self.delegate respondsToSelector:@selector(mapSnapshotter:fillColorForPolygonAnnotation:)]){ + MLNColor *color = [self.delegate mapSnapshotter:self fillColorForPolygonAnnotation:annotation]; + return color.mgl_color; + } + return mbgl::Color::white(); +} + +- (CGFloat)lineWidthForPolylineAnnotation:(MLNPolyline *)annotation +{ + if ([self.delegate respondsToSelector:@selector(mapSnapshotter:lineWidthForPolylineAnnotation:)]) { + return [self.delegate mapSnapshotter:self lineWidthForPolylineAnnotation:annotation]; + } + + return 3.0; +} + @end diff --git a/platform/default/include/mbgl/map/map_snapshotter.hpp b/platform/default/include/mbgl/map/map_snapshotter.hpp index 5bcbf98ed..bb75a4170 100644 --- a/platform/default/include/mbgl/map/map_snapshotter.hpp +++ b/platform/default/include/mbgl/map/map_snapshotter.hpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include @@ -63,6 +65,9 @@ public: void setPadding(const mbgl::EdgeInsets&); mbgl::EdgeInsets getPadding() const; + void addAnnotationImage(std::unique_ptr); + void addAnnotation(const Annotation&); + style::Style& getStyle(); const style::Style& getStyle() const; diff --git a/platform/default/src/mbgl/map/map_snapshotter.cpp b/platform/default/src/mbgl/map/map_snapshotter.cpp index eb81c132f..a87c60080 100644 --- a/platform/default/src/mbgl/map/map_snapshotter.cpp +++ b/platform/default/src/mbgl/map/map_snapshotter.cpp @@ -210,6 +210,10 @@ public: mbgl::EdgeInsets getPadding() const { return regionInsets; } + void addAnnotationImage(std::unique_ptr image) { map.addAnnotationImage(std::move(image)); } + + void addAnnotation(const Annotation& annotation) { map.addAnnotation(annotation); } + void snapshot(MapSnapshotter::Callback callback) { if (!callback) { Log::Error(Event::General, "MapSnapshotter::Callback is not set"); @@ -366,6 +370,14 @@ mbgl::EdgeInsets MapSnapshotter::getPadding() const { return impl->getPadding(); } +void MapSnapshotter::addAnnotationImage(std::unique_ptr image) { + impl->addAnnotationImage(std::move(image)); +} + +void MapSnapshotter::addAnnotation(const Annotation& annotation) { + impl->addAnnotation(annotation); +} + style::Style& MapSnapshotter::getStyle() { return impl->getMap().getStyle(); } diff --git a/test/fixtures/map/map_snapshotter_annotation/expected.png b/test/fixtures/map/map_snapshotter_annotation/expected.png new file mode 100644 index 0000000000000000000000000000000000000000..1d6e919586a950ae70d1a821760d5d4a11399758 GIT binary patch literal 9738 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 literal 0 HcmV?d00001 diff --git a/test/map/map_snapshotter.test.cpp b/test/map/map_snapshotter.test.cpp index 0ea31c5bb..907bd4a57 100644 --- a/test/map/map_snapshotter.test.cpp +++ b/test/map/map_snapshotter.test.cpp @@ -7,9 +7,18 @@ #include #include #include +#include using namespace mbgl; +static PremultipliedImage namedImage(const std::string& name) { + return decodeImage(util::read_file("test/fixtures/sprites/" + name + ".png")); +} + +static std::unique_ptr namedMarker(const std::string& name) { + return std::make_unique(name, namedImage(name), 1.0f); +} + class SnapshotterObserver final : public MapSnapshotterObserver { public: ~SnapshotterObserver() = default; @@ -137,3 +146,31 @@ TEST(MapSnapshotter, TEST_REQUIRES_SERVER(runtimeStyling)) { runLoop.run(); } + +TEST(MapSnapshotter, TEST_REQUIRES_SERVER(annotation)) { + util::RunLoop runLoop; + MapSnapshotter snapshotter(Size{256, 128}, 1.0f, ResourceOptions(), ClientOptions()); + snapshotter.setStyleURL("http://127.0.0.1:3000/online/style.json"); + snapshotter.addAnnotationImage(namedMarker("default_marker")); + snapshotter.addAnnotation(SymbolAnnotation{Point{0, 0}, "default_marker"}); + FillAnnotation fillAnnotation{Polygon{{{{0, 0}, {0, 45}, {45, 45}, {45, 0}}}}}; + fillAnnotation.color = Color::red(); + snapshotter.addAnnotation(fillAnnotation); + LineAnnotation lineAnnotation{LineString{{{0, 0}, {45, 45}, {30, 0}}}}; + lineAnnotation.color = Color::blue(); + lineAnnotation.width = {5}; + snapshotter.addAnnotation(lineAnnotation); + snapshotter.snapshot([&runLoop](std::exception_ptr ptr, + mbgl::PremultipliedImage image, + mbgl::MapSnapshotter::Attributions, + mbgl::MapSnapshotter::PointForFn, + mbgl::MapSnapshotter::LatLngForFn) { + EXPECT_EQ(nullptr, ptr); + EXPECT_EQ(256, image.size.width); + EXPECT_EQ(128, image.size.height); + test::checkImage("test/fixtures/map/map_snapshotter_annotation", image, 0.005, 0.1); + runLoop.stop(); + }); + + runLoop.run(); +}