diff --git a/README.md b/README.md index f6b8c8b11..9eb3bcf9e 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,8 @@ Then you can build the iOS app with make: Consider `sudo npm install -g ios-sim` for auto-launching the simulator, but it can be tricky and it's better to run on an ARM-based device anyway. To do this, open `./ios/llmr-app.xcodeproj` in Xcode, then build and run on the simulator or a device yourself. +Target devices: iPhone 4 and above (4S, 5, 5c, 5s) and iPad 2 and above (3, 4, mini and/or retina). + ## Ubuntu Ensure you have git and other build essentials: @@ -109,7 +111,7 @@ proto/style.proto. The reason for doing so is that we don't have to ship with a JSON/yaml parser. To test the conversion script, run ``` -bin/convert-style.js | protoc --decode=style proto/style.proto +bin/convert-style.js && cat resources/style.pbf | protoc --decode=style proto/style.proto ``` You should see a nicely formatted hierarchical output of the converted style. @@ -118,9 +120,10 @@ You should see a nicely formatted hierarchical output of the converted style. To update the included default style, use ``` -bin/convert-style.js > resources/style.pbf && bin/build-style.js +bin/convert-style.js && bin/build-style.js ``` +This is automatically taken care of as a build phase if you are using the Xcode projects. # Usage @@ -140,4 +143,4 @@ bin/convert-style.js > resources/style.pbf && bin/build-style.js - Two-finger single-tap to zoom out one level - Long-press to reset north - Two-finger long press to reset the transform -- Three-finger long press to toggle debug information \ No newline at end of file +- Three-finger long press to toggle debug information diff --git a/bin/convert-style.js b/bin/convert-style.js index 2f21d6a8b..57aee0136 100755 --- a/bin/convert-style.js +++ b/bin/convert-style.js @@ -4,6 +4,7 @@ var style = require('./style.js'); var Protobuf = require('./protobuf.js'); +var fs = require('fs'); // var fs = require('fs'); @@ -212,4 +213,4 @@ for (var i = 0; i < style.classes.length; i++) { pbf.writeMessage(3 /* class */, createClass(klass)); } -process.stdout.write(pbf.finish()); +fs.writeFileSync('resources/style.pbf', pbf.finish()); diff --git a/include/llmr/map/settings.hpp b/include/llmr/map/settings.hpp index 3e5395b80..31f7ed5bd 100644 --- a/include/llmr/map/settings.hpp +++ b/include/llmr/map/settings.hpp @@ -7,16 +7,17 @@ namespace llmr { class Settings : private util::noncopyable { public: - virtual void save() = 0; + virtual void load() = 0; // load into memory + virtual void persist() = 0; // persist in memory (then call sync() if platform-appropriate) + virtual void sync() = 0; // save to disk + virtual void clear() = 0; // clear from memory and disk virtual ~Settings() {} public: - // position double longitude = 0; double latitude = 0; double scale = 0; double angle = 0; - // debug bool debug = false; }; diff --git a/ios/MBXSettings.h b/ios/MBXSettings.h index fa0e55c72..a0b928ac7 100644 --- a/ios/MBXSettings.h +++ b/ios/MBXSettings.h @@ -14,8 +14,9 @@ namespace llmr { public: Settings_iOS(); - virtual void save(); virtual void load(); + virtual void persist(); + virtual void sync(); virtual void clear(); }; } \ No newline at end of file diff --git a/ios/MBXSettings.mm b/ios/MBXSettings.mm index a19db0dab..57f1d0ac1 100644 --- a/ios/MBXSettings.mm +++ b/ios/MBXSettings.mm @@ -12,57 +12,41 @@ using namespace llmr; -Settings_iOS::Settings_iOS() { - NSDictionary *appDefaults = [NSDictionary dictionaryWithObjectsAndKeys: - - // position - [NSNumber numberWithDouble:longitude], @"longitude", - [NSNumber numberWithDouble:latitude], @"latitude", - [NSNumber numberWithDouble:scale], @"scale", - [NSNumber numberWithDouble:angle], @"angle", - - // debug - [NSNumber numberWithBool:debug], @"debug", - - nil - ]; - - [[NSUserDefaults standardUserDefaults] registerDefaults:appDefaults]; +Settings_iOS::Settings_iOS() +{ + [[NSUserDefaults standardUserDefaults] registerDefaults:@{ @"longitude" : @(longitude), + @"latitude" : @(latitude), + @"scale" : @(scale), + @"angle" : @(angle), + @"debug" : @(debug) }]; } -void Settings_iOS::load() { - NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; +void Settings_iOS::load() +{ + NSDictionary *settings = [[NSUserDefaults standardUserDefaults] dictionaryRepresentation]; - // position - longitude = [defaults doubleForKey:@"longitude"]; - latitude = [defaults doubleForKey:@"latitude"]; - scale = [defaults doubleForKey:@"scale"]; - angle = [defaults doubleForKey:@"angle"]; - - // debug - debug = [defaults boolForKey:@"debug"]; + longitude = [settings[@"longitude"] doubleValue]; + latitude = [settings[@"latitude"] doubleValue]; + scale = [settings[@"scale"] doubleValue]; + angle = [settings[@"angle"] doubleValue]; + debug = [settings[@"debug"] boolValue]; } -void Settings_iOS::save() { - NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; - - NSDictionary *appDefaults = [NSDictionary dictionaryWithObjectsAndKeys: - - // position - [NSNumber numberWithDouble:longitude], @"longitude", - [NSNumber numberWithDouble:latitude], @"latitude", - [NSNumber numberWithDouble:scale], @"scale", - [NSNumber numberWithDouble:angle], @"angle", - - // debug - [NSNumber numberWithBool:debug], @"debug", - - nil - ]; -// [defaults setPersistentDomain:appDefaults forName:[[NSBundle mainBundle] bundleIdentifier]]; - -// [defaults synchronize]; +void Settings_iOS::persist() +{ + [[NSUserDefaults standardUserDefaults] setValuesForKeysWithDictionary:@{ @"longitude" : @(longitude), + @"latitude" : @(latitude), + @"scale" : @(scale), + @"angle" : @(angle), + @"debug" : @(debug) }]; } -void Settings_iOS::clear() { +void Settings_iOS::sync() +{ + [[NSUserDefaults standardUserDefaults] synchronize]; +} + +void Settings_iOS::clear() +{ + [NSUserDefaults resetStandardUserDefaults]; } \ No newline at end of file diff --git a/ios/MBXViewController.mm b/ios/MBXViewController.mm index ca66a4268..c5d6effa1 100644 --- a/ios/MBXViewController.mm +++ b/ios/MBXViewController.mm @@ -128,7 +128,7 @@ class MBXMapView [self.view addGestureRecognizer:quickZoom]; } - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(startRender:) name:MBXNeedsRenderNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateRender:) name:MBXNeedsRenderNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateNetworkActivity:) name:MBXUpdateActivityNotification object:nil]; mapView = new MBXMapView(); @@ -139,9 +139,14 @@ class MBXMapView [displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; } -- (void)startRender:(NSNotification *)notification +- (void)dealloc { - [self startRender]; + mapView->settings.sync(); +} + +- (void)updateRender:(NSNotification *)notification +{ + [self updateRender]; } - (void)updateNetworkActivity:(NSNotification *)notification @@ -153,18 +158,20 @@ class MBXMapView [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:(self.activityCount > 0)]; } -- (void)startRender +- (void)updateRender { [NSObject cancelPreviousPerformRequestsWithTarget:self]; displayLink.paused = NO; - [self performSelector:@selector(stopRender) withObject:nil afterDelay:3.0]; + [self performSelector:@selector(pauseRender) withObject:nil afterDelay:3.0]; } -- (void)stopRender +- (void)pauseRender { displayLink.paused = YES; + + mapView->settings.sync(); } - (void)render:(id)sender @@ -220,7 +227,7 @@ class MBXMapView mapView->map.moveBy(finalCenter.x - self.center.x, finalCenter.y - self.center.y, duration); } - [self startRender]; + [self updateRender]; } - (void)handlePinchGesture:(UIPinchGestureRecognizer *)pinch @@ -264,7 +271,7 @@ class MBXMapView mapView->map.scaleBy(new_scale / scale, [pinch locationInView:pinch.view].x, [pinch locationInView:pinch.view].y, duration); } - [self startRender]; + [self updateRender]; } - (void)handleRotateGesture:(UIRotationGestureRecognizer *)rotate @@ -280,7 +287,7 @@ class MBXMapView mapView->map.setAngle(self.angle + rotate.rotation, [rotate locationInView:rotate.view].x, [rotate locationInView:rotate.view].y); } - [self startRender]; + [self updateRender]; } - (void)handleDoubleTapGesture:(UITapGestureRecognizer *)doubleTap @@ -290,7 +297,7 @@ class MBXMapView if (doubleTap.state == UIGestureRecognizerStateEnded) mapView->map.scaleBy(2, [doubleTap locationInView:doubleTap.view].x, [doubleTap locationInView:doubleTap.view].y, 0.5); - [self startRender]; + [self updateRender]; } - (void)handleTwoFingerTapGesture:(UITapGestureRecognizer *)twoFingerTap @@ -300,7 +307,7 @@ class MBXMapView if (twoFingerTap.state == UIGestureRecognizerStateEnded) mapView->map.scaleBy(0.5, [twoFingerTap locationInView:twoFingerTap.view].x, [twoFingerTap locationInView:twoFingerTap.view].y, 0.5); - [self startRender]; + [self updateRender]; } - (void)handleLongPressGesture:(UILongPressGestureRecognizer *)longPress @@ -310,7 +317,7 @@ class MBXMapView if (longPress.state == UIGestureRecognizerStateBegan) mapView->map.resetNorth(); - [self startRender]; + [self updateRender]; } - (void)handleTwoFingerLongPressGesture:(UILongPressGestureRecognizer *)twoFingerLongPress @@ -320,7 +327,7 @@ class MBXMapView if (twoFingerLongPress.state == UIGestureRecognizerStateBegan) mapView->map.resetPosition(); - [self startRender]; + [self updateRender]; } - (void)handleThreeFingerLongPressGesture:(UILongPressGestureRecognizer *)threeFingerLongPress @@ -334,7 +341,7 @@ class MBXMapView self.debug = ! self.debug; } - [self startRender]; + [self updateRender]; } - (void)handleQuickZoomGesture:(UILongPressGestureRecognizer *)quickZoom @@ -356,7 +363,7 @@ class MBXMapView mapView->map.scaleBy(powf(2, newZoom) / mapView->map.getScale(), self.view.bounds.size.width / 2, self.view.bounds.size.height / 2); } - [self startRender]; + [self updateRender]; } - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer diff --git a/llmr.gyp b/llmr.gyp index fa8fc07d3..bd94746fa 100644 --- a/llmr.gyp +++ b/llmr.gyp @@ -10,7 +10,43 @@ 'type': 'static_library', 'sources': [ 'map.toggleDebug(); + mapView->map.toggleDebug(); break; case GLFW_KEY_R: - if (!mods) view->map.resetPosition(); + if (!mods) mapView->map.resetPosition(); break; case GLFW_KEY_N: - if (!mods) view->map.resetNorth(); + if (!mods) mapView->map.resetNorth(); break; } } @@ -92,7 +92,7 @@ public: static void scroll(GLFWwindow *window, double xoffset, double yoffset) { - MapView *view = (MapView *)glfwGetWindowUserPointer(window); + MapView *mapView = (MapView *)glfwGetWindowUserPointer(window); double delta = yoffset * 40; bool is_wheel = delta != 0 && fmod(delta, 4.000244140625) == 0; @@ -110,51 +110,51 @@ public: scale = 1.0 / scale; } - view->map.scaleBy(scale, view->last_x, view->last_y); + mapView->map.scaleBy(scale, mapView->last_x, mapView->last_y); } static void resize(GLFWwindow *window, int, int) { - MapView *view = (MapView *)glfwGetWindowUserPointer(window); + MapView *mapView = (MapView *)glfwGetWindowUserPointer(window); int width, height; glfwGetWindowSize(window, &width, &height); int fb_width, fb_height; glfwGetFramebufferSize(window, &fb_width, &fb_height); - view->map.resize(width, height, fb_width, fb_height); + mapView->map.resize(width, height, fb_width, fb_height); } static void mouseclick(GLFWwindow *window, int button, int action, int modifiers) { - MapView *view = (MapView *)glfwGetWindowUserPointer(window); + MapView *mapView = (MapView *)glfwGetWindowUserPointer(window); if (button == GLFW_MOUSE_BUTTON_RIGHT || (button == GLFW_MOUSE_BUTTON_LEFT && modifiers & GLFW_MOD_CONTROL)) { - view->rotating = action == GLFW_PRESS; - if (view->rotating) { - view->start_x = view->last_x; - view->start_y = view->last_y; + mapView->rotating = action == GLFW_PRESS; + if (mapView->rotating) { + mapView->start_x = mapView->last_x; + mapView->start_y = mapView->last_y; } } else if (button == GLFW_MOUSE_BUTTON_LEFT) { - view->tracking = action == GLFW_PRESS; + mapView->tracking = action == GLFW_PRESS; if (action == GLFW_RELEASE) { double now = glfwGetTime(); - if (now - view->last_click < 0.4) { - view->map.scaleBy(2.0, view->last_x, view->last_y); + if (now - mapView->last_click < 0.4) { + mapView->map.scaleBy(2.0, mapView->last_x, mapView->last_y); } - view->last_click = now; + mapView->last_click = now; } } } static void mousemove(GLFWwindow *window, double x, double y) { - MapView *view = (MapView *)glfwGetWindowUserPointer(window); - if (view->tracking) { - view->map.moveBy(x - view->last_x, y - view->last_y); - } else if (view->rotating) { - view->map.rotateBy(view->start_x, view->start_y, view->last_x, view->last_y, x, y); + MapView *mapView = (MapView *)glfwGetWindowUserPointer(window); + if (mapView->tracking) { + mapView->map.moveBy(x - mapView->last_x, y - mapView->last_y); + } else if (mapView->rotating) { + mapView->map.rotateBy(mapView->start_x, mapView->start_y, mapView->last_x, mapView->last_y, x, y); } - view->last_x = x; - view->last_y = y; + mapView->last_x = x; + mapView->last_y = y; } int run() { @@ -216,14 +216,14 @@ public: llmr::Map map; }; -MapView *view; +MapView *mapView; NSOperationQueue *queue = NULL; namespace llmr { namespace platform { void restart(void *) { - view->dirty = true; + mapView->dirty = true; } void request_http(std::string url, std::function func, std::function cb) { @@ -264,9 +264,10 @@ double time() { } int main() { - view = new MapView(); - view->init(); - int ret = view->run(); - delete view; + mapView = new MapView(); + mapView->init(); + int ret = mapView->run(); + mapView->settings.sync(); + delete mapView; return ret; } diff --git a/macosx/settings.hpp b/macosx/settings.hpp index 7f5866c50..e507c6bd1 100644 --- a/macosx/settings.hpp +++ b/macosx/settings.hpp @@ -8,8 +8,9 @@ namespace llmr { class Settings_MacOSX : public Settings { public: Settings_MacOSX(); - virtual void save(); virtual void load(); + virtual void persist(); + virtual void sync(); virtual void clear(); }; diff --git a/macosx/settings.mm b/macosx/settings.mm index d788b706e..59091241e 100644 --- a/macosx/settings.mm +++ b/macosx/settings.mm @@ -2,60 +2,43 @@ #include "settings.hpp" - using namespace llmr; -Settings_MacOSX::Settings_MacOSX() { - NSDictionary *appDefaults = [NSDictionary dictionaryWithObjectsAndKeys: - - // position - [NSNumber numberWithDouble:longitude], @"longitude", - [NSNumber numberWithDouble:latitude], @"latitude", - [NSNumber numberWithDouble:scale], @"scale", - [NSNumber numberWithDouble:angle], @"angle", - - // debug - [NSNumber numberWithBool:debug], @"debug", - - nil - ]; - - [[NSUserDefaults standardUserDefaults] registerDefaults:appDefaults]; +Settings_MacOSX::Settings_MacOSX() +{ + [[NSUserDefaults standardUserDefaults] registerDefaults:@{ @"longitude" : @(longitude), + @"latitude" : @(latitude), + @"scale" : @(scale), + @"angle" : @(angle), + @"debug" : @(debug) }]; } -void Settings_MacOSX::load() { - NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; +void Settings_MacOSX::load() +{ + NSDictionary *settings = [[NSUserDefaults standardUserDefaults] dictionaryRepresentation]; - // position - longitude = [defaults doubleForKey:@"longitude"]; - latitude = [defaults doubleForKey:@"latitude"]; - scale = [defaults doubleForKey:@"scale"]; - angle = [defaults doubleForKey:@"angle"]; - - // debug - debug = [defaults boolForKey:@"debug"]; + longitude = [settings[@"longitude"] doubleValue]; + latitude = [settings[@"latitude"] doubleValue]; + scale = [settings[@"scale"] doubleValue]; + angle = [settings[@"angle"] doubleValue]; + debug = [settings[@"debug"] boolValue]; } -void Settings_MacOSX::save() { - NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; - - NSDictionary *appDefaults = [NSDictionary dictionaryWithObjectsAndKeys: - - // position - [NSNumber numberWithDouble:longitude], @"longitude", - [NSNumber numberWithDouble:latitude], @"latitude", - [NSNumber numberWithDouble:scale], @"scale", - [NSNumber numberWithDouble:angle], @"angle", - - // debug - [NSNumber numberWithBool:debug], @"debug", - - nil - ]; - [defaults setPersistentDomain:appDefaults forName:[[NSBundle mainBundle] bundleIdentifier]]; - - [defaults synchronize]; +void Settings_MacOSX::persist() +{ + [[NSUserDefaults standardUserDefaults] setValuesForKeysWithDictionary:@{ @"longitude" : @(longitude), + @"latitude" : @(latitude), + @"scale" : @(scale), + @"angle" : @(angle), + @"debug" : @(debug) }]; } -void Settings_MacOSX::clear() { +void Settings_MacOSX::sync() +{ + [[NSUserDefaults standardUserDefaults] synchronize]; } + +void Settings_MacOSX::clear() +{ + [NSUserDefaults resetStandardUserDefaults]; +} \ No newline at end of file diff --git a/src/map/map.cpp b/src/map/map.cpp index f86d4eb32..e837ecffb 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -22,6 +22,7 @@ Map::Map(Settings& settings) } Map::~Map() { + settings.sync(); } void Map::setup() { @@ -62,7 +63,7 @@ void Map::moveBy(double dx, double dy, double duration) { update(); transform.getLonLat(settings.longitude, settings.latitude); - settings.save(); + settings.persist(); } void Map::scaleBy(double ds, double cx, double cy, double duration) { @@ -72,7 +73,7 @@ void Map::scaleBy(double ds, double cx, double cy, double duration) { transform.getLonLat(settings.longitude, settings.latitude); settings.scale = transform.getScale(); - settings.save(); + settings.persist(); } void Map::rotateBy(double cx, double cy, double sx, double sy, double ex, double ey, double duration) { @@ -80,7 +81,7 @@ void Map::rotateBy(double cx, double cy, double sx, double sy, double ex, double update(); settings.angle = transform.getAngle(); - settings.save(); + settings.persist(); } void Map::setLonLat(double lon, double lat, double duration) { @@ -88,7 +89,7 @@ void Map::setLonLat(double lon, double lat, double duration) { update(); transform.getLonLat(settings.longitude, settings.latitude); - settings.save(); + settings.persist(); } void Map::getLonLat(double &lon, double &lat) const { @@ -102,7 +103,7 @@ void Map::setLonLatZoom(double lon, double lat, double zoom, double duration) { transform.getLonLat(settings.longitude, settings.latitude); settings.scale = transform.getScale(); - settings.save(); + settings.persist(); } void Map::getLonLatZoom(double &lon, double &lat, double &zoom) const { @@ -116,7 +117,7 @@ void Map::setScale(double scale, double cx, double cy, double duration) { transform.getLonLat(settings.longitude, settings.latitude); settings.scale = transform.getScale(); - settings.save(); + settings.persist(); } void Map::setZoom(double zoom, double duration) { @@ -126,7 +127,7 @@ void Map::setZoom(double zoom, double duration) { transform.getLonLat(settings.longitude, settings.latitude); settings.scale = transform.getScale(); - settings.save(); + settings.persist(); } double Map::getZoom() const { @@ -152,7 +153,7 @@ void Map::setAngle(double angle, double x, double y, double duration) { transform.getLonLat(settings.longitude, settings.latitude); settings.angle = transform.getAngle(); - settings.save(); + settings.persist(); } double Map::getScale() const { @@ -168,7 +169,7 @@ void Map::resetNorth() { update(); settings.angle = transform.getAngle(); - settings.save(); + settings.persist(); } void Map::resetPosition() { @@ -181,7 +182,7 @@ void Map::resetPosition() { transform.getLonLat(settings.longitude, settings.latitude); settings.scale = transform.getScale(); settings.angle = transform.getAngle(); - settings.save(); + settings.persist(); } void Map::resetZoom() { @@ -192,7 +193,7 @@ void Map::toggleDebug() { settings.debug = !settings.debug; update(); - settings.save(); + settings.persist(); } void Map::cancelAnimations() {