summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--.gitmodules10
-rw-r--r--.travis.yml16
-rw-r--r--Makefile19
-rw-r--r--README.md23
-rwxr-xr-xandroid/scripts/common.sh4
-rwxr-xr-xandroid/scripts/upload-testmunk.sh25
-rw-r--r--android/test/.gitignore1
-rw-r--r--android/test/features/load-app.feature5
-rwxr-xr-xandroid/test/features/step_definitions/calabash_steps.rb1
-rw-r--r--android/test/features/step_definitions/my_steps.rb6
-rwxr-xr-xandroid/test/features/support/app_installation_hooks.rb36
-rwxr-xr-xandroid/test/features/support/app_life_cycle_hooks.rb13
-rwxr-xr-xandroid/test/features/support/env.rb1
-rwxr-xr-xandroid/test/features/support/hooks.rb0
-rwxr-xr-xconfigure1
-rw-r--r--gyp/install.gypi82
-rw-r--r--gyp/platform-ios.gypi23
-rw-r--r--gyp/platform-osx.gypi1
-rw-r--r--gyp/shaders.gypi2
-rw-r--r--gyp/version.gypi2
-rw-r--r--include/mbgl/ios/MGLMapView.h203
-rw-r--r--include/mbgl/ios/MGLStyleFunctionValue.h23
-rw-r--r--include/mbgl/ios/MGLTypes.h17
-rw-r--r--include/mbgl/ios/NSArray+MGLAdditions.h7
-rw-r--r--include/mbgl/ios/NSDictionary+MGLAdditions.h7
-rw-r--r--include/mbgl/ios/UIColor+MGLAdditions.h11
-rw-r--r--include/mbgl/platform/default/glx.h2
-rw-r--r--include/mbgl/platform/default/headless_view.hpp7
-rw-r--r--ios/FAQ.md49
-rw-r--r--ios/MapboxGL.podspec33
-rw-r--r--ios/README.md68
-rw-r--r--ios/app/MBXAppDelegate.h7
-rw-r--r--ios/app/MBXAppDelegate.m15
-rw-r--r--ios/app/MBXViewController.h5
-rw-r--r--ios/app/MBXViewController.mm266
-rw-r--r--ios/app/README.md3
-rw-r--r--ios/app/app-info.plist51
-rw-r--r--ios/app/img/Default-568h@2x.pngbin0 -> 2239 bytes
-rw-r--r--ios/app/img/Default-667h@2x.pngbin0 -> 2797 bytes
-rw-r--r--ios/app/img/Icon-40.pngbin0 -> 951 bytes
-rw-r--r--ios/app/img/Icon-40@2x.pngbin0 -> 1528 bytes
-rw-r--r--ios/app/img/Icon-60.pngbin0 -> 1313 bytes
-rw-r--r--ios/app/img/Icon-60@2x.pngbin0 -> 2091 bytes
-rw-r--r--ios/app/img/Icon-72.pngbin0 -> 1418 bytes
-rw-r--r--ios/app/img/Icon-72@2x.pngbin0 -> 2442 bytes
-rw-r--r--ios/app/img/Icon-76.pngbin0 -> 1447 bytes
-rw-r--r--ios/app/img/Icon-76@2x.pngbin0 -> 2520 bytes
-rw-r--r--ios/app/img/Icon-Small-50.pngbin0 -> 1118 bytes
-rw-r--r--ios/app/img/Icon-Small-50@2x.pngbin0 -> 1839 bytes
-rw-r--r--ios/app/img/Icon-Small.pngbin0 -> 684 bytes
-rw-r--r--ios/app/img/Icon-Small@2x.pngbin0 -> 1231 bytes
-rw-r--r--ios/app/img/Icon-Spotlight-40.pngbin0 -> 951 bytes
-rw-r--r--ios/app/img/Icon-Spotlight-40@2x.pngbin0 -> 1528 bytes
-rw-r--r--ios/app/img/Icon.pngbin0 -> 1171 bytes
-rw-r--r--ios/app/img/Icon@2x.pngbin0 -> 1931 bytes
-rw-r--r--ios/app/img/iTunesArtworkbin0 -> 264721 bytes
-rw-r--r--ios/app/img/iTunesArtwork.pngbin0 -> 5655 bytes
-rw-r--r--ios/app/img/iTunesArtwork@2xbin0 -> 661346 bytes
-rw-r--r--ios/app/img/iTunesArtwork@2x.pngbin0 -> 9293 bytes
-rw-r--r--ios/app/img/locateUser.pngbin0 -> 1111 bytes
-rw-r--r--ios/app/img/locateUser@2x.pngbin0 -> 1696 bytes
-rw-r--r--ios/app/img/settings.pngbin0 -> 528 bytes
-rw-r--r--ios/app/img/settings@2x.pngbin0 -> 1130 bytes
-rw-r--r--ios/app/main.m10
-rw-r--r--ios/app/mapboxgl-app.gyp57
-rwxr-xr-xios/docs/install_docs.sh24
-rwxr-xr-xios/docs/remove_docs.sh7
m---------ios/mapbox-gl-cocoa0
-rw-r--r--ios/screenshot.pngbin0 -> 248491 bytes
-rw-r--r--mbgl.gyp15
-rw-r--r--platform/darwin/log_nslog.mm2
-rw-r--r--platform/darwin/string_nsstring.mm2
-rw-r--r--platform/default/headless_display.cpp6
-rw-r--r--platform/default/headless_view.cpp6
-rw-r--r--platform/ios/MGLMapView.mm1616
-rw-r--r--platform/ios/MGLStyleFunctionValue.m190
-rw-r--r--platform/ios/MGLTypes.m24
-rw-r--r--platform/ios/NSArray+MGLAdditions.m10
-rw-r--r--platform/ios/NSDictionary+MGLAdditions.m10
-rw-r--r--platform/ios/UIColor+MGLAdditions.m167
-rw-r--r--platform/ios/resources/Compass.pngbin0 -> 1736 bytes
-rw-r--r--platform/ios/resources/Compass@2x.pngbin0 -> 2376 bytes
-rw-r--r--platform/ios/resources/mapbox.pngbin0 -> 5947 bytes
-rw-r--r--platform/ios/resources/mapbox@2x.pngbin0 -> 10713 bytes
-rwxr-xr-x[-rw-r--r--]scripts/build-shaders.py0
-rwxr-xr-x[-rw-r--r--]scripts/build-version.py0
-rwxr-xr-xscripts/ios_travis/add-key.sh25
-rw-r--r--scripts/ios_travis/apple.crtbin0 -> 1063 bytes
-rw-r--r--scripts/ios_travis/ios-dist.cer.enc30
-rw-r--r--scripts/ios_travis/ios-dist.p12.enc32
-rw-r--r--scripts/ios_travis/ios-in-house.mobileprovision.enc153
-rwxr-xr-xscripts/ios_travis/remove-key.sh4
-rwxr-xr-xscripts/package_ios.sh75
-rwxr-xr-xscripts/travis_before_install.sh3
-rwxr-xr-xscripts/travis_script.sh7
-rw-r--r--src/mbgl/map/tile_parser.cpp6
-rw-r--r--src/mbgl/renderer/painter.cpp78
-rw-r--r--src/mbgl/renderer/painter.hpp5
-rw-r--r--src/mbgl/renderer/painter_fill.cpp44
-rw-r--r--src/mbgl/renderer/painter_line.cpp64
-rw-r--r--src/mbgl/shader/linepattern.fragment.glsl22
-rw-r--r--src/mbgl/shader/linepattern_shader.hpp9
-rw-r--r--src/mbgl/shader/pattern.fragment.glsl17
-rw-r--r--src/mbgl/shader/pattern.vertex.glsl9
-rw-r--r--src/mbgl/shader/pattern_shader.hpp9
-rw-r--r--src/mbgl/style/function_properties.cpp22
-rw-r--r--src/mbgl/style/piecewisefunction_properties.cpp52
-rw-r--r--src/mbgl/style/piecewisefunction_properties.hpp24
-rw-r--r--src/mbgl/style/property_fallback.cpp18
-rw-r--r--src/mbgl/style/property_key.hpp18
-rw-r--r--src/mbgl/style/property_value.hpp27
-rw-r--r--src/mbgl/style/style.cpp4
-rw-r--r--src/mbgl/style/style.hpp2
-rw-r--r--src/mbgl/style/style_layer.cpp131
-rw-r--r--src/mbgl/style/style_layer.hpp9
-rw-r--r--src/mbgl/style/style_layer_group.cpp4
-rw-r--r--src/mbgl/style/style_layer_group.hpp2
-rw-r--r--src/mbgl/style/style_parser.cpp358
-rw-r--r--src/mbgl/style/style_parser.hpp14
-rw-r--r--src/mbgl/style/style_properties.hpp10
-rw-r--r--src/mbgl/style/types.hpp10
-rw-r--r--src/mbgl/style/zoom_history.hpp38
-rw-r--r--src/mbgl/util/interpolate.hpp26
m---------styles0
-rw-r--r--test/fixtures/style_parser/function-string-bool-enum.info.json6
-rw-r--r--test/fixtures/style_parser/function-string-bool-enum.style.json43
-rw-r--r--test/ios/.gitignore3
-rw-r--r--test/ios/App-Info.plist45
-rw-r--r--test/ios/Bundle-Info.plist22
m---------test/ios/KIF0
-rw-r--r--test/ios/KIFTestActor+MapboxGL.h12
-rw-r--r--test/ios/KIFTestActor+MapboxGL.m24
-rw-r--r--test/ios/MGLTAppDelegate.h7
-rw-r--r--test/ios/MGLTAppDelegate.m18
-rw-r--r--test/ios/MGLTViewController.h5
-rw-r--r--test/ios/MGLTViewController.m18
-rw-r--r--test/ios/MapViewTests.h5
-rw-r--r--test/ios/MapViewTests.m353
-rw-r--r--test/ios/README.md9
-rw-r--r--test/ios/ios-tests.xcodeproj/project.pbxproj659
-rw-r--r--test/ios/ios-tests.xcodeproj/project.xcworkspace/contents.xcworkspacedata7
-rw-r--r--test/ios/ios-tests.xcodeproj/project.xcworkspace/xcshareddata/Mapbox GL Tests.xccheckout41
-rw-r--r--test/ios/ios-tests.xcodeproj/project.xcworkspace/xcshareddata/ios-tests.xccheckout65
-rw-r--r--test/ios/ios-tests.xcodeproj/xcshareddata/xcschemes/Mapbox GL Tests.xcscheme110
-rw-r--r--test/ios/main.m9
m---------test/suite0
147 files changed, 5440 insertions, 574 deletions
diff --git a/.gitignore b/.gitignore
index 4a61e999eb..3ee0136c6c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,7 @@
/build
/macosx/build
/linux/build
+/ios/build
/test/build
/test/node_modules
/include/mbgl/shader/shaders.hpp
diff --git a/.gitmodules b/.gitmodules
index 8242fab9b2..19de5b6f78 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,7 +1,3 @@
-[submodule "ios/mapbox-gl-cocoa"]
- path = ios/mapbox-gl-cocoa
- url = https://github.com/mapbox/mapbox-gl-cocoa.git
-
[submodule "test/suite"]
path = test/suite
url = https://github.com/mapbox/mapbox-gl-test-suite.git
@@ -14,6 +10,6 @@
path = styles
url = https://github.com/mapbox/mapbox-gl-styles.git
-[submodule "app/ios"]
- path = app/ios
- url = https://github.com/mapbox/mapbox-gl-cocoa.git \ No newline at end of file
+[submodule "test/ios/KIF"]
+ path = test/ios/KIF
+ url = https://github.com/mapbox/KIF.git
diff --git a/.travis.yml b/.travis.yml
index e42f9fde7b..ff578458cc 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -47,6 +47,13 @@ env:
- secure: "CHBiUM60TolDbQnn+4IRA/tvOKwKs3g9EDvv8YHSJMg3FuHmjKQkprBasvxf3hnTXg4WLZEubmeDcyJ6RRzPP5mMSr/hksYl0pSjj/6TUecE5fHPVVeN7txVqkpOBf9i45Y+iBUQMjBb1NnDK3pHXxpnAs1Q/pe7vReErj4GF1U="
- LD_LIBRARY_PATH: '/usr/local/lib'
- TERM: dumb
+ # begin iOS code signing
+ - secure: "I6Iu75X1E+js5tzijtKi1EGtIuBcA4/25nDYe0svV4HAtujY71ZJZ4eB6355CKhFXpLXrF3i7eKVX3v+zWS0QROPEWacgsqsvNg+Ba9cnznW/faUSOYekCfhzWd/6reYDM7KzKAQwSUHLk9JIWK/kkmi4r+vVJK7h+tjPllK5YA="
+ - IOS_APP_NAME="Mapbox GL"
+ - 'IOS_DEVELOPER_NAME="iPhone Distribution: Mapbox, Inc."'
+ - IOS_PROFILE_NAME="ios-in-house"
+ - secure: "nQqSM8rd7OHtV4MqmNqVnkrVHqxKqQsaWRYk4/nPdhbeVWtTtkk0df711LrF1TUtbEPEewHxYUvTZ/UXmwJNeoKdzTHavI8hnatRkgjyxGERPn1il1Otelht9I+LQQHf+plrpRjVWBrNIW0Zox1B3cqn6d3NglpbXrEQ2EjYGNA="
+ # end iOS code signing
before_install:
- if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then sudo service mysql stop; fi
@@ -65,10 +72,19 @@ install:
before_script:
# Set the core file limit to unlimited so a core file is generated upon crash
- ulimit -c unlimited -S
+ # begin iOS code signing
+ - openssl aes-256-cbc -k "$IOS_ENCRYPTION_SECRET" -in scripts/ios_travis/ios-in-house.mobileprovision.enc -d -a -out scripts/ios_travis/ios-in-house.mobileprovision
+ - openssl aes-256-cbc -k "$IOS_ENCRYPTION_SECRET" -in scripts/ios_travis/ios-dist.cer.enc -d -a -out scripts/ios_travis/ios-dist.cer
+ - openssl aes-256-cbc -k "$IOS_ENCRYPTION_SECRET" -in scripts/ios_travis/ios-dist.p12.enc -d -a -out scripts/ios_travis/ios-dist.p12
+ - ./scripts/ios_travis/add-key.sh
+ # end iOS code signing
script:
- ./scripts/travis_script.sh
+after_script:
+- ./scripts/ios_travis/remove-key.sh
+
notifications:
hipchat:
rooms:
diff --git a/Makefile b/Makefile
index d26aa2fbb7..ed7c234228 100644
--- a/Makefile
+++ b/Makefile
@@ -23,7 +23,7 @@ config/%.gypi: configure
#### Library builds ############################################################
-.PRECOIUS: Makefile/mbgl
+.PRECIOUS: Makefile/mbgl
Makefile/mbgl: config/$(HOST).gypi
deps/run_gyp mbgl.gyp $(CONFIG_$(HOST)) $(LIBS_$(HOST)) --generator-output=./build/$(HOST) -f make
@@ -36,6 +36,9 @@ standalone: Makefile/mbgl
install: Makefile/mbgl
LINK=`pwd`/gyp/link.py $(MAKE) -C build/$(HOST) BUILDTYPE=$(BUILDTYPE) install
+.PRECIOUS: Xcode/mbgl
+Xcode/mbgl: config/$(HOST).gypi
+ deps/run_gyp mbgl.gyp $(CONFIG_$(HOST)) $(LIBS_$(HOST)) --generator-output=./build/$(HOST) -f xcode
##### Test builds ##############################################################
@@ -101,15 +104,18 @@ xproj: xosx-proj
#### iOS application builds ####################################################
.PRECIOUS: Xcode/ios
-Xcode/ios: ios/mapbox-gl-cocoa/app/mapboxgl-app.gyp config/ios.gypi
- deps/run_gyp ios/mapbox-gl-cocoa/app/mapboxgl-app.gyp $(CONFIG_ios) $(LIBS_ios) --generator-output=./build/ios -f xcode
+Xcode/ios: ios/app/mapboxgl-app.gyp config/ios.gypi
+ deps/run_gyp ios/app/mapboxgl-app.gyp $(CONFIG_ios) $(LIBS_ios) --generator-output=./build/ios -f xcode
.PHONY: ios-proj ios run-ios
ios-proj: Xcode/ios
- open ./build/ios/ios/mapbox-gl-cocoa/app/mapboxgl-app.xcodeproj
+ open ./build/ios/ios/app/mapboxgl-app.xcodeproj
ios: Xcode/ios
- xcodebuild -sdk iphonesimulator ARCHS=x86_64 -project ./build/ios/ios/mapbox-gl-cocoa/app/mapboxgl-app.xcodeproj -configuration $(BUILDTYPE) -target iosapp -jobs `sysctl -n hw.ncpu`
+ xcodebuild -sdk iphoneos ARCHS="arm64 armv7 armv7s" PROVISIONING_PROFILE="2b532944-bf3d-4bf4-aa6c-a81676984ae8" -project ./build/ios/ios/app/mapboxgl-app.xcodeproj -configuration Release -target iosapp -jobs `sysctl -n hw.ncpu`
+
+isim: Xcode/ios
+ xcodebuild -sdk iphonesimulator ARCHS="x86_64 i386" -project ./build/ios/ios/app/mapboxgl-app.xcodeproj -configuration Debug -target iosapp -jobs `sysctl -n hw.ncpu`
# Legacy name
iproj: ios-proj
@@ -197,8 +203,7 @@ clean: clear_sqlite_cache clear_xcode_cache
-rm -rf ./build/
-rm -rf ./macosx/build
-rm -rf ./linux/build
- -rm -rf ./ios/mapbox-gl-cocoa/build
- -rm -rf ./ios/mapbox-gl-cocoa/app/build
+ -rm -rf ./ios/build
-rm -rf ./test/build
-rm -rf ./config/*.gypi
-rm -rf ./android/java/build ./android/java/app/build ./android/java/lib/build
diff --git a/README.md b/README.md
index ab24baff36..0a19e7ecbf 100644
--- a/README.md
+++ b/README.md
@@ -28,7 +28,7 @@ Be sure to pull down all submodules first:
## OS X
-On OS X, we are using `zlib`, `SQLite3`, `libcurl` and `libpng` provided by the operating system. In addition to that, you need to have the Boost headers installed and ImageMagick for running tests. To install all prerequisites, use [Homebrew](http://brew.sh/) and type `brew install pkg-config boost imagemagick`.
+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`.
To create projects, you can run:
@@ -40,22 +40,19 @@ To create projects, you can run:
Note that you can't have more than one project in Xcode open at a time since they the static library project is shared across the OS X, Linux and iOS project.
-Target OS: 10.9+
+Target OS X: 10.9+
## iOS
-iOS makes use of a Cocoa-specific API called [`mapbox-gl-cocoa`](https://github.com/mapbox/mapbox-gl-cocoa). If you are just interested in running Mapbox GL on iOS and not developing with it, head to that project and you can use this library as a pre-built static library instead. A `UIView` interface to the map view and bundle resources are provided there.
+If you merely want to install the library for iOS and try it out as an Objective-C consumer, run `./scripts/package_ios.sh`. It requires the Boost headers to be installed, so use [Homebrew](http://brew.sh/) to install them (`brew install boost`). The packaging script will produce the statically-linked `libMapboxGL.a`, `MapboxGL.bundle` for resources, and a `Headers` folder.
-If you intend to develop here, `mapbox-gl-cocoa` is included as a submodule of the overall build setup.
+If you want to build from source and/or contribute to development of the project, run `make iproj`, which will create and open an Xcode project which can build the entire library from source as well as an Objective-C test app.
-You can run `make iproj` to create and open an Xcode project with an iOS-specific view controller housing. This will automatically install required dependencies as well.
+Target devices: iPhone 4S and above (5, 5c, 5s, 6, 6 Plus) and iPad 2 and above (3, 4, Mini, Air, Mini 2, Air 2).
-Note that if you are doing OS X development as well, to toggle from OS X back to iOS, you will need to `make iproj` again.
+Target iOS: 7.0 through 8.1
-Target devices: iPhone 4S and above (5, 5c, 5s, 6, 6 Plus) and iPad 2 and above (3, 4, Mini , Air, Mini 2, Air 2).
-
-Target OS: 7.0+
## Linux
@@ -103,7 +100,11 @@ Set an access token as described below, and then run:
make run-linux
-## Android (on Linux)
+## Android
+
+Target devices: All Android devices on Android 4.0 or newer (API level >= 14)
+
+### On Linux
Install a few build dependencies:
@@ -128,7 +129,7 @@ Run:
You can then open `android/java` in Android Studio via "Import Non-Android Studio Project".
-## Android (on OS X)
+### On OS X
Install Oracle JDK 7 (requires license agreement) from http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html
diff --git a/android/scripts/common.sh b/android/scripts/common.sh
index 5c214fe256..69f41997c7 100755
--- a/android/scripts/common.sh
+++ b/android/scripts/common.sh
@@ -16,7 +16,3 @@ make android BUILDTYPE=$BUILDTYPE JOBS=$JOBS
aws s3 cp ./android/java/app/build/outputs/apk/app-debug.apk s3://mapbox-gl-testing/android/${NAME}/app-debug.apk
aws s3 cp ./android/java/app/build/outputs/apk/app-release-unsigned.apk s3://mapbox-gl-testing/android/${NAME}/app-release-unsigned.apk
-
-if [[ $TESTMUNK == "yes" ]]; then
- ./android/scripts/upload-testmunk.sh ./android/java/app/build/outputs/apk/app-debug.apk
-fi
diff --git a/android/scripts/upload-testmunk.sh b/android/scripts/upload-testmunk.sh
deleted file mode 100755
index 3fd1e70a1a..0000000000
--- a/android/scripts/upload-testmunk.sh
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-set -o pipefail
-
-EMAIL="leith@mapbox.com"
-APP_NAME="Mapbox"
-APK_PATH=$1
-
-echo "submitting testrun to testmunk"
-
-echo "uploading features..."
-
-zip -r android/test/features.zip android/test/features/
-RESPONSE=$(curl -f -H "Accept: application/vnd.testmunk.v1+json" -F "file=@android/test/features.zip" "https://${TESTMUNK_KEY}@api.testmunk.com/apps/${APP_NAME}/testcases")
-
-echo "uploading apk..."
-
-RESPONSE=$(curl -f -H "Accept: application/vnd.testmunk.v1+json" -F "file=@${APK_PATH}" -F "email=${EMAIL}" -F "autoStart=true" -F "public=true" "https://${TESTMUNK_KEY}@api.testmunk.com/apps/${APP_NAME}/testruns")
-
-TESTRUN_ID=$(echo "${RESPONSE}" | jq -r '.id')
-TESTRUN_NAME=$(echo "${RESPONSE}" | jq -r '.name')
-
-echo "successully uploaded to Testmunk as '${TESTRUN_NAME}'"
-echo "public link to test results: https://www.testmunk.com/testrun/${TESTRUN_ID}"
diff --git a/android/test/.gitignore b/android/test/.gitignore
deleted file mode 100644
index 68c12ecc03..0000000000
--- a/android/test/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-features.zip
diff --git a/android/test/features/load-app.feature b/android/test/features/load-app.feature
deleted file mode 100644
index e6bd1158a4..0000000000
--- a/android/test/features/load-app.feature
+++ /dev/null
@@ -1,5 +0,0 @@
-Feature: Testapp V.1.2
-
-Scenario: 1) Start app and load map
- Given I await for 5 seconds
-
diff --git a/android/test/features/step_definitions/calabash_steps.rb b/android/test/features/step_definitions/calabash_steps.rb
deleted file mode 100755
index 6d80ff0c97..0000000000
--- a/android/test/features/step_definitions/calabash_steps.rb
+++ /dev/null
@@ -1 +0,0 @@
-require 'calabash-android/calabash_steps'
diff --git a/android/test/features/step_definitions/my_steps.rb b/android/test/features/step_definitions/my_steps.rb
deleted file mode 100644
index 3239322ddc..0000000000
--- a/android/test/features/step_definitions/my_steps.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-
-Then /^I await for ([\d\.]+) second(?:s)?$/ do |num_seconds|
-num_seconds = num_seconds.to_f
-sleep num_seconds
-
-end
diff --git a/android/test/features/support/app_installation_hooks.rb b/android/test/features/support/app_installation_hooks.rb
deleted file mode 100755
index 2e0a203bc5..0000000000
--- a/android/test/features/support/app_installation_hooks.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-require 'calabash-android/management/app_installation'
-
-AfterConfiguration do |config|
- FeatureNameMemory.feature_name = nil
-end
-
-Before do |scenario|
- @scenario_is_outline = (scenario.class == Cucumber::Ast::OutlineTable::ExampleRow)
- if @scenario_is_outline
- scenario = scenario.scenario_outline
- end
-
- feature_name = scenario.feature.title
- if FeatureNameMemory.feature_name != feature_name \
- or ENV["RESET_BETWEEN_SCENARIOS"] == "1"
- if ENV["RESET_BETWEEN_SCENARIOS"] == "1"
- log "New scenario - reinstalling apps"
- else
- log "First scenario in feature - reinstalling apps"
- end
-
- uninstall_apps
- install_app(ENV["TEST_APP_PATH"])
- install_app(ENV["APP_PATH"])
- FeatureNameMemory.feature_name = feature_name
- FeatureNameMemory.invocation = 1
- else
- FeatureNameMemory.invocation += 1
- end
-end
-
-FeatureNameMemory = Class.new
-class << FeatureNameMemory
- @feature_name = nil
- attr_accessor :feature_name, :invocation
-end
diff --git a/android/test/features/support/app_life_cycle_hooks.rb b/android/test/features/support/app_life_cycle_hooks.rb
deleted file mode 100755
index 63d4678d9a..0000000000
--- a/android/test/features/support/app_life_cycle_hooks.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-require 'calabash-android/management/adb'
-require 'calabash-android/operations'
-
-Before do |scenario|
- start_test_server_in_background
-end
-
-After do |scenario|
- if scenario.failed?
- screenshot_embed
- end
- shutdown_test_server
-end
diff --git a/android/test/features/support/env.rb b/android/test/features/support/env.rb
deleted file mode 100755
index bb2505d458..0000000000
--- a/android/test/features/support/env.rb
+++ /dev/null
@@ -1 +0,0 @@
-require 'calabash-android/cucumber'
diff --git a/android/test/features/support/hooks.rb b/android/test/features/support/hooks.rb
deleted file mode 100755
index e69de29bb2..0000000000
--- a/android/test/features/support/hooks.rb
+++ /dev/null
diff --git a/configure b/configure
index fec0bfd311..b818faa07a 100755
--- a/configure
+++ b/configure
@@ -92,7 +92,6 @@ CONFIG="# Do not edit. Generated by the configure script.
'libraries': []
},
'variables': {
- 'python': '${PYTHON}',
"
if [ ! -z ${BOOST_VERSION} ]; then
diff --git a/gyp/install.gypi b/gyp/install.gypi
index 4ed32eea14..01857aa562 100644
--- a/gyp/install.gypi
+++ b/gyp/install.gypi
@@ -1,50 +1,46 @@
{
- 'conditions': [
- ['install_prefix != ""', {
- 'targets': [
- { 'target_name': 'install',
- 'type': 'none',
- 'hard_dependency': 1,
- 'dependencies': [
- 'core',
- 'platform-<(platform_lib)',
- 'http-<(http_lib)',
- 'asset-<(asset_lib)',
- 'cache-<(cache_lib)',
- 'headless-<(headless_lib)',
- 'standalone',
- ],
+ 'targets': [
+ { 'target_name': 'install2',
+ 'type': 'none',
+ 'hard_dependency': 1,
+ 'dependencies': [
+ 'core',
+ 'platform-<(platform_lib)',
+ 'http-<(http_lib)',
+ 'asset-<(asset_lib)',
+ 'cache-<(cache_lib)',
+ 'headless-<(headless_lib)',
+ 'standalone',
+ ],
- 'copies': [
- { 'files': [ '<(PRODUCT_DIR)/libmbgl.a' ], 'destination': '<(install_prefix)/lib' },
- { 'files': [ '<(SHARED_INTERMEDIATE_DIR)/include/mbgl/util/version.hpp' ], 'destination': '<(install_prefix)/include/mbgl/util' },
- ],
+ 'copies': [
+ { 'files': [ '<(PRODUCT_DIR)/libmbgl.a' ], 'destination': '<(install_prefix)/lib' },
+ { 'files': [ '<(SHARED_INTERMEDIATE_DIR)/include/mbgl/util/version.hpp' ], 'destination': '<(install_prefix)/include/mbgl/util' },
+ ],
- 'actions': [
- {
- 'action_name': 'Copy header files',
- 'inputs': [ '../include/mbgl/mbgl.hpp' ],
- 'outputs': [ '../include/mbgl/mbgl.hpp' ],
- 'action': [ 'cp', '-r', 'include', '<(install_prefix)/' ]
- },
+ 'actions': [
+ {
+ 'action_name': 'Copy header files',
+ 'inputs': [ '../include/mbgl/mbgl.hpp' ],
+ 'outputs': [ '<(install_prefix)/include/mbgl/mbgl.hpp' ],
+ 'action': [ 'cp', '-r', 'include', '<(install_prefix)/' ]
+ },
- { 'action_name': 'mbgl-config',
- 'inputs': [
- '../utils/mbgl-config/mbgl-config.template.sh',
- '../utils/mbgl-config/build.sh',
- ],
- 'outputs': [
- '<(install_prefix)/bin/mbgl-config',
- ],
- 'action': [
- './utils/mbgl-config/build.sh',
- '<(install_prefix)',
- '<(PRODUCT_DIR)/libmbgl.a.ldflags',
- ]
- }
+ { 'action_name': 'mbgl-config',
+ 'inputs': [
+ '../utils/mbgl-config/mbgl-config.template.sh',
+ '../utils/mbgl-config/build.sh',
+ ],
+ 'outputs': [
+ '<(install_prefix)/bin/mbgl-config',
+ ],
+ 'action': [
+ './utils/mbgl-config/build.sh',
+ '<(install_prefix)',
+ '<(PRODUCT_DIR)/libmbgl.a.ldflags',
]
- },
- ],
- }],
+ }
+ ]
+ },
],
}
diff --git a/gyp/platform-ios.gypi b/gyp/platform-ios.gypi
index bb1b450728..a8a827af65 100644
--- a/gyp/platform-ios.gypi
+++ b/gyp/platform-ios.gypi
@@ -15,6 +15,19 @@
'../platform/darwin/application_root.mm',
'../platform/darwin/asset_root.mm',
'../platform/darwin/image.mm',
+ '../platform/darwin/reachability.m',
+ '../include/mbgl/ios/MGLMapView.h',
+ '../platform/ios/MGLMapView.mm',
+ '../include/mbgl/ios/MGLStyleFunctionValue.h',
+ '../platform/ios/MGLStyleFunctionValue.m',
+ '../include/mbgl/ios/MGLTypes.h',
+ '../platform/ios/MGLTypes.m',
+ '../include/mbgl/ios/NSArray+MGLAdditions.h',
+ '../platform/ios/NSArray+MGLAdditions.m',
+ '../include/mbgl/ios/NSDictionary+MGLAdditions.h',
+ '../platform/ios/NSDictionary+MGLAdditions.m',
+ '../include/mbgl/ios/UIColor+MGLAdditions.h',
+ '../platform/ios/UIColor+MGLAdditions.m',
],
'variables': {
@@ -26,8 +39,14 @@
'<@(uv_static_libs)',
],
'ldflags': [
+ '-framework CoreGraphics',
+ '-framework CoreLocation',
'-framework ImageIO',
+ '-framework GLKit',
'-framework MobileCoreServices',
+ '-framework OpenGLES',
+ '-framework SystemConfiguration',
+ '-framework UIKit',
],
},
@@ -37,6 +56,7 @@
'xcode_settings': {
'OTHER_CPLUSPLUSFLAGS': [ '<@(cflags_cc)' ],
+ 'CLANG_ENABLE_OBJC_ARC': 'YES',
},
'link_settings': {
@@ -50,6 +70,9 @@
'include_dirs': [
'../include',
],
+ 'mac_bundle_resources': [
+ '<!@(find ./platform/ios/resources -type f)',
+ ],
},
},
],
diff --git a/gyp/platform-osx.gypi b/gyp/platform-osx.gypi
index e3fe7b0d53..011f26e836 100644
--- a/gyp/platform-osx.gypi
+++ b/gyp/platform-osx.gypi
@@ -39,6 +39,7 @@
'xcode_settings': {
'OTHER_CPLUSPLUSFLAGS': [ '<@(cflags_cc)' ],
+ 'CLANG_ENABLE_OBJC_ARC': 'YES',
},
'link_settings': {
diff --git a/gyp/shaders.gypi b/gyp/shaders.gypi
index edbcb61f23..fbf8e3197b 100644
--- a/gyp/shaders.gypi
+++ b/gyp/shaders.gypi
@@ -16,7 +16,7 @@
'<(SHARED_INTERMEDIATE_DIR)/src/shader/shaders_gl.cpp',
'<(SHARED_INTERMEDIATE_DIR)/src/shader/shaders_gles2.cpp',
],
- 'action': ['<@(python)', 'scripts/build-shaders.py', '<(SHARED_INTERMEDIATE_DIR)', '<@(_inputs)'],
+ 'action': ['scripts/build-shaders.py', '<(SHARED_INTERMEDIATE_DIR)', '<@(_inputs)'],
}
],
'direct_dependent_settings': {
diff --git a/gyp/version.gypi b/gyp/version.gypi
index 68f777f0a4..1cc2eb351f 100644
--- a/gyp/version.gypi
+++ b/gyp/version.gypi
@@ -13,7 +13,7 @@
'outputs': [
'<(SHARED_INTERMEDIATE_DIR)/include/mbgl/util/version.hpp',
],
- 'action': ['<@(python)', '<@(_inputs)', '<(SHARED_INTERMEDIATE_DIR)', '<!@(git describe --tags --always --abbrev=0)', '<!@(git rev-parse HEAD)'],
+ 'action': ['<@(_inputs)', '<(SHARED_INTERMEDIATE_DIR)', '<!@(git describe --tags --always --abbrev=0)', '<!@(git rev-parse HEAD)'],
}
],
'direct_dependent_settings': {
diff --git a/include/mbgl/ios/MGLMapView.h b/include/mbgl/ios/MGLMapView.h
new file mode 100644
index 0000000000..6535138d3b
--- /dev/null
+++ b/include/mbgl/ios/MGLMapView.h
@@ -0,0 +1,203 @@
+#import <UIKit/UIKit.h>
+#import <CoreLocation/CoreLocation.h>
+
+@protocol MGLMapViewDelegate;
+
+/** An MGLMapView object provides an embeddable map interface, similar to the one provided by Apple's MapKit. You use this class to display map information and to manipulate the map contents from your application. You can center the map on a given coordinate, specify the size of the area you want to display, and style the features of the map to fit your application's use case.
+*
+* Use of MGLMapView requires a Mapbox API access token. Obtain an access token on the [Mapbox account page](https://www.mapbox.com/account/apps/). If you instantiate an MGLMapView from Interface Builder, rendering of the map won't begin until the accessToken property has been set.
+*
+* @warning Please note that you are responsible for getting permission to use the map data, and for ensuring your use adheres to the relevant terms of use. */
+@interface MGLMapView : UIView
+
+#pragma mark - Initializing a Map View
+
+/** @name Initializing a Map View */
+
+/** Initialize a map view with a given frame, style, and access token.
+* @param frame The frame with which to initialize the map view.
+* @param styleJSON The map stylesheet as JSON text.
+* @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 styleJSON:(NSString *)styleJSON accessToken:(NSString *)accessToken;
+
+/** Initialize a map view with a given frame, the default style, and an 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;
+
+- (instancetype)initWithFrame:(CGRect)frame __attribute__((unavailable("Instantiating an MGLMapView requires setting a style and/or an access token.")));
+
+#pragma mark - Authorizing Access
+
+/** @name Authorizing Access */
+
+/** Sets a Mapbox API access token for the map view.
+* @param accessToken A Mapbox API token. */
+- (void)setAccessToken:(NSString *)accessToken;
+
+#pragma mark - Managing Constraints
+
+/** @name Managing Constraints */
+
+/** A view controller whose top and bottom layout guides to use for proper setup of constraints in the map view internals.
+*
+* Certain components of the map view, such as the heading compass and the data attribution button, need to be aware of the view controller layout in order to avoid positioning content under a top navigation bar or a bottom toolbar. */
+@property (nonatomic, weak) UIViewController *viewControllerForLayoutGuides;
+
+#pragma mark - Accessing Map Properties
+
+/** @name Accessing Map Properties */
+
+/** A Boolean value that determines whether the user may zoom the map.
+*
+* This property controls only user interactions with the map. If you set the value of this property to `NO`, you may still change the map zoom programmatically.
+*
+* The default value of this property is `YES`. */
+@property(nonatomic, getter=isZoomEnabled) BOOL zoomEnabled;
+
+/** A Boolean value that determines whether the user may scroll around the map.
+*
+* This property controls only user interactions with the map. If you set the value of this property to `NO`, you may still change the map location programmatically.
+*
+* The default value of this property is `YES`. */
+@property(nonatomic, getter=isScrollEnabled) BOOL scrollEnabled;
+
+/** A Boolean value that determines whether the user may rotate the map.
+*
+* This property controls only user interactions with the map. If you set the value of this property to `NO`, you may still rotate the map programmatically.
+*
+* The default value of this property is `YES`. */
+@property(nonatomic, getter=isRotateEnabled) BOOL rotateEnabled;
+
+#pragma mark - Accessing the Delegate
+
+/** @name Accessing the Delegate */
+
+// TODO
+@property(nonatomic, weak) id<MGLMapViewDelegate> delegate;
+
+#pragma mark - Manipulating the Visible Portion of the Map
+
+/** @name Manipulating the Visible Portion of the Map */
+
+/** The map coordinate at the center of the map view.
+*
+* Changing the value in this property centers the map on the new coordinate without changing the current zoom level.
+*
+* Changing the value of this property updates the map view immediately. If you want to animate the change, use the setCenterCoordinate:animated: method instead. */
+@property (nonatomic) CLLocationCoordinate2D centerCoordinate;
+
+/** Changes the center coordinate of the map and optionally animates the change.
+* @param coordinate The new center coordinate for the map.
+* @param animated Specify `YES` if you want the map view to scroll to the new location or `NO` if you want the map to display the new location immediately.
+*
+* Changing the center coordinate centers the map on the new coordinate without changing the current zoom level. */
+- (void)setCenterCoordinate:(CLLocationCoordinate2D)coordinate animated:(BOOL)animated;
+
+/** The zoom level of the map view.
+*
+* Changing the value in this property zooms the map in or out without changing the center coordinate. At zoom level 0, tiles cover the entire world map; at zoom level 1, tiles cover 1/4 of the world; at zoom level 2, tiles cover 1/16 of the world, and so on.
+*
+* Changing the value of this property updates the map view immediately. If you want to animate the change, use the setZoomLevel:animated: method instead. */
+@property (nonatomic) double zoomLevel;
+
+/** Changes the zoom level of the map and optionally animates the change.
+* @param zoomLevel The new zoom level for the map.
+* @param animated Specify `YES` if you want the map view to animate the change to the new zoom level or `NO` if you want the map to display the new zoom level immediately.
+*
+* Changing the zoom level scales the map without changing the current center coordinate. At zoom level 0, tiles cover the entire world map; at zoom level 1, tiles cover 1/4 of the world; at zoom level 2, tiles cover 1/16 of the world, and so on. */
+- (void)setZoomLevel:(double)zoomLevel animated:(BOOL)animated;
+
+/** Changes the center coordinate and zoom level of the and optionally animates the change.
+* @param centerCoordinate The new center coordinate for the map.
+* @param zoomLevel The new zoom level for the map.
+* @param animated Specify `YES` if you want the map view to animate scrolling and zooming to the new location or `NO` if you want the map to display the new location immediately. */
+- (void)setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate zoomLevel:(double)zoomLevel animated:(BOOL)animated;
+
+/** The heading of the map (measured in degrees) relative to true north.
+*
+* The value `0` means that the top edge of the map view corresponds to true north. The value `90` means the top of the map is pointing due east. The value `180` means the top of the map points due south, and so on. */
+@property (nonatomic) CLLocationDirection direction;
+
+/** Changes the heading of the map and optionally animates the changes.
+* @param direction The heading of the map (measured in degrees) relative to true north.
+* @param animated Specify `YES` if you want the map view to animate the change to the new heading or `NO` if you want the map to display the new heading immediately.
+*
+* Changing the heading rotates the map without changing the current center coordinate or zoom level. */
+- (void)setDirection:(CLLocationDirection)direction animated:(BOOL)animated;
+
+/** Resets the map rotation to a northern heading. */
+- (void)resetNorth;
+
+#pragma mark - Styling the Map
+
+/** @name Styling the Map */
+
+/** Sets the map style.
+* @param styleJSON The map stylesheet as JSON text. */
+- (void)setStyleJSON:(NSString *)styleJSON;
+
+/** Returns the raw JSON style as a native dictionary object. */
+- (NSDictionary *)getRawStyle;
+
+/** Sets the raw JSON style as a native dictionary object with a transition animation.
+* @param style The style JSON as a dictionary object. */
+- (void)setRawStyle:(NSDictionary *)style;
+
+/** Returns the names of the styles bundled with the library. */
+- (NSArray *)bundledStyleNames;
+
+/** Sets the map style to a named, bundled style.
+* @param styleName The map style name to use. */
+- (void)useBundledStyleNamed:(NSString *)styleName;
+
+#pragma mark - Debugging
+
+/** @name Debugging */
+
+/** A Boolean value that determines whether map debugging information is shown.
+*
+* The default value of this property is `NO`. */
+@property (nonatomic, getter=isDebugActive) BOOL debugActive;
+
+/** Toggle the current value of debugActive. */
+- (void)toggleDebug;
+
+/** Resets the map to the minimum zoom level, a center coordinate of (0, 0), and a northern heading. */
+- (void)resetPosition;
+
+@end
+
+// TODO
+@protocol MGLMapViewDelegate <NSObject>
+
+@optional
+
+// Responding to Map Position Changes
+
+// TODO
+- (void)mapView:(MGLMapView *)mapView regionWillChangeAnimated:(BOOL)animated;
+
+// TODO
+- (void)mapView:(MGLMapView *)mapView regionDidChangeAnimated:(BOOL)animated;
+
+// Loading the Map Data
+
+// TODO
+- (void)mapViewWillStartLoadingMap:(MGLMapView *)mapView;
+
+// TODO
+- (void)mapViewDidFinishLoadingMap:(MGLMapView *)mapView;
+
+// TODO
+- (void)mapViewDidFailLoadingMap:(MGLMapView *)mapView withError:(NSError *)error;
+
+// TODO
+- (void)mapViewWillStartRenderingMap:(MGLMapView *)mapView;
+
+// TODO
+- (void)mapViewDidFinishRenderingMap:(MGLMapView *)mapView fullyRendered:(BOOL)fullyRendered;
+
+@end
diff --git a/include/mbgl/ios/MGLStyleFunctionValue.h b/include/mbgl/ios/MGLStyleFunctionValue.h
new file mode 100644
index 0000000000..27a867548b
--- /dev/null
+++ b/include/mbgl/ios/MGLStyleFunctionValue.h
@@ -0,0 +1,23 @@
+#import <UIKit/UIKit.h>
+
+@interface MGLStyleFunctionValue : NSObject
+
++ (instancetype)linearFunctionWithBaseZoomLevel:(CGFloat)zBase
+ initialValue:(CGFloat)val
+ slope:(CGFloat)slope
+ minimumValue:(CGFloat)min
+ maximumValue:(CGFloat)max;
+
++ (instancetype)exponentialFunctionWithBaseZoomLevel:(CGFloat)zBase
+ initialValue:(CGFloat)val
+ slope:(CGFloat)slope
+ minimumValue:(CGFloat)min
+ maximumValue:(CGFloat)max;
+
++ (instancetype)minimumZoomLevelFunction:(CGFloat)minimumZoom;
+
++ (instancetype)maximumZoomLevelFunction:(CGFloat)maximumZoom;
+
++ (instancetype)stopsFunctionWithZoomLevelsAndValues:(NSNumber *)firstZoom, ... NS_REQUIRES_NIL_TERMINATION;
+
+@end
diff --git a/include/mbgl/ios/MGLTypes.h b/include/mbgl/ios/MGLTypes.h
new file mode 100644
index 0000000000..7a17445770
--- /dev/null
+++ b/include/mbgl/ios/MGLTypes.h
@@ -0,0 +1,17 @@
+#import <Foundation/Foundation.h>
+
+// style property value types
+//
+extern NSString *const MGLStyleValueTypeBoolean;
+extern NSString *const MGLStyleValueTypeNumber;
+extern NSString *const MGLStyleValueTypeNumberPair;
+extern NSString *const MGLStyleValueTypeColor;
+extern NSString *const MGLStyleValueTypeString;
+
+// style property function types
+//
+extern NSString *const MGLStyleValueTypeFunctionMinimumZoom;
+extern NSString *const MGLStyleValueTypeFunctionMaximumZoom;
+extern NSString *const MGLStyleValueTypeFunctionLinear;
+extern NSString *const MGLStyleValueTypeFunctionExponential;
+extern NSString *const MGLStyleValueTypeFunctionStops;
diff --git a/include/mbgl/ios/NSArray+MGLAdditions.h b/include/mbgl/ios/NSArray+MGLAdditions.h
new file mode 100644
index 0000000000..4b87614330
--- /dev/null
+++ b/include/mbgl/ios/NSArray+MGLAdditions.h
@@ -0,0 +1,7 @@
+#import <Foundation/Foundation.h>
+
+@interface NSArray (MGLAdditions)
+
+- (NSMutableArray *)deepMutableCopy;
+
+@end
diff --git a/include/mbgl/ios/NSDictionary+MGLAdditions.h b/include/mbgl/ios/NSDictionary+MGLAdditions.h
new file mode 100644
index 0000000000..04c3396d41
--- /dev/null
+++ b/include/mbgl/ios/NSDictionary+MGLAdditions.h
@@ -0,0 +1,7 @@
+#import <Foundation/Foundation.h>
+
+@interface NSDictionary (MGLAdditions)
+
+- (NSMutableDictionary *)deepMutableCopy;
+
+@end
diff --git a/include/mbgl/ios/UIColor+MGLAdditions.h b/include/mbgl/ios/UIColor+MGLAdditions.h
new file mode 100644
index 0000000000..3758c04eef
--- /dev/null
+++ b/include/mbgl/ios/UIColor+MGLAdditions.h
@@ -0,0 +1,11 @@
+#import <UIKit/UIKit.h>
+
+@interface UIColor (MGLAdditions)
+
++ (UIColor *)colorWithRGBAString:(NSString *)rgbaString;
+- (NSString *)rgbaStringFromColor;
+
++ (UIColor *)colorWithHexString:(NSString *)hexString;
+- (NSString *)hexStringFromColor;
+
+@end
diff --git a/include/mbgl/platform/default/glx.h b/include/mbgl/platform/default/glx.h
deleted file mode 100644
index 6b7d9a3df9..0000000000
--- a/include/mbgl/platform/default/glx.h
+++ /dev/null
@@ -1,2 +0,0 @@
-#include <GL/glx.h>
-#undef None
diff --git a/include/mbgl/platform/default/headless_view.hpp b/include/mbgl/platform/default/headless_view.hpp
index 2fad430e6e..ba318c2b41 100644
--- a/include/mbgl/platform/default/headless_view.hpp
+++ b/include/mbgl/platform/default/headless_view.hpp
@@ -5,9 +5,12 @@
#define MBGL_USE_CGL 1
#else
#define GL_GLEXT_PROTOTYPES
-#include <mbgl/platform/default/glx.h>
#define MBGL_USE_GLX 1
-#undef Status
+typedef struct _XDisplay Display;
+typedef struct __GLXcontextRec* GLXContext;
+typedef struct __GLXFBConfigRec* GLXFBConfig;
+typedef long unsigned int XID;
+typedef XID GLXPbuffer;
#endif
#include <mbgl/map/view.hpp>
diff --git a/ios/FAQ.md b/ios/FAQ.md
new file mode 100644
index 0000000000..19ae74fdba
--- /dev/null
+++ b/ios/FAQ.md
@@ -0,0 +1,49 @@
+# Mapbox GL for iOS FAQ
+
+Mapbox GL is a completely new renderer technology which will eventually replace and/or merge Mapbox's existing iOS toolsets, the [Mapbox iOS SDK](http://www.mapbox.com/mapbox-ios-sdk/) and [MBXMapKit](https://www.mapbox.com/mbxmapkit/). This FAQ shares our current plans for that migration. The plans are subject to change as Mapbox GL matures, but we wanted to both clarify our thinking as well as set expectations for where things are headed.
+
+### When will Mapbox GL be released?
+
+The library is [open source](https://github.com/mapbox/mapbox-gl-native) right now, but an official, production-recommended release will come later in 2015.
+
+### What iOS versions will be supported?
+
+Mapbox GL currently supports iOS 7 and later. This will continue to be the case. MBXMapKit also supports iOS 7 and greater, but this does mean that moving from the iOS SDK will leave behind iOS 5 and 6.
+
+### Will the API be similar to the Mapbox iOS SDK/MBXMapKit/MapKit?
+
+Yes. We are shooting for bringing the Mapbox GL API in line with Apple's MapKit for the easiest transition ability.
+
+MBXMapKit is already an add-on to MapKit, so Apple's `MKMapView` API is used directly.
+
+The Mapbox iOS SDK is "workalike", since it descends from an [upstream open source project](https://github.com/Alpstein/route-me) that predates Apple's own MapKit. It uses similar concepts like annotations (with the difference that the map view delegate provides `CALayer` instances instead of `UIView`, the intention being that Mapbox GL will support `UIView`), similar API for managing the map view center, bounds, and zoom levels, and an `RMUserLocation` API that is very much like `MKUserLocation`. But the Mapbox iOS SDK also features unique APIs like extensible tile sources, offline caching, UTFGrid interactivity, and point annotation clustering.
+
+### Will the iOS SDK's extra APIs make it over to Mapbox GL?
+
+The intention is yes. This includes:
+
+#### Tile sources
+
+The SDK uses the `RMTileSource` protocol to allow for extensible remote and local raster tile sources, as well as custom on-the-fly raster tile generation. We are planning to build a transitional API in Mapbox GL that acts as a wrapper to `RMTileSource` so that existing sources can be used to plug into the main Mapbox GL source API.
+
+#### Offline caching
+
+Both the Mapbox iOS SDK and MBXMapKit feature robust support for offline caching of raster tiles and the SDK features support for [MBTiles](http://mbtiles.org) tile sources.
+
+We're likely going to take a similar approach to both performance caching and offline maps as is done with the `0.3.0` release of MBXMapKit. That is, `NSURLCache` will be used for performance caching and specific, separate offline map database management will be used for fetching areas of map tiles. This is a cleaner design than exists in the SDK, which combines performance and offline caches and has a non-intuitive configuration API.
+
+We will likely also port over MBTiles support to Mapbox GL.
+
+#### UTFGrid interactivity
+
+[UTFGrid](https://github.com/mapbox/utfgrid-spec) is a technology for enabling interactive features in raster tiles. Support for the technology is provided in the Mapbox iOS SDK with categories in `RMInteractiveSource` in combination with either `RMMapboxSource` or `RMMBTilesSource`.
+
+Mapbox GL obsoletes this sort of pixel-based interactivity since the source data used for rendering can be queried on-device, so we are unlikely to port UTFGrid over to GL.
+
+#### Annotation clustering
+
+We're planning on adding support for point clustering to Mapbox GL and it will likely have a similar API to the SDK's. Clustering will happen on an individual style layer level. The performance for clustering, as well as annotations in general, will hopefully be better than in the SDK, since it will be done with direct OpenGL rendering instead of Core Animation layers.
+
+### What will the migration path look like?
+
+Ideally, the migration will be pretty lightweight because of the APIs supported above. There may be slight syntax changes, but they likely won't be more than would be expected from something like Mapbox iOS SDK version `1.x` to a hypothetical `2.x`.
diff --git a/ios/MapboxGL.podspec b/ios/MapboxGL.podspec
new file mode 100644
index 0000000000..6cdec1826d
--- /dev/null
+++ b/ios/MapboxGL.podspec
@@ -0,0 +1,33 @@
+Pod::Spec.new do |m|
+
+ m.name = 'MapboxGL'
+ m.version = '0.1.0'
+
+ 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.'
+ m.homepage = 'https://www.mapbox.com/blog/mapbox-gl/'
+ m.license = 'BSD'
+ m.author = { 'Mapbox' => 'mobile@mapbox.com' }
+ m.screenshot = 'https://raw.githubusercontent.com/mapbox/mapbox-gl-cocoa/master/pkg/screenshot.png'
+ m.social_media_url = 'https://twitter.com/Mapbox'
+
+ m.source = { :git => 'https://github.com/mapbox/mapbox-gl-cocoa.git', :tag => m.version.to_s }
+
+ m.platform = :ios
+ m.ios.deployment_target = '7.0'
+
+ m.source_files = 'dist/static/Headers/*.h'
+
+ m.requires_arc = true
+
+ m.resource_bundle = { 'MapboxGL' => 'dist/static/MapboxGL.bundle/*' }
+
+ m.frameworks = 'CoreLocation', 'Foundation', 'GLKit', 'SystemConfiguration', 'UIKit'
+
+ m.libraries = 'MapboxGL', 'c++', 'sqlite3', 'z'
+
+ m.vendored_libraries = 'dist/static/libMapboxGL.a'
+
+ m.xcconfig = { 'OTHER_CPLUSPLUSFLAGS' => '-std=gnu++11 -stdlib=libc++' }
+
+end
diff --git a/ios/README.md b/ios/README.md
new file mode 100644
index 0000000000..2af03691e2
--- /dev/null
+++ b/ios/README.md
@@ -0,0 +1,68 @@
+# Mapbox GL for iOS
+
+Use or edit this project to get access to vector maps (via [Mapbox Vector Tiles](https://www.mapbox.com/blog/vector-tiles)) and dynamic OpenGL-based styling in your iOS apps by using `MGLMapView`.
+
+![](./screenshot.png)
+
+## Installation
+
+Currently in flux. See the parent `mapbox-gl-native` project `README` for details on building this library yourself.
+
+Static and dynamic prebuilt binaries are coming back, as is CocoaPods support.
+
+## Example usage
+
+### Objective-C
+
+```objective-c
+MGLMapView *mapView = [[MGLMapView alloc] initWithFrame:CGRectMake(0, 0, 400, 400)
+ accessToken:@"<access token string>"];
+
+[mapView setCenterCoordinate:CLLocationCoordinate2DMake(28.369334, -80.743779)
+ zoomLevel:13
+ animated:NO];
+
+[mapView useBundledStyleNamed:@"outdoors"];
+
+[self.view addSubview:mapView];
+```
+
+### Swift
+
+```swift
+let mapView = MGLMapView(frame: CGRect(x: 0, y: 0, width: 400, height: 400),
+ accessToken: "<access token string>")
+
+mapView.setCenterCoordinate(CLLocationCoordinate2D(latitude: 46.049900, longitude: -122.095678),
+ zoomLevel: 12,
+ animated: false)
+
+mapView.useBundledStyleNamed("outdoors")
+
+view.addSubview(mapView)
+```
+
+## Development
+
+If you'd like to contribute to this project, go up to [Mapbox GL native](https://github.com/mapbox/mapbox-gl-native) and clone the project.
+
+## Testing
+
+Currently in flux and not functioning.
+
+## Requirements
+
+ * iOS 7+
+ * a sense of adventure
+
+## Styling
+
+See the [online style reference](https://www.mapbox.com/mapbox-gl-style-spec/) for the latest documentation. Contained within the `MapboxGL.bundle` assets are a couple of starter styles in JSON format.
+
+The project will eventually get a programmatic styling API as well.
+
+## Related Projects
+
+ * https://github.com/mapbox/mapbox-gl-style-spec
+ * https://github.com/mapbox/mapbox-gl-js
+ * https://github.com/mapbox/vector-tile-spec
diff --git a/ios/app/MBXAppDelegate.h b/ios/app/MBXAppDelegate.h
new file mode 100644
index 0000000000..da081fdcd5
--- /dev/null
+++ b/ios/app/MBXAppDelegate.h
@@ -0,0 +1,7 @@
+#import <UIKit/UIKit.h>
+
+@interface MBXAppDelegate : UIResponder <UIApplicationDelegate>
+
+@property (strong, nonatomic) UIWindow *window;
+
+@end \ No newline at end of file
diff --git a/ios/app/MBXAppDelegate.m b/ios/app/MBXAppDelegate.m
new file mode 100644
index 0000000000..f0541b9971
--- /dev/null
+++ b/ios/app/MBXAppDelegate.m
@@ -0,0 +1,15 @@
+#import "MBXAppDelegate.h"
+#import "MBXViewController.h"
+
+@implementation MBXAppDelegate
+
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
+{
+ self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
+ self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:[MBXViewController new]];
+ [self.window makeKeyAndVisible];
+
+ return YES;
+}
+
+@end
diff --git a/ios/app/MBXViewController.h b/ios/app/MBXViewController.h
new file mode 100644
index 0000000000..924d3af60c
--- /dev/null
+++ b/ios/app/MBXViewController.h
@@ -0,0 +1,5 @@
+#import <UIKit/UIKit.h>
+
+@interface MBXViewController : UIViewController
+
+@end
diff --git a/ios/app/MBXViewController.mm b/ios/app/MBXViewController.mm
new file mode 100644
index 0000000000..4945b73cc5
--- /dev/null
+++ b/ios/app/MBXViewController.mm
@@ -0,0 +1,266 @@
+#import "MBXViewController.h"
+
+#import <mbgl/ios/MGLMapView.h>
+
+#import <mbgl/platform/darwin/settings_nsuserdefaults.hpp>
+
+#import <CoreLocation/CoreLocation.h>
+
+static UIColor *const kTintColor = [UIColor colorWithRed:0.120 green:0.550 blue:0.670 alpha:1.000];
+
+static NSDictionary *const kStyles = @{
+ @"bright-v6": @"Bright",
+ @"basic-v6": @"Basic",
+ @"outdoors-v6": @"Outdoors",
+ @"satellite-v6": @"Satellite"
+};
+
+@interface MBXViewController () <UIActionSheetDelegate, CLLocationManagerDelegate>
+
+@property (nonatomic) MGLMapView *mapView;
+@property (nonatomic) CLLocationManager *locationManager;
+
+@end
+
+@implementation MBXViewController
+
+mbgl::Settings_NSUserDefaults *settings = nullptr;
+
+#pragma mark - Setup
+
+- (id)init
+{
+ self = [super init];
+
+ if (self)
+ {
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(saveState:) name:UIApplicationDidEnterBackgroundNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(restoreState:) name:UIApplicationWillEnterForegroundNotification object:nil];
+ }
+
+ return self;
+}
+
+- (void)viewDidLoad
+{
+ [super viewDidLoad];
+
+ 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.");
+
+ self.mapView = [[MGLMapView alloc] initWithFrame:self.view.bounds accessToken:accessToken];
+ self.mapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+ [self.view addSubview:self.mapView];
+
+ self.mapView.viewControllerForLayoutGuides = self;
+
+ self.view.tintColor = kTintColor;
+ self.navigationController.navigationBar.tintColor = kTintColor;
+ self.mapView.tintColor = kTintColor;
+
+ self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"settings.png"]
+ style:UIBarButtonItemStylePlain
+ target:self
+ action:@selector(showSettings)];
+
+ UIButton *titleButton = [UIButton buttonWithType:UIButtonTypeCustom];
+ [titleButton setFrame:CGRectMake(0, 0, 120, 40)];
+ [titleButton setTitle:[[kStyles allValues] firstObject] forState:UIControlStateNormal];
+ [titleButton setTitleColor:kTintColor forState:UIControlStateNormal];
+ [titleButton addTarget:self action:@selector(cycleStyles) forControlEvents:UIControlEventTouchUpInside];
+ self.navigationItem.titleView = titleButton;
+
+ self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"locateUser.png"]
+ style:UIBarButtonItemStylePlain
+ target:self
+ action:@selector(locateUser)];
+
+ settings = new mbgl::Settings_NSUserDefaults();
+ [self restoreState:nil];
+}
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-parameter"
+
+- (void)saveState:(NSNotification *)notification
+{
+ if (self.mapView && settings)
+ {
+ settings->longitude = self.mapView.centerCoordinate.longitude;
+ settings->latitude = self.mapView.centerCoordinate.latitude;
+ settings->zoom = self.mapView.zoomLevel;
+ settings->bearing = self.mapView.direction;
+ settings->debug = self.mapView.isDebugActive;
+ settings->save();
+ }
+}
+
+- (void)restoreState:(NSNotification *)notification
+{
+ if (self.mapView && settings) {
+ settings->load();
+ [self.mapView setCenterCoordinate:CLLocationCoordinate2DMake(settings->latitude, settings->longitude) zoomLevel:settings->zoom animated:NO];
+ self.mapView.direction = settings->bearing;
+ [self.mapView setDebugActive:settings->debug];
+ }
+}
+
+#pragma clang diagnostic pop
+
+- (NSUInteger)supportedInterfaceOrientations
+{
+ return UIInterfaceOrientationMaskAll;
+}
+
+#pragma mark - Actions
+
+- (void)showSettings
+{
+ UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:@"Map Settings"
+ delegate:self
+ cancelButtonTitle:@"Cancel"
+ destructiveButtonTitle:nil
+ otherButtonTitles:@"Reset North", @"Reset Position", @"Toggle Debug", nil];
+
+ [sheet showFromBarButtonItem:self.navigationItem.leftBarButtonItem animated:YES];
+}
+
+- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex
+{
+ if (buttonIndex == actionSheet.firstOtherButtonIndex)
+ {
+ [self.mapView resetNorth];
+ }
+ else if (buttonIndex == actionSheet.firstOtherButtonIndex + 1)
+ {
+ [self.mapView resetPosition];
+ }
+ else if (buttonIndex == actionSheet.firstOtherButtonIndex + 2)
+ {
+ [self.mapView toggleDebug];
+ }
+}
+
+- (void)cycleStyles
+{
+ UIButton *titleButton = (UIButton *)self.navigationItem.titleView;
+
+ NSString *styleName = [titleButton titleForState:UIControlStateNormal];
+
+ if ( ! styleName)
+ {
+ styleName = [[kStyles allKeys] firstObject];
+ }
+ else
+ {
+ NSUInteger index = [[kStyles allValues] indexOfObject:styleName] + 1;
+ if (index == [[kStyles allKeys] count]) index = 0;
+ styleName = [[kStyles allKeys] objectAtIndex:index];
+ }
+
+ [self.mapView useBundledStyleNamed:styleName];
+
+ [titleButton setTitle:kStyles[styleName] forState:UIControlStateNormal];
+}
+
+- (void)locateUser
+{
+ if ( ! self.locationManager)
+ {
+ self.locationManager = [CLLocationManager new];
+ self.locationManager.delegate = self;
+ }
+
+ if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied)
+ {
+ [[[UIAlertView alloc] initWithTitle:@"Authorization Denied"
+ message:@"Please enable location services for this app in Privacy settings."
+ delegate:nil
+ cancelButtonTitle:nil otherButtonTitles:@"OK", nil] show];
+ }
+ else
+ {
+#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
+ if ([CLLocationManager instancesRespondToSelector:@selector(requestWhenInUseAuthorization)])
+ {
+ if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorizedWhenInUse)
+ {
+ [self.locationManager startUpdatingLocation];
+ }
+ else
+ {
+ [_locationManager requestWhenInUseAuthorization];
+ }
+ }
+ else
+ {
+ [self.locationManager startUpdatingLocation];
+ }
+#else
+ [self.locationManager startUpdatingLocation];
+#endif
+ }
+}
+
+#pragma mark - Destruction
+
+- (void)dealloc
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+
+ if (settings)
+ {
+ [self saveState:nil];
+ delete settings;
+ settings = nullptr;
+ }
+}
+
+#pragma mark - Location
+
+- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
+{
+ switch (status)
+ {
+ case kCLAuthorizationStatusAuthorizedAlways:
+#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
+ case kCLAuthorizationStatusAuthorizedWhenInUse:
+#endif
+ {
+ [manager startUpdatingLocation];
+ break;
+ }
+ default:
+ {
+ }
+ }
+}
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-parameter"
+
+- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
+{
+ CLLocation *latestLocation = locations.lastObject;
+
+ if ([latestLocation distanceFromLocation:[[CLLocation alloc] initWithLatitude:self.mapView.centerCoordinate.latitude longitude:self.mapView.centerCoordinate.longitude]] > 100)
+ {
+ [self.mapView setCenterCoordinate:CLLocationCoordinate2DMake(latestLocation.coordinate.latitude, latestLocation.coordinate.longitude) zoomLevel:17 animated:YES];
+ }
+
+ [self.locationManager stopUpdatingLocation];
+}
+
+#pragma clang diagnostic pop
+
+@end
diff --git a/ios/app/README.md b/ios/app/README.md
new file mode 100644
index 0000000000..325670f1ea
--- /dev/null
+++ b/ios/app/README.md
@@ -0,0 +1,3 @@
+This is the app for use in the development of Mapbox GL for iOS.
+
+If you want to run this app, head up to [`mapbox-gl-native`](https://github.com/mapbox/mapbox-gl-native) and follow the iOS installation instructions there. \ No newline at end of file
diff --git a/ios/app/app-info.plist b/ios/app/app-info.plist
new file mode 100644
index 0000000000..c15cbeb64f
--- /dev/null
+++ b/ios/app/app-info.plist
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleDisplayName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.mapbox.MapboxGL</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>0.0.1</string>
+ <key>CFBundleSignature</key>
+ <string>MBGL</string>
+ <key>CFBundleVersion</key>
+ <string>0.0.1</string>
+ <key>LSRequiresIPhoneOS</key>
+ <true/>
+ <key>NSHumanReadableCopyright</key>
+ <string>(c) 2014 Mapbox</string>
+ <key>NSLocationWhenInUseUsageDescription</key>
+ <string>The map will be centered on the user's location.</string>
+ <key>UILaunchStoryboardName</key>
+ <string>Default</string>
+ <key>UIRequiredDeviceCapabilities</key>
+ <array>
+ <string>armv7</string>
+ </array>
+ <key>UISupportedInterfaceOrientations</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+ <key>UISupportedInterfaceOrientations~ipad</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationPortraitUpsideDown</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+</dict>
+</plist>
diff --git a/ios/app/img/Default-568h@2x.png b/ios/app/img/Default-568h@2x.png
new file mode 100644
index 0000000000..ea3706427a
--- /dev/null
+++ b/ios/app/img/Default-568h@2x.png
Binary files differ
diff --git a/ios/app/img/Default-667h@2x.png b/ios/app/img/Default-667h@2x.png
new file mode 100644
index 0000000000..03f139de66
--- /dev/null
+++ b/ios/app/img/Default-667h@2x.png
Binary files differ
diff --git a/ios/app/img/Icon-40.png b/ios/app/img/Icon-40.png
new file mode 100644
index 0000000000..eca13393e6
--- /dev/null
+++ b/ios/app/img/Icon-40.png
Binary files differ
diff --git a/ios/app/img/Icon-40@2x.png b/ios/app/img/Icon-40@2x.png
new file mode 100644
index 0000000000..070d037539
--- /dev/null
+++ b/ios/app/img/Icon-40@2x.png
Binary files differ
diff --git a/ios/app/img/Icon-60.png b/ios/app/img/Icon-60.png
new file mode 100644
index 0000000000..ff4c6ab4b1
--- /dev/null
+++ b/ios/app/img/Icon-60.png
Binary files differ
diff --git a/ios/app/img/Icon-60@2x.png b/ios/app/img/Icon-60@2x.png
new file mode 100644
index 0000000000..b7f25955f5
--- /dev/null
+++ b/ios/app/img/Icon-60@2x.png
Binary files differ
diff --git a/ios/app/img/Icon-72.png b/ios/app/img/Icon-72.png
new file mode 100644
index 0000000000..0c876f664d
--- /dev/null
+++ b/ios/app/img/Icon-72.png
Binary files differ
diff --git a/ios/app/img/Icon-72@2x.png b/ios/app/img/Icon-72@2x.png
new file mode 100644
index 0000000000..6da408204a
--- /dev/null
+++ b/ios/app/img/Icon-72@2x.png
Binary files differ
diff --git a/ios/app/img/Icon-76.png b/ios/app/img/Icon-76.png
new file mode 100644
index 0000000000..895b4a1761
--- /dev/null
+++ b/ios/app/img/Icon-76.png
Binary files differ
diff --git a/ios/app/img/Icon-76@2x.png b/ios/app/img/Icon-76@2x.png
new file mode 100644
index 0000000000..7bc5208976
--- /dev/null
+++ b/ios/app/img/Icon-76@2x.png
Binary files differ
diff --git a/ios/app/img/Icon-Small-50.png b/ios/app/img/Icon-Small-50.png
new file mode 100644
index 0000000000..6d17da4b00
--- /dev/null
+++ b/ios/app/img/Icon-Small-50.png
Binary files differ
diff --git a/ios/app/img/Icon-Small-50@2x.png b/ios/app/img/Icon-Small-50@2x.png
new file mode 100644
index 0000000000..ac4ec19282
--- /dev/null
+++ b/ios/app/img/Icon-Small-50@2x.png
Binary files differ
diff --git a/ios/app/img/Icon-Small.png b/ios/app/img/Icon-Small.png
new file mode 100644
index 0000000000..aecbbc8a1d
--- /dev/null
+++ b/ios/app/img/Icon-Small.png
Binary files differ
diff --git a/ios/app/img/Icon-Small@2x.png b/ios/app/img/Icon-Small@2x.png
new file mode 100644
index 0000000000..7773852e7a
--- /dev/null
+++ b/ios/app/img/Icon-Small@2x.png
Binary files differ
diff --git a/ios/app/img/Icon-Spotlight-40.png b/ios/app/img/Icon-Spotlight-40.png
new file mode 100644
index 0000000000..eca13393e6
--- /dev/null
+++ b/ios/app/img/Icon-Spotlight-40.png
Binary files differ
diff --git a/ios/app/img/Icon-Spotlight-40@2x.png b/ios/app/img/Icon-Spotlight-40@2x.png
new file mode 100644
index 0000000000..070d037539
--- /dev/null
+++ b/ios/app/img/Icon-Spotlight-40@2x.png
Binary files differ
diff --git a/ios/app/img/Icon.png b/ios/app/img/Icon.png
new file mode 100644
index 0000000000..9ca8194eef
--- /dev/null
+++ b/ios/app/img/Icon.png
Binary files differ
diff --git a/ios/app/img/Icon@2x.png b/ios/app/img/Icon@2x.png
new file mode 100644
index 0000000000..7c2e8ba037
--- /dev/null
+++ b/ios/app/img/Icon@2x.png
Binary files differ
diff --git a/ios/app/img/iTunesArtwork b/ios/app/img/iTunesArtwork
new file mode 100644
index 0000000000..ac6a0c58e8
--- /dev/null
+++ b/ios/app/img/iTunesArtwork
Binary files differ
diff --git a/ios/app/img/iTunesArtwork.png b/ios/app/img/iTunesArtwork.png
new file mode 100644
index 0000000000..b10824b048
--- /dev/null
+++ b/ios/app/img/iTunesArtwork.png
Binary files differ
diff --git a/ios/app/img/iTunesArtwork@2x b/ios/app/img/iTunesArtwork@2x
new file mode 100644
index 0000000000..fae1dad8bf
--- /dev/null
+++ b/ios/app/img/iTunesArtwork@2x
Binary files differ
diff --git a/ios/app/img/iTunesArtwork@2x.png b/ios/app/img/iTunesArtwork@2x.png
new file mode 100644
index 0000000000..fdee900aa4
--- /dev/null
+++ b/ios/app/img/iTunesArtwork@2x.png
Binary files differ
diff --git a/ios/app/img/locateUser.png b/ios/app/img/locateUser.png
new file mode 100644
index 0000000000..aa8d1cf52e
--- /dev/null
+++ b/ios/app/img/locateUser.png
Binary files differ
diff --git a/ios/app/img/locateUser@2x.png b/ios/app/img/locateUser@2x.png
new file mode 100644
index 0000000000..29c4905938
--- /dev/null
+++ b/ios/app/img/locateUser@2x.png
Binary files differ
diff --git a/ios/app/img/settings.png b/ios/app/img/settings.png
new file mode 100644
index 0000000000..5d7643eef5
--- /dev/null
+++ b/ios/app/img/settings.png
Binary files differ
diff --git a/ios/app/img/settings@2x.png b/ios/app/img/settings@2x.png
new file mode 100644
index 0000000000..2bb9f0ebad
--- /dev/null
+++ b/ios/app/img/settings@2x.png
Binary files differ
diff --git a/ios/app/main.m b/ios/app/main.m
new file mode 100644
index 0000000000..954584f141
--- /dev/null
+++ b/ios/app/main.m
@@ -0,0 +1,10 @@
+#import <UIKit/UIKit.h>
+
+#import "MBXAppDelegate.h"
+
+int main(int argc, char * argv[])
+{
+ @autoreleasepool {
+ return UIApplicationMain(argc, argv, nil, NSStringFromClass([MBXAppDelegate class]));
+ }
+}
diff --git a/ios/app/mapboxgl-app.gyp b/ios/app/mapboxgl-app.gyp
new file mode 100644
index 0000000000..7287932fc5
--- /dev/null
+++ b/ios/app/mapboxgl-app.gyp
@@ -0,0 +1,57 @@
+{
+ 'includes': [
+ '../../gyp/common.gypi',
+ ],
+ 'targets': [
+ { 'target_name': 'iosapp',
+ 'product_name': 'Mapbox GL',
+ 'type': 'executable',
+ 'product_extension': 'app',
+ 'mac_bundle': 1,
+ 'mac_bundle_resources': [
+ '<!@(find ./img -type f)',
+ ],
+
+ 'dependencies': [
+ '../../mbgl.gyp:bundle_styles',
+ '../../mbgl.gyp:core',
+ '../../mbgl.gyp:platform-<(platform_lib)',
+ '../../mbgl.gyp:http-<(http_lib)',
+ '../../mbgl.gyp:asset-<(asset_lib)',
+ '../../mbgl.gyp:cache-<(cache_lib)',
+ ],
+
+ 'sources': [
+ './main.m',
+ './MBXAppDelegate.h',
+ './MBXAppDelegate.m',
+ './MBXViewController.h',
+ './MBXViewController.mm',
+ '../../platform/darwin/settings_nsuserdefaults.mm',
+ ],
+
+ 'xcode_settings': {
+ 'SDKROOT': 'iphoneos',
+ 'SUPPORTED_PLATFORMS': 'iphonesimulator iphoneos',
+ 'INFOPLIST_FILE': 'app-info.plist',
+ 'TARGETED_DEVICE_FAMILY': '1,2',
+ 'COMBINE_HIDPI_IMAGES': 'NO', # don't merge @2x.png images into .tiff files
+ 'CLANG_ENABLE_OBJC_ARC': 'YES',
+ },
+
+ 'configurations': {
+ 'Debug': {
+ 'xcode_settings': {
+ 'CODE_SIGN_IDENTITY': 'iPhone Developer',
+ },
+ },
+ 'Release': {
+ 'xcode_settings': {
+ 'CODE_SIGN_IDENTITY': 'iPhone Distribution',
+ 'ARCHS': [ "armv7", "armv7s", "arm64", "i386", "x86_64" ],
+ },
+ },
+ },
+ }
+ ]
+}
diff --git a/ios/docs/install_docs.sh b/ios/docs/install_docs.sh
new file mode 100755
index 0000000000..4d116c0332
--- /dev/null
+++ b/ios/docs/install_docs.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+if [ -z `which appledoc` ]; then
+ echo "Unable to find appledoc. Consider installing it from source or Homebrew."
+ exit 1
+fi
+
+VERSION=$( git tag | sort -r | sed -n '1p' )
+echo "Creating new docs for $VERSION..."
+echo
+
+appledoc \
+ --output /tmp/`uuidgen` \
+ --project-name "Mapbox GL $VERSION" \
+ --project-company Mapbox \
+ --create-docset \
+ --company-id com.mapbox \
+ --ignore app \
+ --ignore dist \
+ --ignore pkg \
+ --ignore test \
+ --ignore .m \
+ --ignore .mm \
+ . \ No newline at end of file
diff --git a/ios/docs/remove_docs.sh b/ios/docs/remove_docs.sh
new file mode 100755
index 0000000000..bb8c008dc2
--- /dev/null
+++ b/ios/docs/remove_docs.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+echo
+echo "Removing docs from ~/Library/Developer/Shared/Documentation/DocSets..."
+echo
+
+rm -rfv ~/Library/Developer/Shared/Documentation/DocSets/com.mapbox.Mapbox-GL-* \ No newline at end of file
diff --git a/ios/mapbox-gl-cocoa b/ios/mapbox-gl-cocoa
deleted file mode 160000
-Subproject f36ee137496c8cf11ab604cacbb63ca645e6741
diff --git a/ios/screenshot.png b/ios/screenshot.png
new file mode 100644
index 0000000000..58db4413db
--- /dev/null
+++ b/ios/screenshot.png
Binary files differ
diff --git a/mbgl.gyp b/mbgl.gyp
index b6b00455e1..c5ede28233 100644
--- a/mbgl.gyp
+++ b/mbgl.gyp
@@ -8,19 +8,20 @@
'./gyp/standalone.gypi',
'./gyp/core.gypi',
'./gyp/none.gypi',
- './gyp/install.gypi',
],
'conditions': [
- ['headless_lib == "cgl"', { 'includes': [ './gyp/headless-cgl.gypi' ] } ],
- ['headless_lib == "glx"', { 'includes': [ './gyp/headless-glx.gypi' ] } ],
- ['platform_lib == "osx"', { 'includes': [ './gyp/platform-osx.gypi' ] } ],
- ['platform_lib == "ios"', { 'includes': [ './gyp/platform-ios.gypi' ] } ],
+ ['headless_lib == "cgl" and host == "osx"', { 'includes': [ './gyp/headless-cgl.gypi' ] } ],
+ ['headless_lib == "glx" and host == "linux"', { 'includes': [ './gyp/headless-glx.gypi' ] } ],
+ ['platform_lib == "osx" and host == "osx"', { 'includes': [ './gyp/platform-osx.gypi' ] } ],
+ ['platform_lib == "ios" and host == "ios"', { 'includes': [ './gyp/platform-ios.gypi' ] } ],
['platform_lib == "linux"', { 'includes': [ './gyp/platform-linux.gypi' ] } ],
- ['platform_lib == "android"', { 'includes': [ './gyp/platform-android.gypi' ] } ],
+ ['platform_lib == "android" and host == "android"', { 'includes': [ './gyp/platform-android.gypi' ] } ],
['http_lib == "curl"', { 'includes': [ './gyp/http-curl.gypi' ] } ],
- ['http_lib == "nsurl"', { 'includes': [ './gyp/http-nsurl.gypi' ] } ],
+ ['http_lib == "nsurl" and (host == "osx" or host == "ios")', { 'includes': [ './gyp/http-nsurl.gypi' ] } ],
['asset_lib == "fs"', { 'includes': [ './gyp/asset-fs.gypi' ] } ],
['asset_lib == "zip"', { 'includes': [ './gyp/asset-zip.gypi' ] } ],
['cache_lib == "sqlite"', { 'includes': [ './gyp/cache-sqlite.gypi' ] } ],
+
+ ['install_prefix != ""', { 'includes': ['./gyp/install.gypi' ] } ],
],
}
diff --git a/platform/darwin/log_nslog.mm b/platform/darwin/log_nslog.mm
index ea5fddf0b9..a82d78a01f 100644
--- a/platform/darwin/log_nslog.mm
+++ b/platform/darwin/log_nslog.mm
@@ -10,7 +10,6 @@ void NSLogBackend::record(EventSeverity severity, Event event, const std::string
NSString *message =
[[NSString alloc] initWithBytes:msg.data() length:msg.size() encoding:NSUTF8StringEncoding];
NSLog(@"[%s] %s: %@", EventSeverityClass(severity).c_str(), EventClass(event).c_str(), message);
- [message release];
}
void NSLogBackend::record(EventSeverity severity, Event event, const char *format, ...) {
@@ -37,7 +36,6 @@ void NSLogBackend::record(EventSeverity severity, Event event, int64_t code,
[[NSString alloc] initWithBytes:msg.data() length:msg.size() encoding:NSUTF8StringEncoding];
NSLog(@"[%s] %s: (%lld) %@", EventSeverityClass(severity).c_str(), EventClass(event).c_str(),
code, message);
- [message release];
}
}
diff --git a/platform/darwin/string_nsstring.mm b/platform/darwin/string_nsstring.mm
index 86c2c07edd..9bf199afc0 100644
--- a/platform/darwin/string_nsstring.mm
+++ b/platform/darwin/string_nsstring.mm
@@ -13,7 +13,6 @@ std::string uppercase(const std::string &string) {
NSString *uppercase = [original uppercaseString];
const std::string result{[uppercase cStringUsingEncoding : NSUTF8StringEncoding],
[uppercase lengthOfBytesUsingEncoding:NSUTF8StringEncoding]};
- [original release];
return result;
}
@@ -25,7 +24,6 @@ std::string lowercase(const std::string &string) {
NSString *lowercase = [original lowercaseString];
const std::string result{[lowercase cStringUsingEncoding : NSUTF8StringEncoding],
[lowercase lengthOfBytesUsingEncoding:NSUTF8StringEncoding]};
- [original release];
return result;
}
diff --git a/platform/default/headless_display.cpp b/platform/default/headless_display.cpp
index 4756c8d2cb..c4535fc1a6 100644
--- a/platform/default/headless_display.cpp
+++ b/platform/default/headless_display.cpp
@@ -3,6 +3,10 @@
#include <cstring>
#include <stdexcept>
+#if MBGL_USE_GLX
+#include <GL/glx.h>
+#endif
+
namespace mbgl {
HeadlessDisplay::HeadlessDisplay() {
@@ -52,7 +56,7 @@ HeadlessDisplay::HeadlessDisplay() {
// We're creating a dummy pbuffer anyway that we're not using.
static int pixelFormat[] = {
GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT,
- 0
+ None
};
int configs = 0;
diff --git a/platform/default/headless_view.cpp b/platform/default/headless_view.cpp
index 9df58f8026..656a774390 100644
--- a/platform/default/headless_view.cpp
+++ b/platform/default/headless_view.cpp
@@ -28,6 +28,8 @@ CGLProc CGLGetProcAddress(const char *proc) {
CFRelease(name);
return symbol;
}
+#elif MBGL_USE_GLX
+#include <GL/glx.h>
#endif
namespace mbgl {
@@ -104,7 +106,7 @@ void HeadlessView::createContext() {
if (!glContext) {
// Try to create a legacy context
- glContext = glXCreateNewContext(xDisplay, fbConfigs[0], GLX_RGBA_TYPE, 0, True);
+ glContext = glXCreateNewContext(xDisplay, fbConfigs[0], GLX_RGBA_TYPE, None, True);
if (glContext) {
if (!glXIsDirect(xDisplay, glContext)) {
mbgl::Log::Error(mbgl::Event::OpenGL, "Failed to create direct OpenGL Legacy context");
@@ -123,7 +125,7 @@ void HeadlessView::createContext() {
int pbufferAttributes[] = {
GLX_PBUFFER_WIDTH, 8,
GLX_PBUFFER_HEIGHT, 8,
- 0
+ None
};
glxPbuffer = glXCreatePbuffer(xDisplay, fbConfigs[0], pbufferAttributes);
#endif
diff --git a/platform/ios/MGLMapView.mm b/platform/ios/MGLMapView.mm
new file mode 100644
index 0000000000..7c1f9eb657
--- /dev/null
+++ b/platform/ios/MGLMapView.mm
@@ -0,0 +1,1616 @@
+#import "MGLMapView.h"
+
+#import <mbgl/platform/darwin/log_nslog.hpp>
+#import <mbgl/platform/gl.hpp>
+
+#import <GLKit/GLKit.h>
+#import <OpenGLES/EAGL.h>
+
+#include <mbgl/mbgl.hpp>
+#include <mbgl/platform/platform.hpp>
+#include <mbgl/platform/darwin/reachability.h>
+#include <mbgl/storage/default_file_source.hpp>
+#include <mbgl/storage/default/sqlite_cache.hpp>
+#include <mbgl/storage/network_status.hpp>
+
+#import "MGLTypes.h"
+#import "MGLStyleFunctionValue.h"
+
+#import "UIColor+MGLAdditions.h"
+#import "NSArray+MGLAdditions.h"
+#import "NSDictionary+MGLAdditions.h"
+
+
+// Returns the path to the default cache database on this system.
+const std::string &defaultCacheDatabase() {
+ static const std::string path = []() -> std::string {
+ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
+ if ([paths count] == 0) {
+ // Disable the cache if we don't have a location to write.
+ return "";
+ }
+
+ NSString *libraryDirectory = [paths objectAtIndex:0];
+ return [[libraryDirectory stringByAppendingPathComponent:@"cache.db"] UTF8String];
+ }();
+ return path;
+}
+
+
+extern NSString *const MGLStyleKeyGeneric;
+extern NSString *const MGLStyleKeyFill;
+extern NSString *const MGLStyleKeyLine;
+extern NSString *const MGLStyleKeyIcon;
+extern NSString *const MGLStyleKeyText;
+extern NSString *const MGLStyleKeyRaster;
+extern NSString *const MGLStyleKeyComposite;
+extern NSString *const MGLStyleKeyBackground;
+
+extern NSString *const MGLStyleValueFunctionAllowed;
+
+NSTimeInterval const MGLAnimationDuration = 0.3;
+
+#pragma mark - Private -
+
+@interface MGLMapView () <UIGestureRecognizerDelegate, GLKViewDelegate>
+
+@property (nonatomic) EAGLContext *context;
+@property (nonatomic) GLKView *glView;
+@property (nonatomic) NSOperationQueue *regionChangeDelegateQueue;
+@property (nonatomic) UIImageView *compass;
+@property (nonatomic) UIImageView *logoBug;
+@property (nonatomic) UIButton *attributionButton;
+@property (nonatomic) UIPanGestureRecognizer *pan;
+@property (nonatomic) UIPinchGestureRecognizer *pinch;
+@property (nonatomic) UIRotationGestureRecognizer *rotate;
+@property (nonatomic) UILongPressGestureRecognizer *quickZoom;
+@property (nonatomic, readonly) NSDictionary *allowedStyleTypes;
+@property (nonatomic) CGPoint centerPoint;
+@property (nonatomic) CGFloat scale;
+@property (nonatomic) CGFloat angle;
+@property (nonatomic) CGFloat quickZoomStart;
+@property (nonatomic, getter=isAnimatingGesture) BOOL animatingGesture;
+@property (nonatomic, readonly, getter=isRotationAllowed) BOOL rotationAllowed;
+
+@end
+
+@interface MGLStyleFunctionValue (MGLMapViewFriend)
+
+@property (nonatomic) NSString *functionType;
+@property (nonatomic) NSDictionary *stops;
+@property (nonatomic) CGFloat zBase;
+@property (nonatomic) CGFloat val;
+@property (nonatomic) CGFloat slope;
+@property (nonatomic) CGFloat min;
+@property (nonatomic) CGFloat max;
+@property (nonatomic) CGFloat minimumZoom;
+@property (nonatomic) CGFloat maximumZoom;
+
+- (id)rawStyle;
+
+@end
+
+@implementation MGLMapView
+
+#pragma mark - Setup -
+
+@dynamic debugActive;
+
+class MBGLView;
+
+std::chrono::steady_clock::duration secondsAsDuration(float duration)
+{
+ return std::chrono::duration_cast<std::chrono::steady_clock::duration>(std::chrono::duration<float, std::chrono::seconds::period>(duration));
+}
+
+mbgl::Map *mbglMap = nullptr;
+MBGLView *mbglView = nullptr;
+mbgl::SQLiteCache *mbglFileCache = nullptr;
+mbgl::DefaultFileSource *mbglFileSource = nullptr;
+
+- (instancetype)initWithFrame:(CGRect)frame styleJSON:(NSString *)styleJSON accessToken:(NSString *)accessToken
+{
+ self = [super initWithFrame:frame];
+
+ if (self && [self commonInit])
+ {
+ if (accessToken) [self setAccessToken:accessToken];
+
+ if (styleJSON || accessToken)
+ {
+ // If style is set directly, pass it on. If not, if we have an access
+ // token, we can pass nil and use the default style.
+ //
+ [self setStyleJSON:styleJSON];
+ }
+ }
+
+ return self;
+}
+
+- (instancetype)initWithFrame:(CGRect)frame accessToken:(NSString *)accessToken
+{
+ return [self initWithFrame:frame styleJSON:nil accessToken:accessToken];
+}
+
+- (instancetype)initWithCoder:(NSCoder *)decoder
+{
+ self = [super initWithCoder:decoder];
+
+ if (self && [self commonInit])
+ {
+ return self;
+ }
+
+ return nil;
+}
+
+- (void)setAccessToken:(NSString *)accessToken
+{
+ if (accessToken)
+ {
+ mbglMap->setAccessToken((std::string)[accessToken cStringUsingEncoding:[NSString defaultCStringEncoding]]);
+ }
+}
+
+- (void)setStyleJSON:(NSString *)styleJSON
+{
+ if ( ! styleJSON)
+ {
+ [self useBundledStyleNamed:@"bright-v6"];
+ }
+ else
+ {
+ if ([@(mbglMap->getStyleJSON().c_str()) length]) mbglMap->stop();
+ mbglMap->setStyleJSON((std::string)[styleJSON cStringUsingEncoding:[NSString defaultCStringEncoding]]);
+ mbglMap->start();
+ }
+}
+
+- (void)setStyleURL:(NSString *)filePathURL
+{
+ if ([@(mbglMap->getStyleJSON().c_str()) length]) mbglMap->stop();
+ mbglMap->setStyleURL(std::string("asset://") + [filePathURL UTF8String]);
+ mbglMap->start();
+}
+
+- (BOOL)commonInit
+{
+ // set logging backend
+ //
+ mbgl::Log::Set<mbgl::NSLogBackend>();
+
+ // create context
+ //
+ _context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
+
+ if ( ! _context)
+ {
+ mbgl::Log::Error(mbgl::Event::Setup, "Failed to create OpenGL ES context");
+
+ return NO;
+ }
+
+ // setup accessibility
+ //
+ self.accessibilityLabel = @"Map";
+
+ // create GL view
+ //
+ _glView = [[GLKView alloc] initWithFrame:self.bounds context:_context];
+ _glView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+ _glView.enableSetNeedsDisplay = NO;
+ _glView.drawableStencilFormat = GLKViewDrawableStencilFormat8;
+ _glView.drawableDepthFormat = GLKViewDrawableDepthFormat16;
+ if ([UIScreen instancesRespondToSelector:@selector(nativeScale)]) {
+ _glView.contentScaleFactor = [[UIScreen mainScreen] nativeScale];
+ }
+ _glView.delegate = self;
+ [_glView bindDrawable];
+ [self addSubview:_glView];
+
+
+ // load extensions
+ //
+ const std::string extensions = (char *)glGetString(GL_EXTENSIONS);
+ {
+ using namespace mbgl;
+
+ if (extensions.find("GL_OES_vertex_array_object") != std::string::npos) {
+ gl::BindVertexArray = glBindVertexArrayOES;
+ gl::DeleteVertexArrays = glDeleteVertexArraysOES;
+ gl::GenVertexArrays = glGenVertexArraysOES;
+ gl::IsVertexArray = glIsVertexArrayOES;
+ }
+
+ if (extensions.find("GL_OES_packed_depth_stencil") != std::string::npos) {
+ gl::isPackedDepthStencilSupported = YES;
+ }
+
+ if (extensions.find("GL_OES_depth24") != std::string::npos) {
+ gl::isDepth24Supported = YES;
+ }
+ }
+
+ // setup mbgl map
+ //
+ mbglView = new MBGLView(self);
+ mbglFileCache = new mbgl::SQLiteCache(defaultCacheDatabase());
+ mbglFileSource = new mbgl::DefaultFileSource(mbglFileCache);
+ mbglMap = new mbgl::Map(*mbglView, *mbglFileSource);
+ mbglMap->resize(self.bounds.size.width, self.bounds.size.height, _glView.contentScaleFactor, _glView.drawableWidth, _glView.drawableHeight);
+
+ // Notify map object when network reachability status changes.
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(reachabilityChanged:)
+ name:kReachabilityChangedNotification
+ object:nil];
+
+ Reachability* reachability = [Reachability reachabilityForInternetConnection];
+ [reachability startNotifier];
+
+ // setup logo bug
+ //
+ _logoBug = [[UIImageView alloc] initWithImage:[MGLMapView resourceImageNamed:@"mapbox.png"]];
+ _logoBug.accessibilityLabel = @"Mapbox logo";
+ _logoBug.frame = CGRectMake(8, self.bounds.size.height - _logoBug.bounds.size.height - 4, _logoBug.bounds.size.width, _logoBug.bounds.size.height);
+ _logoBug.translatesAutoresizingMaskIntoConstraints = NO;
+ [self addSubview:_logoBug];
+
+ // setup attribution
+ //
+ _attributionButton = [UIButton buttonWithType:UIButtonTypeInfoLight];
+ _attributionButton.accessibilityLabel = @"Attribution info";
+ [_attributionButton addTarget:self action:@selector(showAttribution:) forControlEvents:UIControlEventTouchUpInside];
+ _attributionButton.frame = CGRectMake(self.bounds.size.width - _attributionButton.bounds.size.width - 8, self.bounds.size.height - _attributionButton.bounds.size.height - 8, _attributionButton.bounds.size.width, _attributionButton.bounds.size.height);
+ _attributionButton.translatesAutoresizingMaskIntoConstraints = NO;
+ [self addSubview:_attributionButton];
+
+ // setup compass
+ //
+ _compass = [[UIImageView alloc] initWithImage:[MGLMapView resourceImageNamed:@"Compass.png"]];
+ _compass.accessibilityLabel = @"Compass";
+ UIImage *compassImage = [MGLMapView resourceImageNamed:@"Compass.png"];
+ _compass.frame = CGRectMake(0, 0, compassImage.size.width, compassImage.size.height);
+ _compass.alpha = 0;
+ UIView *container = [[UIView alloc] initWithFrame:CGRectMake(self.bounds.size.width - compassImage.size.width - 5, 5, compassImage.size.width, compassImage.size.height)];
+ [container addSubview:_compass];
+ container.translatesAutoresizingMaskIntoConstraints = NO;
+ [container addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleCompassTapGesture:)]];
+ [self addSubview:container];
+
+ self.viewControllerForLayoutGuides = nil;
+
+ // setup interaction
+ //
+ _pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)];
+ _pan.delegate = self;
+ [self addGestureRecognizer:_pan];
+ _scrollEnabled = YES;
+
+ _pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinchGesture:)];
+ _pinch.delegate = self;
+ [self addGestureRecognizer:_pinch];
+ _zoomEnabled = YES;
+
+ _rotate = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(handleRotateGesture:)];
+ _rotate.delegate = self;
+ [self addGestureRecognizer:_rotate];
+ _rotateEnabled = YES;
+
+ UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTapGesture:)];
+ doubleTap.numberOfTapsRequired = 2;
+ [self addGestureRecognizer:doubleTap];
+
+ UITapGestureRecognizer *twoFingerTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTwoFingerTapGesture:)];
+ twoFingerTap.numberOfTouchesRequired = 2;
+ [twoFingerTap requireGestureRecognizerToFail:_pinch];
+ [twoFingerTap requireGestureRecognizerToFail:_rotate];
+ [self addGestureRecognizer:twoFingerTap];
+
+ if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)
+ {
+ _quickZoom = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleQuickZoomGesture:)];
+ _quickZoom.numberOfTapsRequired = 1;
+ _quickZoom.minimumPressDuration = 0.25;
+ [self addGestureRecognizer:_quickZoom];
+ }
+
+ // observe app activity
+ //
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
+
+ // set initial position
+ //
+ mbglMap->setLonLatZoom(0, 0, mbglMap->getMinZoom());
+
+ // setup change delegate queue
+ //
+ _regionChangeDelegateQueue = [NSOperationQueue new];
+ _regionChangeDelegateQueue.maxConcurrentOperationCount = 1;
+
+ return YES;
+}
+
+-(void)reachabilityChanged:(NSNotification*)notification
+{
+ Reachability *reachability = [notification object];
+ if ([reachability isReachable]) {
+ mbgl::NetworkStatus::Reachable();
+ }
+}
+
+- (void)dealloc
+{
+ [_regionChangeDelegateQueue cancelAllOperations];
+
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+
+ if (mbglMap)
+ {
+ delete mbglMap;
+ mbglMap = nullptr;
+ }
+
+ if (mbglFileSource)
+ {
+ delete mbglFileSource;
+ mbglFileSource = nullptr;
+ }
+
+ if (mbglView)
+ {
+ delete mbglView;
+ mbglView = nullptr;
+ }
+
+ if ([[EAGLContext currentContext] isEqual:_context])
+ {
+ [EAGLContext setCurrentContext:nil];
+ }
+}
+
+#pragma mark - Layout -
+
+- (void)setFrame:(CGRect)frame
+{
+ [super setFrame:frame];
+
+ [self setNeedsLayout];
+}
+
+- (void)setBounds:(CGRect)bounds
+{
+ [super setBounds:bounds];
+
+ [self setNeedsLayout];
+}
+
++ (BOOL)requiresConstraintBasedLayout
+{
+ return YES;
+}
+
+- (void)didMoveToSuperview
+{
+ [self.compass.superview removeConstraints:self.compass.superview.constraints];
+ [self.logoBug removeConstraints:self.logoBug.constraints];
+ [self.attributionButton removeConstraints:self.attributionButton.constraints];
+
+ [self setNeedsUpdateConstraints];
+}
+
+- (void)setViewControllerForLayoutGuides:(UIViewController *)viewController
+{
+ _viewControllerForLayoutGuides = viewController;
+
+ [self.compass.superview removeConstraints:self.compass.superview.constraints];
+ [self.logoBug removeConstraints:self.logoBug.constraints];
+ [self.attributionButton removeConstraints:self.attributionButton.constraints];
+
+ [self setNeedsUpdateConstraints];
+}
+
+- (void)updateConstraints
+{
+ // If we have a view controller reference, use its layout guides for our various top & bottom
+ // views so they don't underlap navigation or tool bars. If we don't have a reference, apply
+ // constraints against ourself to maintain (albeit less ideal) placement of the subviews.
+ //
+ NSString *topGuideFormatString = (self.viewControllerForLayoutGuides ? @"[topLayoutGuide]" : @"|");
+ NSString *bottomGuideFormatString = (self.viewControllerForLayoutGuides ? @"[bottomLayoutGuide]" : @"|");
+
+ id topGuideViewsObject = (self.viewControllerForLayoutGuides ? (id)self.viewControllerForLayoutGuides.topLayoutGuide : (id)@"");
+ id bottomGuideViewsObject = (self.viewControllerForLayoutGuides ? (id)self.viewControllerForLayoutGuides.bottomLayoutGuide : (id)@"");
+
+ UIView *constraintParentView = (self.viewControllerForLayoutGuides.view ? self.viewControllerForLayoutGuides.view : self);
+
+ // compass
+ //
+ UIView *compassContainer = self.compass.superview;
+
+ [constraintParentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:@"V:%@-topSpacing-[container]", topGuideFormatString]
+ options:0
+ metrics:@{ @"topSpacing" : @(5) }
+ views:@{ @"topLayoutGuide" : topGuideViewsObject,
+ @"container" : compassContainer }]];
+
+ [constraintParentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[container]-rightSpacing-|"
+ options:0
+ metrics:@{ @"rightSpacing" : @(5) }
+ views:@{ @"container" : compassContainer }]];
+
+ [compassContainer addConstraint:[NSLayoutConstraint constraintWithItem:compassContainer
+ attribute:NSLayoutAttributeWidth
+ relatedBy:NSLayoutRelationEqual
+ toItem:nil
+ attribute:NSLayoutAttributeNotAnAttribute
+ multiplier:1
+ constant:self.compass.image.size.width]];
+
+ [compassContainer addConstraint:[NSLayoutConstraint constraintWithItem:compassContainer
+ attribute:NSLayoutAttributeHeight
+ relatedBy:NSLayoutRelationEqual
+ toItem:nil
+ attribute:NSLayoutAttributeNotAnAttribute
+ multiplier:1
+ constant:self.compass.image.size.height]];
+
+ // logo bug
+ //
+ [constraintParentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:@"V:[logoBug]-bottomSpacing-%@", bottomGuideFormatString]
+ options:0
+ metrics:@{ @"bottomSpacing" : @(4) }
+ views:@{ @"logoBug" : self.logoBug,
+ @"bottomLayoutGuide" : bottomGuideViewsObject }]];
+
+ [constraintParentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-leftSpacing-[logoBug]"
+ options:0
+ metrics:@{ @"leftSpacing" : @(8) }
+ views:@{ @"logoBug" : self.logoBug }]];
+
+ // attribution button
+ //
+ [constraintParentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:@"V:[attributionButton]-bottomSpacing-%@", bottomGuideFormatString]
+ options:0
+ metrics:@{ @"bottomSpacing" : @(8) }
+ views:@{ @"attributionButton" : self.attributionButton,
+ @"bottomLayoutGuide" : bottomGuideViewsObject }]];
+
+ [constraintParentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[attributionButton]-rightSpacing-|"
+ options:0
+ metrics:@{ @"rightSpacing" : @(8) }
+ views:@{ @"attributionButton" : self.attributionButton }]];
+
+ [super updateConstraints];
+}
+
+- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
+{
+ mbglMap->resize(rect.size.width, rect.size.height, view.contentScaleFactor, view.drawableWidth, view.drawableHeight);
+}
+
+- (void)layoutSubviews
+{
+ mbglMap->update();
+
+ [super layoutSubviews];
+}
+
+#pragma mark - Conversions -
+
++ (CGFloat)degreesToRadians:(CGFloat)degrees
+{
+ return degrees * M_PI / 180;
+}
+
++ (CGFloat)radiansToDegrees:(CGFloat)radians
+{
+ return radians * 180 / M_PI;
+}
+
+#pragma mark - Life Cycle -
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-parameter"
+
+- (void)appDidBackground:(NSNotification *)notification
+{
+ mbglMap->stop();
+
+ [self.glView deleteDrawable];
+}
+
+- (void)appWillForeground:(NSNotification *)notification
+{
+ [self.glView bindDrawable];
+
+ mbglMap->start();
+}
+
+#pragma mark - Gestures -
+
+- (void)handleCompassTapGesture:(id)sender
+{
+ [self resetNorthAnimated:YES];
+}
+
+#pragma clang diagnostic pop
+
+- (void)handlePanGesture:(UIPanGestureRecognizer *)pan
+{
+ if ( ! self.isScrollEnabled) return;
+
+ mbglMap->cancelTransitions();
+
+ if (pan.state == UIGestureRecognizerStateBegan)
+ {
+ self.centerPoint = CGPointMake(0, 0);
+ }
+ else if (pan.state == UIGestureRecognizerStateChanged)
+ {
+ CGPoint delta = CGPointMake([pan translationInView:pan.view].x - self.centerPoint.x,
+ [pan translationInView:pan.view].y - self.centerPoint.y);
+
+ mbglMap->moveBy(delta.x, delta.y);
+
+ self.centerPoint = CGPointMake(self.centerPoint.x + delta.x, self.centerPoint.y + delta.y);
+ }
+ else if (pan.state == UIGestureRecognizerStateEnded || pan.state == UIGestureRecognizerStateCancelled)
+ {
+ CGPoint velocity = [pan velocityInView:pan.view];
+ CGFloat duration = 0;
+
+ if ( ! CGPointEqualToPoint(velocity, CGPointZero))
+ {
+ CGFloat ease = 0.25;
+
+ velocity.x = velocity.x * ease;
+ velocity.y = velocity.y * ease;
+
+ CGFloat speed = sqrt(velocity.x * velocity.x + velocity.y * velocity.y);
+ CGFloat deceleration = 2500;
+ duration = speed / (deceleration * ease);
+ }
+
+ CGPoint offset = CGPointMake(velocity.x * duration / 2, velocity.y * duration / 2);
+
+ mbglMap->moveBy(offset.x, offset.y, secondsAsDuration(duration));
+
+ if (duration)
+ {
+ self.animatingGesture = YES;
+
+ __weak MGLMapView *weakSelf = self;
+
+ [self animateWithDelay:duration animations:^
+ {
+ weakSelf.animatingGesture = NO;
+
+ [weakSelf notifyMapChange:@(mbgl::MapChangeRegionDidChangeAnimated)];
+ }];
+ }
+ }
+}
+
+- (void)handlePinchGesture:(UIPinchGestureRecognizer *)pinch
+{
+ if ( ! self.isZoomEnabled) return;
+
+ if (mbglMap->getZoom() <= mbglMap->getMinZoom() && pinch.scale < 1) return;
+
+ mbglMap->cancelTransitions();
+
+ if (pinch.state == UIGestureRecognizerStateBegan)
+ {
+ mbglMap->startScaling();
+
+ self.scale = mbglMap->getScale();
+ }
+ else if (pinch.state == UIGestureRecognizerStateChanged)
+ {
+ CGFloat newScale = self.scale * pinch.scale;
+
+ if (log2(newScale) < mbglMap->getMinZoom()) return;
+
+ double scale = mbglMap->getScale();
+
+ mbglMap->scaleBy(newScale / scale, [pinch locationInView:pinch.view].x, [pinch locationInView:pinch.view].y);
+ }
+ else if (pinch.state == UIGestureRecognizerStateEnded || pinch.state == UIGestureRecognizerStateCancelled)
+ {
+ mbglMap->stopScaling();
+
+ [self unrotateIfNeededAnimated:YES];
+
+ [self notifyMapChange:@(mbgl::MapChangeRegionDidChangeAnimated)];
+ }
+}
+
+- (void)handleRotateGesture:(UIRotationGestureRecognizer *)rotate
+{
+ if ( ! self.isRotateEnabled) return;
+
+ mbglMap->cancelTransitions();
+
+ if (rotate.state == UIGestureRecognizerStateBegan)
+ {
+ mbglMap->startRotating();
+
+ self.angle = [MGLMapView degreesToRadians:mbglMap->getBearing()] * -1;
+ }
+ else if (rotate.state == UIGestureRecognizerStateChanged)
+ {
+ CGFloat newDegrees = [MGLMapView radiansToDegrees:(self.angle + rotate.rotation)] * -1;
+
+ // constrain to +/-30 degrees when merely rotating like Apple does
+ //
+ if ( ! self.isRotationAllowed && fabsf(self.pinch.scale) < 10)
+ {
+ newDegrees = fminf(newDegrees, 30);
+ newDegrees = fmaxf(newDegrees, -30);
+ }
+
+ mbglMap->setBearing(newDegrees,
+ [rotate locationInView:rotate.view].x,
+ [rotate locationInView:rotate.view].y);
+ }
+ else if (rotate.state == UIGestureRecognizerStateEnded || rotate.state == UIGestureRecognizerStateCancelled)
+ {
+ mbglMap->stopRotating();
+
+ [self unrotateIfNeededAnimated:YES];
+
+ [self notifyMapChange:@(mbgl::MapChangeRegionDidChangeAnimated)];
+ }
+}
+
+- (void)handleDoubleTapGesture:(UITapGestureRecognizer *)doubleTap
+{
+ if ( ! self.isZoomEnabled) return;
+
+ mbglMap->cancelTransitions();
+
+ if (doubleTap.state == UIGestureRecognizerStateEnded)
+ {
+ mbglMap->scaleBy(2, [doubleTap locationInView:doubleTap.view].x, [doubleTap locationInView:doubleTap.view].y, secondsAsDuration(MGLAnimationDuration));
+
+ self.animatingGesture = YES;
+
+ __weak MGLMapView *weakSelf = self;
+
+ [self animateWithDelay:MGLAnimationDuration animations:^
+ {
+ weakSelf.animatingGesture = NO;
+
+ [weakSelf unrotateIfNeededAnimated:YES];
+
+ [weakSelf notifyMapChange:@(mbgl::MapChangeRegionDidChangeAnimated)];
+ }];
+ }
+}
+
+- (void)handleTwoFingerTapGesture:(UITapGestureRecognizer *)twoFingerTap
+{
+ if ( ! self.isZoomEnabled) return;
+
+ if (mbglMap->getZoom() == mbglMap->getMinZoom()) return;
+
+ mbglMap->cancelTransitions();
+
+ if (twoFingerTap.state == UIGestureRecognizerStateEnded)
+ {
+ mbglMap->scaleBy(0.5, [twoFingerTap locationInView:twoFingerTap.view].x, [twoFingerTap locationInView:twoFingerTap.view].y, secondsAsDuration(MGLAnimationDuration));
+
+ self.animatingGesture = YES;
+
+ __weak MGLMapView *weakSelf = self;
+
+ [self animateWithDelay:MGLAnimationDuration animations:^
+ {
+ weakSelf.animatingGesture = NO;
+
+ [weakSelf unrotateIfNeededAnimated:YES];
+
+ [weakSelf notifyMapChange:@(mbgl::MapChangeRegionDidChangeAnimated)];
+ }];
+ }
+}
+
+- (void)handleQuickZoomGesture:(UILongPressGestureRecognizer *)quickZoom
+{
+ if ( ! self.isZoomEnabled) return;
+
+ mbglMap->cancelTransitions();
+
+ if (quickZoom.state == UIGestureRecognizerStateBegan)
+ {
+ self.scale = mbglMap->getScale();
+
+ self.quickZoomStart = [quickZoom locationInView:quickZoom.view].y;
+ }
+ else if (quickZoom.state == UIGestureRecognizerStateChanged)
+ {
+ CGFloat distance = self.quickZoomStart - [quickZoom locationInView:quickZoom.view].y;
+
+ CGFloat newZoom = log2f(self.scale) + (distance / 100);
+
+ if (newZoom < mbglMap->getMinZoom()) return;
+
+ mbglMap->scaleBy(powf(2, newZoom) / mbglMap->getScale(), self.bounds.size.width / 2, self.bounds.size.height / 2);
+ }
+ else if (quickZoom.state == UIGestureRecognizerStateEnded || quickZoom.state == UIGestureRecognizerStateCancelled)
+ {
+ [self unrotateIfNeededAnimated:YES];
+
+ [self notifyMapChange:@(mbgl::MapChangeRegionDidChangeAnimated)];
+ }
+}
+
+- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
+{
+ NSArray *validSimultaneousGestures = @[ self.pan, self.pinch, self.rotate ];
+
+ return ([validSimultaneousGestures containsObject:gestureRecognizer] && [validSimultaneousGestures containsObject:otherGestureRecognizer]);
+}
+
+#pragma mark - Settings -
+
+- (void)tintColorDidChange
+{
+ for (UIView *subview in self.subviews)
+ {
+ if ([subview respondsToSelector:@selector(setTintColor:)])
+ {
+ subview.tintColor = self.tintColor;
+ }
+ }
+}
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-parameter"
+
+- (void)showAttribution:(id)sender
+{
+ [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"https://www.mapbox.com/about/maps/"]];
+}
+
+#pragma clang diagnostic pop
+
+- (void)setDebugActive:(BOOL)debugActive
+{
+ mbglMap->setDebug(debugActive);
+}
+
+- (BOOL)isDebugActive
+{
+ return mbglMap->getDebug();
+}
+
+- (void)resetNorth
+{
+ [self resetNorthAnimated:YES];
+}
+
+- (void)resetNorthAnimated:(BOOL)animated
+{
+ CGFloat duration = (animated ? MGLAnimationDuration : 0);
+
+ mbglMap->setBearing(0, secondsAsDuration(duration));
+
+ [UIView animateWithDuration:duration
+ animations:^
+ {
+ self.compass.transform = CGAffineTransformIdentity;
+ }
+ completion:^(BOOL finished)
+ {
+ if (finished)
+ {
+ [UIView animateWithDuration:MGLAnimationDuration
+ animations:^
+ {
+ self.compass.alpha = 0;
+ }];
+ }
+ }];
+}
+
+- (void)resetPosition
+{
+ mbglMap->resetPosition();
+}
+
+- (void)toggleDebug
+{
+ mbglMap->toggleDebug();
+}
+
+- (void)setCenterCoordinate:(CLLocationCoordinate2D)coordinate animated:(BOOL)animated
+{
+ CGFloat duration = (animated ? MGLAnimationDuration : 0);
+
+ mbglMap->setLonLat(coordinate.longitude, coordinate.latitude, secondsAsDuration(duration));
+}
+
+- (void)setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
+{
+ [self setCenterCoordinate:centerCoordinate animated:NO];
+}
+
+- (CLLocationCoordinate2D)centerCoordinate
+{
+ double lon, lat;
+ mbglMap->getLonLat(lon, lat);
+
+ return CLLocationCoordinate2DMake(lat, lon);
+}
+
+- (void)setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate zoomLevel:(double)zoomLevel animated:(BOOL)animated
+{
+ CGFloat duration = (animated ? MGLAnimationDuration : 0);
+
+ mbglMap->setLonLatZoom(centerCoordinate.longitude, centerCoordinate.latitude, zoomLevel, secondsAsDuration(duration));
+
+ [self unrotateIfNeededAnimated:animated];
+}
+
+- (double)zoomLevel
+{
+ return mbglMap->getZoom();
+}
+
+- (void)setZoomLevel:(double)zoomLevel animated:(BOOL)animated
+{
+ CGFloat duration = (animated ? MGLAnimationDuration : 0);
+
+ mbglMap->setZoom(zoomLevel, secondsAsDuration(duration));
+
+ [self unrotateIfNeededAnimated:animated];
+}
+
+- (void)setZoomLevel:(double)zoomLevel
+{
+ [self setZoomLevel:zoomLevel animated:NO];
+}
+
+- (CLLocationDirection)direction
+{
+ double direction = mbglMap->getBearing() * -1;
+
+ while (direction > 360) direction -= 360;
+ while (direction < 0) direction += 360;
+
+ return direction;
+}
+
+- (void)setDirection:(CLLocationDirection)direction animated:(BOOL)animated
+{
+ if ( ! animated && ! self.rotationAllowed) return;
+
+ CGFloat duration = (animated ? MGLAnimationDuration : 0);
+
+ mbglMap->setBearing(direction * -1, secondsAsDuration(duration));
+}
+
+- (void)setDirection:(CLLocationDirection)direction
+{
+ [self setDirection:direction animated:NO];
+}
+
+#pragma mark - Styling -
+
+- (NSDictionary *)getRawStyle
+{
+ const std::string styleJSON = mbglMap->getStyleJSON();
+
+ return [NSJSONSerialization JSONObjectWithData:[@(styleJSON.c_str()) dataUsingEncoding:[NSString defaultCStringEncoding]] options:0 error:nil];
+}
+
+- (void)setRawStyle:(NSDictionary *)style
+{
+ NSData *data = [NSJSONSerialization dataWithJSONObject:style options:0 error:nil];
+
+ [self setStyleJSON:[[NSString alloc] initWithData:data encoding:[NSString defaultCStringEncoding]]];
+}
+
+- (NSArray *)bundledStyleNames
+{
+ NSString *stylesPath = [[MGLMapView resourceBundlePath] stringByAppendingString:@"/styles"];
+
+ NSArray *styleNames = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:stylesPath error:nil];
+
+ return styleNames;
+}
+
+- (void)useBundledStyleNamed:(NSString *)styleName
+{
+ [self setStyleURL:[NSString stringWithFormat:@"styles/%@.json", styleName]];
+}
+
+- (NSArray *)getStyleOrderedLayerNames
+{
+ return [[self getRawStyle] valueForKeyPath:@"layers.id"];
+}
+
+- (void)setStyleOrderedLayerNames:(NSArray *)orderedLayerNames
+{
+ NSMutableDictionary *style = [[self getRawStyle] deepMutableCopy];
+ NSArray *oldLayers = style[@"layers"];
+ NSMutableArray *newLayers = [NSMutableArray array];
+
+ if ([orderedLayerNames count] != [[oldLayers valueForKeyPath:@"id"] count])
+ {
+ [NSException raise:@"invalid layer count"
+ format:@"new layer count (%lu) should equal existing layer count (%lu)",
+ (unsigned long)[orderedLayerNames count],
+ (unsigned long)[[oldLayers valueForKeyPath:@"id"] count]];
+ }
+ else
+ {
+ for (NSString *newLayerName in orderedLayerNames)
+ {
+ if ( ! [[oldLayers valueForKeyPath:@"id"] containsObject:newLayerName])
+ {
+ [NSException raise:@"invalid layer name"
+ format:@"layer name %@ unknown",
+ newLayerName];
+ }
+ else
+ {
+ NSDictionary *newLayer = [oldLayers objectAtIndex:[[oldLayers valueForKeyPath:@"id"] indexOfObject:newLayerName]];
+ [newLayers addObject:newLayer];
+ }
+ }
+ }
+
+ [style setValue:newLayers forKey:@"layers"];
+
+ [self setRawStyle:style];
+}
+
+- (NSArray *)getAppliedStyleClasses
+{
+ NSMutableArray *returnArray = [NSMutableArray array];
+
+ const std::vector<std::string> &appliedClasses = mbglMap->getClasses();
+
+ for (auto class_it = appliedClasses.begin(); class_it != appliedClasses.end(); class_it++)
+ {
+ [returnArray addObject:@(class_it->c_str())];
+ }
+
+ return returnArray;
+}
+
+- (void)setAppliedStyleClasses:(NSArray *)appliedClasses
+{
+ [self setAppliedStyleClasses:appliedClasses transitionDuration:0];
+}
+
+- (void)setAppliedStyleClasses:(NSArray *)appliedClasses transitionDuration:(NSTimeInterval)transitionDuration
+{
+ std::vector<std::string> newAppliedClasses;
+
+ for (NSString *appliedClass in appliedClasses)
+ {
+ newAppliedClasses.insert(newAppliedClasses.end(), [appliedClass cStringUsingEncoding:[NSString defaultCStringEncoding]]);
+ }
+
+ mbglMap->setDefaultTransitionDuration(secondsAsDuration(transitionDuration));
+ mbglMap->setClasses(newAppliedClasses);
+}
+
+- (NSString *)getKeyTypeForLayer:(NSString *)layerName
+{
+ NSDictionary *style = [self getRawStyle];
+
+ NSString *bucketType;
+
+ if ([layerName isEqualToString:@"background"])
+ {
+ bucketType = @"background";
+ }
+ else
+ {
+ for (NSDictionary *layer in style[@"structure"])
+ {
+ if ([layer[@"name"] isEqualToString:layerName])
+ {
+ bucketType = style[@"buckets"][layer[@"bucket"]][@"type"];
+ break;
+ }
+ }
+ }
+
+ NSString *keyType;
+
+ if ([bucketType isEqualToString:@"fill"])
+ {
+ keyType = MGLStyleKeyFill;
+ }
+ else if ([bucketType isEqualToString:@"line"])
+ {
+ keyType = MGLStyleKeyLine;
+ }
+ else if ([bucketType isEqualToString:@"point"])
+ {
+ keyType = MGLStyleKeyIcon;
+ }
+ else if ([bucketType isEqualToString:@"text"])
+ {
+ keyType = MGLStyleKeyText;
+ }
+ else if ([bucketType isEqualToString:@"raster"])
+ {
+ keyType = MGLStyleKeyRaster;
+ }
+ else if ([bucketType isEqualToString:@"composite"])
+ {
+ keyType = MGLStyleKeyComposite;
+ }
+ else if ([bucketType isEqualToString:@"background"])
+ {
+ keyType = MGLStyleKeyBackground;
+ }
+ else
+ {
+ [NSException raise:@"invalid bucket type"
+ format:@"bucket type %@ unknown",
+ bucketType];
+ }
+
+ return keyType;
+}
+
+- (NSDictionary *)getStyleDescriptionForLayer:(NSString *)layerName inClass:(NSString *)className
+{
+ NSDictionary *style = [self getRawStyle];
+
+ if ( ! [[style valueForKeyPath:@"classes.name"] containsObject:className])
+ {
+ [NSException raise:@"invalid class name"
+ format:@"class name %@ unknown",
+ className];
+ }
+
+ NSUInteger classNumber = [[style valueForKeyPath:@"classes.name"] indexOfObject:className];
+
+ if ( ! [[style[@"classes"][classNumber][@"layers"] allKeys] containsObject:layerName])
+ {
+ // layer specified in structure, but not styled
+ //
+ return nil;
+ }
+
+ NSDictionary *layerStyle = style[@"classes"][classNumber][@"layers"][layerName];
+
+ NSMutableDictionary *styleDescription = [NSMutableDictionary dictionary];
+
+ for (NSString *keyName in [layerStyle allKeys])
+ {
+ id value = layerStyle[keyName];
+
+ while ([[style[@"constants"] allKeys] containsObject:value])
+ {
+ value = style[@"constants"][value];
+ }
+
+ if ([[self.allowedStyleTypes[MGLStyleKeyGeneric] allKeys] containsObject:keyName])
+ {
+ [styleDescription setValue:[self typedPropertyForKeyName:keyName
+ ofType:MGLStyleKeyGeneric
+ withValue:value]
+ forKey:keyName];
+ }
+
+ NSString *keyType = [self getKeyTypeForLayer:layerName];
+
+ if ([[self.allowedStyleTypes[keyType] allKeys] containsObject:keyName])
+ {
+ [styleDescription setValue:[self typedPropertyForKeyName:keyName
+ ofType:keyType
+ withValue:value]
+ forKey:keyName];
+ }
+ }
+
+ return styleDescription;
+}
+
+- (NSDictionary *)typedPropertyForKeyName:(NSString *)keyName ofType:(NSString *)keyType withValue:(id)value
+{
+ if ( ! [[self.allowedStyleTypes[keyType] allKeys] containsObject:keyName])
+ {
+ [NSException raise:@"invalid property name"
+ format:@"property name %@ unknown",
+ keyName];
+ }
+
+ NSArray *typeInfo = self.allowedStyleTypes[keyType][keyName];
+
+ if ([value isKindOfClass:[NSArray class]] && ! [typeInfo containsObject:MGLStyleValueTypeColor])
+ {
+ if ([typeInfo containsObject:MGLStyleValueFunctionAllowed])
+ {
+ if ([[(NSArray *)value firstObject] isKindOfClass:[NSString class]])
+ {
+ NSString *functionType;
+
+ if ([[(NSArray *)value firstObject] isEqualToString:@"linear"])
+ {
+ functionType = MGLStyleValueTypeFunctionLinear;
+ }
+ else if ([[(NSArray *)value firstObject] isEqualToString:@"stops"])
+ {
+ functionType = MGLStyleValueTypeFunctionStops;
+ }
+ else if ([[(NSArray *)value firstObject] isEqualToString:@"exponential"])
+ {
+ functionType = MGLStyleValueTypeFunctionExponential;
+ }
+ else if ([[(NSArray *)value firstObject] isEqualToString:@"min"])
+ {
+ functionType = MGLStyleValueTypeFunctionMinimumZoom;
+ }
+ else if ([[(NSArray *)value firstObject] isEqualToString:@"max"])
+ {
+ functionType = MGLStyleValueTypeFunctionMaximumZoom;
+ }
+
+ if (functionType)
+ {
+ return @{ @"type" : functionType,
+ @"value" : value };
+ }
+ }
+ }
+ else if ([typeInfo containsObject:MGLStyleValueTypeNumberPair])
+ {
+ return @{ @"type" : MGLStyleValueTypeNumberPair,
+ @"value" : value };
+ }
+ }
+ else if ([typeInfo containsObject:MGLStyleValueTypeNumber])
+ {
+ return @{ @"type" : MGLStyleValueTypeNumber,
+ @"value" : value };
+ }
+ else if ([typeInfo containsObject:MGLStyleValueTypeBoolean])
+ {
+ return @{ @"type" : MGLStyleValueTypeBoolean,
+ @"value" : @([(NSString *)value boolValue]) };
+ }
+ else if ([typeInfo containsObject:MGLStyleValueTypeString])
+ {
+ return @{ @"type" : MGLStyleValueTypeString,
+ @"value" : value };
+ }
+ else if ([typeInfo containsObject:MGLStyleValueTypeColor])
+ {
+ UIColor *color;
+
+ if ([(NSString *)value hasPrefix:@"#"])
+ {
+ color = [UIColor colorWithHexString:value];
+ }
+ else if ([(NSString *)value hasPrefix:@"rgb"])
+ {
+ color = [UIColor colorWithRGBAString:value];
+ }
+ else if ([(NSString *)value hasPrefix:@"hsl"])
+ {
+ [NSException raise:@"invalid color format"
+ format:@"HSL color format not yet supported natively"];
+ }
+ else if ([value isKindOfClass:[NSArray class]] && [(NSArray *)value count] == 4)
+ {
+ color = [UIColor colorWithRed:[value[0] floatValue]
+ green:[value[1] floatValue]
+ blue:[value[2] floatValue]
+ alpha:[value[3] floatValue]];
+ }
+ else if ([[UIColor class] respondsToSelector:NSSelectorFromString([NSString stringWithFormat:@"%@Color", [(NSString *)value lowercaseString]])])
+ {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+
+ color = [[UIColor class] performSelector:NSSelectorFromString([NSString stringWithFormat:@"%@Color", [(NSString *)value lowercaseString]])];
+
+#pragma clang diagnostic pop
+ }
+
+ return @{ @"type" : MGLStyleValueTypeColor,
+ @"value" : color };
+ }
+
+ return nil;
+}
+
+- (void)setStyleDescription:(NSDictionary *)styleDescription forLayer:(NSString *)layerName inClass:(NSString *)className
+{
+#pragma unused(className)
+
+ NSMutableDictionary *convertedStyle = [NSMutableDictionary dictionary];
+
+ for (NSString *key in [styleDescription allKeys])
+ {
+ NSArray *styleParameters = nil;
+
+ if ([[self.allowedStyleTypes[MGLStyleKeyGeneric] allKeys] containsObject:key])
+ {
+ styleParameters = self.allowedStyleTypes[MGLStyleKeyGeneric][key];
+ }
+ else
+ {
+ NSString *keyType = [self getKeyTypeForLayer:layerName];
+
+ if ([[self.allowedStyleTypes[keyType] allKeys] containsObject:key])
+ {
+ styleParameters = self.allowedStyleTypes[keyType][key];
+ }
+ }
+
+ if (styleParameters)
+ {
+ if ([styleDescription[key][@"value"] isKindOfClass:[MGLStyleFunctionValue class]])
+ {
+ convertedStyle[key] = [(MGLStyleFunctionValue *)styleDescription[key][@"value"] rawStyle];
+ }
+ else if ([styleParameters containsObject:styleDescription[key][@"type"]])
+ {
+ NSString *valueType = styleDescription[key][@"type"];
+
+ if ([valueType isEqualToString:MGLStyleValueTypeColor])
+ {
+ convertedStyle[key] = [@"#" stringByAppendingString:[(UIColor *)styleDescription[key][@"value"] hexStringFromColor]];
+ }
+ else
+ {
+ // the rest (bool/number/pair/string) are already JSON-convertible types
+ //
+ convertedStyle[key] = styleDescription[key][@"value"];
+ }
+ }
+ }
+ else
+ {
+ [NSException raise:@"invalid style description format"
+ format:@"unable to parse key '%@'",
+ key];
+ }
+ }
+
+// NSMutableDictionary *style = [[self getRawStyle] deepMutableCopy];
+//
+// NSUInteger classIndex = [[[self getAllStyleClasses] valueForKey:@"name"] indexOfObject:className];
+//
+// style[@"classes"][classIndex][@"layers"][layerName] = convertedStyle;
+//
+// [self setRawStyle:style];
+}
+
+- (NSDictionary *)allowedStyleTypes
+{
+ static NSDictionary *MGLStyleAllowedTypes = @{
+ MGLStyleKeyGeneric : @{
+ @"enabled" : @[ MGLStyleValueTypeBoolean, MGLStyleValueFunctionAllowed ],
+ @"translate" : @[ MGLStyleValueTypeNumberPair, MGLStyleValueFunctionAllowed ],
+ @"translate-anchor" : @[ MGLStyleValueTypeString, MGLStyleValueFunctionAllowed ],
+ @"opacity" : @[ MGLStyleValueTypeNumber, MGLStyleValueFunctionAllowed ],
+ @"prerender" : @[ MGLStyleValueTypeBoolean ],
+ @"prerender-buffer" : MGLStyleValueTypeNumber,
+ @"prerender-size" : @[ MGLStyleValueTypeNumber ],
+ @"prerender-blur" : @[ MGLStyleValueTypeNumber ] },
+ MGLStyleKeyFill : @{
+ @"color" : @[ MGLStyleValueTypeColor ],
+ @"stroke" : @[ MGLStyleValueTypeColor ],
+ @"antialias" : @[ MGLStyleValueTypeBoolean ],
+ @"image" : @[ MGLStyleValueTypeString ] },
+ MGLStyleKeyLine : @{
+ @"color" : @[ MGLStyleValueTypeColor ],
+ @"width" : @[ MGLStyleValueTypeNumber, MGLStyleValueFunctionAllowed ],
+ @"dasharray" : @[ MGLStyleValueTypeNumberPair, MGLStyleValueFunctionAllowed ] },
+ MGLStyleKeyIcon : @{
+ @"color" : @[ MGLStyleValueTypeColor ],
+ @"image" : @[ MGLStyleValueTypeString ],
+ @"size" : @[ MGLStyleValueTypeNumber, MGLStyleValueFunctionAllowed ],
+ @"radius" : @[ MGLStyleValueTypeNumber, MGLStyleValueFunctionAllowed],
+ @"blur" : @[ MGLStyleValueTypeNumber, MGLStyleValueFunctionAllowed ] },
+ MGLStyleKeyText : @{
+ @"color" : @[ MGLStyleValueTypeColor ],
+ @"stroke" : @[ MGLStyleValueTypeColor ],
+ @"strokeWidth" : @[ MGLStyleValueTypeNumber, MGLStyleValueFunctionAllowed ],
+ @"strokeBlur" : @[ MGLStyleValueTypeNumber, MGLStyleValueFunctionAllowed ],
+ @"size" : @[ MGLStyleValueTypeNumber, MGLStyleValueFunctionAllowed ],
+ @"rotate" : @[ MGLStyleValueTypeNumber, MGLStyleValueFunctionAllowed ],
+ @"alwaysVisible" : @[ MGLStyleValueTypeBoolean ] },
+ MGLStyleKeyRaster : @{},
+ MGLStyleKeyComposite : @{},
+ MGLStyleKeyBackground : @{
+ @"color" : @[ MGLStyleValueTypeColor ] }
+ };
+
+ return MGLStyleAllowedTypes;
+}
+
+#pragma mark - Utility -
+
+- (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);
+}
+
+- (BOOL)isRotationAllowed
+{
+ return (self.zoomLevel > 3);
+}
+
+// correct rotations to north as needed
+//
+- (void)unrotateIfNeededAnimated:(BOOL)animated
+{
+ // don't worry about it in the midst of pinch or rotate gestures
+ //
+ if (self.pinch.state == UIGestureRecognizerStateChanged || self.rotate.state == UIGestureRecognizerStateChanged) return;
+
+ // but otherwise, do
+ //
+ if (self.direction != 0 && ! self.isRotationAllowed)
+ {
+ if (animated)
+ {
+ self.animatingGesture = YES;
+
+ self.userInteractionEnabled = NO;
+
+ __weak MGLMapView *weakSelf = self;
+
+ [self animateWithDelay:0.1 animations:^
+ {
+ [weakSelf resetNorthAnimated:YES];
+
+ [self animateWithDelay:MGLAnimationDuration animations:^
+ {
+ weakSelf.userInteractionEnabled = YES;
+
+ self.animatingGesture = NO;
+ }];
+
+ }];
+ }
+ else
+ {
+ [self resetNorthAnimated:NO];
+ }
+ }
+}
+
+- (void)unsuspendRegionChangeDelegateQueue
+{
+ @synchronized (self.regionChangeDelegateQueue)
+ {
+ [self.regionChangeDelegateQueue setSuspended:NO];
+ }
+}
+
+- (void)notifyMapChange:(NSNumber *)change
+{
+ switch ([change unsignedIntegerValue])
+ {
+ case mbgl::MapChangeRegionWillChange:
+ case mbgl::MapChangeRegionWillChangeAnimated:
+ {
+ BOOL animated = ([change unsignedIntegerValue] == mbgl::MapChangeRegionWillChangeAnimated);
+
+ @synchronized (self.regionChangeDelegateQueue)
+ {
+ if ([self.regionChangeDelegateQueue operationCount] == 0)
+ {
+ if ([self.delegate respondsToSelector:@selector(mapView:regionWillChangeAnimated:)])
+ {
+ [self.delegate mapView:self regionWillChangeAnimated:animated];
+ }
+ }
+
+ [self.regionChangeDelegateQueue setSuspended:YES];
+
+ if ([self.regionChangeDelegateQueue operationCount] == 0)
+ {
+ [self.regionChangeDelegateQueue addOperationWithBlock:^
+ {
+ dispatch_async(dispatch_get_main_queue(), ^
+ {
+ if ([self.delegate respondsToSelector:@selector(mapView:regionDidChangeAnimated:)])
+ {
+ [self.delegate mapView:self regionDidChangeAnimated:animated];
+ }
+ });
+ }];
+ }
+ }
+ break;
+ }
+ case mbgl::MapChangeRegionDidChange:
+ case mbgl::MapChangeRegionDidChangeAnimated:
+ {
+ [self updateCompass];
+
+ if (self.pan.state == UIGestureRecognizerStateChanged ||
+ self.pinch.state == UIGestureRecognizerStateChanged ||
+ self.rotate.state == UIGestureRecognizerStateChanged ||
+ self.quickZoom.state == UIGestureRecognizerStateChanged) return;
+
+ if (self.isAnimatingGesture) return;
+
+ [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(unsuspendRegionChangeDelegateQueue) object:nil];
+ [self performSelector:@selector(unsuspendRegionChangeDelegateQueue) withObject:nil afterDelay:0];
+
+ break;
+ }
+ case mbgl::MapChangeWillStartLoadingMap:
+ {
+ if ([self.delegate respondsToSelector:@selector(mapViewWillStartLoadingMap:)])
+ {
+ [self.delegate mapViewWillStartLoadingMap:self];
+ }
+ break;
+ }
+ case mbgl::MapChangeDidFinishLoadingMap:
+ {
+ if ([self.delegate respondsToSelector:@selector(mapViewDidFinishLoadingMap:)])
+ {
+ [self.delegate mapViewDidFinishLoadingMap:self];
+ }
+ break;
+ }
+ case mbgl::MapChangeDidFailLoadingMap:
+ {
+ if ([self.delegate respondsToSelector:@selector(mapViewDidFailLoadingMap:withError::)])
+ {
+ [self.delegate mapViewDidFailLoadingMap:self withError:nil];
+ }
+ break;
+ }
+ case mbgl::MapChangeWillStartRenderingMap:
+ {
+ if ([self.delegate respondsToSelector:@selector(mapViewWillStartRenderingMap:)])
+ {
+ [self.delegate mapViewWillStartRenderingMap:self];
+ }
+ break;
+ }
+ case mbgl::MapChangeDidFinishRenderingMap:
+ {
+ if ([self.delegate respondsToSelector:@selector(mapViewDidFinishRenderingMap:fullyRendered:)])
+ {
+ [self.delegate mapViewDidFinishRenderingMap:self fullyRendered:NO];
+ }
+ break;
+ }
+ case mbgl::MapChangeDidFinishRenderingMapFullyRendered:
+ {
+ if ([self.delegate respondsToSelector:@selector(mapViewDidFinishRenderingMap:fullyRendered:)])
+ {
+ [self.delegate mapViewDidFinishRenderingMap:self fullyRendered:YES];
+ }
+ break;
+ }
+ }
+}
+
+- (void)updateCompass
+{
+ double degrees = mbglMap->getBearing() * -1;
+ while (degrees >= 360) degrees -= 360;
+ while (degrees < 0) degrees += 360;
+
+ self.compass.transform = CGAffineTransformMakeRotation([MGLMapView degreesToRadians:degrees]);
+
+ if (mbglMap->getBearing() && self.compass.alpha < 1)
+ {
+ [UIView animateWithDuration:MGLAnimationDuration
+ delay:0
+ options:UIViewAnimationOptionBeginFromCurrentState
+ animations:^
+ {
+ self.compass.alpha = 1;
+ }
+ completion:nil];
+ }
+}
+
++ (UIImage *)resourceImageNamed:(NSString *)imageName
+{
+ if ( ! [[imageName pathExtension] length])
+ {
+ imageName = [imageName stringByAppendingString:@".png"];
+ }
+
+ return [UIImage imageWithContentsOfFile:[MGLMapView pathForBundleResourceNamed:imageName ofType:nil inDirectory:@""]];
+}
+
++ (NSString *)pathForBundleResourceNamed:(NSString *)name ofType:(NSString *)extension inDirectory:(NSString *)directory
+{
+ NSString *path = [[NSBundle bundleWithPath:[MGLMapView resourceBundlePath]] pathForResource:name ofType:extension inDirectory:directory];
+
+ NSAssert(path, @"Resource not found in application.");
+
+ return path;
+}
+
++ (NSString *)resourceBundlePath
+{
+ NSString *resourceBundlePath = [[NSBundle bundleForClass:[MGLMapView class]] pathForResource:@"MapboxGL" ofType:@"bundle"];
+
+ if ( ! resourceBundlePath) resourceBundlePath = [[NSBundle mainBundle] bundlePath];
+
+ return resourceBundlePath;
+}
+
+- (void)swap
+{
+ if (mbglMap->needsSwap())
+ {
+ [self.glView display];
+ mbglMap->swapped();
+ }
+}
+
+class MBGLView : public mbgl::View
+{
+ public:
+ MBGLView(MGLMapView *nativeView_) : nativeView(nativeView_) {}
+ virtual ~MBGLView() {}
+
+
+ void notify()
+ {
+ // no-op
+ }
+
+ void notifyMapChange(mbgl::MapChange change, std::chrono::steady_clock::duration delay = std::chrono::steady_clock::duration::zero())
+ {
+ if (delay != std::chrono::steady_clock::duration::zero())
+ {
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, std::chrono::duration_cast<std::chrono::nanoseconds>(delay).count()), dispatch_get_main_queue(), ^
+ {
+ [nativeView performSelector:@selector(notifyMapChange:)
+ withObject:@(change)
+ afterDelay:0];
+ });
+ }
+ else
+ {
+ dispatch_async(dispatch_get_main_queue(), ^
+ {
+ [nativeView performSelector:@selector(notifyMapChange:)
+ withObject:@(change)
+ afterDelay:0];
+ });
+ }
+ }
+
+ void activate()
+ {
+ [EAGLContext setCurrentContext:nativeView.context];
+ }
+
+ void deactivate()
+ {
+ [EAGLContext setCurrentContext:nil];
+ }
+
+ void swap()
+ {
+ [nativeView performSelectorOnMainThread:@selector(swap)
+ withObject:nil
+ waitUntilDone:NO];
+ }
+
+ private:
+ __weak MGLMapView *nativeView = nullptr;
+};
+
+@end
diff --git a/platform/ios/MGLStyleFunctionValue.m b/platform/ios/MGLStyleFunctionValue.m
new file mode 100644
index 0000000000..a319114eef
--- /dev/null
+++ b/platform/ios/MGLStyleFunctionValue.m
@@ -0,0 +1,190 @@
+#import "MGLStyleFunctionValue.h"
+
+#import "MGLTypes.h"
+
+@interface MGLStyleFunctionValue ()
+
+@property (nonatomic) NSString *functionType;
+@property (nonatomic) NSDictionary *stops;
+@property (nonatomic) CGFloat zBase;
+@property (nonatomic) CGFloat val;
+@property (nonatomic) CGFloat slope;
+@property (nonatomic) CGFloat min;
+@property (nonatomic) CGFloat max;
+@property (nonatomic) CGFloat minimumZoom;
+@property (nonatomic) CGFloat maximumZoom;
+
+- (NSDictionary *)rawStyle;
+
+@end
+
+@implementation MGLStyleFunctionValue
+
+- (id)initWithFunctionType:(NSString *)functionType
+ stops:(NSDictionary *)stops
+ zBase:(CGFloat)zBase
+ val:(CGFloat)val
+ slope:(CGFloat)slope
+ min:(CGFloat)min
+ max:(CGFloat)max
+ minimumZoom:(CGFloat)minimumZoom
+ maximumZoom:(CGFloat)maximumZoom
+{
+ self = [super init];
+
+ if (self)
+ {
+ _functionType = functionType;
+ _stops = stops;
+ _zBase = zBase;
+ _val = val;
+ _slope = slope;
+ _min = min;
+ _max = max;
+ _minimumZoom = minimumZoom;
+ _maximumZoom = maximumZoom;
+ }
+
+ return self;
+}
+
+- (id)rawStyle
+{
+ if ([self.functionType isEqualToString:MGLStyleValueTypeFunctionMinimumZoom])
+ {
+ return @[ @"min", @(self.minimumZoom) ];
+ }
+ else if ([self.functionType isEqualToString:MGLStyleValueTypeFunctionMaximumZoom])
+ {
+ return @[ @"max", @(self.maximumZoom) ];
+ }
+ else if ([self.functionType isEqualToString:MGLStyleValueTypeFunctionLinear])
+ {
+ return @[ @"linear", @(self.zBase), @(self.val), @(self.slope), @(self.min), @(self.max) ];
+ }
+ else if ([self.functionType isEqualToString:MGLStyleValueTypeFunctionExponential])
+ {
+ return @[ @"exponential", @(self.zBase), @(self.val), @(self.slope), @(self.min), @(self.max) ];
+ }
+ else if ([self.functionType isEqualToString:MGLStyleValueTypeFunctionStops])
+ {
+ NSMutableArray *returnArray = [NSMutableArray array];
+
+ for (NSNumber *z in [self.stops allKeys])
+ {
+ [returnArray addObject:@{ @"z" : z, @"val" : self.stops[z] }];
+ }
+
+ [returnArray insertObject:@"stops" atIndex:0];
+
+ return returnArray;
+ }
+
+ return nil;
+}
+
++ (instancetype)stopsFunctionWithZoomLevelsAndValues:(NSNumber *)firstZoom, ...
+{
+ NSMutableArray *numbersArray = [NSMutableArray array];
+
+ va_list args;
+ va_start(args, firstZoom);
+
+ for (NSNumber *arg = firstZoom; arg != nil; arg = va_arg(args, NSNumber *))
+ {
+ [numbersArray addObject:arg];
+ }
+
+ va_end(args);
+
+ NSAssert([numbersArray count] % 2 == 0, @"invalid number of arguments");
+
+ NSMutableDictionary *stops = [NSMutableDictionary dictionary];
+
+ for (NSUInteger i = 0; i < [numbersArray count]; i = i + 2)
+ {
+ stops[numbersArray[i]] = stops[numbersArray[i + 1]];
+ }
+
+ return [[self alloc] initWithFunctionType:MGLStyleValueTypeFunctionStops
+ stops:stops
+ zBase:0
+ val:0
+ slope:0
+ min:0
+ max:0
+ minimumZoom:0
+ maximumZoom:0];
+}
+
++ (instancetype)linearFunctionWithBaseZoomLevel:(CGFloat)zBase
+ initialValue:(CGFloat)val
+ slope:(CGFloat)slope
+ minimumValue:(CGFloat)min
+ maximumValue:(CGFloat)max
+{
+ NSAssert(zBase >= 0 && zBase <= 18, @"invalid base zoom level");
+ NSAssert(min < max, @"minimum value must be less than maximum value");
+
+ return [[self alloc] initWithFunctionType:MGLStyleValueTypeFunctionLinear
+ stops:nil
+ zBase:zBase
+ val:val
+ slope:slope
+ min:min
+ max:max
+ minimumZoom:0
+ maximumZoom:0];
+}
+
++ (instancetype)exponentialFunctionWithBaseZoomLevel:(CGFloat)zBase
+ initialValue:(CGFloat)val
+ slope:(CGFloat)slope
+ minimumValue:(CGFloat)min
+ maximumValue:(CGFloat)max
+{
+ NSAssert(zBase >= 0 && zBase <= 18, @"invalid base zoom level");
+ NSAssert(min < max, @"minimum value must be less than maximum value");
+
+ return [[self alloc] initWithFunctionType:MGLStyleValueTypeFunctionExponential
+ stops:nil
+ zBase:zBase
+ val:val
+ slope:slope
+ min:min
+ max:max
+ minimumZoom:0
+ maximumZoom:0];
+}
+
++ (instancetype)minimumZoomLevelFunction:(CGFloat)minimumZoom
+{
+ NSAssert(minimumZoom >= 0 && minimumZoom <= 18, @"invalid minimum zoom value");
+
+ return [[self alloc] initWithFunctionType:MGLStyleValueTypeFunctionMinimumZoom
+ stops:nil
+ zBase:0
+ val:0
+ slope:0
+ min:0
+ max:0
+ minimumZoom:minimumZoom
+ maximumZoom:0];
+}
+
++ (instancetype)maximumZoomLevelFunction:(CGFloat)maximumZoom
+{
+ NSAssert(maximumZoom >= 0 && maximumZoom <= 18, @"invalid maximum zoom value");
+
+ return [[self alloc] initWithFunctionType:MGLStyleValueTypeFunctionMaximumZoom
+ stops:nil
+ zBase:0
+ val:0
+ slope:0
+ min:0
+ max:0
+ minimumZoom:0
+ maximumZoom:maximumZoom];
+}
+
+@end
diff --git a/platform/ios/MGLTypes.m b/platform/ios/MGLTypes.m
new file mode 100644
index 0000000000..2928bc2f4c
--- /dev/null
+++ b/platform/ios/MGLTypes.m
@@ -0,0 +1,24 @@
+#import "MGLTypes.h"
+
+NSString *const MGLStyleKeyGeneric = @"MGLStyleKeyGeneric";
+NSString *const MGLStyleKeyFill = @"MGLStyleKeyFill";
+NSString *const MGLStyleKeyLine = @"MGLStyleKeyLine";
+NSString *const MGLStyleKeyIcon = @"MGLStyleKeyIcon";
+NSString *const MGLStyleKeyText = @"MGLStyleKeyText";
+NSString *const MGLStyleKeyRaster = @"MGLStyleKeyRaster";
+NSString *const MGLStyleKeyComposite = @"MGLStyleKeyComposite";
+NSString *const MGLStyleKeyBackground = @"MGLStyleKeyBackground";
+
+NSString *const MGLStyleValueTypeBoolean = @"MGLStyleValueTypeBoolean";
+NSString *const MGLStyleValueTypeNumber = @"MGLStyleValueTypeNumber";
+NSString *const MGLStyleValueTypeNumberPair = @"MGLStyleValueTypeNumberPair";
+NSString *const MGLStyleValueTypeColor = @"MGLStyleValueTypeColor";
+NSString *const MGLStyleValueTypeString = @"MGLStyleValueTypeString";
+
+NSString *const MGLStyleValueFunctionAllowed = @"MGLStyleValueFunctionAllowed";
+
+NSString *const MGLStyleValueTypeFunctionMinimumZoom = @"MGLStyleValueTypeFunctionMinimumZoom";
+NSString *const MGLStyleValueTypeFunctionMaximumZoom = @"MGLStyleValueTypeFunctionMaximumZoom";
+NSString *const MGLStyleValueTypeFunctionLinear = @"MGLStyleValueTypeFunctionLinear";
+NSString *const MGLStyleValueTypeFunctionExponential = @"MGLStyleValueTypeFunctionExponential";
+NSString *const MGLStyleValueTypeFunctionStops = @"MGLStyleValueTypeFunctionStops";
diff --git a/platform/ios/NSArray+MGLAdditions.m b/platform/ios/NSArray+MGLAdditions.m
new file mode 100644
index 0000000000..2bac42ce0b
--- /dev/null
+++ b/platform/ios/NSArray+MGLAdditions.m
@@ -0,0 +1,10 @@
+#import "NSArray+MGLAdditions.h"
+
+@implementation NSArray (MGLAdditions)
+
+- (NSMutableArray *)deepMutableCopy
+{
+ return (NSMutableArray *)CFBridgingRelease(CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFArrayRef)self, kCFPropertyListMutableContainersAndLeaves));
+}
+
+@end
diff --git a/platform/ios/NSDictionary+MGLAdditions.m b/platform/ios/NSDictionary+MGLAdditions.m
new file mode 100644
index 0000000000..3228d56533
--- /dev/null
+++ b/platform/ios/NSDictionary+MGLAdditions.m
@@ -0,0 +1,10 @@
+#import "NSDictionary+MGLAdditions.h"
+
+@implementation NSDictionary (MGLAdditions)
+
+- (NSMutableDictionary *)deepMutableCopy
+{
+ return (NSMutableDictionary *)CFBridgingRelease(CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFDictionaryRef)self, kCFPropertyListMutableContainersAndLeaves));
+}
+
+@end
diff --git a/platform/ios/UIColor+MGLAdditions.m b/platform/ios/UIColor+MGLAdditions.m
new file mode 100644
index 0000000000..ae40735d15
--- /dev/null
+++ b/platform/ios/UIColor+MGLAdditions.m
@@ -0,0 +1,167 @@
+#import "UIColor+MGLAdditions.h"
+
+/* Portions based on Erica Sadun's uicolor-utilities
+ https://github.com/erica/uicolor-utilities */
+
+@interface UIColor (MGLAdditionsPrivate)
+
++ (UIColor *)colorWithRGBHex:(UInt32)hex;
+- (CGColorSpaceModel)colorSpaceModel;
+- (BOOL)canProvideRGBComponents;
+- (BOOL)red:(CGFloat *)red green:(CGFloat *)green blue:(CGFloat *)blue alpha:(CGFloat *)alpha;
+- (UInt32)rgbHex;
+
+@end
+
+@implementation UIColor (MGLAdditions)
+
++ (UIColor *)colorWithRGBAString:(NSString *)rgbaString
+{
+ UIColor *color;
+
+ NSString *numberString = [rgbaString stringByReplacingOccurrencesOfString:@"rgba("
+ withString:@""];
+ numberString = [numberString stringByReplacingOccurrencesOfString:@"rgb("
+ withString:@""];
+ numberString = [numberString stringByReplacingOccurrencesOfString:@")"
+ withString:@""];
+
+ NSArray *numbers = [numberString componentsSeparatedByString:@","];
+
+ if ([rgbaString hasPrefix:@"rgb("] && [numbers count] == 3)
+ {
+ color = [UIColor colorWithRed:[numbers[0] floatValue] / 255
+ green:[numbers[1] floatValue] / 255
+ blue:[numbers[2] floatValue] / 255
+ alpha:1.0];
+ }
+ else if ([rgbaString hasPrefix:@"rgba("] && [numbers count] == 4)
+ {
+ color = [UIColor colorWithRed:[numbers[0] floatValue] / 255
+ green:[numbers[1] floatValue] / 255
+ blue:[numbers[2] floatValue] / 255
+ alpha:[numbers[3] floatValue]];
+ }
+
+ return color;
+}
+
+- (NSString *)rgbaStringFromColor
+{
+ CGFloat r,g,b,a;
+
+ [self getRed:&r green:&g blue:&b alpha:&a];
+
+ r *= 255;
+ g *= 255;
+ b *= 255;
+ a *= 255;
+
+ return [NSString stringWithFormat:@"rgba(%lu,%lu,%lu,%lu)", (unsigned long)r, (unsigned long)g, (unsigned long)b, (unsigned long)a];
+}
+
++ (UIColor *)colorWithRGBHex:(UInt32)hex
+{
+ int r = (hex >> 16) & 0xFF;
+ int g = (hex >> 8) & 0xFF;
+ int b = (hex) & 0xFF;
+
+ return [UIColor colorWithRed:r / 255.0f
+ green:g / 255.0f
+ blue:b / 255.0f
+ alpha:1.0f];
+}
+
+- (CGColorSpaceModel)colorSpaceModel
+{
+ return CGColorSpaceGetModel(CGColorGetColorSpace(self.CGColor));
+}
+
+- (BOOL)canProvideRGBComponents
+{
+ switch (self.colorSpaceModel)
+ {
+ case kCGColorSpaceModelRGB:
+ case kCGColorSpaceModelMonochrome:
+ {
+ return YES;
+ }
+ default:
+ {
+ return NO;
+ }
+ }
+}
+
+- (BOOL)red:(CGFloat *)red green:(CGFloat *)green blue:(CGFloat *)blue alpha:(CGFloat *)alpha
+{
+ const CGFloat *components = CGColorGetComponents(self.CGColor);
+
+ CGFloat r,g,b,a;
+
+ switch (self.colorSpaceModel)
+ {
+ case kCGColorSpaceModelMonochrome:
+ {
+ r = g = b = components[0];
+ a = components[1];
+
+ break;
+ }
+ case kCGColorSpaceModelRGB:
+ {
+ r = components[0];
+ g = components[1];
+ b = components[2];
+ a = components[3];
+
+ break;
+ }
+ default:
+ {
+ return NO;
+ }
+ }
+
+ if (red) *red = r;
+ if (green) *green = g;
+ if (blue) *blue = b;
+ if (alpha) *alpha = a;
+
+ return YES;
+}
+
+- (UInt32)rgbHex
+{
+ NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use rgbHex");
+
+ CGFloat r, g, b, a;
+
+ if ( ! [self red:&r green:&g blue:&b alpha:&a])
+ return 0;
+
+ r = fminf(fmaxf(r, 0.0f), 1.0f);
+ g = fminf(fmaxf(g, 0.0f), 1.0f);
+ b = fminf(fmaxf(b, 0.0f), 1.0f);
+
+ return (((int)roundf(r * 255)) << 16) | (((int)roundf(g * 255)) << 8) | (((int)roundf(b * 255)));
+}
+
+- (NSString *)hexStringFromColor
+{
+ return [NSString stringWithFormat:@"%0.6X", (unsigned int)(self.rgbHex)];
+}
+
++ (UIColor *)colorWithHexString:(NSString *)hexString
+{
+ NSScanner *scanner = [NSScanner scannerWithString:[hexString stringByReplacingOccurrencesOfString:@"#" withString:@""]];
+
+ unsigned hexNum;
+
+ if ( ! [scanner scanHexInt:&hexNum])
+ return nil;
+
+ return [UIColor colorWithRGBHex:hexNum];
+}
+
+@end
diff --git a/platform/ios/resources/Compass.png b/platform/ios/resources/Compass.png
new file mode 100644
index 0000000000..fd3afe6f68
--- /dev/null
+++ b/platform/ios/resources/Compass.png
Binary files differ
diff --git a/platform/ios/resources/Compass@2x.png b/platform/ios/resources/Compass@2x.png
new file mode 100644
index 0000000000..58e7e08d24
--- /dev/null
+++ b/platform/ios/resources/Compass@2x.png
Binary files differ
diff --git a/platform/ios/resources/mapbox.png b/platform/ios/resources/mapbox.png
new file mode 100644
index 0000000000..01b5596b67
--- /dev/null
+++ b/platform/ios/resources/mapbox.png
Binary files differ
diff --git a/platform/ios/resources/mapbox@2x.png b/platform/ios/resources/mapbox@2x.png
new file mode 100644
index 0000000000..ff4ef2558b
--- /dev/null
+++ b/platform/ios/resources/mapbox@2x.png
Binary files differ
diff --git a/scripts/build-shaders.py b/scripts/build-shaders.py
index c1d7f7f28d..c1d7f7f28d 100644..100755
--- a/scripts/build-shaders.py
+++ b/scripts/build-shaders.py
diff --git a/scripts/build-version.py b/scripts/build-version.py
index 3fe789185c..3fe789185c 100644..100755
--- a/scripts/build-version.py
+++ b/scripts/build-version.py
diff --git a/scripts/ios_travis/add-key.sh b/scripts/ios_travis/add-key.sh
new file mode 100755
index 0000000000..b581445303
--- /dev/null
+++ b/scripts/ios_travis/add-key.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+# This is all taken from http://www.objc.io/issue-6/travis-ci.html
+
+# Create a custom keychain
+security create-keychain -p travis ios-build.keychain
+
+# Make the custom keychain default, so xcodebuild will use it for signing
+security default-keychain -s ios-build.keychain
+
+# Unlock the keychain
+security unlock-keychain -p travis ios-build.keychain
+
+# Set keychain timeout to 1 hour for long builds
+# see http://www.egeek.me/2013/02/23/jenkins-and-xcode-user-interaction-is-not-allowed/
+security set-keychain-settings -t 3600 -l ~/Library/Keychains/ios-build.keychain
+
+# Add certificates to keychain and allow codesign to access them
+security import ./scripts/ios_travis/apple.crt -k ~/Library/Keychains/ios-build.keychain -T /usr/bin/codesign
+security import ./scripts/ios_travis/ios-dist.cer -k ~/Library/Keychains/ios-build.keychain -T /usr/bin/codesign
+security import ./scripts/ios_travis/ios-dist.p12 -k ~/Library/Keychains/ios-build.keychain -P $IOS_KEY_PASSWORD -T /usr/bin/codesign
+
+# Put the provisioning profile in place
+mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
+cp "./scripts/ios_travis/$IOS_PROFILE_NAME.mobileprovision" ~/Library/MobileDevice/Provisioning\ Profiles/
diff --git a/scripts/ios_travis/apple.crt b/scripts/ios_travis/apple.crt
new file mode 100644
index 0000000000..0de099b869
--- /dev/null
+++ b/scripts/ios_travis/apple.crt
Binary files differ
diff --git a/scripts/ios_travis/ios-dist.cer.enc b/scripts/ios_travis/ios-dist.cer.enc
new file mode 100644
index 0000000000..dfdd0ca997
--- /dev/null
+++ b/scripts/ios_travis/ios-dist.cer.enc
@@ -0,0 +1,30 @@
+U2FsdGVkX1/Dn0fx6y0q6CHTK5UmCnel5Nw0kQlpeCqrqpWvXnYS//Ep0updcMIQ
+xj1hu+baBWjw7ccysFG/zcReSh/xgP6JrW7QKiA/kIQvqOvH0vhtzvDsnAQCB52T
+VWVy492mIwFRCBANVTPbdmBbVIeHAkeQcrVkFJJIKOv4dC+CFAw9ArHm8iYrg7FV
+FDK3cmrfR/pgvlQ1lQDa1C0vV1k/R5KuhqwDM3xMML1WbuBlR0RRHccs4xJRmRV7
+NuAuWg2Ob+p5gOW4L3+7PnBTBgxUZl2nbukd3oUlAPBOxgwwbgQSJPDvnsw2zmgp
+uv2X0Q0/fT9R8VxuoSXYkLkgIlOnVVRpyG4C4ou7HPS8VDiLP5iRWKx2PoTvaBLz
+9lNPTXhXu45n2k9L9SbkYk0024u6I8x9EkAmu0wU/rQpdudw4uLO7Pxz7BZHq3/D
+frwBMvtQXRogeQcsi3dKmsISrxdSEO1hax6toVnqujPSXKmFOMHXyhqJg/K9DDhs
+8GAUUO0cVfAaviofVFBZAXwkOd0SaYWz3WY4qHOZHnFiuGN0MCd2lXe5kVKFXRdE
+6ElXMm2+M0YLH+L55MA79hIDjqjv9JXTAAKpLy5wsOkMYEaaXvevdAEhkSW9QzIh
+HGUqBED5jqdrNlm7qOPSMWDcB/9FlnizKFsqrZOjquWFQZ4L34t++8FPT0ipxKCI
+XkYxBqVesZlHBOLPBYQ2bdJUuYaACkyMtB5eRebEieoWRXTB0aD4otvh6F6MYpiY
+4q66GO3faCnPxcZY4OxxwSSuyotBP8qKJcACqf3/Ji0nt42IHOIWJmiwROjBzMQ2
+DgYLtjsi1/b8eXLO5g+6S8o404j8g+2Z2rYXG+Hw6TaxY5bXrAH51fbUNDJP6qFR
+X3K3q8YvJlXcKIU6aZi5CDqaJUz6e22FvsOeWVHjdQYB23kk+LWYQY58hvkFUDXd
+/Nn2FxGPn80280sF4s8EX77YEXLGLqIo293Yg9F9JBwrq76ObY6DJbRlB4EG0Hx+
+vo0WJhEWHahu2uCfwqMJPDhMTGYcYRdHcNSepJlbj+gFTmH8bdKZGI/g13ZsDYEK
+HKP72rtaGQT4JYkS06TC6/Vvi+0+PxXr9SKbODCskN2yJpp0S2wXy8H6IwlpPe1R
+v82oCJQAXrcD8wd65TR2Q7fg4akHzI9VX1q8UGjzNt/Y/EsPEVv48PM/zngk/Hat
+Ptq6SoOFAj/JqbXOMuW0fidBI7uhq5575lgxaQ+VL4nKdDrdoHZXWJgjG/ZQO1j4
+8o12Z+Fk8tNpMPofJaAf3ebuArAE/IUDK7KKB7UoSwT1XPOFI9gCVtG654i7CAtN
+LfkDruwjTcD2XyaVLcVTeDUr2wMMrugE+OS4B/d0nD7jU4hVHTVn5fJxRiAFx+ql
+cBG/TIf951aDZ1x7eIj4xhehletdeWTZ2wGT8RJcFPD3LIB1hddeDIFQrkfEP7Gp
+IY+VdYo3tZtYcj9x0dXey/n9y0kxZw2w8oAY5iJB7NydeSANL4btPWO8y4KxXta9
+P+HNq01JS78ufYxfIOnOxD8l7YtD6Z6Evr1elzGFJOzRl4nCbPUg+VXekR94Lmf8
+0MBLgyG6xSoRBHvIYK6SRHEc+NuWvyfZGSOKBQVTwvf4/y4+xGcbPZy/YnUX6u+/
+zRWaYPJBdRhTrwAqawz9ZLL65uFYM7jhpZPEYGhOVqFoarNsy0m0gF5HNy8r/s71
+N1DBKUyVzdhR+4JZEHkkAr8JnN108d1uDIkaiI1F+hLCK2m5wnMYlZBMj2pP4kSC
+1E7JiIr/EKDiIHE7rOzf3yTYwug0xusBDdNTappMzTRsTCEyJf330k3kS7y0+zoH
+lwtG/9HS6qcwPY2h5buxTb6v9xQqmyWNT28Q8qEb3h8b2XS/NUyB9SQvRpkn9oUR
diff --git a/scripts/ios_travis/ios-dist.p12.enc b/scripts/ios_travis/ios-dist.p12.enc
new file mode 100644
index 0000000000..ad6507ef9f
--- /dev/null
+++ b/scripts/ios_travis/ios-dist.p12.enc
@@ -0,0 +1,32 @@
+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
diff --git a/scripts/ios_travis/ios-in-house.mobileprovision.enc b/scripts/ios_travis/ios-in-house.mobileprovision.enc
new file mode 100644
index 0000000000..51036a7415
--- /dev/null
+++ b/scripts/ios_travis/ios-in-house.mobileprovision.enc
@@ -0,0 +1,153 @@
+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
+mCKBZzE+OCJOYS4bO4/Lbg==
diff --git a/scripts/ios_travis/remove-key.sh b/scripts/ios_travis/remove-key.sh
new file mode 100755
index 0000000000..56b769ac21
--- /dev/null
+++ b/scripts/ios_travis/remove-key.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+security delete-keychain ios-build.keychain
+rm -f "~/Library/MobileDevice/Provisioning Profiles/$IOS_PROFILE_NAME.mobileprovision"
diff --git a/scripts/package_ios.sh b/scripts/package_ios.sh
new file mode 100755
index 0000000000..89aac5ee79
--- /dev/null
+++ b/scripts/package_ios.sh
@@ -0,0 +1,75 @@
+#!/usr/bin/env bash
+
+set -e
+set -o pipefail
+set -u
+
+NAME=MapboxGL
+OUTPUT=build/ios/pkg
+IOS_SDK_VERSION=8.1
+LIBUV_VERSION=0.10.28
+
+function step { >&2 echo -e "\033[1m\033[36m* $@\033[0m"; }
+function finish { >&2 echo -en "\033[0m"; }
+trap finish EXIT
+
+
+rm -rf ${OUTPUT}
+mkdir -p "${OUTPUT}"/static
+
+
+step "Recording library version..."
+VERSION="${OUTPUT}"/static/version.txt
+echo -n "https://github.com/mapbox/mapbox-gl-native/commit/" > ${VERSION}
+HASH=`git log | head -1 | awk '{ print $2 }' | cut -c 1-10` && true
+echo -n "mapbox-gl-native "
+echo ${HASH}
+echo ${HASH} >> ${VERSION}
+
+
+step "Creating build files..."
+export MASON_PLATFORM=ios
+export BUILDTYPE=${BUILDTYPE:-Release}
+export HOST=ios
+make Xcode/mbgl
+
+step "Building iOS targets..."
+xcodebuild -sdk iphoneos${IOS_SDK_VERSION} \
+ ARCHS="arm64 armv7 armv7s" \
+ -project ./build/ios/mbgl.xcodeproj \
+ -configuration ${BUILDTYPE} \
+ -target everything \
+ -jobs `sysctl -n hw.ncpu` | xcpretty -c
+
+
+step "Building iOS Simulator targets..."
+xcodebuild -sdk iphonesimulator${IOS_SDK_VERSION} \
+ ARCHS="x86_64 i386" \
+ -project ./build/ios/mbgl.xcodeproj \
+ -configuration ${BUILDTYPE} \
+ -target everything \
+ -jobs `sysctl -n hw.ncpu` | xcpretty -c
+
+
+step "Building static library..."
+LIBS=(core.a platform-ios.a asset-fs.a cache-sqlite.a http-nsurl.a)
+libtool -static -no_warning_for_no_symbols \
+ -o ${OUTPUT}/static/lib${NAME}.a \
+ ${LIBS[@]/#/build/${BUILDTYPE}-iphoneos/libmbgl-} \
+ ${LIBS[@]/#/build/${BUILDTYPE}-iphonesimulator/libmbgl-} \
+ `find mason_packages/ios-${IOS_SDK_VERSION} -type f -name libuv.a`
+echo "Created ${OUTPUT}/static/lib${NAME}.a"
+
+
+step "Copying Headers..."
+mkdir -p "${OUTPUT}/static/Headers"
+cp -pv include/mbgl/ios/* "${OUTPUT}/static/Headers"
+
+
+# Manually create resource bundle. We don't use a GYP target here because of
+# complications between faked GYP bundles-as-executables, device build
+# dependencies, and code signing.
+step "Copying Resources..."
+mkdir -p "${OUTPUT}/static/${NAME}.bundle"
+cp -pv platform/ios/resources/* "${OUTPUT}/static/${NAME}.bundle"
+cp -prv styles/styles "${OUTPUT}/static/${NAME}.bundle/styles"
diff --git a/scripts/travis_before_install.sh b/scripts/travis_before_install.sh
index 060a142093..ccda31c994 100755
--- a/scripts/travis_before_install.sh
+++ b/scripts/travis_before_install.sh
@@ -47,4 +47,7 @@ elif [[ ${TRAVIS_OS_NAME} == "osx" ]]; then
mapbox_time "install_awscli" \
sudo pip install awscli
+
+ mapbox_time "install_xcpretty" \
+ gem install xcpretty --no-rdoc --no-ri --no-document --quiet
fi
diff --git a/scripts/travis_script.sh b/scripts/travis_script.sh
index 71635b5c7f..45bda62c37 100755
--- a/scripts/travis_script.sh
+++ b/scripts/travis_script.sh
@@ -44,9 +44,8 @@ elif [[ ${TRAVIS_OS_NAME} == "osx" ]]; then
#
# build iOS
#
- mapbox_time "checkout_cocoa_bindings" \
- git submodule update --init ios/mapbox-gl-cocoa
-
- mapbox_time "build_ios_project" \
+ mapbox_time "build_ios_project_device_release" \
make ios -j$JOBS
+ mapbox_time "build_ios_project_simulator_debug" \
+ make isim -j$JOBS
fi
diff --git a/src/mbgl/map/tile_parser.cpp b/src/mbgl/map/tile_parser.cpp
index c3e7a8244b..6118d90a10 100644
--- a/src/mbgl/map/tile_parser.cpp
+++ b/src/mbgl/map/tile_parser.cpp
@@ -161,8 +161,7 @@ void TileParser::applyLayoutProperties<SymbolProperties>(StyleBucket &bucket_des
applyLayoutProperty(PropertyKey::IconPadding, bucket_desc.layout, symbol.icon.padding, z);
applyLayoutProperty(PropertyKey::IconRotate, bucket_desc.layout, symbol.icon.rotate, z);
applyLayoutProperty(PropertyKey::IconKeepUpright, bucket_desc.layout, symbol.icon.keep_upright, z);
- applyLayoutProperty(PropertyKey::IconOffsetX, bucket_desc.layout, symbol.icon.offset[0], z);
- applyLayoutProperty(PropertyKey::IconOffsetY, bucket_desc.layout, symbol.icon.offset[1], z);
+ applyLayoutProperty(PropertyKey::IconOffset, bucket_desc.layout, symbol.icon.offset, z);
applyLayoutProperty(PropertyKey::TextRotationAlignment, bucket_desc.layout, symbol.text.rotation_alignment, z);
applyLayoutProperty(PropertyKey::TextField, bucket_desc.layout, symbol.text.field, z);
@@ -180,8 +179,7 @@ void TileParser::applyLayoutProperties<SymbolProperties>(StyleBucket &bucket_des
applyLayoutProperty(PropertyKey::TextAnchor, bucket_desc.layout, symbol.text.anchor, z);
applyLayoutProperty(PropertyKey::TextKeepUpright, bucket_desc.layout, symbol.text.keep_upright, z);
applyLayoutProperty(PropertyKey::TextTransform, bucket_desc.layout, symbol.text.transform, z);
- applyLayoutProperty(PropertyKey::TextOffsetX, bucket_desc.layout, symbol.text.offset[0], z);
- applyLayoutProperty(PropertyKey::TextOffsetY, bucket_desc.layout, symbol.text.offset[1], z);
+ applyLayoutProperty(PropertyKey::TextOffset, bucket_desc.layout, symbol.text.offset, z);
applyLayoutProperty(PropertyKey::TextAllowOverlap, bucket_desc.layout, symbol.text.allow_overlap, z);
}
diff --git a/src/mbgl/renderer/painter.cpp b/src/mbgl/renderer/painter.cpp
index 3f30e6d0f5..116ae3ad2f 100644
--- a/src/mbgl/renderer/painter.cpp
+++ b/src/mbgl/renderer/painter.cpp
@@ -231,7 +231,7 @@ void Painter::render(const Style& style, const std::set<util::ptr<StyleSource>>&
drawClippingMasks(sources);
- recordZoom(time, state.getNormalizedZoom());
+ frameHistory.record(time, state.getNormalizedZoom());
// Actually render the layers
if (debug::renderTree) { std::cout << "{" << std::endl; indent++; }
@@ -375,39 +375,58 @@ void Painter::renderTileLayer(const Tile& tile, util::ptr<StyleLayer> layer_desc
void Painter::renderBackground(util::ptr<StyleLayer> layer_desc) {
const BackgroundProperties& properties = layer_desc->getProperties<BackgroundProperties>();
- if (properties.image.size()) {
+ if (properties.image.to.size()) {
if ((properties.opacity >= 1.0f) != (pass == RenderPass::Opaque))
return;
- SpriteAtlasPosition imagePos = spriteAtlas.getPosition(properties.image, true);
+ SpriteAtlasPosition imagePosA = spriteAtlas.getPosition(properties.image.from, true);
+ SpriteAtlasPosition imagePosB = spriteAtlas.getPosition(properties.image.to, true);
float zoomFraction = state.getZoomFraction();
useProgram(patternShader->program);
patternShader->u_matrix = identityMatrix;
- patternShader->u_pattern_tl = imagePos.tl;
- patternShader->u_pattern_br = imagePos.br;
- patternShader->u_mix = zoomFraction;
+ patternShader->u_pattern_tl_a = imagePosA.tl;
+ patternShader->u_pattern_br_a = imagePosA.br;
+ patternShader->u_pattern_tl_b = imagePosB.tl;
+ patternShader->u_pattern_br_b = imagePosB.br;
+ patternShader->u_mix = properties.image.t;
patternShader->u_opacity = properties.opacity;
- std::array<float, 2> size = imagePos.size;
double lon, lat;
state.getLonLat(lon, lat);
std::array<float, 2> center = state.locationCoordinate(lon, lat);
float scale = 1 / std::pow(2, zoomFraction);
- mat3 matrix;
- matrix::identity(matrix);
- matrix::scale(matrix, matrix,
- 1.0f / size[0],
- 1.0f / size[1]);
- matrix::translate(matrix, matrix,
- std::fmod(center[0] * 512, size[0]),
- std::fmod(center[1] * 512, size[1]));
- matrix::rotate(matrix, matrix, -state.getAngle());
- matrix::scale(matrix, matrix,
+ std::array<float, 2> sizeA = imagePosA.size;
+ mat3 matrixA;
+ matrix::identity(matrixA);
+ matrix::scale(matrixA, matrixA,
+ 1.0f / (sizeA[0] * properties.image.fromScale),
+ 1.0f / (sizeA[1] * properties.image.fromScale));
+ matrix::translate(matrixA, matrixA,
+ std::fmod(center[0] * 512, sizeA[0] * properties.image.fromScale),
+ std::fmod(center[1] * 512, sizeA[1] * properties.image.fromScale));
+ matrix::rotate(matrixA, matrixA, -state.getAngle());
+ matrix::scale(matrixA, matrixA,
scale * state.getWidth() / 2,
-scale * state.getHeight() / 2);
- patternShader->u_patternmatrix = matrix;
+
+ std::array<float, 2> sizeB = imagePosB.size;
+ mat3 matrixB;
+ matrix::identity(matrixB);
+ matrix::scale(matrixB, matrixB,
+ 1.0f / (sizeB[0] * properties.image.toScale),
+ 1.0f / (sizeB[1] * properties.image.toScale));
+ matrix::translate(matrixB, matrixB,
+ std::fmod(center[0] * 512, sizeB[0] * properties.image.toScale),
+ std::fmod(center[1] * 512, sizeB[1] * properties.image.toScale));
+ matrix::rotate(matrixB, matrixB, -state.getAngle());
+ matrix::scale(matrixB, matrixB,
+ scale * state.getWidth() / 2,
+ -scale * state.getHeight() / 2);
+
+ patternShader->u_patternmatrix_a = matrixA;
+ patternShader->u_patternmatrix_b = matrixB;
backgroundBuffer.bind();
patternShader->bind(0);
@@ -459,26 +478,3 @@ mat4 Painter::translatedMatrix(const mat4& matrix, const std::array<float, 2> &t
return vtxMatrix;
}
}
-
-void Painter::recordZoom(const std::chrono::steady_clock::time_point time, const float zoom) {
- frameHistory.record(time, zoom);
-
- if (lastZoom < 0) {
- // first frame ever
- lastIntegerZoom = std::floor(zoom);
- lastZoom = zoom;
- }
-
- // check whether an integer zoom level was passed since the last frame
- // and if yes, record it with the time. Used for transitioning patterns.
- if (std::floor(lastZoom) < std::floor(zoom)) {
- lastIntegerZoom = std::floor(zoom);
- lastIntegerZoomTime = time;
-
- } else if (std::floor(lastZoom) > std::floor(zoom)) {
- lastIntegerZoom = std::floor(zoom) + 1;
- lastIntegerZoomTime = time;
- }
-
- lastZoom = zoom;
-}
diff --git a/src/mbgl/renderer/painter.hpp b/src/mbgl/renderer/painter.hpp
index bbce588642..f38a9f0559 100644
--- a/src/mbgl/renderer/painter.hpp
+++ b/src/mbgl/renderer/painter.hpp
@@ -140,7 +140,6 @@ private:
mat4 translatedMatrix(const mat4& matrix, const std::array<float, 2> &translation, const Tile::ID &id, TranslateAnchorType anchor);
void prepareTile(const Tile& tile);
- void recordZoom(const std::chrono::steady_clock::time_point time, const float zoom);
template <typename BucketProperties, typename StyleProperties>
void renderSDF(SymbolBucket &bucket,
@@ -193,10 +192,6 @@ private:
RenderPass pass = RenderPass::Opaque;
const float strata_epsilon = 1.0f / (1 << 16);
- int lastIntegerZoom;
- std::chrono::steady_clock::time_point lastIntegerZoomTime = std::chrono::steady_clock::time_point::min();
- float lastZoom = -1;
-
public:
FrameHistory frameHistory;
diff --git a/src/mbgl/renderer/painter_fill.cpp b/src/mbgl/renderer/painter_fill.cpp
index 45de66201c..9cfe26222b 100644
--- a/src/mbgl/renderer/painter_fill.cpp
+++ b/src/mbgl/renderer/painter_fill.cpp
@@ -33,13 +33,13 @@ void Painter::renderFill(FillBucket& bucket, util::ptr<StyleLayer> layer_desc, c
stroke_color[3] *= properties.opacity;
}
- const bool pattern = properties.image.size();
+ const bool pattern = properties.image.from.size();
bool outline = properties.antialias && !pattern && properties.stroke_color != properties.fill_color;
bool fringeline = properties.antialias && !pattern && properties.stroke_color == properties.fill_color;
// Because we're drawing top-to-bottom, and we update the stencil mask
- // below, we have to draw the outline first (!)
+ // befrom, we have to draw the outline first (!)
if (outline && pass == RenderPass::Translucent) {
useProgram(outlineShader->program);
outlineShader->u_matrix = vtxMatrix;
@@ -59,34 +59,32 @@ void Painter::renderFill(FillBucket& bucket, util::ptr<StyleLayer> layer_desc, c
if (pattern) {
// Image fill.
if (pass == RenderPass::Translucent) {
- const SpriteAtlasPosition pos = spriteAtlas.getPosition(properties.image, true);
+ const SpriteAtlasPosition posA = spriteAtlas.getPosition(properties.image.from, true);
+ const SpriteAtlasPosition posB = spriteAtlas.getPosition(properties.image.to, true);
float factor = 8.0 / std::pow(2, state.getIntegerZoom() - id.z);
- float mix;
- std::chrono::steady_clock::duration duration = std::chrono::milliseconds(300);
- const float fraction = std::fmod(float(state.getZoom()), 1.0f);
- float t = std::min(static_cast<float>((std::chrono::steady_clock::now() - lastIntegerZoomTime) / duration), 1.0f);
- if (state.getZoom() > lastIntegerZoom) {
- // zooming in
- mix = fraction + (1.0f - fraction) * t;
- factor *= 2.0;
- } else {
- // zooming out
- mix = fraction - fraction * t;
- }
-
- mat3 patternMatrix;
- matrix::identity(patternMatrix);
- matrix::scale(patternMatrix, patternMatrix, 1.0f / (pos.size[0] * factor), 1.0f / (pos.size[1] * factor));
+ mat3 patternMatrixA;
+ matrix::identity(patternMatrixA);
+ matrix::scale(patternMatrixA, patternMatrixA,
+ 1.0f / (posA.size[0] * factor * properties.image.fromScale),
+ 1.0f / (posA.size[1] * factor * properties.image.fromScale));
+ mat3 patternMatrixB;
+ matrix::identity(patternMatrixB);
+ matrix::scale(patternMatrixB, patternMatrixB,
+ 1.0f / (posB.size[0] * factor * properties.image.toScale),
+ 1.0f / (posB.size[1] * factor * properties.image.toScale));
useProgram(patternShader->program);
patternShader->u_matrix = vtxMatrix;
- patternShader->u_pattern_tl = pos.tl;
- patternShader->u_pattern_br = pos.br;
+ patternShader->u_pattern_tl_a = posA.tl;
+ patternShader->u_pattern_br_a = posA.br;
+ patternShader->u_pattern_tl_b = posB.tl;
+ patternShader->u_pattern_br_b = posB.br;
patternShader->u_opacity = properties.opacity;
patternShader->u_image = 0;
- patternShader->u_mix = mix;
- patternShader->u_patternmatrix = patternMatrix;
+ patternShader->u_mix = properties.image.t;
+ patternShader->u_patternmatrix_a = patternMatrixA;
+ patternShader->u_patternmatrix_b = patternMatrixB;
MBGL_CHECK_ERROR(glActiveTexture(GL_TEXTURE0));
spriteAtlas.bind(true);
diff --git a/src/mbgl/renderer/painter_line.cpp b/src/mbgl/renderer/painter_line.cpp
index 1f69a05511..a66da1fcd6 100644
--- a/src/mbgl/renderer/painter_line.cpp
+++ b/src/mbgl/renderer/painter_line.cpp
@@ -70,11 +70,7 @@ void Painter::renderLine(LineBucket& bucket, util::ptr<StyleLayer> layer_desc, c
bucket.drawPoints(*linejoinShader);
}
- std::chrono::steady_clock::duration duration = std::chrono::milliseconds(300);
- const float fraction = std::fmod(float(state.getZoom()), 1.0f);
- float t = std::min(static_cast<float>(std::chrono::duration<float>(std::chrono::steady_clock::now() - lastIntegerZoomTime) / duration), 1.0f);
-
- if (properties.dash_array.size()) {
+ if (properties.dash_array.from.size()) {
useProgram(linesdfShader->program);
@@ -85,46 +81,31 @@ void Painter::renderLine(LineBucket& bucket, util::ptr<StyleLayer> layer_desc, c
linesdfShader->u_blur = blur;
linesdfShader->u_color = color;
- LinePatternPos pos = lineAtlas.getDashPosition(properties.dash_array, bucket.properties.cap == CapType::Round);
+ LinePatternPos posA = lineAtlas.getDashPosition(properties.dash_array.from, bucket.properties.cap == CapType::Round);
+ LinePatternPos posB = lineAtlas.getDashPosition(properties.dash_array.to, bucket.properties.cap == CapType::Round);
lineAtlas.bind();
float patternratio = std::pow(2.0, std::floor(std::log2(state.getScale())) - id.z) / 8.0;
- float scaleX = patternratio / pos.width / properties.dash_line_width;
- float scaleY = -pos.height / 2.0;
-
- float mix;
- if (state.getZoom() > lastIntegerZoom) {
- // zooming in
- mix = fraction + (1.0f - fraction) * t;
- scaleX /= 2.0;
- } else {
- // zooming out
- mix = fraction - fraction * t;
- }
-
- linesdfShader->u_patternscale_a = {{ scaleX, scaleY }};
- linesdfShader->u_tex_y_a = pos.y;
- linesdfShader->u_patternscale_b = {{ scaleX * 2.0f, scaleY }};
- linesdfShader->u_tex_y_b = pos.y;
+ float scaleXA = patternratio / posA.width / properties.dash_line_width / properties.dash_array.fromScale;
+ float scaleYA = -posA.height / 2.0;
+ float scaleXB = patternratio / posB.width / properties.dash_line_width / properties.dash_array.toScale;
+ float scaleYB = -posB.height / 2.0;
+
+ linesdfShader->u_patternscale_a = {{ scaleXA, scaleYA }};
+ linesdfShader->u_tex_y_a = posA.y;
+ linesdfShader->u_patternscale_b = {{ scaleXB, scaleYB }};
+ linesdfShader->u_tex_y_b = posB.y;
linesdfShader->u_image = 0;
- linesdfShader->u_sdfgamma = lineAtlas.width / (properties.dash_line_width * pos.width * 256.0 * state.getPixelRatio()) / 2;
- linesdfShader->u_mix = mix;
+ linesdfShader->u_sdfgamma = lineAtlas.width / (properties.dash_line_width * std::min(posA.width, posB.width) * 256.0 * state.getPixelRatio()) / 2;
+ linesdfShader->u_mix = properties.dash_array.t;
bucket.drawLineSDF(*linesdfShader);
- } else if (properties.image.size()) {
- SpriteAtlasPosition imagePos = spriteAtlas.getPosition(properties.image, true);
+ } else if (properties.image.from.size()) {
+ SpriteAtlasPosition imagePosA = spriteAtlas.getPosition(properties.image.from, true);
+ SpriteAtlasPosition imagePosB = spriteAtlas.getPosition(properties.image.to, true);
float factor = 8.0 / std::pow(2, state.getIntegerZoom() - id.z);
- float fade;
- if (state.getZoom() > lastIntegerZoom) {
- // zooming in
- fade = fraction + (1.0f - fraction) * t;
- factor *= 2.0;
- } else {
- // zooming out
- fade = fraction - fraction * t;
- }
useProgram(linepatternShader->program);
@@ -134,10 +115,13 @@ void Painter::renderLine(LineBucket& bucket, util::ptr<StyleLayer> layer_desc, c
linepatternShader->u_ratio = ratio;
linepatternShader->u_blur = blur;
- linepatternShader->u_pattern_size = {{imagePos.size[0] * factor, imagePos.size[1]}};
- linepatternShader->u_pattern_tl = imagePos.tl;
- linepatternShader->u_pattern_br = imagePos.br;
- linepatternShader->u_fade = fade;
+ linepatternShader->u_pattern_size_a = {{imagePosA.size[0] * factor * properties.image.fromScale, imagePosA.size[1]}};
+ linepatternShader->u_pattern_tl_a = imagePosA.tl;
+ linepatternShader->u_pattern_br_a = imagePosA.br;
+ linepatternShader->u_pattern_size_b = {{imagePosB.size[0] * factor * properties.image.toScale, imagePosB.size[1]}};
+ linepatternShader->u_pattern_tl_b = imagePosB.tl;
+ linepatternShader->u_pattern_br_b = imagePosB.br;
+ linepatternShader->u_fade = properties.image.t;
linepatternShader->u_opacity = properties.opacity;
MBGL_CHECK_ERROR(glActiveTexture(GL_TEXTURE0));
diff --git a/src/mbgl/shader/linepattern.fragment.glsl b/src/mbgl/shader/linepattern.fragment.glsl
index 45be7c8c2f..08feff4396 100644
--- a/src/mbgl/shader/linepattern.fragment.glsl
+++ b/src/mbgl/shader/linepattern.fragment.glsl
@@ -2,9 +2,12 @@ uniform vec2 u_linewidth;
uniform float u_point;
uniform float u_blur;
-uniform vec2 u_pattern_size;
-uniform vec2 u_pattern_tl;
-uniform vec2 u_pattern_br;
+uniform vec2 u_pattern_size_a;
+uniform vec2 u_pattern_tl_a;
+uniform vec2 u_pattern_br_a;
+uniform vec2 u_pattern_size_b;
+uniform vec2 u_pattern_tl_b;
+uniform vec2 u_pattern_br_b;
uniform float u_fade;
uniform float u_opacity;
@@ -24,13 +27,14 @@ void main() {
// (v_linewidth.s)
float alpha = clamp(min(dist - (u_linewidth.t - u_blur), u_linewidth.s - dist) / u_blur, 0.0, 1.0);
- float x = mod(v_linesofar / u_pattern_size.x, 1.0);
- float y = 0.5 + (v_normal.y * u_linewidth.s / u_pattern_size.y);
- vec2 pos = mix(u_pattern_tl, u_pattern_br, vec2(x, y));
- float x2 = mod(x * 2.0, 1.0);
- vec2 pos2 = mix(u_pattern_tl, u_pattern_br, vec2(x2, y));
+ float x_a = mod(v_linesofar / u_pattern_size_a.x, 1.0);
+ float y_a = 0.5 + (v_normal.y * u_linewidth.s / u_pattern_size_a.y);
+ vec2 pos_a = mix(u_pattern_tl_a, u_pattern_br_a, vec2(x_a, y_a));
+ float x_b = mod(v_linesofar / u_pattern_size_b.x, 1.0);
+ float y_b = 0.5 + (v_normal.y * u_linewidth.s / u_pattern_size_b.y);
+ vec2 pos_b = mix(u_pattern_tl_b, u_pattern_br_b, vec2(x_b, y_b));
- vec4 color = texture2D(u_image, pos) * (1.0 - u_fade) + u_fade * texture2D(u_image, pos2);
+ vec4 color = mix(texture2D(u_image, pos_a), texture2D(u_image, pos_b), u_fade);
alpha *= u_opacity;
diff --git a/src/mbgl/shader/linepattern_shader.hpp b/src/mbgl/shader/linepattern_shader.hpp
index 388cff17d2..16d9ff8be7 100644
--- a/src/mbgl/shader/linepattern_shader.hpp
+++ b/src/mbgl/shader/linepattern_shader.hpp
@@ -15,9 +15,12 @@ public:
UniformMatrix<4> u_matrix = {"u_matrix", *this};
UniformMatrix<4> u_exmatrix = {"u_exmatrix", *this};
Uniform<std::array<float, 2>> u_linewidth = {"u_linewidth", *this};
- Uniform<std::array<float, 2>> u_pattern_size = {"u_pattern_size", *this};
- Uniform<std::array<float, 2>> u_pattern_tl = {"u_pattern_tl", *this};
- Uniform<std::array<float, 2>> u_pattern_br = {"u_pattern_br", *this};
+ Uniform<std::array<float, 2>> u_pattern_size_a = {"u_pattern_size_a", *this};
+ Uniform<std::array<float, 2>> u_pattern_tl_a = {"u_pattern_tl_a", *this};
+ Uniform<std::array<float, 2>> u_pattern_br_a = {"u_pattern_br_a", *this};
+ Uniform<std::array<float, 2>> u_pattern_size_b = {"u_pattern_size_b", *this};
+ Uniform<std::array<float, 2>> u_pattern_tl_b = {"u_pattern_tl_b", *this};
+ Uniform<std::array<float, 2>> u_pattern_br_b = {"u_pattern_br_b", *this};
Uniform<float> u_ratio = {"u_ratio", *this};
Uniform<float> u_point = {"u_point", *this};
Uniform<float> u_blur = {"u_blur", *this};
diff --git a/src/mbgl/shader/pattern.fragment.glsl b/src/mbgl/shader/pattern.fragment.glsl
index ba6aed3023..a28a52ce36 100644
--- a/src/mbgl/shader/pattern.fragment.glsl
+++ b/src/mbgl/shader/pattern.fragment.glsl
@@ -1,20 +1,23 @@
uniform float u_opacity;
-uniform vec2 u_pattern_tl;
-uniform vec2 u_pattern_br;
+uniform vec2 u_pattern_tl_a;
+uniform vec2 u_pattern_br_a;
+uniform vec2 u_pattern_tl_b;
+uniform vec2 u_pattern_br_b;
uniform float u_mix;
uniform sampler2D u_image;
-varying vec2 v_pos;
+varying vec2 v_pos_a;
+varying vec2 v_pos_b;
void main() {
- vec2 imagecoord = mod(v_pos, 1.0);
- vec2 pos = mix(u_pattern_tl, u_pattern_br, imagecoord);
+ vec2 imagecoord = mod(v_pos_a, 1.0);
+ vec2 pos = mix(u_pattern_tl_a, u_pattern_br_a, imagecoord);
vec4 color1 = texture2D(u_image, pos);
- vec2 imagecoord2 = mod(imagecoord * 2.0, 1.0);
- vec2 pos2 = mix(u_pattern_tl, u_pattern_br, imagecoord2);
+ vec2 imagecoord_b = mod(v_pos_b, 1.0);
+ vec2 pos2 = mix(u_pattern_tl_b, u_pattern_br_b, imagecoord_b);
vec4 color2 = texture2D(u_image, pos2);
gl_FragColor = mix(color1, color2, u_mix) * u_opacity;
diff --git a/src/mbgl/shader/pattern.vertex.glsl b/src/mbgl/shader/pattern.vertex.glsl
index f2de884ead..dff4469d2b 100644
--- a/src/mbgl/shader/pattern.vertex.glsl
+++ b/src/mbgl/shader/pattern.vertex.glsl
@@ -1,11 +1,14 @@
uniform mat4 u_matrix;
-uniform mat3 u_patternmatrix;
+uniform mat3 u_patternmatrix_a;
+uniform mat3 u_patternmatrix_b;
attribute vec2 a_pos;
-varying vec2 v_pos;
+varying vec2 v_pos_a;
+varying vec2 v_pos_b;
void main() {
gl_Position = u_matrix * vec4(a_pos, 0, 1);
- v_pos = (u_patternmatrix * vec3(a_pos, 1)).xy;
+ v_pos_a = (u_patternmatrix_a * vec3(a_pos, 1)).xy;
+ v_pos_b = (u_patternmatrix_b * vec3(a_pos, 1)).xy;
}
diff --git a/src/mbgl/shader/pattern_shader.hpp b/src/mbgl/shader/pattern_shader.hpp
index 9fabd8e18a..9fc8dcfa01 100644
--- a/src/mbgl/shader/pattern_shader.hpp
+++ b/src/mbgl/shader/pattern_shader.hpp
@@ -13,12 +13,15 @@ public:
void bind(char *offset);
UniformMatrix<4> u_matrix = {"u_matrix", *this};
- Uniform<std::array<float, 2>> u_pattern_tl = {"u_pattern_tl", *this};
- Uniform<std::array<float, 2>> u_pattern_br = {"u_pattern_br", *this};
+ Uniform<std::array<float, 2>> u_pattern_tl_a = {"u_pattern_tl_a", *this};
+ Uniform<std::array<float, 2>> u_pattern_br_a = {"u_pattern_br_a", *this};
+ Uniform<std::array<float, 2>> u_pattern_tl_b = {"u_pattern_tl_b", *this};
+ Uniform<std::array<float, 2>> u_pattern_br_b = {"u_pattern_br_b", *this};
Uniform<float> u_opacity = {"u_opacity", *this};
Uniform<float> u_mix = {"u_mix", *this};
Uniform<int32_t> u_image = {"u_image", *this};
- UniformMatrix<3> u_patternmatrix = {"u_patternmatrix", *this};
+ UniformMatrix<3> u_patternmatrix_a = {"u_patternmatrix_a", *this};
+ UniformMatrix<3> u_patternmatrix_b = {"u_patternmatrix_b", *this};
private:
int32_t a_pos = -1;
diff --git a/src/mbgl/style/function_properties.cpp b/src/mbgl/style/function_properties.cpp
index 81b1c85c72..1ac2863a7c 100644
--- a/src/mbgl/style/function_properties.cpp
+++ b/src/mbgl/style/function_properties.cpp
@@ -13,7 +13,18 @@ template <> inline bool defaultStopsValue() { return true; }
template <> inline float defaultStopsValue() { return 1.0f; }
template <> inline Color defaultStopsValue() { return {{ 0, 0, 0, 1 }}; }
template <> inline std::vector<float> defaultStopsValue() { return {{ 1, 0 }}; }
+template <> inline std::array<float, 2> defaultStopsValue() { return {{ 0, 0 }}; }
+template <> inline std:: string defaultStopsValue() { return {}; }
+template <> inline TranslateAnchorType defaultStopsValue() { return {}; };
+template <> inline RotateAnchorType defaultStopsValue() { return {}; };
+template <> inline CapType defaultStopsValue() { return {}; };
+template <> inline JoinType defaultStopsValue() { return {}; };
+template <> inline PlacementType defaultStopsValue() { return {}; };
+template <> inline TextAnchorType defaultStopsValue() { return {}; };
+template <> inline TextJustifyType defaultStopsValue() { return {}; };
+template <> inline TextTransformType defaultStopsValue() { return {}; };
+template <> inline RotationAlignmentType defaultStopsValue() { return {}; };
template <typename T>
T StopsFunction<T>::evaluate(float z) const {
@@ -66,5 +77,16 @@ template bool StopsFunction<bool>::evaluate(float z) const;
template float StopsFunction<float>::evaluate(float z) const;
template Color StopsFunction<Color>::evaluate(float z) const;
template std::vector<float> StopsFunction<std::vector<float>>::evaluate(float z) const;
+template std::array<float, 2> StopsFunction<std::array<float, 2>>::evaluate(float z) const;
+template std::string StopsFunction<std::string>::evaluate(float z) const;
+template TranslateAnchorType StopsFunction<TranslateAnchorType>::evaluate(float z) const;
+template RotateAnchorType StopsFunction<RotateAnchorType>::evaluate(float z) const;
+template CapType StopsFunction<CapType>::evaluate(float z) const;
+template JoinType StopsFunction<JoinType>::evaluate(float z) const;
+template PlacementType StopsFunction<PlacementType>::evaluate(float z) const;
+template TextAnchorType StopsFunction<TextAnchorType>::evaluate(float z) const;
+template TextJustifyType StopsFunction<TextJustifyType>::evaluate(float z) const;
+template TextTransformType StopsFunction<TextTransformType>::evaluate(float z) const;
+template RotationAlignmentType StopsFunction<RotationAlignmentType>::evaluate(float z) const;
}
diff --git a/src/mbgl/style/piecewisefunction_properties.cpp b/src/mbgl/style/piecewisefunction_properties.cpp
new file mode 100644
index 0000000000..72ed0b8f73
--- /dev/null
+++ b/src/mbgl/style/piecewisefunction_properties.cpp
@@ -0,0 +1,52 @@
+#include <mbgl/style/piecewisefunction_properties.hpp>
+#include <mbgl/style/types.hpp>
+
+#include <cmath>
+
+namespace mbgl {
+
+template <typename T>
+size_t getBiggestStopLessThan(std::vector<std::pair<float, T>> stops, float z) {
+ for (uint32_t i = 0; i < stops.size(); i++) {
+ if (stops[i].first > z) {
+ return i == 0 ? i : i - 1;
+ }
+ }
+ return stops.size() - 1;
+}
+
+template <typename T>
+T PiecewiseConstantFunction<T>::evaluate(float z, const ZoomHistory &zh) const {
+ T result;
+
+ float fraction = std::fmod(z, 1.0f);
+ float t = std::min((std::chrono::steady_clock::now() - zh.lastIntegerZoomTime) / duration, 1.0f);
+ float fromScale = 1.0f;
+ float toScale = 1.0f;
+ size_t from, to;
+
+ if (z > zh.lastIntegerZoom) {
+ result.t = fraction + (1.0f - fraction) * t;
+ from = getBiggestStopLessThan(values, z - 1.0f);
+ to = getBiggestStopLessThan(values, z);
+ fromScale *= 2.0f;
+
+ } else {
+ result.t = 1 - (1 - t) * fraction;
+ to = getBiggestStopLessThan(values, z);
+ from = getBiggestStopLessThan(values, z + 1.0f);
+ fromScale /= 2.0f;
+ }
+
+
+ result.from = values[from].second.to;
+ result.to = values[to].second.to;
+ result.fromScale = fromScale;
+ result.toScale = toScale;
+ return result;
+}
+
+template Faded<std::string> PiecewiseConstantFunction<Faded<std::string>>::evaluate(float z, const ZoomHistory &zoomHistory) const;
+template Faded<std::vector<float>> PiecewiseConstantFunction<Faded<std::vector<float>>>::evaluate(float z, const ZoomHistory &zoomHistory) const;
+
+}
diff --git a/src/mbgl/style/piecewisefunction_properties.hpp b/src/mbgl/style/piecewisefunction_properties.hpp
new file mode 100644
index 0000000000..0440655ba5
--- /dev/null
+++ b/src/mbgl/style/piecewisefunction_properties.hpp
@@ -0,0 +1,24 @@
+#ifndef MBGL_STYLE_FADEDFUNCTION_PROPERTIES
+#define MBGL_STYLE_FADEDFUNCTION_PROPERTIES
+
+#include <mbgl/style/zoom_history.hpp>
+
+#include <vector>
+
+namespace mbgl {
+
+template <typename T>
+struct PiecewiseConstantFunction {
+ inline PiecewiseConstantFunction(const std::vector<std::pair<float, T>> &values_, std::chrono::duration<float> duration_) : values(values_), duration(duration_) {}
+ inline PiecewiseConstantFunction(T &value, std::chrono::duration<float> duration_) : values({{ 0, value }}), duration(duration_) {}
+ inline PiecewiseConstantFunction() : values(), duration(std::chrono::milliseconds(300)) {}
+ T evaluate(float z, const ZoomHistory &zoomHistory) const;
+
+private:
+ const std::vector<std::pair<float, T>> values;
+ const std::chrono::duration<float> duration;
+};
+
+}
+
+#endif
diff --git a/src/mbgl/style/property_fallback.cpp b/src/mbgl/style/property_fallback.cpp
index 5fc3ce1f04..2827fd6149 100644
--- a/src/mbgl/style/property_fallback.cpp
+++ b/src/mbgl/style/property_fallback.cpp
@@ -9,14 +9,12 @@ const std::map<PropertyKey, PropertyValue> PropertyFallbackValue::properties = {
{ PropertyKey::FillOpacity, defaultStyleProperties<FillProperties>().opacity },
{ PropertyKey::FillColor, defaultStyleProperties<FillProperties>().fill_color },
// no FillOutlineColor on purpose.
- { PropertyKey::FillTranslateX, defaultStyleProperties<FillProperties>().translate[0] },
- { PropertyKey::FillTranslateY, defaultStyleProperties<FillProperties>().translate[1] },
+ { PropertyKey::FillTranslate, defaultStyleProperties<FillProperties>().translate },
{ PropertyKey::FillTranslateAnchor, defaultStyleProperties<FillProperties>().translateAnchor },
{ PropertyKey::LineOpacity, defaultStyleProperties<LineProperties>().opacity },
{ PropertyKey::LineColor, defaultStyleProperties<LineProperties>().color },
- { PropertyKey::LineTranslateX, defaultStyleProperties<LineProperties>().translate[0] },
- { PropertyKey::LineTranslateY, defaultStyleProperties<LineProperties>().translate[1] },
+ { PropertyKey::LineTranslate, defaultStyleProperties<LineProperties>().translate },
{ PropertyKey::LineTranslateAnchor, defaultStyleProperties<LineProperties>().translateAnchor },
{ PropertyKey::LineWidth, defaultStyleProperties<LineProperties>().width },
{ PropertyKey::LineGapWidth, defaultStyleProperties<LineProperties>().gap_width },
@@ -29,8 +27,7 @@ const std::map<PropertyKey, PropertyValue> PropertyFallbackValue::properties = {
{ PropertyKey::IconHaloColor, defaultStyleProperties<SymbolProperties>().icon.halo_color },
{ PropertyKey::IconHaloWidth, defaultStyleProperties<SymbolProperties>().icon.halo_width },
{ PropertyKey::IconHaloBlur, defaultStyleProperties<SymbolProperties>().icon.halo_blur },
- { PropertyKey::IconTranslateX, defaultStyleProperties<SymbolProperties>().icon.translate[0] },
- { PropertyKey::IconTranslateY, defaultStyleProperties<SymbolProperties>().icon.translate[1] },
+ { PropertyKey::IconTranslate, defaultStyleProperties<SymbolProperties>().icon.translate },
{ PropertyKey::IconTranslateAnchor, defaultStyleProperties<SymbolProperties>().icon.translate_anchor },
{ PropertyKey::TextOpacity, defaultStyleProperties<SymbolProperties>().text.opacity },
@@ -39,8 +36,7 @@ const std::map<PropertyKey, PropertyValue> PropertyFallbackValue::properties = {
{ PropertyKey::TextHaloColor, defaultStyleProperties<SymbolProperties>().text.halo_color },
{ PropertyKey::TextHaloWidth, defaultStyleProperties<SymbolProperties>().text.halo_width },
{ PropertyKey::TextHaloBlur, defaultStyleProperties<SymbolProperties>().text.halo_blur },
- { PropertyKey::TextTranslateX, defaultStyleProperties<SymbolProperties>().text.translate[0] },
- { PropertyKey::TextTranslateY, defaultStyleProperties<SymbolProperties>().text.translate[1] },
+ { PropertyKey::TextTranslate, defaultStyleProperties<SymbolProperties>().text.translate },
{ PropertyKey::TextTranslateAnchor, defaultStyleProperties<SymbolProperties>().text.translate_anchor },
{ PropertyKey::RasterOpacity, defaultStyleProperties<RasterProperties>().opacity },
@@ -72,8 +68,7 @@ const std::map<PropertyKey, PropertyValue> PropertyFallbackValue::properties = {
{ PropertyKey::IconRotate, defaultLayoutProperties<StyleBucketSymbol>().icon.rotate },
{ PropertyKey::IconPadding, defaultLayoutProperties<StyleBucketSymbol>().icon.padding },
{ PropertyKey::IconKeepUpright, defaultLayoutProperties<StyleBucketSymbol>().icon.keep_upright },
- { PropertyKey::IconOffsetX, defaultLayoutProperties<StyleBucketSymbol>().icon.offset[0] },
- { PropertyKey::IconOffsetY, defaultLayoutProperties<StyleBucketSymbol>().icon.offset[1] },
+ { PropertyKey::IconOffset, defaultLayoutProperties<StyleBucketSymbol>().icon.offset },
{ PropertyKey::TextRotationAlignment, defaultLayoutProperties<StyleBucketSymbol>().text.rotation_alignment },
{ PropertyKey::TextField, defaultLayoutProperties<StyleBucketSymbol>().text.field },
@@ -89,8 +84,7 @@ const std::map<PropertyKey, PropertyValue> PropertyFallbackValue::properties = {
{ PropertyKey::TextPadding, defaultLayoutProperties<StyleBucketSymbol>().text.padding },
{ PropertyKey::TextKeepUpright, defaultLayoutProperties<StyleBucketSymbol>().text.keep_upright },
{ PropertyKey::TextTransform, defaultLayoutProperties<StyleBucketSymbol>().text.transform },
- { PropertyKey::TextOffsetX, defaultLayoutProperties<StyleBucketSymbol>().text.offset[0] },
- { PropertyKey::TextOffsetY, defaultLayoutProperties<StyleBucketSymbol>().text.offset[1] },
+ { PropertyKey::TextOffset, defaultLayoutProperties<StyleBucketSymbol>().text.offset },
{ PropertyKey::TextAllowOverlap, defaultLayoutProperties<StyleBucketSymbol>().text.allow_overlap },
{ PropertyKey::TextIgnorePlacement, defaultLayoutProperties<StyleBucketSymbol>().text.ignore_placement },
{ PropertyKey::TextOptional, defaultLayoutProperties<StyleBucketSymbol>().text.optional },
diff --git a/src/mbgl/style/property_key.hpp b/src/mbgl/style/property_key.hpp
index f10607a7af..dfe82e4bb0 100644
--- a/src/mbgl/style/property_key.hpp
+++ b/src/mbgl/style/property_key.hpp
@@ -9,16 +9,12 @@ enum class PropertyKey {
FillColor,
FillOutlineColor,
FillTranslate, // for transitions only
- FillTranslateX,
- FillTranslateY,
FillTranslateAnchor,
FillImage,
LineOpacity,
LineColor,
LineTranslate, // for transitions only
- LineTranslateX,
- LineTranslateY,
LineTranslateAnchor,
LineWidth,
LineGapWidth,
@@ -41,9 +37,7 @@ enum class PropertyKey {
IconHaloColor,
IconHaloWidth,
IconHaloBlur,
- IconTranslate, // for transitions only
- IconTranslateX,
- IconTranslateY,
+ IconTranslate,
IconTranslateAnchor,
IconAllowOverlap,
@@ -52,11 +46,10 @@ enum class PropertyKey {
IconRotationAlignment,
IconMaxSize,
IconImage,
+ IconOffset,
IconPadding,
IconRotate,
IconKeepUpright,
- IconOffsetX,
- IconOffsetY,
TextOpacity,
TextSize,
@@ -64,9 +57,7 @@ enum class PropertyKey {
TextHaloColor,
TextHaloWidth,
TextHaloBlur,
- TextTranslate, // for transitions only
- TextTranslateX,
- TextTranslateY,
+ TextTranslate,
TextTranslateAnchor,
TextRotationAlignment,
@@ -85,8 +76,7 @@ enum class PropertyKey {
TextAnchor,
TextKeepUpright,
TextTransform,
- TextOffsetX,
- TextOffsetY,
+ TextOffset,
TextAllowOverlap,
RasterOpacity,
diff --git a/src/mbgl/style/property_value.hpp b/src/mbgl/style/property_value.hpp
index ff5a2f8685..fbc3f3bd6a 100644
--- a/src/mbgl/style/property_value.hpp
+++ b/src/mbgl/style/property_value.hpp
@@ -3,29 +3,32 @@
#include <mbgl/util/variant.hpp>
#include <mbgl/style/function_properties.hpp>
+#include <mbgl/style/piecewisefunction_properties.hpp>
#include <mbgl/style/types.hpp>
#include <vector>
+#include <array>
namespace mbgl {
typedef mapbox::util::variant<
- std::string,
- TranslateAnchorType,
- RotateAnchorType,
- bool,
- CapType,
- JoinType,
+ Function<std::string>,
+ Function<TranslateAnchorType>,
+ Function<RotateAnchorType>,
+ Function<CapType>,
+ Function<JoinType>,
VisibilityType,
- PlacementType,
- RotationAlignmentType,
- TextTransformType,
- TextJustifyType,
- TextAnchorType,
+ Function<PlacementType>,
+ Function<RotationAlignmentType>,
+ Function<TextTransformType>,
+ Function<TextJustifyType>,
+ Function<TextAnchorType>,
+ Function<std::array<float, 2>>,
Function<bool>,
Function<float>,
Function<Color>,
- Function<std::vector<float>>
+ PiecewiseConstantFunction<Faded<std::vector<float>>>,
+ PiecewiseConstantFunction<Faded<std::string>>
> PropertyValue;
}
diff --git a/src/mbgl/style/style.cpp b/src/mbgl/style/style.cpp
index 01781afe3c..45217950f6 100644
--- a/src/mbgl/style/style.cpp
+++ b/src/mbgl/style/style.cpp
@@ -28,8 +28,10 @@ Style::~Style() {}
void Style::updateProperties(float z, std::chrono::steady_clock::time_point now) {
uv::writelock lock(mtx);
+ zoomHistory.update(z, now);
+
if (layers) {
- layers->updateProperties(z, now);
+ layers->updateProperties(z, now, zoomHistory);
}
// Apply transitions after the first time.
diff --git a/src/mbgl/style/style.hpp b/src/mbgl/style/style.hpp
index 751a91be62..4de827a38c 100644
--- a/src/mbgl/style/style.hpp
+++ b/src/mbgl/style/style.hpp
@@ -3,6 +3,7 @@
#include <mbgl/style/property_transition.hpp>
#include <mbgl/style/style_source.hpp>
+#include <mbgl/style/zoom_history.hpp>
#include <mbgl/util/uv.hpp>
#include <mbgl/util/ptr.hpp>
@@ -50,6 +51,7 @@ private:
PropertyTransition defaultTransition;
bool initial_render_complete = false;
std::unique_ptr<uv::rwlock> mtx;
+ ZoomHistory zoomHistory;
};
}
diff --git a/src/mbgl/style/style_layer.cpp b/src/mbgl/style/style_layer.cpp
index 3b82d7f94a..1a4354be27 100644
--- a/src/mbgl/style/style_layer.cpp
+++ b/src/mbgl/style/style_layer.cpp
@@ -94,7 +94,7 @@ void StyleLayer::applyClassProperties(const ClassID class_id,
template <typename T>
struct PropertyEvaluator {
typedef T result_type;
- PropertyEvaluator(float z_) : z(z_) {}
+ PropertyEvaluator(float z_, const ZoomHistory &zoomHistory_) : z(z_), zoomHistory(zoomHistory_) {}
template <typename P, typename std::enable_if<std::is_convertible<P, T>::value, int>::type = 0>
T operator()(const P &value) const {
@@ -105,6 +105,10 @@ struct PropertyEvaluator {
return mapbox::util::apply_visitor(FunctionEvaluator<T>(z), value);
}
+ T operator()(const PiecewiseConstantFunction<T> &value) const {
+ return value.evaluate(z, zoomHistory);
+ }
+
template <typename P, typename std::enable_if<!std::is_convertible<P, T>::value, int>::type = 0>
T operator()(const P &) const {
return T();
@@ -112,15 +116,16 @@ struct PropertyEvaluator {
private:
const float z;
+ const ZoomHistory &zoomHistory;
};
template <typename T>
-void StyleLayer::applyStyleProperty(PropertyKey key, T &target, const float z, const std::chrono::steady_clock::time_point now) {
+void StyleLayer::applyStyleProperty(PropertyKey key, T &target, const float z, const std::chrono::steady_clock::time_point now, const ZoomHistory &zoomHistory) {
auto it = appliedStyle.find(key);
if (it != appliedStyle.end()) {
AppliedClassProperties &applied = it->second;
// Iterate through all properties that we need to apply in order.
- const PropertyEvaluator<T> evaluator(z);
+ const PropertyEvaluator<T> evaluator(z, zoomHistory);
for (AppliedClassProperty &property : applied.properties) {
if (now >= property.begin) {
// We overwrite the current property with the new value.
@@ -133,12 +138,12 @@ void StyleLayer::applyStyleProperty(PropertyKey key, T &target, const float z, c
}
template <typename T>
-void StyleLayer::applyTransitionedStyleProperty(PropertyKey key, T &target, const float z, const std::chrono::steady_clock::time_point now) {
+void StyleLayer::applyTransitionedStyleProperty(PropertyKey key, T &target, const float z, const std::chrono::steady_clock::time_point now, const ZoomHistory &zoomHistory) {
auto it = appliedStyle.find(key);
if (it != appliedStyle.end()) {
AppliedClassProperties &applied = it->second;
// Iterate through all properties that we need to apply in order.
- const PropertyEvaluator<T> evaluator(z);
+ const PropertyEvaluator<T> evaluator(z, zoomHistory);
for (AppliedClassProperty &property : applied.properties) {
if (now >= property.end) {
// We overwrite the current property with the new value.
@@ -155,95 +160,91 @@ void StyleLayer::applyTransitionedStyleProperty(PropertyKey key, T &target, cons
}
template <>
-void StyleLayer::applyStyleProperties<FillProperties>(const float z, const std::chrono::steady_clock::time_point now) {
+void StyleLayer::applyStyleProperties<FillProperties>(const float z, const std::chrono::steady_clock::time_point now, const ZoomHistory &zoomHistory) {
properties.set<FillProperties>();
FillProperties &fill = properties.get<FillProperties>();
- applyStyleProperty(PropertyKey::FillAntialias, fill.antialias, z, now);
- applyTransitionedStyleProperty(PropertyKey::FillOpacity, fill.opacity, z, now);
- applyTransitionedStyleProperty(PropertyKey::FillColor, fill.fill_color, z, now);
- applyTransitionedStyleProperty(PropertyKey::FillOutlineColor, fill.stroke_color, z, now);
- applyTransitionedStyleProperty(PropertyKey::FillTranslateX, fill.translate[0], z, now);
- applyTransitionedStyleProperty(PropertyKey::FillTranslateY, fill.translate[1], z, now);
- applyStyleProperty(PropertyKey::FillTranslateAnchor, fill.translateAnchor, z, now);
- applyStyleProperty(PropertyKey::FillImage, fill.image, z, now);
+ applyStyleProperty(PropertyKey::FillAntialias, fill.antialias, z, now, zoomHistory);
+ applyTransitionedStyleProperty(PropertyKey::FillOpacity, fill.opacity, z, now, zoomHistory);
+ applyTransitionedStyleProperty(PropertyKey::FillColor, fill.fill_color, z, now, zoomHistory);
+ applyTransitionedStyleProperty(PropertyKey::FillOutlineColor, fill.stroke_color, z, now, zoomHistory);
+ applyTransitionedStyleProperty(PropertyKey::FillTranslate, fill.translate, z, now, zoomHistory);
+ applyStyleProperty(PropertyKey::FillTranslateAnchor, fill.translateAnchor, z, now, zoomHistory);
+ applyStyleProperty(PropertyKey::FillImage, fill.image, z, now, zoomHistory);
}
template <>
-void StyleLayer::applyStyleProperties<LineProperties>(const float z, const std::chrono::steady_clock::time_point now) {
+void StyleLayer::applyStyleProperties<LineProperties>(const float z, const std::chrono::steady_clock::time_point now, const ZoomHistory &zoomHistory) {
properties.set<LineProperties>();
LineProperties &line = properties.get<LineProperties>();
- applyTransitionedStyleProperty(PropertyKey::LineOpacity, line.opacity, z, now);
- applyTransitionedStyleProperty(PropertyKey::LineColor, line.color, z, now);
- applyTransitionedStyleProperty(PropertyKey::LineTranslateX, line.translate[0], z, now);
- applyTransitionedStyleProperty(PropertyKey::LineTranslateY, line.translate[1], z, now);
- applyStyleProperty(PropertyKey::LineTranslateAnchor, line.translateAnchor, z, now);
- applyTransitionedStyleProperty(PropertyKey::LineWidth, line.width, z, now);
- applyTransitionedStyleProperty(PropertyKey::LineGapWidth, line.gap_width, z, now);
- applyTransitionedStyleProperty(PropertyKey::LineBlur, line.blur, z, now);
- applyStyleProperty(PropertyKey::LineDashArray, line.dash_array, z, now);
- applyStyleProperty(PropertyKey::LineImage, line.image, z, now);
+ applyTransitionedStyleProperty(PropertyKey::LineOpacity, line.opacity, z, now, zoomHistory);
+ applyTransitionedStyleProperty(PropertyKey::LineColor, line.color, z, now, zoomHistory);
+ applyTransitionedStyleProperty(PropertyKey::LineTranslate, line.translate, z, now, zoomHistory);
+ applyStyleProperty(PropertyKey::LineTranslateAnchor, line.translateAnchor, z, now, zoomHistory);
+ applyTransitionedStyleProperty(PropertyKey::LineWidth, line.width, z, now, zoomHistory);
+ applyTransitionedStyleProperty(PropertyKey::LineGapWidth, line.gap_width, z, now, zoomHistory);
+ applyTransitionedStyleProperty(PropertyKey::LineBlur, line.blur, z, now, zoomHistory);
+ applyStyleProperty(PropertyKey::LineDashArray, line.dash_array, z, now, zoomHistory);
+ applyStyleProperty(PropertyKey::LineImage, line.image, z, now, zoomHistory);
// for scaling dasharrays
- applyStyleProperty(PropertyKey::LineWidth, line.dash_line_width, std::floor(z), std::chrono::steady_clock::time_point::max());
+ applyStyleProperty(PropertyKey::LineWidth, line.dash_line_width, std::floor(z), std::chrono::steady_clock::time_point::max(), zoomHistory);
}
template <>
-void StyleLayer::applyStyleProperties<SymbolProperties>(const float z, const std::chrono::steady_clock::time_point now) {
+void StyleLayer::applyStyleProperties<SymbolProperties>(const float z, const std::chrono::steady_clock::time_point now, const ZoomHistory &zoomHistory) {
properties.set<SymbolProperties>();
SymbolProperties &symbol = properties.get<SymbolProperties>();
- applyTransitionedStyleProperty(PropertyKey::IconOpacity, symbol.icon.opacity, z, now);
- applyTransitionedStyleProperty(PropertyKey::IconRotate, symbol.icon.rotate, z, now);
- applyTransitionedStyleProperty(PropertyKey::IconSize, symbol.icon.size, z, now);
- applyTransitionedStyleProperty(PropertyKey::IconColor, symbol.icon.color, z, now);
- applyTransitionedStyleProperty(PropertyKey::IconHaloColor, symbol.icon.halo_color, z, now);
- applyTransitionedStyleProperty(PropertyKey::IconHaloWidth, symbol.icon.halo_width, z, now);
- applyTransitionedStyleProperty(PropertyKey::IconHaloBlur, symbol.icon.halo_blur, z, now);
- applyTransitionedStyleProperty(PropertyKey::IconTranslateX, symbol.icon.translate[0], z, now);
- applyTransitionedStyleProperty(PropertyKey::IconTranslateY, symbol.icon.translate[1], z, now);
- applyStyleProperty(PropertyKey::IconTranslateAnchor, symbol.icon.translate_anchor, z, now);
-
- applyTransitionedStyleProperty(PropertyKey::TextOpacity, symbol.text.opacity, z, now);
- applyTransitionedStyleProperty(PropertyKey::TextSize, symbol.text.size, z, now);
- applyTransitionedStyleProperty(PropertyKey::TextColor, symbol.text.color, z, now);
- applyTransitionedStyleProperty(PropertyKey::TextHaloColor, symbol.text.halo_color, z, now);
- applyTransitionedStyleProperty(PropertyKey::TextHaloWidth, symbol.text.halo_width, z, now);
- applyTransitionedStyleProperty(PropertyKey::TextHaloBlur, symbol.text.halo_blur, z, now);
- applyTransitionedStyleProperty(PropertyKey::TextTranslateX, symbol.text.translate[0], z, now);
- applyTransitionedStyleProperty(PropertyKey::TextTranslateY, symbol.text.translate[1], z, now);
- applyStyleProperty(PropertyKey::TextTranslateAnchor, symbol.text.translate_anchor, z, now);
+ applyTransitionedStyleProperty(PropertyKey::IconOpacity, symbol.icon.opacity, z, now, zoomHistory);
+ applyTransitionedStyleProperty(PropertyKey::IconRotate, symbol.icon.rotate, z, now, zoomHistory);
+ applyTransitionedStyleProperty(PropertyKey::IconSize, symbol.icon.size, z, now, zoomHistory);
+ applyTransitionedStyleProperty(PropertyKey::IconColor, symbol.icon.color, z, now, zoomHistory);
+ applyTransitionedStyleProperty(PropertyKey::IconHaloColor, symbol.icon.halo_color, z, now, zoomHistory);
+ applyTransitionedStyleProperty(PropertyKey::IconHaloWidth, symbol.icon.halo_width, z, now, zoomHistory);
+ applyTransitionedStyleProperty(PropertyKey::IconHaloBlur, symbol.icon.halo_blur, z, now, zoomHistory);
+ applyTransitionedStyleProperty(PropertyKey::IconTranslate, symbol.icon.translate, z, now, zoomHistory);
+ applyStyleProperty(PropertyKey::IconTranslateAnchor, symbol.icon.translate_anchor, z, now, zoomHistory);
+
+ applyTransitionedStyleProperty(PropertyKey::TextOpacity, symbol.text.opacity, z, now, zoomHistory);
+ applyTransitionedStyleProperty(PropertyKey::TextSize, symbol.text.size, z, now, zoomHistory);
+ applyTransitionedStyleProperty(PropertyKey::TextColor, symbol.text.color, z, now, zoomHistory);
+ applyTransitionedStyleProperty(PropertyKey::TextHaloColor, symbol.text.halo_color, z, now, zoomHistory);
+ applyTransitionedStyleProperty(PropertyKey::TextHaloWidth, symbol.text.halo_width, z, now, zoomHistory);
+ applyTransitionedStyleProperty(PropertyKey::TextHaloBlur, symbol.text.halo_blur, z, now, zoomHistory);
+ applyTransitionedStyleProperty(PropertyKey::TextTranslate, symbol.text.translate, z, now, zoomHistory);
+ applyStyleProperty(PropertyKey::TextTranslateAnchor, symbol.text.translate_anchor, z, now, zoomHistory);
}
template <>
-void StyleLayer::applyStyleProperties<RasterProperties>(const float z, const std::chrono::steady_clock::time_point now) {
+void StyleLayer::applyStyleProperties<RasterProperties>(const float z, const std::chrono::steady_clock::time_point now, const ZoomHistory &zoomHistory) {
properties.set<RasterProperties>();
RasterProperties &raster = properties.get<RasterProperties>();
- applyTransitionedStyleProperty(PropertyKey::RasterOpacity, raster.opacity, z, now);
- applyTransitionedStyleProperty(PropertyKey::RasterHueRotate, raster.hue_rotate, z, now);
- applyTransitionedStyleProperty(PropertyKey::RasterBrightnessLow, raster.brightness[0], z, now);
- applyTransitionedStyleProperty(PropertyKey::RasterBrightnessHigh, raster.brightness[1], z, now);
- applyTransitionedStyleProperty(PropertyKey::RasterSaturation, raster.saturation, z, now);
- applyTransitionedStyleProperty(PropertyKey::RasterContrast, raster.contrast, z, now);
- applyTransitionedStyleProperty(PropertyKey::RasterFade, raster.fade, z, now);
+ applyTransitionedStyleProperty(PropertyKey::RasterOpacity, raster.opacity, z, now, zoomHistory);
+ applyTransitionedStyleProperty(PropertyKey::RasterHueRotate, raster.hue_rotate, z, now, zoomHistory);
+ applyTransitionedStyleProperty(PropertyKey::RasterBrightnessLow, raster.brightness[0], z, now, zoomHistory);
+ applyTransitionedStyleProperty(PropertyKey::RasterBrightnessHigh, raster.brightness[1], z, now, zoomHistory);
+ applyTransitionedStyleProperty(PropertyKey::RasterSaturation, raster.saturation, z, now, zoomHistory);
+ applyTransitionedStyleProperty(PropertyKey::RasterContrast, raster.contrast, z, now, zoomHistory);
+ applyTransitionedStyleProperty(PropertyKey::RasterFade, raster.fade, z, now, zoomHistory);
}
template <>
-void StyleLayer::applyStyleProperties<BackgroundProperties>(const float z, const std::chrono::steady_clock::time_point now) {
+void StyleLayer::applyStyleProperties<BackgroundProperties>(const float z, const std::chrono::steady_clock::time_point now, const ZoomHistory &zoomHistory) {
properties.set<BackgroundProperties>();
BackgroundProperties &background = properties.get<BackgroundProperties>();
- applyTransitionedStyleProperty(PropertyKey::BackgroundOpacity, background.opacity, z, now);
- applyTransitionedStyleProperty(PropertyKey::BackgroundColor, background.color, z, now);
- applyStyleProperty(PropertyKey::BackgroundImage, background.image, z, now);
+ applyTransitionedStyleProperty(PropertyKey::BackgroundOpacity, background.opacity, z, now, zoomHistory);
+ applyTransitionedStyleProperty(PropertyKey::BackgroundColor, background.color, z, now, zoomHistory);
+ applyStyleProperty(PropertyKey::BackgroundImage, background.image, z, now, zoomHistory);
}
-void StyleLayer::updateProperties(float z, const std::chrono::steady_clock::time_point now) {
+void StyleLayer::updateProperties(float z, const std::chrono::steady_clock::time_point now, ZoomHistory &zoomHistory) {
cleanupAppliedStyleProperties(now);
switch (type) {
- case StyleLayerType::Fill: applyStyleProperties<FillProperties>(z, now); break;
- case StyleLayerType::Line: applyStyleProperties<LineProperties>(z, now); break;
- case StyleLayerType::Symbol: applyStyleProperties<SymbolProperties>(z, now); break;
- case StyleLayerType::Raster: applyStyleProperties<RasterProperties>(z, now); break;
- case StyleLayerType::Background: applyStyleProperties<BackgroundProperties>(z, now); break;
+ case StyleLayerType::Fill: applyStyleProperties<FillProperties>(z, now, zoomHistory); break;
+ case StyleLayerType::Line: applyStyleProperties<LineProperties>(z, now, zoomHistory); break;
+ case StyleLayerType::Symbol: applyStyleProperties<SymbolProperties>(z, now, zoomHistory); break;
+ case StyleLayerType::Raster: applyStyleProperties<RasterProperties>(z, now, zoomHistory); break;
+ case StyleLayerType::Background: applyStyleProperties<BackgroundProperties>(z, now, zoomHistory); break;
default: properties.set<std::false_type>(); break;
}
}
diff --git a/src/mbgl/style/style_layer.hpp b/src/mbgl/style/style_layer.hpp
index b6cfe779c7..69af1dc230 100644
--- a/src/mbgl/style/style_layer.hpp
+++ b/src/mbgl/style/style_layer.hpp
@@ -5,6 +5,7 @@
#include <mbgl/style/class_properties.hpp>
#include <mbgl/style/style_properties.hpp>
#include <mbgl/style/applied_class_properties.hpp>
+#include <mbgl/style/zoom_history.hpp>
#include <mbgl/util/ptr.hpp>
@@ -36,7 +37,7 @@ public:
// Updates the StyleProperties information in this layer by evaluating all
// pending transitions and applied classes in order.
- void updateProperties(float z, std::chrono::steady_clock::time_point now);
+ void updateProperties(float z, std::chrono::steady_clock::time_point now, ZoomHistory &zoomHistory);
// Sets the list of classes and creates transitions to the currently applied values.
void setClasses(const std::vector<std::string> &class_names, std::chrono::steady_clock::time_point now,
@@ -51,9 +52,9 @@ private:
// Sets the properties of this object by evaluating all pending transitions and
// aplied classes in order.
- template <typename T> void applyStyleProperties(float z, std::chrono::steady_clock::time_point now);
- template <typename T> void applyStyleProperty(PropertyKey key, T &, float z, std::chrono::steady_clock::time_point now);
- template <typename T> void applyTransitionedStyleProperty(PropertyKey key, T &, float z, std::chrono::steady_clock::time_point now);
+ template <typename T> void applyStyleProperties(float z, std::chrono::steady_clock::time_point now, const ZoomHistory &zoomHistory);
+ template <typename T> void applyStyleProperty(PropertyKey key, T &, float z, std::chrono::steady_clock::time_point now, const ZoomHistory &zoomHistory);
+ template <typename T> void applyTransitionedStyleProperty(PropertyKey key, T &, float z, std::chrono::steady_clock::time_point now, const ZoomHistory &zoomHistory);
// Removes all expired style transitions.
void cleanupAppliedStyleProperties(std::chrono::steady_clock::time_point now);
diff --git a/src/mbgl/style/style_layer_group.cpp b/src/mbgl/style/style_layer_group.cpp
index 3f1a4a5111..f57ec5cc7b 100644
--- a/src/mbgl/style/style_layer_group.cpp
+++ b/src/mbgl/style/style_layer_group.cpp
@@ -11,10 +11,10 @@ void StyleLayerGroup::setClasses(const std::vector<std::string> &class_names, st
}
}
-void StyleLayerGroup::updateProperties(float z, std::chrono::steady_clock::time_point now) {
+void StyleLayerGroup::updateProperties(float z, std::chrono::steady_clock::time_point now, ZoomHistory &zoomHistory) {
for (const util::ptr<StyleLayer> &layer: layers) {
if (layer) {
- layer->updateProperties(z, now);
+ layer->updateProperties(z, now, zoomHistory);
}
}
}
diff --git a/src/mbgl/style/style_layer_group.hpp b/src/mbgl/style/style_layer_group.hpp
index 3f5a20073d..79b75a55e9 100644
--- a/src/mbgl/style/style_layer_group.hpp
+++ b/src/mbgl/style/style_layer_group.hpp
@@ -12,7 +12,7 @@ class StyleLayerGroup {
public:
void setClasses(const std::vector<std::string> &class_names, std::chrono::steady_clock::time_point now,
const PropertyTransition &defaultTransition);
- void updateProperties(float z, std::chrono::steady_clock::time_point now);
+ void updateProperties(float z, std::chrono::steady_clock::time_point now, ZoomHistory &zoomHistory);
bool hasTransitions() const;
public:
diff --git a/src/mbgl/style/style_parser.cpp b/src/mbgl/style/style_parser.cpp
index 15aedd8ead..af6a84492d 100644
--- a/src/mbgl/style/style_parser.cpp
+++ b/src/mbgl/style/style_parser.cpp
@@ -209,7 +209,7 @@ Color parseColor(JSVal value) {
css_color.a}};
}
-std::tuple<bool,std::vector<float>> parseFloatArray(JSVal value) {
+std::tuple<bool,std::vector<float>> StyleParser::parseFloatArray(JSVal value) {
if (!value.IsArray()) {
Log::Warning(Event::ParseStyle, "dasharray value must be an array of numbers");
return std::tuple<bool, std::vector<float>> { false, std::vector<float>() };
@@ -217,7 +217,7 @@ std::tuple<bool,std::vector<float>> parseFloatArray(JSVal value) {
std::vector<float> vec;
for (rapidjson::SizeType i = 0; i < value.Size(); ++i) {
- JSVal part = value[i];
+ JSVal part = replaceConstant(value[i]);
if (!part.IsNumber()) {
Log::Warning(Event::ParseStyle, "dasharray value must be an array of numbers");
return std::tuple<bool, std::vector<float>> { false, std::vector<float>() };
@@ -228,46 +228,101 @@ std::tuple<bool,std::vector<float>> parseFloatArray(JSVal value) {
}
template <>
-bool StyleParser::parseFunctionArgument(JSVal value) {
- JSVal rvalue = replaceConstant(value);
- if (rvalue.IsBool()) {
- return rvalue.GetBool();
- } else if (rvalue.IsNumber()) {
- return rvalue.GetDouble();
+std::tuple<bool, std::array<float, 2>> StyleParser::parseProperty(JSVal value, const char*) {
+ if (value.IsArray() && value.Size() == 2 &&
+ value[rapidjson::SizeType(0)].IsNumber() &&
+ value[rapidjson::SizeType(1)].IsNumber()) {
+
+ float first = value[rapidjson::SizeType(0)].GetDouble();
+ float second = value[rapidjson::SizeType(1)].GetDouble();
+ return std::tuple<bool, std::array<float, 2>> { false, {{ first, second }} };
} else {
- Log::Warning(Event::ParseStyle, "function argument must be a boolean or numeric value");
- return false;
+ Log::Warning(Event::ParseStyle, "value must be array of two numbers");
+ return std::tuple<bool, std::array<float, 2>> { false, {{ 0.0f, 0.0f }} };
}
}
template <>
-float StyleParser::parseFunctionArgument(JSVal value) {
+std::tuple<bool, float> StyleParser::parseProperty(JSVal value, const char* property_name) {
JSVal rvalue = replaceConstant(value);
if (rvalue.IsNumber()) {
- return rvalue.GetDouble();
+ return std::tuple<bool, float> { true, rvalue.GetDouble() };
} else {
- Log::Warning(Event::ParseStyle, "function argument must be a numeric value");
- return 0.0f;
+ Log::Warning(Event::ParseStyle, "value of '%s' must be a number, or a number function", property_name);
+ return std::tuple<bool, float> { false, 0.0f };
}
}
template <>
-Color StyleParser::parseFunctionArgument(JSVal value) {
+std::tuple<bool, Color> StyleParser::parseProperty(JSVal value, const char*) {
+ JSVal rvalue = replaceConstant(value);
+ return std::tuple<bool, Color> { true, parseColor(rvalue) };
+}
+
+template <>
+std::tuple<bool, Faded<std::vector<float>>> StyleParser::parseProperty(JSVal value, const char*) {
+ Faded<std::vector<float>> parsed;
JSVal rvalue = replaceConstant(value);
- return parseColor(rvalue);
+ parsed.to = std::get<1>(parseFloatArray(rvalue));
+ return std::tuple<bool, Faded<std::vector<float>>> { true, parsed };
}
template <>
-std::vector<float> StyleParser::parseFunctionArgument(JSVal value) {
+std::tuple<bool, Faded<std::string>> StyleParser::parseProperty(JSVal value, const char *property_name) {
JSVal rvalue = replaceConstant(value);
- return std::get<1>(parseFloatArray(rvalue));
+ Faded<std::string> parsed;
+ if (rvalue.IsString()) {
+ parsed.to = { value.GetString(), value.GetStringLength() };
+ return std::tuple<bool, Faded<std::string>> { true, parsed };
+ } else {
+ Log::Warning(Event::ParseStyle, "value of '%s' must be a string, or a string function", property_name);
+ return std::tuple<bool, Faded<std::string>> { false, parsed };
+ }
+}
+
+template <typename T>
+std::tuple<bool, std::vector<std::pair<float, T>>> StyleParser::parseStops(JSVal value_stops, const char *property_name) {
+
+ if (!value_stops.IsArray()) {
+ Log::Warning(Event::ParseStyle, "stops function must specify a stops array");
+ return std::tuple<bool, std::vector<std::pair<float, T>>> { false, {}};
+ }
+
+ std::vector<std::pair<float, T>> stops;
+
+ for (rapidjson::SizeType i = 0; i < value_stops.Size(); ++i) {
+ JSVal stop = value_stops[i];
+ if (stop.IsArray()) {
+ if (stop.Size() != 2) {
+ Log::Warning(Event::ParseStyle, "stop must have zoom level and value specification");
+ return std::tuple<bool, std::vector<std::pair<float, T>>> { false, {}};
+ }
+
+ JSVal z = stop[rapidjson::SizeType(0)];
+ if (!z.IsNumber()) {
+ Log::Warning(Event::ParseStyle, "zoom level in stop must be a number");
+ return std::tuple<bool, std::vector<std::pair<float, T>>> { false, {}};
+ }
+
+ stops.emplace_back(z.GetDouble(), std::get<1>(parseProperty<T>(replaceConstant(stop[rapidjson::SizeType(1)]), property_name)));
+ } else {
+ Log::Warning(Event::ParseStyle, "function argument must be a numeric value");
+ return std::tuple<bool, std::vector<std::pair<float, T>>> { false, {}};
+ }
+ }
+ return std::tuple<bool, std::vector<std::pair<float, T>>>(true, stops);
}
template <typename T> inline float defaultBaseValue() { return 1.75; }
template <> inline float defaultBaseValue<Color>() { return 1.0; }
template <typename T>
-std::tuple<bool, Function<T>> StyleParser::parseFunction(JSVal value) {
+std::tuple<bool, Function<T>> StyleParser::parseFunction(JSVal value, const char *property_name) {
+
+ if (!value.IsObject()) {
+ return std::tuple<bool, Function<T>> { true, ConstantFunction<T>(std::get<1>(parseProperty<T>(value, property_name))) };
+ }
+
if (!value.HasMember("stops")) {
Log::Warning(Event::ParseStyle, "function must specify a function type");
return std::tuple<bool, Function<T>> { false, ConstantFunction<T>(T()) };
@@ -284,47 +339,47 @@ std::tuple<bool, Function<T>> StyleParser::parseFunction(JSVal value) {
}
}
- JSVal value_stops = value["stops"];
- if (!value_stops.IsArray()) {
- Log::Warning(Event::ParseStyle, "stops function must specify a stops array");
+ auto stops = parseStops<T>(value["stops"], property_name);
+
+ if (!std::get<0>(stops)) {
return std::tuple<bool, Function<T>> { false, ConstantFunction<T>(T()) };
}
- std::vector<std::pair<float, T>> stops;
- for (rapidjson::SizeType i = 0; i < value_stops.Size(); ++i) {
- JSVal stop = value_stops[i];
- if (stop.IsArray()) {
- if (stop.Size() != 2) {
- Log::Warning(Event::ParseStyle, "stop must have zoom level and value specification");
- return std::tuple<bool, Function<T>> { false, ConstantFunction<T>(T()) };
- }
+ return std::tuple<bool, Function<T>> { true, StopsFunction<T>(std::get<1>(stops), base) };
+}
- JSVal z = stop[rapidjson::SizeType(0)];
- if (!z.IsNumber()) {
- Log::Warning(Event::ParseStyle, "zoom level in stop must be a number");
- return std::tuple<bool, Function<T>> { false, ConstantFunction<T>(T()) };
- }
+template <typename T>
+std::tuple<bool, PiecewiseConstantFunction<T>> StyleParser::parsePiecewiseConstantFunction(JSVal value, std::chrono::steady_clock::duration duration) {
+ if (!value.HasMember("stops")) {
+ Log::Warning(Event::ParseStyle, "function must specify a function type");
+ return std::tuple<bool, PiecewiseConstantFunction<T>> { false, {} };
+ }
- stops.emplace_back(z.GetDouble(), parseFunctionArgument<T>(stop[rapidjson::SizeType(1)]));
- } else {
- Log::Warning(Event::ParseStyle, "function argument must be a numeric value");
- return std::tuple<bool, Function<T>> { false, ConstantFunction<T>(T()) };
- }
+ auto stops = parseStops<T>(value["stops"], "");
+
+ if (!std::get<0>(stops)) {
+ return std::tuple<bool, PiecewiseConstantFunction<T>> { false, {} };
}
- return std::tuple<bool, Function<T>> { true, StopsFunction<T>(stops, base) };
+ return std::tuple<bool, PiecewiseConstantFunction<T>> { true, { std::get<1>(stops), duration } };
}
-
template <typename T>
bool StyleParser::setProperty(JSVal value, const char *property_name, PropertyKey key, ClassProperties &klass) {
- bool parsed;
- T result;
- std::tie(parsed, result) = parseProperty<T>(value, property_name);
- if (parsed) {
- klass.set(key, result);
+ auto res = parseProperty<T>(value, property_name);
+ if (std::get<0>(res)) {
+ klass.set(key, std::get<1>(res));
+ }
+ return std::get<0>(res);
+}
+
+template <typename T>
+bool StyleParser::setProperty(JSVal value, const char *property_name, PropertyKey key, ClassProperties &klass, JSVal transition) {
+ auto res = parseProperty<T>(value, property_name, transition);
+ if (std::get<0>(res)) {
+ klass.set(key, std::get<1>(res));
}
- return parsed;
+ return std::get<0>(res);
}
template<typename T>
@@ -348,6 +403,21 @@ bool StyleParser::parseOptionalProperty(const char *property_name, PropertyKey k
}
}
+template<typename T>
+bool StyleParser::parseOptionalProperty(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value, const char *transition_name) {
+ if (!value.HasMember(property_name)) {
+ return false;
+ } else {
+ if (value.HasMember(transition_name)) {
+ return setProperty<T>(replaceConstant(value[property_name]), property_name, key, klass, value[transition_name]);
+ } else {
+ JSVal val = JSVal(rapidjson::kObjectType);
+ return setProperty<T>(replaceConstant(value[property_name]), property_name, key, klass, val);
+ }
+ }
+}
+
+
template<> std::tuple<bool, std::string> StyleParser::parseProperty(JSVal value, const char *property_name) {
if (!value.IsString()) {
Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name);
@@ -465,72 +535,99 @@ template<> std::tuple<bool, PropertyTransition> StyleParser::parseProperty(JSVal
return std::tuple<bool, PropertyTransition> { true, std::move(transition) };
}
+template<> std::tuple<bool, Function<std::array<float, 2>>> StyleParser::parseProperty(JSVal value, const char *property_name) {
+ return parseFunction<std::array<float, 2>>(value, property_name);
+}
+
+template<> std::tuple<bool, Function<std::string>> StyleParser::parseProperty(JSVal value, const char *property_name) {
+ return parseFunction<std::string>(value, property_name);
+}
+
+template<> std::tuple<bool, Function<TranslateAnchorType>> StyleParser::parseProperty(JSVal value, const char *property_name) {
+ return parseFunction<TranslateAnchorType>(value, property_name);
+}
+
+template<> std::tuple<bool, Function<RotateAnchorType>> StyleParser::parseProperty(JSVal value, const char *property_name) {
+ return parseFunction<RotateAnchorType>(value, property_name);
+}
+
+template<> std::tuple<bool, Function<CapType>> StyleParser::parseProperty(JSVal value, const char *property_name) {
+ return parseFunction<CapType>(value, property_name);
+}
+
+template<> std::tuple<bool, Function<JoinType>> StyleParser::parseProperty(JSVal value, const char *property_name) {
+ return parseFunction<JoinType>(value, property_name);
+}
+
+template<> std::tuple<bool, Function<PlacementType>> StyleParser::parseProperty(JSVal value, const char *property_name) {
+ return parseFunction<PlacementType>(value, property_name);
+}
+
+template<> std::tuple<bool, Function<TextAnchorType>> StyleParser::parseProperty(JSVal value, const char *property_name) {
+ return parseFunction<TextAnchorType>(value, property_name);
+}
+
+template<> std::tuple<bool, Function<TextJustifyType>> StyleParser::parseProperty(JSVal value, const char *property_name) {
+ return parseFunction<TextJustifyType>(value, property_name);
+}
+
+template<> std::tuple<bool, Function<TextTransformType>> StyleParser::parseProperty(JSVal value, const char *property_name) {
+ return parseFunction<TextTransformType>(value, property_name);
+}
+
+template<> std::tuple<bool, Function<RotationAlignmentType>> StyleParser::parseProperty(JSVal value, const char *property_name) {
+ return parseFunction<RotationAlignmentType>(value, property_name);
+}
+
+
template<> std::tuple<bool, Function<bool>> StyleParser::parseProperty(JSVal value, const char *property_name) {
- if (value.IsObject()) {
- return parseFunction<bool>(value);
- } else if (value.IsNumber()) {
- return std::tuple<bool, Function<bool>> { true, ConstantFunction<bool>(value.GetDouble()) };
- } else if (value.IsBool()) {
- return std::tuple<bool, Function<bool>> { true, ConstantFunction<bool>(value.GetBool()) };
- } else {
- Log::Warning(Event::ParseStyle, "value of '%s' must be convertible to boolean, or a boolean function", property_name);
- return std::tuple<bool, Function<bool>> { false, ConstantFunction<bool>(false) };
- }
+ return parseFunction<bool>(value, property_name);
}
template<> std::tuple<bool, Function<float>> StyleParser::parseProperty(JSVal value, const char *property_name) {
- if (value.IsObject()) {
- return parseFunction<float>(value);
- } else if (value.IsNumber()) {
- return std::tuple<bool, Function<float>> { true, ConstantFunction<float>(value.GetDouble()) };
- } else if (value.IsBool()) {
- return std::tuple<bool, Function<float>> { true, ConstantFunction<float>(value.GetBool()) };
- } else {
- Log::Warning(Event::ParseStyle, "value of '%s' must be a number, or a number function", property_name);
- return std::tuple<bool, Function<float>> { false, ConstantFunction<float>(0) };
- }
+ return parseFunction<float>(value, property_name);
}
template<> std::tuple<bool, Function<Color>> StyleParser::parseProperty(JSVal value, const char *property_name) {
- if (value.IsObject()) {
- return parseFunction<Color>(value);
- } else if (value.IsString()) {
- return std::tuple<bool, Function<Color>> { true, ConstantFunction<Color>(parseColor(value)) };
- } else {
- Log::Warning(Event::ParseStyle, "value of '%s' must be a color, or a color function", property_name);
- return std::tuple<bool, Function<Color>> { false, ConstantFunction<Color>(Color {{ 0, 0, 0, 0 }}) };
- }
+ return parseFunction<Color>(value, property_name);
}
-template<> std::tuple<bool, Function<std::vector<float>>> StyleParser::parseProperty(JSVal value, const char *property_name) {
+template<> std::tuple<bool, PiecewiseConstantFunction<Faded<std::vector<float>>>> StyleParser::parseProperty(JSVal value, const char *property_name, JSVal transition) {
+ std::chrono::steady_clock::duration duration = std::chrono::milliseconds(300);
+ if (transition.HasMember("duration")) {
+ duration = std::chrono::milliseconds(transition["duration"].GetUint());
+ }
+
if (value.IsObject()) {
- return parseFunction<std::vector<float>>(value);
+ return parsePiecewiseConstantFunction<Faded<std::vector<float>>>(value, duration);
} else if (value.IsArray()) {
- std::tuple<bool, std::vector<float>> parsed = parseFloatArray(value);
- return std::tuple<bool, Function<std::vector<float>>> { std::get<0>(parsed), ConstantFunction<std::vector<float>>(std::get<1>(parsed)) };
+ Faded<std::vector<float>> parsed;
+ std::tuple<bool, std::vector<float>> floatarray = parseFloatArray(value);
+ parsed.to = std::get<1>(floatarray);
+ return std::tuple<bool, PiecewiseConstantFunction<Faded<std::vector<float>>>> { std::get<0>(floatarray), { parsed, duration } };
} else {
Log::Warning(Event::ParseStyle, "value of '%s' must be an array of numbers, or a number array function", property_name);
- return std::tuple<bool, Function<std::vector<float>>> { false, ConstantFunction<std::vector<float>>(std::vector<float>()) };
+ return std::tuple<bool, PiecewiseConstantFunction<Faded<std::vector<float>>>> { false, {} };
}
}
-template <typename T>
-bool StyleParser::parseOptionalProperty(const char *property_name, const std::vector<PropertyKey> &keys, ClassProperties &klass, JSVal value) {
- if (value.HasMember(property_name)) {
- JSVal rvalue = replaceConstant(value[property_name]);
- if (!rvalue.IsArray()) {
- Log::Warning(Event::ParseStyle, "array value must be an array");
- }
+template<> std::tuple<bool, PiecewiseConstantFunction<Faded<std::string>>> StyleParser::parseProperty(JSVal value, const char *property_name, JSVal transition) {
- if (rvalue.Size() != keys.size()) {
- Log::Warning(Event::ParseStyle, "array value has unexpected number of elements");
- }
+ std::chrono::steady_clock::duration duration = std::chrono::milliseconds(300);
+ if (transition.HasMember("duration")) {
+ duration = std::chrono::milliseconds(transition["duration"].GetUint());
+ }
- for (uint16_t i = 0; i < keys.size(); i++) {
- setProperty<T>(rvalue[(rapidjson::SizeType)i], property_name, keys[i], klass);
- }
+ if (value.IsObject()) {
+ return parsePiecewiseConstantFunction<Faded<std::string>>(value, duration);
+ } else if (value.IsString()) {
+ Faded<std::string> parsed;
+ parsed.to = { value.GetString(), value.GetStringLength() };
+ return std::tuple<bool, PiecewiseConstantFunction<Faded<std::string>>> { true, { parsed, duration } };
+ } else {
+ Log::Warning(Event::ParseStyle, "value of '%s' must be string or a string function", property_name);
+ return std::tuple<bool, PiecewiseConstantFunction<Faded<std::string>>> { false, {} };
}
- return true;
}
#pragma mark - Parse Layers
@@ -654,26 +751,26 @@ void StyleParser::parsePaint(JSVal value, ClassProperties &klass) {
parseOptionalProperty<PropertyTransition>("fill-color-transition", Key::FillColor, klass, value);
parseOptionalProperty<Function<Color>>("fill-outline-color", Key::FillOutlineColor, klass, value);
parseOptionalProperty<PropertyTransition>("fill-outline-color-transition", Key::FillOutlineColor, klass, value);
- parseOptionalProperty<Function<float>>("fill-translate", { Key::FillTranslateX, Key::FillTranslateY }, klass, value);
+ parseOptionalProperty<Function<std::array<float, 2>>>("fill-translate", Key::FillTranslate, klass, value);
parseOptionalProperty<PropertyTransition>("fill-translate-transition", Key::FillTranslate, klass, value);
- parseOptionalProperty<TranslateAnchorType>("fill-translate-anchor", Key::FillTranslateAnchor, klass, value);
- parseOptionalProperty<std::string>("fill-image", Key::FillImage, klass, value);
+ parseOptionalProperty<Function<TranslateAnchorType>>("fill-translate-anchor", Key::FillTranslateAnchor, klass, value);
+ parseOptionalProperty<PiecewiseConstantFunction<Faded<std::string>>>("fill-image", Key::FillImage, klass, value, "fill-image-transition");
parseOptionalProperty<Function<float>>("line-opacity", Key::LineOpacity, klass, value);
parseOptionalProperty<PropertyTransition>("line-opacity-transition", Key::LineOpacity, klass, value);
parseOptionalProperty<Function<Color>>("line-color", Key::LineColor, klass, value);
parseOptionalProperty<PropertyTransition>("line-color-transition", Key::LineColor, klass, value);
- parseOptionalProperty<Function<float>>("line-translate", { Key::LineTranslateX, Key::LineTranslateY }, klass, value);
+ parseOptionalProperty<Function<std::array<float,2>>>("line-translate", Key::LineTranslate, klass, value);
parseOptionalProperty<PropertyTransition>("line-translate-transition", Key::LineTranslate, klass, value);
- parseOptionalProperty<TranslateAnchorType>("line-translate-anchor", Key::LineTranslateAnchor, klass, value);
+ parseOptionalProperty<Function<TranslateAnchorType>>("line-translate-anchor", Key::LineTranslateAnchor, klass, value);
parseOptionalProperty<Function<float>>("line-width", Key::LineWidth, klass, value);
parseOptionalProperty<PropertyTransition>("line-width-transition", Key::LineWidth, klass, value);
parseOptionalProperty<Function<float>>("line-gap-width", Key::LineGapWidth, klass, value);
parseOptionalProperty<PropertyTransition>("line-gap-width-transition", Key::LineGapWidth, klass, value);
parseOptionalProperty<Function<float>>("line-blur", Key::LineBlur, klass, value);
parseOptionalProperty<PropertyTransition>("line-blur-transition", Key::LineBlur, klass, value);
- parseOptionalProperty<Function<std::vector<float>>>("line-dasharray", Key::LineDashArray, klass, value);
- parseOptionalProperty<std::string>("line-image", Key::LineImage, klass, value);
+ parseOptionalProperty<PiecewiseConstantFunction<Faded<std::vector<float>>>>("line-dasharray", Key::LineDashArray, klass, value, "line-dasharray-transition");
+ parseOptionalProperty<PiecewiseConstantFunction<Faded<std::string>>>("line-image", Key::LineImage, klass, value, "line-image-transition");
parseOptionalProperty<Function<float>>("icon-opacity", Key::IconOpacity, klass, value);
parseOptionalProperty<PropertyTransition>("icon-opacity-transition", Key::IconOpacity, klass, value);
@@ -688,9 +785,9 @@ void StyleParser::parsePaint(JSVal value, ClassProperties &klass) {
parseOptionalProperty<PropertyTransition>("icon-halo-width-transition", Key::IconHaloWidth, klass, value);
parseOptionalProperty<Function<float>>("icon-halo-blur", Key::IconHaloBlur, klass, value);
parseOptionalProperty<PropertyTransition>("icon-halo-blur-transition", Key::IconHaloBlur, klass, value);
- parseOptionalProperty<Function<float>>("icon-translate", { Key::IconTranslateX, Key::IconTranslateY }, klass, value);
+ parseOptionalProperty<Function<std::array<float, 2>>>("icon-translate", Key::IconTranslate, klass, value);
parseOptionalProperty<PropertyTransition>("icon-translate-transition", Key::IconTranslate, klass, value);
- parseOptionalProperty<TranslateAnchorType>("icon-translate-anchor", Key::IconTranslateAnchor, klass, value);
+ parseOptionalProperty<Function<TranslateAnchorType>>("icon-translate-anchor", Key::IconTranslateAnchor, klass, value);
parseOptionalProperty<Function<float>>("text-opacity", Key::TextOpacity, klass, value);
parseOptionalProperty<PropertyTransition>("text-opacity-transition", Key::TextOpacity, klass, value);
@@ -704,15 +801,16 @@ void StyleParser::parsePaint(JSVal value, ClassProperties &klass) {
parseOptionalProperty<PropertyTransition>("text-halo-width-transition", Key::TextHaloWidth, klass, value);
parseOptionalProperty<Function<float>>("text-halo-blur", Key::TextHaloBlur, klass, value);
parseOptionalProperty<PropertyTransition>("text-halo-blur-transition", Key::TextHaloBlur, klass, value);
- parseOptionalProperty<Function<float>>("text-translate", { Key::TextTranslateX, Key::TextTranslateY }, klass, value);
+ parseOptionalProperty<Function<std::array<float, 2>>>("text-translate", Key::TextTranslate, klass, value);
parseOptionalProperty<PropertyTransition>("text-translate-transition", Key::TextTranslate, klass, value);
- parseOptionalProperty<TranslateAnchorType>("text-translate-anchor", Key::TextTranslateAnchor, klass, value);
+ parseOptionalProperty<Function<TranslateAnchorType>>("text-translate-anchor", Key::TextTranslateAnchor, klass, value);
parseOptionalProperty<Function<float>>("raster-opacity", Key::RasterOpacity, klass, value);
parseOptionalProperty<PropertyTransition>("raster-opacity-transition", Key::RasterOpacity, klass, value);
parseOptionalProperty<Function<float>>("raster-hue-rotate", Key::RasterHueRotate, klass, value);
parseOptionalProperty<PropertyTransition>("raster-hue-rotate-transition", Key::RasterHueRotate, klass, value);
- parseOptionalProperty<Function<float>>("raster-brightness", { Key::RasterBrightnessLow, Key::RasterBrightnessHigh }, klass, value);
+ parseOptionalProperty<Function<float>>("raster-brightness-min", Key::RasterBrightnessLow, klass, value);
+ parseOptionalProperty<Function<float>>("raster-brightness-max", Key::RasterBrightnessHigh, klass, value);
parseOptionalProperty<PropertyTransition>("raster-brightness-transition", Key::RasterBrightness, klass, value);
parseOptionalProperty<Function<float>>("raster-saturation", Key::RasterSaturation, klass, value);
parseOptionalProperty<PropertyTransition>("raster-saturation-transition", Key::RasterSaturation, klass, value);
@@ -723,7 +821,7 @@ void StyleParser::parsePaint(JSVal value, ClassProperties &klass) {
parseOptionalProperty<Function<float>>("background-opacity", Key::BackgroundOpacity, klass, value);
parseOptionalProperty<Function<Color>>("background-color", Key::BackgroundColor, klass, value);
- parseOptionalProperty<std::string>("background-image", Key::BackgroundImage, klass, value);
+ parseOptionalProperty<PiecewiseConstantFunction<Faded<std::string>>>("background-image", Key::BackgroundImage, klass, value, "background-image-transition");
}
void StyleParser::parseLayout(JSVal value, util::ptr<StyleBucket> &bucket) {
@@ -731,42 +829,42 @@ void StyleParser::parseLayout(JSVal value, util::ptr<StyleBucket> &bucket) {
parseVisibility<VisibilityType>(*bucket, value);
- parseOptionalProperty<CapType>("line-cap", Key::LineCap, bucket->layout, value);
- parseOptionalProperty<JoinType>("line-join", Key::LineJoin, bucket->layout, value);
+ parseOptionalProperty<Function<CapType>>("line-cap", Key::LineCap, bucket->layout, value);
+ parseOptionalProperty<Function<JoinType>>("line-join", Key::LineJoin, bucket->layout, value);
parseOptionalProperty<Function<float>>("line-miter-limit", Key::LineMiterLimit, bucket->layout, value);
parseOptionalProperty<Function<float>>("line-round-limit", Key::LineRoundLimit, bucket->layout, value);
- parseOptionalProperty<PlacementType>("symbol-placement", Key::SymbolPlacement, bucket->layout, value);
+ parseOptionalProperty<Function<PlacementType>>("symbol-placement", Key::SymbolPlacement, bucket->layout, value);
parseOptionalProperty<Function<float>>("symbol-min-distance", Key::SymbolMinDistance, bucket->layout, value);
- parseOptionalProperty<bool>("symbol-avoid-edges", Key::SymbolAvoidEdges, bucket->layout, value);
- parseOptionalProperty<bool>("icon-allow-overlap", Key::IconAllowOverlap, bucket->layout, value);
- parseOptionalProperty<bool>("icon-ignore-placement", Key::IconIgnorePlacement, bucket->layout, value);
- parseOptionalProperty<bool>("icon-optional", Key::IconOptional, bucket->layout, value);
- parseOptionalProperty<RotationAlignmentType>("icon-rotation-alignment", Key::IconRotationAlignment, bucket->layout, value);
+ parseOptionalProperty<Function<bool>>("symbol-avoid-edges", Key::SymbolAvoidEdges, bucket->layout, value);
+ parseOptionalProperty<Function<bool>>("icon-allow-overlap", Key::IconAllowOverlap, bucket->layout, value);
+ parseOptionalProperty<Function<bool>>("icon-ignore-placement", Key::IconIgnorePlacement, bucket->layout, value);
+ parseOptionalProperty<Function<bool>>("icon-optional", Key::IconOptional, bucket->layout, value);
+ parseOptionalProperty<Function<RotationAlignmentType>>("icon-rotation-alignment", Key::IconRotationAlignment, bucket->layout, value);
parseOptionalProperty<Function<float>>("icon-max-size", Key::IconMaxSize, bucket->layout, value);
- parseOptionalProperty<std::string>("icon-image", Key::IconImage, bucket->layout, value);
+ parseOptionalProperty<Function<std::string>>("icon-image", Key::IconImage, bucket->layout, value);
parseOptionalProperty<Function<float>>("icon-rotate", Key::IconRotate, bucket->layout, value);
parseOptionalProperty<Function<float>>("icon-padding", Key::IconPadding, bucket->layout, value);
- parseOptionalProperty<bool>("icon-keep-upright", Key::IconKeepUpright, bucket->layout, value);
- parseOptionalProperty<Function<float>>("icon-offset", { Key::IconOffsetX, Key::IconOffsetY }, bucket->layout, value);
- parseOptionalProperty<RotationAlignmentType>("text-rotation-alignment", Key::TextRotationAlignment, bucket->layout, value);
- parseOptionalProperty<std::string>("text-field", Key::TextField, bucket->layout, value);
- parseOptionalProperty<std::string>("text-font", Key::TextFont, bucket->layout, value);
+ parseOptionalProperty<Function<bool>>("icon-keep-upright", Key::IconKeepUpright, bucket->layout, value);
+ parseOptionalProperty<Function<std::array<float, 2>>>("icon-offset", Key::IconOffset, bucket->layout, value);
+ parseOptionalProperty<Function<RotationAlignmentType>>("text-rotation-alignment", Key::TextRotationAlignment, bucket->layout, value);
+ parseOptionalProperty<Function<std::string>>("text-field", Key::TextField, bucket->layout, value);
+ parseOptionalProperty<Function<std::string>>("text-font", Key::TextFont, bucket->layout, value);
parseOptionalProperty<Function<float>>("text-max-size", Key::TextMaxSize, bucket->layout, value);
parseOptionalProperty<Function<float>>("text-max-width", Key::TextMaxWidth, bucket->layout, value);
parseOptionalProperty<Function<float>>("text-line-height", Key::TextLineHeight, bucket->layout, value);
parseOptionalProperty<Function<float>>("text-letter-spacing", Key::TextLetterSpacing, bucket->layout, value);
- parseOptionalProperty<TextJustifyType>("text-justify", Key::TextJustify, bucket->layout, value);
- parseOptionalProperty<TextAnchorType>("text-anchor", Key::TextAnchor, bucket->layout, value);
+ parseOptionalProperty<Function<TextJustifyType>>("text-justify", Key::TextJustify, bucket->layout, value);
+ parseOptionalProperty<Function<TextAnchorType>>("text-anchor", Key::TextAnchor, bucket->layout, value);
parseOptionalProperty<Function<float>>("text-max-angle", Key::TextMaxAngle, bucket->layout, value);
parseOptionalProperty<Function<float>>("text-rotate", Key::TextRotate, bucket->layout, value);
parseOptionalProperty<Function<float>>("text-padding", Key::TextPadding, bucket->layout, value);
- parseOptionalProperty<bool>("text-keep-upright", Key::TextKeepUpright, bucket->layout, value);
- parseOptionalProperty<TextTransformType>("text-transform", Key::TextTransform, bucket->layout, value);
- parseOptionalProperty<Function<float>>("text-offset", { Key::TextOffsetX, Key::TextOffsetY }, bucket->layout, value);
- parseOptionalProperty<bool>("text-allow-overlap", Key::TextAllowOverlap, bucket->layout, value);
- parseOptionalProperty<bool>("text-ignore-placement", Key::TextIgnorePlacement, bucket->layout, value);
- parseOptionalProperty<bool>("text-optional", Key::TextOptional, bucket->layout, value);
+ parseOptionalProperty<Function<bool>>("text-keep-upright", Key::TextKeepUpright, bucket->layout, value);
+ parseOptionalProperty<Function<TextTransformType>>("text-transform", Key::TextTransform, bucket->layout, value);
+ parseOptionalProperty<Function<std::array<float, 2>>>("text-offset", Key::TextOffset, bucket->layout, value);
+ parseOptionalProperty<Function<bool>>("text-allow-overlap", Key::TextAllowOverlap, bucket->layout, value);
+ parseOptionalProperty<Function<bool>>("text-ignore-placement", Key::TextIgnorePlacement, bucket->layout, value);
+ parseOptionalProperty<Function<bool>>("text-optional", Key::TextOptional, bucket->layout, value);
}
diff --git a/src/mbgl/style/style_parser.hpp b/src/mbgl/style/style_parser.hpp
index 24fd4292a0..736cb9e9fa 100644
--- a/src/mbgl/style/style_parser.hpp
+++ b/src/mbgl/style/style_parser.hpp
@@ -69,17 +69,25 @@ private:
template <typename T>
bool parseOptionalProperty(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value);
template <typename T>
- bool parseOptionalProperty(const char *property_name, const std::vector<PropertyKey> &keys, ClassProperties &klass, JSVal value);
+ bool parseOptionalProperty(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value, const char *transition_name);
template <typename T>
bool setProperty(JSVal value, const char *property_name, PropertyKey key, ClassProperties &klass);
+ template <typename T>
+ bool setProperty(JSVal value, const char *property_name, PropertyKey key, ClassProperties &klass, JSVal transition);
template <typename T>
std::tuple<bool, T> parseProperty(JSVal value, const char *property_name);
+ template <typename T>
+ std::tuple<bool, T> parseProperty(JSVal value, const char *property_name, JSVal transition);
template <typename T>
- std::tuple<bool, Function<T>> parseFunction(JSVal value);
+ std::tuple<bool, Function<T>> parseFunction(JSVal value, const char *);
template <typename T>
- T parseFunctionArgument(JSVal value);
+ std::tuple<bool, PiecewiseConstantFunction<T>> parsePiecewiseConstantFunction(JSVal value, std::chrono::steady_clock::duration duration);
+ template <typename T>
+ std::tuple<bool, std::vector<std::pair<float, T>>> parseStops(JSVal value, const char *property_name);
+
+ std::tuple<bool,std::vector<float>> parseFloatArray(JSVal value);
FilterExpression parseFilter(JSVal);
diff --git a/src/mbgl/style/style_properties.hpp b/src/mbgl/style/style_properties.hpp
index c5149b2d1c..f50722542d 100644
--- a/src/mbgl/style/style_properties.hpp
+++ b/src/mbgl/style/style_properties.hpp
@@ -3,7 +3,7 @@
#include <mbgl/util/variant.hpp>
#include <mbgl/style/types.hpp>
-#include <mbgl/style/function_properties.hpp>
+#include <mbgl/style/piecewisefunction_properties.hpp>
#include <array>
#include <string>
@@ -21,7 +21,7 @@ struct FillProperties {
Color stroke_color = {{ 0, 0, 0, -1 }};
std::array<float, 2> translate = {{ 0, 0 }};
TranslateAnchorType translateAnchor = TranslateAnchorType::Map;
- std::string image;
+ Faded<std::string> image;
inline bool isVisible() const {
return opacity > 0 && (fill_color[3] > 0 || stroke_color[3] > 0);
@@ -37,9 +37,9 @@ struct LineProperties {
float width = 1;
float gap_width = 0;
float blur = 0;
- std::vector<float> dash_array;
+ Faded<std::vector<float>> dash_array;
float dash_line_width = 1;
- std::string image;
+ Faded<std::string> image;
inline bool isVisible() const {
return opacity > 0 && color[3] > 0 && width > 0;
@@ -96,7 +96,7 @@ struct BackgroundProperties {
inline BackgroundProperties() {}
float opacity = 1.0f;
Color color = {{ 0, 0, 0, 1 }};
- std::string image;
+ Faded<std::string> image;
};
typedef mapbox::util::variant<
diff --git a/src/mbgl/style/types.hpp b/src/mbgl/style/types.hpp
index 6149270170..78938a2823 100644
--- a/src/mbgl/style/types.hpp
+++ b/src/mbgl/style/types.hpp
@@ -11,6 +11,16 @@ namespace mbgl {
// Stores a premultiplied color, with all four channels ranging from 0..1
typedef std::array<float, 4> Color;
+
+template <typename T>
+struct Faded {
+ T from;
+ float fromScale;
+ T to;
+ float toScale;
+ float t;
+};
+
// -------------------------------------------------------------------------------------------------
enum class StyleLayerType : uint8_t {
diff --git a/src/mbgl/style/zoom_history.hpp b/src/mbgl/style/zoom_history.hpp
new file mode 100644
index 0000000000..2488687108
--- /dev/null
+++ b/src/mbgl/style/zoom_history.hpp
@@ -0,0 +1,38 @@
+#ifndef MBGL_STYLE_ZOOM_HISTORY
+#define MBGL_STYLE_ZOOM_HISTORY
+
+#include <chrono>
+#include <cmath>
+
+namespace mbgl {
+
+struct ZoomHistory {
+ float lastZoom;
+ float lastIntegerZoom;
+ std::chrono::steady_clock::time_point lastIntegerZoomTime;
+ bool first = true;
+
+ void update(float z, std::chrono::steady_clock::time_point now) {
+ if (first) {
+ first = false;
+
+ lastIntegerZoom = std::floor(z);
+ lastIntegerZoomTime = std::chrono::steady_clock::time_point(std::chrono::steady_clock::duration(0));
+ lastZoom = z;
+ }
+
+ if (std::floor(lastZoom) < std::floor(z)) {
+ lastIntegerZoom = std::floor(z);
+ lastIntegerZoomTime = now;
+
+ } else if (std::floor(lastZoom) > std::floor(z)) {
+ lastIntegerZoom = std::floor(z + 1);
+ lastIntegerZoomTime = now;
+ }
+
+ lastZoom = z;
+ }
+};
+}
+
+#endif
diff --git a/src/mbgl/util/interpolate.hpp b/src/mbgl/util/interpolate.hpp
index 952d7b9c10..55b1e2add4 100644
--- a/src/mbgl/util/interpolate.hpp
+++ b/src/mbgl/util/interpolate.hpp
@@ -4,6 +4,8 @@
#include <array>
#include <vector>
+#include <mbgl/style/types.hpp>
+
namespace mbgl {
namespace util {
@@ -22,10 +24,30 @@ inline std::array<T, 4> interpolate(const std::array<T, 4>& a, const std::array<
}};
}
-inline std::vector<float> interpolate(const std::vector<float> &a, const std::vector<float>, const double) {
- return a;
+template <typename T>
+inline std::array<T, 2> interpolate(const std::array<T, 2>& a, const std::array<T, 2>& b, const double t) {
+ return {{
+ interpolate(a[0], b[0], t),
+ interpolate(a[1], b[1], t)
+ }};
}
+// fake interpolations that just return the first value
+template<> inline bool interpolate(const bool a, const bool, const double) { return a; }
+template<> inline std::vector<float> interpolate(const std::vector<float> a, const std::vector<float>, const double) { return a; }
+template<> inline std::string interpolate(const std::string a, const std::string, const double) { return a; }
+template<> inline TranslateAnchorType interpolate(const TranslateAnchorType a, const TranslateAnchorType, const double) { return a; }
+template<> inline RotateAnchorType interpolate(const RotateAnchorType a, const RotateAnchorType, const double) { return a; }
+template<> inline CapType interpolate(const CapType a, const CapType, const double) { return a; }
+template<> inline JoinType interpolate(const JoinType a, const JoinType, const double) { return a; }
+template<> inline PlacementType interpolate(const PlacementType a, const PlacementType, const double) { return a; }
+template<> inline TextAnchorType interpolate(const TextAnchorType a, const TextAnchorType, const double) { return a; }
+template<> inline TextJustifyType interpolate(const TextJustifyType a, const TextJustifyType, const double) { return a; }
+template<> inline TextTransformType interpolate(const TextTransformType a, const TextTransformType, const double) { return a; }
+template<> inline RotationAlignmentType interpolate(const RotationAlignmentType a, const RotationAlignmentType, const double) { return a; }
+
+
+
}
}
diff --git a/styles b/styles
-Subproject 25b1b7dff37a18151e3286144bc8013b432a886
+Subproject 9b582e2adc9ed852bf1a9a26b0e10d43389e861
diff --git a/test/fixtures/style_parser/function-string-bool-enum.info.json b/test/fixtures/style_parser/function-string-bool-enum.info.json
new file mode 100644
index 0000000000..9c25a2f488
--- /dev/null
+++ b/test/fixtures/style_parser/function-string-bool-enum.info.json
@@ -0,0 +1,6 @@
+{
+ "default": {
+ "log": [
+ ]
+ }
+}
diff --git a/test/fixtures/style_parser/function-string-bool-enum.style.json b/test/fixtures/style_parser/function-string-bool-enum.style.json
new file mode 100644
index 0000000000..c38f195259
--- /dev/null
+++ b/test/fixtures/style_parser/function-string-bool-enum.style.json
@@ -0,0 +1,43 @@
+{
+ "version": 6,
+ "sources": {
+ "mapbox": {
+ "type": "vector",
+ "url": "mapbox://mapbox.mapbox-terrain-v1,mapbox.mapbox-streets-v5",
+ "maxzoom": 14
+ }
+ },
+ "layers": [{
+ "id": "waterway_river_canal",
+ "type": "line",
+ "source": "mapbox",
+ "source-layer": "waterway",
+ "filter": ["in", "type", "river", "canal"],
+ "layout": {
+ "line-cap": {
+ "stops": [[0, "round"], [10, "round"]]
+ }
+ },
+ "paint": {
+ "line-translate-anchor": {
+ "stops": [[0, "map"], [10, "viewport"]]
+ }
+ }
+ },
+ {
+ "id": "label",
+ "type": "symbol",
+ "source": "mapbox",
+ "source-layer": "waterway",
+ "layout": {
+ "text-font": {
+ "stops": [[0, "Open Sans Regular"]]
+ },
+ "text-keep-upright": {
+ "stops": [[0, false], [10, true]]
+ }
+ },
+ "paint": {
+ }
+ }]
+}
diff --git a/test/ios/.gitignore b/test/ios/.gitignore
new file mode 100644
index 0000000000..516812b72d
--- /dev/null
+++ b/test/ios/.gitignore
@@ -0,0 +1,3 @@
+!*.xcodeproj
+!xcshareddata
+xcuserdata
diff --git a/test/ios/App-Info.plist b/test/ios/App-Info.plist
new file mode 100644
index 0000000000..d7e9d5c462
--- /dev/null
+++ b/test/ios/App-Info.plist
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleDisplayName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.mapbox.${PRODUCT_NAME:rfc1034identifier}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+ <key>LSRequiresIPhoneOS</key>
+ <true/>
+ <key>UIRequiredDeviceCapabilities</key>
+ <array>
+ <string>armv7</string>
+ </array>
+ <key>UISupportedInterfaceOrientations</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+ <key>UISupportedInterfaceOrientations~ipad</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationPortraitUpsideDown</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+</dict>
+</plist>
diff --git a/test/ios/Bundle-Info.plist b/test/ios/Bundle-Info.plist
new file mode 100644
index 0000000000..4ea0aed89c
--- /dev/null
+++ b/test/ios/Bundle-Info.plist
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.mapbox.${PRODUCT_NAME:rfc1034identifier}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundlePackageType</key>
+ <string>BNDL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+</dict>
+</plist>
diff --git a/test/ios/KIF b/test/ios/KIF
new file mode 160000
+Subproject ab5a46ff7e970de5578df48a1e1f013bd5e1bd4
diff --git a/test/ios/KIFTestActor+MapboxGL.h b/test/ios/KIFTestActor+MapboxGL.h
new file mode 100644
index 0000000000..199091b29f
--- /dev/null
+++ b/test/ios/KIFTestActor+MapboxGL.h
@@ -0,0 +1,12 @@
+#import <KIF/KIF.h>
+
+@class MGLMapView;
+
+@interface KIFTestActor (MapboxGL)
+
+@property (nonatomic, readonly) UIWindow *window;
+@property (nonatomic, readonly) UIViewController *viewController;
+@property (nonatomic, readonly) MGLMapView *mapView;
+@property (nonatomic, readonly) UIView *compass;
+
+@end
diff --git a/test/ios/KIFTestActor+MapboxGL.m b/test/ios/KIFTestActor+MapboxGL.m
new file mode 100644
index 0000000000..b267e0c0be
--- /dev/null
+++ b/test/ios/KIFTestActor+MapboxGL.m
@@ -0,0 +1,24 @@
+#import "KIFTestActor+MapboxGL.h"
+#import <KIF/UIApplication-KIFAdditions.h>
+#import <KIF/UIAccessibilityElement-KIFAdditions.h>
+#import "MGLMapView.h"
+
+@implementation KIFTestActor (MapboxGL)
+
+- (UIWindow *)window {
+ return [[UIApplication sharedApplication] statusBarWindow];
+}
+
+- (UIViewController *)viewController {
+ return (UIViewController *)[[tester.mapView nextResponder] nextResponder];
+}
+
+- (MGLMapView *)mapView {
+ return (MGLMapView *)[tester waitForViewWithAccessibilityLabel:@"Map"];
+}
+
+- (UIView *)compass {
+ return [tester waitForViewWithAccessibilityLabel:@"Compass"];
+}
+
+@end
diff --git a/test/ios/MGLTAppDelegate.h b/test/ios/MGLTAppDelegate.h
new file mode 100644
index 0000000000..e5c459beb0
--- /dev/null
+++ b/test/ios/MGLTAppDelegate.h
@@ -0,0 +1,7 @@
+#import <UIKit/UIKit.h>
+
+@interface MGLTAppDelegate : UIResponder <UIApplicationDelegate>
+
+@property (strong, nonatomic) UIWindow *window;
+
+@end
diff --git a/test/ios/MGLTAppDelegate.m b/test/ios/MGLTAppDelegate.m
new file mode 100644
index 0000000000..4bd6b64882
--- /dev/null
+++ b/test/ios/MGLTAppDelegate.m
@@ -0,0 +1,18 @@
+#import "MGLTAppDelegate.h"
+#import "MGLTViewController.h"
+
+@implementation MGLTAppDelegate
+
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
+{
+ self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
+ UINavigationController *wrapper = [[UINavigationController alloc] initWithRootViewController:[MGLTViewController new]];
+ self.window.rootViewController = wrapper;
+ wrapper.navigationBarHidden = YES;
+ wrapper.toolbarHidden = YES;
+ [self.window makeKeyAndVisible];
+
+ return YES;
+}
+
+@end
diff --git a/test/ios/MGLTViewController.h b/test/ios/MGLTViewController.h
new file mode 100644
index 0000000000..39df59bb5e
--- /dev/null
+++ b/test/ios/MGLTViewController.h
@@ -0,0 +1,5 @@
+#import <UIKit/UIKit.h>
+
+@interface MGLTViewController : UIViewController
+
+@end
diff --git a/test/ios/MGLTViewController.m b/test/ios/MGLTViewController.m
new file mode 100644
index 0000000000..9caa64c79a
--- /dev/null
+++ b/test/ios/MGLTViewController.m
@@ -0,0 +1,18 @@
+#import "MGLTViewController.h"
+#import "MGLMapView.h"
+
+@implementation MGLTViewController
+
+- (void)viewDidLoad
+{
+ [super viewDidLoad];
+
+ MGLMapView *mapView = [[MGLMapView alloc] initWithFrame:self.view.bounds
+ accessToken:@"pk.eyJ1IjoianVzdGluIiwiYSI6Ik9RX3RRQzAifQ.dmOg_BAp1ywuDZMM7YsXRg"];
+ mapView.viewControllerForLayoutGuides = self;
+ mapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+
+ [self.view addSubview:mapView];
+}
+
+@end
diff --git a/test/ios/MapViewTests.h b/test/ios/MapViewTests.h
new file mode 100644
index 0000000000..5057ffd641
--- /dev/null
+++ b/test/ios/MapViewTests.h
@@ -0,0 +1,5 @@
+#import <KIF/KIF.h>
+
+@interface MapViewTests : KIFTestCase
+
+@end
diff --git a/test/ios/MapViewTests.m b/test/ios/MapViewTests.m
new file mode 100644
index 0000000000..adf3aeba06
--- /dev/null
+++ b/test/ios/MapViewTests.m
@@ -0,0 +1,353 @@
+#import "MapViewTests.h"
+#import <KIF/KIFTestStepValidation.h>
+#import "KIFTestActor+MapboxGL.h"
+#import "MGLMapView.h"
+
+@interface MapViewTests () <MGLMapViewDelegate>
+
+@end
+
+@implementation MapViewTests
+
+- (void)beforeEach {
+ [system simulateDeviceRotationToOrientation:UIDeviceOrientationPortrait];
+ tester.mapView.viewControllerForLayoutGuides = tester.viewController;
+ tester.mapView.centerCoordinate = CLLocationCoordinate2DMake(38.913175, -77.032458);
+ tester.mapView.zoomLevel = 14;
+ tester.mapView.direction = 0;
+ tester.mapView.zoomEnabled = YES;
+ tester.mapView.scrollEnabled = YES;
+ tester.mapView.rotateEnabled = YES;
+ tester.viewController.navigationController.navigationBarHidden = YES;
+ tester.viewController.navigationController.toolbarHidden = YES;
+ tester.mapView.delegate = self;
+}
+
+- (void)testDirectionSet {
+ tester.mapView.direction = 270;
+ __KIFAssertEqual(tester.mapView.direction,
+ 270,
+ @"setting direction should take effect");
+
+ [tester waitForTimeInterval:1];
+
+ __KIFAssertEqual(tester.compass.alpha,
+ 1,
+ @"compass should be visible when map is rotated");
+ __KIFAssertEqualObjects([NSValue valueWithCGAffineTransform:tester.compass.transform],
+ [NSValue valueWithCGAffineTransform:CGAffineTransformMakeRotation(M_PI * 1.5)],
+ @"compass rotation should indicate map rotation");
+}
+
+- (void)testCompassTap {
+ tester.mapView.direction = 180;
+ __KIFAssertEqual(tester.mapView.direction,
+ 180,
+ @"setting direction should take effect");
+
+ [tester waitForTimeInterval:1];
+
+ [tester.compass tap];
+
+ [tester waitForTimeInterval:1];
+
+ __KIFAssertEqual(tester.mapView.direction,
+ 0,
+ @"tapping compass should reset map direction");
+ __KIFAssertEqual(tester.compass.alpha,
+ 0,
+ @"compass should not be visible when map is unrotated");
+ __KIFAssertEqualObjects([NSValue valueWithCGAffineTransform:tester.compass.transform],
+ [NSValue valueWithCGAffineTransform:CGAffineTransformIdentity],
+ @"compass rotation should indicate map rotation");
+}
+
+- (void)testDirectionReset {
+ tester.mapView.direction = 100;
+ __KIFAssertEqual(tester.mapView.direction,
+ 100,
+ @"setting direction should take effect");
+
+ [tester.mapView resetNorth];
+
+ [tester waitForTimeInterval:1];
+
+ __KIFAssertEqual(tester.mapView.direction,
+ 0,
+ @"resetting north should reset map direction");
+ __KIFAssertEqual(tester.compass.alpha,
+ 0,
+ @"compass should not be visible when map is unrotated");
+ __KIFAssertEqualObjects([NSValue valueWithCGAffineTransform:tester.compass.transform],
+ [NSValue valueWithCGAffineTransform:CGAffineTransformIdentity],
+ @"compass rotation should indicate map rotation");
+}
+
+- (void)testZoom {
+ double zoom = tester.mapView.zoomLevel;
+
+ [tester.mapView zoomAtPoint:CGPointMake(tester.mapView.bounds.size.width / 2,
+ tester.mapView.bounds.size.height / 2)
+ distance:50
+ steps:10];
+
+ XCTAssertTrue(tester.mapView.zoomLevel > zoom,
+ @"zoom gesture should increase zoom level");
+
+ zoom = tester.mapView.zoomLevel;
+ [tester.mapView pinchAtPoint:CGPointMake(tester.mapView.bounds.size.width / 2,
+ tester.mapView.bounds.size.height / 2)
+ distance:50
+ steps:10];
+
+ XCTAssertTrue(tester.mapView.zoomLevel < zoom,
+ @"pinch gesture should decrease zoom level");
+}
+
+- (void)testZoomDisabled {
+ tester.mapView.zoomEnabled = NO;
+ double zoom = tester.mapView.zoomLevel;
+
+ [tester.mapView zoomAtPoint:CGPointMake(tester.mapView.bounds.size.width / 2,
+ tester.mapView.bounds.size.height / 2)
+ distance:50
+ steps:10];
+
+ __KIFAssertEqual(tester.mapView.zoomLevel,
+ zoom,
+ @"disabling zoom gesture should disallow zooming");
+
+ [tester.mapView pinchAtPoint:CGPointMake(tester.mapView.bounds.size.width / 2,
+ tester.mapView.bounds.size.height / 2)
+ distance:50
+ steps:10];
+
+ __KIFAssertEqual(tester.mapView.zoomLevel,
+ zoom,
+ @"disabling zoom gesture should disallow pinching");
+}
+
+- (void)testPan {
+ CLLocationCoordinate2D centerCoordinate = tester.mapView.centerCoordinate;
+
+ [tester.mapView dragFromPoint:CGPointMake(10, 10) toPoint:CGPointMake(300, 300) steps:10];
+
+ XCTAssertTrue(tester.mapView.centerCoordinate.latitude > centerCoordinate.latitude,
+ @"panning map down should increase center latitude");
+ XCTAssertTrue(tester.mapView.centerCoordinate.longitude < centerCoordinate.longitude,
+ @"panning map right should decrease center longitude");
+}
+
+- (void)testPanDisabled {
+ tester.mapView.scrollEnabled = NO;
+ CLLocationCoordinate2D centerCoordinate = tester.mapView.centerCoordinate;
+
+ [tester.mapView dragFromPoint:CGPointMake(10, 10) toPoint:CGPointMake(300, 300) steps:10];
+
+ __KIFAssertEqual(centerCoordinate.latitude,
+ tester.mapView.centerCoordinate.latitude,
+ @"disabling pan gesture should disallow vertical panning");
+ __KIFAssertEqual(centerCoordinate.longitude,
+ tester.mapView.centerCoordinate.longitude,
+ @"disabling pan gesture should disallow horizontal panning");
+}
+
+- (void)testCenterSet {
+ CLLocationCoordinate2D newCenterCoordinate = CLLocationCoordinate2DMake(45.23237263, -122.23287129);
+ XCTAssertNotEqual(tester.mapView.centerCoordinate.latitude,
+ newCenterCoordinate.latitude,
+ @"initial setup should have differing center latitude");
+ XCTAssertNotEqual(tester.mapView.centerCoordinate.longitude,
+ newCenterCoordinate.longitude,
+ @"initial setup should have differing center longitude");
+
+ [tester.mapView setCenterCoordinate:newCenterCoordinate];
+
+ XCTAssertTrue(tester.mapView.centerCoordinate.latitude == newCenterCoordinate.latitude,
+ @"setting center should change latitude");
+ XCTAssertTrue(tester.mapView.centerCoordinate.longitude == newCenterCoordinate.longitude,
+ @"setting center should change longitude");
+}
+
+- (void)testZoomSet {
+ double newZoom = 11.65;
+ XCTAssertNotEqual(tester.mapView.zoomLevel,
+ newZoom,
+ @"initial setup should have differing zoom");
+
+ tester.mapView.zoomLevel = newZoom;
+
+ __KIFAssertEqual(tester.mapView.zoomLevel,
+ newZoom,
+ @"setting zoom should take effect");
+}
+
+- (void)testTopLayoutGuide {
+ CGRect statusBarFrame, navigationBarFrame, compassFrame;
+ UINavigationBar *navigationBar = tester.viewController.navigationController.navigationBar;
+
+ compassFrame = [tester.compass.superview convertRect:tester.compass.frame toView:nil];
+ statusBarFrame = [tester.window convertRect:[[UIApplication sharedApplication] statusBarFrame] toView:nil];
+ XCTAssertFalse(CGRectIntersectsRect(compassFrame, statusBarFrame),
+ @"compass should not be under status bar");
+
+ tester.viewController.navigationController.navigationBarHidden = NO;
+ compassFrame = [tester.compass.superview convertRect:tester.compass.frame toView:nil];
+ navigationBarFrame = [tester.window convertRect:navigationBar.frame toView:nil];
+ XCTAssertFalse(CGRectIntersectsRect(compassFrame, navigationBarFrame),
+ @"compass should not be under navigation bar");
+
+ [system simulateDeviceRotationToOrientation:UIDeviceOrientationLandscapeLeft];
+
+ compassFrame = [tester.compass.superview convertRect:tester.compass.frame toView:nil];
+ navigationBarFrame = [tester.window convertRect:navigationBar.frame toView:nil];
+ XCTAssertFalse(CGRectIntersectsRect(compassFrame, navigationBarFrame),
+ @"rotated device should not have compass under navigation bar");
+
+ tester.viewController.navigationController.navigationBarHidden = YES;
+ compassFrame = [tester.compass.superview convertRect:tester.compass.frame toView:nil];
+ statusBarFrame = [tester.window convertRect:[[UIApplication sharedApplication] statusBarFrame] toView:nil];
+ XCTAssertFalse(CGRectIntersectsRect(compassFrame, statusBarFrame),
+ @"rotated device should not have compass under status bar");
+}
+
+- (void)testBottomLayoutGuide {
+ CGRect logoBugFrame, toolbarFrame, attributionButtonFrame;
+ UIView *logoBug = (UIView *)[tester waitForViewWithAccessibilityLabel:@"Mapbox logo"];
+ UIToolbar *toolbar = tester.viewController.navigationController.toolbar;
+ UIView *attributionButton = (UIView *)[tester waitForViewWithAccessibilityLabel:@"Attribution info"];
+
+ tester.viewController.navigationController.toolbarHidden = NO;
+
+ logoBugFrame = [logoBug.superview convertRect:logoBug.frame toView:nil];
+ toolbarFrame = [tester.window convertRect:toolbar.frame toView:nil];
+ XCTAssertFalse(CGRectIntersectsRect(logoBugFrame, toolbarFrame),
+ @"logo bug should not be under toolbar");
+
+ attributionButtonFrame = [attributionButton.superview convertRect:attributionButton.frame toView:nil];
+ XCTAssertFalse(CGRectIntersectsRect(attributionButtonFrame, toolbarFrame),
+ @"attribution button should not be under toolbar");
+
+ [system simulateDeviceRotationToOrientation:UIDeviceOrientationLandscapeRight];
+
+ logoBugFrame = [logoBug.superview convertRect:logoBug.frame toView:nil];
+ toolbarFrame = [tester.window convertRect:toolbar.frame toView:nil];
+ XCTAssertFalse(CGRectIntersectsRect(logoBugFrame, toolbarFrame),
+ @"rotated device should not have logo buy under toolbar");
+
+ attributionButtonFrame = [attributionButton.superview convertRect:attributionButton.frame toView:nil];
+ XCTAssertFalse(CGRectIntersectsRect(attributionButtonFrame, toolbarFrame),
+ @"rotated device should not have attribution button under toolbar");
+}
+
+- (void)testDelegateRegionWillChange {
+ __block NSUInteger unanimatedCount = 0;
+ __block NSUInteger animatedCount = 0;
+ [[NSNotificationCenter defaultCenter] addObserverForName:@"regionWillChangeAnimated"
+ object:tester.mapView
+ queue:[NSOperationQueue mainQueue]
+ usingBlock:^(NSNotification *note) {
+ if ([note.userInfo[@"animated"] boolValue]) {
+ animatedCount++;
+ } else {
+ unanimatedCount++;
+ }
+ }];
+
+ NSNotification *notification = [system waitForNotificationName:@"regionWillChangeAnimated"
+ object:tester.mapView
+ whileExecutingBlock:^{
+ tester.mapView.centerCoordinate = CLLocationCoordinate2DMake(0, 0);
+ }];
+ [tester waitForTimeInterval:1];
+ XCTAssertNotNil(notification,
+ @"regionWillChange delegate should produce a notification");
+ __KIFAssertEqual([notification.userInfo[@"animated"] boolValue],
+ NO,
+ @"regionWillChange delegate should not indicate animated change");
+ __KIFAssertEqual(unanimatedCount,
+ 1,
+ @"regionWillChange delegate should indicate one unanimated change");
+
+ notification = [system waitForNotificationName:@"regionWillChangeAnimated"
+ object:tester.mapView
+ whileExecutingBlock:^{
+ [tester.mapView setCenterCoordinate:CLLocationCoordinate2DMake(45, 100) animated:YES];
+ }];
+ [tester waitForTimeInterval:1];
+ XCTAssertNotNil(notification,
+ @"regionWillChange delegate should produce a notification");
+ __KIFAssertEqual([notification.userInfo[@"animated"] boolValue],
+ YES,
+ @"regionWillChange delegate should indicate an animated change");
+ __KIFAssertEqual(animatedCount,
+ 1,
+ @"regionWillChange delegate should indicate one animated change");
+
+ [[NSNotificationCenter defaultCenter] removeObserver:self
+ name:@"regionWillChangeAnimated"
+ object:tester.mapView];
+}
+
+- (void)mapView:(MGLMapView *)mapView regionWillChangeAnimated:(BOOL)animated {
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"regionWillChangeAnimated"
+ object:mapView
+ userInfo:@{ @"animated" : @(animated) }];
+}
+
+- (void)testDelegateRegionDidChange {
+ __block NSUInteger unanimatedCount = 0;
+ __block NSUInteger animatedCount = 0;
+ [[NSNotificationCenter defaultCenter] addObserverForName:@"regionDidChangeAnimated"
+ object:tester.mapView
+ queue:[NSOperationQueue mainQueue]
+ usingBlock:^(NSNotification *note) {
+ if ([note.userInfo[@"animated"] boolValue]) {
+ animatedCount++;
+ } else {
+ unanimatedCount++;
+ }
+ }];
+
+ NSNotification *notification = [system waitForNotificationName:@"regionDidChangeAnimated"
+ object:tester.mapView
+ whileExecutingBlock:^{
+ tester.mapView.centerCoordinate = CLLocationCoordinate2DMake(0, 0);
+ }];
+ [tester waitForTimeInterval:1];
+ XCTAssertNotNil(notification,
+ @"regionDidChange delegate should produce a notification");
+ __KIFAssertEqual([notification.userInfo[@"animated"] boolValue],
+ NO,
+ @"regionDidChange delegate should not indicate animated change");
+ __KIFAssertEqual(unanimatedCount,
+ 1,
+ @"regionDidChange delegate should indicate one unanimated change");
+
+ notification = [system waitForNotificationName:@"regionDidChangeAnimated"
+ object:tester.mapView
+ whileExecutingBlock:^{
+ [tester.mapView setCenterCoordinate:CLLocationCoordinate2DMake(45, 100) animated:YES];
+ }];
+ [tester waitForTimeInterval:1];
+ XCTAssertNotNil(notification,
+ @"regionDidChange delegate should produce a notification");
+ __KIFAssertEqual([notification.userInfo[@"animated"] boolValue],
+ YES,
+ @"regionDidChange delegate should indicate animated change");
+ __KIFAssertEqual(animatedCount,
+ 1,
+ @"regionDidChange delegate should indicate one animated change");
+
+ [[NSNotificationCenter defaultCenter] removeObserver:self
+ name:@"regionDidChangeAnimated"
+ object:tester.mapView];
+}
+
+- (void)mapView:(MGLMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"regionDidChangeAnimated"
+ object:mapView
+ userInfo:@{ @"animated" : @(animated) }];
+}
+
+@end
diff --git a/test/ios/README.md b/test/ios/README.md
new file mode 100644
index 0000000000..21975e177e
--- /dev/null
+++ b/test/ios/README.md
@@ -0,0 +1,9 @@
+This is a harness app for integration testing of Mapbox GL Cocoa. It uses the static library build of Mapbox GL Cocoa in order to be entirely self-contained and not need the `mapbox-gl-native` upstream C++ project.
+
+To run tests, either open the enclosed `ios-tests.xcodeproj` and run the tests or use `xcodebuild test` at the command line from this directory. For example:
+
+```bash
+xcodebuild -scheme 'Mapbox GL Tests' \
+ -destination 'platform=iOS Simulator,name=iPad,OS=7.1' \
+ test
+```
diff --git a/test/ios/ios-tests.xcodeproj/project.pbxproj b/test/ios/ios-tests.xcodeproj/project.pbxproj
new file mode 100644
index 0000000000..86f1251324
--- /dev/null
+++ b/test/ios/ios-tests.xcodeproj/project.pbxproj
@@ -0,0 +1,659 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ DD043327196DB9BC00E6F39D /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD043326196DB9BC00E6F39D /* Foundation.framework */; };
+ DD043329196DB9BC00E6F39D /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD043328196DB9BC00E6F39D /* CoreGraphics.framework */; };
+ DD04332B196DB9BC00E6F39D /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD04332A196DB9BC00E6F39D /* UIKit.framework */; };
+ DD043363196DBBD500E6F39D /* MGLTAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DD04335F196DBBD500E6F39D /* MGLTAppDelegate.m */; };
+ DD043364196DBBD500E6F39D /* MGLTViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DD043360196DBBD500E6F39D /* MGLTViewController.m */; };
+ DD043366196DBBE000E6F39D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DD043365196DBBE000E6F39D /* main.m */; };
+ DD1A9EA8199BEA0D007BC651 /* libc++.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = DD1A9EA7199BEA0D007BC651 /* libc++.dylib */; };
+ DD61240819CCF06E006845B1 /* libMapboxGL.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DD61240619CCF06E006845B1 /* libMapboxGL.a */; };
+ DD61240919CCF06E006845B1 /* MapboxGL.bundle in Resources */ = {isa = PBXBuildFile; fileRef = DD61240719CCF06E006845B1 /* MapboxGL.bundle */; };
+ DD8A790D196DC0A900FAD883 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = DD8A790C196DC0A900FAD883 /* libz.dylib */; };
+ DD8A790F196DC0AD00FAD883 /* GLKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD8A790E196DC0AD00FAD883 /* GLKit.framework */; };
+ DD8A7911196DC0BB00FAD883 /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD8A7910196DC0BB00FAD883 /* CoreLocation.framework */; };
+ DD96917919F1C08400729E7D /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = DD96917819F1C08400729E7D /* libsqlite3.dylib */; };
+ DD96918119F1C09200729E7D /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD96918019F1C09200729E7D /* SystemConfiguration.framework */; };
+ DDBD0154196DC3D70033959E /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DDBD0153196DC3D70033959E /* XCTest.framework */; };
+ DDBD0155196DC3D70033959E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD043326196DB9BC00E6F39D /* Foundation.framework */; };
+ DDBD0156196DC3D70033959E /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD04332A196DB9BC00E6F39D /* UIKit.framework */; };
+ DDBD016C196DC4740033959E /* MapViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DDBD0168196DC4740033959E /* MapViewTests.m */; };
+ DDBD016D196DC4740033959E /* KIFTestActor+MapboxGL.m in Sources */ = {isa = PBXBuildFile; fileRef = DDBD016A196DC4740033959E /* KIFTestActor+MapboxGL.m */; };
+ DDBD016E196DC4A10033959E /* libKIF.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DDBD0144196DC3AE0033959E /* libKIF.a */; };
+ DDBD016F196DC4A90033959E /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD043328196DB9BC00E6F39D /* CoreGraphics.framework */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ DDBD0143196DC3AE0033959E /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = DDBD013A196DC3AE0033959E /* KIF.xcodeproj */;
+ proxyType = 2;
+ remoteGlobalIDString = EABD46AA1857A0C700A5F081;
+ remoteInfo = KIF;
+ };
+ DDBD0145196DC3AE0033959E /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = DDBD013A196DC3AE0033959E /* KIF.xcodeproj */;
+ proxyType = 2;
+ remoteGlobalIDString = EB72047C1680DDAD00278DA2;
+ remoteInfo = "KIF-OCUnit";
+ };
+ DDBD0147196DC3AE0033959E /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = DDBD013A196DC3AE0033959E /* KIF.xcodeproj */;
+ proxyType = 2;
+ remoteGlobalIDString = EB60ECC1177F8C83005A041A;
+ remoteInfo = "Test Host";
+ };
+ DDBD0149196DC3AE0033959E /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = DDBD013A196DC3AE0033959E /* KIF.xcodeproj */;
+ proxyType = 2;
+ remoteGlobalIDString = EABD46CD1857A0F300A5F081;
+ remoteInfo = "KIF Tests";
+ };
+ DDBD014B196DC3AE0033959E /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = DDBD013A196DC3AE0033959E /* KIF.xcodeproj */;
+ proxyType = 2;
+ remoteGlobalIDString = EB60ECEB177F8DB3005A041A;
+ remoteInfo = "KIF Tests-OCUnit";
+ };
+ DDBD0160196DC3D70033959E /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = DD04331B196DB9BC00E6F39D /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = DD043322196DB9BC00E6F39D;
+ remoteInfo = "Mapbox GL Tests";
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXFileReference section */
+ DD043323196DB9BC00E6F39D /* Mapbox GL Tests.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Mapbox GL Tests.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+ DD043326196DB9BC00E6F39D /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
+ DD043328196DB9BC00E6F39D /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
+ DD04332A196DB9BC00E6F39D /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
+ DD04335F196DBBD500E6F39D /* MGLTAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLTAppDelegate.m; sourceTree = SOURCE_ROOT; };
+ DD043360196DBBD500E6F39D /* MGLTViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLTViewController.m; sourceTree = SOURCE_ROOT; };
+ DD043361196DBBD500E6F39D /* MGLTViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLTViewController.h; sourceTree = SOURCE_ROOT; };
+ DD043362196DBBD500E6F39D /* MGLTAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLTAppDelegate.h; sourceTree = SOURCE_ROOT; };
+ DD043365196DBBE000E6F39D /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = SOURCE_ROOT; };
+ DD043367196DBCC200E6F39D /* App-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "App-Info.plist"; sourceTree = SOURCE_ROOT; };
+ DD1A9EA7199BEA0D007BC651 /* libc++.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libc++.dylib"; path = "usr/lib/libc++.dylib"; sourceTree = SDKROOT; };
+ DD61240019CCF06E006845B1 /* MGLMapView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MGLMapView.h; path = ../dist/static/Headers/MGLMapView.h; sourceTree = SOURCE_ROOT; };
+ DD61240119CCF06E006845B1 /* MGLStyleFunctionValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MGLStyleFunctionValue.h; path = ../dist/static/Headers/MGLStyleFunctionValue.h; sourceTree = SOURCE_ROOT; };
+ DD61240219CCF06E006845B1 /* MGLTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MGLTypes.h; path = ../dist/static/Headers/MGLTypes.h; sourceTree = SOURCE_ROOT; };
+ DD61240319CCF06E006845B1 /* NSArray+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSArray+MGLAdditions.h"; path = "../dist/static/Headers/NSArray+MGLAdditions.h"; sourceTree = SOURCE_ROOT; };
+ DD61240419CCF06E006845B1 /* NSDictionary+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSDictionary+MGLAdditions.h"; path = "../dist/static/Headers/NSDictionary+MGLAdditions.h"; sourceTree = SOURCE_ROOT; };
+ DD61240519CCF06E006845B1 /* UIColor+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIColor+MGLAdditions.h"; path = "../dist/static/Headers/UIColor+MGLAdditions.h"; sourceTree = SOURCE_ROOT; };
+ DD61240619CCF06E006845B1 /* libMapboxGL.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libMapboxGL.a; path = ../dist/static/libMapboxGL.a; sourceTree = SOURCE_ROOT; };
+ DD61240719CCF06E006845B1 /* MapboxGL.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = MapboxGL.bundle; path = ../dist/static/MapboxGL.bundle; sourceTree = SOURCE_ROOT; };
+ DD8A790C196DC0A900FAD883 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
+ DD8A790E196DC0AD00FAD883 /* GLKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GLKit.framework; path = System/Library/Frameworks/GLKit.framework; sourceTree = SDKROOT; };
+ DD8A7910196DC0BB00FAD883 /* CoreLocation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreLocation.framework; path = System/Library/Frameworks/CoreLocation.framework; sourceTree = SDKROOT; };
+ DD96917819F1C08400729E7D /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; };
+ DD96918019F1C09200729E7D /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };
+ DDBD013A196DC3AE0033959E /* KIF.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = KIF.xcodeproj; path = KIF/KIF.xcodeproj; sourceTree = SOURCE_ROOT; };
+ DDBD0152196DC3D70033959E /* Test Bundle.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Test Bundle.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
+ DDBD0153196DC3D70033959E /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
+ DDBD0165196DC4560033959E /* Bundle-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Bundle-Info.plist"; sourceTree = SOURCE_ROOT; };
+ DDBD0168196DC4740033959E /* MapViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MapViewTests.m; sourceTree = SOURCE_ROOT; };
+ DDBD0169196DC4740033959E /* MapViewTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MapViewTests.h; sourceTree = SOURCE_ROOT; };
+ DDBD016A196DC4740033959E /* KIFTestActor+MapboxGL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "KIFTestActor+MapboxGL.m"; sourceTree = SOURCE_ROOT; };
+ DDBD016B196DC4740033959E /* KIFTestActor+MapboxGL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "KIFTestActor+MapboxGL.h"; sourceTree = SOURCE_ROOT; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ DD043320196DB9BC00E6F39D /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ DD043329196DB9BC00E6F39D /* CoreGraphics.framework in Frameworks */,
+ DD8A7911196DC0BB00FAD883 /* CoreLocation.framework in Frameworks */,
+ DD043327196DB9BC00E6F39D /* Foundation.framework in Frameworks */,
+ DD8A790F196DC0AD00FAD883 /* GLKit.framework in Frameworks */,
+ DD96918119F1C09200729E7D /* SystemConfiguration.framework in Frameworks */,
+ DD04332B196DB9BC00E6F39D /* UIKit.framework in Frameworks */,
+ DD61240819CCF06E006845B1 /* libMapboxGL.a in Frameworks */,
+ DD1A9EA8199BEA0D007BC651 /* libc++.dylib in Frameworks */,
+ DD96917919F1C08400729E7D /* libsqlite3.dylib in Frameworks */,
+ DD8A790D196DC0A900FAD883 /* libz.dylib in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ DDBD014F196DC3D70033959E /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ DDBD016F196DC4A90033959E /* CoreGraphics.framework in Frameworks */,
+ DDBD0155196DC3D70033959E /* Foundation.framework in Frameworks */,
+ DDBD0156196DC3D70033959E /* UIKit.framework in Frameworks */,
+ DDBD0154196DC3D70033959E /* XCTest.framework in Frameworks */,
+ DDBD016E196DC4A10033959E /* libKIF.a in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ DD04331A196DB9BC00E6F39D = {
+ isa = PBXGroup;
+ children = (
+ DD04332C196DB9BC00E6F39D /* App */,
+ DDBD0139196DC38D0033959E /* Tests */,
+ DD043325196DB9BC00E6F39D /* Frameworks */,
+ DD043324196DB9BC00E6F39D /* Products */,
+ );
+ sourceTree = "<group>";
+ };
+ DD043324196DB9BC00E6F39D /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ DD043323196DB9BC00E6F39D /* Mapbox GL Tests.app */,
+ DDBD0152196DC3D70033959E /* Test Bundle.xctest */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ DD043325196DB9BC00E6F39D /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ DD96918019F1C09200729E7D /* SystemConfiguration.framework */,
+ DD96917819F1C08400729E7D /* libsqlite3.dylib */,
+ DD043328196DB9BC00E6F39D /* CoreGraphics.framework */,
+ DD8A7910196DC0BB00FAD883 /* CoreLocation.framework */,
+ DD043326196DB9BC00E6F39D /* Foundation.framework */,
+ DD8A790E196DC0AD00FAD883 /* GLKit.framework */,
+ DD04332A196DB9BC00E6F39D /* UIKit.framework */,
+ DDBD0153196DC3D70033959E /* XCTest.framework */,
+ DD1A9EA7199BEA0D007BC651 /* libc++.dylib */,
+ DD8A790C196DC0A900FAD883 /* libz.dylib */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+ DD04332C196DB9BC00E6F39D /* App */ = {
+ isa = PBXGroup;
+ children = (
+ DD043362196DBBD500E6F39D /* MGLTAppDelegate.h */,
+ DD04335F196DBBD500E6F39D /* MGLTAppDelegate.m */,
+ DD043361196DBBD500E6F39D /* MGLTViewController.h */,
+ DD043360196DBBD500E6F39D /* MGLTViewController.m */,
+ DD8A77AD196DBFDA00FAD883 /* GL Library */,
+ DD04332D196DB9BC00E6F39D /* Supporting Files */,
+ );
+ name = App;
+ path = "Mapbox GL Tests";
+ sourceTree = "<group>";
+ };
+ DD04332D196DB9BC00E6F39D /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+ DD043367196DBCC200E6F39D /* App-Info.plist */,
+ DD043365196DBBE000E6F39D /* main.m */,
+ );
+ name = "Supporting Files";
+ sourceTree = "<group>";
+ };
+ DD6123FF19CCF06E006845B1 /* Headers */ = {
+ isa = PBXGroup;
+ children = (
+ DD61240019CCF06E006845B1 /* MGLMapView.h */,
+ DD61240119CCF06E006845B1 /* MGLStyleFunctionValue.h */,
+ DD61240219CCF06E006845B1 /* MGLTypes.h */,
+ DD61240319CCF06E006845B1 /* NSArray+MGLAdditions.h */,
+ DD61240419CCF06E006845B1 /* NSDictionary+MGLAdditions.h */,
+ DD61240519CCF06E006845B1 /* UIColor+MGLAdditions.h */,
+ );
+ name = Headers;
+ path = ../../dist/static/Headers;
+ sourceTree = SOURCE_ROOT;
+ };
+ DD8A77AD196DBFDA00FAD883 /* GL Library */ = {
+ isa = PBXGroup;
+ children = (
+ DD6123FF19CCF06E006845B1 /* Headers */,
+ DD61240619CCF06E006845B1 /* libMapboxGL.a */,
+ DD61240719CCF06E006845B1 /* MapboxGL.bundle */,
+ );
+ name = "GL Library";
+ sourceTree = "<group>";
+ };
+ DDBD0139196DC38D0033959E /* Tests */ = {
+ isa = PBXGroup;
+ children = (
+ DDBD016B196DC4740033959E /* KIFTestActor+MapboxGL.h */,
+ DDBD016A196DC4740033959E /* KIFTestActor+MapboxGL.m */,
+ DDBD0169196DC4740033959E /* MapViewTests.h */,
+ DDBD0168196DC4740033959E /* MapViewTests.m */,
+ DDBD0167196DC46B0033959E /* Supporting Files */,
+ DDBD014D196DC3B00033959E /* KIF */,
+ );
+ name = Tests;
+ path = "Mapbox GL Tests";
+ sourceTree = "<group>";
+ };
+ DDBD013B196DC3AE0033959E /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ DDBD0144196DC3AE0033959E /* libKIF.a */,
+ DDBD0146196DC3AE0033959E /* libKIF-OCUnit.a */,
+ DDBD0148196DC3AE0033959E /* Test Host.app */,
+ DDBD014A196DC3AE0033959E /* KIF Tests - XCTest.xctest */,
+ DDBD014C196DC3AE0033959E /* KIF Tests-OCUnit.octest */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ DDBD014D196DC3B00033959E /* KIF */ = {
+ isa = PBXGroup;
+ children = (
+ DDBD013A196DC3AE0033959E /* KIF.xcodeproj */,
+ );
+ name = KIF;
+ sourceTree = "<group>";
+ };
+ DDBD0167196DC46B0033959E /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+ DDBD0165196DC4560033959E /* Bundle-Info.plist */,
+ );
+ name = "Supporting Files";
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ DD043322196DB9BC00E6F39D /* Mapbox GL Tests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = DD043358196DB9BC00E6F39D /* Build configuration list for PBXNativeTarget "Mapbox GL Tests" */;
+ buildPhases = (
+ DD04331F196DB9BC00E6F39D /* Sources */,
+ DD043320196DB9BC00E6F39D /* Frameworks */,
+ DD043321196DB9BC00E6F39D /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = "Mapbox GL Tests";
+ productName = "Mapbox GL Tests";
+ productReference = DD043323196DB9BC00E6F39D /* Mapbox GL Tests.app */;
+ productType = "com.apple.product-type.application";
+ };
+ DDBD0151196DC3D70033959E /* Test Bundle */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = DDBD0162196DC3D70033959E /* Build configuration list for PBXNativeTarget "Test Bundle" */;
+ buildPhases = (
+ DDBD014E196DC3D70033959E /* Sources */,
+ DDBD014F196DC3D70033959E /* Frameworks */,
+ DDBD0150196DC3D70033959E /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ DDBD0161196DC3D70033959E /* PBXTargetDependency */,
+ );
+ name = "Test Bundle";
+ productName = "Test Bundle";
+ productReference = DDBD0152196DC3D70033959E /* Test Bundle.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ DD04331B196DB9BC00E6F39D /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ CLASSPREFIX = MGLT;
+ LastUpgradeCheck = 0510;
+ ORGANIZATIONNAME = Mapbox;
+ TargetAttributes = {
+ DDBD0151196DC3D70033959E = {
+ TestTargetID = DD043322196DB9BC00E6F39D;
+ };
+ };
+ };
+ buildConfigurationList = DD04331E196DB9BC00E6F39D /* Build configuration list for PBXProject "ios-tests" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = DD04331A196DB9BC00E6F39D;
+ productRefGroup = DD043324196DB9BC00E6F39D /* Products */;
+ projectDirPath = "";
+ projectReferences = (
+ {
+ ProductGroup = DDBD013B196DC3AE0033959E /* Products */;
+ ProjectRef = DDBD013A196DC3AE0033959E /* KIF.xcodeproj */;
+ },
+ );
+ projectRoot = "";
+ targets = (
+ DD043322196DB9BC00E6F39D /* Mapbox GL Tests */,
+ DDBD0151196DC3D70033959E /* Test Bundle */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXReferenceProxy section */
+ DDBD0144196DC3AE0033959E /* libKIF.a */ = {
+ isa = PBXReferenceProxy;
+ fileType = archive.ar;
+ path = libKIF.a;
+ remoteRef = DDBD0143196DC3AE0033959E /* PBXContainerItemProxy */;
+ sourceTree = BUILT_PRODUCTS_DIR;
+ };
+ DDBD0146196DC3AE0033959E /* libKIF-OCUnit.a */ = {
+ isa = PBXReferenceProxy;
+ fileType = archive.ar;
+ path = "libKIF-OCUnit.a";
+ remoteRef = DDBD0145196DC3AE0033959E /* PBXContainerItemProxy */;
+ sourceTree = BUILT_PRODUCTS_DIR;
+ };
+ DDBD0148196DC3AE0033959E /* Test Host.app */ = {
+ isa = PBXReferenceProxy;
+ fileType = wrapper.application;
+ path = "Test Host.app";
+ remoteRef = DDBD0147196DC3AE0033959E /* PBXContainerItemProxy */;
+ sourceTree = BUILT_PRODUCTS_DIR;
+ };
+ DDBD014A196DC3AE0033959E /* KIF Tests - XCTest.xctest */ = {
+ isa = PBXReferenceProxy;
+ fileType = wrapper.cfbundle;
+ path = "KIF Tests - XCTest.xctest";
+ remoteRef = DDBD0149196DC3AE0033959E /* PBXContainerItemProxy */;
+ sourceTree = BUILT_PRODUCTS_DIR;
+ };
+ DDBD014C196DC3AE0033959E /* KIF Tests-OCUnit.octest */ = {
+ isa = PBXReferenceProxy;
+ fileType = wrapper.cfbundle;
+ path = "KIF Tests-OCUnit.octest";
+ remoteRef = DDBD014B196DC3AE0033959E /* PBXContainerItemProxy */;
+ sourceTree = BUILT_PRODUCTS_DIR;
+ };
+/* End PBXReferenceProxy section */
+
+/* Begin PBXResourcesBuildPhase section */
+ DD043321196DB9BC00E6F39D /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ DD61240919CCF06E006845B1 /* MapboxGL.bundle in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ DDBD0150196DC3D70033959E /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ DD04331F196DB9BC00E6F39D /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ DD043364196DBBD500E6F39D /* MGLTViewController.m in Sources */,
+ DD043366196DBBE000E6F39D /* main.m in Sources */,
+ DD043363196DBBD500E6F39D /* MGLTAppDelegate.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ DDBD014E196DC3D70033959E /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ DDBD016D196DC4740033959E /* KIFTestActor+MapboxGL.m in Sources */,
+ DDBD016C196DC4740033959E /* MapViewTests.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ DDBD0161196DC3D70033959E /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = DD043322196DB9BC00E6F39D /* Mapbox GL Tests */;
+ targetProxy = DDBD0160196DC3D70033959E /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+ DD043356196DB9BC00E6F39D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 7.1;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ DD043357196DB9BC00E6F39D /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = YES;
+ ENABLE_NS_ASSERTIONS = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 7.1;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ DD043359196DB9BC00E6F39D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = (
+ armv7,
+ armv7s,
+ arm64,
+ i386,
+ x86_64,
+ );
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
+ HEADER_SEARCH_PATHS = (
+ "$(inherited)",
+ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
+ ../dist/static/Headers,
+ );
+ INFOPLIST_FILE = "$(SRCROOT)/App-Info.plist";
+ IPHONEOS_DEPLOYMENT_TARGET = 7.0;
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ ../dist/static,
+ );
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TARGETED_DEVICE_FAMILY = "1,2";
+ WRAPPER_EXTENSION = app;
+ };
+ name = Debug;
+ };
+ DD04335A196DB9BC00E6F39D /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = (
+ armv7,
+ armv7s,
+ arm64,
+ i386,
+ x86_64,
+ );
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
+ HEADER_SEARCH_PATHS = (
+ "$(inherited)",
+ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
+ ../dist/static/Headers,
+ );
+ INFOPLIST_FILE = "$(SRCROOT)/App-Info.plist";
+ IPHONEOS_DEPLOYMENT_TARGET = 7.0;
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ ../dist/static,
+ );
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TARGETED_DEVICE_FAMILY = "1,2";
+ WRAPPER_EXTENSION = app;
+ };
+ name = Release;
+ };
+ DDBD0163196DC3D70033959E /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/Mapbox GL Tests.app/Mapbox GL Tests";
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(SDKROOT)/Developer/Library/Frameworks",
+ "$(inherited)",
+ "$(DEVELOPER_FRAMEWORKS_DIR)",
+ );
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ "KIF_XCTEST=1",
+ );
+ HEADER_SEARCH_PATHS = (
+ "$(inherited)",
+ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
+ ../dist/static/Headers,
+ );
+ INFOPLIST_FILE = "Bundle-Info.plist";
+ OTHER_LDFLAGS = (
+ "$(inherited)",
+ "-framework",
+ XCTest,
+ "-ObjC",
+ );
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TEST_HOST = "$(BUNDLE_LOADER)";
+ WRAPPER_EXTENSION = xctest;
+ };
+ name = Debug;
+ };
+ DDBD0164196DC3D70033959E /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/Mapbox GL Tests.app/Mapbox GL Tests";
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(SDKROOT)/Developer/Library/Frameworks",
+ "$(inherited)",
+ "$(DEVELOPER_FRAMEWORKS_DIR)",
+ );
+ GCC_PREPROCESSOR_DEFINITIONS = "KIF_XCTEST=1";
+ HEADER_SEARCH_PATHS = (
+ "$(inherited)",
+ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
+ ../dist/static/Headers,
+ );
+ INFOPLIST_FILE = "Bundle-Info.plist";
+ OTHER_LDFLAGS = (
+ "$(inherited)",
+ "-framework",
+ XCTest,
+ "-ObjC",
+ );
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TEST_HOST = "$(BUNDLE_LOADER)";
+ WRAPPER_EXTENSION = xctest;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ DD04331E196DB9BC00E6F39D /* Build configuration list for PBXProject "ios-tests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ DD043356196DB9BC00E6F39D /* Debug */,
+ DD043357196DB9BC00E6F39D /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ DD043358196DB9BC00E6F39D /* Build configuration list for PBXNativeTarget "Mapbox GL Tests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ DD043359196DB9BC00E6F39D /* Debug */,
+ DD04335A196DB9BC00E6F39D /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ DDBD0162196DC3D70033959E /* Build configuration list for PBXNativeTarget "Test Bundle" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ DDBD0163196DC3D70033959E /* Debug */,
+ DDBD0164196DC3D70033959E /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = DD04331B196DB9BC00E6F39D /* Project object */;
+}
diff --git a/test/ios/ios-tests.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/test/ios/ios-tests.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000000..4cad7961db
--- /dev/null
+++ b/test/ios/ios-tests.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "self:Mapbox GL Tests.xcodeproj">
+ </FileRef>
+</Workspace>
diff --git a/test/ios/ios-tests.xcodeproj/project.xcworkspace/xcshareddata/Mapbox GL Tests.xccheckout b/test/ios/ios-tests.xcodeproj/project.xcworkspace/xcshareddata/Mapbox GL Tests.xccheckout
new file mode 100644
index 0000000000..68c68a2234
--- /dev/null
+++ b/test/ios/ios-tests.xcodeproj/project.xcworkspace/xcshareddata/Mapbox GL Tests.xccheckout
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IDESourceControlProjectFavoriteDictionaryKey</key>
+ <false/>
+ <key>IDESourceControlProjectIdentifier</key>
+ <string>A9E9DC14-62C7-4C7A-B782-6B24B7657046</string>
+ <key>IDESourceControlProjectName</key>
+ <string>Mapbox GL Tests</string>
+ <key>IDESourceControlProjectOriginsDictionary</key>
+ <dict>
+ <key>1F4F0A5F-01E4-4945-AE04-F4B1B763C2BF</key>
+ <string>ssh://github.com/mapbox/mapbox-gl-cocoa.git</string>
+ </dict>
+ <key>IDESourceControlProjectPath</key>
+ <string>test/Mapbox GL Tests/Mapbox GL Tests.xcodeproj/project.xcworkspace</string>
+ <key>IDESourceControlProjectRelativeInstallPathDictionary</key>
+ <dict>
+ <key>1F4F0A5F-01E4-4945-AE04-F4B1B763C2BF</key>
+ <string>../../../..</string>
+ </dict>
+ <key>IDESourceControlProjectURL</key>
+ <string>ssh://github.com/mapbox/mapbox-gl-cocoa.git</string>
+ <key>IDESourceControlProjectVersion</key>
+ <integer>110</integer>
+ <key>IDESourceControlProjectWCCIdentifier</key>
+ <string>1F4F0A5F-01E4-4945-AE04-F4B1B763C2BF</string>
+ <key>IDESourceControlProjectWCConfigurations</key>
+ <array>
+ <dict>
+ <key>IDESourceControlRepositoryExtensionIdentifierKey</key>
+ <string>public.vcs.git</string>
+ <key>IDESourceControlWCCIdentifierKey</key>
+ <string>1F4F0A5F-01E4-4945-AE04-F4B1B763C2BF</string>
+ <key>IDESourceControlWCCName</key>
+ <string>mapbox-gl-cocoa</string>
+ </dict>
+ </array>
+</dict>
+</plist>
diff --git a/test/ios/ios-tests.xcodeproj/project.xcworkspace/xcshareddata/ios-tests.xccheckout b/test/ios/ios-tests.xcodeproj/project.xcworkspace/xcshareddata/ios-tests.xccheckout
new file mode 100644
index 0000000000..8f3ca408b6
--- /dev/null
+++ b/test/ios/ios-tests.xcodeproj/project.xcworkspace/xcshareddata/ios-tests.xccheckout
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IDESourceControlProjectFavoriteDictionaryKey</key>
+ <false/>
+ <key>IDESourceControlProjectIdentifier</key>
+ <string>31F71B93-A6C7-4EE6-B62A-5324738E25FE</string>
+ <key>IDESourceControlProjectName</key>
+ <string>ios-tests</string>
+ <key>IDESourceControlProjectOriginsDictionary</key>
+ <dict>
+ <key>10265E242415D473A6A613214DB7AC3EE3D43F93</key>
+ <string>https://github.com/mapbox/KIF.git</string>
+ <key>7E68CB584078A487C0535CC191D3B7551EEE2095</key>
+ <string>github.com:mapbox/mapbox-gl-native.git</string>
+ <key>FC967DACF69B3B67E6F2E9FBE56B64B73B118AFF</key>
+ <string>github.com:mapbox/mapbox-gl-cocoa.git</string>
+ </dict>
+ <key>IDESourceControlProjectPath</key>
+ <string>test/ios-tests.xcodeproj</string>
+ <key>IDESourceControlProjectRelativeInstallPathDictionary</key>
+ <dict>
+ <key>10265E242415D473A6A613214DB7AC3EE3D43F93</key>
+ <string>../../..test/KIF/</string>
+ <key>7E68CB584078A487C0535CC191D3B7551EEE2095</key>
+ <string>../../../../..</string>
+ <key>FC967DACF69B3B67E6F2E9FBE56B64B73B118AFF</key>
+ <string>../../..</string>
+ </dict>
+ <key>IDESourceControlProjectURL</key>
+ <string>github.com:mapbox/mapbox-gl-cocoa.git</string>
+ <key>IDESourceControlProjectVersion</key>
+ <integer>111</integer>
+ <key>IDESourceControlProjectWCCIdentifier</key>
+ <string>FC967DACF69B3B67E6F2E9FBE56B64B73B118AFF</string>
+ <key>IDESourceControlProjectWCConfigurations</key>
+ <array>
+ <dict>
+ <key>IDESourceControlRepositoryExtensionIdentifierKey</key>
+ <string>public.vcs.git</string>
+ <key>IDESourceControlWCCIdentifierKey</key>
+ <string>7E68CB584078A487C0535CC191D3B7551EEE2095</string>
+ <key>IDESourceControlWCCName</key>
+ <string>..</string>
+ </dict>
+ <dict>
+ <key>IDESourceControlRepositoryExtensionIdentifierKey</key>
+ <string>public.vcs.git</string>
+ <key>IDESourceControlWCCIdentifierKey</key>
+ <string>10265E242415D473A6A613214DB7AC3EE3D43F93</string>
+ <key>IDESourceControlWCCName</key>
+ <string>KIF</string>
+ </dict>
+ <dict>
+ <key>IDESourceControlRepositoryExtensionIdentifierKey</key>
+ <string>public.vcs.git</string>
+ <key>IDESourceControlWCCIdentifierKey</key>
+ <string>FC967DACF69B3B67E6F2E9FBE56B64B73B118AFF</string>
+ <key>IDESourceControlWCCName</key>
+ <string>mapbox-gl-cocoa</string>
+ </dict>
+ </array>
+</dict>
+</plist>
diff --git a/test/ios/ios-tests.xcodeproj/xcshareddata/xcschemes/Mapbox GL Tests.xcscheme b/test/ios/ios-tests.xcodeproj/xcshareddata/xcschemes/Mapbox GL Tests.xcscheme
new file mode 100644
index 0000000000..f3c2bd7ffe
--- /dev/null
+++ b/test/ios/ios-tests.xcodeproj/xcshareddata/xcschemes/Mapbox GL Tests.xcscheme
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "0510"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "EABD46791857A0C700A5F081"
+ BuildableName = "libKIF.a"
+ BlueprintName = "KIF"
+ ReferencedContainer = "container:KIF/KIF.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DD043322196DB9BC00E6F39D"
+ BuildableName = "Mapbox GL Tests.app"
+ BlueprintName = "Mapbox GL Tests"
+ ReferencedContainer = "container:ios-tests.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ buildConfiguration = "Release">
+ <Testables>
+ <TestableReference
+ skipped = "NO">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DDBD0151196DC3D70033959E"
+ BuildableName = "Test Bundle.xctest"
+ BlueprintName = "Test Bundle"
+ ReferencedContainer = "container:ios-tests.xcodeproj">
+ </BuildableReference>
+ </TestableReference>
+ </Testables>
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DD043322196DB9BC00E6F39D"
+ BuildableName = "Mapbox GL Tests.app"
+ BlueprintName = "Mapbox GL Tests"
+ ReferencedContainer = "container:ios-tests.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ </TestAction>
+ <LaunchAction
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ buildConfiguration = "Debug"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ allowLocationSimulation = "YES">
+ <BuildableProductRunnable>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DD043322196DB9BC00E6F39D"
+ BuildableName = "Mapbox GL Tests.app"
+ BlueprintName = "Mapbox GL Tests"
+ ReferencedContainer = "container:ios-tests.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </LaunchAction>
+ <ProfileAction
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ buildConfiguration = "Release"
+ debugDocumentVersioning = "YES">
+ <BuildableProductRunnable>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DD043322196DB9BC00E6F39D"
+ BuildableName = "Mapbox GL Tests.app"
+ BlueprintName = "Mapbox GL Tests"
+ ReferencedContainer = "container:ios-tests.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/test/ios/main.m b/test/ios/main.m
new file mode 100644
index 0000000000..d79750dcdd
--- /dev/null
+++ b/test/ios/main.m
@@ -0,0 +1,9 @@
+#import <UIKit/UIKit.h>
+#import "MGLTAppDelegate.h"
+
+int main(int argc, char * argv[])
+{
+ @autoreleasepool {
+ return UIApplicationMain(argc, argv, nil, NSStringFromClass([MGLTAppDelegate class]));
+ }
+}
diff --git a/test/suite b/test/suite
-Subproject dba0f2b1fa749691435d06a251d733173d41e3f
+Subproject d2fff4e2048bc4b0b2eb92c779fce01b4a45b06