summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnsis Brammanis <brammanis@gmail.com>2015-05-28 20:01:15 -0400
committerAnsis Brammanis <brammanis@gmail.com>2015-05-28 20:01:15 -0400
commitff6a5ce5bc674e17a24c3f18b305d5dd2f0e21b0 (patch)
tree405ec719109186590311262045ff1651b06e9506
parentfd19eb79056b8e62f037c0ae4532f4f7cc970580 (diff)
parent6c166b564ebb3acefb56bb4d39be4813851db4a7 (diff)
downloadqtlocation-mapboxgl-ff6a5ce5bc674e17a24c3f18b305d5dd2f0e21b0.tar.gz
Merge remote-tracking branch 'origin/master' into new-labelling
Conflicts: src/mbgl/map/source.cpp src/mbgl/map/source.hpp src/mbgl/map/tile_data.cpp src/mbgl/map/tile_parser.cpp src/mbgl/map/vector_tile_data.cpp src/mbgl/renderer/painter.cpp src/mbgl/renderer/symbol_bucket.cpp src/mbgl/text/glyph.hpp src/mbgl/text/glyph_store.cpp src/mbgl/text/placement.cpp test/suite
m---------.mason0
-rw-r--r--.travis.yml2
-rw-r--r--README.md27
-rw-r--r--android/cpp/jni.cpp4
-rw-r--r--android/cpp/native_map_view.cpp3
-rw-r--r--android/java/MapboxGLAndroidSDK/build.gradle2
-rw-r--r--android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxgl/views/MapView.java4
-rw-r--r--android/java/MapboxGLAndroidSDKTestApp/build.gradle2
-rw-r--r--bin/render.cpp20
-rw-r--r--gyp/platform-ios.gypi1
-rw-r--r--include/mbgl/ios/MGLAccountManager.h2
-rw-r--r--include/mbgl/ios/MGLMapView+IBAdditions.h3
-rw-r--r--include/mbgl/ios/MGLMapView.h34
-rw-r--r--include/mbgl/map/map.hpp6
-rw-r--r--include/mbgl/platform/darwin/reachability.h41
-rw-r--r--include/mbgl/platform/default/image_reader.hpp2
-rw-r--r--include/mbgl/platform/event.hpp2
-rw-r--r--include/mbgl/platform/log.hpp1
-rw-r--r--include/mbgl/storage/default_file_source.hpp4
-rw-r--r--include/mbgl/storage/file_source.hpp1
-rw-r--r--include/mbgl/storage/resource.hpp10
-rw-r--r--include/mbgl/util/exception.hpp20
-rw-r--r--include/mbgl/util/exclusive.hpp30
-rw-r--r--include/mbgl/util/std.hpp23
-rw-r--r--ios/MapboxGL.podspec2
-rw-r--r--ios/app/MBXAppDelegate.m32
-rw-r--r--linux/main.cpp20
-rw-r--r--macosx/main.mm17
-rw-r--r--platform/darwin/http_request_nsurl.mm28
-rw-r--r--platform/darwin/image.mm3
-rw-r--r--platform/darwin/reachability.m234
-rw-r--r--platform/default/asset_request_fs.cpp9
-rw-r--r--platform/default/asset_request_zip.cpp7
-rw-r--r--platform/default/headless_view.cpp7
-rw-r--r--platform/default/http_request_curl.cpp10
-rw-r--r--platform/default/image.cpp3
-rw-r--r--platform/default/image_reader.cpp4
-rw-r--r--platform/default/sqlite_cache.cpp16
-rw-r--r--platform/ios/MGLAccountManager.m23
-rw-r--r--platform/ios/MGLAccountManager_Private.h10
-rw-r--r--platform/ios/MGLMapView.mm406
-rw-r--r--platform/ios/MGLMapboxEvents.h1
-rw-r--r--platform/ios/MGLMapboxEvents.m60
-rw-r--r--src/mbgl/geometry/glyph_atlas.cpp13
-rw-r--r--src/mbgl/geometry/line_atlas.cpp3
-rw-r--r--src/mbgl/geometry/sprite_atlas.cpp20
-rw-r--r--src/mbgl/map/annotation.cpp5
-rw-r--r--src/mbgl/map/annotation.hpp1
-rw-r--r--src/mbgl/map/map.cpp16
-rw-r--r--src/mbgl/map/map_context.cpp39
-rw-r--r--src/mbgl/map/map_context.hpp3
-rw-r--r--src/mbgl/map/map_data.hpp10
-rw-r--r--src/mbgl/map/resource_loader.cpp41
-rw-r--r--src/mbgl/map/resource_loader.hpp10
-rw-r--r--src/mbgl/map/source.cpp61
-rw-r--r--src/mbgl/map/source.hpp9
-rw-r--r--src/mbgl/map/sprite.cpp62
-rw-r--r--src/mbgl/map/sprite.hpp6
-rw-r--r--src/mbgl/map/tile_data.cpp22
-rw-r--r--src/mbgl/map/tile_data.hpp13
-rw-r--r--src/mbgl/map/tile_parser.cpp7
-rw-r--r--src/mbgl/map/transform.cpp2
-rw-r--r--src/mbgl/map/vector_tile_data.cpp9
-rw-r--r--src/mbgl/renderer/fill_bucket.cpp5
-rw-r--r--src/mbgl/renderer/line_bucket.cpp3
-rw-r--r--src/mbgl/renderer/painter.cpp27
-rw-r--r--src/mbgl/renderer/painter_fill.cpp1
-rw-r--r--src/mbgl/renderer/painter_raster.cpp1
-rw-r--r--src/mbgl/renderer/symbol_bucket.cpp11
-rw-r--r--src/mbgl/shader/shader.cpp6
-rw-r--r--src/mbgl/storage/default_file_source.cpp27
-rw-r--r--src/mbgl/storage/request.cpp5
-rw-r--r--src/mbgl/style/style.cpp3
-rw-r--r--src/mbgl/style/style_parser.cpp1
-rw-r--r--src/mbgl/text/font_stack.cpp140
-rw-r--r--src/mbgl/text/font_stack.hpp28
-rw-r--r--src/mbgl/text/glyph.hpp14
-rw-r--r--src/mbgl/text/glyph_pbf.cpp115
-rw-r--r--src/mbgl/text/glyph_pbf.hpp52
-rw-r--r--src/mbgl/text/glyph_store.cpp309
-rw-r--r--src/mbgl/text/glyph_store.hpp85
-rw-r--r--src/mbgl/util/raster.cpp3
-rw-r--r--src/mbgl/util/run_loop.hpp70
-rw-r--r--src/mbgl/util/thread.hpp40
-rw-r--r--src/mbgl/util/worker.cpp4
-rw-r--r--test/api/repeated_render.cpp6
-rw-r--r--test/api/set_style.cpp2
-rw-r--r--test/fixtures/resources/glyphs.pbfbin0 -> 74722 bytes
-rw-r--r--test/fixtures/resources/source.json1
-rw-r--r--test/fixtures/resources/sprite.json1
-rw-r--r--test/fixtures/resources/sprite.pngbin0 -> 39041 bytes
-rw-r--r--test/fixtures/resources/style.json31
-rw-r--r--test/fixtures/resources/vector.pbfbin0 -> 103021 bytes
-rw-r--r--test/headless/headless.cpp3
-rw-r--r--test/ios/App-Info.plist6
-rw-r--r--test/ios/Images.xcassets/AppIcon.appiconset/Contents.json162
-rw-r--r--test/ios/Images.xcassets/LaunchImage.launchimage/Contents.json53
-rw-r--r--test/ios/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.pngbin0 -> 184 bytes
-rw-r--r--test/ios/Images.xcassets/LaunchImage.launchimage/Default@2x.pngbin0 -> 170 bytes
-rw-r--r--test/ios/LaunchScreen.xib45
-rw-r--r--test/ios/MGLTAppDelegate.m2
-rw-r--r--test/ios/MGLTViewController.m3
-rw-r--r--test/ios/MetricsTests.m6
-rw-r--r--test/ios/ios-tests.xcodeproj/project.pbxproj8
-rw-r--r--test/miscellaneous/bilinear.cpp3
-rw-r--r--test/miscellaneous/clip_ids.cpp1
-rw-r--r--test/miscellaneous/thread.cpp102
-rw-r--r--test/resources/mock_file_source.cpp82
-rw-r--r--test/resources/mock_file_source.hpp40
-rw-r--r--test/resources/mock_view.hpp21
-rw-r--r--test/resources/resource_loader.cpp204
-rw-r--r--test/storage/database.cpp22
-rw-r--r--test/test.gypi8
113 files changed, 2096 insertions, 1154 deletions
diff --git a/.mason b/.mason
-Subproject c43967404c84d020d63f6aba450d97770dfeaeb
+Subproject ace19da711199cb0283c6243b435575fbec16a9
diff --git a/.travis.yml b/.travis.yml
index f08aa81491..1ac66b3862 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -94,7 +94,7 @@ script:
after_failure:
- "[ -f ./scripts/${FLAVOR}/after_failure.sh ] && ./scripts/${FLAVOR}/after_failure.sh"
-after_failure:
+after_script:
- "[ -f ./scripts/${FLAVOR}/after_script.sh ] && ./scripts/${FLAVOR}/after_script.sh"
notifications:
diff --git a/README.md b/README.md
index 6b15f596c6..123c507c98 100644
--- a/README.md
+++ b/README.md
@@ -28,7 +28,7 @@ Be sure to pull down all submodules first:
## OS X
-Prerequisites include the Boost headers (for routines provided therein) and ImageMagick (for tests). To install all prerequisites, use [Homebrew](http://brew.sh/) and type `brew install pkg-config boost imagemagick`.
+ImageMagick is required for tests. It's recommended to install it with [Homebrew](http://brew.sh/) via `brew install imagemagick`.
To create projects, you can run:
@@ -58,7 +58,6 @@ pod 'MapboxGL'
#### Manually
-1. Use [Homebrew](http://brew.sh/) to install Boost headers: `brew install boost`.
1. Install [appledoc](http://appledoc.gentlebytes.com/appledoc/) for API docs generation. We recommend [`2.2v963`](https://github.com/tomaz/appledoc/releases/tag/v2.2-963), which currently isn't available in Homebrew.
1. Run `make ipackage`. The packaging script will produce the statically-linked `libMapboxGL.a`, `MapboxGL.bundle` for resources, a `Headers` folder, and a `Docs` folder with HTML API documentation.
1. Copy the contents of `build/ios/pkg/static` into your project. It should happen automatically, but ensure that:
@@ -94,7 +93,7 @@ Target iOS: 7.0 through latest 8.x.
We are using Ubuntu for development. While the software should work on other distributions as well, we are not providing explicit build instructions here.
-Install GCC 4.9+ if you are running Ubuntu 13.10 or older. Alternatively, you can also use Clang 3.5+.
+Install GCC 4.9+ if you are running Ubuntu 14.04 or older. Alternatively, you can also use [Clang 3.5+](http://llvm.org/apt/).
sudo add-apt-repository --yes ppa:ubuntu-toolchain-r/test
sudo apt-get update
@@ -105,7 +104,8 @@ Ensure you have git and other build essentials:
sudo apt-get install curl git build-essential zlib1g-dev automake \
libtool xutils-dev make cmake pkg-config python-pip \
- libcurl4-openssl-dev libpng-dev libsqlite3-dev
+ libcurl4-openssl-dev libpng-dev libsqlite3-dev \
+ libllvm3.4
Install glfw3 dependencies:
@@ -114,16 +114,6 @@ Install glfw3 dependencies:
x11proto-xf86vidmode-dev libxxf86vm-dev \
libxcursor-dev libxinerama-dev
-Finally, install Boost. If you're running Ubuntu 12.04 or older, you need to install a backport PPA since the version provided by APT doesn't contain Boost Geometry:
-
- sudo add-apt-repository --yes ppa:boost-latest/ppa
- sudo apt-get update
- sudo apt-get install libboost1.55-dev
-
-Otherwise, you can just install
-
- sudo apt-get install libboost-dev libboost-program-options-dev
-
Then, you can then proceed to build the library:
git submodule update --init
@@ -148,9 +138,9 @@ Install Oracle JDK 7 (requires license agreement) from http://www.oracle.com/tec
export JAVA_HOME="/dir/to/jdk1.7.0_71"
-Install the [Android NDK Revision 10d](https://developer.android.com/tools/sdk/ndk/index.html).
+Install the [Android NDK Revision 10e](https://developer.android.com/tools/sdk/ndk/index.html).
- export ANDROID_NDK_PATH="/dir/to/android-ndk-r10d"
+ export ANDROID_NDK_PATH="/dir/to/android-ndk-r10e"
Install the Android SDK. We recommend doing this by way of [Android Studio](https://developer.android.com/sdk/installing/studio.html).
@@ -168,7 +158,7 @@ Install Oracle JDK 7+:
brew cask install java
-Install the [Android NDK Revision 10d](https://developer.android.com/tools/sdk/ndk/index.html) for 64-bit OS X:
+Install the [Android NDK Revision 10e](https://developer.android.com/tools/sdk/ndk/index.html) for 64-bit OS X:
brew install android-ndk
@@ -182,7 +172,7 @@ Install [Android Studio](https://developer.android.com/sdk/installing/studio.htm
By default, the SDK will be installed to `/usr/local/opt/android-sdk`. If you open Android Studio at this point, you may get an error message telling you that it can't find a JVM, it's because you installed a custom Java VM from Oracle. Follow [these instructions](http://tools.android.com/recent/androidstudio1rc3_releasecandidate3released) to start Android Studio. You'll wind up setting these environment variables in your .bash_profile or similar:
echo "export ANDROID_HOME=`brew --prefix android-sdk`" >> .bash_profile
- echo "export ANDROID_NDK_PATH=`brew --cellar android-ndk`/r10d" >> .bash_profile
+ echo "export ANDROID_NDK_PATH=`brew --cellar android-ndk`/r10e" >> .bash_profile
# Replace <path to JDK> with something like /Library/Java/JavaVirtualMachines/jdk1.8.0_31.jdk
echo "export JAVA_HOME=<path to JDK>" >> .bash_profile
echo "export STUDIO_JDK=$JAVA_HOME" >> .bash_profile
@@ -253,6 +243,7 @@ Some styles in JSON format are included at `./styles`. See the [style spec](http
## Desktop
+- Press `S` to cycle through bundled styles
- Press `X` to reset the transform
- Press `N` to reset north
- Press `Tab` to toggle debug information
diff --git a/android/cpp/jni.cpp b/android/cpp/jni.cpp
index 2c7e757783..64e72e1499 100644
--- a/android/cpp/jni.cpp
+++ b/android/cpp/jni.cpp
@@ -373,14 +373,14 @@ nativeSetAccessToken(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jstring a
mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetAccessToken");
assert(nativeMapViewPtr != 0);
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
- nativeMapView->getMap().setAccessToken(std_string_from_jstring(env, accessToken));
+ nativeMapView->getFileSource().setAccessToken(std_string_from_jstring(env, accessToken));
}
jstring JNICALL nativeGetAccessToken(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetAccessToken");
assert(nativeMapViewPtr != 0);
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
- return std_string_to_jstring(env, nativeMapView->getMap().getAccessToken());
+ return std_string_to_jstring(env, nativeMapView->getFileSource().getAccessToken());
}
void JNICALL nativeCancelTransitions(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
diff --git a/android/cpp/native_map_view.cpp b/android/cpp/native_map_view.cpp
index 03530aefff..1d25442a55 100644
--- a/android/cpp/native_map_view.cpp
+++ b/android/cpp/native_map_view.cpp
@@ -15,7 +15,6 @@
#include <mbgl/platform/event.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/platform/gl.hpp>
-#include <mbgl/util/std.hpp>
namespace mbgl {
namespace android {
@@ -214,7 +213,7 @@ void NativeMapView::initializeDisplay() {
throw new std::runtime_error("eglChooseConfig() failed");
}
- const std::unique_ptr<EGLConfig[]> configs = mbgl::util::make_unique<EGLConfig[]>(numConfigs);
+ const auto configs = std::make_unique<EGLConfig[]>(numConfigs);
if (!eglChooseConfig(display, configAttribs, configs.get(), numConfigs, &numConfigs)) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglChooseConfig() returned error %d", eglGetError());
throw new std::runtime_error("eglChooseConfig() failed");
diff --git a/android/java/MapboxGLAndroidSDK/build.gradle b/android/java/MapboxGLAndroidSDK/build.gradle
index f0659b8c7b..39e46fcc04 100644
--- a/android/java/MapboxGLAndroidSDK/build.gradle
+++ b/android/java/MapboxGLAndroidSDK/build.gradle
@@ -3,7 +3,7 @@ buildscript {
mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:1.2.2'
+ classpath 'com.android.tools.build:gradle:1.2.3'
classpath 'com.jakewharton.sdkmanager:gradle-plugin:0.12.0'
}
}
diff --git a/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxgl/views/MapView.java b/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxgl/views/MapView.java
index da4b40c4c6..e482c0be5a 100644
--- a/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxgl/views/MapView.java
+++ b/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxgl/views/MapView.java
@@ -149,9 +149,9 @@ public class MapView extends SurfaceView {
// Load the attributes
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MapView, 0, 0);
try {
- double centerLongitude = typedArray.getFloat(R.styleable.MapView_centerLongitude, 0.0f);
double centerLatitude = typedArray.getFloat(R.styleable.MapView_centerLatitude, 0.0f);
- LatLng centerCoordinate = new LatLng(centerLongitude, centerLatitude);
+ double centerLongitude = typedArray.getFloat(R.styleable.MapView_centerLongitude, 0.0f);
+ LatLng centerCoordinate = new LatLng(centerLatitude, centerLongitude);
setCenterCoordinate(centerCoordinate);
setZoomLevel(typedArray.getFloat(R.styleable.MapView_zoomLevel, 0.0f)); // need to set zoom level first because of limitation on rotating when zoomed out
setDirection(typedArray.getFloat(R.styleable.MapView_direction, 0.0f));
diff --git a/android/java/MapboxGLAndroidSDKTestApp/build.gradle b/android/java/MapboxGLAndroidSDKTestApp/build.gradle
index 5f2db91d7e..46c95c21ec 100644
--- a/android/java/MapboxGLAndroidSDKTestApp/build.gradle
+++ b/android/java/MapboxGLAndroidSDKTestApp/build.gradle
@@ -3,7 +3,7 @@ buildscript {
mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:1.2.2'
+ classpath 'com.android.tools.build:gradle:1.2.3'
classpath 'com.jakewharton.sdkmanager:gradle-plugin:0.12.0'
}
}
diff --git a/bin/render.cpp b/bin/render.cpp
index 33e216f52a..5ae83c1d48 100644
--- a/bin/render.cpp
+++ b/bin/render.cpp
@@ -1,7 +1,6 @@
#include <mbgl/map/map.hpp>
#include <mbgl/map/still_image.hpp>
#include <mbgl/util/image.hpp>
-#include <mbgl/util/std.hpp>
#include <mbgl/util/io.hpp>
#include <mbgl/platform/default/headless_view.hpp>
@@ -81,14 +80,14 @@ int main(int argc, char *argv[]) {
}
}
- HeadlessView view;
- Map map(view, fileSource, MapMode::Still);
-
// Set access token if present
if (token.size()) {
- map.setAccessToken(std::string(token));
+ fileSource.setAccessToken(std::string(token));
}
+ HeadlessView view;
+ Map map(view, fileSource, MapMode::Still);
+
map.setStyleJSON(style, ".");
map.setClasses(classes);
@@ -111,7 +110,16 @@ int main(int argc, char *argv[]) {
util::write_file(output, png);
});
- map.renderStill([async](std::unique_ptr<const StillImage> image) {
+ map.renderStill([async](std::exception_ptr error, std::unique_ptr<const StillImage> image) {
+ try {
+ if (error) {
+ std::rethrow_exception(error);
+ }
+ } catch(std::exception& e) {
+ std::cout << "Error: " << e.what() << std::endl;
+ exit(1);
+ }
+
async->data = const_cast<StillImage *>(image.release());
uv_async_send(async);
});
diff --git a/gyp/platform-ios.gypi b/gyp/platform-ios.gypi
index 0034831a91..0ec67786a7 100644
--- a/gyp/platform-ios.gypi
+++ b/gyp/platform-ios.gypi
@@ -26,6 +26,7 @@
'../platform/ios/MGLFileCache.h',
'../platform/ios/MGLFileCache.mm',
'../include/mbgl/ios/MGLAccountManager.h',
+ '../platform/ios/MGLAccountManager_Private.h',
'../platform/ios/MGLAccountManager.m',
'../include/mbgl/ios/MGLAnnotation.h',
'../include/mbgl/ios/MGLUserLocation.h',
diff --git a/include/mbgl/ios/MGLAccountManager.h b/include/mbgl/ios/MGLAccountManager.h
index 7ec4135f18..6b127543ee 100644
--- a/include/mbgl/ios/MGLAccountManager.h
+++ b/include/mbgl/ios/MGLAccountManager.h
@@ -21,7 +21,7 @@
/** Certain Mapbox plans require the collection of user metrics. If you aren't using a preference switch in an existing or new `Settings.bundle` in your application, set this value to `YES` to indicate that you are providing a metrics opt-out for users within your app's interface directly.
* @param showsOptOut Whether your application's interface provides a user opt-out preference. The default value is `NO`, meaning a `Settings.bundle` is expected for providing a user opt-out preference. */
-+ (void)setMapboxMetricsEnabledSettingShownInApp:(BOOL)showsOptOut;
++ (void)setMapboxMetricsEnabledSettingShownInApp:(BOOL)showsOptOut __attribute__((unavailable("Set MGLMapboxMetricsEnabledSettingShownInApp in Info.plist.")));
/** Whether in-app user metrics opt-out is configured. If set to the default value of `NO`, a user opt-out preference is expected in a `Settings.bundle` that shows in the application's section within the system Settings app. */
+ (BOOL)mapboxMetricsEnabledSettingShownInApp;
diff --git a/include/mbgl/ios/MGLMapView+IBAdditions.h b/include/mbgl/ios/MGLMapView+IBAdditions.h
index df9a19a6b7..23762cca94 100644
--- a/include/mbgl/ios/MGLMapView+IBAdditions.h
+++ b/include/mbgl/ios/MGLMapView+IBAdditions.h
@@ -8,8 +8,7 @@
// inspectables declared in MGLMapView.h are always sorted before those in
// MGLMapView+IBAdditions.h, due to ASCII sort order.
-@property (nonatomic) IBInspectable NSString *accessToken;
-@property (nonatomic) IBInspectable NSString *mapID;
+@property (nonatomic) IBInspectable NSString *styleID;
// Convenience properties related to the initial viewport. These properties
// are not meant to be used outside of Interface Builder. latitude and longitude
diff --git a/include/mbgl/ios/MGLMapView.h b/include/mbgl/ios/MGLMapView.h
index 9d7b5c9019..0ccb8225bb 100644
--- a/include/mbgl/ios/MGLMapView.h
+++ b/include/mbgl/ios/MGLMapView.h
@@ -20,30 +20,25 @@ IB_DESIGNABLE
/** @name Initializing a Map View */
-/** Initialize a map view with the default style, given frame, and access token set in MapboxGL singleton.
-* @param frame The frame with which to initialize the map view.
-* @return An initialized map view, or `nil` if the map view was unable to be initialized. */
+/** Initializes and returns a newly allocated map view with the specified frame and the default style.
+* @param frame The frame for the view, measured in points.
+* @return An initialized map view. */
- (instancetype)initWithFrame:(CGRect)frame;
+- (instancetype)initWithFrame:(CGRect)frame accessToken:(NSString *)accessToken __attribute__((unavailable("Use -initWithFrame:. Set MGLMapboxAccessToken in the Info.plist or call +[MGLAccountManager setAccessToken:].")));
-/** Initialize a map view with the default style and a given frame and access token.
-* @param frame The frame with which to initialize the map view.
-* @param accessToken A Mapbox API access token.
-* @return An initialized map view, or `nil` if the map view was unable to be initialized. */
-- (instancetype)initWithFrame:(CGRect)frame accessToken:(NSString *)accessToken;
-
-/** Initialize a map view with a given frame, access token, and style URL.
- * @param frame The frame with which to initialize the map view.
- * @param accessToken A Mapbox API access token.
- * @param styleURL The map style URL to use. Can be either an HTTP/HTTPS URL or a Mapbox map ID style URL (`mapbox://<user.style>`).
- * @return An initialized map view, or `nil` if the map view was unable to be initialized. */
-- (instancetype)initWithFrame:(CGRect)frame accessToken:(NSString *)accessToken styleURL:(NSURL *)styleURL;
+/** Initializes and returns a newly allocated map view with the specified frame and style URL.
+* @param frame The frame for the view, measured in points.
+* @param styleURL The map style URL to use. Can be either an HTTP/HTTPS URL or a Mapbox map ID style URL (`mapbox://<user.style>`).
+* @return An initialized map view. */
+- (instancetype)initWithFrame:(CGRect)frame styleURL:(NSURL *)styleURL;
+- (instancetype)initWithFrame:(CGRect)frame accessToken:(NSString *)accessToken styleURL:(NSURL *)styleURL __attribute__((unavailable("Use -initWithFrame:styleURL:. Set MGLMapboxAccessToken in the Info.plist or call +[MGLAccountManager setAccessToken:].")));
#pragma mark - Authorizing Access
/** @name Authorizing Access */
/** Mapbox API access token for the map view. */
-@property (nonatomic) NSString *accessToken;
+@property (nonatomic) NSString *accessToken __attribute__((unavailable("Use +[MGLAccountManager accessToken] and +[MGLAccountManager setAccessToken:].")));
#pragma mark - Managing Constraints
@@ -162,12 +157,13 @@ IB_DESIGNABLE
/** @name Styling the Map */
-/** Mapbox map ID of the style currently displayed in the receiver, or `nil` if the style does not have a map ID.
+/** Mapbox ID of the style currently displayed in the receiver, or `nil` if the style does not have an ID.
*
-* The style may lack a map ID if it is located at an HTTP, HTTPS, or local file URL. Use `styleURL` to get the URL in these cases.
+* The style may lack an ID if it is located at an HTTP, HTTPS, or local file URL. Use `styleURL` to get the URL in these cases.
*
* To display the default style, set this property to `nil`. */
-@property (nonatomic) NSString *mapID;
+@property (nonatomic) NSString *styleID;
+@property (nonatomic) NSString *mapID __attribute__((unavailable("Use styleID.")));
/** Returns the URLs to the styles bundled with the library. */
- (NSArray *)bundledStyleURLs;
diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp
index ccc963d086..5535dbcc91 100644
--- a/include/mbgl/map/map.hpp
+++ b/include/mbgl/map/map.hpp
@@ -42,7 +42,7 @@ public:
// Register a callback that will get called (on the render thread) when all resources have
// been loaded and a complete render occurs.
- using StillImageCallback = std::function<void(std::unique_ptr<const StillImage>)>;
+ using StillImageCallback = std::function<void(std::exception_ptr, std::unique_ptr<const StillImage>)>;
void renderStill(StillImageCallback callback);
// Triggers a synchronous or asynchronous render.
@@ -99,10 +99,6 @@ public:
uint16_t getWidth() const;
uint16_t getHeight() const;
- // API
- void setAccessToken(const std::string &token);
- std::string getAccessToken() const;
-
// Projection
void getWorldBoundsMeters(ProjectedMeters &sw, ProjectedMeters &ne) const;
void getWorldBoundsLatLng(LatLng &sw, LatLng &ne) const;
diff --git a/include/mbgl/platform/darwin/reachability.h b/include/mbgl/platform/darwin/reachability.h
index 1cf7d2ecea..0101b91d48 100644
--- a/include/mbgl/platform/darwin/reachability.h
+++ b/include/mbgl/platform/darwin/reachability.h
@@ -28,24 +28,6 @@
#import <Foundation/Foundation.h>
#import <SystemConfiguration/SystemConfiguration.h>
-#import <sys/socket.h>
-#import <netinet/in.h>
-#import <netinet6/in6.h>
-#import <arpa/inet.h>
-#import <ifaddrs.h>
-#import <netdb.h>
-
-/**
- * Does ARC support GCD objects?
- * It does if the minimum deployment target is iOS 6+ or Mac OS X 8+
- *
- * @see http://opensource.apple.com/source/libdispatch/libdispatch-228.18/os/object.h
- **/
-#if OS_OBJECT_USE_OBJC
-#define NEEDS_DISPATCH_RETAIN_RELEASE 0
-#else
-#define NEEDS_DISPATCH_RETAIN_RELEASE 1
-#endif
/**
* Create NS_ENUM macro if it does not exist on the targeted version of iOS or OS X.
@@ -65,28 +47,29 @@ typedef NS_ENUM(NSInteger, NetworkStatus) {
ReachableViaWWAN = 1
};
-@class Reachability;
+@class MGLReachability;
+
+typedef void (^NetworkReachable)(MGLReachability * reachability);
+typedef void (^NetworkUnreachable)(MGLReachability * reachability);
-typedef void (^NetworkReachable)(Reachability * reachability);
-typedef void (^NetworkUnreachable)(Reachability * reachability);
-@interface Reachability : NSObject
+@interface MGLReachability : NSObject
@property (nonatomic, copy) NetworkReachable reachableBlock;
@property (nonatomic, copy) NetworkUnreachable unreachableBlock;
-
@property (nonatomic, assign) BOOL reachableOnWWAN;
-+(Reachability*)reachabilityWithHostname:(NSString*)hostname;
+
++(instancetype)reachabilityWithHostname:(NSString*)hostname;
// This is identical to the function above, but is here to maintain
//compatibility with Apples original code. (see .m)
-+(Reachability*)reachabilityWithHostName:(NSString*)hostname;
-+(Reachability*)reachabilityForInternetConnection;
-+(Reachability*)reachabilityWithAddress:(const struct sockaddr_in*)hostAddress;
-+(Reachability*)reachabilityForLocalWiFi;
++(instancetype)reachabilityWithHostName:(NSString*)hostname;
++(instancetype)reachabilityForInternetConnection;
++(instancetype)reachabilityWithAddress:(void *)hostAddress;
++(instancetype)reachabilityForLocalWiFi;
--(Reachability *)initWithReachabilityRef:(SCNetworkReachabilityRef)ref;
+-(instancetype)initWithReachabilityRef:(SCNetworkReachabilityRef)ref;
-(BOOL)startNotifier;
-(void)stopNotifier;
diff --git a/include/mbgl/platform/default/image_reader.hpp b/include/mbgl/platform/default/image_reader.hpp
index 985e4874cd..52a67a2830 100644
--- a/include/mbgl/platform/default/image_reader.hpp
+++ b/include/mbgl/platform/default/image_reader.hpp
@@ -1,11 +1,11 @@
#ifndef MBGL_UTIL_IMAGE_READER_HPP
#define MBGL_UTIL_IMAGE_READER_HPP
-#include <mbgl/util/std.hpp>
#include <mbgl/util/noncopyable.hpp>
// stl
#include <stdexcept>
#include <string>
+#include <memory>
namespace mbgl { namespace util {
diff --git a/include/mbgl/platform/event.hpp b/include/mbgl/platform/event.hpp
index 5fd64119cc..8cdd1d50a9 100644
--- a/include/mbgl/platform/event.hpp
+++ b/include/mbgl/platform/event.hpp
@@ -29,6 +29,7 @@ enum class Event : uint8_t {
ParseStyle,
ParseTile,
Render,
+ ResourceLoader,
Database,
HttpRequest,
Sprite,
@@ -46,6 +47,7 @@ MBGL_DEFINE_ENUM_CLASS(EventClass, Event, {
{ Event::ParseStyle, "ParseStyle" },
{ Event::ParseTile, "ParseTile" },
{ Event::Render, "Render" },
+ { Event::ResourceLoader, "ResourceLoader" },
{ Event::Database, "Database" },
{ Event::HttpRequest, "HttpRequest" },
{ Event::Sprite, "Sprite" },
diff --git a/include/mbgl/platform/log.hpp b/include/mbgl/platform/log.hpp
index d6f3cd1ab4..86a8cdbef1 100644
--- a/include/mbgl/platform/log.hpp
+++ b/include/mbgl/platform/log.hpp
@@ -3,7 +3,6 @@
#include <mbgl/platform/event.hpp>
-#include <mbgl/util/std.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <memory>
diff --git a/include/mbgl/storage/default_file_source.hpp b/include/mbgl/storage/default_file_source.hpp
index 405b04a49c..8cfae03a96 100644
--- a/include/mbgl/storage/default_file_source.hpp
+++ b/include/mbgl/storage/default_file_source.hpp
@@ -15,6 +15,9 @@ public:
DefaultFileSource(FileCache *cache, const std::string &root = "");
~DefaultFileSource() override;
+ void setAccessToken(const std::string& t) { accessToken = t; }
+ std::string getAccessToken() const { return accessToken; }
+
// FileSource API
Request* request(const Resource&, uv_loop_t*, Callback) override;
void cancel(Request*) override;
@@ -23,6 +26,7 @@ public:
class Impl;
private:
const std::unique_ptr<util::Thread<Impl>> thread;
+ std::string accessToken;
};
}
diff --git a/include/mbgl/storage/file_source.hpp b/include/mbgl/storage/file_source.hpp
index 7c6e578a9a..3b19e00788 100644
--- a/include/mbgl/storage/file_source.hpp
+++ b/include/mbgl/storage/file_source.hpp
@@ -5,7 +5,6 @@
#include "resource.hpp"
#include <mbgl/util/noncopyable.hpp>
-#include <mbgl/util/std.hpp>
#include <mbgl/util/util.hpp>
#include <functional>
diff --git a/include/mbgl/storage/resource.hpp b/include/mbgl/storage/resource.hpp
index cfd52caa75..2fcb3b1b78 100644
--- a/include/mbgl/storage/resource.hpp
+++ b/include/mbgl/storage/resource.hpp
@@ -9,10 +9,12 @@ namespace mbgl {
struct Resource {
enum Kind : uint8_t {
Unknown = 0,
- Tile = 1,
- Glyphs = 2,
- Image = 3,
- JSON = 4,
+ Style,
+ Source,
+ Tile,
+ Glyphs,
+ JSON,
+ Image
};
const Kind kind;
diff --git a/include/mbgl/util/exception.hpp b/include/mbgl/util/exception.hpp
index 0b4403270c..da61aa482a 100644
--- a/include/mbgl/util/exception.hpp
+++ b/include/mbgl/util/exception.hpp
@@ -11,6 +11,11 @@ struct Exception : std::runtime_error {
inline Exception(const std::string &msg) : std::runtime_error(msg) {}
};
+struct GlyphRangeLoadingException : Exception {
+ inline GlyphRangeLoadingException(const char *msg) : Exception(msg) {}
+ inline GlyphRangeLoadingException(const std::string &msg) : Exception(msg) {}
+};
+
struct MisuseException : Exception {
inline MisuseException(const char *msg) : Exception(msg) {}
inline MisuseException(const std::string &msg) : Exception(msg) {}
@@ -21,6 +26,21 @@ struct ShaderException : Exception {
inline ShaderException(const std::string &msg) : Exception(msg) {}
};
+struct SourceLoadingException : Exception {
+ inline SourceLoadingException(const char *msg) : Exception(msg) {}
+ inline SourceLoadingException(const std::string &msg) : Exception(msg) {}
+};
+
+struct SpriteLoadingException : Exception {
+ inline SpriteLoadingException(const char *msg) : Exception(msg) {}
+ inline SpriteLoadingException(const std::string &msg) : Exception(msg) {}
+};
+
+struct TileLoadingException : Exception {
+ inline TileLoadingException(const char *msg) : Exception(msg) {}
+ inline TileLoadingException(const std::string &msg) : Exception(msg) {}
+};
+
}
}
diff --git a/include/mbgl/util/exclusive.hpp b/include/mbgl/util/exclusive.hpp
new file mode 100644
index 0000000000..bb3395996b
--- /dev/null
+++ b/include/mbgl/util/exclusive.hpp
@@ -0,0 +1,30 @@
+#ifndef MBGL_UTIL_EXCLUSIVE
+#define MBGL_UTIL_EXCLUSIVE
+
+#include <memory>
+#include <mutex>
+
+
+namespace mbgl {
+namespace util {
+
+template <class T>
+class exclusive {
+public:
+ inline exclusive(T* val, std::unique_ptr<std::lock_guard<std::mutex>> mtx) : ptr(val), lock(std::move(mtx)) {}
+
+ inline T* operator->() { return ptr; }
+ inline const T* operator->() const { return ptr; }
+ inline T* operator*() { return ptr; }
+ inline const T* operator*() const { return ptr; }
+
+private:
+ T *ptr;
+ std::unique_ptr<std::lock_guard<std::mutex>> lock;
+};
+
+
+} // end namespace util
+} // end namespace mbgl
+
+#endif
diff --git a/include/mbgl/util/std.hpp b/include/mbgl/util/std.hpp
index e64820de47..0e2d3346bf 100644
--- a/include/mbgl/util/std.hpp
+++ b/include/mbgl/util/std.hpp
@@ -8,29 +8,6 @@
namespace mbgl {
namespace util {
-// C++14 backfill based on http://llvm.org/svn/llvm-project/libcxx/trunk/include/memory
-
-namespace detail {
-template<class T> struct unique_type { typedef ::std::unique_ptr<T> single; };
-template<class T> struct unique_type<T[]> { typedef ::std::unique_ptr<T[]> unknown_bound; };
-template<class T, size_t size> struct unique_type<T[size]> { typedef void known_bound; };
-}
-
-template<class T, class... Args>
-typename detail::unique_type<T>::single make_unique(Args&&... args) {
- return ::std::unique_ptr<T>(new T(::std::forward<Args>(args)...));
-}
-
-template<class T>
-typename detail::unique_type<T>::unknown_bound make_unique(size_t size) {
- return ::std::unique_ptr<T>(new typename ::std::remove_extent<T>::type[size]());
-}
-
-template<class T, class... Args>
-typename detail::unique_type<T>::known_bound make_unique(Args&&...) = delete;
-
-
-
template <typename Container, typename ForwardIterator, typename Predicate>
void erase_if(Container &container, ForwardIterator it, const ForwardIterator end, Predicate pred) {
while (it != end) {
diff --git a/ios/MapboxGL.podspec b/ios/MapboxGL.podspec
index 67bcc95710..a604c9e308 100644
--- a/ios/MapboxGL.podspec
+++ b/ios/MapboxGL.podspec
@@ -1,7 +1,7 @@
Pod::Spec.new do |m|
m.name = 'MapboxGL'
- m.version = '0.3.1'
+ m.version = '0.3.2'
m.summary = 'Open source vector map solution for iOS with full styling capabilities.'
m.description = 'Open source OpenGL-based vector map solution for iOS with full styling capabilities and Cocoa bindings.'
diff --git a/ios/app/MBXAppDelegate.m b/ios/app/MBXAppDelegate.m
index dc04105083..b09235a278 100644
--- a/ios/app/MBXAppDelegate.m
+++ b/ios/app/MBXAppDelegate.m
@@ -6,24 +6,22 @@
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
- // Set Access Token
- NSString *accessToken = [[NSProcessInfo processInfo] environment][@"MAPBOX_ACCESS_TOKEN"];
- if (accessToken) {
- // Store to preferences so that we can launch the app later on without having to specify
- // token.
- [[NSUserDefaults standardUserDefaults] setObject:accessToken forKey:@"access_token"];
- } else {
- // Try to retrieve from preferences, maybe we've stored them there previously and can reuse
- // the token.
- accessToken = [[NSUserDefaults standardUserDefaults] objectForKey:@"access_token"];
- }
- if ( ! accessToken) NSLog(@"No access token set. Mapbox vector tiles won't work.");
-
- // Signal To SDK That Opt Out Is In App UI
-// [MGLAccountManager setMapboxMetricsEnabledSettingShownInApp:YES];
+ // Set access token, unless MGLAccountManager already read it in from Info.plist.
+ if ( ! [MGLAccountManager accessToken]) {
+ NSString *accessToken = [[NSProcessInfo processInfo] environment][@"MAPBOX_ACCESS_TOKEN"];
+ if (accessToken) {
+ // Store to preferences so that we can launch the app later on without having to specify
+ // token.
+ [[NSUserDefaults standardUserDefaults] setObject:accessToken forKey:@"access_token"];
+ } else {
+ // Try to retrieve from preferences, maybe we've stored them there previously and can reuse
+ // the token.
+ accessToken = [[NSUserDefaults standardUserDefaults] objectForKey:@"access_token"];
+ }
+ if ( ! accessToken) NSLog(@"No access token set. Mapbox vector tiles won't work.");
- // Start Mapbox GL SDK
- [MGLAccountManager setAccessToken:accessToken];
+ [MGLAccountManager setAccessToken:accessToken];
+ }
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:[MBXViewController new]];
diff --git a/linux/main.cpp b/linux/main.cpp
index d279e9d8fe..9145b180f4 100644
--- a/linux/main.cpp
+++ b/linux/main.cpp
@@ -1,6 +1,5 @@
#include <mbgl/mbgl.hpp>
#include "../platform/default/default_styles.hpp"
-#include <mbgl/util/std.hpp>
#include <mbgl/util/uv.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/platform/platform.hpp>
@@ -66,10 +65,19 @@ int main(int argc, char *argv[]) {
sigIntHandler.sa_flags = 0;
sigaction(SIGINT, &sigIntHandler, NULL);
- view = mbgl::util::make_unique<GLFWView>();
+ view = std::make_unique<GLFWView>();
mbgl::SQLiteCache cache("/tmp/mbgl-cache.db");
mbgl::DefaultFileSource fileSource(&cache);
+
+ // Set access token if present
+ const char *token = getenv("MAPBOX_ACCESS_TOKEN");
+ if (token == nullptr) {
+ mbgl::Log::Warning(mbgl::Event::Setup, "no access token set. mapbox.com tiles won't work.");
+ } else {
+ fileSource.setAccessToken(std::string(token));
+ }
+
mbgl::Map map(*view, fileSource);
// Load settings
@@ -92,14 +100,6 @@ int main(int argc, char *argv[]) {
mbgl::Log::Info(mbgl::Event::Setup, std::string("Changed style to: ") + newStyle.first);
});
- // Set access token if present
- const char *token = getenv("MAPBOX_ACCESS_TOKEN");
- if (token == nullptr) {
- mbgl::Log::Warning(mbgl::Event::Setup, "no access token set. mapbox.com tiles won't work.");
- } else {
- map.setAccessToken(std::string(token));
- }
-
// Load style
if (style.empty()) {
const auto& newStyle = mbgl::util::defaultStyles.front();
diff --git a/macosx/main.mm b/macosx/main.mm
index e5a711753f..4a786f5772 100644
--- a/macosx/main.mm
+++ b/macosx/main.mm
@@ -2,7 +2,7 @@
#include "../platform/default/default_styles.hpp"
#include <mbgl/platform/platform.hpp>
#include <mbgl/platform/darwin/settings_nsuserdefaults.hpp>
-#include <mbgl/platform/darwin/Reachability.h>
+#include <mbgl/platform/darwin/reachability.h>
#include <mbgl/platform/default/glfw_view.hpp>
#include <mbgl/storage/default_file_source.hpp>
#include <mbgl/storage/sqlite_cache.hpp>
@@ -106,6 +106,12 @@ int main() {
mbgl::SQLiteCache cache(defaultCacheDatabase());
mbgl::DefaultFileSource fileSource(&cache);
+
+ // Set access token if present
+ NSString *accessToken = [[NSProcessInfo processInfo] environment][@"MAPBOX_ACCESS_TOKEN"];
+ if (!accessToken) mbgl::Log::Warning(mbgl::Event::Setup, "No access token set. Mapbox vector tiles won't work.");
+ if (accessToken) fileSource.setAccessToken([accessToken cStringUsingEncoding:[NSString defaultCStringEncoding]]);
+
mbgl::Map map(view, fileSource);
URLHandler *handler = [[URLHandler alloc] init];
@@ -114,8 +120,8 @@ int main() {
[appleEventManager setEventHandler:handler andSelector:@selector(handleGetURLEvent:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL];
// Notify map object when network reachability status changes.
- Reachability* reachability = [Reachability reachabilityForInternetConnection];
- reachability.reachableBlock = ^(Reachability *) {
+ MGLReachability* reachability = [MGLReachability reachabilityForInternetConnection];
+ reachability.reachableBlock = ^(MGLReachability *) {
mbgl::NetworkStatus::Reachable();
};
[reachability startNotifier];
@@ -140,11 +146,6 @@ int main() {
mbgl::Log::Info(mbgl::Event::Setup, std::string("Changed style to: ") + newStyle.first);
});
- // Set access token if present
- NSString *accessToken = [[NSProcessInfo processInfo] environment][@"MAPBOX_ACCESS_TOKEN"];
- if (!accessToken) mbgl::Log::Warning(mbgl::Event::Setup, "No access token set. Mapbox vector tiles won't work.");
- if (accessToken) map.setAccessToken([accessToken cStringUsingEncoding:[NSString defaultCStringEncoding]]);
-
// Load style
const auto& newStyle = mbgl::util::defaultStyles.front();
map.setStyleURL(newStyle.first);
diff --git a/platform/darwin/http_request_nsurl.mm b/platform/darwin/http_request_nsurl.mm
index 5ab4e1b43e..207c33b23c 100644
--- a/platform/darwin/http_request_nsurl.mm
+++ b/platform/darwin/http_request_nsurl.mm
@@ -2,7 +2,6 @@
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
-#include <mbgl/util/std.hpp>
#include <mbgl/util/time.hpp>
#include <mbgl/util/parsedate.h>
@@ -91,6 +90,7 @@ public:
NSURLSession *session = nil;
NSString *userAgent = nil;
+ NSInteger accountType = 0;
};
HTTPNSURLContext::HTTPNSURLContext(uv_loop_t *loop_) : HTTPContext(loop_) {
@@ -107,6 +107,8 @@ HTTPNSURLContext::HTTPNSURLContext(uv_loop_t *loop_) : HTTPContext(loop_) {
// Write user agent string
userAgent = @"MapboxGL";
+
+ accountType = [[NSUserDefaults standardUserDefaults] integerForKey:@"MGLMapboxAccountType"];
}
}
@@ -153,17 +155,15 @@ void HTTPRequest::start() {
@autoreleasepool {
- NSMutableString *url = [NSMutableString stringWithString:@(resource.url.c_str())];
- if ([[NSUserDefaults standardUserDefaults] integerForKey:@"MGLMapboxAccountType"] == 0) {
- if ([url rangeOfString:@"?"].location == NSNotFound) {
- [url appendString:@"?"];
- } else {
- [url appendString:@"&"];
- }
- [url appendString:@"events=true"];
+ NSURL *url = [NSURL URLWithString:@(resource.url.c_str())];
+ if (context->accountType == 0 &&
+ ([url.host isEqualToString:@"mapbox.com"] || [url.host hasSuffix:@".mapbox.com"])) {
+ NSString *absoluteString = [url.absoluteString stringByAppendingFormat:
+ (url.query ? @"&%@" : @"?%@"), @"events=true"];
+ url = [NSURL URLWithString:absoluteString];
}
- NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
+ NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
if (existingResponse) {
if (!existingResponse->etag.empty()) {
[req addValue:@(existingResponse->etag.c_str()) forHTTPHeaderField:@"If-None-Match"];
@@ -249,7 +249,7 @@ void HTTPRequest::handleResult(NSData *data, NSURLResponse *res, NSError *error)
} else {
// TODO: Use different codes for host not found, timeout, invalid URL etc.
// These can be categorized in temporary and permanent errors.
- response = util::make_unique<Response>();
+ response = std::make_unique<Response>();
response->status = Response::Error;
response->message = [[error localizedDescription] UTF8String];
@@ -281,7 +281,7 @@ void HTTPRequest::handleResult(NSData *data, NSURLResponse *res, NSError *error)
} else if ([res isKindOfClass:[NSHTTPURLResponse class]]) {
const long responseCode = [(NSHTTPURLResponse *)res statusCode];
- response = util::make_unique<Response>();
+ response = std::make_unique<Response>();
response->data = {(const char *)[data bytes], [data length]};
NSDictionary *headers = [(NSHTTPURLResponse *)res allHeaderFields];
@@ -337,7 +337,7 @@ void HTTPRequest::handleResult(NSData *data, NSURLResponse *res, NSError *error)
} else {
// This should never happen.
status = ResponseStatus::PermanentError;
- response = util::make_unique<Response>();
+ response = std::make_unique<Response>();
response->status = Response::Error;
response->message = "response class is not NSHTTPURLResponse";
}
@@ -363,7 +363,7 @@ void HTTPRequest::retry() {
}
std::unique_ptr<HTTPContext> HTTPContext::createContext(uv_loop_t* loop) {
- return util::make_unique<HTTPNSURLContext>(loop);
+ return std::make_unique<HTTPNSURLContext>(loop);
}
}
diff --git a/platform/darwin/image.mm b/platform/darwin/image.mm
index a9044774dc..f6a8a6783c 100644
--- a/platform/darwin/image.mm
+++ b/platform/darwin/image.mm
@@ -1,5 +1,4 @@
#include <mbgl/util/image.hpp>
-#include <mbgl/util/std.hpp>
#import <ImageIO/ImageIO.h>
@@ -98,7 +97,7 @@ Image::Image(const std::string &source_data) {
height = uint32_t(CGImageGetHeight(image));
CGRect rect = {{ 0, 0 }, { static_cast<CGFloat>(width), static_cast<CGFloat>(height) }};
- img = util::make_unique<char[]>(width * height * 4);
+ img = std::make_unique<char[]>(width * height * 4);
CGContextRef context = CGBitmapContextCreate(img.get(), width, height, 8, width * 4,
color_space, kCGImageAlphaPremultipliedLast);
if (!context) {
diff --git a/platform/darwin/reachability.m b/platform/darwin/reachability.m
index 8234659402..d1413321b7 100644
--- a/platform/darwin/reachability.m
+++ b/platform/darwin/reachability.m
@@ -27,32 +27,33 @@
#import <mbgl/platform/darwin/reachability.h>
+#import <sys/socket.h>
+#import <netinet/in.h>
+#import <netinet6/in6.h>
+#import <arpa/inet.h>
+#import <ifaddrs.h>
+#import <netdb.h>
-NSString *const kReachabilityChangedNotification = @"kReachabilityChangedNotification";
-@interface Reachability ()
+NSString *const kReachabilityChangedNotification = @"kReachabilityChangedNotification";
-@property (nonatomic, assign) SCNetworkReachabilityRef reachabilityRef;
+@interface MGLReachability ()
-#if NEEDS_DISPATCH_RETAIN_RELEASE
-@property (nonatomic, assign) dispatch_queue_t reachabilitySerialQueue;
-#else
+@property (nonatomic, assign) SCNetworkReachabilityRef reachabilityRef;
@property (nonatomic, strong) dispatch_queue_t reachabilitySerialQueue;
-#endif
-
-
-@property (nonatomic, strong) id reachabilityObject;
+@property (nonatomic, strong) id reachabilityObject;
-(void)reachabilityChanged:(SCNetworkReachabilityFlags)flags;
-(BOOL)isReachableWithFlags:(SCNetworkReachabilityFlags)flags;
@end
+
static NSString *reachabilityFlags(SCNetworkReachabilityFlags flags)
{
return [NSString stringWithFormat:@"%c%c %c%c%c%c%c%c%c",
-#if TARGET_OS_IPHONE
+#if TARGET_OS_IPHONE
(flags & kSCNetworkReachabilityFlagsIsWWAN) ? 'W' : '-',
#else
'X',
@@ -71,11 +72,8 @@ static NSString *reachabilityFlags(SCNetworkReachabilityFlags flags)
static void TMReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info)
{
#pragma unused (target)
-#if __has_feature(objc_arc)
- Reachability *reachability = ((__bridge Reachability*)info);
-#else
- Reachability *reachability = ((Reachability*)info);
-#endif
+
+ MGLReachability *reachability = ((__bridge MGLReachability*)info);
// We probably don't need an autoreleasepool here, as GCD docs state each queue has its own autorelease pool,
// but what the heck eh?
@@ -86,61 +84,42 @@ static void TMReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkRea
}
-@implementation Reachability
-
-@synthesize reachabilityRef;
-@synthesize reachabilitySerialQueue;
-
-@synthesize reachableOnWWAN;
-
-@synthesize reachableBlock;
-@synthesize unreachableBlock;
-
-@synthesize reachabilityObject;
+@implementation MGLReachability
#pragma mark - Class Constructor Methods
-+(Reachability*)reachabilityWithHostName:(NSString*)hostname
++(instancetype)reachabilityWithHostName:(NSString*)hostname
{
- return [Reachability reachabilityWithHostname:hostname];
+ return [MGLReachability reachabilityWithHostname:hostname];
}
-+(Reachability*)reachabilityWithHostname:(NSString*)hostname
++(instancetype)reachabilityWithHostname:(NSString*)hostname
{
SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithName(NULL, [hostname UTF8String]);
if (ref)
{
id reachability = [[self alloc] initWithReachabilityRef:ref];
-#if __has_feature(objc_arc)
return reachability;
-#else
- return [reachability autorelease];
-#endif
-
}
return nil;
}
-+(Reachability *)reachabilityWithAddress:(const struct sockaddr_in *)hostAddress
++(instancetype)reachabilityWithAddress:(void *)hostAddress
{
SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)hostAddress);
if (ref)
{
id reachability = [[self alloc] initWithReachabilityRef:ref];
-#if __has_feature(objc_arc)
return reachability;
-#else
- return [reachability autorelease];
-#endif
}
return nil;
}
-+(Reachability *)reachabilityForInternetConnection
++(instancetype)reachabilityForInternetConnection
{
struct sockaddr_in zeroAddress;
bzero(&zeroAddress, sizeof(zeroAddress));
@@ -150,7 +129,7 @@ static void TMReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkRea
return [self reachabilityWithAddress:&zeroAddress];
}
-+(Reachability*)reachabilityForLocalWiFi
++(instancetype)reachabilityForLocalWiFi
{
struct sockaddr_in localWifiAddress;
bzero(&localWifiAddress, sizeof(localWifiAddress));
@@ -165,13 +144,18 @@ static void TMReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkRea
// Initialization methods
--(Reachability *)initWithReachabilityRef:(SCNetworkReachabilityRef)ref
+-(instancetype)initWithReachabilityRef:(SCNetworkReachabilityRef)ref
{
self = [super init];
if (self != nil)
{
self.reachableOnWWAN = YES;
self.reachabilityRef = ref;
+
+ // We need to create a serial queue.
+ // We allocate this once for the lifetime of the notifier.
+
+ self.reachabilitySerialQueue = dispatch_queue_create("com.tonymillion.reachability", NULL);
}
return self;
@@ -187,14 +171,9 @@ static void TMReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkRea
self.reachabilityRef = nil;
}
- self.reachableBlock = nil;
- self.unreachableBlock = nil;
-
-#if !(__has_feature(objc_arc))
- [super dealloc];
-#endif
-
-
+ self.reachableBlock = nil;
+ self.unreachableBlock = nil;
+ self.reachabilitySerialQueue = nil;
}
#pragma mark - Notifier Methods
@@ -206,75 +185,46 @@ static void TMReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkRea
-(BOOL)startNotifier
{
- SCNetworkReachabilityContext context = { 0, NULL, NULL, NULL, NULL };
-
- // this should do a retain on ourself, so as long as we're in notifier mode we shouldn't disappear out from under ourselves
- // woah
- self.reachabilityObject = self;
-
-
-
- // First, we need to create a serial queue.
- // We allocate this once for the lifetime of the notifier.
- self.reachabilitySerialQueue = dispatch_queue_create("com.tonymillion.reachability", NULL);
- if(self.reachabilitySerialQueue == nil)
+ // allow start notifier to be called multiple times
+ if(self.reachabilityObject && (self.reachabilityObject == self))
{
- return NO;
+ return YES;
}
-#if __has_feature(objc_arc)
+
+ SCNetworkReachabilityContext context = { 0, NULL, NULL, NULL, NULL };
context.info = (__bridge void *)self;
-#else
- context.info = (void *)self;
-#endif
- if (!SCNetworkReachabilitySetCallback(self.reachabilityRef, TMReachabilityCallback, &context))
+ if(SCNetworkReachabilitySetCallback(self.reachabilityRef, TMReachabilityCallback, &context))
{
+ // Set it as our reachability queue, which will retain the queue
+ if(SCNetworkReachabilitySetDispatchQueue(self.reachabilityRef, self.reachabilitySerialQueue))
+ {
+ // this should do a retain on ourself, so as long as we're in notifier mode we shouldn't disappear out from under ourselves
+ // woah
+ self.reachabilityObject = self;
+ return YES;
+ }
+ else
+ {
#ifdef DEBUG
- NSLog(@"SCNetworkReachabilitySetCallback() failed: %s", SCErrorString(SCError()));
+ NSLog(@"SCNetworkReachabilitySetDispatchQueue() failed: %s", SCErrorString(SCError()));
#endif
- // Clear out the dispatch queue
- if(self.reachabilitySerialQueue)
- {
-#if NEEDS_DISPATCH_RETAIN_RELEASE
- dispatch_release(self.reachabilitySerialQueue);
-#endif
- self.reachabilitySerialQueue = nil;
+ // UH OH - FAILURE - stop any callbacks!
+ SCNetworkReachabilitySetCallback(self.reachabilityRef, NULL, NULL);
}
-
- self.reachabilityObject = nil;
-
- return NO;
}
-
- // Set it as our reachability queue, which will retain the queue
- if(!SCNetworkReachabilitySetDispatchQueue(self.reachabilityRef, self.reachabilitySerialQueue))
+ else
{
#ifdef DEBUG
- NSLog(@"SCNetworkReachabilitySetDispatchQueue() failed: %s", SCErrorString(SCError()));
-#endif
-
- // UH OH - FAILURE!
-
- // First stop, any callbacks!
- SCNetworkReachabilitySetCallback(self.reachabilityRef, NULL, NULL);
-
- // Then clear out the dispatch queue.
- if(self.reachabilitySerialQueue)
- {
-#if NEEDS_DISPATCH_RETAIN_RELEASE
- dispatch_release(self.reachabilitySerialQueue);
+ NSLog(@"SCNetworkReachabilitySetCallback() failed: %s", SCErrorString(SCError()));
#endif
- self.reachabilitySerialQueue = nil;
- }
-
- self.reachabilityObject = nil;
-
- return NO;
}
- return YES;
+ // if we get here we fail at the internet
+ self.reachabilityObject = nil;
+ return NO;
}
-(void)stopNotifier
@@ -285,14 +235,6 @@ static void TMReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkRea
// Unregister target from the GCD serial dispatch queue.
SCNetworkReachabilitySetDispatchQueue(self.reachabilityRef, NULL);
- if(self.reachabilitySerialQueue)
- {
-#if NEEDS_DISPATCH_RETAIN_RELEASE
- dispatch_release(self.reachabilitySerialQueue);
-#endif
- self.reachabilitySerialQueue = nil;
- }
-
self.reachabilityObject = nil;
}
@@ -318,7 +260,7 @@ static void TMReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkRea
if( (flags & testcase) == testcase )
connectionUP = NO;
-#if TARGET_OS_IPHONE
+#if TARGET_OS_IPHONE
if(flags & kSCNetworkReachabilityFlagsIsWWAN)
{
// We're on 3G.
@@ -345,11 +287,11 @@ static void TMReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkRea
-(BOOL)isReachableViaWWAN
{
-#if TARGET_OS_IPHONE
+#if TARGET_OS_IPHONE
SCNetworkReachabilityFlags flags = 0;
- if(SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
+ if(SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
{
// Check we're REACHABLE
if(flags & kSCNetworkReachabilityFlagsReachable)
@@ -370,12 +312,12 @@ static void TMReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkRea
{
SCNetworkReachabilityFlags flags = 0;
- if(SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
+ if(SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
{
// Check we're reachable
if((flags & kSCNetworkReachabilityFlagsReachable))
{
-#if TARGET_OS_IPHONE
+#if TARGET_OS_IPHONE
// Check we're NOT on WWAN
if((flags & kSCNetworkReachabilityFlagsIsWWAN))
{
@@ -401,10 +343,10 @@ static void TMReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkRea
{
SCNetworkReachabilityFlags flags;
- if(SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
+ if(SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
{
- return (flags & kSCNetworkReachabilityFlagsConnectionRequired);
- }
+ return (flags & kSCNetworkReachabilityFlagsConnectionRequired);
+ }
return NO;
}
@@ -412,15 +354,15 @@ static void TMReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkRea
// Dynamic, on demand connection?
-(BOOL)isConnectionOnDemand
{
- SCNetworkReachabilityFlags flags;
+ SCNetworkReachabilityFlags flags;
- if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
+ if (SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
{
- return ((flags & kSCNetworkReachabilityFlagsConnectionRequired) &&
- (flags & (kSCNetworkReachabilityFlagsConnectionOnTraffic | kSCNetworkReachabilityFlagsConnectionOnDemand)));
- }
+ return ((flags & kSCNetworkReachabilityFlagsConnectionRequired) &&
+ (flags & (kSCNetworkReachabilityFlagsConnectionOnTraffic | kSCNetworkReachabilityFlagsConnectionOnDemand)));
+ }
- return NO;
+ return NO;
}
// Is user intervention required?
@@ -428,13 +370,13 @@ static void TMReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkRea
{
SCNetworkReachabilityFlags flags;
- if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
+ if (SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
{
- return ((flags & kSCNetworkReachabilityFlagsConnectionRequired) &&
- (flags & kSCNetworkReachabilityFlagsInterventionRequired));
- }
+ return ((flags & kSCNetworkReachabilityFlagsConnectionRequired) &&
+ (flags & kSCNetworkReachabilityFlagsInterventionRequired));
+ }
- return NO;
+ return NO;
}
@@ -447,7 +389,7 @@ static void TMReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkRea
if([self isReachableViaWiFi])
return ReachableViaWiFi;
-#if TARGET_OS_IPHONE
+#if TARGET_OS_IPHONE
return ReachableViaWWAN;
#endif
}
@@ -459,7 +401,7 @@ static void TMReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkRea
{
SCNetworkReachabilityFlags flags = 0;
- if(SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
+ if(SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
{
return flags;
}
@@ -469,19 +411,19 @@ static void TMReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkRea
-(NSString*)currentReachabilityString
{
- NetworkStatus temp = [self currentReachabilityStatus];
+ NetworkStatus temp = [self currentReachabilityStatus];
- if(temp == reachableOnWWAN)
- {
+ if(temp == ReachableViaWWAN)
+ {
// Updated for the fact that we have CDMA phones now!
- return NSLocalizedString(@"Cellular", @"");
- }
- if (temp == ReachableViaWiFi)
- {
- return NSLocalizedString(@"WiFi", @"");
- }
-
- return NSLocalizedString(@"No Connection", @"");
+ return NSLocalizedString(@"Cellular", @"");
+ }
+ if (temp == ReachableViaWiFi)
+ {
+ return NSLocalizedString(@"WiFi", @"");
+ }
+
+ return NSLocalizedString(@"No Connection", @"");
}
-(NSString*)currentReachabilityFlags
@@ -519,8 +461,8 @@ static void TMReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkRea
- (NSString *) description
{
- NSString *description = [NSString stringWithFormat:@"<%@: %#x>",
- NSStringFromClass([self class]), (unsigned int) self];
+ NSString *description = [NSString stringWithFormat:@"<%@: %#x (%@)>",
+ NSStringFromClass([self class]), (unsigned int) self, [self currentReachabilityFlags]];
return description;
}
diff --git a/platform/default/asset_request_fs.cpp b/platform/default/asset_request_fs.cpp
index 9dae2e8a9c..04f85fbc3d 100644
--- a/platform/default/asset_request_fs.cpp
+++ b/platform/default/asset_request_fs.cpp
@@ -1,7 +1,6 @@
#include <mbgl/storage/asset_context.hpp>
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
-#include <mbgl/util/std.hpp>
#include <mbgl/util/util.hpp>
#include <mbgl/util/url.hpp>
#include <mbgl/util/uv.hpp>
@@ -114,7 +113,7 @@ void AssetRequest::fileStated(uv_fs_t *req) {
if (stat->st_size > std::numeric_limits<int>::max()) {
// File is too large for us to open this way because uv_buf's only support unsigned
// ints as maximum size.
- auto response = util::make_unique<Response>();
+ auto response = std::make_unique<Response>();
response->status = Response::Error;
#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
response->message = uv_strerror(uv_err_t {UV_EFBIG, 0});
@@ -126,7 +125,7 @@ void AssetRequest::fileStated(uv_fs_t *req) {
uv_fs_req_cleanup(req);
uv_fs_close(req->loop, req, self->fd, fileClosed);
} else {
- self->response = util::make_unique<Response>();
+ self->response = std::make_unique<Response>();
#ifdef __APPLE__
self->response->modified = stat->st_mtimespec.tv_sec;
#else
@@ -184,7 +183,7 @@ void AssetRequest::notifyError(uv_fs_t *req) {
MBGL_VERIFY_THREAD(self->tid);
if (req->result < 0 && !self->canceled && req->result != UV_ECANCELED) {
- auto response = util::make_unique<Response>();
+ auto response = std::make_unique<Response>();
response->status = Response::Error;
response->message = uv::getFileRequestError(req);
self->notify(std::move(response), FileCache::Hint::No);
@@ -208,7 +207,7 @@ void AssetRequest::cancel() {
}
std::unique_ptr<AssetContext> AssetContext::createContext(uv_loop_t*) {
- return util::make_unique<AssetFSContext>();
+ return std::make_unique<AssetFSContext>();
}
}
diff --git a/platform/default/asset_request_zip.cpp b/platform/default/asset_request_zip.cpp
index 7666000d00..7fd93c55d1 100644
--- a/platform/default/asset_request_zip.cpp
+++ b/platform/default/asset_request_zip.cpp
@@ -3,7 +3,6 @@
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/platform/log.hpp>
-#include <mbgl/util/std.hpp>
#include <mbgl/util/util.hpp>
#include <mbgl/util/uv.hpp>
@@ -178,7 +177,7 @@ void AssetRequest::fileStated(uv_zip_t *zip) {
notifyError("Could not determine file size in zip file");
cleanup(zip);
} else {
- response = util::make_unique<Response>();
+ response = std::make_unique<Response>();
// Allocate the space for reading the data.
response->data.resize(zip->stat->size);
@@ -247,7 +246,7 @@ void AssetRequest::notifyError(const char *message) {
MBGL_VERIFY_THREAD(tid);
if (!cancelled) {
- response = util::make_unique<Response>();
+ response = std::make_unique<Response>();
response->status = Response::Error;
response->message = message;
notify(std::move(response), FileCache::Hint::No);
@@ -261,7 +260,7 @@ void AssetRequest::cancel() {
}
std::unique_ptr<AssetContext> AssetContext::createContext(uv_loop_t* loop) {
- return util::make_unique<AssetZipContext>(loop);
+ return std::make_unique<AssetZipContext>(loop);
}
}
diff --git a/platform/default/headless_view.cpp b/platform/default/headless_view.cpp
index aa07706d84..0f00c7563d 100644
--- a/platform/default/headless_view.cpp
+++ b/platform/default/headless_view.cpp
@@ -5,7 +5,6 @@
#include <mbgl/map/still_image.hpp>
-#include <mbgl/util/std.hpp>
#include <stdexcept>
#include <sstream>
@@ -172,15 +171,15 @@ std::unique_ptr<StillImage> HeadlessView::readStillImage() {
const unsigned int w = dimensions.pixelWidth();
const unsigned int h = dimensions.pixelHeight();
- auto image = util::make_unique<StillImage>();
+ auto image = std::make_unique<StillImage>();
image->width = w;
image->height = h;
- image->pixels = util::make_unique<uint32_t[]>(w * h);
+ image->pixels = std::make_unique<uint32_t[]>(w * h);
MBGL_CHECK_ERROR(glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, image->pixels.get()));
const int stride = w * 4;
- auto tmp = util::make_unique<char[]>(stride);
+ auto tmp = std::make_unique<char[]>(stride);
char *rgba = reinterpret_cast<char *>(image->pixels.get());
for (int i = 0, j = h - 1; i < j; i++, j--) {
std::memcpy(tmp.get(), rgba + i * stride, stride);
diff --git a/platform/default/http_request_curl.cpp b/platform/default/http_request_curl.cpp
index 45115b621f..8599ea05ee 100644
--- a/platform/default/http_request_curl.cpp
+++ b/platform/default/http_request_curl.cpp
@@ -385,7 +385,7 @@ static CURLcode sslctx_function(CURL * /* curl */, void *sslctx, void * /* parm
return CURLE_SSL_CACERT_BADFILE;
}
- const auto pem = util::make_unique<char[]>(stat.size);
+ const auto pem = std::make_unique<char[]>(stat.size);
if (static_cast<zip_uint64_t>(zip_fread(apkFile, reinterpret_cast<void *>(pem.get()), stat.size)) != stat.size) {
zip_fclose(apkFile);
@@ -537,7 +537,7 @@ size_t HTTPRequest::writeCallback(void *const contents, const size_t size, const
MBGL_VERIFY_THREAD(impl->tid);
if (!impl->response) {
- impl->response = util::make_unique<Response>();
+ impl->response = std::make_unique<Response>();
}
impl->response->data.append((char *)contents, size * nmemb);
@@ -581,7 +581,7 @@ size_t HTTPRequest::headerCallback(char *const buffer, const size_t size, const
MBGL_VERIFY_THREAD(baton->tid);
if (!baton->response) {
- baton->response = util::make_unique<Response>();
+ baton->response = std::make_unique<Response>();
}
const size_t length = size * nmemb;
@@ -675,7 +675,7 @@ void HTTPRequest::handleResult(CURLcode code) {
// Make sure a response object exists in case we haven't got any headers
// or content.
if (!response) {
- response = util::make_unique<Response>();
+ response = std::make_unique<Response>();
}
// Add human-readable error code
@@ -734,7 +734,7 @@ void HTTPRequest::handleResult(CURLcode code) {
}
std::unique_ptr<HTTPContext> HTTPContext::createContext(uv_loop_t* loop) {
- return util::make_unique<HTTPCURLContext>(loop);
+ return std::make_unique<HTTPCURLContext>(loop);
}
}
diff --git a/platform/default/image.cpp b/platform/default/image.cpp
index 1a10d78ffa..12aea898c8 100644
--- a/platform/default/image.cpp
+++ b/platform/default/image.cpp
@@ -1,7 +1,6 @@
#include <mbgl/util/image.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/util/string.hpp>
-#include <mbgl/util/std.hpp>
#include <png.h>
@@ -82,7 +81,7 @@ Image::Image(std::string const& data)
auto reader = getImageReader(data.c_str(), data.size());
width = reader->width();
height = reader->height();
- img = util::make_unique<char[]>(width * height * 4);
+ img = std::make_unique<char[]>(width * height * 4);
reader->read(0, 0, width, height, img.get());
}
catch (ImageReaderException const& ex)
diff --git a/platform/default/image_reader.cpp b/platform/default/image_reader.cpp
index fc6daec6a5..e80ccb6819 100644
--- a/platform/default/image_reader.cpp
+++ b/platform/default/image_reader.cpp
@@ -49,11 +49,11 @@ std::unique_ptr<ImageReader> getImageReader(char const* data, size_t size)
{
if (*type == "png")
{
- return util::make_unique<PngReader<boost::iostreams::array_source>>(data, size);
+ return std::make_unique<PngReader<boost::iostreams::array_source>>(data, size);
}
else if (*type == "jpeg")
{
- return util::make_unique<JpegReader<boost::iostreams::array_source>>(data, size);
+ return std::make_unique<JpegReader<boost::iostreams::array_source>>(data, size);
}
}
throw ImageReaderException("ImageReader: can't determine type from input data");
diff --git a/platform/default/sqlite_cache.cpp b/platform/default/sqlite_cache.cpp
index fb4cdf74e7..06d168ce4e 100644
--- a/platform/default/sqlite_cache.cpp
+++ b/platform/default/sqlite_cache.cpp
@@ -62,7 +62,7 @@ std::string unifyMapboxURLs(const std::string &url) {
using namespace mapbox::sqlite;
SQLiteCache::SQLiteCache(const std::string& path_)
- : thread(util::make_unique<util::Thread<Impl>>("SQLite Cache", util::ThreadPriority::Low, path_)) {
+ : thread(std::make_unique<util::Thread<Impl>>("SQLite Cache", util::ThreadPriority::Low, path_)) {
}
SQLiteCache::~SQLiteCache() = default;
@@ -85,7 +85,7 @@ SQLiteCache::Impl::~Impl() {
}
void SQLiteCache::Impl::createDatabase() {
- db = util::make_unique<Database>(path.c_str(), ReadWrite | Create);
+ db = std::make_unique<Database>(path.c_str(), ReadWrite | Create);
}
void SQLiteCache::Impl::createSchema() {
@@ -114,7 +114,7 @@ void SQLiteCache::Impl::createSchema() {
} catch (util::IOException& ioEx) {
Log::Error(Event::Database, ex.code, ex.what());
}
- db = util::make_unique<Database>(path.c_str(), ReadWrite | Create);
+ db = std::make_unique<Database>(path.c_str(), ReadWrite | Create);
} else {
Log::Error(Event::Database, ex.code, ex.what());
}
@@ -146,8 +146,8 @@ std::unique_ptr<Response> SQLiteCache::Impl::get(const Resource &resource) {
}
if (!getStmt) {
- // Initialize the statement 0 1
- getStmt = util::make_unique<Statement>(db->prepare("SELECT `status`, `modified`, "
+ // Initialize the statement 0 1
+ getStmt = std::make_unique<Statement>(db->prepare("SELECT `status`, `modified`, "
// 2 3 4 5 1
"`etag`, `expires`, `data`, `compressed` FROM `http_cache` WHERE `url` = ?"));
} else {
@@ -158,7 +158,7 @@ std::unique_ptr<Response> SQLiteCache::Impl::get(const Resource &resource) {
getStmt->bind(1, unifiedURL.c_str());
if (getStmt->run()) {
// There is data.
- auto response = util::make_unique<Response>();
+ auto response = std::make_unique<Response>();
response->status = Response::Status(getStmt->get<int>(0));
response->modified = getStmt->get<int64_t>(1);
response->etag = getStmt->get<std::string>(2);
@@ -200,7 +200,7 @@ void SQLiteCache::Impl::put(const Resource& resource, std::shared_ptr<const Resp
}
if (!putStmt) {
- putStmt = util::make_unique<Statement>(db->prepare("REPLACE INTO `http_cache` ("
+ putStmt = std::make_unique<Statement>(db->prepare("REPLACE INTO `http_cache` ("
// 1 2 3 4 5 6 7 8
"`url`, `status`, `kind`, `modified`, `etag`, `expires`, `data`, `compressed`"
") VALUES(?, ?, ?, ?, ?, ?, ?, ?)"));
@@ -249,7 +249,7 @@ void SQLiteCache::Impl::refresh(const Resource& resource, int64_t expires) {
}
if (!refreshStmt) {
- refreshStmt = util::make_unique<Statement>( // 1 2
+ refreshStmt = std::make_unique<Statement>( // 1 2
db->prepare("UPDATE `http_cache` SET `expires` = ? WHERE `url` = ?"));
} else {
refreshStmt->reset();
diff --git a/platform/ios/MGLAccountManager.m b/platform/ios/MGLAccountManager.m
index a49433777d..71987786c9 100644
--- a/platform/ios/MGLAccountManager.m
+++ b/platform/ios/MGLAccountManager.m
@@ -1,6 +1,6 @@
#import <Foundation/Foundation.h>
-#import "MGLAccountManager.h"
+#import "MGLAccountManager_Private.h"
#import "MGLMapboxEvents.h"
#import "NSProcessInfo+MGLAdditions.h"
@@ -14,6 +14,18 @@
@implementation MGLAccountManager
++ (void)load {
+ // Read the initial configuration from Info.plist. The shown-in-app setting
+ // preempts the Settings bundle check in -[MGLMapboxEvents init] triggered
+ // by setting the access token.
+ NSBundle *bundle = [NSBundle mainBundle];
+ NSNumber *shownInAppNumber = [bundle objectForInfoDictionaryKey:@"MGLMapboxMetricsEnabledSettingShownInApp"];
+ if (shownInAppNumber) {
+ [MGLAccountManager sharedManager].mapboxMetricsEnabledSettingShownInApp = [shownInAppNumber boolValue];
+ }
+ self.accessToken = [bundle objectForInfoDictionaryKey:@"MGLMapboxAccessToken"];
+}
+
// Can be called from any thread.
//
+ (instancetype) sharedManager {
@@ -25,7 +37,6 @@
void (^setupBlock)() = ^{
dispatch_once(&onceToken, ^{
_sharedManager = [[self alloc] init];
- _sharedManager.mapboxMetricsEnabledSettingShownInApp = NO;
});
};
if ( ! [[NSThread currentThread] isMainThread]) {
@@ -39,16 +50,14 @@
return _sharedManager;
}
-+ (void) setMapboxMetricsEnabledSettingShownInApp:(BOOL)shown {
- [MGLAccountManager sharedManager].mapboxMetricsEnabledSettingShownInApp = shown;
-}
-
+ (BOOL) mapboxMetricsEnabledSettingShownInApp {
return [MGLAccountManager sharedManager].mapboxMetricsEnabledSettingShownInApp;
}
+ (void) setAccessToken:(NSString *) accessToken {
- [[MGLAccountManager sharedManager] setAccessToken:accessToken];
+ if ( ! [accessToken length]) return;
+
+ [MGLAccountManager sharedManager].accessToken = accessToken;
// Update MGLMapboxEvents
// NOTE: This is (likely) the initial setup of MGLMapboxEvents
diff --git a/platform/ios/MGLAccountManager_Private.h b/platform/ios/MGLAccountManager_Private.h
new file mode 100644
index 0000000000..3cc05b09f0
--- /dev/null
+++ b/platform/ios/MGLAccountManager_Private.h
@@ -0,0 +1,10 @@
+#import "MGLAccountManager.h"
+
+@interface MGLAccountManager (Private)
+
+/** Returns the shared instance of the `MGLAccountManager` class. */
++ (instancetype)sharedManager;
+
+@property (atomic) NSString *accessToken;
+
+@end
diff --git a/platform/ios/MGLMapView.mm b/platform/ios/MGLMapView.mm
index 5d14107518..a517a4d26f 100644
--- a/platform/ios/MGLMapView.mm
+++ b/platform/ios/MGLMapView.mm
@@ -24,6 +24,7 @@
#import "MGLUserLocationAnnotationView.h"
#import "MGLUserLocation_Private.h"
#import "MGLFileCache.h"
+#import "MGLAccountManager_Private.h"
#import "MGLMapboxEvents.h"
#import "SMCalloutView.h"
@@ -35,7 +36,7 @@ class MBGLView;
NSString *const MGLDefaultStyleName = @"mapbox-streets";
NSString *const MGLStyleVersion = @"7";
NSString *const MGLDefaultStyleMarkerSymbolName = @"default_marker";
-NSString *const MGLMapboxAccessTokenManagerURLDisplayString = @"mapbox.com/account/apps";
+NSString *const MGLMapboxSetupDocumentationURLDisplayString = @"mapbox.com/guides/first-steps-gl-ios";
const NSTimeInterval MGLAnimationDuration = 0.3;
const CGSize MGLAnnotationUpdateViewportOutset = {150, 150};
@@ -48,6 +49,16 @@ static NSURL *MGLURLForBundledStyleNamed(NSString *styleName)
return [NSURL URLWithString:[NSString stringWithFormat:@"asset://styles/%@.json", styleName]];
}
+CGFloat MGLRadiansFromDegrees(CLLocationDegrees degrees)
+{
+ return degrees * M_PI / 180;
+}
+
+CLLocationDegrees MGLDegreesFromRadians(CGFloat radians)
+{
+ return radians * 180 / M_PI;
+}
+
#pragma mark - Private -
@interface MGLMapView () <UIGestureRecognizerDelegate, GLKViewDelegate, CLLocationManagerDelegate, UIActionSheetDelegate>
@@ -103,63 +114,48 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration)
- (instancetype)initWithFrame:(CGRect)frame
{
- self = [super initWithFrame:frame];
-
- if (self && [self commonInit])
+ if (self = [super initWithFrame:frame])
{
+ [self commonInit];
self.styleURL = nil;
- self.accessToken = [MGLAccountManager accessToken];
- return self;
}
-
- return nil;
-}
-
-- (instancetype)initWithFrame:(CGRect)frame accessToken:(NSString *)accessToken
-{
- return [self initWithFrame:frame accessToken:accessToken styleURL:nil];
+ return self;
}
-- (instancetype)initWithFrame:(CGRect)frame accessToken:(NSString *)accessToken styleURL:(NSURL *)styleURL
+- (instancetype)initWithFrame:(CGRect)frame styleURL:(NSURL *)styleURL
{
- self = [super initWithFrame:frame];
-
- if (self && [self commonInit])
+ if (self = [super initWithFrame:frame])
{
- self.accessToken = accessToken;
+ [self commonInit];
self.styleURL = styleURL;
}
-
return self;
}
- (instancetype)initWithCoder:(NSCoder *)decoder
{
- self = [super initWithCoder:decoder];
-
- if (self && [self commonInit])
+ if (self = [super initWithCoder:decoder])
{
+ [self commonInit];
self.styleURL = nil;
- return self;
}
-
- return nil;
+ return self;
}
- (NSString *)accessToken
{
- return @(_mbglMap->getAccessToken().c_str()).mgl_stringOrNilIfEmpty;
+ NSAssert(NO, @"-[MGLMapView accessToken] has been removed. Use +[MGLAccountManager accessToken] or get MGLMapboxAccessToken from the Info.plist.");
+ return nil;
}
- (void)setAccessToken:(NSString *)accessToken
{
- _mbglMap->setAccessToken(accessToken ? (std::string)[accessToken UTF8String] : "");
- [MGLAccountManager setAccessToken:accessToken.mgl_stringOrNilIfEmpty];
+ NSAssert(NO, @"-[MGLMapView setAccessToken:] has been replaced by +[MGLAccountManager setAccessToken:].\n\nIf you previously set this access token in a storyboard inspectable, select the MGLMapView in Interface Builder and delete the “accessToken” entry from the User Defined Runtime Attributes section of the Identity inspector. Then go to the Info.plist file and set MGLMapboxAccessToken to “%@”.", accessToken);
}
+ (NSSet *)keyPathsForValuesAffectingStyleURL
{
- return [NSSet setWithObjects:@"mapID", @"accessToken", nil];
+ return [NSSet setWithObject:@"styleID"];
}
- (NSURL *)styleURL
@@ -188,20 +184,14 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration)
_mbglMap->setStyleURL([[styleURL absoluteString] UTF8String]);
}
-- (BOOL)commonInit
+- (void)commonInit
{
_isTargetingInterfaceBuilder = NSProcessInfo.processInfo.mgl_isInterfaceBuilderDesignablesAgent;
// create context
//
_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
-
- if ( ! _context)
- {
- mbgl::Log::Error(mbgl::Event::Setup, "failed to create OpenGL ES context");
-
- return NO;
- }
+ NSAssert(_context, @"Failed to create OpenGL ES context.");
// setup accessibility
//
@@ -253,13 +243,20 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration)
}
_mbglMap->resize(self.bounds.size.width, self.bounds.size.height, _glView.contentScaleFactor);
+ // Observe for changes to the global access token (and find out the current one).
+ [[MGLAccountManager sharedManager] addObserver:self
+ forKeyPath:@"accessToken"
+ options:(NSKeyValueObservingOptionInitial |
+ NSKeyValueObservingOptionNew)
+ context:NULL];
+
// Notify map object when network reachability status changes.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(reachabilityChanged:)
name:kReachabilityChangedNotification
object:nil];
- Reachability* reachability = [Reachability reachabilityForInternetConnection];
+ MGLReachability* reachability = [MGLReachability reachabilityForInternetConnection];
[reachability startNotifier];
// setup annotations
@@ -294,6 +291,12 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration)
@"Improve This Map",
nil];
+ // iOS 8+: add action that opens app's Settings.app panel, if applicable
+ if (&UIApplicationOpenSettingsURLString != NULL && ! [MGLAccountManager mapboxMetricsEnabledSettingShownInApp])
+ {
+ [_attributionSheet addButtonWithTitle:@"Adjust Privacy Settings"];
+ }
+
// setup compass
//
_compass = [[UIImageView alloc] initWithImage:[MGLMapView resourceImageNamed:@"Compass.png"]];
@@ -376,13 +379,11 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration)
MGLEventKeyZoomLevel: @(zoom),
MGLEventKeyPushEnabled: @([MGLMapboxEvents checkPushEnabled])
}];
-
- return YES;
}
-(void)reachabilityChanged:(NSNotification*)notification
{
- Reachability *reachability = [notification object];
+ MGLReachability *reachability = [notification object];
if ([reachability isReachable]) {
mbgl::NetworkStatus::Reachable();
}
@@ -393,6 +394,7 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration)
[_regionChangeDelegateQueue cancelAllOperations];
[[NSNotificationCenter defaultCenter] removeObserver:self];
+ [[MGLAccountManager sharedManager] removeObserver:self forKeyPath:@"accessToken"];
if (_mbglMap)
{
@@ -905,13 +907,13 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration)
_mbglMap->setGestureInProgress(true);
- self.angle = [MGLMapView degreesToRadians:_mbglMap->getBearing()] * -1;
+ self.angle = MGLRadiansFromDegrees(_mbglMap->getBearing()) * -1;
self.userTrackingMode = MGLUserTrackingModeNone;
}
else if (rotate.state == UIGestureRecognizerStateChanged)
{
- CGFloat newDegrees = [MGLMapView radiansToDegrees:(self.angle + rotate.rotation)] * -1;
+ CGFloat newDegrees = MGLDegreesFromRadians(self.angle + rotate.rotation) * -1;
// constrain to +/-30 degrees when merely rotating like Apple does
//
@@ -934,7 +936,7 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration)
CGFloat radians = self.angle + rotate.rotation;
CGFloat duration = UIScrollViewDecelerationRateNormal;
CGFloat newRadians = radians + velocity * duration * 0.1;
- CGFloat newDegrees = [MGLMapView radiansToDegrees:newRadians] * -1;
+ CGFloat newDegrees = MGLDegreesFromRadians(newRadians) * -1;
_mbglMap->setBearing(newDegrees, secondsAsDuration(duration));
@@ -999,16 +1001,16 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration)
mbgl::LatLngBounds tapBounds;
coordinate = [self convertPoint:tapRectLowerLeft toCoordinateFromView:self];
- tapBounds.extend(coordinateToLatLng(coordinate));
+ tapBounds.extend(MGLLatLngFromLocationCoordinate2D(coordinate));
coordinate = [self convertPoint:tapRectUpperLeft toCoordinateFromView:self];
- tapBounds.extend(coordinateToLatLng(coordinate));
+ tapBounds.extend(MGLLatLngFromLocationCoordinate2D(coordinate));
coordinate = [self convertPoint:tapRectUpperRight toCoordinateFromView:self];
- tapBounds.extend(coordinateToLatLng(coordinate));
+ tapBounds.extend(MGLLatLngFromLocationCoordinate2D(coordinate));
coordinate = [self convertPoint:tapRectLowerRight toCoordinateFromView:self];
- tapBounds.extend(coordinateToLatLng(coordinate));
+ tapBounds.extend(MGLLatLngFromLocationCoordinate2D(coordinate));
// query for nearby annotations
std::vector<uint32_t> nearbyAnnotations = _mbglMap->getAnnotationsInBounds(tapBounds);
@@ -1270,10 +1272,25 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration)
[[UIApplication sharedApplication] openURL:
[NSURL URLWithString:feedbackURL]];
}
+ // skips to 4 because button is conditionally added after cancel (index 3)
+ else if (buttonIndex == actionSheet.firstOtherButtonIndex + 4)
+ {
+ [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
+ }
}
#pragma mark - Properties -
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(__unused void *)context
+{
+ // Synchronize mbgl::Map’s access token with the global one in MGLAccountManager.
+ if ([keyPath isEqualToString:@"accessToken"] && object == [MGLAccountManager sharedManager])
+ {
+ NSString *accessToken = change[NSKeyValueChangeNewKey];
+ _mbglFileSource->setAccessToken(accessToken ? (std::string)[accessToken UTF8String] : "");
+ }
+}
+
+ (NSSet *)keyPathsForValuesAffectingZoomEnabled
{
return [NSSet setWithObject:@"allowsZooming"];
@@ -1361,7 +1378,7 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration)
{
CGFloat duration = (animated ? MGLAnimationDuration : 0);
- _mbglMap->setLatLngZoom(coordinateToLatLng(coordinate),
+ _mbglMap->setLatLngZoom(MGLLatLngFromLocationCoordinate2D(coordinate),
fmaxf(_mbglMap->getZoom(), self.currentMinimumZoom),
secondsAsDuration(duration));
@@ -1375,7 +1392,7 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration)
- (CLLocationCoordinate2D)centerCoordinate
{
- return latLngToCoordinate(_mbglMap->getLatLng());
+ return MGLLocationCoordinate2DFromLatLng(_mbglMap->getLatLng());
}
- (void)setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate zoomLevel:(double)zoomLevel animated:(BOOL)animated
@@ -1384,7 +1401,7 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration)
CGFloat duration = (animated ? MGLAnimationDuration : 0);
- _mbglMap->setLatLngZoom(coordinateToLatLng(centerCoordinate), zoomLevel, secondsAsDuration(duration));
+ _mbglMap->setLatLngZoom(MGLLatLngFromLocationCoordinate2D(centerCoordinate), zoomLevel, secondsAsDuration(duration));
[self unrotateIfNeededAnimated:animated];
@@ -1468,12 +1485,12 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration)
//
convertedPoint.y = self.bounds.size.height - convertedPoint.y;
- return latLngToCoordinate(_mbglMap->latLngForPixel(mbgl::vec2<double>(convertedPoint.x, convertedPoint.y)));
+ return MGLLocationCoordinate2DFromLatLng(_mbglMap->latLngForPixel(mbgl::vec2<double>(convertedPoint.x, convertedPoint.y)));
}
- (CGPoint)convertCoordinate:(CLLocationCoordinate2D)coordinate toPointToView:(UIView *)view
{
- mbgl::vec2<double> pixel = _mbglMap->pixelForLatLng(coordinateToLatLng(coordinate));
+ mbgl::vec2<double> pixel = _mbglMap->pixelForLatLng(MGLLatLngFromLocationCoordinate2D(coordinate));
// flip y coordinate for iOS view origin in top left
//
@@ -1487,12 +1504,12 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration)
return _mbglMap->getMetersPerPixelAtLatitude(latitude, self.zoomLevel);
}
-mbgl::LatLng coordinateToLatLng(CLLocationCoordinate2D coordinate)
+mbgl::LatLng MGLLatLngFromLocationCoordinate2D(CLLocationCoordinate2D coordinate)
{
return mbgl::LatLng(coordinate.latitude, coordinate.longitude);
}
-CLLocationCoordinate2D latLngToCoordinate(mbgl::LatLng latLng)
+CLLocationCoordinate2D MGLLocationCoordinate2DFromLatLng(mbgl::LatLng latLng)
{
return CLLocationCoordinate2DMake(latLng.latitude, latLng.longitude);
}
@@ -1501,13 +1518,13 @@ CLLocationCoordinate2D latLngToCoordinate(mbgl::LatLng latLng)
{
mbgl::LatLngBounds bounds;
- bounds.extend(coordinateToLatLng(
+ bounds.extend(MGLLatLngFromLocationCoordinate2D(
[self convertPoint:CGPointMake(0, 0) toCoordinateFromView:self]));
- bounds.extend(coordinateToLatLng(
+ bounds.extend(MGLLatLngFromLocationCoordinate2D(
[self convertPoint:CGPointMake(self.bounds.size.width, 0) toCoordinateFromView:self]));
- bounds.extend(coordinateToLatLng(
+ bounds.extend(MGLLatLngFromLocationCoordinate2D(
[self convertPoint:CGPointMake(0, self.bounds.size.height) toCoordinateFromView:self]));
- bounds.extend(coordinateToLatLng(
+ bounds.extend(MGLLatLngFromLocationCoordinate2D(
[self convertPoint:CGPointMake(self.bounds.size.width, self.bounds.size.height) toCoordinateFromView:self]));
return bounds;
@@ -1533,27 +1550,31 @@ CLLocationCoordinate2D latLngToCoordinate(mbgl::LatLng latLng)
return [NSArray arrayWithArray:_bundledStyleURLs];
}
-+ (NSSet *)keyPathsForValuesAffectingMapID
++ (NSSet *)keyPathsForValuesAffectingStyleID
{
- return [NSSet setWithObjects:@"styleURL", @"accessToken", nil];
+ return [NSSet setWithObject:@"styleURL"];
}
-- (NSString *)mapID
+- (NSString *)styleID
{
NSURL *styleURL = self.styleURL;
return [styleURL.scheme isEqualToString:@"mapbox"] ? styleURL.host.mgl_stringOrNilIfEmpty : nil;
}
+- (void)setStyleID:(NSString *)styleID
+{
+ self.styleURL = styleID ? [NSURL URLWithString:[NSString stringWithFormat:@"mapbox://%@", styleID]] : nil;
+}
+
+- (NSString *)mapID
+{
+ NSAssert(NO, @"-[MGLMapView mapID] has been renamed -[MGLMapView styleID].");
+ return nil;
+}
+
- (void)setMapID:(NSString *)mapID
{
- if (mapID)
- {
- self.styleURL = [NSURL URLWithString:[NSString stringWithFormat:@"mapbox://%@", mapID]];
- }
- else
- {
- self.styleURL = nil;
- }
+ NSAssert(NO, @"-[MGLMapView setMapID:] has been renamed -[MGLMapView setStyleID:].\n\nIf you previously set this map ID in a storyboard inspectable, select the MGLMapView in Interface Builder and delete the “mapID” entry from the User Defined Runtime Attributes section of the Identity inspector. Then go to the Attributes inspector and enter “%@” into the “Style ID” field.", mapID);
}
- (NSArray *)styleClasses
@@ -1657,7 +1678,7 @@ CLLocationCoordinate2D latLngToCoordinate(mbgl::LatLng latLng)
{
assert([annotation conformsToProtocol:@protocol(MGLAnnotation)]);
- latLngs.push_back(coordinateToLatLng(annotation.coordinate));
+ latLngs.push_back(MGLLatLngFromLocationCoordinate2D(annotation.coordinate));
NSString *symbolName = nil;
@@ -1726,7 +1747,7 @@ CLLocationCoordinate2D latLngToCoordinate(mbgl::LatLng latLng)
assert([firstAnnotation conformsToProtocol:@protocol(MGLAnnotation)]);
- if ( ! [self viewportBounds].contains(coordinateToLatLng(firstAnnotation.coordinate))) return;
+ if ( ! [self viewportBounds].contains(MGLLatLngFromLocationCoordinate2D(firstAnnotation.coordinate))) return;
[self selectAnnotation:firstAnnotation animated:NO];
}
@@ -1735,7 +1756,7 @@ CLLocationCoordinate2D latLngToCoordinate(mbgl::LatLng latLng)
{
if ( ! annotation) return;
- if ( ! [self viewportBounds].contains(coordinateToLatLng(annotation.coordinate))) return;
+ if ( ! [self viewportBounds].contains(MGLLatLngFromLocationCoordinate2D(annotation.coordinate))) return;
if (annotation == self.selectedAnnotation) return;
@@ -2171,16 +2192,6 @@ CLLocationCoordinate2D latLngToCoordinate(mbgl::LatLng latLng)
#pragma mark - Utility -
-+ (CGFloat)degreesToRadians:(CGFloat)degrees
-{
- return degrees * M_PI / 180;
-}
-
-+ (CGFloat)radiansToDegrees:(CGFloat)radians
-{
- return radians * 180 / M_PI;
-}
-
- (void)animateWithDelay:(NSTimeInterval)delay animations:(void (^)(void))animations
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), animations);
@@ -2398,7 +2409,7 @@ CLLocationCoordinate2D latLngToCoordinate(mbgl::LatLng latLng)
while (degrees >= 360) degrees -= 360;
while (degrees < 0) degrees += 360;
- self.compass.transform = CGAffineTransformMakeRotation([MGLMapView degreesToRadians:degrees]);
+ self.compass.transform = CGAffineTransformMakeRotation(MGLRadiansFromDegrees(degrees));
if (_mbglMap->getBearing() && self.compass.alpha < 1)
{
@@ -2431,14 +2442,14 @@ CLLocationCoordinate2D latLngToCoordinate(mbgl::LatLng latLng)
imageName = [imageName stringByAppendingString:@".png"];
}
- return [UIImage imageWithContentsOfFile:[MGLMapView pathForBundleResourceNamed:imageName ofType:nil inDirectory:@""]];
+ return [UIImage imageWithContentsOfFile:[self pathForBundleResourceNamed:imageName ofType:nil inDirectory:@""]];
}
+ (NSString *)pathForBundleResourceNamed:(NSString *)name ofType:(NSString *)extension inDirectory:(NSString *)directory
{
NSString *path = [[NSBundle bundleWithPath:[NSBundle mgl_resourceBundlePath]] pathForResource:name ofType:extension inDirectory:directory];
- NSAssert(path, @"Resource not found in application.");
+ NSAssert(path, @"Resource %@ not found in application.", name);
return path;
}
@@ -2461,129 +2472,106 @@ CLLocationCoordinate2D latLngToCoordinate(mbgl::LatLng latLng)
{
[super prepareForInterfaceBuilder];
- self.layer.borderColor = [UIColor colorWithWhite:184/255. alpha:1].CGColor;
- self.layer.borderWidth = 1;
-
- if (self.accessToken)
- {
- self.layer.backgroundColor = [UIColor colorWithRed:59/255.
- green:178/255.
- blue:208/255.
- alpha:0.8].CGColor;
-
- UIImage *image = [[self class] resourceImageNamed:@"mapbox.png"];
- UIImageView *previewView = [[UIImageView alloc] initWithImage:image];
- previewView.translatesAutoresizingMaskIntoConstraints = NO;
- [self addSubview:previewView];
- [self addConstraint:
- [NSLayoutConstraint constraintWithItem:previewView
- attribute:NSLayoutAttributeCenterXWithinMargins
- relatedBy:NSLayoutRelationEqual
- toItem:self
- attribute:NSLayoutAttributeCenterXWithinMargins
- multiplier:1
- constant:0]];
- [self addConstraint:
- [NSLayoutConstraint constraintWithItem:previewView
- attribute:NSLayoutAttributeCenterYWithinMargins
- relatedBy:NSLayoutRelationEqual
- toItem:self
- attribute:NSLayoutAttributeCenterYWithinMargins
- multiplier:1
- constant:0]];
- }
- else
- {
- UIView *diagnosticView = [[UIView alloc] init];
- diagnosticView.translatesAutoresizingMaskIntoConstraints = NO;
- [self addSubview:diagnosticView];
-
- // Headline
- UILabel *headlineLabel = [[UILabel alloc] init];
- headlineLabel.text = @"No Access Token";
- headlineLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];
- headlineLabel.textAlignment = NSTextAlignmentCenter;
- headlineLabel.numberOfLines = 1;
- headlineLabel.translatesAutoresizingMaskIntoConstraints = NO;
- [headlineLabel setContentCompressionResistancePriority:UILayoutPriorityDefaultLow
- forAxis:UILayoutConstraintAxisHorizontal];
- [diagnosticView addSubview:headlineLabel];
-
- // Explanation
- UILabel *explanationLabel = [[UILabel alloc] init];
- explanationLabel.text = @"To display a map here, you must provide a Mapbox access token. Get an access token from:";
- explanationLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
- explanationLabel.numberOfLines = 0;
- explanationLabel.translatesAutoresizingMaskIntoConstraints = NO;
- [explanationLabel setContentCompressionResistancePriority:UILayoutPriorityDefaultLow
- forAxis:UILayoutConstraintAxisHorizontal];
- [diagnosticView addSubview:explanationLabel];
-
- // Link
- UIButton *linkButton = [UIButton buttonWithType:UIButtonTypeSystem];
- [linkButton setTitle:MGLMapboxAccessTokenManagerURLDisplayString forState:UIControlStateNormal];
- linkButton.translatesAutoresizingMaskIntoConstraints = NO;
- [linkButton setContentCompressionResistancePriority:UILayoutPriorityDefaultLow
- forAxis:UILayoutConstraintAxisHorizontal];
- [diagnosticView addSubview:linkButton];
-
- // More explanation
- UILabel *explanationLabel2 = [[UILabel alloc] init];
- explanationLabel2.text = @"and enter it into the Access Token field in the Attributes inspector.";
- explanationLabel2.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
- explanationLabel2.numberOfLines = 0;
- explanationLabel2.translatesAutoresizingMaskIntoConstraints = NO;
- [explanationLabel2 setContentCompressionResistancePriority:UILayoutPriorityDefaultLow
- forAxis:UILayoutConstraintAxisHorizontal];
- [diagnosticView addSubview:explanationLabel2];
-
- // Constraints
- NSDictionary *views = @{
- @"container": diagnosticView,
- @"headline": headlineLabel,
- @"explanation": explanationLabel,
- @"link": linkButton,
- @"explanation2": explanationLabel2,
- };
- [self addConstraint:
- [NSLayoutConstraint constraintWithItem:diagnosticView
- attribute:NSLayoutAttributeCenterYWithinMargins
- relatedBy:NSLayoutRelationEqual
- toItem:self
- attribute:NSLayoutAttributeCenterYWithinMargins
- multiplier:1
- constant:0]];
- [self addConstraints:
- [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[container(20@20)]-|"
- options:NSLayoutFormatAlignAllCenterY
- metrics:nil
- views:views]];
- [self addConstraints:
- [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[headline]-[explanation]-[link]-[explanation2]|"
- options:0
- metrics:nil
- views:views]];
- [self addConstraints:
- [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[headline]|"
- options:0
- metrics:nil
- views:views]];
- [self addConstraints:
- [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[explanation]|"
- options:0
- metrics:nil
- views:views]];
- [self addConstraints:
- [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[link]|"
- options:0
- metrics:nil
- views:views]];
- [self addConstraints:
- [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[explanation2]|"
- options:0
- metrics:nil
- views:views]];
- }
+ self.layer.borderColor = [UIColor colorWithRed:59/255.
+ green:178/255.
+ blue:208/255.
+ alpha:0.8].CGColor;
+ self.layer.borderWidth = 4;
+ self.layer.backgroundColor = [UIColor whiteColor].CGColor;
+
+ UIView *diagnosticView = [[UIView alloc] init];
+ diagnosticView.translatesAutoresizingMaskIntoConstraints = NO;
+ [self addSubview:diagnosticView];
+
+ // Headline
+ UILabel *headlineLabel = [[UILabel alloc] init];
+ headlineLabel.text = @"MGLMapView";
+ headlineLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];
+ headlineLabel.textAlignment = NSTextAlignmentCenter;
+ headlineLabel.numberOfLines = 1;
+ headlineLabel.translatesAutoresizingMaskIntoConstraints = NO;
+ [headlineLabel setContentCompressionResistancePriority:UILayoutPriorityDefaultLow
+ forAxis:UILayoutConstraintAxisHorizontal];
+ [diagnosticView addSubview:headlineLabel];
+
+ // Explanation
+ UILabel *explanationLabel = [[UILabel alloc] init];
+ explanationLabel.text = (@"To display a Mapbox-hosted map here:\n\n"
+ @"1. Set MGLMapboxAccessToken to your access token in Info.plist\n"
+ @"2. Add a Settings bundle that allows the user to turn Mapbox Metrics on and off\n\n"
+ @"For detailed instructions, see:");
+ explanationLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
+ explanationLabel.numberOfLines = 0;
+ explanationLabel.translatesAutoresizingMaskIntoConstraints = NO;
+ [explanationLabel setContentCompressionResistancePriority:UILayoutPriorityDefaultLow
+ forAxis:UILayoutConstraintAxisHorizontal];
+ [diagnosticView addSubview:explanationLabel];
+
+ // Link
+ UIButton *linkButton = [UIButton buttonWithType:UIButtonTypeSystem];
+ [linkButton setTitle:MGLMapboxSetupDocumentationURLDisplayString forState:UIControlStateNormal];
+ linkButton.translatesAutoresizingMaskIntoConstraints = NO;
+ linkButton.titleLabel.numberOfLines = 0;
+ [linkButton setContentCompressionResistancePriority:UILayoutPriorityDefaultLow
+ forAxis:UILayoutConstraintAxisHorizontal];
+ [diagnosticView addSubview:linkButton];
+
+ // Constraints
+ NSDictionary *views = @{
+ @"container": diagnosticView,
+ @"headline": headlineLabel,
+ @"explanation": explanationLabel,
+ @"link": linkButton,
+ };
+ [self addConstraint:
+ [NSLayoutConstraint constraintWithItem:diagnosticView
+ attribute:NSLayoutAttributeCenterYWithinMargins
+ relatedBy:NSLayoutRelationEqual
+ toItem:self
+ attribute:NSLayoutAttributeCenterYWithinMargins
+ multiplier:1
+ constant:0]];
+ [self addConstraint:
+ [NSLayoutConstraint constraintWithItem:diagnosticView
+ attribute:NSLayoutAttributeTopMargin
+ relatedBy:NSLayoutRelationGreaterThanOrEqual
+ toItem:self
+ attribute:NSLayoutAttributeTopMargin
+ multiplier:1
+ constant:8]];
+ [self addConstraint:
+ [NSLayoutConstraint constraintWithItem:self
+ attribute:NSLayoutAttributeBottomMargin
+ relatedBy:NSLayoutRelationGreaterThanOrEqual
+ toItem:diagnosticView
+ attribute:NSLayoutAttributeBottomMargin
+ multiplier:1
+ constant:8]];
+ [self addConstraints:
+ [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[container(20@20)]-|"
+ options:NSLayoutFormatAlignAllCenterY
+ metrics:nil
+ views:views]];
+ [self addConstraints:
+ [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[headline]-[explanation]-[link]|"
+ options:0
+ metrics:nil
+ views:views]];
+ [self addConstraints:
+ [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[headline]|"
+ options:0
+ metrics:nil
+ views:views]];
+ [self addConstraints:
+ [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[explanation]|"
+ options:0
+ metrics:nil
+ views:views]];
+ [self addConstraints:
+ [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[link]|"
+ options:0
+ metrics:nil
+ views:views]];
}
class MBGLView : public mbgl::View
diff --git a/platform/ios/MGLMapboxEvents.h b/platform/ios/MGLMapboxEvents.h
index 94aa8be783..00de64a8b0 100644
--- a/platform/ios/MGLMapboxEvents.h
+++ b/platform/ios/MGLMapboxEvents.h
@@ -1,5 +1,6 @@
#import <Foundation/Foundation.h>
+extern NSString *const MGLEventTypeAppUserTurnstile;
extern NSString *const MGLEventTypeMapLoad;
extern NSString *const MGLEventTypeMapTap;
extern NSString *const MGLEventTypeMapDragEnd;
diff --git a/platform/ios/MGLMapboxEvents.m b/platform/ios/MGLMapboxEvents.m
index 98bfe21cfe..e7f400c5dc 100644
--- a/platform/ios/MGLMapboxEvents.m
+++ b/platform/ios/MGLMapboxEvents.m
@@ -13,9 +13,11 @@
#include <sys/sysctl.h>
+static const NSUInteger version = 1;
static NSString *const MGLMapboxEventsUserAgent = @"MapboxEventsiOS/1.0";
static NSString *MGLMapboxEventsAPIBase = @"https://api.tiles.mapbox.com";
+NSString *const MGLEventTypeAppUserTurnstile = @"appUserTurnstile";
NSString *const MGLEventTypeMapLoad = @"map.load";
NSString *const MGLEventTypeMapTap = @"map.click";
NSString *const MGLEventTypeMapDragEnd = @"map.dragend";
@@ -52,7 +54,6 @@ const NSTimeInterval MGLFlushInterval = 60;
// All of the following properties are written to only from
// the main thread, but can be read on any thread.
//
-@property (atomic) NSString *instanceID;
@property (atomic) NSString *advertiserId;
@property (atomic) NSString *vendorId;
@property (atomic) NSString *model;
@@ -66,8 +67,7 @@ const NSTimeInterval MGLFlushInterval = 60;
- (instancetype)init {
if (self = [super init]) {
- _instanceID = [[NSUUID UUID] UUIDString];
-
+
// Dynamic detection of ASIdentifierManager from Mixpanel
// https://github.com/mixpanel/mixpanel-iphone/blob/master/LICENSE
_advertiserId = @"";
@@ -143,6 +143,7 @@ const NSTimeInterval MGLFlushInterval = 60;
@property (atomic) NSString *appName;
@property (atomic) NSString *appVersion;
@property (atomic) NSString *appBuildNumber;
+@property (atomic) NSString *instanceID;
@property (atomic) NSDateFormatter *rfc3339DateFormatter;
@property (atomic) NSURLSession *session;
@property (atomic) NSData *digicertCert;
@@ -176,8 +177,10 @@ const NSTimeInterval MGLFlushInterval = 60;
+ (void)initialize {
if (self == [MGLMapboxEvents class]) {
+ NSBundle *bundle = [NSBundle mainBundle];
+ NSNumber *accountTypeNumber = [bundle objectForInfoDictionaryKey:@"MGLMapboxAccountType"];
[[NSUserDefaults standardUserDefaults] registerDefaults:@{
- @"MGLMapboxAccountType": @0,
+ @"MGLMapboxAccountType": accountTypeNumber ? accountTypeNumber : @0,
@"MGLMapboxMetricsEnabled": @YES,
}];
}
@@ -195,7 +198,8 @@ const NSTimeInterval MGLFlushInterval = 60;
self = [super init];
if (self) {
- if (! [MGLAccountManager mapboxMetricsEnabledSettingShownInApp]) {
+ if (! [MGLAccountManager mapboxMetricsEnabledSettingShownInApp] &&
+ [[NSUserDefaults standardUserDefaults] integerForKey:@"MGLMapboxAccountType"] == 0) {
// Opt Out is not configured in UI, so check for Settings.bundle
// Put Settings bundle into memory
id defaultEnabledValue;
@@ -213,7 +217,7 @@ const NSTimeInterval MGLFlushInterval = 60;
}
}
- NSAssert(defaultEnabledValue, @"End users must be able to opt out of Metrics in your app, either inside Settings (via Settings.bundle) or inside this app. If you implement the opt-out control inside this app, disable this assertion by setting [MGLAccountManager setMapboxMetricsEnabledSettingShownInApp:YES] before the app initializes any Mapbox GL classes.");
+ NSAssert(defaultEnabledValue, @"End users must be able to opt out of Metrics in your app, either inside Settings (via Settings.bundle) or inside this app. If you implement the opt-out control inside this app, disable this assertion by setting MGLMapboxMetricsEnabledSettingShownInApp to YES in Info.plist.");
[[NSUserDefaults standardUserDefaults] registerDefaults:@{
@"MGLMapboxMetricsEnabled": defaultEnabledValue,
}];
@@ -223,7 +227,8 @@ const NSTimeInterval MGLFlushInterval = 60;
_appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"];
_appVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
_appBuildNumber = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
-
+ _instanceID = [[NSUUID UUID] UUIDString];
+
NSString *uniqueID = [[NSProcessInfo processInfo] globallyUniqueString];
_serialQueue = dispatch_queue_create([[NSString stringWithFormat:@"%@.%@.events.serial", _appBundleId, uniqueID] UTF8String], DISPATCH_QUEUE_SERIAL);
@@ -277,6 +282,11 @@ const NSTimeInterval MGLFlushInterval = 60;
MGLMapboxEvents *strongSelf = weakSelf;
[strongSelf validate];
}];
+
+ // Turn the Mapbox Turnstile to Count App Users
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
+ [self pushTurnstileEvent];
+ });
}
return self;
}
@@ -315,6 +325,7 @@ const NSTimeInterval MGLFlushInterval = 60;
[[MGLMapboxEvents sharedManager] validate];
}
+// Used to determine if Mapbox Metrics should be collected at any given point in time
- (void)validate {
MGLAssertIsMainThread();
BOOL enabledInSettings = [[self class] isEnabled];
@@ -414,6 +425,33 @@ const NSTimeInterval MGLFlushInterval = 60;
}
}
+- (void) pushTurnstileEvent {
+
+ __weak MGLMapboxEvents *weakSelf = self;
+
+ dispatch_async(_serialQueue, ^{
+
+ MGLMapboxEvents *strongSelf = weakSelf;
+
+ if ( ! strongSelf) return;
+
+ // Build only IDFV event
+ NSString *vid = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
+ NSDictionary *vevt = @{@"event" : MGLEventTypeAppUserTurnstile,
+ @"created" : [strongSelf.rfc3339DateFormatter stringFromDate:[NSDate date]],
+ @"appBundleId" : strongSelf.appBundleId,
+ @"vendorId": vid,
+ @"version": @(version),
+ @"instance": strongSelf.instanceID};
+
+ // Add to Queue
+ [_eventQueue addObject:vevt];
+
+ // Flush
+ [strongSelf flush];
+ });
+}
+
// Can be called from any thread. Can be called rapidly from
// the UI thread, so performance is paramount.
//
@@ -430,9 +468,11 @@ const NSTimeInterval MGLFlushInterval = 60;
__weak MGLMapboxEvents *weakSelf = self;
dispatch_async(_serialQueue, ^{
+
MGLMapboxEvents *strongSelf = weakSelf;
+
if ( ! strongSelf) return;
-
+
// Metrics Collection Has Been Paused
if (_paused) {
return;
@@ -443,9 +483,9 @@ const NSTimeInterval MGLFlushInterval = 60;
NSMutableDictionary *evt = [[NSMutableDictionary alloc] initWithDictionary:attributeDictionary];
// mapbox-events stock attributes
[evt setObject:event forKey:@"event"];
- [evt setObject:@(1) forKey:@"version"];
+ [evt setObject:@(version) forKey:@"version"];
[evt setObject:[strongSelf.rfc3339DateFormatter stringFromDate:[NSDate date]] forKey:@"created"];
- [evt setObject:strongSelf.data.instanceID forKey:@"instance"];
+ [evt setObject:strongSelf.instanceID forKey:@"instance"];
[evt setObject:strongSelf.data.advertiserId forKey:@"advertiserId"];
[evt setObject:strongSelf.data.vendorId forKey:@"vendorId"];
[evt setObject:strongSelf.appBundleId forKeyedSubscript:@"appBundleId"];
diff --git a/src/mbgl/geometry/glyph_atlas.cpp b/src/mbgl/geometry/glyph_atlas.cpp
index bb230e4efd..067c5a4062 100644
--- a/src/mbgl/geometry/glyph_atlas.cpp
+++ b/src/mbgl/geometry/glyph_atlas.cpp
@@ -1,11 +1,11 @@
#include <mbgl/geometry/glyph_atlas.hpp>
+#include <mbgl/text/font_stack.hpp>
+
#include <mbgl/platform/gl.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/platform/platform.hpp>
-#include <mbgl/util/std.hpp>
-
#include <cassert>
#include <algorithm>
@@ -16,7 +16,7 @@ GlyphAtlas::GlyphAtlas(uint16_t width_, uint16_t height_)
: width(width_),
height(height_),
bin(width_, height_),
- data(util::make_unique<uint8_t[]>(width_ * height_)),
+ data(std::make_unique<uint8_t[]>(width_ * height_)),
dirty(true) {
}
@@ -69,8 +69,9 @@ Rect<uint16_t> GlyphAtlas::addGlyph(uintptr_t tileUID,
uint16_t buffered_height = glyph.metrics.height + buffer * 2;
// Add a 1px border around every image.
- uint16_t pack_width = buffered_width;
- uint16_t pack_height = buffered_height;
+ const uint16_t padding = 1;
+ uint16_t pack_width = buffered_width + 2 * padding;
+ uint16_t pack_height = buffered_height + 2 * padding;
// Increase to next number divisible by 4, but at least 1.
// This is so we can scale down the texture coordinates and pack them
@@ -92,7 +93,7 @@ Rect<uint16_t> GlyphAtlas::addGlyph(uintptr_t tileUID,
// Copy the bitmap
const uint8_t* source = reinterpret_cast<const uint8_t*>(glyph.bitmap.data());
for (uint32_t y = 0; y < buffered_height; y++) {
- uint32_t y1 = width * (rect.y + y) + rect.x;
+ uint32_t y1 = width * (rect.y + y + padding) + rect.x + padding;
uint32_t y2 = buffered_width * y;
for (uint32_t x = 0; x < buffered_width; x++) {
data[y1 + x] = source[y2 + x];
diff --git a/src/mbgl/geometry/line_atlas.cpp b/src/mbgl/geometry/line_atlas.cpp
index 91ac15639b..dc5ee0901a 100644
--- a/src/mbgl/geometry/line_atlas.cpp
+++ b/src/mbgl/geometry/line_atlas.cpp
@@ -3,7 +3,6 @@
#include <mbgl/platform/gl.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/platform/platform.hpp>
-#include <mbgl/util/std.hpp>
#include <boost/functional/hash.hpp>
@@ -15,7 +14,7 @@ using namespace mbgl;
LineAtlas::LineAtlas(uint16_t w, uint16_t h)
: width(w),
height(h),
- data(util::make_unique<uint8_t[]>(w * h)),
+ data(std::make_unique<uint8_t[]>(w * h)),
dirty(true) {
}
diff --git a/src/mbgl/geometry/sprite_atlas.cpp b/src/mbgl/geometry/sprite_atlas.cpp
index 5d47793cf5..93bc76aa5c 100644
--- a/src/mbgl/geometry/sprite_atlas.cpp
+++ b/src/mbgl/geometry/sprite_atlas.cpp
@@ -127,10 +127,12 @@ SpriteAtlasPosition SpriteAtlas::getPosition(const std::string& name, bool repea
rect.h = pos.height / pos.pixelRatio;
}
+ const float padding = 1;
+
return SpriteAtlasPosition {
{{ float(rect.w), float(rect.h) }},
- {{ float(rect.x) / width, float(rect.y) / height }},
- {{ float(rect.x + rect.w) / width, float(rect.y + rect.h) / height }}
+ {{ float(rect.x + padding) / width, float(rect.y + padding) / height }},
+ {{ float(rect.x + padding + rect.w) / width, float(rect.y + padding + rect.h) / height }}
};
}
@@ -138,7 +140,7 @@ void SpriteAtlas::allocate() {
if (!data) {
dimension w = static_cast<dimension>(width * pixelRatio);
dimension h = static_cast<dimension>(height * pixelRatio);
- data = util::make_unique<uint32_t[]>(w * h);
+ data = std::make_unique<uint32_t[]>(w * h);
std::fill(data.get(), data.get() + w * h, 0);
}
}
@@ -151,12 +153,14 @@ void SpriteAtlas::copy(const Rect<dimension>& dst, const SpritePosition& src, co
const vec2<uint32_t> srcSize { sprite->raster->getWidth(), sprite->raster->getHeight() };
const Rect<uint32_t> srcPos { src.x, src.y, src.width, src.height };
+ const int offset = 1;
+
allocate();
uint32_t *const dstData = data.get();
const vec2<uint32_t> dstSize { static_cast<unsigned int>(width * pixelRatio),
static_cast<unsigned int>(height * pixelRatio) };
- const Rect<uint32_t> dstPos { static_cast<uint32_t>(dst.x * pixelRatio),
- static_cast<uint32_t>(dst.y * pixelRatio),
+ const Rect<uint32_t> dstPos { static_cast<uint32_t>((offset + dst.x) * pixelRatio),
+ static_cast<uint32_t>((offset + dst.y) * pixelRatio),
static_cast<uint32_t>(dst.originalW * pixelRatio),
static_cast<uint32_t>(dst.originalH * pixelRatio) };
@@ -295,6 +299,8 @@ void SpriteAtlas::bind(bool linear) {
SpriteAtlas::~SpriteAtlas() {
std::lock_guard<std::recursive_mutex> lock(mtx);
- Environment::Get().abandonTexture(texture);
- texture = 0;
+ if (texture) {
+ Environment::Get().abandonTexture(texture);
+ texture = 0;
+ }
}
diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp
index 9d21b4e48b..6df49ec647 100644
--- a/src/mbgl/map/annotation.cpp
+++ b/src/mbgl/map/annotation.cpp
@@ -5,7 +5,6 @@
#include <mbgl/map/map_data.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/ptr.hpp>
-#include <mbgl/util/std.hpp>
#include <algorithm>
#include <memory>
@@ -110,7 +109,7 @@ AnnotationManager::addPointAnnotations(const std::vector<LatLng>& points,
// track the annotation global ID and its geometry
auto anno_it = annotations.emplace(
annotationID,
- util::make_unique<Annotation>(AnnotationType::Point,
+ std::make_unique<Annotation>(AnnotationType::Point,
AnnotationSegments({ { points[i] } })));
const uint8_t maxZoom = data.transform.getMaxZoom();
@@ -162,7 +161,7 @@ AnnotationManager::addPointAnnotations(const std::vector<LatLng>& points,
// create tile & record annotation association
auto tile_pos = tiles.emplace(
tileID, std::make_pair(std::unordered_set<uint32_t>({ annotationID }),
- util::make_unique<LiveTile>()));
+ std::make_unique<LiveTile>()));
// add point layer to tile
tile_pos.first->second.second->addLayer(layerID, layer);
}
diff --git a/src/mbgl/map/annotation.hpp b/src/mbgl/map/annotation.hpp
index a80b03226f..0c9a078e57 100644
--- a/src/mbgl/map/annotation.hpp
+++ b/src/mbgl/map/annotation.hpp
@@ -4,7 +4,6 @@
#include <mbgl/map/tile_id.hpp>
#include <mbgl/util/geo.hpp>
#include <mbgl/util/noncopyable.hpp>
-#include <mbgl/util/std.hpp>
#include <mbgl/util/vec.hpp>
#include <string>
diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp
index 3ba82252de..0479189300 100644
--- a/src/mbgl/map/map.cpp
+++ b/src/mbgl/map/map.cpp
@@ -3,15 +3,14 @@
#include <mbgl/map/view.hpp>
#include <mbgl/map/map_data.hpp>
-#include <mbgl/util/std.hpp>
#include <mbgl/util/projection.hpp>
#include <mbgl/util/thread.hpp>
namespace mbgl {
Map::Map(View& view, FileSource& fileSource, MapMode mode)
- : data(util::make_unique<MapData>(view, mode)),
- context(util::make_unique<util::Thread<MapContext>>("Map", util::ThreadPriority::Regular, view, fileSource, *data))
+ : data(std::make_unique<MapData>(view, mode)),
+ context(std::make_unique<util::Thread<MapContext>>("Map", util::ThreadPriority::Regular, view, fileSource, *data))
{
view.initialize(this);
}
@@ -195,17 +194,6 @@ void Map::resetNorth() {
}
-#pragma mark - Access Token
-
-void Map::setAccessToken(const std::string &token) {
- data->setAccessToken(token);
-}
-
-std::string Map::getAccessToken() const {
- return data->getAccessToken();
-}
-
-
#pragma mark - Projection
void Map::getWorldBoundsMeters(ProjectedMeters& sw, ProjectedMeters& ne) const {
diff --git a/src/mbgl/map/map_context.cpp b/src/mbgl/map/map_context.cpp
index f373274ebd..c8948097f7 100644
--- a/src/mbgl/map/map_context.cpp
+++ b/src/mbgl/map/map_context.cpp
@@ -20,11 +20,9 @@
#include <mbgl/style/style.hpp>
-#include <mbgl/util/std.hpp>
#include <mbgl/util/uv_detail.hpp>
#include <mbgl/util/worker.hpp>
#include <mbgl/util/texture_pool.hpp>
-#include <mbgl/util/mapbox.hpp>
#include <mbgl/util/exception.hpp>
namespace mbgl {
@@ -35,12 +33,12 @@ MapContext::MapContext(uv_loop_t* loop, View& view_, FileSource& fileSource, Map
env(fileSource),
envScope(env, ThreadType::Map, "Map"),
updated(static_cast<UpdateType>(Update::Nothing)),
- asyncUpdate(util::make_unique<uv::async>(loop, [this] { update(); })),
- glyphStore(util::make_unique<GlyphStore>(loop, env)),
- glyphAtlas(util::make_unique<GlyphAtlas>(1024, 1024)),
- spriteAtlas(util::make_unique<SpriteAtlas>(512, 512)),
- lineAtlas(util::make_unique<LineAtlas>(512, 512)),
- texturePool(util::make_unique<TexturePool>()) {
+ asyncUpdate(std::make_unique<uv::async>(loop, [this] { update(); })),
+ glyphStore(std::make_unique<GlyphStore>(loop, env)),
+ glyphAtlas(std::make_unique<GlyphAtlas>(1024, 1024)),
+ spriteAtlas(std::make_unique<SpriteAtlas>(512, 512)),
+ lineAtlas(std::make_unique<LineAtlas>(512, 512)),
+ texturePool(std::make_unique<TexturePool>()) {
assert(Environment::currentlyOn(ThreadType::Map));
asyncUpdate->unref();
@@ -90,7 +88,7 @@ void MapContext::triggerUpdate(const Update u) {
}
void MapContext::setStyleURL(const std::string& url) {
- styleURL = mbgl::util::mapbox::normalizeStyleURL(url, data.getAccessToken());
+ styleURL = url;
styleJSON.clear();
const size_t pos = styleURL.rfind('/');
@@ -99,7 +97,7 @@ void MapContext::setStyleURL(const std::string& url) {
base = styleURL.substr(0, pos + 1);
}
- env.request({ Resource::Kind::JSON, styleURL }, [this, base](const Response &res) {
+ env.request({ Resource::Kind::Style, styleURL }, [this, base](const Response &res) {
if (res.status == Response::Successful) {
loadStyleJSON(res.data, base);
} else {
@@ -121,17 +119,15 @@ void MapContext::loadStyleJSON(const std::string& json, const std::string& base)
resourceLoader.reset();
style.reset();
- style = util::make_unique<Style>();
+ style = std::make_unique<Style>();
style->base = base;
style->loadJSON((const uint8_t *)json.c_str());
style->cascade(data.getClasses());
style->setDefaultTransitionDuration(data.getDefaultTransitionDuration());
- const std::string glyphURL = util::mapbox::normalizeGlyphsURL(style->glyph_url, data.getAccessToken());
- glyphStore->setURL(glyphURL);
+ glyphStore->setURL(style->glyph_url);
- resourceLoader = util::make_unique<ResourceLoader>();
- resourceLoader->setAccessToken(data.getAccessToken());
+ resourceLoader = std::make_unique<ResourceLoader>();
resourceLoader->setObserver(this);
resourceLoader->setStyle(style.get());
resourceLoader->setGlyphStore(glyphStore.get());
@@ -224,7 +220,7 @@ void MapContext::render() {
assert(style);
if (!painter) {
- painter = util::make_unique<Painter>(*spriteAtlas, *glyphAtlas, *lineAtlas);
+ painter = std::make_unique<Painter>(*spriteAtlas, *glyphAtlas, *lineAtlas);
painter->setup();
}
@@ -232,7 +228,7 @@ void MapContext::render() {
painter->render(*style, transformState, data.getAnimationTime());
if (data.mode == MapMode::Still) {
- callback(view.readStillImage());
+ callback(nullptr, view.readStillImage());
callback = nullptr;
}
@@ -274,4 +270,13 @@ void MapContext::onTileDataChanged() {
triggerUpdate();
}
+void MapContext::onResourceLoadingFailed(std::exception_ptr error) {
+ assert(Environment::currentlyOn(ThreadType::Map));
+
+ if (data.mode == MapMode::Still && callback) {
+ callback(error, nullptr);
+ callback = nullptr;
+ }
+}
+
}
diff --git a/src/mbgl/map/map_context.hpp b/src/mbgl/map/map_context.hpp
index c842454849..fb9fdb4d4b 100644
--- a/src/mbgl/map/map_context.hpp
+++ b/src/mbgl/map/map_context.hpp
@@ -43,7 +43,7 @@ public:
void resize(uint16_t width, uint16_t height, float ratio);
- using StillImageCallback = std::function<void(std::unique_ptr<const StillImage>)>;
+ using StillImageCallback = std::function<void(std::exception_ptr, std::unique_ptr<const StillImage>)>;
void renderStill(StillImageCallback callback);
void triggerUpdate(Update = Update::Nothing);
@@ -61,6 +61,7 @@ public:
// ResourceLoader::Observer implementation.
void onTileDataChanged() override;
+ void onResourceLoadingFailed(std::exception_ptr error) override;
private:
void updateTiles();
diff --git a/src/mbgl/map/map_data.hpp b/src/mbgl/map/map_data.hpp
index 803a9b1acb..32722d07e8 100644
--- a/src/mbgl/map/map_data.hpp
+++ b/src/mbgl/map/map_data.hpp
@@ -27,15 +27,6 @@ public:
setDefaultTransitionDuration(Duration::zero());
}
- inline std::string getAccessToken() const {
- Lock lock(mtx);
- return accessToken;
- }
- inline void setAccessToken(const std::string &token) {
- Lock lock(mtx);
- accessToken = token;
- }
-
// Adds the class if it's not yet set. Returns true when it added the class, and false when it
// was already present.
bool addClass(const std::string& klass);
@@ -105,7 +96,6 @@ public:
private:
mutable std::mutex mtx;
- std::string accessToken;
std::vector<std::string> classes;
std::atomic<uint8_t> debug { false };
std::atomic<uint8_t> collisionDebug { false };
diff --git a/src/mbgl/map/resource_loader.cpp b/src/mbgl/map/resource_loader.cpp
index 2be86dc5e2..8e054c0d82 100644
--- a/src/mbgl/map/resource_loader.cpp
+++ b/src/mbgl/map/resource_loader.cpp
@@ -45,7 +45,7 @@ void ResourceLoader::setStyle(Style* style) {
for (const auto& source : style->sources) {
source->setObserver(this);
- source->load(accessToken_);
+ source->load();
}
}
@@ -60,11 +60,6 @@ void ResourceLoader::setGlyphStore(GlyphStore* glyphStore) {
glyphStore_->setObserver(this);
}
-
-void ResourceLoader::setAccessToken(const std::string& accessToken) {
- accessToken_ = accessToken;
-}
-
void ResourceLoader::update(MapData& data,
const TransformState& transform,
GlyphAtlas& glyphAtlas,
@@ -76,7 +71,7 @@ void ResourceLoader::update(MapData& data,
const float pixelRatio = transform.getPixelRatio();
if (!sprite_ || !sprite_->hasPixelRatio(pixelRatio)) {
- sprite_ = util::make_unique<Sprite>(style_->getSpriteURL(), pixelRatio);
+ sprite_ = std::make_unique<Sprite>(style_->getSpriteURL(), pixelRatio);
sprite_->setObserver(this);
spriteAtlas.resize(pixelRatio);
@@ -104,10 +99,18 @@ void ResourceLoader::onGlyphRangeLoaded() {
emitTileDataChanged();
}
+void ResourceLoader::onGlyphRangeLoadingFailed(std::exception_ptr error) {
+ emitResourceLoadingFailed(error);
+}
+
void ResourceLoader::onSourceLoaded() {
emitTileDataChanged();
}
+void ResourceLoader::onSourceLoadingFailed(std::exception_ptr error) {
+ emitResourceLoadingFailed(error);
+}
+
void ResourceLoader::onTileLoaded(bool isNewTile) {
if (isNewTile) {
shouldReparsePartialTiles_ = true;
@@ -116,12 +119,20 @@ void ResourceLoader::onTileLoaded(bool isNewTile) {
emitTileDataChanged();
}
+void ResourceLoader::onTileLoadingFailed(std::exception_ptr error) {
+ emitResourceLoadingFailed(error);
+}
+
void ResourceLoader::onSpriteLoaded() {
shouldReparsePartialTiles_ = true;
emitTileDataChanged();
}
+void ResourceLoader::onSpriteLoadingFailed(std::exception_ptr error) {
+ emitResourceLoadingFailed(error);
+}
+
void ResourceLoader::emitTileDataChanged() {
assert(Environment::currentlyOn(ThreadType::Map));
@@ -130,4 +141,20 @@ void ResourceLoader::emitTileDataChanged() {
}
}
+void ResourceLoader::emitResourceLoadingFailed(std::exception_ptr error) {
+ assert(Environment::currentlyOn(ThreadType::Map));
+
+ try {
+ if (error) {
+ std::rethrow_exception(error);
+ }
+ } catch(const std::exception& e) {
+ Log::Error(Event::ResourceLoader, e.what());
+ }
+
+ if (observer_) {
+ observer_->onResourceLoadingFailed(error);
+ }
+}
+
}
diff --git a/src/mbgl/map/resource_loader.hpp b/src/mbgl/map/resource_loader.hpp
index 379444135e..525e4653a0 100644
--- a/src/mbgl/map/resource_loader.hpp
+++ b/src/mbgl/map/resource_loader.hpp
@@ -32,6 +32,7 @@ public:
virtual ~Observer() = default;
virtual void onTileDataChanged() = 0;
+ virtual void onResourceLoadingFailed(std::exception_ptr error) = 0;
};
ResourceLoader();
@@ -48,9 +49,6 @@ public:
// style.
void setGlyphStore(GlyphStore* glyphStore);
- // Set the access token to be used for loading the tile data.
- void setAccessToken(const std::string& accessToken);
-
// Fetch the tiles needed by the current viewport and emit a signal when
// a tile is ready so observers can render the tile.
void update(MapData&, const TransformState&, GlyphAtlas&, SpriteAtlas&, TexturePool&);
@@ -62,20 +60,24 @@ public:
// GlyphStore::Observer implementation.
void onGlyphRangeLoaded() override;
+ void onGlyphRangeLoadingFailed(std::exception_ptr error) override;
// Source::Observer implementation.
void onSourceLoaded() override;
+ void onSourceLoadingFailed(std::exception_ptr error) override;
void onTileLoaded(bool isNewTile) override;
+ void onTileLoadingFailed(std::exception_ptr error) override;
// Sprite::Observer implementation.
void onSpriteLoaded() override;
+ void onSpriteLoadingFailed(std::exception_ptr error) override;
private:
void emitTileDataChanged();
+ void emitResourceLoadingFailed(std::exception_ptr error);
bool shouldReparsePartialTiles_ = false;
- std::string accessToken_;
util::ptr<Sprite> sprite_;
GlyphStore* glyphStore_ = nullptr;
diff --git a/src/mbgl/map/source.cpp b/src/mbgl/map/source.cpp
index 331e48436f..b6838a4713 100644
--- a/src/mbgl/map/source.cpp
+++ b/src/mbgl/map/source.cpp
@@ -4,6 +4,7 @@
#include <mbgl/map/transform.hpp>
#include <mbgl/map/tile.hpp>
#include <mbgl/renderer/painter.hpp>
+#include <mbgl/util/exception.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
@@ -13,6 +14,7 @@
#include <mbgl/style/style_layer.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/util/uv_detail.hpp>
+#include <mbgl/util/std.hpp>
#include <mbgl/util/token.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/tile_cover.hpp>
@@ -139,18 +141,19 @@ bool Source::isLoaded() const {
// Note: This is a separate function that must be called exactly once after creation
// The reason this isn't part of the constructor is that calling shared_from_this() in
// the constructor fails.
-void Source::load(const std::string& accessToken) {
+void Source::load() {
if (info.url.empty()) {
loaded = true;
return;
}
- const std::string url = util::mapbox::normalizeSourceURL(info.url, accessToken);
- req = Environment::Get().request({ Resource::Kind::JSON, url }, [this](const Response &res) {
+ req = Environment::Get().request({ Resource::Kind::Source, info.url }, [this](const Response &res) {
req = nullptr;
if (res.status != Response::Successful) {
- Log::Warning(Event::General, "Failed to load source TileJSON: %s", res.message.c_str());
+ std::stringstream message;
+ message << "Failed to load [" << info.url << "]: " << res.message;
+ emitSourceLoadingFailed(message.str());
return;
}
@@ -158,7 +161,9 @@ void Source::load(const std::string& accessToken) {
d.Parse<0>(res.data.c_str());
if (d.HasParseError()) {
- Log::Error(Event::General, "Invalid source TileJSON; Parse Error at %d: %s", d.GetErrorOffset(), d.GetParseError());
+ std::stringstream message;
+ message << "Failed to parse [" << info.url << "]: " << d.GetErrorOffset() << " - " << d.GetParseError();
+ emitSourceLoadingFailed(message.str());
return;
}
@@ -261,7 +266,8 @@ TileData::State Source::addTile(MapData& data,
}
const float overscaling = id.z > info.max_zoom ? std::pow(2.0f, id.z - info.max_zoom) : 1.0f;
- auto pos = tiles.emplace(id, util::make_unique<Tile>(id));//, util::tileSize * overscaling));
+ auto pos = tiles.emplace(id, std::make_unique<Tile>(id));
+
Tile& new_tile = *pos.first->second;
// We couldn't find the tile in the list. Create a new one.
@@ -283,8 +289,9 @@ TileData::State Source::addTile(MapData& data,
new_tile.data = cache.get(normalized_id.to_uint64());
}
- auto callback = std::bind(&Source::emitTileLoaded, this, true);
if (!new_tile.data) {
+ auto callback = std::bind(&Source::tileLoadingCompleteCallback, this, normalized_id);
+
// If we don't find working tile data, we're just going to load it.
if (info.type == SourceType::Vector) {
new_tile.data =
@@ -294,7 +301,8 @@ TileData::State Source::addTile(MapData& data,
new_tile.data->request(style.workers, transformState.getPixelRatio(), callback);
} else if (info.type == SourceType::Raster) {
new_tile.data = std::make_shared<RasterTileData>(normalized_id, texturePool, info);
- new_tile.data->request(style.workers, transformState.getPixelRatio(), callback);
+ new_tile.data->request(
+ style.workers, transformState.getPixelRatio(), callback);
} else if (info.type == SourceType::Annotations) {
new_tile.data = std::make_shared<LiveTileData>(normalized_id, data.annotationManager,
style, glyphAtlas,
@@ -546,16 +554,53 @@ void Source::setObserver(Observer* observer) {
observer_ = observer;
}
+void Source::tileLoadingCompleteCallback(const TileID& normalized_id) {
+ auto it = tile_data.find(normalized_id);
+ if (it == tile_data.end()) {
+ return;
+ }
+
+ util::ptr<TileData> data = it->second.lock();
+ if (!data) {
+ return;
+ }
+
+ if (data->getState() == TileData::State::obsolete && !data->getError().empty()) {
+ emitTileLoadingFailed(data->getError());
+ return;
+ }
+
+ emitTileLoaded(true);
+}
+
void Source::emitSourceLoaded() {
if (observer_) {
observer_->onSourceLoaded();
}
}
+void Source::emitSourceLoadingFailed(const std::string& message) {
+ if (!observer_) {
+ return;
+ }
+
+ auto error = std::make_exception_ptr(util::SourceLoadingException(message));
+ observer_->onSourceLoadingFailed(error);
+}
+
void Source::emitTileLoaded(bool isNewTile) {
if (observer_) {
observer_->onTileLoaded(isNewTile);
}
}
+void Source::emitTileLoadingFailed(const std::string& message) {
+ if (!observer_) {
+ return;
+ }
+
+ auto error = std::make_exception_ptr(util::TileLoadingException(message));
+ observer_->onTileLoadingFailed(error);
+}
+
}
diff --git a/src/mbgl/map/source.hpp b/src/mbgl/map/source.hpp
index 505292cc74..bf49bd69ea 100644
--- a/src/mbgl/map/source.hpp
+++ b/src/mbgl/map/source.hpp
@@ -58,13 +58,16 @@ public:
virtual ~Observer() = default;
virtual void onSourceLoaded() = 0;
+ virtual void onSourceLoadingFailed(std::exception_ptr error) = 0;
+
virtual void onTileLoaded(bool isNewTile) = 0;
+ virtual void onTileLoadingFailed(std::exception_ptr error) = 0;
};
Source();
~Source();
- void load(const std::string& accessToken);
+ void load();
bool isLoaded() const;
void load(MapData&, Environment&, std::function<void()> callback);
@@ -102,8 +105,12 @@ public:
private:
void redoPlacement(const TransformState& transformState, bool collisionDebug);
+ void tileLoadingCompleteCallback(const TileID& normalized_id);
+
void emitSourceLoaded();
+ void emitSourceLoadingFailed(const std::string& message);
void emitTileLoaded(bool isNewTile);
+ void emitTileLoadingFailed(const std::string& message);
bool handlePartialTile(const TileID &id, Worker &worker);
bool findLoadedChildren(const TileID& id, int32_t maxCoveringZoom, std::forward_list<TileID>& retain);
diff --git a/src/mbgl/map/sprite.cpp b/src/mbgl/map/sprite.cpp
index cfc0974571..d5af3e1d1d 100644
--- a/src/mbgl/map/sprite.cpp
+++ b/src/mbgl/map/sprite.cpp
@@ -1,17 +1,19 @@
#include <mbgl/map/sprite.hpp>
-#include <mbgl/util/raster.hpp>
-#include <mbgl/platform/log.hpp>
-#include <string>
-#include <mbgl/platform/platform.hpp>
#include <mbgl/map/environment.hpp>
+#include <mbgl/platform/log.hpp>
+#include <mbgl/platform/platform.hpp>
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
+#include <mbgl/util/exception.hpp>
+#include <mbgl/util/raster.hpp>
#include <mbgl/util/uv_detail.hpp>
-#include <mbgl/util/std.hpp>
#include <rapidjson/document.h>
+#include <string>
+#include <sstream>
+
using namespace mbgl;
SpritePosition::SpritePosition(uint16_t x_, uint16_t y_, uint16_t width_, uint16_t height_, float pixelRatio_, bool sdf_)
@@ -39,27 +41,31 @@ Sprite::Sprite(const std::string& baseUrl, float pixelRatio_)
std::string spriteURL(baseUrl + (pixelRatio_ > 1 ? "@2x" : "") + ".png");
std::string jsonURL(baseUrl + (pixelRatio_ > 1 ? "@2x" : "") + ".json");
- jsonRequest = env.request({ Resource::Kind::JSON, jsonURL }, [this](const Response &res) {
+ jsonRequest = env.request({ Resource::Kind::JSON, jsonURL }, [this, jsonURL](const Response &res) {
jsonRequest = nullptr;
if (res.status == Response::Successful) {
body = res.data;
- parseJSON();
+ parseJSON(jsonURL);
} else {
- Log::Warning(Event::Sprite, "Failed to load sprite info: %s", res.message.c_str());
+ std::stringstream message;
+ message << "Failed to load [" << jsonURL << "]: " << res.message;
+ emitSpriteLoadingFailed(message.str());
+ return;
}
- loadedJSON = true;
emitSpriteLoadedIfComplete();
});
- spriteRequest = env.request({ Resource::Kind::Image, spriteURL }, [this](const Response &res) {
+ spriteRequest = env.request({ Resource::Kind::Image, spriteURL }, [this, spriteURL](const Response &res) {
spriteRequest = nullptr;
if (res.status == Response::Successful) {
image = res.data;
- parseImage();
+ parseImage(spriteURL);
} else {
- Log::Warning(Event::Sprite, "Failed to load sprite image: %s", res.message.c_str());
+ std::stringstream message;
+ message << "Failed to load [" << spriteURL << "]: " << res.message;
+ emitSpriteLoadingFailed(message.str());
+ return;
}
- loadedImage = true;
emitSpriteLoadedIfComplete();
});
}
@@ -80,6 +86,15 @@ void Sprite::emitSpriteLoadedIfComplete() {
}
}
+void Sprite::emitSpriteLoadingFailed(const std::string& message) {
+ if (!observer) {
+ return;
+ }
+
+ auto error = std::make_exception_ptr(util::SpriteLoadingException(message));
+ observer->onSpriteLoadingFailed(error);
+}
+
bool Sprite::isLoaded() const {
return loadedImage && loadedJSON;
}
@@ -88,21 +103,29 @@ bool Sprite::hasPixelRatio(float ratio) const {
return pixelRatio == (ratio > 1 ? 2 : 1);
}
-void Sprite::parseImage() {
- raster = util::make_unique<util::Image>(image);
+void Sprite::parseImage(const std::string& spriteURL) {
+ raster = std::make_unique<util::Image>(image);
if (!*raster) {
raster.reset();
+ std::stringstream message;
+ message << "Failed to parse [" << spriteURL << "]";
+ emitSpriteLoadingFailed(message.str());
+ return;
}
+
image.clear();
+ loadedImage = true;
}
-void Sprite::parseJSON() {
+void Sprite::parseJSON(const std::string& jsonURL) {
rapidjson::Document d;
d.Parse<0>(body.c_str());
body.clear();
if (d.HasParseError()) {
- Log::Warning(Event::Sprite, "sprite JSON is invalid");
+ std::stringstream message;
+ message << "Failed to parse [" << jsonURL << "]: " << d.GetErrorOffset() << " - " << d.GetParseError();
+ emitSpriteLoadingFailed(message.str());
} else if (d.IsObject()) {
for (rapidjson::Value::ConstMemberIterator itr = d.MemberBegin(); itr != d.MemberEnd(); ++itr) {
const std::string& name = itr->name.GetString();
@@ -125,8 +148,11 @@ void Sprite::parseJSON() {
pos.emplace(name, SpritePosition { x, y, width, height, spritePixelRatio, sdf });
}
}
+ loadedJSON = true;
} else {
- Log::Warning(Event::Sprite, "sprite JSON root is not an object");
+ std::stringstream message;
+ message << "Failed to parse [" << jsonURL << "]: Root is not an object";
+ emitSpriteLoadingFailed(message.str());
}
}
diff --git a/src/mbgl/map/sprite.hpp b/src/mbgl/map/sprite.hpp
index ccf2561618..b0fda30018 100644
--- a/src/mbgl/map/sprite.hpp
+++ b/src/mbgl/map/sprite.hpp
@@ -39,6 +39,7 @@ public:
virtual ~Observer() = default;
virtual void onSpriteLoaded() = 0;
+ virtual void onSpriteLoadingFailed(std::exception_ptr error) = 0;
};
Sprite(const std::string& baseUrl, float pixelRatio);
@@ -56,9 +57,10 @@ public:
void setObserver(Observer* observer);
private:
void emitSpriteLoadedIfComplete();
+ void emitSpriteLoadingFailed(const std::string& message);
- void parseJSON();
- void parseImage();
+ void parseJSON(const std::string& jsonURL);
+ void parseImage(const std::string& spriteURL);
std::string body;
std::string image;
diff --git a/src/mbgl/map/tile_data.cpp b/src/mbgl/map/tile_data.cpp
index 83488ecb9b..6ff92bb6e5 100644
--- a/src/mbgl/map/tile_data.cpp
+++ b/src/mbgl/map/tile_data.cpp
@@ -1,12 +1,14 @@
#include <mbgl/map/tile_data.hpp>
+
#include <mbgl/map/environment.hpp>
#include <mbgl/map/source.hpp>
#include <mbgl/map/transform_state.hpp>
-
+#include <mbgl/platform/log.hpp>
#include <mbgl/storage/file_source.hpp>
-#include <mbgl/util/worker.hpp>
#include <mbgl/util/work_request.hpp>
-#include <mbgl/platform/log.hpp>
+#include <mbgl/util/worker.hpp>
+
+#include <sstream>
using namespace mbgl;
@@ -35,7 +37,9 @@ void TileData::setState(const State& state_) {
state = state_;
}
-void TileData::request(Worker& worker, float pixelRatio, std::function<void()> callback) {
+void TileData::request(Worker& worker,
+ float pixelRatio,
+ const std::function<void()>& callback) {
std::string url = source.tileURL(id, pixelRatio);
state = State::loading;
@@ -43,7 +47,10 @@ void TileData::request(Worker& worker, float pixelRatio, std::function<void()> c
req = nullptr;
if (res.status != Response::Successful) {
- Log::Error(Event::HttpRequest, "[%s] tile loading failed: %s", url.c_str(), res.message.c_str());
+ std::stringstream message;
+ message << "Failed to load [" << url << "]: " << res.message;
+ setError(message.str());
+ callback();
return;
}
@@ -82,3 +89,8 @@ bool TileData::reparse(Worker& worker, std::function<void()> callback) {
workRequest = worker.send([this] { parse(); endParsing(); }, callback);
return true;
}
+
+void TileData::setError(const std::string& message) {
+ error = message;
+ setState(State::obsolete);
+}
diff --git a/src/mbgl/map/tile_data.hpp b/src/mbgl/map/tile_data.hpp
index 1ec58ec839..2b237a61cb 100644
--- a/src/mbgl/map/tile_data.hpp
+++ b/src/mbgl/map/tile_data.hpp
@@ -45,7 +45,7 @@ public:
TileData(const TileID&, const SourceInfo&);
~TileData();
- void request(Worker&, float pixelRatio, std::function<void ()> callback);
+ void request(Worker&, float pixelRatio, const std::function<void()>& callback);
// Schedule a tile reparse on a worker thread and call the callback on
// completion. It will return true if the work was schedule or false it was
@@ -69,13 +69,20 @@ public:
// We let subclasses override setState() so they
// can intercept the state change and react accordingly.
virtual void setState(const State& state);
-
inline State getState() const {
return state;
}
void endParsing();
+ // Error message to be set in case of request
+ // and parsing errors.
+ void setError(const std::string& message);
+
+ std::string getError() const {
+ return error;
+ }
+
// Override this in the child class.
virtual void parse() = 0;
virtual Bucket* getBucket(StyleLayer const &layer_desc) = 0;
@@ -106,6 +113,8 @@ protected:
private:
std::atomic<State> state;
+ std::string error;
+
protected:
// Contains the tile ID string for painting debug information.
DebugFontBuffer debugFontBuffer;
diff --git a/src/mbgl/map/tile_parser.cpp b/src/mbgl/map/tile_parser.cpp
index eb9a7c2e9f..61af227034 100644
--- a/src/mbgl/map/tile_parser.cpp
+++ b/src/mbgl/map/tile_parser.cpp
@@ -8,7 +8,6 @@
#include <mbgl/renderer/line_bucket.hpp>
#include <mbgl/renderer/symbol_bucket.hpp>
#include <mbgl/util/constants.hpp>
-#include <mbgl/util/std.hpp>
#include <mbgl/style/style.hpp>
#include <locale>
@@ -154,7 +153,7 @@ void TileParser::addBucketGeometries(Bucket& bucket, const GeometryTileLayer& la
std::unique_ptr<Bucket> TileParser::createFillBucket(const GeometryTileLayer& layer,
const StyleBucket& bucket_desc) {
- auto bucket = util::make_unique<FillBucket>(tile.fillVertexBuffer,
+ auto bucket = std::make_unique<FillBucket>(tile.fillVertexBuffer,
tile.triangleElementsBuffer,
tile.lineElementsBuffer);
addBucketGeometries(bucket, layer, bucket_desc.filter);
@@ -163,7 +162,7 @@ std::unique_ptr<Bucket> TileParser::createFillBucket(const GeometryTileLayer& la
std::unique_ptr<Bucket> TileParser::createLineBucket(const GeometryTileLayer& layer,
const StyleBucket& bucket_desc) {
- auto bucket = util::make_unique<LineBucket>(tile.lineVertexBuffer,
+ auto bucket = std::make_unique<LineBucket>(tile.lineVertexBuffer,
tile.triangleElementsBuffer);
const float z = tile.id.z;
@@ -180,7 +179,7 @@ std::unique_ptr<Bucket> TileParser::createLineBucket(const GeometryTileLayer& la
std::unique_ptr<Bucket> TileParser::createSymbolBucket(const GeometryTileLayer& layer,
const StyleBucket& bucket_desc) {
- auto bucket = util::make_unique<SymbolBucket>(*tile.getCollision(), tile.id.overscaling);
+ auto bucket = std::make_unique<SymbolBucket>(*tile.getCollision(), tile.id.overscaling);
const float z = tile.id.z;
auto& layout = bucket->layout;
diff --git a/src/mbgl/map/transform.cpp b/src/mbgl/map/transform.cpp
index 32127fb26f..182e534295 100644
--- a/src/mbgl/map/transform.cpp
+++ b/src/mbgl/map/transform.cpp
@@ -2,7 +2,6 @@
#include <mbgl/map/view.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/mat4.hpp>
-#include <mbgl/util/std.hpp>
#include <mbgl/util/math.hpp>
#include <mbgl/util/unitbezier.hpp>
#include <mbgl/util/interpolate.hpp>
@@ -384,6 +383,7 @@ void Transform::_setAngle(double new_angle, const Duration duration) {
MapChangeRegionWillChange);
final.angle = _normalizeAngle(new_angle, current.angle);
+ current.angle = _normalizeAngle(current.angle, final.angle);
if (duration == Duration::zero()) {
current.angle = final.angle;
diff --git a/src/mbgl/map/vector_tile_data.cpp b/src/mbgl/map/vector_tile_data.cpp
index fb64c224cf..4cb657038d 100644
--- a/src/mbgl/map/vector_tile_data.cpp
+++ b/src/mbgl/map/vector_tile_data.cpp
@@ -1,6 +1,5 @@
#include <mbgl/map/vector_tile_data.hpp>
#include <mbgl/map/tile_parser.hpp>
-#include <mbgl/util/std.hpp>
#include <mbgl/style/style_layer.hpp>
#include <mbgl/style/style_bucket.hpp>
#include <mbgl/map/source.hpp>
@@ -31,7 +30,7 @@ VectorTileData::VectorTileData(const TileID& id_,
sprite(sprite_),
style(style_),
overscaling(overscaling_),
- collision(util::make_unique<CollisionTile>(id_.z, 4096, source_.tile_size * id.overscaling, angle, collisionDebug)),
+ collision(std::make_unique<CollisionTile>(id_.z, 4096, source_.tile_size * id.overscaling, angle, collisionDebug)),
lastAngle(angle),
currentAngle(angle) {
}
@@ -68,9 +67,9 @@ void VectorTileData::parse() {
redoPlacement();
}
} catch (const std::exception& ex) {
- Log::Error(Event::ParseTile, "Parsing [%d/%d/%d] failed: %s", id.z, id.x, id.y, ex.what());
- setState(State::obsolete);
- return;
+ std::stringstream message;
+ message << "Failed to parse [" << int(id.z) << "/" << id.x << "/" << id.y << "]: " << ex.what();
+ setError(message.str());
}
}
diff --git a/src/mbgl/renderer/fill_bucket.cpp b/src/mbgl/renderer/fill_bucket.cpp
index c59b0970e0..a98a8b7cdf 100644
--- a/src/mbgl/renderer/fill_bucket.cpp
+++ b/src/mbgl/renderer/fill_bucket.cpp
@@ -7,7 +7,6 @@
#include <mbgl/shader/plain_shader.hpp>
#include <mbgl/shader/pattern_shader.hpp>
#include <mbgl/shader/outline_shader.hpp>
-#include <mbgl/util/std.hpp>
#include <mbgl/platform/gl.hpp>
#include <mbgl/platform/log.hpp>
@@ -107,7 +106,7 @@ void FillBucket::tessellate() {
if (!lineGroups.size() || (lineGroups.back()->vertex_length + total_vertex_count > 65535)) {
// Move to a new group because the old one can't hold the geometry.
- lineGroups.emplace_back(util::make_unique<LineGroup>());
+ lineGroups.emplace_back(std::make_unique<LineGroup>());
}
assert(lineGroups.back());
@@ -154,7 +153,7 @@ void FillBucket::tessellate() {
if (!triangleGroups.size() || (triangleGroups.back()->vertex_length + total_vertex_count > 65535)) {
// Move to a new group because the old one can't hold the geometry.
- triangleGroups.emplace_back(util::make_unique<TriangleGroup>());
+ triangleGroups.emplace_back(std::make_unique<TriangleGroup>());
}
// We're generating triangle fans, so we always start with the first
diff --git a/src/mbgl/renderer/line_bucket.cpp b/src/mbgl/renderer/line_bucket.cpp
index bbfc02ead1..f696874afe 100644
--- a/src/mbgl/renderer/line_bucket.cpp
+++ b/src/mbgl/renderer/line_bucket.cpp
@@ -8,7 +8,6 @@
#include <mbgl/shader/linesdf_shader.hpp>
#include <mbgl/shader/linepattern_shader.hpp>
#include <mbgl/util/math.hpp>
-#include <mbgl/util/std.hpp>
#include <mbgl/platform/gl.hpp>
#ifndef BUFFER_OFFSET
@@ -290,7 +289,7 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
if (!triangleGroups.size() ||
(triangleGroups.back()->vertex_length + vertexCount > 65535)) {
// Move to a new group because the old one can't hold the geometry.
- triangleGroups.emplace_back(util::make_unique<TriangleGroup>());
+ triangleGroups.emplace_back(std::make_unique<TriangleGroup>());
}
assert(triangleGroups.back());
diff --git a/src/mbgl/renderer/painter.cpp b/src/mbgl/renderer/painter.cpp
index 722a520027..977b6e565f 100644
--- a/src/mbgl/renderer/painter.cpp
+++ b/src/mbgl/renderer/painter.cpp
@@ -27,7 +27,6 @@
#include <mbgl/shader/gaussian_shader.hpp>
#include <mbgl/shader/box_shader.hpp>
-#include <mbgl/util/std.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/mat3.hpp>
@@ -95,19 +94,19 @@ void Painter::setup() {
}
void Painter::setupShaders() {
- if (!plainShader) plainShader = util::make_unique<PlainShader>();
- if (!outlineShader) outlineShader = util::make_unique<OutlineShader>();
- if (!lineShader) lineShader = util::make_unique<LineShader>();
- if (!linesdfShader) linesdfShader = util::make_unique<LineSDFShader>();
- if (!linepatternShader) linepatternShader = util::make_unique<LinepatternShader>();
- if (!patternShader) patternShader = util::make_unique<PatternShader>();
- if (!iconShader) iconShader = util::make_unique<IconShader>();
- if (!rasterShader) rasterShader = util::make_unique<RasterShader>();
- if (!sdfGlyphShader) sdfGlyphShader = util::make_unique<SDFGlyphShader>();
- if (!sdfIconShader) sdfIconShader = util::make_unique<SDFIconShader>();
- if (!dotShader) dotShader = util::make_unique<DotShader>();
- if (!gaussianShader) gaussianShader = util::make_unique<GaussianShader>();
- if (!collisionBoxShader) collisionBoxShader = util::make_unique<CollisionBoxShader>();
+ if (!plainShader) plainShader = std::make_unique<PlainShader>();
+ if (!outlineShader) outlineShader = std::make_unique<OutlineShader>();
+ if (!lineShader) lineShader = std::make_unique<LineShader>();
+ if (!linesdfShader) linesdfShader = std::make_unique<LineSDFShader>();
+ if (!linepatternShader) linepatternShader = std::make_unique<LinepatternShader>();
+ if (!patternShader) patternShader = std::make_unique<PatternShader>();
+ if (!iconShader) iconShader = std::make_unique<IconShader>();
+ if (!rasterShader) rasterShader = std::make_unique<RasterShader>();
+ if (!sdfGlyphShader) sdfGlyphShader = std::make_unique<SDFGlyphShader>();
+ if (!sdfIconShader) sdfIconShader = std::make_unique<SDFIconShader>();
+ if (!dotShader) dotShader = std::make_unique<DotShader>();
+ if (!gaussianShader) gaussianShader = std::make_unique<GaussianShader>();
+ if (!collisionBoxShader) collisionBoxShader = std::make_unique<CollisionBoxShader>();
}
void Painter::resize() {
diff --git a/src/mbgl/renderer/painter_fill.cpp b/src/mbgl/renderer/painter_fill.cpp
index 649bd46794..b2fdafadbd 100644
--- a/src/mbgl/renderer/painter_fill.cpp
+++ b/src/mbgl/renderer/painter_fill.cpp
@@ -9,7 +9,6 @@
#include <mbgl/shader/outline_shader.hpp>
#include <mbgl/shader/pattern_shader.hpp>
#include <mbgl/shader/plain_shader.hpp>
-#include <mbgl/util/std.hpp>
#include <mbgl/util/mat3.hpp>
using namespace mbgl;
diff --git a/src/mbgl/renderer/painter_raster.cpp b/src/mbgl/renderer/painter_raster.cpp
index 61aff9c1a8..2a8f8e7078 100644
--- a/src/mbgl/renderer/painter_raster.cpp
+++ b/src/mbgl/renderer/painter_raster.cpp
@@ -3,7 +3,6 @@
#include <mbgl/renderer/raster_bucket.hpp>
#include <mbgl/style/style_layer.hpp>
#include <mbgl/shader/raster_shader.hpp>
-#include <mbgl/util/std.hpp>
using namespace mbgl;
diff --git a/src/mbgl/renderer/symbol_bucket.cpp b/src/mbgl/renderer/symbol_bucket.cpp
index 359a9fe9f8..b0b8a6db4f 100644
--- a/src/mbgl/renderer/symbol_bucket.cpp
+++ b/src/mbgl/renderer/symbol_bucket.cpp
@@ -9,6 +9,7 @@
#include <mbgl/text/get_anchors.hpp>
#include <mbgl/renderer/painter.hpp>
#include <mbgl/text/glyph_store.hpp>
+#include <mbgl/text/font_stack.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/text/collision_tile.hpp>
#include <mbgl/shader/sdf_shader.hpp>
@@ -217,7 +218,7 @@ void SymbolBucket::addFeatures(uintptr_t tileUID,
layout.text.justify == TextJustifyType::Left ? 0 :
0.5;
- auto* fontStack = glyphStore.getFontStack(layout.text.font);
+ auto fontStack = glyphStore.getFontStack(layout.text.font);
for (const auto& feature : features) {
if (!feature.geometry.size()) continue;
@@ -241,7 +242,7 @@ void SymbolBucket::addFeatures(uintptr_t tileUID,
// Add the glyphs we need for this label to the glyph atlas.
if (shapedText) {
- glyphAtlas.addGlyphs(tileUID, feature.label, layout.text.font, *fontStack, face);
+ glyphAtlas.addGlyphs(tileUID, feature.label, layout.text.font, **fontStack, face);
}
}
@@ -322,7 +323,7 @@ void SymbolBucket::placeFeatures() {
void SymbolBucket::placeFeatures(bool swapImmediately) {
- renderDataInProgress = util::make_unique<SymbolRenderData>();
+ renderDataInProgress = std::make_unique<SymbolRenderData>();
// Calculate which labels can be shown and when they can be shown and
// create the bufers used for rendering.
@@ -427,7 +428,7 @@ void SymbolBucket::addSymbols(Buffer &buffer, const SymbolQuads &symbols, float
if (!buffer.groups.size() ||
(buffer.groups.back()->vertex_length + glyph_vertex_length > 65535)) {
// Move to a new group because the old one can't hold the geometry.
- buffer.groups.emplace_back(util::make_unique<GroupType>());
+ buffer.groups.emplace_back(std::make_unique<GroupType>());
}
// We're generating triangle fans, so we always start with the first
@@ -488,7 +489,7 @@ void SymbolBucket::addToDebugBuffers() {
auto& collisionBox = renderDataInProgress->collisionBox;
if (!collisionBox.groups.size()) {
// Move to a new group because the old one can't hold the geometry.
- collisionBox.groups.emplace_back(util::make_unique<CollisionBoxElementGroup>());
+ collisionBox.groups.emplace_back(std::make_unique<CollisionBoxElementGroup>());
}
collisionBox.vertices.add(anchor.x, anchor.y, tl.x, tl.y, maxZoom, placementZoom);
diff --git a/src/mbgl/shader/shader.cpp b/src/mbgl/shader/shader.cpp
index a079409aa0..6210c1c32e 100644
--- a/src/mbgl/shader/shader.cpp
+++ b/src/mbgl/shader/shader.cpp
@@ -51,7 +51,7 @@ Shader::Shader(const char *name_, const GLchar *vertSource, const GLchar *fragSo
if (status == 0) {
GLint logLength;
MBGL_CHECK_ERROR(glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength));
- std::unique_ptr<GLchar[]> log = mbgl::util::make_unique<GLchar[]>(logLength);
+ const auto log = std::make_unique<GLchar[]>(logLength);
if (logLength > 0) {
MBGL_CHECK_ERROR(glGetProgramInfoLog(program, logLength, &logLength, log.get()));
Log::Error(Event::Shader, "Program failed to link: %s", log.get());
@@ -76,7 +76,7 @@ Shader::Shader(const char *name_, const GLchar *vertSource, const GLchar *fragSo
if (status == 0) {
GLint logLength;
MBGL_CHECK_ERROR(glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength));
- std::unique_ptr<GLchar[]> log = mbgl::util::make_unique<GLchar[]>(logLength);
+ const auto log = std::make_unique<GLchar[]>(logLength);
if (logLength > 0) {
MBGL_CHECK_ERROR(glGetProgramInfoLog(program, logLength, &logLength, log.get()));
Log::Error(Event::Shader, "Program failed to validate: %s", log.get());
@@ -115,7 +115,7 @@ bool Shader::compileShader(GLuint *shader, GLenum type, const GLchar *source) {
GLint logLength;
MBGL_CHECK_ERROR(glGetShaderiv(*shader, GL_INFO_LOG_LENGTH, &logLength));
if (logLength > 0) {
- std::unique_ptr<GLchar[]> log = mbgl::util::make_unique<GLchar[]>(logLength);
+ const auto log = std::make_unique<GLchar[]>(logLength);
MBGL_CHECK_ERROR(glGetShaderInfoLog(*shader, logLength, &logLength, log.get()));
Log::Error(Event::Shader, "Shader failed to compile: %s", log.get());
}
diff --git a/src/mbgl/storage/default_file_source.cpp b/src/mbgl/storage/default_file_source.cpp
index e8831f5465..b46696aaa3 100644
--- a/src/mbgl/storage/default_file_source.cpp
+++ b/src/mbgl/storage/default_file_source.cpp
@@ -5,11 +5,12 @@
#include <mbgl/storage/response.hpp>
#include <mbgl/platform/platform.hpp>
+#include <mbgl/platform/log.hpp>
#include <mbgl/util/uv_detail.hpp>
#include <mbgl/util/chrono.hpp>
#include <mbgl/util/thread.hpp>
-#include <mbgl/platform/log.hpp>
+#include <mbgl/util/mapbox.hpp>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
@@ -27,7 +28,7 @@ namespace algo = boost::algorithm;
namespace mbgl {
DefaultFileSource::DefaultFileSource(FileCache* cache, const std::string& root)
- : thread(util::make_unique<util::Thread<Impl>>("FileSource", util::ThreadPriority::Low, cache, root)) {
+ : thread(std::make_unique<util::Thread<Impl>>("FileSource", util::ThreadPriority::Low, cache, root)) {
}
DefaultFileSource::~DefaultFileSource() {
@@ -38,7 +39,27 @@ Request* DefaultFileSource::request(const Resource& resource,
uv_loop_t* l,
Callback callback) {
assert(l);
- auto req = new Request(resource, l, std::move(callback));
+
+ std::string url;
+
+ switch (resource.kind) {
+ case Resource::Kind::Style:
+ url = mbgl::util::mapbox::normalizeStyleURL(resource.url, accessToken);
+ break;
+
+ case Resource::Kind::Source:
+ url = util::mapbox::normalizeSourceURL(resource.url, accessToken);
+ break;
+
+ case Resource::Kind::Glyphs:
+ url = util::mapbox::normalizeGlyphsURL(resource.url, accessToken);
+ break;
+
+ default:
+ url = resource.url;
+ }
+
+ auto req = new Request({ resource.kind, url }, l, std::move(callback));
thread->invoke(&Impl::add, req);
return req;
}
diff --git a/src/mbgl/storage/request.cpp b/src/mbgl/storage/request.cpp
index a6d845ce4a..b653a41e71 100644
--- a/src/mbgl/storage/request.cpp
+++ b/src/mbgl/storage/request.cpp
@@ -4,7 +4,6 @@
#include <mbgl/storage/response.hpp>
#include <mbgl/util/util.hpp>
-#include <mbgl/util/std.hpp>
#include <mbgl/util/uv_detail.hpp>
#include <cassert>
@@ -16,7 +15,7 @@ struct Request::Canceled { std::mutex mutex; bool confirmed = false; };
// Note: This requires that loop is running in the current thread (or not yet running).
Request::Request(const Resource &resource_, uv_loop_t *loop, Callback callback_)
- : async(util::make_unique<uv::async>(loop, [this] { notifyCallback(); })),
+ : async(std::make_unique<uv::async>(loop, [this] { notifyCallback(); })),
callback(callback_),
resource(resource_) {
}
@@ -62,7 +61,7 @@ void Request::notify(const std::shared_ptr<const Response> &response_) {
void Request::cancel() {
assert(async);
assert(!canceled);
- canceled = util::make_unique<Canceled>();
+ canceled = std::make_unique<Canceled>();
}
diff --git a/src/mbgl/style/style.cpp b/src/mbgl/style/style.cpp
index 51d92fdcaf..0acf66eb56 100644
--- a/src/mbgl/style/style.cpp
+++ b/src/mbgl/style/style.cpp
@@ -5,7 +5,6 @@
#include <mbgl/style/style_parser.hpp>
#include <mbgl/style/style_bucket.hpp>
#include <mbgl/util/constants.hpp>
-#include <mbgl/util/std.hpp>
#include <mbgl/util/uv_detail.hpp>
#include <mbgl/platform/log.hpp>
#include <csscolorparser/csscolorparser.hpp>
@@ -17,7 +16,7 @@
namespace mbgl {
Style::Style()
- : mtx(util::make_unique<uv::rwlock>()),
+ : mtx(std::make_unique<uv::rwlock>()),
workers(4) {
}
diff --git a/src/mbgl/style/style_parser.cpp b/src/mbgl/style/style_parser.cpp
index 313fe3df89..83a113f885 100644
--- a/src/mbgl/style/style_parser.cpp
+++ b/src/mbgl/style/style_parser.cpp
@@ -3,7 +3,6 @@
#include <mbgl/style/style_layer.hpp>
#include <mbgl/map/annotation.hpp>
#include <mbgl/util/constants.hpp>
-#include <mbgl/util/std.hpp>
#include <mbgl/util/vec.hpp>
#include <mbgl/util/uv_detail.hpp>
#include <mbgl/platform/log.hpp>
diff --git a/src/mbgl/text/font_stack.cpp b/src/mbgl/text/font_stack.cpp
new file mode 100644
index 0000000000..adabe2eb69
--- /dev/null
+++ b/src/mbgl/text/font_stack.cpp
@@ -0,0 +1,140 @@
+#include <mbgl/text/font_stack.hpp>
+#include <cassert>
+#include <mbgl/util/math.hpp>
+
+namespace mbgl {
+
+void FontStack::insert(uint32_t id, const SDFGlyph &glyph) {
+ metrics.emplace(id, glyph.metrics);
+ bitmaps.emplace(id, glyph.bitmap);
+ sdfs.emplace(id, glyph);
+}
+
+const std::map<uint32_t, GlyphMetrics> &FontStack::getMetrics() const {
+ return metrics;
+}
+
+const std::map<uint32_t, SDFGlyph> &FontStack::getSDFs() const {
+ return sdfs;
+}
+
+const Shaping FontStack::getShaping(const std::u32string &string, const float maxWidth,
+ const float lineHeight, const float horizontalAlign,
+ const float verticalAlign, const float justify,
+ const float spacing, const vec2<float> &translate) const {
+ Shaping shaping(translate.x * 24, translate.y * 24);
+
+ // the y offset *should* be part of the font metadata
+ const int32_t yOffset = -17;
+
+ int32_t x = std::round(translate.x * 24); // one em
+ const int32_t y = std::round(translate.y * 24) + yOffset; // one em
+
+ // Loop through all characters of this label and shape.
+ for (uint32_t chr : string) {
+ shaping.positionedGlyphs.emplace_back(chr, x, y);
+ auto metric = metrics.find(chr);
+ if (metric != metrics.end()) {
+ x += metric->second.advance + spacing;
+ }
+ }
+
+ if (!shaping.positionedGlyphs.size())
+ return shaping;
+
+ lineWrap(shaping, lineHeight, maxWidth, horizontalAlign, verticalAlign, justify);
+
+ return shaping;
+}
+
+void align(Shaping &shaping, const float justify, const float horizontalAlign,
+ const float verticalAlign, const uint32_t maxLineLength, const float lineHeight,
+ const uint32_t line) {
+ const float shiftX = (justify - horizontalAlign) * maxLineLength;
+ const float shiftY = (-verticalAlign * (line + 1) + 0.5) * lineHeight;
+
+ for (auto& glyph : shaping.positionedGlyphs) {
+ glyph.x += shiftX;
+ glyph.y += shiftY;
+ }
+}
+
+void justifyLine(std::vector<PositionedGlyph> &positionedGlyphs, const std::map<uint32_t, GlyphMetrics> &metrics, uint32_t start,
+ uint32_t end, float justify) {
+ PositionedGlyph &glyph = positionedGlyphs[end];
+ auto metric = metrics.find(glyph.glyph);
+ if (metric != metrics.end()) {
+ const uint32_t lastAdvance = metric->second.advance;
+ const float lineIndent = float(glyph.x + lastAdvance) * justify;
+
+ for (uint32_t j = start; j <= end; j++) {
+ positionedGlyphs[j].x -= lineIndent;
+ }
+ }
+}
+
+void FontStack::lineWrap(Shaping &shaping, const float lineHeight, const float maxWidth,
+ const float horizontalAlign, const float verticalAlign,
+ const float justify) const {
+ uint32_t lastSafeBreak = 0;
+
+ uint32_t lengthBeforeCurrentLine = 0;
+ uint32_t lineStartIndex = 0;
+ uint32_t line = 0;
+
+ uint32_t maxLineLength = 0;
+
+ std::vector<PositionedGlyph> &positionedGlyphs = shaping.positionedGlyphs;
+
+ if (maxWidth) {
+ for (uint32_t i = 0; i < positionedGlyphs.size(); i++) {
+ PositionedGlyph &shape = positionedGlyphs[i];
+
+ shape.x -= lengthBeforeCurrentLine;
+ shape.y += lineHeight * line;
+
+ if (shape.x > maxWidth && lastSafeBreak > 0) {
+
+ uint32_t lineLength = positionedGlyphs[lastSafeBreak + 1].x;
+ maxLineLength = util::max(lineLength, maxLineLength);
+
+ for (uint32_t k = lastSafeBreak + 1; k <= i; k++) {
+ positionedGlyphs[k].y += lineHeight;
+ positionedGlyphs[k].x -= lineLength;
+ }
+
+ if (justify) {
+ justifyLine(positionedGlyphs, metrics, lineStartIndex, lastSafeBreak - 1, justify);
+ }
+
+ lineStartIndex = lastSafeBreak + 1;
+ lastSafeBreak = 0;
+ lengthBeforeCurrentLine += lineLength;
+ line++;
+ }
+
+ if (shape.glyph == 32) {
+ lastSafeBreak = i;
+ }
+ }
+ }
+
+ const PositionedGlyph& lastPositionedGlyph = positionedGlyphs.back();
+ const auto lastGlyphMetric = metrics.find(lastPositionedGlyph.glyph);
+ assert(lastGlyphMetric != metrics.end());
+ const uint32_t lastLineLength = lastPositionedGlyph.x + lastGlyphMetric->second.advance;
+ maxLineLength = std::max(maxLineLength, lastLineLength);
+
+ const uint32_t height = (line + 1) * lineHeight;
+
+ justifyLine(positionedGlyphs, metrics, lineStartIndex, uint32_t(positionedGlyphs.size()) - 1, justify);
+ align(shaping, justify, horizontalAlign, verticalAlign, maxLineLength, lineHeight, line);
+
+ // Calculate the bounding box
+ shaping.top += -verticalAlign * height;
+ shaping.bottom = shaping.top + height;
+ shaping.left += -horizontalAlign * maxLineLength;
+ shaping.right = shaping.left + maxLineLength;
+}
+
+} // end namespace mbgl
diff --git a/src/mbgl/text/font_stack.hpp b/src/mbgl/text/font_stack.hpp
new file mode 100644
index 0000000000..7340cb17a9
--- /dev/null
+++ b/src/mbgl/text/font_stack.hpp
@@ -0,0 +1,28 @@
+#ifndef MBGL_TEXT_FONT_STACK
+#define MBGL_TEXT_FONT_STACK
+
+#include <mbgl/text/glyph.hpp>
+#include <mbgl/util/vec.hpp>
+
+namespace mbgl {
+
+class FontStack {
+public:
+ void insert(uint32_t id, const SDFGlyph &glyph);
+ const std::map<uint32_t, GlyphMetrics> &getMetrics() const;
+ const std::map<uint32_t, SDFGlyph> &getSDFs() const;
+ const Shaping getShaping(const std::u32string &string, float maxWidth, float lineHeight,
+ float horizontalAlign, float verticalAlign, float justify,
+ float spacing, const vec2<float> &translate) const;
+ void lineWrap(Shaping &shaping, float lineHeight, float maxWidth, float horizontalAlign,
+ float verticalAlign, float justify) const;
+
+private:
+ std::map<uint32_t, std::string> bitmaps;
+ std::map<uint32_t, GlyphMetrics> metrics;
+ std::map<uint32_t, SDFGlyph> sdfs;
+};
+
+} // end namespace mbgl
+
+#endif
diff --git a/src/mbgl/text/glyph.hpp b/src/mbgl/text/glyph.hpp
index 03016f0fbf..be7c9befd5 100644
--- a/src/mbgl/text/glyph.hpp
+++ b/src/mbgl/text/glyph.hpp
@@ -5,6 +5,7 @@
#include <cstdint>
#include <vector>
+#include <string>
#include <map>
namespace mbgl {
@@ -68,6 +69,17 @@ class Shaping {
operator bool() const { return positionedGlyphs.size(); }
};
-}
+class SDFGlyph {
+public:
+ uint32_t id = 0;
+
+ // A signed distance field of the glyph with a border of 3 pixels.
+ std::string bitmap;
+
+ // Glyph metrics
+ GlyphMetrics metrics;
+};
+
+} // end namespace mbgl
#endif
diff --git a/src/mbgl/text/glyph_pbf.cpp b/src/mbgl/text/glyph_pbf.cpp
new file mode 100644
index 0000000000..899df39d38
--- /dev/null
+++ b/src/mbgl/text/glyph_pbf.cpp
@@ -0,0 +1,115 @@
+#include <mbgl/text/glyph_pbf.hpp>
+#include <mbgl/text/font_stack.hpp>
+
+#include <mbgl/map/environment.hpp>
+
+#include <mbgl/storage/resource.hpp>
+#include <mbgl/storage/response.hpp>
+
+#include <mbgl/util/pbf.hpp>
+#include <mbgl/util/string.hpp>
+#include <mbgl/util/token.hpp>
+#include <mbgl/util/url.hpp>
+
+#include <sstream>
+
+namespace mbgl {
+
+GlyphPBF::GlyphPBF(const std::string& glyphURL,
+ const std::string& fontStack,
+ GlyphRange glyphRange,
+ Environment& env_,
+ const GlyphLoadedCallback& successCallback,
+ const GlyphLoadingFailedCallback& failureCallback)
+ : parsed(false), env(env_) {
+ // Load the glyph set URL
+ url = util::replaceTokens(glyphURL, [&](const std::string &name) -> std::string {
+ if (name == "fontstack") return util::percentEncode(fontStack);
+ if (name == "range") return util::toString(glyphRange.first) + "-" + util::toString(glyphRange.second);
+ return "";
+ });
+
+ // The prepare call jumps back to the main thread.
+ req = env.request({ Resource::Kind::Glyphs, url }, [&, successCallback, failureCallback](const Response &res) {
+ req = nullptr;
+
+ if (res.status != Response::Successful) {
+ std::stringstream message;
+ message << "Failed to load [" << url << "]: " << res.message;
+ failureCallback(message.str());
+ } else {
+ // Transfer the data to the GlyphSet and signal its availability.
+ // Once it is available, the caller will need to call parse() to actually
+ // parse the data we received. We are not doing this here since this callback is being
+ // called from another (unknown) thread.
+ data = res.data;
+ successCallback(this);
+ }
+ });
+}
+
+GlyphPBF::~GlyphPBF() {
+ if (req) {
+ env.cancelRequest(req);
+ }
+}
+
+void GlyphPBF::parse(FontStack &stack) {
+ if (!data.size()) {
+ // If there is no data, this means we either haven't received any data, or
+ // we have already parsed the data.
+ return;
+ }
+
+ // Parse the glyph PBF
+ pbf glyphs_pbf(reinterpret_cast<const uint8_t *>(data.data()), data.size());
+
+ while (glyphs_pbf.next()) {
+ if (glyphs_pbf.tag == 1) { // stacks
+ pbf fontstack_pbf = glyphs_pbf.message();
+ while (fontstack_pbf.next()) {
+ if (fontstack_pbf.tag == 3) { // glyphs
+ pbf glyph_pbf = fontstack_pbf.message();
+
+ SDFGlyph glyph;
+
+ while (glyph_pbf.next()) {
+ if (glyph_pbf.tag == 1) { // id
+ glyph.id = glyph_pbf.varint();
+ } else if (glyph_pbf.tag == 2) { // bitmap
+ glyph.bitmap = glyph_pbf.string();
+ } else if (glyph_pbf.tag == 3) { // width
+ glyph.metrics.width = glyph_pbf.varint();
+ } else if (glyph_pbf.tag == 4) { // height
+ glyph.metrics.height = glyph_pbf.varint();
+ } else if (glyph_pbf.tag == 5) { // left
+ glyph.metrics.left = glyph_pbf.svarint();
+ } else if (glyph_pbf.tag == 6) { // top
+ glyph.metrics.top = glyph_pbf.svarint();
+ } else if (glyph_pbf.tag == 7) { // advance
+ glyph.metrics.advance = glyph_pbf.varint();
+ } else {
+ glyph_pbf.skip();
+ }
+ }
+
+ stack.insert(glyph.id, glyph);
+ } else {
+ fontstack_pbf.skip();
+ }
+ }
+ } else {
+ glyphs_pbf.skip();
+ }
+ }
+
+ data.clear();
+
+ parsed = true;
+}
+
+bool GlyphPBF::isParsed() const {
+ return parsed;
+}
+
+}
diff --git a/src/mbgl/text/glyph_pbf.hpp b/src/mbgl/text/glyph_pbf.hpp
new file mode 100644
index 0000000000..bb6fa83ae6
--- /dev/null
+++ b/src/mbgl/text/glyph_pbf.hpp
@@ -0,0 +1,52 @@
+#ifndef MBGL_TEXT_GLYPH_PBF
+#define MBGL_TEXT_GLYPH_PBF
+
+#include <mbgl/text/glyph.hpp>
+
+#include <functional>
+#include <atomic>
+#include <string>
+
+namespace mbgl {
+
+class Environment;
+class FontStack;
+class Request;
+
+class GlyphPBF {
+public:
+ using GlyphLoadedCallback = std::function<void(GlyphPBF*)>;
+ using GlyphLoadingFailedCallback = std::function<void(const std::string&)>;
+
+ GlyphPBF(const std::string &glyphURL,
+ const std::string &fontStack,
+ GlyphRange glyphRange,
+ Environment &env,
+ const GlyphLoadedCallback& successCallback,
+ const GlyphLoadingFailedCallback& failureCallback);
+ ~GlyphPBF();
+
+ void parse(FontStack &stack);
+ bool isParsed() const;
+
+ std::string getURL() const {
+ return url;
+ }
+
+private:
+ GlyphPBF(const GlyphPBF &) = delete;
+ GlyphPBF(GlyphPBF &&) = delete;
+ GlyphPBF &operator=(const GlyphPBF &) = delete;
+ GlyphPBF &operator=(GlyphPBF &&) = delete;
+
+ std::string data;
+ std::string url;
+ std::atomic<bool> parsed;
+
+ Environment& env;
+ Request* req = nullptr;
+};
+
+} // end namespace mbgl
+
+#endif
diff --git a/src/mbgl/text/glyph_store.cpp b/src/mbgl/text/glyph_store.cpp
index 37e26b193c..9520b63c06 100644
--- a/src/mbgl/text/glyph_store.cpp
+++ b/src/mbgl/text/glyph_store.cpp
@@ -1,264 +1,19 @@
#include <mbgl/text/glyph_store.hpp>
+#include <mbgl/text/glyph_pbf.hpp>
+#include <mbgl/text/font_stack.hpp>
-#include <mbgl/map/environment.hpp>
-#include <mbgl/util/std.hpp>
-#include <mbgl/util/string.hpp>
-#include <mbgl/util/utf.hpp>
-#include <mbgl/util/pbf.hpp>
-#include <mbgl/util/url.hpp>
-#include <mbgl/util/constants.hpp>
-#include <mbgl/util/token.hpp>
-#include <mbgl/util/math.hpp>
+#include <mbgl/util/exception.hpp>
#include <mbgl/util/uv_detail.hpp>
-#include <mbgl/storage/file_source.hpp>
-#include <mbgl/platform/log.hpp>
-#include <mbgl/platform/platform.hpp>
-#include <mbgl/util/uv_detail.hpp>
-#include <algorithm>
namespace mbgl {
-
-void FontStack::insert(uint32_t id, const SDFGlyph &glyph) {
- std::lock_guard<std::mutex> lock(mtx);
- metrics.emplace(id, glyph.metrics);
- bitmaps.emplace(id, glyph.bitmap);
- sdfs.emplace(id, glyph);
-}
-
-const std::map<uint32_t, GlyphMetrics> &FontStack::getMetrics() const {
- std::lock_guard<std::mutex> lock(mtx);
- return metrics;
-}
-
-const std::map<uint32_t, SDFGlyph> &FontStack::getSDFs() const {
- std::lock_guard<std::mutex> lock(mtx);
- return sdfs;
-}
-
-const Shaping FontStack::getShaping(const std::u32string &string, const float maxWidth,
- const float lineHeight, const float horizontalAlign,
- const float verticalAlign, const float justify,
- const float spacing, const vec2<float> &translate) const {
- std::lock_guard<std::mutex> lock(mtx);
-
- Shaping shaping(translate.x * 24, translate.y * 24);
-
- // the y offset *should* be part of the font metadata
- const int32_t yOffset = -17;
-
- int32_t x = std::round(translate.x * 24); // one em
- const int32_t y = std::round(translate.y * 24) + yOffset; // one em
-
- // Loop through all characters of this label and shape.
- for (uint32_t chr : string) {
- shaping.positionedGlyphs.emplace_back(chr, x, y);
- auto metric = metrics.find(chr);
- if (metric != metrics.end()) {
- x += metric->second.advance + spacing;
- }
- }
-
- if (!shaping.positionedGlyphs.size())
- return shaping;
-
- lineWrap(shaping, lineHeight, maxWidth, horizontalAlign, verticalAlign, justify);
-
- return shaping;
-}
-
-void align(Shaping &shaping, const float justify, const float horizontalAlign,
- const float verticalAlign, const uint32_t maxLineLength, const float lineHeight,
- const uint32_t line) {
- const float shiftX = (justify - horizontalAlign) * maxLineLength;
- const float shiftY = (-verticalAlign * (line + 1) + 0.5) * lineHeight;
-
- for (auto& glyph : shaping.positionedGlyphs) {
- glyph.x += shiftX;
- glyph.y += shiftY;
- }
-}
-
-void justifyLine(std::vector<PositionedGlyph> &positionedGlyphs, const std::map<uint32_t, GlyphMetrics> &metrics, uint32_t start,
- uint32_t end, float justify) {
- PositionedGlyph &glyph = positionedGlyphs[end];
- auto metric = metrics.find(glyph.glyph);
- if (metric != metrics.end()) {
- const uint32_t lastAdvance = metric->second.advance;
- const float lineIndent = float(glyph.x + lastAdvance) * justify;
-
- for (uint32_t j = start; j <= end; j++) {
- positionedGlyphs[j].x -= lineIndent;
- }
- }
-}
-
-void FontStack::lineWrap(Shaping &shaping, const float lineHeight, const float maxWidth,
- const float horizontalAlign, const float verticalAlign,
- const float justify) const {
- uint32_t lastSafeBreak = 0;
-
- uint32_t lengthBeforeCurrentLine = 0;
- uint32_t lineStartIndex = 0;
- uint32_t line = 0;
-
- uint32_t maxLineLength = 0;
-
- std::vector<PositionedGlyph> &positionedGlyphs = shaping.positionedGlyphs;
-
- if (maxWidth) {
- for (uint32_t i = 0; i < positionedGlyphs.size(); i++) {
- PositionedGlyph &shape = positionedGlyphs[i];
-
- shape.x -= lengthBeforeCurrentLine;
- shape.y += lineHeight * line;
-
- if (shape.x > maxWidth && lastSafeBreak > 0) {
-
- uint32_t lineLength = positionedGlyphs[lastSafeBreak + 1].x;
- maxLineLength = util::max(lineLength, maxLineLength);
-
- for (uint32_t k = lastSafeBreak + 1; k <= i; k++) {
- positionedGlyphs[k].y += lineHeight;
- positionedGlyphs[k].x -= lineLength;
- }
-
- if (justify) {
- justifyLine(positionedGlyphs, metrics, lineStartIndex, lastSafeBreak - 1, justify);
- }
-
- lineStartIndex = lastSafeBreak + 1;
- lastSafeBreak = 0;
- lengthBeforeCurrentLine += lineLength;
- line++;
- }
-
- if (shape.glyph == 32) {
- lastSafeBreak = i;
- }
- }
- }
-
- const PositionedGlyph& lastPositionedGlyph = positionedGlyphs.back();
- const auto lastGlyphMetric = metrics.find(lastPositionedGlyph.glyph);
- assert(lastGlyphMetric != metrics.end());
- const uint32_t lastLineLength = lastPositionedGlyph.x + lastGlyphMetric->second.advance;
- maxLineLength = std::max(maxLineLength, lastLineLength);
-
- const uint32_t height = (line + 1) * lineHeight;
-
- justifyLine(positionedGlyphs, metrics, lineStartIndex, uint32_t(positionedGlyphs.size()) - 1, justify);
- align(shaping, justify, horizontalAlign, verticalAlign, maxLineLength, lineHeight, line);
-
- // Calculate the bounding box
- shaping.top += -verticalAlign * height;
- shaping.bottom = shaping.top + height;
- shaping.left += -horizontalAlign * maxLineLength;
- shaping.right = shaping.left + maxLineLength;
-}
-
-GlyphPBF::GlyphPBF(const std::string& glyphURL,
- const std::string& fontStack,
- GlyphRange glyphRange,
- Environment& env_,
- const GlyphLoadedCallback& callback)
- : parsed(false), env(env_) {
- // Load the glyph set URL
- std::string url = util::replaceTokens(glyphURL, [&](const std::string &name) -> std::string {
- if (name == "fontstack") return util::percentEncode(fontStack);
- if (name == "range") return util::toString(glyphRange.first) + "-" + util::toString(glyphRange.second);
- return "";
- });
-
- // The prepare call jumps back to the main thread.
- req = env.request({ Resource::Kind::Glyphs, url }, [&, url, callback](const Response &res) {
- req = nullptr;
-
- if (res.status != Response::Successful) {
- // Something went wrong with loading the glyph pbf.
- const std::string msg = std::string { "[ERROR] failed to load glyphs: " } + url + " message: " + res.message;
- Log::Error(Event::HttpRequest, msg);
- } else {
- // Transfer the data to the GlyphSet and signal its availability.
- // Once it is available, the caller will need to call parse() to actually
- // parse the data we received. We are not doing this here since this callback is being
- // called from another (unknown) thread.
- data = res.data;
- parsed = true;
- callback(this);
- }
- });
-}
-
-GlyphPBF::~GlyphPBF() {
- if (req) {
- env.cancelRequest(req);
- }
-}
-
-void GlyphPBF::parse(FontStack &stack) {
- std::lock_guard<std::mutex> lock(mtx);
-
- if (!data.size()) {
- // If there is no data, this means we either haven't received any data, or
- // we have already parsed the data.
- return;
- }
-
- // Parse the glyph PBF
- pbf glyphs_pbf(reinterpret_cast<const uint8_t *>(data.data()), data.size());
-
- while (glyphs_pbf.next()) {
- if (glyphs_pbf.tag == 1) { // stacks
- pbf fontstack_pbf = glyphs_pbf.message();
- while (fontstack_pbf.next()) {
- if (fontstack_pbf.tag == 3) { // glyphs
- pbf glyph_pbf = fontstack_pbf.message();
-
- SDFGlyph glyph;
-
- while (glyph_pbf.next()) {
- if (glyph_pbf.tag == 1) { // id
- glyph.id = glyph_pbf.varint();
- } else if (glyph_pbf.tag == 2) { // bitmap
- glyph.bitmap = glyph_pbf.string();
- } else if (glyph_pbf.tag == 3) { // width
- glyph.metrics.width = glyph_pbf.varint();
- } else if (glyph_pbf.tag == 4) { // height
- glyph.metrics.height = glyph_pbf.varint();
- } else if (glyph_pbf.tag == 5) { // left
- glyph.metrics.left = glyph_pbf.svarint();
- } else if (glyph_pbf.tag == 6) { // top
- glyph.metrics.top = glyph_pbf.svarint();
- } else if (glyph_pbf.tag == 7) { // advance
- glyph.metrics.advance = glyph_pbf.varint();
- } else {
- glyph_pbf.skip();
- }
- }
-
- stack.insert(glyph.id, glyph);
- } else {
- fontstack_pbf.skip();
- }
- }
- } else {
- glyphs_pbf.skip();
- }
- }
-
- data.clear();
-}
-
-bool GlyphPBF::isParsed() const {
- return parsed;
-}
-
GlyphStore::GlyphStore(uv_loop_t* loop, Environment& env_)
: env(env_),
- asyncEmitGlyphRangeLoaded(util::make_unique<uv::async>(loop, [this] { emitGlyphRangeLoaded(); })),
+ asyncEmitGlyphRangeLoaded(std::make_unique<uv::async>(loop, [this] { emitGlyphRangeLoaded(); })),
+ asyncEmitGlyphRangeLoadedingFailed(std::make_unique<uv::async>(loop, [this] { emitGlyphRangeLoadingFailed(); })),
observer(nullptr) {
asyncEmitGlyphRangeLoaded->unref();
+ asyncEmitGlyphRangeLoadedingFailed->unref();
}
GlyphStore::~GlyphStore() {
@@ -269,7 +24,7 @@ void GlyphStore::setURL(const std::string &url) {
glyphURL = url;
}
-bool GlyphStore::requestGlyphRangesIfNeeded(const std::string& fontStack,
+bool GlyphStore::requestGlyphRangesIfNeeded(const std::string& fontStackName,
const std::set<GlyphRange>& glyphRanges) {
bool requestIsNeeded = false;
@@ -277,18 +32,32 @@ bool GlyphStore::requestGlyphRangesIfNeeded(const std::string& fontStack,
return requestIsNeeded;
}
- auto callback = [this, fontStack](GlyphPBF* glyph) {
- glyph->parse(*createFontStack(fontStack));
- asyncEmitGlyphRangeLoaded->send();
+ auto successCallback = [this, fontStackName](GlyphPBF* glyph) {
+ auto fontStack = createFontStack(fontStackName);
+ try {
+ glyph->parse(**fontStack);
+ asyncEmitGlyphRangeLoaded->send();
+ } catch (const std::exception&) {
+ std::lock_guard<std::mutex> lock(errorMessageMutex);
+ errorMessage = "Failed to parse [" + glyph->getURL() + "]";
+ asyncEmitGlyphRangeLoadedingFailed->send();
+ }
+ };
+
+ auto failureCallback = [this](const std::string& message) {
+ std::lock_guard<std::mutex> lock(errorMessageMutex);
+ errorMessage = message;
+ asyncEmitGlyphRangeLoadedingFailed->send();
};
std::lock_guard<std::mutex> lock(rangesMutex);
- auto& rangeSets = ranges[fontStack];
+ auto& rangeSets = ranges[fontStackName];
for (const auto& range : glyphRanges) {
const auto& rangeSets_it = rangeSets.find(range);
if (rangeSets_it == rangeSets.end()) {
- auto glyph = util::make_unique<GlyphPBF>(glyphURL, fontStack, range, env, callback);
+ auto glyph = std::make_unique<GlyphPBF>(glyphURL, fontStackName, range, env,
+ successCallback, failureCallback);
rangeSets.emplace(range, std::move(glyph));
requestIsNeeded = true;
continue;
@@ -302,26 +71,26 @@ bool GlyphStore::requestGlyphRangesIfNeeded(const std::string& fontStack,
return requestIsNeeded;
}
-FontStack* GlyphStore::createFontStack(const std::string &fontStack) {
- std::lock_guard<std::mutex> lock(stacksMutex);
+util::exclusive<FontStack> GlyphStore::createFontStack(const std::string &fontStack) {
+ auto lock = std::make_unique<std::lock_guard<std::mutex>>(stacksMutex);
auto stack_it = stacks.find(fontStack);
if (stack_it == stacks.end()) {
- stack_it = stacks.emplace(fontStack, util::make_unique<FontStack>()).first;
+ stack_it = stacks.emplace(fontStack, std::make_unique<FontStack>()).first;
}
- return stack_it->second.get();
+ return { stack_it->second.get(), std::move(lock) };
}
-FontStack* GlyphStore::getFontStack(const std::string &fontStack) {
- std::lock_guard<std::mutex> lock(stacksMutex);
+util::exclusive<FontStack> GlyphStore::getFontStack(const std::string &fontStack) {
+ auto lock = std::make_unique<std::lock_guard<std::mutex>>(stacksMutex);
const auto& stack_it = stacks.find(fontStack);
if (stack_it == stacks.end()) {
- return nullptr;
+ return { nullptr, nullptr };
}
- return stack_it->second.get();
+ return { stack_it->second.get(), std::move(lock) };
}
void GlyphStore::setObserver(Observer* observer_) {
@@ -334,4 +103,14 @@ void GlyphStore::emitGlyphRangeLoaded() {
}
}
+void GlyphStore::emitGlyphRangeLoadingFailed() {
+ if (!observer) {
+ return;
+ }
+
+ std::lock_guard<std::mutex> lock(errorMessageMutex);
+ auto error = std::make_exception_ptr(util::GlyphRangeLoadingException(errorMessage));
+ observer->onGlyphRangeLoadingFailed(error);
+}
+
}
diff --git a/src/mbgl/text/glyph_store.hpp b/src/mbgl/text/glyph_store.hpp
index efa848dd08..746bcd1338 100644
--- a/src/mbgl/text/glyph_store.hpp
+++ b/src/mbgl/text/glyph_store.hpp
@@ -2,90 +2,27 @@
#define MBGL_TEXT_GLYPH_STORE
#include <mbgl/text/glyph.hpp>
-#include <mbgl/util/vec.hpp>
-#include <mbgl/util/ptr.hpp>
-#include <cstdint>
-#include <vector>
-#include <future>
-#include <map>
+#include <mbgl/util/exclusive.hpp>
+
#include <set>
#include <string>
#include <unordered_map>
+#include <exception>
typedef struct uv_loop_s uv_loop_t;
namespace uv {
-
class async;
-
}
namespace mbgl {
-class FileSource;
class Environment;
-class Request;
-
-class SDFGlyph {
-public:
- uint32_t id = 0;
-
- // A signed distance field of the glyph with a border of 3 pixels.
- std::string bitmap;
-
- // Glyph metrics
- GlyphMetrics metrics;
-};
-
-class FontStack {
-public:
- void insert(uint32_t id, const SDFGlyph &glyph);
- const std::map<uint32_t, GlyphMetrics> &getMetrics() const;
- const std::map<uint32_t, SDFGlyph> &getSDFs() const;
- const Shaping getShaping(const std::u32string &string, float maxWidth, float lineHeight,
- float horizontalAlign, float verticalAlign, float justify,
- float spacing, const vec2<float> &translate) const;
- void lineWrap(Shaping &shaping, float lineHeight, float maxWidth, float horizontalAlign,
- float verticalAlign, float justify) const;
-
-private:
- std::map<uint32_t, std::string> bitmaps;
- std::map<uint32_t, GlyphMetrics> metrics;
- std::map<uint32_t, SDFGlyph> sdfs;
- mutable std::mutex mtx;
-};
-
-class GlyphPBF {
-public:
- using GlyphLoadedCallback = std::function<void(GlyphPBF*)>;
-
- GlyphPBF(const std::string &glyphURL,
- const std::string &fontStack,
- GlyphRange glyphRange,
- Environment &env,
- const GlyphLoadedCallback& callback);
- ~GlyphPBF();
-
- void parse(FontStack &stack);
- bool isParsed() const;
-
-private:
- GlyphPBF(const GlyphPBF &) = delete;
- GlyphPBF(GlyphPBF &&) = delete;
- GlyphPBF &operator=(const GlyphPBF &) = delete;
- GlyphPBF &operator=(GlyphPBF &&) = delete;
-
- std::string data;
- std::atomic_bool parsed;
-
- Environment& env;
- Request* req = nullptr;
+class FontStack;
+class GlyphPBF;
- mutable std::mutex mtx;
-};
-
-// Manages Glyphrange PBF loading.
+// Manages GlyphRange PBF loading.
class GlyphStore {
public:
class Observer {
@@ -93,6 +30,7 @@ public:
virtual ~Observer() = default;
virtual void onGlyphRangeLoaded() = 0;
+ virtual void onGlyphRangeLoadingFailed(std::exception_ptr error) = 0;
};
GlyphStore(uv_loop_t* loop, Environment &);
@@ -104,7 +42,7 @@ public:
// GlyphRanges are already available, and thus, no request is performed.
bool requestGlyphRangesIfNeeded(const std::string &fontStack, const std::set<GlyphRange> &glyphRanges);
- FontStack* getFontStack(const std::string &fontStack);
+ util::exclusive<FontStack> getFontStack(const std::string &fontStack);
void setURL(const std::string &url);
@@ -112,8 +50,9 @@ public:
private:
void emitGlyphRangeLoaded();
+ void emitGlyphRangeLoadingFailed();
- FontStack* createFontStack(const std::string &fontStack);
+ util::exclusive<FontStack> createFontStack(const std::string &fontStack);
std::string glyphURL;
Environment &env;
@@ -124,7 +63,11 @@ private:
std::unordered_map<std::string, std::unique_ptr<FontStack>> stacks;
std::mutex stacksMutex;
+ std::string errorMessage;
+ std::mutex errorMessageMutex;
+
std::unique_ptr<uv::async> asyncEmitGlyphRangeLoaded;
+ std::unique_ptr<uv::async> asyncEmitGlyphRangeLoadedingFailed;
Observer* observer;
};
diff --git a/src/mbgl/util/raster.cpp b/src/mbgl/util/raster.cpp
index f2171a6165..b6b5df27f6 100644
--- a/src/mbgl/util/raster.cpp
+++ b/src/mbgl/util/raster.cpp
@@ -4,7 +4,6 @@
#include <mbgl/util/raster.hpp>
#include <mbgl/util/uv_detail.hpp>
-#include <mbgl/util/std.hpp>
#include <cassert>
#include <cstring>
@@ -27,7 +26,7 @@ bool Raster::isLoaded() const {
}
bool Raster::load(const std::string &data) {
- img = util::make_unique<util::Image>(data);
+ img = std::make_unique<util::Image>(data);
width = img->getWidth();
height = img->getHeight();
diff --git a/src/mbgl/util/run_loop.hpp b/src/mbgl/util/run_loop.hpp
index ed2b364cef..1c92847b69 100644
--- a/src/mbgl/util/run_loop.hpp
+++ b/src/mbgl/util/run_loop.hpp
@@ -2,10 +2,10 @@
#define MBGL_UTIL_RUN_LOOP
#include <mbgl/util/noncopyable.hpp>
-#include <mbgl/util/std.hpp>
#include <mbgl/util/uv_detail.hpp>
#include <functional>
+#include <utility>
#include <queue>
#include <mutex>
@@ -19,37 +19,38 @@ public:
void stop();
- // Invoke fn() in the runloop thread.
- template <class Fn>
- void invoke(Fn&& fn) {
- auto invokable = util::make_unique<Invoker<Fn>>(std::move(fn));
+ // Invoke fn(args...) on this RunLoop.
+ template <class Fn, class... Args>
+ void invoke(Fn&& fn, Args&&... args) {
+ auto tuple = std::make_tuple(std::move(args)...);
+ auto invokable = std::make_unique<Invoker<Fn, decltype(tuple), Args...>>(std::move(fn), std::move(tuple));
withMutex([&] { queue.push(std::move(invokable)); });
async.send();
}
- // Invoke fn() in the runloop thread, then invoke callback(result) in the current thread.
- template <class Fn, class R>
- void invokeWithResult(Fn&& fn, std::function<void (R)>&& callback) {
- RunLoop* outer = current.get();
- assert(outer);
-
- invoke([fn = std::move(fn), callback = std::move(callback), outer] () mutable {
- outer->invoke([callback = std::move(callback), result = std::move(fn())] () mutable {
- callback(std::move(result));
- });
- });
+ // Return a function that invokes the given function on this RunLoop.
+ template <class... Args>
+ auto bind(std::function<void (Args...)> fn) {
+ return [this, fn = std::move(fn)] (Args&&... args) {
+ invoke(std::move(fn), std::move(args)...);
+ };
}
- // Invoke fn() in the runloop thread, then invoke callback() in the current thread.
- template <class Fn>
- void invokeWithResult(Fn&& fn, std::function<void ()>&& callback) {
- RunLoop* outer = current.get();
- assert(outer);
+ // Invoke fn(args...) on this RunLoop, then invoke callback(result) on the current RunLoop.
+ template <class R, class Fn, class... Args>
+ void invokeWithResult(Fn&& fn, std::function<void (R)> callback, Args&&... args) {
+ invoke([fn = std::move(fn), callback = current.get()->bind(callback)] (Args&&... a) mutable {
+ callback(fn(std::forward<Args>(a)...));
+ }, std::forward<Args>(args)...);
+ }
- invoke([fn = std::move(fn), callback = std::move(callback), outer] () mutable {
- fn();
- outer->invoke(std::move(callback));
- });
+ // Invoke fn(args...) on this RunLoop, then invoke callback() on the current RunLoop.
+ template <class Fn, class... Args>
+ void invokeWithResult(Fn&& fn, std::function<void ()> callback, Args&&... args) {
+ invoke([fn = std::move(fn), callback = current.get()->bind(callback)] (Args&&... a) mutable {
+ fn(std::forward<Args>(a)...);
+ callback();
+ }, std::forward<Args>(args)...);
}
uv_loop_t* get() { return async.get()->loop; }
@@ -65,11 +66,24 @@ private:
virtual ~Message() = default;
};
- template <class F>
+ template <class F, class P, class... Args>
struct Invoker : Message {
- Invoker(F&& f) : func(std::move(f)) {}
- void operator()() override { func(); }
+ Invoker(F&& f, P&& p)
+ : func(std::move(f)),
+ params(std::move(p)) {
+ }
+
+ void operator()() override {
+ invoke(std::index_sequence_for<Args...>{});
+ }
+
+ template <std::size_t... I>
+ void invoke(std::index_sequence<I...>) {
+ func(std::forward<Args>(std::get<I>(params))...);
+ }
+
F func;
+ P params;
};
using Queue = std::queue<std::unique_ptr<Message>>;
diff --git a/src/mbgl/util/thread.hpp b/src/mbgl/util/thread.hpp
index a2ad958645..f3a9baa6f3 100644
--- a/src/mbgl/util/thread.hpp
+++ b/src/mbgl/util/thread.hpp
@@ -4,26 +4,12 @@
#include <future>
#include <thread>
#include <atomic>
+#include <utility>
#include <functional>
#include <mbgl/util/run_loop.hpp>
#include <mbgl/platform/platform.hpp>
-namespace {
-
-template <::std::size_t...>
-struct index_sequence {};
-
-template <::std::size_t N, ::std::size_t... I>
-struct integer_sequence : integer_sequence<N - 1, N - 1, I...> {};
-
-template <::std::size_t... I>
-struct integer_sequence<0, I...> {
- using type = index_sequence<I...>;
-};
-
-}
-
namespace mbgl {
namespace util {
@@ -50,19 +36,19 @@ public:
// Invoke object->fn(args...) in the runloop thread.
template <typename Fn, class... Args>
void invoke(Fn fn, Args&&... args) {
- loop->invoke(std::bind(fn, object, args...));
+ loop->invoke(bind<Fn, Args...>(fn), std::forward<Args>(args)...);
}
// Invoke object->fn(args...) in the runloop thread, then invoke callback(result) in the current thread.
- template <typename Fn, class R, class... Args>
- void invokeWithResult(Fn fn, std::function<void (R)>&& callback, Args&&... args) {
- loop->invokeWithResult(std::bind(fn, object, std::move(args)...), std::move(callback));
+ template <class R, typename Fn, class... Args>
+ void invokeWithResult(Fn fn, std::function<void (R)> callback, Args&&... args) {
+ loop->invokeWithResult(bind<Fn, Args...>(fn), callback, std::forward<Args>(args)...);
}
// Invoke object->fn(args...) in the runloop thread, then invoke callback() in the current thread.
template <typename Fn, class... Args>
- void invokeWithResult(Fn fn, std::function<void ()>&& callback, Args&&... args) {
- loop->invokeWithResult(std::bind(fn, object, std::move(args)...), std::move(callback));
+ void invokeWithResult(Fn fn, std::function<void ()> callback, Args&&... args) {
+ loop->invokeWithResult(bind<Fn, Args...>(fn), callback, std::forward<Args>(args)...);
}
// Invoke object->fn(args...) in the runloop thread, and wait for the result.
@@ -89,8 +75,13 @@ private:
Thread& operator=(const Thread&) = delete;
Thread& operator=(Thread&&) = delete;
+ template <typename Fn, class... Args>
+ auto bind(Fn fn) {
+ return [fn, this] (Args&&... a) { return (object->*fn)(std::forward<Args>(a)...); };
+ }
+
template <typename P, std::size_t... I>
- void run(P&& params, index_sequence<I...>);
+ void run(P&& params, std::index_sequence<I...>);
std::promise<void> running;
std::promise<void> joinable;
@@ -119,8 +110,7 @@ Thread<Object>::Thread(const std::string& name, ThreadPriority priority, Args&&.
platform::makeThreadLowPriority();
}
- constexpr auto seq = typename integer_sequence<sizeof...(Args)>::type();
- run(std::move(params), seq);
+ run(std::move(params), std::index_sequence_for<Args...>{});
});
running.get_future().get();
@@ -128,7 +118,7 @@ Thread<Object>::Thread(const std::string& name, ThreadPriority priority, Args&&.
template <class Object>
template <typename P, std::size_t... I>
-void Thread<Object>::run(P&& params, index_sequence<I...>) {
+void Thread<Object>::run(P&& params, std::index_sequence<I...>) {
uv::loop l;
{
diff --git a/src/mbgl/util/worker.cpp b/src/mbgl/util/worker.cpp
index 984c55c95e..3022d30277 100644
--- a/src/mbgl/util/worker.cpp
+++ b/src/mbgl/util/worker.cpp
@@ -19,7 +19,7 @@ public:
Worker::Worker(std::size_t count) {
for (std::size_t i = 0; i < count; i++) {
- threads.emplace_back(util::make_unique<util::Thread<Impl>>("Worker", util::ThreadPriority::Low));
+ threads.emplace_back(std::make_unique<util::Thread<Impl>>("Worker", util::ThreadPriority::Low));
}
}
@@ -27,7 +27,7 @@ Worker::~Worker() = default;
std::unique_ptr<WorkRequest> Worker::send(Fn work, Fn after) {
auto task = std::make_shared<WorkTask>(work, after);
- auto request = util::make_unique<WorkRequest>(task);
+ auto request = std::make_unique<WorkRequest>(task);
threads[current]->invokeWithResult(&Worker::Impl::doWork, [task] {
task->runAfter();
diff --git a/test/api/repeated_render.cpp b/test/api/repeated_render.cpp
index ce7e6cbb0b..7f55237744 100644
--- a/test/api/repeated_render.cpp
+++ b/test/api/repeated_render.cpp
@@ -20,7 +20,7 @@ TEST(API, RepeatedRender) {
HeadlessView view(display);
DefaultFileSource fileSource(nullptr);
- Log::setObserver(util::make_unique<FixtureLogObserver>());
+ Log::setObserver(std::make_unique<FixtureLogObserver>());
Map map(view, fileSource, MapMode::Still);
@@ -28,7 +28,7 @@ TEST(API, RepeatedRender) {
map.resize(128, 512, 1);
map.setStyleJSON(style, "test/suite");
std::promise<std::unique_ptr<const StillImage>> promise;
- map.renderStill([&promise](std::unique_ptr<const StillImage> image) {
+ map.renderStill([&promise](std::exception_ptr, std::unique_ptr<const StillImage> image) {
promise.set_value(std::move(image));
});
auto result = promise.get_future().get();
@@ -42,7 +42,7 @@ TEST(API, RepeatedRender) {
map.resize(512, 512, 2);
map.setStyleJSON(style, "TEST_DATA/suite");
std::promise<std::unique_ptr<const StillImage>> promise;
- map.renderStill([&promise](std::unique_ptr<const StillImage> image) {
+ map.renderStill([&promise](std::exception_ptr, std::unique_ptr<const StillImage> image) {
promise.set_value(std::move(image));
});
auto result = promise.get_future().get();
diff --git a/test/api/set_style.cpp b/test/api/set_style.cpp
index 01ce1f0252..72260e6343 100644
--- a/test/api/set_style.cpp
+++ b/test/api/set_style.cpp
@@ -14,7 +14,7 @@ TEST(API, SetStyle) {
HeadlessView view(display);
DefaultFileSource fileSource(nullptr);
- Log::setObserver(util::make_unique<FixtureLogObserver>());
+ Log::setObserver(std::make_unique<FixtureLogObserver>());
{
Map map(view, fileSource, MapMode::Still);
diff --git a/test/fixtures/resources/glyphs.pbf b/test/fixtures/resources/glyphs.pbf
new file mode 100644
index 0000000000..0d160f7898
--- /dev/null
+++ b/test/fixtures/resources/glyphs.pbf
Binary files differ
diff --git a/test/fixtures/resources/source.json b/test/fixtures/resources/source.json
new file mode 100644
index 0000000000..db516f9f95
--- /dev/null
+++ b/test/fixtures/resources/source.json
@@ -0,0 +1 @@
+{"attribution":"<a href=\"https://www.mapbox.com/about/maps/\" target=\"_blank\">&copy; Mapbox</a> <a href=\"http://www.openstreetmap.org/about/\" target=\"_blank\">&copy; OpenStreetMap</a> <a class=\"mapbox-improve-map\" href=\"https://www.mapbox.com/map-feedback/\" target=\"_blank\">Improve this map</a>","bounds":[-180,-85.0511,180,85.0511],"center":[0,0,0],"format":"pbf","maxzoom":15,"minzoom":0,"name":"Mapbox Streets V6 + Vector Terrain V2","scheme":"xyz","tilejson":"2.0.0","tiles":["test/fixtures/resources/vector.pbf"],"vector_layers":[{"description":"Generalized landcover classification","fields":{"class":"One of: wood, scrub, grass, crop, snow"},"id":"landcover","maxzoom":22,"minzoom":0},{"description":"","fields":{"class":"One of: shadow, highlight","level":"Brightness %. One of: 94, 90, 89, 78, 67, 56"},"id":"hillshade","maxzoom":22,"minzoom":0},{"description":"Elevation contour polygons","fields":{"ele":"Integer. The elevation of the contour in meters.","index":"Indicator for every 2nd, 5th, or 10th contour. Coastlines are given -1. One of: 2, 5, 10, -1, null"},"id":"contour","maxzoom":22,"minzoom":0},{"description":"","fields":{"class":"One of: cemetery, hospital, industrial, park, parking, pitch, sand, school, wood","osm_id":"Unique OSM ID number","type":"OSM tag, more specific than class"},"id":"landuse"},{"description":"","fields":{"class":"One of: river, canal, stream, stream_intermittent, ditch, drain","osm_id":"Unique OSM ID number","type":"One of: river, canal, stream, ditch, drain"},"id":"waterway"},{"description":"","fields":{"osm_id":"Unique OSM ID number"},"id":"water"},{"description":"","fields":{"osm_id":"Unique OSM ID number","type":"One of: runway, taxiway, apron"},"id":"aeroway"},{"description":"","fields":{"class":"One of: fence, hedge, cliff, gate","osm_id":"Unique OSM ID number"},"id":"barrier_line"},{"description":"","fields":{"osm_id":"Unique OSM ID number"},"id":"building"},{"description":"","fields":{"class":"One of: wetland, wetland_noveg","osm_id":"Unique OSM ID number"},"id":"landuse_overlay"},{"description":"","fields":{"class":"One of: motorway, motorway_link, main, street, street_limited, service, driveway, path, major_rail, minor_rail, aerialway","layer":"Number used for ordering overlapping tunnels","oneway":"Number. Oneway roads are 1, all others are 0.","osm_id":"Unique OSM ID number","type":"The value of the tunnel's highway tag"},"id":"tunnel"},{"description":"","fields":{"class":"One of: motorway, motorway_link, main, street, street_limited, service, driveway, path, major_rail, minor_rail, aerialway","oneway":"Number. Oneway roads are 1, all others are 0.","osm_id":"Unique OSM ID number","type":"The value of the road's highway tag"},"id":"road"},{"description":"","fields":{"class":"One of: motorway, motorway_link, main, street, street_limited, service, driveway, path, major_rail, minor_rail, aerialway","layer":"Number used for ordering overlapping bridges","oneway":"Number. Oneway bridges are 1, all others are 0.","osm_id":"Unique OSM ID number","type":"The value of the bridge's highway tag"},"id":"bridge"},{"description":"","fields":{"admin_level":"The OSM administrative level of the boundary","disputed":"Number. Disputed boundaries are 1, all others are 0.","maritime":"Number. Maritime boundaries are 1, all others are 0."},"id":"admin"},{"description":"","fields":{"code":"ISO 3166-1 Alpha-2 code","name":"Local name of the country","name_de":"German name of the country","name_en":"English name of the country","name_es":"Spanish name of the country","name_fr":"French name of the country","name_ru":"Russian name of the country","name_zh":"Chinese name of the country","osm_id":"Unique OSM ID number","parent":"ISO 3166-1 Alpha-2 code of the administering/parent state, if any","scalerank":"Number, 1-6. Useful for styling text sizes.","type":"One of: country, territory, disputed territory, sar"},"id":"country_label"},{"description":"","fields":{"labelrank":"Number, 1-6. Useful for styling text sizes.","name":"Local or international name of the water body","name_de":"German name of the water body","name_en":"English name of the water body","name_es":"Spanish name of the water body","name_fr":"French name of the water body","name_ru":"Russian name of the water body","name_zh":"Chinese name of the water body","placement":"One of: point, line"},"id":"marine_label"},{"description":"","fields":{"abbr":"Abbreviated state name","area":"The area of the state in kilometers²","name":"Local name of the state","name_de":"German name of the state","name_en":"English name of the state","name_es":"Spanish name of the state","name_fr":"French name of the state","name_ru":"Russian name of the state","name_zh":"Chinese name of the state","osm_id":"Unique OSM ID number"},"id":"state_label"},{"description":"","fields":{"capital":"Admin level the city is a capital of, if any. One of: 2, 3, 4, null","ldir":"A hint for label placement at low zoom levels. One of: N, E, S, W, NE, SE, SW, NW, null","localrank":"Number. Priority relative to nearby places. Useful for limiting label density.","name":"Local name of the place","name_de":"German name of the place","name_en":"English name of the place","name_es":"Spanish name of the place","name_fr":"French name of the place","name_ru":"Russian name of the place","name_zh":"Chinese name of the place","osm_id":"Unique OSM ID number","scalerank":"Number, 0-9 or null. Useful for styling text & marker sizes.","type":"One of: city, town, village, hamlet, suburb, neighbourhood"},"id":"place_label"},{"description":"","fields":{"area":"The area of the water polygon in Mercator meters²","name":"Local name of the water body","name_de":"German name of the water body","name_en":"English name of the water body","name_es":"Spanish name of the water body","name_fr":"French name of the water body","name_ru":"Russian name of the water body","name_zh":"Chinese name of the water body","osm_id":"Unique OSM ID number"},"id":"water_label"},{"description":"","fields":{"address":"Street address of the POI","localrank":"Number. Priority relative to nearby POIs. Useful for limiting label density.","maki":"The name of the Maki icon that should be used for the POI","name":"Local name of the POI","name_de":"German name of the POI","name_en":"English name of the POI","name_es":"Spanish name of the POI","name_fr":"French name of the POI","name_ru":"Russian name of the POI","name_zh":"Chinese name of the POI","network":"For rail stations, the network(s) that the station serves. Useful for icon styling.","osm_id":"Unique OSM ID number","ref":"Short reference code, if any","scalerank":"Number. 1-4. Useful for styling icon sizes and minimum zoom levels.","type":"The original OSM tag value","website":"URL of the POI"},"id":"poi_label"},{"description":"","fields":{"class":"One of: motorway, motorway_link, main, street, street_limited, service, driveway, path","len":"Number. Length of the road segment in Mercator meters.","localrank":"Number. Used for shield points only. Priority relative to nearby shields. Useful for limiting shield density.","name":"Local name of the road","name_de":"German name of the road","name_en":"English name of the road","name_es":"Spanish name of the road","name_fr":"French name of the road","name_ru":"Russian name of the road","name_zh":"Chinese name of the road","osm_id":"Unique OSM ID number","ref":"Route number of the road","reflen":"Number. How many characters long the ref tag is. Useful for shield styling.","shield":"The shield style to use. One of: default, mx-federal, mx-state, us-highway, us-highway-alternate, us-highway-business, us-highway-duplex, us-interstate, us-interstate-business, us-interstate-duplex, us-interstate-truck, us-state"},"id":"road_label"},{"description":"","fields":{"class":"One of: river, canal, stream, stream_intermittent","name":"Local name of the waterway","name_de":"German name of the waterway","name_en":"English name of the waterway","name_es":"Spanish name of the waterway","name_fr":"French name of the waterway","name_ru":"Russian name of the waterway","name_zh":"Chinese name of the waterway","osm_id":"Unique OSM ID number","type":"One of: river, canal, stream"},"id":"waterway_label"},{"description":"","fields":{"house_num":"House number","osm_id":"Unique OSM ID number"},"id":"housenum_label"}]}
diff --git a/test/fixtures/resources/sprite.json b/test/fixtures/resources/sprite.json
new file mode 100644
index 0000000000..dcc2b4808c
--- /dev/null
+++ b/test/fixtures/resources/sprite.json
@@ -0,0 +1 @@
+{"background":{"x":0,"y":20,"width":50,"height":50,"pixelRatio":1,"sdf":false},"grass_pattern":{"x":100,"y":80,"width":50,"height":50,"pixelRatio":1,"sdf":false},"interstate_1":{"x":0,"y":100,"width":41,"height":40,"pixelRatio":1,"sdf":false},"interstate_2":{"x":0,"y":100,"width":41,"height":40,"pixelRatio":1,"sdf":false},"interstate_3":{"x":41,"y":100,"width":48,"height":39,"pixelRatio":1,"sdf":false},"us_state_1":{"x":0,"y":73,"width":29,"height":24,"pixelRatio":1,"sdf":false},"us_state_2":{"x":0,"y":73,"width":29,"height":24,"pixelRatio":1,"sdf":false},"us_state_3":{"x":30,"y":73,"width":32,"height":24,"pixelRatio":1,"sdf":false},"us_highway_1":{"x":0,"y":142,"width":29,"height":29,"pixelRatio":1,"sdf":false},"us_highway_2":{"x":30,"y":142,"width":33,"height":29,"pixelRatio":1,"sdf":false},"us_highway_3":{"x":64,"y":142,"width":36,"height":29,"pixelRatio":1,"sdf":false},"default_1":{"x":0,"y":0,"width":17,"height":16,"pixelRatio":1,"sdf":false},"default_2":{"x":17,"y":0,"width":22,"height":16,"pixelRatio":1,"sdf":false},"default_3":{"x":39,"y":0,"width":27,"height":16,"pixelRatio":1,"sdf":false},"default_4":{"x":66,"y":0,"width":32,"height":16,"pixelRatio":1,"sdf":false},"default_5":{"x":98,"y":0,"width":37,"height":16,"pixelRatio":1,"sdf":false},"default_6":{"x":135,"y":0,"width":42,"height":16,"pixelRatio":1,"sdf":false},"london-overground":{"x":70,"y":25,"width":18,"height":18,"pixelRatio":1,"sdf":false},"london-underground":{"x":88,"y":25,"width":18,"height":18,"pixelRatio":1,"sdf":false},"national-rail":{"x":106,"y":25,"width":18,"height":18,"pixelRatio":1,"sdf":false},"dlr":{"x":106,"y":25,"width":18,"height":18,"pixelRatio":1,"sdf":false},"dlr.london-overground.london-underground.national-rail":{"x":70,"y":25,"width":72,"height":18,"pixelRatio":1,"sdf":false},"dlr.london-underground":{"x":88,"y":25,"width":36,"height":18,"pixelRatio":1,"sdf":false},"dlr.london-underground.national-rail":{"x":88,"y":25,"width":54,"height":18,"pixelRatio":1,"sdf":false},"dlr.national-rail":{"x":106,"y":25,"width":36,"height":18,"pixelRatio":1,"sdf":false},"london-overground.london-underground":{"x":70,"y":25,"width":36,"height":18,"pixelRatio":1,"sdf":false},"london-overground.london-underground.national-rail":{"x":124,"y":25,"width":54,"height":18,"pixelRatio":1,"sdf":false},"london-overground.national-rail":{"x":124,"y":25,"width":36,"height":18,"pixelRatio":1,"sdf":false},"london-underground.national-rail":{"x":124,"y":43,"width":36,"height":18,"pixelRatio":1,"sdf":false},"metro":{"x":71,"y":43,"width":18,"height":18,"pixelRatio":1,"sdf":false},"rer":{"x":87,"y":43,"width":18,"height":18,"pixelRatio":1,"sdf":false},"metro.rer":{"x":71,"y":43,"width":34,"height":18,"pixelRatio":1,"sdf":false},"rer.transilien":{"x":87,"y":43,"width":36,"height":18,"pixelRatio":1,"sdf":false},"u-bahn":{"x":70,"y":62,"width":18,"height":18,"pixelRatio":1,"sdf":false},"s-bahn":{"x":88,"y":62,"width":18,"height":18,"pixelRatio":1,"sdf":false},"s-bahn.u-bahn":{"x":70,"y":62,"width":36,"height":18,"pixelRatio":1,"sdf":false},"washington-metro":{"x":106,"y":62,"width":18,"height":18,"pixelRatio":1,"sdf":false},"wiener-linien":{"x":124,"y":62,"width":18,"height":18,"pixelRatio":1,"sdf":false},"moscow-metro":{"x":142,"y":61,"width":21,"height":18,"pixelRatio":1,"sdf":false},"generic-metro":{"x":160,"y":43,"width":18,"height":18,"pixelRatio":1,"sdf":false},"generic-rail":{"x":178,"y":43,"width":18,"height":18,"pixelRatio":1,"sdf":false},"dot":{"x":166,"y":63,"width":13,"height":13,"pixelRatio":1,"sdf":false},"default_marker":{"x":0,"y":175,"width":33,"height":86,"pixelRatio":1,"sdf":false},"secondary_marker":{"x":33,"y":175,"width":33,"height":86,"pixelRatio":1,"sdf":false},"oneway_motorway":{"x":178,"y":24,"width":21,"height":19,"pixelRatio":1,"sdf":false},"oneway_road":{"x":178,"y":62,"width":21,"height":19,"pixelRatio":1,"sdf":false},"hospital_icon":{"x":157,"y":259,"width":18,"height":18,"pixelRatio":1,"sdf":false},"fire-station_icon":{"x":157,"y":241,"width":18,"height":18,"pixelRatio":1,"sdf":false},"cemetery_icon":{"x":157,"y":79,"width":18,"height":18,"pixelRatio":1,"sdf":false},"zoo_icon":{"x":177,"y":79,"width":18,"height":18,"pixelRatio":1,"sdf":false},"park_icon":{"x":177,"y":97,"width":18,"height":18,"pixelRatio":1,"sdf":false},"golf_icon":{"x":177,"y":115,"width":18,"height":18,"pixelRatio":1,"sdf":false},"school_icon":{"x":177,"y":133,"width":18,"height":18,"pixelRatio":1,"sdf":false},"monument_icon":{"x":177,"y":151,"width":18,"height":18,"pixelRatio":1,"sdf":false},"library_icon":{"x":177,"y":169,"width":18,"height":18,"pixelRatio":1,"sdf":false},"museum_icon":{"x":177,"y":187,"width":18,"height":18,"pixelRatio":1,"sdf":false},"college_icon":{"x":177,"y":205,"width":18,"height":18,"pixelRatio":1,"sdf":false},"religious-christian_icon":{"x":157,"y":115,"width":18,"height":18,"pixelRatio":1,"sdf":false},"religious-jewish_icon":{"x":157,"y":133,"width":18,"height":18,"pixelRatio":1,"sdf":false},"religious-muslim_icon":{"x":157,"y":151,"width":18,"height":18,"pixelRatio":1,"sdf":false},"government_icon":{"x":157,"y":169,"width":18,"height":18,"pixelRatio":1,"sdf":false},"post_icon":{"x":157,"y":205,"width":18,"height":18,"pixelRatio":1,"sdf":false},"embassy_icon":{"x":157,"y":223,"width":18,"height":18,"pixelRatio":1,"sdf":false},"police_icon":{"x":157,"y":169,"width":18,"height":18,"pixelRatio":1,"sdf":false},"marker_icon":{"x":157,"y":169,"width":18,"height":18,"pixelRatio":1,"sdf":false},"prison_icon":{"x":157,"y":169,"width":18,"height":18,"pixelRatio":1,"sdf":false},"airfield_icon":{"x":157,"y":187,"width":18,"height":18,"pixelRatio":1,"sdf":false},"airport_icon":{"x":157,"y":187,"width":18,"height":18,"pixelRatio":1,"sdf":false},"harbor_icon":{"x":139,"y":169,"width":18,"height":18,"pixelRatio":1,"sdf":false},"generic_icon":{"x":139,"y":187,"width":18,"height":18,"pixelRatio":1,"sdf":false},"hospital_striped":{"x":117,"y":135,"width":3,"height":3,"pixelRatio":1,"sdf":false},"school_striped":{"x":114,"y":135,"width":3,"height":3,"pixelRatio":1,"sdf":false},"sand_noise":{"x":75,"y":174,"width":50,"height":50,"pixelRatio":1,"sdf":false}} \ No newline at end of file
diff --git a/test/fixtures/resources/sprite.png b/test/fixtures/resources/sprite.png
new file mode 100644
index 0000000000..967f2e76a6
--- /dev/null
+++ b/test/fixtures/resources/sprite.png
Binary files differ
diff --git a/test/fixtures/resources/style.json b/test/fixtures/resources/style.json
new file mode 100644
index 0000000000..698f3fb1e8
--- /dev/null
+++ b/test/fixtures/resources/style.json
@@ -0,0 +1,31 @@
+{
+ "version": 1,
+ "name": "Test",
+ "sources": {
+ "compositedsource": {
+ "url": "test/fixtures/resources/source.json",
+ "type": "vector"
+ }
+ },
+ "sprite": "test/fixtures/resources/sprite",
+ "glyphs": "test/fixtures/resources/glyphs.pbf",
+ "layers": [{
+ "id": "road",
+ "type": "symbol",
+ "source": "compositedsource",
+ "source-layer": "road_label",
+ "layout": {
+ "text-font": "Open Sans Regular, Arial Unicode MS Regular",
+ "text-field": "{name_en}"
+ }
+ }, {
+ "id": "poi",
+ "type": "symbol",
+ "source": "compositedsource",
+ "source-layer": "poi_label",
+ "layout": {
+ "text-font": "Open Sans Regular, Arial Unicode MS Regular",
+ "icon-image": "{maki}_icon"
+ }
+ }]
+}
diff --git a/test/fixtures/resources/vector.pbf b/test/fixtures/resources/vector.pbf
new file mode 100644
index 0000000000..cea355eeff
--- /dev/null
+++ b/test/fixtures/resources/vector.pbf
Binary files differ
diff --git a/test/headless/headless.cpp b/test/headless/headless.cpp
index a02062dbdd..c65e9855e7 100644
--- a/test/headless/headless.cpp
+++ b/test/headless/headless.cpp
@@ -4,7 +4,6 @@
#include <mbgl/map/map.hpp>
#include <mbgl/map/still_image.hpp>
#include <mbgl/util/image.hpp>
-#include <mbgl/util/std.hpp>
#include <mbgl/util/io.hpp>
#include <rapidjson/document.h>
@@ -152,7 +151,7 @@ TEST_P(HeadlessTest, render) {
map.setLatLngZoom(mbgl::LatLng(latitude, longitude), zoom);
map.setBearing(bearing);
- map.renderStill([&](std::unique_ptr<const StillImage> image) {
+ map.renderStill([&](std::exception_ptr, std::unique_ptr<const StillImage> image) {
const std::string png = util::compress_png(image->width, image->height, image->pixels.get());
util::write_file("test/suite/tests/" + base + "/" + name + "/actual.png", png);
promise.set_value();
diff --git a/test/ios/App-Info.plist b/test/ios/App-Info.plist
index d7e9d5c462..f5a44890e3 100644
--- a/test/ios/App-Info.plist
+++ b/test/ios/App-Info.plist
@@ -24,6 +24,12 @@
<string>1.0</string>
<key>LSRequiresIPhoneOS</key>
<true/>
+ <key>MGLMapboxAccessToken</key>
+ <string>pk.eyJ1IjoianVzdGluIiwiYSI6Ik9RX3RRQzAifQ.dmOg_BAp1ywuDZMM7YsXRg</string>
+ <key>MGLMapboxMetricsEnabledSettingShownInApp</key>
+ <true/>
+ <key>UILaunchStoryboardName</key>
+ <string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
diff --git a/test/ios/Images.xcassets/AppIcon.appiconset/Contents.json b/test/ios/Images.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000000..5008911f9e
--- /dev/null
+++ b/test/ios/Images.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,162 @@
+{
+ "images" : [
+ {
+ "idiom" : "iphone",
+ "size" : "29x29",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "29x29",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "29x29",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "40x40",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "40x40",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "57x57",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "57x57",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "60x60",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "60x60",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "29x29",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "29x29",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "40x40",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "40x40",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "50x50",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "50x50",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "72x72",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "72x72",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "76x76",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "76x76",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "car",
+ "size" : "120x120",
+ "scale" : "1x"
+ },
+ {
+ "size" : "24x24",
+ "idiom" : "watch",
+ "scale" : "2x",
+ "role" : "notificationCenter",
+ "subtype" : "38mm"
+ },
+ {
+ "size" : "27.5x27.5",
+ "idiom" : "watch",
+ "scale" : "2x",
+ "role" : "notificationCenter",
+ "subtype" : "42mm"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "watch",
+ "role" : "companionSettings",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "watch",
+ "role" : "companionSettings",
+ "scale" : "3x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "watch",
+ "scale" : "2x",
+ "role" : "appLauncher",
+ "subtype" : "38mm"
+ },
+ {
+ "size" : "44x44",
+ "idiom" : "watch",
+ "scale" : "2x",
+ "role" : "longLook",
+ "subtype" : "42mm"
+ },
+ {
+ "size" : "86x86",
+ "idiom" : "watch",
+ "scale" : "2x",
+ "role" : "quickLook",
+ "subtype" : "38mm"
+ },
+ {
+ "size" : "98x98",
+ "idiom" : "watch",
+ "scale" : "2x",
+ "role" : "quickLook",
+ "subtype" : "42mm"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+} \ No newline at end of file
diff --git a/test/ios/Images.xcassets/LaunchImage.launchimage/Contents.json b/test/ios/Images.xcassets/LaunchImage.launchimage/Contents.json
new file mode 100644
index 0000000000..628027f247
--- /dev/null
+++ b/test/ios/Images.xcassets/LaunchImage.launchimage/Contents.json
@@ -0,0 +1,53 @@
+{
+ "images" : [
+ {
+ "orientation" : "portrait",
+ "idiom" : "iphone",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "filename" : "Default@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "extent" : "full-screen",
+ "idiom" : "iphone",
+ "subtype" : "retina4",
+ "filename" : "Default-568h@2x.png",
+ "minimum-system-version" : "7.0",
+ "orientation" : "portrait",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "ipad",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "1x"
+ },
+ {
+ "orientation" : "landscape",
+ "idiom" : "ipad",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "1x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "ipad",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "landscape",
+ "idiom" : "ipad",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+} \ No newline at end of file
diff --git a/test/ios/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png b/test/ios/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png
new file mode 100644
index 0000000000..208eea205a
--- /dev/null
+++ b/test/ios/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png
Binary files differ
diff --git a/test/ios/Images.xcassets/LaunchImage.launchimage/Default@2x.png b/test/ios/Images.xcassets/LaunchImage.launchimage/Default@2x.png
new file mode 100644
index 0000000000..4438336215
--- /dev/null
+++ b/test/ios/Images.xcassets/LaunchImage.launchimage/Default@2x.png
Binary files differ
diff --git a/test/ios/LaunchScreen.xib b/test/ios/LaunchScreen.xib
new file mode 100644
index 0000000000..e0c780d8d4
--- /dev/null
+++ b/test/ios/LaunchScreen.xib
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7706" systemVersion="14E17e" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
+ <dependencies>
+ <deployment identifier="iOS"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7703"/>
+ <capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
+ </dependencies>
+ <objects>
+ <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+ <view contentMode="scaleToFill" id="iN0-l3-epB">
+ <rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <subviews>
+ <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="© 2015 Mapbox. All rights reserved." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="8ie-xW-0ye">
+ <rect key="frame" x="20" y="439" width="441" height="21"/>
+ <fontDescription key="fontDescription" type="system" pointSize="17"/>
+ <color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+ <nil key="highlightedColor"/>
+ <variation key="widthClass=compact">
+ <fontDescription key="fontDescription" type="system" pointSize="11"/>
+ </variation>
+ </label>
+ <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="ios-tests" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
+ <rect key="frame" x="20" y="140" width="441" height="43"/>
+ <fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
+ <color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+ <nil key="highlightedColor"/>
+ </label>
+ </subviews>
+ <color key="backgroundColor" red="0.23137254900000001" green="0.69803921570000005" blue="0.81568627449999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+ <constraints>
+ <constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="Kid-kn-2rF"/>
+ <constraint firstAttribute="centerX" secondItem="kId-c2-rCX" secondAttribute="centerX" id="Koa-jz-hwk"/>
+ <constraint firstAttribute="bottom" secondItem="8ie-xW-0ye" secondAttribute="bottom" constant="20" id="Kzo-t9-V3l"/>
+ <constraint firstItem="8ie-xW-0ye" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="MfP-vx-nX0"/>
+ <constraint firstAttribute="centerX" secondItem="8ie-xW-0ye" secondAttribute="centerX" id="ZEH-qu-HZ9"/>
+ <constraint firstItem="kId-c2-rCX" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="fvb-Df-36g"/>
+ </constraints>
+ <nil key="simulatedStatusBarMetrics"/>
+ <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+ <point key="canvasLocation" x="404" y="445"/>
+ </view>
+ </objects>
+</document>
diff --git a/test/ios/MGLTAppDelegate.m b/test/ios/MGLTAppDelegate.m
index bbd67821fe..3fa68b6da1 100644
--- a/test/ios/MGLTAppDelegate.m
+++ b/test/ios/MGLTAppDelegate.m
@@ -13,8 +13,6 @@
wrapper.toolbarHidden = YES;
[self.window makeKeyAndVisible];
- [MGLAccountManager setMapboxMetricsEnabledSettingShownInApp:YES]; // a lie, but a convenient one
-
return YES;
}
diff --git a/test/ios/MGLTViewController.m b/test/ios/MGLTViewController.m
index bdce2202d7..e3357616f8 100644
--- a/test/ios/MGLTViewController.m
+++ b/test/ios/MGLTViewController.m
@@ -10,8 +10,7 @@
{
[super viewDidLoad];
- _mapView = [[MGLMapView alloc] initWithFrame:self.view.bounds
- accessToken:@"pk.eyJ1IjoianVzdGluIiwiYSI6Ik9RX3RRQzAifQ.dmOg_BAp1ywuDZMM7YsXRg"];
+ _mapView = [[MGLMapView alloc] initWithFrame:self.view.bounds];
_mapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view addSubview:_mapView];
diff --git a/test/ios/MetricsTests.m b/test/ios/MetricsTests.m
index 5b1a7956ea..758cf1f5be 100644
--- a/test/ios/MetricsTests.m
+++ b/test/ios/MetricsTests.m
@@ -60,12 +60,6 @@
XCTAssertEqualObjects([[MGLMapboxEvents sharedManager] appBundleId], @"com.mapbox.Mapbox-GL-Tests");
}
-- (void)testTokenSetViaMapView {
- [tester.mapView setAccessToken:@"test_token"];
-
- XCTAssertEqualObjects([[MGLMapboxEvents sharedManager] token], @"test_token");
-}
-
- (void)testResumeMetricsCollection {
[MGLMapboxEvents resumeMetricsCollection];
diff --git a/test/ios/ios-tests.xcodeproj/project.pbxproj b/test/ios/ios-tests.xcodeproj/project.pbxproj
index 4f31a5b4a4..cbe294a3eb 100644
--- a/test/ios/ios-tests.xcodeproj/project.pbxproj
+++ b/test/ios/ios-tests.xcodeproj/project.pbxproj
@@ -7,6 +7,8 @@
objects = {
/* Begin PBXBuildFile section */
+ 96567A231B0E84CD00D78776 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 96567A221B0E84CD00D78776 /* LaunchScreen.xib */; };
+ 96567A311B0E8BB900D78776 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 96567A301B0E8BB900D78776 /* Images.xcassets */; };
DD043363196DBBD500E6F39D /* MGLTAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DD04335F196DBBD500E6F39D /* MGLTAppDelegate.m */; };
DD043364196DBBD500E6F39D /* MGLTViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DD043360196DBBD500E6F39D /* MGLTViewController.m */; };
DD043366196DBBE000E6F39D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DD043365196DBBE000E6F39D /* main.m */; };
@@ -81,6 +83,8 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
+ 96567A221B0E84CD00D78776 /* LaunchScreen.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LaunchScreen.xib; sourceTree = SOURCE_ROOT; };
+ 96567A301B0E8BB900D78776 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = SOURCE_ROOT; };
DACAD7111B08719F009119DC /* MGLMapboxEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MGLMapboxEvents.h; path = ../../platform/ios/MGLMapboxEvents.h; sourceTree = SOURCE_ROOT; };
DD043323196DB9BC00E6F39D /* Mapbox GL Tests.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Mapbox GL Tests.app"; sourceTree = BUILT_PRODUCTS_DIR; };
DD04335F196DBBD500E6F39D /* MGLTAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLTAppDelegate.m; sourceTree = SOURCE_ROOT; };
@@ -217,8 +221,10 @@
DD04332D196DB9BC00E6F39D /* Supporting Files */ = {
isa = PBXGroup;
children = (
+ 96567A301B0E8BB900D78776 /* Images.xcassets */,
DD043367196DBCC200E6F39D /* App-Info.plist */,
DD043365196DBBE000E6F39D /* main.m */,
+ 96567A221B0E84CD00D78776 /* LaunchScreen.xib */,
);
name = "Supporting Files";
sourceTree = "<group>";
@@ -443,8 +449,10 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ 96567A311B0E8BB900D78776 /* Images.xcassets in Resources */,
DD0581041ACB661200B112C9 /* Headers in Resources */,
DD0581061ACB661C00B112C9 /* MapboxGL.bundle in Resources */,
+ 96567A231B0E84CD00D78776 /* LaunchScreen.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
diff --git a/test/miscellaneous/bilinear.cpp b/test/miscellaneous/bilinear.cpp
index 9808e20fef..bdfb9d9117 100644
--- a/test/miscellaneous/bilinear.cpp
+++ b/test/miscellaneous/bilinear.cpp
@@ -3,7 +3,6 @@
#include <mbgl/util/scaling.hpp>
#include <mbgl/util/image.hpp>
#include <mbgl/util/io.hpp>
-#include <mbgl/util/std.hpp>
#include <algorithm>
#include <cstring>
@@ -24,7 +23,7 @@ TEST(Bilinear, Scaling) {
const uint32_t *srcData = reinterpret_cast<const uint32_t *>(src + 8);
const vec2<uint32_t> srcSize { width, height };
const vec2<uint32_t> dstSize { 128, 128 };
- auto dst = util::make_unique<uint32_t[]>(dstSize.x * dstSize.y);
+ auto dst = std::make_unique<uint32_t[]>(dstSize.x * dstSize.y);
uint32_t *dstData = dst.get();
std::fill(dstData, dstData + dstSize.x * dstSize.y, 0xFFFF00FF);
diff --git a/test/miscellaneous/clip_ids.cpp b/test/miscellaneous/clip_ids.cpp
index e168ab5e01..4ecd1797e6 100644
--- a/test/miscellaneous/clip_ids.cpp
+++ b/test/miscellaneous/clip_ids.cpp
@@ -5,7 +5,6 @@
#include <mbgl/util/clip_id.hpp>
#include <mbgl/map/tile.hpp>
-#include <mbgl/util/std.hpp>
using namespace mbgl;
diff --git a/test/miscellaneous/thread.cpp b/test/miscellaneous/thread.cpp
new file mode 100644
index 0000000000..b0dd2210e9
--- /dev/null
+++ b/test/miscellaneous/thread.cpp
@@ -0,0 +1,102 @@
+#include <mbgl/util/thread.hpp>
+#include <mbgl/util/run_loop.hpp>
+
+#include "../fixtures/util.hpp"
+
+using namespace mbgl::util;
+
+class TestObject {
+public:
+ TestObject(uv_loop_t*, std::thread::id otherTid)
+ : tid(std::this_thread::get_id()) {
+ EXPECT_NE(tid, otherTid);
+ }
+
+ void fn1(int val) {
+ EXPECT_EQ(tid, std::this_thread::get_id());
+ EXPECT_EQ(val, 1);
+ }
+
+ int fn2() {
+ EXPECT_EQ(tid, std::this_thread::get_id());
+ return 1;
+ }
+
+ void transferIn(std::unique_ptr<int> val) {
+ EXPECT_EQ(tid, std::this_thread::get_id());
+ EXPECT_EQ(*val, 1);
+ }
+
+ std::unique_ptr<int> transferOut() {
+ EXPECT_EQ(tid, std::this_thread::get_id());
+ return std::make_unique<int>(1);
+ }
+
+ std::unique_ptr<int> transferInOut(std::unique_ptr<int> val) {
+ EXPECT_EQ(tid, std::this_thread::get_id());
+ EXPECT_EQ(*val, 1);
+ return std::move(val);
+ }
+
+ void transferInShared(std::shared_ptr<int> val) {
+ EXPECT_EQ(tid, std::this_thread::get_id());
+ EXPECT_EQ(*val, 1);
+ }
+
+ std::shared_ptr<int> transferOutShared() {
+ EXPECT_EQ(tid, std::this_thread::get_id());
+ return std::make_shared<int>(1);
+ }
+
+ std::string transferString(const std::string& string) {
+ EXPECT_EQ(tid, std::this_thread::get_id());
+ EXPECT_EQ(string, "test");
+ return string;
+ }
+
+ const std::thread::id tid;
+};
+
+TEST(Thread, invoke) {
+ const std::thread::id tid = std::this_thread::get_id();
+
+ RunLoop loop(uv_default_loop());
+
+ loop.invoke([&] {
+ EXPECT_EQ(tid, std::this_thread::get_id());
+ Thread<TestObject> thread("Test", ThreadPriority::Regular, tid);
+
+ thread.invoke(&TestObject::fn1, 1);
+ thread.invokeWithResult<int>(&TestObject::fn2, [&] (int result) {
+ EXPECT_EQ(tid, std::this_thread::get_id());
+ EXPECT_EQ(result, 1);
+ });
+
+ thread.invoke(&TestObject::transferIn, std::make_unique<int>(1));
+ thread.invokeWithResult<std::unique_ptr<int>>(&TestObject::transferOut, [&] (std::unique_ptr<int> result) {
+ EXPECT_EQ(tid, std::this_thread::get_id());
+ EXPECT_EQ(*result, 1);
+ });
+
+ thread.invokeWithResult<std::unique_ptr<int>>(&TestObject::transferInOut, [&] (std::unique_ptr<int> result) {
+ EXPECT_EQ(tid, std::this_thread::get_id());
+ EXPECT_EQ(*result, 1);
+ }, std::make_unique<int>(1));
+
+ thread.invoke(&TestObject::transferInShared, std::make_shared<int>(1));
+ thread.invokeWithResult<std::shared_ptr<int>>(&TestObject::transferOutShared, [&] (std::shared_ptr<int> result) {
+ EXPECT_EQ(tid, std::this_thread::get_id());
+ EXPECT_EQ(*result, 1);
+ });
+
+ std::string test("test");
+ thread.invokeWithResult<std::string>(&TestObject::transferString, [&] (std::string result){
+ EXPECT_EQ(tid, std::this_thread::get_id());
+ EXPECT_EQ(result, "test");
+ loop.stop();
+ }, test);
+ test.clear();
+ });
+
+ uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+}
diff --git a/test/resources/mock_file_source.cpp b/test/resources/mock_file_source.cpp
new file mode 100644
index 0000000000..42067a2a73
--- /dev/null
+++ b/test/resources/mock_file_source.cpp
@@ -0,0 +1,82 @@
+#include "../fixtures/util.hpp"
+#include "mock_file_source.hpp"
+
+#include <mbgl/storage/request.hpp>
+#include <mbgl/util/io.hpp>
+#include <mbgl/util/thread.hpp>
+
+namespace mbgl {
+
+class MockFileSource::Impl {
+public:
+ Impl(uv_loop_t*, Type type, const std::string& match) : type_(type), match_(match) {}
+
+ void handleRequest(Request* req) const;
+
+private:
+ void replyWithFailure(Response* res) const;
+ void replyWithCorruptedData(Response* res, const std::string& url) const;
+ void replyWithSuccess(Response* res, const std::string& url) const;
+
+ Type type_;
+ std::string match_;
+};
+
+void MockFileSource::Impl::replyWithFailure(Response* res) const {
+ res->status = Response::Status::Error;
+ res->message = "Failed by the test case";
+}
+
+void MockFileSource::Impl::replyWithCorruptedData(Response* res, const std::string& url) const {
+ res->status = Response::Status::Successful;
+ res->data = util::read_file(url);
+ res->data.insert(0, "CORRUPTED");
+}
+
+void MockFileSource::Impl::replyWithSuccess(Response* res, const std::string& url) const {
+ res->status = Response::Status::Successful;
+ res->data = util::read_file(url);
+}
+
+void MockFileSource::Impl::handleRequest(Request* req) const {
+ const std::string& url = req->resource.url;
+ std::shared_ptr<Response> response = std::make_shared<Response>();
+
+ if (url.find(match_) == std::string::npos) {
+ replyWithSuccess(response.get(), url);
+ req->notify(response);
+ return;
+ }
+
+ switch (type_) {
+ case Type::Success:
+ replyWithSuccess(response.get(), url);
+ break;
+ case Type::RequestFail:
+ replyWithFailure(response.get());
+ break;
+ case Type::RequestWithCorruptedData:
+ replyWithCorruptedData(response.get(), url);
+ break;
+ default:
+ EXPECT_TRUE(false) << "Should never be reached.";
+ }
+
+ req->notify(response);
+}
+
+MockFileSource::MockFileSource(Type type, const std::string& match)
+ : thread_(std::make_unique<util::Thread<Impl>>("FileSource", util::ThreadPriority::Low, type, match)) {
+}
+
+Request* MockFileSource::request(const Resource& resource, uv_loop_t* loop, Callback callback) {
+ Request* req = new Request(resource, loop, std::move(callback));
+ thread_->invoke(&Impl::handleRequest, req);
+
+ return req;
+}
+
+void MockFileSource::cancel(Request*) {
+}
+
+}
diff --git a/test/resources/mock_file_source.hpp b/test/resources/mock_file_source.hpp
new file mode 100644
index 0000000000..bb9fb55a30
--- /dev/null
+++ b/test/resources/mock_file_source.hpp
@@ -0,0 +1,40 @@
+#ifndef TEST_RESOURCES_MOCK_FILE_SOURCE
+#define TEST_RESOURCES_MOCK_FILE_SOURCE
+
+#include <mbgl/storage/file_source.hpp>
+
+#include <string>
+#include <memory>
+
+namespace mbgl {
+
+namespace util {
+template <typename T> class Thread;
+}
+
+// This mock FileSource will read data from the disk and will fail
+// the request if the URL matches a string.
+class MockFileSource : public FileSource {
+public:
+ enum Type {
+ Success,
+ RequestFail,
+ RequestWithCorruptedData
+ };
+
+ class Impl;
+
+ MockFileSource(Type type, const std::string& match);
+ ~MockFileSource() override = default;
+
+ // FileSource implementation.
+ Request* request(const Resource&, uv_loop_t*, Callback) override;
+ void cancel(Request*) override;
+
+private:
+ const std::unique_ptr<util::Thread<Impl>> thread_;
+};
+
+}
+
+#endif
diff --git a/test/resources/mock_view.hpp b/test/resources/mock_view.hpp
new file mode 100644
index 0000000000..865cd2da55
--- /dev/null
+++ b/test/resources/mock_view.hpp
@@ -0,0 +1,21 @@
+#ifndef TEST_RESOURCES_MOCK_VIEW
+#define TEST_RESOURCES_MOCK_VIEW
+
+#include <mbgl/map/view.hpp>
+
+namespace mbgl {
+
+class MockView : public View {
+public:
+ MockView() = default;
+
+ // View implementation.
+ void activate() override {};
+ void deactivate() override {};
+ void notify() override {};
+ void invalidate(std::function<void()>) override {};
+};
+
+}
+
+#endif
diff --git a/test/resources/resource_loader.cpp b/test/resources/resource_loader.cpp
new file mode 100644
index 0000000000..7d57f47ee6
--- /dev/null
+++ b/test/resources/resource_loader.cpp
@@ -0,0 +1,204 @@
+#include "../fixtures/fixture_log_observer.hpp"
+#include "../fixtures/util.hpp"
+#include "mock_file_source.hpp"
+#include "mock_view.hpp"
+
+#include <mbgl/geometry/glyph_atlas.hpp>
+#include <mbgl/geometry/sprite_atlas.hpp>
+#include <mbgl/map/environment.hpp>
+#include <mbgl/map/map_data.hpp>
+#include <mbgl/map/resource_loader.hpp>
+#include <mbgl/map/transform_state.hpp>
+#include <mbgl/style/style.hpp>
+#include <mbgl/text/glyph_store.hpp>
+#include <mbgl/util/exception.hpp>
+#include <mbgl/util/io.hpp>
+#include <mbgl/util/run_loop.hpp>
+#include <mbgl/util/texture_pool.hpp>
+#include <mbgl/util/thread.hpp>
+
+using namespace mbgl;
+
+namespace {
+
+class MockMapContext : public ResourceLoader::Observer {
+public:
+ MockMapContext(uv_loop_t* loop,
+ View& view,
+ FileSource& fileSource,
+ const std::function<void(std::exception_ptr error)>& callback)
+ : env_(fileSource),
+ envScope_(env_, ThreadType::Map, "Map"),
+ data_(view, MapMode::Still),
+ glyphStore_(std::make_unique<GlyphStore>(loop, env_)),
+ glyphAtlas_(std::make_unique<GlyphAtlas>(1024, 1024)),
+ spriteAtlas_(std::make_unique<SpriteAtlas>(512, 512)),
+ texturePool_(std::make_unique<TexturePool>()),
+ style_(std::make_unique<Style>()),
+ resourceLoader_(std::make_unique<ResourceLoader>()),
+ asyncUpdate(std::make_unique<uv::async>(loop, [this] { update(); })),
+ callback_(callback) {
+ asyncUpdate->unref();
+
+ data_.transform.resize(1000, 1000, 1.0, 1000, 1000);
+ data_.transform.setLatLngZoom({0, 0}, 16);
+
+ const std::string style = util::read_file("test/fixtures/resources/style.json");
+ style_->loadJSON(reinterpret_cast<const uint8_t *>(style.c_str()));
+
+ glyphStore_->setURL(style_->glyph_url);
+
+ resourceLoader_->setGlyphStore(glyphStore_.get());
+ resourceLoader_->setObserver(this);
+ resourceLoader_->setStyle(style_.get());
+ }
+
+ ~MockMapContext() {
+ resourceLoader_.reset();
+ style_.reset();
+ texturePool_.reset();
+ spriteAtlas_.reset();
+ glyphAtlas_.reset();
+ glyphStore_.reset();
+ }
+
+ void update() {
+ const auto now = Clock::now();
+
+ data_.setAnimationTime(now);
+ data_.transform.updateTransitions(now);
+
+ transformState_ = data_.transform.currentState();
+
+ resourceLoader_->update(
+ data_, transformState_, *glyphAtlas_, *spriteAtlas_, *texturePool_);
+ }
+
+ // ResourceLoader::Observer implementation.
+ void onTileDataChanged() override {
+ util::ptr<Sprite> sprite = resourceLoader_->getSprite();
+ if (sprite && sprite->isLoaded() && style_->isLoaded()) {
+ callback_(nullptr);
+ }
+
+ asyncUpdate->send();
+ };
+
+ void onResourceLoadingFailed(std::exception_ptr error) override {
+ callback_(error);
+ }
+
+private:
+ Environment env_;
+ EnvironmentScope envScope_;
+
+ MapData data_;
+ TransformState transformState_;
+
+ std::unique_ptr<GlyphStore> glyphStore_;
+ std::unique_ptr<GlyphAtlas> glyphAtlas_;
+ std::unique_ptr<SpriteAtlas> spriteAtlas_;
+ std::unique_ptr<TexturePool> texturePool_;
+ std::unique_ptr<Style> style_;
+ std::unique_ptr<ResourceLoader> resourceLoader_;
+
+ std::unique_ptr<uv::async> asyncUpdate;
+
+ std::function<void(std::exception_ptr error)> callback_;
+};
+
+void runTestCase(MockFileSource::Type type,
+ const std::string& param,
+ const std::string& message) {
+ util::RunLoop loop(uv_default_loop());
+
+ MockView view;
+ MockFileSource fileSource(type, param);
+
+ FixtureLogObserver* log = new FixtureLogObserver();
+ Log::setObserver(std::unique_ptr<Log::Observer>(log));
+
+ auto callback = [&loop, &param](std::exception_ptr error) {
+ try {
+ if (error) {
+ std::rethrow_exception(error);
+ }
+ } catch (const util::GlyphRangeLoadingException&) {
+ EXPECT_EQ(param, "glyphs.pbf");
+ } catch (const util::SourceLoadingException&) {
+ EXPECT_EQ(param, "source.json");
+ } catch (const util::SpriteLoadingException&) {
+ EXPECT_TRUE(param == "sprite.png" || param == "sprite.json");
+ } catch (const util::TileLoadingException&) {
+ EXPECT_EQ(param, "vector.pbf");
+ } catch (const std::exception&) {
+ EXPECT_TRUE(false) << "Unhandled exception.";
+ }
+
+ loop.stop();
+ };
+
+ std::unique_ptr<util::Thread<MockMapContext>> context(
+ std::make_unique<util::Thread<MockMapContext>>(
+ "Map", util::ThreadPriority::Regular, view, fileSource, callback));
+
+ uv_run(loop.get(), UV_RUN_DEFAULT);
+
+ // Needed because it will make the Map thread
+ // join and cease logging after this point.
+ context.reset();
+
+ const FixtureLogObserver::LogMessage logMessage {
+ EventSeverity::Error,
+ Event::ResourceLoader,
+ int64_t(-1),
+ message,
+ };
+
+ if (type == MockFileSource::Success) {
+ EXPECT_EQ(log->count(logMessage), 0u);
+ } else {
+ EXPECT_GT(log->count(logMessage), 0u);
+ }
+
+ // Clear the remaining error messages
+ log->unchecked().size();
+}
+
+}
+
+class ResourceLoaderTest : public ::testing::TestWithParam<std::string> {
+};
+
+TEST_P(ResourceLoaderTest, Success) {
+ runTestCase(MockFileSource::Success, GetParam(), std::string());
+}
+
+TEST_P(ResourceLoaderTest, RequestFail) {
+ std::stringstream message;
+ message << "Failed to load [test/fixtures/resources/" << GetParam() << "]: Failed by the test case";
+
+ runTestCase(MockFileSource::RequestFail, GetParam(), message.str());
+}
+
+TEST_P(ResourceLoaderTest, RequestWithCorruptedData) {
+ const std::string param(GetParam());
+
+ std::stringstream message;
+ message << "Failed to parse ";
+
+ if (param == "vector.pbf") {
+ message << "[15/16384/16384]: pbf unknown field type exception";
+ } else {
+ message << "[test/fixtures/resources/" << param << "]";
+ }
+
+ if (param.find("json") != std::string::npos) {
+ message << ": 0 - Expect either an object or array at root";
+ }
+
+ runTestCase(MockFileSource::RequestWithCorruptedData, GetParam(), message.str());
+}
+
+INSTANTIATE_TEST_CASE_P(ResourceLoader, ResourceLoaderTest,
+ ::testing::Values("source.json", "sprite.json", "sprite.png", "vector.pbf", "glyphs.pbf"));
diff --git a/test/storage/database.cpp b/test/storage/database.cpp
index a44c7ff4b4..b54b69a133 100644
--- a/test/storage/database.cpp
+++ b/test/storage/database.cpp
@@ -11,7 +11,7 @@
TEST_F(Storage, DatabaseDoesNotExist) {
using namespace mbgl;
- Log::setObserver(util::make_unique<FixtureLogObserver>());
+ Log::setObserver(std::make_unique<FixtureLogObserver>());
SQLiteCache::Impl cache(nullptr, "test/fixtures/404/cache.db");
@@ -52,7 +52,7 @@ TEST_F(Storage, DatabaseCreate) {
createDir("test/fixtures/database");
deleteFile("test/fixtures/database/cache.db");
- Log::setObserver(util::make_unique<FixtureLogObserver>());
+ Log::setObserver(std::make_unique<FixtureLogObserver>());
SQLiteCache::Impl cache(nullptr, "test/fixtures/database/cache.db");
@@ -113,7 +113,7 @@ TEST_F(Storage, DatabaseLockedRead) {
{
// First request should fail.
- Log::setObserver(util::make_unique<FixtureLogObserver>());
+ Log::setObserver(std::make_unique<FixtureLogObserver>());
std::unique_ptr<Response> res = cache.get({ Resource::Unknown, "mapbox://test" });
EXPECT_EQ(nullptr, res.get());
@@ -129,7 +129,7 @@ TEST_F(Storage, DatabaseLockedRead) {
{
// First, try getting a file (the cache value should not exist).
- Log::setObserver(util::make_unique<FixtureLogObserver>());
+ Log::setObserver(std::make_unique<FixtureLogObserver>());
std::unique_ptr<Response> res = cache.get({ Resource::Unknown, "mapbox://test" });
EXPECT_EQ(nullptr, res.get());
@@ -153,7 +153,7 @@ TEST_F(Storage, DatabaseLockedWrite) {
{
// Adds a file (which should fail).
- Log::setObserver(util::make_unique<FixtureLogObserver>());
+ Log::setObserver(std::make_unique<FixtureLogObserver>());
auto response = std::make_shared<Response>();
cache.put({ Resource::Unknown, "mapbox://test" }, response);
@@ -170,7 +170,7 @@ TEST_F(Storage, DatabaseLockedWrite) {
{
// Then, set a file and obtain it again.
- Log::setObserver(util::make_unique<FixtureLogObserver>());
+ Log::setObserver(std::make_unique<FixtureLogObserver>());
auto response = std::make_shared<Response>();
response->data = "Demo";
@@ -201,7 +201,7 @@ TEST_F(Storage, DatabaseLockedRefresh) {
{
// Adds a file.
- Log::setObserver(util::make_unique<FixtureLogObserver>());
+ Log::setObserver(std::make_unique<FixtureLogObserver>());
auto response = std::make_shared<Response>();
response->data = "Demo";
@@ -216,7 +216,7 @@ TEST_F(Storage, DatabaseLockedRefresh) {
{
// Then, try to refresh it.
- Log::setObserver(util::make_unique<FixtureLogObserver>());
+ Log::setObserver(std::make_unique<FixtureLogObserver>());
auto response = std::make_shared<Response>();
response->data = "Demo";
@@ -244,7 +244,7 @@ TEST_F(Storage, DatabaseDeleted) {
{
// Adds a file.
- Log::setObserver(util::make_unique<FixtureLogObserver>());
+ Log::setObserver(std::make_unique<FixtureLogObserver>());
auto response = std::make_shared<Response>();
response->data = "Demo";
@@ -260,7 +260,7 @@ TEST_F(Storage, DatabaseDeleted) {
{
// Adds a file.
- Log::setObserver(util::make_unique<FixtureLogObserver>());
+ Log::setObserver(std::make_unique<FixtureLogObserver>());
auto response = std::make_shared<Response>();
response->data = "Demo";
@@ -289,7 +289,7 @@ TEST_F(Storage, DatabaseInvalid) {
{
// Adds a file.
- Log::setObserver(util::make_unique<FixtureLogObserver>());
+ Log::setObserver(std::make_unique<FixtureLogObserver>());
auto response = std::make_shared<Response>();
response->data = "Demo";
diff --git a/test/test.gypi b/test/test.gypi
index bc29016530..24582df992 100644
--- a/test/test.gypi
+++ b/test/test.gypi
@@ -52,11 +52,17 @@
'miscellaneous/merge_lines.cpp',
'miscellaneous/style_parser.cpp',
'miscellaneous/text_conversions.cpp',
- 'miscellaneous/transform.cpp',
+ 'miscellaneous/thread.cpp',
'miscellaneous/tile.cpp',
+ 'miscellaneous/transform.cpp',
'miscellaneous/variant.cpp',
'miscellaneous/worker.cpp',
+ 'resources/mock_file_source.cpp',
+ 'resources/mock_file_source.hpp',
+ 'resources/mock_view.hpp',
+ 'resources/resource_loader.cpp',
+
'storage/storage.hpp',
'storage/storage.cpp',
'storage/cache_response.cpp',