Merge branch 'master' into node

This commit is contained in:
Mike Morris
2015-08-21 16:24:02 -04:00
1164 changed files with 318182 additions and 0 deletions

18
.clang-format Normal file
View File

@@ -0,0 +1,18 @@
Standard: Cpp11
IndentWidth: 4
AccessModifierOffset: -4
UseTab: Never
BinPackParameters: false
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AllowShortBlocksOnASingleLine: false
AllowShortFunctionsOnASingleLine: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
AlwaysBreakTemplateDeclarations: true
NamespaceIndentation: None
PointerBindsToType: true
SpacesInParentheses: false
BreakBeforeBraces: Attach
ColumnLimit: 100
Cpp11BracedListStyle: false
SpacesBeforeTrailingComments: 1

35
.gitignore vendored Normal file
View File

@@ -0,0 +1,35 @@
.DS_Store
*.xcodeproj
*.o
*.actual.png
*.diff.png
*.pyc
/android/debug
/android/sdk
/android/**/.classpath
/android/**/.project
/android/**/.settings/
/mason_packages
/config/*.gypi
/build
/macosx/build
/linux/build
/ios/build
/ios/app/build
/test/build
/gyp/build
/gyp/java
/test/node_modules
/test/fixtures/*/*_actual.*
/test/fixtures/api/1.png
/test/fixtures/api/2.png
/test/fixtures/database/*.db
/include/mbgl/shader/shaders.hpp
/src/shader/shaders_gl.cpp
/src/shader/shaders_gles2.cpp
/bin/style.bin.js
*~
/ios/benchmark/assets/tiles/mapbox.mapbox-streets-v6/
/ios/benchmark/assets/tiles/mapbox.mapbox-terrain-v2/
/ios/benchmark/assets/glyphs/DIN Offc Pro*/

23
.gitmodules vendored Normal file
View File

@@ -0,0 +1,23 @@
[submodule "test/suite"]
path = test/suite
url = https://github.com/mapbox/mapbox-gl-test-suite.git
[submodule ".mason"]
path = .mason
url = https://github.com/mapbox/mason.git
[submodule "styles"]
path = styles
url = https://github.com/mapbox/mapbox-gl-styles.git
[submodule "test/ios/KIF"]
path = test/ios/KIF
url = https://github.com/kif-framework/KIF.git
[submodule "platform/ios/vendor/SMCalloutView"]
path = platform/ios/vendor/SMCalloutView
url = https://github.com/nfarina/calloutview.git
[submodule "src/mbgl/util/geojsonvt"]
path = src/mbgl/util/geojsonvt
url = https://github.com/mapbox/geojson-vt-cpp

1
.mason Submodule

Submodule .mason added at d8597d9445

100
.travis.yml Normal file
View File

@@ -0,0 +1,100 @@
language: android
sudo: false
matrix:
exclude:
- os: linux
include:
- os: linux
env: FLAVOR=linux CXX=g++-4.9 BUILDTYPE=Release
addons:
apt:
sources: [ 'ubuntu-toolchain-r-test' ]
packages: [ 'gdb', 'g++-4.9', 'gcc-4.9', 'libllvm3.4', 'xutils-dev', 'libxxf86vm-dev', 'x11proto-xf86vidmode-dev', 'mesa-utils' ]
- os: linux
env: FLAVOR=linux CXX=clang++-3.5 BUILDTYPE=Debug
addons:
apt:
sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.5' ]
packages: [ 'gdb', 'clang-3.5', 'libstdc++-4.9-dev', 'libstdc++6', 'libllvm3.4', 'xutils-dev', 'libxxf86vm-dev', 'x11proto-xf86vidmode-dev', 'mesa-utils' ]
- os: linux
env: FLAVOR=linux CXX=clang++-3.5 BUILDTYPE=Release
addons:
apt:
sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.5' ]
packages: [ 'gdb', 'clang-3.5', 'libstdc++-4.9-dev', 'libstdc++6', 'libllvm3.4', 'xutils-dev', 'libxxf86vm-dev', 'x11proto-xf86vidmode-dev', 'mesa-utils' ]
- os: osx
osx_image: xcode6.4
env: FLAVOR=osx BUILDTYPE=Debug
- os: osx
osx_image: xcode6.4
env: FLAVOR=ios BUILDTYPE=Release
- os: linux
env: FLAVOR=android ANDROID_ABI=arm-v7 BUILDTYPE=Release
android:
components: [ 'build-tools-23', 'android-23', 'extra-android-m2repository' ]
addons:
apt:
packages: [ 'lib32stdc++6' ]
- os: linux
env: FLAVOR=android ANDROID_ABI=arm-v7 BUILDTYPE=Debug
android:
components: [ 'build-tools-23', 'android-23', 'extra-android-m2repository' ]
addons:
apt:
packages: [ 'lib32stdc++6' ]
- os: linux
env: FLAVOR=android ANDROID_ABI=arm-v8 BUILDTYPE=Release
android:
components: [ 'build-tools-23', 'android-23', 'extra-android-m2repository' ]
addons:
apt:
packages: [ 'lib32stdc++6' ]
- os: linux
env: FLAVOR=android ANDROID_ABI=x86 BUILDTYPE=Release
android:
components: [ 'build-tools-23', 'android-23', 'extra-android-m2repository' ]
addons:
apt:
packages: [ 'lib32stdc++6' ]
env:
global:
- TERM: dumb
# AWS
- secure: "MZHblLZXG/jWf2w0ZFlxCLDwx2qtGgRDODQyg1BR7JIuMz6AtWv8XR/sUczWLbiABCL0a/NzJF1g4v2pI7X69IntcjOdIABBgTh7++6+1TJ0Kp8viEltb55nQG3lHy/R6fOaI7Pj9tuCX0PCRtGA5C/fGnodLGEjy3RVOJ09ln0="
- secure: "KaSQbhgjtV7ZCkesHmvrNsbQVjk5SPfGKB1VkWenRGYhLF45HpSRNwSxMQddZ566Pg7qIFgF1iWl/B0QW3B6AWL5WmzQ5AOJgwS876pNIc/UT7ubMPtgAtjpvw1bQvQP3B8MrB+3OE5c6tD+a3LhR9krV//dOsfErR5Yy+3Mbkc="
# Access Token
- secure: "RiBIBfVhhaMjU5ksuwJO3shdvG9FpinBjdSv4co9jg9171SR8edNriedHjVKSIeBhSGNmZmX+twS3dJS/By6tl/LKh9sTynA+ZAYYljkE7jn881B/gMrlYvdAA6og5KvkhV1/0iJWlhuZrMTkhpDR200iLgg3EWBhWjltzmDW/I="
# Testmunk
- secure: "CHBiUM60TolDbQnn+4IRA/tvOKwKs3g9EDvv8YHSJMg3FuHmjKQkprBasvxf3hnTXg4WLZEubmeDcyJ6RRzPP5mMSr/hksYl0pSjj/6TUecE5fHPVVeN7txVqkpOBf9i45Y+iBUQMjBb1NnDK3pHXxpnAs1Q/pe7vReErj4GF1U="
# 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="
- KIF_SCREENSHOTS="${TRAVIS_BUILD_DIR}/screenshots"
before_install:
- source ./scripts/travis_helper.sh
install:
- ./scripts/${FLAVOR}/install.sh
script:
- ./scripts/${FLAVOR}/run.sh
after_failure:
- "[ -f ./scripts/${FLAVOR}/after_failure.sh ] && ./scripts/${FLAVOR}/after_failure.sh"
after_script:
- "[ -f ./scripts/${FLAVOR}/after_script.sh ] && ./scripts/${FLAVOR}/after_script.sh"
notifications:
slack:
secure: HHQYr7sF8M1SzoWSqgKVYtwAgGdLLCyTMsQjFhEEQNYO92ZwURE5s03qWTGH5k8+4Yqn26yrXt3NztLC4JIOpcGervN2mSZyq4dZgFTcWEd61igw0qwSenlwvFfbE1ASK/KYCzfyn9MIfHN+ovwLoRxXZkPwinKDvl3DXjBaFNg=
git:
submodules: false

162
.ycm_extra_conf.py Normal file
View File

@@ -0,0 +1,162 @@
# Version of .ycm_extra_conf.py sample file adapted
# for the Mapbox GL Native project.
#
# This file is NOT licensed under the GPLv3, which is the license for the rest
# of YouCompleteMe.
#
# Here's the license text for this file:
#
# This is free and unencumbered software released into the public domain.
#
# Anyone is free to copy, modify, publish, use, compile, sell, or
# distribute this software, either in source code form or as a compiled
# binary, for any purpose, commercial or non-commercial, and by any
# means.
#
# In jurisdictions that recognize copyright laws, the author or authors
# of this software dedicate any and all copyright interest in the
# software to the public domain. We make this dedication for the benefit
# of the public at large and to the detriment of our heirs and
# successors. We intend this dedication to be an overt act of
# relinquishment in perpetuity of all present and future rights to this
# software under copyright law.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
#
# For more information, please refer to <http://unlicense.org/>
import os
import subprocess
import sys
import ycm_core
def DirectoryOfThisScript():
return os.path.dirname(os.path.abspath(__file__))
default_flags = [
'-std=c++14',
'-I%s' % (os.path.join(DirectoryOfThisScript(), 'src')),
'-I%s' % (os.path.join(DirectoryOfThisScript(), 'include')),
]
def MakeRelativePathsInFlagsAbsolute(flags, working_directory):
if not working_directory:
return list(flags)
new_flags = []
make_next_absolute = False
path_flags = ['-isystem', '-I', '-iquote', '--sysroot=']
for flag in flags:
new_flag = flag
if make_next_absolute:
make_next_absolute = False
if not flag.startswith('/'):
new_flag = os.path.join(working_directory, flag)
for path_flag in path_flags:
if flag == path_flag:
make_next_absolute = True
break
if flag.startswith(path_flag):
path = flag[len(path_flag):]
new_flag = path_flag + os.path.join(working_directory, path)
break
if new_flag:
new_flags.append(new_flag)
return new_flags
def FlagsForFileDefault():
return {
'flags': default_flags,
'do_cache': False,
}
def FlagsForFileLinux(filename):
working_directory = os.path.join('build', 'linux')
if not os.path.exists(os.path.join(DirectoryOfThisScript(), working_directory)):
return FlagsForFileDefault()
relative_source_path = filename.replace(DirectoryOfThisScript() + '/', '')
relative_obj_path = os.path.splitext(relative_source_path)[0] + '.o'
make_cmd = 'make -pn |grep %s: |grep GYP_CXXFLAGS |cut -d\' \' -f4-' % relative_obj_path
p = subprocess.Popen([make_cmd], shell=True, stdout=subprocess.PIPE)
stdout, stderr = p.communicate()
if p.returncode:
return FlagsForFileDefault()
flags = stdout.split(' ')
flags_relative = MakeRelativePathsInFlagsAbsolute(flags, os.path.join(DirectoryOfThisScript(), working_directory))
flags_final = [flag for flag in flags_relative if flag.startswith("-")]
return {
'flags': flags_final,
'do_cache': True,
}
def FlagsForFileDarwin(filename):
working_directory = os.path.join('build', 'osx')
if not os.path.exists(os.path.join(DirectoryOfThisScript(), working_directory)):
return FlagsForFileDefault()
relative_source_path = filename.replace(DirectoryOfThisScript() + '/', '')
relative_obj_path = os.path.splitext(relative_source_path)[0] + '.o'
make_cmd = 'make -pn |grep "%s :=" |grep "NDEBUG" |tail -n 1 | cut -d\' \' -f4-' % relative_obj_path
p = subprocess.Popen([make_cmd], shell=True, stdout=subprocess.PIPE)
stdout, stderr = p.communicate()
if p.returncode:
return FlagsForFileDefault()
flags = stdout.split(' ')
flags_relative = MakeRelativePathsInFlagsAbsolute(flags, os.path.join(DirectoryOfThisScript(), working_directory))
flags_final = [flag for flag in flags_relative if flag.startswith("-")]
# Append OSX sysroot SDK path
xcrun_cmd = 'xcrun --show-sdk-path'
p = subprocess.Popen([xcrun_cmd], shell=True, stdout=subprocess.PIPE)
stdout, stderr = p.communicate()
if p.returncode:
return FlagsForFileDefault()
flags_final.append('-isysroot ' + stdout)
return {
'flags': flags_final,
'do_cache': True,
}
def FlagsForFile(filename, **kwargs):
if sys.platform.startswith('linux'):
return FlagsForFileLinux(filename)
if sys.platform.startswith('darwin'):
return FlagsForFileDarwin(filename)
return FlagsForFileDefault()

72
CHANGELOG.md Normal file
View File

@@ -0,0 +1,72 @@
# Changelog
## 0.5.1
### iOS
- Added support for CocoaPods 0.38.0. ([#1876](https://github.com/mapbox/mapbox-gl-native/pull/1876))
## 0.5.0
### Core
- Support for runtime marker imagery. ([#941](https://github.com/mapbox/mapbox-gl-native/pull/941))
- Added `Map::fitBounds()` for region-based viewport setting. ([#1092](https://github.com/mapbox/mapbox-gl-native/issues/1092))
- Added a raster satellite bundled style and improved raster rendering. ([#963](https://github.com/mapbox/mapbox-gl-native/issues/963))
- Improved round line joins for semi-transparent lines. ([#1839](https://github.com/mapbox/mapbox-gl-native/pull/1839))
- Improved map render lifecycle notifications. ([#1026](https://github.com/mapbox/mapbox-gl-native/issues/1026))
- Fixed a bug that caused annotations not to show at zoom level zero. ([#1279](https://github.com/mapbox/mapbox-gl-native/issues/1279))
- Fixed a bug with the ordering of shape layers. ([#1866](https://github.com/mapbox/mapbox-gl-native/pull/1866))
- Other bug fixes and performance improvements.
### iOS
- **Breaking:** Headers now make use of lightweight generics, eliminating many unnecessary casts when working with annotations in Swift 2.0 in Xcode 7. ([#1711](https://github.com/mapbox/mapbox-gl-native/pull/1711))
- **Breaking:** `-mapView:symbolNameForAnnotation:` has been removed from the `MGLMapViewDelegate` protocol. Implement `-mapView:imageForAnnotation:` instead, which accepts images at runtime. ([#941](https://github.com/mapbox/mapbox-gl-native/pull/941))
- **Breaking:** `MGLMapView.direction` is now expressed in terms of degrees clockwise from true north, as indicated in the documentation, rather than counterclockwise. ([#1789](https://github.com/mapbox/mapbox-gl-native/pull/1789))
- A Satellite style showing Mapbox Satellite imagery is now bundled with Mapbox GL. ([#1845](https://github.com/mapbox/mapbox-gl-native/pull/1845))
- Improved `UIView` tracking to the map. ([#1813](https://github.com/mapbox/mapbox-gl-native/pull/1813))
- Delegate method `-[MGLMapViewDelegate mapView:didFailToLocateUserWithError:]` now works. ([#1608](https://github.com/mapbox/mapbox-gl-native/pull/1608))
- It is now possible to fit the map’s viewport to a coordinate bounding box via `-[MGLMapView setVisibleCoordinateBounds:animated:]` or to a specific set of coordinates via `-[MGLMapView setVisibleCoordinates:count:edgePadding:animated:]`. ([#1783](https://github.com/mapbox/mapbox-gl-native/pull/1783), [#1795](https://github.com/mapbox/mapbox-gl-native/pull/1795))
- The logo and ℹ️ no longer disappear or get distorted after embedding MGLMapView in a different view, and you can now access these subviews directly via properties on MGLMapView. ([#1779](https://github.com/mapbox/mapbox-gl-native/pull/1779), [#1815](https://github.com/mapbox/mapbox-gl-native/pull/1815))
- Raster tiles now look sharper midway between two zoom levels. ([#1843](https://github.com/mapbox/mapbox-gl-native/pull/1843))
- Resetting the map rotation to north no longer also resets the user location tracking mode. ([#1809](https://github.com/mapbox/mapbox-gl-native/pull/1809))
- `-[MGLMapView convertPoint:toCoordinateFromView:]` now returns accurate coordinates on iPhone 6. ([#1827](https://github.com/mapbox/mapbox-gl-native/pull/1827))
- Fixed an issue in which `-[MGLMapView direction]` would sometimes return 360 instead of 0. ([#1829](https://github.com/mapbox/mapbox-gl-native/pull/1829))
- Build against iOS 8.4. ([#1868](https://github.com/mapbox/mapbox-gl-native/pull/1868))
## 0.4.0
### Core
- Support for polyline and polygon shape annotations. ([#1655](https://github.com/mapbox/mapbox-gl-native/issues/1655))
- Improved placement and density of labels. ([#1666](https://github.com/mapbox/mapbox-gl-native/issues/1666), [blog](https://www.mapbox.com/blog/better-label-placement-mapbox-mobile/))
- Improved z-ordering appearance of point markers. ([#988](https://github.com/mapbox/mapbox-gl-native/issues/988))
- Fixed an issue in which certain features, such as roundabouts, were not rendered completely. ([#1725](https://github.com/mapbox/mapbox-gl-native/issues/1725))
- Many bug fixes and performance and stability improvements.
- Improved tests.
### iOS
- **Breaking:** `MGLMapView` no longer manages Mapbox access tokens directly; an access token cannot be passed in when initializing the map view. Instead, set `MGLMapboxAccessToken` to your access token in your app’s `Info.plist` file, or call `+[MGLAccountManager setAccessToken:]` before initializing the map view. If you were setting the access token inside an Interface Builder inspectable, also remove it from the User Defined Runtime Attributes section of the Identity inspector. ([#1553](https://github.com/mapbox/mapbox-gl-native/issues/1553))
- **Breaking:** `MGLAccountManager`'s `-setMapboxMetricsEnabledSettingShownInApp:` has been removed. If you implement a Mapbox Metrics switch inside your app, instead of inside a Settings bundle, set `MGLMapboxMetricsEnabledSettingShownInApp` to `YES` in the `Info.plist` file. ([#1553](https://github.com/mapbox/mapbox-gl-native/issues/1553))
- **Breaking:** `MGLMapView`'s `-mapID` has been renamed to `-styleID`. ([#1561](https://github.com/mapbox/mapbox-gl-native/issues/1561))
- Headers have been audited for nullability, improving type safety in both Objective-C and Swift 1.2 when compiling with Xcode 6.3 or above. ([#1578](https://github.com/mapbox/mapbox-gl-native/issues/1578))
- Fixed an issue in which the map would sometimes spin 180° while rotating the map with two fingers. ([#1453](https://github.com/mapbox/mapbox-gl-native/issues/1453))
- Added a shortcut to the Mapbox Metrics switch in `MGLMapView`'s action sheet that is attached to the ℹ️ button. ([#1611](https://github.com/mapbox/mapbox-gl-native/issues/1611))
- `MGLMapView` now supports Interface Builder designables. When you add an `MGLMapView` to a storyboard, it displays instructions for getting set up directly on the storyboard canvas. ([#1573](https://github.com/mapbox/mapbox-gl-native/issues/1573))
- The default title for the user location annotation is now “You Are Here”. You can customize the title by setting `mapView.userAnnotation.title`. ([#1559](https://github.com/mapbox/mapbox-gl-native/issues/1559))
- Internal use of the Reachability library has been cleaned up so that your app can include its own copy of Reachability. ([#1718](https://github.com/mapbox/mapbox-gl-native/issues/1718))
- Now distribute a binary stripped of debugging symbols by default with an optional, secondary symbols build. ([#1650](https://github.com/mapbox/mapbox-gl-native/issues/1650))
## 0.3.1
- Temporarily removed `IBDesignable` support on iOS.
## 0.3.0
- Initial iOS beta release.
Known issues:
- None.

11
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,11 @@
# Contributing
If you have a usage question for a product built on Mapbox GL (such as Mapbox Mobile toolkits like our SDKs), please visit https://www.mapbox.com/help/.
If you want to contribute code:
1. Please familiarize yourself with the [install process](INSTALL.md).
1. Ensure that the [existing issues](https://github.com/mapbox/mapbox-gl-native/issues?utf8=✓&q=) don't already cover your question or contribution.
1. Pull requests gladly accepted.

347
INSTALL.md Normal file
View File

@@ -0,0 +1,347 @@
# Install
This document describes the process for setting up Mapbox GL for development _on the framework itself_.
# Depends
- Modern C++ compiler that supports `-std=c++14` (clang++ 3.5 or later or g++-4.9 or later)
- [Boost headers](http://boost.org/)
- [`zlib`](http://www.zlib.net/)
- [`libpng`](http://www.libpng.org/pub/png/libpng.html)
- [`libuv 0.10+`](https://github.com/joyent/libuv)
- [`glfw 3.1+`](http://www.glfw.org/)
- [`libcurl`](http://curl.haxx.se/libcurl/) (depends on OpenSSL; Linux only)
- Apple Command Line Tools (for build on OS X; available at [Apple Developer](https://developer.apple.com/downloads))
- `pkg-config` (for build only)
- [Homebrew](http://brew.sh) (for building on OS X)
- [xcpretty](https://github.com/supermarin/xcpretty) (for building on OS X)
- [Cask](http://caskroom.io/) (for building Android on OS X)
- Python 2.x (for build only)
# Build instructions
We try to link to as many system-provided libraries as possible. When these are unavailable or too outdated, we run a thin build-script layer called [Mason](https://github.com/mapbox/mason) to automate builds, and load precompiled binary packages when possible.
Be sure to pull down all submodules first:
git submodule init
git submodule update
## OS X
This process gives you either a native OS X windowed app or a Linux desktop app with a map view for development purposes, built on an OS X host system.
### Build
ImageMagick is required for tests. It's recommended to install it with [Homebrew](http://brew.sh/) via `brew install imagemagick`.
To create projects, you can run:
- `make xproj`: Creates an Xcode project with OS X-specific handlers for HTTP downloads and settings storage. It uses [GLFW](http://www.glfw.org) for window handling.
- `make lproj`: Creates an Xcode project with platform-independent handlers for downloads and settings storage. This is what is also being built on Linux.
- `make osx run-osx`: Builds and runs the OS X application on the command line with `xcodebuild`.
- `make linux run-linux`: Builds and runs the Linux application with `make`.
Note that you can't have more than one project in Xcode open at a time since the static library project is shared across the OS X, Linux and iOS project.
### Test
- `make test-*` Builds and runs all tests. You can specify individual tests by replacing * with their name.
### Usage
- Press `S` to cycle through bundled styles
- Press `X` to reset the transform
- Press `N` to reset north
- Press `Tab` to toggle debug information
- Press `C` to toggle symbol collision debug boxes
- Press `Esc` to quit
- Press `1` through `6` to add increasing numbers of point annotations for testing
### Target
OS X 10.9+
## iOS (Test App)
This process gives you a native iOS app for simulator or device which compiles the source to GL within it for fastest turnaround time during development. Normal, production use of the library is via a precompiled binary linked into the app target.
Developing for iOS implies an OS X host system.
### Build
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. If you don't have an Apple Developer account, change the destination from "My Mac" to a simulator such as "iPhone 6" before you run and build the app.
### Test
You can run `make itest` to run the included integration tests. Requires `gem install xcpretty`. If you want to run the tests in Xcode instead, first `make ipackage` to create a local static library version, then open `test/ios/ios-tests.xcodeproj`, and lastly `Command + U` on the `Mapbox GL Tests` application target.
### Usage
- Pan to move
- Pinch to zoom
- Use two fingers to rotate
- Double-tap to zoom in one level
- Two-finger single-tap to zoom out one level
- Double-tap, long-pressing the second, then pan up and down to "quick zoom" (iPhone only, meant for one-handed use)
- Use the debug menu to add test annotations, reset position, and toggle debug info.
## iOS (Your App)
**If you want a production version of this project, use [Mapbox Mobile](http://mapbox.com/mobile).**
This section is for people contributing to Mapbox GL directly in the context of their own app.
### Build
1. Install [appledoc](http://appledoc.gentlebytes.com/appledoc/) 2.2v963 for API docs generation (only this exact version works right now ([#2117](https://github.com/mapbox/mapbox-gl-native/issues/2117)).
```
curl -L -o appledoc.zip https://github.com/tomaz/appledoc/releases/download/v2.2-963/appledoc.zip
unzip appledoc.zip
cp appledoc /usr/local/bin
cp -Rf Templates/ ~/.appledoc
```
1. Run `make ipackage`. The packaging script will produce the statically-linked `libMapbox.a`, `Mapbox.bundle` for resources, a `Headers` folder, and a `Docs` folder with HTML API documentation.
### Test
In the context of your own app, you can now either:
#### CocoaPods
Currently, until [#1437](https://github.com/mapbox/mapbox-gl-native/issues/1437) is completed, to install a _development version_ of Mapbox GL using CocoaPods you will need to build it from source manually per above.
1. Zip up the build product.
cd build/ios/pkg/static
ZIP=mapbox-ios-sdk.zip
rm -f ../${ZIP}
zip -r ../${ZIP} *
1. Modify a custom `Mapbox-iOS-SDK.podspec` to download this zip file.
{...}
m.source = {
:http => "http://{...}/mapbox-ios-sdk.zip",
:flatten => true
}
{...}
1. Update your app's `Podfile` to point to the `Mapbox-iOS-SDK.podspec`.
pod 'Mapbox-iOS-SDK', :podspec => 'http://{...}/Mapbox-iOS-SDK.podspec'
1. Run `pod update` to grab the newly-built library.
#### Binary
1. Built from source manually per above.
1. Copy the contents of `build/ios/pkg/static` into your project. It should happen automatically, but ensure that:
- `Headers` is in your *Header Search Paths* (`HEADER_SEARCH_PATHS`) build setting.
- `Mapbox.bundle` is in your target's *Copy Bundle Resources* build phase.
- `libMapbox.a` is in your target's *Link Binary With Libraries* build phase.
1. Add the following Cocoa framework dependencies to your target's *Link Binary With Libraries* build phase:
- `CoreTelephony.framework`
- `GLKit.framework`
- `ImageIO.framework`
- `MobileCoreServices.framework`
- `QuartzCore.framework`
- `SystemConfiguration.framework`
- `libc++.dylib`
- `libsqlite3.dylib`
- `libz.dylib`
1. Add `-ObjC` to your target's "Other Linker Flags" build setting (`OTHER_LDFLAGS`).
### Target
Devices: iPhone 4S and above (5, 5c, 5s, 6, 6 Plus), iPad 2 and above (3, 4, Mini, Air, Mini 2, Air 2), iPod touch 5th gen and above
iOS: 7.0+
## Linux
We are using Ubuntu for development. While the software should work on other distributions as well, we are not providing explicit build instructions here.
This process gives you a Linux desktop app built on a Linux host system.
### Build
Install GCC 4.9+ if you are running Ubuntu 14.04 or older. Alternatively, you can also use [Clang 3.5+](http://llvm.org/apt/).
sudo add-apt-repository --yes ppa:ubuntu-toolchain-r/test
sudo apt-get update
sudo apt-get install gcc-4.9 g++-4.9
export CXX=g++-4.9
Ensure you have git and other build essentials:
sudo apt-get install curl git build-essential zlib1g-dev automake \
libtool xutils-dev make cmake pkg-config python-pip \
libcurl4-openssl-dev libpng-dev libsqlite3-dev \
libllvm3.4
Install glfw3 dependencies:
sudo apt-get install libxi-dev libglu1-mesa-dev x11proto-randr-dev \
x11proto-xext-dev libxrandr-dev \
x11proto-xf86vidmode-dev libxxf86vm-dev \
libxcursor-dev libxinerama-dev
Then, you can then proceed to build the library:
git submodule update --init
make linux
Set an access token as described below, and then run:
make run-linux
### Test
- `make test-*` Builds and runs all tests. You can specify individual tests by replacing * with their name.
### Usage
- Press `S` to cycle through bundled styles
- Press `X` to reset the transform
- Press `N` to reset north
- Press `Tab` to toggle debug information
- Press `C` to toggle symbol collision debug boxes
- Press `Esc` to quit
- Press `1` through `6` to add increasing numbers of point annotations for testing
### Target
Ubuntu
## Android
### Build (Linux Host)
Install a few build dependencies:
apt-get install -y make git build-essential automake \
libtool make cmake pkg-config lib32stdc++6 lib32z1
Install Oracle JDK 7 (requires license agreement) from http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html
export JAVA_HOME="/dir/to/jdk1.7.0_71"
Install the [Android NDK Revision 10e](https://developer.android.com/tools/sdk/ndk/index.html).
export ANDROID_NDK_PATH="/dir/to/android-ndk-r10e"
Install the Android SDK. We recommend doing this by way of [Android Studio](https://developer.android.com/sdk/installing/studio.html).
export ANDROID_HOME="/dir/to/android-sdk-linux"
Run:
make android
You can then open `android/java` in Android Studio via "Import Non-Android Studio Project".
### Build (OS X Host)
Install Oracle JDK 7+:
brew cask install java
Install the [Android NDK Revision 10e](https://developer.android.com/tools/sdk/ndk/index.html) for 64-bit OS X:
brew install android-ndk
This will also install the dependency `android-sdk`.
Install [Android Studio](https://developer.android.com/sdk/installing/studio.html):
brew cask install android-studio
android
By default, the SDK will be installed to `/usr/local/opt/android-sdk`. If you open Android Studio at this point, you may get an error message telling you that it can't find a JVM, it's because you installed a custom Java VM from Oracle. Follow [these instructions](http://tools.android.com/recent/androidstudio1rc3_releasecandidate3released) to start Android Studio. You'll wind up setting these environment variables in your .bash_profile or similar:
echo "export ANDROID_HOME=`brew --prefix android-sdk`" >> .bash_profile
echo "export ANDROID_NDK_PATH=`brew --cellar android-ndk`/r10e" >> .bash_profile
# Replace <path to JDK> with something like /Library/Java/JavaVirtualMachines/jdk1.8.0_31.jdk
echo "export JAVA_HOME=<path to JDK>" >> .bash_profile
echo "export STUDIO_JDK=$JAVA_HOME" >> .bash_profile
Run:
make android
open -a Android\ Studio
You can then open `android/java` in Android Studio via "Import Non-Android Studio Project".
### Setting up Android emulator
If you want to run the test app in the emulator, we recommend the x86 build because it will run a lot faster.
First ensure you have an `MAPBOX_ACCESS_TOKEN` environment variable set, as described below. Then, create an x86 build:
ANDROID_ABI=x86 make android
In Android Studio, create an x86 AVD (Android Virtual Device):
- Open AVD Manager via the Tools menu -> Android -> AVD Manager
- Click "Create Virtual Device" at the bottom on AVD Manager window
- Select one of the device profiles, for example the Nexus 4
- Click "Next"
- Select a Lollipop or Kitkat release with ABI of x86. If the line is greyed out click Download to download the OS files.
- Click "Next"
- Under "Emulated Performance" check "Host GPU" and uncheck "Store a snapshot for faster startup"
- Click "Finish"
- Close the AVD Manager
Now when you run or debug the Android project you will see a window "Choose Device". Select your new AVD from drop down under "Launch emulator". If you select "Use same device for future launches" Android Studio will remember the selection and not ask again.
### Running on a hardware device
First read the [Google documentation](http://developer.android.com/tools/device.html) to set up your device and your OS to connect to the device.
When you plug your device in and then run or debug the Android project you will see a window "Choose Device". Choose your device from the "Choose a running device" list.
If your device does not show up, you have not set it up properly. Double check the [Google documentation](http://developer.android.com/tools/device.html).
### Target
Devices running Android 4.0+ (API level 14+).
# Troubleshooting
To trigger a complete rebuild, run `make clean` and then start over generating the Xcode projects or `Makefile` as described above.
If you are having trouble getting the dependencies right, you can blow away the `mason_packages` directory, or run `make distclean`. This means the `Makefile` and configure script will automatically install the dependencies again on the next try.
On OS X, you can also try clearing the Xcode cache with `make clear_xcode_cache`.
# Mapbox API access tokens
The demo applications use Mapbox vector tiles, which **require a Mapbox account and API access token**. Obtain an access token on the [Mapbox account page](https://www.mapbox.com/account/apps/).
For iOS and OS X use of the demo apps in Xcode, setup the access token by editing the scheme for the application target, then adding an environment variable with the name `MAPBOX_ACCESS_TOKEN`.
![edit scheme](https://cloud.githubusercontent.com/assets/98601/5460702/c4610262-8519-11e4-873a-8597821da468.png)
![setting access token in Xcode scheme](https://cloud.githubusercontent.com/assets/162976/5349358/0a086f00-7f8c-11e4-8433-bdbaccda2b58.png)
For Linux, set the environment variable `MAPBOX_ACCESS_TOKEN` to your token.
For Android, gradle will take the value of `MAPBOX_ACCESS_TOKEN` and save it to `android/java/MapboxGLAndroidSDKTestApp/src/main/res/raw/token.txt` where the app will read it from.
# Style
Default styles in the JSON-based [styling format](https://github.com/mapbox/mapbox-gl-style-spec) are included at `./styles` as a submodule. See the [styles project](https://github.com/mapbox/mapbox-gl-styles) to report problems or to contribute to these styles.

65
LICENSE.md Normal file
View File

@@ -0,0 +1,65 @@
mapbox-gl-native copyright (c) 2014, Mapbox.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
===========================================================================
Mapbox GL uses portions of the Mapbox iOS SDK, which was derived from the
Route-Me open source project, including the Alpstein fork of it.
The Route-Me license appears below.
Copyright (c) 2008-2013, Route-Me Contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
===========================================================================
Mapbox GL uses portions of SVPulsingAnnotationView.
Copyright (c) 2013, Sam Vermette <hello@samvermette.com>
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

134
Makefile Normal file
View File

@@ -0,0 +1,134 @@
export BUILDTYPE ?= Release
export BUILD_TEST ?= 1
export BUILD_RENDER ?= 1
# Determine build platform
ifeq ($(shell uname -s), Darwin)
export BUILD = osx
export JOBS ?= $(shell sysctl -n hw.ncpu)
export XCPRETTY := $(shell ./scripts/xcpretty.sh)
else ifeq ($(shell uname -s), Linux)
export BUILD = linux
export JOBS ?= $(shell grep --count processor /proc/cpuinfo)
else
$(error Cannot determine build platform)
endif
export BUILD_VERSION = $(shell uname -m)
RUN = +@$(MAKE) -f scripts/main.mk
default: ; @printf "You must specify a valid target\n"
#### OS X targets ##############################################################
ifeq ($(BUILD),osx)
.PHONY: osx xosx run-osx run-xosx
osx: ; $(RUN) HOST=osx HOST_VERSION=x86_64 Makefile/osxapp
xosx: ; $(RUN) HOST=osx HOST_VERSION=x86_64 Xcode/osxapp
run-osx: osx ; @"build/osx-x86_64/$(BUILDTYPE)/Mapbox GL.app/Contents/MacOS/Mapbox GL"
run-xosx: xosx ; @"gyp/build/$(BUILDTYPE)/Mapbox GL.app/Contents/MacOS/Mapbox GL"
.PHONY: Xcode/osx Xcode/ios
Xcode/ios: ; $(RUN) HOST=ios Xcode/__project__
Xcode/osx: ; $(RUN) HOST=osx HOST_VERSION=x86_64 Xcode/__project__
.PHONY: xproj iproj
xproj: Xcode/osx ; @open ./build/osx-x86_64/gyp/osx.xcodeproj
iproj: Xcode/ios ; @open ./build/ios-all/gyp/ios.xcodeproj
.PHONY: ios ibench isim
ios ibench isim: export XCODEBUILD_ARGS += PROVISIONING_PROFILE="$$(PROVISIONING_PROFILE)"
ios: export XCODEBUILD_ARGS += -sdk iphoneos ARCHS="arm64 armv7 armv7s"
ios: ; $(RUN) HOST=ios Xcode/iosapp
isim: export XCODEBUILD_ARGS += -sdk iphonesimulator ARCHS="x86_64 i386"
isim: ; $(RUN) HOST=ios Xcode/iosapp
ibench: export XCODEBUILD_ARGS += -sdk iphoneos ARCHS="arm64"
ibench: ; $(RUN) HOST=ios Xcode/ios-bench
.PHONY: ipackage ipackage-strip ipackage-sim itest
ipackage: Xcode/ios ; @JOBS=$(JOBS) ./scripts/ios/package.sh
ipackage-strip: Xcode/ios ; @JOBS=$(JOBS) ./scripts/ios/package.sh strip
ipackage-sim: Xcode/ios ; @JOBS=$(JOBS) ./scripts/ios/package.sh sim
itest: ipackage-sim ; ./scripts/ios/test.sh
endif
#### All platforms targets #####################################################
.PHONY: linux run-linux run-valgrind-linux
linux: ; $(RUN) Makefile/linuxapp
run-linux: linux ; (cd build/linux-x86_64/$(BUILDTYPE) && ./mapbox-gl)
run-valgrind-linux: linux
(cd build/linux-x86_64/$(BUILDTYPE) && valgrind --leak-check=full --suppressions=../../../scripts/valgrind.sup ./mapbox-gl)
.PHONY: android android-lib
# Builds a particular android architecture.
android-lib-%: ; $(RUN) HOST=android HOST_VERSION=$* Makefile/androidapp
# Builds the default Android library
android-lib: ; $(RUN) HOST=android Makefile/androidapp
# Builds the selected/default Android library
android: android-lib
cd android/java && ./gradlew --parallel --max-workers=$(JOBS) assemble$(BUILDTYPE)
# Builds all android architectures.
android-all: android-lib-arm-v5 android-lib-arm-v7 android-lib-arm-v8
android-all: android-lib-x86 android-lib-x86-64
android-all: android-lib-mips android-lib-mips-64
cd android/java && ./gradlew --parallel-threads=$(JOBS) assemble$(BUILDTYPE)
.PHONY: test
test: ; $(RUN) Makefile/test
test-%: ; $(RUN) test-$*
ifeq ($(BUILD),osx)
xtest: ; $(RUN) HOST=osx HOST_VERSION=x86_64 Xcode/test
endif
.PHONY: render xrender
render: ; $(RUN) Makefile/mbgl-render
ifeq ($(BUILD),osx)
xrender: ; $(RUN) HOST=osx HOST_VERSION=x86_64 Xcode/mbgl-render
endif
##### Maintenace operations ####################################################
.PHONY: clear_xcode_cache
clear_xcode_cache:
ifeq ($(BUILD), osx)
@CUSTOM_DD=`defaults read com.apple.dt.Xcode IDECustomDerivedDataLocation 2>/dev/null`; \
if [ $$CUSTOM_DD ]; then \
echo clearing files in $$CUSTOM_DD older than one day; \
find $$CUSTOM_DD/mapboxgl-app-* -mtime +1 | xargs rm -rf; \
fi; \
if [ -d ~/Library/Developer/Xcode/DerivedData/ ] && [ ! $$CUSTOM_DD ]; then \
echo 'clearing files in ~/Library/Developer/Xcode/DerivedData/mapboxgl-app-* older than one day'; \
find ~/Library/Developer/Xcode/DerivedData/mapboxgl-app-* -mtime +1 | xargs rm -rf; \
fi
endif
.PHONY: clear_sqlite_cache
clear_sqlite_cache:
ifeq ($(BUILD), osx)
rm -f ~/Library/Application\ Support/Mapbox\ GL/cache.db
else
rm -f /tmp/mbgl-cache.db
endif
clean: clear_sqlite_cache clear_xcode_cache
-find ./deps/gyp -name "*.pyc" -exec rm {} \;
-rm -rf ./build/
-rm -rf ./macosx/build
-rm -rf ./linux/build
-rm -rf ./ios/build
-rm -rf ./test/build
-rm -rf ./config/*.gypi
-rm -rf ./android/java/build ./android/java/MapboxGLAndroidSDKTestApp/build ./android/java/MapboxGLAndroidSDKTestApp/build
-rm -rf ./android/java/MapboxGLAndroidSDK/src/main/jniLibs ./android/java/MapboxGLAndroidSDK/src/main/assets
-rm -f ./android/test/features.zip
distclean: clean
-rm -rf ./mason_packages

18
README.md Normal file
View File

@@ -0,0 +1,18 @@
# Mapbox GL (Native)
[![Travis](https://api.travis-ci.org/mapbox/mapbox-gl-native.svg?branch=master)](https://travis-ci.org/mapbox/mapbox-gl-native/builds)
An OpenGL ES-based renderer for [Mapbox Vector Tiles](https://www.mapbox.com/blog/vector-tiles), consisting of a C++ library for OS X and Linux and SDK bindings for iOS and Android.
**This repository is for development on Mapbox GL itself.**
If you want to use products _based on_ Mapbox GL, check out:
- [Mapbox Mobile](http://mapbox.com/mobile) — iOS and Android native development libraries for vector maps in apps
- [`node-mapbox-gl-native`](https://github.com/mapbox/node-mapbox-gl-native) — Node.js library for vector map rendering
- [`react-native-mapbox-gl`](https://github.com/mapbox/react-native-mapbox-gl`) — React Native library for vector maps in apps
- [`qmapboxgl`](https://github.com/tmpsantos/qmapboxgl) — Qt library for vector map rendering
--
See [`INSTALL.md`](./INSTALL.md) for development setup and install instructions, including dependencies.

View File

@@ -0,0 +1,13 @@
Android Gesture Detectors Framework Licence
===================================
Copyright (c) 2012, Almer Thie
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

13
android/LICENSE_LOST.txt Normal file
View File

@@ -0,0 +1,13 @@
Copyright (c) 2014 Mapzen
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

1863
android/cpp/jni.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,728 @@
#include <cstdlib>
#include <ctime>
#include <cassert>
#include <memory>
#include <list>
#include <tuple>
#include <sys/system_properties.h>
#include <GLES2/gl2.h>
#include <mbgl/android/native_map_view.hpp>
#include <mbgl/android/jni.hpp>
#include <mbgl/platform/platform.hpp>
#include <mbgl/platform/event.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/platform/gl.hpp>
#include <mbgl/util/constants.hpp>
namespace mbgl {
namespace android {
void log_egl_string(EGLDisplay display, EGLint name, const char *label) {
const char *str = eglQueryString(display, name);
if (str == nullptr) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglQueryString(%d) returned error %d", name,
eglGetError());
throw new std::runtime_error("eglQueryString() failed");
} else {
char buf[513];
for (int len = std::strlen(str), pos = 0; len > 0; len -= 512, pos += 512) {
strncpy(buf, str + pos, 512);
buf[512] = 0;
mbgl::Log::Info(mbgl::Event::OpenGL, "EGL %s: %s", label, buf);
}
}
}
void log_gl_string(GLenum name, const char *label) {
const GLubyte *str = glGetString(name);
if (str == nullptr) {
mbgl::Log::Error(mbgl::Event::OpenGL, "glGetString(%d) returned error %d", name,
glGetError());
throw new std::runtime_error("glGetString() failed");
} else {
char buf[513];
for (int len = std::strlen(reinterpret_cast<const char *>(str)), pos = 0; len > 0;
len -= 512, pos += 512) {
strncpy(buf, reinterpret_cast<const char *>(str) + pos, 512);
buf[512] = 0;
mbgl::Log::Info(mbgl::Event::OpenGL, "GL %s: %s", label, buf);
}
}
}
NativeMapView::NativeMapView(JNIEnv *env, jobject obj_, float pixelRatio_, int availableProcessors_, size_t totalMemory_)
: mbgl::View(*this),
pixelRatio(pixelRatio_),
availableProcessors(availableProcessors_),
totalMemory(totalMemory_),
fileCache(mbgl::android::cachePath + "/mbgl-cache.db"),
fileSource(&fileCache),
map(*this, fileSource, MapMode::Continuous) {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::NativeMapView");
map.pause();
assert(env != nullptr);
assert(obj_ != nullptr);
if (env->GetJavaVM(&vm) < 0) {
env->ExceptionDescribe();
return;
}
obj = env->NewGlobalRef(obj_);
if (obj == nullptr) {
env->ExceptionDescribe();
return;
}
}
NativeMapView::~NativeMapView() {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::~NativeMapView");
terminateContext();
destroySurface();
terminateDisplay();
assert(vm != nullptr);
assert(obj != nullptr);
jint ret;
JNIEnv *env = nullptr;
ret = vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
if (ret == JNI_OK) {
env->DeleteGlobalRef(obj);
} else {
mbgl::Log::Error(mbgl::Event::JNI, "GetEnv() failed with %i", ret);
throw new std::runtime_error("GetEnv() failed");
}
obj = nullptr;
vm = nullptr;
}
float NativeMapView::getPixelRatio() const {
return pixelRatio;
}
std::array<uint16_t, 2> NativeMapView::getSize() const {
return {{ static_cast<uint16_t>(width), static_cast<uint16_t>(height) }};
}
std::array<uint16_t, 2> NativeMapView::getFramebufferSize() const {
return {{ static_cast<uint16_t>(fbWidth), static_cast<uint16_t>(fbHeight) }};
}
void NativeMapView::activate() {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::activate");
if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE) && (context != EGL_NO_CONTEXT)) {
if (!eglMakeCurrent(display, surface, surface, context)) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent() returned error %d",
eglGetError());
throw new std::runtime_error("eglMakeCurrent() failed");
}
} else {
mbgl::Log::Info(mbgl::Event::Android, "Not activating as we are not ready");
}
}
void NativeMapView::deactivate() {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::deactivate");
if (display != EGL_NO_DISPLAY) {
if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent(EGL_NO_CONTEXT) returned error %d",
eglGetError());
throw new std::runtime_error("eglMakeCurrent() failed");
}
} else {
mbgl::Log::Info(mbgl::Event::Android, "Not deactivating as we are not ready");
}
}
void NativeMapView::invalidate() {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::invalidate()");
clean.clear();
assert(vm != nullptr);
assert(obj != nullptr);
JNIEnv *env = nullptr;
bool detach = attach_jni_thread(vm, &env, "NativeMapView::invalidate()");
env->CallVoidMethod(obj, onInvalidateId);
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
}
detach_jni_thread(vm, &env, detach);
}
void NativeMapView::swap() {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::swap");
if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE)) {
if (!eglSwapBuffers(display, surface)) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglSwapBuffers() returned error %d",
eglGetError());
throw new std::runtime_error("eglSwapBuffers() failed");
}
updateFps();
} else {
mbgl::Log::Info(mbgl::Event::Android, "Not swapping as we are not ready");
}
}
void NativeMapView::notify() {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::notify()");
// noop
}
mbgl::Map &NativeMapView::getMap() { return map; }
mbgl::DefaultFileSource &NativeMapView::getFileSource() { return fileSource; }
bool NativeMapView::inEmulator() {
// Detect if we are in emulator
char prop[PROP_VALUE_MAX];
__system_property_get("ro.kernel.qemu", prop);
return strtol(prop, nullptr, 0) == 1;
}
void NativeMapView::initializeDisplay() {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::initializeDisplay");
assert(display == EGL_NO_DISPLAY);
assert(config == nullptr);
assert(format < 0);
display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (display == EGL_NO_DISPLAY) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglGetDisplay() returned error %d", eglGetError());
throw new std::runtime_error("eglGetDisplay() failed");
}
EGLint major, minor;
if (!eglInitialize(display, &major, &minor)) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglInitialize() returned error %d", eglGetError());
throw new std::runtime_error("eglInitialize() failed");
}
if ((major <= 1) && (minor < 3)) {
mbgl::Log::Error(mbgl::Event::OpenGL, "EGL version is too low, need 1.3, got %d.%d", major,
minor);
throw new std::runtime_error("EGL version is too low");
}
log_egl_string(display, EGL_VENDOR, "Vendor");
log_egl_string(display, EGL_VERSION, "Version");
log_egl_string(display, EGL_CLIENT_APIS, "Client APIs");
log_egl_string(display, EGL_EXTENSIONS, "Client Extensions");
// Detect if we are in emulator
if (inEmulator()) {
mbgl::Log::Warning(mbgl::Event::Android, "In emulator! Enabling hacks :-(");
}
// Get all configs at least RGB 565 with 16 depth and 8 stencil
EGLint configAttribs[] = {
EGL_CONFIG_CAVEAT, EGL_NONE, EGL_RENDERABLE_TYPE,
EGL_OPENGL_ES2_BIT, EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_BUFFER_SIZE, 16, EGL_RED_SIZE,
5, EGL_GREEN_SIZE, 6,
EGL_BLUE_SIZE, 5, EGL_DEPTH_SIZE,
16, EGL_STENCIL_SIZE, 8,
(inEmulator() ? EGL_NONE : EGL_CONFORMANT), EGL_OPENGL_ES2_BIT, // Ugly hack
(inEmulator() ? EGL_NONE : EGL_COLOR_BUFFER_TYPE), EGL_RGB_BUFFER, // Ugly hack
EGL_NONE};
EGLint numConfigs;
if (!eglChooseConfig(display, configAttribs, nullptr, 0, &numConfigs)) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglChooseConfig(NULL) returned error %d",
eglGetError());
throw new std::runtime_error("eglChooseConfig() failed");
}
if (numConfigs < 1) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglChooseConfig() returned no configs.");
throw new std::runtime_error("eglChooseConfig() failed");
}
const auto configs = std::make_unique<EGLConfig[]>(numConfigs);
if (!eglChooseConfig(display, configAttribs, configs.get(), numConfigs, &numConfigs)) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglChooseConfig() returned error %d", eglGetError());
throw new std::runtime_error("eglChooseConfig() failed");
}
config = chooseConfig(configs.get(), numConfigs);
if (config == nullptr) {
mbgl::Log::Error(mbgl::Event::OpenGL, "No config chosen");
throw new std::runtime_error("No config chosen");
}
if (!eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format)) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglGetConfigAttrib() returned error %d",
eglGetError());
throw new std::runtime_error("eglGetConfigAttrib() failed");
}
mbgl::Log::Info(mbgl::Event::OpenGL, "Chosen window format is %d", format);
}
void NativeMapView::terminateDisplay() {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::terminateDisplay");
if (display != EGL_NO_DISPLAY) {
// Destroy the surface first, if it still exists. This call needs a valid surface.
if (surface != EGL_NO_SURFACE) {
if (!eglDestroySurface(display, surface)) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglDestroySurface() returned error %d",
eglGetError());
throw new std::runtime_error("eglDestroySurface() failed");
}
surface = EGL_NO_SURFACE;
}
if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
mbgl::Log::Error(mbgl::Event::OpenGL,
"eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", eglGetError());
throw new std::runtime_error("eglMakeCurrent() failed");
}
if (!eglTerminate(display)) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglTerminate() returned error %d",
eglGetError());
throw new std::runtime_error("eglTerminate() failed");
}
}
display = EGL_NO_DISPLAY;
config = nullptr;
format = -1;
}
void NativeMapView::initializeContext() {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::initializeContext");
assert(display != EGL_NO_DISPLAY);
assert(context == EGL_NO_CONTEXT);
assert(config != nullptr);
const EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs);
if (context == EGL_NO_CONTEXT) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglCreateContext() returned error %d",
eglGetError());
throw new std::runtime_error("eglCreateContext() failed");
}
}
void NativeMapView::terminateContext() {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::terminateContext");
if (display != EGL_NO_DISPLAY) {
if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
mbgl::Log::Error(mbgl::Event::OpenGL,
"eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", eglGetError());
throw new std::runtime_error("eglMakeCurrent() failed");
}
if (context != EGL_NO_CONTEXT) {
if (!eglDestroyContext(display, context)) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglDestroyContext() returned error %d",
eglGetError());
throw new std::runtime_error("eglDestroyContext() failed");
}
}
}
context = EGL_NO_CONTEXT;
}
void NativeMapView::createSurface(ANativeWindow *window_) {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::createSurface");
assert(window == nullptr);
assert(window_ != nullptr);
window = window_;
assert(display != EGL_NO_DISPLAY);
assert(surface == EGL_NO_SURFACE);
assert(config != nullptr);
assert(format >= 0);
ANativeWindow_setBuffersGeometry(window, 0, 0, format);
const EGLint surfaceAttribs[] = {EGL_NONE};
surface = eglCreateWindowSurface(display, config, window, surfaceAttribs);
if (surface == EGL_NO_SURFACE) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglCreateWindowSurface() returned error %d",
eglGetError());
throw new std::runtime_error("eglCreateWindowSurface() failed");
}
if (!firstTime) {
firstTime = true;
if (!eglMakeCurrent(display, surface, surface, context)) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent() returned error %d",
eglGetError());
throw new std::runtime_error("eglMakeCurrent() failed");
}
log_gl_string(GL_VENDOR, "Vendor");
log_gl_string(GL_RENDERER, "Renderer");
log_gl_string(GL_VERSION, "Version");
if (!inEmulator()) {
log_gl_string(GL_SHADING_LANGUAGE_VERSION,
"SL Version"); // In the emulator this returns NULL with error code 0?
// https://code.google.com/p/android/issues/detail?id=78977
}
log_gl_string(GL_EXTENSIONS, "Extensions");
mbgl::gl::InitializeExtensions([] (const char * name) {
return reinterpret_cast<mbgl::gl::glProc>(eglGetProcAddress(name));
});
if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
mbgl::Log::Error(mbgl::Event::OpenGL,
"eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", eglGetError());
throw new std::runtime_error("eglMakeCurrent() failed");
}
}
resume();
}
void NativeMapView::destroySurface() {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::destroySurface");
pause();
if (surface != EGL_NO_SURFACE) {
if (!eglDestroySurface(display, surface)) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglDestroySurface() returned error %d",
eglGetError());
throw new std::runtime_error("eglDestroySurface() failed");
}
}
surface = EGL_NO_SURFACE;
if (window != nullptr) {
ANativeWindow_release(window);
window = nullptr;
}
}
// Speed
/*
typedef enum {
Format16Bit = 0,
Format32BitNoAlpha = 1,
Format32BitAlpha = 2,
Format24Bit = 3,
Unknown = 4
} BufferFormat;
typedef enum {
Format16Depth8Stencil = 0,
Format24Depth8Stencil = 1,
} DepthStencilFormat;
*/
// Quality
typedef enum {
Format16Bit = 3,
Format32BitNoAlpha = 1,
Format32BitAlpha = 2,
Format24Bit = 0,
Unknown = 4
} BufferFormat;
typedef enum {
Format16Depth8Stencil = 1,
Format24Depth8Stencil = 0,
} DepthStencilFormat;
// Tuple is <buffer_format, depth_stencil_format, is_not_conformant, is_caveat, config_num,
// config_id>
typedef std::tuple<BufferFormat, DepthStencilFormat, bool, bool, int, EGLConfig> ConfigProperties;
EGLConfig NativeMapView::chooseConfig(const EGLConfig configs[], EGLint numConfigs) {
mbgl::Log::Info(mbgl::Event::OpenGL, "Found %d configs", numConfigs);
// Create a list of configs that pass our filters
std::list<ConfigProperties> configList;
for (int i = 0; i < numConfigs; i++) {
mbgl::Log::Info(mbgl::Event::OpenGL, "Config %d:", i);
EGLint caveat, conformant, bits, red, green, blue, alpha, alphaMask, depth, stencil,
sampleBuffers, samples;
if (!eglGetConfigAttrib(display, configs[i], EGL_CONFIG_CAVEAT, &caveat)) {
mbgl::Log::Error(mbgl::Event::OpenGL,
"eglGetConfigAttrib(EGL_CONFIG_CAVEAT) returned error %d",
eglGetError());
throw new std::runtime_error("eglGetConfigAttrib() failed");
}
if (!eglGetConfigAttrib(display, configs[i], EGL_CONFORMANT, &conformant)) {
mbgl::Log::Error(mbgl::Event::OpenGL,
"eglGetConfigAttrib(EGL_CONFORMANT) returned error %d", eglGetError());
throw new std::runtime_error("eglGetConfigAttrib() failed");
}
if (!eglGetConfigAttrib(display, configs[i], EGL_BUFFER_SIZE, &bits)) {
mbgl::Log::Error(mbgl::Event::OpenGL,
"eglGetConfigAttrib(EGL_BUFFER_SIZE) returned error %d",
eglGetError());
throw new std::runtime_error("eglGetConfigAttrib() failed");
}
if (!eglGetConfigAttrib(display, configs[i], EGL_RED_SIZE, &red)) {
mbgl::Log::Error(mbgl::Event::OpenGL,
"eglGetConfigAttrib(EGL_RED_SIZE) returned error %d", eglGetError());
throw new std::runtime_error("eglGetConfigAttrib() failed");
}
if (!eglGetConfigAttrib(display, configs[i], EGL_GREEN_SIZE, &green)) {
mbgl::Log::Error(mbgl::Event::OpenGL,
"eglGetConfigAttrib(EGL_GREEN_SIZE) returned error %d", eglGetError());
throw new std::runtime_error("eglGetConfigAttrib() failed");
}
if (!eglGetConfigAttrib(display, configs[i], EGL_BLUE_SIZE, &blue)) {
mbgl::Log::Error(mbgl::Event::OpenGL,
"eglGetConfigAttrib(EGL_BLUE_SIZE) returned error %d", eglGetError());
throw new std::runtime_error("eglGetConfigAttrib() failed");
}
if (!eglGetConfigAttrib(display, configs[i], EGL_ALPHA_SIZE, &alpha)) {
mbgl::Log::Error(mbgl::Event::OpenGL,
"eglGetConfigAttrib(EGL_ALPHA_SIZE) returned error %d", eglGetError());
throw new std::runtime_error("eglGetConfigAttrib() failed");
}
if (!eglGetConfigAttrib(display, configs[i], EGL_ALPHA_MASK_SIZE, &alphaMask)) {
mbgl::Log::Error(mbgl::Event::OpenGL,
"eglGetConfigAttrib(EGL_ALPHA_MASK_SIZE) returned error %d",
eglGetError());
throw new std::runtime_error("eglGetConfigAttrib() failed");
}
if (!eglGetConfigAttrib(display, configs[i], EGL_DEPTH_SIZE, &depth)) {
mbgl::Log::Error(mbgl::Event::OpenGL,
"eglGetConfigAttrib(EGL_DEPTH_SIZE) returned error %d", eglGetError());
throw new std::runtime_error("eglGetConfigAttrib() failed");
}
if (!eglGetConfigAttrib(display, configs[i], EGL_STENCIL_SIZE, &stencil)) {
mbgl::Log::Error(mbgl::Event::OpenGL,
"eglGetConfigAttrib(EGL_STENCIL_SIZE) returned error %d",
eglGetError());
throw new std::runtime_error("eglGetConfigAttrib() failed");
}
if (!eglGetConfigAttrib(display, configs[i], EGL_SAMPLE_BUFFERS, &sampleBuffers)) {
mbgl::Log::Error(mbgl::Event::OpenGL,
"eglGetConfigAttrib(EGL_SAMPLE_BUFFERS) returned error %d",
eglGetError());
throw new std::runtime_error("eglGetConfigAttrib() failed");
}
if (!eglGetConfigAttrib(display, configs[i], EGL_SAMPLES, &samples)) {
mbgl::Log::Error(mbgl::Event::OpenGL,
"eglGetConfigAttrib(EGL_SAMPLES) returned error %d", eglGetError());
throw new std::runtime_error("eglGetConfigAttrib() failed");
}
mbgl::Log::Info(mbgl::Event::OpenGL, "...Caveat: %d", caveat);
mbgl::Log::Info(mbgl::Event::OpenGL, "...Conformant: %d", conformant);
mbgl::Log::Info(mbgl::Event::OpenGL, "...Color: %d", bits);
mbgl::Log::Info(mbgl::Event::OpenGL, "...Red: %d", red);
mbgl::Log::Info(mbgl::Event::OpenGL, "...Green: %d", green);
mbgl::Log::Info(mbgl::Event::OpenGL, "...Blue: %d", blue);
mbgl::Log::Info(mbgl::Event::OpenGL, "...Alpha: %d", alpha);
mbgl::Log::Info(mbgl::Event::OpenGL, "...Alpha mask: %d", alphaMask);
mbgl::Log::Info(mbgl::Event::OpenGL, "...Depth: %d", depth);
mbgl::Log::Info(mbgl::Event::OpenGL, "...Stencil: %d", stencil);
mbgl::Log::Info(mbgl::Event::OpenGL, "...Sample buffers: %d", sampleBuffers);
mbgl::Log::Info(mbgl::Event::OpenGL, "...Samples: %d", samples);
bool configOk = true;
configOk &= (depth == 24) || (depth == 16);
configOk &= stencil == 8;
configOk &= sampleBuffers == 0;
configOk &= samples == 0;
// Filter our configs first for depth, stencil and anti-aliasing
if (configOk) {
// Work out the config's buffer format
BufferFormat bufferFormat;
if ((bits == 16) && (red == 5) && (green == 6) && (blue == 5) && (alpha == 0)) {
bufferFormat = Format16Bit;
} else if ((bits == 32) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 0)) {
bufferFormat = Format32BitNoAlpha;
} else if ((bits == 32) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 8)) {
bufferFormat = Format32BitAlpha;
} else if ((bits == 24) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 0)) {
bufferFormat = Format24Bit;
} else {
bufferFormat = Unknown;
}
// Work out the config's depth stencil format
DepthStencilFormat depthStencilFormat;
if ((depth == 16) && (stencil == 8)) {
depthStencilFormat = Format16Depth8Stencil;
} else {
depthStencilFormat = Format24Depth8Stencil;
}
bool isNotConformant = (conformant & EGL_OPENGL_ES2_BIT) != EGL_OPENGL_ES2_BIT;
bool isCaveat = caveat != EGL_NONE;
EGLConfig configId = configs[i];
// Ignore formats we don't recognise
if (bufferFormat != Unknown) {
configList.push_back(std::make_tuple(bufferFormat, depthStencilFormat,
isNotConformant, isCaveat, i, configId));
}
}
}
if (configList.empty()) {
mbgl::Log::Error(mbgl::Event::OpenGL, "Config list was empty.");
}
// Sort the configs to find the best one
configList.sort();
bool isConformant = !std::get<2>(configList.front());
bool isCaveat = std::get<3>(configList.front());
int configNum = std::get<4>(configList.front());
EGLConfig configId = std::get<5>(configList.front());
mbgl::Log::Info(mbgl::Event::OpenGL, "Chosen config is %d", configNum);
if (isCaveat) {
mbgl::Log::Warning(mbgl::Event::OpenGL, "Chosen config has a caveat.");
}
if (!isConformant) {
mbgl::Log::Warning(mbgl::Event::OpenGL, "Chosen config is not conformant.");
}
return configId;
}
void NativeMapView::pause() {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::pause");
if ((display != EGL_NO_DISPLAY) && (context != EGL_NO_CONTEXT)) {
map.pause();
}
}
void NativeMapView::resume() {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::resume");
assert(display != EGL_NO_DISPLAY);
assert(context != EGL_NO_CONTEXT);
if (surface != EGL_NO_SURFACE) {
map.resume();
} else {
mbgl::Log::Debug(mbgl::Event::Android, "Not resuming because we are not ready");
}
}
void NativeMapView::notifyMapChange(mbgl::MapChange) {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::notifyMapChange()");
assert(vm != nullptr);
assert(obj != nullptr);
JNIEnv *env = nullptr;
bool detach = attach_jni_thread(vm, &env, "NativeMapView::notifyMapChange()");
env->CallVoidMethod(obj, onMapChangedId);
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
}
detach_jni_thread(vm, &env, detach);
}
void NativeMapView::enableFps(bool enable) {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::enableFps()");
fpsEnabled = enable;
}
void NativeMapView::updateFps() {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::updateFps()");
if (!fpsEnabled) {
return;
}
static int frames = 0;
static int64_t timeElapsed = 0LL;
frames++;
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
int64_t currentTime = now.tv_sec * 1000000000LL + now.tv_nsec;
if (currentTime - timeElapsed >= 1) {
fps = frames / ((currentTime - timeElapsed) / 1E9);
mbgl::Log::Debug(mbgl::Event::Render, "FPS: %4.2f", fps);
timeElapsed = currentTime;
frames = 0;
}
assert(vm != nullptr);
assert(obj != nullptr);
JNIEnv *env = nullptr;
bool detach = attach_jni_thread(vm, &env, "NativeMapView::updateFps()");
env->CallVoidMethod(obj, onFpsChangedId, fps);
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
}
detach_jni_thread(vm, &env, detach);
}
void NativeMapView::onInvalidate() {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::onInvalidate()");
const bool dirty = !clean.test_and_set();
if (dirty) {
float zoomFactor = map.getMaxZoom() - map.getMinZoom() + 1;
float cpuFactor = availableProcessors;
float memoryFactor = static_cast<float>(totalMemory) / 1000.0f / 1000.0f / 1000.0f;
float sizeFactor = (static_cast<float>(map.getWidth()) / mbgl::util::tileSize) *
(static_cast<float>(map.getHeight()) / mbgl::util::tileSize);
size_t cacheSize = zoomFactor * cpuFactor * memoryFactor * sizeFactor * 0.5f;
map.setSourceTileCacheSize(cacheSize);
map.renderSync();
map.nudgeTransitions();
}
}
void NativeMapView::resizeView(int w, int h) {
width = w;
height = h;
map.update(mbgl::Update::Dimensions);
}
void NativeMapView::resizeFramebuffer(int w, int h) {
fbWidth = w;
fbHeight = h;
map.update(mbgl::Update::Repaint);
}
}
}

23
android/java/.gitignore vendored Normal file
View File

@@ -0,0 +1,23 @@
# Gradle files
.gradle/
# IntelliJ files
.idea/
*.iml
# Build files
build/
*.so
*.apk
# Lib assets
MapboxGLAndroidSDK/src/main/assets/
# Local settings
local.properties
# Token file
MapboxGLAndroidSDKTestApp/src/main/res/raw/token.txt
# Twitter Fabric / Crashlytics
fabric.properties

View File

@@ -0,0 +1,256 @@
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.3.0'
classpath 'com.jakewharton.sdkmanager:gradle-plugin:0.12.0'
}
}
apply plugin: 'android-sdk-manager'
apply plugin: 'com.android.library'
apply plugin: 'checkstyle'
apply plugin: 'maven'
apply plugin: 'signing'
allprojects {
group project.GROUP
version project.VERSION_NAME
repositories {
mavenCentral()
}
}
repositories {
mavenCentral()
}
dependencies {
compile 'com.android.support:support-annotations:23.0.0'
compile 'com.android.support:support-v4:23.0.0'
compile 'com.android.support:design:23.0.0'
compile 'com.squareup.okhttp:okhttp:2.4.0'
compile 'com.mapzen.android:lost:1.0.1'
}
android {
compileSdkVersion Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION)
buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION
defaultConfig {
minSdkVersion Integer.parseInt(project.ANDROID_MIN_SDK)
targetSdkVersion Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION)
}
repositories {
mavenCentral()
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
lintOptions {
checkAllWarnings true
warningsAsErrors true
disable 'InvalidPackage'
}
buildTypes {
debug {
jniDebuggable true
}
release {
minifyEnabled false
jniDebuggable false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
}
}
configurations {
all*.exclude group: 'commons-logging', module: 'commons-logging'
all*.exclude group: 'commons-collections', module: 'commons-collections'
}
android.libraryVariants.all { variant ->
def name = variant.name
task "javadoc$name"(type: Javadoc) {
description = "Generates javadoc for build $name"
failOnError = false
destinationDir = new File(destinationDir, variant.baseName)
source = files(variant.javaCompile.source)
classpath = files(variant.javaCompile.classpath.files) + files(android.bootClasspath)
options.links("http://docs.oracle.com/javase/7/docs/api/")
options.links("http://d.android.com/reference/")
exclude '**/R.html', '**/R.*.html'
}
}
checkstyle {
configFile project.file('../checks.xml')
showViolations true
}
/*
task cleanJNIBuilds {
def jniLibsDir = new File("MapboxGLAndroidSDK/src/main/jniLibs")
delete jniLibsDir.absolutePath
}
*/
android.libraryVariants.all { variant ->
def name = variant.buildType.name
def checkstyle = project.tasks.create "checkstyle${name.capitalize()}", Checkstyle
checkstyle.dependsOn variant.javaCompile
checkstyle.source variant.javaCompile.source
checkstyle.classpath = project.fileTree(variant.javaCompile.destinationDir)
checkstyle.exclude('**/BuildConfig.java')
checkstyle.exclude('**/R.java')
checkstyle.exclude('**/com/almeros/android/multitouch/**')
project.tasks.getByName("check").dependsOn checkstyle
}
// From https://raw.github.com/mcxiaoke/gradle-mvn-push/master/jar.gradle
android.libraryVariants.all { variant ->
def jarTask = project.tasks.create(name: "jar${variant.name.capitalize()}", type: Jar) {
from variant.javaCompile.destinationDir
exclude "**/R.class"
exclude "**/BuildConfig.class"
}
jarTask.dependsOn variant.javaCompile
artifacts.add('archives', jarTask);
}
// From https://raw.github.com/mcxiaoke/gradle-mvn-push/master/gradle-mvn-push.gradle
def isReleaseBuild() {
return VERSION_NAME.contains("SNAPSHOT") == false
}
def getReleaseRepositoryUrl() {
return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL :
"https://oss.sonatype.org/service/local/staging/deploy/maven2/"
}
def getSnapshotRepositoryUrl() {
return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL :
"https://oss.sonatype.org/content/repositories/snapshots/"
}
def getRepositoryUsername() {
return hasProperty('USERNAME') ? USERNAME :
(hasProperty('NEXUS_USERNAME') ? NEXUS_USERNAME : "")
}
def getRepositoryPassword() {
return hasProperty('PASSWORD') ? PASSWORD :
(hasProperty('NEXUS_PASSWORD') ? NEXUS_PASSWORD : "")
}
task apklib(type: Zip) {
appendix = extension = 'apklib'
from 'AndroidManifest.xml'
into('res') {
from 'res'
}
into('src') {
from 'src'
}
}
artifacts {
archives apklib
}
afterEvaluate { project ->
uploadArchives {
repositories {
mavenDeployer {
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
pom.groupId = GROUP
pom.artifactId = POM_ARTIFACT_ID
pom.version = VERSION_NAME
repository(url: getReleaseRepositoryUrl()) {
authentication(userName: getRepositoryUsername(),
password: getRepositoryPassword())
}
snapshotRepository(url: getSnapshotRepositoryUrl()) {
authentication(userName: getRepositoryUsername(),
password: getRepositoryPassword())
}
/*
// Leaving out as artifact was incorrectly named when found
addFilter('aar') { artifact, file ->
artifact.name == archivesBaseName
}
addFilter('apklib') { artifact, file ->
artifact.name == archivesBaseName + '-apklib'
}
*/
pom.project {
name POM_NAME
packaging POM_PACKAGING
description POM_DESCRIPTION
url POM_URL
scm {
url POM_SCM_URL
connection POM_SCM_CONNECTION
developerConnection POM_SCM_DEV_CONNECTION
}
licenses {
license {
name POM_LICENCE_NAME
url POM_LICENCE_URL
distribution POM_LICENCE_DIST
}
}
developers {
developer {
id POM_DEVELOPER_ID
name POM_DEVELOPER_NAME
}
}
}
}
}
}
signing {
required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") }
sign configurations.archives
}
task androidJavadocs(type: Javadoc) {
source = android.sourceSets.main.java.sourceFiles
classpath = files(android.bootClasspath)
}
task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) {
classifier = 'javadoc'
from androidJavadocs.destinationDir
}
task androidSourcesJar(type: Jar) {
classifier = 'sources'
from android.sourceSets.main.java.sourceFiles
}
artifacts {
archives androidSourcesJar
archives androidJavadocsJar
}
}

View File

@@ -0,0 +1,2 @@
#!/usr/bin/env bash
../gradlew -b build.gradle clean assembleRelease uploadArchives

View File

@@ -0,0 +1,22 @@
GROUP=com.mapbox.mapboxgl
VERSION_NAME=0.0.1-SNAPSHOT
POM_DESCRIPTION=Mapbox GL Android SDK
POM_URL=https://github.com/mapbox/mapbox-gl-native
POM_SCM_URL=https://github.com/mapbox/mapbox-gl-native
POM_SCM_CONNECTION=scm:git@github.com:mapbox/mapbox-gl-native.git
POM_SCM_DEV_CONNECTION=scm:git@github.com:mapbox/mapbox-gl-native.git
POM_LICENCE_NAME=The Apache Software License, Version 2.0
POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
POM_LICENCE_DIST=repo
POM_DEVELOPER_ID=mapbox
POM_DEVELOPER_NAME=Mapbox
ANDROID_MIN_SDK=8
ANDROID_BUILD_TARGET_SDK_VERSION=23
ANDROID_BUILD_TOOLS_VERSION=23
ANDROID_BUILD_SDK_VERSION=23
POM_NAME=Mapbox GL Android SDK
POM_ARTIFACT_ID=mapbox-gl-android-sdk
POM_PACKAGING=aar

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<lint>
<!-- Ignore errors caused by OkHTTP -->
<issue id="InvalidPackage" severity="ignore">
<ignore path="*/okio-1.2.0.jar" />
</issue>
</lint>

View File

@@ -0,0 +1,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mapbox.mapboxgl.views">
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
</manifest>

View File

@@ -0,0 +1,161 @@
package com.almeros.android.multitouch.gesturedetectors;
import android.content.Context;
import android.view.MotionEvent;
/**
* @author Almer Thie (code.almeros.com) Copyright (c) 2013, Almer Thie
* (code.almeros.com)
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
public abstract class BaseGestureDetector {
protected final Context mContext;
protected boolean mGestureInProgress;
protected MotionEvent mPrevEvent;
protected MotionEvent mCurrEvent;
protected float mCurrPressure;
protected float mPrevPressure;
protected long mTimeDelta;
/**
* This value is the threshold ratio between the previous combined pressure
* and the current combined pressure. When pressure decreases rapidly
* between events the position values can often be imprecise, as it usually
* indicates that the user is in the process of lifting a pointer off of the
* device. This value was tuned experimentally.
*/
protected static final float PRESSURE_THRESHOLD = 0.67f;
public BaseGestureDetector(Context context) {
mContext = context;
}
/**
* All gesture detectors need to be called through this method to be able to
* detect gestures. This method delegates work to handler methods
* (handleStartProgressEvent, handleInProgressEvent) implemented in
* extending classes.
*
* @param event MotionEvent
* @return {@code true} as handled
*/
public boolean onTouchEvent(MotionEvent event) {
final int actionCode = event.getAction() & MotionEvent.ACTION_MASK;
if (!mGestureInProgress) {
handleStartProgressEvent(actionCode, event);
} else {
handleInProgressEvent(actionCode, event);
}
return true;
}
/**
* Called when the current event occurred when NO gesture is in progress
* yet. The handling in this implementation may set the gesture in progress
* (via mGestureInProgress) or out of progress
*
* @param actionCode Action Code from MotionEvent
* @param event MotionEvent
*/
protected abstract void handleStartProgressEvent(int actionCode,
MotionEvent event);
/**
* Called when the current event occurred when a gesture IS in progress. The
* handling in this implementation may set the gesture out of progress (via
* mGestureInProgress).
*
*
* @param actionCode Action Code from MotionEvent
* @param event MotionEvent
*/
protected abstract void handleInProgressEvent(int actionCode,
MotionEvent event);
protected void updateStateByEvent(MotionEvent curr) {
final MotionEvent prev = mPrevEvent;
// Reset mCurrEvent
if (mCurrEvent != null) {
mCurrEvent.recycle();
mCurrEvent = null;
}
mCurrEvent = MotionEvent.obtain(curr);
// Delta time
mTimeDelta = curr.getEventTime() - prev.getEventTime();
// Pressure
mCurrPressure = curr.getPressure(curr.getActionIndex());
mPrevPressure = prev.getPressure(prev.getActionIndex());
}
protected void resetState() {
if (mPrevEvent != null) {
mPrevEvent.recycle();
mPrevEvent = null;
}
if (mCurrEvent != null) {
mCurrEvent.recycle();
mCurrEvent = null;
}
mGestureInProgress = false;
}
/**
* Returns {@code true} if a gesture is currently in progress.
*
* @return {@code true} if a gesture is currently in progress, {@code false}
* otherwise.
*/
public boolean isInProgress() {
return mGestureInProgress;
}
/**
* Return the time difference in milliseconds between the previous accepted
* GestureDetector event and the current GestureDetector event.
*
* @return Time difference since the last move event in milliseconds.
*/
public long getTimeDelta() {
return mTimeDelta;
}
/**
* Return the event time of the current GestureDetector event being
* processed.
*
* @return Current GestureDetector event time in milliseconds.
*/
public long getEventTime() {
return mCurrEvent.getEventTime();
}
}

View File

@@ -0,0 +1,185 @@
package com.almeros.android.multitouch.gesturedetectors;
import android.content.Context;
import android.graphics.PointF;
import android.view.MotionEvent;
/**
* @author Almer Thie (code.almeros.com) Copyright (c) 2013, Almer Thie
* (code.almeros.com)
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
public class MoveGestureDetector extends BaseGestureDetector {
/**
* Listener which must be implemented which is used by MoveGestureDetector
* to perform callbacks to any implementing class which is registered to a
* MoveGestureDetector via the constructor.
*
* @see MoveGestureDetector.SimpleOnMoveGestureListener
*/
public interface OnMoveGestureListener {
public boolean onMove(MoveGestureDetector detector);
public boolean onMoveBegin(MoveGestureDetector detector);
public void onMoveEnd(MoveGestureDetector detector);
}
/**
* Helper class which may be extended and where the methods may be
* implemented. This way it is not necessary to implement all methods of
* OnMoveGestureListener.
*/
public static class SimpleOnMoveGestureListener implements
OnMoveGestureListener {
public boolean onMove(MoveGestureDetector detector) {
return false;
}
public boolean onMoveBegin(MoveGestureDetector detector) {
return true;
}
public void onMoveEnd(MoveGestureDetector detector) {
// Do nothing, overridden implementation may be used
}
}
private static final PointF FOCUS_DELTA_ZERO = new PointF();
private final OnMoveGestureListener mListener;
private PointF mFocusExternal = new PointF();
private PointF mFocusDeltaExternal = new PointF();
public MoveGestureDetector(Context context, OnMoveGestureListener listener) {
super(context);
mListener = listener;
}
@Override
protected void handleStartProgressEvent(int actionCode, MotionEvent event) {
switch (actionCode) {
case MotionEvent.ACTION_DOWN:
resetState(); // In case we missed an UP/CANCEL event
mPrevEvent = MotionEvent.obtain(event);
mTimeDelta = 0;
updateStateByEvent(event);
break;
case MotionEvent.ACTION_MOVE:
mGestureInProgress = mListener.onMoveBegin(this);
break;
}
}
@Override
protected void handleInProgressEvent(int actionCode, MotionEvent event) {
switch (actionCode) {
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mListener.onMoveEnd(this);
resetState();
break;
case MotionEvent.ACTION_MOVE:
updateStateByEvent(event);
// Only accept the event if our relative pressure is within
// a certain limit. This can help filter shaky data as a
// finger is lifted.
if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD) {
final boolean updatePrevious = mListener.onMove(this);
if (updatePrevious) {
mPrevEvent.recycle();
mPrevEvent = MotionEvent.obtain(event);
}
}
break;
}
}
protected void updateStateByEvent(MotionEvent curr) {
super.updateStateByEvent(curr);
final MotionEvent prev = mPrevEvent;
// Focus intenal
PointF mCurrFocusInternal = determineFocalPoint(curr);
PointF mPrevFocusInternal = determineFocalPoint(prev);
// Focus external
// - Prevent skipping of focus delta when a finger is added or removed
boolean mSkipNextMoveEvent = prev.getPointerCount() != curr
.getPointerCount();
mFocusDeltaExternal = mSkipNextMoveEvent ? FOCUS_DELTA_ZERO
: new PointF(mCurrFocusInternal.x - mPrevFocusInternal.x,
mCurrFocusInternal.y - mPrevFocusInternal.y);
// - Don't directly use mFocusInternal (or skipping will occur). Add
// unskipped delta values to mFocusExternal instead.
mFocusExternal.x += mFocusDeltaExternal.x;
mFocusExternal.y += mFocusDeltaExternal.y;
}
/**
* Determine (multi)finger focal point (a.k.a. center point between all
* fingers)
*
* @param e
* @return PointF focal point
*/
private PointF determineFocalPoint(MotionEvent e) {
// Number of fingers on screen
final int pCount = e.getPointerCount();
float x = 0.0f;
float y = 0.0f;
for (int i = 0; i < pCount; i++) {
x += e.getX(i);
y += e.getY(i);
}
return new PointF(x / pCount, y / pCount);
}
public float getFocusX() {
return mFocusExternal.x;
}
public float getFocusY() {
return mFocusExternal.y;
}
public PointF getFocusDelta() {
return mFocusDeltaExternal;
}
}

View File

@@ -0,0 +1,180 @@
package com.almeros.android.multitouch.gesturedetectors;
import android.content.Context;
import android.view.MotionEvent;
/**
* @author Almer Thie (code.almeros.com) Copyright (c) 2013, Almer Thie
* (code.almeros.com)
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
public class RotateGestureDetector extends TwoFingerGestureDetector {
/**
* Listener which must be implemented which is used by RotateGestureDetector
* to perform callbacks to any implementing class which is registered to a
* RotateGestureDetector via the constructor.
*
* @see RotateGestureDetector.SimpleOnRotateGestureListener
*/
public interface OnRotateGestureListener {
public boolean onRotate(RotateGestureDetector detector);
public boolean onRotateBegin(RotateGestureDetector detector);
public void onRotateEnd(RotateGestureDetector detector);
}
/**
* Helper class which may be extended and where the methods may be
* implemented. This way it is not necessary to implement all methods of
* OnRotateGestureListener.
*/
public static class SimpleOnRotateGestureListener implements
OnRotateGestureListener {
public boolean onRotate(RotateGestureDetector detector) {
return false;
}
public boolean onRotateBegin(RotateGestureDetector detector) {
return true;
}
public void onRotateEnd(RotateGestureDetector detector) {
// Do nothing, overridden implementation may be used
}
}
private final OnRotateGestureListener mListener;
private boolean mSloppyGesture;
public RotateGestureDetector(Context context,
OnRotateGestureListener listener) {
super(context);
mListener = listener;
}
@Override
protected void handleStartProgressEvent(int actionCode, MotionEvent event) {
switch (actionCode) {
case MotionEvent.ACTION_POINTER_DOWN:
// At least the second finger is on screen now
resetState(); // In case we missed an UP/CANCEL event
mPrevEvent = MotionEvent.obtain(event);
mTimeDelta = 0;
updateStateByEvent(event);
// See if we have a sloppy gesture
mSloppyGesture = isSloppyGesture(event);
if (!mSloppyGesture) {
// No, start gesture now
mGestureInProgress = mListener.onRotateBegin(this);
}
break;
case MotionEvent.ACTION_MOVE:
if (!mSloppyGesture) {
break;
}
// See if we still have a sloppy gesture
mSloppyGesture = isSloppyGesture(event);
if (!mSloppyGesture) {
// No, start normal gesture now
mGestureInProgress = mListener.onRotateBegin(this);
}
break;
case MotionEvent.ACTION_POINTER_UP:
if (!mSloppyGesture) {
break;
}
break;
}
}
@Override
protected void handleInProgressEvent(int actionCode, MotionEvent event) {
switch (actionCode) {
case MotionEvent.ACTION_POINTER_UP:
// Gesture ended but
updateStateByEvent(event);
if (!mSloppyGesture) {
mListener.onRotateEnd(this);
}
resetState();
break;
case MotionEvent.ACTION_CANCEL:
if (!mSloppyGesture) {
mListener.onRotateEnd(this);
}
resetState();
break;
case MotionEvent.ACTION_MOVE:
updateStateByEvent(event);
// Only accept the event if our relative pressure is within
// a certain limit. This can help filter shaky data as a
// finger is lifted.
if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD) {
final boolean updatePrevious = mListener.onRotate(this);
if (updatePrevious) {
mPrevEvent.recycle();
mPrevEvent = MotionEvent.obtain(event);
}
}
break;
}
}
@Override
protected void resetState() {
super.resetState();
mSloppyGesture = false;
}
/**
* Return the rotation difference from the previous rotate event to the
* current event.
*
* @return The current rotation //difference in degrees.
*/
public float getRotationDegreesDelta() {
double diffRadians = Math.atan2(mPrevFingerDiffY, mPrevFingerDiffX)
- Math.atan2(mCurrFingerDiffY, mCurrFingerDiffX);
return (float) (diffRadians * 180.0 / Math.PI);
}
}

View File

@@ -0,0 +1,213 @@
package com.almeros.android.multitouch.gesturedetectors;
import android.content.Context;
import android.view.MotionEvent;
/**
* @author Robert Nordan (robert.nordan@norkart.no)
*
* Copyright (c) 2013, Norkart AS
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
public class ShoveGestureDetector extends TwoFingerGestureDetector {
/**
* Listener which must be implemented which is used by ShoveGestureDetector
* to perform callbacks to any implementing class which is registered to a
* ShoveGestureDetector via the constructor.
*
* @see ShoveGestureDetector.SimpleOnShoveGestureListener
*/
public interface OnShoveGestureListener {
public boolean onShove(ShoveGestureDetector detector);
public boolean onShoveBegin(ShoveGestureDetector detector);
public void onShoveEnd(ShoveGestureDetector detector);
}
/**
* Helper class which may be extended and where the methods may be
* implemented. This way it is not necessary to implement all methods of
* OnShoveGestureListener.
*/
public static class SimpleOnShoveGestureListener implements
OnShoveGestureListener {
public boolean onShove(ShoveGestureDetector detector) {
return false;
}
public boolean onShoveBegin(ShoveGestureDetector detector) {
return true;
}
public void onShoveEnd(ShoveGestureDetector detector) {
// Do nothing, overridden implementation may be used
}
}
private float mPrevAverageY;
private float mCurrAverageY;
private final OnShoveGestureListener mListener;
private boolean mSloppyGesture;
public ShoveGestureDetector(Context context, OnShoveGestureListener listener) {
super(context);
mListener = listener;
}
@Override
protected void handleStartProgressEvent(int actionCode, MotionEvent event) {
switch (actionCode) {
case MotionEvent.ACTION_POINTER_DOWN:
// At least the second finger is on screen now
resetState(); // In case we missed an UP/CANCEL event
mPrevEvent = MotionEvent.obtain(event);
mTimeDelta = 0;
updateStateByEvent(event);
// See if we have a sloppy gesture
mSloppyGesture = isSloppyGesture(event);
if (!mSloppyGesture) {
// No, start gesture now
mGestureInProgress = mListener.onShoveBegin(this);
}
break;
case MotionEvent.ACTION_MOVE:
if (!mSloppyGesture) {
break;
}
// See if we still have a sloppy gesture
mSloppyGesture = isSloppyGesture(event);
if (!mSloppyGesture) {
// No, start normal gesture now
mGestureInProgress = mListener.onShoveBegin(this);
}
break;
case MotionEvent.ACTION_POINTER_UP:
if (!mSloppyGesture) {
break;
}
break;
}
}
@Override
protected void handleInProgressEvent(int actionCode, MotionEvent event) {
switch (actionCode) {
case MotionEvent.ACTION_POINTER_UP:
// Gesture ended but
updateStateByEvent(event);
if (!mSloppyGesture) {
mListener.onShoveEnd(this);
}
resetState();
break;
case MotionEvent.ACTION_CANCEL:
if (!mSloppyGesture) {
mListener.onShoveEnd(this);
}
resetState();
break;
case MotionEvent.ACTION_MOVE:
updateStateByEvent(event);
// Only accept the event if our relative pressure is within
// a certain limit. This can help filter shaky data as a
// finger is lifted. Also check that shove is meaningful.
if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD
&& Math.abs(getShovePixelsDelta()) > 0.5f) {
final boolean updatePrevious = mListener.onShove(this);
if (updatePrevious) {
mPrevEvent.recycle();
mPrevEvent = MotionEvent.obtain(event);
}
}
break;
}
}
@Override
protected void resetState() {
super.resetState();
mSloppyGesture = false;
mPrevAverageY = 0.0f;
mCurrAverageY = 0.0f;
}
@Override
protected void updateStateByEvent(MotionEvent curr) {
super.updateStateByEvent(curr);
final MotionEvent prev = mPrevEvent;
float py0 = prev.getY(0);
float py1 = prev.getY(1);
mPrevAverageY = (py0 + py1) / 2.0f;
float cy0 = curr.getY(0);
float cy1 = curr.getY(1);
mCurrAverageY = (cy0 + cy1) / 2.0f;
}
@Override
protected boolean isSloppyGesture(MotionEvent event) {
boolean sloppy = super.isSloppyGesture(event);
if (sloppy)
return true;
// If it's not traditionally sloppy, we check if the angle between
// fingers
// is acceptable.
double angle = Math.abs(Math.atan2(mCurrFingerDiffY, mCurrFingerDiffX));
// about 20 degrees, left or right
return !((0.0f < angle && angle < 0.35f) || 2.79f < angle
&& angle < Math.PI);
}
/**
* Return the distance in pixels from the previous shove event to the
* current event.
*
* @return The current distance in pixels.
*/
public float getShovePixelsDelta() {
return mCurrAverageY - mPrevAverageY;
}
}

View File

@@ -0,0 +1,223 @@
package com.almeros.android.multitouch.gesturedetectors;
import android.content.Context;
import android.graphics.PointF;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
/**
* @author Almer Thie (code.almeros.com) Copyright (c) 2013, Almer Thie
* (code.almeros.com)
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
public abstract class TwoFingerGestureDetector extends BaseGestureDetector {
private final float mEdgeSlop;
protected float mPrevFingerDiffX;
protected float mPrevFingerDiffY;
protected float mCurrFingerDiffX;
protected float mCurrFingerDiffY;
private float mCurrLen;
private float mPrevLen;
private PointF mFocus;
public TwoFingerGestureDetector(Context context) {
super(context);
ViewConfiguration config = ViewConfiguration.get(context);
mEdgeSlop = config.getScaledEdgeSlop();
}
@Override
protected abstract void handleStartProgressEvent(int actionCode,
MotionEvent event);
@Override
protected abstract void handleInProgressEvent(int actionCode,
MotionEvent event);
protected void updateStateByEvent(MotionEvent curr) {
super.updateStateByEvent(curr);
final MotionEvent prev = mPrevEvent;
mCurrLen = -1;
mPrevLen = -1;
// Previous
final float px0 = prev.getX(0);
final float py0 = prev.getY(0);
final float px1 = prev.getX(1);
final float py1 = prev.getY(1);
final float pvx = px1 - px0;
final float pvy = py1 - py0;
mPrevFingerDiffX = pvx;
mPrevFingerDiffY = pvy;
// Current
final float cx0 = curr.getX(0);
final float cy0 = curr.getY(0);
final float cx1 = curr.getX(1);
final float cy1 = curr.getY(1);
final float cvx = cx1 - cx0;
final float cvy = cy1 - cy0;
mCurrFingerDiffX = cvx;
mCurrFingerDiffY = cvy;
mFocus = determineFocalPoint(curr);
}
/**
* Return the current distance between the two pointers forming the gesture
* in progress.
*
* @return Distance between pointers in pixels.
*/
public float getCurrentSpan() {
if (mCurrLen == -1) {
final float cvx = mCurrFingerDiffX;
final float cvy = mCurrFingerDiffY;
mCurrLen = (float) Math.sqrt(cvx * cvx + cvy * cvy);
}
return mCurrLen;
}
/**
* Return the previous distance between the two pointers forming the gesture
* in progress.
*
* @return Previous distance between pointers in pixels.
*/
public float getPreviousSpan() {
if (mPrevLen == -1) {
final float pvx = mPrevFingerDiffX;
final float pvy = mPrevFingerDiffY;
mPrevLen = (float) Math.sqrt(pvx * pvx + pvy * pvy);
}
return mPrevLen;
}
/**
* MotionEvent has no getRawX(int) method; simulate it pending future API
* approval.
*
* @param event Motion Event
* @param pointerIndex Pointer Index
* @return Raw x value or 0
*/
protected static float getRawX(MotionEvent event, int pointerIndex) {
float offset = event.getX() - event.getRawX();
if (pointerIndex < event.getPointerCount()) {
return event.getX(pointerIndex) + offset;
}
return 0.0f;
}
/**
* MotionEvent has no getRawY(int) method; simulate it pending future API
* approval.
*
* @param event Motion Event
* @param pointerIndex Pointer Index
* @return Raw y value or 0
*/
protected static float getRawY(MotionEvent event, int pointerIndex) {
float offset = event.getY() - event.getRawY();
if (pointerIndex < event.getPointerCount()) {
return event.getY(pointerIndex) + offset;
}
return 0.0f;
}
/**
* Check if we have a sloppy gesture. Sloppy gestures can happen if the edge
* of the user's hand is touching the screen, for example.
*
* @param event Motion Event
* @return {@code true} if is sloppy gesture, {@code false} if not
*/
protected boolean isSloppyGesture(MotionEvent event) {
// As orientation can change, query the metrics in touch down
DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
float mRightSlopEdge = metrics.widthPixels - mEdgeSlop;
float mBottomSlopEdge = metrics.heightPixels - mEdgeSlop;
final float edgeSlop = mEdgeSlop;
final float x0 = event.getRawX();
final float y0 = event.getRawY();
final float x1 = getRawX(event, 1);
final float y1 = getRawY(event, 1);
boolean p0sloppy = x0 < edgeSlop || y0 < edgeSlop || x0 > mRightSlopEdge
|| y0 > mBottomSlopEdge;
boolean p1sloppy = x1 < edgeSlop || y1 < edgeSlop || x1 > mRightSlopEdge
|| y1 > mBottomSlopEdge;
if (p0sloppy && p1sloppy) {
return true;
} else if (p0sloppy) {
return true;
} else if (p1sloppy) {
return true;
}
return false;
}
/**
* Determine (multi)finger focal point (a.k.a. center point between all
* fingers)
*
* @param e Motion Event
* @return PointF focal point
*/
public static PointF determineFocalPoint(MotionEvent e) {
// Number of fingers on screen
final int pCount = e.getPointerCount();
float x = 0.0f;
float y = 0.0f;
for (int i = 0; i < pCount; i++) {
x += e.getX(i);
y += e.getY(i);
}
return new PointF(x / pCount, y / pCount);
}
public float getFocusX() {
return mFocus.x;
}
public float getFocusY() {
return mFocus.y;
}
}

View File

@@ -0,0 +1,63 @@
package com.mapbox.mapboxgl.annotations;
import com.mapbox.mapboxgl.views.MapView;
public abstract class Annotation {
/**
* The annotation id
*
* Internal C++ id is stored as unsigned int.
*/
protected long id = -1; // -1 unless added to a MapView
private MapView mapView;
float alpha = 1.0f;
boolean visible = true;
public Annotation() {}
public float getAlpha() {
return alpha;
}
public long getId() {
return id;
}
public boolean isVisible() {
return visible;
}
public void remove() {
if (mapView == null) return;
mapView.removeAnnotation(this);
}
public void setAlpha(float alpha) {
this.alpha = alpha;
}
public void setId(long id) {
this.id = id;
}
public void setMapView(MapView mapView) {
this.mapView = mapView;
}
public void setVisible(boolean visible) {
this.visible = visible;
}
// TODO: Implement getZIndex of Google Maps Android API
// public float getZIndex() {
//
// }
// TODO: Implement setZIndex of Google Maps Android API
// public void setZIndex(float zIndex) {
//
// }
}

View File

@@ -0,0 +1,22 @@
package com.mapbox.mapboxgl.annotations;
public abstract class AnnotationOptions {
protected Annotation annotation;
public AnnotationOptions() {}
public AnnotationOptions alpha(float alpha) {
annotation.alpha = alpha;
return this;
}
public float getAlpha() {
return annotation.alpha;
}
public boolean isVisible() {
return annotation.visible;
}
}

View File

@@ -0,0 +1,67 @@
package com.mapbox.mapboxgl.annotations;
import android.graphics.Color;
import com.mapbox.mapboxgl.geometry.LatLng;
/**
* UNIMPLEMENTED: Needs implementation in Native.
*
* https://github.com/mapbox/mapbox-gl-native/issues/1882
* https://github.com/mapbox/mapbox-gl-native/issues/1726
*/
public class Circle extends Annotation {
LatLng center;
int fillColor = Color.BLACK;
double radius;
int strokeColor = Color.BLACK;
float strokeWidth = 10; // Google Maps API defaults to 10
public LatLng getCenter() {
return center;
}
public int getFillColor() {
return fillColor;
}
/**
* Returns the circle's radius, in meters.
*
* @return radius in meters
*/
public double getRadius() {
return radius;
}
public int getStrokeColor() {
return strokeColor;
}
public float getStrokeWidth() {
return strokeWidth;
}
public void setCenter(LatLng center) {
this.center = center;
}
public void setFillColor(int color) {
fillColor = color;
}
public void setRadius(double radius) {
this.radius = radius;
}
public void setStrokeColor (int color) {
strokeColor = color;
}
public void setStrokeWidth (float width) {
strokeWidth = width;
}
}

View File

@@ -0,0 +1,64 @@
package com.mapbox.mapboxgl.annotations;
import com.mapbox.mapboxgl.geometry.LatLng;
/**
* UNIMPLEMENTED: Needs implementation in Native.
*
* https://github.com/mapbox/mapbox-gl-native/issues/1882
* https://github.com/mapbox/mapbox-gl-native/issues/1726
*/
public class CircleOptions extends AnnotationOptions {
private Circle circle;
public CircleOptions() {
circle = new Circle();
}
public CircleOptions center(LatLng center) {
circle.center = center;
return this;
}
public CircleOptions fillColor(int color) {
circle.fillColor = color;
return this;
}
public LatLng getCenter() {
return circle.center;
}
public int getFillColor() {
return circle.fillColor;
}
public double getRadius() {
return circle.radius;
}
public int getStrokeColor () {
return circle.strokeColor;
}
public float getStrokeWidth() {
return circle.strokeWidth;
}
public CircleOptions radius (double radius) {
circle.radius = radius;
return this;
}
public CircleOptions strokeColor(int color) {
circle.strokeColor = color;
return this;
}
public CircleOptions strokeWidth (float width) {
circle.strokeWidth = width;
return this;
}
}

View File

@@ -0,0 +1,143 @@
package com.mapbox.mapboxgl.annotations;
import com.mapbox.mapboxgl.geometry.LatLng;
public class Marker extends Annotation {
float anchorU;
float anchorV;
boolean draggable;
boolean flat;
float infoWindowAnchorU;
float infoWindowAnchorV;
LatLng position;
float rotation;
String snippet;
String sprite = "default_marker";
String title;
private boolean infoWindowShown = false;
public Marker() {}
/**
* If two markers have the same LatLng, they are equal.
*
* @param other object
* @return boolean - do they have the same LatLng
*/
public boolean equals(Object other) {
if (!(other instanceof Marker)) return false;
double lat = position.getLatitude();
double lng = position.getLongitude();
LatLng otherPosition = ((Marker)other).getPosition();
double otherLat = otherPosition.getLatitude();
double otherLng = otherPosition.getLongitude();
return (lat == otherLat && otherLng == lng);
}
public float getAlpha() {
return alpha;
}
public float getAnchorU() {
return anchorU;
}
public float getAnchorV() {
return anchorV;
}
public LatLng getPosition() {
return position;
}
public float getRotation() {
return rotation;
}
public String getSnippet() {
return snippet;
}
public String getTitle() {
return title;
}
public void hideInfoWindow() {
//TODO hideInfoWindow
infoWindowShown = false;
}
public boolean isDraggable() {
return draggable;
}
public boolean isFlat() {
return flat;
}
public boolean isInfoWindowShown () {
return infoWindowShown;
}
void setAnchor(float u, float v) {
this.anchorU = u;
this.anchorV = v;
}
void setDraggable(boolean draggable) {
this.draggable = draggable;
}
void setFlat(boolean flat) {
this.flat = flat;
}
void setInfoWindowAnchor(float u, float v) {
infoWindowAnchorU = u;
infoWindowAnchorV = v;
}
void setPosition(LatLng latlng) {
this.position = position;
}
void setRotation(float rotation) {
this.rotation = rotation;
}
void setSnippet(String snippet) {
this.snippet = snippet;
}
/**
* You can specify the name of a sprite to get a marker other than the default marker.
* This name can be found in the sprite json file:
*
* https://github.com/mapbox/mapbox-gl-styles/blob/mb-pages/sprites/mapbox-streets.json
*
* @param sprite
*/
void setSprite(String sprite) {
this.sprite = sprite;
}
void setTitle(String title) {
this.title = title;
}
void showInfoWindow() {
infoWindowShown = true;
}
// TODO Method in Google Maps Android API
// public int hashCode()
// TODO: Implement this method of Google Maps Android API
// void setIcon(BitmapDescriptor icon) {
//
// }
}

View File

@@ -0,0 +1,125 @@
package com.mapbox.mapboxgl.annotations;
import com.mapbox.mapboxgl.geometry.LatLng;
public class MarkerOptions extends AnnotationOptions {
public MarkerOptions() {
annotation = new Marker();
}
public MarkerOptions anchor(float u, float v) {
((Marker)annotation).anchorU = u;
((Marker)annotation).anchorV = v;
return this;
}
public MarkerOptions draggable(boolean draggable) {
((Marker)annotation).draggable = draggable;
return this;
}
public MarkerOptions flat(boolean flat) {
((Marker)annotation).flat = flat;
return this;
}
public float getAnchorU() {
return ((Marker)annotation).anchorU;
}
public float getAnchorV() {
return ((Marker)annotation).anchorV;
}
// TODO: Implement this method of Google Maps Android API
// public BitmapDescriptor getIcon () {
//
// }
public float getInfoWindowAnchorU() {
return ((Marker)annotation).infoWindowAnchorU;
}
public float getInfoWindowAnchorV() {
return ((Marker)annotation).infoWindowAnchorV;
}
public Marker getMarker() {
return (Marker)annotation;
}
public LatLng getPosition() {
return ((Marker)annotation).position;
}
public float getRotation() {
return ((Marker)annotation).rotation;
}
public String getSnippet() {
return ((Marker)annotation).snippet;
}
public String getTitle() {
return ((Marker)annotation).title;
}
public MarkerOptions infoWindowAnchor(float u, float v) {
((Marker)annotation).infoWindowAnchorU = u;
((Marker)annotation).infoWindowAnchorV = v;
return this;
}
public boolean isDraggable() {
return ((Marker)annotation).draggable;
}
public boolean isFlat() {
return ((Marker)annotation).flat;
}
public boolean isVisible() {
return ((Marker)annotation).visible;
}
public MarkerOptions position(LatLng position) {
((Marker)annotation).position = position;
return this;
}
public MarkerOptions rotation(float rotation) {
((Marker)annotation).rotation = rotation;
return this;
}
public MarkerOptions snippet(String snippet) {
((Marker)annotation).snippet = snippet;
return this;
}
public MarkerOptions sprite(String sprite) {
((Marker)annotation).sprite = sprite;
return this;
}
public MarkerOptions title(String title) {
((Marker)annotation).title = title;
return this;
}
public MarkerOptions visible(boolean visible) {
annotation.visible = visible;
return this;
}
// TODO: Implement this method of Google Maps Android API
// public MarkerOptions icon(BitmapDescriptor icon) {
//
// }
// TODO: Implement this method of Google Maps Android API
// public void writeToParcel (Parcel out, int flags)
}

View File

@@ -0,0 +1,53 @@
package com.mapbox.mapboxgl.annotations;
import com.mapbox.mapboxgl.geometry.LatLng;
import java.util.ArrayList;
import java.util.List;
public abstract class MultiPoint extends Annotation {
List<LatLng> points;
public MultiPoint() {
super();
points = new ArrayList<>();
}
/**
* Returns a copy of the points.
*
* @return points - as a copy
*/
public List<LatLng> getPoints() {
return new ArrayList<>(points);
}
/**
* Sets the points of this polyline. This method will take a copy
* of the points, so further mutations to points will have no effect
* on this polyline.
*
* @param points
*/
void setPoints(List<LatLng> points) {
this.points = new ArrayList<>(points);
}
// TODO: Implement hashCode of Google Maps Android API
// public int hashCode() {
//
// }
// TODO: Implement isGeodesic of Google Maps Android API
// public boolean isGeodesic() {
//
// }
// TODO: Implement setGeodesic of Google Maps Android API
// public void setGeodesic(boolean geodesic) {
//
// }
}

View File

@@ -0,0 +1,15 @@
package com.mapbox.mapboxgl.annotations;
import com.mapbox.mapboxgl.geometry.LatLng;
import java.util.List;
public abstract class MultiPointOptions extends AnnotationOptions {
public MultiPointOptions() {}
public List<LatLng> getPoints() {
// the getter gives us a copy, which is the safe thing to do...
return ((MultiPoint)annotation).getPoints();
}
}

View File

@@ -0,0 +1,99 @@
package com.mapbox.mapboxgl.annotations;
import android.graphics.Color;
import com.mapbox.mapboxgl.geometry.LatLng;
import java.util.ArrayList;
import java.util.List;
public class Polygon extends MultiPoint {
int fillColor = Color.BLACK; // default fillColor is black
float strokeAlpha = 1;
int strokeColor = Color.BLACK; // default strokeColor is black
float strokeWidth = 10; // As specified by Google API Docs (in pixels)
List<List<LatLng>> holes;
public Polygon() {
super();
holes = new ArrayList<>();
}
public int getFillColor() {
return fillColor;
}
public List<List<LatLng>> getHoles () {
return holes;
}
public int getStrokeColor() {
return strokeColor;
}
/**
* UNIMPLEMENTED: Needs implementation in Native.
* https://github.com/mapbox/mapbox-gl-native/issues/1737
* @return stroke width as float
*/
public float getStrokeWidth() {
return strokeWidth;
}
public void setFillAlpha(float alpha) {
this.alpha = alpha;
}
public void setFillColor(int color) {
fillColor = color;
}
/**
* Sets the holes of this polygon. This method will take a copy of the holes,
* so further mutations to holes parameter will have no effect on this polygon.
*
* @param holes
*/
public void setHoles(List<? extends List<LatLng>> holes) {
this.holes = new ArrayList<>();
for (List<LatLng> hole : holes) {
this.holes.add(new ArrayList<>(hole));
}
}
/**
* Sets the alpha (opacity) of the stroke
*
* UNIMPLEMENTED: Needs implementation in Native.
*/
public void setStrokeAlpha(float alpha) {
strokeAlpha = alpha;
}
public void setStrokeColor(int color) {
strokeColor = color;
}
/**
* UNIMPLEMENTED: Needs implementation in Native.
* https://github.com/mapbox/mapbox-gl-native/issues/1737
* @return stroke width as float
*/
public void setStrokeWidth(float width) {
strokeWidth = width;
}
// TODO: Implement equals of Google Maps Android API
// public boolean equals (Object other) {
//
// }
// TODO: Implement hashCode of Google Maps Android API
// public int hashCode () {
//
// }
}

View File

@@ -0,0 +1,125 @@
package com.mapbox.mapboxgl.annotations;
import com.mapbox.mapboxgl.geometry.LatLng;
import java.util.ArrayList;
import java.util.List;
public class PolygonOptions extends MultiPointOptions {
public PolygonOptions() {
annotation = new Polygon();
}
public PolygonOptions add(LatLng point) {
((MultiPoint)annotation).points.add(point);
return this;
}
public PolygonOptions add(LatLng... points) {
for (LatLng point : points) {
add(point);
}
return this;
}
public PolygonOptions addAll(Iterable<LatLng> points) {
for (LatLng point : points) {
add(point);
}
return this;
}
/**
* UNIMPLEMENTED: Needs implementation in Native.
* https://github.com/mapbox/mapbox-gl-native/issues/1729
*
* @param points - an iterable (list) of points for cutting a hole
* @return PolygonOptions - the options object
*/
public PolygonOptions addHole (Iterable<LatLng> points) {
List<LatLng> hole = new ArrayList<>();
for (LatLng point : points) {
hole.add(point);
}
((Polygon)annotation).holes.add(hole);
return this;
}
/**
* Sets the color of the polygon.
*
* @param color - the color in ARGB format
* @return PolygonOptions - the options object
*/
public PolygonOptions fillColor(int color) {
((Polygon)annotation).fillColor = color;
return this;
}
public int getFillColor() {
return ((Polygon)annotation).fillColor;
}
/**
* UNIMPLEMENTED: Needs implementation in Native.
* https://github.com/mapbox/mapbox-gl-native/issues/1729
*
* @return a list of lists of points for cutting holes
*/
public List<List<LatLng>> getHoles() {
return ((Polygon)annotation).holes;
}
public Polygon getPolygon() {
return ((Polygon)annotation);
}
public int getStrokeColor() {
return ((Polygon)annotation).strokeColor;
}
/**
* UNIMPLEMENTED: Needs implementation in Native.
* https://github.com/mapbox/mapbox-gl-native/issues/1737
*
* @return stroke width as float
*/
public float getStrokeWidth() {
return ((Polygon)annotation).strokeWidth;
}
/**
* Sets the color of the stroke of the polygon.
*
* @param color - the color in ARGB format
* @return PolygonOptions - the options object
*/
public PolygonOptions strokeColor(int color) {
((Polygon)annotation).strokeColor = color;
return this;
}
/**
* UNIMPLEMENTED: Needs implementation in Native.
* https://github.com/mapbox/mapbox-gl-native/issues/1737
*
* @return stroke width as float
*/
public PolygonOptions strokeWidth(float width) {
((Polygon)annotation).strokeWidth = width;
return this;
}
public PolygonOptions visible(boolean visible) {
annotation.visible = visible;
return this;
}
// TODO: Implement writeToParcel of Google Maps Android API
// public void writeToParcel(Parcel out, int flags) {
//
// }
}

View File

@@ -0,0 +1,51 @@
package com.mapbox.mapboxgl.annotations;
import android.graphics.Color;
import com.mapbox.mapboxgl.geometry.LatLng;
import java.util.List;
public class Polyline extends MultiPoint {
int color = Color.BLACK; // default color is black
float width = 10; // As specified by Google API Docs (in pixels)
public Polyline() {
super();
}
public int getColor() {
return color;
}
public float getWidth() {
return width;
}
/**
* Sets the color of the polyline.
*
* @param color - the color in ARGB format
*/
public void setColor(int color) {
this.color = color;
}
/**
* Sets the width of the polyline.
*
* @param width in pixels
* @return
*/
public void setWidth(float width) {
this.width = width;
}
// TODO: Implement equals of Google Maps Android API
// public boolean equals(Object other) {
//
// }
}

View File

@@ -0,0 +1,75 @@
package com.mapbox.mapboxgl.annotations;
import com.mapbox.mapboxgl.geometry.LatLng;
import java.util.List;
public class PolylineOptions extends MultiPointOptions {
public PolylineOptions() {
annotation = new Polyline();
}
public PolylineOptions add(LatLng point) {
((MultiPoint)annotation).points.add(point);
return this;
}
public PolylineOptions add(LatLng... points) {
for (LatLng point : points) {
add(point);
}
return this;
}
public PolylineOptions addAll(Iterable<LatLng> points) {
for (LatLng point : points) {
add(point);
}
return this;
}
/**
* Sets the color of the polyline.
*
* @param color - the color in ARGB format
*/
public PolylineOptions color(int color) {
((Polyline)annotation).color = color;
return this;
}
public int getColor() {
return ((Polyline)annotation).color;
}
public Polyline getPolyline() {
return ((Polyline)annotation);
}
public float getWidth() {
return ((Polyline)annotation).width;
}
public PolylineOptions visible(boolean visible) {
annotation.visible = visible;
return this;
}
/**
* Sets the width of the polyline.
*
* @param width in pixels
* @return
*/
public PolylineOptions width(float width) {
((Polyline)annotation).width = width;
return this;
}
// TODO: Implement writeToParcel of Google Maps Android API
// public void writeToParcel(Parcel out, int flags) {
//
// }
}

View File

@@ -0,0 +1,10 @@
package com.mapbox.mapboxgl.constants;
public class GeoConstants {
// http://en.wikipedia.org/wiki/Earth_radius#Equatorial_radius
public static final int RADIUS_EARTH_METERS = 6378137;
public static final double MIN_LATITUDE = -85.05112878;
public static final double MAX_LATITUDE = 85.05112878;
public static final double MIN_LONGITUDE = -180;
public static final double MAX_LONGITUDE = 180;
}

View File

@@ -0,0 +1,10 @@
package com.mapbox.mapboxgl.constants;
import java.util.Locale;
public class MapboxConstants {
// Default Locale for data processing (ex: String.toLowerCase(MAPBOX_LOCALE, "foo"))
public static final Locale MAPBOX_LOCALE = Locale.US;
}

View File

@@ -0,0 +1,8 @@
package com.mapbox.mapboxgl.constants;
public class MathConstants {
public static final double DEG2RAD = (Math.PI / 180.0);
public static final double RAD2DEG = (180.0 / Math.PI);
public static final double PI = Math.PI;
}

View File

@@ -0,0 +1,273 @@
package com.mapbox.mapboxgl.geometry;
import android.os.Parcel;
import android.os.Parcelable;
import java.io.Serializable;
import java.util.List;
/**
* A rectangular geographical area defined in latitude and longitude units.
*/
public final class BoundingBox implements Parcelable, Serializable {
private final double mLatNorth;
private final double mLatSouth;
private final double mLonEast;
private final double mLonWest;
private final boolean mIsValid;
/**
* Construct a new bounding box based on its corners, given in NESW
* order.
*
* @param north Northern Coordinate
* @param east Eastern Coordinate
* @param south Southern Coordinate
* @param west Western Coordinate
*/
public BoundingBox(final double north, final double east, final double south, final double west) {
this.mLatNorth = north;
this.mLonEast = east;
this.mLatSouth = south;
this.mLonWest = west;
this.mIsValid = ((this.mLonWest < this.mLonEast) && (this.mLatNorth > this.mLatSouth));
}
/**
* Construct a new bounding box based on its corners, given in NESW
* order.
*
* @param northEast Coordinate
* @param southWest Coordinate
*/
public BoundingBox(final LatLng northEast, final LatLng southWest) {
this(northEast.getLatitude(), northEast.getLongitude(), southWest.getLatitude(), southWest.getLongitude());
}
/**
* Create a bounding box from another bounding box
*
* @param other the other bounding box
*/
public BoundingBox(final BoundingBox other) {
this.mLatNorth = other.getLatNorth();
this.mLonEast = other.getLonEast();
this.mLatSouth = other.getLatSouth();
this.mLonWest = other.getLonWest();
this.mIsValid = other.isValid();
}
/**
* Create a new BoundingBox with no size centered at 0, 0, also known as null island
*/
public BoundingBox() {
this(0, 0, 0, 0);
}
/**
* Calculates the centerpoint of this bounding box by simple interpolation and returns
* it as a point. This is a non-geodesic calculation which is not the geographic center.
*
* @return LatLng center of this BoundingBox
*/
public LatLng getCenter() {
return new LatLng((this.mLatNorth + this.mLatSouth) / 2,
(this.mLonEast + this.mLonWest) / 2);
}
public double getLatNorth() {
return this.mLatNorth;
}
public double getLatSouth() {
return this.mLatSouth;
}
public double getLonEast() {
return this.mLonEast;
}
public double getLonWest() {
return this.mLonWest;
}
public boolean isValid() {
return this.mIsValid;
}
/**
* Get the absolute distance, in degrees, between the north and
* south boundaries of this bounding box
*
* @return Span distance
*/
public double getLatitudeSpan() {
return Math.abs(this.mLatNorth - this.mLatSouth);
}
/**
* Get the absolute distance, in degrees, between the west and
* east boundaries of this bounding box
*
* @return Span distance
*/
public double getLongitudeSpan() {
return Math.abs(this.mLonEast - this.mLonWest);
}
@Override
public String toString() {
return "N:" + this.mLatNorth + "; E:" + this.mLonEast + "; S:" + this.mLatSouth + "; W:" + this.mLonWest;
}
/**
* Constructs a bounding box that contains all of a list of LatLng
* objects. Empty lists will yield invalid bounding boxes.
*
* @param latLngs List of LatLng objects
* @return BoundingBox
*/
public static BoundingBox fromLatLngs(final List<? extends ILatLng> latLngs) {
double minLat = 90,
minLon = 180,
maxLat = -90,
maxLon = -180;
for (final ILatLng gp : latLngs) {
final double latitude = gp.getLatitude();
final double longitude = gp.getLongitude();
minLat = Math.min(minLat, latitude);
minLon = Math.min(minLon, longitude);
maxLat = Math.max(maxLat, latitude);
maxLon = Math.max(maxLon, longitude);
}
return new BoundingBox(maxLat, maxLon, minLat, minLon);
}
/**
* Determines whether this bounding box matches another one via coordinates.
*
* @param other another bounding box
* @return a boolean indicating whether the bounding boxes are equal
*/
public boolean equals(final BoundingBox other) {
return other != null && (other == this || mLatNorth == other.getLatNorth() && mLatSouth == other.getLatSouth() && mLonEast == other.getLonEast() && mLonWest == other.getLonWest());
}
/**
* Determines whether this bounding box contains a point and the point
* does not touch its boundary.
*
* @param pGeoPoint the point which may be contained
* @return true, if the point is contained within the box.
*/
public boolean contains(final ILatLng pGeoPoint) {
final double latitude = pGeoPoint.getLatitude();
final double longitude = pGeoPoint.getLongitude();
return ((latitude < this.mLatNorth) && (latitude > this.mLatSouth)) && ((longitude
< this.mLonEast) && (longitude > this.mLonWest));
}
/**
* Returns a new BoundingBox that stretches to contain both this and another BoundingBox.
*
* @param box BoundingBox to add
* @return BoundingBox
*/
public BoundingBox union(BoundingBox box) {
return union(box.getLatNorth(), box.getLonEast(), box.getLatSouth(), box.getLonWest());
}
/**
* Returns a new BoundingBox that stretches to include another bounding box,
* given by corner points.
*
* @param pLatNorth Northern Coordinate
* @param pLonEast Eastern Coordinate
* @param pLatSouth Southern Coordinate
* @param pLonWest Western Coordinate
* @return BoundingBox
*/
public BoundingBox union(final double pLatNorth, final double pLonEast, final double pLatSouth,
final double pLonWest) {
return new BoundingBox((this.mLatNorth < pLatNorth) ? pLatNorth : this.mLatNorth,
(this.mLonEast < pLonEast) ? pLonEast : this.mLonEast,
(this.mLatSouth > pLatSouth) ? pLatSouth : this.mLatSouth,
(this.mLonWest > pLonWest) ? pLonWest : this.mLonWest);
}
/**
* Returns a new BoundingBox that is the intersection of this with another box
*
* @param box BoundingBox to intersect with
* @return BoundingBox
*/
public BoundingBox intersect(BoundingBox box) {
double maxLonWest = Math.max(this.mLonWest, box.getLonWest());
double minLonEast = Math.min(this.mLonEast, box.getLonEast());
double maxLatNorth = Math.min(this.mLatNorth, box.getLatNorth());
double minLatSouth = Math.max(this.mLatSouth, box.getLatSouth());
return new BoundingBox(maxLatNorth, minLonEast, minLatSouth, maxLonWest);
}
/**
* Returns a new BoundingBox that is the intersection of this with another box
*
* @param north Northern Coordinate
* @param east Eastern Coordinate
* @param south Southern Coordinate
* @param west Western Coordinate
* @return BoundingBox
*/
public BoundingBox intersect(double north, double east, double south, double west) {
return intersect(new BoundingBox(north, east, south, west));
}
public static final Parcelable.Creator<BoundingBox> CREATOR =
new Parcelable.Creator<BoundingBox>() {
@Override
public BoundingBox createFromParcel(final Parcel in) {
return readFromParcel(in);
}
@Override
public BoundingBox[] newArray(final int size) {
return new BoundingBox[size];
}
};
@Override
public int hashCode() {
return (int) ((mLatNorth + 90)
+ ((mLatSouth + 90) * 1000)
+ ((mLonEast + 180) * 1000000)
+ ((mLonEast + 180) * 1000000000));
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(final Parcel out, final int arg1) {
out.writeDouble(this.mLatNorth);
out.writeDouble(this.mLonEast);
out.writeDouble(this.mLatSouth);
out.writeDouble(this.mLonWest);
}
private static BoundingBox readFromParcel(final Parcel in) {
final double latNorth = in.readDouble();
final double lonEast = in.readDouble();
final double latSouth = in.readDouble();
final double lonWest = in.readDouble();
return new BoundingBox(latNorth, lonEast, latSouth, lonWest);
}
}

View File

@@ -0,0 +1,30 @@
package com.mapbox.mapboxgl.geometry;
/**
* Implementation of iOS MKCoordinateRegion
*/
public class CoordinateRegion {
private LatLng center;
private CoordinateSpan span;
public CoordinateRegion(final LatLng center, final CoordinateSpan span) {
this.center = center;
this.span = span;
}
public LatLng getCenter() {
return center;
}
public void setCenter(final LatLng center) {
this.center = center;
}
public CoordinateSpan getSpan() {
return span;
}
public void setSpan(final CoordinateSpan span) {
this.span = span;
}
}

View File

@@ -0,0 +1,31 @@
package com.mapbox.mapboxgl.geometry;
/**
* Implementation of iOS MKCoordinateSpan
*/
public class CoordinateSpan {
private double latitudeSpan;
private double longitudeSpan;
public CoordinateSpan(final double latitudeSpan, final double longitudeSpan) {
this.latitudeSpan = latitudeSpan;
this.longitudeSpan = longitudeSpan;
}
public double getLatitudeSpan() {
return latitudeSpan;
}
public void setLatitudeSpan(final double latitudeSpan) {
this.latitudeSpan = latitudeSpan;
}
public double getLongitudeSpan() {
return longitudeSpan;
}
public void setLongitudeSpan(final double longitudeSpan) {
this.longitudeSpan = longitudeSpan;
}
}

View File

@@ -0,0 +1,12 @@
package com.mapbox.mapboxgl.geometry;
/**
* A Latitude, Longitude point.
*/
public interface ILatLng {
double getLatitude();
double getLongitude();
double getAltitude();
}

View File

@@ -0,0 +1,7 @@
package com.mapbox.mapboxgl.geometry;
public interface IProjectedMeters {
double getNorthing();
double getEasting();
}

View File

@@ -0,0 +1,156 @@
package com.mapbox.mapboxgl.geometry;
import android.location.Location;
import android.os.Parcel;
import android.os.Parcelable;
import com.mapbox.mapboxgl.constants.GeoConstants;
import com.mapbox.mapboxgl.constants.MathConstants;
import java.io.Serializable;
public class LatLng implements ILatLng, Parcelable, Serializable {
public static final Parcelable.Creator<LatLng> CREATOR = new Parcelable.Creator<LatLng>() {
public LatLng createFromParcel(Parcel in) {
return new LatLng(in);
}
public LatLng[] newArray(int size) {
return new LatLng[size];
}
};
private double latitude;
private double longitude;
private double altitude = 0.0;
/**
* Construct a new latitude, longitude point given float arguments
* @param latitude Latitude in degrees
* @param longitude Longitude in degrees
*/
public LatLng(double latitude, double longitude) {
this.latitude = latitude;
this.longitude = longitude;
}
/**
* Construct a new latitude, longitude, altitude point given float arguments
* @param latitude Latitude in degrees
* @param longitude Longitude in degress
* @param altitude Altitude in meters
*/
public LatLng(double latitude, double longitude, double altitude) {
this.latitude = latitude;
this.longitude = longitude;
this.altitude = altitude;
}
/**
* Transform a Location into a LatLng point
* @param location Android Location
*/
public LatLng(Location location) {
this(location.getLatitude(), location.getLongitude(), location.getAltitude());
}
/**
* Clone an existing latitude longitude point
* @param aLatLng LatLng
*/
public LatLng(LatLng aLatLng) {
this.latitude = aLatLng.latitude;
this.longitude = aLatLng.longitude;
this.altitude = aLatLng.altitude;
}
protected LatLng(Parcel in) {
latitude = in.readDouble();
longitude = in.readDouble();
altitude = in.readDouble();
}
@Override
public double getLatitude() {
return latitude;
}
@Override
public double getLongitude() {
return longitude;
}
@Override
public double getAltitude() {
return altitude;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
LatLng latLng = (LatLng) o;
return Double.compare(latLng.altitude, altitude) == 0 && Double.compare(latLng.latitude, latitude) == 0 && Double.compare(latLng.longitude, longitude) == 0;
}
@Override
public int hashCode() {
int result;
long temp;
temp = Double.doubleToLongBits(latitude);
result = (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(longitude);
result = 31 * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(altitude);
result = 31 * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public String toString() {
return "LatLng [longitude=" + longitude + ", latitude=" + latitude + ", altitude=" + altitude + "]";
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeDouble(latitude);
out.writeDouble(longitude);
out.writeDouble(altitude);
}
/**
* Calculate distance between two points
* @param other Other LatLng to compare to
* @return distance in meters
*/
public double distanceTo(LatLng other) {
final double a1 = MathConstants.DEG2RAD * this.latitude;
final double a2 = MathConstants.DEG2RAD * this.longitude;
final double b1 = MathConstants.DEG2RAD * other.getLatitude();
final double b2 = MathConstants.DEG2RAD * other.getLongitude();
final double cosa1 = Math.cos(a1);
final double cosb1 = Math.cos(b1);
final double t1 = cosa1 * Math.cos(a2) * cosb1 * Math.cos(b2);
final double t2 = cosa1 * Math.sin(a2) * cosb1 * Math.sin(b2);
final double t3 = Math.sin(a1) * Math.sin(b1);
final double tt = Math.acos(t1 + t2 + t3);
return GeoConstants.RADIUS_EARTH_METERS * tt;
}
}

View File

@@ -0,0 +1,88 @@
package com.mapbox.mapboxgl.geometry;
import android.os.Parcel;
import android.os.Parcelable;
import java.io.Serializable;
public class LatLngZoom extends LatLng implements Parcelable, Serializable {
public static final Parcelable.Creator<LatLngZoom> CREATOR = new Parcelable.Creator<LatLngZoom>() {
public LatLngZoom createFromParcel(Parcel in) {
return new LatLngZoom(in);
}
public LatLngZoom[] newArray(int size) {
return new LatLngZoom[size];
}
};
private double zoom;
public LatLngZoom(double latitude, double longitude, double zoom) {
super(latitude, longitude);
this.zoom = zoom;
}
public LatLngZoom(LatLng latLng, double zoom) {
super(latLng.getLatitude(), latLng.getLongitude());
this.zoom = zoom;
}
private LatLngZoom(Parcel in) {
super(in);
zoom = in.readDouble();
}
public double getZoom() {
return zoom;
}
public void setZoom(double zoom) {
this.zoom = zoom;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (!super.equals(o)) {
return false;
}
LatLngZoom that = (LatLngZoom) o;
return Double.compare(that.zoom, zoom) == 0;
}
@Override
public int hashCode() {
int result = super.hashCode();
long temp;
temp = Double.doubleToLongBits(zoom);
result = 31 * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public String toString() {
return "LatLngZoom [latitude=" + super.getLatitude() + ", longitude=" + super.getLongitude() + ", altitude=" + super.getAltitude() + ", zoom=" + zoom + "]";
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeDouble(zoom);
}
}

View File

@@ -0,0 +1,89 @@
package com.mapbox.mapboxgl.geometry;
import android.os.Parcel;
import android.os.Parcelable;
import java.io.Serializable;
public class ProjectedMeters implements IProjectedMeters, Parcelable, Serializable {
public static final Creator<ProjectedMeters> CREATOR = new Creator<ProjectedMeters>() {
public ProjectedMeters createFromParcel(Parcel in) {
return new ProjectedMeters(in);
}
public ProjectedMeters[] newArray(int size) {
return new ProjectedMeters[size];
}
};
private double northing;
private double easting;
public ProjectedMeters(double northing, double easting) {
this.northing = northing;
this.easting = easting;
}
public ProjectedMeters(ProjectedMeters aProjectedMeters) {
this.northing = aProjectedMeters.northing;
this.easting = aProjectedMeters.easting;
}
protected ProjectedMeters(Parcel in) {
northing = in.readDouble();
easting = in.readDouble();
}
@Override
public double getNorthing() {
return northing;
}
@Override
public double getEasting() {
return easting;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ProjectedMeters projectedMeters = (ProjectedMeters) o;
return Double.compare(projectedMeters.easting, easting) == 0 && Double.compare(projectedMeters.northing, northing) == 0;
}
@Override
public int hashCode() {
int result;
long temp;
temp = Double.doubleToLongBits(easting);
result = (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(northing);
result = 31 * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public String toString() {
return "ProjectedMeters [northing=" + northing + ", easting=" + easting + "]";
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeDouble(northing);
out.writeDouble(easting);
}
}

View File

@@ -0,0 +1,100 @@
package com.mapbox.mapboxgl.http;
import com.mapbox.mapboxgl.constants.MapboxConstants;
import com.squareup.okhttp.Call;
import com.squareup.okhttp.Callback;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.ProtocolException;
import java.net.SocketException;
import java.net.UnknownHostException;
import javax.net.ssl.SSLException;
class HTTPContext {
private static final int CONNECTION_ERROR = 0;
private static final int TEMPORARY_ERROR = 1;
private static final int PERMANENT_ERROR = 2;
private static HTTPContext mInstance = null;
private OkHttpClient mClient;
private HTTPContext() {
super();
mClient = new OkHttpClient();
}
public static HTTPContext getInstance() {
if (mInstance == null) {
mInstance = new HTTPContext();
}
return mInstance;
}
public HTTPRequest createRequest(long nativePtr, String resourceUrl, String userAgent, String etag, String modified) {
return new HTTPRequest(nativePtr, resourceUrl, userAgent, etag, modified);
}
public class HTTPRequest implements Callback {
private long mNativePtr = 0;
private Call mCall;
private Request mRequest;
private native void nativeOnFailure(long nativePtr, int type, String message);
private native void nativeOnResponse(long nativePtr, int code, String message, String etag, String modified, String cacheControl, String expires, byte[] body);
private HTTPRequest(long nativePtr, String resourceUrl, String userAgent, String etag, String modified) {
mNativePtr = nativePtr;
Request.Builder builder = new Request.Builder().url(resourceUrl).tag(resourceUrl.toLowerCase(MapboxConstants.MAPBOX_LOCALE)).addHeader("User-Agent", userAgent);
if (etag.length() > 0) {
builder = builder.addHeader("If-None-Match", etag);
} else if (modified.length() > 0) {
builder = builder.addHeader("If-Modified-Since", modified);
}
mRequest = builder.build();
}
public void start() {
mCall = HTTPContext.getInstance().mClient.newCall(mRequest);
mCall.enqueue(this);
}
public void cancel() {
mCall.cancel();
}
@Override
public void onFailure(Request request, IOException e) {
int type = PERMANENT_ERROR;
if ((e instanceof UnknownHostException) || (e instanceof SocketException) || (e instanceof ProtocolException) || (e instanceof SSLException)) {
type = CONNECTION_ERROR;
} else if ((e instanceof InterruptedIOException)) {
type = TEMPORARY_ERROR;
}
nativeOnFailure(mNativePtr, type, e.getMessage());
}
@Override
public void onResponse(Response response) throws IOException {
byte[] body;
try {
body = response.body().bytes();
} catch (IOException e) {
onFailure(mRequest, e);
return;
} finally {
response.body().close();
}
nativeOnResponse(mNativePtr, response.code(), response.message(), response.header("ETag"), response.header("Last-Modified"), response.header("Cache-Control"), response.header("Expires"), body);
}
}
}

View File

@@ -0,0 +1,600 @@
package com.mapbox.mapboxgl.views;
import android.graphics.PointF;
import android.view.Surface;
import com.mapbox.mapboxgl.annotations.Marker;
import com.mapbox.mapboxgl.annotations.Polygon;
import com.mapbox.mapboxgl.annotations.Polyline;
import com.mapbox.mapboxgl.geometry.LatLng;
import com.mapbox.mapboxgl.geometry.LatLngZoom;
import com.mapbox.mapboxgl.geometry.ProjectedMeters;
import java.util.List;
// Class that wraps the native methods for convenience
class NativeMapView {
//
// Static members
//
//
// Instance members
//
// Holds the pointer to JNI NativeMapView
private long mNativeMapViewPtr = 0;
// Used for callbacks
private MapView mMapView;
//
// Static methods
//
static {
System.loadLibrary("mapbox-gl");
}
//
// Constructors
//
public NativeMapView(MapView mapView, String cachePath, String dataPath, String apkPath, float pixelRatio, int availableProcessors, long totalMemory) {
if (availableProcessors < 0) {
throw new IllegalArgumentException("availableProcessors cannot be negative.");
}
if (totalMemory < 0) {
throw new IllegalArgumentException("totalMemory cannot be negative.");
}
mMapView = mapView;
// Create the NativeMapView
mNativeMapViewPtr = nativeCreate(cachePath, dataPath, apkPath, pixelRatio, availableProcessors, totalMemory);
}
//
// Methods
//
public void initializeDisplay() {
nativeInitializeDisplay(mNativeMapViewPtr);
}
public void terminateDisplay() {
nativeTerminateDisplay(mNativeMapViewPtr);
}
public void initializeContext() {
nativeInitializeContext(mNativeMapViewPtr);
}
public void terminateContext() {
nativeTerminateContext(mNativeMapViewPtr);
}
public void createSurface(Surface surface) {
nativeCreateSurface(mNativeMapViewPtr, surface);
}
public void destroySurface() {
nativeDestroySurface(mNativeMapViewPtr);
}
public void pause() {
nativePause(mNativeMapViewPtr);
}
public void resume() {
nativeResume(mNativeMapViewPtr);
}
public void update() {
nativeUpdate(mNativeMapViewPtr);
}
public void invalidate() {
nativeOnInvalidate(mNativeMapViewPtr);
}
public void resizeView(int width, int height) {
if (width < 0) {
throw new IllegalArgumentException("width cannot be negative.");
}
if (height < 0) {
throw new IllegalArgumentException("height cannot be negative.");
}
if (width > 65535) {
throw new IllegalArgumentException(
"width cannot be greater than 65535.");
}
if (height > 65535) {
throw new IllegalArgumentException(
"height cannot be greater than 65535.");
}
nativeViewResize(mNativeMapViewPtr, width, height);
}
public void resizeFramebuffer(int fbWidth, int fbHeight) {
if (fbWidth < 0) {
throw new IllegalArgumentException("fbWidth cannot be negative.");
}
if (fbHeight < 0) {
throw new IllegalArgumentException("fbHeight cannot be negative.");
}
if (fbWidth > 65535) {
throw new IllegalArgumentException(
"fbWidth cannot be greater than 65535.");
}
if (fbHeight > 65535) {
throw new IllegalArgumentException(
"fbHeight cannot be greater than 65535.");
}
nativeFramebufferResize(mNativeMapViewPtr, fbWidth, fbHeight);
}
public void addClass(String clazz) {
nativeAddClass(mNativeMapViewPtr, clazz);
}
public void removeClass(String clazz) {
nativeRemoveClass(mNativeMapViewPtr, clazz);
}
public boolean hasClass(String clazz) {
return nativeHasClass(mNativeMapViewPtr, clazz);
}
public void setClasses(List<String> classes) {
nativeSetClasses(mNativeMapViewPtr, classes);
}
public List<String> getClasses() {
return nativeGetClasses(mNativeMapViewPtr);
}
public void setDefaultTransitionDuration() {
setDefaultTransitionDuration(0);
}
public long getDefaultTransitionDuration() {
return nativeGetDefaultTransitionDuration(mNativeMapViewPtr);
}
public void setDefaultTransitionDuration(long milliseconds) {
if (milliseconds < 0) {
throw new IllegalArgumentException(
"milliseconds cannot be negative.");
}
nativeSetDefaultTransitionDuration(mNativeMapViewPtr,
milliseconds);
}
public void setStyleUrl(String url) {
nativeSetStyleUrl(mNativeMapViewPtr, url);
}
public void setStyleJson(String newStyleJson) {
setStyleJson(newStyleJson, "");
}
public void setStyleJson(String newStyleJson, String base) {
nativeSetStyleJson(mNativeMapViewPtr, newStyleJson, base);
}
public String getStyleJson() {
return nativeGetStyleJson(mNativeMapViewPtr);
}
public void setAccessToken(String accessToken) {
nativeSetAccessToken(mNativeMapViewPtr, accessToken);
}
public String getAccessToken() {
return nativeGetAccessToken(mNativeMapViewPtr);
}
public void cancelTransitions() {
nativeCancelTransitions(mNativeMapViewPtr);
}
public void setGestureInProgress(boolean inProgress) {
nativeSetGestureInProgress(mNativeMapViewPtr, inProgress);
}
public void moveBy(double dx, double dy) {
moveBy(dx, dy, 0);
}
public void moveBy(double dx, double dy, long duration) {
nativeMoveBy(mNativeMapViewPtr, dx, dy, duration);
}
public void setLatLng(LatLng latLng) {
setLatLng(latLng, 0);
}
public void setLatLng(LatLng latLng, long duration) {
nativeSetLatLng(mNativeMapViewPtr, latLng, duration);
}
public LatLng getLatLng() {
return nativeGetLatLng(mNativeMapViewPtr);
}
public void resetPosition() {
nativeResetPosition(mNativeMapViewPtr);
}
public void scaleBy(double ds) {
scaleBy(ds, -1.0, -1.0);
}
public void scaleBy(double ds, double cx, double cy) {
scaleBy(ds, cx, cy, 0);
}
public void scaleBy(double ds, double cx, double cy, long duration) {
nativeScaleBy(mNativeMapViewPtr, ds, cx, cy, duration);
}
public void setScale(double scale) {
setScale(scale, -1.0, -1.0);
}
public void setScale(double scale, double cx, double cy) {
setScale(scale, cx, cy, 0);
}
public void setScale(double scale, double cx, double cy, long duration) {
nativeSetScale(mNativeMapViewPtr, scale, cx, cy, duration);
}
public double getScale() {
return nativeGetScale(mNativeMapViewPtr);
}
public void setZoom(double zoom) {
setZoom(zoom, 0);
}
public void setZoom(double zoom, long duration) {
nativeSetZoom(mNativeMapViewPtr, zoom, duration);
}
public double getZoom() {
return nativeGetZoom(mNativeMapViewPtr);
}
public void setLatLngZoom(LatLngZoom latLngZoom) {
setLatLngZoom(latLngZoom, 0);
}
public void setLatLngZoom(LatLngZoom latLngZoom, long duration) {
nativeSetLatLngZoom(mNativeMapViewPtr, latLngZoom, duration);
}
public LatLngZoom getLatLngZoom() {
return nativeGetLatLngZoom(mNativeMapViewPtr);
}
public void resetZoom() {
nativeResetZoom(mNativeMapViewPtr);
}
public double getMinZoom() {
return nativeGetMinZoom(mNativeMapViewPtr);
}
public double getMaxZoom() {
return nativeGetMaxZoom(mNativeMapViewPtr);
}
public void rotateBy(double sx, double sy, double ex, double ey) {
rotateBy(sx, sy, ex, ey, 0);
}
public void rotateBy(double sx, double sy, double ex, double ey,
long duration) {
nativeRotateBy(mNativeMapViewPtr, sx, sy, ex, ey, duration);
}
public void setBearing(double degrees) {
setBearing(degrees, 0);
}
public void setBearing(double degrees, long duration) {
nativeSetBearing(mNativeMapViewPtr, degrees, duration);
}
public void setBearing(double degrees, double cx, double cy) {
nativeSetBearing(mNativeMapViewPtr, degrees, cx, cy);
}
public double getBearing() {
return nativeGetBearing(mNativeMapViewPtr);
}
public void resetNorth() {
nativeResetNorth(mNativeMapViewPtr);
}
public long addMarker(Marker marker) {
return nativeAddMarker(mNativeMapViewPtr, marker);
}
public long addPolyline(Polyline polyline) {
return nativeAddPolyline(mNativeMapViewPtr, polyline);
}
public long addPolygon(Polygon polygon) {
return nativeAddPolygon(mNativeMapViewPtr, polygon);
}
public long[] addPolygons(List<Polygon> polygon) {
return nativeAddPolygons(mNativeMapViewPtr, polygon);
}
public void removeAnnotation(long id) {
nativeRemoveAnnotation(mNativeMapViewPtr, id);
}
public void removeAnnotations(long[] ids) {
nativeRemoveAnnotations(mNativeMapViewPtr, ids);
}
public void setSprite(String symbol, int width, int height, float scale, byte[] pixels) {
nativeSetSprite(mNativeMapViewPtr, symbol, width, height, scale, pixels);
}
public void onLowMemory() {
nativeOnLowMemory(mNativeMapViewPtr);
}
public void setDebug(boolean debug) {
nativeSetDebug(mNativeMapViewPtr, debug);
}
public void toggleDebug() {
nativeToggleDebug(mNativeMapViewPtr);
}
public boolean getDebug() {
return nativeGetDebug(mNativeMapViewPtr);
}
public void setCollisionDebug(boolean debug) {
nativeSetCollisionDebug(mNativeMapViewPtr, debug);
}
public void toggleCollisionDebug() {
nativeToggleCollisionDebug(mNativeMapViewPtr);
}
public boolean getCollisionDebug() {
return nativeGetCollisionDebug(mNativeMapViewPtr);
}
public boolean isFullyLoaded() {
return nativeIsFullyLoaded(mNativeMapViewPtr);
}
public void setReachability(boolean status) {
nativeSetReachability(mNativeMapViewPtr, status);
}
//public void ();
//public void getWorldBoundsLatLng();
public double getMetersPerPixelAtLatitude(double lat, double zoom) {
return nativeGetMetersPerPixelAtLatitude(mNativeMapViewPtr, lat, zoom);
}
public ProjectedMeters projectedMetersForLatLng(LatLng latLng) {
return nativeProjectedMetersForLatLng(mNativeMapViewPtr, latLng);
}
public LatLng latLngForProjectedMeters(ProjectedMeters projectedMeters) {
return nativeLatLngForProjectedMeters(mNativeMapViewPtr, projectedMeters);
}
public PointF pixelForLatLng(LatLng latLng) {
return nativePixelForLatLng(mNativeMapViewPtr, latLng);
}
public LatLng latLngForPixel(PointF pixel) {
return nativeLatLngForPixel(mNativeMapViewPtr, pixel);
}
//
// Callbacks
//
protected void onInvalidate() {
mMapView.onInvalidate();
}
protected void onMapChanged() {
mMapView.onMapChanged();
}
protected void onFpsChanged(double fps) {
mMapView.onFpsChanged(fps);
}
//
// JNI methods
//
@Override
protected void finalize() throws Throwable {
nativeDestroy(mNativeMapViewPtr);
mNativeMapViewPtr = 0;
super.finalize();
}
private native long nativeCreate(String cachePath, String dataPath, String apkPath, float pixelRatio, int availableProcessors, long totalMemory);
private native void nativeDestroy(long nativeMapViewPtr);
private native void nativeInitializeDisplay(long nativeMapViewPtr);
private native void nativeTerminateDisplay(long nativeMapViewPtr);
private native void nativeInitializeContext(long nativeMapViewPtr);
private native void nativeTerminateContext(long nativeMapViewPtr);
private native void nativeCreateSurface(long nativeMapViewPtr,
Surface surface);
private native void nativeDestroySurface(long nativeMapViewPtr);
private native void nativePause(long nativeMapViewPtr);
private native void nativeResume(long nativeMapViewPtr);
private native void nativeUpdate(long nativeMapViewPtr);
private native void nativeOnInvalidate(long nativeMapViewPtr);
private native void nativeViewResize(long nativeMapViewPtr, int width, int height);
private native void nativeFramebufferResize(long nativeMapViewPtr, int fbWidth, int fbHeight);
private native void nativeAddClass(long nativeMapViewPtr, String clazz);
private native void nativeRemoveClass(long nativeMapViewPtr, String clazz);
private native boolean nativeHasClass(long nativeMapViewPtr, String clazz);
private native void nativeSetClasses(long nativeMapViewPtr,
List<String> classes);
private native List<String> nativeGetClasses(long nativeMapViewPtr);
private native void nativeSetDefaultTransitionDuration(
long nativeMapViewPtr, long duration);
private native long nativeGetDefaultTransitionDuration(long nativeMapViewPtr);
private native void nativeSetStyleUrl(long nativeMapViewPtr, String url);
private native void nativeSetStyleJson(long nativeMapViewPtr,
String newStyleJson, String base);
private native String nativeGetStyleJson(long nativeMapViewPtr);
private native void nativeSetAccessToken(long nativeMapViewPtr, String accessToken);
private native String nativeGetAccessToken(long nativeMapViewPtr);
private native void nativeCancelTransitions(long nativeMapViewPtr);
private native void nativeSetGestureInProgress(long nativeMapViewPtr, boolean inProgress);
private native void nativeMoveBy(long nativeMapViewPtr, double dx,
double dy, long duration);
private native void nativeSetLatLng(long nativeMapViewPtr, LatLng latLng,
long duration);
private native LatLng nativeGetLatLng(long nativeMapViewPtr);
private native void nativeResetPosition(long nativeMapViewPtr);
private native void nativeScaleBy(long nativeMapViewPtr, double ds,
double cx, double cy, long duration);
private native void nativeSetScale(long nativeMapViewPtr, double scale,
double cx, double cy, long duration);
private native double nativeGetScale(long nativeMapViewPtr);
private native void nativeSetZoom(long nativeMapViewPtr, double zoom,
long duration);
private native double nativeGetZoom(long nativeMapViewPtr);
private native void nativeSetLatLngZoom(long nativeMapViewPtr,
LatLngZoom lonLatZoom, long duration);
private native LatLngZoom nativeGetLatLngZoom(long nativeMapViewPtr);
private native void nativeResetZoom(long nativeMapViewPtr);
private native double nativeGetMinZoom(long nativeMapViewPtr);
private native double nativeGetMaxZoom(long nativeMapViewPtr);
private native void nativeRotateBy(long nativeMapViewPtr, double sx,
double sy, double ex, double ey, long duration);
private native void nativeSetBearing(long nativeMapViewPtr, double degrees,
long duration);
private native void nativeSetBearing(long nativeMapViewPtr, double degrees,
double cx, double cy);
private native double nativeGetBearing(long nativeMapViewPtr);
private native void nativeResetNorth(long nativeMapViewPtr);
private native long nativeAddMarker(long nativeMapViewPtr, Marker marker);
private native long nativeAddPolyline(long nativeMapViewPtr, Polyline polyline);
private native long nativeAddPolygon(long mNativeMapViewPtr, Polygon polygon);
private native long[] nativeAddPolygons(long mNativeMapViewPtr, List<Polygon> polygon);
private native void nativeRemoveAnnotation(long nativeMapViewPtr, long id);
private native void nativeRemoveAnnotations(long nativeMapViewPtr, long[] id);
private native void nativeSetSprite(long nativeMapViewPtr, String symbol,
int width, int height, float scale, byte[] pixels);
private native void nativeOnLowMemory(long nativeMapViewPtr);
private native void nativeSetDebug(long nativeMapViewPtr, boolean debug);
private native void nativeToggleDebug(long nativeMapViewPtr);
private native boolean nativeGetDebug(long nativeMapViewPtr);
private native void nativeSetCollisionDebug(long nativeMapViewPtr, boolean debug);
private native void nativeToggleCollisionDebug(long nativeMapViewPtr);
private native boolean nativeGetCollisionDebug(long nativeMapViewPtr);
private native boolean nativeIsFullyLoaded(long nativeMapViewPtr);
private native void nativeSetReachability(long nativeMapViewPtr, boolean status);
//private native void nativeGetWorldBoundsMeters(long nativeMapViewPtr);
//private native void nativeGetWorldBoundsLatLng(long nativeMapViewPtr);
private native double nativeGetMetersPerPixelAtLatitude(long nativeMapViewPtr, double lat, double zoom);
private native ProjectedMeters nativeProjectedMetersForLatLng(long nativeMapViewPtr, LatLng latLng);
private native LatLng nativeLatLngForProjectedMeters(long nativeMapViewPtr, ProjectedMeters projectedMeters);
private native PointF nativePixelForLatLng(long nativeMapViewPtr, LatLng latLng);
private native LatLng nativeLatLngForPixel(long nativeMapViewPtr, PointF pixel);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

@@ -0,0 +1,17 @@
<resources>
<declare-styleable name="MapView">
<attr name="centerLongitude" format="float" />
<attr name="centerLatitude" format="float" />
<attr name="zoomLevel" format="float" />
<attr name="direction" format="float" />
<attr name="zoomEnabled" format="boolean" />
<attr name="scrollEnabled" format="boolean" />
<attr name="rotateEnabled" format="boolean" />
<attr name="debugActive" format="boolean" />
<attr name="styleUrl" format="string" />
<attr name="accessToken" format="string"/>
<attr name="styleClasses" format="string"/>
<attr name="compassEnabled" format="boolean" />
<attr name="myLocationEnabled" format="boolean" />
</declare-styleable>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="compassContentDescription">Map compass. Click to reset the map rotation to North.</string>
</resources>

View File

@@ -0,0 +1,108 @@
buildscript {
repositories {
mavenCentral()
maven { url 'https://maven.fabric.io/public' }
}
dependencies {
classpath 'com.android.tools.build:gradle:1.3.0'
classpath 'com.jakewharton.sdkmanager:gradle-plugin:0.12.0'
classpath 'io.fabric.tools:gradle:1.+'
}
}
apply plugin: 'android-sdk-manager'
apply plugin: 'com.android.application'
apply plugin: 'io.fabric'
repositories {
maven { url 'https://maven.fabric.io/public' }
}
apply plugin: 'checkstyle'
task accessToken {
def rawDir = new File("MapboxGLAndroidSDKTestApp/src/main/res/raw")
rawDir.mkdirs()
def tokenFile = new File("MapboxGLAndroidSDKTestApp/src/main/res/raw/token.txt")
if (!tokenFile.exists()) {
String token = "$System.env.MAPBOX_ACCESS_TOKEN"
if (token == null) {
throw new InvalidUserDataException("You must set the MAPBOX_ACCESS_TOKEN environment variable.")
}
tokenFile.write(token)
}
}
gradle.projectsEvaluated {
preBuild.dependsOn('accessToken')
}
android {
compileSdkVersion 23
buildToolsVersion "23"
repositories {
mavenCentral()
}
defaultConfig {
applicationId "com.mapbox.mapboxgl.testapp"
minSdkVersion 8
targetSdkVersion 23
versionCode 4
versionName "0.1.3"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
packagingOptions {
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/NOTICE.txt'
}
lintOptions {
checkAllWarnings true
warningsAsErrors true
disable 'IconDensities'
disable 'InvalidPackage'
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
}
}
dependencies {
compile(project(':MapboxGLAndroidSDK')) {
transitive = true
}
compile 'com.android.support:support-annotations:23.0.0'
compile 'com.android.support:support-v4:23.0.0'
compile 'com.android.support:appcompat-v7:23.0.0'
compile 'com.android.support:design:23.0.0'
compile('com.crashlytics.sdk.android:crashlytics:2.5.1@aar') {
transitive = true;
}
}
checkstyle {
configFile project.file('../checks.xml')
showViolations true
}
android.applicationVariants.all { variant ->
def name = variant.buildType.name
def checkstyle = project.tasks.create "checkstyle${name.capitalize()}", Checkstyle
checkstyle.dependsOn variant.javaCompile
checkstyle.source variant.javaCompile.source
checkstyle.classpath = project.fileTree(variant.javaCompile.destinationDir)
checkstyle.exclude('**/BuildConfig.java')
checkstyle.exclude('**/R.java')
project.tasks.getByName("check").dependsOn checkstyle
}

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<lint>
<!-- Ignore errors caused by icon burning -->
<issue id="IconLauncherShape" severity="ignore">
<ignore path="res/drawable-mdpi/icon.png" />
<ignore path="res/drawable-hdpi/icon.png" />
<ignore path="res/drawable-xdpi/icon.png" />
<ignore path="res/drawable-xxdpi/icon.png" />
<ignore path="res/drawable-mdpi/icon_burned.png" />
<ignore path="res/drawable-hdpi/icon_burned.png" />
<ignore path="res/drawable-xdpi/icon_burned.png" />
<ignore path="res/drawable-xxdpi/icon_burned.png" />
</issue>
<issue id="IconDuplicates" severity="ignore">
<ignore path="res/drawable-mdpi/icon.png" />
<ignore path="res/drawable-hdpi/icon.png" />
<ignore path="res/drawable-xdpi/icon.png" />
<ignore path="res/drawable-xxdpi/icon.png" />
<ignore path="res/drawable-mdpi/icon_burned.png" />
<ignore path="res/drawable-hdpi/icon_burned.png" />
<ignore path="res/drawable-xdpi/icon_burned.png" />
<ignore path="res/drawable-xxdpi/icon_burned.png" />
</issue>
<issue id="UnusedResources" severity="ignore">
<ignore path="res/drawable-mdpi/icon.png" />
<ignore path="res/drawable-hdpi/icon.png" />
<ignore path="res/drawable-xdpi/icon.png" />
<ignore path="res/drawable-xxdpi/icon.png" />
<ignore path="res/drawable-mdpi/icon_burned.png" />
<ignore path="res/drawable-hdpi/icon_burned.png" />
<ignore path="res/drawable-xdpi/icon_burned.png" />
<ignore path="res/drawable-xxdpi/icon_burned.png" />
</issue>
</lint>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
package="com.mapbox.mapboxgl.testapp">
<application
android:allowBackup="true"
android:fullBackupContent="true"
android:icon="@drawable/icon_burned"
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:supportsRtl="true">
<activity
android:name="com.mapbox.mapboxgl.testapp.MainActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<meta-data
android:name="io.fabric.ApiKey"
android:value="9724157045ff7d083492c6d9ae03e60e8609d461" />
</application>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
</manifest>

View File

@@ -0,0 +1 @@
{"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-121.3714599609375,38.64583568648869],[-121.48681640624999,38.64047263931154],[-121.53076171875,38.61579745317875],[-121.56509399414061,38.57823196583313],[-121.75186157226561,38.533127435052776],[-121.97021484374999,38.35350340353833],[-122.08007812499999,38.24680876017446],[-122.16796875,38.19718009396176],[-122.23800659179686,38.09457899232253],[-122.27371215820312,38.01455819225337],[-122.33551025390625,37.94094845586459],[-122.27371215820312,37.81846319511331],[-122.4151611328125,37.7652868250379]]}}]}

View File

@@ -0,0 +1 @@
{"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[-122.28813171386719,38.617406963286136],[-122.26959228515624,38.6833657775237],[-122.18238830566406,38.55568323796419],[-122.10617065429688,38.51378825951165],[-122.11509704589845,38.50465406475561],[-122.18307495117188,38.542795073979015],[-122.19955444335938,38.496593518947556],[-122.28813171386719,38.617406963286136]]]}}]}

View File

@@ -0,0 +1,453 @@
package com.mapbox.mapboxgl.testapp;
import android.Manifest;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.graphics.PointF;
import android.location.Location;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.NavigationView;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;
import com.crashlytics.android.Crashlytics;
import com.mapbox.mapboxgl.annotations.MarkerOptions;
import com.mapbox.mapboxgl.annotations.PolygonOptions;
import com.mapbox.mapboxgl.annotations.PolylineOptions;
import com.mapbox.mapboxgl.geometry.LatLng;
import com.mapbox.mapboxgl.views.MapView;
import io.fabric.sdk.android.Fabric;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
//
// Static members
//
// Used for saving instance state
private static final String STATE_IS_ANNOTATIONS_ON = "isAnnotationsOn";
private static final String STATE_SELECTED_STYLE = "selectedStyle";
// Used for permissions requests
private static final int PERMISSIONS_LOCATION = 0;
//
// Instance members
//
// Used for the UI
private DrawerLayout mDrawerLayout;
private MapView mMapView;
private TextView mFpsTextView;
private int mSelectedStyle = R.id.actionStyleMapboxStreets;
NavigationView mNavigationView;
// Used for GPS
private FloatingActionButton mLocationFAB;
// Used for Annotations
private boolean mIsAnnotationsOn = false;
//
// Lifecycle events
//
// Called when activity is created
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Fabric.with(this, new Crashlytics());
// Load the layout
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
final ActionBar ab = getSupportActionBar();
if (ab != null) {
ab.setHomeAsUpIndicator(R.drawable.ic_menu);
ab.setDisplayHomeAsUpEnabled(true);
}
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mNavigationView = (NavigationView) findViewById(R.id.nav_view);
if (mNavigationView != null) {
setupDrawerContent(mNavigationView);
}
mMapView = (MapView) findViewById(R.id.mainMapView);
// Load the access token
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(getResources().openRawResource(R.raw.token)));
String line = reader.readLine();
mMapView.setAccessToken(line);
} catch (IOException e) {
Log.e(TAG, "Error loading access token from token.txt: " + e.toString());
}
mMapView.onCreate(savedInstanceState);
mMapView.setOnFpsChangedListener(new MyOnFpsChangedListener());
final GestureDetector gestureDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() {
public void onLongPress(final MotionEvent e) {
float x = e.getX();
float y = e.getY();
// flip y direction vertically to match core GL
y = mMapView.getHeight() - y;
LatLng position = mMapView.fromScreenLocation(new PointF(x, y));
mMapView.addMarker(new MarkerOptions()
.position(position)
.title("Dropped Pin")
.sprite("default_marker"));
}
});
mMapView.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
});
mFpsTextView = (TextView) findViewById(R.id.view_fps);
mFpsTextView.setText("");
mLocationFAB = (FloatingActionButton)findViewById(R.id.locationFAB);
mLocationFAB.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Toggle GPS position updates
toggleGps(!mMapView.isMyLocationEnabled());
if (mMapView.isMyLocationEnabled()) {
Location location = mMapView.getMyLocation();
if (location != null) {
mMapView.setZoomLevel(8);
mMapView.setCenterCoordinate(new LatLng(location));
}
}
}
});
// Restore saved state
if (savedInstanceState != null) {
mIsAnnotationsOn = savedInstanceState.getBoolean(STATE_IS_ANNOTATIONS_ON);
mSelectedStyle = savedInstanceState.getInt(STATE_SELECTED_STYLE);
}
// Set default UI state
mNavigationView.getMenu().findItem(R.id.action_compass).setChecked(mMapView.isCompassEnabled());
mNavigationView.getMenu().findItem(R.id.action_debug).setChecked(mMapView.isDebugActive());
mNavigationView.getMenu().findItem(R.id.action_markers).setChecked(mIsAnnotationsOn);
changeMapStyle(mSelectedStyle);
toggleGps(mMapView.isMyLocationEnabled());
}
/**
* Dispatch onStart() to all fragments. Ensure any created loaders are
* now started.
*/
@Override
protected void onStart() {
super.onStart();
mMapView.onStart();
}
@Override
protected void onStop() {
super.onStop();
mMapView.onStop();
}
// Called when our app goes into the background
@Override
public void onPause() {
super.onPause();
mMapView.onPause();
}
// Called when our app comes into the foreground
@Override
public void onResume() {
super.onResume();
mMapView.onResume();
}
@Override
protected void onDestroy() {
super.onDestroy();
mMapView.onDestroy();
}
// Called when we need to save instance state
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mMapView.onSaveInstanceState(outState);
outState.putBoolean(STATE_IS_ANNOTATIONS_ON, mIsAnnotationsOn);
outState.putInt(STATE_SELECTED_STYLE, mSelectedStyle);
}
// Called when the system is running low on memory
@Override
public void onLowMemory() {
super.onLowMemory();
mMapView.onLowMemory();
}
//
// Other events
//
// Called when pressing action bar items
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
mDrawerLayout.openDrawer(GravityCompat.START);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
switch (requestCode) {
case PERMISSIONS_LOCATION: {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
enableGps();
}
}
}
}
private void setupDrawerContent(NavigationView navigationView) {
navigationView.setNavigationItemSelectedListener(
new NavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(MenuItem menuItem) {
mDrawerLayout.closeDrawers();
// Respond To Selection
switch (menuItem.getItemId()) {
case R.id.action_debug:
// Toggle debug mode
mMapView.toggleDebug();
toggleFpsCounter(mMapView.isDebugActive());
menuItem.setChecked(mMapView.isDebugActive());
return true;
case R.id.action_markers:
// Toggle markers
toggleAnnotations(!mIsAnnotationsOn);
menuItem.setChecked(mIsAnnotationsOn);
return true;
case R.id.action_compass:
// Toggle compass
mMapView.setCompassEnabled(!mMapView.isCompassEnabled());
menuItem.setChecked(mMapView.isCompassEnabled());
return true;
/*
case R.id.followNone:
mMapView.setUserLocationTrackingMode(MapView.UserLocationTrackingMode.NONE);
return true;
case R.id.followFollow:
mMapView.setUserLocationTrackingMode(MapView.UserLocationTrackingMode.FOLLOW);
return true;
case R.id.followBearing:
mMapView.setUserLocationTrackingMode(MapView.UserLocationTrackingMode.FOLLOW_BEARING);
return true;
*/
default:
return changeMapStyle(menuItem.getItemId());
}
}
});
}
private void toggleFpsCounter(boolean enableFps) {
// Show the FPS counter
if (enableFps) {
mFpsTextView.setVisibility(View.VISIBLE);
mFpsTextView.setText(getResources().getString(R.string.label_fps));
} else {
mFpsTextView.setVisibility(View.INVISIBLE);
}
}
private boolean changeMapStyle(int id) {
switch (id) {
case R.id.actionStyleMapboxStreets:
mMapView.setStyleUrl(getString(R.string.styleURLMapboxStreets));
mNavigationView.getMenu().findItem(id).setChecked(true);
mSelectedStyle = id;
return true;
case R.id.actionStyleEmerald:
mMapView.setStyleUrl(getString(R.string.styleURLEmerald));
mNavigationView.getMenu().findItem(id).setChecked(true);
mSelectedStyle = id;
return true;
case R.id.actionStyleLight:
mMapView.setStyleUrl(getString(R.string.styleURLLight));
mNavigationView.getMenu().findItem(id).setChecked(true);
mSelectedStyle = id;
return true;
case R.id.actionStyleDark:
mMapView.setStyleUrl(getString(R.string.styleURLDark));
mNavigationView.getMenu().findItem(id).setChecked(true);
mSelectedStyle = id;
return true;
case R.id.actionStyleSatellite:
mMapView.setStyleUrl(getString(R.string.styleURLSatellite));
mNavigationView.getMenu().findItem(id).setChecked(true);
mSelectedStyle = id;
return true;
default:
return false;
}
}
/**
* Enabled / Disable GPS location updates along with updating the UI
* @param enableGps true if GPS is to be enabled, false if GPS is to be disabled
*/
private void toggleGps(boolean enableGps) {
if (enableGps) {
if ((ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) ||
(ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED))
{
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSIONS_LOCATION);
} else {
enableGps();
}
} else {
mMapView.setMyLocationEnabled(false);
mLocationFAB.setColorFilter(Color.TRANSPARENT);
}
}
private void enableGps() {
mMapView.setMyLocationEnabled(true);
mLocationFAB.setColorFilter(ContextCompat.getColor(this, R.color.primary));
}
/**
* Enable / Disable Annotations.
*
* @param enableAnnotations True to display, False to hide
*/
private void toggleAnnotations(boolean enableAnnotations) {
if (enableAnnotations) {
if (!mIsAnnotationsOn) {
mIsAnnotationsOn = true;
addMarkers();
addPolyline();
addPolygon();
mMapView.setZoomLevel(7);
mMapView.setCenterCoordinate(new LatLng(38.11727, -122.22839));
}
} else {
if (mIsAnnotationsOn) {
mIsAnnotationsOn = false;
removeAnnotations();
}
}
}
private void addMarkers() {
LatLng backLot = new LatLng(38.649441, -121.369064);
mMapView.addMarker(new MarkerOptions()
.position(backLot)
.title("Back Lot")
.snippet("The back lot behind my house"));
LatLng cheeseRoom = new LatLng(38.531577,-122.010646);
mMapView.addMarker(new MarkerOptions()
.position(cheeseRoom)
.sprite("dog-park-15")
.title("Cheese Room")
.snippet("The only air conditioned room on the property!"));
}
private void addPolyline() {
try {
String geojsonStr = Util.loadStringFromAssets(this, "small_line.geojson");
LatLng[] latLngs = Util.parseGeoJSONCoordinates(geojsonStr);
MapView map = mMapView;
map.addPolyline(new PolylineOptions()
.add(latLngs)
.width(2)
.color(Color.RED));
} catch (Exception e) {
Log.e(TAG, "Error adding Polyline: "+ e);
e.printStackTrace();
}
}
private void addPolygon() {
try {
String geojsonStr = Util.loadStringFromAssets(this, "small_poly.geojson");
LatLng[] latLngs = Util.parseGeoJSONCoordinates(geojsonStr);
MapView map = mMapView;
ArrayList<PolygonOptions> opts = new ArrayList<>();
opts.add(new PolygonOptions()
.add(latLngs)
.strokeColor(Color.MAGENTA)
.fillColor(Color.BLUE));
map.addPolygons(opts).get(0);
} catch (Exception e) {
Log.e(TAG, "Error adding Polygon: "+ e);
e.printStackTrace();
}
}
private void removeAnnotations() {
mMapView.removeAnnotations();
}
// Called when FPS changes
private class MyOnFpsChangedListener implements MapView.OnFpsChangedListener {
@Override
public void onFpsChanged(double fps) {
mFpsTextView.setText(getResources().getString(R.string.label_fps) + String.format(" %4.2f", fps));
}
}
}

View File

@@ -0,0 +1,116 @@
package com.mapbox.mapboxgl.testapp;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.mapbox.mapboxgl.views.MapView;
public class MapFragment extends Fragment {
//
// Static members
//
// Tag used for logging
private static final String TAG = "MapFragment";
//
// Instance members
//
// The map
private MapView mMap;
//
// Lifecycle events
//
// Called when the fragment is created
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
Log.v(TAG, "onCreateView");
// Create the map
mMap = (MapView) inflater.inflate(R.layout.fragment_main, container, true);
// Need to pass on any saved state to the map
mMap.onCreate(savedInstanceState);
// Return the map as the root view
return mMap;
}
// Called when the fragment is destroyed
@Override
public void onDestroyView() {
super.onDestroyView();
Log.v(TAG, "onDestroyView");
// Need to pass on to view
mMap.onDestroy();
mMap = null;
}
// Called when the fragment is visible
@Override
public void onStart() {
super.onStart();
Log.v(TAG, "onStart");
// Need to pass on to view
mMap.onStart();
}
// Called when the fragment is invisible
@Override
public void onStop() {
super.onStop();
Log.v(TAG, "onStop");
// Need to pass on to view
mMap.onStop();
}
// Called when the fragment is in the background
@Override
public void onPause() {
super.onPause();
Log.v(TAG, "onPause");
// Need to pass on to view
mMap.onPause();
}
// Called when the fragment is no longer in the background
@Override
public void onResume() {
super.onResume();
Log.v(TAG, "onResume");
// Need to pass on to view
mMap.onResume();
}
// Called before fragment is destroyed
@Override
public void onSaveInstanceState(Bundle outState) {
Log.v(TAG, "onSaveInstanceState");
// Need to retrieve any saved state from the map
mMap.onSaveInstanceState(outState);
super.onSaveInstanceState(outState);
}
//
// Property methods
//
public MapView getMap() {
return mMap;
}
}

View File

@@ -0,0 +1,61 @@
package com.mapbox.mapboxgl.testapp;
import android.content.Context;
import android.text.TextUtils;
import com.mapbox.mapboxgl.geometry.LatLng;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.Charset;
public class Util {
public static String loadStringFromAssets(final Context context, final String fileName) throws IOException {
if (TextUtils.isEmpty(fileName)) {
throw new NullPointerException("No GeoJSON File Name passed in.");
}
InputStream is = context.getAssets().open(fileName);
BufferedReader rd = new BufferedReader(new InputStreamReader(is, Charset.forName("UTF-8")));
return readAll(rd);
}
public static LatLng[] parseGeoJSONCoordinates(String geojsonStr) throws JSONException {
JSONObject jsonObject = new JSONObject(geojsonStr);
JSONArray features = jsonObject.getJSONArray("features");
JSONObject feature = features.getJSONObject(0);
JSONObject geometry = feature.getJSONObject("geometry");
String type = geometry.getString("type");
JSONArray coordinates;
if (type.equals("Polygon")) {
coordinates = geometry.getJSONArray("coordinates").getJSONArray(0);
} else {
coordinates = geometry.getJSONArray("coordinates");
}
int len = coordinates.length();
LatLng[] latLngs = new LatLng[coordinates.length()];
for (int i = 0; i < len; ++i) {
JSONArray coord = coordinates.getJSONArray(i);
double lng = coord.getDouble(0);
double lat = coord.getDouble(1);
latLngs[i] = new LatLng(lat, lng);
}
return latLngs;
}
private static String readAll(Reader rd) throws IOException {
StringBuilder sb = new StringBuilder();
int cp;
while ((cp = rd.read()) != -1) {
sb.append((char) cp);
}
return sb.toString();
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 549 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 660 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 976 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
android:id="@+id/drawer_layout"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/primary"
android:minHeight="?attr/actionBarSize"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"/>
<FrameLayout
android:id="@+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/toolbar">
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.mapbox.mapboxgl.views.MapView
android:id="@+id/mainMapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
<TextView
android:id="@+id/view_fps"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="@string/label_fps"
android:textAppearance="?android:attr/textAppearanceLarge"/>
<android.support.design.widget.FloatingActionButton
android:id="@+id/locationFAB"
android:src="@drawable/ic_my_location_black_24dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="@dimen/fab_margin"
app:backgroundTint="@color/white"
app:borderWidth="0dp"
/>
</android.support.design.widget.CoordinatorLayout>
</FrameLayout>
</RelativeLayout>
<android.support.design.widget.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:menu="@menu/menu_drawer"/>
</android.support.v4.widget.DrawerLayout>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<com.mapbox.mapboxgl.views.MapView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:mapbox="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>

View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_debug"
android:icon="@android:drawable/ic_menu_info_details"
android:title="@string/action_debug"
android:checkable="true"
/>
<item
android:id="@+id/action_markers"
android:icon="@android:drawable/ic_menu_myplaces"
android:title="@string/action_point_annotations"
android:checkable="true"
/>
<item android:id="@+id/action_compass"
android:icon="@android:drawable/ic_menu_compass"
android:title="@string/action_compass"
android:checkable="true"
/>
<!--
<group android:id="@+id/locationFollowModesSeparator"/>
<group android:id="@+id/locationFollowModes" android:checkableBehavior="single">
<item android:id="@+id/followNone"
android:title="@string/action_location_none"/>
<item android:id="@+id/followFollow"
android:title="@string/action_location_follow"/>
<item android:id="@+id/followBearing"
android:title="@string/action_location_bearing"/>
</group>
-->
<group android:id="@+id/stylesSeparator"/>
<group android:id="@+id/stylesGroup" android:checkableBehavior="single">
<item
android:id="@+id/actionStyleMapboxStreets"
android:title="@string/styleMapboxStreets" />
<item
android:id="@+id/actionStyleEmerald"
android:title="@string/styleEmerald" />
<item
android:id="@+id/actionStyleLight"
android:title="@string/styleLight" />
<item
android:id="@+id/actionStyleDark"
android:title="@string/styleDark" />
<item
android:id="@+id/actionStyleSatellite"
android:title="@string/styleSatellite" />
</group>
</menu>

View File

@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
</menu>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="primary">#1E8CAB</color>
<color name="primaryDark">#166B83</color>
<color name="accent">#E55E5E</color>
<color name="white">#FFFFFF</color>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="fab_margin">16dp</dimen>
</resources>

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Mapbox GL</string>
<string name="action_gps">Toggle GPS location</string>
<string name="action_location_none">Disable tracking</string>
<string name="action_location_follow">Use follow tracking mode</string>
<string name="action_location_bearing">Use bearing tracking mode tracking</string>
<string name="action_compass">Toggle compass</string>
<string name="action_debug">Toggle debug mode</string>
<string name="action_point_annotations">Toggle point annotations</string>
<string name="label_fps">FPS:</string>
<string name="styleMapboxStreets">Mapbox Streets</string>
<string name="styleEmerald">Emerald</string>
<string name="styleLight">Light</string>
<string name="styleDark">Dark</string>
<string name="styleSatellite">Satellite</string>
<string name="styleURLMapboxStreets">asset://styles/mapbox-streets-v7.json</string>
<string name="styleURLEmerald">asset://styles/emerald-v7.json</string>
<string name="styleURLLight">asset://styles/light-v7.json</string>
<string name="styleURLDark">asset://styles/dark-v7.json</string>
<string name="styleURLSatellite">asset://styles/satellite-v7.json</string>
<string-array name="outdoors_class_list">
<item>Day</item>
<item>Night</item>
</string-array>
</resources>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppTheme" parent="AppBaseTheme" />
<style name="AppBaseTheme" parent="@style/Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/primary</item>
<item name="colorPrimaryDark">@color/primaryDark</item>
<item name="colorAccent">@color/accent</item>
</style>
</resources>

View File

@@ -0,0 +1,4 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
task wrapper(type: Wrapper) {
gradleVersion = '2.5'
}

Some files were not shown because too many files have changed in this diff Show More