This commit is contained in:
Konstantin Käfer
2014-02-14 11:27:46 +01:00
11 changed files with 215 additions and 160 deletions

View File

@@ -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
- Three-finger long press to toggle debug information

View File

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

View File

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

View File

@@ -14,8 +14,9 @@ namespace llmr
{
public:
Settings_iOS();
virtual void save();
virtual void load();
virtual void persist();
virtual void sync();
virtual void clear();
};
}

View File

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

View File

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

View File

@@ -10,7 +10,43 @@
'type': 'static_library',
'sources': [
'<!@(find src -name "*.cpp")',
'<!@(find include -name "*.hpp")'
'<!@(find include -name "*.hpp")',
'<!@(find src -name "*.glsl")',
'bin/style.js'
],
'actions': [
{
'action_name': 'Build Shaders',
'inputs': [
'<!@(find src -name "*.glsl")'
],
'outputs': [
'include/llmr/shader/shaders.hpp',
'src/shader/shaders.cpp'
],
'action': ['bin/build-shaders.js'],
},
{
'action_name': 'Convert Style to Protobuf',
'inputs': [
'bin/style.js'
],
'outputs': [
'resources/style.pbf'
],
'action': ['bin/convert-style.js'],
},
{
'action_name': 'Update Style Resources',
'inputs': [
'resources/style.pbf'
],
'outputs': [
'include/llmr/style/resources.hpp',
'src/style/resources.cpp'
],
'action': ['bin/build-style.js']
}
],
'xcode_settings': {
'SDKROOT': 'macosx',
@@ -60,7 +96,43 @@
'type': 'static_library',
'sources': [
'<!@(find src -name "*.cpp")',
'<!@(find include -name "*.hpp")'
'<!@(find include -name "*.hpp")',
'<!@(find src -name "*.glsl")',
'bin/style.js'
],
'actions': [
{
'action_name': 'Build Shaders',
'inputs': [
'<!@(find src -name "*.glsl")'
],
'outputs': [
'include/llmr/shader/shaders.hpp',
'src/shader/shaders.cpp'
],
'action': ['bin/build-shaders.js'],
},
{
'action_name': 'Convert Style to Protobuf',
'inputs': [
'bin/style.js'
],
'outputs': [
'resources/style.pbf'
],
'action': ['bin/convert-style.js'],
},
{
'action_name': 'Update Style Resources',
'inputs': [
'resources/style.pbf'
],
'outputs': [
'include/llmr/style/resources.hpp',
'src/style/resources.cpp'
],
'action': ['bin/build-style.js']
}
],
'xcode_settings': {
'SDKROOT': 'iphoneos',

View File

@@ -70,7 +70,7 @@ public:
static void key(GLFWwindow *window, int key, int scancode, int action, int mods) {
MapView *view = (MapView *)glfwGetWindowUserPointer(window);
MapView *mapView = (MapView *)glfwGetWindowUserPointer(window);
if (action == GLFW_RELEASE) {
switch (key) {
@@ -78,13 +78,13 @@ public:
glfwSetWindowShouldClose(window, true);
break;
case GLFW_KEY_TAB:
view->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<void(Response&)> func, std::function<void()> 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;
}

View File

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

View File

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

View File

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