diff options
Diffstat (limited to 'platform')
147 files changed, 4057 insertions, 855 deletions
diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index 7807334c14..a9d31426c3 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -3,11 +3,51 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started. ## 5.1.0 - TBA +* Fix tracking mode + camera race condition [#9133](https://github.com/mapbox/mapbox-gl-native/pull/9133) +* Harden orientation changes [#9128](https://github.com/mapbox/mapbox-gl-native/pull/9128) + +## 5.1.0-beta.3 - May 26, 2017 + +* Add binding support for Light [#9013](https://github.com/mapbox/mapbox-gl-native/pull/9013) * Update attribution with new Mapbox wordmark [#8774](https://github.com/mapbox/mapbox-gl-native/pull/8774) +* LatLngBounds bearing default value [#9102](https://github.com/mapbox/mapbox-gl-native/pull/9102) +* Stop location updates when toggling MyLocationView [#9099](https://github.com/mapbox/mapbox-gl-native/pull/9099) +* Horizontally rotated in snapshot [#9083](https://github.com/mapbox/mapbox-gl-native/pull/9083) +* Disable letter spacing for Arabic text [#9071](https://github.com/mapbox/mapbox-gl-native/pull/9071) +* Correct bearing conversion when animating the map [#9050](https://github.com/mapbox/mapbox-gl-native/pull/9050) +* Don't leak selected markers when removing [#9047](https://github.com/mapbox/mapbox-gl-native/pull/9047) +* Bump tools and support lib version [#9046](https://github.com/mapbox/mapbox-gl-native/pull/9046) +* MarkerView deselect events with OnMarkerViewClickListener [#9047](https://github.com/mapbox/mapbox-gl-native/pull/9047) +* LOST update to v3.0.0 [#9112](https://github.com/mapbox/mapbox-gl-native/pull/9112) +* Convert dp to pixels for meters per pixel at latitude [#9048](https://github.com/mapbox/mapbox-gl-native/pull/9048) + +## 5.1.0-beta.2 - May 12, 2017 + +5.1.0-beta.2 builds further on 5.1.0-beta.1 and adds: + +* When a layer is added, reload its source's tiles [#8963](https://github.com/mapbox/mapbox-gl-native/pull/8963) +* Update release script to support CircleCI builds [#8950](https://github.com/mapbox/mapbox-gl-native/pull/8950) +* URL getter on Sources [#8959](https://github.com/mapbox/mapbox-gl-native/pull/8959) +* Build SNAPSHOT from release branch CI configuration [#8958](https://github.com/mapbox/mapbox-gl-native/pull/8958) +* Fix UI test filter in Makefile [#8960](https://github.com/mapbox/mapbox-gl-native/pull/8960) +* Bump & fixup dependencies [#8921](https://github.com/mapbox/mapbox-gl-native/pull/8921) +* Ignore already deleted region [#8920](https://github.com/mapbox/mapbox-gl-native/pull/8920) +* Keep offline observer when timeout occurs [#8919](https://github.com/mapbox/mapbox-gl-native/pull/8919) +* Show error message when no browser installed [#8920](https://github.com/mapbox/mapbox-gl-native/pull/8920) +* Reset observers of removed Sources and Layers [#8900](https://github.com/mapbox/mapbox-gl-native/pull/8900) +* Only build custom layer .so for debug builds [#8885](https://github.com/mapbox/mapbox-gl-native/pull/8885) +* Update localizations [#8883](https://github.com/mapbox/mapbox-gl-native/pull/8883) +* Reset observers of removed Sources and Layers [#8862](https://github.com/mapbox/mapbox-gl-native/pull/8862) +* Remove force style cascade [#8866](https://github.com/mapbox/mapbox-gl-native/pull/8866) +* Update proguard config [#8944](https://github.com/mapbox/mapbox-gl-native/pull/8944) +* Update LOST to 2.3.0-SNAPSHOT [#8872](https://github.com/mapbox/mapbox-gl-native/pull/8872) +* Update logo [#8774](https://github.com/mapbox/mapbox-gl-native/pull/8774) +* Camera change listener v2.0 [#8644](https://github.com/mapbox/mapbox-gl-native/pull/8644) +* Allow filesource url transform reset [#8957](https://github.com/mapbox/mapbox-gl-native/pull/8957) ## 5.1.0-beta.1 - May 2, 2017 -5.1.0 builds further on 5.0.2 and adds: +5.1.0-beta.1 builds further on 5.0.2 and adds: * Support for FillExtrusionLayer [#8431](https://github.com/mapbox/mapbox-gl-native/pull/8431) * Limit Viewport [#8622](https://github.com/mapbox/mapbox-gl-native/pull/8622) diff --git a/platform/android/MapboxGLAndroidSDK/build.gradle b/platform/android/MapboxGLAndroidSDK/build.gradle index 40712da065..adbc7cad7f 100644 --- a/platform/android/MapboxGLAndroidSDK/build.gradle +++ b/platform/android/MapboxGLAndroidSDK/build.gradle @@ -95,6 +95,9 @@ android { } } } + + // proguard config for .aar + consumerProguardFiles 'proguard-rules.pro' } // avoid naming conflicts, force usage of prefix @@ -126,7 +129,6 @@ android { release { // aar proguard configuration - consumerProguardFiles 'proguard-rules.pro' jniDebuggable false } } diff --git a/platform/android/MapboxGLAndroidSDK/gradle-checkstyle.gradle b/platform/android/MapboxGLAndroidSDK/gradle-checkstyle.gradle index 05037c6be7..e0bc076d3d 100644 --- a/platform/android/MapboxGLAndroidSDK/gradle-checkstyle.gradle +++ b/platform/android/MapboxGLAndroidSDK/gradle-checkstyle.gradle @@ -15,6 +15,7 @@ task checkstyle(type: Checkstyle) { exclude '**/style/layers/Property.java' exclude '**/style/layers/PropertyFactory.java' exclude '**/style/layers/*Layer.java' + exclude '**/style/light/Light.java' classpath = files() ignoreFailures = false } diff --git a/platform/android/MapboxGLAndroidSDK/proguard-rules.pro b/platform/android/MapboxGLAndroidSDK/proguard-rules.pro index baecd80e50..8e47815451 100644 --- a/platform/android/MapboxGLAndroidSDK/proguard-rules.pro +++ b/platform/android/MapboxGLAndroidSDK/proguard-rules.pro @@ -2,89 +2,8 @@ # in ../sdk/tools/proguard/proguard-android.txt, # contents of this file will be appended into proguard-android.txt -keepattributes Signature, *Annotation*, EnclosingMethod - -# Square okio, ignoring warnings, -# see https://github.com/square/okio/issues/60 --dontwarn okhttp3.** --dontwarn okio.** - -# Gesture package -keep class almeros.android.multitouch.gesturedetectors.** { *; } - -# Package annotations --keep class com.mapbox.mapboxsdk.annotations.** { *; } - -# Package camera --keep class com.mapbox.mapboxsdk.camera.** { *; } - -# Package geometry --keep class com.mapbox.mapboxsdk.geometry.** { *; } - -# Package http --keep class com.mapbox.mapboxsdk.http.** { *; } - -# Package maps --keep class com.mapbox.mapboxsdk.maps.** { *; } - -# Package net --keep class com.mapbox.mapboxsdk.net.** { *; } - -# Package offline --keep class com.mapbox.mapboxsdk.offline.** { *; } - -# Package storage --keep class com.mapbox.mapboxsdk.storage.** { *; } - -# Package style --keep class com.mapbox.mapboxsdk.style.layers.** { *; } --keep class com.mapbox.mapboxsdk.style.sources.** { *; } --keep class com.mapbox.mapboxsdk.style.functions.** { *; } - -# Package telemetry --keep class com.mapbox.mapboxsdk.telemetry.** { *; } - -# -# Mapbox-java Proguard rules -# We include these rules since libjava is a Jar file not AAR -# - -# Retrofit 2 -# Platform calls Class.forName on types which do not exist on Android to determine platform. --dontnote retrofit2.Platform -# Platform used when running on RoboVM on iOS. Will not be used at runtime. --dontnote retrofit2.Platform$IOS$MainThreadExecutor -# Platform used when running on Java 8 VMs. Will not be used at runtime. --dontwarn retrofit2.Platform$Java8 -# Retain generic type information for use by reflection by converters and adapters. --keepattributes Signature -# Retain declared checked exceptions for use by a Proxy instance. --keepattributes Exceptions - -# For using GSON @Expose annotation --keepattributes *Annotation* -# Gson specific classes --dontwarn sun.misc.** - -# Prevent proguard from stripping interface information from TypeAdapterFactory, -# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter) --keep class * implements com.google.gson.TypeAdapterFactory --keep class * implements com.google.gson.JsonSerializer --keep class * implements com.google.gson.JsonDeserializer - -# MAS Data Models --keep class com.mapbox.services.commons.geojson.** { *; } --keep class com.mapbox.services.mapmatching.v4.models.** { *; } --keep class com.mapbox.services.distance.v1.models.** { *; } --keep class com.mapbox.services.directions.v4.models.** { *; } --keep class com.mapbox.services.directions.v5.models.** { *; } --keep class com.mapbox.services.geocoding.v5.models.** { *; } - --dontwarn javax.annotation.** - --keepclassmembers class rx.internal.util.unsafe.** { - long producerIndex; - long consumerIndex; -} - --keep class com.google.** { *; } --dontwarn com.google.**
\ No newline at end of file +-keep class com.mapbox.mapboxsdk.** { *; } +-keep interface com.mapbox.mapboxsdk.** { *; } +-keep class com.mapbox.services.android.telemetry.** { *; } +-keep class com.mapbox.services.commons.** { *;}
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java index 8098ee4d86..81134e9497 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java @@ -1,5 +1,6 @@ package com.mapbox.mapboxsdk; +import android.app.Application; import android.content.Context; import android.net.ConnectivityManager; import android.net.NetworkInfo; @@ -15,6 +16,14 @@ import com.mapbox.services.android.telemetry.MapboxTelemetry; import com.mapbox.services.android.telemetry.location.LocationEngine; import com.mapbox.services.android.telemetry.location.LocationEnginePriority; +/** + * The entry point of the Mapbox Android SDK. + * <p> + * Obtain a reference by calling {@link #getInstance(Context, String)}. Usually this class is configured in + * {@link Application#onCreate()} and is responsible for the active access token, application context, and + * connectivity state. + * </p> + */ public final class Mapbox { private static Mapbox INSTANCE; @@ -25,7 +34,7 @@ public final class Mapbox { /** * Get an instance of Mapbox. * <p> - * This class manages the active access token, application context and connectivity state. + * This class manages the active access token, application context, and connectivity state. * </p> * * @param context Android context which holds or is an application context @@ -52,9 +61,9 @@ public final class Mapbox { } /** - * Access Token for this application. + * Access token for this application. * - * @return Mapbox Access Token + * @return Mapbox access token */ public static String getAccessToken() { validateMapbox(); @@ -72,7 +81,7 @@ public final class Mapbox { } /** - * Runtime validation of Access Token. + * Runtime validation of access token. * * @throws MapboxConfigurationException exception thrown when not using a valid accessToken */ @@ -92,11 +101,11 @@ public final class Mapbox { } /** - * Manually sets the connectivity state of the app. This is useful for apps that control their + * Manually sets the connectivity state of the app. This is useful for apps which control their * own connectivity state and want to bypass any checks to the ConnectivityManager. * * @param connected flag to determine the connectivity state, true for connected, false for - * disconnected, null for ConnectivityManager to determine. + * disconnected, and null for ConnectivityManager to determine. */ public static synchronized void setConnected(Boolean connected) { // Connectivity state overridden by app @@ -104,10 +113,10 @@ public final class Mapbox { } /** - * Determines whether we have an Internet connection available. Please do not rely on this - * method in your apps, this method is used internally by the SDK. + * Determines whether we have an internet connection available. Please do not rely on this + * method in your apps. This method is used internally by the SDK. * - * @return true if there is an Internet connection, false otherwise + * @return true if there is an internet connection, false otherwise */ public static synchronized Boolean isConnected() { if (INSTANCE.connected != null) { @@ -119,4 +128,4 @@ public final class Mapbox { NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); return (activeNetwork != null && activeNetwork.isConnected()); } -}
\ No newline at end of file +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Icon.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Icon.java index b1a05ec436..2ee17c227d 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Icon.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Icon.java @@ -1,15 +1,18 @@ package com.mapbox.mapboxsdk.annotations; import android.graphics.Bitmap; +import android.util.DisplayMetrics; -import com.mapbox.mapboxsdk.maps.MapView; +import java.nio.ByteBuffer; /** - * Icon is the visual representation of a {@link Marker} on a {@link MapView}. + * Icon is the visual representation of a Marker on a MapView. * * @see Marker + * @see IconFactory */ public class Icon { + private Bitmap mBitmap; private String mId; @@ -19,29 +22,67 @@ public class Icon { } /** - * {@link String} identifier for this {@link Icon}. + * String identifier for this icon. * - * @return {@link String} identifier for this {@link Icon}. + * @return String identifier for this icon. */ public String getId() { return mId; } /** - * Get the {@link Bitmap} being used for this {@link Icon}. + * Get the bitmap being used for this icon. * - * @return The {@link Bitmap} being used for the {@link Icon}. + * @return The bitmap being used for the icon. */ public Bitmap getBitmap() { + if (mBitmap.getConfig() != Bitmap.Config.ARGB_8888) { + mBitmap = mBitmap.copy(Bitmap.Config.ARGB_8888, false); + } return mBitmap; } /** - * Compares this {@link Icon} object with another {@link Icon} and determines if they match. + * Get the icon bitmap scale. + * <p> + * Requires the bitmap to be set before calling this method. + * </p> + * + * @return the scale of the bitmap + */ + public float getScale() { + if (mBitmap == null) { + throw new IllegalStateException("Required to set a Icon before calling getScale"); + } + float density = mBitmap.getDensity(); + if (density == Bitmap.DENSITY_NONE) { + density = DisplayMetrics.DENSITY_DEFAULT; + } + return density / DisplayMetrics.DENSITY_DEFAULT; + } + + /** + * Get the icon bitmap bytes. + * <p> + * Requires the bitmap to be set before calling this method. + * </p> * - * @param object Another {@link Icon} to compare with this object. - * @return True if the {@link Icon} being passed in matches this {@link Icon} object. Else, - * false. + * @return the bytes of the bitmap + */ + public byte[] toBytes() { + if (mBitmap == null) { + throw new IllegalStateException("Required to set a Icon before calling toBytes"); + } + ByteBuffer buffer = ByteBuffer.allocate(mBitmap.getRowBytes() * mBitmap.getHeight()); + mBitmap.copyPixelsToBuffer(buffer); + return buffer.array(); + } + + /** + * Compares this icon object with another icon and determines if they match. + * + * @param object Another iconi to compare with this object. + * @return True if the icon being passed in matches this icon object. Else, false. */ @Override public boolean equals(Object object) { @@ -53,11 +94,7 @@ public class Icon { } Icon icon = (Icon) object; - - if (!mBitmap.equals(icon.mBitmap)) { - return false; - } - return mId.equals(icon.mId); + return mBitmap.equals(icon.mBitmap) && mId.equals(icon.mId); } /** diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerView.java index 30336d4ebd..56e8cc4ce2 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerView.java @@ -3,6 +3,7 @@ package com.mapbox.mapboxsdk.annotations; import android.support.annotation.FloatRange; import android.support.annotation.Nullable; +import com.mapbox.mapboxsdk.Mapbox; import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.maps.MapboxMap; @@ -361,6 +362,9 @@ public class MarkerView extends Marker { */ @Override public Icon getIcon() { + if (markerViewIcon == null) { + setIcon(IconFactory.getInstance(Mapbox.getApplicationContext()).defaultMarkerView()); + } return markerViewIcon; } 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 5e958ff565..66c261f1d0 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 @@ -156,7 +156,7 @@ public final class CameraPosition implements Parcelable { } /** - * Builder for composing {@link CameraPosition} objects. + * Builder for composing CameraPosition objects. */ public static final class Builder { @@ -190,7 +190,7 @@ public final class CameraPosition implements Parcelable { /** * Create Builder with an existing CameraPosition data. * - * @param typedArray TypedArray containgin attribute values + * @param typedArray TypedArray containing attribute values */ public Builder(TypedArray typedArray) { super(); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactory.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactory.java index 64b86054a0..8e1411e273 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactory.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactory.java @@ -15,7 +15,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** - * Factory for creating {@link CameraUpdate} objects. + * Factory for creating CameraUpdate objects. */ public final class CameraUpdateFactory { diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java index f53c65d055..88c3bef673 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java @@ -16,7 +16,7 @@ import java.lang.annotation.RetentionPolicy; public class MyBearingTracking { /** - * Indicates the parameter accepts one of the values from {@link MyBearingTracking}. + * Indicates that the parameter accepts one of the values from MyBearingTracking. */ @IntDef( {NONE, COMPASS, GPS, /**COMBINED**/}) @Retention(RetentionPolicy.SOURCE) @@ -38,6 +38,4 @@ public class MyBearingTracking { */ public static final int GPS = 0x00000008; - // public static final int COMBINED = 0x00000012; - } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyLocationTracking.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyLocationTracking.java index 39f653596f..a1744d701f 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyLocationTracking.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyLocationTracking.java @@ -17,7 +17,7 @@ import java.lang.annotation.RetentionPolicy; public class MyLocationTracking { /** - * Indicates the parameter accepts one of the values from {@link MyLocationTracking}. + * Indicates the parameter accepts one of the values from MyLocationTracking. */ @IntDef( {TRACKING_NONE, TRACKING_FOLLOW}) @Retention(RetentionPolicy.SOURCE) @@ -30,7 +30,7 @@ public class MyLocationTracking { public static final int TRACKING_NONE = 0x00000000; /** - * Tracking the location of the user, {@link MapView} will reposition to center of {@link MyLocationView} + * Tracking the location of the user. {@link MapView} will reposition to center of {@link MyLocationView} */ public static final int TRACKING_FOLLOW = 0x00000004; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/Style.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/Style.java index 77d0929df3..31e6313509 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/Style.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/Style.java @@ -19,7 +19,7 @@ public class Style { /** - * Indicates the parameter accepts one of the values from {@link Style}. Using one of these + * Indicates the parameter accepts one of the values from Style. Using one of these * constants means your map style will always use the latest version and may change as we * improve the style */ 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 b795cf1d5b..4e934fa3cc 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 @@ -2,7 +2,7 @@ package com.mapbox.mapboxsdk.location; import android.content.Context; import android.location.Location; -import android.util.Log; +import android.support.annotation.NonNull; import com.mapbox.services.android.telemetry.location.LocationEngine; import com.mapbox.services.android.telemetry.location.LocationEngineListener; @@ -15,6 +15,8 @@ import com.mapzen.android.lost.api.LostApiClient; import java.lang.ref.WeakReference; +import timber.log.Timber; + /** * Manages locational updates. Contains methods to register and unregister location listeners. * <ul> @@ -33,8 +35,6 @@ import java.lang.ref.WeakReference; public class LocationSource extends LocationEngine implements LostApiClient.ConnectionCallbacks, LocationListener { - private static final String LOG_TAG = LocationSource.class.getSimpleName(); - private static LocationEngine instance; private WeakReference<Context> context; @@ -48,7 +48,13 @@ public class LocationSource extends LocationEngine implements .build(); } - public static synchronized LocationEngine getLocationEngine(Context context) { + /** + * Get the LocationEngine instance. + * + * @param context a Context from which the application context is derived + * @return the LocationEngine instance + */ + public static synchronized LocationEngine getLocationEngine(@NonNull Context context) { if (instance == null) { instance = new LocationSource(context.getApplicationContext()); } @@ -56,6 +62,10 @@ public class LocationSource extends LocationEngine implements return instance; } + /** + * Activate the location engine which will connect whichever location provider you are using. You'll need to call + * this before requesting user location updates using {@link LocationEngine#requestLocationUpdates()}. + */ @Override public void activate() { if (lostApiClient != null && !lostApiClient.isConnected()) { @@ -63,6 +73,11 @@ public class LocationSource extends LocationEngine implements } } + /** + * Disconnect the location engine which is useful when you no longer need location updates or requesting the users + * {@link LocationEngine#getLastLocation()}. Before deactivating, you'll need to stop request user location updates + * using {@link LocationEngine#removeLocationUpdates()}. + */ @Override public void deactivate() { if (lostApiClient != null && lostApiClient.isConnected()) { @@ -70,11 +85,20 @@ public class LocationSource extends LocationEngine implements } } + /** + * Check if your location provider has been activated/connected. This is mainly used internally but is also useful in + * the rare case when you'd like to know if your location engine is connected or not. + * + * @return boolean true if the location engine has been activated/connected, else false. + */ @Override public boolean isConnected() { return lostApiClient.isConnected(); } + /** + * Invoked when the location provider has connected. + */ @Override public void onConnected() { for (LocationEngineListener listener : locationListeners) { @@ -82,11 +106,19 @@ public class LocationSource extends LocationEngine implements } } + /** + * Invoked when the location provider connection has been suspended. + */ @Override public void onConnectionSuspended() { - Log.d(LOG_TAG, "Connection suspended."); + Timber.d("Connection suspended."); } + /** + * Returns the Last known location is the location provider is connected and location permissions are granted. + * + * @return the last known location + */ @Override public Location getLastLocation() { if (lostApiClient.isConnected() && PermissionsManager.areLocationPermissionsGranted(context.get())) { @@ -97,6 +129,9 @@ public class LocationSource extends LocationEngine implements return null; } + /** + * Request location updates to the location provider. + */ @Override public void requestLocationUpdates() { // Common params @@ -122,6 +157,9 @@ public class LocationSource extends LocationEngine implements } } + /** + * Dismiss ongoing location update to the location provider. + */ @Override public void removeLocationUpdates() { if (lostApiClient.isConnected()) { @@ -129,20 +167,15 @@ public class LocationSource extends LocationEngine implements } } + /** + * Invoked when the Location has changed. + * + * @param location the new location + */ @Override public void onLocationChanged(Location location) { for (LocationEngineListener listener : locationListeners) { listener.onLocationChanged(location); } } - - @Override - public void onProviderDisabled(String provider) { - Log.d(LOG_TAG, "Provider disabled: " + provider); - } - - @Override - public void onProviderEnabled(String provider) { - Log.d(LOG_TAG, "Provider enabled: " + provider); - } } 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 9b6706b90c..7e7947047e 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 @@ -100,6 +100,10 @@ class AnnotationManager { if (annotation instanceof Marker) { Marker marker = (Marker) annotation; marker.hideInfoWindow(); + if (selectedMarkers.contains(marker)) { + selectedMarkers.remove(marker); + } + if (marker instanceof MarkerView) { markerViewManager.removeMarkerView((MarkerView) marker); } @@ -112,6 +116,10 @@ class AnnotationManager { if (annotation instanceof Marker) { Marker marker = (Marker) annotation; marker.hideInfoWindow(); + if (selectedMarkers.contains(marker)) { + selectedMarkers.remove(marker); + } + if (marker instanceof MarkerView) { markerViewManager.removeMarkerView((MarkerView) marker); } @@ -124,6 +132,7 @@ class AnnotationManager { Annotation annotation; int count = annotationsArray.size(); long[] ids = new long[count]; + selectedMarkers.clear(); for (int i = 0; i < count; i++) { ids[i] = annotationsArray.keyAt(i); annotation = annotationsArray.get(ids[i]); @@ -383,12 +392,15 @@ class AnnotationManager { for (Marker nearbyMarker : nearbyMarkers) { for (Marker selectedMarker : selectedMarkers) { if (nearbyMarker.equals(selectedMarker)) { - if (onMarkerClickListener != null) { - // end developer has provided a custom click listener + if (nearbyMarker instanceof MarkerView) { + handledDefaultClick = markerViewManager.onClickMarkerView((MarkerView) nearbyMarker); + } else if (onMarkerClickListener != null) { handledDefaultClick = onMarkerClickListener.onMarkerClick(nearbyMarker); - if (!handledDefaultClick) { - deselectMarker(nearbyMarker); - } + } + + if (!handledDefaultClick) { + // only deselect marker if user didn't handle the click event themselves + deselectMarker(nearbyMarker); } return true; } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CameraChangeDispatcher.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CameraChangeDispatcher.java new file mode 100644 index 0000000000..bd028aecb6 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CameraChangeDispatcher.java @@ -0,0 +1,67 @@ +package com.mapbox.mapboxsdk.maps; + +import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraIdleListener; +import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveCanceledListener; +import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveStartedListener; +import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveListener; + +class CameraChangeDispatcher implements MapboxMap.OnCameraMoveStartedListener, MapboxMap.OnCameraMoveListener, + MapboxMap.OnCameraMoveCanceledListener, OnCameraIdleListener { + + private boolean idle = true; + + private OnCameraMoveStartedListener onCameraMoveStartedListener; + private OnCameraMoveCanceledListener onCameraMoveCanceledListener; + private OnCameraMoveListener onCameraMoveListener; + private OnCameraIdleListener onCameraIdleListener; + + void setOnCameraMoveStartedListener(OnCameraMoveStartedListener onCameraMoveStartedListener) { + this.onCameraMoveStartedListener = onCameraMoveStartedListener; + } + + void setOnCameraMoveCanceledListener(OnCameraMoveCanceledListener onCameraMoveCanceledListener) { + this.onCameraMoveCanceledListener = onCameraMoveCanceledListener; + } + + void setOnCameraMoveListener(OnCameraMoveListener onCameraMoveListener) { + this.onCameraMoveListener = onCameraMoveListener; + } + + void setOnCameraIdleListener(OnCameraIdleListener onCameraIdleListener) { + this.onCameraIdleListener = onCameraIdleListener; + } + + @Override + public void onCameraMoveStarted(int reason) { + if (!idle) { + return; + } + + idle = false; + if (onCameraMoveStartedListener != null) { + onCameraMoveStartedListener.onCameraMoveStarted(reason); + } + } + + @Override + public void onCameraMove() { + if (onCameraMoveListener != null && !idle) { + onCameraMoveListener.onCameraMove(); + } + } + + @Override + public void onCameraMoveCanceled() { + if (onCameraMoveCanceledListener != null && !idle) { + onCameraMoveCanceledListener.onCameraMoveCanceled(); + } + } + + @Override + public void onCameraIdle() { + if (onCameraIdleListener != null && !idle) { + idle = true; + onCameraIdleListener.onCameraIdle(); + } + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/FocalPointChangeListener.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/FocalPointChangeListener.java index 006122a4e2..aec9a848b7 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/FocalPointChangeListener.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/FocalPointChangeListener.java @@ -2,6 +2,9 @@ package com.mapbox.mapboxsdk.maps; import android.graphics.PointF; +/** + * Interface definition of a callback that is invoked when the focal point will change. + */ public interface FocalPointChangeListener { void onFocalPointChanged(PointF pointF); 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 c9d81a88bc..9f4171aee8 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 @@ -1,15 +1,14 @@ package com.mapbox.mapboxsdk.maps; import android.graphics.Bitmap; -import android.util.DisplayMetrics; +import com.mapbox.mapboxsdk.Mapbox; 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.exceptions.IconBitmapChangedException; -import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; @@ -41,118 +40,109 @@ class IconManager { Icon loadIconForMarker(Marker marker) { Icon icon = marker.getIcon(); - - // calculating average before adding - int iconSize = icons.size() + 1; - - // TODO replace former if case with anchor implementation, - // current workaround for having extra pixels is diving height by 2 if (icon == null) { - icon = IconFactory.getInstance(nativeMapView.getContext()).defaultMarker(); - Bitmap bitmap = icon.getBitmap(); - averageIconHeight = averageIconHeight + (bitmap.getHeight() / 2 - averageIconHeight) / iconSize; - averageIconWidth = averageIconWidth + (bitmap.getWidth() - averageIconWidth) / iconSize; - marker.setIcon(icon); - } else { - Bitmap bitmap = icon.getBitmap(); - averageIconHeight = averageIconHeight + (bitmap.getHeight() - averageIconHeight) / iconSize; - averageIconWidth = averageIconWidth + (bitmap.getWidth() - averageIconWidth) / iconSize; - } - - if (!icons.contains(icon)) { - icons.add(icon); - loadIcon(icon); + // TODO replace with anchor implementation, we are faking an anchor by adding extra pixels and diving height by 2 + // TODO we can move this code afterwards to getIcon as with MarkerView.getIcon + icon = loadDefaultIconForMarker(marker); } else { - Icon oldIcon = icons.get(icons.indexOf(icon)); - if (!oldIcon.getBitmap().sameAs(icon.getBitmap())) { - throw new IconBitmapChangedException(); - } + updateAverageIconSize(icon); } + addIcon(icon); return icon; } - Icon loadIconForMarkerView(MarkerView marker) { + void 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; + updateAverageIconSize(bitmap); + addIcon(icon, false); } int getTopOffsetPixelsForIcon(Icon icon) { return (int) (nativeMapView.getTopOffsetPixelsForAnnotationSymbol(icon.getId()) * nativeMapView.getPixelRatio()); } - void loadIcon(Icon icon) { + int getAverageIconHeight() { + return averageIconHeight; + } + + int getAverageIconWidth() { + return averageIconWidth; + } + + private Icon loadDefaultIconForMarker(Marker marker) { + Icon icon = IconFactory.getInstance(Mapbox.getApplicationContext()).defaultMarker(); Bitmap bitmap = icon.getBitmap(); - String id = icon.getId(); - if (bitmap.getConfig() != Bitmap.Config.ARGB_8888) { - bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, false); - } - ByteBuffer buffer = ByteBuffer.allocate(bitmap.getRowBytes() * bitmap.getHeight()); - bitmap.copyPixelsToBuffer(buffer); + updateAverageIconSize(bitmap.getWidth(), bitmap.getHeight() / 2); + marker.setIcon(icon); + return icon; + } + + private void addIcon(Icon icon) { + addIcon(icon, true); + } - float density = bitmap.getDensity(); - if (density == Bitmap.DENSITY_NONE) { - density = DisplayMetrics.DENSITY_DEFAULT; + private void addIcon(Icon icon, boolean addIconToMap) { + if (!icons.contains(icon)) { + icons.add(icon); + if (addIconToMap) { + loadIcon(icon); + } + } else { + validateIconChanged(icon); } - float scale = density / DisplayMetrics.DENSITY_DEFAULT; - nativeMapView.addAnnotationIcon( - id, + } + + private void updateAverageIconSize(Icon icon) { + updateAverageIconSize(icon.getBitmap()); + } + + private void updateAverageIconSize(Bitmap bitmap) { + updateAverageIconSize(bitmap.getWidth(), bitmap.getHeight()); + } + + private void updateAverageIconSize(int width, int height) { + int iconSize = icons.size() + 1; + averageIconHeight = averageIconHeight + (height - averageIconHeight) / iconSize; + averageIconWidth = averageIconWidth + (width - averageIconWidth) / iconSize; + } + + private void loadIcon(Icon icon) { + Bitmap bitmap = icon.getBitmap(); + nativeMapView.addAnnotationIcon(icon.getId(), bitmap.getWidth(), bitmap.getHeight(), - scale, buffer.array()); + icon.getScale(), + icon.toBytes()); } void reloadIcons() { - int count = icons.size(); - for (int i = 0; i < count; i++) { - Icon icon = icons.get(i); + for (Icon icon : icons) { loadIcon(icon); } } + private void validateIconChanged(Icon icon) { + Icon oldIcon = icons.get(icons.indexOf(icon)); + if (!oldIcon.getBitmap().sameAs(icon.getBitmap())) { + throw new IconBitmapChangedException(); + } + } + void ensureIconLoaded(Marker marker, MapboxMap mapboxMap) { Icon icon = marker.getIcon(); if (icon == null) { - icon = IconFactory.getInstance(nativeMapView.getContext()).defaultMarker(); - marker.setIcon(icon); - } - if (!icons.contains(icon)) { - icons.add(icon); - loadIcon(icon); - } else { - Icon oldIcon = icons.get(icons.indexOf(icon)); - if (!oldIcon.getBitmap().sameAs(icon.getBitmap())) { - throw new IconBitmapChangedException(); - } + icon = loadDefaultIconForMarker(marker); } + addIcon(icon); + setTopOffsetPixels(marker, mapboxMap, icon); + } + private void setTopOffsetPixels(Marker marker, MapboxMap mapboxMap, Icon icon) { // this seems to be a costly operation according to the profiler so I'm trying to save some calls Marker previousMarker = marker.getId() != -1 ? (Marker) mapboxMap.getAnnotation(marker.getId()) : null; if (previousMarker == null || previousMarker.getIcon() == null || previousMarker.getIcon() != marker.getIcon()) { marker.setTopOffsetPixels(getTopOffsetPixelsForIcon(icon)); } } - - int getAverageIconHeight() { - return averageIconHeight; - } - - int getAverageIconWidth() { - return averageIconWidth; - } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java index 80fd6248bc..33e13c5ecc 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java @@ -22,6 +22,8 @@ import com.mapbox.services.android.telemetry.MapboxTelemetry; import com.mapbox.services.android.telemetry.utils.MathUtils; import com.mapbox.services.android.telemetry.utils.TelemetryUtils; +import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE; + /** * Manages gestures events on a MapView. * <p> @@ -35,6 +37,7 @@ final class MapGestureDetector { private final UiSettings uiSettings; private final TrackingSettings trackingSettings; private final AnnotationManager annotationManager; + private final CameraChangeDispatcher cameraChangeDispatcher; private final GestureDetectorCompat gestureDetector; private final ScaleGestureDetector scaleGestureDetector; @@ -56,12 +59,14 @@ final class MapGestureDetector { private boolean scaleGestureOccurred = false; MapGestureDetector(Context context, Transform transform, Projection projection, UiSettings uiSettings, - TrackingSettings trackingSettings, AnnotationManager annotationManager) { + TrackingSettings trackingSettings, AnnotationManager annotationManager, + CameraChangeDispatcher cameraChangeDispatcher) { this.annotationManager = annotationManager; this.transform = transform; this.projection = projection; this.uiSettings = uiSettings; this.trackingSettings = trackingSettings; + this.cameraChangeDispatcher = cameraChangeDispatcher; // Touch gesture detectors gestureDetector = new GestureDetectorCompat(context, new GestureListener()); @@ -187,6 +192,7 @@ final class MapGestureDetector { MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapDragEndEvent( getLocationFromGesture(event.getX(), event.getY()), transform)); scrollInProgress = false; + cameraChangeDispatcher.onCameraIdle(); } twoTap = false; @@ -273,6 +279,9 @@ final class MapGestureDetector { break; } + // notify camera change listener + cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); + // Single finger double tap if (focalPoint != null) { // User provided focal point @@ -337,6 +346,7 @@ final class MapGestureDetector { // and ignore when a scale gesture has occurred return false; } + cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); float screenDensity = uiSettings.getPixelRatio(); @@ -362,9 +372,7 @@ final class MapGestureDetector { long animationTime = (long) (velocityXY / 7 / tiltFactor + MapboxConstants.ANIMATION_DURATION_FLING_BASE); // update transformation - transform.setGestureInProgress(true); transform.moveBy(offsetX, offsetY, animationTime); - transform.setGestureInProgress(false); if (onFlingListener != null) { onFlingListener.onFling(); @@ -375,12 +383,6 @@ final class MapGestureDetector { // Called for drags @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { - if (!scrollInProgress) { - scrollInProgress = true; - MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent( - getLocationFromGesture(e1.getX(), e1.getY()), - MapboxEvent.GESTURE_PAN_START, transform)); - } if (!trackingSettings.isScrollGestureCurrentlyEnabled()) { return false; } @@ -389,10 +391,19 @@ final class MapGestureDetector { return false; } + if (!scrollInProgress) { + scrollInProgress = true; + + // Cancel any animation + transform.cancelTransitions(); + cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); + MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent( + getLocationFromGesture(e1.getX(), e1.getY()), + MapboxEvent.GESTURE_PAN_START, transform)); + } + // reset tracking if needed trackingSettings.resetTrackingModesIfRequired(true, false, false); - // Cancel any animation - transform.cancelTransitions(); // Scroll the map transform.moveBy(-distanceX, -distanceY, 0 /*no duration*/); @@ -446,6 +457,8 @@ final class MapGestureDetector { // If scale is large enough ignore a tap scaleFactor *= detector.getScaleFactor(); if ((scaleFactor > 1.05f) || (scaleFactor < 0.95f)) { + // notify camera change listener + cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); zoomStarted = true; } @@ -465,9 +478,6 @@ final class MapGestureDetector { return false; } - // Cancel any animation - transform.cancelTransitions(); - // Gesture is a quickzoom if there aren't two fingers quickZoom = !twoTap; @@ -512,6 +522,9 @@ final class MapGestureDetector { return false; } + // notify camera change listener + cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); + beginTime = detector.getEventTime(); MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent( getLocationFromGesture(detector.getFocusX(), detector.getFocusY()), @@ -522,6 +535,7 @@ final class MapGestureDetector { // Called when the fingers leave the screen @Override public void onRotateEnd(RotateGestureDetector detector) { + // notify camera change listener beginTime = 0; totalAngle = 0.0f; started = false; @@ -553,13 +567,8 @@ final class MapGestureDetector { if (!started) { return false; } - - // Cancel any animation - transform.cancelTransitions(); - // rotation constitutes translation of anything except the center of // rotation, so cancel both location and bearing tracking if required - trackingSettings.resetTrackingModesIfRequired(true, true, false); // Get rotate value @@ -593,6 +602,8 @@ final class MapGestureDetector { return false; } + // notify camera change listener + cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); beginTime = detector.getEventTime(); MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent( getLocationFromGesture(detector.getFocusX(), detector.getFocusY()), @@ -633,9 +644,6 @@ final class MapGestureDetector { return false; } - // Cancel any animation - transform.cancelTransitions(); - // Get tilt value (scale and clamp) double pitch = transform.getTilt(); pitch -= 0.1 * detector.getShovePixelsDelta(); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java index 4f90b0a8fe..cf1841e180 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java @@ -131,6 +131,9 @@ public class MapView extends FrameLayout { // callback for zooming in the camera CameraZoomInvalidator zoomInvalidator = new CameraZoomInvalidator(); + // callback for camera change events + CameraChangeDispatcher cameraChangeDispatcher = new CameraChangeDispatcher(); + // setup components for MapboxMap creation Projection proj = new Projection(nativeMapView); UiSettings uiSettings = new UiSettings(proj, focalPoint, compassView, attrView, view.findViewById(R.id.logoView)); @@ -145,13 +148,14 @@ public class MapView extends FrameLayout { Polylines polylines = new PolylineContainer(nativeMapView, annotationsArray); AnnotationManager annotationManager = new AnnotationManager(nativeMapView, this, annotationsArray, markerViewManager, iconManager, annotations, markers, polygons, polylines); - Transform transform = new Transform(nativeMapView, annotationManager.getMarkerViewManager(), trackingSettings); + Transform transform = new Transform(nativeMapView, annotationManager.getMarkerViewManager(), trackingSettings, + cameraChangeDispatcher); mapboxMap = new MapboxMap(nativeMapView, transform, uiSettings, trackingSettings, myLocationViewSettings, proj, - registerTouchListener, annotationManager); + registerTouchListener, annotationManager, cameraChangeDispatcher); // user input mapGestureDetector = new MapGestureDetector(context, transform, proj, uiSettings, trackingSettings, - annotationManager); + annotationManager, cameraChangeDispatcher); mapKeyListener = new MapKeyListener(transform, trackingSettings, uiSettings); MapZoomControllerListener zoomListener = new MapZoomControllerListener(mapGestureDetector, uiSettings, transform); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java index 5f1ed0755c..f60ddf616a 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java @@ -41,6 +41,7 @@ import com.mapbox.mapboxsdk.location.LocationSource; import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings; import com.mapbox.mapboxsdk.style.layers.Filter; import com.mapbox.mapboxsdk.style.layers.Layer; +import com.mapbox.mapboxsdk.style.light.Light; import com.mapbox.mapboxsdk.style.sources.Source; import com.mapbox.services.android.telemetry.location.LocationEngine; import com.mapbox.services.commons.geojson.Feature; @@ -69,6 +70,7 @@ public final class MapboxMap { private final Transform transform; private final AnnotationManager annotationManager; private final MyLocationViewSettings myLocationViewSettings; + private final CameraChangeDispatcher cameraChangeDispatcher; private final OnRegisterTouchListener onRegisterTouchListener; @@ -76,7 +78,7 @@ public final class MapboxMap { MapboxMap(NativeMapView map, Transform transform, UiSettings ui, TrackingSettings tracking, MyLocationViewSettings myLocationView, Projection projection, OnRegisterTouchListener listener, - AnnotationManager annotations) { + AnnotationManager annotations, CameraChangeDispatcher cameraChangeDispatcher) { this.nativeMapView = map; this.uiSettings = ui; this.trackingSettings = tracking; @@ -85,6 +87,7 @@ public final class MapboxMap { this.annotationManager = annotations.bind(this); this.transform = transform; this.onRegisterTouchListener = listener; + this.cameraChangeDispatcher = cameraChangeDispatcher; } void initialise(@NonNull Context context, @NonNull MapboxMapOptions options) { @@ -566,6 +569,20 @@ public final class MapboxMap { } // + // + // + + /** + * Get the global light source used to change lighting conditions on extruded fill layers. + * + * @return the global light source + */ + @Nullable + public Light getLight() { + return nativeMapView.getLight(); + } + + // // Camera API // @@ -1623,11 +1640,52 @@ public final class MapboxMap { * To unset the callback, use null. */ @UiThread + @Deprecated public void setOnCameraChangeListener(@Nullable OnCameraChangeListener listener) { transform.setOnCameraChangeListener(listener); } /** + * Sets a callback that is invoked when camera movement has ended. + * + * @param listener the listener to notify + */ + @UiThread + public void setOnCameraIdleListener(@Nullable OnCameraIdleListener listener) { + cameraChangeDispatcher.setOnCameraIdleListener(listener); + } + + /** + * Sets a callback that is invoked when camera movement was cancelled. + * + * @param listener the listener to notify + */ + @UiThread + public void setOnCameraMoveCancelListener(@Nullable OnCameraMoveCanceledListener listener) { + cameraChangeDispatcher.setOnCameraMoveCanceledListener(listener); + } + + /** + * Sets a callback that is invoked when camera movement has started. + * + * @param listener the listener to notify + */ + @UiThread + public void setOnCameraMoveStartedistener(@Nullable OnCameraMoveStartedListener listener) { + cameraChangeDispatcher.setOnCameraMoveStartedListener(listener); + } + + /** + * Sets a callback that is invoked when camera position changes. + * + * @param listener the listener to notify + */ + @UiThread + public void setOnCameraMoveListener(@Nullable OnCameraMoveListener listener) { + cameraChangeDispatcher.setOnCameraMoveListener(listener); + } + + /** * Sets a callback that's invoked on every frame rendered to the map view. * * @param listener The callback that's invoked on every frame rendered to the map view. @@ -1941,7 +1999,12 @@ public final class MapboxMap { /** * Interface definition for a callback to be invoked when the camera changes position. + * + * @deprecated Replaced by {@link MapboxMap.OnCameraMoveStartedListener}, {@link MapboxMap.OnCameraMoveListener} and + * {@link MapboxMap.OnCameraIdleListener}. The order in which the deprecated onCameraChange method will be called in + * relation to the methods in the new camera change listeners is undefined. */ + @Deprecated public interface OnCameraChangeListener { /** * Called after the camera position has changed. During an animation, @@ -1954,6 +2017,56 @@ public final class MapboxMap { } /** + * Interface definition for a callback to be invoked for when the camera motion starts. + */ + public interface OnCameraMoveStartedListener { + int REASON_API_GESTURE = 1; + int REASON_DEVELOPER_ANIMATION = 2; + int REASON_API_ANIMATION = 3; + + /** + * Called when the camera starts moving after it has been idle or when the reason for camera motion has changed. + * + * @param reason the reason for the camera change + */ + void onCameraMoveStarted(int reason); + } + + /** + * Interface definition for a callback to be invoked for when the camera changes position. + */ + public interface OnCameraMoveListener { + /** + * Called repeatedly as the camera continues to move after an onCameraMoveStarted call. + * This may be called as often as once every frame and should not perform expensive operations. + */ + void onCameraMove(); + } + + /** + * Interface definition for a callback to be invoked for when the camera's motion has been stopped or when the camera + * starts moving for a new reason. + */ + public interface OnCameraMoveCanceledListener { + /** + * Called when the developer explicitly calls the cancelTransitions() method or if the reason for camera motion has + * changed before the onCameraIdle had a chance to fire after the previous animation. + * Do not update or animate the camera from within this method. + */ + void onCameraMoveCanceled(); + } + + /** + * Interface definition for a callback to be invoked for when camera movement has ended. + */ + public interface OnCameraIdleListener { + /** + * Called when camera movement has ended. + */ + void onCameraIdle(); + } + + /** * Interface definition for a callback to be invoked when a frame is rendered to the map view. * * @see MapboxMap#setOnFpsChangedListener(OnFpsChangedListener) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java index 68603ab1a3..98f94ddb39 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java @@ -36,7 +36,7 @@ import java.util.Arrays; public class MapboxMapOptions implements Parcelable { private static final float FOUR_DP = 4f; - private static final float EIGHTY_NINE_DP = 92f; + private static final float NINETY_TWO_DP = 92f; private CameraPosition cameraPosition; @@ -241,7 +241,7 @@ public class MapboxMapOptions implements Parcelable { R.styleable.mapbox_MapView_mapbox_uiAttributionGravity, Gravity.BOTTOM)); mapboxMapOptions.attributionMargins(new int[] { (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiAttributionMarginLeft, - EIGHTY_NINE_DP * pxlRatio)), + NINETY_TWO_DP * pxlRatio)), (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiAttributionMarginTop, FOUR_DP * pxlRatio)), (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiAttributionMarginRight, diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java index ae4a8ee8d2..6d9df8aebd 100755 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java @@ -27,6 +27,7 @@ import com.mapbox.mapboxsdk.storage.FileSource; import com.mapbox.mapboxsdk.style.layers.CannotAddLayerException; import com.mapbox.mapboxsdk.style.layers.Filter; import com.mapbox.mapboxsdk.style.layers.Layer; +import com.mapbox.mapboxsdk.style.light.Light; import com.mapbox.mapboxsdk.style.sources.CannotAddSourceException; import com.mapbox.mapboxsdk.style.sources.Source; import com.mapbox.services.commons.geojson.Feature; @@ -617,7 +618,7 @@ final class NativeMapView { if (isDestroyedOn("getMetersPerPixelAtLatitude")) { return 0; } - return nativeGetMetersPerPixelAtLatitude(lat, getZoom()); + return nativeGetMetersPerPixelAtLatitude(lat, getZoom()) / pixelRatio; } public ProjectedMeters projectedMetersForLatLng(LatLng latLng) { @@ -882,12 +883,15 @@ final class NativeMapView { fileSource.setApiBaseUrl(baseUrl); } - public float getPixelRatio() { - return pixelRatio; + public Light getLight() { + if (isDestroyedOn("getLight")) { + return null; + } + return nativeGetLight(); } - public Context getContext() { - return mapView.getContext(); + public float getPixelRatio() { + return pixelRatio; } // @@ -1117,6 +1121,8 @@ final class NativeMapView { String[] layerIds, Object[] filter); + private native Light nativeGetLight(); + int getWidth() { if (isDestroyedOn("")) { return 0; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java index 09213934ae..7dcd84de75 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java @@ -282,8 +282,10 @@ public final class TrackingSettings { */ void resetTrackingModesIfRequired(CameraPosition currentCameraPosition, CameraPosition targetCameraPosition, boolean isFromLocation) { - resetTrackingModesIfRequired(!currentCameraPosition.target.equals(targetCameraPosition.target), false, - isFromLocation); + if (currentCameraPosition.target != null) { + resetTrackingModesIfRequired(!currentCameraPosition.target.equals(targetCameraPosition.target), false, + isFromLocation); + } } Location getMyLocation() { diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java index 507bec270d..7f44e0de07 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java @@ -16,6 +16,7 @@ import com.mapbox.mapboxsdk.maps.widgets.MyLocationView; import timber.log.Timber; import static com.mapbox.mapboxsdk.maps.MapView.REGION_DID_CHANGE_ANIMATED; +import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveStartedListener; /** * Resembles the current Map transformation. @@ -33,13 +34,18 @@ final class Transform implements MapView.OnMapChangedListener { private CameraPosition cameraPosition; private MapboxMap.CancelableCallback cameraCancelableCallback; + private MapboxMap.OnCameraChangeListener onCameraChangeListener; - Transform(NativeMapView mapView, MarkerViewManager markerViewManager, TrackingSettings trackingSettings) { + private CameraChangeDispatcher cameraChangeDispatcher; + + Transform(NativeMapView mapView, MarkerViewManager markerViewManager, TrackingSettings trackingSettings, + CameraChangeDispatcher cameraChangeDispatcher) { this.mapView = mapView; this.markerViewManager = markerViewManager; this.trackingSettings = trackingSettings; this.myLocationView = trackingSettings.getMyLocationView(); + this.cameraChangeDispatcher = cameraChangeDispatcher; } void initialise(@NonNull MapboxMap mapboxMap, @NonNull MapboxMapOptions options) { @@ -79,6 +85,7 @@ final class Transform implements MapView.OnMapChangedListener { cameraCancelableCallback.onFinish(); cameraCancelableCallback = null; } + cameraChangeDispatcher.onCameraIdle(); mapView.removeOnMapChangedListener(this); } } @@ -89,10 +96,12 @@ final class Transform implements MapView.OnMapChangedListener { if (!cameraPosition.equals(this.cameraPosition)) { trackingSettings.resetTrackingModesIfRequired(this.cameraPosition, cameraPosition, false); cancelTransitions(); + cameraChangeDispatcher.onCameraMoveStarted(OnCameraMoveStartedListener.REASON_API_ANIMATION); mapView.jumpTo(cameraPosition.bearing, cameraPosition.target, cameraPosition.tilt, cameraPosition.zoom); if (callback != null) { callback.onFinish(); } + cameraChangeDispatcher.onCameraIdle(); } } @@ -103,6 +112,8 @@ final class Transform implements MapView.OnMapChangedListener { if (!cameraPosition.equals(this.cameraPosition)) { trackingSettings.resetTrackingModesIfRequired(this.cameraPosition, cameraPosition, isDismissable); cancelTransitions(); + cameraChangeDispatcher.onCameraMoveStarted(OnCameraMoveStartedListener.REASON_API_ANIMATION); + if (callback != null) { cameraCancelableCallback = callback; mapView.addOnMapChangedListener(this); @@ -118,8 +129,9 @@ final class Transform implements MapView.OnMapChangedListener { CameraPosition cameraPosition = update.getCameraPosition(mapboxMap); if (!cameraPosition.equals(this.cameraPosition)) { trackingSettings.resetTrackingModesIfRequired(this.cameraPosition, cameraPosition, false); - cancelTransitions(); + cameraChangeDispatcher.onCameraMoveStarted(OnCameraMoveStartedListener.REASON_API_ANIMATION); + if (callback != null) { cameraCancelableCallback = callback; mapView.addOnMapChangedListener(this); @@ -134,7 +146,12 @@ final class Transform implements MapView.OnMapChangedListener { @Nullable CameraPosition invalidateCameraPosition() { if (mapView != null) { - cameraPosition = mapView.getCameraPosition(); + CameraPosition cameraPosition = mapView.getCameraPosition(); + if (this.cameraPosition != null && !this.cameraPosition.equals(cameraPosition)) { + cameraChangeDispatcher.onCameraMove(); + } + + this.cameraPosition = cameraPosition; if (onCameraChangeListener != null) { onCameraChangeListener.onCameraChange(this.cameraPosition); } @@ -143,10 +160,17 @@ final class Transform implements MapView.OnMapChangedListener { } void cancelTransitions() { + // notify user about cancel + cameraChangeDispatcher.onCameraMoveCanceled(); + + // notify animateCamera and easeCamera about cancelling if (cameraCancelableCallback != null) { + cameraChangeDispatcher.onCameraIdle(); cameraCancelableCallback.onCancel(); cameraCancelableCallback = null; } + + // cancel ongoing transitions mapView.cancelTransitions(); } @@ -156,6 +180,10 @@ final class Transform implements MapView.OnMapChangedListener { mapView.resetNorth(); } + // + // Camera change listener API + // + void setOnCameraChangeListener(@Nullable MapboxMap.OnCameraChangeListener listener) { this.onCameraChangeListener = listener; } @@ -171,9 +199,6 @@ final class Transform implements MapView.OnMapChangedListener { } void zoom(boolean zoomIn, @NonNull PointF focalPoint) { - // Cancel any animation - cancelTransitions(); - CameraPosition cameraPosition = invalidateCameraPosition(); if (cameraPosition != null) { int newZoom = (int) Math.round(cameraPosition.zoom + (zoomIn ? 1 : -1)); @@ -186,6 +211,15 @@ final class Transform implements MapView.OnMapChangedListener { } void setZoom(double zoom, @NonNull PointF focalPoint, long duration) { + mapView.addOnMapChangedListener(new MapView.OnMapChangedListener() { + @Override + public void onMapChanged(int change) { + if (change == MapView.REGION_DID_CHANGE_ANIMATED) { + mapView.removeOnMapChangedListener(this); + cameraChangeDispatcher.onCameraIdle(); + } + } + }); mapView.setZoom(zoom, focalPoint, duration); } @@ -277,6 +311,17 @@ final class Transform implements MapView.OnMapChangedListener { } void moveBy(double offsetX, double offsetY, long duration) { + if (duration > 0) { + mapView.addOnMapChangedListener(new MapView.OnMapChangedListener() { + @Override + public void onMapChanged(int change) { + if (change == MapView.DID_FINISH_RENDERING_MAP_FULLY_RENDERED) { + mapView.removeOnMapChangedListener(this); + cameraChangeDispatcher.onCameraIdle(); + } + } + }); + } mapView.moveBy(offsetX, offsetY, duration); } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java index 1bcf8a70b9..5f7b6c571b 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java @@ -193,12 +193,16 @@ public final class UiSettings { private void initialiseLogo(MapboxMapOptions options, Resources resources) { setLogoEnabled(options.getLogoEnabled()); setLogoGravity(options.getLogoGravity()); - int[] logoMargins = options.getLogoMargins(); + setLogoMargins(resources, options.getLogoMargins()); + } + + private void setLogoMargins(Resources resources, int[] logoMargins) { if (logoMargins != null) { setLogoMargins(logoMargins[0], logoMargins[1], logoMargins[2], logoMargins[3]); } else { - int twoDp = (int) resources.getDimension(R.dimen.mapbox_two_dp); - setLogoMargins(twoDp, twoDp, twoDp, twoDp); + // user did not specify margins when programmatically creating a map + int fourDp = (int) resources.getDimension(R.dimen.mapbox_four_dp); + setLogoMargins(fourDp, fourDp, fourDp, fourDp); } } @@ -223,15 +227,23 @@ public final class UiSettings { private void initialiseAttribution(Context context, MapboxMapOptions options) { setAttributionEnabled(options.getAttributionEnabled()); setAttributionGravity(options.getAttributionGravity()); - int[] attributionMargins = options.getAttributionMargins(); + setAttributionMargins(context, options.getAttributionMargins()); + int attributionTintColor = options.getAttributionTintColor(); + setAttributionTintColor(attributionTintColor != -1 + ? attributionTintColor : ColorUtils.getPrimaryColor(context)); + } + + private void setAttributionMargins(Context context, int[] attributionMargins) { if (attributionMargins != null) { setAttributionMargins(attributionMargins[0], attributionMargins[1], attributionMargins[2], attributionMargins[3]); + } else { + // user did not specify margins when programmatically creating a map + Resources resources = context.getResources(); + int margin = (int) resources.getDimension(R.dimen.mapbox_four_dp); + int leftMargin = (int) resources.getDimension(R.dimen.mapbox_ninety_two_dp); + setAttributionMargins(leftMargin, margin, margin, margin); } - - int attributionTintColor = options.getAttributionTintColor(); - setAttributionTintColor(attributionTintColor != -1 - ? attributionTintColor : ColorUtils.getPrimaryColor(context)); } private void saveAttribution(Bundle outState) { diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java index db2a335453..f5ef46a5d3 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java @@ -440,6 +440,7 @@ public class MyLocationView extends View { } else { // Disable location and user dot location = null; + locationSource.removeLocationUpdates(); locationSource.removeLocationEngineListener(userLocationListener); locationSource.deactivate(); } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java index 50ae6716e6..ce498da8f5 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java @@ -47,7 +47,7 @@ public class OfflineManager { /** * This callback receives an asynchronous response containing a list of all - * {@link OfflineRegion} in the database, or an error message otherwise. + * OfflineRegion in the database or an error message otherwise. */ public interface ListOfflineRegionsCallback { /** @@ -67,7 +67,7 @@ public class OfflineManager { /** * This callback receives an asynchronous response containing the newly created - * {@link OfflineRegion} in the database, or an error message otherwise. + * OfflineRegion in the database or an error message otherwise. */ public interface CreateOfflineRegionCallback { /** diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java index 5ed6579e1c..fae53c2086 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java @@ -4,6 +4,7 @@ import android.os.Handler; import android.os.Looper; import android.support.annotation.IntDef; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import com.mapbox.mapboxsdk.LibraryLoader; import com.mapbox.mapboxsdk.storage.FileSource; @@ -37,6 +38,9 @@ public class OfflineRegion { //Region id private long id; + // delete status + private boolean isDeleted; + private OfflineRegionDefinition definition; /** @@ -95,7 +99,7 @@ public class OfflineRegion { } /** - * This callback receives an asynchronous response containing the {@link OfflineRegionStatus} + * This callback receives an asynchronous response containing the OfflineRegionStatus * of the offline region, or a {@link String} error message otherwise. */ public interface OfflineRegionStatusCallback { @@ -248,7 +252,7 @@ public class OfflineRegion { * * @param observer the observer to be notified */ - public void setObserver(@NonNull final OfflineRegionObserver observer) { + public void setObserver(@Nullable final OfflineRegionObserver observer) { setOfflineRegionObserver(new OfflineRegionObserver() { @Override public void onStatusChanged(final OfflineRegionStatus status) { @@ -256,7 +260,9 @@ public class OfflineRegion { getHandler().post(new Runnable() { @Override public void run() { - observer.onStatusChanged(status); + if (observer != null) { + observer.onStatusChanged(status); + } } }); } @@ -268,7 +274,9 @@ public class OfflineRegion { getHandler().post(new Runnable() { @Override public void run() { - observer.onError(error); + if (observer != null) { + observer.onError(error); + } } }); } @@ -280,7 +288,9 @@ public class OfflineRegion { getHandler().post(new Runnable() { @Override public void run() { - observer.mapboxTileCountLimitExceeded(limit); + if (observer != null) { + observer.mapboxTileCountLimitExceeded(limit); + } } }); } @@ -347,28 +357,31 @@ public class OfflineRegion { * @param callback the callback to be invoked */ public void delete(@NonNull final OfflineRegionDeleteCallback callback) { - deleteOfflineRegion(new OfflineRegionDeleteCallback() { - @Override - public void onDelete() { - getHandler().post(new Runnable() { - @Override - public void run() { - callback.onDelete(); - OfflineRegion.this.finalize(); - } - }); - } + if (!isDeleted) { + deleteOfflineRegion(new OfflineRegionDeleteCallback() { + @Override + public void onDelete() { + isDeleted = true; + getHandler().post(new Runnable() { + @Override + public void run() { + callback.onDelete(); + OfflineRegion.this.finalize(); + } + }); + } - @Override - public void onError(final String error) { - getHandler().post(new Runnable() { - @Override - public void run() { - callback.onError(error); - } - }); - } - }); + @Override + public void onError(final String error) { + getHandler().post(new Runnable() { + @Override + public void run() { + callback.onError(error); + } + }); + } + }); + } } /** diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/Resource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/Resource.java index af98a46a9b..eae83e8c1f 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/Resource.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/Resource.java @@ -5,9 +5,15 @@ import android.support.annotation.IntDef; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +/** + * Resource provides access to resource types. + */ public final class Resource { // Note: Keep this in sync with include/mbgl/storage/resource.hpp + /** + * Resource type variants. + */ @IntDef( {UNKNOWN, STYLE, SOURCE, TILE, GLYPHS, SPRITE_IMAGE, SPRITE_JSON}) @Retention(RetentionPolicy.SOURCE) public @interface Kind { diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/CompositeFunction.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/CompositeFunction.java index 8ded7ecd34..15e4474105 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/CompositeFunction.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/CompositeFunction.java @@ -13,7 +13,7 @@ import com.mapbox.mapboxsdk.style.layers.PropertyValue; import java.util.Map; /** - * Composite functions combine {@link android.graphics.Camera} and {@link SourceFunction}s. + * Composite functions combine Camera and SourceFunctions. * <p> * Composite functions allow the appearance of a map feature to change with both its * properties and zoom. Each stop is an array with two elements, the first is an object diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Filter.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Filter.java index 643a126388..4dbb461e4c 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Filter.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Filter.java @@ -11,7 +11,7 @@ import java.util.Collections; public class Filter { /** - * Base {@link Filter} statement. Subclassed to provide concrete statements. + * Base Filter statement. Subclassed to provide concrete statements. */ public abstract static class Statement { protected final String operator; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java index 48e0ec5de3..5e345268f9 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java @@ -467,6 +467,27 @@ public final class Property { @Retention(RetentionPolicy.SOURCE) public @interface FILL_EXTRUSION_TRANSLATE_ANCHOR {} + // ANCHOR: Whether extruded geometries are lit relative to the map or viewport. + + /** + * The position of the light source is aligned to the rotation of the map. + */ + public static final String ANCHOR_MAP = "map"; + /** + * The position of the light source is aligned to the rotation of the viewport. + */ + public static final String ANCHOR_VIEWPORT = "viewport"; + + /** + * Whether extruded geometries are lit relative to the map or viewport. + */ + @StringDef({ + ANCHOR_MAP, + ANCHOR_VIEWPORT, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ANCHOR {} + private Property() { } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/TransitionOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/TransitionOptions.java index a46c11b35c..6e6e4ca613 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/TransitionOptions.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/TransitionOptions.java @@ -1,23 +1,51 @@ package com.mapbox.mapboxsdk.style.layers; +/** + * Resembles transition property from the style specification. + * + * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#transition">Transition documentation</a> + */ public class TransitionOptions { private long duration; private long delay; + /** + * Create a transition property based on duration and a delay. + * + * @param duration the duration of the transition + * @param delay the delay to start the transition + */ public TransitionOptions(long duration, long delay) { this.duration = duration; this.delay = delay; } + /** + * Create a transition property based on duration and a delay. + * + * @param duration the duration of the transition + * @param delay the delay to start the transition + * @return a new transition property object + */ public static TransitionOptions fromTransitionOptions(long duration, long delay) { return new TransitionOptions(duration, delay); } + /** + * Get the transition duration. + * + * @return the transition duration + */ public long getDuration() { return duration; } + /** + * Get the transition delay. + * + * @return the transition delay + */ public long getDelay() { return delay; } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Light.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Light.java new file mode 100644 index 0000000000..b66a50b8a4 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Light.java @@ -0,0 +1,181 @@ +// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`. + +package com.mapbox.mapboxsdk.style.light; + +import android.support.annotation.ColorInt; +import android.support.annotation.NonNull; +import android.support.annotation.UiThread; + +import com.mapbox.mapboxsdk.style.layers.Property; +import com.mapbox.mapboxsdk.style.layers.PropertyFactory; +import com.mapbox.mapboxsdk.style.layers.TransitionOptions; + +/** + * The global light source. + * + * @see <a href="https://www.mapbox.com/mapbox-gl-style-spec/#light>">The online documentation</a> + */ +@UiThread +public class Light { + + private long nativePtr; + + /** + * Creates a Light. + * + * @param nativePtr pointer used by core + */ + public Light(long nativePtr) { + this.nativePtr = nativePtr; + } + + /** + * Set the Anchor property. Whether extruded geometries are lit relative to the map or viewport. + * + * @param anchor as String + */ + public void setAnchor(@Property.ANCHOR String anchor) { + nativeSetAnchor(anchor); + } + + /** + * Get the Anchor property. Whether extruded geometries are lit relative to the map or viewport. + * + * @return anchor as String + */ + @Property.ANCHOR public String getAnchor() { + return nativeGetAnchor(); + } + + /** + * Set the Position property. Position of the light source relative to lit (extruded) geometries, in [r radial coordinate, a azimuthal angle, p polar angle] where r indicates the distance from the center of the base of an object to its light, a indicates the position of the light relative to 0° (0° when `light.anchor` is set to `viewport` corresponds to the top of the viewport, or 0° when `light.anchor` is set to `map` corresponds to due north, and degrees proceed clockwise), and p indicates the height of the light (from 0°, directly above, to 180°, directly below). + * + * @param position of the light + */ + public void setPosition(@NonNull Position position) { + nativeSetPosition(position); + } + + /** + * Get the Position property. Position of the light source relative to lit (extruded) geometries, in [r radial coordinate, a azimuthal angle, p polar angle] where r indicates the distance from the center of the base of an object to its light, a indicates the position of the light relative to 0° (0° when `light.anchor` is set to `viewport` corresponds to the top of the viewport, or 0° when `light.anchor` is set to `map` corresponds to due north, and degrees proceed clockwise), and p indicates the height of the light (from 0°, directly above, to 180°, directly below). + * + * @return position as Position + */ + public Position getPosition() { + return nativeGetPosition(); + } + + /** + * Get the Position property transition options. + * + * @return transition options for position + */ + public TransitionOptions getPositionTransition() { + return nativeGetPositionTransition(); + } + + /** + * Set the Position property transition options. + * + * @param options transition options for position + */ + public void setPositionTransition(TransitionOptions options) { + nativeSetPositionTransition(options.getDuration(), options.getDelay()); + } + + /** + * Set the Color property. Color tint for lighting extruded geometries. + * + * @param color as int + */ + public void setColor(@ColorInt int color) { + nativeSetColor(PropertyFactory.colorToRgbaString(color)); + } + + /** + * Set the Color property. Color tint for lighting extruded geometries. + * + * @param color as String + */ + public void setColor(String color) { + nativeSetColor(color); + } + + /** + * Get the Color property. Color tint for lighting extruded geometries. + * + * @return color as String + */ + public String getColor() { + return nativeGetColor(); + } + + /** + * Get the Color property transition options. + * + * @return transition options for color + */ + public TransitionOptions getColorTransition() { + return nativeGetColorTransition(); + } + + /** + * Set the Color property transition options. + * + * @param options transition options for color + */ + public void setColorTransition(TransitionOptions options) { + nativeSetColorTransition(options.getDuration(), options.getDelay()); + } + + /** + * Set the Intensity property. Intensity of lighting (on a scale from 0 to 1). Higher numbers will present as more extreme contrast. + * + * @param intensity as Float + */ + public void setIntensity(float intensity) { + nativeSetIntensity(intensity); + } + + /** + * Get the Intensity property. Intensity of lighting (on a scale from 0 to 1). Higher numbers will present as more extreme contrast. + * + * @return intensity as Float + */ + public float getIntensity() { + return nativeGetIntensity(); + } + + /** + * Get the Intensity property transition options. + * + * @return transition options for intensity + */ + public TransitionOptions getIntensityTransition() { + return nativeGetIntensityTransition(); + } + + /** + * Set the Intensity property transition options. + * + * @param options transition options for intensity + */ + public void setIntensityTransition(TransitionOptions options) { + nativeSetIntensityTransition(options.getDuration(), options.getDelay()); + } + + private native void nativeSetAnchor(String anchor); + private native String nativeGetAnchor(); + private native void nativeSetPosition(Position position); + private native Position nativeGetPosition(); + private native TransitionOptions nativeGetPositionTransition(); + private native void nativeSetPositionTransition(long duration, long delay); + private native void nativeSetColor(String color); + private native String nativeGetColor(); + private native TransitionOptions nativeGetColorTransition(); + private native void nativeSetColorTransition(long duration, long delay); + private native void nativeSetIntensity(float intensity); + private native float nativeGetIntensity(); + private native TransitionOptions nativeGetIntensityTransition(); + private native void nativeSetIntensityTransition(long duration, long delay); +}
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Position.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Position.java new file mode 100644 index 0000000000..215db03ad2 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Position.java @@ -0,0 +1,81 @@ +package com.mapbox.mapboxsdk.style.light; + +/** + * Position of the light source relative to lit (extruded) geometries. + * <p> + * The position is constructed out of a radial coordinate, an azimuthal angle and a polar angle. + * where the radial coordinate indicates the distance from the center of the base of an object to its light, the + * azimuthal angle indicates the position of the light relative to 0° (0° when + * {@link com.mapbox.mapboxsdk.style.layers.Property.ANCHOR} is set to viewport corresponds to the top of the + * viewport, or 0° when {@link com.mapbox.mapboxsdk.style.layers.Property.ANCHOR} is set to map corresponds to due + * north, and degrees proceed clockwise), and polar indicates the height of the light + * (from 0°, directly above, to 180°, directly below). + */ +public class Position { + + private float radialCoordinate; + private float azimuthalAngle; + private float polarAngle; + + /** + * Creates a Position from a radial coordinate, an azimuthal angle & a polar angle. + * + * @param radialCoordinate the distance from the center of the base of an object to its light + * @param azimuthalAngle the position of the light relative to 0° + * @param polarAngle the height of the light + */ + public Position(float radialCoordinate, float azimuthalAngle, float polarAngle) { + this.radialCoordinate = radialCoordinate; + this.azimuthalAngle = azimuthalAngle; + this.polarAngle = polarAngle; + } + + /** + * Returns a Position from a radial coordinate, an azimuthal angle & a polar angle + * + * @param radialCoordinate the radial coordinate + * @param azimuthalAngle the azimuthal angle + * @param polarAngle the polar angle + * @return the created Position object + */ + public static Position fromPosition(float radialCoordinate, float azimuthalAngle, float polarAngle) { + return new Position(radialCoordinate, azimuthalAngle, polarAngle); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + Position position = (Position) o; + + if (Float.compare(position.radialCoordinate, radialCoordinate) != 0) { + return false; + } + if (Float.compare(position.azimuthalAngle, azimuthalAngle) != 0) { + return false; + } + return Float.compare(position.polarAngle, polarAngle) == 0; + } + + @Override + public int hashCode() { + int result = (radialCoordinate != +0.0f ? Float.floatToIntBits(radialCoordinate) : 0); + result = 31 * result + (azimuthalAngle != +0.0f ? Float.floatToIntBits(azimuthalAngle) : 0); + result = 31 * result + (polarAngle != +0.0f ? Float.floatToIntBits(polarAngle) : 0); + return result; + } + + @Override + public String toString() { + return "Position{" + + "radialCoordinate=" + radialCoordinate + + ", azimuthalAngle=" + azimuthalAngle + + ", polarAngle=" + polarAngle + + '}'; + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/light.java.ejs b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/light.java.ejs new file mode 100644 index 0000000000..067efe1092 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/light.java.ejs @@ -0,0 +1,121 @@ +<% + const properties = locals.properties; + const doc = locals.doc; +-%> +// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`. + +package com.mapbox.mapboxsdk.style.light; + +import android.support.annotation.ColorInt; +import android.support.annotation.NonNull; +import android.support.annotation.UiThread; + +import com.mapbox.mapboxsdk.style.layers.Property; +import com.mapbox.mapboxsdk.style.layers.PropertyFactory; +import com.mapbox.mapboxsdk.style.layers.TransitionOptions; + +/** + * The global light source. + * + * @see <a href="https://www.mapbox.com/mapbox-gl-style-spec/#light>">The online documentation</a> + */ +@UiThread +public class Light { + + private long nativePtr; + + /** + * Creates a Light. + * + * @param nativePtr pointer used by core + */ + public Light(long nativePtr) { + this.nativePtr = nativePtr; + } +<% for (const property of properties) { -%> +<% if (property.name == "position") {-%> + + /** + * Set the <%- camelize(property.name) %> property. <%- property.doc %> + * + * @param position of the light + */ + public void set<%- camelize(property.name) %>(@NonNull Position position) { + nativeSet<%- camelize(property.name) %>(position); + } + + /** + * Get the <%- camelize(property.name) %> property. <%- property.doc %> + * + * @return <%- property.name %> as Position + */ + public Position get<%- camelize(property.name) %>() { + return nativeGet<%- camelize(property.name) %>(); + } +<% } else { -%> +<% if (property.name == "color") {-%> + + /** + * Set the <%- camelize(property.name) %> property. <%- property.doc %> + * + * @param <%- property.name %> as int + */ + public void set<%- camelize(property.name) %>(@ColorInt int <%- property.name %>) { + nativeSet<%- camelize(property.name) %>(PropertyFactory.colorToRgbaString(<%- property.name %>)); + } +<% } -%> + + /** + * Set the <%- camelize(property.name) %> property. <%- property.doc %> + * + * @param <%- property.name %> as <%- propertyType(property) %> + */ + public void set<%- camelize(property.name) %>(<%- propertyTypeAnnotation(property) %><%- iff(() => propertyTypeAnnotation(property), " ") %><%- propertyJavaType(property) %> <%- property.name %>) { + nativeSet<%- camelize(property.name) %>(<%- property.name %>); + } + + /** + * Get the <%- camelize(property.name) %> property. <%- property.doc %> + * + * @return <%- property.name %> as <%- propertyType(property) %> + */ + <%- propertyTypeAnnotation(property) %> public <%- propertyJavaType(property) %> get<%- camelize(property.name) %>() { + return nativeGet<%- camelize(property.name) %>(); + } +<% } -%> +<% if (property.transition) { -%> + + /** + * Get the <%- camelize(property.name) %> property transition options. + * + * @return transition options for <%- property.name %> + */ + public TransitionOptions get<%- camelize(property.name) %>Transition() { + return nativeGet<%- camelize(property.name) %>Transition(); + } + + /** + * Set the <%- camelize(property.name) %> property transition options. + * + * @param options transition options for <%- property.name %> + */ + public void set<%- camelize(property.name) %>Transition(TransitionOptions options) { + nativeSet<%- camelize(property.name) %>Transition(options.getDuration(), options.getDelay()); + } +<% } -%> +<% } -%> + +<% for (const property of properties) { -%> +<% if (property.name == "position") {-%> + private native void nativeSet<%- camelize(property.name) %>(Position position); + private native Position nativeGet<%- camelize(property.name) %>(); +<% } else { -%> + private native void nativeSet<%- camelize(property.name) %>(<%- propertyJavaType(property) -%> <%- property.name %>); + private native <%- propertyJavaType(property) -%> nativeGet<%- camelize(property.name) %>(); +<% } -%> +<% if (property.transition) { -%> + private native TransitionOptions nativeGet<%- camelize(property.name) %>Transition(); + private native void nativeSet<%- camelize(property.name) %>Transition(long duration, long delay); +<% } -%> +<% } -%> +}
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/package-info.java new file mode 100644 index 0000000000..a613bf9587 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/package-info.java @@ -0,0 +1,4 @@ +/** + * Contains the Mapbox Maps Android Style Light API classes. + */ +package com.mapbox.mapboxsdk.style.light; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/AnimatorUtils.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/AnimatorUtils.java index 7694604d9f..34c52c829b 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/AnimatorUtils.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/AnimatorUtils.java @@ -10,13 +10,31 @@ import android.support.annotation.Nullable; import android.support.v4.view.animation.FastOutSlowInInterpolator; import android.view.View; +/** + * Animator utility class. + */ public class AnimatorUtils { + /** + * Animate a view from an animator resource. + * + * @param view the view to be animated + * @param animatorRes the animator resource to be loaded + * @param listener the animator end listener + */ public static void animate(@NonNull final View view, @AnimatorRes int animatorRes, @Nullable OnAnimationEndListener listener) { animate(view, animatorRes, -1, listener); } + /** + * Animate a view from an animator resource. + * + * @param view the view to be animated + * @param animatorRes the animator resource to be loaded + * @param duration the duration of the animator + * @param listener the animator end listener + */ public static void animate(final View view, @AnimatorRes int animatorRes, int duration, @Nullable final OnAnimationEndListener listener) { if (view == null) { @@ -43,14 +61,33 @@ public class AnimatorUtils { animator.start(); } + /** + * Animate a view from an animator resource. + * + * @param view the view to be animated + * @param animatorRes the animator resource to be loaded + */ public static void animate(@NonNull final View view, @AnimatorRes int animatorRes) { animate(view, animatorRes, -1); } + /** + * Animate a view from an animator resource. + * + * @param view the view to be animated + * @param animatorRes the animator resource to be loaded + * @param duration the duration of the animator + */ public static void animate(@NonNull final View view, @AnimatorRes int animatorRes, int duration) { animate(view, animatorRes, duration, null); } + /** + * Animate a view rotation property to a value. + * + * @param view the view to be rotated + * @param rotation the value to animate to + */ public static void rotate(@NonNull final View view, float rotation) { view.setLayerType(View.LAYER_TYPE_HARDWARE, null); ObjectAnimator rotateAnimator = ObjectAnimator.ofFloat(view, View.ROTATION, view.getRotation(), rotation); @@ -64,6 +101,12 @@ public class AnimatorUtils { rotateAnimator.start(); } + /** + * Animate a view rotation property by a value. + * + * @param view the view to be rotated + * @param rotationBy the value to animate by + */ public static void rotateBy(@NonNull final View view, float rotationBy) { view.setLayerType(View.LAYER_TYPE_HARDWARE, null); view.animate().rotationBy(rotationBy).setInterpolator(new FastOutSlowInInterpolator()).setListener( @@ -76,6 +119,13 @@ public class AnimatorUtils { }); } + /** + * Animate a view alpha property to a value. + * + * @param convertView the view to be animated + * @param alpha the value to animate to + * @param listener the animator end listener + */ public static void alpha(@NonNull final View convertView, float alpha, @Nullable final OnAnimationEndListener listener) { convertView.setLayerType(View.LAYER_TYPE_HARDWARE, null); @@ -99,10 +149,19 @@ public class AnimatorUtils { rotateAnimator.start(); } + /** + * Animate a view alpha property to a value. + * + * @param convertView the view to be animated + * @param alpha the value to animate to + */ public static void alpha(@NonNull final View convertView, float alpha) { alpha(convertView, alpha, null); } + /** + * An interface definition that is invoked when an animation ends. + */ public interface OnAnimationEndListener { void onAnimationEnd(); } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/ColorUtils.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/ColorUtils.java index 2da2472d69..24c76243d9 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/ColorUtils.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/ColorUtils.java @@ -17,6 +17,9 @@ import com.mapbox.mapboxsdk.exceptions.ConversionException; import java.util.regex.Matcher; import java.util.regex.Pattern; +/** + * Color utility class. + */ public class ColorUtils { /** diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml index ce20cb9a8b..8edbd47c29 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml @@ -13,6 +13,6 @@ <dimen name="mapbox_eight_dp">8dp</dimen> <dimen name="mapbox_ten_dp">10dp</dimen> <dimen name="mapbox_sixteen_dp">16dp</dimen> - <dimen name="mapbox_ninety_five_dp">95dp</dimen> + <dimen name="mapbox_ninety_two_dp">92dp</dimen> <dimen name="mapbox_my_locationview_outer_circle">18dp</dimen> </resources> diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/IconTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/IconTest.java index 5f6f6b6c6d..1c259af2d0 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/IconTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/IconTest.java @@ -9,6 +9,7 @@ import org.mockito.MockitoAnnotations; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotSame; +import static org.mockito.Mockito.when; public class IconTest { @@ -18,6 +19,7 @@ public class IconTest { @Before public void beforeTest() { MockitoAnnotations.initMocks(this); + when(bitmap.getConfig()).thenReturn(Bitmap.Config.ARGB_8888); } @Test diff --git a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle index 7c263652af..656789fcdb 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle +++ b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle @@ -57,10 +57,7 @@ dependencies { } // Support libraries - compile rootProject.ext.dep.supportAnnotations - compile rootProject.ext.dep.supportV4 compile rootProject.ext.dep.supportAppcompatV7 - compile rootProject.ext.dep.supportDesign compile rootProject.ext.dep.supportRecyclerView // Leak Canary diff --git a/platform/android/MapboxGLAndroidSDKTestApp/proguard-rules.pro b/platform/android/MapboxGLAndroidSDKTestApp/proguard-rules.pro index 5d944b5dd4..f8243ca44f 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/proguard-rules.pro +++ b/platform/android/MapboxGLAndroidSDKTestApp/proguard-rules.pro @@ -4,5 +4,6 @@ -dontwarn org.codehaus.** -keep class com.google.** -dontwarn com.google.** +-dontwarn java.nio.** -keep class com.mapbox.mapboxsdk.testapp.model.customlayer.ExampleCustomLayer { *; } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LightTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LightTest.java new file mode 100644 index 0000000000..36833fb4ee --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LightTest.java @@ -0,0 +1,162 @@ +package com.mapbox.mapboxsdk.testapp.style; + +import android.graphics.Color; +import android.support.test.espresso.UiController; +import android.support.test.espresso.ViewAction; +import android.support.test.runner.AndroidJUnit4; +import android.view.View; + +import com.mapbox.mapboxsdk.style.light.Light; +import com.mapbox.mapboxsdk.style.functions.Function; +import com.mapbox.mapboxsdk.style.functions.stops.IdentityStops; +import com.mapbox.mapboxsdk.style.layers.FillExtrusionLayer; +import com.mapbox.mapboxsdk.style.layers.TransitionOptions; +import com.mapbox.mapboxsdk.style.light.Position; +import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest; +import com.mapbox.mapboxsdk.testapp.activity.style.FillExtrusionStyleTestActivity; + +import timber.log.Timber; + +import org.hamcrest.Matcher; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static android.support.test.espresso.Espresso.onView; +import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; +import static android.support.test.espresso.matcher.ViewMatchers.withId; +import static com.mapbox.mapboxsdk.style.layers.Filter.eq; +import static com.mapbox.mapboxsdk.style.layers.Property.ANCHOR_MAP; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionBase; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionColor; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionHeight; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionOpacity; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotNull; + +@RunWith(AndroidJUnit4.class) +public class LightTest extends BaseActivityTest { + + private Light light; + + @Test + public void testAnchor() { + validateTestSetup(); + setupLayer(); + Timber.i("anchor"); + assertNotNull(light); + // Set and Get + light.setAnchor(ANCHOR_MAP); + assertEquals("Anchor should match", ANCHOR_MAP, light.getAnchor()); + } + + @Test + public void testPositionTransition() { + validateTestSetup(); + setupLayer(); + Timber.i("positionTransitionOptions"); + assertNotNull(light); + + // Set and Get + TransitionOptions options = new TransitionOptions(300, 100); + light.setPositionTransition(options); + assertEquals("Transition options should match", options, light.getPositionTransition()); + } + + @Test + public void testPosition() { + validateTestSetup(); + setupLayer(); + Timber.i("position"); + assertNotNull(light); + + // Set and Get + Position position = new Position(1,2,3); + light.setPosition(position); + assertEquals("Position should match", position, light.getPosition()); + } + + @Test + public void testColorTransition() { + validateTestSetup(); + setupLayer(); + Timber.i("colorTransitionOptions"); + assertNotNull(light); + + // Set and Get + TransitionOptions options = new TransitionOptions(300, 100); + light.setColorTransition(options); + assertEquals("Transition options should match", options, light.getColorTransition()); + } + + @Test + public void testColor() { + validateTestSetup(); + setupLayer(); + Timber.i("color"); + assertNotNull(light); + // Set and Get + light.setColor("rgba(0, 0, 0, 1)"); + assertEquals("Color should match", "rgba(0, 0, 0, 1)".replaceAll("\\s+",""), light.getColor()); + } + + @Test + public void testIntensityTransition() { + validateTestSetup(); + setupLayer(); + Timber.i("intensityTransitionOptions"); + assertNotNull(light); + + // Set and Get + TransitionOptions options = new TransitionOptions(300, 100); + light.setIntensityTransition(options); + assertEquals("Transition options should match", options, light.getIntensityTransition()); + } + + @Test + public void testIntensity() { + validateTestSetup(); + setupLayer(); + Timber.i("intensity"); + assertNotNull(light); + // Set and Get + light.setIntensity(0.3f); + assertEquals("Intensity should match", 0.3f, light.getIntensity()); + } + + private void setupLayer() { + onView(withId(R.id.mapView)).perform(new ViewAction() { + @Override + public Matcher<View> getConstraints() { + return isDisplayed(); + } + + @Override + public String getDescription() { + return getClass().getSimpleName(); + } + + @Override + public void perform(UiController uiController, View view) { + light = mapboxMap.getLight(); + FillExtrusionLayer fillExtrusionLayer = new FillExtrusionLayer("3d-buildings", "composite"); + fillExtrusionLayer.setSourceLayer("building"); + fillExtrusionLayer.setFilter(eq("extrude", "true")); + fillExtrusionLayer.setMinZoom(15); + fillExtrusionLayer.setProperties( + fillExtrusionColor(Color.LTGRAY), + fillExtrusionHeight(Function.property("height", new IdentityStops<Float>())), + fillExtrusionBase(Function.property("min_height", new IdentityStops<Float>())), + fillExtrusionOpacity(0.6f) + ); + mapboxMap.addLayer(fillExtrusionLayer); + } + }); + } + + @Override + protected Class getActivityClass() { + return FillExtrusionStyleTestActivity.class; + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/light.junit.ejs b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/light.junit.ejs new file mode 100644 index 0000000000..cb3ba58100 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/light.junit.ejs @@ -0,0 +1,128 @@ +<% + const properties = locals.properties; +-%> +package com.mapbox.mapboxsdk.testapp.style; + +import android.graphics.Color; +import android.support.test.espresso.UiController; +import android.support.test.espresso.ViewAction; +import android.support.test.runner.AndroidJUnit4; +import android.view.View; + +import com.mapbox.mapboxsdk.style.light.Light; +import com.mapbox.mapboxsdk.style.functions.Function; +import com.mapbox.mapboxsdk.style.functions.stops.IdentityStops; +import com.mapbox.mapboxsdk.style.layers.FillExtrusionLayer; +import com.mapbox.mapboxsdk.style.layers.TransitionOptions; +import com.mapbox.mapboxsdk.style.light.Position; +import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest; +import com.mapbox.mapboxsdk.testapp.activity.style.FillExtrusionStyleTestActivity; + +import timber.log.Timber; + +import org.hamcrest.Matcher; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static android.support.test.espresso.Espresso.onView; +import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; +import static android.support.test.espresso.matcher.ViewMatchers.withId; +import static com.mapbox.mapboxsdk.style.layers.Filter.eq; +import static com.mapbox.mapboxsdk.style.layers.Property.ANCHOR_MAP; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionBase; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionColor; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionHeight; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionOpacity; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotNull; + +@RunWith(AndroidJUnit4.class) +public class LightTest extends BaseActivityTest { + + private Light light; +<% for (const property of properties) { -%> +<% if (property.transition) { -%> + + @Test + public void test<%- camelize(property.name) %>Transition() { + validateTestSetup(); + setupLayer(); + Timber.i("<%- property.name %>TransitionOptions"); + assertNotNull(light); + + // Set and Get + TransitionOptions options = new TransitionOptions(300, 100); + light.set<%- camelize(property.name) %>Transition(options); + assertEquals("Transition options should match", options, light.get<%- camelize(property.name) %>Transition()); + } +<% } -%> +<% if (property.name == "position") { -%> + + @Test + public void test<%- camelize(property.name) %>() { + validateTestSetup(); + setupLayer(); + Timber.i("<%- property.name %>"); + assertNotNull(light); + + // Set and Get + Position position = new Position(1,2,3); + light.set<%- camelize(property.name) %>(position); + assertEquals("Position should match", position, light.get<%- camelize(property.name) %>()); + } +<% } else { -%> + + @Test + public void test<%- camelize(property.name) %>() { + validateTestSetup(); + setupLayer(); + Timber.i("<%- property.name %>"); + assertNotNull(light); + // Set and Get + light.set<%- camelize(property.name) %>(<%- defaultValueJava(property) %>); +<% if (property.name == 'color') { -%> + assertEquals("<%- camelize(property.name) %> should match", <%- defaultValueJava(property) %>.replaceAll("\\s+",""), light.get<%- camelize(property.name) %>()); +<% } else { -%> + assertEquals("<%- camelize(property.name) %> should match", <%- defaultValueJava(property) %>, light.get<%- camelize(property.name) %>()); +<% } -%> + } +<% } -%> +<% } -%> + + private void setupLayer() { + onView(withId(R.id.mapView)).perform(new ViewAction() { + @Override + public Matcher<View> getConstraints() { + return isDisplayed(); + } + + @Override + public String getDescription() { + return getClass().getSimpleName(); + } + + @Override + public void perform(UiController uiController, View view) { + light = mapboxMap.getLight(); + FillExtrusionLayer fillExtrusionLayer = new FillExtrusionLayer("3d-buildings", "composite"); + fillExtrusionLayer.setSourceLayer("building"); + fillExtrusionLayer.setFilter(eq("extrude", "true")); + fillExtrusionLayer.setMinZoom(15); + fillExtrusionLayer.setProperties( + fillExtrusionColor(Color.LTGRAY), + fillExtrusionHeight(Function.property("height", new IdentityStops<Float>())), + fillExtrusionBase(Function.property("min_height", new IdentityStops<Float>())), + fillExtrusionOpacity(0.6f) + ); + mapboxMap.addLayer(fillExtrusionLayer); + } + }); + } + + @Override + protected Class getActivityClass() { + return FillExtrusionStyleTestActivity.class; + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml index 9ede763533..1a70e4548a 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml @@ -661,7 +661,9 @@ <activity android:name=".activity.espresso.EspressoTestActivity" android:screenOrientation="portrait"/> - + <activity + android:name=".activity.style.FillExtrusionStyleTestActivity" + android:screenOrientation="portrait"/> <!-- Configuration Settings --> <meta-data android:name="com.mapbox.TestEventsServer" diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraPositionActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraPositionActivity.java index 08399f3b3e..60518239c8 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraPositionActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraPositionActivity.java @@ -43,10 +43,35 @@ public class CameraPositionActivity extends AppCompatActivity implements OnMapRe public void onMapReady(@NonNull final MapboxMap mapboxMap) { this.mapboxMap = mapboxMap; - mapboxMap.setOnCameraChangeListener(new MapboxMap.OnCameraChangeListener() { + mapboxMap.setOnCameraIdleListener(new MapboxMap.OnCameraIdleListener() { @Override - public void onCameraChange(CameraPosition position) { - Timber.w("OnCameraChange: " + position); + public void onCameraIdle() { + Timber.e("OnCameraIdle"); + } + }); + + mapboxMap.setOnCameraMoveCancelListener(new MapboxMap.OnCameraMoveCanceledListener() { + @Override + public void onCameraMoveCanceled() { + Timber.e("OnCameraMoveCanceled"); + } + }); + + mapboxMap.setOnCameraMoveListener(new MapboxMap.OnCameraMoveListener() { + @Override + public void onCameraMove() { + Timber.e("OnCameraMove"); + } + }); + + mapboxMap.setOnCameraMoveStartedistener(new MapboxMap.OnCameraMoveStartedListener() { + + private final String[] REASONS = {"REASON_API_GESTURE", "REASON_DEVELOPER_ANIMATION", "REASON_API_ANIMATION"}; + + @Override + public void onCameraMoveStarted(int reason) { + // reason ranges from 1 <-> 3 + Timber.e("OnCameraMoveStarted: %s", REASONS[reason - 1]); } }); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/OfflineActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/OfflineActivity.java index be5d809457..344e9e140a 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/OfflineActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/OfflineActivity.java @@ -274,22 +274,20 @@ public class OfflineActivity extends AppCompatActivity } // Debug - Timber.d(String.format("%s/%s resources; %s bytes downloaded.", + Timber.d("%s/%s resources; %s bytes downloaded.", String.valueOf(status.getCompletedResourceCount()), String.valueOf(status.getRequiredResourceCount()), - String.valueOf(status.getCompletedResourceSize()))); + String.valueOf(status.getCompletedResourceSize())); } @Override public void onError(OfflineRegionError error) { - Timber.e("onError reason: " + error.getReason()); - Timber.e("onError message: " + error.getMessage()); - offlineRegion.setObserver(null); + Timber.e("onError: %s, %s", error.getReason(), error.getMessage()); } @Override public void mapboxTileCountLimitExceeded(long limit) { - Timber.e("Mapbox tile count limit exceeded: " + limit); + Timber.e("Mapbox tile count limit exceeded: %s", limit); } }); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/BuildingFillExtrusionActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/BuildingFillExtrusionActivity.java index f444aa40f0..def7d1678a 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/BuildingFillExtrusionActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/BuildingFillExtrusionActivity.java @@ -4,6 +4,9 @@ import android.graphics.Color; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v7.app.AppCompatActivity; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; @@ -11,6 +14,10 @@ import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; import com.mapbox.mapboxsdk.style.functions.Function; import com.mapbox.mapboxsdk.style.functions.stops.IdentityStops; import com.mapbox.mapboxsdk.style.layers.FillExtrusionLayer; +import com.mapbox.mapboxsdk.style.layers.Property; +import com.mapbox.mapboxsdk.style.layers.PropertyFactory; +import com.mapbox.mapboxsdk.style.light.Light; +import com.mapbox.mapboxsdk.style.light.Position; import com.mapbox.mapboxsdk.testapp.R; import static com.mapbox.mapboxsdk.style.layers.Filter.eq; @@ -25,6 +32,13 @@ import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionOpa public class BuildingFillExtrusionActivity extends AppCompatActivity { private MapView mapView; + private MapboxMap mapboxMap; + private Light light; + + private boolean isMapAnchorLight; + private boolean isLowIntensityLight; + private boolean isRedColor; + private boolean isInitPosition; @Override public void onCreate(Bundle savedInstanceState) { @@ -35,21 +49,72 @@ public class BuildingFillExtrusionActivity extends AppCompatActivity { mapView.getMapAsync(new OnMapReadyCallback() { @Override public void onMapReady(@NonNull final MapboxMap map) { - FillExtrusionLayer fillExtrusionLayer = new FillExtrusionLayer("3d-buildings", "composite"); - fillExtrusionLayer.setSourceLayer("building"); - fillExtrusionLayer.setFilter(eq("extrude", "true")); - fillExtrusionLayer.setMinZoom(15); - fillExtrusionLayer.setProperties( - fillExtrusionColor(Color.LTGRAY), - fillExtrusionHeight(Function.property("height", new IdentityStops<Float>())), - fillExtrusionBase(Function.property("min_height", new IdentityStops<Float>())), - fillExtrusionOpacity(0.6f) - ); - map.addLayer(fillExtrusionLayer); + mapboxMap = map; + setupBuildings(); + setupLight(); } }); } + private void setupBuildings() { + FillExtrusionLayer fillExtrusionLayer = new FillExtrusionLayer("3d-buildings", "composite"); + fillExtrusionLayer.setSourceLayer("building"); + fillExtrusionLayer.setFilter(eq("extrude", "true")); + fillExtrusionLayer.setMinZoom(15); + fillExtrusionLayer.setProperties( + fillExtrusionColor(Color.LTGRAY), + fillExtrusionHeight(Function.property("height", new IdentityStops<Float>())), + fillExtrusionBase(Function.property("min_height", new IdentityStops<Float>())), + fillExtrusionOpacity(0.9f) + ); + mapboxMap.addLayer(fillExtrusionLayer); + } + + private void setupLight() { + light = mapboxMap.getLight(); + + findViewById(R.id.fabLightPosition).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + isInitPosition = !isInitPosition; + if (isInitPosition) { + light.setPosition(new Position(1.5f, 90, 80)); + } else { + light.setPosition(new Position(1.15f, 210, 30)); + } + } + }); + + findViewById(R.id.fabLightColor).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + isRedColor = !isRedColor; + light.setColor(PropertyFactory.colorToRgbaString(isRedColor ? Color.RED : Color.BLUE)); + } + }); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.menu_building, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (light != null) { + int id = item.getItemId(); + if (id == R.id.menu_action_anchor) { + isMapAnchorLight = !isMapAnchorLight; + light.setAnchor(isMapAnchorLight ? Property.ANCHOR_MAP : Property.ANCHOR_VIEWPORT); + } else if (id == R.id.menu_action_intensity) { + isLowIntensityLight = !isLowIntensityLight; + light.setIntensity(isLowIntensityLight ? 0.35f : 1.0f); + } + } + return super.onOptionsItemSelected(item); + } + @Override protected void onStart() { super.onStart(); @@ -91,5 +156,4 @@ public class BuildingFillExtrusionActivity extends AppCompatActivity { super.onDestroy(); mapView.onDestroy(); } - } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionStyleTestActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionStyleTestActivity.java new file mode 100644 index 0000000000..1ff0b0e8e1 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionStyleTestActivity.java @@ -0,0 +1,78 @@ +package com.mapbox.mapboxsdk.testapp.activity.style; + + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; + +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; +import com.mapbox.mapboxsdk.testapp.R; + +public class FillExtrusionStyleTestActivity extends AppCompatActivity { + + public MapView mapView; + private MapboxMap mapboxMap; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_extrusion_test); + + // Initialize map as normal + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(new OnMapReadyCallback() { + @Override + public void onMapReady(MapboxMap mapboxMap) { + FillExtrusionStyleTestActivity.this.mapboxMap = mapboxMap; + } + }); + } + + public MapboxMap getMapboxMap() { + return mapboxMap; + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MockLocationEngine.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MockLocationEngine.java index b87c723fda..b02b35b0d0 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MockLocationEngine.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MockLocationEngine.java @@ -63,7 +63,9 @@ public class MockLocationEngine extends LocationEngine { @Override public void removeLocationUpdates() { - handler.removeCallbacksAndMessages(null); + if (handler != null) { + handler.removeCallbacksAndMessages(null); + } } private Location getNextLocation() { diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_paint.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_paint.xml new file mode 100644 index 0000000000..f9e55e1480 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_paint.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M18,4V3c0,-0.55 -0.45,-1 -1,-1H5c-0.55,0 -1,0.45 -1,1v4c0,0.55 0.45,1 1,1h12c0.55,0 1,-0.45 1,-1V6h1v4H9v11c0,0.55 0.45,1 1,1h2c0.55,0 1,-0.45 1,-1v-9h8V4h-3z"/> +</vector> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_building_layer.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_building_layer.xml index d9a10871b5..fa37c485d7 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_building_layer.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_building_layer.xml @@ -1,9 +1,8 @@ <?xml version="1.0" encoding="utf-8"?> -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical"> + android:layout_height="match_parent"> <com.mapbox.mapboxsdk.maps.MapView android:id="@id/mapView" @@ -13,6 +12,27 @@ app:mapbox_cameraTargetLng="-74.0066" app:mapbox_cameraTilt="45" app:mapbox_cameraZoom="15" - app:mapbox_styleUrl="@string/mapbox_style_mapbox_streets"/> + app:mapbox_styleUrl="@string/mapbox_style_dark"/> -</RelativeLayout> + <android.support.design.widget.FloatingActionButton + android:id="@+id/fabLightPosition" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="end|bottom" + android:layout_marginBottom="82dp" + android:layout_marginRight="@dimen/fab_margin" + android:layout_marginEnd="@dimen/fab_margin" + android:src="@drawable/ic_my_location" + app:backgroundTint="@color/accent" + app:layout_anchorGravity="top"/> + + <android.support.design.widget.FloatingActionButton + android:id="@+id/fabLightColor" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="end|bottom" + android:layout_margin="@dimen/fab_margin" + android:src="@drawable/ic_paint" + app:backgroundTint="@color/primary"/> + +</FrameLayout> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_extrusion_test.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_extrusion_test.xml new file mode 100644 index 0000000000..d9a10871b5 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_extrusion_test.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <com.mapbox.mapboxsdk.maps.MapView + android:id="@id/mapView" + android:layout_width="match_parent" + android:layout_height="match_parent" + app:mapbox_cameraTargetLat="40.7135" + app:mapbox_cameraTargetLng="-74.0066" + app:mapbox_cameraTilt="45" + app:mapbox_cameraZoom="15" + app:mapbox_styleUrl="@string/mapbox_style_mapbox_streets"/> + +</RelativeLayout> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_press_for_marker.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_press_for_marker.xml index 9932803907..c2098b0f5b 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_press_for_marker.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_press_for_marker.xml @@ -16,6 +16,6 @@ app:mapbox_styleUrl="@string/mapbox_style_mapbox_streets" app:mapbox_uiAttributionGravity="top|end" app:mapbox_uiLogoGravity="top|end" - app:mapbox_uiLogoMarginRight="10dp"/> + app:mapbox_uiLogoMarginRight="28dp"/> </RelativeLayout> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_building.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_building.xml new file mode 100644 index 0000000000..92d1dd5380 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_building.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> + <item + android:id="@+id/menu_action_intensity" + android:title="Change intensity" + app:showAsAction="never"/> + <item + android:id="@+id/menu_action_anchor" + android:title="Change Anchor" + app:showAsAction="never"/> +</menu> diff --git a/platform/android/build.gradle b/platform/android/build.gradle index 120c0219e4..bc90896812 100644 --- a/platform/android/build.gradle +++ b/platform/android/build.gradle @@ -12,6 +12,7 @@ buildscript { allprojects { repositories { jcenter() + maven { url "http://oss.sonatype.org/content/repositories/snapshots/" } } } diff --git a/platform/android/config.cmake b/platform/android/config.cmake index c111744ed0..84591d644b 100644 --- a/platform/android/config.cmake +++ b/platform/android/config.cmake @@ -168,6 +168,10 @@ add_library(mbgl-android STATIC platform/android/src/style/functions/identity_stops.hpp platform/android/src/style/functions/interval_stops.cpp platform/android/src/style/functions/interval_stops.hpp + platform/android/src/style/position.cpp + platform/android/src/style/position.hpp + platform/android/src/style/light.cpp + platform/android/src/style/light.hpp # FileSource holder platform/android/src/file_source.cpp diff --git a/platform/android/dependencies.gradle b/platform/android/dependencies.gradle index 738f571c09..5e80e344e4 100644 --- a/platform/android/dependencies.gradle +++ b/platform/android/dependencies.gradle @@ -7,21 +7,21 @@ ext { versionCode = 11 versionName = "5.0.0" + mapboxServicesVersion = "2.1.1" supportLibVersion = "25.3.1" - leakCanaryVersion = '1.5' wearableVersion = '2.0.0' - espressoVersion = '2.2.2' testRunnerVersion = '0.5' + leakCanaryVersion = '1.5' dep = [ // mapbox - mapboxJavaServices : 'com.mapbox.mapboxsdk:mapbox-java-services:2.1.0@jar', - mapboxJavaGeoJSON : 'com.mapbox.mapboxsdk:mapbox-java-geojson:2.1.0@jar', - mapboxAndroidTelemetry : 'com.mapbox.mapboxsdk:mapbox-android-telemetry:2.1.0@aar', + mapboxJavaServices : "com.mapbox.mapboxsdk:mapbox-java-services:${mapboxServicesVersion}@jar", + mapboxJavaGeoJSON : "com.mapbox.mapboxsdk:mapbox-java-geojson:${mapboxServicesVersion}@jar", + mapboxAndroidTelemetry : "com.mapbox.mapboxsdk:mapbox-android-telemetry:${mapboxServicesVersion}@aar", // mapzen lost - lost : 'com.mapzen.android:lost:2.2.0', + lost : 'com.mapzen.android:lost:3.0.0', // unit test junit : 'junit:junit:4.12', @@ -47,7 +47,7 @@ ext { // square crew timber : 'com.jakewharton.timber:timber:4.5.1', - okhttp3 : 'com.squareup.okhttp3:okhttp:3.6.0', + okhttp3 : 'com.squareup.okhttp3:okhttp:3.7.0', leakCanaryDebug : "com.squareup.leakcanary:leakcanary-android:${leakCanaryVersion}", leakCanaryRelease : "com.squareup.leakcanary:leakcanary-android-no-op:${leakCanaryVersion}", leakCanaryTest : "com.squareup.leakcanary:leakcanary-android-no-op:${leakCanaryVersion}" diff --git a/platform/android/gradle/wrapper/gradle-wrapper.properties b/platform/android/gradle/wrapper/gradle-wrapper.properties index c7ad166b13..1d35abd7b2 100644 --- a/platform/android/gradle/wrapper/gradle-wrapper.properties +++ b/platform/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
\ No newline at end of file diff --git a/platform/android/scripts/generate-style-code.js b/platform/android/scripts/generate-style-code.js index 5d8fc4cc6d..bb04e3ba2a 100644 --- a/platform/android/scripts/generate-style-code.js +++ b/platform/android/scripts/generate-style-code.js @@ -8,6 +8,13 @@ const _ = require('lodash'); require('../../../scripts/style-code'); // Specification parsing // +const lightProperties = Object.keys(spec[`light`]).reduce((memo, name) => { + var property = spec[`light`][name]; + property.name = name; + property['light-property'] = true; + memo.push(property); + return memo; +}, []); // Collect layer types from spec var layers = Object.keys(spec.layer.type.values).map((type) => { @@ -37,7 +44,7 @@ var layers = Object.keys(spec.layer.type.values).map((type) => { // Process all layer properties const layoutProperties = _(layers).map('layoutProperties').flatten().value(); const paintProperties = _(layers).map('paintProperties').flatten().value(); -const allProperties = _(layoutProperties).union(paintProperties).value(); +const allProperties = _(layoutProperties).union(paintProperties).union(lightProperties).value(); const enumProperties = _(allProperties).filter({'type': 'enum'}).value(); global.propertyType = function propertyType(property) { @@ -59,12 +66,31 @@ global.propertyType = function propertyType(property) { } } +global.propertyJavaType = function propertyType(property) { + switch (property.type) { + case 'boolean': + return 'boolean'; + case 'number': + return 'float'; + case 'string': + return 'String'; + case 'enum': + return 'String'; + case 'color': + return 'String'; + case 'array': + return `${propertyJavaType({type:property.value})}[]`; + default: + throw new Error(`unknown type for ${property.name}`); + } + } + global.propertyJNIType = function propertyJNIType(property) { switch (property.type) { case 'boolean': return 'jboolean'; - case 'jfloat': - return 'Float'; + case 'number': + return 'jfloat'; case 'String': return 'String'; case 'enum': @@ -93,6 +119,9 @@ global.propertyNativeType = function (property) { case 'string': return 'std::string'; case 'enum': + if(property['light-property']){ + return `Light${camelize(property.name)}Type`; + } return `${camelize(property.name)}Type`; case 'color': return `Color`; @@ -215,6 +244,53 @@ global.propertyValueDoc = function (property, value) { return doc; }; +global.isDataDriven = function (property) { + return property['property-function'] === true; +}; + +global.isLightProperty = function (property) { + return property['light-property'] === true; +}; + +global.propertyValueType = function (property) { + if (isDataDriven(property)) { + return `DataDrivenPropertyValue<${evaluatedType(property)}>`; + } else { + return `PropertyValue<${evaluatedType(property)}>`; + } +}; + +global.evaluatedType = function (property) { + if (/-translate-anchor$/.test(property.name)) { + return 'TranslateAnchorType'; + } + if (/-(rotation|pitch|illumination)-alignment$/.test(property.name)) { + return 'AlignmentType'; + } + if (/position/.test(property.name)) { + return 'Position'; + } + switch (property.type) { + case 'boolean': + return 'bool'; + case 'number': + return 'float'; + case 'string': + return 'std::string'; + case 'enum': + return (isLightProperty(property) ? 'Light' : '') + `${camelize(property.name)}Type`; + case 'color': + return `Color`; + case 'array': + if (property.length) { + return `std::array<${evaluatedType({type: property.value})}, ${property.length}>`; + } else { + return `std::vector<${evaluatedType({type: property.value})}>`; + } + default: throw new Error(`unknown type for ${property.name}`) + } +}; + global.supportsZoomFunction = function (property) { return property['zoom-function'] === true; }; @@ -225,6 +301,16 @@ global.supportsPropertyFunction = function (property) { // Template processing // +// Java + JNI Light (Peer model) +const lightHpp = ejs.compile(fs.readFileSync('platform/android/src/style/light.hpp.ejs', 'utf8'), {strict: true});; +const lightCpp = ejs.compile(fs.readFileSync('platform/android/src/style/light.cpp.ejs', 'utf8'), {strict: true});; +const lightJava = ejs.compile(fs.readFileSync('platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/light.java.ejs', 'utf8'), {strict: true}); +const lightJavaUnitTests = ejs.compile(fs.readFileSync('platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/light.junit.ejs', 'utf8'), {strict: true}); +writeIfModified(`platform/android/src/style/light.hpp`, lightHpp({properties: lightProperties})); +writeIfModified(`platform/android/src/style/light.cpp`, lightCpp({properties: lightProperties})); +writeIfModified(`platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Light.java`, lightJava({properties: lightProperties})); +writeIfModified(`platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LightTest.java`, lightJavaUnitTests({properties: lightProperties})); + // Java + JNI Layers (Peer model) const layerHpp = ejs.compile(fs.readFileSync('platform/android/src/style/layers/layer.hpp.ejs', 'utf8'), {strict: true}); const layerCpp = ejs.compile(fs.readFileSync('platform/android/src/style/layers/layer.cpp.ejs', 'utf8'), {strict: true}); @@ -238,7 +324,6 @@ for (const layer of layers) { writeIfModified(`platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/${camelize(layer.type)}LayerTest.java`, layerJavaUnitTests(layer)); } - // Java PropertyFactory const propertiesTemplate = ejs.compile(fs.readFileSync('platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/property_factory.java.ejs', 'utf8'), {strict: true}); writeIfModified( diff --git a/platform/android/scripts/release.py b/platform/android/scripts/release.py index 8abcccffdb..ce554d1362 100644 --- a/platform/android/scripts/release.py +++ b/platform/android/scripts/release.py @@ -1,5 +1,5 @@ ''' -Utility to schedule SDK builds in Bitrise. +Utility to schedule SDK builds on CircleCI. Examples: @@ -33,7 +33,7 @@ Examples: TODO: -- Add a flag to wait until the release has been built (Bitrise) and published (Maven). +- Add a flag to wait until the release has been built (CircleCI) and published (Maven). ''' @@ -49,8 +49,8 @@ ALLOWED_STAGES = ['snapshot', 'beta', 'final'] # Get the version from GRADLE_PROPERTIES_PATH below CURRENT_VERSION_TAG = 'current' -# You can find the API token in https://www.bitrise.io/app/79cdcbdc42de4303#/code -> API token -BITRISE_API_TOKEN_ENV_VAR = 'BITRISE_API_TOKEN' +# You can add your API token on https://circleci.com/account/api +CIRCLECI_API_TOKEN_ENV_VAR = 'CIRCLECI_API_TOKEN' # In the future we might want to consider alpha, or rc. ALLOWED_PRE_RELEASE = ['beta'] @@ -62,8 +62,8 @@ GRADLE_TOKEN = 'VERSION_NAME=' FABRIC_PROPERTIES_PATH = '%s/src/main/resources/fabric/com.mapbox.mapboxsdk.mapbox-android-sdk.properties' % MAPBOX_GL_ANDROID_SDK_PATH FABRIC_TOKEN = 'fabric-version=' -# Bitrise -URL_BITRISE = 'https://www.bitrise.io/app/79cdcbdc42de4303/build/start.json' +# Triggers a new build, returns a summary of the build +URL_CIRCLECI = 'https://circleci.com/api/v1.1/project/github/mapbox/mapbox-gl-native/tree/' # + :branch # We support three parameters: stage, branch, and version @click.command() @@ -129,7 +129,7 @@ def publish_snapshot(branch, version): if dirty_gradle: git_add(path=GRADLE_PROPERTIES_PATH) git_commit_and_push(branch=branch, version=version) - do_bitrise_request(build_params={'branch': branch, 'workflow_id': 'scheduled'}) + do_circleci_request(branch=branch) def publish_beta(branch, version): click.echo('Publishing beta from branch: %s (version: %s).' % (branch, version)) @@ -137,7 +137,7 @@ def publish_beta(branch, version): if dirty_gradle: git_add(path=GRADLE_PROPERTIES_PATH) git_commit_and_push(branch=branch, version=version) - do_bitrise_request(build_params={'branch': branch, 'workflow_id': 'scheduled'}) + do_circleci_request(branch=branch) def publish_final(branch, version): click.echo('Publishing final release from branch: %s (version: %s).' % (branch, version)) @@ -149,7 +149,7 @@ def publish_final(branch, version): git_add(path=FABRIC_PROPERTIES_PATH) if dirty_gradle or dirty_fabric: git_commit_and_push(branch=branch, version=version) - do_bitrise_request(build_params={'branch': branch, 'workflow_id': 'scheduled'}) + do_circleci_request(branch=branch) # # Utils @@ -166,26 +166,24 @@ def execute_call(command): abort_with_message('Command failed: %s' % command) # -# Bitrise +# CircleCI # -def get_bitrise_api_token(): - bitrise_api_token = os.environ.get(BITRISE_API_TOKEN_ENV_VAR) - if not bitrise_api_token: - abort_with_message('You need to set the BITRISE_API_TOKEN environment variable.') - click.echo('Found Bitrise API token.') - return bitrise_api_token - -def do_bitrise_request(build_params): - data = { - 'hook_info': {'type': 'bitrise', 'api_token': get_bitrise_api_token()}, - 'build_params' : build_params} - click.echo('Bitrise request data: %s' % json.dumps(data)) +def get_circleci_api_token(): + circleci_api_token = os.environ.get(CIRCLECI_API_TOKEN_ENV_VAR) + if not circleci_api_token: + abort_with_message('You need to set the CIRCLECI_API_TOKEN environment variable.') + click.echo('Found CircleCI API token.') + return circleci_api_token + +def do_circleci_request(branch): + url = URL_CIRCLECI + branch + params = {'circle-token': get_circleci_api_token()} + click.echo('CircleCI request to %s (params: %s)' % (url, json.dumps(params))) click.confirm('\nDo you want to start a build?', abort=True) - r = requests.post(URL_BITRISE, data=json.dumps(data)) - click.echo('- Bitrise response code: %s' % r.status_code) - click.echo('- Bitrise response content: %s' % r.text) + r = requests.post(url, params=params) + click.echo('- CircleCI response code: %s' % r.status_code) # # Git diff --git a/platform/android/src/jni.cpp b/platform/android/src/jni.cpp index 53691acb39..6c490fad5c 100755 --- a/platform/android/src/jni.cpp +++ b/platform/android/src/jni.cpp @@ -45,6 +45,7 @@ #include "style/functions/stop.hpp" #include "style/layers/layers.hpp" #include "style/sources/sources.hpp" +#include "style/light.hpp" namespace mbgl { namespace android { @@ -153,6 +154,8 @@ void registerNatives(JavaVM *vm) { TransitionOptions::registerNative(env); registerNativeLayers(env); registerNativeSources(env); + Light::registerNative(env); + Position::registerNative(env); Stop::registerNative(env); CategoricalStops::registerNative(env); ExponentialStops::registerNative(env); diff --git a/platform/android/src/map/camera_position.cpp b/platform/android/src/map/camera_position.cpp index aa5873b273..d6f2cb83e8 100644 --- a/platform/android/src/map/camera_position.cpp +++ b/platform/android/src/map/camera_position.cpp @@ -11,8 +11,9 @@ jni::Object<CameraPosition> CameraPosition::New(jni::JNIEnv &env, mbgl::CameraOp auto center = options.center.value(); center.wrap(); - // convert bearing, core ranges from [−π rad, π rad], android from 0 to 360 degrees - double bearing_degrees = options.angle.value_or(0) * 180.0 / M_PI; + // convert bearing, measured in radians counterclockwise from true north. + // Wrapped to [−π rad, π rad). Android binding from 0 to 360 degrees + double bearing_degrees = -options.angle.value_or(0) * util::RAD2DEG; while (bearing_degrees > 360) { bearing_degrees -= 360; } @@ -21,7 +22,7 @@ jni::Object<CameraPosition> CameraPosition::New(jni::JNIEnv &env, mbgl::CameraOp } // convert tilt, core ranges from [0 rad, 1,0472 rad], android ranges from 0 to 60 - double tilt_degrees = options.pitch.value_or(0) * 180 / M_PI; + double tilt_degrees = options.pitch.value_or(0) * util::RAD2DEG; return CameraPosition::javaClass.New(env, constructor, LatLng::New(env, center), options.zoom.value_or(0), tilt_degrees, bearing_degrees); } diff --git a/platform/android/src/native_map_view.cpp b/platform/android/src/native_map_view.cpp index 51ce9c031d..73a4f96549 100755 --- a/platform/android/src/native_map_view.cpp +++ b/platform/android/src/native_map_view.cpp @@ -46,6 +46,7 @@ #include "java/util.hpp" #include "geometry/lat_lng_bounds.hpp" #include "map/camera_position.hpp" +#include "style/light.hpp" namespace mbgl { namespace android { @@ -385,12 +386,12 @@ void NativeMapView::moveBy(jni::JNIEnv&, jni::jdouble dx, jni::jdouble dy, jni:: void NativeMapView::jumpTo(jni::JNIEnv&, jni::jdouble angle, jni::jdouble latitude, jni::jdouble longitude, jni::jdouble pitch, jni::jdouble zoom) { mbgl::CameraOptions options; if (angle != -1) { - options.angle = (-angle * M_PI) / 180; + options.angle = -angle * util::DEG2RAD; } options.center = mbgl::LatLng(latitude, longitude); options.padding = insets; if (pitch != -1) { - options.pitch = pitch * M_PI / 180; + options.pitch = pitch * util::DEG2RAD; } if (zoom != -1) { options.zoom = zoom; @@ -402,12 +403,12 @@ void NativeMapView::jumpTo(jni::JNIEnv&, jni::jdouble angle, jni::jdouble latitu void NativeMapView::easeTo(jni::JNIEnv&, jni::jdouble angle, jni::jdouble latitude, jni::jdouble longitude, jni::jlong duration, jni::jdouble pitch, jni::jdouble zoom, jni::jboolean easing) { mbgl::CameraOptions cameraOptions; if (angle != -1) { - cameraOptions.angle = (-angle * M_PI) / 180; + cameraOptions.angle = -angle * util::DEG2RAD; } cameraOptions.center = mbgl::LatLng(latitude, longitude); cameraOptions.padding = insets; if (pitch != -1) { - cameraOptions.pitch = pitch * M_PI / 180; + cameraOptions.pitch = pitch * util::DEG2RAD; } if (zoom != -1) { cameraOptions.zoom = zoom; @@ -426,12 +427,12 @@ void NativeMapView::easeTo(jni::JNIEnv&, jni::jdouble angle, jni::jdouble latitu void NativeMapView::flyTo(jni::JNIEnv&, jni::jdouble angle, jni::jdouble latitude, jni::jdouble longitude, jni::jlong duration, jni::jdouble pitch, jni::jdouble zoom) { mbgl::CameraOptions cameraOptions; if (angle != -1) { - cameraOptions.angle = (-angle * M_PI) / 180 ; + cameraOptions.angle = -angle * util::DEG2RAD; } cameraOptions.center = mbgl::LatLng(latitude, longitude); cameraOptions.padding = insets; if (pitch != -1) { - cameraOptions.pitch = pitch * M_PI / 180; + cameraOptions.pitch = pitch * util::DEG2RAD; } if (zoom != -1) { cameraOptions.zoom = zoom; @@ -819,6 +820,15 @@ jni::Array<jni::Object<geojson::Feature>> NativeMapView::queryRenderedFeaturesFo return *convert<jni::Array<jni::Object<Feature>>, std::vector<mbgl::Feature>>(env, map->queryRenderedFeatures(box, { layers, toFilter(env, jfilter) })); } +jni::Object<Light> NativeMapView::getLight(JNIEnv& env) { + mbgl::style::Light* light = map->getLight(); + if (light) { + return jni::Object<Light>(Light::createJavaLightPeer(env, *map, *light)); + } else { + return jni::Object<Light>(); + } +} + jni::Array<jni::Object<Layer>> NativeMapView::getLayers(JNIEnv& env) { // Get the core layers @@ -1535,6 +1545,7 @@ void NativeMapView::registerNative(jni::JNIEnv& env) { METHOD(&NativeMapView::queryPointAnnotations, "nativeQueryPointAnnotations"), METHOD(&NativeMapView::queryRenderedFeaturesForPoint, "nativeQueryRenderedFeaturesForPoint"), METHOD(&NativeMapView::queryRenderedFeaturesForBox, "nativeQueryRenderedFeaturesForBox"), + METHOD(&NativeMapView::getLight, "nativeGetLight"), METHOD(&NativeMapView::getLayers, "nativeGetLayers"), METHOD(&NativeMapView::getLayer, "nativeGetLayer"), METHOD(&NativeMapView::addLayer, "nativeAddLayer"), diff --git a/platform/android/src/native_map_view.hpp b/platform/android/src/native_map_view.hpp index e5fcb9badd..df43ad08b7 100755 --- a/platform/android/src/native_map_view.hpp +++ b/platform/android/src/native_map_view.hpp @@ -24,6 +24,7 @@ #include "style/sources/sources.hpp" #include "geometry/lat_lng_bounds.hpp" #include "map/camera_position.hpp" +#include "style/light.hpp" #include <exception> #include <string> @@ -230,6 +231,8 @@ public: jni::jfloat, jni::Array<jni::String>, jni::Array<jni::Object<>> jfilter); + jni::Object<Light> getLight(JNIEnv&); + jni::Array<jni::Object<Layer>> getLayers(JNIEnv&); jni::Object<Layer> getLayer(JNIEnv&, jni::String); diff --git a/platform/android/src/style/conversion/position.hpp b/platform/android/src/style/conversion/position.hpp new file mode 100644 index 0000000000..f32a892c0c --- /dev/null +++ b/platform/android/src/style/conversion/position.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include "../../conversion/conversion.hpp" + +#include <jni/jni.hpp> +#include <mbgl/style/position.hpp> +#include "../../jni/local_object.hpp" +#include "../position.hpp" + +namespace mbgl { +namespace android { +namespace conversion { + +template<> +struct Converter<jni::Object<Position>, mbgl::style::Position> { + Result<jni::Object<Position>> operator()(jni::JNIEnv &env, const mbgl::style::Position &value) const { + std::array<float, 3> cartPosition = value.getSpherical(); + return Position::fromPosition(env, cartPosition[0], cartPosition[1], cartPosition[2]); + } +}; + +template<> +struct Converter<mbgl::style::Position, jni::Object<Position>> { + Result<mbgl::style::Position> operator()(jni::JNIEnv &env, const jni::Object<Position> &value) const { + float radialCoordinate = Position::getRadialCoordinate(env, value); + float azimuthalAngle = Position::getAzimuthalAngle(env, value); + float polarAngle = Position::getPolarAngle(env, value); + std::array<float, 3> cartPosition {{radialCoordinate, azimuthalAngle, polarAngle}}; + mbgl::style::Position position{}; + position.set(cartPosition); + return position; + } +}; + +} +} +}
\ No newline at end of file diff --git a/platform/android/src/style/conversion/types.hpp b/platform/android/src/style/conversion/types.hpp index d9921e582e..a00f668c24 100644 --- a/platform/android/src/style/conversion/types.hpp +++ b/platform/android/src/style/conversion/types.hpp @@ -92,6 +92,13 @@ struct Converter<jni::jobject*, mbgl::style::CirclePitchScaleType> { } }; +template <> +struct Converter<jni::jobject*, mbgl::style::LightAnchorType> { + Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::LightAnchorType& value) const { + return convert<jni::jobject*, std::string>(env, toString(value)); + } +}; + } // namespace conversion } // namespace android diff --git a/platform/android/src/style/conversion/types_string_values.hpp b/platform/android/src/style/conversion/types_string_values.hpp index e3108fdf5b..e96de3b01e 100644 --- a/platform/android/src/style/conversion/types_string_values.hpp +++ b/platform/android/src/style/conversion/types_string_values.hpp @@ -206,6 +206,20 @@ namespace conversion { } } + // anchor + inline std::string toString(mbgl::style::LightAnchorType value) { + switch (value) { + case mbgl::style::LightAnchorType::Map: + return "map"; + break; + case mbgl::style::LightAnchorType::Viewport: + return "viewport"; + break; + default: + throw std::runtime_error("Not implemented"); + } + } + } // namespace conversion } // namespace android diff --git a/platform/android/src/style/light.cpp b/platform/android/src/style/light.cpp new file mode 100644 index 0000000000..71f1cb076e --- /dev/null +++ b/platform/android/src/style/light.cpp @@ -0,0 +1,146 @@ +// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`. + +#include <mbgl/map/map.hpp> +#include "light.hpp" +#include "conversion/transition_options.hpp" +#include "conversion/position.hpp" + +namespace mbgl { +namespace android { + +Light::Light(mbgl::Map& coreMap, mbgl::style::Light& coreLight) + : light(coreLight) , map(&coreMap) { +} + +static Light* initializeLightPeer(mbgl::Map& map, mbgl::style::Light& coreLight) { + return new Light(map, coreLight); +} + +jni::jobject* Light::createJavaLightPeer(jni::JNIEnv& env, Map& map, mbgl::style::Light& coreLight) { + std::unique_ptr<Light> peerLight = std::unique_ptr<Light>(initializeLightPeer(map, coreLight)); + jni::jobject* result = peerLight->createJavaPeer(env); + peerLight.release(); + return result; +} + +jni::Class<Light> Light::javaClass; + +jni::jobject* Light::createJavaPeer(jni::JNIEnv& env) { + static auto constructor = Light::javaClass.template GetConstructor<jni::jlong>(env); + return Light::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this)); +} + +void Light::setAnchor(jni::JNIEnv& env, jni::String property) { + std::string anchorStr = jni::Make<std::string>(env, property); + if (anchorStr.compare("map") == 0) { + light.setAnchor(LightAnchorType::Map); + } else if (anchorStr.compare("viewport") == 0) { + light.setAnchor(LightAnchorType::Viewport); + } +} + +jni::String Light::getAnchor(jni::JNIEnv& env) { + auto anchorType = light.getAnchor(); + if (anchorType == LightAnchorType::Map) { + return jni::Make<jni::String>(env, "map"); + } else { + return jni::Make<jni::String>(env, "viewport"); + } +} + +void Light::setPosition(jni::JNIEnv& env, jni::Object<Position> jposition) { + using namespace mbgl::android::conversion; + auto position = *convert<mbgl::style::Position>(env, jposition); + light.setPosition(position); +} + +jni::Object<Position> Light::getPosition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::Position position = light.getPosition().asConstant(); + return *convert<jni::Object<Position>>(env, position); +} + +jni::Object<TransitionOptions> Light::getPositionTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = light.getPositionTransition(); + return *convert<jni::Object<TransitionOptions>>(env, options); +} + +void Light::setPositionTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + light.setPositionTransition(options); +} + +void Light::setColor(jni::JNIEnv& env, jni::String property) { + auto color = Color::parse(jni::Make<std::string>(env, property)); + if (color) { + light.setColor(color.value()); + } +} + +jni::String Light::getColor(jni::JNIEnv &env) { + auto color = light.getColor().asConstant(); + return jni::Make<jni::String>(env, color.stringify()); +} + +jni::Object<TransitionOptions> Light::getColorTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = light.getColorTransition(); + return *convert<jni::Object<TransitionOptions>>(env, options); +} + +void Light::setColorTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + light.setColorTransition(options); +} + +void Light::setIntensity(jni::JNIEnv&, jni::jfloat property) { + light.setIntensity(property); +} + +jni::jfloat Light::getIntensity(jni::JNIEnv&) { + return light.getIntensity().asConstant(); +} + +jni::Object<TransitionOptions> Light::getIntensityTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = light.getIntensityTransition(); + return *convert<jni::Object<TransitionOptions>>(env, options); +} + +void Light::setIntensityTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + light.setIntensityTransition(options); +} + +void Light::registerNative(jni::JNIEnv& env) { + // Lookup the class + Light::javaClass = *jni::Class<Light>::Find(env).NewGlobalRef(env).release(); + +#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name) + // Register the peer + jni::RegisterNativePeer<Light>(env, Light::javaClass, "nativePtr", + METHOD(&Light::getAnchor, "nativeGetAnchor"), + METHOD(&Light::setAnchor, "nativeSetAnchor"), + METHOD(&Light::getPositionTransition, "nativeGetPositionTransition"), + METHOD(&Light::setPositionTransition, "nativeSetPositionTransition"), + METHOD(&Light::getPosition, "nativeGetPosition"), + METHOD(&Light::setPosition, "nativeSetPosition"), + METHOD(&Light::getColorTransition, "nativeGetColorTransition"), + METHOD(&Light::setColorTransition, "nativeSetColorTransition"), + METHOD(&Light::getColor, "nativeGetColor"), + METHOD(&Light::setColor, "nativeSetColor"), + METHOD(&Light::getIntensityTransition, "nativeGetIntensityTransition"), + METHOD(&Light::setIntensityTransition, "nativeSetIntensityTransition"), + METHOD(&Light::getIntensity, "nativeGetIntensity"), + METHOD(&Light::setIntensity, "nativeSetIntensity")); +} + +} // namespace android +} // namespace mb diff --git a/platform/android/src/style/light.cpp.ejs b/platform/android/src/style/light.cpp.ejs new file mode 100644 index 0000000000..17f0bba09d --- /dev/null +++ b/platform/android/src/style/light.cpp.ejs @@ -0,0 +1,123 @@ +<% + const properties = locals.properties; +-%> +// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`. + +#include <mbgl/map/map.hpp> +#include "light.hpp" +#include "conversion/transition_options.hpp" +#include "conversion/position.hpp" + +namespace mbgl { +namespace android { + +Light::Light(mbgl::Map& coreMap, mbgl::style::Light& coreLight) + : light(coreLight) , map(&coreMap) { +} + +static Light* initializeLightPeer(mbgl::Map& map, mbgl::style::Light& coreLight) { + return new Light(map, coreLight); +} + +jni::jobject* Light::createJavaLightPeer(jni::JNIEnv& env, Map& map, mbgl::style::Light& coreLight) { + std::unique_ptr<Light> peerLight = std::unique_ptr<Light>(initializeLightPeer(map, coreLight)); + jni::jobject* result = peerLight->createJavaPeer(env); + peerLight.release(); + return result; +} + +jni::Class<Light> Light::javaClass; + +jni::jobject* Light::createJavaPeer(jni::JNIEnv& env) { + static auto constructor = Light::javaClass.template GetConstructor<jni::jlong>(env); + return Light::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this)); +} + +<% for (const property of properties) { -%> +<% if (property.name == "position") { -%> +void Light::set<%- camelize(property.name) %>(jni::JNIEnv& env, jni::Object<<%- camelize(property.name) %>> j<%- property.name %>) { + using namespace mbgl::android::conversion; + auto position = *convert<mbgl::style::<%- camelize(property.name) %>>(env, jposition); + light.set<%- camelize(property.name) %>(<%- property.name %>); +} + +jni::Object<Position> Light::get<%- camelize(property.name) %>(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::<%- camelize(property.name) %> <%- property.name %> = light.get<%- camelize(property.name) %>().asConstant(); + return *convert<jni::Object<<%- camelize(property.name) %>>>(env, <%- property.name %>); +} +<% } else { -%> +<% if(property.name == "color") {-%> +void Light::set<%- camelize(property.name) %>(jni::JNIEnv& env, jni::<%- propertyJNIType(property) %> property) { + auto color = Color::parse(jni::Make<std::string>(env, property)); + if (color) { + light.set<%- camelize(property.name) %>(color.value()); + } +} + +jni::String Light::get<%- camelize(property.name) %>(jni::JNIEnv &env) { + auto color = light.get<%- camelize(property.name) %>().asConstant(); + return jni::Make<jni::String>(env, color.stringify()); +} +<% } else if(property.name == "anchor"){ -%> +void Light::set<%- camelize(property.name) %>(jni::JNIEnv& env, jni::<%- propertyJNIType(property) %> property) { + std::string anchorStr = jni::Make<std::string>(env, property); + if (anchorStr.compare("map") == 0) { + light.setAnchor(LightAnchorType::Map); + } else if (anchorStr.compare("viewport") == 0) { + light.setAnchor(LightAnchorType::Viewport); + } +} + +jni::String Light::getAnchor(jni::JNIEnv& env) { + auto anchorType = light.getAnchor(); + if (anchorType == LightAnchorType::Map) { + return jni::Make<jni::String>(env, "map"); + } else { + return jni::Make<jni::String>(env, "viewport"); + } +} +<% } else { -%> +void Light::set<%- camelize(property.name) %>(jni::JNIEnv&, jni::<%- propertyJNIType(property) %> property) { + light.set<%- camelize(property.name) %>(property); +} + +jni::<%- propertyJNIType(property) %> Light::get<%- camelize(property.name) %>(jni::JNIEnv&) { + return light.get<%- camelize(property.name) %>().asConstant(); +} +<% } -%> +<% } -%> + +<% if (property.transition) { -%> +jni::Object<TransitionOptions> Light::get<%- camelize(property.name) %>Transition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = light.get<%- camelize(property.name) %>Transition(); + return *convert<jni::Object<TransitionOptions>>(env, options); +} + +void Light::set<%- camelize(property.name) %>Transition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + light.set<%- camelize(property.name) %>Transition(options); +} + +<% } -%> +<% } -%> +void Light::registerNative(jni::JNIEnv& env) { + // Lookup the class + Light::javaClass = *jni::Class<Light>::Find(env).NewGlobalRef(env).release(); + +#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name) + // Register the peer + jni::RegisterNativePeer<Light>(env, Light::javaClass, "nativePtr",<% for(var i = 0; i < properties.length; i++) {%> +<% if (properties[i].transition) { -%> + METHOD(&Light::get<%- camelize(properties[i].name) %>Transition, "nativeGet<%- camelize(properties[i].name) %>Transition"), + METHOD(&Light::set<%- camelize(properties[i].name) %>Transition, "nativeSet<%- camelize(properties[i].name) %>Transition"), +<% } -%> + METHOD(&Light::get<%- camelize(properties[i].name) %>, "nativeGet<%- camelize(properties[i].name) %>"), + METHOD(&Light::set<%- camelize(properties[i].name) %>, "nativeSet<%- camelize(properties[i].name) %>")<% if(i != (properties.length -1)) {-%>,<% } -%><% } -%>); +} + +} // namespace android +} // namespace mb diff --git a/platform/android/src/style/light.hpp b/platform/android/src/style/light.hpp new file mode 100644 index 0000000000..2c314067be --- /dev/null +++ b/platform/android/src/style/light.hpp @@ -0,0 +1,57 @@ +// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`. + +#pragma once + +#include <mbgl/util/noncopyable.hpp> + +#include <jni/jni.hpp> +#include <mbgl/style/light.hpp> +#include "transition_options.hpp" +#include "position.hpp" +#include <mbgl/style/types.hpp> +#include <mbgl/style/property_value.hpp> + +namespace mbgl { +namespace android { + +using namespace style; + +class Light : private mbgl::util::noncopyable { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/light/Light"; }; + + static jni::Class<Light> javaClass; + + static void registerNative(jni::JNIEnv&); + + static jni::jobject* createJavaLightPeer(jni::JNIEnv&, mbgl::Map&, mbgl::style::Light&); + + Light(mbgl::Map&, mbgl::style::Light&); + + void setAnchor(jni::JNIEnv&, jni::String); + jni::String getAnchor(jni::JNIEnv&); + void setPosition(jni::JNIEnv&, jni::Object<Position>); + jni::Object<Position> getPosition(jni::JNIEnv&); + void setPositionTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Object<TransitionOptions> getPositionTransition(jni::JNIEnv&); + void setColor(jni::JNIEnv&, jni::String); + jni::String getColor(jni::JNIEnv&); + void setColorTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Object<TransitionOptions> getColorTransition(jni::JNIEnv&); + void setIntensity(jni::JNIEnv&, jni::jfloat); + jni::jfloat getIntensity(jni::JNIEnv&); + void setIntensityTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Object<TransitionOptions> getIntensityTransition(jni::JNIEnv&); + jni::jobject* createJavaPeer(jni::JNIEnv&); + +protected: + + // Raw reference to the light + mbgl::style::Light& light; + + // Map is set when the light is retrieved + mbgl::Map* map; +}; +} // namespace android +} // namespace mbgl
\ No newline at end of file diff --git a/platform/android/src/style/light.hpp.ejs b/platform/android/src/style/light.hpp.ejs new file mode 100644 index 0000000000..18f961b9e0 --- /dev/null +++ b/platform/android/src/style/light.hpp.ejs @@ -0,0 +1,59 @@ +<% + const properties = locals.properties; +-%> +// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`. + +#pragma once + +#include <mbgl/util/noncopyable.hpp> + +#include <jni/jni.hpp> +#include <mbgl/style/light.hpp> +#include "transition_options.hpp" +#include "position.hpp" +#include <mbgl/style/types.hpp> +#include <mbgl/style/property_value.hpp> + +namespace mbgl { +namespace android { + +using namespace style; + +class Light : private mbgl::util::noncopyable { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/light/Light"; }; + + static jni::Class<Light> javaClass; + + static void registerNative(jni::JNIEnv&); + + static jni::jobject* createJavaLightPeer(jni::JNIEnv&, mbgl::Map&, mbgl::style::Light&); + + Light(mbgl::Map&, mbgl::style::Light&); + +<% for (const property of properties) { -%> +<% if (property.name=="position") {-%> + void set<%- camelize(property.name) %>(jni::JNIEnv&, jni::Object<Position>); + jni::Object<<%- camelize(property.name) %>> get<%- camelize(property.name) %>(jni::JNIEnv&); +<% } else { -%> + void set<%- camelize(property.name) %>(jni::JNIEnv&, jni::<%- propertyJNIType(property) %>); + jni::<%- propertyJNIType(property) %> get<%- camelize(property.name) %>(jni::JNIEnv&); +<% } -%> +<% if (property.transition) { -%> + void set<%- camelize(property.name) %>Transition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Object<TransitionOptions> get<%- camelize(property.name) %>Transition(jni::JNIEnv&); +<% } -%> +<% } -%> + jni::jobject* createJavaPeer(jni::JNIEnv&); + +protected: + + // Raw reference to the light + mbgl::style::Light& light; + + // Map is set when the light is retrieved + mbgl::Map* map; +}; +} // namespace android +} // namespace mbgl
\ No newline at end of file diff --git a/platform/android/src/style/position.cpp b/platform/android/src/style/position.cpp new file mode 100644 index 0000000000..0bbcefcbcd --- /dev/null +++ b/platform/android/src/style/position.cpp @@ -0,0 +1,34 @@ +#include "position.hpp" + +namespace mbgl { +namespace android { + +jni::Object<Position> Position::fromPosition(jni::JNIEnv& env, jfloat radialCoordinate, jfloat azimuthalAngle, jfloat polarAngle) { + static auto method = Position::javaClass.GetStaticMethod<jni::Object<Position> (jfloat, jfloat, jfloat)>(env, "fromPosition"); + return Position::javaClass.Call(env, method, radialCoordinate, azimuthalAngle, polarAngle); +} + +void Position::registerNative(jni::JNIEnv& env) { + // Lookup the class + Position::javaClass = *jni::Class<Position>::Find(env).NewGlobalRef(env).release(); +} + +jni::Class<Position> Position::javaClass; + +float Position::getRadialCoordinate(jni::JNIEnv& env, jni::Object<Position> position){ + static auto field = Position::javaClass.GetField<jfloat>(env, "radialCoordinate"); + return position.Get(env, field); +} + +float Position::getAzimuthalAngle(jni::JNIEnv& env, jni::Object<Position> position){ + static auto field = Position::javaClass.GetField<jfloat>(env, "azimuthalAngle"); + return position.Get(env, field); +} + +float Position::getPolarAngle(jni::JNIEnv& env, jni::Object<Position> position){ + static auto field = Position::javaClass.GetField<jfloat>(env, "polarAngle"); + return position.Get(env, field); +} + +} // namespace andr[oid +} // namespace mbgl
\ No newline at end of file diff --git a/platform/android/src/style/position.hpp b/platform/android/src/style/position.hpp new file mode 100644 index 0000000000..4aafa853db --- /dev/null +++ b/platform/android/src/style/position.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include <mbgl/util/noncopyable.hpp> + +#include <jni/jni.hpp> + +namespace mbgl { +namespace android { + +class Position : private mbgl::util::noncopyable { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/light/Position"; }; + + static jni::Object<Position> fromPosition(jni::JNIEnv&, jfloat, jfloat, jfloat); + + static jni::Class<Position> javaClass; + + static void registerNative(jni::JNIEnv&); + + static float getRadialCoordinate(jni::JNIEnv&, jni::Object<Position>); + static float getAzimuthalAngle(jni::JNIEnv&, jni::Object<Position>); + static float getPolarAngle(jni::JNIEnv&, jni::Object<Position>); + +}; + + +} // namespace android +} // namespace mbgl
\ No newline at end of file diff --git a/platform/darwin/docs/guides/Tile URL Templates.md.ejs b/platform/darwin/docs/guides/Tile URL Templates.md.ejs new file mode 100644 index 0000000000..78fb297138 --- /dev/null +++ b/platform/darwin/docs/guides/Tile URL Templates.md.ejs @@ -0,0 +1,109 @@ +<% + const os = locals.os; + const iOS = os === 'iOS'; + //const macOS = os === 'macOS'; + //const cocoaPrefix = iOS ? 'UI' : 'NS'; +-%> +<!-- + This file is generated. + Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. +--> +# Tile URL Templates + +`MGLTileSource` objects, specifically `MGLRasterSource` and `MGLVectorSource` +objects, can be created using an initializer that accepts an array of tile URL +templates. Tile URL templates are strings that specify the URLs of the vector +tiles or raster tile images to load. A template resembles an absolute URL, but +with any number of placeholder strings that the source evaluates based on the +tile it needs to load. For example: + +* `http://www.example.com/tiles/{z}/{x}/{y}.pbf` could be + evaluated as `http://www.example.com/tiles/14/6/9.pbf`. +* `http://www.example.com/tiles/{z}/{x}/{y}{ratio}.png` could be + evaluated as `http://www.example.com/tiles/14/6/9@2x.png`. + +Tile URL templates are also used to define tilesets in TileJSON manifests or +[`raster`](https://www.mapbox.com/mapbox-gl-js/style-spec/#sources-raster-tiles) +and +[`vector`](https://www.mapbox.com/mapbox-gl-js/style-spec/#sources-vector-tiles) +sources in style JSON files. See the +[TileJSON specification](https://github.com/mapbox/tilejson-spec/tree/master/2.2.0) +for information about tile URL templates in the context of a TileJSON or style +JSON file. + +Tile sources support the following placeholder strings in tile URL templates, +all of which are optional: + +<table> +<thead> +<tr><th>Placeholder string</th><th>Description</th></tr> +</thead> +<tbody> +<tr> + <td><code>{x}</code></td> + <td>The index of the tile along the map’s x axis according to Spherical + Mercator projection. If the value is 0, the tile’s left edge corresponds + to the 180th meridian west. If the value is 2<sup><var>z</var></sup>−1, + the tile’s right edge corresponds to the 180th meridian east.</td> +</tr> +<tr> + <td><code>{y}</code></td> + <td>The index of the tile along the map’s y axis according to Spherical + Mercator projection. If the value is 0, the tile’s tile edge corresponds + to arctan(sinh(π)), or approximately 85.0511 degrees north. If the value + is 2<sup><var>z</var></sup>−1, the tile’s bottom edge corresponds to + −arctan(sinh(π)), or approximately 85.0511 degrees south. The y axis is + inverted if the <code>options</code> parameter contains + <code>MGLTileSourceOptionTileCoordinateSystem</code> with a value of + <code>MGLTileCoordinateSystemTMS</code>.</td> +</tr> +<tr> + <td><code>{z}</code></td> + <td>The tile’s zoom level. At zoom level 0, each tile covers the entire + world map; at zoom level 1, it covers ¼ of the world; at zoom level 2, + <sup>1</sup>⁄<sub>16</sub> of the world, and so on. For tiles loaded by + a <code>MGLRasterSource</code> object, whether the tile zoom level + matches the map’s current zoom level depends on the value of the + source’s tile size as specified in the + <code>MGLTileSourceOptionTileSize</code> key of the <code>options</code> + parameter.</td> +</tr> +<tr> + <td><code>{bbox-epsg-3857}</code></td> + <td>The tile’s bounding box, expressed as a comma-separated list of the + tile’s western, southern, eastern, and northern extents according to + Spherical Mercator (EPSG:3857) projection. The bounding box is typically + used with map services conforming to the + <a href="http://www.opengeospatial.org/standards/wms">Web Map Service</a> + protocol.</td> +</tr> +<tr> + <td><code>{quadkey}</code></td> + <td>A quadkey indicating both the tile’s location and its zoom level. The + quadkey is typically used with + <a href="https://msdn.microsoft.com/en-us/library/bb259689.aspx">Bing Maps</a>. + </td> +</tr> +<tr> + <td><code>{ratio}</code></td> + <td>A suffix indicating the resolution of the tile image. The suffix is the + empty string for standard resolution displays and <code>@2x</code> for +<% if (iOS) { -%> + Retina displays, including displays for which <code>UIScreen.scale</code> + is 3. +<% } else { -%> + Retina displays. +<% } -%> + </td> +</tr> +<tr> + <td><code>{prefix}</code></td> + <td>Two hexadecimal digits chosen such that each visible tile has a + different prefix. The prefix is typically used for domain sharding.</td> +</tr> +</tbody> +</table> + +For more information about the `{x}`, `{y}`, and `{z}` placeholder strings, +consult the +[OpenStreetMap Wiki](https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames). diff --git a/platform/darwin/scripts/generate-style-code.js b/platform/darwin/scripts/generate-style-code.js index 330a8f1d03..7efc8d441c 100644 --- a/platform/darwin/scripts/generate-style-code.js +++ b/platform/darwin/scripts/generate-style-code.js @@ -522,8 +522,9 @@ global.setSourceLayer = function() { const layerH = ejs.compile(fs.readFileSync('platform/darwin/src/MGLStyleLayer.h.ejs', 'utf8'), { strict: true }); const layerM = ejs.compile(fs.readFileSync('platform/darwin/src/MGLStyleLayer.mm.ejs', 'utf8'), { strict: true}); const testLayers = ejs.compile(fs.readFileSync('platform/darwin/test/MGLStyleLayerTests.mm.ejs', 'utf8'), { strict: true}); -const guideMD = ejs.compile(fs.readFileSync('platform/darwin/docs/guides/For Style Authors.md.ejs', 'utf8'), { strict: true }); +const forStyleAuthorsMD = ejs.compile(fs.readFileSync('platform/darwin/docs/guides/For Style Authors.md.ejs', 'utf8'), { strict: true }); const ddsGuideMD = ejs.compile(fs.readFileSync('platform/darwin/docs/guides/Using Style Functions at Runtime.md.ejs', 'utf8'), { strict: true }); +const templatesMD = ejs.compile(fs.readFileSync('platform/darwin/docs/guides/Tile URL Templates.md.ejs', 'utf8'), { strict: true }); const layers = _(spec.layer.type.values).map((value, layerType) => { const layoutProperties = Object.keys(spec[`layout_${layerType}`]).reduce((memo, name) => { @@ -635,12 +636,12 @@ global.guideExample = function (guide, exampleId, os) { return '```swift\n' + example + '\n```'; }; -fs.writeFileSync(`platform/ios/docs/guides/For Style Authors.md`, guideMD({ +fs.writeFileSync(`platform/ios/docs/guides/For Style Authors.md`, forStyleAuthorsMD({ os: 'iOS', renamedProperties: renamedPropertiesByLayerType, layers: layers, })); -fs.writeFileSync(`platform/macos/docs/guides/For Style Authors.md`, guideMD({ +fs.writeFileSync(`platform/macos/docs/guides/For Style Authors.md`, forStyleAuthorsMD({ os: 'macOS', renamedProperties: renamedPropertiesByLayerType, layers: layers, @@ -651,3 +652,9 @@ fs.writeFileSync(`platform/ios/docs/guides/Using Style Functions at Runtime.md`, fs.writeFileSync(`platform/macos/docs/guides/Using Style Functions at Runtime.md`, ddsGuideMD({ os: 'macOS', })); +fs.writeFileSync(`platform/ios/docs/guides/Tile URL Templates.md`, templatesMD({ + os: 'iOS', +})); +fs.writeFileSync(`platform/macos/docs/guides/Tile URL Templates.md`, templatesMD({ + os: 'macOS', +})); diff --git a/platform/darwin/src/MGLAttributionInfo.mm b/platform/darwin/src/MGLAttributionInfo.mm index 7583244491..770aeb25e7 100644 --- a/platform/darwin/src/MGLAttributionInfo.mm +++ b/platform/darwin/src/MGLAttributionInfo.mm @@ -6,8 +6,10 @@ #import <Cocoa/Cocoa.h> #endif +#import "MGLAccountManager.h" #import "MGLMapCamera.h" #import "NSArray+MGLAdditions.h" +#import "NSBundle+MGLAdditions.h" #import "NSString+MGLAdditions.h" #include <string> @@ -126,13 +128,34 @@ } - (nullable NSURL *)feedbackURLAtCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate zoomLevel:(double)zoomLevel { + return [self feedbackURLForStyleURL:nil atCenterCoordinate:centerCoordinate zoomLevel:zoomLevel direction:0 pitch:0]; +} + +- (nullable NSURL *)feedbackURLForStyleURL:(nullable NSURL *)styleURL atCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate zoomLevel:(double)zoomLevel direction:(CLLocationDirection)direction pitch:(CGFloat)pitch { if (!self.feedbackLink) { return nil; } - - NSURLComponents *components = [NSURLComponents componentsWithURL:self.URL resolvingAgainstBaseURL:NO]; - components.fragment = [NSString stringWithFormat:@"/%.5f/%.5f/%i", - centerCoordinate.longitude, centerCoordinate.latitude, (int)round(zoomLevel + 1)]; + + NSURLComponents *components = [NSURLComponents componentsWithString:@"https://www.mapbox.com/feedback/"]; + components.fragment = [NSString stringWithFormat:@"/%.5f/%.5f/%.2f/%.1f/%i", + centerCoordinate.longitude, centerCoordinate.latitude, zoomLevel, + direction, (int)round(pitch)]; + + NSURLQueryItem *referrerQueryItem = [NSURLQueryItem queryItemWithName:@"referrer" + value:[NSBundle mgl_applicationBundleIdentifier]]; + NSMutableArray<NSURLQueryItem *> *queryItems = [NSMutableArray arrayWithObject:referrerQueryItem]; + if ([styleURL.scheme isEqualToString:@"mapbox"] && [styleURL.host isEqualToString:@"styles"]) { + NSArray<NSString *> *stylePathComponents = styleURL.pathComponents; + if (stylePathComponents.count >= 3) { + [queryItems addObjectsFromArray:@[ + [NSURLQueryItem queryItemWithName:@"owner" value:stylePathComponents[1]], + [NSURLQueryItem queryItemWithName:@"id" value:stylePathComponents[2]], + [NSURLQueryItem queryItemWithName:@"access_token" value:[MGLAccountManager accessToken]], + ]]; + } + } + components.queryItems = queryItems; + return components.URL; } diff --git a/platform/darwin/src/MGLAttributionInfo_Private.h b/platform/darwin/src/MGLAttributionInfo_Private.h index 08bc6bfc4d..c639752ac3 100644 --- a/platform/darwin/src/MGLAttributionInfo_Private.h +++ b/platform/darwin/src/MGLAttributionInfo_Private.h @@ -20,6 +20,24 @@ NS_ASSUME_NONNULL_BEGIN + (NSAttributedString *)attributedStringForAttributionInfos:(NS_ARRAY_OF(MGLAttributionInfo *) *)attributionInfos; +/** + Returns a copy of the `URL` property modified to account for the given style + URL, center coordinate, and zoom level. + + @param styleURL The map’s style URL. + @param centerCoordinate The map’s center coordinate. + @param zoomLevel The map’s zoom level. See the `MGLMapView.zoomLevel` property + for more information. + @param direction The heading of the map, measured in degrees clockwise from + true north. + @param pitch Pitch toward the horizon measured in degrees, with 0 degrees + resulting in a two-dimensional map. + @return A modified URL containing a fragment that points to the specified + viewport. If the `feedbackLink` property is set to `NO`, this method returns + `nil`. + */ +- (nullable NSURL *)feedbackURLForStyleURL:(nullable NSURL *)styleURL atCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate zoomLevel:(double)zoomLevel direction:(CLLocationDirection)direction pitch:(CGFloat)pitch; + @end @interface NSMutableArray (MGLAttributionInfoAdditions) diff --git a/platform/darwin/src/MGLFillExtrusionStyleLayer.h b/platform/darwin/src/MGLFillExtrusionStyleLayer.h index 84f6bedde4..c4fb9aa77e 100644 --- a/platform/darwin/src/MGLFillExtrusionStyleLayer.h +++ b/platform/darwin/src/MGLFillExtrusionStyleLayer.h @@ -46,7 +46,7 @@ typedef NS_ENUM(NSUInteger, MGLFillExtrusionTranslationAnchor) { layer.sourceLayerIdentifier = "building" layer.fillExtrusionHeight = MGLStyleValue(interpolationMode: .identity, sourceStops: nil, attributeName: "height", options: nil) layer.fillExtrusionBase = MGLStyleValue(interpolationMode: .identity, sourceStops: nil, attributeName: "min_height", options: nil) - layer.predicate = NSPredicate(format: "extrude == TRUE") + layer.predicate = NSPredicate(format: "extrude == 'true'") mapView.style?.addLayer(layer) ``` */ diff --git a/platform/darwin/src/MGLGeometry_Private.h b/platform/darwin/src/MGLGeometry_Private.h index 3e029e8ee0..7ad8314a79 100644 --- a/platform/darwin/src/MGLGeometry_Private.h +++ b/platform/darwin/src/MGLGeometry_Private.h @@ -8,6 +8,26 @@ #import <mbgl/util/geo.hpp> #import <mbgl/util/geometry.hpp> +typedef double MGLLocationRadians; +typedef double MGLRadianDistance; +typedef double MGLRadianDirection; + +/** Defines the coordinate by a `MGLRadianCoordinate2D`. */ +typedef struct MGLRadianCoordinate2D { + MGLLocationRadians latitude; + MGLLocationRadians longitude; +} MGLRadianCoordinate2D; + +/** + Creates a new `MGLRadianCoordinate2D` from the given latitudinal and longitudinal. + */ +NS_INLINE MGLRadianCoordinate2D MGLRadianCoordinate2DMake(MGLLocationRadians latitude, MGLLocationRadians longitude) { + MGLRadianCoordinate2D radianCoordinate; + radianCoordinate.latitude = latitude; + radianCoordinate.longitude = longitude; + return radianCoordinate; +} + /// Returns the smallest rectangle that contains both the given rectangle and /// the given point. CGRect MGLExtendRect(CGRect rect, CGPoint point); @@ -18,6 +38,10 @@ NS_INLINE mbgl::Point<double> MGLPointFromLocationCoordinate2D(CLLocationCoordin return mbgl::Point<double>(coordinate.longitude, coordinate.latitude); } +NS_INLINE CLLocationCoordinate2D MGLLocationCoordinate2DFromPoint(mbgl::Point<double> point) { + return CLLocationCoordinate2DMake(point.y, point.x); +} + NS_INLINE CLLocationCoordinate2D MGLLocationCoordinate2DFromLatLng(mbgl::LatLng latLng) { return CLLocationCoordinate2DMake(latLng.latitude(), latLng.longitude()); } @@ -59,3 +83,43 @@ CLLocationDistance MGLAltitudeForZoomLevel(double zoomLevel, CGFloat pitch, CLLo @param size The size of the viewport. @return A zero-based zoom level. */ double MGLZoomLevelForAltitude(CLLocationDistance altitude, CGFloat pitch, CLLocationDegrees latitude, CGSize size); + +/** Returns MGLRadianCoordinate2D, converted from CLLocationCoordinate2D. */ +NS_INLINE MGLRadianCoordinate2D MGLRadianCoordinateFromLocationCoordinate(CLLocationCoordinate2D locationCoordinate) { + return MGLRadianCoordinate2DMake(MGLRadiansFromDegrees(locationCoordinate.latitude), + MGLRadiansFromDegrees(locationCoordinate.longitude)); +} + +/* + Returns the distance in radians given two coordinates. + */ +NS_INLINE MGLRadianDistance MGLDistanceBetweenRadianCoordinates(MGLRadianCoordinate2D from, MGLRadianCoordinate2D to) +{ + double a = pow(sin((to.latitude - from.latitude) / 2), 2) + + pow(sin((to.longitude - from.longitude) / 2), 2) * cos(from.latitude) * cos(to.latitude); + + return 2 * atan2(sqrt(a), sqrt(1 - a)); +} + +/* + Returns direction in radians given two coordinates. + */ +NS_INLINE MGLRadianDirection MGLRadianCoordinatesDirection(MGLRadianCoordinate2D from, MGLRadianCoordinate2D to) { + double a = sin(to.longitude - from.longitude) * cos(to.latitude); + double b = cos(from.latitude) * sin(to.latitude) + - sin(from.latitude) * cos(to.latitude) * cos(to.longitude - from.longitude); + return atan2(a, b); +} + +/* + Returns coordinate at a given distance and direction away from coordinate. + */ +NS_INLINE MGLRadianCoordinate2D MGLRadianCoordinateAtDistanceFacingDirection(MGLRadianCoordinate2D coordinate, + MGLRadianDistance distance, + MGLRadianDirection direction) { + double otherLatitude = asin(sin(coordinate.latitude) * cos(distance) + + cos(coordinate.latitude) * sin(distance) * cos(direction)); + double otherLongitude = coordinate.longitude + atan2(sin(direction) * sin(distance) * cos(coordinate.latitude), + cos(distance) - sin(coordinate.latitude) * sin(otherLatitude)); + return MGLRadianCoordinate2DMake(otherLatitude, otherLongitude); +} diff --git a/platform/darwin/src/MGLLight.h b/platform/darwin/src/MGLLight.h new file mode 100644 index 0000000000..ef9811bd33 --- /dev/null +++ b/platform/darwin/src/MGLLight.h @@ -0,0 +1,125 @@ +#import <CoreLocation/CoreLocation.h> + +#import "MGLFoundation.h" +#import "MGLStyleValue.h" + +NS_ASSUME_NONNULL_BEGIN + + +/** Options to specify extruded geometries are lit relative to the map or viewport. */ +typedef NS_ENUM(NSUInteger, MGLLightAnchor) { + /** The position of the light source is aligned to the rotation of the map. */ + MGLLightAnchorMap, + /** The position of the light source is aligned to the rotation of the viewport. */ + MGLLightAnchorViewport +}; + +/** + A structure containing information about the position of the light source + relative to lit geometries. + */ +typedef struct MGLSphericalPosition { + /** Distance from the center of the base of an object to its light. */ + CLLocationDistance radial; + /** Position of the light relative to 0° (0° when `MGLLight.anchor` is set to viewport corresponds + to the top of the viewport, or 0° when `MGLLight.anchor` is set to map corresponds to due north, + and degrees proceed clockwise). */ + CLLocationDirection azimuthal; + /** Indicates the height of the light (from 0°, directly above, to 180°, directly below). */ + CLLocationDirection polar; +} MGLSphericalPosition; + +/** + Creates a new `MGLSphericalPosition` from the given radial, azimuthal, polar. + + @param radial The radial coordinate. + @param azimuthal The azimuthal angle. + @param polar The polar angle. + + @return Returns a `MGLSphericalPosition` struct containing the position attributes. + */ +NS_INLINE MGLSphericalPosition MGLSphericalPositionMake(CLLocationDistance radial, CLLocationDirection azimuthal, CLLocationDirection polar) { + MGLSphericalPosition position; + position.radial = radial; + position.azimuthal = azimuthal; + position.polar = polar; + + return position; +} + +/** + An `MGLLight` object represents the light source for extruded geometries in `MGLStyle`. + */ +MGL_EXPORT +@interface MGLLight : NSObject + +/** + `anchor` Whether extruded geometries are lit relative to the map or viewport. + + This property corresponds to the <a + href="https://www.mapbox.com/mapbox-gl-js/style-spec/#light-anchor"><code>anchor</code></a> + light property in the Mapbox Style Specification. + */ +@property (nonatomic) MGLStyleValue<NSValue *> *anchor; + +/** + Values describing animated transitions to `anchor` property. + */ +@property (nonatomic) MGLTransition anchorTransition; + + +/** + Position of the light source relative to lit (extruded) geometries. + + This property corresponds to the <a + href="https://www.mapbox.com/mapbox-gl-js/style-spec/#light-position"><code>position</code></a> + light property in the Mapbox Style Specification. + */ +@property (nonatomic) MGLStyleValue<NSValue *> *position; + +/** + Values describing animated transitions to `position` property. + */ +@property (nonatomic) MGLTransition positionTransiton; + + +#if TARGET_OS_IPHONE +/** + Color tint for lighting extruded geometries. + + This property corresponds to the <a + href="https://www.mapbox.com/mapbox-gl-js/style-spec/#light-color"><code>color</code></a> + light property in the Mapbox Style Specification. + */ +@property (nonatomic) MGLStyleValue<UIColor *> *color; +#else + +/** + Color tint for lighting extruded geometries. + */ +@property (nonatomic) MGLStyleValue<NSColor *> *color; +#endif + +/** + Values describing animated transitions to `color` property. + */ +@property (nonatomic) MGLTransition colorTransiton; + + +/** + Intensity of lighting (on a scale from 0 to 1). Higher numbers will present as more extreme contrast. + + This property corresponds to the <a + href="https://www.mapbox.com/mapbox-gl-js/style-spec/#light-intensity"><code>intensity</code></a> + light property in the Mapbox Style Specification. + */ +@property(nonatomic) MGLStyleValue<NSNumber *> *intensity; + +/** + Values describing animated transitions to `intensity` property. + */ +@property (nonatomic) MGLTransition intensityTransition; + +@end + +NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/MGLLight.mm b/platform/darwin/src/MGLLight.mm new file mode 100644 index 0000000000..262fad3b07 --- /dev/null +++ b/platform/darwin/src/MGLLight.mm @@ -0,0 +1,113 @@ +#import "MGLLight.h" + +#import "MGLTypes.h" +#import "NSDate+MGLAdditions.h" +#import "MGLStyleValue_Private.h" +#import "NSValue+MGLAdditions.h" + +#import <mbgl/style/light.hpp> +#import <mbgl/style/types.hpp> + +namespace mbgl { + + MBGL_DEFINE_ENUM(MGLLightAnchor, { + { MGLLightAnchorMap, "map" }, + { MGLLightAnchorViewport, "viewport" }, + }); + +} + +NS_INLINE MGLTransition MGLTransitionFromOptions(const mbgl::style::TransitionOptions& options) { + MGLTransition transition; + transition.duration = MGLTimeIntervalFromDuration(options.duration.value_or(mbgl::Duration::zero())); + transition.delay = MGLTimeIntervalFromDuration(options.delay.value_or(mbgl::Duration::zero())); + + return transition; +} + +NS_INLINE mbgl::style::TransitionOptions MGLOptionsFromTransition(MGLTransition transition) { + mbgl::style::TransitionOptions options { { MGLDurationFromTimeInterval(transition.duration) }, { MGLDurationFromTimeInterval(transition.delay) } }; + return options; +} + +@interface MGLLight() + +@end + +@implementation MGLLight + +- (instancetype)initWithMBGLLight:(const mbgl::style::Light *)mbglLight +{ + if (self = [super init]) { + auto anchor = mbglLight->getAnchor(); + MGLStyleValue<NSValue *> *anchorStyleValue; + if (anchor.isUndefined()) { + mbgl::style::PropertyValue<mbgl::style::LightAnchorType> defaultAnchor = mbglLight->getDefaultAnchor(); + anchorStyleValue = MGLStyleValueTransformer<mbgl::style::LightAnchorType, NSValue *, mbgl::style::LightAnchorType, MGLLightAnchor>().toEnumStyleValue(defaultAnchor); + } else { + anchorStyleValue = MGLStyleValueTransformer<mbgl::style::LightAnchorType, NSValue *, mbgl::style::LightAnchorType, MGLLightAnchor>().toEnumStyleValue(anchor); + } + + _anchor = anchorStyleValue; + + _anchorTransition = MGLTransitionFromOptions(mbglLight->getAnchorTransition()); + + auto positionValue = mbglLight->getPosition(); + if (positionValue.isUndefined()) { + _position = MGLStyleValueTransformer<mbgl::style::Position, NSValue *>().toStyleValue(mbglLight->getDefaultPosition()); + } else { + _position = MGLStyleValueTransformer<mbgl::style::Position, NSValue *>().toStyleValue(positionValue); + } + + _positionTransiton = MGLTransitionFromOptions(mbglLight->getPositionTransition()); + + auto colorValue = mbglLight->getColor(); + if (colorValue.isUndefined()) { + _color = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toStyleValue(mbglLight->getDefaultColor()); + } else { + _color = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toStyleValue(colorValue); + } + + _colorTransiton = MGLTransitionFromOptions(mbglLight->getColorTransition()); + + auto intensityValue = mbglLight->getIntensity(); + if (intensityValue.isUndefined()) { + _intensity = MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(mbglLight->getDefaultIntensity()); + } else { + _intensity = MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(intensityValue); + } + + _intensityTransition = MGLTransitionFromOptions(mbglLight->getIntensityTransition()); + } + + return self; +} + +- (mbgl::style::Light)mbglLight +{ + mbgl::style::Light mbglLight; + + auto anchor = MGLStyleValueTransformer<mbgl::style::LightAnchorType, NSValue *, mbgl::style::LightAnchorType, MGLLightAnchor>().toEnumPropertyValue(self.anchor); + mbglLight.setAnchor(anchor); + + mbglLight.setAnchorTransition(MGLOptionsFromTransition(self.anchorTransition)); + + auto position = MGLStyleValueTransformer<mbgl::style::Position, NSValue *>().toInterpolatablePropertyValue(self.position); + mbglLight.setPosition(position); + + mbglLight.setPositionTransition(MGLOptionsFromTransition(self.positionTransiton)); + + auto color = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toInterpolatablePropertyValue(self.color); + mbglLight.setColor(color); + + mbglLight.setColorTransition(MGLOptionsFromTransition(self.colorTransiton)); + + auto intensity = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(self.intensity); + mbglLight.setIntensity(intensity); + + mbglLight.setIntensityTransition(MGLOptionsFromTransition(self.intensityTransition)); + + return mbglLight; +} + +@end diff --git a/platform/darwin/src/MGLLight_Private.h b/platform/darwin/src/MGLLight_Private.h new file mode 100644 index 0000000000..dbc29c1eff --- /dev/null +++ b/platform/darwin/src/MGLLight_Private.h @@ -0,0 +1,23 @@ +#import <Foundation/Foundation.h> + +#import "MGLLight.h" + +namespace mbgl { + namespace style { + class Light; + } +} + +@interface MGLLight (Private) + +/** + Initializes and returns a `MGLLight` associated with a style's light. + */ +- (instancetype)initWithMBGLLight:(const mbgl::style::Light *)mbglLight; + +/** + Returns an `mbgl::style::Light` representation of the `MGLLight`. + */ +- (mbgl::style::Light)mbglLight; + +@end diff --git a/platform/darwin/src/MGLOfflineStorage.mm b/platform/darwin/src/MGLOfflineStorage.mm index 195ef3c36a..81774ad3cb 100644 --- a/platform/darwin/src/MGLOfflineStorage.mm +++ b/platform/darwin/src/MGLOfflineStorage.mm @@ -7,6 +7,7 @@ #import "MGLOfflinePack_Private.h" #import "MGLOfflineRegion_Private.h" #import "MGLTilePyramidOfflineRegion.h" +#import "NSBundle+MGLAdditions.h" #import "NSValue+MGLAdditions.h" #include <mbgl/util/string.hpp> @@ -132,7 +133,7 @@ NSString * const MGLOfflinePackMaximumCountUserInfoKey = MGLOfflinePackUserInfoK appropriateForURL:nil create:YES error:nil]; - NSString *bundleIdentifier = [self bundleIdentifier]; + NSString *bundleIdentifier = [NSBundle mgl_applicationBundleIdentifier]; if (!bundleIdentifier) { // There’s no main bundle identifier when running in a unit test bundle. bundleIdentifier = [NSBundle bundleForClass:self].bundleIdentifier; @@ -166,7 +167,7 @@ NSString * const MGLOfflinePackMaximumCountUserInfoKey = MGLOfflinePackUserInfoK NSString *legacyCachePath = [legacyPaths.firstObject stringByAppendingPathComponent:MGLOfflineStorageFileName3_2_0_beta_1]; #elif TARGET_OS_MAC // ~/Library/Caches/tld.app.bundle.id/offline.db - NSString *bundleIdentifier = [self bundleIdentifier]; + NSString *bundleIdentifier = [NSBundle mgl_applicationBundleIdentifier]; NSURL *legacyCacheDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSCachesDirectory inDomain:NSUserDomainMask appropriateForURL:nil @@ -219,15 +220,6 @@ NSString * const MGLOfflinePackMaximumCountUserInfoKey = MGLOfflinePackUserInfoK return self; } -+ (NSString *)bundleIdentifier { - NSString *bundleIdentifier = [NSBundle mainBundle].bundleIdentifier; - if (!bundleIdentifier) { - // There’s no main bundle identifier when running in a unit test bundle. - bundleIdentifier = [NSBundle bundleForClass:self].bundleIdentifier; - } - return bundleIdentifier; -} - - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; [[MGLNetworkConfiguration sharedManager] removeObserver:self forKeyPath:@"apiBaseURL"]; diff --git a/platform/darwin/src/MGLPolygon.mm b/platform/darwin/src/MGLPolygon.mm index ceafe873bf..d966ff13ce 100644 --- a/platform/darwin/src/MGLPolygon.mm +++ b/platform/darwin/src/MGLPolygon.mm @@ -6,6 +6,7 @@ #import "MGLPolygon+MGLAdditions.h" #import <mbgl/util/geojson.hpp> +#import <mapbox/polylabel.hpp> @implementation MGLPolygon @@ -54,6 +55,13 @@ return [super hash] + [[self geoJSONDictionary] hash]; } +- (CLLocationCoordinate2D)coordinate { + // pole of inaccessibility + auto poi = mapbox::polylabel([self polygon]); + + return MGLLocationCoordinate2DFromPoint(poi); +} + - (mbgl::LinearRing<double>)ring { NSUInteger count = self.pointCount; CLLocationCoordinate2D *coordinates = self.coordinates; @@ -155,11 +163,17 @@ return hash; } +- (CLLocationCoordinate2D)coordinate { + MGLPolygon *firstPolygon = self.polygons.firstObject; + + return firstPolygon.coordinate; +} + - (BOOL)intersectsOverlayBounds:(MGLCoordinateBounds)overlayBounds { return MGLCoordinateBoundsIntersectsCoordinateBounds(_overlayBounds, overlayBounds); } -- (mbgl::Geometry<double>)geometryObject { +- (mbgl::MultiPolygon<double>)multiPolygon { mbgl::MultiPolygon<double> multiPolygon; multiPolygon.reserve(self.polygons.count); for (MGLPolygon *polygon in self.polygons) { @@ -173,6 +187,10 @@ return multiPolygon; } +- (mbgl::Geometry<double>)geometryObject { + return [self multiPolygon]; +} + - (NSDictionary *)geoJSONDictionary { NSMutableArray *coordinates = [[NSMutableArray alloc] initWithCapacity:self.polygons.count]; for (MGLPolygonFeature *feature in self.polygons) { diff --git a/platform/darwin/src/MGLPolyline.mm b/platform/darwin/src/MGLPolyline.mm index ae4fbe61de..fd75dc2795 100644 --- a/platform/darwin/src/MGLPolyline.mm +++ b/platform/darwin/src/MGLPolyline.mm @@ -6,6 +6,7 @@ #import "MGLPolyline+MGLAdditions.h" #import <mbgl/util/geojson.hpp> +#import <mapbox/polylabel.hpp> @implementation MGLPolyline @@ -52,6 +53,61 @@ return self == other || ([other isKindOfClass:[MGLPolyline class]] && [super isEqual:other]); } +- (CLLocationCoordinate2D)coordinate { + NSUInteger count = self.pointCount; + NSAssert(count > 0, @"Polyline must have coordinates"); + + CLLocationCoordinate2D *coordinates = self.coordinates; + CLLocationDistance middle = [self length] / 2.0; + CLLocationDistance traveled = 0.0; + + if (count > 1 || middle > traveled) { + for (NSUInteger i = 0; i < count; i++) { + + MGLRadianCoordinate2D from = MGLRadianCoordinateFromLocationCoordinate(coordinates[i]); + MGLRadianCoordinate2D to = MGLRadianCoordinateFromLocationCoordinate(coordinates[i + 1]); + + if (traveled >= middle) { + double overshoot = middle - traveled; + if (overshoot == 0) { + return coordinates[i]; + } + to = MGLRadianCoordinateFromLocationCoordinate(coordinates[i - 1]); + CLLocationDirection direction = [self direction:from to:to] - 180; + MGLRadianCoordinate2D otherCoordinate = MGLRadianCoordinateAtDistanceFacingDirection(from, + overshoot/mbgl::util::EARTH_RADIUS_M, + MGLRadiansFromDegrees(direction)); + return CLLocationCoordinate2DMake(MGLDegreesFromRadians(otherCoordinate.latitude), + MGLDegreesFromRadians(otherCoordinate.longitude)); + } + + traveled += (MGLDistanceBetweenRadianCoordinates(from, to) * mbgl::util::EARTH_RADIUS_M); + + } + } + + return coordinates[count - 1]; +} + +- (CLLocationDistance)length +{ + CLLocationDistance length = 0.0; + + NSUInteger count = self.pointCount; + CLLocationCoordinate2D *coordinates = self.coordinates; + + for (NSUInteger i = 0; i < count - 1; i++) { + length += (MGLDistanceBetweenRadianCoordinates(MGLRadianCoordinateFromLocationCoordinate(coordinates[i]), MGLRadianCoordinateFromLocationCoordinate(coordinates[i + 1])) * mbgl::util::EARTH_RADIUS_M); + } + + return length; +} + +- (CLLocationDirection)direction:(MGLRadianCoordinate2D)from to:(MGLRadianCoordinate2D)to +{ + return MGLDegreesFromRadians(MGLRadianCoordinatesDirection(from, to)); +} + @end @interface MGLMultiPolyline () @@ -114,6 +170,15 @@ return hash; } +- (CLLocationCoordinate2D)coordinate { + MGLPolyline *polyline = self.polylines.firstObject; + CLLocationCoordinate2D *coordinates = polyline.coordinates; + NSAssert([polyline pointCount] > 0, @"Polyline must have coordinates"); + CLLocationCoordinate2D firstCoordinate = coordinates[0]; + + return firstCoordinate; +} + - (BOOL)intersectsOverlayBounds:(MGLCoordinateBounds)overlayBounds { return MGLCoordinateBoundsIntersectsCoordinateBounds(_overlayBounds, overlayBounds); } diff --git a/platform/darwin/src/MGLRasterSource.h b/platform/darwin/src/MGLRasterSource.h index 519784f4f1..4f4b7c96c3 100644 --- a/platform/darwin/src/MGLRasterSource.h +++ b/platform/darwin/src/MGLRasterSource.h @@ -108,96 +108,13 @@ MGL_EXPORT Returns a raster source initialized an identifier, tile URL templates, and options. + Tile URL templates are strings that specify the URLs of the raster tile images + to load. See the “<a href="../tile-url-templates.html">Tile URL Templates</a>” + guide for information about the format of a tile URL template. + After initializing and configuring the source, add it to a map view’s style using the `-[MGLStyle addSource:]` method. - #### Tile URL templates - - Tile URL templates are strings that specify the URLs of the tile images to - load. Each template resembles an absolute URL, but with any number of - placeholder strings that the source evaluates based on the tile it needs to - load. For example: - - <ul> - <li><code>http://www.example.com/tiles/{z}/{x}/{y}.pbf</code> could be - evaluated as <code>http://www.example.com/tiles/14/6/9.pbf</code>.</li> - <li><code>http://www.example.com/tiles/{z}/{x}/{y}{ratio}.png</code> could be - evaluated as <code>http://www.example.com/tiles/14/6/9@2x.png</code>.</li> - </ul> - - Tile sources support the following placeholder strings in tile URL templates, - all of which are optional: - - <table> - <thead> - <tr><th>Placeholder string</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><code>{x}</code></td> - <td>The index of the tile along the map’s x axis according to Spherical - Mercator projection. If the value is 0, the tile’s left edge corresponds - to the 180th meridian west. If the value is 2<sup><var>z</var></sup>−1, - the tile’s right edge corresponds to the 180th meridian east.</td> - </tr> - <tr> - <td><code>{y}</code></td> - <td>The index of the tile along the map’s y axis according to Spherical - Mercator projection. If the value is 0, the tile’s tile edge corresponds - to arctan(sinh(π)), or approximately 85.0511 degrees north. If the value - is 2<sup><var>z</var></sup>−1, the tile’s bottom edge corresponds to - −arctan(sinh(π)), or approximately 85.0511 degrees south. The y axis is - inverted if the <code>options</code> parameter contains - <code>MGLTileSourceOptionTileCoordinateSystem</code> with a value of - <code>MGLTileCoordinateSystemTMS</code>.</td> - </tr> - <tr> - <td><code>{z}</code></td> - <td>The tile’s zoom level. At zoom level 0, each tile covers the entire - world map; at zoom level 1, it covers ¼ of the world; at zoom level 2, - <sup>1</sup>⁄<sub>16</sub> of the world, and so on. For tiles loaded by - a <code>MGLRasterSource</code> object, whether the tile zoom level - matches the map’s current zoom level depends on the value of the - source’s tile size as specified in the - <code>MGLTileSourceOptionTileSize</code> key of the - <code>options</code> parameter.</td> - </tr> - <tr> - <td><code>{bbox-epsg-3857}</code></td> - <td>The tile’s bounding box, expressed as a comma-separated list of the - tile’s western, southern, eastern, and northern extents according to - Spherical Mercator (EPSG:3857) projection. The bounding box is typically - used with map services conforming to the - <a href="http://www.opengeospatial.org/standards/wms">Web Map Service</a> - protocol.</td> - </tr> - <tr> - <td><code>{quadkey}</code></td> - <td>A quadkey indicating both the tile’s location and its zoom level. The - quadkey is typically used with - <a href="https://msdn.microsoft.com/en-us/library/bb259689.aspx">Bing Maps</a>. - </td> - </tr> - <tr> - <td><code>{ratio}</code></td> - <td>A suffix indicating the resolution of the tile image. The suffix is the - empty string for standard resolution displays and <code>@2x</code> for - Retina displays, including displays for which - <code>NSScreen.backingScaleFactor</code> or <code>UIScreen.scale</code> - is 3.</td> - </tr> - <tr> - <td><code>{prefix}</code></td> - <td>Two hexadecimal digits chosen such that each visible tile has a - different prefix. The prefix is typically used for domain sharding.</td> - </tr> - </tbody> - </table> - - For more information about the `{x}`, `{y}`, and `{z}` placeholder strings, - consult the - <a href="https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames">OpenStreetMap Wiki</a>. - @param identifier A string that uniquely identifies the source in the style to which it is added. @param tileURLTemplates An array of tile URL template strings. Only the first diff --git a/platform/darwin/src/MGLStyle.h b/platform/darwin/src/MGLStyle.h index ccec863236..6fb4a6cc6b 100644 --- a/platform/darwin/src/MGLStyle.h +++ b/platform/darwin/src/MGLStyle.h @@ -6,6 +6,7 @@ #import "MGLTypes.h" @class MGLSource; +@class MGLLight; NS_ASSUME_NONNULL_BEGIN @@ -548,6 +549,14 @@ MGL_EXPORT */ - (void)removeImageForName:(NSString *)name; + +#pragma mark Managing the Style's Light + +/** + Provides global light source for the style. + */ +@property (nonatomic, strong) MGLLight *light; + @end NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/MGLStyle.mm b/platform/darwin/src/MGLStyle.mm index 5f26b4fed2..2e6f2bc29a 100644 --- a/platform/darwin/src/MGLStyle.mm +++ b/platform/darwin/src/MGLStyle.mm @@ -14,6 +14,7 @@ #import "MGLStyle_Private.h" #import "MGLStyleLayer_Private.h" #import "MGLSource_Private.h" +#import "MGLLight_Private.h" #import "NSDate+MGLAdditions.h" @@ -28,6 +29,7 @@ #include <mbgl/map/map.hpp> #include <mbgl/util/default_styles.hpp> #include <mbgl/style/image.hpp> +#include <mbgl/style/light.hpp> #include <mbgl/style/layers/fill_layer.hpp> #include <mbgl/style/layers/fill_extrusion_layer.hpp> #include <mbgl/style/layers/line_layer.hpp> @@ -556,6 +558,21 @@ static NSURL *MGLStyleURL_emerald; return transition; } +#pragma mark Style light + +- (void)setLight:(MGLLight *)light +{ + std::unique_ptr<mbgl::style::Light> mbglLight = std::make_unique<mbgl::style::Light>([light mbglLight]); + self.mapView.mbglMap->setLight(std::move(mbglLight)); +} + +- (MGLLight *)light +{ + auto mbglLight = self.mapView.mbglMap->getLight(); + MGLLight *light = [[MGLLight alloc] initWithMBGLLight:mbglLight]; + return light; +} + - (NSString *)description { return [NSString stringWithFormat:@"<%@: %p; name = %@, URL = %@>", diff --git a/platform/darwin/src/MGLStyleValue.h b/platform/darwin/src/MGLStyleValue.h index 70074c8a13..2bb3aca4f4 100644 --- a/platform/darwin/src/MGLStyleValue.h +++ b/platform/darwin/src/MGLStyleValue.h @@ -75,9 +75,9 @@ typedef NS_ENUM(NSUInteger, MGLInterpolationMode) { */ MGLInterpolationModeCategorical, /** - Values between two stops are not interpolated. Instead, values are set to their - input value. Use identity interpolation mode to show attribute values that can be - used as style values. + Values between two stops are not interpolated. Instead, for any given feature, the + style value matches a value in that feature’s attributes dictionary. Use identity + interpolation mode to show attribute values that can be used as style values. */ MGLInterpolationModeIdentity }; diff --git a/platform/darwin/src/MGLStyleValue.mm b/platform/darwin/src/MGLStyleValue.mm index 33b6babadf..4dd6b550d8 100644 --- a/platform/darwin/src/MGLStyleValue.mm +++ b/platform/darwin/src/MGLStyleValue.mm @@ -128,7 +128,7 @@ const MGLStyleFunctionOption MGLStyleFunctionOptionDefaultValue = @"MGLStyleFunc return {}; } - if (self == [super init]) { + if (self = [super init]) { self.interpolationMode = interpolationMode; self.stops = stops; @@ -181,7 +181,7 @@ const MGLStyleFunctionOption MGLStyleFunctionOptionDefaultValue = @"MGLStyleFunc } - (instancetype)initWithInterpolationMode:(MGLInterpolationMode)interpolationMode stops:(NSDictionary *)stops attributeName:(NSString *)attributeName options:(NSDictionary *)options { - if (self == [super init]) { + if (self = [super init]) { self.interpolationMode = interpolationMode; self.stops = stops; _attributeName = attributeName; @@ -251,7 +251,7 @@ const MGLStyleFunctionOption MGLStyleFunctionOptionDefaultValue = @"MGLStyleFunc } - (instancetype)initWithInterpolationMode:(MGLInterpolationMode)interpolationMode stops:(NSDictionary *)stops attributeName:(NSString *)attributeName options:(NSDictionary *)options { - if (self == [super init]) { + if (self = [super init]) { self.interpolationMode = interpolationMode; self.stops = stops; _attributeName = attributeName; diff --git a/platform/darwin/src/MGLStyleValue_Private.h b/platform/darwin/src/MGLStyleValue_Private.h index 263b54d7e5..2155c657bd 100644 --- a/platform/darwin/src/MGLStyleValue_Private.h +++ b/platform/darwin/src/MGLStyleValue_Private.h @@ -3,11 +3,13 @@ #import "MGLStyleValue.h" #import "NSValue+MGLStyleAttributeAdditions.h" +#import "NSValue+MGLAdditions.h" #import "MGLTypes.h" #import "MGLConversion.h" #include <mbgl/style/conversion/data_driven_property_value.hpp> #include <mbgl/style/conversion.hpp> +#import <mbgl/style/types.hpp> #import <mbgl/util/enum.hpp> @@ -415,6 +417,12 @@ private: // Private utilities for converting from mgl to mbgl values mbglValue.push_back(mbglElement); } } + + void getMBGLValue(NSValue *rawValue, mbgl::style::Position &mbglValue) { + auto spherical = rawValue.mgl_lightPositionArrayValue; + mbgl::style::Position position(spherical); + mbglValue = position; + } // Enumerations template <typename MBGLEnum = MBGLType, @@ -473,6 +481,12 @@ private: // Private utilities for converting from mbgl to mgl values } return array; } + + static NSValue *toMGLRawStyleValue(const mbgl::style::Position &mbglStopValue) { + std::array<float, 3> spherical = mbglStopValue.getSpherical(); + MGLSphericalPosition position = MGLSphericalPositionMake(spherical[0], spherical[1], spherical[2]); + return [NSValue valueWithMGLSphericalPosition:position]; + } // Enumerations template <typename MBGLEnum = MBGLType, typename MGLEnum = ObjCEnum> diff --git a/platform/darwin/src/MGLTypes.h b/platform/darwin/src/MGLTypes.h index c06fd8b0e7..16f510b5a6 100644 --- a/platform/darwin/src/MGLTypes.h +++ b/platform/darwin/src/MGLTypes.h @@ -1,4 +1,5 @@ #import <Foundation/Foundation.h> +#import <CoreGraphics/CoreGraphics.h> #import "MGLFoundation.h" diff --git a/platform/darwin/src/MGLVectorSource.h b/platform/darwin/src/MGLVectorSource.h index a48434f7a3..968be3c0e0 100644 --- a/platform/darwin/src/MGLVectorSource.h +++ b/platform/darwin/src/MGLVectorSource.h @@ -74,96 +74,13 @@ MGL_EXPORT Returns a vector source initialized an identifier, tile URL templates, and options. + Tile URL templates are strings that specify the URLs of the vector tiles to + load. See the “<a href="../tile-url-templates.html">Tile URL Templates</a>” + guide for information about the format of a tile URL template. + After initializing and configuring the source, add it to a map view’s style using the `-[MGLStyle addSource:]` method. - #### Tile URL templates - - Tile URL templates are strings that specify the URLs of the tile images to - load. Each template resembles an absolute URL, but with any number of - placeholder strings that the source evaluates based on the tile it needs to - load. For example: - - <ul> - <li><code>http://www.example.com/tiles/{z}/{x}/{y}.pbf</code> could be - evaluated as <code>http://www.example.com/tiles/14/6/9.pbf</code>.</li> - <li><code>http://www.example.com/tiles/{z}/{x}/{y}{ratio}.png</code> could be - evaluated as <code>http://www.example.com/tiles/14/6/9@2x.png</code>.</li> - </ul> - - Tile sources support the following placeholder strings in tile URL templates, - all of which are optional: - - <table> - <thead> - <tr><th>Placeholder string</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><code>{x}</code></td> - <td>The index of the tile along the map’s x axis according to Spherical - Mercator projection. If the value is 0, the tile’s left edge corresponds - to the 180th meridian west. If the value is 2<sup><var>z</var></sup>−1, - the tile’s right edge corresponds to the 180th meridian east.</td> - </tr> - <tr> - <td><code>{y}</code></td> - <td>The index of the tile along the map’s y axis according to Spherical - Mercator projection. If the value is 0, the tile’s tile edge corresponds - to arctan(sinh(π)), or approximately 85.0511 degrees north. If the value - is 2<sup><var>z</var></sup>−1, the tile’s bottom edge corresponds to - −arctan(sinh(π)), or approximately 85.0511 degrees south. The y axis is - inverted if the <code>options</code> parameter contains - <code>MGLTileSourceOptionTileCoordinateSystem</code> with a value of - <code>MGLTileCoordinateSystemTMS</code>.</td> - </tr> - <tr> - <td><code>{z}</code></td> - <td>The tile’s zoom level. At zoom level 0, each tile covers the entire - world map; at zoom level 1, it covers ¼ of the world; at zoom level 2, - <sup>1</sup>⁄<sub>16</sub> of the world, and so on. For tiles loaded by - a <code>MGLRasterSource</code> object, whether the tile zoom level - matches the map’s current zoom level depends on the value of the - source’s tile size as specified in the - <code>MGLTileSourceOptionTileSize</code> key of the - <code>options</code> parameter.</td> - </tr> - <tr> - <td><code>{bbox-epsg-3857}</code></td> - <td>The tile’s bounding box, expressed as a comma-separated list of the - tile’s western, southern, eastern, and northern extents according to - Spherical Mercator (EPSG:3857) projection. The bounding box is typically - used with map services conforming to the - <a href="http://www.opengeospatial.org/standards/wms">Web Map Service</a> - protocol.</td> - </tr> - <tr> - <td><code>{quadkey}</code></td> - <td>A quadkey indicating both the tile’s location and its zoom level. The - quadkey is typically used with - <a href="https://msdn.microsoft.com/en-us/library/bb259689.aspx">Bing Maps</a>. - </td> - </tr> - <tr> - <td><code>{ratio}</code></td> - <td>A suffix indicating the resolution of the tile image. The suffix is the - empty string for standard resolution displays and <code>@2x</code> for - Retina displays, including displays for which - <code>NSScreen.backingScaleFactor</code> or <code>UIScreen.scale</code> - is 3.</td> - </tr> - <tr> - <td><code>{prefix}</code></td> - <td>Two hexadecimal digits chosen such that each visible tile has a - different prefix. The prefix is typically used for domain sharding.</td> - </tr> - </tbody> - </table> - - For more information about the `{x}`, `{y}`, and `{z}` placeholder strings, - consult the - <a href="https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames">OpenStreetMap Wiki</a>. - @param identifier A string that uniquely identifies the source in the style to which it is added. @param tileURLTemplates An array of tile URL template strings. Only the first diff --git a/platform/darwin/src/NSBundle+MGLAdditions.h b/platform/darwin/src/NSBundle+MGLAdditions.h index 1fc9e8b896..ad5e9d5369 100644 --- a/platform/darwin/src/NSBundle+MGLAdditions.h +++ b/platform/darwin/src/NSBundle+MGLAdditions.h @@ -36,9 +36,7 @@ NS_ASSUME_NONNULL_BEGIN + (nullable NS_DICTIONARY_OF(NSString *, id) *)mgl_frameworkInfoDictionary; -/// The relative path to the directory containing the SDK’s resource files, or -/// `nil` if the files are located directly within the bundle’s root directory. -@property (readonly, copy, nullable) NSString *mgl_resourcesDirectory; ++ (nullable NSString *)mgl_applicationBundleIdentifier; @end diff --git a/platform/darwin/src/NSBundle+MGLAdditions.m b/platform/darwin/src/NSBundle+MGLAdditions.m index 76d9cc0db7..f383a50300 100644 --- a/platform/darwin/src/NSBundle+MGLAdditions.m +++ b/platform/darwin/src/NSBundle+MGLAdditions.m @@ -6,12 +6,19 @@ + (instancetype)mgl_frameworkBundle { NSBundle *bundle = [self bundleForClass:[MGLAccountManager class]]; - if (![bundle.infoDictionary[@"CFBundlePackageType"] isEqualToString:@"FMWK"] && !bundle.mgl_resourcesDirectory) { + + if (![bundle.infoDictionary[@"CFBundlePackageType"] isEqualToString:@"FMWK"]) { // For static frameworks, the bundle is the containing application - // bundle but the resources are still in the framework bundle. - bundle = [NSBundle bundleWithPath:[bundle.privateFrameworksPath - stringByAppendingPathComponent:@"Mapbox.framework"]]; + // bundle but the resources are in Mapbox.bundle. + NSString *bundlePath = [bundle pathForResource:@"Mapbox" ofType:@"bundle"]; + if (bundlePath) { + bundle = [self bundleWithPath:bundlePath]; + } else { + [NSException raise:@"MGLBundleNotFoundException" format: + @"The Mapbox framework bundle could not be found. If using the Mapbox iOS SDK as a static framework, make sure that Mapbox.bundle is copied into the root of the app bundle."]; + } } + return bundle; } @@ -21,18 +28,16 @@ + (nullable NS_DICTIONARY_OF(NSString *, id) *)mgl_frameworkInfoDictionary { NSBundle *bundle = self.mgl_frameworkBundle; - if (bundle.mgl_resourcesDirectory) { - NSString *infoPlistPath = [bundle pathForResource:@"Info" - ofType:@"plist" - inDirectory:bundle.mgl_resourcesDirectory]; - return [NSDictionary dictionaryWithContentsOfFile:infoPlistPath]; - } else { - return bundle.infoDictionary; - } + return bundle.infoDictionary; } -- (NSString *)mgl_resourcesDirectory { - return [self pathForResource:@"Mapbox" ofType:@"bundle"] ? @"Mapbox.bundle" : nil; ++ (nullable NSString *)mgl_applicationBundleIdentifier { + NSString *bundleIdentifier = [NSBundle mainBundle].bundleIdentifier; + if (!bundleIdentifier) { + // There’s no main bundle identifier when running in a unit test bundle. + bundleIdentifier = [NSBundle bundleForClass:[MGLAccountManager class]].bundleIdentifier; + } + return bundleIdentifier; } @end diff --git a/platform/darwin/src/NSValue+MGLAdditions.h b/platform/darwin/src/NSValue+MGLAdditions.h index e6755021d0..0aaa2a337a 100644 --- a/platform/darwin/src/NSValue+MGLAdditions.h +++ b/platform/darwin/src/NSValue+MGLAdditions.h @@ -1,6 +1,7 @@ #import <Foundation/Foundation.h> #import "MGLGeometry.h" +#import "MGLLight.h" #import "MGLOfflinePack.h" #import "MGLTypes.h" @@ -87,6 +88,34 @@ NS_ASSUME_NONNULL_BEGIN */ @property (readonly) MGLTransition MGLTransitionValue; +/** + Creates a new value object containing the given `MGLSphericalPosition` + structure. + + @param lightPosition The value for the new object. + @return A new value object that contains the light position information. + */ ++ (instancetype)valueWithMGLSphericalPosition:(MGLSphericalPosition)lightPosition; + +/** + The `MGLSphericalPosition` structure representation of the value. + */ +@property (readonly) MGLSphericalPosition MGLSphericalPositionValue; + +/** + Creates a new value object containing the given `MGLLightAnchor` + enum. + + @param lightAnchor The value for the new object. + @return A new value object that contains the light anchor information. + */ ++ (NSValue *)valueWithMGLLightAnchor:(MGLLightAnchor)lightAnchor; + +/** + The `MGLLightAnchor` enum representation of the value. + */ +@property (readonly) MGLLightAnchor MGLLightAnchorValue; + @end NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/NSValue+MGLAdditions.m b/platform/darwin/src/NSValue+MGLAdditions.m index a95ef23941..ef894f0eb4 100644 --- a/platform/darwin/src/NSValue+MGLAdditions.m +++ b/platform/darwin/src/NSValue+MGLAdditions.m @@ -58,4 +58,27 @@ return transition; } ++ (NSValue *)valueWithMGLSphericalPosition:(MGLSphericalPosition)lightPosition +{ + return [NSValue value:&lightPosition withObjCType:@encode(MGLSphericalPosition)]; +} + +- (MGLSphericalPosition)MGLSphericalPositionValue +{ + MGLSphericalPosition lightPosition; + [self getValue:&lightPosition]; + return lightPosition; +} + ++ (NSValue *)valueWithMGLLightAnchor:(MGLLightAnchor)lightAnchor { + return [NSValue value:&lightAnchor withObjCType:@encode(MGLLightAnchor)]; +} + +- (MGLLightAnchor)MGLLightAnchorValue +{ + MGLLightAnchor achorType; + [self getValue:&achorType]; + return achorType; +} + @end diff --git a/platform/darwin/src/NSValue+MGLStyleAttributeAdditions.h b/platform/darwin/src/NSValue+MGLStyleAttributeAdditions.h index 60c1ee4075..0f1e511694 100644 --- a/platform/darwin/src/NSValue+MGLStyleAttributeAdditions.h +++ b/platform/darwin/src/NSValue+MGLStyleAttributeAdditions.h @@ -9,5 +9,6 @@ - (std::array<float, 2>)mgl_offsetArrayValue; - (std::array<float, 4>)mgl_paddingArrayValue; +- (std::array<float, 3>)mgl_lightPositionArrayValue; @end diff --git a/platform/darwin/src/NSValue+MGLStyleAttributeAdditions.mm b/platform/darwin/src/NSValue+MGLStyleAttributeAdditions.mm index e66145aec1..a41950b6b3 100644 --- a/platform/darwin/src/NSValue+MGLStyleAttributeAdditions.mm +++ b/platform/darwin/src/NSValue+MGLStyleAttributeAdditions.mm @@ -1,5 +1,5 @@ #import "NSValue+MGLStyleAttributeAdditions.h" - +#import "MGLLight.h" #if TARGET_OS_IPHONE #import <UIKit/UIKit.h> #define MGLEdgeInsets UIEdgeInsets @@ -61,4 +61,17 @@ }; } +- (std::array<float, 3>)mgl_lightPositionArrayValue +{ + NSAssert(strcmp(self.objCType, @encode(MGLSphericalPosition)) == 0, @"Value does not represent an MGLSphericalPosition"); + MGLSphericalPosition lightPosition; + [self getValue:&lightPosition]; + // Style specification defines padding in clockwise order: top, right, bottom, left. + return { + static_cast<float>(lightPosition.radial), + static_cast<float>(lightPosition.azimuthal), + static_cast<float>(lightPosition.polar), + }; +} + @end diff --git a/platform/darwin/test/MGLAttributionInfoTests.m b/platform/darwin/test/MGLAttributionInfoTests.m index e258671c09..ed4927d44b 100644 --- a/platform/darwin/test/MGLAttributionInfoTests.m +++ b/platform/darwin/test/MGLAttributionInfoTests.m @@ -46,8 +46,18 @@ XCTAssertEqualObjects(infos[3].title.string, @"Improve this map"); XCTAssertEqualObjects(infos[3].URL, [NSURL URLWithString:@"https://www.mapbox.com/map-feedback/"]); XCTAssertTrue(infos[3].feedbackLink); + NSURL *styleURL = [MGLStyle satelliteStreetsStyleURLWithVersion:99]; +#if TARGET_OS_IPHONE + XCTAssertEqualObjects([infos[3] feedbackURLAtCenterCoordinate:mapbox zoomLevel:14], + [NSURL URLWithString:@"https://www.mapbox.com/feedback/?referrer=com.mapbox.sdk.ios#/77.63680/12.98108/14.00/0.0/0"]); + XCTAssertEqualObjects([infos[3] feedbackURLForStyleURL:styleURL atCenterCoordinate:mapbox zoomLevel:3.14159 direction:90.9 pitch:12.5], + [NSURL URLWithString:@"https://www.mapbox.com/feedback/?referrer=com.mapbox.sdk.ios&owner=mapbox&id=satellite-streets-v99&access_token#/77.63680/12.98108/3.14/90.9/13"]); +#else XCTAssertEqualObjects([infos[3] feedbackURLAtCenterCoordinate:mapbox zoomLevel:14], - [NSURL URLWithString:@"https://www.mapbox.com/map-feedback/#/77.63680/12.98108/15"]); + [NSURL URLWithString:@"https://www.mapbox.com/feedback/?referrer=com.mapbox.MapboxGL#/77.63680/12.98108/14.00/0.0/0"]); + XCTAssertEqualObjects([infos[3] feedbackURLForStyleURL:styleURL atCenterCoordinate:mapbox zoomLevel:3.14159 direction:90.9 pitch:12.5], + [NSURL URLWithString:@"https://www.mapbox.com/feedback/?referrer=com.mapbox.MapboxGL&owner=mapbox&id=satellite-streets-v99&access_token#/77.63680/12.98108/3.14/90.9/13"]); +#endif } - (void)testStyle { diff --git a/platform/darwin/test/MGLCodingTests.m b/platform/darwin/test/MGLCodingTests.m index ff0d674ad1..ac61672b76 100644 --- a/platform/darwin/test/MGLCodingTests.m +++ b/platform/darwin/test/MGLCodingTests.m @@ -66,12 +66,54 @@ [unarchivedPolyline replaceCoordinatesInRange:NSMakeRange(0, 1) withCoordinates:otherCoordinates]; XCTAssertNotEqualObjects(polyline, unarchivedPolyline); + + CLLocationCoordinate2D multiLineCoordinates[] = { + CLLocationCoordinate2DMake(51.000000, 0.000000), + CLLocationCoordinate2DMake(51.000000, 1.000000), + CLLocationCoordinate2DMake(51.000000, 2.000000), + }; + + NSUInteger multiLineCoordinatesCount = sizeof(multiLineCoordinates) / sizeof(CLLocationCoordinate2D); + MGLPolyline *multiLine = [MGLPolyline polylineWithCoordinates:multiLineCoordinates count:multiLineCoordinatesCount]; + CLLocationCoordinate2D multiLineCenter = CLLocationCoordinate2DMake(51.000000, 1.000000); + + XCTAssertEqual([multiLine coordinate].latitude, multiLineCenter.latitude); + XCTAssertEqual([multiLine coordinate].longitude, multiLineCenter.longitude); + + CLLocationCoordinate2D segmentCoordinates[] = { + CLLocationCoordinate2DMake(35.040390, -85.311477), + CLLocationCoordinate2DMake(35.040390, -85.209510), + }; + + NSUInteger segmentCoordinatesCount = sizeof(segmentCoordinates) / sizeof(CLLocationCoordinate2D); + MGLPolyline *segmentLine = [MGLPolyline polylineWithCoordinates:segmentCoordinates count:segmentCoordinatesCount]; + CLLocationCoordinate2D segmentCenter = CLLocationCoordinate2DMake(35.0404006631, -85.2604935); + + XCTAssertEqualWithAccuracy([segmentLine coordinate].latitude, segmentCenter.latitude, 0.0001); + XCTAssertEqualWithAccuracy([segmentLine coordinate].longitude, segmentCenter.longitude, 0.0001); + + CLLocationCoordinate2D sfToBerkeleyCoordinates[] = { + CLLocationCoordinate2DMake(37.782440, -122.397111), + CLLocationCoordinate2DMake(37.818384, -122.352994), + CLLocationCoordinate2DMake(37.831401, -122.274545), + CLLocationCoordinate2DMake(37.862172, -122.262700), + }; + + NSUInteger sfToBerkeleyCoordinatesCount = sizeof(sfToBerkeleyCoordinates) / sizeof(CLLocationCoordinate2D); + MGLPolyline *sfToBerkeleyLine = [MGLPolyline polylineWithCoordinates:sfToBerkeleyCoordinates count:sfToBerkeleyCoordinatesCount]; + CLLocationCoordinate2D sfToBerkeleyCenter = CLLocationCoordinate2DMake(37.8230575118,-122.324867587); + + XCTAssertEqualWithAccuracy([sfToBerkeleyLine coordinate].latitude, sfToBerkeleyCenter.latitude, 0.0001); + XCTAssertEqualWithAccuracy([sfToBerkeleyLine coordinate].longitude, sfToBerkeleyCenter.longitude, 0.0001); + } - (void)testPolygon { CLLocationCoordinate2D coordinates[] = { - CLLocationCoordinate2DMake(0.664482398, 1.8865675), - CLLocationCoordinate2DMake(2.13224687, 3.9984632) + CLLocationCoordinate2DMake(35.090745, -85.300259), + CLLocationCoordinate2DMake(35.092035, -85.298885), + CLLocationCoordinate2DMake(35.090639, -85.297416), + CLLocationCoordinate2DMake(35.089112, -85.298928) }; NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D); @@ -84,8 +126,24 @@ [NSKeyedArchiver archiveRootObject:polygon toFile:filePath]; MGLPolygon *unarchivedPolygon = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath]; + [unarchivedPolygon coordinate]; XCTAssertEqualObjects(polygon, unarchivedPolygon); + + CLLocationCoordinate2D squareCoordinates[] = { + CLLocationCoordinate2DMake(100.0, 0.0), + CLLocationCoordinate2DMake(101.0, 0.0), + CLLocationCoordinate2DMake(101.0, 1.0), + CLLocationCoordinate2DMake(100.0, 1.0), + }; + + NSUInteger squareCoordinatesCount = sizeof(squareCoordinates) / sizeof(CLLocationCoordinate2D); + MGLPolygon *squarePolygon = [MGLPolygon polygonWithCoordinates:squareCoordinates count:squareCoordinatesCount]; + CLLocationCoordinate2D squareCenter = CLLocationCoordinate2DMake(100.5, 0.5); + + XCTAssertEqual([squarePolygon coordinate].latitude, squareCenter.latitude); + XCTAssertEqual([squarePolygon coordinate].longitude, squareCenter.longitude); + } - (void)testPolygonWithInteriorPolygons { @@ -169,6 +227,11 @@ NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D); MGLPointCollection *pointCollection = [MGLPointCollection pointCollectionWithCoordinates:coordinates count:numberOfCoordinates]; + CLLocationCoordinate2D pointsCenter = CLLocationCoordinate2DMake(0, 1); + + XCTAssertEqual([pointCollection coordinate].latitude, pointsCenter.latitude); + XCTAssertEqual([pointCollection coordinate].longitude, pointsCenter.longitude); + NSString *filePath = [self temporaryFilePathForClass:[MGLPointCollection class]]; [NSKeyedArchiver archiveRootObject:pointCollection toFile:filePath]; @@ -218,6 +281,30 @@ CLLocationCoordinate2DMake(20, 21), CLLocationCoordinate2DMake(30, 31), }; + + CLLocationCoordinate2D line1[] = { + CLLocationCoordinate2DMake(100, 40), + CLLocationCoordinate2DMake(105, 45), + CLLocationCoordinate2DMake(110, 55) + }; + + CLLocationCoordinate2D line2[] = { + CLLocationCoordinate2DMake(105, 40), + CLLocationCoordinate2DMake(110, 45), + CLLocationCoordinate2DMake(115, 55) + }; + + NSUInteger road1CoordinatesCount = sizeof(line1) / sizeof(CLLocationCoordinate2D); + NSUInteger road2CoordinatesCount = sizeof(line2) / sizeof(CLLocationCoordinate2D); + + MGLPolyline *road1Polyline = [MGLPolyline polylineWithCoordinates:line1 count:road1CoordinatesCount]; + MGLPolyline *road2Polyline = [MGLPolyline polylineWithCoordinates:line1 count:road2CoordinatesCount]; + + MGLMultiPolyline *roads = [MGLMultiPolyline multiPolylineWithPolylines:@[road1Polyline, road2Polyline]]; + CLLocationCoordinate2D roadCenter = CLLocationCoordinate2DMake(100, 40); + + XCTAssertEqual([roads coordinate].latitude, roadCenter.latitude); + XCTAssertEqual([roads coordinate].longitude, roadCenter.longitude); NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D); @@ -248,6 +335,31 @@ CLLocationCoordinate2DMake(20, 21), CLLocationCoordinate2DMake(30, 31), }; + + CLLocationCoordinate2D outerSquare[] = { + CLLocationCoordinate2DMake(100.0, 0.0), + CLLocationCoordinate2DMake(101.0, 0.0), + CLLocationCoordinate2DMake(101.0, 1.0), + CLLocationCoordinate2DMake(100.0, 1.0), + }; + + CLLocationCoordinate2D innerSquare[] = { + CLLocationCoordinate2DMake(100.35, 0.35), + CLLocationCoordinate2DMake(100.65, 0.35), + CLLocationCoordinate2DMake(100.65, 0.65), + CLLocationCoordinate2DMake(100.35, 0.65), + }; + + NSUInteger outerCoordinatesCount = sizeof(outerSquare) / sizeof(CLLocationCoordinate2D); + NSUInteger innerCoordinatesCount = sizeof(innerSquare) / sizeof(CLLocationCoordinate2D); + + MGLPolygon *innerPolygonSquare = [MGLPolygon polygonWithCoordinates:innerSquare count:innerCoordinatesCount]; + MGLPolygon *outerPolygonSquare = [MGLPolygon polygonWithCoordinates:outerSquare count:outerCoordinatesCount interiorPolygons:@[innerPolygonSquare]]; + MGLMultiPolygon *squares = [MGLMultiPolygon multiPolygonWithPolygons:@[outerPolygonSquare, innerPolygonSquare]]; + CLLocationCoordinate2D squareCenter = CLLocationCoordinate2DMake(100.5, 0.5); + + XCTAssertEqual([squares coordinate].latitude, squareCenter.latitude); + XCTAssertEqual([squares coordinate].longitude, squareCenter.longitude); NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D); @@ -265,9 +377,10 @@ MGLMultiPolygon *unarchivedMultiPolygon = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath]; MGLMultiPolygon *anotherMultiPolygon = [MGLMultiPolygon multiPolygonWithPolygons:[polygons subarrayWithRange:NSMakeRange(0, polygons.count/2)]]; - + XCTAssertEqualObjects(multiPolygon, unarchivedMultiPolygon); XCTAssertNotEqualObjects(anotherMultiPolygon, unarchivedMultiPolygon); + } - (void)testShapeCollection { diff --git a/platform/darwin/test/MGLDocumentationExampleTests.swift b/platform/darwin/test/MGLDocumentationExampleTests.swift index 6d2dc597a9..48e6b17f44 100644 --- a/platform/darwin/test/MGLDocumentationExampleTests.swift +++ b/platform/darwin/test/MGLDocumentationExampleTests.swift @@ -168,7 +168,7 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate { layer.sourceLayerIdentifier = "building" layer.fillExtrusionHeight = MGLStyleValue(interpolationMode: .identity, sourceStops: nil, attributeName: "height", options: nil) layer.fillExtrusionBase = MGLStyleValue(interpolationMode: .identity, sourceStops: nil, attributeName: "min_height", options: nil) - layer.predicate = NSPredicate(format: "extrude == TRUE") + layer.predicate = NSPredicate(format: "extrude == 'true'") mapView.style?.addLayer(layer) //#-end-example-code diff --git a/platform/darwin/test/MGLLightTest.mm b/platform/darwin/test/MGLLightTest.mm new file mode 100644 index 0000000000..2c3d1c7bd1 --- /dev/null +++ b/platform/darwin/test/MGLLightTest.mm @@ -0,0 +1,217 @@ +#import <XCTest/XCTest.h> +#import <Mapbox/Mapbox.h> + +#import "MGLLight_Private.h" + +#import "../../darwin/src/NSDate+MGLAdditions.h" + +#import <mbgl/style/light.hpp> +#import <mbgl/style/types.hpp> +#include <mbgl/style/transition_options.hpp> + +@interface MGLLightTest : XCTestCase + +@end + +@implementation MGLLightTest + +- (void)testProperties { + + MGLTransition defaultTransition = MGLTransitionMake(0, 0); + MGLTransition transition = MGLTransitionMake(6, 3); + mbgl::style::TransitionOptions transitionOptions { { MGLDurationFromTimeInterval(6) }, { MGLDurationFromTimeInterval(3) } }; + + // anchor + { + mbgl::style::Light light; + MGLLight *mglLight = [[MGLLight alloc] initWithMBGLLight:&light]; + + NSAssert([mglLight.anchor isKindOfClass:[MGLConstantStyleValue class]], @"mglLight.anchor isn’t a MGLConstantStyleValue."); + NSValue *anchorValue = ((MGLConstantStyleValue *)mglLight.anchor).rawValue; + XCTAssertEqual(anchorValue.MGLLightAnchorValue, MGLLightAnchorViewport); + XCTAssertEqual(mglLight.anchorTransition.delay, defaultTransition.delay); + XCTAssertEqual(mglLight.anchorTransition.duration, defaultTransition.duration); + + auto lightFromMGLlight = [mglLight mbglLight]; + + XCTAssertEqual(light.getDefaultAnchor(), lightFromMGLlight.getAnchor().asConstant()); + auto anchorTransition = lightFromMGLlight.getAnchorTransition(); + XCTAssert(anchorTransition.delay && MGLTimeIntervalFromDuration(*anchorTransition.delay) == defaultTransition.delay); + XCTAssert(anchorTransition.duration && MGLTimeIntervalFromDuration(*anchorTransition.duration) == defaultTransition.duration); + + MGLStyleValue<NSValue *> *anchorStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLLightAnchor:MGLLightAnchorMap]]; + mglLight.anchor = anchorStyleValue; + mglLight.anchorTransition = transition; + NSAssert([mglLight.anchor isKindOfClass:[MGLConstantStyleValue class]], @"mglLight.anchor isn’t a MGLConstantStyleValue."); + anchorValue = ((MGLConstantStyleValue *)mglLight.anchor).rawValue; + + XCTAssertEqual(anchorValue.MGLLightAnchorValue, MGLLightAnchorMap); + XCTAssertEqual(mglLight.anchorTransition.delay, transition.delay); + XCTAssertEqual(mglLight.anchorTransition.duration, transition.duration); + + mbgl::style::PropertyValue<mbgl::style::LightAnchorType> anchorProperty = { mbgl::style::LightAnchorType::Map }; + light.setAnchor(anchorProperty); + light.setAnchorTransition(transitionOptions); + + lightFromMGLlight = [mglLight mbglLight]; + + XCTAssertEqual(light.getAnchor().asConstant(), lightFromMGLlight.getAnchor().asConstant()); + anchorTransition = lightFromMGLlight.getAnchorTransition(); + XCTAssert(anchorTransition.delay && MGLTimeIntervalFromDuration(*anchorTransition.delay) == transition.delay); + XCTAssert(anchorTransition.duration && MGLTimeIntervalFromDuration(*anchorTransition.duration) == transition.duration); + + } + + // position + { + mbgl::style::Light light; + MGLLight *mglLight = [[MGLLight alloc] initWithMBGLLight:&light]; + NSAssert([mglLight.position isKindOfClass:[MGLConstantStyleValue class]], @"mglLight.position isn’t a MGLConstantStyleValue."); + NSValue *positionValue = ((MGLConstantStyleValue *)mglLight.position).rawValue; + auto positionArray = light.getDefaultPosition().getSpherical(); + MGLSphericalPosition defaultPosition = MGLSphericalPositionMake(positionArray[0], positionArray[1], positionArray[2]); + + XCTAssert(defaultPosition.radial == positionValue.MGLSphericalPositionValue.radial); + XCTAssert(defaultPosition.azimuthal == positionValue.MGLSphericalPositionValue.azimuthal); + XCTAssert(defaultPosition.polar == positionValue.MGLSphericalPositionValue.polar); + XCTAssertEqual(mglLight.positionTransiton.delay, defaultTransition.delay); + XCTAssertEqual(mglLight.positionTransiton.duration, defaultTransition.duration); + + auto lightFromMGLlight = [mglLight mbglLight]; + + XCTAssertEqual(positionArray, lightFromMGLlight.getPosition().asConstant().getSpherical()); + auto positionTransition = lightFromMGLlight.getPositionTransition(); + XCTAssert(positionTransition.delay && MGLTimeIntervalFromDuration(*positionTransition.delay) == defaultTransition.delay); + XCTAssert(positionTransition.duration && MGLTimeIntervalFromDuration(*positionTransition.duration) == defaultTransition.duration); + + defaultPosition = MGLSphericalPositionMake(6, 180, 90); + MGLStyleValue<NSValue *> *positionStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLSphericalPosition:defaultPosition]]; + mglLight.position = positionStyleValue; + mglLight.positionTransiton = transition; + + NSAssert([mglLight.position isKindOfClass:[MGLConstantStyleValue class]], @"mglLight.position isn’t a MGLConstantStyleValue."); + positionValue = ((MGLConstantStyleValue *)mglLight.position).rawValue; + + XCTAssert(defaultPosition.radial == positionValue.MGLSphericalPositionValue.radial); + XCTAssert(defaultPosition.azimuthal == positionValue.MGLSphericalPositionValue.azimuthal); + XCTAssert(defaultPosition.polar == positionValue.MGLSphericalPositionValue.polar); + XCTAssertEqual(mglLight.positionTransiton.delay, transition.delay); + XCTAssertEqual(mglLight.positionTransiton.duration, transition.duration); + + lightFromMGLlight = [mglLight mbglLight]; + + positionArray = { { 6, 180, 90 } }; + mbgl::style::Position position = { positionArray }; + mbgl::style::PropertyValue<mbgl::style::Position> positionProperty = { position }; + light.setPosition(positionProperty); + light.setPositionTransition(transitionOptions); + + XCTAssertEqual(positionArray, lightFromMGLlight.getPosition().asConstant().getSpherical()); + positionTransition = lightFromMGLlight.getPositionTransition(); + XCTAssert(positionTransition.delay && MGLTimeIntervalFromDuration(*positionTransition.delay) == transition.delay); + XCTAssert(positionTransition.duration && MGLTimeIntervalFromDuration(*positionTransition.duration) == transition.duration); + + } + + // color + { + mbgl::style::Light light; + MGLLight *mglLight = [[MGLLight alloc] initWithMBGLLight:&light]; + NSAssert([mglLight.color isKindOfClass:[MGLConstantStyleValue class]], @"mglLight.color isn’t a MGLConstantStyleValue."); + MGLColor *colorValue = ((MGLConstantStyleValue *)mglLight.color).rawValue; + auto color = light.getDefaultColor(); + const CGFloat *colorComponents = CGColorGetComponents(colorValue.CGColor); + + XCTAssert(color.r == colorComponents[0] && color.g == colorComponents[1] && color.b == colorComponents[2] && + color.a == colorComponents[3]); + XCTAssertEqual(mglLight.colorTransiton.delay, defaultTransition.delay); + XCTAssertEqual(mglLight.colorTransiton.duration, defaultTransition.duration); + + auto lightFromMGLlight = [mglLight mbglLight]; + + XCTAssertEqual(color, lightFromMGLlight.getColor().asConstant()); + auto colorTransition = lightFromMGLlight.getColorTransition(); + XCTAssert(colorTransition.delay && MGLTimeIntervalFromDuration(*colorTransition.delay) == defaultTransition.delay); + XCTAssert(colorTransition.duration && MGLTimeIntervalFromDuration(*colorTransition.duration) == defaultTransition.duration); + + MGLStyleValue<MGLColor *> *colorStyleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor blackColor]]; + mglLight.color = colorStyleValue; + mglLight.colorTransiton = transition; + + NSAssert([mglLight.color isKindOfClass:[MGLConstantStyleValue class]], @"mglLight.color isn’t a MGLConstantStyleValue."); + colorValue = ((MGLConstantStyleValue *)mglLight.color).rawValue; + + XCTAssertEqual([MGLColor blackColor], colorValue); + XCTAssertEqual(mglLight.colorTransiton.delay, transition.delay); + XCTAssertEqual(mglLight.colorTransiton.duration, transition.duration); + + mbgl::style::PropertyValue<mbgl::Color> colorProperty = { { 0, 0, 0, 1 } }; + light.setColor(colorProperty); + light.setColorTransition(transitionOptions); + + lightFromMGLlight = [mglLight mbglLight]; + + colorComponents = CGColorGetComponents(colorValue.CGColor); + color = lightFromMGLlight.getColor().asConstant(); + XCTAssertEqual(light.getColor().asConstant(),lightFromMGLlight.getColor().asConstant()); + colorTransition = lightFromMGLlight.getColorTransition(); + XCTAssert(colorTransition.delay && MGLTimeIntervalFromDuration(*colorTransition.delay) == transition.delay); + XCTAssert(colorTransition.duration && MGLTimeIntervalFromDuration(*colorTransition.duration) == transition.duration); + } + + // intensity + { + mbgl::style::Light light; + MGLLight *mglLight = [[MGLLight alloc] initWithMBGLLight:&light]; + NSAssert([mglLight.intensity isKindOfClass:[MGLConstantStyleValue class]], @"mglLight.intensity isn’t a MGLConstantStyleValue."); + NSNumber *intensityNumber = ((MGLConstantStyleValue *)mglLight.intensity).rawValue; + auto intensity = light.getDefaultIntensity(); + + XCTAssert(intensityNumber.floatValue == intensity); + XCTAssertEqual(mglLight.intensityTransition.delay, defaultTransition.delay); + XCTAssertEqual(mglLight.intensityTransition.duration, defaultTransition.duration); + + auto lightFromMGLlight = [mglLight mbglLight]; + + XCTAssertEqual(intensity, lightFromMGLlight.getIntensity().asConstant()); + auto intensityTransition = lightFromMGLlight.getIntensityTransition(); + XCTAssert(intensityTransition.delay && MGLTimeIntervalFromDuration(*intensityTransition.delay) == defaultTransition.delay); + XCTAssert(intensityTransition.duration && MGLTimeIntervalFromDuration(*intensityTransition.duration) == defaultTransition.duration); + + NSNumber *intensityValue = @0.4; + MGLStyleValue<NSNumber *> *intensityStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:intensityValue]; + mglLight.intensity = intensityStyleValue; + mglLight.intensityTransition = transition; + + NSAssert([mglLight.intensity isKindOfClass:[MGLConstantStyleValue class]], @"mglLight.intensity isn’t a MGLConstantStyleValue."); + intensityNumber = ((MGLConstantStyleValue *)mglLight.intensity).rawValue; + XCTAssert(intensityNumber.floatValue == intensityValue.floatValue); + XCTAssertEqual(mglLight.intensityTransition.delay, transition.delay); + XCTAssertEqual(mglLight.intensityTransition.duration, transition.duration); + + mbgl::style::PropertyValue<float> intensityProperty = { 0.4 }; + light.setIntensity(intensityProperty); + light.setIntensityTransition(transitionOptions); + + lightFromMGLlight = [mglLight mbglLight]; + + XCTAssertEqual(light.getIntensity().asConstant(), lightFromMGLlight.getIntensity().asConstant()); + intensityTransition = lightFromMGLlight.getIntensityTransition(); + XCTAssert(intensityTransition.delay && MGLTimeIntervalFromDuration(*intensityTransition.delay) == transition.delay); + XCTAssert(intensityTransition.duration && MGLTimeIntervalFromDuration(*intensityTransition.duration) == transition.duration); + + } + +} + +- (void)testValueAdditions { + MGLSphericalPosition position = MGLSphericalPositionMake(1.15, 210, 30); + + XCTAssertEqual([NSValue valueWithMGLSphericalPosition:position].MGLSphericalPositionValue.radial, position.radial); + XCTAssertEqual([NSValue valueWithMGLSphericalPosition:position].MGLSphericalPositionValue.azimuthal, position.azimuthal); + XCTAssertEqual([NSValue valueWithMGLSphericalPosition:position].MGLSphericalPositionValue.polar, position.polar); + XCTAssertEqual([NSValue valueWithMGLLightAnchor:MGLLightAnchorMap].MGLLightAnchorValue, MGLLightAnchorMap); + XCTAssertEqual([NSValue valueWithMGLLightAnchor:MGLLightAnchorViewport].MGLLightAnchorValue, MGLLightAnchorViewport); +} + +@end diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index ceb371118d..977dd1b418 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -2,7 +2,13 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONTRIBUTING.md](../../CONTRIBUTING.md) to get started. -## master +## 3.6.0 + +### Packaging + +* Xcode 8.0 or higher is now recommended for using this SDK. ([#8775](https://github.com/mapbox/mapbox-gl-native/pull/8775)) +* Fixed an issue in the static framework where localizations would never load. ([#9074](https://github.com/mapbox/mapbox-gl-native/pull/9074)) +* Updated MGLMapView’s logo view to display [the new Mapbox logo](https://www.mapbox.com/blog/new-mapbox-logo/). ([#8771](https://github.com/mapbox/mapbox-gl-native/pull/8771), [#8773](https://github.com/mapbox/mapbox-gl-native/pull/8773)) * The previously-deprecated support for style classes has been removed. For interface compatibility, the API methods remain, but they are now non-functional. * Added an `overlays` property to `MGLMapView`. ([#8617](https://github.com/mapbox/mapbox-gl-native/pull/8617)) @@ -16,26 +22,36 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Added class methods to MGLStyle that correspond to the new [Traffic Day and Traffic Night](https://www.mapbox.com/blog/live-traffic-maps/) styles. ([#6301](https://github.com/mapbox/mapbox-gl-native/pull/6301)) * MGLSymbolStyleLayer’s `iconImageName`, `iconScale`, `textFontSize`, `textOffset`, and `textRotation` properties can now be set to a source or composite function. ([#8544](https://github.com/mapbox/mapbox-gl-native/pull/8544), [#8590](https://github.com/mapbox/mapbox-gl-native/pull/8590), [#8592](https://github.com/mapbox/mapbox-gl-native/pull/8592), [#8593](https://github.com/mapbox/mapbox-gl-native/pull/8593)) * Fixed an issue where setting the `MGLVectorStyleLayer.predicate` property failed to take effect if the relevant source was not in use by a visible layer at the time. ([#8653](https://github.com/mapbox/mapbox-gl-native/pull/8653)) +* Fixed an issue preventing programmatically added style layers from appearing in already cached tiles. ([#8954](https://github.com/mapbox/mapbox-gl-native/pull/8954)) * Fixed an issue causing a composite function’s highest zoom level stop to be misinterpreted. ([#8613](https://github.com/mapbox/mapbox-gl-native/pull/8613), [#8790](https://github.com/mapbox/mapbox-gl-native/pull/8790)) * Fixed an issue where re-adding a layer that had been previously removed from a style would reset its paint properties. Moved initializers for `MGLTileSource`, `MGLStyleLayer`, and `MGLForegroundStyleLayer` to their concrete subclasses; because these classes were already intended for initialization only via concrete subclasses, this should have no developer impact. ([#8626](https://github.com/mapbox/mapbox-gl-native/pull/8626)) * Feature querying results now account for any changes to a feature’s size caused by a source or composite style function. ([#8665](https://github.com/mapbox/mapbox-gl-native/pull/8665)) +* Letter spacing is now disabled in Arabic text so that ligatures are drawn correctly. ([#9062](https://github.com/mapbox/mapbox-gl-native/pull/9062)) ### Annotations +* Added a new initializer to `MGLAnnotationView` so that it is possible to create a new instance with an associated annotation object. ([#9029](https://github.com/mapbox/mapbox-gl-native/pull/9029)) +* Added a new `rotatesToMatchCamera` property to `MGLAnnotationView` that, when set to true, causes the annotation view to rotate along with the map's rotation angle giving the appearance that the annoation view is pinned to the map. ([#9147](https://github.com/mapbox/mapbox-gl-native/pull/9147)) * Fixed an issue causing a view-backed annotation to disappear immediately instead of animating when the annotation’s `coordinate` property is set to a value outside the current viewport. ([#8565](https://github.com/mapbox/mapbox-gl-native/pull/8565)) * Fixed an issue in which `MGLMapView` overrode the tint colors of its annotation views. ([#8789](https://github.com/mapbox/mapbox-gl-native/pull/8789)) +* Fixed an issue causing annotation views to persist in the map’s annotation container view even after their associated annotations were removed. ([#9025](https://github.com/mapbox/mapbox-gl-native/pull/9025)) +* The `MGLPolyline.coordinate` and `MGLPolygon.coordinate` properties now return the midpoint and centroid, respectively, instead of the first coordinate. ([#8713](https://github.com/mapbox/mapbox-gl-native/pull/8713)) ### User interaction * Added a scale bar to `MGLMapView` that indicates the scale of the map. ([#7631](https://github.com/mapbox/mapbox-gl-native/pull/7631)) * 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)) +* Fixed an issue preventing the Mapbox Telemetry confirmation dialog from appearing when opened from within a map view in a modal view controller. ([#9027](https://github.com/mapbox/mapbox-gl-native/pull/9027)) +* Corrected the size of MGLMapView’s compass. ([#9060](https://github.com/mapbox/mapbox-gl-native/pull/9060)) +* The Improve This Map button in the attribution action sheet now leads to a feedback tool that matches MGLMapView’s rotation and pitch. `-[MGLAttributionInfo feedbackURLAtCenterCoordinate:zoomLevel:]` no longer respects the feedback URL specified in TileJSON. ([#9078](https://github.com/mapbox/mapbox-gl-native/pull/9078)) ### Other changes -* Xcode 8.0 or higher is now recommended for using this SDK. ([#8775](https://github.com/mapbox/mapbox-gl-native/pull/8775)) -* Updated MGLMapView’s logo view to display [the new Mapbox logo](https://www.mapbox.com/blog/new-mapbox-logo/). ([#8771](https://github.com/mapbox/mapbox-gl-native/pull/8771), [#8773](https://github.com/mapbox/mapbox-gl-native/pull/8773)) +* Fixed a crash that occurred when accessing the `MGLMultiPolygon.coordinate` property. ([#8713](https://github.com/mapbox/mapbox-gl-native/pull/8713)) * Fixed a crash or console spew when MGLMapView is initialized with a frame smaller than 64 points wide by 64 points tall. ([#8562](https://github.com/mapbox/mapbox-gl-native/pull/8562)) +* Fixed an issue that caused the compass and scale bar to underlap navigation and tab bars. ([#7716](https://github.com/mapbox/mapbox-gl-native/pull/7716)) * 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)) +* Improved CPU and battery performance while animating a tilted map’s camera in an area with many labels. ([#9031](https://github.com/mapbox/mapbox-gl-native/pull/9031)) * Fixed an issue rendering polylines that contain duplicate vertices. ([#8808](https://github.com/mapbox/mapbox-gl-native/pull/8808)) ## 3.5.4 - May 9, 2017 diff --git a/platform/ios/Mapbox-iOS-SDK-symbols.podspec b/platform/ios/Mapbox-iOS-SDK-symbols.podspec index f8b20777fa..3116ede9f5 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.6.0-alpha.1' + version = '3.6.0-beta.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 15658e6241..f6bc3030ab 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.6.0-alpha.1' + version = '3.6.0-beta.2' m.name = 'Mapbox-iOS-SDK' m.version = version diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m index 7cbe9d594f..7f3adfb78b 100644 --- a/platform/ios/app/MBXViewController.m +++ b/platform/ios/app/MBXViewController.m @@ -1396,19 +1396,20 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { - (NSString *)bestLanguageForUser { - NSArray *supportedLanguages = @[ @"en", @"es", @"fr", @"de", @"ru", @"zh" ]; - NSArray<NSString *> *preferredLanguages = [NSLocale preferredLanguages]; - NSString *bestLanguage; - - for (NSString *language in preferredLanguages) { - NSString *thisLanguage = [[NSLocale localeWithLocaleIdentifier:language] objectForKey:NSLocaleLanguageCode]; - if ([supportedLanguages containsObject:thisLanguage]) { - bestLanguage = thisLanguage; - break; + // https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview + NSArray *supportedLanguages = @[ @"ar", @"en", @"es", @"fr", @"de", @"pt", @"ru", @"zh", @"zh-Hans" ]; + NSArray<NSString *> *preferredLanguages = [NSBundle preferredLocalizationsFromArray:supportedLanguages forPreferences:[NSLocale preferredLanguages]]; + NSString *mostSpecificLanguage; + + for (NSString *language in preferredLanguages) + { + if (language.length > mostSpecificLanguage.length) + { + mostSpecificLanguage = language; } } - return bestLanguage ?: @"en"; + return mostSpecificLanguage ?: @"en"; } - (IBAction)startWorldTour diff --git a/platform/ios/docs/guides/Tile URL Templates.md b/platform/ios/docs/guides/Tile URL Templates.md new file mode 100644 index 0000000000..f61d2ea33a --- /dev/null +++ b/platform/ios/docs/guides/Tile URL Templates.md @@ -0,0 +1,99 @@ +<!-- + This file is generated. + Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. +--> +# Tile URL Templates + +`MGLTileSource` objects, specifically `MGLRasterSource` and `MGLVectorSource` +objects, can be created using an initializer that accepts an array of tile URL +templates. Tile URL templates are strings that specify the URLs of the vector +tiles or raster tile images to load. A template resembles an absolute URL, but +with any number of placeholder strings that the source evaluates based on the +tile it needs to load. For example: + +* `http://www.example.com/tiles/{z}/{x}/{y}.pbf` could be + evaluated as `http://www.example.com/tiles/14/6/9.pbf`. +* `http://www.example.com/tiles/{z}/{x}/{y}{ratio}.png` could be + evaluated as `http://www.example.com/tiles/14/6/9@2x.png`. + +Tile URL templates are also used to define tilesets in TileJSON manifests or +[`raster`](https://www.mapbox.com/mapbox-gl-js/style-spec/#sources-raster-tiles) +and +[`vector`](https://www.mapbox.com/mapbox-gl-js/style-spec/#sources-vector-tiles) +sources in style JSON files. See the +[TileJSON specification](https://github.com/mapbox/tilejson-spec/tree/master/2.2.0) +for information about tile URL templates in the context of a TileJSON or style +JSON file. + +Tile sources support the following placeholder strings in tile URL templates, +all of which are optional: + +<table> +<thead> +<tr><th>Placeholder string</th><th>Description</th></tr> +</thead> +<tbody> +<tr> + <td><code>{x}</code></td> + <td>The index of the tile along the map’s x axis according to Spherical + Mercator projection. If the value is 0, the tile’s left edge corresponds + to the 180th meridian west. If the value is 2<sup><var>z</var></sup>−1, + the tile’s right edge corresponds to the 180th meridian east.</td> +</tr> +<tr> + <td><code>{y}</code></td> + <td>The index of the tile along the map’s y axis according to Spherical + Mercator projection. If the value is 0, the tile’s tile edge corresponds + to arctan(sinh(π)), or approximately 85.0511 degrees north. If the value + is 2<sup><var>z</var></sup>−1, the tile’s bottom edge corresponds to + −arctan(sinh(π)), or approximately 85.0511 degrees south. The y axis is + inverted if the <code>options</code> parameter contains + <code>MGLTileSourceOptionTileCoordinateSystem</code> with a value of + <code>MGLTileCoordinateSystemTMS</code>.</td> +</tr> +<tr> + <td><code>{z}</code></td> + <td>The tile’s zoom level. At zoom level 0, each tile covers the entire + world map; at zoom level 1, it covers ¼ of the world; at zoom level 2, + <sup>1</sup>⁄<sub>16</sub> of the world, and so on. For tiles loaded by + a <code>MGLRasterSource</code> object, whether the tile zoom level + matches the map’s current zoom level depends on the value of the + source’s tile size as specified in the + <code>MGLTileSourceOptionTileSize</code> key of the <code>options</code> + parameter.</td> +</tr> +<tr> + <td><code>{bbox-epsg-3857}</code></td> + <td>The tile’s bounding box, expressed as a comma-separated list of the + tile’s western, southern, eastern, and northern extents according to + Spherical Mercator (EPSG:3857) projection. The bounding box is typically + used with map services conforming to the + <a href="http://www.opengeospatial.org/standards/wms">Web Map Service</a> + protocol.</td> +</tr> +<tr> + <td><code>{quadkey}</code></td> + <td>A quadkey indicating both the tile’s location and its zoom level. The + quadkey is typically used with + <a href="https://msdn.microsoft.com/en-us/library/bb259689.aspx">Bing Maps</a>. + </td> +</tr> +<tr> + <td><code>{ratio}</code></td> + <td>A suffix indicating the resolution of the tile image. The suffix is the + empty string for standard resolution displays and <code>@2x</code> for + Retina displays, including displays for which <code>UIScreen.scale</code> + is 3. + </td> +</tr> +<tr> + <td><code>{prefix}</code></td> + <td>Two hexadecimal digits chosen such that each visible tile has a + different prefix. The prefix is typically used for domain sharding.</td> +</tr> +</tbody> +</table> + +For more information about the `{x}`, `{y}`, and `{z}` placeholder strings, +consult the +[OpenStreetMap Wiki](https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames). diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index 215d31810d..b6d422d0fc 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -9,6 +9,13 @@ /* Begin PBXBuildFile section */ 1753ED421E53CE6F00A9FD90 /* MGLConversion.h in Headers */ = {isa = PBXBuildFile; fileRef = 1753ED411E53CE6F00A9FD90 /* MGLConversion.h */; }; 1753ED431E53CE6F00A9FD90 /* MGLConversion.h in Headers */ = {isa = PBXBuildFile; fileRef = 1753ED411E53CE6F00A9FD90 /* MGLConversion.h */; }; + 1F06668A1EC64F8E001C16D7 /* MGLLight.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F0666881EC64F8E001C16D7 /* MGLLight.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 1F06668D1EC64F8E001C16D7 /* MGLLight.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1F0666891EC64F8E001C16D7 /* MGLLight.mm */; }; + 1F7454921ECBB42C00021D39 /* MGLLight.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1F0666891EC64F8E001C16D7 /* MGLLight.mm */; }; + 1F7454931ECBB43F00021D39 /* MGLLight.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F0666881EC64F8E001C16D7 /* MGLLight.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 1F7454961ECD450D00021D39 /* MGLLight_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F7454941ECD450D00021D39 /* MGLLight_Private.h */; }; + 1F7454971ECD450D00021D39 /* MGLLight_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F7454941ECD450D00021D39 /* MGLLight_Private.h */; }; + 1F7454A91ED08AB400021D39 /* MGLLightTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1F7454A61ED08AB400021D39 /* MGLLightTest.mm */; }; 1F95931D1E6DE2E900D5B294 /* MGLNSDateAdditionsTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1F95931C1E6DE2E900D5B294 /* MGLNSDateAdditionsTests.mm */; }; 30E578171DAA85520050F07E /* UIImage+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 30E578111DAA7D690050F07E /* UIImage+MGLAdditions.h */; }; 30E578181DAA85520050F07E /* UIImage+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 30E578111DAA7D690050F07E /* UIImage+MGLAdditions.h */; }; @@ -165,6 +172,7 @@ 408AA8571DAEDA1700022900 /* NSDictionary+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 408AA8551DAEDA0800022900 /* NSDictionary+MGLAdditions.h */; }; 408AA8581DAEDA1E00022900 /* NSDictionary+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 408AA8561DAEDA0800022900 /* NSDictionary+MGLAdditions.mm */; }; 408AA8591DAEDA1E00022900 /* NSDictionary+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 408AA8561DAEDA0800022900 /* NSDictionary+MGLAdditions.mm */; }; + 409D0A0D1ED614CE00C95D0C /* MGLAnnotationViewIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 409D0A0C1ED614CE00C95D0C /* MGLAnnotationViewIntegrationTests.swift */; }; 409F43FD1E9E781C0048729D /* MGLMapViewDelegateIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 409F43FC1E9E781C0048729D /* MGLMapViewDelegateIntegrationTests.swift */; }; 40CF6DBB1DAC3C6600A4D18B /* MGLShape_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 40CF6DBA1DAC3C1800A4D18B /* MGLShape_Private.h */; }; 40CFA6511D7875BB008103BD /* MGLShapeSourceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 40CFA6501D787579008103BD /* MGLShapeSourceTests.mm */; }; @@ -193,6 +201,8 @@ 7E016D861D9E890300A29A21 /* MGLPolygon+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 7E016D831D9E890300A29A21 /* MGLPolygon+MGLAdditions.m */; }; 7E016D871D9E890300A29A21 /* MGLPolygon+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 7E016D831D9E890300A29A21 /* MGLPolygon+MGLAdditions.m */; }; 920A3E5D1E6F995200C16EFC /* MGLSourceQueryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 920A3E5C1E6F995200C16EFC /* MGLSourceQueryTests.m */; }; + 960D0C361ECF5AAF008E151F /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 960D0C351ECF5AAF008E151F /* Images.xcassets */; }; + 960D0C371ECF5AAF008E151F /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 960D0C351ECF5AAF008E151F /* Images.xcassets */; }; 9620BB381E69FE1700705A1D /* MGLSDKUpdateChecker.h in Headers */ = {isa = PBXBuildFile; fileRef = 9620BB361E69FE1700705A1D /* MGLSDKUpdateChecker.h */; }; 9620BB391E69FE1700705A1D /* MGLSDKUpdateChecker.h in Headers */ = {isa = PBXBuildFile; fileRef = 9620BB361E69FE1700705A1D /* MGLSDKUpdateChecker.h */; }; 9620BB3A1E69FE1700705A1D /* MGLSDKUpdateChecker.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9620BB371E69FE1700705A1D /* MGLSDKUpdateChecker.mm */; }; @@ -326,15 +336,6 @@ DA88485C1CBAFB9800AB86E3 /* MGLFaux3DUserLocationAnnotationView.h in Headers */ = {isa = PBXBuildFile; fileRef = DA88484D1CBAFB9800AB86E3 /* MGLFaux3DUserLocationAnnotationView.h */; }; DA88485D1CBAFB9800AB86E3 /* MGLFaux3DUserLocationAnnotationView.m in Sources */ = {isa = PBXBuildFile; fileRef = DA88484E1CBAFB9800AB86E3 /* MGLFaux3DUserLocationAnnotationView.m */; }; DA8848601CBAFC2E00AB86E3 /* Mapbox.h in Headers */ = {isa = PBXBuildFile; fileRef = DA88485E1CBAFC2E00AB86E3 /* Mapbox.h */; settings = {ATTRIBUTES = (Public, ); }; }; - DA88486D1CBAFCC100AB86E3 /* Compass.png in Resources */ = {isa = PBXBuildFile; fileRef = DA8848631CBAFCC100AB86E3 /* Compass.png */; }; - DA88486E1CBAFCC100AB86E3 /* Compass@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA8848641CBAFCC100AB86E3 /* Compass@2x.png */; }; - DA88486F1CBAFCC100AB86E3 /* Compass@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA8848651CBAFCC100AB86E3 /* Compass@3x.png */; }; - DA8848701CBAFCC100AB86E3 /* default_marker.png in Resources */ = {isa = PBXBuildFile; fileRef = DA8848661CBAFCC100AB86E3 /* default_marker.png */; }; - DA8848711CBAFCC100AB86E3 /* default_marker@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA8848671CBAFCC100AB86E3 /* default_marker@2x.png */; }; - DA8848721CBAFCC100AB86E3 /* default_marker@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA8848681CBAFCC100AB86E3 /* default_marker@3x.png */; }; - DA8848731CBAFCC100AB86E3 /* mapbox.png in Resources */ = {isa = PBXBuildFile; fileRef = DA8848691CBAFCC100AB86E3 /* mapbox.png */; }; - DA8848741CBAFCC100AB86E3 /* mapbox@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA88486A1CBAFCC100AB86E3 /* mapbox@2x.png */; }; - DA8848751CBAFCC100AB86E3 /* mapbox@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA88486B1CBAFCC100AB86E3 /* mapbox@3x.png */; }; DA8848841CBB033F00AB86E3 /* FABAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848801CBB033F00AB86E3 /* FABAttributes.h */; }; DA8848851CBB033F00AB86E3 /* FABKitProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848811CBB033F00AB86E3 /* FABKitProtocol.h */; }; DA8848861CBB033F00AB86E3 /* Fabric+FABKits.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848821CBB033F00AB86E3 /* Fabric+FABKits.h */; }; @@ -348,15 +349,6 @@ DA8933DB1CCD31D400E68420 /* Foundation.strings in Resources */ = {isa = PBXBuildFile; fileRef = DA8933BA1CCD2CA100E68420 /* Foundation.strings */; }; DA8933DC1CCD31D400E68420 /* Foundation.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = DA8933BD1CCD2CAD00E68420 /* Foundation.stringsdict */; }; DA8933E01CCD31DF00E68420 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = DA89339F1CCC951200E68420 /* Localizable.strings */; }; - DA8933E11CCD31DF00E68420 /* Compass.png in Resources */ = {isa = PBXBuildFile; fileRef = DA8848631CBAFCC100AB86E3 /* Compass.png */; }; - DA8933E21CCD31DF00E68420 /* Compass@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA8848641CBAFCC100AB86E3 /* Compass@2x.png */; }; - DA8933E31CCD31DF00E68420 /* Compass@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA8848651CBAFCC100AB86E3 /* Compass@3x.png */; }; - DA8933E41CCD31DF00E68420 /* default_marker.png in Resources */ = {isa = PBXBuildFile; fileRef = DA8848661CBAFCC100AB86E3 /* default_marker.png */; }; - DA8933E51CCD31DF00E68420 /* default_marker@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA8848671CBAFCC100AB86E3 /* default_marker@2x.png */; }; - DA8933E61CCD31DF00E68420 /* default_marker@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA8848681CBAFCC100AB86E3 /* default_marker@3x.png */; }; - DA8933E71CCD31DF00E68420 /* mapbox.png in Resources */ = {isa = PBXBuildFile; fileRef = DA8848691CBAFCC100AB86E3 /* mapbox.png */; }; - DA8933E81CCD31DF00E68420 /* mapbox@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA88486A1CBAFCC100AB86E3 /* mapbox@2x.png */; }; - DA8933E91CCD31DF00E68420 /* mapbox@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA88486B1CBAFCC100AB86E3 /* mapbox@3x.png */; }; DA8933F01CCD387900E68420 /* strip-frameworks.sh in Resources */ = {isa = PBXBuildFile; fileRef = DA8933EF1CCD387900E68420 /* strip-frameworks.sh */; }; DA8963371CC549A100684375 /* glyphs in Resources */ = {isa = PBXBuildFile; fileRef = DA8963331CC549A100684375 /* glyphs */; }; DA8963381CC549A100684375 /* sprites in Resources */ = {isa = PBXBuildFile; fileRef = DA8963341CC549A100684375 /* sprites */; }; @@ -545,6 +537,10 @@ /* Begin PBXFileReference section */ 1753ED411E53CE6F00A9FD90 /* MGLConversion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLConversion.h; sourceTree = "<group>"; }; + 1F0666881EC64F8E001C16D7 /* MGLLight.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLLight.h; sourceTree = "<group>"; }; + 1F0666891EC64F8E001C16D7 /* MGLLight.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLLight.mm; sourceTree = "<group>"; }; + 1F7454941ECD450D00021D39 /* MGLLight_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLLight_Private.h; sourceTree = "<group>"; }; + 1F7454A61ED08AB400021D39 /* MGLLightTest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLLightTest.mm; path = ../../darwin/test/MGLLightTest.mm; sourceTree = "<group>"; }; 1F95931C1E6DE2E900D5B294 /* MGLNSDateAdditionsTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLNSDateAdditionsTests.mm; path = ../../darwin/test/MGLNSDateAdditionsTests.mm; sourceTree = "<group>"; }; 20DABE861DF78148007AC5FF /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Foundation.strings"; sourceTree = "<group>"; }; 20DABE881DF78148007AC5FF /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = "<group>"; }; @@ -644,6 +640,7 @@ 4085AF081D933DEA00F11B22 /* MGLTileSetTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLTileSetTests.mm; path = ../../darwin/test/MGLTileSetTests.mm; sourceTree = "<group>"; }; 408AA8551DAEDA0800022900 /* NSDictionary+MGLAdditions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+MGLAdditions.h"; sourceTree = "<group>"; }; 408AA8561DAEDA0800022900 /* NSDictionary+MGLAdditions.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSDictionary+MGLAdditions.mm"; sourceTree = "<group>"; }; + 409D0A0C1ED614CE00C95D0C /* MGLAnnotationViewIntegrationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MGLAnnotationViewIntegrationTests.swift; sourceTree = "<group>"; }; 409F43FC1E9E781C0048729D /* MGLMapViewDelegateIntegrationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MGLMapViewDelegateIntegrationTests.swift; sourceTree = "<group>"; }; 40CF6DBA1DAC3C1800A4D18B /* MGLShape_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLShape_Private.h; sourceTree = "<group>"; }; 40CFA6501D787579008103BD /* MGLShapeSourceTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLShapeSourceTests.mm; path = ../../darwin/test/MGLShapeSourceTests.mm; sourceTree = "<group>"; }; @@ -668,6 +665,7 @@ 7E016D821D9E890300A29A21 /* MGLPolygon+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MGLPolygon+MGLAdditions.h"; sourceTree = "<group>"; }; 7E016D831D9E890300A29A21 /* MGLPolygon+MGLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MGLPolygon+MGLAdditions.m"; sourceTree = "<group>"; }; 920A3E5C1E6F995200C16EFC /* MGLSourceQueryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLSourceQueryTests.m; path = ../../darwin/test/MGLSourceQueryTests.m; sourceTree = "<group>"; }; + 960D0C351ECF5AAF008E151F /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; }; 9620BB361E69FE1700705A1D /* MGLSDKUpdateChecker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLSDKUpdateChecker.h; sourceTree = "<group>"; }; 9620BB371E69FE1700705A1D /* MGLSDKUpdateChecker.mm */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = MGLSDKUpdateChecker.mm; sourceTree = "<group>"; }; 9660916B1E5BBFD700A9A03B /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; }; @@ -836,15 +834,6 @@ DA88484D1CBAFB9800AB86E3 /* MGLFaux3DUserLocationAnnotationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLFaux3DUserLocationAnnotationView.h; sourceTree = "<group>"; }; DA88484E1CBAFB9800AB86E3 /* MGLFaux3DUserLocationAnnotationView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLFaux3DUserLocationAnnotationView.m; sourceTree = "<group>"; }; DA88485E1CBAFC2E00AB86E3 /* Mapbox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Mapbox.h; path = src/Mapbox.h; sourceTree = SOURCE_ROOT; }; - DA8848631CBAFCC100AB86E3 /* Compass.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Compass.png; sourceTree = "<group>"; }; - DA8848641CBAFCC100AB86E3 /* Compass@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Compass@2x.png"; sourceTree = "<group>"; }; - DA8848651CBAFCC100AB86E3 /* Compass@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Compass@3x.png"; sourceTree = "<group>"; }; - DA8848661CBAFCC100AB86E3 /* default_marker.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = default_marker.png; sourceTree = "<group>"; }; - DA8848671CBAFCC100AB86E3 /* default_marker@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "default_marker@2x.png"; sourceTree = "<group>"; }; - DA8848681CBAFCC100AB86E3 /* default_marker@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "default_marker@3x.png"; sourceTree = "<group>"; }; - DA8848691CBAFCC100AB86E3 /* mapbox.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = mapbox.png; sourceTree = "<group>"; }; - DA88486A1CBAFCC100AB86E3 /* mapbox@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "mapbox@2x.png"; sourceTree = "<group>"; }; - DA88486B1CBAFCC100AB86E3 /* mapbox@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "mapbox@3x.png"; sourceTree = "<group>"; }; DA8848801CBB033F00AB86E3 /* FABAttributes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FABAttributes.h; sourceTree = "<group>"; }; DA8848811CBB033F00AB86E3 /* FABKitProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FABKitProtocol.h; sourceTree = "<group>"; }; DA8848821CBB033F00AB86E3 /* Fabric+FABKits.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Fabric+FABKits.h"; sourceTree = "<group>"; }; @@ -1039,6 +1028,9 @@ 35599DB81D46AD7F0048254D /* Categories */, 353933F01D3FB6BA003F57D7 /* Layers */, 35136D491D4277EA00C20EFD /* Sources */, + 1F0666881EC64F8E001C16D7 /* MGLLight.h */, + 1F0666891EC64F8E001C16D7 /* MGLLight.mm */, + 1F7454941ECD450D00021D39 /* MGLLight_Private.h */, DAAF72291DA903C700312FA4 /* MGLStyleValue.h */, DAAF722A1DA903C700312FA4 /* MGLStyleValue_Private.h */, 35599DEA1D46F14E0048254D /* MGLStyleValue.mm */, @@ -1069,6 +1061,7 @@ children = ( 3575798F1D513EF1000B822E /* Layers */, 40CFA64E1D78754A008103BD /* Sources */, + 1F7454A61ED08AB400021D39 /* MGLLightTest.mm */, 357F09091DF84F3800941873 /* MGLStyleValueTests.h */, 3599A3E51DF708BC00E77FB2 /* MGLStyleValueTests.m */, DA2207BE1DC0805F0002F84D /* MGLStyleValueTests.swift */, @@ -1128,6 +1121,7 @@ isa = PBXGroup; children = ( 409F43FC1E9E781C0048729D /* MGLMapViewDelegateIntegrationTests.swift */, + 409D0A0C1ED614CE00C95D0C /* MGLAnnotationViewIntegrationTests.swift */, ); name = "Swift Integration"; sourceTree = "<group>"; @@ -1348,17 +1342,9 @@ DA8848621CBAFCC100AB86E3 /* Kit Resources */ = { isa = PBXGroup; children = ( + 960D0C351ECF5AAF008E151F /* Images.xcassets */, DA89339F1CCC951200E68420 /* Localizable.strings */, DAC49C5F1CD02BC9009E1AA3 /* Localizable.stringsdict */, - DA8848631CBAFCC100AB86E3 /* Compass.png */, - DA8848641CBAFCC100AB86E3 /* Compass@2x.png */, - DA8848651CBAFCC100AB86E3 /* Compass@3x.png */, - DA8848661CBAFCC100AB86E3 /* default_marker.png */, - DA8848671CBAFCC100AB86E3 /* default_marker@2x.png */, - DA8848681CBAFCC100AB86E3 /* default_marker@3x.png */, - DA8848691CBAFCC100AB86E3 /* mapbox.png */, - DA88486A1CBAFCC100AB86E3 /* mapbox@2x.png */, - DA88486B1CBAFCC100AB86E3 /* mapbox@3x.png */, DA8933EF1CCD387900E68420 /* strip-frameworks.sh */, 40599F001DEE1B2400182B5D /* api_mapbox_staging.der */, 40599F011DEE1B2400182B5D /* api_mapbox_com-digicert.der */, @@ -1657,6 +1643,7 @@ DA8847F01CBAFA5100AB86E3 /* MGLAnnotation.h in Headers */, 7E016D841D9E890300A29A21 /* MGLPolygon+MGLAdditions.h in Headers */, 400533011DB0862B0069F638 /* NSArray+MGLAdditions.h in Headers */, + 1F06668A1EC64F8E001C16D7 /* MGLLight.h in Headers */, 4049C29D1DB6CD6C00B3F799 /* MGLPointCollection.h in Headers */, 40CF6DBB1DAC3C6600A4D18B /* MGLShape_Private.h in Headers */, 4018B1CA1CDC288E00F666AF /* MGLAnnotationView.h in Headers */, @@ -1705,6 +1692,7 @@ 353933F21D3FB753003F57D7 /* MGLCircleStyleLayer.h in Headers */, DA8847F31CBAFA5100AB86E3 /* MGLMultiPoint.h in Headers */, 30E578171DAA85520050F07E /* UIImage+MGLAdditions.h in Headers */, + 1F7454961ECD450D00021D39 /* MGLLight_Private.h in Headers */, DAD1656C1CF41981001FF4B9 /* MGLFeature.h in Headers */, 40EDA1C01CFE0E0200D9EA68 /* MGLAnnotationContainerView.h in Headers */, 9620BB381E69FE1700705A1D /* MGLSDKUpdateChecker.h in Headers */, @@ -1783,6 +1771,7 @@ 35CE61831D4165D9004F2359 /* UIColor+MGLAdditions.h in Headers */, DABFB8671CBE99E500D62B32 /* MGLPolygon.h in Headers */, 404C26E81D89C55D000AA13D /* MGLTileSource_Private.h in Headers */, + 1F7454931ECBB43F00021D39 /* MGLLight.h in Headers */, DAAF722C1DA903C700312FA4 /* MGLStyleValue.h in Headers */, DABFB8651CBE99E500D62B32 /* MGLOverlay.h in Headers */, 35E79F211D41266300957B9E /* MGLStyleLayer_Private.h in Headers */, @@ -1816,6 +1805,7 @@ DA35A2BC1CCA9A6900E826B2 /* MGLClockDirectionFormatter.h in Headers */, 35D13AC41D3D19DD00AFB4E0 /* MGLFillStyleLayer.h in Headers */, DABFB86E1CBE9A0F00D62B32 /* MGLCalloutView.h in Headers */, + 1F7454971ECD450D00021D39 /* MGLLight_Private.h in Headers */, DABFB8601CBE99E500D62B32 /* MGLMapCamera.h in Headers */, DA737EE21D056A4E005BDA16 /* MGLMapViewDelegate.h in Headers */, DAF0D8191DFE6B2800B28378 /* MGLAttributionInfo_Private.h in Headers */, @@ -2073,18 +2063,10 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - DA8848731CBAFCC100AB86E3 /* mapbox.png in Resources */, DA8933BC1CCD2CA100E68420 /* Foundation.strings in Resources */, - DA8848741CBAFCC100AB86E3 /* mapbox@2x.png in Resources */, DA8933A31CCC95B000E68420 /* Localizable.strings in Resources */, - DA88486D1CBAFCC100AB86E3 /* Compass.png in Resources */, - DA8848721CBAFCC100AB86E3 /* default_marker@3x.png in Resources */, - DA88486F1CBAFCC100AB86E3 /* Compass@3x.png in Resources */, - DA88486E1CBAFCC100AB86E3 /* Compass@2x.png in Resources */, - DA8848701CBAFCC100AB86E3 /* default_marker.png in Resources */, - DA8848711CBAFCC100AB86E3 /* default_marker@2x.png in Resources */, + 960D0C361ECF5AAF008E151F /* Images.xcassets in Resources */, DA8933F01CCD387900E68420 /* strip-frameworks.sh in Resources */, - DA8848751CBAFCC100AB86E3 /* mapbox@3x.png in Resources */, DAC49C5C1CD02BC9009E1AA3 /* Localizable.stringsdict in Resources */, DA8933BF1CCD2CAD00E68420 /* Foundation.stringsdict in Resources */, 408982E91DEE208200754016 /* api_mapbox_staging.der in Resources */, @@ -2098,16 +2080,8 @@ buildActionMask = 2147483647; files = ( DA8933E01CCD31DF00E68420 /* Localizable.strings in Resources */, - DA8933E11CCD31DF00E68420 /* Compass.png in Resources */, - DA8933E21CCD31DF00E68420 /* Compass@2x.png in Resources */, - DA8933E31CCD31DF00E68420 /* Compass@3x.png in Resources */, - DA8933E41CCD31DF00E68420 /* default_marker.png in Resources */, - DA8933E51CCD31DF00E68420 /* default_marker@2x.png in Resources */, - DA8933E61CCD31DF00E68420 /* default_marker@3x.png in Resources */, - DA8933E71CCD31DF00E68420 /* mapbox.png in Resources */, - DA8933E81CCD31DF00E68420 /* mapbox@2x.png in Resources */, - DA8933E91CCD31DF00E68420 /* mapbox@3x.png in Resources */, DA8933DB1CCD31D400E68420 /* Foundation.strings in Resources */, + 960D0C371ECF5AAF008E151F /* Images.xcassets in Resources */, DA8933DC1CCD31D400E68420 /* Foundation.stringsdict in Resources */, DAC49C5D1CD02BC9009E1AA3 /* Localizable.stringsdict in Resources */, 40599F0C1DEE1B7600182B5D /* api_mapbox_staging.der in Resources */, @@ -2166,6 +2140,7 @@ DAE7DEC21E245455007505A6 /* MGLNSStringAdditionsTests.m in Sources */, 4085AF091D933DEA00F11B22 /* MGLTileSetTests.mm in Sources */, DAEDC4341D603417000224FF /* MGLAttributionInfoTests.m in Sources */, + 1F7454A91ED08AB400021D39 /* MGLLightTest.mm in Sources */, 357579851D502AF5000B822E /* MGLSymbolStyleLayerTests.mm in Sources */, 357579871D502AFE000B822E /* MGLLineStyleLayerTests.mm in Sources */, 357579891D502B06000B822E /* MGLCircleStyleLayerTests.mm in Sources */, @@ -2176,6 +2151,7 @@ 1F95931D1E6DE2E900D5B294 /* MGLNSDateAdditionsTests.mm in Sources */, DD58A4C61D822BD000E1F038 /* MGLExpressionTests.mm in Sources */, 3575798B1D502B0C000B822E /* MGLBackgroundStyleLayerTests.mm in Sources */, + 409D0A0D1ED614CE00C95D0C /* MGLAnnotationViewIntegrationTests.swift in Sources */, DA2E88621CC0382C00F24E7B /* MGLOfflinePackTests.m in Sources */, 55E2AD131E5B125400E8C587 /* MGLOfflineStorageTests.mm in Sources */, 920A3E5D1E6F995200C16EFC /* MGLSourceQueryTests.m in Sources */, @@ -2221,6 +2197,7 @@ 3566C76E1D4A8DFA008152BC /* MGLRasterSource.mm in Sources */, DA88488C1CBB037E00AB86E3 /* SMCalloutView.m in Sources */, 35136D4E1D4277FC00C20EFD /* MGLSource.mm in Sources */, + 1F06668D1EC64F8E001C16D7 /* MGLLight.mm in Sources */, DA35A2B81CCA9A5D00E826B2 /* MGLClockDirectionFormatter.m in Sources */, DAD1657A1CF4CDFF001FF4B9 /* MGLShapeCollection.mm in Sources */, 35136D451D42275100C20EFD /* MGLSymbolStyleLayer.mm in Sources */, @@ -2328,6 +2305,7 @@ DAA4E4321CBB730400178DFB /* MGLMapView.mm in Sources */, DAA4E41E1CBB730400178DFB /* MGLMapCamera.mm in Sources */, FA68F14E1E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.mm in Sources */, + 1F7454921ECBB42C00021D39 /* MGLLight.mm in Sources */, 404C26E51D89B877000AA13D /* MGLTileSource.mm in Sources */, 355AE0021E9281DA00F3939D /* MGLScaleBar.mm in Sources */, 4018B1C81CDC287F00F666AF /* MGLAnnotationView.mm in Sources */, @@ -2681,7 +2659,10 @@ baseConfigurationReference = 55D8C9941D0F133500F42F10 /* config.xcconfig */; buildSettings = { CLANG_ENABLE_MODULES = YES; - HEADER_SEARCH_PATHS = "$(mbgl_core_INCLUDE_DIRECTORIES)"; + HEADER_SEARCH_PATHS = ( + "$(mbgl_core_INCLUDE_DIRECTORIES)", + "$(polylabel_INCLUDE_DIRECTORIES)", + ); INFOPLIST_FILE = test/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; @@ -2705,7 +2686,10 @@ baseConfigurationReference = 55D8C9941D0F133500F42F10 /* config.xcconfig */; buildSettings = { CLANG_ENABLE_MODULES = YES; - HEADER_SEARCH_PATHS = "$(mbgl_core_INCLUDE_DIRECTORIES)"; + HEADER_SEARCH_PATHS = ( + "$(mbgl_core_INCLUDE_DIRECTORIES)", + "$(polylabel_INCLUDE_DIRECTORIES)", + ); INFOPLIST_FILE = test/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; @@ -2733,7 +2717,10 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - HEADER_SEARCH_PATHS = "$(mbgl_core_INCLUDE_DIRECTORIES)"; + HEADER_SEARCH_PATHS = ( + "$(mbgl_core_INCLUDE_DIRECTORIES)", + "$(polylabel_INCLUDE_DIRECTORIES)", + ); INFOPLIST_FILE = framework/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; @@ -2767,7 +2754,10 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - HEADER_SEARCH_PATHS = "$(mbgl_core_INCLUDE_DIRECTORIES)"; + HEADER_SEARCH_PATHS = ( + "$(mbgl_core_INCLUDE_DIRECTORIES)", + "$(polylabel_INCLUDE_DIRECTORIES)", + ); INFOPLIST_FILE = framework/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; @@ -2818,7 +2808,10 @@ baseConfigurationReference = 55D8C9941D0F133500F42F10 /* config.xcconfig */; buildSettings = { BITCODE_GENERATION_MODE = bitcode; - HEADER_SEARCH_PATHS = "$(mbgl_core_INCLUDE_DIRECTORIES)"; + HEADER_SEARCH_PATHS = ( + "$(mbgl_core_INCLUDE_DIRECTORIES)", + "$(polylabel_INCLUDE_DIRECTORIES)", + ); OTHER_CPLUSPLUSFLAGS = ( "$(OTHER_CFLAGS)", "$(sqlite_cflags)", @@ -2844,7 +2837,10 @@ baseConfigurationReference = 55D8C9941D0F133500F42F10 /* config.xcconfig */; buildSettings = { BITCODE_GENERATION_MODE = bitcode; - HEADER_SEARCH_PATHS = "$(mbgl_core_INCLUDE_DIRECTORIES)"; + HEADER_SEARCH_PATHS = ( + "$(mbgl_core_INCLUDE_DIRECTORIES)", + "$(polylabel_INCLUDE_DIRECTORIES)", + ); OTHER_CPLUSPLUSFLAGS = ( "$(OTHER_CFLAGS)", "$(sqlite_cflags)", diff --git a/platform/ios/jazzy.yml b/platform/ios/jazzy.yml index e0ce29beba..31380faa2c 100644 --- a/platform/ios/jazzy.yml +++ b/platform/ios/jazzy.yml @@ -23,6 +23,7 @@ custom_categories: - Working with Mapbox Studio - Working with GeoJSON Data - For Style Authors + - Tile URL Templates - Info.plist Keys - Gesture Recognizers - name: Maps @@ -59,6 +60,7 @@ custom_categories: children: - MGLStyle - MGLStyleValue + - MGLLight - name: Style Primitives children: - MGLFeature diff --git a/platform/ios/originals/compass.sketch b/platform/ios/originals/compass.sketch Binary files differnew file mode 100644 index 0000000000..f5b848cd96 --- /dev/null +++ b/platform/ios/originals/compass.sketch diff --git a/platform/ios/resources/Compass.png b/platform/ios/resources/Compass.png Binary files differdeleted file mode 100644 index 08bed0591b..0000000000 --- a/platform/ios/resources/Compass.png +++ /dev/null diff --git a/platform/ios/resources/Compass@2x.png b/platform/ios/resources/Compass@2x.png Binary files differdeleted file mode 100644 index 8473a2d1ec..0000000000 --- a/platform/ios/resources/Compass@2x.png +++ /dev/null diff --git a/platform/ios/resources/Compass@3x.png b/platform/ios/resources/Compass@3x.png Binary files differdeleted file mode 100644 index 9cf66ca483..0000000000 --- a/platform/ios/resources/Compass@3x.png +++ /dev/null diff --git a/platform/ios/resources/Images.xcassets/Compass.imageset/Contents.json b/platform/ios/resources/Images.xcassets/Compass.imageset/Contents.json new file mode 100644 index 0000000000..6065a93b4e --- /dev/null +++ b/platform/ios/resources/Images.xcassets/Compass.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "compass.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +}
\ No newline at end of file diff --git a/platform/ios/resources/Images.xcassets/Compass.imageset/compass.pdf b/platform/ios/resources/Images.xcassets/Compass.imageset/compass.pdf Binary files differnew file mode 100644 index 0000000000..2048f96089 --- /dev/null +++ b/platform/ios/resources/Images.xcassets/Compass.imageset/compass.pdf diff --git a/platform/ios/resources/Images.xcassets/Contents.json b/platform/ios/resources/Images.xcassets/Contents.json new file mode 100644 index 0000000000..da4a164c91 --- /dev/null +++ b/platform/ios/resources/Images.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +}
\ No newline at end of file diff --git a/platform/ios/resources/Images.xcassets/default_marker.imageset/Contents.json b/platform/ios/resources/Images.xcassets/default_marker.imageset/Contents.json new file mode 100644 index 0000000000..9bef658a11 --- /dev/null +++ b/platform/ios/resources/Images.xcassets/default_marker.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "default_marker.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +}
\ No newline at end of file diff --git a/platform/ios/resources/Images.xcassets/default_marker.imageset/default_marker.pdf b/platform/ios/resources/Images.xcassets/default_marker.imageset/default_marker.pdf Binary files differnew file mode 100644 index 0000000000..4e2e332301 --- /dev/null +++ b/platform/ios/resources/Images.xcassets/default_marker.imageset/default_marker.pdf diff --git a/platform/ios/resources/Images.xcassets/mapbox.imageset/Contents.json b/platform/ios/resources/Images.xcassets/mapbox.imageset/Contents.json new file mode 100644 index 0000000000..b49c53da84 --- /dev/null +++ b/platform/ios/resources/Images.xcassets/mapbox.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "mapbox.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +}
\ No newline at end of file diff --git a/platform/ios/resources/Images.xcassets/mapbox.imageset/mapbox.pdf b/platform/ios/resources/Images.xcassets/mapbox.imageset/mapbox.pdf Binary files differnew file mode 100644 index 0000000000..45111c31a6 --- /dev/null +++ b/platform/ios/resources/Images.xcassets/mapbox.imageset/mapbox.pdf diff --git a/platform/ios/resources/default_marker.png b/platform/ios/resources/default_marker.png Binary files differdeleted file mode 100644 index b112096c18..0000000000 --- a/platform/ios/resources/default_marker.png +++ /dev/null diff --git a/platform/ios/resources/default_marker@2x.png b/platform/ios/resources/default_marker@2x.png Binary files differdeleted file mode 100644 index d05c82bfe2..0000000000 --- a/platform/ios/resources/default_marker@2x.png +++ /dev/null diff --git a/platform/ios/resources/default_marker@3x.png b/platform/ios/resources/default_marker@3x.png Binary files differdeleted file mode 100644 index 703b172c15..0000000000 --- a/platform/ios/resources/default_marker@3x.png +++ /dev/null diff --git a/platform/ios/resources/mapbox.png b/platform/ios/resources/mapbox.png Binary files differdeleted file mode 100644 index 00bc897a58..0000000000 --- a/platform/ios/resources/mapbox.png +++ /dev/null diff --git a/platform/ios/resources/mapbox@2x.png b/platform/ios/resources/mapbox@2x.png Binary files differdeleted file mode 100644 index 206ed5883e..0000000000 --- a/platform/ios/resources/mapbox@2x.png +++ /dev/null diff --git a/platform/ios/resources/mapbox@3x.png b/platform/ios/resources/mapbox@3x.png Binary files differdeleted file mode 100644 index efd631b587..0000000000 --- a/platform/ios/resources/mapbox@3x.png +++ /dev/null diff --git a/platform/ios/src/MGLAPIClient.m b/platform/ios/src/MGLAPIClient.m index 22ee5c55f5..124d436197 100644 --- a/platform/ios/src/MGLAPIClient.m +++ b/platform/ios/src/MGLAPIClient.m @@ -117,7 +117,7 @@ static NSString * const MGLAPIClientHTTPMethodPost = @"POST"; - (void)loadCertificate:(NSData **)certificate withResource:(NSString *)resource { NSBundle *frameworkBundle = [NSBundle mgl_frameworkBundle]; - NSString *cerPath = [frameworkBundle pathForResource:resource ofType:@"der" inDirectory:frameworkBundle.mgl_resourcesDirectory]; + NSString *cerPath = [frameworkBundle pathForResource:resource ofType:@"der"]; if (cerPath != nil) { *certificate = [NSData dataWithContentsOfFile:cerPath]; } diff --git a/platform/ios/src/MGLAnnotationView.h b/platform/ios/src/MGLAnnotationView.h index 184efdb324..2802d31b05 100644 --- a/platform/ios/src/MGLAnnotationView.h +++ b/platform/ios/src/MGLAnnotationView.h @@ -74,6 +74,36 @@ typedef NS_ENUM(NSUInteger, MGLAnnotationViewDragState) { - (instancetype)initWithReuseIdentifier:(nullable NSString *)reuseIdentifier; /** + Initializes and returns a new annotation view object. + + Providing an annotation allows you to explicitly associate the annotation instance + with the new view and, in custom subclasses of `MGLAnnotationView`, customize the view + based on properties of the annotation instance in an overridden initializer. However, + annotation views that are reused will not necessarily be associated with the + same annotation they were initialized with. Also, annotation views that are in + the reuse queue will have a nil value for the annotation property. Passing an annotation + instance to the view is optional and the map view will automatically associate annotations + with views when views are provided to the map via the `-[MGLMapViewDelegate mapView:viewForAnnotation:]` + method. + + The reuse identifier provides a way for you to improve performance by recycling + annotation views as they enter and leave the map’s viewport. As an annotation + leaves the viewport, the map view moves its associated view to a reuse queue. + When a new annotation becomes visible, you can request a view for that + annotation by passing the appropriate reuse identifier string to the + `-[MGLMapView dequeueReusableAnnotationViewWithIdentifier:]` method. + + @param annotation The annotation object to associate with the new view. + @param reuseIdentifier A unique string identifier for this view that allows you + to reuse this view with multiple similar annotations. You can set this + parameter to `nil` if you don’t intend to reuse the view, but it is a good + idea in general to specify a reuse identifier to avoid creating redundant + views. + @return The initialized annotation view object. + */ +- (instancetype)initWithAnnotation:(nullable id<MGLAnnotation>)annotation reuseIdentifier:(nullable NSString *)reuseIdentifier; + +/** Called when the view is removed from the reuse queue. The default implementation of this method does nothing. You can override it in @@ -141,6 +171,19 @@ typedef NS_ENUM(NSUInteger, MGLAnnotationViewDragState) { */ @property (nonatomic, assign) BOOL scalesWithViewingDistance; +/** + A Boolean value that determines whether the annotation view rotates together + with the map. + + When the value of this property is `YES` and the map is rotated, the annotation + view rotates. This is also the behavior of `MGLAnnotationImage` objects. When the + value of this property is `NO` the annotation has its rotation angle fixed. + + The default value of this property is `NO`. Set this property to `YES` if the + view’s rotation is important. + */ +@property (nonatomic, assign) BOOL rotatesToMatchCamera; + #pragma mark Managing the Selection State /** diff --git a/platform/ios/src/MGLAnnotationView.mm b/platform/ios/src/MGLAnnotationView.mm index 5e0ae3b848..94d0649413 100644 --- a/platform/ios/src/MGLAnnotationView.mm +++ b/platform/ios/src/MGLAnnotationView.mm @@ -11,6 +11,7 @@ @property (nonatomic, readwrite, nullable) NSString *reuseIdentifier; @property (nonatomic, readwrite) CATransform3D lastAppliedScaleTransform; +@property (nonatomic, readwrite) CATransform3D lastAppliedRotateTransform; @property (nonatomic, weak) UIPanGestureRecognizer *panGestureRecognizer; @property (nonatomic, weak) UILongPressGestureRecognizer *longPressRecognizer; @property (nonatomic, weak) MGLMapView *mapView; @@ -19,21 +20,32 @@ @implementation MGLAnnotationView -- (instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier -{ - self = [self initWithFrame:CGRectZero]; - if (self) - { - _lastAppliedScaleTransform = CATransform3DIdentity; - _reuseIdentifier = [reuseIdentifier copy]; - _scalesWithViewingDistance = YES; - _enabled = YES; ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier { + self = [super initWithFrame:CGRectZero]; + if (self) { + [self commonInitWithAnnotation:nil reuseIdentifier:reuseIdentifier]; } return self; } -+ (BOOL)supportsSecureCoding { - return YES; +- (instancetype)initWithAnnotation:(nullable id<MGLAnnotation>)annotation reuseIdentifier:(nullable NSString *)reuseIdentifier { + self = [super initWithFrame:CGRectZero]; + if (self) { + [self commonInitWithAnnotation:annotation reuseIdentifier:reuseIdentifier]; + } + return self; +} + +- (void)commonInitWithAnnotation:(nullable id<MGLAnnotation>)annotation reuseIdentifier:(nullable NSString *)reuseIdentifier { + _lastAppliedScaleTransform = CATransform3DIdentity; + _annotation = annotation; + _reuseIdentifier = [reuseIdentifier copy]; + _scalesWithViewingDistance = YES; + _enabled = YES; } - (instancetype)initWithCoder:(NSCoder *)decoder { @@ -42,6 +54,7 @@ _annotation = [decoder decodeObjectOfClass:[NSObject class] forKey:@"annotation"]; _centerOffset = [decoder decodeCGVectorForKey:@"centerOffset"]; _scalesWithViewingDistance = [decoder decodeBoolForKey:@"scalesWithViewingDistance"]; + _rotatesToMatchCamera = [decoder decodeBoolForKey:@"rotatesToMatchCamera"]; _selected = [decoder decodeBoolForKey:@"selected"]; _enabled = [decoder decodeBoolForKey:@"enabled"]; self.draggable = [decoder decodeBoolForKey:@"draggable"]; @@ -55,6 +68,7 @@ [coder encodeObject:_annotation forKey:@"annotation"]; [coder encodeCGVector:_centerOffset forKey:@"centerOffset"]; [coder encodeBool:_scalesWithViewingDistance forKey:@"scalesWithViewingDistance"]; + [coder encodeBool:_rotatesToMatchCamera forKey:@"rotatesToMatchCamera"]; [coder encodeBool:_selected forKey:@"selected"]; [coder encodeBool:_enabled forKey:@"enabled"]; [coder encodeBool:_draggable forKey:@"draggable"]; @@ -98,6 +112,7 @@ super.center = center; [self updateScaleTransformForViewingDistance]; + [self updateRotateTransform]; } - (void)setScalesWithViewingDistance:(BOOL)scalesWithViewingDistance @@ -146,6 +161,26 @@ } } +- (void)setRotatesToMatchCamera:(BOOL)rotatesToMatchCamera +{ + if (_rotatesToMatchCamera != rotatesToMatchCamera) + { + _rotatesToMatchCamera = rotatesToMatchCamera; + [self updateRotateTransform]; + } +} + +- (void)updateRotateTransform +{ + if (self.rotatesToMatchCamera == NO) return; + + CGFloat directionRad = self.mapView.direction * M_PI / 180.0; + CATransform3D newRotateTransform = CATransform3DMakeRotation(-directionRad, 0, 0, 1); + self.layer.transform = CATransform3DConcat(CATransform3DIdentity, newRotateTransform); + + _lastAppliedRotateTransform = newRotateTransform; +} + #pragma mark - Draggable - (void)setDraggable:(BOOL)draggable diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index d97c0b6602..9a9f71d5c3 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -64,7 +64,7 @@ #import "MGLCompactCalloutView.h" #import "MGLAnnotationContainerView.h" #import "MGLAnnotationContainerView_Private.h" -#import "MGLAttributionInfo.h" +#import "MGLAttributionInfo_Private.h" #include <algorithm> #include <cstdlib> @@ -131,6 +131,9 @@ const CGFloat MGLAnnotationImagePaddingForCallout = 1; const CGSize MGLAnnotationAccessibilityElementMinimumSize = CGSizeMake(10, 10); +// Context for KVO observing UILayoutGuides. +static void * MGLLayoutGuidesUpdatedContext = &MGLLayoutGuidesUpdatedContext; + /// Unique identifier representing a single annotation in mbgl. typedef uint32_t MGLAnnotationTag; @@ -233,13 +236,9 @@ public: @property (nonatomic) GLKView *glView; @property (nonatomic) UIImageView *glSnapshotView; @property (nonatomic, readwrite) MGLScaleBar *scaleBar; -@property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *scaleBarConstraints; @property (nonatomic, readwrite) UIImageView *compassView; -@property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *compassViewConstraints; @property (nonatomic, readwrite) UIImageView *logoView; -@property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *logoViewConstraints; @property (nonatomic, readwrite) UIButton *attributionButton; -@property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *attributionButtonConstraints; @property (nonatomic, readwrite) MGLStyle *style; @property (nonatomic) UITapGestureRecognizer *singleTapGestureRecognizer; @property (nonatomic) UITapGestureRecognizer *doubleTap; @@ -294,7 +293,8 @@ public: NSDate *_userLocationAnimationCompletionDate; /// True if a willChange notification has been issued for shape annotation layers and a didChange notification is pending. BOOL _isChangingAnnotationLayers; - + BOOL _isObservingTopLayoutGuide; + BOOL _isObservingBottomLayoutGuide; BOOL _isWaitingForRedundantReachableNotification; BOOL _isTargetingInterfaceBuilder; @@ -467,13 +467,11 @@ public: // setup logo bug // - UIImage *logo = [MGLMapView resourceImageNamed:@"mapbox.png"]; + UIImage *logo = [MGLMapView resourceImageNamed:@"mapbox"]; _logoView = [[UIImageView alloc] initWithImage:logo]; _logoView.accessibilityTraits = UIAccessibilityTraitStaticText; _logoView.accessibilityLabel = NSLocalizedStringWithDefaultValue(@"LOGO_A11Y_LABEL", nil, nil, @"Mapbox", @"Accessibility label"); - _logoView.translatesAutoresizingMaskIntoConstraints = NO; [self addSubview:_logoView]; - _logoViewConstraints = [NSMutableArray array]; // setup attribution // @@ -481,9 +479,7 @@ public: _attributionButton.accessibilityLabel = NSLocalizedStringWithDefaultValue(@"INFO_A11Y_LABEL", nil, nil, @"About this map", @"Accessibility label"); _attributionButton.accessibilityHint = NSLocalizedStringWithDefaultValue(@"INFO_A11Y_HINT", nil, nil, @"Shows credits, a feedback form, and more", @"Accessibility hint"); [_attributionButton addTarget:self action:@selector(showAttribution) forControlEvents:UIControlEventTouchUpInside]; - _attributionButton.translatesAutoresizingMaskIntoConstraints = NO; [self addSubview:_attributionButton]; - _attributionButtonConstraints = [NSMutableArray array]; [_attributionButton addObserver:self forKeyPath:@"hidden" options:NSKeyValueObservingOptionNew context:NULL]; // setup compass @@ -495,16 +491,12 @@ public: _compassView.accessibilityTraits = UIAccessibilityTraitButton; _compassView.accessibilityLabel = NSLocalizedStringWithDefaultValue(@"COMPASS_A11Y_LABEL", nil, nil, @"Compass", @"Accessibility label"); _compassView.accessibilityHint = NSLocalizedStringWithDefaultValue(@"COMPASS_A11Y_HINT", nil, nil, @"Rotates the map to face due north", @"Accessibility hint"); - _compassView.translatesAutoresizingMaskIntoConstraints = NO; [self addSubview:_compassView]; - _compassViewConstraints = [NSMutableArray array]; // setup scale control // _scaleBar = [[MGLScaleBar alloc] init]; - _scaleBar.translatesAutoresizingMaskIntoConstraints = NO; [self addSubview:_scaleBar]; - _scaleBarConstraints = [NSMutableArray array]; // setup interaction // @@ -624,11 +616,11 @@ public: - (UIImage *)compassImage { - UIImage *scaleImage = [MGLMapView resourceImageNamed:@"Compass.png"]; + UIImage *scaleImage = [MGLMapView resourceImageNamed:@"Compass"]; UIGraphicsBeginImageContextWithOptions(scaleImage.size, NO, [UIScreen mainScreen].scale); [scaleImage drawInRect:{ CGPointZero, scaleImage.size }]; - CGFloat northSize = 9; + CGFloat northSize = 11; UIFont *northFont; if ([UIFont respondsToSelector:@selector(systemFontOfSize:weight:)]) { @@ -643,7 +635,7 @@ public: NSForegroundColorAttributeName: [UIColor whiteColor], }]; CGRect stringRect = CGRectMake((scaleImage.size.width - north.size.width) / 2, - scaleImage.size.height * 0.45, + scaleImage.size.height * 0.435, north.size.width, north.size.height); [north drawInRect:stringRect]; @@ -664,6 +656,48 @@ public: _isWaitingForRedundantReachableNotification = NO; } +- (void)willMoveToWindow:(UIWindow *)newWindow +{ + [super willMoveToWindow:newWindow]; + + if (newWindow) { + [self addLayoutGuideObserversIfNeeded]; + } else { + [self removeLayoutGuideObserversIfNeeded]; + } +} + +- (void)addLayoutGuideObserversIfNeeded +{ + UIViewController *viewController = self.viewControllerForLayoutGuides; + BOOL useLayoutGuides = viewController.view && viewController.automaticallyAdjustsScrollViewInsets; + + if (useLayoutGuides && viewController.topLayoutGuide && !_isObservingTopLayoutGuide) { + [(NSObject *)viewController.topLayoutGuide addObserver:self forKeyPath:@"bounds" options:0 context:(void *)&MGLLayoutGuidesUpdatedContext]; + _isObservingTopLayoutGuide = YES; + } + + if (useLayoutGuides && viewController.bottomLayoutGuide && !_isObservingBottomLayoutGuide) { + [(NSObject *)viewController.bottomLayoutGuide addObserver:self forKeyPath:@"bounds" options:0 context:(void *)&MGLLayoutGuidesUpdatedContext]; + _isObservingBottomLayoutGuide = YES; + } +} + +- (void)removeLayoutGuideObserversIfNeeded +{ + UIViewController *viewController = self.viewControllerForLayoutGuides; + + if (_isObservingTopLayoutGuide) { + [(NSObject *)viewController.topLayoutGuide removeObserver:self forKeyPath:@"bounds" context:(void *)&MGLLayoutGuidesUpdatedContext]; + _isObservingTopLayoutGuide = NO; + } + + if (_isObservingBottomLayoutGuide) { + [(NSObject *)viewController.bottomLayoutGuide removeObserver:self forKeyPath:@"bounds" context:(void *)&MGLLayoutGuidesUpdatedContext]; + _isObservingBottomLayoutGuide = NO; + } +} + - (void)dealloc { [_reachability stopNotifier]; @@ -672,6 +706,8 @@ public: [[NSNotificationCenter defaultCenter] removeObserver:self]; [_attributionButton removeObserver:self forKeyPath:@"hidden"]; + [self removeLayoutGuideObserversIfNeeded]; + // Removing the annotations unregisters any outstanding KVO observers. NSArray *annotations = self.annotations; if (annotations) @@ -697,11 +733,6 @@ public: { [EAGLContext setCurrentContext:nil]; } - - [self.logoViewConstraints removeAllObjects]; - self.logoViewConstraints = nil; - [self.attributionButtonConstraints removeAllObjects]; - self.attributionButtonConstraints = nil; } - (void)setDelegate:(nullable id<MGLMapViewDelegate>)delegate @@ -786,105 +817,13 @@ public: - (void)updateConstraints { - // scale control - // - [self removeConstraints:self.scaleBarConstraints]; - [self.scaleBarConstraints removeAllObjects]; - - [self.scaleBarConstraints addObject: - [NSLayoutConstraint constraintWithItem:self.scaleBar - attribute:NSLayoutAttributeTop - relatedBy:NSLayoutRelationEqual - toItem:self - attribute:NSLayoutAttributeTop - multiplier:1 - constant:5+self.contentInset.top]]; - - [self.scaleBarConstraints addObject: - [NSLayoutConstraint constraintWithItem:self.scaleBar - attribute:NSLayoutAttributeLeading - relatedBy:NSLayoutRelationEqual - toItem:self - attribute:NSLayoutAttributeLeading - multiplier:1 - constant:8 + self.contentInset.left]]; - - [self addConstraints:self.scaleBarConstraints]; - - // compass - // - [self removeConstraints:self.compassViewConstraints]; - [self.compassViewConstraints removeAllObjects]; - - [self.compassViewConstraints addObject: - [NSLayoutConstraint constraintWithItem:self.compassView - attribute:NSLayoutAttributeTop - relatedBy:NSLayoutRelationEqual - toItem:self - attribute:NSLayoutAttributeTop - multiplier:1 - constant:5 + self.contentInset.top]]; - - [self.compassViewConstraints addObject: - [NSLayoutConstraint constraintWithItem:self - attribute:NSLayoutAttributeTrailing - relatedBy:NSLayoutRelationEqual - toItem:self.compassView - attribute:NSLayoutAttributeTrailing - multiplier:1 - constant:5 + self.contentInset.right]]; - - [self addConstraints:self.compassViewConstraints]; - - // logo bug - // - [self removeConstraints:self.logoViewConstraints]; - [self.logoViewConstraints removeAllObjects]; - - [self.logoViewConstraints addObject: - [NSLayoutConstraint constraintWithItem:self - attribute:NSLayoutAttributeBottom - relatedBy:NSLayoutRelationEqual - toItem:self.logoView - attribute:NSLayoutAttributeBaseline - multiplier:1 - constant:8 + self.contentInset.bottom]]; - - [self.logoViewConstraints addObject: - [NSLayoutConstraint constraintWithItem:self.logoView - attribute:NSLayoutAttributeLeading - relatedBy:NSLayoutRelationEqual - toItem:self - attribute:NSLayoutAttributeLeading - multiplier:1 - constant:8 + self.contentInset.left]]; - [self addConstraints:self.logoViewConstraints]; - - // attribution button - // - [self removeConstraints:self.attributionButtonConstraints]; - [self.attributionButtonConstraints removeAllObjects]; - - [self.attributionButtonConstraints addObject: - [NSLayoutConstraint constraintWithItem:self - attribute:NSLayoutAttributeBottom - relatedBy:NSLayoutRelationEqual - toItem:self.attributionButton - attribute:NSLayoutAttributeBaseline - multiplier:1 - constant:8 + self.contentInset.bottom]]; - - [self.attributionButtonConstraints addObject: - [NSLayoutConstraint constraintWithItem:self - attribute:NSLayoutAttributeTrailing - relatedBy:NSLayoutRelationEqual - toItem:self.attributionButton - attribute:NSLayoutAttributeTrailing - multiplier:1 - constant:8 + self.contentInset.right]]; - [self addConstraints:self.attributionButtonConstraints]; - [super updateConstraints]; + + // If we have a view controller reference and its automaticallyAdjustsScrollViewInsets + // is set to YES, -[MGLMapView adjustContentInset] takes top and bottom layout + // guides into account. To get notified about changes to the layout guides, + // we need to observe their bounds and re-layout accordingly. + [self addLayoutGuideObserversIfNeeded]; } - (BOOL)isOpaque @@ -917,6 +856,8 @@ public: [super layoutSubviews]; [self adjustContentInset]; + + [self layoutOrnaments]; if (!_isTargetingInterfaceBuilder) { _mbglMap->setSize([self size]); @@ -931,6 +872,39 @@ public: [self updateUserLocationAnnotationView]; } +- (void)layoutOrnaments +{ + // scale bar + self.scaleBar.frame = { + self.contentInset.left+8, + self.contentInset.top+5, + CGRectGetWidth(self.scaleBar.frame), + CGRectGetHeight(self.scaleBar.frame) + }; + + // compass + self.compassView.center = { + .x = CGRectGetWidth(self.bounds)-CGRectGetMidX(self.compassView.bounds)-self.contentInset.right-5, + .y = CGRectGetMidY(self.compassView.bounds)+self.contentInset.top+5 + }; + + // logo bug + self.logoView.frame = { + self.contentInset.left+8, + CGRectGetHeight(self.bounds)-8-self.contentInset.bottom-CGRectGetHeight(self.logoView.bounds), + CGRectGetWidth(self.logoView.bounds), + CGRectGetHeight(self.logoView.bounds) + }; + + // attribution + self.attributionButton.frame = { + CGRectGetWidth(self.bounds)-CGRectGetWidth(self.attributionButton.bounds)-self.contentInset.right-8, + CGRectGetHeight(self.bounds)-CGRectGetHeight(self.attributionButton.bounds)-self.contentInset.bottom-8, + CGRectGetWidth(self.attributionButton.bounds), + CGRectGetHeight(self.attributionButton.bounds) + }; +} + /// Updates `contentInset` to reflect the current window geometry. - (void)adjustContentInset { @@ -1000,7 +974,7 @@ public: } // Compass, logo and attribution button constraints needs to be updated. - [self setNeedsUpdateConstraints]; + [self setNeedsLayout]; } /// Returns the frame of inset content within the map view. @@ -1921,8 +1895,12 @@ public: { if (info.feedbackLink) { - url = [info feedbackURLAtCenterCoordinate:self.centerCoordinate - zoomLevel:self.zoomLevel]; + MGLMapCamera *camera = self.camera; + url = [info feedbackURLForStyleURL:self.styleURL + atCenterCoordinate:camera.centerCoordinate + zoomLevel:self.zoomLevel + direction:camera.heading + pitch:camera.pitch]; } [[UIApplication sharedApplication] openURL:url]; } @@ -1999,9 +1977,10 @@ public: }]; [alertController addAction:participateAction]; - [self.window.rootViewController presentViewController:alertController - animated:YES - completion:NULL]; + UIViewController *viewController = [self.window.rootViewController mgl_topMostViewController]; + [viewController presentViewController:alertController + animated:YES + completion:NULL]; } #pragma mark - Properties - @@ -2065,6 +2044,10 @@ public: [self updateCalloutView]; } } + else if (context == MGLLayoutGuidesUpdatedContext && [keyPath isEqualToString:@"bounds"]) + { + [self setNeedsLayout]; + } } + (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingZoomEnabled @@ -3501,6 +3484,7 @@ public: annotationView.annotation = nil; [annotationView removeFromSuperview]; + [self.annotationContainerView.annotationViews removeObject:annotationView]; if (annotationTag == _selectedAnnotationTag) { @@ -5314,18 +5298,17 @@ public: + (UIImage *)resourceImageNamed:(NSString *)imageName { - NSString *extension = imageName.pathExtension.length ? imageName.pathExtension : @"png"; - NSBundle *bundle = [NSBundle mgl_frameworkBundle]; - NSString *path = [bundle pathForResource:imageName.stringByDeletingPathExtension - ofType:extension - inDirectory:bundle.mgl_resourcesDirectory]; - if ( ! path) + UIImage *image = [UIImage imageNamed:imageName + inBundle:[NSBundle mgl_frameworkBundle] + compatibleWithTraitCollection:nil]; + + if ( ! image) { - [NSException raise:@"Resource not found" format: + [NSException raise:@"MGLResourceNotFoundException" format: @"The resource named “%@” could not be found in the Mapbox framework bundle.", imageName]; } - return [UIImage imageWithContentsOfFile:path]; + return image; } - (BOOL)isFullyLoaded diff --git a/platform/ios/src/MGLMapboxEvents.m b/platform/ios/src/MGLMapboxEvents.m index 7b28ccf1a8..4f1413d300 100644 --- a/platform/ios/src/MGLMapboxEvents.m +++ b/platform/ios/src/MGLMapboxEvents.m @@ -317,13 +317,6 @@ const NSTimeInterval MGLFlushInterval = 180; return; } - if ([self.eventQueue count] <= 1) { - [self.eventQueue removeAllObjects]; - [[UIApplication sharedApplication] endBackgroundTask:_backgroundTaskIdentifier]; - _backgroundTaskIdentifier = UIBackgroundTaskInvalid; - return; - } - NSArray *events = [NSArray arrayWithArray:self.eventQueue]; [self.eventQueue removeAllObjects]; diff --git a/platform/ios/src/MGLScaleBar.mm b/platform/ios/src/MGLScaleBar.mm index 1216e400b3..cd88c1e08e 100644 --- a/platform/ios/src/MGLScaleBar.mm +++ b/platform/ios/src/MGLScaleBar.mm @@ -220,6 +220,12 @@ static const CGFloat MGLFeetPerMeter = 3.28084; self.row = [self preferredRow]; + CGSize size = self.intrinsicContentSize; + self.frame = CGRectMake(CGRectGetMinX(self.frame), + CGRectGetMinY(self.frame), + size.width, + size.height); + [self invalidateIntrinsicContentSize]; [self setNeedsLayout]; } diff --git a/platform/ios/src/Mapbox.h b/platform/ios/src/Mapbox.h index 9a9dc702ca..67a26e8ed4 100644 --- a/platform/ios/src/Mapbox.h +++ b/platform/ios/src/Mapbox.h @@ -19,6 +19,7 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[]; #import "MGLDistanceFormatter.h" #import "MGLFeature.h" #import "MGLGeometry.h" +#import "MGLLight.h" #import "MGLMapCamera.h" #import "MGLMapView.h" #import "MGLMapView+IBAdditions.h" diff --git a/platform/ios/test/MGLAnnotationViewIntegrationTests.swift b/platform/ios/test/MGLAnnotationViewIntegrationTests.swift new file mode 100644 index 0000000000..82a57a4009 --- /dev/null +++ b/platform/ios/test/MGLAnnotationViewIntegrationTests.swift @@ -0,0 +1,23 @@ +import XCTest +import Mapbox + +class CustomAnnotationView: MGLAnnotationView { + + override init(reuseIdentifier: String?) { + super.init(reuseIdentifier: reuseIdentifier) + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + +} + +class MGLAnnotationViewIntegrationTests: XCTestCase { + + func testCreatingCustomAnnotationView() { + let customAnnotationView = CustomAnnotationView(reuseIdentifier: "resuse-id") + XCTAssertNotNil(customAnnotationView) + } + +} diff --git a/platform/ios/test/MGLAnnotationViewTests.m b/platform/ios/test/MGLAnnotationViewTests.m index c0978eaf65..fc4f35a9e1 100644 --- a/platform/ios/test/MGLAnnotationViewTests.m +++ b/platform/ios/test/MGLAnnotationViewTests.m @@ -3,11 +3,24 @@ static NSString * const MGLTestAnnotationReuseIdentifer = @"MGLTestAnnotationReuseIdentifer"; +@interface MGLCustomAnnotationView : MGLAnnotationView + +@end + +@implementation MGLCustomAnnotationView + +- (instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier { + return [super initWithReuseIdentifier:@"reuse-id"]; +} + +@end + @interface MGLAnnotationView (Test) + @property (nonatomic) MGLMapView *mapView; @property (nonatomic, readwrite) MGLAnnotationViewDragState dragState; - - (void)setDragState:(MGLAnnotationViewDragState)dragState; + @end @interface MGLMapView (Test) @@ -79,13 +92,19 @@ static NSString * const MGLTestAnnotationReuseIdentifer = @"MGLTestAnnotationReu XCTAssertNil(_annotationView.annotation, @"annotation property should be nil"); } +- (void)testCustomAnnotationView +{ + MGLCustomAnnotationView *customAnnotationView = [[MGLCustomAnnotationView alloc] initWithReuseIdentifier:@"reuse-id"]; + XCTAssertNotNil(customAnnotationView); +} + - (MGLAnnotationView *)mapView:(MGLMapView *)mapView viewForAnnotation:(id<MGLAnnotation>)annotation { MGLAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:MGLTestAnnotationReuseIdentifer]; if (!annotationView) { - annotationView = [[MGLAnnotationView alloc] initWithReuseIdentifier:MGLTestAnnotationReuseIdentifer]; + annotationView = [[MGLAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:MGLTestAnnotationReuseIdentifer]; } _annotationView = annotationView; diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index 579624f0e6..4301566258 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -1,6 +1,11 @@ # Changelog for Mapbox macOS SDK -## master +## 3.6.0 + +### Packaging + +* Xcode 8.0 or higher is now recommended for using this SDK. ([#8775](https://github.com/mapbox/mapbox-gl-native/pull/8775)) +* Updated MGLMapView’s logo view to display [the new Mapbox logo](https://www.mapbox.com/blog/new-mapbox-logo/). ([#8771](https://github.com/mapbox/mapbox-gl-native/pull/8771), [#8773](https://github.com/mapbox/mapbox-gl-native/pull/8773)) * The previously-deprecated support for style classes has been removed. For interface compatibility, the API methods remain, but they are now non-functional. * Added an `overlays` property to `MGLMapView`. ([#8617](https://github.com/mapbox/mapbox-gl-native/pull/8617)) @@ -14,17 +19,21 @@ * Added class methods to MGLStyle that correspond to the new [Traffic Day and Traffic Night](https://www.mapbox.com/blog/live-traffic-maps/) styles. ([#6301](https://github.com/mapbox/mapbox-gl-native/pull/6301)) * MGLSymbolStyleLayer’s `iconImageName`, `iconScale`, `textFontSize`, `textOffset`, and `textRotation` properties can now be set to a source or composite function. ([#8544](https://github.com/mapbox/mapbox-gl-native/pull/8544), [#8590](https://github.com/mapbox/mapbox-gl-native/pull/8590), [#8592](https://github.com/mapbox/mapbox-gl-native/pull/8592), [#8593](https://github.com/mapbox/mapbox-gl-native/pull/8593)) * Fixed an issue where setting the `MGLVectorStyleLayer.predicate` property failed to take effect if the relevant source was not in use by a visible layer at the time. ([#8653](https://github.com/mapbox/mapbox-gl-native/pull/8653)) +* Fixed an issue preventing programmatically added style layers from appearing in already cached tiles. ([#8954](https://github.com/mapbox/mapbox-gl-native/pull/8954)) * Fixed an issue causing a composite function’s highest zoom level stop to be misinterpreted. ([#8613](https://github.com/mapbox/mapbox-gl-native/pull/8613), [#8790](https://github.com/mapbox/mapbox-gl-native/pull/8790)) * Fixed an issue where re-adding a layer that had been previously removed from a style would reset its paint properties. Moved initializers for `MGLTileSource`, `MGLStyleLayer`, and `MGLForegroundStyleLayer` to their concrete subclasses; because these classes were already intended for initialization only via concrete subclasses, this should have no developer impact. ([#8626](https://github.com/mapbox/mapbox-gl-native/pull/8626)) * Feature querying results now account for any changes to a feature’s size caused by a source or composite style function. ([#8665](https://github.com/mapbox/mapbox-gl-native/pull/8665)) +* Letter spacing is now disabled in Arabic text so that ligatures are drawn correctly. ([#9062](https://github.com/mapbox/mapbox-gl-native/pull/9062)) ### Other changes -* Xcode 8.0 or higher is now recommended for using this SDK. ([#8775](https://github.com/mapbox/mapbox-gl-native/pull/8775)) -* Updated MGLMapView’s logo view to display [the new Mapbox logo](https://www.mapbox.com/blog/new-mapbox-logo/). ([#8771](https://github.com/mapbox/mapbox-gl-native/pull/8771), [#8773](https://github.com/mapbox/mapbox-gl-native/pull/8773)) +* Fixed a crash when calling `MGLMultiPolygon.coordinate` [#8713](https://github.com/mapbox/mapbox-gl-native/pull/8713) * Fixed an issue causing attribution button text to appear blue instead of black. ([#8701](https://github.com/mapbox/mapbox-gl-native/pull/8701)) * Fixed a crash or console spew when MGLMapView is initialized with a frame smaller than 64 points wide by 64 points tall. ([#8562](https://github.com/mapbox/mapbox-gl-native/pull/8562)) +* The Improve This Map button in the attribution action sheet now leads to a feedback tool that matches MGLMapView’s rotation and pitch. `-[MGLAttributionInfo feedbackURLAtCenterCoordinate:zoomLevel:]` no longer respects the feedback URL specified in TileJSON. ([#9078](https://github.com/mapbox/mapbox-gl-native/pull/9078)) * 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)) +* The `MGLPolyline.coordinate` and `MGLPolygon.coordinate` properties now return the midpoint and centroid, respectively, instead of the first coordinate. ([#8713](https://github.com/mapbox/mapbox-gl-native/pull/8713)) +* Improved CPU and battery performance while animating a tilted map’s camera in an area with many labels. ([#9031](https://github.com/mapbox/mapbox-gl-native/pull/9031)) * Fixed an issue rendering polylines that contain duplicate vertices. ([#8808](https://github.com/mapbox/mapbox-gl-native/pull/8808)) ## 0.4.1 - April 8, 2017 diff --git a/platform/macos/app/MGLVectorSource+MBXAdditions.h b/platform/macos/app/MGLVectorSource+MBXAdditions.h index 312081ec51..1e25ee5a60 100644 --- a/platform/macos/app/MGLVectorSource+MBXAdditions.h +++ b/platform/macos/app/MGLVectorSource+MBXAdditions.h @@ -4,7 +4,7 @@ NS_ASSUME_NONNULL_BEGIN @interface MGLVectorSource (MBXAdditions) -+ (nullable NSString *)preferredMapboxStreetsLanguage; ++ (NSString *)preferredMapboxStreetsLanguage; - (NS_DICTIONARY_OF(NSString *, NSString *) *)localizedKeysByKeyForPreferredLanguage:(nullable NSString *)preferredLanguage; diff --git a/platform/macos/app/MGLVectorSource+MBXAdditions.m b/platform/macos/app/MGLVectorSource+MBXAdditions.m index 644b43a651..323bc74366 100644 --- a/platform/macos/app/MGLVectorSource+MBXAdditions.m +++ b/platform/macos/app/MGLVectorSource+MBXAdditions.m @@ -7,19 +7,23 @@ static dispatch_once_t onceToken; static NS_SET_OF(NSString *) *mapboxStreetsLanguages; dispatch_once(&onceToken, ^{ - mapboxStreetsLanguages = [NSSet setWithObjects:@"en", @"es", @"fr", @"de", @"ru", @"zh", nil]; + // https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview + mapboxStreetsLanguages = [NSSet setWithObjects:@"ar", @"de", @"en", @"es", @"fr", @"pt", @"ru", @"zh", @"zh-Hans", nil]; }); return mapboxStreetsLanguages; } -+ (nullable NSString *)preferredMapboxStreetsLanguage { - for (NSString *language in [NSLocale preferredLanguages]) { - NSString *languageCode = [[NSLocale localeWithLocaleIdentifier:language] objectForKey:NSLocaleLanguageCode]; - if ([[MGLVectorSource mapboxStreetsLanguages] containsObject:languageCode]) { - return languageCode; ++ (NSString *)preferredMapboxStreetsLanguage { + NSArray<NSString *> *supportedLanguages = [MGLVectorSource mapboxStreetsLanguages].allObjects; + NSArray<NSString *> *preferredLanguages = [NSBundle preferredLocalizationsFromArray:supportedLanguages + forPreferences:[NSLocale preferredLanguages]]; + NSString *mostSpecificLanguage; + for (NSString *language in preferredLanguages) { + if (language.length > mostSpecificLanguage.length) { + mostSpecificLanguage = language; } } - return nil; + return mostSpecificLanguage ?: @"en"; } - (BOOL)isMapboxStreets { diff --git a/platform/macos/app/MapDocument.m b/platform/macos/app/MapDocument.m index 59c1817f63..59844d363e 100644 --- a/platform/macos/app/MapDocument.m +++ b/platform/macos/app/MapDocument.m @@ -345,7 +345,7 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio - (void)updateLabels { MGLStyle *style = self.mapView.style; - NSString *preferredLanguage = _isLocalizingLabels ? ([MGLVectorSource preferredMapboxStreetsLanguage] ?: @"en") : nil; + NSString *preferredLanguage = _isLocalizingLabels ? [MGLVectorSource preferredMapboxStreetsLanguage] : nil; NSMutableDictionary *localizedKeysByKeyBySourceIdentifier = [NSMutableDictionary dictionary]; for (MGLSymbolStyleLayer *layer in style.layers) { if (![layer isKindOfClass:[MGLSymbolStyleLayer class]]) { @@ -807,6 +807,9 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio case 7: state = [styleURL isEqual:[MGLStyle trafficDayStyleURL]]; break; + case 8: + state = [styleURL isEqual:[MGLStyle trafficNightStyleURL]]; + break; default: return NO; } @@ -855,8 +858,7 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio if (menuItem.tag) { NSLocale *locale = [NSLocale localeWithLocaleIdentifier:[NSBundle mainBundle].developmentLocalization]; NSString *preferredLanguage = [MGLVectorSource preferredMapboxStreetsLanguage]; - menuItem.enabled = !!preferredLanguage; - menuItem.title = [locale displayNameForKey:NSLocaleIdentifier value:preferredLanguage ?: @"Preferred Language"]; + menuItem.title = [locale displayNameForKey:NSLocaleIdentifier value:preferredLanguage]; } return YES; } diff --git a/platform/macos/docs/guides/Tile URL Templates.md b/platform/macos/docs/guides/Tile URL Templates.md new file mode 100644 index 0000000000..01672c6686 --- /dev/null +++ b/platform/macos/docs/guides/Tile URL Templates.md @@ -0,0 +1,98 @@ +<!-- + This file is generated. + Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. +--> +# Tile URL Templates + +`MGLTileSource` objects, specifically `MGLRasterSource` and `MGLVectorSource` +objects, can be created using an initializer that accepts an array of tile URL +templates. Tile URL templates are strings that specify the URLs of the vector +tiles or raster tile images to load. A template resembles an absolute URL, but +with any number of placeholder strings that the source evaluates based on the +tile it needs to load. For example: + +* `http://www.example.com/tiles/{z}/{x}/{y}.pbf` could be + evaluated as `http://www.example.com/tiles/14/6/9.pbf`. +* `http://www.example.com/tiles/{z}/{x}/{y}{ratio}.png` could be + evaluated as `http://www.example.com/tiles/14/6/9@2x.png`. + +Tile URL templates are also used to define tilesets in TileJSON manifests or +[`raster`](https://www.mapbox.com/mapbox-gl-js/style-spec/#sources-raster-tiles) +and +[`vector`](https://www.mapbox.com/mapbox-gl-js/style-spec/#sources-vector-tiles) +sources in style JSON files. See the +[TileJSON specification](https://github.com/mapbox/tilejson-spec/tree/master/2.2.0) +for information about tile URL templates in the context of a TileJSON or style +JSON file. + +Tile sources support the following placeholder strings in tile URL templates, +all of which are optional: + +<table> +<thead> +<tr><th>Placeholder string</th><th>Description</th></tr> +</thead> +<tbody> +<tr> + <td><code>{x}</code></td> + <td>The index of the tile along the map’s x axis according to Spherical + Mercator projection. If the value is 0, the tile’s left edge corresponds + to the 180th meridian west. If the value is 2<sup><var>z</var></sup>−1, + the tile’s right edge corresponds to the 180th meridian east.</td> +</tr> +<tr> + <td><code>{y}</code></td> + <td>The index of the tile along the map’s y axis according to Spherical + Mercator projection. If the value is 0, the tile’s tile edge corresponds + to arctan(sinh(π)), or approximately 85.0511 degrees north. If the value + is 2<sup><var>z</var></sup>−1, the tile’s bottom edge corresponds to + −arctan(sinh(π)), or approximately 85.0511 degrees south. The y axis is + inverted if the <code>options</code> parameter contains + <code>MGLTileSourceOptionTileCoordinateSystem</code> with a value of + <code>MGLTileCoordinateSystemTMS</code>.</td> +</tr> +<tr> + <td><code>{z}</code></td> + <td>The tile’s zoom level. At zoom level 0, each tile covers the entire + world map; at zoom level 1, it covers ¼ of the world; at zoom level 2, + <sup>1</sup>⁄<sub>16</sub> of the world, and so on. For tiles loaded by + a <code>MGLRasterSource</code> object, whether the tile zoom level + matches the map’s current zoom level depends on the value of the + source’s tile size as specified in the + <code>MGLTileSourceOptionTileSize</code> key of the <code>options</code> + parameter.</td> +</tr> +<tr> + <td><code>{bbox-epsg-3857}</code></td> + <td>The tile’s bounding box, expressed as a comma-separated list of the + tile’s western, southern, eastern, and northern extents according to + Spherical Mercator (EPSG:3857) projection. The bounding box is typically + used with map services conforming to the + <a href="http://www.opengeospatial.org/standards/wms">Web Map Service</a> + protocol.</td> +</tr> +<tr> + <td><code>{quadkey}</code></td> + <td>A quadkey indicating both the tile’s location and its zoom level. The + quadkey is typically used with + <a href="https://msdn.microsoft.com/en-us/library/bb259689.aspx">Bing Maps</a>. + </td> +</tr> +<tr> + <td><code>{ratio}</code></td> + <td>A suffix indicating the resolution of the tile image. The suffix is the + empty string for standard resolution displays and <code>@2x</code> for + Retina displays. + </td> +</tr> +<tr> + <td><code>{prefix}</code></td> + <td>Two hexadecimal digits chosen such that each visible tile has a + different prefix. The prefix is typically used for domain sharding.</td> +</tr> +</tbody> +</table> + +For more information about the `{x}`, `{y}`, and `{z}` placeholder strings, +consult the +[OpenStreetMap Wiki](https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames). diff --git a/platform/macos/jazzy.yml b/platform/macos/jazzy.yml index 3c24b351cc..fcd4bfd301 100644 --- a/platform/macos/jazzy.yml +++ b/platform/macos/jazzy.yml @@ -20,6 +20,7 @@ custom_categories: - Working with GeoJSON Data - For Style Authors - Using Style Functions at Runtime + - Tile URL Templates - Info.plist Keys - name: Maps children: @@ -45,6 +46,7 @@ custom_categories: children: - MGLStyle - MGLStyleValue + - MGLLight - name: Content Primitives children: - MGLFeature diff --git a/platform/macos/macos.xcodeproj/project.pbxproj b/platform/macos/macos.xcodeproj/project.pbxproj index 9a18ce4a0e..564d81afb2 100644 --- a/platform/macos/macos.xcodeproj/project.pbxproj +++ b/platform/macos/macos.xcodeproj/project.pbxproj @@ -8,6 +8,10 @@ /* Begin PBXBuildFile section */ 1753ED401E53CE6100A9FD90 /* MGLConversion.h in Headers */ = {isa = PBXBuildFile; fileRef = 1753ED3F1E53CE5200A9FD90 /* MGLConversion.h */; }; + 1F7454A31ECFB00300021D39 /* MGLLight_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F7454A01ECFB00300021D39 /* MGLLight_Private.h */; }; + 1F7454A41ECFB00300021D39 /* MGLLight.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F7454A11ECFB00300021D39 /* MGLLight.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 1F7454A51ECFB00300021D39 /* MGLLight.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1F7454A21ECFB00300021D39 /* MGLLight.mm */; }; + 1F7454AB1ED1DDBD00021D39 /* MGLLightTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1F7454AA1ED1DDBD00021D39 /* MGLLightTest.mm */; }; 1F95931B1E6DE2B600D5B294 /* MGLNSDateAdditionsTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1F95931A1E6DE2B600D5B294 /* MGLNSDateAdditionsTests.mm */; }; 30E5781B1DAA857E0050F07E /* NSImage+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 30E578141DAA7D920050F07E /* NSImage+MGLAdditions.h */; }; 3508EC641D749D39009B0EE4 /* NSExpression+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3508EC621D749D39009B0EE4 /* NSExpression+MGLAdditions.h */; }; @@ -268,6 +272,10 @@ /* Begin PBXFileReference section */ 1753ED3F1E53CE5200A9FD90 /* MGLConversion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLConversion.h; sourceTree = "<group>"; }; + 1F7454A01ECFB00300021D39 /* MGLLight_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLLight_Private.h; sourceTree = "<group>"; }; + 1F7454A11ECFB00300021D39 /* MGLLight.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLLight.h; sourceTree = "<group>"; }; + 1F7454A21ECFB00300021D39 /* MGLLight.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLLight.mm; sourceTree = "<group>"; }; + 1F7454AA1ED1DDBD00021D39 /* MGLLightTest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLLightTest.mm; sourceTree = "<group>"; }; 1F95931A1E6DE2B600D5B294 /* MGLNSDateAdditionsTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLNSDateAdditionsTests.mm; path = ../../darwin/test/MGLNSDateAdditionsTests.mm; sourceTree = "<group>"; }; 30E578141DAA7D920050F07E /* NSImage+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSImage+MGLAdditions.h"; path = "src/NSImage+MGLAdditions.h"; sourceTree = SOURCE_ROOT; }; 3508EC621D749D39009B0EE4 /* NSExpression+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSExpression+MGLAdditions.h"; sourceTree = "<group>"; }; @@ -670,6 +678,9 @@ 352742771D4C220900A1ECE6 /* MGLStyleValue.h */, DA8F259B1D51CB000010E6B5 /* MGLStyleValue_Private.h */, 3527429E1D4C25BD00A1ECE6 /* MGLStyleValue.mm */, + 1F7454A11ECFB00300021D39 /* MGLLight.h */, + 1F7454A21ECFB00300021D39 /* MGLLight.mm */, + 1F7454A01ECFB00300021D39 /* MGLLight_Private.h */, ); name = Styling; sourceTree = "<group>"; @@ -810,6 +821,7 @@ children = ( DA8F257C1D51C5F40010E6B5 /* Layers */, DA87A99A1DC9D88800810D09 /* Sources */, + 1F7454AA1ED1DDBD00021D39 /* MGLLightTest.mm */, 353722EB1DF850ED004D2F3F /* MGLStyleValueTests.h */, 3599A3E71DF70E2000E77FB2 /* MGLStyleValueTests.m */, DA2207BB1DC076940002F84D /* MGLStyleValueTests.swift */, @@ -1133,6 +1145,7 @@ DA8F258B1D51CA540010E6B5 /* MGLLineStyleLayer.h in Headers */, 35C6DF841E214C0400ACA483 /* MGLDistanceFormatter.h in Headers */, DA8F25B21D51CB270010E6B5 /* NSValue+MGLStyleAttributeAdditions.h in Headers */, + 1F7454A31ECFB00300021D39 /* MGLLight_Private.h in Headers */, 359819591E02F611008FC139 /* NSCoder+MGLAdditions.h in Headers */, DAE6C38E1CC31E2A00DB3429 /* MGLOfflineStorage_Private.h in Headers */, 408AA8661DAEEE3600022900 /* MGLPolyline+MGLAdditions.h in Headers */, @@ -1166,6 +1179,7 @@ DACC22181CF3D4F700D220D9 /* MGLFeature_Private.h in Headers */, DA6408D71DA4E5DA00908C90 /* MGLVectorStyleLayer.h in Headers */, 352742891D4C245800A1ECE6 /* MGLShapeSource.h in Headers */, + 1F7454A41ECFB00300021D39 /* MGLLight.h in Headers */, 408AA8671DAEEE3900022900 /* NSDictionary+MGLAdditions.h in Headers */, DAE6C3671CC31E0400DB3429 /* MGLStyle.h in Headers */, ); @@ -1380,6 +1394,7 @@ DAE6C38C1CC31E2A00DB3429 /* MGLOfflinePack.mm in Sources */, 35D65C5B1D65AD5500722C23 /* NSDate+MGLAdditions.mm in Sources */, DD0902B21DB1AC6400C5BDCE /* MGLNetworkConfiguration.m in Sources */, + 1F7454A51ECFB00300021D39 /* MGLLight.mm in Sources */, DAE6C3B11CC31EF300DB3429 /* MGLAnnotationImage.m in Sources */, 3508EC651D749D39009B0EE4 /* NSExpression+MGLAdditions.mm in Sources */, DACC22151CF3D3E200D220D9 /* MGLFeature.mm in Sources */, @@ -1446,6 +1461,7 @@ DA35A2C21CCA9F4A00E826B2 /* MGLClockDirectionFormatterTests.m in Sources */, DAE6C3D41CC34C9900DB3429 /* MGLOfflineRegionTests.m in Sources */, DAE6C3D61CC34C9900DB3429 /* MGLStyleTests.mm in Sources */, + 1F7454AB1ED1DDBD00021D39 /* MGLLightTest.mm in Sources */, DAEDC4371D606291000224FF /* MGLAttributionButtonTests.m in Sources */, 920A3E591E6F859D00C16EFC /* MGLSourceQueryTests.m in Sources */, DA35A2B61CCA14D700E826B2 /* MGLCompassDirectionFormatterTests.m in Sources */, @@ -1763,7 +1779,10 @@ DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_VERSION = A; - HEADER_SEARCH_PATHS = "$(mbgl_core_INCLUDE_DIRECTORIES)"; + HEADER_SEARCH_PATHS = ( + "$(mbgl_core_INCLUDE_DIRECTORIES)", + "$(polylabel_INCLUDE_DIRECTORIES)", + ); INFOPLIST_FILE = "$(SRCROOT)/sdk/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; @@ -1797,7 +1816,10 @@ DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_VERSION = A; - HEADER_SEARCH_PATHS = "$(mbgl_core_INCLUDE_DIRECTORIES)"; + HEADER_SEARCH_PATHS = ( + "$(mbgl_core_INCLUDE_DIRECTORIES)", + "$(polylabel_INCLUDE_DIRECTORIES)", + ); INFOPLIST_FILE = "$(SRCROOT)/sdk/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; @@ -1821,7 +1843,10 @@ buildSettings = { CLANG_ENABLE_MODULES = YES; COMBINE_HIDPI_IMAGES = YES; - HEADER_SEARCH_PATHS = "$(mbgl_core_INCLUDE_DIRECTORIES)"; + HEADER_SEARCH_PATHS = ( + "$(mbgl_core_INCLUDE_DIRECTORIES)", + "$(polylabel_INCLUDE_DIRECTORIES)", + ); INFOPLIST_FILE = test/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; LIBRARY_SEARCH_PATHS = ( @@ -1850,7 +1875,10 @@ buildSettings = { CLANG_ENABLE_MODULES = YES; COMBINE_HIDPI_IMAGES = YES; - HEADER_SEARCH_PATHS = "$(mbgl_core_INCLUDE_DIRECTORIES)"; + HEADER_SEARCH_PATHS = ( + "$(mbgl_core_INCLUDE_DIRECTORIES)", + "$(polylabel_INCLUDE_DIRECTORIES)", + ); INFOPLIST_FILE = test/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; OTHER_CFLAGS = "-fvisibility=hidden"; diff --git a/platform/macos/src/MGLMapView.mm b/platform/macos/src/MGLMapView.mm index 98958f045d..c7f6e59c80 100644 --- a/platform/macos/src/MGLMapView.mm +++ b/platform/macos/src/MGLMapView.mm @@ -1,17 +1,18 @@ #import "MGLMapView_Private.h" -#import "MGLAnnotationImage_Private.h" + #import "MGLAttributionButton.h" -#import "MGLAttributionInfo.h" #import "MGLCompassCell.h" #import "MGLOpenGLLayer.h" #import "MGLStyle.h" +#import "MGLAnnotationImage_Private.h" +#import "MGLAttributionInfo_Private.h" #import "MGLFeature_Private.h" +#import "MGLFoundation_Private.h" #import "MGLGeometry_Private.h" #import "MGLMultiPoint_Private.h" #import "MGLOfflineStorage_Private.h" #import "MGLStyle_Private.h" -#import "MGLFoundation_Private.h" #import "MGLAccountManager.h" #import "MGLMapCamera.h" @@ -1735,11 +1736,15 @@ public: } - (IBAction)giveFeedback:(id)sender { - CLLocationCoordinate2D centerCoordinate = self.centerCoordinate; + MGLMapCamera *camera = self.camera; double zoomLevel = self.zoomLevel; NSMutableArray *urls = [NSMutableArray array]; for (MGLAttributionInfo *info in [self.style attributionInfosWithFontSize:0 linkColor:nil]) { - NSURL *url = [info feedbackURLAtCenterCoordinate:centerCoordinate zoomLevel:zoomLevel]; + NSURL *url = [info feedbackURLForStyleURL:self.styleURL + atCenterCoordinate:camera.centerCoordinate + zoomLevel:zoomLevel + direction:camera.heading + pitch:camera.pitch]; if (url) { [urls addObject:url]; } diff --git a/platform/macos/src/Mapbox.h b/platform/macos/src/Mapbox.h index dcb5b50b8f..0f47dace70 100644 --- a/platform/macos/src/Mapbox.h +++ b/platform/macos/src/Mapbox.h @@ -17,6 +17,7 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[]; #import "MGLDistanceFormatter.h" #import "MGLFeature.h" #import "MGLGeometry.h" +#import "MGLLight.h" #import "MGLMapCamera.h" #import "MGLMapView.h" #import "MGLMapView+IBAdditions.h" |