Merge branch 'master' of https://github.com/mapbox/mapbox-gl-native
This commit is contained in:
1
Makefile
1
Makefile
@@ -5,6 +5,7 @@ ANDROID_ABI ?= arm-v7
|
||||
|
||||
ifeq ($(shell uname -s), Darwin)
|
||||
HOST ?= osx
|
||||
HEADLESS ?= cgl
|
||||
JOBS ?= $(shell sysctl -n hw.ncpu)
|
||||
endif
|
||||
HOST ?= linux
|
||||
|
||||
@@ -927,9 +927,7 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
// NOTE: if you get java.lang.UnsatisfiedLinkError you likely forgot to set the size of the
|
||||
// array correctly (too large)
|
||||
std::array<JNINativeMethod, 62> methods = {{ // Can remove the extra brace in C++14
|
||||
const std::vector<JNINativeMethod> methods = {
|
||||
{"nativeCreate", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)J",
|
||||
reinterpret_cast<void *>(&nativeCreate)},
|
||||
{"nativeDestroy", "(J)V", reinterpret_cast<void *>(&nativeDestroy)},
|
||||
@@ -1014,7 +1012,7 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
|
||||
{"nativeLatLngForProjectedMeters", "(JLcom/mapbox/mapboxgl/geometry/ProjectedMeters;)Lcom/mapbox/mapboxgl/geometry/LatLng;", reinterpret_cast<void *>(&nativeLatLngForProjectedMeters)},
|
||||
{"nativePixelForLatLng", "(JLcom/mapbox/mapboxgl/geometry/LatLng;)Landroid/graphics/PointF;", reinterpret_cast<void *>(&nativePixelForLatLng)},
|
||||
{"nativeLatLngForPixel", "(JLandroid/graphics/PointF;)Lcom/mapbox/mapboxgl/geometry/LatLng;", reinterpret_cast<void *>(&nativeLatLngForPixel)},
|
||||
}};
|
||||
};
|
||||
|
||||
if (env->RegisterNatives(nativeMapViewClass, methods.data(), methods.size()) < 0) {
|
||||
env->ExceptionDescribe();
|
||||
|
||||
@@ -83,7 +83,7 @@ int main(int argc, char *argv[]) {
|
||||
HeadlessView view;
|
||||
Map map(view, fileSource);
|
||||
|
||||
map.start(Map::Mode::Static);
|
||||
map.start(Map::Mode::Still);
|
||||
|
||||
// Set access token if present
|
||||
if (token.size()) {
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
'hard_dependency': 1,
|
||||
'dependencies': [ 'touch_styles' ], # required for xcode http://openradar.appspot.com/7232149
|
||||
'conditions': [
|
||||
['OS == "mac"', {
|
||||
['platform_lib == "osx" or platform_lib == "ios"', {
|
||||
'direct_dependent_settings': {
|
||||
'mac_bundle_resources': [ '../styles/styles' ],
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ extern NSString *const MGLEventGesturePanStart;
|
||||
extern NSString *const MGLEventGesturePinchStart;
|
||||
extern NSString *const MGLEventGestureRotateStart;
|
||||
|
||||
@interface MGLMapboxEvents : NSObject
|
||||
@interface MGLMapboxEvents : NSObject <NSURLSessionDelegate>
|
||||
|
||||
// You must call these methods from the main thread.
|
||||
//
|
||||
|
||||
@@ -51,7 +51,7 @@ public:
|
||||
enum class Mode : uint8_t {
|
||||
None, // we're not doing any processing
|
||||
Continuous, // continually updating map
|
||||
Static, // a once-off static image.
|
||||
Still, // a once-off still image.
|
||||
};
|
||||
|
||||
explicit Map(View&, FileSource&);
|
||||
|
||||
@@ -98,6 +98,12 @@ T clamp(T value, T min, T max) {
|
||||
return value < min ? min : (value > max ? max : value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T wrap(T value, T min, T max) {
|
||||
T d = max - min;
|
||||
return value == max ? value : std::fmod((std::fmod((value - min), d) + d), d) + min;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T smoothstep(T edge0, T edge1, T x) {
|
||||
T t = clamp((x - edge0) / (edge1 - edge0), T(0), T(1));
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Pod::Spec.new do |m|
|
||||
|
||||
m.name = 'MapboxGL'
|
||||
m.version = '0.2.13'
|
||||
m.version = '0.2.14'
|
||||
|
||||
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.'
|
||||
|
||||
@@ -1701,6 +1701,8 @@ CLLocationCoordinate2D latLngToCoordinate(mbgl::LatLng latLng)
|
||||
}
|
||||
|
||||
self.userLocationAnnotationView = [[MGLUserLocationAnnotationView alloc] initInMapView:self];
|
||||
self.userLocationAnnotationView.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin |
|
||||
UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin);
|
||||
|
||||
self.locationManager = [CLLocationManager new];
|
||||
|
||||
|
||||
@@ -73,6 +73,8 @@ NSString *const MGLEventGestureRotateStart = @"Rotation";
|
||||
@property (atomic) NSUInteger flushAt;
|
||||
@property (atomic) NSDateFormatter *rfc3339DateFormatter;
|
||||
@property (atomic) CGFloat scale;
|
||||
@property (atomic) NSURLSession *session;
|
||||
@property (atomic) NSData *geoTrustCert;
|
||||
|
||||
|
||||
// The isPaused state tracker is only ever accessed from the main thread.
|
||||
@@ -132,11 +134,31 @@ NSString *const MGLEventGestureRotateStart = @"Rotation";
|
||||
_serialQueue = dispatch_queue_create([[NSString stringWithFormat:@"%@.%@.events.serial", _appBundleId, uniqueID] UTF8String], DISPATCH_QUEUE_SERIAL);
|
||||
|
||||
// Configure Events Infrastructure
|
||||
// ===============================
|
||||
|
||||
_session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:nil];
|
||||
|
||||
// Load Local Copy of Server's Public Key
|
||||
NSString *cerPath = nil;
|
||||
NSArray *bundles = [NSBundle allFrameworks];
|
||||
for (int lc = 0; lc < bundles.count; lc++) {
|
||||
NSBundle *b = [bundles objectAtIndex:lc];
|
||||
cerPath = [[NSBundle mainBundle] pathForResource:@"api_mapbox_com-geotrust" ofType:@"der"];
|
||||
if (cerPath != nil) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (cerPath != nil) {
|
||||
_geoTrustCert = [NSData dataWithContentsOfFile:cerPath];
|
||||
}
|
||||
|
||||
// Events Control
|
||||
_eventQueue = [[NSMutableArray alloc] init];
|
||||
_flushAt = 20;
|
||||
_flushAfter = 60;
|
||||
_token = nil;
|
||||
_instanceID = [[NSUUID UUID] UUIDString];
|
||||
|
||||
// Dynamic detection of ASIdentifierManager from Mixpanel
|
||||
// https://github.com/mixpanel/mixpanel-iphone/blob/master/LICENSE
|
||||
_advertiserId = @"";
|
||||
@@ -177,6 +199,9 @@ NSString *const MGLEventGestureRotateStart = @"Rotation";
|
||||
[NSTimeZone resetSystemTimeZone];
|
||||
[_rfc3339DateFormatter setTimeZone:[NSTimeZone systemTimeZone]];
|
||||
|
||||
// Enable Battery Monitoring
|
||||
[UIDevice currentDevice].batteryMonitoringEnabled = YES;
|
||||
|
||||
_isPaused = NO;
|
||||
}
|
||||
return self;
|
||||
@@ -300,7 +325,7 @@ NSString *const MGLEventGestureRotateStart = @"Rotation";
|
||||
[evt setValue:weakSelf.model forKey:@"model"];
|
||||
[evt setValue:weakSelf.iOSVersion forKey:@"operatingSystem"];
|
||||
[evt setValue:[weakSelf getDeviceOrientation] forKey:@"orientation"];
|
||||
[evt setValue:@(100 * [UIDevice currentDevice].batteryLevel) forKey:@"batteryLevel"];
|
||||
[evt setValue:@((int)(100 * [UIDevice currentDevice].batteryLevel)) forKey:@"batteryLevel"];
|
||||
[evt setValue:@(weakSelf.scale) forKey:@"resolution"];
|
||||
[evt setValue:weakSelf.carrier forKey:@"carrier"];
|
||||
|
||||
@@ -379,10 +404,13 @@ NSString *const MGLEventGestureRotateStart = @"Rotation";
|
||||
__weak MGLMapboxEvents *weakSelf = self;
|
||||
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
|
||||
MGLMapboxEvents *strongSelf = weakSelf;
|
||||
if (!strongSelf) return;
|
||||
|
||||
// Setup URL Request
|
||||
NSString *url = [NSString stringWithFormat:@"%@/events/v1?access_token=%@", MGLMapboxEventsAPIBase, weakSelf.token];
|
||||
NSString *url = [NSString stringWithFormat:@"%@/events/v1?access_token=%@", MGLMapboxEventsAPIBase, strongSelf.token];
|
||||
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url]];
|
||||
[request setValue:[weakSelf getUserAgent] forHTTPHeaderField:@"User-Agent"];
|
||||
[request setValue:[strongSelf getUserAgent] forHTTPHeaderField:@"User-Agent"];
|
||||
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
|
||||
[request setHTTPMethod:@"POST"];
|
||||
|
||||
@@ -392,9 +420,8 @@ NSString *const MGLEventGestureRotateStart = @"Rotation";
|
||||
[request setHTTPBody:jsonData];
|
||||
|
||||
// Send non blocking HTTP Request to server
|
||||
[NSURLConnection sendAsynchronousRequest:request
|
||||
queue:nil
|
||||
completionHandler:nil];
|
||||
NSURLSessionDataTask *task = [_session dataTaskWithRequest:request completionHandler: nil];
|
||||
[task resume];
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -635,4 +662,49 @@ NSString *const MGLEventGestureRotateStart = @"Rotation";
|
||||
return result;
|
||||
}
|
||||
|
||||
#pragma mark NSURLSessionDelegate
|
||||
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^) (NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {
|
||||
|
||||
if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
|
||||
|
||||
SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust];
|
||||
NSString *domain = [[challenge protectionSpace] host];
|
||||
SecTrustResultType trustResult;
|
||||
|
||||
// Validate the certificate chain with the device's trust store anyway
|
||||
// This *might* give use revocation checking
|
||||
SecTrustEvaluate(serverTrust, &trustResult);
|
||||
if (trustResult == kSecTrustResultUnspecified)
|
||||
{
|
||||
// Look for a pinned certificate in the server's certificate chain
|
||||
long numKeys = SecTrustGetCertificateCount(serverTrust);
|
||||
|
||||
BOOL found = false;
|
||||
for (int lc = 0; lc < numKeys; lc++) {
|
||||
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, lc);
|
||||
NSData *remoteCertificateData = CFBridgingRelease(SecCertificateCopyData(certificate));
|
||||
|
||||
// Compare Remote Key With Local Version
|
||||
if ([remoteCertificateData isEqualToData:_geoTrustCert]) {
|
||||
// Found the certificate; continue connecting
|
||||
completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
// The certificate wasn't found in the certificate chain; cancel the connection
|
||||
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Certificate chain validation failed; cancel the connection
|
||||
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
BIN
platform/ios/resources/api_mapbox_com-geotrust.der
Normal file
BIN
platform/ios/resources/api_mapbox_com-geotrust.der
Normal file
Binary file not shown.
@@ -247,15 +247,15 @@ void Map::resume() {
|
||||
void Map::renderStill(StillImageCallback fn) {
|
||||
assert(Environment::currentlyOn(ThreadType::Main));
|
||||
|
||||
if (mode != Mode::Static) {
|
||||
throw util::Exception("Map is not in static render mode");
|
||||
if (mode != Mode::Still) {
|
||||
throw util::Exception("Map is not in still image render mode");
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
throw util::Exception("Map is currently rendering an image");
|
||||
}
|
||||
|
||||
assert(mode == Mode::Static);
|
||||
assert(mode == Mode::Still);
|
||||
|
||||
callback = std::move(fn);
|
||||
|
||||
@@ -287,7 +287,7 @@ void Map::run() {
|
||||
uv_run(env->loop, UV_RUN_DEFAULT);
|
||||
checkForPause();
|
||||
}
|
||||
} else if (mode == Mode::Static) {
|
||||
} else if (mode == Mode::Still) {
|
||||
terminating = false;
|
||||
while (!terminating) {
|
||||
uv_run(env->loop, UV_RUN_DEFAULT);
|
||||
|
||||
@@ -12,6 +12,21 @@
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
/** Converts the given angle (in radians) to be numerically close to the anchor angle, allowing it to be interpolated properly without sudden jumps. */
|
||||
static double _normalizeAngle(double angle, double anchorAngle)
|
||||
{
|
||||
angle = util::wrap(angle, -M_PI, M_PI);
|
||||
double diff = std::abs(angle - anchorAngle);
|
||||
if (std::abs(angle - util::M2PI - anchorAngle) < diff) {
|
||||
angle -= util::M2PI;
|
||||
}
|
||||
if (std::abs(angle + util::M2PI - anchorAngle) < diff) {
|
||||
angle += util::M2PI;
|
||||
}
|
||||
|
||||
return angle;
|
||||
}
|
||||
|
||||
Transform::Transform(View &view_)
|
||||
: view(view_)
|
||||
{
|
||||
@@ -330,12 +345,7 @@ void Transform::_setAngle(double new_angle, const Duration duration) {
|
||||
MapChangeRegionWillChangeAnimated :
|
||||
MapChangeRegionWillChange);
|
||||
|
||||
while (new_angle > M_PI)
|
||||
new_angle -= util::M2PI;
|
||||
while (new_angle <= -M_PI)
|
||||
new_angle += util::M2PI;
|
||||
|
||||
final.angle = new_angle;
|
||||
final.angle = _normalizeAngle(new_angle, current.angle);
|
||||
|
||||
if (duration == Duration::zero()) {
|
||||
current.angle = final.angle;
|
||||
|
||||
@@ -24,7 +24,7 @@ TEST(API, RepeatedRender) {
|
||||
|
||||
Map map(view, fileSource);
|
||||
|
||||
map.start(Map::Mode::Static);
|
||||
map.start(Map::Mode::Still);
|
||||
|
||||
{
|
||||
view.resize(128, 512, 1);
|
||||
|
||||
@@ -18,7 +18,7 @@ TEST(API, SetStyle) {
|
||||
|
||||
Map map(view, fileSource);
|
||||
|
||||
map.start(Map::Mode::Static);
|
||||
map.start(Map::Mode::Still);
|
||||
|
||||
map.setStyleJSON("invalid", "test/suite");
|
||||
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
#include <mbgl/platform/default/headless_display.hpp>
|
||||
#include <mbgl/storage/default_file_source.hpp>
|
||||
|
||||
#include <uv.h>
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#include <future>
|
||||
|
||||
void rewriteLocalScheme(rapidjson::Value &value, rapidjson::Document::AllocatorType &allocator) {
|
||||
ASSERT_TRUE(value.IsString());
|
||||
auto string = std::string { value.GetString(),value.GetStringLength() };
|
||||
@@ -145,7 +145,7 @@ TEST_P(HeadlessTest, render) {
|
||||
DefaultFileSource fileSource(nullptr);
|
||||
Map map(view, fileSource);
|
||||
|
||||
map.start(Map::Mode::Static);
|
||||
map.start(Map::Mode::Still);
|
||||
|
||||
map.setClasses(classes);
|
||||
map.setStyleJSON(style, "test/suite");
|
||||
@@ -154,32 +154,15 @@ TEST_P(HeadlessTest, render) {
|
||||
map.setLatLngZoom(mbgl::LatLng(latitude, longitude), zoom);
|
||||
map.setBearing(bearing);
|
||||
|
||||
struct Data {
|
||||
std::string path;
|
||||
std::unique_ptr<const StillImage> image;
|
||||
};
|
||||
std::promise<void> promise;
|
||||
|
||||
uv_async_t *async = new uv_async_t;
|
||||
async->data = new Data { "test/suite/tests/" + base + "/" + name + "/actual.png", nullptr };
|
||||
uv_async_init(uv_default_loop(), async, [](uv_async_t *as, int) {
|
||||
auto data = std::unique_ptr<Data>(reinterpret_cast<Data *>(as->data));
|
||||
as->data = nullptr;
|
||||
uv_close(reinterpret_cast<uv_handle_t *>(as), [](uv_handle_t *handle) {
|
||||
delete reinterpret_cast<uv_async_t *>(handle);
|
||||
});
|
||||
|
||||
assert(data);
|
||||
const std::string png = util::compress_png(data->image->width, data->image->height, data->image->pixels.get());
|
||||
util::write_file(data->path, png);
|
||||
map.renderStill([&](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();
|
||||
});
|
||||
|
||||
map.renderStill([async](std::unique_ptr<const StillImage> image) {
|
||||
reinterpret_cast<Data *>(async->data)->image = std::move(image);
|
||||
uv_async_send(async);
|
||||
});
|
||||
|
||||
// This loop will terminate once the async was fired.
|
||||
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
|
||||
promise.get_future().get();
|
||||
|
||||
map.stop();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user