summaryrefslogtreecommitdiff
path: root/platform/darwin
diff options
context:
space:
mode:
Diffstat (limited to 'platform/darwin')
-rw-r--r--platform/darwin/docs/guides/For Style Authors.md.ejs321
-rw-r--r--platform/darwin/docs/guides/Working with GeoJSON Data.md99
-rw-r--r--platform/darwin/docs/theme/assets/css/jazzy.css.scss25
-rwxr-xr-xplatform/darwin/docs/theme/assets/js/jazzy.js5
-rw-r--r--platform/darwin/docs/theme/templates/nav.mustache2
-rw-r--r--platform/darwin/scripts/generate-style-code.js327
-rw-r--r--platform/darwin/scripts/style-spec-cocoa-conventions-v8.json26
-rw-r--r--platform/darwin/scripts/style-spec-overrides-v8.json27
-rw-r--r--platform/darwin/scripts/update-examples.js152
-rw-r--r--platform/darwin/src/MGLAttributionInfo.h2
-rw-r--r--platform/darwin/src/MGLAttributionInfo.mm13
-rw-r--r--platform/darwin/src/MGLBackgroundStyleLayer.h46
-rw-r--r--platform/darwin/src/MGLBackgroundStyleLayer.mm3
-rw-r--r--platform/darwin/src/MGLCircleStyleLayer.h211
-rw-r--r--platform/darwin/src/MGLCircleStyleLayer.mm108
-rw-r--r--platform/darwin/src/MGLFeature.h138
-rw-r--r--platform/darwin/src/MGLFeature.mm31
-rw-r--r--platform/darwin/src/MGLFeature_Private.h28
-rw-r--r--platform/darwin/src/MGLFillStyleLayer.h165
-rw-r--r--platform/darwin/src/MGLFillStyleLayer.mm67
-rw-r--r--platform/darwin/src/MGLLineStyleLayer.h265
-rw-r--r--platform/darwin/src/MGLLineStyleLayer.mm87
-rw-r--r--platform/darwin/src/MGLMultiPoint.h19
-rw-r--r--platform/darwin/src/MGLMultiPoint.mm38
-rw-r--r--platform/darwin/src/MGLNetworkConfiguration.m4
-rw-r--r--platform/darwin/src/MGLOfflinePack.h4
-rw-r--r--platform/darwin/src/MGLOfflineStorage.h14
-rw-r--r--platform/darwin/src/MGLOpenGLStyleLayer.mm18
-rw-r--r--platform/darwin/src/MGLOverlay.h5
-rw-r--r--platform/darwin/src/MGLPointAnnotation.h32
-rw-r--r--platform/darwin/src/MGLPointAnnotation.mm36
-rw-r--r--platform/darwin/src/MGLPointCollection.h27
-rw-r--r--platform/darwin/src/MGLPointCollection.mm42
-rw-r--r--platform/darwin/src/MGLPolygon.h58
-rw-r--r--platform/darwin/src/MGLPolygon.mm55
-rw-r--r--platform/darwin/src/MGLPolyline.h58
-rw-r--r--platform/darwin/src/MGLPolyline.mm30
-rw-r--r--platform/darwin/src/MGLRasterSource.h14
-rw-r--r--platform/darwin/src/MGLRasterSource.mm8
-rw-r--r--platform/darwin/src/MGLRasterStyleLayer.h81
-rw-r--r--platform/darwin/src/MGLRasterStyleLayer.mm28
-rw-r--r--platform/darwin/src/MGLShape.h55
-rw-r--r--platform/darwin/src/MGLShape.mm66
-rw-r--r--platform/darwin/src/MGLShapeCollection.h24
-rw-r--r--platform/darwin/src/MGLShapeCollection.mm30
-rw-r--r--platform/darwin/src/MGLShapeSource.h78
-rw-r--r--platform/darwin/src/MGLShapeSource.mm16
-rw-r--r--platform/darwin/src/MGLShape_Private.h3
-rw-r--r--platform/darwin/src/MGLSource.h2
-rw-r--r--platform/darwin/src/MGLStyle.h35
-rw-r--r--platform/darwin/src/MGLStyle.mm34
-rw-r--r--platform/darwin/src/MGLStyleLayer.h.ejs94
-rw-r--r--platform/darwin/src/MGLStyleLayer.mm.ejs58
-rw-r--r--platform/darwin/src/MGLStyleValue.h44
-rw-r--r--platform/darwin/src/MGLStyleValue.mm24
-rw-r--r--platform/darwin/src/MGLStyleValue_Private.h9
-rw-r--r--platform/darwin/src/MGLSymbolStyleLayer.h978
-rw-r--r--platform/darwin/src/MGLSymbolStyleLayer.mm284
-rw-r--r--platform/darwin/src/MGLTileSource.h123
-rw-r--r--platform/darwin/src/MGLTileSource.mm6
-rw-r--r--platform/darwin/src/MGLVectorSource.h13
-rw-r--r--platform/darwin/src/MGLVectorSource.mm6
-rw-r--r--platform/darwin/src/MGLVectorStyleLayer.h30
-rw-r--r--platform/darwin/src/MGLVectorStyleLayer.m4
-rw-r--r--platform/darwin/src/NSArray+MGLAdditions.h15
-rw-r--r--platform/darwin/src/NSArray+MGLAdditions.mm28
-rw-r--r--platform/darwin/src/NSCoder+MGLAdditions.h16
-rw-r--r--platform/darwin/src/NSCoder+MGLAdditions.mm26
-rw-r--r--platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm212
-rw-r--r--platform/darwin/src/NSCompoundPredicate+MGLAdditions.mm53
-rw-r--r--platform/darwin/src/NSDictionary+MGLAdditions.mm2
-rw-r--r--platform/darwin/src/NSExpression+MGLAdditions.h10
-rw-r--r--platform/darwin/src/NSExpression+MGLAdditions.mm73
-rw-r--r--platform/darwin/src/NSPredicate+MGLAdditions.mm113
-rw-r--r--platform/darwin/src/NSString+MGLAdditions.h9
-rw-r--r--platform/darwin/src/NSString+MGLAdditions.m22
-rw-r--r--platform/darwin/src/NSValue+MGLStyleAttributeAdditions.mm8
-rw-r--r--platform/darwin/src/NSValue+MGLStyleEnumAttributeAdditions.h232
-rw-r--r--platform/darwin/src/NSValue+MGLStyleEnumAttributeAdditions.h.ejs66
-rw-r--r--platform/darwin/src/NSValue+MGLStyleEnumAttributeAdditions.mm169
-rw-r--r--platform/darwin/src/NSValue+MGLStyleEnumAttributeAdditions.mm.ejs46
-rw-r--r--platform/darwin/test/MGLBackgroundStyleLayerTests.m44
-rw-r--r--platform/darwin/test/MGLBackgroundStyleLayerTests.mm134
-rw-r--r--platform/darwin/test/MGLCircleStyleLayerTests.m85
-rw-r--r--platform/darwin/test/MGLCircleStyleLayerTests.mm413
-rw-r--r--platform/darwin/test/MGLCodingTests.m469
-rw-r--r--platform/darwin/test/MGLDocumentationExampleTests.swift220
-rw-r--r--platform/darwin/test/MGLExpressionTests.mm61
-rw-r--r--platform/darwin/test/MGLFeatureTests.mm9
-rw-r--r--platform/darwin/test/MGLFillStyleLayerTests.m69
-rw-r--r--platform/darwin/test/MGLFillStyleLayerTests.mm306
-rw-r--r--platform/darwin/test/MGLFilterTests.mm194
-rw-r--r--platform/darwin/test/MGLLineStyleLayerTests.m106
-rw-r--r--platform/darwin/test/MGLLineStyleLayerTests.mm557
-rw-r--r--platform/darwin/test/MGLNSStringAdditionsTests.m42
-rw-r--r--platform/darwin/test/MGLPredicateTests.mm419
-rw-r--r--platform/darwin/test/MGLRasterStyleLayerTests.m68
-rw-r--r--platform/darwin/test/MGLRasterStyleLayerTests.mm277
-rw-r--r--platform/darwin/test/MGLRuntimeStylingHelper.h35
-rw-r--r--platform/darwin/test/MGLRuntimeStylingHelper.m122
-rw-r--r--platform/darwin/test/MGLShapeSourceTests.mm52
-rw-r--r--platform/darwin/test/MGLStyleLayerTests.h11
-rw-r--r--platform/darwin/test/MGLStyleLayerTests.m59
-rw-r--r--platform/darwin/test/MGLStyleLayerTests.m.ejs72
-rw-r--r--platform/darwin/test/MGLStyleLayerTests.mm.ejs114
-rw-r--r--platform/darwin/test/MGLStyleLayerTests.xib39
-rw-r--r--platform/darwin/test/MGLStyleTests.mm139
-rw-r--r--platform/darwin/test/MGLStyleValueTests.swift8
-rw-r--r--platform/darwin/test/MGLSymbolStyleLayerTests.m283
-rw-r--r--platform/darwin/test/MGLSymbolStyleLayerTests.mm1797
-rw-r--r--platform/darwin/test/one-liner.json1
111 files changed, 9313 insertions, 2727 deletions
diff --git a/platform/darwin/docs/guides/For Style Authors.md.ejs b/platform/darwin/docs/guides/For Style Authors.md.ejs
new file mode 100644
index 0000000000..3d775c8a89
--- /dev/null
+++ b/platform/darwin/docs/guides/For Style Authors.md.ejs
@@ -0,0 +1,321 @@
+<%
+ const os = locals.os;
+ const iOS = os === 'iOS';
+ const macOS = os === 'macOS';
+ const cocoaPrefix = iOS ? 'UI' : 'NS';
+ const layers = locals.layers;
+ const renamedProperties = locals.renamedProperties;
+-%>
+<!--
+ This file is generated.
+ Edit platform/darwin/scripts/generate-style-code.js, then run `make style-code-darwin`.
+-->
+# Information for Style Authors
+
+A _style_ defines a map view’s content and appearance. If you’ve authored a
+style using
+[Mapbox Studio’s Styles editor](https://www.mapbox.com/studio/styles/) or as
+JSON in a text editor, you can use that style in this SDK and manipulate it
+afterwards in code. This document provides information you can use to ensure a
+seamless transition from Mapbox Studio to your application.
+
+<% if (iOS) { -%>
+## Designing for iOS
+<% } else { -%>
+## Designing for macOS
+<% } -%>
+
+When designing your style, consider the context in which your application shows
+the style. There are a number of considerations specific to <%- os %> that may
+not be obvious when designing your style in Mapbox Studio on the Web. A map view
+is essentially a graphical user interface element, so many of same issues in
+user interface design also apply when designing a map style.
+
+### Color
+
+Ensure sufficient contrast in your application’s user interface when your map
+style is present. Standard user interface elements such as toolbars, sidebars,
+and sheets often overlap the map view with a translucent, blurred background, so
+make sure the contents of these elements remain legible with the map view
+underneath.
+<% if (iOS) { -%>
+The user location annotation view, the attribution button, any buttons in
+callout views, and any items in the navigation bar are influenced by your
+application’s tint color, so choose a tint color that constrasts well with your
+map style. If you intend your style to be used in the dark, consider the impact
+that Night Shift may have on your style’s colors.
+<% } -%>
+
+### Typography and graphics
+
+<% if (iOS) { -%>
+Choose font and icon sizes appropriate to iOS devices. iPhones and iPads have
+smaller screens than the typical browser window in which you would use Mapbox
+Studio, especially when multitasking is enabled. Your user’s viewing distance
+may be shorter than on a desktop computer. Some of your users may use the Larger
+Dynamic Type and Accessibility Text features to increase the size of all text on
+the device. You can use the
+[runtime styling API](#manipulating-the-style-at-runtime) to adjust your style’s
+font and icon sizes accordingly.
+<% } -%>
+
+Design sprite images and choose font weights that look crisp on both
+standard-resolution displays and Retina displays. This SDK supports the same
+resolutions as <%- os %>.
+<% if (iOS) { -%>
+Standard-resolution displays are limited to older devices that your application
+may or may not support, depending on its minimum deployment target.
+<% } else { -%>
+Standard-resolution displays are often found on external monitors. Even with
+built-in screens, some of your users may use the Larger Text option in Display
+Preferences, which is essentially standard resolution, to make text easier to
+read.
+<% } -%>
+
+Icon and text labels should be legible regardless of the map’s orientation.
+<% if (iOS) { -%>
+By default, this SDK makes it easy for your users to rotate or tilt the map
+using multitouch gestures.
+<% } else { -%>
+By default, this SDK makes it easy for your users to rotate or tilt the map
+using multitouch trackpad gestures or keyboard shortcuts.
+<% } -%>
+If you do not intend your design to accommodate rotation and tilting, disable
+these gestures using the `MGLMapView.rotateEnabled` and
+`MGLMapView.pitchEnabled` properties, respectively, or the corresponding
+inspectables in Interface Builder.
+
+### Interactivity
+
+Pay attention to whether elements of your style appear to be interactive.
+<% if (iOS) { -%>
+A text label may look like a tappable button merely due to matching your
+application’s tint color or the default blue tint color.
+<% } else { -%>
+An icon with a shadow or shading effect may appear to be clickable.
+<% } -%>
+You can make an icon or text label interactive by installing a gesture
+recognizer and performing feature querying (e.g.,
+`-[MGLMapView visibleFeaturesAtPoint:]`) to get details about the selected
+feature.
+<% if (macOS) { -%>
+You can install cursor or tooltip tracking rectangles to indicate interactive
+features as an alternative to prominent hover effects.
+<% } -%>
+
+<% if (iOS) { -%>
+Make sure your users can easily distinguish any interactive elements from the
+surrounding map, such as pins, the user location annotation view, or a route
+line. Avoid relying on hover effects to indicate interactive elements. Leave
+enough room between interactive elements to accommodate imprecise tapping
+gestures.
+<% } else { -%>
+Make sure your users can easily distinguish any interactive elements from the
+surrounding map, such as pins or a route line. If your application supports
+printing, consider using the
+[runtime styling API](#manipulating-the-style-at-runtime) to optimize your style
+for ink economy before printing the map view.
+<% } -%>
+
+<% if (iOS) { -%>
+For more information about user interface design, consult Apple’s
+[_iOS Human Interface Guidelines_](https://developer.apple.com/ios/human-interface-guidelines/).
+<% } else { -%>
+For more information about user interface design, consult Apple’s
+[_macOS Human Interface Guidelines_](https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/OSXHIGuidelines/).
+<% } -%>
+
+## Applying your style
+
+You set an `MGLMapView` object’s style either in code, by setting the
+`MGLMapView.styleURL` property, or in Interface Builder, by setting the “Style
+URL” inspectable. The URL must point to a local or remote style JSON file. The
+style JSON file format is defined by the
+[Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/). This
+SDK supports the functionality defined by version 8 of the specification unless
+otherwise noted in the
+[style specification documentation](https://www.mapbox.com/mapbox-gl-style-spec/).
+
+## Manipulating the style at runtime
+
+The _runtime styling API_ enables you to modify every aspect of a style
+dynamically as a user interacts with your application. The style itself is
+represented at runtime by an `MGLStyle` object, which provides access to various
+`MGLSource` and `MGLStyleLayer` objects that represent content sources and style
+layers, respectively.
+<% if (iOS) { -%>
+For more information about the capabilities exposed by the runtime styling API,
+see “[Runtime Styling](runtime-styling.html)”.
+<% } -%>
+
+The names of runtime styling classes and properties on <%- os %> are generally
+consistent with the style specification and Mapbox Studio’s Styles editor. Any
+exceptions are listed in this document.
+
+To avoid conflicts with Objective-C keywords or Cocoa terminology, this SDK uses
+the following terms for concepts defined in the style specification:
+
+In the style specification | In the SDK
+---------------------------|---------
+class | style class
+filter | predicate
+id | identifier
+image | style image
+layer | style layer
+property | attribute
+SDF icon | template image
+source | content source
+
+## Specifying the map’s content
+
+Each source defined by a style JSON file is represented at runtime by a content
+source object that you can use to initialize new style layers. The content
+source object is a member of one of the following subclasses of `MGLSource`:
+
+In style JSON | In the SDK
+--------------|-----------
+`geojson` | `MGLShapeSource`
+`raster` | `MGLRasterSource`
+`vector` | `MGLVectorSource`
+
+`image` and `video` sources are not supported.
+
+### Tile sources
+
+Raster and vector sources may be defined in TileJSON configuration files. This
+SDK supports the properties defined in the style specification, which are a
+subset of the keys defined in version 2.1.0 of the
+[TileJSON](https://github.com/mapbox/tilejson-spec/tree/master/2.1.0)
+specification. As an alternative to authoring a custom TileJSON file, you may
+supply various tile source options when creating a raster or vector source.
+These options are detailed in the `MGLTileSourceOption` documentation:
+
+In style JSON | In TileJSON | In the SDK
+--------------|---------------|-----------
+`url` | — | `configurationURL` parameter in `-[MGLTileSource initWithIdentifier:configurationURL:]`
+`tiles` | `tiles` | `tileURLTemplates` parameter in `-[MGLTileSource initWithIdentifier:tileURLTemplates:options:]`
+`minzoom` | `minzoom` | `MGLTileSourceOptionMinimumZoomLevel`
+`maxzoom` | `maxzoom` | `MGLTileSourceOptionMaximumZoomLevel`
+`tileSize` | — | `MGLTileSourceOptionTileSize`
+`attribution` | `attribution` | `MGLTileSourceOptionAttributionHTMLString` (but consider specifying `MGLTileSourceOptionAttributionInfos` instead for improved security)
+`scheme` | `scheme` | `MGLTileSourceOptionTileCoordinateSystem`
+
+### Shape sources
+
+Shape sources also accept various options. These options are detailed in the
+`MGLShapeSourceOption` documentation:
+
+In style JSON | In the SDK
+-----------------|-----------
+`data` | `url` parameter in `-[MGLShapeSource initWithIdentifier:URL:options:]`
+`maxzoom` | `MGLShapeSourceOptionMaximumZoomLevel`
+`buffer` | `MGLShapeSourceOptionBuffer`
+`tolerance` | `MGLShapeSourceOptionSimplificationTolerance`
+`cluster` | `MGLShapeSourceOptionClustered`
+`clusterRadius` | `MGLShapeSourceOptionClusterRadius`
+`clusterMaxZoom` | `MGLShapeSourceOptionMaximumZoomLevelForClustering`
+
+To create a shape source from local GeoJSON data, first
+[convert the GeoJSON data into a shape](working-with-geojson-data.html#converting-geojson-data-into-shape-objects),
+then use the `-[MGLShapeSource initWithIdentifier:shape:options:]` method.
+
+## Configuring the map content’s appearance
+
+Each layer defined by the style JSON file is represented at runtime by a style
+layer object, which you can use to refine the map’s appearance. The style layer
+object is a member of one of the following subclasses of `MGLStyleLayer`:
+
+In style JSON | In the SDK
+--------------|-----------
+<% for (const layer of layers) { -%>
+`<%- layer.type %>` | `MGL<%- camelize(layer.type) %>StyleLayer`
+<% } -%>
+
+You configure layout and paint attributes by setting properties on these style
+layer objects. The property names generally correspond to the style JSON
+properties, except for the use of camelCase instead of kebab-case. Properties
+whose names differ from the style specification are listed below:
+<% for (const type in renamedProperties) { -%>
+<% if (renamedProperties.hasOwnProperty(type)) { -%>
+
+### <%- camelize(type) %> style layers
+
+In style JSON | In Objective-C | In Swift
+--------------|----------------|---------
+<% for (const name in renamedProperties[type]) { -%>
+<% if (renamedProperties[type].hasOwnProperty(name)) { -%>
+`<%- originalPropertyName(renamedProperties[type][name]) %>` | `MGL<%- camelize(type) %>StyleLayer.<%- objCName(renamedProperties[type][name]) %>` | `MGL<%- camelize(type) %>StyleLayer.<%- objCGetter(renamedProperties[type][name]) %>`
+<% } -%>
+<% } -%>
+<% } -%>
+<% } -%>
+
+## Setting attribute values
+
+Each property representing a layout or paint attribute is set to an
+`MGLStyleValue` object, which is either an `MGLStyleConstantValue` object (for
+constant values) or an `MGLStyleFunction` object (for zoom level functions). The
+style value object is a container for the raw value or function parameters that
+you want the attribute to be set to.
+
+In contrast to the JSON type that the style specification defines for each
+layout or paint property, the style value object often contains a more specific
+Foundation or Cocoa type. General rules for attribute types are listed below.
+Pay close attention to the SDK documentation for the attribute you want to get
+or set.
+
+In style JSON | In Objective-C | In Swift
+--------------|-----------------------|---------
+Color | `<%- cocoaPrefix %>Color` | `<%- cocoaPrefix %>Color`
+Enum | `NSValue` (see `NSValue(MGLAdditions)`) | `NSValue` (see `NSValue(MGLAdditions)`)
+String | `NSString` | `String`
+Boolean | `NSNumber.boolValue` | `Bool`
+Number | `NSNumber.floatValue` | `Float`
+Array (`-dasharray`) | `NSArray<NSNumber>` | `[Float]`
+Array (`-font`) | `NSArray<NSString>` | `[String]`
+<% if (iOS) { -%>
+Array (`-offset`, `-translate`) | `NSValue.CGVectorValue` | `NSValue.cgVectorValue`
+Array (`-padding`) | `NSValue.UIEdgeInsetsValue` | `NSValue.uiEdgeInsetsValue`
+<% } else { -%>
+Array (`-offset`, `-translate`) | `NSValue` containing `CGVector` | `NSValue` containing `CGVector`
+Array (`-padding`) | `NSValue.edgeInsetsValue` | `NSValue.edgeInsetsValue`
+<% } -%>
+
+For padding attributes, note that the arguments to
+`<%- cocoaPrefix %>EdgeInsetsMake()` in Objective-C and
+`EdgeInsets(top:left:bottom:right:)` in Swift are specified in counterclockwise
+order, in contrast to the clockwise order defined by the style specification.
+
+<% if (macOS) { -%>
+Additionally, on macOS, a screen coordinate of (0, 0) is located at the
+lower-left corner of the screen. Therefore, a positive `CGVector.dy` means an
+offset or translation upward, while a negative `CGVector.dy` means an offset or
+translation downward. This is the reverse of how `CGVector` is interpreted on
+iOS.
+
+<% } -%>
+## Filtering sources
+
+You can filter a shape or vector source by setting the
+`MGLVectorStyleLayer.predicate` property to an `NSPredicate` object. Below is a
+table of style JSON operators and the corresponding operators used in the
+predicate format string:
+
+In style JSON | In the format string
+--------------------------|---------------------
+`["has", key]` | `key != nil`
+`["!has", key]` | `key == nil`
+`["==", key, value]` | `key == value`
+`["!=", key, value]` | `key != value`
+`[">", key, value]` | `key > value`
+`[">=", key, value]` | `key >= value`
+`["<", key, value]` | `key < value`
+`["<=", key, value]` | `key <= value`
+`["in", key, v0, …, vn]` | `key IN {v0, …, vn}`
+`["!in", key, v0, …, vn]` | `NOT key IN {v0, …, vn}`
+`["all", f0, …, fn]` | `p0 AND … AND pn`
+`["any", f0, …, fn]` | `p0 OR … OR pn`
+`["none", f0, …, fn]` | `NOT (p0 OR … OR pn)`
+
+See the `MGLVectorStyleLayer.predicate` documentation for a full description of
+the supported operators and operand types.
diff --git a/platform/darwin/docs/guides/Working with GeoJSON Data.md b/platform/darwin/docs/guides/Working with GeoJSON Data.md
new file mode 100644
index 0000000000..57aaa3855d
--- /dev/null
+++ b/platform/darwin/docs/guides/Working with GeoJSON Data.md
@@ -0,0 +1,99 @@
+# Working with GeoJSON Data
+
+This SDK offers several ways to work with [GeoJSON](http://geojson.org/) files.
+GeoJSON is a standard file format for representing geographic data.
+
+## Adding a GeoJSON file to the map
+
+You can use
+[Mapbox Studio’s Datasets editor](https://www.mapbox.com/studio/datasets/) to
+upload a GeoJSON file and include it in your custom map style. The GeoJSON data
+will be hosted on Mapbox servers. When a user loads your style, the SDK
+automatically loads the GeoJSON data for display.
+
+Alternatively, if you need to host the GeoJSON file elsewhere or bundle it with
+your application, you can use a GeoJSON file as the basis of an `MGLShapeSource`
+object. Pass the file’s URL into the
+`-[MGLShapeSource initWithIdentifier:URL:options:]` initializer and add the
+shape source to the map using the `-[MGLStyle addSource:]` method. The URL may
+be a local file URL, an HTTP URL, or an HTTPS URL.
+
+Once you’ve added the GeoJSON file to the map via an `MGLShapeSource` object,
+you can configure the appearance of its data and control what data is visible
+using `MGLStyleLayer` objects, you can
+[access the data programmatically](#extracting-geojson-data-from-the-map).
+
+## Converting GeoJSON data into shape objects
+
+If you have GeoJSON data in the form of source code (also known as “GeoJSON
+text”), you can convert it into an `MGLShape`, `MGLFeature`, or
+`MGLShapeCollectionFeature` object that the `MGLShapeSource` class understands
+natively. First, create an `NSData` object out of the source code string or file
+contents, then pass that data object into the
+`+[MGLShape shapeWithData:encoding:error:]` method. Finally, you can pass the
+resulting shape or feature object into the
+`-[MGLShapeSource initWithIdentifier:shape:options:]` initializer and add it to
+the map, or you can use the object and its properties to power non-map-related
+functionality in your application.
+
+To include multiple shapes in the source, create and pass an `MGLShapeCollection` or
+ `MGLShapeCollectionFeature` object to
+ `-[MGLShapeSource initWithIdentifier:shape:options:]`. Alternatively, use the
+ `-[MGLShapeSource initWithIdentifier:features:options:]` or
+ `-[MGLShapeSource initWithIdentifier:shapes:options:]` method to create a shape source
+ with an array. `-[MGLShapeSource initWithIdentifier:features:options:]` accepts only `MGLFeature`
+ instances, such as `MGLPointFeature` objects, whose attributes you can use when
+ applying a predicate to `MGLVectorStyleLayer` or configuring a style layer’s
+ appearance.
+
+## Extracting GeoJSON data from the map
+
+Any `MGLShape`, `MGLFeature`, or `MGLShapeCollectionFeature` object has an
+`-[MGLShape geoJSONDataUsingEncoding:]` method that you can use to create a
+GeoJSON source code representation of the object. You can extract a feature
+object from the map using a method such as
+`-[MGLMapView visibleFeaturesAtPoint:]`.
+
+## About GeoJSON deserialization
+
+The process of converting GeoJSON text into `MGLShape`, `MGLFeature`, or
+`MGLShapeCollectionFeature` objects is known as “GeoJSON deserialization”.
+GeoJSON geometries, features, and feature collections are known in this SDK as
+shapes, features, and shape collection features, respectively.
+
+Each GeoJSON object type corresponds to a type provided by either this SDK or
+the Core Location framework:
+
+GeoJSON object type | SDK type
+--------------------|---------
+`Position` (longitude, latitude) | `CLLocationCoordinate2D` (latitude, longitude)
+`Point` | `MGLPointAnnotation`
+`MultiPoint` | `MGLPointCollection`
+`LineString` | `MGLPolyline`
+`MultiLineString` | `MGLMultiPolyline`
+`Polygon` | `MGLPolygon`
+Linear ring | `MGLPolygon.coordinates`, `MGLPolygon.interiorPolygons`
+`MultiPolygon` | `MGLMultiPolygon`
+`GeometryCollection` | `MGLShapeCollection`
+`Feature` | `MGLFeature`
+`FeatureCollection` | `MGLShapeCollectionFeature`
+
+A `Feature` object in GeoJSON corresponds to an instance of an `MGLShape`
+subclass conforming to the `MGLFeature` protocol. There is a distinct
+`MGLFeature`-conforming class for each type of geometry that a GeoJSON feature
+can contain. This allows features to be used as shapes where convenient. For
+example, some features can be added to a map view as annotations.
+
+In contrast to the GeoJSON standard, it is possible for `MGLShape` subclasses
+other than `MGLPointAnnotation` to straddle the antimeridian.
+
+The following GeoJSON data types correspond straightforwardly to Foundation data
+types when they occur as feature identifiers or property values:
+
+GeoJSON data type | Objective-C representation | Swift representation
+-------------------|----------------------------|---------------------
+`null` | `NSNull` | `NSNull`
+`true`, `false` | `NSNumber.boolValue` | `Bool`
+Integer | `NSNumber.unsignedLongLongValue`, `NSNumber.longLongValue` | `UInt64`, `Int64`
+Floating-point number | `NSNumber.doubleValue` | `Double`
+String | `NSString` | `String`
diff --git a/platform/darwin/docs/theme/assets/css/jazzy.css.scss b/platform/darwin/docs/theme/assets/css/jazzy.css.scss
index 9c1f347142..6b4dffc879 100644
--- a/platform/darwin/docs/theme/assets/css/jazzy.css.scss
+++ b/platform/darwin/docs/theme/assets/css/jazzy.css.scss
@@ -51,6 +51,7 @@ $navigation_bg_color: #fbfbfb;
$navigation_task_color: $link_color;
$section_name_color: $color_darkblue;
+$navigation_gloss_color: #999;
// ----- Content
@@ -367,6 +368,30 @@ pre code {
color: $navigation_task_color;
}
+%nav-group-task-gloss {
+ color: $navigation_gloss_color;
+ font-size: 90%;
+ margin-left: 0.5em;
+}
+
+.nav-group-task[data-url^="Protocols/"] > .nav-group-task-link::after {
+ @extend %nav-group-task-gloss;
+ content: "(Protocol)";
+}
+
+.nav-group-task[data-name="MGLForegroundStyleLayer"],
+.nav-group-task[data-name="MGLMultiPoint"],
+.nav-group-task[data-name="MGLShape"],
+.nav-group-task[data-name="MGLSource"],
+.nav-group-task[data-name="MGLStyleLayer"],
+.nav-group-task[data-name="MGLTileSource"],
+.nav-group-task[data-name="MGLVectorStyleLayer"] {
+ .nav-group-task-link::after {
+ @extend %nav-group-task-gloss;
+ content: "(Abstract Class)";
+ }
+}
+
.nav-group-name > .small-heading,
.nav-group-task-link {
display: block;
diff --git a/platform/darwin/docs/theme/assets/js/jazzy.js b/platform/darwin/docs/theme/assets/js/jazzy.js
index 2e42193e38..e07f181acc 100755
--- a/platform/darwin/docs/theme/assets/js/jazzy.js
+++ b/platform/darwin/docs/theme/assets/js/jazzy.js
@@ -40,3 +40,8 @@ $(".token").click(function(event) {
}
event.preventDefault();
});
+
+// Dumb down quotes within code blocks that delimit strings instead of quotations.
+$("code q").replaceWith(function () {
+ return ["\"", $(this).contents(), "\""];
+});
diff --git a/platform/darwin/docs/theme/templates/nav.mustache b/platform/darwin/docs/theme/templates/nav.mustache
index 7584f49652..bfbd8e7d78 100644
--- a/platform/darwin/docs/theme/templates/nav.mustache
+++ b/platform/darwin/docs/theme/templates/nav.mustache
@@ -5,7 +5,7 @@
<a class="small-heading" href="{{path_to_root}}{{section}}.html">{{section}}<span class="anchor-icon" /></a>
<ul class="nav-group-tasks">
{{#children}}
- <li class="nav-group-task" data-name="{{name}}">
+ <li class="nav-group-task" data-name="{{name}}" data-url="{{url}}">
<a title="{{name}}" class="nav-group-task-link" href="{{path_to_root}}{{url}}">{{name}}</a>
</li>
{{/children}}
diff --git a/platform/darwin/scripts/generate-style-code.js b/platform/darwin/scripts/generate-style-code.js
index 6606c956f2..3998246580 100644
--- a/platform/darwin/scripts/generate-style-code.js
+++ b/platform/darwin/scripts/generate-style-code.js
@@ -55,17 +55,38 @@ _.forOwn(cocoaConventions, function (properties, kind) {
})
});
+String.prototype.wrap = function (cols, indent) {
+ let wrapRe = new RegExp(`(.{1,${cols - indent}})(?: +|\n|$)`, "gm");
+ return this.replace(wrapRe, "$1\n").replace(/\s+$/, "").indent(indent);
+};
+
+String.prototype.indent = function (cols) {
+ return this.replace(/^|\n/g, "$&" + " ".repeat(cols));
+};
+
+global.camelize = function (str) {
+ return str.replace(/(?:^|-)(.)/g, function (_, x) {
+ return x.toUpperCase();
+ });
+};
+
+global.camelizeWithLeadingLowercase = function (str) {
+ return str.replace(/-(.)/g, function (_, x) {
+ return x.toUpperCase();
+ });
+};
+
global.objCName = function (property) {
return camelizeWithLeadingLowercase(property.name);
-}
+};
global.objCGetter = function (property) {
return camelizeWithLeadingLowercase(property.getter || property.name);
-}
+};
global.objCType = function (layerType, propertyName) {
return `${prefix}${camelize(propertyName)}`;
-}
+};
global.arrayType = function (property) {
return property.type === 'array' ? originalPropertyName(property).split('-').pop() : false;
@@ -74,7 +95,87 @@ global.arrayType = function (property) {
global.testImplementation = function (property, layerType, isFunction) {
let helperMsg = testHelperMessage(property, layerType, isFunction);
return `layer.${objCName(property)} = [MGLRuntimeStylingHelper ${helperMsg}];`;
-}
+};
+
+global.objCTestValue = function (property, layerType, indent) {
+ let propertyName = originalPropertyName(property);
+ switch (property.type) {
+ case 'boolean':
+ return property.default ? '@NO' : '@YES';
+ case 'number':
+ return '@0xff';
+ case 'string':
+ return `@"${_.startCase(propertyName)}"`;
+ case 'enum':
+ let type = objCType(layerType, property.name);
+ let value = `${type}${camelize(_.last(_.keys(property.values)))}`;
+ return `[NSValue valueWith${type}:${value}]`;
+ case 'color':
+ return '[MGLColor redColor]';
+ case 'array':
+ switch (arrayType(property)) {
+ case 'dasharray':
+ return '@[@1, @2]';
+ case 'font':
+ return `@[@"${_.startCase(propertyName)}", @"${_.startCase(_.reverse(propertyName.split('')).join(''))}"]`;
+ case 'padding': {
+ let iosValue = '[NSValue valueWithUIEdgeInsets:UIEdgeInsetsMake(1, 1, 1, 1)]'.indent(indent * 4);
+ let macosValue = '[NSValue valueWithEdgeInsets:NSEdgeInsetsMake(1, 1, 1, 1)]'.indent(indent * 4);
+ return `\n#if TARGET_OS_IPHONE\n${iosValue}\n#else\n${macosValue}\n#endif\n${''.indent((indent - 1) * 4)}`;
+ }
+ case 'offset':
+ case 'translate':
+ let iosValue = '[NSValue valueWithCGVector:CGVectorMake(1, 1)]'.indent(indent * 4);
+ let macosValue = '[NSValue valueWithMGLVector:CGVectorMake(1, -1)]'.indent(indent * 4);
+ return `\n#if TARGET_OS_IPHONE\n${iosValue}\n#else\n${macosValue}\n#endif\n${''.indent((indent - 1) * 4)}`;
+ default:
+ throw new Error(`unknown array type for ${property.name}`);
+ }
+ default:
+ throw new Error(`unknown type for ${property.name}`);
+ }
+};
+
+global.mbglTestValue = function (property, layerType) {
+ let propertyName = originalPropertyName(property);
+ switch (property.type) {
+ case 'boolean':
+ return property.default ? 'false' : 'true';
+ case 'number':
+ return '0xff';
+ case 'string':
+ return `"${_.startCase(propertyName)}"`;
+ case 'enum': {
+ let type = camelize(originalPropertyName(property));
+ if (/-translate-anchor$/.test(originalPropertyName(property))) {
+ type = 'TranslateAnchor';
+ }
+ if (/-(rotation|pitch)-alignment$/.test(originalPropertyName(property))) {
+ type = 'Alignment';
+ }
+ let value = camelize(_.last(_.keys(property.values)));
+ return `mbgl::style::${type}Type::${value}`;
+ }
+ case 'color':
+ return '{ 1, 0, 0, 1 }';
+ case 'array':
+ switch (arrayType(property)) {
+ case 'dasharray':
+ return '{1, 2}';
+ case 'font':
+ return `{ "${_.startCase(propertyName)}", "${_.startCase(_.reverse(propertyName.split('')).join(''))}" }`;
+ case 'padding':
+ return '{ 1, 1, 1, 1 }';
+ case 'offset':
+ case 'translate':
+ return '{ 1, 1 }';
+ default:
+ throw new Error(`unknown array type for ${property.name}`);
+ }
+ default:
+ throw new Error(`unknown type for ${property.name}`);
+ }
+};
global.testGetterImplementation = function (property, layerType, isFunction) {
let helperMsg = testHelperMessage(property, layerType, isFunction);
@@ -87,7 +188,7 @@ global.testGetterImplementation = function (property, layerType, isFunction) {
XCTAssertEqualObjects(gLayer.${objCName(property)}, ${value});`;
}
return `XCTAssertEqualObjects(gLayer.${objCName(property)}, ${value});`;
-}
+};
global.testHelperMessage = function (property, layerType, isFunction) {
let fnSuffix = isFunction ? 'Function' : '';
@@ -123,7 +224,7 @@ global.testHelperMessage = function (property, layerType, isFunction) {
}
};
-global.propertyDoc = function (propertyName, property, layerType) {
+global.propertyDoc = function (propertyName, property, layerType, kind) {
// Match references to other property names & values.
// Requires the format 'When `foo` is set to `bar`,'.
let doc = property.doc.replace(/`([^`]+?)` is set to `([^`]+?)`/g, function (m, peerPropertyName, propertyValue, offset, str) {
@@ -141,7 +242,7 @@ global.propertyDoc = function (propertyName, property, layerType) {
// Requires symbols to be surrounded by backticks.
doc = doc.replace(/`(.+?)`/g, function (m, symbol, offset, str) {
if ('values' in property && Object.keys(property.values).indexOf(symbol) !== -1) {
- let objCType = objCType(layerType, property.name);
+ let objCType = global.objCType(layerType, property.name);
return '`' + `${objCType}${camelize(symbol)}` + '`';
}
if (str.substr(offset - 4, 3) !== 'CSS') {
@@ -154,11 +255,33 @@ global.propertyDoc = function (propertyName, property, layerType) {
if (!property.units.match(/s$/)) {
property.units += 's';
}
- doc += `
-
- This property is measured in ${property.units}.`;
+ doc += `\n\nThis property is measured in ${property.units}.`;
+ }
+ doc = doc.replace(/(p)ixel/gi, '$1oint').replace(/(\d)px\b/g, '$1pt');
+ if (kind !== 'enum') {
+ if ('default' in property) {
+ doc += `\n\nThe default value of this property is ${propertyDefault(property, layerType)}.`;
+ if (!property.required) {
+ doc += ' Set this property to `nil` to reset it to the default value.';
+ }
+ }
+ if ('requires' in property) {
+ doc += '\n\n' + propertyReqs(property, spec[`${kind}_${layerType}`], layerType);
+ }
+ if ('original' in property) {
+ let anchor;
+ switch (kind) {
+ case 'layout':
+ anchor = `layout-${layerType}-${property.original}`;
+ break;
+ case 'paint':
+ anchor = `paint-${property.original}`;
+ break;
+ }
+ doc += `\n\nThis attribute corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#${anchor}"><code>${property.original}</code></a> layout property in the Mapbox Style Specification.`;
+ }
}
- return doc.replace(/(p)ixel/gi, '$1oint').replace(/(\d)px\b/g, '$1pt');
+ return doc;
};
global.propertyReqs = function (property, propertiesByName, type) {
@@ -217,30 +340,32 @@ global.describeValue = function (value, property, layerType) {
throw new Error(`unrecognized color format in default value of ${property.name}`);
}
if (color.r === 0 && color.g === 0 && color.b === 0 && color.a === 0) {
- return '`MGLColor.clearColor`';
+ return '`UIColor.clearColor`';
}
if (color.r === 0 && color.g === 0 && color.b === 0 && color.a === 1) {
- return '`MGLColor.blackColor`';
+ return '`UIColor.blackColor`';
}
if (color.r === 1 && color.g === 1 && color.b === 1 && color.a === 1) {
- return '`MGLColor.whiteColor`';
+ return '`UIColor.whiteColor`';
}
- return 'an `MGLColor`' + ` object whose RGB value is ${color.r}, ${color.g}, ${color.b} and whose alpha value is ${color.a}`;
+ return 'a `UIColor`' + ` object whose RGB value is ${color.r}, ${color.g}, ${color.b} and whose alpha value is ${color.a}`;
case 'array':
let units = property.units || '';
if (units) {
units = ` ${units}`.replace(/pixel/, 'point');
}
- if (property.name.indexOf('padding') !== -1) {
- if (value[0] === 0 && value[1] === 0 && value[2] === 0 && value[3] === 0) {
- return 'an `NSValue` object containing `NSEdgeInsetsZero` or `UIEdgeInsetsZero`';
- }
- return 'an `NSValue` object containing an `NSEdgeInsets` or `UIEdgeInsets` struct set to' + ` ${value[0]}${units} on the top, ${value[3]}${units} on the left, ${value[2]}${units} on the bottom, and ${value[1]}${units} on the right`;
- }
- if (property.name.indexOf('offset') !== -1 || property.name.indexOf('translate') !== -1) {
- return 'an `NSValue` object containing a `CGVector` struct set to' + ` ${value[0]}${units} from the left and ${value[1]}${units} from the top`;
+ switch (arrayType(property)) {
+ case 'padding':
+ if (value[0] === 0 && value[1] === 0 && value[2] === 0 && value[3] === 0) {
+ return 'an `NSValue` object containing `UIEdgeInsetsZero`';
+ }
+ return 'an `NSValue` object containing a `UIEdgeInsets` struct set to' + ` ${value[0]}${units} on the top, ${value[3]}${units} on the left, ${value[2]}${units} on the bottom, and ${value[1]}${units} on the right`;
+ case 'offset':
+ case 'translate':
+ return 'an `NSValue` object containing a `CGVector` struct set to' + ` ${value[0]}${units} rightward and ${value[1]}${units} downward`;
+ default:
+ return 'the array `' + value.join('`, `') + '`';
}
- return 'the array `' + value.join('`, `') + '`';
default:
throw new Error(`unknown type for ${property.name}`);
}
@@ -252,7 +377,7 @@ global.propertyDefault = function (property, layerType) {
global.originalPropertyName = function (property) {
return property.original || property.name;
-}
+};
global.propertyType = function (property) {
switch (property.type) {
@@ -317,100 +442,144 @@ global.valueTransformerArguments = function (property) {
}
};
+global.mbglType = function(property) {
+ switch (property.type) {
+ case 'boolean':
+ return 'bool';
+ case 'number':
+ return 'float';
+ case 'string':
+ return 'std::string';
+ case 'enum': {
+ let type = camelize(originalPropertyName(property));
+ if (/-translate-anchor$/.test(originalPropertyName(property))) {
+ type = 'TranslateAnchor';
+ }
+ if (/-(rotation|pitch)-alignment$/.test(originalPropertyName(property))) {
+ type = 'Alignment';
+ }
+ return `mbgl::style::${type}Type`;
+ }
+ case 'color':
+ return 'mbgl::Color';
+ case 'array':
+ switch (arrayType(property)) {
+ case 'dasharray':
+ return 'std::vector<float>';
+ case 'font':
+ return 'std::vector<std::string>';
+ case 'padding':
+ return 'std::array<float, 4>';
+ case 'offset':
+ case 'translate':
+ return 'std::array<float, 2>';
+ default:
+ throw new Error(`unknown array type for ${property.name}`);
+ }
+ default:
+ throw new Error(`unknown type for ${property.name}`);
+ }
+};
+
global.initLayer = function (layerType) {
if (layerType == "background") {
return `_layer = new mbgl::style::${camelize(layerType)}Layer(identifier.UTF8String);`
} else {
return `_layer = new mbgl::style::${camelize(layerType)}Layer(identifier.UTF8String, source.identifier.UTF8String);`
}
-}
+};
global.setSourceLayer = function() {
- return `_layer->setSourceLayer(sourceLayer.UTF8String);`
-}
-
-global.mbglType = function(property) {
- let mbglType = camelize(originalPropertyName(property)) + 'Type';
- if (/-translate-anchor$/.test(originalPropertyName(property))) {
- mbglType = 'TranslateAnchorType';
- }
- if (/-(rotation|pitch)-alignment$/.test(originalPropertyName(property))) {
- mbglType = 'AlignmentType';
- }
- return mbglType;
-}
+ return `_layer->setSourceLayer(sourceLayer.UTF8String);`
+};
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.m.ejs', 'utf8'), { strict: true});
-const categoryH = ejs.compile(fs.readFileSync('platform/darwin/src/NSValue+MGLStyleEnumAttributeAdditions.h.ejs', 'utf8'), { strict: true});
-const categoryM = ejs.compile(fs.readFileSync('platform/darwin/src/NSValue+MGLStyleEnumAttributeAdditions.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 layers = Object.keys(spec.layer.type.values).map((type) => {
- const layoutProperties = Object.keys(spec[`layout_${type}`]).reduce((memo, name) => {
+const layers = _(spec.layer.type.values).map((value, layerType) => {
+ const layoutProperties = Object.keys(spec[`layout_${layerType}`]).reduce((memo, name) => {
if (name !== 'visibility') {
- spec[`layout_${type}`][name].name = name;
- memo.push(spec[`layout_${type}`][name]);
+ spec[`layout_${layerType}`][name].name = name;
+ memo.push(spec[`layout_${layerType}`][name]);
}
return memo;
}, []);
- const paintProperties = Object.keys(spec[`paint_${type}`]).reduce((memo, name) => {
- spec[`paint_${type}`][name].name = name;
- memo.push(spec[`paint_${type}`][name]);
+ const paintProperties = Object.keys(spec[`paint_${layerType}`]).reduce((memo, name) => {
+ spec[`paint_${layerType}`][name].name = name;
+ memo.push(spec[`paint_${layerType}`][name]);
return memo;
}, []);
return {
- type: type,
+ doc: spec.layer.type.values[layerType].doc,
+ type: layerType,
layoutProperties: _.sortBy(layoutProperties, ['name']),
paintProperties: _.sortBy(paintProperties, ['name']),
- layoutPropertiesByName: spec[`layout_${type}`],
- paintPropertiesByName: spec[`paint_${type}`],
};
-});
+}).sortBy(['type']).value();
function duplicatePlatformDecls(src) {
- // Look for a documentation comment that contains “MGLColor” and the
- // subsequent function, method, or property declaration. Try not to match
- // greedily.
- return src.replace(/(\/\*\*(?:\*[^\/]|[^*])*?\bMGLColor\b[\s\S]*?\*\/)(\s*.+?;)/g,
+ // Look for a documentation comment that contains “MGLColor” or “UIColor”
+ // and the subsequent function, method, or property declaration. Try not to
+ // match greedily.
+ return src.replace(/(\/\*\*(?:\*[^\/]|[^*])*?\*\/)(\s*[^;]+?\b(?:MGL|NS|UI)(?:Color|EdgeInsets(?:Zero)?)\b[^;]+?;)/g,
(match, comment, decl) => {
- let iosComment = comment.replace(/\bMGLColor\b/g, 'UIColor')
+ let macosComment = comment.replace(/\b(?:MGL|UI)(Color|EdgeInsets(?:Zero)?)\b/g, 'NS$1')
// Use the correct indefinite article.
- .replace(/\b(a)n(\s+`?UIColor)\b/gi, '$1$2');
- let macosComment = comment.replace(/\bMGLColor\b/g, 'NSColor');
+ .replace(/\ba(\s+`?NS(?:Color|EdgeInsets))\b/gi, 'an$1');
+ let iosDecl = decl.replace(/\bMGL(Color|EdgeInsets)\b/g, 'UI$1');
+ let macosDecl = decl.replace(/\b(?:MGL|UI)(Color|EdgeInsets)\b/g, 'NS$1');
return `\
#if TARGET_OS_IPHONE
-${iosComment}${decl}
+${comment}${iosDecl}
+#else
+${macosComment}${macosDecl}
+#endif`;
+ })
+ // Do the same for CGVector-typed properties.
+ .replace(/(\/\*\*(?:\*[^\/]|[^*])*?\b(?:CGVector|UIEdgeInsets(?:Zero)?)\b[\s\S]*?\*\/)(\s*.+?;)/g,
+ (match, comment, decl) => {
+ let macosComment = comment.replace(/\bdownward\b/g, 'upward')
+ .replace(/\bUI(EdgeInsets(?:Zero)?)\b/g, 'NS$1');
+ return `\
+#if TARGET_OS_IPHONE
+${comment}${decl}
#else
${macosComment}${decl}
#endif`;
});
}
-var allLayoutProperties = [];
-var allPaintProperties = [];
-var allTypes = [];
+var renamedPropertiesByLayerType = {};
for (var layer of layers) {
- allLayoutProperties.push(layer.layoutProperties);
- allPaintProperties.push(layer.paintProperties);
- allTypes.push(layer.type);
- const containsEnumerationProperties = _.filter(layer.layoutProperties, function(property){ return property["type"] === "enum"; }).length || _.filter(layer.paintProperties, function(property){ return property["type"] === "enum"; }).length;
- layer.containsEnumerationProperties = containsEnumerationProperties;
+ layer.properties = _.concat(layer.layoutProperties, layer.paintProperties);
+ let enumProperties = _.filter(layer.properties, prop => prop.type === 'enum');
+ if (enumProperties.length) {
+ layer.enumProperties = enumProperties;
+ }
+
+ let renamedProperties = {};
+ _.assign(renamedProperties, _.filter(layer.properties, prop => 'original' in prop || 'getter' in prop));
+ if (!_.isEmpty(renamedProperties)) {
+ renamedPropertiesByLayerType[layer.type] = renamedProperties;
+ }
- writeIfModified(`platform/darwin/src/${prefix}${camelize(layer.type)}${suffix}.h`, duplicatePlatformDecls(layerH(layer)));
- writeIfModified(`platform/darwin/src/${prefix}${camelize(layer.type)}${suffix}.mm`, layerM(layer));
- writeIfModified(`platform/darwin/test/${prefix}${camelize(layer.type)}${suffix}Tests.m`, testLayers(layer));
+ fs.writeFileSync(`platform/darwin/src/${prefix}${camelize(layer.type)}${suffix}.h`, duplicatePlatformDecls(layerH(layer)));
+ fs.writeFileSync(`platform/darwin/src/${prefix}${camelize(layer.type)}${suffix}.mm`, layerM(layer));
+ fs.writeFileSync(`platform/darwin/test/${prefix}${camelize(layer.type)}${suffix}Tests.mm`, testLayers(layer));
}
-fs.writeFileSync(`platform/darwin/src/NSValue+MGLStyleEnumAttributeAdditions.h`, categoryH({
- layoutProperties: _.flatten(allLayoutProperties),
- paintProperties: _.flatten(allPaintProperties),
- types: allTypes
+fs.writeFileSync(`platform/ios/docs/guides/For Style Authors.md`, guideMD({
+ os: 'iOS',
+ renamedProperties: renamedPropertiesByLayerType,
+ layers: layers,
}));
-fs.writeFileSync(`platform/darwin/src/NSValue+MGLStyleEnumAttributeAdditions.mm`, categoryM({
- layoutProperties: _.flatten(allLayoutProperties),
- paintProperties: _.flatten(allPaintProperties)
+fs.writeFileSync(`platform/macos/docs/guides/For Style Authors.md`, guideMD({
+ os: 'macOS',
+ renamedProperties: renamedPropertiesByLayerType,
+ layers: layers,
}));
diff --git a/platform/darwin/scripts/style-spec-cocoa-conventions-v8.json b/platform/darwin/scripts/style-spec-cocoa-conventions-v8.json
index 49cf515e3e..9d80ff3896 100644
--- a/platform/darwin/scripts/style-spec-cocoa-conventions-v8.json
+++ b/platform/darwin/scripts/style-spec-cocoa-conventions-v8.json
@@ -9,13 +9,26 @@
"icon-size": "icon-scale",
"symbol-avoid-edges": "symbol-avoids-edges",
"text-allow-overlap": "text-allows-overlap",
+ "text-field": "text",
+ "text-font": "text-font-names",
"text-ignore-placement": "text-ignores-placement",
"text-justify": "text-justification",
"text-keep-upright": "keeps-text-upright",
"text-max-angle": "maximum-text-angle",
"text-max-width": "maximum-text-width",
"text-optional": "is-text-optional",
- "text-rotate": "text-rotation"
+ "text-rotate": "text-rotation",
+ "text-size": "text-font-size"
+ },
+ "paint_circle": {
+ "circle-pitch-scale": "circle-scale-alignment",
+ "circle-translate": "circle-translation",
+ "circle-translate-anchor": "circle-translation-anchor"
+ },
+ "paint_fill": {
+ "fill-antialias": "is-fill-antialiased",
+ "fill-translate": "fill-translation",
+ "fill-translate-anchor": "fill-translation-anchor"
},
"paint_raster": {
"raster-brightness-min": "minimum-raster-brightness",
@@ -23,9 +36,14 @@
"raster-hue-rotate": "raster-hue-rotation"
},
"paint_line": {
- "line-dasharray": "line-dash-pattern"
+ "line-dasharray": "line-dash-pattern",
+ "line-translate": "line-translation",
+ "line-translate-anchor": "line-translation-anchor"
},
- "paint_fill": {
- "fill-antialias": "is-fill-antialiased"
+ "paint_symbol": {
+ "icon-translate": "icon-translation",
+ "icon-translate-anchor": "icon-translation-anchor",
+ "text-translate": "text-translation",
+ "text-translate-anchor": "text-translation-anchor"
}
} \ No newline at end of file
diff --git a/platform/darwin/scripts/style-spec-overrides-v8.json b/platform/darwin/scripts/style-spec-overrides-v8.json
index cfda85b7fb..28740d458b 100644
--- a/platform/darwin/scripts/style-spec-overrides-v8.json
+++ b/platform/darwin/scripts/style-spec-overrides-v8.json
@@ -1,4 +1,28 @@
{
+ "layer": {
+ "type": {
+ "values": {
+ "fill": {
+ "doc": "An `MGLFillStyleLayer` is a style layer that renders one or more filled (and optionally stroked) polygons on the map.\n\nUse a fill style layer to configure the visual appearance of polygon or multipolygon features in vector tiles loaded by an `MGLVectorSource` object or `MGLPolygon`, `MGLPolygonFeature`, `MGLMultiPolygon`, or `MGLMultiPolygonFeature` instances in an `MGLShapeSource` object."
+ },
+ "line": {
+ "doc": "An `MGLLineStyleLayer` is a style layer that renders one or more stroked polylines on the map.\n\nUse a line style layer to configure the visual appearance of polyline or multipolyline features in vector tiles loaded by an `MGLVectorSource` object or `MGLPolyline`, `MGLPolylineFeature`, `MGLMultiPolyline`, or `MGLMultiPolylineFeature` instances in an `MGLShapeSource` object."
+ },
+ "symbol": {
+ "doc": "An `MGLSymbolStyleLayer` is a style layer that renders icon and text labels at points or along lines on the map.\n\nUse a symbol style layer to configure the visual appearance of labels for features in vector tiles loaded by an `MGLVectorSource` object or `MGLShape` or `MGLFeature` instances in an `MGLShapeSource` object."
+ },
+ "circle": {
+ "doc": "An `MGLCircleStyleLayer` is a style layer that renders one or more filled circles on the map.\n\nUse a circle style layer to configure the visual appearance of point or point collection features in vector tiles loaded by an `MGLVectorSource` object or `MGLPointAnnotation`, `MGLPointFeature`, `MGLPointCollection`, or `MGLPointCollectionFeature` instances in an `MGLShapeSource` object.\n\nA circle style layer renders circles whose radii are measured in screen units. To display circles on the map whose radii correspond to real-world distances, use many-sided regular polygons and configure their appearance using an `MGLFillStyleLayer` object."
+ },
+ "raster": {
+ "doc": "An `MGLRasterStyleLayer` is a style layer that renders raster tiles on the map.\n\nUse a raster style layer to configure the color parameters of raster tiles loaded by an `MGLRasterSource` object. For example, you could use a raster style layer to render <a href=\"https://www.mapbox.com/satellite/\">Mapbox Satellite</a> imagery, a <a href=\"https://www.mapbox.com/help/define-tileset/#raster-tilesets\">raster tile set</a> uploaded to Mapbox Studio, or a raster map authored in <a href=\"https://tilemill-project.github.io/tilemill/\">TileMill</a>, the classic Mapbox Editor, or Mapbox Studio Classic."
+ },
+ "background": {
+ "doc": "An `MGLBackgroundStyleLayer` is a style layer that covers the entire map. Use a background style layer to configure a color or pattern to show below all other map content. If the style’s other layers use the Mapbox Streets source, the background style layer is responsible for drawing land, whereas the oceans and other bodies of water are drawn by `MGLFillStyleLayer` objects.\n\nA background style layer is typically the bottommost layer in a style, because it covers the entire map and can occlude any layers below it. You can therefore access it by getting the last item in the `MGLStyle.layers` array.\n\nIf the background style layer is transparent or omitted from the style, any portion of the map view that does not show another style layer is transparent."
+ }
+ }
+ }
+ },
"layout_symbol": {
"icon-text-fit-padding": {
"doc": "Size of the additional area added to dimensions determined by `icon-text-fit`."
@@ -6,6 +30,9 @@
"icon-offset": {
"doc": "Offset distance of icon from its anchor."
},
+ "text-font": {
+ "doc": "An array of font face names used to display the text.\n\nEach font name must be included in the `{fontstack}` portion of the JSON stylesheet’s <a href=\"https://www.mapbox.com/mapbox-gl-style-spec/#glyphs\"><code>glyphs</code></a> property. You can register a custom font when designing the style in Mapbox Studio. Fonts installed on the system are not used.\n\nThe first font named in the array is applied to the text. For each character in the text, if the first font lacks a glyph for the character, the next font is applied as a fallback, and so on."
+ },
"text-offset": {
"doc": "Offset distance of text from its anchor."
},
diff --git a/platform/darwin/scripts/update-examples.js b/platform/darwin/scripts/update-examples.js
new file mode 100644
index 0000000000..6291326068
--- /dev/null
+++ b/platform/darwin/scripts/update-examples.js
@@ -0,0 +1,152 @@
+'use strict';
+
+const fs = require('fs');
+const execFileSync = require('child_process').execFileSync;
+const _ = require('lodash');
+
+const examplesSrc = fs.readFileSync('platform/darwin/test/MGLDocumentationExampleTests.swift', 'utf8');
+
+// Regex extracts the following block
+// /** Front matter to describe the example. **/
+// func testMGLClass$member() {
+// ...
+// //#-example-code
+// let sampleCode: String?
+// //#-end-example-code
+// ...
+// }
+//
+// into the following regex groups:
+// 1 (test method name): "MGLClass" or "MGLClass$member" or "MGLClass$initWithArg_anotherArg_"
+// 2 (indentation): " "
+// 3 (sample code): "let sampleCode: String?"
+const exampleRegex = /func test([\w$]+)\s*\(\)\s*\{[^]*?\n([ \t]+)\/\/#-example-code\n([^]+?)\n\2\/\/#-end-example-code\n/gm;
+
+/**
+ * Returns the given source with example code inserted into the documentation
+ * comment for the symbol at the given one-based line.
+ *
+ * @param {String} src Source code to insert the example code into.
+ * @param {Number} line One-based line number of the symbol being documented.
+ * @param {String} exampleCode Example code to insert.
+ * @returns {String} `src` with `exampleCode` inserted just above `line`.
+ */
+function completeSymbolInSource(src, line, exampleCode) {
+ // Split the file contents right before the symbol declaration (but after its documentation comment).
+ let srcUpToSymbol = src.split('\n', line - 1).join('\n');
+ let srcFromSymbol = src.substr(srcUpToSymbol.length);
+
+ // Match the documentation comment block that is not followed by the beginning or end of a declaration.
+ let commentMatch = srcUpToSymbol.match(/\/\*\*\s*(?:[^*]|\*(?!\/))+?\s*\*\/[^;{}]*?$/);
+
+ // Replace the Swift code block with the test method’s contents.
+ let completedComment = commentMatch[0].replace(/^([ \t]*)```swift\n[^]*?```/m, function (m, indentation) {
+ // Apply the original indentation to each line.
+ return ('```swift\n' + exampleCode + '\n```').replace(/^/gm, indentation);
+ });
+
+ // Splice the modified comment into the overall file contents.
+ srcUpToSymbol = (srcUpToSymbol.substr(0, commentMatch.index) + completedComment +
+ srcUpToSymbol.substr(commentMatch.index + commentMatch[0].length));
+ return srcUpToSymbol + srcFromSymbol;
+}
+
+let examples = {};
+let match;
+while ((match = exampleRegex.exec(examplesSrc)) !== null) {
+ let testMethodName = match[1],
+ indentation = match[2],
+ exampleCode = match[3];
+
+ // Trim leading whitespace from the example code.
+ exampleCode = exampleCode.replace(new RegExp('^' + indentation, 'gm'), '');
+
+ examples[testMethodName] = exampleCode;
+}
+
+function completeExamples(os) {
+ console.log(`Installing ${os} SDK examples…`);
+
+ let sdk = os === 'iOS' ? 'iphonesimulator' : 'macosx';
+ let sysroot = execFileSync('xcrun', ['--show-sdk-path', '--sdk', sdk]).toString().trim();
+ let umbrellaPath = `platform/${os.toLowerCase()}/src/Mapbox.h`;
+ let docArgs = ['doc', '--objc', umbrellaPath, '--',
+ '-x', 'objective-c', '-I', 'platform/darwin/src/', '-isysroot', sysroot];
+ let docStr = execFileSync('sourcekitten', docArgs).toString().trim();
+ let docJson = JSON.parse(docStr);
+ _.forEach(docJson, function (result) {
+ _.forEach(result, function (structure, path) {
+ let src;
+ let newSrc;
+ // Recursively search for code blocks in documentation comments and populate
+ // them with example code from the test methods. Find and replace the code
+ // blocks in reverse to keep the SourceKitten line numbers accurate.
+ _.forEachRight(structure['key.substructure'], function completeSubstructure(substructure, idx, substructures, symbolPath) {
+ if (!symbolPath) {
+ symbolPath = [substructure['key.name']];
+ }
+ _.forEachRight(substructure['key.substructure'], function (substructure, idx, substructures) {
+ completeSubstructure(substructure, idx, substructures, _.concat(symbolPath, substructure['key.name']));
+ });
+
+ let comment = substructure['key.doc.comment'];
+ if (!comment || !comment.match(/^(?:\s*)```swift\n/m)) {
+ return;
+ }
+
+ // Lazily read in the existing file.
+ if (!src) {
+ newSrc = src = fs.readFileSync(path, 'utf8');
+ }
+
+ // Get the contents of the test method whose name matches the symbol path.
+ let testMethodName = symbolPath.join('$').replace(/\$[+-]/, '$').replace(/:/g, '_');
+ let example = examples[testMethodName];
+ if (!example) {
+ console.error(`MGLDocumentationExampleTests.${testMethodName}() not found.`);
+ process.exit(1);
+ }
+
+ // Resolve conditional compilation blocks.
+ example = example.replace(/^(\s*)#if\s+os\((iOS|macOS)\)\n([^]*?)(?:^\1#else\n([^]*?))?^\1#endif\n/gm,
+ function (m, indentation, ifOs, ifCase, elseCase) {
+ return (os === ifOs ? ifCase : elseCase).replace(new RegExp('^ ', 'gm'), '');
+ });
+
+ // Insert the test method contents into the documentation comment just
+ // above the substructure.
+ let startLine = substructure['key.parsed_scope.start'];
+ newSrc = completeSymbolInSource(newSrc, startLine, example);
+ });
+
+ if (!src) {
+ return;
+ }
+
+ // Write out the modified file contents.
+ if (src === newSrc) {
+ console.log('Skipping', path);
+ } else {
+ console.log('Updating', path);
+ if (['0', 'false'].indexOf(process.env.DRY_RUN || '0') !== -1) {
+ fs.writeFileSync(path, newSrc);
+ }
+ }
+ });
+ });
+}
+
+function ensureSourceKittenIsInstalled() {
+ try {
+ execFileSync('which', ['sourcekitten']);
+ } catch (e) {
+ console.log(`Installing SourceKitten via Homebrew…`);
+ execFileSync('brew', ['install', 'sourcekitten']);
+ }
+}
+
+ensureSourceKittenIsInstalled();
+
+// Where a particular comment is part of both SDKs, prefer the iOS example.
+completeExamples('macOS');
+completeExamples('iOS');
diff --git a/platform/darwin/src/MGLAttributionInfo.h b/platform/darwin/src/MGLAttributionInfo.h
index 28b4fe50e6..c4e037059e 100644
--- a/platform/darwin/src/MGLAttributionInfo.h
+++ b/platform/darwin/src/MGLAttributionInfo.h
@@ -51,7 +51,7 @@ MGL_EXPORT
coordinate and zoom level.
@param centerCoordinate The map’s center coordinate.
- @param zoomLevel The map’s zoom level. See `MGLMapView`’s `zoomLevel` property
+ @param zoomLevel The map’s zoom level. See the `MGLMapView.zoomLevel` property
for more information.
@return A modified URL containing a fragment that points to the specified
viewport. If the `feedbackLink` property is set to `NO`, this method returns
diff --git a/platform/darwin/src/MGLAttributionInfo.mm b/platform/darwin/src/MGLAttributionInfo.mm
index cf7b3cb22f..2546808b34 100644
--- a/platform/darwin/src/MGLAttributionInfo.mm
+++ b/platform/darwin/src/MGLAttributionInfo.mm
@@ -26,9 +26,20 @@
// Apply a bogus, easily detectable style rule to any feedback link, since
// NSAttributedString doesn’t preserve the class attribute.
NSMutableString *css = [NSMutableString stringWithString:
+ @"html { font-family: -apple-system, -apple-system-font, sans-serif; }"
@".mapbox-improve-map { -webkit-text-stroke-width: 1000px; }"];
if (fontSize) {
- [css appendFormat:@"html { font-size: %.1fpx; }", fontSize];
+ NSString *sizeRule = [NSString stringWithFormat:@"font-size: %.1fpx;", fontSize];
+#if !TARGET_OS_IPHONE
+ if (fontSize == [NSFont systemFontSizeForControlSize:NSMiniControlSize]) {
+ sizeRule = @"font: -webkit-mini-control";
+ } else if (fontSize == [NSFont systemFontSizeForControlSize:NSSmallControlSize]) {
+ sizeRule = @"font: -webkit-small-control";
+ } else if (fontSize == [NSFont systemFontSizeForControlSize:NSRegularControlSize]) {
+ sizeRule = @"font: -webkit-control";
+ }
+#endif
+ [css appendFormat:@"html { %@ }", sizeRule];
}
if (linkColor) {
CGFloat red;
diff --git a/platform/darwin/src/MGLBackgroundStyleLayer.h b/platform/darwin/src/MGLBackgroundStyleLayer.h
index e0773d14d9..06d7c9ee08 100644
--- a/platform/darwin/src/MGLBackgroundStyleLayer.h
+++ b/platform/darwin/src/MGLBackgroundStyleLayer.h
@@ -8,10 +8,18 @@
NS_ASSUME_NONNULL_BEGIN
/**
- A map style's background layer is the bottommost layer and is used to style a color
- or pattern to show below all other map features. You can query an `MGLMapView` for its
- `style` and obtain the background layer using the `-[MGLStyle layerWithIdentifier:]`
- method and passing `background` for the identifier.
+ An `MGLBackgroundStyleLayer` is a style layer that covers the entire map. Use a
+ background style layer to configure a color or pattern to show below all other
+ map content. If the style’s other layers use the Mapbox Streets source, the
+ background style layer is responsible for drawing land, whereas the oceans and
+ other bodies of water are drawn by `MGLFillStyleLayer` objects.
+
+ A background style layer is typically the bottommost layer in a style, because
+ it covers the entire map and can occlude any layers below it. You can therefore
+ access it by getting the last item in the `MGLStyle.layers` array.
+
+ If the background style layer is transparent or omitted from the style, any
+ portion of the map view that does not show another style layer is transparent.
*/
MGL_EXPORT
@interface MGLBackgroundStyleLayer : MGLStyleLayer
@@ -24,31 +32,41 @@ MGL_EXPORT
/**
The color with which the background will be drawn.
- The default value of this property is an `MGLStyleValue` object containing `UIColor.blackColor`. Set this property to `nil` to reset it to the default value.
-
- This property is only applied to the style if `backgroundPattern` is set to `nil`. Otherwise, it is ignored.
+ The default value of this property is an `MGLStyleValue` object containing
+ `UIColor.blackColor`. Set this property to `nil` to reset it to the default
+ value.
+
+ This property is only applied to the style if `backgroundPattern` is set to
+ `nil`. Otherwise, it is ignored.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<MGLColor *> *backgroundColor;
+@property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *backgroundColor;
#else
/**
The color with which the background will be drawn.
- The default value of this property is an `MGLStyleValue` object containing `NSColor.blackColor`. Set this property to `nil` to reset it to the default value.
-
- This property is only applied to the style if `backgroundPattern` is set to `nil`. Otherwise, it is ignored.
+ The default value of this property is an `MGLStyleValue` object containing
+ `NSColor.blackColor`. Set this property to `nil` to reset it to the default
+ value.
+
+ This property is only applied to the style if `backgroundPattern` is set to
+ `nil`. Otherwise, it is ignored.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<MGLColor *> *backgroundColor;
+@property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *backgroundColor;
#endif
/**
The opacity at which the background will be drawn.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing the float `1`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `1`. Set this property to `nil` to reset
+ it to the default value.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *backgroundOpacity;
/**
- Name of image in style images to use for drawing an image background. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512).
+ Name of image in style images to use for drawing an image background. For
+ seamless patterns, image width and height must be a factor of two (2, 4, 8,
+ ..., 512).
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSString *> *backgroundPattern;
diff --git a/platform/darwin/src/MGLBackgroundStyleLayer.mm b/platform/darwin/src/MGLBackgroundStyleLayer.mm
index f6a00de941..caf8ca9681 100644
--- a/platform/darwin/src/MGLBackgroundStyleLayer.mm
+++ b/platform/darwin/src/MGLBackgroundStyleLayer.mm
@@ -1,5 +1,5 @@
// This file is generated.
-// Edit platform/darwin/scripts/generate-style-code.js, then run `make style-code-darwin`.
+// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
#import "MGLSource.h"
#import "MGLMapView_Private.h"
@@ -8,6 +8,7 @@
#import "MGLStyleValue_Private.h"
#import "MGLBackgroundStyleLayer.h"
+#include <mbgl/map/map.hpp>
#include <mbgl/style/layers/background_layer.hpp>
@interface MGLBackgroundStyleLayer ()
diff --git a/platform/darwin/src/MGLCircleStyleLayer.h b/platform/darwin/src/MGLCircleStyleLayer.h
index 17ac3e5881..8d8c4588e7 100644
--- a/platform/darwin/src/MGLCircleStyleLayer.h
+++ b/platform/darwin/src/MGLCircleStyleLayer.h
@@ -10,40 +10,71 @@ NS_ASSUME_NONNULL_BEGIN
/**
Controls the scaling behavior of the circle when the map is pitched.
- Values of this type are used in the `circlePitchScale` property of `MGLCircleStyleLayer`.
+ Values of this type are used in the `MGLCircleStyleLayer.circleScaleAlignment`
+ property.
*/
-typedef NS_ENUM(NSUInteger, MGLCirclePitchScale) {
+typedef NS_ENUM(NSUInteger, MGLCircleScaleAlignment) {
/**
Circles are scaled according to their apparent distance to the camera.
*/
- MGLCirclePitchScaleMap,
+ MGLCircleScaleAlignmentMap,
/**
Circles are not scaled.
*/
- MGLCirclePitchScaleViewport,
+ MGLCircleScaleAlignmentViewport,
};
/**
Controls the translation reference point.
- Values of this type are used in the `circleTranslateAnchor` property of `MGLCircleStyleLayer`.
+ Values of this type are used in the `MGLCircleStyleLayer.circleTranslationAnchor`
+ property.
*/
-typedef NS_ENUM(NSUInteger, MGLCircleTranslateAnchor) {
+typedef NS_ENUM(NSUInteger, MGLCircleTranslationAnchor) {
/**
The circle is translated relative to the map.
*/
- MGLCircleTranslateAnchorMap,
+ MGLCircleTranslationAnchorMap,
/**
The circle is translated relative to the viewport.
*/
- MGLCircleTranslateAnchorViewport,
+ MGLCircleTranslationAnchorViewport,
};
/**
- A circle layer which allows customization of styling properties at runtime. You may
- instantiate a new circle layer to add to a map style or you may query an
- `MGLMapView` for its `style` and obtain existing layers using the
- `-[MGLStyle layerWithIdentifier:]` method.
+ An `MGLCircleStyleLayer` is a style layer that renders one or more filled
+ circles on the map.
+
+ Use a circle style layer to configure the visual appearance of point or point
+ collection features in vector tiles loaded by an `MGLVectorSource` object or
+ `MGLPointAnnotation`, `MGLPointFeature`, `MGLPointCollection`, or
+ `MGLPointCollectionFeature` instances in an `MGLShapeSource` object.
+
+ A circle style layer renders circles whose radii are measured in screen units.
+ To display circles on the map whose radii correspond to real-world distances,
+ use many-sided regular polygons and configure their appearance using an
+ `MGLFillStyleLayer` object.
+
+ You can access an existing circle style layer using the
+ `-[MGLStyle layerWithIdentifier:]` method if you know its identifier;
+ otherwise, find it using the `MGLStyle.layers` property. You can also create a
+ new circle style layer and add it to the style using a method such as
+ `-[MGLStyle addLayer:]`.
+
+ ### Example
+
+ ```swift
+ let layer = MGLCircleStyleLayer(identifier: "circles", source: population)
+ layer.sourceLayerIdentifier = "population"
+ layer.circleColor = MGLStyleValue(rawValue: .green)
+ layer.circleRadius = MGLStyleValue(interpolationBase: 1.75, stops: [
+ 12: MGLStyleValue(rawValue: 2),
+ 22: MGLStyleValue(rawValue: 180)
+ ])
+ layer.circleOpacity = MGLStyleValue(rawValue: 0.7)
+ layer.predicate = NSPredicate(format: "%K == %@", "marital-status", "married")
+ mapView.style?.addLayer(layer)
+ ```
*/
MGL_EXPORT
@interface MGLCircleStyleLayer : MGLVectorStyleLayer
@@ -51,9 +82,12 @@ MGL_EXPORT
#pragma mark - Accessing the Paint Attributes
/**
- Amount to blur the circle. 1 blurs the circle such that only the centerpoint is full opacity.
+ Amount to blur the circle. 1 blurs the circle such that only the centerpoint is
+ full opacity.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing the float `0`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `0`. Set this property to `nil` to reset
+ it to the default value.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *circleBlur;
@@ -61,90 +95,185 @@ MGL_EXPORT
/**
The fill color of the circle.
- The default value of this property is an `MGLStyleValue` object containing `UIColor.blackColor`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing
+ `UIColor.blackColor`. Set this property to `nil` to reset it to the default
+ value.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<MGLColor *> *circleColor;
+@property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *circleColor;
#else
/**
The fill color of the circle.
- The default value of this property is an `MGLStyleValue` object containing `NSColor.blackColor`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing
+ `NSColor.blackColor`. Set this property to `nil` to reset it to the default
+ value.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<MGLColor *> *circleColor;
+@property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *circleColor;
#endif
/**
The opacity at which the circle will be drawn.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing the float `1`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `1`. Set this property to `nil` to reset
+ it to the default value.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *circleOpacity;
/**
- Controls the scaling behavior of the circle when the map is pitched.
+ Circle radius.
- The default value of this property is an `MGLStyleValue` object containing an `NSValue` object containing `MGLCirclePitchScaleMap`. Set this property to `nil` to reset it to the default value.
+ This property is measured in points.
+
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `5`. Set this property to `nil` to reset
+ it to the default value.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *circlePitchScale;
+@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *circleRadius;
/**
- Circle radius.
-
- This property is measured in points.
+ Controls the scaling behavior of the circle when the map is pitched.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing the float `5`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSValue` object containing `MGLCircleScaleAlignmentMap`. Set this property to
+ `nil` to reset it to the default value.
+
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-circle-pitch-scale"><code>circle-pitch-scale</code></a>
+ layout property in the Mapbox Style Specification.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *circleRadius;
+@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *circleScaleAlignment;
+
+@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *circlePitchScale __attribute__((unavailable("Use circleScaleAlignment instead.")));
#if TARGET_OS_IPHONE
/**
The stroke color of the circle.
- The default value of this property is an `MGLStyleValue` object containing `UIColor.blackColor`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing
+ `UIColor.blackColor`. Set this property to `nil` to reset it to the default
+ value.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<MGLColor *> *circleStrokeColor;
+@property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *circleStrokeColor;
#else
/**
The stroke color of the circle.
- The default value of this property is an `MGLStyleValue` object containing `NSColor.blackColor`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing
+ `NSColor.blackColor`. Set this property to `nil` to reset it to the default
+ value.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<MGLColor *> *circleStrokeColor;
+@property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *circleStrokeColor;
#endif
/**
The opacity of the circle's stroke.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing the float `1`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `1`. Set this property to `nil` to reset
+ it to the default value.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *circleStrokeOpacity;
/**
- The width of the circle's stroke. Strokes are placed outside of the `circleRadius`.
-
+ The width of the circle's stroke. Strokes are placed outside of the
+ "circle-radius".
+
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing the float `0`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `0`. Set this property to `nil` to reset
+ it to the default value.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *circleStrokeWidth;
+#if TARGET_OS_IPHONE
/**
The geometry's offset.
-
+
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an `NSValue` object containing a `CGVector` struct set to 0 points from the left and 0 points from the top. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSValue` object containing a `CGVector` struct set to 0 points rightward and 0
+ points downward. Set this property to `nil` to reset it to the default value.
+
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-circle-translate"><code>circle-translate</code></a>
+ layout property in the Mapbox Style Specification.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *circleTranslate;
+@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *circleTranslation;
+#else
+/**
+ The geometry's offset.
+
+ This property is measured in points.
+
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSValue` object containing a `CGVector` struct set to 0 points rightward and 0
+ points upward. Set this property to `nil` to reset it to the default value.
+
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-circle-translate"><code>circle-translate</code></a>
+ layout property in the Mapbox Style Specification.
+ */
+@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *circleTranslation;
+#endif
+
+@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *circleTranslate __attribute__((unavailable("Use circleTranslation instead.")));
/**
Controls the translation reference point.
- The default value of this property is an `MGLStyleValue` object containing an `NSValue` object containing `MGLCircleTranslateAnchorMap`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSValue` object containing `MGLCircleTranslationAnchorMap`. Set this property
+ to `nil` to reset it to the default value.
+
+ This property is only applied to the style if `circleTranslation` is non-`nil`.
+ Otherwise, it is ignored.
+
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-circle-translate-anchor"><code>circle-translate-anchor</code></a>
+ layout property in the Mapbox Style Specification.
+ */
+@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *circleTranslationAnchor;
+
+@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *circleTranslateAnchor __attribute__((unavailable("Use circleTranslationAnchor instead.")));
+
+@end
+
+/**
+ Methods for wrapping an enumeration value for a style layer attribute in an
+ `MGLCircleStyleLayer` object and unwrapping its raw value.
+ */
+@interface NSValue (MGLCircleStyleLayerAdditions)
- This property is only applied to the style if `circleTranslate` is non-`nil`. Otherwise, it is ignored.
+#pragma mark Working with Circle Style Layer Attribute Values
+
+/**
+ Creates a new value object containing the given `MGLCircleScaleAlignment` enumeration.
+
+ @param circleScaleAlignment The value for the new object.
+ @return A new value object that contains the enumeration value.
+ */
++ (instancetype)valueWithMGLCircleScaleAlignment:(MGLCircleScaleAlignment)circleScaleAlignment;
+
+/**
+ The `MGLCircleScaleAlignment` enumeration representation of the value.
+ */
+@property (readonly) MGLCircleScaleAlignment MGLCircleScaleAlignmentValue;
+
+/**
+ Creates a new value object containing the given `MGLCircleTranslationAnchor` enumeration.
+
+ @param circleTranslationAnchor The value for the new object.
+ @return A new value object that contains the enumeration value.
+ */
++ (instancetype)valueWithMGLCircleTranslationAnchor:(MGLCircleTranslationAnchor)circleTranslationAnchor;
+
+/**
+ The `MGLCircleTranslationAnchor` enumeration representation of the value.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *circleTranslateAnchor;
+@property (readonly) MGLCircleTranslationAnchor MGLCircleTranslationAnchorValue;
@end
diff --git a/platform/darwin/src/MGLCircleStyleLayer.mm b/platform/darwin/src/MGLCircleStyleLayer.mm
index e8ee2bca7e..adc5ed8dd3 100644
--- a/platform/darwin/src/MGLCircleStyleLayer.mm
+++ b/platform/darwin/src/MGLCircleStyleLayer.mm
@@ -1,5 +1,5 @@
// This file is generated.
-// Edit platform/darwin/scripts/generate-style-code.js, then run `make style-code-darwin`.
+// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
#import "MGLSource.h"
#import "MGLMapView_Private.h"
@@ -8,17 +8,19 @@
#import "MGLStyleValue_Private.h"
#import "MGLCircleStyleLayer.h"
+#include <mbgl/map/map.hpp>
#include <mbgl/style/layers/circle_layer.hpp>
+
namespace mbgl {
- MBGL_DEFINE_ENUM(MGLCirclePitchScale, {
- { MGLCirclePitchScaleMap, "map" },
- { MGLCirclePitchScaleViewport, "viewport" },
+ MBGL_DEFINE_ENUM(MGLCircleScaleAlignment, {
+ { MGLCircleScaleAlignmentMap, "map" },
+ { MGLCircleScaleAlignmentViewport, "viewport" },
});
- MBGL_DEFINE_ENUM(MGLCircleTranslateAnchor, {
- { MGLCircleTranslateAnchorMap, "map" },
- { MGLCircleTranslateAnchorViewport, "viewport" },
+ MBGL_DEFINE_ENUM(MGLCircleTranslationAnchor, {
+ { MGLCircleTranslationAnchorMap, "map" },
+ { MGLCircleTranslationAnchorViewport, "viewport" },
});
}
@@ -54,6 +56,13 @@ namespace mbgl {
super.rawLayer = rawLayer;
}
+- (NSString *)sourceIdentifier
+{
+ MGLAssertStyleLayerIsValid();
+
+ return @(self.rawLayer->getSourceID().c_str());
+}
+
- (NSString *)sourceLayerIdentifier
{
MGLAssertStyleLayerIsValid();
@@ -73,7 +82,7 @@ namespace mbgl {
{
MGLAssertStyleLayerIsValid();
- self.rawLayer->setFilter(predicate.mgl_filter);
+ self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::NullFilter());
}
- (NSPredicate *)predicate
@@ -166,32 +175,39 @@ namespace mbgl {
return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
}
-- (void)setCirclePitchScale:(MGLStyleValue<NSValue *> *)circlePitchScale {
+- (void)setCircleRadius:(MGLStyleValue<NSNumber *> *)circleRadius {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::style::CirclePitchScaleType, NSValue *, mbgl::style::CirclePitchScaleType, MGLCirclePitchScale>().toEnumPropertyValue(circlePitchScale);
- self.rawLayer->setCirclePitchScale(mbglValue);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue(circleRadius);
+ self.rawLayer->setCircleRadius(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)circlePitchScale {
+- (MGLStyleValue<NSNumber *> *)circleRadius {
MGLAssertStyleLayerIsValid();
- auto propertyValue = self.rawLayer->getCirclePitchScale() ?: self.rawLayer->getDefaultCirclePitchScale();
- return MGLStyleValueTransformer<mbgl::style::CirclePitchScaleType, NSValue *, mbgl::style::CirclePitchScaleType, MGLCirclePitchScale>().toEnumStyleValue(propertyValue);
+ auto propertyValue = self.rawLayer->getCircleRadius() ?: self.rawLayer->getDefaultCircleRadius();
+ return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
}
-- (void)setCircleRadius:(MGLStyleValue<NSNumber *> *)circleRadius {
+- (void)setCircleScaleAlignment:(MGLStyleValue<NSValue *> *)circleScaleAlignment {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue(circleRadius);
- self.rawLayer->setCircleRadius(mbglValue);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::style::CirclePitchScaleType, NSValue *, mbgl::style::CirclePitchScaleType, MGLCircleScaleAlignment>().toEnumPropertyValue(circleScaleAlignment);
+ self.rawLayer->setCirclePitchScale(mbglValue);
}
-- (MGLStyleValue<NSNumber *> *)circleRadius {
+- (MGLStyleValue<NSValue *> *)circleScaleAlignment {
MGLAssertStyleLayerIsValid();
- auto propertyValue = self.rawLayer->getCircleRadius() ?: self.rawLayer->getDefaultCircleRadius();
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
+ auto propertyValue = self.rawLayer->getCirclePitchScale() ?: self.rawLayer->getDefaultCirclePitchScale();
+ return MGLStyleValueTransformer<mbgl::style::CirclePitchScaleType, NSValue *, mbgl::style::CirclePitchScaleType, MGLCircleScaleAlignment>().toEnumStyleValue(propertyValue);
+}
+
+- (void)setCirclePitchScale:(MGLStyleValue<NSValue *> *)circlePitchScale {
+}
+
+- (MGLStyleValue<NSValue *> *)circlePitchScale {
+ return self.circleScaleAlignment;
}
- (void)setCircleStrokeColor:(MGLStyleValue<MGLColor *> *)circleStrokeColor {
@@ -236,33 +252,71 @@ namespace mbgl {
return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
}
-- (void)setCircleTranslate:(MGLStyleValue<NSValue *> *)circleTranslate {
+- (void)setCircleTranslation:(MGLStyleValue<NSValue *> *)circleTranslation {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toPropertyValue(circleTranslate);
+ auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toPropertyValue(circleTranslation);
self.rawLayer->setCircleTranslate(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)circleTranslate {
+- (MGLStyleValue<NSValue *> *)circleTranslation {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getCircleTranslate() ?: self.rawLayer->getDefaultCircleTranslate();
return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toStyleValue(propertyValue);
}
-- (void)setCircleTranslateAnchor:(MGLStyleValue<NSValue *> *)circleTranslateAnchor {
+- (void)setCircleTranslate:(MGLStyleValue<NSValue *> *)circleTranslate {
+}
+
+- (MGLStyleValue<NSValue *> *)circleTranslate {
+ return self.circleTranslation;
+}
+
+- (void)setCircleTranslationAnchor:(MGLStyleValue<NSValue *> *)circleTranslationAnchor {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLCircleTranslateAnchor>().toEnumPropertyValue(circleTranslateAnchor);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLCircleTranslationAnchor>().toEnumPropertyValue(circleTranslationAnchor);
self.rawLayer->setCircleTranslateAnchor(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)circleTranslateAnchor {
+- (MGLStyleValue<NSValue *> *)circleTranslationAnchor {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getCircleTranslateAnchor() ?: self.rawLayer->getDefaultCircleTranslateAnchor();
- return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLCircleTranslateAnchor>().toEnumStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLCircleTranslationAnchor>().toEnumStyleValue(propertyValue);
+}
+
+- (void)setCircleTranslateAnchor:(MGLStyleValue<NSValue *> *)circleTranslateAnchor {
}
+- (MGLStyleValue<NSValue *> *)circleTranslateAnchor {
+ return self.circleTranslationAnchor;
+}
+
+
+@end
+
+@implementation NSValue (MGLCircleStyleLayerAdditions)
+
++ (NSValue *)valueWithMGLCircleScaleAlignment:(MGLCircleScaleAlignment)circleScaleAlignment {
+ return [NSValue value:&circleScaleAlignment withObjCType:@encode(MGLCircleScaleAlignment)];
+}
+
+- (MGLCircleScaleAlignment)MGLCircleScaleAlignmentValue {
+ MGLCircleScaleAlignment circleScaleAlignment;
+ [self getValue:&circleScaleAlignment];
+ return circleScaleAlignment;
+}
+
++ (NSValue *)valueWithMGLCircleTranslationAnchor:(MGLCircleTranslationAnchor)circleTranslationAnchor {
+ return [NSValue value:&circleTranslationAnchor withObjCType:@encode(MGLCircleTranslationAnchor)];
+}
+
+- (MGLCircleTranslationAnchor)MGLCircleTranslationAnchorValue {
+ MGLCircleTranslationAnchor circleTranslationAnchor;
+ [self getValue:&circleTranslationAnchor];
+ return circleTranslationAnchor;
+}
@end
diff --git a/platform/darwin/src/MGLFeature.h b/platform/darwin/src/MGLFeature.h
index b3a1868ed2..82dbb450cf 100644
--- a/platform/darwin/src/MGLFeature.h
+++ b/platform/darwin/src/MGLFeature.h
@@ -11,30 +11,41 @@ NS_ASSUME_NONNULL_BEGIN
/**
The `MGLFeature` protocol is used to provide details about geographic features
- contained in a map view’s
- <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources">tile sources</a>.
- Each concrete subclass of `MGLShape` in turn has a subclass that conforms to
- this protocol.
-
- Typically, you do not create feature objects yourself but rather obtain them
- using `-[MGLMapView visibleFeaturesAtPoint:]` and related methods. Each feature
- object associates a shape with an identifier and attributes as specified by the
- source. Like ordinary `MGLAnnotation` objects, some kinds of `MGLFeature`
- objects can also be added to a map view using an `MGLShapeSource` or
- `-[MGLMapView addAnnotations:]` and related methods.
+ contained in an `MGLShapeSource` or `MGLVectorSource` object. Each concrete
+ subclass of `MGLShape` in turn has a subclass that conforms to this protocol. A
+ feature object associates a shape with an optional identifier and attributes.
+
+ You can add custom data to display on the map by creating feature objects and
+ adding them to an `MGLShapeSource` using the
+ `-[MGLShapeSource initWithIdentifier:shape:options:]` method or
+ `MGLShapeSource.shape` property. Similarly, you can add `MGLPointFeature`,
+ `MGLPolylineFeature`, and `MGLPolygonFeature` objects to the map as annotations
+ using `-[MGLMapView addAnnotations:]` and related methods.
+
+ In addition to adding data to the map, you can also extract data from the map:
+ `-[MGLMapView visibleFeaturesAtPoint:]` and related methods return feature
+ objects that correspond to features in the source. This enables you to inspect
+ the properties of features in vector tiles loaded by `MGLVectorSource` objects.
+ You also reuse these feature objects as overlay annotations.
*/
@protocol MGLFeature <MGLAnnotation>
/**
- An object that uniquely identifies the feature in its containing
- <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources">tile source</a>.
+ An object that uniquely identifies the feature in its containing content
+ source.
- The identifier corresponds to the
+ You can configure an `MGLVectorStyleLayer` object to include or exclude a
+ specific feature in an `MGLShapeSource` or `MGLVectorSource`. In the
+ `MGLVectorStyleLayer.predicate` property, compare the special `$id` attribute
+ to the feature’s identifier.
+
+ In vector tiles loaded by `MGLVectorSource` objects, the identifier corresponds
+ to the
<a href="https://github.com/mapbox/vector-tile-spec/tree/master/2.1#42-features">feature identifier</a>
- (`id`) in the tile source. If the source does not specify the feature’s
- identifier, the value of this property is `nil`. If specified, the identifier
- may be an integer, floating-point number, or string. These data types are
- mapped to instances of the following Foundation classes:
+ (`id`). If the source does not specify the feature’s identifier, the value of
+ this property is `nil`. If specified, the identifier may be an integer,
+ floating-point number, or string. These data types are mapped to instances of
+ the following Foundation classes:
<table>
<thead>
@@ -50,22 +61,40 @@ NS_ASSUME_NONNULL_BEGIN
For details about the identifiers used in most Mapbox-provided styles, consult
the
<a href="https://www.mapbox.com/vector-tiles/mapbox-streets/">Mapbox Streets</a>
- layer reference. Note that while it is possible to change this value on feature
- instances obtained from `-[MGLMapView visibleFeaturesAtPoint:]` and related
- methods, there will be no effect on the map. Setting this value can be useful
- when the feature instance is used to initialize an `MGLShapeSource` and that
- source is added to the map and styled.
+ layer reference.
+
+ The identifier should be set before adding the feature to an `MGLShapeSource`
+ object; setting it afterwards has no effect on the map’s contents. While it is
+ possible to change this value on feature instances obtained from
+ `-[MGLMapView visibleFeaturesAtPoint:]` and related methods, doing so likewise
+ has no effect on the map’s contents.
*/
@property (nonatomic, copy, nullable) id identifier;
/**
- A dictionary of attributes for this feature specified by the
- <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources">tile source</a>.
+ A dictionary of attributes for this feature.
+
+ You can configure an `MGLVectorStyleLayer` object to include or exclude a
+ specific feature in an `MGLShapeSource` or `MGLVectorSource`. In the
+ `MGLVectorStyleLayer.predicate` property, compare a key of the attribute
+ dictionary to the value you want to include. For example, if you want an
+ `MGLLineStyleLayer` object to display only important features, you might assign
+ a value above 50 to the important features’ `importance` attribute, then set
+ `MGLVectorStyleLayer.predicate` to an `NSPredicate` with the format
+ `importance > 50`.
- The keys and values of this dictionary are determined by the tile source. In
- the tile source, each attribute name is a string, while each attribute value
- may be a null value, Boolean value, integer, floating-point number, or string.
- These data types are mapped to instances of the following Foundation classes:
+ You can also configure some attributes of an `MGLSymbolStyleLayer` object to
+ include the value of an attribute in this dictionary whenever it renders this
+ feature. For example, to label features in an `MGLShapeSource` object by their
+ names, you can assign a `name` attribute to each of the source’s features, then
+ set `MGLSymbolStyleLayer.textField` to an `MGLStyleValue` object containing the
+ string `{name}`.
+
+ In vector tiles loaded by `MGLVectorSource` objects, the keys and values of
+ each feature’s attribute dictionary are determined by the source. Each
+ attribute name is a string, while each attribute value may be a null value,
+ Boolean value, integer, floating-point number, or string. These data types are
+ mapped to instances of the following Foundation classes:
<table>
<thead>
@@ -85,7 +114,9 @@ NS_ASSUME_NONNULL_BEGIN
<a href="https://www.mapbox.com/vector-tiles/mapbox-streets/">Mapbox Streets</a>
and
<a href="https://www.mapbox.com/vector-tiles/mapbox-terrain/">Mapbox Terrain</a>
- layer references. Note that while it is possible to change this value on feature
+ layer references.
+
+ Note that while it is possible to change this value on feature
instances obtained from `-[MGLMapView visibleFeaturesAtPoint:]` and related
methods, there will be no effect on the map. Setting this value can be useful
when the feature instance is used to initialize an `MGLShapeSource` and that
@@ -115,56 +146,75 @@ NS_ASSUME_NONNULL_BEGIN
@end
/**
- The `MGLPointFeature` class represents a point in a
- <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources">tile source</a>.
+ An `MGLPointFeature` object associates a point shape with an optional
+ identifier and attributes.
*/
MGL_EXPORT
@interface MGLPointFeature : MGLPointAnnotation <MGLFeature>
@end
/**
- The `MGLPolylineFeature` class represents a polyline in a
- <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources">tile source</a>.
+ An `MGLPolylineFeature` object associates a polyline shape with an optional
+ identifier and attributes.
+
+ A polyline feature is known as a
+ <a href="https://tools.ietf.org/html/rfc7946#section-3.1.4">LineString</a>
+ feature in GeoJSON.
*/
MGL_EXPORT
@interface MGLPolylineFeature : MGLPolyline <MGLFeature>
@end
/**
- The `MGLPolygonFeature` class represents a polygon in a
- <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources">tile source</a>.
+ An `MGLPolygonFeature` object associates a polygon shape with an optional
+ identifier and attributes.
*/
MGL_EXPORT
@interface MGLPolygonFeature : MGLPolygon <MGLFeature>
@end
/**
- The `MGLPointCollectionFeature` class represents a multipoint in a
- <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources">tile source</a>.
+ An `MGLPointCollectionFeature` object associates a point collection with an
+ optional identifier and attributes.
+
+ A point collection feature is known as a
+ <a href="https://tools.ietf.org/html/rfc7946#section-3.1.3">MultiPoint</a>
+ feature in GeoJSON.
*/
MGL_EXPORT
@interface MGLPointCollectionFeature : MGLPointCollection <MGLFeature>
@end
+// https://github.com/mapbox/mapbox-gl-native/issues/7473
+@compatibility_alias MGLMultiPointFeature MGLPointCollectionFeature;
+
/**
- The `MGLMultiPolylineFeature` class represents a multipolyline in a
- <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources">tile source</a>.
+ An `MGLMultiPolylineFeature` object associates a multipolyline shape with an
+ optional identifier and attributes.
+
+ A multipolyline feature is known as a
+ <a href="https://tools.ietf.org/html/rfc7946#section-3.1.5">MultiLineString</a>
+ feature in GeoJSON.
*/
MGL_EXPORT
@interface MGLMultiPolylineFeature : MGLMultiPolyline <MGLFeature>
@end
/**
- The `MGLMultiPolygonFeature` class represents a multipolygon in a
- <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources">tile source</a>.
+ An `MGLMultiPolygonFeature` object associates a multipolygon shape with an
+ optional identifier and attributes.
*/
MGL_EXPORT
@interface MGLMultiPolygonFeature : MGLMultiPolygon <MGLFeature>
@end
/**
- The `MGLShapeCollectionFeature` class represents a shape collection in a
- <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources">tile source</a>.
+ An `MGLShapeCollectionFeature` object associates a shape collection with an
+ optional identifier and attributes.
+
+ A shape collection feature is known as a
+ <a href="https://tools.ietf.org/html/rfc7946#section-3.3">feature collection</a>
+ in GeoJSON.
*/
MGL_EXPORT
@interface MGLShapeCollectionFeature : MGLShapeCollection <MGLFeature>
diff --git a/platform/darwin/src/MGLFeature.mm b/platform/darwin/src/MGLFeature.mm
index c1e0c312a0..3bd7eae535 100644
--- a/platform/darwin/src/MGLFeature.mm
+++ b/platform/darwin/src/MGLFeature.mm
@@ -10,6 +10,7 @@
#import "MGLPolyline+MGLAdditions.h"
#import "MGLPolygon+MGLAdditions.h"
#import "NSDictionary+MGLAdditions.h"
+#import "NSArray+MGLAdditions.h"
#import "NSExpression+MGLAdditions.h"
@@ -25,6 +26,10 @@
@synthesize identifier;
@synthesize attributes;
+MGL_DEFINE_FEATURE_INIT_WITH_CODER();
+MGL_DEFINE_FEATURE_ENCODE();
+MGL_DEFINE_FEATURE_IS_EQUAL();
+
- (id)attributeForKey:(NSString *)key {
return self.attributes[key];
}
@@ -47,6 +52,10 @@
@synthesize identifier;
@synthesize attributes;
+MGL_DEFINE_FEATURE_INIT_WITH_CODER();
+MGL_DEFINE_FEATURE_ENCODE();
+MGL_DEFINE_FEATURE_IS_EQUAL();
+
- (id)attributeForKey:(NSString *)key {
return self.attributes[key];
}
@@ -69,6 +78,10 @@
@synthesize identifier;
@synthesize attributes;
+MGL_DEFINE_FEATURE_INIT_WITH_CODER();
+MGL_DEFINE_FEATURE_ENCODE();
+MGL_DEFINE_FEATURE_IS_EQUAL();
+
- (id)attributeForKey:(NSString *)key {
return self.attributes[key];
}
@@ -91,6 +104,10 @@
@synthesize identifier;
@synthesize attributes;
+MGL_DEFINE_FEATURE_INIT_WITH_CODER();
+MGL_DEFINE_FEATURE_ENCODE();
+MGL_DEFINE_FEATURE_IS_EQUAL();
+
- (id)attributeForKey:(NSString *)key {
return self.attributes[key];
}
@@ -113,6 +130,10 @@
@synthesize identifier;
@synthesize attributes;
+MGL_DEFINE_FEATURE_INIT_WITH_CODER();
+MGL_DEFINE_FEATURE_ENCODE();
+MGL_DEFINE_FEATURE_IS_EQUAL();
+
- (id)attributeForKey:(NSString *)key {
return self.attributes[key];
}
@@ -135,6 +156,10 @@
@synthesize identifier;
@synthesize attributes;
+MGL_DEFINE_FEATURE_INIT_WITH_CODER();
+MGL_DEFINE_FEATURE_ENCODE();
+MGL_DEFINE_FEATURE_IS_EQUAL();
+
- (id)attributeForKey:(NSString *)key {
return self.attributes[key];
}
@@ -159,10 +184,14 @@
@dynamic shapes;
-+ (instancetype)shapeCollectionWithShapes:(NSArray *)shapes {
++ (instancetype)shapeCollectionWithShapes:(NS_ARRAY_OF(MGLShape<MGLFeature> *) *)shapes {
return [super shapeCollectionWithShapes:shapes];
}
+MGL_DEFINE_FEATURE_INIT_WITH_CODER();
+MGL_DEFINE_FEATURE_ENCODE();
+MGL_DEFINE_FEATURE_IS_EQUAL();
+
- (id)attributeForKey:(NSString *)key {
return self.attributes[key];
}
diff --git a/platform/darwin/src/MGLFeature_Private.h b/platform/darwin/src/MGLFeature_Private.h
index 59c5ab8611..6751b3196a 100644
--- a/platform/darwin/src/MGLFeature_Private.h
+++ b/platform/darwin/src/MGLFeature_Private.h
@@ -39,3 +39,31 @@ mbgl::Feature mbglFeature(mbgl::Feature feature, id identifier, NSDictionary *at
NS_DICTIONARY_OF(NSString *, id) *NSDictionaryFeatureForGeometry(NSDictionary *geometry, NSDictionary *attributes, id identifier);
NS_ASSUME_NONNULL_END
+
+#define MGL_DEFINE_FEATURE_INIT_WITH_CODER() \
+ - (instancetype)initWithCoder:(NSCoder *)decoder { \
+ if (self = [super initWithCoder:decoder]) { \
+ NSSet<Class> *identifierClasses = [NSSet setWithArray:@[[NSString class], [NSNumber class]]]; \
+ identifier = [decoder decodeObjectOfClasses:identifierClasses forKey:@"identifier"]; \
+ attributes = [decoder decodeObjectOfClass:[NSDictionary class] forKey:@"attributes"]; \
+ } \
+ return self; \
+ }
+
+#define MGL_DEFINE_FEATURE_ENCODE() \
+ - (void)encodeWithCoder:(NSCoder *)coder { \
+ [super encodeWithCoder:coder]; \
+ [coder encodeObject:identifier forKey:@"identifier"]; \
+ [coder encodeObject:attributes forKey:@"attributes"]; \
+ }
+
+#define MGL_DEFINE_FEATURE_IS_EQUAL() \
+ - (BOOL)isEqual:(id)other { \
+ if (other == self) return YES; \
+ if (![other isKindOfClass:[self class]]) return NO; \
+ __typeof(self) otherFeature = other; \
+ return [super isEqual:other] && [self geoJSONObject] == [otherFeature geoJSONObject]; \
+ } \
+ - (NSUInteger)hash { \
+ return [super hash] + [[self geoJSONDictionary] hash]; \
+ }
diff --git a/platform/darwin/src/MGLFillStyleLayer.h b/platform/darwin/src/MGLFillStyleLayer.h
index c586ce413b..2ab02acf5a 100644
--- a/platform/darwin/src/MGLFillStyleLayer.h
+++ b/platform/darwin/src/MGLFillStyleLayer.h
@@ -10,24 +10,44 @@ NS_ASSUME_NONNULL_BEGIN
/**
Controls the translation reference point.
- Values of this type are used in the `fillTranslateAnchor` property of `MGLFillStyleLayer`.
+ Values of this type are used in the `MGLFillStyleLayer.fillTranslationAnchor`
+ property.
*/
-typedef NS_ENUM(NSUInteger, MGLFillTranslateAnchor) {
+typedef NS_ENUM(NSUInteger, MGLFillTranslationAnchor) {
/**
The fill is translated relative to the map.
*/
- MGLFillTranslateAnchorMap,
+ MGLFillTranslationAnchorMap,
/**
The fill is translated relative to the viewport.
*/
- MGLFillTranslateAnchorViewport,
+ MGLFillTranslationAnchorViewport,
};
/**
- A fill layer which allows customization of styling properties at runtime. You may
- instantiate a new fill layer to add to a map style or you may query an
- `MGLMapView` for its `style` and obtain existing layers using the
- `-[MGLStyle layerWithIdentifier:]` method.
+ An `MGLFillStyleLayer` is a style layer that renders one or more filled (and
+ optionally stroked) polygons on the map.
+
+ Use a fill style layer to configure the visual appearance of polygon or
+ multipolygon features in vector tiles loaded by an `MGLVectorSource` object or
+ `MGLPolygon`, `MGLPolygonFeature`, `MGLMultiPolygon`, or
+ `MGLMultiPolygonFeature` instances in an `MGLShapeSource` object.
+
+ You can access an existing fill style layer using the
+ `-[MGLStyle layerWithIdentifier:]` method if you know its identifier;
+ otherwise, find it using the `MGLStyle.layers` property. You can also create a
+ new fill style layer and add it to the style using a method such as
+ `-[MGLStyle addLayer:]`.
+
+ ### Example
+
+ ```swift
+ let layer = MGLFillStyleLayer(identifier: "parks", source: parks)
+ layer.sourceLayerIdentifier = "parks"
+ layer.fillColor = MGLStyleValue(rawValue: .green)
+ layer.predicate = NSPredicate(format: "type == %@", "national-park")
+ mapView.style?.addLayer(layer)
+ ```
*/
MGL_EXPORT
@interface MGLFillStyleLayer : MGLVectorStyleLayer
@@ -37,71 +57,154 @@ MGL_EXPORT
/**
Whether or not the fill should be antialiased.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing `YES`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing `YES`. Set this property to `nil` to reset it to
+ the default value.
- This attribute corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-fill-antialias"><code>fill-antialias</code></a> paint property in the Mapbox Style Specification.
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-fill-antialias"><code>fill-antialias</code></a>
+ layout property in the Mapbox Style Specification.
*/
@property (nonatomic, null_resettable, getter=isFillAntialiased) MGLStyleValue<NSNumber *> *fillAntialiased;
-
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *fillAntialias __attribute__((unavailable("Use fillAntialiased instead.")));
#if TARGET_OS_IPHONE
/**
The color of the filled part of this layer.
- The default value of this property is an `MGLStyleValue` object containing `UIColor.blackColor`. Set this property to `nil` to reset it to the default value.
-
- This property is only applied to the style if `fillPattern` is set to `nil`. Otherwise, it is ignored.
+ The default value of this property is an `MGLStyleValue` object containing
+ `UIColor.blackColor`. Set this property to `nil` to reset it to the default
+ value.
+
+ This property is only applied to the style if `fillPattern` is set to `nil`.
+ Otherwise, it is ignored.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<MGLColor *> *fillColor;
+@property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *fillColor;
#else
/**
The color of the filled part of this layer.
- The default value of this property is an `MGLStyleValue` object containing `NSColor.blackColor`. Set this property to `nil` to reset it to the default value.
-
- This property is only applied to the style if `fillPattern` is set to `nil`. Otherwise, it is ignored.
+ The default value of this property is an `MGLStyleValue` object containing
+ `NSColor.blackColor`. Set this property to `nil` to reset it to the default
+ value.
+
+ This property is only applied to the style if `fillPattern` is set to `nil`.
+ Otherwise, it is ignored.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<MGLColor *> *fillColor;
+@property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *fillColor;
#endif
/**
- The opacity of the entire fill layer. In contrast to the `fillColor`, this value will also affect the 1pt stroke around the fill, if the stroke is used.
+ The opacity of the entire fill layer. In contrast to the `fillColor`, this
+ value will also affect the 1pt stroke around the fill, if the stroke is used.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing the float `1`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `1`. Set this property to `nil` to reset
+ it to the default value.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *fillOpacity;
+#if TARGET_OS_IPHONE
/**
The outline color of the fill. Matches the value of `fillColor` if unspecified.
-
- This property is only applied to the style if `fillPattern` is set to `nil`, and `fillAntialiased` is set to an `MGLStyleValue` object containing an `NSNumber` object containing `YES`. Otherwise, it is ignored.
+
+ This property is only applied to the style if `fillPattern` is set to `nil`,
+ and `fillAntialiased` is set to an `MGLStyleValue` object containing an
+ `NSNumber` object containing `YES`. Otherwise, it is ignored.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<MGLColor *> *fillOutlineColor;
+@property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *fillOutlineColor;
+#else
+/**
+ The outline color of the fill. Matches the value of `fillColor` if unspecified.
+
+ This property is only applied to the style if `fillPattern` is set to `nil`,
+ and `fillAntialiased` is set to an `MGLStyleValue` object containing an
+ `NSNumber` object containing `YES`. Otherwise, it is ignored.
+ */
+@property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *fillOutlineColor;
+#endif
/**
- Name of image in sprite to use for drawing image fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512).
+ Name of image in sprite to use for drawing image fills. For seamless patterns,
+ image width and height must be a factor of two (2, 4, 8, ..., 512).
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSString *> *fillPattern;
+#if TARGET_OS_IPHONE
/**
The geometry's offset.
-
+
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an `NSValue` object containing a `CGVector` struct set to 0 points from the left and 0 points from the top. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSValue` object containing a `CGVector` struct set to 0 points rightward and 0
+ points downward. Set this property to `nil` to reset it to the default value.
+
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-fill-translate"><code>fill-translate</code></a>
+ layout property in the Mapbox Style Specification.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *fillTranslate;
+@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *fillTranslation;
+#else
+/**
+ The geometry's offset.
+
+ This property is measured in points.
+
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSValue` object containing a `CGVector` struct set to 0 points rightward and 0
+ points upward. Set this property to `nil` to reset it to the default value.
+
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-fill-translate"><code>fill-translate</code></a>
+ layout property in the Mapbox Style Specification.
+ */
+@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *fillTranslation;
+#endif
+
+@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *fillTranslate __attribute__((unavailable("Use fillTranslation instead.")));
/**
Controls the translation reference point.
- The default value of this property is an `MGLStyleValue` object containing an `NSValue` object containing `MGLFillTranslateAnchorMap`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSValue` object containing `MGLFillTranslationAnchorMap`. Set this property to
+ `nil` to reset it to the default value.
+
+ This property is only applied to the style if `fillTranslation` is non-`nil`.
+ Otherwise, it is ignored.
+
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-fill-translate-anchor"><code>fill-translate-anchor</code></a>
+ layout property in the Mapbox Style Specification.
+ */
+@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *fillTranslationAnchor;
+
+@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *fillTranslateAnchor __attribute__((unavailable("Use fillTranslationAnchor instead.")));
- This property is only applied to the style if `fillTranslate` is non-`nil`. Otherwise, it is ignored.
+@end
+
+/**
+ Methods for wrapping an enumeration value for a style layer attribute in an
+ `MGLFillStyleLayer` object and unwrapping its raw value.
+ */
+@interface NSValue (MGLFillStyleLayerAdditions)
+
+#pragma mark Working with Fill Style Layer Attribute Values
+
+/**
+ Creates a new value object containing the given `MGLFillTranslationAnchor` enumeration.
+
+ @param fillTranslationAnchor The value for the new object.
+ @return A new value object that contains the enumeration value.
+ */
++ (instancetype)valueWithMGLFillTranslationAnchor:(MGLFillTranslationAnchor)fillTranslationAnchor;
+
+/**
+ The `MGLFillTranslationAnchor` enumeration representation of the value.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *fillTranslateAnchor;
+@property (readonly) MGLFillTranslationAnchor MGLFillTranslationAnchorValue;
@end
diff --git a/platform/darwin/src/MGLFillStyleLayer.mm b/platform/darwin/src/MGLFillStyleLayer.mm
index 891ab85d0b..63a482ac2e 100644
--- a/platform/darwin/src/MGLFillStyleLayer.mm
+++ b/platform/darwin/src/MGLFillStyleLayer.mm
@@ -1,5 +1,5 @@
// This file is generated.
-// Edit platform/darwin/scripts/generate-style-code.js, then run `make style-code-darwin`.
+// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
#import "MGLSource.h"
#import "MGLMapView_Private.h"
@@ -8,12 +8,14 @@
#import "MGLStyleValue_Private.h"
#import "MGLFillStyleLayer.h"
+#include <mbgl/map/map.hpp>
#include <mbgl/style/layers/fill_layer.hpp>
+
namespace mbgl {
- MBGL_DEFINE_ENUM(MGLFillTranslateAnchor, {
- { MGLFillTranslateAnchorMap, "map" },
- { MGLFillTranslateAnchorViewport, "viewport" },
+ MBGL_DEFINE_ENUM(MGLFillTranslationAnchor, {
+ { MGLFillTranslationAnchorMap, "map" },
+ { MGLFillTranslationAnchorViewport, "viewport" },
});
}
@@ -49,6 +51,13 @@ namespace mbgl {
super.rawLayer = rawLayer;
}
+- (NSString *)sourceIdentifier
+{
+ MGLAssertStyleLayerIsValid();
+
+ return @(self.rawLayer->getSourceID().c_str());
+}
+
- (NSString *)sourceLayerIdentifier
{
MGLAssertStyleLayerIsValid();
@@ -68,7 +77,7 @@ namespace mbgl {
{
MGLAssertStyleLayerIsValid();
- self.rawLayer->setFilter(predicate.mgl_filter);
+ self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::NullFilter());
}
- (NSPredicate *)predicate
@@ -133,9 +142,11 @@ namespace mbgl {
return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(propertyValue);
}
-
- (void)setFillAntialias:(MGLStyleValue<NSNumber *> *)fillAntialias {
- NSAssert(NO, @"Use -setFillAntialiased: instead.");
+}
+
+- (MGLStyleValue<NSNumber *> *)fillAntialias {
+ return self.isFillAntialiased;
}
- (void)setFillColor:(MGLStyleValue<MGLColor *> *)fillColor {
@@ -194,33 +205,61 @@ namespace mbgl {
return MGLStyleValueTransformer<std::string, NSString *>().toStyleValue(propertyValue);
}
-- (void)setFillTranslate:(MGLStyleValue<NSValue *> *)fillTranslate {
+- (void)setFillTranslation:(MGLStyleValue<NSValue *> *)fillTranslation {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toPropertyValue(fillTranslate);
+ auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toPropertyValue(fillTranslation);
self.rawLayer->setFillTranslate(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)fillTranslate {
+- (MGLStyleValue<NSValue *> *)fillTranslation {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getFillTranslate() ?: self.rawLayer->getDefaultFillTranslate();
return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toStyleValue(propertyValue);
}
-- (void)setFillTranslateAnchor:(MGLStyleValue<NSValue *> *)fillTranslateAnchor {
+- (void)setFillTranslate:(MGLStyleValue<NSValue *> *)fillTranslate {
+}
+
+- (MGLStyleValue<NSValue *> *)fillTranslate {
+ return self.fillTranslation;
+}
+
+- (void)setFillTranslationAnchor:(MGLStyleValue<NSValue *> *)fillTranslationAnchor {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLFillTranslateAnchor>().toEnumPropertyValue(fillTranslateAnchor);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLFillTranslationAnchor>().toEnumPropertyValue(fillTranslationAnchor);
self.rawLayer->setFillTranslateAnchor(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)fillTranslateAnchor {
+- (MGLStyleValue<NSValue *> *)fillTranslationAnchor {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getFillTranslateAnchor() ?: self.rawLayer->getDefaultFillTranslateAnchor();
- return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLFillTranslateAnchor>().toEnumStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLFillTranslationAnchor>().toEnumStyleValue(propertyValue);
+}
+
+- (void)setFillTranslateAnchor:(MGLStyleValue<NSValue *> *)fillTranslateAnchor {
+}
+
+- (MGLStyleValue<NSValue *> *)fillTranslateAnchor {
+ return self.fillTranslationAnchor;
}
@end
+
+@implementation NSValue (MGLFillStyleLayerAdditions)
+
++ (NSValue *)valueWithMGLFillTranslationAnchor:(MGLFillTranslationAnchor)fillTranslationAnchor {
+ return [NSValue value:&fillTranslationAnchor withObjCType:@encode(MGLFillTranslationAnchor)];
+}
+
+- (MGLFillTranslationAnchor)MGLFillTranslationAnchorValue {
+ MGLFillTranslationAnchor fillTranslationAnchor;
+ [self getValue:&fillTranslationAnchor];
+ return fillTranslationAnchor;
+}
+
+@end
diff --git a/platform/darwin/src/MGLLineStyleLayer.h b/platform/darwin/src/MGLLineStyleLayer.h
index 6df0d38088..98f2778291 100644
--- a/platform/darwin/src/MGLLineStyleLayer.h
+++ b/platform/darwin/src/MGLLineStyleLayer.h
@@ -10,19 +10,24 @@ NS_ASSUME_NONNULL_BEGIN
/**
The display of line endings.
- Values of this type are used in the `lineCap` property of `MGLLineStyleLayer`.
+ Values of this type are used in the `MGLLineStyleLayer.lineCap`
+ property.
*/
typedef NS_ENUM(NSUInteger, MGLLineCap) {
/**
- A cap with a squared-off end which is drawn to the exact endpoint of the line.
+ A cap with a squared-off end which is drawn to the exact endpoint of the
+ line.
*/
MGLLineCapButt,
/**
- A cap with a rounded end which is drawn beyond the endpoint of the line at a radius of one-half of the line's width and centered on the endpoint of the line.
+ A cap with a rounded end which is drawn beyond the endpoint of the line at
+ a radius of one-half of the line's width and centered on the endpoint of
+ the line.
*/
MGLLineCapRound,
/**
- A cap with a squared-off end which is drawn beyond the endpoint of the line at a distance of one-half of the line's width.
+ A cap with a squared-off end which is drawn beyond the endpoint of the line
+ at a distance of one-half of the line's width.
*/
MGLLineCapSquare,
};
@@ -30,19 +35,24 @@ typedef NS_ENUM(NSUInteger, MGLLineCap) {
/**
The display of lines when joining.
- Values of this type are used in the `lineJoin` property of `MGLLineStyleLayer`.
+ Values of this type are used in the `MGLLineStyleLayer.lineJoin`
+ property.
*/
typedef NS_ENUM(NSUInteger, MGLLineJoin) {
/**
- A join with a squared-off end which is drawn beyond the endpoint of the line at a distance of one-half of the line's width.
+ A join with a squared-off end which is drawn beyond the endpoint of the
+ line at a distance of one-half of the line's width.
*/
MGLLineJoinBevel,
/**
- A join with a rounded end which is drawn beyond the endpoint of the line at a radius of one-half of the line's width and centered on the endpoint of the line.
+ A join with a rounded end which is drawn beyond the endpoint of the line at
+ a radius of one-half of the line's width and centered on the endpoint of
+ the line.
*/
MGLLineJoinRound,
/**
- A join with a sharp, angled corner which is drawn with the outer sides beyond the endpoint of the path until they meet.
+ A join with a sharp, angled corner which is drawn with the outer sides
+ beyond the endpoint of the path until they meet.
*/
MGLLineJoinMiter,
};
@@ -50,24 +60,49 @@ typedef NS_ENUM(NSUInteger, MGLLineJoin) {
/**
Controls the translation reference point.
- Values of this type are used in the `lineTranslateAnchor` property of `MGLLineStyleLayer`.
+ Values of this type are used in the `MGLLineStyleLayer.lineTranslationAnchor`
+ property.
*/
-typedef NS_ENUM(NSUInteger, MGLLineTranslateAnchor) {
+typedef NS_ENUM(NSUInteger, MGLLineTranslationAnchor) {
/**
The line is translated relative to the map.
*/
- MGLLineTranslateAnchorMap,
+ MGLLineTranslationAnchorMap,
/**
The line is translated relative to the viewport.
*/
- MGLLineTranslateAnchorViewport,
+ MGLLineTranslationAnchorViewport,
};
/**
- A line layer which allows customization of styling properties at runtime. You may
- instantiate a new line layer to add to a map style or you may query an
- `MGLMapView` for its `style` and obtain existing layers using the
- `-[MGLStyle layerWithIdentifier:]` method.
+ An `MGLLineStyleLayer` is a style layer that renders one or more stroked
+ polylines on the map.
+
+ Use a line style layer to configure the visual appearance of polyline or
+ multipolyline features in vector tiles loaded by an `MGLVectorSource` object or
+ `MGLPolyline`, `MGLPolylineFeature`, `MGLMultiPolyline`, or
+ `MGLMultiPolylineFeature` instances in an `MGLShapeSource` object.
+
+ You can access an existing line style layer using the
+ `-[MGLStyle layerWithIdentifier:]` method if you know its identifier;
+ otherwise, find it using the `MGLStyle.layers` property. You can also create a
+ new line style layer and add it to the style using a method such as
+ `-[MGLStyle addLayer:]`.
+
+ ### Example
+
+ ```swift
+ let layer = MGLLineStyleLayer(identifier: "trails-path", source: trails)
+ layer.sourceLayerIdentifier = "trails"
+ layer.lineWidth = MGLStyleValue(interpolationBase: 1.5, stops: [
+ 14: MGLStyleValue(rawValue: 2),
+ 18: MGLStyleValue(rawValue: 20),
+ ])
+ layer.lineColor = MGLStyleValue(rawValue: .brown)
+ layer.lineCap = MGLStyleValue(rawValue: NSValue(mglLineCap: .round))
+ layer.predicate = NSPredicate(format: "%K == %@", "trail-type", "mountain-biking")
+ mapView.style?.addLayer(layer)
+ ```
*/
MGL_EXPORT
@interface MGLLineStyleLayer : MGLVectorStyleLayer
@@ -77,32 +112,44 @@ MGL_EXPORT
/**
The display of line endings.
- The default value of this property is an `MGLStyleValue` object containing an `NSValue` object containing `MGLLineCapButt`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSValue` object containing `MGLLineCapButt`. Set this property to `nil` to
+ reset it to the default value.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *lineCap;
/**
The display of lines when joining.
- The default value of this property is an `MGLStyleValue` object containing an `NSValue` object containing `MGLLineJoinMiter`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSValue` object containing `MGLLineJoinMiter`. Set this property to `nil` to
+ reset it to the default value.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *lineJoin;
/**
Used to automatically convert miter joins to bevel joins for sharp angles.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing the float `2`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `2`. Set this property to `nil` to reset
+ it to the default value.
- This property is only applied to the style if `lineJoin` is set to an `MGLStyleValue` object containing an `NSValue` object containing `MGLLineJoinMiter`. Otherwise, it is ignored.
+ This property is only applied to the style if `lineJoin` is set to an
+ `MGLStyleValue` object containing an `NSValue` object containing
+ `MGLLineJoinMiter`. Otherwise, it is ignored.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *lineMiterLimit;
/**
Used to automatically convert round joins to miter joins for shallow angles.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing the float `1.05`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `1.05`. Set this property to `nil` to
+ reset it to the default value.
- This property is only applied to the style if `lineJoin` is set to an `MGLStyleValue` object containing an `NSValue` object containing `MGLLineJoinRound`. Otherwise, it is ignored.
+ This property is only applied to the style if `lineJoin` is set to an
+ `MGLStyleValue` object containing an `NSValue` object containing
+ `MGLLineJoinRound`. Otherwise, it is ignored.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *lineRoundLimit;
@@ -110,10 +157,12 @@ MGL_EXPORT
/**
Blur applied to the line, in points.
-
+
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing the float `0`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `0`. Set this property to `nil` to reset
+ it to the default value.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *lineBlur;
@@ -121,93 +170,199 @@ MGL_EXPORT
/**
The color with which the line will be drawn.
- The default value of this property is an `MGLStyleValue` object containing `UIColor.blackColor`. Set this property to `nil` to reset it to the default value.
-
- This property is only applied to the style if `linePattern` is set to `nil`. Otherwise, it is ignored.
+ The default value of this property is an `MGLStyleValue` object containing
+ `UIColor.blackColor`. Set this property to `nil` to reset it to the default
+ value.
+
+ This property is only applied to the style if `linePattern` is set to `nil`.
+ Otherwise, it is ignored.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<MGLColor *> *lineColor;
+@property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *lineColor;
#else
/**
The color with which the line will be drawn.
- The default value of this property is an `MGLStyleValue` object containing `NSColor.blackColor`. Set this property to `nil` to reset it to the default value.
-
- This property is only applied to the style if `linePattern` is set to `nil`. Otherwise, it is ignored.
+ The default value of this property is an `MGLStyleValue` object containing
+ `NSColor.blackColor`. Set this property to `nil` to reset it to the default
+ value.
+
+ This property is only applied to the style if `linePattern` is set to `nil`.
+ Otherwise, it is ignored.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<MGLColor *> *lineColor;
+@property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *lineColor;
#endif
/**
- Specifies the lengths of the alternating dashes and gaps that form the dash pattern. The lengths are later scaled by the line width. To convert a dash length to points, multiply the length by the current line width.
-
+ Specifies the lengths of the alternating dashes and gaps that form the dash
+ pattern. The lengths are later scaled by the line width. To convert a dash
+ length to points, multiply the length by the current line width.
+
This property is measured in line widths.
-
- This property is only applied to the style if `linePattern` is set to `nil`. Otherwise, it is ignored.
- This attribute corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-line-dasharray"><code>line-dasharray</code></a> paint property in the Mapbox Style Specification.
+ This property is only applied to the style if `linePattern` is set to `nil`.
+ Otherwise, it is ignored.
+
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-line-dasharray"><code>line-dasharray</code></a>
+ layout property in the Mapbox Style Specification.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSArray<NSNumber *> *> *lineDashPattern;
-
@property (nonatomic, null_resettable) MGLStyleValue<NSArray<NSNumber *> *> *lineDasharray __attribute__((unavailable("Use lineDashPattern instead.")));
/**
- Draws a line casing outside of a line's actual path. Value indicates the width of the inner gap.
-
+ Draws a line casing outside of a line's actual path. Value indicates the width
+ of the inner gap.
+
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing the float `0`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `0`. Set this property to `nil` to reset
+ it to the default value.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *lineGapWidth;
/**
- The line's offset. For linear features, a positive value offsets the line to the right, relative to the direction of the line, and a negative value to the left. For polygon features, a positive value results in an inset, and a negative value results in an outset.
-
+ The line's offset. For linear features, a positive value offsets the line to
+ the right, relative to the direction of the line, and a negative value to the
+ left. For polygon features, a positive value results in an inset, and a
+ negative value results in an outset.
+
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing the float `0`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `0`. Set this property to `nil` to reset
+ it to the default value.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *lineOffset;
/**
The opacity at which the line will be drawn.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing the float `1`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `1`. Set this property to `nil` to reset
+ it to the default value.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *lineOpacity;
/**
- Name of image in style images to use for drawing image lines. For seamless patterns, image width must be a factor of two (2, 4, 8, ..., 512).
+ Name of image in style images to use for drawing image lines. For seamless
+ patterns, image width must be a factor of two (2, 4, 8, ..., 512).
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSString *> *linePattern;
+#if TARGET_OS_IPHONE
/**
The geometry's offset.
-
+
+ This property is measured in points.
+
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSValue` object containing a `CGVector` struct set to 0 points rightward and 0
+ points downward. Set this property to `nil` to reset it to the default value.
+
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-line-translate"><code>line-translate</code></a>
+ layout property in the Mapbox Style Specification.
+ */
+@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *lineTranslation;
+#else
+/**
+ The geometry's offset.
+
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an `NSValue` object containing a `CGVector` struct set to 0 points from the left and 0 points from the top. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSValue` object containing a `CGVector` struct set to 0 points rightward and 0
+ points upward. Set this property to `nil` to reset it to the default value.
+
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-line-translate"><code>line-translate</code></a>
+ layout property in the Mapbox Style Specification.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *lineTranslate;
+@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *lineTranslation;
+#endif
+
+@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *lineTranslate __attribute__((unavailable("Use lineTranslation instead.")));
/**
Controls the translation reference point.
- The default value of this property is an `MGLStyleValue` object containing an `NSValue` object containing `MGLLineTranslateAnchorMap`. Set this property to `nil` to reset it to the default value.
-
- This property is only applied to the style if `lineTranslate` is non-`nil`. Otherwise, it is ignored.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSValue` object containing `MGLLineTranslationAnchorMap`. Set this property to
+ `nil` to reset it to the default value.
+
+ This property is only applied to the style if `lineTranslation` is non-`nil`.
+ Otherwise, it is ignored.
+
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-line-translate-anchor"><code>line-translate-anchor</code></a>
+ layout property in the Mapbox Style Specification.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *lineTranslateAnchor;
+@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *lineTranslationAnchor;
+
+@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *lineTranslateAnchor __attribute__((unavailable("Use lineTranslationAnchor instead.")));
/**
Stroke thickness.
-
+
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing the float `1`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `1`. Set this property to `nil` to reset
+ it to the default value.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *lineWidth;
@end
+/**
+ Methods for wrapping an enumeration value for a style layer attribute in an
+ `MGLLineStyleLayer` object and unwrapping its raw value.
+ */
+@interface NSValue (MGLLineStyleLayerAdditions)
+
+#pragma mark Working with Line Style Layer Attribute Values
+
+/**
+ Creates a new value object containing the given `MGLLineCap` enumeration.
+
+ @param lineCap The value for the new object.
+ @return A new value object that contains the enumeration value.
+ */
++ (instancetype)valueWithMGLLineCap:(MGLLineCap)lineCap;
+
+/**
+ The `MGLLineCap` enumeration representation of the value.
+ */
+@property (readonly) MGLLineCap MGLLineCapValue;
+
+/**
+ Creates a new value object containing the given `MGLLineJoin` enumeration.
+
+ @param lineJoin The value for the new object.
+ @return A new value object that contains the enumeration value.
+ */
++ (instancetype)valueWithMGLLineJoin:(MGLLineJoin)lineJoin;
+
+/**
+ The `MGLLineJoin` enumeration representation of the value.
+ */
+@property (readonly) MGLLineJoin MGLLineJoinValue;
+
+/**
+ Creates a new value object containing the given `MGLLineTranslationAnchor` enumeration.
+
+ @param lineTranslationAnchor The value for the new object.
+ @return A new value object that contains the enumeration value.
+ */
++ (instancetype)valueWithMGLLineTranslationAnchor:(MGLLineTranslationAnchor)lineTranslationAnchor;
+
+/**
+ The `MGLLineTranslationAnchor` enumeration representation of the value.
+ */
+@property (readonly) MGLLineTranslationAnchor MGLLineTranslationAnchorValue;
+
+@end
+
NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLLineStyleLayer.mm b/platform/darwin/src/MGLLineStyleLayer.mm
index 48164ed0c8..13408d426c 100644
--- a/platform/darwin/src/MGLLineStyleLayer.mm
+++ b/platform/darwin/src/MGLLineStyleLayer.mm
@@ -1,5 +1,5 @@
// This file is generated.
-// Edit platform/darwin/scripts/generate-style-code.js, then run `make style-code-darwin`.
+// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
#import "MGLSource.h"
#import "MGLMapView_Private.h"
@@ -8,7 +8,9 @@
#import "MGLStyleValue_Private.h"
#import "MGLLineStyleLayer.h"
+#include <mbgl/map/map.hpp>
#include <mbgl/style/layers/line_layer.hpp>
+
namespace mbgl {
MBGL_DEFINE_ENUM(MGLLineCap, {
@@ -23,9 +25,9 @@ namespace mbgl {
{ MGLLineJoinMiter, "miter" },
});
- MBGL_DEFINE_ENUM(MGLLineTranslateAnchor, {
- { MGLLineTranslateAnchorMap, "map" },
- { MGLLineTranslateAnchorViewport, "viewport" },
+ MBGL_DEFINE_ENUM(MGLLineTranslationAnchor, {
+ { MGLLineTranslationAnchorMap, "map" },
+ { MGLLineTranslationAnchorViewport, "viewport" },
});
}
@@ -61,6 +63,13 @@ namespace mbgl {
super.rawLayer = rawLayer;
}
+- (NSString *)sourceIdentifier
+{
+ MGLAssertStyleLayerIsValid();
+
+ return @(self.rawLayer->getSourceID().c_str());
+}
+
- (NSString *)sourceLayerIdentifier
{
MGLAssertStyleLayerIsValid();
@@ -80,7 +89,7 @@ namespace mbgl {
{
MGLAssertStyleLayerIsValid();
- self.rawLayer->setFilter(predicate.mgl_filter);
+ self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::NullFilter());
}
- (NSPredicate *)predicate
@@ -231,9 +240,11 @@ namespace mbgl {
return MGLStyleValueTransformer<std::vector<float>, NSArray<NSNumber *> *, float>().toStyleValue(propertyValue);
}
-
- (void)setLineDasharray:(MGLStyleValue<NSArray<NSNumber *> *> *)lineDasharray {
- NSAssert(NO, @"Use -setLineDashPattern: instead.");
+}
+
+- (MGLStyleValue<NSArray<NSNumber *> *> *)lineDasharray {
+ return self.lineDashPattern;
}
- (void)setLineGapWidth:(MGLStyleValue<NSNumber *> *)lineGapWidth {
@@ -292,32 +303,46 @@ namespace mbgl {
return MGLStyleValueTransformer<std::string, NSString *>().toStyleValue(propertyValue);
}
-- (void)setLineTranslate:(MGLStyleValue<NSValue *> *)lineTranslate {
+- (void)setLineTranslation:(MGLStyleValue<NSValue *> *)lineTranslation {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toPropertyValue(lineTranslate);
+ auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toPropertyValue(lineTranslation);
self.rawLayer->setLineTranslate(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)lineTranslate {
+- (MGLStyleValue<NSValue *> *)lineTranslation {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getLineTranslate() ?: self.rawLayer->getDefaultLineTranslate();
return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toStyleValue(propertyValue);
}
-- (void)setLineTranslateAnchor:(MGLStyleValue<NSValue *> *)lineTranslateAnchor {
+- (void)setLineTranslate:(MGLStyleValue<NSValue *> *)lineTranslate {
+}
+
+- (MGLStyleValue<NSValue *> *)lineTranslate {
+ return self.lineTranslation;
+}
+
+- (void)setLineTranslationAnchor:(MGLStyleValue<NSValue *> *)lineTranslationAnchor {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLLineTranslateAnchor>().toEnumPropertyValue(lineTranslateAnchor);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLLineTranslationAnchor>().toEnumPropertyValue(lineTranslationAnchor);
self.rawLayer->setLineTranslateAnchor(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)lineTranslateAnchor {
+- (MGLStyleValue<NSValue *> *)lineTranslationAnchor {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getLineTranslateAnchor() ?: self.rawLayer->getDefaultLineTranslateAnchor();
- return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLLineTranslateAnchor>().toEnumStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLLineTranslationAnchor>().toEnumStyleValue(propertyValue);
+}
+
+- (void)setLineTranslateAnchor:(MGLStyleValue<NSValue *> *)lineTranslateAnchor {
+}
+
+- (MGLStyleValue<NSValue *> *)lineTranslateAnchor {
+ return self.lineTranslationAnchor;
}
- (void)setLineWidth:(MGLStyleValue<NSNumber *> *)lineWidth {
@@ -336,3 +361,37 @@ namespace mbgl {
@end
+
+@implementation NSValue (MGLLineStyleLayerAdditions)
+
++ (NSValue *)valueWithMGLLineCap:(MGLLineCap)lineCap {
+ return [NSValue value:&lineCap withObjCType:@encode(MGLLineCap)];
+}
+
+- (MGLLineCap)MGLLineCapValue {
+ MGLLineCap lineCap;
+ [self getValue:&lineCap];
+ return lineCap;
+}
+
++ (NSValue *)valueWithMGLLineJoin:(MGLLineJoin)lineJoin {
+ return [NSValue value:&lineJoin withObjCType:@encode(MGLLineJoin)];
+}
+
+- (MGLLineJoin)MGLLineJoinValue {
+ MGLLineJoin lineJoin;
+ [self getValue:&lineJoin];
+ return lineJoin;
+}
+
++ (NSValue *)valueWithMGLLineTranslationAnchor:(MGLLineTranslationAnchor)lineTranslationAnchor {
+ return [NSValue value:&lineTranslationAnchor withObjCType:@encode(MGLLineTranslationAnchor)];
+}
+
+- (MGLLineTranslationAnchor)MGLLineTranslationAnchorValue {
+ MGLLineTranslationAnchor lineTranslationAnchor;
+ [self getValue:&lineTranslationAnchor];
+ return lineTranslationAnchor;
+}
+
+@end
diff --git a/platform/darwin/src/MGLMultiPoint.h b/platform/darwin/src/MGLMultiPoint.h
index eaac50af46..31ab5744a3 100644
--- a/platform/darwin/src/MGLMultiPoint.h
+++ b/platform/darwin/src/MGLMultiPoint.h
@@ -8,10 +8,15 @@ NS_ASSUME_NONNULL_BEGIN
/**
The `MGLMultiPoint` class is an abstract superclass used to define shapes
- composed of multiple vertices. You should not create instances of this class
- directly. Instead, you should create instances of the `MGLPolyline` or
- `MGLPolygon` classes. However, you can use the method and properties of this
- class to access information about the vertices of the line or polygon.
+ composed of multiple vertices.
+
+ You do not create instances of this class directly. Instead, you create
+ instances of the `MGLPolyline` or `MGLPolygon` classes. However, you can use
+ the method and properties of this class to access information about the
+ vertices of the line or polygon.
+
+ Do not confuse `MGLMultiPoint` with `MGLPointCollection`, which represents a
+ collection of related but disconnected points.
*/
MGL_EXPORT
@interface MGLMultiPoint : MGLShape
@@ -57,7 +62,7 @@ MGL_EXPORT
the map, it is redrawn immediately.
@param coords The array of coordinates to insert into the shape. The data in
- this array is copied to the shape’s `coordinate` property.
+ this array is copied to the shape’s `coordinates` property.
@param count The number of items in the `coords` array.
@param index The zero-based index at which the first coordinate in `coords`
will appear in the `coordinates` property.
@@ -69,7 +74,7 @@ MGL_EXPORT
the map, it is redrawn immediately.
@param coords The array of coordinates to add to the shape. The data in this
- array is copied to the shape’s `coordinate` property.
+ array is copied to the shape’s `coordinates` property.
@param count The number of items in the `coords` array.
*/
- (void)appendCoordinates:(const CLLocationCoordinate2D *)coords count:(NSUInteger)count;
@@ -92,7 +97,7 @@ MGL_EXPORT
being the second vertex, and so on. The `length` field indicates the number
of vertices to replace.
@param coords The array of coordinates defining part of the shape. The data in
- this array is copied to the shape’s `coordinate` property.
+ this array is copied to the shape’s `coordinates` property.
*/
- (void)replaceCoordinatesInRange:(NSRange)range withCoordinates:(const CLLocationCoordinate2D *)coords;
diff --git a/platform/darwin/src/MGLMultiPoint.mm b/platform/darwin/src/MGLMultiPoint.mm
index c49e970c6b..3b03b78ca6 100644
--- a/platform/darwin/src/MGLMultiPoint.mm
+++ b/platform/darwin/src/MGLMultiPoint.mm
@@ -1,10 +1,9 @@
#import "MGLMultiPoint_Private.h"
#import "MGLGeometry_Private.h"
+#import "MGLShape_Private.h"
+#import "NSCoder+MGLAdditions.h"
#import "MGLTypes.h"
-#include <mbgl/util/geo.hpp>
-#include <mbgl/util/optional.hpp>
-
@implementation MGLMultiPoint
{
mbgl::optional<mbgl::LatLngBounds> _bounds;
@@ -27,6 +26,39 @@
return self;
}
+- (instancetype)initWithCoder:(NSCoder *)decoder
+{
+ if (self = [super initWithCoder:decoder]) {
+ _coordinates = [decoder mgl_decodeLocationCoordinates2DForKey:@"coordinates"];
+ }
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder
+{
+ [super encodeWithCoder:coder];
+ [coder mgl_encodeLocationCoordinates2D:_coordinates forKey:@"coordinates"];
+}
+
+- (BOOL)isEqual:(id)other
+{
+ if (self == other) return YES;
+ if (![other isKindOfClass:[MGLMultiPoint class]]) return NO;
+
+ MGLMultiPoint *otherMultipoint = other;
+ return ([super isEqual:otherMultipoint]
+ && _coordinates == otherMultipoint->_coordinates);
+}
+
+- (NSUInteger)hash
+{
+ NSUInteger hash = [super hash];
+ for (auto coord : _coordinates) {
+ hash += @(coord.latitude+coord.longitude).hash;
+ }
+ return hash;
+}
+
- (CLLocationCoordinate2D)coordinate
{
NSAssert([self pointCount] > 0, @"A multipoint must have coordinates");
diff --git a/platform/darwin/src/MGLNetworkConfiguration.m b/platform/darwin/src/MGLNetworkConfiguration.m
index d661d9090e..82d333dc99 100644
--- a/platform/darwin/src/MGLNetworkConfiguration.m
+++ b/platform/darwin/src/MGLNetworkConfiguration.m
@@ -1,5 +1,4 @@
#import "MGLNetworkConfiguration.h"
-#import "NSProcessInfo+MGLAdditions.h"
@implementation MGLNetworkConfiguration
@@ -12,9 +11,6 @@
}
+ (instancetype)sharedManager {
- if (NSProcessInfo.processInfo.mgl_isInterfaceBuilderDesignablesAgent) {
- return nil;
- }
static dispatch_once_t onceToken;
static MGLNetworkConfiguration *_sharedManager;
void (^setupBlock)() = ^{
diff --git a/platform/darwin/src/MGLOfflinePack.h b/platform/darwin/src/MGLOfflinePack.h
index de76228ff0..8436434e68 100644
--- a/platform/darwin/src/MGLOfflinePack.h
+++ b/platform/darwin/src/MGLOfflinePack.h
@@ -26,8 +26,8 @@ typedef NS_ENUM (NSInteger, MGLOfflinePackState) {
The pack is incomplete and is not currently downloading.
This is the initial state of a pack that is created using the
- `-[MGLOfflinePack addPackForRegion:withContext:completionHandler:]` method,
- as well as after the `-[MGLOfflinePack suspend]` method is
+ `-[MGLOfflineStorage addPackForRegion:withContext:completionHandler:]`
+ method, as well as after the `-[MGLOfflinePack suspend]` method is
called.
*/
MGLOfflinePackStateInactive = 1,
diff --git a/platform/darwin/src/MGLOfflineStorage.h b/platform/darwin/src/MGLOfflineStorage.h
index 9557258b41..9125c5341e 100644
--- a/platform/darwin/src/MGLOfflineStorage.h
+++ b/platform/darwin/src/MGLOfflineStorage.h
@@ -19,7 +19,8 @@ NS_ASSUME_NONNULL_BEGIN
`userInfo` dictionary contains the pack’s current state in the
`MGLOfflinePackStateUserInfoKey` key and details about the pack’s current
progress in the `MGLOfflinePackProgressUserInfoKey` key. You may also consult
- the pack’s `state` and `progress` properties, which provide the same values.
+ the `MGLOfflinePack.state` and `MGLOfflinePack.progress` properties, which
+ provide the same values.
If you only need to observe changes in a particular pack’s progress, you can
alternatively observe KVO change notifications to the pack’s `progress` key
@@ -183,8 +184,8 @@ MGL_EXPORT
- (void)addPackForRegion:(id <MGLOfflineRegion>)region withContext:(NSData *)context completionHandler:(nullable MGLOfflinePackAdditionCompletionHandler)completion;
/**
- Unregisters the given offline pack and frees any resources that are no longer
- required by any remaining packs.
+ Unregisters the given offline pack and allows resources that are no longer
+ required by any remaining packs to be potentially freed.
As soon as this method is called on a pack, the pack becomes invalid; any
attempt to send it a message will result in an exception being thrown. If an
@@ -196,6 +197,13 @@ MGL_EXPORT
KVO change notifications on the shared offline storage object’s `packs` key
path. Removals from that array result in an `NSKeyValueChangeRemoval` change.
+ When you remove an offline pack, any resources that are required by that pack,
+ but not other packs, become eligible for deletion from offline storage. Because
+ the backing store used for offline storage is also used as a general purpose
+ cache for map resources, such resources may not be immediately removed if the
+ implementation determines that they remain useful for general performance of
+ the map.
+
@param pack The offline pack to remove.
@param completion The completion handler to call once the pack has been
removed. This handler is executed asynchronously on the main queue.
diff --git a/platform/darwin/src/MGLOpenGLStyleLayer.mm b/platform/darwin/src/MGLOpenGLStyleLayer.mm
index 745eacbf4e..5d81eb85ea 100644
--- a/platform/darwin/src/MGLOpenGLStyleLayer.mm
+++ b/platform/darwin/src/MGLOpenGLStyleLayer.mm
@@ -4,6 +4,7 @@
#import "MGLStyle_Private.h"
#import "MGLStyleLayer_Private.h"
+#include <mbgl/map/map.hpp>
#include <mbgl/style/layers/custom_layer.hpp>
#include <mbgl/math/wrap.hpp>
@@ -52,10 +53,19 @@ void MGLFinishCustomStyleLayer(void *context) {
}
/**
- An `MGLOpenGLStyleLayer` is a style layer that is rendered by OpenGL code in
- Objective-C blocks or Swift closures that you specify. You may initialize a new
- OpenGL style layer to add to an `MGLStyle` or obtain one from an `MGLMapView`’s
- current style using the `-[MGLStyle layerWithIdentifier:]` method.
+ An `MGLOpenGLStyleLayer` is a style layer that is rendered by OpenGL code that
+ you provide.
+
+ By default, this class does nothing. You can subclass this class to provide
+ custom OpenGL drawing code that is run on each frame of the map. Your subclass
+ should override the `-didMoveToMapView:`, `-willMoveFromMapView:`, and
+ `-drawInMapView:withContext:` methods.
+
+ You can access an existing OpenGL style layer using the
+ `-[MGLStyle layerWithIdentifier:]` method if you know its identifier;
+ otherwise, find it using the `MGLStyle.layers` property. You can also create a
+ new OpenGL style layer and add it to the style using a method such as
+ `-[MGLStyle addLayer:]`.
@warning This API is undocumented and therefore unsupported. It may change at
any time without notice.
diff --git a/platform/darwin/src/MGLOverlay.h b/platform/darwin/src/MGLOverlay.h
index 1066a86d1e..cc32bad1e6 100644
--- a/platform/darwin/src/MGLOverlay.h
+++ b/platform/darwin/src/MGLOverlay.h
@@ -9,9 +9,8 @@ NS_ASSUME_NONNULL_BEGIN
/**
The `MGLOverlay` protocol defines a specific type of annotation that represents
both a point and an area on a map. Overlay objects are essentially data objects
- that contain the geographic data needed to represent the map area. For example,
- overlays can take the form of common shapes such as rectangles and circles.
- They can also describe polygons and other complex shapes.
+ that contain the geographic data needed to represent the map area. Overlays can
+ take the form of a polyline or polygon.
You use overlays to layer more sophisticated content on top of a map view. For
example, you could use an overlay to show the boundaries of a national park or
diff --git a/platform/darwin/src/MGLPointAnnotation.h b/platform/darwin/src/MGLPointAnnotation.h
index 6b2e1dd43a..aeac43bd39 100644
--- a/platform/darwin/src/MGLPointAnnotation.h
+++ b/platform/darwin/src/MGLPointAnnotation.h
@@ -7,16 +7,38 @@
NS_ASSUME_NONNULL_BEGIN
/**
- The `MGLPointAnnotation` class defines a concrete annotation object located at
- a specified point. You can use this class, rather than define your own, in
- situations where all you want to do is associate a point on the map with a
- title.
+ An `MGLPointAnnotation` object represents a one-dimensional shape located at a
+ single geographical coordinate. Depending on how it is used, an
+ `MGLPointAnnotation` object is known as a point annotation or point shape. For
+ example, you could use a point shape to represent a city at low zoom levels, an
+ address at high zoom levels, or the location of a long press gesture.
+
+ You can add point shapes to the map by adding them to an `MGLShapeSource`
+ object. Configure the appearance of an `MGLShapeSource`’s or
+ `MGLVectorSource`’s point shapes collectively using an `MGLCircleStyleLayer` or
+ `MGLSymbolStyleLayer` object.
+
+ For more interactivity, add a selectable point annotation to a map view using
+ the `-[MGLMapView addAnnotation:]` method. Alternatively, define your own model
+ class that conforms to the `MGLAnnotation` protocol. Configure a point
+ annotation’s appearance using
+ `-[MGLMapViewDelegate mapView:imageForAnnotation:]` or
+ `-[MGLMapViewDelegate mapView:viewForAnnotation:]` (iOS only). A point
+ annotation’s `MGLShape.title` and `MGLShape.subtitle` properties define the
+ default content of the annotation’s callout (on iOS) or popover (on macOS).
+
+ To group multiple related points together in one shape, use an
+ `MGLPointCollection` or `MGLShapeCollection` object.
+
+ A point shape is known as a
+ <a href="https://tools.ietf.org/html/rfc7946#section-3.1.2">Point</a> geometry
+ in GeoJSON.
*/
MGL_EXPORT
@interface MGLPointAnnotation : MGLShape
/**
- The coordinate point of the annotation, specified as a latitude and longitude.
+ The coordinate point of the shape, specified as a latitude and longitude.
*/
@property (nonatomic, assign) CLLocationCoordinate2D coordinate;
diff --git a/platform/darwin/src/MGLPointAnnotation.mm b/platform/darwin/src/MGLPointAnnotation.mm
index d2e87f07d1..a2108a9e3b 100644
--- a/platform/darwin/src/MGLPointAnnotation.mm
+++ b/platform/darwin/src/MGLPointAnnotation.mm
@@ -1,6 +1,7 @@
#import "MGLPointAnnotation.h"
#import "MGLShape_Private.h"
+#import "NSCoder+MGLAdditions.h"
#import <mbgl/util/geometry.hpp>
@@ -9,6 +10,41 @@
@synthesize coordinate;
++ (BOOL)supportsSecureCoding
+{
+ return YES;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)coder
+{
+ if (self = [super initWithCoder:coder]) {
+ self.coordinate = [coder decodeMGLCoordinateForKey:@"coordinate"];
+ }
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder
+{
+ [super encodeWithCoder:coder];
+ [coder encodeMGLCoordinate:coordinate forKey:@"coordinate"];
+}
+
+- (BOOL)isEqual:(id)other
+{
+ if (other == self) return YES;
+ if (![other isKindOfClass:[MGLPointAnnotation class]]) return NO;
+
+ MGLPointAnnotation *otherAnnotation = other;
+ return ([super isEqual:other]
+ && self.coordinate.latitude == otherAnnotation.coordinate.latitude
+ && self.coordinate.longitude == otherAnnotation.coordinate.longitude);
+}
+
+- (NSUInteger)hash
+{
+ return [super hash] + @(self.coordinate.latitude).hash + @(self.coordinate.longitude).hash;
+}
+
- (NSString *)description
{
return [NSString stringWithFormat:@"<%@: %p; title = %@; subtitle = %@; coordinate = %f, %f>",
diff --git a/platform/darwin/src/MGLPointCollection.h b/platform/darwin/src/MGLPointCollection.h
index e090c60b10..c7054c6bbf 100644
--- a/platform/darwin/src/MGLPointCollection.h
+++ b/platform/darwin/src/MGLPointCollection.h
@@ -6,15 +6,26 @@
#import "MGLShape.h"
/**
- The `MGLPointCollection` class is used to define an array of disconnected
- coordinates. The points in the collection may be related but are not
- connected visually in any way.
+ An `MGLPointCollection` object represents a shape consisting of one or more
+ disconnected vertices, specified as `CLLocationCoordinate2D` instances. The
+ points in the collection may be related but are not connected spatially. For
+ example, you could use a point collection to represent all the trees in an
+ orchard.
- @note `MGLPointCollection` objects cannot be added to a map view using
- `-[MGLMapView addAnnotations:]` and related methods. However, when used in a
- `MGLPointCollectionFeature` to initialize a `MGLShapeSource` that is added
- to the map view's style, the point collection represents as a group of distinct
- annotations.
+ You can add point collections to the map by adding them to an `MGLShapeSource`
+ object. Configure the appearance of an `MGLShapeSource`’s or
+ `MGLVectorSource`’s point collections collectively using an
+ `MGLCircleStyleLayer` or `MGLSymbolStyleLayer` object.
+
+ You cannot add an `MGLPointCollection` object directly to a map view as an
+ annotation. However, you can create individual `MGLPointAnnotation` objects
+ from the `coordinates` array and add those annotation objects to the map view
+ using the `-[MGLMapView addAnnotations:]` method.
+
+ A point collection is known as a
+ <a href="https://tools.ietf.org/html/rfc7946#section-3.1.3">MultiPoint</a>
+ geometry in GeoJSON. Do not confuse `MGLPointCollection` with `MGLMultiPoint`,
+ the abstract superclass of `MGLPolyline` and `MGLPolygon`.
*/
MGL_EXPORT
@interface MGLPointCollection : MGLShape <MGLOverlay>
diff --git a/platform/darwin/src/MGLPointCollection.mm b/platform/darwin/src/MGLPointCollection.mm
index 387a575b2d..acd78b8b33 100644
--- a/platform/darwin/src/MGLPointCollection.mm
+++ b/platform/darwin/src/MGLPointCollection.mm
@@ -1,5 +1,6 @@
#import "MGLPointCollection_Private.h"
#import "MGLGeometry_Private.h"
+#import "NSArray+MGLAdditions.h"
#import <mbgl/util/geojson.hpp>
#import <mbgl/util/geometry.hpp>
@@ -8,12 +9,10 @@ NS_ASSUME_NONNULL_BEGIN
@implementation MGLPointCollection
{
- MGLCoordinateBounds _overlayBounds;
+ mbgl::optional<mbgl::LatLngBounds> _bounds;
std::vector<CLLocationCoordinate2D> _coordinates;
}
-@synthesize overlayBounds = _overlayBounds;
-
+ (instancetype)pointCollectionWithCoordinates:(const CLLocationCoordinate2D *)coords count:(NSUInteger)count
{
return [[self alloc] initWithCoordinates:coords count:count];
@@ -25,14 +24,41 @@ NS_ASSUME_NONNULL_BEGIN
if (self)
{
_coordinates = { coords, coords + count };
+ }
+ return self;
+}
+
+- (nullable instancetype)initWithCoder:(NSCoder *)decoder {
+ if (self = [super initWithCoder:decoder]) {
+ NSArray *coordinates = [decoder decodeObjectOfClass:[NSArray class] forKey:@"coordinates"];
+ _coordinates = [coordinates mgl_coordinates];
+ }
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder {
+ [super encodeWithCoder:coder];
+ [coder encodeObject:[NSArray mgl_coordinatesFromCoordinates:_coordinates] forKey:@"coordinates"];
+}
+
+- (BOOL)isEqual:(id)other {
+ if (self == other) return YES;
+ if (![other isKindOfClass:[MGLPointCollection class]]) return NO;
+
+ MGLPointCollection *otherCollection = (MGLPointCollection *)other;
+ return ([super isEqual:other]
+ && ((![self geoJSONDictionary] && ![otherCollection geoJSONDictionary]) || [[self geoJSONDictionary] isEqualToDictionary:[otherCollection geoJSONDictionary]]));
+}
+
+- (MGLCoordinateBounds)overlayBounds {
+ if (!_bounds) {
mbgl::LatLngBounds bounds = mbgl::LatLngBounds::empty();
- for (auto coordinate : _coordinates)
- {
+ for (auto coordinate : _coordinates) {
bounds.extend(mbgl::LatLng(coordinate.latitude, coordinate.longitude));
}
- _overlayBounds = MGLCoordinateBoundsFromLatLngBounds(bounds);
+ _bounds = bounds;
}
- return self;
+ return MGLCoordinateBoundsFromLatLngBounds(*_bounds);
}
- (NSUInteger)pointCount
@@ -65,7 +91,7 @@ NS_ASSUME_NONNULL_BEGIN
- (BOOL)intersectsOverlayBounds:(MGLCoordinateBounds)overlayBounds
{
- return MGLCoordinateBoundsIntersectsCoordinateBounds(_overlayBounds, overlayBounds);
+ return MGLCoordinateBoundsIntersectsCoordinateBounds(self.overlayBounds, overlayBounds);
}
- (mbgl::Geometry<double>)geometryObject
diff --git a/platform/darwin/src/MGLPolygon.h b/platform/darwin/src/MGLPolygon.h
index 57402326d5..560741a150 100644
--- a/platform/darwin/src/MGLPolygon.h
+++ b/platform/darwin/src/MGLPolygon.h
@@ -10,10 +10,39 @@
NS_ASSUME_NONNULL_BEGIN
/**
- The `MGLPolygon` class represents a shape consisting of one or more points that
- define a closed polygon. The points are connected end-to-end in the order they
- are provided. The first and last points are connected to each other to create
- the closed shape.
+ An `MGLPolygon` object represents a closed shape consisting of four or more
+ vertices, specified as `CLLocationCoordinate2D` instances, and the edges that
+ connect them. For example, you could use a polygon shape to represent a
+ building, a lake, or an area you want to highlight.
+
+ You can add polygon shapes to the map by adding them to an `MGLShapeSource`
+ object. Configure the appearance of an `MGLShapeSource`’s or
+ `MGLVectorSource`’s polygons collectively using an `MGLFillStyleLayer` or
+ `MGLSymbolStyleLayer` object.
+
+ Alternatively, you can add a polygon overlay directly to a map view using the
+ `-[MGLMapView addAnnotation:]` or `-[MGLMapView addOverlay:]` method. Configure
+ a polygon overlay’s appearance using
+ `-[MGLMapViewDelegate mapView:strokeColorForShapeAnnotation:]` and
+ `-[MGLMapViewDelegate mapView:fillColorForPolygonAnnotation:]`.
+
+ The vertices are automatically connected in the order in which you provide
+ them. You should close the polygon by specifying the same
+ `CLLocationCoordinate2D` as the first and last vertices; otherwise, the
+ polygon’s fill may not cover the area you expect it to. To avoid filling the
+ space within the shape, give the polygon a transparent fill or use an
+ `MGLPolyline` object.
+
+ A polygon may have one or more interior polygons, or holes, that you specify as
+ `MGLPolygon` objects with the `+polygonWithCoordinates:count:interiorPolygons:`
+ method. For example, if a polygon represents a lake, it could exclude an island
+ within the lake using an interior polygon. Interior polygons may not themselves
+ have interior polygons. To represent a shape that includes a polygon within a
+ hole or, more generally, to group multiple polygons together in one shape, use
+ an `MGLMultiPolygon` or `MGLShapeCollection` object.
+
+ To make the polygon straddle the antimeridian, specify some longitudes less
+ than −180 degrees or greater than 180 degrees.
*/
MGL_EXPORT
@interface MGLPolygon : MGLMultiPoint <MGLOverlay>
@@ -57,14 +86,21 @@ MGL_EXPORT
@end
/**
- The `MGLMultiPolygon` class represents a shape consisting of one or more
- polygons that do not overlap. For example, you would use an `MGLMultiPolygon`
- object to represent an atoll together with an island in the atoll’s lagoon:
- the atoll itself would be one `MGLPolygon` object, while the inner island would
- be another.
+ An `MGLMultiPolygon` object represents a shape consisting of one or more
+ polygons that do not overlap. For example, you could use a multipolygon shape
+ to represent the body of land that consists of an island surrounded by an
+ atoll: the inner island would be one `MGLPolygon` object, while the surrounding
+ atoll would be another. You could also use a multipolygon shape to represent a
+ group of disconnected but related buildings.
+
+ You can add multipolygon shapes to the map by adding them to an
+ `MGLShapeSource` object. Configure the appearance of an `MGLShapeSource`’s or
+ `MGLVectorSource`’s multipolygons collectively using an `MGLFillStyleLayer` or
+ `MGLSymbolStyleLayer` object.
- @note `MGLMultiPolygon` objects cannot be added to a map view using
- `-[MGLMapView addAnnotations:]` and related methods.
+ You cannot add an `MGLMultiPolygon` object directly to a map view using
+ `-[MGLMapView addAnnotation:]` or `-[MGLMapView addOverlay:]`. However, you can
+ add the `polygons` array’s items as overlays individually.
*/
MGL_EXPORT
@interface MGLMultiPolygon : MGLShape <MGLOverlay>
diff --git a/platform/darwin/src/MGLPolygon.mm b/platform/darwin/src/MGLPolygon.mm
index 393bd31d0d..565de017cc 100644
--- a/platform/darwin/src/MGLPolygon.mm
+++ b/platform/darwin/src/MGLPolygon.mm
@@ -28,6 +28,32 @@
return self;
}
+- (instancetype)initWithCoder:(NSCoder *)decoder {
+ self = [super initWithCoder:decoder];
+ if (self) {
+ _interiorPolygons = [decoder decodeObjectOfClass:[NSArray class] forKey:@"interiorPolygons"];
+ }
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder {
+ [super encodeWithCoder:coder];
+ [coder encodeObject:self.interiorPolygons forKey:@"interiorPolygons"];
+}
+
+- (BOOL)isEqual:(id)other {
+ if (self == other) return YES;
+ if (![other isKindOfClass:[MGLPolygon class]]) return NO;
+
+ MGLPolygon *otherPolygon = (MGLPolygon *)other;
+ return ([super isEqual:otherPolygon] &&
+ [[self geoJSONDictionary] isEqualToDictionary:[otherPolygon geoJSONDictionary]]);
+}
+
+- (NSUInteger)hash {
+ return [super hash] + [[self geoJSONDictionary] hash];
+}
+
- (mbgl::LinearRing<double>)ring {
NSUInteger count = self.pointCount;
CLLocationCoordinate2D *coordinates = self.coordinates;
@@ -100,6 +126,35 @@
return self;
}
+- (instancetype)initWithCoder:(NSCoder *)decoder {
+ if (self = [super initWithCoder:decoder]) {
+ _polygons = [decoder decodeObjectOfClass:[NSArray class] forKey:@"polygons"];
+ }
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder {
+ [super encodeWithCoder:coder];
+ [coder encodeObject:_polygons forKey:@"polygons"];
+}
+
+- (BOOL)isEqual:(id)other {
+ if (self == other) return YES;
+ if (![other isKindOfClass:[MGLMultiPolygon class]]) return NO;
+
+ MGLMultiPolygon *otherMultiPolygon = other;
+ return [super isEqual:other]
+ && [self.polygons isEqualToArray:otherMultiPolygon.polygons];
+}
+
+- (NSUInteger)hash {
+ NSUInteger hash = [super hash];
+ for (MGLPolygon *polygon in self.polygons) {
+ hash += [polygon hash];
+ }
+ return hash;
+}
+
- (BOOL)intersectsOverlayBounds:(MGLCoordinateBounds)overlayBounds {
return MGLCoordinateBoundsIntersectsCoordinateBounds(_overlayBounds, overlayBounds);
}
diff --git a/platform/darwin/src/MGLPolyline.h b/platform/darwin/src/MGLPolyline.h
index dfc471bae9..ca1f8e36cc 100644
--- a/platform/darwin/src/MGLPolyline.h
+++ b/platform/darwin/src/MGLPolyline.h
@@ -10,10 +10,35 @@
NS_ASSUME_NONNULL_BEGIN
/**
- The `MGLPolyline` class represents a shape consisting of one or more points
- that define connecting line segments. The points are connected end-to-end in
- the order they are provided. The first and last points are not connected to
- each other.
+ An `MGLPolyline` object represents a shape consisting of two or more vertices,
+ specified as `CLLocationCoordinate2D` instances, and the line segments that
+ connect them. For example, you could use an polyline to represent a road or the
+ path along which something moves.
+
+ You can add polyline shapes to the map by adding them to an `MGLShapeSource`
+ object. Configure the appearance of an `MGLShapeSource`’s or
+ `MGLVectorSource`’s polylines collectively using an `MGLLineStyleLayer` or
+ `MGLSymbolStyleLayer` object.
+
+ Alternatively, you can add a polyline overlay directly to a map view using the
+ `-[MGLMapView addAnnotation:]` or `-[MGLMapView addOverlay:]` method. Configure
+ a polyline overlay’s appearance using
+ `-[MGLMapViewDelegate mapView:strokeColorForShapeAnnotation:]` and
+ `-[MGLMapViewDelegate mapView:lineWidthForPolylineAnnotation:]`.
+
+ The vertices are automatically connected in the order in which you provide
+ them. The first and last vertices are not connected to each other, but you can
+ specify the same `CLLocationCoordinate2D` as the first and last vertices in
+ order to close the polyline. To fill the space within the shape, use an
+ `MGLPolygon` object. To group multiple polylines together in one shape, use an
+ `MGLMultiPolyline` or `MGLShapeCollection` object.
+
+ To make the polyline straddle the antimeridian, specify some longitudes less
+ than −180 degrees or greater than 180 degrees.
+
+ A polyline is known as a
+ <a href="https://tools.ietf.org/html/rfc7946#section-3.1.4">LineString</a>
+ geometry in GeoJSON.
*/
MGL_EXPORT
@interface MGLPolyline : MGLMultiPoint <MGLOverlay>
@@ -32,14 +57,23 @@ MGL_EXPORT
@end
/**
- The `MGLMultiPolyline` class represents a shape consisting of one or more
- polylines. For example, you could use an `MGLMultiPolyline` object to represent
- both sides of a divided highway (dual carriageway), excluding the median
- (central reservation): each carriageway would be a distinct `MGLPolyline`
- object.
-
- @note `MGLMultiPolyline` objects cannot be added to a map view using
- `-[MGLMapView addAnnotations:]` and related methods.
+ An `MGLMultiPolyline` object represents a shape consisting of one or more
+ polylines. For example, you could use a multipolyline shape to represent both
+ sides of a divided highway (dual carriageway), excluding the median (central
+ reservation): each carriageway would be a distinct `MGLPolyline` object.
+
+ You can add multipolyline shapes to the map by adding them to an
+ `MGLShapeSource` object. Configure the appearance of an `MGLShapeSource`’s or
+ `MGLVectorSource`’s multipolylines collectively using an `MGLLineStyleLayer` or
+ `MGLSymbolStyleLayer` object.
+
+ You cannot add an `MGLMultiPolyline` object directly to a map view using
+ `-[MGLMapView addAnnotation:]` or `-[MGLMapView addOverlay:]`. However, you can
+ add the `polylines` array’s items as overlays individually.
+
+ A multipolyline is known as a
+ <a href="https://tools.ietf.org/html/rfc7946#section-3.1.5">MultiLineString</a>
+ geometry in GeoJSON.
*/
MGL_EXPORT
@interface MGLMultiPolyline : MGLShape <MGLOverlay>
diff --git a/platform/darwin/src/MGLPolyline.mm b/platform/darwin/src/MGLPolyline.mm
index 0baeb68e1a..e6b1cdebf6 100644
--- a/platform/darwin/src/MGLPolyline.mm
+++ b/platform/darwin/src/MGLPolyline.mm
@@ -80,6 +80,36 @@
return self;
}
+- (instancetype)initWithCoder:(NSCoder *)decoder {
+ if (self = [super initWithCoder:decoder]) {
+ _polylines = [decoder decodeObjectOfClass:[NSArray class] forKey:@"polylines"];
+ }
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder {
+ [super encodeWithCoder:coder];
+ [coder encodeObject:_polylines forKey:@"polylines"];
+}
+
+- (BOOL)isEqual:(id)other
+{
+ if (self == other) return YES;
+ if (![other isKindOfClass:[MGLMultiPolyline class]]) return NO;
+
+ MGLMultiPolyline *otherMultipoline = other;
+ return ([super isEqual:otherMultipoline]
+ && [self.polylines isEqualToArray:otherMultipoline.polylines]);
+}
+
+- (NSUInteger)hash {
+ NSUInteger hash = [super hash];
+ for (MGLPolyline *polyline in self.polylines) {
+ hash += [polyline hash];
+ }
+ return hash;
+}
+
- (BOOL)intersectsOverlayBounds:(MGLCoordinateBounds)overlayBounds {
return MGLCoordinateBoundsIntersectsCoordinateBounds(_overlayBounds, overlayBounds);
}
diff --git a/platform/darwin/src/MGLRasterSource.h b/platform/darwin/src/MGLRasterSource.h
index 80b740692c..ac5be60105 100644
--- a/platform/darwin/src/MGLRasterSource.h
+++ b/platform/darwin/src/MGLRasterSource.h
@@ -36,6 +36,20 @@ extern MGL_EXPORT const MGLTileSourceOption MGLTileSourceOptionTileSize;
`MGLRasterSource` 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 source = MGLRasterSource(identifier: "clouds", tileURLTemplates: ["https://example.com/raster-tiles/{z}/{x}/{y}.png"], options: [
+ .minimumZoomLevel: 9,
+ .maximumZoomLevel: 16,
+ .tileSize: 512,
+ .attributionInfos: [
+ MGLAttributionInfo(title: NSAttributedString(string: "© Mapbox"), url: URL(string: "http://mapbox.com"))
+ ]
+ ])
+ mapView.style?.addSource(source)
+ ```
*/
MGL_EXPORT
@interface MGLRasterSource : MGLTileSource
diff --git a/platform/darwin/src/MGLRasterSource.mm b/platform/darwin/src/MGLRasterSource.mm
index edca8bced1..fd36413fe0 100644
--- a/platform/darwin/src/MGLRasterSource.mm
+++ b/platform/darwin/src/MGLRasterSource.mm
@@ -5,6 +5,7 @@
#import "MGLTileSource_Private.h"
#import "NSURL+MGLAdditions.h"
+#include <mbgl/map/map.hpp>
#include <mbgl/style/sources/raster_source.hpp>
const MGLTileSourceOption MGLTileSourceOptionTileSize = @"MGLTileSourceOptionTileSize";
@@ -50,7 +51,7 @@ static const CGFloat MGLRasterSourceRetinaTileSize = 512;
if (self = [super initWithIdentifier:identifier tileURLTemplates:tileURLTemplates options:options]) {
mbgl::Tileset tileSet = MGLTileSetFromTileURLTemplates(tileURLTemplates, options);
- uint16_t tileSize;
+ uint16_t tileSize = MGLRasterSourceRetinaTileSize;
if (NSNumber *tileSizeNumber = options[MGLTileSourceOptionTileSize]) {
if (![tileSizeNumber isKindOfClass:[NSNumber class]]) {
[NSException raise:NSInvalidArgumentException
@@ -95,6 +96,11 @@ static const CGFloat MGLRasterSourceRetinaTileSize = 512;
super.rawSource = rawSource;
}
+- (NSURL *)configurationURL {
+ auto url = self.rawSource->getURL();
+ return url ? [NSURL URLWithString:@(url->c_str())] : nil;
+}
+
- (NSString *)attributionHTMLString {
auto attribution = self.rawSource->getAttribution();
return attribution ? @(attribution->c_str()) : nil;
diff --git a/platform/darwin/src/MGLRasterStyleLayer.h b/platform/darwin/src/MGLRasterStyleLayer.h
index 1bdff12466..cd57a74f6a 100644
--- a/platform/darwin/src/MGLRasterStyleLayer.h
+++ b/platform/darwin/src/MGLRasterStyleLayer.h
@@ -8,10 +8,30 @@
NS_ASSUME_NONNULL_BEGIN
/**
- A raster layer which allows customization of styling properties at runtime. You may
- instantiate a new raster layer to add to a map style or you may query an
- `MGLMapView` for its `style` and obtain existing layers using the
- `-[MGLStyle layerWithIdentifier:]` method.
+ An `MGLRasterStyleLayer` is a style layer that renders raster tiles on the map.
+
+ Use a raster style layer to configure the color parameters of raster tiles
+ loaded by an `MGLRasterSource` object. For example, you could use a raster
+ style layer to render <a href="https://www.mapbox.com/satellite/">Mapbox
+ Satellite</a> imagery, a <a
+ href="https://www.mapbox.com/help/define-tileset/#raster-tilesets">raster tile
+ set</a> uploaded to Mapbox Studio, or a raster map authored in <a
+ href="https://tilemill-project.github.io/tilemill/">TileMill</a>, the classic
+ Mapbox Editor, or Mapbox Studio Classic.
+
+ You can access an existing raster style layer using the
+ `-[MGLStyle layerWithIdentifier:]` method if you know its identifier;
+ otherwise, find it using the `MGLStyle.layers` property. You can also create a
+ new raster style layer and add it to the style using a method such as
+ `-[MGLStyle addLayer:]`.
+
+ ### Example
+
+ ```swift
+ let layer = MGLRasterStyleLayer(identifier: "clouds", source: source)
+ layer.rasterOpacity = MGLStyleValue(rawValue: 0.5)
+ mapView.style?.addLayer(layer)
+ ```
*/
MGL_EXPORT
@interface MGLRasterStyleLayer : MGLForegroundStyleLayer
@@ -19,70 +39,89 @@ MGL_EXPORT
#pragma mark - Accessing the Paint Attributes
/**
- Increase or reduce the brightness of the image. The value is the maximum brightness.
+ Increase or reduce the brightness of the image. The value is the maximum
+ brightness.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing the float `1`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `1`. Set this property to `nil` to reset
+ it to the default value.
- This attribute corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-raster-brightness-max"><code>raster-brightness-max</code></a> paint property in the Mapbox Style Specification.
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-raster-brightness-max"><code>raster-brightness-max</code></a>
+ layout property in the Mapbox Style Specification.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *maximumRasterBrightness;
-
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *rasterBrightnessMax __attribute__((unavailable("Use maximumRasterBrightness instead.")));
/**
- Increase or reduce the brightness of the image. The value is the minimum brightness.
+ Increase or reduce the brightness of the image. The value is the minimum
+ brightness.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing the float `0`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `0`. Set this property to `nil` to reset
+ it to the default value.
- This attribute corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-raster-brightness-min"><code>raster-brightness-min</code></a> paint property in the Mapbox Style Specification.
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-raster-brightness-min"><code>raster-brightness-min</code></a>
+ layout property in the Mapbox Style Specification.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *minimumRasterBrightness;
-
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *rasterBrightnessMin __attribute__((unavailable("Use minimumRasterBrightness instead.")));
/**
Increase or reduce the contrast of the image.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing the float `0`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `0`. Set this property to `nil` to reset
+ it to the default value.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *rasterContrast;
/**
Fade duration when a new tile is added.
-
+
This property is measured in milliseconds.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing the float `300`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `300`. Set this property to `nil` to
+ reset it to the default value.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *rasterFadeDuration;
/**
Rotates hues around the color wheel.
-
+
This property is measured in degrees.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing the float `0`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `0`. Set this property to `nil` to reset
+ it to the default value.
- This attribute corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-raster-hue-rotate"><code>raster-hue-rotate</code></a> paint property in the Mapbox Style Specification.
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-raster-hue-rotate"><code>raster-hue-rotate</code></a>
+ layout property in the Mapbox Style Specification.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *rasterHueRotation;
-
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *rasterHueRotate __attribute__((unavailable("Use rasterHueRotation instead.")));
/**
The opacity at which the image will be drawn.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing the float `1`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `1`. Set this property to `nil` to reset
+ it to the default value.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *rasterOpacity;
/**
Increase or reduce the saturation of the image.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing the float `0`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `0`. Set this property to `nil` to reset
+ it to the default value.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *rasterSaturation;
diff --git a/platform/darwin/src/MGLRasterStyleLayer.mm b/platform/darwin/src/MGLRasterStyleLayer.mm
index e61532773c..87afb8da9d 100644
--- a/platform/darwin/src/MGLRasterStyleLayer.mm
+++ b/platform/darwin/src/MGLRasterStyleLayer.mm
@@ -1,5 +1,5 @@
// This file is generated.
-// Edit platform/darwin/scripts/generate-style-code.js, then run `make style-code-darwin`.
+// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
#import "MGLSource.h"
#import "MGLMapView_Private.h"
@@ -8,6 +8,7 @@
#import "MGLStyleValue_Private.h"
#import "MGLRasterStyleLayer.h"
+#include <mbgl/map/map.hpp>
#include <mbgl/style/layers/raster_layer.hpp>
@interface MGLRasterStyleLayer ()
@@ -41,6 +42,13 @@
super.rawLayer = rawLayer;
}
+- (NSString *)sourceIdentifier
+{
+ MGLAssertStyleLayerIsValid();
+
+ return @(self.rawLayer->getSourceID().c_str());
+}
+
#pragma mark - Adding to and removing from a map view
- (void)addToMapView:(MGLMapView *)mapView belowLayer:(MGLStyleLayer *)otherLayer
@@ -96,9 +104,11 @@
return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
}
-
- (void)setRasterBrightnessMax:(MGLStyleValue<NSNumber *> *)rasterBrightnessMax {
- NSAssert(NO, @"Use -setMaximumRasterBrightness: instead.");
+}
+
+- (MGLStyleValue<NSNumber *> *)rasterBrightnessMax {
+ return self.maximumRasterBrightness;
}
- (void)setMinimumRasterBrightness:(MGLStyleValue<NSNumber *> *)minimumRasterBrightness {
@@ -115,9 +125,11 @@
return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
}
-
- (void)setRasterBrightnessMin:(MGLStyleValue<NSNumber *> *)rasterBrightnessMin {
- NSAssert(NO, @"Use -setMinimumRasterBrightness: instead.");
+}
+
+- (MGLStyleValue<NSNumber *> *)rasterBrightnessMin {
+ return self.minimumRasterBrightness;
}
- (void)setRasterContrast:(MGLStyleValue<NSNumber *> *)rasterContrast {
@@ -162,9 +174,11 @@
return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
}
-
- (void)setRasterHueRotate:(MGLStyleValue<NSNumber *> *)rasterHueRotate {
- NSAssert(NO, @"Use -setRasterHueRotation: instead.");
+}
+
+- (MGLStyleValue<NSNumber *> *)rasterHueRotate {
+ return self.rasterHueRotation;
}
- (void)setRasterOpacity:(MGLStyleValue<NSNumber *> *)rasterOpacity {
diff --git a/platform/darwin/src/MGLShape.h b/platform/darwin/src/MGLShape.h
index cc545d783f..d1c150d02e 100644
--- a/platform/darwin/src/MGLShape.h
+++ b/platform/darwin/src/MGLShape.h
@@ -6,14 +6,30 @@
NS_ASSUME_NONNULL_BEGIN
/**
- The `MGLShape` class is an abstract class that defines the basic properties for
- all shape-based annotation objects. This class must be subclassed and cannot be
- used as is. Subclasses are responsible for defining the geometry of the shape
- and providing an appropriate value for the coordinate property inherited from
- the `MGLAnnotation` protocol.
+ `MGLShape` is an abstract class that represents a shape or annotation. Shapes
+ constitute the content of a map – not only the overlays atop the map, but also
+ the content that forms the base map.
+
+ You do not create instances of this class directly or create subclasses of this
+ class. Instead, you create instances of `MGLPointAnnotation`,
+ `MGLPointCollection`, `MGLPolyline`, `MGLMultiPolyline`, `MGLPolygon`,
+ `MGLMultiPolygon`, or `MGLShapeCollection`. The shape classes correspond to the
+ <a href="https://tools.ietf.org/html/rfc7946#section-3.1">Geometry</a> object
+ types in the GeoJSON standard, but some have nonstandard names for backwards
+ compatibility.
+
+ Although you do not create instances of this class directly, you can use its
+ `+[MGLShape shapeWithData:encoding:error:]` factory method to create one of the
+ concrete subclasses of `MGLShape` noted above from GeoJSON data.
+
+ You can add shapes to the map by adding them to an `MGLShapeSource` object.
+ Configure the appearance of an `MGLShapeSource`’s or `MGLVectorSource`’s shapes
+ collectively using a concrete instance of `MGLVectorStyleLayer`. Alternatively,
+ you can add some kinds of shapes directly to a map view as annotations or
+ overlays.
*/
MGL_EXPORT
-@interface MGLShape : NSObject <MGLAnnotation>
+@interface MGLShape : NSObject <MGLAnnotation, NSSecureCoding>
#pragma mark Creating a Shape
@@ -27,6 +43,14 @@ MGL_EXPORT
collection object, the returned value is an instance of
`MGLShapeCollectionFeature`.
+ ### Example
+
+ ```swift
+ let url = mainBundle.url(forResource: "amsterdam", withExtension: "geojson")!
+ let data = try! Data(contentsOf: url)
+ let feature = try! MGLShape(data: data, encoding: String.Encoding.utf8.rawValue) as! MGLShapeCollectionFeature
+ ```
+
@param data String data containing GeoJSON source code.
@param encoding The encoding used by `data`.
@param outError Upon return, if an error has occurred, a pointer to an
@@ -40,21 +64,34 @@ MGL_EXPORT
#pragma mark Accessing the Shape Attributes
/**
- The title of the shape annotation. The default value of this property is `nil`.
+ The title of the shape annotation.
+
+ The default value of this property is `nil`.
+
+ This property is ignored when the shape is used in an `MGLShapeSource`. To name
+ a shape used in a shape source, create an `MGLFeature` and add an attribute to
+ the `MGLFeature.attributes` property.
*/
@property (nonatomic, copy, nullable) NSString *title;
/**
The subtitle of the shape annotation. The default value of this property is
`nil`.
+
+ This property is ignored when the shape is used in an `MGLShapeSource`. To
+ provide additional information about a shape used in a shape source, create an
+ `MGLFeature` and add an attribute to the `MGLFeature.attributes` property.
*/
@property (nonatomic, copy, nullable) NSString *subtitle;
#if !TARGET_OS_IPHONE
/**
- The tooltip of the shape annotation. The default value of this property is
- `nil`.
+ The tooltip of the shape annotation.
+
+ The default value of this property is `nil`.
+
+ This property is ignored when the shape is used in an `MGLShapeSource`.
*/
@property (nonatomic, copy, nullable) NSString *toolTip;
diff --git a/platform/darwin/src/MGLShape.mm b/platform/darwin/src/MGLShape.mm
index 889ef8b3d7..984235fd97 100644
--- a/platform/darwin/src/MGLShape.mm
+++ b/platform/darwin/src/MGLShape.mm
@@ -2,6 +2,15 @@
#import "MGLFeature_Private.h"
+#import "NSString+MGLAdditions.h"
+#import "MGLTypes.h"
+
+#import <mbgl/util/geo.hpp>
+
+bool operator==(const CLLocationCoordinate2D lhs, const CLLocationCoordinate2D rhs) {
+ return lhs.latitude == rhs.latitude && lhs.longitude == rhs.longitude;
+}
+
@implementation MGLShape
+ (nullable MGLShape *)shapeWithData:(NSData *)data encoding:(NSStringEncoding)encoding error:(NSError * _Nullable *)outError {
@@ -42,10 +51,61 @@
return [string dataUsingEncoding:NSUTF8StringEncoding];
}
-- (CLLocationCoordinate2D)coordinate {
- [NSException raise:@"MGLAbstractClassException"
- format:@"MGLShape is an abstract class"];
++ (BOOL)supportsSecureCoding
+{
+ return YES;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)coder
+{
+ if (self = [super init]) {
+ _title = [coder decodeObjectOfClass:[NSString class] forKey:@"title"];
+ _subtitle = [coder decodeObjectOfClass:[NSString class] forKey:@"subtitle"];
+#if !TARGET_OS_IPHONE
+ _toolTip = [coder decodeObjectOfClass:[NSString class] forKey:@"toolTip"];
+#endif
+ }
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder
+{
+ [coder encodeObject:_title forKey:@"title"];
+ [coder encodeObject:_subtitle forKey:@"subtitle"];
+#if !TARGET_OS_IPHONE
+ [coder encodeObject:_toolTip forKey:@"toolTip"];
+#endif
+}
+
+- (BOOL)isEqual:(id)other
+{
+ if (other == self) { return YES; }
+ id <MGLAnnotation> annotation = other;
+
+#if TARGET_OS_IPHONE
+ return ((!_title && ![annotation title]) || [_title isEqualToString:[annotation title]])
+ && ((!_subtitle && ![annotation subtitle]) || [_subtitle isEqualToString:[annotation subtitle]]);
+#else
+ return ((!_title && ![annotation title]) || [_title isEqualToString:[annotation title]])
+ && ((!_subtitle && ![annotation subtitle]) || [_subtitle isEqualToString:[annotation subtitle]])
+ && ((!_toolTip && ![annotation toolTip]) || [_toolTip isEqualToString:[annotation toolTip]]);
+#endif
+}
+
+- (NSUInteger)hash
+{
+ NSUInteger hash = _title.hash + _subtitle.hash;
+#if !TARGET_OS_IPHONE
+ hash += _toolTip.hash;
+#endif
+ return hash;
+}
+- (CLLocationCoordinate2D)coordinate
+{
+ [[NSException exceptionWithName:@"MGLAbstractClassException"
+ reason:@"MGLShape is an abstract class"
+ userInfo:nil] raise];
return kCLLocationCoordinate2DInvalid;
}
diff --git a/platform/darwin/src/MGLShapeCollection.h b/platform/darwin/src/MGLShapeCollection.h
index dfad080de2..5d2ce588c9 100644
--- a/platform/darwin/src/MGLShapeCollection.h
+++ b/platform/darwin/src/MGLShapeCollection.h
@@ -8,12 +8,30 @@
NS_ASSUME_NONNULL_BEGIN
/**
- The `MGLShapeCollection` class represents a shape consisting of one or more
+ An `MGLShapeCollection` object represents a shape consisting of zero or more
distinct but related shapes that are instances of `MGLShape`. The constituent
shapes can be a mixture of different kinds of shapes.
- @note `MGLShapeCollection` objects cannot be added to a map view using
- `-[MGLMapView addAnnotations:]` and related methods.
+ `MGLShapeCollection` is most commonly used to add multiple shapes to a single
+ `MGLShapeSource`. Configure the appearance of an `MGLShapeSource`’s or
+ `MGLVectorSource`’s shape collection collectively using an
+ `MGLSymbolStyleLayer` object, or use multiple instances of
+ `MGLCircleStyleLayer`, `MGLFillStyleLayer`, and `MGLLineStyleLayer` to
+ configure the appearance of each kind of shape inside the collection.
+
+ You cannot add an `MGLShapeCollection` object directly to a map view as an
+ annotation. However, you can create individual `MGLPointAnnotation`,
+ `MGLPolyline`, and `MGLPolygon` objects from the `shapes` array and add those
+ annotation objects to the map view using the `-[MGLMapView addAnnotations:]`
+ method.
+
+ To represent a collection of point, polyline, or polygon shapes, it may be more
+ convenient to use an `MGLPointCollection`, `MGLMultiPolyline`, or
+ `MGLMultiPolygon` object, respectively.
+
+ A multipolyline is known as a
+ <a href="https://tools.ietf.org/html/rfc7946#section-3.1.8">GeometryCollection</a>
+ geometry in GeoJSON.
*/
MGL_EXPORT
@interface MGLShapeCollection : MGLShape
diff --git a/platform/darwin/src/MGLShapeCollection.mm b/platform/darwin/src/MGLShapeCollection.mm
index e317a443fe..4b468a1cbb 100644
--- a/platform/darwin/src/MGLShapeCollection.mm
+++ b/platform/darwin/src/MGLShapeCollection.mm
@@ -12,12 +12,40 @@
- (instancetype)initWithShapes:(NS_ARRAY_OF(MGLShape *) *)shapes {
if (self = [super init]) {
- NSAssert(shapes.count, @"Cannot create an empty shape collection");
_shapes = shapes.copy;
}
return self;
}
+- (instancetype)initWithCoder:(NSCoder *)decoder {
+ if (self = [super initWithCoder:decoder]) {
+ _shapes = [decoder decodeObjectOfClass:[NSArray class] forKey:@"shapes"];
+ }
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder {
+ [super encodeWithCoder:coder];
+ [coder encodeObject:_shapes forKey:@"shapes"];
+}
+
+- (BOOL)isEqual:(id)other {
+ if (self == other) return YES;
+ if (![other isKindOfClass:[MGLShapeCollection class]]) return NO;
+
+ MGLShapeCollection *otherShapeCollection = other;
+ return [super isEqual:otherShapeCollection]
+ && [_shapes isEqualToArray:otherShapeCollection.shapes];
+}
+
+- (NSUInteger)hash {
+ NSUInteger hash = [super hash];
+ for (MGLShape *shape in _shapes) {
+ hash += [shape hash];
+ }
+ return hash;
+}
+
- (CLLocationCoordinate2D)coordinate {
return _shapes.firstObject.coordinate;
}
diff --git a/platform/darwin/src/MGLShapeSource.h b/platform/darwin/src/MGLShapeSource.h
index 7bd85c8d1f..34806c548d 100644
--- a/platform/darwin/src/MGLShapeSource.h
+++ b/platform/darwin/src/MGLShapeSource.h
@@ -17,6 +17,10 @@ typedef NSString *MGLShapeSourceOption NS_STRING_ENUM;
An `NSNumber` object containing a Boolean enabling or disabling clustering.
If the `shape` property contains point shapes, setting this option to
`YES` clusters the points by radius into groups. The default value is `NO`.
+
+ This attribute corresponds to the
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-cluster"><code>cluster</code></a>
+ source property in the Mapbox Style Specification.
*/
extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionClustered;
@@ -32,6 +36,10 @@ extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionClusterRadius;
which to cluster points if clustering is enabled. Defaults to one zoom level
less than the value of `MGLShapeSourceOptionMaximumZoomLevel` so that, at the
maximum zoom level, the shapes are not clustered.
+
+ This attribute corresponds to the
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-clusterMaxZoom"><code>clusterMaxZoom</code></a>
+ source property in the Mapbox Style Specification.
*/
extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevelForClustering;
@@ -39,6 +47,10 @@ extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLeve
An `NSNumber` object containing an integer; specifies the maximum zoom level at
which to create vector tiles. A greater value produces greater detail at high
zoom levels. The default value is 18.
+
+ This attribute corresponds to the
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-maxzoom"><code>maxzoom</code></a>
+ source property in the Mapbox Style Specification.
*/
extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevel;
@@ -47,6 +59,10 @@ extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLeve
buffer on each side. A value of 0 produces no buffer. A value of 512 produces a
buffer as wide as the tile itself. Larger values produce fewer rendering
artifacts near tile edges and slower performance. The default value is 128.
+
+ This attribute corresponds to the
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-buffer"><code>buffer</code></a>
+ source property in the Mapbox Style Specification.
*/
extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionBuffer;
@@ -54,6 +70,10 @@ extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionBuffer;
An `NSNumber` object containing a double; specifies the Douglas-Peucker
simplification tolerance. A greater value produces simpler geometries and
improves performance. The default value is 0.375.
+
+ This attribute corresponds to the
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-tolerance"><code>tolerance</code></a>
+ source property in the Mapbox Style Specification.
*/
extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance;
@@ -75,6 +95,18 @@ extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionSimplificationT
Any vector style layer initialized with a shape source should have a `nil`
value in its `sourceLayerIdentifier` property.
+
+ ### Example
+
+ ```swift
+ var coordinates: [CLLocationCoordinate2D] = [
+ CLLocationCoordinate2D(latitude: 37.77, longitude: -122.42),
+ CLLocationCoordinate2D(latitude: 38.91, longitude: -77.04),
+ ]
+ let polyline = MGLPolylineFeature(coordinates: &coordinates, count: UInt(coordinates.count))
+ let source = MGLShapeSource(identifier: "lines", features: [polyline], options: nil)
+ mapView.style?.addSource(source)
+ ```
*/
MGL_EXPORT
@interface MGLShapeSource : MGLSource
@@ -100,7 +132,9 @@ MGL_EXPORT
To specify attributes about the shape, use an instance of an `MGLShape`
subclass that conforms to the `MGLFeature` protocol, such as `MGLPointFeature`.
To include multiple shapes in the source, use an `MGLShapeCollection` or
- `MGLShapeCollectionFeature` object.
+ `MGLShapeCollectionFeature` object, or use the
+ `-initWithIdentifier:features:options:` or
+ `-initWithIdentifier:shapes:options:` methods.
To create a shape from GeoJSON source code, use the
`+[MGLShape shapeWithData:encoding:error:]` method.
@@ -112,6 +146,45 @@ MGL_EXPORT
*/
- (instancetype)initWithIdentifier:(NSString *)identifier shape:(nullable MGLShape *)shape options:(nullable NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options NS_DESIGNATED_INITIALIZER;
+/**
+ Returns a shape source with an identifier, an array of features, and a dictionary
+ of options for the source.
+
+ Unlike `-initWithIdentifier:shapes:options:`, this method accepts `MGLFeature`
+ instances, such as `MGLPointFeature` objects, whose attributes you can use when
+ applying a predicate to `MGLVectorStyleLayer` or configuring a style layer’s
+ appearance.
+
+ To create a shape from GeoJSON source code, use the
+ `+[MGLShape shapeWithData:encoding:error:]` method.
+
+ @param identifier A string that uniquely identifies the source.
+ @param features An array of objects that conform to the MGLFeature protocol.
+ @param options An `NSDictionary` of options for this source.
+ @return An initialized shape source.
+ */
+- (instancetype)initWithIdentifier:(NSString *)identifier features:(NS_ARRAY_OF(MGLShape<MGLFeature> *) *)features options:(nullable NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options;
+
+/**
+ Returns a shape source with an identifier, an array of shapes, and a dictionary of
+ options for the source.
+
+ Any `MGLFeature` instance passed into this initializer is treated as an ordinary
+ shape, causing any attributes to be inaccessible to an `MGLVectorStyleLayer` when
+ evaluating a predicate or configuring certain layout or paint attributes. To
+ preserve the attributes associated with each feature, use the
+ `-initWithIdentifier:features:options:` method instead.
+
+ To create a shape from GeoJSON source code, use the
+ `+[MGLShape shapeWithData:encoding:error:]` method.
+
+ @param identifier A string that uniquely identifies the source.
+ @param shapes An array of shapes; each shape is a member of a concrete subclass of MGLShape.
+ @param options An `NSDictionary` of options for this source.
+ @return An initialized shape source.
+ */
+- (instancetype)initWithIdentifier:(NSString *)identifier shapes:(NS_ARRAY_OF(MGLShape *) *)shapes options:(nullable NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options;
+
#pragma mark Accessing a Source’s Content
/**
@@ -121,6 +194,9 @@ MGL_EXPORT
If the receiver was initialized using `-initWithIdentifier:URL:options:`, this
property is set to `nil`. This property is unavailable until the receiver is
passed into `-[MGLStyle addSource:]`.
+
+ You can get/set the shapes within a collection via this property. Actions must
+ be performed on the application's main thread.
*/
@property (nonatomic, copy, nullable) MGLShape *shape;
diff --git a/platform/darwin/src/MGLShapeSource.mm b/platform/darwin/src/MGLShapeSource.mm
index d062e656c2..245b7f2de2 100644
--- a/platform/darwin/src/MGLShapeSource.mm
+++ b/platform/darwin/src/MGLShapeSource.mm
@@ -7,6 +7,7 @@
#import "NSURL+MGLAdditions.h"
+#include <mbgl/map/map.hpp>
#include <mbgl/style/sources/geojson_source.hpp>
const MGLShapeSourceOption MGLShapeSourceOptionClustered = @"MGLShapeSourceOptionClustered";
@@ -55,6 +56,21 @@ const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance = @"MGLSh
return self;
}
+- (instancetype)initWithIdentifier:(NSString *)identifier features:(NS_ARRAY_OF(MGLShape<MGLFeature> *) *)features options:(nullable NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options {
+ for (id <MGLFeature> feature in features) {
+ if (![feature conformsToProtocol:@protocol(MGLFeature)]) {
+ [NSException raise:NSInvalidArgumentException format:@"The object %@ included in the features argument does not conform to the MGLFeature protocol.", feature];
+ }
+ }
+ MGLShapeCollectionFeature *shapeCollectionFeature = [MGLShapeCollectionFeature shapeCollectionWithShapes:features];
+ return [self initWithIdentifier:identifier shape:shapeCollectionFeature options:options];
+}
+
+- (instancetype)initWithIdentifier:(NSString *)identifier shapes:(NS_ARRAY_OF(MGLShape *) *)shapes options:(nullable NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options {
+ MGLShapeCollection *shapeCollection = [MGLShapeCollection shapeCollectionWithShapes:shapes];
+ return [self initWithIdentifier:identifier shape:shapeCollection options:options];
+}
+
- (instancetype)initWithRawSource:(mbgl::style::GeoJSONSource *)rawSource {
return [super initWithRawSource:rawSource];
}
diff --git a/platform/darwin/src/MGLShape_Private.h b/platform/darwin/src/MGLShape_Private.h
index 15d1c1eb97..9821d49176 100644
--- a/platform/darwin/src/MGLShape_Private.h
+++ b/platform/darwin/src/MGLShape_Private.h
@@ -2,6 +2,9 @@
#import <mbgl/util/geojson.hpp>
#import <mbgl/util/geometry.hpp>
+#import <mbgl/util/geo.hpp>
+
+bool operator==(const CLLocationCoordinate2D lhs, const CLLocationCoordinate2D rhs);
@interface MGLShape (Private)
diff --git a/platform/darwin/src/MGLSource.h b/platform/darwin/src/MGLSource.h
index 550996f51b..7b3242e3ae 100644
--- a/platform/darwin/src/MGLSource.h
+++ b/platform/darwin/src/MGLSource.h
@@ -18,7 +18,7 @@ NS_ASSUME_NONNULL_BEGIN
Do not create instances of this class directly, and do not create your own
subclasses of this class. Instead, create instances of `MGLShapeSource` and the
- concrete subclasses of `MGLTileSource`.
+ concrete subclasses of `MGLTileSource`, `MGLVectorSource` and `MGLRasterSource`.
*/
MGL_EXPORT
@interface MGLSource : NSObject
diff --git a/platform/darwin/src/MGLStyle.h b/platform/darwin/src/MGLStyle.h
index 6192632e75..9bd35142c2 100644
--- a/platform/darwin/src/MGLStyle.h
+++ b/platform/darwin/src/MGLStyle.h
@@ -41,10 +41,10 @@ static MGL_EXPORT const NSInteger MGLStyleDefaultVersion = 9;
via `-[MGLMapView style]` by updating the style's data sources or layers.
@note Wait until the map style has finished loading before modifying a map's
- style via any of the MGLStyle instance methods below.
- You can use the `MGLMapViewDelegate` methods `-mapViewDidFinishLoadingMap:`
- or `-mapView:didFinishLoadingStyle:` as indicators that it's safe
- to modify the map's style.
+ style via any of the `MGLStyle` instance methods below. You can use the
+ `-[MGLMapViewDelegate mapView:didFinishLoadingStyle:]` or
+ `-[MGLMapViewDelegate mapViewDidFinishLoadingMap:]` methods as indicators
+ that it's safe to modify the map's style.
*/
MGL_EXPORT
@interface MGLStyle : NSObject
@@ -191,7 +191,7 @@ MGL_EXPORT
/**
A set containing the style’s sources.
*/
-@property (nonatomic, strong) NS_MUTABLE_SET_OF(MGLSource *) *sources;
+@property (nonatomic, strong) NS_SET_OF(__kindof MGLSource *) *sources;
/**
Returns a source with the given identifier in the current style.
@@ -215,8 +215,13 @@ MGL_EXPORT
@note Adding the same source instance more than once will result in a
`MGLRedundantSourceException`. Reusing the same source identifier, even with
different source instances, will result in a
- `MGLRedundantSourceIdentifierException`.
+ `MGLRedundantSourceIdentifierException`.
+ @note Sources should be added in
+ `-[MGLMapViewDelegate mapView:didFinishLoadingStyle:]` or
+ `-[MGLMapViewDelegate mapViewDidFinishLoadingMap:]` to ensure that the map
+ has loaded the style and is ready to accept a new source.
+
@param source The source to add to the current style.
*/
- (void)addSource:(MGLSource *)source;
@@ -239,10 +244,10 @@ MGL_EXPORT
#pragma mark Managing Style Layers
/**
- The layers included in the style, arranged according to their front-to-back
+ The layers included in the style, arranged according to their back-to-front
ordering on the screen.
*/
-@property (nonatomic, strong) NS_MUTABLE_ARRAY_OF(MGLStyleLayer *) *layers;
+@property (nonatomic, strong) NS_ARRAY_OF(__kindof MGLStyleLayer *) *layers;
/**
Returns a style layer with the given identifier in the current style.
@@ -266,7 +271,12 @@ MGL_EXPORT
@note Adding the same layer instance more than once will result in a
`MGLRedundantLayerException`. Reusing the same layer identifer, even with
- different layer instances, will also result in an exception.
+ different layer instances, will also result in an exception.
+
+ @note Layers should be added in
+ `-[MGLMapViewDelegate mapView:didFinishLoadingStyle:]` or
+ `-[MGLMapViewDelegate mapViewDidFinishLoadingMap:]` to ensure that the map
+ has loaded the style and is ready to accept a new layer.
@param layer The layer object to add to the map view. This object must be an
instance of a concrete subclass of `MGLStyleLayer`.
@@ -278,7 +288,12 @@ MGL_EXPORT
@note Adding the same layer instance more than once will result in a
`MGLRedundantLayerException`. Reusing the same layer identifer, even with
- different layer instances, will also result in an exception.
+ different layer instances, will also result in an exception.
+
+ @note Layers should be added in
+ `-[MGLMapViewDelegate mapView:didFinishLoadingStyle:]` or
+ `-[MGLMapViewDelegate mapViewDidFinishLoadingMap:]` to ensure that the map
+ has loaded the style and is ready to accept a new layer.
@param layer The layer to insert.
@param index The index at which to insert the layer. An index of 0 would send
diff --git a/platform/darwin/src/MGLStyle.mm b/platform/darwin/src/MGLStyle.mm
index 9ea9e760f5..7e96c08ccc 100644
--- a/platform/darwin/src/MGLStyle.mm
+++ b/platform/darwin/src/MGLStyle.mm
@@ -24,6 +24,7 @@
#import "MGLAttributionInfo_Private.h"
+#include <mbgl/map/map.hpp>
#include <mbgl/util/default_styles.hpp>
#include <mbgl/sprite/sprite_image.hpp>
#include <mbgl/style/layers/fill_layer.hpp>
@@ -130,9 +131,9 @@ static NSURL *MGLStyleURL_emerald;
#pragma mark Sources
-- (NS_MUTABLE_SET_OF(MGLSource *) *)sources {
+- (NS_SET_OF(__kindof MGLSource *) *)sources {
auto rawSources = self.mapView.mbglMap->getSources();
- NSMutableSet *sources = [NSMutableSet setWithCapacity:rawSources.size()];
+ NS_MUTABLE_SET_OF(__kindof MGLSource *) *sources = [NSMutableSet setWithCapacity:rawSources.size()];
for (auto rawSource = rawSources.begin(); rawSource != rawSources.end(); ++rawSource) {
MGLSource *source = [self sourceFromMBGLSource:*rawSource];
[sources addObject:source];
@@ -140,7 +141,7 @@ static NSURL *MGLStyleURL_emerald;
return sources;
}
-- (void)setSources:(NS_MUTABLE_SET_OF(MGLSource *) *)sources {
+- (void)setSources:(NS_SET_OF(__kindof MGLSource *) *)sources {
for (MGLSource *source in self.sources) {
[self removeSource:source];
}
@@ -225,22 +226,22 @@ static NSURL *MGLStyleURL_emerald;
#pragma mark Style layers
-- (NS_MUTABLE_ARRAY_OF(MGLStyleLayer *) *)layers
+- (NS_ARRAY_OF(__kindof MGLStyleLayer *) *)layers
{
auto layers = self.mapView.mbglMap->getLayers();
- NSMutableArray *styleLayers = [NSMutableArray arrayWithCapacity:layers.size()];
- for (auto layer = layers.rbegin(); layer != layers.rend(); ++layer) {
- MGLStyleLayer *styleLayer = [self layerFromMBGLLayer:*layer];
+ NS_MUTABLE_ARRAY_OF(__kindof MGLStyleLayer *) *styleLayers = [NSMutableArray arrayWithCapacity:layers.size()];
+ for (auto layer : layers) {
+ MGLStyleLayer *styleLayer = [self layerFromMBGLLayer:layer];
[styleLayers addObject:styleLayer];
}
return styleLayers;
}
-- (void)setLayers:(NS_MUTABLE_ARRAY_OF(MGLStyleLayer *) *)layers {
- for (MGLStyleLayer *layer in self.layers.reverseObjectEnumerator) {
+- (void)setLayers:(NS_ARRAY_OF(__kindof MGLStyleLayer *) *)layers {
+ for (MGLStyleLayer *layer in self.layers) {
[self removeLayer:layer];
}
- for (MGLStyleLayer *layer in layers.reverseObjectEnumerator) {
+ for (MGLStyleLayer *layer in layers) {
[self addLayer:layer];
}
}
@@ -253,12 +254,12 @@ static NSURL *MGLStyleURL_emerald;
- (MGLStyleLayer *)objectInLayersAtIndex:(NSUInteger)index
{
auto layers = self.mapView.mbglMap->getLayers();
- if (index > layers.size() - 1) {
+ if (index >= layers.size()) {
[NSException raise:NSRangeException
format:@"No style layer at index %lu.", (unsigned long)index];
return nil;
}
- auto layer = layers.at(layers.size() - 1 - index);
+ auto layer = layers.at(index);
return [self layerFromMBGLLayer:layer];
}
@@ -290,13 +291,14 @@ static NSURL *MGLStyleURL_emerald;
format:@"Cannot insert style layer at out-of-bounds index %lu.", (unsigned long)index];
} else if (index == 0) {
try {
- [styleLayer addToMapView:self.mapView belowLayer:nil];
+ MGLStyleLayer *sibling = layers.size() ? [self layerFromMBGLLayer:layers.at(0)] : nil;
+ [styleLayer addToMapView:self.mapView belowLayer:sibling];
} catch (const std::runtime_error & err) {
[NSException raise:@"MGLRedundantLayerIdentifierException" format:@"%s", err.what()];
}
} else {
try {
- MGLStyleLayer *sibling = [self layerFromMBGLLayer:layers.at(layers.size() - index)];
+ MGLStyleLayer *sibling = [self layerFromMBGLLayer:layers.at(index)];
[styleLayer addToMapView:self.mapView belowLayer:sibling];
} catch (std::runtime_error & err) {
[NSException raise:@"MGLRedundantLayerIdentifierException" format:@"%s", err.what()];
@@ -307,11 +309,11 @@ static NSURL *MGLStyleURL_emerald;
- (void)removeObjectFromLayersAtIndex:(NSUInteger)index
{
auto layers = self.mapView.mbglMap->getLayers();
- if (index > layers.size() - 1) {
+ if (index >= layers.size()) {
[NSException raise:NSRangeException
format:@"Cannot remove style layer at out-of-bounds index %lu.", (unsigned long)index];
}
- auto layer = layers.at(layers.size() - 1 - index);
+ auto layer = layers.at(index);
MGLStyleLayer *styleLayer = [self layerFromMBGLLayer:layer];
[styleLayer removeFromMapView:self.mapView];
}
diff --git a/platform/darwin/src/MGLStyleLayer.h.ejs b/platform/darwin/src/MGLStyleLayer.h.ejs
index 7d0f6922f4..8ffed66b54 100644
--- a/platform/darwin/src/MGLStyleLayer.h.ejs
+++ b/platform/darwin/src/MGLStyleLayer.h.ejs
@@ -1,9 +1,9 @@
<%
+ const doc = locals.doc;
const type = locals.type;
const layoutProperties = locals.layoutProperties;
const paintProperties = locals.paintProperties;
- const layoutPropertiesByName = locals.layoutPropertiesByName;
- const paintPropertiesByName = locals.paintPropertiesByName;
+ const enumProperties = locals.enumProperties;
-%>
// This file is generated.
// Edit platform/darwin/scripts/generate-style-code.js, then run `make style-code-darwin`.
@@ -21,14 +21,15 @@ NS_ASSUME_NONNULL_BEGIN
<% for (const property of layoutProperties) { -%>
<% if (property.type == "enum") { -%>
/**
- <%- propertyDoc(property.name, property, type) %>
+<%- propertyDoc(property.name, property, type, 'enum').wrap(80, 1) %>
- Values of this type are used in the `<%- camelizeWithLeadingLowercase(property.name) %>` property of `MGL<%- camelize(type) %>StyleLayer`.
+ Values of this type are used in the `MGL<%- camelize(type) %>StyleLayer.<%- camelizeWithLeadingLowercase(property.name) %>`
+ property.
*/
typedef NS_ENUM(NSUInteger, MGL<%- camelize(property.name) %>) {
<% for (const value in property.values) { -%>
/**
- <%- propertyDoc(property.name, property.values[value], type) %>
+<%- propertyDoc(property.name, property.values[value], type, 'enum').wrap(80, 4+1) %>
*/
MGL<%- camelize(property.name) %><%- camelize(value) %>,
<% } -%>
@@ -39,14 +40,15 @@ typedef NS_ENUM(NSUInteger, MGL<%- camelize(property.name) %>) {
<% for (const property of paintProperties) { -%>
<% if (property.type == "enum") { -%>
/**
- <%- propertyDoc(property.name, property, type) %>
+<%- propertyDoc(property.name, property, type, 'enum').wrap(80, 1) %>
- Values of this type are used in the `<%- camelizeWithLeadingLowercase(property.name) %>` property of `MGL<%- camelize(type) %>StyleLayer`.
+ Values of this type are used in the `MGL<%- camelize(type) %>StyleLayer.<%- camelizeWithLeadingLowercase(property.name) %>`
+ property.
*/
typedef NS_ENUM(NSUInteger, MGL<%- camelize(property.name) %>) {
<% for (const value in property.values) { -%>
/**
- <%- propertyDoc(property.name, property.values[value], type) %>
+<%- propertyDoc(property.name, property.values[value], type, 'enum').wrap(80, 4+1) %>
*/
MGL<%- camelize(property.name) %><%- camelize(value) %>,
<% } -%>
@@ -56,17 +58,22 @@ typedef NS_ENUM(NSUInteger, MGL<%- camelize(property.name) %>) {
<% } -%>
<% if (type == 'background') { -%>
/**
- A map style's background layer is the bottommost layer and is used to style a color
- or pattern to show below all other map features. You can query an `MGLMapView` for its
- `style` and obtain the background layer using the `-[MGLStyle layerWithIdentifier:]`
- method and passing `background` for the identifier.
+<%- doc.wrap(80, 1) %>
*/
<% } else { -%>
/**
- A <%- type %> layer which allows customization of styling properties at runtime. You may
- instantiate a new <%- type %> layer to add to a map style or you may query an
- `MGLMapView` for its `style` and obtain existing layers using the
- `-[MGLStyle layerWithIdentifier:]` method.
+<%- doc.wrap(80, 1) %>
+
+ You can access an existing <%- type %> style layer using the
+ `-[MGLStyle layerWithIdentifier:]` method if you know its identifier;
+ otherwise, find it using the `MGLStyle.layers` property. You can also create a
+ new <%- type %> style layer and add it to the style using a method such as
+ `-[MGLStyle addLayer:]`.
+
+ ### Example
+
+ ```swift
+ ```
*/
<% } -%>
MGL_EXPORT
@@ -85,18 +92,7 @@ MGL_EXPORT
<% for (const property of layoutProperties) { -%>
/**
- <%- propertyDoc(property.name, property, type) %>
-<% if ('default' in property) { -%>
-
- The default value of this property is <%- propertyDefault(property, type) %>.<% if (!property.required) { %> Set this property to `nil` to reset it to the default value.<% } %>
-<% } if (property.requires) { -%>
-
- <%- propertyReqs(property, layoutPropertiesByName, type) %>
-<% } -%>
-<% if (property.original) { -%>
-
- This attribute corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-<%- type -%>-<%- property.original -%>"><code><%- property.original -%></code></a> layout property in the Mapbox Style Specification.
-<% } -%>
+<%- propertyDoc(property.name, property, type, 'layout').wrap(80, 1) %>
*/
@property (nonatomic<% if (!property.required) { %>, null_resettable<% } if (property.getter) { %>, getter=<%- objCGetter(property) -%><% } %>) MGLStyleValue<<%- propertyType(property, true) %>> *<%- camelizeWithLeadingLowercase(property.name) %>;
@@ -111,22 +107,11 @@ MGL_EXPORT
<% for (const property of paintProperties) { -%>
/**
- <%- propertyDoc(property.name, property, type) %>
-<% if ('default' in property) { -%>
-
- The default value of this property is <%- propertyDefault(property, type) %>.<% if (!property.required) { %> Set this property to `nil` to reset it to the default value.<% } %>
-<% } if (property.requires) { -%>
-
- <%- propertyReqs(property, paintPropertiesByName, type) %>
-<% } -%>
-<% if (property.original) { -%>
-
- This attribute corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-<%- property.original -%>"><code><%- property.original -%></code></a> paint property in the Mapbox Style Specification.
-<% } -%>
+<%- propertyDoc(property.name, property, type, 'paint').wrap(80, 1) %>
*/
@property (nonatomic<% if (!property.required) { %>, null_resettable<% } if (property.getter) { %>, getter=<%- objCGetter(property) -%><% } %>) MGLStyleValue<<%- propertyType(property, true) %>> *<%- camelizeWithLeadingLowercase(property.name) %>;
-<% if (property.original) { %>
+<% if (property.original) { -%>
@property (nonatomic<% if (!property.required) { %>, null_resettable<% } %>) MGLStyleValue<<%- propertyType(property, true) %>> *<%- camelizeWithLeadingLowercase(originalPropertyName(property)) %> __attribute__((unavailable("Use <%- camelizeWithLeadingLowercase(property.name) %> instead.")));
<% } -%>
@@ -134,4 +119,31 @@ MGL_EXPORT
<% } -%>
@end
+<% if (enumProperties) { -%>
+/**
+ Methods for wrapping an enumeration value for a style layer attribute in an
+ `MGL<%- camelize(type) %>StyleLayer` object and unwrapping its raw value.
+ */
+@interface NSValue (MGL<%- camelize(type) %>StyleLayerAdditions)
+
+#pragma mark Working with <%- camelize(type) %> Style Layer Attribute Values
+
+<% for (let property of enumProperties) { -%>
+/**
+ Creates a new value object containing the given `MGL<%- camelize(property.name) %>` enumeration.
+
+ @param <%- objCName(property) %> The value for the new object.
+ @return A new value object that contains the enumeration value.
+ */
++ (instancetype)valueWithMGL<%- camelize(property.name) %>:(MGL<%- camelize(property.name) %>)<%- objCName(property) %>;
+
+/**
+ The `MGL<%- camelize(property.name) %>` enumeration representation of the value.
+ */
+@property (readonly) MGL<%- camelize(property.name) %> MGL<%- camelize(property.name) %>Value;
+
+<% } -%>
+@end
+
+<% } -%>
NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLStyleLayer.mm.ejs b/platform/darwin/src/MGLStyleLayer.mm.ejs
index baeed3b882..9445c3cf21 100644
--- a/platform/darwin/src/MGLStyleLayer.mm.ejs
+++ b/platform/darwin/src/MGLStyleLayer.mm.ejs
@@ -2,10 +2,10 @@
const type = locals.type;
const layoutProperties = locals.layoutProperties;
const paintProperties = locals.paintProperties;
- const containsEnumerationProperties = locals.containsEnumerationProperties;
+ const enumProperties = locals.enumProperties;
-%>
// This file is generated.
-// Edit platform/darwin/scripts/generate-style-code.js, then run `make style-code-darwin`.
+// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
#import "MGLSource.h"
#import "MGLMapView_Private.h"
@@ -14,8 +14,10 @@
#import "MGLStyleValue_Private.h"
#import "MGL<%- camelize(type) %>StyleLayer.h"
+#include <mbgl/map/map.hpp>
#include <mbgl/style/layers/<%- type.replace('-', '_') %>_layer.hpp>
-<% if (containsEnumerationProperties) { -%>
+<% if (enumProperties) { -%>
+
namespace mbgl {
<% if (layoutProperties.length) { -%>
@@ -89,7 +91,15 @@ namespace mbgl {
super.rawLayer = rawLayer;
}
-<% if (type !== 'background' && type !== 'raster') { -%>
+<% if (type !== 'background') { -%>
+- (NSString *)sourceIdentifier
+{
+ MGLAssertStyleLayerIsValid();
+
+ return @(self.rawLayer->getSourceID().c_str());
+}
+
+<% if (type !== 'raster') { -%>
- (NSString *)sourceLayerIdentifier
{
MGLAssertStyleLayerIsValid();
@@ -109,7 +119,7 @@ namespace mbgl {
{
MGLAssertStyleLayerIsValid();
- self.rawLayer->setFilter(predicate.mgl_filter);
+ self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::NullFilter());
}
- (NSPredicate *)predicate
@@ -119,7 +129,7 @@ namespace mbgl {
return [NSPredicate mgl_predicateWithFilter:self.rawLayer->getFilter()];
}
-<% } -%>
+<% }} -%>
#pragma mark - Adding to and removing from a map view
- (void)addToMapView:(MGLMapView *)mapView belowLayer:(MGLStyleLayer *)otherLayer
@@ -167,7 +177,7 @@ namespace mbgl {
MGLAssertStyleLayerIsValid();
<% if (property.type == "enum") { -%>
- auto mbglValue = MGLStyleValueTransformer<mbgl::style::<%- mbglType(property) %>, NSValue *, mbgl::style::<%- mbglType(property) %>, MGL<%- camelize(property.name) %>>().toEnumPropertyValue(<%- objCName(property) %>);
+ auto mbglValue = MGLStyleValueTransformer<<%- mbglType(property) %>, NSValue *, <%- mbglType(property) %>, MGL<%- camelize(property.name) %>>().toEnumPropertyValue(<%- objCName(property) %>);
self.rawLayer->set<%- camelize(originalPropertyName(property)) %>(mbglValue);
<% } else { -%>
auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toPropertyValue(<%- objCName(property) %>);
@@ -180,15 +190,14 @@ namespace mbgl {
auto propertyValue = self.rawLayer->get<%- camelize(originalPropertyName(property)) %>() ?: self.rawLayer->getDefault<%- camelize(originalPropertyName(property)) %>();
<% if (property.type == "enum") { -%>
- return MGLStyleValueTransformer<mbgl::style::<%- mbglType(property) %>, NSValue *, mbgl::style::<%- mbglType(property) %>, MGL<%- camelize(property.name) %>>().toEnumStyleValue(propertyValue);
+ return MGLStyleValueTransformer<<%- mbglType(property) %>, NSValue *, <%- mbglType(property) %>, MGL<%- camelize(property.name) %>>().toEnumStyleValue(propertyValue);
<% } else { -%>
return MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toStyleValue(propertyValue);
<% } -%>
}
-<% if (property.original) { %>
+<% if (property.original) { -%>
- (void)set<%- camelize(originalPropertyName(property)) %>:(MGLStyleValue<<%- propertyType(property, true) %>> *)<%- camelizeWithLeadingLowercase(originalPropertyName(property)) %> {
- self.<%- camelizeWithLeadingLowercase(property.name) %> = <%- camelizeWithLeadingLowercase(originalPropertyName(property)) %>;
}
- (MGLStyleValue<<%- propertyType(property, true) %>> *)<%- camelizeWithLeadingLowercase(originalPropertyName(property)) %> {
@@ -206,7 +215,7 @@ namespace mbgl {
MGLAssertStyleLayerIsValid();
<% if (property.type == "enum") { -%>
- auto mbglValue = MGLStyleValueTransformer<mbgl::style::<%- mbglType(property) %>, NSValue *, mbgl::style::<%- mbglType(property) %>, MGL<%- camelize(property.name) %>>().toEnumPropertyValue(<%- objCName(property) %>);
+ auto mbglValue = MGLStyleValueTransformer<<%- mbglType(property) %>, NSValue *, <%- mbglType(property) %>, MGL<%- camelize(property.name) %>>().toEnumPropertyValue(<%- objCName(property) %>);
self.rawLayer->set<%- camelize(originalPropertyName(property)) %>(mbglValue);
<% } else { -%>
auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toPropertyValue(<%- objCName(property) %>);
@@ -219,15 +228,18 @@ namespace mbgl {
auto propertyValue = self.rawLayer->get<%- camelize(originalPropertyName(property)) %>() ?: self.rawLayer->getDefault<%- camelize(originalPropertyName(property)) %>();
<% if (property.type == "enum") { -%>
- return MGLStyleValueTransformer<mbgl::style::<%- mbglType(property) %>, NSValue *, mbgl::style::<%- mbglType(property) %>, MGL<%- camelize(property.name) %>>().toEnumStyleValue(propertyValue);
+ return MGLStyleValueTransformer<<%- mbglType(property) %>, NSValue *, <%- mbglType(property) %>, MGL<%- camelize(property.name) %>>().toEnumStyleValue(propertyValue);
<% } else { -%>
return MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toStyleValue(propertyValue);
<% } -%>
}
-<% if (property.original) { %>
+<% if (property.original) { -%>
- (void)set<%- camelize(originalPropertyName(property)) %>:(MGLStyleValue<<%- propertyType(property, true) %>> *)<%- camelizeWithLeadingLowercase(originalPropertyName(property)) %> {
- NSAssert(NO, @"Use -set<%- camelize(property.name) %>: instead.");
+}
+
+- (MGLStyleValue<<%- propertyType(property, true) %>> *)<%- camelizeWithLeadingLowercase(originalPropertyName(property)) %> {
+ return self.<%- objCGetter(property) %>;
}
<% } -%>
@@ -235,3 +247,21 @@ namespace mbgl {
<% } -%>
@end
+<% if (enumProperties) { -%>
+
+@implementation NSValue (MGL<%- camelize(type) %>StyleLayerAdditions)
+
+<% for (let property of enumProperties) { -%>
++ (NSValue *)valueWithMGL<%- camelize(property.name) %>:(MGL<%- camelize(property.name) %>)<%- objCName(property) %> {
+ return [NSValue value:&<%- objCName(property) %> withObjCType:@encode(MGL<%- camelize(property.name) %>)];
+}
+
+- (MGL<%- camelize(property.name) %>)MGL<%- camelize(property.name) %>Value {
+ MGL<%- camelize(property.name) %> <%- objCName(property) %>;
+ [self getValue:&<%- objCName(property) %>];
+ return <%- objCName(property) %>;
+}
+
+<% } -%>
+@end
+<% } -%>
diff --git a/platform/darwin/src/MGLStyleValue.h b/platform/darwin/src/MGLStyleValue.h
index 315d48b761..5b6528f6fd 100644
--- a/platform/darwin/src/MGLStyleValue.h
+++ b/platform/darwin/src/MGLStyleValue.h
@@ -18,13 +18,13 @@ NS_ASSUME_NONNULL_BEGIN
The `MGLStyleValue` class takes a generic parameter `T` that indicates the
Foundation class being wrapped by this class. Common values for `T` include:
-
+
<ul>
- <li>`NSNumber` (for Boolean values, floating-point numbers, and enumerations)</li>
- <li>`NSValue` (for `CGVector`, `NSEdgeInset`, and `UIEdgeInset`s)</li>
- <li>`NSString`</li>
- <li>`NSColor` or `UIColor`</li>
- <li>`NSArray`</li>
+ <li><code>NSNumber</code> (for Boolean values and floating-point numbers)</li>
+ <li><code>NSValue</code> (for <code>CGVector</code>, <code>NSEdgeInsets</code>, <code>UIEdgeInsets</code>, and enumerations)</li>
+ <li><code>NSString</code></li>
+ <li><code>NSColor</code> or <code>UIColor</code></li>
+ <li><code>NSArray</code></li>
</ul>
*/
MGL_EXPORT
@@ -52,13 +52,13 @@ MGL_EXPORT
/**
Creates and returns an `MGLStyleFunction` object representing a zoom level
- function with an exponential base and any number of stops.
+ function with an exponential interpolation base and any number of stops.
- @param base The exponential base of the interpolation curve.
+ @param interpolationBase The exponential base of the interpolation curve.
@param stops A dictionary associating zoom levels with style values.
- @return An `MGLStyleFunction` object with the given base and stops.
+ @return An `MGLStyleFunction` object with the given interpolation base and stops.
*/
-+ (instancetype)valueWithBase:(CGFloat)base stops:(NSDictionary<NSNumber *, MGLStyleValue<T> *> *)stops;
++ (instancetype)valueWithInterpolationBase:(CGFloat)interpolationBase stops:(NSDictionary<NSNumber *, MGLStyleValue<T> *> *)stops;
@end
@@ -131,38 +131,38 @@ MGL_EXPORT
/**
Creates and returns an `MGLStyleFunction` object representing a zoom level
- function with an exponential base and any number of stops.
+ function with an exponential interpolation base and any number of stops.
- @param base The exponential base of the interpolation curve.
+ @param interpolationBase The exponential base of the interpolation curve.
@param stops A dictionary associating zoom levels with style values.
- @return An `MGLStyleFunction` object with the given base and stops.
+ @return An `MGLStyleFunction` object with the given interpolation base and stops.
*/
-+ (instancetype)functionWithBase:(CGFloat)base stops:(NSDictionary<NSNumber *, MGLStyleValue<T> *> *)stops;
++ (instancetype)functionWithInterpolationBase:(CGFloat)interpolationBase stops:(NSDictionary<NSNumber *, MGLStyleValue<T> *> *)stops;
#pragma mark Initializing a Style Function
/**
Returns an `MGLStyleFunction` object representing a zoom level function with an
- exponential base and any number of stops.
+ exponential interpolation base and any number of stops.
- @param base The exponential base of the interpolation curve.
+ @param interpolationBase The exponential base of the interpolation curve.
@param stops A dictionary associating zoom levels with style values.
- @return An `MGLStyleFunction` object with the given base and stops.
+ @return An `MGLStyleFunction` object with the given interpolation base and stops.
*/
-- (instancetype)initWithBase:(CGFloat)base stops:(NSDictionary<NSNumber *, MGLStyleValue<T> *> *)stops NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithInterpolationBase:(CGFloat)interpolationBase stops:(NSDictionary<NSNumber *, MGLStyleValue<T> *> *)stops NS_DESIGNATED_INITIALIZER;
#pragma mark Accessing the Parameters of a Function
/**
- The exponential base of the function’s interpolation curve.
+ The exponential interpolation base of the function’s interpolation curve.
- The exponential base controls the rate at which the function’s output values
+ The exponential interpolation base controls the rate at which the function’s output values
increase. A value of 1 causes the function to increase linearly by zoom level.
- A higher exponential base causes the function’s output values to vary
+ A higher exponential interpolation base causes the function’s output values to vary
exponentially, increasing more rapidly towards the high end of the function’s
range. The default value of this property is 1, for a linear curve.
*/
-@property (nonatomic) CGFloat base;
+@property (nonatomic) CGFloat interpolationBase;
/**
A dictionary associating zoom levels with style values.
diff --git a/platform/darwin/src/MGLStyleValue.mm b/platform/darwin/src/MGLStyleValue.mm
index 6ced819cd1..9e77114378 100644
--- a/platform/darwin/src/MGLStyleValue.mm
+++ b/platform/darwin/src/MGLStyleValue.mm
@@ -6,8 +6,8 @@
return [MGLStyleConstantValue valueWithRawValue:rawValue];
}
-+ (instancetype)valueWithBase:(CGFloat)base stops:(NSDictionary *)stops {
- return [MGLStyleFunction functionWithBase:base stops:stops];
++ (instancetype)valueWithInterpolationBase:(CGFloat)interpolationBase stops:(NSDictionary *)stops {
+ return [MGLStyleFunction functionWithInterpolationBase:interpolationBase stops:stops];
}
+ (instancetype)valueWithStops:(NSDictionary *)stops {
@@ -49,44 +49,44 @@
@implementation MGLStyleFunction
-+ (instancetype)functionWithBase:(CGFloat)base stops:(NSDictionary *)stops {
- return [[self alloc] initWithBase:base stops:stops];
++ (instancetype)functionWithInterpolationBase:(CGFloat)interpolationBase stops:(NSDictionary *)stops {
+ return [[self alloc] initWithInterpolationBase:interpolationBase stops:stops];
}
+ (instancetype)functionWithStops:(NSDictionary *)stops {
- return [[self alloc] initWithBase:1 stops:stops];
+ return [[self alloc] initWithInterpolationBase:1 stops:stops];
}
- (instancetype)init {
- return [self initWithBase:1 stops:@{}];
+ return [self initWithInterpolationBase:1 stops:@{}];
}
-- (instancetype)initWithBase:(CGFloat)base stops:(NSDictionary *)stops {
+- (instancetype)initWithInterpolationBase:(CGFloat)interpolationBase stops:(NSDictionary *)stops {
if (self = [super init]) {
if (!stops.count)
{
[NSException raise:NSInvalidArgumentException format:@"%@ requires at least one stop.", self];
}
- _base = base;
+ _interpolationBase = interpolationBase;
_stops = stops;
}
return self;
}
- (NSString *)description {
- return [NSString stringWithFormat:@"<%@: %p, base = %f; stops = %@>",
+ return [NSString stringWithFormat:@"<%@: %p, interpolationBase = %f; stops = %@>",
NSStringFromClass([self class]), (void *)self,
- self.base,
+ self.interpolationBase,
self.stops];
}
- (BOOL)isEqual:(MGLStyleFunction *)other {
- return ([other isKindOfClass:[self class]] && other.base == self.base
+ return ([other isKindOfClass:[self class]] && other.interpolationBase == self.interpolationBase
&& [other.stops isEqualToDictionary:self.stops]);
}
- (NSUInteger)hash {
- return self.base + self.stops.hash;
+ return self.interpolationBase + self.stops.hash;
}
@end
diff --git a/platform/darwin/src/MGLStyleValue_Private.h b/platform/darwin/src/MGLStyleValue_Private.h
index 492ce20f1a..2c3de3fb74 100644
--- a/platform/darwin/src/MGLStyleValue_Private.h
+++ b/platform/darwin/src/MGLStyleValue_Private.h
@@ -4,7 +4,6 @@
#import "NSValue+MGLStyleAttributeAdditions.h"
#import "MGLTypes.h"
-#import "MGLLineStyleLayer.h"
#import <mbgl/util/enum.hpp>
#if TARGET_OS_IPHONE
@@ -42,7 +41,7 @@ public:
for (const auto &mbglStop : mbglStops) {
stops[@(mbglStop.first)] = toEnumStyleConstantValue<>(mbglStop.second);
}
- return [MGLStyleFunction<NSValue *> functionWithBase:mbglValue.asFunction().getBase() stops:stops];
+ return [MGLStyleFunction<NSValue *> functionWithInterpolationBase:mbglValue.asFunction().getBase() stops:stops];
} else {
return nil;
}
@@ -62,7 +61,7 @@ public:
NSCAssert(mbglStopValue.isConstant(), @"Stops must be constant");
mbglStops.emplace_back(zoomKey.floatValue, mbglStopValue.asConstant());
}];
- return mbgl::style::Function<MBGLType>({{mbglStops}}, function.base);
+ return mbgl::style::Function<MBGLType>({{mbglStops}}, function.interpolationBase);
} else if (value) {
[NSException raise:@"MGLAbstractClassException" format:
@"The style value %@ cannot be applied to the style. "
@@ -92,7 +91,7 @@ public:
NSCAssert(mbglStopValue.isConstant(), @"Stops must be constant");
mbglStops.emplace_back(zoomKey.floatValue, mbglStopValue.asConstant());
}];
- return mbgl::style::Function<MBGLEnum>({{mbglStops}}, function.base);
+ return mbgl::style::Function<MBGLEnum>({{mbglStops}}, function.interpolationBase);
} else if (value) {
[NSException raise:@"MGLAbstractClassException" format:
@"The style value %@ cannot be applied to the style. "
@@ -118,7 +117,7 @@ private:
auto rawValue = toMGLRawStyleValue(mbglStop.second);
stops[@(mbglStop.first)] = [MGLStyleValue valueWithRawValue:rawValue];
}
- return [MGLStyleFunction<ObjCType> functionWithBase:mbglFunction.getBase() stops:stops];
+ return [MGLStyleFunction<ObjCType> functionWithInterpolationBase:mbglFunction.getBase() stops:stops];
}
template <typename MBGLEnum = MBGLType,
diff --git a/platform/darwin/src/MGLSymbolStyleLayer.h b/platform/darwin/src/MGLSymbolStyleLayer.h
index 57a8f71827..23ddd59d0d 100644
--- a/platform/darwin/src/MGLSymbolStyleLayer.h
+++ b/platform/darwin/src/MGLSymbolStyleLayer.h
@@ -8,21 +8,29 @@
NS_ASSUME_NONNULL_BEGIN
/**
- In combination with `symbolPlacement`, determines the rotation behavior of icons.
+ In combination with `symbolPlacement`, determines the rotation behavior of
+ icons.
- Values of this type are used in the `iconRotationAlignment` property of `MGLSymbolStyleLayer`.
+ Values of this type are used in the `MGLSymbolStyleLayer.iconRotationAlignment`
+ property.
*/
typedef NS_ENUM(NSUInteger, MGLIconRotationAlignment) {
/**
- When `symbolPlacement` is set to `MGLSymbolPlacementPoint`, aligns icons east-west. When `symbolPlacement` is set to `MGLSymbolPlacementLine`, aligns icon x-axes with the line.
+ When `symbolPlacement` is set to `MGLSymbolPlacementPoint`, aligns icons
+ east-west. When `symbolPlacement` is set to `MGLSymbolPlacementLine`,
+ aligns icon x-axes with the line.
*/
MGLIconRotationAlignmentMap,
/**
- Produces icons whose x-axes are aligned with the x-axis of the viewport, regardless of the value of `symbolPlacement`.
+ Produces icons whose x-axes are aligned with the x-axis of the viewport,
+ regardless of the value of `symbolPlacement`.
*/
MGLIconRotationAlignmentViewport,
/**
- When `symbolPlacement` is set to `MGLSymbolPlacementPoint`, this is equivalent to `MGLIconRotationAlignmentViewport`. When `symbolPlacement` is set to `MGLSymbolPlacementLine`, this is equivalent to `MGLIconRotationAlignmentMap`.
+ When `symbolPlacement` is set to `MGLSymbolPlacementPoint`, this is
+ equivalent to `MGLIconRotationAlignmentViewport`. When `symbolPlacement` is
+ set to `MGLSymbolPlacementLine`, this is equivalent to
+ `MGLIconRotationAlignmentMap`.
*/
MGLIconRotationAlignmentAuto,
};
@@ -30,7 +38,8 @@ typedef NS_ENUM(NSUInteger, MGLIconRotationAlignment) {
/**
Scales the icon to fit around the associated text.
- Values of this type are used in the `iconTextFit` property of `MGLSymbolStyleLayer`.
+ Values of this type are used in the `MGLSymbolStyleLayer.iconTextFit`
+ property.
*/
typedef NS_ENUM(NSUInteger, MGLIconTextFit) {
/**
@@ -54,7 +63,8 @@ typedef NS_ENUM(NSUInteger, MGLIconTextFit) {
/**
Label placement relative to its geometry.
- Values of this type are used in the `symbolPlacement` property of `MGLSymbolStyleLayer`.
+ Values of this type are used in the `MGLSymbolStyleLayer.symbolPlacement`
+ property.
*/
typedef NS_ENUM(NSUInteger, MGLSymbolPlacement) {
/**
@@ -62,7 +72,8 @@ typedef NS_ENUM(NSUInteger, MGLSymbolPlacement) {
*/
MGLSymbolPlacementPoint,
/**
- The label is placed along the line of the geometry. Can only be used on `LineString` and `Polygon` geometries.
+ The label is placed along the line of the geometry. Can only be used on
+ `LineString` and `Polygon` geometries.
*/
MGLSymbolPlacementLine,
};
@@ -70,7 +81,8 @@ typedef NS_ENUM(NSUInteger, MGLSymbolPlacement) {
/**
Part of the text placed closest to the anchor.
- Values of this type are used in the `textAnchor` property of `MGLSymbolStyleLayer`.
+ Values of this type are used in the `MGLSymbolStyleLayer.textAnchor`
+ property.
*/
typedef NS_ENUM(NSUInteger, MGLTextAnchor) {
/**
@@ -114,7 +126,8 @@ typedef NS_ENUM(NSUInteger, MGLTextAnchor) {
/**
Text justification options.
- Values of this type are used in the `textJustification` property of `MGLSymbolStyleLayer`.
+ Values of this type are used in the `MGLSymbolStyleLayer.textJustification`
+ property.
*/
typedef NS_ENUM(NSUInteger, MGLTextJustification) {
/**
@@ -134,7 +147,8 @@ typedef NS_ENUM(NSUInteger, MGLTextJustification) {
/**
Orientation of text when map is pitched.
- Values of this type are used in the `textPitchAlignment` property of `MGLSymbolStyleLayer`.
+ Values of this type are used in the `MGLSymbolStyleLayer.textPitchAlignment`
+ property.
*/
typedef NS_ENUM(NSUInteger, MGLTextPitchAlignment) {
/**
@@ -152,21 +166,29 @@ typedef NS_ENUM(NSUInteger, MGLTextPitchAlignment) {
};
/**
- In combination with `symbolPlacement`, determines the rotation behavior of the individual glyphs forming the text.
+ In combination with `symbolPlacement`, determines the rotation behavior of the
+ individual glyphs forming the text.
- Values of this type are used in the `textRotationAlignment` property of `MGLSymbolStyleLayer`.
+ Values of this type are used in the `MGLSymbolStyleLayer.textRotationAlignment`
+ property.
*/
typedef NS_ENUM(NSUInteger, MGLTextRotationAlignment) {
/**
- When `symbolPlacement` is set to `MGLSymbolPlacementPoint`, aligns text east-west. When `symbolPlacement` is set to `MGLSymbolPlacementLine`, aligns text x-axes with the line.
+ When `symbolPlacement` is set to `MGLSymbolPlacementPoint`, aligns text
+ east-west. When `symbolPlacement` is set to `MGLSymbolPlacementLine`,
+ aligns text x-axes with the line.
*/
MGLTextRotationAlignmentMap,
/**
- Produces glyphs whose x-axes are aligned with the x-axis of the viewport, regardless of the value of `symbolPlacement`.
+ Produces glyphs whose x-axes are aligned with the x-axis of the viewport,
+ regardless of the value of `symbolPlacement`.
*/
MGLTextRotationAlignmentViewport,
/**
- When `symbolPlacement` is set to `MGLSymbolPlacementPoint`, this is equivalent to `MGLTextRotationAlignmentViewport`. When `symbolPlacement` is set to `MGLSymbolPlacementLine`, this is equivalent to `MGLTextRotationAlignmentMap`.
+ When `symbolPlacement` is set to `MGLSymbolPlacementPoint`, this is
+ equivalent to `MGLTextRotationAlignmentViewport`. When `symbolPlacement` is
+ set to `MGLSymbolPlacementLine`, this is equivalent to
+ `MGLTextRotationAlignmentMap`.
*/
MGLTextRotationAlignmentAuto,
};
@@ -174,7 +196,8 @@ typedef NS_ENUM(NSUInteger, MGLTextRotationAlignment) {
/**
Specifies how to capitalize text.
- Values of this type are used in the `textTransform` property of `MGLSymbolStyleLayer`.
+ Values of this type are used in the `MGLSymbolStyleLayer.textTransform`
+ property.
*/
typedef NS_ENUM(NSUInteger, MGLTextTransform) {
/**
@@ -194,40 +217,65 @@ typedef NS_ENUM(NSUInteger, MGLTextTransform) {
/**
Controls the translation reference point.
- Values of this type are used in the `iconTranslateAnchor` property of `MGLSymbolStyleLayer`.
+ Values of this type are used in the `MGLSymbolStyleLayer.iconTranslationAnchor`
+ property.
*/
-typedef NS_ENUM(NSUInteger, MGLIconTranslateAnchor) {
+typedef NS_ENUM(NSUInteger, MGLIconTranslationAnchor) {
/**
Icons are translated relative to the map.
*/
- MGLIconTranslateAnchorMap,
+ MGLIconTranslationAnchorMap,
/**
Icons are translated relative to the viewport.
*/
- MGLIconTranslateAnchorViewport,
+ MGLIconTranslationAnchorViewport,
};
/**
Controls the translation reference point.
- Values of this type are used in the `textTranslateAnchor` property of `MGLSymbolStyleLayer`.
+ Values of this type are used in the `MGLSymbolStyleLayer.textTranslationAnchor`
+ property.
*/
-typedef NS_ENUM(NSUInteger, MGLTextTranslateAnchor) {
+typedef NS_ENUM(NSUInteger, MGLTextTranslationAnchor) {
/**
The text is translated relative to the map.
*/
- MGLTextTranslateAnchorMap,
+ MGLTextTranslationAnchorMap,
/**
The text is translated relative to the viewport.
*/
- MGLTextTranslateAnchorViewport,
+ MGLTextTranslationAnchorViewport,
};
/**
- A symbol layer which allows customization of styling properties at runtime. You may
- instantiate a new symbol layer to add to a map style or you may query an
- `MGLMapView` for its `style` and obtain existing layers using the
- `-[MGLStyle layerWithIdentifier:]` method.
+ An `MGLSymbolStyleLayer` is a style layer that renders icon and text labels at
+ points or along lines on the map.
+
+ Use a symbol style layer to configure the visual appearance of labels for
+ features in vector tiles loaded by an `MGLVectorSource` object or `MGLShape` or
+ `MGLFeature` instances in an `MGLShapeSource` object.
+
+ You can access an existing symbol style layer using the
+ `-[MGLStyle layerWithIdentifier:]` method if you know its identifier;
+ otherwise, find it using the `MGLStyle.layers` property. You can also create a
+ new symbol style layer and add it to the style using a method such as
+ `-[MGLStyle addLayer:]`.
+
+ ### Example
+
+ ```swift
+ let layer = MGLSymbolStyleLayer(identifier: "coffeeshops", source: pois)
+ layer.sourceLayerIdentifier = "pois"
+ layer.iconImageName = MGLStyleValue(rawValue: "coffee")
+ layer.iconScale = MGLStyleValue(rawValue: 0.5)
+ layer.text = MGLStyleValue(rawValue: "{name}")
+ layer.textTranslation = MGLStyleValue(rawValue: NSValue(cgVector: CGVector(dx: 10, dy: 0)))
+ layer.textJustification = MGLStyleValue(rawValue: NSValue(mglTextJustification: .left))
+ layer.textAnchor = MGLStyleValue(rawValue: NSValue(mglTextAnchor: .left))
+ layer.predicate = NSPredicate(format: "%K == %@", "venue-type", "coffee")
+ mapView.style?.addLayer(layer)
+ ```
*/
MGL_EXPORT
@interface MGLSymbolStyleLayer : MGLVectorStyleLayer
@@ -235,13 +283,19 @@ MGL_EXPORT
#pragma mark - Accessing the Layout Attributes
/**
- If true, the icon will be visible even if it collides with other previously drawn symbols.
+ If true, the icon will be visible even if it collides with other previously
+ drawn symbols.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing `NO`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing `NO`. Set this property to `nil` to reset it to
+ the default value.
- This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored.
+ This property is only applied to the style if `iconImageName` is non-`nil`.
+ Otherwise, it is ignored.
- This attribute corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-icon-allow-overlap"><code>icon-allow-overlap</code></a> layout property in the Mapbox Style Specification.
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-icon-allow-overlap"><code>icon-allow-overlap</code></a>
+ layout property in the Mapbox Style Specification.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconAllowsOverlap;
@@ -251,11 +305,16 @@ MGL_EXPORT
/**
If true, other symbols can be visible even if they collide with the icon.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing `NO`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing `NO`. Set this property to `nil` to reset it to
+ the default value.
- This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored.
+ This property is only applied to the style if `iconImageName` is non-`nil`.
+ Otherwise, it is ignored.
- This attribute corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-icon-ignore-placement"><code>icon-ignore-placement</code></a> layout property in the Mapbox Style Specification.
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-icon-ignore-placement"><code>icon-ignore-placement</code></a>
+ layout property in the Mapbox Style Specification.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconIgnoresPlacement;
@@ -265,52 +324,84 @@ MGL_EXPORT
/**
A string with {tokens} replaced, referencing the data property to pull from.
- This attribute corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-icon-image"><code>icon-image</code></a> layout property in the Mapbox Style Specification.
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-icon-image"><code>icon-image</code></a>
+ layout property in the Mapbox Style Specification.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSString *> *iconImageName;
@property (nonatomic, null_resettable) MGLStyleValue<NSString *> *iconImage __attribute__((unavailable("Use iconImageName instead.")));
+#if TARGET_OS_IPHONE
+/**
+ Offset distance of icon from its anchor.
+
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSValue` object containing a `CGVector` struct set to 0 rightward and 0
+ downward. Set this property to `nil` to reset it to the default value.
+
+ This property is only applied to the style if `iconImageName` is non-`nil`.
+ Otherwise, it is ignored.
+ */
+@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconOffset;
+#else
/**
Offset distance of icon from its anchor.
- The default value of this property is an `MGLStyleValue` object containing an `NSValue` object containing a `CGVector` struct set to 0 from the left and 0 from the top. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSValue` object containing a `CGVector` struct set to 0 rightward and 0
+ upward. Set this property to `nil` to reset it to the default value.
- This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored.
+ This property is only applied to the style if `iconImageName` is non-`nil`.
+ Otherwise, it is ignored.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconOffset;
+#endif
/**
- If true, text will display without their corresponding icons when the icon collides with other symbols and the text does not.
+ If true, text will display without their corresponding icons when the icon
+ collides with other symbols and the text does not.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing `NO`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing `NO`. Set this property to `nil` to reset it to
+ the default value.
- This property is only applied to the style if `iconImageName` is non-`nil`, and `textField` is non-`nil`. Otherwise, it is ignored.
+ This property is only applied to the style if `iconImageName` is non-`nil`, and
+ `text` is non-`nil`. Otherwise, it is ignored.
*/
@property (nonatomic, null_resettable, getter=isIconOptional) MGLStyleValue<NSNumber *> *iconOptional;
/**
- Size of the additional area around the icon bounding box used for detecting symbol collisions.
-
+ Size of the additional area around the icon bounding box used for detecting
+ symbol collisions.
+
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing the float `2`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `2`. Set this property to `nil` to reset
+ it to the default value.
- This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored.
+ This property is only applied to the style if `iconImageName` is non-`nil`.
+ Otherwise, it is ignored.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconPadding;
/**
Rotates the icon clockwise.
-
+
This property is measured in degrees.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing the float `0`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `0`. Set this property to `nil` to reset
+ it to the default value.
- This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored.
+ This property is only applied to the style if `iconImageName` is non-`nil`.
+ Otherwise, it is ignored.
- This attribute corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-icon-rotate"><code>icon-rotate</code></a> layout property in the Mapbox Style Specification.
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-icon-rotate"><code>icon-rotate</code></a>
+ layout property in the Mapbox Style Specification.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconRotation;
@@ -318,22 +409,31 @@ MGL_EXPORT
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconRotate __attribute__((unavailable("Use iconRotation instead.")));
/**
- In combination with `symbolPlacement`, determines the rotation behavior of icons.
+ In combination with `symbolPlacement`, determines the rotation behavior of
+ icons.
- The default value of this property is an `MGLStyleValue` object containing an `NSValue` object containing `MGLIconRotationAlignmentAuto`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSValue` object containing `MGLIconRotationAlignmentAuto`. Set this property
+ to `nil` to reset it to the default value.
- This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored.
+ This property is only applied to the style if `iconImageName` is non-`nil`.
+ Otherwise, it is ignored.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconRotationAlignment;
/**
Scale factor for icon. 1 is original size, 3 triples the size.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing the float `1`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `1`. Set this property to `nil` to reset
+ it to the default value.
- This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored.
+ This property is only applied to the style if `iconImageName` is non-`nil`.
+ Otherwise, it is ignored.
- This attribute corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-icon-size"><code>icon-size</code></a> layout property in the Mapbox Style Specification.
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-icon-size"><code>icon-size</code></a>
+ layout property in the Mapbox Style Specification.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconScale;
@@ -343,31 +443,65 @@ MGL_EXPORT
/**
Scales the icon to fit around the associated text.
- The default value of this property is an `MGLStyleValue` object containing an `NSValue` object containing `MGLIconTextFitNone`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSValue` object containing `MGLIconTextFitNone`. Set this property to `nil` to
+ reset it to the default value.
- This property is only applied to the style if `iconImageName` is non-`nil`, and `textField` is non-`nil`. Otherwise, it is ignored.
+ This property is only applied to the style if `iconImageName` is non-`nil`, and
+ `text` is non-`nil`. Otherwise, it is ignored.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconTextFit;
+#if TARGET_OS_IPHONE
/**
Size of the additional area added to dimensions determined by `iconTextFit`.
-
+
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an `NSValue` object containing `NSEdgeInsetsZero` or `UIEdgeInsetsZero`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSValue` object containing `UIEdgeInsetsZero`. Set this property to `nil` to
+ reset it to the default value.
- This property is only applied to the style if `iconImageName` is non-`nil`, and `textField` is non-`nil`, and `iconTextFit` is set to an `MGLStyleValue` object containing an `NSValue` object containing `MGLIconTextFitBoth`, `MGLIconTextFitWidth`, or `MGLIconTextFitHeight`. Otherwise, it is ignored.
+ This property is only applied to the style if `iconImageName` is non-`nil`, and
+ `text` is non-`nil`, and `iconTextFit` is set to an `MGLStyleValue` object
+ containing an `NSValue` object containing `MGLIconTextFitBoth`,
+ `MGLIconTextFitWidth`, or `MGLIconTextFitHeight`. Otherwise, it is ignored.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconTextFitPadding;
+#else
+/**
+ Size of the additional area added to dimensions determined by `iconTextFit`.
+
+ This property is measured in points.
+
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSValue` object containing `NSEdgeInsetsZero`. Set this property to `nil` to
+ reset it to the default value.
+
+ This property is only applied to the style if `iconImageName` is non-`nil`, and
+ `text` is non-`nil`, and `iconTextFit` is set to an `MGLStyleValue` object
+ containing an `NSValue` object containing `MGLIconTextFitBoth`,
+ `MGLIconTextFitWidth`, or `MGLIconTextFitHeight`. Otherwise, it is ignored.
+ */
+@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconTextFitPadding;
+#endif
/**
If true, the icon may be flipped to prevent it from being rendered upside-down.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing `NO`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing `NO`. Set this property to `nil` to reset it to
+ the default value.
- This property is only applied to the style if `iconImageName` is non-`nil`, and `iconRotationAlignment` is set to an `MGLStyleValue` object containing an `NSValue` object containing `MGLIconRotationAlignmentMap`, and `symbolPlacement` is set to an `MGLStyleValue` object containing an `NSValue` object containing `MGLSymbolPlacementLine`. Otherwise, it is ignored.
+ This property is only applied to the style if `iconImageName` is non-`nil`, and
+ `iconRotationAlignment` is set to an `MGLStyleValue` object containing an
+ `NSValue` object containing `MGLIconRotationAlignmentMap`, and
+ `symbolPlacement` is set to an `MGLStyleValue` object containing an `NSValue`
+ object containing `MGLSymbolPlacementLine`. Otherwise, it is ignored.
- This attribute corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-icon-keep-upright"><code>icon-keep-upright</code></a> layout property in the Mapbox Style Specification.
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-icon-keep-upright"><code>icon-keep-upright</code></a>
+ layout property in the Mapbox Style Specification.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *keepsIconUpright;
@@ -375,13 +509,22 @@ MGL_EXPORT
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconKeepUpright __attribute__((unavailable("Use keepsIconUpright instead.")));
/**
- If true, the text may be flipped vertically to prevent it from being rendered upside-down.
-
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing `YES`. Set this property to `nil` to reset it to the default value.
-
- This property is only applied to the style if `textField` is non-`nil`, and `textRotationAlignment` is set to an `MGLStyleValue` object containing an `NSValue` object containing `MGLTextRotationAlignmentMap`, and `symbolPlacement` is set to an `MGLStyleValue` object containing an `NSValue` object containing `MGLSymbolPlacementLine`. Otherwise, it is ignored.
-
- This attribute corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-text-keep-upright"><code>text-keep-upright</code></a> layout property in the Mapbox Style Specification.
+ If true, the text may be flipped vertically to prevent it from being rendered
+ upside-down.
+
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing `YES`. Set this property to `nil` to reset it to
+ the default value.
+
+ This property is only applied to the style if `text` is non-`nil`, and
+ `textRotationAlignment` is set to an `MGLStyleValue` object containing an
+ `NSValue` object containing `MGLTextRotationAlignmentMap`, and
+ `symbolPlacement` is set to an `MGLStyleValue` object containing an `NSValue`
+ object containing `MGLSymbolPlacementLine`. Otherwise, it is ignored.
+
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-text-keep-upright"><code>text-keep-upright</code></a>
+ layout property in the Mapbox Style Specification.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *keepsTextUpright;
@@ -390,14 +533,20 @@ MGL_EXPORT
/**
Maximum angle change between adjacent characters.
-
+
This property is measured in degrees.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing the float `45`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `45`. Set this property to `nil` to
+ reset it to the default value.
- This property is only applied to the style if `textField` is non-`nil`, and `symbolPlacement` is set to an `MGLStyleValue` object containing an `NSValue` object containing `MGLSymbolPlacementLine`. Otherwise, it is ignored.
+ This property is only applied to the style if `text` is non-`nil`, and
+ `symbolPlacement` is set to an `MGLStyleValue` object containing an `NSValue`
+ object containing `MGLSymbolPlacementLine`. Otherwise, it is ignored.
- This attribute corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-text-max-angle"><code>text-max-angle</code></a> layout property in the Mapbox Style Specification.
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-text-max-angle"><code>text-max-angle</code></a>
+ layout property in the Mapbox Style Specification.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *maximumTextAngle;
@@ -406,14 +555,19 @@ MGL_EXPORT
/**
The maximum line width for text wrapping.
-
+
This property is measured in ems.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing the float `10`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `10`. Set this property to `nil` to
+ reset it to the default value.
- This property is only applied to the style if `textField` is non-`nil`. Otherwise, it is ignored.
+ This property is only applied to the style if `text` is non-`nil`. Otherwise,
+ it is ignored.
- This attribute corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-text-max-width"><code>text-max-width</code></a> layout property in the Mapbox Style Specification.
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-text-max-width"><code>text-max-width</code></a>
+ layout property in the Mapbox Style Specification.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *maximumTextWidth;
@@ -421,11 +575,18 @@ MGL_EXPORT
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textMaxWidth __attribute__((unavailable("Use maximumTextWidth instead.")));
/**
- If true, the symbols will not cross tile edges to avoid mutual collisions. Recommended in layers that don't have enough padding in the vector tile to prevent collisions, or if it is a point symbol layer placed after a line symbol layer.
-
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing `NO`. Set this property to `nil` to reset it to the default value.
-
- This attribute corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-symbol-avoid-edges"><code>symbol-avoid-edges</code></a> layout property in the Mapbox Style Specification.
+ If true, the symbols will not cross tile edges to avoid mutual collisions.
+ Recommended in layers that don't have enough padding in the vector tile to
+ prevent collisions, or if it is a point symbol layer placed after a line symbol
+ layer.
+
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing `NO`. Set this property to `nil` to reset it to
+ the default value.
+
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-symbol-avoid-edges"><code>symbol-avoid-edges</code></a>
+ layout property in the Mapbox Style Specification.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *symbolAvoidsEdges;
@@ -435,29 +596,57 @@ MGL_EXPORT
/**
Label placement relative to its geometry.
- The default value of this property is an `MGLStyleValue` object containing an `NSValue` object containing `MGLSymbolPlacementPoint`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSValue` object containing `MGLSymbolPlacementPoint`. Set this property to
+ `nil` to reset it to the default value.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *symbolPlacement;
/**
Distance between two symbol anchors.
-
+
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing the float `250`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `250`. Set this property to `nil` to
+ reset it to the default value.
- This property is only applied to the style if `symbolPlacement` is set to an `MGLStyleValue` object containing an `NSValue` object containing `MGLSymbolPlacementLine`. Otherwise, it is ignored.
+ This property is only applied to the style if `symbolPlacement` is set to an
+ `MGLStyleValue` object containing an `NSValue` object containing
+ `MGLSymbolPlacementLine`. Otherwise, it is ignored.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *symbolSpacing;
/**
- If true, the text will be visible even if it collides with other previously drawn symbols.
+ Value to use for a text label. Feature properties are specified using tokens
+ like {field_name}.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing `NO`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing the
+ empty string. Set this property to `nil` to reset it to the default value.
- This property is only applied to the style if `textField` is non-`nil`. Otherwise, it is ignored.
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-text-field"><code>text-field</code></a>
+ layout property in the Mapbox Style Specification.
+ */
+@property (nonatomic, null_resettable) MGLStyleValue<NSString *> *text;
+
+
+@property (nonatomic, null_resettable) MGLStyleValue<NSString *> *textField __attribute__((unavailable("Use text instead.")));
+
+/**
+ If true, the text will be visible even if it collides with other previously
+ drawn symbols.
- This attribute corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-text-allow-overlap"><code>text-allow-overlap</code></a> layout property in the Mapbox Style Specification.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing `NO`. Set this property to `nil` to reset it to
+ the default value.
+
+ This property is only applied to the style if `text` is non-`nil`. Otherwise,
+ it is ignored.
+
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-text-allow-overlap"><code>text-allow-overlap</code></a>
+ layout property in the Mapbox Style Specification.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textAllowsOverlap;
@@ -467,36 +656,78 @@ MGL_EXPORT
/**
Part of the text placed closest to the anchor.
- The default value of this property is an `MGLStyleValue` object containing an `NSValue` object containing `MGLTextAnchorCenter`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSValue` object containing `MGLTextAnchorCenter`. Set this property to `nil`
+ to reset it to the default value.
- This property is only applied to the style if `textField` is non-`nil`. Otherwise, it is ignored.
+ This property is only applied to the style if `text` is non-`nil`. Otherwise,
+ it is ignored.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textAnchor;
/**
- Value to use for a text label. Feature properties are specified using tokens like {field_name}.
+ An array of font face names used to display the text.
+
+ Each font name must be included in the `{fontstack}` portion of the JSON
+ stylesheet’s <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#glyphs"><code>glyphs</code></a>
+ property. You can register a custom font when designing the style in Mapbox
+ Studio. Fonts installed on the system are not used.
- The default value of this property is an `MGLStyleValue` object containing the empty string. Set this property to `nil` to reset it to the default value.
+ The first font named in the array is applied to the text. For each character in
+ the text, if the first font lacks a glyph for the character, the next font is
+ applied as a fallback, and so on.
+
+ The default value of this property is an `MGLStyleValue` object containing the
+ array `Open Sans Regular`, `Arial Unicode MS Regular`. Set this property to
+ `nil` to reset it to the default value.
+
+ This property is only applied to the style if `text` is non-`nil`. Otherwise,
+ it is ignored.
+
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-text-font"><code>text-font</code></a>
+ layout property in the Mapbox Style Specification.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSString *> *textField;
+@property (nonatomic, null_resettable) MGLStyleValue<NSArray<NSString *> *> *textFontNames;
+
+
+@property (nonatomic, null_resettable) MGLStyleValue<NSArray<NSString *> *> *textFont __attribute__((unavailable("Use textFontNames instead.")));
/**
- Font stack to use for displaying text.
+ Font size.
+
+ This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing the array `Open Sans Regular`, `Arial Unicode MS Regular`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `16`. Set this property to `nil` to
+ reset it to the default value.
- This property is only applied to the style if `textField` is non-`nil`. Otherwise, it is ignored.
+ This property is only applied to the style if `text` is non-`nil`. Otherwise,
+ it is ignored.
+
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-text-size"><code>text-size</code></a>
+ layout property in the Mapbox Style Specification.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSArray<NSString *> *> *textFont;
+@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textFontSize;
+
+
+@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textSize __attribute__((unavailable("Use textFontSize instead.")));
/**
If true, other symbols can be visible even if they collide with the text.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing `NO`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing `NO`. Set this property to `nil` to reset it to
+ the default value.
- This property is only applied to the style if `textField` is non-`nil`. Otherwise, it is ignored.
+ This property is only applied to the style if `text` is non-`nil`. Otherwise,
+ it is ignored.
- This attribute corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-text-ignore-placement"><code>text-ignore-placement</code></a> layout property in the Mapbox Style Specification.
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-text-ignore-placement"><code>text-ignore-placement</code></a>
+ layout property in the Mapbox Style Specification.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textIgnoresPlacement;
@@ -506,11 +737,16 @@ MGL_EXPORT
/**
Text justification options.
- The default value of this property is an `MGLStyleValue` object containing an `NSValue` object containing `MGLTextJustificationCenter`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSValue` object containing `MGLTextJustificationCenter`. Set this property to
+ `nil` to reset it to the default value.
- This property is only applied to the style if `textField` is non-`nil`. Otherwise, it is ignored.
+ This property is only applied to the style if `text` is non-`nil`. Otherwise,
+ it is ignored.
- This attribute corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-text-justify"><code>text-justify</code></a> layout property in the Mapbox Style Specification.
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-text-justify"><code>text-justify</code></a>
+ layout property in the Mapbox Style Specification.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textJustification;
@@ -519,76 +755,117 @@ MGL_EXPORT
/**
Text tracking amount.
-
+
This property is measured in ems.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing the float `0`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `0`. Set this property to `nil` to reset
+ it to the default value.
- This property is only applied to the style if `textField` is non-`nil`. Otherwise, it is ignored.
+ This property is only applied to the style if `text` is non-`nil`. Otherwise,
+ it is ignored.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textLetterSpacing;
/**
Text leading value for multi-line text.
-
+
This property is measured in ems.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing the float `1.2`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `1.2`. Set this property to `nil` to
+ reset it to the default value.
- This property is only applied to the style if `textField` is non-`nil`. Otherwise, it is ignored.
+ This property is only applied to the style if `text` is non-`nil`. Otherwise,
+ it is ignored.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textLineHeight;
+#if TARGET_OS_IPHONE
/**
Offset distance of text from its anchor.
-
+
This property is measured in ems.
- The default value of this property is an `MGLStyleValue` object containing an `NSValue` object containing a `CGVector` struct set to 0 ems from the left and 0 ems from the top. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSValue` object containing a `CGVector` struct set to 0 ems rightward and 0
+ ems downward. Set this property to `nil` to reset it to the default value.
- This property is only applied to the style if `textField` is non-`nil`. Otherwise, it is ignored.
+ This property is only applied to the style if `text` is non-`nil`. Otherwise,
+ it is ignored.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textOffset;
+#else
+/**
+ Offset distance of text from its anchor.
+
+ This property is measured in ems.
+
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSValue` object containing a `CGVector` struct set to 0 ems rightward and 0
+ ems upward. Set this property to `nil` to reset it to the default value.
+
+ This property is only applied to the style if `text` is non-`nil`. Otherwise,
+ it is ignored.
+ */
+@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textOffset;
+#endif
/**
- If true, icons will display without their corresponding text when the text collides with other symbols and the icon does not.
+ If true, icons will display without their corresponding text when the text
+ collides with other symbols and the icon does not.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing `NO`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing `NO`. Set this property to `nil` to reset it to
+ the default value.
- This property is only applied to the style if `textField` is non-`nil`, and `iconImageName` is non-`nil`. Otherwise, it is ignored.
+ This property is only applied to the style if `text` is non-`nil`, and
+ `iconImageName` is non-`nil`. Otherwise, it is ignored.
*/
@property (nonatomic, null_resettable, getter=isTextOptional) MGLStyleValue<NSNumber *> *textOptional;
/**
- Size of the additional area around the text bounding box used for detecting symbol collisions.
-
+ Size of the additional area around the text bounding box used for detecting
+ symbol collisions.
+
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing the float `2`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `2`. Set this property to `nil` to reset
+ it to the default value.
- This property is only applied to the style if `textField` is non-`nil`. Otherwise, it is ignored.
+ This property is only applied to the style if `text` is non-`nil`. Otherwise,
+ it is ignored.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textPadding;
/**
Orientation of text when map is pitched.
- The default value of this property is an `MGLStyleValue` object containing an `NSValue` object containing `MGLTextPitchAlignmentAuto`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSValue` object containing `MGLTextPitchAlignmentAuto`. Set this property to
+ `nil` to reset it to the default value.
- This property is only applied to the style if `textField` is non-`nil`. Otherwise, it is ignored.
+ This property is only applied to the style if `text` is non-`nil`. Otherwise,
+ it is ignored.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textPitchAlignment;
/**
Rotates the text clockwise.
-
+
This property is measured in degrees.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing the float `0`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `0`. Set this property to `nil` to reset
+ it to the default value.
- This property is only applied to the style if `textField` is non-`nil`. Otherwise, it is ignored.
+ This property is only applied to the style if `text` is non-`nil`. Otherwise,
+ it is ignored.
- This attribute corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-text-rotate"><code>text-rotate</code></a> layout property in the Mapbox Style Specification.
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-text-rotate"><code>text-rotate</code></a>
+ layout property in the Mapbox Style Specification.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textRotation;
@@ -596,31 +873,27 @@ MGL_EXPORT
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textRotate __attribute__((unavailable("Use textRotation instead.")));
/**
- In combination with `symbolPlacement`, determines the rotation behavior of the individual glyphs forming the text.
+ In combination with `symbolPlacement`, determines the rotation behavior of the
+ individual glyphs forming the text.
- The default value of this property is an `MGLStyleValue` object containing an `NSValue` object containing `MGLTextRotationAlignmentAuto`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSValue` object containing `MGLTextRotationAlignmentAuto`. Set this property
+ to `nil` to reset it to the default value.
- This property is only applied to the style if `textField` is non-`nil`. Otherwise, it is ignored.
+ This property is only applied to the style if `text` is non-`nil`. Otherwise,
+ it is ignored.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textRotationAlignment;
/**
- Font size.
-
- This property is measured in points.
-
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing the float `16`. Set this property to `nil` to reset it to the default value.
-
- This property is only applied to the style if `textField` is non-`nil`. Otherwise, it is ignored.
- */
-@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textSize;
-
-/**
Specifies how to capitalize text.
- The default value of this property is an `MGLStyleValue` object containing an `NSValue` object containing `MGLTextTransformNone`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSValue` object containing `MGLTextTransformNone`. Set this property to `nil`
+ to reset it to the default value.
- This property is only applied to the style if `textField` is non-`nil`. Otherwise, it is ignored.
+ This property is only applied to the style if `text` is non-`nil`. Otherwise,
+ it is ignored.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textTransform;
@@ -628,123 +901,195 @@ MGL_EXPORT
#if TARGET_OS_IPHONE
/**
- The tint color to apply to the icon. The `iconImageName` property must be set to a template image.
+ The tint color to apply to the icon. The `iconImageName` property must be set
+ to a template image.
- The default value of this property is an `MGLStyleValue` object containing `UIColor.blackColor`. Set this property to `nil` to reset it to the default value.
-
- This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored.
+ The default value of this property is an `MGLStyleValue` object containing
+ `UIColor.blackColor`. Set this property to `nil` to reset it to the default
+ value.
+
+ This property is only applied to the style if `iconImageName` is non-`nil`.
+ Otherwise, it is ignored.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<MGLColor *> *iconColor;
+@property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *iconColor;
#else
/**
- The tint color to apply to the icon. The `iconImageName` property must be set to a template image.
+ The tint color to apply to the icon. The `iconImageName` property must be set
+ to a template image.
- The default value of this property is an `MGLStyleValue` object containing `NSColor.blackColor`. Set this property to `nil` to reset it to the default value.
-
- This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored.
+ The default value of this property is an `MGLStyleValue` object containing
+ `NSColor.blackColor`. Set this property to `nil` to reset it to the default
+ value.
+
+ This property is only applied to the style if `iconImageName` is non-`nil`.
+ Otherwise, it is ignored.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<MGLColor *> *iconColor;
+@property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *iconColor;
#endif
/**
Fade out the halo towards the outside.
-
+
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing the float `0`. Set this property to `nil` to reset it to the default value.
-
- This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `0`. Set this property to `nil` to reset
+ it to the default value.
+
+ This property is only applied to the style if `iconImageName` is non-`nil`.
+ Otherwise, it is ignored.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconHaloBlur;
#if TARGET_OS_IPHONE
/**
- The color of the icon’s halo. The `iconImageName` property must be set to a template image.
+ The color of the icon’s halo. The `iconImageName` property must be set to a
+ template image.
- The default value of this property is an `MGLStyleValue` object containing `UIColor.clearColor`. Set this property to `nil` to reset it to the default value.
-
- This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored.
+ The default value of this property is an `MGLStyleValue` object containing
+ `UIColor.clearColor`. Set this property to `nil` to reset it to the default
+ value.
+
+ This property is only applied to the style if `iconImageName` is non-`nil`.
+ Otherwise, it is ignored.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<MGLColor *> *iconHaloColor;
+@property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *iconHaloColor;
#else
/**
- The color of the icon’s halo. The `iconImageName` property must be set to a template image.
+ The color of the icon’s halo. The `iconImageName` property must be set to a
+ template image.
- The default value of this property is an `MGLStyleValue` object containing `NSColor.clearColor`. Set this property to `nil` to reset it to the default value.
-
- This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored.
+ The default value of this property is an `MGLStyleValue` object containing
+ `NSColor.clearColor`. Set this property to `nil` to reset it to the default
+ value.
+
+ This property is only applied to the style if `iconImageName` is non-`nil`.
+ Otherwise, it is ignored.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<MGLColor *> *iconHaloColor;
+@property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *iconHaloColor;
#endif
/**
Distance of halo to the icon outline.
-
+
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing the float `0`. Set this property to `nil` to reset it to the default value.
-
- This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `0`. Set this property to `nil` to reset
+ it to the default value.
+
+ This property is only applied to the style if `iconImageName` is non-`nil`.
+ Otherwise, it is ignored.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconHaloWidth;
/**
The opacity at which the icon will be drawn.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing the float `1`. Set this property to `nil` to reset it to the default value.
-
- This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `1`. Set this property to `nil` to reset
+ it to the default value.
+
+ This property is only applied to the style if `iconImageName` is non-`nil`.
+ Otherwise, it is ignored.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconOpacity;
+#if TARGET_OS_IPHONE
/**
Distance that the icon's anchor is moved from its original placement.
-
+
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an `NSValue` object containing a `CGVector` struct set to 0 points from the left and 0 points from the top. Set this property to `nil` to reset it to the default value.
-
- This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSValue` object containing a `CGVector` struct set to 0 points rightward and 0
+ points downward. Set this property to `nil` to reset it to the default value.
+
+ This property is only applied to the style if `iconImageName` is non-`nil`.
+ Otherwise, it is ignored.
+
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-icon-translate"><code>icon-translate</code></a>
+ layout property in the Mapbox Style Specification.
+ */
+@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconTranslation;
+#else
+/**
+ Distance that the icon's anchor is moved from its original placement.
+
+ This property is measured in points.
+
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSValue` object containing a `CGVector` struct set to 0 points rightward and 0
+ points upward. Set this property to `nil` to reset it to the default value.
+
+ This property is only applied to the style if `iconImageName` is non-`nil`.
+ Otherwise, it is ignored.
+
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-icon-translate"><code>icon-translate</code></a>
+ layout property in the Mapbox Style Specification.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconTranslate;
+@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconTranslation;
+#endif
+
+@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconTranslate __attribute__((unavailable("Use iconTranslation instead.")));
/**
Controls the translation reference point.
- The default value of this property is an `MGLStyleValue` object containing an `NSValue` object containing `MGLIconTranslateAnchorMap`. Set this property to `nil` to reset it to the default value.
-
- This property is only applied to the style if `iconImageName` is non-`nil`, and `iconTranslate` is non-`nil`. Otherwise, it is ignored.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSValue` object containing `MGLIconTranslationAnchorMap`. Set this property to
+ `nil` to reset it to the default value.
+
+ This property is only applied to the style if `iconImageName` is non-`nil`, and
+ `iconTranslation` is non-`nil`. Otherwise, it is ignored.
+
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-icon-translate-anchor"><code>icon-translate-anchor</code></a>
+ layout property in the Mapbox Style Specification.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconTranslateAnchor;
+@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconTranslationAnchor;
+
+@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconTranslateAnchor __attribute__((unavailable("Use iconTranslationAnchor instead.")));
#if TARGET_OS_IPHONE
/**
The color with which the text will be drawn.
- The default value of this property is an `MGLStyleValue` object containing `UIColor.blackColor`. Set this property to `nil` to reset it to the default value.
-
- This property is only applied to the style if `textField` is non-`nil`. Otherwise, it is ignored.
+ The default value of this property is an `MGLStyleValue` object containing
+ `UIColor.blackColor`. Set this property to `nil` to reset it to the default
+ value.
+
+ This property is only applied to the style if `text` is non-`nil`. Otherwise,
+ it is ignored.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<MGLColor *> *textColor;
+@property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *textColor;
#else
/**
The color with which the text will be drawn.
- The default value of this property is an `MGLStyleValue` object containing `NSColor.blackColor`. Set this property to `nil` to reset it to the default value.
-
- This property is only applied to the style if `textField` is non-`nil`. Otherwise, it is ignored.
+ The default value of this property is an `MGLStyleValue` object containing
+ `NSColor.blackColor`. Set this property to `nil` to reset it to the default
+ value.
+
+ This property is only applied to the style if `text` is non-`nil`. Otherwise,
+ it is ignored.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<MGLColor *> *textColor;
+@property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *textColor;
#endif
/**
The halo's fadeout distance towards the outside.
-
+
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing the float `0`. Set this property to `nil` to reset it to the default value.
-
- This property is only applied to the style if `textField` is non-`nil`. Otherwise, it is ignored.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `0`. Set this property to `nil` to reset
+ it to the default value.
+
+ This property is only applied to the style if `text` is non-`nil`. Otherwise,
+ it is ignored.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textHaloBlur;
@@ -752,61 +1097,252 @@ MGL_EXPORT
/**
The color of the text's halo, which helps it stand out from backgrounds.
- The default value of this property is an `MGLStyleValue` object containing `UIColor.clearColor`. Set this property to `nil` to reset it to the default value.
-
- This property is only applied to the style if `textField` is non-`nil`. Otherwise, it is ignored.
+ The default value of this property is an `MGLStyleValue` object containing
+ `UIColor.clearColor`. Set this property to `nil` to reset it to the default
+ value.
+
+ This property is only applied to the style if `text` is non-`nil`. Otherwise,
+ it is ignored.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<MGLColor *> *textHaloColor;
+@property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *textHaloColor;
#else
/**
The color of the text's halo, which helps it stand out from backgrounds.
- The default value of this property is an `MGLStyleValue` object containing `NSColor.clearColor`. Set this property to `nil` to reset it to the default value.
-
- This property is only applied to the style if `textField` is non-`nil`. Otherwise, it is ignored.
+ The default value of this property is an `MGLStyleValue` object containing
+ `NSColor.clearColor`. Set this property to `nil` to reset it to the default
+ value.
+
+ This property is only applied to the style if `text` is non-`nil`. Otherwise,
+ it is ignored.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<MGLColor *> *textHaloColor;
+@property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *textHaloColor;
#endif
/**
- Distance of halo to the font outline. Max text halo width is 1/4 of the font-size.
-
+ Distance of halo to the font outline. Max text halo width is 1/4 of the
+ font-size.
+
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing the float `0`. Set this property to `nil` to reset it to the default value.
-
- This property is only applied to the style if `textField` is non-`nil`. Otherwise, it is ignored.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `0`. Set this property to `nil` to reset
+ it to the default value.
+
+ This property is only applied to the style if `text` is non-`nil`. Otherwise,
+ it is ignored.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textHaloWidth;
/**
The opacity at which the text will be drawn.
- The default value of this property is an `MGLStyleValue` object containing an `NSNumber` object containing the float `1`. Set this property to `nil` to reset it to the default value.
-
- This property is only applied to the style if `textField` is non-`nil`. Otherwise, it is ignored.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `1`. Set this property to `nil` to reset
+ it to the default value.
+
+ This property is only applied to the style if `text` is non-`nil`. Otherwise,
+ it is ignored.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textOpacity;
+#if TARGET_OS_IPHONE
/**
Distance that the text's anchor is moved from its original placement.
-
+
This property is measured in points.
- The default value of this property is an `MGLStyleValue` object containing an `NSValue` object containing a `CGVector` struct set to 0 points from the left and 0 points from the top. Set this property to `nil` to reset it to the default value.
-
- This property is only applied to the style if `textField` is non-`nil`. Otherwise, it is ignored.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSValue` object containing a `CGVector` struct set to 0 points rightward and 0
+ points downward. Set this property to `nil` to reset it to the default value.
+
+ This property is only applied to the style if `text` is non-`nil`. Otherwise,
+ it is ignored.
+
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-text-translate"><code>text-translate</code></a>
+ layout property in the Mapbox Style Specification.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textTranslate;
+@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textTranslation;
+#else
+/**
+ Distance that the text's anchor is moved from its original placement.
+
+ This property is measured in points.
+
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSValue` object containing a `CGVector` struct set to 0 points rightward and 0
+ points upward. Set this property to `nil` to reset it to the default value.
+
+ This property is only applied to the style if `text` is non-`nil`. Otherwise,
+ it is ignored.
+
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-text-translate"><code>text-translate</code></a>
+ layout property in the Mapbox Style Specification.
+ */
+@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textTranslation;
+#endif
+
+@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textTranslate __attribute__((unavailable("Use textTranslation instead.")));
/**
Controls the translation reference point.
- The default value of this property is an `MGLStyleValue` object containing an `NSValue` object containing `MGLTextTranslateAnchorMap`. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSValue` object containing `MGLTextTranslationAnchorMap`. Set this property to
+ `nil` to reset it to the default value.
+
+ This property is only applied to the style if `text` is non-`nil`, and
+ `textTranslation` is non-`nil`. Otherwise, it is ignored.
+
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-text-translate-anchor"><code>text-translate-anchor</code></a>
+ layout property in the Mapbox Style Specification.
+ */
+@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textTranslationAnchor;
+
+@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textTranslateAnchor __attribute__((unavailable("Use textTranslationAnchor instead.")));
+
+@end
+
+/**
+ Methods for wrapping an enumeration value for a style layer attribute in an
+ `MGLSymbolStyleLayer` object and unwrapping its raw value.
+ */
+@interface NSValue (MGLSymbolStyleLayerAdditions)
+
+#pragma mark Working with Symbol Style Layer Attribute Values
+
+/**
+ Creates a new value object containing the given `MGLIconRotationAlignment` enumeration.
+
+ @param iconRotationAlignment The value for the new object.
+ @return A new value object that contains the enumeration value.
+ */
++ (instancetype)valueWithMGLIconRotationAlignment:(MGLIconRotationAlignment)iconRotationAlignment;
+
+/**
+ The `MGLIconRotationAlignment` enumeration representation of the value.
+ */
+@property (readonly) MGLIconRotationAlignment MGLIconRotationAlignmentValue;
+
+/**
+ Creates a new value object containing the given `MGLIconTextFit` enumeration.
- This property is only applied to the style if `textField` is non-`nil`, and `textTranslate` is non-`nil`. Otherwise, it is ignored.
+ @param iconTextFit The value for the new object.
+ @return A new value object that contains the enumeration value.
+ */
++ (instancetype)valueWithMGLIconTextFit:(MGLIconTextFit)iconTextFit;
+
+/**
+ The `MGLIconTextFit` enumeration representation of the value.
+ */
+@property (readonly) MGLIconTextFit MGLIconTextFitValue;
+
+/**
+ Creates a new value object containing the given `MGLSymbolPlacement` enumeration.
+
+ @param symbolPlacement The value for the new object.
+ @return A new value object that contains the enumeration value.
+ */
++ (instancetype)valueWithMGLSymbolPlacement:(MGLSymbolPlacement)symbolPlacement;
+
+/**
+ The `MGLSymbolPlacement` enumeration representation of the value.
+ */
+@property (readonly) MGLSymbolPlacement MGLSymbolPlacementValue;
+
+/**
+ Creates a new value object containing the given `MGLTextAnchor` enumeration.
+
+ @param textAnchor The value for the new object.
+ @return A new value object that contains the enumeration value.
+ */
++ (instancetype)valueWithMGLTextAnchor:(MGLTextAnchor)textAnchor;
+
+/**
+ The `MGLTextAnchor` enumeration representation of the value.
+ */
+@property (readonly) MGLTextAnchor MGLTextAnchorValue;
+
+/**
+ Creates a new value object containing the given `MGLTextJustification` enumeration.
+
+ @param textJustification The value for the new object.
+ @return A new value object that contains the enumeration value.
+ */
++ (instancetype)valueWithMGLTextJustification:(MGLTextJustification)textJustification;
+
+/**
+ The `MGLTextJustification` enumeration representation of the value.
+ */
+@property (readonly) MGLTextJustification MGLTextJustificationValue;
+
+/**
+ Creates a new value object containing the given `MGLTextPitchAlignment` enumeration.
+
+ @param textPitchAlignment The value for the new object.
+ @return A new value object that contains the enumeration value.
+ */
++ (instancetype)valueWithMGLTextPitchAlignment:(MGLTextPitchAlignment)textPitchAlignment;
+
+/**
+ The `MGLTextPitchAlignment` enumeration representation of the value.
+ */
+@property (readonly) MGLTextPitchAlignment MGLTextPitchAlignmentValue;
+
+/**
+ Creates a new value object containing the given `MGLTextRotationAlignment` enumeration.
+
+ @param textRotationAlignment The value for the new object.
+ @return A new value object that contains the enumeration value.
+ */
++ (instancetype)valueWithMGLTextRotationAlignment:(MGLTextRotationAlignment)textRotationAlignment;
+
+/**
+ The `MGLTextRotationAlignment` enumeration representation of the value.
+ */
+@property (readonly) MGLTextRotationAlignment MGLTextRotationAlignmentValue;
+
+/**
+ Creates a new value object containing the given `MGLTextTransform` enumeration.
+
+ @param textTransform The value for the new object.
+ @return A new value object that contains the enumeration value.
+ */
++ (instancetype)valueWithMGLTextTransform:(MGLTextTransform)textTransform;
+
+/**
+ The `MGLTextTransform` enumeration representation of the value.
+ */
+@property (readonly) MGLTextTransform MGLTextTransformValue;
+
+/**
+ Creates a new value object containing the given `MGLIconTranslationAnchor` enumeration.
+
+ @param iconTranslationAnchor The value for the new object.
+ @return A new value object that contains the enumeration value.
+ */
++ (instancetype)valueWithMGLIconTranslationAnchor:(MGLIconTranslationAnchor)iconTranslationAnchor;
+
+/**
+ The `MGLIconTranslationAnchor` enumeration representation of the value.
+ */
+@property (readonly) MGLIconTranslationAnchor MGLIconTranslationAnchorValue;
+
+/**
+ Creates a new value object containing the given `MGLTextTranslationAnchor` enumeration.
+
+ @param textTranslationAnchor The value for the new object.
+ @return A new value object that contains the enumeration value.
+ */
++ (instancetype)valueWithMGLTextTranslationAnchor:(MGLTextTranslationAnchor)textTranslationAnchor;
+
+/**
+ The `MGLTextTranslationAnchor` enumeration representation of the value.
*/
-@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textTranslateAnchor;
+@property (readonly) MGLTextTranslationAnchor MGLTextTranslationAnchorValue;
@end
diff --git a/platform/darwin/src/MGLSymbolStyleLayer.mm b/platform/darwin/src/MGLSymbolStyleLayer.mm
index 31c584b473..493a8c5b6f 100644
--- a/platform/darwin/src/MGLSymbolStyleLayer.mm
+++ b/platform/darwin/src/MGLSymbolStyleLayer.mm
@@ -1,5 +1,5 @@
// This file is generated.
-// Edit platform/darwin/scripts/generate-style-code.js, then run `make style-code-darwin`.
+// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
#import "MGLSource.h"
#import "MGLMapView_Private.h"
@@ -8,7 +8,9 @@
#import "MGLStyleValue_Private.h"
#import "MGLSymbolStyleLayer.h"
+#include <mbgl/map/map.hpp>
#include <mbgl/style/layers/symbol_layer.hpp>
+
namespace mbgl {
MBGL_DEFINE_ENUM(MGLIconRotationAlignment, {
@@ -65,14 +67,14 @@ namespace mbgl {
{ MGLTextTransformLowercase, "lowercase" },
});
- MBGL_DEFINE_ENUM(MGLIconTranslateAnchor, {
- { MGLIconTranslateAnchorMap, "map" },
- { MGLIconTranslateAnchorViewport, "viewport" },
+ MBGL_DEFINE_ENUM(MGLIconTranslationAnchor, {
+ { MGLIconTranslationAnchorMap, "map" },
+ { MGLIconTranslationAnchorViewport, "viewport" },
});
- MBGL_DEFINE_ENUM(MGLTextTranslateAnchor, {
- { MGLTextTranslateAnchorMap, "map" },
- { MGLTextTranslateAnchorViewport, "viewport" },
+ MBGL_DEFINE_ENUM(MGLTextTranslationAnchor, {
+ { MGLTextTranslationAnchorMap, "map" },
+ { MGLTextTranslationAnchorViewport, "viewport" },
});
}
@@ -108,6 +110,13 @@ namespace mbgl {
super.rawLayer = rawLayer;
}
+- (NSString *)sourceIdentifier
+{
+ MGLAssertStyleLayerIsValid();
+
+ return @(self.rawLayer->getSourceID().c_str());
+}
+
- (NSString *)sourceLayerIdentifier
{
MGLAssertStyleLayerIsValid();
@@ -127,7 +136,7 @@ namespace mbgl {
{
MGLAssertStyleLayerIsValid();
- self.rawLayer->setFilter(predicate.mgl_filter);
+ self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::NullFilter());
}
- (NSPredicate *)predicate
@@ -192,9 +201,7 @@ namespace mbgl {
return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(propertyValue);
}
-
- (void)setIconAllowOverlap:(MGLStyleValue<NSNumber *> *)iconAllowOverlap {
- self.iconAllowsOverlap = iconAllowOverlap;
}
- (MGLStyleValue<NSNumber *> *)iconAllowOverlap {
@@ -215,9 +222,7 @@ namespace mbgl {
return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(propertyValue);
}
-
- (void)setIconIgnorePlacement:(MGLStyleValue<NSNumber *> *)iconIgnorePlacement {
- self.iconIgnoresPlacement = iconIgnorePlacement;
}
- (MGLStyleValue<NSNumber *> *)iconIgnorePlacement {
@@ -238,9 +243,7 @@ namespace mbgl {
return MGLStyleValueTransformer<std::string, NSString *>().toStyleValue(propertyValue);
}
-
- (void)setIconImage:(MGLStyleValue<NSString *> *)iconImage {
- self.iconImageName = iconImage;
}
- (MGLStyleValue<NSString *> *)iconImage {
@@ -303,9 +306,7 @@ namespace mbgl {
return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
}
-
- (void)setIconRotate:(MGLStyleValue<NSNumber *> *)iconRotate {
- self.iconRotation = iconRotate;
}
- (MGLStyleValue<NSNumber *> *)iconRotate {
@@ -340,9 +341,7 @@ namespace mbgl {
return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
}
-
- (void)setIconSize:(MGLStyleValue<NSNumber *> *)iconSize {
- self.iconScale = iconSize;
}
- (MGLStyleValue<NSNumber *> *)iconSize {
@@ -391,9 +390,7 @@ namespace mbgl {
return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(propertyValue);
}
-
- (void)setIconKeepUpright:(MGLStyleValue<NSNumber *> *)iconKeepUpright {
- self.keepsIconUpright = iconKeepUpright;
}
- (MGLStyleValue<NSNumber *> *)iconKeepUpright {
@@ -414,9 +411,7 @@ namespace mbgl {
return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(propertyValue);
}
-
- (void)setTextKeepUpright:(MGLStyleValue<NSNumber *> *)textKeepUpright {
- self.keepsTextUpright = textKeepUpright;
}
- (MGLStyleValue<NSNumber *> *)textKeepUpright {
@@ -437,9 +432,7 @@ namespace mbgl {
return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
}
-
- (void)setTextMaxAngle:(MGLStyleValue<NSNumber *> *)textMaxAngle {
- self.maximumTextAngle = textMaxAngle;
}
- (MGLStyleValue<NSNumber *> *)textMaxAngle {
@@ -460,9 +453,7 @@ namespace mbgl {
return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
}
-
- (void)setTextMaxWidth:(MGLStyleValue<NSNumber *> *)textMaxWidth {
- self.maximumTextWidth = textMaxWidth;
}
- (MGLStyleValue<NSNumber *> *)textMaxWidth {
@@ -483,9 +474,7 @@ namespace mbgl {
return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(propertyValue);
}
-
- (void)setSymbolAvoidEdges:(MGLStyleValue<NSNumber *> *)symbolAvoidEdges {
- self.symbolAvoidsEdges = symbolAvoidEdges;
}
- (MGLStyleValue<NSNumber *> *)symbolAvoidEdges {
@@ -520,6 +509,27 @@ namespace mbgl {
return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
}
+- (void)setText:(MGLStyleValue<NSString *> *)text {
+ MGLAssertStyleLayerIsValid();
+
+ auto mbglValue = MGLStyleValueTransformer<std::string, NSString *>().toPropertyValue(text);
+ self.rawLayer->setTextField(mbglValue);
+}
+
+- (MGLStyleValue<NSString *> *)text {
+ MGLAssertStyleLayerIsValid();
+
+ auto propertyValue = self.rawLayer->getTextField() ?: self.rawLayer->getDefaultTextField();
+ return MGLStyleValueTransformer<std::string, NSString *>().toStyleValue(propertyValue);
+}
+
+- (void)setTextField:(MGLStyleValue<NSString *> *)textField {
+}
+
+- (MGLStyleValue<NSString *> *)textField {
+ return self.text;
+}
+
- (void)setTextAllowsOverlap:(MGLStyleValue<NSNumber *> *)textAllowsOverlap {
MGLAssertStyleLayerIsValid();
@@ -534,9 +544,7 @@ namespace mbgl {
return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(propertyValue);
}
-
- (void)setTextAllowOverlap:(MGLStyleValue<NSNumber *> *)textAllowOverlap {
- self.textAllowsOverlap = textAllowOverlap;
}
- (MGLStyleValue<NSNumber *> *)textAllowOverlap {
@@ -557,32 +565,46 @@ namespace mbgl {
return MGLStyleValueTransformer<mbgl::style::TextAnchorType, NSValue *, mbgl::style::TextAnchorType, MGLTextAnchor>().toEnumStyleValue(propertyValue);
}
-- (void)setTextField:(MGLStyleValue<NSString *> *)textField {
+- (void)setTextFontNames:(MGLStyleValue<NSArray<NSString *> *> *)textFontNames {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<std::string, NSString *>().toPropertyValue(textField);
- self.rawLayer->setTextField(mbglValue);
+ auto mbglValue = MGLStyleValueTransformer<std::vector<std::string>, NSArray<NSString *> *, std::string>().toPropertyValue(textFontNames);
+ self.rawLayer->setTextFont(mbglValue);
}
-- (MGLStyleValue<NSString *> *)textField {
+- (MGLStyleValue<NSArray<NSString *> *> *)textFontNames {
MGLAssertStyleLayerIsValid();
- auto propertyValue = self.rawLayer->getTextField() ?: self.rawLayer->getDefaultTextField();
- return MGLStyleValueTransformer<std::string, NSString *>().toStyleValue(propertyValue);
+ auto propertyValue = self.rawLayer->getTextFont() ?: self.rawLayer->getDefaultTextFont();
+ return MGLStyleValueTransformer<std::vector<std::string>, NSArray<NSString *> *, std::string>().toStyleValue(propertyValue);
}
- (void)setTextFont:(MGLStyleValue<NSArray<NSString *> *> *)textFont {
+}
+
+- (MGLStyleValue<NSArray<NSString *> *> *)textFont {
+ return self.textFontNames;
+}
+
+- (void)setTextFontSize:(MGLStyleValue<NSNumber *> *)textFontSize {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<std::vector<std::string>, NSArray<NSString *> *, std::string>().toPropertyValue(textFont);
- self.rawLayer->setTextFont(mbglValue);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue(textFontSize);
+ self.rawLayer->setTextSize(mbglValue);
}
-- (MGLStyleValue<NSArray<NSString *> *> *)textFont {
+- (MGLStyleValue<NSNumber *> *)textFontSize {
MGLAssertStyleLayerIsValid();
- auto propertyValue = self.rawLayer->getTextFont() ?: self.rawLayer->getDefaultTextFont();
- return MGLStyleValueTransformer<std::vector<std::string>, NSArray<NSString *> *, std::string>().toStyleValue(propertyValue);
+ auto propertyValue = self.rawLayer->getTextSize() ?: self.rawLayer->getDefaultTextSize();
+ return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
+}
+
+- (void)setTextSize:(MGLStyleValue<NSNumber *> *)textSize {
+}
+
+- (MGLStyleValue<NSNumber *> *)textSize {
+ return self.textFontSize;
}
- (void)setTextIgnoresPlacement:(MGLStyleValue<NSNumber *> *)textIgnoresPlacement {
@@ -599,9 +621,7 @@ namespace mbgl {
return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(propertyValue);
}
-
- (void)setTextIgnorePlacement:(MGLStyleValue<NSNumber *> *)textIgnorePlacement {
- self.textIgnoresPlacement = textIgnorePlacement;
}
- (MGLStyleValue<NSNumber *> *)textIgnorePlacement {
@@ -622,9 +642,7 @@ namespace mbgl {
return MGLStyleValueTransformer<mbgl::style::TextJustifyType, NSValue *, mbgl::style::TextJustifyType, MGLTextJustification>().toEnumStyleValue(propertyValue);
}
-
- (void)setTextJustify:(MGLStyleValue<NSValue *> *)textJustify {
- self.textJustification = textJustify;
}
- (MGLStyleValue<NSValue *> *)textJustify {
@@ -729,9 +747,7 @@ namespace mbgl {
return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
}
-
- (void)setTextRotate:(MGLStyleValue<NSNumber *> *)textRotate {
- self.textRotation = textRotate;
}
- (MGLStyleValue<NSNumber *> *)textRotate {
@@ -752,20 +768,6 @@ namespace mbgl {
return MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLTextRotationAlignment>().toEnumStyleValue(propertyValue);
}
-- (void)setTextSize:(MGLStyleValue<NSNumber *> *)textSize {
- MGLAssertStyleLayerIsValid();
-
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue(textSize);
- self.rawLayer->setTextSize(mbglValue);
-}
-
-- (MGLStyleValue<NSNumber *> *)textSize {
- MGLAssertStyleLayerIsValid();
-
- auto propertyValue = self.rawLayer->getTextSize() ?: self.rawLayer->getDefaultTextSize();
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
-}
-
- (void)setTextTransform:(MGLStyleValue<NSValue *> *)textTransform {
MGLAssertStyleLayerIsValid();
@@ -852,32 +854,46 @@ namespace mbgl {
return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
}
-- (void)setIconTranslate:(MGLStyleValue<NSValue *> *)iconTranslate {
+- (void)setIconTranslation:(MGLStyleValue<NSValue *> *)iconTranslation {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toPropertyValue(iconTranslate);
+ auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toPropertyValue(iconTranslation);
self.rawLayer->setIconTranslate(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)iconTranslate {
+- (MGLStyleValue<NSValue *> *)iconTranslation {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getIconTranslate() ?: self.rawLayer->getDefaultIconTranslate();
return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toStyleValue(propertyValue);
}
-- (void)setIconTranslateAnchor:(MGLStyleValue<NSValue *> *)iconTranslateAnchor {
+- (void)setIconTranslate:(MGLStyleValue<NSValue *> *)iconTranslate {
+}
+
+- (MGLStyleValue<NSValue *> *)iconTranslate {
+ return self.iconTranslation;
+}
+
+- (void)setIconTranslationAnchor:(MGLStyleValue<NSValue *> *)iconTranslationAnchor {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLIconTranslateAnchor>().toEnumPropertyValue(iconTranslateAnchor);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLIconTranslationAnchor>().toEnumPropertyValue(iconTranslationAnchor);
self.rawLayer->setIconTranslateAnchor(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)iconTranslateAnchor {
+- (MGLStyleValue<NSValue *> *)iconTranslationAnchor {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getIconTranslateAnchor() ?: self.rawLayer->getDefaultIconTranslateAnchor();
- return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLIconTranslateAnchor>().toEnumStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLIconTranslationAnchor>().toEnumStyleValue(propertyValue);
+}
+
+- (void)setIconTranslateAnchor:(MGLStyleValue<NSValue *> *)iconTranslateAnchor {
+}
+
+- (MGLStyleValue<NSValue *> *)iconTranslateAnchor {
+ return self.iconTranslationAnchor;
}
- (void)setTextColor:(MGLStyleValue<MGLColor *> *)textColor {
@@ -950,33 +966,151 @@ namespace mbgl {
return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
}
-- (void)setTextTranslate:(MGLStyleValue<NSValue *> *)textTranslate {
+- (void)setTextTranslation:(MGLStyleValue<NSValue *> *)textTranslation {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toPropertyValue(textTranslate);
+ auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toPropertyValue(textTranslation);
self.rawLayer->setTextTranslate(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)textTranslate {
+- (MGLStyleValue<NSValue *> *)textTranslation {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getTextTranslate() ?: self.rawLayer->getDefaultTextTranslate();
return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toStyleValue(propertyValue);
}
-- (void)setTextTranslateAnchor:(MGLStyleValue<NSValue *> *)textTranslateAnchor {
+- (void)setTextTranslate:(MGLStyleValue<NSValue *> *)textTranslate {
+}
+
+- (MGLStyleValue<NSValue *> *)textTranslate {
+ return self.textTranslation;
+}
+
+- (void)setTextTranslationAnchor:(MGLStyleValue<NSValue *> *)textTranslationAnchor {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLTextTranslateAnchor>().toEnumPropertyValue(textTranslateAnchor);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLTextTranslationAnchor>().toEnumPropertyValue(textTranslationAnchor);
self.rawLayer->setTextTranslateAnchor(mbglValue);
}
-- (MGLStyleValue<NSValue *> *)textTranslateAnchor {
+- (MGLStyleValue<NSValue *> *)textTranslationAnchor {
MGLAssertStyleLayerIsValid();
auto propertyValue = self.rawLayer->getTextTranslateAnchor() ?: self.rawLayer->getDefaultTextTranslateAnchor();
- return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLTextTranslateAnchor>().toEnumStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLTextTranslationAnchor>().toEnumStyleValue(propertyValue);
+}
+
+- (void)setTextTranslateAnchor:(MGLStyleValue<NSValue *> *)textTranslateAnchor {
+}
+
+- (MGLStyleValue<NSValue *> *)textTranslateAnchor {
+ return self.textTranslationAnchor;
+}
+
+
+@end
+
+@implementation NSValue (MGLSymbolStyleLayerAdditions)
+
++ (NSValue *)valueWithMGLIconRotationAlignment:(MGLIconRotationAlignment)iconRotationAlignment {
+ return [NSValue value:&iconRotationAlignment withObjCType:@encode(MGLIconRotationAlignment)];
}
+- (MGLIconRotationAlignment)MGLIconRotationAlignmentValue {
+ MGLIconRotationAlignment iconRotationAlignment;
+ [self getValue:&iconRotationAlignment];
+ return iconRotationAlignment;
+}
+
++ (NSValue *)valueWithMGLIconTextFit:(MGLIconTextFit)iconTextFit {
+ return [NSValue value:&iconTextFit withObjCType:@encode(MGLIconTextFit)];
+}
+
+- (MGLIconTextFit)MGLIconTextFitValue {
+ MGLIconTextFit iconTextFit;
+ [self getValue:&iconTextFit];
+ return iconTextFit;
+}
+
++ (NSValue *)valueWithMGLSymbolPlacement:(MGLSymbolPlacement)symbolPlacement {
+ return [NSValue value:&symbolPlacement withObjCType:@encode(MGLSymbolPlacement)];
+}
+
+- (MGLSymbolPlacement)MGLSymbolPlacementValue {
+ MGLSymbolPlacement symbolPlacement;
+ [self getValue:&symbolPlacement];
+ return symbolPlacement;
+}
+
++ (NSValue *)valueWithMGLTextAnchor:(MGLTextAnchor)textAnchor {
+ return [NSValue value:&textAnchor withObjCType:@encode(MGLTextAnchor)];
+}
+
+- (MGLTextAnchor)MGLTextAnchorValue {
+ MGLTextAnchor textAnchor;
+ [self getValue:&textAnchor];
+ return textAnchor;
+}
+
++ (NSValue *)valueWithMGLTextJustification:(MGLTextJustification)textJustification {
+ return [NSValue value:&textJustification withObjCType:@encode(MGLTextJustification)];
+}
+
+- (MGLTextJustification)MGLTextJustificationValue {
+ MGLTextJustification textJustification;
+ [self getValue:&textJustification];
+ return textJustification;
+}
+
++ (NSValue *)valueWithMGLTextPitchAlignment:(MGLTextPitchAlignment)textPitchAlignment {
+ return [NSValue value:&textPitchAlignment withObjCType:@encode(MGLTextPitchAlignment)];
+}
+
+- (MGLTextPitchAlignment)MGLTextPitchAlignmentValue {
+ MGLTextPitchAlignment textPitchAlignment;
+ [self getValue:&textPitchAlignment];
+ return textPitchAlignment;
+}
+
++ (NSValue *)valueWithMGLTextRotationAlignment:(MGLTextRotationAlignment)textRotationAlignment {
+ return [NSValue value:&textRotationAlignment withObjCType:@encode(MGLTextRotationAlignment)];
+}
+
+- (MGLTextRotationAlignment)MGLTextRotationAlignmentValue {
+ MGLTextRotationAlignment textRotationAlignment;
+ [self getValue:&textRotationAlignment];
+ return textRotationAlignment;
+}
+
++ (NSValue *)valueWithMGLTextTransform:(MGLTextTransform)textTransform {
+ return [NSValue value:&textTransform withObjCType:@encode(MGLTextTransform)];
+}
+
+- (MGLTextTransform)MGLTextTransformValue {
+ MGLTextTransform textTransform;
+ [self getValue:&textTransform];
+ return textTransform;
+}
+
++ (NSValue *)valueWithMGLIconTranslationAnchor:(MGLIconTranslationAnchor)iconTranslationAnchor {
+ return [NSValue value:&iconTranslationAnchor withObjCType:@encode(MGLIconTranslationAnchor)];
+}
+
+- (MGLIconTranslationAnchor)MGLIconTranslationAnchorValue {
+ MGLIconTranslationAnchor iconTranslationAnchor;
+ [self getValue:&iconTranslationAnchor];
+ return iconTranslationAnchor;
+}
+
++ (NSValue *)valueWithMGLTextTranslationAnchor:(MGLTextTranslationAnchor)textTranslationAnchor {
+ return [NSValue value:&textTranslationAnchor withObjCType:@encode(MGLTextTranslationAnchor)];
+}
+
+- (MGLTextTranslationAnchor)MGLTextTranslationAnchorValue {
+ MGLTextTranslationAnchor textTranslationAnchor;
+ [self getValue:&textTranslationAnchor];
+ return textTranslationAnchor;
+}
@end
diff --git a/platform/darwin/src/MGLTileSource.h b/platform/darwin/src/MGLTileSource.h
index 7e1a8206a3..99d23f4add 100644
--- a/platform/darwin/src/MGLTileSource.h
+++ b/platform/darwin/src/MGLTileSource.h
@@ -20,6 +20,10 @@ typedef NSString *MGLTileSourceOption NS_STRING_ENUM;
The value should be between 0 and 22, inclusive, and less than
`MGLTileSourceOptionMaximumZoomLevel`, if specified. The default value for this
option is 0.
+
+ This option corresponds to the `minzoom` key in the
+ <a href="https://github.com/mapbox/tilejson-spec/tree/master/2.1.0">TileJSON</a>
+ specification.
*/
extern MGL_EXPORT const MGLTileSourceOption MGLTileSourceOptionMinimumZoomLevel;
@@ -30,6 +34,10 @@ extern MGL_EXPORT const MGLTileSourceOption MGLTileSourceOptionMinimumZoomLevel;
The value should be between 0 and 22, inclusive, and less than
`MGLTileSourceOptionMinimumZoomLevel`, if specified. The default value for this
option is 22.
+
+ This option corresponds to the `maxzoom` key in the
+ <a href="https://github.com/mapbox/tilejson-spec/tree/master/2.1.0">TileJSON</a>
+ specification.
*/
extern MGL_EXPORT const MGLTileSourceOption MGLTileSourceOptionMaximumZoomLevel;
@@ -42,6 +50,10 @@ extern MGL_EXPORT const MGLTileSourceOption MGLTileSourceOptionMaximumZoomLevel;
By default, no attribution statements are displayed. If the
`MGLTileSourceOptionAttributionInfos` option is specified, this option is
ignored.
+
+ This option corresponds to the `attribution` key in the
+ <a href="https://github.com/mapbox/tilejson-spec/tree/master/2.1.0">TileJSON</a>
+ specification.
*/
extern MGL_EXPORT const MGLTileSourceOption MGLTileSourceOptionAttributionHTMLString;
@@ -61,6 +73,10 @@ extern MGL_EXPORT const MGLTileSourceOption MGLTileSourceOptionAttributionInfos;
By default, no attribution statements are displayed. If the
`MGLTileSourceOptionAttributionInfos` option is specified, this option is
ignored.
+
+ This option corresponds to the `attribution` key in the
+ <a href="https://github.com/mapbox/tilejson-spec/tree/master/2.1.0">TileJSON</a>
+ specification.
*/
extern MGL_EXPORT const MGLTileSourceOption MGLTileSourceOptionAttributionHTMLString;
@@ -80,6 +96,10 @@ extern MGL_EXPORT const MGLTileSourceOption MGLTileSourceOptionAttributionInfos;
the constants described in `MGLTileCoordinateSystem`.
The default value for this option is `MGLTileCoordinateSystemXYZ`.
+
+ This option corresponds to the `scheme` key in the
+ <a href="https://github.com/mapbox/tilejson-spec/tree/master/2.1.0">TileJSON</a>
+ specification.
*/
extern MGL_EXPORT const MGLTileSourceOption MGLTileSourceOptionTileCoordinateSystem;
@@ -150,13 +170,101 @@ MGL_EXPORT
/**
Returns a tile source initialized an identifier, tile URL templates, and
options.
-
+
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.
+ @param tileURLTemplates An array of tile URL template strings. Only the first
+ string is used; any additional strings are ignored.
@param options A dictionary containing configuration options. See
`MGLTileSourceOption` for available keys and values. Pass in `nil` to use
the default values.
@@ -164,6 +272,17 @@ MGL_EXPORT
*/
- (instancetype)initWithIdentifier:(NSString *)identifier tileURLTemplates:(NS_ARRAY_OF(NSString *) *)tileURLTemplates options:(nullable NS_DICTIONARY_OF(MGLTileSourceOption, id) *)options;
+#pragma mark Accessing a Source’s Content
+
+/**
+ The URL to the TileJSON configuration file that specifies the contents of the
+ source.
+
+ If the receiver was initialized using
+ `-initWithIdentifier:tileURLTemplates:options`, this property is set to `nil`.
+ */
+@property (nonatomic, copy, nullable, readonly) NSURL *configurationURL;
+
#pragma mark Accessing Attribution Strings
/**
diff --git a/platform/darwin/src/MGLTileSource.mm b/platform/darwin/src/MGLTileSource.mm
index 522675bc88..1aef81d53c 100644
--- a/platform/darwin/src/MGLTileSource.mm
+++ b/platform/darwin/src/MGLTileSource.mm
@@ -27,6 +27,12 @@ const MGLTileSourceOption MGLTileSourceOptionTileCoordinateSystem = @"MGLTileSou
return [super initWithIdentifier:identifier];
}
+- (NSURL *)configurationURL {
+ [NSException raise:@"MGLAbstractClassException"
+ format:@"MGLTileSource is an abstract class"];
+ return nil;
+}
+
- (NS_ARRAY_OF(MGLAttributionInfo *) *)attributionInfos {
return [self attributionInfosWithFontSize:0 linkColor:nil];
}
diff --git a/platform/darwin/src/MGLVectorSource.h b/platform/darwin/src/MGLVectorSource.h
index a5fd2584f8..bfa52f5b49 100644
--- a/platform/darwin/src/MGLVectorSource.h
+++ b/platform/darwin/src/MGLVectorSource.h
@@ -25,6 +25,19 @@ NS_ASSUME_NONNULL_BEGIN
(<var>extent</var>&nbsp;×&nbsp;2)&nbsp;−&nbsp;1, inclusive. Any vector style
layer initialized with a vector source must have a non-`nil` value in its
`sourceLayerIdentifier` property.
+
+ ### Example
+
+ ```swift
+ let source = MGLVectorSource(identifier: "pois", tileURLTemplates: ["https://example.com/vector-tiles/{z}/{x}/{y}.mvt"], options: [
+ .minimumZoomLevel: 9,
+ .maximumZoomLevel: 16,
+ .attributionInfos: [
+ MGLAttributionInfo(title: NSAttributedString(string: "© Mapbox"), url: URL(string: "http://mapbox.com"))
+ ]
+ ])
+ mapView.style?.addSource(source)
+ ```
*/
MGL_EXPORT
@interface MGLVectorSource : MGLTileSource
diff --git a/platform/darwin/src/MGLVectorSource.mm b/platform/darwin/src/MGLVectorSource.mm
index 0eea8dd18c..8fda528546 100644
--- a/platform/darwin/src/MGLVectorSource.mm
+++ b/platform/darwin/src/MGLVectorSource.mm
@@ -5,6 +5,7 @@
#import "MGLTileSource_Private.h"
#import "NSURL+MGLAdditions.h"
+#include <mbgl/map/map.hpp>
#include <mbgl/style/sources/vector_source.hpp>
@interface MGLVectorSource ()
@@ -69,6 +70,11 @@
super.rawSource = rawSource;
}
+- (NSURL *)configurationURL {
+ auto url = self.rawSource->getURL();
+ return url ? [NSURL URLWithString:@(url->c_str())] : nil;
+}
+
- (NSString *)attributionHTMLString {
auto attribution = self.rawSource->getAttribution();
return attribution ? @(attribution->c_str()) : nil;
diff --git a/platform/darwin/src/MGLVectorStyleLayer.h b/platform/darwin/src/MGLVectorStyleLayer.h
index f59ef4bf7f..ca09c11716 100644
--- a/platform/darwin/src/MGLVectorStyleLayer.h
+++ b/platform/darwin/src/MGLVectorStyleLayer.h
@@ -61,11 +61,15 @@ MGL_EXPORT
<li><code>NSContainsPredicateOperatorType</code> (<code>CONTAINS</code>)</li>
</ul>
- To test whether a feature has or lacks a specific attribute, compare the attribute to `NULL` or `NIL`. Predicates created using the `+[NSPredicate predicateWithValue:]` method are also supported. String operators and custom operators are not supported.
+ To test whether a feature has or lacks a specific attribute, compare the
+ attribute to `NULL` or `NIL`. Predicates created using the
+ `+[NSPredicate predicateWithValue:]` method are also supported. String
+ operators and custom operators are not supported.
For details about the predicate format string syntax, consult the “Predicate
- Format String Syntax” chapter of the “Predicate Programming Guide” in Apple
- developer documentation.
+ Format String Syntax” chapter of the
+ “<a href="https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Predicates/">Predicate Programming Guide</a>”
+ in Apple developer documentation.
The predicate's left-hand expression must be a string that identifies a feature
attribute or, alternatively, one of the following special attributes:
@@ -121,15 +125,29 @@ MGL_EXPORT
Automatic type casting is not performed. Therefore, a feature only matches this
predicate if its value for the attribute in question is of the same type as the
- value specified in the predicate. Also, operator modifiers `c`, `d`, and the
- combined `cd` for case and diacritic insensitivity are unsupported for
- comparison and aggregate operators that are used in the predicate.
+ value specified in the predicate. Also, operator modifiers such as `c` (for
+ case insensitivity), `d` (for diacritic insensitivity), and `l` (for locale
+ sensitivity) are unsupported for comparison and aggregate operators that are
+ used in the predicate.
It is possible to create expressions that contain special characters in the
predicate format syntax. This includes the `$` in the `$id` and `$type` special
style attributes and also `hyphen-minus` and `tag:subtag`. However, you must use
`%K` in the format string to represent these variables:
`@"%K == 'LineString'", @"$type"`.
+
+ ### Example
+
+ To filter the layer to include only the features whose `index` attribute is 5
+ or 10 and whose `ele` attribute is at least 1,500, you could create an
+ `NSCompoundPredicate` along these lines:
+
+ ```swift
+ let layer = MGLLineStyleLayer(identifier: "contour", source: terrain)
+ layer.sourceLayerIdentifier = "contours"
+ layer.predicate = NSPredicate(format: "(index == 5 || index == 10) && ele >= 1500.0")
+ mapView.style?.addLayer(layer)
+ ```
*/
@property (nonatomic, nullable) NSPredicate *predicate;
diff --git a/platform/darwin/src/MGLVectorStyleLayer.m b/platform/darwin/src/MGLVectorStyleLayer.m
index d8146f4246..da6da0ea7f 100644
--- a/platform/darwin/src/MGLVectorStyleLayer.m
+++ b/platform/darwin/src/MGLVectorStyleLayer.m
@@ -4,12 +4,12 @@
- (void)setPredicate:(NSPredicate *)predicate {
[NSException raise:@"MGLAbstractClassException"
- format:@"MGLVectorLayer is an abstract class"];
+ format:@"MGLVectorStyleLayer is an abstract class"];
}
- (NSPredicate *)predicate {
[NSException raise:@"MGLAbstractClassException"
- format:@"MGLVectorLayer is an abstract class"];
+ format:@"MGLVectorStyleLayer is an abstract class"];
return nil;
}
diff --git a/platform/darwin/src/NSArray+MGLAdditions.h b/platform/darwin/src/NSArray+MGLAdditions.h
index eb1cfb7c47..c4dfd8207b 100644
--- a/platform/darwin/src/NSArray+MGLAdditions.h
+++ b/platform/darwin/src/NSArray+MGLAdditions.h
@@ -1,4 +1,5 @@
#import <Foundation/Foundation.h>
+#import <CoreLocation/CoreLocation.h>
#import <mbgl/util/feature.hpp>
@@ -9,4 +10,18 @@
/** Returns a string resulting from inserting a separator between each attributed string in the array */
- (NSAttributedString *)mgl_attributedComponentsJoinedByString:(NSString *)separator;
+/**
+ Converts std::vector<CLLocationCoordinate> into an NSArray containing dictionary
+ representations of coordinates with the following structure:
+ [{"latitude": lat, "longitude": lng}]
+ */
++ (NSArray *)mgl_coordinatesFromCoordinates:(std::vector<CLLocationCoordinate2D>)coords;
+
+/**
+ Converts the receiver into a std::vector<CLLocationCoordinate>.
+ Receiver must conform to the following structure:
+ [{"latitude": lat, "longitude": lng}]
+ */
+- (std::vector<CLLocationCoordinate2D>)mgl_coordinates;
+
@end
diff --git a/platform/darwin/src/NSArray+MGLAdditions.mm b/platform/darwin/src/NSArray+MGLAdditions.mm
index b2799c46e1..8ec344f580 100644
--- a/platform/darwin/src/NSArray+MGLAdditions.mm
+++ b/platform/darwin/src/NSArray+MGLAdditions.mm
@@ -17,9 +17,9 @@
vector.push_back(propertyMap);
} else {
NSExpression *expression = [NSExpression expressionForConstantValue:value];
- vector.push_back([expression mgl_filterValue]);
+ vector.push_back(expression.mgl_constantMBGLValue);
}
- }
+ }
return vector;
}
@@ -38,4 +38,28 @@
return attributedString;
}
++ (NSArray *)mgl_coordinatesFromCoordinates:(std::vector<CLLocationCoordinate2D>)coords {
+ NSMutableArray *coordinates = [NSMutableArray array];
+ for (auto coord : coords) {
+ [coordinates addObject:@{@"latitude": @(coord.latitude),
+ @"longitude": @(coord.longitude)}];
+ }
+ return coordinates;
+}
+
+- (std::vector<CLLocationCoordinate2D>)mgl_coordinates {
+ NSUInteger numberOfCoordinates = [self count];
+ CLLocationCoordinate2D *coords = (CLLocationCoordinate2D *)malloc(numberOfCoordinates * sizeof(CLLocationCoordinate2D));
+
+ for (NSUInteger i = 0; i < numberOfCoordinates; i++) {
+ coords[i] = CLLocationCoordinate2DMake([self[i][@"latitude"] doubleValue],
+ [self[i][@"longitude"] doubleValue]);
+ }
+
+ std::vector<CLLocationCoordinate2D> coordinates = { coords, coords + numberOfCoordinates };
+ free(coords);
+
+ return coordinates;
+}
+
@end
diff --git a/platform/darwin/src/NSCoder+MGLAdditions.h b/platform/darwin/src/NSCoder+MGLAdditions.h
new file mode 100644
index 0000000000..036a99c5af
--- /dev/null
+++ b/platform/darwin/src/NSCoder+MGLAdditions.h
@@ -0,0 +1,16 @@
+#import <Foundation/Foundation.h>
+#import <CoreLocation/CoreLocation.h>
+
+#import <mbgl/util/feature.hpp>
+
+@interface NSCoder (MGLAdditions)
+
+- (void)encodeMGLCoordinate:(CLLocationCoordinate2D)coordinate forKey:(NSString *)key;
+
+- (CLLocationCoordinate2D)decodeMGLCoordinateForKey:(NSString *)key;
+
+- (void)mgl_encodeLocationCoordinates2D:(std::vector<CLLocationCoordinate2D>)coordinates forKey:(NSString *)key;
+
+- (std::vector<CLLocationCoordinate2D>)mgl_decodeLocationCoordinates2DForKey:(NSString *)key;
+
+@end
diff --git a/platform/darwin/src/NSCoder+MGLAdditions.mm b/platform/darwin/src/NSCoder+MGLAdditions.mm
new file mode 100644
index 0000000000..4af6c7588b
--- /dev/null
+++ b/platform/darwin/src/NSCoder+MGLAdditions.mm
@@ -0,0 +1,26 @@
+#import "NSCoder+MGLAdditions.h"
+
+#import "NSArray+MGLAdditions.h"
+#import "NSValue+MGLAdditions.h"
+
+@implementation NSCoder (MGLAdditions)
+
+- (void)mgl_encodeLocationCoordinates2D:(std::vector<CLLocationCoordinate2D>)coordinates forKey:(NSString *)key {
+ [self encodeObject:[NSArray mgl_coordinatesFromCoordinates:coordinates] forKey:key];
+}
+
+- (std::vector<CLLocationCoordinate2D>)mgl_decodeLocationCoordinates2DForKey:(NSString *)key {
+ NSArray *coordinates = [self decodeObjectOfClass:[NSArray class] forKey:key];
+ return [coordinates mgl_coordinates];
+}
+
+- (void)encodeMGLCoordinate:(CLLocationCoordinate2D)coordinate forKey:(NSString *)key {
+ [self encodeObject:@{@"latitude": @(coordinate.latitude), @"longitude": @(coordinate.longitude)} forKey:key];
+}
+
+- (CLLocationCoordinate2D)decodeMGLCoordinateForKey:(NSString *)key {
+ NSDictionary *coordinate = [self decodeObjectForKey:key];
+ return CLLocationCoordinate2DMake([coordinate[@"latitude"] doubleValue], [coordinate[@"longitude"] doubleValue]);
+}
+
+@end
diff --git a/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm b/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm
index 19c264aa40..58b37fae0e 100644
--- a/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm
+++ b/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm
@@ -7,95 +7,189 @@
- (mbgl::style::Filter)mgl_filter
{
+ NSExpression *leftExpression = self.leftExpression;
+ NSExpression *rightExpression = self.rightExpression;
+ NSExpressionType leftType = leftExpression.expressionType;
+ NSExpressionType rightType = rightExpression.expressionType;
+ BOOL isReversed = ((leftType == NSConstantValueExpressionType || leftType == NSAggregateExpressionType)
+ && rightType == NSKeyPathExpressionType);
switch (self.predicateOperatorType) {
case NSEqualToPredicateOperatorType: {
- if (self.rightExpression.constantValue)
- {
- auto filter = mbgl::style::EqualsFilter();
- filter.key = self.leftExpression.keyPath.UTF8String;
- filter.value = self.rightExpression.mgl_filterValue;
- return filter;
- }
- else
- {
- auto filter = mbgl::style::NotHasFilter();
- filter.key = self.leftExpression.keyPath.UTF8String;
- return filter;
+ mbgl::style::EqualsFilter eqFilter;
+ eqFilter.key = self.mgl_keyPath.UTF8String;
+ eqFilter.value = self.mgl_constantValue;
+
+ // Convert == nil to NotHasFilter.
+ if (eqFilter.value.is<mbgl::NullValue>()) {
+ mbgl::style::NotHasFilter notHasFilter;
+ notHasFilter.key = eqFilter.key;
+ return notHasFilter;
}
+
+ return eqFilter;
}
case NSNotEqualToPredicateOperatorType: {
- if (self.rightExpression.constantValue)
- {
- auto filter = mbgl::style::NotEqualsFilter();
- filter.key = self.leftExpression.keyPath.UTF8String;
- filter.value = self.rightExpression.mgl_filterValue;
- return filter;
- }
- else
- {
- auto filter = mbgl::style::HasFilter();
- filter.key = self.leftExpression.keyPath.UTF8String;
- return filter;
+ mbgl::style::NotEqualsFilter neFilter;
+ neFilter.key = self.mgl_keyPath.UTF8String;
+ neFilter.value = self.mgl_constantValue;
+
+ // Convert != nil to HasFilter.
+ if (neFilter.value.is<mbgl::NullValue>()) {
+ mbgl::style::HasFilter hasFilter;
+ hasFilter.key = neFilter.key;
+ return hasFilter;
}
+
+ return neFilter;
}
case NSGreaterThanPredicateOperatorType: {
- auto filter = mbgl::style::GreaterThanFilter();
- filter.key = self.leftExpression.keyPath.UTF8String;
- filter.value = self.rightExpression.mgl_filterValue;
- return filter;
+ if (isReversed) {
+ mbgl::style::LessThanFilter ltFilter;
+ ltFilter.key = self.mgl_keyPath.UTF8String;
+ ltFilter.value = self.mgl_constantValue;
+ return ltFilter;
+ } else {
+ mbgl::style::GreaterThanFilter gtFilter;
+ gtFilter.key = self.mgl_keyPath.UTF8String;
+ gtFilter.value = self.mgl_constantValue;
+ return gtFilter;
+ }
}
case NSGreaterThanOrEqualToPredicateOperatorType: {
- auto filter = mbgl::style::GreaterThanEqualsFilter();
- filter.key = self.leftExpression.keyPath.UTF8String;
- filter.value = self.rightExpression.mgl_filterValue;
- return filter;
+ if (isReversed) {
+ mbgl::style::LessThanEqualsFilter lteFilter;
+ lteFilter.key = self.mgl_keyPath.UTF8String;
+ lteFilter.value = self.mgl_constantValue;
+ return lteFilter;
+ } else {
+ mbgl::style::GreaterThanEqualsFilter gteFilter;
+ gteFilter.key = self.mgl_keyPath.UTF8String;
+ gteFilter.value = self.mgl_constantValue;
+ return gteFilter;
+ }
}
case NSLessThanPredicateOperatorType: {
- auto filter = mbgl::style::LessThanFilter();
- filter.key = self.leftExpression.keyPath.UTF8String;
- filter.value = self.rightExpression.mgl_filterValue;
- return filter;
+ if (isReversed) {
+ mbgl::style::GreaterThanFilter gtFilter;
+ gtFilter.key = self.mgl_keyPath.UTF8String;
+ gtFilter.value = self.mgl_constantValue;
+ return gtFilter;
+ } else {
+ mbgl::style::LessThanFilter ltFilter;
+ ltFilter.key = self.mgl_keyPath.UTF8String;
+ ltFilter.value = self.mgl_constantValue;
+ return ltFilter;
+ }
}
case NSLessThanOrEqualToPredicateOperatorType: {
- auto filter = mbgl::style::LessThanEqualsFilter();
- filter.key = self.leftExpression.keyPath.UTF8String;
- filter.value = self.rightExpression.mgl_filterValue;
- return filter;
+ if (isReversed) {
+ mbgl::style::GreaterThanEqualsFilter gteFilter;
+ gteFilter.key = self.mgl_keyPath.UTF8String;
+ gteFilter.value = self.mgl_constantValue;
+ return gteFilter;
+ } else {
+ mbgl::style::LessThanEqualsFilter lteFilter;
+ lteFilter.key = self.mgl_keyPath.UTF8String;
+ lteFilter.value = self.mgl_constantValue;
+ return lteFilter;
+ }
}
case NSInPredicateOperatorType: {
- auto filter = mbgl::style::InFilter();
- filter.key = self.leftExpression.keyPath.UTF8String;
- filter.values = self.rightExpression.mgl_filterValues;
- return filter;
+ if (isReversed) {
+ if (leftType == NSConstantValueExpressionType && [leftExpression.constantValue isKindOfClass:[NSString class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"CONTAINS not supported for string comparison."];
+ }
+ [NSException raise:NSInvalidArgumentException
+ format:@"Predicate cannot compare values IN attribute."];
+ }
+ mbgl::style::InFilter inFilter;
+ inFilter.key = leftExpression.keyPath.UTF8String;
+ inFilter.values = rightExpression.mgl_aggregateMBGLValue;
+ return inFilter;
}
case NSContainsPredicateOperatorType: {
- auto filter = mbgl::style::InFilter();
- filter.key = [self.rightExpression.constantValue UTF8String];
- filter.values = self.leftExpression.mgl_filterValues;
- return filter;
+ if (!isReversed) {
+ if (rightType == NSConstantValueExpressionType && [rightExpression.constantValue isKindOfClass:[NSString class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"IN not supported for string comparison."];
+ }
+ [NSException raise:NSInvalidArgumentException
+ format:@"Predicate cannot compare attribute CONTAINS values."];
+ }
+ mbgl::style::InFilter inFilter;
+ inFilter.key = rightExpression.keyPath.UTF8String;
+ inFilter.values = leftExpression.mgl_aggregateMBGLValue;
+ return inFilter;
}
case NSBetweenPredicateOperatorType: {
- auto filter = mbgl::style::AllFilter();
- auto gteFilter = mbgl::style::GreaterThanEqualsFilter();
- gteFilter.key = self.leftExpression.keyPath.UTF8String;
- gteFilter.value = self.rightExpression.mgl_filterValues[0];
- filter.filters.push_back(gteFilter);
- auto lteFilter = mbgl::style::LessThanEqualsFilter();
- lteFilter.key = self.leftExpression.keyPath.UTF8String;
- lteFilter.value = self.rightExpression.mgl_filterValues[1];
- filter.filters.push_back(lteFilter);
- return filter;
+ if (isReversed) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Predicate cannot compare bounds BETWEEN attribute."];
+ }
+ if (![rightExpression.constantValue isKindOfClass:[NSArray class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Right side of BETWEEN predicate must be an array."]; // not NSSet
+ }
+ auto values = rightExpression.mgl_aggregateMBGLValue;
+ if (values.size() != 2) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Right side of BETWEEN predicate must have two items."];
+ }
+ mbgl::style::AllFilter allFilter;
+ mbgl::style::GreaterThanEqualsFilter gteFilter;
+ gteFilter.key = leftExpression.keyPath.UTF8String;
+ gteFilter.value = values[0];
+ allFilter.filters.push_back(gteFilter);
+ mbgl::style::LessThanEqualsFilter lteFilter;
+ lteFilter.key = leftExpression.keyPath.UTF8String;
+ lteFilter.value = values[1];
+ allFilter.filters.push_back(lteFilter);
+ return allFilter;
}
case NSMatchesPredicateOperatorType:
case NSLikePredicateOperatorType:
case NSBeginsWithPredicateOperatorType:
case NSEndsWithPredicateOperatorType:
case NSCustomSelectorPredicateOperatorType:
- [NSException raise:@"Unsupported operator type"
+ [NSException raise:NSInvalidArgumentException
format:@"NSPredicateOperatorType:%lu is not supported.", (unsigned long)self.predicateOperatorType];
}
return {};
}
+- (NSString *)mgl_keyPath {
+ NSExpression *leftExpression = self.leftExpression;
+ NSExpression *rightExpression = self.rightExpression;
+ NSExpressionType leftType = leftExpression.expressionType;
+ NSExpressionType rightType = rightExpression.expressionType;
+ if (leftType == NSKeyPathExpressionType && rightType == NSConstantValueExpressionType) {
+ return leftExpression.keyPath;
+ } else if (leftType == NSConstantValueExpressionType && rightType == NSKeyPathExpressionType) {
+ return rightExpression.keyPath;
+ }
+
+ [NSException raise:NSInvalidArgumentException
+ format:@"Comparison predicate must compare an attribute (as a key path) to a constant or vice versa."];
+ return nil;
+}
+
+- (mbgl::Value)mgl_constantValue {
+ NSExpression *leftExpression = self.leftExpression;
+ NSExpression *rightExpression = self.rightExpression;
+ NSExpressionType leftType = leftExpression.expressionType;
+ NSExpressionType rightType = rightExpression.expressionType;
+ mbgl::Value value;
+ if (leftType == NSKeyPathExpressionType && rightType == NSConstantValueExpressionType) {
+ value = rightExpression.mgl_constantMBGLValue;
+ } else if (leftType == NSConstantValueExpressionType && rightType == NSKeyPathExpressionType) {
+ value = leftExpression.mgl_constantMBGLValue;
+ } else {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Comparison predicate must compare an attribute (as a key path) to a constant or vice versa."];
+ }
+ return value;
+}
+
@end
diff --git a/platform/darwin/src/NSCompoundPredicate+MGLAdditions.mm b/platform/darwin/src/NSCompoundPredicate+MGLAdditions.mm
index 07114308c9..2697467198 100644
--- a/platform/darwin/src/NSCompoundPredicate+MGLAdditions.mm
+++ b/platform/darwin/src/NSCompoundPredicate+MGLAdditions.mm
@@ -18,32 +18,49 @@
{
switch (self.compoundPredicateType) {
case NSNotPredicateType: {
+ NSAssert(self.subpredicates.count <= 1, @"NOT predicate cannot have multiple subpredicates.");
+ NSPredicate *subpredicate = self.subpredicates.firstObject;
+ mbgl::style::Filter subfilter = subpredicate.mgl_filter;
- // Translate a nested NSComparisonPredicate with operator type NSInPredicateOperatorType into a flat mbgl::NotIn filter.
- NSArray<NSComparisonPredicate *> *comparisonPredicates = [self.subpredicates filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"class == %@", [NSComparisonPredicate class]]];
- NSArray<NSComparisonPredicate *> *notInPredicates = [comparisonPredicates filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSComparisonPredicate *_Nonnull predicate, NSDictionary<NSString *,id> * _Nullable bindings) {
- return predicate.predicateOperatorType == NSInPredicateOperatorType;
- }]];
+ // Convert NOT(!= nil) to NotHasFilter.
+ if (subfilter.is<mbgl::style::HasFilter>()) {
+ auto hasFilter = subfilter.get<mbgl::style::HasFilter>();
+ return mbgl::style::NotHasFilter { .key = hasFilter.key };
+ }
+
+ // Convert NOT(== nil) to HasFilter.
+ if (subfilter.is<mbgl::style::NotHasFilter>()) {
+ auto hasFilter = subfilter.get<mbgl::style::NotHasFilter>();
+ return mbgl::style::HasFilter { .key = hasFilter.key };
+ }
+
+ // Convert NOT(IN) or NOT(CONTAINS) to NotInFilter.
+ if (subfilter.is<mbgl::style::InFilter>()) {
+ auto inFilter = subfilter.get<mbgl::style::InFilter>();
+ mbgl::style::NotInFilter notInFilter;
+ notInFilter.key = inFilter.key;
+ notInFilter.values = inFilter.values;
+ return notInFilter;
+ }
- if (notInPredicates.count) {
- auto filter = mbgl::style::NotInFilter();
- filter.key = notInPredicates.firstObject.leftExpression.keyPath.UTF8String;
- filter.values = notInPredicates.firstObject.rightExpression.mgl_filterValues;
- return filter;
- } else {
- auto filter = mbgl::style::NoneFilter();
- filter.filters = [self mgl_subfilters];
- return filter;
+ // Convert NOT(), NOT(AND), NOT(NOT), NOT(==), etc. into NoneFilter.
+ mbgl::style::NoneFilter noneFilter;
+ if (subfilter.is<mbgl::style::AnyFilter>()) {
+ // Flatten NOT(OR).
+ noneFilter.filters = subfilter.get<mbgl::style::AnyFilter>().filters;
+ } else if (subpredicate) {
+ noneFilter.filters = { subfilter };
}
+ return noneFilter;
}
case NSAndPredicateType: {
- auto filter = mbgl::style::AllFilter();
- filter.filters = [self mgl_subfilters];
+ mbgl::style::AllFilter filter;
+ filter.filters = self.mgl_subfilters;
return filter;
}
case NSOrPredicateType: {
- auto filter = mbgl::style::AnyFilter();
- filter.filters = [self mgl_subfilters];
+ mbgl::style::AnyFilter filter;
+ filter.filters = self.mgl_subfilters;
return filter;
}
}
diff --git a/platform/darwin/src/NSDictionary+MGLAdditions.mm b/platform/darwin/src/NSDictionary+MGLAdditions.mm
index 1023e91a48..aad7fd8810 100644
--- a/platform/darwin/src/NSDictionary+MGLAdditions.mm
+++ b/platform/darwin/src/NSDictionary+MGLAdditions.mm
@@ -15,7 +15,7 @@
propertyMap[[key UTF8String]] = [array mgl_vector];
} else {
NSExpression *expression = [NSExpression expressionForConstantValue:self[key]];
- propertyMap[[key UTF8String]] = [expression mgl_filterValue];
+ propertyMap[[key UTF8String]] = expression.mgl_constantMBGLValue;
}
}
return propertyMap;
diff --git a/platform/darwin/src/NSExpression+MGLAdditions.h b/platform/darwin/src/NSExpression+MGLAdditions.h
index 6d0fff5760..c60d6d78ba 100644
--- a/platform/darwin/src/NSExpression+MGLAdditions.h
+++ b/platform/darwin/src/NSExpression+MGLAdditions.h
@@ -2,10 +2,14 @@
#include <mbgl/style/filter.hpp>
+NS_ASSUME_NONNULL_BEGIN
+
@interface NSExpression (MGLAdditions)
-- (mbgl::Value)mgl_filterValue;
-- (std::vector<mbgl::Value>)mgl_filterValues;
-- (mbgl::FeatureIdentifier)mgl_featureIdentifier;
+@property (nonatomic, readonly) mbgl::Value mgl_constantMBGLValue;
+@property (nonatomic, readonly) std::vector<mbgl::Value> mgl_aggregateMBGLValue;
+@property (nonatomic, readonly) mbgl::FeatureIdentifier mgl_featureIdentifier;
@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/NSExpression+MGLAdditions.mm b/platform/darwin/src/NSExpression+MGLAdditions.mm
index 25a2945cfb..b095091b17 100644
--- a/platform/darwin/src/NSExpression+MGLAdditions.mm
+++ b/platform/darwin/src/NSExpression+MGLAdditions.mm
@@ -2,27 +2,25 @@
@implementation NSExpression (MGLAdditions)
-- (std::vector<mbgl::Value>)mgl_filterValues
-{
- if ([self.constantValue isKindOfClass:NSArray.class]) {
- NSArray *values = self.constantValue;
- std::vector<mbgl::Value>convertedValues;
- for (id value in values) {
- convertedValues.push_back([self mgl_convertedValueWithValue:value]);
+- (std::vector<mbgl::Value>)mgl_aggregateMBGLValue {
+ if ([self.constantValue isKindOfClass:[NSArray class]] || [self.constantValue isKindOfClass:[NSSet class]]) {
+ std::vector<mbgl::Value> convertedValues;
+ for (id value in self.constantValue) {
+ NSExpression *expression = value;
+ if (![expression isKindOfClass:[NSExpression class]]) {
+ expression = [NSExpression expressionForConstantValue:expression];
+ }
+ convertedValues.push_back(expression.mgl_constantMBGLValue);
}
return convertedValues;
}
- [NSException raise:@"Values not handled" format:@""];
- return { };
-}
-
-- (mbgl::Value)mgl_filterValue
-{
- return [self mgl_convertedValueWithValue:self.constantValue];
+ [NSException raise:NSInvalidArgumentException
+ format:@"Constant value expression must contain an array or set."];
+ return {};
}
-- (mbgl::Value)mgl_convertedValueWithValue:(id)value
-{
+- (mbgl::Value)mgl_constantMBGLValue {
+ id value = self.constantValue;
if ([value isKindOfClass:NSString.class]) {
return { std::string([(NSString *)value UTF8String]) };
} else if ([value isKindOfClass:NSNumber.class]) {
@@ -42,7 +40,10 @@
// We still do this conversion in order to provide a valid value.
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
- NSLog(@"One-time warning: Float values are converted to double and can introduce imprecision; please use double values explicitly in predicate arguments.");
+ NSLog(@"Float value in expression will be converted to a double; some imprecision may result. "
+ @"Use double values explicitly when specifying constant expression values and "
+ @"when specifying arguments to predicate and expression format strings. "
+ @"This will be logged only once.");
});
return { (double)number.doubleValue };
} else if ([number compare:@(0)] == NSOrderedDescending ||
@@ -55,33 +56,27 @@
// We use long long here to avoid any truncation.
return { (int64_t)number.longLongValue };
}
+ } else if (value && value != [NSNull null]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Can’t convert %s:%@ to mbgl::Value", [value objCType], value];
}
- [NSException raise:@"Value not handled"
- format:@"Can’t convert %s:%@ to mbgl::Value", [value objCType], value];
- return { };
+ return {};
}
-- (mbgl::FeatureIdentifier)mgl_featureIdentifier
-{
- id value = self.constantValue;
- mbgl::Value mbglValue = [self mgl_filterValue];
+- (mbgl::FeatureIdentifier)mgl_featureIdentifier {
+ mbgl::Value mbglValue = self.mgl_constantMBGLValue;
- if ([value isKindOfClass:NSString.class]) {
+ if (mbglValue.is<std::string>()) {
return mbglValue.get<std::string>();
- } else if ([value isKindOfClass:NSNumber.class]) {
- NSNumber *number = (NSNumber *)value;
- if ((strcmp([number objCType], @encode(char)) == 0) ||
- (strcmp([number objCType], @encode(BOOL)) == 0)) {
- return uint64_t(mbglValue.get<bool>());
- } else if ( strcmp([number objCType], @encode(double)) == 0 ||
- strcmp([number objCType], @encode(float)) == 0) {
- return mbglValue.get<double>();
- } else if ([number compare:@(0)] == NSOrderedDescending ||
- [number compare:@(0)] == NSOrderedSame) {
- return mbglValue.get<uint64_t>();
- } else if ([number compare:@(0)] == NSOrderedAscending) {
- return mbglValue.get<int64_t>();
- }
+ }
+ if (mbglValue.is<double>()) {
+ return mbglValue.get<double>();
+ }
+ if (mbglValue.is<uint64_t>()) {
+ return mbglValue.get<uint64_t>();
+ }
+ if (mbglValue.is<int64_t>()) {
+ return mbglValue.get<int64_t>();
}
return {};
diff --git a/platform/darwin/src/NSPredicate+MGLAdditions.mm b/platform/darwin/src/NSPredicate+MGLAdditions.mm
index 64ad277e4d..0ac68095f9 100644
--- a/platform/darwin/src/NSPredicate+MGLAdditions.mm
+++ b/platform/darwin/src/NSPredicate+MGLAdditions.mm
@@ -5,7 +5,7 @@
class FilterEvaluator {
public:
- NSArray* getPredicates(std::vector<mbgl::style::Filter> filters) {
+ NSArray *getPredicates(std::vector<mbgl::style::Filter> filters) {
NSMutableArray *predicates = [NSMutableArray arrayWithCapacity:filters.size()];
for (auto filter : filters) {
[predicates addObject:mbgl::style::Filter::visit(filter, FilterEvaluator())];
@@ -13,73 +13,118 @@ public:
return predicates;
}
- NSArray* getValues(std::vector<mbgl::Value> values) {
+ NSExpression *getValues(std::vector<mbgl::Value> values) {
NSMutableArray *array = [NSMutableArray arrayWithCapacity:values.size()];
for (auto value : values) {
- [array addObject:mbgl::Value::visit(value, ValueEvaluator())];
+ id constantValue = mbgl::Value::visit(value, ValueEvaluator());
+ [array addObject:[NSExpression expressionForConstantValue:constantValue]];
}
- return array;
+ return [NSExpression expressionForAggregate:array];
}
- NSPredicate* operator()(mbgl::style::NullFilter filter) {
+ NSPredicate *operator()(mbgl::style::NullFilter filter) {
return nil;
}
- NSPredicate* operator()(mbgl::style::EqualsFilter filter) {
+ NSPredicate *operator()(mbgl::style::EqualsFilter filter) {
return [NSPredicate predicateWithFormat:@"%K == %@", @(filter.key.c_str()), mbgl::Value::visit(filter.value, ValueEvaluator())];
}
- NSPredicate* operator()(mbgl::style::NotEqualsFilter filter) {
+ NSPredicate *operator()(mbgl::style::NotEqualsFilter filter) {
return [NSPredicate predicateWithFormat:@"%K != %@", @(filter.key.c_str()), mbgl::Value::visit(filter.value, ValueEvaluator())];
}
- NSPredicate* operator()(mbgl::style::GreaterThanFilter filter) {
+ NSPredicate *operator()(mbgl::style::GreaterThanFilter filter) {
return [NSPredicate predicateWithFormat:@"%K > %@", @(filter.key.c_str()), mbgl::Value::visit(filter.value, ValueEvaluator())];
}
- NSPredicate* operator()(mbgl::style::GreaterThanEqualsFilter filter) {
+ NSPredicate *operator()(mbgl::style::GreaterThanEqualsFilter filter) {
return [NSPredicate predicateWithFormat:@"%K >= %@", @(filter.key.c_str()), mbgl::Value::visit(filter.value, ValueEvaluator())];
}
- NSPredicate* operator()(mbgl::style::LessThanFilter filter) {
+ NSPredicate *operator()(mbgl::style::LessThanFilter filter) {
return [NSPredicate predicateWithFormat:@"%K < %@", @(filter.key.c_str()), mbgl::Value::visit(filter.value, ValueEvaluator())];
}
- NSPredicate* operator()(mbgl::style::LessThanEqualsFilter filter) {
+ NSPredicate *operator()(mbgl::style::LessThanEqualsFilter filter) {
return [NSPredicate predicateWithFormat:@"%K <= %@", @(filter.key.c_str()), mbgl::Value::visit(filter.value, ValueEvaluator())];
}
- NSPredicate* operator()(mbgl::style::InFilter filter) {
+ NSPredicate *operator()(mbgl::style::InFilter filter) {
return [NSPredicate predicateWithFormat:@"%K IN %@", @(filter.key.c_str()), getValues(filter.values)];
}
- NSPredicate* operator()(mbgl::style::NotInFilter filter) {
+ NSPredicate *operator()(mbgl::style::NotInFilter filter) {
return [NSPredicate predicateWithFormat:@"NOT %K IN %@", @(filter.key.c_str()), getValues(filter.values)];
}
- NSPredicate* operator()(mbgl::style::AnyFilter filter) {
- return [NSCompoundPredicate orPredicateWithSubpredicates:getPredicates(filter.filters)];
- }
-
- NSPredicate* operator()(mbgl::style::AllFilter filter) {
- return [NSCompoundPredicate andPredicateWithSubpredicates:getPredicates(filter.filters)];
+ NSPredicate *operator()(mbgl::style::AnyFilter filter) {
+ NSArray *subpredicates = getPredicates(filter.filters);
+ if (subpredicates.count) {
+ return [NSCompoundPredicate orPredicateWithSubpredicates:subpredicates];
+ }
+ return [NSPredicate predicateWithValue:NO];
+ }
+
+ NSPredicate *operator()(mbgl::style::AllFilter filter) {
+ // Convert [all, [>=, key, lower], [<=, key, upper]] to key BETWEEN {lower, upper}
+ if (filter.filters.size() == 2) {
+ auto leftFilter = filter.filters[0];
+ auto rightFilter = filter.filters[1];
+
+ std::string lowerKey;
+ std::string upperKey;
+ mbgl::Value lowerBound;
+ mbgl::Value upperBound;
+ if (leftFilter.is<mbgl::style::GreaterThanEqualsFilter>()) {
+ lowerKey = leftFilter.get<mbgl::style::GreaterThanEqualsFilter>().key;
+ lowerBound = leftFilter.get<mbgl::style::GreaterThanEqualsFilter>().value;
+ } else if (rightFilter.is<mbgl::style::GreaterThanEqualsFilter>()) {
+ lowerKey = rightFilter.get<mbgl::style::GreaterThanEqualsFilter>().key;
+ lowerBound = rightFilter.get<mbgl::style::GreaterThanEqualsFilter>().value;
+ }
+
+ if (leftFilter.is<mbgl::style::LessThanEqualsFilter>()) {
+ upperKey = leftFilter.get<mbgl::style::LessThanEqualsFilter>().key;
+ upperBound = leftFilter.get<mbgl::style::LessThanEqualsFilter>().value;
+ } else if (rightFilter.is<mbgl::style::LessThanEqualsFilter>()) {
+ upperKey = rightFilter.get<mbgl::style::LessThanEqualsFilter>().key;
+ upperBound = rightFilter.get<mbgl::style::LessThanEqualsFilter>().value;
+ }
+
+ if (!lowerBound.is<mbgl::NullValue>() && !upperBound.is<mbgl::NullValue>()
+ && lowerKey == upperKey) {
+ return [NSPredicate predicateWithFormat:@"%K BETWEEN {%@, %@}",
+ @(lowerKey.c_str()),
+ mbgl::Value::visit(lowerBound, ValueEvaluator()),
+ mbgl::Value::visit(upperBound, ValueEvaluator())];
+ }
+ }
+
+ NSArray *subpredicates = getPredicates(filter.filters);
+ if (subpredicates.count) {
+ return [NSCompoundPredicate andPredicateWithSubpredicates:subpredicates];
+ }
+ return [NSPredicate predicateWithValue:YES];
}
- NSPredicate* operator()(mbgl::style::NoneFilter filter) {
- NSArray *predicates = getPredicates(filter.filters);
- if (predicates.count > 1) {
- NSCompoundPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:predicates];
+ NSPredicate *operator()(mbgl::style::NoneFilter filter) {
+ NSArray *subpredicates = getPredicates(filter.filters);
+ if (subpredicates.count > 1) {
+ NSCompoundPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:subpredicates];
return [NSCompoundPredicate notPredicateWithSubpredicate:predicate];
+ } else if (subpredicates.count) {
+ return [NSCompoundPredicate notPredicateWithSubpredicate:subpredicates.firstObject];
} else {
- return [NSCompoundPredicate notPredicateWithSubpredicate:predicates.firstObject];
+ return [NSPredicate predicateWithValue:YES];
}
}
- NSPredicate* operator()(mbgl::style::HasFilter filter) {
+ NSPredicate *operator()(mbgl::style::HasFilter filter) {
return [NSPredicate predicateWithFormat:@"%K != nil", @(filter.key.c_str())];
}
- NSPredicate* operator()(mbgl::style::NotHasFilter filter) {
+ NSPredicate *operator()(mbgl::style::NotHasFilter filter) {
return [NSPredicate predicateWithFormat:@"%K == nil", @(filter.key.c_str())];
}
@@ -91,18 +136,22 @@ public:
{
if ([self isEqual:[NSPredicate predicateWithValue:YES]])
{
- auto filter = mbgl::style::AllFilter();
- return filter;
+ return mbgl::style::AllFilter();
}
if ([self isEqual:[NSPredicate predicateWithValue:NO]])
{
- auto filter = mbgl::style::AnyFilter();
- return filter;
+ return mbgl::style::AnyFilter();
+ }
+
+ if ([self.predicateFormat hasPrefix:@"BLOCKPREDICATE("])
+ {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Block-based predicates are not supported."];
}
- [NSException raise:@"Not supported"
- format:@"Try with NSComparisonPredicate or NSCompoundPredicate instead."];
+ [NSException raise:NSInvalidArgumentException
+ format:@"Unrecognized predicate type."];
return {};
}
diff --git a/platform/darwin/src/NSString+MGLAdditions.h b/platform/darwin/src/NSString+MGLAdditions.h
index 45fea25588..246dc084f4 100644
--- a/platform/darwin/src/NSString+MGLAdditions.h
+++ b/platform/darwin/src/NSString+MGLAdditions.h
@@ -10,6 +10,15 @@ NS_ASSUME_NONNULL_BEGIN
/** Returns the receiver if non-empty or nil if empty. */
- (nullable NSString *)mgl_stringOrNilIfEmpty;
+/**
+ Returns a title-cased representation of the receiver using the specified
+ locale.
+
+ @param The locale. For strings presented to users, pass in the current locale
+ (`+[NSLocale currentLocale]`). To use the system locale, pass in `nil`.
+ */
+- (NSString *)mgl_titleCasedStringWithLocale:(NSLocale *)locale;
+
@end
@interface NSAttributedString (MGLAdditions)
diff --git a/platform/darwin/src/NSString+MGLAdditions.m b/platform/darwin/src/NSString+MGLAdditions.m
index 04a65dc5e2..5c32f4b789 100644
--- a/platform/darwin/src/NSString+MGLAdditions.m
+++ b/platform/darwin/src/NSString+MGLAdditions.m
@@ -10,6 +10,28 @@
return self.length ? self : nil;
}
+- (NSString *)mgl_titleCasedStringWithLocale:(NSLocale *)locale {
+ NSMutableString *string = self.mutableCopy;
+ [string enumerateLinguisticTagsInRange:string.mgl_wholeRange scheme:NSLinguisticTagSchemeLexicalClass options:0 orthography:nil usingBlock:^(NSString * _Nonnull tag, NSRange tokenRange, NSRange sentenceRange, BOOL * _Nonnull stop) {
+ NSString *word = [string substringWithRange:tokenRange];
+ if (word.length > 3
+ || !([tag isEqualToString:NSLinguisticTagConjunction]
+ || [tag isEqualToString:NSLinguisticTagPreposition]
+ || [tag isEqualToString:NSLinguisticTagDeterminer]
+ || [tag isEqualToString:NSLinguisticTagParticle]
+ || [tag isEqualToString:NSLinguisticTagClassifier])) {
+ unichar firstLetter = [[word capitalizedStringWithLocale:locale] characterAtIndex:0];
+ NSString *suffix = [word substringFromIndex:1];
+ if (!([word hasPrefix:@"i"] && suffix.length
+ && [[NSCharacterSet uppercaseLetterCharacterSet] characterIsMember:[suffix characterAtIndex:0]])) {
+ word = [NSString stringWithFormat:@"%C%@", firstLetter, suffix];
+ }
+ }
+ [string replaceCharactersInRange:tokenRange withString:word];
+ }];
+ return string;
+}
+
@end
@implementation NSAttributedString (MGLAdditions)
diff --git a/platform/darwin/src/NSValue+MGLStyleAttributeAdditions.mm b/platform/darwin/src/NSValue+MGLStyleAttributeAdditions.mm
index 59afe559d7..e66145aec1 100644
--- a/platform/darwin/src/NSValue+MGLStyleAttributeAdditions.mm
+++ b/platform/darwin/src/NSValue+MGLStyleAttributeAdditions.mm
@@ -12,6 +12,11 @@
+ (instancetype)mgl_valueWithOffsetArray:(std::array<float, 2>)offsetArray
{
CGVector vector = CGVectorMake(offsetArray[0], offsetArray[1]);
+#if !TARGET_OS_IPHONE
+ // Style specification assumes an origin at the upper-left corner.
+ // macOS defines an origin at the lower-left corner.
+ vector.dy *= -1;
+#endif
return [NSValue value:&vector withObjCType:@encode(CGVector)];
}
@@ -33,6 +38,9 @@
NSAssert(strcmp(self.objCType, @encode(CGVector)) == 0, @"Value does not represent a CGVector");
CGVector vector;
[self getValue:&vector];
+#if !TARGET_OS_IPHONE
+ vector.dy *= -1;
+#endif
return {
static_cast<float>(vector.dx),
static_cast<float>(vector.dy),
diff --git a/platform/darwin/src/NSValue+MGLStyleEnumAttributeAdditions.h b/platform/darwin/src/NSValue+MGLStyleEnumAttributeAdditions.h
deleted file mode 100644
index 3387ce8188..0000000000
--- a/platform/darwin/src/NSValue+MGLStyleEnumAttributeAdditions.h
+++ /dev/null
@@ -1,232 +0,0 @@
-// This file is generated.
-// Edit platform/darwin/scripts/generate-style-code.js, then run `make style-code-darwin`.
-
-#import <Foundation/Foundation.h>
-
-#import "MGLFillStyleLayer.h"
-#import "MGLLineStyleLayer.h"
-#import "MGLSymbolStyleLayer.h"
-#import "MGLCircleStyleLayer.h"
-#import "MGLRasterStyleLayer.h"
-#import "MGLBackgroundStyleLayer.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/**
- Methods for round-tripping values for Mapbox style layer enumeration values.
-*/
-@interface NSValue (MGLStyleEnumAttributeAdditions)
-
-#pragma mark Working with Style Layer Enumeration Attribute Values
-
-/**
- Creates a new value object containing the given `MGLLineCap` enumeration.
-
- @param type The value for the new object.
- @return A new value object that contains the style enumeration type.
-*/
-+ (instancetype)valueWithMGLLineCap:(MGLLineCap)lineCap;
-
-/**
- The `MGLLineCap` enumeration representation of the value.
-*/
-@property (readonly) MGLLineCap MGLLineCapValue;
-
-/**
- Creates a new value object containing the given `MGLLineJoin` enumeration.
-
- @param type The value for the new object.
- @return A new value object that contains the style enumeration type.
-*/
-+ (instancetype)valueWithMGLLineJoin:(MGLLineJoin)lineJoin;
-
-/**
- The `MGLLineJoin` enumeration representation of the value.
-*/
-@property (readonly) MGLLineJoin MGLLineJoinValue;
-
-/**
- Creates a new value object containing the given `MGLIconRotationAlignment` enumeration.
-
- @param type The value for the new object.
- @return A new value object that contains the style enumeration type.
-*/
-+ (instancetype)valueWithMGLIconRotationAlignment:(MGLIconRotationAlignment)iconRotationAlignment;
-
-/**
- The `MGLIconRotationAlignment` enumeration representation of the value.
-*/
-@property (readonly) MGLIconRotationAlignment MGLIconRotationAlignmentValue;
-
-/**
- Creates a new value object containing the given `MGLIconTextFit` enumeration.
-
- @param type The value for the new object.
- @return A new value object that contains the style enumeration type.
-*/
-+ (instancetype)valueWithMGLIconTextFit:(MGLIconTextFit)iconTextFit;
-
-/**
- The `MGLIconTextFit` enumeration representation of the value.
-*/
-@property (readonly) MGLIconTextFit MGLIconTextFitValue;
-
-/**
- Creates a new value object containing the given `MGLSymbolPlacement` enumeration.
-
- @param type The value for the new object.
- @return A new value object that contains the style enumeration type.
-*/
-+ (instancetype)valueWithMGLSymbolPlacement:(MGLSymbolPlacement)symbolPlacement;
-
-/**
- The `MGLSymbolPlacement` enumeration representation of the value.
-*/
-@property (readonly) MGLSymbolPlacement MGLSymbolPlacementValue;
-
-/**
- Creates a new value object containing the given `MGLTextAnchor` enumeration.
-
- @param type The value for the new object.
- @return A new value object that contains the style enumeration type.
-*/
-+ (instancetype)valueWithMGLTextAnchor:(MGLTextAnchor)textAnchor;
-
-/**
- The `MGLTextAnchor` enumeration representation of the value.
-*/
-@property (readonly) MGLTextAnchor MGLTextAnchorValue;
-
-/**
- Creates a new value object containing the given `MGLTextJustification` enumeration.
-
- @param type The value for the new object.
- @return A new value object that contains the style enumeration type.
-*/
-+ (instancetype)valueWithMGLTextJustification:(MGLTextJustification)textJustification;
-
-/**
- The `MGLTextJustification` enumeration representation of the value.
-*/
-@property (readonly) MGLTextJustification MGLTextJustificationValue;
-
-/**
- Creates a new value object containing the given `MGLTextPitchAlignment` enumeration.
-
- @param type The value for the new object.
- @return A new value object that contains the style enumeration type.
-*/
-+ (instancetype)valueWithMGLTextPitchAlignment:(MGLTextPitchAlignment)textPitchAlignment;
-
-/**
- The `MGLTextPitchAlignment` enumeration representation of the value.
-*/
-@property (readonly) MGLTextPitchAlignment MGLTextPitchAlignmentValue;
-
-/**
- Creates a new value object containing the given `MGLTextRotationAlignment` enumeration.
-
- @param type The value for the new object.
- @return A new value object that contains the style enumeration type.
-*/
-+ (instancetype)valueWithMGLTextRotationAlignment:(MGLTextRotationAlignment)textRotationAlignment;
-
-/**
- The `MGLTextRotationAlignment` enumeration representation of the value.
-*/
-@property (readonly) MGLTextRotationAlignment MGLTextRotationAlignmentValue;
-
-/**
- Creates a new value object containing the given `MGLTextTransform` enumeration.
-
- @param type The value for the new object.
- @return A new value object that contains the style enumeration type.
-*/
-+ (instancetype)valueWithMGLTextTransform:(MGLTextTransform)textTransform;
-
-/**
- The `MGLTextTransform` enumeration representation of the value.
-*/
-@property (readonly) MGLTextTransform MGLTextTransformValue;
-
-/**
- Creates a new value object containing the given `MGLFillTranslateAnchor` structure.
-
- @param type The value for the new object.
- @return A new value object that contains the style enumeration type.
-*/
-+ (instancetype)valueWithMGLFillTranslateAnchor:(MGLFillTranslateAnchor)fillTranslateAnchor;
-
-/**
- The `MGLFillTranslateAnchor` enumeration representation of the value.
-*/
-@property (readonly) MGLFillTranslateAnchor MGLFillTranslateAnchorValue;
-
-/**
- Creates a new value object containing the given `MGLLineTranslateAnchor` structure.
-
- @param type The value for the new object.
- @return A new value object that contains the style enumeration type.
-*/
-+ (instancetype)valueWithMGLLineTranslateAnchor:(MGLLineTranslateAnchor)lineTranslateAnchor;
-
-/**
- The `MGLLineTranslateAnchor` enumeration representation of the value.
-*/
-@property (readonly) MGLLineTranslateAnchor MGLLineTranslateAnchorValue;
-
-/**
- Creates a new value object containing the given `MGLIconTranslateAnchor` structure.
-
- @param type The value for the new object.
- @return A new value object that contains the style enumeration type.
-*/
-+ (instancetype)valueWithMGLIconTranslateAnchor:(MGLIconTranslateAnchor)iconTranslateAnchor;
-
-/**
- The `MGLIconTranslateAnchor` enumeration representation of the value.
-*/
-@property (readonly) MGLIconTranslateAnchor MGLIconTranslateAnchorValue;
-
-/**
- Creates a new value object containing the given `MGLTextTranslateAnchor` structure.
-
- @param type The value for the new object.
- @return A new value object that contains the style enumeration type.
-*/
-+ (instancetype)valueWithMGLTextTranslateAnchor:(MGLTextTranslateAnchor)textTranslateAnchor;
-
-/**
- The `MGLTextTranslateAnchor` enumeration representation of the value.
-*/
-@property (readonly) MGLTextTranslateAnchor MGLTextTranslateAnchorValue;
-
-/**
- Creates a new value object containing the given `MGLCirclePitchScale` structure.
-
- @param type The value for the new object.
- @return A new value object that contains the style enumeration type.
-*/
-+ (instancetype)valueWithMGLCirclePitchScale:(MGLCirclePitchScale)circlePitchScale;
-
-/**
- The `MGLCirclePitchScale` enumeration representation of the value.
-*/
-@property (readonly) MGLCirclePitchScale MGLCirclePitchScaleValue;
-
-/**
- Creates a new value object containing the given `MGLCircleTranslateAnchor` structure.
-
- @param type The value for the new object.
- @return A new value object that contains the style enumeration type.
-*/
-+ (instancetype)valueWithMGLCircleTranslateAnchor:(MGLCircleTranslateAnchor)circleTranslateAnchor;
-
-/**
- The `MGLCircleTranslateAnchor` enumeration representation of the value.
-*/
-@property (readonly) MGLCircleTranslateAnchor MGLCircleTranslateAnchorValue;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/NSValue+MGLStyleEnumAttributeAdditions.h.ejs b/platform/darwin/src/NSValue+MGLStyleEnumAttributeAdditions.h.ejs
deleted file mode 100644
index c078ac7d02..0000000000
--- a/platform/darwin/src/NSValue+MGLStyleEnumAttributeAdditions.h.ejs
+++ /dev/null
@@ -1,66 +0,0 @@
-// This file is generated.
-// Edit platform/darwin/scripts/generate-style-code.js, then run `make style-code-darwin`.
-
-<%
-const types = locals.types;
-const layoutProperties = locals.layoutProperties;
-const paintProperties = locals.paintProperties;
--%>
-#import <Foundation/Foundation.h>
-
-<% if (types) { -%>
-<% for (const type of types) { -%>
-#import "MGL<%- camelize(type) %>StyleLayer.h"
-<% } -%>
-<% } -%>
-
-NS_ASSUME_NONNULL_BEGIN
-
-/**
- Methods for round-tripping values for Mapbox style layer enumeration values.
-*/
-@interface NSValue (MGLStyleEnumAttributeAdditions)
-
-#pragma mark Working with Style Layer Enumeration Attribute Values
-
-<% if (layoutProperties.length) { -%>
-<% for (const property of layoutProperties) { -%>
-<% if (property.type == "enum") { -%>
-/**
- Creates a new value object containing the given `MGL<%- camelize(property.name) %>` enumeration.
-
- @param type The value for the new object.
- @return A new value object that contains the style enumeration type.
-*/
-+ (instancetype)valueWithMGL<%- camelize(property.name) %>:(MGL<%- camelize(property.name) %>)<%- objCName(property) %>;
-
-/**
- The `MGL<%- camelize(property.name) %>` enumeration representation of the value.
-*/
-@property (readonly) MGL<%- camelize(property.name) %> MGL<%- camelize(property.name) %>Value;
-
-<% } -%>
-<% } -%>
-<% } -%>
-<% if (paintProperties.length) { -%>
-<% for (const property of paintProperties) { -%>
-<% if (property.type == "enum") { -%>
-/**
- Creates a new value object containing the given `MGL<%- camelize(property.name) %>` structure.
-
- @param type The value for the new object.
- @return A new value object that contains the style enumeration type.
-*/
-+ (instancetype)valueWithMGL<%- camelize(property.name) %>:(MGL<%- camelize(property.name) %>)<%- objCName(property) %>;
-
-/**
- The `MGL<%- camelize(property.name) %>` enumeration representation of the value.
-*/
-@property (readonly) MGL<%- camelize(property.name) %> MGL<%- camelize(property.name) %>Value;
-
-<% } -%>
-<% } -%>
-<% } -%>
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/NSValue+MGLStyleEnumAttributeAdditions.mm b/platform/darwin/src/NSValue+MGLStyleEnumAttributeAdditions.mm
deleted file mode 100644
index db91408c5a..0000000000
--- a/platform/darwin/src/NSValue+MGLStyleEnumAttributeAdditions.mm
+++ /dev/null
@@ -1,169 +0,0 @@
-// This file is generated.
-// Edit platform/darwin/scripts/generate-style-code.js, then run `make style-code-darwin`.
-
-#import "NSValue+MGLStyleEnumAttributeAdditions.h"
-
-@implementation NSValue (MGLStyleEnumAttributeAdditions)
-
-+ (NSValue *)valueWithMGLLineCap:(MGLLineCap)lineCap {
- return [NSValue value:&lineCap withObjCType:@encode(MGLLineCap)];
-}
-
-- (MGLLineCap)MGLLineCapValue {
- MGLLineCap value;
- [self getValue:&value];
- return value;
-}
-
-+ (NSValue *)valueWithMGLLineJoin:(MGLLineJoin)lineJoin {
- return [NSValue value:&lineJoin withObjCType:@encode(MGLLineJoin)];
-}
-
-- (MGLLineJoin)MGLLineJoinValue {
- MGLLineJoin value;
- [self getValue:&value];
- return value;
-}
-
-+ (NSValue *)valueWithMGLIconRotationAlignment:(MGLIconRotationAlignment)iconRotationAlignment {
- return [NSValue value:&iconRotationAlignment withObjCType:@encode(MGLIconRotationAlignment)];
-}
-
-- (MGLIconRotationAlignment)MGLIconRotationAlignmentValue {
- MGLIconRotationAlignment value;
- [self getValue:&value];
- return value;
-}
-
-+ (NSValue *)valueWithMGLIconTextFit:(MGLIconTextFit)iconTextFit {
- return [NSValue value:&iconTextFit withObjCType:@encode(MGLIconTextFit)];
-}
-
-- (MGLIconTextFit)MGLIconTextFitValue {
- MGLIconTextFit value;
- [self getValue:&value];
- return value;
-}
-
-+ (NSValue *)valueWithMGLSymbolPlacement:(MGLSymbolPlacement)symbolPlacement {
- return [NSValue value:&symbolPlacement withObjCType:@encode(MGLSymbolPlacement)];
-}
-
-- (MGLSymbolPlacement)MGLSymbolPlacementValue {
- MGLSymbolPlacement value;
- [self getValue:&value];
- return value;
-}
-
-+ (NSValue *)valueWithMGLTextAnchor:(MGLTextAnchor)textAnchor {
- return [NSValue value:&textAnchor withObjCType:@encode(MGLTextAnchor)];
-}
-
-- (MGLTextAnchor)MGLTextAnchorValue {
- MGLTextAnchor value;
- [self getValue:&value];
- return value;
-}
-
-+ (NSValue *)valueWithMGLTextJustification:(MGLTextJustification)textJustification {
- return [NSValue value:&textJustification withObjCType:@encode(MGLTextJustification)];
-}
-
-- (MGLTextJustification)MGLTextJustificationValue {
- MGLTextJustification value;
- [self getValue:&value];
- return value;
-}
-
-+ (NSValue *)valueWithMGLTextPitchAlignment:(MGLTextPitchAlignment)textPitchAlignment {
- return [NSValue value:&textPitchAlignment withObjCType:@encode(MGLTextPitchAlignment)];
-}
-
-- (MGLTextPitchAlignment)MGLTextPitchAlignmentValue {
- MGLTextPitchAlignment value;
- [self getValue:&value];
- return value;
-}
-
-+ (NSValue *)valueWithMGLTextRotationAlignment:(MGLTextRotationAlignment)textRotationAlignment {
- return [NSValue value:&textRotationAlignment withObjCType:@encode(MGLTextRotationAlignment)];
-}
-
-- (MGLTextRotationAlignment)MGLTextRotationAlignmentValue {
- MGLTextRotationAlignment value;
- [self getValue:&value];
- return value;
-}
-
-+ (NSValue *)valueWithMGLTextTransform:(MGLTextTransform)textTransform {
- return [NSValue value:&textTransform withObjCType:@encode(MGLTextTransform)];
-}
-
-- (MGLTextTransform)MGLTextTransformValue {
- MGLTextTransform value;
- [self getValue:&value];
- return value;
-}
-
-+ (NSValue *)valueWithMGLFillTranslateAnchor:(MGLFillTranslateAnchor)fillTranslateAnchor {
-return [NSValue value:&fillTranslateAnchor withObjCType:@encode(MGLFillTranslateAnchor)];
-}
-
-- (MGLFillTranslateAnchor)MGLFillTranslateAnchorValue {
- MGLFillTranslateAnchor value;
- [self getValue:&value];
- return value;
-}
-
-+ (NSValue *)valueWithMGLLineTranslateAnchor:(MGLLineTranslateAnchor)lineTranslateAnchor {
-return [NSValue value:&lineTranslateAnchor withObjCType:@encode(MGLLineTranslateAnchor)];
-}
-
-- (MGLLineTranslateAnchor)MGLLineTranslateAnchorValue {
- MGLLineTranslateAnchor value;
- [self getValue:&value];
- return value;
-}
-
-+ (NSValue *)valueWithMGLIconTranslateAnchor:(MGLIconTranslateAnchor)iconTranslateAnchor {
-return [NSValue value:&iconTranslateAnchor withObjCType:@encode(MGLIconTranslateAnchor)];
-}
-
-- (MGLIconTranslateAnchor)MGLIconTranslateAnchorValue {
- MGLIconTranslateAnchor value;
- [self getValue:&value];
- return value;
-}
-
-+ (NSValue *)valueWithMGLTextTranslateAnchor:(MGLTextTranslateAnchor)textTranslateAnchor {
-return [NSValue value:&textTranslateAnchor withObjCType:@encode(MGLTextTranslateAnchor)];
-}
-
-- (MGLTextTranslateAnchor)MGLTextTranslateAnchorValue {
- MGLTextTranslateAnchor value;
- [self getValue:&value];
- return value;
-}
-
-+ (NSValue *)valueWithMGLCirclePitchScale:(MGLCirclePitchScale)circlePitchScale {
-return [NSValue value:&circlePitchScale withObjCType:@encode(MGLCirclePitchScale)];
-}
-
-- (MGLCirclePitchScale)MGLCirclePitchScaleValue {
- MGLCirclePitchScale value;
- [self getValue:&value];
- return value;
-}
-
-+ (NSValue *)valueWithMGLCircleTranslateAnchor:(MGLCircleTranslateAnchor)circleTranslateAnchor {
-return [NSValue value:&circleTranslateAnchor withObjCType:@encode(MGLCircleTranslateAnchor)];
-}
-
-- (MGLCircleTranslateAnchor)MGLCircleTranslateAnchorValue {
- MGLCircleTranslateAnchor value;
- [self getValue:&value];
- return value;
-}
-
-
-@end
diff --git a/platform/darwin/src/NSValue+MGLStyleEnumAttributeAdditions.mm.ejs b/platform/darwin/src/NSValue+MGLStyleEnumAttributeAdditions.mm.ejs
deleted file mode 100644
index fab278aac8..0000000000
--- a/platform/darwin/src/NSValue+MGLStyleEnumAttributeAdditions.mm.ejs
+++ /dev/null
@@ -1,46 +0,0 @@
-// This file is generated.
-// Edit platform/darwin/scripts/generate-style-code.js, then run `make style-code-darwin`.
-<%
-const type = locals.type;
-const layoutProperties = locals.layoutProperties;
-const paintProperties = locals.paintProperties;
--%>
-
-#import "NSValue+MGLStyleEnumAttributeAdditions.h"
-
-@implementation NSValue (MGLStyleEnumAttributeAdditions)
-
-<% if (layoutProperties.length) { -%>
-<% for (const property of layoutProperties) { -%>
-<% if (property.type == "enum") { -%>
-+ (NSValue *)valueWithMGL<%- camelize(property.name) %>:(MGL<%- camelize(property.name) %>)<%- objCName(property) %> {
- return [NSValue value:&<%- objCName(property) %> withObjCType:@encode(MGL<%- camelize(property.name) %>)];
-}
-
-- (MGL<%- camelize(property.name) %>)MGL<%- camelize(property.name) %>Value {
- MGL<%- camelize(property.name) %> value;
- [self getValue:&value];
- return value;
-}
-
-<% } -%>
-<% } -%>
-<% } -%>
-<% if (paintProperties.length) { -%>
-<% for (const property of paintProperties) { -%>
-<% if (property.type == "enum") { -%>
-+ (NSValue *)valueWithMGL<%- camelize(property.name) %>:(MGL<%- camelize(property.name) %>)<%- objCName(property) %> {
-return [NSValue value:&<%- objCName(property) %> withObjCType:@encode(MGL<%- camelize(property.name) %>)];
-}
-
-- (MGL<%- camelize(property.name) %>)MGL<%- camelize(property.name) %>Value {
- MGL<%- camelize(property.name) %> value;
- [self getValue:&value];
- return value;
-}
-
-<% } -%>
-<% } -%>
-<% } -%>
-
-@end
diff --git a/platform/darwin/test/MGLBackgroundStyleLayerTests.m b/platform/darwin/test/MGLBackgroundStyleLayerTests.m
deleted file mode 100644
index 934021d6b8..0000000000
--- a/platform/darwin/test/MGLBackgroundStyleLayerTests.m
+++ /dev/null
@@ -1,44 +0,0 @@
-// This file is generated.
-// Edit platform/darwin/scripts/generate-style-code.js, then run `make style-code-darwin`.
-
-#import "MGLStyleLayerTests.h"
-
-@interface MGLBackgroundLayerTests : MGLStyleLayerTests
-@end
-
-@implementation MGLBackgroundLayerTests
-
-+ (NSString *)layerType {
- return @"background";
-}
-
-- (void)testBackgroundLayer {
- MGLBackgroundStyleLayer *layer = [[MGLBackgroundStyleLayer alloc] initWithIdentifier:@"layerID"];
- [self.mapView.style addLayer:layer];
-
- layer.backgroundColor = [MGLRuntimeStylingHelper testColor];
- layer.backgroundOpacity = [MGLRuntimeStylingHelper testNumber];
- layer.backgroundPattern = [MGLRuntimeStylingHelper testString];
-
- MGLBackgroundStyleLayer *gLayer = (MGLBackgroundStyleLayer *)[self.mapView.style layerWithIdentifier:@"layerID"];
- XCTAssertTrue([gLayer isKindOfClass:[MGLBackgroundStyleLayer class]]);
- XCTAssertEqualObjects(gLayer.backgroundColor, [MGLRuntimeStylingHelper testColor]);
- XCTAssertEqualObjects(gLayer.backgroundOpacity, [MGLRuntimeStylingHelper testNumber]);
- XCTAssertEqualObjects(gLayer.backgroundPattern, [MGLRuntimeStylingHelper testString]);
-
- layer.backgroundColor = [MGLRuntimeStylingHelper testColorFunction];
- layer.backgroundOpacity = [MGLRuntimeStylingHelper testNumberFunction];
- layer.backgroundPattern = [MGLRuntimeStylingHelper testStringFunction];
-
- XCTAssertEqualObjects(gLayer.backgroundColor, [MGLRuntimeStylingHelper testColorFunction]);
- XCTAssertEqualObjects(gLayer.backgroundOpacity, [MGLRuntimeStylingHelper testNumberFunction]);
- XCTAssertEqualObjects(gLayer.backgroundPattern, [MGLRuntimeStylingHelper testStringFunction]);
-}
-
-- (void)testPropertyNames {
- [self testPropertyName:@"background-color" isBoolean:NO];
- [self testPropertyName:@"background-opacity" isBoolean:NO];
- [self testPropertyName:@"background-pattern" isBoolean:NO];
-}
-
-@end
diff --git a/platform/darwin/test/MGLBackgroundStyleLayerTests.mm b/platform/darwin/test/MGLBackgroundStyleLayerTests.mm
new file mode 100644
index 0000000000..60a332a5e7
--- /dev/null
+++ b/platform/darwin/test/MGLBackgroundStyleLayerTests.mm
@@ -0,0 +1,134 @@
+// This file is generated.
+// Edit platform/darwin/scripts/generate-style-code.js, then run `make style-code-darwin`.
+
+#import "MGLStyleLayerTests.h"
+
+#import "MGLStyleLayer_Private.h"
+
+#include <mbgl/style/layers/background_layer.hpp>
+
+@interface MGLBackgroundLayerTests : MGLStyleLayerTests
+@end
+
+@implementation MGLBackgroundLayerTests
+
++ (NSString *)layerType {
+ return @"background";
+}
+
+- (void)testProperties {
+ MGLBackgroundStyleLayer *layer = [[MGLBackgroundStyleLayer alloc] initWithIdentifier:@"layerID"];
+ XCTAssertNotEqual(layer.rawLayer, nullptr);
+ XCTAssertTrue(layer.rawLayer->is<mbgl::style::BackgroundLayer>());
+ auto rawLayer = layer.rawLayer->as<mbgl::style::BackgroundLayer>();
+
+ // background-color
+ {
+ XCTAssertTrue(rawLayer->getBackgroundColor().isUndefined(),
+ @"background-color should be unset initially.");
+ MGLStyleValue<MGLColor *> *defaultStyleValue = layer.backgroundColor;
+
+ MGLStyleValue<MGLColor *> *styleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]];
+ layer.backgroundColor = styleValue;
+ mbgl::style::PropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } };
+ XCTAssertEqual(rawLayer->getBackgroundColor(), propertyValue,
+ @"Setting backgroundColor to a constant value should update background-color.");
+ XCTAssertEqualObjects(layer.backgroundColor, styleValue,
+ @"backgroundColor should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<MGLColor *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.backgroundColor = styleValue;
+ propertyValue = { mbgl::style::Function<mbgl::Color> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getBackgroundColor(), propertyValue,
+ @"Setting backgroundColor to a function should update background-color.");
+ XCTAssertEqualObjects(layer.backgroundColor, styleValue,
+ @"backgroundColor should round-trip functions.");
+
+ layer.backgroundColor = nil;
+ XCTAssertTrue(rawLayer->getBackgroundColor().isUndefined(),
+ @"Unsetting backgroundColor should return background-color to the default value.");
+ XCTAssertEqualObjects(layer.backgroundColor, defaultStyleValue,
+ @"backgroundColor should return the default value after being unset.");
+ }
+
+ // background-opacity
+ {
+ XCTAssertTrue(rawLayer->getBackgroundOpacity().isUndefined(),
+ @"background-opacity should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.backgroundOpacity;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.backgroundOpacity = styleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getBackgroundOpacity(), propertyValue,
+ @"Setting backgroundOpacity to a constant value should update background-opacity.");
+ XCTAssertEqualObjects(layer.backgroundOpacity, styleValue,
+ @"backgroundOpacity should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.backgroundOpacity = styleValue;
+ propertyValue = { mbgl::style::Function<float> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getBackgroundOpacity(), propertyValue,
+ @"Setting backgroundOpacity to a function should update background-opacity.");
+ XCTAssertEqualObjects(layer.backgroundOpacity, styleValue,
+ @"backgroundOpacity should round-trip functions.");
+
+ layer.backgroundOpacity = nil;
+ XCTAssertTrue(rawLayer->getBackgroundOpacity().isUndefined(),
+ @"Unsetting backgroundOpacity should return background-opacity to the default value.");
+ XCTAssertEqualObjects(layer.backgroundOpacity, defaultStyleValue,
+ @"backgroundOpacity should return the default value after being unset.");
+ }
+
+ // background-pattern
+ {
+ XCTAssertTrue(rawLayer->getBackgroundPattern().isUndefined(),
+ @"background-pattern should be unset initially.");
+ MGLStyleValue<NSString *> *defaultStyleValue = layer.backgroundPattern;
+
+ MGLStyleValue<NSString *> *styleValue = [MGLStyleValue<NSString *> valueWithRawValue:@"Background Pattern"];
+ layer.backgroundPattern = styleValue;
+ mbgl::style::PropertyValue<std::string> propertyValue = { "Background Pattern" };
+ XCTAssertEqual(rawLayer->getBackgroundPattern(), propertyValue,
+ @"Setting backgroundPattern to a constant value should update background-pattern.");
+ XCTAssertEqualObjects(layer.backgroundPattern, styleValue,
+ @"backgroundPattern should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSString *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.backgroundPattern = styleValue;
+ propertyValue = { mbgl::style::Function<std::string> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getBackgroundPattern(), propertyValue,
+ @"Setting backgroundPattern to a function should update background-pattern.");
+ XCTAssertEqualObjects(layer.backgroundPattern, styleValue,
+ @"backgroundPattern should round-trip functions.");
+
+ layer.backgroundPattern = nil;
+ XCTAssertTrue(rawLayer->getBackgroundPattern().isUndefined(),
+ @"Unsetting backgroundPattern should return background-pattern to the default value.");
+ XCTAssertEqualObjects(layer.backgroundPattern, defaultStyleValue,
+ @"backgroundPattern should return the default value after being unset.");
+ }
+}
+
+- (void)testPropertyNames {
+ [self testPropertyName:@"background-color" isBoolean:NO];
+ [self testPropertyName:@"background-opacity" isBoolean:NO];
+ [self testPropertyName:@"background-pattern" isBoolean:NO];
+}
+
+@end
diff --git a/platform/darwin/test/MGLCircleStyleLayerTests.m b/platform/darwin/test/MGLCircleStyleLayerTests.m
deleted file mode 100644
index ce17d93557..0000000000
--- a/platform/darwin/test/MGLCircleStyleLayerTests.m
+++ /dev/null
@@ -1,85 +0,0 @@
-// This file is generated.
-// Edit platform/darwin/scripts/generate-style-code.js, then run `make style-code-darwin`.
-
-#import "MGLStyleLayerTests.h"
-
-@interface MGLCircleLayerTests : MGLStyleLayerTests
-@end
-
-@implementation MGLCircleLayerTests
-
-+ (NSString *)layerType {
- return @"circle";
-}
-
-- (void)testCircleLayer {
- NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"amsterdam" ofType:@"geojson"];
- NSURL *url = [NSURL fileURLWithPath:filePath];
- MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"sourceID" URL:url options:nil];
- [self.mapView.style addSource:source];
- MGLCircleStyleLayer *layer = [[MGLCircleStyleLayer alloc] initWithIdentifier:@"layerID" source:source];
- [self.mapView.style addLayer:layer];
-
- layer.circleBlur = [MGLRuntimeStylingHelper testNumber];
- layer.circleColor = [MGLRuntimeStylingHelper testColor];
- layer.circleOpacity = [MGLRuntimeStylingHelper testNumber];
- layer.circlePitchScale = [MGLRuntimeStylingHelper testEnum:MGLCirclePitchScaleViewport type:@encode(MGLCirclePitchScale)];
- layer.circleRadius = [MGLRuntimeStylingHelper testNumber];
- layer.circleStrokeColor = [MGLRuntimeStylingHelper testColor];
- layer.circleStrokeOpacity = [MGLRuntimeStylingHelper testNumber];
- layer.circleStrokeWidth = [MGLRuntimeStylingHelper testNumber];
- layer.circleTranslate = [MGLRuntimeStylingHelper testOffset];
- layer.circleTranslateAnchor = [MGLRuntimeStylingHelper testEnum:MGLCircleTranslateAnchorViewport type:@encode(MGLCircleTranslateAnchor)];
-
- MGLCircleStyleLayer *gLayer = (MGLCircleStyleLayer *)[self.mapView.style layerWithIdentifier:@"layerID"];
- XCTAssertTrue([gLayer isKindOfClass:[MGLCircleStyleLayer class]]);
- XCTAssertEqualObjects(gLayer.circleBlur, [MGLRuntimeStylingHelper testNumber]);
- XCTAssertEqualObjects(gLayer.circleColor, [MGLRuntimeStylingHelper testColor]);
- XCTAssertEqualObjects(gLayer.circleOpacity, [MGLRuntimeStylingHelper testNumber]);
- XCTAssert([gLayer.circlePitchScale isKindOfClass:[MGLStyleConstantValue class]]);
- XCTAssertEqualObjects(gLayer.circlePitchScale, [MGLRuntimeStylingHelper testEnum:MGLCirclePitchScaleViewport type:@encode(MGLCirclePitchScale)]);
- XCTAssertEqualObjects(gLayer.circleRadius, [MGLRuntimeStylingHelper testNumber]);
- XCTAssertEqualObjects(gLayer.circleStrokeColor, [MGLRuntimeStylingHelper testColor]);
- XCTAssertEqualObjects(gLayer.circleStrokeOpacity, [MGLRuntimeStylingHelper testNumber]);
- XCTAssertEqualObjects(gLayer.circleStrokeWidth, [MGLRuntimeStylingHelper testNumber]);
- XCTAssertEqualObjects(gLayer.circleTranslate, [MGLRuntimeStylingHelper testOffset]);
- XCTAssert([gLayer.circleTranslateAnchor isKindOfClass:[MGLStyleConstantValue class]]);
- XCTAssertEqualObjects(gLayer.circleTranslateAnchor, [MGLRuntimeStylingHelper testEnum:MGLCircleTranslateAnchorViewport type:@encode(MGLCircleTranslateAnchor)]);
-
- layer.circleBlur = [MGLRuntimeStylingHelper testNumberFunction];
- layer.circleColor = [MGLRuntimeStylingHelper testColorFunction];
- layer.circleOpacity = [MGLRuntimeStylingHelper testNumberFunction];
- layer.circlePitchScale = [MGLRuntimeStylingHelper testEnumFunction:MGLCirclePitchScaleViewport type:@encode(MGLCirclePitchScale)];
- layer.circleRadius = [MGLRuntimeStylingHelper testNumberFunction];
- layer.circleStrokeColor = [MGLRuntimeStylingHelper testColorFunction];
- layer.circleStrokeOpacity = [MGLRuntimeStylingHelper testNumberFunction];
- layer.circleStrokeWidth = [MGLRuntimeStylingHelper testNumberFunction];
- layer.circleTranslate = [MGLRuntimeStylingHelper testOffsetFunction];
- layer.circleTranslateAnchor = [MGLRuntimeStylingHelper testEnumFunction:MGLCircleTranslateAnchorViewport type:@encode(MGLCircleTranslateAnchor)];
-
- XCTAssertEqualObjects(gLayer.circleBlur, [MGLRuntimeStylingHelper testNumberFunction]);
- XCTAssertEqualObjects(gLayer.circleColor, [MGLRuntimeStylingHelper testColorFunction]);
- XCTAssertEqualObjects(gLayer.circleOpacity, [MGLRuntimeStylingHelper testNumberFunction]);
- XCTAssertEqualObjects(gLayer.circlePitchScale, [MGLRuntimeStylingHelper testEnumFunction:MGLCirclePitchScaleViewport type:@encode(MGLCirclePitchScale)]);
- XCTAssertEqualObjects(gLayer.circleRadius, [MGLRuntimeStylingHelper testNumberFunction]);
- XCTAssertEqualObjects(gLayer.circleStrokeColor, [MGLRuntimeStylingHelper testColorFunction]);
- XCTAssertEqualObjects(gLayer.circleStrokeOpacity, [MGLRuntimeStylingHelper testNumberFunction]);
- XCTAssertEqualObjects(gLayer.circleStrokeWidth, [MGLRuntimeStylingHelper testNumberFunction]);
- XCTAssertEqualObjects(gLayer.circleTranslate, [MGLRuntimeStylingHelper testOffsetFunction]);
- XCTAssertEqualObjects(gLayer.circleTranslateAnchor, [MGLRuntimeStylingHelper testEnumFunction:MGLCircleTranslateAnchorViewport type:@encode(MGLCircleTranslateAnchor)]);
-}
-
-- (void)testPropertyNames {
- [self testPropertyName:@"circle-blur" isBoolean:NO];
- [self testPropertyName:@"circle-color" isBoolean:NO];
- [self testPropertyName:@"circle-opacity" isBoolean:NO];
- [self testPropertyName:@"circle-pitch-scale" isBoolean:NO];
- [self testPropertyName:@"circle-radius" isBoolean:NO];
- [self testPropertyName:@"circle-stroke-color" isBoolean:NO];
- [self testPropertyName:@"circle-stroke-opacity" isBoolean:NO];
- [self testPropertyName:@"circle-stroke-width" isBoolean:NO];
- [self testPropertyName:@"circle-translate" isBoolean:NO];
- [self testPropertyName:@"circle-translate-anchor" isBoolean:NO];
-}
-
-@end
diff --git a/platform/darwin/test/MGLCircleStyleLayerTests.mm b/platform/darwin/test/MGLCircleStyleLayerTests.mm
new file mode 100644
index 0000000000..35e29b31d5
--- /dev/null
+++ b/platform/darwin/test/MGLCircleStyleLayerTests.mm
@@ -0,0 +1,413 @@
+// This file is generated.
+// Edit platform/darwin/scripts/generate-style-code.js, then run `make style-code-darwin`.
+
+#import "MGLStyleLayerTests.h"
+
+#import "MGLStyleLayer_Private.h"
+
+#include <mbgl/style/layers/circle_layer.hpp>
+
+@interface MGLCircleLayerTests : MGLStyleLayerTests
+@end
+
+@implementation MGLCircleLayerTests
+
++ (NSString *)layerType {
+ return @"circle";
+}
+
+- (void)testPredicates {
+ MGLPointFeature *feature = [[MGLPointFeature alloc] init];
+ MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"sourceID" shape:feature options:nil];
+ MGLCircleStyleLayer *layer = [[MGLCircleStyleLayer alloc] initWithIdentifier:@"layerID" source:source];
+
+ XCTAssertNil(layer.sourceLayerIdentifier);
+ layer.sourceLayerIdentifier = @"layerID";
+ XCTAssertEqualObjects(layer.sourceLayerIdentifier, @"layerID");
+ layer.sourceLayerIdentifier = nil;
+ XCTAssertNil(layer.sourceLayerIdentifier);
+
+ XCTAssertNil(layer.predicate);
+ layer.predicate = [NSPredicate predicateWithValue:NO];
+ XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithValue:NO]);
+ layer.predicate = nil;
+ XCTAssertNil(layer.predicate);
+}
+
+- (void)testProperties {
+ MGLPointFeature *feature = [[MGLPointFeature alloc] init];
+ MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"sourceID" shape:feature options:nil];
+
+ MGLCircleStyleLayer *layer = [[MGLCircleStyleLayer alloc] initWithIdentifier:@"layerID" source:source];
+ XCTAssertNotEqual(layer.rawLayer, nullptr);
+ XCTAssertTrue(layer.rawLayer->is<mbgl::style::CircleLayer>());
+ auto rawLayer = layer.rawLayer->as<mbgl::style::CircleLayer>();
+
+ // circle-blur
+ {
+ XCTAssertTrue(rawLayer->getCircleBlur().isUndefined(),
+ @"circle-blur should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.circleBlur;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.circleBlur = styleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getCircleBlur(), propertyValue,
+ @"Setting circleBlur to a constant value should update circle-blur.");
+ XCTAssertEqualObjects(layer.circleBlur, styleValue,
+ @"circleBlur should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.circleBlur = styleValue;
+ propertyValue = { mbgl::style::Function<float> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getCircleBlur(), propertyValue,
+ @"Setting circleBlur to a function should update circle-blur.");
+ XCTAssertEqualObjects(layer.circleBlur, styleValue,
+ @"circleBlur should round-trip functions.");
+
+ layer.circleBlur = nil;
+ XCTAssertTrue(rawLayer->getCircleBlur().isUndefined(),
+ @"Unsetting circleBlur should return circle-blur to the default value.");
+ XCTAssertEqualObjects(layer.circleBlur, defaultStyleValue,
+ @"circleBlur should return the default value after being unset.");
+ }
+
+ // circle-color
+ {
+ XCTAssertTrue(rawLayer->getCircleColor().isUndefined(),
+ @"circle-color should be unset initially.");
+ MGLStyleValue<MGLColor *> *defaultStyleValue = layer.circleColor;
+
+ MGLStyleValue<MGLColor *> *styleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]];
+ layer.circleColor = styleValue;
+ mbgl::style::PropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } };
+ XCTAssertEqual(rawLayer->getCircleColor(), propertyValue,
+ @"Setting circleColor to a constant value should update circle-color.");
+ XCTAssertEqualObjects(layer.circleColor, styleValue,
+ @"circleColor should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<MGLColor *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.circleColor = styleValue;
+ propertyValue = { mbgl::style::Function<mbgl::Color> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getCircleColor(), propertyValue,
+ @"Setting circleColor to a function should update circle-color.");
+ XCTAssertEqualObjects(layer.circleColor, styleValue,
+ @"circleColor should round-trip functions.");
+
+ layer.circleColor = nil;
+ XCTAssertTrue(rawLayer->getCircleColor().isUndefined(),
+ @"Unsetting circleColor should return circle-color to the default value.");
+ XCTAssertEqualObjects(layer.circleColor, defaultStyleValue,
+ @"circleColor should return the default value after being unset.");
+ }
+
+ // circle-opacity
+ {
+ XCTAssertTrue(rawLayer->getCircleOpacity().isUndefined(),
+ @"circle-opacity should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.circleOpacity;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.circleOpacity = styleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getCircleOpacity(), propertyValue,
+ @"Setting circleOpacity to a constant value should update circle-opacity.");
+ XCTAssertEqualObjects(layer.circleOpacity, styleValue,
+ @"circleOpacity should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.circleOpacity = styleValue;
+ propertyValue = { mbgl::style::Function<float> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getCircleOpacity(), propertyValue,
+ @"Setting circleOpacity to a function should update circle-opacity.");
+ XCTAssertEqualObjects(layer.circleOpacity, styleValue,
+ @"circleOpacity should round-trip functions.");
+
+ layer.circleOpacity = nil;
+ XCTAssertTrue(rawLayer->getCircleOpacity().isUndefined(),
+ @"Unsetting circleOpacity should return circle-opacity to the default value.");
+ XCTAssertEqualObjects(layer.circleOpacity, defaultStyleValue,
+ @"circleOpacity should return the default value after being unset.");
+ }
+
+ // circle-radius
+ {
+ XCTAssertTrue(rawLayer->getCircleRadius().isUndefined(),
+ @"circle-radius should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.circleRadius;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.circleRadius = styleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getCircleRadius(), propertyValue,
+ @"Setting circleRadius to a constant value should update circle-radius.");
+ XCTAssertEqualObjects(layer.circleRadius, styleValue,
+ @"circleRadius should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.circleRadius = styleValue;
+ propertyValue = { mbgl::style::Function<float> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getCircleRadius(), propertyValue,
+ @"Setting circleRadius to a function should update circle-radius.");
+ XCTAssertEqualObjects(layer.circleRadius, styleValue,
+ @"circleRadius should round-trip functions.");
+
+ layer.circleRadius = nil;
+ XCTAssertTrue(rawLayer->getCircleRadius().isUndefined(),
+ @"Unsetting circleRadius should return circle-radius to the default value.");
+ XCTAssertEqualObjects(layer.circleRadius, defaultStyleValue,
+ @"circleRadius should return the default value after being unset.");
+ }
+
+ // circle-pitch-scale
+ {
+ XCTAssertTrue(rawLayer->getCirclePitchScale().isUndefined(),
+ @"circle-pitch-scale should be unset initially.");
+ MGLStyleValue<NSValue *> *defaultStyleValue = layer.circleScaleAlignment;
+
+ MGLStyleValue<NSValue *> *styleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLCircleScaleAlignment:MGLCircleScaleAlignmentViewport]];
+ layer.circleScaleAlignment = styleValue;
+ mbgl::style::PropertyValue<mbgl::style::CirclePitchScaleType> propertyValue = { mbgl::style::CirclePitchScaleType::Viewport };
+ XCTAssertEqual(rawLayer->getCirclePitchScale(), propertyValue,
+ @"Setting circleScaleAlignment to a constant value should update circle-pitch-scale.");
+ XCTAssertEqualObjects(layer.circleScaleAlignment, styleValue,
+ @"circleScaleAlignment should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSValue *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.circleScaleAlignment = styleValue;
+ propertyValue = { mbgl::style::Function<mbgl::style::CirclePitchScaleType> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getCirclePitchScale(), propertyValue,
+ @"Setting circleScaleAlignment to a function should update circle-pitch-scale.");
+ XCTAssertEqualObjects(layer.circleScaleAlignment, styleValue,
+ @"circleScaleAlignment should round-trip functions.");
+
+ layer.circleScaleAlignment = nil;
+ XCTAssertTrue(rawLayer->getCirclePitchScale().isUndefined(),
+ @"Unsetting circleScaleAlignment should return circle-pitch-scale to the default value.");
+ XCTAssertEqualObjects(layer.circleScaleAlignment, defaultStyleValue,
+ @"circleScaleAlignment should return the default value after being unset.");
+ }
+
+ // circle-stroke-color
+ {
+ XCTAssertTrue(rawLayer->getCircleStrokeColor().isUndefined(),
+ @"circle-stroke-color should be unset initially.");
+ MGLStyleValue<MGLColor *> *defaultStyleValue = layer.circleStrokeColor;
+
+ MGLStyleValue<MGLColor *> *styleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]];
+ layer.circleStrokeColor = styleValue;
+ mbgl::style::PropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } };
+ XCTAssertEqual(rawLayer->getCircleStrokeColor(), propertyValue,
+ @"Setting circleStrokeColor to a constant value should update circle-stroke-color.");
+ XCTAssertEqualObjects(layer.circleStrokeColor, styleValue,
+ @"circleStrokeColor should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<MGLColor *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.circleStrokeColor = styleValue;
+ propertyValue = { mbgl::style::Function<mbgl::Color> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getCircleStrokeColor(), propertyValue,
+ @"Setting circleStrokeColor to a function should update circle-stroke-color.");
+ XCTAssertEqualObjects(layer.circleStrokeColor, styleValue,
+ @"circleStrokeColor should round-trip functions.");
+
+ layer.circleStrokeColor = nil;
+ XCTAssertTrue(rawLayer->getCircleStrokeColor().isUndefined(),
+ @"Unsetting circleStrokeColor should return circle-stroke-color to the default value.");
+ XCTAssertEqualObjects(layer.circleStrokeColor, defaultStyleValue,
+ @"circleStrokeColor should return the default value after being unset.");
+ }
+
+ // circle-stroke-opacity
+ {
+ XCTAssertTrue(rawLayer->getCircleStrokeOpacity().isUndefined(),
+ @"circle-stroke-opacity should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.circleStrokeOpacity;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.circleStrokeOpacity = styleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getCircleStrokeOpacity(), propertyValue,
+ @"Setting circleStrokeOpacity to a constant value should update circle-stroke-opacity.");
+ XCTAssertEqualObjects(layer.circleStrokeOpacity, styleValue,
+ @"circleStrokeOpacity should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.circleStrokeOpacity = styleValue;
+ propertyValue = { mbgl::style::Function<float> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getCircleStrokeOpacity(), propertyValue,
+ @"Setting circleStrokeOpacity to a function should update circle-stroke-opacity.");
+ XCTAssertEqualObjects(layer.circleStrokeOpacity, styleValue,
+ @"circleStrokeOpacity should round-trip functions.");
+
+ layer.circleStrokeOpacity = nil;
+ XCTAssertTrue(rawLayer->getCircleStrokeOpacity().isUndefined(),
+ @"Unsetting circleStrokeOpacity should return circle-stroke-opacity to the default value.");
+ XCTAssertEqualObjects(layer.circleStrokeOpacity, defaultStyleValue,
+ @"circleStrokeOpacity should return the default value after being unset.");
+ }
+
+ // circle-stroke-width
+ {
+ XCTAssertTrue(rawLayer->getCircleStrokeWidth().isUndefined(),
+ @"circle-stroke-width should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.circleStrokeWidth;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.circleStrokeWidth = styleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getCircleStrokeWidth(), propertyValue,
+ @"Setting circleStrokeWidth to a constant value should update circle-stroke-width.");
+ XCTAssertEqualObjects(layer.circleStrokeWidth, styleValue,
+ @"circleStrokeWidth should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.circleStrokeWidth = styleValue;
+ propertyValue = { mbgl::style::Function<float> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getCircleStrokeWidth(), propertyValue,
+ @"Setting circleStrokeWidth to a function should update circle-stroke-width.");
+ XCTAssertEqualObjects(layer.circleStrokeWidth, styleValue,
+ @"circleStrokeWidth should round-trip functions.");
+
+ layer.circleStrokeWidth = nil;
+ XCTAssertTrue(rawLayer->getCircleStrokeWidth().isUndefined(),
+ @"Unsetting circleStrokeWidth should return circle-stroke-width to the default value.");
+ XCTAssertEqualObjects(layer.circleStrokeWidth, defaultStyleValue,
+ @"circleStrokeWidth should return the default value after being unset.");
+ }
+
+ // circle-translate
+ {
+ XCTAssertTrue(rawLayer->getCircleTranslate().isUndefined(),
+ @"circle-translate should be unset initially.");
+ MGLStyleValue<NSValue *> *defaultStyleValue = layer.circleTranslation;
+
+ MGLStyleValue<NSValue *> *styleValue = [MGLStyleValue<NSValue *> valueWithRawValue:
+#if TARGET_OS_IPHONE
+ [NSValue valueWithCGVector:CGVectorMake(1, 1)]
+#else
+ [NSValue valueWithMGLVector:CGVectorMake(1, -1)]
+#endif
+ ];
+ layer.circleTranslation = styleValue;
+ mbgl::style::PropertyValue<std::array<float, 2>> propertyValue = { { 1, 1 } };
+ XCTAssertEqual(rawLayer->getCircleTranslate(), propertyValue,
+ @"Setting circleTranslation to a constant value should update circle-translate.");
+ XCTAssertEqualObjects(layer.circleTranslation, styleValue,
+ @"circleTranslation should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSValue *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.circleTranslation = styleValue;
+ propertyValue = { mbgl::style::Function<std::array<float, 2>> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getCircleTranslate(), propertyValue,
+ @"Setting circleTranslation to a function should update circle-translate.");
+ XCTAssertEqualObjects(layer.circleTranslation, styleValue,
+ @"circleTranslation should round-trip functions.");
+
+ layer.circleTranslation = nil;
+ XCTAssertTrue(rawLayer->getCircleTranslate().isUndefined(),
+ @"Unsetting circleTranslation should return circle-translate to the default value.");
+ XCTAssertEqualObjects(layer.circleTranslation, defaultStyleValue,
+ @"circleTranslation should return the default value after being unset.");
+ }
+
+ // circle-translate-anchor
+ {
+ XCTAssertTrue(rawLayer->getCircleTranslateAnchor().isUndefined(),
+ @"circle-translate-anchor should be unset initially.");
+ MGLStyleValue<NSValue *> *defaultStyleValue = layer.circleTranslationAnchor;
+
+ MGLStyleValue<NSValue *> *styleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLCircleTranslationAnchor:MGLCircleTranslationAnchorViewport]];
+ layer.circleTranslationAnchor = styleValue;
+ mbgl::style::PropertyValue<mbgl::style::TranslateAnchorType> propertyValue = { mbgl::style::TranslateAnchorType::Viewport };
+ XCTAssertEqual(rawLayer->getCircleTranslateAnchor(), propertyValue,
+ @"Setting circleTranslationAnchor to a constant value should update circle-translate-anchor.");
+ XCTAssertEqualObjects(layer.circleTranslationAnchor, styleValue,
+ @"circleTranslationAnchor should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSValue *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.circleTranslationAnchor = styleValue;
+ propertyValue = { mbgl::style::Function<mbgl::style::TranslateAnchorType> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getCircleTranslateAnchor(), propertyValue,
+ @"Setting circleTranslationAnchor to a function should update circle-translate-anchor.");
+ XCTAssertEqualObjects(layer.circleTranslationAnchor, styleValue,
+ @"circleTranslationAnchor should round-trip functions.");
+
+ layer.circleTranslationAnchor = nil;
+ XCTAssertTrue(rawLayer->getCircleTranslateAnchor().isUndefined(),
+ @"Unsetting circleTranslationAnchor should return circle-translate-anchor to the default value.");
+ XCTAssertEqualObjects(layer.circleTranslationAnchor, defaultStyleValue,
+ @"circleTranslationAnchor should return the default value after being unset.");
+ }
+}
+
+- (void)testPropertyNames {
+ [self testPropertyName:@"circle-blur" isBoolean:NO];
+ [self testPropertyName:@"circle-color" isBoolean:NO];
+ [self testPropertyName:@"circle-opacity" isBoolean:NO];
+ [self testPropertyName:@"circle-radius" isBoolean:NO];
+ [self testPropertyName:@"circle-scale-alignment" isBoolean:NO];
+ [self testPropertyName:@"circle-stroke-color" isBoolean:NO];
+ [self testPropertyName:@"circle-stroke-opacity" isBoolean:NO];
+ [self testPropertyName:@"circle-stroke-width" isBoolean:NO];
+ [self testPropertyName:@"circle-translation" isBoolean:NO];
+ [self testPropertyName:@"circle-translation-anchor" isBoolean:NO];
+}
+
+- (void)testValueAdditions {
+ XCTAssertEqual([NSValue valueWithMGLCircleScaleAlignment:MGLCircleScaleAlignmentMap].MGLCircleScaleAlignmentValue, MGLCircleScaleAlignmentMap);
+ XCTAssertEqual([NSValue valueWithMGLCircleScaleAlignment:MGLCircleScaleAlignmentViewport].MGLCircleScaleAlignmentValue, MGLCircleScaleAlignmentViewport);
+ XCTAssertEqual([NSValue valueWithMGLCircleTranslationAnchor:MGLCircleTranslationAnchorMap].MGLCircleTranslationAnchorValue, MGLCircleTranslationAnchorMap);
+ XCTAssertEqual([NSValue valueWithMGLCircleTranslationAnchor:MGLCircleTranslationAnchorViewport].MGLCircleTranslationAnchorValue, MGLCircleTranslationAnchorViewport);
+}
+
+@end
diff --git a/platform/darwin/test/MGLCodingTests.m b/platform/darwin/test/MGLCodingTests.m
new file mode 100644
index 0000000000..b9b299d50f
--- /dev/null
+++ b/platform/darwin/test/MGLCodingTests.m
@@ -0,0 +1,469 @@
+#import <Mapbox/Mapbox.h>
+#import <XCTest/XCTest.h>
+
+#if TARGET_OS_IPHONE
+#import "MGLUserLocation_Private.h"
+#endif
+
+@interface MGLCodingTests : XCTestCase
+@end
+
+@implementation MGLCodingTests
+
+- (NSString *)temporaryFilePathForClass:(Class)clazz {
+ return [NSTemporaryDirectory() stringByAppendingPathComponent:NSStringFromClass(clazz)];
+}
+
+- (void)testPointAnnotation {
+ MGLPointAnnotation *annotation = [[MGLPointAnnotation alloc] init];
+ annotation.coordinate = CLLocationCoordinate2DMake(0.5, 0.5);
+ annotation.title = @"title";
+ annotation.subtitle = @"subtitle";
+
+ NSString *filePath = [self temporaryFilePathForClass:MGLPointAnnotation.class];
+ [NSKeyedArchiver archiveRootObject:annotation toFile:filePath];
+ MGLPointAnnotation *unarchivedAnnotation = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+
+ XCTAssertEqualObjects(annotation, unarchivedAnnotation);
+}
+
+- (void)testPointFeature {
+ MGLPointFeature *pointFeature = [[MGLPointFeature alloc] init];
+ pointFeature.title = @"title";
+ pointFeature.subtitle = @"subtitle";
+ pointFeature.identifier = @(123);
+ pointFeature.attributes = @{@"bbox": @[@1, @2, @3, @4]};
+
+ NSString *filePath = [self temporaryFilePathForClass:MGLPointFeature.class];
+ [NSKeyedArchiver archiveRootObject:pointFeature toFile:filePath];
+ MGLPointFeature *unarchivedPointFeature = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+
+ XCTAssertEqualObjects(pointFeature, unarchivedPointFeature);
+}
+
+- (void)testPolyline {
+ CLLocationCoordinate2D coordinates[] = {
+ CLLocationCoordinate2DMake(0.129631234123, 1.7812739312551),
+ CLLocationCoordinate2DMake(2.532083092342, 3.5216418292392)
+ };
+
+ NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
+
+ MGLPolyline *polyline = [MGLPolyline polylineWithCoordinates:coordinates count:numberOfCoordinates];
+ polyline.title = @"title";
+ polyline.subtitle = @"subtitle";
+
+ NSString *filePath = [self temporaryFilePathForClass:[MGLPolyline class]];
+ [NSKeyedArchiver archiveRootObject:polyline toFile:filePath];
+ MGLPolyline *unarchivedPolyline = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+
+ XCTAssertEqualObjects(polyline, unarchivedPolyline);
+
+ CLLocationCoordinate2D otherCoordinates[] = {
+ CLLocationCoordinate2DMake(-1, -2)
+ };
+
+ [unarchivedPolyline replaceCoordinatesInRange:NSMakeRange(0, 1) withCoordinates:otherCoordinates];
+
+ XCTAssertNotEqualObjects(polyline, unarchivedPolyline);
+}
+
+- (void)testPolygon {
+ CLLocationCoordinate2D coordinates[] = {
+ CLLocationCoordinate2DMake(0.664482398, 1.8865675),
+ CLLocationCoordinate2DMake(2.13224687, 3.9984632)
+ };
+
+ NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
+
+ MGLPolygon *polygon = [MGLPolygon polygonWithCoordinates:coordinates count:numberOfCoordinates];
+ polygon.title = nil;
+ polygon.subtitle = @"subtitle";
+
+ NSString *filePath = [self temporaryFilePathForClass:[MGLPolygon class]];
+ [NSKeyedArchiver archiveRootObject:polygon toFile:filePath];
+
+ MGLPolygon *unarchivedPolygon = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+
+ XCTAssertEqualObjects(polygon, unarchivedPolygon);
+}
+
+- (void)testPolygonWithInteriorPolygons {
+ CLLocationCoordinate2D coordinates[] = {
+ CLLocationCoordinate2DMake(0, 1),
+ CLLocationCoordinate2DMake(10, 20)
+ };
+
+ NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
+
+ CLLocationCoordinate2D interiorCoordinates[] = {
+ CLLocationCoordinate2DMake(4, 4),
+ CLLocationCoordinate2DMake(6, 6)
+ };
+
+ NSUInteger numberOfInteriorCoordinates = sizeof(interiorCoordinates) / sizeof(CLLocationCoordinate2D);
+
+ MGLPolygon *interiorPolygon = [MGLPolygon polygonWithCoordinates:interiorCoordinates count:numberOfInteriorCoordinates];
+ MGLPolygon *polygon = [MGLPolygon polygonWithCoordinates:coordinates count:numberOfCoordinates interiorPolygons:@[interiorPolygon]];
+
+ NSString *filePath = [self temporaryFilePathForClass:[MGLPolygon class]];
+ [NSKeyedArchiver archiveRootObject:polygon toFile:filePath];
+
+ MGLPolygon *unarchivedPolygon = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+
+ XCTAssertEqualObjects(polygon, unarchivedPolygon);
+}
+
+- (void)testPolylineFeature {
+ CLLocationCoordinate2D coordinates[] = {
+ CLLocationCoordinate2DMake(0, 1),
+ CLLocationCoordinate2DMake(10, 20)
+ };
+
+ NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
+ MGLPolylineFeature *polylineFeature = [MGLPolylineFeature polylineWithCoordinates:coordinates count:numberOfCoordinates];
+ polylineFeature.attributes = @{@"bbox": @[@0, @1, @2, @3]};
+ polylineFeature.identifier = @"identifier";
+
+ NSString *filePath = [self temporaryFilePathForClass:[MGLPolylineFeature class]];
+ [NSKeyedArchiver archiveRootObject:polylineFeature toFile:filePath];
+
+ MGLPolylineFeature *unarchivedPolylineFeature = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+
+ XCTAssertEqualObjects(polylineFeature, unarchivedPolylineFeature);
+
+ unarchivedPolylineFeature.attributes = @{@"bbox": @[@4, @3, @2, @1]};
+
+ XCTAssertNotEqualObjects(polylineFeature, unarchivedPolylineFeature);
+}
+
+- (void)testPolygonFeature {
+ CLLocationCoordinate2D coordinates[] = {
+ CLLocationCoordinate2DMake(0, 1),
+ CLLocationCoordinate2DMake(10, 20)
+ };
+
+ NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
+ MGLPolygonFeature *polygonFeature = [MGLPolygonFeature polygonWithCoordinates:coordinates count:numberOfCoordinates];
+
+ NSString *filePath = [self temporaryFilePathForClass:[MGLPolygonFeature class]];
+ [NSKeyedArchiver archiveRootObject:polygonFeature toFile:filePath];
+
+ MGLPolygonFeature *unarchivedPolygonFeature = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+
+ XCTAssertEqualObjects(polygonFeature, unarchivedPolygonFeature);
+
+ unarchivedPolygonFeature.identifier = @"test";
+
+ XCTAssertNotEqualObjects(polygonFeature, unarchivedPolygonFeature);
+}
+
+- (void)testPointCollection {
+ CLLocationCoordinate2D coordinates[] = {
+ CLLocationCoordinate2DMake(0, 1),
+ CLLocationCoordinate2DMake(10, 11),
+ CLLocationCoordinate2DMake(20, 21),
+ CLLocationCoordinate2DMake(30, 31),
+ };
+
+ NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
+
+ MGLPointCollection *pointCollection = [MGLPointCollection pointCollectionWithCoordinates:coordinates count:numberOfCoordinates];
+ NSString *filePath = [self temporaryFilePathForClass:[MGLPointCollection class]];
+ [NSKeyedArchiver archiveRootObject:pointCollection toFile:filePath];
+
+ MGLPointCollection *unarchivedPointCollection = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+
+ XCTAssertEqualObjects(pointCollection, unarchivedPointCollection);
+}
+
+- (void)testPointCollectionFeature {
+ NSMutableArray *features = [NSMutableArray array];
+ for (NSUInteger i = 0; i < 100; i++) {
+ MGLPointFeature *feature = [[MGLPointFeature alloc] init];
+ feature.coordinate = CLLocationCoordinate2DMake(arc4random() % 90, arc4random() % 180);
+ [features addObject:feature];
+ }
+
+ CLLocationCoordinate2D coordinates[] = {
+ CLLocationCoordinate2DMake(0, 1),
+ CLLocationCoordinate2DMake(10, 11),
+ CLLocationCoordinate2DMake(20, 21),
+ CLLocationCoordinate2DMake(30, 31),
+ };
+
+ NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
+
+ MGLPointCollectionFeature *collection = [MGLPointCollectionFeature pointCollectionWithCoordinates:coordinates count:numberOfCoordinates];
+ collection.identifier = @"identifier";
+ collection.attributes = @{@"bbox": @[@1, @2, @3, @4]};
+
+ NSString *filePath = [self temporaryFilePathForClass:[MGLPointCollectionFeature class]];
+ [NSKeyedArchiver archiveRootObject:collection toFile:filePath];
+
+ MGLPointCollectionFeature *unarchivedCollection = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+
+ XCTAssertEqualObjects(collection, unarchivedCollection);
+
+ unarchivedCollection.identifier = @"newIdentifier";
+
+ XCTAssertNotEqualObjects(collection, unarchivedCollection);
+}
+
+- (void)testMultiPolyline {
+
+ CLLocationCoordinate2D coordinates[] = {
+ CLLocationCoordinate2DMake(0, 1),
+ CLLocationCoordinate2DMake(10, 11),
+ CLLocationCoordinate2DMake(20, 21),
+ CLLocationCoordinate2DMake(30, 31),
+ };
+
+ NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
+
+ NSMutableArray *polylines = [NSMutableArray array];
+
+ for (NSUInteger i = 0; i < 100; i++) {
+ MGLPolyline *polyline = [MGLPolyline polylineWithCoordinates:coordinates count:numberOfCoordinates];
+ [polylines addObject:polyline];
+ }
+
+ MGLMultiPolyline *multiPolyline = [MGLMultiPolyline multiPolylineWithPolylines:polylines];
+
+ NSString *filePath = [self temporaryFilePathForClass:[MGLMultiPolyline class]];
+ [NSKeyedArchiver archiveRootObject:multiPolyline toFile:filePath];
+
+ MGLMultiPolyline *unarchivedMultiPolyline = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+ MGLMultiPolyline *anotherMultipolyline = [MGLMultiPolyline multiPolylineWithPolylines:[polylines subarrayWithRange:NSMakeRange(0, polylines.count/2)]];
+
+ XCTAssertEqualObjects(multiPolyline, unarchivedMultiPolyline);
+ XCTAssertNotEqualObjects(unarchivedMultiPolyline, anotherMultipolyline);
+}
+
+- (void)testMultiPolygon {
+
+ CLLocationCoordinate2D coordinates[] = {
+ CLLocationCoordinate2DMake(0, 1),
+ CLLocationCoordinate2DMake(10, 11),
+ CLLocationCoordinate2DMake(20, 21),
+ CLLocationCoordinate2DMake(30, 31),
+ };
+
+ NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
+
+ NSMutableArray *polygons = [NSMutableArray array];
+
+ for (NSUInteger i = 0; i < 100; i++) {
+ MGLPolygon *polygon = [MGLPolygon polygonWithCoordinates:coordinates count:numberOfCoordinates];
+ [polygons addObject:polygon];
+ }
+
+ MGLMultiPolygon *multiPolygon = [MGLMultiPolygon multiPolygonWithPolygons:polygons];
+
+ NSString *filePath = [self temporaryFilePathForClass:[MGLMultiPolygon class]];
+ [NSKeyedArchiver archiveRootObject:multiPolygon toFile:filePath];
+
+ MGLMultiPolygon *unarchivedMultiPolygon = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+ MGLMultiPolygon *anotherMultiPolygon = [MGLMultiPolygon multiPolygonWithPolygons:[polygons subarrayWithRange:NSMakeRange(0, polygons.count/2)]];
+
+ XCTAssertEqualObjects(multiPolygon, unarchivedMultiPolygon);
+ XCTAssertNotEqualObjects(anotherMultiPolygon, unarchivedMultiPolygon);
+}
+
+- (void)testShapeCollection {
+ CLLocationCoordinate2D coordinates[] = {
+ CLLocationCoordinate2DMake(10.12315786, 11.23451186),
+ CLLocationCoordinate2DMake(20.91836515, 21.93689215),
+ CLLocationCoordinate2DMake(30.55697246, 31.33988123),
+ };
+
+ NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
+
+ MGLPolyline *polyline = [MGLPolyline polylineWithCoordinates:coordinates count:numberOfCoordinates];
+ MGLPolygon *polygon = [MGLPolygon polygonWithCoordinates:coordinates count:numberOfCoordinates];
+
+ MGLShapeCollection *shapeCollection = [MGLShapeCollection shapeCollectionWithShapes:@[polyline, polygon]];
+
+ NSString *filePath = [self temporaryFilePathForClass:[MGLShapeCollection class]];
+ [NSKeyedArchiver archiveRootObject:shapeCollection toFile:filePath];
+
+ MGLShapeCollection *unarchivedShapeCollection = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+ MGLShapeCollection *anotherShapeCollection = [MGLShapeCollection shapeCollectionWithShapes:@[polygon]];
+
+ XCTAssertEqualObjects(shapeCollection, unarchivedShapeCollection);
+ XCTAssertNotEqualObjects(shapeCollection, anotherShapeCollection);
+}
+
+- (void)testMultiPolylineFeature {
+ CLLocationCoordinate2D coordinates[] = {
+ CLLocationCoordinate2DMake(10.12315786, 11.23451186),
+ CLLocationCoordinate2DMake(20.91836515, 21.93689215),
+ CLLocationCoordinate2DMake(30.55697246, 31.33988123),
+ };
+
+ NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
+
+ NSMutableArray *polylines = [NSMutableArray array];
+ for (NSUInteger i = 0; i < 100; i++) {
+ MGLPolylineFeature *polylineFeature = [MGLPolylineFeature polylineWithCoordinates:coordinates count:numberOfCoordinates];
+ polylineFeature.identifier = @(arc4random() % 100).stringValue;
+ [polylines addObject:polylineFeature];
+ }
+
+ MGLMultiPolylineFeature *multiPolylineFeature = [MGLMultiPolylineFeature multiPolylineWithPolylines:polylines];
+ multiPolylineFeature.attributes = @{@"bbox": @[@4, @3, @2, @1]};
+
+ NSString *filePath = [self temporaryFilePathForClass:[MGLMultiPolylineFeature class]];
+ [NSKeyedArchiver archiveRootObject:multiPolylineFeature toFile:filePath];
+
+ MGLMultiPolylineFeature *unarchivedMultiPolylineFeature = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+ MGLMultiPolylineFeature *anotherMultiPolylineFeature = [MGLMultiPolylineFeature multiPolylineWithPolylines:[polylines subarrayWithRange:NSMakeRange(0, polylines.count/2)]];
+
+ XCTAssertEqualObjects(multiPolylineFeature, unarchivedMultiPolylineFeature);
+ XCTAssertNotEqualObjects(unarchivedMultiPolylineFeature, anotherMultiPolylineFeature);
+}
+
+- (void)testMultiPolygonFeature {
+ CLLocationCoordinate2D coordinates[] = {
+ CLLocationCoordinate2DMake(10.12315786, 11.23451185),
+ CLLocationCoordinate2DMake(20.88471238, 21.93684215),
+ CLLocationCoordinate2DMake(30.15697236, 31.32988123),
+ };
+
+ NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
+
+ NSMutableArray *polygons = [NSMutableArray array];
+ for (NSUInteger i = 0; i < 100; i++ ) {
+ MGLPolygonFeature *polygonFeature = [MGLPolygonFeature polygonWithCoordinates:coordinates count:numberOfCoordinates];
+ polygonFeature.identifier = @(arc4random_uniform(100)).stringValue;
+ [polygons addObject:polygonFeature];
+ }
+
+ MGLMultiPolygonFeature *multiPolygonFeature = [MGLMultiPolygonFeature multiPolygonWithPolygons:polygons];
+ multiPolygonFeature.attributes = @{@"bbox": @[@(arc4random_uniform(100)),
+ @(arc4random_uniform(100)),
+ @(arc4random_uniform(100)),
+ @(arc4random_uniform(100))]};
+
+ NSString *filePath = [self temporaryFilePathForClass:[MGLMultiPolylineFeature class]];
+ [NSKeyedArchiver archiveRootObject:multiPolygonFeature toFile:filePath];
+
+ MGLMultiPolygonFeature *unarchivedMultiPolygonFeature = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+ MGLMultiPolygonFeature *anotherMultiPolygonFeature = [MGLMultiPolygonFeature multiPolygonWithPolygons:[polygons subarrayWithRange:NSMakeRange(0, polygons.count/2)]];
+
+ XCTAssertEqualObjects(multiPolygonFeature, unarchivedMultiPolygonFeature);
+ XCTAssertNotEqualObjects(anotherMultiPolygonFeature, unarchivedMultiPolygonFeature);
+}
+
+- (void)testShapeCollectionFeature {
+ CLLocationCoordinate2D coordinates[] = {
+ CLLocationCoordinate2DMake(10.12315786, 11.23451186),
+ CLLocationCoordinate2DMake(20.91836515, 21.93689215),
+ CLLocationCoordinate2DMake(30.55697246, 31.33988123),
+ };
+
+ NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
+
+ MGLPolylineFeature *polyline = [MGLPolylineFeature polylineWithCoordinates:coordinates count:numberOfCoordinates];
+ MGLPolygonFeature *polygon = [MGLPolygonFeature polygonWithCoordinates:coordinates count:numberOfCoordinates];
+
+ MGLShapeCollectionFeature *shapeCollectionFeature = [MGLShapeCollectionFeature shapeCollectionWithShapes:@[polyline, polygon]];
+ shapeCollectionFeature.identifier = @(arc4random_uniform(100)).stringValue;
+ shapeCollectionFeature.attributes = @{@"bbox":@[@(arc4random_uniform(100)),
+ @(arc4random_uniform(100)),
+ @(arc4random_uniform(100)),
+ @(arc4random_uniform(100))]};
+
+ NSString *filePath = [self temporaryFilePathForClass:[MGLShapeCollectionFeature class]];
+ [NSKeyedArchiver archiveRootObject:shapeCollectionFeature toFile:filePath];
+
+ MGLShapeCollectionFeature *unarchivedShapeCollectionFeature = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+
+ XCTAssertEqualObjects(shapeCollectionFeature, unarchivedShapeCollectionFeature);
+}
+
+- (void)testAnnotationImage {
+#if TARGET_OS_IPHONE
+ UIGraphicsBeginImageContext(CGSizeMake(10, 10));
+ [[UIColor redColor] setFill];
+ UIRectFill(CGRectMake(0, 0, 10, 10));
+ UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
+ UIGraphicsEndImageContext();
+#else
+ NSImage *image = [[NSImage alloc] initWithSize:CGSizeMake(10, 10)];
+ [image lockFocus];
+ [[NSColor redColor] drawSwatchInRect:CGRectMake(0, 0, 10, 10)];
+ [image unlockFocus];
+#endif
+
+ MGLAnnotationImage *annotationImage = [MGLAnnotationImage annotationImageWithImage:image reuseIdentifier:@(arc4random_uniform(100)).stringValue];
+
+ NSString *filePath = [self temporaryFilePathForClass:[MGLAnnotationImage class]];
+ [NSKeyedArchiver archiveRootObject:annotationImage toFile:filePath];
+
+ MGLAnnotationImage *unarchivedAnnotationImage = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+
+ XCTAssertEqualObjects(annotationImage, unarchivedAnnotationImage);
+}
+
+#if TARGET_OS_IPHONE
+- (void)testAnnotationView {
+ MGLAnnotationView *annotationView = [[MGLAnnotationView alloc] initWithReuseIdentifier:@"id"];
+ annotationView.enabled = NO;
+ annotationView.selected = YES;
+ annotationView.draggable = YES;
+ annotationView.centerOffset = CGVectorMake(10, 10);
+ annotationView.scalesWithViewingDistance = NO;
+
+ NSString *filePath = [self temporaryFilePathForClass:[MGLAnnotationView class]];
+ [NSKeyedArchiver archiveRootObject:annotationView toFile:filePath];
+
+ MGLAnnotationView *unarchivedAnnotationView = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+
+ XCTAssertEqual(annotationView.enabled, unarchivedAnnotationView.enabled);
+ XCTAssertEqual(annotationView.selected, unarchivedAnnotationView.selected);
+ XCTAssertEqual(annotationView.draggable, unarchivedAnnotationView.draggable);
+ XCTAssertEqualObjects(NSStringFromCGVector(annotationView.centerOffset), NSStringFromCGVector(unarchivedAnnotationView.centerOffset));
+ XCTAssertEqual(annotationView.scalesWithViewingDistance, unarchivedAnnotationView.scalesWithViewingDistance);
+}
+#endif
+
+#if TARGET_OS_IPHONE
+- (void)testUserLocation {
+ MGLUserLocation *userLocation = [[MGLUserLocation alloc] init];
+ userLocation.location = [[CLLocation alloc] initWithLatitude:1 longitude:1];
+
+ NSString *filePath = [self temporaryFilePathForClass:[MGLUserLocation class]];
+ [NSKeyedArchiver archiveRootObject:userLocation toFile:filePath];
+
+ MGLUserLocation *unarchivedUserLocation = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+
+ XCTAssertEqualObjects(userLocation, unarchivedUserLocation);
+ unarchivedUserLocation.location = [[CLLocation alloc] initWithLatitude:10 longitude:10];
+ XCTAssertNotEqualObjects(userLocation, unarchivedUserLocation);
+}
+#endif
+
+#if TARGET_OS_IPHONE
+- (void)testUserLocationAnnotationView {
+ MGLUserLocationAnnotationView *annotationView = [[MGLUserLocationAnnotationView alloc] init];
+ annotationView.enabled = NO;
+ annotationView.selected = YES;
+ annotationView.draggable = YES;
+ annotationView.centerOffset = CGVectorMake(10, 10);
+ annotationView.scalesWithViewingDistance = NO;
+
+ NSString *filePath = [self temporaryFilePathForClass:[MGLUserLocationAnnotationView class]];
+ [NSKeyedArchiver archiveRootObject:annotationView toFile:filePath];
+
+ MGLUserLocationAnnotationView *unarchivedAnnotationView = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+
+ XCTAssertEqual(annotationView.enabled, unarchivedAnnotationView.enabled);
+ XCTAssertEqual(annotationView.selected, unarchivedAnnotationView.selected);
+ XCTAssertEqual(annotationView.draggable, unarchivedAnnotationView.draggable);
+ XCTAssertEqualObjects(NSStringFromCGVector(annotationView.centerOffset), NSStringFromCGVector(unarchivedAnnotationView.centerOffset));
+ XCTAssertEqual(annotationView.scalesWithViewingDistance, unarchivedAnnotationView.scalesWithViewingDistance);
+}
+#endif
+
+@end
diff --git a/platform/darwin/test/MGLDocumentationExampleTests.swift b/platform/darwin/test/MGLDocumentationExampleTests.swift
new file mode 100644
index 0000000000..d796b4e708
--- /dev/null
+++ b/platform/darwin/test/MGLDocumentationExampleTests.swift
@@ -0,0 +1,220 @@
+import XCTest
+import Mapbox
+#if os(iOS)
+ import UIKit
+#else
+ import Cocoa
+#endif
+
+/**
+ Test cases that ensure the inline examples in the project documentation
+ compile.
+
+ To add an example:
+ 1. Add a test case named in the form testMGLClass or testMGLClass$method.
+ 2. Wrap the code you'd like to appear in the documentation within the
+ following comment blocks:
+ ```
+ //#-example-code
+ ...
+ //#-end-example-code
+ ```
+ 3. Insert an empty Swift code block inside the header file where you'd like the
+ example code to be inserted.
+ 4. Run `make darwin-update-examples` to extract example code from the test
+ method below and insert it into the header.
+ */
+class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
+ var mapView: MGLMapView!
+ var styleLoadingExpectation: XCTestExpectation!
+
+ override func setUp() {
+ super.setUp()
+ let styleURL = Bundle(for: MGLDocumentationExampleTests.self).url(forResource: "one-liner", withExtension: "json")
+ mapView = MGLMapView(frame: CGRect(x: 0, y: 0, width: 256, height: 256), styleURL: styleURL)
+ mapView.delegate = self
+ styleLoadingExpectation = expectation(description: "Map view should finish loading style")
+ waitForExpectations(timeout: 1, handler: nil)
+ }
+
+ override func tearDown() {
+ mapView = nil
+ styleLoadingExpectation = nil
+ super.tearDown()
+ }
+
+ func mapView(_ mapView: MGLMapView, didFinishLoading style: MGLStyle) {
+ styleLoadingExpectation.fulfill()
+ }
+
+ func testMGLShape$shapeWithData_encoding_error_() {
+ let mainBundle = Bundle(for: MGLDocumentationExampleTests.self)
+
+ //#-example-code
+ let url = mainBundle.url(forResource: "amsterdam", withExtension: "geojson")!
+ let data = try! Data(contentsOf: url)
+ let feature = try! MGLShape(data: data, encoding: String.Encoding.utf8.rawValue) as! MGLShapeCollectionFeature
+ //#-end-example-code
+
+ XCTAssertNotNil(feature.shapes.first as? MGLPolygonFeature)
+ }
+
+ func testMGLShapeSource() {
+ //#-example-code
+ var coordinates: [CLLocationCoordinate2D] = [
+ CLLocationCoordinate2D(latitude: 37.77, longitude: -122.42),
+ CLLocationCoordinate2D(latitude: 38.91, longitude: -77.04),
+ ]
+ let polyline = MGLPolylineFeature(coordinates: &coordinates, count: UInt(coordinates.count))
+ let source = MGLShapeSource(identifier: "lines", features: [polyline], options: nil)
+ mapView.style?.addSource(source)
+ //#-end-example-code
+
+ XCTAssertNotNil(mapView.style?.source(withIdentifier: "lines"))
+ }
+
+ func testMGLRasterSource() {
+ //#-example-code
+ let source = MGLRasterSource(identifier: "clouds", tileURLTemplates: ["https://example.com/raster-tiles/{z}/{x}/{y}.png"], options: [
+ .minimumZoomLevel: 9,
+ .maximumZoomLevel: 16,
+ .tileSize: 512,
+ .attributionInfos: [
+ MGLAttributionInfo(title: NSAttributedString(string: "© Mapbox"), url: URL(string: "http://mapbox.com"))
+ ]
+ ])
+ mapView.style?.addSource(source)
+ //#-end-example-code
+
+ XCTAssertNotNil(mapView.style?.source(withIdentifier: "clouds"))
+ }
+
+ func testMGLVectorSource() {
+ //#-example-code
+ let source = MGLVectorSource(identifier: "pois", tileURLTemplates: ["https://example.com/vector-tiles/{z}/{x}/{y}.mvt"], options: [
+ .minimumZoomLevel: 9,
+ .maximumZoomLevel: 16,
+ .attributionInfos: [
+ MGLAttributionInfo(title: NSAttributedString(string: "© Mapbox"), url: URL(string: "http://mapbox.com"))
+ ]
+ ])
+ mapView.style?.addSource(source)
+ //#-end-example-code
+
+ XCTAssertNotNil(mapView.style?.source(withIdentifier: "pois"))
+ }
+
+ func testMGLCircleStyleLayer() {
+ let population = MGLVectorSource(identifier: "population", configurationURL: URL(string: "https://example.com/style.json")!)
+ mapView.style?.addSource(population)
+
+ //#-example-code
+ let layer = MGLCircleStyleLayer(identifier: "circles", source: population)
+ layer.sourceLayerIdentifier = "population"
+ layer.circleColor = MGLStyleValue(rawValue: .green)
+ layer.circleRadius = MGLStyleValue(interpolationBase: 1.75, stops: [
+ 12: MGLStyleValue(rawValue: 2),
+ 22: MGLStyleValue(rawValue: 180)
+ ])
+ layer.circleOpacity = MGLStyleValue(rawValue: 0.7)
+ layer.predicate = NSPredicate(format: "%K == %@", "marital-status", "married")
+ mapView.style?.addLayer(layer)
+ //#-end-example-code
+
+ XCTAssertNotNil(mapView.style?.layer(withIdentifier: "circles"))
+ }
+
+ func testMGLLineStyleLayer() {
+ let trails = MGLVectorSource(identifier: "trails", configurationURL: URL(string: "https://example.com/style.json")!)
+ mapView.style?.addSource(trails)
+
+ //#-example-code
+ let layer = MGLLineStyleLayer(identifier: "trails-path", source: trails)
+ layer.sourceLayerIdentifier = "trails"
+ layer.lineWidth = MGLStyleValue(interpolationBase: 1.5, stops: [
+ 14: MGLStyleValue(rawValue: 2),
+ 18: MGLStyleValue(rawValue: 20),
+ ])
+ layer.lineColor = MGLStyleValue(rawValue: .brown)
+ layer.lineCap = MGLStyleValue(rawValue: NSValue(mglLineCap: .round))
+ layer.predicate = NSPredicate(format: "%K == %@", "trail-type", "mountain-biking")
+ mapView.style?.addLayer(layer)
+ //#-end-example-code
+
+ XCTAssertNotNil(mapView.style?.layer(withIdentifier: "trails-path"))
+ }
+
+ func testMGLFillStyleLayer() {
+ let parks = MGLVectorSource(identifier: "parks", configurationURL: URL(string: "https://example.com/style.json")!)
+ mapView.style?.addSource(parks)
+
+ //#-example-code
+ let layer = MGLFillStyleLayer(identifier: "parks", source: parks)
+ layer.sourceLayerIdentifier = "parks"
+ layer.fillColor = MGLStyleValue(rawValue: .green)
+ layer.predicate = NSPredicate(format: "type == %@", "national-park")
+ mapView.style?.addLayer(layer)
+ //#-end-example-code
+
+ XCTAssertNotNil(mapView.style?.layer(withIdentifier: "parks"))
+ }
+
+ func testMGLSymbolStyleLayer() {
+ let pois = MGLVectorSource(identifier: "pois", configurationURL: URL(string: "https://example.com/style.json")!)
+ mapView.style?.addSource(pois)
+
+ //#-example-code
+ let layer = MGLSymbolStyleLayer(identifier: "coffeeshops", source: pois)
+ layer.sourceLayerIdentifier = "pois"
+ layer.iconImageName = MGLStyleValue(rawValue: "coffee")
+ layer.iconScale = MGLStyleValue(rawValue: 0.5)
+ layer.text = MGLStyleValue(rawValue: "{name}")
+ #if os(macOS)
+ var vector = CGVector(dx: 10, dy: 0)
+ layer.textTranslation = MGLStyleValue(rawValue: NSValue(bytes: &vector, objCType: "{CGVector=dd}"))
+ #else
+ layer.textTranslation = MGLStyleValue(rawValue: NSValue(cgVector: CGVector(dx: 10, dy: 0)))
+ #endif
+ layer.textJustification = MGLStyleValue(rawValue: NSValue(mglTextJustification: .left))
+ layer.textAnchor = MGLStyleValue(rawValue: NSValue(mglTextAnchor: .left))
+ layer.predicate = NSPredicate(format: "%K == %@", "venue-type", "coffee")
+ mapView.style?.addLayer(layer)
+ //#-end-example-code
+
+ XCTAssertNotNil(mapView.style?.layer(withIdentifier: "coffeeshops"))
+ }
+
+ func testMGLRasterStyleLayer() {
+ let source = MGLRasterSource(identifier: "clouds", tileURLTemplates: ["https://example.com/raster-tiles/{z}/{x}/{y}.png"], options: [
+ .minimumZoomLevel: 9,
+ .maximumZoomLevel: 16,
+ .tileSize: 512,
+ .attributionInfos: [
+ MGLAttributionInfo(title: NSAttributedString(string: "© Mapbox"), url: URL(string: "http://mapbox.com"))
+ ]
+ ])
+ mapView.style?.addSource(source)
+
+ //#-example-code
+ let layer = MGLRasterStyleLayer(identifier: "clouds", source: source)
+ layer.rasterOpacity = MGLStyleValue(rawValue: 0.5)
+ mapView.style?.addLayer(layer)
+ //#-end-example-code
+
+ XCTAssertNotNil(mapView.style?.layer(withIdentifier: "clouds"))
+ }
+
+ func testMGLVectorStyleLayer$predicate() {
+ let terrain = MGLVectorSource(identifier: "terrain", configurationURL: URL(string: "https://example.com/style.json")!)
+ mapView.style?.addSource(terrain)
+
+ //#-example-code
+ let layer = MGLLineStyleLayer(identifier: "contour", source: terrain)
+ layer.sourceLayerIdentifier = "contours"
+ layer.predicate = NSPredicate(format: "(index == 5 || index == 10) && ele >= 1500.0")
+ mapView.style?.addLayer(layer)
+ //#-end-example-code
+
+ XCTAssertNotNil(mapView.style?.layer(withIdentifier: "contour"))
+ }
+}
diff --git a/platform/darwin/test/MGLExpressionTests.mm b/platform/darwin/test/MGLExpressionTests.mm
index 971f7d0e54..00b57c15f0 100644
--- a/platform/darwin/test/MGLExpressionTests.mm
+++ b/platform/darwin/test/MGLExpressionTests.mm
@@ -28,27 +28,35 @@
- (void)testExpressionConversionString
{
NSComparisonPredicate *predicate = [self equalityComparisonPredicateWithRightConstantValue:@"bar"];
- mbgl::Value convertedValue = predicate.rightExpression.mgl_filterValue;
- XCTAssert(convertedValue.is<std::string>() == true);
+ mbgl::Value convertedValue = predicate.rightExpression.mgl_constantMBGLValue;
+ XCTAssertTrue(convertedValue.is<std::string>());
XCTAssertEqualObjects(@(convertedValue.get<std::string>().c_str()), @"bar");
}
+- (void)testExpressionConversionStringWithUnicode
+{
+ NSComparisonPredicate *predicate = [self equalityComparisonPredicateWithRightConstantValue:@"🆔🆗🇦🇶"];
+ mbgl::Value convertedValue = predicate.rightExpression.mgl_constantMBGLValue;
+ XCTAssertTrue(convertedValue.is<std::string>());
+ XCTAssertEqual(convertedValue.get<std::string>(), "🆔🆗🇦🇶");
+}
+
#pragma mark - Boolean Tests
- (void)testExpressionConversionBooleanTrue
{
NSComparisonPredicate *predicate = [self equalityComparisonPredicateWithRightConstantValue:@YES];
- mbgl::Value convertedValue = predicate.rightExpression.mgl_filterValue;
- XCTAssert(convertedValue.is<bool>() == true);
- XCTAssert(convertedValue.get<bool>() == true);
+ mbgl::Value convertedValue = predicate.rightExpression.mgl_constantMBGLValue;
+ XCTAssertTrue(convertedValue.is<bool>());
+ XCTAssertEqual(convertedValue.get<bool>(), true);
}
- (void)testExpressionConversionBooleanFalse
{
NSComparisonPredicate *predicate = [self equalityComparisonPredicateWithRightConstantValue:@NO];
- mbgl::Value convertedValue = predicate.rightExpression.mgl_filterValue;
- XCTAssert(convertedValue.is<bool>() == true);
- XCTAssert(convertedValue.get<bool>() == false);
+ mbgl::Value convertedValue = predicate.rightExpression.mgl_constantMBGLValue;
+ XCTAssertTrue(convertedValue.is<bool>());
+ XCTAssertEqual(convertedValue.get<bool>(), false);
}
#pragma mark - Floating Point Tests
@@ -59,12 +67,12 @@
mbgl::Value convertedValue;
predicate = [self equalityComparisonPredicateWithRightConstantValue:[NSNumber numberWithDouble:DBL_MIN]];
- convertedValue = predicate.rightExpression.mgl_filterValue;
+ convertedValue = predicate.rightExpression.mgl_constantMBGLValue;
XCTAssertTrue(convertedValue.is<double>());
XCTAssertEqual(convertedValue.get<double>(), DBL_MIN);
predicate = [self equalityComparisonPredicateWithRightConstantValue:[NSNumber numberWithDouble:DBL_MAX]];
- convertedValue = predicate.rightExpression.mgl_filterValue;
+ convertedValue = predicate.rightExpression.mgl_constantMBGLValue;
XCTAssertTrue(convertedValue.is<double>());
XCTAssertEqual(convertedValue.get<double>(), DBL_MAX);
}
@@ -72,7 +80,7 @@
- (void)testExpressionConversionFloat
{
// Because we can't guarantee precision when using float, and because
- // we warn the user to this effect in mgl_convertedValueWithValue:,
+ // we warn the user to this effect in -[NSExpression mgl_constantMBGLValue],
// we just check that things are in the ballpark here with integer values
// and some lower-precision checks.
@@ -80,26 +88,24 @@
mbgl::Value convertedValue;
predicate = [self equalityComparisonPredicateWithRightConstantValue:[NSNumber numberWithFloat:-1]];
- convertedValue = predicate.rightExpression.mgl_filterValue;
+ convertedValue = predicate.rightExpression.mgl_constantMBGLValue;
XCTAssertTrue(convertedValue.is<double>());
XCTAssertEqual(convertedValue.get<double>(), -1);
predicate = [self equalityComparisonPredicateWithRightConstantValue:[NSNumber numberWithFloat:1]];
- convertedValue = predicate.rightExpression.mgl_filterValue;
+ convertedValue = predicate.rightExpression.mgl_constantMBGLValue;
XCTAssertTrue(convertedValue.is<double>());
XCTAssertEqual(convertedValue.get<double>(), 1);
predicate = [self equalityComparisonPredicateWithRightConstantValue:[NSNumber numberWithFloat:-23.232342]];
- convertedValue = predicate.rightExpression.mgl_filterValue;
+ convertedValue = predicate.rightExpression.mgl_constantMBGLValue;
XCTAssertTrue(convertedValue.is<double>());
- XCTAssertLessThan(-23.24, convertedValue.get<double>());
- XCTAssertGreaterThan(-23.23, convertedValue.get<double>());
+ XCTAssertEqualWithAccuracy(convertedValue.get<double>(), -23.232342, 0.000001);
predicate = [self equalityComparisonPredicateWithRightConstantValue:[NSNumber numberWithFloat:23.232342]];
- convertedValue = predicate.rightExpression.mgl_filterValue;
+ convertedValue = predicate.rightExpression.mgl_constantMBGLValue;
XCTAssertTrue(convertedValue.is<double>());
- XCTAssertLessThan(23.23, convertedValue.get<double>());
- XCTAssertGreaterThan(23.24, convertedValue.get<double>());
+ XCTAssertEqualWithAccuracy(convertedValue.get<double>(), 23.232342, 0.000001);
}
#pragma mark - Integer Tests
@@ -132,7 +138,7 @@
for (NSNumber *min in minValues)
{
predicate = [self equalityComparisonPredicateWithRightConstantValue:min];
- convertedValue = predicate.rightExpression.mgl_filterValue;
+ convertedValue = predicate.rightExpression.mgl_constantMBGLValue;
XCTAssertTrue(convertedValue.is<int64_t>());
XCTAssertEqual(convertedValue.get<int64_t>(), min.longLongValue);
}
@@ -144,7 +150,7 @@
for (NSNumber *max in maxValues)
{
predicate = [self equalityComparisonPredicateWithRightConstantValue:max];
- convertedValue = predicate.rightExpression.mgl_filterValue;
+ convertedValue = predicate.rightExpression.mgl_constantMBGLValue;
XCTAssertTrue(convertedValue.is<uint64_t>());
XCTAssertEqual(convertedValue.get<uint64_t>(), max.unsignedLongLongValue);
}
@@ -179,7 +185,7 @@
for (NSNumber *min in minValues)
{
predicate = [self equalityComparisonPredicateWithRightConstantValue:min];
- convertedValue = predicate.rightExpression.mgl_filterValue;
+ convertedValue = predicate.rightExpression.mgl_constantMBGLValue;
XCTAssertTrue(convertedValue.is<uint64_t>());
XCTAssertEqual(convertedValue.get<uint64_t>(), min.unsignedLongLongValue);
}
@@ -191,10 +197,19 @@
for (NSNumber *max in maxValues)
{
predicate = [self equalityComparisonPredicateWithRightConstantValue:max];
- convertedValue = predicate.rightExpression.mgl_filterValue;
+ convertedValue = predicate.rightExpression.mgl_constantMBGLValue;
XCTAssertTrue(convertedValue.is<uint64_t>());
XCTAssertEqual(convertedValue.get<uint64_t>(), max.unsignedLongLongValue);
}
}
+#pragma mark - Null Tests
+
+- (void)testExpressionConversionNull
+{
+ NSComparisonPredicate *predicate = [self equalityComparisonPredicateWithRightConstantValue:[NSNull null]];
+ mbgl::Value convertedValue = predicate.rightExpression.mgl_constantMBGLValue;
+ XCTAssertTrue(convertedValue.is<mbgl::NullValue>());
+}
+
@end
diff --git a/platform/darwin/test/MGLFeatureTests.mm b/platform/darwin/test/MGLFeatureTests.mm
index efc30d307b..91ec9d429e 100644
--- a/platform/darwin/test/MGLFeatureTests.mm
+++ b/platform/darwin/test/MGLFeatureTests.mm
@@ -322,6 +322,15 @@
@[@(coord2.longitude), @(coord2.latitude)]]}
]};
XCTAssertEqualObjects(geoJSONFeature[@"geometry"], expectedGeometry);
+
+ // When the shape collection is created with an empty array of shapes
+ shapeCollectionFeature = [MGLShapeCollectionFeature shapeCollectionWithShapes:@[]];
+
+ // it has the correct (empty) geometry
+ geoJSONFeature = [shapeCollectionFeature geoJSONDictionary];
+ expectedGeometry = @{@"type": @"GeometryCollection",
+ @"geometries": @[]};
+ XCTAssertEqualObjects(geoJSONFeature[@"geometry"], expectedGeometry);
}
@end
diff --git a/platform/darwin/test/MGLFillStyleLayerTests.m b/platform/darwin/test/MGLFillStyleLayerTests.m
deleted file mode 100644
index 7d51c15cf4..0000000000
--- a/platform/darwin/test/MGLFillStyleLayerTests.m
+++ /dev/null
@@ -1,69 +0,0 @@
-// This file is generated.
-// Edit platform/darwin/scripts/generate-style-code.js, then run `make style-code-darwin`.
-
-#import "MGLStyleLayerTests.h"
-
-@interface MGLFillLayerTests : MGLStyleLayerTests
-@end
-
-@implementation MGLFillLayerTests
-
-+ (NSString *)layerType {
- return @"fill";
-}
-
-- (void)testFillLayer {
- NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"amsterdam" ofType:@"geojson"];
- NSURL *url = [NSURL fileURLWithPath:filePath];
- MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"sourceID" URL:url options:nil];
- [self.mapView.style addSource:source];
- MGLFillStyleLayer *layer = [[MGLFillStyleLayer alloc] initWithIdentifier:@"layerID" source:source];
- [self.mapView.style addLayer:layer];
-
- layer.fillAntialiased = [MGLRuntimeStylingHelper testBool];
- layer.fillColor = [MGLRuntimeStylingHelper testColor];
- layer.fillOpacity = [MGLRuntimeStylingHelper testNumber];
- layer.fillOutlineColor = [MGLRuntimeStylingHelper testColor];
- layer.fillPattern = [MGLRuntimeStylingHelper testString];
- layer.fillTranslate = [MGLRuntimeStylingHelper testOffset];
- layer.fillTranslateAnchor = [MGLRuntimeStylingHelper testEnum:MGLFillTranslateAnchorViewport type:@encode(MGLFillTranslateAnchor)];
-
- MGLFillStyleLayer *gLayer = (MGLFillStyleLayer *)[self.mapView.style layerWithIdentifier:@"layerID"];
- XCTAssertTrue([gLayer isKindOfClass:[MGLFillStyleLayer class]]);
- XCTAssertEqualObjects(gLayer.fillAntialiased, [MGLRuntimeStylingHelper testBool]);
- XCTAssertEqualObjects(gLayer.fillColor, [MGLRuntimeStylingHelper testColor]);
- XCTAssertEqualObjects(gLayer.fillOpacity, [MGLRuntimeStylingHelper testNumber]);
- XCTAssertEqualObjects(gLayer.fillOutlineColor, [MGLRuntimeStylingHelper testColor]);
- XCTAssertEqualObjects(gLayer.fillPattern, [MGLRuntimeStylingHelper testString]);
- XCTAssertEqualObjects(gLayer.fillTranslate, [MGLRuntimeStylingHelper testOffset]);
- XCTAssert([gLayer.fillTranslateAnchor isKindOfClass:[MGLStyleConstantValue class]]);
- XCTAssertEqualObjects(gLayer.fillTranslateAnchor, [MGLRuntimeStylingHelper testEnum:MGLFillTranslateAnchorViewport type:@encode(MGLFillTranslateAnchor)]);
-
- layer.fillAntialiased = [MGLRuntimeStylingHelper testBoolFunction];
- layer.fillColor = [MGLRuntimeStylingHelper testColorFunction];
- layer.fillOpacity = [MGLRuntimeStylingHelper testNumberFunction];
- layer.fillOutlineColor = [MGLRuntimeStylingHelper testColorFunction];
- layer.fillPattern = [MGLRuntimeStylingHelper testStringFunction];
- layer.fillTranslate = [MGLRuntimeStylingHelper testOffsetFunction];
- layer.fillTranslateAnchor = [MGLRuntimeStylingHelper testEnumFunction:MGLFillTranslateAnchorViewport type:@encode(MGLFillTranslateAnchor)];
-
- XCTAssertEqualObjects(gLayer.fillAntialiased, [MGLRuntimeStylingHelper testBoolFunction]);
- XCTAssertEqualObjects(gLayer.fillColor, [MGLRuntimeStylingHelper testColorFunction]);
- XCTAssertEqualObjects(gLayer.fillOpacity, [MGLRuntimeStylingHelper testNumberFunction]);
- XCTAssertEqualObjects(gLayer.fillOutlineColor, [MGLRuntimeStylingHelper testColorFunction]);
- XCTAssertEqualObjects(gLayer.fillPattern, [MGLRuntimeStylingHelper testStringFunction]);
- XCTAssertEqualObjects(gLayer.fillTranslate, [MGLRuntimeStylingHelper testOffsetFunction]);
- XCTAssertEqualObjects(gLayer.fillTranslateAnchor, [MGLRuntimeStylingHelper testEnumFunction:MGLFillTranslateAnchorViewport type:@encode(MGLFillTranslateAnchor)]);
-}
-
-- (void)testPropertyNames {
- [self testPropertyName:@"is-fill-antialiased" isBoolean:YES];
- [self testPropertyName:@"fill-color" isBoolean:NO];
- [self testPropertyName:@"fill-opacity" isBoolean:NO];
- [self testPropertyName:@"fill-outline-color" isBoolean:NO];
- [self testPropertyName:@"fill-pattern" isBoolean:NO];
- [self testPropertyName:@"fill-translate" isBoolean:NO];
- [self testPropertyName:@"fill-translate-anchor" isBoolean:NO];
-}
-
-@end
diff --git a/platform/darwin/test/MGLFillStyleLayerTests.mm b/platform/darwin/test/MGLFillStyleLayerTests.mm
new file mode 100644
index 0000000000..fb50512afd
--- /dev/null
+++ b/platform/darwin/test/MGLFillStyleLayerTests.mm
@@ -0,0 +1,306 @@
+// This file is generated.
+// Edit platform/darwin/scripts/generate-style-code.js, then run `make style-code-darwin`.
+
+#import "MGLStyleLayerTests.h"
+
+#import "MGLStyleLayer_Private.h"
+
+#include <mbgl/style/layers/fill_layer.hpp>
+
+@interface MGLFillLayerTests : MGLStyleLayerTests
+@end
+
+@implementation MGLFillLayerTests
+
++ (NSString *)layerType {
+ return @"fill";
+}
+
+- (void)testPredicates {
+ MGLPointFeature *feature = [[MGLPointFeature alloc] init];
+ MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"sourceID" shape:feature options:nil];
+ MGLFillStyleLayer *layer = [[MGLFillStyleLayer alloc] initWithIdentifier:@"layerID" source:source];
+
+ XCTAssertNil(layer.sourceLayerIdentifier);
+ layer.sourceLayerIdentifier = @"layerID";
+ XCTAssertEqualObjects(layer.sourceLayerIdentifier, @"layerID");
+ layer.sourceLayerIdentifier = nil;
+ XCTAssertNil(layer.sourceLayerIdentifier);
+
+ XCTAssertNil(layer.predicate);
+ layer.predicate = [NSPredicate predicateWithValue:NO];
+ XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithValue:NO]);
+ layer.predicate = nil;
+ XCTAssertNil(layer.predicate);
+}
+
+- (void)testProperties {
+ MGLPointFeature *feature = [[MGLPointFeature alloc] init];
+ MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"sourceID" shape:feature options:nil];
+
+ MGLFillStyleLayer *layer = [[MGLFillStyleLayer alloc] initWithIdentifier:@"layerID" source:source];
+ XCTAssertNotEqual(layer.rawLayer, nullptr);
+ XCTAssertTrue(layer.rawLayer->is<mbgl::style::FillLayer>());
+ auto rawLayer = layer.rawLayer->as<mbgl::style::FillLayer>();
+
+ // fill-antialias
+ {
+ XCTAssertTrue(rawLayer->getFillAntialias().isUndefined(),
+ @"fill-antialias should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.fillAntialiased;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@NO];
+ layer.fillAntialiased = styleValue;
+ mbgl::style::PropertyValue<bool> propertyValue = { false };
+ XCTAssertEqual(rawLayer->getFillAntialias(), propertyValue,
+ @"Setting fillAntialiased to a constant value should update fill-antialias.");
+ XCTAssertEqualObjects(layer.fillAntialiased, styleValue,
+ @"fillAntialiased should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.fillAntialiased = styleValue;
+ propertyValue = { mbgl::style::Function<bool> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getFillAntialias(), propertyValue,
+ @"Setting fillAntialiased to a function should update fill-antialias.");
+ XCTAssertEqualObjects(layer.fillAntialiased, styleValue,
+ @"fillAntialiased should round-trip functions.");
+
+ layer.fillAntialiased = nil;
+ XCTAssertTrue(rawLayer->getFillAntialias().isUndefined(),
+ @"Unsetting fillAntialiased should return fill-antialias to the default value.");
+ XCTAssertEqualObjects(layer.fillAntialiased, defaultStyleValue,
+ @"fillAntialiased should return the default value after being unset.");
+ }
+
+ // fill-color
+ {
+ XCTAssertTrue(rawLayer->getFillColor().isUndefined(),
+ @"fill-color should be unset initially.");
+ MGLStyleValue<MGLColor *> *defaultStyleValue = layer.fillColor;
+
+ MGLStyleValue<MGLColor *> *styleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]];
+ layer.fillColor = styleValue;
+ mbgl::style::PropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } };
+ XCTAssertEqual(rawLayer->getFillColor(), propertyValue,
+ @"Setting fillColor to a constant value should update fill-color.");
+ XCTAssertEqualObjects(layer.fillColor, styleValue,
+ @"fillColor should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<MGLColor *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.fillColor = styleValue;
+ propertyValue = { mbgl::style::Function<mbgl::Color> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getFillColor(), propertyValue,
+ @"Setting fillColor to a function should update fill-color.");
+ XCTAssertEqualObjects(layer.fillColor, styleValue,
+ @"fillColor should round-trip functions.");
+
+ layer.fillColor = nil;
+ XCTAssertTrue(rawLayer->getFillColor().isUndefined(),
+ @"Unsetting fillColor should return fill-color to the default value.");
+ XCTAssertEqualObjects(layer.fillColor, defaultStyleValue,
+ @"fillColor should return the default value after being unset.");
+ }
+
+ // fill-opacity
+ {
+ XCTAssertTrue(rawLayer->getFillOpacity().isUndefined(),
+ @"fill-opacity should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.fillOpacity;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.fillOpacity = styleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getFillOpacity(), propertyValue,
+ @"Setting fillOpacity to a constant value should update fill-opacity.");
+ XCTAssertEqualObjects(layer.fillOpacity, styleValue,
+ @"fillOpacity should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.fillOpacity = styleValue;
+ propertyValue = { mbgl::style::Function<float> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getFillOpacity(), propertyValue,
+ @"Setting fillOpacity to a function should update fill-opacity.");
+ XCTAssertEqualObjects(layer.fillOpacity, styleValue,
+ @"fillOpacity should round-trip functions.");
+
+ layer.fillOpacity = nil;
+ XCTAssertTrue(rawLayer->getFillOpacity().isUndefined(),
+ @"Unsetting fillOpacity should return fill-opacity to the default value.");
+ XCTAssertEqualObjects(layer.fillOpacity, defaultStyleValue,
+ @"fillOpacity should return the default value after being unset.");
+ }
+
+ // fill-outline-color
+ {
+ XCTAssertTrue(rawLayer->getFillOutlineColor().isUndefined(),
+ @"fill-outline-color should be unset initially.");
+ MGLStyleValue<MGLColor *> *defaultStyleValue = layer.fillOutlineColor;
+
+ MGLStyleValue<MGLColor *> *styleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]];
+ layer.fillOutlineColor = styleValue;
+ mbgl::style::PropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } };
+ XCTAssertEqual(rawLayer->getFillOutlineColor(), propertyValue,
+ @"Setting fillOutlineColor to a constant value should update fill-outline-color.");
+ XCTAssertEqualObjects(layer.fillOutlineColor, styleValue,
+ @"fillOutlineColor should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<MGLColor *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.fillOutlineColor = styleValue;
+ propertyValue = { mbgl::style::Function<mbgl::Color> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getFillOutlineColor(), propertyValue,
+ @"Setting fillOutlineColor to a function should update fill-outline-color.");
+ XCTAssertEqualObjects(layer.fillOutlineColor, styleValue,
+ @"fillOutlineColor should round-trip functions.");
+
+ layer.fillOutlineColor = nil;
+ XCTAssertTrue(rawLayer->getFillOutlineColor().isUndefined(),
+ @"Unsetting fillOutlineColor should return fill-outline-color to the default value.");
+ XCTAssertEqualObjects(layer.fillOutlineColor, defaultStyleValue,
+ @"fillOutlineColor should return the default value after being unset.");
+ }
+
+ // fill-pattern
+ {
+ XCTAssertTrue(rawLayer->getFillPattern().isUndefined(),
+ @"fill-pattern should be unset initially.");
+ MGLStyleValue<NSString *> *defaultStyleValue = layer.fillPattern;
+
+ MGLStyleValue<NSString *> *styleValue = [MGLStyleValue<NSString *> valueWithRawValue:@"Fill Pattern"];
+ layer.fillPattern = styleValue;
+ mbgl::style::PropertyValue<std::string> propertyValue = { "Fill Pattern" };
+ XCTAssertEqual(rawLayer->getFillPattern(), propertyValue,
+ @"Setting fillPattern to a constant value should update fill-pattern.");
+ XCTAssertEqualObjects(layer.fillPattern, styleValue,
+ @"fillPattern should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSString *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.fillPattern = styleValue;
+ propertyValue = { mbgl::style::Function<std::string> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getFillPattern(), propertyValue,
+ @"Setting fillPattern to a function should update fill-pattern.");
+ XCTAssertEqualObjects(layer.fillPattern, styleValue,
+ @"fillPattern should round-trip functions.");
+
+ layer.fillPattern = nil;
+ XCTAssertTrue(rawLayer->getFillPattern().isUndefined(),
+ @"Unsetting fillPattern should return fill-pattern to the default value.");
+ XCTAssertEqualObjects(layer.fillPattern, defaultStyleValue,
+ @"fillPattern should return the default value after being unset.");
+ }
+
+ // fill-translate
+ {
+ XCTAssertTrue(rawLayer->getFillTranslate().isUndefined(),
+ @"fill-translate should be unset initially.");
+ MGLStyleValue<NSValue *> *defaultStyleValue = layer.fillTranslation;
+
+ MGLStyleValue<NSValue *> *styleValue = [MGLStyleValue<NSValue *> valueWithRawValue:
+#if TARGET_OS_IPHONE
+ [NSValue valueWithCGVector:CGVectorMake(1, 1)]
+#else
+ [NSValue valueWithMGLVector:CGVectorMake(1, -1)]
+#endif
+ ];
+ layer.fillTranslation = styleValue;
+ mbgl::style::PropertyValue<std::array<float, 2>> propertyValue = { { 1, 1 } };
+ XCTAssertEqual(rawLayer->getFillTranslate(), propertyValue,
+ @"Setting fillTranslation to a constant value should update fill-translate.");
+ XCTAssertEqualObjects(layer.fillTranslation, styleValue,
+ @"fillTranslation should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSValue *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.fillTranslation = styleValue;
+ propertyValue = { mbgl::style::Function<std::array<float, 2>> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getFillTranslate(), propertyValue,
+ @"Setting fillTranslation to a function should update fill-translate.");
+ XCTAssertEqualObjects(layer.fillTranslation, styleValue,
+ @"fillTranslation should round-trip functions.");
+
+ layer.fillTranslation = nil;
+ XCTAssertTrue(rawLayer->getFillTranslate().isUndefined(),
+ @"Unsetting fillTranslation should return fill-translate to the default value.");
+ XCTAssertEqualObjects(layer.fillTranslation, defaultStyleValue,
+ @"fillTranslation should return the default value after being unset.");
+ }
+
+ // fill-translate-anchor
+ {
+ XCTAssertTrue(rawLayer->getFillTranslateAnchor().isUndefined(),
+ @"fill-translate-anchor should be unset initially.");
+ MGLStyleValue<NSValue *> *defaultStyleValue = layer.fillTranslationAnchor;
+
+ MGLStyleValue<NSValue *> *styleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLFillTranslationAnchor:MGLFillTranslationAnchorViewport]];
+ layer.fillTranslationAnchor = styleValue;
+ mbgl::style::PropertyValue<mbgl::style::TranslateAnchorType> propertyValue = { mbgl::style::TranslateAnchorType::Viewport };
+ XCTAssertEqual(rawLayer->getFillTranslateAnchor(), propertyValue,
+ @"Setting fillTranslationAnchor to a constant value should update fill-translate-anchor.");
+ XCTAssertEqualObjects(layer.fillTranslationAnchor, styleValue,
+ @"fillTranslationAnchor should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSValue *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.fillTranslationAnchor = styleValue;
+ propertyValue = { mbgl::style::Function<mbgl::style::TranslateAnchorType> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getFillTranslateAnchor(), propertyValue,
+ @"Setting fillTranslationAnchor to a function should update fill-translate-anchor.");
+ XCTAssertEqualObjects(layer.fillTranslationAnchor, styleValue,
+ @"fillTranslationAnchor should round-trip functions.");
+
+ layer.fillTranslationAnchor = nil;
+ XCTAssertTrue(rawLayer->getFillTranslateAnchor().isUndefined(),
+ @"Unsetting fillTranslationAnchor should return fill-translate-anchor to the default value.");
+ XCTAssertEqualObjects(layer.fillTranslationAnchor, defaultStyleValue,
+ @"fillTranslationAnchor should return the default value after being unset.");
+ }
+}
+
+- (void)testPropertyNames {
+ [self testPropertyName:@"is-fill-antialiased" isBoolean:YES];
+ [self testPropertyName:@"fill-color" isBoolean:NO];
+ [self testPropertyName:@"fill-opacity" isBoolean:NO];
+ [self testPropertyName:@"fill-outline-color" isBoolean:NO];
+ [self testPropertyName:@"fill-pattern" isBoolean:NO];
+ [self testPropertyName:@"fill-translation" isBoolean:NO];
+ [self testPropertyName:@"fill-translation-anchor" isBoolean:NO];
+}
+
+- (void)testValueAdditions {
+ XCTAssertEqual([NSValue valueWithMGLFillTranslationAnchor:MGLFillTranslationAnchorMap].MGLFillTranslationAnchorValue, MGLFillTranslationAnchorMap);
+ XCTAssertEqual([NSValue valueWithMGLFillTranslationAnchor:MGLFillTranslationAnchorViewport].MGLFillTranslationAnchorValue, MGLFillTranslationAnchorViewport);
+}
+
+@end
diff --git a/platform/darwin/test/MGLFilterTests.mm b/platform/darwin/test/MGLFilterTests.mm
deleted file mode 100644
index e688d50583..0000000000
--- a/platform/darwin/test/MGLFilterTests.mm
+++ /dev/null
@@ -1,194 +0,0 @@
-#import "MGLStyleLayerTests.h"
-
-#import "NSPredicate+MGLAdditions.h"
-#import "MGLValueEvaluator.h"
-
-
-@interface MGLFilterTests : MGLStyleLayerTests {
- MGLShapeSource *source;
- MGLLineStyleLayer *layer;
-}
-@end
-
-@implementation MGLFilterTests
-
-- (void)setUp
-{
- [super setUp];
- NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"amsterdam" ofType:@"geojson"];
- NSURL *url = [NSURL fileURLWithPath:filePath];
- NSData *geoJSONData = [NSData dataWithContentsOfURL:url];
- NSError *error;
- MGLShape *shape = [MGLShape shapeWithData:geoJSONData encoding:NSUTF8StringEncoding error:&error];
- XCTAssertNil(error);
- XCTAssertNotNil(shape);
- source = [[MGLShapeSource alloc] initWithIdentifier:@"test-source" shape:shape options:nil];
- [self.mapView.style addSource:source];
- layer = [[MGLLineStyleLayer alloc] initWithIdentifier:@"test-layer" source:source];
-}
-
-- (void)tearDown
-{
- [self.mapView.style removeLayer:layer];
- [self.mapView.style removeSource:source];
-}
-
-- (NSArray<NSPredicate *> *)predicates
-{
- NSPredicate *equalPredicate = [NSPredicate predicateWithFormat:@"type == 'neighbourhood'"];
- NSPredicate *notEqualPredicate = [NSPredicate predicateWithFormat:@"type != 'park'"];
- NSPredicate *greaterThanPredicate = [NSPredicate predicateWithFormat:@"%K > %@", @"stroke-width", @2.1];
- NSPredicate *greaterThanOrEqualToPredicate = [NSPredicate predicateWithFormat:@"%K >= %@", @"stroke-width", @2.1];
- NSPredicate *lessThanOrEqualToPredicate = [NSPredicate predicateWithFormat:@"%K <= %@", @"stroke-width", @2.1];
- NSPredicate *lessThanPredicate = [NSPredicate predicateWithFormat:@"%K < %@", @"stroke-width", @2.1];
- NSPredicate *inPredicate = [NSPredicate predicateWithFormat:@"type IN %@", @[@"park", @"neighbourhood"]];
- NSPredicate *notInPredicate = [NSPredicate predicateWithFormat:@"NOT (type IN %@)", @[@"park", @"neighbourhood"]];
- NSPredicate *inNotInPredicate = [NSPredicate predicateWithFormat:@"type IN %@ AND NOT (type IN %@)", @[@"park"], @[@"neighbourhood", @"test"]];
- NSPredicate *typePredicate = [NSPredicate predicateWithFormat:@"%K == %@", @"$type", @"Feature"];
- NSPredicate *idPredicate = [NSPredicate predicateWithFormat:@"%K == %@", @"$id", @"1234123"];
- NSPredicate *specialCharsPredicate = [NSPredicate predicateWithFormat:@"%K == %@", @"ty-’pè", @"sŒm-ethįng"];
- NSPredicate *booleanPredicate = [NSPredicate predicateWithFormat:@"cluster != YES"];
- NSPredicate *nilEqualsPredicate = [NSPredicate predicateWithFormat:@"type == %@", nil];
- NSPredicate *nilNotEqualsPredicate = [NSPredicate predicateWithFormat:@"type != %@", nil];
- return @[
- equalPredicate,
- notEqualPredicate,
- greaterThanPredicate,
- greaterThanOrEqualToPredicate,
- lessThanOrEqualToPredicate,
- lessThanPredicate,
- inPredicate,
- notInPredicate,
- inNotInPredicate,
- typePredicate,
- idPredicate,
- specialCharsPredicate,
- booleanPredicate,
- nilEqualsPredicate,
- nilNotEqualsPredicate
- ];
-}
-
-- (void)testAllPredicates
-{
- for (NSPredicate *predicate in self.predicates) {
- layer.predicate = predicate;
- XCTAssertEqualObjects(layer.predicate, predicate);
- }
- [self.mapView.style addLayer:layer];
-}
-
-- (void)testContainsPredicate
-{
- // core does not have a "contains" filter but we can achieve the equivalent by creating an `mbgl::style::InFilter`
- // and searching the value for the key
- NSPredicate *expectedPredicate = [NSPredicate predicateWithFormat:@"park IN %@", @[@"park", @"neighbourhood"]];
- NSPredicate *containsPredicate = [NSPredicate predicateWithFormat:@"%@ CONTAINS %@", @[@"park", @"neighbourhood"], @"park"];
-
- layer.predicate = containsPredicate;
- XCTAssertEqualObjects(layer.predicate, expectedPredicate);
- [self.mapView.style addLayer:layer];
-}
-
-- (void)testBetweenPredicate
-{
- // core does not have a "between" filter but we can achieve the equivalent by creating a set of greater than or equal / less than or equal
- // filters for the lower and upper bounds (inclusive)
- NSPredicate *expectedPredicate = [NSCompoundPredicate predicateWithFormat:@"%K >= 2 AND %K <= 3", @"stroke-width", @"stroke-width"];
- NSPredicate *betweenPredicate = [NSPredicate predicateWithFormat:@"%K BETWEEN %@", @"stroke-width", @[@2.0, @3.0]];
-
- layer.predicate = betweenPredicate;
- XCTAssertEqualObjects(layer.predicate, expectedPredicate);
- [self.mapView.style addLayer:layer];
-}
-
-- (void)testTruePredicate
-{
- // This comes out of the class cluster as an NSTruePredicate and it is equal to `[NSPredicate predicateWithValue:YES]`
- NSPredicate *truePredicate = [NSPredicate predicateWithFormat:@"TRUEPREDICATE"];
-
- layer.predicate = truePredicate;
- XCTAssertEqualObjects(layer.predicate.description, truePredicate.description);
- [self.mapView.style addLayer:layer];
-}
-
-- (void)testFalsePredicate
-{
- // This comes out of the class cluster as an NSFalsePredicate and it is equal to `[NSPredicate predicateWithValue:NO]`
- NSPredicate *falsePredicate = [NSPredicate predicateWithFormat:@"FALSEPREDICATE"];
-
- layer.predicate = falsePredicate;
- XCTAssertEqualObjects(layer.predicate.description, falsePredicate.description);
- [self.mapView.style addLayer:layer];
-}
-
-- (void)testIntermittentEncoding
-{
- NSPredicate *specialCharsPredicate = [NSPredicate predicateWithFormat:@"%K == %@", @"ty-’pè", @"sŒm-ethįng"];
- layer.predicate = specialCharsPredicate;
-
- NSComparisonPredicate *getPredicate = (NSComparisonPredicate *)layer.predicate;
- mbgl::style::EqualsFilter filter = layer.predicate.mgl_filter.get<mbgl::style::EqualsFilter>();
-
- id objcKey = getPredicate.leftExpression.keyPath;
- id cppKey = @(filter.key.c_str());
- id objcValue = mbgl::Value::visit(getPredicate.rightExpression.mgl_filterValue, ValueEvaluator());
- id cppValue = mbgl::Value::visit(filter.value, ValueEvaluator());
-
- XCTAssertEqualObjects(objcKey, cppKey);
- XCTAssertEqualObjects(objcValue, cppValue);
-
- [self.mapView.style addLayer:layer];
-}
-
-- (void)testNestedFilters
-{
- NSPredicate *equalPredicate = [NSPredicate predicateWithFormat:@"type == 'neighbourhood'"];
- NSPredicate *notEqualPredicate = [NSPredicate predicateWithFormat:@"type != 'park'"];
-
- NSPredicate *allPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[equalPredicate, notEqualPredicate]];
- NSPredicate *anyPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:@[equalPredicate, notEqualPredicate]];
-
- layer.predicate = allPredicate;
- XCTAssertEqualObjects(layer.predicate, allPredicate);
- layer.predicate = anyPredicate;
- XCTAssertEqualObjects(layer.predicate, anyPredicate);
-
- [self.mapView.style addLayer:layer];
-}
-
-- (void)testAndPredicates
-{
- NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:self.predicates];
- layer.predicate = predicate;
- XCTAssertEqualObjects(predicate, layer.predicate);
- [self.mapView.style addLayer:layer];
-}
-
-- (void)testOrPredicates
-{
- NSPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:self.predicates];
- layer.predicate = predicate;
- XCTAssertEqualObjects(predicate, layer.predicate);
- [self.mapView.style addLayer:layer];
-}
-
-- (void)testNotAndPredicates
-{
- NSPredicate *predicates = [NSCompoundPredicate andPredicateWithSubpredicates:self.predicates];
- NSCompoundPredicate *predicate = [NSCompoundPredicate notPredicateWithSubpredicate:predicates];
- layer.predicate = predicate;
- XCTAssertEqualObjects(predicate, layer.predicate);
- [self.mapView.style addLayer:layer];
-}
-
-- (void)testNotOrPredicates
-{
- NSPredicate *predicates = [NSCompoundPredicate orPredicateWithSubpredicates:self.predicates];
- NSCompoundPredicate *predicate = [NSCompoundPredicate notPredicateWithSubpredicate:predicates];
- layer.predicate = predicate;
- XCTAssertEqualObjects(predicate, layer.predicate);
- [self.mapView.style addLayer:layer];
-}
-
-@end
diff --git a/platform/darwin/test/MGLLineStyleLayerTests.m b/platform/darwin/test/MGLLineStyleLayerTests.m
deleted file mode 100644
index e877c1d57a..0000000000
--- a/platform/darwin/test/MGLLineStyleLayerTests.m
+++ /dev/null
@@ -1,106 +0,0 @@
-// This file is generated.
-// Edit platform/darwin/scripts/generate-style-code.js, then run `make style-code-darwin`.
-
-#import "MGLStyleLayerTests.h"
-
-@interface MGLLineLayerTests : MGLStyleLayerTests
-@end
-
-@implementation MGLLineLayerTests
-
-+ (NSString *)layerType {
- return @"line";
-}
-
-- (void)testLineLayer {
- NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"amsterdam" ofType:@"geojson"];
- NSURL *url = [NSURL fileURLWithPath:filePath];
- MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"sourceID" URL:url options:nil];
- [self.mapView.style addSource:source];
- MGLLineStyleLayer *layer = [[MGLLineStyleLayer alloc] initWithIdentifier:@"layerID" source:source];
- [self.mapView.style addLayer:layer];
-
- layer.lineCap = [MGLRuntimeStylingHelper testEnum:MGLLineCapSquare type:@encode(MGLLineCap)];
- layer.lineJoin = [MGLRuntimeStylingHelper testEnum:MGLLineJoinMiter type:@encode(MGLLineJoin)];
- layer.lineMiterLimit = [MGLRuntimeStylingHelper testNumber];
- layer.lineRoundLimit = [MGLRuntimeStylingHelper testNumber];
- layer.lineBlur = [MGLRuntimeStylingHelper testNumber];
- layer.lineColor = [MGLRuntimeStylingHelper testColor];
- layer.lineDashPattern = [MGLRuntimeStylingHelper testDashArray];
- layer.lineGapWidth = [MGLRuntimeStylingHelper testNumber];
- layer.lineOffset = [MGLRuntimeStylingHelper testNumber];
- layer.lineOpacity = [MGLRuntimeStylingHelper testNumber];
- layer.linePattern = [MGLRuntimeStylingHelper testString];
- layer.lineTranslate = [MGLRuntimeStylingHelper testOffset];
- layer.lineTranslateAnchor = [MGLRuntimeStylingHelper testEnum:MGLLineTranslateAnchorViewport type:@encode(MGLLineTranslateAnchor)];
- layer.lineWidth = [MGLRuntimeStylingHelper testNumber];
-
- MGLLineStyleLayer *gLayer = (MGLLineStyleLayer *)[self.mapView.style layerWithIdentifier:@"layerID"];
- XCTAssertTrue([gLayer isKindOfClass:[MGLLineStyleLayer class]]);
- XCTAssert([gLayer.lineCap isKindOfClass:[MGLStyleConstantValue class]]);
- XCTAssertEqualObjects(gLayer.lineCap, [MGLRuntimeStylingHelper testEnum:MGLLineCapSquare type:@encode(MGLLineCap)]);
- XCTAssert([gLayer.lineJoin isKindOfClass:[MGLStyleConstantValue class]]);
- XCTAssertEqualObjects(gLayer.lineJoin, [MGLRuntimeStylingHelper testEnum:MGLLineJoinMiter type:@encode(MGLLineJoin)]);
- XCTAssertEqualObjects(gLayer.lineMiterLimit, [MGLRuntimeStylingHelper testNumber]);
- XCTAssertEqualObjects(gLayer.lineRoundLimit, [MGLRuntimeStylingHelper testNumber]);
- XCTAssertEqualObjects(gLayer.lineBlur, [MGLRuntimeStylingHelper testNumber]);
- XCTAssertEqualObjects(gLayer.lineColor, [MGLRuntimeStylingHelper testColor]);
- XCTAssertEqualObjects(gLayer.lineDashPattern, [MGLRuntimeStylingHelper testDashArray]);
- XCTAssertEqualObjects(gLayer.lineGapWidth, [MGLRuntimeStylingHelper testNumber]);
- XCTAssertEqualObjects(gLayer.lineOffset, [MGLRuntimeStylingHelper testNumber]);
- XCTAssertEqualObjects(gLayer.lineOpacity, [MGLRuntimeStylingHelper testNumber]);
- XCTAssertEqualObjects(gLayer.linePattern, [MGLRuntimeStylingHelper testString]);
- XCTAssertEqualObjects(gLayer.lineTranslate, [MGLRuntimeStylingHelper testOffset]);
- XCTAssert([gLayer.lineTranslateAnchor isKindOfClass:[MGLStyleConstantValue class]]);
- XCTAssertEqualObjects(gLayer.lineTranslateAnchor, [MGLRuntimeStylingHelper testEnum:MGLLineTranslateAnchorViewport type:@encode(MGLLineTranslateAnchor)]);
- XCTAssertEqualObjects(gLayer.lineWidth, [MGLRuntimeStylingHelper testNumber]);
-
- layer.lineCap = [MGLRuntimeStylingHelper testEnumFunction:MGLLineCapSquare type:@encode(MGLLineCap)];
- layer.lineJoin = [MGLRuntimeStylingHelper testEnumFunction:MGLLineJoinMiter type:@encode(MGLLineJoin)];
- layer.lineMiterLimit = [MGLRuntimeStylingHelper testNumberFunction];
- layer.lineRoundLimit = [MGLRuntimeStylingHelper testNumberFunction];
- layer.lineBlur = [MGLRuntimeStylingHelper testNumberFunction];
- layer.lineColor = [MGLRuntimeStylingHelper testColorFunction];
- layer.lineDashPattern = [MGLRuntimeStylingHelper testDashArrayFunction];
- layer.lineGapWidth = [MGLRuntimeStylingHelper testNumberFunction];
- layer.lineOffset = [MGLRuntimeStylingHelper testNumberFunction];
- layer.lineOpacity = [MGLRuntimeStylingHelper testNumberFunction];
- layer.linePattern = [MGLRuntimeStylingHelper testStringFunction];
- layer.lineTranslate = [MGLRuntimeStylingHelper testOffsetFunction];
- layer.lineTranslateAnchor = [MGLRuntimeStylingHelper testEnumFunction:MGLLineTranslateAnchorViewport type:@encode(MGLLineTranslateAnchor)];
- layer.lineWidth = [MGLRuntimeStylingHelper testNumberFunction];
-
- XCTAssertEqualObjects(gLayer.lineCap, [MGLRuntimeStylingHelper testEnumFunction:MGLLineCapSquare type:@encode(MGLLineCap)]);
- XCTAssertEqualObjects(gLayer.lineJoin, [MGLRuntimeStylingHelper testEnumFunction:MGLLineJoinMiter type:@encode(MGLLineJoin)]);
- XCTAssertEqualObjects(gLayer.lineMiterLimit, [MGLRuntimeStylingHelper testNumberFunction]);
- XCTAssertEqualObjects(gLayer.lineRoundLimit, [MGLRuntimeStylingHelper testNumberFunction]);
- XCTAssertEqualObjects(gLayer.lineBlur, [MGLRuntimeStylingHelper testNumberFunction]);
- XCTAssertEqualObjects(gLayer.lineColor, [MGLRuntimeStylingHelper testColorFunction]);
- XCTAssertEqualObjects(gLayer.lineDashPattern, [MGLRuntimeStylingHelper testDashArrayFunction]);
- XCTAssertEqualObjects(gLayer.lineGapWidth, [MGLRuntimeStylingHelper testNumberFunction]);
- XCTAssertEqualObjects(gLayer.lineOffset, [MGLRuntimeStylingHelper testNumberFunction]);
- XCTAssertEqualObjects(gLayer.lineOpacity, [MGLRuntimeStylingHelper testNumberFunction]);
- XCTAssertEqualObjects(gLayer.linePattern, [MGLRuntimeStylingHelper testStringFunction]);
- XCTAssertEqualObjects(gLayer.lineTranslate, [MGLRuntimeStylingHelper testOffsetFunction]);
- XCTAssertEqualObjects(gLayer.lineTranslateAnchor, [MGLRuntimeStylingHelper testEnumFunction:MGLLineTranslateAnchorViewport type:@encode(MGLLineTranslateAnchor)]);
- XCTAssertEqualObjects(gLayer.lineWidth, [MGLRuntimeStylingHelper testNumberFunction]);
-}
-
-- (void)testPropertyNames {
- [self testPropertyName:@"line-cap" isBoolean:NO];
- [self testPropertyName:@"line-join" isBoolean:NO];
- [self testPropertyName:@"line-miter-limit" isBoolean:NO];
- [self testPropertyName:@"line-round-limit" isBoolean:NO];
- [self testPropertyName:@"line-blur" isBoolean:NO];
- [self testPropertyName:@"line-color" isBoolean:NO];
- [self testPropertyName:@"line-dash-pattern" isBoolean:NO];
- [self testPropertyName:@"line-gap-width" isBoolean:NO];
- [self testPropertyName:@"line-offset" isBoolean:NO];
- [self testPropertyName:@"line-opacity" isBoolean:NO];
- [self testPropertyName:@"line-pattern" isBoolean:NO];
- [self testPropertyName:@"line-translate" isBoolean:NO];
- [self testPropertyName:@"line-translate-anchor" isBoolean:NO];
- [self testPropertyName:@"line-width" isBoolean:NO];
-}
-
-@end
diff --git a/platform/darwin/test/MGLLineStyleLayerTests.mm b/platform/darwin/test/MGLLineStyleLayerTests.mm
new file mode 100644
index 0000000000..24a9d7afea
--- /dev/null
+++ b/platform/darwin/test/MGLLineStyleLayerTests.mm
@@ -0,0 +1,557 @@
+// This file is generated.
+// Edit platform/darwin/scripts/generate-style-code.js, then run `make style-code-darwin`.
+
+#import "MGLStyleLayerTests.h"
+
+#import "MGLStyleLayer_Private.h"
+
+#include <mbgl/style/layers/line_layer.hpp>
+
+@interface MGLLineLayerTests : MGLStyleLayerTests
+@end
+
+@implementation MGLLineLayerTests
+
++ (NSString *)layerType {
+ return @"line";
+}
+
+- (void)testPredicates {
+ MGLPointFeature *feature = [[MGLPointFeature alloc] init];
+ MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"sourceID" shape:feature options:nil];
+ MGLLineStyleLayer *layer = [[MGLLineStyleLayer alloc] initWithIdentifier:@"layerID" source:source];
+
+ XCTAssertNil(layer.sourceLayerIdentifier);
+ layer.sourceLayerIdentifier = @"layerID";
+ XCTAssertEqualObjects(layer.sourceLayerIdentifier, @"layerID");
+ layer.sourceLayerIdentifier = nil;
+ XCTAssertNil(layer.sourceLayerIdentifier);
+
+ XCTAssertNil(layer.predicate);
+ layer.predicate = [NSPredicate predicateWithValue:NO];
+ XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithValue:NO]);
+ layer.predicate = nil;
+ XCTAssertNil(layer.predicate);
+}
+
+- (void)testProperties {
+ MGLPointFeature *feature = [[MGLPointFeature alloc] init];
+ MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"sourceID" shape:feature options:nil];
+
+ MGLLineStyleLayer *layer = [[MGLLineStyleLayer alloc] initWithIdentifier:@"layerID" source:source];
+ XCTAssertNotEqual(layer.rawLayer, nullptr);
+ XCTAssertTrue(layer.rawLayer->is<mbgl::style::LineLayer>());
+ auto rawLayer = layer.rawLayer->as<mbgl::style::LineLayer>();
+
+ // line-cap
+ {
+ XCTAssertTrue(rawLayer->getLineCap().isUndefined(),
+ @"line-cap should be unset initially.");
+ MGLStyleValue<NSValue *> *defaultStyleValue = layer.lineCap;
+
+ MGLStyleValue<NSValue *> *styleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLLineCap:MGLLineCapSquare]];
+ layer.lineCap = styleValue;
+ mbgl::style::PropertyValue<mbgl::style::LineCapType> propertyValue = { mbgl::style::LineCapType::Square };
+ XCTAssertEqual(rawLayer->getLineCap(), propertyValue,
+ @"Setting lineCap to a constant value should update line-cap.");
+ XCTAssertEqualObjects(layer.lineCap, styleValue,
+ @"lineCap should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSValue *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.lineCap = styleValue;
+ propertyValue = { mbgl::style::Function<mbgl::style::LineCapType> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getLineCap(), propertyValue,
+ @"Setting lineCap to a function should update line-cap.");
+ XCTAssertEqualObjects(layer.lineCap, styleValue,
+ @"lineCap should round-trip functions.");
+
+ layer.lineCap = nil;
+ XCTAssertTrue(rawLayer->getLineCap().isUndefined(),
+ @"Unsetting lineCap should return line-cap to the default value.");
+ XCTAssertEqualObjects(layer.lineCap, defaultStyleValue,
+ @"lineCap should return the default value after being unset.");
+ }
+
+ // line-join
+ {
+ XCTAssertTrue(rawLayer->getLineJoin().isUndefined(),
+ @"line-join should be unset initially.");
+ MGLStyleValue<NSValue *> *defaultStyleValue = layer.lineJoin;
+
+ MGLStyleValue<NSValue *> *styleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLLineJoin:MGLLineJoinMiter]];
+ layer.lineJoin = styleValue;
+ mbgl::style::PropertyValue<mbgl::style::LineJoinType> propertyValue = { mbgl::style::LineJoinType::Miter };
+ XCTAssertEqual(rawLayer->getLineJoin(), propertyValue,
+ @"Setting lineJoin to a constant value should update line-join.");
+ XCTAssertEqualObjects(layer.lineJoin, styleValue,
+ @"lineJoin should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSValue *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.lineJoin = styleValue;
+ propertyValue = { mbgl::style::Function<mbgl::style::LineJoinType> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getLineJoin(), propertyValue,
+ @"Setting lineJoin to a function should update line-join.");
+ XCTAssertEqualObjects(layer.lineJoin, styleValue,
+ @"lineJoin should round-trip functions.");
+
+ layer.lineJoin = nil;
+ XCTAssertTrue(rawLayer->getLineJoin().isUndefined(),
+ @"Unsetting lineJoin should return line-join to the default value.");
+ XCTAssertEqualObjects(layer.lineJoin, defaultStyleValue,
+ @"lineJoin should return the default value after being unset.");
+ }
+
+ // line-miter-limit
+ {
+ XCTAssertTrue(rawLayer->getLineMiterLimit().isUndefined(),
+ @"line-miter-limit should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.lineMiterLimit;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.lineMiterLimit = styleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getLineMiterLimit(), propertyValue,
+ @"Setting lineMiterLimit to a constant value should update line-miter-limit.");
+ XCTAssertEqualObjects(layer.lineMiterLimit, styleValue,
+ @"lineMiterLimit should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.lineMiterLimit = styleValue;
+ propertyValue = { mbgl::style::Function<float> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getLineMiterLimit(), propertyValue,
+ @"Setting lineMiterLimit to a function should update line-miter-limit.");
+ XCTAssertEqualObjects(layer.lineMiterLimit, styleValue,
+ @"lineMiterLimit should round-trip functions.");
+
+ layer.lineMiterLimit = nil;
+ XCTAssertTrue(rawLayer->getLineMiterLimit().isUndefined(),
+ @"Unsetting lineMiterLimit should return line-miter-limit to the default value.");
+ XCTAssertEqualObjects(layer.lineMiterLimit, defaultStyleValue,
+ @"lineMiterLimit should return the default value after being unset.");
+ }
+
+ // line-round-limit
+ {
+ XCTAssertTrue(rawLayer->getLineRoundLimit().isUndefined(),
+ @"line-round-limit should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.lineRoundLimit;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.lineRoundLimit = styleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getLineRoundLimit(), propertyValue,
+ @"Setting lineRoundLimit to a constant value should update line-round-limit.");
+ XCTAssertEqualObjects(layer.lineRoundLimit, styleValue,
+ @"lineRoundLimit should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.lineRoundLimit = styleValue;
+ propertyValue = { mbgl::style::Function<float> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getLineRoundLimit(), propertyValue,
+ @"Setting lineRoundLimit to a function should update line-round-limit.");
+ XCTAssertEqualObjects(layer.lineRoundLimit, styleValue,
+ @"lineRoundLimit should round-trip functions.");
+
+ layer.lineRoundLimit = nil;
+ XCTAssertTrue(rawLayer->getLineRoundLimit().isUndefined(),
+ @"Unsetting lineRoundLimit should return line-round-limit to the default value.");
+ XCTAssertEqualObjects(layer.lineRoundLimit, defaultStyleValue,
+ @"lineRoundLimit should return the default value after being unset.");
+ }
+
+ // line-blur
+ {
+ XCTAssertTrue(rawLayer->getLineBlur().isUndefined(),
+ @"line-blur should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.lineBlur;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.lineBlur = styleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getLineBlur(), propertyValue,
+ @"Setting lineBlur to a constant value should update line-blur.");
+ XCTAssertEqualObjects(layer.lineBlur, styleValue,
+ @"lineBlur should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.lineBlur = styleValue;
+ propertyValue = { mbgl::style::Function<float> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getLineBlur(), propertyValue,
+ @"Setting lineBlur to a function should update line-blur.");
+ XCTAssertEqualObjects(layer.lineBlur, styleValue,
+ @"lineBlur should round-trip functions.");
+
+ layer.lineBlur = nil;
+ XCTAssertTrue(rawLayer->getLineBlur().isUndefined(),
+ @"Unsetting lineBlur should return line-blur to the default value.");
+ XCTAssertEqualObjects(layer.lineBlur, defaultStyleValue,
+ @"lineBlur should return the default value after being unset.");
+ }
+
+ // line-color
+ {
+ XCTAssertTrue(rawLayer->getLineColor().isUndefined(),
+ @"line-color should be unset initially.");
+ MGLStyleValue<MGLColor *> *defaultStyleValue = layer.lineColor;
+
+ MGLStyleValue<MGLColor *> *styleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]];
+ layer.lineColor = styleValue;
+ mbgl::style::PropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } };
+ XCTAssertEqual(rawLayer->getLineColor(), propertyValue,
+ @"Setting lineColor to a constant value should update line-color.");
+ XCTAssertEqualObjects(layer.lineColor, styleValue,
+ @"lineColor should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<MGLColor *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.lineColor = styleValue;
+ propertyValue = { mbgl::style::Function<mbgl::Color> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getLineColor(), propertyValue,
+ @"Setting lineColor to a function should update line-color.");
+ XCTAssertEqualObjects(layer.lineColor, styleValue,
+ @"lineColor should round-trip functions.");
+
+ layer.lineColor = nil;
+ XCTAssertTrue(rawLayer->getLineColor().isUndefined(),
+ @"Unsetting lineColor should return line-color to the default value.");
+ XCTAssertEqualObjects(layer.lineColor, defaultStyleValue,
+ @"lineColor should return the default value after being unset.");
+ }
+
+ // line-dasharray
+ {
+ XCTAssertTrue(rawLayer->getLineDasharray().isUndefined(),
+ @"line-dasharray should be unset initially.");
+ MGLStyleValue<NSArray<NSNumber *> *> *defaultStyleValue = layer.lineDashPattern;
+
+ MGLStyleValue<NSArray<NSNumber *> *> *styleValue = [MGLStyleValue<NSArray<NSNumber *> *> valueWithRawValue:@[@1, @2]];
+ layer.lineDashPattern = styleValue;
+ mbgl::style::PropertyValue<std::vector<float>> propertyValue = { {1, 2} };
+ XCTAssertEqual(rawLayer->getLineDasharray(), propertyValue,
+ @"Setting lineDashPattern to a constant value should update line-dasharray.");
+ XCTAssertEqualObjects(layer.lineDashPattern, styleValue,
+ @"lineDashPattern should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSArray<NSNumber *> *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.lineDashPattern = styleValue;
+ propertyValue = { mbgl::style::Function<std::vector<float>> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getLineDasharray(), propertyValue,
+ @"Setting lineDashPattern to a function should update line-dasharray.");
+ XCTAssertEqualObjects(layer.lineDashPattern, styleValue,
+ @"lineDashPattern should round-trip functions.");
+
+ layer.lineDashPattern = nil;
+ XCTAssertTrue(rawLayer->getLineDasharray().isUndefined(),
+ @"Unsetting lineDashPattern should return line-dasharray to the default value.");
+ XCTAssertEqualObjects(layer.lineDashPattern, defaultStyleValue,
+ @"lineDashPattern should return the default value after being unset.");
+ }
+
+ // line-gap-width
+ {
+ XCTAssertTrue(rawLayer->getLineGapWidth().isUndefined(),
+ @"line-gap-width should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.lineGapWidth;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.lineGapWidth = styleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getLineGapWidth(), propertyValue,
+ @"Setting lineGapWidth to a constant value should update line-gap-width.");
+ XCTAssertEqualObjects(layer.lineGapWidth, styleValue,
+ @"lineGapWidth should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.lineGapWidth = styleValue;
+ propertyValue = { mbgl::style::Function<float> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getLineGapWidth(), propertyValue,
+ @"Setting lineGapWidth to a function should update line-gap-width.");
+ XCTAssertEqualObjects(layer.lineGapWidth, styleValue,
+ @"lineGapWidth should round-trip functions.");
+
+ layer.lineGapWidth = nil;
+ XCTAssertTrue(rawLayer->getLineGapWidth().isUndefined(),
+ @"Unsetting lineGapWidth should return line-gap-width to the default value.");
+ XCTAssertEqualObjects(layer.lineGapWidth, defaultStyleValue,
+ @"lineGapWidth should return the default value after being unset.");
+ }
+
+ // line-offset
+ {
+ XCTAssertTrue(rawLayer->getLineOffset().isUndefined(),
+ @"line-offset should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.lineOffset;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.lineOffset = styleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getLineOffset(), propertyValue,
+ @"Setting lineOffset to a constant value should update line-offset.");
+ XCTAssertEqualObjects(layer.lineOffset, styleValue,
+ @"lineOffset should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.lineOffset = styleValue;
+ propertyValue = { mbgl::style::Function<float> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getLineOffset(), propertyValue,
+ @"Setting lineOffset to a function should update line-offset.");
+ XCTAssertEqualObjects(layer.lineOffset, styleValue,
+ @"lineOffset should round-trip functions.");
+
+ layer.lineOffset = nil;
+ XCTAssertTrue(rawLayer->getLineOffset().isUndefined(),
+ @"Unsetting lineOffset should return line-offset to the default value.");
+ XCTAssertEqualObjects(layer.lineOffset, defaultStyleValue,
+ @"lineOffset should return the default value after being unset.");
+ }
+
+ // line-opacity
+ {
+ XCTAssertTrue(rawLayer->getLineOpacity().isUndefined(),
+ @"line-opacity should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.lineOpacity;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.lineOpacity = styleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getLineOpacity(), propertyValue,
+ @"Setting lineOpacity to a constant value should update line-opacity.");
+ XCTAssertEqualObjects(layer.lineOpacity, styleValue,
+ @"lineOpacity should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.lineOpacity = styleValue;
+ propertyValue = { mbgl::style::Function<float> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getLineOpacity(), propertyValue,
+ @"Setting lineOpacity to a function should update line-opacity.");
+ XCTAssertEqualObjects(layer.lineOpacity, styleValue,
+ @"lineOpacity should round-trip functions.");
+
+ layer.lineOpacity = nil;
+ XCTAssertTrue(rawLayer->getLineOpacity().isUndefined(),
+ @"Unsetting lineOpacity should return line-opacity to the default value.");
+ XCTAssertEqualObjects(layer.lineOpacity, defaultStyleValue,
+ @"lineOpacity should return the default value after being unset.");
+ }
+
+ // line-pattern
+ {
+ XCTAssertTrue(rawLayer->getLinePattern().isUndefined(),
+ @"line-pattern should be unset initially.");
+ MGLStyleValue<NSString *> *defaultStyleValue = layer.linePattern;
+
+ MGLStyleValue<NSString *> *styleValue = [MGLStyleValue<NSString *> valueWithRawValue:@"Line Pattern"];
+ layer.linePattern = styleValue;
+ mbgl::style::PropertyValue<std::string> propertyValue = { "Line Pattern" };
+ XCTAssertEqual(rawLayer->getLinePattern(), propertyValue,
+ @"Setting linePattern to a constant value should update line-pattern.");
+ XCTAssertEqualObjects(layer.linePattern, styleValue,
+ @"linePattern should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSString *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.linePattern = styleValue;
+ propertyValue = { mbgl::style::Function<std::string> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getLinePattern(), propertyValue,
+ @"Setting linePattern to a function should update line-pattern.");
+ XCTAssertEqualObjects(layer.linePattern, styleValue,
+ @"linePattern should round-trip functions.");
+
+ layer.linePattern = nil;
+ XCTAssertTrue(rawLayer->getLinePattern().isUndefined(),
+ @"Unsetting linePattern should return line-pattern to the default value.");
+ XCTAssertEqualObjects(layer.linePattern, defaultStyleValue,
+ @"linePattern should return the default value after being unset.");
+ }
+
+ // line-translate
+ {
+ XCTAssertTrue(rawLayer->getLineTranslate().isUndefined(),
+ @"line-translate should be unset initially.");
+ MGLStyleValue<NSValue *> *defaultStyleValue = layer.lineTranslation;
+
+ MGLStyleValue<NSValue *> *styleValue = [MGLStyleValue<NSValue *> valueWithRawValue:
+#if TARGET_OS_IPHONE
+ [NSValue valueWithCGVector:CGVectorMake(1, 1)]
+#else
+ [NSValue valueWithMGLVector:CGVectorMake(1, -1)]
+#endif
+ ];
+ layer.lineTranslation = styleValue;
+ mbgl::style::PropertyValue<std::array<float, 2>> propertyValue = { { 1, 1 } };
+ XCTAssertEqual(rawLayer->getLineTranslate(), propertyValue,
+ @"Setting lineTranslation to a constant value should update line-translate.");
+ XCTAssertEqualObjects(layer.lineTranslation, styleValue,
+ @"lineTranslation should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSValue *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.lineTranslation = styleValue;
+ propertyValue = { mbgl::style::Function<std::array<float, 2>> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getLineTranslate(), propertyValue,
+ @"Setting lineTranslation to a function should update line-translate.");
+ XCTAssertEqualObjects(layer.lineTranslation, styleValue,
+ @"lineTranslation should round-trip functions.");
+
+ layer.lineTranslation = nil;
+ XCTAssertTrue(rawLayer->getLineTranslate().isUndefined(),
+ @"Unsetting lineTranslation should return line-translate to the default value.");
+ XCTAssertEqualObjects(layer.lineTranslation, defaultStyleValue,
+ @"lineTranslation should return the default value after being unset.");
+ }
+
+ // line-translate-anchor
+ {
+ XCTAssertTrue(rawLayer->getLineTranslateAnchor().isUndefined(),
+ @"line-translate-anchor should be unset initially.");
+ MGLStyleValue<NSValue *> *defaultStyleValue = layer.lineTranslationAnchor;
+
+ MGLStyleValue<NSValue *> *styleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLLineTranslationAnchor:MGLLineTranslationAnchorViewport]];
+ layer.lineTranslationAnchor = styleValue;
+ mbgl::style::PropertyValue<mbgl::style::TranslateAnchorType> propertyValue = { mbgl::style::TranslateAnchorType::Viewport };
+ XCTAssertEqual(rawLayer->getLineTranslateAnchor(), propertyValue,
+ @"Setting lineTranslationAnchor to a constant value should update line-translate-anchor.");
+ XCTAssertEqualObjects(layer.lineTranslationAnchor, styleValue,
+ @"lineTranslationAnchor should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSValue *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.lineTranslationAnchor = styleValue;
+ propertyValue = { mbgl::style::Function<mbgl::style::TranslateAnchorType> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getLineTranslateAnchor(), propertyValue,
+ @"Setting lineTranslationAnchor to a function should update line-translate-anchor.");
+ XCTAssertEqualObjects(layer.lineTranslationAnchor, styleValue,
+ @"lineTranslationAnchor should round-trip functions.");
+
+ layer.lineTranslationAnchor = nil;
+ XCTAssertTrue(rawLayer->getLineTranslateAnchor().isUndefined(),
+ @"Unsetting lineTranslationAnchor should return line-translate-anchor to the default value.");
+ XCTAssertEqualObjects(layer.lineTranslationAnchor, defaultStyleValue,
+ @"lineTranslationAnchor should return the default value after being unset.");
+ }
+
+ // line-width
+ {
+ XCTAssertTrue(rawLayer->getLineWidth().isUndefined(),
+ @"line-width should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.lineWidth;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.lineWidth = styleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getLineWidth(), propertyValue,
+ @"Setting lineWidth to a constant value should update line-width.");
+ XCTAssertEqualObjects(layer.lineWidth, styleValue,
+ @"lineWidth should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.lineWidth = styleValue;
+ propertyValue = { mbgl::style::Function<float> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getLineWidth(), propertyValue,
+ @"Setting lineWidth to a function should update line-width.");
+ XCTAssertEqualObjects(layer.lineWidth, styleValue,
+ @"lineWidth should round-trip functions.");
+
+ layer.lineWidth = nil;
+ XCTAssertTrue(rawLayer->getLineWidth().isUndefined(),
+ @"Unsetting lineWidth should return line-width to the default value.");
+ XCTAssertEqualObjects(layer.lineWidth, defaultStyleValue,
+ @"lineWidth should return the default value after being unset.");
+ }
+}
+
+- (void)testPropertyNames {
+ [self testPropertyName:@"line-cap" isBoolean:NO];
+ [self testPropertyName:@"line-join" isBoolean:NO];
+ [self testPropertyName:@"line-miter-limit" isBoolean:NO];
+ [self testPropertyName:@"line-round-limit" isBoolean:NO];
+ [self testPropertyName:@"line-blur" isBoolean:NO];
+ [self testPropertyName:@"line-color" isBoolean:NO];
+ [self testPropertyName:@"line-dash-pattern" isBoolean:NO];
+ [self testPropertyName:@"line-gap-width" isBoolean:NO];
+ [self testPropertyName:@"line-offset" isBoolean:NO];
+ [self testPropertyName:@"line-opacity" isBoolean:NO];
+ [self testPropertyName:@"line-pattern" isBoolean:NO];
+ [self testPropertyName:@"line-translation" isBoolean:NO];
+ [self testPropertyName:@"line-translation-anchor" isBoolean:NO];
+ [self testPropertyName:@"line-width" isBoolean:NO];
+}
+
+- (void)testValueAdditions {
+ XCTAssertEqual([NSValue valueWithMGLLineCap:MGLLineCapButt].MGLLineCapValue, MGLLineCapButt);
+ XCTAssertEqual([NSValue valueWithMGLLineCap:MGLLineCapRound].MGLLineCapValue, MGLLineCapRound);
+ XCTAssertEqual([NSValue valueWithMGLLineCap:MGLLineCapSquare].MGLLineCapValue, MGLLineCapSquare);
+ XCTAssertEqual([NSValue valueWithMGLLineJoin:MGLLineJoinBevel].MGLLineJoinValue, MGLLineJoinBevel);
+ XCTAssertEqual([NSValue valueWithMGLLineJoin:MGLLineJoinRound].MGLLineJoinValue, MGLLineJoinRound);
+ XCTAssertEqual([NSValue valueWithMGLLineJoin:MGLLineJoinMiter].MGLLineJoinValue, MGLLineJoinMiter);
+ XCTAssertEqual([NSValue valueWithMGLLineTranslationAnchor:MGLLineTranslationAnchorMap].MGLLineTranslationAnchorValue, MGLLineTranslationAnchorMap);
+ XCTAssertEqual([NSValue valueWithMGLLineTranslationAnchor:MGLLineTranslationAnchorViewport].MGLLineTranslationAnchorValue, MGLLineTranslationAnchorViewport);
+}
+
+@end
diff --git a/platform/darwin/test/MGLNSStringAdditionsTests.m b/platform/darwin/test/MGLNSStringAdditionsTests.m
new file mode 100644
index 0000000000..0c8a9f8143
--- /dev/null
+++ b/platform/darwin/test/MGLNSStringAdditionsTests.m
@@ -0,0 +1,42 @@
+#import <XCTest/XCTest.h>
+
+#import "NSString+MGLAdditions.h"
+
+@interface MGLNSStringAdditionsTests : XCTestCase
+
+@end
+
+@implementation MGLNSStringAdditionsTests
+
+- (void)testTitleCasedString {
+ NSLocale *locale = [NSLocale currentLocale];
+
+ XCTAssertEqualObjects([@"© OpenStreetMap" mgl_titleCasedStringWithLocale:locale], @"© OpenStreetMap");
+ XCTAssertEqualObjects([@"© OSM" mgl_titleCasedStringWithLocale:locale], @"© OSM");
+
+ XCTAssertEqualObjects([@"Improve this map" mgl_titleCasedStringWithLocale:locale], @"Improve This Map");
+ XCTAssertEqualObjects([@"Improve This Map" mgl_titleCasedStringWithLocale:locale], @"Improve This Map");
+
+ XCTAssertEqualObjects([@"Improve the map" mgl_titleCasedStringWithLocale:locale], @"Improve the Map");
+ XCTAssertEqualObjects([@"Improve The Map" mgl_titleCasedStringWithLocale:locale], @"Improve The Map");
+
+ XCTAssertEqualObjects([@"Improve a map" mgl_titleCasedStringWithLocale:locale], @"Improve a Map");
+ XCTAssertEqualObjects([@"Improve A Map" mgl_titleCasedStringWithLocale:locale], @"Improve A Map");
+
+ XCTAssertEqualObjects([@"Improve for the map" mgl_titleCasedStringWithLocale:locale], @"Improve for the Map");
+ XCTAssertEqualObjects([@"Improve For The Map" mgl_titleCasedStringWithLocale:locale], @"Improve For The Map");
+
+ XCTAssertEqualObjects([@"Improve and map" mgl_titleCasedStringWithLocale:locale], @"Improve and Map");
+ XCTAssertEqualObjects([@"Improve And Map" mgl_titleCasedStringWithLocale:locale], @"Improve And Map");
+
+ XCTAssertEqualObjects([@"Improve while mapping" mgl_titleCasedStringWithLocale:locale], @"Improve While Mapping");
+ XCTAssertEqualObjects([@"Improve While Mapping" mgl_titleCasedStringWithLocale:locale], @"Improve While Mapping");
+
+ XCTAssertEqualObjects([@"Improve with the map" mgl_titleCasedStringWithLocale:locale], @"Improve With the Map");
+ XCTAssertEqualObjects([@"Improve With The Map" mgl_titleCasedStringWithLocale:locale], @"Improve With The Map");
+
+ XCTAssertEqualObjects([@"Improve this iPhone" mgl_titleCasedStringWithLocale:locale], @"Improve This iPhone");
+ XCTAssertEqualObjects([@"Improve This iPhone" mgl_titleCasedStringWithLocale:locale], @"Improve This iPhone");
+}
+
+@end
diff --git a/platform/darwin/test/MGLPredicateTests.mm b/platform/darwin/test/MGLPredicateTests.mm
new file mode 100644
index 0000000000..fbd144d28a
--- /dev/null
+++ b/platform/darwin/test/MGLPredicateTests.mm
@@ -0,0 +1,419 @@
+#import <XCTest/XCTest.h>
+#import <Mapbox/Mapbox.h>
+
+#import "NSPredicate+MGLAdditions.h"
+#import "MGLValueEvaluator.h"
+
+namespace mbgl {
+ namespace style {
+ bool operator!=(const Filter &a, const Filter &b) {
+ return !(a == b);
+ }
+ }
+}
+
+#define MGLAssertEqualFilters(actual, expected, ...) \
+ XCTAssertTrue(actual.is<__typeof__(expected)>()); \
+ if (actual.is<__typeof__(expected)>()) { \
+ XCTAssertEqual(actual.get<__typeof__(expected)>(), expected, __VA_ARGS__); \
+ }
+
+@interface MGLPredicateTests : XCTestCase
+@end
+
+@implementation MGLPredicateTests
+
+- (void)testFilterization {
+ {
+ auto actual = [NSPredicate predicateWithValue:YES].mgl_filter;
+ mbgl::style::AllFilter expected;
+ MGLAssertEqualFilters(actual, expected);
+ }
+
+ {
+ auto actual = [NSPredicate predicateWithValue:NO].mgl_filter;
+ mbgl::style::AnyFilter expected;
+ MGLAssertEqualFilters(actual, expected);
+ }
+
+ {
+ auto actual = [NSPredicate predicateWithFormat:@"a = 'b'"].mgl_filter;
+ mbgl::style::EqualsFilter expected = { .key = "a", .value = std::string("b") };
+ MGLAssertEqualFilters(actual, expected);
+ }
+
+ {
+ auto actual = [NSPredicate predicateWithFormat:@"a = nil"].mgl_filter;
+ mbgl::style::NotHasFilter expected = { .key = "a" };
+ MGLAssertEqualFilters(actual, expected);
+ }
+
+ {
+ auto actual = [NSPredicate predicateWithFormat:@"a != 'b'"].mgl_filter;
+ mbgl::style::NotEqualsFilter expected = { .key = "a", .value = std::string("b") };
+ MGLAssertEqualFilters(actual, expected);
+ }
+
+ {
+ auto actual = [NSPredicate predicateWithFormat:@"a != nil"].mgl_filter;
+ mbgl::style::HasFilter expected = { .key = "a" };
+ MGLAssertEqualFilters(actual, expected);
+ }
+
+ {
+ auto actual = [NSPredicate predicateWithFormat:@"a < 'b'"].mgl_filter;
+ mbgl::style::LessThanFilter expected = { .key = "a", .value = std::string("b") };
+ MGLAssertEqualFilters(actual, expected);
+ }
+
+ {
+ auto actual = [NSPredicate predicateWithFormat:@"a <= 'b'"].mgl_filter;
+ mbgl::style::LessThanEqualsFilter expected = { .key = "a", .value = std::string("b") };
+ MGLAssertEqualFilters(actual, expected);
+ }
+
+ {
+ auto actual = [NSPredicate predicateWithFormat:@"a > 'b'"].mgl_filter;
+ mbgl::style::GreaterThanFilter expected = { .key = "a", .value = std::string("b") };
+ MGLAssertEqualFilters(actual, expected);
+ }
+
+ {
+ auto actual = [NSPredicate predicateWithFormat:@"a >= 'b'"].mgl_filter;
+ mbgl::style::GreaterThanEqualsFilter expected = { .key = "a", .value = std::string("b") };
+ MGLAssertEqualFilters(actual, expected);
+ }
+
+ {
+ auto actual = [NSPredicate predicateWithFormat:@"a BETWEEN {'b', 'z'}"].mgl_filter;
+ mbgl::style::AllFilter expected = {
+ .filters = {
+ mbgl::style::GreaterThanEqualsFilter { .key = "a", .value = std::string("b") },
+ mbgl::style::LessThanEqualsFilter { .key = "a", .value = std::string("z") },
+ },
+ };
+ MGLAssertEqualFilters(actual, expected);
+ }
+
+ {
+ auto actual = [NSPredicate predicateWithFormat:@"a BETWEEN %@", @[@"b", @"z"]].mgl_filter;
+ mbgl::style::AllFilter expected = {
+ .filters = {
+ mbgl::style::GreaterThanEqualsFilter { .key = "a", .value = std::string("b") },
+ mbgl::style::LessThanEqualsFilter { .key = "a", .value = std::string("z") },
+ },
+ };
+ MGLAssertEqualFilters(actual, expected);
+ }
+
+ {
+ auto actual = [NSPredicate predicateWithFormat:@"a IN {'b', 'c'}"].mgl_filter;
+ mbgl::style::InFilter expected = { .key = "a", .values = { std::string("b"), std::string("c") } };
+ MGLAssertEqualFilters(actual, expected);
+ }
+
+ {
+ auto actual = [NSPredicate predicateWithFormat:@"a IN %@", @[@"b", @"c"]].mgl_filter;
+ mbgl::style::InFilter expected = { .key = "a", .values = { std::string("b"), std::string("c") } };
+ MGLAssertEqualFilters(actual, expected);
+ }
+
+ XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"'Mapbox' IN a"].mgl_filter, NSException, NSInvalidArgumentException);
+
+ {
+ auto actual = [NSPredicate predicateWithFormat:@"{'b', 'c'} CONTAINS a"].mgl_filter;
+ mbgl::style::InFilter expected = { .key = "a", .values = { std::string("b"), std::string("c") } };
+ MGLAssertEqualFilters(actual, expected);
+ }
+
+ {
+ auto actual = [NSPredicate predicateWithFormat:@"%@ CONTAINS a", @[@"b", @"c"]].mgl_filter;
+ mbgl::style::InFilter expected = { .key = "a", .values = { std::string("b"), std::string("c") } };
+ MGLAssertEqualFilters(actual, expected);
+ }
+
+ XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a CONTAINS 'Mapbox'"].mgl_filter, NSException, NSInvalidArgumentException);
+
+ {
+ auto actual = [NSPredicate predicateWithFormat:@"a == 'b' AND c == 'd'"].mgl_filter;
+ mbgl::style::AllFilter expected = {
+ .filters = {
+ mbgl::style::EqualsFilter { .key = "a", .value = std::string("b") },
+ mbgl::style::EqualsFilter { .key = "c", .value = std::string("d") },
+ },
+ };
+ MGLAssertEqualFilters(actual, expected);
+ }
+
+ {
+ auto actual = [NSPredicate predicateWithFormat:@"a == 'b' OR c == 'd'"].mgl_filter;
+ mbgl::style::AnyFilter expected = {
+ .filters = {
+ mbgl::style::EqualsFilter { .key = "a", .value = std::string("b") },
+ mbgl::style::EqualsFilter { .key = "c", .value = std::string("d") },
+ },
+ };
+ MGLAssertEqualFilters(actual, expected);
+ }
+
+ {
+ auto actual = [NSPredicate predicateWithFormat:@"NOT(a == 'b' AND c == 'd')"].mgl_filter;
+ mbgl::style::NoneFilter expected = {
+ .filters = {
+ mbgl::style::AllFilter {
+ .filters = {
+ mbgl::style::EqualsFilter { .key = "a", .value = std::string("b") },
+ mbgl::style::EqualsFilter { .key = "c", .value = std::string("d") },
+ },
+ },
+ },
+ };
+ MGLAssertEqualFilters(actual, expected);
+ }
+
+ {
+ auto actual = [NSPredicate predicateWithFormat:@"NOT(a == 'b' OR c == 'd')"].mgl_filter;
+ mbgl::style::NoneFilter expected = {
+ .filters = {
+ mbgl::style::EqualsFilter { .key = "a", .value = std::string("b") },
+ mbgl::style::EqualsFilter { .key = "c", .value = std::string("d") },
+ },
+ };
+ MGLAssertEqualFilters(actual, expected);
+ }
+
+ {
+ auto actual = [NSPredicate predicateWithFormat:@"NOT a == nil"].mgl_filter;
+ mbgl::style::HasFilter expected = { .key = "a" };
+ MGLAssertEqualFilters(actual, expected);
+ }
+
+ {
+ auto actual = [NSPredicate predicateWithFormat:@"NOT a != nil"].mgl_filter;
+ mbgl::style::NotHasFilter expected = { .key = "a" };
+ MGLAssertEqualFilters(actual, expected);
+ }
+
+ {
+ auto actual = [NSPredicate predicateWithFormat:@"NOT a IN {'b', 'c'}"].mgl_filter;
+ mbgl::style::NotInFilter expected = { .key = "a", .values = { std::string("b"), std::string("c") } };
+ MGLAssertEqualFilters(actual, expected);
+ }
+
+ {
+ auto actual = [NSPredicate predicateWithFormat:@"NOT a IN %@", @[@"b", @"c"]].mgl_filter;
+ mbgl::style::NotInFilter expected = { .key = "a", .values = { std::string("b"), std::string("c") } };
+ MGLAssertEqualFilters(actual, expected);
+ }
+
+ {
+ auto actual = [NSPredicate predicateWithFormat:@"NOT {'b', 'c'} CONTAINS a"].mgl_filter;
+ mbgl::style::NotInFilter expected = { .key = "a", .values = { std::string("b"), std::string("c") } };
+ MGLAssertEqualFilters(actual, expected);
+ }
+
+ {
+ auto actual = [NSPredicate predicateWithFormat:@"NOT %@ CONTAINS a", @[@"b", @"c"]].mgl_filter;
+ mbgl::style::NotInFilter expected = { .key = "a", .values = { std::string("b"), std::string("c") } };
+ MGLAssertEqualFilters(actual, expected);
+ }
+
+ XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a BEGINSWITH 'L'"].mgl_filter, NSException, NSInvalidArgumentException);
+ XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a ENDSWITH 'itude'"].mgl_filter, NSException, NSInvalidArgumentException);
+ XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a LIKE 'glob?trotter'"].mgl_filter, NSException, NSInvalidArgumentException);
+ XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a MATCHES 'i\\w{18}n'"].mgl_filter, NSException, NSInvalidArgumentException);
+ NSPredicate *selectorPredicate = [NSPredicate predicateWithFormat:@"(SELF isKindOfClass: %@)", [MGLPolyline class]];
+ XCTAssertThrowsSpecificNamed(selectorPredicate.mgl_filter, NSException, NSInvalidArgumentException);
+
+ XCTAssertThrowsSpecificNamed([NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary<NSString *, id> * _Nullable bindings) {
+ XCTAssertTrue(NO, @"Predicate block should not be evaluated.");
+ return NO;
+ }].mgl_filter, NSException, NSInvalidArgumentException);
+}
+
+- (void)testPredication {
+ XCTAssertNil([NSPredicate mgl_predicateWithFilter:mbgl::style::NullFilter()]);
+
+ {
+ mbgl::style::EqualsFilter filter = { .key = "a", .value = std::string("b") };
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a = 'b'"]);
+ }
+
+ {
+ mbgl::style::NotHasFilter filter = { .key = "a" };
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a = nil"]);
+ }
+
+ {
+ mbgl::style::NotEqualsFilter filter = { .key = "a", .value = std::string("b") };
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a != 'b'"]);
+ }
+
+ {
+ mbgl::style::HasFilter filter = { .key = "a" };
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a != nil"]);
+ }
+
+ {
+ mbgl::style::LessThanFilter filter = { .key = "a", .value = std::string("b") };
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a < 'b'"]);
+ }
+
+ {
+ mbgl::style::LessThanEqualsFilter filter = { .key = "a", .value = std::string("b") };
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a <= 'b'"]);
+ }
+
+ {
+ mbgl::style::GreaterThanFilter filter = { .key = "a", .value = std::string("b") };
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a > 'b'"]);
+ }
+
+ {
+ mbgl::style::GreaterThanEqualsFilter filter = { .key = "a", .value = std::string("b") };
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a >= 'b'"]);
+ }
+
+ {
+ mbgl::style::AllFilter filter = {
+ .filters = {
+ mbgl::style::GreaterThanEqualsFilter { .key = "a", .value = std::string("b") },
+ mbgl::style::LessThanEqualsFilter { .key = "a", .value = std::string("z") },
+ },
+ };
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a BETWEEN {'b', 'z'}"]);
+ }
+
+ {
+ mbgl::style::AllFilter filter = {
+ .filters = {
+ mbgl::style::LessThanEqualsFilter { .key = "a", .value = std::string("z") },
+ mbgl::style::GreaterThanEqualsFilter { .key = "a", .value = std::string("b") },
+ },
+ };
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a BETWEEN {'b', 'z'}"]);
+ }
+
+ {
+ mbgl::style::InFilter filter = { .key = "a", .values = { std::string("b"), std::string("c") } };
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter].predicateFormat, [NSPredicate predicateWithFormat:@"a IN {'b', 'c'}"].predicateFormat);
+ }
+
+ {
+ mbgl::style::NotInFilter filter = { .key = "a", .values = { std::string("b"), std::string("c") } };
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter].predicateFormat, [NSPredicate predicateWithFormat:@"NOT a IN {'b', 'c'}"].predicateFormat);
+ }
+
+ {
+ mbgl::style::AllFilter filter;
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithValue:YES]);
+ }
+
+ {
+ mbgl::style::AllFilter filter = {
+ .filters = {
+ mbgl::style::EqualsFilter { .key = "a", .value = std::string("b") },
+ mbgl::style::EqualsFilter { .key = "c", .value = std::string("d") },
+ },
+ };
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a == 'b' AND c == 'd'"]);
+ }
+
+ {
+ mbgl::style::AnyFilter filter;
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithValue:NO]);
+ }
+
+ {
+ mbgl::style::AnyFilter filter = {
+ .filters = {
+ mbgl::style::EqualsFilter { .key = "a", .value = std::string("b") },
+ mbgl::style::EqualsFilter { .key = "c", .value = std::string("d") },
+ },
+ };
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a == 'b' OR c == 'd'"]);
+ }
+
+ {
+ mbgl::style::NoneFilter filter;
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithValue:YES]);
+ }
+
+ {
+ mbgl::style::NoneFilter filter = {
+ .filters = {
+ mbgl::style::EqualsFilter { .key = "a", .value = std::string("b") },
+ mbgl::style::EqualsFilter { .key = "c", .value = std::string("d") },
+ },
+ };
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"NOT(a == 'b' OR c == 'd')"]);
+ }
+}
+
+- (void)testSymmetry {
+ [self testSymmetryWithFormat:@"a = 1" reverseFormat:@"1 = a" mustRoundTrip:YES];
+ [self testSymmetryWithFormat:@"a != 1" reverseFormat:@"1 != a" mustRoundTrip:YES];
+ XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a = b"].mgl_filter, NSException, NSInvalidArgumentException);
+ XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"1 = 1"].mgl_filter, NSException, NSInvalidArgumentException);
+
+ // In the predicate format language, $ is a special character denoting a
+ // variable. Use %K to escape the special feature attribute $id.
+ XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"$id == 670861802"].mgl_filter, NSException, NSInvalidArgumentException);
+ XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a = $id"].mgl_filter, NSException, NSInvalidArgumentException);
+
+ [self testSymmetryWithFormat:@"a = nil" reverseFormat:@"nil = a" mustRoundTrip:YES];
+ [self testSymmetryWithFormat:@"a != nil" reverseFormat:@"nil != a" mustRoundTrip:YES];
+
+ [self testSymmetryWithFormat:@"a < 1" reverseFormat:@"1 > a" mustRoundTrip:YES];
+ [self testSymmetryWithFormat:@"a <= 1" reverseFormat:@"1 >= a" mustRoundTrip:YES];
+ [self testSymmetryWithFormat:@"a > 1" reverseFormat:@"1 < a" mustRoundTrip:YES];
+ [self testSymmetryWithFormat:@"a >= 1" reverseFormat:@"1 <= a" mustRoundTrip:YES];
+
+ [self testSymmetryWithFormat:@"a BETWEEN {1, 2}" reverseFormat:@"1 <= a && 2 >= a" mustRoundTrip:YES];
+ [self testSymmetryWithPredicate:[NSPredicate predicateWithFormat:@"a BETWEEN %@", @[@1, @2]]
+ reversePredicate:[NSPredicate predicateWithFormat:@"1 <= a && 2 >= a"]
+ mustRoundTrip:YES];
+ XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"{1, 2} BETWEEN a"].mgl_filter, NSException, NSInvalidArgumentException);
+ NSPredicate *betweenSetPredicate = [NSPredicate predicateWithFormat:@"a BETWEEN %@", [NSSet setWithObjects:@1, @2, nil]];
+ XCTAssertThrowsSpecificNamed(betweenSetPredicate.mgl_filter, NSException, NSInvalidArgumentException);
+ XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a BETWEEN {1}"].mgl_filter, NSException, NSInvalidArgumentException);
+ XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a BETWEEN {1, 2, 3}"].mgl_filter, NSException, NSInvalidArgumentException);
+
+ [self testSymmetryWithFormat:@"a IN {1, 2}" reverseFormat:@"{1, 2} CONTAINS a" mustRoundTrip:NO];
+ [self testSymmetryWithPredicate:[NSPredicate predicateWithFormat:@"a IN %@", @[@1, @2]]
+ reversePredicate:[NSPredicate predicateWithFormat:@"%@ CONTAINS a", @[@1, @2]]
+ mustRoundTrip:YES];
+
+ // The reverse formats here are a bit backwards because we canonicalize
+ // a reverse CONTAINS to a forward IN.
+ [self testSymmetryWithFormat:@"{1, 2} CONTAINS a" reverseFormat:@"{1, 2} CONTAINS a" mustRoundTrip:NO];
+ [self testSymmetryWithPredicate:[NSPredicate predicateWithFormat:@"%@ CONTAINS a", @[@1, @2]]
+ reversePredicate:[NSPredicate predicateWithFormat:@"%@ CONTAINS a", @[@1, @2]]
+ mustRoundTrip:NO];
+}
+
+- (void)testSymmetryWithFormat:(NSString *)forwardFormat reverseFormat:(NSString *)reverseFormat mustRoundTrip:(BOOL)mustRoundTrip {
+ NSPredicate *forwardPredicate = [NSPredicate predicateWithFormat:forwardFormat];
+ NSPredicate *reversePredicate = reverseFormat ? [NSPredicate predicateWithFormat:reverseFormat] : nil;
+ [self testSymmetryWithPredicate:forwardPredicate reversePredicate:reversePredicate mustRoundTrip:mustRoundTrip];
+}
+
+- (void)testSymmetryWithPredicate:(NSPredicate *)forwardPredicate reversePredicate:(NSPredicate *)reversePredicate mustRoundTrip:(BOOL)mustRoundTrip {
+ auto forwardFilter = forwardPredicate.mgl_filter;
+ NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter];
+ if (mustRoundTrip) {
+ // A collection of ints may turn into an aggregate of longs, for
+ // example, so compare formats instead of the predicates themselves.
+ XCTAssertEqualObjects(forwardPredicate.predicateFormat, forwardPredicateAfter.predicateFormat);
+ }
+
+ if (reversePredicate) {
+ auto reverseFilter = reversePredicate.mgl_filter;
+ NSPredicate *reversePredicateAfter = [NSPredicate mgl_predicateWithFilter:reverseFilter];
+ XCTAssertNotEqualObjects(reversePredicate, reversePredicateAfter);
+
+ XCTAssertEqualObjects(forwardPredicateAfter, reversePredicateAfter);
+ }
+}
+
+@end
diff --git a/platform/darwin/test/MGLRasterStyleLayerTests.m b/platform/darwin/test/MGLRasterStyleLayerTests.m
deleted file mode 100644
index f8de191da0..0000000000
--- a/platform/darwin/test/MGLRasterStyleLayerTests.m
+++ /dev/null
@@ -1,68 +0,0 @@
-// This file is generated.
-// Edit platform/darwin/scripts/generate-style-code.js, then run `make style-code-darwin`.
-
-#import "MGLStyleLayerTests.h"
-
-@interface MGLRasterLayerTests : MGLStyleLayerTests
-@end
-
-@implementation MGLRasterLayerTests
-
-+ (NSString *)layerType {
- return @"raster";
-}
-
-- (void)testRasterLayer {
- NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"amsterdam" ofType:@"geojson"];
- NSURL *url = [NSURL fileURLWithPath:filePath];
- MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"sourceID" URL:url options:nil];
- [self.mapView.style addSource:source];
- MGLRasterStyleLayer *layer = [[MGLRasterStyleLayer alloc] initWithIdentifier:@"layerID" source:source];
- [self.mapView.style addLayer:layer];
-
- layer.maximumRasterBrightness = [MGLRuntimeStylingHelper testNumber];
- layer.minimumRasterBrightness = [MGLRuntimeStylingHelper testNumber];
- layer.rasterContrast = [MGLRuntimeStylingHelper testNumber];
- layer.rasterFadeDuration = [MGLRuntimeStylingHelper testNumber];
- layer.rasterHueRotation = [MGLRuntimeStylingHelper testNumber];
- layer.rasterOpacity = [MGLRuntimeStylingHelper testNumber];
- layer.rasterSaturation = [MGLRuntimeStylingHelper testNumber];
-
- MGLRasterStyleLayer *gLayer = (MGLRasterStyleLayer *)[self.mapView.style layerWithIdentifier:@"layerID"];
- XCTAssertTrue([gLayer isKindOfClass:[MGLRasterStyleLayer class]]);
- XCTAssertEqualObjects(gLayer.maximumRasterBrightness, [MGLRuntimeStylingHelper testNumber]);
- XCTAssertEqualObjects(gLayer.minimumRasterBrightness, [MGLRuntimeStylingHelper testNumber]);
- XCTAssertEqualObjects(gLayer.rasterContrast, [MGLRuntimeStylingHelper testNumber]);
- XCTAssertEqualObjects(gLayer.rasterFadeDuration, [MGLRuntimeStylingHelper testNumber]);
- XCTAssertEqualObjects(gLayer.rasterHueRotation, [MGLRuntimeStylingHelper testNumber]);
- XCTAssertEqualObjects(gLayer.rasterOpacity, [MGLRuntimeStylingHelper testNumber]);
- XCTAssertEqualObjects(gLayer.rasterSaturation, [MGLRuntimeStylingHelper testNumber]);
-
- layer.maximumRasterBrightness = [MGLRuntimeStylingHelper testNumberFunction];
- layer.minimumRasterBrightness = [MGLRuntimeStylingHelper testNumberFunction];
- layer.rasterContrast = [MGLRuntimeStylingHelper testNumberFunction];
- layer.rasterFadeDuration = [MGLRuntimeStylingHelper testNumberFunction];
- layer.rasterHueRotation = [MGLRuntimeStylingHelper testNumberFunction];
- layer.rasterOpacity = [MGLRuntimeStylingHelper testNumberFunction];
- layer.rasterSaturation = [MGLRuntimeStylingHelper testNumberFunction];
-
- XCTAssertEqualObjects(gLayer.maximumRasterBrightness, [MGLRuntimeStylingHelper testNumberFunction]);
- XCTAssertEqualObjects(gLayer.minimumRasterBrightness, [MGLRuntimeStylingHelper testNumberFunction]);
- XCTAssertEqualObjects(gLayer.rasterContrast, [MGLRuntimeStylingHelper testNumberFunction]);
- XCTAssertEqualObjects(gLayer.rasterFadeDuration, [MGLRuntimeStylingHelper testNumberFunction]);
- XCTAssertEqualObjects(gLayer.rasterHueRotation, [MGLRuntimeStylingHelper testNumberFunction]);
- XCTAssertEqualObjects(gLayer.rasterOpacity, [MGLRuntimeStylingHelper testNumberFunction]);
- XCTAssertEqualObjects(gLayer.rasterSaturation, [MGLRuntimeStylingHelper testNumberFunction]);
-}
-
-- (void)testPropertyNames {
- [self testPropertyName:@"maximum-raster-brightness" isBoolean:NO];
- [self testPropertyName:@"minimum-raster-brightness" isBoolean:NO];
- [self testPropertyName:@"raster-contrast" isBoolean:NO];
- [self testPropertyName:@"raster-fade-duration" isBoolean:NO];
- [self testPropertyName:@"raster-hue-rotation" isBoolean:NO];
- [self testPropertyName:@"raster-opacity" isBoolean:NO];
- [self testPropertyName:@"raster-saturation" isBoolean:NO];
-}
-
-@end
diff --git a/platform/darwin/test/MGLRasterStyleLayerTests.mm b/platform/darwin/test/MGLRasterStyleLayerTests.mm
new file mode 100644
index 0000000000..28a201961c
--- /dev/null
+++ b/platform/darwin/test/MGLRasterStyleLayerTests.mm
@@ -0,0 +1,277 @@
+// This file is generated.
+// Edit platform/darwin/scripts/generate-style-code.js, then run `make style-code-darwin`.
+
+#import "MGLStyleLayerTests.h"
+
+#import "MGLStyleLayer_Private.h"
+
+#include <mbgl/style/layers/raster_layer.hpp>
+
+@interface MGLRasterLayerTests : MGLStyleLayerTests
+@end
+
+@implementation MGLRasterLayerTests
+
++ (NSString *)layerType {
+ return @"raster";
+}
+
+- (void)testProperties {
+ MGLPointFeature *feature = [[MGLPointFeature alloc] init];
+ MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"sourceID" shape:feature options:nil];
+
+ MGLRasterStyleLayer *layer = [[MGLRasterStyleLayer alloc] initWithIdentifier:@"layerID" source:source];
+ XCTAssertNotEqual(layer.rawLayer, nullptr);
+ XCTAssertTrue(layer.rawLayer->is<mbgl::style::RasterLayer>());
+ auto rawLayer = layer.rawLayer->as<mbgl::style::RasterLayer>();
+
+ // raster-brightness-max
+ {
+ XCTAssertTrue(rawLayer->getRasterBrightnessMax().isUndefined(),
+ @"raster-brightness-max should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.maximumRasterBrightness;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.maximumRasterBrightness = styleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getRasterBrightnessMax(), propertyValue,
+ @"Setting maximumRasterBrightness to a constant value should update raster-brightness-max.");
+ XCTAssertEqualObjects(layer.maximumRasterBrightness, styleValue,
+ @"maximumRasterBrightness should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.maximumRasterBrightness = styleValue;
+ propertyValue = { mbgl::style::Function<float> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getRasterBrightnessMax(), propertyValue,
+ @"Setting maximumRasterBrightness to a function should update raster-brightness-max.");
+ XCTAssertEqualObjects(layer.maximumRasterBrightness, styleValue,
+ @"maximumRasterBrightness should round-trip functions.");
+
+ layer.maximumRasterBrightness = nil;
+ XCTAssertTrue(rawLayer->getRasterBrightnessMax().isUndefined(),
+ @"Unsetting maximumRasterBrightness should return raster-brightness-max to the default value.");
+ XCTAssertEqualObjects(layer.maximumRasterBrightness, defaultStyleValue,
+ @"maximumRasterBrightness should return the default value after being unset.");
+ }
+
+ // raster-brightness-min
+ {
+ XCTAssertTrue(rawLayer->getRasterBrightnessMin().isUndefined(),
+ @"raster-brightness-min should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.minimumRasterBrightness;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.minimumRasterBrightness = styleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getRasterBrightnessMin(), propertyValue,
+ @"Setting minimumRasterBrightness to a constant value should update raster-brightness-min.");
+ XCTAssertEqualObjects(layer.minimumRasterBrightness, styleValue,
+ @"minimumRasterBrightness should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.minimumRasterBrightness = styleValue;
+ propertyValue = { mbgl::style::Function<float> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getRasterBrightnessMin(), propertyValue,
+ @"Setting minimumRasterBrightness to a function should update raster-brightness-min.");
+ XCTAssertEqualObjects(layer.minimumRasterBrightness, styleValue,
+ @"minimumRasterBrightness should round-trip functions.");
+
+ layer.minimumRasterBrightness = nil;
+ XCTAssertTrue(rawLayer->getRasterBrightnessMin().isUndefined(),
+ @"Unsetting minimumRasterBrightness should return raster-brightness-min to the default value.");
+ XCTAssertEqualObjects(layer.minimumRasterBrightness, defaultStyleValue,
+ @"minimumRasterBrightness should return the default value after being unset.");
+ }
+
+ // raster-contrast
+ {
+ XCTAssertTrue(rawLayer->getRasterContrast().isUndefined(),
+ @"raster-contrast should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.rasterContrast;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.rasterContrast = styleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getRasterContrast(), propertyValue,
+ @"Setting rasterContrast to a constant value should update raster-contrast.");
+ XCTAssertEqualObjects(layer.rasterContrast, styleValue,
+ @"rasterContrast should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.rasterContrast = styleValue;
+ propertyValue = { mbgl::style::Function<float> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getRasterContrast(), propertyValue,
+ @"Setting rasterContrast to a function should update raster-contrast.");
+ XCTAssertEqualObjects(layer.rasterContrast, styleValue,
+ @"rasterContrast should round-trip functions.");
+
+ layer.rasterContrast = nil;
+ XCTAssertTrue(rawLayer->getRasterContrast().isUndefined(),
+ @"Unsetting rasterContrast should return raster-contrast to the default value.");
+ XCTAssertEqualObjects(layer.rasterContrast, defaultStyleValue,
+ @"rasterContrast should return the default value after being unset.");
+ }
+
+ // raster-fade-duration
+ {
+ XCTAssertTrue(rawLayer->getRasterFadeDuration().isUndefined(),
+ @"raster-fade-duration should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.rasterFadeDuration;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.rasterFadeDuration = styleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getRasterFadeDuration(), propertyValue,
+ @"Setting rasterFadeDuration to a constant value should update raster-fade-duration.");
+ XCTAssertEqualObjects(layer.rasterFadeDuration, styleValue,
+ @"rasterFadeDuration should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.rasterFadeDuration = styleValue;
+ propertyValue = { mbgl::style::Function<float> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getRasterFadeDuration(), propertyValue,
+ @"Setting rasterFadeDuration to a function should update raster-fade-duration.");
+ XCTAssertEqualObjects(layer.rasterFadeDuration, styleValue,
+ @"rasterFadeDuration should round-trip functions.");
+
+ layer.rasterFadeDuration = nil;
+ XCTAssertTrue(rawLayer->getRasterFadeDuration().isUndefined(),
+ @"Unsetting rasterFadeDuration should return raster-fade-duration to the default value.");
+ XCTAssertEqualObjects(layer.rasterFadeDuration, defaultStyleValue,
+ @"rasterFadeDuration should return the default value after being unset.");
+ }
+
+ // raster-hue-rotate
+ {
+ XCTAssertTrue(rawLayer->getRasterHueRotate().isUndefined(),
+ @"raster-hue-rotate should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.rasterHueRotation;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.rasterHueRotation = styleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getRasterHueRotate(), propertyValue,
+ @"Setting rasterHueRotation to a constant value should update raster-hue-rotate.");
+ XCTAssertEqualObjects(layer.rasterHueRotation, styleValue,
+ @"rasterHueRotation should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.rasterHueRotation = styleValue;
+ propertyValue = { mbgl::style::Function<float> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getRasterHueRotate(), propertyValue,
+ @"Setting rasterHueRotation to a function should update raster-hue-rotate.");
+ XCTAssertEqualObjects(layer.rasterHueRotation, styleValue,
+ @"rasterHueRotation should round-trip functions.");
+
+ layer.rasterHueRotation = nil;
+ XCTAssertTrue(rawLayer->getRasterHueRotate().isUndefined(),
+ @"Unsetting rasterHueRotation should return raster-hue-rotate to the default value.");
+ XCTAssertEqualObjects(layer.rasterHueRotation, defaultStyleValue,
+ @"rasterHueRotation should return the default value after being unset.");
+ }
+
+ // raster-opacity
+ {
+ XCTAssertTrue(rawLayer->getRasterOpacity().isUndefined(),
+ @"raster-opacity should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.rasterOpacity;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.rasterOpacity = styleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getRasterOpacity(), propertyValue,
+ @"Setting rasterOpacity to a constant value should update raster-opacity.");
+ XCTAssertEqualObjects(layer.rasterOpacity, styleValue,
+ @"rasterOpacity should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.rasterOpacity = styleValue;
+ propertyValue = { mbgl::style::Function<float> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getRasterOpacity(), propertyValue,
+ @"Setting rasterOpacity to a function should update raster-opacity.");
+ XCTAssertEqualObjects(layer.rasterOpacity, styleValue,
+ @"rasterOpacity should round-trip functions.");
+
+ layer.rasterOpacity = nil;
+ XCTAssertTrue(rawLayer->getRasterOpacity().isUndefined(),
+ @"Unsetting rasterOpacity should return raster-opacity to the default value.");
+ XCTAssertEqualObjects(layer.rasterOpacity, defaultStyleValue,
+ @"rasterOpacity should return the default value after being unset.");
+ }
+
+ // raster-saturation
+ {
+ XCTAssertTrue(rawLayer->getRasterSaturation().isUndefined(),
+ @"raster-saturation should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.rasterSaturation;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.rasterSaturation = styleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getRasterSaturation(), propertyValue,
+ @"Setting rasterSaturation to a constant value should update raster-saturation.");
+ XCTAssertEqualObjects(layer.rasterSaturation, styleValue,
+ @"rasterSaturation should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.rasterSaturation = styleValue;
+ propertyValue = { mbgl::style::Function<float> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getRasterSaturation(), propertyValue,
+ @"Setting rasterSaturation to a function should update raster-saturation.");
+ XCTAssertEqualObjects(layer.rasterSaturation, styleValue,
+ @"rasterSaturation should round-trip functions.");
+
+ layer.rasterSaturation = nil;
+ XCTAssertTrue(rawLayer->getRasterSaturation().isUndefined(),
+ @"Unsetting rasterSaturation should return raster-saturation to the default value.");
+ XCTAssertEqualObjects(layer.rasterSaturation, defaultStyleValue,
+ @"rasterSaturation should return the default value after being unset.");
+ }
+}
+
+- (void)testPropertyNames {
+ [self testPropertyName:@"maximum-raster-brightness" isBoolean:NO];
+ [self testPropertyName:@"minimum-raster-brightness" isBoolean:NO];
+ [self testPropertyName:@"raster-contrast" isBoolean:NO];
+ [self testPropertyName:@"raster-fade-duration" isBoolean:NO];
+ [self testPropertyName:@"raster-hue-rotation" isBoolean:NO];
+ [self testPropertyName:@"raster-opacity" isBoolean:NO];
+ [self testPropertyName:@"raster-saturation" isBoolean:NO];
+}
+
+@end
diff --git a/platform/darwin/test/MGLRuntimeStylingHelper.h b/platform/darwin/test/MGLRuntimeStylingHelper.h
deleted file mode 100644
index 8857dba9c5..0000000000
--- a/platform/darwin/test/MGLRuntimeStylingHelper.h
+++ /dev/null
@@ -1,35 +0,0 @@
-#import <Foundation/Foundation.h>
-
-#import "MGLTypes.h"
-#import "MGLStyleValue.h"
-
-@interface MGLRuntimeStylingHelper : NSObject
-
-+ (MGLStyleConstantValue<NSValue *> *)testPadding;
-+ (MGLStyleFunction<NSValue *> *)testPaddingFunction;
-
-+ (MGLStyleConstantValue<NSValue *> *)testOffset;
-+ (MGLStyleFunction<NSValue *> *)testOffsetFunction;
-
-+ (MGLStyleConstantValue<NSArray<NSString *> *> *)testFont;
-+ (MGLStyleFunction<NSArray<NSString *> *> *)testFontFunction;
-
-+ (MGLStyleConstantValue<NSArray<NSNumber *> *> *)testDashArray;
-+ (MGLStyleFunction<NSArray<NSNumber *> *> *)testDashArrayFunction;
-
-+ (MGLStyleConstantValue<NSNumber *> *)testNumber;
-+ (MGLStyleFunction<NSNumber *> *)testNumberFunction;
-
-+ (MGLStyleConstantValue<NSNumber *> *)testBool;
-+ (MGLStyleFunction<NSNumber *> *)testBoolFunction;
-
-+ (MGLStyleConstantValue<NSString *> *)testString;
-+ (MGLStyleFunction<NSString *> *)testStringFunction;
-
-+ (MGLStyleConstantValue<MGLColor *> *)testColor;
-+ (MGLStyleFunction<MGLColor *> *)testColorFunction;
-
-+ (MGLStyleConstantValue<NSValue *> *)testEnum:(NSUInteger)value type:(const char *)type;
-+ (MGLStyleFunction<NSValue *> *)testEnumFunction:(NSUInteger)value type:(const char *)type;
-
-@end
diff --git a/platform/darwin/test/MGLRuntimeStylingHelper.m b/platform/darwin/test/MGLRuntimeStylingHelper.m
deleted file mode 100644
index 955c664f2c..0000000000
--- a/platform/darwin/test/MGLRuntimeStylingHelper.m
+++ /dev/null
@@ -1,122 +0,0 @@
-#import "MGLRuntimeStylingHelper.h"
-
-#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
- #import <UIKit/UIKit.h>
- #define MGLEdgeInsets UIEdgeInsets
-#else
- #import <Cocoa/Cocoa.h>
- #define MGLEdgeInsets NSEdgeInsets
-#endif
-
-@implementation MGLRuntimeStylingHelper
-
-+ (MGLStyleConstantValue<NSValue *> *)testPadding
-{
- MGLEdgeInsets insets = {
- .top = 1,
- .left = 1,
- .bottom = 1,
- .right = 1,
- };
- return [MGLStyleConstantValue<NSValue *> valueWithRawValue:[NSValue value:&insets withObjCType:@encode(MGLEdgeInsets)]];
-}
-
-+ (MGLStyleFunction<NSValue *> *)testPaddingFunction
-{
- return [MGLStyleFunction<NSValue *> functionWithStops:@{@(18): self.testPadding}];
-}
-
-+ (MGLStyleConstantValue<NSValue *> *)testOffset
-{
- CGVector vector = CGVectorMake(1, 1);
- return [MGLStyleConstantValue<NSValue *> valueWithRawValue:[NSValue value:&vector withObjCType:@encode(CGVector)]];
-}
-
-+ (MGLStyleFunction<NSValue *> *)testOffsetFunction
-{
- return [MGLStyleFunction<NSValue *> valueWithStops:@{ @(18): self.testOffset }];
-}
-
-+ (MGLStyleConstantValue<NSArray<NSString *> *> *)testFont
-{
- return [MGLStyleConstantValue<NSArray<NSString *> *> valueWithRawValue:@[@"Open Sans Regular", @"Arial Unicode MS Regular"]];
-}
-
-+ (MGLStyleFunction<NSArray<NSString *> *> *)testFontFunction
-{
- return [MGLStyleFunction<NSArray<NSString *> *> valueWithStops:@{ @18: self.testFont }];
-}
-
-+ (MGLStyleConstantValue<NSArray<NSNumber *> *> *)testDashArray
-{
- return [MGLStyleConstantValue<NSArray<NSNumber *> *> valueWithRawValue:@[@1, @2]];
-}
-
-+ (MGLStyleFunction<NSArray<NSNumber *> *> *)testDashArrayFunction
-{
- return [MGLStyleFunction<NSArray<NSNumber *> *> valueWithStops:@{
- @18: self.testDashArray,
- }];
-}
-
-+ (MGLStyleConstantValue<NSNumber *> *)testNumber
-{
- return [MGLStyleConstantValue<NSNumber *> valueWithRawValue:@1];
-}
-
-+ (MGLStyleFunction<NSNumber *> *)testNumberFunction
-{
- return [MGLStyleFunction<NSNumber *> valueWithStops:@{
- @18: self.testNumber,
- }];
-}
-
-+ (MGLStyleConstantValue<NSNumber *> *)testBool
-{
- return [MGLStyleConstantValue<NSNumber *> valueWithRawValue:@YES];
-}
-
-+ (MGLStyleFunction<NSNumber *> *)testBoolFunction
-{
- return [MGLStyleFunction<NSNumber *> valueWithStops:@{
- @18: self.testBool,
- }];
-}
-
-+ (MGLStyleConstantValue<NSString *> *)testString
-{
- return [MGLStyleConstantValue<NSString *> valueWithRawValue:@"test"];
-}
-
-+ (MGLStyleFunction<NSString *> *)testStringFunction
-{
- return [MGLStyleFunction<NSString *> valueWithStops:@{
- @18: self.testString,
- }];
-}
-
-+ (MGLStyleConstantValue<MGLColor *> *)testColor
-{
- return [MGLStyleConstantValue<MGLColor *> valueWithRawValue:[MGLColor redColor]];
-}
-
-+ (MGLStyleFunction<MGLColor *> *)testColorFunction
-{
- return [MGLStyleFunction<MGLColor *> valueWithStops:@{
- @18: self.testColor,
- }];
-}
-
-+ (MGLStyleConstantValue<NSValue *> *)testEnum:(NSUInteger)value type:(const char *)type
-{
- return [MGLStyleConstantValue<NSValue *> valueWithRawValue:[NSValue value:&value withObjCType:type]];
-}
-
-+ (MGLStyleFunction<NSValue *> *)testEnumFunction:(NSUInteger)value type:(const char *)type
-{
- return [MGLStyleFunction<NSValue *> valueWithStops:@{
- @18: [self testEnum:value type:type],
- }];
-}
-
-@end
diff --git a/platform/darwin/test/MGLShapeSourceTests.mm b/platform/darwin/test/MGLShapeSourceTests.mm
index efff4b393e..cf32b5c821 100644
--- a/platform/darwin/test/MGLShapeSourceTests.mm
+++ b/platform/darwin/test/MGLShapeSourceTests.mm
@@ -37,6 +37,19 @@
XCTAssertNil(source.shape);
}
+- (void)testUnclusterableShape {
+ NSDictionary *options = @{
+ MGLShapeSourceOptionClustered: @YES,
+ };
+
+ MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"id" shape:[[MGLPointFeature alloc] init] options:options];
+ XCTAssertTrue([source.shape isKindOfClass:[MGLPointFeature class]]);
+
+ MGLShapeCollectionFeature *feature = [MGLShapeCollectionFeature shapeCollectionWithShapes:@[]];
+ source = [[MGLShapeSource alloc] initWithIdentifier:@"id" shape:feature options:options];
+ XCTAssertTrue([source.shape isKindOfClass:[MGLShapeCollectionFeature class]]);
+}
+
- (void)testMGLShapeSourceWithDataMultipleFeatures {
NSString *geoJSON = @"{\"type\": \"FeatureCollection\",\"features\": [{\"type\": \"Feature\",\"properties\": {},\"geometry\": {\"type\": \"LineString\",\"coordinates\": [[-107.75390625,40.329795743702064],[-104.34814453125,37.64903402157866]]}}]}";
@@ -265,4 +278,43 @@
XCTAssert(shape.shapes.count == 6, @"Shape collection should contain 6 shapes");
}
+- (void)testMGLShapeSourceWithFeaturesConvenienceInitializer {
+ CLLocationCoordinate2D coordinates[] = {
+ CLLocationCoordinate2DMake(100.0, 0.0),
+ CLLocationCoordinate2DMake(101.0, 0.0),
+ CLLocationCoordinate2DMake(101.0, 1.0),
+ CLLocationCoordinate2DMake(100.0, 1.0),
+ CLLocationCoordinate2DMake(100.0, 0.0)};
+
+ MGLPolygonFeature *polygonFeature = [MGLPolygonFeature polygonWithCoordinates:coordinates count:sizeof(coordinates)/sizeof(coordinates[0]) interiorPolygons:nil];
+
+ MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"source-id" features:@[polygonFeature] options:nil];
+ MGLShapeCollectionFeature *shape = (MGLShapeCollectionFeature *)source.shape;
+
+ XCTAssertTrue([shape isKindOfClass:[MGLShapeCollectionFeature class]]);
+ XCTAssertEqual(shape.shapes.count, 1, @"Shape collection should contain 1 shape");
+
+ // when a shape is included in the features array
+ MGLPolygon *polygon = [MGLPolygon polygonWithCoordinates:coordinates count:sizeof(coordinates)/sizeof(coordinates[0]) interiorPolygons:nil];
+
+ XCTAssertThrowsSpecificNamed([[MGLShapeSource alloc] initWithIdentifier:@"source-id-invalid" features:@[polygon] options:nil], NSException, NSInvalidArgumentException, @"Shape source should raise an exception if a shape is sent to the features initializer");
+}
+
+- (void)testMGLShapeSourceWithShapesConvenienceInitializer {
+ CLLocationCoordinate2D coordinates[] = {
+ CLLocationCoordinate2DMake(100.0, 0.0),
+ CLLocationCoordinate2DMake(101.0, 0.0),
+ CLLocationCoordinate2DMake(101.0, 1.0),
+ CLLocationCoordinate2DMake(100.0, 1.0),
+ CLLocationCoordinate2DMake(100.0, 0.0)};
+
+ MGLPolygon *polygon = [MGLPolygon polygonWithCoordinates:coordinates count:sizeof(coordinates)/sizeof(coordinates[0]) interiorPolygons:nil];
+
+ MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"source-id" shapes:@[polygon] options:nil];
+ MGLShapeCollectionFeature *shape = (MGLShapeCollectionFeature *)source.shape;
+
+ XCTAssertTrue([shape isKindOfClass:[MGLShapeCollection class]]);
+ XCTAssertEqual(shape.shapes.count, 1, @"Shape collection should contain 1 shape");
+}
+
@end
diff --git a/platform/darwin/test/MGLStyleLayerTests.h b/platform/darwin/test/MGLStyleLayerTests.h
index 74ce62e894..f0b889f022 100644
--- a/platform/darwin/test/MGLStyleLayerTests.h
+++ b/platform/darwin/test/MGLStyleLayerTests.h
@@ -1,11 +1,8 @@
#import <Mapbox/Mapbox.h>
-#import "MGLRuntimeStylingHelper.h"
#import <XCTest/XCTest.h>
@interface MGLStyleLayerTests : XCTestCase <MGLMapViewDelegate>
-@property (nonatomic) IBOutlet MGLMapView *mapView;
-@property (nonatomic) XCTestExpectation *expectation;
@property (nonatomic, copy, readonly, class) NSString *layerType;
- (void)testPropertyName:(NSString *)name isBoolean:(BOOL)isBoolean;
@@ -18,3 +15,11 @@
@property (nonatomic, readonly, copy) NSString *lemma;
@end
+
+@interface NSValue (MGLStyleLayerTestAdditions)
+
++ (instancetype)valueWithMGLVector:(CGVector)vector;
+
+@property (readonly) CGVector MGLVectorValue;
+
+@end
diff --git a/platform/darwin/test/MGLStyleLayerTests.m b/platform/darwin/test/MGLStyleLayerTests.m
index 590d6eda7f..1dba9f4305 100644
--- a/platform/darwin/test/MGLStyleLayerTests.m
+++ b/platform/darwin/test/MGLStyleLayerTests.m
@@ -8,21 +8,28 @@
@dynamic layerType;
-- (void)setUp {
- [super setUp];
-#if TARGET_OS_IPHONE
- UIApplication *app = [UIApplication sharedApplication];
- UIViewController *vc = [[UIViewController alloc] init];
- app.keyWindow.rootViewController = vc;
- [vc view]; // Force load xib
- _mapView = [[MGLMapView alloc] initWithFrame:CGRectMake(0, 0, 256, 256)];
- [vc.view addSubview:_mapView];
- _mapView.delegate = self;
-#else
- [MGLAccountManager setAccessToken:@"pk.feedcafedeadbeefbadebede"];
- NSWindowController *windowController = [[NSWindowController alloc] initWithWindowNibName:@"MGLStyleLayerTests" owner:self];
- [windowController showWindow:nil];
-#endif
+- (void)testProperties {
+ MGLPointFeature *feature = [[MGLPointFeature alloc] init];
+ MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"sourceID" shape:feature options:nil];
+
+ MGLFillStyleLayer *layer = [[MGLFillStyleLayer alloc] initWithIdentifier:@"layerID" source:source];
+
+ XCTAssertEqualObjects(layer.identifier, @"layerID");
+ XCTAssertEqualObjects(layer.sourceIdentifier, source.identifier);
+
+ XCTAssertTrue(layer.visible);
+ layer.visible = NO;
+ XCTAssertFalse(layer.visible);
+ layer.visible = YES;
+ XCTAssertTrue(layer.visible);
+
+ XCTAssertEqual(layer.minimumZoomLevel, -INFINITY);
+ layer.minimumZoomLevel = 22;
+ XCTAssertEqual(layer.minimumZoomLevel, 22);
+
+ XCTAssertEqual(layer.maximumZoomLevel, INFINITY);
+ layer.maximumZoomLevel = 0;
+ XCTAssertEqual(layer.maximumZoomLevel, 0);
}
- (void)testPropertyName:(NSString *)name isBoolean:(BOOL)isBoolean {
@@ -87,3 +94,25 @@
}
@end
+
+@implementation NSValue (MGLStyleLayerTestAdditions)
+
++ (instancetype)valueWithMGLVector:(CGVector)vector {
+#if TARGET_OS_IPHONE
+ return [self valueWithCGVector:vector];
+#else
+ return [self value:&vector withObjCType:@encode(CGVector)];
+#endif
+}
+
+- (CGVector)MGLVectorValue {
+#if TARGET_OS_IPHONE
+ return self.CGVectorValue;
+#else
+ CGVector vector;
+ [self getValue:&vector];
+ return vector;
+#endif
+}
+
+@end
diff --git a/platform/darwin/test/MGLStyleLayerTests.m.ejs b/platform/darwin/test/MGLStyleLayerTests.m.ejs
deleted file mode 100644
index 6b7bfe2f1c..0000000000
--- a/platform/darwin/test/MGLStyleLayerTests.m.ejs
+++ /dev/null
@@ -1,72 +0,0 @@
-<%
- const type = locals.type;
- const layoutProperties = locals.layoutProperties;
- const paintProperties = locals.paintProperties;
--%>
-// This file is generated.
-// Edit platform/darwin/scripts/generate-style-code.js, then run `make style-code-darwin`.
-
-#import "MGLStyleLayerTests.h"
-
-@interface MGL<%- camelize(type) %>LayerTests : MGLStyleLayerTests
-@end
-
-@implementation MGL<%- camelize(type) %>LayerTests
-
-+ (NSString *)layerType {
- return @"<%- type %>";
-}
-
-- (void)test<%- camelize(type) %>Layer {
-<% if (type === 'background') { -%>
- MGL<%- camelize(type) %>StyleLayer *layer = [[MGL<%- camelize(type) %>StyleLayer alloc] initWithIdentifier:@"layerID"];
-<% } else { -%>
- NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"amsterdam" ofType:@"geojson"];
- NSURL *url = [NSURL fileURLWithPath:filePath];
- MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"sourceID" URL:url options:nil];
- [self.mapView.style addSource:source];
- MGL<%- camelize(type) %>StyleLayer *layer = [[MGL<%- camelize(type) %>StyleLayer alloc] initWithIdentifier:@"layerID" source:source];
-<% } -%>
- [self.mapView.style addLayer:layer];
-
-<% for (const property of layoutProperties) { -%>
- <%- testImplementation(property, type) %>
-<% } -%>
-<% for (const property of paintProperties) { -%>
- <%- testImplementation(property, type) %>
-<% } -%>
-
- MGL<%- camelize(type) %>StyleLayer *gLayer = (MGL<%- camelize(type) %>StyleLayer *)[self.mapView.style layerWithIdentifier:@"layerID"];
- XCTAssertTrue([gLayer isKindOfClass:[MGL<%- camelize(type) %>StyleLayer class]]);
-<% for (const property of layoutProperties) { -%>
- <%- testGetterImplementation(property, type) %>
-<% } -%>
-<% for (const property of paintProperties) { -%>
- <%- testGetterImplementation(property, type) %>
-<% } -%>
-
-<% for (const property of layoutProperties) { -%>
- <%- testImplementation(property, type, true) %>
-<% } -%>
-<% for (const property of paintProperties) { -%>
- <%- testImplementation(property, type, true) %>
-<% } -%>
-
-<% for (const property of layoutProperties) { -%>
- <%- testGetterImplementation(property, type, true) %>
-<% } -%>
-<% for (const property of paintProperties) { -%>
- <%- testGetterImplementation(property, type, true) %>
-<% } -%>
-}
-
-- (void)testPropertyNames {
-<% for (const property of layoutProperties) { -%>
- [self testPropertyName:@"<%- property.getter || property.name %>" isBoolean:<%- property.type === 'boolean' ? 'YES' : 'NO' %>];
-<% } -%>
-<% for (const property of paintProperties) { -%>
- [self testPropertyName:@"<%- property.getter || property.name %>" isBoolean:<%- property.type === 'boolean' ? 'YES' : 'NO' %>];
-<% } -%>
-}
-
-@end
diff --git a/platform/darwin/test/MGLStyleLayerTests.mm.ejs b/platform/darwin/test/MGLStyleLayerTests.mm.ejs
new file mode 100644
index 0000000000..00842a5b4e
--- /dev/null
+++ b/platform/darwin/test/MGLStyleLayerTests.mm.ejs
@@ -0,0 +1,114 @@
+<%
+ const type = locals.type;
+ const properties = locals.properties;
+ const enumProperties = locals.enumProperties;
+-%>
+// This file is generated.
+// Edit platform/darwin/scripts/generate-style-code.js, then run `make style-code-darwin`.
+
+#import "MGLStyleLayerTests.h"
+
+#import "MGLStyleLayer_Private.h"
+
+#include <mbgl/style/layers/<%- type %>_layer.hpp>
+
+@interface MGL<%- camelize(type) %>LayerTests : MGLStyleLayerTests
+@end
+
+@implementation MGL<%- camelize(type) %>LayerTests
+
++ (NSString *)layerType {
+ return @"<%- type %>";
+}
+
+<% if (type !== 'background' && type !== 'raster') { -%>
+- (void)testPredicates {
+ MGLPointFeature *feature = [[MGLPointFeature alloc] init];
+ MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"sourceID" shape:feature options:nil];
+ MGL<%- camelize(type) %>StyleLayer *layer = [[MGL<%- camelize(type) %>StyleLayer alloc] initWithIdentifier:@"layerID" source:source];
+
+ XCTAssertNil(layer.sourceLayerIdentifier);
+ layer.sourceLayerIdentifier = @"layerID";
+ XCTAssertEqualObjects(layer.sourceLayerIdentifier, @"layerID");
+ layer.sourceLayerIdentifier = nil;
+ XCTAssertNil(layer.sourceLayerIdentifier);
+
+ XCTAssertNil(layer.predicate);
+ layer.predicate = [NSPredicate predicateWithValue:NO];
+ XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithValue:NO]);
+ layer.predicate = nil;
+ XCTAssertNil(layer.predicate);
+}
+
+<% } -%>
+- (void)testProperties {
+<% if (type === 'background') { -%>
+ MGL<%- camelize(type) %>StyleLayer *layer = [[MGL<%- camelize(type) %>StyleLayer alloc] initWithIdentifier:@"layerID"];
+<% } else { -%>
+ MGLPointFeature *feature = [[MGLPointFeature alloc] init];
+ MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"sourceID" shape:feature options:nil];
+
+ MGL<%- camelize(type) %>StyleLayer *layer = [[MGL<%- camelize(type) %>StyleLayer alloc] initWithIdentifier:@"layerID" source:source];
+<% } -%>
+ XCTAssertNotEqual(layer.rawLayer, nullptr);
+ XCTAssertTrue(layer.rawLayer->is<mbgl::style::<%- camelize(type) %>Layer>());
+ auto rawLayer = layer.rawLayer->as<mbgl::style::<%- camelize(type) %>Layer>();
+<% for (const property of properties) { -%>
+
+ // <%- originalPropertyName(property) %>
+ {
+ XCTAssertTrue(rawLayer->get<%- camelize(originalPropertyName(property)) %>().isUndefined(),
+ @"<%- originalPropertyName(property) %> should be unset initially.");
+ MGLStyleValue<<%- propertyType(property) %>> *defaultStyleValue = layer.<%- objCName(property) %>;
+
+ MGLStyleValue<<%- propertyType(property) %>> *styleValue = [MGLStyleValue<<%- propertyType(property) %>> valueWithRawValue:<%- objCTestValue(property, type, 3) %>];
+ layer.<%- objCName(property) %> = styleValue;
+ mbgl::style::PropertyValue<<%- mbglType(property) %>> propertyValue = { <%- mbglTestValue(property, type) %> };
+ XCTAssertEqual(rawLayer->get<%- camelize(originalPropertyName(property)) %>(), propertyValue,
+ @"Setting <%- objCName(property) %> to a constant value should update <%- originalPropertyName(property) %>.");
+ XCTAssertEqualObjects(layer.<%- objCName(property) %>, styleValue,
+ @"<%- objCName(property) %> should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<<%- propertyType(property) %>> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.<%- objCName(property) %> = styleValue;
+ propertyValue = { mbgl::style::Function<<%- mbglType(property) %>> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->get<%- camelize(originalPropertyName(property)) %>(), propertyValue,
+ @"Setting <%- objCName(property) %> to a function should update <%- originalPropertyName(property) %>.");
+ XCTAssertEqualObjects(layer.<%- objCName(property) %>, styleValue,
+ @"<%- objCName(property) %> should round-trip functions.");
+<% if (!property.required) { -%>
+
+ layer.<%- objCName(property) %> = nil;
+ XCTAssertTrue(rawLayer->get<%- camelize(originalPropertyName(property)) %>().isUndefined(),
+ @"Unsetting <%- objCName(property) %> should return <%- originalPropertyName(property) %> to the default value.");
+ XCTAssertEqualObjects(layer.<%- objCName(property) %>, defaultStyleValue,
+ @"<%- objCName(property) %> should return the default value after being unset.");
+<% } -%>
+ }
+<% } -%>
+}
+
+- (void)testPropertyNames {
+<% for (const property of properties) { -%>
+ [self testPropertyName:@"<%- property.getter || property.name %>" isBoolean:<%- property.type === 'boolean' ? 'YES' : 'NO' %>];
+<% } -%>
+}
+
+<% if (enumProperties) { -%>
+- (void)testValueAdditions {
+<% for (let property of enumProperties) { -%>
+<% for (let value in property.values) { -%>
+<% if (property.values.hasOwnProperty(value)) { -%>
+ XCTAssertEqual([NSValue valueWithMGL<%- camelize(property.name) %>:MGL<%- camelize(property.name) %><%- camelize(value) %>].MGL<%- camelize(property.name) %>Value, MGL<%- camelize(property.name) %><%- camelize(value) %>);
+<% } -%>
+<% } -%>
+<% } -%>
+}
+
+<% } -%>
+@end
diff --git a/platform/darwin/test/MGLStyleLayerTests.xib b/platform/darwin/test/MGLStyleLayerTests.xib
deleted file mode 100644
index 23ad22e7e3..0000000000
--- a/platform/darwin/test/MGLStyleLayerTests.xib
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10117" systemVersion="16A304a" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
- <dependencies>
- <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10117"/>
- </dependencies>
- <objects>
- <customObject id="-2" userLabel="File's Owner" customClass="MGLStyleLayerTests">
- <connections>
- <outlet property="mapView" destination="6RL-d9-juy" id="0ch-aR-Um6"/>
- </connections>
- </customObject>
- <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
- <customObject id="-3" userLabel="Application" customClass="NSObject"/>
- <window title="MGLStyleLayerTests" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g">
- <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
- <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
- <rect key="contentRect" x="196" y="240" width="256" height="256"/>
- <rect key="screenRect" x="0.0" y="0.0" width="1920" height="1058"/>
- <view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
- <rect key="frame" x="0.0" y="0.0" width="256" height="256"/>
- <autoresizingMask key="autoresizingMask"/>
- <subviews>
- <customView translatesAutoresizingMaskIntoConstraints="NO" id="6RL-d9-juy" customClass="MGLMapView">
- <rect key="frame" x="0.0" y="0.0" width="256" height="256"/>
- <connections>
- <outlet property="delegate" destination="-2" id="6kS-ct-JEg"/>
- </connections>
- </customView>
- </subviews>
- <constraints>
- <constraint firstItem="6RL-d9-juy" firstAttribute="leading" secondItem="EiT-Mj-1SZ" secondAttribute="leading" id="KEN-aL-UF0"/>
- <constraint firstAttribute="bottom" secondItem="6RL-d9-juy" secondAttribute="bottom" id="V27-f3-xHZ"/>
- <constraint firstAttribute="trailing" secondItem="6RL-d9-juy" secondAttribute="trailing" id="vjq-iM-OyA"/>
- <constraint firstItem="6RL-d9-juy" firstAttribute="top" secondItem="EiT-Mj-1SZ" secondAttribute="top" id="yWg-v4-wJB"/>
- </constraints>
- </view>
- </window>
- </objects>
-</document>
diff --git a/platform/darwin/test/MGLStyleTests.mm b/platform/darwin/test/MGLStyleTests.mm
index e9ba4a9afa..176217619d 100644
--- a/platform/darwin/test/MGLStyleTests.mm
+++ b/platform/darwin/test/MGLStyleTests.mm
@@ -1,17 +1,4 @@
-#import "MGLMapView.h"
-#import "MGLStyle_Private.h"
-
-#import "MGLShapeSource.h"
-#import "MGLRasterSource.h"
-#import "MGLVectorSource.h"
-
-#import "MGLBackgroundStyleLayer.h"
-#import "MGLCircleStyleLayer.h"
-#import "MGLFillStyleLayer.h"
-#import "MGLLineStyleLayer.h"
-#import "MGLOpenGLStyleLayer.h"
-#import "MGLRasterStyleLayer.h"
-#import "MGLSymbolStyleLayer.h"
+#import <Mapbox/Mapbox.h>
#import "NSBundle+MGLAdditions.h"
@@ -25,20 +12,46 @@
#endif
#import <objc/runtime.h>
-@interface MGLStyleTests : XCTestCase
+@interface MGLStyleTests : XCTestCase <MGLMapViewDelegate>
@property (nonatomic) MGLMapView *mapView;
@property (nonatomic) MGLStyle *style;
@end
-@implementation MGLStyleTests
+@implementation MGLStyleTests {
+ XCTestExpectation *_styleLoadingExpectation;
+}
- (void)setUp {
[super setUp];
+
+ [MGLAccountManager setAccessToken:@"pk.feedcafedeadbeefbadebede"];
+ NSURL *styleURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"one-liner" withExtension:@"json"];
+ self.mapView = [[MGLMapView alloc] initWithFrame:CGRectMake(0, 0, 100, 100) styleURL:styleURL];
+ self.mapView.delegate = self;
+ if (!self.mapView.style) {
+ _styleLoadingExpectation = [self expectationWithDescription:@"Map view should finish loading style."];
+ [self waitForExpectationsWithTimeout:1 handler:nil];
+ }
+}
- self.mapView = [[MGLMapView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
- self.style = [[MGLStyle alloc] initWithMapView:self.mapView];
+- (void)mapView:(MGLMapView *)mapView didFinishLoadingStyle:(MGLStyle *)style {
+ XCTAssertNotNil(mapView.style);
+ XCTAssertEqual(mapView.style, style);
+
+ [_styleLoadingExpectation fulfill];
+}
+
+- (void)tearDown {
+ _styleLoadingExpectation = nil;
+ self.mapView = nil;
+
+ [super tearDown];
+}
+
+- (MGLStyle *)style {
+ return self.mapView.style;
}
- (void)testUnversionedStyleURLs {
@@ -121,6 +134,24 @@
}];
}
+- (void)testName {
+ XCTAssertNil(self.style.name);
+}
+
+- (void)testSources {
+ NSSet<MGLSource *> *initialSources = self.style.sources;
+ if ([initialSources.anyObject.identifier isEqualToString:@"com.mapbox.annotations"]) {
+ XCTAssertEqual(self.style.sources.count, 1);
+ } else {
+ XCTAssertEqual(self.style.sources.count, 0);
+ }
+ MGLShapeSource *shapeSource = [[MGLShapeSource alloc] initWithIdentifier:@"shapeSource" shape:nil options:nil];
+ [self.style addSource:shapeSource];
+ XCTAssertEqual(self.style.sources.count, initialSources.count + 1);
+ [self.style removeSource:shapeSource];
+ XCTAssertEqual(self.style.sources.count, initialSources.count);
+}
+
- (void)testAddingSourcesTwice {
MGLShapeSource *shapeSource = [[MGLShapeSource alloc] initWithIdentifier:@"shapeSource" shape:nil options:nil];
[self.style addSource:shapeSource];
@@ -143,6 +174,22 @@
XCTAssertThrowsSpecificNamed([self.style addSource: source2], NSException, @"MGLRedundantSourceIdentifierException");
}
+- (void)testLayers {
+ NSArray<MGLStyleLayer *> *initialLayers = self.style.layers;
+ if ([initialLayers.firstObject.identifier isEqualToString:@"com.mapbox.annotations.points"]) {
+ XCTAssertEqual(self.style.layers.count, 1);
+ } else {
+ XCTAssertEqual(self.style.layers.count, 0);
+ }
+ MGLShapeSource *shapeSource = [[MGLShapeSource alloc] initWithIdentifier:@"shapeSource" shape:nil options:nil];
+ [self.style addSource:shapeSource];
+ MGLFillStyleLayer *fillLayer = [[MGLFillStyleLayer alloc] initWithIdentifier:@"fillLayer" source:shapeSource];
+ [self.style addLayer:fillLayer];
+ XCTAssertEqual(self.style.layers.count, initialLayers.count + 1);
+ [self.style removeLayer:fillLayer];
+ XCTAssertEqual(self.style.layers.count, initialLayers.count);
+}
+
- (void)testAddingLayersTwice {
MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"shapeSource" shape:nil options:nil];
@@ -174,18 +221,18 @@
- (void)testAddingLayersWithDuplicateIdentifiers {
//Just some source
MGLVectorSource *source = [[MGLVectorSource alloc] initWithIdentifier:@"my-source" configurationURL:[NSURL URLWithString:@"mapbox://mapbox.mapbox-terrain-v2"]];
- [self.mapView.style addSource: source];
+ [self.style addSource: source];
//Add initial layer
MGLFillStyleLayer *initial = [[MGLFillStyleLayer alloc] initWithIdentifier:@"my-layer" source:source];
- [self.mapView.style addLayer:initial];
+ [self.style addLayer:initial];
//Try to add the duplicate
- XCTAssertThrowsSpecificNamed([self.mapView.style addLayer:[[MGLFillStyleLayer alloc] initWithIdentifier:@"my-layer" source:source]], NSException, @"MGLRedundantLayerIdentifierException");
- XCTAssertThrowsSpecificNamed([self.mapView.style insertLayer:[[MGLFillStyleLayer alloc] initWithIdentifier:@"my-layer" source:source] belowLayer:initial],NSException, @"MGLRedundantLayerIdentifierException");
- XCTAssertThrowsSpecificNamed([self.mapView.style insertLayer:[[MGLFillStyleLayer alloc] initWithIdentifier:@"my-layer" source:source] aboveLayer:initial], NSException, @"MGLRedundantLayerIdentifierException");
- XCTAssertThrowsSpecificNamed([self.mapView.style insertLayer:[[MGLFillStyleLayer alloc] initWithIdentifier:@"my-layer" source:source] atIndex:0], NSException, @"MGLRedundantLayerIdentifierException");
- XCTAssertThrowsSpecificNamed([self.mapView.style insertLayer:[[MGLOpenGLStyleLayer alloc] initWithIdentifier:@"my-layer"] atIndex:0], NSException, @"MGLRedundantLayerIdentifierException");
+ XCTAssertThrowsSpecificNamed([self.style addLayer:[[MGLFillStyleLayer alloc] initWithIdentifier:@"my-layer" source:source]], NSException, @"MGLRedundantLayerIdentifierException");
+ XCTAssertThrowsSpecificNamed([self.style insertLayer:[[MGLFillStyleLayer alloc] initWithIdentifier:@"my-layer" source:source] belowLayer:initial],NSException, @"MGLRedundantLayerIdentifierException");
+ XCTAssertThrowsSpecificNamed([self.style insertLayer:[[MGLFillStyleLayer alloc] initWithIdentifier:@"my-layer" source:source] aboveLayer:initial], NSException, @"MGLRedundantLayerIdentifierException");
+ XCTAssertThrowsSpecificNamed([self.style insertLayer:[[MGLFillStyleLayer alloc] initWithIdentifier:@"my-layer" source:source] atIndex:0], NSException, @"MGLRedundantLayerIdentifierException");
+ XCTAssertThrowsSpecificNamed([self.style insertLayer:[[MGLOpenGLStyleLayer alloc] initWithIdentifier:@"my-layer"] atIndex:0], NSException, @"MGLRedundantLayerIdentifierException");
}
- (NSString *)stringWithContentsOfStyleHeader {
@@ -198,6 +245,10 @@
return styleHeader;
}
+- (void)testClasses {
+ XCTAssertEqual(self.style.styleClasses.count, 0);
+}
+
- (void)testImages {
NSString *imageName = @"TrackingLocationMask";
#if TARGET_OS_IPHONE
@@ -209,12 +260,46 @@
#endif
XCTAssertNotNil(image);
- [self.mapView.style setImage:image forName:imageName];
- MGLImage *styleImage = [self.mapView.style imageForName:imageName];
+ [self.style setImage:image forName:imageName];
+ MGLImage *styleImage = [self.style imageForName:imageName];
XCTAssertNotNil(styleImage);
XCTAssertEqual(image.size.width, styleImage.size.width);
XCTAssertEqual(image.size.height, styleImage.size.height);
}
+- (void)testLayersOrder {
+ NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"amsterdam" ofType:@"geojson"];
+ NSURL *url = [NSURL fileURLWithPath:filePath];
+ MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"sourceID" URL:url options:nil];
+ [self.style addSource:source];
+
+ MGLCircleStyleLayer *layer1 = [[MGLCircleStyleLayer alloc] initWithIdentifier:@"layer1" source:source];
+ [self.style addLayer:layer1];
+
+ MGLCircleStyleLayer *layer3 = [[MGLCircleStyleLayer alloc] initWithIdentifier:@"layer3" source:source];
+ [self.style addLayer:layer3];
+
+ MGLCircleStyleLayer *layer2 = [[MGLCircleStyleLayer alloc] initWithIdentifier:@"layer2" source:source];
+ [self.style insertLayer:layer2 aboveLayer:layer1];
+
+ MGLCircleStyleLayer *layer4 = [[MGLCircleStyleLayer alloc] initWithIdentifier:@"layer4" source:source];
+ [self.style insertLayer:layer4 aboveLayer:layer3];
+
+ MGLCircleStyleLayer *layer0 = [[MGLCircleStyleLayer alloc] initWithIdentifier:@"layer0" source:source];
+ [self.style insertLayer:layer0 belowLayer:layer1];
+
+ NSArray<MGLStyleLayer *> *layers = [self.style layers];
+ NSUInteger startIndex = 0;
+ if ([layers.firstObject.identifier isEqualToString:@"com.mapbox.annotations.points"]) {
+ startIndex++;
+ }
+
+ XCTAssertEqualObjects(layers[startIndex++].identifier, layer0.identifier);
+ XCTAssertEqualObjects(layers[startIndex++].identifier, layer1.identifier);
+ XCTAssertEqualObjects(layers[startIndex++].identifier, layer2.identifier);
+ XCTAssertEqualObjects(layers[startIndex++].identifier, layer3.identifier);
+ XCTAssertEqualObjects(layers[startIndex++].identifier, layer4.identifier);
+}
+
@end
diff --git a/platform/darwin/test/MGLStyleValueTests.swift b/platform/darwin/test/MGLStyleValueTests.swift
index bf01435114..18b6a901de 100644
--- a/platform/darwin/test/MGLStyleValueTests.swift
+++ b/platform/darwin/test/MGLStyleValueTests.swift
@@ -17,8 +17,8 @@ extension MGLStyleValueTests {
XCTAssertEqual((symbolStyleLayer.iconHaloWidth as! MGLStyleConstantValue<NSNumber>).rawValue, 3)
// String
- symbolStyleLayer.textField = MGLStyleConstantValue(rawValue: "{name}")
- XCTAssertEqual((symbolStyleLayer.textField as! MGLStyleConstantValue<NSString>).rawValue, "{name}")
+ symbolStyleLayer.text = MGLStyleConstantValue(rawValue: "{name}")
+ XCTAssertEqual((symbolStyleLayer.text as! MGLStyleConstantValue<NSString>).rawValue, "{name}")
}
func testFunctions() {
@@ -32,7 +32,7 @@ extension MGLStyleValueTests {
3: MGLStyleValue(rawValue: true),
4: MGLStyleValue(rawValue: false),
]
- symbolStyleLayer.iconAllowsOverlap = MGLStyleFunction<NSNumber>(base: 1, stops: stops)
- XCTAssertEqual((symbolStyleLayer.iconAllowsOverlap as! MGLStyleFunction<NSNumber>), MGLStyleFunction(base: 1, stops: stops))
+ symbolStyleLayer.iconAllowsOverlap = MGLStyleFunction<NSNumber>(interpolationBase: 1, stops: stops)
+ XCTAssertEqual((symbolStyleLayer.iconAllowsOverlap as! MGLStyleFunction<NSNumber>), MGLStyleFunction(interpolationBase: 1, stops: stops))
}
}
diff --git a/platform/darwin/test/MGLSymbolStyleLayerTests.m b/platform/darwin/test/MGLSymbolStyleLayerTests.m
deleted file mode 100644
index 40250a8c72..0000000000
--- a/platform/darwin/test/MGLSymbolStyleLayerTests.m
+++ /dev/null
@@ -1,283 +0,0 @@
-// This file is generated.
-// Edit platform/darwin/scripts/generate-style-code.js, then run `make style-code-darwin`.
-
-#import "MGLStyleLayerTests.h"
-
-@interface MGLSymbolLayerTests : MGLStyleLayerTests
-@end
-
-@implementation MGLSymbolLayerTests
-
-+ (NSString *)layerType {
- return @"symbol";
-}
-
-- (void)testSymbolLayer {
- NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"amsterdam" ofType:@"geojson"];
- NSURL *url = [NSURL fileURLWithPath:filePath];
- MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"sourceID" URL:url options:nil];
- [self.mapView.style addSource:source];
- MGLSymbolStyleLayer *layer = [[MGLSymbolStyleLayer alloc] initWithIdentifier:@"layerID" source:source];
- [self.mapView.style addLayer:layer];
-
- layer.iconAllowsOverlap = [MGLRuntimeStylingHelper testBool];
- layer.iconIgnoresPlacement = [MGLRuntimeStylingHelper testBool];
- layer.iconImageName = [MGLRuntimeStylingHelper testString];
- layer.iconOffset = [MGLRuntimeStylingHelper testOffset];
- layer.iconOptional = [MGLRuntimeStylingHelper testBool];
- layer.iconPadding = [MGLRuntimeStylingHelper testNumber];
- layer.iconRotation = [MGLRuntimeStylingHelper testNumber];
- layer.iconRotationAlignment = [MGLRuntimeStylingHelper testEnum:MGLIconRotationAlignmentAuto type:@encode(MGLIconRotationAlignment)];
- layer.iconScale = [MGLRuntimeStylingHelper testNumber];
- layer.iconTextFit = [MGLRuntimeStylingHelper testEnum:MGLIconTextFitBoth type:@encode(MGLIconTextFit)];
- layer.iconTextFitPadding = [MGLRuntimeStylingHelper testPadding];
- layer.keepsIconUpright = [MGLRuntimeStylingHelper testBool];
- layer.keepsTextUpright = [MGLRuntimeStylingHelper testBool];
- layer.maximumTextAngle = [MGLRuntimeStylingHelper testNumber];
- layer.maximumTextWidth = [MGLRuntimeStylingHelper testNumber];
- layer.symbolAvoidsEdges = [MGLRuntimeStylingHelper testBool];
- layer.symbolPlacement = [MGLRuntimeStylingHelper testEnum:MGLSymbolPlacementLine type:@encode(MGLSymbolPlacement)];
- layer.symbolSpacing = [MGLRuntimeStylingHelper testNumber];
- layer.textAllowsOverlap = [MGLRuntimeStylingHelper testBool];
- layer.textAnchor = [MGLRuntimeStylingHelper testEnum:MGLTextAnchorBottomRight type:@encode(MGLTextAnchor)];
- layer.textField = [MGLRuntimeStylingHelper testString];
- layer.textFont = [MGLRuntimeStylingHelper testFont];
- layer.textIgnoresPlacement = [MGLRuntimeStylingHelper testBool];
- layer.textJustification = [MGLRuntimeStylingHelper testEnum:MGLTextJustificationRight type:@encode(MGLTextJustification)];
- layer.textLetterSpacing = [MGLRuntimeStylingHelper testNumber];
- layer.textLineHeight = [MGLRuntimeStylingHelper testNumber];
- layer.textOffset = [MGLRuntimeStylingHelper testOffset];
- layer.textOptional = [MGLRuntimeStylingHelper testBool];
- layer.textPadding = [MGLRuntimeStylingHelper testNumber];
- layer.textPitchAlignment = [MGLRuntimeStylingHelper testEnum:MGLTextPitchAlignmentAuto type:@encode(MGLTextPitchAlignment)];
- layer.textRotation = [MGLRuntimeStylingHelper testNumber];
- layer.textRotationAlignment = [MGLRuntimeStylingHelper testEnum:MGLTextRotationAlignmentAuto type:@encode(MGLTextRotationAlignment)];
- layer.textSize = [MGLRuntimeStylingHelper testNumber];
- layer.textTransform = [MGLRuntimeStylingHelper testEnum:MGLTextTransformLowercase type:@encode(MGLTextTransform)];
- layer.iconColor = [MGLRuntimeStylingHelper testColor];
- layer.iconHaloBlur = [MGLRuntimeStylingHelper testNumber];
- layer.iconHaloColor = [MGLRuntimeStylingHelper testColor];
- layer.iconHaloWidth = [MGLRuntimeStylingHelper testNumber];
- layer.iconOpacity = [MGLRuntimeStylingHelper testNumber];
- layer.iconTranslate = [MGLRuntimeStylingHelper testOffset];
- layer.iconTranslateAnchor = [MGLRuntimeStylingHelper testEnum:MGLIconTranslateAnchorViewport type:@encode(MGLIconTranslateAnchor)];
- layer.textColor = [MGLRuntimeStylingHelper testColor];
- layer.textHaloBlur = [MGLRuntimeStylingHelper testNumber];
- layer.textHaloColor = [MGLRuntimeStylingHelper testColor];
- layer.textHaloWidth = [MGLRuntimeStylingHelper testNumber];
- layer.textOpacity = [MGLRuntimeStylingHelper testNumber];
- layer.textTranslate = [MGLRuntimeStylingHelper testOffset];
- layer.textTranslateAnchor = [MGLRuntimeStylingHelper testEnum:MGLTextTranslateAnchorViewport type:@encode(MGLTextTranslateAnchor)];
-
- MGLSymbolStyleLayer *gLayer = (MGLSymbolStyleLayer *)[self.mapView.style layerWithIdentifier:@"layerID"];
- XCTAssertTrue([gLayer isKindOfClass:[MGLSymbolStyleLayer class]]);
- XCTAssertEqualObjects(gLayer.iconAllowsOverlap, [MGLRuntimeStylingHelper testBool]);
- XCTAssertEqualObjects(gLayer.iconIgnoresPlacement, [MGLRuntimeStylingHelper testBool]);
- XCTAssertEqualObjects(gLayer.iconImageName, [MGLRuntimeStylingHelper testString]);
- XCTAssertEqualObjects(gLayer.iconOffset, [MGLRuntimeStylingHelper testOffset]);
- XCTAssertEqualObjects(gLayer.iconOptional, [MGLRuntimeStylingHelper testBool]);
- XCTAssertEqualObjects(gLayer.iconPadding, [MGLRuntimeStylingHelper testNumber]);
- XCTAssertEqualObjects(gLayer.iconRotation, [MGLRuntimeStylingHelper testNumber]);
- XCTAssert([gLayer.iconRotationAlignment isKindOfClass:[MGLStyleConstantValue class]]);
- XCTAssertEqualObjects(gLayer.iconRotationAlignment, [MGLRuntimeStylingHelper testEnum:MGLIconRotationAlignmentAuto type:@encode(MGLIconRotationAlignment)]);
- XCTAssertEqualObjects(gLayer.iconScale, [MGLRuntimeStylingHelper testNumber]);
- XCTAssert([gLayer.iconTextFit isKindOfClass:[MGLStyleConstantValue class]]);
- XCTAssertEqualObjects(gLayer.iconTextFit, [MGLRuntimeStylingHelper testEnum:MGLIconTextFitBoth type:@encode(MGLIconTextFit)]);
- XCTAssertEqualObjects(gLayer.iconTextFitPadding, [MGLRuntimeStylingHelper testPadding]);
- XCTAssertEqualObjects(gLayer.keepsIconUpright, [MGLRuntimeStylingHelper testBool]);
- XCTAssertEqualObjects(gLayer.keepsTextUpright, [MGLRuntimeStylingHelper testBool]);
- XCTAssertEqualObjects(gLayer.maximumTextAngle, [MGLRuntimeStylingHelper testNumber]);
- XCTAssertEqualObjects(gLayer.maximumTextWidth, [MGLRuntimeStylingHelper testNumber]);
- XCTAssertEqualObjects(gLayer.symbolAvoidsEdges, [MGLRuntimeStylingHelper testBool]);
- XCTAssert([gLayer.symbolPlacement isKindOfClass:[MGLStyleConstantValue class]]);
- XCTAssertEqualObjects(gLayer.symbolPlacement, [MGLRuntimeStylingHelper testEnum:MGLSymbolPlacementLine type:@encode(MGLSymbolPlacement)]);
- XCTAssertEqualObjects(gLayer.symbolSpacing, [MGLRuntimeStylingHelper testNumber]);
- XCTAssertEqualObjects(gLayer.textAllowsOverlap, [MGLRuntimeStylingHelper testBool]);
- XCTAssert([gLayer.textAnchor isKindOfClass:[MGLStyleConstantValue class]]);
- XCTAssertEqualObjects(gLayer.textAnchor, [MGLRuntimeStylingHelper testEnum:MGLTextAnchorBottomRight type:@encode(MGLTextAnchor)]);
- XCTAssertEqualObjects(gLayer.textField, [MGLRuntimeStylingHelper testString]);
- XCTAssertEqualObjects(gLayer.textFont, [MGLRuntimeStylingHelper testFont]);
- XCTAssertEqualObjects(gLayer.textIgnoresPlacement, [MGLRuntimeStylingHelper testBool]);
- XCTAssert([gLayer.textJustification isKindOfClass:[MGLStyleConstantValue class]]);
- XCTAssertEqualObjects(gLayer.textJustification, [MGLRuntimeStylingHelper testEnum:MGLTextJustificationRight type:@encode(MGLTextJustification)]);
- XCTAssertEqualObjects(gLayer.textLetterSpacing, [MGLRuntimeStylingHelper testNumber]);
- XCTAssertEqualObjects(gLayer.textLineHeight, [MGLRuntimeStylingHelper testNumber]);
- XCTAssertEqualObjects(gLayer.textOffset, [MGLRuntimeStylingHelper testOffset]);
- XCTAssertEqualObjects(gLayer.textOptional, [MGLRuntimeStylingHelper testBool]);
- XCTAssertEqualObjects(gLayer.textPadding, [MGLRuntimeStylingHelper testNumber]);
- XCTAssert([gLayer.textPitchAlignment isKindOfClass:[MGLStyleConstantValue class]]);
- XCTAssertEqualObjects(gLayer.textPitchAlignment, [MGLRuntimeStylingHelper testEnum:MGLTextPitchAlignmentAuto type:@encode(MGLTextPitchAlignment)]);
- XCTAssertEqualObjects(gLayer.textRotation, [MGLRuntimeStylingHelper testNumber]);
- XCTAssert([gLayer.textRotationAlignment isKindOfClass:[MGLStyleConstantValue class]]);
- XCTAssertEqualObjects(gLayer.textRotationAlignment, [MGLRuntimeStylingHelper testEnum:MGLTextRotationAlignmentAuto type:@encode(MGLTextRotationAlignment)]);
- XCTAssertEqualObjects(gLayer.textSize, [MGLRuntimeStylingHelper testNumber]);
- XCTAssert([gLayer.textTransform isKindOfClass:[MGLStyleConstantValue class]]);
- XCTAssertEqualObjects(gLayer.textTransform, [MGLRuntimeStylingHelper testEnum:MGLTextTransformLowercase type:@encode(MGLTextTransform)]);
- XCTAssertEqualObjects(gLayer.iconColor, [MGLRuntimeStylingHelper testColor]);
- XCTAssertEqualObjects(gLayer.iconHaloBlur, [MGLRuntimeStylingHelper testNumber]);
- XCTAssertEqualObjects(gLayer.iconHaloColor, [MGLRuntimeStylingHelper testColor]);
- XCTAssertEqualObjects(gLayer.iconHaloWidth, [MGLRuntimeStylingHelper testNumber]);
- XCTAssertEqualObjects(gLayer.iconOpacity, [MGLRuntimeStylingHelper testNumber]);
- XCTAssertEqualObjects(gLayer.iconTranslate, [MGLRuntimeStylingHelper testOffset]);
- XCTAssert([gLayer.iconTranslateAnchor isKindOfClass:[MGLStyleConstantValue class]]);
- XCTAssertEqualObjects(gLayer.iconTranslateAnchor, [MGLRuntimeStylingHelper testEnum:MGLIconTranslateAnchorViewport type:@encode(MGLIconTranslateAnchor)]);
- XCTAssertEqualObjects(gLayer.textColor, [MGLRuntimeStylingHelper testColor]);
- XCTAssertEqualObjects(gLayer.textHaloBlur, [MGLRuntimeStylingHelper testNumber]);
- XCTAssertEqualObjects(gLayer.textHaloColor, [MGLRuntimeStylingHelper testColor]);
- XCTAssertEqualObjects(gLayer.textHaloWidth, [MGLRuntimeStylingHelper testNumber]);
- XCTAssertEqualObjects(gLayer.textOpacity, [MGLRuntimeStylingHelper testNumber]);
- XCTAssertEqualObjects(gLayer.textTranslate, [MGLRuntimeStylingHelper testOffset]);
- XCTAssert([gLayer.textTranslateAnchor isKindOfClass:[MGLStyleConstantValue class]]);
- XCTAssertEqualObjects(gLayer.textTranslateAnchor, [MGLRuntimeStylingHelper testEnum:MGLTextTranslateAnchorViewport type:@encode(MGLTextTranslateAnchor)]);
-
- layer.iconAllowsOverlap = [MGLRuntimeStylingHelper testBoolFunction];
- layer.iconIgnoresPlacement = [MGLRuntimeStylingHelper testBoolFunction];
- layer.iconImageName = [MGLRuntimeStylingHelper testStringFunction];
- layer.iconOffset = [MGLRuntimeStylingHelper testOffsetFunction];
- layer.iconOptional = [MGLRuntimeStylingHelper testBoolFunction];
- layer.iconPadding = [MGLRuntimeStylingHelper testNumberFunction];
- layer.iconRotation = [MGLRuntimeStylingHelper testNumberFunction];
- layer.iconRotationAlignment = [MGLRuntimeStylingHelper testEnumFunction:MGLIconRotationAlignmentAuto type:@encode(MGLIconRotationAlignment)];
- layer.iconScale = [MGLRuntimeStylingHelper testNumberFunction];
- layer.iconTextFit = [MGLRuntimeStylingHelper testEnumFunction:MGLIconTextFitBoth type:@encode(MGLIconTextFit)];
- layer.iconTextFitPadding = [MGLRuntimeStylingHelper testPaddingFunction];
- layer.keepsIconUpright = [MGLRuntimeStylingHelper testBoolFunction];
- layer.keepsTextUpright = [MGLRuntimeStylingHelper testBoolFunction];
- layer.maximumTextAngle = [MGLRuntimeStylingHelper testNumberFunction];
- layer.maximumTextWidth = [MGLRuntimeStylingHelper testNumberFunction];
- layer.symbolAvoidsEdges = [MGLRuntimeStylingHelper testBoolFunction];
- layer.symbolPlacement = [MGLRuntimeStylingHelper testEnumFunction:MGLSymbolPlacementLine type:@encode(MGLSymbolPlacement)];
- layer.symbolSpacing = [MGLRuntimeStylingHelper testNumberFunction];
- layer.textAllowsOverlap = [MGLRuntimeStylingHelper testBoolFunction];
- layer.textAnchor = [MGLRuntimeStylingHelper testEnumFunction:MGLTextAnchorBottomRight type:@encode(MGLTextAnchor)];
- layer.textField = [MGLRuntimeStylingHelper testStringFunction];
- layer.textFont = [MGLRuntimeStylingHelper testFontFunction];
- layer.textIgnoresPlacement = [MGLRuntimeStylingHelper testBoolFunction];
- layer.textJustification = [MGLRuntimeStylingHelper testEnumFunction:MGLTextJustificationRight type:@encode(MGLTextJustification)];
- layer.textLetterSpacing = [MGLRuntimeStylingHelper testNumberFunction];
- layer.textLineHeight = [MGLRuntimeStylingHelper testNumberFunction];
- layer.textOffset = [MGLRuntimeStylingHelper testOffsetFunction];
- layer.textOptional = [MGLRuntimeStylingHelper testBoolFunction];
- layer.textPadding = [MGLRuntimeStylingHelper testNumberFunction];
- layer.textPitchAlignment = [MGLRuntimeStylingHelper testEnumFunction:MGLTextPitchAlignmentAuto type:@encode(MGLTextPitchAlignment)];
- layer.textRotation = [MGLRuntimeStylingHelper testNumberFunction];
- layer.textRotationAlignment = [MGLRuntimeStylingHelper testEnumFunction:MGLTextRotationAlignmentAuto type:@encode(MGLTextRotationAlignment)];
- layer.textSize = [MGLRuntimeStylingHelper testNumberFunction];
- layer.textTransform = [MGLRuntimeStylingHelper testEnumFunction:MGLTextTransformLowercase type:@encode(MGLTextTransform)];
- layer.iconColor = [MGLRuntimeStylingHelper testColorFunction];
- layer.iconHaloBlur = [MGLRuntimeStylingHelper testNumberFunction];
- layer.iconHaloColor = [MGLRuntimeStylingHelper testColorFunction];
- layer.iconHaloWidth = [MGLRuntimeStylingHelper testNumberFunction];
- layer.iconOpacity = [MGLRuntimeStylingHelper testNumberFunction];
- layer.iconTranslate = [MGLRuntimeStylingHelper testOffsetFunction];
- layer.iconTranslateAnchor = [MGLRuntimeStylingHelper testEnumFunction:MGLIconTranslateAnchorViewport type:@encode(MGLIconTranslateAnchor)];
- layer.textColor = [MGLRuntimeStylingHelper testColorFunction];
- layer.textHaloBlur = [MGLRuntimeStylingHelper testNumberFunction];
- layer.textHaloColor = [MGLRuntimeStylingHelper testColorFunction];
- layer.textHaloWidth = [MGLRuntimeStylingHelper testNumberFunction];
- layer.textOpacity = [MGLRuntimeStylingHelper testNumberFunction];
- layer.textTranslate = [MGLRuntimeStylingHelper testOffsetFunction];
- layer.textTranslateAnchor = [MGLRuntimeStylingHelper testEnumFunction:MGLTextTranslateAnchorViewport type:@encode(MGLTextTranslateAnchor)];
-
- XCTAssertEqualObjects(gLayer.iconAllowsOverlap, [MGLRuntimeStylingHelper testBoolFunction]);
- XCTAssertEqualObjects(gLayer.iconIgnoresPlacement, [MGLRuntimeStylingHelper testBoolFunction]);
- XCTAssertEqualObjects(gLayer.iconImageName, [MGLRuntimeStylingHelper testStringFunction]);
- XCTAssertEqualObjects(gLayer.iconOffset, [MGLRuntimeStylingHelper testOffsetFunction]);
- XCTAssertEqualObjects(gLayer.iconOptional, [MGLRuntimeStylingHelper testBoolFunction]);
- XCTAssertEqualObjects(gLayer.iconPadding, [MGLRuntimeStylingHelper testNumberFunction]);
- XCTAssertEqualObjects(gLayer.iconRotation, [MGLRuntimeStylingHelper testNumberFunction]);
- XCTAssertEqualObjects(gLayer.iconRotationAlignment, [MGLRuntimeStylingHelper testEnumFunction:MGLIconRotationAlignmentAuto type:@encode(MGLIconRotationAlignment)]);
- XCTAssertEqualObjects(gLayer.iconScale, [MGLRuntimeStylingHelper testNumberFunction]);
- XCTAssertEqualObjects(gLayer.iconTextFit, [MGLRuntimeStylingHelper testEnumFunction:MGLIconTextFitBoth type:@encode(MGLIconTextFit)]);
- XCTAssertEqualObjects(gLayer.iconTextFitPadding, [MGLRuntimeStylingHelper testPaddingFunction]);
- XCTAssertEqualObjects(gLayer.keepsIconUpright, [MGLRuntimeStylingHelper testBoolFunction]);
- XCTAssertEqualObjects(gLayer.keepsTextUpright, [MGLRuntimeStylingHelper testBoolFunction]);
- XCTAssertEqualObjects(gLayer.maximumTextAngle, [MGLRuntimeStylingHelper testNumberFunction]);
- XCTAssertEqualObjects(gLayer.maximumTextWidth, [MGLRuntimeStylingHelper testNumberFunction]);
- XCTAssertEqualObjects(gLayer.symbolAvoidsEdges, [MGLRuntimeStylingHelper testBoolFunction]);
- XCTAssertEqualObjects(gLayer.symbolPlacement, [MGLRuntimeStylingHelper testEnumFunction:MGLSymbolPlacementLine type:@encode(MGLSymbolPlacement)]);
- XCTAssertEqualObjects(gLayer.symbolSpacing, [MGLRuntimeStylingHelper testNumberFunction]);
- XCTAssertEqualObjects(gLayer.textAllowsOverlap, [MGLRuntimeStylingHelper testBoolFunction]);
- XCTAssertEqualObjects(gLayer.textAnchor, [MGLRuntimeStylingHelper testEnumFunction:MGLTextAnchorBottomRight type:@encode(MGLTextAnchor)]);
- XCTAssertEqualObjects(gLayer.textField, [MGLRuntimeStylingHelper testStringFunction]);
- XCTAssertEqualObjects(gLayer.textFont, [MGLRuntimeStylingHelper testFontFunction]);
- XCTAssertEqualObjects(gLayer.textIgnoresPlacement, [MGLRuntimeStylingHelper testBoolFunction]);
- XCTAssertEqualObjects(gLayer.textJustification, [MGLRuntimeStylingHelper testEnumFunction:MGLTextJustificationRight type:@encode(MGLTextJustification)]);
- XCTAssertEqualObjects(gLayer.textLetterSpacing, [MGLRuntimeStylingHelper testNumberFunction]);
- XCTAssertEqualObjects(gLayer.textLineHeight, [MGLRuntimeStylingHelper testNumberFunction]);
- XCTAssertEqualObjects(gLayer.textOffset, [MGLRuntimeStylingHelper testOffsetFunction]);
- XCTAssertEqualObjects(gLayer.textOptional, [MGLRuntimeStylingHelper testBoolFunction]);
- XCTAssertEqualObjects(gLayer.textPadding, [MGLRuntimeStylingHelper testNumberFunction]);
- XCTAssertEqualObjects(gLayer.textPitchAlignment, [MGLRuntimeStylingHelper testEnumFunction:MGLTextPitchAlignmentAuto type:@encode(MGLTextPitchAlignment)]);
- XCTAssertEqualObjects(gLayer.textRotation, [MGLRuntimeStylingHelper testNumberFunction]);
- XCTAssertEqualObjects(gLayer.textRotationAlignment, [MGLRuntimeStylingHelper testEnumFunction:MGLTextRotationAlignmentAuto type:@encode(MGLTextRotationAlignment)]);
- XCTAssertEqualObjects(gLayer.textSize, [MGLRuntimeStylingHelper testNumberFunction]);
- XCTAssertEqualObjects(gLayer.textTransform, [MGLRuntimeStylingHelper testEnumFunction:MGLTextTransformLowercase type:@encode(MGLTextTransform)]);
- XCTAssertEqualObjects(gLayer.iconColor, [MGLRuntimeStylingHelper testColorFunction]);
- XCTAssertEqualObjects(gLayer.iconHaloBlur, [MGLRuntimeStylingHelper testNumberFunction]);
- XCTAssertEqualObjects(gLayer.iconHaloColor, [MGLRuntimeStylingHelper testColorFunction]);
- XCTAssertEqualObjects(gLayer.iconHaloWidth, [MGLRuntimeStylingHelper testNumberFunction]);
- XCTAssertEqualObjects(gLayer.iconOpacity, [MGLRuntimeStylingHelper testNumberFunction]);
- XCTAssertEqualObjects(gLayer.iconTranslate, [MGLRuntimeStylingHelper testOffsetFunction]);
- XCTAssertEqualObjects(gLayer.iconTranslateAnchor, [MGLRuntimeStylingHelper testEnumFunction:MGLIconTranslateAnchorViewport type:@encode(MGLIconTranslateAnchor)]);
- XCTAssertEqualObjects(gLayer.textColor, [MGLRuntimeStylingHelper testColorFunction]);
- XCTAssertEqualObjects(gLayer.textHaloBlur, [MGLRuntimeStylingHelper testNumberFunction]);
- XCTAssertEqualObjects(gLayer.textHaloColor, [MGLRuntimeStylingHelper testColorFunction]);
- XCTAssertEqualObjects(gLayer.textHaloWidth, [MGLRuntimeStylingHelper testNumberFunction]);
- XCTAssertEqualObjects(gLayer.textOpacity, [MGLRuntimeStylingHelper testNumberFunction]);
- XCTAssertEqualObjects(gLayer.textTranslate, [MGLRuntimeStylingHelper testOffsetFunction]);
- XCTAssertEqualObjects(gLayer.textTranslateAnchor, [MGLRuntimeStylingHelper testEnumFunction:MGLTextTranslateAnchorViewport type:@encode(MGLTextTranslateAnchor)]);
-}
-
-- (void)testPropertyNames {
- [self testPropertyName:@"icon-allows-overlap" isBoolean:YES];
- [self testPropertyName:@"icon-ignores-placement" isBoolean:YES];
- [self testPropertyName:@"icon-image-name" isBoolean:NO];
- [self testPropertyName:@"icon-offset" isBoolean:NO];
- [self testPropertyName:@"is-icon-optional" isBoolean:YES];
- [self testPropertyName:@"icon-padding" isBoolean:NO];
- [self testPropertyName:@"icon-rotation" isBoolean:NO];
- [self testPropertyName:@"icon-rotation-alignment" isBoolean:NO];
- [self testPropertyName:@"icon-scale" isBoolean:NO];
- [self testPropertyName:@"icon-text-fit" isBoolean:NO];
- [self testPropertyName:@"icon-text-fit-padding" isBoolean:NO];
- [self testPropertyName:@"keeps-icon-upright" isBoolean:YES];
- [self testPropertyName:@"keeps-text-upright" isBoolean:YES];
- [self testPropertyName:@"maximum-text-angle" isBoolean:NO];
- [self testPropertyName:@"maximum-text-width" isBoolean:NO];
- [self testPropertyName:@"symbol-avoids-edges" isBoolean:YES];
- [self testPropertyName:@"symbol-placement" isBoolean:NO];
- [self testPropertyName:@"symbol-spacing" isBoolean:NO];
- [self testPropertyName:@"text-allows-overlap" isBoolean:YES];
- [self testPropertyName:@"text-anchor" isBoolean:NO];
- [self testPropertyName:@"text-field" isBoolean:NO];
- [self testPropertyName:@"text-font" isBoolean:NO];
- [self testPropertyName:@"text-ignores-placement" isBoolean:YES];
- [self testPropertyName:@"text-justification" isBoolean:NO];
- [self testPropertyName:@"text-letter-spacing" isBoolean:NO];
- [self testPropertyName:@"text-line-height" isBoolean:NO];
- [self testPropertyName:@"text-offset" isBoolean:NO];
- [self testPropertyName:@"is-text-optional" isBoolean:YES];
- [self testPropertyName:@"text-padding" isBoolean:NO];
- [self testPropertyName:@"text-pitch-alignment" isBoolean:NO];
- [self testPropertyName:@"text-rotation" isBoolean:NO];
- [self testPropertyName:@"text-rotation-alignment" isBoolean:NO];
- [self testPropertyName:@"text-size" isBoolean:NO];
- [self testPropertyName:@"text-transform" isBoolean:NO];
- [self testPropertyName:@"icon-color" isBoolean:NO];
- [self testPropertyName:@"icon-halo-blur" isBoolean:NO];
- [self testPropertyName:@"icon-halo-color" isBoolean:NO];
- [self testPropertyName:@"icon-halo-width" isBoolean:NO];
- [self testPropertyName:@"icon-opacity" isBoolean:NO];
- [self testPropertyName:@"icon-translate" isBoolean:NO];
- [self testPropertyName:@"icon-translate-anchor" isBoolean:NO];
- [self testPropertyName:@"text-color" isBoolean:NO];
- [self testPropertyName:@"text-halo-blur" isBoolean:NO];
- [self testPropertyName:@"text-halo-color" isBoolean:NO];
- [self testPropertyName:@"text-halo-width" isBoolean:NO];
- [self testPropertyName:@"text-opacity" isBoolean:NO];
- [self testPropertyName:@"text-translate" isBoolean:NO];
- [self testPropertyName:@"text-translate-anchor" isBoolean:NO];
-}
-
-@end
diff --git a/platform/darwin/test/MGLSymbolStyleLayerTests.mm b/platform/darwin/test/MGLSymbolStyleLayerTests.mm
new file mode 100644
index 0000000000..80a9c9d3ec
--- /dev/null
+++ b/platform/darwin/test/MGLSymbolStyleLayerTests.mm
@@ -0,0 +1,1797 @@
+// This file is generated.
+// Edit platform/darwin/scripts/generate-style-code.js, then run `make style-code-darwin`.
+
+#import "MGLStyleLayerTests.h"
+
+#import "MGLStyleLayer_Private.h"
+
+#include <mbgl/style/layers/symbol_layer.hpp>
+
+@interface MGLSymbolLayerTests : MGLStyleLayerTests
+@end
+
+@implementation MGLSymbolLayerTests
+
++ (NSString *)layerType {
+ return @"symbol";
+}
+
+- (void)testPredicates {
+ MGLPointFeature *feature = [[MGLPointFeature alloc] init];
+ MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"sourceID" shape:feature options:nil];
+ MGLSymbolStyleLayer *layer = [[MGLSymbolStyleLayer alloc] initWithIdentifier:@"layerID" source:source];
+
+ XCTAssertNil(layer.sourceLayerIdentifier);
+ layer.sourceLayerIdentifier = @"layerID";
+ XCTAssertEqualObjects(layer.sourceLayerIdentifier, @"layerID");
+ layer.sourceLayerIdentifier = nil;
+ XCTAssertNil(layer.sourceLayerIdentifier);
+
+ XCTAssertNil(layer.predicate);
+ layer.predicate = [NSPredicate predicateWithValue:NO];
+ XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithValue:NO]);
+ layer.predicate = nil;
+ XCTAssertNil(layer.predicate);
+}
+
+- (void)testProperties {
+ MGLPointFeature *feature = [[MGLPointFeature alloc] init];
+ MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"sourceID" shape:feature options:nil];
+
+ MGLSymbolStyleLayer *layer = [[MGLSymbolStyleLayer alloc] initWithIdentifier:@"layerID" source:source];
+ XCTAssertNotEqual(layer.rawLayer, nullptr);
+ XCTAssertTrue(layer.rawLayer->is<mbgl::style::SymbolLayer>());
+ auto rawLayer = layer.rawLayer->as<mbgl::style::SymbolLayer>();
+
+ // icon-allow-overlap
+ {
+ XCTAssertTrue(rawLayer->getIconAllowOverlap().isUndefined(),
+ @"icon-allow-overlap should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.iconAllowsOverlap;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@YES];
+ layer.iconAllowsOverlap = styleValue;
+ mbgl::style::PropertyValue<bool> propertyValue = { true };
+ XCTAssertEqual(rawLayer->getIconAllowOverlap(), propertyValue,
+ @"Setting iconAllowsOverlap to a constant value should update icon-allow-overlap.");
+ XCTAssertEqualObjects(layer.iconAllowsOverlap, styleValue,
+ @"iconAllowsOverlap should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.iconAllowsOverlap = styleValue;
+ propertyValue = { mbgl::style::Function<bool> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getIconAllowOverlap(), propertyValue,
+ @"Setting iconAllowsOverlap to a function should update icon-allow-overlap.");
+ XCTAssertEqualObjects(layer.iconAllowsOverlap, styleValue,
+ @"iconAllowsOverlap should round-trip functions.");
+
+ layer.iconAllowsOverlap = nil;
+ XCTAssertTrue(rawLayer->getIconAllowOverlap().isUndefined(),
+ @"Unsetting iconAllowsOverlap should return icon-allow-overlap to the default value.");
+ XCTAssertEqualObjects(layer.iconAllowsOverlap, defaultStyleValue,
+ @"iconAllowsOverlap should return the default value after being unset.");
+ }
+
+ // icon-ignore-placement
+ {
+ XCTAssertTrue(rawLayer->getIconIgnorePlacement().isUndefined(),
+ @"icon-ignore-placement should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.iconIgnoresPlacement;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@YES];
+ layer.iconIgnoresPlacement = styleValue;
+ mbgl::style::PropertyValue<bool> propertyValue = { true };
+ XCTAssertEqual(rawLayer->getIconIgnorePlacement(), propertyValue,
+ @"Setting iconIgnoresPlacement to a constant value should update icon-ignore-placement.");
+ XCTAssertEqualObjects(layer.iconIgnoresPlacement, styleValue,
+ @"iconIgnoresPlacement should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.iconIgnoresPlacement = styleValue;
+ propertyValue = { mbgl::style::Function<bool> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getIconIgnorePlacement(), propertyValue,
+ @"Setting iconIgnoresPlacement to a function should update icon-ignore-placement.");
+ XCTAssertEqualObjects(layer.iconIgnoresPlacement, styleValue,
+ @"iconIgnoresPlacement should round-trip functions.");
+
+ layer.iconIgnoresPlacement = nil;
+ XCTAssertTrue(rawLayer->getIconIgnorePlacement().isUndefined(),
+ @"Unsetting iconIgnoresPlacement should return icon-ignore-placement to the default value.");
+ XCTAssertEqualObjects(layer.iconIgnoresPlacement, defaultStyleValue,
+ @"iconIgnoresPlacement should return the default value after being unset.");
+ }
+
+ // icon-image
+ {
+ XCTAssertTrue(rawLayer->getIconImage().isUndefined(),
+ @"icon-image should be unset initially.");
+ MGLStyleValue<NSString *> *defaultStyleValue = layer.iconImageName;
+
+ MGLStyleValue<NSString *> *styleValue = [MGLStyleValue<NSString *> valueWithRawValue:@"Icon Image"];
+ layer.iconImageName = styleValue;
+ mbgl::style::PropertyValue<std::string> propertyValue = { "Icon Image" };
+ XCTAssertEqual(rawLayer->getIconImage(), propertyValue,
+ @"Setting iconImageName to a constant value should update icon-image.");
+ XCTAssertEqualObjects(layer.iconImageName, styleValue,
+ @"iconImageName should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSString *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.iconImageName = styleValue;
+ propertyValue = { mbgl::style::Function<std::string> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getIconImage(), propertyValue,
+ @"Setting iconImageName to a function should update icon-image.");
+ XCTAssertEqualObjects(layer.iconImageName, styleValue,
+ @"iconImageName should round-trip functions.");
+
+ layer.iconImageName = nil;
+ XCTAssertTrue(rawLayer->getIconImage().isUndefined(),
+ @"Unsetting iconImageName should return icon-image to the default value.");
+ XCTAssertEqualObjects(layer.iconImageName, defaultStyleValue,
+ @"iconImageName should return the default value after being unset.");
+ }
+
+ // icon-offset
+ {
+ XCTAssertTrue(rawLayer->getIconOffset().isUndefined(),
+ @"icon-offset should be unset initially.");
+ MGLStyleValue<NSValue *> *defaultStyleValue = layer.iconOffset;
+
+ MGLStyleValue<NSValue *> *styleValue = [MGLStyleValue<NSValue *> valueWithRawValue:
+#if TARGET_OS_IPHONE
+ [NSValue valueWithCGVector:CGVectorMake(1, 1)]
+#else
+ [NSValue valueWithMGLVector:CGVectorMake(1, -1)]
+#endif
+ ];
+ layer.iconOffset = styleValue;
+ mbgl::style::PropertyValue<std::array<float, 2>> propertyValue = { { 1, 1 } };
+ XCTAssertEqual(rawLayer->getIconOffset(), propertyValue,
+ @"Setting iconOffset to a constant value should update icon-offset.");
+ XCTAssertEqualObjects(layer.iconOffset, styleValue,
+ @"iconOffset should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSValue *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.iconOffset = styleValue;
+ propertyValue = { mbgl::style::Function<std::array<float, 2>> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getIconOffset(), propertyValue,
+ @"Setting iconOffset to a function should update icon-offset.");
+ XCTAssertEqualObjects(layer.iconOffset, styleValue,
+ @"iconOffset should round-trip functions.");
+
+ layer.iconOffset = nil;
+ XCTAssertTrue(rawLayer->getIconOffset().isUndefined(),
+ @"Unsetting iconOffset should return icon-offset to the default value.");
+ XCTAssertEqualObjects(layer.iconOffset, defaultStyleValue,
+ @"iconOffset should return the default value after being unset.");
+ }
+
+ // icon-optional
+ {
+ XCTAssertTrue(rawLayer->getIconOptional().isUndefined(),
+ @"icon-optional should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.iconOptional;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@YES];
+ layer.iconOptional = styleValue;
+ mbgl::style::PropertyValue<bool> propertyValue = { true };
+ XCTAssertEqual(rawLayer->getIconOptional(), propertyValue,
+ @"Setting iconOptional to a constant value should update icon-optional.");
+ XCTAssertEqualObjects(layer.iconOptional, styleValue,
+ @"iconOptional should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.iconOptional = styleValue;
+ propertyValue = { mbgl::style::Function<bool> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getIconOptional(), propertyValue,
+ @"Setting iconOptional to a function should update icon-optional.");
+ XCTAssertEqualObjects(layer.iconOptional, styleValue,
+ @"iconOptional should round-trip functions.");
+
+ layer.iconOptional = nil;
+ XCTAssertTrue(rawLayer->getIconOptional().isUndefined(),
+ @"Unsetting iconOptional should return icon-optional to the default value.");
+ XCTAssertEqualObjects(layer.iconOptional, defaultStyleValue,
+ @"iconOptional should return the default value after being unset.");
+ }
+
+ // icon-padding
+ {
+ XCTAssertTrue(rawLayer->getIconPadding().isUndefined(),
+ @"icon-padding should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.iconPadding;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.iconPadding = styleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getIconPadding(), propertyValue,
+ @"Setting iconPadding to a constant value should update icon-padding.");
+ XCTAssertEqualObjects(layer.iconPadding, styleValue,
+ @"iconPadding should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.iconPadding = styleValue;
+ propertyValue = { mbgl::style::Function<float> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getIconPadding(), propertyValue,
+ @"Setting iconPadding to a function should update icon-padding.");
+ XCTAssertEqualObjects(layer.iconPadding, styleValue,
+ @"iconPadding should round-trip functions.");
+
+ layer.iconPadding = nil;
+ XCTAssertTrue(rawLayer->getIconPadding().isUndefined(),
+ @"Unsetting iconPadding should return icon-padding to the default value.");
+ XCTAssertEqualObjects(layer.iconPadding, defaultStyleValue,
+ @"iconPadding should return the default value after being unset.");
+ }
+
+ // icon-rotate
+ {
+ XCTAssertTrue(rawLayer->getIconRotate().isUndefined(),
+ @"icon-rotate should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.iconRotation;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.iconRotation = styleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getIconRotate(), propertyValue,
+ @"Setting iconRotation to a constant value should update icon-rotate.");
+ XCTAssertEqualObjects(layer.iconRotation, styleValue,
+ @"iconRotation should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.iconRotation = styleValue;
+ propertyValue = { mbgl::style::Function<float> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getIconRotate(), propertyValue,
+ @"Setting iconRotation to a function should update icon-rotate.");
+ XCTAssertEqualObjects(layer.iconRotation, styleValue,
+ @"iconRotation should round-trip functions.");
+
+ layer.iconRotation = nil;
+ XCTAssertTrue(rawLayer->getIconRotate().isUndefined(),
+ @"Unsetting iconRotation should return icon-rotate to the default value.");
+ XCTAssertEqualObjects(layer.iconRotation, defaultStyleValue,
+ @"iconRotation should return the default value after being unset.");
+ }
+
+ // icon-rotation-alignment
+ {
+ XCTAssertTrue(rawLayer->getIconRotationAlignment().isUndefined(),
+ @"icon-rotation-alignment should be unset initially.");
+ MGLStyleValue<NSValue *> *defaultStyleValue = layer.iconRotationAlignment;
+
+ MGLStyleValue<NSValue *> *styleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLIconRotationAlignment:MGLIconRotationAlignmentAuto]];
+ layer.iconRotationAlignment = styleValue;
+ mbgl::style::PropertyValue<mbgl::style::AlignmentType> propertyValue = { mbgl::style::AlignmentType::Auto };
+ XCTAssertEqual(rawLayer->getIconRotationAlignment(), propertyValue,
+ @"Setting iconRotationAlignment to a constant value should update icon-rotation-alignment.");
+ XCTAssertEqualObjects(layer.iconRotationAlignment, styleValue,
+ @"iconRotationAlignment should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSValue *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.iconRotationAlignment = styleValue;
+ propertyValue = { mbgl::style::Function<mbgl::style::AlignmentType> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getIconRotationAlignment(), propertyValue,
+ @"Setting iconRotationAlignment to a function should update icon-rotation-alignment.");
+ XCTAssertEqualObjects(layer.iconRotationAlignment, styleValue,
+ @"iconRotationAlignment should round-trip functions.");
+
+ layer.iconRotationAlignment = nil;
+ XCTAssertTrue(rawLayer->getIconRotationAlignment().isUndefined(),
+ @"Unsetting iconRotationAlignment should return icon-rotation-alignment to the default value.");
+ XCTAssertEqualObjects(layer.iconRotationAlignment, defaultStyleValue,
+ @"iconRotationAlignment should return the default value after being unset.");
+ }
+
+ // icon-size
+ {
+ XCTAssertTrue(rawLayer->getIconSize().isUndefined(),
+ @"icon-size should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.iconScale;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.iconScale = styleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getIconSize(), propertyValue,
+ @"Setting iconScale to a constant value should update icon-size.");
+ XCTAssertEqualObjects(layer.iconScale, styleValue,
+ @"iconScale should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.iconScale = styleValue;
+ propertyValue = { mbgl::style::Function<float> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getIconSize(), propertyValue,
+ @"Setting iconScale to a function should update icon-size.");
+ XCTAssertEqualObjects(layer.iconScale, styleValue,
+ @"iconScale should round-trip functions.");
+
+ layer.iconScale = nil;
+ XCTAssertTrue(rawLayer->getIconSize().isUndefined(),
+ @"Unsetting iconScale should return icon-size to the default value.");
+ XCTAssertEqualObjects(layer.iconScale, defaultStyleValue,
+ @"iconScale should return the default value after being unset.");
+ }
+
+ // icon-text-fit
+ {
+ XCTAssertTrue(rawLayer->getIconTextFit().isUndefined(),
+ @"icon-text-fit should be unset initially.");
+ MGLStyleValue<NSValue *> *defaultStyleValue = layer.iconTextFit;
+
+ MGLStyleValue<NSValue *> *styleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLIconTextFit:MGLIconTextFitBoth]];
+ layer.iconTextFit = styleValue;
+ mbgl::style::PropertyValue<mbgl::style::IconTextFitType> propertyValue = { mbgl::style::IconTextFitType::Both };
+ XCTAssertEqual(rawLayer->getIconTextFit(), propertyValue,
+ @"Setting iconTextFit to a constant value should update icon-text-fit.");
+ XCTAssertEqualObjects(layer.iconTextFit, styleValue,
+ @"iconTextFit should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSValue *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.iconTextFit = styleValue;
+ propertyValue = { mbgl::style::Function<mbgl::style::IconTextFitType> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getIconTextFit(), propertyValue,
+ @"Setting iconTextFit to a function should update icon-text-fit.");
+ XCTAssertEqualObjects(layer.iconTextFit, styleValue,
+ @"iconTextFit should round-trip functions.");
+
+ layer.iconTextFit = nil;
+ XCTAssertTrue(rawLayer->getIconTextFit().isUndefined(),
+ @"Unsetting iconTextFit should return icon-text-fit to the default value.");
+ XCTAssertEqualObjects(layer.iconTextFit, defaultStyleValue,
+ @"iconTextFit should return the default value after being unset.");
+ }
+
+ // icon-text-fit-padding
+ {
+ XCTAssertTrue(rawLayer->getIconTextFitPadding().isUndefined(),
+ @"icon-text-fit-padding should be unset initially.");
+ MGLStyleValue<NSValue *> *defaultStyleValue = layer.iconTextFitPadding;
+
+ MGLStyleValue<NSValue *> *styleValue = [MGLStyleValue<NSValue *> valueWithRawValue:
+#if TARGET_OS_IPHONE
+ [NSValue valueWithUIEdgeInsets:UIEdgeInsetsMake(1, 1, 1, 1)]
+#else
+ [NSValue valueWithEdgeInsets:NSEdgeInsetsMake(1, 1, 1, 1)]
+#endif
+ ];
+ layer.iconTextFitPadding = styleValue;
+ mbgl::style::PropertyValue<std::array<float, 4>> propertyValue = { { 1, 1, 1, 1 } };
+ XCTAssertEqual(rawLayer->getIconTextFitPadding(), propertyValue,
+ @"Setting iconTextFitPadding to a constant value should update icon-text-fit-padding.");
+ XCTAssertEqualObjects(layer.iconTextFitPadding, styleValue,
+ @"iconTextFitPadding should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSValue *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.iconTextFitPadding = styleValue;
+ propertyValue = { mbgl::style::Function<std::array<float, 4>> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getIconTextFitPadding(), propertyValue,
+ @"Setting iconTextFitPadding to a function should update icon-text-fit-padding.");
+ XCTAssertEqualObjects(layer.iconTextFitPadding, styleValue,
+ @"iconTextFitPadding should round-trip functions.");
+
+ layer.iconTextFitPadding = nil;
+ XCTAssertTrue(rawLayer->getIconTextFitPadding().isUndefined(),
+ @"Unsetting iconTextFitPadding should return icon-text-fit-padding to the default value.");
+ XCTAssertEqualObjects(layer.iconTextFitPadding, defaultStyleValue,
+ @"iconTextFitPadding should return the default value after being unset.");
+ }
+
+ // icon-keep-upright
+ {
+ XCTAssertTrue(rawLayer->getIconKeepUpright().isUndefined(),
+ @"icon-keep-upright should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.keepsIconUpright;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@YES];
+ layer.keepsIconUpright = styleValue;
+ mbgl::style::PropertyValue<bool> propertyValue = { true };
+ XCTAssertEqual(rawLayer->getIconKeepUpright(), propertyValue,
+ @"Setting keepsIconUpright to a constant value should update icon-keep-upright.");
+ XCTAssertEqualObjects(layer.keepsIconUpright, styleValue,
+ @"keepsIconUpright should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.keepsIconUpright = styleValue;
+ propertyValue = { mbgl::style::Function<bool> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getIconKeepUpright(), propertyValue,
+ @"Setting keepsIconUpright to a function should update icon-keep-upright.");
+ XCTAssertEqualObjects(layer.keepsIconUpright, styleValue,
+ @"keepsIconUpright should round-trip functions.");
+
+ layer.keepsIconUpright = nil;
+ XCTAssertTrue(rawLayer->getIconKeepUpright().isUndefined(),
+ @"Unsetting keepsIconUpright should return icon-keep-upright to the default value.");
+ XCTAssertEqualObjects(layer.keepsIconUpright, defaultStyleValue,
+ @"keepsIconUpright should return the default value after being unset.");
+ }
+
+ // text-keep-upright
+ {
+ XCTAssertTrue(rawLayer->getTextKeepUpright().isUndefined(),
+ @"text-keep-upright should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.keepsTextUpright;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@NO];
+ layer.keepsTextUpright = styleValue;
+ mbgl::style::PropertyValue<bool> propertyValue = { false };
+ XCTAssertEqual(rawLayer->getTextKeepUpright(), propertyValue,
+ @"Setting keepsTextUpright to a constant value should update text-keep-upright.");
+ XCTAssertEqualObjects(layer.keepsTextUpright, styleValue,
+ @"keepsTextUpright should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.keepsTextUpright = styleValue;
+ propertyValue = { mbgl::style::Function<bool> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getTextKeepUpright(), propertyValue,
+ @"Setting keepsTextUpright to a function should update text-keep-upright.");
+ XCTAssertEqualObjects(layer.keepsTextUpright, styleValue,
+ @"keepsTextUpright should round-trip functions.");
+
+ layer.keepsTextUpright = nil;
+ XCTAssertTrue(rawLayer->getTextKeepUpright().isUndefined(),
+ @"Unsetting keepsTextUpright should return text-keep-upright to the default value.");
+ XCTAssertEqualObjects(layer.keepsTextUpright, defaultStyleValue,
+ @"keepsTextUpright should return the default value after being unset.");
+ }
+
+ // text-max-angle
+ {
+ XCTAssertTrue(rawLayer->getTextMaxAngle().isUndefined(),
+ @"text-max-angle should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.maximumTextAngle;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.maximumTextAngle = styleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getTextMaxAngle(), propertyValue,
+ @"Setting maximumTextAngle to a constant value should update text-max-angle.");
+ XCTAssertEqualObjects(layer.maximumTextAngle, styleValue,
+ @"maximumTextAngle should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.maximumTextAngle = styleValue;
+ propertyValue = { mbgl::style::Function<float> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getTextMaxAngle(), propertyValue,
+ @"Setting maximumTextAngle to a function should update text-max-angle.");
+ XCTAssertEqualObjects(layer.maximumTextAngle, styleValue,
+ @"maximumTextAngle should round-trip functions.");
+
+ layer.maximumTextAngle = nil;
+ XCTAssertTrue(rawLayer->getTextMaxAngle().isUndefined(),
+ @"Unsetting maximumTextAngle should return text-max-angle to the default value.");
+ XCTAssertEqualObjects(layer.maximumTextAngle, defaultStyleValue,
+ @"maximumTextAngle should return the default value after being unset.");
+ }
+
+ // text-max-width
+ {
+ XCTAssertTrue(rawLayer->getTextMaxWidth().isUndefined(),
+ @"text-max-width should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.maximumTextWidth;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.maximumTextWidth = styleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getTextMaxWidth(), propertyValue,
+ @"Setting maximumTextWidth to a constant value should update text-max-width.");
+ XCTAssertEqualObjects(layer.maximumTextWidth, styleValue,
+ @"maximumTextWidth should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.maximumTextWidth = styleValue;
+ propertyValue = { mbgl::style::Function<float> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getTextMaxWidth(), propertyValue,
+ @"Setting maximumTextWidth to a function should update text-max-width.");
+ XCTAssertEqualObjects(layer.maximumTextWidth, styleValue,
+ @"maximumTextWidth should round-trip functions.");
+
+ layer.maximumTextWidth = nil;
+ XCTAssertTrue(rawLayer->getTextMaxWidth().isUndefined(),
+ @"Unsetting maximumTextWidth should return text-max-width to the default value.");
+ XCTAssertEqualObjects(layer.maximumTextWidth, defaultStyleValue,
+ @"maximumTextWidth should return the default value after being unset.");
+ }
+
+ // symbol-avoid-edges
+ {
+ XCTAssertTrue(rawLayer->getSymbolAvoidEdges().isUndefined(),
+ @"symbol-avoid-edges should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.symbolAvoidsEdges;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@YES];
+ layer.symbolAvoidsEdges = styleValue;
+ mbgl::style::PropertyValue<bool> propertyValue = { true };
+ XCTAssertEqual(rawLayer->getSymbolAvoidEdges(), propertyValue,
+ @"Setting symbolAvoidsEdges to a constant value should update symbol-avoid-edges.");
+ XCTAssertEqualObjects(layer.symbolAvoidsEdges, styleValue,
+ @"symbolAvoidsEdges should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.symbolAvoidsEdges = styleValue;
+ propertyValue = { mbgl::style::Function<bool> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getSymbolAvoidEdges(), propertyValue,
+ @"Setting symbolAvoidsEdges to a function should update symbol-avoid-edges.");
+ XCTAssertEqualObjects(layer.symbolAvoidsEdges, styleValue,
+ @"symbolAvoidsEdges should round-trip functions.");
+
+ layer.symbolAvoidsEdges = nil;
+ XCTAssertTrue(rawLayer->getSymbolAvoidEdges().isUndefined(),
+ @"Unsetting symbolAvoidsEdges should return symbol-avoid-edges to the default value.");
+ XCTAssertEqualObjects(layer.symbolAvoidsEdges, defaultStyleValue,
+ @"symbolAvoidsEdges should return the default value after being unset.");
+ }
+
+ // symbol-placement
+ {
+ XCTAssertTrue(rawLayer->getSymbolPlacement().isUndefined(),
+ @"symbol-placement should be unset initially.");
+ MGLStyleValue<NSValue *> *defaultStyleValue = layer.symbolPlacement;
+
+ MGLStyleValue<NSValue *> *styleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLSymbolPlacement:MGLSymbolPlacementLine]];
+ layer.symbolPlacement = styleValue;
+ mbgl::style::PropertyValue<mbgl::style::SymbolPlacementType> propertyValue = { mbgl::style::SymbolPlacementType::Line };
+ XCTAssertEqual(rawLayer->getSymbolPlacement(), propertyValue,
+ @"Setting symbolPlacement to a constant value should update symbol-placement.");
+ XCTAssertEqualObjects(layer.symbolPlacement, styleValue,
+ @"symbolPlacement should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSValue *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.symbolPlacement = styleValue;
+ propertyValue = { mbgl::style::Function<mbgl::style::SymbolPlacementType> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getSymbolPlacement(), propertyValue,
+ @"Setting symbolPlacement to a function should update symbol-placement.");
+ XCTAssertEqualObjects(layer.symbolPlacement, styleValue,
+ @"symbolPlacement should round-trip functions.");
+
+ layer.symbolPlacement = nil;
+ XCTAssertTrue(rawLayer->getSymbolPlacement().isUndefined(),
+ @"Unsetting symbolPlacement should return symbol-placement to the default value.");
+ XCTAssertEqualObjects(layer.symbolPlacement, defaultStyleValue,
+ @"symbolPlacement should return the default value after being unset.");
+ }
+
+ // symbol-spacing
+ {
+ XCTAssertTrue(rawLayer->getSymbolSpacing().isUndefined(),
+ @"symbol-spacing should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.symbolSpacing;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.symbolSpacing = styleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getSymbolSpacing(), propertyValue,
+ @"Setting symbolSpacing to a constant value should update symbol-spacing.");
+ XCTAssertEqualObjects(layer.symbolSpacing, styleValue,
+ @"symbolSpacing should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.symbolSpacing = styleValue;
+ propertyValue = { mbgl::style::Function<float> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getSymbolSpacing(), propertyValue,
+ @"Setting symbolSpacing to a function should update symbol-spacing.");
+ XCTAssertEqualObjects(layer.symbolSpacing, styleValue,
+ @"symbolSpacing should round-trip functions.");
+
+ layer.symbolSpacing = nil;
+ XCTAssertTrue(rawLayer->getSymbolSpacing().isUndefined(),
+ @"Unsetting symbolSpacing should return symbol-spacing to the default value.");
+ XCTAssertEqualObjects(layer.symbolSpacing, defaultStyleValue,
+ @"symbolSpacing should return the default value after being unset.");
+ }
+
+ // text-field
+ {
+ XCTAssertTrue(rawLayer->getTextField().isUndefined(),
+ @"text-field should be unset initially.");
+ MGLStyleValue<NSString *> *defaultStyleValue = layer.text;
+
+ MGLStyleValue<NSString *> *styleValue = [MGLStyleValue<NSString *> valueWithRawValue:@"Text Field"];
+ layer.text = styleValue;
+ mbgl::style::PropertyValue<std::string> propertyValue = { "Text Field" };
+ XCTAssertEqual(rawLayer->getTextField(), propertyValue,
+ @"Setting text to a constant value should update text-field.");
+ XCTAssertEqualObjects(layer.text, styleValue,
+ @"text should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSString *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.text = styleValue;
+ propertyValue = { mbgl::style::Function<std::string> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getTextField(), propertyValue,
+ @"Setting text to a function should update text-field.");
+ XCTAssertEqualObjects(layer.text, styleValue,
+ @"text should round-trip functions.");
+
+ layer.text = nil;
+ XCTAssertTrue(rawLayer->getTextField().isUndefined(),
+ @"Unsetting text should return text-field to the default value.");
+ XCTAssertEqualObjects(layer.text, defaultStyleValue,
+ @"text should return the default value after being unset.");
+ }
+
+ // text-allow-overlap
+ {
+ XCTAssertTrue(rawLayer->getTextAllowOverlap().isUndefined(),
+ @"text-allow-overlap should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.textAllowsOverlap;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@YES];
+ layer.textAllowsOverlap = styleValue;
+ mbgl::style::PropertyValue<bool> propertyValue = { true };
+ XCTAssertEqual(rawLayer->getTextAllowOverlap(), propertyValue,
+ @"Setting textAllowsOverlap to a constant value should update text-allow-overlap.");
+ XCTAssertEqualObjects(layer.textAllowsOverlap, styleValue,
+ @"textAllowsOverlap should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.textAllowsOverlap = styleValue;
+ propertyValue = { mbgl::style::Function<bool> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getTextAllowOverlap(), propertyValue,
+ @"Setting textAllowsOverlap to a function should update text-allow-overlap.");
+ XCTAssertEqualObjects(layer.textAllowsOverlap, styleValue,
+ @"textAllowsOverlap should round-trip functions.");
+
+ layer.textAllowsOverlap = nil;
+ XCTAssertTrue(rawLayer->getTextAllowOverlap().isUndefined(),
+ @"Unsetting textAllowsOverlap should return text-allow-overlap to the default value.");
+ XCTAssertEqualObjects(layer.textAllowsOverlap, defaultStyleValue,
+ @"textAllowsOverlap should return the default value after being unset.");
+ }
+
+ // text-anchor
+ {
+ XCTAssertTrue(rawLayer->getTextAnchor().isUndefined(),
+ @"text-anchor should be unset initially.");
+ MGLStyleValue<NSValue *> *defaultStyleValue = layer.textAnchor;
+
+ MGLStyleValue<NSValue *> *styleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLTextAnchor:MGLTextAnchorBottomRight]];
+ layer.textAnchor = styleValue;
+ mbgl::style::PropertyValue<mbgl::style::TextAnchorType> propertyValue = { mbgl::style::TextAnchorType::BottomRight };
+ XCTAssertEqual(rawLayer->getTextAnchor(), propertyValue,
+ @"Setting textAnchor to a constant value should update text-anchor.");
+ XCTAssertEqualObjects(layer.textAnchor, styleValue,
+ @"textAnchor should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSValue *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.textAnchor = styleValue;
+ propertyValue = { mbgl::style::Function<mbgl::style::TextAnchorType> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getTextAnchor(), propertyValue,
+ @"Setting textAnchor to a function should update text-anchor.");
+ XCTAssertEqualObjects(layer.textAnchor, styleValue,
+ @"textAnchor should round-trip functions.");
+
+ layer.textAnchor = nil;
+ XCTAssertTrue(rawLayer->getTextAnchor().isUndefined(),
+ @"Unsetting textAnchor should return text-anchor to the default value.");
+ XCTAssertEqualObjects(layer.textAnchor, defaultStyleValue,
+ @"textAnchor should return the default value after being unset.");
+ }
+
+ // text-font
+ {
+ XCTAssertTrue(rawLayer->getTextFont().isUndefined(),
+ @"text-font should be unset initially.");
+ MGLStyleValue<NSArray<NSString *> *> *defaultStyleValue = layer.textFontNames;
+
+ MGLStyleValue<NSArray<NSString *> *> *styleValue = [MGLStyleValue<NSArray<NSString *> *> valueWithRawValue:@[@"Text Font", @"Tnof Txet"]];
+ layer.textFontNames = styleValue;
+ mbgl::style::PropertyValue<std::vector<std::string>> propertyValue = { { "Text Font", "Tnof Txet" } };
+ XCTAssertEqual(rawLayer->getTextFont(), propertyValue,
+ @"Setting textFontNames to a constant value should update text-font.");
+ XCTAssertEqualObjects(layer.textFontNames, styleValue,
+ @"textFontNames should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSArray<NSString *> *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.textFontNames = styleValue;
+ propertyValue = { mbgl::style::Function<std::vector<std::string>> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getTextFont(), propertyValue,
+ @"Setting textFontNames to a function should update text-font.");
+ XCTAssertEqualObjects(layer.textFontNames, styleValue,
+ @"textFontNames should round-trip functions.");
+
+ layer.textFontNames = nil;
+ XCTAssertTrue(rawLayer->getTextFont().isUndefined(),
+ @"Unsetting textFontNames should return text-font to the default value.");
+ XCTAssertEqualObjects(layer.textFontNames, defaultStyleValue,
+ @"textFontNames should return the default value after being unset.");
+ }
+
+ // text-size
+ {
+ XCTAssertTrue(rawLayer->getTextSize().isUndefined(),
+ @"text-size should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.textFontSize;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.textFontSize = styleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getTextSize(), propertyValue,
+ @"Setting textFontSize to a constant value should update text-size.");
+ XCTAssertEqualObjects(layer.textFontSize, styleValue,
+ @"textFontSize should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.textFontSize = styleValue;
+ propertyValue = { mbgl::style::Function<float> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getTextSize(), propertyValue,
+ @"Setting textFontSize to a function should update text-size.");
+ XCTAssertEqualObjects(layer.textFontSize, styleValue,
+ @"textFontSize should round-trip functions.");
+
+ layer.textFontSize = nil;
+ XCTAssertTrue(rawLayer->getTextSize().isUndefined(),
+ @"Unsetting textFontSize should return text-size to the default value.");
+ XCTAssertEqualObjects(layer.textFontSize, defaultStyleValue,
+ @"textFontSize should return the default value after being unset.");
+ }
+
+ // text-ignore-placement
+ {
+ XCTAssertTrue(rawLayer->getTextIgnorePlacement().isUndefined(),
+ @"text-ignore-placement should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.textIgnoresPlacement;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@YES];
+ layer.textIgnoresPlacement = styleValue;
+ mbgl::style::PropertyValue<bool> propertyValue = { true };
+ XCTAssertEqual(rawLayer->getTextIgnorePlacement(), propertyValue,
+ @"Setting textIgnoresPlacement to a constant value should update text-ignore-placement.");
+ XCTAssertEqualObjects(layer.textIgnoresPlacement, styleValue,
+ @"textIgnoresPlacement should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.textIgnoresPlacement = styleValue;
+ propertyValue = { mbgl::style::Function<bool> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getTextIgnorePlacement(), propertyValue,
+ @"Setting textIgnoresPlacement to a function should update text-ignore-placement.");
+ XCTAssertEqualObjects(layer.textIgnoresPlacement, styleValue,
+ @"textIgnoresPlacement should round-trip functions.");
+
+ layer.textIgnoresPlacement = nil;
+ XCTAssertTrue(rawLayer->getTextIgnorePlacement().isUndefined(),
+ @"Unsetting textIgnoresPlacement should return text-ignore-placement to the default value.");
+ XCTAssertEqualObjects(layer.textIgnoresPlacement, defaultStyleValue,
+ @"textIgnoresPlacement should return the default value after being unset.");
+ }
+
+ // text-justify
+ {
+ XCTAssertTrue(rawLayer->getTextJustify().isUndefined(),
+ @"text-justify should be unset initially.");
+ MGLStyleValue<NSValue *> *defaultStyleValue = layer.textJustification;
+
+ MGLStyleValue<NSValue *> *styleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLTextJustification:MGLTextJustificationRight]];
+ layer.textJustification = styleValue;
+ mbgl::style::PropertyValue<mbgl::style::TextJustifyType> propertyValue = { mbgl::style::TextJustifyType::Right };
+ XCTAssertEqual(rawLayer->getTextJustify(), propertyValue,
+ @"Setting textJustification to a constant value should update text-justify.");
+ XCTAssertEqualObjects(layer.textJustification, styleValue,
+ @"textJustification should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSValue *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.textJustification = styleValue;
+ propertyValue = { mbgl::style::Function<mbgl::style::TextJustifyType> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getTextJustify(), propertyValue,
+ @"Setting textJustification to a function should update text-justify.");
+ XCTAssertEqualObjects(layer.textJustification, styleValue,
+ @"textJustification should round-trip functions.");
+
+ layer.textJustification = nil;
+ XCTAssertTrue(rawLayer->getTextJustify().isUndefined(),
+ @"Unsetting textJustification should return text-justify to the default value.");
+ XCTAssertEqualObjects(layer.textJustification, defaultStyleValue,
+ @"textJustification should return the default value after being unset.");
+ }
+
+ // text-letter-spacing
+ {
+ XCTAssertTrue(rawLayer->getTextLetterSpacing().isUndefined(),
+ @"text-letter-spacing should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.textLetterSpacing;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.textLetterSpacing = styleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getTextLetterSpacing(), propertyValue,
+ @"Setting textLetterSpacing to a constant value should update text-letter-spacing.");
+ XCTAssertEqualObjects(layer.textLetterSpacing, styleValue,
+ @"textLetterSpacing should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.textLetterSpacing = styleValue;
+ propertyValue = { mbgl::style::Function<float> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getTextLetterSpacing(), propertyValue,
+ @"Setting textLetterSpacing to a function should update text-letter-spacing.");
+ XCTAssertEqualObjects(layer.textLetterSpacing, styleValue,
+ @"textLetterSpacing should round-trip functions.");
+
+ layer.textLetterSpacing = nil;
+ XCTAssertTrue(rawLayer->getTextLetterSpacing().isUndefined(),
+ @"Unsetting textLetterSpacing should return text-letter-spacing to the default value.");
+ XCTAssertEqualObjects(layer.textLetterSpacing, defaultStyleValue,
+ @"textLetterSpacing should return the default value after being unset.");
+ }
+
+ // text-line-height
+ {
+ XCTAssertTrue(rawLayer->getTextLineHeight().isUndefined(),
+ @"text-line-height should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.textLineHeight;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.textLineHeight = styleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getTextLineHeight(), propertyValue,
+ @"Setting textLineHeight to a constant value should update text-line-height.");
+ XCTAssertEqualObjects(layer.textLineHeight, styleValue,
+ @"textLineHeight should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.textLineHeight = styleValue;
+ propertyValue = { mbgl::style::Function<float> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getTextLineHeight(), propertyValue,
+ @"Setting textLineHeight to a function should update text-line-height.");
+ XCTAssertEqualObjects(layer.textLineHeight, styleValue,
+ @"textLineHeight should round-trip functions.");
+
+ layer.textLineHeight = nil;
+ XCTAssertTrue(rawLayer->getTextLineHeight().isUndefined(),
+ @"Unsetting textLineHeight should return text-line-height to the default value.");
+ XCTAssertEqualObjects(layer.textLineHeight, defaultStyleValue,
+ @"textLineHeight should return the default value after being unset.");
+ }
+
+ // text-offset
+ {
+ XCTAssertTrue(rawLayer->getTextOffset().isUndefined(),
+ @"text-offset should be unset initially.");
+ MGLStyleValue<NSValue *> *defaultStyleValue = layer.textOffset;
+
+ MGLStyleValue<NSValue *> *styleValue = [MGLStyleValue<NSValue *> valueWithRawValue:
+#if TARGET_OS_IPHONE
+ [NSValue valueWithCGVector:CGVectorMake(1, 1)]
+#else
+ [NSValue valueWithMGLVector:CGVectorMake(1, -1)]
+#endif
+ ];
+ layer.textOffset = styleValue;
+ mbgl::style::PropertyValue<std::array<float, 2>> propertyValue = { { 1, 1 } };
+ XCTAssertEqual(rawLayer->getTextOffset(), propertyValue,
+ @"Setting textOffset to a constant value should update text-offset.");
+ XCTAssertEqualObjects(layer.textOffset, styleValue,
+ @"textOffset should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSValue *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.textOffset = styleValue;
+ propertyValue = { mbgl::style::Function<std::array<float, 2>> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getTextOffset(), propertyValue,
+ @"Setting textOffset to a function should update text-offset.");
+ XCTAssertEqualObjects(layer.textOffset, styleValue,
+ @"textOffset should round-trip functions.");
+
+ layer.textOffset = nil;
+ XCTAssertTrue(rawLayer->getTextOffset().isUndefined(),
+ @"Unsetting textOffset should return text-offset to the default value.");
+ XCTAssertEqualObjects(layer.textOffset, defaultStyleValue,
+ @"textOffset should return the default value after being unset.");
+ }
+
+ // text-optional
+ {
+ XCTAssertTrue(rawLayer->getTextOptional().isUndefined(),
+ @"text-optional should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.textOptional;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@YES];
+ layer.textOptional = styleValue;
+ mbgl::style::PropertyValue<bool> propertyValue = { true };
+ XCTAssertEqual(rawLayer->getTextOptional(), propertyValue,
+ @"Setting textOptional to a constant value should update text-optional.");
+ XCTAssertEqualObjects(layer.textOptional, styleValue,
+ @"textOptional should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.textOptional = styleValue;
+ propertyValue = { mbgl::style::Function<bool> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getTextOptional(), propertyValue,
+ @"Setting textOptional to a function should update text-optional.");
+ XCTAssertEqualObjects(layer.textOptional, styleValue,
+ @"textOptional should round-trip functions.");
+
+ layer.textOptional = nil;
+ XCTAssertTrue(rawLayer->getTextOptional().isUndefined(),
+ @"Unsetting textOptional should return text-optional to the default value.");
+ XCTAssertEqualObjects(layer.textOptional, defaultStyleValue,
+ @"textOptional should return the default value after being unset.");
+ }
+
+ // text-padding
+ {
+ XCTAssertTrue(rawLayer->getTextPadding().isUndefined(),
+ @"text-padding should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.textPadding;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.textPadding = styleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getTextPadding(), propertyValue,
+ @"Setting textPadding to a constant value should update text-padding.");
+ XCTAssertEqualObjects(layer.textPadding, styleValue,
+ @"textPadding should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.textPadding = styleValue;
+ propertyValue = { mbgl::style::Function<float> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getTextPadding(), propertyValue,
+ @"Setting textPadding to a function should update text-padding.");
+ XCTAssertEqualObjects(layer.textPadding, styleValue,
+ @"textPadding should round-trip functions.");
+
+ layer.textPadding = nil;
+ XCTAssertTrue(rawLayer->getTextPadding().isUndefined(),
+ @"Unsetting textPadding should return text-padding to the default value.");
+ XCTAssertEqualObjects(layer.textPadding, defaultStyleValue,
+ @"textPadding should return the default value after being unset.");
+ }
+
+ // text-pitch-alignment
+ {
+ XCTAssertTrue(rawLayer->getTextPitchAlignment().isUndefined(),
+ @"text-pitch-alignment should be unset initially.");
+ MGLStyleValue<NSValue *> *defaultStyleValue = layer.textPitchAlignment;
+
+ MGLStyleValue<NSValue *> *styleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLTextPitchAlignment:MGLTextPitchAlignmentAuto]];
+ layer.textPitchAlignment = styleValue;
+ mbgl::style::PropertyValue<mbgl::style::AlignmentType> propertyValue = { mbgl::style::AlignmentType::Auto };
+ XCTAssertEqual(rawLayer->getTextPitchAlignment(), propertyValue,
+ @"Setting textPitchAlignment to a constant value should update text-pitch-alignment.");
+ XCTAssertEqualObjects(layer.textPitchAlignment, styleValue,
+ @"textPitchAlignment should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSValue *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.textPitchAlignment = styleValue;
+ propertyValue = { mbgl::style::Function<mbgl::style::AlignmentType> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getTextPitchAlignment(), propertyValue,
+ @"Setting textPitchAlignment to a function should update text-pitch-alignment.");
+ XCTAssertEqualObjects(layer.textPitchAlignment, styleValue,
+ @"textPitchAlignment should round-trip functions.");
+
+ layer.textPitchAlignment = nil;
+ XCTAssertTrue(rawLayer->getTextPitchAlignment().isUndefined(),
+ @"Unsetting textPitchAlignment should return text-pitch-alignment to the default value.");
+ XCTAssertEqualObjects(layer.textPitchAlignment, defaultStyleValue,
+ @"textPitchAlignment should return the default value after being unset.");
+ }
+
+ // text-rotate
+ {
+ XCTAssertTrue(rawLayer->getTextRotate().isUndefined(),
+ @"text-rotate should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.textRotation;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.textRotation = styleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getTextRotate(), propertyValue,
+ @"Setting textRotation to a constant value should update text-rotate.");
+ XCTAssertEqualObjects(layer.textRotation, styleValue,
+ @"textRotation should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.textRotation = styleValue;
+ propertyValue = { mbgl::style::Function<float> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getTextRotate(), propertyValue,
+ @"Setting textRotation to a function should update text-rotate.");
+ XCTAssertEqualObjects(layer.textRotation, styleValue,
+ @"textRotation should round-trip functions.");
+
+ layer.textRotation = nil;
+ XCTAssertTrue(rawLayer->getTextRotate().isUndefined(),
+ @"Unsetting textRotation should return text-rotate to the default value.");
+ XCTAssertEqualObjects(layer.textRotation, defaultStyleValue,
+ @"textRotation should return the default value after being unset.");
+ }
+
+ // text-rotation-alignment
+ {
+ XCTAssertTrue(rawLayer->getTextRotationAlignment().isUndefined(),
+ @"text-rotation-alignment should be unset initially.");
+ MGLStyleValue<NSValue *> *defaultStyleValue = layer.textRotationAlignment;
+
+ MGLStyleValue<NSValue *> *styleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLTextRotationAlignment:MGLTextRotationAlignmentAuto]];
+ layer.textRotationAlignment = styleValue;
+ mbgl::style::PropertyValue<mbgl::style::AlignmentType> propertyValue = { mbgl::style::AlignmentType::Auto };
+ XCTAssertEqual(rawLayer->getTextRotationAlignment(), propertyValue,
+ @"Setting textRotationAlignment to a constant value should update text-rotation-alignment.");
+ XCTAssertEqualObjects(layer.textRotationAlignment, styleValue,
+ @"textRotationAlignment should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSValue *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.textRotationAlignment = styleValue;
+ propertyValue = { mbgl::style::Function<mbgl::style::AlignmentType> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getTextRotationAlignment(), propertyValue,
+ @"Setting textRotationAlignment to a function should update text-rotation-alignment.");
+ XCTAssertEqualObjects(layer.textRotationAlignment, styleValue,
+ @"textRotationAlignment should round-trip functions.");
+
+ layer.textRotationAlignment = nil;
+ XCTAssertTrue(rawLayer->getTextRotationAlignment().isUndefined(),
+ @"Unsetting textRotationAlignment should return text-rotation-alignment to the default value.");
+ XCTAssertEqualObjects(layer.textRotationAlignment, defaultStyleValue,
+ @"textRotationAlignment should return the default value after being unset.");
+ }
+
+ // text-transform
+ {
+ XCTAssertTrue(rawLayer->getTextTransform().isUndefined(),
+ @"text-transform should be unset initially.");
+ MGLStyleValue<NSValue *> *defaultStyleValue = layer.textTransform;
+
+ MGLStyleValue<NSValue *> *styleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLTextTransform:MGLTextTransformLowercase]];
+ layer.textTransform = styleValue;
+ mbgl::style::PropertyValue<mbgl::style::TextTransformType> propertyValue = { mbgl::style::TextTransformType::Lowercase };
+ XCTAssertEqual(rawLayer->getTextTransform(), propertyValue,
+ @"Setting textTransform to a constant value should update text-transform.");
+ XCTAssertEqualObjects(layer.textTransform, styleValue,
+ @"textTransform should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSValue *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.textTransform = styleValue;
+ propertyValue = { mbgl::style::Function<mbgl::style::TextTransformType> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getTextTransform(), propertyValue,
+ @"Setting textTransform to a function should update text-transform.");
+ XCTAssertEqualObjects(layer.textTransform, styleValue,
+ @"textTransform should round-trip functions.");
+
+ layer.textTransform = nil;
+ XCTAssertTrue(rawLayer->getTextTransform().isUndefined(),
+ @"Unsetting textTransform should return text-transform to the default value.");
+ XCTAssertEqualObjects(layer.textTransform, defaultStyleValue,
+ @"textTransform should return the default value after being unset.");
+ }
+
+ // icon-color
+ {
+ XCTAssertTrue(rawLayer->getIconColor().isUndefined(),
+ @"icon-color should be unset initially.");
+ MGLStyleValue<MGLColor *> *defaultStyleValue = layer.iconColor;
+
+ MGLStyleValue<MGLColor *> *styleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]];
+ layer.iconColor = styleValue;
+ mbgl::style::PropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } };
+ XCTAssertEqual(rawLayer->getIconColor(), propertyValue,
+ @"Setting iconColor to a constant value should update icon-color.");
+ XCTAssertEqualObjects(layer.iconColor, styleValue,
+ @"iconColor should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<MGLColor *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.iconColor = styleValue;
+ propertyValue = { mbgl::style::Function<mbgl::Color> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getIconColor(), propertyValue,
+ @"Setting iconColor to a function should update icon-color.");
+ XCTAssertEqualObjects(layer.iconColor, styleValue,
+ @"iconColor should round-trip functions.");
+
+ layer.iconColor = nil;
+ XCTAssertTrue(rawLayer->getIconColor().isUndefined(),
+ @"Unsetting iconColor should return icon-color to the default value.");
+ XCTAssertEqualObjects(layer.iconColor, defaultStyleValue,
+ @"iconColor should return the default value after being unset.");
+ }
+
+ // icon-halo-blur
+ {
+ XCTAssertTrue(rawLayer->getIconHaloBlur().isUndefined(),
+ @"icon-halo-blur should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.iconHaloBlur;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.iconHaloBlur = styleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getIconHaloBlur(), propertyValue,
+ @"Setting iconHaloBlur to a constant value should update icon-halo-blur.");
+ XCTAssertEqualObjects(layer.iconHaloBlur, styleValue,
+ @"iconHaloBlur should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.iconHaloBlur = styleValue;
+ propertyValue = { mbgl::style::Function<float> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getIconHaloBlur(), propertyValue,
+ @"Setting iconHaloBlur to a function should update icon-halo-blur.");
+ XCTAssertEqualObjects(layer.iconHaloBlur, styleValue,
+ @"iconHaloBlur should round-trip functions.");
+
+ layer.iconHaloBlur = nil;
+ XCTAssertTrue(rawLayer->getIconHaloBlur().isUndefined(),
+ @"Unsetting iconHaloBlur should return icon-halo-blur to the default value.");
+ XCTAssertEqualObjects(layer.iconHaloBlur, defaultStyleValue,
+ @"iconHaloBlur should return the default value after being unset.");
+ }
+
+ // icon-halo-color
+ {
+ XCTAssertTrue(rawLayer->getIconHaloColor().isUndefined(),
+ @"icon-halo-color should be unset initially.");
+ MGLStyleValue<MGLColor *> *defaultStyleValue = layer.iconHaloColor;
+
+ MGLStyleValue<MGLColor *> *styleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]];
+ layer.iconHaloColor = styleValue;
+ mbgl::style::PropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } };
+ XCTAssertEqual(rawLayer->getIconHaloColor(), propertyValue,
+ @"Setting iconHaloColor to a constant value should update icon-halo-color.");
+ XCTAssertEqualObjects(layer.iconHaloColor, styleValue,
+ @"iconHaloColor should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<MGLColor *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.iconHaloColor = styleValue;
+ propertyValue = { mbgl::style::Function<mbgl::Color> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getIconHaloColor(), propertyValue,
+ @"Setting iconHaloColor to a function should update icon-halo-color.");
+ XCTAssertEqualObjects(layer.iconHaloColor, styleValue,
+ @"iconHaloColor should round-trip functions.");
+
+ layer.iconHaloColor = nil;
+ XCTAssertTrue(rawLayer->getIconHaloColor().isUndefined(),
+ @"Unsetting iconHaloColor should return icon-halo-color to the default value.");
+ XCTAssertEqualObjects(layer.iconHaloColor, defaultStyleValue,
+ @"iconHaloColor should return the default value after being unset.");
+ }
+
+ // icon-halo-width
+ {
+ XCTAssertTrue(rawLayer->getIconHaloWidth().isUndefined(),
+ @"icon-halo-width should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.iconHaloWidth;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.iconHaloWidth = styleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getIconHaloWidth(), propertyValue,
+ @"Setting iconHaloWidth to a constant value should update icon-halo-width.");
+ XCTAssertEqualObjects(layer.iconHaloWidth, styleValue,
+ @"iconHaloWidth should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.iconHaloWidth = styleValue;
+ propertyValue = { mbgl::style::Function<float> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getIconHaloWidth(), propertyValue,
+ @"Setting iconHaloWidth to a function should update icon-halo-width.");
+ XCTAssertEqualObjects(layer.iconHaloWidth, styleValue,
+ @"iconHaloWidth should round-trip functions.");
+
+ layer.iconHaloWidth = nil;
+ XCTAssertTrue(rawLayer->getIconHaloWidth().isUndefined(),
+ @"Unsetting iconHaloWidth should return icon-halo-width to the default value.");
+ XCTAssertEqualObjects(layer.iconHaloWidth, defaultStyleValue,
+ @"iconHaloWidth should return the default value after being unset.");
+ }
+
+ // icon-opacity
+ {
+ XCTAssertTrue(rawLayer->getIconOpacity().isUndefined(),
+ @"icon-opacity should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.iconOpacity;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.iconOpacity = styleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getIconOpacity(), propertyValue,
+ @"Setting iconOpacity to a constant value should update icon-opacity.");
+ XCTAssertEqualObjects(layer.iconOpacity, styleValue,
+ @"iconOpacity should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.iconOpacity = styleValue;
+ propertyValue = { mbgl::style::Function<float> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getIconOpacity(), propertyValue,
+ @"Setting iconOpacity to a function should update icon-opacity.");
+ XCTAssertEqualObjects(layer.iconOpacity, styleValue,
+ @"iconOpacity should round-trip functions.");
+
+ layer.iconOpacity = nil;
+ XCTAssertTrue(rawLayer->getIconOpacity().isUndefined(),
+ @"Unsetting iconOpacity should return icon-opacity to the default value.");
+ XCTAssertEqualObjects(layer.iconOpacity, defaultStyleValue,
+ @"iconOpacity should return the default value after being unset.");
+ }
+
+ // icon-translate
+ {
+ XCTAssertTrue(rawLayer->getIconTranslate().isUndefined(),
+ @"icon-translate should be unset initially.");
+ MGLStyleValue<NSValue *> *defaultStyleValue = layer.iconTranslation;
+
+ MGLStyleValue<NSValue *> *styleValue = [MGLStyleValue<NSValue *> valueWithRawValue:
+#if TARGET_OS_IPHONE
+ [NSValue valueWithCGVector:CGVectorMake(1, 1)]
+#else
+ [NSValue valueWithMGLVector:CGVectorMake(1, -1)]
+#endif
+ ];
+ layer.iconTranslation = styleValue;
+ mbgl::style::PropertyValue<std::array<float, 2>> propertyValue = { { 1, 1 } };
+ XCTAssertEqual(rawLayer->getIconTranslate(), propertyValue,
+ @"Setting iconTranslation to a constant value should update icon-translate.");
+ XCTAssertEqualObjects(layer.iconTranslation, styleValue,
+ @"iconTranslation should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSValue *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.iconTranslation = styleValue;
+ propertyValue = { mbgl::style::Function<std::array<float, 2>> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getIconTranslate(), propertyValue,
+ @"Setting iconTranslation to a function should update icon-translate.");
+ XCTAssertEqualObjects(layer.iconTranslation, styleValue,
+ @"iconTranslation should round-trip functions.");
+
+ layer.iconTranslation = nil;
+ XCTAssertTrue(rawLayer->getIconTranslate().isUndefined(),
+ @"Unsetting iconTranslation should return icon-translate to the default value.");
+ XCTAssertEqualObjects(layer.iconTranslation, defaultStyleValue,
+ @"iconTranslation should return the default value after being unset.");
+ }
+
+ // icon-translate-anchor
+ {
+ XCTAssertTrue(rawLayer->getIconTranslateAnchor().isUndefined(),
+ @"icon-translate-anchor should be unset initially.");
+ MGLStyleValue<NSValue *> *defaultStyleValue = layer.iconTranslationAnchor;
+
+ MGLStyleValue<NSValue *> *styleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLIconTranslationAnchor:MGLIconTranslationAnchorViewport]];
+ layer.iconTranslationAnchor = styleValue;
+ mbgl::style::PropertyValue<mbgl::style::TranslateAnchorType> propertyValue = { mbgl::style::TranslateAnchorType::Viewport };
+ XCTAssertEqual(rawLayer->getIconTranslateAnchor(), propertyValue,
+ @"Setting iconTranslationAnchor to a constant value should update icon-translate-anchor.");
+ XCTAssertEqualObjects(layer.iconTranslationAnchor, styleValue,
+ @"iconTranslationAnchor should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSValue *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.iconTranslationAnchor = styleValue;
+ propertyValue = { mbgl::style::Function<mbgl::style::TranslateAnchorType> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getIconTranslateAnchor(), propertyValue,
+ @"Setting iconTranslationAnchor to a function should update icon-translate-anchor.");
+ XCTAssertEqualObjects(layer.iconTranslationAnchor, styleValue,
+ @"iconTranslationAnchor should round-trip functions.");
+
+ layer.iconTranslationAnchor = nil;
+ XCTAssertTrue(rawLayer->getIconTranslateAnchor().isUndefined(),
+ @"Unsetting iconTranslationAnchor should return icon-translate-anchor to the default value.");
+ XCTAssertEqualObjects(layer.iconTranslationAnchor, defaultStyleValue,
+ @"iconTranslationAnchor should return the default value after being unset.");
+ }
+
+ // text-color
+ {
+ XCTAssertTrue(rawLayer->getTextColor().isUndefined(),
+ @"text-color should be unset initially.");
+ MGLStyleValue<MGLColor *> *defaultStyleValue = layer.textColor;
+
+ MGLStyleValue<MGLColor *> *styleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]];
+ layer.textColor = styleValue;
+ mbgl::style::PropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } };
+ XCTAssertEqual(rawLayer->getTextColor(), propertyValue,
+ @"Setting textColor to a constant value should update text-color.");
+ XCTAssertEqualObjects(layer.textColor, styleValue,
+ @"textColor should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<MGLColor *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.textColor = styleValue;
+ propertyValue = { mbgl::style::Function<mbgl::Color> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getTextColor(), propertyValue,
+ @"Setting textColor to a function should update text-color.");
+ XCTAssertEqualObjects(layer.textColor, styleValue,
+ @"textColor should round-trip functions.");
+
+ layer.textColor = nil;
+ XCTAssertTrue(rawLayer->getTextColor().isUndefined(),
+ @"Unsetting textColor should return text-color to the default value.");
+ XCTAssertEqualObjects(layer.textColor, defaultStyleValue,
+ @"textColor should return the default value after being unset.");
+ }
+
+ // text-halo-blur
+ {
+ XCTAssertTrue(rawLayer->getTextHaloBlur().isUndefined(),
+ @"text-halo-blur should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.textHaloBlur;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.textHaloBlur = styleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getTextHaloBlur(), propertyValue,
+ @"Setting textHaloBlur to a constant value should update text-halo-blur.");
+ XCTAssertEqualObjects(layer.textHaloBlur, styleValue,
+ @"textHaloBlur should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.textHaloBlur = styleValue;
+ propertyValue = { mbgl::style::Function<float> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getTextHaloBlur(), propertyValue,
+ @"Setting textHaloBlur to a function should update text-halo-blur.");
+ XCTAssertEqualObjects(layer.textHaloBlur, styleValue,
+ @"textHaloBlur should round-trip functions.");
+
+ layer.textHaloBlur = nil;
+ XCTAssertTrue(rawLayer->getTextHaloBlur().isUndefined(),
+ @"Unsetting textHaloBlur should return text-halo-blur to the default value.");
+ XCTAssertEqualObjects(layer.textHaloBlur, defaultStyleValue,
+ @"textHaloBlur should return the default value after being unset.");
+ }
+
+ // text-halo-color
+ {
+ XCTAssertTrue(rawLayer->getTextHaloColor().isUndefined(),
+ @"text-halo-color should be unset initially.");
+ MGLStyleValue<MGLColor *> *defaultStyleValue = layer.textHaloColor;
+
+ MGLStyleValue<MGLColor *> *styleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]];
+ layer.textHaloColor = styleValue;
+ mbgl::style::PropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } };
+ XCTAssertEqual(rawLayer->getTextHaloColor(), propertyValue,
+ @"Setting textHaloColor to a constant value should update text-halo-color.");
+ XCTAssertEqualObjects(layer.textHaloColor, styleValue,
+ @"textHaloColor should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<MGLColor *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.textHaloColor = styleValue;
+ propertyValue = { mbgl::style::Function<mbgl::Color> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getTextHaloColor(), propertyValue,
+ @"Setting textHaloColor to a function should update text-halo-color.");
+ XCTAssertEqualObjects(layer.textHaloColor, styleValue,
+ @"textHaloColor should round-trip functions.");
+
+ layer.textHaloColor = nil;
+ XCTAssertTrue(rawLayer->getTextHaloColor().isUndefined(),
+ @"Unsetting textHaloColor should return text-halo-color to the default value.");
+ XCTAssertEqualObjects(layer.textHaloColor, defaultStyleValue,
+ @"textHaloColor should return the default value after being unset.");
+ }
+
+ // text-halo-width
+ {
+ XCTAssertTrue(rawLayer->getTextHaloWidth().isUndefined(),
+ @"text-halo-width should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.textHaloWidth;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.textHaloWidth = styleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getTextHaloWidth(), propertyValue,
+ @"Setting textHaloWidth to a constant value should update text-halo-width.");
+ XCTAssertEqualObjects(layer.textHaloWidth, styleValue,
+ @"textHaloWidth should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.textHaloWidth = styleValue;
+ propertyValue = { mbgl::style::Function<float> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getTextHaloWidth(), propertyValue,
+ @"Setting textHaloWidth to a function should update text-halo-width.");
+ XCTAssertEqualObjects(layer.textHaloWidth, styleValue,
+ @"textHaloWidth should round-trip functions.");
+
+ layer.textHaloWidth = nil;
+ XCTAssertTrue(rawLayer->getTextHaloWidth().isUndefined(),
+ @"Unsetting textHaloWidth should return text-halo-width to the default value.");
+ XCTAssertEqualObjects(layer.textHaloWidth, defaultStyleValue,
+ @"textHaloWidth should return the default value after being unset.");
+ }
+
+ // text-opacity
+ {
+ XCTAssertTrue(rawLayer->getTextOpacity().isUndefined(),
+ @"text-opacity should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.textOpacity;
+
+ MGLStyleValue<NSNumber *> *styleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.textOpacity = styleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getTextOpacity(), propertyValue,
+ @"Setting textOpacity to a constant value should update text-opacity.");
+ XCTAssertEqualObjects(layer.textOpacity, styleValue,
+ @"textOpacity should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSNumber *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.textOpacity = styleValue;
+ propertyValue = { mbgl::style::Function<float> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getTextOpacity(), propertyValue,
+ @"Setting textOpacity to a function should update text-opacity.");
+ XCTAssertEqualObjects(layer.textOpacity, styleValue,
+ @"textOpacity should round-trip functions.");
+
+ layer.textOpacity = nil;
+ XCTAssertTrue(rawLayer->getTextOpacity().isUndefined(),
+ @"Unsetting textOpacity should return text-opacity to the default value.");
+ XCTAssertEqualObjects(layer.textOpacity, defaultStyleValue,
+ @"textOpacity should return the default value after being unset.");
+ }
+
+ // text-translate
+ {
+ XCTAssertTrue(rawLayer->getTextTranslate().isUndefined(),
+ @"text-translate should be unset initially.");
+ MGLStyleValue<NSValue *> *defaultStyleValue = layer.textTranslation;
+
+ MGLStyleValue<NSValue *> *styleValue = [MGLStyleValue<NSValue *> valueWithRawValue:
+#if TARGET_OS_IPHONE
+ [NSValue valueWithCGVector:CGVectorMake(1, 1)]
+#else
+ [NSValue valueWithMGLVector:CGVectorMake(1, -1)]
+#endif
+ ];
+ layer.textTranslation = styleValue;
+ mbgl::style::PropertyValue<std::array<float, 2>> propertyValue = { { 1, 1 } };
+ XCTAssertEqual(rawLayer->getTextTranslate(), propertyValue,
+ @"Setting textTranslation to a constant value should update text-translate.");
+ XCTAssertEqualObjects(layer.textTranslation, styleValue,
+ @"textTranslation should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSValue *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.textTranslation = styleValue;
+ propertyValue = { mbgl::style::Function<std::array<float, 2>> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getTextTranslate(), propertyValue,
+ @"Setting textTranslation to a function should update text-translate.");
+ XCTAssertEqualObjects(layer.textTranslation, styleValue,
+ @"textTranslation should round-trip functions.");
+
+ layer.textTranslation = nil;
+ XCTAssertTrue(rawLayer->getTextTranslate().isUndefined(),
+ @"Unsetting textTranslation should return text-translate to the default value.");
+ XCTAssertEqualObjects(layer.textTranslation, defaultStyleValue,
+ @"textTranslation should return the default value after being unset.");
+ }
+
+ // text-translate-anchor
+ {
+ XCTAssertTrue(rawLayer->getTextTranslateAnchor().isUndefined(),
+ @"text-translate-anchor should be unset initially.");
+ MGLStyleValue<NSValue *> *defaultStyleValue = layer.textTranslationAnchor;
+
+ MGLStyleValue<NSValue *> *styleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLTextTranslationAnchor:MGLTextTranslationAnchorViewport]];
+ layer.textTranslationAnchor = styleValue;
+ mbgl::style::PropertyValue<mbgl::style::TranslateAnchorType> propertyValue = { mbgl::style::TranslateAnchorType::Viewport };
+ XCTAssertEqual(rawLayer->getTextTranslateAnchor(), propertyValue,
+ @"Setting textTranslationAnchor to a constant value should update text-translate-anchor.");
+ XCTAssertEqualObjects(layer.textTranslationAnchor, styleValue,
+ @"textTranslationAnchor should round-trip constant values.");
+
+ styleValue = [MGLStyleValue<NSValue *> valueWithStops:@{
+ @18: styleValue,
+ }];
+ layer.textTranslationAnchor = styleValue;
+ propertyValue = { mbgl::style::Function<mbgl::style::TranslateAnchorType> {
+ {{ 18, propertyValue.asConstant() }},
+ 1,
+ }};
+ XCTAssertEqual(rawLayer->getTextTranslateAnchor(), propertyValue,
+ @"Setting textTranslationAnchor to a function should update text-translate-anchor.");
+ XCTAssertEqualObjects(layer.textTranslationAnchor, styleValue,
+ @"textTranslationAnchor should round-trip functions.");
+
+ layer.textTranslationAnchor = nil;
+ XCTAssertTrue(rawLayer->getTextTranslateAnchor().isUndefined(),
+ @"Unsetting textTranslationAnchor should return text-translate-anchor to the default value.");
+ XCTAssertEqualObjects(layer.textTranslationAnchor, defaultStyleValue,
+ @"textTranslationAnchor should return the default value after being unset.");
+ }
+}
+
+- (void)testPropertyNames {
+ [self testPropertyName:@"icon-allows-overlap" isBoolean:YES];
+ [self testPropertyName:@"icon-ignores-placement" isBoolean:YES];
+ [self testPropertyName:@"icon-image-name" isBoolean:NO];
+ [self testPropertyName:@"icon-offset" isBoolean:NO];
+ [self testPropertyName:@"is-icon-optional" isBoolean:YES];
+ [self testPropertyName:@"icon-padding" isBoolean:NO];
+ [self testPropertyName:@"icon-rotation" isBoolean:NO];
+ [self testPropertyName:@"icon-rotation-alignment" isBoolean:NO];
+ [self testPropertyName:@"icon-scale" isBoolean:NO];
+ [self testPropertyName:@"icon-text-fit" isBoolean:NO];
+ [self testPropertyName:@"icon-text-fit-padding" isBoolean:NO];
+ [self testPropertyName:@"keeps-icon-upright" isBoolean:YES];
+ [self testPropertyName:@"keeps-text-upright" isBoolean:YES];
+ [self testPropertyName:@"maximum-text-angle" isBoolean:NO];
+ [self testPropertyName:@"maximum-text-width" isBoolean:NO];
+ [self testPropertyName:@"symbol-avoids-edges" isBoolean:YES];
+ [self testPropertyName:@"symbol-placement" isBoolean:NO];
+ [self testPropertyName:@"symbol-spacing" isBoolean:NO];
+ [self testPropertyName:@"text" isBoolean:NO];
+ [self testPropertyName:@"text-allows-overlap" isBoolean:YES];
+ [self testPropertyName:@"text-anchor" isBoolean:NO];
+ [self testPropertyName:@"text-font-names" isBoolean:NO];
+ [self testPropertyName:@"text-font-size" isBoolean:NO];
+ [self testPropertyName:@"text-ignores-placement" isBoolean:YES];
+ [self testPropertyName:@"text-justification" isBoolean:NO];
+ [self testPropertyName:@"text-letter-spacing" isBoolean:NO];
+ [self testPropertyName:@"text-line-height" isBoolean:NO];
+ [self testPropertyName:@"text-offset" isBoolean:NO];
+ [self testPropertyName:@"is-text-optional" isBoolean:YES];
+ [self testPropertyName:@"text-padding" isBoolean:NO];
+ [self testPropertyName:@"text-pitch-alignment" isBoolean:NO];
+ [self testPropertyName:@"text-rotation" isBoolean:NO];
+ [self testPropertyName:@"text-rotation-alignment" isBoolean:NO];
+ [self testPropertyName:@"text-transform" isBoolean:NO];
+ [self testPropertyName:@"icon-color" isBoolean:NO];
+ [self testPropertyName:@"icon-halo-blur" isBoolean:NO];
+ [self testPropertyName:@"icon-halo-color" isBoolean:NO];
+ [self testPropertyName:@"icon-halo-width" isBoolean:NO];
+ [self testPropertyName:@"icon-opacity" isBoolean:NO];
+ [self testPropertyName:@"icon-translation" isBoolean:NO];
+ [self testPropertyName:@"icon-translation-anchor" isBoolean:NO];
+ [self testPropertyName:@"text-color" isBoolean:NO];
+ [self testPropertyName:@"text-halo-blur" isBoolean:NO];
+ [self testPropertyName:@"text-halo-color" isBoolean:NO];
+ [self testPropertyName:@"text-halo-width" isBoolean:NO];
+ [self testPropertyName:@"text-opacity" isBoolean:NO];
+ [self testPropertyName:@"text-translation" isBoolean:NO];
+ [self testPropertyName:@"text-translation-anchor" isBoolean:NO];
+}
+
+- (void)testValueAdditions {
+ XCTAssertEqual([NSValue valueWithMGLIconRotationAlignment:MGLIconRotationAlignmentMap].MGLIconRotationAlignmentValue, MGLIconRotationAlignmentMap);
+ XCTAssertEqual([NSValue valueWithMGLIconRotationAlignment:MGLIconRotationAlignmentViewport].MGLIconRotationAlignmentValue, MGLIconRotationAlignmentViewport);
+ XCTAssertEqual([NSValue valueWithMGLIconRotationAlignment:MGLIconRotationAlignmentAuto].MGLIconRotationAlignmentValue, MGLIconRotationAlignmentAuto);
+ XCTAssertEqual([NSValue valueWithMGLIconTextFit:MGLIconTextFitNone].MGLIconTextFitValue, MGLIconTextFitNone);
+ XCTAssertEqual([NSValue valueWithMGLIconTextFit:MGLIconTextFitWidth].MGLIconTextFitValue, MGLIconTextFitWidth);
+ XCTAssertEqual([NSValue valueWithMGLIconTextFit:MGLIconTextFitHeight].MGLIconTextFitValue, MGLIconTextFitHeight);
+ XCTAssertEqual([NSValue valueWithMGLIconTextFit:MGLIconTextFitBoth].MGLIconTextFitValue, MGLIconTextFitBoth);
+ XCTAssertEqual([NSValue valueWithMGLSymbolPlacement:MGLSymbolPlacementPoint].MGLSymbolPlacementValue, MGLSymbolPlacementPoint);
+ XCTAssertEqual([NSValue valueWithMGLSymbolPlacement:MGLSymbolPlacementLine].MGLSymbolPlacementValue, MGLSymbolPlacementLine);
+ XCTAssertEqual([NSValue valueWithMGLTextAnchor:MGLTextAnchorCenter].MGLTextAnchorValue, MGLTextAnchorCenter);
+ XCTAssertEqual([NSValue valueWithMGLTextAnchor:MGLTextAnchorLeft].MGLTextAnchorValue, MGLTextAnchorLeft);
+ XCTAssertEqual([NSValue valueWithMGLTextAnchor:MGLTextAnchorRight].MGLTextAnchorValue, MGLTextAnchorRight);
+ XCTAssertEqual([NSValue valueWithMGLTextAnchor:MGLTextAnchorTop].MGLTextAnchorValue, MGLTextAnchorTop);
+ XCTAssertEqual([NSValue valueWithMGLTextAnchor:MGLTextAnchorBottom].MGLTextAnchorValue, MGLTextAnchorBottom);
+ XCTAssertEqual([NSValue valueWithMGLTextAnchor:MGLTextAnchorTopLeft].MGLTextAnchorValue, MGLTextAnchorTopLeft);
+ XCTAssertEqual([NSValue valueWithMGLTextAnchor:MGLTextAnchorTopRight].MGLTextAnchorValue, MGLTextAnchorTopRight);
+ XCTAssertEqual([NSValue valueWithMGLTextAnchor:MGLTextAnchorBottomLeft].MGLTextAnchorValue, MGLTextAnchorBottomLeft);
+ XCTAssertEqual([NSValue valueWithMGLTextAnchor:MGLTextAnchorBottomRight].MGLTextAnchorValue, MGLTextAnchorBottomRight);
+ XCTAssertEqual([NSValue valueWithMGLTextJustification:MGLTextJustificationLeft].MGLTextJustificationValue, MGLTextJustificationLeft);
+ XCTAssertEqual([NSValue valueWithMGLTextJustification:MGLTextJustificationCenter].MGLTextJustificationValue, MGLTextJustificationCenter);
+ XCTAssertEqual([NSValue valueWithMGLTextJustification:MGLTextJustificationRight].MGLTextJustificationValue, MGLTextJustificationRight);
+ XCTAssertEqual([NSValue valueWithMGLTextPitchAlignment:MGLTextPitchAlignmentMap].MGLTextPitchAlignmentValue, MGLTextPitchAlignmentMap);
+ XCTAssertEqual([NSValue valueWithMGLTextPitchAlignment:MGLTextPitchAlignmentViewport].MGLTextPitchAlignmentValue, MGLTextPitchAlignmentViewport);
+ XCTAssertEqual([NSValue valueWithMGLTextPitchAlignment:MGLTextPitchAlignmentAuto].MGLTextPitchAlignmentValue, MGLTextPitchAlignmentAuto);
+ XCTAssertEqual([NSValue valueWithMGLTextRotationAlignment:MGLTextRotationAlignmentMap].MGLTextRotationAlignmentValue, MGLTextRotationAlignmentMap);
+ XCTAssertEqual([NSValue valueWithMGLTextRotationAlignment:MGLTextRotationAlignmentViewport].MGLTextRotationAlignmentValue, MGLTextRotationAlignmentViewport);
+ XCTAssertEqual([NSValue valueWithMGLTextRotationAlignment:MGLTextRotationAlignmentAuto].MGLTextRotationAlignmentValue, MGLTextRotationAlignmentAuto);
+ XCTAssertEqual([NSValue valueWithMGLTextTransform:MGLTextTransformNone].MGLTextTransformValue, MGLTextTransformNone);
+ XCTAssertEqual([NSValue valueWithMGLTextTransform:MGLTextTransformUppercase].MGLTextTransformValue, MGLTextTransformUppercase);
+ XCTAssertEqual([NSValue valueWithMGLTextTransform:MGLTextTransformLowercase].MGLTextTransformValue, MGLTextTransformLowercase);
+ XCTAssertEqual([NSValue valueWithMGLIconTranslationAnchor:MGLIconTranslationAnchorMap].MGLIconTranslationAnchorValue, MGLIconTranslationAnchorMap);
+ XCTAssertEqual([NSValue valueWithMGLIconTranslationAnchor:MGLIconTranslationAnchorViewport].MGLIconTranslationAnchorValue, MGLIconTranslationAnchorViewport);
+ XCTAssertEqual([NSValue valueWithMGLTextTranslationAnchor:MGLTextTranslationAnchorMap].MGLTextTranslationAnchorValue, MGLTextTranslationAnchorMap);
+ XCTAssertEqual([NSValue valueWithMGLTextTranslationAnchor:MGLTextTranslationAnchorViewport].MGLTextTranslationAnchorValue, MGLTextTranslationAnchorViewport);
+}
+
+@end
diff --git a/platform/darwin/test/one-liner.json b/platform/darwin/test/one-liner.json
new file mode 100644
index 0000000000..23c546181f
--- /dev/null
+++ b/platform/darwin/test/one-liner.json
@@ -0,0 +1 @@
+{"version":8,"sources":{},"layers":[]} \ No newline at end of file