From 08916f2d5f63a6ef746e0560e3a56784db81e3dc Mon Sep 17 00:00:00 2001 From: Antonio Zugaldia Date: Thu, 11 May 2017 09:19:24 -0400 Subject: [PATCH 01/62] [android] Update release script to support CircleCI builds (#8950) * update release script to trigger builds on circleci now * update release script to trigger builds on circleci now --- platform/android/scripts/release.py | 48 ++++++++++++++--------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/platform/android/scripts/release.py b/platform/android/scripts/release.py index 8abcccffdbd..ce554d13629 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 @@ 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 @@ # 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 @@ 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 From db370a02b13f39ce79b8df11e784b3543f2908cb Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Wed, 10 May 2017 17:51:55 -0700 Subject: [PATCH 02/62] [core] When a layer is added, reload its source's tiles --- mapbox-gl-js | 2 +- src/mbgl/style/style.cpp | 31 ++++++++++++++++--------------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/mapbox-gl-js b/mapbox-gl-js index 44641e917ed..8b085a21157 160000 --- a/mapbox-gl-js +++ b/mapbox-gl-js @@ -1 +1 @@ -Subproject commit 44641e917ed0e37a1c1622ab154f068b22927b03 +Subproject commit 8b085a211579d417ad8b3d58bc502c4ffbdfc2e9 diff --git a/src/mbgl/style/style.cpp b/src/mbgl/style/style.cpp index 4b694917c3f..f083ca47b88 100644 --- a/src/mbgl/style/style.cpp +++ b/src/mbgl/style/style.cpp @@ -52,6 +52,21 @@ namespace style { static Observer nullObserver; +struct QueueSourceReloadVisitor { + UpdateBatch& updateBatch; + + // No need to reload sources for these types; their visibility can change but + // they don't participate in layout. + void operator()(CustomLayer&) {} + void operator()(RasterLayer&) {} + void operator()(BackgroundLayer&) {} + + template + void operator()(VectorLayer& layer) { + updateBatch.sourceIDs.insert(layer.getSourceID()); + } +}; + Style::Style(Scheduler& scheduler_, FileSource& fileSource_, float pixelRatio) : scheduler(scheduler_), fileSource(fileSource_), @@ -248,6 +263,7 @@ Layer* Style::addLayer(std::unique_ptr layer, optional befor } layer->baseImpl->setObserver(this); + layer->accept(QueueSourceReloadVisitor { updateBatch }); auto added = layers.emplace(before ? findLayer(*before) : layers.end(), std::move(layer))->get(); renderLayers.emplace(before ? findRenderLayer(*before) : renderLayers.end(), added->baseImpl->createRenderLayer()); @@ -726,21 +742,6 @@ void Style::onSpriteError(std::exception_ptr error) { observer->onResourceError(error); } -struct QueueSourceReloadVisitor { - UpdateBatch& updateBatch; - - // No need to reload sources for these types; their visibility can change but - // they don't participate in layout. - void operator()(CustomLayer&) {} - void operator()(RasterLayer&) {} - void operator()(BackgroundLayer&) {} - - template - void operator()(VectorLayer& layer) { - updateBatch.sourceIDs.insert(layer.getSourceID()); - } -}; - void Style::onLayerFilterChanged(Layer& layer) { layer.accept(QueueSourceReloadVisitor { updateBatch }); observer->onUpdate(Update::Repaint); From ad46d67aeb84b258668126dddc00c581f4808f3e Mon Sep 17 00:00:00 2001 From: Tobrun Date: Thu, 11 May 2017 21:00:41 +0200 Subject: [PATCH 03/62] [android] - keep observer when timeout occurs, make observer param nullable, fixup log messages (#8919) --- .../mapbox/mapboxsdk/offline/OfflineRegion.java | 17 ++++++++++++----- .../activity/offline/OfflineActivity.java | 10 ++++------ 2 files changed, 16 insertions(+), 11 deletions(-) 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 a55e8dd8485..2110e12b369 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.Looper; import android.support.annotation.IntDef; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import com.mapbox.mapboxsdk.storage.FileSource; @@ -205,7 +206,7 @@ private boolean deliverMessages() { /** * Constructor - * + *

* For JNI use only, to create a new offline region, use * {@link OfflineManager#createOfflineRegion} instead. */ @@ -247,7 +248,7 @@ private Handler getHandler() { * * @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) { @@ -255,7 +256,9 @@ public void onStatusChanged(final OfflineRegionStatus status) { getHandler().post(new Runnable() { @Override public void run() { - observer.onStatusChanged(status); + if (observer != null) { + observer.onStatusChanged(status); + } } }); } @@ -267,7 +270,9 @@ public void onError(final OfflineRegionError error) { getHandler().post(new Runnable() { @Override public void run() { - observer.onError(error); + if (observer != null) { + observer.onError(error); + } } }); } @@ -279,7 +284,9 @@ public void mapboxTileCountLimitExceeded(final long limit) { getHandler().post(new Runnable() { @Override public void run() { - observer.mapboxTileCountLimitExceeded(limit); + if (observer != null) { + observer.mapboxTileCountLimitExceeded(limit); + } } }); } 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 be5d8094572..344e9e140a3 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 void onStatusChanged(OfflineRegionStatus status) { } // 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); } }); From 850b008a2fd872c6158502b821da17864711bb12 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Thu, 11 May 2017 21:39:17 +0200 Subject: [PATCH 04/62] [android] - avoid crashing when deleting already deleted region (#8920) --- .../mapboxsdk/offline/OfflineRegion.java | 48 +++++++++++-------- 1 file changed, 27 insertions(+), 21 deletions(-) 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 2110e12b369..44bb2a05ee9 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 @@ -37,6 +37,9 @@ public class OfflineRegion { //Region id private long id; + // delete status + private boolean isDeleted; + private OfflineRegionDefinition definition; /** @@ -353,28 +356,31 @@ public void run() { * @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); + } + }); + } + }); + } } /** From e681f69993ea9b28e745e8933744960eb5a608f8 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Fri, 12 May 2017 15:16:13 +0200 Subject: [PATCH 05/62] [android] - update LOST to 2.3.0 (#8872) --- platform/android/MapboxGLAndroidSDK/build.gradle | 4 +++- .../mapbox/mapboxsdk/location/LocationSource.java | 10 ---------- .../android/MapboxGLAndroidSDKTestApp/build.gradle | 3 --- platform/android/build.gradle | 1 + platform/android/dependencies.gradle | 14 +++++++------- 5 files changed, 11 insertions(+), 21 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/build.gradle b/platform/android/MapboxGLAndroidSDK/build.gradle index 1156a6ef434..c4668f598ce 100644 --- a/platform/android/MapboxGLAndroidSDK/build.gradle +++ b/platform/android/MapboxGLAndroidSDK/build.gradle @@ -6,7 +6,9 @@ dependencies { compile rootProject.ext.dep.supportDesign compile rootProject.ext.dep.timber compile rootProject.ext.dep.okhttp3 - compile rootProject.ext.dep.lost + compile(rootProject.ext.dep.lost) { + exclude module: 'support-compat' + } // Mapbox Android Services (GeoJSON support) compile(rootProject.ext.dep.mapboxJavaGeoJSON) { 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 b795cf1d5b2..c4bdb4a17de 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 @@ -135,14 +135,4 @@ public void onLocationChanged(Location location) { 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/MapboxGLAndroidSDKTestApp/build.gradle b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle index 3dc20c9f5b5..56b537e2a23 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle +++ b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle @@ -61,10 +61,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/build.gradle b/platform/android/build.gradle index 120c0219e45..bc90896812f 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/dependencies.gradle b/platform/android/dependencies.gradle index 738f571c097..817697140ee 100644 --- a/platform/android/dependencies.gradle +++ b/platform/android/dependencies.gradle @@ -7,21 +7,21 @@ ext { versionCode = 11 versionName = "5.0.0" - supportLibVersion = "25.3.1" - leakCanaryVersion = '1.5' + mapboxServicesVersion = "2.2.0-SNAPSHOT" + supportLibVersion = "25.1.0" 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:2.3.0-SNAPSHOT', // unit test junit : 'junit:junit:4.12', From cb737362b631284e1516561caf30c2027f9bdc03 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Fri, 12 May 2017 15:27:27 +0200 Subject: [PATCH 06/62] [android] - update proguard config, allow debug mimification, update OkHttp to latest version (#8944) --- .../android/MapboxGLAndroidSDK/build.gradle | 4 +- .../MapboxGLAndroidSDK/proguard-rules.pro | 89 +------------------ .../proguard-rules.pro | 1 + platform/android/dependencies.gradle | 2 +- 4 files changed, 9 insertions(+), 87 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/build.gradle b/platform/android/MapboxGLAndroidSDK/build.gradle index c4668f598ce..fca36237c3c 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 @@ -122,7 +125,6 @@ android { release { // aar proguard configuration - consumerProguardFiles 'proguard-rules.pro' jniDebuggable false } } diff --git a/platform/android/MapboxGLAndroidSDK/proguard-rules.pro b/platform/android/MapboxGLAndroidSDK/proguard-rules.pro index baecd80e50f..8e478154512 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/MapboxGLAndroidSDKTestApp/proguard-rules.pro b/platform/android/MapboxGLAndroidSDKTestApp/proguard-rules.pro index 5d944b5dd44..f8243ca44f3 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/dependencies.gradle b/platform/android/dependencies.gradle index 817697140ee..bbf1fb075d7 100644 --- a/platform/android/dependencies.gradle +++ b/platform/android/dependencies.gradle @@ -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}" From 0ef1293dada1490c808ae821228d5a2f3324afdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguye=CC=82=CC=83n?= Date: Fri, 9 Sep 2016 10:17:42 -0700 Subject: [PATCH 07/62] [core, android, ios, macos, qt] v10 default styles Upgraded from v9 default styles to v10 wherever the developer expects to get the latest and greatest, as well as in a couple tests where it may be beneficial to ensure that we can handle a two-digit version number in the style URL. Cherry-picked from ed54849e9909e7f7cd8724b39b2bc94f16cf9a11. --- .../java/com/mapbox/mapboxsdk/constants/Style.java | 6 +++--- .../src/main/res/values/strings.xml | 6 +++--- .../src/main/res/layout/activity_multi_map.xml | 4 ++-- .../src/main/res/layout/fragment_dialog_map.xml | 2 +- .../src/main/res/layout/activity_simple_mapview.xml | 2 +- platform/darwin/src/MGLStyle.h | 2 +- platform/default/mbgl/util/default_styles.cpp | 12 ++++++------ platform/default/mbgl/util/default_styles.hpp | 2 +- platform/ios/CHANGELOG.md | 2 +- platform/macos/CHANGELOG.md | 1 + test/util/mapbox.test.cpp | 4 ++-- 11 files changed, 22 insertions(+), 21 deletions(-) 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 d55fd4c0236..fae3bdde2cd 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 @@ -35,13 +35,13 @@ public class Style { * constant means your map style will always use the latest version and may change as we * improve the style. */ - public static final String MAPBOX_STREETS = "mapbox://styles/mapbox/streets-v9"; + public static final String MAPBOX_STREETS = "mapbox://styles/mapbox/streets-v10"; /** * Outdoors: A general-purpose style tailored to outdoor activities. Using this constant means * your map style will always use the latest version and may change as we improve the style. */ - public static final String OUTDOORS = "mapbox://styles/mapbox/outdoors-v9"; + public static final String OUTDOORS = "mapbox://styles/mapbox/outdoors-v10"; /** * Light: Subtle light backdrop for data visualizations. Using this constant means your map @@ -66,5 +66,5 @@ public class Style { * constant means your map style will always use the latest version and may change as we * improve the style. */ - public static final String SATELLITE_STREETS = "mapbox://styles/mapbox/satellite-streets-v9"; + public static final String SATELLITE_STREETS = "mapbox://styles/mapbox/satellite-streets-v10"; } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/strings.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/strings.xml index 0862305cfbc..7adc29e2de7 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/strings.xml +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/strings.xml @@ -18,10 +18,10 @@ - mapbox://styles/mapbox/streets-v9 - mapbox://styles/mapbox/outdoors-v9 + mapbox://styles/mapbox/streets-v10 + mapbox://styles/mapbox/outdoors-v10 mapbox://styles/mapbox/light-v9 mapbox://styles/mapbox/dark-v9 mapbox://styles/mapbox/satellite-v9 - mapbox://styles/mapbox/satellite-streets-v9 + mapbox://styles/mapbox/satellite-streets-v10 diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_multi_map.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_multi_map.xml index 0b3fd9acdfc..599ae3fa1c5 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_multi_map.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_multi_map.xml @@ -23,7 +23,7 @@ android:layout_weight="0.5" mapbox:center_latitude="38.913187" mapbox:center_longitude="-77.032546" - mapbox:style_url="mapbox://styles/mapbox/streets-v9" + mapbox:style_url="mapbox://styles/mapbox/streets-v10" mapbox:zoom="12"/> @@ -35,7 +35,7 @@ android:layout_weight="0.5" mapbox:center_latitude="37.775732" mapbox:center_longitude="-122.413985" - mapbox:style_url="mapbox://styles/mapbox/outdoors-v9" + mapbox:style_url="mapbox://styles/mapbox/outdoors-v10" mapbox:zoom="13"/> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_dialog_map.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_dialog_map.xml index 8241d0264f4..afebfa1c475 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_dialog_map.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_dialog_map.xml @@ -13,6 +13,6 @@ mapbox:mapbox_cameraTargetLng="-122.3421" mapbox:mapbox_cameraZoom="11" mapbox:mapbox_renderTextureMode="true" - mapbox:mapbox_styleUrl="mapbox://styles/mapbox/streets-v9" /> + mapbox:mapbox_styleUrl="mapbox://styles/mapbox/streets-v10" /> diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/layout/activity_simple_mapview.xml b/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/layout/activity_simple_mapview.xml index 8f260c46ff9..44374f2c6c6 100644 --- a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/layout/activity_simple_mapview.xml +++ b/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/layout/activity_simple_mapview.xml @@ -15,7 +15,7 @@ android:layout_height="match_parent" mapbox:mapbox_cameraTargetLat="40.73581" mapbox:mapbox_cameraTargetLng="-73.99155" - mapbox:mapbox_styleUrl="mapbox://styles/mapbox/streets-v9" + mapbox:mapbox_styleUrl="mapbox://styles/mapbox/streets-v10" mapbox:mapbox_cameraZoom="11" mapbox:mapbox_uiZoomControls="false"/> diff --git a/platform/darwin/src/MGLStyle.h b/platform/darwin/src/MGLStyle.h index bd17fdec445..0e1403b1142 100644 --- a/platform/darwin/src/MGLStyle.h +++ b/platform/darwin/src/MGLStyle.h @@ -28,7 +28,7 @@ NS_ASSUME_NONNULL_BEGIN the constant itself. Such details may change significantly from version to version. */ -static MGL_EXPORT const NSInteger MGLStyleDefaultVersion = 9; +static MGL_EXPORT const NSInteger MGLStyleDefaultVersion = 10; /** The proxy object for the current map style. diff --git a/platform/default/mbgl/util/default_styles.cpp b/platform/default/mbgl/util/default_styles.cpp index 17cc2f5740a..5457d0f0a2d 100644 --- a/platform/default/mbgl/util/default_styles.cpp +++ b/platform/default/mbgl/util/default_styles.cpp @@ -4,12 +4,12 @@ namespace mbgl { namespace util { namespace default_styles { -const DefaultStyle streets = { "mapbox://styles/mapbox/streets-v9", "Streets" }; -const DefaultStyle outdoors = { "mapbox://styles/mapbox/outdoors-v9", "Outdoors" }; -const DefaultStyle light = { "mapbox://styles/mapbox/light-v9", "Light" }; -const DefaultStyle dark = { "mapbox://styles/mapbox/dark-v9", "Dark" }; -const DefaultStyle satellite = { "mapbox://styles/mapbox/satellite-v9", "Satellite" }; -const DefaultStyle satelliteStreets = { "mapbox://styles/mapbox/satellite-streets-v9", "Satellite Streets" }; +const DefaultStyle streets = { "mapbox://styles/mapbox/streets-v10", "Streets" }; +const DefaultStyle outdoors = { "mapbox://styles/mapbox/outdoors-v10", "Outdoors" }; +const DefaultStyle light = { "mapbox://styles/mapbox/light-v9", "Light" }; +const DefaultStyle dark = { "mapbox://styles/mapbox/dark-v9", "Dark" }; +const DefaultStyle satellite = { "mapbox://styles/mapbox/satellite-v9", "Satellite" }; +const DefaultStyle satelliteStreets = { "mapbox://styles/mapbox/satellite-streets-v10", "Satellite Streets" }; } // namespace default_styles } // end namespace util diff --git a/platform/default/mbgl/util/default_styles.hpp b/platform/default/mbgl/util/default_styles.hpp index eb7e0347223..32944394aa9 100644 --- a/platform/default/mbgl/util/default_styles.hpp +++ b/platform/default/mbgl/util/default_styles.hpp @@ -24,7 +24,7 @@ const DefaultStyle orderedStyles[] = { }; const size_t numOrderedStyles = sizeof(orderedStyles) / sizeof(DefaultStyle); -static const unsigned currentVersion = 9; +static const unsigned currentVersion = 10; } // end namespace default_styles } // end namespace util diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index aa70346d703..e877613f1c4 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -7,6 +7,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT ### Styles * Added support for 3D extrusion of buildings and other polygonal features via the `MGLFillExtrusionStyleLayer` class and the `fill-extrusion` layer type in style JSON. ([#8431](https://github.com/mapbox/mapbox-gl-native/pull/8431)) +* MGLMapView and MGLTilePyramidOfflineRegion now default to version 10 of the Mapbox Streets style. Similarly, several style URL class methods of MGLStyle return URLs to version 10 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 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)) @@ -61,7 +62,6 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT ### Styles * Added support for data-driven styling in the form of source and composite style functions. `MGLStyleFunction` is now an abstract class, with `MGLCameraStyleFunction` providing the behavior of `MGLStyleFunction` in previous releases. New `MGLStyleFunction` subclasses allow you to vary a style attribute by the values of attributes of features in the source. ([#7596](https://github.com/mapbox/mapbox-gl-native/pull/7596)) -* Added methods to MGLShapeSource and MGLVectorSource for querying features loaded by the source, whether or not they’re visible on the map. ([#8263](https://github.com/mapbox/mapbox-gl-native/pull/8263)) * Added `circleStrokeColor`, `circleStrokeWidth`, and `circleStrokeOpacity` properties to MGLCircleStyleLayer and support for corresponding properties in style JSON files. ([#7356](https://github.com/mapbox/mapbox-gl-native/pull/7356)) * Point-placed labels in symbol style layers are now placed at more optimal locations within polygons. ([#7465](https://github.com/mapbox/mapbox-gl-native/pull/7465)) * Fixed flickering that occurred when manipulating a style layer. ([#7616](https://github.com/mapbox/mapbox-gl-native/pull/7616)) diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index d364bc9bf6e..31315541a8f 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -5,6 +5,7 @@ ### Styles * Added support for 3D extrusion of buildings and other polygonal features via the `MGLFillExtrusionStyleLayer` class and the `fill-extrusion` layer type in style JSON. ([#8431](https://github.com/mapbox/mapbox-gl-native/pull/8431)) +* MGLMapView and MGLTilePyramidOfflineRegion now default to version 10 of the Mapbox Streets style. Similarly, several style URL class methods of MGLStyle return URLs to version 10 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 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)) diff --git a/test/util/mapbox.test.cpp b/test/util/mapbox.test.cpp index ba867ccb5ce..cdbd85118fd 100644 --- a/test/util/mapbox.test.cpp +++ b/test/util/mapbox.test.cpp @@ -91,9 +91,9 @@ TEST(Mapbox, SpriteURL) { "https://api.mapbox.com/styles/v1/mapbox/streets-v8/draft/sprite@2x.png?access_token=key", mbgl::util::mapbox::normalizeSpriteURL(util::API_BASE_URL, "mapbox://sprites/mapbox/streets-v8/draft@2x.png", "key")); EXPECT_EQ( - "https://api.mapbox.com/styles/v1/mapbox/streets-v9/sprite?access_token=key&fresh=true.png", + "https://api.mapbox.com/styles/v1/mapbox/streets-v10/sprite?access_token=key&fresh=true.png", mbgl::util::mapbox::normalizeSpriteURL(util::API_BASE_URL, - "mapbox://sprites/mapbox/streets-v9?fresh=true.png", + "mapbox://sprites/mapbox/streets-v10?fresh=true.png", "key")); EXPECT_EQ("mapbox://////", mbgl::util::mapbox::normalizeSpriteURL(util::API_BASE_URL, "mapbox://////", "key")); } From d85471b7deb9c57973f9b70e0dfe4a8f386c8a01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguye=CC=82=CC=83n?= Date: Fri, 9 Sep 2016 10:29:45 -0700 Subject: [PATCH 08/62] [ios, macos] Updated documented default style version MGLStyleDefaultVersion is just for Streets now. Deleted style version documentation tests because not all styles are on the same version. Cherry-picked from ca97dd8cf4c70a5ed380c99700749fd3239715f0. --- platform/darwin/src/MGLStyle.h | 12 ++++++------ platform/darwin/test/MGLStyleTests.mm | 13 ------------- 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/platform/darwin/src/MGLStyle.h b/platform/darwin/src/MGLStyle.h index 0e1403b1142..ab5a20686a9 100644 --- a/platform/darwin/src/MGLStyle.h +++ b/platform/darwin/src/MGLStyle.h @@ -10,9 +10,9 @@ NS_ASSUME_NONNULL_BEGIN /** - A version number identifying the default version of the suite of default styles - provided by Mapbox. This version number may be passed into one of the - “StyleURLWithVersion” class methods of MGLStyle. + A version number identifying the default version of the Mapbox Streets style + obtained through the `-streetsStyleURL` method. This version number may also be + passed into the `-streetsStyleURLWithVersion:` method. The value of this constant generally corresponds to the latest released version as of the date on which this SDK was published. You can use this constant to @@ -72,7 +72,7 @@ MGL_EXPORT is specified explicitly. @param version The style’s latest released version. As of publication, the - current version is `9`. + current version is `10`. */ + (NSURL *)streetsStyleURLWithVersion:(NSInteger)version; @@ -91,7 +91,7 @@ MGL_EXPORT Outdoors is a general-purpose style tailored to outdoor activities. @param version The style’s latest released version. As of publication, the - current version is `9`. + current version is `10`. */ + (NSURL *)outdoorsStyleURLWithVersion:(NSInteger)version; @@ -173,7 +173,7 @@ MGL_EXPORT Streets. @param version The style’s latest released version. As of publication, the - current version is `9`. + current version is `10`. */ + (NSURL *)satelliteStreetsStyleURLWithVersion:(NSInteger)version; diff --git a/platform/darwin/test/MGLStyleTests.mm b/platform/darwin/test/MGLStyleTests.mm index f9598a143d6..8681bddc46d 100644 --- a/platform/darwin/test/MGLStyleTests.mm +++ b/platform/darwin/test/MGLStyleTests.mm @@ -119,19 +119,6 @@ - (void)testStyleURLDeclarations { XCTAssertNil(versionedMethodError, @"Error compiling regular expression to search for versioned methods."); NSUInteger numVersionedMethodDeclarations = [versionedMethodExpression numberOfMatchesInString:styleHeader options:0 range:NSMakeRange(0, styleHeader.length)]; XCTAssertEqual(numVersionedMethodDeclarations, numVersionedMethods); - - // Test that “current version is” statements are present and current for all versioned style methods. - NSError *versionError; - NSString *versionExpressionString = @(R"RE(current version is `(\d+)`)RE"); - NSRegularExpression *versionExpression = [NSRegularExpression regularExpressionWithPattern:versionExpressionString options:0 error:&versionError]; - XCTAssertNil(versionError, @"Error compiling regular expression to search for current version statements."); - NSUInteger numVersionDeclarations = [versionExpression numberOfMatchesInString:styleHeader options:0 range:NSMakeRange(0, styleHeader.length)]; - XCTAssertEqual(numVersionDeclarations, numVersionedMethods); - [versionExpression enumerateMatchesInString:styleHeader options:0 range:NSMakeRange(0, styleHeader.length) usingBlock:^(NSTextCheckingResult * _Nullable result, NSMatchingFlags flags, BOOL * _Nonnull stop) { - XCTAssertEqual(result.numberOfRanges, 2, @"Regular expression should have one capture group."); - NSString *version = [styleHeader substringWithRange:[result rangeAtIndex:1]]; - XCTAssertEqual([version integerValue], MGLStyleDefaultVersion, @"Versioned style URL method should document current version as %ld, not %ld.", MGLStyleDefaultVersion, version.integerValue); - }]; } - (void)testName { From c5ee41a93a1f012acf41f29dc8bd75dc736ff4bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguye=CC=82=CC=83n?= Date: Tue, 25 Apr 2017 20:52:47 -0400 Subject: [PATCH 09/62] [ios, macos] Undeprecated unversioned style URL factory methods Undeprecated the unversioned style URL factory methods in MGLStyle for consistency with the Android and Qt SDKs. Added warnings about using them with the runtime styling API. Refactored mbgl::util::default_styles to track different versions for different styles. Cherry-picked from 9e384b78fbcf46d66d390122eefdf273f91b314e. --- platform/darwin/src/MGLStyle.h | 104 +++++++++++++----- platform/darwin/src/MGLStyle.mm | 10 +- platform/darwin/test/MGLStyleTests.mm | 50 ++++++--- platform/default/mbgl/util/default_styles.cpp | 12 +- platform/default/mbgl/util/default_styles.hpp | 3 +- platform/ios/CHANGELOG.md | 2 +- platform/macos/CHANGELOG.md | 2 +- 7 files changed, 124 insertions(+), 59 deletions(-) diff --git a/platform/darwin/src/MGLStyle.h b/platform/darwin/src/MGLStyle.h index ab5a20686a9..49898a18637 100644 --- a/platform/darwin/src/MGLStyle.h +++ b/platform/darwin/src/MGLStyle.h @@ -52,15 +52,22 @@ MGL_EXPORT #pragma mark Accessing Default Styles /** - Returns the URL to version 8 of the - Mapbox Streets style. + Returns the URL to the current version of the + Mapbox Streets style as of + publication. Streets is a general-purpose style with detailed road and transit networks. `MGLMapView` and `MGLTilePyramidOfflineRegion` use Mapbox Streets when no style is specified explicitly. + + @warning The return value may change in a future release of the SDK. If you use + any feature that depends on a specific aspect of a default style – for + instance, the minimum zoom level that includes roads – use the + `-streetsStyleURLWithVersion:` method instead. Such details may change + significantly from version to version. */ -+ (NSURL *)streetsStyleURL __attribute__((deprecated("Use -streetsStyleURLWithVersion:."))); ++ (NSURL *)streetsStyleURL; /** Returns the URL to the given version of the @@ -71,8 +78,7 @@ MGL_EXPORT `MGLMapView` and `MGLTilePyramidOfflineRegion` use Mapbox Streets when no style is specified explicitly. - @param version The style’s latest released version. As of publication, the - current version is `10`. + @param version A specific version of the style. */ + (NSURL *)streetsStyleURLWithVersion:(NSInteger)version; @@ -84,71 +90,103 @@ MGL_EXPORT */ + (NSURL *)emeraldStyleURL __attribute__((deprecated("Create an NSURL object with the string “mapbox://styles/mapbox/emerald-v8”."))); +/** + Returns the URL to the current version of the + Mapbox Outdoors style as of + publication. + + Outdoors is a general-purpose style tailored to outdoor activities. + + @warning The return value may change in a future release of the SDK. If you use + any feature that depends on a specific aspect of a default style – for + instance, the minimum zoom level that includes roads – use the + `-outdoorsStyleURLWithVersion:` method instead. Such details may change + significantly from version to version. + */ ++ (NSURL *)outdoorsStyleURL; + /** Returns the URL to the given version of the Mapbox Outdoors style. Outdoors is a general-purpose style tailored to outdoor activities. - @param version The style’s latest released version. As of publication, the - current version is `10`. + @param version A specific version of the style. */ + (NSURL *)outdoorsStyleURLWithVersion:(NSInteger)version; /** - Returns the URL to version 8 of the + Returns the URL to the current version of the Mapbox Light style. Light is a subtle, light-colored backdrop for data visualizations. + + @warning The return value may change in a future release of the SDK. If you use + any feature that depends on a specific aspect of a default style – for + instance, the minimum zoom level that includes roads – use the + `-lightStyleURLWithVersion:` method instead. Such details may change + significantly from version to version. */ -+ (NSURL *)lightStyleURL __attribute__((deprecated("Use -lightStyleURLWithVersion:."))); ++ (NSURL *)lightStyleURL; /** Returns the URL to the given version of the - Mapbox Light style. + Mapbox Light style as of + publication. Light is a subtle, light-colored backdrop for data visualizations. - @param version The style’s latest released version. As of publication, the - current version is `9`. + @param version A specific version of the style. */ + (NSURL *)lightStyleURLWithVersion:(NSInteger)version; /** - Returns the URL to version 8 of the + Returns the URL to the current version of the Mapbox Dark style. Dark is a subtle, dark-colored backdrop for data visualizations. + + @warning The return value may change in a future release of the SDK. If you use + any feature that depends on a specific aspect of a default style – for + instance, the minimum zoom level that includes roads – use the + `-darkStyleURLWithVersion:` method instead. Such details may change + significantly from version to version. */ -+ (NSURL *)darkStyleURL __attribute__((deprecated("Use -darkStyleURLWithVersion:."))); ++ (NSURL *)darkStyleURL; /** Returns the URL to the given version of the - Mapbox Dark style. + Mapbox Dark style as of + publication. Dark is a subtle, dark-colored backdrop for data visualizations. - @param version The style’s latest released version. As of publication, the - current version is `9`. + @param version A specific version of the style. */ + (NSURL *)darkStyleURLWithVersion:(NSInteger)version; /** - Returns the URL to version 8 of the + Returns the URL to the current version of the Mapbox Satellite style. Satellite is high-resolution satellite and aerial imagery. + + @warning The return value may change in a future release of the SDK. If you use + any feature that depends on a specific aspect of a default style – for + instance, the raster tile sets included in the style – use the + `-satelliteStyleURLWithVersion:` method instead. Such details may change + significantly from version to version. */ -+ (NSURL *)satelliteStyleURL __attribute__((deprecated("Use -satelliteStyleURLWithVersion:."))); ++ (NSURL *)satelliteStyleURL; /** Returns the URL to the given version of the - Mapbox Satellite style. + Mapbox Satellite style as + of publication. Satellite is high-resolution satellite and aerial imagery. - @param version The style’s latest released version. As of publication, the - current version is `9`. + @param version A specific version of the style. */ + (NSURL *)satelliteStyleURLWithVersion:(NSInteger)version; @@ -161,7 +199,24 @@ MGL_EXPORT Mapbox Satellite with unobtrusive labels and translucent roads from Mapbox Streets. */ -+ (NSURL *)hybridStyleURL __attribute__((deprecated("Use -satelliteStreetsStyleURLWithVersion:."))); ++ (NSURL *)hybridStyleURL __attribute__((deprecated("Use -satelliteStreetsStyleURL."))); + +/** + Returns the URL to the current version of the + Mapbox Satellite Streets + style as of publication. + + Satellite Streets combines the high-resolution satellite and aerial imagery of + Mapbox Satellite with unobtrusive labels and translucent roads from Mapbox + Streets. + + @warning The return value may change in a future release of the SDK. If you use + any feature that depends on a specific aspect of a default style – for + instance, the minimum zoom level that includes roads – use the + `-satelliteStreetsStyleURLWithVersion:` method instead. Such details may + change significantly from version to version. + */ ++ (NSURL *)satelliteStreetsStyleURL; /** Returns the URL to the given version of the @@ -172,8 +227,7 @@ MGL_EXPORT Mapbox Satellite with unobtrusive labels and translucent roads from Mapbox Streets. - @param version The style’s latest released version. As of publication, the - current version is `10`. + @param version A specific version of the style. */ + (NSURL *)satelliteStreetsStyleURLWithVersion:(NSInteger)version; diff --git a/platform/darwin/src/MGLStyle.mm b/platform/darwin/src/MGLStyle.mm index 81b6446e7fa..ce9e55bb994 100644 --- a/platform/darwin/src/MGLStyle.mm +++ b/platform/darwin/src/MGLStyle.mm @@ -58,8 +58,6 @@ @implementation MGLStyle #pragma mark Default style URLs -static_assert(mbgl::util::default_styles::currentVersion == MGLStyleDefaultVersion, "mbgl::util::default_styles::currentVersion and MGLStyleDefaultVersion disagree."); - /// @param name The style’s marketing name, written in lower camelCase. /// @param fileName The last path component in the style’s URL, excluding the version suffix. #define MGL_DEFINE_STYLE(name, fileName) \ @@ -67,17 +65,13 @@ @implementation MGLStyle + (NSURL *)name##StyleURL { \ static dispatch_once_t onceToken; \ dispatch_once(&onceToken, ^{ \ - MGLStyleURL_##name = [self name##StyleURLWithVersion:8]; \ + MGLStyleURL_##name = [self name##StyleURLWithVersion:mbgl::util::default_styles::name.currentVersion]; \ }); \ return MGLStyleURL_##name; \ } \ \ + (NSURL *)name##StyleURL##WithVersion:(NSInteger)version { \ - if (mbgl::util::default_styles::currentVersion == version) { \ - return [NSURL URLWithString:@(mbgl::util::default_styles::name.url)]; \ - } else { \ - return [NSURL URLWithString:[@"mapbox://styles/mapbox/" #fileName "-v" stringByAppendingFormat:@"%li", (long)version]]; \ - } \ + return [NSURL URLWithString:[@"mapbox://styles/mapbox/" #fileName "-v" stringByAppendingFormat:@"%li", (long)version]]; \ } MGL_DEFINE_STYLE(streets, streets) diff --git a/platform/darwin/test/MGLStyleTests.mm b/platform/darwin/test/MGLStyleTests.mm index 8681bddc46d..a2ad1cbb3f6 100644 --- a/platform/darwin/test/MGLStyleTests.mm +++ b/platform/darwin/test/MGLStyleTests.mm @@ -55,13 +55,19 @@ - (MGLStyle *)style { } - (void)testUnversionedStyleURLs { + XCTAssertEqual(mbgl::util::default_styles::streets.currentVersion, MGLStyleDefaultVersion, + "mbgl::util::default_styles::streets.currentVersion and MGLStyleDefaultVersion disagree."); + + XCTAssertEqualObjects([MGLStyle streetsStyleURL].absoluteString, @(mbgl::util::default_styles::streets.url)); + XCTAssertEqualObjects([MGLStyle outdoorsStyleURL].absoluteString, @(mbgl::util::default_styles::outdoors.url)); + XCTAssertEqualObjects([MGLStyle lightStyleURL].absoluteString, @(mbgl::util::default_styles::light.url)); + XCTAssertEqualObjects([MGLStyle darkStyleURL].absoluteString, @(mbgl::util::default_styles::dark.url)); + XCTAssertEqualObjects([MGLStyle satelliteStyleURL].absoluteString, @(mbgl::util::default_styles::satellite.url)); + XCTAssertEqualObjects([MGLStyle satelliteStreetsStyleURL].absoluteString, @(mbgl::util::default_styles::satelliteStreets.url)); + #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" - XCTAssertEqualObjects([MGLStyle streetsStyleURL].absoluteString, @"mapbox://styles/mapbox/streets-v8"); XCTAssertEqualObjects([MGLStyle emeraldStyleURL].absoluteString, @"mapbox://styles/mapbox/emerald-v8"); - XCTAssertEqualObjects([MGLStyle lightStyleURL].absoluteString, @"mapbox://styles/mapbox/light-v8"); - XCTAssertEqualObjects([MGLStyle darkStyleURL].absoluteString, @"mapbox://styles/mapbox/dark-v8"); - XCTAssertEqualObjects([MGLStyle satelliteStyleURL].absoluteString, @"mapbox://styles/mapbox/satellite-v8"); XCTAssertEqualObjects([MGLStyle hybridStyleURL].absoluteString, @"mapbox://styles/mapbox/satellite-hybrid-v8"); #pragma clang diagnostic pop } @@ -69,18 +75,30 @@ - (void)testUnversionedStyleURLs { - (void)testVersionedStyleURLs { // Test that all the default styles have publicly-declared MGLStyle class // methods and that the URLs all have the right values. - XCTAssertEqualObjects([MGLStyle streetsStyleURLWithVersion:MGLStyleDefaultVersion].absoluteString, @(mbgl::util::default_styles::streets.url)); - XCTAssertEqualObjects([MGLStyle streetsStyleURLWithVersion:99].absoluteString, @"mapbox://styles/mapbox/streets-v99"); - XCTAssertEqualObjects([MGLStyle outdoorsStyleURLWithVersion:MGLStyleDefaultVersion].absoluteString, @(mbgl::util::default_styles::outdoors.url)); - XCTAssertEqualObjects([MGLStyle outdoorsStyleURLWithVersion:99].absoluteString, @"mapbox://styles/mapbox/outdoors-v99"); - XCTAssertEqualObjects([MGLStyle lightStyleURLWithVersion:MGLStyleDefaultVersion].absoluteString, @(mbgl::util::default_styles::light.url)); - XCTAssertEqualObjects([MGLStyle lightStyleURLWithVersion:99].absoluteString, @"mapbox://styles/mapbox/light-v99"); - XCTAssertEqualObjects([MGLStyle darkStyleURLWithVersion:MGLStyleDefaultVersion].absoluteString, @(mbgl::util::default_styles::dark.url)); - XCTAssertEqualObjects([MGLStyle darkStyleURLWithVersion:99].absoluteString, @"mapbox://styles/mapbox/dark-v99"); - XCTAssertEqualObjects([MGLStyle satelliteStyleURLWithVersion:MGLStyleDefaultVersion].absoluteString, @(mbgl::util::default_styles::satellite.url)); - XCTAssertEqualObjects([MGLStyle satelliteStyleURLWithVersion:99].absoluteString, @"mapbox://styles/mapbox/satellite-v99"); - XCTAssertEqualObjects([MGLStyle satelliteStreetsStyleURLWithVersion:MGLStyleDefaultVersion].absoluteString, @(mbgl::util::default_styles::satelliteStreets.url)); - XCTAssertEqualObjects([MGLStyle satelliteStreetsStyleURLWithVersion:99].absoluteString, @"mapbox://styles/mapbox/satellite-streets-v99"); + XCTAssertEqualObjects([MGLStyle streetsStyleURLWithVersion:mbgl::util::default_styles::streets.currentVersion].absoluteString, + @(mbgl::util::default_styles::streets.url)); + XCTAssertEqualObjects([MGLStyle streetsStyleURLWithVersion:99].absoluteString, + @"mapbox://styles/mapbox/streets-v99"); + XCTAssertEqualObjects([MGLStyle outdoorsStyleURLWithVersion:mbgl::util::default_styles::outdoors.currentVersion].absoluteString, + @(mbgl::util::default_styles::outdoors.url)); + XCTAssertEqualObjects([MGLStyle outdoorsStyleURLWithVersion:99].absoluteString, + @"mapbox://styles/mapbox/outdoors-v99"); + XCTAssertEqualObjects([MGLStyle lightStyleURLWithVersion:mbgl::util::default_styles::light.currentVersion].absoluteString, + @(mbgl::util::default_styles::light.url)); + XCTAssertEqualObjects([MGLStyle lightStyleURLWithVersion:99].absoluteString, + @"mapbox://styles/mapbox/light-v99"); + XCTAssertEqualObjects([MGLStyle darkStyleURLWithVersion:mbgl::util::default_styles::dark.currentVersion].absoluteString, + @(mbgl::util::default_styles::dark.url)); + XCTAssertEqualObjects([MGLStyle darkStyleURLWithVersion:99].absoluteString, + @"mapbox://styles/mapbox/dark-v99"); + XCTAssertEqualObjects([MGLStyle satelliteStyleURLWithVersion:mbgl::util::default_styles::satellite.currentVersion].absoluteString, + @(mbgl::util::default_styles::satellite.url)); + XCTAssertEqualObjects([MGLStyle satelliteStyleURLWithVersion:99].absoluteString, + @"mapbox://styles/mapbox/satellite-v99"); + XCTAssertEqualObjects([MGLStyle satelliteStreetsStyleURLWithVersion:mbgl::util::default_styles::satelliteStreets.currentVersion].absoluteString, + @(mbgl::util::default_styles::satelliteStreets.url)); + XCTAssertEqualObjects([MGLStyle satelliteStreetsStyleURLWithVersion:99].absoluteString, + @"mapbox://styles/mapbox/satellite-streets-v99"); static_assert(6 == mbgl::util::default_styles::numOrderedStyles, "MGLStyleTests isn’t testing all the styles in mbgl::util::default_styles."); diff --git a/platform/default/mbgl/util/default_styles.cpp b/platform/default/mbgl/util/default_styles.cpp index 5457d0f0a2d..e051817adaa 100644 --- a/platform/default/mbgl/util/default_styles.cpp +++ b/platform/default/mbgl/util/default_styles.cpp @@ -4,12 +4,12 @@ namespace mbgl { namespace util { namespace default_styles { -const DefaultStyle streets = { "mapbox://styles/mapbox/streets-v10", "Streets" }; -const DefaultStyle outdoors = { "mapbox://styles/mapbox/outdoors-v10", "Outdoors" }; -const DefaultStyle light = { "mapbox://styles/mapbox/light-v9", "Light" }; -const DefaultStyle dark = { "mapbox://styles/mapbox/dark-v9", "Dark" }; -const DefaultStyle satellite = { "mapbox://styles/mapbox/satellite-v9", "Satellite" }; -const DefaultStyle satelliteStreets = { "mapbox://styles/mapbox/satellite-streets-v10", "Satellite Streets" }; +const DefaultStyle streets = { "mapbox://styles/mapbox/streets-v10", "Streets", 10 }; +const DefaultStyle outdoors = { "mapbox://styles/mapbox/outdoors-v10", "Outdoors", 10 }; +const DefaultStyle light = { "mapbox://styles/mapbox/light-v9", "Light", 9 }; +const DefaultStyle dark = { "mapbox://styles/mapbox/dark-v9", "Dark", 9 }; +const DefaultStyle satellite = { "mapbox://styles/mapbox/satellite-v9", "Satellite", 9 }; +const DefaultStyle satelliteStreets = { "mapbox://styles/mapbox/satellite-streets-v10", "Satellite Streets", 10 }; } // namespace default_styles } // end namespace util diff --git a/platform/default/mbgl/util/default_styles.hpp b/platform/default/mbgl/util/default_styles.hpp index 32944394aa9..5e0d083d82f 100644 --- a/platform/default/mbgl/util/default_styles.hpp +++ b/platform/default/mbgl/util/default_styles.hpp @@ -10,6 +10,7 @@ namespace default_styles { struct DefaultStyle { const char* url; const char* name; + const unsigned currentVersion; }; extern const DefaultStyle streets; @@ -24,8 +25,6 @@ const DefaultStyle orderedStyles[] = { }; const size_t numOrderedStyles = sizeof(orderedStyles) / sizeof(DefaultStyle); -static const unsigned currentVersion = 10; - } // end namespace default_styles } // end namespace util } // end namespace mbgl diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index e877613f1c4..b975030b1b3 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -7,7 +7,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT ### Styles * Added support for 3D extrusion of buildings and other polygonal features via the `MGLFillExtrusionStyleLayer` class and the `fill-extrusion` layer type in style JSON. ([#8431](https://github.com/mapbox/mapbox-gl-native/pull/8431)) -* MGLMapView and MGLTilePyramidOfflineRegion now default to version 10 of the Mapbox Streets style. Similarly, several style URL class methods of MGLStyle return URLs to version 10 styles. ([#6301](https://github.com/mapbox/mapbox-gl-native/pull/6301)) +* MGLMapView and MGLTilePyramidOfflineRegion now default to version 10 of the Mapbox Streets style. Similarly, several style URL class methods of MGLStyle return URLs to version 10 styles. Unversioned variations of these methods are no longer deprecated. `MGLStyleDefaultVersion` should no longer be used with any style other than Streets. ([#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 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)) diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index 31315541a8f..298cc8ecfc4 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -5,7 +5,7 @@ ### Styles * Added support for 3D extrusion of buildings and other polygonal features via the `MGLFillExtrusionStyleLayer` class and the `fill-extrusion` layer type in style JSON. ([#8431](https://github.com/mapbox/mapbox-gl-native/pull/8431)) -* MGLMapView and MGLTilePyramidOfflineRegion now default to version 10 of the Mapbox Streets style. Similarly, several style URL class methods of MGLStyle return URLs to version 10 styles. ([#6301](https://github.com/mapbox/mapbox-gl-native/pull/6301)) +* MGLMapView and MGLTilePyramidOfflineRegion now default to version 10 of the Mapbox Streets style. Similarly, several style URL class methods of MGLStyle return URLs to version 10 styles. Unversioned variations of these methods are no longer deprecated. `MGLStyleDefaultVersion` should no longer be used with any style other than Streets. ([#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 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)) From 886bf2528ff1615a50521e75fc6ea211cef3b8d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguye=CC=82=CC=83n?= Date: Mon, 8 May 2017 17:45:22 -0700 Subject: [PATCH 10/62] [core, android, ios, macos] Added Traffic Day/Night to default styles The Styles API section of the Mapbox API Documentation site now lists Traffic Day v2 and Traffic Night v2, so this change adds those styles to all the places where styles are listed. Also switched iosapp and macosapp to unversioned style factory methods since MGLStyleDefaultVersion is no longer applicable for all styles. Cherry-picked from 4d6f54553d277c0af24a0b8ff03d2dd7e9284ca2. --- platform/android/CHANGELOG.md | 1 + .../com/mapbox/mapboxsdk/constants/Style.java | 20 +++++- .../src/main/res/values/strings.xml | 2 + platform/darwin/src/MGLStyle.h | 62 +++++++++++++++++++ platform/darwin/src/MGLStyle.mm | 4 +- platform/darwin/test/MGLStyleTests.mm | 12 +++- platform/default/mbgl/util/default_styles.cpp | 2 + platform/default/mbgl/util/default_styles.hpp | 3 + platform/ios/CHANGELOG.md | 1 + platform/ios/app/MBXViewController.m | 16 +++-- platform/macos/CHANGELOG.md | 1 + platform/macos/app/Base.lproj/MainMenu.xib | 22 +++++-- platform/macos/app/Base.lproj/MapDocument.xib | 16 +++-- platform/macos/app/MapDocument.m | 47 ++++++++------ 14 files changed, 170 insertions(+), 39 deletions(-) diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index 4f12907f382..fdc6343b7c6 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -37,6 +37,7 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to * Derived source attribution [#8630](https://github.com/mapbox/mapbox-gl-native/pull/8630) * Consistent use of duration unit [#8578](https://github.com/mapbox/mapbox-gl-native/pull/8578) * Swedish localization [#8883](https://github.com/mapbox/mapbox-gl-native/pull/8883) +* Streets v10, Outdoors v10, Satellite Streets v10, Traffic Day v2, Traffic Night v2 [#6301](https://github.com/mapbox/mapbox-gl-native/pull/6301) ## 5.0.2 - April 3, 2017 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 fae3bdde2cd..77d0929df3e 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 @@ -23,7 +23,7 @@ public class Style { * constants means your map style will always use the latest version and may change as we * improve the style */ - @StringDef( {MAPBOX_STREETS, OUTDOORS, LIGHT, DARK, SATELLITE, SATELLITE_STREETS}) + @StringDef( {MAPBOX_STREETS, OUTDOORS, LIGHT, DARK, SATELLITE, SATELLITE_STREETS, TRAFFIC_DAY, TRAFFIC_NIGHT}) @Retention(RetentionPolicy.SOURCE) public @interface StyleUrl { } @@ -67,4 +67,22 @@ public class Style { * improve the style. */ public static final String SATELLITE_STREETS = "mapbox://styles/mapbox/satellite-streets-v10"; + + /** + * Traffic Day: Color-coded roads based on live traffic congestion data. Traffic data is currently + * available in + * these select + * countries. Using this constant means your map style will always use the latest version and + * may change as we improve the style. + */ + public static final String TRAFFIC_DAY = "mapbox://styles/mapbox/traffic-day-v2"; + + /** + * Traffic Night: Color-coded roads based on live traffic congestion data, designed to maximize + * legibility in low-light situations. Traffic data is currently available in + * these select + * countries. Using this constant means your map style will always use the latest version and + * may change as we improve the style. + */ + public static final String TRAFFIC_NIGHT = "mapbox://styles/mapbox/traffic-night-v2"; } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/strings.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/strings.xml index 7adc29e2de7..65fb3e14a3a 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/strings.xml +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/strings.xml @@ -24,4 +24,6 @@ mapbox://styles/mapbox/dark-v9 mapbox://styles/mapbox/satellite-v9 mapbox://styles/mapbox/satellite-streets-v10 + mapbox://styles/mapbox/traffic-day-v2 + mapbox://styles/mapbox/traffic-night-v2 diff --git a/platform/darwin/src/MGLStyle.h b/platform/darwin/src/MGLStyle.h index 49898a18637..26434eb4927 100644 --- a/platform/darwin/src/MGLStyle.h +++ b/platform/darwin/src/MGLStyle.h @@ -231,6 +231,68 @@ MGL_EXPORT */ + (NSURL *)satelliteStreetsStyleURLWithVersion:(NSInteger)version; +/** + Returns the URL to the current version of the + Mapbox Traffic Day + style. + + Traffic Day color-codes roads based on live traffic congestion data. Traffic + data is currently available in + these select countries. + + @warning The return value may change in a future release of the SDK. If you use + any feature that depends on a specific aspect of a default style – for + instance, the minimum zoom level that includes roads – use the + `-trafficDayStyleURLWithVersion:` method instead. Such details may change + significantly from version to version. + */ ++ (NSURL *)trafficDayStyleURL; + +/** + Returns the URL to the given version of the + Mapbox Traffic Day + style as of publication. + + Traffic Day color-codes roads based on live traffic congestion data. Traffic + data is currently available in + these select countries. + + @param version A specific version of the style. + */ ++ (NSURL *)trafficDayStyleURLWithVersion:(NSInteger)version; + +/** + Returns the URL to the current version of the + Mapbox Traffic Night + style. + + Traffic Night color-codes roads based on live traffic congestion data and is + designed to maximize legibility in low-light situations. Traffic data is + currently available in + these select countries. + + @warning The return value may change in a future release of the SDK. If you use + any feature that depends on a specific aspect of a default style – for + instance, the minimum zoom level that includes roads – use the + `-trafficNightStyleURLWithVersion:` method instead. Such details may change + significantly from version to version. + */ ++ (NSURL *)trafficNightStyleURL; + +/** + Returns the URL to the given version of the + Mapbox Traffic Night + style as of publication. + + Traffic Night color-codes roads based on live traffic congestion data and is + designed to maximize legibility in low-light situations. Traffic data is + currently available in + these select countries. + + @param version A specific version of the style. + */ ++ (NSURL *)trafficNightStyleURLWithVersion:(NSInteger)version; + #pragma mark Accessing Metadata About the Style /** diff --git a/platform/darwin/src/MGLStyle.mm b/platform/darwin/src/MGLStyle.mm index ce9e55bb994..af02c31b6d2 100644 --- a/platform/darwin/src/MGLStyle.mm +++ b/platform/darwin/src/MGLStyle.mm @@ -80,10 +80,12 @@ + (NSURL *)name##StyleURL##WithVersion:(NSInteger)version { \ MGL_DEFINE_STYLE(dark, dark) MGL_DEFINE_STYLE(satellite, satellite) MGL_DEFINE_STYLE(satelliteStreets, satellite-streets) +MGL_DEFINE_STYLE(trafficDay, traffic-day) +MGL_DEFINE_STYLE(trafficNight, traffic-night) // Make sure all the styles listed in mbgl::util::default_styles::orderedStyles // are defined above and also declared in MGLStyle.h. -static_assert(6 == mbgl::util::default_styles::numOrderedStyles, +static_assert(8 == mbgl::util::default_styles::numOrderedStyles, "mbgl::util::default_styles::orderedStyles and MGLStyle have different numbers of styles."); // Hybrid has been renamed Satellite Streets, so the last Hybrid version is hard-coded here. diff --git a/platform/darwin/test/MGLStyleTests.mm b/platform/darwin/test/MGLStyleTests.mm index a2ad1cbb3f6..f80d5776f03 100644 --- a/platform/darwin/test/MGLStyleTests.mm +++ b/platform/darwin/test/MGLStyleTests.mm @@ -99,8 +99,16 @@ - (void)testVersionedStyleURLs { @(mbgl::util::default_styles::satelliteStreets.url)); XCTAssertEqualObjects([MGLStyle satelliteStreetsStyleURLWithVersion:99].absoluteString, @"mapbox://styles/mapbox/satellite-streets-v99"); - - static_assert(6 == mbgl::util::default_styles::numOrderedStyles, + XCTAssertEqualObjects([MGLStyle trafficDayStyleURLWithVersion:mbgl::util::default_styles::trafficDay.currentVersion].absoluteString, + @(mbgl::util::default_styles::trafficDay.url)); + XCTAssertEqualObjects([MGLStyle trafficDayStyleURLWithVersion:99].absoluteString, + @"mapbox://styles/mapbox/traffic-day-v99"); + XCTAssertEqualObjects([MGLStyle trafficNightStyleURLWithVersion:mbgl::util::default_styles::trafficNight.currentVersion].absoluteString, + @(mbgl::util::default_styles::trafficNight.url)); + XCTAssertEqualObjects([MGLStyle trafficNightStyleURLWithVersion:99].absoluteString, + @"mapbox://styles/mapbox/traffic-night-v99"); + + static_assert(8 == mbgl::util::default_styles::numOrderedStyles, "MGLStyleTests isn’t testing all the styles in mbgl::util::default_styles."); } diff --git a/platform/default/mbgl/util/default_styles.cpp b/platform/default/mbgl/util/default_styles.cpp index e051817adaa..5f4ca862fe8 100644 --- a/platform/default/mbgl/util/default_styles.cpp +++ b/platform/default/mbgl/util/default_styles.cpp @@ -10,6 +10,8 @@ const DefaultStyle light = { "mapbox://styles/mapbox/light-v9", const DefaultStyle dark = { "mapbox://styles/mapbox/dark-v9", "Dark", 9 }; const DefaultStyle satellite = { "mapbox://styles/mapbox/satellite-v9", "Satellite", 9 }; const DefaultStyle satelliteStreets = { "mapbox://styles/mapbox/satellite-streets-v10", "Satellite Streets", 10 }; +const DefaultStyle trafficDay = { "mapbox://styles/mapbox/traffic-day-v2", "Traffic Day", 2 }; +const DefaultStyle trafficNight = { "mapbox://styles/mapbox/traffic-night-v2", "Traffic Night", 2 }; } // namespace default_styles } // end namespace util diff --git a/platform/default/mbgl/util/default_styles.hpp b/platform/default/mbgl/util/default_styles.hpp index 5e0d083d82f..466102d6233 100644 --- a/platform/default/mbgl/util/default_styles.hpp +++ b/platform/default/mbgl/util/default_styles.hpp @@ -19,9 +19,12 @@ extern const DefaultStyle light; extern const DefaultStyle dark; extern const DefaultStyle satellite; extern const DefaultStyle satelliteStreets; +extern const DefaultStyle trafficDay; +extern const DefaultStyle trafficNight; const DefaultStyle orderedStyles[] = { streets, outdoors, light, dark, satellite, satelliteStreets, + trafficDay, trafficNight, }; const size_t numOrderedStyles = sizeof(orderedStyles) / sizeof(DefaultStyle); diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index b975030b1b3..922b7b6486c 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -8,6 +8,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Added support for 3D extrusion of buildings and other polygonal features via the `MGLFillExtrusionStyleLayer` class and the `fill-extrusion` layer type in style JSON. ([#8431](https://github.com/mapbox/mapbox-gl-native/pull/8431)) * MGLMapView and MGLTilePyramidOfflineRegion now default to version 10 of the Mapbox Streets style. Similarly, several style URL class methods of MGLStyle return URLs to version 10 styles. Unversioned variations of these methods are no longer deprecated. `MGLStyleDefaultVersion` should no longer be used with any style other than Streets. ([#6301](https://github.com/mapbox/mapbox-gl-native/pull/6301)) +* 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 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)) diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m index 70271c02b9a..209998adb9c 100644 --- a/platform/ios/app/MBXViewController.m +++ b/platform/ios/app/MBXViewController.m @@ -1543,14 +1543,18 @@ - (IBAction)cycleStyles:(__unused id)sender @"Dark", @"Satellite", @"Satellite Streets", + @"Traffic Day", + @"Traffic Night", ]; styleURLs = @[ - [MGLStyle streetsStyleURLWithVersion:MGLStyleDefaultVersion], - [MGLStyle outdoorsStyleURLWithVersion:MGLStyleDefaultVersion], - [MGLStyle lightStyleURLWithVersion:MGLStyleDefaultVersion], - [MGLStyle darkStyleURLWithVersion:MGLStyleDefaultVersion], - [MGLStyle satelliteStyleURLWithVersion:MGLStyleDefaultVersion], - [MGLStyle satelliteStreetsStyleURLWithVersion:MGLStyleDefaultVersion], + [MGLStyle streetsStyleURL], + [MGLStyle outdoorsStyleURL], + [MGLStyle lightStyleURL], + [MGLStyle darkStyleURL], + [MGLStyle satelliteStyleURL], + [MGLStyle satelliteStreetsStyleURL], + [MGLStyle trafficDayStyleURL], + [MGLStyle trafficNightStyleURL], ]; NSAssert(styleNames.count == styleURLs.count, @"Style names and URLs don’t match."); diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index 298cc8ecfc4..632bcfa44b6 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -6,6 +6,7 @@ * Added support for 3D extrusion of buildings and other polygonal features via the `MGLFillExtrusionStyleLayer` class and the `fill-extrusion` layer type in style JSON. ([#8431](https://github.com/mapbox/mapbox-gl-native/pull/8431)) * MGLMapView and MGLTilePyramidOfflineRegion now default to version 10 of the Mapbox Streets style. Similarly, several style URL class methods of MGLStyle return URLs to version 10 styles. Unversioned variations of these methods are no longer deprecated. `MGLStyleDefaultVersion` should no longer be used with any style other than Streets. ([#6301](https://github.com/mapbox/mapbox-gl-native/pull/6301)) +* 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 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)) diff --git a/platform/macos/app/Base.lproj/MainMenu.xib b/platform/macos/app/Base.lproj/MainMenu.xib index 941bed21368..20a4f65b3f3 100644 --- a/platform/macos/app/Base.lproj/MainMenu.xib +++ b/platform/macos/app/Base.lproj/MainMenu.xib @@ -1,7 +1,7 @@ - - + + - + @@ -395,6 +395,16 @@ + + + + + + + + + + @@ -643,7 +653,7 @@ CA - + @@ -654,7 +664,7 @@ CA - + @@ -723,7 +733,7 @@ CA - + diff --git a/platform/macos/app/Base.lproj/MapDocument.xib b/platform/macos/app/Base.lproj/MapDocument.xib index 065acc41fd0..d95f21b2e95 100644 --- a/platform/macos/app/Base.lproj/MapDocument.xib +++ b/platform/macos/app/Base.lproj/MapDocument.xib @@ -1,7 +1,7 @@ - + - + @@ -48,7 +48,7 @@ - + @@ -183,9 +183,9 @@ - + - + @@ -204,6 +204,12 @@ + + + + + + diff --git a/platform/macos/app/MapDocument.m b/platform/macos/app/MapDocument.m index 5be17c1cbee..59c1817f638 100644 --- a/platform/macos/app/MapDocument.m +++ b/platform/macos/app/MapDocument.m @@ -165,22 +165,28 @@ - (IBAction)showStyle:(id)sender { NSURL *styleURL; switch (tag) { case 1: - styleURL = [MGLStyle streetsStyleURLWithVersion:MGLStyleDefaultVersion]; + styleURL = [MGLStyle streetsStyleURL]; break; case 2: - styleURL = [MGLStyle outdoorsStyleURLWithVersion:MGLStyleDefaultVersion]; + styleURL = [MGLStyle outdoorsStyleURL]; break; case 3: - styleURL = [MGLStyle lightStyleURLWithVersion:MGLStyleDefaultVersion]; + styleURL = [MGLStyle lightStyleURL]; break; case 4: - styleURL = [MGLStyle darkStyleURLWithVersion:MGLStyleDefaultVersion]; + styleURL = [MGLStyle darkStyleURL]; break; case 5: - styleURL = [MGLStyle satelliteStyleURLWithVersion:MGLStyleDefaultVersion]; + styleURL = [MGLStyle satelliteStyleURL]; break; case 6: - styleURL = [MGLStyle satelliteStreetsStyleURLWithVersion:MGLStyleDefaultVersion]; + styleURL = [MGLStyle satelliteStreetsStyleURL]; + break; + case 7: + styleURL = [MGLStyle trafficDayStyleURL]; + break; + case 8: + styleURL = [MGLStyle trafficNightStyleURL]; break; default: NSAssert(NO, @"Cannot set style from control with tag %li", (long)tag); @@ -781,22 +787,25 @@ - (BOOL)validateMenuItem:(NSMenuItem *)menuItem { NSCellStateValue state; switch (menuItem.tag) { case 1: - state = [styleURL isEqual:[MGLStyle streetsStyleURLWithVersion:MGLStyleDefaultVersion]]; + state = [styleURL isEqual:[MGLStyle streetsStyleURL]]; break; case 2: - state = [styleURL isEqual:[MGLStyle outdoorsStyleURLWithVersion:MGLStyleDefaultVersion]]; + state = [styleURL isEqual:[MGLStyle outdoorsStyleURL]]; break; case 3: - state = [styleURL isEqual:[MGLStyle lightStyleURLWithVersion:MGLStyleDefaultVersion]]; + state = [styleURL isEqual:[MGLStyle lightStyleURL]]; break; case 4: - state = [styleURL isEqual:[MGLStyle darkStyleURLWithVersion:MGLStyleDefaultVersion]]; + state = [styleURL isEqual:[MGLStyle darkStyleURL]]; break; case 5: - state = [styleURL isEqual:[MGLStyle satelliteStyleURLWithVersion:MGLStyleDefaultVersion]]; + state = [styleURL isEqual:[MGLStyle satelliteStyleURL]]; break; case 6: - state = [styleURL isEqual:[MGLStyle satelliteStreetsStyleURLWithVersion:MGLStyleDefaultVersion]]; + state = [styleURL isEqual:[MGLStyle satelliteStreetsStyleURL]]; + break; + case 7: + state = [styleURL isEqual:[MGLStyle trafficDayStyleURL]]; break; default: return NO; @@ -954,12 +963,14 @@ - (NSUInteger)indexOfStyleInToolbarItem { } NSArray *styleURLs = @[ - [MGLStyle streetsStyleURLWithVersion:MGLStyleDefaultVersion], - [MGLStyle outdoorsStyleURLWithVersion:MGLStyleDefaultVersion], - [MGLStyle lightStyleURLWithVersion:MGLStyleDefaultVersion], - [MGLStyle darkStyleURLWithVersion:MGLStyleDefaultVersion], - [MGLStyle satelliteStyleURLWithVersion:MGLStyleDefaultVersion], - [MGLStyle satelliteStreetsStyleURLWithVersion:MGLStyleDefaultVersion], + [MGLStyle streetsStyleURL], + [MGLStyle outdoorsStyleURL], + [MGLStyle lightStyleURL], + [MGLStyle darkStyleURL], + [MGLStyle satelliteStyleURL], + [MGLStyle satelliteStreetsStyleURL], + [MGLStyle trafficDayStyleURL], + [MGLStyle trafficNightStyleURL], ]; return [styleURLs indexOfObject:self.mapView.styleURL]; } From a68788a2cfb85efe69b0d81672bff7272368494e Mon Sep 17 00:00:00 2001 From: Tobrun Date: Fri, 12 May 2017 20:07:06 +0200 Subject: [PATCH 11/62] [android] Release android-v5.1.0-beta.2 (#8976) * [android] url getter on sources * [android] fix ui test filter in makefile * [android] - build SNAPSHOT from release branch (#8958) * [android] - update changelog for 5.1.0-beta.2 * [android] - bump version number * [android] - Camera change listener v2.0 * [core] allow filesource url transform reset * [android] Update attribution wordmark (#8774) * Update wordmark on android * Moved attribution i icon to the right of mapbox word (in mapview preview image) * update padding and margin * [android] update hardcoded branch name * revert version to 5.1.0-SNAPSHOT --- Makefile | 2 +- circle.yml | 2 +- platform/android/CHANGELOG.md | 28 ++++- .../maps/CameraChangeDispatcher.java | 67 ++++++++++++ .../mapboxsdk/maps/MapGestureDetector.java | 39 ++++--- .../com/mapbox/mapboxsdk/maps/MapView.java | 11 +- .../com/mapbox/mapboxsdk/maps/MapboxMap.java | 100 +++++++++++++++++- .../mapboxsdk/maps/MapboxMapOptions.java | 30 +++--- .../com/mapbox/mapboxsdk/maps/Transform.java | 57 ++++++++-- .../com/mapbox/mapboxsdk/maps/UiSettings.java | 23 ++-- .../style/sources/GeoJsonSource.java | 10 ++ .../mapboxsdk/style/sources/RasterSource.java | 13 +++ .../mapboxsdk/style/sources/VectorSource.java | 10 ++ .../res/drawable-hdpi/mapbox_logo_icon.png | Bin 3408 -> 4778 bytes .../res/drawable-mdpi/mapbox_logo_icon.png | Bin 1958 -> 2622 bytes .../res/drawable-xhdpi/mapbox_logo_icon.png | Bin 4492 -> 6579 bytes .../res/drawable-xxhdpi/mapbox_logo_icon.png | Bin 7059 -> 10802 bytes .../res/drawable-xxxhdpi/mapbox_logo_icon.png | Bin 9402 -> 14441 bytes .../res/drawable/mapbox_info_icon_default.xml | 8 +- .../drawable/mapbox_info_icon_selected.xml | 10 +- .../res/layout/mapbox_mapview_internal.xml | 3 +- .../res/layout/mapbox_mapview_preview.xml | 35 +++--- .../src/main/res/values/dimens.xml | 9 +- .../testapp/style/RuntimeStyleTests.java | 33 ++++++ .../camera/CameraPositionActivity.java | 31 +++++- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../src/style/sources/geojson_source.cpp | 6 ++ .../src/style/sources/geojson_source.hpp | 2 + .../src/style/sources/raster_source.cpp | 8 +- .../src/style/sources/raster_source.hpp | 2 + .../src/style/sources/vector_source.cpp | 8 +- .../src/style/sources/vector_source.hpp | 2 + platform/default/default_file_source.cpp | 16 +-- 33 files changed, 468 insertions(+), 99 deletions(-) create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CameraChangeDispatcher.java diff --git a/Makefile b/Makefile index e8f7f175ff5..abccb306a6a 100644 --- a/Makefile +++ b/Makefile @@ -571,7 +571,7 @@ run-android-ui-test-$1: platform/android/configuration.gradle run-android-ui-test-$1-%: platform/android/configuration.gradle adb uninstall com.mapbox.mapboxsdk.testapp > /dev/null - cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=$2 :MapboxGLAndroidSDKTestApp:connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.class="$*" + cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=$2 :MapboxGLAndroidSDKTestApp:connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.class="$$*" endef diff --git a/circle.yml b/circle.yml index 7d75427bcb5..4ce00872b4f 100644 --- a/circle.yml +++ b/circle.yml @@ -133,4 +133,4 @@ jobs: - deploy: name: Publish to Maven command: | - if [ "${CIRCLE_BRANCH}" == "master" ]; then make run-android-upload-archives ; fi + if [ "${CIRCLE_BRANCH}" == "release-android-v5.1.0-beta.2" ]; then make run-android-upload-archives ; fi diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index fdc6343b7c6..bc307e50614 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -3,11 +3,35 @@ 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 - +* Update attribution with new Mapbox wordmark [#8774](https://github.com/mapbox/mapbox-gl-native/pull/8774) + +## 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/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 00000000000..bd028aecb6d --- /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/MapGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java index dca833bbf48..39433d8b933 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.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. *

@@ -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 @@ boolean onTouchEvent(@NonNull MotionEvent event) { MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapDragEndEvent( getLocationFromGesture(event.getX(), event.getY()), transform)); scrollInProgress = false; + cameraChangeDispatcher.onCameraIdle(); } twoTap = false; @@ -273,6 +279,9 @@ public boolean onDoubleTapEvent(MotionEvent e) { 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 @@ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float ve // and ignore when a scale gesture has occurred return false; } + cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); float screenDensity = uiSettings.getPixelRatio(); @@ -362,9 +372,7 @@ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float ve 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(); @@ -377,6 +385,10 @@ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float ve public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { 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)); @@ -391,8 +403,6 @@ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float d // reset tracking if needed trackingSettings.resetTrackingModesIfRequired(true, false); - // Cancel any animation - transform.cancelTransitions(); // Scroll the map transform.moveBy(-distanceX, -distanceY, 0 /*no duration*/); @@ -446,6 +456,8 @@ public boolean onScale(ScaleGestureDetector detector) { // 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 +477,6 @@ public boolean onScale(ScaleGestureDetector detector) { return false; } - // Cancel any animation - transform.cancelTransitions(); - // Gesture is a quickzoom if there aren't two fingers quickZoom = !twoTap; @@ -512,6 +521,9 @@ public boolean onRotateBegin(RotateGestureDetector detector) { 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 +534,7 @@ public boolean onRotateBegin(RotateGestureDetector detector) { // 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 +566,8 @@ public boolean onRotate(RotateGestureDetector detector) { 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); // Get rotate value @@ -593,6 +601,8 @@ public boolean onShoveBegin(ShoveGestureDetector detector) { 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 +643,6 @@ public boolean onShove(ShoveGestureDetector detector) { 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 872802292c4..d00da4c155d 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 @@ -129,6 +129,9 @@ private void initialise(@NonNull final Context context, @NonNull final MapboxMap // 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)); @@ -136,12 +139,14 @@ private void initialise(@NonNull final Context context, @NonNull final MapboxMap MyLocationViewSettings myLocationViewSettings = new MyLocationViewSettings(myLocationView, proj, focalPoint); MarkerViewManager markerViewManager = new MarkerViewManager((ViewGroup) findViewById(R.id.markerViewContainer)); AnnotationManager annotations = new AnnotationManager(nativeMapView, this, markerViewManager); - Transform transform = new Transform(nativeMapView, annotations.getMarkerViewManager(), trackingSettings); + Transform transform = new Transform(nativeMapView, annotations.getMarkerViewManager(), trackingSettings, + cameraChangeDispatcher); mapboxMap = new MapboxMap(nativeMapView, transform, uiSettings, trackingSettings, myLocationViewSettings, proj, - registerTouchListener, annotations); + registerTouchListener, annotations, cameraChangeDispatcher); // user input - mapGestureDetector = new MapGestureDetector(context, transform, proj, uiSettings, trackingSettings, annotations); + mapGestureDetector = new MapGestureDetector(context, transform, proj, uiSettings, trackingSettings, annotations, + 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 1751dcf0428..96603355f19 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 @@ -69,6 +69,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 +77,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 +86,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) { @@ -1595,10 +1597,51 @@ public int[] getPadding() { * 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. * @@ -1913,7 +1956,12 @@ public interface OnScrollListener { /** * Interface definition for a callback to be invoked when the camera changes position. + * + * @deprecated Replaced by MapboxMap.OnCameraMoveStartedListener, MapboxMap.OnCameraMoveListener and + * 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, @@ -1925,6 +1973,56 @@ public interface OnCameraChangeListener { void onCameraChange(CameraPosition position); } + /** + * 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. * 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 8fcfdd6db0b..68603ab1a38 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 @@ -35,10 +35,8 @@ */ public class MapboxMapOptions implements Parcelable { - private static final float DIMENSION_SEVEN_DP = 7f; - private static final float DIMENSION_TEN_DP = 10f; - private static final float DIMENSION_SIXTEEN_DP = 16f; - private static final float DIMENSION_SEVENTY_SIX_DP = 76f; + private static final float FOUR_DP = 4f; + private static final float EIGHTY_NINE_DP = 92f; private CameraPosition cameraPosition; @@ -206,13 +204,13 @@ public static MapboxMapOptions createFromAttributes(@NonNull Context context, @N Gravity.TOP | Gravity.END)); mapboxMapOptions.compassMargins(new int[] { (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiCompassMarginLeft, - DIMENSION_TEN_DP * pxlRatio)), + FOUR_DP * pxlRatio)), ((int) typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiCompassMarginTop, - DIMENSION_TEN_DP * pxlRatio)), + FOUR_DP * pxlRatio)), ((int) typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiCompassMarginRight, - DIMENSION_TEN_DP * pxlRatio)), + FOUR_DP * pxlRatio)), ((int) typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiCompassMarginBottom, - DIMENSION_TEN_DP * pxlRatio))}); + FOUR_DP * pxlRatio))}); mapboxMapOptions.compassFadesWhenFacingNorth(typedArray.getBoolean( R.styleable.mapbox_MapView_mapbox_uiCompassFadeFacingNorth, true)); Drawable compassDrawable = typedArray.getDrawable( @@ -227,13 +225,13 @@ public static MapboxMapOptions createFromAttributes(@NonNull Context context, @N Gravity.BOTTOM | Gravity.START)); mapboxMapOptions.logoMargins(new int[] { (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiLogoMarginLeft, - DIMENSION_SIXTEEN_DP * pxlRatio)), + FOUR_DP * pxlRatio)), (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiLogoMarginTop, - DIMENSION_SIXTEEN_DP * pxlRatio)), + FOUR_DP * pxlRatio)), (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiLogoMarginRight, - DIMENSION_SIXTEEN_DP * pxlRatio)), + FOUR_DP * pxlRatio)), (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiLogoMarginBottom, - DIMENSION_SIXTEEN_DP * pxlRatio))}); + FOUR_DP * pxlRatio))}); mapboxMapOptions.attributionTintColor(typedArray.getColor( R.styleable.mapbox_MapView_mapbox_uiAttributionTintColor, -1)); @@ -243,13 +241,13 @@ public static MapboxMapOptions createFromAttributes(@NonNull Context context, @N R.styleable.mapbox_MapView_mapbox_uiAttributionGravity, Gravity.BOTTOM)); mapboxMapOptions.attributionMargins(new int[] { (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiAttributionMarginLeft, - DIMENSION_SEVENTY_SIX_DP) * pxlRatio), + EIGHTY_NINE_DP * pxlRatio)), (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiAttributionMarginTop, - DIMENSION_SEVEN_DP * pxlRatio)), + FOUR_DP * pxlRatio)), (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiAttributionMarginRight, - DIMENSION_SEVEN_DP * pxlRatio)), + FOUR_DP * pxlRatio)), (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiAttributionMarginBottom, - DIMENSION_SEVEN_DP * pxlRatio))}); + FOUR_DP * pxlRatio))}); mapboxMapOptions.locationEnabled(typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_myLocation, false)); mapboxMapOptions.myLocationForegroundTintColor( 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 e101340111b..af44a08a814 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 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 @@ public void onMapChanged(@MapView.MapChange int change) { cameraCancelableCallback.onFinish(); cameraCancelableCallback = null; } + cameraChangeDispatcher.onCameraIdle(); mapView.removeOnMapChangedListener(this); } } @@ -89,10 +96,12 @@ final void moveCamera(MapboxMap mapboxMap, CameraUpdate update, MapboxMap.Cancel if (!cameraPosition.equals(this.cameraPosition)) { trackingSettings.resetTrackingModesIfRequired(cameraPosition); 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 void easeCamera(MapboxMap mapboxMap, CameraUpdate update, int durationMs, if (!cameraPosition.equals(this.cameraPosition)) { trackingSettings.resetTrackingModesIfRequired(cameraPosition); cancelTransitions(); + cameraChangeDispatcher.onCameraMoveStarted(OnCameraMoveStartedListener.REASON_API_ANIMATION); + if (callback != null) { cameraCancelableCallback = callback; mapView.addOnMapChangedListener(this); @@ -119,8 +130,9 @@ final void animateCamera(MapboxMap mapboxMap, CameraUpdate update, int durationM CameraPosition cameraPosition = update.getCameraPosition(mapboxMap); if (!cameraPosition.equals(this.cameraPosition)) { trackingSettings.resetTrackingModesIfRequired(cameraPosition); - cancelTransitions(); + cameraChangeDispatcher.onCameraMoveStarted(OnCameraMoveStartedListener.REASON_API_ANIMATION); + if (callback != null) { cameraCancelableCallback = callback; mapView.addOnMapChangedListener(this); @@ -135,7 +147,12 @@ final void animateCamera(MapboxMap mapboxMap, CameraUpdate update, int durationM @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); } @@ -144,10 +161,17 @@ CameraPosition invalidateCameraPosition() { } 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(); } @@ -157,6 +181,10 @@ void resetNorth() { mapView.resetNorth(); } + // + // Camera change listener API + // + void setOnCameraChangeListener(@Nullable MapboxMap.OnCameraChangeListener listener) { this.onCameraChangeListener = listener; } @@ -172,9 +200,6 @@ void setOnCameraChangeListener(@Nullable MapboxMap.OnCameraChangeListener listen } 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)); @@ -187,6 +212,15 @@ void setZoom(double zoom, @NonNull PointF focalPoint) { } 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); } @@ -278,6 +312,17 @@ void zoomBy(double z, float x, float y) { } 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 354a42536a3..1bcf8a70b96 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 @@ -146,7 +146,7 @@ private void initialiseCompass(MapboxMapOptions options, Resources resources) { if (compassMargins != null) { setCompassMargins(compassMargins[0], compassMargins[1], compassMargins[2], compassMargins[3]); } else { - int tenDp = (int) resources.getDimension(R.dimen.mapbox_ten_dp); + int tenDp = (int) resources.getDimension(R.dimen.mapbox_four_dp); setCompassMargins(tenDp, tenDp, tenDp, tenDp); } setCompassFadeFacingNorth(options.getCompassFadeFacingNorth()); @@ -171,9 +171,7 @@ private void saveCompass(Bundle outState) { private byte[] convert(Bitmap resource) { ByteArrayOutputStream stream = new ByteArrayOutputStream(); resource.compress(Bitmap.CompressFormat.PNG, 100, stream); - byte[] byteArray = stream.toByteArray(); - - return byteArray; + return stream.toByteArray(); } private void restoreCompass(Bundle savedInstanceState) { @@ -189,10 +187,7 @@ private void restoreCompass(Bundle savedInstanceState) { private Drawable decode(byte[] bitmap) { Bitmap compass = BitmapFactory.decodeByteArray(bitmap, 0, bitmap.length); - - Drawable compassImage = new BitmapDrawable(compassView.getResources(), compass); - - return compassImage; + return new BitmapDrawable(compassView.getResources(), compass); } private void initialiseLogo(MapboxMapOptions options, Resources resources) { @@ -202,8 +197,8 @@ private void initialiseLogo(MapboxMapOptions options, Resources resources) { if (logoMargins != null) { setLogoMargins(logoMargins[0], logoMargins[1], logoMargins[2], logoMargins[3]); } else { - int sixteenDp = (int) resources.getDimension(R.dimen.mapbox_sixteen_dp); - setLogoMargins(sixteenDp, sixteenDp, sixteenDp, sixteenDp); + int twoDp = (int) resources.getDimension(R.dimen.mapbox_two_dp); + setLogoMargins(twoDp, twoDp, twoDp, twoDp); } } @@ -226,16 +221,12 @@ private void restoreLogo(Bundle savedInstanceState) { } private void initialiseAttribution(Context context, MapboxMapOptions options) { - Resources resources = context.getResources(); setAttributionEnabled(options.getAttributionEnabled()); setAttributionGravity(options.getAttributionGravity()); int[] attributionMargins = options.getAttributionMargins(); if (attributionMargins != null) { - setAttributionMargins(attributionMargins[0], attributionMargins[1], attributionMargins[2], attributionMargins[3]); - } else { - int sevenDp = (int) resources.getDimension(R.dimen.mapbox_seven_dp); - int seventySixDp = (int) resources.getDimension(R.dimen.mapbox_seventy_six_dp); - setAttributionMargins(seventySixDp, sevenDp, sevenDp, sevenDp); + setAttributionMargins(attributionMargins[0], attributionMargins[1], + attributionMargins[2], attributionMargins[3]); } int attributionTintColor = options.getAttributionTintColor(); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java index ec801867617..10ecb945ad4 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java @@ -227,6 +227,14 @@ public void setUrl(String url) { nativeSetUrl(url); } + /** + * @return The url or null + */ + @Nullable + public String getUrl() { + return nativeGetUrl(); + } + /** * Queries the source for features. * @@ -243,6 +251,8 @@ public List querySourceFeatures(@Nullable Filter.Statement filter) { protected native void nativeSetUrl(String url); + protected native String nativeGetUrl(); + private native void nativeSetGeoJsonString(String geoJson); private native void nativeSetFeatureCollection(FeatureCollection geoJson); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/RasterSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/RasterSource.java index 98b74afcffb..38ed2086184 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/RasterSource.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/RasterSource.java @@ -1,5 +1,7 @@ package com.mapbox.mapboxsdk.style.sources; +import android.support.annotation.Nullable; + import java.net.URL; /** @@ -72,8 +74,19 @@ public RasterSource(String id, TileSet tileSet, int tileSize) { initialize(id, tileSet.toValueObject(), tileSize); } + /** + * @return The url or null + */ + @Nullable + public String getUrl() { + return nativeGetUrl(); + } + protected native void initialize(String layerId, Object payload, int tileSize); @Override protected native void finalize() throws Throwable; + + protected native String nativeGetUrl(); + } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/VectorSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/VectorSource.java index 7230492d0e9..9b59cf8967d 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/VectorSource.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/VectorSource.java @@ -76,11 +76,21 @@ public List querySourceFeatures(@Size(min = 1) String[] sourceLayerIds, return features != null ? Arrays.asList(features) : new ArrayList(); } + /** + * @return The url or null + */ + @Nullable + public String getUrl() { + return nativeGetUrl(); + } + protected native void initialize(String layerId, Object payload); @Override protected native void finalize() throws Throwable; + protected native String nativeGetUrl(); + private native Feature[] querySourceFeatures(String[] sourceLayerId, Object[] filter); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/mapbox_logo_icon.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/mapbox_logo_icon.png index c0f4ed2c4ccea49c85cea67dfdbbe73093e6aed2..7568387a8da1c702981dd72c9a154e278a7436ca 100644 GIT binary patch literal 4778 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 Ef@lR+_y7O^ literal 3408 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 literal 1958 zcmV;X2U+-uP)fd(ofB(zNMk49Xeq6EmKHg(DPkz!zyVl8E4t@TnU?Y+CV z=hA!Id#<-Tyu7?;Gcq!=9IB}k zl>^KI<^qL0INAVD0L6|`q;x(r&K5q49U@t2-eq8i?QAvjm28TdR|rgZ_?R<>JgxB$ z(cj;HqoAPR*|TTQt}ZPtJ-unurY9FJTv(y&dS7N{rgQU|*hkrYA_VXQ_kk~rv5WwH zW4xQCh6&sOdX#=1Fptp$Ca_Pe^W%mAA9ff|aLD_B&UA~82#13Q4{jSC9*!D@F<4$+ zzS{5iUl9>Ge*F0Bx~}&*!eLoqVd2->+S=+9iG(I15|76}xOVN@?{afCX;c)FoxEyUHdIy(HO*!Bw^dO zZI$QGpFbobGB7aEAtDk81g-&ICkffv*=2se|6LIg!!V+nrnMV}5fc%)bLY-GY9n6L zG!uql3m zibkWiBaz6zMZ_{qbNJ}dqd&^d&Mwn5txZIvsj2DL9*<|arfD}tL>d|zeyQwC``9FS zJf7uh5wR@G*t&J=cikkMIC0`?<=-shn$M@p#cu+*lojZ4~^LRYVW3gDDWm#r@ef_SKUR_9Gh%O9J3Mbm-7F5wULGym>0M zsFQ@QuCBj`h^R{PW$c)J{@Af&KNS&aZ*Tu&8WQFLFC0F6_=h4Q9UUESr6FM^u&kk> zp;|Poon6?l(6#cLC()=H?d_6%_#V_V)VJUV4CDz_@$&t`DH7sAxLC#*G`_ zF$`l6psTCv!o`ahBiISi`lKX;foMrd$s5nhK7cc zC&Y|0Fg*U^Z9%sfLXI<6=P@n)Jd2)d-m*NfbQ<@ zet@Q?rteRjIPoz+=FgwMs;a8$Y3$NGqN;ox5(KybypP=(#@z~6uU@?Zux{PDSC=eV zG84NTP5%hHb1Nz;o?5eJ%@%+Q7cMlX(OIy5{rcJE<>l)EIeq$c^WMFC-`91$Z_b=K zU#+jNUxppupR22@d%n21_@#I}9;mLazExXWyQrk3gSo6nORj;Rj(Ho z7EZ=4=jpBtK>D1pz@;+-S5C;v$}02w{a00H9}I;;t-)ZhB@_y^sS0rcS9Q=dtv#7c zrm90KzDHCCRY8VDMB3Wg{=n$!Akk>FI~t9Ss1A!3E&7J8>jBHMl6&^-c@^+>c6Odq z`RPwQ$R&@C1XQ2U+S+8_1t$qkc`O#|xqSKZZ^r5qM8u3nqrS$*#$Ty*=3#e+ zrMq_R+7Ji?-Zf1#VVdS}Fc@q}IqdWKnp7e^rsh32Z{EC@W3kvh5ix6PYqtY0rW|q~ zi40&dBQHo@z&*!v4t51dg^~cR*m3#9SnL9rrulXx66wmz%iH1*3wB~lFm~y3cZ|~% zA=xfH1G@`$`qiW6J_k&8$OnLb!sxbHu;c!5htUC-P8?{#?$N=%O6%C!z8kwdPI7JJ zF-A8hA1K>lYyvWrvrfk>wVnnf9rF9Y-<8cNYHm!~n!VR~BAJD$h8L!^VK`C*rQNDViV*#C`;!nhZxM~FDUgb5Q~QqMw~ sD)VV!%rfymz;x`eNF7CYn0;#aFXeA|gUDnK2><{907*qoM6N<$f(Q%0c>n+a diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/mapbox_logo_icon.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/mapbox_logo_icon.png index 194aa64da214388eec2e2449cf35d57a4d0fbed9..7eec45f4b199ffffb46c4274a7c5973127d084ba 100644 GIT binary patch literal 6579 zcmV;k8BFGhP)k{d7jDkIlHs7v$L~%lOy!U zAc5xPz#?FN2Z@IEI+#j)1Go*$hHMUGnFBep+#(c`a8?B+partneh>8}u&cmqq0J)m z{>|01BB%vnR9&!AB*R*^Y#D#@$tM@6T)A?5g$fl4t9;$Mbvu)jlXov&x^&NX-+i|` zvJL4q-~w=0Kv{NHHV48ukQqVEMT>f14KSx#=g*%%$DMcHc~XZC9qQDtU%zpI0tHGc zKN{@WvuE?XdGlrt88T$?z<~od@7=ris8{z0;R;X!d)Uj%4zf9rjsqD9s_f3DU~${> z<(FTsbnm_QUR}6w;j%U_E8(tPyMBG=op*-z=+Q$O)xs7cQ^9>UBRkA82ddLT$|T-t zshw;knCRsFEu*mnRko;v)Yzdn-E>pJE3drLu2iW~wNJ!IH*em&yldC4Uw`t+CtE#w zvR|iw+fM}je;JJ`jQ6YK<1flcD;@rJC?ik)?070%{FY<@a9ubJ++o}R=qQp#Q#i~GomNbpiMwCu!$2V z{%Jz$ez$4Urfu;INI(VpwoDGA&6SZ2*&K)zRJor@h%Ilu^;QkET$VNIfddD29-5+ZyjinmZTt4^tHXdmeK^g;vj1dr01miYzfOxa#g!XwxFO;G z`|rO)@yx=iRjcN{{`%|RzW@IFoA&M7cf@Vis8OT*k3RZn&9*>XT%2yP0s%ANju|s% z^#1I#&vd}51Q)O8hJpu?ri@K6W<1kw#rI_YHLB?-vH{2AiCuMBKwZ*C!@m0Jt5;?A;DlXy00DikTeogWA{Yd- zXV2b?&vkyea^)6(^UXJ{PdVk37D4s&O}>34g8Cli>Wj4MjG(&YX;C!RS?$df<9+ z4wzYil>pe?il`AXEi!?4B7Q6YqtVmCsSk=bL7?)=U_(%2n-6Mitw1AFgf3{|O$H_0 z;?+4|&^$`d>J>x3Q?iGzqxS0Jmv*ue$20;w4L#%=hDuKW?2gX;PYZA)!gR5S#)^LkjdgP3$R3 z2%w+y>Z`9VTCG~Of;)HaJowX3Kkb+@WlBaGO5=(Nx>RY?rcIF=HEI;ty?ghepMU;2 zZQ8VHmTq|(mjbnjCA1kre0+S)Yp%H_p=#Bt@m#*v-;w-)hdkyG1Yt*dw3FdagvZL+tC#(R_r))=FCqJ%JnY&)YR13FTM2AW44{> zb6D(m=+L2Lw;pE8vNzs%;|W`juq`OY3eEz}cM;5+5$pkcH_>gc_|~mkr#$=Yvt3n} zaehu;mJ8{F1i`dd*r`*eE_^S^;zJKT^f3LunZ?p~|Ni~U0_aY2#{i9M!Ls0GqQ`FZ zgxlunr=RYjejs+ z&6LMstt;Osqd2!-Dd0|a1Yct+--VGQM}FR}UAwuwLFjRk<IKUl^r~IFfDK1yiePD2wi1v=pMA` z@#Du2MO!s3Sg@e9c~n@oY}vGP&N*ikV?RtKp|n>X1hv{Dk34dpZ8%}Vgs-62f-7#j z&D)}|@RwhHnRNEqXOBmik5X^|90#fojlmGtSJ@@p&f3CSTfT>rPb5Tc|-n41c66E83gjQL-H8jCA2oyw_0SL{AKmuf9)7JLN)VG)dVQFZBt&p

L7~>ITNnP|gAcj{2`S$Uqr(JW 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 literal 7059 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 diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/mapbox_logo_icon.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/mapbox_logo_icon.png index 5f9647610a1458e227fe3b4f67bdbd6ff125cda4..e5780ccc071d00fc6e14ee6c94235106271b15ab 100644 GIT binary patch literal 14441 zcmXYYb5v&E`*yZ%+cqcHlU|&RY9E=U#j7 zb!*?(6|JHqgN#6c00stzEGH|e1_lPM@%63&2lF*k?7XpufW zll$^m?Pw|kS7Lgr)j^*H@9tY=7J|G_Jo$h6fDrt}VybM*^37LlDi}6&Ak|{&3eH)+ z;y>szM5^T;=c^us+_ueuGg;i{1G0=6%=)da6>6oA>QY@^*E@}7V+rwWtMOG@HF}-K z10hnJRvJ526MKi)1F_pY$F+4tJ z<1|E;_JS3YNaEGrfcF<-l*m|j?` zAb5H{gJ3@4|E(baYaCl*rLo-6*rN}YzSp43^T@I9{c&}S#MfTAWHDX*N0Xw2Uhczb zd459oah|UwAXh+hsyal>b7jF8kA_G?IS1!!|NB{i5FN%K==nO#6UH>pt6x9*0^iS# z3nOPf+8i+iiS{_ZeFb#qJlaTD2KHX65qp<=jrwzl^xNXSJE5G*VXUNe#2 zTE4=jm*@adGn7s$wwbnSJf;FcmxL^l78A&~K6o7{OEJ>A@tP!oavCe|#60^nWms|x zgz{9(6M9~1niKJb!l)7`CY`1-fxycKd!xe|!=;99rYM8NTLrHQcp(O1GqGcFLUUMv zQlz^=kpY*i>SchPC1ug=$7#+vq2liyu6n~prpB+2mxMywl^W?Q_|(He0D)uB5MQ z3Lm4>U^)`_rQ^MCC<6KZ{;sZ|!ziftdmGeyZaclND0H)cCD@6bfWkd4_cJQ=`-@xF zzKbgh9hxRGF6SkrZ9^~Y$6ZDrm;c`dnYvoNmP%FE2Q0eLDETyo70U{(wX>C~xsStC zZskB0KuKQr$6sFnl{hc_pG8E zrT=peulx>H=;Pmk)2`qB%%+wDY`vbhg zX7ee&O7Af3``;zyVpP$$8?V(GeJfXK=0nst2t^y}kG>J)UyO7Gw=3g2i-UuMP1!sS zK2gH6ou41C*D{_b^S=!kbGljfUP>8RP_D!gqEsV-hsF<0%8&`T=wip-rh9TTW5B-j zojneriso$Bn@Gs*J6g+zHJEjAj{g47xC1({;D%JPzvZPnV#sm4pH&ODQM>kghv%0)op(5zSj(4(l-F-jXrY!uh zEF^Rz?t6pD%(oW=$?)BC!$OhIyVNWehq?2xNcZBi@1!_U) zKpE=6@hmv&qM1eK+L@4!pdxpw|G`*C<+-x}rB!uXfI&5K;-nAW!+DxvzPTjsq*~mr z@WF^nRTHMg%fMk1!8W;bv*295p6cRzYC45aMNLu0D&u)4=CZGfK64iK7_IlRNUqd( zSdA`8<&g^iqKcPjYkCd+C#WdtGb7DZQ2k4))(qSV%L2ebZI`*SS9(PXF`%+)d6VW* zX3BB$xfs`F*-+5eulD+QI+OEgR`8nWWj~gcWv=;Z_w5g_CaL#cuo)r(LZfB1?64>q zxM+)8`Aab*0(`v3uq2|jm^`A&v39vvAEE~XvWf?n+{Cl7x=JWNaTGjuNJ^1nsPx<> z>GHj%QPOGT;V{|vVGk6#h+O6<6CS9CK!TvtJmqvK37u(8%#Cj<9?jq_EPBoEONpT2 z;Q~d%rvB0x*%0d;1T4%tu+FKN$D+c?mU-TD zx%&4#TvwwHANv&_SRr7fv=_rz3wjUydn~X z+Y`2`aOCIV9_e14J&+9R^uexmVy6E7&Zd!Hxg=fXZ;EDX)Ed_UPyqV_ty{5_=~q&{Cn;ubLN zNpbg)W=eJNY&{?nZX#`g!QXjA>;I;{`QC8a)Hx^%qK)-XKxaQsart*Hw$EI!~nKd?6P#a=D zRy>WB9}Mr)VP#N}7B2?-6>SiOV`4J*onMj<-aibmrZoahce;NF>NbB)t~Z$Ndc0M@ zfv2lz`d@V%0|52#E0US!C7ZDo7dG>5f}tIzWww1K-G;`U2{j^R6V<#rInzJKa2YvQ z?y37_1+HNy^K=%)uH`ZONL7$Hn5>DAzz_#kyl)r=_{0S2A!w?MG!zxK6uYqbWh)9hp2{eW)nY@fxn6?T8` zOPK_GPMW%}Ioh%*C;4ONi{3~QpA$Nf1`7Hy6aun?3v}?Otm?*;q<@RznS_q*h47a) z!wiv|M3U<9ny6F9bNPKl%%;!pPrIV|H?u)Jd?`RCc_XiF59C4x|5kn;kbawEPtDh` z9}8=@zQdO>H!+Pb;@7UhiYa?`ff#@=*?I`Wes@)Hq1HR!o7-t%`+fv~Z=mgaKiIQOv5s4sx> z@sax<{;IRk;ow&`HJuWK#7%Yan?T(tXi5>kF}gTkt6yjhi}V|_*3hBE+*vkw?@((* zL+jm9hEZN*(@M?bCaG?A(9Ss=o2=b36nVw+5O0(l*nwZ)l9FJJ>iwS?J8{-dhZTme zh_^W{XgE|JyXuxV>Pj>}Wm8t1!?=h3`2Ex*^kD)dC$?q}YrcA_!v?}N2TnrynlJyUIz?mZvpiyr<|SHkblKvHw*{H*}lmG|RMrgm-j-Q)(s z0Ul#waY8uGcEX>)fAT&Y%JZ|zP8~<-@O~BjGjE!j5P0SmA0JPBZ3j2;X8bM+aSOQf z6lFVJyZ#a+0snqmh-BiRvv#hp1hT8k39N{tvVr79@5U)%|6Xpj*AU^}SzmiTpXi$4jMAB$$gN?E_eNZaZ`6Z8gmboMSz?3No)8 z82@c!?R#^r=z~vEf6V`Uv${`J7U^Kb)Rmd!GXkcu{}_qmj)e>zz56IDpuTjb))&t- z<-(8dkb7}ui=$%*Jeo|Cnrj^+_WIt&CS%26!u3jQp-F2lurC3Fa zBU=8vc}Jh%0D>EjG{3bc-*o{lfg>^F&UIx|%=5_~Z@dr5bSsv>Q&x%BD^=Rq&0CE4 zn9|Wc8;urCzyY%Yvmn8%_F{O(Or=5*xki$p<2cE2_a}P(iU_)<+`?9} z3l*7cZaXa$7iF;%0S(vNd*Q?0u&huLS%w?Ae>I@CDATKMj0JN6w4@}5Q1iYBy z`-loyeM#+?j9*)7G9bNYwL$cNR?1g?v5?e<~ltR#NBKSrT z=c)xRXt!5ch8`=L<%3XEH^Fvp=_+s$GAR*ewOj2 z|3MxkS|0hSw8^*gU?<=8xM_F(Z7lT}qmIZnISAhIa&I8iNTMj0MR5WwI|@@>7|H$b zL_LzG-B95%*-JC48>bL2x>Pn#_w854lIwfbu`90p8fJ~XQ1S+Ey{0zII}9Mcq|O0* zvENuT3hY6BzW};pCTfoQOKs*SNIdrhe!lK1Y}-7t{TYmrZEJH$>Z$u9^D<_=@q!vG zW&~ohn8bRH@$-^sl=$n=%+KF?Q6Jp@8dE>lICH`8{y8T4(V(fyw&e6x6vYBED>kVu z^}cGh;$#-T&-S~_APe2CXO%r;b-hl$1&FL>p2Pe3*(BOxy5-wZzr^!Rx@is6q8e?N}NxYFT`>6r)j_S!Ns9?@=-XZ zwtALsqq1p@2V;4g*Lexl?QyGECG+a7{F+0oegz)Nh2ESPwqIoAPm$Wz7Gh)tX+*@s zfXsr7Jn#KzGo-ZY`hBK|%8WC7Fi03AzQsvk)%j>Vp?1Feq8zD}9;qjA(zNU+tCY-j zx}0z~$YT1#$Vv?-t+J{x$39FNdmxF|9jmKPk1X>eFPJ%+mGWe%e2(lz-xpiDh4pn& zfz==>KcCck3CRa7u5{&Op?EHcTwcB*^d;rOMp1kYYUwMRy_0P}9@3!NFh^#4xMB-N zY*A-9pG3mGXxj}qO4d^9|K-t0&hLtaR3uCTtOCvhX6Z7eD0(nYj9fOGZOU))A3u@_ z&FH{{QCB1p@6lPELpRI47?U(M8C;IvHr_{Yjoih-+F@vF-uQzU2&0kD=duTs)M%`_3|WJoAKMVR(^J%XA&?)OCj*%L{@!3!vmU8hGr*4A_+ z^a!JCc$%YBE0UG4f~QUmOIo3sBopS#Y-%PFmO!1cwjG~yX9+cM7nG*tyPLd84jtBk z$91LO-@!ji{l^lB6DKwOAAQ_!P%G`6e80CoX;y03-0b*X8^pfY$lc*>rtJjj(EQZO+1)|5vD$yz+0s2A2CIoWfHUY{xglNnPW(r(KLLw ztL^v`2{`Z9(n25n+mgl_O1@LLxVZ@7WCC|?IhQBQ!j`H`5&Nc3kRrszbvPl19d8tPk)$@c|CYof9pWPVDZs)-RoOBP1>93`cv%Hb4g^wS^}~x6hjnr7q!JhmgAEIS!(WLUdNf*zmCXBm+;TFSZ+AIBC_CP(|H=GMQ#}hilFF9> zGkL5zq^X5@(9Di8|21Yi0K4*o%}eMvae>6rg9Rw6?-B{y96Ma*)d0rvgj)D2vO!;A zG107_;^A+os<&ZdS2cZH#=X9}UngI8h6D1Cn4g7<&z_C$CyL!!ZJc!1{Q-}(R6q6E zjD*15ko6!^tr>_LS>jgMWp8$r3sbivBj0dJQeVg2Fb#KKqdzBZK8aM9HGhAdZ_Bcn zOqTG?S4=zx>nvwmo0B~UTk*b{ntqf=22F6Uf zn{Y90~>5` z#7)je^-0Fsr-@_ZFm(MDMFJa?=J<46a5N?NNun~;WYNnA`Xd1R#hfUWj%0w-R)Pq$kbiY;z}n;)IZI=& z{ucY{GUgNrmVwM}Ha{nSYP(cF{tHMLoGKevqeyfC7$=>QV1Y1 z*-w+VhPvfQZ#j~bw{Uq&?EZvZVpr!5QzRAiZadHczhk92V;&tvhnHZO5wJ*w zEcQp^l#0@eJiWQLXZ&O#=F}`@oBWmoLahfkaXcfBAe@e%EmXu;q_Kv#V;Apsq)U&& z*e3P68A!F@9T_ly^lfG33>P_{7ZPDxrY>|QIx#Mrq>VUBCb{`rYG7J0s2a4CPa`ui z7dts*sn);OyPxAW+W`aPbjPHpnI2y8LBM^+q7f+q;w_B-n65{xmM{9Q`l`rvH_suT zTh`s|MA77F46Du$GwGR5*;9hg2Ul>60OPmG{iE{W>#mJ-6vEjEUj7HLN6~Y6I0m8w zI`U8Po2U0tns%O<$p?||_fB8nmM6@>Ia{sl(c;Q^1`4Ile23fdfCxjr5j%V227qqv z)shUnTpO24YH65AV>+2;0!!{rH-{#fsE1GIPpGoMSI+Wp*WZJwj8Onx@|`6<%s+)* z(Iv&1om6-<>vuB^j4yHZ{jVtZPptQ4ZA`IA$NojR)D31;?5Tblwu$M+HM_oyCIb`( zjb||#EX)%`U=|>?$a@3EH+QuMuX4;Sg5iR3P#?PZdvXp=_;wX5H-twsw*GUiiDx^; zt6>w|97C+`Z@%S~Eekjo%xSR9IlNNIj6kb6!dk^}nMuZx6oNTv|* z&y$3WM-*4I*UIYqPFL%Ke9IfeCiy_eF+=rN?*xZm?6 zzN)*AE{*!+U1t%SoLkqWJZ82G^<%nEZ*4H*Q^iSGMlvC3dKa+0*9k2x+16W40npfq z+pBBc;T2ZAmjSwf?K%_y8t$alMMlEtD|R+DAhneSZkOHir^;;!_l>U#&8=Ut+`fz6 zp5P(!SIXxi%(+||br`>db2FzxAgFKj7__VCKStR@umXu4>DhTXcj(#b-jlms3!xic?~99KJTm~&`Z;#hy0{@v+fyH zmdQ35P1VbAfrlb^Wvz^>BDHJiPt8e^)~g1765H*rI-o_F@vQE70&VpGjZ^POvq0T) z4Rryqu6!Gq+)Ld%4)$6LEFB=6EAryHf#K~(}14uQnj+r;Oz1gOzq%E}{{B9(Dy z{rak6`A0k33_>@LGubk^T4CfCM7D~&npBHpFEbPLasHuZFmIqg_w~!N6otQlUN)pv z{FSfQQb~_@aEA%Sc~sI_VGs<~Wyi{!#`PNg@>YIguNOQ^tK6*Zv@z1C#>L0k{v zc&QWl@<5AE1LPPg4jBZE6P{-5!pgM63x}DPmmadjjghHOTF2;^m}Nnj!4T(WMNe!R z9-c&659(>{j?OAsc!j_efok}_qJ1#L4rX@-qhU>x8f061C+)#2LX5}{eiAvrba+e5 zj``dmuFI#s!?%f{1)R-4PAlCWH&l+CTT6nusQcs(TN^uOK zmTDuD2fanmnWL9XcR#FCnW`o{8oNkJuEXn5cR$7|(+RFP9@${eE)IzznggJH@t>J= z3>&K93>~a-gE5IjEH}v8>EDzvn1@9+`NJ^LG$Qs2EEIk@V}+ zO;8iti~9!>vi{x%GKqZN457XY^c3d-@GDW{T_;tDk!MuFEHjF;KD3b`r-oGccWR4( zmAj!j!u8WhG?s^g-m*&t_nsph$h2aAe+1kh-KzN!uv0#V@%>_-F zO|q0Vh@r|%w#)3(TA~P2KKeojK0J}%{g3;Qw;)ZSrTg#Ng2T?lslw9^f&iCfMk9uC zn*2A7{$m&uH|S&b@tiaq;~o9qf=H?17hc%jtPw9gE8DaF4n z4$&xoqZMI%s@aD-iiI#JcYcXk?$wgn<3$|(z_CztxndBWrpn!b0w;4N4a>{|x=UZ0 zF?;R-#WCFBbok8upCQd$s_+V9=}doSp@oX2R?wo-@b7+x*>I?QYatn}%xB-!F%EyP z1=1M(9+yfbubI2Ei}S^dgC|qm)0;{_0aNpMkQTe$JVZd zW|4nWh9PM>M}ffN?hP;nC6|!7Ea#u~lNik57iTx+wnhX}utf^C_nom5YO@VL2@(0; zLDS8fwnSQ<5Doo~AxmnnIY*01`SHf%G1ay350!Ia_m&KadY7B1f~Ru{UQOJ8NU!(b zpYk3z7bR}#sWuvcCw{cxRU+vP+$~oIYn8S2PAc&apaZsAv94k#hX^PosJmPfqz%a8 zb(E)MK}bkj9JLESFF#aS$4n$ zpzqeX6fQwu1B4X^5Xhf+erzNpv7(1YL!#7fWrr{5U zOgd|zabtDAnn2sB0+&L0-W1N!#LxyrQ3pEPly@_@`Y7*L|4yg*yYlu`H=K6QM)1l! zv9eCwD%SM`L&O=*+)5x!0LVNY771tLX&U&3UFl-1O%rFtvYnZO zhi)(vPm~mU!iW=kQ3wtYv>#lU03edb2$A~GQ=_vDO*YpyKkT=Q-!p-341}>;ZDkO@ zoTV!&uFD&(U#%E!?&Bj_L}{t7vhL4;)xfAp*xdU`ma&#v0U`0$1D>OT^SnH$e7N-c z+&bl7cqxMD+B;vi%^?qBL)^t5R~3*9I)Y6Wj5TU;)oKvmgJdL@7RYdL2-o5}OLNoh zl43SHdNKj|R)w9NjIRbFxp7zIsiB9q4m3g@vq@~H=wV-5nocsnS8|?M(^{ozeN$s3 z6FDZk=7H7C#xs#2u{6cAmC&!xbG(=@mPtf;tbZqw8F$3fIJ}LZ(n+8WrU@nk`PX=R z`oG?L%*EgtHQ|~$Qd$Z|@*^K#D{*ldiATHP1E$s0^77@GnI$eg!D8F6>i^K+=$Z3;Ef4D!OSiBZ^NxD4n{`)JH9lTchbKycK_lIqES^WzdzfOFPLSES(_t;@CFCK$Uf!cjGWH98U} zCPIf9%^SE_#YtugvjlQ1W5Fbv1g8hi`>{ytic3Q(9VLSw1;BE9I|^%u8`*izz4bj6 z+~{Ml#W*mxtWk)^xEEvseJ6?1fXnl5hNwO6kMxGVM(_zk;xq(#stA^kY2+g4;gUO% z#Ia=wGz4;ssi)GXEB&4Yct9Bj^Qj-C`|)6?>k|UMph(#)$x zB9*x^o9P~;mZS;&WqfO%-7@#7f#6bBQM~L!?pOd_q*(jEJ_kPmd-A3t&$D6<*+?M{=s*ilH!d)E@KQP^eNnd<`LLNlm zFN+Eom67nqoVt#I-epf*KI&x_p_2`XzBK1k?yUz`1%3fG|DiDSO??IDS%m>Y5kCKA zH%RJhdDBCC?CcG3*~V(mn)v!W8El0wm5qEXn=bAKPXLlQ|0bk_h`MKAHUBp4t;F(EGfZPd*sUU{OXewaVOA>5-p`(mE@tIt`6x&HaXbB}e7ngOt zafWCpGCuP++)0XJ4e65R*!ov#)5?px(|(Fu&JdEB^f^{t-X0n{L|XL#7e zsW&tNzN|X_{Ptct5ZIQbsOy$;&0BTo_DsNYTTT5d!%Xb6rG3_P_vdaB-g;&|KR`H3G1dK{@oyP~ zT~Xr52Lt>jZ&w72?GfBA5<14sKi=VdXJRYDW~K7rg3WHYd}cs|rS^C?#acH6CX8V9i_{~*y0f*vKveRMTXFIAJB zTS85HhMVVTAwXkgM2H`^xS2?qNJzsd4$ErMfiK?t6&Xt8-;tVL78(l zep(S#Ooe$Sv(C!ox;wg}QCY_XE6@@AZl0lt0Mkkjnho(QEx7Y(C z?P|N=!}Y5xm2>`8t+Gq?ARwzHxF#s)^nbEFOJIx?*f{_lNqvbYeC6hef8F9lt}reA z${S}A%uQHaA;Epf8~7u-S)`RN6|g$85|WhB4>*dSA&?}Lpzh^tA$Mw!f|gUvVr}wabwgcF(_q98Ce+AFo*9RSf0nxG_-C}(gWg2TJPQD;j^Wn7a~fI zk4vXn`Wo93iS%|I_h)ow&F&3f%~@6Vs_X3H&o=|VrwT5{|NCSY8fKki{<7Rmi&&Nu&V)*M z{^d~$_;amP-b**+9+cq|R`W49ozapogL#@n98+p$$J?T=u_xocoNd^HH10!#Ay=^KQv3ZxYVWGE|yk0A`Zl< zP#K?Ye$YSrpfG<_*>FB&vUL5CGQ|@98@7HCCBU>yblR&fPY+iRtnYj zk2%3>A$}0xQ!p8uR@=Z&L8fC0eHJ!b_ZCM2JM@MPs=K*&3ZWVyq&DigH?On6b)&?_ zffB=<@WO@HMDQ$A(V(mmZG|h9nY=D~BC8UPSbNaVXVmkn`@wFFIurzM|HoelmXm#9 zPRC1ysQ6Hd}=!B`8 z(vHpgSMWdV+(Yv#4iD8h&%~$y&#F*enx@H(7l%8;Q|aFR(+@LU;TyD)sZ2L5Oq#)k zrGRM-2h&nnWg^{&)XV$t&XP4|67z7%Anc`brpj;G7kTJ0<&8!Abq9x^R<|-jHFDHxUr8=aKKkBce(~V8 z9P=0rKZ4-HuFIKhNE{LoS#Q>(@Wj}^oo@`h%;ajnWaoE!N5W{cNg@s$S$M|!_HqdG z;?=Y21OR4smqw?oq?5BYKI!{OOm>6x?lezNnPYDWwUxING0b1{(#HgcK4mJm z`p0jcNzB93h0xetqaNjNst4W$L2YcaM7mC>gn5z(^(H0@6Ou@YRGay`L zYUwih6=skRPbzDOND!AB**QvLKhFvQj3qT;9pI;1!KXEUN0Pkyr3Le$d#{*aXz!nf zwc-%VYK$QyHDpsU7U+EADmz5$E!}+R-#_RHG6|Tn+4Ds{5v)eB@Y3YY)Y&V5lwzgj zADB<@#_$V#JPn6SylM`r5OMCfD0mCJ;PbBVdK<;-5c9#4*T-s~ z&grf1Xl(4!@bO^61h6h=+49#=0$}RHtqH_}D3{9>OKf3{LmS@nh~P2+S~N5r9y0R4 zA(tFXi#4R_9Dw zc04o{K(TWxl4A1EcX<^?8D$S%7Bs7#kgM+1P-fq!VID^K$gvww=b+|Bv3%#IC09gb zt43@;TOlQb;dtj}yRf|w-B#f93);e@CRa;(p8pQ`Q7tI4 zEpP+$&Q-*5*;UK*F{o)?pug%FwD?ZitH!s5Kd^WbmHtfJ3eC!Y&|B?m&6yt|>|?SQ zg`_WEi2zTcTpyhw!TxA+od{;F=n7@Fi+K!u(Tv3o_N#)@vbtl#Jt3EsQ|uP)rJ|KA z7%HUL5|_E5o7@rRJ{w1tp#9ac&;;+2Wk)}~L#lmB$fi97zt=x9y`vrBEn$w9SQ@)L z8j+276II1KD`jqL$4SvVL^xaN8`^Q^i4cN%J7F+p-%+tKQ@l7YjO)a9FvSwNq`u?L z-!G$Ai-S1w+{HHvPs~l{LM+#Ol1cG3Ls1d%f$Nc{=89@?@P1{ffBK|}= z_)LyTtK|A#0%dkBO(?u)kt@|v6rHoBh6@!Y=?@B%Z#23xytNw$7NROLgTdF90F29( z69_hFS=592&%X$pvuTqTo#NkwF#*FAM-I}8-zTqP6r-ND_&D7V+ZE5?Rd6EJ_@g(4 z$}QFWpQ3JS&z@wHbls@Opb|KR>`7cqGAm*fMwViim(1VL$7QE-by zy@4TAkW2*X#vc_dh@)V>K! zZ5+_wxj!D1F!wA$F+YdZ3J4L+dHqfbBMtY%${1_f0kyzfhA*^y3)pmtcSD-V?VYi8 z{n9+x0s{kuk5imoz2YS^=xp9QNz4;$9gFK0w@4xsm>&kGc(V#s^7(?lju^KYiK8l= zn!XbWz15BnBR^hYB^A+q=Ffk|c%)v>c{{Uhvu-qNESOyqXM|*2hBvo!tBn-s&EsN9 zmibKtdPK)IwDAbI6K>clkOr(UG(?NyKhKpTR+HgmNA;lcke&G(ZY)4AnNkO=ERkQ@ zQ=wf0)7V;;ZsA1&)!3H9Tkn%V6Z;C`X^USm6p$8Bo9qGY#mePQ(J@0bDLQyxN-OBTSRRE=LUT?FP@V67`WbPRB2J})spMoa&q-x#?C;Ru`3>{QbJx<4*6e#;DPk(W6BaRu%|F-mUVBMdCQ` z&k2G)C5YjDO~AMV;b0}Z&)v|5gg#P;RM{^iu;BFU2!6JrCX;l{drP5Hi@mLJzEx#2cfi~-jtk=&(zQniw0B}L*A`VgJCIJGCS<^j6O}XOTyoUo8F!{Uak2} zz8FVwSW%NIn$bX8ulisJM=^QkIhh(5j(fq?`p$c7FCgX0XoK~qgMEL%v|7q3-8^eg z`{%c}H*2gUo>kK6L@lN?{Rod5`vs-EFWS-9en}m*F(jQaW*2FEo@teM9P!oWUcBZ{ zK9!WY@!2Dy2vyjZriC#^HZk>mF&**``_mhmSCr#3dUR}i7X&piK4gAHjAJDU%Mi#Q zvSEsUn>vXxUBg=$t*=udOHHtN_e#42BQch0uk$-EMQQ~pt<%Cf=T)}$VH4W#_WEPW z+#vsMYS?wwaX~9yj)CF-9S5^w`U-g)>LwN3sj-aj{tp6zVQqChdawF_92_Xeg)-2|;fZRc6#^`GfX%#vQah2GU) zwwXFeuRPG^tqfr2L1%4cE(IS2??>Ur-H&(_!%2A%WeGoffAU6_D6?zMeDtb(f#^7h zz}NqN(N?aIQiZEQT2Om6yEAgvnEwF7+I}G>B(!O;awWEPIcl5WEpoyCN8(C?hjtxN zd9YtuLAro@k*_MD4zkdCHTYsGKyPqU*Bw=yD=OB+V1PIuUoc?EAQ2)`YR(w=`IHJJl>!E-3ScD8Jzi_{r>$7@Q z{;!WMNrvZNK<6%$sXB&(NN%&A2(6-3C*w$U ztTMW69Y0%*&J4`*u`hZDS{8)VfJ#lI_K`G76+}H@QsF0aEjsoFK|WwB~ZCyTl2#Kjt8=ZqX3l8Y~#+Z z1IGfk#9ua!A@Lt2jUAe!TuhadyDwdnOq>y-xlIpPRjTpg87E>=V4x&Gu8mtng+HdOuF!{NIPJ d%E6zos^Sknx4$2GeQ^iD->AHve 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 wd%6(RK> + android:width="21dp" + android:height="21dp" + android:viewportHeight="24.0" + android:viewportWidth="24.0"> diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable/mapbox_info_icon_selected.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable/mapbox_info_icon_selected.xml index 074928d05ad..ccbd1d8d399 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable/mapbox_info_icon_selected.xml +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable/mapbox_info_icon_selected.xml @@ -1,9 +1,9 @@ + android:width="21dp" + android:height="21dp" + android:viewportHeight="24.0" + android:viewportWidth="24.0"> + android:pathData="M11,17h2v-6h-2v6zm1,-15C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm0,18c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2V7h-2v2z"/> diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_internal.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_internal.xml index e6a26777858..6d07de7baac 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_internal.xml +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_internal.xml @@ -39,10 +39,9 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:adjustViewBounds="true" - android:background="@drawable/mapbox_default_bg_selector" android:clickable="true" + android:focusable="true" android:contentDescription="@string/mapbox_attributionsIconContentDescription" - android:padding="7dp" android:src="@drawable/mapbox_info_bg_selector"/> diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_preview.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_preview.xml index d015bc57854..1c1ab0e71b6 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_preview.xml +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_preview.xml @@ -1,22 +1,29 @@ + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + android:contentDescription="@null" + android:src="@drawable/mapbox_mapview_preview" + android:layout_alignParentTop="true" + android:layout_alignParentLeft="true" + android:layout_alignParentStart="true"/> @@ -24,18 +31,22 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" - android:layout_marginBottom="@dimen/mapbox_seven_dp" - android:layout_marginLeft="@dimen/mapbox_seventy_six_dp" + android:layout_toEndOf="@+id/logoView" + android:contentDescription="@null" + android:layout_marginBottom="@dimen/mapbox_four_dp" android:background="@drawable/mapbox_default_bg_selector" - android:clickable="true" - android:contentDescription="@string/mapbox_attributionsIconContentDescription" - android:src="@drawable/mapbox_info_bg_selector" /> + android:src="@drawable/mapbox_info_bg_selector" + android:layout_marginLeft="@dimen/mapbox_four_dp" + android:layout_marginStart="@dimen/mapbox_four_dp" + android:layout_toRightOf="@+id/logoView"/> + android:contentDescription="@null" + android:src="@drawable/mapbox_compass_icon"/> diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml index df6983e11dd..ce20cb9a8bf 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml @@ -4,10 +4,15 @@ 8dp -2dp 1.5dp - 7dp + @dimen/mapbox_two_dp + @dimen/mapbox_two_dp + @dimen/mapbox_two_dp + @dimen/mapbox_two_dp + 2dp + 4dp 8dp 10dp 16dp - 76dp + 95dp 18dp diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RuntimeStyleTests.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RuntimeStyleTests.java index 354f5def6f2..a1c46903bf3 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RuntimeStyleTests.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RuntimeStyleTests.java @@ -15,6 +15,8 @@ import com.mapbox.mapboxsdk.style.layers.Property; import com.mapbox.mapboxsdk.style.layers.PropertyFactory; import com.mapbox.mapboxsdk.style.sources.CannotAddSourceException; +import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; +import com.mapbox.mapboxsdk.style.sources.RasterSource; import com.mapbox.mapboxsdk.style.sources.Source; import com.mapbox.mapboxsdk.style.sources.VectorSource; import com.mapbox.mapboxsdk.testapp.R; @@ -28,6 +30,8 @@ import org.junit.Test; import org.junit.runner.RunWith; +import java.net.MalformedURLException; +import java.net.URL; import java.util.List; import timber.log.Timber; @@ -190,6 +194,35 @@ public void testAddRemoveSource() { onView(withId(R.id.mapView)).perform(new AddRemoveSourceAction()); } + @Test + public void testVectorSourceUrlGetter() { + validateTestSetup(); + + VectorSource source = new VectorSource("my-source", "mapbox://mapbox.mapbox-terrain-v2"); + mapboxMap.addSource(source); + assertEquals("mapbox://mapbox.mapbox-terrain-v2", source.getUrl()); + } + + @Test + public void testRasterSourceUrlGetter() { + validateTestSetup(); + + RasterSource source = new RasterSource("my-source", "mapbox://mapbox.mapbox-terrain-v2"); + mapboxMap.addSource(source); + assertEquals("mapbox://mapbox.mapbox-terrain-v2", source.getUrl()); + } + + @Test + public void testGeoJsonSourceUrlGetter() throws MalformedURLException { + validateTestSetup(); + + GeoJsonSource source = new GeoJsonSource("my-source"); + mapboxMap.addSource(source); + assertNull(source.getUrl()); + source.setUrl(new URL("http://mapbox.com/my-file.json")); + assertEquals("http://mapbox.com/my-file.json", source.getUrl()); + } + /** * https://github.com/mapbox/mapbox-gl-native/issues/7973 */ 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 08399f3b3ee..60518239c86 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 @@ protected void onCreate(Bundle savedInstanceState) { 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/gradle/wrapper/gradle-wrapper.properties b/platform/android/gradle/wrapper/gradle-wrapper.properties index c7ad166b13b..1d35abd7b29 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/src/style/sources/geojson_source.cpp b/platform/android/src/style/sources/geojson_source.cpp index 9b56446e31e..780cc4b6f6f 100644 --- a/platform/android/src/style/sources/geojson_source.cpp +++ b/platform/android/src/style/sources/geojson_source.cpp @@ -97,6 +97,11 @@ namespace android { source.as()->GeoJSONSource::setURL(jni::Make(env, url)); } + jni::String GeoJSONSource::getURL(jni::JNIEnv& env) { + optional url = source.as()->GeoJSONSource::getURL(); + return url ? jni::Make(env, *url) : jni::String(); + } + jni::Array> GeoJSONSource::querySourceFeatures(jni::JNIEnv& env, jni::Array> jfilter) { using namespace mbgl::android::conversion; @@ -133,6 +138,7 @@ namespace android { METHOD(&GeoJSONSource::setFeature, "nativeSetFeature"), METHOD(&GeoJSONSource::setGeometry, "nativeSetGeometry"), METHOD(&GeoJSONSource::setURL, "nativeSetUrl"), + METHOD(&GeoJSONSource::getURL, "nativeGetUrl"), METHOD(&GeoJSONSource::querySourceFeatures, "querySourceFeatures") ); } diff --git a/platform/android/src/style/sources/geojson_source.hpp b/platform/android/src/style/sources/geojson_source.hpp index 51ea452fb2d..938a20612c6 100644 --- a/platform/android/src/style/sources/geojson_source.hpp +++ b/platform/android/src/style/sources/geojson_source.hpp @@ -38,6 +38,8 @@ class GeoJSONSource : public Source { jni::Array> querySourceFeatures(jni::JNIEnv&, jni::Array> jfilter); + jni::String getURL(jni::JNIEnv&); + jni::jobject* createJavaPeer(jni::JNIEnv&); }; // class GeoJSONSource diff --git a/platform/android/src/style/sources/raster_source.cpp b/platform/android/src/style/sources/raster_source.cpp index 0234901a77c..32fdb163b0c 100644 --- a/platform/android/src/style/sources/raster_source.cpp +++ b/platform/android/src/style/sources/raster_source.cpp @@ -28,6 +28,11 @@ namespace android { RasterSource::~RasterSource() = default; + jni::String RasterSource::getURL(jni::JNIEnv& env) { + optional url = source.as()->RasterSource::getURL(); + return url ? jni::Make(env, *url) : jni::String(); + } + jni::Class RasterSource::javaClass; jni::jobject* RasterSource::createJavaPeer(jni::JNIEnv& env) { @@ -46,7 +51,8 @@ namespace android { env, RasterSource::javaClass, "nativePtr", std::make_unique, jni::jint>, "initialize", - "finalize" + "finalize", + METHOD(&RasterSource::getURL, "nativeGetUrl") ); } diff --git a/platform/android/src/style/sources/raster_source.hpp b/platform/android/src/style/sources/raster_source.hpp index 6600096f6de..a79ccc10a4d 100644 --- a/platform/android/src/style/sources/raster_source.hpp +++ b/platform/android/src/style/sources/raster_source.hpp @@ -22,6 +22,8 @@ class RasterSource : public Source { ~RasterSource(); + jni::String getURL(jni::JNIEnv&); + jni::jobject* createJavaPeer(jni::JNIEnv&); }; // class RasterSource diff --git a/platform/android/src/style/sources/vector_source.cpp b/platform/android/src/style/sources/vector_source.cpp index a8f74df142e..e2d9f60dec4 100644 --- a/platform/android/src/style/sources/vector_source.cpp +++ b/platform/android/src/style/sources/vector_source.cpp @@ -34,6 +34,11 @@ namespace android { VectorSource::~VectorSource() = default; + jni::String VectorSource::getURL(jni::JNIEnv& env) { + optional url = source.as()->VectorSource::getURL(); + return url ? jni::Make(env, *url) : jni::String(); + } + jni::Array> VectorSource::querySourceFeatures(jni::JNIEnv& env, jni::Array jSourceLayerIds, jni::Array> jfilter) { @@ -66,7 +71,8 @@ namespace android { std::make_unique>, "initialize", "finalize", - METHOD(&VectorSource::querySourceFeatures, "querySourceFeatures") + METHOD(&VectorSource::querySourceFeatures, "querySourceFeatures"), + METHOD(&VectorSource::getURL, "nativeGetUrl") ); } diff --git a/platform/android/src/style/sources/vector_source.hpp b/platform/android/src/style/sources/vector_source.hpp index cac687bb6fc..643b4683386 100644 --- a/platform/android/src/style/sources/vector_source.hpp +++ b/platform/android/src/style/sources/vector_source.hpp @@ -26,6 +26,8 @@ class VectorSource : public Source { jni::Array> querySourceFeatures(jni::JNIEnv&, jni::Array, jni::Array> jfilter); + jni::String getURL(jni::JNIEnv&); + jni::jobject* createJavaPeer(jni::JNIEnv&); }; // class VectorSource diff --git a/platform/default/default_file_source.cpp b/platform/default/default_file_source.cpp index 27c3950e2a4..7db0b854611 100644 --- a/platform/default/default_file_source.cpp +++ b/platform/default/default_file_source.cpp @@ -200,12 +200,16 @@ std::string DefaultFileSource::getAccessToken() const { } void DefaultFileSource::setResourceTransform(std::function transform) { - auto loop = util::RunLoop::Get(); - thread->invoke(&Impl::setResourceTransform, [loop, transform](Resource::Kind kind_, std::string&& url_, auto callback_) { - return loop->invokeWithCallback([transform](Resource::Kind kind, std::string&& url, auto callback) { - callback(transform(kind, std::move(url))); - }, kind_, std::move(url_), callback_); - }); + if (transform) { + auto loop = util::RunLoop::Get(); + thread->invoke(&Impl::setResourceTransform, [loop, transform](Resource::Kind kind_, std::string&& url_, auto callback_) { + return loop->invokeWithCallback([transform](Resource::Kind kind, std::string&& url, auto callback) { + callback(transform(kind, std::move(url))); + }, kind_, std::move(url_), callback_); + }); + } else { + thread->invoke(&Impl::setResourceTransform, nullptr); + } } std::unique_ptr DefaultFileSource::request(const Resource& resource, Callback callback) { From 59442167b16ec5b92c29aad4670340b740a0a9e4 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Mon, 15 May 2017 13:39:34 +0200 Subject: [PATCH 12/62] [android] - MarkerView deselect events with OnMarkerViewClickListener integration (#8996) --- .../mapbox/mapboxsdk/maps/AnnotationManager.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) 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 6553b64592c..e811470d649 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 @@ -670,12 +670,15 @@ boolean onTap(PointF tapPoint, float screenDensity) { 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; } From 627455c7cc6938e8a05508824450e34dfa75c15e Mon Sep 17 00:00:00 2001 From: Tobrun Date: Mon, 15 May 2017 14:39:11 +0200 Subject: [PATCH 13/62] [android] - publish SNAPSHOT from release 5.1.0 branch (#8995) --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 4ce00872b4f..6bced990de5 100644 --- a/circle.yml +++ b/circle.yml @@ -133,4 +133,4 @@ jobs: - deploy: name: Publish to Maven command: | - if [ "${CIRCLE_BRANCH}" == "release-android-v5.1.0-beta.2" ]; then make run-android-upload-archives ; fi + if [ "${CIRCLE_BRANCH}" == "release-ios-v3.6.0-android-v5.1.0" ]; then make run-android-upload-archives ; fi From c7acb5516b7575feb20f20d6f6cfaabede97c948 Mon Sep 17 00:00:00 2001 From: Jordan Kiley Date: Mon, 15 May 2017 11:27:10 -0700 Subject: [PATCH 14/62] [ios, macos] edited identity interpolation mode documentation (#8657) --- platform/darwin/src/MGLStyleValue.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/platform/darwin/src/MGLStyleValue.h b/platform/darwin/src/MGLStyleValue.h index 70074c8a131..2bb3aca4f4a 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 }; From 30b919196b72351c43a5834b66a36068c0588fe0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguye=CC=82=CC=83n?= Date: Tue, 9 May 2017 18:36:20 -0700 Subject: [PATCH 15/62] [ios, macos] Factored out tile URL template guide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Factored out redundant tile URL template documentation from the MGLRasterSource and MGLVectorSource documentation into a jazzy guide. This documentation used to live in one place, on a method on MGLTileSource, but that method had to be moved to MGLTileSource’s concrete subclasses. A jazzy guide is easier to link to, in any case. --- .../docs/guides/Tile URL Templates.md.ejs | 109 ++++++++++++++++++ .../darwin/scripts/generate-style-code.js | 13 ++- platform/darwin/src/MGLRasterSource.h | 91 +-------------- platform/darwin/src/MGLVectorSource.h | 91 +-------------- .../ios/docs/guides/Tile URL Templates.md | 99 ++++++++++++++++ platform/ios/jazzy.yml | 1 + .../macos/docs/guides/Tile URL Templates.md | 98 ++++++++++++++++ platform/macos/jazzy.yml | 1 + 8 files changed, 326 insertions(+), 177 deletions(-) create mode 100644 platform/darwin/docs/guides/Tile URL Templates.md.ejs create mode 100644 platform/ios/docs/guides/Tile URL Templates.md create mode 100644 platform/macos/docs/guides/Tile URL Templates.md 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 00000000000..78fb2971384 --- /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'; +-%> + +# 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: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Placeholder stringDescription
{x}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 2z−1, + the tile’s right edge corresponds to the 180th meridian east.
{y}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 2z−1, the tile’s bottom edge corresponds to + −arctan(sinh(π)), or approximately 85.0511 degrees south. The y axis is + inverted if the options parameter contains + MGLTileSourceOptionTileCoordinateSystem with a value of + MGLTileCoordinateSystemTMS.
{z}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, + 116 of the world, and so on. For tiles loaded by + a MGLRasterSource 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 + MGLTileSourceOptionTileSize key of the options + parameter.
{bbox-epsg-3857}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 + Web Map Service + protocol.
{quadkey}A quadkey indicating both the tile’s location and its zoom level. The + quadkey is typically used with + Bing Maps. +
{ratio}A suffix indicating the resolution of the tile image. The suffix is the + empty string for standard resolution displays and @2x for +<% if (iOS) { -%> + Retina displays, including displays for which UIScreen.scale + is 3. +<% } else { -%> + Retina displays. +<% } -%> +
{prefix}Two hexadecimal digits chosen such that each visible tile has a + different prefix. The prefix is typically used for domain sharding.
+ +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 330a8f1d03f..7efc8d441cf 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/MGLRasterSource.h b/platform/darwin/src/MGLRasterSource.h index 519784f4f17..4f4b7c96c39 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 “
Tile URL Templates” + 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: - -

    -
  • 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 sources support the following placeholder strings in tile URL templates, - all of which are optional: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Placeholder stringDescription
{x}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 2z−1, - the tile’s right edge corresponds to the 180th meridian east.
{y}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 2z−1, the tile’s bottom edge corresponds to - −arctan(sinh(π)), or approximately 85.0511 degrees south. The y axis is - inverted if the options parameter contains - MGLTileSourceOptionTileCoordinateSystem with a value of - MGLTileCoordinateSystemTMS.
{z}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, - 116 of the world, and so on. For tiles loaded by - a MGLRasterSource 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 - MGLTileSourceOptionTileSize key of the - options parameter.
{bbox-epsg-3857}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 - Web Map Service - protocol.
{quadkey}A quadkey indicating both the tile’s location and its zoom level. The - quadkey is typically used with - Bing Maps. -
{ratio}A suffix indicating the resolution of the tile image. The suffix is the - empty string for standard resolution displays and @2x for - Retina displays, including displays for which - NSScreen.backingScaleFactor or UIScreen.scale - is 3.
{prefix}Two hexadecimal digits chosen such that each visible tile has a - different prefix. The prefix is typically used for domain sharding.
- - For more information about the `{x}`, `{y}`, and `{z}` placeholder strings, - consult the - OpenStreetMap Wiki. - @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/MGLVectorSource.h b/platform/darwin/src/MGLVectorSource.h index a48434f7a3d..968be3c0e00 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 “Tile URL Templates” + 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: - -
    -
  • 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 sources support the following placeholder strings in tile URL templates, - all of which are optional: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Placeholder stringDescription
{x}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 2z−1, - the tile’s right edge corresponds to the 180th meridian east.
{y}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 2z−1, the tile’s bottom edge corresponds to - −arctan(sinh(π)), or approximately 85.0511 degrees south. The y axis is - inverted if the options parameter contains - MGLTileSourceOptionTileCoordinateSystem with a value of - MGLTileCoordinateSystemTMS.
{z}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, - 116 of the world, and so on. For tiles loaded by - a MGLRasterSource 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 - MGLTileSourceOptionTileSize key of the - options parameter.
{bbox-epsg-3857}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 - Web Map Service - protocol.
{quadkey}A quadkey indicating both the tile’s location and its zoom level. The - quadkey is typically used with - Bing Maps. -
{ratio}A suffix indicating the resolution of the tile image. The suffix is the - empty string for standard resolution displays and @2x for - Retina displays, including displays for which - NSScreen.backingScaleFactor or UIScreen.scale - is 3.
{prefix}Two hexadecimal digits chosen such that each visible tile has a - different prefix. The prefix is typically used for domain sharding.
- - For more information about the `{x}`, `{y}`, and `{z}` placeholder strings, - consult the - OpenStreetMap Wiki. - @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/ios/docs/guides/Tile URL Templates.md b/platform/ios/docs/guides/Tile URL Templates.md new file mode 100644 index 00000000000..f61d2ea33a9 --- /dev/null +++ b/platform/ios/docs/guides/Tile URL Templates.md @@ -0,0 +1,99 @@ + +# 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: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Placeholder stringDescription
{x}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 2z−1, + the tile’s right edge corresponds to the 180th meridian east.
{y}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 2z−1, the tile’s bottom edge corresponds to + −arctan(sinh(π)), or approximately 85.0511 degrees south. The y axis is + inverted if the options parameter contains + MGLTileSourceOptionTileCoordinateSystem with a value of + MGLTileCoordinateSystemTMS.
{z}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, + 116 of the world, and so on. For tiles loaded by + a MGLRasterSource 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 + MGLTileSourceOptionTileSize key of the options + parameter.
{bbox-epsg-3857}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 + Web Map Service + protocol.
{quadkey}A quadkey indicating both the tile’s location and its zoom level. The + quadkey is typically used with + Bing Maps. +
{ratio}A suffix indicating the resolution of the tile image. The suffix is the + empty string for standard resolution displays and @2x for + Retina displays, including displays for which UIScreen.scale + is 3. +
{prefix}Two hexadecimal digits chosen such that each visible tile has a + different prefix. The prefix is typically used for domain sharding.
+ +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/jazzy.yml b/platform/ios/jazzy.yml index e0ce29bebad..638abaaf6c7 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 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 00000000000..01672c66860 --- /dev/null +++ b/platform/macos/docs/guides/Tile URL Templates.md @@ -0,0 +1,98 @@ + +# 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: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Placeholder stringDescription
{x}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 2z−1, + the tile’s right edge corresponds to the 180th meridian east.
{y}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 2z−1, the tile’s bottom edge corresponds to + −arctan(sinh(π)), or approximately 85.0511 degrees south. The y axis is + inverted if the options parameter contains + MGLTileSourceOptionTileCoordinateSystem with a value of + MGLTileCoordinateSystemTMS.
{z}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, + 116 of the world, and so on. For tiles loaded by + a MGLRasterSource 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 + MGLTileSourceOptionTileSize key of the options + parameter.
{bbox-epsg-3857}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 + Web Map Service + protocol.
{quadkey}A quadkey indicating both the tile’s location and its zoom level. The + quadkey is typically used with + Bing Maps. +
{ratio}A suffix indicating the resolution of the tile image. The suffix is the + empty string for standard resolution displays and @2x for + Retina displays. +
{prefix}Two hexadecimal digits chosen such that each visible tile has a + different prefix. The prefix is typically used for domain sharding.
+ +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 3c24b351cca..53f260b242e 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: From 4f90d7a56608d6962cfb86505dfeba92b8cd3154 Mon Sep 17 00:00:00 2001 From: Jordan Kiley Date: Wed, 17 May 2017 15:46:27 -0700 Subject: [PATCH 16/62] [ios] Telemetry button in modal view controllers (#9027) Fixes #8980. --- platform/ios/src/MGLMapView.mm | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index a4c9f5194c4..4054099dbd2 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -1999,9 +1999,10 @@ - (void)presentTelemetryAlertController }]; [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 - From eb83c7250933b7d3705a3508976c69571a9b9d4c Mon Sep 17 00:00:00 2001 From: Tobrun Date: Fri, 19 May 2017 09:14:54 +0200 Subject: [PATCH 17/62] [android] - bump tools and support lib version due to SNAPSHOT dependencies (#9046) * [android] - bump tools and support lib version for SNAPSHOT dependencies * revert unsupported Circle CI build tools version --- platform/android/MapboxGLAndroidSDK/build.gradle | 4 +--- platform/android/dependencies.gradle | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/build.gradle b/platform/android/MapboxGLAndroidSDK/build.gradle index fca36237c3c..5e19e94f5de 100644 --- a/platform/android/MapboxGLAndroidSDK/build.gradle +++ b/platform/android/MapboxGLAndroidSDK/build.gradle @@ -6,9 +6,7 @@ dependencies { compile rootProject.ext.dep.supportDesign compile rootProject.ext.dep.timber compile rootProject.ext.dep.okhttp3 - compile(rootProject.ext.dep.lost) { - exclude module: 'support-compat' - } + compile rootProject.ext.dep.lost // Mapbox Android Services (GeoJSON support) compile(rootProject.ext.dep.mapboxJavaGeoJSON) { diff --git a/platform/android/dependencies.gradle b/platform/android/dependencies.gradle index bbf1fb075d7..eb717ba6004 100644 --- a/platform/android/dependencies.gradle +++ b/platform/android/dependencies.gradle @@ -8,7 +8,7 @@ ext { versionName = "5.0.0" mapboxServicesVersion = "2.2.0-SNAPSHOT" - supportLibVersion = "25.1.0" + supportLibVersion = "25.3.1" wearableVersion = '2.0.0' espressoVersion = '2.2.2' testRunnerVersion = '0.5' From f0b0167640fa6cbd852ec1bed237277ee7c5f06d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Thu, 18 May 2017 12:39:03 +0200 Subject: [PATCH 18/62] [core] Make destructor virtual to avoid object splicing during destruction --- src/mbgl/style/sources/geojson_source_impl.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mbgl/style/sources/geojson_source_impl.hpp b/src/mbgl/style/sources/geojson_source_impl.hpp index e8b881d05ef..dece1269f85 100644 --- a/src/mbgl/style/sources/geojson_source_impl.hpp +++ b/src/mbgl/style/sources/geojson_source_impl.hpp @@ -13,6 +13,7 @@ namespace style { class GeoJSONData { public: + virtual ~GeoJSONData() = default; virtual mapbox::geometry::feature_collection getTile(const CanonicalTileID&) = 0; }; From b52e171b6737522fe5e7739646f523becd315381 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Thu, 18 May 2017 14:30:20 +0200 Subject: [PATCH 19/62] [core] add error for non-virtual destructor deletes + add virtual dtors --- CMakeLists.txt | 2 +- include/mbgl/map/map_observer.hpp | 2 ++ src/mbgl/programs/symbol_program.hpp | 8 +++++--- src/mbgl/sprite/sprite_atlas.hpp | 1 + src/mbgl/text/glyph_atlas.hpp | 1 + 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f8757d0c888..f0c7a2ac572 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,7 +66,7 @@ endif(WITH_COVERAGE) set(CMAKE_CONFIGURATION_TYPES Debug Release) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -ftemplate-depth=1024 -Wall -Wextra -Wshadow -Werror -Wno-variadic-macros -Wno-unknown-pragmas") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -ftemplate-depth=1024 -Wall -Wextra -Wshadow -Wnon-virtual-dtor -Werror -Wno-variadic-macros -Wno-unknown-pragmas") if(APPLE) # -Wno-error=unused-command-line-argument is required due to https://llvm.org/bugs/show_bug.cgi?id=7798 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=unused-command-line-argument") diff --git a/include/mbgl/map/map_observer.hpp b/include/mbgl/map/map_observer.hpp index 4fefb791215..f63e5f2af32 100644 --- a/include/mbgl/map/map_observer.hpp +++ b/include/mbgl/map/map_observer.hpp @@ -10,6 +10,8 @@ namespace mbgl { class MapObserver { public: + virtual ~MapObserver() = default; + static MapObserver& nullObserver() { static MapObserver mapObserver; return mapObserver; diff --git a/src/mbgl/programs/symbol_program.hpp b/src/mbgl/programs/symbol_program.hpp index 1cac99bfc2d..46f532e5c4b 100644 --- a/src/mbgl/programs/symbol_program.hpp +++ b/src/mbgl/programs/symbol_program.hpp @@ -93,6 +93,8 @@ class SymbolSizeAttributes : public gl::Attributes { // particular attribute & uniform logic needed by each possible type of the {Text,Icon}Size properties. class SymbolSizeBinder { public: + virtual ~SymbolSizeBinder() = default; + using Uniforms = gl::Uniforms< uniforms::u_is_size_zoom_constant, uniforms::u_is_size_feature_constant, @@ -129,7 +131,7 @@ Range getCoveringStops(Stops s, float lowerZoom, float upperZoom) { }; } -class ConstantSymbolSizeBinder : public SymbolSizeBinder { +class ConstantSymbolSizeBinder final : public SymbolSizeBinder { public: using PropertyValue = variant>; @@ -198,7 +200,7 @@ class ConstantSymbolSizeBinder : public SymbolSizeBinder { optional> function; }; -class SourceFunctionSymbolSizeBinder : public SymbolSizeBinder { +class SourceFunctionSymbolSizeBinder final : public SymbolSizeBinder { public: using Vertex = gl::detail::Vertex>; using VertexVector = gl::VertexVector; @@ -251,7 +253,7 @@ class SourceFunctionSymbolSizeBinder : public SymbolSizeBinder { optional buffer; }; -class CompositeFunctionSymbolSizeBinder: public SymbolSizeBinder { +class CompositeFunctionSymbolSizeBinder final : public SymbolSizeBinder { public: using Vertex = SymbolSizeAttributes::Vertex; using VertexVector = gl::VertexVector; diff --git a/src/mbgl/sprite/sprite_atlas.hpp b/src/mbgl/sprite/sprite_atlas.hpp index 5afddaebce1..c3efeff44bc 100644 --- a/src/mbgl/sprite/sprite_atlas.hpp +++ b/src/mbgl/sprite/sprite_atlas.hpp @@ -43,6 +43,7 @@ typedef std::set IconDependencies; class IconRequestor { public: + virtual ~IconRequestor() = default; virtual void onIconsAvailable(IconMap) = 0; }; diff --git a/src/mbgl/text/glyph_atlas.hpp b/src/mbgl/text/glyph_atlas.hpp index f4c46015305..ad9cf35adc8 100644 --- a/src/mbgl/text/glyph_atlas.hpp +++ b/src/mbgl/text/glyph_atlas.hpp @@ -30,6 +30,7 @@ class Context; class GlyphRequestor { public: + virtual ~GlyphRequestor() = default; virtual void onGlyphsAvailable(GlyphPositionMap) = 0; }; From f4f587f46dbde355bebd70e57cacf2ad788d0fd9 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Fri, 19 May 2017 12:23:47 +0200 Subject: [PATCH 20/62] [android] - remove marker from selected markers when removing marker from annotation manager. (#9047) --- .../com/mapbox/mapboxsdk/maps/AnnotationManager.java | 9 +++++++++ 1 file changed, 9 insertions(+) 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 e811470d649..3694668a7e6 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 @@ -92,6 +92,10 @@ void removeAnnotation(@NonNull Annotation annotation) { 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); } @@ -118,6 +122,10 @@ void removeAnnotations(@NonNull List annotationList) { 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); } @@ -138,6 +146,7 @@ void removeAnnotations() { Annotation annotation; int count = annotations.size(); long[] ids = new long[count]; + selectedMarkers.clear(); for (int i = 0; i < count; i++) { ids[i] = annotations.keyAt(i); annotation = annotations.get(ids[i]); From 3b109c8540ebd4da943e81beff5d9ae1483500c3 Mon Sep 17 00:00:00 2001 From: Fredrik Karlsson Date: Fri, 19 May 2017 14:01:35 +0200 Subject: [PATCH 21/62] Observe layout guides (#7716) * [ios] observe layout guides * [ios] update changelog --- platform/ios/CHANGELOG.md | 1 + platform/ios/src/MGLMapView.mm | 218 +++++++++++++++----------------- platform/ios/src/MGLScaleBar.mm | 6 + 3 files changed, 111 insertions(+), 114 deletions(-) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 922b7b6486c..ed3cb18b1b5 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -32,6 +32,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * 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 error passed into `-[MGLMapViewDelegate mapViewDidFailLoadingMap:withError:]` now includes a more specific description and failure reason. ([#8418](https://github.com/mapbox/mapbox-gl-native/pull/8418)) * Fixed an issue rendering polylines that contain duplicate vertices. ([#8808](https://github.com/mapbox/mapbox-gl-native/pull/8808)) +* Fixed a bug which caused the compass and other ornaments to underlap navigation and tab bars. ([#7716](https://github.com/mapbox/mapbox-gl-native/pull/7716)) ## 3.5.2 - April 7, 2017 diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 4054099dbd2..5acb6007971 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -131,6 +131,9 @@ typedef NS_ENUM(NSUInteger, MGLUserTrackingState) { 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 @@ @interface MGLMapView () )delegate @@ -786,105 +781,31 @@ - (UIViewController *)viewControllerForLayoutGuides - (void)updateConstraints { - // scale control - // - [self removeConstraints:self.scaleBarConstraints]; - [self.scaleBarConstraints removeAllObjects]; + [super updateConstraints]; - [self.scaleBarConstraints addObject: - [NSLayoutConstraint constraintWithItem:self.scaleBar - attribute:NSLayoutAttributeTop - relatedBy:NSLayoutRelationEqual - toItem:self - attribute:NSLayoutAttributeTop - multiplier:1 - constant:5+self.contentInset.top]]; + // 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.scaleBarConstraints addObject: - [NSLayoutConstraint constraintWithItem:self.scaleBar - attribute:NSLayoutAttributeLeading - relatedBy:NSLayoutRelationEqual - toItem:self - attribute:NSLayoutAttributeLeading - multiplier:1 - constant:8 + self.contentInset.left]]; + UIViewController *viewController = self.viewControllerForLayoutGuides; + BOOL useLayoutGuides = viewController.view && viewController.automaticallyAdjustsScrollViewInsets; - [self addConstraints:self.scaleBarConstraints]; + if (useLayoutGuides && viewController.topLayoutGuide && !_isObservingTopLayoutGuide) { + [(NSObject *)viewController.topLayoutGuide addObserver:self forKeyPath:@"bounds" options:0 context:(void *)&MGLLayoutGuidesUpdatedContext]; + _isObservingTopLayoutGuide = YES; + } else if (!useLayoutGuides && _isObservingTopLayoutGuide) { + [(NSObject *)viewController.topLayoutGuide removeObserver:self forKeyPath:@"bounds" context:(void *)&MGLLayoutGuidesUpdatedContext]; + _isObservingTopLayoutGuide = NO; + } - // 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 (useLayoutGuides && viewController.bottomLayoutGuide && !_isObservingBottomLayoutGuide) { + [(NSObject *)viewController.bottomLayoutGuide addObserver:self forKeyPath:@"bounds" options:0 context:(void *)&MGLLayoutGuidesUpdatedContext]; + _isObservingBottomLayoutGuide = YES; + } else if (!useLayoutGuides && _isObservingBottomLayoutGuide) { + [(NSObject *)viewController.bottomLayoutGuide removeObserver:self forKeyPath:@"bounds" context:(void *)&MGLLayoutGuidesUpdatedContext]; + _isObservingBottomLayoutGuide = NO; + } } - (BOOL)isOpaque @@ -917,6 +838,10 @@ - (void)layoutSubviews [super layoutSubviews]; [self adjustContentInset]; + + [self observeLayoutGuidesIfNeeded]; + + [self layoutOrnaments]; if (!_isTargetingInterfaceBuilder) { _mbglMap->setSize([self size]); @@ -931,6 +856,39 @@ - (void)layoutSubviews [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+5, + CGRectGetHeight(self.bounds)-5-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 { @@ -970,6 +928,34 @@ - (void)adjustContentInset self.contentInset = contentInset; } +- (void)observeLayoutGuidesIfNeeded +{ + UIViewController *viewController = self.viewControllerForLayoutGuides; + BOOL useLayoutGuides = viewController.view && viewController.automaticallyAdjustsScrollViewInsets; + + if (!_isObservingTopLayoutGuide && useLayoutGuides && viewController.topLayoutGuide) + { + [(NSObject *)viewController.topLayoutGuide addObserver:self forKeyPath:@"bounds" options:0 context:MGLLayoutGuidesUpdatedContext]; + _isObservingTopLayoutGuide = YES; + } + else if (!useLayoutGuides && _isObservingTopLayoutGuide) + { + [(NSObject *)viewController.topLayoutGuide removeObserver:self forKeyPath:@"bounds" context:MGLLayoutGuidesUpdatedContext]; + _isObservingTopLayoutGuide = NO; + } + + if (!_isObservingBottomLayoutGuide && useLayoutGuides && viewController.bottomLayoutGuide) + { + [(NSObject *)viewController.bottomLayoutGuide addObserver:self forKeyPath:@"bounds" options:0 context:MGLLayoutGuidesUpdatedContext]; + _isObservingBottomLayoutGuide = YES; + } + else if (!useLayoutGuides && _isObservingBottomLayoutGuide) + { + [(NSObject *)viewController.bottomLayoutGuide removeObserver:self forKeyPath:@"bounds" context:MGLLayoutGuidesUpdatedContext]; + _isObservingBottomLayoutGuide = NO; + } +} + - (void)setContentInset:(UIEdgeInsets)contentInset { [self setContentInset:contentInset animated:NO]; @@ -1000,7 +986,7 @@ - (void)setContentInset:(UIEdgeInsets)contentInset animated:(BOOL)animated } // Compass, logo and attribution button constraints needs to be updated. - [self setNeedsUpdateConstraints]; + [self setNeedsLayout]; } /// Returns the frame of inset content within the map view. @@ -2066,6 +2052,10 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N [self updateCalloutView]; } } + else if (context == MGLLayoutGuidesUpdatedContext && [keyPath isEqualToString:@"bounds"]) + { + [self setNeedsLayout]; + } } + (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingZoomEnabled diff --git a/platform/ios/src/MGLScaleBar.mm b/platform/ios/src/MGLScaleBar.mm index 1216e400b3d..cd88c1e08e0 100644 --- a/platform/ios/src/MGLScaleBar.mm +++ b/platform/ios/src/MGLScaleBar.mm @@ -220,6 +220,12 @@ - (void)setMetersPerPoint:(CLLocationDistance)metersPerPoint { 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]; } From 7612e23dff48453abd00434b4fe8ba6645235875 Mon Sep 17 00:00:00 2001 From: Jesse Bounds Date: Wed, 17 May 2017 14:52:19 -0700 Subject: [PATCH 22/62] [ios, macos] Change == to = in style function initializers --- platform/darwin/src/MGLStyleValue.mm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/platform/darwin/src/MGLStyleValue.mm b/platform/darwin/src/MGLStyleValue.mm index 33b6babadfc..4dd6b550d8e 100644 --- a/platform/darwin/src/MGLStyleValue.mm +++ b/platform/darwin/src/MGLStyleValue.mm @@ -128,7 +128,7 @@ - (instancetype)initWithInterpolationMode:(MGLInterpolationMode)interpolationMod return {}; } - if (self == [super init]) { + if (self = [super init]) { self.interpolationMode = interpolationMode; self.stops = stops; @@ -181,7 +181,7 @@ - (instancetype)initWithInterpolationBase:(CGFloat)interpolationBase stops:(NSDi } - (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 @@ - (instancetype)initWithInterpolationBase:(CGFloat)interpolationBase stops:(NSDi } - (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; From 554b1cf3e2c8ba21fd3e2259d04915811668a3ac Mon Sep 17 00:00:00 2001 From: Jesse Bounds Date: Fri, 19 May 2017 09:35:09 -0700 Subject: [PATCH 23/62] [ios] Add annotation view initializer with annotation and reuse id (#9029) --- platform/ios/CHANGELOG.md | 1 + platform/ios/src/MGLAnnotationView.h | 30 ++++++++++++++++++++++ platform/ios/src/MGLAnnotationView.mm | 18 ++++++++----- platform/ios/test/MGLAnnotationViewTests.m | 2 +- 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index ed3cb18b1b5..b09b1587d53 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -17,6 +17,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT ### 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)) * 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)) diff --git a/platform/ios/src/MGLAnnotationView.h b/platform/ios/src/MGLAnnotationView.h index 184efdb3245..9b17f05a6e4 100644 --- a/platform/ios/src/MGLAnnotationView.h +++ b/platform/ios/src/MGLAnnotationView.h @@ -73,6 +73,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)annotation reuseIdentifier:(nullable NSString *)reuseIdentifier; + /** Called when the view is removed from the reuse queue. diff --git a/platform/ios/src/MGLAnnotationView.mm b/platform/ios/src/MGLAnnotationView.mm index 5e0ae3b8485..9e1212b4fb8 100644 --- a/platform/ios/src/MGLAnnotationView.mm +++ b/platform/ios/src/MGLAnnotationView.mm @@ -19,12 +19,20 @@ @interface MGLAnnotationView () @implementation MGLAnnotationView -- (instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier -{ - self = [self initWithFrame:CGRectZero]; ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier { + return [self initWithAnnotation:nil reuseIdentifier:reuseIdentifier]; +} + +- (instancetype)initWithAnnotation:(nullable id)annotation reuseIdentifier:(nullable NSString *)reuseIdentifier { + self = [super initWithFrame:CGRectZero]; if (self) { _lastAppliedScaleTransform = CATransform3DIdentity; + _annotation = annotation; _reuseIdentifier = [reuseIdentifier copy]; _scalesWithViewingDistance = YES; _enabled = YES; @@ -32,10 +40,6 @@ - (instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier return self; } -+ (BOOL)supportsSecureCoding { - return YES; -} - - (instancetype)initWithCoder:(NSCoder *)decoder { if (self = [super initWithCoder:decoder]) { _reuseIdentifier = [decoder decodeObjectOfClass:[NSString class] forKey:@"reuseIdentifier"]; diff --git a/platform/ios/test/MGLAnnotationViewTests.m b/platform/ios/test/MGLAnnotationViewTests.m index c0978eaf651..88ca755476c 100644 --- a/platform/ios/test/MGLAnnotationViewTests.m +++ b/platform/ios/test/MGLAnnotationViewTests.m @@ -85,7 +85,7 @@ - (MGLAnnotationView *)mapView:(MGLMapView *)mapView viewForAnnotation:(id Date: Fri, 19 May 2017 11:27:46 -0700 Subject: [PATCH 24/62] [ios] Remove annotation view from container view when annotation removed (#9025) The annotation container view keeps an array of annotation views that is separate from the array of subviews that is a property of the UIView parent class. This removes an annotation view from that container view array when the associated annotation is removed. This avoids issue related to previously removed annotation views continuing to be involved in map view logic around annotation view selection due to taps. --- platform/ios/CHANGELOG.md | 1 + platform/ios/src/MGLMapView.mm | 1 + 2 files changed, 2 insertions(+) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index b09b1587d53..332ec06ab41 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -20,6 +20,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * 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)) * 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)) ### User interaction diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 5acb6007971..720c1d506a6 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -3492,6 +3492,7 @@ - (void)removeAnnotations:(NS_ARRAY_OF(id ) *)annotations annotationView.annotation = nil; [annotationView removeFromSuperview]; + [self.annotationContainerView.annotationViews removeObject:annotationView]; if (annotationTag == _selectedAnnotationTag) { From 4d8625b13ca32dc8c9ca9484db663bf3aba52a6d Mon Sep 17 00:00:00 2001 From: Tobrun Date: Fri, 19 May 2017 20:38:23 +0200 Subject: [PATCH 25/62] [android] - correct bearing conversion when animating the map with jumpTo, easeTo and animateTo. (#9050) --- platform/android/src/map/camera_position.cpp | 2 +- platform/android/src/native_map_view.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/platform/android/src/map/camera_position.cpp b/platform/android/src/map/camera_position.cpp index aa5873b2736..f059fbd4131 100644 --- a/platform/android/src/map/camera_position.cpp +++ b/platform/android/src/map/camera_position.cpp @@ -12,7 +12,7 @@ jni::Object CameraPosition::New(jni::JNIEnv &env, mbgl::CameraOp 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; + double bearing_degrees = (options.angle.value_or(0) * 180.0 / M_PI) + 180; while (bearing_degrees > 360) { bearing_degrees -= 360; } diff --git a/platform/android/src/native_map_view.cpp b/platform/android/src/native_map_view.cpp index 1e2464f1a07..3dbfd6809ae 100755 --- a/platform/android/src/native_map_view.cpp +++ b/platform/android/src/native_map_view.cpp @@ -385,7 +385,7 @@ 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 - 180 * M_PI) / 180; } options.center = mbgl::LatLng(latitude, longitude); options.padding = insets; @@ -402,7 +402,7 @@ 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 - 180 * M_PI) / 180; } cameraOptions.center = mbgl::LatLng(latitude, longitude); cameraOptions.padding = insets; @@ -426,7 +426,7 @@ 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 - 180 * M_PI / 180); } cameraOptions.center = mbgl::LatLng(latitude, longitude); cameraOptions.padding = insets; From 13dee80e2e90f06ee6cb2883dda16f7c4442aca5 Mon Sep 17 00:00:00 2001 From: Jordan Kiley Date: Mon, 1 May 2017 15:42:54 -0700 Subject: [PATCH 26/62] [ios, macos] Updated `maximumZoomLevel` description, cherry-picked #8818 (#8842) Cherry-picked from a3e4e67ea68c455178d5c5ef3d864972fcf41147. --- platform/darwin/src/MGLStyleLayer.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/darwin/src/MGLStyleLayer.h b/platform/darwin/src/MGLStyleLayer.h index d68aee29bc3..7d181667d60 100644 --- a/platform/darwin/src/MGLStyleLayer.h +++ b/platform/darwin/src/MGLStyleLayer.h @@ -45,12 +45,12 @@ MGL_EXPORT @property (nonatomic, assign, getter=isVisible) BOOL visible; /** - The maximum zoom level at which the layer gets parsed and appears. + The maximum zoom level at which the layer gets parsed and appears. This value is a floating-point number. */ @property (nonatomic, assign) float maximumZoomLevel; /** - The minimum zoom level at which the layer gets parsed and appears. + The minimum zoom level at which the layer gets parsed and appears. This value is a floating-point number. */ @property (nonatomic, assign) float minimumZoomLevel; From ade7b0e1fdd111eeb5d83fe34ba216248db15384 Mon Sep 17 00:00:00 2001 From: Jordan Kiley Date: Tue, 2 May 2017 16:28:00 -0700 Subject: [PATCH 27/62] [ios] Updated podspecs and changelog for v3.5.3 (#8870) Cherry-picked from 25c19902a22e240da4e7ebf1974125b7e67bd21e. --- platform/ios/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 332ec06ab41..cb3175935f5 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -36,6 +36,9 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Fixed an issue rendering polylines that contain duplicate vertices. ([#8808](https://github.com/mapbox/mapbox-gl-native/pull/8808)) * Fixed a bug which caused the compass and other ornaments to underlap navigation and tab bars. ([#7716](https://github.com/mapbox/mapbox-gl-native/pull/7716)) +## 3.5.3 - May 2, 2017 +* Fixed an issue that prevented the attribution `UIAlertController` from showing in modal hierarchies. ([#8837](https://github.com/mapbox/mapbox-gl-native/pull/8837)) + ## 3.5.2 - April 7, 2017 * Fixed an issue that caused a crash when the user location annotation was presenting a callout view and the map was moved. ([#8686](https://github.com/mapbox/mapbox-gl-native/pull/8686)) From 9ae73714f15def72ab1f0189be98aaa13b7125db Mon Sep 17 00:00:00 2001 From: Jesse Bounds Date: Tue, 9 May 2017 13:41:56 -0700 Subject: [PATCH 28/62] [ios] Update podspecs and changelog for iOS v3.5.4 Cherry-picked from db7bb509e95d737199efa73a47bdcc973966ed97. --- platform/ios/CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index cb3175935f5..3a4890c17d4 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -36,7 +36,12 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Fixed an issue rendering polylines that contain duplicate vertices. ([#8808](https://github.com/mapbox/mapbox-gl-native/pull/8808)) * Fixed a bug which caused the compass and other ornaments to underlap navigation and tab bars. ([#7716](https://github.com/mapbox/mapbox-gl-native/pull/7716)) +## 3.5.4 - May 9, 2017 + +* Fixed an issue that caused view backed annotations to become detached from the map view during pan gestures combined with animations of annotation view size or when the annotation view had no size but contained subviews with a non-zero size. ([#8926](https://github.com/mapbox/mapbox-gl-native/pull/8926)) + ## 3.5.3 - May 2, 2017 + * Fixed an issue that prevented the attribution `UIAlertController` from showing in modal hierarchies. ([#8837](https://github.com/mapbox/mapbox-gl-native/pull/8837)) ## 3.5.2 - April 7, 2017 From e630e8c1839c6e23532d18cb7f86e584531ebb5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Thu, 18 May 2017 20:25:28 -0700 Subject: [PATCH 29/62] [ios, macos] Updated changelogs --- platform/ios/CHANGELOG.md | 4 +++- platform/macos/CHANGELOG.md | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 3a4890c17d4..ca9d22f2305 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -2,7 +2,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONTRIBUTING.md](../../CONTRIBUTING.md) to get started. -## master +## 3.6.0 ### Styles @@ -11,6 +11,7 @@ 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)) @@ -26,6 +27,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * 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)) ### Other changes diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index 632bcfa44b6..ba09002d142 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog for Mapbox macOS SDK -## master +## 3.6.0 ### Styles @@ -9,6 +9,7 @@ * 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)) From a19fd817300abf00b81cba5765fda62c48057efa Mon Sep 17 00:00:00 2001 From: Jordan Kiley Date: Fri, 19 May 2017 16:08:03 -0700 Subject: [PATCH 30/62] [ios, macos] changed TRUE -> 'true' (#9059) fixes #9056 --- platform/darwin/src/MGLFillExtrusionStyleLayer.h | 2 +- platform/darwin/test/MGLDocumentationExampleTests.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/darwin/src/MGLFillExtrusionStyleLayer.h b/platform/darwin/src/MGLFillExtrusionStyleLayer.h index 84f6bedde4a..c4fb9aa77ea 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/test/MGLDocumentationExampleTests.swift b/platform/darwin/test/MGLDocumentationExampleTests.swift index 6d2dc597a94..48e6b17f445 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 From e52249c17fb529c476569b6d7ef141be1bff7d7f Mon Sep 17 00:00:00 2001 From: Fabian Guerra Soto Date: Tue, 23 May 2017 10:59:24 -0400 Subject: [PATCH 31/62] [ios, macos] Light property implementation in MGLStyle (#9043) * [ios, macos] Add MGLLight to MGLStyle * [ios, macos] Implement Objc bindings for Light object * [ios, macos] Remove rawLight from MGLLight and re-implement it as value class * [ios, macos] Fix build on macos * [ios, macos] Add MGLLight documentation, Move MGLLightPosition to MGLLight * [ios, macos] Add MGLLight tests. * [ios, macos] Update changelogs * [ios, macos] Fix misspelling * [ios, macos] Fix MGLLightAnchor enum property names * [ios, macos] Update documentation. Improve varialble naming. * [ios, macos] Rename MGLLightPosition to MGLSphericalPosition * [ios, macos] Update data types of MGLSphericalPosition --- platform/darwin/src/MGLLight.h | 125 +++++++++++ platform/darwin/src/MGLLight.mm | 117 ++++++++++ platform/darwin/src/MGLLight_Private.h | 23 ++ platform/darwin/src/MGLStyle.h | 9 + platform/darwin/src/MGLStyle.mm | 17 ++ platform/darwin/src/MGLStyleValue_Private.h | 14 ++ platform/darwin/src/MGLTypes.h | 1 + platform/darwin/src/NSValue+MGLAdditions.h | 29 +++ platform/darwin/src/NSValue+MGLAdditions.m | 23 ++ .../src/NSValue+MGLStyleAttributeAdditions.h | 1 + .../src/NSValue+MGLStyleAttributeAdditions.mm | 15 +- platform/darwin/test/MGLLightTest.mm | 211 ++++++++++++++++++ platform/ios/ios.xcodeproj/project.pbxproj | 22 ++ platform/ios/src/Mapbox.h | 1 + .../macos/macos.xcodeproj/project.pbxproj | 16 ++ platform/macos/src/Mapbox.h | 1 + 16 files changed, 624 insertions(+), 1 deletion(-) create mode 100644 platform/darwin/src/MGLLight.h create mode 100644 platform/darwin/src/MGLLight.mm create mode 100644 platform/darwin/src/MGLLight_Private.h create mode 100644 platform/darwin/test/MGLLightTest.mm diff --git a/platform/darwin/src/MGLLight.h b/platform/darwin/src/MGLLight.h new file mode 100644 index 00000000000..d9a22d60fae --- /dev/null +++ b/platform/darwin/src/MGLLight.h @@ -0,0 +1,125 @@ +#import + +#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 anchor + light property in the Mapbox Style Specification. + */ +@property (nonatomic) MGLLightAnchor 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 position + light property in the Mapbox Style Specification. + */ +@property (nonatomic) MGLStyleValue * 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 color + light property in the Mapbox Style Specification. + */ +@property (nonatomic) MGLStyleValue *color; +#else + +/** + Color tint for lighting extruded geometries. + */ +@property (nonatomic) MGLStyleValue *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 intensity + light property in the Mapbox Style Specification. + */ +@property(nonatomic) MGLStyleValue *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 00000000000..02d55e76ed8 --- /dev/null +++ b/platform/darwin/src/MGLLight.mm @@ -0,0 +1,117 @@ +#import "MGLLight.h" + +#import "MGLTypes.h" +#import "NSDate+MGLAdditions.h" +#import "MGLStyleValue_Private.h" +#import "NSValue+MGLAdditions.h" + +#import +#import + +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 *anchorStyleValue; + if (anchor.isUndefined()) { + mbgl::style::PropertyValue defaultAnchor = mbglLight->getDefaultAnchor(); + anchorStyleValue = MGLStyleValueTransformer().toEnumStyleValue(defaultAnchor); + } else { + anchorStyleValue = MGLStyleValueTransformer().toEnumStyleValue(anchor); + } + + NSAssert([anchorStyleValue isKindOfClass:[MGLConstantStyleValue class]], @"Anchor isn’t a constant."); + NSValue *anchorValue = ((MGLConstantStyleValue *)anchorStyleValue).rawValue; + _anchor = [anchorValue MGLLightAnchorValue]; + + _anchorTransition = MGLTransitionFromOptions(mbglLight->getAnchorTransition()); + + auto positionValue = mbglLight->getPosition(); + if (positionValue.isUndefined()) { + _position = MGLStyleValueTransformer().toStyleValue(mbglLight->getDefaultPosition()); + } else { + _position = MGLStyleValueTransformer().toStyleValue(positionValue); + } + + _positionTransiton = MGLTransitionFromOptions(mbglLight->getPositionTransition()); + + auto colorValue = mbglLight->getColor(); + if (colorValue.isUndefined()) { + _color = MGLStyleValueTransformer().toStyleValue(mbglLight->getDefaultColor()); + } else { + _color = MGLStyleValueTransformer().toStyleValue(colorValue); + } + + _colorTransiton = MGLTransitionFromOptions(mbglLight->getColorTransition()); + + auto intensityValue = mbglLight->getIntensity(); + if (intensityValue.isUndefined()) { + _intensity = MGLStyleValueTransformer().toStyleValue(mbglLight->getDefaultIntensity()); + } else { + _intensity = MGLStyleValueTransformer().toStyleValue(intensityValue); + } + + _intensityTransition = MGLTransitionFromOptions(mbglLight->getIntensityTransition()); + } + + return self; +} + +- (mbgl::style::Light)mbglLight +{ + mbgl::style::Light mbglLight; + + MGLStyleValue *anchorType = [MGLStyleValue valueWithRawValue:[NSValue valueWithMGLLightAnchor:self.anchor]]; + auto anchor = MGLStyleValueTransformer().toEnumPropertyValue(anchorType); + mbglLight.setAnchor(anchor); + + + mbglLight.setAnchorTransition(MGLOptionsFromTransition(self.anchorTransition)); + + auto position = MGLStyleValueTransformer().toInterpolatablePropertyValue(self.position); + mbglLight.setPosition(position); + + mbglLight.setPositionTransition(MGLOptionsFromTransition(self.positionTransiton)); + + auto color = MGLStyleValueTransformer().toInterpolatablePropertyValue(self.color); + mbglLight.setColor(color); + + mbglLight.setColorTransition(MGLOptionsFromTransition(self.colorTransiton)); + + auto intensity = MGLStyleValueTransformer().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 00000000000..dbc29c1eff7 --- /dev/null +++ b/platform/darwin/src/MGLLight_Private.h @@ -0,0 +1,23 @@ +#import + +#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/MGLStyle.h b/platform/darwin/src/MGLStyle.h index 26434eb4927..08c6c047f18 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 @@ -564,6 +565,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 af02c31b6d2..eb838085d72 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 #include #include +#include #include #include #include @@ -584,6 +586,21 @@ - (MGLTransition)transition return transition; } +#pragma mark Style light + +- (void)setLight:(MGLLight *)light +{ + std::unique_ptr mbglLight = std::make_unique([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_Private.h b/platform/darwin/src/MGLStyleValue_Private.h index 263b54d7e52..2155c657bd0 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 #include +#import #import @@ -415,6 +417,12 @@ class MGLStyleValueTransformer { 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 spherical = mbglStopValue.getSpherical(); + MGLSphericalPosition position = MGLSphericalPositionMake(spherical[0], spherical[1], spherical[2]); + return [NSValue valueWithMGLSphericalPosition:position]; + } // Enumerations template diff --git a/platform/darwin/src/MGLTypes.h b/platform/darwin/src/MGLTypes.h index c06fd8b0e7f..16f510b5a6e 100644 --- a/platform/darwin/src/MGLTypes.h +++ b/platform/darwin/src/MGLTypes.h @@ -1,4 +1,5 @@ #import +#import #import "MGLFoundation.h" diff --git a/platform/darwin/src/NSValue+MGLAdditions.h b/platform/darwin/src/NSValue+MGLAdditions.h index e6755021d00..0aaa2a337a4 100644 --- a/platform/darwin/src/NSValue+MGLAdditions.h +++ b/platform/darwin/src/NSValue+MGLAdditions.h @@ -1,6 +1,7 @@ #import #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 a95ef239417..ef894f0eb47 100644 --- a/platform/darwin/src/NSValue+MGLAdditions.m +++ b/platform/darwin/src/NSValue+MGLAdditions.m @@ -58,4 +58,27 @@ - (MGLTransition)MGLTransitionValue { 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 60c1ee4075b..0f1e5116947 100644 --- a/platform/darwin/src/NSValue+MGLStyleAttributeAdditions.h +++ b/platform/darwin/src/NSValue+MGLStyleAttributeAdditions.h @@ -9,5 +9,6 @@ - (std::array)mgl_offsetArrayValue; - (std::array)mgl_paddingArrayValue; +- (std::array)mgl_lightPositionArrayValue; @end diff --git a/platform/darwin/src/NSValue+MGLStyleAttributeAdditions.mm b/platform/darwin/src/NSValue+MGLStyleAttributeAdditions.mm index e66145aec15..a41950b6b3e 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 #define MGLEdgeInsets UIEdgeInsets @@ -61,4 +61,17 @@ + (instancetype)mgl_valueWithPaddingArray:(std::array)paddingArray }; } +- (std::array)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(lightPosition.radial), + static_cast(lightPosition.azimuthal), + static_cast(lightPosition.polar), + }; +} + @end diff --git a/platform/darwin/test/MGLLightTest.mm b/platform/darwin/test/MGLLightTest.mm new file mode 100644 index 00000000000..4639b13cded --- /dev/null +++ b/platform/darwin/test/MGLLightTest.mm @@ -0,0 +1,211 @@ +#import +#import + +#import "MGLLight_Private.h" + +#import "../../darwin/src/NSDate+MGLAdditions.h" + +#import +#import +#include + +@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]; + XCTAssertEqual(mglLight.anchor, 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); + + mglLight.anchor = MGLLightAnchorMap; + mglLight.anchorTransition = transition; + + XCTAssertEqual(mglLight.anchor, MGLLightAnchorMap); + XCTAssertEqual(mglLight.anchorTransition.delay, transition.delay); + XCTAssertEqual(mglLight.anchorTransition.duration, transition.duration); + + mbgl::style::PropertyValue 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 *positionStyleValue = [MGLStyleValue 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 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 *colorStyleValue = [MGLStyleValue 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 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 *intensityStyleValue = [MGLStyleValue 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 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/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index 215d31810dc..27582c1acfb 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 */; }; @@ -545,6 +552,10 @@ /* Begin PBXFileReference section */ 1753ED411E53CE6F00A9FD90 /* MGLConversion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLConversion.h; sourceTree = ""; }; + 1F0666881EC64F8E001C16D7 /* MGLLight.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLLight.h; sourceTree = ""; }; + 1F0666891EC64F8E001C16D7 /* MGLLight.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLLight.mm; sourceTree = ""; }; + 1F7454941ECD450D00021D39 /* MGLLight_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLLight_Private.h; sourceTree = ""; }; + 1F7454A61ED08AB400021D39 /* MGLLightTest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLLightTest.mm; path = ../../darwin/test/MGLLightTest.mm; sourceTree = ""; }; 1F95931C1E6DE2E900D5B294 /* MGLNSDateAdditionsTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLNSDateAdditionsTests.mm; path = ../../darwin/test/MGLNSDateAdditionsTests.mm; sourceTree = ""; }; 20DABE861DF78148007AC5FF /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Foundation.strings"; sourceTree = ""; }; 20DABE881DF78148007AC5FF /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; @@ -1039,6 +1050,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 +1083,7 @@ children = ( 3575798F1D513EF1000B822E /* Layers */, 40CFA64E1D78754A008103BD /* Sources */, + 1F7454A61ED08AB400021D39 /* MGLLightTest.mm */, 357F09091DF84F3800941873 /* MGLStyleValueTests.h */, 3599A3E51DF708BC00E77FB2 /* MGLStyleValueTests.m */, DA2207BE1DC0805F0002F84D /* MGLStyleValueTests.swift */, @@ -1657,6 +1672,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 +1721,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 +1800,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 +1834,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 */, @@ -2166,6 +2185,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 */, @@ -2221,6 +2241,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 +2349,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 */, diff --git a/platform/ios/src/Mapbox.h b/platform/ios/src/Mapbox.h index 9a9dc702cab..67a26e8ed48 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/macos/macos.xcodeproj/project.pbxproj b/platform/macos/macos.xcodeproj/project.pbxproj index 9a18ce4a0e0..11275240981 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 = ""; }; + 1F7454A01ECFB00300021D39 /* MGLLight_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLLight_Private.h; sourceTree = ""; }; + 1F7454A11ECFB00300021D39 /* MGLLight.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLLight.h; sourceTree = ""; }; + 1F7454A21ECFB00300021D39 /* MGLLight.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLLight.mm; sourceTree = ""; }; + 1F7454AA1ED1DDBD00021D39 /* MGLLightTest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLLightTest.mm; sourceTree = ""; }; 1F95931A1E6DE2B600D5B294 /* MGLNSDateAdditionsTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLNSDateAdditionsTests.mm; path = ../../darwin/test/MGLNSDateAdditionsTests.mm; sourceTree = ""; }; 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 = ""; }; @@ -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 = ""; @@ -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 */, diff --git a/platform/macos/src/Mapbox.h b/platform/macos/src/Mapbox.h index dcb5b50b8f2..0f47dace708 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" From 71b686517298e100f8021dca62db65a2ca9baad2 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Tue, 23 May 2017 17:14:18 +0200 Subject: [PATCH 32/62] [android] - horizontally rotated in snapshot (#9083) --- platform/android/src/map/camera_position.cpp | 7 ++++--- platform/android/src/native_map_view.cpp | 12 ++++++------ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/platform/android/src/map/camera_position.cpp b/platform/android/src/map/camera_position.cpp index f059fbd4131..27b19f7ad65 100644 --- a/platform/android/src/map/camera_position.cpp +++ b/platform/android/src/map/camera_position.cpp @@ -11,8 +11,9 @@ jni::Object 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) + 180; + // 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(-M_PI) * util::RAD2DEG; while (bearing_degrees > 360) { bearing_degrees -= 360; } @@ -21,7 +22,7 @@ jni::Object 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 3dbfd6809ae..1012a0e9d8f 100755 --- a/platform/android/src/native_map_view.cpp +++ b/platform/android/src/native_map_view.cpp @@ -385,12 +385,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 - 180 * 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 +402,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 - 180 * 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 +426,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 - 180 * 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; From fe17f7e8db266a125ac05d805003f5d2bf1548c6 Mon Sep 17 00:00:00 2001 From: Fabian Guerra Soto Date: Tue, 23 May 2017 12:02:09 -0400 Subject: [PATCH 33/62] Cherry-pick arabic text to release branch v3.6.0 (#9071) * [core] Throttle tiles to redo symbol placement at most once every 300ms. Fixes issue #8435 and prepares for pitch-scaling changes in issue #8967. * [core] Disable letter-spacing for Arabic labels (issue #9057) --- cmake/core-files.cmake | 2 ++ src/mbgl/layout/symbol_layout.cpp | 2 +- src/mbgl/tile/geometry_tile.cpp | 12 +++++++++-- src/mbgl/tile/geometry_tile.hpp | 5 +++++ src/mbgl/util/i18n.cpp | 19 +++++++++++----- src/mbgl/util/i18n.hpp | 4 ++++ src/mbgl/util/throttler.cpp | 36 +++++++++++++++++++++++++++++++ src/mbgl/util/throttler.hpp | 22 +++++++++++++++++++ 8 files changed, 94 insertions(+), 8 deletions(-) create mode 100644 src/mbgl/util/throttler.cpp create mode 100644 src/mbgl/util/throttler.hpp diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake index e9d7442e5d7..4779abba865 100644 --- a/cmake/core-files.cmake +++ b/cmake/core-files.cmake @@ -583,6 +583,8 @@ set(MBGL_CORE_FILES src/mbgl/util/thread_context.cpp src/mbgl/util/thread_context.hpp src/mbgl/util/thread_local.hpp + src/mbgl/util/throttler.cpp + src/mbgl/util/throttler.hpp src/mbgl/util/tile_coordinate.hpp src/mbgl/util/tile_cover.cpp src/mbgl/util/tile_cover.hpp diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp index aa91eb1688e..2accac281bd 100644 --- a/src/mbgl/layout/symbol_layout.cpp +++ b/src/mbgl/layout/symbol_layout.cpp @@ -237,7 +237,7 @@ void SymbolLayout::prepare(const GlyphPositionMap& glyphs, const IconMap& icons) /* horizontalAlign */ horizontalAlign, /* verticalAlign */ verticalAlign, /* justify */ justify, - /* spacing: ems */ layout.get() * oneEm, + /* spacing: ems */ util::i18n::allowsLetterSpacing(*feature.text) ? layout.get() * oneEm : 0.0f, /* translate */ Point(layout.evaluate(zoom, feature)[0] * oneEm, layout.evaluate(zoom, feature)[1] * oneEm), /* verticalHeight */ oneEm, /* writingMode */ writingMode, diff --git a/src/mbgl/tile/geometry_tile.cpp b/src/mbgl/tile/geometry_tile.cpp index a8eda382840..29ba7d42cd2 100644 --- a/src/mbgl/tile/geometry_tile.cpp +++ b/src/mbgl/tile/geometry_tile.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -41,7 +42,8 @@ GeometryTile::GeometryTile(const OverscaledTileID& id_, obsolete, parameters.mode), glyphAtlas(glyphAtlas_), - spriteAtlas(spriteAtlas_) { + spriteAtlas(spriteAtlas_), + placementThrottler(Milliseconds(300), [this] { invokePlacement(); }) { } GeometryTile::~GeometryTile() { @@ -81,7 +83,13 @@ void GeometryTile::setPlacementConfig(const PlacementConfig& desiredConfig) { ++correlationID; requestedConfig = desiredConfig; - worker.invoke(&GeometryTileWorker::setPlacementConfig, desiredConfig, correlationID); + placementThrottler.invoke(); +} + +void GeometryTile::invokePlacement() { + if (requestedConfig) { + worker.invoke(&GeometryTileWorker::setPlacementConfig, *requestedConfig, correlationID); + } } void GeometryTile::redoLayout() { diff --git a/src/mbgl/tile/geometry_tile.hpp b/src/mbgl/tile/geometry_tile.hpp index 594952d329d..ed5d8d87bf3 100644 --- a/src/mbgl/tile/geometry_tile.hpp +++ b/src/mbgl/tile/geometry_tile.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -87,6 +88,8 @@ class GeometryTile : public Tile, public GlyphRequestor, IconRequestor { } private: + void invokePlacement(); + const std::string sourceID; style::Style& style; @@ -108,6 +111,8 @@ class GeometryTile : public Tile, public GlyphRequestor, IconRequestor { std::unordered_map> symbolBuckets; std::unique_ptr collisionTile; + + util::Throttler placementThrottler; }; } // namespace mbgl diff --git a/src/mbgl/util/i18n.cpp b/src/mbgl/util/i18n.cpp index 8e56877a64d..6cfdc697e39 100644 --- a/src/mbgl/util/i18n.cpp +++ b/src/mbgl/util/i18n.cpp @@ -1,5 +1,6 @@ #include "i18n.hpp" +#include #include namespace { @@ -29,14 +30,14 @@ DEFINE_IS_IN_UNICODE_BLOCK(Latin1Supplement, 0x0080, 0x00FF) // DEFINE_IS_IN_UNICODE_BLOCK(CyrillicSupplement, 0x0500, 0x052F) // DEFINE_IS_IN_UNICODE_BLOCK(Armenian, 0x0530, 0x058F) // DEFINE_IS_IN_UNICODE_BLOCK(Hebrew, 0x0590, 0x05FF) -// DEFINE_IS_IN_UNICODE_BLOCK(Arabic, 0x0600, 0x06FF) +DEFINE_IS_IN_UNICODE_BLOCK(Arabic, 0x0600, 0x06FF) // DEFINE_IS_IN_UNICODE_BLOCK(Syriac, 0x0700, 0x074F) -// DEFINE_IS_IN_UNICODE_BLOCK(ArabicSupplement, 0x0750, 0x077F) +DEFINE_IS_IN_UNICODE_BLOCK(ArabicSupplement, 0x0750, 0x077F) // DEFINE_IS_IN_UNICODE_BLOCK(Thaana, 0x0780, 0x07BF) // DEFINE_IS_IN_UNICODE_BLOCK(NKo, 0x07C0, 0x07FF) // DEFINE_IS_IN_UNICODE_BLOCK(Samaritan, 0x0800, 0x083F) // DEFINE_IS_IN_UNICODE_BLOCK(Mandaic, 0x0840, 0x085F) -// DEFINE_IS_IN_UNICODE_BLOCK(ArabicExtendedA, 0x08A0, 0x08FF) +DEFINE_IS_IN_UNICODE_BLOCK(ArabicExtendedA, 0x08A0, 0x08FF) // DEFINE_IS_IN_UNICODE_BLOCK(Devanagari, 0x0900, 0x097F) // DEFINE_IS_IN_UNICODE_BLOCK(Bengali, 0x0980, 0x09FF) // DEFINE_IS_IN_UNICODE_BLOCK(Gurmukhi, 0x0A00, 0x0A7F) @@ -169,13 +170,13 @@ DEFINE_IS_IN_UNICODE_BLOCK(HangulJamoExtendedB, 0xD7B0, 0xD7FF) DEFINE_IS_IN_UNICODE_BLOCK(PrivateUseArea, 0xE000, 0xF8FF) DEFINE_IS_IN_UNICODE_BLOCK(CJKCompatibilityIdeographs, 0xF900, 0xFAFF) // DEFINE_IS_IN_UNICODE_BLOCK(AlphabeticPresentationForms, 0xFB00, 0xFB4F) -// DEFINE_IS_IN_UNICODE_BLOCK(ArabicPresentationFormsA, 0xFB50, 0xFDFF) +DEFINE_IS_IN_UNICODE_BLOCK(ArabicPresentationFormsA, 0xFB50, 0xFDFF) // DEFINE_IS_IN_UNICODE_BLOCK(VariationSelectors, 0xFE00, 0xFE0F) DEFINE_IS_IN_UNICODE_BLOCK(VerticalForms, 0xFE10, 0xFE1F) // DEFINE_IS_IN_UNICODE_BLOCK(CombiningHalfMarks, 0xFE20, 0xFE2F) DEFINE_IS_IN_UNICODE_BLOCK(CJKCompatibilityForms, 0xFE30, 0xFE4F) DEFINE_IS_IN_UNICODE_BLOCK(SmallFormVariants, 0xFE50, 0xFE6F) -// DEFINE_IS_IN_UNICODE_BLOCK(ArabicPresentationFormsB, 0xFE70, 0xFEFF) +DEFINE_IS_IN_UNICODE_BLOCK(ArabicPresentationFormsB, 0xFE70, 0xFEFF) DEFINE_IS_IN_UNICODE_BLOCK(HalfwidthandFullwidthForms, 0xFF00, 0xFFEF) // DEFINE_IS_IN_UNICODE_BLOCK(Specials, 0xFFF0, 0xFFFF) // DEFINE_IS_IN_UNICODE_BLOCK(LinearBSyllabary, 0x10000, 0x1007F) @@ -332,6 +333,14 @@ bool allowsWordBreaking(char16_t chr) { || chr == 0x2013 /* en dash */); } +bool charAllowsLetterSpacing(char16_t chr) { + return !(isInArabic(chr) || isInArabicSupplement(chr) || isInArabicExtendedA(chr) || isInArabicPresentationFormsA(chr) || isInArabicPresentationFormsB(chr)); +} + +bool allowsLetterSpacing(const std::u16string& string) { + return std::all_of(string.begin(), string.end(), charAllowsLetterSpacing); +} + bool allowsIdeographicBreaking(const std::u16string& string) { for (char16_t chr : string) { if (!allowsIdeographicBreaking(chr)) { diff --git a/src/mbgl/util/i18n.hpp b/src/mbgl/util/i18n.hpp index 186212f50d2..61c5a1ea962 100644 --- a/src/mbgl/util/i18n.hpp +++ b/src/mbgl/util/i18n.hpp @@ -10,6 +10,10 @@ namespace i18n { by the given Unicode codepoint due to word breaking. */ bool allowsWordBreaking(char16_t chr); +/** Returns whether the given string can be displayed with letter-spacing. + False for Arabic scripts, where letter-spacing will break ligatures. */ +bool allowsLetterSpacing(const std::u16string& string); + /** Returns whether a line break can be inserted after any character in the given string. If false, line breaking should occur on word boundaries instead. */ diff --git a/src/mbgl/util/throttler.cpp b/src/mbgl/util/throttler.cpp new file mode 100644 index 00000000000..910810ce2fd --- /dev/null +++ b/src/mbgl/util/throttler.cpp @@ -0,0 +1,36 @@ +#include + +namespace mbgl { +namespace util { + +Throttler::Throttler(Duration frequency_, std::function&& function_) + : frequency(frequency_) + , function(std::move(function_)) + , pendingInvocation(false) + , lastInvocation(TimePoint::min()) +{} + +void Throttler::invoke() { + if (pendingInvocation) { + return; + } + + Duration timeToNextInvocation = lastInvocation == TimePoint::min() + ? Duration::zero() + : (lastInvocation + frequency) - Clock::now(); + + if (timeToNextInvocation <= Duration::zero()) { + lastInvocation = Clock::now(); + function(); + } else { + pendingInvocation = true; + timer.start(timeToNextInvocation, Duration::zero(), [this]{ + pendingInvocation = false; + lastInvocation = Clock::now(); + function(); + }); + } +} + +} // namespace util +} // namespace mbgl diff --git a/src/mbgl/util/throttler.hpp b/src/mbgl/util/throttler.hpp new file mode 100644 index 00000000000..175de7ccaf7 --- /dev/null +++ b/src/mbgl/util/throttler.hpp @@ -0,0 +1,22 @@ +#include +#include + +namespace mbgl { +namespace util { + +class Throttler { +public: + Throttler(Duration frequency, std::function&& function); + + void invoke(); +private: + Duration frequency; + std::function function; + + Timer timer; + bool pendingInvocation; + TimePoint lastInvocation; +}; + +} // namespace util +} // namespace mbgl From a57e9bc41093eb8f028dcab3593c3921d887bdc0 Mon Sep 17 00:00:00 2001 From: Fabian Guerra Soto Date: Tue, 23 May 2017 12:32:18 -0400 Subject: [PATCH 34/62] [ios,macos] multipolygon coordinate (#8713) * [ios] MGLMultiPolygon's coordinate property implemented * [ios,macos] Add polylabalel to project config * [ios,macos] Change coordinate property for MGLPolyline,MGLPolygon,MGLMultiPolygon * [macos] Change project configuration to support polilabel * [ios,macos] Add MGLLocationCoordinate2DFromPoint * [ios, macos] Update changelogs * [ios, macos] remove unnecesary variables * [ios, macos] Add radians conversions * [ios, macos] Add coordinate calc to MGLPolyline * [ios, macos] Move radian fuctions to MGLGeometry_Private.h * [ios, macos] Fix code style * [ios, macos] Fix code indentation * [ios, macos] Fix radian per meters constant * [ios, macos] Add test scenarios to testPolyline * [ios, macos] Fix test accuracy --- platform/darwin/src/MGLGeometry_Private.h | 64 ++++++++++ platform/darwin/src/MGLPolygon.mm | 20 ++- platform/darwin/src/MGLPolyline.mm | 65 ++++++++++ platform/darwin/test/MGLCodingTests.m | 119 +++++++++++++++++- platform/ios/CHANGELOG.md | 1 + platform/ios/ios.xcodeproj/project.pbxproj | 30 ++++- platform/macos/CHANGELOG.md | 1 + .../macos/macos.xcodeproj/project.pbxproj | 20 ++- scripts/config.xcconfig.in | 1 + 9 files changed, 307 insertions(+), 14 deletions(-) diff --git a/platform/darwin/src/MGLGeometry_Private.h b/platform/darwin/src/MGLGeometry_Private.h index 3e029e8ee05..7ad8314a792 100644 --- a/platform/darwin/src/MGLGeometry_Private.h +++ b/platform/darwin/src/MGLGeometry_Private.h @@ -8,6 +8,26 @@ #import #import +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 MGLPointFromLocationCoordinate2D(CLLocationCoordin return mbgl::Point(coordinate.longitude, coordinate.latitude); } +NS_INLINE CLLocationCoordinate2D MGLLocationCoordinate2DFromPoint(mbgl::Point 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/MGLPolygon.mm b/platform/darwin/src/MGLPolygon.mm index ceafe873bf5..d966ff13ce4 100644 --- a/platform/darwin/src/MGLPolygon.mm +++ b/platform/darwin/src/MGLPolygon.mm @@ -6,6 +6,7 @@ #import "MGLPolygon+MGLAdditions.h" #import +#import @implementation MGLPolygon @@ -54,6 +55,13 @@ - (NSUInteger)hash { return [super hash] + [[self geoJSONDictionary] hash]; } +- (CLLocationCoordinate2D)coordinate { + // pole of inaccessibility + auto poi = mapbox::polylabel([self polygon]); + + return MGLLocationCoordinate2DFromPoint(poi); +} + - (mbgl::LinearRing)ring { NSUInteger count = self.pointCount; CLLocationCoordinate2D *coordinates = self.coordinates; @@ -155,11 +163,17 @@ - (NSUInteger)hash { return hash; } +- (CLLocationCoordinate2D)coordinate { + MGLPolygon *firstPolygon = self.polygons.firstObject; + + return firstPolygon.coordinate; +} + - (BOOL)intersectsOverlayBounds:(MGLCoordinateBounds)overlayBounds { return MGLCoordinateBoundsIntersectsCoordinateBounds(_overlayBounds, overlayBounds); } -- (mbgl::Geometry)geometryObject { +- (mbgl::MultiPolygon)multiPolygon { mbgl::MultiPolygon multiPolygon; multiPolygon.reserve(self.polygons.count); for (MGLPolygon *polygon in self.polygons) { @@ -173,6 +187,10 @@ - (BOOL)intersectsOverlayBounds:(MGLCoordinateBounds)overlayBounds { return multiPolygon; } +- (mbgl::Geometry)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 ae4fbe61de3..fd75dc27950 100644 --- a/platform/darwin/src/MGLPolyline.mm +++ b/platform/darwin/src/MGLPolyline.mm @@ -6,6 +6,7 @@ #import "MGLPolyline+MGLAdditions.h" #import +#import @implementation MGLPolyline @@ -52,6 +53,61 @@ - (BOOL)isEqual:(id)other { 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 @@ - (NSUInteger)hash { 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/test/MGLCodingTests.m b/platform/darwin/test/MGLCodingTests.m index ff0d674ad17..ac61672b760 100644 --- a/platform/darwin/test/MGLCodingTests.m +++ b/platform/darwin/test/MGLCodingTests.m @@ -66,12 +66,54 @@ - (void)testPolyline { [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 @@ - (void)testPolygon { [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 @@ - (void)testPointCollection { 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 @@ - (void)testMultiPolyline { 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 @@ - (void)testMultiPolygon { 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 @@ - (void)testMultiPolygon { 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/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index ca9d22f2305..15e3c7a081e 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -32,6 +32,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT ### Other changes * Xcode 8.0 or higher is now recommended for using this SDK. ([#8775](https://github.com/mapbox/mapbox-gl-native/pull/8775)) +* Fixed a crash when calling `MGLMultiPolygon.coordinate` [#8713](https://github.com/mapbox/mapbox-gl-native/pull/8713) * 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 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 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)) diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index 27582c1acfb..ef57eac1406 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -2703,7 +2703,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"; @@ -2727,7 +2730,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"; @@ -2755,7 +2761,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; @@ -2789,7 +2798,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; @@ -2840,7 +2852,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)", @@ -2866,7 +2881,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/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index ba09002d142..a1515e9d7a0 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -17,6 +17,7 @@ ### Other changes * Xcode 8.0 or higher is now recommended for using this SDK. ([#8775](https://github.com/mapbox/mapbox-gl-native/pull/8775)) +* Fixed a crash when calling `MGLMultiPolygon.coordinate` [#8713](https://github.com/mapbox/mapbox-gl-native/pull/8713) * 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 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)) diff --git a/platform/macos/macos.xcodeproj/project.pbxproj b/platform/macos/macos.xcodeproj/project.pbxproj index 11275240981..564d81afb2c 100644 --- a/platform/macos/macos.xcodeproj/project.pbxproj +++ b/platform/macos/macos.xcodeproj/project.pbxproj @@ -1779,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"; @@ -1813,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"; @@ -1837,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 = ( @@ -1866,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/scripts/config.xcconfig.in b/scripts/config.xcconfig.in index eb6bc71b890..b70ff9d6771 100644 --- a/scripts/config.xcconfig.in +++ b/scripts/config.xcconfig.in @@ -3,3 +3,4 @@ // mbgl-core mbgl_core_INCLUDE_DIRECTORIES = "@mbgl_core_INCLUDE_DIRECTORIES@" mbgl_core_LINK_LIBRARIES = "@mbgl_core_LINK_LIBRARIES@" +polylabel_INCLUDE_DIRECTORIES = "@MASON_PACKAGE_polylabel_INCLUDE_DIRS@" From c9de6cdb6c8b4a640c10940adbd76ed508900022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguye=CC=82=CC=83n?= Date: Sat, 20 May 2017 19:44:52 -0700 Subject: [PATCH 35/62] [ios, macos] More robust Streets localization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added Arabic, Portuguese, and Simplified Chinese to the list of languages with specialized fields in the Mapbox Streets source. Rely on NSBundle to select the most appropriate locale based on the user’s preferred languages. --- platform/ios/app/MBXViewController.m | 21 ++++++++++--------- .../macos/app/MGLVectorSource+MBXAdditions.h | 2 +- .../macos/app/MGLVectorSource+MBXAdditions.m | 18 +++++++++------- platform/macos/app/MapDocument.m | 5 ++--- 4 files changed, 25 insertions(+), 21 deletions(-) diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m index 209998adb9c..d3927374a7a 100644 --- a/platform/ios/app/MBXViewController.m +++ b/platform/ios/app/MBXViewController.m @@ -1397,19 +1397,20 @@ - (void)styleLabelLanguageForLayersNamed:(NSArray *)layers - (NSString *)bestLanguageForUser { - NSArray *supportedLanguages = @[ @"en", @"es", @"fr", @"de", @"ru", @"zh" ]; - NSArray *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 *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/macos/app/MGLVectorSource+MBXAdditions.h b/platform/macos/app/MGLVectorSource+MBXAdditions.h index 312081ec515..1e25ee5a601 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 644b43a6512..323bc74366c 100644 --- a/platform/macos/app/MGLVectorSource+MBXAdditions.m +++ b/platform/macos/app/MGLVectorSource+MBXAdditions.m @@ -7,19 +7,23 @@ @implementation MGLVectorSource (MBXAdditions) 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 *supportedLanguages = [MGLVectorSource mapboxStreetsLanguages].allObjects; + NSArray *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 59c1817f638..a30e7626452 100644 --- a/platform/macos/app/MapDocument.m +++ b/platform/macos/app/MapDocument.m @@ -345,7 +345,7 @@ - (IBAction)setLabelLanguage:(NSMenuItem *)sender { - (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]]) { @@ -855,8 +855,7 @@ - (BOOL)validateMenuItem:(NSMenuItem *)menuItem { 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; } From 9b11bb98fa855da9d845ed01e59451943bb29af4 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Tue, 23 May 2017 13:24:16 -0400 Subject: [PATCH 36/62] [ios] Fallback to Mapbox.bundle as the framework bundle (#9074) Fixes an issue where localizations could not be found when using static builds. Throws exception if our bundle can't be found. --- platform/darwin/src/NSBundle+MGLAdditions.h | 4 --- platform/darwin/src/NSBundle+MGLAdditions.m | 28 +++++++++------------ platform/ios/CHANGELOG.md | 1 + platform/ios/src/MGLAPIClient.m | 2 +- platform/ios/src/MGLMapView.mm | 3 +-- 5 files changed, 15 insertions(+), 23 deletions(-) diff --git a/platform/darwin/src/NSBundle+MGLAdditions.h b/platform/darwin/src/NSBundle+MGLAdditions.h index 1fc9e8b8963..df70d239231 100644 --- a/platform/darwin/src/NSBundle+MGLAdditions.h +++ b/platform/darwin/src/NSBundle+MGLAdditions.h @@ -36,10 +36,6 @@ 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; - @end NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/NSBundle+MGLAdditions.m b/platform/darwin/src/NSBundle+MGLAdditions.m index 76d9cc0db78..ad122831ba9 100644 --- a/platform/darwin/src/NSBundle+MGLAdditions.m +++ b/platform/darwin/src/NSBundle+MGLAdditions.m @@ -6,12 +6,19 @@ @implementation NSBundle (MGLAdditions) + (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,7 @@ + (nullable NSString *)mgl_frameworkBundleIdentifier { + (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; - } -} - -- (NSString *)mgl_resourcesDirectory { - return [self pathForResource:@"Mapbox" ofType:@"bundle"] ? @"Mapbox.bundle" : nil; + return bundle.infoDictionary; } @end diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 15e3c7a081e..b5a0e7dd7d3 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -38,6 +38,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * The error passed into `-[MGLMapViewDelegate mapViewDidFailLoadingMap:withError:]` now includes a more specific description and failure reason. ([#8418](https://github.com/mapbox/mapbox-gl-native/pull/8418)) * Fixed an issue rendering polylines that contain duplicate vertices. ([#8808](https://github.com/mapbox/mapbox-gl-native/pull/8808)) * Fixed a bug which caused the compass and other ornaments to underlap navigation and tab bars. ([#7716](https://github.com/mapbox/mapbox-gl-native/pull/7716)) +* Fixed an issue in the static framework where localizations would never load. ([#9074](https://github.com/mapbox/mapbox-gl-native/pull/9074)) ## 3.5.4 - May 9, 2017 diff --git a/platform/ios/src/MGLAPIClient.m b/platform/ios/src/MGLAPIClient.m index 22ee5c55f5d..124d4361975 100644 --- a/platform/ios/src/MGLAPIClient.m +++ b/platform/ios/src/MGLAPIClient.m @@ -117,7 +117,7 @@ - (void)loadCertificates { - (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/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 720c1d506a6..9a27cf24c66 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -5293,8 +5293,7 @@ + (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]; + ofType:extension]; if ( ! path) { [NSException raise:@"Resource not found" format: From d90c1722c9718e2532d406c13b17edf988070e2e Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Fri, 19 May 2017 13:08:21 -0400 Subject: [PATCH 37/62] [ios] Move image resources to an asset catalog & switch to PDFs --- platform/ios/ios.xcodeproj/project.pbxproj | 60 ++---------------- platform/ios/originals/compass.sketch | Bin 0 -> 4881 bytes platform/ios/resources/Compass.png | Bin 1139 -> 0 bytes platform/ios/resources/Compass@2x.png | Bin 1892 -> 0 bytes platform/ios/resources/Compass@3x.png | Bin 3398 -> 0 bytes .../Compass.imageset/Contents.json | 12 ++++ .../Compass.imageset/compass.pdf | Bin 0 -> 4574 bytes .../resources/Images.xcassets/Contents.json | 6 ++ .../default_marker.imageset/Contents.json | 12 ++++ .../default_marker.pdf | Bin 0 -> 2601 bytes .../mapbox.imageset/Contents.json | 12 ++++ .../mapbox.imageset/mapbox.pdf | Bin 0 -> 8823 bytes platform/ios/resources/default_marker.png | Bin 1010 -> 0 bytes platform/ios/resources/default_marker@2x.png | Bin 1995 -> 0 bytes platform/ios/resources/default_marker@3x.png | Bin 2998 -> 0 bytes platform/ios/resources/mapbox.png | Bin 2347 -> 0 bytes platform/ios/resources/mapbox@2x.png | Bin 5290 -> 0 bytes platform/ios/resources/mapbox@3x.png | Bin 8245 -> 0 bytes platform/ios/src/MGLMapView.mm | 26 ++++---- 19 files changed, 61 insertions(+), 67 deletions(-) create mode 100644 platform/ios/originals/compass.sketch delete mode 100644 platform/ios/resources/Compass.png delete mode 100644 platform/ios/resources/Compass@2x.png delete mode 100644 platform/ios/resources/Compass@3x.png create mode 100644 platform/ios/resources/Images.xcassets/Compass.imageset/Contents.json create mode 100644 platform/ios/resources/Images.xcassets/Compass.imageset/compass.pdf create mode 100644 platform/ios/resources/Images.xcassets/Contents.json create mode 100644 platform/ios/resources/Images.xcassets/default_marker.imageset/Contents.json create mode 100644 platform/ios/resources/Images.xcassets/default_marker.imageset/default_marker.pdf create mode 100644 platform/ios/resources/Images.xcassets/mapbox.imageset/Contents.json create mode 100644 platform/ios/resources/Images.xcassets/mapbox.imageset/mapbox.pdf delete mode 100644 platform/ios/resources/default_marker.png delete mode 100644 platform/ios/resources/default_marker@2x.png delete mode 100644 platform/ios/resources/default_marker@3x.png delete mode 100644 platform/ios/resources/mapbox.png delete mode 100644 platform/ios/resources/mapbox@2x.png delete mode 100644 platform/ios/resources/mapbox@3x.png diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index ef57eac1406..6ff148a18eb 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -200,6 +200,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 */; }; @@ -333,15 +335,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 */; }; @@ -355,15 +348,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 */; }; @@ -679,6 +663,7 @@ 7E016D821D9E890300A29A21 /* MGLPolygon+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MGLPolygon+MGLAdditions.h"; sourceTree = ""; }; 7E016D831D9E890300A29A21 /* MGLPolygon+MGLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MGLPolygon+MGLAdditions.m"; sourceTree = ""; }; 920A3E5C1E6F995200C16EFC /* MGLSourceQueryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLSourceQueryTests.m; path = ../../darwin/test/MGLSourceQueryTests.m; sourceTree = ""; }; + 960D0C351ECF5AAF008E151F /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 9620BB361E69FE1700705A1D /* MGLSDKUpdateChecker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLSDKUpdateChecker.h; sourceTree = ""; }; 9620BB371E69FE1700705A1D /* MGLSDKUpdateChecker.mm */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = MGLSDKUpdateChecker.mm; sourceTree = ""; }; 9660916B1E5BBFD700A9A03B /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = ""; }; @@ -847,15 +832,6 @@ DA88484D1CBAFB9800AB86E3 /* MGLFaux3DUserLocationAnnotationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLFaux3DUserLocationAnnotationView.h; sourceTree = ""; }; DA88484E1CBAFB9800AB86E3 /* MGLFaux3DUserLocationAnnotationView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLFaux3DUserLocationAnnotationView.m; sourceTree = ""; }; 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 = ""; }; - DA8848641CBAFCC100AB86E3 /* Compass@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Compass@2x.png"; sourceTree = ""; }; - DA8848651CBAFCC100AB86E3 /* Compass@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Compass@3x.png"; sourceTree = ""; }; - DA8848661CBAFCC100AB86E3 /* default_marker.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = default_marker.png; sourceTree = ""; }; - DA8848671CBAFCC100AB86E3 /* default_marker@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "default_marker@2x.png"; sourceTree = ""; }; - DA8848681CBAFCC100AB86E3 /* default_marker@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "default_marker@3x.png"; sourceTree = ""; }; - DA8848691CBAFCC100AB86E3 /* mapbox.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = mapbox.png; sourceTree = ""; }; - DA88486A1CBAFCC100AB86E3 /* mapbox@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "mapbox@2x.png"; sourceTree = ""; }; - DA88486B1CBAFCC100AB86E3 /* mapbox@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "mapbox@3x.png"; sourceTree = ""; }; DA8848801CBB033F00AB86E3 /* FABAttributes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FABAttributes.h; sourceTree = ""; }; DA8848811CBB033F00AB86E3 /* FABKitProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FABKitProtocol.h; sourceTree = ""; }; DA8848821CBB033F00AB86E3 /* Fabric+FABKits.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Fabric+FABKits.h"; sourceTree = ""; }; @@ -1363,17 +1339,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 */, @@ -2092,18 +2060,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 */, @@ -2117,16 +2077,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 */, diff --git a/platform/ios/originals/compass.sketch b/platform/ios/originals/compass.sketch new file mode 100644 index 0000000000000000000000000000000000000000..f5b848cd968b95ba8fef65f3c414cbb23c490068 GIT binary patch literal 4881 zcmb7IXH-+$whj=gAiYUPiXad=C>Vs$Lhr@Uqy>;F1f*A`d*~3Vp*Lv?(n*jGDkxPD zq)0E)2}NE!=e&2{J>%UUZ+&C#wbvNyTV?LC);IUm(U|qnN&WNO=^+ges!kW{XiiCnojt>jcw9svg=3z4az0_q|M0+l-97M{$$HVI*8LYl zEd<l6#z9{kFR@r4( z-==f(vtsCdSU+I~>pvLs4S@W*V28eR?ccD^ZbmAjsCmQ%*x6Fng5-gxFo_PnNKtEaRGlxa*GRE5;e5w@bGC4E`Z}A@acQJAXwr3tz-U=1slOyfQ96sUu=hL9ZrvpKc{n0Ch4g-91zUs zdQx1(bvr}A540sG+c+E{arnhS3RWBU?#EQkB*&0>{UD(=GKlH;YJyF1wu?Wc{RaJ_WOU(Ch%fAjvT_~P&Ib7Zh!b3otC}vqg;F1cMUERa|eu=sf zY>)Aq%K0Z_UOD)VqA;z)CJtTrRt@CSW?|MlHAE5n2KhHD#-@$eY)I{sC3GJl=_6ul zDhh+!yN2P_H`oU`4OopFV@Y(Q>n!2NVH2><&B;miCr?%u_ca0yz4Iy_WFa50TKVPe z+_x^n7M}Gd5@iLb^6C-{rA2bhUxZ2aF%R+hO4mvL@OemdM$+eJI=S3*%~5uRc_`E7 z_(1z}c|~Qxn%)`fr zJeN1~33_DwhP&Xp>CUD$DbU1H3yp4?vXm6ukv=H=QV(3N%z#Ck8@>9R6J$`6*_5@) z=+IaL13637AZMRhTGQ3$Bs8^c`s@3tz)EK4^h`&miaccXHE48@OGNKDLJn@Uu)677 zG-mriY%X>zC+JaW7z-T(#a3>6!(y-4*FP!rWSq5U@+y5IlwqErNi5#C#Ri%6gR<6l zDmPt%h`+%Umv&nhren@Be7jIpKP_k379gj4(o3g^Yj@MD@?F!NU%whq$Ian{&G6;m zZ-{+*K6_T3%D!bz%FCf>1jHK-(fY!-t)jrfyo}$S z8n6|gr~+Bnm%3Ga_-016Eb+QNY9~}jwF*q`kJj5>71*Y|(R*ueV3rT;>7(XJWx~ZT zKT?L^qXIgyUgkHad6&h|>Q0W)@&+srJ@Zy9;Zp{r&|~tvX^}S0q9Yr1n20Ad1Wo-+ zb!;z4S8U23u$t9&ALb<=S1PqZwyq-T6(-{6j6qMWI|keWlG`>_cIlOqXU~)88abdW zoKmhtnKNUr1WZ1F(1w_)%24`(d7rJ9iG?hV+Y@USbtC1Uva}x`Xi%tFe5Es2lSqrx zqIGsEExsi#Bmm`sTX@FO>yO8{`-^j#J<(`R$#sHYJ-^4^xp*A3r2gqQAOS>?hPGmA zVPt9RTJt(h>clPEj;3WJzu!p)}Uam4I_*~L&}4d(Zv^z9{OI^}d|uOd&JIlPW7E$~v#l_U# z^iMH36n#&9mT^m1iiQSqG?<|D)$FA#bq`CG_}+kb>sDth7!e`B80C~TNeeE{*;VdI z|I?pEqpA1cVLS|^EaE-sy84w!HJ%UZj**fx-a%+u$0Pf_jU}!{!~T*#XIP$-Ei{m> z3K_sU>Jsv@HV6T;+ETS1yH_|q9zE+Jm9d~9S9k%f@E`|AOZt_`iUj|?mlsaXZmY?L zlic&wp^t2Vg-X!73HamW=bdfpY2(=v+KC2cmWzj9Kd&XNLzjo(#qi=auXW@-=O7Jg zUjy!&6258TE=iej=BTi|&lCYaB$9!)TD`H8Og{yPLKpI$((g0l;Gf2#W^n(pS6l=m zU4(9iGz@Vsinqz?g`+01^m?5X#JEvh$>oy2x^gjtB_7^>2mk)odQf;oTe|a3&%0 z2MGXW1v$53mDBt^JI0~4`MWt(HEm`=l-d`GUU(^ zV?K1KSA@HiypHGH6A8I^7fIum_L8tcngOvAB{mfE^?W6Gb$pDp`{rH1Ix7hA93?Dv}89DE-Cx#qkiP0b!ry64w%_TMsA zjc0e`UN>h82)g(5ekhVq$w}jr;DlVa4Tgz|lPn#~=X!VM1}c5l`LeE`=qVC2Kx#tv zkiqE2eNS$L2-BDg72zU{XD?8LYUD89YR*afr&z{f9c}bJ1eN``4{J`HujI?OZKZpg z$$7%b7a2HZwTru)>%P>8RhqZg)!m8Sq;(;iMBH;Td_9M7%2rNuk|2Ji6^QxetWeDyKd<>KCRkfeN0-%)HdUu-5DA% z5BBxC7OwtzZl{qHSro#Q_ih-z?zJ)ek&~_xgPASA@$*t<;GFO;R{{S14FRIPex3sX zfMcA|;auhU)WO%;!Ot7?_lt<9I~?yANOlpcr=vzec8d(xhXSguY=Eop{5BFI+%w@< zvm36$^EOaZ0@QqD-2ec9wNPb6BQw$uX7@Dh6{&WaDUI6E1eAK@^7xiM`#r}*1ig2u z#SBEne#*#ZGsT2wXT>r`sJ$w8;*K{h{O%#D7aGQWOWiHB!{@Ljq<>w+a8zOsVH$ee zu~}6K>3QB;*P}MPgT*4**x4I~hc!>m&Ia=H^Of3akI?^0N5&7$GPk;>(+sovndHB5UrW z0Suy7G5nb5XyVG(uMtv$5AI1yDjOOacKU>qx`IgCv=g?ro|u@L_V4cQYPl>gFT2{? z_ZY36;3v$?n5UA7>Ql1R6kbH^?6_%YY55$f`ua-e<>!C>>cegUgB4X)3g&?M`CQQ% zI;_!Wo0}S%H(%X>!C*#KR_@}DEG@O!*w{*2b8}hm%gIShK*_O`YUt9`X%btjmDN>2 zba*(P1{A8lNAtF`6F25p*W(7uUy(khrkUCxFgX83bhPmU*ZUu!w2IY2TM_FU8`SSN zlnqiy&MBWVnSe9t6>ZQ>2t*EvEmn^CP_yBIM511JbY;bUb7yB_DJ;@U!W39P%n}{}e(!y>Rd&b9>ZqHDQ6P?! z@486#fAPvn~KCMbCdbQ;n6!gJ7R;->LoLF z+j{k+m7Z*Moe0i`S~J=~D3!9yI!_i_e&ngkdB*+khu<;OnqJ+4((DL}oP&b{r+|QL zcX#*l`g*ZVZB{dPLT`@bqsz7OArK;0!2ybwnO?ciV;R0Fqdk){VImW-L@pVI!fi{&wqBdAqSd5H}j(%{hm1QvL zSiUTxz$%UkI3Emg74GMX^zUnBKm45SDK{$au#<}jwPPOA!*EyQ7rb}a?QKO$C+JSU5s4Wug4593R!HdtfHN@ zj~->AUr@Y@zay;pN=ioN&Fn0!kH3~Qm*2qE*?ACmyc$|tht8xO4ePx#9BD*PQ~&?~0ssI2000006951JC;$Ke6951J5&!@KZy%7+@c;k2#>UbQ4}6cZE$QA7HwGk=Nh4=} zDi#Ij@xl8b`zT#|;)Z}~wc2mD+c$PSOjM`SxdCruJzn@@84ZWxh9L56Hhbo}?j)DX zUFiF^DE7gD*Kyh!{>Z}kYW;60?5AqAT5i2wA7}#t;@JmhV=O-A-1}4V5bh`<2)f@U#)E%Jn&h>X4v%?i$DKvu1|?!{nY5cS>TJC6?jNFvD+{S};W98`IsD~v`IolGeB(;u zj+zmr)9D`P&}cN8G@H%e)_U+~BHG-6rE3U8zAH48$jxkTep~JU^tn|$u=gfVt z{et71_CLJ+qXZGjsoQ*dUaQpx_BrS6qr*3DbwgB?{L}K%Ko_Le)T8u=V9?Y-_ zI)49Vtc2*1`a}4n2OYj0$tt#}IrN~zH~yf_L~(~HQ?+?SHG`<}qa*Pj00960HmcP1 z0005LNklPZ|FXNYOD^|~v4-gp1U@Y8l^ugAd{|s7I|ftuu;?j^ znDRXDx!dh7-1Xm>cRHO#xm=zlXyL=+Tv=eMSS(HlgTc!8{dK?J_X~wW#H``{bVLtb z^yx#tkQP2HPL$m$0z#|RnwLtYCo@09h<>x#d<$vQA0H81X%=?<*E#)TsA^@Th?%L? zYA=mO<27k7>l*dwr10rW|3qABHDU^(<#M^M*XwD55x!e*=G3E;VvN42IMvRLB51@z zIhjm8TtoSMK9dobhYotq;h`UWx8h8@cM5CF}~TF{5LMKJ?3k3$Y#) zu`9x<;W)eRnNK0iuT&~Ccw0^}`t;cg6xb`KJTTa9x97v*aAoFZbWO1i55D1qMs)W! zq?!O+TJCw|iaEC+*h?~%#M|g)zGZ=ngdZM4PRB?QF}LTOdUSR^)Z%1DRZL!R_RX(6 zi%Iy@QGYP-Dc~E`Q}9*i@5Wg1xEYzV&OW@@r$cN14_{Fa_(&A-Px#32;bRa{vGqB>(^xB>_oNB=7(L032yVPgDQ^00IC20000003!eZ04M+e03!eZ z03rYY1L?G~6?Oms1GPy+K~#7F?3zDF8&Mp`&DbHaohs@Ou^l=XOBaVuT}*LEW}%~{ zW7h7SN=c@I_=nD2+ytYOE-kL2gQy6)G*Dw86$%yX?_={G=5m*N@9yP{#V`E0KkvQY z`+mRq-6iiX0|WQCRR@M;_E!>f5+5YKNUTbHlh~2iZ*4i|Jni(OKjRqxU*LtPcqg$S zu`Pk{sKI#hkdM4~3PZwnR$^XaS0btj-NuoZ{LI6AeTz|H_Eus=qMIPsZOq5K%-=V7 zAWZngzDh(Ed?ko8zBNBQ;B%{(lG)1=kH#Rn!larIj-t!j6=x}n6>A0!R%*>olPfwp%kTJgGT;+rx zJbm(^Cl>fx>kyRB=TEq{`_&kD!4IB({N-Hauj?rpTn$LN->=3fUR?~~Lq-?+tiVs@ z&|j*GfQyRxm>vEpb9Pjv%{j#<#1J(H`mB}&v!-MqP^;DcMEIIi1*Wi#lP3ZXbfPyDEW@pZ zMIEE61XJQxOGFOn?TR63U6Dx46jde4l(^LrfjfH99f(;9>Btgci&55SHnzO4I3dteff0yzB>6i897@ zwZI=8=s}l<54wFHhVztM8^S&hbc)5|>Dt;_v(ac=E-o(CCnhH1tUYx8pej+OxT+TT z!W;hRKu^GfFZ6d{LC$E>-uk;}+h%(lRwc?97u5n^cxSWOu*VnZZv!uy zwj}~haoHADCCU_6)ndpU{cSJYcmtP+A*NNLjB!yd7TWMZV@~!GF~zh>lqm^U3qEM@ z&bH^K*j9-$Cc$dK2Mu0k_uLfQDp95+SS|RVu_}8#H{}k@fe#w|{o&q}e}4 zMWxsjWAMSjN0nkzjKK#7TPnq-7=sTE-l`OvVhlbwNUC(Im}O(oj}I;!s1|BUI-S1Q z+uOT6K0faD8#irz)oQg1?^G&vF@PKTeb`ou%apmfxn`kIXqC(5Zl}|ED3wY-vf1pV zcic4BS5{Uo;SGOupa)$6oYC*Y8@1FG&V-YbleVW@BTcH7%!%m)m09X{}SQgExhsfe_R z)OeRsV=#sF;Su@PU2`UeWo2d?eyDH>o^YM@SeNyu7K>uWC$_JsD<)wOdCAW@toN+I z!(g70A1F>0qZq_|@{lj|A+!3P6?oCa8@Me6?`(OQeWc*;4`&Jt<4B!$`WeS~=J{S! esV&C8a^N?IQPx#32;bRa{vG#H~;`4H~~eLF<<}y032yVPgDQ^00IC20000005kvq04M+e05kvq z05kvq2Qf$#A~*m52K`AyK~#7F>|AR|q*WMRMN4;`aW)kEA#Ce9Iy&oG6bkJcgi2Z_ z8}&nTf0VfW@`?&VMe{5F6a?M86qLj&VQLXX%Pyg|8*Ba(t=UVt%6KWTovB%GIrcu% z87Aku-{#Yq7hcZH`QCHR`##Tkzw^y^jvhG@lNbUzAYh?B9i%0s)ub0lFYZ&+;TmjU zi#9RQ0f9{F^952D=_S&iNZkq*ZPAB|6n&!an8-xHM)uEACu0KN(EPIJB$q<;0t_;dO1l5yg@omsx{bZ`-4w!lETNRlhYLON2EWK zss&Q-_V5wD!skOT*%Tpy{e@I>U>h15mZ5s|{h!b99Wfj_u~J01UCfbcP7LdFYHI2} zlzrE!L=1=}mGn?ZB}H+KR5xN+-v$xH;y_H#kRDh`AWdRA6@$vC)yWRe)RC_Kv zEeI+rD_ejsT>bOZAx6Y{pacu*Wm3t3j*N`>JRZ+>LPCP%Hv9WmOHff!(flq+dwcup z{=P-NBh~}+5OIN=oe>ErjD)(nIuF39sj2bEZOiP{5;QO{aBqEmJ&>NBew+0vqeRSz z-L#TOo^_LkfiI;lF){Jx($doQ`1rVAianQ&8iH&#+bzG}zcVy6-5WDAGaE|AfA z-LYD&w^*Ij(&cjf4wY)p?8?i_7mz*8&CM&UPNNdo0HcVAg+?7FRYU||yVBCqJ+N9@ zTC@g>*H1PnEEbFGNx>IKNIftDR)z`|LEL9~gI05Gc6N4SZf;J?+YPUu85tS3aa-;0 z@BdSc&mwJsm7xy~Z;Lp@3y73s(S936h>YZdtS^KdAR&zjv2lw>24=vmZCjwkmLIyC#<)*c(mKbYaf@&vQ=PQ{t=C zHYzhGl%(r4rY3J1UX$uPmmMPs@7S={!eygHUG+n$i@RqZ+1-AkFp#5T79j@2q9Sub zNqU`PS0&hg7kp}?>!Aa3eg;W+Lmbl`g6P-pF$;>gGUFH4zoZ{l*d7K@?@gxjJhb-2cnwnNpQc`YGJH=qjZCfTWdl*4G z^xLTLShAsO5q`{$jg9%RTZ=aj6B85u+S*!=$iYnfa0Lgvf)o=48fVBlIyzSAB+Mp= z!XXcA8Rz8W@XZX-KBbJyY{m<+Mb{oi4lBpgq*o*8>-y&$okDLNbq$3Et*p zZ)Ibq;Lye9=4QZ%ZR)2|Q|>a*<&?;xM-9lLii(Oof?8W!R}>yXd5153;^@K+HcRD} zIDDbe`zgU}`~GzH-*Sivwf#CTOZT-%0}Q|cxCoO|R3ivZq_00|RhB)BBxKQJmZS5L zjzhxuoPQ+tqJ)MKCGe_>>|rE*ldpdkX@DcR2oo38M3exb@y}V0DO!6NF{}(w7CmnM zECLr{f`9N}T7s@PvlOpAj2u>eo1gWUnPVm2z(ts-`41kt5_^`i2Q!}mthPj7#pcc; zaPdFE*kWf(pydJ(7@2znvJq5L81R&O*A6*QznQKT6`VqK|-Kj1~d-dWHe6y*%=li~y zoq2b!U3(<&;Sf&Mo}QlF)z#IwnU0N(?WgurpC4gKY|OA@9Wge*i=14DEbVPca8jfQ z^4$b2P9})wzbKM?LlU`2lkZTXwl9BRKrYfGh!RDRKQJH{Y4RFM6hZ#L;EgnPP*AA| z^34RT(jzDjg*O#RVk89ufpB?w`5gfq$pN|0l}}J=Ynv-7D%u_#9E@&lZ5_3>wcWSd zKiAr~x3}LvK0Zz?E-oIlw6y%;Aihu0$538DjU|F=Yioa>nwolWaBy(;5VE?%wYAk~>m&9V zMR%La%gYPT&(FuYySpO=1qHWZC5=Q?W{yc|Y3ZGzp`j?TG{DMqd()VE0w=c&3>XC{Tm=k+T5=1Kny0;h*bIbBHT85yCByTVv*4HhwbKdYPMUZC`lvvxA7trHZs9Htt zVnB?EwIflh$X#+c`M-9=%z3A0apadLp;g4^Q_aoIM)&?gwncq&(x0&~CMSJU`Vd<_ zq!yC*(c7^z3}kY0@&TVblLV4J5Mt)M^OHFK?1UtZ9DaM|b2|wEIi4s^ z{MYNl7#Is<5+sS35?d+K4lB06$whKB3?z~Ck`!i=jC`RvnCY0ygI*tK$l zBe5ffuF5;GZU|ssUtg4t(s8qPxm3qk8FPPse>6i}#Lg4>7^ApGKvh-M4qqL6MKZ_o z;B7E53wXi~1}T7k3w4DgzB1<9+Oo1Tk>m@@zJ)@eqm7M?Be&u?$BLMFD$M8p9%>l~ zD{Tu43vb6_u`?!JSZOb#4U=B@y{4(D$&IHS#E4j3iSYgT4^U|&aUUmGCa;AJ#|j36 ze^TxGQ(RoU?b4(mHW_ma(8g8aEu*xR#3T!3!Z_M)*4EbkrNSRW5{>G#ZB=5P1u=2$ zb{e*ANg-MbUy~Xd8t!rRjE;`(!=7!BMF?UV8K1E+8OF)jDSc+}`O3C6jIArY2!05) z4XoB@c6K(#^duWOWS7PgA&5RKFE7V;cXv+(lVU!CEzfTVFs5aB3oX~7m%z*PnHeuk zOJeR=rnW})q6Bqzc1E~@nYm;%hDkYNX6#uZ&%?_fp_WM+9vY7E~svGHlT&`bHlgd*Ocm zDwIFJFnuA2dKusw+2GjpfxhJAH3o4B3m+~LpwFN;p+;cJD(4s+i(_8-gH8C5RT6-= zyePj84MJz3BL9fOzO)g9(k5+l3?-5g;Q!e0&xT%v-hjRheK$o}#yYlP+qD0_L75VT cfEWV*0b5(%?M3ms7sn}4suV>K1Pus55FsH{rT3;ZQ92}$&}%{wr1vIW1f@3tDGDe8p-X=T zq=PgebVQ1PMC3~>-+RvcX1+UjvYY=sdv<5T9RYKq z_9pc(=yU!6rfuB1w_=)8ddQ%ji9aR%g`D}i9*{3#=_H5_(dX$k*wNB!<)-GQwuX6d zZ7a+qcjs<_0Y_da3oBxH=6Ss7N4#`E{F!CAe@8KX^X27z-Vu?a5ND_Szt%C0DO=gS zTklI#EpUBf^!3xS8c$9xAM6&GC|+Qvty zlLdTDfHJLPty`k`lRA{1@fT#2oky*p_`rVeq*SyHE*CV1EXS0!i;rBA2JH(O?JSh( zU3HIiYFBjW79Yv2`b&8?{7_!JMW8i|{B+2<=rv^Sn?QZGmaNsHWb>%;oG61tWJcaT ze@GkDJDJqcm_B%zRVwD=LWfrULC5I>*QYG6=pPKlwTl&0PNw+|i@{E>Ei`hDe8J$R$^uV~Fqj3&xN&2QeGQNIBr$PHn0X(fn z>}RT|YUkUofpg|_EN$a^F^6V8Q5x)Ks?2j&CibeLn_tcg;UsAMVQHYxlUEOTW}WME zq$aMI)XVuyA_zsy1hlJDik}{W^Pwj$DNvnr@V+H7yxk|LlSHE9runIqf|h9uE&~Sm&>0qr4lE?uy-a=fi+Z z(b&m{hpVIfXWB*cR;BtbRP*mln0`Ce$s75C8sTjHD_aOXVv-Rvi`Ytlp~Q#8uJAwd z@6LT!1W-r^X7l6M9pQ`yLVxC>9s=d!;ckUM0mXg_iZ0G*!gCamn7@Q%y@jy0gDJRp z15LmLL4KV$PhFBmk)`5`1o~FNdB-Ax=mQ7kUa6Ua3&lTKp`cBr=1l-PgUXnA0nBLyzb~v zFh^i9;+TQ6TtJ>ZHXkbB`o_30z(7i8NZK-wL;_R~mB@y_VWiX3pQ&|L?|%{uv~HLZXx0ItMo$6~xx^K@ zPm@q9kBCY6gII86Df-oKS%psky?d#A!r>5M=3J`0=UfXNm5%$xOV=8D9;bm8bJ>jE zu{Mfi#Wv{7Wbn-A#{zOqAM?!F?)JeOY=btiS@N-2Mzf9^ z&xJ6|B>UXPPdKWZQVmFIoKDN(=AUbC0ZDdFk<`pQ-Mtji5pO!8SYkhpcaI(-MY4=x8 zyTH@z7MAU>6$9Os6spiD+lDZyAj1SfYwFN|k@B-HYdaZ=9^ zdsmX-AfExi?O-w2)24uXEoWS5XaiN{sKZ+|!K5=mc9g-N8P4a)mnJY5(5EYmGF&vK zyss#)Bf&)eRxv7pAxZA*0O;8T{*cmKMIGtAD`>WQO0$ro9Et&;$mwHJzfi`wQ0%_(`Ge@em9GBvMJ$1Fqlx{YQ2;%5?pFpIWiO1W-I@P*niChI_CCV8IbOjgR#Hl5^Es~#($b~(=z}(5Y5MdNM55Ef=g=>IE9$P)W ztHrV;hGc*SeGM*zkGv={3TGjo6k({}91$Q>kf`>jyRwv~uh)G+6U0Gq`wxq@zx%+bW6$|yw zJaZm4EPY&`X{?P=^3(7WQcZ@Y?WQgClqZ)ZI;5_r`SNI|jHP*{SqNtHT5twvd@uS| zN2c2<*Qy_sUv)QEWxl3OI!s+TBQF&m3D1B>!9zZZzSZSvObSYReO9|fZnf$?Pa!9p z!cgJ;_jNV5YHmy-Cczx4;syAjaiBa zCdW3!#_x(Oy}>SEr^G7l%ugEmQor&$^FK)CN=2rUrpA|Q8MurvjbxTOm1+-z?Lqc1 z`w4seh+tV9wixS`h0U}OIa&M`u~tY5bhFE|3z}7%O#*sb27r^6G4fAZpXEm9Hp8-D z`SVs+N)yWl`SXqQZ+1#py>g4*mRT?XRYhg0C#lEv+|nN(p1EULkFCMdIxN|OM9M@P zy7H<%7#TfR8O@Pwm+F3yfB(tXYc$d^hB2AU)65nEn*#dR*99&c@fn>mbgEgaI%=Qn z$Z#;T^6Lt>U$ClvS2Vp;TV8T~*0Xuj?`DQs)iiU%?FQ^V-98r{gojW_M|ekI=nlrw zo>v<}{YDtqSLW;9ZO1N9@cQz;7*a2u%`|$)_wLNQd>UGo&n&~&FSrl7&DXKJCkk2% z##(s0<_;T7r7jq+ANAiFi(B^WaO#*Im!3GT!(XG^Jsc^?DO)`7+xD70eDWeLR|n*0okPd8y}ba} z;mS&i9Sv&n-S4|qW8#=KnY~ncRNpF(sNPccRZdgZOfwWPs$NuyHI4-qNi$!*c;1)W z)4k;2{j_Gp!p2AHg-?P|(?``ClkT;+l0Ixjf<>#58~>$?mpNt`PNL4WmEg20_AK6b z;Q(XF4H5XH<<4u%aoc8xh;NC2w9uEKgGy-~W>Hq^73$~J7s#~bYv(*(TI>WZmZT)4 z&!HV_0b|aq*&gIDsCTd+p86g`UKm4xs zMJ<;52~UU-O||{Y85>nZcCT(f(@#-ei{N6&5#-d_|c>g8e6*nZIHCftLZ!L8yyiw4d$6SAB zLg)C_@x0^aG_tzfekzU2eDDjr?_=~wZJrNF>$66F_r6Z|?}u(;3ik`~UUQz5ULU4c z>vs*);YQ{Y0~K>-<+%Qxyq(t#_cogD!c5#O>FwgJ<7t3nJNjWX>Q#Z5ov)Vf>}udpCdWID>#|Qy9_+>Lr)O06 z+Gt`E-<^N@L^9jUe@CG+Y&#~3CE~_Cv`l@?;`_{ss*0JuptMT!?KkcvQyBa=gS0Z6 z3Q4n@ewlul&G6&6(Mt_kk1RI)?N)2IoG`1d)yIz(${#kJ@>@PK!wb#q*Zbifo@8Dm zk2!txNwCTH#QcNws^84ucUUpkW#nCfmO|ttz$GLc`y{^R74edki z)a*D}uLo4$cDzAO$fenX&|Un$shUWkKTui>BK#XxiTp~`2#~9;sHgx#A*_K!fYk$< z{Wc+@^=~Hro4ir~o27}6>_Omh2vA7V&KgC4XCk;0y!``^Aw=~4)1x8`4Rds{`3d!? zUo`(WQbQoWMqIrGvjS@C0}X)?upmTOPznZfH1I>~70WBg?##YdkQNqpODS~OrIA!Xhuzs_%CfW0%u=vH zg$kCEHpUk*wgt6ONq|rdHrB=;6-;1TU)V@ZEmA7@s#dL5skPoak7bu_tH1o?WHK}N z%$$4f`Of*iJ89M;a}J@5?C<5`Mq#F`JZS_9>yc*61d?78KeW{-bcy(TFs>B+D58v0T%VsyvE zx~2_xzEe(*wVC~{^IeV)zjr0{w9m%9H+X(#Y(LY3js2 zldYm^ViwRhvk_L}S|^{eN0f{yaXwjWFGjz2bf z+mW`0+q zr9C};x#3`I*`V+9D~F$1cXoCAp@tre@;W6b;1dE7A&2e|JC#<4-lLH!2(bWaaWikR z^AJ#wmxm1SP?9(t0Hvt_5J2f5;T6Ixz2me%D_2CcDyz2E?VudI;M1%FI`kTOz*CB; z55@$_$`&65lvdJB3GuBYOL=)1LauTd<)q0%rWrf{O*kNQK%-IUjpzV1f{Tgq^3aC^ zs9VI=u&|8Z7m|S(M6k*Wx5LmZ>E(PVprb`LmjIsi3rj(no8VVPR1@u>Y?eawiIk#^ z;ut^cpg4fwvxTH)fMWzJ0!)l5!67VF?qRhsx={n8sCIuHFQ8&(*hb;YLKy(UAm)(A9t-NrsHy*A|}qX@;sIC4PXwClt=x|m_EgHr`BQT;ql~@>Bfb0vZN<6AC zvA?0u?Tm`;#KzyQUg%neeKUqFtbHC+{88Oj9o2s{{`JTT5QvZAB`71g1_5!%n*xuo zo)iWafLIaA5+oVnp21cU-O)rq2p|#~sgCdliIDJ6P+|uz(!&XwFX;+OQl3_#8X*!C z(duS&i=^qT2xvqSq%ug_vnT|FC|RM{Qu`}b?Zy*K*Ey^gjOKhamy&y;a>eg6j@iFH zNv}_Dy!u9OYVMks3g!kylJy-TKoCLI7gj4m zC7>CEKSVTdCi^f&cuJ6?tYyeWAd!@F!lb3NlY_F2)ZP}Zd4>g< z4w7aWKqv@}0tcD(Jny?#3AY|-bhD(do_25whIMCU3303}<$Ho~Zb{fG|Wm2J3M4@tAch7*tu zOKfTlq)fPt(4wL(vEe$XgW)z@V~Dir@rW3N))*N>qlNtvJ|AH)M$AVT^ssE<->Gml zgmQ_E(1py$vn1`ISYdCp(~nRHKSWF$!@!8Kdk~56dNas+}A`Jt~(A^4>(%qtTi*$ENNDLhkg0yrNp0kS+9A=B>ftr>& zR61WzDtq=4uf>MeKK< zC?A?%5c=)v`QF}Y`;}>YPfq{ifWu36k2yTAs9s@PYler6n8f18TL)W9JDZ}Sz1Q#Q zH$0T-osPN`O*>-$a6T`R&}cQ?@p?xzmDy4lj$e^_Yd8Z}7vVOQG? z_=fN1vfZIC3JpPb?wCO&M}l_wNP4Ch{h=Zo&H+U9;0x7oDD6RJ^QA|x4z*E%5P_@B zbpAsi!I}Ra(Sqo%pFkAxAdS8!EuJaidq$1X-O*`L5k&8#fg9o($R&60>Cuc;O$Ewk z?av0~!L9dHOg0?~q;D>J1H%+zY-ZPH3N8;wEs6Ww8ATNUA8-JP_i+}aE&1pSIz{{_ znVo&ZP}Sf9X)NV-4&_7dm?d3j*U$r;`v`#}vUQfd4V`%nu=L>ki+8WTK=xYK=qe-x z9i)>?vh?Z^UH%Ze$#!Le5MO-pGLZPW#SFzVua=}xm$>prRLj*$(S8bwp12;*~ZyBHL&qg~9iR3m-P&`z=ymHo@KYgU^RhU)3bsZmI zzzY(y>GdvXuK=E>54xaf`xt~m~EHyi@ghoPJI(Ky=(e8 zQD)mt+b4{LAW3^aU3+HDX^6wf#?ixzEF*Hm4O^ih3Lkv$j04D_;^p3!b)|l0ZSLc? zN)I5tU%S4=pM3s4gx2`YY**dNw+}3u!cVeZ9KLei@ORw%lsbtB83QMF^~RNi)Bxxz z?n4-eDL*a!%65;x`f{R7fBx}o*A6HrIpa2OlXJW|6RhouH;5YFkfrw!v%tDs5-z(N zyP+LFI4Idi(96{4JRj0;n3l1EcR3qj)}T%W0Y9|>QJ8#{?6ZZAzB^p6RTGa|$C#hv zU2Qlo=_Hd{od|Exva|M2rKVY!rx~!|ODC0e)ufiCP%z=B^DO}MR|d?G5rLz0V+8Os z)#afSE990vnb}@s@A(L&N~V=M3@(BacD_-1Q07yIc^(sH$Nhq!wpEr_Jla!Xr5DNT z;U?szrRrdB{$OQMv@sj-fV0+bmbqgZ5kkI`2;S+J_czE&E6QD4ZgX?v(30dXF_&aH zk?-y0I8h`*8er*@=)BzZFm3Y7Se4gQR0+Qve&r)MztSI3d zKyg+%^S5d0Y}hdzqwWW3@dv2}LY&D>Pb6nKyZPniq^i;~70&d>3Rcv)?L)t_Lcv;V_Khr{%te4>WMRpu)OvQB?O|fhrd_Jia(V(< z>MS=|Tk8y)J3R-%i;<3o+dYACtmBcGipHfg57S>$KmHkBb(z2$z=N|$&T=F`kBEaPIJw)Xh*or#_p5=Ldatji zvlFUU`90^{&v^?9-LDBoMv5JIYe;qnQ~*K?N5^Z`P8b(d5;j(8sP8dj1pvD!hbJ~G zXTSJ6e2KzgiO*=5re!fm^cd2Pfds6P9W#_UQm>q{ zZg`4o{5i@msBpQHzLtHogy5mehoil*^D~w8^0zOZ4M+LJpe!Txe|?5w<1NEgp{p?R zimmfGYie$Pf}+#rT%N>Lo5gz^d`MnSGVDzYqkRmDw7K z!$zGA=f|j8O_emP7C6O@**%nipMGI+xcY=B{*!6gd{t3>t2Tum=8R^ZDPdxjjP^{l z#P%NG!wyA;;3e1Yod9v{?m0kMai;8^nCQ2qy7q*9EJ@ff@_gx1+nDx7Vah@_ie`!) zi&hq0(XQheWaR+SuITqH60|i!*&LL`Z+H=MAq$#`&UU5xp&*S0$peBMYMLDvv z1*u^>VD{w`&uC5%HCVpX02dRv^up!AoPNr^l*8@ysz*JgmstE@Il?Ue4PzW>tIQn# zFhyq2H66$b0lcc2z#~6)U9z}g<1phWe+@{CbTM+and*wpMi@FnVv`yfji@YYdr}Yy z-I|t(_ypwI49z=nY8=FJTFk+lj7>?!sN0RJOJAtSb;C@!?8>Wc6V+>_I%*5Oz)Iu6Kt);Z!cu=K`&#ybt=mT z=?YS?eZLcSY-naZ*64R?)+%f-&Qm4K;O$PD={JGa;?06P%gve@!)|m1GgHeI_N0fR zB`K8;Kzf~SF-!*COtF@!)b)VGOkeUa6ur zJCn05D?cpsR;Lq>r6(wU(vVi}ok#OsJhoyx&kJ+e2 zxFWYyz8pR&2SwIkuHn|cQybH5qaJl=S%~PfF$9Mg2`7Yon#<71^25lz$c!8Z#7hIt z3@0kl9#Tn~m1!=#(SS6S%KDSJTgN$}#a)hl$7DELE0H%t$qnk_@2~euj0|w8wf^2n z8(Y{}%a&qBac&t@$2dt~RZwl1oRcd|UjhrH7ISmNi{Q;Qh@uPD+m?;?r_oe>p z{d)e{QSuq1O&VE8Mk~p(D65RZfuHf$*1`p7aY=A4S-BD3kDlXimY=nkchJ1XMYE~m z_$VxB-y8E-*QC@35fc%-wMkEv(SMJVp{Hw;FMc1}RyfS+RAEUj|5WUe>qUtW=1*Nu z?*d;n7nx=OZK#cpm*GHFVVW^ROErRp2KIiN02pzwJ=y6#HFDNF<3*p{#W)#Q*z%3t z$Y*4TH3bkxNgGZr&yTG`phRtTme{`B-=qYp_GsakdO@7?(cXlK-C21EAQ$G5`$JPf zKLN#ttLtF3ti5Pu7d4r>x?5nZ{@K#0D!zjyrc73X7bM1im9=;S`@((+FReDd!4 zkVJ+qTb<=EWJVri{zP3LUYQL36u0HgWzOsGKFx`JnXhqEm+^K)DpOag zKc65HUp))`p27*!h?>tI?nmK%@^UrS9m}EU_=-7B8{-7=Qx@4XM(g*iJspDj86=$~T@DL${LKfD@@mZLGruf-i}k*jUi&I=}# zRK#%SLzQkcJ0IV_!lD6^Wt?T4SW2@i``372e?R$pGhD_Ws77VoD@km!mES8hYniIb zoCy&s;gEZ67@~}-5pnPenpc{CEcuwBRa$N4i0#oO>#ihSuEJB!MhRY}TQE9QN+}Da zOfScIjjJ?5PC)9WSb<uoz8k8mlbNxuQJm!2s`XlmCl)BAni3fTsf#I44+ zTaZBVIeaX@~)Ca8+vt^nl|{_Pi&=}eng%1)ury|05`rzU+K zB73zZx~V@xpxJh8-B9SJ;CwFB#_A0*aND6jzvkF!kH!$Y4(tXdt>Txa8lAUbA-d|2 zw7R*m!bTa~_lTRP@=^PrKkoC@4HeqMuwVy(X^td3*pG<EI32= zDZf7wUnvD6$)0!$=xuW}>4bi29_bcorRg~LTO=k2lT1AxTEY#i+jQ{@AF9Bod1nl(EdWWW5~m}yZG!)>at^lW_PgF7>UD;WFi3o z`4}hzZ*3VkUQtmVi8?+wy^jQX4j$Xnt9jTlYPVI;Wng&@F5wGxsDoIu3J%?_JJ;8l zyy?m2-PfdFky9Bm3=6)cWTe9oO)a6=3)qMuJT8dp!@}%QPrkMcQJR4Jw6H{>h1t!= zmUA;9+L{Spnc>g3f4*?SV_8hgGnQkc8Y;4iKFYTDK7@+-Uvf*gk~|c499wlFPceG< zxEg*B-Hq#)H9}-}yXFIzA{>7qSm;$lVqWIeaI`X^{}Sf5m#A;OZ;tglZT~%k%jPCz zIAH=*Q?)OLn_k8^{Dn|kwlnFqYH;iF2C%03@rXEj}EXRWz%e#CHo!(lD?es4~NO}HSFDq|ou`ij=Z_{9Mp+|KN89qjh)PCmQS%bNgh|?a0Q5k&20=amn8yI`R*3t1mb+1Rv)u`Js!sN%8gLhY-tDWjEWiK&l(M(6 zchYcxnZf~g0aDI90D<3WMMVKX84nkzhRdzG_uB`(^$Fhj@PJTfFaYu=>_1c8dH+oH z*Mk0&0cZ*XfVd%d3gO>{-6ix;j>i)$4twi$h{4N5he7jjdi90#c?yg#( zG~CV76s``H{QpoXliW2mW+{S^Ednhg_ZR`pCp0f{0b@dPh4J|$kqsQg1E$Er&Hl)^ z(p_eATCmR1)Sr^fvYJJsGfdsgn;y}mx|qtmoD+eOrJusQWS--N4R6tuPeoZfdptgV z{$2?MME2HbaDyewCKN0~M!gq$goc8X&<$yJA_~8NSfrzPdX-&(}(meB3)>P)R$T(r%8rGKq) zEzx zdT@|{C>d#XeMa(Yr5$29)ZfMVKBJxT@Xh9PwaorP^2EAGuer~MDyO(BjLg8Yi+hDi7j)E2EV-7`1ufD zKg@1fNrF*h{gfcu%!xUhlRD>XpiKwby%=nlOgvscZV(!$F(IAr69@D~07{^*90__7 zI=38}r8q{Vgy;+ko&?Vfs#}1S1ImQo%MlD^e|`rneT*0Fgbw%wzEE+zpbkY4>Y|?| zj{gA(dA7ud7>Zotw~|vNw7NK+QWC0yWayuyLSsnc#4koz3aQuwK4eL$ikv)hp=rc1 z2>6+aJp$mxx<>U5BuzmZhY`-8)cVer3(?^~I|r7eJ;>iM zaK16>jK>l0!$BXF)yAPcl$MOqWYN+;I6x-$inDYZ*ry>y;;0 zpwGZ|gy@KR+o`|NIlQi?(g)k|m15KpPe(NQZg&wbvmAOfvoDhE`xAFKADDPCwBq^D z`Otrje%RB-8iT8gVeL=MkOW||VbUa_!P&sM4kYd5Rr4!UabXN1@$A+&VXKImk!Y5u zV^NMLlMiB|RA*v)TkWSQZ^dZ9BEr(b5a0jQ4C+LsBW|ct%9H^xVkV9gQ%!ixLZ+fX zCnGtl3{hwh_Z0V(%2OvSw42cWkkXi@tI{Ivt>6uW#y?3uPG0FRi7$?|PTWp@#jKJr zo$Q`$#GS!v#NeZFlYdo@rq&_eq2ZTPu9YRbT-_=1MqVa0JJBS>B-JF;B;Xt0CpG5g zIKQ~Mdn!fZTjgWSc?>j?<9VKA_0`X+pMHUV0X={UxY=@o3ulJwtvz0& zTccU4`mB!C2{Jx^qE4L`5g*YM5q-?N-hf;|&heL78vbf#OT1vWWA{pAOhhE2CPsfy zdTu{SHktOp_Jhg<$cn`ZW;JVdImuldi7Y_6rz6vhcz>;41%EGZ41HPu)9OdpcaiV$ zG^sSLG}JWDX_9$+_^4CuQu|ZaQa5;et1Wc#YcO=zbT?`YL5Xko1~n1PRpF4VlG5D* zu2Iq4g<_Es)50ok^Bm=>;rj-0+4?4tTtFWEN9vD(TCXLQT80&9HAaT(^|){L(vsImh#Xv`8fH={%0C9 z6N{S0jmT;wf%Uot3vV%BQ*UejaFEtG0dzCh8W+=M(O zI%T|Mx#YnX3HAtXAv&9JaeLep=siigy}4XJd>FBU&H9S9VqCu9Ynsk5+c4p94n6_p z0p$c2mD8x>ay^|>EVmhVgpr3s*2MF<#1-A`pM%e)Z}szT+wO%Kk=g6|OAca1;#g>S zZ$d%*1`QJ5FeJI`MeK{Ry0|lweXIQf(Wj!JqLHHaTisfVy@BVjYqV>(p9{y+2OB@9 zFI6xEF+SpM;^ktfi5PDN*93yQ%jJKykD_PC$uCsplyajID>8bFrD79!=NUHI(<>cP; z``O~7u}Y7fbr4M{Q8!9IviW2~ZnL|9s4B6VOJ+12{b4fjE$o{L^L*U)R~_#c7Yl=@ zfx9hvr+Js|OKv#s^9x&z$J%dAbPQ)lN|y{u)&`HVkLsFU?6hjZ^c;de0y3rxOUzdWLy_*kZlTIISJg4b#nazrS$6SmhX7l zgW(5UV()%=okX0zO|ANDu852sCV&49lHvaPNV4b6VR$HI@Y5GAqK(z7V`-)3rHcc8 z$z_Iz4NgUKEtgl%lZ(wuAqGO;Y2GcnLD!K}^iAnWMmw)9w`%rnTecjku9H?uqFOP% zH+~vi0vAskz1O0CrO~2?WBuIYZhiI3a9(7~d+~Y{yNK9OZ1APvwZ*mR&Pp~bo(g;V z2eFdt$@6JMQOn6r=Cm=gD#ScGs9p;AEAgUCr3({TgyfK|mi-Pbfg&R=A{Jg&oP0VM zh)5n^dDF&rQ+t*;TAcCP{j$Z}q`&*XdUX4(n8IRVE9B?YsqKc@=IXe+<$iua*kW<3 z*s0%j)}`r5Z|v5N%DGBnMx>a#7w*-O2a7FY{p(ra@#Vk4{cjk02Tj5JJm5d@_6|nh z*>176yp)tA%o%P5_>E!J0S13!?jZgjNc$fK;QW8a^mkz0^_Ca#0Dy{?X3n=D{*FN0 zX8R{d{x_$Pg1NwK?9Klo7tViJg%;e&+0xz)0OaB3{pa{U6+M0iGXx& z^8?geVNNbD0k^#2Hv#c*Vf(WRo+SuY$bYQ& zmW!CVn%*8D;Q5>ANZpw@vW2;{Ry_fxx$W`QLH85Watn|Lf_eW@pNkXB z(gyDIn+$4LzP#OMJ^)bN-u||${%R(mydA>+w#9xkOL^(rCKHCiOh9IE&=V6=ety^! vJ{X+Gga;x3=Mgl6@WVl1m?+-=JLIn>b#}RJq2C|wwmbOon3-i%WbytNgrevj literal 0 HcmV?d00001 diff --git a/platform/ios/resources/default_marker.png b/platform/ios/resources/default_marker.png deleted file mode 100644 index b112096c180eecca2d6afde217a9fe54eae6f327..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1010 zcmVm{G8B)KoXJFa>pBy66(wY{N1{V$IeF+19SHcT z6u!wx>Kh-Ywd`huNn7=Ohk*=HT|M7$glc?V+76YwcTrt+HGNN~8&5eL(u>;+Am2oF`sBU=Mo2*bi}Ud?l#qhEy>oiS&DPrAXu9LANYbpjS$iC z=2g_qjQ8{N)H5?fi~IJ`oYP6~Mn>poE=RRmlXReI@Ly?%6|^ zBN6h~*Kc*13179f5wY$#>x=+L|K#LFq}&)Vy%DK>Tv(vf?d^FoH4N>kHsZ`C`lHvnlCvw70rk7d}-2OGCW*D&88mdA?;f;6Xy?E;tH z<$FW1S<9of9pD3BaOhnoX71%{sJB>@r2rrJf+J6;q4oXrG?_=J6krP<_!{Ssv?x)_ z&c2C>9TRG{otiJ1BLr^7mzJWRcaS=4U<)7fNg+d&_z1OrnEqBWb?9INTg``xMM$?R zE8i+Vvkyz9-ZMH%uz{^vmmjQfIP9pZsyfKaP(lz!gPF_;!Db7XZ=!)%j7Am~#-6*~ zw-mcwVjqzuttd(^Psn6W+>tEqtrKovcjt1Say1`+ObS+3SR9mZD$x`##@|9n5*! zI5shs>|fmc@Qvnci0*J56TGa@y7niKS;nWPrZz4AzO)gNH1U>54<|%nqID{xj55k7 gql_~COZ*aG0OGQs<%kp{B>(^b07*qoM6N<$f?LhmRsaA1 diff --git a/platform/ios/resources/default_marker@2x.png b/platform/ios/resources/default_marker@2x.png deleted file mode 100644 index d05c82bfe28d9e4d87f8cce2b74a6b39b4805c1f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1995 zcmV;+2Q>JJP)v@uO-+Qf8KOssBPh>J=a zQEaH95(bcmj(~&1LuWce|L<}R9Ol8jceqd&_e&1<&b{aH&42#a`3DDOh;NFD>iE*g zZ7(-Jx4MKbo!mm)+PJlTHa^~&$vO^`q^kJx3Ac~ARhe9?kK0e&T9|B~r39H6By^nH z=ki&OD~l>iim5z5pS)|<(0abAc=YTUO+J54!+aeW9itbqf7REgxivG<(6R|Kxdy)c zCbp5v3JPdjSvge}7nu^@z}Pr-jSSPs#DshVatE2@)r<);xf4Qinw0j|)+R~HEFjso z2M;vq3P6%4OcP{sKMH-L+I8!wzPg&+E*DvWNGwL}fdD;t{8&DMUN)ub>(V0!sqL(& zAeYlgRsnNuEGnXW-j)9GaXhNq@9|V!nwhzvOOVOI5WbM)HkFprN}(t(FOlQ~lWb4B z#lpAU!hL;B4Q(weqt(DaBO}z_+bcirRDEXRzF}*rs<>p;$N^XjYpNWQ2#T$(gMWLc zb`z~OuqM_92f-ytg2(5!*1WY&Mj~`S6rzq`P;0oyGc&-NSX<=+WC`iPV*T)SW{hHF zYKm^Ks~F;IDr{gQ*v&4gjITz3wL2a@gpc%t6YFA&CMvcb?ECsRDl4hf=QA|fAFSBd zhK48{jcT7?-M5dn95_H5_v|5$-%q1GJ=(i_PoGjG9;XU}^Ew<38ssrB1-9t#9U=h; z;K@hz^@g?=RS^?Hasu$MiySjGV&^ zLG4_iWVq?AD>qlWc3-}%`8lINUyM=bSkf35xP+YIcu3<@`pWC&Z@)q42b{1`$i5is zH6THSOXmW?nQ3D#gJUaz2+`Bps@orZWHgaubqUTaCfKGOTF1r=4MyNc*aCh(a)hp* zJehL4@eJ?KM}|71NrS?r%^}XbT6F$!*Dme!W;9ulAy4{7=}KS_Qs>OXVX1$=c67*( zg$|K!;oRG3HqEy>-AYLRQ7$={C7$Cn)z~#WY>FTjexJV-!WSZhbi!~YuvV{0mjZx; z!igk7X!|K7ia1!CJ)DEc<_yTBI@Ajn6B3-}9JQ#CwBY{cz`*|&HI%hmWZg!MBxf|e znwT);w5Ub*+WsT-krQvk>AWF0$SXSo>8Vu%E z{N8E939IlyT9oe^99j{#gE=v`z>#$I>a-a($_{+FisJXRo*v5yVop(5zbe+sDCKXG z2GbJ^T1F0YDTdLM@tH(5nD*X)C0mU-FxOIsk+#B`4xEmm>RN}A#CzMeEsNl#t}ZFP zPA0e0s#D$kRwG}_6c}rCYfTXpHDk@HZ}kuGp=Qc7IgG(r#cDe(BUoKs?f4fk(_8OMz0hW8Y1^+AVB?m z5Sk?lw|c!zA*U1T7V+FA++v|nXvP#lCYGxZb1j^rossf*yuUCWe>WHoQ%Rne3UWP5 zlEZIw_*F?etz~C_c|9lRe!}{GUJGrSiAHq^LTY@e;g*wX!MoYneOseZ-E_Lxoa=8!A`P?gc=40T zNvc`9HtDA_lya|hcT@PqbBa0~_rJ~0KWe}pAjybI(y5Sx7%Ge^{e`@|PoYy|Oi*s2 z3OvI*^g&+}_EaIbBw2B}p3y8i{kvS@dv5npnSrbR5%)4fMj!MwLC__*BoXwRbsZNy zo{pd^>+88t*sCRTh2r<1Q0_rX%wjtOUh>0a^PNy z>E*y!AQ0$Fl;H4krDzdig3DqfVWo`0lV@Vh7Gr>=H<>b$z=Ok+qk?9Y0hyE>$c-%- ziv>xX*z-h;Tn(To-OJ>jnM(OZY=&2NuMjcp)^MWKOrJ?aaw(h3f~~|EIK;%3|G!%) z$ohnidW2+i4QwAIg$yOkq{3vwh7B7wY}l}2!-fqTHf-3iVZ(+E8#Zj%uwlc74I4IW d=-@vA1^|>xdY&#X)Q$iE002ovPDHLkV1g93zViS8 diff --git a/platform/ios/resources/default_marker@3x.png b/platform/ios/resources/default_marker@3x.png deleted file mode 100644 index 703b172c154d584d8c46e9cec018410bd2fc7075..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2998 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 YkEChNJ2NU=6>%yZO#_Vzb=$E20b+i|v;Y7A diff --git a/platform/ios/resources/mapbox.png b/platform/ios/resources/mapbox.png deleted file mode 100644 index 00bc897a58e6213c360bdab77200003788926d85..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2347 zcmV+`3Dow9P)8Hrtu{&llOh!_rC9a-h1C)LlHs{u22-U0zf;dY480UZB?IUs zgg8DzslZTqQz?pC3m{Ptgsj}$+znc-Rz2H5o6Qz%X=(Xub#?Vk0KgO8WvZ z1E8G{GXA3qB!oZ$b}hp&MQv?ucZ0#8ixA>`R}BV(uC}(ey9~n=0Z5hS`j9FBEz7bM zU0q!_>+0%^0CxY_G9wgEKC!8(iOGGg+xiv^IXm@ZCwf)1(1O3yM1KmdS^ z5Mon|O;#An=Kx+R<_bvPCl6h4e!wSo}xn4+jC02_}TJC+Rq7cN}5fAHYJ->h1-D(3d>+fVE2 z>h5gbym>`oVc}YpN<|(%d^mCB$PqI|QS+q*f5G#7+OA!@eyLKah|lK>HZ(NcOHWT< z1^|_nl^r^rjxH)HN*);*nGi*BUQSNV&!VED7RtP4vsnYcquf)tIn8FXhN7r;=|EVH zhXA{1wqJ)iHUjH+1WXI<;s;rhG7 z7z_gxMfCyzTUl9oI-I`bd0r17A3*-fl`D$^fxweZn>LloG(ivy$}yFdm8Su)k}it_ zkneW8-G0B{C4Xh!OP4PF-Rt$bmHNB2wY5{8ySBFWvfL;0dA;5@a{BNS%%l^b<69YBj1+*cI)-}^U6MX4YKXf>-FaeARF8UtiyS$+pA+ zK)%Ujx*7^B##>z2ouOs1;<@J<$XOkIQU5B_j*vBJ$vsU7ah^MO?oXj|W$I2|7d-m+T$;nAd(%CKm4vWR&n3{qf z0D1v*0f67{{|*4OTJ6IB#^-c8eR4m;FhKz5=;&|(2oOTrWx3FIeY>E(#}`uYyZS04uu9#o&t7cn|I`b=)y?e^(iyLNpuH8u55p6AVWyL}n}_UzfS zX33HzuYYKlWME*x4FF4*E?p*()&of7I4&tVI{GblFCig8CmnDqfb|T+ELgN?QJi#e zQ?u~_Kzw{WE%$po9yI`DW@au05JgecMgWOC&!@%4#*!B=UQ7W1$8kyP)~(ZsqDav+ z{kpWYGy_1QU1Xa&mG$FDNMZ1%S1(ESBeay;5h&I%~G7RNl%eS<$zw zzhXX}QV(++S2YXmKdisfu+64Zm7c}Btp6;0Z&`n34YmH=-Q8AYTQ+t8wDSNsO(xSo zSy@?*9P7u%#*zaE4rE@uc=2zt!=I3l@RRiP^ktcunQK#0Qs{6#t#m7#3g>vHv9a-* z`~W1MQQO$qculQVzminUIDqFNwBeGHk`}Ag`gr^H?VrWO#JmdiQ$uy?(W6JZckbMo zLesQb;eS2=U>hABrE6+xu8xe1Ogww`?By(c766gu<>hE^Z?}cL{z(?8kgNsx|ZB{}^KuTlEQO`>$l8XxElhzz1y&jDsq0x0PXk4r`iZ1z5n-l((eOm zXy$*%jvdL_+1W`nO{i diff --git a/platform/ios/resources/mapbox@2x.png b/platform/ios/resources/mapbox@2x.png deleted file mode 100644 index 206ed5883e2240f5fe1987679b1c7a12a912a8aa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5290 zcmWld1y~ea6ov;CK^ml$M!FUdq>)@Yl@3`z2}Nln6$Dpe0ZD-cC8R;RTVUyQX%zU8 zPU-qD^E|V&yK`su-ZSSr-+LCRtF1~*Ku-Wc5V0CuNgo{Zz|Mnz8@zV!`kH{lo!4*^ zHwdDBaJ}QG>9g-Z5T&`A(qlvKsZ9%CUCORsa*aL8A;z(dWlE2Sm5c-n_fDuMc)326 zg;Hq6Y2E2~p`S}mRjy3dT$y3WPA2q2SCl?Hn_`IFh9&%QTo*1e>nW13T0Z&+F$vGO zb(7HO^sL>%nWLOEI!kj4onK^Q|8^i%c0Wb-`{3#*a|A3RgiIj;vc`kNAZubMLGp_% zZV(AH1j*m+Jmya7R%&mZdj0ql*%us00ZMw*_T2M^{DaP#&a8mDkvwp^*OEU}HDll% zUY?#+X>x&=^EePSpYaX{)G}M|#e@UR)O&63UYs3`^Tg+;Awu{?-i<2>66=RVKZN+Q zRkgvDf!mEo%S9&T_CFdM8;R2gnTEPD8ynNG6BD_Z{(gR`z{_(Tz7|I)qkHLg(hDVK zJC(r`$XcbP_W2+qDN<>{#>q)aC>Ae&+!3vx*KqKY#%f95dJ`|mH%HGSM3DSzGi=*I zvgY#bgkd_L_oNvhwQn=-vKGxt%;?UkndJ$-$CHHa<}WivA~)SXdesjZbb zJUB>`2sl5^XlZFtx}B>*rr=Mz$Z_{PRP~;iv$xy!T%(J@_hKRDhi4*3;axX!A1qFn z;+)pUCMIIzva+(6-^p9IrkeJ2+pyjx#^n|X7rzNT@>nx$fkXLE3w(~Y=L5}kb$7Pk zzkeTy#bQyzj+NcC8E8olo*)TPge@!vX_@5P**6VAAt51rM~8=KyFw(^u`;)!qT=HG zWPeiJWv=j9m(l|YjAou8k7=!Ic1lxjP<8cDVQHxlHa9mn1+oq%`|@>A&d$~zRm0FH zA?1y}BN9wwQexkRN+#C{qcIVouzPVc$le>xur4B$i5_@M(z0)>j0cIr1}hP>Qz5Xe zA(lWUw~~h%0>9VVg)W}LAmi3>-G$N<*q-*gd3##5bSI%s`kUK$P)2{ZrxxuZ$rPst z>$4n?WWMxLYK|^0f4M0?rA9|b6Ir~BQ@4xP&nu(||M@c}Cg$Gk?Chq;NiLKBzu_+t z5fKEpSF|Brkv2Mht%O@O_$-_pd~8cQ<9)QRCvaN)LUhR!T_VURR%@7O$u!5*N9-_+$&Dfzi$1;P}BX(tOZV%sKF%(OPT$1rU z^nUc{kufddP%s{84m$_O94yEG@#Dvz)6?ha{F{n{gM)vfWqhTpT;?xjAY<3a9DEij zMd)CEKc(6h6N`i(`p9KD${l_To|=k}xVZS)o5fC<^^J|O<)tN2z0brjg%vTUe->(y zD$La3n=>;rIg=9;5xCyyXZ4<&ro>c5zgh1o)=?mp&Z&5xYI?kWodiLz&ki>Sz|b%v zD-9K~OhpZ636GtE4%&Hy2_cNXM4q{HXJ;o#*?oF)GLN5|+rfw@{t2bE5j{(6beYQA z>6jQpI3-&Q+#G7L⁡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 iLpNVg!Mlp@`NNJ)2>bf=P%4&B``bSe$+@L20I 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 diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 9a27cf24c66..ed989459f52 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -467,7 +467,7 @@ - (void)commonInit // 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"); @@ -616,11 +616,11 @@ - (void)createGLView - (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:)]) { @@ -635,7 +635,7 @@ - (UIImage *)compassImage 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]; @@ -874,8 +874,8 @@ - (void)layoutOrnaments // logo bug self.logoView.frame = { - self.contentInset.left+5, - CGRectGetHeight(self.bounds)-5-self.contentInset.bottom-CGRectGetHeight(self.logoView.bounds), + self.contentInset.left+8, + CGRectGetHeight(self.bounds)-8-self.contentInset.bottom-CGRectGetHeight(self.logoView.bounds), CGRectGetWidth(self.logoView.bounds), CGRectGetHeight(self.logoView.bounds) }; @@ -5290,17 +5290,17 @@ - (void)updateCompass + (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]; - 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 From bf000c7d63991d8a38012be0b3e84d29cde17fd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Tue, 23 May 2017 11:40:43 -0700 Subject: [PATCH 38/62] [ios, macos] Updated changelogs Reorganized the changelogs with a new packaging section. Added blurbs about #9062, #8713, #9060, and #9031. --- platform/ios/CHANGELOG.md | 19 +++++++++++++------ platform/macos/CHANGELOG.md | 10 ++++++++-- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index b5a0e7dd7d3..9caec9c0d72 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -4,6 +4,12 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT ## 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)) + ### Styles * Added support for 3D extrusion of buildings and other polygonal features via the `MGLFillExtrusionStyleLayer` class and the `fill-extrusion` layer type in style JSON. ([#8431](https://github.com/mapbox/mapbox-gl-native/pull/8431)) @@ -15,30 +21,31 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * 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)) * 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)) +* 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)) ### Other changes -* Xcode 8.0 or higher is now recommended for using this SDK. ([#8775](https://github.com/mapbox/mapbox-gl-native/pull/8775)) -* Fixed a crash when calling `MGLMultiPolygon.coordinate` [#8713](https://github.com/mapbox/mapbox-gl-native/pull/8713) -* 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)) -* Fixed a bug which caused the compass and other ornaments to underlap navigation and tab bars. ([#7716](https://github.com/mapbox/mapbox-gl-native/pull/7716)) -* Fixed an issue in the static framework where localizations would never load. ([#9074](https://github.com/mapbox/mapbox-gl-native/pull/9074)) ## 3.5.4 - May 9, 2017 diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index a1515e9d7a0..ca08811c0aa 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -2,6 +2,11 @@ ## 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)) + ### Styles * Added support for 3D extrusion of buildings and other polygonal features via the `MGLFillExtrusionStyleLayer` class and the `fill-extrusion` layer type in style JSON. ([#8431](https://github.com/mapbox/mapbox-gl-native/pull/8431)) @@ -13,15 +18,16 @@ * 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)) * Fixed a crash when calling `MGLMultiPolygon.coordinate` [#8713](https://github.com/mapbox/mapbox-gl-native/pull/8713) -* 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 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 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 From 091d3d1d7ba519500a04907eac8fe2567ea454e2 Mon Sep 17 00:00:00 2001 From: Fabian Guerra Date: Tue, 23 May 2017 16:52:28 -0400 Subject: [PATCH 39/62] [ios] Update pods spec for iOS v3.6.0-beta.1 --- platform/ios/Mapbox-iOS-SDK-symbols.podspec | 2 +- platform/ios/Mapbox-iOS-SDK.podspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/ios/Mapbox-iOS-SDK-symbols.podspec b/platform/ios/Mapbox-iOS-SDK-symbols.podspec index f8b20777fae..c94b349b8c4 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.1' 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 15658e62413..0d464de4001 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.1' m.name = 'Mapbox-iOS-SDK' m.version = version From 8c73cb1a4631211ef805ca585c74701f40699c75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguye=CC=82=CC=83n?= Date: Tue, 23 May 2017 13:21:34 -0700 Subject: [PATCH 40/62] =?UTF-8?q?[macos]=20Enable=20View=20=E2=80=A3=20Tra?= =?UTF-8?q?ffic=20Night?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable the View ‣ Traffic Night menu item and check it when that style is active. --- platform/macos/app/MapDocument.m | 3 +++ 1 file changed, 3 insertions(+) diff --git a/platform/macos/app/MapDocument.m b/platform/macos/app/MapDocument.m index a30e7626452..59844d363e9 100644 --- a/platform/macos/app/MapDocument.m +++ b/platform/macos/app/MapDocument.m @@ -807,6 +807,9 @@ - (BOOL)validateMenuItem:(NSMenuItem *)menuItem { case 7: state = [styleURL isEqual:[MGLStyle trafficDayStyleURL]]; break; + case 8: + state = [styleURL isEqual:[MGLStyle trafficNightStyleURL]]; + break; default: return NO; } From 3c35bf1d170d1b331d871f75ad031834d99d2e78 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Wed, 24 May 2017 14:47:31 +0200 Subject: [PATCH 41/62] [android] - stop location updates when toggle MyLocationView state (#9099) --- .../java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java | 1 + 1 file changed, 1 insertion(+) 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 aecf3cc6555..338d29c87cc 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 @@ private void toggleGps(boolean enableGps) { } else { // Disable location and user dot location = null; + locationSource.removeLocationUpdates(); locationSource.removeLocationEngineListener(userLocationListener); locationSource.deactivate(); } From 8199315e9d554a002e950508d0b6e51f22f79d1c Mon Sep 17 00:00:00 2001 From: Tobrun Date: Wed, 24 May 2017 20:39:42 +0200 Subject: [PATCH 42/62] [android] - LatLngBounds bearing default value (#9102) --- platform/android/src/map/camera_position.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/android/src/map/camera_position.cpp b/platform/android/src/map/camera_position.cpp index 27b19f7ad65..d6f2cb83e8e 100644 --- a/platform/android/src/map/camera_position.cpp +++ b/platform/android/src/map/camera_position.cpp @@ -13,7 +13,7 @@ jni::Object CameraPosition::New(jni::JNIEnv &env, mbgl::CameraOp // 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(-M_PI) * util::RAD2DEG; + double bearing_degrees = -options.angle.value_or(0) * util::RAD2DEG; while (bearing_degrees > 360) { bearing_degrees -= 360; } From e1431f9085153f7afb33ee169289ff46d38fe209 Mon Sep 17 00:00:00 2001 From: Fabian Guerra Date: Wed, 24 May 2017 12:20:58 -0400 Subject: [PATCH 43/62] [ios, macos] Fix MGLLight.achor to accept style functions --- platform/darwin/src/MGLLight.h | 4 ++-- platform/darwin/src/MGLLight.mm | 8 ++------ platform/darwin/test/MGLLightTest.mm | 12 +++++++++--- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/platform/darwin/src/MGLLight.h b/platform/darwin/src/MGLLight.h index d9a22d60fae..ef9811bd33c 100644 --- a/platform/darwin/src/MGLLight.h +++ b/platform/darwin/src/MGLLight.h @@ -60,7 +60,7 @@ MGL_EXPORT href="https://www.mapbox.com/mapbox-gl-js/style-spec/#light-anchor">anchor light property in the Mapbox Style Specification. */ -@property (nonatomic) MGLLightAnchor anchor; +@property (nonatomic) MGLStyleValue *anchor; /** Values describing animated transitions to `anchor` property. @@ -75,7 +75,7 @@ MGL_EXPORT href="https://www.mapbox.com/mapbox-gl-js/style-spec/#light-position">position light property in the Mapbox Style Specification. */ -@property (nonatomic) MGLStyleValue * position; +@property (nonatomic) MGLStyleValue *position; /** Values describing animated transitions to `position` property. diff --git a/platform/darwin/src/MGLLight.mm b/platform/darwin/src/MGLLight.mm index 02d55e76ed8..262fad3b070 100644 --- a/platform/darwin/src/MGLLight.mm +++ b/platform/darwin/src/MGLLight.mm @@ -48,9 +48,7 @@ - (instancetype)initWithMBGLLight:(const mbgl::style::Light *)mbglLight anchorStyleValue = MGLStyleValueTransformer().toEnumStyleValue(anchor); } - NSAssert([anchorStyleValue isKindOfClass:[MGLConstantStyleValue class]], @"Anchor isn’t a constant."); - NSValue *anchorValue = ((MGLConstantStyleValue *)anchorStyleValue).rawValue; - _anchor = [anchorValue MGLLightAnchorValue]; + _anchor = anchorStyleValue; _anchorTransition = MGLTransitionFromOptions(mbglLight->getAnchorTransition()); @@ -89,11 +87,9 @@ - (instancetype)initWithMBGLLight:(const mbgl::style::Light *)mbglLight { mbgl::style::Light mbglLight; - MGLStyleValue *anchorType = [MGLStyleValue valueWithRawValue:[NSValue valueWithMGLLightAnchor:self.anchor]]; - auto anchor = MGLStyleValueTransformer().toEnumPropertyValue(anchorType); + auto anchor = MGLStyleValueTransformer().toEnumPropertyValue(self.anchor); mbglLight.setAnchor(anchor); - mbglLight.setAnchorTransition(MGLOptionsFromTransition(self.anchorTransition)); auto position = MGLStyleValueTransformer().toInterpolatablePropertyValue(self.position); diff --git a/platform/darwin/test/MGLLightTest.mm b/platform/darwin/test/MGLLightTest.mm index 4639b13cded..2c3d1c7bd1e 100644 --- a/platform/darwin/test/MGLLightTest.mm +++ b/platform/darwin/test/MGLLightTest.mm @@ -25,7 +25,10 @@ - (void)testProperties { { mbgl::style::Light light; MGLLight *mglLight = [[MGLLight alloc] initWithMBGLLight:&light]; - XCTAssertEqual(mglLight.anchor, MGLLightAnchorViewport); + + 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); @@ -36,10 +39,13 @@ - (void)testProperties { XCTAssert(anchorTransition.delay && MGLTimeIntervalFromDuration(*anchorTransition.delay) == defaultTransition.delay); XCTAssert(anchorTransition.duration && MGLTimeIntervalFromDuration(*anchorTransition.duration) == defaultTransition.duration); - mglLight.anchor = MGLLightAnchorMap; + MGLStyleValue *anchorStyleValue = [MGLStyleValue 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(mglLight.anchor, MGLLightAnchorMap); + XCTAssertEqual(anchorValue.MGLLightAnchorValue, MGLLightAnchorMap); XCTAssertEqual(mglLight.anchorTransition.delay, transition.delay); XCTAssertEqual(mglLight.anchorTransition.duration, transition.duration); From fce7747da099519d8e29eff4befc5c205048492a Mon Sep 17 00:00:00 2001 From: Jesse Bounds Date: Wed, 24 May 2017 21:08:19 -0700 Subject: [PATCH 44/62] [ios] Fix annotation initializers for subclasses of MGLAnnotationView (#9104) Use a common init function in both of the provided initializers so that subclasses of `MGLAnnotationView` written in Swift don't need to override `init(annotation, reuseIdentifier)` --- platform/ios/ios.xcodeproj/project.pbxproj | 4 ++++ platform/ios/src/MGLAnnotationView.mm | 23 ++++++++++++------- .../MGLAnnotationViewIntegrationTests.swift | 23 +++++++++++++++++++ platform/ios/test/MGLAnnotationViewTests.m | 21 ++++++++++++++++- 4 files changed, 62 insertions(+), 9 deletions(-) create mode 100644 platform/ios/test/MGLAnnotationViewIntegrationTests.swift diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index 6ff148a18eb..b6d422d0fc0 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -172,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 */; }; @@ -639,6 +640,7 @@ 4085AF081D933DEA00F11B22 /* MGLTileSetTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLTileSetTests.mm; path = ../../darwin/test/MGLTileSetTests.mm; sourceTree = ""; }; 408AA8551DAEDA0800022900 /* NSDictionary+MGLAdditions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+MGLAdditions.h"; sourceTree = ""; }; 408AA8561DAEDA0800022900 /* NSDictionary+MGLAdditions.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSDictionary+MGLAdditions.mm"; sourceTree = ""; }; + 409D0A0C1ED614CE00C95D0C /* MGLAnnotationViewIntegrationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MGLAnnotationViewIntegrationTests.swift; sourceTree = ""; }; 409F43FC1E9E781C0048729D /* MGLMapViewDelegateIntegrationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MGLMapViewDelegateIntegrationTests.swift; sourceTree = ""; }; 40CF6DBA1DAC3C1800A4D18B /* MGLShape_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLShape_Private.h; sourceTree = ""; }; 40CFA6501D787579008103BD /* MGLShapeSourceTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLShapeSourceTests.mm; path = ../../darwin/test/MGLShapeSourceTests.mm; sourceTree = ""; }; @@ -1119,6 +1121,7 @@ isa = PBXGroup; children = ( 409F43FC1E9E781C0048729D /* MGLMapViewDelegateIntegrationTests.swift */, + 409D0A0C1ED614CE00C95D0C /* MGLAnnotationViewIntegrationTests.swift */, ); name = "Swift Integration"; sourceTree = ""; @@ -2148,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 */, diff --git a/platform/ios/src/MGLAnnotationView.mm b/platform/ios/src/MGLAnnotationView.mm index 9e1212b4fb8..78e38f10cb9 100644 --- a/platform/ios/src/MGLAnnotationView.mm +++ b/platform/ios/src/MGLAnnotationView.mm @@ -24,22 +24,29 @@ + (BOOL)supportsSecureCoding { } - (instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier { - return [self initWithAnnotation:nil reuseIdentifier:reuseIdentifier]; + self = [super initWithFrame:CGRectZero]; + if (self) { + [self commonInitWithAnnotation:nil reuseIdentifier:reuseIdentifier]; + } + return self; } - (instancetype)initWithAnnotation:(nullable id)annotation reuseIdentifier:(nullable NSString *)reuseIdentifier { self = [super initWithFrame:CGRectZero]; - if (self) - { - _lastAppliedScaleTransform = CATransform3DIdentity; - _annotation = annotation; - _reuseIdentifier = [reuseIdentifier copy]; - _scalesWithViewingDistance = YES; - _enabled = YES; + if (self) { + [self commonInitWithAnnotation:annotation reuseIdentifier:reuseIdentifier]; } return self; } +- (void)commonInitWithAnnotation:(nullable id)annotation reuseIdentifier:(nullable NSString *)reuseIdentifier { + _lastAppliedScaleTransform = CATransform3DIdentity; + _annotation = annotation; + _reuseIdentifier = [reuseIdentifier copy]; + _scalesWithViewingDistance = YES; + _enabled = YES; +} + - (instancetype)initWithCoder:(NSCoder *)decoder { if (self = [super initWithCoder:decoder]) { _reuseIdentifier = [decoder decodeObjectOfClass:[NSString class] forKey:@"reuseIdentifier"]; diff --git a/platform/ios/test/MGLAnnotationViewIntegrationTests.swift b/platform/ios/test/MGLAnnotationViewIntegrationTests.swift new file mode 100644 index 00000000000..82a57a40097 --- /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 88ca755476c..fc4f35a9e12 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,6 +92,12 @@ - (void)testAnnotationView 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)annotation { MGLAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:MGLTestAnnotationReuseIdentifer]; From 35f548a07d11a62fda91d0352650d25ee51b1381 Mon Sep 17 00:00:00 2001 From: Jordan Kiley Date: Thu, 25 May 2017 18:09:45 -0700 Subject: [PATCH 45/62] [ios] Moved `MGLLight` in jazzy table of contents (#9111) --- platform/ios/jazzy.yml | 1 + platform/macos/jazzy.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/platform/ios/jazzy.yml b/platform/ios/jazzy.yml index 638abaaf6c7..31380faa2c7 100644 --- a/platform/ios/jazzy.yml +++ b/platform/ios/jazzy.yml @@ -60,6 +60,7 @@ custom_categories: children: - MGLStyle - MGLStyleValue + - MGLLight - name: Style Primitives children: - MGLFeature diff --git a/platform/macos/jazzy.yml b/platform/macos/jazzy.yml index 53f260b242e..fcd4bfd3019 100644 --- a/platform/macos/jazzy.yml +++ b/platform/macos/jazzy.yml @@ -46,6 +46,7 @@ custom_categories: children: - MGLStyle - MGLStyleValue + - MGLLight - name: Content Primitives children: - MGLFeature From c392c678ab4ea560cefca59d14b215e2d4c79ecb Mon Sep 17 00:00:00 2001 From: Tobrun Date: Fri, 26 May 2017 10:32:11 +0200 Subject: [PATCH 46/62] [android] - logo placement for creating a Mapview programatically (#9094) * [android] - Correct logo placement for creating a Mapview programatically, fixup non default placements * Fixed NIGHTY_TWO_DP typo to NINETY_TWO_DP --- .../mapboxsdk/maps/MapboxMapOptions.java | 4 +-- .../com/mapbox/mapboxsdk/maps/UiSettings.java | 28 +++++++++++++------ .../src/main/res/values/dimens.xml | 2 +- .../res/layout/activity_press_for_marker.xml | 2 +- 4 files changed, 24 insertions(+), 12 deletions(-) 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 68603ab1a38..98f94ddb39a 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 @@ 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 static MapboxMapOptions createFromAttributes(@NonNull Context context, @N 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/UiSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java index 1bcf8a70b96..accecf232b7 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 @@ private Drawable decode(byte[] bitmap) { 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 @@ private void restoreLogo(Bundle savedInstanceState) { 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/res/values/dimens.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml index ce20cb9a8bf..8edbd47c297 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 @@ 8dp 10dp 16dp - 95dp + 92dp 18dp 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 9932803907f..c2098b0f5bf 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"/> From 5716bd3aecc777813161292e104ac9ceba3f230a Mon Sep 17 00:00:00 2001 From: Tobrun Date: Fri, 26 May 2017 16:04:09 +0200 Subject: [PATCH 47/62] [android] - add binding support for Light (#9013) --- .../gradle-checkstyle.gradle | 1 + .../com/mapbox/mapboxsdk/maps/MapboxMap.java | 15 ++ .../mapbox/mapboxsdk/maps/NativeMapView.java | 10 + .../mapboxsdk/style/layers/Property.java | 21 ++ .../mapbox/mapboxsdk/style/light/Light.java | 181 ++++++++++++++++++ .../mapboxsdk/style/light/Position.java | 81 ++++++++ .../mapboxsdk/style/light/light.java.ejs | 121 ++++++++++++ .../mapboxsdk/testapp/style/LightTest.java | 162 ++++++++++++++++ .../mapboxsdk/testapp/style/light.junit.ejs | 128 +++++++++++++ .../src/main/AndroidManifest.xml | 4 +- .../style/BuildingFillExtrusionActivity.java | 88 +++++++-- .../style/FillExtrusionStyleTestActivity.java | 78 ++++++++ .../src/main/res/drawable/ic_paint.xml | 9 + .../res/layout/activity_building_layer.xml | 30 ++- .../res/layout/activity_extrusion_test.xml | 18 ++ .../src/main/res/menu/menu_building.xml | 12 ++ platform/android/config.cmake | 4 + .../android/scripts/generate-style-code.js | 93 ++++++++- platform/android/src/jni.cpp | 3 + platform/android/src/native_map_view.cpp | 11 ++ platform/android/src/native_map_view.hpp | 3 + .../android/src/style/conversion/position.hpp | 37 ++++ .../android/src/style/conversion/types.hpp | 7 + .../style/conversion/types_string_values.hpp | 14 ++ platform/android/src/style/light.cpp | 146 ++++++++++++++ platform/android/src/style/light.cpp.ejs | 123 ++++++++++++ platform/android/src/style/light.hpp | 57 ++++++ platform/android/src/style/light.hpp.ejs | 59 ++++++ platform/android/src/style/position.cpp | 34 ++++ platform/android/src/style/position.hpp | 29 +++ 30 files changed, 1557 insertions(+), 22 deletions(-) create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Light.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Position.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/light.java.ejs create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LightTest.java create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/light.junit.ejs create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionStyleTestActivity.java create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_paint.xml create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_extrusion_test.xml create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_building.xml create mode 100644 platform/android/src/style/conversion/position.hpp create mode 100644 platform/android/src/style/light.cpp create mode 100644 platform/android/src/style/light.cpp.ejs create mode 100644 platform/android/src/style/light.hpp create mode 100644 platform/android/src/style/light.hpp.ejs create mode 100644 platform/android/src/style/position.cpp create mode 100644 platform/android/src/style/position.hpp diff --git a/platform/android/MapboxGLAndroidSDK/gradle-checkstyle.gradle b/platform/android/MapboxGLAndroidSDK/gradle-checkstyle.gradle index 05037c6be7b..e0bc076d3d8 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/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java index 96603355f19..0bfa74d1c00 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.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; @@ -567,6 +568,20 @@ public Projection getProjection() { return projection; } + // + // + // + + /** + * 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 // 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 cf8faa9012e..008a1971ccc 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 @@ -26,6 +26,7 @@ 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; @@ -885,6 +886,13 @@ public void setApiBaseUrl(String baseUrl) { fileSource.setApiBaseUrl(baseUrl); } + public Light getLight() { + if (isDestroyedOn("getLight")) { + return null; + } + return nativeGetLight(); + } + public float getPixelRatio() { return pixelRatio; } @@ -1120,6 +1128,8 @@ private native Feature[] nativeQueryRenderedFeaturesForBox(float left, float top 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/style/layers/Property.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java index 48e0ec5de39..5e345268f94 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/light/Light.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Light.java new file mode 100644 index 00000000000..b66a50b8a4d --- /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 The online documentation + */ +@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 00000000000..215db03ad25 --- /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. + *

+ * 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 00000000000..067efe1092a --- /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 The online documentation + */ +@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/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 00000000000..36833fb4eed --- /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 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())), + fillExtrusionBase(Function.property("min_height", new IdentityStops())), + 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 00000000000..cb3ba581004 --- /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 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())), + fillExtrusionBase(Function.property("min_height", new IdentityStops())), + 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 9ede763533f..1a70e4548a1 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml @@ -661,7 +661,9 @@ - + ())), - fillExtrusionBase(Function.property("min_height", new IdentityStops())), - 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())), + fillExtrusionBase(Function.property("min_height", new IdentityStops())), + 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 void onDestroy() { 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 00000000000..1ff0b0e8e1d --- /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/res/drawable/ic_paint.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_paint.xml new file mode 100644 index 00000000000..f9e55e14804 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_paint.xml @@ -0,0 +1,9 @@ + + + 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 d9a10871b5d..fa37c485d71 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 @@ - + android:layout_height="match_parent"> + app:mapbox_styleUrl="@string/mapbox_style_dark"/> - + + + + + 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 00000000000..d9a10871b5d --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_extrusion_test.xml @@ -0,0 +1,18 @@ + + + + + + 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 00000000000..92d1dd53809 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_building.xml @@ -0,0 +1,12 @@ + +

+ + + diff --git a/platform/android/config.cmake b/platform/android/config.cmake index c111744ed0c..84591d644bc 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/scripts/generate-style-code.js b/platform/android/scripts/generate-style-code.js index 5d8fc4cc6de..bb04e3ba2ad 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/src/jni.cpp b/platform/android/src/jni.cpp index 53691acb392..6c490fad5ce 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/native_map_view.cpp b/platform/android/src/native_map_view.cpp index 1012a0e9d8f..0a57d3e6b4a 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 { @@ -819,6 +820,15 @@ jni::Array> NativeMapView::queryRenderedFeaturesFo return *convert>, std::vector>(env, map->queryRenderedFeatures(box, { layers, toFilter(env, jfilter) })); } +jni::Object NativeMapView::getLight(JNIEnv& env) { + mbgl::style::Light* light = map->getLight(); + if (light) { + return jni::Object(Light::createJavaLightPeer(env, *map, *light)); + } else { + return jni::Object(); + } +} + jni::Array> NativeMapView::getLayers(JNIEnv& env) { // Get the core layers @@ -1533,6 +1543,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 e5fcb9badd0..df43ad08b7d 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 #include @@ -230,6 +231,8 @@ class NativeMapView : public View, public Backend { jni::jfloat, jni::Array, jni::Array> jfilter); + jni::Object getLight(JNIEnv&); + jni::Array> getLayers(JNIEnv&); jni::Object 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 00000000000..f32a892c0ca --- /dev/null +++ b/platform/android/src/style/conversion/position.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include "../../conversion/conversion.hpp" + +#include +#include +#include "../../jni/local_object.hpp" +#include "../position.hpp" + +namespace mbgl { +namespace android { +namespace conversion { + +template<> +struct Converter, mbgl::style::Position> { + Result> operator()(jni::JNIEnv &env, const mbgl::style::Position &value) const { + std::array cartPosition = value.getSpherical(); + return Position::fromPosition(env, cartPosition[0], cartPosition[1], cartPosition[2]); + } +}; + +template<> +struct Converter> { + Result operator()(jni::JNIEnv &env, const jni::Object &value) const { + float radialCoordinate = Position::getRadialCoordinate(env, value); + float azimuthalAngle = Position::getAzimuthalAngle(env, value); + float polarAngle = Position::getPolarAngle(env, value); + std::array 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 d9921e582e1..a00f668c24a 100644 --- a/platform/android/src/style/conversion/types.hpp +++ b/platform/android/src/style/conversion/types.hpp @@ -92,6 +92,13 @@ struct Converter { } }; +template <> +struct Converter { + Result operator()(jni::JNIEnv& env, const mbgl::style::LightAnchorType& value) const { + return convert(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 e3108fdf5b8..e96de3b01e4 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 00000000000..71f1cb076e1 --- /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 +#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 peerLight = std::unique_ptr(initializeLightPeer(map, coreLight)); + jni::jobject* result = peerLight->createJavaPeer(env); + peerLight.release(); + return result; +} + +jni::Class Light::javaClass; + +jni::jobject* Light::createJavaPeer(jni::JNIEnv& env) { + static auto constructor = Light::javaClass.template GetConstructor(env); + return Light::javaClass.New(env, constructor, reinterpret_cast(this)); +} + +void Light::setAnchor(jni::JNIEnv& env, jni::String property) { + std::string anchorStr = jni::Make(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(env, "map"); + } else { + return jni::Make(env, "viewport"); + } +} + +void Light::setPosition(jni::JNIEnv& env, jni::Object jposition) { + using namespace mbgl::android::conversion; + auto position = *convert(env, jposition); + light.setPosition(position); +} + +jni::Object Light::getPosition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::Position position = light.getPosition().asConstant(); + return *convert>(env, position); +} + +jni::Object Light::getPositionTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = light.getPositionTransition(); + return *convert>(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(env, property)); + if (color) { + light.setColor(color.value()); + } +} + +jni::String Light::getColor(jni::JNIEnv &env) { + auto color = light.getColor().asConstant(); + return jni::Make(env, color.stringify()); +} + +jni::Object Light::getColorTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = light.getColorTransition(); + return *convert>(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 Light::getIntensityTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = light.getIntensityTransition(); + return *convert>(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::Find(env).NewGlobalRef(env).release(); + +#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) + // Register the peer + jni::RegisterNativePeer(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 00000000000..17f0bba09dc --- /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 +#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 peerLight = std::unique_ptr(initializeLightPeer(map, coreLight)); + jni::jobject* result = peerLight->createJavaPeer(env); + peerLight.release(); + return result; +} + +jni::Class Light::javaClass; + +jni::jobject* Light::createJavaPeer(jni::JNIEnv& env) { + static auto constructor = Light::javaClass.template GetConstructor(env); + return Light::javaClass.New(env, constructor, reinterpret_cast(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>(env, jposition); + light.set<%- camelize(property.name) %>(<%- property.name %>); +} + +jni::Object 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>>(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(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(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(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(env, "map"); + } else { + return jni::Make(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 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>(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::Find(env).NewGlobalRef(env).release(); + +#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) + // Register the peer + jni::RegisterNativePeer(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 00000000000..2c314067be4 --- /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 + +#include +#include +#include "transition_options.hpp" +#include "position.hpp" +#include +#include + +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 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); + jni::Object getPosition(jni::JNIEnv&); + void setPositionTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Object getPositionTransition(jni::JNIEnv&); + void setColor(jni::JNIEnv&, jni::String); + jni::String getColor(jni::JNIEnv&); + void setColorTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Object getColorTransition(jni::JNIEnv&); + void setIntensity(jni::JNIEnv&, jni::jfloat); + jni::jfloat getIntensity(jni::JNIEnv&); + void setIntensityTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Object 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 00000000000..18f961b9e0d --- /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 + +#include +#include +#include "transition_options.hpp" +#include "position.hpp" +#include +#include + +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 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); + 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 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 00000000000..0bbcefcbcd3 --- /dev/null +++ b/platform/android/src/style/position.cpp @@ -0,0 +1,34 @@ +#include "position.hpp" + +namespace mbgl { +namespace android { + +jni::Object Position::fromPosition(jni::JNIEnv& env, jfloat radialCoordinate, jfloat azimuthalAngle, jfloat polarAngle) { + static auto method = Position::javaClass.GetStaticMethod (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::Find(env).NewGlobalRef(env).release(); +} + +jni::Class Position::javaClass; + +float Position::getRadialCoordinate(jni::JNIEnv& env, jni::Object position){ + static auto field = Position::javaClass.GetField(env, "radialCoordinate"); + return position.Get(env, field); +} + +float Position::getAzimuthalAngle(jni::JNIEnv& env, jni::Object position){ + static auto field = Position::javaClass.GetField(env, "azimuthalAngle"); + return position.Get(env, field); +} + +float Position::getPolarAngle(jni::JNIEnv& env, jni::Object position){ + static auto field = Position::javaClass.GetField(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 00000000000..4aafa853dbe --- /dev/null +++ b/platform/android/src/style/position.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include + +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 fromPosition(jni::JNIEnv&, jfloat, jfloat, jfloat); + + static jni::Class javaClass; + + static void registerNative(jni::JNIEnv&); + + static float getRadialCoordinate(jni::JNIEnv&, jni::Object); + static float getAzimuthalAngle(jni::JNIEnv&, jni::Object); + static float getPolarAngle(jni::JNIEnv&, jni::Object); + +}; + + +} // namespace android +} // namespace mbgl \ No newline at end of file From 82a3a7e74681dd0cdae810f28fae70122f306834 Mon Sep 17 00:00:00 2001 From: Pablo Guardiola Date: Fri, 26 May 2017 20:00:40 +0200 Subject: [PATCH 48/62] [android] Update Lost to final version 3.0.0 (#9112) * update lost to final version 3.0.0 * bump MAS version to 2.1.1 --- platform/android/dependencies.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/android/dependencies.gradle b/platform/android/dependencies.gradle index eb717ba6004..5e80e344e4e 100644 --- a/platform/android/dependencies.gradle +++ b/platform/android/dependencies.gradle @@ -7,7 +7,7 @@ ext { versionCode = 11 versionName = "5.0.0" - mapboxServicesVersion = "2.2.0-SNAPSHOT" + mapboxServicesVersion = "2.1.1" supportLibVersion = "25.3.1" wearableVersion = '2.0.0' espressoVersion = '2.2.2' @@ -21,7 +21,7 @@ ext { mapboxAndroidTelemetry : "com.mapbox.mapboxsdk:mapbox-android-telemetry:${mapboxServicesVersion}@aar", // mapzen lost - lost : 'com.mapzen.android:lost:2.3.0-SNAPSHOT', + lost : 'com.mapzen.android:lost:3.0.0', // unit test junit : 'junit:junit:4.12', From bc5b147df79425ec03f2fbe6f3a2566ee45b4800 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Fri, 26 May 2017 20:01:57 +0200 Subject: [PATCH 49/62] [android] - convert dp to pixels when getting meters per pixel at. (#9048) --- .../src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 008a1971ccc..02eeb25f834 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 @@ -621,7 +621,7 @@ public double getMetersPerPixelAtLatitude(double lat) { if (isDestroyedOn("getMetersPerPixelAtLatitude")) { return 0; } - return nativeGetMetersPerPixelAtLatitude(lat, getZoom()); + return nativeGetMetersPerPixelAtLatitude(lat, getZoom()) / pixelRatio; } public ProjectedMeters projectedMetersForLatLng(LatLng latLng) { From 40a73b390b7e692e8415c98dba267ccefc909357 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Fri, 26 May 2017 22:08:54 +0200 Subject: [PATCH 50/62] Release Android v5.1.0-beta.3 (#9115) * [android] - Changelog update for Android release v5.1.0-beta.3 * update CI & version * reset release properties --- platform/android/CHANGELOG.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index bc307e50614..e99276dd200 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -2,8 +2,20 @@ 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 +## 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 From fc495dec12a5136f7476aa9d5cb8d76c95024919 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Tue, 30 May 2017 16:56:19 +0200 Subject: [PATCH 51/62] [android] - harden orientation changes (#9128) --- .../mapbox/mapboxsdk/annotations/Icon.java | 67 ++++++-- .../mapboxsdk/annotations/MarkerView.java | 4 + .../mapbox/mapboxsdk/maps/IconManager.java | 148 ++++++++---------- .../mapbox/mapboxsdk/maps/NativeMapView.java | 4 - .../mapboxsdk/annotations/IconTest.java | 2 + 5 files changed, 127 insertions(+), 98 deletions(-) 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 b1a05ec4365..2ee17c227d2 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. + *

+ * Requires the bitmap to be set before calling this method. + *

+ * + * @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. + *

+ * Requires the bitmap to be set before calling this method. + *

* - * @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 boolean equals(Object object) { } 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 30336d4ebd8..56e8cc4ce22 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 @@ 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 @@ void setSelected(boolean selected) { */ @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/maps/IconManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/IconManager.java index c9d81a88bc4..9f4171aee8e 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/NativeMapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java index 02eeb25f834..63f2e653ccd 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 @@ -897,10 +897,6 @@ public float getPixelRatio() { return pixelRatio; } - public Context getContext() { - return mapView.getContext(); - } - // // Callbacks // diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/IconTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/IconTest.java index 5f6f6b6c6db..1c259af2d09 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/IconTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/IconTest.java @@ -9,6 +9,7 @@ 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 From 7b85bcccae5c7b8f3b82135362e16a799413fcc5 Mon Sep 17 00:00:00 2001 From: Jesse Bounds Date: Tue, 30 May 2017 10:51:07 -0700 Subject: [PATCH 52/62] [ios] Remove filter of single metric event --- platform/ios/src/MGLMapboxEvents.m | 7 ------- 1 file changed, 7 deletions(-) diff --git a/platform/ios/src/MGLMapboxEvents.m b/platform/ios/src/MGLMapboxEvents.m index 7b28ccf1a89..4f1413d3009 100644 --- a/platform/ios/src/MGLMapboxEvents.m +++ b/platform/ios/src/MGLMapboxEvents.m @@ -317,13 +317,6 @@ - (void)flush { 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]; From 608fd66ab833937671a52d1366caf11a23f7c457 Mon Sep 17 00:00:00 2001 From: Fredrik Karlsson Date: Thu, 25 May 2017 21:42:28 +0200 Subject: [PATCH 53/62] [ios] remove layout guide observers --- platform/ios/src/MGLMapView.mm | 100 +++++++++++++++------------------ 1 file changed, 44 insertions(+), 56 deletions(-) diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index ed989459f52..39a9281a7a0 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -656,21 +656,57 @@ - (void)reachabilityChanged:(NSNotification *)notification _isWaitingForRedundantReachableNotification = NO; } -- (void)dealloc +- (void)willMoveToWindow:(UIWindow *)newWindow { - [_reachability stopNotifier]; + [super willMoveToWindow:newWindow]; + + if (newWindow) { + [self addLayoutGuideObserversIfNeeded]; + } else { + [self removeLayoutGuideObserversIfNeeded]; + } +} - [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications]; - [[NSNotificationCenter defaultCenter] removeObserver:self]; - [_attributionButton removeObserver:self forKeyPath:@"hidden"]; +- (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 *)self.viewControllerForLayoutGuides.topLayoutGuide removeObserver:self forKeyPath:@"bounds" context:(void *)&MGLLayoutGuidesUpdatedContext]; + [(NSObject *)viewController.topLayoutGuide removeObserver:self forKeyPath:@"bounds" context:(void *)&MGLLayoutGuidesUpdatedContext]; + _isObservingTopLayoutGuide = NO; } if (_isObservingBottomLayoutGuide) { - [(NSObject *)self.viewControllerForLayoutGuides.bottomLayoutGuide removeObserver:self forKeyPath:@"bounds" context:(void *)&MGLLayoutGuidesUpdatedContext]; + [(NSObject *)viewController.bottomLayoutGuide removeObserver:self forKeyPath:@"bounds" context:(void *)&MGLLayoutGuidesUpdatedContext]; + _isObservingBottomLayoutGuide = NO; } +} + +- (void)dealloc +{ + [_reachability stopNotifier]; + + [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications]; + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [_attributionButton removeObserver:self forKeyPath:@"hidden"]; + + [self removeLayoutGuideObserversIfNeeded]; // Removing the annotations unregisters any outstanding KVO observers. NSArray *annotations = self.annotations; @@ -787,25 +823,7 @@ - (void)updateConstraints // 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. - - 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; - } else if (!useLayoutGuides && _isObservingTopLayoutGuide) { - [(NSObject *)viewController.topLayoutGuide removeObserver:self forKeyPath:@"bounds" context:(void *)&MGLLayoutGuidesUpdatedContext]; - _isObservingTopLayoutGuide = NO; - } - - if (useLayoutGuides && viewController.bottomLayoutGuide && !_isObservingBottomLayoutGuide) { - [(NSObject *)viewController.bottomLayoutGuide addObserver:self forKeyPath:@"bounds" options:0 context:(void *)&MGLLayoutGuidesUpdatedContext]; - _isObservingBottomLayoutGuide = YES; - } else if (!useLayoutGuides && _isObservingBottomLayoutGuide) { - [(NSObject *)viewController.bottomLayoutGuide removeObserver:self forKeyPath:@"bounds" context:(void *)&MGLLayoutGuidesUpdatedContext]; - _isObservingBottomLayoutGuide = NO; - } + [self addLayoutGuideObserversIfNeeded]; } - (BOOL)isOpaque @@ -839,8 +857,6 @@ - (void)layoutSubviews [self adjustContentInset]; - [self observeLayoutGuidesIfNeeded]; - [self layoutOrnaments]; if (!_isTargetingInterfaceBuilder) { @@ -928,34 +944,6 @@ - (void)adjustContentInset self.contentInset = contentInset; } -- (void)observeLayoutGuidesIfNeeded -{ - UIViewController *viewController = self.viewControllerForLayoutGuides; - BOOL useLayoutGuides = viewController.view && viewController.automaticallyAdjustsScrollViewInsets; - - if (!_isObservingTopLayoutGuide && useLayoutGuides && viewController.topLayoutGuide) - { - [(NSObject *)viewController.topLayoutGuide addObserver:self forKeyPath:@"bounds" options:0 context:MGLLayoutGuidesUpdatedContext]; - _isObservingTopLayoutGuide = YES; - } - else if (!useLayoutGuides && _isObservingTopLayoutGuide) - { - [(NSObject *)viewController.topLayoutGuide removeObserver:self forKeyPath:@"bounds" context:MGLLayoutGuidesUpdatedContext]; - _isObservingTopLayoutGuide = NO; - } - - if (!_isObservingBottomLayoutGuide && useLayoutGuides && viewController.bottomLayoutGuide) - { - [(NSObject *)viewController.bottomLayoutGuide addObserver:self forKeyPath:@"bounds" options:0 context:MGLLayoutGuidesUpdatedContext]; - _isObservingBottomLayoutGuide = YES; - } - else if (!useLayoutGuides && _isObservingBottomLayoutGuide) - { - [(NSObject *)viewController.bottomLayoutGuide removeObserver:self forKeyPath:@"bounds" context:MGLLayoutGuidesUpdatedContext]; - _isObservingBottomLayoutGuide = NO; - } -} - - (void)setContentInset:(UIEdgeInsets)contentInset { [self setContentInset:contentInset animated:NO]; From a39116a552bbb7b77f2946b5931944c7065d818e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Tue, 30 May 2017 15:14:00 -0700 Subject: [PATCH 54/62] =?UTF-8?q?Migrate=20to=20GL=20JS=E2=80=93powered=20?= =?UTF-8?q?feedback=20form=20(#9078)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [ios, macos] Updated feedback URL * [ios, macos] Add referrer, heading, pitch to feedback URL * [ios, macos] Updated changelogs for feedback changes * [ios] Vary referrer by platform --- platform/darwin/src/MGLAttributionInfo.mm | 31 ++++++++++++++++--- .../darwin/src/MGLAttributionInfo_Private.h | 18 +++++++++++ platform/darwin/src/MGLOfflineStorage.mm | 14 ++------- platform/darwin/src/NSBundle+MGLAdditions.h | 2 ++ platform/darwin/src/NSBundle+MGLAdditions.m | 9 ++++++ .../darwin/test/MGLAttributionInfoTests.m | 12 ++++++- platform/ios/CHANGELOG.md | 1 + platform/ios/src/MGLMapView.mm | 10 ++++-- platform/macos/CHANGELOG.md | 1 + platform/macos/src/MGLMapView.mm | 15 ++++++--- 10 files changed, 89 insertions(+), 24 deletions(-) diff --git a/platform/darwin/src/MGLAttributionInfo.mm b/platform/darwin/src/MGLAttributionInfo.mm index 75832444910..770aeb25e76 100644 --- a/platform/darwin/src/MGLAttributionInfo.mm +++ b/platform/darwin/src/MGLAttributionInfo.mm @@ -6,8 +6,10 @@ #import #endif +#import "MGLAccountManager.h" #import "MGLMapCamera.h" #import "NSArray+MGLAdditions.h" +#import "NSBundle+MGLAdditions.h" #import "NSString+MGLAdditions.h" #include @@ -126,13 +128,34 @@ - (instancetype)initWithTitle:(NSAttributedString *)title URL:(NSURL *)URL { } - (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 *queryItems = [NSMutableArray arrayWithObject:referrerQueryItem]; + if ([styleURL.scheme isEqualToString:@"mapbox"] && [styleURL.host isEqualToString:@"styles"]) { + NSArray *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 08bc6bfc4d5..c639752ac3a 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/MGLOfflineStorage.mm b/platform/darwin/src/MGLOfflineStorage.mm index 195ef3c36ab..81774ad3cb5 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 @@ -132,7 +133,7 @@ + (NSURL *)cacheURLIncludingSubdirectory:(BOOL)useSubdirectory { 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 *)legacyCachePath { 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 @@ - (instancetype)init { 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/NSBundle+MGLAdditions.h b/platform/darwin/src/NSBundle+MGLAdditions.h index df70d239231..ad5e9d5369c 100644 --- a/platform/darwin/src/NSBundle+MGLAdditions.h +++ b/platform/darwin/src/NSBundle+MGLAdditions.h @@ -36,6 +36,8 @@ NS_ASSUME_NONNULL_BEGIN + (nullable NS_DICTIONARY_OF(NSString *, id) *)mgl_frameworkInfoDictionary; ++ (nullable NSString *)mgl_applicationBundleIdentifier; + @end NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/NSBundle+MGLAdditions.m b/platform/darwin/src/NSBundle+MGLAdditions.m index ad122831ba9..f383a50300e 100644 --- a/platform/darwin/src/NSBundle+MGLAdditions.m +++ b/platform/darwin/src/NSBundle+MGLAdditions.m @@ -31,4 +31,13 @@ + (nullable NSString *)mgl_frameworkBundleIdentifier { return bundle.infoDictionary; } ++ (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/test/MGLAttributionInfoTests.m b/platform/darwin/test/MGLAttributionInfoTests.m index e258671c09a..ed4927d44bd 100644 --- a/platform/darwin/test/MGLAttributionInfoTests.m +++ b/platform/darwin/test/MGLAttributionInfoTests.m @@ -46,8 +46,18 @@ - (void)testParsing { 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/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 9caec9c0d72..7228a946493 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -37,6 +37,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * 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 diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 39a9281a7a0..9d4c295d626 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 #include @@ -1895,8 +1895,12 @@ - (void)showAttribution { 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]; } diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index ca08811c0aa..fc4e88b08f4 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -25,6 +25,7 @@ * 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)) diff --git a/platform/macos/src/MGLMapView.mm b/platform/macos/src/MGLMapView.mm index a36766b7457..3e91a6c5a54 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 @@ - (IBAction)rotate:(NSSlider *)sender { } - (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]; } From 83df8a8621287e548ee771599c9c98b61b9971a3 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Wed, 31 May 2017 09:46:12 +0200 Subject: [PATCH 55/62] [android] - javadoc update for 5.1.0 release (#9138) * [android] - javadoc update for 5.1.0 release * oxford comma * Grammar/spelling tweaks * grammar tweak * grammar tweak * Grammar tweaks * Grammar tweaks --- .../java/com/mapbox/mapboxsdk/Mapbox.java | 33 +++++++---- .../mapboxsdk/camera/CameraPosition.java | 4 +- .../mapboxsdk/camera/CameraUpdateFactory.java | 2 +- .../constants/MyBearingTracking.java | 4 +- .../constants/MyLocationTracking.java | 4 +- .../com/mapbox/mapboxsdk/constants/Style.java | 2 +- .../mapboxsdk/location/LocationSource.java | 53 +++++++++++++++-- .../maps/FocalPointChangeListener.java | 3 + .../com/mapbox/mapboxsdk/maps/MapboxMap.java | 6 +- .../mapboxsdk/offline/OfflineManager.java | 4 +- .../mapboxsdk/offline/OfflineRegion.java | 2 +- .../mapbox/mapboxsdk/storage/Resource.java | 6 ++ .../style/functions/CompositeFunction.java | 2 +- .../mapbox/mapboxsdk/style/layers/Filter.java | 2 +- .../style/layers/TransitionOptions.java | 28 +++++++++ .../mapboxsdk/style/light/package-info.java | 4 ++ .../mapbox/mapboxsdk/utils/AnimatorUtils.java | 59 +++++++++++++++++++ .../mapbox/mapboxsdk/utils/ColorUtils.java | 3 + 18 files changed, 188 insertions(+), 33 deletions(-) create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/package-info.java 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 8098ee4d86e..f75d7ed90d6 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.location.LocationEngine; import com.mapbox.services.android.telemetry.location.LocationEnginePriority; +/** + * The entry point of the Mapbox Android SDK. + *

+ * 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. + *

+ */ public final class Mapbox { private static Mapbox INSTANCE; @@ -25,7 +34,7 @@ public final class Mapbox { /** * Get an instance of Mapbox. *

- * This class manages the active access token, application context and connectivity state. + * This class manages the active access token, application context, and connectivity state. *

* * @param context Android context which holds or is an application context @@ -52,9 +61,9 @@ public static synchronized Mapbox getInstance(@NonNull Context context, @NonNull } /** - * Access Token for this application. + * Access token for this application. * - * @return Mapbox Access Token + * @return Mapbox access token */ public static String getAccessToken() { validateMapbox(); @@ -72,12 +81,14 @@ private static void validateMapbox() throws MapboxConfigurationException { } /** - * Runtime validation of Access Token. + * Runtime validation of access token. * * @throws MapboxConfigurationException exception thrown when not using a valid accessToken */ private static void validateAccessToken() throws MapboxConfigurationException { - String accessToken = INSTANCE.accessToken; + String + + = INSTANCE.accessToken; if (TextUtils.isEmpty(accessToken) || (!accessToken.toLowerCase(MapboxConstants.MAPBOX_LOCALE).startsWith("pk.") && !accessToken.toLowerCase(MapboxConstants.MAPBOX_LOCALE).startsWith("sk."))) { throw new MapboxConfigurationException(); @@ -92,11 +103,11 @@ public static Context getApplicationContext() { } /** - * 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 +115,10 @@ public static synchronized void setConnected(Boolean connected) { } /** - * 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 +130,4 @@ public static synchronized Boolean isConnected() { 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/camera/CameraPosition.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java index 5e958ff5651..66c261f1d0b 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 int hashCode() { } /** - * Builder for composing {@link CameraPosition} objects. + * Builder for composing CameraPosition objects. */ public static final class Builder { @@ -190,7 +190,7 @@ public Builder(CameraPosition previous) { /** * 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 64b86054a0c..8e1411e2739 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.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 f53c65d055a..88c3bef6733 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 @@ 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 39f653596f6..a1744d701f2 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 @@ 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 77d0929df3e..31e6313509c 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 c4bdb4a17de..4e934fa3ccb 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 @@ 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 java.lang.ref.WeakReference; +import timber.log.Timber; + /** * Manages locational updates. Contains methods to register and unregister location listeners. *
    @@ -33,8 +35,6 @@ 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; @@ -48,7 +48,13 @@ private LocationSource(Context context) { .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 static synchronized LocationEngine getLocationEngine(Context context) { 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 void activate() { } } + /** + * 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 void deactivate() { } } + /** + * 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 void onConnected() { } } + /** + * 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 Location getLastLocation() { return null; } + /** + * Request location updates to the location provider. + */ @Override public void requestLocationUpdates() { // Common params @@ -122,6 +157,9 @@ public void requestLocationUpdates() { } } + /** + * Dismiss ongoing location update to the location provider. + */ @Override public void removeLocationUpdates() { if (lostApiClient.isConnected()) { @@ -129,6 +167,11 @@ public void removeLocationUpdates() { } } + /** + * Invoked when the Location has changed. + * + * @param location the new location + */ @Override public void onLocationChanged(Location location) { for (LocationEngineListener listener : locationListeners) { 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 006122a4e2e..aec9a848b7e 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 @@ 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/MapboxMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java index 0bfa74d1c00..0ba6c108315 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 @@ -1972,9 +1972,9 @@ public interface OnScrollListener { /** * Interface definition for a callback to be invoked when the camera changes position. * - * @deprecated Replaced by MapboxMap.OnCameraMoveStartedListener, MapboxMap.OnCameraMoveListener and - * 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 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 { 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 225278b17d9..95cb7d66c43 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 @@ -46,7 +46,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 { /** @@ -66,7 +66,7 @@ public interface ListOfflineRegionsCallback { /** * 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 44bb2a05ee9..baa815491fa 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 @@ -98,7 +98,7 @@ public interface OfflineRegionObserver { } /** - * 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 { 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 af98a46a9b3..eae83e8c1f7 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 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 8ded7ecd346..15e44741057 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 java.util.Map; /** - * Composite functions combine {@link android.graphics.Camera} and {@link SourceFunction}s. + * Composite functions combine Camera and SourceFunctions. *

    * 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 643a126388b..4dbb461e4ca 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 @@ 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/TransitionOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/TransitionOptions.java index a46c11b35cd..6e6e4ca6138 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 Transition documentation + */ 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/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/package-info.java new file mode 100644 index 00000000000..a613bf9587b --- /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 7694604d9fd..34c52c829bc 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.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 void onAnimationEnd(Animator animation) { 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 void onAnimationEnd(Animator animation) { 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 void onAnimationEnd(Animator animation) { }); } + /** + * 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 void onAnimationEnd(Animator animation) { 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 2da2472d69a..24c76243d96 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 java.util.regex.Matcher; import java.util.regex.Pattern; +/** + * Color utility class. + */ public class ColorUtils { /** From b6a4f1212cbf7454134a1e3f367d2e47db305a15 Mon Sep 17 00:00:00 2001 From: Pablo Guardiola Date: Wed, 31 May 2017 12:59:01 +0200 Subject: [PATCH 56/62] [android] fix missing access token variable issue (#9151) --- .../src/main/java/com/mapbox/mapboxsdk/Mapbox.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) 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 f75d7ed90d6..81134e9497d 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 @@ -86,9 +86,7 @@ private static void validateMapbox() throws MapboxConfigurationException { * @throws MapboxConfigurationException exception thrown when not using a valid accessToken */ private static void validateAccessToken() throws MapboxConfigurationException { - String - - = INSTANCE.accessToken; + String accessToken = INSTANCE.accessToken; if (TextUtils.isEmpty(accessToken) || (!accessToken.toLowerCase(MapboxConstants.MAPBOX_LOCALE).startsWith("pk.") && !accessToken.toLowerCase(MapboxConstants.MAPBOX_LOCALE).startsWith("sk."))) { throw new MapboxConfigurationException(); From af2547776acc0302d2fcfb45661198872348a980 Mon Sep 17 00:00:00 2001 From: Fabian Guerra Date: Wed, 31 May 2017 13:41:09 -0400 Subject: [PATCH 57/62] [ios] Update pods spec for iOS v3.6.0-beta.2 --- platform/ios/Mapbox-iOS-SDK-symbols.podspec | 2 +- platform/ios/Mapbox-iOS-SDK.podspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/ios/Mapbox-iOS-SDK-symbols.podspec b/platform/ios/Mapbox-iOS-SDK-symbols.podspec index c94b349b8c4..3116ede9f53 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-beta.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 0d464de4001..f6bc3030aba 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-beta.1' + version = '3.6.0-beta.2' m.name = 'Mapbox-iOS-SDK' m.version = version From 52eba423aecca51dd65c15d2d3dbffe2f06856ba Mon Sep 17 00:00:00 2001 From: Pablo Guardiola Date: Thu, 1 Jun 2017 10:05:31 +0200 Subject: [PATCH 58/62] [android] Cherry picking 9133 (#9145) * [android] Fix tracking mode + camera race condition (#9133) * [android] fix tracking mode + camera race condition * fix resetTrackingModesIfRequired bug (comparing current camera position with target camera position * cherry pick #9133 and update CHANGELOG * add null check to prevent null pointer exception --- platform/android/CHANGELOG.md | 5 ++ .../mapboxsdk/constants/MapboxConstants.java | 1 - .../mapboxsdk/maps/MapGestureDetector.java | 8 +-- .../com/mapbox/mapboxsdk/maps/MapboxMap.java | 30 ++++++++++- .../mapboxsdk/maps/TrackingSettings.java | 54 ++++++------------- .../com/mapbox/mapboxsdk/maps/Transform.java | 9 ++-- .../maps/widgets/MyLocationView.java | 49 ++--------------- 7 files changed, 62 insertions(+), 94 deletions(-) diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index e99276dd200..22bede23f6c 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -2,6 +2,11 @@ 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-rc.1 - 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) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java index 9adefa3221d..ecb6ffe24e4 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java @@ -113,7 +113,6 @@ public class MapboxConstants { public static final String STATE_MY_BEARING_TRACKING_MODE = "mapbox_myBearingTracking"; public static final String STATE_MY_LOCATION_TRACKING_DISMISS = "mapbox_myLocationTrackingDismiss"; public static final String STATE_MY_BEARING_TRACKING_DISMISS = "mapbox_myBearingTrackingDismiss"; - public static final String STATE_MY_TRACKING_MODE_DISMISS_FOR_CAMERA = "mapbox_myBearingTrackingDismiss"; public static final String STATE_COMPASS_ENABLED = "mapbox_compassEnabled"; public static final String STATE_COMPASS_GRAVITY = "mapbox_compassGravity"; public static final String STATE_COMPASS_MARGIN_LEFT = "mapbox_compassMarginLeft"; 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 39433d8b933..e60f9b1d1e6 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 @@ -357,7 +357,7 @@ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float ve return false; } - trackingSettings.resetTrackingModesIfRequired(true, false); + trackingSettings.resetTrackingModesIfRequired(true, false, false); // cancel any animation transform.cancelTransitions(); @@ -402,7 +402,7 @@ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float d } // reset tracking if needed - trackingSettings.resetTrackingModesIfRequired(true, false); + trackingSettings.resetTrackingModesIfRequired(true, false, false); // Scroll the map transform.moveBy(-distanceX, -distanceY, 0 /*no duration*/); @@ -484,7 +484,7 @@ public boolean onScale(ScaleGestureDetector detector) { // to be in the center of the map. Therefore the zoom will translate the map center, so tracking // should be disabled. - trackingSettings.resetTrackingModesIfRequired(!quickZoom, false); + trackingSettings.resetTrackingModesIfRequired(!quickZoom, false, false); // Scale the map if (focalPoint != null) { // arround user provided focal point @@ -568,7 +568,7 @@ public boolean onRotate(RotateGestureDetector detector) { } // rotation constitutes translation of anything except the center of // rotation, so cancel both location and bearing tracking if required - trackingSettings.resetTrackingModesIfRequired(true, true); + trackingSettings.resetTrackingModesIfRequired(true, true, false); // Get rotate value double bearing = transform.getRawBearing(); 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 0ba6c108315..7a62f06ad25 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 @@ -746,10 +746,38 @@ public final void easeCamera(CameraUpdate update, int durationMs, boolean easing @UiThread public final void easeCamera(final CameraUpdate update, final int durationMs, final boolean easingInterpolator, final MapboxMap.CancelableCallback callback) { + easeCamera(update, durationMs, easingInterpolator, callback, false); + } + + /** + * Gradually move the camera by a specified duration in milliseconds, zoom will not be affected + * unless specified within {@link CameraUpdate}. A callback can be used to be notified when + * easing the camera stops. If {@link #getCameraPosition()} is called during the animation, it + * will return the current location of the camera in flight. + *

    + * Note that this will cancel location tracking mode if enabled. You can change this behaviour by calling + * {@link TrackingSettings#setDismissTrackingModeForCameraPositionChange(boolean)} with false before invoking this + * method and calling it with true in the {@link CancelableCallback#onFinish()}. + *

    + * + * @param update The change that should be applied to the camera. + * @param durationMs The duration of the animation in milliseconds. This must be strictly + * positive, otherwise an IllegalArgumentException will be thrown. + * @param easingInterpolator True for easing interpolator, false for linear. + * @param callback An optional callback to be notified from the main thread when the animation + * stops. If the animation stops due to its natural completion, the callback + * will be notified with onFinish(). If the animation stops due to interruption + * by a later camera movement or a user gesture, onCancel() will be called. + * Do not update or ease the camera from within onCancel(). + * @param isDismissable true will allow animated camera changes dismiss a tracking mode. + */ + @UiThread + public final void easeCamera(final CameraUpdate update, final int durationMs, final boolean easingInterpolator, + final MapboxMap.CancelableCallback callback, final boolean isDismissable) { new Handler().post(new Runnable() { @Override public void run() { - transform.easeCamera(MapboxMap.this, update, durationMs, easingInterpolator, callback); + transform.easeCamera(MapboxMap.this, update, durationMs, easingInterpolator, callback, isDismissable); } }); } 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 25b60aa72d9..7dcd84de758 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 @@ -33,7 +33,6 @@ public final class TrackingSettings { private boolean myLocationEnabled; private boolean dismissLocationTrackingOnGesture = true; private boolean dismissBearingTrackingOnGesture = true; - private boolean isResetTrackingWithCameraPositionChange = true; private MapboxMap.OnMyLocationTrackingModeChangeListener onMyLocationTrackingModeChangeListener; private MapboxMap.OnMyBearingTrackingModeChangeListener onMyBearingTrackingModeChangeListener; @@ -57,8 +56,6 @@ void onSaveInstanceState(Bundle outState) { outState.putBoolean(MapboxConstants.STATE_MY_LOCATION_TRACKING_DISMISS, isDismissLocationTrackingOnGesture()); outState.putBoolean(MapboxConstants.STATE_MY_BEARING_TRACKING_DISMISS, isDismissBearingTrackingOnGesture()); outState.putBoolean(MapboxConstants.STATE_MY_LOCATION_ENABLED, isMyLocationEnabled()); - outState.putBoolean(MapboxConstants.STATE_MY_TRACKING_MODE_DISMISS_FOR_CAMERA, - isDismissTrackingModesForCameraPositionChange()); } void onRestoreInstanceState(Bundle savedInstanceState) { @@ -77,8 +74,6 @@ void onRestoreInstanceState(Bundle savedInstanceState) { MapboxConstants.STATE_MY_LOCATION_TRACKING_DISMISS, true)); setDismissBearingTrackingOnGesture(savedInstanceState.getBoolean( MapboxConstants.STATE_MY_BEARING_TRACKING_DISMISS, true)); - setDismissTrackingModeForCameraPositionChange(savedInstanceState.getBoolean( - MapboxConstants.STATE_MY_TRACKING_MODE_DISMISS_FOR_CAMERA, true)); } /** @@ -259,15 +254,16 @@ public boolean isScrollGestureCurrentlyEnabled() { } /** - * Reset the tracking modes as necessary. Location tracking is reset if the map center is changed, - * bearing tracking if there is a rotation. + * Reset the tracking modes as necessary. Location tracking is reset if the map center is changed and not from + * location, bearing tracking if there is a rotation. * - * @param translate true if translation - * @param rotate true if rotation + * @param translate true if translation + * @param rotate true if rotation + * @param isFromLocation true if from location */ - void resetTrackingModesIfRequired(boolean translate, boolean rotate) { + void resetTrackingModesIfRequired(boolean translate, boolean rotate, boolean isFromLocation) { // if tracking is on, and we should dismiss tracking with gestures, and this is a scroll action, turn tracking off - if (translate && !isLocationTrackingDisabled() && isDismissLocationTrackingOnGesture()) { + if (translate && !isLocationTrackingDisabled() && isDismissLocationTrackingOnGesture() && !isFromLocation) { setMyLocationTrackingMode(MyLocationTracking.TRACKING_NONE); } @@ -280,38 +276,18 @@ void resetTrackingModesIfRequired(boolean translate, boolean rotate) { /** * Reset the tracking modes as necessary. Animated camera position changes can reset the underlying tracking modes. * - * @param cameraPosition the changed camera position + * @param currentCameraPosition the current camera position + * @param targetCameraPosition the changed camera position + * @param isFromLocation true if from location */ - void resetTrackingModesIfRequired(CameraPosition cameraPosition) { - if (isDismissTrackingModesForCameraPositionChange()) { - resetTrackingModesIfRequired(cameraPosition.target != null, false); + void resetTrackingModesIfRequired(CameraPosition currentCameraPosition, CameraPosition targetCameraPosition, + boolean isFromLocation) { + if (currentCameraPosition.target != null) { + resetTrackingModesIfRequired(!currentCameraPosition.target.equals(targetCameraPosition.target), false, + isFromLocation); } } - /** - * Returns if a animation allows to dismiss a tracking mode. - *

    - * By default this is set to true. - *

    - * - * @return True if camera animations will allow to dismiss a tracking mode - */ - public boolean isDismissTrackingModesForCameraPositionChange() { - return isResetTrackingWithCameraPositionChange; - } - - /** - * Sets a flag to allow animated camera position changes to dismiss a tracking mode. - *

    - *

    - *

    - * - * @param willAllowToDismiss True will allow animated camera changes dismiss a trackig mode - */ - public void setDismissTrackingModeForCameraPositionChange(boolean willAllowToDismiss) { - isResetTrackingWithCameraPositionChange = willAllowToDismiss; - } - Location getMyLocation() { return myLocationView.getLocation(); } 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 af44a08a814..7f44e0de070 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 @@ -94,7 +94,7 @@ public void onMapChanged(@MapView.MapChange int change) { final void moveCamera(MapboxMap mapboxMap, CameraUpdate update, MapboxMap.CancelableCallback callback) { CameraPosition cameraPosition = update.getCameraPosition(mapboxMap); if (!cameraPosition.equals(this.cameraPosition)) { - trackingSettings.resetTrackingModesIfRequired(cameraPosition); + trackingSettings.resetTrackingModesIfRequired(this.cameraPosition, cameraPosition, false); cancelTransitions(); cameraChangeDispatcher.onCameraMoveStarted(OnCameraMoveStartedListener.REASON_API_ANIMATION); mapView.jumpTo(cameraPosition.bearing, cameraPosition.target, cameraPosition.tilt, cameraPosition.zoom); @@ -107,10 +107,10 @@ final void moveCamera(MapboxMap mapboxMap, CameraUpdate update, MapboxMap.Cancel @UiThread final void easeCamera(MapboxMap mapboxMap, CameraUpdate update, int durationMs, boolean easingInterpolator, - final MapboxMap.CancelableCallback callback) { + final MapboxMap.CancelableCallback callback, boolean isDismissable) { CameraPosition cameraPosition = update.getCameraPosition(mapboxMap); if (!cameraPosition.equals(this.cameraPosition)) { - trackingSettings.resetTrackingModesIfRequired(cameraPosition); + trackingSettings.resetTrackingModesIfRequired(this.cameraPosition, cameraPosition, isDismissable); cancelTransitions(); cameraChangeDispatcher.onCameraMoveStarted(OnCameraMoveStartedListener.REASON_API_ANIMATION); @@ -118,7 +118,6 @@ final void easeCamera(MapboxMap mapboxMap, CameraUpdate update, int durationMs, cameraCancelableCallback = callback; mapView.addOnMapChangedListener(this); } - mapView.easeTo(cameraPosition.bearing, cameraPosition.target, durationMs, cameraPosition.tilt, cameraPosition.zoom, easingInterpolator); } @@ -129,7 +128,7 @@ final void animateCamera(MapboxMap mapboxMap, CameraUpdate update, int durationM final MapboxMap.CancelableCallback callback) { CameraPosition cameraPosition = update.getCameraPosition(mapboxMap); if (!cameraPosition.equals(this.cameraPosition)) { - trackingSettings.resetTrackingModesIfRequired(cameraPosition); + trackingSettings.resetTrackingModesIfRequired(this.cameraPosition, cameraPosition, false); cancelTransitions(); cameraChangeDispatcher.onCameraMoveStarted(OnCameraMoveStartedListener.REASON_API_ANIMATION); 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 338d29c87cc..f5ef46a5d3d 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 @@ -485,19 +485,8 @@ public void setMyLocationTrackingMode(@MyLocationTracking.Mode int myLocationTra if (location != null) { if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) { // center map directly - mapboxMap.getTrackingSettings().setDismissTrackingModeForCameraPositionChange(false); mapboxMap.easeCamera(CameraUpdateFactory.newLatLng(new LatLng(location)), 0, false /*linear interpolator*/, - new MapboxMap.CancelableCallback() { - @Override - public void onCancel() { - - } - - @Override - public void onFinish() { - mapboxMap.getTrackingSettings().setDismissTrackingModeForCameraPositionChange(true); - } - }); + null, true); } else { // do not use interpolated location from tracking mode latLng = null; @@ -663,19 +652,8 @@ public void onSensorChanged(SensorEvent event) { private void rotateCamera(float rotation) { CameraPosition.Builder builder = new CameraPosition.Builder(); builder.bearing(rotation); - mapboxMap.getTrackingSettings().setDismissTrackingModeForCameraPositionChange(false); mapboxMap.easeCamera(CameraUpdateFactory.newCameraPosition(builder.build()), COMPASS_UPDATE_RATE_MS, - false /*linear interpolator*/, new MapboxMap.CancelableCallback() { - @Override - public void onCancel() { - - } - - @Override - public void onFinish() { - mapboxMap.getTrackingSettings().setDismissTrackingModeForCameraPositionChange(true); - } - }); + false /*linear interpolator*/, null); } @Override @@ -750,7 +728,7 @@ void updateAccuracy(@NonNull Location location) { abstract void invalidate(); } - private class MyLocationTrackingBehavior extends MyLocationBehavior implements MapboxMap.CancelableCallback { + private class MyLocationTrackingBehavior extends MyLocationBehavior { @Override void updateLatLng(@NonNull Location location) { @@ -789,10 +767,9 @@ void updateLatLng(@NonNull Location location) { // accuracy updateAccuracy(location); - // disable dismiss of tracking settings, enabled in #onFinish - mapboxMap.getTrackingSettings().setDismissTrackingModeForCameraPositionChange(false); // ease to new camera position with a linear interpolator - mapboxMap.easeCamera(CameraUpdateFactory.newCameraPosition(builder.build()), animationDuration, false, this); + mapboxMap.easeCamera(CameraUpdateFactory.newCameraPosition(builder.build()), animationDuration, false, null, + true); } @Override @@ -803,22 +780,6 @@ void invalidate() { screenLocation = new PointF(x, y); MyLocationView.this.invalidate(); } - - @Override - public void onCancel() { - //no op - } - - @Override - public void onFinish() { - // Posting to end message queue to avoid race condition #8560 - post(new Runnable() { - @Override - public void run() { - mapboxMap.getTrackingSettings().setDismissTrackingModeForCameraPositionChange(true); - } - }); - } } private class MyLocationShowBehavior extends MyLocationBehavior { From b2f277ac620cc41883dad8e06bb45458c18ffe72 Mon Sep 17 00:00:00 2001 From: Pablo Guardiola Date: Thu, 1 Jun 2017 14:10:18 +0200 Subject: [PATCH 59/62] add null check in custom location engine activity to prevent null pointer exception (#9159) --- .../testapp/activity/userlocation/MockLocationEngine.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 b87c723fda7..b02b35b0d02 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 void requestLocationUpdates() { @Override public void removeLocationUpdates() { - handler.removeCallbacksAndMessages(null); + if (handler != null) { + handler.removeCallbacksAndMessages(null); + } } private Location getNextLocation() { From 0d171dbe0e2d172190c21f98fb2cc4a1819ed9a2 Mon Sep 17 00:00:00 2001 From: Bruno de Oliveira Abinader Date: Thu, 1 Jun 2017 00:58:03 +0300 Subject: [PATCH 60/62] [core] Make TransformState LatLngBounds optional --- include/mbgl/map/map.hpp | 4 ++-- src/mbgl/map/map.cpp | 4 ++-- src/mbgl/map/transform.cpp | 7 ++++--- src/mbgl/map/transform.hpp | 2 +- src/mbgl/map/transform_state.cpp | 17 +++++++++++------ src/mbgl/map/transform_state.hpp | 7 ++++--- test/map/transform.test.cpp | 10 +++++++--- 7 files changed, 31 insertions(+), 20 deletions(-) diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp index 1c0c2c544b2..8072eda7ddd 100644 --- a/include/mbgl/map/map.hpp +++ b/include/mbgl/map/map.hpp @@ -109,8 +109,8 @@ class Map : private util::noncopyable { void resetZoom(); // Bounds - void setLatLngBounds(const LatLngBounds&); - LatLngBounds getLatLngBounds() const; + void setLatLngBounds(optional); + optional getLatLngBounds() const; void setMinZoom(double); double getMinZoom() const; void setMaxZoom(double); diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index 52ac323df4a..181370dc142 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -620,11 +620,11 @@ void Map::resetZoom() { #pragma mark - Bounds -LatLngBounds Map::getLatLngBounds() const { +optional Map::getLatLngBounds() const { return impl->transform.getState().getLatLngBounds(); } -void Map::setLatLngBounds(const LatLngBounds& bounds) { +void Map::setLatLngBounds(optional bounds) { impl->cameraMutated = true; impl->transform.setLatLngBounds(bounds); impl->onUpdate(Update::Repaint); diff --git a/src/mbgl/map/transform.cpp b/src/mbgl/map/transform.cpp index 9f3a2a83d25..20e29db6ac7 100644 --- a/src/mbgl/map/transform.cpp +++ b/src/mbgl/map/transform.cpp @@ -406,10 +406,11 @@ double Transform::getZoom() const { #pragma mark - Bounds -void Transform::setLatLngBounds(const LatLngBounds& bounds) { - if (bounds.valid()) { - state.setLatLngBounds(bounds); +void Transform::setLatLngBounds(optional bounds) { + if (bounds && !bounds->valid()) { + throw std::runtime_error("failed to set bounds: bounds are invalid"); } + state.setLatLngBounds(bounds); } void Transform::setMinZoom(const double minZoom) { diff --git a/src/mbgl/map/transform.hpp b/src/mbgl/map/transform.hpp index af2f8216027..749228bdf5e 100644 --- a/src/mbgl/map/transform.hpp +++ b/src/mbgl/map/transform.hpp @@ -56,7 +56,7 @@ class Transform : private util::noncopyable { // Bounds - void setLatLngBounds(const LatLngBounds&); + void setLatLngBounds(optional); void setMinZoom(double); void setMaxZoom(double); void setMinPitch(double); diff --git a/src/mbgl/map/transform_state.cpp b/src/mbgl/map/transform_state.cpp index 8c3c70feecf..bbf7e22b310 100644 --- a/src/mbgl/map/transform_state.cpp +++ b/src/mbgl/map/transform_state.cpp @@ -142,12 +142,14 @@ double TransformState::getZoomFraction() const { #pragma mark - Bounds -void TransformState::setLatLngBounds(const LatLngBounds& bounds_) { - bounds = bounds_; - setLatLngZoom(getLatLng(LatLng::Unwrapped), getZoom()); +void TransformState::setLatLngBounds(optional bounds_) { + if (bounds_ != bounds) { + bounds = bounds_; + setLatLngZoom(getLatLng(LatLng::Unwrapped), getZoom()); + } } -LatLngBounds TransformState::getLatLngBounds() const { +optional TransformState::getLatLngBounds() const { return bounds; } @@ -350,8 +352,11 @@ void TransformState::moveLatLng(const LatLng& latLng, const ScreenCoordinate& an setLatLngZoom(Projection::unproject(centerCoord + latLngCoord - anchorCoord, scale), getZoom()); } -void TransformState::setLatLngZoom(const LatLng &latLng, double zoom) { - const LatLng constrained = bounds.constrain(latLng); +void TransformState::setLatLngZoom(const LatLng& latLng, double zoom) { + LatLng constrained = latLng; + if (bounds) { + constrained = bounds->constrain(latLng); + } double newScale = zoomScale(zoom); const double newWorldSize = newScale * util::tileSize; diff --git a/src/mbgl/map/transform_state.hpp b/src/mbgl/map/transform_state.hpp index d0bf455ead0..e6464aeaccf 100644 --- a/src/mbgl/map/transform_state.hpp +++ b/src/mbgl/map/transform_state.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -50,8 +51,8 @@ class TransformState { double getZoomFraction() const; // Bounds - void setLatLngBounds(const LatLngBounds&); - LatLngBounds getLatLngBounds() const; + void setLatLngBounds(optional); + optional getLatLngBounds() const; void setMinZoom(double); double getMinZoom() const; void setMaxZoom(double); @@ -89,7 +90,7 @@ class TransformState { bool rotatedNorth() const; void constrain(double& scale, double& x, double& y) const; - LatLngBounds bounds = LatLngBounds::world(); + optional bounds; // Limit the amount of zooming possible on the map. double min_scale = std::pow(2, 0); diff --git a/test/map/transform.test.cpp b/test/map/transform.test.cpp index 40cae09d8ba..aa49d250b6e 100644 --- a/test/map/transform.test.cpp +++ b/test/map/transform.test.cpp @@ -537,12 +537,16 @@ TEST(Transform, LatLngBounds) { transform.setLatLngZoom({ 0, 0 }, transform.getState().getMaxZoom()); // Default bounds. - ASSERT_EQ(transform.getState().getLatLngBounds(), LatLngBounds::world()); + ASSERT_EQ(transform.getState().getLatLngBounds(), optional {}); ASSERT_EQ(transform.getLatLng(), nullIsland); // Invalid bounds. - transform.setLatLngBounds(LatLngBounds::empty()); - ASSERT_EQ(transform.getState().getLatLngBounds(), LatLngBounds::world()); + try { + transform.setLatLngBounds(LatLngBounds::empty()); + ASSERT_TRUE(false) << "Should throw"; + } catch (...) { + ASSERT_EQ(transform.getState().getLatLngBounds(), optional {}); + } transform.setLatLng(sanFrancisco); ASSERT_EQ(transform.getLatLng(), sanFrancisco); From 012e88cc9b069f62824d9072ca3ede6a3c37d04b Mon Sep 17 00:00:00 2001 From: Jesse Bounds Date: Thu, 1 Jun 2017 10:53:02 -0700 Subject: [PATCH 61/62] [ios] Make annotation view rotation alignment configurable (#9147) This commit adds `rotatesWithMap` property on `MGLAnnotationView`. This property, when set to `YES` fixes the annotation to a map such that view follows map's rotation angle. This is useful when user wants to display rotation-dependent annotations (e.g. sector lights). --- platform/ios/CHANGELOG.md | 1 + platform/ios/src/MGLAnnotationView.h | 13 +++++++++++++ platform/ios/src/MGLAnnotationView.mm | 24 ++++++++++++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 7228a946493..c57063707a9 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -26,6 +26,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT ### 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)) diff --git a/platform/ios/src/MGLAnnotationView.h b/platform/ios/src/MGLAnnotationView.h index 9b17f05a6e4..2802d31b052 100644 --- a/platform/ios/src/MGLAnnotationView.h +++ b/platform/ios/src/MGLAnnotationView.h @@ -171,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 78e38f10cb9..94d06494130 100644 --- a/platform/ios/src/MGLAnnotationView.mm +++ b/platform/ios/src/MGLAnnotationView.mm @@ -11,6 +11,7 @@ @interface MGLAnnotationView () @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; @@ -53,6 +54,7 @@ - (instancetype)initWithCoder:(NSCoder *)decoder { _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"]; @@ -66,6 +68,7 @@ - (void)encodeWithCoder:(NSCoder *)coder { [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"]; @@ -109,6 +112,7 @@ - (void)setCenter:(CGPoint)center super.center = center; [self updateScaleTransformForViewingDistance]; + [self updateRotateTransform]; } - (void)setScalesWithViewingDistance:(BOOL)scalesWithViewingDistance @@ -157,6 +161,26 @@ - (void)updateScaleTransformForViewingDistance } } +- (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 From da532007043b1827e83686faca0de2a45c4ef730 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Fri, 2 Jun 2017 12:39:35 +0200 Subject: [PATCH 62/62] [android] - validate if gestures should execute (#9173) --- .../mapbox/mapboxsdk/maps/MapGestureDetector.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) 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 e60f9b1d1e6..33e13c5ecc3 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 @@ -383,6 +383,14 @@ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float ve // Called for drags @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { + if (!trackingSettings.isScrollGestureCurrentlyEnabled()) { + return false; + } + + if (dragStarted) { + return false; + } + if (!scrollInProgress) { scrollInProgress = true; @@ -393,13 +401,6 @@ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float d getLocationFromGesture(e1.getX(), e1.getY()), MapboxEvent.GESTURE_PAN_START, transform)); } - if (!trackingSettings.isScrollGestureCurrentlyEnabled()) { - return false; - } - - if (dragStarted) { - return false; - } // reset tracking if needed trackingSettings.resetTrackingModesIfRequired(true, false, false);