Support extra annotations for snapshotter for iOS (#3963)
Co-authored-by: Wei Chen <wei.chen@grabtaxi.com> Co-authored-by: Stefan Karschti <4198736+stefankarschti@users.noreply.github.com>
This commit is contained in:
@@ -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<MLNAnnotation>)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<id<MLNAnnotation>> *)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<MLNAnnotation>)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
|
||||
|
||||
@@ -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 <CoreGraphics/CoreGraphics.h>
|
||||
#import <CoreImage/CIContext.h>
|
||||
#import <CoreImage/CIFilter.h>
|
||||
@@ -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() <MLNMultiPointDelegate>
|
||||
@property (nonatomic) BOOL loading;
|
||||
@property (nonatomic) BOOL terminated;
|
||||
@end
|
||||
@@ -232,6 +242,8 @@ private:
|
||||
@implementation MLNMapSnapshotter {
|
||||
std::unique_ptr<mbgl::MapSnapshotter> _mbglMapSnapshotter;
|
||||
std::unique_ptr<MLNMapSnapshotterDelegateHost> _delegateHost;
|
||||
NSMutableArray<id<MLNAnnotation>> *_annotationsToInstall;
|
||||
NSMutableSet<NSString *> *_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<MLNAnnotation>)annotation
|
||||
{
|
||||
MLNLogDebug(@"Adding annotation: %@", annotation);
|
||||
if ( ! annotation) return;
|
||||
return [self addAnnotations:@[annotation]];
|
||||
}
|
||||
|
||||
- (void)addAnnotations:(NSArray<id<MLNAnnotation>> *)annotations {
|
||||
MLNLogDebug(@"Adding: %lu annotations", annotations.count);
|
||||
|
||||
if (!_annotationsToInstall) {
|
||||
_annotationsToInstall = [NSMutableArray array];
|
||||
}
|
||||
|
||||
[_annotationsToInstall addObjectsFromArray:annotations];
|
||||
}
|
||||
|
||||
- (void)installAnnotations:(NSArray<id<MLNAnnotation>> *)annotations
|
||||
{
|
||||
MLNLogDebug(@"install: %lu annotations", annotations.count);
|
||||
if ( ! annotations) return;
|
||||
|
||||
for (id <MLNAnnotation> 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<MLNAttributionInfo *> *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<MLNAttributionInfo *> *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
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <mbgl/util/client_options.hpp>
|
||||
#include <mbgl/util/image.hpp>
|
||||
#include <mbgl/util/geo.hpp>
|
||||
#include <mbgl/annotation/annotation.hpp>
|
||||
#include <mbgl/style/image.hpp>
|
||||
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
@@ -63,6 +65,9 @@ public:
|
||||
void setPadding(const mbgl::EdgeInsets&);
|
||||
mbgl::EdgeInsets getPadding() const;
|
||||
|
||||
void addAnnotationImage(std::unique_ptr<style::Image>);
|
||||
void addAnnotation(const Annotation&);
|
||||
|
||||
style::Style& getStyle();
|
||||
const style::Style& getStyle() const;
|
||||
|
||||
|
||||
@@ -210,6 +210,10 @@ public:
|
||||
|
||||
mbgl::EdgeInsets getPadding() const { return regionInsets; }
|
||||
|
||||
void addAnnotationImage(std::unique_ptr<style::Image> 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<style::Image> image) {
|
||||
impl->addAnnotationImage(std::move(image));
|
||||
}
|
||||
|
||||
void MapSnapshotter::addAnnotation(const Annotation& annotation) {
|
||||
impl->addAnnotation(annotation);
|
||||
}
|
||||
|
||||
style::Style& MapSnapshotter::getStyle() {
|
||||
return impl->getMap().getStyle();
|
||||
}
|
||||
|
||||
BIN
test/fixtures/map/map_snapshotter_annotation/expected.png
vendored
Normal file
BIN
test/fixtures/map/map_snapshotter_annotation/expected.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.5 KiB |
@@ -7,9 +7,18 @@
|
||||
#include <mbgl/util/client_options.hpp>
|
||||
#include <mbgl/util/run_loop.hpp>
|
||||
#include <mbgl/util/timer.hpp>
|
||||
#include <mbgl/util/io.hpp>
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
static PremultipliedImage namedImage(const std::string& name) {
|
||||
return decodeImage(util::read_file("test/fixtures/sprites/" + name + ".png"));
|
||||
}
|
||||
|
||||
static std::unique_ptr<style::Image> namedMarker(const std::string& name) {
|
||||
return std::make_unique<style::Image>(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<double>{0, 0}, "default_marker"});
|
||||
FillAnnotation fillAnnotation{Polygon<double>{{{{0, 0}, {0, 45}, {45, 45}, {45, 0}}}}};
|
||||
fillAnnotation.color = Color::red();
|
||||
snapshotter.addAnnotation(fillAnnotation);
|
||||
LineAnnotation lineAnnotation{LineString<double>{{{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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user