This commit is contained in:
Peter Liu
2015-04-22 15:23:59 -07:00
16 changed files with 125 additions and 53 deletions

View File

@@ -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

View File

@@ -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();

View File

@@ -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()) {

View File

@@ -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' ],
}

View File

@@ -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.
//

View File

@@ -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&);

View File

@@ -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));

View File

@@ -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.'

View File

@@ -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];

View File

@@ -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

Binary file not shown.

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);

View File

@@ -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");

View File

@@ -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();
}