diff options
author | Fabian Guerra <fabian.guerra@mapbox.com> | 2017-04-11 21:46:23 -0400 |
---|---|---|
committer | Fabian Guerra <fabian.guerra@mapbox.com> | 2017-04-11 21:46:23 -0400 |
commit | 12b6fdbe3b1caad95089150d5c3ab69af31fb725 (patch) | |
tree | 414df39761f08c8221e9f51f73c18ced91656a11 /platform | |
parent | 0cb05f926b7dcc18ba59c7a3c16f94860d1a0b1b (diff) | |
parent | 4d0605a004844726fc5f5e015c6d4320a880bfa0 (diff) | |
download | qtlocation-mapboxgl-12b6fdbe3b1caad95089150d5c3ab69af31fb725.tar.gz |
Merge branch 'release-ios-v3.5.0-android-v5.0.0' into boundsj-merge-release-branch
# Conflicts:
# cmake/core-files.cmake
# mapbox-gl-js
# platform/android/CHANGELOG.md
# platform/ios/CHANGELOG.md
# platform/macos/CHANGELOG.md
# platform/qt/bitrise-qt5.yml
# src/mbgl/gl/attribute.hpp
# src/mbgl/gl/context.cpp
# src/mbgl/gl/program.hpp
# src/mbgl/map/map.cpp
# src/mbgl/programs/program.hpp
# src/mbgl/renderer/painter.cpp
Diffstat (limited to 'platform')
44 files changed, 513 insertions, 597 deletions
diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index 6bb0d925c8..98ee22a1a9 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -15,6 +15,21 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to * Expose world bounds in LatLngBounds [#8517](https://github.com/mapbox/mapbox-gl-native/pull/8517) * OfflineRegion are validated if the bounds is found in the world bounds, else onError will be invoked [#8517](https://github.com/mapbox/mapbox-gl-native/pull/8517) +## 5.0.2 - April 3, 2017 + +5.0.2 is a patch release that contains the following changes: + +* Binary shader caching [#8604](https://github.com/mapbox/mapbox-gl-native/pull/8604) +* Fix resource transform callback [#8582](https://github.com/mapbox/mapbox-gl-native/pull/8582) +* Restore onTouch behaviour to 4.x version [#8585](https://github.com/mapbox/mapbox-gl-native/pull/8585) +* Restore anchoring after updating MarkerView Icon [#8519](https://github.com/mapbox/mapbox-gl-native/pull/8519) + +## 5.0.1 - March 22nd, 2017 + +5.0.1 is a patch release that addresses a shader precision issue that created a rendering problem on some devices. + +* Use `highp` for color attributes [#8385](https://github.com/mapbox/mapbox-gl-native/issues/8385) + ## 5.0.0 - March 17th, 2017 5.0.0 final release contains: diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewContainer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewContainer.java deleted file mode 100644 index d590582f09..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewContainer.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.mapbox.mapboxsdk.annotations; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.view.ViewGroup; -import android.widget.FrameLayout; - -/** - * ViewGroup that dispatches TouchEvents to the parent ViewGroup. - * <p> - * This allows to dispatch touch events that occur on MarkerView to MapView. - * https://github.com/mapbox/mapbox-gl-native/issues/5388 - * </p> - */ -public class MarkerViewContainer extends FrameLayout { - - public MarkerViewContainer(Context context, AttributeSet attrs) { - super(context, attrs); - setTag(false); - } - - @Override - public boolean dispatchTouchEvent(MotionEvent ev) { - super.dispatchTouchEvent(ev); - boolean actionUp = (boolean) getTag(); - if (!actionUp) { - ((ViewGroup) getParent()).onTouchEvent(ev); - } else { - setTag(false); - } - return true; - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewManager.java index 3b77bc8a1d..bb51f3bfc2 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewManager.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewManager.java @@ -8,7 +8,6 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.util.LongSparseArray; import android.view.LayoutInflater; -import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; @@ -505,26 +504,6 @@ public class MarkerViewManager implements MapView.OnMapChangedListener { } } - adaptedView.setOnTouchListener(new View.OnTouchListener() { - - @Override - public boolean onTouch(View v, MotionEvent event) { - if (event.getAction() == MotionEvent.ACTION_UP) { - boolean clickHandled = false; - if (onMarkerViewClickListener != null) { - clickHandled = onMarkerViewClickListener.onMarkerClick(marker, v, adapter); - markerViewContainer.setTag(true); - } - - if (!clickHandled) { - ensureInfoWindowOffset(marker); - select(marker, v, adapter); - } - } - return true; - } - }); - marker.setMapboxMap(mapboxMap); markerViewMap.put(marker, adaptedView); if (convertView == null) { @@ -553,6 +532,34 @@ public class MarkerViewManager implements MapView.OnMapChangedListener { } /** + * When the provided {@link MarkerView} is clicked on by a user, we check if a custom click + * event has been created and if not, display a {@link InfoWindow}. + * + * @param markerView that the click event occurred. + */ + public boolean onClickMarkerView(MarkerView markerView) { + boolean clickHandled = false; + + MapboxMap.MarkerViewAdapter adapter = getViewAdapter(markerView); + View view = getView(markerView); + if (adapter == null || view == null) { + // not a valid state + return true; + } + + if (onMarkerViewClickListener != null) { + clickHandled = onMarkerViewClickListener.onMarkerClick(markerView, view, adapter); + } + + if (!clickHandled) { + ensureInfoWindowOffset(markerView); + select(markerView, view, adapter); + } + + return clickHandled; + } + + /** * Handles the {@link MarkerView}'s info window offset. * * @param marker that we are ensuring info window offset. diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java index 74170bb72b..a8fc58d51c 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java @@ -293,7 +293,7 @@ public final class CameraPosition implements Parcelable { * Set the tilt in degrees * <p> * value is clamped to 0 and 60. - * <p/> + * </p> * * @param tilt Tilt value * @return Builder diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationSource.java index f41a022fa2..dd6e43d06a 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationSource.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationSource.java @@ -18,11 +18,10 @@ import java.lang.ref.WeakReference; /** * Manages locational updates. Contains methods to register and unregister location listeners. * <ul> - * <li>You can register a {@link LocationEngineListener} with - * {@link #addLocationEngineListener(LocationEngineListener)} to receive - * location updates.</li> - * <li> You can unregister a {@link LocationEngineListener} with - * {@link #removeLocationEngineListener(LocationEngineListener)} to stop receiving location updates.</li> + * <li>You can register a LocationEngineListener with LocationSource#addLocationEngineListener(LocationEngineListener) + * to receive location updates.</li> + * <li> You can unregister a LocationEngineListener with + * LocationEngine#removeLocationEngineListener(LocationEngineListener)} to stop receiving location updates.</li> * </ul> * <p> * Note: If registering a listener in your Activity.onStart() implementation, you should unregister it in diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java index e91779dfd1..0c77723354 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java @@ -10,7 +10,6 @@ import com.mapbox.mapboxsdk.annotations.Annotation; import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions; import com.mapbox.mapboxsdk.annotations.BaseMarkerViewOptions; import com.mapbox.mapboxsdk.annotations.Icon; -import com.mapbox.mapboxsdk.annotations.IconFactory; import com.mapbox.mapboxsdk.annotations.Marker; import com.mapbox.mapboxsdk.annotations.MarkerView; import com.mapbox.mapboxsdk.annotations.MarkerViewManager; @@ -257,11 +256,7 @@ class AnnotationManager { private MarkerView prepareViewMarker(BaseMarkerViewOptions markerViewOptions) { MarkerView marker = markerViewOptions.getMarker(); - Icon icon = markerViewOptions.getIcon(); - if (icon == null) { - icon = IconFactory.getInstance(mapView.getContext()).defaultMarkerView(); - } - marker.setIcon(icon); + iconManager.loadIconForMarkerView(marker); return marker; } @@ -665,16 +660,24 @@ class AnnotationManager { if (annotation.getId() == newSelectedMarkerId) { Marker marker = (Marker) annotation; - if (!(marker instanceof MarkerView)) { + if (marker instanceof MarkerView) { + handledDefaultClick = markerViewManager.onClickMarkerView((MarkerView) marker); + } else { if (onMarkerClickListener != null) { // end developer has provided a custom click listener handledDefaultClick = onMarkerClickListener.onMarkerClick(marker); } + } + + if (annotation instanceof MarkerView) { + markerViewManager.onClickMarkerView((MarkerView) annotation); + } else { if (!handledDefaultClick) { // only select marker if user didn't handle the click event themselves selectMarker(marker); } } + return true; } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/IconManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/IconManager.java index c152630278..c9d81a88bc 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/IconManager.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/IconManager.java @@ -71,6 +71,27 @@ class IconManager { return icon; } + Icon loadIconForMarkerView(MarkerView marker) { + Icon icon = marker.getIcon(); + int iconSize = icons.size() + 1; + if (icon == null) { + icon = IconFactory.getInstance(nativeMapView.getContext()).defaultMarkerView(); + marker.setIcon(icon); + } + Bitmap bitmap = icon.getBitmap(); + averageIconHeight = averageIconHeight + (bitmap.getHeight() - averageIconHeight) / iconSize; + averageIconWidth = averageIconWidth + (bitmap.getWidth() - averageIconWidth) / iconSize; + if (!icons.contains(icon)) { + icons.add(icon); + } else { + Icon oldIcon = icons.get(icons.indexOf(icon)); + if (!oldIcon.getBitmap().sameAs(icon.getBitmap())) { + throw new IconBitmapChangedException(); + } + } + return icon; + } + int getTopOffsetPixelsForIcon(Icon icon) { return (int) (nativeMapView.getTopOffsetPixelsForAnnotationSymbol(icon.getId()) * nativeMapView.getPixelRatio()); } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java index 718b790df2..ec80186761 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java @@ -42,7 +42,7 @@ public class GeoJsonSource extends Source { } /** - * Create an empty GeoJsonSource with non-default {@link GeoJsonOptions} + * Create an empty GeoJsonSource with non-default GeoJsonOptions. * * @param id the source id * @param options options @@ -67,7 +67,7 @@ public class GeoJsonSource extends Source { } /** - * Create a GeoJsonSource from a raw json string and non-default {@link GeoJsonOptions} + * Create a GeoJsonSource from a raw json string and non-default GeoJsonOptions * * @param id the source id * @param geoJson raw Json body @@ -93,7 +93,7 @@ public class GeoJsonSource extends Source { } /** - * Create a GeoJsonSource from a remote geo json file and non-default {@link GeoJsonOptions} + * Create a GeoJsonSource from a remote geo json file and non-default GeoJsonOptions * * @param id the source id * @param url remote json file @@ -105,7 +105,7 @@ public class GeoJsonSource extends Source { } /** - * Create a GeoJsonSource from a {@link FeatureCollection} + * Create a GeoJsonSource from a FeatureCollection. * * @param id the source id * @param features the features @@ -116,7 +116,7 @@ public class GeoJsonSource extends Source { } /** - * Create a GeoJsonSource from a {@link FeatureCollection} and non-default {@link GeoJsonOptions} + * Create a GeoJsonSource from a FeatureCollection and non-default GeoJsonOptions. * * @param id the source id * @param features the features @@ -194,7 +194,7 @@ public class GeoJsonSource extends Source { /** * Updates the GeoJson * - * @param features the GeoJSON {@link FeatureCollection} + * @param features the GeoJSON FeatureCollection */ public void setGeoJson(FeatureCollection features) { nativeSetFeatureCollection(features); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_internal.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_internal.xml index 7a38437fee..e6a2677785 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_internal.xml +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_internal.xml @@ -8,7 +8,7 @@ android:contentDescription="@null" android:visibility="gone"/> - <com.mapbox.mapboxsdk.annotations.MarkerViewContainer + <FrameLayout android:id="@+id/markerViewContainer" android:layout_width="match_parent" android:layout_height="match_parent" diff --git a/platform/android/MapboxGLAndroidSDK/src/main/resources/fabric/com.mapbox.mapboxsdk.mapbox-android-sdk.properties b/platform/android/MapboxGLAndroidSDK/src/main/resources/fabric/com.mapbox.mapboxsdk.mapbox-android-sdk.properties index 1d436f1291..bc0350fe1f 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/resources/fabric/com.mapbox.mapboxsdk.mapbox-android-sdk.properties +++ b/platform/android/MapboxGLAndroidSDK/src/main/resources/fabric/com.mapbox.mapboxsdk.mapbox-android-sdk.properties @@ -1,3 +1,3 @@ fabric-identifier=com.mapbox.mapboxsdk.mapbox-android-sdk -fabric-version=5.0.0 +fabric-version=5.0.1 fabric-build-type=binary diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewActivity.java index b8a008e592..f2f82865d1 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewActivity.java @@ -196,13 +196,6 @@ public class MarkerViewActivity extends AppCompatActivity { // open infowindow offscreen markers mapboxMap.selectMarker(markerRightOffScreen); mapboxMap.selectMarker(markerRightBottomOffScreen); - - mapboxMap.setOnMapClickListener(new MapboxMap.OnMapClickListener() { - @Override - public void onMapClick(@NonNull LatLng point) { - Toast.makeText(MarkerViewActivity.this, point.toString(), Toast.LENGTH_SHORT).show(); - } - }); } }); } diff --git a/platform/darwin/docs/guides/For Style Authors.md.ejs b/platform/darwin/docs/guides/For Style Authors.md.ejs index 92ce4c1594..86f8c46f53 100644 --- a/platform/darwin/docs/guides/For Style Authors.md.ejs +++ b/platform/darwin/docs/guides/For Style Authors.md.ejs @@ -240,30 +240,6 @@ whose names differ from the style specification are listed below: <% for (const type in renamedProperties) { -%> <% if (renamedProperties.hasOwnProperty(type)) { -%> -### <%- camelize(type) %> style functions - -The runtime styling API introduces `MGLStyleFunction` to the <%- os %> SDK. -[Data-driven styling](data-driven-styling.html) expands `MGLStyleFunction`. -Individual style property documentation includes which subclasses of -`MGLStyleFunction` are enabled for that property. You can use `MGLStyleValue` -methods to create a `MGLStyleFunction`. - -In style specification | In the SDK | [MGLStyleValue valueWithInterpolationMode:...] ------------------------|-------------------------------|------------- -`zoom function` | `MGLCameraStyleFunction` | `cameraStops:options:` -`property function` | `MGLSourceStyleFunction` | `sourceStops:attributeName:options:` -`zoom-and-property functions`| `MGLCompositeStyleFunction` | `compositeStops:attributeName:options:` - -Data-driven styling also introduces interpolation mode, which defines the -relationship between style values and attributes or zoom levels. - -In style specification | In the SDK ------------------------------|----------- -`exponential` | `MGLInterpolationModeExponential` -`interval` | `MGLInterpolationModeInterval` -`categorical` | `MGLInterpolationModeCategorical` -`identity` | `MGLInterpolationModeIdentity` - ### <%- camelize(type) %> style layers In style JSON | In Objective-C | In Swift @@ -280,10 +256,12 @@ In style JSON | In Objective-C | In Swift Each property representing a layout or paint attribute is set to an `MGLStyleValue` object, which is either an `MGLConstantStyleValue` object (for -constant values) or an `MGLStyleFunction` object (for zoom level functions). The +constant values) or an `MGLStyleFunction` object (for style functions). The style value object is a container for the raw value or function parameters that you want the attribute to be set to. +### Constant style values + 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. @@ -326,6 +304,39 @@ translation downward. This is the reverse of how `CGVector` is interpreted on iOS. <% } -%> +### Style functions + +A _style function_ allows you to vary the value of a layout or paint attribute +based on the zoom level, data provided by content sources, or both. For more +information about style functions, see “[Using Style Functions at Runtime](using-style-functions-at-runtime.html)”. + +Each kind of style function is represented by a distinct class, but you +typically create style functions as you create any other style value, using +class methods on `MGLStyleValue`: + +In style specification | SDK class | SDK factory method +---------------------------|-----------------------------|------------------- +zoom function | `MGLCameraStyleFunction` | `+[MGLStyleValue valueWithInterpolationMode:cameraStops:options:]` +property function | `MGLSourceStyleFunction` | `+[MGLStyleValue valueWithInterpolationMode:sourceStops:attributeName:options:]` +zoom-and-property function | `MGLCompositeStyleFunction` | `+[MGLStyleValue valueWithInterpolationMode:compositeStops:attributeName:options:]` + +The documentation for each individual style layer property indicates the kinds +of style functions that are enabled for that property. + +When you create a style function, you specify an _interpolation mode_ and a +series of _stops_. Each stop determines the effective value displayed at a +particular zoom level (for camera functions) or the effective value on features +with a particular attribute value in the content source (for source functions). +The interpolation mode tells the SDK how to calculate the effective value +between any two stops: + +In style specification | In the SDK +-----------------------------|----------- +`exponential` | `MGLInterpolationModeExponential` +`interval` | `MGLInterpolationModeInterval` +`categorical` | `MGLInterpolationModeCategorical` +`identity` | `MGLInterpolationModeIdentity` + ## Filtering sources You can filter a shape or vector source by setting the diff --git a/platform/darwin/docs/guides/Data-Driven Styling.md.ejs b/platform/darwin/docs/guides/Using Style Functions at Runtime.md.ejs index 820f71f594..bd477042c7 100644 --- a/platform/darwin/docs/guides/Data-Driven Styling.md.ejs +++ b/platform/darwin/docs/guides/Using Style Functions at Runtime.md.ejs @@ -10,46 +10,47 @@ Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. --> -# Data-Driven Styling +# Using Style Functions at Runtime -Mapbox’s data-driven styling features allow you to use data properties to style your maps. You can style map features automatically based on their individual attributes. +[Runtime Styling](runtime-styling.html) enables you to modify every aspect of the map’s appearance dynamically as a user interacts with your application. Much of the runtime styling API allows you to specify _style functions_ instead of constant values. A style function allows you to specify in advance how a layout or paint attribute will vary as the zoom level changes or how the appearance of individual features vary based on metadata provided by a content source. -Vary POI icons, transit route line colors, city polygon opacity, and more based on any attribute in your data. Need to visualize hotel data by price? You can have your map’s point radii and colors change automatically with your data. +Style functions spare you the inconvenience of manually calculating intermediate values between different zoom levels or creating a multitude of style layers to handle homogeneous features in the map content. For example, if your content source indicates the prices of hotels in an area, you can color-code the hotels by price, relying on a style function to smoothly interpolate among desired colors without having to specify the color for each exact price. -![available bikes](img/data-driven-styling/citibikes.png) ![subway lines](img/data-driven-styling/polylineExample.png) +_Data-driven styling_ specifically refers to the use of style functions to vary the map’s appearance based on data in a content source. + +You can also specify style functions in a style JSON file, to be applied automatically when the map loads. See the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-js/style-spec/#types-function) for details. -# How to use Data-Driven Styling -This guide uses earthquake data from the [U.S. Geological Survey](https://earthquake.usgs.gov/earthquakes/feed/v1.0/geojson.php) to style a map based on attributes. For more information about how to work with GeoJSON data in our iOS SDK, please see our [working with GeoJSON data](working-with-geojson-data.html) guide. +![available bikes](img/data-driven-styling/citibikes.png) ![subway lines](img/data-driven-styling/polylineExample.png) -`MGLStyleFunction` +This guide uses earthquake data from the [U.S. Geological Survey](https://earthquake.usgs.gov/earthquakes/feed/v1.0/geojson.php) and data-driven styling to style a map based on attributes. For more information about how to work with GeoJSON data in our iOS SDK, please see our [working with GeoJSON data](working-with-geojson-data.html) guide. -There are three subclasses of `MGLStyleFunction`: +A style function is represented at runtime by the `MGLStyleFunction` class. There are three subclasses of `MGLStyleFunction`: -* `MGLCameraStyleFunction` - For a style value that changes with zoom level. For example, you can make the radius of a circle increase according to zoom level. -* `MGLSourceStyleFunction` - For a style value that changes with the attributes of a feature. For example, you can adjust the radius of a circle based on the magnitude of an earthquake. -* `MGLCompositeStyleFunction` - For a style value that changes with both zoom level and attribute values. For example, you can add a circle layer where each circle has a radius based on both zoom level and the magnitude of an earthquake. +* `MGLCameraStyleFunction` is a style value that changes with zoom level. For example, you can make the radius of a circle increase according to zoom level. +* `MGLSourceStyleFunction` is a style value that changes with the attributes of a feature. For example, you can adjust the radius of a circle based on the magnitude of an earthquake. +* `MGLCompositeStyleFunction` is a style value that changes with both zoom level and attribute values. For example, you can add a circle layer where each circle has a radius based on both zoom level and the magnitude of an earthquake. -The documentation for individual style properties will note which style functions are enabled for that property. +The documentation for each individual style layer property notes which style functions are enabled for that property. ## Stops Stops are key-value pairs that that determine a style value. With a `MGLCameraSourceFunction` stop, you can use a dictionary with a zoom level for a key and a `MGLStyleValue` for the value. For example, you can use a stops dictionary with zoom levels 0, 10, and 20 as keys, and yellow, orange, and red as the values. A `MGLSourceStyleFunction` uses the relevant attribute value as the key. ```swift -let stops = [0: MGLStyleValue(rawValue: UIColor.yellow), - 2.5: MGLStyleValue(rawValue: UIColor.orange), - 5: MGLStyleValue(rawValue: UIColor.red), - 7.5: MGLStyleValue(rawValue: UIColor.blue), - 10: MGLStyleValue(rawValue: UIColor.white)] +let stops = [0: MGLStyleValue<<%- cocoaPrefix %>Color>(rawValue: .yellow), + 2.5: MGLStyleValue(rawValue: .orange), + 5: MGLStyleValue(rawValue: .red), + 7.5: MGLStyleValue(rawValue: .blue), + 10: MGLStyleValue(rawValue: .white)] ``` -## Interpolation Mode +## Interpolation mode The effect a key has on the style value is determined by the interpolation mode. There are four interpolation modes that can be used with a source style function: exponential, interval, categorical, and identity. You can also use exponential and interval interpolation modes with a camera style function. ### Linear -`MGLInterpolationModelExponential` interpolates linearly or exponentially between style function stop values. By default, the `MGLStyleFunction` options parameter `MGLStyleFunctionOptionInterpolationBase` equals `1`, which represents linear interpolation, and doesn’t need to be included in the options dictionary. +`MGLInterpolationModeExponential` interpolates linearly or exponentially between style function stop values. By default, the `MGLStyleFunction` options parameter `MGLStyleFunctionOptionInterpolationBase` equals `1`, which represents linear interpolation and doesn’t need to be included in the options dictionary. The stops dictionary below, for example, shows colors that continuously shift from yellow to orange to red to blue to white based on the attribute value. @@ -61,11 +62,11 @@ let symbolLayer = MGLSymbolStyleLayer(identifier: "place-city-sm", source: symbo let source = MGLShapeSource(identifier: "earthquakes", url: url, options: nil) style.addSource(source) -let stops = [0: MGLStyleValue(rawValue: <%- cocoaPrefix %>Color.yellow), - 2.5: MGLStyleValue(rawValue: <%- cocoaPrefix %>Color.orange), - 5: MGLStyleValue(rawValue: <%- cocoaPrefix %>Color.red), - 7.5: MGLStyleValue(rawValue: <%- cocoaPrefix %>Color.blue), - 10: MGLStyleValue(rawValue: <%- cocoaPrefix %>Color.white)] +let stops = [0: MGLStyleValue<<%- cocoaPrefix %>Color>(rawValue: .yellow), + 2.5: MGLStyleValue(rawValue: .orange), + 5: MGLStyleValue(rawValue: .red), + 7.5: MGLStyleValue(rawValue: .blue), + 10: MGLStyleValue(rawValue: .white)] let layer = MGLCircleStyleLayer(identifier: "circles", source: source) layer.circleColor = MGLStyleValue(interpolationMode: .exponential, @@ -80,7 +81,7 @@ style.insertLayer(layer, below: symbolLayer) ### Exponential -`MGLInterpolationModelExponential` combined with any `MGLStyleFunctionOptionInterpolationBase` greater than `0`, you can interpolate between values exponentially, create an accelerated ramp effect. +By combining `MGLInterpolationModeExponential` with an `MGLStyleFunctionOptionInterpolationBase` greater than `0` (other than `1`), you can interpolate between values exponentially, create an accelerated ramp effect. Here’s a visualization from Mapbox Studio (see [Working with Mapbox Studio](working-with-mapbox-studio.html)) comparing interpolation base values of `1.5` and `0.5` based on zoom. @@ -106,11 +107,11 @@ layer.circleRadius = MGLStyleValue(interpolationMode: .exponential, When we use the stops dictionary given above with an interval interpolation mode, we create ranges where earthquakes with a magnitude of 0 to just less than 2.5 would be yellow, 2.5 to just less than 5 would be orange, and so on. ``` swift -let stops = [0: MGLStyleValue(rawValue: <%- cocoaPrefix %>Color.yellow), - 2.5: MGLStyleValue(rawValue: <%- cocoaPrefix %>Color.orange), - 5: MGLStyleValue(rawValue: <%- cocoaPrefix %>Color.red), - 7.5: MGLStyleValue(rawValue: <%- cocoaPrefix %>Color.blue), - 10: MGLStyleValue(rawValue: <%- cocoaPrefix %>Color.white)] +let stops = [0: MGLStyleValue<<%- cocoaPrefix %>Color>(rawValue: .yellow), + 2.5: MGLStyleValue(rawValue: .orange), + 5: MGLStyleValue(rawValue: .red), + 7.5: MGLStyleValue(rawValue: .blue), + 10: MGLStyleValue(rawValue: .white)] layer.circleColor = MGLStyleValue(interpolationMode: .interval, sourceStops: stops, @@ -122,19 +123,19 @@ layer.circleColor = MGLStyleValue(interpolationMode: .interval, ### Categorical -Returns the output value that is equal to the stop for the function input. We’re going to use a different stops dictionary than we did for the previous two modes. +At each stop, `MGLInterpolationModeCategorical` produces an output value equal to the function input. We’re going to use a different stops dictionary than we did for the previous two modes. -There are three main types of events in the dataset: earthquakes, explosions, and quarry blasts. In this case, the color of the circle layer will be determined by the type of event, with a default value of green to catch any events that do not fall into any of those categories. +There are three main types of events in the dataset: earthquakes, explosions, and quarry blasts. In this case, the color of the circle layer will be determined by the type of event, with a default value of blue to catch any events that do not fall into any of those categories. ``` swift -let categoricalStops = ["earthquake": MGLStyleValue(rawValue: <%- cocoaPrefix %>Color.orange), - "explosion": MGLStyleValue(rawValue: <%- cocoaPrefix %>Color.red), - "quarry blast": MGLStyleValue(rawValue: <%- cocoaPrefix %>Color.yellow)] +let categoricalStops = ["earthquake": MGLStyleValue<<%- cocoaPrefix %>Color>(rawValue: .orange), + "explosion": MGLStyleValue(rawValue: .red), + "quarry blast": MGLStyleValue(rawValue: .yellow)] layer.circleColor = MGLStyleValue(interpolationMode: .categorical, sourceStops: categoricalStops, attributeName: "type", - options: [.defaultValue: MGLStyleValue(rawValue: <%- cocoaPrefix %>Color.blue)]) + options: [.defaultValue: MGLStyleValue<<%- cocoaPrefix %>Color>(rawValue: .blue)]) ``` diff --git a/platform/ios/docs/img/data-driven-styling/categorical1.png b/platform/darwin/docs/img/data-driven-styling/categorical1.png Binary files differindex 969846b41b..969846b41b 100644 --- a/platform/ios/docs/img/data-driven-styling/categorical1.png +++ b/platform/darwin/docs/img/data-driven-styling/categorical1.png diff --git a/platform/ios/docs/img/data-driven-styling/categorical2.png b/platform/darwin/docs/img/data-driven-styling/categorical2.png Binary files differindex 5008c522ed..5008c522ed 100644 --- a/platform/ios/docs/img/data-driven-styling/categorical2.png +++ b/platform/darwin/docs/img/data-driven-styling/categorical2.png diff --git a/platform/ios/docs/img/data-driven-styling/citibikes.png b/platform/darwin/docs/img/data-driven-styling/citibikes.png Binary files differindex a616672a32..a616672a32 100644 --- a/platform/ios/docs/img/data-driven-styling/citibikes.png +++ b/platform/darwin/docs/img/data-driven-styling/citibikes.png diff --git a/platform/ios/docs/img/data-driven-styling/exponential-function-1.png b/platform/darwin/docs/img/data-driven-styling/exponential-function-1.png Binary files differindex 6aa129a305..6aa129a305 100644 --- a/platform/ios/docs/img/data-driven-styling/exponential-function-1.png +++ b/platform/darwin/docs/img/data-driven-styling/exponential-function-1.png diff --git a/platform/ios/docs/img/data-driven-styling/exponential-function.png b/platform/darwin/docs/img/data-driven-styling/exponential-function.png Binary files differindex c14969f0a8..c14969f0a8 100644 --- a/platform/ios/docs/img/data-driven-styling/exponential-function.png +++ b/platform/darwin/docs/img/data-driven-styling/exponential-function.png diff --git a/platform/ios/docs/img/data-driven-styling/exponential.png b/platform/darwin/docs/img/data-driven-styling/exponential.png Binary files differindex 87ddc1350e..87ddc1350e 100644 --- a/platform/ios/docs/img/data-driven-styling/exponential.png +++ b/platform/darwin/docs/img/data-driven-styling/exponential.png diff --git a/platform/ios/docs/img/data-driven-styling/identity.png b/platform/darwin/docs/img/data-driven-styling/identity.png Binary files differindex 632ccdf3d5..632ccdf3d5 100644 --- a/platform/ios/docs/img/data-driven-styling/identity.png +++ b/platform/darwin/docs/img/data-driven-styling/identity.png diff --git a/platform/ios/docs/img/data-driven-styling/interval.png b/platform/darwin/docs/img/data-driven-styling/interval.png Binary files differindex d15aff2025..d15aff2025 100644 --- a/platform/ios/docs/img/data-driven-styling/interval.png +++ b/platform/darwin/docs/img/data-driven-styling/interval.png diff --git a/platform/ios/docs/img/data-driven-styling/polylineExample.png b/platform/darwin/docs/img/data-driven-styling/polylineExample.png Binary files differindex cd9b39bae4..cd9b39bae4 100644 --- a/platform/ios/docs/img/data-driven-styling/polylineExample.png +++ b/platform/darwin/docs/img/data-driven-styling/polylineExample.png diff --git a/platform/darwin/scripts/generate-style-code.js b/platform/darwin/scripts/generate-style-code.js index 9c4569709d..7e798154e4 100644 --- a/platform/darwin/scripts/generate-style-code.js +++ b/platform/darwin/scripts/generate-style-code.js @@ -528,7 +528,7 @@ const layerH = ejs.compile(fs.readFileSync('platform/darwin/src/MGLStyleLayer.h. const layerM = ejs.compile(fs.readFileSync('platform/darwin/src/MGLStyleLayer.mm.ejs', 'utf8'), { strict: true}); const testLayers = ejs.compile(fs.readFileSync('platform/darwin/test/MGLStyleLayerTests.mm.ejs', 'utf8'), { strict: true}); const guideMD = ejs.compile(fs.readFileSync('platform/darwin/docs/guides/For Style Authors.md.ejs', 'utf8'), { strict: true }); -const ddsGuideMD = ejs.compile(fs.readFileSync('platform/darwin/docs/guides/Data-Driven Styling.md.ejs', 'utf8'), { strict: true }); +const ddsGuideMD = ejs.compile(fs.readFileSync('platform/darwin/docs/guides/Using Style Functions at Runtime.md.ejs', 'utf8'), { strict: true }); const layers = _(spec.layer.type.values).map((value, layerType) => { const layoutProperties = Object.keys(spec[`layout_${layerType}`]).reduce((memo, name) => { @@ -615,9 +615,9 @@ fs.writeFileSync(`platform/macos/docs/guides/For Style Authors.md`, guideMD({ renamedProperties: renamedPropertiesByLayerType, layers: layers, })); -fs.writeFileSync(`platform/ios/docs/guides/Data-Driven Styling.md`, ddsGuideMD({ +fs.writeFileSync(`platform/ios/docs/guides/Using Style Functions at Runtime.md`, ddsGuideMD({ os: 'iOS', })); -fs.writeFileSync(`platform/macos/docs/guides/Data-Driven Styling.md`, ddsGuideMD({ +fs.writeFileSync(`platform/macos/docs/guides/Using Style Functions at Runtime.md`, ddsGuideMD({ os: 'macOS', })); diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 53df1f7f4a..a90a210b6a 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -7,7 +7,19 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * The error passed into `-[MGLMapViewDelegate mapViewDidFailLoadingMap:withError:]` now includes a more specific description and failure reason. ([#8418](https://github.com/mapbox/mapbox-gl-native/pull/8418)) * Fixed an issue where gesture recognizers associated with map view interactivity were not disabled when their related interactions were disabled. ([#8304](https://github.com/mapbox/mapbox-gl-native/pull/8304)) -## 3.5.0 +## 3.5.2 - April 7, 2017 + +* Fixed an issue that caused a crash when the user location annotation was presenting a callout view and the map was moved. ([#8686](https://github.com/mapbox/mapbox-gl-native/pull/8686)) +* This release was built with Xcode 8.3.1, which fixed [a significant bitcode issue](http://www.openradar.me/31302382) introduced in Xcode 8.3 that caused Mapbox iOS SDK 3.5.1 to be 2× larger than 3.5.0. + +## 3.5.1 - April 5, 2017 + +* Fixed an issue that caused the return type of a map view delegate method to bridge incorrectly to applications written in Swift. ([#8541](https://github.com/mapbox/mapbox-gl-native/pull/8541)) +* Fixed a crash that could occur when calling `-[MGLShapeSource featuresMatchingPredicate:]` or `-[MGLVectorSource featuresInSourceLayersWithIdentifiers:predicate:]`. ([#8553](https://github.com/mapbox/mapbox-gl-native/pull/8553)) +* Fixed a crash that could occur after adding view-backed annotations to the map. ([#8513](https://github.com/mapbox/mapbox-gl-native/pull/8513)) +* Renamed the “Data-Driven Styling” guide to “Using Style Functions at Runtime” and clarified the meaning of data-driven styling in the guide’s discussion of runtime style functions. ([#8627](https://github.com/mapbox/mapbox-gl-native/pull/8627)) + +## 3.5.0 - March 21, 2017 ### Packaging @@ -20,8 +32,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Added support for right-to-left text and Arabic ligatures in labels. ([#6984](https://github.com/mapbox/mapbox-gl-native/pull/6984), [#7123](https://github.com/mapbox/mapbox-gl-native/pull/7123)) * Improved the line wrapping behavior of point-placed labels, especially labels written in Chinese and Japanese. ([#6828](https://github.com/mapbox/mapbox-gl-native/pull/6828), [#7446](https://github.com/mapbox/mapbox-gl-native/pull/7446)) * CJK characters now remain upright in vertically oriented labels that have line placement, such as road labels. ([#7114](https://github.com/mapbox/mapbox-gl-native/issues/7114)) -* Added Catalan, Chinese (Simplified and Traditional), Dutch, Finnish, French, German, Japanese, Lithuanian, Polish, Portuguese (Brazilian), Russian, Spanish, Swedish, Ukrainian, and Vietnamese localizations. ([#7316](https://github.com/mapbox/mapbox-gl-native/pull/7316), [#7899](https://github.com/mapbox/mapbox-gl-native/pull/7899), [#7999](https://github.com/mapbox/mapbox-gl-native/pull/7999), -[#8113](https://github.com/mapbox/mapbox-gl-native/pull/8113), [#8256](https://github.com/mapbox/mapbox-gl-native/pull/8256)) +* Added Catalan, Chinese (Simplified and Traditional), Dutch, Finnish, French, German, Japanese, Lithuanian, Polish, Portuguese (Brazilian), Russian, Spanish, Swedish, Ukrainian, and Vietnamese localizations. ([#7316](https://github.com/mapbox/mapbox-gl-native/pull/7316), [#7899](https://github.com/mapbox/mapbox-gl-native/pull/7899), [#7999](https://github.com/mapbox/mapbox-gl-native/pull/7999), [#8113](https://github.com/mapbox/mapbox-gl-native/pull/8113), [#8256](https://github.com/mapbox/mapbox-gl-native/pull/8256)) ### Styles diff --git a/platform/ios/Mapbox-iOS-SDK-symbols.podspec b/platform/ios/Mapbox-iOS-SDK-symbols.podspec index aa9a6cf7e5..190087afaf 100644 --- a/platform/ios/Mapbox-iOS-SDK-symbols.podspec +++ b/platform/ios/Mapbox-iOS-SDK-symbols.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '3.5.0-rc.2' + version = '3.5.2' m.name = 'Mapbox-iOS-SDK-symbols' m.version = "#{version}-symbols" diff --git a/platform/ios/Mapbox-iOS-SDK.podspec b/platform/ios/Mapbox-iOS-SDK.podspec index 296015da11..ae9e470e87 100644 --- a/platform/ios/Mapbox-iOS-SDK.podspec +++ b/platform/ios/Mapbox-iOS-SDK.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '3.5.0-rc.2' + version = '3.5.2' m.name = 'Mapbox-iOS-SDK' m.version = version diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m index c249949c09..7aeb2042df 100644 --- a/platform/ios/app/MBXViewController.m +++ b/platform/ios/app/MBXViewController.m @@ -1749,7 +1749,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { }]; } -- (UIView<MGLCalloutView> *)mapView:(__unused MGLMapView *)mapView calloutViewForAnnotation:(id<MGLAnnotation>)annotation +- (nullable id <MGLCalloutView>)mapView:(__unused MGLMapView *)mapView calloutViewForAnnotation:(id<MGLAnnotation>)annotation { if ([annotation respondsToSelector:@selector(title)] && [annotation isKindOfClass:[MBXCustomCalloutAnnotation class]]) diff --git a/platform/ios/docs/guides/Adding Points to a Map.md b/platform/ios/docs/guides/Adding Points to a Map.md index 17e8ad3592..ab1702a076 100644 --- a/platform/ios/docs/guides/Adding Points to a Map.md +++ b/platform/ios/docs/guides/Adding Points to a Map.md @@ -79,5 +79,5 @@ From there, you can create one or many `MGLSymbolStyleLayer` or `MGLCircleStyleL **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 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-layer/)). * 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 index 952be6bec7..ed9018b121 100644 --- a/platform/ios/docs/guides/For Style Authors.md +++ b/platform/ios/docs/guides/For Style Authors.md @@ -190,30 +190,6 @@ 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 functions - -The runtime styling API introduces `MGLStyleFunction` to the iOS SDK. -[Data-driven styling](data-driven-styling.html) expands `MGLStyleFunction`. -Individual style property documentation includes which subclasses of -`MGLStyleFunction` are enabled for that property. You can use `MGLStyleValue` -methods to create a `MGLStyleFunction`. - -In style specification | In the SDK | [MGLStyleValue valueWithInterpolationMode:...] ------------------------|-------------------------------|------------- -`zoom function` | `MGLCameraStyleFunction` | `cameraStops:options:` -`property function` | `MGLSourceStyleFunction` | `sourceStops:attributeName:options:` -`zoom-and-property functions`| `MGLCompositeStyleFunction` | `compositeStops:attributeName:options:` - -Data-driven styling also introduces interpolation mode, which defines the -relationship between style values and attributes or zoom levels. - -In style specification | In the SDK ------------------------------|----------- -`exponential` | `MGLInterpolationModeExponential` -`interval` | `MGLInterpolationModeInterval` -`categorical` | `MGLInterpolationModeCategorical` -`identity` | `MGLInterpolationModeIdentity` - ### Circle style layers In style JSON | In Objective-C | In Swift @@ -222,30 +198,6 @@ In style JSON | In Objective-C | In Swift `circle-translate` | `MGLCircleStyleLayer.circleTranslation` | `MGLCircleStyleLayer.circleTranslation` `circle-translate-anchor` | `MGLCircleStyleLayer.circleTranslationAnchor` | `MGLCircleStyleLayer.circleTranslationAnchor` -### Fill style functions - -The runtime styling API introduces `MGLStyleFunction` to the iOS SDK. -[Data-driven styling](data-driven-styling.html) expands `MGLStyleFunction`. -Individual style property documentation includes which subclasses of -`MGLStyleFunction` are enabled for that property. You can use `MGLStyleValue` -methods to create a `MGLStyleFunction`. - -In style specification | In the SDK | [MGLStyleValue valueWithInterpolationMode:...] ------------------------|-------------------------------|------------- -`zoom function` | `MGLCameraStyleFunction` | `cameraStops:options:` -`property function` | `MGLSourceStyleFunction` | `sourceStops:attributeName:options:` -`zoom-and-property functions`| `MGLCompositeStyleFunction` | `compositeStops:attributeName:options:` - -Data-driven styling also introduces interpolation mode, which defines the -relationship between style values and attributes or zoom levels. - -In style specification | In the SDK ------------------------------|----------- -`exponential` | `MGLInterpolationModeExponential` -`interval` | `MGLInterpolationModeInterval` -`categorical` | `MGLInterpolationModeCategorical` -`identity` | `MGLInterpolationModeIdentity` - ### Fill style layers In style JSON | In Objective-C | In Swift @@ -254,30 +206,6 @@ In style JSON | In Objective-C | In Swift `fill-translate` | `MGLFillStyleLayer.fillTranslation` | `MGLFillStyleLayer.fillTranslation` `fill-translate-anchor` | `MGLFillStyleLayer.fillTranslationAnchor` | `MGLFillStyleLayer.fillTranslationAnchor` -### Line style functions - -The runtime styling API introduces `MGLStyleFunction` to the iOS SDK. -[Data-driven styling](data-driven-styling.html) expands `MGLStyleFunction`. -Individual style property documentation includes which subclasses of -`MGLStyleFunction` are enabled for that property. You can use `MGLStyleValue` -methods to create a `MGLStyleFunction`. - -In style specification | In the SDK | [MGLStyleValue valueWithInterpolationMode:...] ------------------------|-------------------------------|------------- -`zoom function` | `MGLCameraStyleFunction` | `cameraStops:options:` -`property function` | `MGLSourceStyleFunction` | `sourceStops:attributeName:options:` -`zoom-and-property functions`| `MGLCompositeStyleFunction` | `compositeStops:attributeName:options:` - -Data-driven styling also introduces interpolation mode, which defines the -relationship between style values and attributes or zoom levels. - -In style specification | In the SDK ------------------------------|----------- -`exponential` | `MGLInterpolationModeExponential` -`interval` | `MGLInterpolationModeInterval` -`categorical` | `MGLInterpolationModeCategorical` -`identity` | `MGLInterpolationModeIdentity` - ### Line style layers In style JSON | In Objective-C | In Swift @@ -286,30 +214,6 @@ In style JSON | In Objective-C | In Swift `line-translate` | `MGLLineStyleLayer.lineTranslation` | `MGLLineStyleLayer.lineTranslation` `line-translate-anchor` | `MGLLineStyleLayer.lineTranslationAnchor` | `MGLLineStyleLayer.lineTranslationAnchor` -### Raster style functions - -The runtime styling API introduces `MGLStyleFunction` to the iOS SDK. -[Data-driven styling](data-driven-styling.html) expands `MGLStyleFunction`. -Individual style property documentation includes which subclasses of -`MGLStyleFunction` are enabled for that property. You can use `MGLStyleValue` -methods to create a `MGLStyleFunction`. - -In style specification | In the SDK | [MGLStyleValue valueWithInterpolationMode:...] ------------------------|-------------------------------|------------- -`zoom function` | `MGLCameraStyleFunction` | `cameraStops:options:` -`property function` | `MGLSourceStyleFunction` | `sourceStops:attributeName:options:` -`zoom-and-property functions`| `MGLCompositeStyleFunction` | `compositeStops:attributeName:options:` - -Data-driven styling also introduces interpolation mode, which defines the -relationship between style values and attributes or zoom levels. - -In style specification | In the SDK ------------------------------|----------- -`exponential` | `MGLInterpolationModeExponential` -`interval` | `MGLInterpolationModeInterval` -`categorical` | `MGLInterpolationModeCategorical` -`identity` | `MGLInterpolationModeIdentity` - ### Raster style layers In style JSON | In Objective-C | In Swift @@ -318,30 +222,6 @@ In style JSON | In Objective-C | In Swift `raster-brightness-min` | `MGLRasterStyleLayer.minimumRasterBrightness` | `MGLRasterStyleLayer.minimumRasterBrightness` `raster-hue-rotate` | `MGLRasterStyleLayer.rasterHueRotation` | `MGLRasterStyleLayer.rasterHueRotation` -### Symbol style functions - -The runtime styling API introduces `MGLStyleFunction` to the iOS SDK. -[Data-driven styling](data-driven-styling.html) expands `MGLStyleFunction`. -Individual style property documentation includes which subclasses of -`MGLStyleFunction` are enabled for that property. You can use `MGLStyleValue` -methods to create a `MGLStyleFunction`. - -In style specification | In the SDK | [MGLStyleValue valueWithInterpolationMode:...] ------------------------|-------------------------------|------------- -`zoom function` | `MGLCameraStyleFunction` | `cameraStops:options:` -`property function` | `MGLSourceStyleFunction` | `sourceStops:attributeName:options:` -`zoom-and-property functions`| `MGLCompositeStyleFunction` | `compositeStops:attributeName:options:` - -Data-driven styling also introduces interpolation mode, which defines the -relationship between style values and attributes or zoom levels. - -In style specification | In the SDK ------------------------------|----------- -`exponential` | `MGLInterpolationModeExponential` -`interval` | `MGLInterpolationModeInterval` -`categorical` | `MGLInterpolationModeCategorical` -`identity` | `MGLInterpolationModeIdentity` - ### Symbol style layers In style JSON | In Objective-C | In Swift @@ -374,10 +254,12 @@ In style JSON | In Objective-C | In Swift Each property representing a layout or paint attribute is set to an `MGLStyleValue` object, which is either an `MGLConstantStyleValue` object (for -constant values) or an `MGLStyleFunction` object (for zoom level functions). The +constant values) or an `MGLStyleFunction` object (for style functions). The style value object is a container for the raw value or function parameters that you want the attribute to be set to. +### Constant style values + 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. @@ -402,6 +284,39 @@ in Swift are specified in counterclockwise order, in contrast to the clockwise order defined by the style specification. +### Style functions + +A _style function_ allows you to vary the value of a layout or paint attribute +based on the zoom level, data provided by content sources, or both. For more +information about style functions, see “[Using Style Functions at Runtime](using-style-functions-at-runtime.html)”. + +Each kind of style function is represented by a distinct class, but you +typically create style functions as you create any other style value, using +class methods on `MGLStyleValue`: + +In style specification | SDK class | SDK factory method +---------------------------|-----------------------------|------------------- +zoom function | `MGLCameraStyleFunction` | `+[MGLStyleValue valueWithInterpolationMode:cameraStops:options:]` +property function | `MGLSourceStyleFunction` | `+[MGLStyleValue valueWithInterpolationMode:sourceStops:attributeName:options:]` +zoom-and-property function | `MGLCompositeStyleFunction` | `+[MGLStyleValue valueWithInterpolationMode:compositeStops:attributeName:options:]` + +The documentation for each individual style layer property indicates the kinds +of style functions that are enabled for that property. + +When you create a style function, you specify an _interpolation mode_ and a +series of _stops_. Each stop determines the effective value displayed at a +particular zoom level (for camera functions) or the effective value on features +with a particular attribute value in the content source (for source functions). +The interpolation mode tells the SDK how to calculate the effective value +between any two stops: + +In style specification | In the SDK +-----------------------------|----------- +`exponential` | `MGLInterpolationModeExponential` +`interval` | `MGLInterpolationModeInterval` +`categorical` | `MGLInterpolationModeCategorical` +`identity` | `MGLInterpolationModeIdentity` + ## Filtering sources You can filter a shape or vector source by setting the diff --git a/platform/ios/docs/guides/Data-Driven Styling.md b/platform/ios/docs/guides/Using Style Functions at Runtime.md index 0b685512b4..13c4cc0bbc 100644 --- a/platform/ios/docs/guides/Data-Driven Styling.md +++ b/platform/ios/docs/guides/Using Style Functions at Runtime.md @@ -4,46 +4,47 @@ Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. --> -# Data-Driven Styling +# Using Style Functions at Runtime -Mapbox’s data-driven styling features allow you to use data properties to style your maps. You can style map features automatically based on their individual attributes. +[Runtime Styling](runtime-styling.html) enables you to modify every aspect of the map’s appearance dynamically as a user interacts with your application. Much of the runtime styling API allows you to specify _style functions_ instead of constant values. A style function allows you to specify in advance how a layout or paint attribute will vary as the zoom level changes or how the appearance of individual features vary based on metadata provided by a content source. -Vary POI icons, transit route line colors, city polygon opacity, and more based on any attribute in your data. Need to visualize hotel data by price? You can have your map’s point radii and colors change automatically with your data. +Style functions spare you the inconvenience of manually calculating intermediate values between different zoom levels or creating a multitude of style layers to handle homogeneous features in the map content. For example, if your content source indicates the prices of hotels in an area, you can color-code the hotels by price, relying on a style function to smoothly interpolate among desired colors without having to specify the color for each exact price. -![available bikes](img/data-driven-styling/citibikes.png) ![subway lines](img/data-driven-styling/polylineExample.png) +_Data-driven styling_ specifically refers to the use of style functions to vary the map’s appearance based on data in a content source. + +You can also specify style functions in a style JSON file, to be applied automatically when the map loads. See the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-js/style-spec/#types-function) for details. -# How to use Data-Driven Styling -This guide uses earthquake data from the [U.S. Geological Survey](https://earthquake.usgs.gov/earthquakes/feed/v1.0/geojson.php) to style a map based on attributes. For more information about how to work with GeoJSON data in our iOS SDK, please see our [working with GeoJSON data](working-with-geojson-data.html) guide. +![available bikes](img/data-driven-styling/citibikes.png) ![subway lines](img/data-driven-styling/polylineExample.png) -`MGLStyleFunction` +This guide uses earthquake data from the [U.S. Geological Survey](https://earthquake.usgs.gov/earthquakes/feed/v1.0/geojson.php) and data-driven styling to style a map based on attributes. For more information about how to work with GeoJSON data in our iOS SDK, please see our [working with GeoJSON data](working-with-geojson-data.html) guide. -There are three subclasses of `MGLStyleFunction`: +A style function is represented at runtime by the `MGLStyleFunction` class. There are three subclasses of `MGLStyleFunction`: -* `MGLCameraStyleFunction` - For a style value that changes with zoom level. For example, you can make the radius of a circle increase according to zoom level. -* `MGLSourceStyleFunction` - For a style value that changes with the attributes of a feature. For example, you can adjust the radius of a circle based on the magnitude of an earthquake. -* `MGLCompositeStyleFunction` - For a style value that changes with both zoom level and attribute values. For example, you can add a circle layer where each circle has a radius based on both zoom level and the magnitude of an earthquake. +* `MGLCameraStyleFunction` is a style value that changes with zoom level. For example, you can make the radius of a circle increase according to zoom level. +* `MGLSourceStyleFunction` is a style value that changes with the attributes of a feature. For example, you can adjust the radius of a circle based on the magnitude of an earthquake. +* `MGLCompositeStyleFunction` is a style value that changes with both zoom level and attribute values. For example, you can add a circle layer where each circle has a radius based on both zoom level and the magnitude of an earthquake. -The documentation for individual style properties will note which style functions are enabled for that property. +The documentation for each individual style layer property notes which style functions are enabled for that property. ## Stops Stops are key-value pairs that that determine a style value. With a `MGLCameraSourceFunction` stop, you can use a dictionary with a zoom level for a key and a `MGLStyleValue` for the value. For example, you can use a stops dictionary with zoom levels 0, 10, and 20 as keys, and yellow, orange, and red as the values. A `MGLSourceStyleFunction` uses the relevant attribute value as the key. ```swift -let stops = [0: MGLStyleValue(rawValue: UIColor.yellow), - 2.5: MGLStyleValue(rawValue: UIColor.orange), - 5: MGLStyleValue(rawValue: UIColor.red), - 7.5: MGLStyleValue(rawValue: UIColor.blue), - 10: MGLStyleValue(rawValue: UIColor.white)] +let stops = [0: MGLStyleValue<UIColor>(rawValue: .yellow), + 2.5: MGLStyleValue(rawValue: .orange), + 5: MGLStyleValue(rawValue: .red), + 7.5: MGLStyleValue(rawValue: .blue), + 10: MGLStyleValue(rawValue: .white)] ``` -## Interpolation Mode +## Interpolation mode The effect a key has on the style value is determined by the interpolation mode. There are four interpolation modes that can be used with a source style function: exponential, interval, categorical, and identity. You can also use exponential and interval interpolation modes with a camera style function. ### Linear -`MGLInterpolationModelExponential` interpolates linearly or exponentially between style function stop values. By default, the `MGLStyleFunction` options parameter `MGLStyleFunctionOptionInterpolationBase` equals `1`, which represents linear interpolation, and doesn’t need to be included in the options dictionary. +`MGLInterpolationModeExponential` interpolates linearly or exponentially between style function stop values. By default, the `MGLStyleFunction` options parameter `MGLStyleFunctionOptionInterpolationBase` equals `1`, which represents linear interpolation and doesn’t need to be included in the options dictionary. The stops dictionary below, for example, shows colors that continuously shift from yellow to orange to red to blue to white based on the attribute value. @@ -55,11 +56,11 @@ let symbolLayer = MGLSymbolStyleLayer(identifier: "place-city-sm", source: symbo let source = MGLShapeSource(identifier: "earthquakes", url: url, options: nil) style.addSource(source) -let stops = [0: MGLStyleValue(rawValue: UIColor.yellow), - 2.5: MGLStyleValue(rawValue: UIColor.orange), - 5: MGLStyleValue(rawValue: UIColor.red), - 7.5: MGLStyleValue(rawValue: UIColor.blue), - 10: MGLStyleValue(rawValue: UIColor.white)] +let stops = [0: MGLStyleValue<UIColor>(rawValue: .yellow), + 2.5: MGLStyleValue(rawValue: .orange), + 5: MGLStyleValue(rawValue: .red), + 7.5: MGLStyleValue(rawValue: .blue), + 10: MGLStyleValue(rawValue: .white)] let layer = MGLCircleStyleLayer(identifier: "circles", source: source) layer.circleColor = MGLStyleValue(interpolationMode: .exponential, @@ -74,7 +75,7 @@ style.insertLayer(layer, below: symbolLayer) ### Exponential -`MGLInterpolationModelExponential` combined with any `MGLStyleFunctionOptionInterpolationBase` greater than `0`, you can interpolate between values exponentially, create an accelerated ramp effect. +By combining `MGLInterpolationModeExponential` with an `MGLStyleFunctionOptionInterpolationBase` greater than `0` (other than `1`), you can interpolate between values exponentially, create an accelerated ramp effect. Here’s a visualization from Mapbox Studio (see [Working with Mapbox Studio](working-with-mapbox-studio.html)) comparing interpolation base values of `1.5` and `0.5` based on zoom. @@ -100,11 +101,11 @@ layer.circleRadius = MGLStyleValue(interpolationMode: .exponential, When we use the stops dictionary given above with an interval interpolation mode, we create ranges where earthquakes with a magnitude of 0 to just less than 2.5 would be yellow, 2.5 to just less than 5 would be orange, and so on. ``` swift -let stops = [0: MGLStyleValue(rawValue: UIColor.yellow), - 2.5: MGLStyleValue(rawValue: UIColor.orange), - 5: MGLStyleValue(rawValue: UIColor.red), - 7.5: MGLStyleValue(rawValue: UIColor.blue), - 10: MGLStyleValue(rawValue: UIColor.white)] +let stops = [0: MGLStyleValue<UIColor>(rawValue: .yellow), + 2.5: MGLStyleValue(rawValue: .orange), + 5: MGLStyleValue(rawValue: .red), + 7.5: MGLStyleValue(rawValue: .blue), + 10: MGLStyleValue(rawValue: .white)] layer.circleColor = MGLStyleValue(interpolationMode: .interval, sourceStops: stops, @@ -116,19 +117,19 @@ layer.circleColor = MGLStyleValue(interpolationMode: .interval, ### Categorical -Returns the output value that is equal to the stop for the function input. We’re going to use a different stops dictionary than we did for the previous two modes. +At each stop, `MGLInterpolationModeCategorical` produces an output value equal to the function input. We’re going to use a different stops dictionary than we did for the previous two modes. -There are three main types of events in the dataset: earthquakes, explosions, and quarry blasts. In this case, the color of the circle layer will be determined by the type of event, with a default value of green to catch any events that do not fall into any of those categories. +There are three main types of events in the dataset: earthquakes, explosions, and quarry blasts. In this case, the color of the circle layer will be determined by the type of event, with a default value of blue to catch any events that do not fall into any of those categories. ``` swift -let categoricalStops = ["earthquake": MGLStyleValue(rawValue: UIColor.orange), - "explosion": MGLStyleValue(rawValue: UIColor.red), - "quarry blast": MGLStyleValue(rawValue: UIColor.yellow)] +let categoricalStops = ["earthquake": MGLStyleValue<UIColor>(rawValue: .orange), + "explosion": MGLStyleValue(rawValue: .red), + "quarry blast": MGLStyleValue(rawValue: .yellow)] layer.circleColor = MGLStyleValue(interpolationMode: .categorical, sourceStops: categoricalStops, attributeName: "type", - options: [.defaultValue: MGLStyleValue(rawValue: UIColor.blue)]) + options: [.defaultValue: MGLStyleValue<UIColor>(rawValue: .blue)]) ``` diff --git a/platform/ios/jazzy.yml b/platform/ios/jazzy.yml index 5d39e276b6..87af09a9b9 100644 --- a/platform/ios/jazzy.yml +++ b/platform/ios/jazzy.yml @@ -19,7 +19,7 @@ custom_categories: children: - Adding Points to a Map - Runtime Styling - - Data-Driven Styling + - Using Style Functions at Runtime - Working with Mapbox Studio - Working with GeoJSON Data - For Style Authors diff --git a/platform/ios/scripts/document.sh b/platform/ios/scripts/document.sh index d66742a33f..170debb625 100755 --- a/platform/ios/scripts/document.sh +++ b/platform/ios/scripts/document.sh @@ -30,7 +30,8 @@ sed -n -e '/^## /{' -e ':a' -e 'n' -e '/^## /q' -e 'p' -e 'ba' -e '}' platform/i rm -rf ${OUTPUT} mkdir -p ${OUTPUT} -cp -r platform/ios/docs/img "${OUTPUT}/img" +cp -r platform/darwin/docs/img "${OUTPUT}" +cp -r platform/ios/docs/img "${OUTPUT}" DEFAULT_THEME="platform/darwin/docs/theme" THEME=${JAZZY_THEME:-$DEFAULT_THEME} diff --git a/platform/ios/scripts/publish.sh b/platform/ios/scripts/publish.sh index 2934e10217..80ae1ff4e0 100755 --- a/platform/ios/scripts/publish.sh +++ b/platform/ios/scripts/publish.sh @@ -27,7 +27,7 @@ cd build/ios/pkg ZIP="mapbox-ios-sdk-${PUBLISH_VERSION}${PUBLISH_STYLE}.zip" step "Compressing ${ZIP}…" rm -f ../${ZIP} -zip -r ../${ZIP} * +zip -yr ../${ZIP} * # # upload diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index fe3f12b152..2476ec3e97 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -221,10 +221,8 @@ public: @interface MGLMapView () <UIGestureRecognizerDelegate, GLKViewDelegate, CLLocationManagerDelegate, - UIActionSheetDelegate, SMCalloutViewDelegate, MGLCalloutViewDelegate, - UIAlertViewDelegate, MGLMultiPointDelegate, MGLAnnotationImageDelegate> @@ -237,7 +235,6 @@ public: @property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *logoViewConstraints; @property (nonatomic, readwrite) UIButton *attributionButton; @property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *attributionButtonConstraints; -@property (nonatomic) UIActionSheet *attributionSheet; @property (nonatomic, readwrite) MGLStyle *style; @property (nonatomic) UITapGestureRecognizer *singleTapGestureRecognizer; @property (nonatomic) UITapGestureRecognizer *doubleTap; @@ -315,8 +312,6 @@ public: MGLCompassDirectionFormatter *_accessibilityCompassFormatter; - NS_ARRAY_OF(MGLAttributionInfo *) *_attributionInfos; - MGLReachability *_reachability; } @@ -890,11 +885,6 @@ public: _mbglMap->setSize([self size]); } - if (self.attributionSheet.visible) - { - [self.attributionSheet dismissWithClickedButtonIndex:self.attributionSheet.cancelButtonIndex animated:YES]; - } - if (self.compassView.alpha) { [self updateHeadingForDeviceOrientation]; @@ -1456,7 +1446,9 @@ public: } else { - nextElement = _annotationContextsByAnnotationTag[_selectedAnnotationTag].accessibilityElement; + if (_selectedAnnotationTag != MGLAnnotationTagNotFound) { + nextElement = _annotationContextsByAnnotationTag.at(_selectedAnnotationTag).accessibilityElement; + } } [self deselectAnnotation:self.selectedAnnotation animated:YES]; UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nextElement); @@ -1875,82 +1867,108 @@ public: - (void)showAttribution { - self.attributionSheet = [[UIActionSheet alloc] initWithTitle:NSLocalizedStringWithDefaultValue(@"SDK_NAME", nil, nil, @"Mapbox iOS SDK", @"Action sheet title") - delegate:self - cancelButtonTitle:NSLocalizedStringWithDefaultValue(@"CANCEL", nil, nil, @"Cancel", @"") - destructiveButtonTitle:nil - otherButtonTitles:nil]; - - _attributionInfos = [self.style attributionInfosWithFontSize:[UIFont buttonFontSize] linkColor:nil]; - for (MGLAttributionInfo *info in _attributionInfos) + NSString *title = NSLocalizedStringWithDefaultValue(@"SDK_NAME", nil, nil, @"Mapbox iOS SDK", @"Action sheet title"); + UIAlertController *attributionController = [UIAlertController alertControllerWithTitle:title + message:nil + preferredStyle:UIAlertControllerStyleActionSheet]; + + NSArray *attributionInfos = [self.style attributionInfosWithFontSize:[UIFont buttonFontSize] + linkColor:nil]; + for (MGLAttributionInfo *info in attributionInfos) { NSString *title = [info.title.string mgl_titleCasedStringWithLocale:[NSLocale currentLocale]]; - [self.attributionSheet addButtonWithTitle:title]; - } - - [self.attributionSheet addButtonWithTitle:NSLocalizedStringWithDefaultValue(@"TELEMETRY_NAME", nil, nil, @"Mapbox Telemetry", @"Action in attribution sheet")]; - - [self.attributionSheet showFromRect:self.attributionButton.frame inView:self animated:YES]; -} - -- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex -{ - if (buttonIndex == actionSheet.numberOfButtons - 1) - { - NSString *message; - NSString *participate; - NSString *optOut; - - if ([[NSUserDefaults standardUserDefaults] boolForKey:@"MGLMapboxMetricsEnabled"]) - { - message = NSLocalizedStringWithDefaultValue(@"TELEMETRY_ENABLED_MSG", nil, nil, @"You are helping to make OpenStreetMap and Mapbox maps better by contributing anonymous usage data.", @"Telemetry prompt message"); - participate = NSLocalizedStringWithDefaultValue(@"TELEMETRY_ENABLED_ON", nil, nil, @"Keep Participating", @"Telemetry prompt button"); - optOut = NSLocalizedStringWithDefaultValue(@"TELEMETRY_ENABLED_OFF", nil, nil, @"Stop Participating", @"Telemetry prompt button"); - } - else - { - message = NSLocalizedStringWithDefaultValue(@"TELEMETRY_DISABLED_MSG", nil, nil, @"You can help make OpenStreetMap and Mapbox maps better by contributing anonymous usage data.", @"Telemetry prompt message"); - participate = NSLocalizedStringWithDefaultValue(@"TELEMETRY_DISABLED_ON", nil, nil, @"Participate", @"Telemetry prompt button"); - optOut = NSLocalizedStringWithDefaultValue(@"TELEMETRY_DISABLED_OFF", nil, nil, @"Don’t Participate", @"Telemetry prompt button"); - } - - UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedStringWithDefaultValue(@"TELEMETRY_TITLE", nil, nil, @"Make Mapbox Maps Better", @"Telemetry prompt title") - message:message - delegate:self - cancelButtonTitle:participate - otherButtonTitles:NSLocalizedStringWithDefaultValue(@"TELEMETRY_MORE", nil, nil, @"Tell Me More", @"Telemetry prompt button"), optOut, nil]; - [alert show]; - } - else if (buttonIndex > 0) - { - MGLAttributionInfo *info = _attributionInfos[buttonIndex + actionSheet.firstOtherButtonIndex]; - NSURL *url = info.URL; - if (url) - { - if (info.feedbackLink) + UIAlertAction *action = [UIAlertAction actionWithTitle:title + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * _Nonnull action) { + NSURL *url = info.URL; + if (url) { - url = [info feedbackURLAtCenterCoordinate:self.centerCoordinate zoomLevel:self.zoomLevel]; + if (info.feedbackLink) + { + url = [info feedbackURLAtCenterCoordinate:self.centerCoordinate + zoomLevel:self.zoomLevel]; + } + [[UIApplication sharedApplication] openURL:url]; } - [[UIApplication sharedApplication] openURL:url]; - } + }]; + [attributionController addAction:action]; } + + NSString *telemetryTitle = NSLocalizedStringWithDefaultValue(@"TELEMETRY_NAME", nil, nil, @"Mapbox Telemetry", @"Action in attribution sheet"); + UIAlertAction *telemetryAction = [UIAlertAction actionWithTitle:telemetryTitle + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * _Nonnull action) { + [self presentTelemetryAlertController]; + }]; + [attributionController addAction:telemetryAction]; + + NSString *cancelTitle = NSLocalizedStringWithDefaultValue(@"CANCEL", nil, nil, @"Cancel", @""); + UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:cancelTitle + style:UIAlertActionStyleCancel + handler:NULL]; + [attributionController addAction:cancelAction]; + + attributionController.popoverPresentationController.sourceView = self; + attributionController.popoverPresentationController.sourceRect = self.attributionButton.frame; + + UIViewController *viewController = self.window.rootViewController; + if ([viewController isKindOfClass:[UINavigationController class]]) { + viewController = [(UINavigationController *)viewController viewControllers].firstObject; + } + [viewController presentViewController:attributionController + animated:YES + completion:NULL]; } -- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex +- (void)presentTelemetryAlertController { - if (buttonIndex == alertView.cancelButtonIndex) + NSString *title = NSLocalizedStringWithDefaultValue(@"TELEMETRY_TITLE", nil, nil, @"Make Mapbox Maps Better", @"Telemetry prompt title"); + NSString *message; + NSString *participateTitle; + NSString *declineTitle; + if ([[NSUserDefaults standardUserDefaults] boolForKey:@"MGLMapboxMetricsEnabled"]) { - [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"MGLMapboxMetricsEnabled"]; + message = NSLocalizedStringWithDefaultValue(@"TELEMETRY_ENABLED_MSG", nil, nil, @"You are helping to make OpenStreetMap and Mapbox maps better by contributing anonymous usage data.", @"Telemetry prompt message"); + participateTitle = NSLocalizedStringWithDefaultValue(@"TELEMETRY_ENABLED_ON", nil, nil, @"Keep Participating", @"Telemetry prompt button"); + declineTitle = NSLocalizedStringWithDefaultValue(@"TELEMETRY_ENABLED_OFF", nil, nil, @"Stop Participating", @"Telemetry prompt button"); } - else if (buttonIndex == alertView.firstOtherButtonIndex) + else { + message = NSLocalizedStringWithDefaultValue(@"TELEMETRY_DISABLED_MSG", nil, nil, @"You can help make OpenStreetMap and Mapbox maps better by contributing anonymous usage data.", @"Telemetry prompt message"); + participateTitle = NSLocalizedStringWithDefaultValue(@"TELEMETRY_DISABLED_ON", nil, nil, @"Participate", @"Telemetry prompt button"); + declineTitle = NSLocalizedStringWithDefaultValue(@"TELEMETRY_DISABLED_OFF", nil, nil, @"Don’t Participate", @"Telemetry prompt button"); + } + + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title + message:message + preferredStyle:UIAlertControllerStyleAlert]; + + NSString *moreTitle = NSLocalizedStringWithDefaultValue(@"TELEMETRY_MORE", nil, nil, @"Tell Me More", @"Telemetry prompt button"); + UIAlertAction *moreAction = [UIAlertAction actionWithTitle:moreTitle + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * _Nonnull action) { [[UIApplication sharedApplication] openURL: [NSURL URLWithString:@"https://www.mapbox.com/telemetry/"]]; - } - else if (buttonIndex == alertView.firstOtherButtonIndex + 1) - { + }]; + [alertController addAction:moreAction]; + + UIAlertAction *declineAction = [UIAlertAction actionWithTitle:declineTitle + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * _Nonnull action) { [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"MGLMapboxMetricsEnabled"]; - } + }]; + [alertController addAction:declineAction]; + + UIAlertAction *participateAction = [UIAlertAction actionWithTitle:participateTitle + style:UIAlertActionStyleCancel + handler:^(UIAlertAction * _Nonnull action) { + [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"MGLMapboxMetricsEnabled"]; + }]; + [alertController addAction:participateAction]; + + [self.window.rootViewController presentViewController:alertController + animated:YES + completion:NULL]; } #pragma mark - Properties - @@ -1980,19 +1998,21 @@ public: { const mbgl::Point<double> point = MGLPointFromLocationCoordinate2D(annotation.coordinate); - MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(annotationTag); - if (annotationContext.annotationView) - { - // Redundantly move the associated annotation view outside the scope of the animation-less transaction block in -updateAnnotationViews. - annotationContext.annotationView.center = [self convertCoordinate:annotationContext.annotation.coordinate toPointToView:self]; - } + if (annotationTag != MGLAnnotationTagNotFound) { + MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(annotationTag); + if (annotationContext.annotationView) + { + // Redundantly move the associated annotation view outside the scope of the animation-less transaction block in -updateAnnotationViews. + annotationContext.annotationView.center = [self convertCoordinate:annotationContext.annotation.coordinate toPointToView:self]; + } - MGLAnnotationImage *annotationImage = [self imageOfAnnotationWithTag:annotationTag]; - NSString *symbolName = annotationImage.styleIconIdentifier; + MGLAnnotationImage *annotationImage = [self imageOfAnnotationWithTag:annotationTag]; + NSString *symbolName = annotationImage.styleIconIdentifier; - // Update the annotation’s backing geometry to match the annotation model object. Any associated annotation view is also moved by side effect. However, -updateAnnotationViews disables the view’s animation actions, because it can’t distinguish between moves due to the viewport changing and moves due to the annotation’s coordinate changing. - _mbglMap->updateAnnotation(annotationTag, mbgl::SymbolAnnotation { point, symbolName.UTF8String }); - [self updateCalloutView]; + // Update the annotation’s backing geometry to match the annotation model object. Any associated annotation view is also moved by side effect. However, -updateAnnotationViews disables the view’s animation actions, because it can’t distinguish between moves due to the viewport changing and moves due to the annotation’s coordinate changing. + _mbglMap->updateAnnotation(annotationTag, mbgl::SymbolAnnotation { point, symbolName.UTF8String }); + [self updateCalloutView]; + } } } else if ([keyPath isEqualToString:@"coordinates"] && [object isKindOfClass:[MGLMultiPoint class]]) @@ -3068,12 +3088,18 @@ public: for (auto const& annotationTag: annotationTags) { - if (!_annotationContextsByAnnotationTag.count(annotationTag)) + if (!_annotationContextsByAnnotationTag.count(annotationTag) || + annotationTag == MGLAnnotationTagNotFound) { continue; } + MGLAnnotationContext annotationContext = _annotationContextsByAnnotationTag.at(annotationTag); - [annotations addObject:annotationContext.annotation]; + NSAssert(annotationContext.annotation, @"Missing annotation for tag %u.", annotationTag); + if (annotationContext.annotation) + { + [annotations addObject:annotationContext.annotation]; + } } return [annotations copy]; @@ -3085,12 +3111,12 @@ public: /// Returns the annotation assigned the given tag. Cheap. - (id <MGLAnnotation>)annotationWithTag:(MGLAnnotationTag)tag { - if ( ! _annotationContextsByAnnotationTag.count(tag)) - { + if ( ! _annotationContextsByAnnotationTag.count(tag) || + tag == MGLAnnotationTagNotFound) { return nil; } - MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag[tag]; + MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(tag); return annotationContext.annotation; } @@ -3560,8 +3586,12 @@ public: { id <MGLAnnotation> annotation = [self annotationWithTag:annotationTag]; NSAssert(annotation, @"Unknown annotation found nearby tap"); + if ( ! annotation) + { + return true; + } - MGLAnnotationContext annotationContext = _annotationContextsByAnnotationTag[annotationTag]; + MGLAnnotationContext annotationContext = _annotationContextsByAnnotationTag.at(annotationTag); CGRect annotationRect; MGLAnnotationView *annotationView = annotationContext.annotationView; @@ -3684,10 +3714,12 @@ public: { return self.userLocation; } - if ( ! _annotationContextsByAnnotationTag.count(_selectedAnnotationTag)) - { + + if ( ! _annotationContextsByAnnotationTag.count(_selectedAnnotationTag) || + _selectedAnnotationTag == MGLAnnotationTagNotFound) { return nil; } + MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(_selectedAnnotationTag); return annotationContext.annotation; } @@ -3753,19 +3785,16 @@ public: MGLAnnotationView *annotationView = nil; if (annotation != self.userLocation) - { - MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(annotationTag); - - annotationView = annotationContext.annotationView; - - if (annotationView && annotationView.enabled) - { - // Annotations represented by views use the view frame as the positioning rect. - positioningRect = annotationView.frame; - - [annotationView.superview bringSubviewToFront:annotationView]; - - [annotationView setSelected:YES animated:animated]; + if (annotationTag != MGLAnnotationTagNotFound) { + MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(annotationTag); + annotationView = annotationContext.annotationView; + if (annotationView && annotationView.enabled) { + { + // Annotations represented by views use the view frame as the positioning rect. + positioningRect = annotationView.frame; + [annotationView.superview bringSubviewToFront:annotationView]; + [annotationView setSelected:YES animated:animated]; + } } } @@ -3787,7 +3816,14 @@ public: UIView <MGLCalloutView> *calloutView; if ([self.delegate respondsToSelector:@selector(mapView:calloutViewForAnnotation:)]) { - calloutView = [self.delegate mapView:self calloutViewForAnnotation:annotation]; + id providedCalloutView = [self.delegate mapView:self calloutViewForAnnotation:annotation]; + if (providedCalloutView) { + if (![providedCalloutView isKindOfClass:[UIView class]]) { + [NSException raise:NSInvalidArgumentException format:@"Callout view must be a kind of UIView"]; + } + NSAssert([providedCalloutView conformsToProtocol:@protocol(MGLCalloutView)], @"callout view must conform to MGLCalloutView"); + calloutView = providedCalloutView; + } } if (!calloutView) { @@ -3868,8 +3904,6 @@ public: /// and is appropriate for positioning a popover. - (CGRect)positioningRectForCalloutForAnnotationWithTag:(MGLAnnotationTag)annotationTag { - MGLAnnotationContext annotationContext = _annotationContextsByAnnotationTag[annotationTag]; - id <MGLAnnotation> annotation = [self annotationWithTag:annotationTag]; if ( ! annotation) { @@ -5048,8 +5082,12 @@ public: if (isAnchoredToAnnotation) { MGLAnnotationTag tag = [self annotationTagForAnnotation:annotation]; - MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(tag); - MGLAnnotationView *annotationView = annotationContext.annotationView; + MGLAnnotationView *annotationView = nil; + + if (tag != MGLAnnotationTagNotFound) { + MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(tag); + annotationView = annotationContext.annotationView; + } CGRect rect = [self positioningRectForCalloutForAnnotationWithTag:tag]; diff --git a/platform/ios/src/MGLMapViewDelegate.h b/platform/ios/src/MGLMapViewDelegate.h index c1faaa3d07..096711fcbb 100644 --- a/platform/ios/src/MGLMapViewDelegate.h +++ b/platform/ios/src/MGLMapViewDelegate.h @@ -456,7 +456,7 @@ NS_ASSUME_NONNULL_BEGIN @return A view conforming to the `MGLCalloutView` protocol, or `nil` to use the default callout view. */ -- (nullable UIView <MGLCalloutView> *)mapView:(MGLMapView *)mapView calloutViewForAnnotation:(id <MGLAnnotation>)annotation; +- (nullable id <MGLCalloutView>)mapView:(MGLMapView *)mapView calloutViewForAnnotation:(id <MGLAnnotation>)annotation; /** Returns the view to display on the left side of the standard callout bubble. diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index dab02592a5..1f47ef98d4 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -5,15 +5,24 @@ * Fixed an issue causing attribution button text to appear blue instead of black. ([#8701](https://github.com/mapbox/mapbox-gl-native/pull/8701)) * The error passed into `-[MGLMapViewDelegate mapViewDidFailLoadingMap:withError:]` now includes a more specific description and failure reason. ([#8418](https://github.com/mapbox/mapbox-gl-native/pull/8418)) -## 0.4.0 +## 0.4.1 + +This version of the Mapbox macOS SDK corresponds to version 3.5.2 of the Mapbox iOS SDK. + +* Fixed an issue causing code signing failures and bloating the framework. ([#8640](https://github.com/mapbox/mapbox-gl-native/pull/8640)) +* Fixed an issue that could cause a crash if annotations unknown to the map view were interacted with. ([#8686](https://github.com/mapbox/mapbox-gl-native/pull/8686)) +* Renamed the “Data-Driven Styling” guide to “Using Style Functions at Runtime” and clarified the meaning of data-driven styling in the guide’s discussion of runtime style functions. ([#8627](https://github.com/mapbox/mapbox-gl-native/pull/8627)) + +## 0.4.0 - April 2, 2017 + +This version of the Mapbox macOS SDK corresponds to version 3.5.1 of the Mapbox iOS SDK. ### Internationalization * Added support for right-to-left text and Arabic ligatures in labels. ([#6984](https://github.com/mapbox/mapbox-gl-native/pull/6984), [#7123](https://github.com/mapbox/mapbox-gl-native/pull/7123)) * Improved the line wrapping behavior of point-placed labels, especially labels written in Chinese and Japanese. ([#6828](https://github.com/mapbox/mapbox-gl-native/pull/6828), [#7446](https://github.com/mapbox/mapbox-gl-native/pull/7446)) * CJK characters now remain upright in vertically oriented labels that have line placement, such as road labels. ([#7114](https://github.com/mapbox/mapbox-gl-native/issues/7114)) -* Added Catalan, Chinese (Simplified and Traditional), Dutch, Finnish, French, German, Japanese, Lithuanian, Polish, Portuguese (Brazilian), Spanish, Swedish, Ukrainian, and Vietnamese localizations. ([#7316](https://github.com/mapbox/mapbox-gl-native/pull/7316), [#7503](https://github.com/mapbox/mapbox-gl-native/pull/7503), [#7899](https://github.com/mapbox/mapbox-gl-native/pull/7899), [#7999](https://github.com/mapbox/mapbox-gl-native/pull/7999), -[#8113](https://github.com/mapbox/mapbox-gl-native/pull/8113), [#8256](https://github.com/mapbox/mapbox-gl-native/pull/8256)) +* Added Catalan, Chinese (Simplified and Traditional), Dutch, Finnish, French, German, Japanese, Lithuanian, Polish, Portuguese (Brazilian), Spanish, Swedish, Ukrainian, and Vietnamese localizations. ([#7316](https://github.com/mapbox/mapbox-gl-native/pull/7316), [#7503](https://github.com/mapbox/mapbox-gl-native/pull/7503), [#7899](https://github.com/mapbox/mapbox-gl-native/pull/7899), [#7999](https://github.com/mapbox/mapbox-gl-native/pull/7999), [#8113](https://github.com/mapbox/mapbox-gl-native/pull/8113), [#8256](https://github.com/mapbox/mapbox-gl-native/pull/8256)) ### Styles diff --git a/platform/macos/Mapbox-macOS-SDK-symbols.podspec b/platform/macos/Mapbox-macOS-SDK-symbols.podspec index c17e665512..c621a7312b 100644 --- a/platform/macos/Mapbox-macOS-SDK-symbols.podspec +++ b/platform/macos/Mapbox-macOS-SDK-symbols.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '0.3.1' + version = '0.4.1' m.name = 'Mapbox-macOS-SDK-symbols' m.version = "#{version}-symbols" diff --git a/platform/macos/Mapbox-macOS-SDK.podspec b/platform/macos/Mapbox-macOS-SDK.podspec index a6f2d2838b..2417fb11ec 100644 --- a/platform/macos/Mapbox-macOS-SDK.podspec +++ b/platform/macos/Mapbox-macOS-SDK.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '0.3.1' + version = '0.4.1' m.name = 'Mapbox-macOS-SDK' m.version = version diff --git a/platform/macos/docs/guides/For Style Authors.md b/platform/macos/docs/guides/For Style Authors.md index 9c3665d816..9ed69ac9ea 100644 --- a/platform/macos/docs/guides/For Style Authors.md +++ b/platform/macos/docs/guides/For Style Authors.md @@ -179,30 +179,6 @@ 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 functions - -The runtime styling API introduces `MGLStyleFunction` to the macOS SDK. -[Data-driven styling](data-driven-styling.html) expands `MGLStyleFunction`. -Individual style property documentation includes which subclasses of -`MGLStyleFunction` are enabled for that property. You can use `MGLStyleValue` -methods to create a `MGLStyleFunction`. - -In style specification | In the SDK | [MGLStyleValue valueWithInterpolationMode:...] ------------------------|-------------------------------|------------- -`zoom function` | `MGLCameraStyleFunction` | `cameraStops:options:` -`property function` | `MGLSourceStyleFunction` | `sourceStops:attributeName:options:` -`zoom-and-property functions`| `MGLCompositeStyleFunction` | `compositeStops:attributeName:options:` - -Data-driven styling also introduces interpolation mode, which defines the -relationship between style values and attributes or zoom levels. - -In style specification | In the SDK ------------------------------|----------- -`exponential` | `MGLInterpolationModeExponential` -`interval` | `MGLInterpolationModeInterval` -`categorical` | `MGLInterpolationModeCategorical` -`identity` | `MGLInterpolationModeIdentity` - ### Circle style layers In style JSON | In Objective-C | In Swift @@ -211,30 +187,6 @@ In style JSON | In Objective-C | In Swift `circle-translate` | `MGLCircleStyleLayer.circleTranslation` | `MGLCircleStyleLayer.circleTranslation` `circle-translate-anchor` | `MGLCircleStyleLayer.circleTranslationAnchor` | `MGLCircleStyleLayer.circleTranslationAnchor` -### Fill style functions - -The runtime styling API introduces `MGLStyleFunction` to the macOS SDK. -[Data-driven styling](data-driven-styling.html) expands `MGLStyleFunction`. -Individual style property documentation includes which subclasses of -`MGLStyleFunction` are enabled for that property. You can use `MGLStyleValue` -methods to create a `MGLStyleFunction`. - -In style specification | In the SDK | [MGLStyleValue valueWithInterpolationMode:...] ------------------------|-------------------------------|------------- -`zoom function` | `MGLCameraStyleFunction` | `cameraStops:options:` -`property function` | `MGLSourceStyleFunction` | `sourceStops:attributeName:options:` -`zoom-and-property functions`| `MGLCompositeStyleFunction` | `compositeStops:attributeName:options:` - -Data-driven styling also introduces interpolation mode, which defines the -relationship between style values and attributes or zoom levels. - -In style specification | In the SDK ------------------------------|----------- -`exponential` | `MGLInterpolationModeExponential` -`interval` | `MGLInterpolationModeInterval` -`categorical` | `MGLInterpolationModeCategorical` -`identity` | `MGLInterpolationModeIdentity` - ### Fill style layers In style JSON | In Objective-C | In Swift @@ -243,30 +195,6 @@ In style JSON | In Objective-C | In Swift `fill-translate` | `MGLFillStyleLayer.fillTranslation` | `MGLFillStyleLayer.fillTranslation` `fill-translate-anchor` | `MGLFillStyleLayer.fillTranslationAnchor` | `MGLFillStyleLayer.fillTranslationAnchor` -### Line style functions - -The runtime styling API introduces `MGLStyleFunction` to the macOS SDK. -[Data-driven styling](data-driven-styling.html) expands `MGLStyleFunction`. -Individual style property documentation includes which subclasses of -`MGLStyleFunction` are enabled for that property. You can use `MGLStyleValue` -methods to create a `MGLStyleFunction`. - -In style specification | In the SDK | [MGLStyleValue valueWithInterpolationMode:...] ------------------------|-------------------------------|------------- -`zoom function` | `MGLCameraStyleFunction` | `cameraStops:options:` -`property function` | `MGLSourceStyleFunction` | `sourceStops:attributeName:options:` -`zoom-and-property functions`| `MGLCompositeStyleFunction` | `compositeStops:attributeName:options:` - -Data-driven styling also introduces interpolation mode, which defines the -relationship between style values and attributes or zoom levels. - -In style specification | In the SDK ------------------------------|----------- -`exponential` | `MGLInterpolationModeExponential` -`interval` | `MGLInterpolationModeInterval` -`categorical` | `MGLInterpolationModeCategorical` -`identity` | `MGLInterpolationModeIdentity` - ### Line style layers In style JSON | In Objective-C | In Swift @@ -275,30 +203,6 @@ In style JSON | In Objective-C | In Swift `line-translate` | `MGLLineStyleLayer.lineTranslation` | `MGLLineStyleLayer.lineTranslation` `line-translate-anchor` | `MGLLineStyleLayer.lineTranslationAnchor` | `MGLLineStyleLayer.lineTranslationAnchor` -### Raster style functions - -The runtime styling API introduces `MGLStyleFunction` to the macOS SDK. -[Data-driven styling](data-driven-styling.html) expands `MGLStyleFunction`. -Individual style property documentation includes which subclasses of -`MGLStyleFunction` are enabled for that property. You can use `MGLStyleValue` -methods to create a `MGLStyleFunction`. - -In style specification | In the SDK | [MGLStyleValue valueWithInterpolationMode:...] ------------------------|-------------------------------|------------- -`zoom function` | `MGLCameraStyleFunction` | `cameraStops:options:` -`property function` | `MGLSourceStyleFunction` | `sourceStops:attributeName:options:` -`zoom-and-property functions`| `MGLCompositeStyleFunction` | `compositeStops:attributeName:options:` - -Data-driven styling also introduces interpolation mode, which defines the -relationship between style values and attributes or zoom levels. - -In style specification | In the SDK ------------------------------|----------- -`exponential` | `MGLInterpolationModeExponential` -`interval` | `MGLInterpolationModeInterval` -`categorical` | `MGLInterpolationModeCategorical` -`identity` | `MGLInterpolationModeIdentity` - ### Raster style layers In style JSON | In Objective-C | In Swift @@ -307,30 +211,6 @@ In style JSON | In Objective-C | In Swift `raster-brightness-min` | `MGLRasterStyleLayer.minimumRasterBrightness` | `MGLRasterStyleLayer.minimumRasterBrightness` `raster-hue-rotate` | `MGLRasterStyleLayer.rasterHueRotation` | `MGLRasterStyleLayer.rasterHueRotation` -### Symbol style functions - -The runtime styling API introduces `MGLStyleFunction` to the macOS SDK. -[Data-driven styling](data-driven-styling.html) expands `MGLStyleFunction`. -Individual style property documentation includes which subclasses of -`MGLStyleFunction` are enabled for that property. You can use `MGLStyleValue` -methods to create a `MGLStyleFunction`. - -In style specification | In the SDK | [MGLStyleValue valueWithInterpolationMode:...] ------------------------|-------------------------------|------------- -`zoom function` | `MGLCameraStyleFunction` | `cameraStops:options:` -`property function` | `MGLSourceStyleFunction` | `sourceStops:attributeName:options:` -`zoom-and-property functions`| `MGLCompositeStyleFunction` | `compositeStops:attributeName:options:` - -Data-driven styling also introduces interpolation mode, which defines the -relationship between style values and attributes or zoom levels. - -In style specification | In the SDK ------------------------------|----------- -`exponential` | `MGLInterpolationModeExponential` -`interval` | `MGLInterpolationModeInterval` -`categorical` | `MGLInterpolationModeCategorical` -`identity` | `MGLInterpolationModeIdentity` - ### Symbol style layers In style JSON | In Objective-C | In Swift @@ -363,10 +243,12 @@ In style JSON | In Objective-C | In Swift Each property representing a layout or paint attribute is set to an `MGLStyleValue` object, which is either an `MGLConstantStyleValue` object (for -constant values) or an `MGLStyleFunction` object (for zoom level functions). The +constant values) or an `MGLStyleFunction` object (for style functions). The style value object is a container for the raw value or function parameters that you want the attribute to be set to. +### Constant style values + 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. @@ -397,6 +279,39 @@ 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. +### Style functions + +A _style function_ allows you to vary the value of a layout or paint attribute +based on the zoom level, data provided by content sources, or both. For more +information about style functions, see “[Using Style Functions at Runtime](using-style-functions-at-runtime.html)”. + +Each kind of style function is represented by a distinct class, but you +typically create style functions as you create any other style value, using +class methods on `MGLStyleValue`: + +In style specification | SDK class | SDK factory method +---------------------------|-----------------------------|------------------- +zoom function | `MGLCameraStyleFunction` | `+[MGLStyleValue valueWithInterpolationMode:cameraStops:options:]` +property function | `MGLSourceStyleFunction` | `+[MGLStyleValue valueWithInterpolationMode:sourceStops:attributeName:options:]` +zoom-and-property function | `MGLCompositeStyleFunction` | `+[MGLStyleValue valueWithInterpolationMode:compositeStops:attributeName:options:]` + +The documentation for each individual style layer property indicates the kinds +of style functions that are enabled for that property. + +When you create a style function, you specify an _interpolation mode_ and a +series of _stops_. Each stop determines the effective value displayed at a +particular zoom level (for camera functions) or the effective value on features +with a particular attribute value in the content source (for source functions). +The interpolation mode tells the SDK how to calculate the effective value +between any two stops: + +In style specification | In the SDK +-----------------------------|----------- +`exponential` | `MGLInterpolationModeExponential` +`interval` | `MGLInterpolationModeInterval` +`categorical` | `MGLInterpolationModeCategorical` +`identity` | `MGLInterpolationModeIdentity` + ## Filtering sources You can filter a shape or vector source by setting the diff --git a/platform/macos/docs/guides/Data-Driven Styling.md b/platform/macos/docs/guides/Using Style Functions at Runtime.md index 9e38e71318..b3098dfe04 100644 --- a/platform/macos/docs/guides/Data-Driven Styling.md +++ b/platform/macos/docs/guides/Using Style Functions at Runtime.md @@ -4,46 +4,47 @@ Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. --> -# Data-Driven Styling +# Using Style Functions at Runtime -Mapbox’s data-driven styling features allow you to use data properties to style your maps. You can style map features automatically based on their individual attributes. +[Runtime Styling](runtime-styling.html) enables you to modify every aspect of the map’s appearance dynamically as a user interacts with your application. Much of the runtime styling API allows you to specify _style functions_ instead of constant values. A style function allows you to specify in advance how a layout or paint attribute will vary as the zoom level changes or how the appearance of individual features vary based on metadata provided by a content source. -Vary POI icons, transit route line colors, city polygon opacity, and more based on any attribute in your data. Need to visualize hotel data by price? You can have your map’s point radii and colors change automatically with your data. +Style functions spare you the inconvenience of manually calculating intermediate values between different zoom levels or creating a multitude of style layers to handle homogeneous features in the map content. For example, if your content source indicates the prices of hotels in an area, you can color-code the hotels by price, relying on a style function to smoothly interpolate among desired colors without having to specify the color for each exact price. -![available bikes](img/data-driven-styling/citibikes.png) ![subway lines](img/data-driven-styling/polylineExample.png) +_Data-driven styling_ specifically refers to the use of style functions to vary the map’s appearance based on data in a content source. + +You can also specify style functions in a style JSON file, to be applied automatically when the map loads. See the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-js/style-spec/#types-function) for details. -# How to use Data-Driven Styling -This guide uses earthquake data from the [U.S. Geological Survey](https://earthquake.usgs.gov/earthquakes/feed/v1.0/geojson.php) to style a map based on attributes. For more information about how to work with GeoJSON data in our iOS SDK, please see our [working with GeoJSON data](working-with-geojson-data.html) guide. +![available bikes](img/data-driven-styling/citibikes.png) ![subway lines](img/data-driven-styling/polylineExample.png) -`MGLStyleFunction` +This guide uses earthquake data from the [U.S. Geological Survey](https://earthquake.usgs.gov/earthquakes/feed/v1.0/geojson.php) and data-driven styling to style a map based on attributes. For more information about how to work with GeoJSON data in our iOS SDK, please see our [working with GeoJSON data](working-with-geojson-data.html) guide. -There are three subclasses of `MGLStyleFunction`: +A style function is represented at runtime by the `MGLStyleFunction` class. There are three subclasses of `MGLStyleFunction`: -* `MGLCameraStyleFunction` - For a style value that changes with zoom level. For example, you can make the radius of a circle increase according to zoom level. -* `MGLSourceStyleFunction` - For a style value that changes with the attributes of a feature. For example, you can adjust the radius of a circle based on the magnitude of an earthquake. -* `MGLCompositeStyleFunction` - For a style value that changes with both zoom level and attribute values. For example, you can add a circle layer where each circle has a radius based on both zoom level and the magnitude of an earthquake. +* `MGLCameraStyleFunction` is a style value that changes with zoom level. For example, you can make the radius of a circle increase according to zoom level. +* `MGLSourceStyleFunction` is a style value that changes with the attributes of a feature. For example, you can adjust the radius of a circle based on the magnitude of an earthquake. +* `MGLCompositeStyleFunction` is a style value that changes with both zoom level and attribute values. For example, you can add a circle layer where each circle has a radius based on both zoom level and the magnitude of an earthquake. -The documentation for individual style properties will note which style functions are enabled for that property. +The documentation for each individual style layer property notes which style functions are enabled for that property. ## Stops Stops are key-value pairs that that determine a style value. With a `MGLCameraSourceFunction` stop, you can use a dictionary with a zoom level for a key and a `MGLStyleValue` for the value. For example, you can use a stops dictionary with zoom levels 0, 10, and 20 as keys, and yellow, orange, and red as the values. A `MGLSourceStyleFunction` uses the relevant attribute value as the key. ```swift -let stops = [0: MGLStyleValue(rawValue: UIColor.yellow), - 2.5: MGLStyleValue(rawValue: UIColor.orange), - 5: MGLStyleValue(rawValue: UIColor.red), - 7.5: MGLStyleValue(rawValue: UIColor.blue), - 10: MGLStyleValue(rawValue: UIColor.white)] +let stops = [0: MGLStyleValue<NSColor>(rawValue: .yellow), + 2.5: MGLStyleValue(rawValue: .orange), + 5: MGLStyleValue(rawValue: .red), + 7.5: MGLStyleValue(rawValue: .blue), + 10: MGLStyleValue(rawValue: .white)] ``` -## Interpolation Mode +## Interpolation mode The effect a key has on the style value is determined by the interpolation mode. There are four interpolation modes that can be used with a source style function: exponential, interval, categorical, and identity. You can also use exponential and interval interpolation modes with a camera style function. ### Linear -`MGLInterpolationModelExponential` interpolates linearly or exponentially between style function stop values. By default, the `MGLStyleFunction` options parameter `MGLStyleFunctionOptionInterpolationBase` equals `1`, which represents linear interpolation, and doesn’t need to be included in the options dictionary. +`MGLInterpolationModeExponential` interpolates linearly or exponentially between style function stop values. By default, the `MGLStyleFunction` options parameter `MGLStyleFunctionOptionInterpolationBase` equals `1`, which represents linear interpolation and doesn’t need to be included in the options dictionary. The stops dictionary below, for example, shows colors that continuously shift from yellow to orange to red to blue to white based on the attribute value. @@ -55,11 +56,11 @@ let symbolLayer = MGLSymbolStyleLayer(identifier: "place-city-sm", source: symbo let source = MGLShapeSource(identifier: "earthquakes", url: url, options: nil) style.addSource(source) -let stops = [0: MGLStyleValue(rawValue: NSColor.yellow), - 2.5: MGLStyleValue(rawValue: NSColor.orange), - 5: MGLStyleValue(rawValue: NSColor.red), - 7.5: MGLStyleValue(rawValue: NSColor.blue), - 10: MGLStyleValue(rawValue: NSColor.white)] +let stops = [0: MGLStyleValue<NSColor>(rawValue: .yellow), + 2.5: MGLStyleValue(rawValue: .orange), + 5: MGLStyleValue(rawValue: .red), + 7.5: MGLStyleValue(rawValue: .blue), + 10: MGLStyleValue(rawValue: .white)] let layer = MGLCircleStyleLayer(identifier: "circles", source: source) layer.circleColor = MGLStyleValue(interpolationMode: .exponential, @@ -74,7 +75,7 @@ style.insertLayer(layer, below: symbolLayer) ### Exponential -`MGLInterpolationModelExponential` combined with any `MGLStyleFunctionOptionInterpolationBase` greater than `0`, you can interpolate between values exponentially, create an accelerated ramp effect. +By combining `MGLInterpolationModeExponential` with an `MGLStyleFunctionOptionInterpolationBase` greater than `0` (other than `1`), you can interpolate between values exponentially, create an accelerated ramp effect. Here’s a visualization from Mapbox Studio (see [Working with Mapbox Studio](working-with-mapbox-studio.html)) comparing interpolation base values of `1.5` and `0.5` based on zoom. @@ -100,11 +101,11 @@ layer.circleRadius = MGLStyleValue(interpolationMode: .exponential, When we use the stops dictionary given above with an interval interpolation mode, we create ranges where earthquakes with a magnitude of 0 to just less than 2.5 would be yellow, 2.5 to just less than 5 would be orange, and so on. ``` swift -let stops = [0: MGLStyleValue(rawValue: NSColor.yellow), - 2.5: MGLStyleValue(rawValue: NSColor.orange), - 5: MGLStyleValue(rawValue: NSColor.red), - 7.5: MGLStyleValue(rawValue: NSColor.blue), - 10: MGLStyleValue(rawValue: NSColor.white)] +let stops = [0: MGLStyleValue<NSColor>(rawValue: .yellow), + 2.5: MGLStyleValue(rawValue: .orange), + 5: MGLStyleValue(rawValue: .red), + 7.5: MGLStyleValue(rawValue: .blue), + 10: MGLStyleValue(rawValue: .white)] layer.circleColor = MGLStyleValue(interpolationMode: .interval, sourceStops: stops, @@ -116,19 +117,19 @@ layer.circleColor = MGLStyleValue(interpolationMode: .interval, ### Categorical -Returns the output value that is equal to the stop for the function input. We’re going to use a different stops dictionary than we did for the previous two modes. +At each stop, `MGLInterpolationModeCategorical` produces an output value equal to the function input. We’re going to use a different stops dictionary than we did for the previous two modes. -There are three main types of events in the dataset: earthquakes, explosions, and quarry blasts. In this case, the color of the circle layer will be determined by the type of event, with a default value of green to catch any events that do not fall into any of those categories. +There are three main types of events in the dataset: earthquakes, explosions, and quarry blasts. In this case, the color of the circle layer will be determined by the type of event, with a default value of blue to catch any events that do not fall into any of those categories. ``` swift -let categoricalStops = ["earthquake": MGLStyleValue(rawValue: NSColor.orange), - "explosion": MGLStyleValue(rawValue: NSColor.red), - "quarry blast": MGLStyleValue(rawValue: NSColor.yellow)] +let categoricalStops = ["earthquake": MGLStyleValue<NSColor>(rawValue: .orange), + "explosion": MGLStyleValue(rawValue: .red), + "quarry blast": MGLStyleValue(rawValue: .yellow)] layer.circleColor = MGLStyleValue(interpolationMode: .categorical, sourceStops: categoricalStops, attributeName: "type", - options: [.defaultValue: MGLStyleValue(rawValue: NSColor.blue)]) + options: [.defaultValue: MGLStyleValue<NSColor>(rawValue: .blue)]) ``` diff --git a/platform/macos/jazzy.yml b/platform/macos/jazzy.yml index 9323119718..1f0e2fbc74 100644 --- a/platform/macos/jazzy.yml +++ b/platform/macos/jazzy.yml @@ -19,7 +19,7 @@ custom_categories: children: - Working with GeoJSON Data - For Style Authors - - Data-Driven Styling + - Using Style Functions at Runtime - Info.plist Keys - name: Maps children: @@ -27,7 +27,6 @@ custom_categories: - MGLMapCamera - MGLMapView - MGLMapViewDelegate - - MGLUserTrackingMode - name: Shapes and Annotations children: - MGLAnnotation diff --git a/platform/macos/scripts/deploy-packages.sh b/platform/macos/scripts/deploy-packages.sh index 8f61519244..d9d74b2867 100755 --- a/platform/macos/scripts/deploy-packages.sh +++ b/platform/macos/scripts/deploy-packages.sh @@ -37,7 +37,7 @@ buildPackageStyle() { step "Compressing ${file_name}…" cd build/macos/pkg rm -f ../deploy/${file_name} - zip -r ../deploy/${file_name} * + zip -yr ../deploy/${file_name} * cd - if [[ "${GITHUB_RELEASE}" == true ]]; then echo "Uploading ${file_name} to GitHub" diff --git a/platform/macos/scripts/document.sh b/platform/macos/scripts/document.sh index 69c9aaa871..2e53dc359b 100755 --- a/platform/macos/scripts/document.sh +++ b/platform/macos/scripts/document.sh @@ -36,7 +36,8 @@ sed -n -e '/^## /{' -e ':a' -e 'n' -e '/^## /q' -e 'p' -e 'ba' -e '}' platform/m rm -rf ${OUTPUT} mkdir -p ${OUTPUT} -cp -r platform/macos/docs/img "${OUTPUT}/img" +cp -r platform/darwin/docs/img "${OUTPUT}" +cp -r platform/macos/docs/img "${OUTPUT}" jazzy \ --config platform/macos/jazzy.yml \ diff --git a/platform/macos/src/MGLMapView.mm b/platform/macos/src/MGLMapView.mm index 5d14192c61..68a21b5666 100644 --- a/platform/macos/src/MGLMapView.mm +++ b/platform/macos/src/MGLMapView.mm @@ -1806,12 +1806,18 @@ public: for (auto const& annotationTag: annotationTags) { - if (!_annotationContextsByAnnotationTag.count(annotationTag)) + if (!_annotationContextsByAnnotationTag.count(annotationTag) || + annotationTag == MGLAnnotationTagNotFound) { continue; } + MGLAnnotationContext annotationContext = _annotationContextsByAnnotationTag.at(annotationTag); - [annotations addObject:annotationContext.annotation]; + NSAssert(annotationContext.annotation, @"Missing annotation for tag %u.", annotationTag); + if (annotationContext.annotation) + { + [annotations addObject:annotationContext.annotation]; + } } return [annotations copy]; @@ -1822,11 +1828,12 @@ public: /// Returns the annotation assigned the given tag. Cheap. - (id <MGLAnnotation>)annotationWithTag:(MGLAnnotationTag)tag { - if (!_annotationContextsByAnnotationTag.count(tag)) { + if ( ! _annotationContextsByAnnotationTag.count(tag) || + tag == MGLAnnotationTagNotFound) { return nil; } - MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag[tag]; + MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(tag); return annotationContext.annotation; } @@ -2063,8 +2070,8 @@ public: // Filter out any annotation whose image is unselectable or for which // hit testing fails. auto end = std::remove_if(nearbyAnnotations.begin(), nearbyAnnotations.end(), [&](const MGLAnnotationTag annotationTag) { - NSAssert(_annotationContextsByAnnotationTag.count(annotationTag) != 0, @"Unknown annotation found nearby click"); id <MGLAnnotation> annotation = [self annotationWithTag:annotationTag]; + NSAssert(annotation, @"Unknown annotation found nearby click"); if (!annotation) { return true; } @@ -2153,9 +2160,11 @@ public: } - (id <MGLAnnotation>)selectedAnnotation { - if (!_annotationContextsByAnnotationTag.count(_selectedAnnotationTag)) { + if ( ! _annotationContextsByAnnotationTag.count(_selectedAnnotationTag) || + _selectedAnnotationTag == MGLAnnotationTagNotFound) { return nil; } + MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(_selectedAnnotationTag); return annotationContext.annotation; } |