diff options
Diffstat (limited to 'platform/darwin')
30 files changed, 1064 insertions, 223 deletions
diff --git a/platform/darwin/docs/guides/Tile URL Templates.md.ejs b/platform/darwin/docs/guides/Tile URL Templates.md.ejs new file mode 100644 index 0000000000..78fb297138 --- /dev/null +++ b/platform/darwin/docs/guides/Tile URL Templates.md.ejs @@ -0,0 +1,109 @@ +<% + const os = locals.os; + const iOS = os === 'iOS'; + //const macOS = os === 'macOS'; + //const cocoaPrefix = iOS ? 'UI' : 'NS'; +-%> +<!-- + This file is generated. + Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. +--> +# Tile URL Templates + +`MGLTileSource` objects, specifically `MGLRasterSource` and `MGLVectorSource` +objects, can be created using an initializer that accepts an array of tile URL +templates. Tile URL templates are strings that specify the URLs of the vector +tiles or raster tile images to load. A template resembles an absolute URL, but +with any number of placeholder strings that the source evaluates based on the +tile it needs to load. For example: + +* `http://www.example.com/tiles/{z}/{x}/{y}.pbf` could be + evaluated as `http://www.example.com/tiles/14/6/9.pbf`. +* `http://www.example.com/tiles/{z}/{x}/{y}{ratio}.png` could be + evaluated as `http://www.example.com/tiles/14/6/9@2x.png`. + +Tile URL templates are also used to define tilesets in TileJSON manifests or +[`raster`](https://www.mapbox.com/mapbox-gl-js/style-spec/#sources-raster-tiles) +and +[`vector`](https://www.mapbox.com/mapbox-gl-js/style-spec/#sources-vector-tiles) +sources in style JSON files. See the +[TileJSON specification](https://github.com/mapbox/tilejson-spec/tree/master/2.2.0) +for information about tile URL templates in the context of a TileJSON or style +JSON file. + +Tile sources support the following placeholder strings in tile URL templates, +all of which are optional: + +<table> +<thead> +<tr><th>Placeholder string</th><th>Description</th></tr> +</thead> +<tbody> +<tr> + <td><code>{x}</code></td> + <td>The index of the tile along the map’s x axis according to Spherical + Mercator projection. If the value is 0, the tile’s left edge corresponds + to the 180th meridian west. If the value is 2<sup><var>z</var></sup>−1, + the tile’s right edge corresponds to the 180th meridian east.</td> +</tr> +<tr> + <td><code>{y}</code></td> + <td>The index of the tile along the map’s y axis according to Spherical + Mercator projection. If the value is 0, the tile’s tile edge corresponds + to arctan(sinh(π)), or approximately 85.0511 degrees north. If the value + is 2<sup><var>z</var></sup>−1, the tile’s bottom edge corresponds to + −arctan(sinh(π)), or approximately 85.0511 degrees south. The y axis is + inverted if the <code>options</code> parameter contains + <code>MGLTileSourceOptionTileCoordinateSystem</code> with a value of + <code>MGLTileCoordinateSystemTMS</code>.</td> +</tr> +<tr> + <td><code>{z}</code></td> + <td>The tile’s zoom level. At zoom level 0, each tile covers the entire + world map; at zoom level 1, it covers ¼ of the world; at zoom level 2, + <sup>1</sup>⁄<sub>16</sub> of the world, and so on. For tiles loaded by + a <code>MGLRasterSource</code> object, whether the tile zoom level + matches the map’s current zoom level depends on the value of the + source’s tile size as specified in the + <code>MGLTileSourceOptionTileSize</code> key of the <code>options</code> + parameter.</td> +</tr> +<tr> + <td><code>{bbox-epsg-3857}</code></td> + <td>The tile’s bounding box, expressed as a comma-separated list of the + tile’s western, southern, eastern, and northern extents according to + Spherical Mercator (EPSG:3857) projection. The bounding box is typically + used with map services conforming to the + <a href="http://www.opengeospatial.org/standards/wms">Web Map Service</a> + protocol.</td> +</tr> +<tr> + <td><code>{quadkey}</code></td> + <td>A quadkey indicating both the tile’s location and its zoom level. The + quadkey is typically used with + <a href="https://msdn.microsoft.com/en-us/library/bb259689.aspx">Bing Maps</a>. + </td> +</tr> +<tr> + <td><code>{ratio}</code></td> + <td>A suffix indicating the resolution of the tile image. The suffix is the + empty string for standard resolution displays and <code>@2x</code> for +<% if (iOS) { -%> + Retina displays, including displays for which <code>UIScreen.scale</code> + is 3. +<% } else { -%> + Retina displays. +<% } -%> + </td> +</tr> +<tr> + <td><code>{prefix}</code></td> + <td>Two hexadecimal digits chosen such that each visible tile has a + different prefix. The prefix is typically used for domain sharding.</td> +</tr> +</tbody> +</table> + +For more information about the `{x}`, `{y}`, and `{z}` placeholder strings, +consult the +[OpenStreetMap Wiki](https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames). diff --git a/platform/darwin/scripts/generate-style-code.js b/platform/darwin/scripts/generate-style-code.js index 330a8f1d03..7efc8d441c 100644 --- a/platform/darwin/scripts/generate-style-code.js +++ b/platform/darwin/scripts/generate-style-code.js @@ -522,8 +522,9 @@ global.setSourceLayer = function() { const layerH = ejs.compile(fs.readFileSync('platform/darwin/src/MGLStyleLayer.h.ejs', 'utf8'), { strict: true }); const layerM = ejs.compile(fs.readFileSync('platform/darwin/src/MGLStyleLayer.mm.ejs', 'utf8'), { strict: true}); const testLayers = ejs.compile(fs.readFileSync('platform/darwin/test/MGLStyleLayerTests.mm.ejs', 'utf8'), { strict: true}); -const guideMD = ejs.compile(fs.readFileSync('platform/darwin/docs/guides/For Style Authors.md.ejs', 'utf8'), { strict: true }); +const forStyleAuthorsMD = ejs.compile(fs.readFileSync('platform/darwin/docs/guides/For Style Authors.md.ejs', 'utf8'), { strict: true }); const ddsGuideMD = ejs.compile(fs.readFileSync('platform/darwin/docs/guides/Using Style Functions at Runtime.md.ejs', 'utf8'), { strict: true }); +const templatesMD = ejs.compile(fs.readFileSync('platform/darwin/docs/guides/Tile URL Templates.md.ejs', 'utf8'), { strict: true }); const layers = _(spec.layer.type.values).map((value, layerType) => { const layoutProperties = Object.keys(spec[`layout_${layerType}`]).reduce((memo, name) => { @@ -635,12 +636,12 @@ global.guideExample = function (guide, exampleId, os) { return '```swift\n' + example + '\n```'; }; -fs.writeFileSync(`platform/ios/docs/guides/For Style Authors.md`, guideMD({ +fs.writeFileSync(`platform/ios/docs/guides/For Style Authors.md`, forStyleAuthorsMD({ os: 'iOS', renamedProperties: renamedPropertiesByLayerType, layers: layers, })); -fs.writeFileSync(`platform/macos/docs/guides/For Style Authors.md`, guideMD({ +fs.writeFileSync(`platform/macos/docs/guides/For Style Authors.md`, forStyleAuthorsMD({ os: 'macOS', renamedProperties: renamedPropertiesByLayerType, layers: layers, @@ -651,3 +652,9 @@ fs.writeFileSync(`platform/ios/docs/guides/Using Style Functions at Runtime.md`, fs.writeFileSync(`platform/macos/docs/guides/Using Style Functions at Runtime.md`, ddsGuideMD({ os: 'macOS', })); +fs.writeFileSync(`platform/ios/docs/guides/Tile URL Templates.md`, templatesMD({ + os: 'iOS', +})); +fs.writeFileSync(`platform/macos/docs/guides/Tile URL Templates.md`, templatesMD({ + os: 'macOS', +})); diff --git a/platform/darwin/src/MGLAttributionInfo.mm b/platform/darwin/src/MGLAttributionInfo.mm index 7583244491..770aeb25e7 100644 --- a/platform/darwin/src/MGLAttributionInfo.mm +++ b/platform/darwin/src/MGLAttributionInfo.mm @@ -6,8 +6,10 @@ #import <Cocoa/Cocoa.h> #endif +#import "MGLAccountManager.h" #import "MGLMapCamera.h" #import "NSArray+MGLAdditions.h" +#import "NSBundle+MGLAdditions.h" #import "NSString+MGLAdditions.h" #include <string> @@ -126,13 +128,34 @@ } - (nullable NSURL *)feedbackURLAtCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate zoomLevel:(double)zoomLevel { + return [self feedbackURLForStyleURL:nil atCenterCoordinate:centerCoordinate zoomLevel:zoomLevel direction:0 pitch:0]; +} + +- (nullable NSURL *)feedbackURLForStyleURL:(nullable NSURL *)styleURL atCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate zoomLevel:(double)zoomLevel direction:(CLLocationDirection)direction pitch:(CGFloat)pitch { if (!self.feedbackLink) { return nil; } - - NSURLComponents *components = [NSURLComponents componentsWithURL:self.URL resolvingAgainstBaseURL:NO]; - components.fragment = [NSString stringWithFormat:@"/%.5f/%.5f/%i", - centerCoordinate.longitude, centerCoordinate.latitude, (int)round(zoomLevel + 1)]; + + NSURLComponents *components = [NSURLComponents componentsWithString:@"https://www.mapbox.com/feedback/"]; + components.fragment = [NSString stringWithFormat:@"/%.5f/%.5f/%.2f/%.1f/%i", + centerCoordinate.longitude, centerCoordinate.latitude, zoomLevel, + direction, (int)round(pitch)]; + + NSURLQueryItem *referrerQueryItem = [NSURLQueryItem queryItemWithName:@"referrer" + value:[NSBundle mgl_applicationBundleIdentifier]]; + NSMutableArray<NSURLQueryItem *> *queryItems = [NSMutableArray arrayWithObject:referrerQueryItem]; + if ([styleURL.scheme isEqualToString:@"mapbox"] && [styleURL.host isEqualToString:@"styles"]) { + NSArray<NSString *> *stylePathComponents = styleURL.pathComponents; + if (stylePathComponents.count >= 3) { + [queryItems addObjectsFromArray:@[ + [NSURLQueryItem queryItemWithName:@"owner" value:stylePathComponents[1]], + [NSURLQueryItem queryItemWithName:@"id" value:stylePathComponents[2]], + [NSURLQueryItem queryItemWithName:@"access_token" value:[MGLAccountManager accessToken]], + ]]; + } + } + components.queryItems = queryItems; + return components.URL; } diff --git a/platform/darwin/src/MGLAttributionInfo_Private.h b/platform/darwin/src/MGLAttributionInfo_Private.h index 08bc6bfc4d..c639752ac3 100644 --- a/platform/darwin/src/MGLAttributionInfo_Private.h +++ b/platform/darwin/src/MGLAttributionInfo_Private.h @@ -20,6 +20,24 @@ NS_ASSUME_NONNULL_BEGIN + (NSAttributedString *)attributedStringForAttributionInfos:(NS_ARRAY_OF(MGLAttributionInfo *) *)attributionInfos; +/** + Returns a copy of the `URL` property modified to account for the given style + URL, center coordinate, and zoom level. + + @param styleURL The map’s style URL. + @param centerCoordinate The map’s center coordinate. + @param zoomLevel The map’s zoom level. See the `MGLMapView.zoomLevel` property + for more information. + @param direction The heading of the map, measured in degrees clockwise from + true north. + @param pitch Pitch toward the horizon measured in degrees, with 0 degrees + resulting in a two-dimensional map. + @return A modified URL containing a fragment that points to the specified + viewport. If the `feedbackLink` property is set to `NO`, this method returns + `nil`. + */ +- (nullable NSURL *)feedbackURLForStyleURL:(nullable NSURL *)styleURL atCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate zoomLevel:(double)zoomLevel direction:(CLLocationDirection)direction pitch:(CGFloat)pitch; + @end @interface NSMutableArray (MGLAttributionInfoAdditions) diff --git a/platform/darwin/src/MGLFillExtrusionStyleLayer.h b/platform/darwin/src/MGLFillExtrusionStyleLayer.h index 84f6bedde4..c4fb9aa77e 100644 --- a/platform/darwin/src/MGLFillExtrusionStyleLayer.h +++ b/platform/darwin/src/MGLFillExtrusionStyleLayer.h @@ -46,7 +46,7 @@ typedef NS_ENUM(NSUInteger, MGLFillExtrusionTranslationAnchor) { layer.sourceLayerIdentifier = "building" layer.fillExtrusionHeight = MGLStyleValue(interpolationMode: .identity, sourceStops: nil, attributeName: "height", options: nil) layer.fillExtrusionBase = MGLStyleValue(interpolationMode: .identity, sourceStops: nil, attributeName: "min_height", options: nil) - layer.predicate = NSPredicate(format: "extrude == TRUE") + layer.predicate = NSPredicate(format: "extrude == 'true'") mapView.style?.addLayer(layer) ``` */ diff --git a/platform/darwin/src/MGLGeometry_Private.h b/platform/darwin/src/MGLGeometry_Private.h index 3e029e8ee0..7ad8314a79 100644 --- a/platform/darwin/src/MGLGeometry_Private.h +++ b/platform/darwin/src/MGLGeometry_Private.h @@ -8,6 +8,26 @@ #import <mbgl/util/geo.hpp> #import <mbgl/util/geometry.hpp> +typedef double MGLLocationRadians; +typedef double MGLRadianDistance; +typedef double MGLRadianDirection; + +/** Defines the coordinate by a `MGLRadianCoordinate2D`. */ +typedef struct MGLRadianCoordinate2D { + MGLLocationRadians latitude; + MGLLocationRadians longitude; +} MGLRadianCoordinate2D; + +/** + Creates a new `MGLRadianCoordinate2D` from the given latitudinal and longitudinal. + */ +NS_INLINE MGLRadianCoordinate2D MGLRadianCoordinate2DMake(MGLLocationRadians latitude, MGLLocationRadians longitude) { + MGLRadianCoordinate2D radianCoordinate; + radianCoordinate.latitude = latitude; + radianCoordinate.longitude = longitude; + return radianCoordinate; +} + /// Returns the smallest rectangle that contains both the given rectangle and /// the given point. CGRect MGLExtendRect(CGRect rect, CGPoint point); @@ -18,6 +38,10 @@ NS_INLINE mbgl::Point<double> MGLPointFromLocationCoordinate2D(CLLocationCoordin return mbgl::Point<double>(coordinate.longitude, coordinate.latitude); } +NS_INLINE CLLocationCoordinate2D MGLLocationCoordinate2DFromPoint(mbgl::Point<double> point) { + return CLLocationCoordinate2DMake(point.y, point.x); +} + NS_INLINE CLLocationCoordinate2D MGLLocationCoordinate2DFromLatLng(mbgl::LatLng latLng) { return CLLocationCoordinate2DMake(latLng.latitude(), latLng.longitude()); } @@ -59,3 +83,43 @@ CLLocationDistance MGLAltitudeForZoomLevel(double zoomLevel, CGFloat pitch, CLLo @param size The size of the viewport. @return A zero-based zoom level. */ double MGLZoomLevelForAltitude(CLLocationDistance altitude, CGFloat pitch, CLLocationDegrees latitude, CGSize size); + +/** Returns MGLRadianCoordinate2D, converted from CLLocationCoordinate2D. */ +NS_INLINE MGLRadianCoordinate2D MGLRadianCoordinateFromLocationCoordinate(CLLocationCoordinate2D locationCoordinate) { + return MGLRadianCoordinate2DMake(MGLRadiansFromDegrees(locationCoordinate.latitude), + MGLRadiansFromDegrees(locationCoordinate.longitude)); +} + +/* + Returns the distance in radians given two coordinates. + */ +NS_INLINE MGLRadianDistance MGLDistanceBetweenRadianCoordinates(MGLRadianCoordinate2D from, MGLRadianCoordinate2D to) +{ + double a = pow(sin((to.latitude - from.latitude) / 2), 2) + + pow(sin((to.longitude - from.longitude) / 2), 2) * cos(from.latitude) * cos(to.latitude); + + return 2 * atan2(sqrt(a), sqrt(1 - a)); +} + +/* + Returns direction in radians given two coordinates. + */ +NS_INLINE MGLRadianDirection MGLRadianCoordinatesDirection(MGLRadianCoordinate2D from, MGLRadianCoordinate2D to) { + double a = sin(to.longitude - from.longitude) * cos(to.latitude); + double b = cos(from.latitude) * sin(to.latitude) + - sin(from.latitude) * cos(to.latitude) * cos(to.longitude - from.longitude); + return atan2(a, b); +} + +/* + Returns coordinate at a given distance and direction away from coordinate. + */ +NS_INLINE MGLRadianCoordinate2D MGLRadianCoordinateAtDistanceFacingDirection(MGLRadianCoordinate2D coordinate, + MGLRadianDistance distance, + MGLRadianDirection direction) { + double otherLatitude = asin(sin(coordinate.latitude) * cos(distance) + + cos(coordinate.latitude) * sin(distance) * cos(direction)); + double otherLongitude = coordinate.longitude + atan2(sin(direction) * sin(distance) * cos(coordinate.latitude), + cos(distance) - sin(coordinate.latitude) * sin(otherLatitude)); + return MGLRadianCoordinate2DMake(otherLatitude, otherLongitude); +} diff --git a/platform/darwin/src/MGLLight.h b/platform/darwin/src/MGLLight.h new file mode 100644 index 0000000000..ef9811bd33 --- /dev/null +++ b/platform/darwin/src/MGLLight.h @@ -0,0 +1,125 @@ +#import <CoreLocation/CoreLocation.h> + +#import "MGLFoundation.h" +#import "MGLStyleValue.h" + +NS_ASSUME_NONNULL_BEGIN + + +/** Options to specify extruded geometries are lit relative to the map or viewport. */ +typedef NS_ENUM(NSUInteger, MGLLightAnchor) { + /** The position of the light source is aligned to the rotation of the map. */ + MGLLightAnchorMap, + /** The position of the light source is aligned to the rotation of the viewport. */ + MGLLightAnchorViewport +}; + +/** + A structure containing information about the position of the light source + relative to lit geometries. + */ +typedef struct MGLSphericalPosition { + /** Distance from the center of the base of an object to its light. */ + CLLocationDistance radial; + /** Position of the light relative to 0° (0° when `MGLLight.anchor` is set to viewport corresponds + to the top of the viewport, or 0° when `MGLLight.anchor` is set to map corresponds to due north, + and degrees proceed clockwise). */ + CLLocationDirection azimuthal; + /** Indicates the height of the light (from 0°, directly above, to 180°, directly below). */ + CLLocationDirection polar; +} MGLSphericalPosition; + +/** + Creates a new `MGLSphericalPosition` from the given radial, azimuthal, polar. + + @param radial The radial coordinate. + @param azimuthal The azimuthal angle. + @param polar The polar angle. + + @return Returns a `MGLSphericalPosition` struct containing the position attributes. + */ +NS_INLINE MGLSphericalPosition MGLSphericalPositionMake(CLLocationDistance radial, CLLocationDirection azimuthal, CLLocationDirection polar) { + MGLSphericalPosition position; + position.radial = radial; + position.azimuthal = azimuthal; + position.polar = polar; + + return position; +} + +/** + An `MGLLight` object represents the light source for extruded geometries in `MGLStyle`. + */ +MGL_EXPORT +@interface MGLLight : NSObject + +/** + `anchor` Whether extruded geometries are lit relative to the map or viewport. + + This property corresponds to the <a + href="https://www.mapbox.com/mapbox-gl-js/style-spec/#light-anchor"><code>anchor</code></a> + light property in the Mapbox Style Specification. + */ +@property (nonatomic) MGLStyleValue<NSValue *> *anchor; + +/** + Values describing animated transitions to `anchor` property. + */ +@property (nonatomic) MGLTransition anchorTransition; + + +/** + Position of the light source relative to lit (extruded) geometries. + + This property corresponds to the <a + href="https://www.mapbox.com/mapbox-gl-js/style-spec/#light-position"><code>position</code></a> + light property in the Mapbox Style Specification. + */ +@property (nonatomic) MGLStyleValue<NSValue *> *position; + +/** + Values describing animated transitions to `position` property. + */ +@property (nonatomic) MGLTransition positionTransiton; + + +#if TARGET_OS_IPHONE +/** + Color tint for lighting extruded geometries. + + This property corresponds to the <a + href="https://www.mapbox.com/mapbox-gl-js/style-spec/#light-color"><code>color</code></a> + light property in the Mapbox Style Specification. + */ +@property (nonatomic) MGLStyleValue<UIColor *> *color; +#else + +/** + Color tint for lighting extruded geometries. + */ +@property (nonatomic) MGLStyleValue<NSColor *> *color; +#endif + +/** + Values describing animated transitions to `color` property. + */ +@property (nonatomic) MGLTransition colorTransiton; + + +/** + Intensity of lighting (on a scale from 0 to 1). Higher numbers will present as more extreme contrast. + + This property corresponds to the <a + href="https://www.mapbox.com/mapbox-gl-js/style-spec/#light-intensity"><code>intensity</code></a> + light property in the Mapbox Style Specification. + */ +@property(nonatomic) MGLStyleValue<NSNumber *> *intensity; + +/** + Values describing animated transitions to `intensity` property. + */ +@property (nonatomic) MGLTransition intensityTransition; + +@end + +NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/MGLLight.mm b/platform/darwin/src/MGLLight.mm new file mode 100644 index 0000000000..262fad3b07 --- /dev/null +++ b/platform/darwin/src/MGLLight.mm @@ -0,0 +1,113 @@ +#import "MGLLight.h" + +#import "MGLTypes.h" +#import "NSDate+MGLAdditions.h" +#import "MGLStyleValue_Private.h" +#import "NSValue+MGLAdditions.h" + +#import <mbgl/style/light.hpp> +#import <mbgl/style/types.hpp> + +namespace mbgl { + + MBGL_DEFINE_ENUM(MGLLightAnchor, { + { MGLLightAnchorMap, "map" }, + { MGLLightAnchorViewport, "viewport" }, + }); + +} + +NS_INLINE MGLTransition MGLTransitionFromOptions(const mbgl::style::TransitionOptions& options) { + MGLTransition transition; + transition.duration = MGLTimeIntervalFromDuration(options.duration.value_or(mbgl::Duration::zero())); + transition.delay = MGLTimeIntervalFromDuration(options.delay.value_or(mbgl::Duration::zero())); + + return transition; +} + +NS_INLINE mbgl::style::TransitionOptions MGLOptionsFromTransition(MGLTransition transition) { + mbgl::style::TransitionOptions options { { MGLDurationFromTimeInterval(transition.duration) }, { MGLDurationFromTimeInterval(transition.delay) } }; + return options; +} + +@interface MGLLight() + +@end + +@implementation MGLLight + +- (instancetype)initWithMBGLLight:(const mbgl::style::Light *)mbglLight +{ + if (self = [super init]) { + auto anchor = mbglLight->getAnchor(); + MGLStyleValue<NSValue *> *anchorStyleValue; + if (anchor.isUndefined()) { + mbgl::style::PropertyValue<mbgl::style::LightAnchorType> defaultAnchor = mbglLight->getDefaultAnchor(); + anchorStyleValue = MGLStyleValueTransformer<mbgl::style::LightAnchorType, NSValue *, mbgl::style::LightAnchorType, MGLLightAnchor>().toEnumStyleValue(defaultAnchor); + } else { + anchorStyleValue = MGLStyleValueTransformer<mbgl::style::LightAnchorType, NSValue *, mbgl::style::LightAnchorType, MGLLightAnchor>().toEnumStyleValue(anchor); + } + + _anchor = anchorStyleValue; + + _anchorTransition = MGLTransitionFromOptions(mbglLight->getAnchorTransition()); + + auto positionValue = mbglLight->getPosition(); + if (positionValue.isUndefined()) { + _position = MGLStyleValueTransformer<mbgl::style::Position, NSValue *>().toStyleValue(mbglLight->getDefaultPosition()); + } else { + _position = MGLStyleValueTransformer<mbgl::style::Position, NSValue *>().toStyleValue(positionValue); + } + + _positionTransiton = MGLTransitionFromOptions(mbglLight->getPositionTransition()); + + auto colorValue = mbglLight->getColor(); + if (colorValue.isUndefined()) { + _color = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toStyleValue(mbglLight->getDefaultColor()); + } else { + _color = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toStyleValue(colorValue); + } + + _colorTransiton = MGLTransitionFromOptions(mbglLight->getColorTransition()); + + auto intensityValue = mbglLight->getIntensity(); + if (intensityValue.isUndefined()) { + _intensity = MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(mbglLight->getDefaultIntensity()); + } else { + _intensity = MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(intensityValue); + } + + _intensityTransition = MGLTransitionFromOptions(mbglLight->getIntensityTransition()); + } + + return self; +} + +- (mbgl::style::Light)mbglLight +{ + mbgl::style::Light mbglLight; + + auto anchor = MGLStyleValueTransformer<mbgl::style::LightAnchorType, NSValue *, mbgl::style::LightAnchorType, MGLLightAnchor>().toEnumPropertyValue(self.anchor); + mbglLight.setAnchor(anchor); + + mbglLight.setAnchorTransition(MGLOptionsFromTransition(self.anchorTransition)); + + auto position = MGLStyleValueTransformer<mbgl::style::Position, NSValue *>().toInterpolatablePropertyValue(self.position); + mbglLight.setPosition(position); + + mbglLight.setPositionTransition(MGLOptionsFromTransition(self.positionTransiton)); + + auto color = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toInterpolatablePropertyValue(self.color); + mbglLight.setColor(color); + + mbglLight.setColorTransition(MGLOptionsFromTransition(self.colorTransiton)); + + auto intensity = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(self.intensity); + mbglLight.setIntensity(intensity); + + mbglLight.setIntensityTransition(MGLOptionsFromTransition(self.intensityTransition)); + + return mbglLight; +} + +@end diff --git a/platform/darwin/src/MGLLight_Private.h b/platform/darwin/src/MGLLight_Private.h new file mode 100644 index 0000000000..dbc29c1eff --- /dev/null +++ b/platform/darwin/src/MGLLight_Private.h @@ -0,0 +1,23 @@ +#import <Foundation/Foundation.h> + +#import "MGLLight.h" + +namespace mbgl { + namespace style { + class Light; + } +} + +@interface MGLLight (Private) + +/** + Initializes and returns a `MGLLight` associated with a style's light. + */ +- (instancetype)initWithMBGLLight:(const mbgl::style::Light *)mbglLight; + +/** + Returns an `mbgl::style::Light` representation of the `MGLLight`. + */ +- (mbgl::style::Light)mbglLight; + +@end diff --git a/platform/darwin/src/MGLOfflineStorage.mm b/platform/darwin/src/MGLOfflineStorage.mm index 195ef3c36a..81774ad3cb 100644 --- a/platform/darwin/src/MGLOfflineStorage.mm +++ b/platform/darwin/src/MGLOfflineStorage.mm @@ -7,6 +7,7 @@ #import "MGLOfflinePack_Private.h" #import "MGLOfflineRegion_Private.h" #import "MGLTilePyramidOfflineRegion.h" +#import "NSBundle+MGLAdditions.h" #import "NSValue+MGLAdditions.h" #include <mbgl/util/string.hpp> @@ -132,7 +133,7 @@ NSString * const MGLOfflinePackMaximumCountUserInfoKey = MGLOfflinePackUserInfoK appropriateForURL:nil create:YES error:nil]; - NSString *bundleIdentifier = [self bundleIdentifier]; + NSString *bundleIdentifier = [NSBundle mgl_applicationBundleIdentifier]; if (!bundleIdentifier) { // There’s no main bundle identifier when running in a unit test bundle. bundleIdentifier = [NSBundle bundleForClass:self].bundleIdentifier; @@ -166,7 +167,7 @@ NSString * const MGLOfflinePackMaximumCountUserInfoKey = MGLOfflinePackUserInfoK NSString *legacyCachePath = [legacyPaths.firstObject stringByAppendingPathComponent:MGLOfflineStorageFileName3_2_0_beta_1]; #elif TARGET_OS_MAC // ~/Library/Caches/tld.app.bundle.id/offline.db - NSString *bundleIdentifier = [self bundleIdentifier]; + NSString *bundleIdentifier = [NSBundle mgl_applicationBundleIdentifier]; NSURL *legacyCacheDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSCachesDirectory inDomain:NSUserDomainMask appropriateForURL:nil @@ -219,15 +220,6 @@ NSString * const MGLOfflinePackMaximumCountUserInfoKey = MGLOfflinePackUserInfoK return self; } -+ (NSString *)bundleIdentifier { - NSString *bundleIdentifier = [NSBundle mainBundle].bundleIdentifier; - if (!bundleIdentifier) { - // There’s no main bundle identifier when running in a unit test bundle. - bundleIdentifier = [NSBundle bundleForClass:self].bundleIdentifier; - } - return bundleIdentifier; -} - - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; [[MGLNetworkConfiguration sharedManager] removeObserver:self forKeyPath:@"apiBaseURL"]; diff --git a/platform/darwin/src/MGLPolygon.mm b/platform/darwin/src/MGLPolygon.mm index ceafe873bf..d966ff13ce 100644 --- a/platform/darwin/src/MGLPolygon.mm +++ b/platform/darwin/src/MGLPolygon.mm @@ -6,6 +6,7 @@ #import "MGLPolygon+MGLAdditions.h" #import <mbgl/util/geojson.hpp> +#import <mapbox/polylabel.hpp> @implementation MGLPolygon @@ -54,6 +55,13 @@ return [super hash] + [[self geoJSONDictionary] hash]; } +- (CLLocationCoordinate2D)coordinate { + // pole of inaccessibility + auto poi = mapbox::polylabel([self polygon]); + + return MGLLocationCoordinate2DFromPoint(poi); +} + - (mbgl::LinearRing<double>)ring { NSUInteger count = self.pointCount; CLLocationCoordinate2D *coordinates = self.coordinates; @@ -155,11 +163,17 @@ return hash; } +- (CLLocationCoordinate2D)coordinate { + MGLPolygon *firstPolygon = self.polygons.firstObject; + + return firstPolygon.coordinate; +} + - (BOOL)intersectsOverlayBounds:(MGLCoordinateBounds)overlayBounds { return MGLCoordinateBoundsIntersectsCoordinateBounds(_overlayBounds, overlayBounds); } -- (mbgl::Geometry<double>)geometryObject { +- (mbgl::MultiPolygon<double>)multiPolygon { mbgl::MultiPolygon<double> multiPolygon; multiPolygon.reserve(self.polygons.count); for (MGLPolygon *polygon in self.polygons) { @@ -173,6 +187,10 @@ return multiPolygon; } +- (mbgl::Geometry<double>)geometryObject { + return [self multiPolygon]; +} + - (NSDictionary *)geoJSONDictionary { NSMutableArray *coordinates = [[NSMutableArray alloc] initWithCapacity:self.polygons.count]; for (MGLPolygonFeature *feature in self.polygons) { diff --git a/platform/darwin/src/MGLPolyline.mm b/platform/darwin/src/MGLPolyline.mm index ae4fbe61de..fd75dc2795 100644 --- a/platform/darwin/src/MGLPolyline.mm +++ b/platform/darwin/src/MGLPolyline.mm @@ -6,6 +6,7 @@ #import "MGLPolyline+MGLAdditions.h" #import <mbgl/util/geojson.hpp> +#import <mapbox/polylabel.hpp> @implementation MGLPolyline @@ -52,6 +53,61 @@ return self == other || ([other isKindOfClass:[MGLPolyline class]] && [super isEqual:other]); } +- (CLLocationCoordinate2D)coordinate { + NSUInteger count = self.pointCount; + NSAssert(count > 0, @"Polyline must have coordinates"); + + CLLocationCoordinate2D *coordinates = self.coordinates; + CLLocationDistance middle = [self length] / 2.0; + CLLocationDistance traveled = 0.0; + + if (count > 1 || middle > traveled) { + for (NSUInteger i = 0; i < count; i++) { + + MGLRadianCoordinate2D from = MGLRadianCoordinateFromLocationCoordinate(coordinates[i]); + MGLRadianCoordinate2D to = MGLRadianCoordinateFromLocationCoordinate(coordinates[i + 1]); + + if (traveled >= middle) { + double overshoot = middle - traveled; + if (overshoot == 0) { + return coordinates[i]; + } + to = MGLRadianCoordinateFromLocationCoordinate(coordinates[i - 1]); + CLLocationDirection direction = [self direction:from to:to] - 180; + MGLRadianCoordinate2D otherCoordinate = MGLRadianCoordinateAtDistanceFacingDirection(from, + overshoot/mbgl::util::EARTH_RADIUS_M, + MGLRadiansFromDegrees(direction)); + return CLLocationCoordinate2DMake(MGLDegreesFromRadians(otherCoordinate.latitude), + MGLDegreesFromRadians(otherCoordinate.longitude)); + } + + traveled += (MGLDistanceBetweenRadianCoordinates(from, to) * mbgl::util::EARTH_RADIUS_M); + + } + } + + return coordinates[count - 1]; +} + +- (CLLocationDistance)length +{ + CLLocationDistance length = 0.0; + + NSUInteger count = self.pointCount; + CLLocationCoordinate2D *coordinates = self.coordinates; + + for (NSUInteger i = 0; i < count - 1; i++) { + length += (MGLDistanceBetweenRadianCoordinates(MGLRadianCoordinateFromLocationCoordinate(coordinates[i]), MGLRadianCoordinateFromLocationCoordinate(coordinates[i + 1])) * mbgl::util::EARTH_RADIUS_M); + } + + return length; +} + +- (CLLocationDirection)direction:(MGLRadianCoordinate2D)from to:(MGLRadianCoordinate2D)to +{ + return MGLDegreesFromRadians(MGLRadianCoordinatesDirection(from, to)); +} + @end @interface MGLMultiPolyline () @@ -114,6 +170,15 @@ return hash; } +- (CLLocationCoordinate2D)coordinate { + MGLPolyline *polyline = self.polylines.firstObject; + CLLocationCoordinate2D *coordinates = polyline.coordinates; + NSAssert([polyline pointCount] > 0, @"Polyline must have coordinates"); + CLLocationCoordinate2D firstCoordinate = coordinates[0]; + + return firstCoordinate; +} + - (BOOL)intersectsOverlayBounds:(MGLCoordinateBounds)overlayBounds { return MGLCoordinateBoundsIntersectsCoordinateBounds(_overlayBounds, overlayBounds); } diff --git a/platform/darwin/src/MGLRasterSource.h b/platform/darwin/src/MGLRasterSource.h index 519784f4f1..4f4b7c96c3 100644 --- a/platform/darwin/src/MGLRasterSource.h +++ b/platform/darwin/src/MGLRasterSource.h @@ -108,96 +108,13 @@ MGL_EXPORT Returns a raster source initialized an identifier, tile URL templates, and options. + Tile URL templates are strings that specify the URLs of the raster tile images + to load. See the “<a href="../tile-url-templates.html">Tile URL Templates</a>” + guide for information about the format of a tile URL template. + After initializing and configuring the source, add it to a map view’s style using the `-[MGLStyle addSource:]` method. - #### Tile URL templates - - Tile URL templates are strings that specify the URLs of the tile images to - load. Each template resembles an absolute URL, but with any number of - placeholder strings that the source evaluates based on the tile it needs to - load. For example: - - <ul> - <li><code>http://www.example.com/tiles/{z}/{x}/{y}.pbf</code> could be - evaluated as <code>http://www.example.com/tiles/14/6/9.pbf</code>.</li> - <li><code>http://www.example.com/tiles/{z}/{x}/{y}{ratio}.png</code> could be - evaluated as <code>http://www.example.com/tiles/14/6/9@2x.png</code>.</li> - </ul> - - Tile sources support the following placeholder strings in tile URL templates, - all of which are optional: - - <table> - <thead> - <tr><th>Placeholder string</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><code>{x}</code></td> - <td>The index of the tile along the map’s x axis according to Spherical - Mercator projection. If the value is 0, the tile’s left edge corresponds - to the 180th meridian west. If the value is 2<sup><var>z</var></sup>−1, - the tile’s right edge corresponds to the 180th meridian east.</td> - </tr> - <tr> - <td><code>{y}</code></td> - <td>The index of the tile along the map’s y axis according to Spherical - Mercator projection. If the value is 0, the tile’s tile edge corresponds - to arctan(sinh(π)), or approximately 85.0511 degrees north. If the value - is 2<sup><var>z</var></sup>−1, the tile’s bottom edge corresponds to - −arctan(sinh(π)), or approximately 85.0511 degrees south. The y axis is - inverted if the <code>options</code> parameter contains - <code>MGLTileSourceOptionTileCoordinateSystem</code> with a value of - <code>MGLTileCoordinateSystemTMS</code>.</td> - </tr> - <tr> - <td><code>{z}</code></td> - <td>The tile’s zoom level. At zoom level 0, each tile covers the entire - world map; at zoom level 1, it covers ¼ of the world; at zoom level 2, - <sup>1</sup>⁄<sub>16</sub> of the world, and so on. For tiles loaded by - a <code>MGLRasterSource</code> object, whether the tile zoom level - matches the map’s current zoom level depends on the value of the - source’s tile size as specified in the - <code>MGLTileSourceOptionTileSize</code> key of the - <code>options</code> parameter.</td> - </tr> - <tr> - <td><code>{bbox-epsg-3857}</code></td> - <td>The tile’s bounding box, expressed as a comma-separated list of the - tile’s western, southern, eastern, and northern extents according to - Spherical Mercator (EPSG:3857) projection. The bounding box is typically - used with map services conforming to the - <a href="http://www.opengeospatial.org/standards/wms">Web Map Service</a> - protocol.</td> - </tr> - <tr> - <td><code>{quadkey}</code></td> - <td>A quadkey indicating both the tile’s location and its zoom level. The - quadkey is typically used with - <a href="https://msdn.microsoft.com/en-us/library/bb259689.aspx">Bing Maps</a>. - </td> - </tr> - <tr> - <td><code>{ratio}</code></td> - <td>A suffix indicating the resolution of the tile image. The suffix is the - empty string for standard resolution displays and <code>@2x</code> for - Retina displays, including displays for which - <code>NSScreen.backingScaleFactor</code> or <code>UIScreen.scale</code> - is 3.</td> - </tr> - <tr> - <td><code>{prefix}</code></td> - <td>Two hexadecimal digits chosen such that each visible tile has a - different prefix. The prefix is typically used for domain sharding.</td> - </tr> - </tbody> - </table> - - For more information about the `{x}`, `{y}`, and `{z}` placeholder strings, - consult the - <a href="https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames">OpenStreetMap Wiki</a>. - @param identifier A string that uniquely identifies the source in the style to which it is added. @param tileURLTemplates An array of tile URL template strings. Only the first diff --git a/platform/darwin/src/MGLStyle.h b/platform/darwin/src/MGLStyle.h index ccec863236..6fb4a6cc6b 100644 --- a/platform/darwin/src/MGLStyle.h +++ b/platform/darwin/src/MGLStyle.h @@ -6,6 +6,7 @@ #import "MGLTypes.h" @class MGLSource; +@class MGLLight; NS_ASSUME_NONNULL_BEGIN @@ -548,6 +549,14 @@ MGL_EXPORT */ - (void)removeImageForName:(NSString *)name; + +#pragma mark Managing the Style's Light + +/** + Provides global light source for the style. + */ +@property (nonatomic, strong) MGLLight *light; + @end NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/MGLStyle.mm b/platform/darwin/src/MGLStyle.mm index 5f26b4fed2..2e6f2bc29a 100644 --- a/platform/darwin/src/MGLStyle.mm +++ b/platform/darwin/src/MGLStyle.mm @@ -14,6 +14,7 @@ #import "MGLStyle_Private.h" #import "MGLStyleLayer_Private.h" #import "MGLSource_Private.h" +#import "MGLLight_Private.h" #import "NSDate+MGLAdditions.h" @@ -28,6 +29,7 @@ #include <mbgl/map/map.hpp> #include <mbgl/util/default_styles.hpp> #include <mbgl/style/image.hpp> +#include <mbgl/style/light.hpp> #include <mbgl/style/layers/fill_layer.hpp> #include <mbgl/style/layers/fill_extrusion_layer.hpp> #include <mbgl/style/layers/line_layer.hpp> @@ -556,6 +558,21 @@ static NSURL *MGLStyleURL_emerald; return transition; } +#pragma mark Style light + +- (void)setLight:(MGLLight *)light +{ + std::unique_ptr<mbgl::style::Light> mbglLight = std::make_unique<mbgl::style::Light>([light mbglLight]); + self.mapView.mbglMap->setLight(std::move(mbglLight)); +} + +- (MGLLight *)light +{ + auto mbglLight = self.mapView.mbglMap->getLight(); + MGLLight *light = [[MGLLight alloc] initWithMBGLLight:mbglLight]; + return light; +} + - (NSString *)description { return [NSString stringWithFormat:@"<%@: %p; name = %@, URL = %@>", diff --git a/platform/darwin/src/MGLStyleValue.h b/platform/darwin/src/MGLStyleValue.h index 70074c8a13..2bb3aca4f4 100644 --- a/platform/darwin/src/MGLStyleValue.h +++ b/platform/darwin/src/MGLStyleValue.h @@ -75,9 +75,9 @@ typedef NS_ENUM(NSUInteger, MGLInterpolationMode) { */ MGLInterpolationModeCategorical, /** - Values between two stops are not interpolated. Instead, values are set to their - input value. Use identity interpolation mode to show attribute values that can be - used as style values. + Values between two stops are not interpolated. Instead, for any given feature, the + style value matches a value in that feature’s attributes dictionary. Use identity + interpolation mode to show attribute values that can be used as style values. */ MGLInterpolationModeIdentity }; diff --git a/platform/darwin/src/MGLStyleValue.mm b/platform/darwin/src/MGLStyleValue.mm index 33b6babadf..4dd6b550d8 100644 --- a/platform/darwin/src/MGLStyleValue.mm +++ b/platform/darwin/src/MGLStyleValue.mm @@ -128,7 +128,7 @@ const MGLStyleFunctionOption MGLStyleFunctionOptionDefaultValue = @"MGLStyleFunc return {}; } - if (self == [super init]) { + if (self = [super init]) { self.interpolationMode = interpolationMode; self.stops = stops; @@ -181,7 +181,7 @@ const MGLStyleFunctionOption MGLStyleFunctionOptionDefaultValue = @"MGLStyleFunc } - (instancetype)initWithInterpolationMode:(MGLInterpolationMode)interpolationMode stops:(NSDictionary *)stops attributeName:(NSString *)attributeName options:(NSDictionary *)options { - if (self == [super init]) { + if (self = [super init]) { self.interpolationMode = interpolationMode; self.stops = stops; _attributeName = attributeName; @@ -251,7 +251,7 @@ const MGLStyleFunctionOption MGLStyleFunctionOptionDefaultValue = @"MGLStyleFunc } - (instancetype)initWithInterpolationMode:(MGLInterpolationMode)interpolationMode stops:(NSDictionary *)stops attributeName:(NSString *)attributeName options:(NSDictionary *)options { - if (self == [super init]) { + if (self = [super init]) { self.interpolationMode = interpolationMode; self.stops = stops; _attributeName = attributeName; diff --git a/platform/darwin/src/MGLStyleValue_Private.h b/platform/darwin/src/MGLStyleValue_Private.h index 263b54d7e5..2155c657bd 100644 --- a/platform/darwin/src/MGLStyleValue_Private.h +++ b/platform/darwin/src/MGLStyleValue_Private.h @@ -3,11 +3,13 @@ #import "MGLStyleValue.h" #import "NSValue+MGLStyleAttributeAdditions.h" +#import "NSValue+MGLAdditions.h" #import "MGLTypes.h" #import "MGLConversion.h" #include <mbgl/style/conversion/data_driven_property_value.hpp> #include <mbgl/style/conversion.hpp> +#import <mbgl/style/types.hpp> #import <mbgl/util/enum.hpp> @@ -415,6 +417,12 @@ private: // Private utilities for converting from mgl to mbgl values mbglValue.push_back(mbglElement); } } + + void getMBGLValue(NSValue *rawValue, mbgl::style::Position &mbglValue) { + auto spherical = rawValue.mgl_lightPositionArrayValue; + mbgl::style::Position position(spherical); + mbglValue = position; + } // Enumerations template <typename MBGLEnum = MBGLType, @@ -473,6 +481,12 @@ private: // Private utilities for converting from mbgl to mgl values } return array; } + + static NSValue *toMGLRawStyleValue(const mbgl::style::Position &mbglStopValue) { + std::array<float, 3> spherical = mbglStopValue.getSpherical(); + MGLSphericalPosition position = MGLSphericalPositionMake(spherical[0], spherical[1], spherical[2]); + return [NSValue valueWithMGLSphericalPosition:position]; + } // Enumerations template <typename MBGLEnum = MBGLType, typename MGLEnum = ObjCEnum> diff --git a/platform/darwin/src/MGLTypes.h b/platform/darwin/src/MGLTypes.h index c06fd8b0e7..16f510b5a6 100644 --- a/platform/darwin/src/MGLTypes.h +++ b/platform/darwin/src/MGLTypes.h @@ -1,4 +1,5 @@ #import <Foundation/Foundation.h> +#import <CoreGraphics/CoreGraphics.h> #import "MGLFoundation.h" diff --git a/platform/darwin/src/MGLVectorSource.h b/platform/darwin/src/MGLVectorSource.h index a48434f7a3..968be3c0e0 100644 --- a/platform/darwin/src/MGLVectorSource.h +++ b/platform/darwin/src/MGLVectorSource.h @@ -74,96 +74,13 @@ MGL_EXPORT Returns a vector source initialized an identifier, tile URL templates, and options. + Tile URL templates are strings that specify the URLs of the vector tiles to + load. See the “<a href="../tile-url-templates.html">Tile URL Templates</a>” + guide for information about the format of a tile URL template. + After initializing and configuring the source, add it to a map view’s style using the `-[MGLStyle addSource:]` method. - #### Tile URL templates - - Tile URL templates are strings that specify the URLs of the tile images to - load. Each template resembles an absolute URL, but with any number of - placeholder strings that the source evaluates based on the tile it needs to - load. For example: - - <ul> - <li><code>http://www.example.com/tiles/{z}/{x}/{y}.pbf</code> could be - evaluated as <code>http://www.example.com/tiles/14/6/9.pbf</code>.</li> - <li><code>http://www.example.com/tiles/{z}/{x}/{y}{ratio}.png</code> could be - evaluated as <code>http://www.example.com/tiles/14/6/9@2x.png</code>.</li> - </ul> - - Tile sources support the following placeholder strings in tile URL templates, - all of which are optional: - - <table> - <thead> - <tr><th>Placeholder string</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><code>{x}</code></td> - <td>The index of the tile along the map’s x axis according to Spherical - Mercator projection. If the value is 0, the tile’s left edge corresponds - to the 180th meridian west. If the value is 2<sup><var>z</var></sup>−1, - the tile’s right edge corresponds to the 180th meridian east.</td> - </tr> - <tr> - <td><code>{y}</code></td> - <td>The index of the tile along the map’s y axis according to Spherical - Mercator projection. If the value is 0, the tile’s tile edge corresponds - to arctan(sinh(π)), or approximately 85.0511 degrees north. If the value - is 2<sup><var>z</var></sup>−1, the tile’s bottom edge corresponds to - −arctan(sinh(π)), or approximately 85.0511 degrees south. The y axis is - inverted if the <code>options</code> parameter contains - <code>MGLTileSourceOptionTileCoordinateSystem</code> with a value of - <code>MGLTileCoordinateSystemTMS</code>.</td> - </tr> - <tr> - <td><code>{z}</code></td> - <td>The tile’s zoom level. At zoom level 0, each tile covers the entire - world map; at zoom level 1, it covers ¼ of the world; at zoom level 2, - <sup>1</sup>⁄<sub>16</sub> of the world, and so on. For tiles loaded by - a <code>MGLRasterSource</code> object, whether the tile zoom level - matches the map’s current zoom level depends on the value of the - source’s tile size as specified in the - <code>MGLTileSourceOptionTileSize</code> key of the - <code>options</code> parameter.</td> - </tr> - <tr> - <td><code>{bbox-epsg-3857}</code></td> - <td>The tile’s bounding box, expressed as a comma-separated list of the - tile’s western, southern, eastern, and northern extents according to - Spherical Mercator (EPSG:3857) projection. The bounding box is typically - used with map services conforming to the - <a href="http://www.opengeospatial.org/standards/wms">Web Map Service</a> - protocol.</td> - </tr> - <tr> - <td><code>{quadkey}</code></td> - <td>A quadkey indicating both the tile’s location and its zoom level. The - quadkey is typically used with - <a href="https://msdn.microsoft.com/en-us/library/bb259689.aspx">Bing Maps</a>. - </td> - </tr> - <tr> - <td><code>{ratio}</code></td> - <td>A suffix indicating the resolution of the tile image. The suffix is the - empty string for standard resolution displays and <code>@2x</code> for - Retina displays, including displays for which - <code>NSScreen.backingScaleFactor</code> or <code>UIScreen.scale</code> - is 3.</td> - </tr> - <tr> - <td><code>{prefix}</code></td> - <td>Two hexadecimal digits chosen such that each visible tile has a - different prefix. The prefix is typically used for domain sharding.</td> - </tr> - </tbody> - </table> - - For more information about the `{x}`, `{y}`, and `{z}` placeholder strings, - consult the - <a href="https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames">OpenStreetMap Wiki</a>. - @param identifier A string that uniquely identifies the source in the style to which it is added. @param tileURLTemplates An array of tile URL template strings. Only the first diff --git a/platform/darwin/src/NSBundle+MGLAdditions.h b/platform/darwin/src/NSBundle+MGLAdditions.h index 1fc9e8b896..ad5e9d5369 100644 --- a/platform/darwin/src/NSBundle+MGLAdditions.h +++ b/platform/darwin/src/NSBundle+MGLAdditions.h @@ -36,9 +36,7 @@ NS_ASSUME_NONNULL_BEGIN + (nullable NS_DICTIONARY_OF(NSString *, id) *)mgl_frameworkInfoDictionary; -/// The relative path to the directory containing the SDK’s resource files, or -/// `nil` if the files are located directly within the bundle’s root directory. -@property (readonly, copy, nullable) NSString *mgl_resourcesDirectory; ++ (nullable NSString *)mgl_applicationBundleIdentifier; @end diff --git a/platform/darwin/src/NSBundle+MGLAdditions.m b/platform/darwin/src/NSBundle+MGLAdditions.m index 76d9cc0db7..f383a50300 100644 --- a/platform/darwin/src/NSBundle+MGLAdditions.m +++ b/platform/darwin/src/NSBundle+MGLAdditions.m @@ -6,12 +6,19 @@ + (instancetype)mgl_frameworkBundle { NSBundle *bundle = [self bundleForClass:[MGLAccountManager class]]; - if (![bundle.infoDictionary[@"CFBundlePackageType"] isEqualToString:@"FMWK"] && !bundle.mgl_resourcesDirectory) { + + if (![bundle.infoDictionary[@"CFBundlePackageType"] isEqualToString:@"FMWK"]) { // For static frameworks, the bundle is the containing application - // bundle but the resources are still in the framework bundle. - bundle = [NSBundle bundleWithPath:[bundle.privateFrameworksPath - stringByAppendingPathComponent:@"Mapbox.framework"]]; + // bundle but the resources are in Mapbox.bundle. + NSString *bundlePath = [bundle pathForResource:@"Mapbox" ofType:@"bundle"]; + if (bundlePath) { + bundle = [self bundleWithPath:bundlePath]; + } else { + [NSException raise:@"MGLBundleNotFoundException" format: + @"The Mapbox framework bundle could not be found. If using the Mapbox iOS SDK as a static framework, make sure that Mapbox.bundle is copied into the root of the app bundle."]; + } } + return bundle; } @@ -21,18 +28,16 @@ + (nullable NS_DICTIONARY_OF(NSString *, id) *)mgl_frameworkInfoDictionary { NSBundle *bundle = self.mgl_frameworkBundle; - if (bundle.mgl_resourcesDirectory) { - NSString *infoPlistPath = [bundle pathForResource:@"Info" - ofType:@"plist" - inDirectory:bundle.mgl_resourcesDirectory]; - return [NSDictionary dictionaryWithContentsOfFile:infoPlistPath]; - } else { - return bundle.infoDictionary; - } + return bundle.infoDictionary; } -- (NSString *)mgl_resourcesDirectory { - return [self pathForResource:@"Mapbox" ofType:@"bundle"] ? @"Mapbox.bundle" : nil; ++ (nullable NSString *)mgl_applicationBundleIdentifier { + NSString *bundleIdentifier = [NSBundle mainBundle].bundleIdentifier; + if (!bundleIdentifier) { + // There’s no main bundle identifier when running in a unit test bundle. + bundleIdentifier = [NSBundle bundleForClass:[MGLAccountManager class]].bundleIdentifier; + } + return bundleIdentifier; } @end diff --git a/platform/darwin/src/NSValue+MGLAdditions.h b/platform/darwin/src/NSValue+MGLAdditions.h index e6755021d0..0aaa2a337a 100644 --- a/platform/darwin/src/NSValue+MGLAdditions.h +++ b/platform/darwin/src/NSValue+MGLAdditions.h @@ -1,6 +1,7 @@ #import <Foundation/Foundation.h> #import "MGLGeometry.h" +#import "MGLLight.h" #import "MGLOfflinePack.h" #import "MGLTypes.h" @@ -87,6 +88,34 @@ NS_ASSUME_NONNULL_BEGIN */ @property (readonly) MGLTransition MGLTransitionValue; +/** + Creates a new value object containing the given `MGLSphericalPosition` + structure. + + @param lightPosition The value for the new object. + @return A new value object that contains the light position information. + */ ++ (instancetype)valueWithMGLSphericalPosition:(MGLSphericalPosition)lightPosition; + +/** + The `MGLSphericalPosition` structure representation of the value. + */ +@property (readonly) MGLSphericalPosition MGLSphericalPositionValue; + +/** + Creates a new value object containing the given `MGLLightAnchor` + enum. + + @param lightAnchor The value for the new object. + @return A new value object that contains the light anchor information. + */ ++ (NSValue *)valueWithMGLLightAnchor:(MGLLightAnchor)lightAnchor; + +/** + The `MGLLightAnchor` enum representation of the value. + */ +@property (readonly) MGLLightAnchor MGLLightAnchorValue; + @end NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/NSValue+MGLAdditions.m b/platform/darwin/src/NSValue+MGLAdditions.m index a95ef23941..ef894f0eb4 100644 --- a/platform/darwin/src/NSValue+MGLAdditions.m +++ b/platform/darwin/src/NSValue+MGLAdditions.m @@ -58,4 +58,27 @@ return transition; } ++ (NSValue *)valueWithMGLSphericalPosition:(MGLSphericalPosition)lightPosition +{ + return [NSValue value:&lightPosition withObjCType:@encode(MGLSphericalPosition)]; +} + +- (MGLSphericalPosition)MGLSphericalPositionValue +{ + MGLSphericalPosition lightPosition; + [self getValue:&lightPosition]; + return lightPosition; +} + ++ (NSValue *)valueWithMGLLightAnchor:(MGLLightAnchor)lightAnchor { + return [NSValue value:&lightAnchor withObjCType:@encode(MGLLightAnchor)]; +} + +- (MGLLightAnchor)MGLLightAnchorValue +{ + MGLLightAnchor achorType; + [self getValue:&achorType]; + return achorType; +} + @end diff --git a/platform/darwin/src/NSValue+MGLStyleAttributeAdditions.h b/platform/darwin/src/NSValue+MGLStyleAttributeAdditions.h index 60c1ee4075..0f1e511694 100644 --- a/platform/darwin/src/NSValue+MGLStyleAttributeAdditions.h +++ b/platform/darwin/src/NSValue+MGLStyleAttributeAdditions.h @@ -9,5 +9,6 @@ - (std::array<float, 2>)mgl_offsetArrayValue; - (std::array<float, 4>)mgl_paddingArrayValue; +- (std::array<float, 3>)mgl_lightPositionArrayValue; @end diff --git a/platform/darwin/src/NSValue+MGLStyleAttributeAdditions.mm b/platform/darwin/src/NSValue+MGLStyleAttributeAdditions.mm index e66145aec1..a41950b6b3 100644 --- a/platform/darwin/src/NSValue+MGLStyleAttributeAdditions.mm +++ b/platform/darwin/src/NSValue+MGLStyleAttributeAdditions.mm @@ -1,5 +1,5 @@ #import "NSValue+MGLStyleAttributeAdditions.h" - +#import "MGLLight.h" #if TARGET_OS_IPHONE #import <UIKit/UIKit.h> #define MGLEdgeInsets UIEdgeInsets @@ -61,4 +61,17 @@ }; } +- (std::array<float, 3>)mgl_lightPositionArrayValue +{ + NSAssert(strcmp(self.objCType, @encode(MGLSphericalPosition)) == 0, @"Value does not represent an MGLSphericalPosition"); + MGLSphericalPosition lightPosition; + [self getValue:&lightPosition]; + // Style specification defines padding in clockwise order: top, right, bottom, left. + return { + static_cast<float>(lightPosition.radial), + static_cast<float>(lightPosition.azimuthal), + static_cast<float>(lightPosition.polar), + }; +} + @end diff --git a/platform/darwin/test/MGLAttributionInfoTests.m b/platform/darwin/test/MGLAttributionInfoTests.m index e258671c09..ed4927d44b 100644 --- a/platform/darwin/test/MGLAttributionInfoTests.m +++ b/platform/darwin/test/MGLAttributionInfoTests.m @@ -46,8 +46,18 @@ XCTAssertEqualObjects(infos[3].title.string, @"Improve this map"); XCTAssertEqualObjects(infos[3].URL, [NSURL URLWithString:@"https://www.mapbox.com/map-feedback/"]); XCTAssertTrue(infos[3].feedbackLink); + NSURL *styleURL = [MGLStyle satelliteStreetsStyleURLWithVersion:99]; +#if TARGET_OS_IPHONE + XCTAssertEqualObjects([infos[3] feedbackURLAtCenterCoordinate:mapbox zoomLevel:14], + [NSURL URLWithString:@"https://www.mapbox.com/feedback/?referrer=com.mapbox.sdk.ios#/77.63680/12.98108/14.00/0.0/0"]); + XCTAssertEqualObjects([infos[3] feedbackURLForStyleURL:styleURL atCenterCoordinate:mapbox zoomLevel:3.14159 direction:90.9 pitch:12.5], + [NSURL URLWithString:@"https://www.mapbox.com/feedback/?referrer=com.mapbox.sdk.ios&owner=mapbox&id=satellite-streets-v99&access_token#/77.63680/12.98108/3.14/90.9/13"]); +#else XCTAssertEqualObjects([infos[3] feedbackURLAtCenterCoordinate:mapbox zoomLevel:14], - [NSURL URLWithString:@"https://www.mapbox.com/map-feedback/#/77.63680/12.98108/15"]); + [NSURL URLWithString:@"https://www.mapbox.com/feedback/?referrer=com.mapbox.MapboxGL#/77.63680/12.98108/14.00/0.0/0"]); + XCTAssertEqualObjects([infos[3] feedbackURLForStyleURL:styleURL atCenterCoordinate:mapbox zoomLevel:3.14159 direction:90.9 pitch:12.5], + [NSURL URLWithString:@"https://www.mapbox.com/feedback/?referrer=com.mapbox.MapboxGL&owner=mapbox&id=satellite-streets-v99&access_token#/77.63680/12.98108/3.14/90.9/13"]); +#endif } - (void)testStyle { diff --git a/platform/darwin/test/MGLCodingTests.m b/platform/darwin/test/MGLCodingTests.m index ff0d674ad1..ac61672b76 100644 --- a/platform/darwin/test/MGLCodingTests.m +++ b/platform/darwin/test/MGLCodingTests.m @@ -66,12 +66,54 @@ [unarchivedPolyline replaceCoordinatesInRange:NSMakeRange(0, 1) withCoordinates:otherCoordinates]; XCTAssertNotEqualObjects(polyline, unarchivedPolyline); + + CLLocationCoordinate2D multiLineCoordinates[] = { + CLLocationCoordinate2DMake(51.000000, 0.000000), + CLLocationCoordinate2DMake(51.000000, 1.000000), + CLLocationCoordinate2DMake(51.000000, 2.000000), + }; + + NSUInteger multiLineCoordinatesCount = sizeof(multiLineCoordinates) / sizeof(CLLocationCoordinate2D); + MGLPolyline *multiLine = [MGLPolyline polylineWithCoordinates:multiLineCoordinates count:multiLineCoordinatesCount]; + CLLocationCoordinate2D multiLineCenter = CLLocationCoordinate2DMake(51.000000, 1.000000); + + XCTAssertEqual([multiLine coordinate].latitude, multiLineCenter.latitude); + XCTAssertEqual([multiLine coordinate].longitude, multiLineCenter.longitude); + + CLLocationCoordinate2D segmentCoordinates[] = { + CLLocationCoordinate2DMake(35.040390, -85.311477), + CLLocationCoordinate2DMake(35.040390, -85.209510), + }; + + NSUInteger segmentCoordinatesCount = sizeof(segmentCoordinates) / sizeof(CLLocationCoordinate2D); + MGLPolyline *segmentLine = [MGLPolyline polylineWithCoordinates:segmentCoordinates count:segmentCoordinatesCount]; + CLLocationCoordinate2D segmentCenter = CLLocationCoordinate2DMake(35.0404006631, -85.2604935); + + XCTAssertEqualWithAccuracy([segmentLine coordinate].latitude, segmentCenter.latitude, 0.0001); + XCTAssertEqualWithAccuracy([segmentLine coordinate].longitude, segmentCenter.longitude, 0.0001); + + CLLocationCoordinate2D sfToBerkeleyCoordinates[] = { + CLLocationCoordinate2DMake(37.782440, -122.397111), + CLLocationCoordinate2DMake(37.818384, -122.352994), + CLLocationCoordinate2DMake(37.831401, -122.274545), + CLLocationCoordinate2DMake(37.862172, -122.262700), + }; + + NSUInteger sfToBerkeleyCoordinatesCount = sizeof(sfToBerkeleyCoordinates) / sizeof(CLLocationCoordinate2D); + MGLPolyline *sfToBerkeleyLine = [MGLPolyline polylineWithCoordinates:sfToBerkeleyCoordinates count:sfToBerkeleyCoordinatesCount]; + CLLocationCoordinate2D sfToBerkeleyCenter = CLLocationCoordinate2DMake(37.8230575118,-122.324867587); + + XCTAssertEqualWithAccuracy([sfToBerkeleyLine coordinate].latitude, sfToBerkeleyCenter.latitude, 0.0001); + XCTAssertEqualWithAccuracy([sfToBerkeleyLine coordinate].longitude, sfToBerkeleyCenter.longitude, 0.0001); + } - (void)testPolygon { CLLocationCoordinate2D coordinates[] = { - CLLocationCoordinate2DMake(0.664482398, 1.8865675), - CLLocationCoordinate2DMake(2.13224687, 3.9984632) + CLLocationCoordinate2DMake(35.090745, -85.300259), + CLLocationCoordinate2DMake(35.092035, -85.298885), + CLLocationCoordinate2DMake(35.090639, -85.297416), + CLLocationCoordinate2DMake(35.089112, -85.298928) }; NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D); @@ -84,8 +126,24 @@ [NSKeyedArchiver archiveRootObject:polygon toFile:filePath]; MGLPolygon *unarchivedPolygon = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath]; + [unarchivedPolygon coordinate]; XCTAssertEqualObjects(polygon, unarchivedPolygon); + + CLLocationCoordinate2D squareCoordinates[] = { + CLLocationCoordinate2DMake(100.0, 0.0), + CLLocationCoordinate2DMake(101.0, 0.0), + CLLocationCoordinate2DMake(101.0, 1.0), + CLLocationCoordinate2DMake(100.0, 1.0), + }; + + NSUInteger squareCoordinatesCount = sizeof(squareCoordinates) / sizeof(CLLocationCoordinate2D); + MGLPolygon *squarePolygon = [MGLPolygon polygonWithCoordinates:squareCoordinates count:squareCoordinatesCount]; + CLLocationCoordinate2D squareCenter = CLLocationCoordinate2DMake(100.5, 0.5); + + XCTAssertEqual([squarePolygon coordinate].latitude, squareCenter.latitude); + XCTAssertEqual([squarePolygon coordinate].longitude, squareCenter.longitude); + } - (void)testPolygonWithInteriorPolygons { @@ -169,6 +227,11 @@ NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D); MGLPointCollection *pointCollection = [MGLPointCollection pointCollectionWithCoordinates:coordinates count:numberOfCoordinates]; + CLLocationCoordinate2D pointsCenter = CLLocationCoordinate2DMake(0, 1); + + XCTAssertEqual([pointCollection coordinate].latitude, pointsCenter.latitude); + XCTAssertEqual([pointCollection coordinate].longitude, pointsCenter.longitude); + NSString *filePath = [self temporaryFilePathForClass:[MGLPointCollection class]]; [NSKeyedArchiver archiveRootObject:pointCollection toFile:filePath]; @@ -218,6 +281,30 @@ CLLocationCoordinate2DMake(20, 21), CLLocationCoordinate2DMake(30, 31), }; + + CLLocationCoordinate2D line1[] = { + CLLocationCoordinate2DMake(100, 40), + CLLocationCoordinate2DMake(105, 45), + CLLocationCoordinate2DMake(110, 55) + }; + + CLLocationCoordinate2D line2[] = { + CLLocationCoordinate2DMake(105, 40), + CLLocationCoordinate2DMake(110, 45), + CLLocationCoordinate2DMake(115, 55) + }; + + NSUInteger road1CoordinatesCount = sizeof(line1) / sizeof(CLLocationCoordinate2D); + NSUInteger road2CoordinatesCount = sizeof(line2) / sizeof(CLLocationCoordinate2D); + + MGLPolyline *road1Polyline = [MGLPolyline polylineWithCoordinates:line1 count:road1CoordinatesCount]; + MGLPolyline *road2Polyline = [MGLPolyline polylineWithCoordinates:line1 count:road2CoordinatesCount]; + + MGLMultiPolyline *roads = [MGLMultiPolyline multiPolylineWithPolylines:@[road1Polyline, road2Polyline]]; + CLLocationCoordinate2D roadCenter = CLLocationCoordinate2DMake(100, 40); + + XCTAssertEqual([roads coordinate].latitude, roadCenter.latitude); + XCTAssertEqual([roads coordinate].longitude, roadCenter.longitude); NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D); @@ -248,6 +335,31 @@ CLLocationCoordinate2DMake(20, 21), CLLocationCoordinate2DMake(30, 31), }; + + CLLocationCoordinate2D outerSquare[] = { + CLLocationCoordinate2DMake(100.0, 0.0), + CLLocationCoordinate2DMake(101.0, 0.0), + CLLocationCoordinate2DMake(101.0, 1.0), + CLLocationCoordinate2DMake(100.0, 1.0), + }; + + CLLocationCoordinate2D innerSquare[] = { + CLLocationCoordinate2DMake(100.35, 0.35), + CLLocationCoordinate2DMake(100.65, 0.35), + CLLocationCoordinate2DMake(100.65, 0.65), + CLLocationCoordinate2DMake(100.35, 0.65), + }; + + NSUInteger outerCoordinatesCount = sizeof(outerSquare) / sizeof(CLLocationCoordinate2D); + NSUInteger innerCoordinatesCount = sizeof(innerSquare) / sizeof(CLLocationCoordinate2D); + + MGLPolygon *innerPolygonSquare = [MGLPolygon polygonWithCoordinates:innerSquare count:innerCoordinatesCount]; + MGLPolygon *outerPolygonSquare = [MGLPolygon polygonWithCoordinates:outerSquare count:outerCoordinatesCount interiorPolygons:@[innerPolygonSquare]]; + MGLMultiPolygon *squares = [MGLMultiPolygon multiPolygonWithPolygons:@[outerPolygonSquare, innerPolygonSquare]]; + CLLocationCoordinate2D squareCenter = CLLocationCoordinate2DMake(100.5, 0.5); + + XCTAssertEqual([squares coordinate].latitude, squareCenter.latitude); + XCTAssertEqual([squares coordinate].longitude, squareCenter.longitude); NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D); @@ -265,9 +377,10 @@ MGLMultiPolygon *unarchivedMultiPolygon = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath]; MGLMultiPolygon *anotherMultiPolygon = [MGLMultiPolygon multiPolygonWithPolygons:[polygons subarrayWithRange:NSMakeRange(0, polygons.count/2)]]; - + XCTAssertEqualObjects(multiPolygon, unarchivedMultiPolygon); XCTAssertNotEqualObjects(anotherMultiPolygon, unarchivedMultiPolygon); + } - (void)testShapeCollection { diff --git a/platform/darwin/test/MGLDocumentationExampleTests.swift b/platform/darwin/test/MGLDocumentationExampleTests.swift index 6d2dc597a9..48e6b17f44 100644 --- a/platform/darwin/test/MGLDocumentationExampleTests.swift +++ b/platform/darwin/test/MGLDocumentationExampleTests.swift @@ -168,7 +168,7 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate { layer.sourceLayerIdentifier = "building" layer.fillExtrusionHeight = MGLStyleValue(interpolationMode: .identity, sourceStops: nil, attributeName: "height", options: nil) layer.fillExtrusionBase = MGLStyleValue(interpolationMode: .identity, sourceStops: nil, attributeName: "min_height", options: nil) - layer.predicate = NSPredicate(format: "extrude == TRUE") + layer.predicate = NSPredicate(format: "extrude == 'true'") mapView.style?.addLayer(layer) //#-end-example-code diff --git a/platform/darwin/test/MGLLightTest.mm b/platform/darwin/test/MGLLightTest.mm new file mode 100644 index 0000000000..2c3d1c7bd1 --- /dev/null +++ b/platform/darwin/test/MGLLightTest.mm @@ -0,0 +1,217 @@ +#import <XCTest/XCTest.h> +#import <Mapbox/Mapbox.h> + +#import "MGLLight_Private.h" + +#import "../../darwin/src/NSDate+MGLAdditions.h" + +#import <mbgl/style/light.hpp> +#import <mbgl/style/types.hpp> +#include <mbgl/style/transition_options.hpp> + +@interface MGLLightTest : XCTestCase + +@end + +@implementation MGLLightTest + +- (void)testProperties { + + MGLTransition defaultTransition = MGLTransitionMake(0, 0); + MGLTransition transition = MGLTransitionMake(6, 3); + mbgl::style::TransitionOptions transitionOptions { { MGLDurationFromTimeInterval(6) }, { MGLDurationFromTimeInterval(3) } }; + + // anchor + { + mbgl::style::Light light; + MGLLight *mglLight = [[MGLLight alloc] initWithMBGLLight:&light]; + + NSAssert([mglLight.anchor isKindOfClass:[MGLConstantStyleValue class]], @"mglLight.anchor isn’t a MGLConstantStyleValue."); + NSValue *anchorValue = ((MGLConstantStyleValue *)mglLight.anchor).rawValue; + XCTAssertEqual(anchorValue.MGLLightAnchorValue, MGLLightAnchorViewport); + XCTAssertEqual(mglLight.anchorTransition.delay, defaultTransition.delay); + XCTAssertEqual(mglLight.anchorTransition.duration, defaultTransition.duration); + + auto lightFromMGLlight = [mglLight mbglLight]; + + XCTAssertEqual(light.getDefaultAnchor(), lightFromMGLlight.getAnchor().asConstant()); + auto anchorTransition = lightFromMGLlight.getAnchorTransition(); + XCTAssert(anchorTransition.delay && MGLTimeIntervalFromDuration(*anchorTransition.delay) == defaultTransition.delay); + XCTAssert(anchorTransition.duration && MGLTimeIntervalFromDuration(*anchorTransition.duration) == defaultTransition.duration); + + MGLStyleValue<NSValue *> *anchorStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLLightAnchor:MGLLightAnchorMap]]; + mglLight.anchor = anchorStyleValue; + mglLight.anchorTransition = transition; + NSAssert([mglLight.anchor isKindOfClass:[MGLConstantStyleValue class]], @"mglLight.anchor isn’t a MGLConstantStyleValue."); + anchorValue = ((MGLConstantStyleValue *)mglLight.anchor).rawValue; + + XCTAssertEqual(anchorValue.MGLLightAnchorValue, MGLLightAnchorMap); + XCTAssertEqual(mglLight.anchorTransition.delay, transition.delay); + XCTAssertEqual(mglLight.anchorTransition.duration, transition.duration); + + mbgl::style::PropertyValue<mbgl::style::LightAnchorType> anchorProperty = { mbgl::style::LightAnchorType::Map }; + light.setAnchor(anchorProperty); + light.setAnchorTransition(transitionOptions); + + lightFromMGLlight = [mglLight mbglLight]; + + XCTAssertEqual(light.getAnchor().asConstant(), lightFromMGLlight.getAnchor().asConstant()); + anchorTransition = lightFromMGLlight.getAnchorTransition(); + XCTAssert(anchorTransition.delay && MGLTimeIntervalFromDuration(*anchorTransition.delay) == transition.delay); + XCTAssert(anchorTransition.duration && MGLTimeIntervalFromDuration(*anchorTransition.duration) == transition.duration); + + } + + // position + { + mbgl::style::Light light; + MGLLight *mglLight = [[MGLLight alloc] initWithMBGLLight:&light]; + NSAssert([mglLight.position isKindOfClass:[MGLConstantStyleValue class]], @"mglLight.position isn’t a MGLConstantStyleValue."); + NSValue *positionValue = ((MGLConstantStyleValue *)mglLight.position).rawValue; + auto positionArray = light.getDefaultPosition().getSpherical(); + MGLSphericalPosition defaultPosition = MGLSphericalPositionMake(positionArray[0], positionArray[1], positionArray[2]); + + XCTAssert(defaultPosition.radial == positionValue.MGLSphericalPositionValue.radial); + XCTAssert(defaultPosition.azimuthal == positionValue.MGLSphericalPositionValue.azimuthal); + XCTAssert(defaultPosition.polar == positionValue.MGLSphericalPositionValue.polar); + XCTAssertEqual(mglLight.positionTransiton.delay, defaultTransition.delay); + XCTAssertEqual(mglLight.positionTransiton.duration, defaultTransition.duration); + + auto lightFromMGLlight = [mglLight mbglLight]; + + XCTAssertEqual(positionArray, lightFromMGLlight.getPosition().asConstant().getSpherical()); + auto positionTransition = lightFromMGLlight.getPositionTransition(); + XCTAssert(positionTransition.delay && MGLTimeIntervalFromDuration(*positionTransition.delay) == defaultTransition.delay); + XCTAssert(positionTransition.duration && MGLTimeIntervalFromDuration(*positionTransition.duration) == defaultTransition.duration); + + defaultPosition = MGLSphericalPositionMake(6, 180, 90); + MGLStyleValue<NSValue *> *positionStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLSphericalPosition:defaultPosition]]; + mglLight.position = positionStyleValue; + mglLight.positionTransiton = transition; + + NSAssert([mglLight.position isKindOfClass:[MGLConstantStyleValue class]], @"mglLight.position isn’t a MGLConstantStyleValue."); + positionValue = ((MGLConstantStyleValue *)mglLight.position).rawValue; + + XCTAssert(defaultPosition.radial == positionValue.MGLSphericalPositionValue.radial); + XCTAssert(defaultPosition.azimuthal == positionValue.MGLSphericalPositionValue.azimuthal); + XCTAssert(defaultPosition.polar == positionValue.MGLSphericalPositionValue.polar); + XCTAssertEqual(mglLight.positionTransiton.delay, transition.delay); + XCTAssertEqual(mglLight.positionTransiton.duration, transition.duration); + + lightFromMGLlight = [mglLight mbglLight]; + + positionArray = { { 6, 180, 90 } }; + mbgl::style::Position position = { positionArray }; + mbgl::style::PropertyValue<mbgl::style::Position> positionProperty = { position }; + light.setPosition(positionProperty); + light.setPositionTransition(transitionOptions); + + XCTAssertEqual(positionArray, lightFromMGLlight.getPosition().asConstant().getSpherical()); + positionTransition = lightFromMGLlight.getPositionTransition(); + XCTAssert(positionTransition.delay && MGLTimeIntervalFromDuration(*positionTransition.delay) == transition.delay); + XCTAssert(positionTransition.duration && MGLTimeIntervalFromDuration(*positionTransition.duration) == transition.duration); + + } + + // color + { + mbgl::style::Light light; + MGLLight *mglLight = [[MGLLight alloc] initWithMBGLLight:&light]; + NSAssert([mglLight.color isKindOfClass:[MGLConstantStyleValue class]], @"mglLight.color isn’t a MGLConstantStyleValue."); + MGLColor *colorValue = ((MGLConstantStyleValue *)mglLight.color).rawValue; + auto color = light.getDefaultColor(); + const CGFloat *colorComponents = CGColorGetComponents(colorValue.CGColor); + + XCTAssert(color.r == colorComponents[0] && color.g == colorComponents[1] && color.b == colorComponents[2] && + color.a == colorComponents[3]); + XCTAssertEqual(mglLight.colorTransiton.delay, defaultTransition.delay); + XCTAssertEqual(mglLight.colorTransiton.duration, defaultTransition.duration); + + auto lightFromMGLlight = [mglLight mbglLight]; + + XCTAssertEqual(color, lightFromMGLlight.getColor().asConstant()); + auto colorTransition = lightFromMGLlight.getColorTransition(); + XCTAssert(colorTransition.delay && MGLTimeIntervalFromDuration(*colorTransition.delay) == defaultTransition.delay); + XCTAssert(colorTransition.duration && MGLTimeIntervalFromDuration(*colorTransition.duration) == defaultTransition.duration); + + MGLStyleValue<MGLColor *> *colorStyleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor blackColor]]; + mglLight.color = colorStyleValue; + mglLight.colorTransiton = transition; + + NSAssert([mglLight.color isKindOfClass:[MGLConstantStyleValue class]], @"mglLight.color isn’t a MGLConstantStyleValue."); + colorValue = ((MGLConstantStyleValue *)mglLight.color).rawValue; + + XCTAssertEqual([MGLColor blackColor], colorValue); + XCTAssertEqual(mglLight.colorTransiton.delay, transition.delay); + XCTAssertEqual(mglLight.colorTransiton.duration, transition.duration); + + mbgl::style::PropertyValue<mbgl::Color> colorProperty = { { 0, 0, 0, 1 } }; + light.setColor(colorProperty); + light.setColorTransition(transitionOptions); + + lightFromMGLlight = [mglLight mbglLight]; + + colorComponents = CGColorGetComponents(colorValue.CGColor); + color = lightFromMGLlight.getColor().asConstant(); + XCTAssertEqual(light.getColor().asConstant(),lightFromMGLlight.getColor().asConstant()); + colorTransition = lightFromMGLlight.getColorTransition(); + XCTAssert(colorTransition.delay && MGLTimeIntervalFromDuration(*colorTransition.delay) == transition.delay); + XCTAssert(colorTransition.duration && MGLTimeIntervalFromDuration(*colorTransition.duration) == transition.duration); + } + + // intensity + { + mbgl::style::Light light; + MGLLight *mglLight = [[MGLLight alloc] initWithMBGLLight:&light]; + NSAssert([mglLight.intensity isKindOfClass:[MGLConstantStyleValue class]], @"mglLight.intensity isn’t a MGLConstantStyleValue."); + NSNumber *intensityNumber = ((MGLConstantStyleValue *)mglLight.intensity).rawValue; + auto intensity = light.getDefaultIntensity(); + + XCTAssert(intensityNumber.floatValue == intensity); + XCTAssertEqual(mglLight.intensityTransition.delay, defaultTransition.delay); + XCTAssertEqual(mglLight.intensityTransition.duration, defaultTransition.duration); + + auto lightFromMGLlight = [mglLight mbglLight]; + + XCTAssertEqual(intensity, lightFromMGLlight.getIntensity().asConstant()); + auto intensityTransition = lightFromMGLlight.getIntensityTransition(); + XCTAssert(intensityTransition.delay && MGLTimeIntervalFromDuration(*intensityTransition.delay) == defaultTransition.delay); + XCTAssert(intensityTransition.duration && MGLTimeIntervalFromDuration(*intensityTransition.duration) == defaultTransition.duration); + + NSNumber *intensityValue = @0.4; + MGLStyleValue<NSNumber *> *intensityStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:intensityValue]; + mglLight.intensity = intensityStyleValue; + mglLight.intensityTransition = transition; + + NSAssert([mglLight.intensity isKindOfClass:[MGLConstantStyleValue class]], @"mglLight.intensity isn’t a MGLConstantStyleValue."); + intensityNumber = ((MGLConstantStyleValue *)mglLight.intensity).rawValue; + XCTAssert(intensityNumber.floatValue == intensityValue.floatValue); + XCTAssertEqual(mglLight.intensityTransition.delay, transition.delay); + XCTAssertEqual(mglLight.intensityTransition.duration, transition.duration); + + mbgl::style::PropertyValue<float> intensityProperty = { 0.4 }; + light.setIntensity(intensityProperty); + light.setIntensityTransition(transitionOptions); + + lightFromMGLlight = [mglLight mbglLight]; + + XCTAssertEqual(light.getIntensity().asConstant(), lightFromMGLlight.getIntensity().asConstant()); + intensityTransition = lightFromMGLlight.getIntensityTransition(); + XCTAssert(intensityTransition.delay && MGLTimeIntervalFromDuration(*intensityTransition.delay) == transition.delay); + XCTAssert(intensityTransition.duration && MGLTimeIntervalFromDuration(*intensityTransition.duration) == transition.duration); + + } + +} + +- (void)testValueAdditions { + MGLSphericalPosition position = MGLSphericalPositionMake(1.15, 210, 30); + + XCTAssertEqual([NSValue valueWithMGLSphericalPosition:position].MGLSphericalPositionValue.radial, position.radial); + XCTAssertEqual([NSValue valueWithMGLSphericalPosition:position].MGLSphericalPositionValue.azimuthal, position.azimuthal); + XCTAssertEqual([NSValue valueWithMGLSphericalPosition:position].MGLSphericalPositionValue.polar, position.polar); + XCTAssertEqual([NSValue valueWithMGLLightAnchor:MGLLightAnchorMap].MGLLightAnchorValue, MGLLightAnchorMap); + XCTAssertEqual([NSValue valueWithMGLLightAnchor:MGLLightAnchorViewport].MGLLightAnchorValue, MGLLightAnchorViewport); +} + +@end |