Merge branch 'release-agua' into tvn-merge-release

This commit is contained in:
Tobrun
2017-11-10 06:16:35 +01:00
117 changed files with 5073 additions and 824 deletions

3
.gitmodules vendored
View File

@@ -1,6 +1,3 @@
[submodule "platform/ios/vendor/SMCalloutView"]
path = platform/ios/vendor/SMCalloutView
url = https://github.com/nfarina/calloutview.git
[submodule "platform/ios/uitest/KIF"]
path = platform/ios/uitest/KIF
url = https://github.com/kif-framework/KIF.git

View File

@@ -222,6 +222,14 @@ iproj: $(IOS_PROJ_PATH)
ios-test: $(IOS_PROJ_PATH)
set -o pipefail && $(IOS_XCODEBUILD_SIM) -scheme 'CI' test $(XCPRETTY)
.PHONY: ios-sanitize-address
ios-sanitize-address: $(IOS_PROJ_PATH)
set -o pipefail && $(IOS_XCODEBUILD_SIM) -scheme 'CI' -enableAddressSanitizer YES test $(XCPRETTY)
.PHONY: ios-sanitize-thread
ios-sanitize-thread: $(IOS_PROJ_PATH)
set -o pipefail && $(IOS_XCODEBUILD_SIM) -scheme 'CI' -enableThreadSanitizer YES test $(XCPRETTY)
.PHONY: ipackage
ipackage: $(IOS_PROJ_PATH)
FORMAT=$(FORMAT) BUILD_DEVICE=$(BUILD_DEVICE) SYMBOLS=$(SYMBOLS) \
@@ -553,6 +561,11 @@ run-android-ui-test-$1-%: platform/android/configuration.gradle
-adb uninstall com.mapbox.mapboxsdk.testapp 2> /dev/null
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=$2 :MapboxGLAndroidSDKTestApp:connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.class="$$*"
# Symbolicate native stack trace with the specified abi
.PHONY: android-ndk-stack-$1
android-ndk-stack-$1: platform/android/configuration.gradle
adb logcat | ndk-stack -sym platform/android/MapboxGLAndroidSDK/build/intermediates/cmake/debug/obj/$2/
endef
# Explodes the arguments into individual variables
@@ -582,9 +595,9 @@ run-android-ui-test-%: run-android-ui-test-arm-v7-%
# Run Java Unit tests on the JVM of the development machine executing this
.PHONY: run-android-unit-test
run-android-unit-test: platform/android/configuration.gradle
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=none :MapboxGLAndroidSDKTestApp:testDebugUnitTest
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=none :MapboxGLAndroidSDK:testDebugUnitTest
run-android-unit-test-%: platform/android/configuration.gradle
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=none :MapboxGLAndroidSDKTestApp:testDebugUnitTest --tests "$*"
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=none :MapboxGLAndroidSDK:testDebugUnitTest --tests "$*"
# Run Instrumentation tests on AWS device farm, requires additional authentication through gradle.properties
.PHONY: run-android-ui-test-aws
@@ -641,6 +654,10 @@ android-lint-test-app: platform/android/configuration.gradle
android-javadoc: platform/android/configuration.gradle
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=none :MapboxGLAndroidSDK:javadocrelease
# Symbolicate ndk stack traces for the arm-v7 abi
.PHONY: android-ndk-stack
android-ndk-stack: android-ndk-stack-arm-v7
# Open Android Studio if machine is macos
ifeq ($(HOST_PLATFORM), macos)
.PHONY: aproj

View File

@@ -30,6 +30,10 @@ workflows:
- linux-gcc5-debug-coverage
- linux-gcc5-release-qt4
- linux-gcc5-release-qt5
- ios-debug
#- ios-sanitize-address
- ios-sanitize-thread
- macos-debug
step-library:
- &generate-cache-key
@@ -47,7 +51,7 @@ step-library:
- &save-cache
save_cache:
key: 'v3/{{ .Environment.CIRCLE_JOB }}/{{ arch }}/{{ .Branch }}/{{ checksum ".circle-week" }}'
paths: [ "node_modules", "/root/.ccache", "mason_packages/.binaries" ]
paths: [ "node_modules", "/root/.ccache", "~/.ccache", "mason_packages/.binaries" ]
- &reset-ccache-stats
@@ -97,6 +101,28 @@ step-library:
run:
name: Build qt-test
command: make qt-test
- &build-ios-test
run:
name: Build ios-test
command: make ios-test
- &build-macos-test
run:
name: Build and run macOS tests
command: make run-test
- &check-public-symbols
run:
name: Check public symbols
command: make check-public-symbols
- &install-macos-dependencies
run:
name: Install dependencies
command: |
brew install cmake
brew install ccache
- &run-node-tests
@@ -225,7 +251,7 @@ jobs:
--app platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/MapboxGLAndroidSDKTestApp-debug.apk \
--test platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/MapboxGLAndroidSDKTestApp-debug-androidTest.apk \
--device-ids shamu --os-version-ids 22 --locales en --orientations portrait --timeout 20m \
--test-targets "package com.mapbox.mapboxsdk.testapp.style" 2>&1 | tee firebase.log) || EXIT_CODE=$?
2>&1 | tee firebase.log) || EXIT_CODE=$?
FIREBASE_TEST_BUCKET=$(sed -n 's|^.*\[https://console.developers.google.com/storage/browser/\([^]]*\).*|gs://\1|p' firebase.log)
echo "Downloading from: ${FIREBASE_TEST_BUCKET}"
@@ -294,7 +320,7 @@ jobs:
- deploy:
name: Publish to Maven
command: |
if [ "${CIRCLE_BRANCH}" == "master" ]; then make run-android-upload-archives ; fi
if [ "${CIRCLE_BRANCH}" == "release-agua" ]; then make run-android-upload-archives ; fi
# ------------------------------------------------------------------------------
@@ -589,3 +615,80 @@ jobs:
command: |
xvfb-run --server-args="-screen 0 1024x768x24" \
scripts/valgrind.sh build/qt-linux-x86_64/Release/mbgl-test --gtest_filter=-*.Load --gtest_filter=-Memory.Vector
# ------------------------------------------------------------------------------
ios-debug:
macos:
xcode: "9.0"
environment:
BUILDTYPE: Debug
HOMEBREW_NO_AUTO_UPDATE: 1
steps:
- checkout
- *install-macos-dependencies
- *generate-cache-key
- *restore-cache
- *reset-ccache-stats
- *build-ios-test
- *check-public-symbols
- *show-ccache-stats
- *save-cache
# ------------------------------------------------------------------------------
ios-sanitize-address:
macos:
xcode: "9.0"
environment:
BUILDTYPE: Debug
HOMEBREW_NO_AUTO_UPDATE: 1
steps:
- checkout
- *install-macos-dependencies
- *generate-cache-key
- *restore-cache
- *reset-ccache-stats
- run:
name: Build and run SDK unit tests with address sanitizer
command: make ios-sanitize-address
- *show-ccache-stats
- *save-cache
# ------------------------------------------------------------------------------
ios-sanitize-thread:
macos:
xcode: "9.0"
environment:
BUILDTYPE: Debug
HOMEBREW_NO_AUTO_UPDATE: 1
steps:
- checkout
- *install-macos-dependencies
- *generate-cache-key
- *restore-cache
- *reset-ccache-stats
- run:
name: Build and run SDK unit tests with thread sanitizer
command: make ios-sanitize-thread
- *show-ccache-stats
- *save-cache
# ------------------------------------------------------------------------------
macos-debug:
macos:
xcode: "9.0"
environment:
BUILDTYPE: Debug
HOMEBREW_NO_AUTO_UPDATE: 1
steps:
- checkout
- *install-macos-dependencies
- *generate-cache-key
- *restore-cache
- *reset-ccache-stats
- *build-macos-test
- *check-public-symbols
- *show-ccache-stats
- *save-cache
- store_artifacts:
path: test/fixtures
destination: test/fixtures

View File

@@ -40,6 +40,8 @@ public:
std::vector<Feature> queryRenderedFeatures(const ScreenBox& box, const RenderedQueryOptions& options = {}) const;
std::vector<Feature> querySourceFeatures(const std::string& sourceID, const SourceQueryOptions& options = {}) const;
AnnotationIDs queryPointAnnotations(const ScreenBox& box) const;
AnnotationIDs queryShapeAnnotations(const ScreenBox& box) const;
AnnotationIDs getAnnotationIDs(const std::vector<Feature>&) const;
// Debug
void dumpDebugLogs();

View File

@@ -4,11 +4,110 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to
## 5.2.0 - TBA
* Add support for ImageSource [#9110](https://github.com/mapbox/mapbox-gl-native/pull/9110)
* Increased the default maximum zoom level from 20 to 22. ([#9835](https://github.com/mapbox/mapbox-gl-native/pull/9835))
* Added `MapboxMap.getCameraForGeometry()` to get a camera with zoom level and center coordinate computed to fit a shape. ([#10107](https://github.com/mapbox/mapbox-gl-native/pull/10107))
## 5.2.0-beta.4 - November 3, 2017
### 5.1.4 - September 25, 2017
- Revert adding mapbox-android-core dependency (#10354) [#10380](https://github.com/mapbox/mapbox-gl-native/pull/10380)
- Asynchronous TextureView [#10370](https://github.com/mapbox/mapbox-gl-native/pull/10370)
- Workaround OkHttp bug on Android O [10366](https://github.com/mapbox/mapbox-gl-native/pull/10366)
- Revisit logo resize [10553](https://github.com/mapbox/mapbox-gl-native/pull/10353)
- Logo resize for MapSnapshotter [#10312](https://github.com/mapbox/mapbox-gl-native/pull/10312)
- Make location provider optional [#10354](https://github.com/mapbox/mapbox-gl-native/pull/10354)
- Check for positive animation value [#10348](https://github.com/mapbox/mapbox-gl-native/pull/10348)
- Fix IAE of ease/animate [#10338](https://github.com/mapbox/mapbox-gl-native/pull/10338)
- Run full test suite on CI [#10333](https://github.com/mapbox/mapbox-gl-native/pull/10333)
- Make sure camera position gets updated in onFinish() callback after camera.move [#10324](https://github.com/mapbox/mapbox-gl-native/pull/10324)
- throw IAE in animate() and easeCamera() when duration <= 0 [#10321](https://github.com/mapbox/mapbox-gl-native/pull/10321)
- Don't save state if map hasn't been initialised [#10320](https://github.com/mapbox/mapbox-gl-native/pull/10320)
- Make map snapshot optional [#10310](https://github.com/mapbox/mapbox-gl-native/pull/10310)
- Synchronise locationlastions with Transifex [#10309](https://github.com/mapbox/mapbox-gl-native/pull/10309)
- MapboxMap#addImages [#10281](https://github.com/mapbox/mapbox-gl-native/pull/10281)
- Move shape annotation click handling to core [#10267](https://github.com/mapbox/mapbox-gl-native/pull/10267)
- Map snapshotter additions [#10163](https://github.com/mapbox/mapbox-gl-native/pull/10163)
- Add velocity to gestures / port animations to SDK animators [#10202](https://github.com/mapbox/mapbox-gl-native/pull/10202)
- Don't save state if map hasn't been initialised [#10320](https://github.com/mapbox/mapbox-gl-native/pull/10320)
- android.hardware.location.gps feature should not be required [#10347](https://github.com/mapbox/mapbox-gl-native/pull/10347)
## 5.2.0-beta.3 - October 26, 2017
- Reorganize dependencies [#10268](https://github.com/mapbox/mapbox-gl-native/pull/10268)
- Blacklist VAO usage on adreno 3xx [#10291](https://github.com/mapbox/mapbox-gl-native/pull/10291)
- On stop null check [#10259](https://github.com/mapbox/mapbox-gl-native/pull/10259)
## 5.2.0-beta.2 - October 19, 2017
- Wire up MapZoomButtonController with camera change events [#10221](https://github.com/mapbox/mapbox-gl-native/pull/10221)
- Execute callbacks only when not idle [#10220](https://github.com/mapbox/mapbox-gl-native/pull/10220)
- Cleanup unused gradle plugins [#10211](https://github.com/mapbox/mapbox-gl-native/pull/10211)
- add FileSource pause/resume [#9977](https://github.com/mapbox/mapbox-gl-native/pull/9977)
- add make target for ndk-stack [#10185](https://github.com/mapbox/mapbox-gl-native/pull/10185)
- Add interpolator examples [#10067](https://github.com/mapbox/mapbox-gl-native/pull/10067)
- Add an UnsatisfiedLinkError safeguard [#10180](https://github.com/mapbox/mapbox-gl-native/pull/10180)
- Hold off handling hover events untill map has been created [#10142](https://github.com/mapbox/mapbox-gl-native/pull/10142)
- Added `MapboxMap.getCameraForGeometry()` to get a camera with zoom level and center coordinate computed to fit a shape [#10107](https://github.com/mapbox/mapbox-gl-native/pull/10107)
- Fine tune gesture zoom & rotation [#10134](https://github.com/mapbox/mapbox-gl-native/pull/10134)
## 5.2.0-beta.1 - October 6, 2017
- Allow multiple listeners for camera events, deprecate old API [#10141](https://github.com/mapbox/mapbox-gl-native/pull/10141)
- Update symbol layer example with location [#10092](https://github.com/mapbox/mapbox-gl-native/pull/10092)
- Make OfflineTilePyramidRegionDefinition parceable [#10080](https://github.com/mapbox/mapbox-gl-native/pull/10080)
- Fix 5.2.0-SNAPSHOT CI build failing [#10079](https://github.com/mapbox/mapbox-gl-native/pull/10079)
- Deprecate MarkerView [#9782](https://github.com/mapbox/mapbox-gl-native/pull/9782)
- Hide overlain views on initalisation [#10068](https://github.com/mapbox/mapbox-gl-native/pull/10068)
- API for platform side animations [#10001](https://github.com/mapbox/mapbox-gl-native/pull/10001)
- Android asynchronous rendering [#9576](https://github.com/mapbox/mapbox-gl-native/pull/9576)
- Set error handler when starting snapshotter [#10035](https://github.com/mapbox/mapbox-gl-native/pull/10035)
- Hook camera events into compass [#10019](https://github.com/mapbox/mapbox-gl-native/pull/10019)
- Testapp cleanup [#10006](https://github.com/mapbox/mapbox-gl-native/pull/10006)
- Update zoom function example with selected state [#9987](https://github.com/mapbox/mapbox-gl-native/pull/9987)
- Add style inspection to debug activity [#9773](https://github.com/mapbox/mapbox-gl-native/pull/9773)
- Bump external dependencies [#9972](https://github.com/mapbox/mapbox-gl-native/pull/9972)
- Don't recycle bitmap for icon reuse. [#9966](https://github.com/mapbox/mapbox-gl-native/pull/9966)
- Android snapshotter [#9748](https://github.com/mapbox/mapbox-gl-native/pull/9748)
- Revert #9764 [#9851](https://github.com/mapbox/mapbox-gl-native/pull/9851)
- Update docs replacing Bitrise mentions with CircleCI [#9515](https://github.com/mapbox/mapbox-gl-native/pull/9515)
- Style image accessor [#9763](https://github.com/mapbox/mapbox-gl-native/pull/9763)
- Update readme with checkstyle and ndk-stack [#9788](https://github.com/mapbox/mapbox-gl-native/pull/9788)
- make android-check [#9787](https://github.com/mapbox/mapbox-gl-native/pull/9787)
- Deprecate MyLocationView in favor of LocationLayer plugin [#9771](https://github.com/mapbox/mapbox-gl-native/pull/9771)
- Increase firebase timeout for CI testing [#9774](https://github.com/mapbox/mapbox-gl-native/pull/9774)
- Restore max zoom to 25.5 [#9765](https://github.com/mapbox/mapbox-gl-native/pull/9765)
- Update example of camera zoom function on a symbol layer. [#9743](https://github.com/mapbox/mapbox-gl-native/pull/9743)
- Optimise icon management [#9643](https://github.com/mapbox/mapbox-gl-native/pull/9643)
- Expose setStyleJson and getStyleJson [#9714](https://github.com/mapbox/mapbox-gl-native/pull/9714)
- update LatLngBounds activity with BottomSheet interaction [#9736](https://github.com/mapbox/mapbox-gl-native/pull/9736)
- post updating InfoWindow update for InfoWindowAdapter [#9716](https://github.com/mapbox/mapbox-gl-native/pull/9716)
- Annotate MapboxMap class with UiThread [#9712](https://github.com/mapbox/mapbox-gl-native/pull/9712)
- Move ZoomButtonController creation to view initalisation [#9587](https://github.com/mapbox/mapbox-gl-native/pull/9587)
- Solve lint issues, reduce baseline [#9627](https://github.com/mapbox/mapbox-gl-native/pull/9627)
- Remove wear module from project [#9618](https://github.com/mapbox/mapbox-gl-native/pull/9618)
- Add zMediaOverlay configuration + bottom sheet integration [#9592](https://github.com/mapbox/mapbox-gl-native/pull/9592)
- Forward getMapAsync to map for fragment [#9621](https://github.com/mapbox/mapbox-gl-native/pull/9621)
- Make target for dumping system gfx information [#9616](https://github.com/mapbox/mapbox-gl-native/pull/9616)
- Make target documentation [#9617](https://github.com/mapbox/mapbox-gl-native/pull/9617)
- onGlobalLayout hook for map creation [#9607](https://github.com/mapbox/mapbox-gl-native/pull/9607)
- Custom viewpager for horizontal swiping [#9601](https://github.com/mapbox/mapbox-gl-native/pull/9601)
- Disable program caching on Adreno 3xx, 4xx, and 5xx GPUs due to known bugs [#9574](https://github.com/mapbox/mapbox-gl-native/pull/9574)
- Avoid creating InfoWindow iterator if no InfoWindows are shown [#9477](https://github.com/mapbox/mapbox-gl-native/pull/9477)
- Rewire map initialisation [#9462](https://github.com/mapbox/mapbox-gl-native/pull/9462)
- Trying to update non-existent polyline fix [#9544](https://github.com/mapbox/mapbox-gl-native/pull/9544)
- Location accuracy threshold [#9472](https://github.com/mapbox/mapbox-gl-native/pull/9472)
- Rewire gesture handling and telemetry event push [#9494](https://github.com/mapbox/mapbox-gl-native/pull/9494)
- run style instrumentation tests on CI [#9353](https://github.com/mapbox/mapbox-gl-native/pull/9353)
- Fix javadoc comment for public setOfflineMapboxTileCountLimit method [#9454](https://github.com/mapbox/mapbox-gl-native/pull/9454)
- add Map change & visibility test activities [#9425](https://github.com/mapbox/mapbox-gl-native/pull/9425)
- build release package once during ci build [#9351](https://github.com/mapbox/mapbox-gl-native/pull/9351)
- Add support for ImageSource [#9110](https://github.com/mapbox/mapbox-gl-native/pull/9110)
- Increased the default maximum zoom level from 20 to 22. [#9835](https://github.com/mapbox/mapbox-gl-native/pull/9835)
## 5.1.5 - October 31, 2017
* Remove obsolete terminate context/display calls [#10162](https://github.com/mapbox/mapbox-gl-native/pull/10162)
* Determine need for clip ID based on actual layers/tiles [#10216](https://github.com/mapbox/mapbox-gl-native/pull/10216)
* Correctly alter sprite URLs [#10217](https://github.com/mapbox/mapbox-gl-native/pull/10217)
* Russian and Ukrainian localizations [#9945](https://github.com/mapbox/mapbox-gl-native/pull/9945)
## 5.1.4 - September 25, 2017
* Update translations [#10033](https://github.com/mapbox/mapbox-gl-native/pull/10033) & [#9945](https://github.com/mapbox/mapbox-gl-native/pull/9945)
* Continue rendering tiles despite erros [#10012](https://github.com/mapbox/mapbox-gl-native/pull/10012)
@@ -101,7 +200,7 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to
* Infinite location animation updates [#9194](https://github.com/mapbox/mapbox-gl-native/pull/9194)
* Invoke callback with valid fling gestures [#9192](https://github.com/mapbox/mapbox-gl-native/pull/9192)
* Keep location tracking after screen rotation [#9187](https://github.com/mapbox/mapbox-gl-native/pull/9187)
* Update components with camera values when animating [#9174](https://github.com/mapbox/mapbox-gl-native/pull/9174)
* Update components with camera values when animating [#9174](https://github.com/mapbox/mapbox-gl-native/pull/9174)
* Validate if gestures should execute [#9173](https://github.com/mapbox/mapbox-gl-native/pull/9173)
* Custom location source and LOST integration [#9142](https://github.com/mapbox/mapbox-gl-native/pull/9142)
@@ -503,12 +602,6 @@ Mapbox Android 4.0.0 is the most ambitious Android release to date with 3 major
- Satellite Streets Style ([#2739](https://github.com/mapbox/mapbox-gl-native/issues/2739))
- **RESOLVED** Black Screen On Ice Cream Sandwich and Jelly Bean devices ([#2802](https://github.com/mapbox/mapbox-gl-native/issues/2802))
## 2.1.0 - October 21, 2015
- Initial Android release.
Known issues:
- Black Screen On Ice Cream Sandwich and Jelly Bean devices ([#2802](https://github.com/mapbox/mapbox-gl-native/issues/2802))
- Resolved in 2.2.0

View File

@@ -2,8 +2,7 @@ apply plugin: 'com.android.library'
dependencies {
compile rootProject.ext.dep.supportAnnotations
compile rootProject.ext.dep.supportV4
compile rootProject.ext.dep.supportDesign
compile rootProject.ext.dep.supportFragmentV4
compile rootProject.ext.dep.timber
compile rootProject.ext.dep.okhttp3
provided(rootProject.ext.dep.lost) {
@@ -21,6 +20,7 @@ dependencies {
// Mapbox Android Services (Telemetry support)
compile(rootProject.ext.dep.mapboxAndroidTelemetry) {
transitive = true
exclude group: 'com.android.support'
}
}

View File

@@ -3,6 +3,7 @@
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
<uses-feature android:name="android.hardware.wifi" android:required="false" /> <!-- Implied by ACCESS_WIFI_STATE. -->
<uses-feature android:name="android.hardware.location.gps" android:required="false"/>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

View File

@@ -1,5 +1,7 @@
package com.mapbox.mapboxsdk;
import timber.log.Timber;
/**
* Centralises the knowledge about "mapbox-gl" library loading.
*/
@@ -9,7 +11,10 @@ public class LibraryLoader {
* Loads "libmapbox-gl.so" native shared library.
*/
public static void load() {
System.loadLibrary("mapbox-gl");
try {
System.loadLibrary("mapbox-gl");
} catch (UnsatisfiedLinkError error) {
Timber.e(error, "Failed to load native shared library.");
}
}
}

View File

@@ -80,12 +80,12 @@ public class MapboxConstants {
/**
* The currently used minimun scale factor to clamp to when a quick zoom gesture occurs
*/
public static final float MINIMUM_SCALE_FACTOR_CLAMP = 0.65f;
public static final float MINIMUM_SCALE_FACTOR_CLAMP = 0.00f;
/**
* The currently used maximum scale factor to clamp to when a quick zoom gesture occurs
*/
public static final float MAXIMUM_SCALE_FACTOR_CLAMP = 1.35f;
public static final float MAXIMUM_SCALE_FACTOR_CLAMP = 0.45f;
/**
* Fragment Argument Key for MapboxMapOptions

View File

@@ -85,7 +85,14 @@ class HTTPRequest implements Callback {
}
mRequest = builder.build();
mCall = mClient.newCall(mRequest);
mCall.enqueue(this);
// TODO remove code block for workaround in #10303
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) {
mCall.enqueue(this);
} else {
// Calling execute instead of enqueue is a workaround for #10303
onResponse(mCall, mCall.execute());
}
} catch (Exception exception) {
onFailure(exception);
}

View File

@@ -21,7 +21,6 @@ import com.mapbox.mapboxsdk.annotations.Polygon;
import com.mapbox.mapboxsdk.annotations.PolygonOptions;
import com.mapbox.mapboxsdk.annotations.Polyline;
import com.mapbox.mapboxsdk.annotations.PolylineOptions;
import com.mapbox.services.commons.geojson.Feature;
import java.util.ArrayList;
import java.util.List;
@@ -41,7 +40,6 @@ import timber.log.Timber;
*/
class AnnotationManager {
private static final String LAYER_ID_SHAPE_ANNOTATIONS = "com.mapbox.annotations.shape.";
private static final long NO_ANNOTATION_ID = -1;
private final MapView mapView;
@@ -50,7 +48,6 @@ class AnnotationManager {
private final MarkerViewManager markerViewManager;
private final LongSparseArray<Annotation> annotationsArray;
private final List<Marker> selectedMarkers = new ArrayList<>();
private final List<String> shapeAnnotationIds = new ArrayList<>();
private MapboxMap mapboxMap;
private MapboxMap.OnMarkerClickListener onMarkerClickListener;
@@ -58,13 +55,14 @@ class AnnotationManager {
private MapboxMap.OnPolylineClickListener onPolylineClickListener;
private Annotations annotations;
private ShapeAnnotations shapeAnnotations;
private Markers markers;
private Polygons polygons;
private Polylines polylines;
AnnotationManager(NativeMapView view, MapView mapView, LongSparseArray<Annotation> annotationsArray,
MarkerViewManager markerViewManager, IconManager iconManager, Annotations annotations,
Markers markers, Polygons polygons, Polylines polylines) {
Markers markers, Polygons polygons, Polylines polylines, ShapeAnnotations shapeAnnotations) {
this.mapView = mapView;
this.annotationsArray = annotationsArray;
this.markerViewManager = markerViewManager;
@@ -73,6 +71,7 @@ class AnnotationManager {
this.markers = markers;
this.polygons = polygons;
this.polylines = polylines;
this.shapeAnnotations = shapeAnnotations;
if (view != null) {
// null checking needed for unit tests
view.addOnMapChangedListener(markerViewManager);
@@ -122,9 +121,6 @@ class AnnotationManager {
// do icon cleanup
iconManager.iconCleanup(marker.getIcon());
}
} else {
// instanceOf Polygon/Polyline
shapeAnnotationIds.remove(annotation.getId());
}
annotations.removeBy(annotation);
}
@@ -143,9 +139,6 @@ class AnnotationManager {
} else {
iconManager.iconCleanup(marker.getIcon());
}
} else {
// instanceOf Polygon/Polyline
shapeAnnotationIds.remove(annotation.getId());
}
}
annotations.removeBy(annotationList);
@@ -167,9 +160,6 @@ class AnnotationManager {
} else {
iconManager.iconCleanup(marker.getIcon());
}
} else {
// instanceOf Polygon/Polyline
shapeAnnotationIds.remove(annotation.getId());
}
}
annotations.removeAll();
@@ -227,17 +217,11 @@ class AnnotationManager {
//
Polygon addPolygon(@NonNull PolygonOptions polygonOptions, @NonNull MapboxMap mapboxMap) {
Polygon polygon = polygons.addBy(polygonOptions, mapboxMap);
shapeAnnotationIds.add(LAYER_ID_SHAPE_ANNOTATIONS + polygon.getId());
return polygon;
return polygons.addBy(polygonOptions, mapboxMap);
}
List<Polygon> addPolygons(@NonNull List<PolygonOptions> polygonOptionsList, @NonNull MapboxMap mapboxMap) {
List<Polygon> polygonList = polygons.addBy(polygonOptionsList, mapboxMap);
for (Polygon polygon : polygonList) {
shapeAnnotationIds.add(LAYER_ID_SHAPE_ANNOTATIONS + polygon.getId());
}
return polygonList;
return polygons.addBy(polygonOptionsList, mapboxMap);
}
void updatePolygon(Polygon polygon) {
@@ -257,17 +241,11 @@ class AnnotationManager {
//
Polyline addPolyline(@NonNull PolylineOptions polylineOptions, @NonNull MapboxMap mapboxMap) {
Polyline polyline = polylines.addBy(polylineOptions, mapboxMap);
shapeAnnotationIds.add(LAYER_ID_SHAPE_ANNOTATIONS + polyline.getId());
return polyline;
return polylines.addBy(polylineOptions, mapboxMap);
}
List<Polyline> addPolylines(@NonNull List<PolylineOptions> polylineOptionsList, @NonNull MapboxMap mapboxMap) {
List<Polyline> polylineList = polylines.addBy(polylineOptionsList, mapboxMap);
for (Polyline polyline : polylineList) {
shapeAnnotationIds.add(LAYER_ID_SHAPE_ANNOTATIONS + polyline.getId());
}
return polylineList;
return polylines.addBy(polylineOptionsList, mapboxMap);
}
void updatePolyline(Polyline polyline) {
@@ -397,11 +375,11 @@ class AnnotationManager {
//
boolean onTap(PointF tapPoint) {
if (!shapeAnnotationIds.isEmpty()) {
ShapeAnnotationHit shapeAnnotationHit = getShapeAnnotationHitFromTap(tapPoint);
long shapeAnnotationId = new ShapeAnnotationHitResolver(mapboxMap).execute(shapeAnnotationHit);
if (shapeAnnotationId != NO_ANNOTATION_ID) {
handleClickForShapeAnnotation(shapeAnnotationId);
ShapeAnnotationHit shapeAnnotationHit = getShapeAnnotationHitFromTap(tapPoint);
Annotation annotation = new ShapeAnnotationHitResolver(shapeAnnotations).execute(shapeAnnotationHit);
if (annotation != null) {
if (handleClickForShapeAnnotation(annotation)) {
return true;
}
}
@@ -418,16 +396,18 @@ class AnnotationManager {
tapPoint.x + touchTargetSide,
tapPoint.y + touchTargetSide
);
return new ShapeAnnotationHit(tapRect, shapeAnnotationIds.toArray(new String[shapeAnnotationIds.size()]));
return new ShapeAnnotationHit(tapRect);
}
private void handleClickForShapeAnnotation(long shapeAnnotationId) {
Annotation annotation = getAnnotation(shapeAnnotationId);
private boolean handleClickForShapeAnnotation(Annotation annotation) {
if (annotation instanceof Polygon && onPolygonClickListener != null) {
onPolygonClickListener.onPolygonClick((Polygon) annotation);
return true;
} else if (annotation instanceof Polyline && onPolylineClickListener != null) {
onPolylineClickListener.onPolylineClick((Polyline) annotation);
return true;
}
return false;
}
private MarkerHit getMarkerHitFromTouchArea(PointF tapPoint) {
@@ -470,28 +450,19 @@ class AnnotationManager {
private static class ShapeAnnotationHitResolver {
private MapboxMap mapboxMap;
private ShapeAnnotations shapeAnnotations;
ShapeAnnotationHitResolver(MapboxMap mapboxMap) {
this.mapboxMap = mapboxMap;
ShapeAnnotationHitResolver(ShapeAnnotations shapeAnnotations) {
this.shapeAnnotations = shapeAnnotations;
}
public long execute(ShapeAnnotationHit shapeHit) {
long foundAnnotationId = NO_ANNOTATION_ID;
List<Feature> features = mapboxMap.queryRenderedFeatures(shapeHit.tapPoint, shapeHit.layerIds);
if (!features.isEmpty()) {
foundAnnotationId = getIdFromFeature(features.get(0));
}
return foundAnnotationId;
}
private long getIdFromFeature(Feature feature) {
try {
return Long.valueOf(feature.getId());
} catch (NumberFormatException exception) {
Timber.e(exception, "Couldn't parse feature id to a long, with id: %s", feature.getId());
return NO_ANNOTATION_ID;
public Annotation execute(ShapeAnnotationHit shapeHit) {
Annotation foundAnnotation = null;
List<Annotation> annotations = shapeAnnotations.obtainAllIn(shapeHit.tapPoint);
if (annotations.size() > 0) {
foundAnnotation = annotations.get(0);
}
return foundAnnotation;
}
}
@@ -567,11 +538,9 @@ class AnnotationManager {
private static class ShapeAnnotationHit {
private final RectF tapPoint;
private final String[] layerIds;
ShapeAnnotationHit(RectF tapRect, String[] layerIds) {
this.tapPoint = tapRect;
this.layerIds = layerIds;
ShapeAnnotationHit(RectF tapPoint) {
this.tapPoint = tapPoint;
}
}

View File

@@ -1,12 +1,12 @@
package com.mapbox.mapboxsdk.maps;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.v7.app.AlertDialog;
import android.text.Html;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
@@ -56,7 +56,7 @@ class AttributionDialogManager implements View.OnClickListener, DialogInterface.
private void showAttributionDialog() {
attributionKeys = attributionMap.keySet().toArray(new String[attributionMap.size()]);
AlertDialog.Builder builder = new AlertDialog.Builder(context, R.style.mapbox_AlertDialogStyle);
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.mapbox_attributionsDialogTitle);
builder.setAdapter(new ArrayAdapter<>(context, R.layout.mapbox_attribution_list_item, attributionKeys), this);
builder.show();
@@ -77,7 +77,7 @@ class AttributionDialogManager implements View.OnClickListener, DialogInterface.
}
private void showTelemetryDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(context, R.style.mapbox_AlertDialogStyle);
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.mapbox_attributionTelemetryTitle);
builder.setMessage(R.string.mapbox_attributionTelemetryMessage);
builder.setPositiveButton(R.string.mapbox_attributionTelemetryPositive, new DialogInterface.OnClickListener() {

View File

@@ -1,5 +1,10 @@
package com.mapbox.mapboxsdk.maps;
import android.support.annotation.NonNull;
import java.util.ArrayList;
import java.util.List;
import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraIdleListener;
import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveCanceledListener;
import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveListener;
@@ -10,23 +15,32 @@ class CameraChangeDispatcher implements MapboxMap.OnCameraMoveStartedListener, M
private boolean idle = true;
private final List<OnCameraMoveStartedListener> onCameraMoveStartedListenerList = new ArrayList<>();
private final List<OnCameraMoveCanceledListener> onCameraMoveCanceledListenerList = new ArrayList<>();
private final List<OnCameraMoveListener> onCameraMoveListenerList = new ArrayList<>();
private final List<OnCameraIdleListener> onCameraIdleListenerList = new ArrayList<>();
private OnCameraMoveStartedListener onCameraMoveStartedListener;
private OnCameraMoveCanceledListener onCameraMoveCanceledListener;
private OnCameraMoveListener onCameraMoveListener;
private OnCameraIdleListener onCameraIdleListener;
@Deprecated
void setOnCameraMoveStartedListener(OnCameraMoveStartedListener onCameraMoveStartedListener) {
this.onCameraMoveStartedListener = onCameraMoveStartedListener;
}
@Deprecated
void setOnCameraMoveCanceledListener(OnCameraMoveCanceledListener onCameraMoveCanceledListener) {
this.onCameraMoveCanceledListener = onCameraMoveCanceledListener;
}
@Deprecated
void setOnCameraMoveListener(OnCameraMoveListener onCameraMoveListener) {
this.onCameraMoveListener = onCameraMoveListener;
}
@Deprecated
void setOnCameraIdleListener(OnCameraIdleListener onCameraIdleListener) {
this.onCameraIdleListener = onCameraIdleListener;
}
@@ -36,34 +50,106 @@ class CameraChangeDispatcher implements MapboxMap.OnCameraMoveStartedListener, M
if (!idle) {
return;
}
idle = false;
// deprecated API
if (onCameraMoveStartedListener != null) {
onCameraMoveStartedListener.onCameraMoveStarted(reason);
}
// new API
if (!onCameraMoveStartedListenerList.isEmpty()) {
for (OnCameraMoveStartedListener cameraMoveStartedListener : onCameraMoveStartedListenerList) {
cameraMoveStartedListener.onCameraMoveStarted(reason);
}
}
}
@Override
public void onCameraMove() {
// deprecated API
if (onCameraMoveListener != null && !idle) {
onCameraMoveListener.onCameraMove();
}
// new API
if (!onCameraMoveListenerList.isEmpty() && !idle) {
for (OnCameraMoveListener cameraMoveListener : onCameraMoveListenerList) {
cameraMoveListener.onCameraMove();
}
}
}
@Override
public void onCameraMoveCanceled() {
// deprecated API
if (onCameraMoveCanceledListener != null && !idle) {
onCameraMoveCanceledListener.onCameraMoveCanceled();
}
// new API
if (!onCameraMoveCanceledListenerList.isEmpty() && !idle) {
for (OnCameraMoveCanceledListener cameraMoveCanceledListener : onCameraMoveCanceledListenerList) {
cameraMoveCanceledListener.onCameraMoveCanceled();
}
}
}
@Override
public void onCameraIdle() {
if (!idle) {
idle = true;
// deprecated API
if (onCameraIdleListener != null) {
onCameraIdleListener.onCameraIdle();
}
// new API
if (!onCameraIdleListenerList.isEmpty()) {
for (OnCameraIdleListener cameraIdleListener : onCameraIdleListenerList) {
cameraIdleListener.onCameraIdle();
}
}
}
}
void addOnCameraIdleListener(@NonNull OnCameraIdleListener listener) {
onCameraIdleListenerList.add(listener);
}
void removeOnCameraIdleListener(@NonNull OnCameraIdleListener listener) {
if (onCameraIdleListenerList.contains(listener)) {
onCameraIdleListenerList.remove(listener);
}
}
void addOnCameraMoveCancelListener(OnCameraMoveCanceledListener listener) {
onCameraMoveCanceledListenerList.add(listener);
}
void removeOnCameraMoveCancelListener(OnCameraMoveCanceledListener listener) {
if (onCameraMoveCanceledListenerList.contains(listener)) {
onCameraMoveCanceledListenerList.remove(listener);
}
}
void addOnCameraMoveStartedListener(OnCameraMoveStartedListener listener) {
onCameraMoveStartedListenerList.add(listener);
}
void removeOnCameraMoveStartedListener(OnCameraMoveStartedListener listener) {
if (onCameraMoveStartedListenerList.contains(listener)) {
onCameraMoveStartedListenerList.remove(listener);
}
}
void addOnCameraMoveListener(OnCameraMoveListener listener) {
onCameraMoveListenerList.add(listener);
}
void removeOnCameraMoveListener(OnCameraMoveListener listener) {
if (onCameraMoveListenerList.contains(listener)) {
onCameraMoveListenerList.remove(listener);
}
}
}

View File

@@ -143,11 +143,14 @@ class IconManager {
}
void iconCleanup(Icon icon) {
int refCounter = iconMap.get(icon) - 1;
if (refCounter == 0) {
remove(icon);
} else {
updateIconRefCounter(icon, refCounter);
Integer refCounter = iconMap.get(icon);
if (refCounter != null) {
refCounter--;
if (refCounter == 0) {
remove(icon);
} else {
updateIconRefCounter(icon, refCounter);
}
}
}

View File

@@ -0,0 +1,17 @@
package com.mapbox.mapboxsdk.maps;
class Image {
private final byte[] buffer;
private final float pixelRatio;
private final String name;
private final int width;
private final int height;
public Image(byte[] buffer, float pixelRatio, String name, int width, int height) {
this.buffer = buffer;
this.pixelRatio = pixelRatio;
this.name = name;
this.width = width;
this.height = height;
}
}

View File

@@ -1,14 +1,19 @@
package com.mapbox.mapboxsdk.maps;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.PointF;
import android.location.Location;
import android.support.annotation.Nullable;
import android.support.v4.view.GestureDetectorCompat;
import android.support.v4.view.ScaleGestureDetectorCompat;
import android.support.v4.view.animation.FastOutSlowInInterpolator;
import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.VelocityTracker;
import android.view.ViewConfiguration;
import com.almeros.android.multitouch.gesturedetectors.RotateGestureDetector;
@@ -57,8 +62,14 @@ final class MapGestureDetector {
private boolean scaleGestureOccurred;
private boolean recentScaleGestureOccurred;
private boolean scaleAnimating;
private long scaleBeginTime;
private VelocityTracker velocityTracker;
private boolean wasZoomingIn;
private boolean wasClockwiseRotating;
private boolean rotateGestureOccurred;
MapGestureDetector(Context context, Transform transform, Projection projection, UiSettings uiSettings,
TrackingSettings trackingSettings, AnnotationManager annotationManager,
CameraChangeDispatcher cameraChangeDispatcher) {
@@ -153,6 +164,12 @@ final class MapGestureDetector {
// Handle two finger tap
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
if (velocityTracker == null) {
velocityTracker = VelocityTracker.obtain();
} else {
velocityTracker.clear();
}
velocityTracker.addMovement(event);
// First pointer down, reset scaleGestureOccurred, used to avoid triggering a fling after a scale gesture #7666
recentScaleGestureOccurred = false;
transform.setGestureInProgress(true);
@@ -203,11 +220,23 @@ final class MapGestureDetector {
twoTap = false;
transform.setGestureInProgress(false);
if (velocityTracker != null) {
velocityTracker.recycle();
}
velocityTracker = null;
break;
case MotionEvent.ACTION_CANCEL:
twoTap = false;
transform.setGestureInProgress(false);
if (velocityTracker != null) {
velocityTracker.recycle();
}
velocityTracker = null;
break;
case MotionEvent.ACTION_MOVE:
velocityTracker.addMovement(event);
velocityTracker.computeCurrentVelocity(1000);
break;
}
@@ -430,7 +459,11 @@ final class MapGestureDetector {
*/
private class ScaleGestureListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
private static final int ANIMATION_TIME_MULTIPLIER = 77;
private static final double ZOOM_DISTANCE_DIVIDER = 5;
private float scaleFactor = 1.0f;
private PointF scalePointBegin;
// Called when two fingers first touch the screen
@Override
@@ -440,6 +473,7 @@ final class MapGestureDetector {
}
recentScaleGestureOccurred = true;
scalePointBegin = new PointF(detector.getFocusX(), detector.getFocusY());
scaleBeginTime = detector.getEventTime();
MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent(
getLocationFromGesture(detector.getFocusX(), detector.getFocusY()),
@@ -447,15 +481,6 @@ final class MapGestureDetector {
return true;
}
// Called when fingers leave screen
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
scaleGestureOccurred = false;
scaleBeginTime = 0;
scaleFactor = 1.0f;
cameraChangeDispatcher.onCameraIdle();
}
// Called each time a finger moves
// Called for pinch zooms and quickzooms/quickscales
@Override
@@ -464,6 +489,7 @@ final class MapGestureDetector {
return super.onScale(detector);
}
wasZoomingIn = (Math.log(detector.getScaleFactor()) / Math.log(Math.PI / 2)) >= 0;
if (tiltGestureOccurred) {
return false;
}
@@ -478,7 +504,7 @@ final class MapGestureDetector {
// If scale is large enough ignore a tap
scaleFactor *= detector.getScaleFactor();
if ((scaleFactor > 1.05f) || (scaleFactor < 0.95f)) {
if ((scaleFactor > 1.1f) || (scaleFactor < 0.9f)) {
// notify camera change listener
cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
scaleGestureOccurred = true;
@@ -497,7 +523,6 @@ final class MapGestureDetector {
// make an assumption here; if the zoom center is specified by the gesture, it's NOT going
// to be in the center of the map. Therefore the zoom will translate the map center, so tracking
// should be disabled.
trackingSettings.resetTrackingModesIfRequired(!quickZoom, false, false);
// Scale the map
if (focalPoint != null) {
@@ -506,19 +531,79 @@ final class MapGestureDetector {
} else if (quickZoom) {
cameraChangeDispatcher.onCameraMove();
// clamp scale factors we feed to core #7514
float scaleFactor = MathUtils.clamp(detector.getScaleFactor(),
float scaleFactor = detector.getScaleFactor();
// around center map
double zoomBy = Math.log(scaleFactor) / Math.log(Math.PI / 2);
boolean negative = zoomBy < 0;
zoomBy = MathUtils.clamp(Math.abs(zoomBy),
MapboxConstants.MINIMUM_SCALE_FACTOR_CLAMP,
MapboxConstants.MAXIMUM_SCALE_FACTOR_CLAMP);
// around center map
transform.zoomBy(Math.log(scaleFactor) / Math.log(Math.PI / 2),
uiSettings.getWidth() / 2, uiSettings.getHeight() / 2);
transform.zoomBy(negative ? -zoomBy : zoomBy, uiSettings.getWidth() / 2, uiSettings.getHeight() / 2);
recentScaleGestureOccurred = true;
} else {
// around gesture
transform.zoomBy(Math.log(detector.getScaleFactor()) / Math.log(Math.PI / 2),
detector.getFocusX(), detector.getFocusY());
scalePointBegin.x, scalePointBegin.y);
}
return true;
}
// Called when fingers leave screen
@Override
public void onScaleEnd(final ScaleGestureDetector detector) {
if (rotateGestureOccurred || quickZoom) {
reset();
return;
}
double velocityXY = Math.abs(velocityTracker.getYVelocity()) + Math.abs(velocityTracker.getXVelocity());
if (velocityXY > MapboxConstants.VELOCITY_THRESHOLD_IGNORE_FLING / 2) {
scaleAnimating = true;
double zoomAddition = calculateScale(velocityXY);
double currentZoom = transform.getRawZoom();
long animationTime = (long) (Math.log(velocityXY) * ANIMATION_TIME_MULTIPLIER);
createScaleAnimator(currentZoom, zoomAddition, animationTime).start();
} else if (!scaleAnimating) {
reset();
}
}
private void reset() {
scaleAnimating = false;
scaleGestureOccurred = false;
scaleBeginTime = 0;
scaleFactor = 1.0f;
cameraChangeDispatcher.onCameraIdle();
}
private double calculateScale(double velocityXY) {
double zoomAddition = (float) (Math.log(velocityXY) / ZOOM_DISTANCE_DIVIDER);
if (!wasZoomingIn) {
zoomAddition = -zoomAddition;
}
return zoomAddition;
}
private Animator createScaleAnimator(double currentZoom, double zoomAddition, long animationTime) {
ValueAnimator animator = ValueAnimator.ofFloat((float) currentZoom, (float) (currentZoom + zoomAddition));
animator.setDuration(animationTime);
animator.setInterpolator(new FastOutSlowInInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
transform.setZoom((Float) animation.getAnimatedValue(), scalePointBegin);
}
});
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
reset();
}
});
return animator;
}
}
/**
@@ -526,11 +611,13 @@ final class MapGestureDetector {
*/
private class RotateGestureListener extends RotateGestureDetector.SimpleOnRotateGestureListener {
private static final long ROTATE_INVOKE_WAIT_TIME = 1500;
private static final float ROTATE_INVOKE_ANGLE = 15.30f;
private static final float ROTATE_LIMITATION_ANGLE = 3.35f;
private static final float ROTATE_LIMITATION_DURATION = ROTATE_LIMITATION_ANGLE * 1.85f;
private long beginTime = 0;
private float totalAngle = 0.0f;
private boolean started = false;
private boolean animating = false;
// Called when two fingers first touch the screen
@Override
@@ -546,15 +633,6 @@ final class MapGestureDetector {
return true;
}
// Called when the fingers leave the screen
@Override
public void onRotateEnd(RotateGestureDetector detector) {
// notify camera change listener
beginTime = 0;
totalAngle = 0.0f;
started = false;
}
// Called each time one of the two fingers moves
// Called for rotation
@Override
@@ -563,18 +641,10 @@ final class MapGestureDetector {
return false;
}
// Ignore short touches in case it is a tap
// Also ignore small rotate
long time = detector.getEventTime();
long interval = time - beginTime;
if (!started && (interval <= ViewConfiguration.getTapTimeout() || isScaleGestureActive(time))) {
return false;
}
// If rotate is large enough ignore a tap
// Also is zoom already started, don't rotate
totalAngle += detector.getRotationDegreesDelta();
if (totalAngle > 35.0f || totalAngle < -35.0f) {
float angle = detector.getRotationDegreesDelta();
if (Math.abs(angle) >= ROTATE_INVOKE_ANGLE) {
MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent(
getLocationFromGesture(detector.getFocusX(), detector.getFocusY()),
MapboxEvent.GESTURE_ROTATION_START, transform));
@@ -585,13 +655,17 @@ final class MapGestureDetector {
return false;
}
wasClockwiseRotating = detector.getRotationDegreesDelta() > 0;
if (scaleBeginTime != 0) {
rotateGestureOccurred = true;
}
// rotation constitutes translation of anything except the center of
// rotation, so cancel both location and bearing tracking if required
trackingSettings.resetTrackingModesIfRequired(true, true, false);
// Get rotate value
double bearing = transform.getRawBearing();
bearing += detector.getRotationDegreesDelta();
// Calculate map bearing value
double bearing = transform.getRawBearing() + angle;
// Rotate the map
if (focalPoint != null) {
@@ -604,11 +678,81 @@ final class MapGestureDetector {
return true;
}
private boolean isScaleGestureActive(long time) {
long scaleExecutionTime = time - scaleBeginTime;
boolean scaleGestureStarted = scaleBeginTime != 0;
boolean scaleOffsetTimeValid = scaleExecutionTime > ROTATE_INVOKE_WAIT_TIME;
return (scaleGestureStarted && scaleOffsetTimeValid) || scaleGestureOccurred;
// Called when the fingers leave the screen
@Override
public void onRotateEnd(RotateGestureDetector detector) {
long interval = detector.getEventTime() - beginTime;
if ((!started && (interval <= ViewConfiguration.getTapTimeout())) || scaleAnimating || interval > 500) {
reset();
return;
}
double angularVelocity = calculateVelocityVector(detector);
if (Math.abs(angularVelocity) > 0.001 && rotateGestureOccurred && !animating) {
animateRotateVelocity();
} else if (!animating) {
reset();
}
}
private void reset() {
beginTime = 0;
started = false;
animating = false;
rotateGestureOccurred = false;
}
private void animateRotateVelocity() {
animating = true;
double currentRotation = transform.getRawBearing();
double rotateAdditionDegrees = calculateVelocityInDegrees();
createAnimator(currentRotation, rotateAdditionDegrees).start();
}
private double calculateVelocityVector(RotateGestureDetector detector) {
return ((detector.getFocusX() * velocityTracker.getYVelocity())
+ (detector.getFocusY() * velocityTracker.getXVelocity()))
/ (Math.pow(detector.getFocusX(), 2) + Math.pow(detector.getFocusY(), 2));
}
private double calculateVelocityInDegrees() {
double angleRadians = Math.atan2(velocityTracker.getXVelocity(), velocityTracker.getYVelocity());
double angle = angleRadians / (Math.PI / 180);
if (angle <= 0) {
angle += 360;
}
// limit the angle
angle = angle / ROTATE_LIMITATION_ANGLE;
// correct direction
if (!wasClockwiseRotating) {
angle = -angle;
}
return angle;
}
private Animator createAnimator(double currentRotation, double rotateAdditionDegrees) {
ValueAnimator animator = ValueAnimator.ofFloat(
(float) currentRotation,
(float) (currentRotation + rotateAdditionDegrees)
);
animator.setDuration((long) (Math.abs(rotateAdditionDegrees) * ROTATE_LIMITATION_DURATION));
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
transform.setBearing((Float) animation.getAnimatedValue());
}
});
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
reset();
}
});
return animator;
}
}

View File

@@ -28,13 +28,14 @@ import com.mapbox.mapboxsdk.annotations.Annotation;
import com.mapbox.mapboxsdk.annotations.MarkerViewManager;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.constants.Style;
import com.mapbox.mapboxsdk.maps.renderer.glsurfaceview.GLSurfaceViewMapRenderer;
import com.mapbox.mapboxsdk.maps.renderer.MapRenderer;
import com.mapbox.mapboxsdk.maps.renderer.glsurfaceview.GLSurfaceViewMapRenderer;
import com.mapbox.mapboxsdk.maps.renderer.textureview.TextureViewMapRenderer;
import com.mapbox.mapboxsdk.maps.widgets.CompassView;
import com.mapbox.mapboxsdk.maps.widgets.MyLocationView;
import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings;
import com.mapbox.mapboxsdk.net.ConnectivityReceiver;
import com.mapbox.mapboxsdk.storage.FileSource;
import com.mapbox.services.android.telemetry.MapboxTelemetry;
import java.lang.annotation.Retention;
@@ -175,8 +176,9 @@ public class MapView extends FrameLayout {
Markers markers = new MarkerContainer(nativeMapView, this, annotationsArray, iconManager, markerViewManager);
Polygons polygons = new PolygonContainer(nativeMapView, annotationsArray);
Polylines polylines = new PolylineContainer(nativeMapView, annotationsArray);
ShapeAnnotations shapeAnnotations = new ShapeAnnotationContainer(nativeMapView, annotationsArray);
AnnotationManager annotationManager = new AnnotationManager(nativeMapView, this, annotationsArray,
markerViewManager, iconManager, annotations, markers, polygons, polylines);
markerViewManager, iconManager, annotations, markers, polygons, polylines, shapeAnnotations);
Transform transform = new Transform(nativeMapView, annotationManager.getMarkerViewManager(), trackingSettings,
cameraChangeDispatcher);
@@ -191,8 +193,10 @@ public class MapView extends FrameLayout {
annotationManager, cameraChangeDispatcher);
mapKeyListener = new MapKeyListener(transform, trackingSettings, uiSettings);
// overlain zoom buttons
mapZoomButtonController = new MapZoomButtonController(new ZoomButtonsController(this));
MapZoomControllerListener zoomListener = new MapZoomControllerListener(mapGestureDetector, uiSettings, transform);
MapZoomControllerListener zoomListener = new MapZoomControllerListener(mapGestureDetector, uiSettings, transform,
cameraChangeDispatcher, getWidth(), getHeight());
mapZoomButtonController.bind(uiSettings, zoomListener);
compassView.injectCompassAnimationListener(createCompassAnimationListener(cameraChangeDispatcher));
@@ -345,8 +349,10 @@ public class MapView extends FrameLayout {
*/
@UiThread
public void onSaveInstanceState(@NonNull Bundle outState) {
outState.putBoolean(MapboxConstants.STATE_HAS_SAVED_STATE, true);
mapboxMap.onSaveInstanceState(outState);
if (mapboxMap != null) {
outState.putBoolean(MapboxConstants.STATE_HAS_SAVED_STATE, true);
mapboxMap.onSaveInstanceState(outState);
}
}
/**
@@ -355,6 +361,7 @@ public class MapView extends FrameLayout {
@UiThread
public void onStart() {
ConnectivityReceiver.instance(getContext()).activate();
FileSource.getInstance(getContext()).activate();
if (mapboxMap != null) {
mapboxMap.onStart();
}
@@ -385,8 +392,12 @@ public class MapView extends FrameLayout {
*/
@UiThread
public void onStop() {
mapboxMap.onStop();
if (mapboxMap != null) {
// map was destroyed before it was started
mapboxMap.onStop();
}
ConnectivityReceiver.instance(getContext()).deactivate();
FileSource.getInstance(getContext()).deactivate();
}
/**
@@ -406,6 +417,10 @@ public class MapView extends FrameLayout {
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!isMapInitialized() || !isZoomButtonControllerInitialized()) {
return super.onTouchEvent(event);
}
if (event.getAction() == MotionEvent.ACTION_DOWN) {
mapZoomButtonController.setVisible(true);
}
@@ -434,11 +449,18 @@ public class MapView extends FrameLayout {
@Override
public boolean onGenericMotionEvent(MotionEvent event) {
if (mapGestureDetector == null) {
return super.onGenericMotionEvent(event);
}
return mapGestureDetector.onGenericMotionEvent(event) || super.onGenericMotionEvent(event);
}
@Override
public boolean onHoverEvent(MotionEvent event) {
if (!isZoomButtonControllerInitialized()) {
return super.onHoverEvent(event);
}
switch (event.getActionMasked()) {
case MotionEvent.ACTION_HOVER_ENTER:
case MotionEvent.ACTION_HOVER_MOVE:
@@ -495,7 +517,7 @@ public class MapView extends FrameLayout {
if (destroyed) {
return;
}
if (nativeMapView == null) {
if (!isMapInitialized()) {
mapboxMapOptions.styleUrl(url);
return;
}
@@ -512,7 +534,7 @@ public class MapView extends FrameLayout {
return;
}
if (!isInEditMode() && nativeMapView != null) {
if (!isInEditMode() && isMapInitialized()) {
nativeMapView.resizeView(width, height);
}
}
@@ -526,7 +548,9 @@ public class MapView extends FrameLayout {
@CallSuper
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mapZoomButtonController.setVisible(false);
if (isZoomButtonControllerInitialized()) {
mapZoomButtonController.setVisible(false);
}
}
// Called when view is hidden and shown
@@ -536,7 +560,7 @@ public class MapView extends FrameLayout {
return;
}
if (mapZoomButtonController != null) {
if (isZoomButtonControllerInitialized()) {
mapZoomButtonController.setVisible(visibility == View.VISIBLE);
}
}
@@ -598,6 +622,14 @@ public class MapView extends FrameLayout {
}
}
private boolean isMapInitialized() {
return nativeMapView != null;
}
private boolean isZoomButtonControllerInitialized() {
return mapZoomButtonController != null;
}
MapboxMap getMapboxMap() {
return mapboxMap;
}
@@ -883,16 +915,23 @@ public class MapView extends FrameLayout {
}
}
private class MapZoomControllerListener implements ZoomButtonsController.OnZoomListener {
private static class MapZoomControllerListener implements ZoomButtonsController.OnZoomListener {
private final MapGestureDetector mapGestureDetector;
private final UiSettings uiSettings;
private final Transform transform;
private final CameraChangeDispatcher cameraChangeDispatcher;
private final float mapWidth;
private final float mapHeight;
MapZoomControllerListener(MapGestureDetector detector, UiSettings uiSettings, Transform transform) {
MapZoomControllerListener(MapGestureDetector detector, UiSettings uiSettings, Transform transform,
CameraChangeDispatcher dispatcher, float mapWidth, float mapHeight) {
this.mapGestureDetector = detector;
this.uiSettings = uiSettings;
this.transform = transform;
this.cameraChangeDispatcher = dispatcher;
this.mapWidth = mapWidth;
this.mapHeight = mapHeight;
}
// Not used
@@ -905,6 +944,7 @@ public class MapView extends FrameLayout {
@Override
public void onZoom(boolean zoomIn) {
if (uiSettings.isZoomGesturesEnabled()) {
cameraChangeDispatcher.onCameraMoveStarted(CameraChangeDispatcher.REASON_API_ANIMATION);
onZoom(zoomIn, mapGestureDetector.getFocalPoint());
}
}
@@ -913,7 +953,7 @@ public class MapView extends FrameLayout {
if (focalPoint != null) {
transform.zoom(zoomIn, focalPoint);
} else {
PointF centerPoint = new PointF(getMeasuredWidth() / 2, getMeasuredHeight() / 2);
PointF centerPoint = new PointF(mapWidth / 2, mapHeight / 2);
transform.zoom(zoomIn, centerPoint);
}
}

View File

@@ -47,6 +47,7 @@ import com.mapbox.services.commons.geojson.Feature;
import com.mapbox.services.commons.geojson.Geometry;
import java.lang.reflect.ParameterizedType;
import java.util.HashMap;
import java.util.List;
import timber.log.Timber;
@@ -462,6 +463,13 @@ public final class MapboxMap {
nativeMapView.addImage(name, image);
}
/**
* Adds an images to be used in the map's style
*/
public void addImages(@NonNull HashMap<String, Bitmap> images) {
nativeMapView.addImages(images);
}
/**
* Removes an image from the map's style
*
@@ -709,6 +717,10 @@ public final class MapboxMap {
// MapChange.REGION_DID_CHANGE_ANIMATED is not called for `jumpTo`
// invalidate camera position to provide OnCameraChange event.
invalidateCameraPosition();
if (callback != null) {
callback.onFinish();
}
}
});
}
@@ -1753,38 +1765,122 @@ public final class MapboxMap {
* Sets a callback that is invoked when camera movement has ended.
*
* @param listener the listener to notify
* @deprecated use {@link #addOnCameraIdleListener(OnCameraIdleListener)}
* and {@link #removeOnCameraIdleListener(OnCameraIdleListener)} instead
*/
@Deprecated
public void setOnCameraIdleListener(@Nullable OnCameraIdleListener listener) {
cameraChangeDispatcher.setOnCameraIdleListener(listener);
}
/**
* Adds a callback that is invoked when camera movement has ended.
*
* @param listener the listener to notify
*/
public void addOnCameraIdleListener(@Nullable OnCameraIdleListener listener) {
cameraChangeDispatcher.addOnCameraIdleListener(listener);
}
/**
* Removes a callback that is invoked when camera movement has ended.
*
* @param listener the listener to remove
*/
public void removeOnCameraIdleListener(@Nullable OnCameraIdleListener listener) {
cameraChangeDispatcher.removeOnCameraIdleListener(listener);
}
/**
* Sets a callback that is invoked when camera movement was cancelled.
*
* @param listener the listener to notify
* @deprecated use {@link #addOnCameraMoveCancelListener(OnCameraMoveCanceledListener)} and
* {@link #removeOnCameraMoveCancelListener(OnCameraMoveCanceledListener)} instead
*/
@Deprecated
public void setOnCameraMoveCancelListener(@Nullable OnCameraMoveCanceledListener listener) {
cameraChangeDispatcher.setOnCameraMoveCanceledListener(listener);
}
/**
* Adds a callback that is invoked when camera movement was cancelled.
*
* @param listener the listener to notify
*/
public void addOnCameraMoveCancelListener(@Nullable OnCameraMoveCanceledListener listener) {
cameraChangeDispatcher.addOnCameraMoveCancelListener(listener);
}
/**
* Removes a callback that is invoked when camera movement was cancelled.
*
* @param listener the listener to remove
*/
public void removeOnCameraMoveCancelListener(@Nullable OnCameraMoveCanceledListener listener) {
cameraChangeDispatcher.removeOnCameraMoveCancelListener(listener);
}
/**
* Sets a callback that is invoked when camera movement has started.
*
* @param listener the listener to notify
* @deprecated use {@link #addOnCameraMoveStartedListener(OnCameraMoveStartedListener)} and
* {@link #removeOnCameraMoveStartedListener(OnCameraMoveStartedListener)} instead
*/
@Deprecated
public void setOnCameraMoveStartedListener(@Nullable OnCameraMoveStartedListener listener) {
cameraChangeDispatcher.setOnCameraMoveStartedListener(listener);
}
/**
* Adds a callback that is invoked when camera movement has started.
*
* @param listener the listener to notify
*/
public void addOnCameraMoveStartedListener(@Nullable OnCameraMoveStartedListener listener) {
cameraChangeDispatcher.addOnCameraMoveStartedListener(listener);
}
/**
* Removes a callback that is invoked when camera movement has started.
*
* @param listener the listener to remove
*/
public void removeOnCameraMoveStartedListener(@Nullable OnCameraMoveStartedListener listener) {
cameraChangeDispatcher.removeOnCameraMoveStartedListener(listener);
}
/**
* Sets a callback that is invoked when camera position changes.
*
* @param listener the listener to notify
* @deprecated use {@link #addOnCameraMoveListener(OnCameraMoveListener)} and
* {@link #removeOnCameraMoveListener(OnCameraMoveListener)}instead
*/
@Deprecated
public void setOnCameraMoveListener(@Nullable OnCameraMoveListener listener) {
cameraChangeDispatcher.setOnCameraMoveListener(listener);
}
/**
* Adds a callback that is invoked when camera position changes.
*
* @param listener the listener to notify
*/
public void addOnCameraMoveListener(@Nullable OnCameraMoveListener listener) {
cameraChangeDispatcher.addOnCameraMoveListener(listener);
}
/**
* Removes a callback that is invoked when camera position changes.
*
* @param listener the listener to remove
*/
public void removeOnCameraMoveListener(@Nullable OnCameraMoveListener listener) {
cameraChangeDispatcher.removeOnCameraMoveListener(listener);
}
/**
* Sets a callback that's invoked on every frame rendered to the map view.
*

View File

@@ -98,15 +98,8 @@ class MarkerContainer implements Markers {
@NonNull
@Override
public List<Marker> obtainAllIn(@NonNull RectF rectangle) {
// convert Rectangle to be density depedent
float pixelRatio = nativeMapView.getPixelRatio();
RectF rect = new RectF(rectangle.left / pixelRatio,
rectangle.top / pixelRatio,
rectangle.right / pixelRatio,
rectangle.bottom / pixelRatio);
RectF rect = nativeMapView.getDensityDependantRectangle(rectangle);
long[] ids = nativeMapView.queryPointAnnotations(rect);
List<Long> idsList = new ArrayList<>(ids.length);
for (long id : ids) {
idsList.add(id);

View File

@@ -4,6 +4,7 @@ import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.PointF;
import android.graphics.RectF;
import android.os.AsyncTask;
import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@@ -34,7 +35,9 @@ import com.mapbox.services.commons.geojson.Geometry;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import timber.log.Timber;
@@ -468,6 +471,13 @@ final class NativeMapView {
return nativeQueryPointAnnotations(rect);
}
public long[] queryShapeAnnotations(RectF rectF) {
if (isDestroyedOn("queryShapeAnnotations")) {
return new long[] {};
}
return nativeQueryShapeAnnotations(rectF);
}
public void addAnnotationIcon(String symbol, int width, int height, float scale, byte[] pixels) {
if (isDestroyedOn("addAnnotationIcon")) {
return;
@@ -746,6 +756,7 @@ final class NativeMapView {
if (isDestroyedOn("addImage")) {
return;
}
// Check/correct config
if (image.getConfig() != Bitmap.Config.ARGB_8888) {
image = image.copy(Bitmap.Config.ARGB_8888, false);
@@ -762,6 +773,14 @@ final class NativeMapView {
nativeAddImage(name, image.getWidth(), image.getHeight(), pixelRatio, buffer.array());
}
public void addImages(@NonNull HashMap<String, Bitmap> bitmapHashMap) {
if (isDestroyedOn("addImages")) {
return;
}
//noinspection unchecked
new BitmapImageConversionTask(this).execute(bitmapHashMap);
}
public void removeImage(String name) {
if (isDestroyedOn("removeImage")) {
return;
@@ -825,6 +844,15 @@ final class NativeMapView {
return pixelRatio;
}
RectF getDensityDependantRectangle(final RectF rectangle) {
return new RectF(
rectangle.left / pixelRatio,
rectangle.top / pixelRatio,
rectangle.right / pixelRatio,
rectangle.bottom / pixelRatio
);
}
//
// Callbacks
//
@@ -927,6 +955,8 @@ final class NativeMapView {
private native long[] nativeQueryPointAnnotations(RectF rect);
private native long[] nativeQueryShapeAnnotations(RectF rect);
private native void nativeAddAnnotationIcon(String symbol, int width, int height, float scale, byte[] pixels);
private native void nativeRemoveAnnotationIcon(String symbol);
@@ -1006,6 +1036,8 @@ final class NativeMapView {
private native void nativeAddImage(String name, int width, int height, float pixelRatio,
byte[] array);
private native void nativeAddImages(Image[] images);
private native void nativeRemoveImage(String name);
private native Bitmap nativeGetImage(String name);
@@ -1093,4 +1125,55 @@ final class NativeMapView {
});
}
//
// Image conversion
//
private static class BitmapImageConversionTask extends AsyncTask<HashMap<String, Bitmap>, Void, List<Image>> {
private NativeMapView nativeMapView;
BitmapImageConversionTask(NativeMapView nativeMapView) {
this.nativeMapView = nativeMapView;
}
@Override
protected List<Image> doInBackground(HashMap<String, Bitmap>... params) {
HashMap<String, Bitmap> bitmapHashMap = params[0];
List<Image> images = new ArrayList<>();
ByteBuffer buffer;
String name;
Bitmap bitmap;
for (Map.Entry<String, Bitmap> stringBitmapEntry : bitmapHashMap.entrySet()) {
name = stringBitmapEntry.getKey();
bitmap = stringBitmapEntry.getValue();
if (bitmap.getConfig() != Bitmap.Config.ARGB_8888) {
bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, false);
}
buffer = ByteBuffer.allocate(bitmap.getByteCount());
bitmap.copyPixelsToBuffer(buffer);
float density = bitmap.getDensity() == Bitmap.DENSITY_NONE ? Bitmap.DENSITY_NONE : bitmap.getDensity();
float pixelRatio = density / DisplayMetrics.DENSITY_DEFAULT;
images.add(new Image(buffer.array(), pixelRatio, name, bitmap.getWidth(), bitmap.getHeight()));
}
return images;
}
@Override
protected void onPostExecute(List<Image> images) {
super.onPostExecute(images);
if (nativeMapView != null && !nativeMapView.isDestroyedOn("nativeAddImages")) {
nativeMapView.nativeAddImages(images.toArray(new Image[images.size()]));
}
}
}
}

View File

@@ -0,0 +1,38 @@
package com.mapbox.mapboxsdk.maps;
import android.graphics.RectF;
import android.support.v4.util.LongSparseArray;
import com.mapbox.mapboxsdk.annotations.Annotation;
import java.util.ArrayList;
import java.util.List;
class ShapeAnnotationContainer implements ShapeAnnotations {
private final NativeMapView nativeMapView;
private final LongSparseArray<Annotation> annotations;
ShapeAnnotationContainer(NativeMapView nativeMapView, LongSparseArray<Annotation> annotations) {
this.nativeMapView = nativeMapView;
this.annotations = annotations;
}
@Override
public List<Annotation> obtainAllIn(RectF rectangle) {
RectF rect = nativeMapView.getDensityDependantRectangle(rectangle);
long[] annotationIds = nativeMapView.queryShapeAnnotations(rect);
return getAnnotationsFromIds(annotationIds);
}
private List<Annotation> getAnnotationsFromIds(long[] annotationIds) {
List<Annotation> shapeAnnotations = new ArrayList<>();
for (long annotationId : annotationIds) {
Annotation annotation = annotations.get(annotationId);
if (annotation != null) {
shapeAnnotations.add(annotation);
}
}
return shapeAnnotations;
}
}

View File

@@ -0,0 +1,13 @@
package com.mapbox.mapboxsdk.maps;
import android.graphics.RectF;
import com.mapbox.mapboxsdk.annotations.Annotation;
import java.util.List;
interface ShapeAnnotations {
List<Annotation> obtainAllIn(RectF rectF);
}

View File

@@ -98,9 +98,6 @@ final class Transform implements MapView.OnMapChangedListener {
cancelTransitions();
cameraChangeDispatcher.onCameraMoveStarted(OnCameraMoveStartedListener.REASON_API_ANIMATION);
mapView.jumpTo(cameraPosition.bearing, cameraPosition.target, cameraPosition.tilt, cameraPosition.zoom);
if (callback != null) {
callback.onFinish();
}
cameraChangeDispatcher.onCameraIdle();
}
}
@@ -210,6 +207,10 @@ final class Transform implements MapView.OnMapChangedListener {
return cameraPosition.zoom;
}
double getRawZoom() {
return mapView.getZoom();
}
void zoom(boolean zoomIn, @NonNull PointF focalPoint) {
CameraPosition cameraPosition = invalidateCameraPosition();
if (cameraPosition != null) {
@@ -221,6 +222,17 @@ final class Transform implements MapView.OnMapChangedListener {
}
}
void zoom(double zoomAddition, @NonNull PointF focalPoint, long duration) {
CameraPosition cameraPosition = invalidateCameraPosition();
if (cameraPosition != null) {
int newZoom = (int) Math.round(cameraPosition.zoom + zoomAddition);
setZoom(newZoom, focalPoint, duration);
} else {
// we are not transforming, notify about being idle
cameraChangeDispatcher.onCameraIdle();
}
}
void setZoom(double zoom, @NonNull PointF focalPoint) {
setZoom(zoom, focalPoint, 0);
}

View File

@@ -6,10 +6,10 @@ import android.support.annotation.NonNull;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.ViewPropertyAnimatorCompat;
import android.support.v4.view.ViewPropertyAnimatorListenerAdapter;
import android.support.v7.widget.AppCompatImageView;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
@@ -22,7 +22,7 @@ import com.mapbox.mapboxsdk.maps.MapboxMap;
* use {@link com.mapbox.mapboxsdk.maps.UiSettings}.
* </p>
*/
public final class CompassView extends AppCompatImageView implements Runnable {
public final class CompassView extends ImageView implements Runnable {
public static final long TIME_WAIT_IDLE = 500;
public static final long TIME_MAP_NORTH_ANIMATION = 150;

View File

@@ -300,10 +300,19 @@ public class OfflineRegion {
/**
* Pause or resume downloading of regional resources.
* <p>
* After a download has been completed, you are required to reset the state of the region to STATE_INACTIVE.
* </p>
*
* @param state the download state
*/
public void setDownloadState(@DownloadState int state) {
if (state == STATE_ACTIVE) {
fileSource.activate();
} else {
fileSource.deactivate();
}
this.state = state;
setOfflineRegionDownloadState(state);
}

View File

@@ -4,6 +4,7 @@ import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
@@ -262,35 +263,59 @@ public class MapSnapshotter {
nativeCancel();
}
protected void addOverlay(Bitmap original) {
/**
* Draw an overlay on the map snapshot.
*
* @param mapSnapshot the map snapshot to draw the overlay on
*/
protected void addOverlay(MapSnapshot mapSnapshot) {
Bitmap original = mapSnapshot.getBitmap();
Canvas canvas = new Canvas(original);
addLogo(canvas, original);
}
/**
* Draw a logo on the canvas created from the map snapshot.
*
* @param canvas the canvas to draw the bitmap on
* @param original the map snapshot image
*/
private void addLogo(Canvas canvas, Bitmap original) {
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
float margin = displayMetrics.density * LOGO_MARGIN_DP;
Canvas canvas = new Canvas(original);
Bitmap logo = createScaledLogo(original);
canvas.drawBitmap(logo, margin, original.getHeight() - logo.getHeight() - margin, null);
}
// Decode just the boundaries
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
bitmapOptions.inJustDecodeBounds = true;
BitmapFactory.decodeResource(context.getResources(), R.drawable.mapbox_logo_icon, bitmapOptions);
int srcWidth = bitmapOptions.outWidth;
int srcHeight = bitmapOptions.outHeight;
/**
* Create a scaled logo for a map snapshot.
*
* @param snapshot the map snapshot where the logo should be placed on
* @return the scaled bitmap logo
*/
private Bitmap createScaledLogo(Bitmap snapshot) {
Bitmap logo = BitmapFactory.decodeResource(context.getResources(), R.drawable.mapbox_logo_icon, null);
float scale = calculateLogoScale(snapshot, logo);
Matrix matrix = new Matrix();
matrix.postScale(scale, scale);
return Bitmap.createBitmap(logo, 0, 0, logo.getWidth(), logo.getHeight(), matrix, true);
}
// Ratio, preferred dimensions and resulting sample size
float widthRatio = displayMetrics.widthPixels / original.getWidth();
float heightRatio = displayMetrics.heightPixels / original.getHeight();
float prefWidth = srcWidth / widthRatio;
float prefHeight = srcHeight / heightRatio;
int sampleSize = MapSnaphotUtil.calculateInSampleSize(bitmapOptions, (int) prefWidth, (int) prefHeight);
// Scale bitmap
bitmapOptions.inJustDecodeBounds = false;
bitmapOptions.inScaled = true;
bitmapOptions.inSampleSize = sampleSize;
bitmapOptions.inDensity = srcWidth;
bitmapOptions.inTargetDensity = (int) prefWidth * bitmapOptions.inSampleSize;
Bitmap logo = BitmapFactory.decodeResource(context.getResources(), R.drawable.mapbox_logo_icon, bitmapOptions);
float scaledHeight = bitmapOptions.outHeight * heightRatio / bitmapOptions.inSampleSize;
canvas.drawBitmap(logo, margin, original.getHeight() - scaledHeight - margin, null);
/**
* Calculates the scale of the logo, only allow downscaling.
*
* @param snapshot the bitmap of the map snapshot
* @param logo the bitmap of the mapbox logo
* @return the scale value
*/
private float calculateLogoScale(Bitmap snapshot, Bitmap logo) {
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
float widthRatio = displayMetrics.widthPixels / snapshot.getWidth();
float heightRatio = displayMetrics.heightPixels / snapshot.getHeight();
float prefWidth = logo.getWidth() / widthRatio;
float prefHeight = logo.getHeight() / heightRatio;
float calculatedScale = Math.min(prefWidth / logo.getWidth(), prefHeight / logo.getHeight()) * 2;
return calculatedScale < 1 ? calculatedScale : 1.0f;
}
/**
@@ -302,7 +327,7 @@ public class MapSnapshotter {
protected void onSnapshotReady(MapSnapshot snapshot) {
if (callback != null) {
if (snapshot.isShowLogo()) {
addOverlay(snapshot.getBitmap());
addOverlay(snapshot);
}
callback.onSnapshotReady(snapshot);
reset();

View File

@@ -72,7 +72,7 @@ public class FileSource {
MapboxConstants.KEY_META_DATA_SET_STORAGE_EXTERNAL,
MapboxConstants.DEFAULT_SET_STORAGE_EXTERNAL);
} catch (PackageManager.NameNotFoundException exception) {
Timber.e(exception,"Failed to read the package metadata: ");
Timber.e(exception, "Failed to read the package metadata: ");
} catch (Exception exception) {
Timber.e(exception, "Failed to read the storage key: ");
}
@@ -119,17 +119,39 @@ public class FileSource {
}
private long nativePtr;
private long activeCounter;
private boolean wasPaused;
private FileSource(String cachePath, AssetManager assetManager) {
initialize(Mapbox.getAccessToken(), cachePath, assetManager);
}
public void activate() {
activeCounter++;
if (activeCounter == 1 && wasPaused) {
wasPaused = false;
resume();
}
}
public void deactivate() {
activeCounter--;
if (activeCounter == 0) {
wasPaused = true;
pause();
}
}
public native void setAccessToken(@NonNull String accessToken);
public native String getAccessToken();
public native void setApiBaseUrl(String baseUrl);
private native void resume();
private native void pause();
/**
* Sets a callback for transforming URLs requested from the internet
* <p>

View File

@@ -5,6 +5,7 @@ import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
import android.support.v4.graphics.drawable.DrawableCompat;
@@ -30,10 +31,15 @@ public class ColorUtils {
*/
@ColorInt
public static int getPrimaryColor(@NonNull Context context) {
TypedValue typedValue = new TypedValue();
Resources.Theme theme = context.getTheme();
theme.resolveAttribute(R.attr.colorPrimary, typedValue, true);
return typedValue.data;
try {
TypedValue typedValue = new TypedValue();
Resources.Theme theme = context.getTheme();
int id = context.getResources().getIdentifier("colorPrimary", "attrs", context.getPackageName());
theme.resolveAttribute(id, typedValue, true);
return typedValue.data;
} catch (Exception exception) {
return getColorCompat(context, R.color.mapbox_blue);
}
}
/**
@@ -44,10 +50,15 @@ public class ColorUtils {
*/
@ColorInt
public static int getPrimaryDarkColor(@NonNull Context context) {
TypedValue typedValue = new TypedValue();
Resources.Theme theme = context.getTheme();
theme.resolveAttribute(R.attr.colorPrimaryDark, typedValue, true);
return typedValue.data;
try {
TypedValue typedValue = new TypedValue();
Resources.Theme theme = context.getTheme();
int id = context.getResources().getIdentifier("colorPrimaryDark", "attrs", context.getPackageName());
theme.resolveAttribute(id, typedValue, true);
return typedValue.data;
} catch (Exception exception) {
return getColorCompat(context, R.color.mapbox_blue);
}
}
/**
@@ -58,10 +69,15 @@ public class ColorUtils {
*/
@ColorInt
public static int getAccentColor(@NonNull Context context) {
TypedValue typedValue = new TypedValue();
Resources.Theme theme = context.getTheme();
theme.resolveAttribute(R.attr.colorAccent, typedValue, true);
return typedValue.data;
try {
TypedValue typedValue = new TypedValue();
Resources.Theme theme = context.getTheme();
int id = context.getResources().getIdentifier("colorAccent", "attrs", context.getPackageName());
theme.resolveAttribute(id, typedValue, true);
return typedValue.data;
} catch (Exception exception) {
return getColorCompat(context, R.color.mapbox_gray);
}
}
/**
@@ -122,4 +138,12 @@ public class ColorUtils {
throw new ConversionException("Not a valid rgb/rgba value");
}
}
private static int getColorCompat(Context context, int id) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return context.getResources().getColor(id, context.getTheme());
} else {
return context.getResources().getColor(id);
}
}
}

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="mapbox_compassContentDescription">Компас на картата. Активирай, за да насочиш картата на север.</string>
<string name="mapbox_attributionsIconContentDescription">Иконка функции. Активирай, за да покажеш диалог функции.</string>
<string name="mapbox_myLocationViewContentDescription">Изглед локация. Това показва местоположението ти на картата.</string>
<string name="mapbox_mapActionDescription">Показва карта създадена с Mapbox. Скролни с два пръста. Мащабирай с два пръста.</string>
<string name="mapbox_attributionsDialogTitle">Mapbox Android SDK</string>
<string name="mapbox_attributionTelemetryTitle">Направи Mapbox картите по-добри.</string>
<string name="mapbox_attributionTelemetryMessage">Помагаш OpenStreetMap и Mapbox картите да станат по-добри, като предоставяш анонимни данни за потребление.</string>
<string name="mapbox_attributionTelemetryPositive">Съгласявам се</string>
<string name="mapbox_attributionTelemetryNegative">Не се съгласявам</string>
<string name="mapbox_attributionTelemetryNeutral">Повече инфо</string>
<string name="mapbox_offline_error_region_definition_invalid">Предоставените OfflineRegionDefinition не пасват в границите на света: %s</string>
</resources>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="mapbox_myLocationViewContentDescription">Hely nézet. Megmutatja, hol vagy a térképen.</string>
<string name="mapbox_mapActionDescription">Egy Mapbox-szal készült térkép megjelenítése. Húzd két ujjadat a görgetéshez. Csippentsd össze a nagyításhoz.</string>
<string name="mapbox_attributionsDialogTitle">Mapbox Android SDK</string>
<string name="mapbox_attributionTelemetryTitle">Tedd jobbá a Mapbox térképeket</string>
<string name="mapbox_attributionTelemetryMessage">Segítesz az OpenStreetMap és Mapbox térképek jobbá tételében névtelen felhasználási adatok elküldésével.</string>
<string name="mapbox_attributionTelemetryPositive">Egyetértek</string>
<string name="mapbox_attributionTelemetryNegative">Nem értek egyet</string>
<string name="mapbox_attributionTelemetryNeutral">Több infó</string>
<string name="mapbox_offline_error_region_definition_invalid">A megadott OfflineRegionDefinition nem fér bele a világ kereteibe: %s</string>
</resources>

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="mapbox_AlertDialogStyle" parent="Theme.AppCompat.Light.Dialog.Alert"/>
</resources>

View File

@@ -73,15 +73,6 @@ public class LatLngBoundsTest {
assertEquals("Center should match", new LatLng(1, 1), center);
}
@Test
public void emptySpan() {
latLngBounds = new LatLngBounds.Builder()
.include(LAT_LNG_NOT_NULL_ISLAND)
.include(LAT_LNG_NOT_NULL_ISLAND)
.build();
assertTrue("Should be empty", latLngBounds.isEmptySpan());
}
@Test
public void notEmptySpan() {
latLngBounds = new LatLngBounds.Builder()

View File

@@ -10,6 +10,7 @@ import com.mapbox.mapboxsdk.annotations.MarkerViewManager;
import com.mapbox.mapboxsdk.geometry.LatLng;
import org.junit.Test;
import org.mockito.ArgumentMatchers;
import java.util.ArrayList;
import java.util.List;
@@ -32,8 +33,9 @@ public class AnnotationManagerTest {
Markers markers = new MarkerContainer(aNativeMapView, aMapView, annotationsArray, aIconManager, aMarkerViewManager);
Polygons polygons = new PolygonContainer(aNativeMapView, annotationsArray);
Polylines polylines = new PolylineContainer(aNativeMapView, annotationsArray);
ShapeAnnotations shapeAnnotations = new ShapeAnnotationContainer(aNativeMapView, annotationsArray);
AnnotationManager annotationManager = new AnnotationManager(aNativeMapView, aMapView, annotationsArray,
aMarkerViewManager, aIconManager, annotations, markers, polygons, polylines);
aMarkerViewManager, aIconManager, annotations, markers, polygons, polylines, shapeAnnotations);
Marker aMarker = mock(Marker.class);
long aId = 5L;
when(aNativeMapView.addMarker(aMarker)).thenReturn(aId);
@@ -58,18 +60,23 @@ public class AnnotationManagerTest {
Markers markers = new MarkerContainer(aNativeMapView, aMapView, annotationsArray, aIconManager, aMarkerViewManager);
Polygons polygons = new PolygonContainer(aNativeMapView, annotationsArray);
Polylines polylines = new PolylineContainer(aNativeMapView, annotationsArray);
ShapeAnnotations shapeAnnotations = new ShapeAnnotationContainer(aNativeMapView, annotationsArray);
AnnotationManager annotationManager = new AnnotationManager(aNativeMapView, aMapView, annotationsArray,
aMarkerViewManager, aIconManager, annotations, markers, polygons, polylines);
aMarkerViewManager, aIconManager, annotations, markers, polygons, polylines, shapeAnnotations);
long firstId = 1L;
long secondId = 2L;
List<BaseMarkerOptions> markerList = new ArrayList<>();
MarkerOptions firstMarkerOption = new MarkerOptions().position(new LatLng()).title("first");
MarkerOptions secondMarkerOption = new MarkerOptions().position(new LatLng()).title("second");
markerList.add(firstMarkerOption);
markerList.add(secondMarkerOption);
MapboxMap aMapboxMap = mock(MapboxMap.class);
when(aNativeMapView.addMarker(any(Marker.class))).thenReturn(firstId, secondId);
when(aNativeMapView.addMarkers(ArgumentMatchers.<Marker>anyList()))
.thenReturn(new long[]{firstId, secondId});
annotationManager.addMarkers(markerList, aMapboxMap);
assertEquals(2, annotationManager.getAnnotations().size());

View File

@@ -0,0 +1,87 @@
package com.mapbox.mapboxsdk.maps;
import org.junit.Test;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
public class CameraChangeDispatcherTest {
@Test
public void testSetCameraIdleListener() {
CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
MapboxMap.OnCameraIdleListener listener = mock(MapboxMap.OnCameraIdleListener.class);
dispatcher.setOnCameraIdleListener(listener);
dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
dispatcher.onCameraIdle();
verify(listener).onCameraIdle();
}
@Test
public void testSetCameraMoveStartedListener() {
CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
MapboxMap.OnCameraMoveStartedListener listener = mock(MapboxMap.OnCameraMoveStartedListener.class);
dispatcher.setOnCameraMoveStartedListener(listener);
dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
verify(listener).onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
}
@Test
public void testSetCameraMoveCancelListener() {
CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
MapboxMap.OnCameraMoveCanceledListener listener = mock(MapboxMap.OnCameraMoveCanceledListener.class);
dispatcher.setOnCameraMoveCanceledListener(listener);
dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
dispatcher.onCameraMoveCanceled();
verify(listener).onCameraMoveCanceled();
}
@Test
public void testSetCameraMoveListener() {
CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
MapboxMap.OnCameraMoveListener listener = mock(MapboxMap.OnCameraMoveListener.class);
dispatcher.setOnCameraMoveListener(listener);
dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
dispatcher.onCameraMove();
verify(listener).onCameraMove();
}
@Test
public void testAddCameraIdleListener() {
CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
MapboxMap.OnCameraIdleListener listener = mock(MapboxMap.OnCameraIdleListener.class);
dispatcher.addOnCameraIdleListener(listener);
dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
dispatcher.onCameraIdle();
verify(listener).onCameraIdle();
}
@Test
public void testAddCameraMoveStartedListener() {
CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
MapboxMap.OnCameraMoveStartedListener listener = mock(MapboxMap.OnCameraMoveStartedListener.class);
dispatcher.addOnCameraMoveStartedListener(listener);
dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
verify(listener).onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
}
@Test
public void testAddCameraMoveCancelListener() {
CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
MapboxMap.OnCameraMoveCanceledListener listener = mock(MapboxMap.OnCameraMoveCanceledListener.class);
dispatcher.addOnCameraMoveCancelListener(listener);
dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
dispatcher.onCameraMoveCanceled();
verify(listener).onCameraMoveCanceled();
}
@Test
public void testAddCameraMoveListener() {
CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
MapboxMap.OnCameraMoveListener listener = mock(MapboxMap.OnCameraMoveListener.class);
dispatcher.addOnCameraMoveListener(listener);
dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
dispatcher.onCameraMove();
verify(listener).onCameraMove();
}
}

View File

@@ -59,6 +59,7 @@ dependencies {
// Support libraries
compile rootProject.ext.dep.supportAppcompatV7
compile rootProject.ext.dep.supportRecyclerView
compile rootProject.ext.dep.supportDesign
// Leak Canary
debugCompile rootProject.ext.dep.leakCanaryDebug
@@ -71,7 +72,6 @@ dependencies {
compile rootProject.ext.dep.lost
// Testing dependencies
androidTestCompile rootProject.ext.dep.testSpoonRunner
androidTestCompile rootProject.ext.dep.supportAnnotations
androidTestCompile rootProject.ext.dep.testRunner
androidTestCompile rootProject.ext.dep.testRules
@@ -81,8 +81,6 @@ dependencies {
apply from: 'gradle-make.gradle'
apply from: 'gradle-config.gradle'
apply from: 'gradle-device-farm.gradle'
apply from: 'gradle-spoon.gradle'
apply from: 'gradle-checkstyle.gradle'
apply from: '../gradle-lint.gradle'

View File

@@ -1,43 +0,0 @@
apply plugin: 'devicefarm'
def getAccessKeyDeviceFarm() {
if (project.hasProperty('AWS_ACCESS_KEY_ID_DEVICE_FARM')) {
return AWS_ACCESS_KEY_ID_DEVICE_FARM
} else {
println("Could not locate AWS_ACCESS_KEY_ID_DEVICE_FARM in gradle.properties")
return ""
}
}
def getSecretAccessKeyDeviceFarm() {
if (project.hasProperty('AWS_SECRET_ACCESS_KEY_DEVICE_FARM')) {
return AWS_SECRET_ACCESS_KEY_DEVICE_FARM
} else {
println("Could not locate AWS_SECRET_ACCESS_KEY_DEVICE_FARM in gradle.properties")
return ""
}
}
devicefarm {
projectName "Mapbox GL Android" // required: Must already exists.
devicePool "sanity" // optional: Defaults to "Top Devices"
authentication {
accessKey getAccessKeyDeviceFarm()
secretKey getSecretAccessKeyDeviceFarm()
}
devicestate {
wifi "on"
bluetooth "off"
gps "on"
nfc "on"
latitude 47.6204 // default
longitude - 122.3491 // default
}
instrumentation {
}
}

View File

@@ -1,8 +0,0 @@
println "configuring spoon"
apply plugin: 'spoon'
spoon {
// Spoon: distributes instrumentation tests to all your Androids
// for more options see https://github.com/stanfy/spoon-gradle-plugin
grantAllPermissions = true
debug = true
}

View File

@@ -14,6 +14,7 @@ import com.mapbox.mapboxsdk.annotations.Polygon;
import com.mapbox.mapboxsdk.annotations.PolygonOptions;
import com.mapbox.mapboxsdk.annotations.Polyline;
import com.mapbox.mapboxsdk.annotations.PolylineOptions;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.exceptions.InvalidMarkerPositionException;
import com.mapbox.mapboxsdk.geometry.LatLng;
@@ -31,9 +32,12 @@ import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import timber.log.Timber;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static com.mapbox.mapboxsdk.testapp.utils.TestConstants.LAT_LNG_DELTA;
import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertNotNull;
import static org.junit.Assert.assertEquals;
@@ -90,8 +94,33 @@ public class MapboxMapTest extends BaseActivityTest {
}
//
// CameraForLatLngBounds
// Camera tests
//
@Test
public void testCameraPositionOnFinish() {
ViewUtils.checkViewIsDisplayed(R.id.mapView);
onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
@Override
public void onViewAction(UiController uiController, View view) {
final LatLng latLng = new LatLng(30.0, 30.0);
mapboxMap.moveCamera(CameraUpdateFactory.newLatLng(latLng), new MapboxMap.CancelableCallback() {
@Override
public void onCancel() {
}
@Override
public void onFinish() {
LatLng cameraPositionLatLng = mapboxMap.getCameraPosition().target;
Timber.d(cameraPositionLatLng.toString());
assertEquals(cameraPositionLatLng.getLatitude(), latLng.getLatitude(), LAT_LNG_DELTA);
assertEquals(cameraPositionLatLng.getLongitude(), latLng.getLongitude(), LAT_LNG_DELTA);
}
});
}
}));
}
@Test
public void testCameraForLatLngBounds() {
ViewUtils.checkViewIsDisplayed(R.id.mapView);

View File

@@ -49,8 +49,9 @@ public abstract class BaseActivityTest {
throw new RuntimeException("Could not start test for " + getActivityClass().getSimpleName() + ".\n"
+ "The ViewHierarchy doesn't contain a view with resource id = R.id.mapView or \n"
+ "the Activity doesn't contain an instance variable with a name equal to mapboxMap.\n"
+ "You can resolve this issue be implementing the requirements above or\n add "
+ getActivityClass().getSimpleName() + " to the excludeActivities array in `generate-test-code.js`.\n");
+ "You can resolve this issue by adding the requirements above or\n add "
+ getActivityClass().getSimpleName() + " to the platform/android/scripts/exclude-activity-gen.json to blacklist"
+ " the Activity from being generated.\n");
}
}
@@ -67,8 +68,7 @@ public abstract class BaseActivityTest {
protected abstract Class getActivityClass();
protected void checkViewIsDisplayed(int id) {
onView(withId(id))
.check(matches(isDisplayed()));
onView(withId(id)).check(matches(isDisplayed()));
}
protected void waitLoop() {

View File

@@ -21,6 +21,7 @@ import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity;
import org.hamcrest.Matcher;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import static android.support.test.espresso.Espresso.onData;
@@ -67,6 +68,7 @@ public class AttributionTest extends BaseActivityTest {
}
@Test
@Ignore
public void testMapboxStreetsMapboxAttributionLink() {
validateTestSetup();
if (urlSpans == null) {
@@ -87,6 +89,7 @@ public class AttributionTest extends BaseActivityTest {
}
@Test
@Ignore
public void testMapboxStreetsOpenStreetMapAttributionLink() {
validateTestSetup();
if (urlSpans == null) {
@@ -107,6 +110,7 @@ public class AttributionTest extends BaseActivityTest {
}
@Test
@Ignore
public void testImproveMapLink() {
validateTestSetup();
if (urlSpans == null) {

View File

@@ -13,10 +13,8 @@ public class OnMapReadyIdlingResource implements IdlingResource, OnMapReadyCallb
private MapboxMap mapboxMap;
private IdlingResource.ResourceCallback resourceCallback;
private Activity activity;
public OnMapReadyIdlingResource(Activity activity) {
this.activity = activity;
try {
Field field = activity.getClass().getDeclaredField("mapView");
field.setAccessible(true);
@@ -24,7 +22,6 @@ public class OnMapReadyIdlingResource implements IdlingResource, OnMapReadyCallb
} catch (Exception err) {
throw new RuntimeException(err);
}
}
@Override
@@ -53,4 +50,4 @@ public class OnMapReadyIdlingResource implements IdlingResource, OnMapReadyCallb
resourceCallback.onTransitionToIdle();
}
}
}
}

View File

@@ -5,13 +5,20 @@ import android.animation.AnimatorSet;
import android.animation.TypeEvaluator;
import android.animation.ValueAnimator;
import android.os.Bundle;
import android.support.v4.util.LongSparseArray;
import android.support.v4.view.animation.FastOutLinearInInterpolator;
import android.support.v4.view.animation.FastOutSlowInInterpolator;
import android.support.v4.view.animation.PathInterpolatorCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.animation.AnticipateOvershootInterpolator;
import android.view.animation.BounceInterpolator;
import android.view.animation.Interpolator;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
@@ -24,6 +31,51 @@ import com.mapbox.mapboxsdk.testapp.R;
public class CameraAnimatorActivity extends AppCompatActivity implements OnMapReadyCallback {
private static final double ANIMATION_DELAY_FACTOR = 1.5;
private static final LatLng START_LAT_LNG = new LatLng(37.787947, -122.407432);
private final LongSparseArray<AnimatorBuilder> animators = new LongSparseArray<AnimatorBuilder>() {
{
put(R.id.menu_action_accelerate_decelerate_interpolator, new AnimatorBuilder() {
@Override
public Animator build() {
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(
createLatLngAnimator(START_LAT_LNG, new LatLng(37.826715, -122.422795)),
obtainExampleInterpolator(new FastOutSlowInInterpolator(), 2500)
);
return animatorSet;
}
});
put(R.id.menu_action_bounce_interpolator, new AnimatorBuilder() {
@Override
public Animator build() {
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(
createLatLngAnimator(START_LAT_LNG, new LatLng(37.787947, -122.407432)),
obtainExampleInterpolator(new BounceInterpolator(), 3750)
);
return animatorSet;
}
});
put(R.id.menu_action_anticipate_overshoot_interpolator, new AnimatorBuilder() {
@Override
public Animator build() {
return obtainExampleInterpolator(new AnticipateOvershootInterpolator(), 2500);
}
});
put(R.id.menu_action_path_interpolator, new AnimatorBuilder() {
@Override
public Animator build() {
return obtainExampleInterpolator(
PathInterpolatorCompat.create(.22f, .68f, 0, 1.71f), 2500);
}
});
}
};
private MapView mapView;
private MapboxMap mapboxMap;
@@ -32,7 +84,6 @@ public class CameraAnimatorActivity extends AppCompatActivity implements OnMapRe
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera_animator);
mapView = (MapView) findViewById(R.id.mapView);
if (mapView != null) {
mapView.onCreate(savedInstanceState);
@@ -43,27 +94,42 @@ public class CameraAnimatorActivity extends AppCompatActivity implements OnMapRe
@Override
public void onMapReady(final MapboxMap map) {
mapboxMap = map;
initFab();
}
private void initFab() {
findViewById(R.id.fab).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
view.setVisibility(View.GONE);
createAnimator(mapboxMap.getCameraPosition()).start();
CameraPosition animatedPosition = new CameraPosition.Builder()
.target(new LatLng(37.789992, -122.402214))
.tilt(60)
.zoom(14.5f)
.bearing(135)
.build();
createExampleAnimator(mapboxMap.getCameraPosition(), animatedPosition).start();
}
});
}
private Animator createAnimator(CameraPosition currentPosition) {
//
// Animator API used for the animation on the FAB
//
private Animator createExampleAnimator(CameraPosition currentPosition, CameraPosition targetPosition) {
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(createLatLngAnimator(currentPosition.target));
animatorSet.play(createZoomAnimator(currentPosition.zoom));
animatorSet.play(createBearingAnimator(currentPosition.bearing));
animatorSet.play(createTiltAnimator(currentPosition.tilt));
animatorSet.play(createLatLngAnimator(currentPosition.target, targetPosition.target));
animatorSet.play(createZoomAnimator(currentPosition.zoom, targetPosition.zoom));
animatorSet.play(createBearingAnimator(currentPosition.bearing, targetPosition.bearing));
animatorSet.play(createTiltAnimator(currentPosition.tilt, targetPosition.tilt));
return animatorSet;
}
private Animator createLatLngAnimator(LatLng currentPosition) {
LatLng target = new LatLng(37.789992, -122.402214);
ValueAnimator latLngAnimator = ValueAnimator.ofObject(new LatLngEvaluator(), currentPosition, target);
private Animator createLatLngAnimator(LatLng currentPosition, LatLng targetPosition) {
ValueAnimator latLngAnimator = ValueAnimator.ofObject(new LatLngEvaluator(), currentPosition, targetPosition);
latLngAnimator.setDuration((long) (1000 * ANIMATION_DELAY_FACTOR));
latLngAnimator.setInterpolator(new FastOutSlowInInterpolator());
latLngAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@@ -75,8 +141,8 @@ public class CameraAnimatorActivity extends AppCompatActivity implements OnMapRe
return latLngAnimator;
}
private Animator createZoomAnimator(double currentZoom) {
ValueAnimator zoomAnimator = ValueAnimator.ofFloat((float) currentZoom, 14.5f);
private Animator createZoomAnimator(double currentZoom, double targetZoom) {
ValueAnimator zoomAnimator = ValueAnimator.ofFloat((float) currentZoom, (float) targetZoom);
zoomAnimator.setDuration((long) (2200 * ANIMATION_DELAY_FACTOR));
zoomAnimator.setStartDelay((long) (600 * ANIMATION_DELAY_FACTOR));
zoomAnimator.setInterpolator(new AnticipateOvershootInterpolator());
@@ -89,8 +155,8 @@ public class CameraAnimatorActivity extends AppCompatActivity implements OnMapRe
return zoomAnimator;
}
private Animator createBearingAnimator(double currentBearing) {
ValueAnimator bearingAnimator = ValueAnimator.ofFloat((float) currentBearing, 135);
private Animator createBearingAnimator(double currentBearing, double targetBearing) {
ValueAnimator bearingAnimator = ValueAnimator.ofFloat((float) currentBearing, (float) targetBearing);
bearingAnimator.setDuration((long) (1000 * ANIMATION_DELAY_FACTOR));
bearingAnimator.setStartDelay((long) (1000 * ANIMATION_DELAY_FACTOR));
bearingAnimator.setInterpolator(new FastOutLinearInInterpolator());
@@ -103,8 +169,8 @@ public class CameraAnimatorActivity extends AppCompatActivity implements OnMapRe
return bearingAnimator;
}
private Animator createTiltAnimator(double currentTilt) {
ValueAnimator tiltAnimator = ValueAnimator.ofFloat((float) currentTilt, 60);
private Animator createTiltAnimator(double currentTilt, double targetTilt) {
ValueAnimator tiltAnimator = ValueAnimator.ofFloat((float) currentTilt, (float) targetTilt);
tiltAnimator.setDuration((long) (1000 * ANIMATION_DELAY_FACTOR));
tiltAnimator.setStartDelay((long) (1500 * ANIMATION_DELAY_FACTOR));
tiltAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@@ -116,20 +182,66 @@ public class CameraAnimatorActivity extends AppCompatActivity implements OnMapRe
return tiltAnimator;
}
private static class LatLngEvaluator implements TypeEvaluator<LatLng> {
//
// Interpolator examples
//
private final LatLng latLng = new LatLng();
private Animator obtainExampleInterpolator(int menuItemId) {
return animators.get(menuItemId).build();
}
@Override
public LatLng evaluate(float fraction, LatLng startValue, LatLng endValue) {
latLng.setLatitude(startValue.getLatitude()
+ ((endValue.getLatitude() - startValue.getLatitude()) * fraction));
latLng.setLongitude(startValue.getLongitude()
+ ((endValue.getLongitude() - startValue.getLongitude()) * fraction));
return latLng;
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_animator, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (mapboxMap == null) {
return false;
}
findViewById(R.id.fab).setVisibility(View.GONE);
resetCameraPosition();
playAnimation(item.getItemId());
return super.onOptionsItemSelected(item);
}
private void resetCameraPosition() {
mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(
new CameraPosition.Builder()
.target(START_LAT_LNG)
.zoom(11)
.bearing(0)
.tilt(0)
.build()
));
}
private void playAnimation(int itemId) {
Animator animator = obtainExampleInterpolator(itemId);
if (animator != null) {
animator.start();
}
}
private Animator obtainExampleInterpolator(Interpolator interpolator, long duration) {
ValueAnimator zoomAnimator = ValueAnimator.ofFloat(11.0f, 16.0f);
zoomAnimator.setDuration((long) (duration * ANIMATION_DELAY_FACTOR));
zoomAnimator.setInterpolator(interpolator);
zoomAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mapboxMap.setZoom((Float) animation.getAnimatedValue());
}
});
return zoomAnimator;
}
//
// MapView lifecycle
//
@Override
protected void onStart() {
super.onStart();
@@ -171,4 +283,25 @@ public class CameraAnimatorActivity extends AppCompatActivity implements OnMapRe
super.onLowMemory();
mapView.onLowMemory();
}
/**
* Helper class to evaluate LatLng objects with a ValueAnimator
*/
private static class LatLngEvaluator implements TypeEvaluator<LatLng> {
private final LatLng latLng = new LatLng();
@Override
public LatLng evaluate(float fraction, LatLng startValue, LatLng endValue) {
latLng.setLatitude(startValue.getLatitude()
+ ((endValue.getLatitude() - startValue.getLatitude()) * fraction));
latLng.setLongitude(startValue.getLongitude()
+ ((endValue.getLongitude() - startValue.getLongitude()) * fraction));
return latLng;
}
}
interface AnimatorBuilder {
Animator build();
}
}

View File

@@ -1,6 +1,5 @@
package com.mapbox.mapboxsdk.testapp.activity.camera;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
@@ -28,11 +27,13 @@ import timber.log.Timber;
/**
* Test activity showcasing how to listen to camera change events.
*/
public class CameraPositionActivity extends AppCompatActivity implements OnMapReadyCallback {
public class CameraPositionActivity extends AppCompatActivity implements OnMapReadyCallback, View.OnClickListener,
MapboxMap.OnMapLongClickListener {
private MapView mapView;
private MapboxMap mapboxMap;
private FloatingActionButton fab;
private boolean logCameraChanges;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -47,96 +48,48 @@ public class CameraPositionActivity extends AppCompatActivity implements OnMapRe
@Override
public void onMapReady(@NonNull final MapboxMap map) {
mapboxMap = map;
mapboxMap.setOnCameraIdleListener(new MapboxMap.OnCameraIdleListener() {
@Override
public void onCameraIdle() {
Timber.e("OnCameraIdle");
fab.setColorFilter(ContextCompat.getColor(CameraPositionActivity.this, android.R.color.holo_green_dark));
}
});
mapboxMap.setOnCameraMoveCancelListener(new MapboxMap.OnCameraMoveCanceledListener() {
@Override
public void onCameraMoveCanceled() {
Timber.e("OnCameraMoveCanceled");
}
});
mapboxMap.setOnCameraMoveListener(new MapboxMap.OnCameraMoveListener() {
@Override
public void onCameraMove() {
Timber.e("OnCameraMove");
fab.setColorFilter(ContextCompat.getColor(CameraPositionActivity.this, android.R.color.holo_orange_dark));
}
});
mapboxMap.setOnCameraMoveStartedListener(new MapboxMap.OnCameraMoveStartedListener() {
private final String[] REASONS = {"REASON_API_GESTURE", "REASON_DEVELOPER_ANIMATION", "REASON_API_ANIMATION"};
@Override
public void onCameraMoveStarted(int reason) {
// reason ranges from 1 <-> 3
fab.setColorFilter(ContextCompat.getColor(CameraPositionActivity.this, android.R.color.holo_red_dark));
Timber.e("OnCameraMoveStarted: %s", REASONS[reason - 1]);
}
});
toggleLogCameraChanges();
// add a listener to FAB
fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setColorFilter(ContextCompat.getColor(CameraPositionActivity.this, R.color.primary));
fab.setOnClickListener(new View.OnClickListener() {
@SuppressLint("InflateParams")
@Override
public void onClick(View view) {
Context context = view.getContext();
final View dialogContent = LayoutInflater.from(context).inflate(R.layout.dialog_camera_position, null);
AlertDialog.Builder builder = new AlertDialog.Builder(
context, com.mapbox.mapboxsdk.R.style.mapbox_AlertDialogStyle);
builder.setTitle(R.string.dialog_camera_position);
builder.setView(onInflateDialogContent(dialogContent));
builder.setPositiveButton("Animate", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
double latitude = Double.parseDouble(
((TextView) dialogContent.findViewById(R.id.value_lat)).getText().toString());
double longitude = Double.parseDouble(
((TextView) dialogContent.findViewById(R.id.value_lon)).getText().toString());
double zoom = Double.parseDouble(
((TextView) dialogContent.findViewById(R.id.value_zoom)).getText().toString());
double bearing = Double.parseDouble(
((TextView) dialogContent.findViewById(R.id.value_bearing)).getText().toString());
double tilt = Double.parseDouble(
((TextView) dialogContent.findViewById(R.id.value_tilt)).getText().toString());
fab.setOnClickListener(this);
CameraPosition cameraPosition = new CameraPosition.Builder()
.target(new LatLng(latitude, longitude))
.zoom(zoom)
.bearing(bearing)
.tilt(tilt)
.build();
// listen to long click events to toggle logging camera changes
mapboxMap.setOnMapLongClickListener(this);
}
mapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), 5000,
new MapboxMap.CancelableCallback() {
@Override
public void onCancel() {
Timber.v("OnCancel called");
}
@Override
public void onMapLongClick(@NonNull LatLng point) {
toggleLogCameraChanges();
}
@Override
public void onFinish() {
Timber.v("OnFinish called");
}
});
Timber.v(cameraPosition.toString());
}
});
builder.setNegativeButton("Cancel", null);
builder.setCancelable(false);
builder.show();
}
});
@Override
public void onClick(View view) {
Context context = view.getContext();
final View dialogContent = LayoutInflater.from(context).inflate(R.layout.dialog_camera_position, null);
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.dialog_camera_position);
builder.setView(onInflateDialogContent(dialogContent));
builder.setPositiveButton("Animate", new DialogClickListener(mapboxMap, dialogContent));
builder.setNegativeButton("Cancel", null);
builder.setCancelable(false);
builder.show();
}
private void toggleLogCameraChanges() {
logCameraChanges = !logCameraChanges;
if (logCameraChanges) {
mapboxMap.addOnCameraIdleListener(idleListener);
mapboxMap.addOnCameraMoveCancelListener(moveCanceledListener);
mapboxMap.addOnCameraMoveListener(moveListener);
mapboxMap.addOnCameraMoveStartedListener(moveStartedListener);
} else {
mapboxMap.removeOnCameraIdleListener(idleListener);
mapboxMap.removeOnCameraMoveCancelListener(moveCanceledListener);
mapboxMap.removeOnCameraMoveListener(moveListener);
mapboxMap.removeOnCameraMoveStartedListener(moveStartedListener);
}
}
@Override
@@ -193,6 +146,42 @@ public class CameraPositionActivity extends AppCompatActivity implements OnMapRe
seekBar.setProgress(defaultValue);
}
private MapboxMap.OnCameraIdleListener idleListener = new MapboxMap.OnCameraIdleListener() {
@Override
public void onCameraIdle() {
Timber.e("OnCameraIdle");
fab.setColorFilter(ContextCompat.getColor(CameraPositionActivity.this, android.R.color.holo_green_dark));
}
};
private MapboxMap.OnCameraMoveListener moveListener = new MapboxMap.OnCameraMoveListener() {
@Override
public void onCameraMove() {
Timber.e("OnCameraMove");
fab.setColorFilter(ContextCompat.getColor(CameraPositionActivity.this, android.R.color.holo_orange_dark));
}
};
private MapboxMap.OnCameraMoveCanceledListener moveCanceledListener = new MapboxMap.OnCameraMoveCanceledListener() {
@Override
public void onCameraMoveCanceled() {
Timber.e("OnCameraMoveCanceled");
}
};
private MapboxMap.OnCameraMoveStartedListener moveStartedListener = new MapboxMap.OnCameraMoveStartedListener() {
private final String[] REASONS = {"REASON_API_GESTURE", "REASON_DEVELOPER_ANIMATION", "REASON_API_ANIMATION"};
@Override
public void onCameraMoveStarted(int reason) {
// reason ranges from 1 <-> 3
fab.setColorFilter(ContextCompat.getColor(CameraPositionActivity.this, android.R.color.holo_red_dark));
Timber.e("OnCameraMoveStarted: %s", REASONS[reason - 1]);
}
};
private class ValueChangeListener implements SeekBar.OnSeekBarChangeListener {
protected TextView textView;
@@ -224,4 +213,50 @@ public class CameraPositionActivity extends AppCompatActivity implements OnMapRe
super.onProgressChanged(seekBar, progress - 180, fromUser);
}
}
private static class DialogClickListener implements DialogInterface.OnClickListener {
private MapboxMap mapboxMap;
private View dialogContent;
public DialogClickListener(MapboxMap mapboxMap, View view) {
this.mapboxMap = mapboxMap;
this.dialogContent = view;
}
@Override
public void onClick(DialogInterface dialog, int which) {
double latitude = Double.parseDouble(
((TextView) dialogContent.findViewById(R.id.value_lat)).getText().toString());
double longitude = Double.parseDouble(
((TextView) dialogContent.findViewById(R.id.value_lon)).getText().toString());
double zoom = Double.parseDouble(
((TextView) dialogContent.findViewById(R.id.value_zoom)).getText().toString());
double bearing = Double.parseDouble(
((TextView) dialogContent.findViewById(R.id.value_bearing)).getText().toString());
double tilt = Double.parseDouble(
((TextView) dialogContent.findViewById(R.id.value_tilt)).getText().toString());
CameraPosition cameraPosition = new CameraPosition.Builder()
.target(new LatLng(latitude, longitude))
.zoom(zoom)
.bearing(bearing)
.tilt(tilt)
.build();
mapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), 5000,
new MapboxMap.CancelableCallback() {
@Override
public void onCancel() {
Timber.v("OnCancel called");
}
@Override
public void onFinish() {
Timber.v("OnFinish called");
}
});
Timber.v(cameraPosition.toString());
}
}
}

View File

@@ -264,6 +264,7 @@ public class OfflineActivity extends AppCompatActivity
if (status.isComplete()) {
// Download complete
endProgress("Region downloaded successfully.");
offlineRegion.setDownloadState(OfflineRegion.STATE_INACTIVE);
offlineRegion.setObserver(null);
return;
} else if (status.isRequiredResourceCountPrecise()) {
@@ -281,11 +282,13 @@ public class OfflineActivity extends AppCompatActivity
@Override
public void onError(OfflineRegionError error) {
Timber.e("onError: %s, %s", error.getReason(), error.getMessage());
offlineRegion.setDownloadState(OfflineRegion.STATE_INACTIVE);
}
@Override
public void mapboxTileCountLimitExceeded(long limit) {
Timber.e("Mapbox tile count limit exceeded: %s", limit);
offlineRegion.setDownloadState(OfflineRegion.STATE_INACTIVE);
}
});

View File

@@ -1,13 +1,14 @@
package com.mapbox.mapboxsdk.testapp.activity.style;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.support.v7.app.AppCompatActivity;
import android.graphics.Color;
import android.graphics.PointF;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
@@ -32,7 +33,7 @@ import com.mapbox.services.commons.geojson.custom.PositionDeserializer;
import com.mapbox.services.commons.models.Position;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import timber.log.Timber;
@@ -64,49 +65,10 @@ public class SymbolGeneratorActivity extends AppCompatActivity implements OnMapR
}
@Override
public void onMapReady(MapboxMap map) {
public void onMapReady(final MapboxMap map) {
mapboxMap = map;
try {
// read local geojson from raw folder
String tinyCountriesJson = ResourceUtils.readRawResource(this, R.raw.tiny_countries);
// convert geojson to a model
FeatureCollection featureCollection = new GsonBuilder()
.registerTypeAdapter(Geometry.class, new GeometryDeserializer())
.registerTypeAdapter(Position.class, new PositionDeserializer())
.create().fromJson(tinyCountriesJson, FeatureCollection.class);
// add a geojson to the map
Source source = new GeoJsonSource(SOURCE_ID, featureCollection);
mapboxMap.addSource(source);
// for each feature add a symbolLayer
for (Feature feature : featureCollection.getFeatures()) {
String countryName = feature.getStringProperty(FEATURE_ID);
// create View
TextView textView = new TextView(this);
textView.setBackgroundColor(getResources().getColor(R.color.blueAccent));
textView.setPadding(10, 5, 10, 5);
textView.setTextColor(Color.WHITE);
textView.setText(countryName);
// create bitmap from view
mapboxMap.addImage(countryName, SymbolGenerator.generate(textView));
}
// create layer use
mapboxMap.addLayer(new SymbolLayer(LAYER_ID, SOURCE_ID)
.withProperties(
iconImage("{" + FEATURE_ID + "}"), // { } is a token notation
iconAllowOverlap(false)
)
);
addSymbolClickListener();
} catch (IOException exception) {
Timber.e(exception);
}
addSymbolClickListener();
new LoadDataTask(map, SymbolGeneratorActivity.this).execute();
}
private void addSymbolClickListener() {
@@ -213,4 +175,91 @@ public class SymbolGeneratorActivity extends AppCompatActivity implements OnMapR
return bitmap;
}
}
}
private static class LoadDataTask extends AsyncTask<Void, Void, FeatureCollection> {
private final MapboxMap mapboxMap;
private final Context context;
LoadDataTask(MapboxMap mapboxMap, Context context) {
this.mapboxMap = mapboxMap;
this.context = context;
}
@Override
protected FeatureCollection doInBackground(Void... params) {
try {
// read local geojson from raw folder
String tinyCountriesJson = ResourceUtils.readRawResource(context, R.raw.tiny_countries);
// convert geojson to a model
FeatureCollection featureCollection = new GsonBuilder()
.registerTypeAdapter(Geometry.class, new GeometryDeserializer())
.registerTypeAdapter(Position.class, new PositionDeserializer())
.create().fromJson(tinyCountriesJson, FeatureCollection.class);
return featureCollection;
} catch (IOException exception) {
return null;
}
}
@Override
protected void onPostExecute(FeatureCollection featureCollection) {
super.onPostExecute(featureCollection);
if (featureCollection == null) {
return;
}
// add a geojson to the map
Source source = new GeoJsonSource(SOURCE_ID, featureCollection);
mapboxMap.addSource(source);
// create layer use
mapboxMap.addLayer(new SymbolLayer(LAYER_ID, SOURCE_ID)
.withProperties(
iconImage("{" + FEATURE_ID + "}"), // { } is a token notation
iconAllowOverlap(false)
)
);
new GenerateSymbolTask(mapboxMap, context).execute(featureCollection);
}
}
private static class GenerateSymbolTask extends AsyncTask<FeatureCollection, Void, HashMap<String, Bitmap>> {
private MapboxMap mapboxMap;
private Context context;
GenerateSymbolTask(MapboxMap mapboxMap, Context context) {
this.mapboxMap = mapboxMap;
this.context = context;
}
@SuppressWarnings("WrongThread")
@Override
protected HashMap<String, Bitmap> doInBackground(FeatureCollection... params) {
FeatureCollection featureCollection = params[0];
HashMap<String, Bitmap> imagesMap = new HashMap<>();
for (Feature feature : featureCollection.getFeatures()) {
String countryName = feature.getStringProperty(FEATURE_ID);
TextView textView = new TextView(context);
textView.setBackgroundColor(context.getResources().getColor(R.color.blueAccent));
textView.setPadding(10, 5, 10, 5);
textView.setTextColor(Color.WHITE);
textView.setText(countryName);
imagesMap.put(countryName, SymbolGenerator.generate(textView));
}
return imagesMap;
}
@Override
protected void onPostExecute(HashMap<String, Bitmap> bitmapHashMap) {
super.onPostExecute(bitmapHashMap);
mapboxMap.addImages(bitmapHashMap);
}
}
}

View File

@@ -12,6 +12,7 @@ import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.style.layers.Property;
import com.mapbox.mapboxsdk.style.layers.SymbolLayer;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
import com.mapbox.mapboxsdk.testapp.R;
@@ -32,6 +33,7 @@ import static com.mapbox.mapboxsdk.style.functions.stops.Stops.interval;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconAllowOverlap;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconImage;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconSize;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.visibility;
/**
* Test activity showcasing changing the icon with a zoom function and adding selection state to a SymbolLayer.
@@ -49,9 +51,11 @@ public class ZoomFunctionSymbolLayerActivity extends AppCompatActivity {
private MapView mapView;
private MapboxMap mapboxMap;
private GeoJsonSource source;
private SymbolLayer layer;
private boolean isInitialPosition = true;
private boolean isSelected = false;
private boolean isShowingSymbolLayer = true;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -81,6 +85,13 @@ public class ZoomFunctionSymbolLayerActivity extends AppCompatActivity {
}
}
private void toggleSymbolLayerVisibility() {
layer.setProperties(
visibility(isShowingSymbolLayer ? Property.NONE : Property.VISIBLE)
);
isShowingSymbolLayer = !isShowingSymbolLayer;
}
private FeatureCollection createFeatureCollection() {
Position position = isInitialPosition
? Position.fromCoordinates(-74.01618140, 40.701745)
@@ -95,7 +106,7 @@ public class ZoomFunctionSymbolLayerActivity extends AppCompatActivity {
}
private void addLayer() {
SymbolLayer layer = new SymbolLayer(LAYER_ID, SOURCE_ID);
layer = new SymbolLayer(LAYER_ID, SOURCE_ID);
layer.setProperties(
iconImage(
zoom(
@@ -145,9 +156,13 @@ public class ZoomFunctionSymbolLayerActivity extends AppCompatActivity {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (mapboxMap != null && item.getItemId() == R.id.menu_action_change_location) {
isInitialPosition = !isInitialPosition;
updateSource();
if (mapboxMap != null) {
if (item.getItemId() == R.id.menu_action_change_location) {
isInitialPosition = !isInitialPosition;
updateSource();
} else if (item.getItemId() == R.id.menu_action_toggle_source) {
toggleSymbolLayerVisibility();
}
}
return super.onOptionsItemSelected(item);
}

View File

@@ -10,8 +10,8 @@
android:id="@id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:mapbox_cameraTargetLat="37.774913"
app:mapbox_cameraTargetLng="-122.419368"
app:mapbox_cameraTargetLat="37.787947"
app:mapbox_cameraTargetLng="-122.407432"
app:mapbox_cameraZoom="11"
app:mapbox_styleUrl="@string/mapbox_style_mapbox_streets"/>

View File

@@ -0,0 +1,20 @@
<?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">
<item
android:id="@+id/menu_action_accelerate_decelerate_interpolator"
android:title="@string/menuitem_title_accelerate_decelerate"
app:showAsAction="never"/>
<item
android:id="@+id/menu_action_bounce_interpolator"
android:title="@string/menuitem_title_bounce"
app:showAsAction="never"/>
<item
android:id="@+id/menu_action_anticipate_overshoot_interpolator"
android:title="@string/menuitem_title_anticipate_overshoot"
app:showAsAction="never"/>
<item
android:id="@+id/menu_action_path_interpolator"
android:title="@string/menuitem_title_path"
app:showAsAction="never"/>
</menu>

View File

@@ -5,4 +5,8 @@
android:id="@+id/menu_action_change_location"
android:title="@string/menuitem_change_location"
app:showAsAction="never"/>
<item
android:id="@+id/menu_action_toggle_source"
android:title="@string/menuitem_toggle_symbol_layer_visibility"
app:showAsAction="never"/>
</menu>

View File

@@ -12,6 +12,11 @@
<string name="menuitem_title_change_location_source_null">Reset location source to null</string>
<string name="menuitem_change_icon_overlap">Toggle icon overlap</string>
<string name="menuitem_change_location">Change location</string>
<string name="menuitem_title_accelerate_decelerate">Accelerate/Decelerate interpolator</string>
<string name="menuitem_title_bounce">Bounce interpolator</string>
<string name="menuitem_title_anticipate_overshoot">Anticipate/Overshoot interpolator</string>
<string name="menuitem_title_path">PathInterpolator interpolator</string>
<string name="menuitem_toggle_symbol_layer_visibility">Toggle Symbol Layer Visibility</string>
<string name="button_camera_move">Move</string>
<string name="button_camera_ease">Ease</string>
<string name="button_camera_animate">Animate</string>

View File

@@ -4,8 +4,6 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.1'
classpath 'com.amazonaws:aws-devicefarm-gradle-plugin:1.2'
classpath 'com.stanfy.spoon:spoon-gradle-plugin:1.2.1'
}
}

View File

@@ -142,6 +142,8 @@ add_library(mbgl-android STATIC
platform/android/src/style/conversion/types_string_values.hpp
platform/android/src/map/camera_position.cpp
platform/android/src/map/camera_position.hpp
platform/android/src/map/image.cpp
platform/android/src/map/image.hpp
# Style conversion Java -> C++
platform/android/src/style/android_conversion.hpp

View File

@@ -27,7 +27,6 @@ ext {
mockito : 'org.mockito:mockito-core:2.10.0',
// instrumentation test
testSpoonRunner : 'com.squareup.spoon:spoon-client:1.7.1',
testRunner : "com.android.support.test:runner:${testRunnerVersion}",
testRules : "com.android.support.test:rules:${testRunnerVersion}",
testEspressoCore : "com.android.support.test.espresso:espresso-core:${espressoVersion}",
@@ -36,7 +35,7 @@ ext {
// support
supportAnnotations : "com.android.support:support-annotations:${supportLibVersion}",
supportAppcompatV7 : "com.android.support:appcompat-v7:${supportLibVersion}",
supportV4 : "com.android.support:support-v4:${supportLibVersion}",
supportFragmentV4 : "com.android.support:support-fragment:${supportLibVersion}",
supportDesign : "com.android.support:design:${supportLibVersion}",
supportRecyclerView : "com.android.support:recyclerview-v7:${supportLibVersion}",

View File

@@ -0,0 +1,31 @@
[
"BaseLocationActivity",
"MapSnapshotterMarkerActivity",
"MapSnapshotterReuseActivity",
"LatLngBoundsActivity",
"BottomSheetActivity",
"MapSnapshotterActivity",
"MockLocationEngine",
"DeleteRegionActivity",
"RealTimeGeoJsonActivity",
"UpdateMetadataActivity",
"CarDrivingActivity",
"MyLocationTrackingModeActivity",
"MyLocationToggleActivity",
"MyLocationTintActivity",
"MyLocationDrawableActivity",
"DoubleMapActivity",
"LocationPickerActivity",
"GeoJsonClusteringActivity",
"RuntimeStyleTestActivity",
"AnimatedMarkerActivity",
"ViewPagerActivity",
"MapFragmentActivity",
"SupportMapFragmentActivity",
"SnapshotActivity",
"NavigationDrawerActivity",
"QueryRenderedFeaturesBoxHighlightActivity",
"MultiMapActivity",
"MapInDialogActivity",
"SimpleMapActivity"
]

View File

@@ -13,8 +13,7 @@ global.camelize = function (str) {
});
}
const excludeActivities = ["BaseLocationActivity","MockLocationEngine","DeleteRegionActivity","RealTimeGeoJsonActivity","UpdateMetadataActivity","CarDrivingActivity","MyLocationTrackingModeActivity","MyLocationToggleActivity","MyLocationTintActivity","MyLocationDrawableActivity","DoubleMapActivity", "LocationPickerActivity","GeoJsonClusteringActivity","RuntimeStyleTestActivity", "AnimatedMarkerActivity", "ViewPagerActivity","MapFragmentActivity","SupportMapFragmentActivity","SnapshotActivity","NavigationDrawerActivity", "QueryRenderedFeaturesBoxHighlightActivity", "MultiMapActivity", "MapInDialogActivity", "SimpleMapActivity"];
const excludeClasses = JSON.parse(fs.readFileSync('platform/android/scripts/exclude-activity-gen.json', 'utf8'));
const appBasePath = 'platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity';
const testBasePath = 'platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/gen';
const subPackages = fs.readdirSync(appBasePath);
@@ -24,7 +23,9 @@ if (!fs.existsSync(testBasePath)){
fs.mkdirSync(testBasePath);
}
console.log("Generating test activities:");
console.log("\nGenerating test activities:\n");
var generatedClasses = [];
var excludedClasses = [];
for(const subPackage of subPackages) {
if(!(subPackage.slice(-5) == '.java')) {
const activities = fs.readdirSync(appBasePath+'/'+subPackage);
@@ -45,18 +46,22 @@ for(const subPackage of subPackages) {
try {
fs.accessSync(filePath, fs.F_OK);
fs.unlinkSync(filePath);
console.log("Removed file: "+filePath);
} catch (e) {
console.log("No file found: "+filePath);
}
// only generate test file if not part of exclude list
if (!(excludeActivities.indexOf(activityName) > -1)) {
console.log("Created file: "+filePath);
// only generate test file if not part of exclude list + if contains Activity in name
if ((!(excludeClasses.indexOf(activityName) > -1)) && activityName.includes("Activity")) {
fs.writeFileSync(filePath, ejsConversionTask([activityName, subPackage]));
generatedClasses.push(activityName);
}else{
console.log("Excluding file: "+filePath);
excludedClasses.push(activityName);
}
}
}
}
for(const generatedClass of generatedClasses){
console.log(generatedClass+"Test");
}
console.log("\nFinished generating " + generatedClasses.length + " activity sanity tests, excluded " + excludeClasses.length + " classes.\n");

View File

@@ -112,6 +112,11 @@ AnnotationIDs AndroidRendererFrontend::queryPointAnnotations(const ScreenBox& bo
return mapRenderer.actor().ask(&Renderer::queryPointAnnotations, box).get();
}
AnnotationIDs AndroidRendererFrontend::queryShapeAnnotations(const ScreenBox& box) const {
// Waits for the result from the orchestration thread and returns
return mapRenderer.actor().ask(&Renderer::queryShapeAnnotations, box).get();
}
} // namespace android
} // namespace mbgl

View File

@@ -36,6 +36,7 @@ public:
std::vector<Feature> queryRenderedFeatures(const ScreenBox&, const RenderedQueryOptions&) const;
std::vector<Feature> querySourceFeatures(const std::string& sourceID, const SourceQueryOptions&) const;
AnnotationIDs queryPointAnnotations(const ScreenBox& box) const;
AnnotationIDs queryShapeAnnotations(const ScreenBox& box) const;
// Memory
void onLowMemory();

View File

@@ -8,8 +8,6 @@
#include "asset_manager_file_source.hpp"
#include "jni/generic_global_ref_deleter.hpp"
#include <string>
namespace mbgl {
namespace android {
@@ -64,6 +62,14 @@ void FileSource::setResourceTransform(jni::JNIEnv& env, jni::Object<FileSource::
}
}
void FileSource::resume(jni::JNIEnv&) {
fileSource->resume();
}
void FileSource::pause(jni::JNIEnv&) {
fileSource->pause();
}
jni::Class<FileSource> FileSource::javaClass;
FileSource* FileSource::getNativePeer(jni::JNIEnv& env, jni::Object<FileSource> jFileSource) {
@@ -93,7 +99,9 @@ void FileSource::registerNative(jni::JNIEnv& env) {
METHOD(&FileSource::getAccessToken, "getAccessToken"),
METHOD(&FileSource::setAccessToken, "setAccessToken"),
METHOD(&FileSource::setAPIBaseUrl, "setApiBaseUrl"),
METHOD(&FileSource::setResourceTransform, "setResourceTransform")
METHOD(&FileSource::setResourceTransform, "setResourceTransform"),
METHOD(&FileSource::resume, "resume"),
METHOD(&FileSource::pause, "pause")
);
}

View File

@@ -41,6 +41,10 @@ public:
void setResourceTransform(jni::JNIEnv&, jni::Object<FileSource::ResourceTransformCallback>);
void resume(jni::JNIEnv&);
void pause(jni::JNIEnv&);
static jni::Class<FileSource> javaClass;
static FileSource* getNativePeer(jni::JNIEnv&, jni::Object<FileSource>);

View File

@@ -172,6 +172,7 @@ void registerNatives(JavaVM *vm) {
// Map
CameraPosition::registerNative(env);
Image::registerNative(env);
// Connectivity
ConnectivityListener::registerNative(env);

View File

@@ -0,0 +1,44 @@
#include <mbgl/style/image.hpp>
#include <mbgl/util/exception.hpp>
#include "image.hpp"
namespace mbgl {
namespace android {
mbgl::style::Image Image::getImage(jni::JNIEnv& env, jni::Object<Image> image) {
static auto widthField = Image::javaClass.GetField<jni::jint>(env, "width");
static auto heightField = Image::javaClass.GetField<jni::jint>(env, "height");
static auto pixelRatioField = Image::javaClass.GetField<jni::jfloat>(env, "pixelRatio");
static auto bufferField = Image::javaClass.GetField<jni::Array<jbyte>>(env, "buffer");
static auto nameField = Image::javaClass.GetField<jni::String>(env, "name");
auto height = image.Get(env, heightField);
auto width = image.Get(env, widthField);
auto pixelRatio = image.Get(env, pixelRatioField);
auto pixels = image.Get(env, bufferField);
auto name = jni::Make<std::string>(env, image.Get(env, nameField));
jni::NullCheck(env, &pixels);
std::size_t size = pixels.Length(env);
mbgl::PremultipliedImage premultipliedImage({ static_cast<uint32_t>(width), static_cast<uint32_t>(height) });
if (premultipliedImage.bytes() != uint32_t(size)) {
throw mbgl::util::SpriteImageException("Sprite image pixel count mismatch");
}
jni::GetArrayRegion(env, *pixels, 0, size, reinterpret_cast<jbyte*>(premultipliedImage.data.get()));
return mbgl::style::Image {name, std::move(premultipliedImage), pixelRatio};
}
void Image::registerNative(jni::JNIEnv &env) {
// Lookup the class
Image::javaClass = *jni::Class<Image>::Find(env).NewGlobalRef(env).release();
}
jni::Class<Image> Image::javaClass;
} // namespace android
} // namespace mb

View File

@@ -0,0 +1,26 @@
#pragma once
#include <mbgl/util/noncopyable.hpp>
#include <jni/jni.hpp>
#include <mbgl/style/image.hpp>
namespace mbgl {
namespace android {
class Image : private mbgl::util::noncopyable {
public:
static constexpr auto Name() { return "com/mapbox/mapboxsdk/maps/Image"; };
static mbgl::style::Image getImage(jni::JNIEnv&, jni::Object<Image>);
static jni::Class<Image> javaClass;
static void registerNative(jni::JNIEnv&);
};
} // namespace android
} // namespace mbgl

View File

@@ -49,6 +49,7 @@
#include "java/util.hpp"
#include "geometry/lat_lng_bounds.hpp"
#include "map/camera_position.hpp"
#include "map/image.hpp"
#include "style/light.hpp"
#include "bitmap_factory.hpp"
@@ -633,6 +634,26 @@ jni::Array<jlong> NativeMapView::queryPointAnnotations(JNIEnv& env, jni::Object<
return result;
}
jni::Array<jlong> NativeMapView::queryShapeAnnotations(JNIEnv &env, jni::Object<RectF> rect) {
using namespace mbgl::style;
using namespace mbgl::style::conversion;
// Convert input
mbgl::ScreenBox box = {
{RectF::getLeft(env, rect), RectF::getTop(env, rect)},
{RectF::getRight(env, rect), RectF::getBottom(env, rect)},
};
mbgl::AnnotationIDs ids = rendererFrontend->queryShapeAnnotations(box);
// Convert result
std::vector<jlong> longIds(ids.begin(), ids.end());
auto result = jni::Array<jni::jlong>::New(env, ids.size());
result.SetRegion<std::vector<jni::jlong>>(env, 0, longIds);
return result;
}
jni::Array<jni::Object<geojson::Feature>> NativeMapView::queryRenderedFeaturesForPoint(JNIEnv& env, jni::jfloat x, jni::jfloat y,
jni::Array<jni::String> layerIds,
jni::Array<jni::Object<>> jfilter) {
@@ -904,6 +925,18 @@ void NativeMapView::addImage(JNIEnv& env, jni::String name, jni::jint w, jni::ji
float(scale)));
}
void NativeMapView::addImages(JNIEnv& env, jni::Array<jni::Object<mbgl::android::Image>> jimages) {
jni::NullCheck(env, &jimages);
std::size_t len = jimages.Length(env);
for (std::size_t i = 0; i < len; i++) {
jni::Object<mbgl::android::Image> jimage = jimages.Get(env, i);
auto image = mbgl::android::Image::getImage(env, jimage);
map->getStyle().addImage(std::make_unique<mbgl::style::Image>(image));
jni::DeleteLocalRef(env, jimage);
}
}
void NativeMapView::removeImage(JNIEnv& env, jni::String name) {
map->getStyle().removeImage(jni::Make<std::string>(env, name));
}
@@ -1000,6 +1033,7 @@ void NativeMapView::registerNative(jni::JNIEnv& env) {
METHOD(&NativeMapView::getTransitionDelay, "nativeGetTransitionDelay"),
METHOD(&NativeMapView::setTransitionDelay, "nativeSetTransitionDelay"),
METHOD(&NativeMapView::queryPointAnnotations, "nativeQueryPointAnnotations"),
METHOD(&NativeMapView::queryShapeAnnotations, "nativeQueryShapeAnnotations"),
METHOD(&NativeMapView::queryRenderedFeaturesForPoint, "nativeQueryRenderedFeaturesForPoint"),
METHOD(&NativeMapView::queryRenderedFeaturesForBox, "nativeQueryRenderedFeaturesForBox"),
METHOD(&NativeMapView::getLight, "nativeGetLight"),
@@ -1017,6 +1051,7 @@ void NativeMapView::registerNative(jni::JNIEnv& env) {
METHOD(&NativeMapView::removeSourceById, "nativeRemoveSourceById"),
METHOD(&NativeMapView::removeSource, "nativeRemoveSource"),
METHOD(&NativeMapView::addImage, "nativeAddImage"),
METHOD(&NativeMapView::addImages, "nativeAddImages"),
METHOD(&NativeMapView::removeImage, "nativeRemoveImage"),
METHOD(&NativeMapView::getImage, "nativeGetImage"),
METHOD(&NativeMapView::setLatLngBounds, "nativeSetLatLngBounds"),

View File

@@ -22,6 +22,7 @@
#include "style/sources/sources.hpp"
#include "geometry/lat_lng_bounds.hpp"
#include "map/camera_position.hpp"
#include "map/image.hpp"
#include "style/light.hpp"
#include "bitmap.hpp"
@@ -197,6 +198,8 @@ public:
jni::Array<jlong> queryPointAnnotations(JNIEnv&, jni::Object<RectF>);
jni::Array<jlong> queryShapeAnnotations(JNIEnv&, jni::Object<RectF>);
jni::Array<jni::Object<geojson::Feature>> queryRenderedFeaturesForPoint(JNIEnv&, jni::jfloat, jni::jfloat,
jni::Array<jni::String>,
jni::Array<jni::Object<>> jfilter);
@@ -235,6 +238,8 @@ public:
void addImage(JNIEnv&, jni::String, jni::jint, jni::jint, jni::jfloat, jni::Array<jbyte>);
void addImages(JNIEnv&, jni::Array<jni::Object<mbgl::android::Image>>);
void removeImage(JNIEnv&, jni::String);
jni::Object<Bitmap> getImage(JNIEnv&, jni::String);

View File

@@ -0,0 +1,291 @@
/* Clock position format, long: {hours} o’clock */
"CLOCK_FMT_LONG" = "%@ часа";
/* Clock position format, medium: {hours} o’clock */
"CLOCK_FMT_MEDIUM" = "%@ часа";
/* Clock position format, short: {hours}:00 */
"CLOCK_FMT_SHORT" = "%@:00";
/* East, long */
"COMPASS_E_LONG" = "изток";
/* East, short */
"COMPASS_E_SHORT" = "И";
/* East by north, long */
"COMPASS_EbN_LONG" = "ийст-тен-уест";
/* East by north, short */
"COMPASS_EbN_SHORT" = "EbN";
/* East by south, long */
"COMPASS_EbS_LONG" = "ийст-тен-саут";
/* East by south, short */
"COMPASS_EbS_SHORT" = "EbS";
/* East-northeast, long */
"COMPASS_ENE_LONG" = "север-североизток";
/* East-northeast, short */
"COMPASS_ENE_SHORT" = "ССИ";
/* East-southeast, long */
"COMPASS_ESE_LONG" = "изток-югоизток";
/* East-southeast, short */
"COMPASS_ESE_SHORT" = "ИЮИ";
/* North, long */
"COMPASS_N_LONG" = "север";
/* North, short */
"COMPASS_N_SHORT" = "С";
/* North by east, long */
"COMPASS_NbE_LONG" = "норд-тен-ийст";
/* North by east, short */
"COMPASS_NbE_SHORT" = "NbE";
/* North by west, long */
"COMPASS_NbW_LONG" = "норд-тен-уест";
/* North by west, short */
"COMPASS_NbW_SHORT" = "NbW";
/* Northeast, long */
"COMPASS_NE_LONG" = "североизток";
/* Northeast, short */
"COMPASS_NE_SHORT" = "СИ";
/* Northeast by east, long */
"COMPASS_NEbE_LONG" = "нордийст-тен-ийст";
/* Northeast by east, short */
"COMPASS_NEbE_SHORT" = "NEbE";
/* Northeast by north, long */
"COMPASS_NEbN_LONG" = "нордийст-тен-норд";
/* Northeast by north, short */
"COMPASS_NEbN_SHORT" = "NEnN";
/* North-northeast, long */
"COMPASS_NNE_LONG" = "север-североизток";
/* North-northeast, short */
"COMPASS_NNE_SHORT" = "ССИ";
/* North-northwest, long */
"COMPASS_NNW_LONG" = "север-северозапад";
/* North-northwest, short */
"COMPASS_NNW_SHORT" = "ССЗ";
/* Northwest, long */
"COMPASS_NW_LONG" = "северозапад";
/* Northwest, short */
"COMPASS_NW_SHORT" = "СЗ";
/* Northwest by north, long */
"COMPASS_NWbN_LONG" = "нордуест-тен-норд";
/* Northwest by north, short */
"COMPASS_NWbN_SHORT" = "NWbN";
/* Northwest by west, long */
"COMPASS_NWbW_LONG" = "нордуест-тен-уест";
/* Northwest by west, short */
"COMPASS_NWbW_SHORT" = "NWbW";
/* South, long */
"COMPASS_S_LONG" = "юг";
/* South, short */
"COMPASS_S_SHORT" = "Ю";
/* South by east, long */
"COMPASS_SbE_LONG" = "саут-тен-ийст";
/* South by east, short */
"COMPASS_SbE_SHORT" = "SbE";
/* South by west, long */
"COMPASS_SbW_LONG" = "саут-тен-уест";
/* South by west, short */
"COMPASS_SbW_SHORT" = "SbW";
/* Southeast, long */
"COMPASS_SE_LONG" = "югоизток";
/* Southeast, short */
"COMPASS_SE_SHORT" = "ЮИ";
/* Southeast by east, long */
"COMPASS_SEbE_LONG" = "саутийст-тен-ийст";
/* Southeast by east, short */
"COMPASS_SEbE_SHORT" = "SEbE";
/* Southeast by south, long */
"COMPASS_SEbS_LONG" = "саутийст-тен-саут";
/* Southeast by south, short */
"COMPASS_SEbS_SHORT" = "SEbS";
/* South-southeast, long */
"COMPASS_SSE_LONG" = "юг-югоизток";
/* South-southeast, short */
"COMPASS_SSE_SHORT" = "ЮЮИ";
/* South-southwest, long */
"COMPASS_SSW_LONG" = "юг-югозапад";
/* South-southwest, short */
"COMPASS_SSW_SHORT" = "ЮЮЗ";
/* Southwest, long */
"COMPASS_SW_LONG" = "югозапад";
/* Southwest, short */
"COMPASS_SW_SHORT" = "ЮЗ";
/* Southwest by south, long */
"COMPASS_SWbS_LONG" = "саутуест-тен-саут";
/* Southwest by south, short */
"COMPASS_SWbS_SHORT" = "SWbS";
/* Southwest by west, long */
"COMPASS_SWbW_LONG" = "саутуест-тен-уест";
/* Southwest by west, short */
"COMPASS_SWbW_SHORT" = "SWbW";
/* West, long */
"COMPASS_W_LONG" = "запад";
/* West, short */
"COMPASS_W_SHORT" = "З";
/* West by north, long */
"COMPASS_WbN_LONG" = "уест-тен-норд";
/* West by north, short */
"COMPASS_WbN_SHORT" = "WbN";
/* West by south, long */
"COMPASS_WbS_LONG" = "уест-тен-саут";
/* West by south, short */
"COMPASS_WbS_SHORT" = "WbS";
/* West-northwest, long */
"COMPASS_WNW_LONG" = "запад-северозапад";
/* West-northwest, short */
"COMPASS_WNW_SHORT" = "ЗСЗ";
/* West-southwest, long */
"COMPASS_WSW_LONG" = "запад-югозапад";
/* West-southwest, short */
"COMPASS_WSW_SHORT" = "ЗСЗ";
/* Degrees format, long */
"COORD_DEG_LONG" = "%d градус(а)";
/* Degrees format, medium: {degrees} */
"COORD_DEG_MEDIUM" = "%d°";
/* Degrees format, short: {degrees} */
"COORD_DEG_SHORT" = "%d°";
/* Coordinate format, long: {degrees}{minutes} */
"COORD_DM_LONG" = "%1$@ и %2$@";
/* Coordinate format, medium: {degrees}{minutes} */
"COORD_DM_MEDIUM" = "%1$@%2$@";
/* Coordinate format, short: {degrees}{minutes} */
"COORD_DM_SHORT" = "%1$@%2$@";
/* Coordinate format, long: {degrees}{minutes}{seconds} */
"COORD_DMS_LONG" = "%1$@, %2$@ и %3$@";
/* Coordinate format, medium: {degrees}{minutes}{seconds} */
"COORD_DMS_MEDIUM" = "%1$@%2$@%3$@";
/* Coordinate format, short: {degrees}{minutes}{seconds} */
"COORD_DMS_SHORT" = "%1$@%2$@%3$@";
/* East longitude format, long: {longitude} */
"COORD_E_LONG" = "%@ изток";
/* East longitude format, medium: {longitude} */
"COORD_E_MEDIUM" = "%@ изток";
/* East longitude format, short: {longitude} */
"COORD_E_SHORT" = "%@И";
/* Coordinate pair format, long: {latitude}, {longitude} */
"COORD_FMT_LONG" = "%1$@ на %2$@";
/* Coordinate pair format, medium: {latitude}, {longitude} */
"COORD_FMT_MEDIUM" = "%1$@, %2$@";
/* Coordinate pair format, short: {latitude}, {longitude} */
"COORD_FMT_SHORT" = "%1$@, %2$@";
/* Minutes format, long */
"COORD_MIN_LONG" = "%d минута(и)";
/* Minutes format, medium: {minutes} */
"COORD_MIN_MEDIUM" = "%d′";
/* Minutes format, short: {minutes} */
"COORD_MIN_SHORT" = "%d′";
/* North latitude format, long: {latitude} */
"COORD_N_LONG" = "%@ север";
/* North latitude format, medium: {latitude} */
"COORD_N_MEDIUM" = "%@ север";
/* North latitude format, short: {latitude} */
"COORD_N_SHORT" = "%@С";
/* South latitude format, long: {latitude} */
"COORD_S_LONG" = "%@ юг";
/* South latitude format, medium: {latitude} */
"COORD_S_MEDIUM" = "%@ юг";
/* South latitude format, short: {latitude} */
"COORD_S_SHORT" = "%@Ю";
/* Seconds format, long */
"COORD_SEC_LONG" = "%d секунда(и)";
/* Seconds format, medium: {seconds} */
"COORD_SEC_MEDIUM" = "%d″";
/* Seconds format, short: {seconds} */
"COORD_SEC_SHORT" = "%d″";
/* West longitude format, long: {longitude} */
"COORD_W_LONG" = "%@ запад";
/* West longitude format, medium: {longitude} */
"COORD_W_MEDIUM" = "%@ запад";
/* West longitude format, short: {longitude} */
"COORD_W_SHORT" = "%@З";

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>COORD_DEG_LONG</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@degrees@</string>
<key>degrees</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>one</key>
<string>%d градус</string>
<key>other</key>
<string>%d градуса</string>
</dict>
</dict>
<key>COORD_MIN_LONG</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@minutes@</string>
<key>minutes</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>one</key>
<string>%d минута</string>
<key>other</key>
<string>%d минути</string>
</dict>
</dict>
<key>COORD_SEC_LONG</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@seconds@</string>
<key>seconds</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>one</key>
<string>%d секунда</string>
<key>other</key>
<string>%d секунди</string>
</dict>
</dict>
</dict>
</plist>

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>COORD_DEG_LONG</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@degrees@</string>
<key>degrees</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>one</key>
<string>%d fok</string>
<key>other</key>
<string>%d fok</string>
</dict>
</dict>
<key>COORD_MIN_LONG</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@minutes@</string>
<key>minutes</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>one</key>
<string>%d perc</string>
<key>other</key>
<string>%d perc</string>
</dict>
</dict>
<key>COORD_SEC_LONG</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@seconds@</string>
<key>seconds</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>one</key>
<string>%d másodperc</string>
<key>other</key>
<string>%d másodperc</string>
</dict>
</dict>
</dict>
</plist>

View File

@@ -0,0 +1,291 @@
/* Clock position format, long: {hours} o’clock */
"CLOCK_FMT_LONG" = "São %@ horas";
/* Clock position format, medium: {hours} o’clock */
"CLOCK_FMT_MEDIUM" = "São %@ horas";
/* Clock position format, short: {hours}:00 */
"CLOCK_FMT_SHORT" = "%@:00";
/* East, long */
"COMPASS_E_LONG" = "leste";
/* East, short */
"COMPASS_E_SHORT" = "L";
/* East by north, long */
"COMPASS_EbN_LONG" = "lest pelo norte";
/* East by north, short */
"COMPASS_EbN_SHORT" = "LpN";
/* East by south, long */
"COMPASS_EbS_LONG" = "leste pelo sul";
/* East by south, short */
"COMPASS_EbS_SHORT" = "LpS";
/* East-northeast, long */
"COMPASS_ENE_LONG" = "lés-nordeste";
/* East-northeast, short */
"COMPASS_ENE_SHORT" = "ENE";
/* East-southeast, long */
"COMPASS_ESE_LONG" = "lés-sudeste";
/* East-southeast, short */
"COMPASS_ESE_SHORT" = "ESE";
/* North, long */
"COMPASS_N_LONG" = "norte";
/* North, short */
"COMPASS_N_SHORT" = "N";
/* North by east, long */
"COMPASS_NbE_LONG" = "norte pelo leste";
/* North by east, short */
"COMPASS_NbE_SHORT" = "NpL";
/* North by west, long */
"COMPASS_NbW_LONG" = "norte pelo oeste";
/* North by west, short */
"COMPASS_NbW_SHORT" = "NpO";
/* Northeast, long */
"COMPASS_NE_LONG" = "nordeste";
/* Northeast, short */
"COMPASS_NE_SHORT" = "NE";
/* Northeast by east, long */
"COMPASS_NEbE_LONG" = "nordeste pelo leste";
/* Northeast by east, short */
"COMPASS_NEbE_SHORT" = "NEpL";
/* Northeast by north, long */
"COMPASS_NEbN_LONG" = "nordeste pelo norte";
/* Northeast by north, short */
"COMPASS_NEbN_SHORT" = "NEpN";
/* North-northeast, long */
"COMPASS_NNE_LONG" = "nor-nordeste";
/* North-northeast, short */
"COMPASS_NNE_SHORT" = "NNE";
/* North-northwest, long */
"COMPASS_NNW_LONG" = "nor-noroeste";
/* North-northwest, short */
"COMPASS_NNW_SHORT" = "NNO";
/* Northwest, long */
"COMPASS_NW_LONG" = "noroeste";
/* Northwest, short */
"COMPASS_NW_SHORT" = "NO";
/* Northwest by north, long */
"COMPASS_NWbN_LONG" = "noroeste pelo norte";
/* Northwest by north, short */
"COMPASS_NWbN_SHORT" = "NOpN";
/* Northwest by west, long */
"COMPASS_NWbW_LONG" = "noroeste pelo oeste";
/* Northwest by west, short */
"COMPASS_NWbW_SHORT" = "NOpO";
/* South, long */
"COMPASS_S_LONG" = "sul";
/* South, short */
"COMPASS_S_SHORT" = "S";
/* South by east, long */
"COMPASS_SbE_LONG" = "sul pelo leste";
/* South by east, short */
"COMPASS_SbE_SHORT" = "SpL";
/* South by west, long */
"COMPASS_SbW_LONG" = "sul pelo oeste";
/* South by west, short */
"COMPASS_SbW_SHORT" = "SbO";
/* Southeast, long */
"COMPASS_SE_LONG" = "sudeste";
/* Southeast, short */
"COMPASS_SE_SHORT" = "SE";
/* Southeast by east, long */
"COMPASS_SEbE_LONG" = "sudeste pelo leste";
/* Southeast by east, short */
"COMPASS_SEbE_SHORT" = "SEpL";
/* Southeast by south, long */
"COMPASS_SEbS_LONG" = "sudeste pelo sul";
/* Southeast by south, short */
"COMPASS_SEbS_SHORT" = "SEpS";
/* South-southeast, long */
"COMPASS_SSE_LONG" = "sul-sudeste";
/* South-southeast, short */
"COMPASS_SSE_SHORT" = "SSE";
/* South-southwest, long */
"COMPASS_SSW_LONG" = "su-sudoeste";
/* South-southwest, short */
"COMPASS_SSW_SHORT" = "SSO";
/* Southwest, long */
"COMPASS_SW_LONG" = "sudoeste";
/* Southwest, short */
"COMPASS_SW_SHORT" = "SO";
/* Southwest by south, long */
"COMPASS_SWbS_LONG" = "sudoeste pelo sul";
/* Southwest by south, short */
"COMPASS_SWbS_SHORT" = "SOpS";
/* Southwest by west, long */
"COMPASS_SWbW_LONG" = "sudoeste pelo oeste";
/* Southwest by west, short */
"COMPASS_SWbW_SHORT" = "SOpO";
/* West, long */
"COMPASS_W_LONG" = "oeste";
/* West, short */
"COMPASS_W_SHORT" = "O";
/* West by north, long */
"COMPASS_WbN_LONG" = "oeste pelo norte";
/* West by north, short */
"COMPASS_WbN_SHORT" = "OpN";
/* West by south, long */
"COMPASS_WbS_LONG" = "oeste pelo sul";
/* West by south, short */
"COMPASS_WbS_SHORT" = "OpS";
/* West-northwest, long */
"COMPASS_WNW_LONG" = "oés-noroeste";
/* West-northwest, short */
"COMPASS_WNW_SHORT" = "ONO";
/* West-southwest, long */
"COMPASS_WSW_LONG" = "oés-sudoeste";
/* West-southwest, short */
"COMPASS_WSW_SHORT" = "OSO";
/* Degrees format, long */
"COORD_DEG_LONG" = "%d grau(s)";
/* Degrees format, medium: {degrees} */
"COORD_DEG_MEDIUM" = "%d°";
/* Degrees format, short: {degrees} */
"COORD_DEG_SHORT" = "%d°";
/* Coordinate format, long: {degrees}{minutes} */
"COORD_DM_LONG" = "%1$@ e %2$@";
/* Coordinate format, medium: {degrees}{minutes} */
"COORD_DM_MEDIUM" = "%1$@%2$@";
/* Coordinate format, short: {degrees}{minutes} */
"COORD_DM_SHORT" = "%1$@%2$@";
/* Coordinate format, long: {degrees}{minutes}{seconds} */
"COORD_DMS_LONG" = "%1$@, %2$@, e %3$@";
/* Coordinate format, medium: {degrees}{minutes}{seconds} */
"COORD_DMS_MEDIUM" = "%1$@%2$@%3$@";
/* Coordinate format, short: {degrees}{minutes}{seconds} */
"COORD_DMS_SHORT" = "%1$@%2$@%3$@";
/* East longitude format, long: {longitude} */
"COORD_E_LONG" = "%@ leste";
/* East longitude format, medium: {longitude} */
"COORD_E_MEDIUM" = "%@ leste";
/* East longitude format, short: {longitude} */
"COORD_E_SHORT" = "%@L";
/* Coordinate pair format, long: {latitude}, {longitude} */
"COORD_FMT_LONG" = "%1$@ por %2$@";
/* Coordinate pair format, medium: {latitude}, {longitude} */
"COORD_FMT_MEDIUM" = "%1$@, %2$@";
/* Coordinate pair format, short: {latitude}, {longitude} */
"COORD_FMT_SHORT" = "%1$@, %2$@";
/* Minutes format, long */
"COORD_MIN_LONG" = "%d minuto(s)";
/* Minutes format, medium: {minutes} */
"COORD_MIN_MEDIUM" = "%d′";
/* Minutes format, short: {minutes} */
"COORD_MIN_SHORT" = "%d′";
/* North latitude format, long: {latitude} */
"COORD_N_LONG" = "%@ norte";
/* North latitude format, medium: {latitude} */
"COORD_N_MEDIUM" = "%@ norte";
/* North latitude format, short: {latitude} */
"COORD_N_SHORT" = "%@N";
/* South latitude format, long: {latitude} */
"COORD_S_LONG" = "%@ sul";
/* South latitude format, medium: {latitude} */
"COORD_S_MEDIUM" = "%@ sul";
/* South latitude format, short: {latitude} */
"COORD_S_SHORT" = "%@S";
/* Seconds format, long */
"COORD_SEC_LONG" = "%d segundo(s)";
/* Seconds format, medium: {seconds} */
"COORD_SEC_MEDIUM" = "%d″";
/* Seconds format, short: {seconds} */
"COORD_SEC_SHORT" = "%d″";
/* West longitude format, long: {longitude} */
"COORD_W_LONG" = "%@ oeste";
/* West longitude format, medium: {longitude} */
"COORD_W_MEDIUM" = "%@ oeste";
/* West longitude format, short: {longitude} */
"COORD_W_SHORT" = "%@O";

View File

@@ -4,6 +4,10 @@
#import <mbgl/util/projection.hpp>
#if !TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
#import <Cocoa/Cocoa.h>
#endif
/** Vertical field of view, measured in degrees, for determining the altitude
of the viewpoint.
@@ -57,3 +61,48 @@ double MGLZoomLevelForAltitude(CLLocationDistance altitude, CGFloat pitch, CLLoc
CGFloat mapPixelWidthAtZoom = std::cos(MGLRadiansFromDegrees(latitude)) * mbgl::util::M2PI * mbgl::util::EARTH_RADIUS_M / metersPerPixel;
return ::log2(mapPixelWidthAtZoom / mbgl::util::tileSize);
}
MGLRadianDistance MGLDistanceBetweenRadianCoordinates(MGLRadianCoordinate2D from, MGLRadianCoordinate2D to) {
double a = pow(sin((to.latitude - from.latitude) / 2), 2)
+ pow(sin((to.longitude - from.longitude) / 2), 2) * cos(from.latitude) * cos(to.latitude);
return 2 * atan2(sqrt(a), sqrt(1 - a));
}
MGLRadianDirection MGLRadianCoordinatesDirection(MGLRadianCoordinate2D from, MGLRadianCoordinate2D to) {
double a = sin(to.longitude - from.longitude) * cos(to.latitude);
double b = cos(from.latitude) * sin(to.latitude)
- sin(from.latitude) * cos(to.latitude) * cos(to.longitude - from.longitude);
return atan2(a, b);
}
MGLRadianCoordinate2D MGLRadianCoordinateAtDistanceFacingDirection(MGLRadianCoordinate2D coordinate,
MGLRadianDistance distance,
MGLRadianDirection direction) {
double otherLatitude = asin(sin(coordinate.latitude) * cos(distance)
+ cos(coordinate.latitude) * sin(distance) * cos(direction));
double otherLongitude = coordinate.longitude + atan2(sin(direction) * sin(distance) * cos(coordinate.latitude),
cos(distance) - sin(coordinate.latitude) * sin(otherLatitude));
return MGLRadianCoordinate2DMake(otherLatitude, otherLongitude);
}
CLLocationDirection MGLDirectionBetweenCoordinates(CLLocationCoordinate2D firstCoordinate, CLLocationCoordinate2D secondCoordinate) {
// Ported from https://github.com/mapbox/turf-swift/blob/857e2e8060678ef4a7a9169d4971b0788fdffc37/Turf/Turf.swift#L23-L31
MGLRadianCoordinate2D firstRadianCoordinate = MGLRadianCoordinateFromLocationCoordinate(firstCoordinate);
MGLRadianCoordinate2D secondRadianCoordinate = MGLRadianCoordinateFromLocationCoordinate(secondCoordinate);
CGFloat a = sin(secondRadianCoordinate.longitude - firstRadianCoordinate.longitude) * cos(secondRadianCoordinate.latitude);
CGFloat b = (cos(firstRadianCoordinate.latitude) * sin(secondRadianCoordinate.latitude)
- sin(firstRadianCoordinate.latitude) * cos(secondRadianCoordinate.latitude) * cos(secondRadianCoordinate.longitude - firstRadianCoordinate.longitude));
MGLRadianDirection radianDirection = atan2(a, b);
return radianDirection * 180 / M_PI;
}
CGPoint MGLPointRounded(CGPoint point) {
#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
CGFloat scaleFactor = [UIScreen instancesRespondToSelector:@selector(nativeScale)] ? [UIScreen mainScreen].nativeScale : [UIScreen mainScreen].scale;
#elif TARGET_OS_MAC
CGFloat scaleFactor = [NSScreen mainScreen].backingScaleFactor;
#endif
return CGPointMake(round(point.x * scaleFactor) / scaleFactor, round(point.y * scaleFactor) / scaleFactor);
}

View File

@@ -105,36 +105,26 @@ NS_INLINE MGLRadianCoordinate2D MGLRadianCoordinateFromLocationCoordinate(CLLoca
MGLRadiansFromDegrees(locationCoordinate.longitude));
}
/*
/**
Returns the distance in radians given two coordinates.
*/
NS_INLINE MGLRadianDistance MGLDistanceBetweenRadianCoordinates(MGLRadianCoordinate2D from, MGLRadianCoordinate2D to)
{
double a = pow(sin((to.latitude - from.latitude) / 2), 2)
+ pow(sin((to.longitude - from.longitude) / 2), 2) * cos(from.latitude) * cos(to.latitude);
return 2 * atan2(sqrt(a), sqrt(1 - a));
}
MGLRadianDistance MGLDistanceBetweenRadianCoordinates(MGLRadianCoordinate2D from, MGLRadianCoordinate2D to);
/*
/**
Returns direction in radians given two coordinates.
*/
NS_INLINE MGLRadianDirection MGLRadianCoordinatesDirection(MGLRadianCoordinate2D from, MGLRadianCoordinate2D to) {
double a = sin(to.longitude - from.longitude) * cos(to.latitude);
double b = cos(from.latitude) * sin(to.latitude)
- sin(from.latitude) * cos(to.latitude) * cos(to.longitude - from.longitude);
return atan2(a, b);
}
MGLRadianDirection MGLRadianCoordinatesDirection(MGLRadianCoordinate2D from, MGLRadianCoordinate2D to);
/*
Returns coordinate at a given distance and direction away from coordinate.
/**
Returns a coordinate at a given distance and direction away from coordinate.
*/
NS_INLINE MGLRadianCoordinate2D MGLRadianCoordinateAtDistanceFacingDirection(MGLRadianCoordinate2D coordinate,
MGLRadianDistance distance,
MGLRadianDirection direction) {
double otherLatitude = asin(sin(coordinate.latitude) * cos(distance)
+ cos(coordinate.latitude) * sin(distance) * cos(direction));
double otherLongitude = coordinate.longitude + atan2(sin(direction) * sin(distance) * cos(coordinate.latitude),
cos(distance) - sin(coordinate.latitude) * sin(otherLatitude));
return MGLRadianCoordinate2DMake(otherLatitude, otherLongitude);
}
MGLRadianCoordinate2D MGLRadianCoordinateAtDistanceFacingDirection(MGLRadianCoordinate2D coordinate,
MGLRadianDistance distance,
MGLRadianDirection direction);
/**
Returns the direction from one coordinate to another.
*/
CLLocationDirection MGLDirectionBetweenCoordinates(CLLocationCoordinate2D firstCoordinate, CLLocationCoordinate2D secondCoordinate);
CGPoint MGLPointRounded(CGPoint point);

View File

@@ -38,8 +38,11 @@ MGL_EXPORT
bottomLeft: CLLocationCoordinate2D(latitude: 37.936, longitude: -80.425),
bottomRight: CLLocationCoordinate2D(latitude: 37.936, longitude: -71.516),
topRight: CLLocationCoordinate2D(latitude: 46.437, longitude: -71.516))
let source = MGLImageSource(identifier: "radar", coordinateQuad: coordinates, url: URL(string: "https://www.mapbox.com/mapbox-gl-js/assets/radar.gif")!)
let source = MGLImageSource(identifier: "radar-source", coordinateQuad: coordinates, url: URL(string: "https://www.mapbox.com/mapbox-gl-js/assets/radar.gif")!)
mapView.style?.addSource(source)
let layer = MGLRasterStyleLayer(identifier: "radar-layer", source: source)
style.addLayer(layer)
```
*/
MGL_EXPORT

View File

@@ -5,10 +5,10 @@
NS_ASSUME_NONNULL_BEGIN
MGL_EXPORT
/**
The options to use when creating images with the `MGLMapsnapshotter`.
The options to use when creating images with the `MGLMapSnapshotter`.
*/
MGL_EXPORT
@interface MGLMapSnapshotOptions : NSObject
/**
@@ -63,7 +63,8 @@ MGL_EXPORT
/**
The scale of the output image. Defaults to the main screen scale.
Minimum is 1.
The minimum scale is 1.
*/
@property (nonatomic) CGFloat scale;
@@ -196,7 +197,7 @@ MGL_EXPORT
@property (nonatomic) CGSize size;
/**
Indicates whether as snapshot is currently being generated.
Indicates whether a snapshot is currently being generated.
*/
@property (nonatomic, readonly, getter=isLoading) BOOL loading;

View File

@@ -14,11 +14,14 @@
#import "MGLGeometry_Private.h"
#import "NSBundle+MGLAdditions.h"
#import "MGLStyle.h"
#import "MGLAttributionInfo_Private.h"
#if TARGET_OS_IPHONE
#import "UIImage+MGLAdditions.h"
#else
#import "NSImage+MGLAdditions.h"
#import <CoreGraphics/CoreGraphics.h>
#import <QuartzCore/QuartzCore.h>
#endif
const CGPoint MGLLogoImagePosition = CGPointMake(8, 8);
@@ -146,6 +149,33 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
dispatch_async(queue, ^{
_snapshotCallback = std::make_unique<mbgl::Actor<mbgl::MapSnapshotter::Callback>>(*mbgl::Scheduler::GetCurrent(), [=](std::exception_ptr mbglError, mbgl::PremultipliedImage image, mbgl::MapSnapshotter::Attributions attributions, mbgl::MapSnapshotter::PointForFn pointForFn) {
_loading = false;
NSMutableArray *infos = [NSMutableArray array];
#if TARGET_OS_IPHONE
CGFloat fontSize = [UIFont smallSystemFontSize];
UIColor *attributeFontColor = [UIColor blackColor];
#else
CGFloat fontSize = [NSFont systemFontSizeForControlSize:NSMiniControlSize];
NSColor *attributeFontColor = [NSColor blackColor];
#endif
for (auto attribution = attributions.begin(); attribution != attributions.end(); ++attribution) {
NSString *attributionHTMLString = @(attribution->c_str());
NSArray *tileSetInfos = [MGLAttributionInfo attributionInfosFromHTMLString:attributionHTMLString
fontSize:fontSize
linkColor:attributeFontColor];
[infos growArrayByAddingAttributionInfosFromArray:tileSetInfos];
}
CGSize attributionBackgroundSize = CGSizeMake(10, 0);
for (MGLAttributionInfo *info in infos) {
if (info.isFeedbackLink) {
continue;
}
attributionBackgroundSize.width += [info.title size].width + 10;
attributionBackgroundSize.height = MAX([info.title size].height, attributionBackgroundSize.height);
}
if (mbglError) {
NSString *description = @(mbgl::util::toString(mbglError).c_str());
NSDictionary *userInfo = @{NSLocalizedDescriptionKey: description};
@@ -170,10 +200,39 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
#if TARGET_OS_IPHONE
UIImage *logoImage = [UIImage imageNamed:@"mapbox" inBundle:[NSBundle mgl_frameworkBundle] compatibleWithTraitCollection:nil];
CGRect logoImageRect = CGRectMake(MGLLogoImagePosition.x, mglImage.size.height - (MGLLogoImagePosition.y + logoImage.size.height), logoImage.size.width, logoImage.size.height);
CGRect attributionBackgroundFrame = CGRectMake(mglImage.size.width - 10 - attributionBackgroundSize.width,
logoImageRect.origin.y + (logoImageRect.size.height / 2) - (attributionBackgroundSize.height / 2) + 1,
attributionBackgroundSize.width,
attributionBackgroundSize.height);
CGPoint attributionTextPosition = CGPointMake(attributionBackgroundFrame.origin.x + 10,
attributionBackgroundFrame.origin.y - 1);
CGRect cropRect = CGRectMake(attributionBackgroundFrame.origin.x * mglImage.scale,
attributionBackgroundFrame.origin.y * mglImage.scale,
attributionBackgroundSize.width * mglImage.scale,
attributionBackgroundSize.height * mglImage.scale);
UIGraphicsBeginImageContextWithOptions(mglImage.size, NO, self.options.scale);
[mglImage drawInRect:CGRectMake(0, 0, mglImage.size.width, mglImage.size.height)];
[logoImage drawInRect:CGRectMake(MGLLogoImagePosition.x, mglImage.size.height - (MGLLogoImagePosition.y + logoImage.size.height), logoImage.size.width,logoImage.size.height)];
[logoImage drawInRect:logoImageRect];
UIImage *currentImage = UIGraphicsGetImageFromCurrentImageContext();
CGImageRef attributionImageRef = CGImageCreateWithImageInRect([currentImage CGImage], cropRect);
UIImage *attributionImage = [UIImage imageWithCGImage:attributionImageRef];
CGImageRelease(attributionImageRef);
CIImage *ciAttributionImage = [[CIImage alloc] initWithCGImage:attributionImage.CGImage];
UIImage *blurredAttributionBackground = [self blurredAttributionBackground:ciAttributionImage];
[blurredAttributionBackground drawInRect:attributionBackgroundFrame];
[self drawAttributionText:infos origin:attributionTextPosition];
UIImage *compositedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
@@ -183,6 +242,15 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
NSSize targetSize = NSMakeSize(self.options.size.width, self.options.size.height);
NSRect targetFrame = NSMakeRect(0, 0, targetSize.width, targetSize.height);
CGRect logoImageRect = CGRectMake(MGLLogoImagePosition.x, MGLLogoImagePosition.y, logoImage.size.width, logoImage.size.height);
CGRect attributionBackgroundFrame = CGRectMake(targetFrame.size.width - 10 - attributionBackgroundSize.width,
MGLLogoImagePosition.y + 1,
attributionBackgroundSize.width,
attributionBackgroundSize.height);
CGPoint attributionTextPosition = CGPointMake(attributionBackgroundFrame.origin.x + 10,
logoImageRect.origin.y + (logoImageRect.size.height / 2) - (attributionBackgroundSize.height / 2));
NSImage *compositedImage = nil;
NSImageRep *sourceImageRep = [sourceImage bestRepresentationForRect:targetFrame
context:nil
@@ -190,10 +258,24 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
compositedImage = [[NSImage alloc] initWithSize:targetSize];
[compositedImage lockFocus];
[sourceImageRep drawInRect: targetFrame];
[logoImage drawInRect:CGRectMake(MGLLogoImagePosition.x, MGLLogoImagePosition.y, logoImage.size.width,logoImage.size.height)];
[logoImage drawInRect:logoImageRect];
NSBitmapImageRep *attributionBackground = [[NSBitmapImageRep alloc] initWithFocusedViewRect:attributionBackgroundFrame];
CIImage *attributionBackgroundImage = [[CIImage alloc] initWithCGImage:[attributionBackground CGImage]];
NSImage *blurredAttributionBackground = [self blurredAttributionBackground:attributionBackgroundImage];
[blurredAttributionBackground drawInRect:attributionBackgroundFrame];
[self drawAttributionText:infos origin:attributionTextPosition];
[compositedImage unlockFocus];
#endif
// Dispatch result to origin queue
@@ -208,6 +290,47 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
});
}
- (void)drawAttributionText:(NSArray *)attributionInfo origin:(CGPoint)origin
{
for (MGLAttributionInfo *info in attributionInfo) {
if (info.isFeedbackLink) {
continue;
}
[info.title drawAtPoint:origin];
origin.x += [info.title size].width + 10;
}
}
- (MGLImage *)blurredAttributionBackground:(CIImage *)backgroundImage
{
CGAffineTransform transform = CGAffineTransformIdentity;
CIFilter *clamp = [CIFilter filterWithName:@"CIAffineClamp"];
[clamp setValue:backgroundImage forKey:kCIInputImageKey];
[clamp setValue:[NSValue valueWithBytes:&transform objCType:@encode(CGAffineTransform)] forKey:@"inputTransform"];
CIFilter *attributionBlurFilter = [CIFilter filterWithName:@"CIGaussianBlur"];
[attributionBlurFilter setValue:[clamp outputImage] forKey:kCIInputImageKey];
[attributionBlurFilter setValue:@10 forKey:kCIInputRadiusKey];
CIFilter *attributionColorFilter = [CIFilter filterWithName:@"CIColorControls"];
[attributionColorFilter setValue:[attributionBlurFilter outputImage] forKey:kCIInputImageKey];
[attributionColorFilter setValue:@(0.1) forKey:kCIInputBrightnessKey];
CIImage *blurredImage = attributionColorFilter.outputImage;
CIContext *ctx = [CIContext contextWithOptions:nil];
CGImageRef cgimg = [ctx createCGImage:blurredImage fromRect:[backgroundImage extent]];
#if TARGET_OS_IPHONE
return [UIImage imageWithCGImage:cgimg];
#else
return [[NSImage alloc] initWithCGImage:cgimg size:[backgroundImage extent].size];
#endif
}
- (void)cancel
{
_snapshotCallback.reset();

View File

@@ -85,7 +85,8 @@ extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionSimplificationT
or they may be defined by local or external
<a href="http://geojson.org/">GeoJSON</a> code. A shape source is added to an
`MGLStyle` object along with an `MGLVectorStyleLayer` object. The vector style
layer defines the appearance of any content supplied by the shape source.
layer defines the appearance of any content supplied by the shape source. You
can update a shape source by setting its `shape` or `URL` property.
Each
<a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson"><code>geojson</code></a>

View File

@@ -638,7 +638,7 @@ static NSURL *MGLStyleURL_trafficNight;
self.URL ? [NSString stringWithFormat:@"\"%@\"", self.URL] : self.URL];
}
#pragma mark Style language preferences
#pragma mark Mapbox Streets source introspection
- (void)setLocalizesLabels:(BOOL)localizesLabels
{
@@ -749,4 +749,29 @@ static NSURL *MGLStyleURL_trafficNight;
}
}
- (NS_SET_OF(MGLVectorSource *) *)mapboxStreetsSources {
return [self.sources objectsPassingTest:^BOOL (__kindof MGLVectorSource * _Nonnull source, BOOL * _Nonnull stop) {
return [source isKindOfClass:[MGLVectorSource class]] && source.mapboxStreets;
}];
}
- (NS_ARRAY_OF(MGLStyleLayer *) *)placeStyleLayers {
NSSet *streetsSourceIdentifiers = [self.mapboxStreetsSources valueForKey:@"identifier"];
NSSet *placeSourceLayerIdentifiers = [NSSet setWithObjects:@"marine_label", @"country_label", @"state_label", @"place_label", @"water_label", @"poi_label", @"rail_station_label", @"mountain_peak_label", nil];
NSPredicate *isPlacePredicate = [NSPredicate predicateWithBlock:^BOOL (MGLVectorStyleLayer * _Nullable layer, NSDictionary<NSString *, id> * _Nullable bindings) {
return [layer isKindOfClass:[MGLVectorStyleLayer class]] && [streetsSourceIdentifiers containsObject:layer.sourceIdentifier] && [placeSourceLayerIdentifiers containsObject:layer.sourceLayerIdentifier];
}];
return [self.layers filteredArrayUsingPredicate:isPlacePredicate];
}
- (NS_ARRAY_OF(MGLStyleLayer *) *)roadStyleLayers {
NSSet *streetsSourceIdentifiers = [self.mapboxStreetsSources valueForKey:@"identifier"];
NSPredicate *isPlacePredicate = [NSPredicate predicateWithBlock:^BOOL (MGLVectorStyleLayer * _Nullable layer, NSDictionary<NSString *, id> * _Nullable bindings) {
return [layer isKindOfClass:[MGLVectorStyleLayer class]] && [streetsSourceIdentifiers containsObject:layer.sourceIdentifier] && [layer.sourceLayerIdentifier isEqualToString:@"road_label"];
}];
return [self.layers filteredArrayUsingPredicate:isPlacePredicate];
}
@end

View File

@@ -14,6 +14,8 @@ namespace mbgl {
@class MGLAttributionInfo;
@class MGLMapView;
@class MGLOpenGLStyleLayer;
@class MGLVectorSource;
@class MGLVectorStyleLayer;
@interface MGLStyle (Private)
@@ -30,4 +32,11 @@ namespace mbgl {
@end
@interface MGLStyle (MGLStreetsAdditions)
@property (nonatomic, readonly, copy) NS_ARRAY_OF(MGLVectorStyleLayer *) *placeStyleLayers;
@property (nonatomic, readonly, copy) NS_ARRAY_OF(MGLVectorStyleLayer *) *roadStyleLayers;
@end
NS_ASSUME_NONNULL_END

View File

@@ -4,6 +4,12 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT
## master
### Annotations and user interaction
* Increased the default maximum zoom level from 20 to 22. ([#9835](https://github.com/mapbox/mapbox-gl-native/pull/9835))
## 3.7.0
### Networking and storage
* Added a new `MGLMapSnapshotter` class for capturing rendered map images from an `MGLMapView`’s camera. ([#9891](https://github.com/mapbox/mapbox-gl-native/pull/9891))
@@ -23,19 +29,29 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT
* Fixed an issue that could cause antialiasing between polygons on the same layer to fail if the fill layers used data-driven styling for the fill color. ([#9699](https://github.com/mapbox/mapbox-gl-native/pull/9699))
* The previously deprecated support for style classes has been removed. For interface compatibility, the API methods remain, but they are now non-functional.
### Annotations and user interaction
### Annotations
* Fixed several bugs and performance issues related to the use of annotations backed by `MGLAnnotationImage`. The limits on the number and size of images and glyphs has been effectively eliminated and should now depend on hardware constraints. These fixes also apply to images used to represent icons in `MGLSymbolStyleLayer`. ([#9213](https://github.com/mapbox/mapbox-gl-native/pull/9213))
* Increased the default maximum zoom level from 20 to 22. ([#9835](https://github.com/mapbox/mapbox-gl-native/pull/9835))
* Added an `overlays` property to `MGLMapView`. ([#8617](https://github.com/mapbox/mapbox-gl-native/pull/8617))
* Selecting an annotation no longer sets the user tracking mode to `MGLUserTrackingModeNone`. ([#10094](https://github.com/mapbox/mapbox-gl-native/pull/10094))
* Added `-[MGLMapView cameraThatFitsShape:direction:edgePadding:]` to get a camera with zoom level and center coordinate computed to fit a shape. ([#10107](https://github.com/mapbox/mapbox-gl-native/pull/10107))
* Added support selection of shape and polyline annotations.([#9984](https://github.com/mapbox/mapbox-gl-native/pull/9984))
* Fixed an issue where view annotations could be slightly misaligned. View annotation placement is now rounded to the nearest pixel. ([#10219](https://github.com/mapbox/mapbox-gl-native/pull/10219))
* Fixed an issue where a shape annotation callout was not displayed if the centroid was not visible. ([#10255](https://github.com/mapbox/mapbox-gl-native/pull/10255))
### User interaction
* Users of VoiceOver can now swipe left and right to navigate among visible places, points of interest, and roads. ([#9950](https://github.com/mapbox/mapbox-gl-native/pull/9950))
* Increased the default maximum zoom level from 20 to 22. ([#9835](https://github.com/mapbox/mapbox-gl-native/pull/9835))
### Other changes
* Added a Bulgarian localization. ([#10309](https://github.com/mapbox/mapbox-gl-native/pull/10309))
* Fixed an issue that could cause line label rendering glitches when the line geometry is projected to a point behind the plane of the camera. ([#9865](https://github.com/mapbox/mapbox-gl-native/pull/9865))
* Fixed an issue that could cause a crash when using `-[MGLMapView flyToCamera:completionHandler:]` and related methods with zoom levels at or near the maximum value. ([#9381](https://github.com/mapbox/mapbox-gl-native/pull/9381))
* Added `-[MGLMapView showAttribution:]` to allow custom attribution buttons to show the default attribution interface. ([#10085](https://github.com/mapbox/mapbox-gl-native/pull/10085))
* Fixed a conflict between multiple copies of SMCalloutView in a project. ([#10183](https://github.com/mapbox/mapbox-gl-native/pull/10183))
* Fixed a crash when enabling the scale bar in iOS 8. ([#10241](https://github.com/mapbox/mapbox-gl-native/pull/10241))
## 3.6.4 - September 25, 2017

View File

@@ -1,6 +1,6 @@
Pod::Spec.new do |m|
version = '3.7.0-alpha.1'
version = '3.7.0-beta.4'
m.name = 'Mapbox-iOS-SDK-nightly-dynamic'
m.version = "#{version}-nightly"

View File

@@ -1,6 +1,6 @@
Pod::Spec.new do |m|
version = '3.7.0-alpha.1'
version = '3.7.0-beta.4'
m.name = 'Mapbox-iOS-SDK-symbols'
m.version = "#{version}-symbols"

View File

@@ -1,6 +1,6 @@
Pod::Spec.new do |m|
version = '3.7.0-alpha.1'
version = '3.7.0-beta.4'
m.name = 'Mapbox-iOS-SDK'
m.version = version

View File

@@ -165,7 +165,9 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
self.mapView.scaleBar.hidden = NO;
self.mapView.showsUserHeadingIndicator = YES;
self.hudLabel.hidden = YES;
self.hudLabel.titleLabel.font = [UIFont monospacedDigitSystemFontOfSize:10 weight:UIFontWeightRegular];
if ([UIFont respondsToSelector:@selector(monospacedDigitSystemFontOfSize:weight:)]) {
self.hudLabel.titleLabel.font = [UIFont monospacedDigitSystemFontOfSize:10 weight:UIFontWeightRegular];
}
if ([MGLAccountManager accessToken].length)
{
@@ -827,6 +829,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
}
MGLPolygon *polygon = [MGLPolygon polygonWithCoordinates:polygonCoordinates count:[stateCoordinatePairs count]];
polygon.title = feature[@"properties"][@"NAME"];
[self.mapView addAnnotation:polygon];

View File

@@ -4,6 +4,8 @@ default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git
trigger_map:
- pattern: nightly-release
workflow: nightly-release
- pattern: release-from-tag
workflow: release-from-tag
- pattern: "*"
is_pull_request_allowed: true
workflow: primary
@@ -11,48 +13,9 @@ workflows:
primary:
steps:
- script:
title: Install Dependencies
title: Skip Workflow
inputs:
- content: |-
#!/bin/bash
set -eu -o pipefail
brew install cmake
- is_debug: 'yes'
- script:
title: Generate Workspace
inputs:
- content: |-
#!/bin/bash
set -eu -o pipefail
export BUILDTYPE=Debug
make iproj
- is_debug: 'yes'
- xcode-test:
title: Run SDK Unit Tests
inputs:
- project_path: platform/ios/ios.xcworkspace
- scheme: CI
- deploy-to-bitrise-io:
title: Deploy to Bitrise.io
inputs:
- notify_user_groups: none
- slack:
title: Post to Slack
inputs:
- webhook_url: "$SLACK_HOOK_URL"
- channel: "#gl-bots"
- from_username: 'Bitrise iOS'
- from_username_on_error: 'Bitrise iOS'
- message: '<${BITRISE_BUILD_URL}|Build #${BITRISE_BUILD_NUMBER}>
for <https://github.com/mapbox/mapbox-gl-native/compare/${BITRISE_GIT_BRANCH}|mapbox/mapbox-gl-native@${BITRISE_GIT_BRANCH}>
by ${GIT_CLONE_COMMIT_COMMITER_NAME}
passed'
- message_on_error: '<${BITRISE_BUILD_URL}|Build #${BITRISE_BUILD_NUMBER}>
for <https://github.com/mapbox/mapbox-gl-native/compare/${BITRISE_GIT_BRANCH}|mapbox/mapbox-gl-native@${BITRISE_GIT_BRANCH}>
by ${GIT_CLONE_COMMIT_COMMITER_NAME}
failed'
- icon_url: https://bitrise-public-content-production.s3.amazonaws.com/slack/bitrise-slack-icon-128.png
- icon_url_on_error: https://bitrise-public-content-production.s3.amazonaws.com/slack/bitrise-slack-error-icon-128.png
- content: echo "This workflow is obsolete — see CircleCi."
nightly-release:
steps:
- script:
@@ -62,9 +25,13 @@ workflows:
#!/bin/bash
set -eu -o pipefail
brew install cmake
sudo easy_install pip
sudo pip install awscli
- is_debug: 'yes'
- script:
title: Configure AWS-CLI
inputs:
- content: |-
#!/bin/bash
pip install awscli
- script:
title: Build package
inputs:
@@ -83,8 +50,8 @@ workflows:
inputs:
- webhook_url: "$SLACK_HOOK_URL"
- channel: "#gl-bots"
- from_username: 'Bitrise iOS Nightly \U0001F31D'
- from_username_on_error: 'Bitrise iOS Nightly \U0001F31D'
- from_username: 'Bitrise iOS Nightly 💤'
- from_username_on_error: 'Bitrise iOS Nightly 💤'
- message: '<${BITRISE_BUILD_URL}|Build #${BITRISE_BUILD_NUMBER}>
for <https://github.com/mapbox/mapbox-gl-native/compare/${BITRISE_GIT_BRANCH}@%7B1day%7D...${BITRISE_GIT_BRANCH}|mapbox/mapbox-gl-native@${BITRISE_GIT_BRANCH}>
completed successfully.'
@@ -93,3 +60,39 @@ workflows:
failed.'
- icon_url: https://bitrise-public-content-production.s3.amazonaws.com/slack/bitrise-slack-icon-128.png
- icon_url_on_error: https://bitrise-public-content-production.s3.amazonaws.com/slack/bitrise-slack-error-icon-128.png
release-from-tag:
steps:
- script:
title: Install Dependencies
inputs:
- content: |-
#!/bin/bash
set -eu -o pipefail
brew install cmake
sudo easy_install pip
sudo pip install awscli
- is_debug: 'yes'
- script:
title: Build package
inputs:
- content: |-
#!/bin/bash
set -eu -o pipefail
export VERSION_TAG=${BITRISE_GIT_TAG}
platform/ios/scripts/deploy-packages.sh
- is_debug: 'yes'
- slack:
title: Post to Slack
inputs:
- webhook_url: "$SLACK_HOOK_URL"
- channel: "#gl-bots"
- from_username: 'Bitrise iOS Deploy'
- from_username_on_error: 'Bitrise iOS Deploy'
- message: '<${BITRISE_BUILD_URL}|Build #${BITRISE_BUILD_NUMBER}>
for <https://github.com/mapbox/mapbox-gl-native/releases/tag/${BITRISE_GIT_TAG}|`${BITRISE_GIT_TAG}`>
completed successfully.'
- message_on_error: '<${BITRISE_BUILD_URL}|Build #${BITRISE_BUILD_NUMBER}>
for <https://github.com/mapbox/mapbox-gl-native/releases/tag/${BITRISE_GIT_TAG}|`${BITRISE_GIT_TAG}`>
failed.'
- icon_url: https://bitrise-public-content-production.s3.amazonaws.com/slack/bitrise-slack-icon-128.png
- icon_url_on_error: https://bitrise-public-content-production.s3.amazonaws.com/slack/bitrise-slack-error-icon-128.png

View File

@@ -51,7 +51,7 @@ To use annotation views, implement `MGLMapViewDelegate` `-mapView:viewForAnnotat
* No limit on style or image size
* Full support for animations
* Relative control over z-ordering using the `zPosition` property on `CALayer`
* [Familiar API for MapKit users](https://www.mapbox.com/help/switch-mapkit/#annotations-pins)
* Familiar API for MapKit users
**Cons**
@@ -63,7 +63,7 @@ To use annotation views, implement `MGLMapViewDelegate` `-mapView:viewForAnnotat
For absolute full control of how points are displayed on a map, consider [runtime styling](runtime-styling.html).
You can use `MGLPointFeature` or any of the other [style feature subclasses](Style%20Features.html) to add points and shapes to an `MGLShapeSource`.
You can use `MGLPointFeature` or any other [style primitives](Style%20Primitives.html) to add points and shapes to an `MGLShapeSource`.
From there, you can create one or many `MGLSymbolStyleLayer` or `MGLCircleStyleLayer` layers to filter and style points for display on the map ([example](https://www.mapbox.com/ios-sdk/examples/runtime-multiple-annotations)).

View File

@@ -0,0 +1,3 @@
"TELEMETRY_GROUP_TITLE" = "Настройки за поверителност";
"TELEMETRY_SWITCH_TITLE" = "Mapbox Телеметрия";
"TELEMETRY_GROUP_FOOTER" = "Тази настройка позволява на приложението да споделя анонимни локации и данни за използване с Mapbox.";

View File

@@ -292,10 +292,15 @@
DA35A2CB1CCAAAD200E826B2 /* NSValue+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2C81CCAAAD200E826B2 /* NSValue+MGLAdditions.m */; };
DA35A2CC1CCAAAD200E826B2 /* NSValue+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2C81CCAAAD200E826B2 /* NSValue+MGLAdditions.m */; };
DA35D0881E1A6309007DED41 /* one-liner.json in Resources */ = {isa = PBXBuildFile; fileRef = DA35D0871E1A6309007DED41 /* one-liner.json */; };
DA5DB12A1FABF1EE001C2326 /* MGLMapAccessibilityElementTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5DB1291FABF1EE001C2326 /* MGLMapAccessibilityElementTests.m */; };
DA6408DB1DA4E7D300908C90 /* MGLVectorStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = DA6408D91DA4E7D300908C90 /* MGLVectorStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
DA6408DC1DA4E7D300908C90 /* MGLVectorStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = DA6408D91DA4E7D300908C90 /* MGLVectorStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
DA6408DD1DA4E7D300908C90 /* MGLVectorStyleLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = DA6408DA1DA4E7D300908C90 /* MGLVectorStyleLayer.m */; };
DA6408DE1DA4E7D300908C90 /* MGLVectorStyleLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = DA6408DA1DA4E7D300908C90 /* MGLVectorStyleLayer.m */; };
DA704CC21F65A475004B3F28 /* MGLMapAccessibilityElement.h in Headers */ = {isa = PBXBuildFile; fileRef = DA704CC01F65A475004B3F28 /* MGLMapAccessibilityElement.h */; };
DA704CC31F65A475004B3F28 /* MGLMapAccessibilityElement.h in Headers */ = {isa = PBXBuildFile; fileRef = DA704CC01F65A475004B3F28 /* MGLMapAccessibilityElement.h */; };
DA704CC41F65A475004B3F28 /* MGLMapAccessibilityElement.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA704CC11F65A475004B3F28 /* MGLMapAccessibilityElement.mm */; };
DA704CC51F65A475004B3F28 /* MGLMapAccessibilityElement.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA704CC11F65A475004B3F28 /* MGLMapAccessibilityElement.mm */; };
DA72620B1DEEE3480043BB89 /* MGLOpenGLStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = DA7262091DEEE3480043BB89 /* MGLOpenGLStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
DA72620C1DEEE3480043BB89 /* MGLOpenGLStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = DA7262091DEEE3480043BB89 /* MGLOpenGLStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
DA72620D1DEEE3480043BB89 /* MGLOpenGLStyleLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA72620A1DEEE3480043BB89 /* MGLOpenGLStyleLayer.mm */; };
@@ -774,6 +779,14 @@
DA2E885D1CC0382C00F24E7B /* MGLOfflinePackTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLOfflinePackTests.m; path = ../../darwin/test/MGLOfflinePackTests.m; sourceTree = "<group>"; };
DA2E885E1CC0382C00F24E7B /* MGLOfflineRegionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLOfflineRegionTests.m; path = ../../darwin/test/MGLOfflineRegionTests.m; sourceTree = "<group>"; };
DA2E88601CC0382C00F24E7B /* MGLStyleTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLStyleTests.mm; path = ../../darwin/test/MGLStyleTests.mm; sourceTree = "<group>"; };
DA33895F1FA3EAB7001EA329 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Foundation.strings"; sourceTree = "<group>"; };
DA3389651FA3EE1B001EA329 /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/Localizable.strings; sourceTree = "<group>"; };
DA3389661FA3EE28001EA329 /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/Foundation.strings; sourceTree = "<group>"; };
DA3389671FA3EE2F001EA329 /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = bg; path = bg.lproj/Foundation.stringsdict; sourceTree = "<group>"; };
DA3389681FA3EE48001EA329 /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/Localizable.strings; sourceTree = "<group>"; };
DA3389691FA3EE50001EA329 /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = bg; path = bg.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
DA33896A1FA3EE58001EA329 /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/Root.strings; sourceTree = "<group>"; };
DA33896B1FA3EF4A001EA329 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = hu; path = hu.lproj/Foundation.stringsdict; sourceTree = "<group>"; };
DA35A29D1CC9E94C00E826B2 /* MGLCoordinateFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLCoordinateFormatter.h; sourceTree = "<group>"; };
DA35A2A01CC9E95F00E826B2 /* MGLCoordinateFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLCoordinateFormatter.m; sourceTree = "<group>"; };
DA35A2A91CCA058D00E826B2 /* MGLCoordinateFormatterTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLCoordinateFormatterTests.m; path = ../../darwin/test/MGLCoordinateFormatterTests.m; sourceTree = "<group>"; };
@@ -794,6 +807,7 @@
DA57D4AC1EBA922A00793288 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = vi; path = vi.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
DA5C09BA1EFC48550056B178 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Localizable.strings; sourceTree = "<group>"; };
DA5C09BB1EFC486C0056B178 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Localizable.strings; sourceTree = "<group>"; };
DA5DB1291FABF1EE001C2326 /* MGLMapAccessibilityElementTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLMapAccessibilityElementTests.m; sourceTree = "<group>"; };
DA6023F11E4CE94300DBFF23 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Foundation.strings; sourceTree = "<group>"; };
DA6023F21E4CE94800DBFF23 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = sv; path = sv.lproj/Foundation.stringsdict; sourceTree = "<group>"; };
DA618B111E68823600CB7F44 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ru; path = ru.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
@@ -812,6 +826,8 @@
DA704CBB1F637311004B3F28 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Foundation.strings; sourceTree = "<group>"; };
DA704CBC1F637405004B3F28 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = uk; path = uk.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
DA704CBD1F63746E004B3F28 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
DA704CC01F65A475004B3F28 /* MGLMapAccessibilityElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLMapAccessibilityElement.h; sourceTree = "<group>"; };
DA704CC11F65A475004B3F28 /* MGLMapAccessibilityElement.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLMapAccessibilityElement.mm; sourceTree = "<group>"; };
DA704CC71F6663A3004B3F28 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Foundation.strings; sourceTree = "<group>"; };
DA7262091DEEE3480043BB89 /* MGLOpenGLStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLOpenGLStyleLayer.h; sourceTree = "<group>"; };
DA72620A1DEEE3480043BB89 /* MGLOpenGLStyleLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLOpenGLStyleLayer.mm; sourceTree = "<group>"; };
@@ -1111,6 +1127,8 @@
35599DB81D46AD7F0048254D /* Categories */ = {
isa = PBXGroup;
children = (
1FDD9D6D1F26936400252B09 /* MGLVectorSource+MGLAdditions.h */,
1FDD9D6E1F26936400252B09 /* MGLVectorSource+MGLAdditions.m */,
350098DA1D484E60004B2AF0 /* NSValue+MGLStyleAttributeAdditions.h */,
350098DB1D484E60004B2AF0 /* NSValue+MGLStyleAttributeAdditions.mm */,
);
@@ -1338,6 +1356,7 @@
3598544C1E1D38AA00B29F84 /* MGLDistanceFormatterTests.m */,
DA0CD58F1CF56F6A00A5F5A5 /* MGLFeatureTests.mm */,
DA2E885C1CC0382C00F24E7B /* MGLGeometryTests.mm */,
DA5DB1291FABF1EE001C2326 /* MGLMapAccessibilityElementTests.m */,
35E208A61D24210F00EC9A46 /* MGLNSDataAdditionsTests.m */,
1F95931C1E6DE2E900D5B294 /* MGLNSDateAdditionsTests.mm */,
DAE7DEC11E245455007505A6 /* MGLNSStringAdditionsTests.m */,
@@ -1415,6 +1434,8 @@
35CE617F1D4165C2004F2359 /* Categories */,
DAD165841CF4D06B001FF4B9 /* Annotations */,
DAD165851CF4D08B001FF4B9 /* Telemetry */,
DA704CC01F65A475004B3F28 /* MGLMapAccessibilityElement.h */,
DA704CC11F65A475004B3F28 /* MGLMapAccessibilityElement.mm */,
DA8848361CBAFB8500AB86E3 /* MGLMapView.h */,
DA17BE2F1CC4BAC300402C41 /* MGLMapView_Private.h */,
DA8848371CBAFB8500AB86E3 /* MGLMapView+IBAdditions.h */,
@@ -1612,8 +1633,6 @@
DAD165831CF4CFED001FF4B9 /* Categories */ = {
isa = PBXGroup;
children = (
1FDD9D6D1F26936400252B09 /* MGLVectorSource+MGLAdditions.h */,
1FDD9D6E1F26936400252B09 /* MGLVectorSource+MGLAdditions.m */,
7E016D821D9E890300A29A21 /* MGLPolygon+MGLAdditions.h */,
7E016D831D9E890300A29A21 /* MGLPolygon+MGLAdditions.m */,
7E016D7C1D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.h */,
@@ -1754,6 +1773,7 @@
DA35A2C91CCAAAD200E826B2 /* NSValue+MGLAdditions.h in Headers */,
3510FFEA1D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.h in Headers */,
DA6408DB1DA4E7D300908C90 /* MGLVectorStyleLayer.h in Headers */,
DA704CC21F65A475004B3F28 /* MGLMapAccessibilityElement.h in Headers */,
DD0902AB1DB192A800C5BDCE /* MGLNetworkConfiguration.h in Headers */,
DA8848571CBAFB9800AB86E3 /* MGLMapboxEvents.h in Headers */,
35D3A1E61E9BE7EB002B38EE /* MGLScaleBar.h in Headers */,
@@ -1889,6 +1909,7 @@
558DE7A11E5615E400C7916D /* MGLFoundation_Private.h in Headers */,
3538AA1E1D542239008EC33D /* MGLForegroundStyleLayer.h in Headers */,
30E578181DAA85520050F07E /* UIImage+MGLAdditions.h in Headers */,
DA704CC31F65A475004B3F28 /* MGLMapAccessibilityElement.h in Headers */,
40F887711D7A1E59008ECB67 /* MGLShapeSource_Private.h in Headers */,
DABFB8631CBE99E500D62B32 /* MGLOfflineRegion.h in Headers */,
DA35A2B21CCA141D00E826B2 /* MGLCompassDirectionFormatter.h in Headers */,
@@ -2109,6 +2130,7 @@
fi,
nl,
hu,
bg,
);
mainGroup = DA1DC9411CB6C1C2006E619F;
productRefGroup = DA1DC94B1CB6C1C2006E619F /* Products */;
@@ -2267,6 +2289,7 @@
DA2E88621CC0382C00F24E7B /* MGLOfflinePackTests.m in Sources */,
55E2AD131E5B125400E8C587 /* MGLOfflineStorageTests.mm in Sources */,
920A3E5D1E6F995200C16EFC /* MGLSourceQueryTests.m in Sources */,
DA5DB12A1FABF1EE001C2326 /* MGLMapAccessibilityElementTests.m in Sources */,
FAE1CDCB1E9D79CB00C40B5B /* MGLFillExtrusionStyleLayerTests.mm in Sources */,
DA35A2AA1CCA058D00E826B2 /* MGLCoordinateFormatterTests.m in Sources */,
357579831D502AE6000B822E /* MGLRasterStyleLayerTests.mm in Sources */,
@@ -2355,6 +2378,7 @@
DA88482A1CBAFA6200AB86E3 /* MGLTilePyramidOfflineRegion.mm in Sources */,
4049C29F1DB6CD6C00B3F799 /* MGLPointCollection.mm in Sources */,
35136D3F1D42273000C20EFD /* MGLLineStyleLayer.mm in Sources */,
DA704CC41F65A475004B3F28 /* MGLMapAccessibilityElement.mm in Sources */,
DA72620D1DEEE3480043BB89 /* MGLOpenGLStyleLayer.mm in Sources */,
DA88481A1CBAFA6200AB86E3 /* MGLAccountManager.m in Sources */,
3510FFFB1D6DCC4700F413B2 /* NSCompoundPredicate+MGLAdditions.mm in Sources */,
@@ -2442,6 +2466,7 @@
DAA4E4211CBB730400178DFB /* MGLOfflineStorage.mm in Sources */,
4049C2A01DB6CD6C00B3F799 /* MGLPointCollection.mm in Sources */,
35136D401D42273000C20EFD /* MGLLineStyleLayer.mm in Sources */,
DA704CC51F65A475004B3F28 /* MGLMapAccessibilityElement.mm in Sources */,
DA72620E1DEEE3480043BB89 /* MGLOpenGLStyleLayer.mm in Sources */,
DAA4E42F1CBB730400178DFB /* MGLCompactCalloutView.m in Sources */,
3510FFFC1D6DCC4700F413B2 /* NSCompoundPredicate+MGLAdditions.mm in Sources */,
@@ -2520,6 +2545,7 @@
DA618B2B1E68932D00CB7F44 /* fi */,
DAE8CCAD1E6E8C70009B5CB0 /* nl */,
DA5C09BA1EFC48550056B178 /* hu */,
DA3389651FA3EE1B001EA329 /* bg */,
);
name = Localizable.strings;
sourceTree = "<group>";
@@ -2544,6 +2570,7 @@
DA618B2C1E68933600CB7F44 /* fi */,
DAE8CCAE1E6E8C76009B5CB0 /* nl */,
DACCD9C81F1F473700BB09A1 /* hu */,
DA33896A1FA3EE58001EA329 /* bg */,
);
name = Root.strings;
sourceTree = "<group>";
@@ -2566,6 +2593,7 @@
DA1AC01B1E5B8774006DF1D6 /* lt */,
DA618B1B1E68884E00CB7F44 /* ca */,
DA5C09BB1EFC486C0056B178 /* hu */,
DA3389681FA3EE48001EA329 /* bg */,
);
name = Localizable.strings;
sourceTree = "<group>";
@@ -2585,6 +2613,8 @@
DAE9E0F11EB7BF1B001E8E8B /* es */,
DA704CBB1F637311004B3F28 /* ru */,
DA704CC71F6663A3004B3F28 /* uk */,
DA33895F1FA3EAB7001EA329 /* pt-BR */,
DA3389661FA3EE28001EA329 /* bg */,
);
name = Foundation.strings;
sourceTree = "<group>";
@@ -2604,6 +2634,8 @@
DA618B1D1E6888F500CB7F44 /* ca */,
DA618B261E68920D00CB7F44 /* lt */,
DACFE7981F66EA2100630DA8 /* vi */,
DA3389671FA3EE2F001EA329 /* bg */,
DA33896B1FA3EF4A001EA329 /* hu */,
);
name = Foundation.stringsdict;
sourceTree = "<group>";
@@ -2631,6 +2663,7 @@
DA57D4AC1EBA922A00793288 /* vi */,
DA704CBC1F637405004B3F28 /* uk */,
DA704CBD1F63746E004B3F28 /* zh-Hant */,
DA3389691FA3EE50001EA329 /* bg */,
);
name = Localizable.stringsdict;
sourceTree = "<group>";

View File

@@ -34,6 +34,9 @@
/* Accessibility label */
"INFO_A11Y_LABEL" = "About this map";
/* List separator */
"LIST_SEPARATOR" = ", ";
/* User-friendly error description */
"LOAD_MAP_FAILED_DESC" = "The map failed to load because an unknown error occurred.";
@@ -46,12 +49,30 @@
/* Accessibility label */
"MAP_A11Y_LABEL" = "Map";
/* Map accessibility value */
"MAP_A11Y_VALUE" = "Zoom %1$dx\n%2$ld annotation(s) visible";
/* Map accessibility value; {number of visible annotations} */
"MAP_A11Y_VALUE_ANNOTATIONS" = "%ld annotation(s) visible.";
/* Map accessibility value; {list of visible places} */
"MAP_A11Y_VALUE_PLACES" = "Places visible: %@.";
/* Map accessibility value; {number of visible roads} */
"MAP_A11Y_VALUE_ROADS" = "%ld road(s) visible.";
/* Map accessibility value; {zoom level} */
"MAP_A11Y_VALUE_ZOOM" = "Zoom %dx.";
/* User-friendly error description */
"PARSE_STYLE_FAILED_DESC" = "The map failed to load because the style is corrupted.";
/* Accessibility value indicating that a road is a divided road (dual carriageway) */
"ROAD_DIVIDED_A11Y_VALUE" = "Divided road";
/* Accessibility value indicating that a road is a one-way road */
"ROAD_ONEWAY_A11Y_VALUE" = "One way";
/* String format for accessibility value for road feature; {route number} */
"ROAD_REF_A11Y_FMT" = "Route %@";
/* Action sheet title */
"SDK_NAME" = "Mapbox iOS SDK";

View File

@@ -0,0 +1,93 @@
/* Accessibility hint */
"ANNOTATION_A11Y_HINT" = "Показва повече инфо";
/* No comment provided by engineer. */
"API_CLIENT_400_DESC" = "Неуспешна сесия за данни. Оригинална заявка: %@";
/* No comment provided by engineer. */
"API_CLIENT_400_REASON" = "Статус кодът беше %ld";
/* No comment provided by engineer. */
"CANCEL" = "Отказ";
/* Accessibility hint for closing the selected annotation’s callout view and returning to the map */
"CLOSE_CALLOUT_A11Y_HINT" = "Връща към картата";
/* Accessibility hint */
"COMPASS_A11Y_HINT" = "Завърта картата в посока север";
/* Accessibility label */
"COMPASS_A11Y_LABEL" = "Компас";
/* Compass abbreviation for north */
"COMPASS_NORTH" = "С";
/* Instructions in Interface Builder designable; {key}, {plist file name} */
"DESIGNABLE" = "За да се показва Mapbox карта тук, добави %1$@ към твоя токен за достъп в %2$@\n\nЗа подробни инструкции, виж:";
/* Setup documentation URL display string; keep as short as possible */
"FIRST_STEPS_URL" = "mapbox.com/help/first-steps-ios-sdk";
/* Accessibility hint */
"INFO_A11Y_HINT" = "Показва кредитите, форма за връзка и още";
/* Accessibility label */
"INFO_A11Y_LABEL" = "За тази карта";
/* User-friendly error description */
"LOAD_MAP_FAILED_DESC" = "Картата не се зареди поради неизвестна грешка.";
/* User-friendly error description */
"LOAD_STYLE_FAILED_DESC" = "Картата не се зареди поради незареждане на стила.";
/* Accessibility label */
"LOGO_A11Y_LABEL" = "Mapbox";
/* Accessibility label */
"MAP_A11Y_LABEL" = "Карта";
/* Map accessibility value */
"MAP_A11Y_VALUE" = "Мащаб %1$dх\n@2$ld видима(и) анотация(и)";
/* User-friendly error description */
"PARSE_STYLE_FAILED_DESC" = "Картата не се зареди поради повреден стил.";
/* Action sheet title */
"SDK_NAME" = "Mapbox iOS SDK";
/* Developer-only SDK update notification; {latest version, in format x.x.x} */
"SDK_UPDATE_AVAILABLE" = "Сега е налична Mapbox iOS SDK версия %@:";
/* User-friendly error description */
"STYLE_NOT_FOUND_DESC" = "Картата не се зареди поради неоткрит или несъвместим стил.";
/* Telemetry prompt message */
"TELEMETRY_DISABLED_MSG" = "Можеш да помогнеш OpenStreetMap и Mapbox да станат по-добри, като предоставиш анонимни данни за потребление.";
/* Telemetry prompt button */
"TELEMETRY_DISABLED_OFF" = "Не участвам";
/* Telemetry prompt button */
"TELEMETRY_DISABLED_ON" = "Участвам";
/* Telemetry prompt message */
"TELEMETRY_ENABLED_MSG" = "Помагаш OpenStreetMap и Mapbox да станат по-добри, като предоставяш анонимни данни за потребление.";
/* Telemetry prompt button */
"TELEMETRY_ENABLED_OFF" = "Спирам участие";
/* Telemetry prompt button */
"TELEMETRY_ENABLED_ON" = "Продължавам да участвам";
/* Telemetry prompt button */
"TELEMETRY_MORE" = "Искам още инфо";
/* Action in attribution sheet */
"TELEMETRY_NAME" = "Mapbox Телеметрия";
/* Telemetry prompt title */
"TELEMETRY_TITLE" = "Направи Mapbox картите по-добри";
/* Default user location annotation title */
"USER_DOT_TITLE" = "Сега си тук";

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>MAP_A11Y_VALUE</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@level@
%#@count@</string>
<key>level</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>one</key>
<string>Мащаб %dx</string>
<key>other</key>
<string>Мащаб %dx</string>
</dict>
<key>count</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>ld</string>
<key>one</key>
<string>%d видима анотация</string>
<key>other</key>
<string>%d видими анотации</string>
</dict>
</dict>
</dict>
</plist>

View File

@@ -2,22 +2,10 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>MAP_A11Y_VALUE</key>
<key>MAP_A11Y_VALUE_ANNOTATIONS</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@level@
%#@count@</string>
<key>level</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>one</key>
<string>Zoom %dx</string>
<key>other</key>
<string>Zoom %dx</string>
</dict>
<string>%#@count@</string>
<key>count</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
@@ -30,5 +18,37 @@
<string>%d annotations visible</string>
</dict>
</dict>
<key>MAP_A11Y_VALUE_ROADS</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@count@</string>
<key>count</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>ld</string>
<key>one</key>
<string>%d road visible</string>
<key>other</key>
<string>%d roads visible</string>
</dict>
</dict>
<key>MAP_A11Y_VALUE_ZOOM</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@level@</string>
<key>level</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>one</key>
<string>Zoom %dx</string>
<key>other</key>
<string>Zoom %dx</string>
</dict>
</dict>
</dict>
</plist>

View File

@@ -2,7 +2,7 @@
"ANNOTATION_A11Y_HINT" = "Mostrar mais informações";
/* No comment provided by engineer. */
"API_CLIENT_400_DESC" = "The session data task failed. Original request was: %@";
"API_CLIENT_400_DESC" = "Tarefa de dados da sessão falhou. Requisição original: %@";
/* No comment provided by engineer. */
"API_CLIENT_400_REASON" = "O código de status foi %ld";
@@ -10,6 +10,9 @@
/* No comment provided by engineer. */
"CANCEL" = "Cancelar";
/* Accessibility hint for closing the selected annotation’s callout view and returning to the map */
"CLOSE_CALLOUT_A11Y_HINT" = "Retornar ao mapa";
/* Accessibility hint */
"COMPASS_A11Y_HINT" = "Rotaciona o mapa com face ao norte";
@@ -20,7 +23,7 @@
"COMPASS_NORTH" = "N";
/* Instructions in Interface Builder designable; {key}, {plist file name} */
"DESIGNABLE" = "To display a Mapbox-hosted map here, set %1$@ to your access token in %2$@\n\nFor detailed instructions, see:";
"DESIGNABLE" = "Para exibir um mapa hospedado no Mapbox aqui, insira %1$@ para seu token de acesso %2$@\n\nPara maiores detalhes, veja:";
/* Setup documentation URL display string; keep as short as possible */
"FIRST_STEPS_URL" = "mapbox.com/help/first-steps-ios-sdk";
@@ -31,6 +34,12 @@
/* Accessibility label */
"INFO_A11Y_LABEL" = "Sobre este mapa";
/* User-friendly error description */
"LOAD_MAP_FAILED_DESC" = "Falha ao carregar mapa devido a um erro desconhecido";
/* User-friendly error description */
"LOAD_STYLE_FAILED_DESC" = "Falha ao carregar mapa porque o estilo não pode ser carregado";
/* Accessibility label */
"LOGO_A11Y_LABEL" = "Mapbox";
@@ -40,11 +49,20 @@
/* Map accessibility value */
"MAP_A11Y_VALUE" = "Zoom %1$dx\n%2$ld anotações visíveis";
/* User-friendly error description */
"PARSE_STYLE_FAILED_DESC" = "Falha ao carregar mapa porque o estilo está corrompido.";
/* Action sheet title */
"SDK_NAME" = "Mapbox iOS SDK";
/* Developer-only SDK update notification; {latest version, in format x.x.x} */
"SDK_UPDATE_AVAILABLE" = "SDK Mapbox para iOS versão %@ está disponível:";
/* User-friendly error description */
"STYLE_NOT_FOUND_DESC" = "Falha ao carregar mapa porque o estilo não pode ser encontrado ou é incompatível.";
/* Telemetry prompt message */
"TELEMETRY_DISABLED_MSG" = "You can help make OpenStreetMap and Mapbox maps better by contributing anonymous usage data.";
"TELEMETRY_DISABLED_MSG" = "Você pode ajudar a tornar o OpenStreetMap e Mapbox ainda melhor contribuindo anonimamente com seus dados de uso.";
/* Telemetry prompt button */
"TELEMETRY_DISABLED_OFF" = "Não Participar";
@@ -53,7 +71,7 @@
"TELEMETRY_DISABLED_ON" = "Participar";
/* Telemetry prompt message */
"TELEMETRY_ENABLED_MSG" = "You are helping to make OpenStreetMap and Mapbox maps better by contributing anonymous usage data.";
"TELEMETRY_ENABLED_MSG" = "Você está ajudando a tornar o OpenStreetMap e Mapbox ainda melhor contribuindo anonimamente com seus dados de uso.";
/* Telemetry prompt button */
"TELEMETRY_ENABLED_OFF" = "Parar de Participar";

View File

@@ -58,17 +58,17 @@ BINARY_DIRECTORY=${BINARY_DIRECTORY:-build/ios/deploy}
GITHUB_RELEASE=${GITHUB_RELEASE:-true}
PUBLISH_PRE_FLAG=''
if [[ -z `which github-release` ]]; then
step "Installing github-release…"
brew install github-release
if [ -z `which github-release` ]; then
echo "Unable to install github-release. See: https://github.com/aktau/github-release"
exit 1
fi
fi
if [[ ${GITHUB_RELEASE} = "true" ]]; then
GITHUB_RELEASE=true # Assign bool, not just a string
if [[ -z `which github-release` ]]; then
step "Installing github-release…"
brew install github-release
if [ -z `which github-release` ]; then
echo "Unable to install github-release. See: https://github.com/aktau/github-release"
exit 1
fi
fi
fi
if [[ -z ${VERSION_TAG} ]]; then
@@ -83,7 +83,7 @@ if [[ $( echo ${VERSION_TAG} | grep --invert-match ios-v ) ]]; then
exit 1
fi
if [[ $( curl --head https://api.github.com/repos/${GITHUB_USER}/${GITHUB_REPO}/releases/tags/${VERSION_TAG} | head -n 1 | grep -c "404 Not Found") == 0 ]]; then
if github-release info --tag ${VERSION_TAG} | grep --quiet "draft: ✗"; then
echo "Error: ${VERSION_TAG} has already been published on GitHub"
echo "See: https://github.com/${GITHUB_USER}/${GITHUB_REPO}/releases/tag/${VERSION_TAG}"
exit 1

View File

@@ -6,7 +6,7 @@ set -u
if [ -z `which jazzy` ]; then
echo "Installing jazzy…"
gem install jazzy
gem install jazzy --no-rdoc --no-ri
if [ -z `which jazzy` ]; then
echo "Unable to install jazzy. See https://github.com/mapbox/mapbox-gl-native/blob/master/platform/ios/INSTALL.md"
exit 1

View File

@@ -7,7 +7,7 @@
callout view displays the represented annotation’s title, subtitle, and
accessory views in a compact, two-line layout.
*/
@interface MGLCompactCalloutView : SMCalloutView <MGLCalloutView>
@interface MGLCompactCalloutView : MGLSMCalloutView <MGLCalloutView>
+ (instancetype)platformCalloutView;

View File

@@ -10,7 +10,7 @@ const CGFloat MGLUserLocationAnnotationDotSize = 22.0;
const CGFloat MGLUserLocationAnnotationHaloSize = 115.0;
const CGFloat MGLUserLocationAnnotationPuckSize = 45.0;
const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuckSize * 0.6;
const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuckSize * 0.5;
const CGFloat MGLUserLocationHeadingUpdateThreshold = 0.01;
@@ -175,12 +175,16 @@ const CGFloat MGLUserLocationHeadingUpdateThreshold = 0.01;
_puckArrow = [CAShapeLayer layer];
_puckArrow.path = [[self puckArrow] CGPath];
_puckArrow.fillColor = [self.mapView.tintColor CGColor];
_puckArrow.bounds = CGRectMake(0, 0, MGLUserLocationAnnotationArrowSize, MGLUserLocationAnnotationArrowSize);
_puckArrow.position = CGPointMake(super.bounds.size.width / 2.0, super.bounds.size.height / 2.0);
_puckArrow.bounds = CGRectMake(0, 0, round(MGLUserLocationAnnotationArrowSize), round(MGLUserLocationAnnotationArrowSize));
_puckArrow.position = CGPointMake(CGRectGetMidX(super.bounds), CGRectGetMidY(super.bounds));
_puckArrow.shouldRasterize = YES;
_puckArrow.rasterizationScale = [UIScreen mainScreen].scale;
_puckArrow.drawsAsynchronously = YES;
_puckArrow.lineJoin = @"round";
_puckArrow.lineWidth = 1.f;
_puckArrow.strokeColor = _puckArrow.fillColor;
[self.layer addSublayer:_puckArrow];
}
if (self.userLocation.location.course >= 0)
@@ -302,7 +306,7 @@ const CGFloat MGLUserLocationHeadingUpdateThreshold = 0.01;
[CATransaction setDisableActions:shouldDisableActions];
_accuracyRingLayer.bounds = CGRectMake(0, 0, accuracyRingSize, accuracyRingSize);
_accuracyRingLayer.cornerRadius = accuracyRingSize / 2;
_accuracyRingLayer.cornerRadius = accuracyRingSize / 2.0;
// match the halo to the accuracy ring
_haloLayer.bounds = _accuracyRingLayer.bounds;
@@ -431,9 +435,11 @@ const CGFloat MGLUserLocationHeadingUpdateThreshold = 0.01;
- (CALayer *)circleLayerWithSize:(CGFloat)layerSize
{
layerSize = round(layerSize);
CALayer *circleLayer = [CALayer layer];
circleLayer.bounds = CGRectMake(0, 0, layerSize, layerSize);
circleLayer.position = CGPointMake(super.bounds.size.width / 2.0, super.bounds.size.height / 2.0);
circleLayer.position = CGPointMake(CGRectGetMidX(super.bounds), CGRectGetMidY(super.bounds));
circleLayer.cornerRadius = layerSize / 2.0;
circleLayer.shouldRasterize = YES;
circleLayer.rasterizationScale = [UIScreen mainScreen].scale;
@@ -456,7 +462,7 @@ const CGFloat MGLUserLocationHeadingUpdateThreshold = 0.01;
- (CGFloat)calculateAccuracyRingSize
{
// diameter in screen points
return self.userLocation.location.horizontalAccuracy / [self.mapView metersPerPointAtLatitude:self.userLocation.coordinate.latitude] * 2.0;
return round(self.userLocation.location.horizontalAccuracy / [self.mapView metersPerPointAtLatitude:self.userLocation.coordinate.latitude] * 2.0);
}
@end

View File

@@ -0,0 +1,54 @@
#import <UIKit/UIKit.h>
#import "MGLFoundation.h"
NS_ASSUME_NONNULL_BEGIN
@protocol MGLFeature;
/// Unique identifier representing a single annotation in mbgl.
typedef uint32_t MGLAnnotationTag;
/** An accessibility element representing something that appears on the map. */
MGL_EXPORT
@interface MGLMapAccessibilityElement : UIAccessibilityElement
@end
/** An accessibility element representing a map annotation. */
@interface MGLAnnotationAccessibilityElement : MGLMapAccessibilityElement
/** The tag of the annotation represented by this element. */
@property (nonatomic) MGLAnnotationTag tag;
- (instancetype)initWithAccessibilityContainer:(id)container tag:(MGLAnnotationTag)identifier NS_DESIGNATED_INITIALIZER;
@end
/** An accessibility element representing a map feature. */
MGL_EXPORT
@interface MGLFeatureAccessibilityElement : MGLMapAccessibilityElement
/** The feature represented by this element. */
@property (nonatomic, strong) id <MGLFeature> feature;
- (instancetype)initWithAccessibilityContainer:(id)container feature:(id <MGLFeature>)feature NS_DESIGNATED_INITIALIZER;
@end
/** An accessibility element representing a place feature. */
MGL_EXPORT
@interface MGLPlaceFeatureAccessibilityElement : MGLFeatureAccessibilityElement
@end
/** An accessibility element representing a road feature. */
MGL_EXPORT
@interface MGLRoadFeatureAccessibilityElement : MGLFeatureAccessibilityElement
@end
/** An accessibility element representing the MGLMapView at large. */
MGL_EXPORT
@interface MGLMapViewProxyAccessibilityElement : UIAccessibilityElement
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,199 @@
#import "MGLMapAccessibilityElement.h"
#import "MGLDistanceFormatter.h"
#import "MGLCompassDirectionFormatter.h"
#import "MGLFeature.h"
#import "MGLVectorSource+MGLAdditions.h"
#import "NSBundle+MGLAdditions.h"
#import "MGLGeometry_Private.h"
@implementation MGLMapAccessibilityElement
- (UIAccessibilityTraits)accessibilityTraits {
return super.accessibilityTraits | UIAccessibilityTraitAdjustable;
}
- (void)accessibilityIncrement {
[self.accessibilityContainer accessibilityIncrement];
}
- (void)accessibilityDecrement {
[self.accessibilityContainer accessibilityDecrement];
}
@end
@implementation MGLAnnotationAccessibilityElement
- (instancetype)initWithAccessibilityContainer:(id)container tag:(MGLAnnotationTag)tag {
if (self = [super initWithAccessibilityContainer:container]) {
_tag = tag;
self.accessibilityHint = NSLocalizedStringWithDefaultValue(@"ANNOTATION_A11Y_HINT", nil, nil, @"Shows more info", @"Accessibility hint");
}
return self;
}
- (UIAccessibilityTraits)accessibilityTraits {
return super.accessibilityTraits | UIAccessibilityTraitButton;
}
@end
@implementation MGLFeatureAccessibilityElement
- (instancetype)initWithAccessibilityContainer:(id)container feature:(id<MGLFeature>)feature {
if (self = [super initWithAccessibilityContainer:container]) {
_feature = feature;
NSString *languageCode = [MGLVectorSource preferredMapboxStreetsLanguage];
NSString *nameAttribute = [NSString stringWithFormat:@"name_%@", languageCode];
NSString *name = [feature attributeForKey:nameAttribute];
// If a feature hasn’t been translated into the preferred language, it
// may be in the local language, which may be written in another script.
// Romanize it.
NSLocale *locale = [NSLocale localeWithLocaleIdentifier:languageCode];
NSOrthography *orthography;
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunguarded-availability-new"
if ([NSOrthography respondsToSelector:@selector(defaultOrthographyForLanguage:)]) {
orthography = [NSOrthography defaultOrthographyForLanguage:locale.localeIdentifier];
}
#pragma clang diagnostic pop
#endif
if ([orthography.dominantScript isEqualToString:@"Latn"]) {
name = [name stringByApplyingTransform:NSStringTransformToLatin reverse:NO];
}
self.accessibilityLabel = name;
}
return self;
}
- (UIAccessibilityTraits)accessibilityTraits {
return super.accessibilityTraits | UIAccessibilityTraitStaticText;
}
@end
@implementation MGLPlaceFeatureAccessibilityElement
- (instancetype)initWithAccessibilityContainer:(id)container feature:(id<MGLFeature>)feature {
if (self = [super initWithAccessibilityContainer:container feature:feature]) {
NSDictionary *attributes = feature.attributes;
NSMutableArray *facts = [NSMutableArray array];
// Announce the kind of place or POI.
if (attributes[@"type"]) {
// FIXME: Unfortunately, these types aren’t a closed set that can be
// localized, since they’re based on OpenStreetMap tags.
NSString *type = [attributes[@"type"] stringByReplacingOccurrencesOfString:@"_"
withString:@" "];
[facts addObject:type];
}
// Announce the kind of airport, rail station, or mountain based on its
// Maki image name.
else if (attributes[@"maki"]) {
// TODO: Localize Maki image names.
[facts addObject:attributes[@"maki"]];
}
// Announce the peak’s elevation in the preferred units.
if (attributes[@"elevation_m"] ?: attributes[@"elevation_ft"]) {
NSLengthFormatter *formatter = [[NSLengthFormatter alloc] init];
formatter.unitStyle = NSFormattingUnitStyleLong;
NSNumber *elevationValue;
NSLengthFormatterUnit unit;
BOOL usesMetricSystem = ![[formatter.numberFormatter.locale objectForKey:NSLocaleMeasurementSystem]
isEqualToString:@"U.S."];
if (usesMetricSystem) {
elevationValue = attributes[@"elevation_m"];
unit = NSLengthFormatterUnitMeter;
} else {
elevationValue = attributes[@"elevation_ft"];
unit = NSLengthFormatterUnitFoot;
}
[facts addObject:[formatter stringFromValue:elevationValue.doubleValue unit:unit]];
}
if (facts.count) {
NSString *separator = NSLocalizedStringWithDefaultValue(@"LIST_SEPARATOR", nil, nil, @", ", @"List separator");
self.accessibilityValue = [facts componentsJoinedByString:separator];
}
}
return self;
}
@end
@implementation MGLRoadFeatureAccessibilityElement
- (instancetype)initWithAccessibilityContainer:(id)container feature:(id<MGLFeature>)feature {
if (self = [super initWithAccessibilityContainer:container feature:feature]) {
NSDictionary *attributes = feature.attributes;
NSMutableArray *facts = [NSMutableArray array];
// Announce the route number.
if (attributes[@"ref"]) {
// TODO: Decorate the route number with the network name based on the shield attribute.
NSString *ref = [NSString stringWithFormat:NSLocalizedStringWithDefaultValue(@"ROAD_REF_A11Y_FMT", nil, nil, @"Route %@", @"String format for accessibility value for road feature; {route number}"), attributes[@"ref"]];
[facts addObject:ref];
}
// Announce whether the road is a one-way road.
if ([attributes[@"oneway"] isEqualToString:@"true"]) {
[facts addObject:NSLocalizedStringWithDefaultValue(@"ROAD_ONEWAY_A11Y_VALUE", nil, nil, @"One way", @"Accessibility value indicating that a road is a one-way road")];
}
// Announce whether the road is a divided road.
MGLPolyline *polyline;
if ([feature isKindOfClass:[MGLMultiPolylineFeature class]]) {
[facts addObject:NSLocalizedStringWithDefaultValue(@"ROAD_DIVIDED_A11Y_VALUE", nil, nil, @"Divided road", @"Accessibility value indicating that a road is a divided road (dual carriageway)")];
polyline = [(MGLMultiPolylineFeature *)feature polylines].firstObject;
}
// Announce the road’s general direction.
if ([feature isKindOfClass:[MGLPolylineFeature class]]) {
polyline = (MGLPolylineFeature *)feature;
}
if (polyline) {
NSUInteger pointCount = polyline.pointCount;
if (pointCount) {
CLLocationCoordinate2D *coordinates = polyline.coordinates;
CLLocationDirection startDirection = MGLDirectionBetweenCoordinates(coordinates[pointCount - 1], coordinates[0]);
CLLocationDirection endDirection = MGLDirectionBetweenCoordinates(coordinates[0], coordinates[pointCount - 1]);
MGLCompassDirectionFormatter *formatter = [[MGLCompassDirectionFormatter alloc] init];
formatter.unitStyle = NSFormattingUnitStyleLong;
NSString *startDirectionString = [formatter stringFromDirection:startDirection];
NSString *endDirectionString = [formatter stringFromDirection:endDirection];
NSString *directionString = [NSString stringWithFormat:NSLocalizedStringWithDefaultValue(@"ROAD_DIRECTION_A11Y_FMT", nil, nil, @"%@ to %@", @"String format for accessibility value for road feature; {starting compass direction}, {ending compass direction}"), startDirectionString, endDirectionString];
[facts addObject:directionString];
}
}
if (facts.count) {
NSString *separator = NSLocalizedStringWithDefaultValue(@"LIST_SEPARATOR", nil, nil, @", ", @"List separator");
self.accessibilityValue = [facts componentsJoinedByString:separator];
}
}
return self;
}
@end
@implementation MGLMapViewProxyAccessibilityElement
- (instancetype)initWithAccessibilityContainer:(id)container {
if (self = [super initWithAccessibilityContainer:container]) {
self.accessibilityTraits = UIAccessibilityTraitButton;
self.accessibilityLabel = [self.accessibilityContainer accessibilityLabel];
self.accessibilityHint = NSLocalizedStringWithDefaultValue(@"CLOSE_CALLOUT_A11Y_HINT", nil, nil, @"Returns to the map", @"Accessibility hint for closing the selected annotation’s callout view and returning to the map");
}
return self;
}
@end

File diff suppressed because it is too large Load Diff

View File

@@ -175,10 +175,15 @@ static const CGFloat MGLFeetPerMeter = 3.28084;
return [self usesMetricSystem] ? self.metersPerPoint : self.metersPerPoint * MGLFeetPerMeter;
}
#pragma mark - Convenient methods
#pragma mark - Convenience methods
- (BOOL)usesRightToLeftLayout {
return [UIView userInterfaceLayoutDirectionForSemanticContentAttribute:self.superview.semanticContentAttribute] == UIUserInterfaceLayoutDirectionRightToLeft;
// semanticContentAttribute is iOS 9+
if ([self.superview respondsToSelector:@selector(semanticContentAttribute)]) {
return [UIView userInterfaceLayoutDirectionForSemanticContentAttribute:self.superview.semanticContentAttribute] == UIUserInterfaceLayoutDirectionRightToLeft;
} else {
return UIApplication.sharedApplication.userInterfaceLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft;
}
}
- (BOOL)usesMetricSystem {
@@ -241,7 +246,7 @@ static const CGFloat MGLFeetPerMeter = 3.28084;
CGFloat alpha = maximumDistance > allowedDistance ? .0f : 1.0f;
if(self.alpha != alpha) {
if (self.alpha != alpha) {
[UIView animateWithDuration:.2f delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
self.alpha = alpha;
} completion:nil];
@@ -331,7 +336,7 @@ static const CGFloat MGLFeetPerMeter = 3.28084;
}
- (void)layoutBars {
CGFloat barWidth = (CGRectGetWidth(self.bounds) - self.borderWidth * 2.0f) / self.bars.count;
CGFloat barWidth = round((CGRectGetWidth(self.bounds) - self.borderWidth * 2.0f) / self.bars.count);
NSUInteger i = 0;
for (UIView *bar in self.bars) {
@@ -354,11 +359,11 @@ static const CGFloat MGLFeetPerMeter = 3.28084;
}
- (void)layoutLabels {
CGFloat barWidth = self.bounds.size.width / self.bars.count;
CGFloat barWidth = round(self.bounds.size.width / self.bars.count);
BOOL RTL = [self usesRightToLeftLayout];
NSUInteger i = RTL ? self.bars.count : 0;
for (MGLScaleBarLabel *label in self.labels) {
CGFloat xPosition = barWidth * i - CGRectGetMidX(label.bounds) + self.borderWidth;
CGFloat xPosition = round(barWidth * i - CGRectGetMidX(label.bounds) + self.borderWidth);
label.frame = CGRectMake(xPosition, 0,
CGRectGetWidth(label.bounds),
CGRectGetHeight(label.bounds));

View File

@@ -34,7 +34,8 @@ MGL_EXPORT
The heading of the user location. (read-only)
This property is `nil` if the user location tracking mode is not
`MGLUserTrackingModeFollowWithHeading`.
`MGLUserTrackingModeFollowWithHeading` or if
`MGLMapView.showsUserHeadingIndicator` is disabled.
*/
@property (nonatomic, readonly, nullable) CLHeading *heading;

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