diff options
author | Asheem Mamoowala <asheem.mamoowala@mapbox.com> | 2017-05-25 14:36:05 -0700 |
---|---|---|
committer | Asheem Mamoowala <asheem.mamoowala@mapbox.com> | 2017-06-19 09:50:08 -0700 |
commit | ab5b310a9eb7c88935cc457da1af81349fbe8d41 (patch) | |
tree | e1485c8aa2f00e2b50daa0aa7f997ddda59c2562 /platform | |
parent | 7b05606464ac4d57b59b64015629e9578cbebac2 (diff) | |
download | qtlocation-mapboxgl-ab5b310a9eb7c88935cc457da1af81349fbe8d41.tar.gz |
[iOS][macOS] Add ImageSource bindings
Diffstat (limited to 'platform')
38 files changed, 777 insertions, 31 deletions
diff --git a/platform/android/src/style/sources/image_source.cpp b/platform/android/src/style/sources/image_source.cpp new file mode 100644 index 0000000000..9909675f44 --- /dev/null +++ b/platform/android/src/style/sources/image_source.cpp @@ -0,0 +1,81 @@ +#include "image_source.hpp" + +// Java -> C++ conversion +#include "../android_conversion.hpp" + +// C++ -> Java conversion +#include "../../conversion/conversion.hpp" +#include <mbgl/style/conversion.hpp> + +#include <string> + +namespace mbgl { +namespace android { + + ImageSource::ImageSource(jni::JNIEnv& env, jni::String sourceId) + : Source(env, std::make_unique<mbgl::style::ImageSource>( + jni::Make<std::string>(env, sourceId) + ) + ) { + } + + ImageSource::ImageSource(mbgl::Map& map, mbgl::style::ImageSource& coreSource) + : Source(map, coreSource) { + } + + ImageSource::~ImageSource() = default; + + void GeoJSONSource::setGeoJSONString(jni::JNIEnv& env, jni::String json) { + using namespace mbgl::style::conversion; + + // Convert the jni object + Error error; + optional<GeoJSON> converted = convert<GeoJSON>(Value(env, json), error); + if(!converted) { + mbgl::Log::Error(mbgl::Event::JNI, "Error setting geo json: " + error.message); + return; + } + + // Update the core source + source.as<mbgl::style::GeoJSONSource>()->GeoJSONSource::setGeoJSON(*converted); + } + + void ImageSource::setURL(jni::JNIEnv& env, jni::String url) { + // Update the core source + source.as<mbgl::style::GeoJSONSource>()->GeoJSONSource::setURL(jni::Make<std::string>(env, url)); + } + + jni::String ImageSource::getURL(jni::JNIEnv& env) { + std::string url = source.as<mbgl::style::ImageSource>()->ImageSource::getURL(); + return !url.empty() ? jni::Make<jni::String>(env, url) : jni::String(); + } + + + jni::Class<ImageSource> ImageSource::javaClass; + + jni::jobject* ImageSource::createJavaPeer(jni::JNIEnv& env) { + static auto constructor = ImageSource::javaClass.template GetConstructor<jni::jlong>(env); + return ImageSource::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this)); + } + + void ImageSource::registerNative(jni::JNIEnv& env) { + // Lookup the class + ImageSource::javaClass = *jni::Class<ImageSource>::Find(env).NewGlobalRef(env).release(); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name) + + // Register the peer + jni::RegisterNativePeer<ImageSource>( + env, ImageSource::javaClass, "nativePtr", + std::make_unique<ImageSource, JNIEnv&, jni::String, jni::Object<>>, + "initialize", + "finalize", + METHOD(&ImageSource::setGeoJSONString, "nativeSetGeoJsonString"), + METHOD(&ImageSource::setURL, "nativeSetUrl"), + METHOD(&ImageSource::getURL, "nativeGetUrl"), + METHOD(&ImageSource::getURL, "nativeGetUrl"), + ); + } + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/style/sources/image_source.hpp b/platform/android/src/style/sources/image_source.hpp new file mode 100644 index 0000000000..d5802ed5a1 --- /dev/null +++ b/platform/android/src/style/sources/image_source.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include "source.hpp" +#include <mbgl/style/sources/image_source.hpp> +#include <jni/jni.hpp> + +namespace mbgl { +namespace android { + +class ImageSource : public Source { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/sources/ImageSource"; }; + + static jni::Class<ImageSource> javaClass; + + static void registerNative(jni::JNIEnv&); + + ImageSource(jni::JNIEnv&, jni::String, jni::Object<>); + + ImageSource(mbgl::Map&, mbgl::style::ImageSource&); + + ~ImageSource(); + + void setURL(jni::JNIEnv&, jni::String); + jni::String getURL(jni::JNIEnv&); + + jni::jobject* createJavaPeer(jni::JNIEnv&); + +}; // class ImageSource + +} // namespace android +} // namespace mbgl diff --git a/platform/darwin/src/MGLGeometry.h b/platform/darwin/src/MGLGeometry.h index 9fcb9dd37c..003a7638e7 100644 --- a/platform/darwin/src/MGLGeometry.h +++ b/platform/darwin/src/MGLGeometry.h @@ -45,6 +45,23 @@ typedef struct MGLCoordinateBounds { CLLocationCoordinate2D ne; } MGLCoordinateBounds; +/** + A quadrilateral area as measured on a two-dimensional map projection. + `MGLCoordinateQuad` differs from `MGLCoordinateBounds` in that it allows + representation of non-axis aligned bounds and non-rectangular quadrilaterals + */ +typedef struct MGLCoordinateQuad { + /** Coordinate at the top left corner. */ + CLLocationCoordinate2D topLeft; + /** Coordinate at the top right corner. */ + CLLocationCoordinate2D topRight; + /** Coordinate at the bottom right corner. */ + CLLocationCoordinate2D bottomRight; + /** Coordinate at the bottom left corner. */ + CLLocationCoordinate2D bottomLeft; +} MGLCoordinateQuad; + + /** Creates a new `MGLCoordinateBounds` structure from the given southwest and northeast coordinates. diff --git a/platform/darwin/src/MGLGeometry_Private.h b/platform/darwin/src/MGLGeometry_Private.h index 7ad8314a79..bdc838667a 100644 --- a/platform/darwin/src/MGLGeometry_Private.h +++ b/platform/darwin/src/MGLGeometry_Private.h @@ -8,6 +8,7 @@ #import <mbgl/util/geo.hpp> #import <mbgl/util/geometry.hpp> +#import <array> typedef double MGLLocationRadians; typedef double MGLRadianDistance; typedef double MGLRadianDirection; @@ -56,6 +57,27 @@ NS_INLINE mbgl::LatLngBounds MGLLatLngBoundsFromCoordinateBounds(MGLCoordinateBo MGLLatLngFromLocationCoordinate2D(coordinateBounds.ne)); } +NS_INLINE std::array<mbgl::LatLng, 4> MGLLatLngArrayFromCoordinateQuad(MGLCoordinateQuad quad) { + return { MGLLatLngFromLocationCoordinate2D(quad.topLeft), + MGLLatLngFromLocationCoordinate2D(quad.topRight), + MGLLatLngFromLocationCoordinate2D(quad.bottomRight), + MGLLatLngFromLocationCoordinate2D(quad.bottomLeft) }; +} + +NS_INLINE MGLCoordinateQuad MGLCoordinateQuadFromLatLngArray(std::array<mbgl::LatLng, 4> quad) { + return { MGLLocationCoordinate2DFromLatLng(quad[0]), + MGLLocationCoordinate2DFromLatLng(quad[1]), + MGLLocationCoordinate2DFromLatLng(quad[2]), + MGLLocationCoordinate2DFromLatLng(quad[3]) }; +} + +NS_INLINE MGLCoordinateQuad MGLCoordinateQuadFromCoordinateBounds(MGLCoordinateBounds bounds) { + return { { bounds.ne.latitude, bounds.sw.longitude }, + bounds.ne, + { bounds.sw.latitude, bounds.ne.longitude }, + bounds.sw }; +} + #if TARGET_OS_IPHONE NS_INLINE mbgl::EdgeInsets MGLEdgeInsetsFromNSEdgeInsets(UIEdgeInsets insets) { return { insets.top, insets.left, insets.bottom, insets.right }; diff --git a/platform/darwin/src/MGLImageSource.h b/platform/darwin/src/MGLImageSource.h new file mode 100644 index 0000000000..9ef2c6719a --- /dev/null +++ b/platform/darwin/src/MGLImageSource.h @@ -0,0 +1,135 @@ +#import "MGLSource.h" + +#import "MGLFoundation.h" +#import "MGLTypes.h" +#import "MGLGeometry.h" + +NS_ASSUME_NONNULL_BEGIN + +MGL_EXPORT +/** + `MGLImageSource` is a map content source that supplies a georeferenced raster + image to be shown on the map. The geographic location of the raster image content, + supplied with `MGLCoordinateQuad`, can be non-axis aligned. + `MGLImageSource` supports raster content from `NSURL` or an `MGLImage`. + Supported image formats are : PNG, TIFF, JPEG, GIF (single frame rendering), + and BMP. + An image source is added to an `MGLStyle` object along with one or more + `MGLRasterStyleLayer` objects. Use a raster style layer to control the + appearance of content supplied by the image source. + + Each + <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-image"><code>image</code></a> + source defined by the style JSON file is represented at runtime by an + `MGLImageSource` object that you can use to initialize new style layers. You + can also add and remove sources dynamically using methods such as + `-[MGLStyle addSource:]` and `-[MGLStyle sourceWithIdentifier:]`. + + ### Example + + ```swift + let coordinates: MGLCoordinateQuad = MGLCoordinateQuad( + topLeft: CLLocationCoordinate2D(latitude: 46.437, longitude: -80.425), + topRight: CLLocationCoordinate2D(latitude: 46.437, longitude: -71.516), + bottomRight: CLLocationCoordinate2D(latitude: 37.936, longitude: -71.516), + bottomLeft: CLLocationCoordinate2D(latitude: 37.936, longitude: -80.425)) + let source = MGLImageSource(identifier: "images", coordinates: coordinates, imageURL: URL(string: "https://www.mapbox.com/mapbox-gl-js/assets/radar.gif")!) + mapView.style?.addSource(source) + ``` + */ +@interface MGLImageSource : MGLSource + +#pragma mark Initializing a Source + +/** + Returns a georeferenced image source with an identifier and coordinates + + @param identifier A string that uniquely identifies the source. + @param coordinateQuad The top left, top right, bottom right, and bottom left coordinates for the image. + @return An initialized image source. + */ +- (instancetype)initWithIdentifier:(NSString *)identifier coordinates:(MGLCoordinateQuad)coordinateQuad NS_DESIGNATED_INITIALIZER; + +/** + Returns a georeferenced image source with an identifier, coordinates and a URL + + @param identifier A string that uniquely identifies the source. + @param coordinateQuad the top left, top right, bottom right, and bottom left coordinates for the image. + @param url An HTTP(S) URL, absolute file URL, or local file URL relative to the + current application’s resource bundle. + @return An initialized shape source. + */ +- (instancetype)initWithIdentifier:(NSString *)identifier coordinates:(MGLCoordinateQuad)coordinateQuad imageURL:(NSURL *)url; + +/** + Returns a georeferenced image source with an identifier, coordinates and an image + + @param identifier A string that uniquely identifies the source. + @param coordinateQuad The top left, top right, bottom right, and bottom left coordinates for the image. + @param image The image to display for the sourcde. + @return An initialized shape source. + */ +- (instancetype)initWithIdentifier:(NSString *)identifier coordinates:(MGLCoordinateQuad)coordinateQuad image:(MGLImage *)image; + +/** + Returns a georeferenced image source with an identifier and coordinates + + @param identifier A string that uniquely identifies the source. + @param bounds The aligned coordinate bounds for the image. + @return An initialized image source. + */ +- (instancetype)initWithIdentifier:(NSString *)identifier bounds:(MGLCoordinateBounds)bounds; + +/** + Returns a georeferenced image source with an identifier, coordinates and a URL + + @param identifier A string that uniquely identifies the source. + @param bounds The aligned coordinate bounds for the image.@param url An HTTP(S) URL, absolute file URL, or local file URL relative to the + current application’s resource bundle. + @return An initialized shape source. + */ +- (instancetype)initWithIdentifier:(NSString *)identifier bounds:(MGLCoordinateBounds)bounds imageURL:(NSURL *)url; + +/** + Returns a georeferenced image source with an identifier, coordinates and an image + + @param identifier A string that uniquely identifies the source. + @param bounds The aligned coordinate bounds for the image. + @param image The image to display for the sourcde. + @return An initialized shape source. + */ +- (instancetype)initWithIdentifier:(NSString *)identifier bounds:(MGLCoordinateBounds)bounds image:(MGLImage *)image; + +#pragma mark Accessing a Source’s Content + +/** + The URL to the source image. + + If the receiver was initialized using `-initWithIdentifier:coordinates:` or + `-initWithIdentifier:coordinates:image:`, or the `image` property is set, + this property is set to `nil`. + */ +@property (nonatomic, copy, nullable)NSURL *URL; + +/** + The source image. + + If the receiver was initialized using `-initWithIdentifier:coordinates:` or + `-initWithIdentifier:coordinates:imageURL:`, or if the `URL` property is set, + this property is set to `nil`. + + + */ +@property (nonatomic, retain, nullable)MGLImage *image; + +/** + The coordinates at which the source image will be placed. + + If the receiver was initialized using `-initWithIdentifier:bounds:`, + `-initWithIdentifier:bounds:image:`, or `initWithIdentifier:bounds:imageURL`, + the bounds will be converted to an `MGLCoordinateQuad`. + */ +@property (nonatomic) MGLCoordinateQuad coordinates; +@end + +NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/MGLImageSource.mm b/platform/darwin/src/MGLImageSource.mm new file mode 100644 index 0000000000..9872a65aff --- /dev/null +++ b/platform/darwin/src/MGLImageSource.mm @@ -0,0 +1,105 @@ +#import "MGLImageSource_Private.h" + +#import "MGLGeometry_Private.h" +#import "MGLSource_Private.h" +#import "MGLTileSource_Private.h" +#import "NSURL+MGLAdditions.h" +#if TARGET_OS_IPHONE + #import "UIImage+MGLAdditions.h" +#else + #import "NSImage+MGLAdditions.h" +#endif + +#include <mbgl/style/sources/image_source.hpp> +#include <mbgl/util/premultiply.hpp> + +@interface MGLImageSource () + +@property (nonatomic, readonly) mbgl::style::ImageSource *rawSource; + +@end + +@implementation MGLImageSource + +- (instancetype)initWithIdentifier:(NSString *)identifier coordinates:(MGLCoordinateQuad)coordinateQuad { + + const auto coordsArray = MGLLatLngArrayFromCoordinateQuad(coordinateQuad); + auto source = std::make_unique<mbgl::style::ImageSource>(identifier.UTF8String, coordsArray); + return self = [super initWithPendingSource:std::move(source)]; +} + + +- (instancetype)initWithIdentifier:(NSString *)identifier coordinates:(MGLCoordinateQuad)coordinateQuad imageURL:(NSURL *)url { + self = [self initWithIdentifier:identifier coordinates: coordinateQuad]; + self.URL = url; + return self; +} + + +- (instancetype)initWithIdentifier:(NSString *)identifier coordinates:(MGLCoordinateQuad)coordinateQuad image:(MGLImage *)image { + self = [self initWithIdentifier:identifier coordinates: coordinateQuad]; + self.image = image; + + return self; +} + +- (instancetype)initWithIdentifier:(NSString *)identifier bounds:(MGLCoordinateBounds)bounds { + self = [self initWithIdentifier:identifier coordinates:MGLCoordinateQuadFromCoordinateBounds(bounds)]; + return self; +} + +- (instancetype)initWithIdentifier:(NSString *)identifier bounds:(MGLCoordinateBounds)bounds imageURL:(NSURL *)url { + self = [self initWithIdentifier:identifier coordinates:MGLCoordinateQuadFromCoordinateBounds(bounds) imageURL:url]; + return self; +} + +- (instancetype)initWithIdentifier:(NSString *)identifier bounds:(MGLCoordinateBounds)bounds image:(MGLImage *)image { + self = [self initWithIdentifier:identifier coordinates:MGLCoordinateQuadFromCoordinateBounds(bounds) image:image]; + return self; +} + +- (NSURL *)URL { + auto url = self.rawSource->getURL(); + return url ? [NSURL URLWithString:@(url->c_str())] : nil; +} + +- (void)setURL:(NSURL *)url { + if (url) { + self.rawSource->setURL(url.mgl_URLByStandardizingScheme.absoluteString.UTF8String); + _image = nil; + } else { + self.image = nullptr; + } +} + +- (void)setImage:(MGLImage *)image { + if (image != nullptr) { + mbgl::UnassociatedImage unassociatedImage = mbgl::util::unpremultiply(image.mgl_PremultipliedImage); + self.rawSource->setImage(std::move(unassociatedImage)); + } + _image = image; +} + +- (MGLCoordinateQuad)coordinates { + return MGLCoordinateQuadFromLatLngArray(self.rawSource->getCoordinates()); +} + +- (void)setCoordinates: (MGLCoordinateQuad)coordinateQuad { + self.rawSource->setCoordinates(MGLLatLngArrayFromCoordinateQuad(coordinateQuad)); +} + +- (NSString *)description { + return [NSString stringWithFormat:@"<%@: %p; identifier = %@; URL = %@; image = %@>", + NSStringFromClass([self class]), (void *)self, self.identifier, self.URL, self.image]; +} + +- (mbgl::style::ImageSource *)rawSource { + return (mbgl::style::ImageSource *)super.rawSource; +} + +- (NSString *)attributionHTMLString { + auto attribution = self.rawSource->getAttribution(); + return attribution ? @(attribution->c_str()) : nil; +} + +@end diff --git a/platform/darwin/src/MGLImageSource_Private.h b/platform/darwin/src/MGLImageSource_Private.h new file mode 100644 index 0000000000..54f6e00afa --- /dev/null +++ b/platform/darwin/src/MGLImageSource_Private.h @@ -0,0 +1,9 @@ +#import "MGLImageSource.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface MGLImageSource (Private) + +@end + +NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/MGLOfflineStorage.h b/platform/darwin/src/MGLOfflineStorage.h index 16f134adb1..90ba15c84c 100644 --- a/platform/darwin/src/MGLOfflineStorage.h +++ b/platform/darwin/src/MGLOfflineStorage.h @@ -154,6 +154,8 @@ typedef NS_ENUM(NSUInteger, MGLResourceKind) { /** JSON part of a sprite sheet. It is constructed of the prefix in https://www.mapbox.com/mapbox-gl-js/style-spec/#root-sprite and a JSON file extension. */ MGLResourceKindSpriteJSON, + /** Image **/ + MGLResourceKindImage, }; /** diff --git a/platform/darwin/src/MGLOfflineStorage.mm b/platform/darwin/src/MGLOfflineStorage.mm index 81774ad3cb..040b36f8be 100644 --- a/platform/darwin/src/MGLOfflineStorage.mm +++ b/platform/darwin/src/MGLOfflineStorage.mm @@ -99,6 +99,9 @@ NSString * const MGLOfflinePackMaximumCountUserInfoKey = MGLOfflinePackUserInfoK case mbgl::Resource::Kind::SpriteJSON: kind = MGLResourceKindSpriteJSON; break; + case mbgl::Resource::Kind::Image: + kind = MGLResourceKindImage; + break; case mbgl::Resource::Kind::Unknown: kind = MGLResourceKindUnknown; break; diff --git a/platform/darwin/src/MGLStyle.mm b/platform/darwin/src/MGLStyle.mm index 2e6f2bc29a..592ab86780 100644 --- a/platform/darwin/src/MGLStyle.mm +++ b/platform/darwin/src/MGLStyle.mm @@ -23,6 +23,7 @@ #import "MGLVectorSource.h" #import "MGLRasterSource.h" #import "MGLShapeSource.h" +#import "MGLImageSource.h" #import "MGLAttributionInfo_Private.h" @@ -41,6 +42,7 @@ #include <mbgl/style/sources/geojson_source.hpp> #include <mbgl/style/sources/vector_source.hpp> #include <mbgl/style/sources/raster_source.hpp> +#include <mbgl/style/sources/image_source.hpp> #if TARGET_OS_IPHONE #import "UIImage+MGLAdditions.h" @@ -178,6 +180,8 @@ static NSURL *MGLStyleURL_emerald; return [[MGLShapeSource alloc] initWithRawSource:geoJSONSource]; } else if (auto rasterSource = rawSource->as<mbgl::style::RasterSource>()) { return [[MGLRasterSource alloc] initWithRawSource:rasterSource]; + } else if (auto imageSource = rawSource->as<mbgl::style::ImageSource>()) { + return [[MGLImageSource alloc] initWithRawSource:imageSource]; } else { return [[MGLSource alloc] initWithRawSource:rawSource]; } diff --git a/platform/darwin/src/NSValue+MGLAdditions.h b/platform/darwin/src/NSValue+MGLAdditions.h index 0aaa2a337a..f3026a389f 100644 --- a/platform/darwin/src/NSValue+MGLAdditions.h +++ b/platform/darwin/src/NSValue+MGLAdditions.h @@ -56,6 +56,20 @@ NS_ASSUME_NONNULL_BEGIN */ @property (readonly) MGLCoordinateBounds MGLCoordinateBoundsValue; +/** + Creates a new value object containing the specified Mapbox coordinate + quad structure. + + @param quad The value for the new object. + @return A new value object that contains the coordinate quad information. + */ ++ (instancetype)valueWithMGLCoordinateQuad:(MGLCoordinateQuad)quad; + +/** + The Mapbox coordinate quad structure representation of the value. + */ +- (MGLCoordinateQuad)MGLCoordinateQuadValue; + #pragma mark Working with Offline Map Values /** diff --git a/platform/darwin/src/NSValue+MGLAdditions.m b/platform/darwin/src/NSValue+MGLAdditions.m index ef894f0eb4..1383056944 100644 --- a/platform/darwin/src/NSValue+MGLAdditions.m +++ b/platform/darwin/src/NSValue+MGLAdditions.m @@ -34,6 +34,16 @@ return bounds; } ++ (instancetype)valueWithMGLCoordinateQuad:(MGLCoordinateQuad)quad { + return [self valueWithBytes:&quad objCType:@encode(MGLCoordinateQuad)]; +} + +- (MGLCoordinateQuad)MGLCoordinateQuadValue { + MGLCoordinateQuad quad; + [self getValue:&quad]; + return quad; +} + #pragma mark Offline maps + (NSValue *)valueWithMGLOfflinePackProgress:(MGLOfflinePackProgress)progress { diff --git a/platform/darwin/test/MGLDocumentationExampleTests.swift b/platform/darwin/test/MGLDocumentationExampleTests.swift index 48e6b17f44..2a441d2774 100644 --- a/platform/darwin/test/MGLDocumentationExampleTests.swift +++ b/platform/darwin/test/MGLDocumentationExampleTests.swift @@ -104,6 +104,20 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate { XCTAssertNotNil(mapView.style?.source(withIdentifier: "pois")) } + func testMGLImageSource() { + //#-example-code + let coordinates: MGLCoordinateQuad = MGLCoordinateQuad( + topLeft: CLLocationCoordinate2D(latitude: 46.437, longitude: -80.425), + topRight: CLLocationCoordinate2D(latitude: 46.437, longitude: -71.516), + bottomRight: CLLocationCoordinate2D(latitude: 37.936, longitude: -71.516), + bottomLeft: CLLocationCoordinate2D(latitude: 37.936, longitude: -80.425)) + let source = MGLImageSource(identifier: "images", coordinates: coordinates, imageURL: URL(string: "https://www.mapbox.com/mapbox-gl-js/assets/radar.gif")!) + mapView.style?.addSource(source) + //#-end-example-code + + XCTAssertNotNil(mapView.style?.source(withIdentifier: "images")) + } + func testMGLCircleStyleLayer() { let population = MGLVectorSource(identifier: "population", configurationURL: URL(string: "https://example.com/style.json")!) mapView.style?.addSource(population) diff --git a/platform/darwin/test/MGLGeometryTests.mm b/platform/darwin/test/MGLGeometryTests.mm index 220a837643..1c85470188 100644 --- a/platform/darwin/test/MGLGeometryTests.mm +++ b/platform/darwin/test/MGLGeometryTests.mm @@ -144,4 +144,23 @@ XCTAssertEqualObjects(serializedGeoJSON, geoJSON, @"MGLPointFeature should serialize as a GeoJSON point feature."); } +- (void)testMGLCoordinateBoundsToMGLCoordinateQuad { + MGLCoordinateBounds bounds = MGLCoordinateBoundsMake(CLLocationCoordinate2DMake(37.936, -80.425), + CLLocationCoordinate2DMake(46.437, -71.516)); + + MGLCoordinateQuad quad = MGLCoordinateQuadFromCoordinateBounds(bounds); + XCTAssertEqualObjects([NSValue valueWithMGLCoordinate:bounds.sw], + [NSValue valueWithMGLCoordinate:quad.bottomLeft], + @"Bounds southwest should be bottom left of quad."); + XCTAssertEqualObjects([NSValue valueWithMGLCoordinate:bounds.ne], + [NSValue valueWithMGLCoordinate:quad.topRight], + @"Bounds northeast should be top right of quad."); + + XCTAssertEqualObjects([NSValue valueWithMGLCoordinate:CLLocationCoordinate2DMake(46.437, -80.425)], + [NSValue valueWithMGLCoordinate:quad.topLeft], + @"Quad top left should be computed correctly."); + XCTAssertEqualObjects([NSValue valueWithMGLCoordinate:CLLocationCoordinate2DMake(37.936, -71.516)], + [NSValue valueWithMGLCoordinate:quad.bottomRight], + @"Quad bottom right should be computed correctly."); +} @end diff --git a/platform/darwin/test/MGLImageSourceTests.m b/platform/darwin/test/MGLImageSourceTests.m new file mode 100644 index 0000000000..3349a7463b --- /dev/null +++ b/platform/darwin/test/MGLImageSourceTests.m @@ -0,0 +1,42 @@ +#import <XCTest/XCTest.h> + +#import <Mapbox/Mapbox.h> + +@interface MGLImageSourceTests : XCTestCase + +@end + +@implementation MGLImageSourceTests + + +- (void)testMGLImageSourceWithImageURL { + + MGLCoordinateQuad quad = { { 80, 37}, { 81, 37}, { 81, 39}, { 80, 39}}; + MGLImageSource *source = [[MGLImageSource alloc] initWithIdentifier:@"source-id" coordinates:quad imageURL: [NSURL URLWithString:@"http://host/image.png"]]; + + XCTAssertNotNil(source.URL); + XCTAssertEqualObjects(source.URL.absoluteString, @"http://host/image.png"); + XCTAssertNil(source.image); +} + +- (void)testMGLImageSourceWithImage { + + NSString *imageName = @"RadarImage"; +#if TARGET_OS_IPHONE + MGLImage *image = [MGLImage imageNamed:imageName + inBundle:[NSBundle bundleForClass:[self class]] + compatibleWithTraitCollection:nil]; +#else + MGLImage *image = [[NSBundle bundleForClass:[self class]] imageForResource:imageName]; +#endif + XCTAssertNotNil(image); + + MGLCoordinateQuad quad = { { 80, 37}, { 81, 37}, { 81, 39}, { 80, 39}}; + MGLImageSource *source = [[MGLImageSource alloc] initWithIdentifier:@"source-id" coordinates:quad image:image]; + + XCTAssertNotNil(source.image); + XCTAssertEqualObjects(source.image, image); + XCTAssertNil(source.URL); +} + +@end diff --git a/platform/darwin/test/MGLStyleTests.mm b/platform/darwin/test/MGLStyleTests.mm index d93483ea6e..7f82dddfac 100644 --- a/platform/darwin/test/MGLStyleTests.mm +++ b/platform/darwin/test/MGLStyleTests.mm @@ -210,9 +210,9 @@ MGLRasterSource *rasterSource = [[MGLRasterSource alloc] initWithIdentifier:@"some-identifier" tileURLTemplates:@[] options:nil]; [self.style addSource:rasterSource]; - // Attempt to remove a shape source with the same identifier as the raster source - MGLShapeSource *shapeSource = [[MGLShapeSource alloc] initWithIdentifier:@"some-identifier" shape:nil options:nil]; - [self.style removeSource:shapeSource]; + // Attempt to remove an image source with the same identifier as the raster source + MGLImageSource *imageSource = [[MGLImageSource alloc] initWithIdentifier:@"some-identifier" coordinates: { }]; + [self.style removeSource:imageSource]; // The raster source should still be added XCTAssertTrue([[self.style sourceWithIdentifier:rasterSource.identifier] isMemberOfClass:[MGLRasterSource class]]); @@ -220,16 +220,16 @@ [self.style removeSource:rasterSource]; // Add the shape source - [self.style addSource:shapeSource]; + [self.style addSource:imageSource]; // Attempt to remove a vector source with the same identifer as the shape source MGLVectorSource *vectorSource = [[MGLVectorSource alloc] initWithIdentifier:@"some-identifier" tileURLTemplates:@[] options:nil]; [self.style removeSource:vectorSource]; - // The shape source should still be added - XCTAssertTrue([[self.style sourceWithIdentifier:shapeSource.identifier] isMemberOfClass:[MGLShapeSource class]]); + // The image source should still be added + XCTAssertTrue([[self.style sourceWithIdentifier:imageSource.identifier] isMemberOfClass:[MGLImageSource class]]); - // Remove the shape source - [self.style removeSource:shapeSource]; + // Remove the image source + [self.style removeSource:imageSource]; // Add the vector source [self.style addSource:vectorSource]; @@ -237,7 +237,7 @@ // Attempt to remove the previously created raster source that has the same identifer as the shape source [self.style removeSource:rasterSource]; // The vector source should still be added - XCTAssertTrue([[self.style sourceWithIdentifier:shapeSource.identifier] isMemberOfClass:[MGLVectorSource class]]); + XCTAssertTrue([[self.style sourceWithIdentifier:imageSource.identifier] isMemberOfClass:[MGLVectorSource class]]); } - (void)testRemovingSourceInUse { diff --git a/platform/darwin/test/Media.xcassets/RadarImage.imageset/Contents.json b/platform/darwin/test/Media.xcassets/RadarImage.imageset/Contents.json new file mode 100644 index 0000000000..79be9ed970 --- /dev/null +++ b/platform/darwin/test/Media.xcassets/RadarImage.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "radar.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +}
\ No newline at end of file diff --git a/platform/darwin/test/Media.xcassets/RadarImage.imageset/radar.png b/platform/darwin/test/Media.xcassets/RadarImage.imageset/radar.png Binary files differnew file mode 100644 index 0000000000..e23697f42a --- /dev/null +++ b/platform/darwin/test/Media.xcassets/RadarImage.imageset/radar.png diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m index 672bb3b997..756f4d5516 100644 --- a/platform/ios/app/MBXViewController.m +++ b/platform/ios/app/MBXViewController.m @@ -72,6 +72,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsRuntimeStylingRows) { MBXSettingsRuntimeStylingUpdateShapeSourceFeatures, MBXSettingsRuntimeStylingVectorSource, MBXSettingsRuntimeStylingRasterSource, + MBXSettingsRuntimeStylingImageSource, MBXSettingsRuntimeStylingCountryLabels, MBXSettingsRuntimeStylingRouteLine, MBXSettingsRuntimeStylingDDSPolygon, @@ -345,6 +346,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { @"Update Shape Source: Features", @"Style Vector Source", @"Style Raster Source", + @"Style Image Source", [NSString stringWithFormat:@"Label Countries in %@", (_usingLocaleBasedCountryLabels ? @"Local Language" : [[NSLocale currentLocale] displayNameForKey:NSLocaleIdentifier value:[self bestLanguageForUser]])], @"Add Route Line", @"Dynamically Style Polygon", @@ -585,6 +587,9 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { case MBXSettingsRuntimeStylingRasterSource: [self styleRasterSource]; break; + case MBXSettingsRuntimeStylingImageSource: + [self styleImageSource]; + break; case MBXSettingsRuntimeStylingCountryLabels: [self styleCountryLabelsLanguage]; break; @@ -1308,6 +1313,21 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { [self.mapView.style addLayer:rasterLayer]; } +- (void)styleImageSource +{ + MGLCoordinateQuad coordinateQuad = { + { 46.437, -80.425 }, + { 46.437, -71.516 }, + { 37.936, -71.516 }, + { 37.936, -80.425 } }; + + MGLImageSource *imageSource = [[MGLImageSource alloc] initWithIdentifier:@"style-image-source-id" coordinates:coordinateQuad imageURL:[NSURL URLWithString:@"https://www.mapbox.com/mapbox-gl-js/assets/radar.gif"]]; + [self.mapView.style addSource:imageSource]; + + MGLRasterStyleLayer *rasterLayer = [[MGLRasterStyleLayer alloc] initWithIdentifier:@"style-raster-image-layer-id" source:imageSource]; + [self.mapView.style addLayer:rasterLayer]; +} + -(void)styleCountryLabelsLanguage { NSArray<NSString *> *labelLayers = @[ diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index 0cfe72de81..0ed3fdbe8c 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -7,6 +7,13 @@ objects = { /* Begin PBXBuildFile section */ + 071BBAFF1EE7613E001FB02A /* MGLImageSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 071BBAFD1EE75CD4001FB02A /* MGLImageSource.mm */; }; + 071BBB001EE7613F001FB02A /* MGLImageSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 071BBAFD1EE75CD4001FB02A /* MGLImageSource.mm */; }; + 071BBB011EE76142001FB02A /* MGLImageSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 071BBAFB1EE75CD4001FB02A /* MGLImageSource_Private.h */; }; + 071BBB021EE76143001FB02A /* MGLImageSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 071BBAFB1EE75CD4001FB02A /* MGLImageSource_Private.h */; }; + 071BBB031EE76146001FB02A /* MGLImageSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 071BBAFC1EE75CD4001FB02A /* MGLImageSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 071BBB041EE76147001FB02A /* MGLImageSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 071BBAFC1EE75CD4001FB02A /* MGLImageSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 071BBB071EE77631001FB02A /* MGLImageSourceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 071BBB051EE7761A001FB02A /* MGLImageSourceTests.m */; }; 1753ED421E53CE6F00A9FD90 /* MGLConversion.h in Headers */ = {isa = PBXBuildFile; fileRef = 1753ED411E53CE6F00A9FD90 /* MGLConversion.h */; }; 1753ED431E53CE6F00A9FD90 /* MGLConversion.h in Headers */ = {isa = PBXBuildFile; fileRef = 1753ED411E53CE6F00A9FD90 /* MGLConversion.h */; }; 1F06668A1EC64F8E001C16D7 /* MGLLight.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F0666881EC64F8E001C16D7 /* MGLLight.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -538,6 +545,10 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 071BBAFB1EE75CD4001FB02A /* MGLImageSource_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLImageSource_Private.h; sourceTree = "<group>"; }; + 071BBAFC1EE75CD4001FB02A /* MGLImageSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLImageSource.h; sourceTree = "<group>"; }; + 071BBAFD1EE75CD4001FB02A /* MGLImageSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLImageSource.mm; sourceTree = "<group>"; }; + 071BBB051EE7761A001FB02A /* MGLImageSourceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLImageSourceTests.m; path = ../../darwin/test/MGLImageSourceTests.m; sourceTree = "<group>"; }; 1753ED411E53CE6F00A9FD90 /* MGLConversion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLConversion.h; sourceTree = "<group>"; }; 1F0666881EC64F8E001C16D7 /* MGLLight.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLLight.h; sourceTree = "<group>"; }; 1F0666891EC64F8E001C16D7 /* MGLLight.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLLight.mm; sourceTree = "<group>"; }; @@ -976,6 +987,9 @@ 35136D491D4277EA00C20EFD /* Sources */ = { isa = PBXGroup; children = ( + 071BBAFC1EE75CD4001FB02A /* MGLImageSource.h */, + 071BBAFB1EE75CD4001FB02A /* MGLImageSource_Private.h */, + 071BBAFD1EE75CD4001FB02A /* MGLImageSource.mm */, 3566C76A1D4A8DFA008152BC /* MGLRasterSource.h */, DAF0D80F1DFE0EA000B28378 /* MGLRasterSource_Private.h */, 3566C76B1D4A8DFA008152BC /* MGLRasterSource.mm */, @@ -1133,6 +1147,7 @@ 40CFA64E1D78754A008103BD /* Sources */ = { isa = PBXGroup; children = ( + 071BBB051EE7761A001FB02A /* MGLImageSourceTests.m */, 40CFA6501D787579008103BD /* MGLShapeSourceTests.mm */, 920A3E5C1E6F995200C16EFC /* MGLSourceQueryTests.m */, 4085AF081D933DEA00F11B22 /* MGLTileSetTests.mm */, @@ -1665,6 +1680,7 @@ DA8848311CBAFA6200AB86E3 /* NSString+MGLAdditions.h in Headers */, 353933F81D3FB79F003F57D7 /* MGLLineStyleLayer.h in Headers */, DAAF722D1DA903C700312FA4 /* MGLStyleValue_Private.h in Headers */, + 071BBB031EE76146001FB02A /* MGLImageSource.h in Headers */, DA8847F41CBAFA5100AB86E3 /* MGLOfflinePack.h in Headers */, DA88482E1CBAFA6200AB86E3 /* NSException+MGLAdditions.h in Headers */, DA8848551CBAFB9800AB86E3 /* MGLLocationManager.h in Headers */, @@ -1705,6 +1721,7 @@ DA8847F21CBAFA5100AB86E3 /* MGLMapCamera.h in Headers */, 3538AA1D1D542239008EC33D /* MGLForegroundStyleLayer.h in Headers */, DA8847F51CBAFA5100AB86E3 /* MGLOfflineRegion.h in Headers */, + 071BBB011EE76142001FB02A /* MGLImageSource_Private.h in Headers */, DA737EE11D056A4E005BDA16 /* MGLMapViewDelegate.h in Headers */, DA8848851CBB033F00AB86E3 /* FABKitProtocol.h in Headers */, DA88481B1CBAFA6200AB86E3 /* MGLGeometry_Private.h in Headers */, @@ -1740,6 +1757,7 @@ FA68F14B1E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.h in Headers */, 353933FC1D3FB7C0003F57D7 /* MGLRasterStyleLayer.h in Headers */, 3566C76D1D4A8DFA008152BC /* MGLRasterSource.h in Headers */, + 071BBB021EE76143001FB02A /* MGLImageSource_Private.h in Headers */, DAED38641D62D0FC00D7640F /* NSURL+MGLAdditions.h in Headers */, DABFB85E1CBE99E500D62B32 /* MGLAnnotation.h in Headers */, DABFB8641CBE99E500D62B32 /* MGLOfflineStorage.h in Headers */, @@ -1748,6 +1766,7 @@ 3566C7671D4A77BA008152BC /* MGLShapeSource.h in Headers */, DA35A29F1CC9E94C00E826B2 /* MGLCoordinateFormatter.h in Headers */, 404C26E31D89B877000AA13D /* MGLTileSource.h in Headers */, + 071BBB041EE76147001FB02A /* MGLImageSource.h in Headers */, DABFB8611CBE99E500D62B32 /* MGLMultiPoint.h in Headers */, 3510FFF11D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h in Headers */, 35D3A1E71E9BE7EC002B38EE /* MGLScaleBar.h in Headers */, @@ -1958,7 +1977,6 @@ TargetAttributes = { DA1DC9491CB6C1C2006E619F = { CreatedOnToolsVersion = 7.3; - DevelopmentTeam = GJZR2MEM28; LastSwiftMigration = 0820; }; DA25D5B81CCD9EDE00607828 = { @@ -2139,6 +2157,7 @@ 35D9DDE21DA25EEC00DAAD69 /* MGLCodingTests.m in Sources */, DA1F8F3D1EBD287B00367E42 /* MGLDocumentationGuideTests.swift in Sources */, 3598544D1E1D38AA00B29F84 /* MGLDistanceFormatterTests.m in Sources */, + 071BBB071EE77631001FB02A /* MGLImageSourceTests.m in Sources */, DA2DBBCE1D51E80400D38FF9 /* MGLStyleLayerTests.m in Sources */, DA35A2C61CCA9F8300E826B2 /* MGLCompassDirectionFormatterTests.m in Sources */, DAE7DEC21E245455007505A6 /* MGLNSStringAdditionsTests.m in Sources */, @@ -2215,6 +2234,7 @@ DD0902A91DB1929D00C5BDCE /* MGLNetworkConfiguration.m in Sources */, 35D13AB91D3D15E300AFB4E0 /* MGLStyleLayer.mm in Sources */, DA35A2CB1CCAAAD200E826B2 /* NSValue+MGLAdditions.m in Sources */, + 071BBB001EE7613F001FB02A /* MGLImageSource.mm in Sources */, DA8848321CBAFA6200AB86E3 /* NSString+MGLAdditions.m in Sources */, 408AA8581DAEDA1E00022900 /* NSDictionary+MGLAdditions.mm in Sources */, DA35A2A11CC9E95F00E826B2 /* MGLCoordinateFormatter.m in Sources */, @@ -2296,6 +2316,7 @@ DD0902AA1DB1929D00C5BDCE /* MGLNetworkConfiguration.m in Sources */, DA35A2B41CCA141D00E826B2 /* MGLCompassDirectionFormatter.m in Sources */, 35D13ABA1D3D15E300AFB4E0 /* MGLStyleLayer.mm in Sources */, + 071BBAFF1EE7613E001FB02A /* MGLImageSource.mm in Sources */, DA35A2CC1CCAAAD200E826B2 /* NSValue+MGLAdditions.m in Sources */, 408AA8591DAEDA1E00022900 /* NSDictionary+MGLAdditions.mm in Sources */, DAA4E4281CBB730400178DFB /* MGLTypes.m in Sources */, @@ -2616,6 +2637,7 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = "$(SRCROOT)/app/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; @@ -2629,6 +2651,7 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = "$(SRCROOT)/app/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; diff --git a/platform/ios/src/Mapbox.h b/platform/ios/src/Mapbox.h index 67a26e8ed4..abe16cc3ee 100644 --- a/platform/ios/src/Mapbox.h +++ b/platform/ios/src/Mapbox.h @@ -52,6 +52,7 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[]; #import "MGLVectorSource.h" #import "MGLShapeSource.h" #import "MGLRasterSource.h" +#import "MGLImageSource.h" #import "MGLTilePyramidOfflineRegion.h" #import "MGLTypes.h" #import "MGLUserLocation.h" diff --git a/platform/ios/src/UIImage+MGLAdditions.h b/platform/ios/src/UIImage+MGLAdditions.h index 0b4cb4c015..671a5ced85 100644 --- a/platform/ios/src/UIImage+MGLAdditions.h +++ b/platform/ios/src/UIImage+MGLAdditions.h @@ -10,6 +10,8 @@ NS_ASSUME_NONNULL_BEGIN - (std::unique_ptr<mbgl::style::Image>)mgl_styleImageWithIdentifier:(NSString *)identifier; +- (mbgl::PremultipliedImage)mgl_PremultipliedImage; + @end NS_ASSUME_NONNULL_END diff --git a/platform/ios/src/UIImage+MGLAdditions.mm b/platform/ios/src/UIImage+MGLAdditions.mm index b10c48a62a..029366c01e 100644 --- a/platform/ios/src/UIImage+MGLAdditions.mm +++ b/platform/ios/src/UIImage+MGLAdditions.mm @@ -25,8 +25,11 @@ - (std::unique_ptr<mbgl::style::Image>)mgl_styleImageWithIdentifier:(NSString *)identifier { BOOL isTemplate = self.renderingMode == UIImageRenderingModeAlwaysTemplate; return std::make_unique<mbgl::style::Image>([identifier UTF8String], - MGLPremultipliedImageFromCGImage(self.CGImage), + self.mgl_PremultipliedImage, float(self.scale), isTemplate); } +-(mbgl::PremultipliedImage)mgl_PremultipliedImage { + return MGLPremultipliedImageFromCGImage(self.CGImage); +} @end diff --git a/platform/macos/app/Assets.xcassets/Radar/Contents.json b/platform/macos/app/Assets.xcassets/Radar/Contents.json new file mode 100644 index 0000000000..da4a164c91 --- /dev/null +++ b/platform/macos/app/Assets.xcassets/Radar/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +}
\ No newline at end of file diff --git a/platform/macos/app/Assets.xcassets/Radar/southeast_0.imageset/Contents.json b/platform/macos/app/Assets.xcassets/Radar/southeast_0.imageset/Contents.json new file mode 100644 index 0000000000..ea096b04b8 --- /dev/null +++ b/platform/macos/app/Assets.xcassets/Radar/southeast_0.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "southeast_radar_0.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +}
\ No newline at end of file diff --git a/platform/macos/app/Assets.xcassets/Radar/southeast_0.imageset/southeast_radar_0.png b/platform/macos/app/Assets.xcassets/Radar/southeast_0.imageset/southeast_radar_0.png Binary files differnew file mode 100644 index 0000000000..c304b619c4 --- /dev/null +++ b/platform/macos/app/Assets.xcassets/Radar/southeast_0.imageset/southeast_radar_0.png diff --git a/platform/macos/app/Assets.xcassets/Radar/southeast_1.imageset/Contents.json b/platform/macos/app/Assets.xcassets/Radar/southeast_1.imageset/Contents.json new file mode 100644 index 0000000000..a6a031ae2b --- /dev/null +++ b/platform/macos/app/Assets.xcassets/Radar/southeast_1.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "southeast_radar_1.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +}
\ No newline at end of file diff --git a/platform/macos/app/Assets.xcassets/Radar/southeast_1.imageset/southeast_radar_1.png b/platform/macos/app/Assets.xcassets/Radar/southeast_1.imageset/southeast_radar_1.png Binary files differnew file mode 100644 index 0000000000..ed09fffbe1 --- /dev/null +++ b/platform/macos/app/Assets.xcassets/Radar/southeast_1.imageset/southeast_radar_1.png diff --git a/platform/macos/app/Assets.xcassets/Radar/southeast_2.imageset/Contents.json b/platform/macos/app/Assets.xcassets/Radar/southeast_2.imageset/Contents.json new file mode 100644 index 0000000000..d607dda298 --- /dev/null +++ b/platform/macos/app/Assets.xcassets/Radar/southeast_2.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "southeast_radar_2.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +}
\ No newline at end of file diff --git a/platform/macos/app/Assets.xcassets/Radar/southeast_2.imageset/southeast_radar_2.png b/platform/macos/app/Assets.xcassets/Radar/southeast_2.imageset/southeast_radar_2.png Binary files differnew file mode 100644 index 0000000000..fee630f863 --- /dev/null +++ b/platform/macos/app/Assets.xcassets/Radar/southeast_2.imageset/southeast_radar_2.png diff --git a/platform/macos/app/Assets.xcassets/Radar/southeast_3.imageset/Contents.json b/platform/macos/app/Assets.xcassets/Radar/southeast_3.imageset/Contents.json new file mode 100644 index 0000000000..9a110068a1 --- /dev/null +++ b/platform/macos/app/Assets.xcassets/Radar/southeast_3.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "southeast_radar_3.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +}
\ No newline at end of file diff --git a/platform/macos/app/Assets.xcassets/Radar/southeast_3.imageset/southeast_radar_3.png b/platform/macos/app/Assets.xcassets/Radar/southeast_3.imageset/southeast_radar_3.png Binary files differnew file mode 100644 index 0000000000..c4c7146afa --- /dev/null +++ b/platform/macos/app/Assets.xcassets/Radar/southeast_3.imageset/southeast_radar_3.png diff --git a/platform/macos/app/Base.lproj/MainMenu.xib b/platform/macos/app/Base.lproj/MainMenu.xib index 20a4f65b3f..9a8cf05c16 100644 --- a/platform/macos/app/Base.lproj/MainMenu.xib +++ b/platform/macos/app/Base.lproj/MainMenu.xib @@ -554,6 +554,12 @@ <action selector="insertCustomStyleLayer:" target="-1" id="LE5-lz-kx3"/> </connections> </menuItem> + <menuItem title="Add Animated Image Source" id="tjA-fT-GbA"> + <modifierMask key="keyEquivalentModifierMask"/> + <connections> + <action selector="addAnimatedImageSource:" target="-1" id="TuN-Pa-hTG"/> + </connections> + </menuItem> <menuItem title="Show All Annnotations" keyEquivalent="A" id="yMj-uM-8SN"> <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/> <connections> diff --git a/platform/macos/app/MapDocument.m b/platform/macos/app/MapDocument.m index 59844d363e..281497ac92 100644 --- a/platform/macos/app/MapDocument.m +++ b/platform/macos/app/MapDocument.m @@ -617,6 +617,34 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio cos(angle) * 20); } +- (IBAction) addAnimatedImageSource:(id)sender { + + MGLImage *image = [[NSBundle bundleForClass:[self class]] imageForResource:@"southeast_0"]; + + MGLCoordinateBounds bounds = { {22.551103322318994, -90.24006072802854}, {36.928147474567794, -75.1441643681673} }; + MGLImageSource *imageSource = [[MGLImageSource alloc] initWithIdentifier:@"radar-source" bounds:bounds image:image]; + [self.mapView.style addSource:imageSource]; + + MGLRasterStyleLayer * imageLayer = [[MGLRasterStyleLayer alloc] initWithIdentifier:@"radar-layer" source:imageSource]; + [self.mapView.style addLayer:imageLayer]; + + [NSTimer scheduledTimerWithTimeInterval:1.0 + target:self + selector:@selector(updateAnimatedImageSource:) + userInfo:imageSource + repeats:YES]; +} + + +- (void)updateAnimatedImageSource:(NSTimer *)timer { + MGLImageSource *imageSource = (MGLImageSource *)timer.userInfo; + double timeInterval = timer.fireDate.timeIntervalSinceReferenceDate; + int radarSuffix = (int)timeInterval % 4; + MGLImage *image = [[NSBundle bundleForClass:[self class]] imageForResource:[NSString stringWithFormat:@"southeast_%d", radarSuffix]]; + + [imageSource setValue:image forKey:@"image"]; +} + - (IBAction)insertCustomStyleLayer:(id)sender { [self.undoManager registerUndoWithTarget:self handler:^(id _Nonnull target) { [self removeCustomStyleLayer:sender]; @@ -695,14 +723,18 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio MGLTransition transition = { .duration = 5, .delay = 1 }; self.mapView.style.transition = transition; - MGLFillStyleLayer *fillStyleLayer = (MGLFillStyleLayer *)[self.mapView.style layerWithIdentifier:@"water"]; - + MGLStyleLayer *waterLayer = [self.mapView.style layerWithIdentifier:@"water"]; MGLStyleValue *colorFunction = [MGLStyleValue<NSColor *> valueWithInterpolationMode:MGLInterpolationModeExponential cameraStops:@{ @0.0: [MGLStyleValue<NSColor *> valueWithRawValue:[NSColor redColor]], @10.0: [MGLStyleValue<NSColor *> valueWithRawValue:[NSColor yellowColor]], @20.0: [MGLStyleValue<NSColor *> valueWithRawValue:[NSColor blackColor]], } options:nil]; - fillStyleLayer.fillColor = colorFunction; + + if ([waterLayer respondsToSelector:@selector(fillColor)]) { + [waterLayer setValue:colorFunction forKey:@"fillColor"]; + } else if ([waterLayer respondsToSelector:@selector(lineColor)]) { + [waterLayer setValue:colorFunction forKey:@"lineColor"]; + } NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"amsterdam" ofType:@"geojson"]; NSURL *geoJSONURL = [NSURL fileURLWithPath:filePath]; @@ -714,21 +746,34 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio fillLayer.predicate = [NSPredicate predicateWithFormat:@"%K == %@", @"type", @"park"]; [self.mapView.style addLayer:fillLayer]; + MGLSource *streetsSource = [self.mapView.style sourceWithIdentifier:@"composite"]; + if (streetsSource) { NSImage *image = [NSImage imageNamed:NSImageNameIChatTheaterTemplate]; [self.mapView.style setImage:image forName:NSImageNameIChatTheaterTemplate]; - MGLSource *streetsSource = [self.mapView.style sourceWithIdentifier:@"composite"]; - MGLSymbolStyleLayer *theaterLayer = [[MGLSymbolStyleLayer alloc] initWithIdentifier:@"theaters" source:streetsSource]; - theaterLayer.sourceLayerIdentifier = @"poi_label"; - theaterLayer.predicate = [NSPredicate predicateWithFormat:@"maki == 'theatre'"]; - theaterLayer.iconImageName = [MGLStyleValue valueWithRawValue:NSImageNameIChatTheaterTemplate]; - theaterLayer.iconScale = [MGLStyleValue valueWithRawValue:@2]; - theaterLayer.iconColor = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeExponential cameraStops:@{ - @16.0: [MGLStyleValue valueWithRawValue:[NSColor redColor]], - @18.0: [MGLStyleValue valueWithRawValue:[NSColor yellowColor]], - @20.0: [MGLStyleValue valueWithRawValue:[NSColor blackColor]], - } options:nil]; - [self.mapView.style addLayer:theaterLayer]; + MGLSymbolStyleLayer *theaterLayer = [[MGLSymbolStyleLayer alloc] initWithIdentifier:@"theaters" source:streetsSource]; + theaterLayer.sourceLayerIdentifier = @"poi_label"; + theaterLayer.predicate = [NSPredicate predicateWithFormat:@"maki == 'theatre'"]; + theaterLayer.iconImageName = [MGLStyleValue valueWithRawValue:NSImageNameIChatTheaterTemplate]; + theaterLayer.iconScale = [MGLStyleValue valueWithRawValue:@2]; + theaterLayer.iconColor = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeExponential cameraStops:@{ + @16.0: [MGLStyleValue valueWithRawValue:[NSColor redColor]], + @18.0: [MGLStyleValue valueWithRawValue:[NSColor yellowColor]], + @20.0: [MGLStyleValue valueWithRawValue:[NSColor blackColor]], + } options:nil]; + [self.mapView.style addLayer:theaterLayer]; + } + + MGLImage *image = [[NSBundle bundleForClass:[self class]] imageForResource:@"RadarImage"]; + MGLCoordinateQuad quad = { {46.437, -80.425}, + {46.437, -71.516}, + {37.936, -71.516}, + {37.936, -80.425} }; + MGLImageSource *imageSource = [[MGLImageSource alloc] initWithIdentifier:@"radar-source" coordinates:quad image:image]; + [self.mapView.style addSource:imageSource]; + + MGLRasterStyleLayer * imageLayer = [[MGLRasterStyleLayer alloc] initWithIdentifier:@"radar-layer" source:imageSource]; + [self.mapView.style addLayer:imageLayer]; } - (IBAction)dropPin:(NSMenuItem *)sender { @@ -937,6 +982,9 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio if (menuItem.action == @selector(drawAnimatedAnnotation:)) { return !_isShowingAnimatedAnnotation; } + if (menuItem.action == @selector(addAnimatedImageSource:)) { + return YES; + } if (menuItem.action == @selector(insertCustomStyleLayer:)) { return ![self.mapView.style layerWithIdentifier:@"mbx-custom"]; } diff --git a/platform/macos/macos.xcodeproj/project.pbxproj b/platform/macos/macos.xcodeproj/project.pbxproj index 564d81afb2..248f913a35 100644 --- a/platform/macos/macos.xcodeproj/project.pbxproj +++ b/platform/macos/macos.xcodeproj/project.pbxproj @@ -7,6 +7,9 @@ objects = { /* Begin PBXBuildFile section */ + 0721493F1EE200E900085505 /* MGLImageSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 07A019EB1ED662D800ACD43E /* MGLImageSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 07A019EF1ED665CD00ACD43E /* MGLImageSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 07A019EC1ED662D800ACD43E /* MGLImageSource.mm */; }; + 07BA4CAC1EE21887004528F5 /* MGLImageSourceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 07BA4CAB1EE21887004528F5 /* MGLImageSourceTests.m */; }; 1753ED401E53CE6100A9FD90 /* MGLConversion.h in Headers */ = {isa = PBXBuildFile; fileRef = 1753ED3F1E53CE5200A9FD90 /* MGLConversion.h */; }; 1F7454A31ECFB00300021D39 /* MGLLight_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F7454A01ECFB00300021D39 /* MGLLight_Private.h */; }; 1F7454A41ECFB00300021D39 /* MGLLight.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F7454A11ECFB00300021D39 /* MGLLight.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -271,6 +274,10 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 07A019EA1ED662D800ACD43E /* MGLImageSource_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLImageSource_Private.h; sourceTree = "<group>"; }; + 07A019EB1ED662D800ACD43E /* MGLImageSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLImageSource.h; sourceTree = "<group>"; }; + 07A019EC1ED662D800ACD43E /* MGLImageSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLImageSource.mm; sourceTree = "<group>"; }; + 07BA4CAB1EE21887004528F5 /* MGLImageSourceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLImageSourceTests.m; sourceTree = "<group>"; }; 1753ED3F1E53CE5200A9FD90 /* MGLConversion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLConversion.h; sourceTree = "<group>"; }; 1F7454A01ECFB00300021D39 /* MGLLight_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLLight_Private.h; sourceTree = "<group>"; }; 1F7454A11ECFB00300021D39 /* MGLLight.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLLight.h; sourceTree = "<group>"; }; @@ -664,6 +671,9 @@ DA8F25951D51CAC70010E6B5 /* MGLVectorSource.h */, DA7DC9801DED5F5C0027472F /* MGLVectorSource_Private.h */, DA8F25961D51CAC70010E6B5 /* MGLVectorSource.mm */, + 07A019EA1ED662D800ACD43E /* MGLImageSource_Private.h */, + 07A019EB1ED662D800ACD43E /* MGLImageSource.h */, + 07A019EC1ED662D800ACD43E /* MGLImageSource.mm */, ); name = Sources; sourceTree = "<group>"; @@ -774,6 +784,7 @@ DA87A9961DC9D88400810D09 /* MGLShapeSourceTests.mm */, DA87A9971DC9D88400810D09 /* MGLTileSetTests.mm */, 920A3E581E6F859D00C16EFC /* MGLSourceQueryTests.m */, + 07BA4CAB1EE21887004528F5 /* MGLImageSourceTests.m */, ); name = Sources; sourceTree = "<group>"; @@ -1166,6 +1177,7 @@ DAE6C3B41CC31EF300DB3429 /* MGLCompassCell.h in Headers */, DA87A99C1DC9D8DD00810D09 /* MGLShapeSource_Private.h in Headers */, 3537CA741D3F93A600380318 /* MGLStyle_Private.h in Headers */, + 0721493F1EE200E900085505 /* MGLImageSource.h in Headers */, DA8F259A1D51CAD00010E6B5 /* MGLSource_Private.h in Headers */, DA8F25931D51CA750010E6B5 /* MGLSymbolStyleLayer.h in Headers */, DAE6C3B91CC31EF300DB3429 /* MGLOpenGLLayer.h in Headers */, @@ -1386,6 +1398,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 07A019EF1ED665CD00ACD43E /* MGLImageSource.mm in Sources */, 40ABDB561DB0022100372083 /* NSImage+MGLAdditions.mm in Sources */, DAE6C3901CC31E2A00DB3429 /* MGLPointAnnotation.mm in Sources */, DAE6C3981CC31E2A00DB3429 /* NSBundle+MGLAdditions.m in Sources */, @@ -1478,6 +1491,7 @@ DA87A9A71DCACC5000810D09 /* MGLBackgroundStyleLayerTests.mm in Sources */, DAA999011E9F5EC5002E6EA6 /* MGLFillExtrusionStyleLayerTests.mm in Sources */, DA29875A1E1A4290002299F5 /* MGLDocumentationExampleTests.swift in Sources */, + 07BA4CAC1EE21887004528F5 /* MGLImageSourceTests.m in Sources */, DAE6C3D31CC34C9900DB3429 /* MGLOfflinePackTests.m in Sources */, DA87A9A51DCACC5000810D09 /* MGLLineStyleLayerTests.mm in Sources */, DA87A9A31DCACC5000810D09 /* MGLRasterStyleLayerTests.mm in Sources */, diff --git a/platform/macos/src/Mapbox.h b/platform/macos/src/Mapbox.h index 0f47dace70..e4ad258b6e 100644 --- a/platform/macos/src/Mapbox.h +++ b/platform/macos/src/Mapbox.h @@ -50,6 +50,7 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[]; #import "MGLVectorSource.h" #import "MGLShapeSource.h" #import "MGLRasterSource.h" +#import "MGLImageSource.h" #import "MGLTilePyramidOfflineRegion.h" #import "MGLTypes.h" #import "NSValue+MGLAdditions.h" diff --git a/platform/macos/src/NSImage+MGLAdditions.h b/platform/macos/src/NSImage+MGLAdditions.h index 1bcec00e8b..0e57784bc5 100644 --- a/platform/macos/src/NSImage+MGLAdditions.h +++ b/platform/macos/src/NSImage+MGLAdditions.h @@ -12,6 +12,8 @@ NS_ASSUME_NONNULL_BEGIN - (std::unique_ptr<mbgl::style::Image>)mgl_styleImageWithIdentifier:(NSString *)identifier; +- (mbgl::PremultipliedImage) mgl_PremultipliedImage; + @end NS_ASSUME_NONNULL_END diff --git a/platform/macos/src/NSImage+MGLAdditions.mm b/platform/macos/src/NSImage+MGLAdditions.mm index ecd8aabe15..e4ef38eea5 100644 --- a/platform/macos/src/NSImage+MGLAdditions.mm +++ b/platform/macos/src/NSImage+MGLAdditions.mm @@ -33,6 +33,15 @@ } - (std::unique_ptr<mbgl::style::Image>)mgl_styleImageWithIdentifier:(NSString *)identifier { + mbgl::PremultipliedImage cPremultipliedImage = self.mgl_PremultipliedImage; + auto imageWidth = cPremultipliedImage.size.width; + return std::make_unique<mbgl::style::Image>([identifier UTF8String], + std::move(cPremultipliedImage), + (float)(imageWidth / self.size.width), + [self isTemplate]); +} + +- (mbgl::PremultipliedImage)mgl_PremultipliedImage { // Create a bitmap image representation from the image, respecting backing // scale factor and any resizing done on the image at runtime. // http://www.cocoabuilder.com/archive/cocoa/82430-nsimage-getting-raw-bitmap-data.html#82431 @@ -42,10 +51,6 @@ mbgl::PremultipliedImage cPremultipliedImage({ static_cast<uint32_t>(rep.pixelsWide), static_cast<uint32_t>(rep.pixelsHigh) }); std::copy(rep.bitmapData, rep.bitmapData + cPremultipliedImage.bytes(), cPremultipliedImage.data.get()); - return std::make_unique<mbgl::style::Image>([identifier UTF8String], - std::move(cPremultipliedImage), - (float)(rep.pixelsWide / self.size.width), - [self isTemplate]); + return cPremultipliedImage; } - @end |