Merge remote-tracking branch 'origin/master' into new-labelling
Conflicts: src/mbgl/map/source.cpp src/mbgl/map/source.hpp src/mbgl/map/tile_data.cpp src/mbgl/map/tile_parser.cpp src/mbgl/map/vector_tile_data.cpp src/mbgl/renderer/painter.cpp src/mbgl/renderer/symbol_bucket.cpp src/mbgl/text/glyph.hpp src/mbgl/text/glyph_store.cpp src/mbgl/text/placement.cpp test/suite
This commit is contained in:
2
.mason
2
.mason
Submodule .mason updated: c43967404c...ace19da711
@@ -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:
|
||||
|
||||
27
README.md
27
README.md
@@ -28,7 +28,7 @@ Be sure to pull down all submodules first:
|
||||
|
||||
## OS X
|
||||
|
||||
Prerequisites include the Boost headers (for routines provided therein) and ImageMagick (for tests). To install all prerequisites, use [Homebrew](http://brew.sh/) and type `brew install pkg-config boost imagemagick`.
|
||||
ImageMagick is required for tests. It's recommended to install it with [Homebrew](http://brew.sh/) via `brew install imagemagick`.
|
||||
|
||||
To create projects, you can run:
|
||||
|
||||
@@ -58,7 +58,6 @@ pod 'MapboxGL'
|
||||
|
||||
#### Manually
|
||||
|
||||
1. Use [Homebrew](http://brew.sh/) to install Boost headers: `brew install boost`.
|
||||
1. Install [appledoc](http://appledoc.gentlebytes.com/appledoc/) for API docs generation. We recommend [`2.2v963`](https://github.com/tomaz/appledoc/releases/tag/v2.2-963), which currently isn't available in Homebrew.
|
||||
1. Run `make ipackage`. The packaging script will produce the statically-linked `libMapboxGL.a`, `MapboxGL.bundle` for resources, a `Headers` folder, and a `Docs` folder with HTML API documentation.
|
||||
1. Copy the contents of `build/ios/pkg/static` into your project. It should happen automatically, but ensure that:
|
||||
@@ -94,7 +93,7 @@ Target iOS: 7.0 through latest 8.x.
|
||||
|
||||
We are using Ubuntu for development. While the software should work on other distributions as well, we are not providing explicit build instructions here.
|
||||
|
||||
Install GCC 4.9+ if you are running Ubuntu 13.10 or older. Alternatively, you can also use Clang 3.5+.
|
||||
Install GCC 4.9+ if you are running Ubuntu 14.04 or older. Alternatively, you can also use [Clang 3.5+](http://llvm.org/apt/).
|
||||
|
||||
sudo add-apt-repository --yes ppa:ubuntu-toolchain-r/test
|
||||
sudo apt-get update
|
||||
@@ -105,7 +104,8 @@ Ensure you have git and other build essentials:
|
||||
|
||||
sudo apt-get install curl git build-essential zlib1g-dev automake \
|
||||
libtool xutils-dev make cmake pkg-config python-pip \
|
||||
libcurl4-openssl-dev libpng-dev libsqlite3-dev
|
||||
libcurl4-openssl-dev libpng-dev libsqlite3-dev \
|
||||
libllvm3.4
|
||||
|
||||
Install glfw3 dependencies:
|
||||
|
||||
@@ -114,16 +114,6 @@ Install glfw3 dependencies:
|
||||
x11proto-xf86vidmode-dev libxxf86vm-dev \
|
||||
libxcursor-dev libxinerama-dev
|
||||
|
||||
Finally, install Boost. If you're running Ubuntu 12.04 or older, you need to install a backport PPA since the version provided by APT doesn't contain Boost Geometry:
|
||||
|
||||
sudo add-apt-repository --yes ppa:boost-latest/ppa
|
||||
sudo apt-get update
|
||||
sudo apt-get install libboost1.55-dev
|
||||
|
||||
Otherwise, you can just install
|
||||
|
||||
sudo apt-get install libboost-dev libboost-program-options-dev
|
||||
|
||||
Then, you can then proceed to build the library:
|
||||
|
||||
git submodule update --init
|
||||
@@ -148,9 +138,9 @@ Install Oracle JDK 7 (requires license agreement) from http://www.oracle.com/tec
|
||||
|
||||
export JAVA_HOME="/dir/to/jdk1.7.0_71"
|
||||
|
||||
Install the [Android NDK Revision 10d](https://developer.android.com/tools/sdk/ndk/index.html).
|
||||
Install the [Android NDK Revision 10e](https://developer.android.com/tools/sdk/ndk/index.html).
|
||||
|
||||
export ANDROID_NDK_PATH="/dir/to/android-ndk-r10d"
|
||||
export ANDROID_NDK_PATH="/dir/to/android-ndk-r10e"
|
||||
|
||||
Install the Android SDK. We recommend doing this by way of [Android Studio](https://developer.android.com/sdk/installing/studio.html).
|
||||
|
||||
@@ -168,7 +158,7 @@ Install Oracle JDK 7+:
|
||||
|
||||
brew cask install java
|
||||
|
||||
Install the [Android NDK Revision 10d](https://developer.android.com/tools/sdk/ndk/index.html) for 64-bit OS X:
|
||||
Install the [Android NDK Revision 10e](https://developer.android.com/tools/sdk/ndk/index.html) for 64-bit OS X:
|
||||
|
||||
brew install android-ndk
|
||||
|
||||
@@ -182,7 +172,7 @@ Install [Android Studio](https://developer.android.com/sdk/installing/studio.htm
|
||||
By default, the SDK will be installed to `/usr/local/opt/android-sdk`. If you open Android Studio at this point, you may get an error message telling you that it can't find a JVM, it's because you installed a custom Java VM from Oracle. Follow [these instructions](http://tools.android.com/recent/androidstudio1rc3_releasecandidate3released) to start Android Studio. You'll wind up setting these environment variables in your .bash_profile or similar:
|
||||
|
||||
echo "export ANDROID_HOME=`brew --prefix android-sdk`" >> .bash_profile
|
||||
echo "export ANDROID_NDK_PATH=`brew --cellar android-ndk`/r10d" >> .bash_profile
|
||||
echo "export ANDROID_NDK_PATH=`brew --cellar android-ndk`/r10e" >> .bash_profile
|
||||
# Replace <path to JDK> with something like /Library/Java/JavaVirtualMachines/jdk1.8.0_31.jdk
|
||||
echo "export JAVA_HOME=<path to JDK>" >> .bash_profile
|
||||
echo "export STUDIO_JDK=$JAVA_HOME" >> .bash_profile
|
||||
@@ -253,6 +243,7 @@ Some styles in JSON format are included at `./styles`. See the [style spec](http
|
||||
|
||||
## Desktop
|
||||
|
||||
- Press `S` to cycle through bundled styles
|
||||
- Press `X` to reset the transform
|
||||
- Press `N` to reset north
|
||||
- Press `Tab` to toggle debug information
|
||||
|
||||
@@ -373,14 +373,14 @@ nativeSetAccessToken(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jstring a
|
||||
mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetAccessToken");
|
||||
assert(nativeMapViewPtr != 0);
|
||||
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
|
||||
nativeMapView->getMap().setAccessToken(std_string_from_jstring(env, accessToken));
|
||||
nativeMapView->getFileSource().setAccessToken(std_string_from_jstring(env, accessToken));
|
||||
}
|
||||
|
||||
jstring JNICALL nativeGetAccessToken(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
|
||||
mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetAccessToken");
|
||||
assert(nativeMapViewPtr != 0);
|
||||
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
|
||||
return std_string_to_jstring(env, nativeMapView->getMap().getAccessToken());
|
||||
return std_string_to_jstring(env, nativeMapView->getFileSource().getAccessToken());
|
||||
}
|
||||
|
||||
void JNICALL nativeCancelTransitions(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include <mbgl/platform/event.hpp>
|
||||
#include <mbgl/platform/log.hpp>
|
||||
#include <mbgl/platform/gl.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace android {
|
||||
@@ -214,7 +213,7 @@ void NativeMapView::initializeDisplay() {
|
||||
throw new std::runtime_error("eglChooseConfig() failed");
|
||||
}
|
||||
|
||||
const std::unique_ptr<EGLConfig[]> configs = mbgl::util::make_unique<EGLConfig[]>(numConfigs);
|
||||
const auto configs = std::make_unique<EGLConfig[]>(numConfigs);
|
||||
if (!eglChooseConfig(display, configAttribs, configs.get(), numConfigs, &numConfigs)) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL, "eglChooseConfig() returned error %d", eglGetError());
|
||||
throw new std::runtime_error("eglChooseConfig() failed");
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include <mbgl/map/map.hpp>
|
||||
#include <mbgl/map/still_image.hpp>
|
||||
#include <mbgl/util/image.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/util/io.hpp>
|
||||
|
||||
#include <mbgl/platform/default/headless_view.hpp>
|
||||
@@ -81,14 +80,14 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
HeadlessView view;
|
||||
Map map(view, fileSource, MapMode::Still);
|
||||
|
||||
// Set access token if present
|
||||
if (token.size()) {
|
||||
map.setAccessToken(std::string(token));
|
||||
fileSource.setAccessToken(std::string(token));
|
||||
}
|
||||
|
||||
HeadlessView view;
|
||||
Map map(view, fileSource, MapMode::Still);
|
||||
|
||||
map.setStyleJSON(style, ".");
|
||||
map.setClasses(classes);
|
||||
|
||||
@@ -111,7 +110,16 @@ int main(int argc, char *argv[]) {
|
||||
util::write_file(output, png);
|
||||
});
|
||||
|
||||
map.renderStill([async](std::unique_ptr<const StillImage> image) {
|
||||
map.renderStill([async](std::exception_ptr error, std::unique_ptr<const StillImage> image) {
|
||||
try {
|
||||
if (error) {
|
||||
std::rethrow_exception(error);
|
||||
}
|
||||
} catch(std::exception& e) {
|
||||
std::cout << "Error: " << e.what() << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
async->data = const_cast<StillImage *>(image.release());
|
||||
uv_async_send(async);
|
||||
});
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -20,30 +20,25 @@ IB_DESIGNABLE
|
||||
|
||||
/** @name Initializing a Map View */
|
||||
|
||||
/** Initialize a map view with the default style, given frame, and access token set in MapboxGL singleton.
|
||||
* @param frame The frame with which to initialize the map view.
|
||||
* @return An initialized map view, or `nil` if the map view was unable to be initialized. */
|
||||
/** Initializes and returns a newly allocated map view with the specified frame and the default style.
|
||||
* @param frame The frame for the view, measured in points.
|
||||
* @return An initialized map view. */
|
||||
- (instancetype)initWithFrame:(CGRect)frame;
|
||||
- (instancetype)initWithFrame:(CGRect)frame accessToken:(NSString *)accessToken __attribute__((unavailable("Use -initWithFrame:. Set MGLMapboxAccessToken in the Info.plist or call +[MGLAccountManager setAccessToken:].")));
|
||||
|
||||
/** Initialize a map view with the default style and a given frame and access token.
|
||||
* @param frame The frame with which to initialize the map view.
|
||||
* @param accessToken A Mapbox API access token.
|
||||
* @return An initialized map view, or `nil` if the map view was unable to be initialized. */
|
||||
- (instancetype)initWithFrame:(CGRect)frame accessToken:(NSString *)accessToken;
|
||||
|
||||
/** Initialize a map view with a given frame, access token, and style URL.
|
||||
* @param frame The frame with which to initialize the map view.
|
||||
* @param accessToken A Mapbox API access token.
|
||||
* @param styleURL The map style URL to use. Can be either an HTTP/HTTPS URL or a Mapbox map ID style URL (`mapbox://<user.style>`).
|
||||
* @return An initialized map view, or `nil` if the map view was unable to be initialized. */
|
||||
- (instancetype)initWithFrame:(CGRect)frame accessToken:(NSString *)accessToken styleURL:(NSURL *)styleURL;
|
||||
/** Initializes and returns a newly allocated map view with the specified frame and style URL.
|
||||
* @param frame The frame for the view, measured in points.
|
||||
* @param styleURL The map style URL to use. Can be either an HTTP/HTTPS URL or a Mapbox map ID style URL (`mapbox://<user.style>`).
|
||||
* @return An initialized map view. */
|
||||
- (instancetype)initWithFrame:(CGRect)frame styleURL:(NSURL *)styleURL;
|
||||
- (instancetype)initWithFrame:(CGRect)frame accessToken:(NSString *)accessToken styleURL:(NSURL *)styleURL __attribute__((unavailable("Use -initWithFrame:styleURL:. Set MGLMapboxAccessToken in the Info.plist or call +[MGLAccountManager setAccessToken:].")));
|
||||
|
||||
#pragma mark - Authorizing Access
|
||||
|
||||
/** @name Authorizing Access */
|
||||
|
||||
/** Mapbox API access token for the map view. */
|
||||
@property (nonatomic) NSString *accessToken;
|
||||
@property (nonatomic) NSString *accessToken __attribute__((unavailable("Use +[MGLAccountManager accessToken] and +[MGLAccountManager setAccessToken:].")));
|
||||
|
||||
#pragma mark - Managing Constraints
|
||||
|
||||
@@ -162,12 +157,13 @@ IB_DESIGNABLE
|
||||
|
||||
/** @name Styling the Map */
|
||||
|
||||
/** Mapbox map ID of the style currently displayed in the receiver, or `nil` if the style does not have a map ID.
|
||||
/** Mapbox ID of the style currently displayed in the receiver, or `nil` if the style does not have an ID.
|
||||
*
|
||||
* The style may lack a map ID if it is located at an HTTP, HTTPS, or local file URL. Use `styleURL` to get the URL in these cases.
|
||||
* The style may lack an ID if it is located at an HTTP, HTTPS, or local file URL. Use `styleURL` to get the URL in these cases.
|
||||
*
|
||||
* To display the default style, set this property to `nil`. */
|
||||
@property (nonatomic) NSString *mapID;
|
||||
@property (nonatomic) NSString *styleID;
|
||||
@property (nonatomic) NSString *mapID __attribute__((unavailable("Use styleID.")));
|
||||
|
||||
/** Returns the URLs to the styles bundled with the library. */
|
||||
- (NSArray *)bundledStyleURLs;
|
||||
|
||||
@@ -42,7 +42,7 @@ public:
|
||||
|
||||
// Register a callback that will get called (on the render thread) when all resources have
|
||||
// been loaded and a complete render occurs.
|
||||
using StillImageCallback = std::function<void(std::unique_ptr<const StillImage>)>;
|
||||
using StillImageCallback = std::function<void(std::exception_ptr, std::unique_ptr<const StillImage>)>;
|
||||
void renderStill(StillImageCallback callback);
|
||||
|
||||
// Triggers a synchronous or asynchronous render.
|
||||
@@ -99,10 +99,6 @@ public:
|
||||
uint16_t getWidth() const;
|
||||
uint16_t getHeight() const;
|
||||
|
||||
// API
|
||||
void setAccessToken(const std::string &token);
|
||||
std::string getAccessToken() const;
|
||||
|
||||
// Projection
|
||||
void getWorldBoundsMeters(ProjectedMeters &sw, ProjectedMeters &ne) const;
|
||||
void getWorldBoundsLatLng(LatLng &sw, LatLng &ne) const;
|
||||
|
||||
@@ -28,24 +28,6 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <SystemConfiguration/SystemConfiguration.h>
|
||||
|
||||
#import <sys/socket.h>
|
||||
#import <netinet/in.h>
|
||||
#import <netinet6/in6.h>
|
||||
#import <arpa/inet.h>
|
||||
#import <ifaddrs.h>
|
||||
#import <netdb.h>
|
||||
|
||||
/**
|
||||
* Does ARC support GCD objects?
|
||||
* It does if the minimum deployment target is iOS 6+ or Mac OS X 8+
|
||||
*
|
||||
* @see http://opensource.apple.com/source/libdispatch/libdispatch-228.18/os/object.h
|
||||
**/
|
||||
#if OS_OBJECT_USE_OBJC
|
||||
#define NEEDS_DISPATCH_RETAIN_RELEASE 0
|
||||
#else
|
||||
#define NEEDS_DISPATCH_RETAIN_RELEASE 1
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Create NS_ENUM macro if it does not exist on the targeted version of iOS or OS X.
|
||||
@@ -65,28 +47,29 @@ typedef NS_ENUM(NSInteger, NetworkStatus) {
|
||||
ReachableViaWWAN = 1
|
||||
};
|
||||
|
||||
@class Reachability;
|
||||
@class MGLReachability;
|
||||
|
||||
typedef void (^NetworkReachable)(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;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#ifndef MBGL_UTIL_IMAGE_READER_HPP
|
||||
#define MBGL_UTIL_IMAGE_READER_HPP
|
||||
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/util/noncopyable.hpp>
|
||||
// stl
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
namespace mbgl { namespace util {
|
||||
|
||||
|
||||
@@ -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" },
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
#include <mbgl/platform/event.hpp>
|
||||
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/util/noncopyable.hpp>
|
||||
|
||||
#include <memory>
|
||||
|
||||
@@ -15,6 +15,9 @@ public:
|
||||
DefaultFileSource(FileCache *cache, const std::string &root = "");
|
||||
~DefaultFileSource() override;
|
||||
|
||||
void setAccessToken(const std::string& t) { accessToken = t; }
|
||||
std::string getAccessToken() const { return accessToken; }
|
||||
|
||||
// FileSource API
|
||||
Request* request(const Resource&, uv_loop_t*, Callback) override;
|
||||
void cancel(Request*) override;
|
||||
@@ -23,6 +26,7 @@ public:
|
||||
class Impl;
|
||||
private:
|
||||
const std::unique_ptr<util::Thread<Impl>> thread;
|
||||
std::string accessToken;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include "resource.hpp"
|
||||
|
||||
#include <mbgl/util/noncopyable.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/util/util.hpp>
|
||||
|
||||
#include <functional>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
30
include/mbgl/util/exclusive.hpp
Normal file
30
include/mbgl/util/exclusive.hpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef MBGL_UTIL_EXCLUSIVE
|
||||
#define MBGL_UTIL_EXCLUSIVE
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
|
||||
namespace mbgl {
|
||||
namespace util {
|
||||
|
||||
template <class T>
|
||||
class exclusive {
|
||||
public:
|
||||
inline exclusive(T* val, std::unique_ptr<std::lock_guard<std::mutex>> mtx) : ptr(val), lock(std::move(mtx)) {}
|
||||
|
||||
inline T* operator->() { return ptr; }
|
||||
inline const T* operator->() const { return ptr; }
|
||||
inline T* operator*() { return ptr; }
|
||||
inline const T* operator*() const { return ptr; }
|
||||
|
||||
private:
|
||||
T *ptr;
|
||||
std::unique_ptr<std::lock_guard<std::mutex>> lock;
|
||||
};
|
||||
|
||||
|
||||
} // end namespace util
|
||||
} // end namespace mbgl
|
||||
|
||||
#endif
|
||||
@@ -8,29 +8,6 @@
|
||||
namespace mbgl {
|
||||
namespace util {
|
||||
|
||||
// C++14 backfill based on http://llvm.org/svn/llvm-project/libcxx/trunk/include/memory
|
||||
|
||||
namespace detail {
|
||||
template<class T> struct unique_type { typedef ::std::unique_ptr<T> single; };
|
||||
template<class T> struct unique_type<T[]> { typedef ::std::unique_ptr<T[]> unknown_bound; };
|
||||
template<class T, size_t size> struct unique_type<T[size]> { typedef void known_bound; };
|
||||
}
|
||||
|
||||
template<class T, class... Args>
|
||||
typename detail::unique_type<T>::single make_unique(Args&&... args) {
|
||||
return ::std::unique_ptr<T>(new T(::std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template<class T>
|
||||
typename detail::unique_type<T>::unknown_bound make_unique(size_t size) {
|
||||
return ::std::unique_ptr<T>(new typename ::std::remove_extent<T>::type[size]());
|
||||
}
|
||||
|
||||
template<class T, class... Args>
|
||||
typename detail::unique_type<T>::known_bound make_unique(Args&&...) = delete;
|
||||
|
||||
|
||||
|
||||
template <typename Container, typename ForwardIterator, typename Predicate>
|
||||
void erase_if(Container &container, ForwardIterator it, const ForwardIterator end, Predicate pred) {
|
||||
while (it != end) {
|
||||
|
||||
@@ -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.'
|
||||
|
||||
@@ -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]];
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include <mbgl/mbgl.hpp>
|
||||
#include "../platform/default/default_styles.hpp"
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/util/uv.hpp>
|
||||
#include <mbgl/platform/log.hpp>
|
||||
#include <mbgl/platform/platform.hpp>
|
||||
@@ -66,10 +65,19 @@ int main(int argc, char *argv[]) {
|
||||
sigIntHandler.sa_flags = 0;
|
||||
sigaction(SIGINT, &sigIntHandler, NULL);
|
||||
|
||||
view = mbgl::util::make_unique<GLFWView>();
|
||||
view = std::make_unique<GLFWView>();
|
||||
|
||||
mbgl::SQLiteCache cache("/tmp/mbgl-cache.db");
|
||||
mbgl::DefaultFileSource fileSource(&cache);
|
||||
|
||||
// Set access token if present
|
||||
const char *token = getenv("MAPBOX_ACCESS_TOKEN");
|
||||
if (token == nullptr) {
|
||||
mbgl::Log::Warning(mbgl::Event::Setup, "no access token set. mapbox.com tiles won't work.");
|
||||
} else {
|
||||
fileSource.setAccessToken(std::string(token));
|
||||
}
|
||||
|
||||
mbgl::Map map(*view, fileSource);
|
||||
|
||||
// Load settings
|
||||
@@ -92,14 +100,6 @@ int main(int argc, char *argv[]) {
|
||||
mbgl::Log::Info(mbgl::Event::Setup, std::string("Changed style to: ") + newStyle.first);
|
||||
});
|
||||
|
||||
// Set access token if present
|
||||
const char *token = getenv("MAPBOX_ACCESS_TOKEN");
|
||||
if (token == nullptr) {
|
||||
mbgl::Log::Warning(mbgl::Event::Setup, "no access token set. mapbox.com tiles won't work.");
|
||||
} else {
|
||||
map.setAccessToken(std::string(token));
|
||||
}
|
||||
|
||||
// Load style
|
||||
if (style.empty()) {
|
||||
const auto& newStyle = mbgl::util::defaultStyles.front();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include "../platform/default/default_styles.hpp"
|
||||
#include <mbgl/platform/platform.hpp>
|
||||
#include <mbgl/platform/darwin/settings_nsuserdefaults.hpp>
|
||||
#include <mbgl/platform/darwin/Reachability.h>
|
||||
#include <mbgl/platform/darwin/reachability.h>
|
||||
#include <mbgl/platform/default/glfw_view.hpp>
|
||||
#include <mbgl/storage/default_file_source.hpp>
|
||||
#include <mbgl/storage/sqlite_cache.hpp>
|
||||
@@ -106,6 +106,12 @@ int main() {
|
||||
|
||||
mbgl::SQLiteCache cache(defaultCacheDatabase());
|
||||
mbgl::DefaultFileSource fileSource(&cache);
|
||||
|
||||
// Set access token if present
|
||||
NSString *accessToken = [[NSProcessInfo processInfo] environment][@"MAPBOX_ACCESS_TOKEN"];
|
||||
if (!accessToken) mbgl::Log::Warning(mbgl::Event::Setup, "No access token set. Mapbox vector tiles won't work.");
|
||||
if (accessToken) fileSource.setAccessToken([accessToken cStringUsingEncoding:[NSString defaultCStringEncoding]]);
|
||||
|
||||
mbgl::Map map(view, fileSource);
|
||||
|
||||
URLHandler *handler = [[URLHandler alloc] init];
|
||||
@@ -114,8 +120,8 @@ int main() {
|
||||
[appleEventManager setEventHandler:handler andSelector:@selector(handleGetURLEvent:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL];
|
||||
|
||||
// Notify map object when network reachability status changes.
|
||||
Reachability* reachability = [Reachability reachabilityForInternetConnection];
|
||||
reachability.reachableBlock = ^(Reachability *) {
|
||||
MGLReachability* reachability = [MGLReachability reachabilityForInternetConnection];
|
||||
reachability.reachableBlock = ^(MGLReachability *) {
|
||||
mbgl::NetworkStatus::Reachable();
|
||||
};
|
||||
[reachability startNotifier];
|
||||
@@ -140,11 +146,6 @@ int main() {
|
||||
mbgl::Log::Info(mbgl::Event::Setup, std::string("Changed style to: ") + newStyle.first);
|
||||
});
|
||||
|
||||
// Set access token if present
|
||||
NSString *accessToken = [[NSProcessInfo processInfo] environment][@"MAPBOX_ACCESS_TOKEN"];
|
||||
if (!accessToken) mbgl::Log::Warning(mbgl::Event::Setup, "No access token set. Mapbox vector tiles won't work.");
|
||||
if (accessToken) map.setAccessToken([accessToken cStringUsingEncoding:[NSString defaultCStringEncoding]]);
|
||||
|
||||
// Load style
|
||||
const auto& newStyle = mbgl::util::defaultStyles.front();
|
||||
map.setStyleURL(newStyle.first);
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#include <mbgl/storage/resource.hpp>
|
||||
#include <mbgl/storage/response.hpp>
|
||||
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/util/time.hpp>
|
||||
#include <mbgl/util/parsedate.h>
|
||||
|
||||
@@ -91,6 +90,7 @@ public:
|
||||
|
||||
NSURLSession *session = nil;
|
||||
NSString *userAgent = nil;
|
||||
NSInteger accountType = 0;
|
||||
};
|
||||
|
||||
HTTPNSURLContext::HTTPNSURLContext(uv_loop_t *loop_) : HTTPContext(loop_) {
|
||||
@@ -107,6 +107,8 @@ HTTPNSURLContext::HTTPNSURLContext(uv_loop_t *loop_) : HTTPContext(loop_) {
|
||||
|
||||
// Write user agent string
|
||||
userAgent = @"MapboxGL";
|
||||
|
||||
accountType = [[NSUserDefaults standardUserDefaults] integerForKey:@"MGLMapboxAccountType"];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,17 +155,15 @@ void HTTPRequest::start() {
|
||||
|
||||
@autoreleasepool {
|
||||
|
||||
NSMutableString *url = [NSMutableString stringWithString:@(resource.url.c_str())];
|
||||
if ([[NSUserDefaults standardUserDefaults] integerForKey:@"MGLMapboxAccountType"] == 0) {
|
||||
if ([url rangeOfString:@"?"].location == NSNotFound) {
|
||||
[url appendString:@"?"];
|
||||
} else {
|
||||
[url appendString:@"&"];
|
||||
}
|
||||
[url appendString:@"events=true"];
|
||||
NSURL *url = [NSURL URLWithString:@(resource.url.c_str())];
|
||||
if (context->accountType == 0 &&
|
||||
([url.host isEqualToString:@"mapbox.com"] || [url.host hasSuffix:@".mapbox.com"])) {
|
||||
NSString *absoluteString = [url.absoluteString stringByAppendingFormat:
|
||||
(url.query ? @"&%@" : @"?%@"), @"events=true"];
|
||||
url = [NSURL URLWithString:absoluteString];
|
||||
}
|
||||
|
||||
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
|
||||
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
|
||||
if (existingResponse) {
|
||||
if (!existingResponse->etag.empty()) {
|
||||
[req addValue:@(existingResponse->etag.c_str()) forHTTPHeaderField:@"If-None-Match"];
|
||||
@@ -249,7 +249,7 @@ void HTTPRequest::handleResult(NSData *data, NSURLResponse *res, NSError *error)
|
||||
} else {
|
||||
// TODO: Use different codes for host not found, timeout, invalid URL etc.
|
||||
// These can be categorized in temporary and permanent errors.
|
||||
response = util::make_unique<Response>();
|
||||
response = std::make_unique<Response>();
|
||||
response->status = Response::Error;
|
||||
response->message = [[error localizedDescription] UTF8String];
|
||||
|
||||
@@ -281,7 +281,7 @@ void HTTPRequest::handleResult(NSData *data, NSURLResponse *res, NSError *error)
|
||||
} else if ([res isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||
const long responseCode = [(NSHTTPURLResponse *)res statusCode];
|
||||
|
||||
response = util::make_unique<Response>();
|
||||
response = std::make_unique<Response>();
|
||||
response->data = {(const char *)[data bytes], [data length]};
|
||||
|
||||
NSDictionary *headers = [(NSHTTPURLResponse *)res allHeaderFields];
|
||||
@@ -337,7 +337,7 @@ void HTTPRequest::handleResult(NSData *data, NSURLResponse *res, NSError *error)
|
||||
} else {
|
||||
// This should never happen.
|
||||
status = ResponseStatus::PermanentError;
|
||||
response = util::make_unique<Response>();
|
||||
response = std::make_unique<Response>();
|
||||
response->status = Response::Error;
|
||||
response->message = "response class is not NSHTTPURLResponse";
|
||||
}
|
||||
@@ -363,7 +363,7 @@ void HTTPRequest::retry() {
|
||||
}
|
||||
|
||||
std::unique_ptr<HTTPContext> HTTPContext::createContext(uv_loop_t* loop) {
|
||||
return util::make_unique<HTTPNSURLContext>(loop);
|
||||
return std::make_unique<HTTPNSURLContext>(loop);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include <mbgl/util/image.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
|
||||
#import <ImageIO/ImageIO.h>
|
||||
|
||||
@@ -98,7 +97,7 @@ Image::Image(const std::string &source_data) {
|
||||
height = uint32_t(CGImageGetHeight(image));
|
||||
CGRect rect = {{ 0, 0 }, { static_cast<CGFloat>(width), static_cast<CGFloat>(height) }};
|
||||
|
||||
img = util::make_unique<char[]>(width * height * 4);
|
||||
img = std::make_unique<char[]>(width * height * 4);
|
||||
CGContextRef context = CGBitmapContextCreate(img.get(), width, height, 8, width * 4,
|
||||
color_space, kCGImageAlphaPremultipliedLast);
|
||||
if (!context) {
|
||||
|
||||
@@ -27,32 +27,33 @@
|
||||
|
||||
#import <mbgl/platform/darwin/reachability.h>
|
||||
|
||||
#import <sys/socket.h>
|
||||
#import <netinet/in.h>
|
||||
#import <netinet6/in6.h>
|
||||
#import <arpa/inet.h>
|
||||
#import <ifaddrs.h>
|
||||
#import <netdb.h>
|
||||
|
||||
|
||||
NSString *const kReachabilityChangedNotification = @"kReachabilityChangedNotification";
|
||||
|
||||
@interface Reachability ()
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include <mbgl/storage/asset_context.hpp>
|
||||
#include <mbgl/storage/resource.hpp>
|
||||
#include <mbgl/storage/response.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/util/util.hpp>
|
||||
#include <mbgl/util/url.hpp>
|
||||
#include <mbgl/util/uv.hpp>
|
||||
@@ -114,7 +113,7 @@ void AssetRequest::fileStated(uv_fs_t *req) {
|
||||
if (stat->st_size > std::numeric_limits<int>::max()) {
|
||||
// File is too large for us to open this way because uv_buf's only support unsigned
|
||||
// ints as maximum size.
|
||||
auto response = util::make_unique<Response>();
|
||||
auto response = std::make_unique<Response>();
|
||||
response->status = Response::Error;
|
||||
#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
|
||||
response->message = uv_strerror(uv_err_t {UV_EFBIG, 0});
|
||||
@@ -126,7 +125,7 @@ void AssetRequest::fileStated(uv_fs_t *req) {
|
||||
uv_fs_req_cleanup(req);
|
||||
uv_fs_close(req->loop, req, self->fd, fileClosed);
|
||||
} else {
|
||||
self->response = util::make_unique<Response>();
|
||||
self->response = std::make_unique<Response>();
|
||||
#ifdef __APPLE__
|
||||
self->response->modified = stat->st_mtimespec.tv_sec;
|
||||
#else
|
||||
@@ -184,7 +183,7 @@ void AssetRequest::notifyError(uv_fs_t *req) {
|
||||
MBGL_VERIFY_THREAD(self->tid);
|
||||
|
||||
if (req->result < 0 && !self->canceled && req->result != UV_ECANCELED) {
|
||||
auto response = util::make_unique<Response>();
|
||||
auto response = std::make_unique<Response>();
|
||||
response->status = Response::Error;
|
||||
response->message = uv::getFileRequestError(req);
|
||||
self->notify(std::move(response), FileCache::Hint::No);
|
||||
@@ -208,7 +207,7 @@ void AssetRequest::cancel() {
|
||||
}
|
||||
|
||||
std::unique_ptr<AssetContext> AssetContext::createContext(uv_loop_t*) {
|
||||
return util::make_unique<AssetFSContext>();
|
||||
return std::make_unique<AssetFSContext>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include <mbgl/storage/resource.hpp>
|
||||
#include <mbgl/storage/response.hpp>
|
||||
#include <mbgl/platform/log.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/util/util.hpp>
|
||||
#include <mbgl/util/uv.hpp>
|
||||
|
||||
@@ -178,7 +177,7 @@ void AssetRequest::fileStated(uv_zip_t *zip) {
|
||||
notifyError("Could not determine file size in zip file");
|
||||
cleanup(zip);
|
||||
} else {
|
||||
response = util::make_unique<Response>();
|
||||
response = std::make_unique<Response>();
|
||||
|
||||
// Allocate the space for reading the data.
|
||||
response->data.resize(zip->stat->size);
|
||||
@@ -247,7 +246,7 @@ void AssetRequest::notifyError(const char *message) {
|
||||
MBGL_VERIFY_THREAD(tid);
|
||||
|
||||
if (!cancelled) {
|
||||
response = util::make_unique<Response>();
|
||||
response = std::make_unique<Response>();
|
||||
response->status = Response::Error;
|
||||
response->message = message;
|
||||
notify(std::move(response), FileCache::Hint::No);
|
||||
@@ -261,7 +260,7 @@ void AssetRequest::cancel() {
|
||||
}
|
||||
|
||||
std::unique_ptr<AssetContext> AssetContext::createContext(uv_loop_t* loop) {
|
||||
return util::make_unique<AssetZipContext>(loop);
|
||||
return std::make_unique<AssetZipContext>(loop);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
#include <mbgl/map/still_image.hpp>
|
||||
|
||||
#include <mbgl/util/std.hpp>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
@@ -172,15 +171,15 @@ std::unique_ptr<StillImage> HeadlessView::readStillImage() {
|
||||
const unsigned int w = dimensions.pixelWidth();
|
||||
const unsigned int h = dimensions.pixelHeight();
|
||||
|
||||
auto image = util::make_unique<StillImage>();
|
||||
auto image = std::make_unique<StillImage>();
|
||||
image->width = w;
|
||||
image->height = h;
|
||||
image->pixels = util::make_unique<uint32_t[]>(w * h);
|
||||
image->pixels = std::make_unique<uint32_t[]>(w * h);
|
||||
|
||||
MBGL_CHECK_ERROR(glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, image->pixels.get()));
|
||||
|
||||
const int stride = w * 4;
|
||||
auto tmp = util::make_unique<char[]>(stride);
|
||||
auto tmp = std::make_unique<char[]>(stride);
|
||||
char *rgba = reinterpret_cast<char *>(image->pixels.get());
|
||||
for (int i = 0, j = h - 1; i < j; i++, j--) {
|
||||
std::memcpy(tmp.get(), rgba + i * stride, stride);
|
||||
|
||||
@@ -385,7 +385,7 @@ static CURLcode sslctx_function(CURL * /* curl */, void *sslctx, void * /* parm
|
||||
return CURLE_SSL_CACERT_BADFILE;
|
||||
}
|
||||
|
||||
const auto pem = util::make_unique<char[]>(stat.size);
|
||||
const auto pem = std::make_unique<char[]>(stat.size);
|
||||
|
||||
if (static_cast<zip_uint64_t>(zip_fread(apkFile, reinterpret_cast<void *>(pem.get()), stat.size)) != stat.size) {
|
||||
zip_fclose(apkFile);
|
||||
@@ -537,7 +537,7 @@ size_t HTTPRequest::writeCallback(void *const contents, const size_t size, const
|
||||
MBGL_VERIFY_THREAD(impl->tid);
|
||||
|
||||
if (!impl->response) {
|
||||
impl->response = util::make_unique<Response>();
|
||||
impl->response = std::make_unique<Response>();
|
||||
}
|
||||
|
||||
impl->response->data.append((char *)contents, size * nmemb);
|
||||
@@ -581,7 +581,7 @@ size_t HTTPRequest::headerCallback(char *const buffer, const size_t size, const
|
||||
MBGL_VERIFY_THREAD(baton->tid);
|
||||
|
||||
if (!baton->response) {
|
||||
baton->response = util::make_unique<Response>();
|
||||
baton->response = std::make_unique<Response>();
|
||||
}
|
||||
|
||||
const size_t length = size * nmemb;
|
||||
@@ -675,7 +675,7 @@ void HTTPRequest::handleResult(CURLcode code) {
|
||||
// Make sure a response object exists in case we haven't got any headers
|
||||
// or content.
|
||||
if (!response) {
|
||||
response = util::make_unique<Response>();
|
||||
response = std::make_unique<Response>();
|
||||
}
|
||||
|
||||
// Add human-readable error code
|
||||
@@ -734,7 +734,7 @@ void HTTPRequest::handleResult(CURLcode code) {
|
||||
}
|
||||
|
||||
std::unique_ptr<HTTPContext> HTTPContext::createContext(uv_loop_t* loop) {
|
||||
return util::make_unique<HTTPCURLContext>(loop);
|
||||
return std::make_unique<HTTPCURLContext>(loop);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include <mbgl/util/image.hpp>
|
||||
#include <mbgl/platform/log.hpp>
|
||||
#include <mbgl/util/string.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
|
||||
#include <png.h>
|
||||
|
||||
@@ -82,7 +81,7 @@ Image::Image(std::string const& data)
|
||||
auto reader = getImageReader(data.c_str(), data.size());
|
||||
width = reader->width();
|
||||
height = reader->height();
|
||||
img = util::make_unique<char[]>(width * height * 4);
|
||||
img = std::make_unique<char[]>(width * height * 4);
|
||||
reader->read(0, 0, width, height, img.get());
|
||||
}
|
||||
catch (ImageReaderException const& ex)
|
||||
|
||||
@@ -49,11 +49,11 @@ std::unique_ptr<ImageReader> getImageReader(char const* data, size_t size)
|
||||
{
|
||||
if (*type == "png")
|
||||
{
|
||||
return util::make_unique<PngReader<boost::iostreams::array_source>>(data, size);
|
||||
return std::make_unique<PngReader<boost::iostreams::array_source>>(data, size);
|
||||
}
|
||||
else if (*type == "jpeg")
|
||||
{
|
||||
return util::make_unique<JpegReader<boost::iostreams::array_source>>(data, size);
|
||||
return std::make_unique<JpegReader<boost::iostreams::array_source>>(data, size);
|
||||
}
|
||||
}
|
||||
throw ImageReaderException("ImageReader: can't determine type from input data");
|
||||
|
||||
@@ -62,7 +62,7 @@ std::string unifyMapboxURLs(const std::string &url) {
|
||||
using namespace mapbox::sqlite;
|
||||
|
||||
SQLiteCache::SQLiteCache(const std::string& path_)
|
||||
: thread(util::make_unique<util::Thread<Impl>>("SQLite Cache", util::ThreadPriority::Low, path_)) {
|
||||
: thread(std::make_unique<util::Thread<Impl>>("SQLite Cache", util::ThreadPriority::Low, path_)) {
|
||||
}
|
||||
|
||||
SQLiteCache::~SQLiteCache() = default;
|
||||
@@ -85,7 +85,7 @@ SQLiteCache::Impl::~Impl() {
|
||||
}
|
||||
|
||||
void SQLiteCache::Impl::createDatabase() {
|
||||
db = util::make_unique<Database>(path.c_str(), ReadWrite | Create);
|
||||
db = std::make_unique<Database>(path.c_str(), ReadWrite | Create);
|
||||
}
|
||||
|
||||
void SQLiteCache::Impl::createSchema() {
|
||||
@@ -114,7 +114,7 @@ void SQLiteCache::Impl::createSchema() {
|
||||
} catch (util::IOException& ioEx) {
|
||||
Log::Error(Event::Database, ex.code, ex.what());
|
||||
}
|
||||
db = util::make_unique<Database>(path.c_str(), ReadWrite | Create);
|
||||
db = std::make_unique<Database>(path.c_str(), ReadWrite | Create);
|
||||
} else {
|
||||
Log::Error(Event::Database, ex.code, ex.what());
|
||||
}
|
||||
@@ -146,8 +146,8 @@ std::unique_ptr<Response> SQLiteCache::Impl::get(const Resource &resource) {
|
||||
}
|
||||
|
||||
if (!getStmt) {
|
||||
// Initialize the statement 0 1
|
||||
getStmt = util::make_unique<Statement>(db->prepare("SELECT `status`, `modified`, "
|
||||
// Initialize the statement 0 1
|
||||
getStmt = std::make_unique<Statement>(db->prepare("SELECT `status`, `modified`, "
|
||||
// 2 3 4 5 1
|
||||
"`etag`, `expires`, `data`, `compressed` FROM `http_cache` WHERE `url` = ?"));
|
||||
} else {
|
||||
@@ -158,7 +158,7 @@ std::unique_ptr<Response> SQLiteCache::Impl::get(const Resource &resource) {
|
||||
getStmt->bind(1, unifiedURL.c_str());
|
||||
if (getStmt->run()) {
|
||||
// There is data.
|
||||
auto response = util::make_unique<Response>();
|
||||
auto response = std::make_unique<Response>();
|
||||
response->status = Response::Status(getStmt->get<int>(0));
|
||||
response->modified = getStmt->get<int64_t>(1);
|
||||
response->etag = getStmt->get<std::string>(2);
|
||||
@@ -200,7 +200,7 @@ void SQLiteCache::Impl::put(const Resource& resource, std::shared_ptr<const Resp
|
||||
}
|
||||
|
||||
if (!putStmt) {
|
||||
putStmt = util::make_unique<Statement>(db->prepare("REPLACE INTO `http_cache` ("
|
||||
putStmt = std::make_unique<Statement>(db->prepare("REPLACE INTO `http_cache` ("
|
||||
// 1 2 3 4 5 6 7 8
|
||||
"`url`, `status`, `kind`, `modified`, `etag`, `expires`, `data`, `compressed`"
|
||||
") VALUES(?, ?, ?, ?, ?, ?, ?, ?)"));
|
||||
@@ -249,7 +249,7 @@ void SQLiteCache::Impl::refresh(const Resource& resource, int64_t expires) {
|
||||
}
|
||||
|
||||
if (!refreshStmt) {
|
||||
refreshStmt = util::make_unique<Statement>( // 1 2
|
||||
refreshStmt = std::make_unique<Statement>( // 1 2
|
||||
db->prepare("UPDATE `http_cache` SET `expires` = ? WHERE `url` = ?"));
|
||||
} else {
|
||||
refreshStmt->reset();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "MGLAccountManager.h"
|
||||
#import "MGLAccountManager_Private.h"
|
||||
#import "MGLMapboxEvents.h"
|
||||
#import "NSProcessInfo+MGLAdditions.h"
|
||||
|
||||
@@ -14,6 +14,18 @@
|
||||
|
||||
@implementation MGLAccountManager
|
||||
|
||||
+ (void)load {
|
||||
// Read the initial configuration from Info.plist. The shown-in-app setting
|
||||
// preempts the Settings bundle check in -[MGLMapboxEvents init] triggered
|
||||
// by setting the access token.
|
||||
NSBundle *bundle = [NSBundle mainBundle];
|
||||
NSNumber *shownInAppNumber = [bundle objectForInfoDictionaryKey:@"MGLMapboxMetricsEnabledSettingShownInApp"];
|
||||
if (shownInAppNumber) {
|
||||
[MGLAccountManager sharedManager].mapboxMetricsEnabledSettingShownInApp = [shownInAppNumber boolValue];
|
||||
}
|
||||
self.accessToken = [bundle objectForInfoDictionaryKey:@"MGLMapboxAccessToken"];
|
||||
}
|
||||
|
||||
// Can be called from any thread.
|
||||
//
|
||||
+ (instancetype) sharedManager {
|
||||
@@ -25,7 +37,6 @@
|
||||
void (^setupBlock)() = ^{
|
||||
dispatch_once(&onceToken, ^{
|
||||
_sharedManager = [[self alloc] init];
|
||||
_sharedManager.mapboxMetricsEnabledSettingShownInApp = NO;
|
||||
});
|
||||
};
|
||||
if ( ! [[NSThread currentThread] isMainThread]) {
|
||||
@@ -39,16 +50,14 @@
|
||||
return _sharedManager;
|
||||
}
|
||||
|
||||
+ (void) setMapboxMetricsEnabledSettingShownInApp:(BOOL)shown {
|
||||
[MGLAccountManager sharedManager].mapboxMetricsEnabledSettingShownInApp = shown;
|
||||
}
|
||||
|
||||
+ (BOOL) mapboxMetricsEnabledSettingShownInApp {
|
||||
return [MGLAccountManager sharedManager].mapboxMetricsEnabledSettingShownInApp;
|
||||
}
|
||||
|
||||
+ (void) setAccessToken:(NSString *) accessToken {
|
||||
[[MGLAccountManager sharedManager] setAccessToken:accessToken];
|
||||
if ( ! [accessToken length]) return;
|
||||
|
||||
[MGLAccountManager sharedManager].accessToken = accessToken;
|
||||
|
||||
// Update MGLMapboxEvents
|
||||
// NOTE: This is (likely) the initial setup of MGLMapboxEvents
|
||||
|
||||
10
platform/ios/MGLAccountManager_Private.h
Normal file
10
platform/ios/MGLAccountManager_Private.h
Normal file
@@ -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
|
||||
@@ -24,6 +24,7 @@
|
||||
#import "MGLUserLocationAnnotationView.h"
|
||||
#import "MGLUserLocation_Private.h"
|
||||
#import "MGLFileCache.h"
|
||||
#import "MGLAccountManager_Private.h"
|
||||
#import "MGLMapboxEvents.h"
|
||||
|
||||
#import "SMCalloutView.h"
|
||||
@@ -35,7 +36,7 @@ class MBGLView;
|
||||
NSString *const MGLDefaultStyleName = @"mapbox-streets";
|
||||
NSString *const MGLStyleVersion = @"7";
|
||||
NSString *const MGLDefaultStyleMarkerSymbolName = @"default_marker";
|
||||
NSString *const MGLMapboxAccessTokenManagerURLDisplayString = @"mapbox.com/account/apps";
|
||||
NSString *const MGLMapboxSetupDocumentationURLDisplayString = @"mapbox.com/guides/first-steps-gl-ios";
|
||||
|
||||
const NSTimeInterval MGLAnimationDuration = 0.3;
|
||||
const CGSize MGLAnnotationUpdateViewportOutset = {150, 150};
|
||||
@@ -48,6 +49,16 @@ static NSURL *MGLURLForBundledStyleNamed(NSString *styleName)
|
||||
return [NSURL URLWithString:[NSString stringWithFormat:@"asset://styles/%@.json", styleName]];
|
||||
}
|
||||
|
||||
CGFloat MGLRadiansFromDegrees(CLLocationDegrees degrees)
|
||||
{
|
||||
return degrees * M_PI / 180;
|
||||
}
|
||||
|
||||
CLLocationDegrees MGLDegreesFromRadians(CGFloat radians)
|
||||
{
|
||||
return radians * 180 / M_PI;
|
||||
}
|
||||
|
||||
#pragma mark - Private -
|
||||
|
||||
@interface MGLMapView () <UIGestureRecognizerDelegate, GLKViewDelegate, CLLocationManagerDelegate, UIActionSheetDelegate>
|
||||
@@ -103,63 +114,48 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration)
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
|
||||
if (self && [self commonInit])
|
||||
if (self = [super initWithFrame:frame])
|
||||
{
|
||||
[self commonInit];
|
||||
self.styleURL = nil;
|
||||
self.accessToken = [MGLAccountManager accessToken];
|
||||
return self;
|
||||
}
|
||||
|
||||
return nil;
|
||||
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<uint32_t> nearbyAnnotations = _mbglMap->getAnnotationsInBounds(tapBounds);
|
||||
@@ -1270,10 +1272,25 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration)
|
||||
[[UIApplication sharedApplication] openURL:
|
||||
[NSURL URLWithString:feedbackURL]];
|
||||
}
|
||||
// skips to 4 because button is conditionally added after cancel (index 3)
|
||||
else if (buttonIndex == actionSheet.firstOtherButtonIndex + 4)
|
||||
{
|
||||
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Properties -
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(__unused void *)context
|
||||
{
|
||||
// Synchronize mbgl::Map’s access token with the global one in MGLAccountManager.
|
||||
if ([keyPath isEqualToString:@"accessToken"] && object == [MGLAccountManager sharedManager])
|
||||
{
|
||||
NSString *accessToken = change[NSKeyValueChangeNewKey];
|
||||
_mbglFileSource->setAccessToken(accessToken ? (std::string)[accessToken UTF8String] : "");
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingZoomEnabled
|
||||
{
|
||||
return [NSSet setWithObject:@"allowsZooming"];
|
||||
@@ -1361,7 +1378,7 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration)
|
||||
{
|
||||
CGFloat duration = (animated ? MGLAnimationDuration : 0);
|
||||
|
||||
_mbglMap->setLatLngZoom(coordinateToLatLng(coordinate),
|
||||
_mbglMap->setLatLngZoom(MGLLatLngFromLocationCoordinate2D(coordinate),
|
||||
fmaxf(_mbglMap->getZoom(), self.currentMinimumZoom),
|
||||
secondsAsDuration(duration));
|
||||
|
||||
@@ -1375,7 +1392,7 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration)
|
||||
|
||||
- (CLLocationCoordinate2D)centerCoordinate
|
||||
{
|
||||
return latLngToCoordinate(_mbglMap->getLatLng());
|
||||
return MGLLocationCoordinate2DFromLatLng(_mbglMap->getLatLng());
|
||||
}
|
||||
|
||||
- (void)setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate zoomLevel:(double)zoomLevel animated:(BOOL)animated
|
||||
@@ -1384,7 +1401,7 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration)
|
||||
|
||||
CGFloat duration = (animated ? MGLAnimationDuration : 0);
|
||||
|
||||
_mbglMap->setLatLngZoom(coordinateToLatLng(centerCoordinate), zoomLevel, secondsAsDuration(duration));
|
||||
_mbglMap->setLatLngZoom(MGLLatLngFromLocationCoordinate2D(centerCoordinate), zoomLevel, secondsAsDuration(duration));
|
||||
|
||||
[self unrotateIfNeededAnimated:animated];
|
||||
|
||||
@@ -1468,12 +1485,12 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration)
|
||||
//
|
||||
convertedPoint.y = self.bounds.size.height - convertedPoint.y;
|
||||
|
||||
return latLngToCoordinate(_mbglMap->latLngForPixel(mbgl::vec2<double>(convertedPoint.x, convertedPoint.y)));
|
||||
return MGLLocationCoordinate2DFromLatLng(_mbglMap->latLngForPixel(mbgl::vec2<double>(convertedPoint.x, convertedPoint.y)));
|
||||
}
|
||||
|
||||
- (CGPoint)convertCoordinate:(CLLocationCoordinate2D)coordinate toPointToView:(UIView *)view
|
||||
{
|
||||
mbgl::vec2<double> pixel = _mbglMap->pixelForLatLng(coordinateToLatLng(coordinate));
|
||||
mbgl::vec2<double> pixel = _mbglMap->pixelForLatLng(MGLLatLngFromLocationCoordinate2D(coordinate));
|
||||
|
||||
// flip y coordinate for iOS view origin in top left
|
||||
//
|
||||
@@ -1487,12 +1504,12 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration)
|
||||
return _mbglMap->getMetersPerPixelAtLatitude(latitude, self.zoomLevel);
|
||||
}
|
||||
|
||||
mbgl::LatLng coordinateToLatLng(CLLocationCoordinate2D coordinate)
|
||||
mbgl::LatLng MGLLatLngFromLocationCoordinate2D(CLLocationCoordinate2D coordinate)
|
||||
{
|
||||
return mbgl::LatLng(coordinate.latitude, coordinate.longitude);
|
||||
}
|
||||
|
||||
CLLocationCoordinate2D latLngToCoordinate(mbgl::LatLng latLng)
|
||||
CLLocationCoordinate2D MGLLocationCoordinate2DFromLatLng(mbgl::LatLng latLng)
|
||||
{
|
||||
return CLLocationCoordinate2DMake(latLng.latitude, latLng.longitude);
|
||||
}
|
||||
@@ -1501,13 +1518,13 @@ CLLocationCoordinate2D latLngToCoordinate(mbgl::LatLng latLng)
|
||||
{
|
||||
mbgl::LatLngBounds bounds;
|
||||
|
||||
bounds.extend(coordinateToLatLng(
|
||||
bounds.extend(MGLLatLngFromLocationCoordinate2D(
|
||||
[self convertPoint:CGPointMake(0, 0) toCoordinateFromView:self]));
|
||||
bounds.extend(coordinateToLatLng(
|
||||
bounds.extend(MGLLatLngFromLocationCoordinate2D(
|
||||
[self convertPoint:CGPointMake(self.bounds.size.width, 0) toCoordinateFromView:self]));
|
||||
bounds.extend(coordinateToLatLng(
|
||||
bounds.extend(MGLLatLngFromLocationCoordinate2D(
|
||||
[self convertPoint:CGPointMake(0, self.bounds.size.height) toCoordinateFromView:self]));
|
||||
bounds.extend(coordinateToLatLng(
|
||||
bounds.extend(MGLLatLngFromLocationCoordinate2D(
|
||||
[self convertPoint:CGPointMake(self.bounds.size.width, self.bounds.size.height) toCoordinateFromView:self]));
|
||||
|
||||
return bounds;
|
||||
@@ -1533,27 +1550,31 @@ CLLocationCoordinate2D latLngToCoordinate(mbgl::LatLng latLng)
|
||||
return [NSArray arrayWithArray:_bundledStyleURLs];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingMapID
|
||||
+ (NSSet *)keyPathsForValuesAffectingStyleID
|
||||
{
|
||||
return [NSSet setWithObjects:@"styleURL", @"accessToken", nil];
|
||||
return [NSSet setWithObject:@"styleURL"];
|
||||
}
|
||||
|
||||
- (NSString *)mapID
|
||||
- (NSString *)styleID
|
||||
{
|
||||
NSURL *styleURL = self.styleURL;
|
||||
return [styleURL.scheme isEqualToString:@"mapbox"] ? styleURL.host.mgl_stringOrNilIfEmpty : nil;
|
||||
}
|
||||
|
||||
- (void)setStyleID:(NSString *)styleID
|
||||
{
|
||||
self.styleURL = styleID ? [NSURL URLWithString:[NSString stringWithFormat:@"mapbox://%@", styleID]] : nil;
|
||||
}
|
||||
|
||||
- (NSString *)mapID
|
||||
{
|
||||
NSAssert(NO, @"-[MGLMapView mapID] has been renamed -[MGLMapView styleID].");
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)setMapID:(NSString *)mapID
|
||||
{
|
||||
if (mapID)
|
||||
{
|
||||
self.styleURL = [NSURL URLWithString:[NSString stringWithFormat:@"mapbox://%@", mapID]];
|
||||
}
|
||||
else
|
||||
{
|
||||
self.styleURL = nil;
|
||||
}
|
||||
NSAssert(NO, @"-[MGLMapView setMapID:] has been renamed -[MGLMapView setStyleID:].\n\nIf you previously set this map ID in a storyboard inspectable, select the MGLMapView in Interface Builder and delete the “mapID” entry from the User Defined Runtime Attributes section of the Identity inspector. Then go to the Attributes inspector and enter “%@” into the “Style ID” field.", mapID);
|
||||
}
|
||||
|
||||
- (NSArray *)styleClasses
|
||||
@@ -1657,7 +1678,7 @@ CLLocationCoordinate2D latLngToCoordinate(mbgl::LatLng latLng)
|
||||
{
|
||||
assert([annotation conformsToProtocol:@protocol(MGLAnnotation)]);
|
||||
|
||||
latLngs.push_back(coordinateToLatLng(annotation.coordinate));
|
||||
latLngs.push_back(MGLLatLngFromLocationCoordinate2D(annotation.coordinate));
|
||||
|
||||
NSString *symbolName = nil;
|
||||
|
||||
@@ -1726,7 +1747,7 @@ CLLocationCoordinate2D latLngToCoordinate(mbgl::LatLng latLng)
|
||||
|
||||
assert([firstAnnotation conformsToProtocol:@protocol(MGLAnnotation)]);
|
||||
|
||||
if ( ! [self viewportBounds].contains(coordinateToLatLng(firstAnnotation.coordinate))) return;
|
||||
if ( ! [self viewportBounds].contains(MGLLatLngFromLocationCoordinate2D(firstAnnotation.coordinate))) return;
|
||||
|
||||
[self selectAnnotation:firstAnnotation animated:NO];
|
||||
}
|
||||
@@ -1735,7 +1756,7 @@ CLLocationCoordinate2D latLngToCoordinate(mbgl::LatLng latLng)
|
||||
{
|
||||
if ( ! annotation) return;
|
||||
|
||||
if ( ! [self viewportBounds].contains(coordinateToLatLng(annotation.coordinate))) return;
|
||||
if ( ! [self viewportBounds].contains(MGLLatLngFromLocationCoordinate2D(annotation.coordinate))) return;
|
||||
|
||||
if (annotation == self.selectedAnnotation) return;
|
||||
|
||||
@@ -2171,16 +2192,6 @@ CLLocationCoordinate2D latLngToCoordinate(mbgl::LatLng latLng)
|
||||
|
||||
#pragma mark - Utility -
|
||||
|
||||
+ (CGFloat)degreesToRadians:(CGFloat)degrees
|
||||
{
|
||||
return degrees * M_PI / 180;
|
||||
}
|
||||
|
||||
+ (CGFloat)radiansToDegrees:(CGFloat)radians
|
||||
{
|
||||
return radians * 180 / M_PI;
|
||||
}
|
||||
|
||||
- (void)animateWithDelay:(NSTimeInterval)delay animations:(void (^)(void))animations
|
||||
{
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), animations);
|
||||
@@ -2398,7 +2409,7 @@ CLLocationCoordinate2D latLngToCoordinate(mbgl::LatLng latLng)
|
||||
while (degrees >= 360) degrees -= 360;
|
||||
while (degrees < 0) degrees += 360;
|
||||
|
||||
self.compass.transform = CGAffineTransformMakeRotation([MGLMapView degreesToRadians:degrees]);
|
||||
self.compass.transform = CGAffineTransformMakeRotation(MGLRadiansFromDegrees(degrees));
|
||||
|
||||
if (_mbglMap->getBearing() && self.compass.alpha < 1)
|
||||
{
|
||||
@@ -2431,14 +2442,14 @@ CLLocationCoordinate2D latLngToCoordinate(mbgl::LatLng latLng)
|
||||
imageName = [imageName stringByAppendingString:@".png"];
|
||||
}
|
||||
|
||||
return [UIImage imageWithContentsOfFile:[MGLMapView pathForBundleResourceNamed:imageName ofType:nil inDirectory:@""]];
|
||||
return [UIImage imageWithContentsOfFile:[self pathForBundleResourceNamed:imageName ofType:nil inDirectory:@""]];
|
||||
}
|
||||
|
||||
+ (NSString *)pathForBundleResourceNamed:(NSString *)name ofType:(NSString *)extension inDirectory:(NSString *)directory
|
||||
{
|
||||
NSString *path = [[NSBundle bundleWithPath:[NSBundle mgl_resourceBundlePath]] pathForResource:name ofType:extension inDirectory:directory];
|
||||
|
||||
NSAssert(path, @"Resource not found in application.");
|
||||
NSAssert(path, @"Resource %@ not found in application.", name);
|
||||
|
||||
return path;
|
||||
}
|
||||
@@ -2461,129 +2472,106 @@ CLLocationCoordinate2D latLngToCoordinate(mbgl::LatLng latLng)
|
||||
{
|
||||
[super prepareForInterfaceBuilder];
|
||||
|
||||
self.layer.borderColor = [UIColor colorWithWhite:184/255. alpha:1].CGColor;
|
||||
self.layer.borderWidth = 1;
|
||||
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
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
extern NSString *const MGLEventTypeAppUserTurnstile;
|
||||
extern NSString *const MGLEventTypeMapLoad;
|
||||
extern NSString *const MGLEventTypeMapTap;
|
||||
extern NSString *const MGLEventTypeMapDragEnd;
|
||||
|
||||
@@ -13,9 +13,11 @@
|
||||
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
static const NSUInteger version = 1;
|
||||
static NSString *const MGLMapboxEventsUserAgent = @"MapboxEventsiOS/1.0";
|
||||
static NSString *MGLMapboxEventsAPIBase = @"https://api.tiles.mapbox.com";
|
||||
|
||||
NSString *const MGLEventTypeAppUserTurnstile = @"appUserTurnstile";
|
||||
NSString *const MGLEventTypeMapLoad = @"map.load";
|
||||
NSString *const MGLEventTypeMapTap = @"map.click";
|
||||
NSString *const MGLEventTypeMapDragEnd = @"map.dragend";
|
||||
@@ -52,7 +54,6 @@ const NSTimeInterval MGLFlushInterval = 60;
|
||||
// All of the following properties are written to only from
|
||||
// the main thread, but can be read on any thread.
|
||||
//
|
||||
@property (atomic) NSString *instanceID;
|
||||
@property (atomic) NSString *advertiserId;
|
||||
@property (atomic) NSString *vendorId;
|
||||
@property (atomic) NSString *model;
|
||||
@@ -66,8 +67,7 @@ const NSTimeInterval MGLFlushInterval = 60;
|
||||
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
_instanceID = [[NSUUID UUID] UUIDString];
|
||||
|
||||
|
||||
// Dynamic detection of ASIdentifierManager from Mixpanel
|
||||
// https://github.com/mixpanel/mixpanel-iphone/blob/master/LICENSE
|
||||
_advertiserId = @"";
|
||||
@@ -143,6 +143,7 @@ const NSTimeInterval MGLFlushInterval = 60;
|
||||
@property (atomic) NSString *appName;
|
||||
@property (atomic) NSString *appVersion;
|
||||
@property (atomic) NSString *appBuildNumber;
|
||||
@property (atomic) NSString *instanceID;
|
||||
@property (atomic) NSDateFormatter *rfc3339DateFormatter;
|
||||
@property (atomic) NSURLSession *session;
|
||||
@property (atomic) NSData *digicertCert;
|
||||
@@ -176,8 +177,10 @@ const NSTimeInterval MGLFlushInterval = 60;
|
||||
|
||||
+ (void)initialize {
|
||||
if (self == [MGLMapboxEvents class]) {
|
||||
NSBundle *bundle = [NSBundle mainBundle];
|
||||
NSNumber *accountTypeNumber = [bundle objectForInfoDictionaryKey:@"MGLMapboxAccountType"];
|
||||
[[NSUserDefaults standardUserDefaults] registerDefaults:@{
|
||||
@"MGLMapboxAccountType": @0,
|
||||
@"MGLMapboxAccountType": accountTypeNumber ? accountTypeNumber : @0,
|
||||
@"MGLMapboxMetricsEnabled": @YES,
|
||||
}];
|
||||
}
|
||||
@@ -195,7 +198,8 @@ const NSTimeInterval MGLFlushInterval = 60;
|
||||
|
||||
self = [super init];
|
||||
if (self) {
|
||||
if (! [MGLAccountManager mapboxMetricsEnabledSettingShownInApp]) {
|
||||
if (! [MGLAccountManager mapboxMetricsEnabledSettingShownInApp] &&
|
||||
[[NSUserDefaults standardUserDefaults] integerForKey:@"MGLMapboxAccountType"] == 0) {
|
||||
// Opt Out is not configured in UI, so check for Settings.bundle
|
||||
// Put Settings bundle into memory
|
||||
id defaultEnabledValue;
|
||||
@@ -213,7 +217,7 @@ const NSTimeInterval MGLFlushInterval = 60;
|
||||
}
|
||||
}
|
||||
|
||||
NSAssert(defaultEnabledValue, @"End users must be able to opt out of Metrics in your app, either inside Settings (via Settings.bundle) or inside this app. If you implement the opt-out control inside this app, disable this assertion by setting [MGLAccountManager setMapboxMetricsEnabledSettingShownInApp:YES] before the app initializes any Mapbox GL classes.");
|
||||
NSAssert(defaultEnabledValue, @"End users must be able to opt out of Metrics in your app, either inside Settings (via Settings.bundle) or inside this app. If you implement the opt-out control inside this app, disable this assertion by setting MGLMapboxMetricsEnabledSettingShownInApp to YES in Info.plist.");
|
||||
[[NSUserDefaults standardUserDefaults] registerDefaults:@{
|
||||
@"MGLMapboxMetricsEnabled": defaultEnabledValue,
|
||||
}];
|
||||
@@ -223,7 +227,8 @@ const NSTimeInterval MGLFlushInterval = 60;
|
||||
_appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"];
|
||||
_appVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
|
||||
_appBuildNumber = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
|
||||
|
||||
_instanceID = [[NSUUID UUID] UUIDString];
|
||||
|
||||
NSString *uniqueID = [[NSProcessInfo processInfo] globallyUniqueString];
|
||||
_serialQueue = dispatch_queue_create([[NSString stringWithFormat:@"%@.%@.events.serial", _appBundleId, uniqueID] UTF8String], DISPATCH_QUEUE_SERIAL);
|
||||
|
||||
@@ -277,6 +282,11 @@ const NSTimeInterval MGLFlushInterval = 60;
|
||||
MGLMapboxEvents *strongSelf = weakSelf;
|
||||
[strongSelf validate];
|
||||
}];
|
||||
|
||||
// Turn the Mapbox Turnstile to Count App Users
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
|
||||
[self pushTurnstileEvent];
|
||||
});
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -315,6 +325,7 @@ const NSTimeInterval MGLFlushInterval = 60;
|
||||
[[MGLMapboxEvents sharedManager] validate];
|
||||
}
|
||||
|
||||
// Used to determine if Mapbox Metrics should be collected at any given point in time
|
||||
- (void)validate {
|
||||
MGLAssertIsMainThread();
|
||||
BOOL enabledInSettings = [[self class] isEnabled];
|
||||
@@ -414,6 +425,33 @@ const NSTimeInterval MGLFlushInterval = 60;
|
||||
}
|
||||
}
|
||||
|
||||
- (void) pushTurnstileEvent {
|
||||
|
||||
__weak MGLMapboxEvents *weakSelf = self;
|
||||
|
||||
dispatch_async(_serialQueue, ^{
|
||||
|
||||
MGLMapboxEvents *strongSelf = weakSelf;
|
||||
|
||||
if ( ! strongSelf) return;
|
||||
|
||||
// Build only IDFV event
|
||||
NSString *vid = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
|
||||
NSDictionary *vevt = @{@"event" : MGLEventTypeAppUserTurnstile,
|
||||
@"created" : [strongSelf.rfc3339DateFormatter stringFromDate:[NSDate date]],
|
||||
@"appBundleId" : strongSelf.appBundleId,
|
||||
@"vendorId": vid,
|
||||
@"version": @(version),
|
||||
@"instance": strongSelf.instanceID};
|
||||
|
||||
// Add to Queue
|
||||
[_eventQueue addObject:vevt];
|
||||
|
||||
// Flush
|
||||
[strongSelf flush];
|
||||
});
|
||||
}
|
||||
|
||||
// Can be called from any thread. Can be called rapidly from
|
||||
// the UI thread, so performance is paramount.
|
||||
//
|
||||
@@ -430,9 +468,11 @@ const NSTimeInterval MGLFlushInterval = 60;
|
||||
__weak MGLMapboxEvents *weakSelf = self;
|
||||
|
||||
dispatch_async(_serialQueue, ^{
|
||||
|
||||
MGLMapboxEvents *strongSelf = weakSelf;
|
||||
|
||||
if ( ! strongSelf) return;
|
||||
|
||||
|
||||
// Metrics Collection Has Been Paused
|
||||
if (_paused) {
|
||||
return;
|
||||
@@ -443,9 +483,9 @@ const NSTimeInterval MGLFlushInterval = 60;
|
||||
NSMutableDictionary *evt = [[NSMutableDictionary alloc] initWithDictionary:attributeDictionary];
|
||||
// mapbox-events stock attributes
|
||||
[evt setObject:event forKey:@"event"];
|
||||
[evt setObject:@(1) forKey:@"version"];
|
||||
[evt setObject:@(version) forKey:@"version"];
|
||||
[evt setObject:[strongSelf.rfc3339DateFormatter stringFromDate:[NSDate date]] forKey:@"created"];
|
||||
[evt setObject:strongSelf.data.instanceID forKey:@"instance"];
|
||||
[evt setObject:strongSelf.instanceID forKey:@"instance"];
|
||||
[evt setObject:strongSelf.data.advertiserId forKey:@"advertiserId"];
|
||||
[evt setObject:strongSelf.data.vendorId forKey:@"vendorId"];
|
||||
[evt setObject:strongSelf.appBundleId forKeyedSubscript:@"appBundleId"];
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#include <mbgl/geometry/glyph_atlas.hpp>
|
||||
|
||||
#include <mbgl/text/font_stack.hpp>
|
||||
|
||||
#include <mbgl/platform/gl.hpp>
|
||||
#include <mbgl/platform/log.hpp>
|
||||
#include <mbgl/platform/platform.hpp>
|
||||
|
||||
#include <mbgl/util/std.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
|
||||
@@ -16,7 +16,7 @@ GlyphAtlas::GlyphAtlas(uint16_t width_, uint16_t height_)
|
||||
: width(width_),
|
||||
height(height_),
|
||||
bin(width_, height_),
|
||||
data(util::make_unique<uint8_t[]>(width_ * height_)),
|
||||
data(std::make_unique<uint8_t[]>(width_ * height_)),
|
||||
dirty(true) {
|
||||
}
|
||||
|
||||
@@ -69,8 +69,9 @@ Rect<uint16_t> GlyphAtlas::addGlyph(uintptr_t tileUID,
|
||||
uint16_t buffered_height = glyph.metrics.height + buffer * 2;
|
||||
|
||||
// Add a 1px border around every image.
|
||||
uint16_t pack_width = buffered_width;
|
||||
uint16_t pack_height = buffered_height;
|
||||
const uint16_t padding = 1;
|
||||
uint16_t pack_width = buffered_width + 2 * padding;
|
||||
uint16_t pack_height = buffered_height + 2 * padding;
|
||||
|
||||
// Increase to next number divisible by 4, but at least 1.
|
||||
// This is so we can scale down the texture coordinates and pack them
|
||||
@@ -92,7 +93,7 @@ Rect<uint16_t> GlyphAtlas::addGlyph(uintptr_t tileUID,
|
||||
// Copy the bitmap
|
||||
const uint8_t* source = reinterpret_cast<const uint8_t*>(glyph.bitmap.data());
|
||||
for (uint32_t y = 0; y < buffered_height; y++) {
|
||||
uint32_t y1 = width * (rect.y + y) + rect.x;
|
||||
uint32_t y1 = width * (rect.y + y + padding) + rect.x + padding;
|
||||
uint32_t y2 = buffered_width * y;
|
||||
for (uint32_t x = 0; x < buffered_width; x++) {
|
||||
data[y1 + x] = source[y2 + x];
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include <mbgl/platform/gl.hpp>
|
||||
#include <mbgl/platform/log.hpp>
|
||||
#include <mbgl/platform/platform.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
|
||||
#include <boost/functional/hash.hpp>
|
||||
|
||||
@@ -15,7 +14,7 @@ using namespace mbgl;
|
||||
LineAtlas::LineAtlas(uint16_t w, uint16_t h)
|
||||
: width(w),
|
||||
height(h),
|
||||
data(util::make_unique<uint8_t[]>(w * h)),
|
||||
data(std::make_unique<uint8_t[]>(w * h)),
|
||||
dirty(true) {
|
||||
}
|
||||
|
||||
|
||||
@@ -127,10 +127,12 @@ SpriteAtlasPosition SpriteAtlas::getPosition(const std::string& name, bool repea
|
||||
rect.h = pos.height / pos.pixelRatio;
|
||||
}
|
||||
|
||||
const float padding = 1;
|
||||
|
||||
return SpriteAtlasPosition {
|
||||
{{ float(rect.w), float(rect.h) }},
|
||||
{{ float(rect.x) / width, float(rect.y) / height }},
|
||||
{{ float(rect.x + rect.w) / width, float(rect.y + rect.h) / height }}
|
||||
{{ float(rect.x + padding) / width, float(rect.y + padding) / height }},
|
||||
{{ float(rect.x + padding + rect.w) / width, float(rect.y + padding + rect.h) / height }}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -138,7 +140,7 @@ void SpriteAtlas::allocate() {
|
||||
if (!data) {
|
||||
dimension w = static_cast<dimension>(width * pixelRatio);
|
||||
dimension h = static_cast<dimension>(height * pixelRatio);
|
||||
data = util::make_unique<uint32_t[]>(w * h);
|
||||
data = std::make_unique<uint32_t[]>(w * h);
|
||||
std::fill(data.get(), data.get() + w * h, 0);
|
||||
}
|
||||
}
|
||||
@@ -151,12 +153,14 @@ void SpriteAtlas::copy(const Rect<dimension>& dst, const SpritePosition& src, co
|
||||
const vec2<uint32_t> srcSize { sprite->raster->getWidth(), sprite->raster->getHeight() };
|
||||
const Rect<uint32_t> srcPos { src.x, src.y, src.width, src.height };
|
||||
|
||||
const int offset = 1;
|
||||
|
||||
allocate();
|
||||
uint32_t *const dstData = data.get();
|
||||
const vec2<uint32_t> dstSize { static_cast<unsigned int>(width * pixelRatio),
|
||||
static_cast<unsigned int>(height * pixelRatio) };
|
||||
const Rect<uint32_t> dstPos { static_cast<uint32_t>(dst.x * pixelRatio),
|
||||
static_cast<uint32_t>(dst.y * pixelRatio),
|
||||
const Rect<uint32_t> dstPos { static_cast<uint32_t>((offset + dst.x) * pixelRatio),
|
||||
static_cast<uint32_t>((offset + dst.y) * pixelRatio),
|
||||
static_cast<uint32_t>(dst.originalW * pixelRatio),
|
||||
static_cast<uint32_t>(dst.originalH * pixelRatio) };
|
||||
|
||||
@@ -295,6 +299,8 @@ void SpriteAtlas::bind(bool linear) {
|
||||
|
||||
SpriteAtlas::~SpriteAtlas() {
|
||||
std::lock_guard<std::recursive_mutex> lock(mtx);
|
||||
Environment::Get().abandonTexture(texture);
|
||||
texture = 0;
|
||||
if (texture) {
|
||||
Environment::Get().abandonTexture(texture);
|
||||
texture = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <mbgl/map/map_data.hpp>
|
||||
#include <mbgl/util/constants.hpp>
|
||||
#include <mbgl/util/ptr.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
@@ -110,7 +109,7 @@ AnnotationManager::addPointAnnotations(const std::vector<LatLng>& points,
|
||||
// track the annotation global ID and its geometry
|
||||
auto anno_it = annotations.emplace(
|
||||
annotationID,
|
||||
util::make_unique<Annotation>(AnnotationType::Point,
|
||||
std::make_unique<Annotation>(AnnotationType::Point,
|
||||
AnnotationSegments({ { points[i] } })));
|
||||
|
||||
const uint8_t maxZoom = data.transform.getMaxZoom();
|
||||
@@ -162,7 +161,7 @@ AnnotationManager::addPointAnnotations(const std::vector<LatLng>& points,
|
||||
// create tile & record annotation association
|
||||
auto tile_pos = tiles.emplace(
|
||||
tileID, std::make_pair(std::unordered_set<uint32_t>({ annotationID }),
|
||||
util::make_unique<LiveTile>()));
|
||||
std::make_unique<LiveTile>()));
|
||||
// add point layer to tile
|
||||
tile_pos.first->second.second->addLayer(layerID, layer);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include <mbgl/map/tile_id.hpp>
|
||||
#include <mbgl/util/geo.hpp>
|
||||
#include <mbgl/util/noncopyable.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/util/vec.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -3,15 +3,14 @@
|
||||
#include <mbgl/map/view.hpp>
|
||||
#include <mbgl/map/map_data.hpp>
|
||||
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/util/projection.hpp>
|
||||
#include <mbgl/util/thread.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
Map::Map(View& view, FileSource& fileSource, MapMode mode)
|
||||
: data(util::make_unique<MapData>(view, mode)),
|
||||
context(util::make_unique<util::Thread<MapContext>>("Map", util::ThreadPriority::Regular, view, fileSource, *data))
|
||||
: data(std::make_unique<MapData>(view, mode)),
|
||||
context(std::make_unique<util::Thread<MapContext>>("Map", util::ThreadPriority::Regular, view, fileSource, *data))
|
||||
{
|
||||
view.initialize(this);
|
||||
}
|
||||
@@ -195,17 +194,6 @@ void Map::resetNorth() {
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Access Token
|
||||
|
||||
void Map::setAccessToken(const std::string &token) {
|
||||
data->setAccessToken(token);
|
||||
}
|
||||
|
||||
std::string Map::getAccessToken() const {
|
||||
return data->getAccessToken();
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Projection
|
||||
|
||||
void Map::getWorldBoundsMeters(ProjectedMeters& sw, ProjectedMeters& ne) const {
|
||||
|
||||
@@ -20,11 +20,9 @@
|
||||
|
||||
#include <mbgl/style/style.hpp>
|
||||
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/util/uv_detail.hpp>
|
||||
#include <mbgl/util/worker.hpp>
|
||||
#include <mbgl/util/texture_pool.hpp>
|
||||
#include <mbgl/util/mapbox.hpp>
|
||||
#include <mbgl/util/exception.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
@@ -35,12 +33,12 @@ MapContext::MapContext(uv_loop_t* loop, View& view_, FileSource& fileSource, Map
|
||||
env(fileSource),
|
||||
envScope(env, ThreadType::Map, "Map"),
|
||||
updated(static_cast<UpdateType>(Update::Nothing)),
|
||||
asyncUpdate(util::make_unique<uv::async>(loop, [this] { update(); })),
|
||||
glyphStore(util::make_unique<GlyphStore>(loop, env)),
|
||||
glyphAtlas(util::make_unique<GlyphAtlas>(1024, 1024)),
|
||||
spriteAtlas(util::make_unique<SpriteAtlas>(512, 512)),
|
||||
lineAtlas(util::make_unique<LineAtlas>(512, 512)),
|
||||
texturePool(util::make_unique<TexturePool>()) {
|
||||
asyncUpdate(std::make_unique<uv::async>(loop, [this] { update(); })),
|
||||
glyphStore(std::make_unique<GlyphStore>(loop, env)),
|
||||
glyphAtlas(std::make_unique<GlyphAtlas>(1024, 1024)),
|
||||
spriteAtlas(std::make_unique<SpriteAtlas>(512, 512)),
|
||||
lineAtlas(std::make_unique<LineAtlas>(512, 512)),
|
||||
texturePool(std::make_unique<TexturePool>()) {
|
||||
assert(Environment::currentlyOn(ThreadType::Map));
|
||||
|
||||
asyncUpdate->unref();
|
||||
@@ -90,7 +88,7 @@ void MapContext::triggerUpdate(const Update u) {
|
||||
}
|
||||
|
||||
void MapContext::setStyleURL(const std::string& url) {
|
||||
styleURL = mbgl::util::mapbox::normalizeStyleURL(url, data.getAccessToken());
|
||||
styleURL = url;
|
||||
styleJSON.clear();
|
||||
|
||||
const size_t pos = styleURL.rfind('/');
|
||||
@@ -99,7 +97,7 @@ void MapContext::setStyleURL(const std::string& url) {
|
||||
base = styleURL.substr(0, pos + 1);
|
||||
}
|
||||
|
||||
env.request({ Resource::Kind::JSON, styleURL }, [this, base](const Response &res) {
|
||||
env.request({ Resource::Kind::Style, styleURL }, [this, base](const Response &res) {
|
||||
if (res.status == Response::Successful) {
|
||||
loadStyleJSON(res.data, base);
|
||||
} else {
|
||||
@@ -121,17 +119,15 @@ void MapContext::loadStyleJSON(const std::string& json, const std::string& base)
|
||||
resourceLoader.reset();
|
||||
style.reset();
|
||||
|
||||
style = util::make_unique<Style>();
|
||||
style = std::make_unique<Style>();
|
||||
style->base = base;
|
||||
style->loadJSON((const uint8_t *)json.c_str());
|
||||
style->cascade(data.getClasses());
|
||||
style->setDefaultTransitionDuration(data.getDefaultTransitionDuration());
|
||||
|
||||
const std::string glyphURL = util::mapbox::normalizeGlyphsURL(style->glyph_url, data.getAccessToken());
|
||||
glyphStore->setURL(glyphURL);
|
||||
glyphStore->setURL(style->glyph_url);
|
||||
|
||||
resourceLoader = util::make_unique<ResourceLoader>();
|
||||
resourceLoader->setAccessToken(data.getAccessToken());
|
||||
resourceLoader = std::make_unique<ResourceLoader>();
|
||||
resourceLoader->setObserver(this);
|
||||
resourceLoader->setStyle(style.get());
|
||||
resourceLoader->setGlyphStore(glyphStore.get());
|
||||
@@ -224,7 +220,7 @@ void MapContext::render() {
|
||||
assert(style);
|
||||
|
||||
if (!painter) {
|
||||
painter = util::make_unique<Painter>(*spriteAtlas, *glyphAtlas, *lineAtlas);
|
||||
painter = std::make_unique<Painter>(*spriteAtlas, *glyphAtlas, *lineAtlas);
|
||||
painter->setup();
|
||||
}
|
||||
|
||||
@@ -232,7 +228,7 @@ void MapContext::render() {
|
||||
painter->render(*style, transformState, data.getAnimationTime());
|
||||
|
||||
if (data.mode == MapMode::Still) {
|
||||
callback(view.readStillImage());
|
||||
callback(nullptr, view.readStillImage());
|
||||
callback = nullptr;
|
||||
}
|
||||
|
||||
@@ -274,4 +270,13 @@ void MapContext::onTileDataChanged() {
|
||||
triggerUpdate();
|
||||
}
|
||||
|
||||
void MapContext::onResourceLoadingFailed(std::exception_ptr error) {
|
||||
assert(Environment::currentlyOn(ThreadType::Map));
|
||||
|
||||
if (data.mode == MapMode::Still && callback) {
|
||||
callback(error, nullptr);
|
||||
callback = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ public:
|
||||
|
||||
void resize(uint16_t width, uint16_t height, float ratio);
|
||||
|
||||
using StillImageCallback = std::function<void(std::unique_ptr<const StillImage>)>;
|
||||
using StillImageCallback = std::function<void(std::exception_ptr, std::unique_ptr<const StillImage>)>;
|
||||
void renderStill(StillImageCallback callback);
|
||||
|
||||
void triggerUpdate(Update = Update::Nothing);
|
||||
@@ -61,6 +61,7 @@ public:
|
||||
|
||||
// ResourceLoader::Observer implementation.
|
||||
void onTileDataChanged() override;
|
||||
void onResourceLoadingFailed(std::exception_ptr error) override;
|
||||
|
||||
private:
|
||||
void updateTiles();
|
||||
|
||||
@@ -27,15 +27,6 @@ public:
|
||||
setDefaultTransitionDuration(Duration::zero());
|
||||
}
|
||||
|
||||
inline std::string getAccessToken() const {
|
||||
Lock lock(mtx);
|
||||
return accessToken;
|
||||
}
|
||||
inline void setAccessToken(const std::string &token) {
|
||||
Lock lock(mtx);
|
||||
accessToken = token;
|
||||
}
|
||||
|
||||
// Adds the class if it's not yet set. Returns true when it added the class, and false when it
|
||||
// was already present.
|
||||
bool addClass(const std::string& klass);
|
||||
@@ -105,7 +96,6 @@ public:
|
||||
private:
|
||||
mutable std::mutex mtx;
|
||||
|
||||
std::string accessToken;
|
||||
std::vector<std::string> classes;
|
||||
std::atomic<uint8_t> debug { false };
|
||||
std::atomic<uint8_t> collisionDebug { false };
|
||||
|
||||
@@ -45,7 +45,7 @@ void ResourceLoader::setStyle(Style* style) {
|
||||
|
||||
for (const auto& source : style->sources) {
|
||||
source->setObserver(this);
|
||||
source->load(accessToken_);
|
||||
source->load();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,11 +60,6 @@ void ResourceLoader::setGlyphStore(GlyphStore* glyphStore) {
|
||||
glyphStore_->setObserver(this);
|
||||
}
|
||||
|
||||
|
||||
void ResourceLoader::setAccessToken(const std::string& accessToken) {
|
||||
accessToken_ = accessToken;
|
||||
}
|
||||
|
||||
void ResourceLoader::update(MapData& data,
|
||||
const TransformState& transform,
|
||||
GlyphAtlas& glyphAtlas,
|
||||
@@ -76,7 +71,7 @@ void ResourceLoader::update(MapData& data,
|
||||
|
||||
const float pixelRatio = transform.getPixelRatio();
|
||||
if (!sprite_ || !sprite_->hasPixelRatio(pixelRatio)) {
|
||||
sprite_ = util::make_unique<Sprite>(style_->getSpriteURL(), pixelRatio);
|
||||
sprite_ = std::make_unique<Sprite>(style_->getSpriteURL(), pixelRatio);
|
||||
sprite_->setObserver(this);
|
||||
|
||||
spriteAtlas.resize(pixelRatio);
|
||||
@@ -104,10 +99,18 @@ void ResourceLoader::onGlyphRangeLoaded() {
|
||||
emitTileDataChanged();
|
||||
}
|
||||
|
||||
void ResourceLoader::onGlyphRangeLoadingFailed(std::exception_ptr error) {
|
||||
emitResourceLoadingFailed(error);
|
||||
}
|
||||
|
||||
void ResourceLoader::onSourceLoaded() {
|
||||
emitTileDataChanged();
|
||||
}
|
||||
|
||||
void ResourceLoader::onSourceLoadingFailed(std::exception_ptr error) {
|
||||
emitResourceLoadingFailed(error);
|
||||
}
|
||||
|
||||
void ResourceLoader::onTileLoaded(bool isNewTile) {
|
||||
if (isNewTile) {
|
||||
shouldReparsePartialTiles_ = true;
|
||||
@@ -116,12 +119,20 @@ void ResourceLoader::onTileLoaded(bool isNewTile) {
|
||||
emitTileDataChanged();
|
||||
}
|
||||
|
||||
void ResourceLoader::onTileLoadingFailed(std::exception_ptr error) {
|
||||
emitResourceLoadingFailed(error);
|
||||
}
|
||||
|
||||
void ResourceLoader::onSpriteLoaded() {
|
||||
shouldReparsePartialTiles_ = true;
|
||||
|
||||
emitTileDataChanged();
|
||||
}
|
||||
|
||||
void ResourceLoader::onSpriteLoadingFailed(std::exception_ptr error) {
|
||||
emitResourceLoadingFailed(error);
|
||||
}
|
||||
|
||||
void ResourceLoader::emitTileDataChanged() {
|
||||
assert(Environment::currentlyOn(ThreadType::Map));
|
||||
|
||||
@@ -130,4 +141,20 @@ void ResourceLoader::emitTileDataChanged() {
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceLoader::emitResourceLoadingFailed(std::exception_ptr error) {
|
||||
assert(Environment::currentlyOn(ThreadType::Map));
|
||||
|
||||
try {
|
||||
if (error) {
|
||||
std::rethrow_exception(error);
|
||||
}
|
||||
} catch(const std::exception& e) {
|
||||
Log::Error(Event::ResourceLoader, e.what());
|
||||
}
|
||||
|
||||
if (observer_) {
|
||||
observer_->onResourceLoadingFailed(error);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ public:
|
||||
virtual ~Observer() = default;
|
||||
|
||||
virtual void onTileDataChanged() = 0;
|
||||
virtual void onResourceLoadingFailed(std::exception_ptr error) = 0;
|
||||
};
|
||||
|
||||
ResourceLoader();
|
||||
@@ -48,9 +49,6 @@ public:
|
||||
// style.
|
||||
void setGlyphStore(GlyphStore* glyphStore);
|
||||
|
||||
// Set the access token to be used for loading the tile data.
|
||||
void setAccessToken(const std::string& accessToken);
|
||||
|
||||
// Fetch the tiles needed by the current viewport and emit a signal when
|
||||
// a tile is ready so observers can render the tile.
|
||||
void update(MapData&, const TransformState&, GlyphAtlas&, SpriteAtlas&, TexturePool&);
|
||||
@@ -62,20 +60,24 @@ public:
|
||||
|
||||
// GlyphStore::Observer implementation.
|
||||
void onGlyphRangeLoaded() override;
|
||||
void onGlyphRangeLoadingFailed(std::exception_ptr error) override;
|
||||
|
||||
// Source::Observer implementation.
|
||||
void onSourceLoaded() override;
|
||||
void onSourceLoadingFailed(std::exception_ptr error) override;
|
||||
void onTileLoaded(bool isNewTile) override;
|
||||
void onTileLoadingFailed(std::exception_ptr error) override;
|
||||
|
||||
// Sprite::Observer implementation.
|
||||
void onSpriteLoaded() override;
|
||||
void onSpriteLoadingFailed(std::exception_ptr error) override;
|
||||
|
||||
private:
|
||||
void emitTileDataChanged();
|
||||
void emitResourceLoadingFailed(std::exception_ptr error);
|
||||
|
||||
bool shouldReparsePartialTiles_ = false;
|
||||
|
||||
std::string accessToken_;
|
||||
util::ptr<Sprite> sprite_;
|
||||
|
||||
GlyphStore* glyphStore_ = nullptr;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <mbgl/map/transform.hpp>
|
||||
#include <mbgl/map/tile.hpp>
|
||||
#include <mbgl/renderer/painter.hpp>
|
||||
#include <mbgl/util/exception.hpp>
|
||||
#include <mbgl/util/constants.hpp>
|
||||
#include <mbgl/storage/resource.hpp>
|
||||
#include <mbgl/storage/response.hpp>
|
||||
@@ -13,6 +14,7 @@
|
||||
#include <mbgl/style/style_layer.hpp>
|
||||
#include <mbgl/platform/log.hpp>
|
||||
#include <mbgl/util/uv_detail.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/util/token.hpp>
|
||||
#include <mbgl/util/string.hpp>
|
||||
#include <mbgl/util/tile_cover.hpp>
|
||||
@@ -139,18 +141,19 @@ bool Source::isLoaded() const {
|
||||
// Note: This is a separate function that must be called exactly once after creation
|
||||
// The reason this isn't part of the constructor is that calling shared_from_this() in
|
||||
// the constructor fails.
|
||||
void Source::load(const std::string& accessToken) {
|
||||
void Source::load() {
|
||||
if (info.url.empty()) {
|
||||
loaded = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string url = util::mapbox::normalizeSourceURL(info.url, accessToken);
|
||||
req = Environment::Get().request({ Resource::Kind::JSON, url }, [this](const Response &res) {
|
||||
req = Environment::Get().request({ Resource::Kind::Source, info.url }, [this](const Response &res) {
|
||||
req = nullptr;
|
||||
|
||||
if (res.status != Response::Successful) {
|
||||
Log::Warning(Event::General, "Failed to load source TileJSON: %s", res.message.c_str());
|
||||
std::stringstream message;
|
||||
message << "Failed to load [" << info.url << "]: " << res.message;
|
||||
emitSourceLoadingFailed(message.str());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -158,7 +161,9 @@ void Source::load(const std::string& accessToken) {
|
||||
d.Parse<0>(res.data.c_str());
|
||||
|
||||
if (d.HasParseError()) {
|
||||
Log::Error(Event::General, "Invalid source TileJSON; Parse Error at %d: %s", d.GetErrorOffset(), d.GetParseError());
|
||||
std::stringstream message;
|
||||
message << "Failed to parse [" << info.url << "]: " << d.GetErrorOffset() << " - " << d.GetParseError();
|
||||
emitSourceLoadingFailed(message.str());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -261,7 +266,8 @@ TileData::State Source::addTile(MapData& data,
|
||||
}
|
||||
|
||||
const float overscaling = id.z > info.max_zoom ? std::pow(2.0f, id.z - info.max_zoom) : 1.0f;
|
||||
auto pos = tiles.emplace(id, util::make_unique<Tile>(id));//, util::tileSize * overscaling));
|
||||
auto pos = tiles.emplace(id, std::make_unique<Tile>(id));
|
||||
|
||||
Tile& new_tile = *pos.first->second;
|
||||
|
||||
// We couldn't find the tile in the list. Create a new one.
|
||||
@@ -283,8 +289,9 @@ TileData::State Source::addTile(MapData& data,
|
||||
new_tile.data = cache.get(normalized_id.to_uint64());
|
||||
}
|
||||
|
||||
auto callback = std::bind(&Source::emitTileLoaded, this, true);
|
||||
if (!new_tile.data) {
|
||||
auto callback = std::bind(&Source::tileLoadingCompleteCallback, this, normalized_id);
|
||||
|
||||
// If we don't find working tile data, we're just going to load it.
|
||||
if (info.type == SourceType::Vector) {
|
||||
new_tile.data =
|
||||
@@ -294,7 +301,8 @@ TileData::State Source::addTile(MapData& data,
|
||||
new_tile.data->request(style.workers, transformState.getPixelRatio(), callback);
|
||||
} else if (info.type == SourceType::Raster) {
|
||||
new_tile.data = std::make_shared<RasterTileData>(normalized_id, texturePool, info);
|
||||
new_tile.data->request(style.workers, transformState.getPixelRatio(), callback);
|
||||
new_tile.data->request(
|
||||
style.workers, transformState.getPixelRatio(), callback);
|
||||
} else if (info.type == SourceType::Annotations) {
|
||||
new_tile.data = std::make_shared<LiveTileData>(normalized_id, data.annotationManager,
|
||||
style, glyphAtlas,
|
||||
@@ -546,16 +554,53 @@ void Source::setObserver(Observer* observer) {
|
||||
observer_ = observer;
|
||||
}
|
||||
|
||||
void Source::tileLoadingCompleteCallback(const TileID& normalized_id) {
|
||||
auto it = tile_data.find(normalized_id);
|
||||
if (it == tile_data.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
util::ptr<TileData> data = it->second.lock();
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (data->getState() == TileData::State::obsolete && !data->getError().empty()) {
|
||||
emitTileLoadingFailed(data->getError());
|
||||
return;
|
||||
}
|
||||
|
||||
emitTileLoaded(true);
|
||||
}
|
||||
|
||||
void Source::emitSourceLoaded() {
|
||||
if (observer_) {
|
||||
observer_->onSourceLoaded();
|
||||
}
|
||||
}
|
||||
|
||||
void Source::emitSourceLoadingFailed(const std::string& message) {
|
||||
if (!observer_) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto error = std::make_exception_ptr(util::SourceLoadingException(message));
|
||||
observer_->onSourceLoadingFailed(error);
|
||||
}
|
||||
|
||||
void Source::emitTileLoaded(bool isNewTile) {
|
||||
if (observer_) {
|
||||
observer_->onTileLoaded(isNewTile);
|
||||
}
|
||||
}
|
||||
|
||||
void Source::emitTileLoadingFailed(const std::string& message) {
|
||||
if (!observer_) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto error = std::make_exception_ptr(util::TileLoadingException(message));
|
||||
observer_->onTileLoadingFailed(error);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -58,13 +58,16 @@ public:
|
||||
virtual ~Observer() = default;
|
||||
|
||||
virtual void onSourceLoaded() = 0;
|
||||
virtual void onSourceLoadingFailed(std::exception_ptr error) = 0;
|
||||
|
||||
virtual void onTileLoaded(bool isNewTile) = 0;
|
||||
virtual void onTileLoadingFailed(std::exception_ptr error) = 0;
|
||||
};
|
||||
|
||||
Source();
|
||||
~Source();
|
||||
|
||||
void load(const std::string& accessToken);
|
||||
void load();
|
||||
bool isLoaded() const;
|
||||
|
||||
void load(MapData&, Environment&, std::function<void()> callback);
|
||||
@@ -102,8 +105,12 @@ public:
|
||||
|
||||
private:
|
||||
void redoPlacement(const TransformState& transformState, bool collisionDebug);
|
||||
void tileLoadingCompleteCallback(const TileID& normalized_id);
|
||||
|
||||
void emitSourceLoaded();
|
||||
void emitSourceLoadingFailed(const std::string& message);
|
||||
void emitTileLoaded(bool isNewTile);
|
||||
void emitTileLoadingFailed(const std::string& message);
|
||||
|
||||
bool handlePartialTile(const TileID &id, Worker &worker);
|
||||
bool findLoadedChildren(const TileID& id, int32_t maxCoveringZoom, std::forward_list<TileID>& retain);
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
#include <mbgl/map/sprite.hpp>
|
||||
#include <mbgl/util/raster.hpp>
|
||||
#include <mbgl/platform/log.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <mbgl/platform/platform.hpp>
|
||||
#include <mbgl/map/environment.hpp>
|
||||
#include <mbgl/platform/log.hpp>
|
||||
#include <mbgl/platform/platform.hpp>
|
||||
#include <mbgl/storage/resource.hpp>
|
||||
#include <mbgl/storage/response.hpp>
|
||||
#include <mbgl/util/exception.hpp>
|
||||
#include <mbgl/util/raster.hpp>
|
||||
#include <mbgl/util/uv_detail.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
|
||||
#include <rapidjson/document.h>
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
SpritePosition::SpritePosition(uint16_t x_, uint16_t y_, uint16_t width_, uint16_t height_, float pixelRatio_, bool sdf_)
|
||||
@@ -39,27 +41,31 @@ Sprite::Sprite(const std::string& baseUrl, float pixelRatio_)
|
||||
std::string spriteURL(baseUrl + (pixelRatio_ > 1 ? "@2x" : "") + ".png");
|
||||
std::string jsonURL(baseUrl + (pixelRatio_ > 1 ? "@2x" : "") + ".json");
|
||||
|
||||
jsonRequest = env.request({ Resource::Kind::JSON, jsonURL }, [this](const Response &res) {
|
||||
jsonRequest = env.request({ Resource::Kind::JSON, jsonURL }, [this, jsonURL](const Response &res) {
|
||||
jsonRequest = nullptr;
|
||||
if (res.status == Response::Successful) {
|
||||
body = res.data;
|
||||
parseJSON();
|
||||
parseJSON(jsonURL);
|
||||
} else {
|
||||
Log::Warning(Event::Sprite, "Failed to load sprite info: %s", res.message.c_str());
|
||||
std::stringstream message;
|
||||
message << "Failed to load [" << jsonURL << "]: " << res.message;
|
||||
emitSpriteLoadingFailed(message.str());
|
||||
return;
|
||||
}
|
||||
loadedJSON = true;
|
||||
emitSpriteLoadedIfComplete();
|
||||
});
|
||||
|
||||
spriteRequest = env.request({ Resource::Kind::Image, spriteURL }, [this](const Response &res) {
|
||||
spriteRequest = env.request({ Resource::Kind::Image, spriteURL }, [this, spriteURL](const Response &res) {
|
||||
spriteRequest = nullptr;
|
||||
if (res.status == Response::Successful) {
|
||||
image = res.data;
|
||||
parseImage();
|
||||
parseImage(spriteURL);
|
||||
} else {
|
||||
Log::Warning(Event::Sprite, "Failed to load sprite image: %s", res.message.c_str());
|
||||
std::stringstream message;
|
||||
message << "Failed to load [" << spriteURL << "]: " << res.message;
|
||||
emitSpriteLoadingFailed(message.str());
|
||||
return;
|
||||
}
|
||||
loadedImage = true;
|
||||
emitSpriteLoadedIfComplete();
|
||||
});
|
||||
}
|
||||
@@ -80,6 +86,15 @@ void Sprite::emitSpriteLoadedIfComplete() {
|
||||
}
|
||||
}
|
||||
|
||||
void Sprite::emitSpriteLoadingFailed(const std::string& message) {
|
||||
if (!observer) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto error = std::make_exception_ptr(util::SpriteLoadingException(message));
|
||||
observer->onSpriteLoadingFailed(error);
|
||||
}
|
||||
|
||||
bool Sprite::isLoaded() const {
|
||||
return loadedImage && loadedJSON;
|
||||
}
|
||||
@@ -88,21 +103,29 @@ bool Sprite::hasPixelRatio(float ratio) const {
|
||||
return pixelRatio == (ratio > 1 ? 2 : 1);
|
||||
}
|
||||
|
||||
void Sprite::parseImage() {
|
||||
raster = util::make_unique<util::Image>(image);
|
||||
void Sprite::parseImage(const std::string& spriteURL) {
|
||||
raster = std::make_unique<util::Image>(image);
|
||||
if (!*raster) {
|
||||
raster.reset();
|
||||
std::stringstream message;
|
||||
message << "Failed to parse [" << spriteURL << "]";
|
||||
emitSpriteLoadingFailed(message.str());
|
||||
return;
|
||||
}
|
||||
|
||||
image.clear();
|
||||
loadedImage = true;
|
||||
}
|
||||
|
||||
void Sprite::parseJSON() {
|
||||
void Sprite::parseJSON(const std::string& jsonURL) {
|
||||
rapidjson::Document d;
|
||||
d.Parse<0>(body.c_str());
|
||||
body.clear();
|
||||
|
||||
if (d.HasParseError()) {
|
||||
Log::Warning(Event::Sprite, "sprite JSON is invalid");
|
||||
std::stringstream message;
|
||||
message << "Failed to parse [" << jsonURL << "]: " << d.GetErrorOffset() << " - " << d.GetParseError();
|
||||
emitSpriteLoadingFailed(message.str());
|
||||
} else if (d.IsObject()) {
|
||||
for (rapidjson::Value::ConstMemberIterator itr = d.MemberBegin(); itr != d.MemberEnd(); ++itr) {
|
||||
const std::string& name = itr->name.GetString();
|
||||
@@ -125,8 +148,11 @@ void Sprite::parseJSON() {
|
||||
pos.emplace(name, SpritePosition { x, y, width, height, spritePixelRatio, sdf });
|
||||
}
|
||||
}
|
||||
loadedJSON = true;
|
||||
} else {
|
||||
Log::Warning(Event::Sprite, "sprite JSON root is not an object");
|
||||
std::stringstream message;
|
||||
message << "Failed to parse [" << jsonURL << "]: Root is not an object";
|
||||
emitSpriteLoadingFailed(message.str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ public:
|
||||
virtual ~Observer() = default;
|
||||
|
||||
virtual void onSpriteLoaded() = 0;
|
||||
virtual void onSpriteLoadingFailed(std::exception_ptr error) = 0;
|
||||
};
|
||||
|
||||
Sprite(const std::string& baseUrl, float pixelRatio);
|
||||
@@ -56,9 +57,10 @@ public:
|
||||
void setObserver(Observer* observer);
|
||||
private:
|
||||
void emitSpriteLoadedIfComplete();
|
||||
void emitSpriteLoadingFailed(const std::string& message);
|
||||
|
||||
void parseJSON();
|
||||
void parseImage();
|
||||
void parseJSON(const std::string& jsonURL);
|
||||
void parseImage(const std::string& spriteURL);
|
||||
|
||||
std::string body;
|
||||
std::string image;
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
#include <mbgl/map/tile_data.hpp>
|
||||
|
||||
#include <mbgl/map/environment.hpp>
|
||||
#include <mbgl/map/source.hpp>
|
||||
#include <mbgl/map/transform_state.hpp>
|
||||
|
||||
#include <mbgl/storage/file_source.hpp>
|
||||
#include <mbgl/util/worker.hpp>
|
||||
#include <mbgl/util/work_request.hpp>
|
||||
#include <mbgl/platform/log.hpp>
|
||||
#include <mbgl/storage/file_source.hpp>
|
||||
#include <mbgl/util/work_request.hpp>
|
||||
#include <mbgl/util/worker.hpp>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
@@ -35,7 +37,9 @@ void TileData::setState(const State& state_) {
|
||||
state = state_;
|
||||
}
|
||||
|
||||
void TileData::request(Worker& worker, float pixelRatio, std::function<void()> callback) {
|
||||
void TileData::request(Worker& worker,
|
||||
float pixelRatio,
|
||||
const std::function<void()>& callback) {
|
||||
std::string url = source.tileURL(id, pixelRatio);
|
||||
state = State::loading;
|
||||
|
||||
@@ -43,7 +47,10 @@ void TileData::request(Worker& worker, float pixelRatio, std::function<void()> c
|
||||
req = nullptr;
|
||||
|
||||
if (res.status != Response::Successful) {
|
||||
Log::Error(Event::HttpRequest, "[%s] tile loading failed: %s", url.c_str(), res.message.c_str());
|
||||
std::stringstream message;
|
||||
message << "Failed to load [" << url << "]: " << res.message;
|
||||
setError(message.str());
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -82,3 +89,8 @@ bool TileData::reparse(Worker& worker, std::function<void()> callback) {
|
||||
workRequest = worker.send([this] { parse(); endParsing(); }, callback);
|
||||
return true;
|
||||
}
|
||||
|
||||
void TileData::setError(const std::string& message) {
|
||||
error = message;
|
||||
setState(State::obsolete);
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ public:
|
||||
TileData(const TileID&, const SourceInfo&);
|
||||
~TileData();
|
||||
|
||||
void request(Worker&, float pixelRatio, std::function<void ()> callback);
|
||||
void request(Worker&, float pixelRatio, const std::function<void()>& callback);
|
||||
|
||||
// Schedule a tile reparse on a worker thread and call the callback on
|
||||
// completion. It will return true if the work was schedule or false it was
|
||||
@@ -69,13 +69,20 @@ public:
|
||||
// We let subclasses override setState() so they
|
||||
// can intercept the state change and react accordingly.
|
||||
virtual void setState(const State& state);
|
||||
|
||||
inline State getState() const {
|
||||
return state;
|
||||
}
|
||||
|
||||
void endParsing();
|
||||
|
||||
// Error message to be set in case of request
|
||||
// and parsing errors.
|
||||
void setError(const std::string& message);
|
||||
|
||||
std::string getError() const {
|
||||
return error;
|
||||
}
|
||||
|
||||
// Override this in the child class.
|
||||
virtual void parse() = 0;
|
||||
virtual Bucket* getBucket(StyleLayer const &layer_desc) = 0;
|
||||
@@ -106,6 +113,8 @@ protected:
|
||||
private:
|
||||
std::atomic<State> state;
|
||||
|
||||
std::string error;
|
||||
|
||||
protected:
|
||||
// Contains the tile ID string for painting debug information.
|
||||
DebugFontBuffer debugFontBuffer;
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include <mbgl/renderer/line_bucket.hpp>
|
||||
#include <mbgl/renderer/symbol_bucket.hpp>
|
||||
#include <mbgl/util/constants.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/style/style.hpp>
|
||||
|
||||
#include <locale>
|
||||
@@ -154,7 +153,7 @@ void TileParser::addBucketGeometries(Bucket& bucket, const GeometryTileLayer& la
|
||||
|
||||
std::unique_ptr<Bucket> TileParser::createFillBucket(const GeometryTileLayer& layer,
|
||||
const StyleBucket& bucket_desc) {
|
||||
auto bucket = util::make_unique<FillBucket>(tile.fillVertexBuffer,
|
||||
auto bucket = std::make_unique<FillBucket>(tile.fillVertexBuffer,
|
||||
tile.triangleElementsBuffer,
|
||||
tile.lineElementsBuffer);
|
||||
addBucketGeometries(bucket, layer, bucket_desc.filter);
|
||||
@@ -163,7 +162,7 @@ std::unique_ptr<Bucket> TileParser::createFillBucket(const GeometryTileLayer& la
|
||||
|
||||
std::unique_ptr<Bucket> TileParser::createLineBucket(const GeometryTileLayer& layer,
|
||||
const StyleBucket& bucket_desc) {
|
||||
auto bucket = util::make_unique<LineBucket>(tile.lineVertexBuffer,
|
||||
auto bucket = std::make_unique<LineBucket>(tile.lineVertexBuffer,
|
||||
tile.triangleElementsBuffer);
|
||||
|
||||
const float z = tile.id.z;
|
||||
@@ -180,7 +179,7 @@ std::unique_ptr<Bucket> TileParser::createLineBucket(const GeometryTileLayer& la
|
||||
|
||||
std::unique_ptr<Bucket> TileParser::createSymbolBucket(const GeometryTileLayer& layer,
|
||||
const StyleBucket& bucket_desc) {
|
||||
auto bucket = util::make_unique<SymbolBucket>(*tile.getCollision(), tile.id.overscaling);
|
||||
auto bucket = std::make_unique<SymbolBucket>(*tile.getCollision(), tile.id.overscaling);
|
||||
|
||||
const float z = tile.id.z;
|
||||
auto& layout = bucket->layout;
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#include <mbgl/map/view.hpp>
|
||||
#include <mbgl/util/constants.hpp>
|
||||
#include <mbgl/util/mat4.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/util/math.hpp>
|
||||
#include <mbgl/util/unitbezier.hpp>
|
||||
#include <mbgl/util/interpolate.hpp>
|
||||
@@ -384,6 +383,7 @@ void Transform::_setAngle(double new_angle, const Duration duration) {
|
||||
MapChangeRegionWillChange);
|
||||
|
||||
final.angle = _normalizeAngle(new_angle, current.angle);
|
||||
current.angle = _normalizeAngle(current.angle, final.angle);
|
||||
|
||||
if (duration == Duration::zero()) {
|
||||
current.angle = final.angle;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include <mbgl/map/vector_tile_data.hpp>
|
||||
#include <mbgl/map/tile_parser.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/style/style_layer.hpp>
|
||||
#include <mbgl/style/style_bucket.hpp>
|
||||
#include <mbgl/map/source.hpp>
|
||||
@@ -31,7 +30,7 @@ VectorTileData::VectorTileData(const TileID& id_,
|
||||
sprite(sprite_),
|
||||
style(style_),
|
||||
overscaling(overscaling_),
|
||||
collision(util::make_unique<CollisionTile>(id_.z, 4096, source_.tile_size * id.overscaling, angle, collisionDebug)),
|
||||
collision(std::make_unique<CollisionTile>(id_.z, 4096, source_.tile_size * id.overscaling, angle, collisionDebug)),
|
||||
lastAngle(angle),
|
||||
currentAngle(angle) {
|
||||
}
|
||||
@@ -68,9 +67,9 @@ void VectorTileData::parse() {
|
||||
redoPlacement();
|
||||
}
|
||||
} catch (const std::exception& ex) {
|
||||
Log::Error(Event::ParseTile, "Parsing [%d/%d/%d] failed: %s", id.z, id.x, id.y, ex.what());
|
||||
setState(State::obsolete);
|
||||
return;
|
||||
std::stringstream message;
|
||||
message << "Failed to parse [" << int(id.z) << "/" << id.x << "/" << id.y << "]: " << ex.what();
|
||||
setError(message.str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include <mbgl/shader/plain_shader.hpp>
|
||||
#include <mbgl/shader/pattern_shader.hpp>
|
||||
#include <mbgl/shader/outline_shader.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/platform/gl.hpp>
|
||||
#include <mbgl/platform/log.hpp>
|
||||
|
||||
@@ -107,7 +106,7 @@ void FillBucket::tessellate() {
|
||||
|
||||
if (!lineGroups.size() || (lineGroups.back()->vertex_length + total_vertex_count > 65535)) {
|
||||
// Move to a new group because the old one can't hold the geometry.
|
||||
lineGroups.emplace_back(util::make_unique<LineGroup>());
|
||||
lineGroups.emplace_back(std::make_unique<LineGroup>());
|
||||
}
|
||||
|
||||
assert(lineGroups.back());
|
||||
@@ -154,7 +153,7 @@ void FillBucket::tessellate() {
|
||||
|
||||
if (!triangleGroups.size() || (triangleGroups.back()->vertex_length + total_vertex_count > 65535)) {
|
||||
// Move to a new group because the old one can't hold the geometry.
|
||||
triangleGroups.emplace_back(util::make_unique<TriangleGroup>());
|
||||
triangleGroups.emplace_back(std::make_unique<TriangleGroup>());
|
||||
}
|
||||
|
||||
// We're generating triangle fans, so we always start with the first
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include <mbgl/shader/linesdf_shader.hpp>
|
||||
#include <mbgl/shader/linepattern_shader.hpp>
|
||||
#include <mbgl/util/math.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/platform/gl.hpp>
|
||||
|
||||
#ifndef BUFFER_OFFSET
|
||||
@@ -290,7 +289,7 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
|
||||
if (!triangleGroups.size() ||
|
||||
(triangleGroups.back()->vertex_length + vertexCount > 65535)) {
|
||||
// Move to a new group because the old one can't hold the geometry.
|
||||
triangleGroups.emplace_back(util::make_unique<TriangleGroup>());
|
||||
triangleGroups.emplace_back(std::make_unique<TriangleGroup>());
|
||||
}
|
||||
|
||||
assert(triangleGroups.back());
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include <mbgl/shader/gaussian_shader.hpp>
|
||||
#include <mbgl/shader/box_shader.hpp>
|
||||
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/util/constants.hpp>
|
||||
#include <mbgl/util/mat3.hpp>
|
||||
|
||||
@@ -95,19 +94,19 @@ void Painter::setup() {
|
||||
}
|
||||
|
||||
void Painter::setupShaders() {
|
||||
if (!plainShader) plainShader = util::make_unique<PlainShader>();
|
||||
if (!outlineShader) outlineShader = util::make_unique<OutlineShader>();
|
||||
if (!lineShader) lineShader = util::make_unique<LineShader>();
|
||||
if (!linesdfShader) linesdfShader = util::make_unique<LineSDFShader>();
|
||||
if (!linepatternShader) linepatternShader = util::make_unique<LinepatternShader>();
|
||||
if (!patternShader) patternShader = util::make_unique<PatternShader>();
|
||||
if (!iconShader) iconShader = util::make_unique<IconShader>();
|
||||
if (!rasterShader) rasterShader = util::make_unique<RasterShader>();
|
||||
if (!sdfGlyphShader) sdfGlyphShader = util::make_unique<SDFGlyphShader>();
|
||||
if (!sdfIconShader) sdfIconShader = util::make_unique<SDFIconShader>();
|
||||
if (!dotShader) dotShader = util::make_unique<DotShader>();
|
||||
if (!gaussianShader) gaussianShader = util::make_unique<GaussianShader>();
|
||||
if (!collisionBoxShader) collisionBoxShader = util::make_unique<CollisionBoxShader>();
|
||||
if (!plainShader) plainShader = std::make_unique<PlainShader>();
|
||||
if (!outlineShader) outlineShader = std::make_unique<OutlineShader>();
|
||||
if (!lineShader) lineShader = std::make_unique<LineShader>();
|
||||
if (!linesdfShader) linesdfShader = std::make_unique<LineSDFShader>();
|
||||
if (!linepatternShader) linepatternShader = std::make_unique<LinepatternShader>();
|
||||
if (!patternShader) patternShader = std::make_unique<PatternShader>();
|
||||
if (!iconShader) iconShader = std::make_unique<IconShader>();
|
||||
if (!rasterShader) rasterShader = std::make_unique<RasterShader>();
|
||||
if (!sdfGlyphShader) sdfGlyphShader = std::make_unique<SDFGlyphShader>();
|
||||
if (!sdfIconShader) sdfIconShader = std::make_unique<SDFIconShader>();
|
||||
if (!dotShader) dotShader = std::make_unique<DotShader>();
|
||||
if (!gaussianShader) gaussianShader = std::make_unique<GaussianShader>();
|
||||
if (!collisionBoxShader) collisionBoxShader = std::make_unique<CollisionBoxShader>();
|
||||
}
|
||||
|
||||
void Painter::resize() {
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <mbgl/shader/outline_shader.hpp>
|
||||
#include <mbgl/shader/pattern_shader.hpp>
|
||||
#include <mbgl/shader/plain_shader.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/util/mat3.hpp>
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include <mbgl/renderer/raster_bucket.hpp>
|
||||
#include <mbgl/style/style_layer.hpp>
|
||||
#include <mbgl/shader/raster_shader.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <mbgl/text/get_anchors.hpp>
|
||||
#include <mbgl/renderer/painter.hpp>
|
||||
#include <mbgl/text/glyph_store.hpp>
|
||||
#include <mbgl/text/font_stack.hpp>
|
||||
#include <mbgl/platform/log.hpp>
|
||||
#include <mbgl/text/collision_tile.hpp>
|
||||
#include <mbgl/shader/sdf_shader.hpp>
|
||||
@@ -217,7 +218,7 @@ void SymbolBucket::addFeatures(uintptr_t tileUID,
|
||||
layout.text.justify == TextJustifyType::Left ? 0 :
|
||||
0.5;
|
||||
|
||||
auto* fontStack = glyphStore.getFontStack(layout.text.font);
|
||||
auto fontStack = glyphStore.getFontStack(layout.text.font);
|
||||
|
||||
for (const auto& feature : features) {
|
||||
if (!feature.geometry.size()) continue;
|
||||
@@ -241,7 +242,7 @@ void SymbolBucket::addFeatures(uintptr_t tileUID,
|
||||
|
||||
// Add the glyphs we need for this label to the glyph atlas.
|
||||
if (shapedText) {
|
||||
glyphAtlas.addGlyphs(tileUID, feature.label, layout.text.font, *fontStack, face);
|
||||
glyphAtlas.addGlyphs(tileUID, feature.label, layout.text.font, **fontStack, face);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,7 +323,7 @@ void SymbolBucket::placeFeatures() {
|
||||
|
||||
void SymbolBucket::placeFeatures(bool swapImmediately) {
|
||||
|
||||
renderDataInProgress = util::make_unique<SymbolRenderData>();
|
||||
renderDataInProgress = std::make_unique<SymbolRenderData>();
|
||||
|
||||
// Calculate which labels can be shown and when they can be shown and
|
||||
// create the bufers used for rendering.
|
||||
@@ -427,7 +428,7 @@ void SymbolBucket::addSymbols(Buffer &buffer, const SymbolQuads &symbols, float
|
||||
if (!buffer.groups.size() ||
|
||||
(buffer.groups.back()->vertex_length + glyph_vertex_length > 65535)) {
|
||||
// Move to a new group because the old one can't hold the geometry.
|
||||
buffer.groups.emplace_back(util::make_unique<GroupType>());
|
||||
buffer.groups.emplace_back(std::make_unique<GroupType>());
|
||||
}
|
||||
|
||||
// We're generating triangle fans, so we always start with the first
|
||||
@@ -488,7 +489,7 @@ void SymbolBucket::addToDebugBuffers() {
|
||||
auto& collisionBox = renderDataInProgress->collisionBox;
|
||||
if (!collisionBox.groups.size()) {
|
||||
// Move to a new group because the old one can't hold the geometry.
|
||||
collisionBox.groups.emplace_back(util::make_unique<CollisionBoxElementGroup>());
|
||||
collisionBox.groups.emplace_back(std::make_unique<CollisionBoxElementGroup>());
|
||||
}
|
||||
|
||||
collisionBox.vertices.add(anchor.x, anchor.y, tl.x, tl.y, maxZoom, placementZoom);
|
||||
|
||||
@@ -51,7 +51,7 @@ Shader::Shader(const char *name_, const GLchar *vertSource, const GLchar *fragSo
|
||||
if (status == 0) {
|
||||
GLint logLength;
|
||||
MBGL_CHECK_ERROR(glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength));
|
||||
std::unique_ptr<GLchar[]> log = mbgl::util::make_unique<GLchar[]>(logLength);
|
||||
const auto log = std::make_unique<GLchar[]>(logLength);
|
||||
if (logLength > 0) {
|
||||
MBGL_CHECK_ERROR(glGetProgramInfoLog(program, logLength, &logLength, log.get()));
|
||||
Log::Error(Event::Shader, "Program failed to link: %s", log.get());
|
||||
@@ -76,7 +76,7 @@ Shader::Shader(const char *name_, const GLchar *vertSource, const GLchar *fragSo
|
||||
if (status == 0) {
|
||||
GLint logLength;
|
||||
MBGL_CHECK_ERROR(glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength));
|
||||
std::unique_ptr<GLchar[]> log = mbgl::util::make_unique<GLchar[]>(logLength);
|
||||
const auto log = std::make_unique<GLchar[]>(logLength);
|
||||
if (logLength > 0) {
|
||||
MBGL_CHECK_ERROR(glGetProgramInfoLog(program, logLength, &logLength, log.get()));
|
||||
Log::Error(Event::Shader, "Program failed to validate: %s", log.get());
|
||||
@@ -115,7 +115,7 @@ bool Shader::compileShader(GLuint *shader, GLenum type, const GLchar *source) {
|
||||
GLint logLength;
|
||||
MBGL_CHECK_ERROR(glGetShaderiv(*shader, GL_INFO_LOG_LENGTH, &logLength));
|
||||
if (logLength > 0) {
|
||||
std::unique_ptr<GLchar[]> log = mbgl::util::make_unique<GLchar[]>(logLength);
|
||||
const auto log = std::make_unique<GLchar[]>(logLength);
|
||||
MBGL_CHECK_ERROR(glGetShaderInfoLog(*shader, logLength, &logLength, log.get()));
|
||||
Log::Error(Event::Shader, "Shader failed to compile: %s", log.get());
|
||||
}
|
||||
|
||||
@@ -5,11 +5,12 @@
|
||||
|
||||
#include <mbgl/storage/response.hpp>
|
||||
#include <mbgl/platform/platform.hpp>
|
||||
#include <mbgl/platform/log.hpp>
|
||||
|
||||
#include <mbgl/util/uv_detail.hpp>
|
||||
#include <mbgl/util/chrono.hpp>
|
||||
#include <mbgl/util/thread.hpp>
|
||||
#include <mbgl/platform/log.hpp>
|
||||
#include <mbgl/util/mapbox.hpp>
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wshadow"
|
||||
@@ -27,7 +28,7 @@ namespace algo = boost::algorithm;
|
||||
namespace mbgl {
|
||||
|
||||
DefaultFileSource::DefaultFileSource(FileCache* cache, const std::string& root)
|
||||
: thread(util::make_unique<util::Thread<Impl>>("FileSource", util::ThreadPriority::Low, cache, root)) {
|
||||
: thread(std::make_unique<util::Thread<Impl>>("FileSource", util::ThreadPriority::Low, cache, root)) {
|
||||
}
|
||||
|
||||
DefaultFileSource::~DefaultFileSource() {
|
||||
@@ -38,7 +39,27 @@ Request* DefaultFileSource::request(const Resource& resource,
|
||||
uv_loop_t* l,
|
||||
Callback callback) {
|
||||
assert(l);
|
||||
auto req = new Request(resource, l, std::move(callback));
|
||||
|
||||
std::string url;
|
||||
|
||||
switch (resource.kind) {
|
||||
case Resource::Kind::Style:
|
||||
url = mbgl::util::mapbox::normalizeStyleURL(resource.url, accessToken);
|
||||
break;
|
||||
|
||||
case Resource::Kind::Source:
|
||||
url = util::mapbox::normalizeSourceURL(resource.url, accessToken);
|
||||
break;
|
||||
|
||||
case Resource::Kind::Glyphs:
|
||||
url = util::mapbox::normalizeGlyphsURL(resource.url, accessToken);
|
||||
break;
|
||||
|
||||
default:
|
||||
url = resource.url;
|
||||
}
|
||||
|
||||
auto req = new Request({ resource.kind, url }, l, std::move(callback));
|
||||
thread->invoke(&Impl::add, req);
|
||||
return req;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include <mbgl/storage/response.hpp>
|
||||
|
||||
#include <mbgl/util/util.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/util/uv_detail.hpp>
|
||||
|
||||
#include <cassert>
|
||||
@@ -16,7 +15,7 @@ struct Request::Canceled { std::mutex mutex; bool confirmed = false; };
|
||||
|
||||
// Note: This requires that loop is running in the current thread (or not yet running).
|
||||
Request::Request(const Resource &resource_, uv_loop_t *loop, Callback callback_)
|
||||
: async(util::make_unique<uv::async>(loop, [this] { notifyCallback(); })),
|
||||
: async(std::make_unique<uv::async>(loop, [this] { notifyCallback(); })),
|
||||
callback(callback_),
|
||||
resource(resource_) {
|
||||
}
|
||||
@@ -62,7 +61,7 @@ void Request::notify(const std::shared_ptr<const Response> &response_) {
|
||||
void Request::cancel() {
|
||||
assert(async);
|
||||
assert(!canceled);
|
||||
canceled = util::make_unique<Canceled>();
|
||||
canceled = std::make_unique<Canceled>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <mbgl/style/style_parser.hpp>
|
||||
#include <mbgl/style/style_bucket.hpp>
|
||||
#include <mbgl/util/constants.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/util/uv_detail.hpp>
|
||||
#include <mbgl/platform/log.hpp>
|
||||
#include <csscolorparser/csscolorparser.hpp>
|
||||
@@ -17,7 +16,7 @@
|
||||
namespace mbgl {
|
||||
|
||||
Style::Style()
|
||||
: mtx(util::make_unique<uv::rwlock>()),
|
||||
: mtx(std::make_unique<uv::rwlock>()),
|
||||
workers(4) {
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include <mbgl/style/style_layer.hpp>
|
||||
#include <mbgl/map/annotation.hpp>
|
||||
#include <mbgl/util/constants.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/util/vec.hpp>
|
||||
#include <mbgl/util/uv_detail.hpp>
|
||||
#include <mbgl/platform/log.hpp>
|
||||
|
||||
140
src/mbgl/text/font_stack.cpp
Normal file
140
src/mbgl/text/font_stack.cpp
Normal file
@@ -0,0 +1,140 @@
|
||||
#include <mbgl/text/font_stack.hpp>
|
||||
#include <cassert>
|
||||
#include <mbgl/util/math.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
void FontStack::insert(uint32_t id, const SDFGlyph &glyph) {
|
||||
metrics.emplace(id, glyph.metrics);
|
||||
bitmaps.emplace(id, glyph.bitmap);
|
||||
sdfs.emplace(id, glyph);
|
||||
}
|
||||
|
||||
const std::map<uint32_t, GlyphMetrics> &FontStack::getMetrics() const {
|
||||
return metrics;
|
||||
}
|
||||
|
||||
const std::map<uint32_t, SDFGlyph> &FontStack::getSDFs() const {
|
||||
return sdfs;
|
||||
}
|
||||
|
||||
const Shaping FontStack::getShaping(const std::u32string &string, const float maxWidth,
|
||||
const float lineHeight, const float horizontalAlign,
|
||||
const float verticalAlign, const float justify,
|
||||
const float spacing, const vec2<float> &translate) const {
|
||||
Shaping shaping(translate.x * 24, translate.y * 24);
|
||||
|
||||
// the y offset *should* be part of the font metadata
|
||||
const int32_t yOffset = -17;
|
||||
|
||||
int32_t x = std::round(translate.x * 24); // one em
|
||||
const int32_t y = std::round(translate.y * 24) + yOffset; // one em
|
||||
|
||||
// Loop through all characters of this label and shape.
|
||||
for (uint32_t chr : string) {
|
||||
shaping.positionedGlyphs.emplace_back(chr, x, y);
|
||||
auto metric = metrics.find(chr);
|
||||
if (metric != metrics.end()) {
|
||||
x += metric->second.advance + spacing;
|
||||
}
|
||||
}
|
||||
|
||||
if (!shaping.positionedGlyphs.size())
|
||||
return shaping;
|
||||
|
||||
lineWrap(shaping, lineHeight, maxWidth, horizontalAlign, verticalAlign, justify);
|
||||
|
||||
return shaping;
|
||||
}
|
||||
|
||||
void align(Shaping &shaping, const float justify, const float horizontalAlign,
|
||||
const float verticalAlign, const uint32_t maxLineLength, const float lineHeight,
|
||||
const uint32_t line) {
|
||||
const float shiftX = (justify - horizontalAlign) * maxLineLength;
|
||||
const float shiftY = (-verticalAlign * (line + 1) + 0.5) * lineHeight;
|
||||
|
||||
for (auto& glyph : shaping.positionedGlyphs) {
|
||||
glyph.x += shiftX;
|
||||
glyph.y += shiftY;
|
||||
}
|
||||
}
|
||||
|
||||
void justifyLine(std::vector<PositionedGlyph> &positionedGlyphs, const std::map<uint32_t, GlyphMetrics> &metrics, uint32_t start,
|
||||
uint32_t end, float justify) {
|
||||
PositionedGlyph &glyph = positionedGlyphs[end];
|
||||
auto metric = metrics.find(glyph.glyph);
|
||||
if (metric != metrics.end()) {
|
||||
const uint32_t lastAdvance = metric->second.advance;
|
||||
const float lineIndent = float(glyph.x + lastAdvance) * justify;
|
||||
|
||||
for (uint32_t j = start; j <= end; j++) {
|
||||
positionedGlyphs[j].x -= lineIndent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FontStack::lineWrap(Shaping &shaping, const float lineHeight, const float maxWidth,
|
||||
const float horizontalAlign, const float verticalAlign,
|
||||
const float justify) const {
|
||||
uint32_t lastSafeBreak = 0;
|
||||
|
||||
uint32_t lengthBeforeCurrentLine = 0;
|
||||
uint32_t lineStartIndex = 0;
|
||||
uint32_t line = 0;
|
||||
|
||||
uint32_t maxLineLength = 0;
|
||||
|
||||
std::vector<PositionedGlyph> &positionedGlyphs = shaping.positionedGlyphs;
|
||||
|
||||
if (maxWidth) {
|
||||
for (uint32_t i = 0; i < positionedGlyphs.size(); i++) {
|
||||
PositionedGlyph &shape = positionedGlyphs[i];
|
||||
|
||||
shape.x -= lengthBeforeCurrentLine;
|
||||
shape.y += lineHeight * line;
|
||||
|
||||
if (shape.x > maxWidth && lastSafeBreak > 0) {
|
||||
|
||||
uint32_t lineLength = positionedGlyphs[lastSafeBreak + 1].x;
|
||||
maxLineLength = util::max(lineLength, maxLineLength);
|
||||
|
||||
for (uint32_t k = lastSafeBreak + 1; k <= i; k++) {
|
||||
positionedGlyphs[k].y += lineHeight;
|
||||
positionedGlyphs[k].x -= lineLength;
|
||||
}
|
||||
|
||||
if (justify) {
|
||||
justifyLine(positionedGlyphs, metrics, lineStartIndex, lastSafeBreak - 1, justify);
|
||||
}
|
||||
|
||||
lineStartIndex = lastSafeBreak + 1;
|
||||
lastSafeBreak = 0;
|
||||
lengthBeforeCurrentLine += lineLength;
|
||||
line++;
|
||||
}
|
||||
|
||||
if (shape.glyph == 32) {
|
||||
lastSafeBreak = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const PositionedGlyph& lastPositionedGlyph = positionedGlyphs.back();
|
||||
const auto lastGlyphMetric = metrics.find(lastPositionedGlyph.glyph);
|
||||
assert(lastGlyphMetric != metrics.end());
|
||||
const uint32_t lastLineLength = lastPositionedGlyph.x + lastGlyphMetric->second.advance;
|
||||
maxLineLength = std::max(maxLineLength, lastLineLength);
|
||||
|
||||
const uint32_t height = (line + 1) * lineHeight;
|
||||
|
||||
justifyLine(positionedGlyphs, metrics, lineStartIndex, uint32_t(positionedGlyphs.size()) - 1, justify);
|
||||
align(shaping, justify, horizontalAlign, verticalAlign, maxLineLength, lineHeight, line);
|
||||
|
||||
// Calculate the bounding box
|
||||
shaping.top += -verticalAlign * height;
|
||||
shaping.bottom = shaping.top + height;
|
||||
shaping.left += -horizontalAlign * maxLineLength;
|
||||
shaping.right = shaping.left + maxLineLength;
|
||||
}
|
||||
|
||||
} // end namespace mbgl
|
||||
28
src/mbgl/text/font_stack.hpp
Normal file
28
src/mbgl/text/font_stack.hpp
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef MBGL_TEXT_FONT_STACK
|
||||
#define MBGL_TEXT_FONT_STACK
|
||||
|
||||
#include <mbgl/text/glyph.hpp>
|
||||
#include <mbgl/util/vec.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class FontStack {
|
||||
public:
|
||||
void insert(uint32_t id, const SDFGlyph &glyph);
|
||||
const std::map<uint32_t, GlyphMetrics> &getMetrics() const;
|
||||
const std::map<uint32_t, SDFGlyph> &getSDFs() const;
|
||||
const Shaping getShaping(const std::u32string &string, float maxWidth, float lineHeight,
|
||||
float horizontalAlign, float verticalAlign, float justify,
|
||||
float spacing, const vec2<float> &translate) const;
|
||||
void lineWrap(Shaping &shaping, float lineHeight, float maxWidth, float horizontalAlign,
|
||||
float verticalAlign, float justify) const;
|
||||
|
||||
private:
|
||||
std::map<uint32_t, std::string> bitmaps;
|
||||
std::map<uint32_t, GlyphMetrics> metrics;
|
||||
std::map<uint32_t, SDFGlyph> sdfs;
|
||||
};
|
||||
|
||||
} // end namespace mbgl
|
||||
|
||||
#endif
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
namespace mbgl {
|
||||
@@ -68,6 +69,17 @@ class Shaping {
|
||||
operator bool() const { return positionedGlyphs.size(); }
|
||||
};
|
||||
|
||||
}
|
||||
class SDFGlyph {
|
||||
public:
|
||||
uint32_t id = 0;
|
||||
|
||||
// A signed distance field of the glyph with a border of 3 pixels.
|
||||
std::string bitmap;
|
||||
|
||||
// Glyph metrics
|
||||
GlyphMetrics metrics;
|
||||
};
|
||||
|
||||
} // end namespace mbgl
|
||||
|
||||
#endif
|
||||
|
||||
115
src/mbgl/text/glyph_pbf.cpp
Normal file
115
src/mbgl/text/glyph_pbf.cpp
Normal file
@@ -0,0 +1,115 @@
|
||||
#include <mbgl/text/glyph_pbf.hpp>
|
||||
#include <mbgl/text/font_stack.hpp>
|
||||
|
||||
#include <mbgl/map/environment.hpp>
|
||||
|
||||
#include <mbgl/storage/resource.hpp>
|
||||
#include <mbgl/storage/response.hpp>
|
||||
|
||||
#include <mbgl/util/pbf.hpp>
|
||||
#include <mbgl/util/string.hpp>
|
||||
#include <mbgl/util/token.hpp>
|
||||
#include <mbgl/util/url.hpp>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
GlyphPBF::GlyphPBF(const std::string& glyphURL,
|
||||
const std::string& fontStack,
|
||||
GlyphRange glyphRange,
|
||||
Environment& env_,
|
||||
const GlyphLoadedCallback& successCallback,
|
||||
const GlyphLoadingFailedCallback& failureCallback)
|
||||
: parsed(false), env(env_) {
|
||||
// Load the glyph set URL
|
||||
url = util::replaceTokens(glyphURL, [&](const std::string &name) -> std::string {
|
||||
if (name == "fontstack") return util::percentEncode(fontStack);
|
||||
if (name == "range") return util::toString(glyphRange.first) + "-" + util::toString(glyphRange.second);
|
||||
return "";
|
||||
});
|
||||
|
||||
// The prepare call jumps back to the main thread.
|
||||
req = env.request({ Resource::Kind::Glyphs, url }, [&, successCallback, failureCallback](const Response &res) {
|
||||
req = nullptr;
|
||||
|
||||
if (res.status != Response::Successful) {
|
||||
std::stringstream message;
|
||||
message << "Failed to load [" << url << "]: " << res.message;
|
||||
failureCallback(message.str());
|
||||
} else {
|
||||
// Transfer the data to the GlyphSet and signal its availability.
|
||||
// Once it is available, the caller will need to call parse() to actually
|
||||
// parse the data we received. We are not doing this here since this callback is being
|
||||
// called from another (unknown) thread.
|
||||
data = res.data;
|
||||
successCallback(this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
GlyphPBF::~GlyphPBF() {
|
||||
if (req) {
|
||||
env.cancelRequest(req);
|
||||
}
|
||||
}
|
||||
|
||||
void GlyphPBF::parse(FontStack &stack) {
|
||||
if (!data.size()) {
|
||||
// If there is no data, this means we either haven't received any data, or
|
||||
// we have already parsed the data.
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse the glyph PBF
|
||||
pbf glyphs_pbf(reinterpret_cast<const uint8_t *>(data.data()), data.size());
|
||||
|
||||
while (glyphs_pbf.next()) {
|
||||
if (glyphs_pbf.tag == 1) { // stacks
|
||||
pbf fontstack_pbf = glyphs_pbf.message();
|
||||
while (fontstack_pbf.next()) {
|
||||
if (fontstack_pbf.tag == 3) { // glyphs
|
||||
pbf glyph_pbf = fontstack_pbf.message();
|
||||
|
||||
SDFGlyph glyph;
|
||||
|
||||
while (glyph_pbf.next()) {
|
||||
if (glyph_pbf.tag == 1) { // id
|
||||
glyph.id = glyph_pbf.varint();
|
||||
} else if (glyph_pbf.tag == 2) { // bitmap
|
||||
glyph.bitmap = glyph_pbf.string();
|
||||
} else if (glyph_pbf.tag == 3) { // width
|
||||
glyph.metrics.width = glyph_pbf.varint();
|
||||
} else if (glyph_pbf.tag == 4) { // height
|
||||
glyph.metrics.height = glyph_pbf.varint();
|
||||
} else if (glyph_pbf.tag == 5) { // left
|
||||
glyph.metrics.left = glyph_pbf.svarint();
|
||||
} else if (glyph_pbf.tag == 6) { // top
|
||||
glyph.metrics.top = glyph_pbf.svarint();
|
||||
} else if (glyph_pbf.tag == 7) { // advance
|
||||
glyph.metrics.advance = glyph_pbf.varint();
|
||||
} else {
|
||||
glyph_pbf.skip();
|
||||
}
|
||||
}
|
||||
|
||||
stack.insert(glyph.id, glyph);
|
||||
} else {
|
||||
fontstack_pbf.skip();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
glyphs_pbf.skip();
|
||||
}
|
||||
}
|
||||
|
||||
data.clear();
|
||||
|
||||
parsed = true;
|
||||
}
|
||||
|
||||
bool GlyphPBF::isParsed() const {
|
||||
return parsed;
|
||||
}
|
||||
|
||||
}
|
||||
52
src/mbgl/text/glyph_pbf.hpp
Normal file
52
src/mbgl/text/glyph_pbf.hpp
Normal file
@@ -0,0 +1,52 @@
|
||||
#ifndef MBGL_TEXT_GLYPH_PBF
|
||||
#define MBGL_TEXT_GLYPH_PBF
|
||||
|
||||
#include <mbgl/text/glyph.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class Environment;
|
||||
class FontStack;
|
||||
class Request;
|
||||
|
||||
class GlyphPBF {
|
||||
public:
|
||||
using GlyphLoadedCallback = std::function<void(GlyphPBF*)>;
|
||||
using GlyphLoadingFailedCallback = std::function<void(const std::string&)>;
|
||||
|
||||
GlyphPBF(const std::string &glyphURL,
|
||||
const std::string &fontStack,
|
||||
GlyphRange glyphRange,
|
||||
Environment &env,
|
||||
const GlyphLoadedCallback& successCallback,
|
||||
const GlyphLoadingFailedCallback& failureCallback);
|
||||
~GlyphPBF();
|
||||
|
||||
void parse(FontStack &stack);
|
||||
bool isParsed() const;
|
||||
|
||||
std::string getURL() const {
|
||||
return url;
|
||||
}
|
||||
|
||||
private:
|
||||
GlyphPBF(const GlyphPBF &) = delete;
|
||||
GlyphPBF(GlyphPBF &&) = delete;
|
||||
GlyphPBF &operator=(const GlyphPBF &) = delete;
|
||||
GlyphPBF &operator=(GlyphPBF &&) = delete;
|
||||
|
||||
std::string data;
|
||||
std::string url;
|
||||
std::atomic<bool> parsed;
|
||||
|
||||
Environment& env;
|
||||
Request* req = nullptr;
|
||||
};
|
||||
|
||||
} // end namespace mbgl
|
||||
|
||||
#endif
|
||||
@@ -1,264 +1,19 @@
|
||||
#include <mbgl/text/glyph_store.hpp>
|
||||
#include <mbgl/text/glyph_pbf.hpp>
|
||||
#include <mbgl/text/font_stack.hpp>
|
||||
|
||||
#include <mbgl/map/environment.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/util/string.hpp>
|
||||
#include <mbgl/util/utf.hpp>
|
||||
#include <mbgl/util/pbf.hpp>
|
||||
#include <mbgl/util/url.hpp>
|
||||
#include <mbgl/util/constants.hpp>
|
||||
#include <mbgl/util/token.hpp>
|
||||
#include <mbgl/util/math.hpp>
|
||||
#include <mbgl/util/exception.hpp>
|
||||
#include <mbgl/util/uv_detail.hpp>
|
||||
#include <mbgl/storage/file_source.hpp>
|
||||
#include <mbgl/platform/log.hpp>
|
||||
#include <mbgl/platform/platform.hpp>
|
||||
#include <mbgl/util/uv_detail.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
|
||||
void FontStack::insert(uint32_t id, const SDFGlyph &glyph) {
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
metrics.emplace(id, glyph.metrics);
|
||||
bitmaps.emplace(id, glyph.bitmap);
|
||||
sdfs.emplace(id, glyph);
|
||||
}
|
||||
|
||||
const std::map<uint32_t, GlyphMetrics> &FontStack::getMetrics() const {
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
return metrics;
|
||||
}
|
||||
|
||||
const std::map<uint32_t, SDFGlyph> &FontStack::getSDFs() const {
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
return sdfs;
|
||||
}
|
||||
|
||||
const Shaping FontStack::getShaping(const std::u32string &string, const float maxWidth,
|
||||
const float lineHeight, const float horizontalAlign,
|
||||
const float verticalAlign, const float justify,
|
||||
const float spacing, const vec2<float> &translate) const {
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
|
||||
Shaping shaping(translate.x * 24, translate.y * 24);
|
||||
|
||||
// the y offset *should* be part of the font metadata
|
||||
const int32_t yOffset = -17;
|
||||
|
||||
int32_t x = std::round(translate.x * 24); // one em
|
||||
const int32_t y = std::round(translate.y * 24) + yOffset; // one em
|
||||
|
||||
// Loop through all characters of this label and shape.
|
||||
for (uint32_t chr : string) {
|
||||
shaping.positionedGlyphs.emplace_back(chr, x, y);
|
||||
auto metric = metrics.find(chr);
|
||||
if (metric != metrics.end()) {
|
||||
x += metric->second.advance + spacing;
|
||||
}
|
||||
}
|
||||
|
||||
if (!shaping.positionedGlyphs.size())
|
||||
return shaping;
|
||||
|
||||
lineWrap(shaping, lineHeight, maxWidth, horizontalAlign, verticalAlign, justify);
|
||||
|
||||
return shaping;
|
||||
}
|
||||
|
||||
void align(Shaping &shaping, const float justify, const float horizontalAlign,
|
||||
const float verticalAlign, const uint32_t maxLineLength, const float lineHeight,
|
||||
const uint32_t line) {
|
||||
const float shiftX = (justify - horizontalAlign) * maxLineLength;
|
||||
const float shiftY = (-verticalAlign * (line + 1) + 0.5) * lineHeight;
|
||||
|
||||
for (auto& glyph : shaping.positionedGlyphs) {
|
||||
glyph.x += shiftX;
|
||||
glyph.y += shiftY;
|
||||
}
|
||||
}
|
||||
|
||||
void justifyLine(std::vector<PositionedGlyph> &positionedGlyphs, const std::map<uint32_t, GlyphMetrics> &metrics, uint32_t start,
|
||||
uint32_t end, float justify) {
|
||||
PositionedGlyph &glyph = positionedGlyphs[end];
|
||||
auto metric = metrics.find(glyph.glyph);
|
||||
if (metric != metrics.end()) {
|
||||
const uint32_t lastAdvance = metric->second.advance;
|
||||
const float lineIndent = float(glyph.x + lastAdvance) * justify;
|
||||
|
||||
for (uint32_t j = start; j <= end; j++) {
|
||||
positionedGlyphs[j].x -= lineIndent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FontStack::lineWrap(Shaping &shaping, const float lineHeight, const float maxWidth,
|
||||
const float horizontalAlign, const float verticalAlign,
|
||||
const float justify) const {
|
||||
uint32_t lastSafeBreak = 0;
|
||||
|
||||
uint32_t lengthBeforeCurrentLine = 0;
|
||||
uint32_t lineStartIndex = 0;
|
||||
uint32_t line = 0;
|
||||
|
||||
uint32_t maxLineLength = 0;
|
||||
|
||||
std::vector<PositionedGlyph> &positionedGlyphs = shaping.positionedGlyphs;
|
||||
|
||||
if (maxWidth) {
|
||||
for (uint32_t i = 0; i < positionedGlyphs.size(); i++) {
|
||||
PositionedGlyph &shape = positionedGlyphs[i];
|
||||
|
||||
shape.x -= lengthBeforeCurrentLine;
|
||||
shape.y += lineHeight * line;
|
||||
|
||||
if (shape.x > maxWidth && lastSafeBreak > 0) {
|
||||
|
||||
uint32_t lineLength = positionedGlyphs[lastSafeBreak + 1].x;
|
||||
maxLineLength = util::max(lineLength, maxLineLength);
|
||||
|
||||
for (uint32_t k = lastSafeBreak + 1; k <= i; k++) {
|
||||
positionedGlyphs[k].y += lineHeight;
|
||||
positionedGlyphs[k].x -= lineLength;
|
||||
}
|
||||
|
||||
if (justify) {
|
||||
justifyLine(positionedGlyphs, metrics, lineStartIndex, lastSafeBreak - 1, justify);
|
||||
}
|
||||
|
||||
lineStartIndex = lastSafeBreak + 1;
|
||||
lastSafeBreak = 0;
|
||||
lengthBeforeCurrentLine += lineLength;
|
||||
line++;
|
||||
}
|
||||
|
||||
if (shape.glyph == 32) {
|
||||
lastSafeBreak = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const PositionedGlyph& lastPositionedGlyph = positionedGlyphs.back();
|
||||
const auto lastGlyphMetric = metrics.find(lastPositionedGlyph.glyph);
|
||||
assert(lastGlyphMetric != metrics.end());
|
||||
const uint32_t lastLineLength = lastPositionedGlyph.x + lastGlyphMetric->second.advance;
|
||||
maxLineLength = std::max(maxLineLength, lastLineLength);
|
||||
|
||||
const uint32_t height = (line + 1) * lineHeight;
|
||||
|
||||
justifyLine(positionedGlyphs, metrics, lineStartIndex, uint32_t(positionedGlyphs.size()) - 1, justify);
|
||||
align(shaping, justify, horizontalAlign, verticalAlign, maxLineLength, lineHeight, line);
|
||||
|
||||
// Calculate the bounding box
|
||||
shaping.top += -verticalAlign * height;
|
||||
shaping.bottom = shaping.top + height;
|
||||
shaping.left += -horizontalAlign * maxLineLength;
|
||||
shaping.right = shaping.left + maxLineLength;
|
||||
}
|
||||
|
||||
GlyphPBF::GlyphPBF(const std::string& glyphURL,
|
||||
const std::string& fontStack,
|
||||
GlyphRange glyphRange,
|
||||
Environment& env_,
|
||||
const GlyphLoadedCallback& callback)
|
||||
: parsed(false), env(env_) {
|
||||
// Load the glyph set URL
|
||||
std::string url = util::replaceTokens(glyphURL, [&](const std::string &name) -> std::string {
|
||||
if (name == "fontstack") return util::percentEncode(fontStack);
|
||||
if (name == "range") return util::toString(glyphRange.first) + "-" + util::toString(glyphRange.second);
|
||||
return "";
|
||||
});
|
||||
|
||||
// The prepare call jumps back to the main thread.
|
||||
req = env.request({ Resource::Kind::Glyphs, url }, [&, url, callback](const Response &res) {
|
||||
req = nullptr;
|
||||
|
||||
if (res.status != Response::Successful) {
|
||||
// Something went wrong with loading the glyph pbf.
|
||||
const std::string msg = std::string { "[ERROR] failed to load glyphs: " } + url + " message: " + res.message;
|
||||
Log::Error(Event::HttpRequest, msg);
|
||||
} else {
|
||||
// Transfer the data to the GlyphSet and signal its availability.
|
||||
// Once it is available, the caller will need to call parse() to actually
|
||||
// parse the data we received. We are not doing this here since this callback is being
|
||||
// called from another (unknown) thread.
|
||||
data = res.data;
|
||||
parsed = true;
|
||||
callback(this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
GlyphPBF::~GlyphPBF() {
|
||||
if (req) {
|
||||
env.cancelRequest(req);
|
||||
}
|
||||
}
|
||||
|
||||
void GlyphPBF::parse(FontStack &stack) {
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
|
||||
if (!data.size()) {
|
||||
// If there is no data, this means we either haven't received any data, or
|
||||
// we have already parsed the data.
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse the glyph PBF
|
||||
pbf glyphs_pbf(reinterpret_cast<const uint8_t *>(data.data()), data.size());
|
||||
|
||||
while (glyphs_pbf.next()) {
|
||||
if (glyphs_pbf.tag == 1) { // stacks
|
||||
pbf fontstack_pbf = glyphs_pbf.message();
|
||||
while (fontstack_pbf.next()) {
|
||||
if (fontstack_pbf.tag == 3) { // glyphs
|
||||
pbf glyph_pbf = fontstack_pbf.message();
|
||||
|
||||
SDFGlyph glyph;
|
||||
|
||||
while (glyph_pbf.next()) {
|
||||
if (glyph_pbf.tag == 1) { // id
|
||||
glyph.id = glyph_pbf.varint();
|
||||
} else if (glyph_pbf.tag == 2) { // bitmap
|
||||
glyph.bitmap = glyph_pbf.string();
|
||||
} else if (glyph_pbf.tag == 3) { // width
|
||||
glyph.metrics.width = glyph_pbf.varint();
|
||||
} else if (glyph_pbf.tag == 4) { // height
|
||||
glyph.metrics.height = glyph_pbf.varint();
|
||||
} else if (glyph_pbf.tag == 5) { // left
|
||||
glyph.metrics.left = glyph_pbf.svarint();
|
||||
} else if (glyph_pbf.tag == 6) { // top
|
||||
glyph.metrics.top = glyph_pbf.svarint();
|
||||
} else if (glyph_pbf.tag == 7) { // advance
|
||||
glyph.metrics.advance = glyph_pbf.varint();
|
||||
} else {
|
||||
glyph_pbf.skip();
|
||||
}
|
||||
}
|
||||
|
||||
stack.insert(glyph.id, glyph);
|
||||
} else {
|
||||
fontstack_pbf.skip();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
glyphs_pbf.skip();
|
||||
}
|
||||
}
|
||||
|
||||
data.clear();
|
||||
}
|
||||
|
||||
bool GlyphPBF::isParsed() const {
|
||||
return parsed;
|
||||
}
|
||||
|
||||
GlyphStore::GlyphStore(uv_loop_t* loop, Environment& env_)
|
||||
: env(env_),
|
||||
asyncEmitGlyphRangeLoaded(util::make_unique<uv::async>(loop, [this] { emitGlyphRangeLoaded(); })),
|
||||
asyncEmitGlyphRangeLoaded(std::make_unique<uv::async>(loop, [this] { emitGlyphRangeLoaded(); })),
|
||||
asyncEmitGlyphRangeLoadedingFailed(std::make_unique<uv::async>(loop, [this] { emitGlyphRangeLoadingFailed(); })),
|
||||
observer(nullptr) {
|
||||
asyncEmitGlyphRangeLoaded->unref();
|
||||
asyncEmitGlyphRangeLoadedingFailed->unref();
|
||||
}
|
||||
|
||||
GlyphStore::~GlyphStore() {
|
||||
@@ -269,7 +24,7 @@ void GlyphStore::setURL(const std::string &url) {
|
||||
glyphURL = url;
|
||||
}
|
||||
|
||||
bool GlyphStore::requestGlyphRangesIfNeeded(const std::string& fontStack,
|
||||
bool GlyphStore::requestGlyphRangesIfNeeded(const std::string& fontStackName,
|
||||
const std::set<GlyphRange>& glyphRanges) {
|
||||
bool requestIsNeeded = false;
|
||||
|
||||
@@ -277,18 +32,32 @@ bool GlyphStore::requestGlyphRangesIfNeeded(const std::string& fontStack,
|
||||
return requestIsNeeded;
|
||||
}
|
||||
|
||||
auto callback = [this, fontStack](GlyphPBF* glyph) {
|
||||
glyph->parse(*createFontStack(fontStack));
|
||||
asyncEmitGlyphRangeLoaded->send();
|
||||
auto successCallback = [this, fontStackName](GlyphPBF* glyph) {
|
||||
auto fontStack = createFontStack(fontStackName);
|
||||
try {
|
||||
glyph->parse(**fontStack);
|
||||
asyncEmitGlyphRangeLoaded->send();
|
||||
} catch (const std::exception&) {
|
||||
std::lock_guard<std::mutex> lock(errorMessageMutex);
|
||||
errorMessage = "Failed to parse [" + glyph->getURL() + "]";
|
||||
asyncEmitGlyphRangeLoadedingFailed->send();
|
||||
}
|
||||
};
|
||||
|
||||
auto failureCallback = [this](const std::string& message) {
|
||||
std::lock_guard<std::mutex> lock(errorMessageMutex);
|
||||
errorMessage = message;
|
||||
asyncEmitGlyphRangeLoadedingFailed->send();
|
||||
};
|
||||
|
||||
std::lock_guard<std::mutex> lock(rangesMutex);
|
||||
auto& rangeSets = ranges[fontStack];
|
||||
auto& rangeSets = ranges[fontStackName];
|
||||
|
||||
for (const auto& range : glyphRanges) {
|
||||
const auto& rangeSets_it = rangeSets.find(range);
|
||||
if (rangeSets_it == rangeSets.end()) {
|
||||
auto glyph = util::make_unique<GlyphPBF>(glyphURL, fontStack, range, env, callback);
|
||||
auto glyph = std::make_unique<GlyphPBF>(glyphURL, fontStackName, range, env,
|
||||
successCallback, failureCallback);
|
||||
rangeSets.emplace(range, std::move(glyph));
|
||||
requestIsNeeded = true;
|
||||
continue;
|
||||
@@ -302,26 +71,26 @@ bool GlyphStore::requestGlyphRangesIfNeeded(const std::string& fontStack,
|
||||
return requestIsNeeded;
|
||||
}
|
||||
|
||||
FontStack* GlyphStore::createFontStack(const std::string &fontStack) {
|
||||
std::lock_guard<std::mutex> lock(stacksMutex);
|
||||
util::exclusive<FontStack> GlyphStore::createFontStack(const std::string &fontStack) {
|
||||
auto lock = std::make_unique<std::lock_guard<std::mutex>>(stacksMutex);
|
||||
|
||||
auto stack_it = stacks.find(fontStack);
|
||||
if (stack_it == stacks.end()) {
|
||||
stack_it = stacks.emplace(fontStack, util::make_unique<FontStack>()).first;
|
||||
stack_it = stacks.emplace(fontStack, std::make_unique<FontStack>()).first;
|
||||
}
|
||||
|
||||
return stack_it->second.get();
|
||||
return { stack_it->second.get(), std::move(lock) };
|
||||
}
|
||||
|
||||
FontStack* GlyphStore::getFontStack(const std::string &fontStack) {
|
||||
std::lock_guard<std::mutex> lock(stacksMutex);
|
||||
util::exclusive<FontStack> GlyphStore::getFontStack(const std::string &fontStack) {
|
||||
auto lock = std::make_unique<std::lock_guard<std::mutex>>(stacksMutex);
|
||||
|
||||
const auto& stack_it = stacks.find(fontStack);
|
||||
if (stack_it == stacks.end()) {
|
||||
return nullptr;
|
||||
return { nullptr, nullptr };
|
||||
}
|
||||
|
||||
return stack_it->second.get();
|
||||
return { stack_it->second.get(), std::move(lock) };
|
||||
}
|
||||
|
||||
void GlyphStore::setObserver(Observer* observer_) {
|
||||
@@ -334,4 +103,14 @@ void GlyphStore::emitGlyphRangeLoaded() {
|
||||
}
|
||||
}
|
||||
|
||||
void GlyphStore::emitGlyphRangeLoadingFailed() {
|
||||
if (!observer) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(errorMessageMutex);
|
||||
auto error = std::make_exception_ptr(util::GlyphRangeLoadingException(errorMessage));
|
||||
observer->onGlyphRangeLoadingFailed(error);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,90 +2,27 @@
|
||||
#define MBGL_TEXT_GLYPH_STORE
|
||||
|
||||
#include <mbgl/text/glyph.hpp>
|
||||
#include <mbgl/util/vec.hpp>
|
||||
#include <mbgl/util/ptr.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <future>
|
||||
#include <map>
|
||||
#include <mbgl/util/exclusive.hpp>
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <exception>
|
||||
|
||||
typedef struct uv_loop_s uv_loop_t;
|
||||
|
||||
namespace uv {
|
||||
|
||||
class async;
|
||||
|
||||
}
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class FileSource;
|
||||
class Environment;
|
||||
class Request;
|
||||
class FontStack;
|
||||
class GlyphPBF;
|
||||
|
||||
class SDFGlyph {
|
||||
public:
|
||||
uint32_t id = 0;
|
||||
|
||||
// A signed distance field of the glyph with a border of 3 pixels.
|
||||
std::string bitmap;
|
||||
|
||||
// Glyph metrics
|
||||
GlyphMetrics metrics;
|
||||
};
|
||||
|
||||
class FontStack {
|
||||
public:
|
||||
void insert(uint32_t id, const SDFGlyph &glyph);
|
||||
const std::map<uint32_t, GlyphMetrics> &getMetrics() const;
|
||||
const std::map<uint32_t, SDFGlyph> &getSDFs() const;
|
||||
const Shaping getShaping(const std::u32string &string, float maxWidth, float lineHeight,
|
||||
float horizontalAlign, float verticalAlign, float justify,
|
||||
float spacing, const vec2<float> &translate) const;
|
||||
void lineWrap(Shaping &shaping, float lineHeight, float maxWidth, float horizontalAlign,
|
||||
float verticalAlign, float justify) const;
|
||||
|
||||
private:
|
||||
std::map<uint32_t, std::string> bitmaps;
|
||||
std::map<uint32_t, GlyphMetrics> metrics;
|
||||
std::map<uint32_t, SDFGlyph> sdfs;
|
||||
mutable std::mutex mtx;
|
||||
};
|
||||
|
||||
class GlyphPBF {
|
||||
public:
|
||||
using GlyphLoadedCallback = std::function<void(GlyphPBF*)>;
|
||||
|
||||
GlyphPBF(const std::string &glyphURL,
|
||||
const std::string &fontStack,
|
||||
GlyphRange glyphRange,
|
||||
Environment &env,
|
||||
const GlyphLoadedCallback& callback);
|
||||
~GlyphPBF();
|
||||
|
||||
void parse(FontStack &stack);
|
||||
bool isParsed() const;
|
||||
|
||||
private:
|
||||
GlyphPBF(const GlyphPBF &) = delete;
|
||||
GlyphPBF(GlyphPBF &&) = delete;
|
||||
GlyphPBF &operator=(const GlyphPBF &) = delete;
|
||||
GlyphPBF &operator=(GlyphPBF &&) = delete;
|
||||
|
||||
std::string data;
|
||||
std::atomic_bool parsed;
|
||||
|
||||
Environment& env;
|
||||
Request* req = nullptr;
|
||||
|
||||
mutable std::mutex mtx;
|
||||
};
|
||||
|
||||
// Manages Glyphrange PBF loading.
|
||||
// Manages GlyphRange PBF loading.
|
||||
class GlyphStore {
|
||||
public:
|
||||
class Observer {
|
||||
@@ -93,6 +30,7 @@ public:
|
||||
virtual ~Observer() = default;
|
||||
|
||||
virtual void onGlyphRangeLoaded() = 0;
|
||||
virtual void onGlyphRangeLoadingFailed(std::exception_ptr error) = 0;
|
||||
};
|
||||
|
||||
GlyphStore(uv_loop_t* loop, Environment &);
|
||||
@@ -104,7 +42,7 @@ public:
|
||||
// GlyphRanges are already available, and thus, no request is performed.
|
||||
bool requestGlyphRangesIfNeeded(const std::string &fontStack, const std::set<GlyphRange> &glyphRanges);
|
||||
|
||||
FontStack* getFontStack(const std::string &fontStack);
|
||||
util::exclusive<FontStack> getFontStack(const std::string &fontStack);
|
||||
|
||||
void setURL(const std::string &url);
|
||||
|
||||
@@ -112,8 +50,9 @@ public:
|
||||
|
||||
private:
|
||||
void emitGlyphRangeLoaded();
|
||||
void emitGlyphRangeLoadingFailed();
|
||||
|
||||
FontStack* createFontStack(const std::string &fontStack);
|
||||
util::exclusive<FontStack> createFontStack(const std::string &fontStack);
|
||||
|
||||
std::string glyphURL;
|
||||
Environment &env;
|
||||
@@ -124,7 +63,11 @@ private:
|
||||
std::unordered_map<std::string, std::unique_ptr<FontStack>> stacks;
|
||||
std::mutex stacksMutex;
|
||||
|
||||
std::string errorMessage;
|
||||
std::mutex errorMessageMutex;
|
||||
|
||||
std::unique_ptr<uv::async> asyncEmitGlyphRangeLoaded;
|
||||
std::unique_ptr<uv::async> asyncEmitGlyphRangeLoadedingFailed;
|
||||
|
||||
Observer* observer;
|
||||
};
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#include <mbgl/util/raster.hpp>
|
||||
#include <mbgl/util/uv_detail.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
@@ -27,7 +26,7 @@ bool Raster::isLoaded() const {
|
||||
}
|
||||
|
||||
bool Raster::load(const std::string &data) {
|
||||
img = util::make_unique<util::Image>(data);
|
||||
img = std::make_unique<util::Image>(data);
|
||||
width = img->getWidth();
|
||||
height = img->getHeight();
|
||||
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
#define MBGL_UTIL_RUN_LOOP
|
||||
|
||||
#include <mbgl/util/noncopyable.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/util/uv_detail.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <queue>
|
||||
#include <mutex>
|
||||
|
||||
@@ -19,37 +19,38 @@ public:
|
||||
|
||||
void stop();
|
||||
|
||||
// Invoke fn() in the runloop thread.
|
||||
template <class Fn>
|
||||
void invoke(Fn&& fn) {
|
||||
auto invokable = util::make_unique<Invoker<Fn>>(std::move(fn));
|
||||
// Invoke fn(args...) on this RunLoop.
|
||||
template <class Fn, class... Args>
|
||||
void invoke(Fn&& fn, Args&&... args) {
|
||||
auto tuple = std::make_tuple(std::move(args)...);
|
||||
auto invokable = std::make_unique<Invoker<Fn, decltype(tuple), Args...>>(std::move(fn), std::move(tuple));
|
||||
withMutex([&] { queue.push(std::move(invokable)); });
|
||||
async.send();
|
||||
}
|
||||
|
||||
// Invoke fn() in the runloop thread, then invoke callback(result) in the current thread.
|
||||
template <class Fn, class R>
|
||||
void invokeWithResult(Fn&& fn, std::function<void (R)>&& callback) {
|
||||
RunLoop* outer = current.get();
|
||||
assert(outer);
|
||||
|
||||
invoke([fn = std::move(fn), callback = std::move(callback), outer] () mutable {
|
||||
outer->invoke([callback = std::move(callback), result = std::move(fn())] () mutable {
|
||||
callback(std::move(result));
|
||||
});
|
||||
});
|
||||
// Return a function that invokes the given function on this RunLoop.
|
||||
template <class... Args>
|
||||
auto bind(std::function<void (Args...)> fn) {
|
||||
return [this, fn = std::move(fn)] (Args&&... args) {
|
||||
invoke(std::move(fn), std::move(args)...);
|
||||
};
|
||||
}
|
||||
|
||||
// Invoke fn() in the runloop thread, then invoke callback() in the current thread.
|
||||
template <class Fn>
|
||||
void invokeWithResult(Fn&& fn, std::function<void ()>&& callback) {
|
||||
RunLoop* outer = current.get();
|
||||
assert(outer);
|
||||
// Invoke fn(args...) on this RunLoop, then invoke callback(result) on the current RunLoop.
|
||||
template <class R, class Fn, class... Args>
|
||||
void invokeWithResult(Fn&& fn, std::function<void (R)> callback, Args&&... args) {
|
||||
invoke([fn = std::move(fn), callback = current.get()->bind(callback)] (Args&&... a) mutable {
|
||||
callback(fn(std::forward<Args>(a)...));
|
||||
}, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
invoke([fn = std::move(fn), callback = std::move(callback), outer] () mutable {
|
||||
fn();
|
||||
outer->invoke(std::move(callback));
|
||||
});
|
||||
// Invoke fn(args...) on this RunLoop, then invoke callback() on the current RunLoop.
|
||||
template <class Fn, class... Args>
|
||||
void invokeWithResult(Fn&& fn, std::function<void ()> callback, Args&&... args) {
|
||||
invoke([fn = std::move(fn), callback = current.get()->bind(callback)] (Args&&... a) mutable {
|
||||
fn(std::forward<Args>(a)...);
|
||||
callback();
|
||||
}, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
uv_loop_t* get() { return async.get()->loop; }
|
||||
@@ -65,11 +66,24 @@ private:
|
||||
virtual ~Message() = default;
|
||||
};
|
||||
|
||||
template <class F>
|
||||
template <class F, class P, class... Args>
|
||||
struct Invoker : Message {
|
||||
Invoker(F&& f) : func(std::move(f)) {}
|
||||
void operator()() override { func(); }
|
||||
Invoker(F&& f, P&& p)
|
||||
: func(std::move(f)),
|
||||
params(std::move(p)) {
|
||||
}
|
||||
|
||||
void operator()() override {
|
||||
invoke(std::index_sequence_for<Args...>{});
|
||||
}
|
||||
|
||||
template <std::size_t... I>
|
||||
void invoke(std::index_sequence<I...>) {
|
||||
func(std::forward<Args>(std::get<I>(params))...);
|
||||
}
|
||||
|
||||
F func;
|
||||
P params;
|
||||
};
|
||||
|
||||
using Queue = std::queue<std::unique_ptr<Message>>;
|
||||
|
||||
@@ -4,26 +4,12 @@
|
||||
#include <future>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <utility>
|
||||
#include <functional>
|
||||
|
||||
#include <mbgl/util/run_loop.hpp>
|
||||
#include <mbgl/platform/platform.hpp>
|
||||
|
||||
namespace {
|
||||
|
||||
template <::std::size_t...>
|
||||
struct index_sequence {};
|
||||
|
||||
template <::std::size_t N, ::std::size_t... I>
|
||||
struct integer_sequence : integer_sequence<N - 1, N - 1, I...> {};
|
||||
|
||||
template <::std::size_t... I>
|
||||
struct integer_sequence<0, I...> {
|
||||
using type = index_sequence<I...>;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace mbgl {
|
||||
namespace util {
|
||||
|
||||
@@ -50,19 +36,19 @@ public:
|
||||
// Invoke object->fn(args...) in the runloop thread.
|
||||
template <typename Fn, class... Args>
|
||||
void invoke(Fn fn, Args&&... args) {
|
||||
loop->invoke(std::bind(fn, object, args...));
|
||||
loop->invoke(bind<Fn, Args...>(fn), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// Invoke object->fn(args...) in the runloop thread, then invoke callback(result) in the current thread.
|
||||
template <typename Fn, class R, class... Args>
|
||||
void invokeWithResult(Fn fn, std::function<void (R)>&& callback, Args&&... args) {
|
||||
loop->invokeWithResult(std::bind(fn, object, std::move(args)...), std::move(callback));
|
||||
template <class R, typename Fn, class... Args>
|
||||
void invokeWithResult(Fn fn, std::function<void (R)> callback, Args&&... args) {
|
||||
loop->invokeWithResult(bind<Fn, Args...>(fn), callback, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// Invoke object->fn(args...) in the runloop thread, then invoke callback() in the current thread.
|
||||
template <typename Fn, class... Args>
|
||||
void invokeWithResult(Fn fn, std::function<void ()>&& callback, Args&&... args) {
|
||||
loop->invokeWithResult(std::bind(fn, object, std::move(args)...), std::move(callback));
|
||||
void invokeWithResult(Fn fn, std::function<void ()> callback, Args&&... args) {
|
||||
loop->invokeWithResult(bind<Fn, Args...>(fn), callback, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// Invoke object->fn(args...) in the runloop thread, and wait for the result.
|
||||
@@ -89,8 +75,13 @@ private:
|
||||
Thread& operator=(const Thread&) = delete;
|
||||
Thread& operator=(Thread&&) = delete;
|
||||
|
||||
template <typename Fn, class... Args>
|
||||
auto bind(Fn fn) {
|
||||
return [fn, this] (Args&&... a) { return (object->*fn)(std::forward<Args>(a)...); };
|
||||
}
|
||||
|
||||
template <typename P, std::size_t... I>
|
||||
void run(P&& params, index_sequence<I...>);
|
||||
void run(P&& params, std::index_sequence<I...>);
|
||||
|
||||
std::promise<void> running;
|
||||
std::promise<void> joinable;
|
||||
@@ -119,8 +110,7 @@ Thread<Object>::Thread(const std::string& name, ThreadPriority priority, Args&&.
|
||||
platform::makeThreadLowPriority();
|
||||
}
|
||||
|
||||
constexpr auto seq = typename integer_sequence<sizeof...(Args)>::type();
|
||||
run(std::move(params), seq);
|
||||
run(std::move(params), std::index_sequence_for<Args...>{});
|
||||
});
|
||||
|
||||
running.get_future().get();
|
||||
@@ -128,7 +118,7 @@ Thread<Object>::Thread(const std::string& name, ThreadPriority priority, Args&&.
|
||||
|
||||
template <class Object>
|
||||
template <typename P, std::size_t... I>
|
||||
void Thread<Object>::run(P&& params, index_sequence<I...>) {
|
||||
void Thread<Object>::run(P&& params, std::index_sequence<I...>) {
|
||||
uv::loop l;
|
||||
|
||||
{
|
||||
|
||||
@@ -19,7 +19,7 @@ public:
|
||||
|
||||
Worker::Worker(std::size_t count) {
|
||||
for (std::size_t i = 0; i < count; i++) {
|
||||
threads.emplace_back(util::make_unique<util::Thread<Impl>>("Worker", util::ThreadPriority::Low));
|
||||
threads.emplace_back(std::make_unique<util::Thread<Impl>>("Worker", util::ThreadPriority::Low));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ Worker::~Worker() = default;
|
||||
|
||||
std::unique_ptr<WorkRequest> Worker::send(Fn work, Fn after) {
|
||||
auto task = std::make_shared<WorkTask>(work, after);
|
||||
auto request = util::make_unique<WorkRequest>(task);
|
||||
auto request = std::make_unique<WorkRequest>(task);
|
||||
|
||||
threads[current]->invokeWithResult(&Worker::Impl::doWork, [task] {
|
||||
task->runAfter();
|
||||
|
||||
@@ -20,7 +20,7 @@ TEST(API, RepeatedRender) {
|
||||
HeadlessView view(display);
|
||||
DefaultFileSource fileSource(nullptr);
|
||||
|
||||
Log::setObserver(util::make_unique<FixtureLogObserver>());
|
||||
Log::setObserver(std::make_unique<FixtureLogObserver>());
|
||||
|
||||
Map map(view, fileSource, MapMode::Still);
|
||||
|
||||
@@ -28,7 +28,7 @@ TEST(API, RepeatedRender) {
|
||||
map.resize(128, 512, 1);
|
||||
map.setStyleJSON(style, "test/suite");
|
||||
std::promise<std::unique_ptr<const StillImage>> promise;
|
||||
map.renderStill([&promise](std::unique_ptr<const StillImage> image) {
|
||||
map.renderStill([&promise](std::exception_ptr, std::unique_ptr<const StillImage> image) {
|
||||
promise.set_value(std::move(image));
|
||||
});
|
||||
auto result = promise.get_future().get();
|
||||
@@ -42,7 +42,7 @@ TEST(API, RepeatedRender) {
|
||||
map.resize(512, 512, 2);
|
||||
map.setStyleJSON(style, "TEST_DATA/suite");
|
||||
std::promise<std::unique_ptr<const StillImage>> promise;
|
||||
map.renderStill([&promise](std::unique_ptr<const StillImage> image) {
|
||||
map.renderStill([&promise](std::exception_ptr, std::unique_ptr<const StillImage> image) {
|
||||
promise.set_value(std::move(image));
|
||||
});
|
||||
auto result = promise.get_future().get();
|
||||
|
||||
@@ -14,7 +14,7 @@ TEST(API, SetStyle) {
|
||||
HeadlessView view(display);
|
||||
DefaultFileSource fileSource(nullptr);
|
||||
|
||||
Log::setObserver(util::make_unique<FixtureLogObserver>());
|
||||
Log::setObserver(std::make_unique<FixtureLogObserver>());
|
||||
|
||||
{
|
||||
Map map(view, fileSource, MapMode::Still);
|
||||
|
||||
BIN
test/fixtures/resources/glyphs.pbf
vendored
Normal file
BIN
test/fixtures/resources/glyphs.pbf
vendored
Normal file
Binary file not shown.
1
test/fixtures/resources/source.json
vendored
Normal file
1
test/fixtures/resources/source.json
vendored
Normal file
File diff suppressed because one or more lines are too long
1
test/fixtures/resources/sprite.json
vendored
Normal file
1
test/fixtures/resources/sprite.json
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
test/fixtures/resources/sprite.png
vendored
Normal file
BIN
test/fixtures/resources/sprite.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
31
test/fixtures/resources/style.json
vendored
Normal file
31
test/fixtures/resources/style.json
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"version": 1,
|
||||
"name": "Test",
|
||||
"sources": {
|
||||
"compositedsource": {
|
||||
"url": "test/fixtures/resources/source.json",
|
||||
"type": "vector"
|
||||
}
|
||||
},
|
||||
"sprite": "test/fixtures/resources/sprite",
|
||||
"glyphs": "test/fixtures/resources/glyphs.pbf",
|
||||
"layers": [{
|
||||
"id": "road",
|
||||
"type": "symbol",
|
||||
"source": "compositedsource",
|
||||
"source-layer": "road_label",
|
||||
"layout": {
|
||||
"text-font": "Open Sans Regular, Arial Unicode MS Regular",
|
||||
"text-field": "{name_en}"
|
||||
}
|
||||
}, {
|
||||
"id": "poi",
|
||||
"type": "symbol",
|
||||
"source": "compositedsource",
|
||||
"source-layer": "poi_label",
|
||||
"layout": {
|
||||
"text-font": "Open Sans Regular, Arial Unicode MS Regular",
|
||||
"icon-image": "{maki}_icon"
|
||||
}
|
||||
}]
|
||||
}
|
||||
BIN
test/fixtures/resources/vector.pbf
vendored
Normal file
BIN
test/fixtures/resources/vector.pbf
vendored
Normal file
Binary file not shown.
@@ -4,7 +4,6 @@
|
||||
#include <mbgl/map/map.hpp>
|
||||
#include <mbgl/map/still_image.hpp>
|
||||
#include <mbgl/util/image.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
|
||||
#include <mbgl/util/io.hpp>
|
||||
#include <rapidjson/document.h>
|
||||
@@ -152,7 +151,7 @@ TEST_P(HeadlessTest, render) {
|
||||
map.setLatLngZoom(mbgl::LatLng(latitude, longitude), zoom);
|
||||
map.setBearing(bearing);
|
||||
|
||||
map.renderStill([&](std::unique_ptr<const StillImage> image) {
|
||||
map.renderStill([&](std::exception_ptr, std::unique_ptr<const StillImage> image) {
|
||||
const std::string png = util::compress_png(image->width, image->height, image->pixels.get());
|
||||
util::write_file("test/suite/tests/" + base + "/" + name + "/actual.png", png);
|
||||
promise.set_value();
|
||||
|
||||
@@ -24,6 +24,12 @@
|
||||
<string>1.0</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>MGLMapboxAccessToken</key>
|
||||
<string>pk.eyJ1IjoianVzdGluIiwiYSI6Ik9RX3RRQzAifQ.dmOg_BAp1ywuDZMM7YsXRg</string>
|
||||
<key>MGLMapboxMetricsEnabledSettingShownInApp</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
|
||||
162
test/ios/Images.xcassets/AppIcon.appiconset/Contents.json
Normal file
162
test/ios/Images.xcassets/AppIcon.appiconset/Contents.json
Normal file
@@ -0,0 +1,162 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "57x57",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "57x57",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "29x29",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "29x29",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "40x40",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "40x40",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "50x50",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "50x50",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "72x72",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "72x72",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "76x76",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "76x76",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "car",
|
||||
"size" : "120x120",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "24x24",
|
||||
"idiom" : "watch",
|
||||
"scale" : "2x",
|
||||
"role" : "notificationCenter",
|
||||
"subtype" : "38mm"
|
||||
},
|
||||
{
|
||||
"size" : "27.5x27.5",
|
||||
"idiom" : "watch",
|
||||
"scale" : "2x",
|
||||
"role" : "notificationCenter",
|
||||
"subtype" : "42mm"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "watch",
|
||||
"role" : "companionSettings",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "watch",
|
||||
"role" : "companionSettings",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "watch",
|
||||
"scale" : "2x",
|
||||
"role" : "appLauncher",
|
||||
"subtype" : "38mm"
|
||||
},
|
||||
{
|
||||
"size" : "44x44",
|
||||
"idiom" : "watch",
|
||||
"scale" : "2x",
|
||||
"role" : "longLook",
|
||||
"subtype" : "42mm"
|
||||
},
|
||||
{
|
||||
"size" : "86x86",
|
||||
"idiom" : "watch",
|
||||
"scale" : "2x",
|
||||
"role" : "quickLook",
|
||||
"subtype" : "38mm"
|
||||
},
|
||||
{
|
||||
"size" : "98x98",
|
||||
"idiom" : "watch",
|
||||
"scale" : "2x",
|
||||
"role" : "quickLook",
|
||||
"subtype" : "42mm"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "iphone",
|
||||
"extent" : "full-screen",
|
||||
"minimum-system-version" : "7.0",
|
||||
"filename" : "Default@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"extent" : "full-screen",
|
||||
"idiom" : "iphone",
|
||||
"subtype" : "retina4",
|
||||
"filename" : "Default-568h@2x.png",
|
||||
"minimum-system-version" : "7.0",
|
||||
"orientation" : "portrait",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "ipad",
|
||||
"extent" : "full-screen",
|
||||
"minimum-system-version" : "7.0",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"orientation" : "landscape",
|
||||
"idiom" : "ipad",
|
||||
"extent" : "full-screen",
|
||||
"minimum-system-version" : "7.0",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "ipad",
|
||||
"extent" : "full-screen",
|
||||
"minimum-system-version" : "7.0",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"orientation" : "landscape",
|
||||
"idiom" : "ipad",
|
||||
"extent" : "full-screen",
|
||||
"minimum-system-version" : "7.0",
|
||||
"scale" : "2x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 184 B |
BIN
test/ios/Images.xcassets/LaunchImage.launchimage/Default@2x.png
Normal file
BIN
test/ios/Images.xcassets/LaunchImage.launchimage/Default@2x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 170 B |
45
test/ios/LaunchScreen.xib
Normal file
45
test/ios/LaunchScreen.xib
Normal file
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7706" systemVersion="14E17e" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7703"/>
|
||||
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="iN0-l3-epB">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="© 2015 Mapbox. All rights reserved." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="8ie-xW-0ye">
|
||||
<rect key="frame" x="20" y="439" width="441" height="21"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<variation key="widthClass=compact">
|
||||
<fontDescription key="fontDescription" type="system" pointSize="11"/>
|
||||
</variation>
|
||||
</label>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="ios-tests" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
|
||||
<rect key="frame" x="20" y="140" width="441" height="43"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.23137254900000001" green="0.69803921570000005" blue="0.81568627449999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="Kid-kn-2rF"/>
|
||||
<constraint firstAttribute="centerX" secondItem="kId-c2-rCX" secondAttribute="centerX" id="Koa-jz-hwk"/>
|
||||
<constraint firstAttribute="bottom" secondItem="8ie-xW-0ye" secondAttribute="bottom" constant="20" id="Kzo-t9-V3l"/>
|
||||
<constraint firstItem="8ie-xW-0ye" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="MfP-vx-nX0"/>
|
||||
<constraint firstAttribute="centerX" secondItem="8ie-xW-0ye" secondAttribute="centerX" id="ZEH-qu-HZ9"/>
|
||||
<constraint firstItem="kId-c2-rCX" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="fvb-Df-36g"/>
|
||||
</constraints>
|
||||
<nil key="simulatedStatusBarMetrics"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<point key="canvasLocation" x="404" y="445"/>
|
||||
</view>
|
||||
</objects>
|
||||
</document>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user