diff --git a/Makefile b/Makefile index c6ed494f4..c4e123d64 100644 --- a/Makefile +++ b/Makefile @@ -163,6 +163,10 @@ macos-test: $(MACOS_PROJ_PATH) xpackage: $(MACOS_PROJ_PATH) SYMBOLS=$(SYMBOLS) ./platform/macos/scripts/package.sh +.PHONY: xdeploy +xdeploy: + caffeinate -i ./platform/macos/scripts/deploy-packages.sh + .PHONY: xdocument xdocument: OUTPUT=$(OUTPUT) ./platform/macos/scripts/document.sh @@ -276,8 +280,13 @@ idocument: .PHONY: darwin-style-code darwin-style-code: node platform/darwin/scripts/generate-style-code.js + node platform/darwin/scripts/update-examples.js style-code: darwin-style-code +.PHONY: darwin-update-examples +darwin-update-examples: + node platform/darwin/scripts/update-examples.js + .PHONY: check-public-symbols check-public-symbols: node platform/darwin/scripts/check-public-symbols.js macOS diff --git a/include/mbgl/style/filter.hpp b/include/mbgl/style/filter.hpp index dd2b20cd0..e5d8081d8 100644 --- a/include/mbgl/style/filter.hpp +++ b/include/mbgl/style/filter.hpp @@ -6,85 +6,143 @@ #include #include +#include namespace mbgl { namespace style { class Filter; -class NullFilter {}; +class NullFilter { +public: + friend bool operator==(const NullFilter&, const NullFilter&) { + return true; + } +}; class EqualsFilter { public: std::string key; Value value; + + friend bool operator==(const EqualsFilter& lhs, const EqualsFilter& rhs) { + return std::tie(lhs.key, lhs.value) == std::tie(rhs.key, rhs.value); + } }; class NotEqualsFilter { public: std::string key; Value value; + + friend bool operator==(const NotEqualsFilter& lhs, const NotEqualsFilter& rhs) { + return std::tie(lhs.key, lhs.value) == std::tie(rhs.key, rhs.value); + } }; class LessThanFilter { public: std::string key; Value value; + + friend bool operator==(const LessThanFilter& lhs, const LessThanFilter& rhs) { + return std::tie(lhs.key, lhs.value) == std::tie(rhs.key, rhs.value); + } }; class LessThanEqualsFilter { public: std::string key; Value value; + + friend bool operator==(const LessThanEqualsFilter& lhs, const LessThanEqualsFilter& rhs) { + return std::tie(lhs.key, lhs.value) == std::tie(rhs.key, rhs.value); + } }; class GreaterThanFilter { public: std::string key; Value value; + + friend bool operator==(const GreaterThanFilter& lhs, const GreaterThanFilter& rhs) { + return std::tie(lhs.key, lhs.value) == std::tie(rhs.key, rhs.value); + } }; class GreaterThanEqualsFilter { public: std::string key; Value value; + + friend bool operator==(const GreaterThanEqualsFilter& lhs, const GreaterThanEqualsFilter& rhs) { + return std::tie(lhs.key, lhs.value) == std::tie(rhs.key, rhs.value); + } }; class InFilter { public: std::string key; std::vector values; + + friend bool operator==(const InFilter& lhs, const InFilter& rhs) { + return std::tie(lhs.key, lhs.values) == std::tie(rhs.key, rhs.values); + } }; class NotInFilter { public: std::string key; std::vector values; + + friend bool operator==(const NotInFilter& lhs, const NotInFilter& rhs) { + return std::tie(lhs.key, lhs.values) == std::tie(rhs.key, rhs.values); + } }; class AnyFilter { public: std::vector filters; + + friend bool operator==(const AnyFilter& lhs, const AnyFilter& rhs) { + return lhs.filters == rhs.filters; + } }; class AllFilter { public: std::vector filters; + + friend bool operator==(const AllFilter& lhs, const AllFilter& rhs) { + return lhs.filters == rhs.filters; + } }; class NoneFilter { public: std::vector filters; + + friend bool operator==(const NoneFilter& lhs, const NoneFilter& rhs) { + return lhs.filters == rhs.filters; + } }; class HasFilter { public: std::string key; + + friend bool operator==(const HasFilter& lhs, const HasFilter& rhs) { + return lhs.key == rhs.key; + } }; class NotHasFilter { public: std::string key; + + friend bool operator==(const NotHasFilter& lhs, const NotHasFilter& rhs) { + return lhs.key == rhs.key; + } }; using FilterBase = variant< diff --git a/include/mbgl/style/sources/geojson_source.hpp b/include/mbgl/style/sources/geojson_source.hpp index e633e6efb..ede030172 100644 --- a/include/mbgl/style/sources/geojson_source.hpp +++ b/include/mbgl/style/sources/geojson_source.hpp @@ -43,7 +43,7 @@ public: void setURL(const std::string& url); void setGeoJSON(const GeoJSON&); - optional getURL(); + optional getURL() const; // Private implementation diff --git a/include/mbgl/style/sources/raster_source.hpp b/include/mbgl/style/sources/raster_source.hpp index 9bb951992..395f25e51 100644 --- a/include/mbgl/style/sources/raster_source.hpp +++ b/include/mbgl/style/sources/raster_source.hpp @@ -11,9 +11,12 @@ class RasterSource : public Source { public: RasterSource(std::string id, variant urlOrTileset, uint16_t tileSize); + optional getURL() const; + // Private implementation class Impl; + Impl* const impl; }; template <> diff --git a/include/mbgl/style/sources/vector_source.hpp b/include/mbgl/style/sources/vector_source.hpp index e16a6a68a..8626ce160 100644 --- a/include/mbgl/style/sources/vector_source.hpp +++ b/include/mbgl/style/sources/vector_source.hpp @@ -11,9 +11,12 @@ class VectorSource : public Source { public: VectorSource(std::string id, variant urlOrTileset); + optional getURL() const; + // Private implementation class Impl; + Impl* const impl; }; template <> 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 000000000..3d775c8a8 --- /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; +-%> + +# 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` | `[Float]` +Array (`-font`) | `NSArray` | `[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 000000000..57aaa3855 --- /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 9c1f34714..6b4dffc87 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 2e42193e3..e07f181ac 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 7584f4965..bfbd8e7d7 100644 --- a/platform/darwin/docs/theme/templates/nav.mustache +++ b/platform/darwin/docs/theme/templates/nav.mustache @@ -5,7 +5,7 @@ {{section}} - 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 + “Predicate Programming Guide” + 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 d8146f424..da6da0ea7 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 eb1cfb7c4..c4dfd8207 100644 --- a/platform/darwin/src/NSArray+MGLAdditions.h +++ b/platform/darwin/src/NSArray+MGLAdditions.h @@ -1,4 +1,5 @@ #import +#import #import @@ -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 into an NSArray containing dictionary + representations of coordinates with the following structure: + [{"latitude": lat, "longitude": lng}] + */ ++ (NSArray *)mgl_coordinatesFromCoordinates:(std::vector)coords; + +/** + Converts the receiver into a std::vector. + Receiver must conform to the following structure: + [{"latitude": lat, "longitude": lng}] + */ +- (std::vector)mgl_coordinates; + @end diff --git a/platform/darwin/src/NSArray+MGLAdditions.mm b/platform/darwin/src/NSArray+MGLAdditions.mm index b2799c46e..8ec344f58 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)coords { + NSMutableArray *coordinates = [NSMutableArray array]; + for (auto coord : coords) { + [coordinates addObject:@{@"latitude": @(coord.latitude), + @"longitude": @(coord.longitude)}]; + } + return coordinates; +} + +- (std::vector)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 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 000000000..036a99c5a --- /dev/null +++ b/platform/darwin/src/NSCoder+MGLAdditions.h @@ -0,0 +1,16 @@ +#import +#import + +#import + +@interface NSCoder (MGLAdditions) + +- (void)encodeMGLCoordinate:(CLLocationCoordinate2D)coordinate forKey:(NSString *)key; + +- (CLLocationCoordinate2D)decodeMGLCoordinateForKey:(NSString *)key; + +- (void)mgl_encodeLocationCoordinates2D:(std::vector)coordinates forKey:(NSString *)key; + +- (std::vector)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 000000000..4af6c7588 --- /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)coordinates forKey:(NSString *)key { + [self encodeObject:[NSArray mgl_coordinatesFromCoordinates:coordinates] forKey:key]; +} + +- (std::vector)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 19c264aa4..58b37fae0 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::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::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 07114308c..269746719 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 *comparisonPredicates = [self.subpredicates filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"class == %@", [NSComparisonPredicate class]]]; - NSArray *notInPredicates = [comparisonPredicates filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSComparisonPredicate *_Nonnull predicate, NSDictionary * _Nullable bindings) { - return predicate.predicateOperatorType == NSInPredicateOperatorType; - }]]; - - 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(!= nil) to NotHasFilter. + if (subfilter.is()) { + auto hasFilter = subfilter.get(); + return mbgl::style::NotHasFilter { .key = hasFilter.key }; } + + // Convert NOT(== nil) to HasFilter. + if (subfilter.is()) { + auto hasFilter = subfilter.get(); + return mbgl::style::HasFilter { .key = hasFilter.key }; + } + + // Convert NOT(IN) or NOT(CONTAINS) to NotInFilter. + if (subfilter.is()) { + auto inFilter = subfilter.get(); + mbgl::style::NotInFilter notInFilter; + notInFilter.key = inFilter.key; + notInFilter.values = inFilter.values; + return notInFilter; + } + + // Convert NOT(), NOT(AND), NOT(NOT), NOT(==), etc. into NoneFilter. + mbgl::style::NoneFilter noneFilter; + if (subfilter.is()) { + // Flatten NOT(OR). + noneFilter.filters = subfilter.get().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 1023e91a4..aad7fd881 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 6d0fff576..c60d6d78b 100644 --- a/platform/darwin/src/NSExpression+MGLAdditions.h +++ b/platform/darwin/src/NSExpression+MGLAdditions.h @@ -2,10 +2,14 @@ #include +NS_ASSUME_NONNULL_BEGIN + @interface NSExpression (MGLAdditions) -- (mbgl::Value)mgl_filterValue; -- (std::vector)mgl_filterValues; -- (mbgl::FeatureIdentifier)mgl_featureIdentifier; +@property (nonatomic, readonly) mbgl::Value mgl_constantMBGLValue; +@property (nonatomic, readonly) std::vector 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 25a2945cf..b095091b1 100644 --- a/platform/darwin/src/NSExpression+MGLAdditions.mm +++ b/platform/darwin/src/NSExpression+MGLAdditions.mm @@ -2,27 +2,25 @@ @implementation NSExpression (MGLAdditions) -- (std::vector)mgl_filterValues -{ - if ([self.constantValue isKindOfClass:NSArray.class]) { - NSArray *values = self.constantValue; - std::vectorconvertedValues; - for (id value in values) { - convertedValues.push_back([self mgl_convertedValueWithValue:value]); +- (std::vector)mgl_aggregateMBGLValue { + if ([self.constantValue isKindOfClass:[NSArray class]] || [self.constantValue isKindOfClass:[NSSet class]]) { + std::vector 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 { }; + [NSException raise:NSInvalidArgumentException + format:@"Constant value expression must contain an array or set."]; + return {}; } -- (mbgl::Value)mgl_filterValue -{ - return [self mgl_convertedValueWithValue:self.constantValue]; -} - -- (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()) { return mbglValue.get(); - } 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()); - } else if ( strcmp([number objCType], @encode(double)) == 0 || - strcmp([number objCType], @encode(float)) == 0) { - return mbglValue.get(); - } else if ([number compare:@(0)] == NSOrderedDescending || - [number compare:@(0)] == NSOrderedSame) { - return mbglValue.get(); - } else if ([number compare:@(0)] == NSOrderedAscending) { - return mbglValue.get(); - } + } + if (mbglValue.is()) { + return mbglValue.get(); + } + if (mbglValue.is()) { + return mbglValue.get(); + } + if (mbglValue.is()) { + return mbglValue.get(); } return {}; diff --git a/platform/darwin/src/NSPredicate+MGLAdditions.mm b/platform/darwin/src/NSPredicate+MGLAdditions.mm index 64ad277e4..0ac68095f 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 filters) { + NSArray *getPredicates(std::vector 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 values) { + NSExpression *getValues(std::vector 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::AnyFilter filter) { + NSArray *subpredicates = getPredicates(filter.filters); + if (subpredicates.count) { + return [NSCompoundPredicate orPredicateWithSubpredicates:subpredicates]; + } + return [NSPredicate predicateWithValue:NO]; } - NSPredicate* operator()(mbgl::style::AllFilter filter) { - return [NSCompoundPredicate andPredicateWithSubpredicates:getPredicates(filter.filters)]; + 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()) { + lowerKey = leftFilter.get().key; + lowerBound = leftFilter.get().value; + } else if (rightFilter.is()) { + lowerKey = rightFilter.get().key; + lowerBound = rightFilter.get().value; + } + + if (leftFilter.is()) { + upperKey = leftFilter.get().key; + upperBound = leftFilter.get().value; + } else if (rightFilter.is()) { + upperKey = rightFilter.get().key; + upperBound = rightFilter.get().value; + } + + if (!lowerBound.is() && !upperBound.is() + && 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(); } - [NSException raise:@"Not supported" - format:@"Try with NSComparisonPredicate or NSCompoundPredicate instead."]; + if ([self.predicateFormat hasPrefix:@"BLOCKPREDICATE("]) + { + [NSException raise:NSInvalidArgumentException + format:@"Block-based predicates are not supported."]; + } + + [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 45fea2558..246dc084f 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 04a65dc5e..5c32f4b78 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 59afe559d..e66145aec 100644 --- a/platform/darwin/src/NSValue+MGLStyleAttributeAdditions.mm +++ b/platform/darwin/src/NSValue+MGLStyleAttributeAdditions.mm @@ -12,6 +12,11 @@ + (instancetype)mgl_valueWithOffsetArray:(std::array)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(vector.dx), static_cast(vector.dy), diff --git a/platform/darwin/src/NSValue+MGLStyleEnumAttributeAdditions.h b/platform/darwin/src/NSValue+MGLStyleEnumAttributeAdditions.h deleted file mode 100644 index 3387ce818..000000000 --- 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 - -#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 c078ac7d0..000000000 --- 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 - -<% 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 db91408c5..000000000 --- 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 fab278aac..000000000 --- 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 934021d6b..000000000 --- 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 000000000..60a332a5e --- /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 + +@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()); + auto rawLayer = layer.rawLayer->as(); + + // background-color + { + XCTAssertTrue(rawLayer->getBackgroundColor().isUndefined(), + @"background-color should be unset initially."); + MGLStyleValue *defaultStyleValue = layer.backgroundColor; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:[MGLColor redColor]]; + layer.backgroundColor = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.backgroundColor = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.backgroundOpacity; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@0xff]; + layer.backgroundOpacity = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.backgroundOpacity = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.backgroundPattern; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@"Background Pattern"]; + layer.backgroundPattern = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.backgroundPattern = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 ce17d9355..000000000 --- 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 000000000..35e29b31d --- /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 + +@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()); + auto rawLayer = layer.rawLayer->as(); + + // circle-blur + { + XCTAssertTrue(rawLayer->getCircleBlur().isUndefined(), + @"circle-blur should be unset initially."); + MGLStyleValue *defaultStyleValue = layer.circleBlur; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@0xff]; + layer.circleBlur = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.circleBlur = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.circleColor; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:[MGLColor redColor]]; + layer.circleColor = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.circleColor = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.circleOpacity; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@0xff]; + layer.circleOpacity = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.circleOpacity = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.circleRadius; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@0xff]; + layer.circleRadius = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.circleRadius = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.circleScaleAlignment; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:[NSValue valueWithMGLCircleScaleAlignment:MGLCircleScaleAlignmentViewport]]; + layer.circleScaleAlignment = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.circleScaleAlignment = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.circleStrokeColor; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:[MGLColor redColor]]; + layer.circleStrokeColor = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.circleStrokeColor = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.circleStrokeOpacity; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@0xff]; + layer.circleStrokeOpacity = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.circleStrokeOpacity = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.circleStrokeWidth; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@0xff]; + layer.circleStrokeWidth = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.circleStrokeWidth = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.circleTranslation; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue: +#if TARGET_OS_IPHONE + [NSValue valueWithCGVector:CGVectorMake(1, 1)] +#else + [NSValue valueWithMGLVector:CGVectorMake(1, -1)] +#endif + ]; + layer.circleTranslation = styleValue; + mbgl::style::PropertyValue> 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.circleTranslation = styleValue; + propertyValue = { mbgl::style::Function> { + {{ 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 *defaultStyleValue = layer.circleTranslationAnchor; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:[NSValue valueWithMGLCircleTranslationAnchor:MGLCircleTranslationAnchorViewport]]; + layer.circleTranslationAnchor = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.circleTranslationAnchor = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 000000000..b9b299d50 --- /dev/null +++ b/platform/darwin/test/MGLCodingTests.m @@ -0,0 +1,469 @@ +#import +#import + +#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 000000000..d796b4e70 --- /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 971f7d0e5..00b57c15f 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() == true); + mbgl::Value convertedValue = predicate.rightExpression.mgl_constantMBGLValue; + XCTAssertTrue(convertedValue.is()); XCTAssertEqualObjects(@(convertedValue.get().c_str()), @"bar"); } +- (void)testExpressionConversionStringWithUnicode +{ + NSComparisonPredicate *predicate = [self equalityComparisonPredicateWithRightConstantValue:@"🆔🆗🇦🇶"]; + mbgl::Value convertedValue = predicate.rightExpression.mgl_constantMBGLValue; + XCTAssertTrue(convertedValue.is()); + XCTAssertEqual(convertedValue.get(), "🆔🆗🇦🇶"); +} + #pragma mark - Boolean Tests - (void)testExpressionConversionBooleanTrue { NSComparisonPredicate *predicate = [self equalityComparisonPredicateWithRightConstantValue:@YES]; - mbgl::Value convertedValue = predicate.rightExpression.mgl_filterValue; - XCTAssert(convertedValue.is() == true); - XCTAssert(convertedValue.get() == true); + mbgl::Value convertedValue = predicate.rightExpression.mgl_constantMBGLValue; + XCTAssertTrue(convertedValue.is()); + XCTAssertEqual(convertedValue.get(), true); } - (void)testExpressionConversionBooleanFalse { NSComparisonPredicate *predicate = [self equalityComparisonPredicateWithRightConstantValue:@NO]; - mbgl::Value convertedValue = predicate.rightExpression.mgl_filterValue; - XCTAssert(convertedValue.is() == true); - XCTAssert(convertedValue.get() == false); + mbgl::Value convertedValue = predicate.rightExpression.mgl_constantMBGLValue; + XCTAssertTrue(convertedValue.is()); + XCTAssertEqual(convertedValue.get(), 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()); XCTAssertEqual(convertedValue.get(), DBL_MIN); predicate = [self equalityComparisonPredicateWithRightConstantValue:[NSNumber numberWithDouble:DBL_MAX]]; - convertedValue = predicate.rightExpression.mgl_filterValue; + convertedValue = predicate.rightExpression.mgl_constantMBGLValue; XCTAssertTrue(convertedValue.is()); XCTAssertEqual(convertedValue.get(), 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()); XCTAssertEqual(convertedValue.get(), -1); predicate = [self equalityComparisonPredicateWithRightConstantValue:[NSNumber numberWithFloat:1]]; - convertedValue = predicate.rightExpression.mgl_filterValue; + convertedValue = predicate.rightExpression.mgl_constantMBGLValue; XCTAssertTrue(convertedValue.is()); XCTAssertEqual(convertedValue.get(), 1); predicate = [self equalityComparisonPredicateWithRightConstantValue:[NSNumber numberWithFloat:-23.232342]]; - convertedValue = predicate.rightExpression.mgl_filterValue; + convertedValue = predicate.rightExpression.mgl_constantMBGLValue; XCTAssertTrue(convertedValue.is()); - XCTAssertLessThan(-23.24, convertedValue.get()); - XCTAssertGreaterThan(-23.23, convertedValue.get()); + XCTAssertEqualWithAccuracy(convertedValue.get(), -23.232342, 0.000001); predicate = [self equalityComparisonPredicateWithRightConstantValue:[NSNumber numberWithFloat:23.232342]]; - convertedValue = predicate.rightExpression.mgl_filterValue; + convertedValue = predicate.rightExpression.mgl_constantMBGLValue; XCTAssertTrue(convertedValue.is()); - XCTAssertLessThan(23.23, convertedValue.get()); - XCTAssertGreaterThan(23.24, convertedValue.get()); + XCTAssertEqualWithAccuracy(convertedValue.get(), 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()); XCTAssertEqual(convertedValue.get(), 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()); XCTAssertEqual(convertedValue.get(), 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()); XCTAssertEqual(convertedValue.get(), 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()); XCTAssertEqual(convertedValue.get(), max.unsignedLongLongValue); } } +#pragma mark - Null Tests + +- (void)testExpressionConversionNull +{ + NSComparisonPredicate *predicate = [self equalityComparisonPredicateWithRightConstantValue:[NSNull null]]; + mbgl::Value convertedValue = predicate.rightExpression.mgl_constantMBGLValue; + XCTAssertTrue(convertedValue.is()); +} + @end diff --git a/platform/darwin/test/MGLFeatureTests.mm b/platform/darwin/test/MGLFeatureTests.mm index efc30d307..91ec9d429 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 7d51c15cf..000000000 --- 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 000000000..fb50512af --- /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 + +@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()); + auto rawLayer = layer.rawLayer->as(); + + // fill-antialias + { + XCTAssertTrue(rawLayer->getFillAntialias().isUndefined(), + @"fill-antialias should be unset initially."); + MGLStyleValue *defaultStyleValue = layer.fillAntialiased; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@NO]; + layer.fillAntialiased = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.fillAntialiased = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.fillColor; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:[MGLColor redColor]]; + layer.fillColor = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.fillColor = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.fillOpacity; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@0xff]; + layer.fillOpacity = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.fillOpacity = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.fillOutlineColor; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:[MGLColor redColor]]; + layer.fillOutlineColor = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.fillOutlineColor = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.fillPattern; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@"Fill Pattern"]; + layer.fillPattern = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.fillPattern = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.fillTranslation; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue: +#if TARGET_OS_IPHONE + [NSValue valueWithCGVector:CGVectorMake(1, 1)] +#else + [NSValue valueWithMGLVector:CGVectorMake(1, -1)] +#endif + ]; + layer.fillTranslation = styleValue; + mbgl::style::PropertyValue> 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.fillTranslation = styleValue; + propertyValue = { mbgl::style::Function> { + {{ 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 *defaultStyleValue = layer.fillTranslationAnchor; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:[NSValue valueWithMGLFillTranslationAnchor:MGLFillTranslationAnchorViewport]]; + layer.fillTranslationAnchor = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.fillTranslationAnchor = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 e688d5058..000000000 --- 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 *)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(); - - 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 e877c1d57..000000000 --- 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 000000000..24a9d7afe --- /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 + +@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()); + auto rawLayer = layer.rawLayer->as(); + + // line-cap + { + XCTAssertTrue(rawLayer->getLineCap().isUndefined(), + @"line-cap should be unset initially."); + MGLStyleValue *defaultStyleValue = layer.lineCap; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:[NSValue valueWithMGLLineCap:MGLLineCapSquare]]; + layer.lineCap = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.lineCap = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.lineJoin; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:[NSValue valueWithMGLLineJoin:MGLLineJoinMiter]]; + layer.lineJoin = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.lineJoin = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.lineMiterLimit; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@0xff]; + layer.lineMiterLimit = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.lineMiterLimit = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.lineRoundLimit; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@0xff]; + layer.lineRoundLimit = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.lineRoundLimit = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.lineBlur; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@0xff]; + layer.lineBlur = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.lineBlur = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.lineColor; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:[MGLColor redColor]]; + layer.lineColor = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.lineColor = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *> *defaultStyleValue = layer.lineDashPattern; + + MGLStyleValue *> *styleValue = [MGLStyleValue *> valueWithRawValue:@[@1, @2]]; + layer.lineDashPattern = styleValue; + mbgl::style::PropertyValue> 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 *> valueWithStops:@{ + @18: styleValue, + }]; + layer.lineDashPattern = styleValue; + propertyValue = { mbgl::style::Function> { + {{ 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 *defaultStyleValue = layer.lineGapWidth; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@0xff]; + layer.lineGapWidth = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.lineGapWidth = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.lineOffset; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@0xff]; + layer.lineOffset = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.lineOffset = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.lineOpacity; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@0xff]; + layer.lineOpacity = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.lineOpacity = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.linePattern; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@"Line Pattern"]; + layer.linePattern = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.linePattern = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.lineTranslation; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue: +#if TARGET_OS_IPHONE + [NSValue valueWithCGVector:CGVectorMake(1, 1)] +#else + [NSValue valueWithMGLVector:CGVectorMake(1, -1)] +#endif + ]; + layer.lineTranslation = styleValue; + mbgl::style::PropertyValue> 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.lineTranslation = styleValue; + propertyValue = { mbgl::style::Function> { + {{ 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 *defaultStyleValue = layer.lineTranslationAnchor; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:[NSValue valueWithMGLLineTranslationAnchor:MGLLineTranslationAnchorViewport]]; + layer.lineTranslationAnchor = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.lineTranslationAnchor = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.lineWidth; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@0xff]; + layer.lineWidth = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.lineWidth = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 000000000..0c8a9f814 --- /dev/null +++ b/platform/darwin/test/MGLNSStringAdditionsTests.m @@ -0,0 +1,42 @@ +#import + +#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 000000000..fbd144d28 --- /dev/null +++ b/platform/darwin/test/MGLPredicateTests.mm @@ -0,0 +1,419 @@ +#import +#import + +#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 * _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 f8de191da..000000000 --- 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 000000000..28a201961 --- /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 + +@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()); + auto rawLayer = layer.rawLayer->as(); + + // raster-brightness-max + { + XCTAssertTrue(rawLayer->getRasterBrightnessMax().isUndefined(), + @"raster-brightness-max should be unset initially."); + MGLStyleValue *defaultStyleValue = layer.maximumRasterBrightness; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@0xff]; + layer.maximumRasterBrightness = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.maximumRasterBrightness = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.minimumRasterBrightness; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@0xff]; + layer.minimumRasterBrightness = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.minimumRasterBrightness = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.rasterContrast; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@0xff]; + layer.rasterContrast = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.rasterContrast = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.rasterFadeDuration; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@0xff]; + layer.rasterFadeDuration = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.rasterFadeDuration = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.rasterHueRotation; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@0xff]; + layer.rasterHueRotation = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.rasterHueRotation = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.rasterOpacity; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@0xff]; + layer.rasterOpacity = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.rasterOpacity = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.rasterSaturation; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@0xff]; + layer.rasterSaturation = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.rasterSaturation = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 8857dba9c..000000000 --- a/platform/darwin/test/MGLRuntimeStylingHelper.h +++ /dev/null @@ -1,35 +0,0 @@ -#import - -#import "MGLTypes.h" -#import "MGLStyleValue.h" - -@interface MGLRuntimeStylingHelper : NSObject - -+ (MGLStyleConstantValue *)testPadding; -+ (MGLStyleFunction *)testPaddingFunction; - -+ (MGLStyleConstantValue *)testOffset; -+ (MGLStyleFunction *)testOffsetFunction; - -+ (MGLStyleConstantValue *> *)testFont; -+ (MGLStyleFunction *> *)testFontFunction; - -+ (MGLStyleConstantValue *> *)testDashArray; -+ (MGLStyleFunction *> *)testDashArrayFunction; - -+ (MGLStyleConstantValue *)testNumber; -+ (MGLStyleFunction *)testNumberFunction; - -+ (MGLStyleConstantValue *)testBool; -+ (MGLStyleFunction *)testBoolFunction; - -+ (MGLStyleConstantValue *)testString; -+ (MGLStyleFunction *)testStringFunction; - -+ (MGLStyleConstantValue *)testColor; -+ (MGLStyleFunction *)testColorFunction; - -+ (MGLStyleConstantValue *)testEnum:(NSUInteger)value type:(const char *)type; -+ (MGLStyleFunction *)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 955c664f2..000000000 --- a/platform/darwin/test/MGLRuntimeStylingHelper.m +++ /dev/null @@ -1,122 +0,0 @@ -#import "MGLRuntimeStylingHelper.h" - -#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR - #import - #define MGLEdgeInsets UIEdgeInsets -#else - #import - #define MGLEdgeInsets NSEdgeInsets -#endif - -@implementation MGLRuntimeStylingHelper - -+ (MGLStyleConstantValue *)testPadding -{ - MGLEdgeInsets insets = { - .top = 1, - .left = 1, - .bottom = 1, - .right = 1, - }; - return [MGLStyleConstantValue valueWithRawValue:[NSValue value:&insets withObjCType:@encode(MGLEdgeInsets)]]; -} - -+ (MGLStyleFunction *)testPaddingFunction -{ - return [MGLStyleFunction functionWithStops:@{@(18): self.testPadding}]; -} - -+ (MGLStyleConstantValue *)testOffset -{ - CGVector vector = CGVectorMake(1, 1); - return [MGLStyleConstantValue valueWithRawValue:[NSValue value:&vector withObjCType:@encode(CGVector)]]; -} - -+ (MGLStyleFunction *)testOffsetFunction -{ - return [MGLStyleFunction valueWithStops:@{ @(18): self.testOffset }]; -} - -+ (MGLStyleConstantValue *> *)testFont -{ - return [MGLStyleConstantValue *> valueWithRawValue:@[@"Open Sans Regular", @"Arial Unicode MS Regular"]]; -} - -+ (MGLStyleFunction *> *)testFontFunction -{ - return [MGLStyleFunction *> valueWithStops:@{ @18: self.testFont }]; -} - -+ (MGLStyleConstantValue *> *)testDashArray -{ - return [MGLStyleConstantValue *> valueWithRawValue:@[@1, @2]]; -} - -+ (MGLStyleFunction *> *)testDashArrayFunction -{ - return [MGLStyleFunction *> valueWithStops:@{ - @18: self.testDashArray, - }]; -} - -+ (MGLStyleConstantValue *)testNumber -{ - return [MGLStyleConstantValue valueWithRawValue:@1]; -} - -+ (MGLStyleFunction *)testNumberFunction -{ - return [MGLStyleFunction valueWithStops:@{ - @18: self.testNumber, - }]; -} - -+ (MGLStyleConstantValue *)testBool -{ - return [MGLStyleConstantValue valueWithRawValue:@YES]; -} - -+ (MGLStyleFunction *)testBoolFunction -{ - return [MGLStyleFunction valueWithStops:@{ - @18: self.testBool, - }]; -} - -+ (MGLStyleConstantValue *)testString -{ - return [MGLStyleConstantValue valueWithRawValue:@"test"]; -} - -+ (MGLStyleFunction *)testStringFunction -{ - return [MGLStyleFunction valueWithStops:@{ - @18: self.testString, - }]; -} - -+ (MGLStyleConstantValue *)testColor -{ - return [MGLStyleConstantValue valueWithRawValue:[MGLColor redColor]]; -} - -+ (MGLStyleFunction *)testColorFunction -{ - return [MGLStyleFunction valueWithStops:@{ - @18: self.testColor, - }]; -} - -+ (MGLStyleConstantValue *)testEnum:(NSUInteger)value type:(const char *)type -{ - return [MGLStyleConstantValue valueWithRawValue:[NSValue value:&value withObjCType:type]]; -} - -+ (MGLStyleFunction *)testEnumFunction:(NSUInteger)value type:(const char *)type -{ - return [MGLStyleFunction valueWithStops:@{ - @18: [self testEnum:value type:type], - }]; -} - -@end diff --git a/platform/darwin/test/MGLShapeSourceTests.mm b/platform/darwin/test/MGLShapeSourceTests.mm index efff4b393..cf32b5c82 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 74ce62e89..f0b889f02 100644 --- a/platform/darwin/test/MGLStyleLayerTests.h +++ b/platform/darwin/test/MGLStyleLayerTests.h @@ -1,11 +1,8 @@ #import -#import "MGLRuntimeStylingHelper.h" #import @interface MGLStyleLayerTests : XCTestCase -@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 590d6eda7..1dba9f430 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 6b7bfe2f1..000000000 --- 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 000000000..00842a5b4 --- /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 _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->isLayer>()); + auto rawLayer = layer.rawLayer->asLayer>(); +<% 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 23ad22e7e..000000000 --- a/platform/darwin/test/MGLStyleLayerTests.xib +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/platform/darwin/test/MGLStyleTests.mm b/platform/darwin/test/MGLStyleTests.mm index e9ba4a9af..176217619 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 #import "NSBundle+MGLAdditions.h" @@ -25,20 +12,46 @@ #endif #import -@interface MGLStyleTests : XCTestCase +@interface MGLStyleTests : XCTestCase @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 *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 *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 *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 bf0143511..18b6a901d 100644 --- a/platform/darwin/test/MGLStyleValueTests.swift +++ b/platform/darwin/test/MGLStyleValueTests.swift @@ -17,8 +17,8 @@ extension MGLStyleValueTests { XCTAssertEqual((symbolStyleLayer.iconHaloWidth as! MGLStyleConstantValue).rawValue, 3) // String - symbolStyleLayer.textField = MGLStyleConstantValue(rawValue: "{name}") - XCTAssertEqual((symbolStyleLayer.textField as! MGLStyleConstantValue).rawValue, "{name}") + symbolStyleLayer.text = MGLStyleConstantValue(rawValue: "{name}") + XCTAssertEqual((symbolStyleLayer.text as! MGLStyleConstantValue).rawValue, "{name}") } func testFunctions() { @@ -32,7 +32,7 @@ extension MGLStyleValueTests { 3: MGLStyleValue(rawValue: true), 4: MGLStyleValue(rawValue: false), ] - symbolStyleLayer.iconAllowsOverlap = MGLStyleFunction(base: 1, stops: stops) - XCTAssertEqual((symbolStyleLayer.iconAllowsOverlap as! MGLStyleFunction), MGLStyleFunction(base: 1, stops: stops)) + symbolStyleLayer.iconAllowsOverlap = MGLStyleFunction(interpolationBase: 1, stops: stops) + XCTAssertEqual((symbolStyleLayer.iconAllowsOverlap as! MGLStyleFunction), MGLStyleFunction(interpolationBase: 1, stops: stops)) } } diff --git a/platform/darwin/test/MGLSymbolStyleLayerTests.m b/platform/darwin/test/MGLSymbolStyleLayerTests.m deleted file mode 100644 index 40250a8c7..000000000 --- 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 000000000..80a9c9d3e --- /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 + +@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()); + auto rawLayer = layer.rawLayer->as(); + + // icon-allow-overlap + { + XCTAssertTrue(rawLayer->getIconAllowOverlap().isUndefined(), + @"icon-allow-overlap should be unset initially."); + MGLStyleValue *defaultStyleValue = layer.iconAllowsOverlap; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@YES]; + layer.iconAllowsOverlap = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.iconAllowsOverlap = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.iconIgnoresPlacement; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@YES]; + layer.iconIgnoresPlacement = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.iconIgnoresPlacement = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.iconImageName; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@"Icon Image"]; + layer.iconImageName = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.iconImageName = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.iconOffset; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue: +#if TARGET_OS_IPHONE + [NSValue valueWithCGVector:CGVectorMake(1, 1)] +#else + [NSValue valueWithMGLVector:CGVectorMake(1, -1)] +#endif + ]; + layer.iconOffset = styleValue; + mbgl::style::PropertyValue> 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.iconOffset = styleValue; + propertyValue = { mbgl::style::Function> { + {{ 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 *defaultStyleValue = layer.iconOptional; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@YES]; + layer.iconOptional = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.iconOptional = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.iconPadding; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@0xff]; + layer.iconPadding = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.iconPadding = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.iconRotation; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@0xff]; + layer.iconRotation = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.iconRotation = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.iconRotationAlignment; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:[NSValue valueWithMGLIconRotationAlignment:MGLIconRotationAlignmentAuto]]; + layer.iconRotationAlignment = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.iconRotationAlignment = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.iconScale; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@0xff]; + layer.iconScale = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.iconScale = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.iconTextFit; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:[NSValue valueWithMGLIconTextFit:MGLIconTextFitBoth]]; + layer.iconTextFit = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.iconTextFit = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.iconTextFitPadding; + + MGLStyleValue *styleValue = [MGLStyleValue 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> 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.iconTextFitPadding = styleValue; + propertyValue = { mbgl::style::Function> { + {{ 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 *defaultStyleValue = layer.keepsIconUpright; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@YES]; + layer.keepsIconUpright = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.keepsIconUpright = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.keepsTextUpright; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@NO]; + layer.keepsTextUpright = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.keepsTextUpright = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.maximumTextAngle; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@0xff]; + layer.maximumTextAngle = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.maximumTextAngle = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.maximumTextWidth; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@0xff]; + layer.maximumTextWidth = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.maximumTextWidth = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.symbolAvoidsEdges; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@YES]; + layer.symbolAvoidsEdges = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.symbolAvoidsEdges = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.symbolPlacement; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:[NSValue valueWithMGLSymbolPlacement:MGLSymbolPlacementLine]]; + layer.symbolPlacement = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.symbolPlacement = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.symbolSpacing; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@0xff]; + layer.symbolSpacing = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.symbolSpacing = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.text; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@"Text Field"]; + layer.text = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.text = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.textAllowsOverlap; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@YES]; + layer.textAllowsOverlap = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.textAllowsOverlap = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.textAnchor; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:[NSValue valueWithMGLTextAnchor:MGLTextAnchorBottomRight]]; + layer.textAnchor = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.textAnchor = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *> *defaultStyleValue = layer.textFontNames; + + MGLStyleValue *> *styleValue = [MGLStyleValue *> valueWithRawValue:@[@"Text Font", @"Tnof Txet"]]; + layer.textFontNames = styleValue; + mbgl::style::PropertyValue> 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 *> valueWithStops:@{ + @18: styleValue, + }]; + layer.textFontNames = styleValue; + propertyValue = { mbgl::style::Function> { + {{ 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 *defaultStyleValue = layer.textFontSize; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@0xff]; + layer.textFontSize = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.textFontSize = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.textIgnoresPlacement; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@YES]; + layer.textIgnoresPlacement = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.textIgnoresPlacement = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.textJustification; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:[NSValue valueWithMGLTextJustification:MGLTextJustificationRight]]; + layer.textJustification = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.textJustification = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.textLetterSpacing; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@0xff]; + layer.textLetterSpacing = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.textLetterSpacing = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.textLineHeight; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@0xff]; + layer.textLineHeight = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.textLineHeight = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.textOffset; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue: +#if TARGET_OS_IPHONE + [NSValue valueWithCGVector:CGVectorMake(1, 1)] +#else + [NSValue valueWithMGLVector:CGVectorMake(1, -1)] +#endif + ]; + layer.textOffset = styleValue; + mbgl::style::PropertyValue> 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.textOffset = styleValue; + propertyValue = { mbgl::style::Function> { + {{ 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 *defaultStyleValue = layer.textOptional; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@YES]; + layer.textOptional = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.textOptional = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.textPadding; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@0xff]; + layer.textPadding = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.textPadding = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.textPitchAlignment; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:[NSValue valueWithMGLTextPitchAlignment:MGLTextPitchAlignmentAuto]]; + layer.textPitchAlignment = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.textPitchAlignment = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.textRotation; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@0xff]; + layer.textRotation = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.textRotation = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.textRotationAlignment; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:[NSValue valueWithMGLTextRotationAlignment:MGLTextRotationAlignmentAuto]]; + layer.textRotationAlignment = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.textRotationAlignment = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.textTransform; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:[NSValue valueWithMGLTextTransform:MGLTextTransformLowercase]]; + layer.textTransform = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.textTransform = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.iconColor; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:[MGLColor redColor]]; + layer.iconColor = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.iconColor = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.iconHaloBlur; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@0xff]; + layer.iconHaloBlur = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.iconHaloBlur = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.iconHaloColor; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:[MGLColor redColor]]; + layer.iconHaloColor = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.iconHaloColor = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.iconHaloWidth; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@0xff]; + layer.iconHaloWidth = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.iconHaloWidth = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.iconOpacity; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@0xff]; + layer.iconOpacity = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.iconOpacity = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.iconTranslation; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue: +#if TARGET_OS_IPHONE + [NSValue valueWithCGVector:CGVectorMake(1, 1)] +#else + [NSValue valueWithMGLVector:CGVectorMake(1, -1)] +#endif + ]; + layer.iconTranslation = styleValue; + mbgl::style::PropertyValue> 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.iconTranslation = styleValue; + propertyValue = { mbgl::style::Function> { + {{ 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 *defaultStyleValue = layer.iconTranslationAnchor; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:[NSValue valueWithMGLIconTranslationAnchor:MGLIconTranslationAnchorViewport]]; + layer.iconTranslationAnchor = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.iconTranslationAnchor = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.textColor; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:[MGLColor redColor]]; + layer.textColor = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.textColor = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.textHaloBlur; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@0xff]; + layer.textHaloBlur = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.textHaloBlur = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.textHaloColor; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:[MGLColor redColor]]; + layer.textHaloColor = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.textHaloColor = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.textHaloWidth; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@0xff]; + layer.textHaloWidth = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.textHaloWidth = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.textOpacity; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:@0xff]; + layer.textOpacity = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.textOpacity = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 *defaultStyleValue = layer.textTranslation; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue: +#if TARGET_OS_IPHONE + [NSValue valueWithCGVector:CGVectorMake(1, 1)] +#else + [NSValue valueWithMGLVector:CGVectorMake(1, -1)] +#endif + ]; + layer.textTranslation = styleValue; + mbgl::style::PropertyValue> 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.textTranslation = styleValue; + propertyValue = { mbgl::style::Function> { + {{ 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 *defaultStyleValue = layer.textTranslationAnchor; + + MGLStyleValue *styleValue = [MGLStyleValue valueWithRawValue:[NSValue valueWithMGLTextTranslationAnchor:MGLTextTranslationAnchorViewport]]; + layer.textTranslationAnchor = styleValue; + mbgl::style::PropertyValue 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 valueWithStops:@{ + @18: styleValue, + }]; + layer.textTranslationAnchor = styleValue; + propertyValue = { mbgl::style::Function { + {{ 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 000000000..23c546181 --- /dev/null +++ b/platform/darwin/test/one-liner.json @@ -0,0 +1 @@ +{"version":8,"sources":{},"layers":[]} \ No newline at end of file diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index fa224eafb..00fdf9f2c 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -15,24 +15,25 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Xcode 7.3 or above is required for using this SDK. ([#6059](https://github.com/mapbox/mapbox-gl-native/issues/6059)) * Clarified that the `-ObjC` linker flag is required for linking against the static framework distribution of this SDK. ([#6213](https://github.com/mapbox/mapbox-gl-native/pull/6213)) +* The API reference has a sharper look. ([#7422](https://github.com/mapbox/mapbox-gl-native/pull/7422)) * Added documentation for the Info.plist keys used by this SDK. ([#6833](https://github.com/mapbox/mapbox-gl-native/pull/6833)) ### Styles and data * A new runtime styling API allows you to adjust the style and content of the base map dynamically. All the options available in [Mapbox Studio](https://www.mapbox.com/studio/) are now exposed via MGLStyle and subclasses of MGLStyleLayer and MGLSource. ([#5727](https://github.com/mapbox/mapbox-gl-native/pull/5727)) * MGLMapView’s `styleURL` property can now be set to an absolute file URL. ([#6026](https://github.com/mapbox/mapbox-gl-native/pull/6026)) -* MGLShapeSource objects, as well as GeoJSON sources specified by the stylesheet at design time, now support `cluster`, `clusterMaxZoom`, and `clusterRadius` attributes for clustering point features on the base map. ([#5724](https://github.com/mapbox/mapbox-gl-native/pull/5724)) +* When creating an MGLShapeSource, you can now specify options for clustering point features within the shape source. Similarly, GeoJSON sources specified by the stylesheet at design time can specify the `cluster`, `clusterMaxZoom`, and `clusterRadius` attributes. ([#5724](https://github.com/mapbox/mapbox-gl-native/pull/5724)) * Added [quadkey](https://msdn.microsoft.com/en-us/library/bb259689.aspx) support and limited WMS support in raster tile URL templates. ([#5628](https://github.com/mapbox/mapbox-gl-native/pull/5628)) -* TileJSON manifests can now specify `"scheme": "tms"` to indicate the use of [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) coordinates. ([#2270](https://github.com/mapbox/mapbox-gl-native/pull/2270)) -* Fixed an issue causing abstract MGLMultiPointFeature objects to be returned in feature query results. Now concrete MGLPointCollectionFeature objects are returned. ([#6742](https://github.com/mapbox/mapbox-gl-native/pull/6742)) +* When creating an MGLTileSource, you can now specify that the tile URLs use [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) coordinates by setting `MGLTileSourceOptionTileCoordinateSystem` to `MGLTileCoordinateSystemTMS`. TileJSON files can specify `"scheme": "tms"`. ([#2270](https://github.com/mapbox/mapbox-gl-native/pull/2270)) +* Fixed an issue causing abstract MGLMultiPointFeature objects to be returned in feature query results. Now concrete MGLPointCollectionFeature objects are returned. MGLMultiPointFeature is now an alias of MGLPointCollectionFeature. ([#6742](https://github.com/mapbox/mapbox-gl-native/pull/6742)) * Fixed rendering artifacts and missing glyphs that occurred after viewing a large number of CJK characters on the map. ([#5908](https://github.com/mapbox/mapbox-gl-native/pull/5908)) * `-[MGLMapView resetPosition]` now resets to the current style’s default center coordinates, zoom level, direction, and pitch, if specified. ([#6127](https://github.com/mapbox/mapbox-gl-native/pull/6127)) * Fixed an issue where feature querying sometimes failed to return the expected features when the map was tilted. ([#6773](https://github.com/mapbox/mapbox-gl-native/pull/6773)) * MGLFeature’s `attributes` and `identifier` properties are now writable. ([#6728](https://github.com/mapbox/mapbox-gl-native/pull/6728)) * The action sheet that appears when tapping the information button in the bottom-right corner now lists the correct attribution for the current style. ([#5999](https://github.com/mapbox/mapbox-gl-native/pull/5999)) -* The `text-pitch-alignment` property is now supported in stylesheets for improved street label legibility on a tilted map. ([#5288](https://github.com/mapbox/mapbox-gl-native/pull/5288)) -* The `icon-text-fit` and `icon-text-fit-padding` properties are now supported in stylesheets, allowing the background of a shield to automatically resize to fit the shield’s text. ([#5334](https://github.com/mapbox/mapbox-gl-native/pull/5334)) -* The `circle-pitch-scale` property is now supported in stylesheets, allowing circle features in a tilted base map to scale or remain the same size as the viewing distance changes. ([#5576](https://github.com/mapbox/mapbox-gl-native/pull/5576)) +* Added support for MGLSymbolStyleLayer’s `textPitchAlignment` property and the corresponding style JSON property for improved street label legibility on a tilted map. ([#5288](https://github.com/mapbox/mapbox-gl-native/pull/5288)) +* Added support for MGLSymbolStyleLayer’s `iconTextFit` and `iconTextFitPadding` properties and the corresponding style JSON properties, allowing the background of a shield to automatically resize to fit the shield’s text. ([#5334](https://github.com/mapbox/mapbox-gl-native/pull/5334)) +* Added support for MGLSymbolStyleLayer’s `circlePitchScale` property and the corresponding style JSON property, allowing circle features in a tilted base map to scale or remain the same size as the viewing distance changes. ([#5576](https://github.com/mapbox/mapbox-gl-native/pull/5576)) * The `identifier` property of an MGLFeature may now be either a number or string. ([#5514](https://github.com/mapbox/mapbox-gl-native/pull/5514)) * If MGLMapView is unable to obtain or parse a style, it now calls its delegate’s `-mapViewDidFailLoadingMap:withError:` method. ([#6145](https://github.com/mapbox/mapbox-gl-native/pull/6145)) * Added the `-[MGLMapViewDelegate mapView:didFinishLoadingStyle:]` delegate method, which offers the earliest opportunity to modify the layout or appearance of the current style before the map view is displayed to the user. ([#6636](https://github.com/mapbox/mapbox-gl-native/pull/6636)) @@ -56,6 +57,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Added new methods to MGLMultiPoint for changing the vertices along a polyline annotation or the exterior of a polygon annotation. ([#6565](https://github.com/mapbox/mapbox-gl-native/pull/6565)) * Added new APIs to MGLMapView to query for visible annotations. Combined with `-[MGLMapView viewForAnnotation:]`, these APIs can be used to access all visible annotation views. ([6061](https://github.com/mapbox/mapbox-gl-native/pull/6061)) +* Shape, feature, and annotation classes now conform to NSSecureCoding. ([#6559](https://github.com/mapbox/mapbox-gl-native/pull/6559)) * Fixed an issue causing offscreen annotation views to be updated even when they were in the reuse queue. ([#5987](https://github.com/mapbox/mapbox-gl-native/pull/5987)) * Fixed an issue preventing MGLAnnotationView from animating when its coordinate changes. ([#6215](https://github.com/mapbox/mapbox-gl-native/pull/6215)) * Fixed an issue causing the wrong annotation view to be selected when tapping an annotation view with a center offset applied. ([#5931](https://github.com/mapbox/mapbox-gl-native/pull/5931)) @@ -63,11 +65,11 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Fixed an issue causing the callout view to be dismissed when panning around. ([#6676](https://github.com/mapbox/mapbox-gl-native/pull/6676)) * Per documentation, the first and last coordinates in an MGLPolygon must be identical in order for the polygon to draw correctly. The same is true for an MGLPolygon’s interior polygon. ([#5514](https://github.com/mapbox/mapbox-gl-native/pull/5514)) * To make an MGLPolyline or MGLPolygon span the antimeridian, specify coordinates with longitudes greater than 180° or less than −180°. ([#6088](https://github.com/mapbox/mapbox-gl-native/pull/6088)) -* Deprecated `-[MGLMapViewDelegate mapView:alphaForShapeAnnotation:]` in favor of specifying an alpha component via `-[MGLMapViewDelegate mapView:strokeColorForShapeAnnotation:]` or `-[MGLMapViewDelegate mapView:fillColorForPolygonAnnotation:]`. ([#6706](https://github.com/mapbox/mapbox-gl-native/pull/6706)) * Various method arguments that are represented as C arrays of `CLLocationCoordinate2D` instances have been marked `const` to streamline bridging to Swift. ([#7215](https://github.com/mapbox/mapbox-gl-native/pull/7215)) * Fixed an issue that caused an annotation view to disappear if it isn’t created using the annotation view reuse queue. ([#6485](https://github.com/mapbox/mapbox-gl-native/pull/6485)) * Fixed an issue that could reset user-added transformations on annotation views. ([#6166](https://github.com/mapbox/mapbox-gl-native/pull/6166)) * Improved the performance of relocating a non-view-backed point annotation by changing its `coordinate` property. ([#5385](https://github.com/mapbox/mapbox-gl-native/pull/5385)) +* Fixed an issue that caused an assertion failure if a `MGLShapeCollection` (a GeoJSON GeometryCollection) was created with an empty array of shapes. ([#7632](https://github.com/mapbox/mapbox-gl-native/pull/7632)) * Improved the precision of annotations at zoom levels greater than 18. ([#5517](https://github.com/mapbox/mapbox-gl-native/pull/5517)) ### Networking and offline maps diff --git a/platform/ios/DEVELOPING.md b/platform/ios/DEVELOPING.md index 22635be26..72ad84868 100644 --- a/platform/ios/DEVELOPING.md +++ b/platform/ios/DEVELOPING.md @@ -6,7 +6,7 @@ This document explains how to build the Mapbox iOS SDK from source. It is intend The Mapbox iOS SDK and iosapp demo application build against the iOS 7.0 SDK. The SDK is intended to run on iOS 7.0 and above, while iosapp is intended to run on iOS 8.0 and above due to the use of a dynamic framework. Both require Xcode on a computer running macOS. -The Mapbox iOS SDK requires Xcode 7.3 or higher. +The Mapbox iOS SDK requires Xcode 7.3 or above. The iosapp demo application requires Xcode 8.0 or above to build. ## Building the SDK @@ -119,6 +119,22 @@ To add a localization to the iOS SDK: 1. In the Project navigator, expand each .strings and .stringsdict file in the project. An additional version for your localization should be listed; translate it. Translate everything on the right side of the equals sign. Leave the left side and any comments unmodified. See Apple’s documentation on the [.strings](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/LoadingResources/Strings/Strings.html) and [.stringsdict](https://developer.apple.com/library/content/documentation/MacOSX/Conceptual/BPInternational/StringsdictFileFormat/StringsdictFileFormat.html) formats. 1. You’re already most of the way towards localizing the macOS SDK too – consider [completing that localization](../macos/DEVELOPING.md#adding-a-localization). +### Adding a code example + +To add an example code listing to the documentation for a class or class member: + +1. Add a test method named in the form `testMGLClass` or `testMGLClass$method` + to [MGLDocumentationExampleTests](test/MGLDocumentationExampleTests.swift). + Wrap the code you’d like to appear in the documentation within + `//#-example-code` and `//#-end-example-code` comments. +1. Insert the code listings into the headers: + +```bash +make darwin-update-examples +``` + +[SourceKitten](https://github.com/jpsim/SourceKitten/) is required and will be installed automatically using Homebrew. + ## Testing `make ios-test` builds and runs unit tests of cross-platform code as well as the SDK. diff --git a/platform/ios/Mapbox-iOS-SDK-symbols.podspec b/platform/ios/Mapbox-iOS-SDK-symbols.podspec index 2cf3695af..1098c5cd1 100644 --- a/platform/ios/Mapbox-iOS-SDK-symbols.podspec +++ b/platform/ios/Mapbox-iOS-SDK-symbols.podspec @@ -1,14 +1,16 @@ Pod::Spec.new do |m| - m.name = 'Mapbox-iOS-SDK' - m.version = '3.4.0-beta.5-symbols' + version = '3.4.0-beta.7' + + m.name = 'Mapbox-iOS-SDK-symbols' + m.version = "#{version}-symbols" m.summary = 'Open source vector map solution for iOS with full styling capabilities.' m.description = 'Open source, OpenGL-based vector map solution for iOS with full styling capabilities and Cocoa Touch APIs.' m.homepage = 'https://www.mapbox.com/ios-sdk/' m.license = { :type => 'BSD', :file => 'LICENSE.md' } m.author = { 'Mapbox' => 'mobile@mapbox.com' } - m.screenshot = 'https://raw.githubusercontent.com/mapbox/mapbox-gl-native/master/platform/ios/screenshot.png' + m.screenshot = "https://www.mapbox.com/ios-sdk/api/#{version}/img/screenshot.png" m.social_media_url = 'https://twitter.com/mapbox' m.documentation_url = 'https://www.mapbox.com/ios-sdk/api/' diff --git a/platform/ios/Mapbox-iOS-SDK.podspec b/platform/ios/Mapbox-iOS-SDK.podspec index 8bc963f91..cf3f3afa6 100644 --- a/platform/ios/Mapbox-iOS-SDK.podspec +++ b/platform/ios/Mapbox-iOS-SDK.podspec @@ -1,14 +1,16 @@ Pod::Spec.new do |m| + version = '3.4.0-beta.7' + m.name = 'Mapbox-iOS-SDK' - m.version = '3.4.0-beta.5' + m.version = version m.summary = 'Open source vector map solution for iOS with full styling capabilities.' m.description = 'Open source, OpenGL-based vector map solution for iOS with full styling capabilities and Cocoa Touch APIs.' m.homepage = 'https://www.mapbox.com/ios-sdk/' m.license = { :type => 'BSD', :file => 'LICENSE.md' } m.author = { 'Mapbox' => 'mobile@mapbox.com' } - m.screenshot = 'https://raw.githubusercontent.com/mapbox/mapbox-gl-native/master/platform/ios/screenshot.png' + m.screenshot = "https://www.mapbox.com/ios-sdk/api/#{version}/img/screenshot.png" m.social_media_url = 'https://twitter.com/mapbox' m.documentation_url = 'https://www.mapbox.com/ios-sdk/api/' diff --git a/platform/ios/README.md b/platform/ios/README.md index 2350d1e52..3d4cc3ff2 100644 --- a/platform/ios/README.md +++ b/platform/ios/README.md @@ -9,4 +9,4 @@ This repository is for day-to-day development of the SDK. Building the SDK yours * [Integrating the Mapbox iOS SDK into your application](INSTALL.md) * [Contributing to the Mapbox iOS SDK](DEVELOPING.md) -![](screenshot.png) +![](docs/img/screenshot.png) diff --git a/platform/ios/app/MBXCustomCalloutView.h b/platform/ios/app/MBXCustomCalloutView.h index a61619b79..961f2d7f6 100644 --- a/platform/ios/app/MBXCustomCalloutView.h +++ b/platform/ios/app/MBXCustomCalloutView.h @@ -7,4 +7,8 @@ * callout title for demonstration purpose. */ @interface MBXCustomCalloutView : UIView + +@property (nonatomic, assign, getter=isAnchoredToAnnotation) BOOL anchoredToAnnotation; +@property (nonatomic, assign) BOOL dismissesAutomatically; + @end diff --git a/platform/ios/app/MBXCustomCalloutView.m b/platform/ios/app/MBXCustomCalloutView.m index 9edc00f6e..e46b727d8 100644 --- a/platform/ios/app/MBXCustomCalloutView.m +++ b/platform/ios/app/MBXCustomCalloutView.m @@ -17,6 +17,8 @@ static CGFloat const tipWidth = 10.0; } @synthesize representedObject = _representedObject; +@synthesize anchoredToAnnotation = _anchoredToAnnotation; +@synthesize dismissesAutomatically = _dismissesAutomatically; @synthesize leftAccessoryView = _leftAccessoryView; @synthesize rightAccessoryView = _rightAccessoryView; @synthesize delegate = _delegate; @@ -66,6 +68,11 @@ static CGFloat const tipWidth = 10.0; } } +- (void)setCenter:(CGPoint)center { + center.y = center.y - CGRectGetMidY(self.bounds); + [super setCenter:center]; +} + - (void)dismissCalloutAnimated:(BOOL)animated { if (self.superview) diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m index 05c2f9169..a14ad9350 100644 --- a/platform/ios/app/MBXViewController.m +++ b/platform/ios/app/MBXViewController.m @@ -89,6 +89,8 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { @end @interface MBXCustomCalloutAnnotation : MGLPointAnnotation +@property (nonatomic, assign) BOOL anchoredToAnnotation; +@property (nonatomic, assign) BOOL dismissesAutomatically; @end @implementation MBXCustomCalloutAnnotation @@ -624,6 +626,17 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { [self.mapView addAnnotation:triangle]; + // West coast polyline + // + CLLocationCoordinate2D lineCoordinates[4] = { + CLLocationCoordinate2DMake(47.6025, -122.3327), + CLLocationCoordinate2DMake(45.5189, -122.6726), + CLLocationCoordinate2DMake(37.7790, -122.4177), + CLLocationCoordinate2DMake(34.0532, -118.2349) + }; + MGLPolyline *line = [MGLPolyline polylineWithCoordinates:lineCoordinates count:4]; + [self.mapView addAnnotation:line]; + // Orcas Island, WA hike polyline // NSDictionary *hike = [NSJSONSerialization JSONObjectWithData: @@ -699,12 +712,28 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { { [self.mapView removeAnnotations:self.mapView.annotations]; - MBXCustomCalloutAnnotation *annotation = [[MBXCustomCalloutAnnotation alloc] init]; - annotation.coordinate = CLLocationCoordinate2DMake(48.8533940, 2.3775439); - annotation.title = @"Custom Callout"; + MBXCustomCalloutAnnotation *firstAnnotation = [[MBXCustomCalloutAnnotation alloc] init]; + firstAnnotation.coordinate = CLLocationCoordinate2DMake(48.8533940, 2.3775439); + firstAnnotation.title = @"Open anchored to annotation"; + firstAnnotation.anchoredToAnnotation = YES; + firstAnnotation.dismissesAutomatically = NO; + + MBXCustomCalloutAnnotation *secondAnnotation = [[MBXCustomCalloutAnnotation alloc] init]; + secondAnnotation.coordinate = CLLocationCoordinate2DMake(48.8543940, 2.3775439); + secondAnnotation.title = @"Open not anchored to annotation"; + secondAnnotation.anchoredToAnnotation = NO; + secondAnnotation.dismissesAutomatically = NO; - [self.mapView addAnnotation:annotation]; - [self.mapView showAnnotations:@[annotation] animated:YES]; + MBXCustomCalloutAnnotation *thirdAnnotation = [[MBXCustomCalloutAnnotation alloc] init]; + thirdAnnotation.coordinate = CLLocationCoordinate2DMake(48.8553940, 2.3775439); + thirdAnnotation.title = @"Dismisses automatically"; + thirdAnnotation.anchoredToAnnotation = YES; + thirdAnnotation.dismissesAutomatically = YES; + + NSArray *annotations = @[firstAnnotation, secondAnnotation, thirdAnnotation]; + [self.mapView addAnnotations:annotations]; + + [self.mapView showAnnotations:annotations animated:YES]; } - (void)styleWaterLayer @@ -901,8 +930,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { } dispatch_async(dispatch_get_main_queue(), ^{ - MGLShapeCollectionFeature *features = [MGLShapeCollectionFeature shapeCollectionWithShapes:visibleFeatures]; - MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:querySourceID shape:features options:nil]; + MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:querySourceID features:visibleFeatures options:nil]; [self.mapView.style addSource:source]; MGLFillStyleLayer *fillLayer = [[MGLFillStyleLayer alloc] initWithIdentifier:queryLayerID source:source]; @@ -1184,13 +1212,13 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { MGLSymbolStyleLayer *layer = (MGLSymbolStyleLayer *)[self.mapView.style layerWithIdentifier:layerName]; if ([layer isKindOfClass:[MGLSymbolStyleLayer class]]) { - if ([layer.textField isKindOfClass:[MGLStyleConstantValue class]]) { - MGLStyleConstantValue *label = (MGLStyleConstantValue *)layer.textField; + if ([layer.text isKindOfClass:[MGLStyleConstantValue class]]) { + MGLStyleConstantValue *label = (MGLStyleConstantValue *)layer.text; if ([label.rawValue hasPrefix:@"{name"]) { - layer.textField = [MGLStyleValue valueWithRawValue:language]; + layer.text = [MGLStyleValue valueWithRawValue:language]; } - } else if ([layer.textField isKindOfClass:[MGLStyleFunction class]]) { - MGLStyleFunction *function = (MGLStyleFunction *)layer.textField; + } else if ([layer.text isKindOfClass:[MGLStyleFunction class]]) { + MGLStyleFunction *function = (MGLStyleFunction *)layer.text; [function.stops enumerateKeysAndObjectsUsingBlock:^(id zoomLevel, id stop, BOOL *done) { if ([stop isKindOfClass:[MGLStyleConstantValue class]]) { MGLStyleConstantValue *label = (MGLStyleConstantValue *)stop; @@ -1199,7 +1227,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { } } }]; - layer.textField = function; + layer.text = function; } } else { NSLog(@"%@ is not a symbol style layer", layerName); @@ -1537,9 +1565,15 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { return YES; } +- (CGFloat)mapView:(__unused MGLMapView *)mapView alphaForShapeAnnotation:(MGLShape *)annotation +{ + return ([annotation isKindOfClass:[MGLPolygon class]] ? 0.5 : 1.0); +} + - (UIColor *)mapView:(__unused MGLMapView *)mapView strokeColorForShapeAnnotation:(MGLShape *)annotation { - return ([annotation isKindOfClass:[MGLPolyline class]] ? [UIColor purpleColor] : [UIColor blackColor]); + UIColor *color = [annotation isKindOfClass:[MGLPolyline class]] ? [UIColor greenColor] : [UIColor blackColor]; + return [color colorWithAlphaComponent:0.9]; } - (UIColor *)mapView:(__unused MGLMapView *)mapView fillColorForPolygonAnnotation:(__unused MGLPolygon *)annotation @@ -1582,8 +1616,11 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { if ([annotation respondsToSelector:@selector(title)] && [annotation isKindOfClass:[MBXCustomCalloutAnnotation class]]) { + MBXCustomCalloutAnnotation *customAnnotation = (MBXCustomCalloutAnnotation *)annotation; MBXCustomCalloutView *calloutView = [[MBXCustomCalloutView alloc] init]; calloutView.representedObject = annotation; + calloutView.anchoredToAnnotation = customAnnotation.anchoredToAnnotation; + calloutView.dismissesAutomatically = customAnnotation.dismissesAutomatically; return calloutView; } return nil; @@ -1613,8 +1650,8 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { { return; } - - MGLPointAnnotation *point = annotation; + + MGLPointAnnotation *point = (MGLPointAnnotation *)annotation; point.coordinate = [self.mapView convertPoint:self.mapView.center toCoordinateFromView:self.mapView]; } diff --git a/platform/ios/benchmark/MBXBenchViewController.mm b/platform/ios/benchmark/MBXBenchViewController.mm index 43e98d7cc..4f26d0cb1 100644 --- a/platform/ios/benchmark/MBXBenchViewController.mm +++ b/platform/ios/benchmark/MBXBenchViewController.mm @@ -3,7 +3,7 @@ #import "MBXBenchAppDelegate.h" #import -#import "MGLMapView_Internal.h" +#import "MGLMapView_Private.h" #include "locations.hpp" diff --git a/platform/ios/docs/doc-README.md b/platform/ios/docs/doc-README.md index 22493b150..e91bc0b1c 100644 --- a/platform/ios/docs/doc-README.md +++ b/platform/ios/docs/doc-README.md @@ -2,7 +2,7 @@ The Mapbox iOS SDK is an open-source framework for embedding interactive map views with scalable, customizable vector maps into Cocoa Touch applications on iOS 7.0 and above using Objective-C, Swift, or Interface Builder. It takes stylesheets that conform to the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/), applies them to vector tiles that conform to the [Mapbox Vector Tile Specification](https://www.mapbox.com/developers/vector-tiles/), and renders them using OpenGL. -![Mapbox iOS SDK screenshots](screenshot.png) +![Mapbox iOS SDK screenshots](img/screenshot.png) For setup information, check out the [Mapbox iOS SDK homepage](https://www.mapbox.com/ios-sdk/). For detailed usage instructions, read “[First steps with the Mapbox iOS SDK](https://www.mapbox.com/help/first-steps-ios-sdk/)” and consult the [online examples](https://www.mapbox.com/ios-sdk/examples/). A [full changelog](https://github.com/mapbox/mapbox-gl-native/blob/master/platform/ios/CHANGELOG.md) is also available. diff --git a/platform/ios/docs/guides/Adding Points to a Map.md b/platform/ios/docs/guides/Adding Points to a Map.md new file mode 100644 index 000000000..17e8ad359 --- /dev/null +++ b/platform/ios/docs/guides/Adding Points to a Map.md @@ -0,0 +1,83 @@ +# Adding Points to a Map + +Mapbox offers a few different ways to add points to a map, each with different tradeoffs. + +## MGLPointAnnotation + +It’s straightforward to add an annotation to a map. You can use `MGLPointAnnotation` as is, or you can subclass it to add annotations with richer data. + +```swift +let annotation = MGLPointAnnotation() +annotation.coordinate = CLLocationCoordinate2D(latitude: 45.5076, longitude: -122.6736) +annotation.title = "Bobby's Coffee" +annotation.subtitle = "Coffeeshop" +mapView.addAnnotation(annotation) +``` + +See the `MGLMapViewDelegate` method `-mapView:annotationCanShowCallout:` and similar methods for allowing interaction with a callout ([example](https://www.mapbox.com/ios-sdk/examples/callout-delegate/)). + +## Displaying annotations + +There are two basic ways to display the annotations you’ve added to a map, each with their own tradeoffs. + +### Annotation Images (`MGLAnnotationImage`) + +Annotation images are the quickest and most performant way to display annotations, but are also the most basic. + +By default, annotations added to the map are displayed with a red pin ([example](https://www.mapbox.com/ios-sdk/examples/marker/)). To use custom images, you can implement `MGLMapViewDelegate` `-mapView:imageForAnnotation:` ([example](https://www.mapbox.com/ios-sdk/examples/marker-image/)). + +**Pros** + +* The easiest way to display a marker on a map +* Easily customizable with any `UIImage` +* High performance, as the images are rendered directly in OpenGL + +**Cons** + +* Annotation images are purely static and cannot be animated +* No control over z-ordering +* Limits to the number and size of images you can add + +### Annotation Views (`MGLAnnotationView`) + +If you’re looking to add custom `UIView`s or have annotations that are dynamic or animatable, consider an `MGLAnnotationView` instead of an `MGLAnnotationImage` ([example](https://www.mapbox.com/ios-sdk/examples/annotation-views/)). + +Annotation views have significant advantages over annotation images when you need every annotation to be unique. For example, annotation views are ideal for showing user locations on a map using high-resolution profile pictures. + +To use annotation views, implement `MGLMapViewDelegate` `-mapView:viewForAnnotation` and provide a custom `MGLAnnotationView` (`UIView`) subclass. + +**Pros** + +* Custom, native UIViews +* No limit on style or image size +* Full support for animations +* Relative control over z-ordering using the `zPosition` property on `CALayer` +* [Familiar API for MapKit users](https://www.mapbox.com/help/switch-mapkit/#annotations-pins) + +**Cons** + +* Performance implications: + * `UIView`s are inherently slow to render compared to OpenGL, more apparent if you’re adding many views or moving the map rapidly + * In some cases, you might consider runtime styling + +## Advanced: Runtime Styling + +For absolute full control of how points are displayed on a map, consider [runtime styling](runtime-styling.html). + +You can use `MGLPointFeature` or any of the other [style feature subclasses](Style%20Features.html) to add points and shapes to an `MGLShapeSource`. + +From there, you can create one or many `MGLSymbolStyleLayer` or `MGLCircleStyleLayer` layers to filter and style points for display on the map ([example](https://www.mapbox.com/ios-sdk/examples/runtime-multiple-annotations)). + +**Pros** + +* Most powerful option +* Highest performance (rendered in GL directly) +* SDK-level support for labels rendered together with icons +* Finer control of z-ordering + * Rendering respects ordering within the data source + * Otherwise layers are lightweight so you can create a new layer for each level you need + +**Cons** + +* Currently you must implement your own tap gesture recognizer together with `MGLMapView.visibleFeaturesAtPoint` to recognize taps and manually show callouts ([example](https://www.mapbox.com/ios-sdk/examples/select-feature)). +* Currently no SDK support for animations. If you need animations, consider using an NSTimer and updating the layer properties accordingly. diff --git a/platform/ios/docs/guides/For Style Authors.md b/platform/ios/docs/guides/For Style Authors.md new file mode 100644 index 000000000..753eb7200 --- /dev/null +++ b/platform/ios/docs/guides/For Style Authors.md @@ -0,0 +1,306 @@ + +# 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. + +## Designing for iOS + +When designing your style, consider the context in which your application shows +the style. There are a number of considerations specific to iOS 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. +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 + +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 iOS. +Standard-resolution displays are limited to older devices that your application +may or may not support, depending on its minimum deployment target. + +Icon and text labels should be legible regardless of the map’s orientation. +By default, this SDK makes it easy for your users to rotate or tilt the map +using multitouch gestures. +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. +A text label may look like a tappable button merely due to matching your +application’s tint color or the default blue tint color. +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. + +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. + +For more information about user interface design, consult Apple’s +[_iOS Human Interface Guidelines_](https://developer.apple.com/ios/human-interface-guidelines/). + +## 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. +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 iOS 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 +--------------|----------- +`background` | `MGLBackgroundStyleLayer` +`circle` | `MGLCircleStyleLayer` +`fill` | `MGLFillStyleLayer` +`line` | `MGLLineStyleLayer` +`raster` | `MGLRasterStyleLayer` +`symbol` | `MGLSymbolStyleLayer` + +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: + +### Circle style layers + +In style JSON | In Objective-C | In Swift +--------------|----------------|--------- +`circle-pitch-scale` | `MGLCircleStyleLayer.circleScaleAlignment` | `MGLCircleStyleLayer.circleScaleAlignment` +`circle-translate` | `MGLCircleStyleLayer.circleTranslation` | `MGLCircleStyleLayer.circleTranslation` +`circle-translate-anchor` | `MGLCircleStyleLayer.circleTranslationAnchor` | `MGLCircleStyleLayer.circleTranslationAnchor` + +### Fill style layers + +In style JSON | In Objective-C | In Swift +--------------|----------------|--------- +`fill-antialias` | `MGLFillStyleLayer.fillAntialiased` | `MGLFillStyleLayer.isFillAntialiased` +`fill-translate` | `MGLFillStyleLayer.fillTranslation` | `MGLFillStyleLayer.fillTranslation` +`fill-translate-anchor` | `MGLFillStyleLayer.fillTranslationAnchor` | `MGLFillStyleLayer.fillTranslationAnchor` + +### Line style layers + +In style JSON | In Objective-C | In Swift +--------------|----------------|--------- +`line-dasharray` | `MGLLineStyleLayer.lineDashPattern` | `MGLLineStyleLayer.lineDashPattern` +`line-translate` | `MGLLineStyleLayer.lineTranslation` | `MGLLineStyleLayer.lineTranslation` +`line-translate-anchor` | `MGLLineStyleLayer.lineTranslationAnchor` | `MGLLineStyleLayer.lineTranslationAnchor` + +### Raster style layers + +In style JSON | In Objective-C | In Swift +--------------|----------------|--------- +`raster-brightness-max` | `MGLRasterStyleLayer.maximumRasterBrightness` | `MGLRasterStyleLayer.maximumRasterBrightness` +`raster-brightness-min` | `MGLRasterStyleLayer.minimumRasterBrightness` | `MGLRasterStyleLayer.minimumRasterBrightness` +`raster-hue-rotate` | `MGLRasterStyleLayer.rasterHueRotation` | `MGLRasterStyleLayer.rasterHueRotation` + +### Symbol style layers + +In style JSON | In Objective-C | In Swift +--------------|----------------|--------- +`icon-allow-overlap` | `MGLSymbolStyleLayer.iconAllowsOverlap` | `MGLSymbolStyleLayer.iconAllowsOverlap` +`icon-ignore-placement` | `MGLSymbolStyleLayer.iconIgnoresPlacement` | `MGLSymbolStyleLayer.iconIgnoresPlacement` +`icon-image` | `MGLSymbolStyleLayer.iconImageName` | `MGLSymbolStyleLayer.iconImageName` +`icon-optional` | `MGLSymbolStyleLayer.iconOptional` | `MGLSymbolStyleLayer.isIconOptional` +`icon-rotate` | `MGLSymbolStyleLayer.iconRotation` | `MGLSymbolStyleLayer.iconRotation` +`icon-size` | `MGLSymbolStyleLayer.iconScale` | `MGLSymbolStyleLayer.iconScale` +`icon-keep-upright` | `MGLSymbolStyleLayer.keepsIconUpright` | `MGLSymbolStyleLayer.keepsIconUpright` +`text-keep-upright` | `MGLSymbolStyleLayer.keepsTextUpright` | `MGLSymbolStyleLayer.keepsTextUpright` +`text-max-angle` | `MGLSymbolStyleLayer.maximumTextAngle` | `MGLSymbolStyleLayer.maximumTextAngle` +`text-max-width` | `MGLSymbolStyleLayer.maximumTextWidth` | `MGLSymbolStyleLayer.maximumTextWidth` +`symbol-avoid-edges` | `MGLSymbolStyleLayer.symbolAvoidsEdges` | `MGLSymbolStyleLayer.symbolAvoidsEdges` +`text-field` | `MGLSymbolStyleLayer.text` | `MGLSymbolStyleLayer.text` +`text-allow-overlap` | `MGLSymbolStyleLayer.textAllowsOverlap` | `MGLSymbolStyleLayer.textAllowsOverlap` +`text-font` | `MGLSymbolStyleLayer.textFontNames` | `MGLSymbolStyleLayer.textFontNames` +`text-size` | `MGLSymbolStyleLayer.textFontSize` | `MGLSymbolStyleLayer.textFontSize` +`text-ignore-placement` | `MGLSymbolStyleLayer.textIgnoresPlacement` | `MGLSymbolStyleLayer.textIgnoresPlacement` +`text-justify` | `MGLSymbolStyleLayer.textJustification` | `MGLSymbolStyleLayer.textJustification` +`text-optional` | `MGLSymbolStyleLayer.textOptional` | `MGLSymbolStyleLayer.isTextOptional` +`text-rotate` | `MGLSymbolStyleLayer.textRotation` | `MGLSymbolStyleLayer.textRotation` +`icon-translate` | `MGLSymbolStyleLayer.iconTranslation` | `MGLSymbolStyleLayer.iconTranslation` +`icon-translate-anchor` | `MGLSymbolStyleLayer.iconTranslationAnchor` | `MGLSymbolStyleLayer.iconTranslationAnchor` +`text-translate` | `MGLSymbolStyleLayer.textTranslation` | `MGLSymbolStyleLayer.textTranslation` +`text-translate-anchor` | `MGLSymbolStyleLayer.textTranslationAnchor` | `MGLSymbolStyleLayer.textTranslationAnchor` + +## 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 | `UIColor` | `UIColor` +Enum | `NSValue` (see `NSValue(MGLAdditions)`) | `NSValue` (see `NSValue(MGLAdditions)`) +String | `NSString` | `String` +Boolean | `NSNumber.boolValue` | `Bool` +Number | `NSNumber.floatValue` | `Float` +Array (`-dasharray`) | `NSArray` | `[Float]` +Array (`-font`) | `NSArray` | `[String]` +Array (`-offset`, `-translate`) | `NSValue.CGVectorValue` | `NSValue.cgVectorValue` +Array (`-padding`) | `NSValue.UIEdgeInsetsValue` | `NSValue.uiEdgeInsetsValue` + +For padding attributes, note that the arguments to +`UIEdgeInsetsMake()` 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. + +## 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/ios/docs/Info.plist Keys.md b/platform/ios/docs/guides/Info.plist Keys.md similarity index 85% rename from platform/ios/docs/Info.plist Keys.md rename to platform/ios/docs/guides/Info.plist Keys.md index 34d3da9e2..c5c7cf1d8 100644 --- a/platform/ios/docs/Info.plist Keys.md +++ b/platform/ios/docs/guides/Info.plist Keys.md @@ -1,6 +1,6 @@ # Info.plist Keys -The Mapbox iOS SDK supports custom `Info.plist` keys in your application in order to configure various settings. +The Mapbox iOS SDK supports custom `Info.plist` keys in your application in order to configure various settings. ## MGLMapboxAccessToken @@ -8,13 +8,13 @@ Set the [Mapbox access token](https://www.mapbox.com/help/define-access-token/) Mapbox-hosted vector tiles and styles require an API access token, which you can obtain from the [Mapbox account page](https://www.mapbox.com/studio/account/tokens/). Access tokens associate requests to Mapbox’s vector tile and style APIs with your Mapbox account. They also deter other developers from using your styles without your permission. -As an alternative, you can use `+[MGLAccountManager setAccessToken:]` to set a token in code. See [our guide](https://www.mapbox.com/help/ios-private-access-token/) for some tips on keeping access tokens in open source code private. +As an alternative, you can use `+[MGLAccountManager setAccessToken:]` to set a token in code. See [our guide](https://www.mapbox.com/help/ios-private-access-token/) for some tips on keeping access tokens in open source code private. ## MGLMapboxAPIBaseURL -Use this key if you need to customize the API base URL used throughout the SDK. If unset, the default Mapbox API is used. +Use this key if you need to customize the API base URL used throughout the SDK. If unset, the default Mapbox API is used. -The default value is `https://api.mapbox.com`. +The default value is `https://api.mapbox.com`. ## MGLMapboxMetricsEnabledSettingShownInApp diff --git a/platform/ios/docs/guides/Runtime Styling.md b/platform/ios/docs/guides/Runtime Styling.md new file mode 100644 index 000000000..e85b466c6 --- /dev/null +++ b/platform/ios/docs/guides/Runtime Styling.md @@ -0,0 +1,53 @@ +# Runtime Styling + +Mapbox’s runtime styling features allow you direct control over every layer in your maps with code. It’s now possible to create dynamic maps and visualizations that aren’t possible with other mobile maps SDKs. + +Runtime styling expands upon the design power of [Mapbox Studio](https://www.mapbox.com/mapbox-studio/) and exposes all of the same properties and attributes directly to mobile developers in our SDK. + +Beyond the custom styled maps that you can create with Mapbox Studio, you can now change the look and feel of your map on the fly having maps in your app visually respond to user interaction or or context. Or leverage the power of OpenGL for highly performant and complex data visualizations. Now it’s possible to mix in your own data and bring your map to life. + +## Example use cases + +As an example of what’s possible with runtime styling, consider some of the following use cases: + +### Styling maps on the fly + +At runtime, you can tailor the map specifically to your user interface. Tweak colors, text, and icons to match the style to your brand. + +![dynamic styles](img/runtime-styling/DynamicStyles.gif "an example showing dynamic styles") + +For maps that aren’t going to change in response to custom data or user interaction, consider creating a custom map style with [Mapbox Studio](https://www.mapbox.com/mapbox-studio/). + +### Map interactivity + +You can customize the map to the point of having it respond dynamically based on the actions your users are taking. Increase the text size of streets while a user is driving, emphasize points of interest tailored to a user’s preferences, or change your UI if users are at parks, trails, landmarks, or rivers. + +![emojis](img/runtime-styling/Emoji.gif "an example showing emoji interaction") + +### Powerful data visualization + +Mapbox maps are built on top of OpenGL and can support rendering data without the traditional overhead of `UIView`-based map annotations. + +Mapbox can support data visualizations that were slow or impossible with traditional map SDKs. Render heatmaps, visualize population density, or even go so far as updating the snow levels in the mountains to match recent snowfall. + +![hex bins](img/runtime-styling/HexBins.gif "an example using hex bins") +![population](img/runtime-styling/Population.gif "an example showing population density") +![snow levels](img/runtime-styling/SnowLevels.gif "an example visualizing snow levels in the mountains") + +### Powerful annotations + +The Mapbox SDK gives you access to all of the same tools we use to render our default map styles. Instead of using generic pin markers, enrich your place data or custom polygons with icons and labels that make your maps stand out. + +![custom annotations](img/runtime-styling/CustomAnnotations.gif "an example showing custom annotations") + +### Custom shapes + +Draw custom shapes on the map the same way you would a custom `UIView` or `CALayer`. These shapes keep their geographic scale and are perfect for visualizing everything from indoor floor plans to metro systems to hurricane tracks. + +## Resources + +* [Information for style authors](for-style-authors.html) +* [Mapbox Streets source reference](https://www.mapbox.com/vector-tiles/mapbox-streets-v7/) +* [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/) +* [Mapbox Studio](https://www.mapbox.com/mapbox-studio/) +* [iOS code examples](https://www.mapbox.com/ios-sdk/examples/) diff --git a/platform/ios/docs/guides/Working with Mapbox Studio.md b/platform/ios/docs/guides/Working with Mapbox Studio.md new file mode 100644 index 000000000..959731a3a --- /dev/null +++ b/platform/ios/docs/guides/Working with Mapbox Studio.md @@ -0,0 +1,96 @@ +# Working with Mapbox Studio + +[Mapbox Studio’s Styles editor](http://mapbox.com/studio) is Mapbox’s tool for creating custom map styles. It also serves as an excellent tool for rapidly prototyping dynamic maps and [runtime styling](runtime-styling.html) interactions for iOS. + +## Creating a base style + +Start by heading to [mapbox.com/studio](https://www.mapbox.com/studio) and creating a new style. Any style that’s close to what you’ll be using in your app is ideal. + +## Prototyping with data + +The goal in using Mapbox Studio for prototyping runtime styling implementations is to test data presentation assumptions as quickly as possible. With the Mapbox Studio tools, you can import a small subset of your own real data, fake data as a placeholder, or prototype with existing Mapbox data. + +### Prototyping with Mapbox data +The default [Mapbox Streets tileset](https://www.mapbox.com/studio/tilesets/mapbox.mapbox-streets-v7/) might offer data similar to your own that you can use to style before you swap in your own data at runtime. + +For example, if you’re looking to prototype points of interest, consider the `poi_label` layer; if you want to style GPS traces, the `roads` layer might be a good proxy. Take a look at what’s available in [Mapbox Streets](https://www.mapbox.com/studio/tilesets/mapbox.mapbox-streets-v7/): there’s probably a layer that closely matches your data. + +### Importing real data +If you can’t find a good approximation for your data in Mapbox Streets, consider uploading a small subset of your data into Mapbox Studio as a custom tileset. + +From the [Mapbox Studio Dashboard](https://www.mapbox.com/studio/), click `Tilesets` in the sidebar, then click `New Tileset` to get started with most common geo file formats including KML, GPX, GeoJSON, Shapefiles, and CSV. + +### Faking placeholder data +If you don’t have any custom data on hand in a format that works easily with the Tileset importer, you can fake placeholder data with the Dataset Editor. + +From the [Mapbox Studio Dashboard](https://www.mapbox.com/studio/), click `Datasets` in the sidebar, then click on `New Dataset` to get started. + +Zoom into your desired location and use the draw tools on the left to start creating a set of sample data. + +![create shapes](img/studio-workflow/create-polygons.gif) + +Next, add data properties you’d like to use to drive your style. Consider categorical properties or numeric properties that you’d use to filter and group your data. Text properties can be used to display icons or labels. + +![add properties](img/studio-workflow/add-properties.gif) + +**General Guidelines:** + +* Text along a line: add line with a text property +* Text at specific points on a line or polygon: in addition to the line, create points at the specific points you’d like with text properties +* If you want circles where scale doesn’t matter relative to the geography (e.g. always 20 pixels), you can add as a point and style with a circle layer or a symbol +* If you want circles or arcs where the scale matters (e.g. 10 mile radius), you’ll need to approximately freehand a polygon that you can create more precisely later in code. + +When you’re done, save your dataset and export as a tileset. When that’s complete, add your tileset to your style. + +### Import into your style + +1. Click `New Layer` +2. Select your tileset +3. Select your shape type: + * `Symbol`: best for text and icons + * `Line`: best for lines or adding strokes to polygons + * `Fill`: best for filling polygons + * `Circle`: for styling points or nodes along a line or polygon as circles. If you need circles of a fixed radius (e.g. 1 mile radius), you should model your data as a polygon. +4. Add filters if necessary + * You can selectively filter your data by their properties to group and style separately +5. Click on `Create Layer` + +## Styling with Mapbox Studio + +Mapbox Studio shines for styling your data and the process is much faster than attempting to style natively. + +There are some nuances to understand between the different layer types and how they work together. Don’t be afraid to use the layers sidebar to peek into the techniques used to style the stock Mapbox maps. You can duplicate these layers, re-point the source to your own data, and tweak as needed. + +**Best Practices:** + +* Layers are cheap, so duplicate and update filters liberally. +* If you’d like to stroke polygons you’ll need to use two layers: one a fill and one a stroked line. +* If you want to stroke a line, create two layers, one for the default stroke and one with a wider width for its casing +* If you intend to animate properties or transition between values, consider creating separate layers for each state and toggling visibility to visualize the difference. + +## Implement on iOS with runtime styling + +Once you’re happy with the styles you’ve created, it’s time to [get setup with Mapbox in your app](https://www.mapbox.com/ios-sdk/). + +To implement your prototypes with runtime styling: + +1. Implement `-[MGLMapViewDelegate mapView:didFinishLoadingStyle:]`. +2. Add your real data as a source: + * This can be done using vector data from tileset editor ([example](https://www.mapbox.com/ios-sdk/examples/runtime-circle-styles)), custom vector tiles, added as GeoJSON ([example](https://www.mapbox.com/ios-sdk/examples/runtime-add-line), or added manually through the app via `MGLShapeSource` ([example](https://www.mapbox.com/ios-sdk/examples/runtime-multiple-annotations)) +3. For each layer you’ve prototyped in Studio, add its corresponding `MGLStyleLayer` subclass. See [“Configuring the map content’s appearance”](for-style-authors.html#configuring-the-map-content-s-appearance) for the available style layer classes. + +**Translating style attributes from Studio** +For each property you’ve edited in Studio, you can hover over the property name to find the corresponding property in the iOS SDK. It’ll generally be the camelCased version of the Property ID, but see [“Configuring the map content’s appearance”](for-style-authors.html#configuring-the-map-content-s-appearance) for a table of properties that differ between Mapbox Studio and the iOS SDK. + +![property values](img/studio-workflow/property-values.png) + +**Translating stop functions** +It’s possible to use stop functions in Mapbox Studio to transition the style of a layer by its zoom level (e.g. a line that gets wider as you zoom in). These can be translated in the mobile SDKs using `+[MGLSyleValue valueWithInterpolationBase:stops:]`. The rate of change between stops in Studio is represented by `interpolationBase`. + +![Stop functions](img/studio-workflow/stop-functions.png) + +## Resources + +* [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/) +* [Mapbox Studio](https://www.mapbox.com/mapbox-studio/) +* [iOS code examples](https://www.mapbox.com/ios-sdk/examples/) diff --git a/platform/ios/docs/img/runtime-styling/CustomAnnotations.gif b/platform/ios/docs/img/runtime-styling/CustomAnnotations.gif new file mode 100644 index 000000000..dee99d01f Binary files /dev/null and b/platform/ios/docs/img/runtime-styling/CustomAnnotations.gif differ diff --git a/platform/ios/docs/img/runtime-styling/DynamicStyles.gif b/platform/ios/docs/img/runtime-styling/DynamicStyles.gif new file mode 100644 index 000000000..b42d30c60 Binary files /dev/null and b/platform/ios/docs/img/runtime-styling/DynamicStyles.gif differ diff --git a/platform/ios/docs/img/runtime-styling/Emoji.gif b/platform/ios/docs/img/runtime-styling/Emoji.gif new file mode 100644 index 000000000..fc50b2897 Binary files /dev/null and b/platform/ios/docs/img/runtime-styling/Emoji.gif differ diff --git a/platform/ios/docs/img/runtime-styling/HexBins.gif b/platform/ios/docs/img/runtime-styling/HexBins.gif new file mode 100644 index 000000000..c810085f2 Binary files /dev/null and b/platform/ios/docs/img/runtime-styling/HexBins.gif differ diff --git a/platform/ios/docs/img/runtime-styling/Population.gif b/platform/ios/docs/img/runtime-styling/Population.gif new file mode 100644 index 000000000..81b6c6310 Binary files /dev/null and b/platform/ios/docs/img/runtime-styling/Population.gif differ diff --git a/platform/ios/docs/img/runtime-styling/SnowLevels.gif b/platform/ios/docs/img/runtime-styling/SnowLevels.gif new file mode 100644 index 000000000..8ee2f9fdd Binary files /dev/null and b/platform/ios/docs/img/runtime-styling/SnowLevels.gif differ diff --git a/platform/ios/screenshot.png b/platform/ios/docs/img/screenshot.png similarity index 100% rename from platform/ios/screenshot.png rename to platform/ios/docs/img/screenshot.png diff --git a/platform/ios/docs/img/studio-workflow/add-properties.gif b/platform/ios/docs/img/studio-workflow/add-properties.gif new file mode 100644 index 000000000..740fae655 Binary files /dev/null and b/platform/ios/docs/img/studio-workflow/add-properties.gif differ diff --git a/platform/ios/docs/img/studio-workflow/create-polygons.gif b/platform/ios/docs/img/studio-workflow/create-polygons.gif new file mode 100644 index 000000000..6eb2c0afb Binary files /dev/null and b/platform/ios/docs/img/studio-workflow/create-polygons.gif differ diff --git a/platform/ios/docs/img/studio-workflow/property-values.png b/platform/ios/docs/img/studio-workflow/property-values.png new file mode 100644 index 000000000..95704241f Binary files /dev/null and b/platform/ios/docs/img/studio-workflow/property-values.png differ diff --git a/platform/ios/docs/img/studio-workflow/stop-functions.png b/platform/ios/docs/img/studio-workflow/stop-functions.png new file mode 100644 index 000000000..4affecf00 Binary files /dev/null and b/platform/ios/docs/img/studio-workflow/stop-functions.png differ diff --git a/platform/ios/docs/pod-README.md b/platform/ios/docs/pod-README.md index 78647b260..b1a763bcf 100644 --- a/platform/ios/docs/pod-README.md +++ b/platform/ios/docs/pod-README.md @@ -4,7 +4,7 @@ The Mapbox iOS SDK is an open-source framework for embedding interactive map vie For more information, check out the [Mapbox iOS SDK homepage](https://www.mapbox.com/ios-sdk/) and the [full changelog](https://github.com/mapbox/mapbox-gl-native/blob/master/platform/ios/CHANGELOG.md) online. -[![](https://raw.githubusercontent.com/mapbox/mapbox-gl-native/master/platform/ios/screenshot.png)]() +[![](https://raw.githubusercontent.com/mapbox/mapbox-gl-native/master/platform/ios/docs/img/screenshot.png)]() ## Installation diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index 6f2ee38e9..42ae50e94 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -86,20 +86,23 @@ 3566C76F1D4A8DFA008152BC /* MGLRasterSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3566C76B1D4A8DFA008152BC /* MGLRasterSource.mm */; }; 3566C7711D4A9198008152BC /* MGLSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 3566C7701D4A9198008152BC /* MGLSource_Private.h */; }; 3566C7721D4A9198008152BC /* MGLSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 3566C7701D4A9198008152BC /* MGLSource_Private.h */; }; - 357579801D501E09000B822E /* MGLFillStyleLayerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3575797F1D501E09000B822E /* MGLFillStyleLayerTests.m */; }; - 357579831D502AE6000B822E /* MGLRasterStyleLayerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 357579821D502AE6000B822E /* MGLRasterStyleLayerTests.m */; }; - 357579851D502AF5000B822E /* MGLSymbolStyleLayerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 357579841D502AF5000B822E /* MGLSymbolStyleLayerTests.m */; }; - 357579871D502AFE000B822E /* MGLLineStyleLayerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 357579861D502AFE000B822E /* MGLLineStyleLayerTests.m */; }; - 357579891D502B06000B822E /* MGLCircleStyleLayerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 357579881D502B06000B822E /* MGLCircleStyleLayerTests.m */; }; - 3575798B1D502B0C000B822E /* MGLBackgroundStyleLayerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3575798A1D502B0C000B822E /* MGLBackgroundStyleLayerTests.m */; }; - 3575798E1D502EC7000B822E /* MGLRuntimeStylingHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 3575798D1D502EC7000B822E /* MGLRuntimeStylingHelper.m */; }; + 357579801D501E09000B822E /* MGLFillStyleLayerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3575797F1D501E09000B822E /* MGLFillStyleLayerTests.mm */; }; + 357579831D502AE6000B822E /* MGLRasterStyleLayerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 357579821D502AE6000B822E /* MGLRasterStyleLayerTests.mm */; }; + 357579851D502AF5000B822E /* MGLSymbolStyleLayerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 357579841D502AF5000B822E /* MGLSymbolStyleLayerTests.mm */; }; + 357579871D502AFE000B822E /* MGLLineStyleLayerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 357579861D502AFE000B822E /* MGLLineStyleLayerTests.mm */; }; + 357579891D502B06000B822E /* MGLCircleStyleLayerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 357579881D502B06000B822E /* MGLCircleStyleLayerTests.mm */; }; + 3575798B1D502B0C000B822E /* MGLBackgroundStyleLayerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3575798A1D502B0C000B822E /* MGLBackgroundStyleLayerTests.mm */; }; + 357FE2DD1E02D2B20068B753 /* NSCoder+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 357FE2DB1E02D2B20068B753 /* NSCoder+MGLAdditions.h */; }; + 357FE2DE1E02D2B20068B753 /* NSCoder+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 357FE2DB1E02D2B20068B753 /* NSCoder+MGLAdditions.h */; }; + 357FE2DF1E02D2B20068B753 /* NSCoder+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 357FE2DC1E02D2B20068B753 /* NSCoder+MGLAdditions.mm */; }; + 357FE2E01E02D2B20068B753 /* NSCoder+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 357FE2DC1E02D2B20068B753 /* NSCoder+MGLAdditions.mm */; }; 3599A3E61DF708BC00E77FB2 /* MGLStyleValueTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3599A3E51DF708BC00E77FB2 /* MGLStyleValueTests.m */; }; 359F57461D2FDDA6005217F1 /* MGLUserLocationAnnotationView_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 359F57451D2FDBD5005217F1 /* MGLUserLocationAnnotationView_Private.h */; }; 35B82BF81D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 35B82BF61D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h */; }; 35B82BF91D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 35B82BF61D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h */; }; 35B82BFA1D6C5F8400B1B721 /* NSPredicate+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 35B82BF71D6C5F8400B1B721 /* NSPredicate+MGLAdditions.mm */; }; 35B82BFB1D6C5F8400B1B721 /* NSPredicate+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 35B82BF71D6C5F8400B1B721 /* NSPredicate+MGLAdditions.mm */; }; - 35B8E08C1D6C8B5100E768D2 /* MGLFilterTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 35B8E08B1D6C8B5100E768D2 /* MGLFilterTests.mm */; }; + 35B8E08C1D6C8B5100E768D2 /* MGLPredicateTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 35B8E08B1D6C8B5100E768D2 /* MGLPredicateTests.mm */; }; 35CE61821D4165D9004F2359 /* UIColor+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 35CE61801D4165D9004F2359 /* UIColor+MGLAdditions.h */; }; 35CE61831D4165D9004F2359 /* UIColor+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 35CE61801D4165D9004F2359 /* UIColor+MGLAdditions.h */; }; 35CE61841D4165D9004F2359 /* UIColor+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 35CE61811D4165D9004F2359 /* UIColor+MGLAdditions.mm */; }; @@ -112,6 +115,7 @@ 35D13AC41D3D19DD00AFB4E0 /* MGLFillStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 35D13AC11D3D19DD00AFB4E0 /* MGLFillStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 35D13AC51D3D19DD00AFB4E0 /* MGLFillStyleLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 35D13AC21D3D19DD00AFB4E0 /* MGLFillStyleLayer.mm */; }; 35D13AC61D3D19DD00AFB4E0 /* MGLFillStyleLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 35D13AC21D3D19DD00AFB4E0 /* MGLFillStyleLayer.mm */; }; + 35D9DDE21DA25EEC00DAAD69 /* MGLCodingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 35D9DDE11DA25EEC00DAAD69 /* MGLCodingTests.m */; }; 35E0CFE61D3E501500188327 /* MGLStyle_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 35E0CFE51D3E501500188327 /* MGLStyle_Private.h */; }; 35E0CFE71D3E501500188327 /* MGLStyle_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 35E0CFE51D3E501500188327 /* MGLStyle_Private.h */; }; 35E1A4D81D74336F007AA97F /* MGLValueEvaluator.h in Headers */ = {isa = PBXBuildFile; fileRef = 35E1A4D71D74336F007AA97F /* MGLValueEvaluator.h */; }; @@ -128,10 +132,6 @@ 4018B1C91CDC288A00F666AF /* MGLAnnotationView_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4018B1C31CDC277F00F666AF /* MGLAnnotationView_Private.h */; }; 4018B1CA1CDC288E00F666AF /* MGLAnnotationView.h in Headers */ = {isa = PBXBuildFile; fileRef = 4018B1C51CDC277F00F666AF /* MGLAnnotationView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4018B1CB1CDC288E00F666AF /* MGLAnnotationView.h in Headers */ = {isa = PBXBuildFile; fileRef = 4018B1C51CDC277F00F666AF /* MGLAnnotationView.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 4032C5BF1DE1FC780062E8BD /* NSValue+MGLStyleEnumAttributeAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 4032C5B81DE1EE7D0062E8BD /* NSValue+MGLStyleEnumAttributeAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 4032C5C01DE1FC780062E8BD /* NSValue+MGLStyleEnumAttributeAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 4032C5B81DE1EE7D0062E8BD /* NSValue+MGLStyleEnumAttributeAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 4032C5C11DE1FC7E0062E8BD /* NSValue+MGLStyleEnumAttributeAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4032C5BD1DE1FC690062E8BD /* NSValue+MGLStyleEnumAttributeAdditions.mm */; }; - 4032C5C21DE1FC7E0062E8BD /* NSValue+MGLStyleEnumAttributeAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4032C5BD1DE1FC690062E8BD /* NSValue+MGLStyleEnumAttributeAdditions.mm */; }; 404326891D5B9B27007111BD /* MGLAnnotationContainerView_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 404326881D5B9B1A007111BD /* MGLAnnotationContainerView_Private.h */; }; 4049C29D1DB6CD6C00B3F799 /* MGLPointCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = 4049C29B1DB6CD6C00B3F799 /* MGLPointCollection.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4049C29E1DB6CD6C00B3F799 /* MGLPointCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = 4049C29B1DB6CD6C00B3F799 /* MGLPointCollection.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -167,6 +167,7 @@ 556660D81E1D085500E2C41B /* MGLVersionNumber.m in Sources */ = {isa = PBXBuildFile; fileRef = 556660D71E1D085500E2C41B /* MGLVersionNumber.m */; }; 556660DB1E1D8E8D00E2C41B /* MGLFoundation.h in Headers */ = {isa = PBXBuildFile; fileRef = 556660C91E1BF3A900E2C41B /* MGLFoundation.h */; settings = {ATTRIBUTES = (Public, ); }; }; 55D8C9961D0F18CE00F42F10 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 55D8C9951D0F18CE00F42F10 /* libsqlite3.tbd */; }; + 6407D6701E0085FD00F6A9C3 /* MGLDocumentationExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6407D66F1E0085FD00F6A9C3 /* MGLDocumentationExampleTests.swift */; }; 7E016D7E1D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 7E016D7C1D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.h */; }; 7E016D7F1D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 7E016D7C1D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.h */; }; 7E016D801D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 7E016D7D1D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.m */; }; @@ -225,6 +226,7 @@ DA35A2CA1CCAAAD200E826B2 /* NSValue+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DA35A2C71CCAAAD200E826B2 /* NSValue+MGLAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; DA35A2CB1CCAAAD200E826B2 /* NSValue+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2C81CCAAAD200E826B2 /* NSValue+MGLAdditions.m */; }; DA35A2CC1CCAAAD200E826B2 /* NSValue+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2C81CCAAAD200E826B2 /* NSValue+MGLAdditions.m */; }; + DA35D0881E1A6309007DED41 /* one-liner.json in Resources */ = {isa = PBXBuildFile; fileRef = DA35D0871E1A6309007DED41 /* one-liner.json */; }; DA6408DB1DA4E7D300908C90 /* MGLVectorStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = DA6408D91DA4E7D300908C90 /* MGLVectorStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; DA6408DC1DA4E7D300908C90 /* MGLVectorStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = DA6408D91DA4E7D300908C90 /* MGLVectorStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; DA6408DD1DA4E7D300908C90 /* MGLVectorStyleLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = DA6408DA1DA4E7D300908C90 /* MGLVectorStyleLayer.m */; }; @@ -410,6 +412,7 @@ DAD165791CF4CDFF001FF4B9 /* MGLShapeCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = DAD165761CF4CDFF001FF4B9 /* MGLShapeCollection.h */; settings = {ATTRIBUTES = (Public, ); }; }; DAD1657A1CF4CDFF001FF4B9 /* MGLShapeCollection.mm in Sources */ = {isa = PBXBuildFile; fileRef = DAD165771CF4CDFF001FF4B9 /* MGLShapeCollection.mm */; }; DAD1657B1CF4CDFF001FF4B9 /* MGLShapeCollection.mm in Sources */ = {isa = PBXBuildFile; fileRef = DAD165771CF4CDFF001FF4B9 /* MGLShapeCollection.mm */; }; + DAE7DEC21E245455007505A6 /* MGLNSStringAdditionsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DAE7DEC11E245455007505A6 /* MGLNSStringAdditionsTests.m */; }; DAED38631D62D0FC00D7640F /* NSURL+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DAED38611D62D0FC00D7640F /* NSURL+MGLAdditions.h */; }; DAED38641D62D0FC00D7640F /* NSURL+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DAED38611D62D0FC00D7640F /* NSURL+MGLAdditions.h */; }; DAED38651D62D0FC00D7640F /* NSURL+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DAED38621D62D0FC00D7640F /* NSURL+MGLAdditions.m */; }; @@ -555,26 +558,27 @@ 3566C76A1D4A8DFA008152BC /* MGLRasterSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLRasterSource.h; sourceTree = ""; }; 3566C76B1D4A8DFA008152BC /* MGLRasterSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLRasterSource.mm; sourceTree = ""; }; 3566C7701D4A9198008152BC /* MGLSource_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLSource_Private.h; sourceTree = ""; }; - 3575797F1D501E09000B822E /* MGLFillStyleLayerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLFillStyleLayerTests.m; path = ../../darwin/test/MGLFillStyleLayerTests.m; sourceTree = ""; }; - 357579821D502AE6000B822E /* MGLRasterStyleLayerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLRasterStyleLayerTests.m; path = ../../darwin/test/MGLRasterStyleLayerTests.m; sourceTree = ""; }; - 357579841D502AF5000B822E /* MGLSymbolStyleLayerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLSymbolStyleLayerTests.m; path = ../../darwin/test/MGLSymbolStyleLayerTests.m; sourceTree = ""; }; - 357579861D502AFE000B822E /* MGLLineStyleLayerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLLineStyleLayerTests.m; path = ../../darwin/test/MGLLineStyleLayerTests.m; sourceTree = ""; }; - 357579881D502B06000B822E /* MGLCircleStyleLayerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLCircleStyleLayerTests.m; path = ../../darwin/test/MGLCircleStyleLayerTests.m; sourceTree = ""; }; - 3575798A1D502B0C000B822E /* MGLBackgroundStyleLayerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLBackgroundStyleLayerTests.m; path = ../../darwin/test/MGLBackgroundStyleLayerTests.m; sourceTree = ""; }; - 3575798C1D502EC7000B822E /* MGLRuntimeStylingHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MGLRuntimeStylingHelper.h; path = ../../darwin/test/MGLRuntimeStylingHelper.h; sourceTree = ""; }; - 3575798D1D502EC7000B822E /* MGLRuntimeStylingHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLRuntimeStylingHelper.m; path = ../../darwin/test/MGLRuntimeStylingHelper.m; sourceTree = ""; }; + 3575797F1D501E09000B822E /* MGLFillStyleLayerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLFillStyleLayerTests.mm; path = ../../darwin/test/MGLFillStyleLayerTests.mm; sourceTree = ""; }; + 357579821D502AE6000B822E /* MGLRasterStyleLayerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLRasterStyleLayerTests.mm; path = ../../darwin/test/MGLRasterStyleLayerTests.mm; sourceTree = ""; }; + 357579841D502AF5000B822E /* MGLSymbolStyleLayerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLSymbolStyleLayerTests.mm; path = ../../darwin/test/MGLSymbolStyleLayerTests.mm; sourceTree = ""; }; + 357579861D502AFE000B822E /* MGLLineStyleLayerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLLineStyleLayerTests.mm; path = ../../darwin/test/MGLLineStyleLayerTests.mm; sourceTree = ""; }; + 357579881D502B06000B822E /* MGLCircleStyleLayerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLCircleStyleLayerTests.mm; path = ../../darwin/test/MGLCircleStyleLayerTests.mm; sourceTree = ""; }; + 3575798A1D502B0C000B822E /* MGLBackgroundStyleLayerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLBackgroundStyleLayerTests.mm; path = ../../darwin/test/MGLBackgroundStyleLayerTests.mm; sourceTree = ""; }; 357F09091DF84F3800941873 /* MGLStyleValueTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MGLStyleValueTests.h; path = ../../darwin/test/MGLStyleValueTests.h; sourceTree = ""; }; + 357FE2DB1E02D2B20068B753 /* NSCoder+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSCoder+MGLAdditions.h"; path = "../../darwin/src/NSCoder+MGLAdditions.h"; sourceTree = ""; }; + 357FE2DC1E02D2B20068B753 /* NSCoder+MGLAdditions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "NSCoder+MGLAdditions.mm"; path = "../../darwin/src/NSCoder+MGLAdditions.mm"; sourceTree = ""; }; 3599A3E51DF708BC00E77FB2 /* MGLStyleValueTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLStyleValueTests.m; path = ../../darwin/test/MGLStyleValueTests.m; sourceTree = ""; }; 359F57451D2FDBD5005217F1 /* MGLUserLocationAnnotationView_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLUserLocationAnnotationView_Private.h; sourceTree = ""; }; 35B82BF61D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSPredicate+MGLAdditions.h"; sourceTree = ""; }; 35B82BF71D6C5F8400B1B721 /* NSPredicate+MGLAdditions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSPredicate+MGLAdditions.mm"; sourceTree = ""; }; - 35B8E08B1D6C8B5100E768D2 /* MGLFilterTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLFilterTests.mm; path = ../../darwin/test/MGLFilterTests.mm; sourceTree = ""; }; + 35B8E08B1D6C8B5100E768D2 /* MGLPredicateTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLPredicateTests.mm; path = ../../darwin/test/MGLPredicateTests.mm; sourceTree = ""; }; 35CE61801D4165D9004F2359 /* UIColor+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIColor+MGLAdditions.h"; sourceTree = ""; }; 35CE61811D4165D9004F2359 /* UIColor+MGLAdditions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "UIColor+MGLAdditions.mm"; sourceTree = ""; }; 35D13AB51D3D15E300AFB4E0 /* MGLStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLStyleLayer.h; sourceTree = ""; }; 35D13AB61D3D15E300AFB4E0 /* MGLStyleLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLStyleLayer.mm; sourceTree = ""; }; 35D13AC11D3D19DD00AFB4E0 /* MGLFillStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLFillStyleLayer.h; sourceTree = ""; }; 35D13AC21D3D19DD00AFB4E0 /* MGLFillStyleLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLFillStyleLayer.mm; sourceTree = ""; }; + 35D9DDE11DA25EEC00DAAD69 /* MGLCodingTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLCodingTests.m; path = ../../darwin/test/MGLCodingTests.m; sourceTree = ""; }; 35E0CFE51D3E501500188327 /* MGLStyle_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLStyle_Private.h; sourceTree = ""; }; 35E1A4D71D74336F007AA97F /* MGLValueEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLValueEvaluator.h; sourceTree = ""; }; 35E208A61D24210F00EC9A46 /* MGLNSDataAdditionsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLNSDataAdditionsTests.m; sourceTree = ""; }; @@ -587,10 +591,6 @@ 4018B1C41CDC277F00F666AF /* MGLAnnotationView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLAnnotationView.mm; sourceTree = ""; }; 4018B1C51CDC277F00F666AF /* MGLAnnotationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAnnotationView.h; sourceTree = ""; }; 402E9DE01CD2C76200FD4519 /* Mapbox.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = Mapbox.playground; sourceTree = ""; }; - 4032C5B71DE1EBB90062E8BD /* NSValue+MGLStyleEnumAttributeAdditions.h.ejs */ = {isa = PBXFileReference; lastKnownFileType = text; path = "NSValue+MGLStyleEnumAttributeAdditions.h.ejs"; sourceTree = ""; }; - 4032C5B81DE1EE7D0062E8BD /* NSValue+MGLStyleEnumAttributeAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSValue+MGLStyleEnumAttributeAdditions.h"; sourceTree = ""; }; - 4032C5BC1DE1FAFC0062E8BD /* NSValue+MGLStyleEnumAttributeAdditions.mm.ejs */ = {isa = PBXFileReference; lastKnownFileType = text; path = "NSValue+MGLStyleEnumAttributeAdditions.mm.ejs"; sourceTree = ""; }; - 4032C5BD1DE1FC690062E8BD /* NSValue+MGLStyleEnumAttributeAdditions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSValue+MGLStyleEnumAttributeAdditions.mm"; sourceTree = ""; }; 404326881D5B9B1A007111BD /* MGLAnnotationContainerView_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLAnnotationContainerView_Private.h; sourceTree = ""; }; 4049C29B1DB6CD6C00B3F799 /* MGLPointCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLPointCollection.h; sourceTree = ""; }; 4049C29C1DB6CD6C00B3F799 /* MGLPointCollection.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLPointCollection.mm; sourceTree = ""; }; @@ -616,6 +616,7 @@ 556660D71E1D085500E2C41B /* MGLVersionNumber.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = MGLVersionNumber.m; path = ../../darwin/test/MGLVersionNumber.m; sourceTree = ""; }; 55D8C9941D0F133500F42F10 /* config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = config.xcconfig; path = ../../build/ios/config.xcconfig; sourceTree = ""; }; 55D8C9951D0F18CE00F42F10 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; }; + 6407D66F1E0085FD00F6A9C3 /* MGLDocumentationExampleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MGLDocumentationExampleTests.swift; path = ../../darwin/test/MGLDocumentationExampleTests.swift; sourceTree = ""; }; 7E016D7C1D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MGLPolyline+MGLAdditions.h"; sourceTree = ""; }; 7E016D7D1D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MGLPolyline+MGLAdditions.m"; sourceTree = ""; }; 7E016D821D9E890300A29A21 /* MGLPolygon+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MGLPolygon+MGLAdditions.h"; sourceTree = ""; }; @@ -667,6 +668,8 @@ DA35A2C71CCAAAD200E826B2 /* NSValue+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSValue+MGLAdditions.h"; sourceTree = ""; }; DA35A2C81CCAAAD200E826B2 /* NSValue+MGLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSValue+MGLAdditions.m"; sourceTree = ""; }; DA35A2D11CCAB25200E826B2 /* jazzy.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = jazzy.yml; sourceTree = ""; }; + DA35D0871E1A6309007DED41 /* one-liner.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = "one-liner.json"; path = "../../darwin/test/one-liner.json"; sourceTree = ""; }; + DA3C6FF21E2859E700F962BE /* test-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "test-Bridging-Header.h"; path = "../../darwin/test/test-Bridging-Header.h"; sourceTree = ""; }; DA4A26961CB6E795000B7809 /* Mapbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Mapbox.framework; sourceTree = BUILT_PRODUCTS_DIR; }; DA6408D91DA4E7D300908C90 /* MGLVectorStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLVectorStyleLayer.h; sourceTree = ""; }; DA6408DA1DA4E7D300908C90 /* MGLVectorStyleLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLVectorStyleLayer.m; sourceTree = ""; }; @@ -767,7 +770,7 @@ DA8963341CC549A100684375 /* sprites */ = {isa = PBXFileReference; lastKnownFileType = folder; path = sprites; sourceTree = ""; }; DA8963351CC549A100684375 /* styles */ = {isa = PBXFileReference; lastKnownFileType = folder; path = styles; sourceTree = ""; }; DA8963361CC549A100684375 /* tiles */ = {isa = PBXFileReference; lastKnownFileType = folder; path = tiles; sourceTree = ""; }; - DA8F25B91D51D2570010E6B5 /* MGLStyleLayerTests.m.ejs */ = {isa = PBXFileReference; lastKnownFileType = text; name = MGLStyleLayerTests.m.ejs; path = ../test/MGLStyleLayerTests.m.ejs; sourceTree = ""; }; + DA8F25B91D51D2570010E6B5 /* MGLStyleLayerTests.mm.ejs */ = {isa = PBXFileReference; lastKnownFileType = text; name = MGLStyleLayerTests.mm.ejs; path = ../test/MGLStyleLayerTests.mm.ejs; sourceTree = ""; }; DA8F25BA1D51D2570010E6B5 /* MGLStyleLayer.h.ejs */ = {isa = PBXFileReference; lastKnownFileType = text; path = MGLStyleLayer.h.ejs; sourceTree = ""; }; DA8F25BB1D51D2570010E6B5 /* MGLStyleLayer.mm.ejs */ = {isa = PBXFileReference; lastKnownFileType = text; path = MGLStyleLayer.mm.ejs; sourceTree = ""; }; DAA4E4021CBB5C2F00178DFB /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; @@ -794,6 +797,7 @@ DAD1656B1CF41981001FF4B9 /* MGLFeature.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLFeature.mm; sourceTree = ""; }; DAD165761CF4CDFF001FF4B9 /* MGLShapeCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLShapeCollection.h; sourceTree = ""; }; DAD165771CF4CDFF001FF4B9 /* MGLShapeCollection.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLShapeCollection.mm; sourceTree = ""; }; + DAE7DEC11E245455007505A6 /* MGLNSStringAdditionsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLNSStringAdditionsTests.m; path = ../../darwin/test/MGLNSStringAdditionsTests.m; sourceTree = ""; }; DAED38611D62D0FC00D7640F /* NSURL+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSURL+MGLAdditions.h"; sourceTree = ""; }; DAED38621D62D0FC00D7640F /* NSURL+MGLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSURL+MGLAdditions.m"; sourceTree = ""; }; DAEDC4331D603417000224FF /* MGLAttributionInfoTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLAttributionInfoTests.m; path = ../../darwin/test/MGLAttributionInfoTests.m; sourceTree = ""; }; @@ -923,8 +927,6 @@ 35599DB81D46AD7F0048254D /* Categories */ = { isa = PBXGroup; children = ( - 4032C5B81DE1EE7D0062E8BD /* NSValue+MGLStyleEnumAttributeAdditions.h */, - 4032C5BD1DE1FC690062E8BD /* NSValue+MGLStyleEnumAttributeAdditions.mm */, 350098DA1D484E60004B2AF0 /* NSValue+MGLStyleAttributeAdditions.h */, 350098DB1D484E60004B2AF0 /* NSValue+MGLStyleAttributeAdditions.mm */, ); @@ -935,11 +937,10 @@ isa = PBXGroup; children = ( 3575798F1D513EF1000B822E /* Layers */, - 35B8E08B1D6C8B5100E768D2 /* MGLFilterTests.mm */, + 40CFA64E1D78754A008103BD /* Sources */, 357F09091DF84F3800941873 /* MGLStyleValueTests.h */, 3599A3E51DF708BC00E77FB2 /* MGLStyleValueTests.m */, DA2207BE1DC0805F0002F84D /* MGLStyleValueTests.swift */, - 40CFA64E1D78754A008103BD /* Sources */, ); name = Styling; sourceTree = ""; @@ -949,14 +950,13 @@ children = ( DA2DBBCC1D51E80400D38FF9 /* MGLStyleLayerTests.h */, DA2DBBCD1D51E80400D38FF9 /* MGLStyleLayerTests.m */, - 3575797F1D501E09000B822E /* MGLFillStyleLayerTests.m */, - 357579821D502AE6000B822E /* MGLRasterStyleLayerTests.m */, - 357579841D502AF5000B822E /* MGLSymbolStyleLayerTests.m */, - 357579861D502AFE000B822E /* MGLLineStyleLayerTests.m */, - 357579881D502B06000B822E /* MGLCircleStyleLayerTests.m */, - 3575798A1D502B0C000B822E /* MGLBackgroundStyleLayerTests.m */, - 3575798C1D502EC7000B822E /* MGLRuntimeStylingHelper.h */, - 3575798D1D502EC7000B822E /* MGLRuntimeStylingHelper.m */, + DA3C6FF21E2859E700F962BE /* test-Bridging-Header.h */, + 3575797F1D501E09000B822E /* MGLFillStyleLayerTests.mm */, + 357579821D502AE6000B822E /* MGLRasterStyleLayerTests.mm */, + 357579841D502AF5000B822E /* MGLSymbolStyleLayerTests.mm */, + 357579861D502AFE000B822E /* MGLLineStyleLayerTests.mm */, + 357579881D502B06000B822E /* MGLCircleStyleLayerTests.mm */, + 3575798A1D502B0C000B822E /* MGLBackgroundStyleLayerTests.mm */, ); name = Layers; sourceTree = ""; @@ -968,6 +968,8 @@ 35CE61811D4165D9004F2359 /* UIColor+MGLAdditions.mm */, 30E578111DAA7D690050F07E /* UIImage+MGLAdditions.h */, 30E578121DAA7D690050F07E /* UIImage+MGLAdditions.mm */, + 357FE2DB1E02D2B20068B753 /* NSCoder+MGLAdditions.h */, + 357FE2DC1E02D2B20068B753 /* NSCoder+MGLAdditions.mm */, ); name = Categories; sourceTree = ""; @@ -1092,19 +1094,24 @@ DAEDC4331D603417000224FF /* MGLAttributionInfoTests.m */, 353D23951D0B0DFE002BE09D /* MGLAnnotationViewTests.m */, DA35A2C31CCA9F8300E826B2 /* MGLClockDirectionFormatterTests.m */, + 35D9DDE11DA25EEC00DAAD69 /* MGLCodingTests.m */, DA35A2C41CCA9F8300E826B2 /* MGLCompassDirectionFormatterTests.m */, DA35A2A91CCA058D00E826B2 /* MGLCoordinateFormatterTests.m */, + 6407D66F1E0085FD00F6A9C3 /* MGLDocumentationExampleTests.swift */, + DD58A4C51D822BD000E1F038 /* MGLExpressionTests.mm */, DA0CD58F1CF56F6A00A5F5A5 /* MGLFeatureTests.mm */, DA2E885C1CC0382C00F24E7B /* MGLGeometryTests.mm */, 35E208A61D24210F00EC9A46 /* MGLNSDataAdditionsTests.m */, + DAE7DEC11E245455007505A6 /* MGLNSStringAdditionsTests.m */, DA2E885D1CC0382C00F24E7B /* MGLOfflinePackTests.m */, DA2E885E1CC0382C00F24E7B /* MGLOfflineRegionTests.m */, DA2E885F1CC0382C00F24E7B /* MGLOfflineStorageTests.m */, + 35B8E08B1D6C8B5100E768D2 /* MGLPredicateTests.mm */, DA2E88601CC0382C00F24E7B /* MGLStyleTests.mm */, - DD58A4C51D822BD000E1F038 /* MGLExpressionTests.mm */, 556660D71E1D085500E2C41B /* MGLVersionNumber.m */, DA2E88551CC036F400F24E7B /* Info.plist */, DA2784FB1DF02FF4001D5B8D /* Media.xcassets */, + DA35D0871E1A6309007DED41 /* one-liner.json */, ); name = "SDK Tests"; path = test; @@ -1258,9 +1265,7 @@ children = ( DA8F25BA1D51D2570010E6B5 /* MGLStyleLayer.h.ejs */, DA8F25BB1D51D2570010E6B5 /* MGLStyleLayer.mm.ejs */, - DA8F25B91D51D2570010E6B5 /* MGLStyleLayerTests.m.ejs */, - 4032C5B71DE1EBB90062E8BD /* NSValue+MGLStyleEnumAttributeAdditions.h.ejs */, - 4032C5BC1DE1FAFC0062E8BD /* NSValue+MGLStyleEnumAttributeAdditions.mm.ejs */, + DA8F25B91D51D2570010E6B5 /* MGLStyleLayerTests.mm.ejs */, ); name = "Foundation Templates"; path = ../../darwin/src; @@ -1462,6 +1467,7 @@ 4018B1C91CDC288A00F666AF /* MGLAnnotationView_Private.h in Headers */, 35E1A4D81D74336F007AA97F /* MGLValueEvaluator.h in Headers */, DA88482C1CBAFA6200AB86E3 /* NSBundle+MGLAdditions.h in Headers */, + 357FE2DD1E02D2B20068B753 /* NSCoder+MGLAdditions.h in Headers */, 7E016D7E1D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.h in Headers */, 35D13AB71D3D15E300AFB4E0 /* MGLStyleLayer.h in Headers */, DA88488E1CBB047F00AB86E3 /* reachability.h in Headers */, @@ -1520,7 +1526,6 @@ DA88488B1CBB037E00AB86E3 /* SMCalloutView.h in Headers */, DA8847FE1CBAFA5100AB86E3 /* MGLTypes.h in Headers */, DA8847F11CBAFA5100AB86E3 /* MGLGeometry.h in Headers */, - 4032C5BF1DE1FC780062E8BD /* NSValue+MGLStyleEnumAttributeAdditions.h in Headers */, DA8848221CBAFA6200AB86E3 /* MGLOfflineRegion_Private.h in Headers */, 35136D4C1D4277FC00C20EFD /* MGLSource.h in Headers */, 3566C76C1D4A8DFA008152BC /* MGLRasterSource.h in Headers */, @@ -1564,7 +1569,6 @@ buildActionMask = 2147483647; files = ( 556660CA1E1BF3A900E2C41B /* MGLFoundation.h in Headers */, - 4032C5C01DE1FC780062E8BD /* NSValue+MGLStyleEnumAttributeAdditions.h in Headers */, 35B82BF91D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h in Headers */, DA35A2CA1CCAAAD200E826B2 /* NSValue+MGLAdditions.h in Headers */, 350098BC1D480108004B2AF0 /* MGLVectorSource.h in Headers */, @@ -1617,6 +1621,7 @@ DA35A2B21CCA141D00E826B2 /* MGLCompassDirectionFormatter.h in Headers */, DAF0D8141DFE0EC500B28378 /* MGLVectorSource_Private.h in Headers */, DABFB8731CBE9A9900D62B32 /* Mapbox.h in Headers */, + 357FE2DE1E02D2B20068B753 /* NSCoder+MGLAdditions.h in Headers */, 354B83971D2E873E005D9406 /* MGLUserLocationAnnotationView.h in Headers */, DAF0D8111DFE0EA000B28378 /* MGLRasterSource_Private.h in Headers */, DABFB86B1CBE99E500D62B32 /* MGLTilePyramidOfflineRegion.h in Headers */, @@ -1779,16 +1784,19 @@ TargetAttributes = { DA1DC9491CB6C1C2006E619F = { CreatedOnToolsVersion = 7.3; + DevelopmentTeam = GJZR2MEM28; + LastSwiftMigration = 0820; }; DA25D5B81CCD9EDE00607828 = { CreatedOnToolsVersion = 7.3; }; DA2E88501CC036F400F24E7B = { CreatedOnToolsVersion = 7.3; - LastSwiftMigration = 0800; + LastSwiftMigration = 0820; }; DA8847D11CBAF91600AB86E3 = { CreatedOnToolsVersion = 7.3; + LastSwiftMigration = 0820; }; DA8933D41CCD306400E68420 = { CreatedOnToolsVersion = 7.3; @@ -1861,6 +1869,7 @@ files = ( DA2784FC1DF02FF4001D5B8D /* Media.xcassets in Resources */, 353BAEF71D646370009A8DA9 /* amsterdam.geojson in Resources */, + DA35D0881E1A6309007DED41 /* one-liner.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1945,29 +1954,31 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 6407D6701E0085FD00F6A9C3 /* MGLDocumentationExampleTests.swift in Sources */, DA2E88631CC0382C00F24E7B /* MGLOfflineRegionTests.m in Sources */, 3599A3E61DF708BC00E77FB2 /* MGLStyleValueTests.m in Sources */, DA2E88651CC0382C00F24E7B /* MGLStyleTests.mm in Sources */, DA2E88611CC0382C00F24E7B /* MGLGeometryTests.mm in Sources */, - 357579801D501E09000B822E /* MGLFillStyleLayerTests.m in Sources */, + 357579801D501E09000B822E /* MGLFillStyleLayerTests.mm in Sources */, + 35D9DDE21DA25EEC00DAAD69 /* MGLCodingTests.m in Sources */, DA2E88641CC0382C00F24E7B /* MGLOfflineStorageTests.m in Sources */, DA2DBBCE1D51E80400D38FF9 /* MGLStyleLayerTests.m in Sources */, DA35A2C61CCA9F8300E826B2 /* MGLCompassDirectionFormatterTests.m in Sources */, - 3575798E1D502EC7000B822E /* MGLRuntimeStylingHelper.m in Sources */, + DAE7DEC21E245455007505A6 /* MGLNSStringAdditionsTests.m in Sources */, 4085AF091D933DEA00F11B22 /* MGLTileSetTests.mm in Sources */, DAEDC4341D603417000224FF /* MGLAttributionInfoTests.m in Sources */, - 357579851D502AF5000B822E /* MGLSymbolStyleLayerTests.m in Sources */, - 357579871D502AFE000B822E /* MGLLineStyleLayerTests.m in Sources */, - 357579891D502B06000B822E /* MGLCircleStyleLayerTests.m in Sources */, + 357579851D502AF5000B822E /* MGLSymbolStyleLayerTests.mm in Sources */, + 357579871D502AFE000B822E /* MGLLineStyleLayerTests.mm in Sources */, + 357579891D502B06000B822E /* MGLCircleStyleLayerTests.mm in Sources */, DA2207BF1DC0805F0002F84D /* MGLStyleValueTests.swift in Sources */, 40CFA6511D7875BB008103BD /* MGLShapeSourceTests.mm in Sources */, DA35A2C51CCA9F8300E826B2 /* MGLClockDirectionFormatterTests.m in Sources */, - 35B8E08C1D6C8B5100E768D2 /* MGLFilterTests.mm in Sources */, + 35B8E08C1D6C8B5100E768D2 /* MGLPredicateTests.mm in Sources */, DD58A4C61D822BD000E1F038 /* MGLExpressionTests.mm in Sources */, - 3575798B1D502B0C000B822E /* MGLBackgroundStyleLayerTests.m in Sources */, + 3575798B1D502B0C000B822E /* MGLBackgroundStyleLayerTests.mm in Sources */, DA2E88621CC0382C00F24E7B /* MGLOfflinePackTests.m in Sources */, DA35A2AA1CCA058D00E826B2 /* MGLCoordinateFormatterTests.m in Sources */, - 357579831D502AE6000B822E /* MGLRasterStyleLayerTests.m in Sources */, + 357579831D502AE6000B822E /* MGLRasterStyleLayerTests.mm in Sources */, 353D23961D0B0DFE002BE09D /* MGLAnnotationViewTests.m in Sources */, 35E208A71D24210F00EC9A46 /* MGLNSDataAdditionsTests.m in Sources */, DA0CD5901CF56F6A00A5F5A5 /* MGLFeatureTests.mm in Sources */, @@ -2021,6 +2032,7 @@ DA35A2A11CC9E95F00E826B2 /* MGLCoordinateFormatter.m in Sources */, 35305D481D22AA680007D005 /* NSData+MGLAdditions.mm in Sources */, DA8848291CBAFA6200AB86E3 /* MGLStyle.mm in Sources */, + 357FE2DF1E02D2B20068B753 /* NSCoder+MGLAdditions.mm in Sources */, DA88481C1CBAFA6200AB86E3 /* MGLGeometry.mm in Sources */, 3510FFF21D6D9D8C00F413B2 /* NSExpression+MGLAdditions.mm in Sources */, DA88481F1CBAFA6200AB86E3 /* MGLMultiPoint.mm in Sources */, @@ -2035,7 +2047,6 @@ DA8848301CBAFA6200AB86E3 /* NSProcessInfo+MGLAdditions.m in Sources */, 353AFA161D65AB17005A69F4 /* NSDate+MGLAdditions.mm in Sources */, 35D13AC51D3D19DD00AFB4E0 /* MGLFillStyleLayer.mm in Sources */, - 4032C5C11DE1FC7E0062E8BD /* NSValue+MGLStyleEnumAttributeAdditions.mm in Sources */, DA8848241CBAFA6200AB86E3 /* MGLOfflineStorage.mm in Sources */, DA88482A1CBAFA6200AB86E3 /* MGLTilePyramidOfflineRegion.mm in Sources */, 4049C29F1DB6CD6C00B3F799 /* MGLPointCollection.mm in Sources */, @@ -2096,6 +2107,7 @@ DAA4E4281CBB730400178DFB /* MGLTypes.m in Sources */, DA35A2A21CC9E95F00E826B2 /* MGLCoordinateFormatter.m in Sources */, 35305D491D22AA680007D005 /* NSData+MGLAdditions.mm in Sources */, + 357FE2E01E02D2B20068B753 /* NSCoder+MGLAdditions.mm in Sources */, DAA4E42D1CBB730400178DFB /* MGLAnnotationImage.m in Sources */, 3510FFF31D6D9D8C00F413B2 /* NSExpression+MGLAdditions.mm in Sources */, DAA4E4301CBB730400178DFB /* MGLLocationManager.m in Sources */, @@ -2110,7 +2122,6 @@ DAA4E4231CBB730400178DFB /* MGLPolygon.mm in Sources */, 353AFA171D65AB17005A69F4 /* NSDate+MGLAdditions.mm in Sources */, 35D13AC61D3D19DD00AFB4E0 /* MGLFillStyleLayer.mm in Sources */, - 4032C5C21DE1FC7E0062E8BD /* NSValue+MGLStyleEnumAttributeAdditions.mm in Sources */, DAA4E42A1CBB730400178DFB /* NSProcessInfo+MGLAdditions.m in Sources */, DAA4E4211CBB730400178DFB /* MGLOfflineStorage.mm in Sources */, 4049C2A01DB6CD6C00B3F799 /* MGLPointCollection.mm in Sources */, @@ -2333,6 +2344,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.MapboxGL; PRODUCT_NAME = "Mapbox GL"; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -2345,6 +2357,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.MapboxGL; PRODUCT_NAME = "Mapbox GL"; + SWIFT_VERSION = 3.0; }; name = Release; }; @@ -2441,6 +2454,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.sdk.ios; PRODUCT_NAME = Mapbox; SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = "__attribute__((visibility (\"default\"))) "; }; @@ -2474,6 +2488,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.sdk.ios; PRODUCT_NAME = Mapbox; SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = "__attribute__((visibility (\"default\"))) "; }; diff --git a/platform/ios/jazzy.yml b/platform/ios/jazzy.yml index 2f62cfa30..71f6be37f 100644 --- a/platform/ios/jazzy.yml +++ b/platform/ios/jazzy.yml @@ -15,63 +15,76 @@ umbrella_header: src/Mapbox.h framework_root: ../darwin/src custom_categories: + - name: Guides + children: + - Adding Points to a Map + - Runtime Styling + - Working with Mapbox Studio + - Working with GeoJSON Data + - For Style Authors + - Info.plist Keys - name: Maps children: - MGLAccountManager - MGLMapCamera - MGLMapView - MGLMapViewDelegate - - MGLStyle - MGLUserTrackingMode - - name: Annotations + - name: Primitive Shapes children: - MGLAnnotation - - MGLAnnotationImage - - MGLAnnotationView - - MGLCalloutView - - MGLCalloutViewDelegate + - MGLOverlay + - MGLShape - MGLMultiPoint - - MGLMultiPolygon - - MGLMultiPolyline - MGLPointAnnotation - MGLPointCollection - MGLPolygon - MGLPolyline - - MGLOverlay - - MGLShape + - MGLMultiPolygon + - MGLMultiPolyline - MGLShapeCollection + - name: Annotations + children: + - MGLAnnotationImage + - MGLAnnotationView - MGLUserLocation - MGLUserLocationAnnotationView - - name: Map Data + - name: User Interaction + children: + - MGLCalloutView + - MGLCalloutViewDelegate + - name: Styling the Map + children: + - MGLStyle + - MGLStyleValue + - name: Style Primitives children: - MGLFeature - - MGLMultiPolygonFeature - - MGLMultiPolylineFeature - - MGLPointCollectionFeature - MGLPointFeature - MGLPolygonFeature - MGLPolylineFeature + - MGLMultiPolygonFeature + - MGLMultiPolylineFeature + - MGLPointCollectionFeature - MGLShapeCollectionFeature + - name: Style Content + children: + - MGLSource + - MGLTileSource + - MGLShapeSource + - MGLRasterSource + - MGLVectorSource - name: Style Layers children: + - MGLStyleLayer + - MGLForegroundStyleLayer - MGLBackgroundStyleLayer + - MGLRasterStyleLayer + - MGLVectorStyleLayer - MGLCircleStyleLayer - MGLFillStyleLayer - - MGLForegroundStyleLayer - MGLLineStyleLayer - - MGLRasterStyleLayer - - MGLStyleLayer - MGLSymbolStyleLayer - - MGLVectorStyleLayer - - name: Content Sources - children: - - MGLAttributionInfo - - MGLRasterSource - - MGLShapeSource - - MGLSource - - MGLTileCoordinateSystem - - MGLTileSource - - MGLVectorSource - name: Offline Maps children: - MGLOfflineRegion @@ -82,15 +95,13 @@ custom_categories: - MGLTilePyramidOfflineRegion - name: Geometry children: - - MGLClockDirectionFormatter - - MGLCompassDirectionFormatter - MGLCoordinateBounds - MGLCoordinateBoundsEqualToCoordinateBounds - MGLCoordinateBoundsGetCoordinateSpan + - MGLCoordinateBoundsIntersectsCoordinateBounds - MGLCoordinateBoundsIsEmpty - MGLCoordinateBoundsMake - MGLCoordinateBoundsOffset - - MGLCoordinateFormatter - MGLCoordinateInCoordinateBounds - MGLCoordinateSpan - MGLCoordinateSpanEqualToCoordinateSpan @@ -99,3 +110,8 @@ custom_categories: - MGLDegreesFromRadians - MGLRadiansFromDegrees - MGLStringFromCoordinateBounds + - name: Formatters + children: + - MGLClockDirectionFormatter + - MGLCompassDirectionFormatter + - MGLCoordinateFormatter diff --git a/platform/ios/scripts/deploy-packages.sh b/platform/ios/scripts/deploy-packages.sh index 8ad5e7abb..2265afdba 100755 --- a/platform/ios/scripts/deploy-packages.sh +++ b/platform/ios/scripts/deploy-packages.sh @@ -44,7 +44,7 @@ buildPackageStyle() { --tag "ios-v${PUBLISH_VERSION}" \ --name ${file_name} \ --file "${BINARY_DIRECTORY}/${file_name}" > /dev/null - fi + fi } export TRAVIS_REPO_SLUG=mapbox-gl-native diff --git a/platform/ios/scripts/document.sh b/platform/ios/scripts/document.sh index fddf5abb0..d66742a33 100755 --- a/platform/ios/scripts/document.sh +++ b/platform/ios/scripts/document.sh @@ -30,7 +30,7 @@ sed -n -e '/^## /{' -e ':a' -e 'n' -e '/^## /q' -e 'p' -e 'ba' -e '}' platform/i rm -rf ${OUTPUT} mkdir -p ${OUTPUT} -cp platform/ios/screenshot.png "${OUTPUT}" +cp -r platform/ios/docs/img "${OUTPUT}/img" DEFAULT_THEME="platform/darwin/docs/theme" THEME=${JAZZY_THEME:-$DEFAULT_THEME} @@ -41,7 +41,7 @@ jazzy \ --github-file-prefix https://github.com/mapbox/mapbox-gl-native/tree/${BRANCH} \ --module-version ${SHORT_VERSION} \ --readme ${README} \ - --documentation="platform/ios/docs/Info.plist Keys.md" \ + --documentation="platform/{darwin,ios}/docs/guides/*.md" \ --root-url https://www.mapbox.com/ios-sdk/api/${RELEASE_VERSION}/ \ --theme ${THEME} \ --output ${OUTPUT} diff --git a/platform/ios/src/MGLAPIClient.h b/platform/ios/src/MGLAPIClient.h index 0f8926d36..4e5ea3b5e 100644 --- a/platform/ios/src/MGLAPIClient.h +++ b/platform/ios/src/MGLAPIClient.h @@ -9,7 +9,6 @@ NS_ASSUME_NONNULL_BEGIN - (void)postEvents:(NS_ARRAY_OF(MGLMapboxEventAttributes *) *)events completionHandler:(nullable void (^)(NSError * _Nullable error))completionHandler; - (void)postEvent:(MGLMapboxEventAttributes *)event completionHandler:(nullable void (^)(NSError * _Nullable error))completionHandler; -- (void)cancelAll; @end diff --git a/platform/ios/src/MGLAPIClient.m b/platform/ios/src/MGLAPIClient.m index 7fb6538e5..5e8ee5fe1 100644 --- a/platform/ios/src/MGLAPIClient.m +++ b/platform/ios/src/MGLAPIClient.m @@ -21,7 +21,6 @@ static NSString * const MGLAPIClientHTTPMethodPost = @"POST"; @property (nonatomic, copy) NSData *geoTrustCert; @property (nonatomic, copy) NSData *testServerCert; @property (nonatomic, copy) NSString *userAgent; -@property (nonatomic) NSMutableArray *dataTasks; @property (nonatomic) BOOL usesTestServer; @end @@ -33,7 +32,6 @@ static NSString * const MGLAPIClientHTTPMethodPost = @"POST"; if (self) { _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:nil]; - _dataTasks = [NSMutableArray array]; [self loadCertificates]; [self setupBaseURL]; [self setupUserAgent]; @@ -59,24 +57,15 @@ static NSString * const MGLAPIClientHTTPMethodPost = @"POST"; error = error ?: statusError; completionHandler(error); } - [self.dataTasks removeObject:dataTask]; dataTask = nil; }]; [dataTask resume]; - if (dataTask) { - [self.dataTasks addObject:dataTask]; - } } - (void)postEvent:(nonnull MGLMapboxEventAttributes *)event completionHandler:(nullable void (^)(NSError * _Nullable error))completionHandler { [self postEvents:@[event] completionHandler:completionHandler]; } -- (void)cancelAll { - [self.dataTasks makeObjectsPerformSelector:@selector(cancel)]; - [self.dataTasks removeAllObjects]; -} - #pragma mark Utilities - (NSURLRequest *)requestForEvents:(NS_ARRAY_OF(MGLMapboxEventAttributes *) *)events { diff --git a/platform/ios/src/MGLAnnotationImage.h b/platform/ios/src/MGLAnnotationImage.h index a7003d7f9..95bce21f5 100644 --- a/platform/ios/src/MGLAnnotationImage.h +++ b/platform/ios/src/MGLAnnotationImage.h @@ -8,7 +8,7 @@ NS_ASSUME_NONNULL_BEGIN objects and may be recycled later and put into a reuse queue that is maintained by the map view. */ -@interface MGLAnnotationImage : NSObject +@interface MGLAnnotationImage : NSObject #pragma mark Initializing and Preparing the Image Object diff --git a/platform/ios/src/MGLAnnotationImage.m b/platform/ios/src/MGLAnnotationImage.m index e1085be98..9c9c175ab 100644 --- a/platform/ios/src/MGLAnnotationImage.m +++ b/platform/ios/src/MGLAnnotationImage.m @@ -30,6 +30,41 @@ return self; } ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (instancetype)initWithCoder:(NSCoder *)decoder { + if (self = [super init]) { + _image = [decoder decodeObjectOfClass:[UIImage class] forKey:@"image"]; + _reuseIdentifier = [decoder decodeObjectOfClass:[NSString class] forKey:@"reuseIdentifier"]; + _enabled = [decoder decodeBoolForKey:@"enabled"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [coder encodeObject:_image forKey:@"image"]; + [coder encodeObject:_reuseIdentifier forKey:@"reuseIdentifier"]; + [coder encodeBool:_enabled forKey:@"enabled"]; +} + +- (BOOL)isEqual:(id)other { + if (self == other) return YES; + if (![other isKindOfClass:[MGLAnnotationImage class]]) return NO; + + MGLAnnotationImage *otherAnnotationImage = other; + + return ((!_reuseIdentifier && !otherAnnotationImage.reuseIdentifier) + || [_reuseIdentifier isEqualToString:otherAnnotationImage.reuseIdentifier]) + && _enabled == otherAnnotationImage.enabled + && (_image == otherAnnotationImage.image || [UIImagePNGRepresentation(_image) isEqualToData:UIImagePNGRepresentation(otherAnnotationImage.image)]); +} + +- (NSUInteger)hash { + return _reuseIdentifier.hash + _enabled + _image.hash; +} + - (void)setImage:(UIImage *)image { _image = image; [self.delegate annotationImageNeedsRedisplay:self]; diff --git a/platform/ios/src/MGLAnnotationView.h b/platform/ios/src/MGLAnnotationView.h index 634e9ad72..d159976a4 100644 --- a/platform/ios/src/MGLAnnotationView.h +++ b/platform/ios/src/MGLAnnotationView.h @@ -50,7 +50,7 @@ typedef NS_ENUM(NSUInteger, MGLAnnotationViewDragState) { interactivity such as dragging, you can use an `MGLAnnotationImage` instead to conserve memory and optimize drawing performance. */ -@interface MGLAnnotationView : UIView +@interface MGLAnnotationView : UIView #pragma mark Initializing and Preparing the View diff --git a/platform/ios/src/MGLAnnotationView.mm b/platform/ios/src/MGLAnnotationView.mm index 96ed8c733..d2243bdf2 100644 --- a/platform/ios/src/MGLAnnotationView.mm +++ b/platform/ios/src/MGLAnnotationView.mm @@ -33,6 +33,34 @@ return self; } ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (instancetype)initWithCoder:(NSCoder *)decoder { + if (self = [super initWithCoder:decoder]) { + _reuseIdentifier = [decoder decodeObjectOfClass:[NSString class] forKey:@"reuseIdentifier"]; + _annotation = [decoder decodeObjectOfClass:[NSObject class] forKey:@"annotation"]; + _centerOffset = [decoder decodeCGVectorForKey:@"centerOffset"]; + _scalesWithViewingDistance = [decoder decodeBoolForKey:@"scalesWithViewingDistance"]; + _selected = [decoder decodeBoolForKey:@"selected"]; + _enabled = [decoder decodeBoolForKey:@"enabled"]; + self.draggable = [decoder decodeBoolForKey:@"draggable"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [super encodeWithCoder:coder]; + [coder encodeObject:_reuseIdentifier forKey:@"reuseIdentifier"]; + [coder encodeObject:_annotation forKey:@"annotation"]; + [coder encodeCGVector:_centerOffset forKey:@"centerOffset"]; + [coder encodeBool:_scalesWithViewingDistance forKey:@"scalesWithViewingDistance"]; + [coder encodeBool:_selected forKey:@"selected"]; + [coder encodeBool:_enabled forKey:@"enabled"]; + [coder encodeBool:_draggable forKey:@"draggable"]; +} + - (void)prepareForReuse { // Intentionally left blank. The default implementation of this method does nothing. diff --git a/platform/ios/src/MGLCalloutView.h b/platform/ios/src/MGLCalloutView.h index 6918aad61..4dc9a25be 100644 --- a/platform/ios/src/MGLCalloutView.h +++ b/platform/ios/src/MGLCalloutView.h @@ -46,6 +46,24 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)dismissCalloutAnimated:(BOOL)animated; +@optional + +/** + A Boolean value indicating whether the callout view should be anchored to + the corresponding annotation. You can adjust the callout view’s precise location by + overriding -[UIView setCenter:]. The callout view will not be anchored to the + annotation if this optional property is unimplemented. + */ +@property (nonatomic, readonly, assign, getter=isAnchoredToAnnotation) BOOL anchoredToAnnotation; + +/** + A Boolean value indicating whether the callout view should be dismissed automatically + when the map view’s viewport changes. Note that a single tap on the map view + still dismisses the callout view regardless of the value of this property. + The callout view will be dismissed if this optional property is unimplemented. + */ +@property (nonatomic, readonly, assign) BOOL dismissesAutomatically; + @end /** diff --git a/platform/ios/src/MGLCompactCalloutView.m b/platform/ios/src/MGLCompactCalloutView.m index 49812c51a..3d2118ca3 100644 --- a/platform/ios/src/MGLCompactCalloutView.m +++ b/platform/ios/src/MGLCompactCalloutView.m @@ -14,6 +14,14 @@ return [[self alloc] init]; } +- (BOOL)isAnchoredToAnnotation { + return YES; +} + +- (BOOL)dismissesAutomatically { + return NO; +} + - (void)setRepresentedObject:(id )representedObject { _representedObject = representedObject; diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h index 771a48c7f..62f053e96 100644 --- a/platform/ios/src/MGLMapView.h +++ b/platform/ios/src/MGLMapView.h @@ -123,6 +123,13 @@ IB_DESIGNABLE Unlike the `styleURL` property, this property is set to an object that allows you to manipulate every aspect of the style locally. + If the style is loading, this property is set to `nil` until the style finishes + loading. If the style has failed to load, this property is set to `nil`. + Because the style loads asynchronously, you should manipulate it in the + `-[MGLMapViewDelegate mapView:didFinishLoadingStyle:]` or + `-[MGLMapViewDelegate mapViewDidFinishLoadingMap:]` method. It is not possible + to manipulate the style before it has finished loading. + @note The default styles provided by Mapbox contain sources and layers with identifiers that will change over time. Applications that use APIs that manipulate a style's sources and layers must first set the style URL to an @@ -130,7 +137,7 @@ IB_DESIGNABLE `+[MGLStyle outdoorsStyleURLWithVersion:]`, `MGLMapView`'s “Style URL” inspectable in Interface Builder, or a manually constructed `NSURL`. */ -@property (nonatomic, readonly) MGLStyle *style; +@property (nonatomic, readonly, nullable) MGLStyle *style; /** URLs of the styles bundled with the library. diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 608acf016..c211d4419 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -7,6 +7,7 @@ #import #import +#include #include #include #include @@ -347,6 +348,11 @@ public: return self; } ++ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingStyle +{ + return [NSSet setWithObject:@"styleURL"]; +} + + (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingStyleURL { return [NSSet setWithObjects:@"styleURL__", nil]; @@ -369,10 +375,8 @@ public: } styleURL = styleURL.mgl_URLByStandardizingScheme; - [self willChangeValueForKey:@"style"]; - _style = [[MGLStyle alloc] initWithMapView:self]; + self.style = nil; _mbglMap->setStyleURL([[styleURL absoluteString] UTF8String]); - [self didChangeValueForKey:@"style"]; } - (IBAction)reloadStyle:(__unused id)sender { @@ -1734,7 +1738,7 @@ public: _attributionInfos = [self.style attributionInfosWithFontSize:[UIFont buttonFontSize] linkColor:nil]; for (MGLAttributionInfo *info in _attributionInfos) { - NSString *title = [info.title.string capitalizedStringWithLocale:[NSLocale currentLocale]]; + NSString *title = [info.title.string mgl_titleCasedStringWithLocale:[NSLocale currentLocale]]; [self.attributionSheet addButtonWithTitle:title]; } @@ -3137,23 +3141,9 @@ public: - (double)alphaForShapeAnnotation:(MGLShape *)annotation { - // The explicit -mapView:alphaForShapeAnnotation: delegate method is deprecated - // but still used, if implemented. When not implemented, call the stroke or - // fill color delegate methods and pull the alpha from the returned color. if (_delegateHasAlphasForShapeAnnotations) { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" return [self.delegate mapView:self alphaForShapeAnnotation:annotation]; -#pragma clang diagnostic pop - } - else if ([annotation isKindOfClass:[MGLPolygon class]]) - { - return [self fillColorForPolygonAnnotation:(MGLPolygon *)annotation].a ?: 1.0; - } - else if ([annotation isKindOfClass:[MGLShape class]]) - { - return [self strokeColorForShapeAnnotation:annotation].a ?: 1.0; } return 1.0; } @@ -4566,21 +4556,34 @@ public: || self.userTrackingMode == MGLUserTrackingModeNone || self.userTrackingState != MGLUserTrackingStateChanged) { - // Deselect annotation if it lies outside the viewport - if (self.selectedAnnotation) { - MGLAnnotationTag tag = [self annotationTagForAnnotation:self.selectedAnnotation]; - MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(tag); - MGLAnnotationView *annotationView = annotationContext.annotationView; - - CGRect rect = [self positioningRectForCalloutForAnnotationWithTag:tag]; - - if (annotationView) - { - rect = annotationView.frame; - } - - if ( ! CGRectIntersectsRect(rect, self.frame)) { - [self deselectAnnotation:self.selectedAnnotation animated:NO]; + UIView *calloutView = self.calloutViewForSelectedAnnotation; + BOOL dismissesAutomatically = (calloutView + && [calloutView respondsToSelector:@selector(dismissesAutomatically)] + && calloutView.dismissesAutomatically); + // dismissesAutomatically is an optional property and we want to dismiss + // the callout view if it's unimplemented. + if (dismissesAutomatically || ![calloutView respondsToSelector:@selector(dismissesAutomatically)]) + { + [self deselectAnnotation:self.selectedAnnotation animated:NO]; + } + else + { + // Deselect annotation if it lies outside the viewport + if (self.selectedAnnotation) { + MGLAnnotationTag tag = [self annotationTagForAnnotation:self.selectedAnnotation]; + MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(tag); + MGLAnnotationView *annotationView = annotationContext.annotationView; + + CGRect rect = [self positioningRectForCalloutForAnnotationWithTag:tag]; + + if (annotationView) + { + rect = annotationView.frame; + } + + if ( ! CGRectIntersectsRect(rect, self.frame)) { + [self deselectAnnotation:self.selectedAnnotation animated:NO]; + } } } } @@ -4690,11 +4693,7 @@ public: } case mbgl::MapChangeDidFinishLoadingStyle: { - [self.style willChangeValueForKey:@"name"]; - [self.style willChangeValueForKey:@"sources"]; - [self.style didChangeValueForKey:@"sources"]; - [self.style willChangeValueForKey:@"layers"]; - [self.style didChangeValueForKey:@"layers"]; + self.style = [[MGLStyle alloc] initWithMapView:self]; if ([self.delegate respondsToSelector:@selector(mapView:didFinishLoadingStyle:)]) { [self.delegate mapView:self didFinishLoadingStyle:self.style]; @@ -4805,7 +4804,12 @@ public: else { CGRect adjustedFrame = annotationView.frame; - adjustedFrame.origin.x = CGRectGetWidth(annotationView.layer.presentationLayer.frame) * -2.0; + if (annotationView.layer.presentationLayer) { + adjustedFrame.origin.x = -CGRectGetWidth(annotationView.layer.presentationLayer.frame) * 10.0; + } else { + // views that are added off screen do not have a presentationLayer + adjustedFrame.origin.x = -CGRectGetWidth(adjustedFrame) * 10.0; + } annotationView.frame = adjustedFrame; [self enqueueAnnotationViewForAnnotationContext:annotationContext]; } @@ -4820,7 +4824,12 @@ public: UIView *calloutView = self.calloutViewForSelectedAnnotation; id annotation = calloutView.representedObject; - if (calloutView && annotation) + BOOL isAnchoredToAnnotation = (calloutView + && annotation + && [calloutView respondsToSelector:@selector(isAnchoredToAnnotation)] + && calloutView.isAnchoredToAnnotation); + + if (isAnchoredToAnnotation) { MGLAnnotationTag tag = [self annotationTagForAnnotation:annotation]; MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(tag); diff --git a/platform/ios/src/MGLMapViewDelegate.h b/platform/ios/src/MGLMapViewDelegate.h index 777af4ba6..09e2465a2 100644 --- a/platform/ios/src/MGLMapViewDelegate.h +++ b/platform/ios/src/MGLMapViewDelegate.h @@ -228,7 +228,22 @@ NS_ASSUME_NONNULL_BEGIN */ - (nullable MGLAnnotationImage *)mapView:(MGLMapView *)mapView imageForAnnotation:(id )annotation; -- (CGFloat)mapView:(MGLMapView *)mapView alphaForShapeAnnotation:(MGLShape *)annotation __attribute__((deprecated("Use -mapView:strokeColorForShapeAnnotation: or -mapView:fillColorForPolygonAnnotation:."))); +/** + Returns the alpha value to use when rendering a shape annotation. + + A value of `0.0` results in a completely transparent shape. A value of `1.0`, + the default, results in a completely opaque shape. + + This method sets the opacity of an entire shape, inclusive of its stroke and + fill. To independently set the values for stroke or fill, specify an alpha + component in the color returned by `-mapView:strokeColorForShapeAnnotation:` or + `-mapView:fillColorForPolygonAnnotation:`. + + @param mapView The map view rendering the shape annotation. + @param annotation The annotation being rendered. + @return An alpha value between `0` and `1.0`. + */ +- (CGFloat)mapView:(MGLMapView *)mapView alphaForShapeAnnotation:(MGLShape *)annotation; /** Returns the color to use when rendering the outline of a shape annotation. diff --git a/platform/ios/src/MGLMapView_Private.h b/platform/ios/src/MGLMapView_Private.h index 6b6a898f9..4e2765377 100644 --- a/platform/ios/src/MGLMapView_Private.h +++ b/platform/ios/src/MGLMapView_Private.h @@ -1,11 +1,13 @@ #import -#import +namespace mbgl { + class Map; +} /// Minimum size of an annotation’s accessibility element. extern const CGSize MGLAnnotationAccessibilityElementMinimumSize; -@interface MGLMapView (Internal) +@interface MGLMapView (Private) /// Currently shown popover representing the selected annotation. @property (nonatomic) UIView *calloutViewForSelectedAnnotation; diff --git a/platform/ios/src/MGLMapboxEvents.m b/platform/ios/src/MGLMapboxEvents.m index 2c25cf0a6..744b80047 100644 --- a/platform/ios/src/MGLMapboxEvents.m +++ b/platform/ios/src/MGLMapboxEvents.m @@ -278,6 +278,7 @@ const NSTimeInterval MGLFlushInterval = 180; if (self.paused && enabled) { [self resumeMetricsCollection]; } else if (!self.paused && !enabled) { + [self flush]; [self pauseMetricsCollection]; } } @@ -434,25 +435,31 @@ const NSTimeInterval MGLFlushInterval = 180; } - (MGLMapboxEventAttributes *)locationEventWithAttributes:(MGLMapboxEventAttributes *)attributeDictionary { - MGLMutableMapboxEventAttributes *attributes = [@{MGLEventKeyEvent: MGLEventTypeLocation, - MGLEventKeySource: MGLEventSource, - MGLEventKeySessionId: self.instanceID, - MGLEventKeyOperatingSystem: self.data.iOSVersion} mutableCopy]; - [self addApplicationStateToAttributes:attributes]; + MGLMutableMapboxEventAttributes *attributes = [NSMutableDictionary dictionary]; + attributes[MGLEventKeyEvent] = MGLEventTypeLocation; + attributes[MGLEventKeySource] = MGLEventSource; + attributes[MGLEventKeySessionId] = self.instanceID; + attributes[MGLEventKeyOperatingSystem] = self.data.iOSVersion; + NSString *currentApplicationState = [self applicationState]; + if (![currentApplicationState isEqualToString:MGLApplicationStateUnknown]) { + attributes[MGLEventKeyApplicationState] = currentApplicationState; + } + return [self eventForAttributes:attributes attributeDictionary:attributeDictionary]; } - (MGLMapboxEventAttributes *)mapLoadEventWithAttributes:(MGLMapboxEventAttributes *)attributeDictionary { - MGLMutableMapboxEventAttributes *attributes = [@{MGLEventKeyEvent: MGLEventTypeMapLoad, - MGLEventKeyCreated: [self.rfc3339DateFormatter stringFromDate:[NSDate date]], - MGLEventKeyVendorID: self.data.vendorId, - MGLEventKeyModel: self.data.model, - MGLEventKeyOperatingSystem: self.data.iOSVersion, - MGLEventKeyResolution: @(self.data.scale), - MGLEventKeyAccessibilityFontScale: @([self contentSizeScale]), - MGLEventKeyOrientation: [self deviceOrientation], - MGLEventKeyWifi: @([[MGLReachability reachabilityForLocalWiFi] isReachableViaWiFi])} mutableCopy]; - [self addBatteryStateToAttributes:attributes]; + MGLMutableMapboxEventAttributes *attributes = [NSMutableDictionary dictionary]; + attributes[MGLEventKeyEvent] = MGLEventTypeMapLoad; + attributes[MGLEventKeyCreated] = [self.rfc3339DateFormatter stringFromDate:[NSDate date]]; + attributes[MGLEventKeyVendorID] = self.data.vendorId; + attributes[MGLEventKeyModel] = self.data.model; + attributes[MGLEventKeyOperatingSystem] = self.data.iOSVersion; + attributes[MGLEventKeyResolution] = @(self.data.scale); + attributes[MGLEventKeyAccessibilityFontScale] = @([self contentSizeScale]); + attributes[MGLEventKeyOrientation] = [self deviceOrientation]; + attributes[MGLEventKeyWifi] = @([[MGLReachability reachabilityForLocalWiFi] isReachableViaWiFi]); + return [self eventForAttributes:attributes attributeDictionary:attributeDictionary]; } @@ -465,19 +472,22 @@ const NSTimeInterval MGLFlushInterval = 180; - (MGLMapboxEventAttributes *)mapDragEndEventWithAttributes:(MGLMapboxEventAttributes *)attributeDictionary { MGLMutableMapboxEventAttributes *attributes = [self interactionEvent]; attributes[MGLEventKeyEvent] = MGLEventTypeMapDragEnd; + return [self eventForAttributes:attributes attributeDictionary:attributeDictionary]; } - (MGLMutableMapboxEventAttributes *)interactionEvent { - MGLMutableMapboxEventAttributes *attributes = [@{MGLEventKeyCreated: [self.rfc3339DateFormatter stringFromDate:[NSDate date]], - MGLEventKeyOrientation: [self deviceOrientation], - MGLEventKeyWifi: @([[MGLReachability reachabilityForLocalWiFi] isReachableViaWiFi])} mutableCopy]; - [self addBatteryStateToAttributes:attributes]; + MGLMutableMapboxEventAttributes *attributes = [NSMutableDictionary dictionary]; + attributes[MGLEventKeyCreated] = [self.rfc3339DateFormatter stringFromDate:[NSDate date]]; + attributes[MGLEventKeyOrientation] = [self deviceOrientation]; + attributes[MGLEventKeyWifi] = @([[MGLReachability reachabilityForLocalWiFi] isReachableViaWiFi]); + return attributes; } - (MGLMapboxEventAttributes *)eventForAttributes:(MGLMutableMapboxEventAttributes *)attributes attributeDictionary:(MGLMapboxEventAttributes *)attributeDictionary { [attributes addEntriesFromDictionary:attributeDictionary]; + return [attributes copy]; } @@ -594,29 +604,6 @@ const NSTimeInterval MGLFlushInterval = 180; return result; } -- (void)addBatteryStateToAttributes:(MGLMutableMapboxEventAttributes *)attributes { - UIDeviceBatteryState batteryState = [[UIDevice currentDevice] batteryState]; - switch (batteryState) { - case UIDeviceBatteryStateCharging: - case UIDeviceBatteryStateFull: - attributes[MGLEventKeyPluggedIn] = @(YES); - break; - case UIDeviceBatteryStateUnplugged: - attributes[MGLEventKeyPluggedIn] = @(NO); - break; - default: - // do nothing - break; - } -} - -- (void)addApplicationStateToAttributes:(MGLMutableMapboxEventAttributes *)attributes { - NSString *currentApplicationState = [self applicationState]; - if (![currentApplicationState isEqualToString:MGLApplicationStateUnknown]) { - attributes[MGLEventKeyApplicationState] = currentApplicationState; - } -} - + (void)ensureMetricsOptoutExists { NSNumber *shownInAppNumber = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"MGLMapboxMetricsEnabledSettingShownInApp"]; BOOL metricsEnabledSettingShownInAppFlag = [shownInAppNumber boolValue]; diff --git a/platform/ios/src/MGLUserLocation.h b/platform/ios/src/MGLUserLocation.h index f2243815c..1a27d31dd 100644 --- a/platform/ios/src/MGLUserLocation.h +++ b/platform/ios/src/MGLUserLocation.h @@ -11,7 +11,7 @@ NS_ASSUME_NONNULL_BEGIN directly. Instead, you retrieve an existing MGLUserLocation object from the `userLocation` property of the map view displayed in your application. */ -@interface MGLUserLocation : NSObject +@interface MGLUserLocation : NSObject #pragma mark Determining the User’s Position diff --git a/platform/ios/src/MGLUserLocation.m b/platform/ios/src/MGLUserLocation.m index a568ec8be..97e3f740f 100644 --- a/platform/ios/src/MGLUserLocation.m +++ b/platform/ios/src/MGLUserLocation.m @@ -26,6 +26,44 @@ NS_ASSUME_NONNULL_END return self; } ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (instancetype)initWithCoder:(NSCoder *)decoder { + if (self = [super init]) { + _location = [decoder decodeObjectOfClass:[CLLocation class] forKey:@"location"]; + _title = [decoder decodeObjectOfClass:[NSString class] forKey:@"title"]; + _subtitle = [decoder decodeObjectOfClass:[NSString class] forKey:@"subtitle"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [coder encodeObject:_location forKey:@"location"]; + [coder encodeObject:_title forKey:@"title"]; + [coder encodeObject:_subtitle forKey:@"subtitle"]; +} + +- (BOOL)isEqual:(id)other { + if (self == other) return YES; + if (![other isKindOfClass:[MGLUserLocation class]]) return NO; + + MGLUserLocation *otherUserLocation = other; + return ((!self.location && !otherUserLocation.location) || [self.location distanceFromLocation:otherUserLocation.location] == 0) + && ((!self.title && !otherUserLocation.title) || [self.title isEqualToString:otherUserLocation.title]) + && ((!self.subtitle && !otherUserLocation.subtitle) || [self.subtitle isEqualToString:otherUserLocation.subtitle]); +} + +- (NSUInteger)hash { + NSUInteger hash = [super hash]; + hash += [_location hash]; + hash += [_heading hash]; + hash += [_title hash]; + hash += [_subtitle hash]; + return hash; +} + + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key { return ! [key isEqualToString:@"location"] && ! [key isEqualToString:@"heading"]; diff --git a/platform/ios/src/Mapbox.h b/platform/ios/src/Mapbox.h index 88efdd5cb..37c649781 100644 --- a/platform/ios/src/Mapbox.h +++ b/platform/ios/src/Mapbox.h @@ -54,6 +54,5 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[]; #import "MGLUserLocation.h" #import "MGLUserLocationAnnotationView.h" #import "NSValue+MGLAdditions.h" -#import "NSValue+MGLStyleEnumAttributeAdditions.h" #import "MGLStyleValue.h" #import "MGLAttributionInfo.h" diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index 8f9ce6fa6..fe1a316fa 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -9,20 +9,23 @@ ## 0.3.0 +This version of the Mapbox macOS SDK corresponds to version 3.4.0 of the Mapbox iOS SDK. The two SDKs have very similar feature sets. The main differences are the lack of user location tracking and annotation views. Some APIs have been adapted to macOS conventions, particularly the use of NSPopover for callout views. + ### Packaging * Fixed an issue causing code signing failures and bloating the framework. ([#5850](https://github.com/mapbox/mapbox-gl-native/pull/5850)) * Xcode 7.3 or higher is now required for using this SDK. ([#6059](https://github.com/mapbox/mapbox-gl-native/issues/6059)) * Fixed an issue with symbols not being properly stripped from the dynamic framework when built with `make xpackage SYMBOLS=NO`. ([#6531](https://github.com/mapbox/mapbox-gl-native/pull/6531)) +* The API reference has a sharper look. ([#7422](https://github.com/mapbox/mapbox-gl-native/pull/7422)) * Added documentation for the Info.plist keys used by this SDK. ([#6833](https://github.com/mapbox/mapbox-gl-native/pull/6833)) ### Styles and data * A new runtime styling API allows you to adjust the style and content of the base map dynamically. All the options available in [Mapbox Studio](https://www.mapbox.com/studio/) are now exposed via MGLStyle and subclasses of MGLStyleLayer and MGLSource. ([#5727](https://github.com/mapbox/mapbox-gl-native/pull/5727)) * MGLMapView’s `styleURL` property can now be set to an absolute file URL. ([#6026](https://github.com/mapbox/mapbox-gl-native/pull/6026)) -* MGLShapeSource objects, as well as GeoJSON sources specified by the stylesheet at design time, now support `cluster`, `clusterMaxZoom`, and `clusterRadius` attributes for clustering point features on the base map. ([#5724](https://github.com/mapbox/mapbox-gl-native/pull/5724)) -* TileJSON manifests can now specify `"scheme": "tms"` to indicate the use of [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) coordinates. ([#2270](https://github.com/mapbox/mapbox-gl-native/pull/2270)) -* Fixed an issue causing abstract `MGLMultiPointFeature` objects to be returned in feature query results. Now concrete `MGLPointCollectionFeature` objects are returned. ([#6742](https://github.com/mapbox/mapbox-gl-native/pull/6742)) +* When creating an MGLShapeSource, you can now specify options for clustering point features within the shape source. Similarly, GeoJSON sources specified by the stylesheet at design time can specify the `cluster`, `clusterMaxZoom`, and `clusterRadius` attributes. ([#5724](https://github.com/mapbox/mapbox-gl-native/pull/5724)) +* When creating an MGLTileSource, you can now specify that the tile URLs use [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) coordinates by setting `MGLTileSourceOptionTileCoordinateSystem` to `MGLTileCoordinateSystemTMS`. TileJSON files can specify `"scheme": "tms"`. ([#2270](https://github.com/mapbox/mapbox-gl-native/pull/2270)) +* Fixed an issue causing abstract `MGLMultiPointFeature` objects to be returned in feature query results. Now concrete `MGLPointCollectionFeature` objects are returned. MGLMultiPointFeature is now an alias of MGLPointCollectionFeature. ([#6742](https://github.com/mapbox/mapbox-gl-native/pull/6742)) * Fixed rendering artifacts and missing glyphs that occurred after viewing a large number of CJK characters on the map. ([#5908](https://github.com/mapbox/mapbox-gl-native/pull/5908)) * Fixed an issue where the style zoom levels were not respected when deciding when to render a layer. ([#5811](https://github.com/mapbox/mapbox-gl-native/issues/5811)) * Fixed an issue where feature querying sometimes failed to return the expected features when the map was tilted. ([#6773](https://github.com/mapbox/mapbox-gl-native/pull/6773)) @@ -42,10 +45,11 @@ * Added `showAnnotations:animated:` and `showAnnotations:edgePadding:animated:`, which moves the map viewport to show the specified annotations. ([#5749](https://github.com/mapbox/mapbox-gl-native/pull/5749)) * Added new methods to MGLMultiPoint for changing the vertices along a polyline annotation or the exterior of a polygon annotation. ([#6565](https://github.com/mapbox/mapbox-gl-native/pull/6565)) * Added new APIs to MGLMapView to query for visible annotations. ([6061](https://github.com/mapbox/mapbox-gl-native/pull/6061)) -* Deprecated `-[MGLMapViewDelegate mapView:alphaForShapeAnnotation:]` in favor of specifying an alpha component via `-[MGLMapViewDelegate mapView:strokeColorForShapeAnnotation:]` or `-[MGLMapViewDelegate mapView:fillColorForPolygonAnnotation:]`. ([#6706](https://github.com/mapbox/mapbox-gl-native/pull/6706)) +* Shape, feature, and annotation classes now conform to NSSecureCoding. ([#6559](https://github.com/mapbox/mapbox-gl-native/pull/6559)) * Various method arguments that are represented as C arrays of `CLLocationCoordinate2D` instances have been marked `const` to streamline bridging to Swift. ([#7215](https://github.com/mapbox/mapbox-gl-native/pull/7215)) * To make an MGLPolyline or MGLPolygon span the antimeridian, specify coordinates with longitudes greater than 180° or less than −180°. ([#6088](https://github.com/mapbox/mapbox-gl-native/pull/6088)) * Fixed an issue where placing a point annotation on Null Island also placed a duplicate annotation on its antipode. ([#3563](https://github.com/mapbox/mapbox-gl-native/pull/3563)) +* Fixed an issue that caused an assertion failure if a `MGLShapeCollection` (a GeoJSON GeometryCollection) was created with an empty array of shapes. ([#7632](https://github.com/mapbox/mapbox-gl-native/pull/7632)) * Improved the precision of annotations at zoom levels greater than 18. ([#5517](https://github.com/mapbox/mapbox-gl-native/pull/5517)) ### Networking and offline maps diff --git a/platform/macos/DEVELOPING.md b/platform/macos/DEVELOPING.md index c8e064fbe..6737566a5 100644 --- a/platform/macos/DEVELOPING.md +++ b/platform/macos/DEVELOPING.md @@ -96,6 +96,32 @@ To add a localization to the macOS SDK: 1. In the Project navigator, expand each .strings and .stringsdict file in the project. An additional version for your localization should be listed; translate it. Translate everything on the right side of the equals sign. Leave the left side and any comments unmodified. See Apple’s documentation on the [.strings](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/LoadingResources/Strings/Strings.html) and [.stringsdict](https://developer.apple.com/library/content/documentation/MacOSX/Conceptual/BPInternational/StringsdictFileFormat/StringsdictFileFormat.html) formats. 1. You’re already most of the way towards localizing the iOS SDK too – consider [completing that localization](../ios/DEVELOPING.md#adding-a-localization). +### Adding a code example + +To add an example code listing to the documentation for a class or class member: + +1. Add a test method named in the form `testMGLClass` or `testMGLClass$method` + to [MGLDocumentationExampleTests](test/MGLDocumentationExampleTests.swift). + Wrap the code you’d like to appear in the documentation within + `//#-example-code` and `//#-end-example-code` comments. +1. Insert the code listings into the headers: + +```bash +make darwin-update-examples +``` + +[SourceKitten](https://github.com/jpsim/SourceKitten/) is required and will be installed automatically using Homebrew. + +## Testing + +`make macos-test` builds and runs unit tests of cross-platform code as well as the SDK. + +To instead run the cross-platform tests in Xcode instead of on the command line: + +1. Run `make xproj` to set up the workspace. +1. Change the scheme to “mbgl-test” and press Command-R to run core unit tests. +1. Change the scheme to “CI” and press Command-U to run SDK integration tests. + ## Access tokens The demo applications use Mapbox vector tiles, which require a Mapbox account and API access token. Obtain an access token on the [Mapbox account page](https://www.mapbox.com/studio/account/tokens/). You will be prompted for this access token the first time you launch the demo application. diff --git a/platform/macos/INSTALL.md b/platform/macos/INSTALL.md index a4b944611..f8b4c675d 100644 --- a/platform/macos/INSTALL.md +++ b/platform/macos/INSTALL.md @@ -11,6 +11,13 @@ The Mapbox macOS SDK requires the macOS 10.10.0 SDK (or above) and Xcode 7.3 (or Grab a [prebuilt release](https://github.com/mapbox/mapbox-gl-native/releases/) – look for the releases that begin with “macos-” – or build the SDK from source: 1. [Install core dependencies](../../INSTALL.md). + +1. Install [jazzy](https://github.com/realm/jazzy) for generating API documentation: + + ``` + [sudo] gem install jazzy + ``` + 1. Run `make xpackage`, which produces a `Mapbox.framework` in the `build/macos/pkg/` folder. ### Installation @@ -64,4 +71,4 @@ script AppDelegate end script ``` -Run `make xdocument` to generate complete API documentation. The [Mapbox iOS SDK](https://www.mapbox.com/ios-sdk/)’s [API documentation](https://www.mapbox.com/ios-sdk/api/) and [online examples](https://www.mapbox.com/ios-sdk/examples/) apply to the Mapbox macOS SDK with few differences, mostly around unimplemented features like user location tracking. +For further instructions, consult the [macOS SDK documentation](https://mapbox.github.io/mapbox-gl-native/macos/) or run `make xdocument` to generate the documentation. The [Mapbox iOS SDK](https://www.mapbox.com/ios-sdk/)’s [API documentation](https://www.mapbox.com/ios-sdk/) and [online examples](https://www.mapbox.com/ios-sdk/examples/) apply to the Mapbox macOS SDK with few differences, mostly around unimplemented features like user location tracking. diff --git a/platform/macos/Mapbox-macOS-SDK-symbols.podspec b/platform/macos/Mapbox-macOS-SDK-symbols.podspec new file mode 100644 index 000000000..6de7f829a --- /dev/null +++ b/platform/macos/Mapbox-macOS-SDK-symbols.podspec @@ -0,0 +1,30 @@ +Pod::Spec.new do |m| + + version = '0.3.0-rc.1' + + m.name = 'Mapbox-macOS-SDK-symbols' + m.version = "#{version}-symbols" + + m.summary = 'Open-source, interactive, fully customizable vector maps.' + m.description = 'Interactive, fully customizable vector maps with tight platform integration and high-performance OpenGL rendering.' + m.homepage = 'https://mapbox.github.io/mapbox-gl-native/macos/' + m.license = { :type => 'BSD', :file => 'LICENSE.md' } + m.author = { 'Mapbox' => 'mobile@mapbox.com' } + m.screenshot = "https://mapbox.github.io/mapbox-gl-native/macos/#{version}/img/screenshot.jpg" + m.social_media_url = 'https://twitter.com/mapbox' + m.documentation_url = 'https://mapbox.github.io/mapbox-gl-native/macos/' + + m.source = { + :http => "https://github.com/mapbox/mapbox-gl-native/releases/download/macos-v#{version}/mapbox-macos-sdk-#{m.version.to_s}.zip", + :flatten => true + } + + m.platform = :osx + m.osx.deployment_target = '10.10' + + m.requires_arc = true + + m.vendored_frameworks = 'Mapbox.framework' + m.module_name = 'Mapbox' + +end diff --git a/platform/macos/Mapbox-macOS-SDK.podspec b/platform/macos/Mapbox-macOS-SDK.podspec new file mode 100644 index 000000000..03ef0eabe --- /dev/null +++ b/platform/macos/Mapbox-macOS-SDK.podspec @@ -0,0 +1,30 @@ +Pod::Spec.new do |m| + + version = '0.3.0-rc.1' + + m.name = 'Mapbox-macOS-SDK' + m.version = version + + m.summary = 'Open-source, interactive, fully customizable vector maps.' + m.description = 'Interactive, fully customizable vector maps with tight platform integration and high-performance OpenGL rendering.' + m.homepage = 'https://mapbox.github.io/mapbox-gl-native/macos/' + m.license = { :type => 'BSD', :file => 'LICENSE.md' } + m.author = { 'Mapbox' => 'mobile@mapbox.com' } + m.screenshot = "https://mapbox.github.io/mapbox-gl-native/macos/#{version}/img/screenshot.jpg" + m.social_media_url = 'https://twitter.com/mapbox' + m.documentation_url = 'https://mapbox.github.io/mapbox-gl-native/macos/' + + m.source = { + :http => "https://github.com/mapbox/mapbox-gl-native/releases/download/macos-v#{version}/mapbox-macos-sdk-#{m.version.to_s}.zip", + :flatten => true + } + + m.platform = :osx + m.osx.deployment_target = '10.10' + + m.requires_arc = true + + m.vendored_frameworks = 'Mapbox.framework' + m.module_name = 'Mapbox' + +end diff --git a/platform/macos/README.md b/platform/macos/README.md index 211b238d5..aea5ba0d6 100644 --- a/platform/macos/README.md +++ b/platform/macos/README.md @@ -2,11 +2,23 @@ [![Bitrise](https://www.bitrise.io/app/155ef7da24b38dcd.svg?token=4KSOw_gd6WxTnvGE2rMttg&branch=master)](https://www.bitrise.io/app/155ef7da24b38dcd) -A library based on [Mapbox GL Native](../../README.md) for embedding interactive map views with scalable, customizable vector maps into Cocoa applications on macOS 10.10.0 and above using Objective-C, Swift, or Interface Builder. +Put interactive, scalable world maps into your native Cocoa application with the Mapbox macOS SDK. -This SDK is analogous to the Mapbox iOS SDK, and much of the iOS SDK documentation applies here. Mapbox does not officially support the macOS SDK to the same extent as the iOS SDK; however, bug reports and pull requests are certainly welcome. +* Mapbox-curated [map styles](https://www.mapbox.com/maps/) and [vector tiles](https://www.mapbox.com/vector-tiles/) make it easy to get started. +* Customize every aspect of the map’s appearance in code or visually using [Mapbox Studio](https://www.mapbox.com/mapbox-studio/). +* High-performance OpenGL rendering and multitouch gestures keep your users happy. +* A well-designed, fully documented API helps you stay productive. +* Develop across [multiple platforms](../../README.md), including [iOS](../ios/README.md), using the same styles and similar APIs. -* [Integrating the Mapbox macOS SDK into your application](INSTALL.md) -* [Contributing to the Mapbox macOS SDK](DEVELOPING.md) +![](docs/img/screenshot.jpg) - +The Mapbox macOS SDK is compatible with macOS 10.10.0 and above for Cocoa applications developed in Objective-C, Swift, Interface Builder, or AppleScript. For hybrid applications, consider [Mapbox GL JS](https://github.com/mapbox/mapbox-gl-js/). + +* [Integrate the Mapbox macOS SDK into your application](INSTALL.md) +* [Contribute to the Mapbox macOS SDK](DEVELOPING.md) +* [Mapbox macOS SDK reference documentation](https://mapbox.github.io/mapbox-gl-native/macos/) +* [See example styles designed in Mapbox Studio](https://www.mapbox.com/gallery/) +* [Hand-authoring a macOS SDK–compatible style](https://www.mapbox.com/mapbox-gl-style-spec/) +* [Mapbox Vector Tile Specification](https://www.mapbox.com/developers/vector-tiles/) + +Mapbox does not officially support the Mapbox macOS SDK to the same extent as the Mapbox iOS SDK; however, bug reports and pull requests are certainly welcome. diff --git a/platform/macos/app/Base.lproj/MapDocument.xib b/platform/macos/app/Base.lproj/MapDocument.xib index e147ba83d..4ba8f0a3a 100644 --- a/platform/macos/app/Base.lproj/MapDocument.xib +++ b/platform/macos/app/Base.lproj/MapDocument.xib @@ -27,6 +27,7 @@ layers name + reversedLayers @@ -38,7 +39,7 @@ visible - + diff --git a/platform/macos/app/MGLStyle+MBXAdditions.h b/platform/macos/app/MGLStyle+MBXAdditions.h new file mode 100644 index 000000000..dcaf42af2 --- /dev/null +++ b/platform/macos/app/MGLStyle+MBXAdditions.h @@ -0,0 +1,7 @@ +#import + +@interface MGLStyle (MBXAdditions) + +@property (nonatomic, strong) NS_ARRAY_OF(__kindof MGLStyleLayer *) *reversedLayers; + +@end diff --git a/platform/macos/app/MGLStyle+MBXAdditions.m b/platform/macos/app/MGLStyle+MBXAdditions.m new file mode 100644 index 000000000..be571d8b3 --- /dev/null +++ b/platform/macos/app/MGLStyle+MBXAdditions.m @@ -0,0 +1,42 @@ +#import "MGLStyle+MBXAdditions.h" + +@implementation MGLStyle (MBXAdditions) + ++ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingReversedLayers { + return [NSSet setWithObject:@"layers"]; +} + +- (NS_ARRAY_OF(__kindof MGLStyleLayer *) *)reversedLayers { + return self.layers.reverseObjectEnumerator.allObjects; +} + +- (void)setReversedLayers:(NS_ARRAY_OF(__kindof MGLStyleLayer *) *)reversedLayers { + self.layers = reversedLayers.reverseObjectEnumerator.allObjects; +} + +- (NSUInteger)countOfReversedLayers { + return self.layers.count; +} + +- (id)objectInReversedLayersAtIndex:(NSUInteger)index { + NSArray *layers = self.layers; + return layers[layers.count - 1 - index]; +} + +- (void)getReversedLayers:(__kindof MGLStyleLayer **)buffer range:(NSRange)inRange { + NSArray *layers = self.layers; + for (NSUInteger i = inRange.location; i < NSMaxRange(inRange); i++) { + MGLStyleLayer *styleLayer = layers[layers.count - 1 - i]; + buffer[i] = styleLayer; + } +} + +- (void)insertObject:(__kindof MGLStyleLayer *)object inReversedLayersAtIndex:(NSUInteger)index { + [self insertLayer:object atIndex:self.layers.count - index]; +} + +- (void)removeObjectFromReversedLayersAtIndex:(NSUInteger)index { + [self removeLayer:[self objectInReversedLayersAtIndex:index]]; +} + +@end diff --git a/platform/macos/app/MGLVectorSource+MBXAdditions.h b/platform/macos/app/MGLVectorSource+MBXAdditions.h new file mode 100644 index 000000000..312081ec5 --- /dev/null +++ b/platform/macos/app/MGLVectorSource+MBXAdditions.h @@ -0,0 +1,15 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface MGLVectorSource (MBXAdditions) + ++ (nullable NSString *)preferredMapboxStreetsLanguage; + +- (NS_DICTIONARY_OF(NSString *, NSString *) *)localizedKeysByKeyForPreferredLanguage:(nullable NSString *)preferredLanguage; + +@property (nonatomic, readonly, getter=isMapboxStreets) BOOL mapboxStreets; + +@end + +NS_ASSUME_NONNULL_END diff --git a/platform/macos/app/MGLVectorSource+MBXAdditions.m b/platform/macos/app/MGLVectorSource+MBXAdditions.m new file mode 100644 index 000000000..f59b72aa9 --- /dev/null +++ b/platform/macos/app/MGLVectorSource+MBXAdditions.m @@ -0,0 +1,49 @@ +#import "MGLVectorSource+MBXAdditions.h" + +@implementation MGLVectorSource (MBXAdditions) + ++ (NS_SET_OF(NSString *) *)mapboxStreetsLanguages { + // https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview + static dispatch_once_t onceToken; + static NS_SET_OF(NSString *) *mapboxStreetsLanguages; + dispatch_once(&onceToken, ^{ + mapboxStreetsLanguages = [NSSet setWithObjects:@"en", @"es", @"fr", @"de", @"ru", @"zh", nil]; + }); + return mapboxStreetsLanguages; +} + ++ (nullable NSString *)preferredMapboxStreetsLanguage { + for (NSString *language in [NSLocale preferredLanguages]) { + NSString *languageCode = [[NSLocale localeWithLocaleIdentifier:language] objectForKey:NSLocaleLanguageCode]; + if ([[MGLVectorSource mapboxStreetsLanguages] containsObject:languageCode]) { + return languageCode; + } + } + return nil; +} + +- (BOOL)isMapboxStreets { + NSURL *url = self.configurationURL; + if (![url.scheme isEqualToString:@"mapbox"]) { + return NO; + } + NSArray *identifiers = [url.host componentsSeparatedByString:@","]; + return [identifiers containsObject:@"mapbox.mapbox-streets-v7"] || [identifiers containsObject:@"mapbox.mapbox-streets-v6"]; +} + +- (NS_DICTIONARY_OF(NSString *, NSString *) *)localizedKeysByKeyForPreferredLanguage:(nullable NSString *)preferredLanguage { + if (!self.mapboxStreets) { + return @{}; + } + + // Replace {name} and {name_*} with the matching localized name tag. + NSString *localizedKey = preferredLanguage ? [NSString stringWithFormat:@"name_%@", preferredLanguage] : @"name"; + NSMutableDictionary *localizedKeysByKey = [NSMutableDictionary dictionaryWithObject:localizedKey forKey:@"name"]; + for (NSString *languageCode in [MGLVectorSource mapboxStreetsLanguages]) { + NSString *key = [NSString stringWithFormat:@"name_%@", languageCode]; + localizedKeysByKey[key] = localizedKey; + } + return localizedKeysByKey; +} + +@end diff --git a/platform/macos/app/MapDocument.m b/platform/macos/app/MapDocument.m index 2c3161077..64833a856 100644 --- a/platform/macos/app/MapDocument.m +++ b/platform/macos/app/MapDocument.m @@ -4,6 +4,9 @@ #import "LimeGreenStyleLayer.h" #import "DroppedPinAnnotation.h" +#import "MGLStyle+MBXAdditions.h" +#import "MGLVectorSource+MBXAdditions.h" + #import static NSString * const MGLDroppedPinAnnotationImageIdentifier = @"dropped"; @@ -143,11 +146,12 @@ NS_ARRAY_OF(id ) *MBXFlattenedShapes(NS_ARRAY_OF(id ) *MBXFlattenedShapes(NS_ARRAY_OF(id ) *MBXFlattenedShapes(NS_ARRAY_OF(id ) *MBXFlattenedShapes(NS_ARRAY_OF(id *)layer.textField rawValue]; - textField = [nameTokenExpression stringByReplacingMatchesInString:textField - options:0 - range:NSMakeRange(0, textField.length) - withTemplate:preferredNameToken]; - layer.textField = [MGLStyleValue valueWithRawValue:textField]; - } else if ([layer.textField isKindOfClass:[MGLStyleFunction class]]) { - MGLStyleFunction *function = (MGLStyleFunction *)layer.textField; + MGLVectorSource *source = (MGLVectorSource *)[style sourceWithIdentifier:layer.sourceIdentifier]; + if (![source isKindOfClass:[MGLVectorSource class]] || !source.mapboxStreets) { + continue; + } + + NSDictionary *localizedKeysByKey = localizedKeysByKeyBySourceIdentifier[layer.sourceIdentifier]; + if (!localizedKeysByKey) { + localizedKeysByKey = localizedKeysByKeyBySourceIdentifier[layer.sourceIdentifier] = [source localizedKeysByKeyForPreferredLanguage:preferredLanguage]; + } + + NSString *(^stringByLocalizingString)(NSString *) = ^ NSString * (NSString *string) { + NSMutableString *localizedString = string.mutableCopy; + [localizedKeysByKey enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSString * _Nonnull localizedKey, BOOL * _Nonnull stop) { + NSAssert([key isKindOfClass:[NSString class]], @"key is not a string"); + NSAssert([localizedKey isKindOfClass:[NSString class]], @"localizedKey is not a string"); + [localizedString replaceOccurrencesOfString:[NSString stringWithFormat:@"{%@}", key] + withString:[NSString stringWithFormat:@"{%@}", localizedKey] + options:0 + range:NSMakeRange(0, localizedString.length)]; + }]; + return localizedString; + }; + + if ([layer.text isKindOfClass:[MGLStyleConstantValue class]]) { + NSString *textField = [(MGLStyleConstantValue *)layer.text rawValue]; + layer.text = [MGLStyleValue valueWithRawValue:stringByLocalizingString(textField)]; + } else if ([layer.text isKindOfClass:[MGLStyleFunction class]]) { + MGLStyleFunction *function = (MGLStyleFunction *)layer.text; NSMutableDictionary *stops = function.stops.mutableCopy; [stops enumerateKeysAndObjectsUsingBlock:^(NSNumber *zoomLevel, MGLStyleConstantValue *stop, BOOL *done) { NSString *textField = stop.rawValue; - textField = [nameTokenExpression stringByReplacingMatchesInString:textField - options:0 - range:NSMakeRange(0, textField.length) - withTemplate:preferredNameToken]; - stops[zoomLevel] = [MGLStyleValue valueWithRawValue:textField]; + stops[zoomLevel] = [MGLStyleValue valueWithRawValue:stringByLocalizingString(textField)]; }]; function.stops = stops; - layer.textField = function; + layer.text = function; } } } -- (NSString *)preferredLanguageCode { - // Languages supported by Mapbox Streets v10. - NSSet *supportedLanguages = [NSSet setWithObjects:@"en", @"es", @"fr", @"de", @"ru", @"zh", nil]; - NSArray *preferredLanguages = [NSLocale preferredLanguages]; - - for (NSString *language in preferredLanguages) { - NSString *languageCode = [[NSLocale localeWithLocaleIdentifier:language] objectForKey:NSLocaleLanguageCode]; - if ([supportedLanguages containsObject:languageCode]) { - return languageCode; - } - } - - return @"en"; -} - - (void)applyPendingState { if (_inheritedStyleURL) { self.mapView.styleURL = _inheritedStyleURL; @@ -824,7 +827,7 @@ NS_ARRAY_OF(id ) *MBXFlattenedShapes(NS_ARRAY_OF(id ) *MBXFlattenedShapes(NS_ARRAY_OF(id ) *MBXFlattenedShapes(NS_ARRAY_OF(id +![](img/screenshot.jpg) -For setup information, consult the README.md that comes with this documentation. The [Mapbox iOS SDK](https://www.mapbox.com/ios-sdk/)’s [API documentation](https://www.mapbox.com/ios-sdk/api/) and [online examples](https://www.mapbox.com/ios-sdk/examples/) apply to the Mapbox macOS SDK with few differences, mostly around unimplemented features like user location tracking. A [full changelog](https://github.com/mapbox/mapbox-gl-native/blob/master/platform/macos/CHANGELOG.md) is also available. +For setup information, consult the README.md that comes with this documentation. For further instructions, consult the [macOS SDK documentation](https://mapbox.github.io/mapbox-gl-native/macos/). The [Mapbox iOS SDK](https://www.mapbox.com/ios-sdk/)’s [API documentation](https://www.mapbox.com/ios-sdk/api/) and [online examples](https://www.mapbox.com/ios-sdk/examples/) apply to the Mapbox macOS SDK with few differences, mostly around unimplemented features like user location tracking. A [full changelog](https://github.com/mapbox/mapbox-gl-native/blob/master/platform/macos/CHANGELOG.md) is also available. Mapbox does not officially support the macOS SDK to the same extent as the iOS SDK; however, [bug reports and pull requests](https://github.com/mapbox/mapbox-gl-native/issues/) are certainly welcome. diff --git a/platform/macos/docs/guides/For Style Authors.md b/platform/macos/docs/guides/For Style Authors.md new file mode 100644 index 000000000..cf25ae09c --- /dev/null +++ b/platform/macos/docs/guides/For Style Authors.md @@ -0,0 +1,300 @@ + +# 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. + +## 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 macOS 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. + +### Typography and graphics + + +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 macOS. +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. +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. +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. +You can install cursor or tooltip tracking rectangles to indicate interactive +features as an alternative to prominent hover effects. + +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. + +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. + +The names of runtime styling classes and properties on macOS 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 +--------------|----------- +`background` | `MGLBackgroundStyleLayer` +`circle` | `MGLCircleStyleLayer` +`fill` | `MGLFillStyleLayer` +`line` | `MGLLineStyleLayer` +`raster` | `MGLRasterStyleLayer` +`symbol` | `MGLSymbolStyleLayer` + +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: + +### Circle style layers + +In style JSON | In Objective-C | In Swift +--------------|----------------|--------- +`circle-pitch-scale` | `MGLCircleStyleLayer.circleScaleAlignment` | `MGLCircleStyleLayer.circleScaleAlignment` +`circle-translate` | `MGLCircleStyleLayer.circleTranslation` | `MGLCircleStyleLayer.circleTranslation` +`circle-translate-anchor` | `MGLCircleStyleLayer.circleTranslationAnchor` | `MGLCircleStyleLayer.circleTranslationAnchor` + +### Fill style layers + +In style JSON | In Objective-C | In Swift +--------------|----------------|--------- +`fill-antialias` | `MGLFillStyleLayer.fillAntialiased` | `MGLFillStyleLayer.isFillAntialiased` +`fill-translate` | `MGLFillStyleLayer.fillTranslation` | `MGLFillStyleLayer.fillTranslation` +`fill-translate-anchor` | `MGLFillStyleLayer.fillTranslationAnchor` | `MGLFillStyleLayer.fillTranslationAnchor` + +### Line style layers + +In style JSON | In Objective-C | In Swift +--------------|----------------|--------- +`line-dasharray` | `MGLLineStyleLayer.lineDashPattern` | `MGLLineStyleLayer.lineDashPattern` +`line-translate` | `MGLLineStyleLayer.lineTranslation` | `MGLLineStyleLayer.lineTranslation` +`line-translate-anchor` | `MGLLineStyleLayer.lineTranslationAnchor` | `MGLLineStyleLayer.lineTranslationAnchor` + +### Raster style layers + +In style JSON | In Objective-C | In Swift +--------------|----------------|--------- +`raster-brightness-max` | `MGLRasterStyleLayer.maximumRasterBrightness` | `MGLRasterStyleLayer.maximumRasterBrightness` +`raster-brightness-min` | `MGLRasterStyleLayer.minimumRasterBrightness` | `MGLRasterStyleLayer.minimumRasterBrightness` +`raster-hue-rotate` | `MGLRasterStyleLayer.rasterHueRotation` | `MGLRasterStyleLayer.rasterHueRotation` + +### Symbol style layers + +In style JSON | In Objective-C | In Swift +--------------|----------------|--------- +`icon-allow-overlap` | `MGLSymbolStyleLayer.iconAllowsOverlap` | `MGLSymbolStyleLayer.iconAllowsOverlap` +`icon-ignore-placement` | `MGLSymbolStyleLayer.iconIgnoresPlacement` | `MGLSymbolStyleLayer.iconIgnoresPlacement` +`icon-image` | `MGLSymbolStyleLayer.iconImageName` | `MGLSymbolStyleLayer.iconImageName` +`icon-optional` | `MGLSymbolStyleLayer.iconOptional` | `MGLSymbolStyleLayer.isIconOptional` +`icon-rotate` | `MGLSymbolStyleLayer.iconRotation` | `MGLSymbolStyleLayer.iconRotation` +`icon-size` | `MGLSymbolStyleLayer.iconScale` | `MGLSymbolStyleLayer.iconScale` +`icon-keep-upright` | `MGLSymbolStyleLayer.keepsIconUpright` | `MGLSymbolStyleLayer.keepsIconUpright` +`text-keep-upright` | `MGLSymbolStyleLayer.keepsTextUpright` | `MGLSymbolStyleLayer.keepsTextUpright` +`text-max-angle` | `MGLSymbolStyleLayer.maximumTextAngle` | `MGLSymbolStyleLayer.maximumTextAngle` +`text-max-width` | `MGLSymbolStyleLayer.maximumTextWidth` | `MGLSymbolStyleLayer.maximumTextWidth` +`symbol-avoid-edges` | `MGLSymbolStyleLayer.symbolAvoidsEdges` | `MGLSymbolStyleLayer.symbolAvoidsEdges` +`text-field` | `MGLSymbolStyleLayer.text` | `MGLSymbolStyleLayer.text` +`text-allow-overlap` | `MGLSymbolStyleLayer.textAllowsOverlap` | `MGLSymbolStyleLayer.textAllowsOverlap` +`text-font` | `MGLSymbolStyleLayer.textFontNames` | `MGLSymbolStyleLayer.textFontNames` +`text-size` | `MGLSymbolStyleLayer.textFontSize` | `MGLSymbolStyleLayer.textFontSize` +`text-ignore-placement` | `MGLSymbolStyleLayer.textIgnoresPlacement` | `MGLSymbolStyleLayer.textIgnoresPlacement` +`text-justify` | `MGLSymbolStyleLayer.textJustification` | `MGLSymbolStyleLayer.textJustification` +`text-optional` | `MGLSymbolStyleLayer.textOptional` | `MGLSymbolStyleLayer.isTextOptional` +`text-rotate` | `MGLSymbolStyleLayer.textRotation` | `MGLSymbolStyleLayer.textRotation` +`icon-translate` | `MGLSymbolStyleLayer.iconTranslation` | `MGLSymbolStyleLayer.iconTranslation` +`icon-translate-anchor` | `MGLSymbolStyleLayer.iconTranslationAnchor` | `MGLSymbolStyleLayer.iconTranslationAnchor` +`text-translate` | `MGLSymbolStyleLayer.textTranslation` | `MGLSymbolStyleLayer.textTranslation` +`text-translate-anchor` | `MGLSymbolStyleLayer.textTranslationAnchor` | `MGLSymbolStyleLayer.textTranslationAnchor` + +## 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 | `NSColor` | `NSColor` +Enum | `NSValue` (see `NSValue(MGLAdditions)`) | `NSValue` (see `NSValue(MGLAdditions)`) +String | `NSString` | `String` +Boolean | `NSNumber.boolValue` | `Bool` +Number | `NSNumber.floatValue` | `Float` +Array (`-dasharray`) | `NSArray` | `[Float]` +Array (`-font`) | `NSArray` | `[String]` +Array (`-offset`, `-translate`) | `NSValue` containing `CGVector` | `NSValue` containing `CGVector` +Array (`-padding`) | `NSValue.edgeInsetsValue` | `NSValue.edgeInsetsValue` + +For padding attributes, note that the arguments to +`NSEdgeInsetsMake()` 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. + +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/macos/docs/Info.plist Keys.md b/platform/macos/docs/guides/Info.plist Keys.md similarity index 81% rename from platform/macos/docs/Info.plist Keys.md rename to platform/macos/docs/guides/Info.plist Keys.md index a92b6fe29..f61ad8c7a 100644 --- a/platform/macos/docs/Info.plist Keys.md +++ b/platform/macos/docs/guides/Info.plist Keys.md @@ -1,6 +1,6 @@ # Info.plist Keys -The Mapbox macOS SDK supports custom `Info.plist` keys in your application in order to configure various settings. +The Mapbox macOS SDK supports custom `Info.plist` keys in your application in order to configure various settings. ## MGLMapboxAccessToken @@ -8,10 +8,10 @@ Set the [Mapbox access token](https://www.mapbox.com/help/define-access-token/) Mapbox-hosted vector tiles and styles require an API access token, which you can obtain from the [Mapbox account page](https://www.mapbox.com/studio/account/tokens/). Access tokens associate requests to Mapbox’s vector tile and style APIs with your Mapbox account. They also deter other developers from using your styles without your permission. -As an alternative, you can use `+[MGLAccountManager setAccessToken:]` to set a token in code. See [our guide](https://www.mapbox.com/help/ios-private-access-token/) for some tips on keeping access tokens in open source code private. +As an alternative, you can use `+[MGLAccountManager setAccessToken:]` to set a token in code. See [our guide](https://www.mapbox.com/help/ios-private-access-token/) for some tips on keeping access tokens in open source code private. ## MGLMapboxAPIBaseURL -Use this key if you need to customize the API base URL used throughout the SDK. If unset, the default Mapbox API is used. +Use this key if you need to customize the API base URL used throughout the SDK. If unset, the default Mapbox API is used. -The default value is `https://api.mapbox.com`. +The default value is `https://api.mapbox.com`. diff --git a/platform/macos/docs/img/screenshot.jpg b/platform/macos/docs/img/screenshot.jpg new file mode 100644 index 000000000..5341a3d6f Binary files /dev/null and b/platform/macos/docs/img/screenshot.jpg differ diff --git a/platform/macos/docs/pod-README.md b/platform/macos/docs/pod-README.md index 8af262cc8..6d45a68c3 100644 --- a/platform/macos/docs/pod-README.md +++ b/platform/macos/docs/pod-README.md @@ -1,18 +1,37 @@ # [Mapbox macOS SDK](https://github.com/mapbox/mapbox-gl-native/tree/master/platform/macos/) -The Mapbox macOS SDK is an open-source framework for embedding interactive map views with scalable, customizable vector maps into Cocoa applications on macOS 10.10.0 and above using Objective-C, Swift, Interface Builder, or AppleScript. It takes stylesheets that conform to the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/), applies them to vector tiles that conform to the [Mapbox Vector Tile Specification](https://www.mapbox.com/developers/vector-tiles/), and renders them using OpenGL. +Put interactive, scalable world maps into your native Cocoa application with the open-source Mapbox macOS SDK. - +* Mapbox-curated [map styles](https://www.mapbox.com/maps/) and [vector tiles](https://www.mapbox.com/vector-tiles/) make it easy to get started. +* Customize every aspect of the map’s appearance in code or visually using [Mapbox Studio](https://www.mapbox.com/mapbox-studio/). +* High-performance OpenGL rendering and multitouch gestures keep your users happy. +* A well-designed, fully documented API helps you stay productive. +* Develop across [multiple platforms](https://www.mapbox.com/maps/), including [iOS](https://www.mapbox.com/ios-sdk/), using the same styles and similar APIs. + +![](https://raw.githubusercontent.com/mapbox/mapbox-gl-native/master/platform/macos/docs/img/screenshot.jpg) + +The Mapbox macOS SDK is compatible with macOS 10.10.0 and above for Cocoa applications developed in Objective-C, Swift, Interface Builder, or AppleScript. For hybrid applications, consider [Mapbox GL JS](https://www.mapbox.com/mapbox-gl-js/). + +Mapbox macOS SDK releases are [available on GitHub](https://github.com/mapbox/mapbox-gl-native/releases/) – look for the releases that begin with “macos-”. You can also integrate the Mapbox macOS SDK into your application using CocoaPods. ## Installation -1. Open the project editor, select your application target, then go to the General tab. Drag Mapbox.framework from the `dynamic` folder into the “Embedded Binaries” section. (Don’t drag it into the “Linked Frameworks and Libraries” section; Xcode will add it there automatically.) In the sheet that appears, make sure “Copy items if needed” is checked, then click Finish. +1. Open the project editor, select your application target, then go to the General tab. Drag Mapbox.framework into the “Embedded Binaries” section. (Don’t drag it into the “Linked Frameworks and Libraries” section; Xcode will add it there automatically.) In the sheet that appears, make sure “Copy items if needed” is checked, then click Finish. -1. Mapbox vector tiles require a Mapbox account and API access token. In the project editor, select the application target, then go to the Info tab. Under the “Custom macOS Target Properties” section, set `MGLMapboxAccessToken` to your access token. You can obtain an access token from the [Mapbox account page](https://www.mapbox.com/studio/account/tokens/). +1. Mapbox vector tiles require a Mapbox account and API access token. In the project editor, select the application target, then go to the Info tab. Under the “Custom macOS Application Target Properties” section, set `MGLMapboxAccessToken` to your access token. You can obtain an access token from the [Mapbox account page](https://www.mapbox.com/studio/account/tokens/). ## Usage -In a storyboard or XIB, add a view to your view controller. (Drag Custom View from the Object library to the View Controller scene on the Interface Builder canvas.) In the Identity inspector, set the view’s custom class to `MGLMapView`. If you need to manipulate the map view programmatically: +In a storyboard or XIB: + +1. Add a view to your view controller or window. (Drag Custom View from the Object library to the View Controller scene on the Interface Builder canvas. In a XIB, drag it instead to the window on the canvas.) +2. In the Identity inspector, set the view’s custom class to `MGLMapView`. +3. MGLMapView needs to be layer-backed: + * You can make the window layer-backed by selecting the window and checking Full Size Content View in the Attributes inspector. This allows the map view to underlap the title bar and toolbar. + * Alternatively, if you don’t want the entire window to be layer-backed, you can make just the map view layer-backed by selecting it and checking its entry under the View Effects inspector’s Core Animation Layer section. +4. Add a map feedback item to your Help menu. (Drag Menu Item from the Object library into Main Menu ‣ Help ‣ Menu.) Title it “Improve This Map” or similar, and connect it to the `giveFeedback:` action of First Responder. + +If you need to manipulate the map view programmatically: 1. Switch to the Assistant Editor. 1. Import the `Mapbox` module. @@ -46,6 +65,6 @@ script AppDelegate end script ``` -Full API documentation is included in this package, within the `documentation` folder. The [Mapbox iOS SDK](https://www.mapbox.com/ios-sdk/)’s [API documentation](https://www.mapbox.com/ios-sdk/api/) and [online examples](https://www.mapbox.com/ios-sdk/examples/) apply to the Mapbox macOS SDK with few differences, mostly around unimplemented features like user location tracking. +Full API documentation is included in this package, within the `documentation` folder, and [online](https://mapbox.github.io/mapbox-gl-native/macos/). The [Mapbox iOS SDK](https://www.mapbox.com/ios-sdk/)’s [API documentation](https://www.mapbox.com/ios-sdk/api/) and [online examples](https://www.mapbox.com/ios-sdk/examples/) apply to the Mapbox macOS SDK with few differences, mostly around unimplemented features like user location tracking. Mapbox does not officially support the macOS SDK to the same extent as the iOS SDK; however, [bug reports and pull requests](https://github.com/mapbox/mapbox-gl-native/issues/) are certainly welcome. diff --git a/platform/macos/jazzy.yml b/platform/macos/jazzy.yml index ae437377a..56f725563 100644 --- a/platform/macos/jazzy.yml +++ b/platform/macos/jazzy.yml @@ -2,6 +2,7 @@ module: Mapbox author: Mapbox author_url: https://www.mapbox.com/ github_url: https://github.com/mapbox/mapbox-gl-native +dash_url: https://mapbox.github.io/mapbox-gl-native/macos/docsets/Mapbox.xml copyright: '© 2014–2017 [Mapbox](https://www.mapbox.com/). See [license](https://github.com/mapbox/mapbox-gl-native/blob/master/LICENSE.md) for more details.' head: | @@ -14,58 +15,64 @@ umbrella_header: src/Mapbox.h framework_root: ../darwin/src custom_categories: + - name: Guides + children: + - Working with GeoJSON Data + - For Style Authors + - Info.plist Keys - name: Maps children: - MGLAccountManager - MGLMapCamera - MGLMapView - MGLMapViewDelegate - - MGLStyle - MGLUserTrackingMode - - name: Annotations + - name: Shapes and Annotations children: - MGLAnnotation - MGLAnnotationImage + - MGLOverlay + - MGLShape - MGLMultiPoint - - MGLMultiPolygon - - MGLMultiPolyline - MGLPointAnnotation - MGLPointCollection - MGLPolygon - MGLPolyline - - MGLOverlay - - MGLShape + - MGLMultiPolygon + - MGLMultiPolyline - MGLShapeCollection - - name: Map Data + - name: Styling the Map children: - - MGLFeature + - MGLStyle + - MGLStyleValue + - name: Content Primitives + children: + - MGLFeature + - MGLPointFeature + - MGLPolygonFeature + - MGLPolylineFeature - MGLMultiPolygonFeature - MGLMultiPolylineFeature - MGLPointCollectionFeature - - MGLPointFeature - - MGLPolygonFeature - - MGLPolylineFeature - MGLShapeCollectionFeature - - name: Style Layers - children: - - MGLBackgroundStyleLayer - - MGLCircleStyleLayer - - MGLFillStyleLayer - - MGLForegroundStyleLayer - - MGLLineStyleLayer - - MGLRasterStyleLayer - - MGLStyleLayer - - MGLSymbolStyleLayer - - MGLVectorStyleLayer - name: Content Sources children: - - MGLAttributionInfo - - MGLRasterSource - - MGLShapeSource - MGLSource - - MGLTileCoordinateSystem - MGLTileSource + - MGLShapeSource + - MGLRasterSource - MGLVectorSource + - name: Style Layers + children: + - MGLStyleLayer + - MGLForegroundStyleLayer + - MGLBackgroundStyleLayer + - MGLRasterStyleLayer + - MGLVectorStyleLayer + - MGLCircleStyleLayer + - MGLFillStyleLayer + - MGLLineStyleLayer + - MGLSymbolStyleLayer - name: Offline Maps children: - MGLOfflineRegion @@ -76,15 +83,13 @@ custom_categories: - MGLTilePyramidOfflineRegion - name: Geometry children: - - MGLClockDirectionFormatter - - MGLCompassDirectionFormatter - MGLCoordinateBounds - MGLCoordinateBoundsEqualToCoordinateBounds - MGLCoordinateBoundsGetCoordinateSpan + - MGLCoordinateBoundsIntersectsCoordinateBounds - MGLCoordinateBoundsIsEmpty - MGLCoordinateBoundsMake - MGLCoordinateBoundsOffset - - MGLCoordinateFormatter - MGLCoordinateInCoordinateBounds - MGLCoordinateSpan - MGLCoordinateSpanEqualToCoordinateSpan @@ -93,3 +98,8 @@ custom_categories: - MGLDegreesFromRadians - MGLRadiansFromDegrees - MGLStringFromCoordinateBounds + - name: Formatters + children: + - MGLClockDirectionFormatter + - MGLCompassDirectionFormatter + - MGLCoordinateFormatter diff --git a/platform/macos/macos.xcodeproj/project.pbxproj b/platform/macos/macos.xcodeproj/project.pbxproj index 6f6aad243..fc9e0c74e 100644 --- a/platform/macos/macos.xcodeproj/project.pbxproj +++ b/platform/macos/macos.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 30E5781B1DAA857E0050F07E /* NSImage+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 30E578141DAA7D920050F07E /* NSImage+MGLAdditions.h */; }; 3508EC641D749D39009B0EE4 /* NSExpression+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3508EC621D749D39009B0EE4 /* NSExpression+MGLAdditions.h */; }; 3508EC651D749D39009B0EE4 /* NSExpression+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3508EC631D749D39009B0EE4 /* NSExpression+MGLAdditions.mm */; }; + 3526EABD1DF9B19800006B43 /* MGLCodingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3526EABC1DF9B19800006B43 /* MGLCodingTests.m */; }; 352742781D4C220900A1ECE6 /* MGLStyleValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 352742771D4C220900A1ECE6 /* MGLStyleValue.h */; settings = {ATTRIBUTES = (Public, ); }; }; 352742811D4C243B00A1ECE6 /* MGLSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 3527427F1D4C243B00A1ECE6 /* MGLSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; 352742821D4C243B00A1ECE6 /* MGLSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 352742801D4C243B00A1ECE6 /* MGLSource.mm */; }; @@ -34,6 +35,8 @@ 35602C001D3EA9B40050646F /* MGLForegroundStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 35602BFD1D3EA9B40050646F /* MGLForegroundStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 35602C011D3EA9B40050646F /* MGLForegroundStyleLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 35602BFE1D3EA9B40050646F /* MGLForegroundStyleLayer.m */; }; 35724FC41D630502002A4AB4 /* amsterdam.geojson in Resources */ = {isa = PBXBuildFile; fileRef = 358EB3AE1D61F0DB00E46D9C /* amsterdam.geojson */; }; + 359819591E02F611008FC139 /* NSCoder+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 359819571E02F611008FC139 /* NSCoder+MGLAdditions.h */; }; + 3598195A1E02F611008FC139 /* NSCoder+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 359819581E02F611008FC139 /* NSCoder+MGLAdditions.mm */; }; 3599A3E81DF70E2000E77FB2 /* MGLStyleValueTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3599A3E71DF70E2000E77FB2 /* MGLStyleValueTests.m */; }; 35C5D8471D6DD66D00E95907 /* NSComparisonPredicate+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 35C5D8431D6DD66D00E95907 /* NSComparisonPredicate+MGLAdditions.h */; }; 35C5D8481D6DD66D00E95907 /* NSComparisonPredicate+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 35C5D8441D6DD66D00E95907 /* NSComparisonPredicate+MGLAdditions.mm */; }; @@ -41,8 +44,6 @@ 35C5D84A1D6DD66D00E95907 /* NSCompoundPredicate+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 35C5D8461D6DD66D00E95907 /* NSCompoundPredicate+MGLAdditions.mm */; }; 35D65C5A1D65AD5500722C23 /* NSDate+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 35D65C581D65AD5500722C23 /* NSDate+MGLAdditions.h */; }; 35D65C5B1D65AD5500722C23 /* NSDate+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 35D65C591D65AD5500722C23 /* NSDate+MGLAdditions.mm */; }; - 4032C5C51DE1FE930062E8BD /* NSValue+MGLStyleEnumAttributeAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 4032C5B91DE1EEBA0062E8BD /* NSValue+MGLStyleEnumAttributeAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 4032C5C61DE1FE9B0062E8BD /* NSValue+MGLStyleEnumAttributeAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4032C5C31DE1FE810062E8BD /* NSValue+MGLStyleEnumAttributeAdditions.mm */; }; 4049C2A51DB6CE7F00B3F799 /* MGLPointCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = 4049C2A11DB6CE7800B3F799 /* MGLPointCollection.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4049C2AD1DB8020600B3F799 /* MGLPointCollection.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4049C2A71DB6D09B00B3F799 /* MGLPointCollection.mm */; }; 408AA85B1DAEECFE00022900 /* MGLShape_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 408AA85A1DAEECF100022900 /* MGLShape_Private.h */; }; @@ -67,7 +68,7 @@ DA0CD58E1CF56F5800A5F5A5 /* MGLFeatureTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA0CD58D1CF56F5800A5F5A5 /* MGLFeatureTests.mm */; }; DA2207BC1DC076940002F84D /* MGLStyleValueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA2207BB1DC076940002F84D /* MGLStyleValueTests.swift */; }; DA2784FE1DF03060001D5B8D /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DA2784FD1DF03060001D5B8D /* Media.xcassets */; }; - DA2DBBCB1D51E30A00D38FF9 /* MGLStyleLayerTests.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA2DBBCA1D51E30A00D38FF9 /* MGLStyleLayerTests.xib */; }; + DA29875A1E1A4290002299F5 /* MGLDocumentationExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA2987591E1A4290002299F5 /* MGLDocumentationExampleTests.swift */; }; DA35A2A41CC9EB1A00E826B2 /* MGLCoordinateFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = DA35A2A31CC9EB1A00E826B2 /* MGLCoordinateFormatter.h */; settings = {ATTRIBUTES = (Public, ); }; }; DA35A2A61CC9EB2700E826B2 /* MGLCoordinateFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2A51CC9EB2700E826B2 /* MGLCoordinateFormatter.m */; }; DA35A2A81CC9F41600E826B2 /* MGLCoordinateFormatterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2A71CC9F41600E826B2 /* MGLCoordinateFormatterTests.m */; }; @@ -79,6 +80,7 @@ DA35A2C21CCA9F4A00E826B2 /* MGLClockDirectionFormatterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2C11CCA9F4A00E826B2 /* MGLClockDirectionFormatterTests.m */; }; DA35A2CF1CCAAED300E826B2 /* NSValue+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DA35A2CD1CCAAED300E826B2 /* NSValue+MGLAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; DA35A2D01CCAAED300E826B2 /* NSValue+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2CE1CCAAED300E826B2 /* NSValue+MGLAdditions.m */; }; + DA35D08A1E1A631B007DED41 /* one-liner.json in Resources */ = {isa = PBXBuildFile; fileRef = DA35D0891E1A631B007DED41 /* one-liner.json */; }; DA551B821DB496AC0009AFAF /* MGLTileSource.h in Headers */ = {isa = PBXBuildFile; fileRef = DA551B7F1DB496AC0009AFAF /* MGLTileSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; DA551B831DB496AC0009AFAF /* MGLTileSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DA551B801DB496AC0009AFAF /* MGLTileSource_Private.h */; }; DA551B841DB496AC0009AFAF /* MGLTileSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA551B811DB496AC0009AFAF /* MGLTileSource.mm */; }; @@ -98,15 +100,14 @@ DA87A9981DC9D88400810D09 /* MGLShapeSourceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA87A9961DC9D88400810D09 /* MGLShapeSourceTests.mm */; }; DA87A9991DC9D88400810D09 /* MGLTileSetTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA87A9971DC9D88400810D09 /* MGLTileSetTests.mm */; }; DA87A99C1DC9D8DD00810D09 /* MGLShapeSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DA87A99B1DC9D8DD00810D09 /* MGLShapeSource_Private.h */; }; - DA87A99E1DC9DC2100810D09 /* MGLFilterTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 35C5D84B1D6DD75B00E95907 /* MGLFilterTests.mm */; }; + DA87A99E1DC9DC2100810D09 /* MGLPredicateTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 35C5D84B1D6DD75B00E95907 /* MGLPredicateTests.mm */; }; DA87A9A01DC9DC6200810D09 /* MGLValueEvaluator.h in Headers */ = {isa = PBXBuildFile; fileRef = DA87A99F1DC9DC6200810D09 /* MGLValueEvaluator.h */; }; - DA87A9A11DC9DCB400810D09 /* MGLRuntimeStylingHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8F257B1D51C5F40010E6B5 /* MGLRuntimeStylingHelper.m */; }; - DA87A9A21DC9DCF100810D09 /* MGLFillStyleLayerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8F25741D51C5F40010E6B5 /* MGLFillStyleLayerTests.m */; }; - DA87A9A31DCACC5000810D09 /* MGLRasterStyleLayerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8F25751D51C5F40010E6B5 /* MGLRasterStyleLayerTests.m */; }; - DA87A9A41DCACC5000810D09 /* MGLSymbolStyleLayerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8F25761D51C5F40010E6B5 /* MGLSymbolStyleLayerTests.m */; }; - DA87A9A51DCACC5000810D09 /* MGLLineStyleLayerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8F25771D51C5F40010E6B5 /* MGLLineStyleLayerTests.m */; }; - DA87A9A61DCACC5000810D09 /* MGLCircleStyleLayerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8F25781D51C5F40010E6B5 /* MGLCircleStyleLayerTests.m */; }; - DA87A9A71DCACC5000810D09 /* MGLBackgroundStyleLayerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8F25791D51C5F40010E6B5 /* MGLBackgroundStyleLayerTests.m */; }; + DA87A9A21DC9DCF100810D09 /* MGLFillStyleLayerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA8F25741D51C5F40010E6B5 /* MGLFillStyleLayerTests.mm */; }; + DA87A9A31DCACC5000810D09 /* MGLRasterStyleLayerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA8F25751D51C5F40010E6B5 /* MGLRasterStyleLayerTests.mm */; }; + DA87A9A41DCACC5000810D09 /* MGLSymbolStyleLayerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA8F25761D51C5F40010E6B5 /* MGLSymbolStyleLayerTests.mm */; }; + DA87A9A51DCACC5000810D09 /* MGLLineStyleLayerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA8F25771D51C5F40010E6B5 /* MGLLineStyleLayerTests.mm */; }; + DA87A9A61DCACC5000810D09 /* MGLCircleStyleLayerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA8F25781D51C5F40010E6B5 /* MGLCircleStyleLayerTests.mm */; }; + DA87A9A71DCACC5000810D09 /* MGLBackgroundStyleLayerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA8F25791D51C5F40010E6B5 /* MGLBackgroundStyleLayerTests.mm */; }; DA8933A51CCD287300E68420 /* MGLAnnotationCallout.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA8933A71CCD287300E68420 /* MGLAnnotationCallout.xib */; }; DA8933AE1CCD290700E68420 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = DA8933AB1CCD290700E68420 /* Localizable.strings */; }; DA8933B51CCD2C2500E68420 /* Foundation.strings in Resources */ = {isa = PBXBuildFile; fileRef = DA8933B31CCD2C2500E68420 /* Foundation.strings */; }; @@ -125,9 +126,11 @@ DA8F259C1D51CB000010E6B5 /* MGLStyleValue_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8F259B1D51CB000010E6B5 /* MGLStyleValue_Private.h */; }; DA8F25B21D51CB270010E6B5 /* NSValue+MGLStyleAttributeAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8F25A61D51CB270010E6B5 /* NSValue+MGLStyleAttributeAdditions.h */; }; DA8F25B31D51CB270010E6B5 /* NSValue+MGLStyleAttributeAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA8F25A71D51CB270010E6B5 /* NSValue+MGLStyleAttributeAdditions.mm */; }; + DAA1BB4A1E2D425C00ABB750 /* libmbgl-loop.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAA1BB491E2D425C00ABB750 /* libmbgl-loop.a */; }; DAA48EFD1D6A4731006A7E36 /* StyleLayerIconTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = DAA48EFC1D6A4731006A7E36 /* StyleLayerIconTransformer.m */; }; DAB2CCE51DF632ED001B2FE1 /* LimeGreenStyleLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = DAB2CCE41DF632ED001B2FE1 /* LimeGreenStyleLayer.m */; }; DAC2ABC51CC6D343006D18C4 /* MGLAnnotationImage_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DAC2ABC41CC6D343006D18C4 /* MGLAnnotationImage_Private.h */; }; + DACB0C391E18DFFD005DDBEA /* MGLStyle+MBXAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DACB0C381E18DFFD005DDBEA /* MGLStyle+MBXAdditions.m */; }; DACC22141CF3D3E200D220D9 /* MGLFeature.h in Headers */ = {isa = PBXBuildFile; fileRef = DACC22121CF3D3E200D220D9 /* MGLFeature.h */; settings = {ATTRIBUTES = (Public, ); }; }; DACC22151CF3D3E200D220D9 /* MGLFeature.mm in Sources */ = {isa = PBXBuildFile; fileRef = DACC22131CF3D3E200D220D9 /* MGLFeature.mm */; }; DACC22181CF3D4F700D220D9 /* MGLFeature_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DACC22171CF3D4F700D220D9 /* MGLFeature_Private.h */; }; @@ -207,12 +210,14 @@ DAE6C3D41CC34C9900DB3429 /* MGLOfflineRegionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DAE6C3CA1CC34BD800DB3429 /* MGLOfflineRegionTests.m */; }; DAE6C3D51CC34C9900DB3429 /* MGLOfflineStorageTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DAE6C3CB1CC34BD800DB3429 /* MGLOfflineStorageTests.m */; }; DAE6C3D61CC34C9900DB3429 /* MGLStyleTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DAE6C3CC1CC34BD800DB3429 /* MGLStyleTests.mm */; }; + DAE7DEC41E24549F007505A6 /* MGLNSStringAdditionsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DAE7DEC31E24549F007505A6 /* MGLNSStringAdditionsTests.m */; }; DAED385F1D62CED700D7640F /* NSURL+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DAED385D1D62CED700D7640F /* NSURL+MGLAdditions.h */; }; DAED38601D62CED700D7640F /* NSURL+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DAED385E1D62CED700D7640F /* NSURL+MGLAdditions.m */; }; DAEDC4321D6033F1000224FF /* MGLAttributionInfoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DAEDC4311D6033F1000224FF /* MGLAttributionInfoTests.m */; }; DAEDC4371D606291000224FF /* MGLAttributionButtonTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DAEDC4361D606291000224FF /* MGLAttributionButtonTests.m */; }; DAF0D80E1DFE0E5D00B28378 /* MGLPointCollection_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DAF0D80D1DFE0E5D00B28378 /* MGLPointCollection_Private.h */; }; DAF0D8161DFE6B1800B28378 /* MGLAttributionInfo_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DAF0D8151DFE6B1800B28378 /* MGLAttributionInfo_Private.h */; }; + DAF0D81C1DFF567C00B28378 /* MGLVectorSource+MBXAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DAF0D81B1DFF567C00B28378 /* MGLVectorSource+MBXAdditions.m */; }; DD0902B21DB1AC6400C5BDCE /* MGLNetworkConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = DD0902AF1DB1AC6400C5BDCE /* MGLNetworkConfiguration.m */; }; DD0902B31DB1AC6400C5BDCE /* MGLNetworkConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = DD0902B01DB1AC6400C5BDCE /* MGLNetworkConfiguration.h */; }; DD58A4C91D822C6700E1F038 /* MGLExpressionTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DD58A4C71D822C6200E1F038 /* MGLExpressionTests.mm */; }; @@ -253,6 +258,7 @@ 30E578141DAA7D920050F07E /* NSImage+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSImage+MGLAdditions.h"; path = "src/NSImage+MGLAdditions.h"; sourceTree = SOURCE_ROOT; }; 3508EC621D749D39009B0EE4 /* NSExpression+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSExpression+MGLAdditions.h"; sourceTree = ""; }; 3508EC631D749D39009B0EE4 /* NSExpression+MGLAdditions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSExpression+MGLAdditions.mm"; sourceTree = ""; }; + 3526EABC1DF9B19800006B43 /* MGLCodingTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLCodingTests.m; path = ../../darwin/test/MGLCodingTests.m; sourceTree = ""; }; 352742771D4C220900A1ECE6 /* MGLStyleValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLStyleValue.h; sourceTree = ""; }; 3527427F1D4C243B00A1ECE6 /* MGLSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLSource.h; sourceTree = ""; }; 352742801D4C243B00A1ECE6 /* MGLSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLSource.mm; sourceTree = ""; }; @@ -277,18 +283,16 @@ 35602BFD1D3EA9B40050646F /* MGLForegroundStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLForegroundStyleLayer.h; sourceTree = ""; }; 35602BFE1D3EA9B40050646F /* MGLForegroundStyleLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLForegroundStyleLayer.m; sourceTree = ""; }; 358EB3AE1D61F0DB00E46D9C /* amsterdam.geojson */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = amsterdam.geojson; path = ../../darwin/test/amsterdam.geojson; sourceTree = ""; }; + 359819571E02F611008FC139 /* NSCoder+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSCoder+MGLAdditions.h"; sourceTree = ""; }; + 359819581E02F611008FC139 /* NSCoder+MGLAdditions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSCoder+MGLAdditions.mm"; sourceTree = ""; }; 3599A3E71DF70E2000E77FB2 /* MGLStyleValueTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLStyleValueTests.m; sourceTree = ""; }; 35C5D8431D6DD66D00E95907 /* NSComparisonPredicate+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSComparisonPredicate+MGLAdditions.h"; sourceTree = ""; }; 35C5D8441D6DD66D00E95907 /* NSComparisonPredicate+MGLAdditions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSComparisonPredicate+MGLAdditions.mm"; sourceTree = ""; }; 35C5D8451D6DD66D00E95907 /* NSCompoundPredicate+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSCompoundPredicate+MGLAdditions.h"; sourceTree = ""; }; 35C5D8461D6DD66D00E95907 /* NSCompoundPredicate+MGLAdditions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSCompoundPredicate+MGLAdditions.mm"; sourceTree = ""; }; - 35C5D84B1D6DD75B00E95907 /* MGLFilterTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLFilterTests.mm; sourceTree = ""; }; + 35C5D84B1D6DD75B00E95907 /* MGLPredicateTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLPredicateTests.mm; path = ../../darwin/test/MGLPredicateTests.mm; sourceTree = ""; }; 35D65C581D65AD5500722C23 /* NSDate+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDate+MGLAdditions.h"; sourceTree = ""; }; 35D65C591D65AD5500722C23 /* NSDate+MGLAdditions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSDate+MGLAdditions.mm"; sourceTree = ""; }; - 4032C5B91DE1EEBA0062E8BD /* NSValue+MGLStyleEnumAttributeAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSValue+MGLStyleEnumAttributeAdditions.h"; sourceTree = ""; }; - 4032C5BA1DE1EECB0062E8BD /* NSValue+MGLStyleEnumAttributeAdditions.h.ejs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "NSValue+MGLStyleEnumAttributeAdditions.h.ejs"; sourceTree = ""; }; - 4032C5C31DE1FE810062E8BD /* NSValue+MGLStyleEnumAttributeAdditions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSValue+MGLStyleEnumAttributeAdditions.mm"; sourceTree = ""; }; - 4032C5C71DE1FEAB0062E8BD /* NSValue+MGLStyleEnumAttributeAdditions.mm.ejs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "NSValue+MGLStyleEnumAttributeAdditions.mm.ejs"; sourceTree = ""; }; 4049C2A11DB6CE7800B3F799 /* MGLPointCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLPointCollection.h; sourceTree = ""; }; 4049C2A71DB6D09B00B3F799 /* MGLPointCollection.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLPointCollection.mm; sourceTree = ""; }; 405C03961DB0004E001AC280 /* NSImage+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSImage+MGLAdditions.h"; sourceTree = ""; }; @@ -318,7 +322,7 @@ DA2207BA1DC076930002F84D /* test-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "test-Bridging-Header.h"; sourceTree = ""; }; DA2207BB1DC076940002F84D /* MGLStyleValueTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MGLStyleValueTests.swift; sourceTree = ""; }; DA2784FD1DF03060001D5B8D /* Media.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Media.xcassets; path = ../../darwin/test/Media.xcassets; sourceTree = ""; }; - DA2DBBCA1D51E30A00D38FF9 /* MGLStyleLayerTests.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MGLStyleLayerTests.xib; sourceTree = ""; }; + DA2987591E1A4290002299F5 /* MGLDocumentationExampleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MGLDocumentationExampleTests.swift; path = ../../darwin/test/MGLDocumentationExampleTests.swift; sourceTree = ""; }; DA35A2A31CC9EB1A00E826B2 /* MGLCoordinateFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLCoordinateFormatter.h; sourceTree = ""; }; DA35A2A51CC9EB2700E826B2 /* MGLCoordinateFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLCoordinateFormatter.m; sourceTree = ""; }; DA35A2A71CC9F41600E826B2 /* MGLCoordinateFormatterTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLCoordinateFormatterTests.m; path = ../../darwin/test/MGLCoordinateFormatterTests.m; sourceTree = ""; }; @@ -330,6 +334,7 @@ DA35A2C11CCA9F4A00E826B2 /* MGLClockDirectionFormatterTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLClockDirectionFormatterTests.m; path = ../../darwin/test/MGLClockDirectionFormatterTests.m; sourceTree = ""; }; DA35A2CD1CCAAED300E826B2 /* NSValue+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSValue+MGLAdditions.h"; sourceTree = ""; }; DA35A2CE1CCAAED300E826B2 /* NSValue+MGLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSValue+MGLAdditions.m"; sourceTree = ""; }; + DA35D0891E1A631B007DED41 /* one-liner.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = "one-liner.json"; path = "../../darwin/test/one-liner.json"; sourceTree = ""; }; DA551B7F1DB496AC0009AFAF /* MGLTileSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLTileSource.h; sourceTree = ""; }; DA551B801DB496AC0009AFAF /* MGLTileSource_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLTileSource_Private.h; sourceTree = ""; }; DA551B811DB496AC0009AFAF /* MGLTileSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLTileSource.mm; sourceTree = ""; }; @@ -360,14 +365,12 @@ DA8933AC1CCD290700E68420 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = ""; }; DA8933B41CCD2C2500E68420 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Foundation.strings; sourceTree = ""; }; DA8933B71CCD2C2D00E68420 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Foundation.stringsdict; sourceTree = ""; }; - DA8F25741D51C5F40010E6B5 /* MGLFillStyleLayerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLFillStyleLayerTests.m; path = ../../darwin/test/MGLFillStyleLayerTests.m; sourceTree = ""; }; - DA8F25751D51C5F40010E6B5 /* MGLRasterStyleLayerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLRasterStyleLayerTests.m; path = ../../darwin/test/MGLRasterStyleLayerTests.m; sourceTree = ""; }; - DA8F25761D51C5F40010E6B5 /* MGLSymbolStyleLayerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLSymbolStyleLayerTests.m; path = ../../darwin/test/MGLSymbolStyleLayerTests.m; sourceTree = ""; }; - DA8F25771D51C5F40010E6B5 /* MGLLineStyleLayerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLLineStyleLayerTests.m; path = ../../darwin/test/MGLLineStyleLayerTests.m; sourceTree = ""; }; - DA8F25781D51C5F40010E6B5 /* MGLCircleStyleLayerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLCircleStyleLayerTests.m; path = ../../darwin/test/MGLCircleStyleLayerTests.m; sourceTree = ""; }; - DA8F25791D51C5F40010E6B5 /* MGLBackgroundStyleLayerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLBackgroundStyleLayerTests.m; path = ../../darwin/test/MGLBackgroundStyleLayerTests.m; sourceTree = ""; }; - DA8F257A1D51C5F40010E6B5 /* MGLRuntimeStylingHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MGLRuntimeStylingHelper.h; path = ../../darwin/test/MGLRuntimeStylingHelper.h; sourceTree = ""; }; - DA8F257B1D51C5F40010E6B5 /* MGLRuntimeStylingHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLRuntimeStylingHelper.m; path = ../../darwin/test/MGLRuntimeStylingHelper.m; sourceTree = ""; }; + DA8F25741D51C5F40010E6B5 /* MGLFillStyleLayerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLFillStyleLayerTests.mm; sourceTree = ""; }; + DA8F25751D51C5F40010E6B5 /* MGLRasterStyleLayerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLRasterStyleLayerTests.mm; sourceTree = ""; }; + DA8F25761D51C5F40010E6B5 /* MGLSymbolStyleLayerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLSymbolStyleLayerTests.mm; sourceTree = ""; }; + DA8F25771D51C5F40010E6B5 /* MGLLineStyleLayerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLLineStyleLayerTests.mm; sourceTree = ""; }; + DA8F25781D51C5F40010E6B5 /* MGLCircleStyleLayerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLCircleStyleLayerTests.mm; sourceTree = ""; }; + DA8F25791D51C5F40010E6B5 /* MGLBackgroundStyleLayerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLBackgroundStyleLayerTests.mm; sourceTree = ""; }; DA8F25851D51C9E10010E6B5 /* MGLBackgroundStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLBackgroundStyleLayer.h; sourceTree = ""; }; DA8F25861D51C9E10010E6B5 /* MGLBackgroundStyleLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLBackgroundStyleLayer.mm; sourceTree = ""; }; DA8F25891D51CA540010E6B5 /* MGLLineStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLLineStyleLayer.h; sourceTree = ""; }; @@ -382,14 +385,17 @@ DA8F259B1D51CB000010E6B5 /* MGLStyleValue_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLStyleValue_Private.h; sourceTree = ""; }; DA8F25A61D51CB270010E6B5 /* NSValue+MGLStyleAttributeAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSValue+MGLStyleAttributeAdditions.h"; sourceTree = ""; }; DA8F25A71D51CB270010E6B5 /* NSValue+MGLStyleAttributeAdditions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSValue+MGLStyleAttributeAdditions.mm"; sourceTree = ""; }; - DA8F25B51D51D2240010E6B5 /* MGLStyleLayerTests.m.ejs */ = {isa = PBXFileReference; lastKnownFileType = text; name = MGLStyleLayerTests.m.ejs; path = ../test/MGLStyleLayerTests.m.ejs; sourceTree = ""; }; + DA8F25B51D51D2240010E6B5 /* MGLStyleLayerTests.mm.ejs */ = {isa = PBXFileReference; lastKnownFileType = text; name = MGLStyleLayerTests.mm.ejs; path = ../test/MGLStyleLayerTests.mm.ejs; sourceTree = ""; }; DA8F25B61D51D2240010E6B5 /* MGLStyleLayer.h.ejs */ = {isa = PBXFileReference; lastKnownFileType = text; path = MGLStyleLayer.h.ejs; sourceTree = ""; }; DA8F25B71D51D2240010E6B5 /* MGLStyleLayer.mm.ejs */ = {isa = PBXFileReference; lastKnownFileType = text; path = MGLStyleLayer.mm.ejs; sourceTree = ""; }; + DAA1BB491E2D425C00ABB750 /* libmbgl-loop.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libmbgl-loop.a"; path = "../../build/macos/Debug/libmbgl-loop.a"; sourceTree = ""; }; DAA48EFB1D6A4731006A7E36 /* StyleLayerIconTransformer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StyleLayerIconTransformer.h; sourceTree = ""; }; DAA48EFC1D6A4731006A7E36 /* StyleLayerIconTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StyleLayerIconTransformer.m; sourceTree = ""; }; DAB2CCE31DF632ED001B2FE1 /* LimeGreenStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LimeGreenStyleLayer.h; sourceTree = ""; }; DAB2CCE41DF632ED001B2FE1 /* LimeGreenStyleLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LimeGreenStyleLayer.m; sourceTree = ""; }; DAC2ABC41CC6D343006D18C4 /* MGLAnnotationImage_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAnnotationImage_Private.h; sourceTree = ""; }; + DACB0C371E18DFFD005DDBEA /* MGLStyle+MBXAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MGLStyle+MBXAdditions.h"; sourceTree = ""; }; + DACB0C381E18DFFD005DDBEA /* MGLStyle+MBXAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MGLStyle+MBXAdditions.m"; sourceTree = ""; }; DACC22121CF3D3E200D220D9 /* MGLFeature.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLFeature.h; sourceTree = ""; }; DACC22131CF3D3E200D220D9 /* MGLFeature.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLFeature.mm; sourceTree = ""; }; DACC22171CF3D4F700D220D9 /* MGLFeature_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLFeature_Private.h; sourceTree = ""; }; @@ -475,12 +481,15 @@ DAE6C3CA1CC34BD800DB3429 /* MGLOfflineRegionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLOfflineRegionTests.m; path = ../../darwin/test/MGLOfflineRegionTests.m; sourceTree = ""; }; DAE6C3CB1CC34BD800DB3429 /* MGLOfflineStorageTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLOfflineStorageTests.m; path = ../../darwin/test/MGLOfflineStorageTests.m; sourceTree = ""; }; DAE6C3CC1CC34BD800DB3429 /* MGLStyleTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLStyleTests.mm; path = ../../darwin/test/MGLStyleTests.mm; sourceTree = ""; }; + DAE7DEC31E24549F007505A6 /* MGLNSStringAdditionsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLNSStringAdditionsTests.m; path = ../../darwin/test/MGLNSStringAdditionsTests.m; sourceTree = ""; }; DAED385D1D62CED700D7640F /* NSURL+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSURL+MGLAdditions.h"; sourceTree = ""; }; DAED385E1D62CED700D7640F /* NSURL+MGLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSURL+MGLAdditions.m"; sourceTree = ""; }; DAEDC4311D6033F1000224FF /* MGLAttributionInfoTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLAttributionInfoTests.m; path = ../../darwin/test/MGLAttributionInfoTests.m; sourceTree = ""; }; DAEDC4361D606291000224FF /* MGLAttributionButtonTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLAttributionButtonTests.m; sourceTree = ""; }; DAF0D80D1DFE0E5D00B28378 /* MGLPointCollection_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLPointCollection_Private.h; sourceTree = ""; }; DAF0D8151DFE6B1800B28378 /* MGLAttributionInfo_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAttributionInfo_Private.h; sourceTree = ""; }; + DAF0D81A1DFF567C00B28378 /* MGLVectorSource+MBXAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MGLVectorSource+MBXAdditions.h"; sourceTree = ""; }; + DAF0D81B1DFF567C00B28378 /* MGLVectorSource+MBXAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MGLVectorSource+MBXAdditions.m"; sourceTree = ""; }; DD0902AF1DB1AC6400C5BDCE /* MGLNetworkConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLNetworkConfiguration.m; sourceTree = ""; }; DD0902B01DB1AC6400C5BDCE /* MGLNetworkConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLNetworkConfiguration.h; sourceTree = ""; }; DD58A4C71D822C6200E1F038 /* MGLExpressionTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLExpressionTests.mm; path = ../../darwin/test/MGLExpressionTests.mm; sourceTree = ""; }; @@ -511,6 +520,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + DAA1BB4A1E2D425C00ABB750 /* libmbgl-loop.a in Frameworks */, DAE0DD7A1D5F015A005A6BB1 /* libmbgl-core.a in Frameworks */, DAE6C3321CC30DB200DB3429 /* Mapbox.framework in Frameworks */, ); @@ -550,8 +560,6 @@ 352742791D4C235C00A1ECE6 /* Categories */ = { isa = PBXGroup; children = ( - 4032C5B91DE1EEBA0062E8BD /* NSValue+MGLStyleEnumAttributeAdditions.h */, - 4032C5C31DE1FE810062E8BD /* NSValue+MGLStyleEnumAttributeAdditions.mm */, DA8F25A61D51CB270010E6B5 /* NSValue+MGLStyleAttributeAdditions.h */, DA8F25A71D51CB270010E6B5 /* NSValue+MGLStyleAttributeAdditions.mm */, ); @@ -629,6 +637,10 @@ DA839E9B1CC2E3400062CAFB /* MapDocument.h */, DA839E9C1CC2E3400062CAFB /* MapDocument.m */, DA839E9E1CC2E3400062CAFB /* MapDocument.xib */, + DACB0C371E18DFFD005DDBEA /* MGLStyle+MBXAdditions.h */, + DACB0C381E18DFFD005DDBEA /* MGLStyle+MBXAdditions.m */, + DAF0D81A1DFF567C00B28378 /* MGLVectorSource+MBXAdditions.h */, + DAF0D81B1DFF567C00B28378 /* MGLVectorSource+MBXAdditions.m */, DAE6C2E91CC3050F00DB3429 /* OfflinePackNameValueTransformer.h */, DAE6C2EA1CC3050F00DB3429 /* OfflinePackNameValueTransformer.m */, DAA48EFB1D6A4731006A7E36 /* StyleLayerIconTransformer.h */, @@ -691,15 +703,12 @@ 40E1601A1DF216E6005EA6D9 /* MGLStyleLayerTests.h */, 40E1601B1DF216E6005EA6D9 /* MGLStyleLayerTests.m */, DA2207BA1DC076930002F84D /* test-Bridging-Header.h */, - DA8F25741D51C5F40010E6B5 /* MGLFillStyleLayerTests.m */, - DA8F25751D51C5F40010E6B5 /* MGLRasterStyleLayerTests.m */, - DA8F25761D51C5F40010E6B5 /* MGLSymbolStyleLayerTests.m */, - DA8F25771D51C5F40010E6B5 /* MGLLineStyleLayerTests.m */, - DA8F25781D51C5F40010E6B5 /* MGLCircleStyleLayerTests.m */, - DA8F25791D51C5F40010E6B5 /* MGLBackgroundStyleLayerTests.m */, - DA8F257A1D51C5F40010E6B5 /* MGLRuntimeStylingHelper.h */, - DA8F257B1D51C5F40010E6B5 /* MGLRuntimeStylingHelper.m */, - DA2DBBCA1D51E30A00D38FF9 /* MGLStyleLayerTests.xib */, + DA8F25741D51C5F40010E6B5 /* MGLFillStyleLayerTests.mm */, + DA8F25751D51C5F40010E6B5 /* MGLRasterStyleLayerTests.mm */, + DA8F25761D51C5F40010E6B5 /* MGLSymbolStyleLayerTests.mm */, + DA8F25771D51C5F40010E6B5 /* MGLLineStyleLayerTests.mm */, + DA8F25781D51C5F40010E6B5 /* MGLCircleStyleLayerTests.mm */, + DA8F25791D51C5F40010E6B5 /* MGLBackgroundStyleLayerTests.mm */, ); name = Layers; sourceTree = ""; @@ -709,7 +718,6 @@ children = ( DA8F257C1D51C5F40010E6B5 /* Layers */, DA87A99A1DC9D88800810D09 /* Sources */, - 35C5D84B1D6DD75B00E95907 /* MGLFilterTests.mm */, 353722EB1DF850ED004D2F3F /* MGLStyleValueTests.h */, 3599A3E71DF70E2000E77FB2 /* MGLStyleValueTests.m */, DA2207BB1DC076940002F84D /* MGLStyleValueTests.swift */, @@ -723,9 +731,7 @@ children = ( DA8F25B61D51D2240010E6B5 /* MGLStyleLayer.h.ejs */, DA8F25B71D51D2240010E6B5 /* MGLStyleLayer.mm.ejs */, - DA8F25B51D51D2240010E6B5 /* MGLStyleLayerTests.m.ejs */, - 4032C5BA1DE1EECB0062E8BD /* NSValue+MGLStyleEnumAttributeAdditions.h.ejs */, - 4032C5C71DE1FEAB0062E8BD /* NSValue+MGLStyleEnumAttributeAdditions.mm.ejs */, + DA8F25B51D51D2240010E6B5 /* MGLStyleLayerTests.mm.ejs */, ); name = "Foundation Templates"; path = ../../darwin/src; @@ -815,6 +821,8 @@ 40B77E421DB11BB0003DA2FE /* NSArray+MGLAdditions.mm */, DAE6C37D1CC31E2A00DB3429 /* NSBundle+MGLAdditions.h */, DAE6C37E1CC31E2A00DB3429 /* NSBundle+MGLAdditions.m */, + 359819571E02F611008FC139 /* NSCoder+MGLAdditions.h */, + 359819581E02F611008FC139 /* NSCoder+MGLAdditions.mm */, 35C5D8431D6DD66D00E95907 /* NSComparisonPredicate+MGLAdditions.h */, 35C5D8441D6DD66D00E95907 /* NSComparisonPredicate+MGLAdditions.mm */, 35C5D8451D6DD66D00E95907 /* NSCompoundPredicate+MGLAdditions.h */, @@ -843,6 +851,7 @@ DAE6C31E1CC308BC00DB3429 /* Frameworks */ = { isa = PBXGroup; children = ( + DAA1BB491E2D425C00ABB750 /* libmbgl-loop.a */, 558F18211D0B13B000123F46 /* libmbgl-loop.a */, 5548BE7B1D0ACBBD005DDE81 /* libmbgl-loop-darwin.a */, 5548BE791D0ACBB2005DDE81 /* libmbgl-loop-darwin.a */, @@ -877,18 +886,23 @@ DAEDC4311D6033F1000224FF /* MGLAttributionInfoTests.m */, DAEDC4361D606291000224FF /* MGLAttributionButtonTests.m */, DA35A2C11CCA9F4A00E826B2 /* MGLClockDirectionFormatterTests.m */, + 3526EABC1DF9B19800006B43 /* MGLCodingTests.m */, DA35A2B51CCA14D700E826B2 /* MGLCompassDirectionFormatterTests.m */, DA35A2A71CC9F41600E826B2 /* MGLCoordinateFormatterTests.m */, + DA2987591E1A4290002299F5 /* MGLDocumentationExampleTests.swift */, + DD58A4C71D822C6200E1F038 /* MGLExpressionTests.mm */, DA0CD58D1CF56F5800A5F5A5 /* MGLFeatureTests.mm */, DAE6C3C81CC34BD800DB3429 /* MGLGeometryTests.mm */, + DAE7DEC31E24549F007505A6 /* MGLNSStringAdditionsTests.m */, DAE6C3C91CC34BD800DB3429 /* MGLOfflinePackTests.m */, DAE6C3CA1CC34BD800DB3429 /* MGLOfflineRegionTests.m */, DAE6C3CB1CC34BD800DB3429 /* MGLOfflineStorageTests.m */, + 35C5D84B1D6DD75B00E95907 /* MGLPredicateTests.mm */, DAE6C3CC1CC34BD800DB3429 /* MGLStyleTests.mm */, - DD58A4C71D822C6200E1F038 /* MGLExpressionTests.mm */, 556660D51E1D07E400E2C41B /* MGLVersionNumber.m */, DAE6C33A1CC30DB200DB3429 /* Info.plist */, DA2784FD1DF03060001D5B8D /* Media.xcassets */, + DA35D0891E1A631B007DED41 /* one-liner.json */, ); name = "SDK Tests"; path = test; @@ -984,7 +998,6 @@ 352742781D4C220900A1ECE6 /* MGLStyleValue.h in Headers */, DAE6C35E1CC31E0400DB3429 /* MGLMultiPoint.h in Headers */, 35602BFF1D3EA9B40050646F /* MGLStyleLayer_Private.h in Headers */, - 4032C5C51DE1FE930062E8BD /* NSValue+MGLStyleEnumAttributeAdditions.h in Headers */, DAF0D8161DFE6B1800B28378 /* MGLAttributionInfo_Private.h in Headers */, DAE6C3971CC31E2A00DB3429 /* NSBundle+MGLAdditions.h in Headers */, DAED385F1D62CED700D7640F /* NSURL+MGLAdditions.h in Headers */, @@ -1019,6 +1032,7 @@ DAE6C39A1CC31E2A00DB3429 /* NSProcessInfo+MGLAdditions.h in Headers */, DA8F258B1D51CA540010E6B5 /* MGLLineStyleLayer.h in Headers */, DA8F25B21D51CB270010E6B5 /* NSValue+MGLStyleAttributeAdditions.h in Headers */, + 359819591E02F611008FC139 /* NSCoder+MGLAdditions.h in Headers */, DAE6C38E1CC31E2A00DB3429 /* MGLOfflineStorage_Private.h in Headers */, 408AA8661DAEEE3600022900 /* MGLPolyline+MGLAdditions.h in Headers */, DA87A9A01DC9DC6200810D09 /* MGLValueEvaluator.h in Headers */, @@ -1210,7 +1224,7 @@ files = ( 35724FC41D630502002A4AB4 /* amsterdam.geojson in Resources */, DA2784FE1DF03060001D5B8D /* Media.xcassets in Resources */, - DA2DBBCB1D51E30A00D38FF9 /* MGLStyleLayerTests.xib in Resources */, + DA35D08A1E1A631B007DED41 /* one-liner.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1227,8 +1241,10 @@ DAE6C2EE1CC3050F00DB3429 /* LocationCoordinate2DTransformer.m in Sources */, DAB2CCE51DF632ED001B2FE1 /* LimeGreenStyleLayer.m in Sources */, DAE6C2F11CC3050F00DB3429 /* TimeIntervalTransformer.m in Sources */, + DACB0C391E18DFFD005DDBEA /* MGLStyle+MBXAdditions.m in Sources */, DA839E9A1CC2E3400062CAFB /* main.m in Sources */, DA839E971CC2E3400062CAFB /* AppDelegate.m in Sources */, + DAF0D81C1DFF567C00B28378 /* MGLVectorSource+MBXAdditions.m in Sources */, DAE6C2F01CC3050F00DB3429 /* OfflinePackNameValueTransformer.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1255,7 +1271,7 @@ DAE6C3931CC31E2A00DB3429 /* MGLShape.mm in Sources */, 352742861D4C244700A1ECE6 /* MGLRasterSource.mm in Sources */, DAE6C39D1CC31E2A00DB3429 /* NSString+MGLAdditions.m in Sources */, - 4032C5C61DE1FE9B0062E8BD /* NSValue+MGLStyleEnumAttributeAdditions.mm in Sources */, + 3598195A1E02F611008FC139 /* NSCoder+MGLAdditions.mm in Sources */, DAE6C3941CC31E2A00DB3429 /* MGLStyle.mm in Sources */, DAE6C3871CC31E2A00DB3429 /* MGLGeometry.mm in Sources */, 3527428E1D4C24AB00A1ECE6 /* MGLCircleStyleLayer.mm in Sources */, @@ -1307,25 +1323,27 @@ files = ( DA35A2C21CCA9F4A00E826B2 /* MGLClockDirectionFormatterTests.m in Sources */, DAE6C3D41CC34C9900DB3429 /* MGLOfflineRegionTests.m in Sources */, - DA87A9A11DC9DCB400810D09 /* MGLRuntimeStylingHelper.m in Sources */, DAE6C3D61CC34C9900DB3429 /* MGLStyleTests.mm in Sources */, DAEDC4371D606291000224FF /* MGLAttributionButtonTests.m in Sources */, DA35A2B61CCA14D700E826B2 /* MGLCompassDirectionFormatterTests.m in Sources */, DAE6C3D21CC34C9900DB3429 /* MGLGeometryTests.mm in Sources */, - DA87A9A41DCACC5000810D09 /* MGLSymbolStyleLayerTests.m in Sources */, + DA87A9A41DCACC5000810D09 /* MGLSymbolStyleLayerTests.mm in Sources */, DAE6C3D51CC34C9900DB3429 /* MGLOfflineStorageTests.m in Sources */, 40E1601D1DF217D6005EA6D9 /* MGLStyleLayerTests.m in Sources */, - DA87A9A61DCACC5000810D09 /* MGLCircleStyleLayerTests.m in Sources */, - DA87A99E1DC9DC2100810D09 /* MGLFilterTests.mm in Sources */, + DA87A9A61DCACC5000810D09 /* MGLCircleStyleLayerTests.mm in Sources */, + DA87A99E1DC9DC2100810D09 /* MGLPredicateTests.mm in Sources */, DD58A4C91D822C6700E1F038 /* MGLExpressionTests.mm in Sources */, - DA87A9A71DCACC5000810D09 /* MGLBackgroundStyleLayerTests.m in Sources */, + DA87A9A71DCACC5000810D09 /* MGLBackgroundStyleLayerTests.mm in Sources */, + DA29875A1E1A4290002299F5 /* MGLDocumentationExampleTests.swift in Sources */, DAE6C3D31CC34C9900DB3429 /* MGLOfflinePackTests.m in Sources */, - DA87A9A51DCACC5000810D09 /* MGLLineStyleLayerTests.m in Sources */, - DA87A9A31DCACC5000810D09 /* MGLRasterStyleLayerTests.m in Sources */, + DA87A9A51DCACC5000810D09 /* MGLLineStyleLayerTests.mm in Sources */, + DA87A9A31DCACC5000810D09 /* MGLRasterStyleLayerTests.mm in Sources */, DA87A9991DC9D88400810D09 /* MGLTileSetTests.mm in Sources */, DA35A2A81CC9F41600E826B2 /* MGLCoordinateFormatterTests.m in Sources */, + DAE7DEC41E24549F007505A6 /* MGLNSStringAdditionsTests.m in Sources */, DA87A9981DC9D88400810D09 /* MGLShapeSourceTests.mm in Sources */, - DA87A9A21DC9DCF100810D09 /* MGLFillStyleLayerTests.m in Sources */, + 3526EABD1DF9B19800006B43 /* MGLCodingTests.m in Sources */, + DA87A9A21DC9DCF100810D09 /* MGLFillStyleLayerTests.mm in Sources */, 3599A3E81DF70E2000E77FB2 /* MGLStyleValueTests.m in Sources */, DAEDC4321D6033F1000224FF /* MGLAttributionInfoTests.m in Sources */, DA0CD58E1CF56F5800A5F5A5 /* MGLFeatureTests.mm in Sources */, @@ -1615,6 +1633,7 @@ HEADER_SEARCH_PATHS = "$(mbgl_core_INCLUDE_DIRECTORIES)"; INFOPLIST_FILE = test/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + OTHER_CFLAGS = "-fvisibility=hidden"; OTHER_CPLUSPLUSFLAGS = ( "$(OTHER_CFLAGS)", "$(variant_cflags)", @@ -1638,6 +1657,7 @@ HEADER_SEARCH_PATHS = "$(mbgl_core_INCLUDE_DIRECTORIES)"; INFOPLIST_FILE = test/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + OTHER_CFLAGS = "-fvisibility=hidden"; OTHER_CPLUSPLUSFLAGS = ( "$(OTHER_CFLAGS)", "$(variant_cflags)", diff --git a/platform/macos/screenshot.png b/platform/macos/screenshot.png deleted file mode 100644 index 3bf0c46ab..000000000 Binary files a/platform/macos/screenshot.png and /dev/null differ diff --git a/platform/macos/scripts/deploy-packages.sh b/platform/macos/scripts/deploy-packages.sh index c13740174..d0c545f8f 100755 --- a/platform/macos/scripts/deploy-packages.sh +++ b/platform/macos/scripts/deploy-packages.sh @@ -4,32 +4,29 @@ set -e set -o pipefail set -u -usage() { -cat <&2 echo -e "\033[1m\033[36m* $@\033[0m"; } +function finish { >&2 echo -en "\033[0m"; } +trap finish EXIT buildPackageStyle() { local package=$1 style="" if [[ ${#} -eq 2 ]]; then style="$2" fi - echo "make ${package} ${style}" + step "Building: make ${package} ${style}" make ${package} - echo "publish ${package} with ${style}" + step "Publishing ${package} with ${style}" local file_name="" if [ -z ${style} ] then @@ -37,47 +34,86 @@ buildPackageStyle() { else file_name=mapbox-macos-sdk-${PUBLISH_VERSION}-${style}.zip fi - echo "compress ${file_name}" + step "Compressing ${file_name}…" cd build/macos/pkg - rm -f ../${file_name} - zip -r ../${file_name} * + rm -f ../deploy/${file_name} + zip -r ../deploy/${file_name} * cd - if [[ "${GITHUB_RELEASE}" == true ]]; then - echo "publish ${file_name} to GitHub" - github-release --verbose upload --tag "macos-v${PUBLISH_VERSION}" --name ${file_name} --file "build/macos/${file_name}" - fi + echo "Uploading ${file_name} to GitHub" + github-release upload \ + --tag "macos-v${PUBLISH_VERSION}" \ + --name ${file_name} \ + --file "${BINARY_DIRECTORY}/${file_name}" > /dev/null + fi } -if [ ${#} -eq 0 -o ${#} -gt 3 ]; then - usage - exit 1 -fi - export TRAVIS_REPO_SLUG=mapbox-gl-native -export PUBLISH_VERSION=$1 export GITHUB_USER=mapbox export GITHUB_REPO=mapbox-gl-native export BUILDTYPE=Release -BINARY_DIRECTORY=$2 +VERSION_TAG=${VERSION_TAG:-''} +PUBLISH_VERSION= +BINARY_DIRECTORY=${BINARY_DIRECTORY:-build/macos/deploy} +GITHUB_RELEASE=${GITHUB_RELEASE:-true} PUBLISH_PRE_FLAG='' -GITHUB_RELEASE=false -echo "Deploying version ${PUBLISH_VERSION}..." +if [[ ${GITHUB_RELEASE} = "true" ]]; then + GITHUB_RELEASE=true # Assign bool, not just a string + + if [[ -z `which github-release` ]]; then + step "Installing github-release…" + brew install github-release + if [ -z `which github-release` ]; then + echo "Unable to install github-release. See: https://github.com/aktau/github-release" + exit 1 + fi + fi +fi + +if [[ -z ${VERSION_TAG} ]]; then + step "Determining version number from most recent relevant git tag…" + VERSION_TAG=$( git describe --tags --match=macos-v*.*.* --abbrev=0 ) + echo "Found tag: ${VERSION_TAG}" +fi + +if [[ $( echo ${VERSION_TAG} | grep --invert-match macos-v ) ]]; then + echo "Error: ${VERSION_TAG} is not a valid macOS version tag" + echo "VERSION_TAG should be in format: macos-vX.X.X-pre.X" + exit 1 +fi + +if [[ $( wget --spider -O- https://api.github.com/repos/${GITHUB_USER}/${GITHUB_REPO}/releases/tags/${VERSION_TAG} 2>&1 | grep -c "404 Not Found" ) == 0 ]]; then + echo "Error: ${VERSION_TAG} has already been published on GitHub" + echo "See: https://github.com/${GITHUB_USER}/${GITHUB_REPO}/releases/tag/${VERSION_TAG}" + exit 1 +fi + +PUBLISH_VERSION=$( echo ${VERSION_TAG} | sed 's/^macos-v//' ) +git checkout ${VERSION_TAG} + +step "Deploying version ${PUBLISH_VERSION}…" if [[ ${#} -eq 3 && $3 == "-g" ]]; then GITHUB_RELEASE=true fi make clean && make distclean +mkdir -p ${BINARY_DIRECTORY} if [[ "${GITHUB_RELEASE}" == true ]]; then - echo "Create GitHub release..." + step "Create GitHub release…" if [[ $( echo ${PUBLISH_VERSION} | awk '/[0-9]-/' ) ]]; then PUBLISH_PRE_FLAG='--pre-release' fi - github-release --verbose release --tag "macos-v${PUBLISH_VERSION}" --name "macos-v${PUBLISH_VERSION}" --draft ${PUBLISH_PRE_FLAG} + github-release release \ + --tag "macos-v${PUBLISH_VERSION}" \ + --name "macos-v${PUBLISH_VERSION}" \ + --draft ${PUBLISH_PRE_FLAG} fi buildPackageStyle "xpackage" "symbols" buildPackageStyle "xpackage SYMBOLS=NO" + +step "Finished deploying ${PUBLISH_VERSION} in $(($SECONDS / 60)) minutes and $(($SECONDS % 60)) seconds" diff --git a/platform/macos/scripts/document.sh b/platform/macos/scripts/document.sh index d03ad9167..69c9aaa87 100755 --- a/platform/macos/scripts/document.sh +++ b/platform/macos/scripts/document.sh @@ -22,15 +22,21 @@ RELEASE_VERSION=$( echo ${SHORT_VERSION} | sed -e 's/^macos-v//' -e 's/-.*//' ) rm -rf /tmp/mbgl mkdir -p /tmp/mbgl/ README=/tmp/mbgl/README.md -cp platform/macos/docs/doc-README.md "${README}" +if [[ ${STANDALONE:-} ]]; then + cp platform/macos/docs/pod-README.md "${README}" + perl -pi -e 's|https://raw.githubusercontent.com/mapbox/mapbox-gl-native/master/platform/macos/docs/||' \ + "${README}" +else + cp platform/macos/docs/doc-README.md "${README}" +fi # http://stackoverflow.com/a/4858011/4585461 -echo "## Changes in version ${RELEASE_VERSION}" >> "${README}" -sed -n -e '/^## /{' -e ':a' -e 'n' -e '/^##/q' -e 'p' -e 'ba' -e '}' platform/macos/CHANGELOG.md >> "${README}" +echo "## Changes in [version ${RELEASE_VERSION}](https://github.com/mapbox/mapbox-gl-native/releases/tag/${BRANCH})" >> "${README}" +sed -n -e '/^## /{' -e ':a' -e 'n' -e '/^## /q' -e 'p' -e 'ba' -e '}' platform/macos/CHANGELOG.md >> "${README}" rm -rf ${OUTPUT} mkdir -p ${OUTPUT} -cp platform/macos/screenshot.png "${OUTPUT}" +cp -r platform/macos/docs/img "${OUTPUT}/img" jazzy \ --config platform/macos/jazzy.yml \ @@ -38,7 +44,7 @@ jazzy \ --github-file-prefix https://github.com/mapbox/mapbox-gl-native/tree/${BRANCH} \ --module-version ${SHORT_VERSION} \ --readme ${README} \ - --documentation="platform/macos/docs/Info.plist Keys.md" \ + --documentation="platform/{darwin,macos}/docs/guides/*.md" \ --theme platform/darwin/docs/theme \ --output ${OUTPUT} # https://github.com/realm/jazzy/issues/411 diff --git a/platform/macos/scripts/package.sh b/platform/macos/scripts/package.sh index 2f30b0917..6ae0cc65c 100755 --- a/platform/macos/scripts/package.sh +++ b/platform/macos/scripts/package.sh @@ -68,6 +68,19 @@ if [[ ${BUILDTYPE} == Release ]]; then "${OUTPUT}/${NAME}.framework/${NAME}" fi +function create_podspec { + step "Creating local podspec…" + [[ $SYMBOLS = YES ]] && POD_SUFFIX="-symbols" || POD_SUFFIX="" + POD_SOURCE_PATH=' :path => ".",' + POD_FRAMEWORKS=" m.vendored_frameworks = '"${NAME}".framework'" + INPUT_PODSPEC=platform/macos/${NAME}-macOS-SDK${POD_SUFFIX}.podspec + OUTPUT_PODSPEC=${OUTPUT}/${NAME}-macOS-SDK${POD_SUFFIX}.podspec + sed "s/.*:http.*/${POD_SOURCE_PATH}/" ${INPUT_PODSPEC} > ${OUTPUT_PODSPEC} + sed -i '' "s/.*vendored_frameworks.*/${POD_FRAMEWORKS}/" ${OUTPUT_PODSPEC} +} + +create_podspec + step "Copying library resources…" cp -pv LICENSE.md "${OUTPUT}" cp -pv platform/macos/docs/pod-README.md "${OUTPUT}/README.md" diff --git a/platform/macos/src/MGLAnnotationImage.h b/platform/macos/src/MGLAnnotationImage.h index d41fd32ec..0f248c721 100644 --- a/platform/macos/src/MGLAnnotationImage.h +++ b/platform/macos/src/MGLAnnotationImage.h @@ -11,7 +11,7 @@ NS_ASSUME_NONNULL_BEGIN and put into a reuse queue that is maintained by the map view. */ MGL_EXPORT -@interface MGLAnnotationImage : NSObject +@interface MGLAnnotationImage : NSObject #pragma mark Initializing and Preparing the Image Object diff --git a/platform/macos/src/MGLAnnotationImage.m b/platform/macos/src/MGLAnnotationImage.m index 1b545651d..d19dbe5df 100644 --- a/platform/macos/src/MGLAnnotationImage.m +++ b/platform/macos/src/MGLAnnotationImage.m @@ -23,4 +23,41 @@ return self; } ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (instancetype)initWithCoder:(NSCoder *)decoder { + if (self = [super init]) { + _image = [decoder decodeObjectOfClass:[NSImage class] forKey:@"image"]; + _reuseIdentifier = [decoder decodeObjectOfClass:[NSString class] forKey:@"reuseIdentifier"]; + _cursor = [decoder decodeObjectOfClass:[NSCursor class] forKey:@"cursor"]; + _selectable = [decoder decodeBoolForKey:@"selectable"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [coder encodeObject:_image forKey:@"image"]; + [coder encodeObject:_reuseIdentifier forKey:@"reuseIdentifier"]; + [coder encodeObject:_cursor forKey:@"cursor"]; + [coder encodeBool:_selectable forKey:@"selectable"]; +} + +- (BOOL)isEqual:(id)other { + if (self == other) return YES; + if (![other isKindOfClass:[MGLAnnotationImage class]]) return NO; + + MGLAnnotationImage *otherAnnotationImage = other; + + return ((!_reuseIdentifier && !otherAnnotationImage.reuseIdentifier) || [_reuseIdentifier isEqualToString:otherAnnotationImage.reuseIdentifier]) + && _selectable == otherAnnotationImage.selectable + && ((!_cursor && !otherAnnotationImage.cursor) || [_cursor isEqual:otherAnnotationImage.cursor]) + && (_image == otherAnnotationImage.image || [[_image TIFFRepresentation] isEqualToData:[otherAnnotationImage.image TIFFRepresentation]]); +} + +- (NSUInteger)hash { + return _reuseIdentifier.hash + @(_selectable).hash + _image.hash; +} + @end diff --git a/platform/macos/src/MGLMapView.h b/platform/macos/src/MGLMapView.h index c934ba6e9..efe83d157 100644 --- a/platform/macos/src/MGLMapView.h +++ b/platform/macos/src/MGLMapView.h @@ -95,6 +95,13 @@ MGL_EXPORT IB_DESIGNABLE Unlike the `styleURL` property, this property is set to an object that allows you to manipulate every aspect of the style locally. + If the style is loading, this property is set to `nil` until the style finishes + loading. If the style has failed to load, this property is set to `nil`. + Because the style loads asynchronously, you should manipulate it in the + `-[MGLMapViewDelegate mapView:didFinishLoadingStyle:]` or + `-[MGLMapViewDelegate mapViewDidFinishLoadingMap:]` method. It is not possible + to manipulate the style before it has finished loading. + @note The default styles provided by Mapbox contain sources and layers with identifiers that will change over time. Applications that use APIs that manipulate a style's sources and layers must first set the style URL to an @@ -102,7 +109,7 @@ MGL_EXPORT IB_DESIGNABLE `+[MGLStyle outdoorsStyleURLWithVersion:]`, `MGLMapView`'s “Style URL” inspectable in Interface Builder, or a manually constructed `NSURL`. */ -@property (nonatomic, readonly) MGLStyle *style; +@property (nonatomic, readonly, nullable) MGLStyle *style; /** URL of the style currently displayed in the receiver. diff --git a/platform/macos/src/MGLMapView.mm b/platform/macos/src/MGLMapView.mm index 92aa0a6c6..0db059dcf 100644 --- a/platform/macos/src/MGLMapView.mm +++ b/platform/macos/src/MGLMapView.mm @@ -19,6 +19,7 @@ #import "MGLAnnotationImage.h" #import "MGLMapViewDelegate.h" +#import #import #import #import @@ -597,6 +598,10 @@ public: #pragma mark Style ++ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingStyle { + return [NSSet setWithObject:@"styleURL"]; +} + - (nonnull NSURL *)styleURL { NSString *styleURLString = @(_mbglMap->getStyleURL().c_str()).mgl_stringOrNilIfEmpty; return styleURLString ? [NSURL URLWithString:styleURLString] : [MGLStyle streetsStyleURLWithVersion:MGLStyleDefaultVersion]; @@ -618,10 +623,8 @@ public: } styleURL = styleURL.mgl_URLByStandardizingScheme; - [self willChangeValueForKey:@"style"]; - _style = [[MGLStyle alloc] initWithMapView:self]; + self.style = nil; _mbglMap->setStyleURL(styleURL.absoluteString.UTF8String); - [self didChangeValueForKey:@"style"]; } - (IBAction)reloadStyle:(__unused id)sender { @@ -630,6 +633,10 @@ public: self.styleURL = styleURL; } +- (mbgl::Map *)mbglMap { + return _mbglMap; +} + #pragma mark View hierarchy and drawing - (void)viewWillMoveToWindow:(NSWindow *)newWindow { @@ -922,11 +929,7 @@ public: } case mbgl::MapChangeDidFinishLoadingStyle: { - [self.style willChangeValueForKey:@"name"]; - [self.style willChangeValueForKey:@"sources"]; - [self.style didChangeValueForKey:@"sources"]; - [self.style willChangeValueForKey:@"layers"]; - [self.style didChangeValueForKey:@"layers"]; + self.style = [[MGLStyle alloc] initWithMapView:self]; if ([self.delegate respondsToSelector:@selector(mapView:didFinishLoadingStyle:)]) { [self.delegate mapView:self didFinishLoadingStyle:self.style]; @@ -2274,28 +2277,11 @@ public: } } -#pragma mark - Runtime styling - -- (mbgl::Map *)mbglMap -{ - return _mbglMap; -} - #pragma mark MGLMultiPointDelegate methods - (double)alphaForShapeAnnotation:(MGLShape *)annotation { - // The explicit -mapView:alphaForShapeAnnotation: delegate method is deprecated - // but still used, if implemented. When not implemented, call the stroke or - // fill color delegate methods and pull the alpha from the returned color. if (_delegateHasAlphasForShapeAnnotations) { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" return [self.delegate mapView:self alphaForShapeAnnotation:annotation]; -#pragma clang diagnostic pop - } else if ([annotation isKindOfClass:[MGLPolygon class]]) { - return [self fillColorForPolygonAnnotation:(MGLPolygon *)annotation].a ?: 1.0; - } else if ([annotation isKindOfClass:[MGLShape class]]) { - return [self strokeColorForShapeAnnotation:annotation].a ?: 1.0; } return 1.0; } @@ -2492,7 +2478,7 @@ public: if (menuItem.action == @selector(giveFeedback:)) { return YES; } - return [super validateMenuItem:menuItem]; + return NO; } #pragma mark Interface Builder methods diff --git a/platform/macos/src/MGLMapViewDelegate.h b/platform/macos/src/MGLMapViewDelegate.h index c5af93f8a..1cf86263f 100644 --- a/platform/macos/src/MGLMapViewDelegate.h +++ b/platform/macos/src/MGLMapViewDelegate.h @@ -156,7 +156,22 @@ NS_ASSUME_NONNULL_BEGIN */ - (nullable MGLAnnotationImage *)mapView:(MGLMapView *)mapView imageForAnnotation:(id )annotation; -- (CGFloat)mapView:(MGLMapView *)mapView alphaForShapeAnnotation:(MGLShape *)annotation __attribute__((deprecated("Use -mapView:strokeColorForShapeAnnotation: or -mapView:fillColorForPolygonAnnotation:."))); +/** + Returns the alpha value to use when rendering a shape annotation. + + A value of 0.0 results in a completely transparent shape. A value of 1.0, the + default, results in a completely opaque shape. + + This method sets the opacity of an entire shape, inclusive of its stroke and + fill. To independently set the values for stroke or fill, specify an alpha + component in the color returned by `-mapView:strokeColorForShapeAnnotation:` or + `-mapView:fillColorForPolygonAnnotation:`. + + @param mapView The map view rendering the shape annotation. + @param annotation The annotation being rendered. + @return An alpha value between 0 and 1.0. + */ +- (CGFloat)mapView:(MGLMapView *)mapView alphaForShapeAnnotation:(MGLShape *)annotation; /** Returns the color to use when rendering the outline of a shape annotation. @@ -257,7 +272,7 @@ NS_ASSUME_NONNULL_BEGIN such as `title` and `subtitle`. If each annotation should have an identical callout, you can set the - `MGLMapView` instance’s `-setCalloutViewController:` method instead. + `MGLMapView.calloutViewController` property instead. @param mapView The map view that is requesting a callout view controller. @param annotation The object representing the annotation. diff --git a/platform/macos/src/MGLMapView_Private.h b/platform/macos/src/MGLMapView_Private.h index 0980252fb..5ac75768a 100644 --- a/platform/macos/src/MGLMapView_Private.h +++ b/platform/macos/src/MGLMapView_Private.h @@ -1,6 +1,8 @@ #import "MGLMapView.h" -#include +namespace mbgl { + class Map; +} @interface MGLMapView (Private) diff --git a/platform/macos/src/Mapbox.h b/platform/macos/src/Mapbox.h index b16aaf7e6..1f3049718 100644 --- a/platform/macos/src/Mapbox.h +++ b/platform/macos/src/Mapbox.h @@ -50,6 +50,5 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[]; #import "MGLTilePyramidOfflineRegion.h" #import "MGLTypes.h" #import "NSValue+MGLAdditions.h" -#import "NSValue+MGLStyleEnumAttributeAdditions.h" #import "MGLStyleValue.h" #import "MGLAttributionInfo.h" diff --git a/platform/macos/src/NSImage+MGLAdditions.mm b/platform/macos/src/NSImage+MGLAdditions.mm index 72ddec83f..9c30d3c37 100644 --- a/platform/macos/src/NSImage+MGLAdditions.mm +++ b/platform/macos/src/NSImage+MGLAdditions.mm @@ -6,7 +6,7 @@ std::string png = encodePNG(spriteImage->image); NSData *data = [[NSData alloc] initWithBytes:png.data() length:png.size()]; NSBitmapImageRep *rep = [NSBitmapImageRep imageRepWithData:data]; - if ([self initWithSize:NSMakeSize(spriteImage->getWidth(), spriteImage->getHeight())]) { + if (self = [self initWithSize:NSMakeSize(spriteImage->getWidth(), spriteImage->getHeight())]) { [self addRepresentation:rep]; [self setTemplate:spriteImage->sdf]; } diff --git a/src/mbgl/style/sources/geojson_source.cpp b/src/mbgl/style/sources/geojson_source.cpp index 748043813..62726cd12 100644 --- a/src/mbgl/style/sources/geojson_source.cpp +++ b/src/mbgl/style/sources/geojson_source.cpp @@ -18,7 +18,7 @@ void GeoJSONSource::setGeoJSON(const mapbox::geojson::geojson& geoJSON) { impl->setGeoJSON(geoJSON); } -optional GeoJSONSource::getURL() { +optional GeoJSONSource::getURL() const { return impl->getURL(); } diff --git a/src/mbgl/style/sources/geojson_source_impl.cpp b/src/mbgl/style/sources/geojson_source_impl.cpp index 7f41e0e32..3f3bc879f 100644 --- a/src/mbgl/style/sources/geojson_source_impl.cpp +++ b/src/mbgl/style/sources/geojson_source_impl.cpp @@ -47,7 +47,7 @@ void GeoJSONSource::Impl::setURL(std::string url_) { } } -optional GeoJSONSource::Impl::getURL() { +optional GeoJSONSource::Impl::getURL() const { return url; } @@ -63,15 +63,9 @@ void GeoJSONSource::Impl::_setGeoJSON(const GeoJSON& geoJSON) { cache.clear(); - if (!options.cluster) { - mapbox::geojsonvt::Options vtOptions; - vtOptions.maxZoom = options.maxzoom; - vtOptions.extent = util::EXTENT; - vtOptions.buffer = std::round(scale * options.buffer); - vtOptions.tolerance = scale * options.tolerance; - geoJSONOrSupercluster = std::make_unique(geoJSON, vtOptions); - - } else { + if (options.cluster + && geoJSON.is>() + && !geoJSON.get>().empty()) { mapbox::supercluster::Options clusterOptions; clusterOptions.maxZoom = options.clusterMaxZoom; clusterOptions.extent = util::EXTENT; @@ -80,6 +74,13 @@ void GeoJSONSource::Impl::_setGeoJSON(const GeoJSON& geoJSON) { const auto& features = geoJSON.get>(); geoJSONOrSupercluster = std::make_unique(features, clusterOptions); + } else { + mapbox::geojsonvt::Options vtOptions; + vtOptions.maxZoom = options.maxzoom; + vtOptions.extent = util::EXTENT; + vtOptions.buffer = std::round(scale * options.buffer); + vtOptions.tolerance = scale * options.tolerance; + geoJSONOrSupercluster = std::make_unique(geoJSON, vtOptions); } for (auto const &item : tiles) { diff --git a/src/mbgl/style/sources/geojson_source_impl.hpp b/src/mbgl/style/sources/geojson_source_impl.hpp index 13994d907..eec7cf060 100644 --- a/src/mbgl/style/sources/geojson_source_impl.hpp +++ b/src/mbgl/style/sources/geojson_source_impl.hpp @@ -17,7 +17,7 @@ public: ~Impl() final; void setURL(std::string); - optional getURL(); + optional getURL() const; void setGeoJSON(const GeoJSON&); void setTileData(GeoJSONTile&, const OverscaledTileID& tileID); diff --git a/src/mbgl/style/sources/raster_source.cpp b/src/mbgl/style/sources/raster_source.cpp index 3c65476df..94fdbcef1 100644 --- a/src/mbgl/style/sources/raster_source.cpp +++ b/src/mbgl/style/sources/raster_source.cpp @@ -5,7 +5,17 @@ namespace mbgl { namespace style { RasterSource::RasterSource(std::string id, variant urlOrTileset, uint16_t tileSize) - : Source(SourceType::Raster, std::make_unique(std::move(id), *this, std::move(urlOrTileset), tileSize)) { + : Source(SourceType::Raster, std::make_unique(std::move(id), *this, std::move(urlOrTileset), tileSize)), + impl(static_cast(baseImpl.get())) { +} + +optional RasterSource::getURL() const { + auto urlOrTileset = impl->getURLOrTileset(); + if (urlOrTileset.is()) { + return urlOrTileset.get(); + } else { + return {}; + } } } // namespace style diff --git a/src/mbgl/style/sources/vector_source.cpp b/src/mbgl/style/sources/vector_source.cpp index f2a6b166a..4bcd3b898 100644 --- a/src/mbgl/style/sources/vector_source.cpp +++ b/src/mbgl/style/sources/vector_source.cpp @@ -5,7 +5,17 @@ namespace mbgl { namespace style { VectorSource::VectorSource(std::string id, variant urlOrTileset) - : Source(SourceType::Vector, std::make_unique(std::move(id), *this, std::move(urlOrTileset))) { + : Source(SourceType::Vector, std::make_unique(std::move(id), *this, std::move(urlOrTileset))), + impl(static_cast(baseImpl.get())) { +} + +optional VectorSource::getURL() const { + auto urlOrTileset = impl->getURLOrTileset(); + if (urlOrTileset.is()) { + return urlOrTileset.get(); + } else { + return {}; + } } } // namespace style