diff --git a/.mason b/.mason index c43967404..ace19da71 160000 --- a/.mason +++ b/.mason @@ -1 +1 @@ -Subproject commit c43967404c84d020d63f6aba450d97770dfeaebb +Subproject commit ace19da711199cb0283c6243b435575fbec16a90 diff --git a/.travis.yml b/.travis.yml index f08aa8149..1ac66b386 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 6b15f596c..123c507c9 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 with something like /Library/Java/JavaVirtualMachines/jdk1.8.0_31.jdk echo "export JAVA_HOME=" >> .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 2c7e75778..64e72e149 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(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(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 03530aeff..1d25442a5 100644 --- a/android/cpp/native_map_view.cpp +++ b/android/cpp/native_map_view.cpp @@ -15,7 +15,6 @@ #include #include #include -#include namespace mbgl { namespace android { @@ -214,7 +213,7 @@ void NativeMapView::initializeDisplay() { throw new std::runtime_error("eglChooseConfig() failed"); } - const std::unique_ptr configs = mbgl::util::make_unique(numConfigs); + const auto configs = std::make_unique(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 f0659b8c7..39e46fcc0 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 da4b40c4c..e482c0be5 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 5f2db91d7..46c95c21e 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 33e216f52..5ae83c1d4 100644 --- a/bin/render.cpp +++ b/bin/render.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #include #include @@ -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 image) { + map.renderStill([async](std::exception_ptr error, std::unique_ptr 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(image.release()); uv_async_send(async); }); diff --git a/gyp/platform-ios.gypi b/gyp/platform-ios.gypi index 0034831a9..0ec67786a 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 7ec4135f1..6b127543e 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 df9a19a6b..23762cca9 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 9d7b5c901..0ccb8225b 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://`). - * @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://`). +* @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 ccc963d08..5535dbcc9 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)>; + using StillImageCallback = std::function)>; 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 1cf7d2ece..0101b91d4 100644 --- a/include/mbgl/platform/darwin/reachability.h +++ b/include/mbgl/platform/darwin/reachability.h @@ -28,24 +28,6 @@ #import #import -#import -#import -#import -#import -#import -#import - -/** - * 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)(Reachability * reachability); -typedef void (^NetworkUnreachable)(Reachability * reachability); +typedef void (^NetworkReachable)(MGLReachability * reachability); +typedef void (^NetworkUnreachable)(MGLReachability * 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 985e4874c..52a67a283 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 #include // stl #include #include +#include namespace mbgl { namespace util { diff --git a/include/mbgl/platform/event.hpp b/include/mbgl/platform/event.hpp index 5fd64119c..8cdd1d50a 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 d6f3cd1ab..86a8cdbef 100644 --- a/include/mbgl/platform/log.hpp +++ b/include/mbgl/platform/log.hpp @@ -3,7 +3,6 @@ #include -#include #include #include diff --git a/include/mbgl/storage/default_file_source.hpp b/include/mbgl/storage/default_file_source.hpp index 405b04a49..8cfae03a9 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> thread; + std::string accessToken; }; } diff --git a/include/mbgl/storage/file_source.hpp b/include/mbgl/storage/file_source.hpp index 7c6e578a9..3b19e0078 100644 --- a/include/mbgl/storage/file_source.hpp +++ b/include/mbgl/storage/file_source.hpp @@ -5,7 +5,6 @@ #include "resource.hpp" #include -#include #include #include diff --git a/include/mbgl/storage/resource.hpp b/include/mbgl/storage/resource.hpp index cfd52caa7..2fcb3b1b7 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 0b4403270..da61aa482 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 000000000..bb3395996 --- /dev/null +++ b/include/mbgl/util/exclusive.hpp @@ -0,0 +1,30 @@ +#ifndef MBGL_UTIL_EXCLUSIVE +#define MBGL_UTIL_EXCLUSIVE + +#include +#include + + +namespace mbgl { +namespace util { + +template +class exclusive { +public: + inline exclusive(T* val, std::unique_ptr> 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> lock; +}; + + +} // end namespace util +} // end namespace mbgl + +#endif diff --git a/include/mbgl/util/std.hpp b/include/mbgl/util/std.hpp index e64820de4..0e2d3346b 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 struct unique_type { typedef ::std::unique_ptr single; }; -template struct unique_type { typedef ::std::unique_ptr unknown_bound; }; -template struct unique_type { typedef void known_bound; }; -} - -template -typename detail::unique_type::single make_unique(Args&&... args) { - return ::std::unique_ptr(new T(::std::forward(args)...)); -} - -template -typename detail::unique_type::unknown_bound make_unique(size_t size) { - return ::std::unique_ptr(new typename ::std::remove_extent::type[size]()); -} - -template -typename detail::unique_type::known_bound make_unique(Args&&...) = delete; - - - template 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 67bcc9571..a604c9e30 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 dc0410508..b09235a27 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"]; + // 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."); + + [MGLAccountManager setAccessToken:accessToken]; } - 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]; - - // Start Mapbox GL SDK - [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 d279e9d8f..9145b180f 100644 --- a/linux/main.cpp +++ b/linux/main.cpp @@ -1,6 +1,5 @@ #include #include "../platform/default/default_styles.hpp" -#include #include #include #include @@ -66,10 +65,19 @@ int main(int argc, char *argv[]) { sigIntHandler.sa_flags = 0; sigaction(SIGINT, &sigIntHandler, NULL); - view = mbgl::util::make_unique(); + view = std::make_unique(); 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 e5a711753..4a786f577 100644 --- a/macosx/main.mm +++ b/macosx/main.mm @@ -2,7 +2,7 @@ #include "../platform/default/default_styles.hpp" #include #include -#include +#include #include #include #include @@ -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 5ab4e1b43..207c33b23 100644 --- a/platform/darwin/http_request_nsurl.mm +++ b/platform/darwin/http_request_nsurl.mm @@ -2,7 +2,6 @@ #include #include -#include #include #include @@ -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 = std::make_unique(); 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 = std::make_unique(); 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 = std::make_unique(); response->status = Response::Error; response->message = "response class is not NSHTTPURLResponse"; } @@ -363,7 +363,7 @@ void HTTPRequest::retry() { } std::unique_ptr HTTPContext::createContext(uv_loop_t* loop) { - return util::make_unique(loop); + return std::make_unique(loop); } } diff --git a/platform/darwin/image.mm b/platform/darwin/image.mm index a9044774d..f6a8a6783 100644 --- a/platform/darwin/image.mm +++ b/platform/darwin/image.mm @@ -1,5 +1,4 @@ #include -#include #import @@ -98,7 +97,7 @@ Image::Image(const std::string &source_data) { height = uint32_t(CGImageGetHeight(image)); CGRect rect = {{ 0, 0 }, { static_cast(width), static_cast(height) }}; - img = util::make_unique(width * height * 4); + img = std::make_unique(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 823465940..d1413321b 100644 --- a/platform/darwin/reachability.m +++ b/platform/darwin/reachability.m @@ -27,32 +27,33 @@ #import +#import +#import +#import +#import +#import +#import + NSString *const kReachabilityChangedNotification = @"kReachabilityChangedNotification"; -@interface Reachability () + +@interface MGLReachability () @property (nonatomic, assign) SCNetworkReachabilityRef reachabilityRef; - - -#if NEEDS_DISPATCH_RETAIN_RELEASE -@property (nonatomic, assign) dispatch_queue_t reachabilitySerialQueue; -#else @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; + + 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(@"SCNetworkReachabilitySetDispatchQueue() failed: %s", SCErrorString(SCError())); #endif - if (!SCNetworkReachabilitySetCallback(self.reachabilityRef, TMReachabilityCallback, &context)) + // UH OH - FAILURE - stop any callbacks! + SCNetworkReachabilitySetCallback(self.reachabilityRef, NULL, NULL); + } + } + else { #ifdef DEBUG NSLog(@"SCNetworkReachabilitySetCallback() 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; - } - - self.reachabilityObject = nil; - - return NO; } - // Set it as our reachability queue, which will retain the queue - if(!SCNetworkReachabilitySetDispatchQueue(self.reachabilityRef, self.reachabilitySerialQueue)) - { -#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); -#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(@"Cellular", @""); + } + if (temp == ReachableViaWiFi) + { + return NSLocalizedString(@"WiFi", @""); + } - return NSLocalizedString(@"No Connection", @""); + 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 9dae2e8a9..04f85fbc3 100644 --- a/platform/default/asset_request_fs.cpp +++ b/platform/default/asset_request_fs.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #include #include #include @@ -114,7 +113,7 @@ void AssetRequest::fileStated(uv_fs_t *req) { if (stat->st_size > std::numeric_limits::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(); + auto response = std::make_unique(); 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(); + self->response = std::make_unique(); #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(); + auto response = std::make_unique(); 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::createContext(uv_loop_t*) { - return util::make_unique(); + return std::make_unique(); } } diff --git a/platform/default/asset_request_zip.cpp b/platform/default/asset_request_zip.cpp index 7666000d0..7fd93c55d 100644 --- a/platform/default/asset_request_zip.cpp +++ b/platform/default/asset_request_zip.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include @@ -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 = std::make_unique(); // 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 = std::make_unique(); 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::createContext(uv_loop_t* loop) { - return util::make_unique(loop); + return std::make_unique(loop); } } diff --git a/platform/default/headless_view.cpp b/platform/default/headless_view.cpp index aa07706d8..0f00c7563 100644 --- a/platform/default/headless_view.cpp +++ b/platform/default/headless_view.cpp @@ -5,7 +5,6 @@ #include -#include #include #include @@ -172,15 +171,15 @@ std::unique_ptr HeadlessView::readStillImage() { const unsigned int w = dimensions.pixelWidth(); const unsigned int h = dimensions.pixelHeight(); - auto image = util::make_unique(); + auto image = std::make_unique(); image->width = w; image->height = h; - image->pixels = util::make_unique(w * h); + image->pixels = std::make_unique(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(stride); + auto tmp = std::make_unique(stride); char *rgba = reinterpret_cast(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 45115b621..8599ea05e 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(stat.size); + const auto pem = std::make_unique(stat.size); if (static_cast(zip_fread(apkFile, reinterpret_cast(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(); + impl->response = std::make_unique(); } 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(); + baton->response = std::make_unique(); } 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 = std::make_unique(); } // Add human-readable error code @@ -734,7 +734,7 @@ void HTTPRequest::handleResult(CURLcode code) { } std::unique_ptr HTTPContext::createContext(uv_loop_t* loop) { - return util::make_unique(loop); + return std::make_unique(loop); } } diff --git a/platform/default/image.cpp b/platform/default/image.cpp index 1a10d78ff..12aea898c 100644 --- a/platform/default/image.cpp +++ b/platform/default/image.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #include @@ -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(width * height * 4); + img = std::make_unique(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 fc6daec6a..e80ccb681 100644 --- a/platform/default/image_reader.cpp +++ b/platform/default/image_reader.cpp @@ -49,11 +49,11 @@ std::unique_ptr getImageReader(char const* data, size_t size) { if (*type == "png") { - return util::make_unique>(data, size); + return std::make_unique>(data, size); } else if (*type == "jpeg") { - return util::make_unique>(data, size); + return std::make_unique>(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 fb4cdf74e..06d168ce4 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>("SQLite Cache", util::ThreadPriority::Low, path_)) { + : thread(std::make_unique>("SQLite Cache", util::ThreadPriority::Low, path_)) { } SQLiteCache::~SQLiteCache() = default; @@ -85,7 +85,7 @@ SQLiteCache::Impl::~Impl() { } void SQLiteCache::Impl::createDatabase() { - db = util::make_unique(path.c_str(), ReadWrite | Create); + db = std::make_unique(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(path.c_str(), ReadWrite | Create); + db = std::make_unique(path.c_str(), ReadWrite | Create); } else { Log::Error(Event::Database, ex.code, ex.what()); } @@ -146,8 +146,8 @@ std::unique_ptr SQLiteCache::Impl::get(const Resource &resource) { } if (!getStmt) { - // Initialize the statement 0 1 - getStmt = util::make_unique(db->prepare("SELECT `status`, `modified`, " + // Initialize the statement 0 1 + getStmt = std::make_unique(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 SQLiteCache::Impl::get(const Resource &resource) { getStmt->bind(1, unifiedURL.c_str()); if (getStmt->run()) { // There is data. - auto response = util::make_unique(); + auto response = std::make_unique(); response->status = Response::Status(getStmt->get(0)); response->modified = getStmt->get(1); response->etag = getStmt->get(2); @@ -200,7 +200,7 @@ void SQLiteCache::Impl::put(const Resource& resource, std::shared_ptr(db->prepare("REPLACE INTO `http_cache` (" + putStmt = std::make_unique(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( // 1 2 + refreshStmt = std::make_unique( // 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 a49433777..71987786c 100644 --- a/platform/ios/MGLAccountManager.m +++ b/platform/ios/MGLAccountManager.m @@ -1,6 +1,6 @@ #import -#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 000000000..3cc05b09f --- /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 5d1410751..a517a4d26 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 () @@ -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; + return self; } -- (instancetype)initWithFrame:(CGRect)frame accessToken:(NSString *)accessToken +- (instancetype)initWithFrame:(CGRect)frame styleURL:(NSURL *)styleURL { - return [self initWithFrame:frame accessToken:accessToken styleURL:nil]; -} - -- (instancetype)initWithFrame:(CGRect)frame accessToken:(NSString *)accessToken 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 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(convertedPoint.x, convertedPoint.y))); + return MGLLocationCoordinate2DFromLatLng(_mbglMap->latLngForPixel(mbgl::vec2(convertedPoint.x, convertedPoint.y))); } - (CGPoint)convertCoordinate:(CLLocationCoordinate2D)coordinate toPointToView:(UIView *)view { - mbgl::vec2 pixel = _mbglMap->pixelForLatLng(coordinateToLatLng(coordinate)); + mbgl::vec2 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; + 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; - if (self.accessToken) - { - self.layer.backgroundColor = [UIColor colorWithRed:59/255. - green:178/255. - blue:208/255. - alpha:0.8].CGColor; + UIView *diagnosticView = [[UIView alloc] init]; + diagnosticView.translatesAutoresizingMaskIntoConstraints = NO; + [self addSubview:diagnosticView]; - 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 = @"MGLMapView"; + headlineLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline]; + headlineLabel.textAlignment = NSTextAlignmentCenter; + headlineLabel.numberOfLines = 1; + headlineLabel.translatesAutoresizingMaskIntoConstraints = NO; + [headlineLabel setContentCompressionResistancePriority:UILayoutPriorityDefaultLow + forAxis:UILayoutConstraintAxisHorizontal]; + [diagnosticView addSubview:headlineLabel]; - // 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 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]; - // 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:MGLMapboxSetupDocumentationURLDisplayString forState:UIControlStateNormal]; + linkButton.translatesAutoresizingMaskIntoConstraints = NO; + linkButton.titleLabel.numberOfLines = 0; + [linkButton setContentCompressionResistancePriority:UILayoutPriorityDefaultLow + forAxis:UILayoutConstraintAxisHorizontal]; + [diagnosticView addSubview:linkButton]; - // 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]]; - } + // 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 94aa8be78..00de64a8b 100644 --- a/platform/ios/MGLMapboxEvents.h +++ b/platform/ios/MGLMapboxEvents.h @@ -1,5 +1,6 @@ #import +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 98bfe21cf..e7f400c5d 100644 --- a/platform/ios/MGLMapboxEvents.m +++ b/platform/ios/MGLMapboxEvents.m @@ -13,9 +13,11 @@ #include +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 bb230e4ef..067c5a406 100644 --- a/src/mbgl/geometry/glyph_atlas.cpp +++ b/src/mbgl/geometry/glyph_atlas.cpp @@ -1,11 +1,11 @@ #include +#include + #include #include #include -#include - #include #include @@ -16,7 +16,7 @@ GlyphAtlas::GlyphAtlas(uint16_t width_, uint16_t height_) : width(width_), height(height_), bin(width_, height_), - data(util::make_unique(width_ * height_)), + data(std::make_unique(width_ * height_)), dirty(true) { } @@ -69,8 +69,9 @@ Rect 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 GlyphAtlas::addGlyph(uintptr_t tileUID, // Copy the bitmap const uint8_t* source = reinterpret_cast(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 91ac15639..dc5ee0901 100644 --- a/src/mbgl/geometry/line_atlas.cpp +++ b/src/mbgl/geometry/line_atlas.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include @@ -15,7 +14,7 @@ using namespace mbgl; LineAtlas::LineAtlas(uint16_t w, uint16_t h) : width(w), height(h), - data(util::make_unique(w * h)), + data(std::make_unique(w * h)), dirty(true) { } diff --git a/src/mbgl/geometry/sprite_atlas.cpp b/src/mbgl/geometry/sprite_atlas.cpp index 5d47793cf..93bc76aa5 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(width * pixelRatio); dimension h = static_cast(height * pixelRatio); - data = util::make_unique(w * h); + data = std::make_unique(w * h); std::fill(data.get(), data.get() + w * h, 0); } } @@ -151,12 +153,14 @@ void SpriteAtlas::copy(const Rect& dst, const SpritePosition& src, co const vec2 srcSize { sprite->raster->getWidth(), sprite->raster->getHeight() }; const Rect srcPos { src.x, src.y, src.width, src.height }; + const int offset = 1; + allocate(); uint32_t *const dstData = data.get(); const vec2 dstSize { static_cast(width * pixelRatio), static_cast(height * pixelRatio) }; - const Rect dstPos { static_cast(dst.x * pixelRatio), - static_cast(dst.y * pixelRatio), + const Rect dstPos { static_cast((offset + dst.x) * pixelRatio), + static_cast((offset + dst.y) * pixelRatio), static_cast(dst.originalW * pixelRatio), static_cast(dst.originalH * pixelRatio) }; @@ -295,6 +299,8 @@ void SpriteAtlas::bind(bool linear) { SpriteAtlas::~SpriteAtlas() { std::lock_guard 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 9d21b4e48..6df49ec64 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include @@ -110,7 +109,7 @@ AnnotationManager::addPointAnnotations(const std::vector& points, // track the annotation global ID and its geometry auto anno_it = annotations.emplace( annotationID, - util::make_unique(AnnotationType::Point, + std::make_unique(AnnotationType::Point, AnnotationSegments({ { points[i] } }))); const uint8_t maxZoom = data.transform.getMaxZoom(); @@ -162,7 +161,7 @@ AnnotationManager::addPointAnnotations(const std::vector& points, // create tile & record annotation association auto tile_pos = tiles.emplace( tileID, std::make_pair(std::unordered_set({ annotationID }), - util::make_unique())); + std::make_unique())); // 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 a80b03226..0c9a078e5 100644 --- a/src/mbgl/map/annotation.hpp +++ b/src/mbgl/map/annotation.hpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index 3ba82252d..047918930 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -3,15 +3,14 @@ #include #include -#include #include #include namespace mbgl { Map::Map(View& view, FileSource& fileSource, MapMode mode) - : data(util::make_unique(view, mode)), - context(util::make_unique>("Map", util::ThreadPriority::Regular, view, fileSource, *data)) + : data(std::make_unique(view, mode)), + context(std::make_unique>("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 f373274eb..c8948097f 100644 --- a/src/mbgl/map/map_context.cpp +++ b/src/mbgl/map/map_context.cpp @@ -20,11 +20,9 @@ #include -#include #include #include #include -#include #include 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(Update::Nothing)), - asyncUpdate(util::make_unique(loop, [this] { update(); })), - glyphStore(util::make_unique(loop, env)), - glyphAtlas(util::make_unique(1024, 1024)), - spriteAtlas(util::make_unique(512, 512)), - lineAtlas(util::make_unique(512, 512)), - texturePool(util::make_unique()) { + asyncUpdate(std::make_unique(loop, [this] { update(); })), + glyphStore(std::make_unique(loop, env)), + glyphAtlas(std::make_unique(1024, 1024)), + spriteAtlas(std::make_unique(512, 512)), + lineAtlas(std::make_unique(512, 512)), + texturePool(std::make_unique()) { 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