From e3795f2a5cff126cf98cf6c668abeab614c6d568 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Paczos?= Date: Tue, 27 Aug 2019 18:48:15 +0200 Subject: [android] cancel CameraAnimatorActivity joint example when the map is destroyed --- .../mapboxsdk/testapp/activity/camera/CameraAnimatorActivity.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimatorActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimatorActivity.java index e278d033be..7ce54831f2 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimatorActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimatorActivity.java @@ -36,6 +36,7 @@ public class CameraAnimatorActivity extends AppCompatActivity implements OnMapRe private static final LatLng START_LAT_LNG = new LatLng(37.787947, -122.407432); private final LongSparseArray animators = new LongSparseArray<>(); + private Animator set; { AnimatorSet accelerateDecelerateAnimatorSet = new AnimatorSet(); @@ -92,7 +93,8 @@ public class CameraAnimatorActivity extends AppCompatActivity implements OnMapRe .bearing(135) .build(); - createExampleAnimator(mapboxMap.getCameraPosition(), animatedPosition).start(); + set = createExampleAnimator(mapboxMap.getCameraPosition(), animatedPosition); + set.start(); }); } @@ -237,6 +239,9 @@ public class CameraAnimatorActivity extends AppCompatActivity implements OnMapRe for (int i = 0; i < animators.size(); i++) { animators.get(animators.keyAt(i)).cancel(); } + if (set != null) { + set.cancel(); + } } @Override -- cgit v1.2.1 From 5d316a8076d29cd3545360bc8f018044464c2f96 Mon Sep 17 00:00:00 2001 From: Alexander Shalamov Date: Fri, 23 Aug 2019 18:34:26 +0300 Subject: [core][qt] Check if QFont supports glyph and adjust backing image width - Don't rasterize glyphs if font does not support requested code point - Render symbols at hardcoded baseline, so that locally rasterized glyphs align better with server side generated fonts. - Resize backing QImage width to be at least 24px --- platform/qt/src/local_glyph_rasterizer.cpp | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/platform/qt/src/local_glyph_rasterizer.cpp b/platform/qt/src/local_glyph_rasterizer.cpp index 3f8df5d876..f62975b377 100644 --- a/platform/qt/src/local_glyph_rasterizer.cpp +++ b/platform/qt/src/local_glyph_rasterizer.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -17,13 +18,15 @@ public: optional fontFamily; QFont font; + optional metrics; }; LocalGlyphRasterizer::Impl::Impl(const optional fontFamily_) : fontFamily(fontFamily_) { if (isConfigured()) { font.setFamily(QString::fromStdString(*fontFamily)); - font.setPixelSize(24); + font.setPixelSize(util::ONE_EM); + metrics = QFontMetrics(font); } } @@ -39,29 +42,33 @@ LocalGlyphRasterizer::~LocalGlyphRasterizer() { } bool LocalGlyphRasterizer::canRasterizeGlyph(const FontStack&, GlyphID glyphID) { - return util::i18n::allowsFixedWidthGlyphGeneration(glyphID) && impl->isConfigured(); + return impl->isConfigured() && impl->metrics->inFont(glyphID) && util::i18n::allowsFixedWidthGlyphGeneration(glyphID); } Glyph LocalGlyphRasterizer::rasterizeGlyph(const FontStack&, GlyphID glyphID) { Glyph glyph; glyph.id = glyphID; - QFontMetrics metrics(impl->font); - Size size(metrics.width(glyphID), metrics.height()); + if (!impl->isConfigured()) { + assert(false); + return glyph; + } - glyph.metrics.width = size.width; - glyph.metrics.height = size.height; + glyph.metrics.width = impl->metrics->width(glyphID); + glyph.metrics.height = impl->metrics->height(); glyph.metrics.left = 3; glyph.metrics.top = -8; - glyph.metrics.advance = metrics.width(glyphID); + glyph.metrics.advance = glyph.metrics.width; + // Set width of a glyph's backing image to be util::ONE_EM. + Size size(util::ONE_EM, glyph.metrics.height); QImage image(QSize(size.width, size.height), QImage::Format_Alpha8); image.fill(qRgba(0, 0, 0, 0)); - QPainter painter(&image); painter.setFont(impl->font); painter.setRenderHints(QPainter::TextAntialiasing); - painter.drawText(QPointF(0, metrics.ascent()), QString(QChar(glyphID))); + // Render at constant baseline, to align with glyphs that are rendered by node-fontnik. + painter.drawText(QPointF(0, 20), QString(QChar(glyphID))); auto img = std::make_unique(image.byteCount()); memcpy(img.get(), image.constBits(), image.byteCount()); -- cgit v1.2.1 From 9e3cb67d1cdf1da5dbc22ab45ff8765576e3d87d Mon Sep 17 00:00:00 2001 From: Alexander Shalamov Date: Sun, 25 Aug 2019 09:45:48 +0300 Subject: [core][qt] Update unit tests and include Korean symbols - Decrease pixelmatch tolerance for local glyph rasterizer - Add Korean symbols to test fixture - Fix ifdefs, as the QT and Apple related blocks were using same results - Add NotoSansCJK test for QT / Linux platform --- test/fixtures/local_glyphs/mixed.json | 4 ++-- test/fixtures/local_glyphs/no_local/expected.png | Bin 6963 -> 8855 bytes .../no_local_with_content_insets/expected.png | Bin 6784 -> 8823 bytes .../expected.png | Bin 6026 -> 7537 bytes .../noto_sans_cjk_kr_regular_qt/expected.png | Bin 0 -> 27980 bytes test/fixtures/local_glyphs/ping_fang/expected.png | Bin 18019 -> 18174 bytes .../fixtures/local_glyphs/ping_fang_qt/expected.png | Bin 25026 -> 28524 bytes test/text/local_glyph_rasterizer.test.cpp | 20 ++++++++++++++++++-- 8 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 test/fixtures/local_glyphs/noto_sans_cjk_kr_regular_qt/expected.png diff --git a/test/fixtures/local_glyphs/mixed.json b/test/fixtures/local_glyphs/mixed.json index b982fd9641..3d943084e4 100644 --- a/test/fixtures/local_glyphs/mixed.json +++ b/test/fixtures/local_glyphs/mixed.json @@ -68,7 +68,7 @@ { "type": "Feature", "properties": { - "name": "身什戰アあ" + "name": "안녕 세상 KR" }, "geometry": { "type": "LineString", @@ -144,7 +144,7 @@ { "type": "Feature", "properties": { - "name": "8身什戰アあ" + "name": "안녕 세상8" }, "geometry": { "type": "LineString", diff --git a/test/fixtures/local_glyphs/no_local/expected.png b/test/fixtures/local_glyphs/no_local/expected.png index 77552c32fa..7e2049518b 100644 Binary files a/test/fixtures/local_glyphs/no_local/expected.png and b/test/fixtures/local_glyphs/no_local/expected.png differ diff --git a/test/fixtures/local_glyphs/no_local_with_content_insets/expected.png b/test/fixtures/local_glyphs/no_local_with_content_insets/expected.png index 52ef264590..da5e6957dd 100644 Binary files a/test/fixtures/local_glyphs/no_local_with_content_insets/expected.png and b/test/fixtures/local_glyphs/no_local_with_content_insets/expected.png differ diff --git a/test/fixtures/local_glyphs/no_local_with_content_insets_and_pitch/expected.png b/test/fixtures/local_glyphs/no_local_with_content_insets_and_pitch/expected.png index a1aa5fcb52..2501471562 100644 Binary files a/test/fixtures/local_glyphs/no_local_with_content_insets_and_pitch/expected.png and b/test/fixtures/local_glyphs/no_local_with_content_insets_and_pitch/expected.png differ diff --git a/test/fixtures/local_glyphs/noto_sans_cjk_kr_regular_qt/expected.png b/test/fixtures/local_glyphs/noto_sans_cjk_kr_regular_qt/expected.png new file mode 100644 index 0000000000..0d69075172 Binary files /dev/null and b/test/fixtures/local_glyphs/noto_sans_cjk_kr_regular_qt/expected.png differ diff --git a/test/fixtures/local_glyphs/ping_fang/expected.png b/test/fixtures/local_glyphs/ping_fang/expected.png index 44c24c276a..2ec62e1bc6 100644 Binary files a/test/fixtures/local_glyphs/ping_fang/expected.png and b/test/fixtures/local_glyphs/ping_fang/expected.png differ diff --git a/test/fixtures/local_glyphs/ping_fang_qt/expected.png b/test/fixtures/local_glyphs/ping_fang_qt/expected.png index 465bce5b77..0ede615967 100644 Binary files a/test/fixtures/local_glyphs/ping_fang_qt/expected.png and b/test/fixtures/local_glyphs/ping_fang_qt/expected.png differ diff --git a/test/text/local_glyph_rasterizer.test.cpp b/test/text/local_glyph_rasterizer.test.cpp index 3f0fa1b91f..2722ee5849 100644 --- a/test/text/local_glyph_rasterizer.test.cpp +++ b/test/text/local_glyph_rasterizer.test.cpp @@ -43,7 +43,7 @@ public: MapAdapter map { frontend, MapObserver::nullObserver(), fileSource, MapOptions().withMapMode(MapMode::Static).withSize(frontend.getSize())}; - void checkRendering(const char * name, double imageMatchPixelsThreshold = 0.05, double pixelMatchThreshold = 0.1) { + void checkRendering(const char * name, double imageMatchPixelsThreshold = 0.015, double pixelMatchThreshold = 0.1) { test::checkImage(std::string("test/fixtures/local_glyphs/") + name, frontend.render(map), imageMatchPixelsThreshold, pixelMatchThreshold); } @@ -64,7 +64,7 @@ TEST(LocalGlyphRasterizer, PingFang) { return response; }; test.map.getStyle().loadJSON(util::read_file("test/fixtures/local_glyphs/mixed.json")); -#if defined(__APPLE__) +#if defined(__APPLE__) && !defined(__QT__) test.checkRendering("ping_fang"); #elif defined(__QT__) test.checkRendering("ping_fang_qt"); @@ -73,6 +73,22 @@ TEST(LocalGlyphRasterizer, PingFang) { #endif // defined(__APPLE__) +#if defined(__linux__) && defined(__QT__) +TEST(LocalGlyphRasterizer, NotoSansCJK) { + LocalGlyphRasterizerTest test(std::string("Noto Sans CJK KR Regular")); + + test.fileSource->glyphsResponse = [&] (const Resource& resource) { + EXPECT_EQ(Resource::Kind::Glyphs, resource.kind); + Response response; + response.data = std::make_shared(util::read_file("test/fixtures/resources/glyphs.pbf")); + return response; + }; + + test.map.getStyle().loadJSON(util::read_file("test/fixtures/local_glyphs/mixed.json")); + test.checkRendering("noto_sans_cjk_kr_regular_qt"); +} +#endif // defined(__linux__) && defined(__QT__) + TEST(LocalGlyphRasterizer, NoLocal) { // Expectation: without any local fonts set, and without any CJK glyphs provided, // the output should just contain basic latin characters. -- cgit v1.2.1 From 6ee69e7493d8c3905d20ab7908543ed0b2d623b1 Mon Sep 17 00:00:00 2001 From: Alexander Shalamov Date: Mon, 26 Aug 2019 15:44:27 +0300 Subject: [core] Bump mbgl/linux-gcc-5-qt-5.9 image to 5cd92d7d1c New docker image contains Noto Sans CJK font that is used by the unit tests --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 47bc1e9bdb..3293de1aca 100644 --- a/circle.yml +++ b/circle.yml @@ -1295,7 +1295,7 @@ jobs: # ------------------------------------------------------------------------------ qt5-linux-gcc5-release: docker: - - image: mbgl/linux-gcc-5-qt-5.9:5132cfd29f + - image: mbgl/linux-gcc-5-qt-5.9:5cd92d7d1c resource_class: large working_directory: /src environment: -- cgit v1.2.1 From 9a42bdd7aea0f23e892f2874be6c18acf2b0f0ae Mon Sep 17 00:00:00 2001 From: Alexander Shalamov Date: Wed, 28 Aug 2019 11:57:40 +0300 Subject: [core] Add filtering option to test-runner This change adds filter command line option to render test runner, so that test can be filtered by providing regular expression, for instance: mbgl-render-test -f .*hillshade.* --- render-test/main.cpp | 37 ++---------------------------- render-test/parser.cpp | 62 ++++++++++++++++++++++++++++++++++++++++++-------- render-test/parser.hpp | 2 +- 3 files changed, 56 insertions(+), 45 deletions(-) diff --git a/render-test/main.cpp b/render-test/main.cpp index c7b0b6be55..3ab48fe5b2 100644 --- a/render-test/main.cpp +++ b/render-test/main.cpp @@ -3,13 +3,11 @@ #include #include -#include "filesystem.hpp" #include "metadata.hpp" #include "parser.hpp" #include "runner.hpp" #include -#include #define ANSI_COLOR_RED "\x1b[31m" #define ANSI_COLOR_GREEN "\x1b[32m" @@ -36,49 +34,18 @@ void operator delete(void* ptr, size_t) noexcept { AllocationIndex::deallocate(ptr); } -namespace { - -TestPaths makeTestPaths(mbgl::filesystem::path stylePath) { - std::vector expectations{ stylePath }; - expectations.front().remove_filename(); - - const static std::regex regex{ TestRunner::getBasePath() }; - for (const std::string& path : TestRunner::getPlatformExpectationsPaths()) { - expectations.emplace_back(std::regex_replace(expectations.front().string(), regex, path)); - assert(!expectations.back().empty()); - } - - return { - std::move(stylePath), - std::move(expectations) - }; -} - -} // namespace - int main(int argc, char** argv) { bool recycleMap; bool shuffle; uint32_t seed; std::string testRootPath; - std::vector ids; + std::vector testPaths; - std::tie(recycleMap, shuffle, seed, testRootPath, ids) = parseArguments(argc, argv); + std::tie(recycleMap, shuffle, seed, testRootPath, testPaths) = parseArguments(argc, argv); const std::string::size_type rootLength = testRootPath.length(); const auto ignores = parseIgnores(); - // Recursively traverse through the test paths and collect test directories containing "style.json". - std::vector testPaths; - testPaths.reserve(ids.size()); - for (const auto& id : ids) { - for (auto& testPath : mbgl::filesystem::recursive_directory_iterator(mbgl::filesystem::path(id))) { - if (testPath.path().filename() == "style.json") { - testPaths.emplace_back(makeTestPaths(testPath)); - } - } - } - if (shuffle) { printf(ANSI_COLOR_YELLOW "Shuffle seed: %d" ANSI_COLOR_RESET "\n", seed); diff --git a/render-test/parser.cpp b/render-test/parser.cpp index 4598ba3786..9b462dee72 100644 --- a/render-test/parser.cpp +++ b/render-test/parser.cpp @@ -13,8 +13,9 @@ #include #include -#include "parser.hpp" +#include "filesystem.hpp" #include "metadata.hpp" +#include "parser.hpp" #include "runner.hpp" #include @@ -162,6 +163,22 @@ mbgl::optional localizeMapboxTilesetURL(const std::string& url) { return getIntegrationPath(url, "tilesets/", regex); } +TestPaths makeTestPaths(mbgl::filesystem::path stylePath) { + std::vector expectations{ stylePath }; + expectations.front().remove_filename(); + + const static std::regex regex{ TestRunner::getBasePath() }; + for (const std::string& path : TestRunner::getPlatformExpectationsPaths()) { + expectations.emplace_back(std::regex_replace(expectations.front().string(), regex, path)); + assert(!expectations.back().empty()); + } + + return { + std::move(stylePath), + std::move(expectations) + }; +} + } // namespace JSONReply readJson(const mbgl::filesystem::path& jsonPath) { @@ -239,6 +256,8 @@ ArgumentsTuple parseArguments(int argc, char** argv) { { "seed" }); args::ValueFlag testPathValue(argumentParser, "rootPath", "Test root rootPath", { 'p', "rootPath" }); + args::ValueFlag testFilterValue(argumentParser, "filter", "Test filter regex", + { 'f', "filter" }); args::PositionalList testNameValues(argumentParser, "URL", "Test name(s)"); try { @@ -260,25 +279,50 @@ ArgumentsTuple parseArguments(int argc, char** argv) { mbgl::Log::Info(mbgl::Event::General, stream.str()); mbgl::Log::Error(mbgl::Event::General, e.what()); exit(2); + } catch (const std::regex_error& e) { + mbgl::Log::Error(mbgl::Event::General, "Invalid filter regular expression: %s", e.what()); + exit(3); } - const std::string testDefaultPath = - std::string(TEST_RUNNER_ROOT_PATH).append("/mapbox-gl-js/test/integration/render-tests"); + mbgl::filesystem::path rootPath {testPathValue ? args::get(testPathValue) : TestRunner::getBasePath()}; + if (!mbgl::filesystem::exists(rootPath)) { + mbgl::Log::Error(mbgl::Event::General, "Provided rootPath '%s' does not exist.", rootPath.string().c_str()); + exit(4); + } - std::vector ids; + std::vector paths; for (const auto& id : args::get(testNameValues)) { - ids.emplace_back(TestRunner::getBasePath() + "/" + id); + paths.emplace_back(TestRunner::getBasePath() + "/" + id); + } + + if (paths.empty()) { + paths.emplace_back(TestRunner::getBasePath()); } - if (ids.empty()) { - ids.emplace_back(TestRunner::getBasePath()); + // Recursively traverse through the test paths and collect test directories containing "style.json". + std::vector testPaths; + testPaths.reserve(paths.size()); + for (const auto& path : paths) { + if (!mbgl::filesystem::exists(path)) { + mbgl::Log::Warning(mbgl::Event::General, "Provided test folder '%s' does not exist.", path.string().c_str()); + continue; + } + for (auto& testPath : mbgl::filesystem::recursive_directory_iterator(path)) { + // Skip paths that fail regexp match. + if (testFilterValue && !std::regex_match(testPath.path().string(), args::get(testFilterValue))) { + continue; + } + if (testPath.path().filename() == "style.json") { + testPaths.emplace_back(makeTestPaths(testPath)); + } + } } return ArgumentsTuple { recycleMapFlag ? args::get(recycleMapFlag) : false, shuffleFlag ? args::get(shuffleFlag) : false, seedValue ? args::get(seedValue) : 1u, - testPathValue ? args::get(testPathValue) : testDefaultPath, - std::move(ids) + testPathValue ? args::get(testPathValue) : TestRunner::getBasePath(), + std::move(testPaths) }; } diff --git a/render-test/parser.hpp b/render-test/parser.hpp index 483089266e..94fb212944 100644 --- a/render-test/parser.hpp +++ b/render-test/parser.hpp @@ -12,7 +12,7 @@ using ErrorMessage = std::string; using JSONReply = mbgl::variant; -using ArgumentsTuple = std::tuple>; +using ArgumentsTuple = std::tuple>; JSONReply readJson(const mbgl::filesystem::path&); std::string serializeJsonValue(const mbgl::JSValue&); -- cgit v1.2.1 From ec1228242b744bd0e8fdc45b7043e18ca9defe2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Paczos?= Date: Wed, 28 Aug 2019 12:40:44 +0200 Subject: [android] invalidate foreground icon sources even when location layer is hidden --- platform/android/CHANGELOG.md | 1 + .../location/LocationLayerController.java | 4 +-- .../location/LocationLayerControllerTest.java | 35 ++++++++++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index 61ff97e10e..9d3c40b63b 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -16,6 +16,7 @@ This release changes how offline tile requests are billed — they are now bille ### Bug fixes - Fixed a rendering issue caused by all icons being treated as SDFs if an SDF and non-SDF icon were in the same layer. [#15456](https://github.com/mapbox/mapbox-gl-native/pull/15456) + - Fixed an issue where changing location's render mode when the`LocationComponent` is disable wouldn't invalidate the foreground icon when it's back enabled. [#15507](https://github.com/mapbox/mapbox-gl-native/pull/15507) ## 8.2.2 - August 23, 2019 [Changes](https://github.com/mapbox/mapbox-gl-native/compare/android-v8.2.1...android-v8.2.2) since [Mapbox Maps SDK for Android v8.2.1](https://github.com/mapbox/mapbox-gl-native/releases/tag/android-v8.2.1): diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationLayerController.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationLayerController.java index b18a7c4742..54f8ee6d1a 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationLayerController.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationLayerController.java @@ -140,11 +140,11 @@ final class LocationLayerController { } this.renderMode = renderMode; + styleForeground(options); + determineIconsSource(options); if (!isHidden) { - styleForeground(options); show(); } - determineIconsSource(options); internalRenderModeChangedListener.onRenderModeChanged(renderMode); } diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationLayerControllerTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationLayerControllerTest.java index 0fbc47df55..ed2d015d85 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationLayerControllerTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationLayerControllerTest.java @@ -632,6 +632,41 @@ public class LocationLayerControllerTest { verify(internalRenderModeChangedListener, times(1)).onRenderModeChanged(RenderMode.GPS); } + @Test + public void layerHidden_renderModeChanged_layerShown_foregroundIconUpdated() { + OnRenderModeChangedListener internalRenderModeChangedListener = mock(OnRenderModeChangedListener.class); + LayerSourceProvider sourceProvider = buildLayerProvider(); + when(sourceProvider.generateSource(any(Feature.class))).thenReturn(mock(GeoJsonSource.class)); + LocationComponentOptions options = mock(LocationComponentOptions.class); + int drawableResId = 123; + int tintColor = 456; + when(options.foregroundDrawable()).thenReturn(drawableResId); + when(options.foregroundTintColor()).thenReturn(tintColor); + LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class); + Bitmap bitmap = mock(Bitmap.class); + when(bitmapProvider.generateBitmap(drawableResId, tintColor)).thenReturn(bitmap); + + LocationLayerController controller = + new LocationLayerController(mapboxMap, mapboxMap.getStyle(), sourceProvider, buildFeatureProvider(options), + bitmapProvider, options, internalRenderModeChangedListener); + + verify(style).addImage(FOREGROUND_ICON, bitmap); + + int drawableGpsResId = 789; + when(options.gpsDrawable()).thenReturn(drawableGpsResId); + + Bitmap bitmapGps = mock(Bitmap.class); + when(bitmapProvider.generateBitmap(drawableGpsResId, tintColor)).thenReturn(bitmapGps); + + controller.hide(); + + controller.setRenderMode(RenderMode.GPS); + + controller.show(); + + verify(style).addImage(FOREGROUND_ICON, bitmapGps); + } + private LayerFeatureProvider buildFeatureProvider(@NonNull LocationComponentOptions options) { LayerFeatureProvider provider = mock(LayerFeatureProvider.class); when(provider.generateLocationFeature(null, options)).thenReturn(mock(Feature.class)); -- cgit v1.2.1 From 6998e9a8ed5a4055e61a7be7414efd055bc1d7a5 Mon Sep 17 00:00:00 2001 From: Jordan Kiley Date: Wed, 28 Aug 2019 14:44:40 -0700 Subject: [ios] Prepare v5.3.0 release (#15516) --- platform/ios/CHANGELOG.md | 4 ++-- platform/ios/Mapbox-iOS-SDK-snapshot-dynamic.podspec | 2 +- platform/ios/Mapbox-iOS-SDK-stripped.podspec | 2 +- platform/ios/Mapbox-iOS-SDK.podspec | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 24e6330286..98dc2111b0 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -6,9 +6,8 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Fixed an issue that caused the tilt gesture to trigger too easily and conflict with pinch or pan gestures. ([#15349](https://github.com/mapbox/mapbox-gl-native/pull/15349)) * Fixed a bug with annotation view positions after camera transitions. ([#15122](https://github.com/mapbox/mapbox-gl-native/pull/15122/)) -* Fixed a rendering issue that non-SDF icon would be treated as SDF icon if they are in the same layer. ([#15456](https://github.com/mapbox/mapbox-gl-native/pull/15456)) -## 5.3.0 +## 5.3.0 - August 28, 2019 This release changes how offline tile requests are billed — they are now billed on a pay-as-you-go basis and all developers are able raise the offline tile limit for their users. Offline requests were previously exempt from monthly active user (MAU) billing and increasing the offline per-user tile limit to more than 6,000 tiles required the purchase of an enterprise license. By upgrading to this release, you are opting into the changes outlined in [this blog post](https://blog.mapbox.com/offline-maps-for-all-bb0fc51827be) and [#15380](https://github.com/mapbox/mapbox-gl-native/pull/15380). @@ -21,6 +20,7 @@ This release changes how offline tile requests are billed — they are now bill * Added the `MGLSymbolStyleLayer.textWritingModes` layout property. This property can be set to `MGLTextWritingModeHorizontal` or `MGLTextWritingModeVertical`. ([#14932](https://github.com/mapbox/mapbox-gl-native/pull/14932)) * Fixed rendering and collision detection issues with using `MGLSymbolStyleLayer.textVariableAnchor` and `MGLSymbolStyleLayer.iconTextFit` properties on the same layer. ([#15367](https://github.com/mapbox/mapbox-gl-native/pull/15367)) * Fixed symbol overlap when zooming out quickly. ([15416](https://github.com/mapbox/mapbox-gl-native/pull/15416)) +* Fixed a rendering issue where non-SDF icons would be treated as SDF icons if they are in the same layer. ([#15456](https://github.com/mapbox/mapbox-gl-native/pull/15456)) ### Other changes diff --git a/platform/ios/Mapbox-iOS-SDK-snapshot-dynamic.podspec b/platform/ios/Mapbox-iOS-SDK-snapshot-dynamic.podspec index edd37c1a9c..4a781ba688 100644 --- a/platform/ios/Mapbox-iOS-SDK-snapshot-dynamic.podspec +++ b/platform/ios/Mapbox-iOS-SDK-snapshot-dynamic.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '5.3.0-beta.1' + version = '5.3.0' m.name = 'Mapbox-iOS-SDK-snapshot-dynamic' m.version = "#{version}-snapshot" diff --git a/platform/ios/Mapbox-iOS-SDK-stripped.podspec b/platform/ios/Mapbox-iOS-SDK-stripped.podspec index fcf234adde..3d37e26c69 100644 --- a/platform/ios/Mapbox-iOS-SDK-stripped.podspec +++ b/platform/ios/Mapbox-iOS-SDK-stripped.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '5.3.0-beta.1' + version = '5.3.0' m.name = 'Mapbox-iOS-SDK-stripped' m.version = "#{version}-stripped" diff --git a/platform/ios/Mapbox-iOS-SDK.podspec b/platform/ios/Mapbox-iOS-SDK.podspec index 46cbabcf4d..2901dd6720 100644 --- a/platform/ios/Mapbox-iOS-SDK.podspec +++ b/platform/ios/Mapbox-iOS-SDK.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '5.3.0-beta.1' + version = '5.3.0' m.name = 'Mapbox-iOS-SDK' m.version = version -- cgit v1.2.1 From 6a4e431dbef4c036b5b27b9972caea70bfc269fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Paczos?= Date: Wed, 28 Aug 2019 16:44:02 +0200 Subject: [android] do not clear native layers and sources when style is reloading This prevents unnecessary layers blink when the same components are re-added after the style reload. --- .../mapbox/mapboxsdk/location/LocationComponent.java | 2 +- .../src/main/java/com/mapbox/mapboxsdk/maps/Style.java | 2 -- .../test/java/com/mapbox/mapboxsdk/maps/StyleTest.kt | 18 ------------------ 3 files changed, 1 insertion(+), 21 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponent.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponent.java index 5b2dcd8554..46de3673e4 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponent.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponent.java @@ -1182,7 +1182,6 @@ public final class LocationComponent { } isLayerReady = false; - locationLayerController.hide(); staleStateManager.onStop(); if (compassEngine != null) { updateCompassListenerState(false); @@ -1289,6 +1288,7 @@ public final class LocationComponent { private void disableLocationComponent() { isEnabled = false; + locationLayerController.hide(); onLocationLayerStop(); } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Style.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Style.java index a707ab13da..b21186050b 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Style.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Style.java @@ -523,14 +523,12 @@ public class Style { for (Layer layer : layers.values()) { if (layer != null) { layer.setDetached(); - nativeMap.removeLayer(layer); } } for (Source source : sources.values()) { if (source != null) { source.setDetached(); - nativeMap.removeSource(source); } } diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/StyleTest.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/StyleTest.kt index d618f17500..a5070ae5c0 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/StyleTest.kt +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/StyleTest.kt @@ -405,22 +405,4 @@ class StyleTest { Assert.assertEquals("Layer that failed to be added shouldn't be cached", layer1, mapboxMap.style!!.getLayer("layer1")) } } - - @Test - fun testClearRemovesSourcesFirst() { - val source1 = mockk(relaxed = true) - every { source1.id } returns "source1" - val layer1 = mockk(relaxed = true) - every { layer1.id } returns "layer1" - - val builder = Style.Builder().withLayer(layer1).withSource(source1) - mapboxMap.setStyle(builder) - mapboxMap.notifyStyleLoaded() - mapboxMap.setStyle(Style.MAPBOX_STREETS) - - verifyOrder { - nativeMapView.removeLayer(layer1) - nativeMapView.removeSource(source1) - } - } } \ No newline at end of file -- cgit v1.2.1 From e5c219d0356d45a5339c027297e441fd6cb96c05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Paczos?= Date: Wed, 21 Aug 2019 18:15:38 +0200 Subject: [core] make padding optional when camera position is requested. When padding is not provided, the current one that's cached in the TransformState is going to be returned. --- include/mbgl/map/map.hpp | 2 +- src/mbgl/map/map.cpp | 2 +- src/mbgl/map/transform.cpp | 2 +- src/mbgl/map/transform.hpp | 2 +- src/mbgl/map/transform_state.cpp | 4 ++-- src/mbgl/map/transform_state.hpp | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp index b1d4b14e4f..ac0a398d25 100644 --- a/include/mbgl/map/map.hpp +++ b/include/mbgl/map/map.hpp @@ -60,7 +60,7 @@ public: bool isPanning() const; // Camera - CameraOptions getCameraOptions(const EdgeInsets& = {}) const; + CameraOptions getCameraOptions(optional = {}) const; void jumpTo(const CameraOptions&); void easeTo(const CameraOptions&, const AnimationOptions&); void flyTo(const CameraOptions&, const AnimationOptions&); diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index c35f33305c..649e0e321e 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -124,7 +124,7 @@ bool Map::isPanning() const { #pragma mark - -CameraOptions Map::getCameraOptions(const EdgeInsets& padding) const { +CameraOptions Map::getCameraOptions(optional padding) const { return impl->transform.getCameraOptions(padding); } diff --git a/src/mbgl/map/transform.cpp b/src/mbgl/map/transform.cpp index d386898c3b..925893b2d6 100644 --- a/src/mbgl/map/transform.cpp +++ b/src/mbgl/map/transform.cpp @@ -63,7 +63,7 @@ void Transform::resize(const Size size) { #pragma mark - Camera -CameraOptions Transform::getCameraOptions(const EdgeInsets& padding) const { +CameraOptions Transform::getCameraOptions(optional padding) const { return state.getCameraOptions(padding); } diff --git a/src/mbgl/map/transform.hpp b/src/mbgl/map/transform.hpp index ffeff3859c..30ce8a37a4 100644 --- a/src/mbgl/map/transform.hpp +++ b/src/mbgl/map/transform.hpp @@ -29,7 +29,7 @@ public: // Camera /** Returns the current camera options. */ - CameraOptions getCameraOptions(const EdgeInsets&) const; + CameraOptions getCameraOptions(optional) const; /** Instantaneously, synchronously applies the given camera options. */ void jumpTo(const CameraOptions&); diff --git a/src/mbgl/map/transform_state.cpp b/src/mbgl/map/transform_state.cpp index 951c5a6202..61007422cb 100644 --- a/src/mbgl/map/transform_state.cpp +++ b/src/mbgl/map/transform_state.cpp @@ -146,10 +146,10 @@ ViewportMode TransformState::getViewportMode() const { #pragma mark - Camera options -CameraOptions TransformState::getCameraOptions(const EdgeInsets& padding) const { +CameraOptions TransformState::getCameraOptions(optional padding) const { return CameraOptions() .withCenter(getLatLng()) - .withPadding(padding) + .withPadding(padding ? padding : edgeInsets) .withZoom(getZoom()) .withBearing(-bearing * util::RAD2DEG) .withPitch(pitch * util::RAD2DEG); diff --git a/src/mbgl/map/transform_state.hpp b/src/mbgl/map/transform_state.hpp index e81ebed27e..10a92187d5 100644 --- a/src/mbgl/map/transform_state.hpp +++ b/src/mbgl/map/transform_state.hpp @@ -43,7 +43,7 @@ public: // Viewport mode ViewportMode getViewportMode() const; - CameraOptions getCameraOptions(const EdgeInsets&) const; + CameraOptions getCameraOptions(optional) const; // Position LatLng getLatLng(LatLng::WrapMode = LatLng::Unwrapped) const; -- cgit v1.2.1 From becc705f05c00854ff9952925f203ed5cf829774 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Paczos?= Date: Wed, 21 Aug 2019 18:18:14 +0200 Subject: [android] expose camera padding option This removes the cached insets on the Android side, making the core TransformState the source of truth. This still leaves an option to lazily set the padding which is going to be applied only when the next camera animation is started. --- platform/android/CHANGELOG.md | 1 + .../mapbox/mapboxsdk/camera/CameraPosition.java | 97 ++++++- .../mapboxsdk/camera/CameraUpdateFactory.java | 80 +++++- .../location/LocationComponentOptions.java | 3 + .../java/com/mapbox/mapboxsdk/maps/MapboxMap.java | 16 +- .../java/com/mapbox/mapboxsdk/maps/NativeMap.java | 10 +- .../com/mapbox/mapboxsdk/maps/NativeMapView.java | 79 +++--- .../java/com/mapbox/mapboxsdk/maps/Projection.java | 22 +- .../java/com/mapbox/mapboxsdk/maps/Transform.java | 7 +- .../mapboxsdk/camera/CameraPositionTest.java | 42 +-- .../com/mapbox/mapboxsdk/maps/TransformTest.kt | 6 +- .../mapboxsdk/camera/CameraUpdateFactoryTest.kt | 253 ++++++++++------- .../com/mapbox/mapboxsdk/maps/NativeMapViewTest.kt | 38 ++- .../testapp/camera/CameraAnimateTest.java | 296 +------------------- .../mapboxsdk/testapp/camera/CameraEaseTest.java | 290 +------------------ .../mapboxsdk/testapp/camera/CameraMoveTest.java | 292 +------------------- .../mapboxsdk/testapp/camera/CameraTest.java | 307 +++++++++++++++++++++ .../mapboxsdk/testapp/utils/TestConstants.java | 1 + platform/android/src/map/camera_position.cpp | 22 +- platform/android/src/map/camera_position.hpp | 4 +- platform/android/src/native_map_view.cpp | 61 ++-- platform/android/src/native_map_view.hpp | 13 +- .../android/src/snapshotter/map_snapshotter.cpp | 4 +- 23 files changed, 832 insertions(+), 1112 deletions(-) create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraTest.java diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index 9d3c40b63b..5721df229a 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -5,6 +5,7 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to ## master ### Features - Introduce `clusterProperties` option for aggregated cluster properties. [#15425](https://github.com/mapbox/mapbox-gl-native/pull/15425) + - Expose the `CameraPosition#padding` field and associated utility camera position builders. This gives a choice to set a persisting map padding immediately during a transition instead of setting it lazily `MapboxMap#setPadding`, which required scheduling additional transition to be applied. This also deprecates `MapboxMap#setPadding` as there should be no need for a lazy padding setter. [#15444](https://github.com/mapbox/mapbox-gl-native/pull/15444) ## 8.3.0 - August 28, 2019 [Changes](https://github.com/mapbox/mapbox-gl-native/compare/android-v8.3.0-beta.1...android-v8.3.0) since [Mapbox Maps SDK for Android v8.3.0-beta.1](https://github.com/mapbox/mapbox-gl-native/releases/tag/android-v8.3.0-beta.1): diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java index f44c0f1904..e2341029ff 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java @@ -5,20 +5,23 @@ import android.os.Parcel; import android.os.Parcelable; import android.support.annotation.FloatRange; import android.support.annotation.Keep; - import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.annotation.Size; + import com.mapbox.mapboxsdk.R; import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.utils.MathUtils; +import java.util.Arrays; + /** * Resembles the position, angle, zoom and tilt of the user's viewpoint. */ public final class CameraPosition implements Parcelable { - public static final CameraPosition DEFAULT = new CameraPosition(new LatLng(), 0, 0, 0); + public static final CameraPosition DEFAULT = new CameraPosition(new LatLng(), 0, 0, 0, new double[] {0, 0, 0, 0}); public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @@ -27,7 +30,9 @@ public final class CameraPosition implements Parcelable { LatLng target = in.readParcelable(LatLng.class.getClassLoader()); double tilt = in.readDouble(); double zoom = in.readDouble(); - return new CameraPosition(target, zoom, tilt, bearing); + double[] padding = new double[4]; + in.readDoubleArray(padding); + return new CameraPosition(target, zoom, tilt, bearing, padding); } public CameraPosition[] newArray(int size) { @@ -49,18 +54,25 @@ public final class CameraPosition implements Parcelable { /** * The angle, in degrees, of the camera angle from the nadir (directly facing the Earth). - * See tilt(float) for details of restrictions on the range of values. + * See {@link Builder#tilt(double)} for details of restrictions on the range of values. */ @Keep public final double tilt; /** - * Zoom level near the center of the screen. See zoom(float) for the definition of the camera's + * Zoom level near the center of the screen. See {@link Builder#zoom(double)} for the definition of the camera's * zoom level. */ @Keep public final double zoom; + /** + * Padding in pixels. Specified in left, top, right, bottom order. + * See {@link Builder#padding(double[])} for the definition of the camera's padding. + */ + @Keep + public final double[] padding; + /** * Constructs a CameraPosition. * @@ -73,13 +85,34 @@ public final class CameraPosition implements Parcelable { * exclusive. * @throws NullPointerException if target is null * @throws IllegalArgumentException if tilt is outside the range of 0 to 90 degrees inclusive. + * @deprecated use {@link CameraPosition#CameraPosition(LatLng, double, double, double, double[])} instead. */ - @Keep + @Deprecated CameraPosition(LatLng target, double zoom, double tilt, double bearing) { + this(target, zoom, tilt, bearing, null); + } + + /** + * Constructs a CameraPosition. + * + * @param target The target location to align with the center of the screen. + * @param zoom Zoom level at target. See zoom(float) for details of restrictions. + * @param tilt The camera angle, in degrees, from the nadir (directly down). See tilt(float) + * for details of restrictions. + * @param bearing Direction that the camera is pointing in, in degrees clockwise from north. + * This value will be normalized to be within 0 degrees inclusive and 360 degrees + * exclusive. + * @param padding Padding in pixels. Specified in left, top, right, bottom order. + * @throws NullPointerException if target is null + * @throws IllegalArgumentException if tilt is outside the range of 0 to 90 degrees inclusive. + */ + @Keep + CameraPosition(LatLng target, double zoom, double tilt, double bearing, double[] padding) { this.target = target; this.bearing = bearing; this.tilt = tilt; this.zoom = zoom; + this.padding = padding; } /** @@ -106,6 +139,7 @@ public final class CameraPosition implements Parcelable { out.writeParcelable(target, flags); out.writeDouble(tilt); out.writeDouble(zoom); + out.writeDoubleArray(padding); } /** @@ -115,7 +149,8 @@ public final class CameraPosition implements Parcelable { */ @Override public String toString() { - return "Target: " + target + ", Zoom:" + zoom + ", Bearing:" + bearing + ", Tilt:" + tilt; + return "Target: " + target + ", Zoom:" + zoom + ", Bearing:" + bearing + ", Tilt:" + tilt + + ", Padding:" + Arrays.toString(padding); } /** @@ -145,6 +180,8 @@ public final class CameraPosition implements Parcelable { return false; } else if (bearing != cameraPosition.bearing) { return false; + } else if (!Arrays.equals(padding, cameraPosition.padding)) { + return false; } return true; } @@ -159,8 +196,16 @@ public final class CameraPosition implements Parcelable { */ @Override public int hashCode() { - int result = 1; + int result; + long temp; + temp = Double.doubleToLongBits(bearing); + result = (int) (temp ^ (temp >>> 32)); result = 31 * result + (target != null ? target.hashCode() : 0); + temp = Double.doubleToLongBits(tilt); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(zoom); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + result = 31 * result + Arrays.hashCode(padding); return result; } @@ -174,6 +219,7 @@ public final class CameraPosition implements Parcelable { private LatLng target = null; private double tilt = -1; private double zoom = -1; + private double[] padding = null; /** * Create an empty builder. @@ -194,6 +240,7 @@ public final class CameraPosition implements Parcelable { this.target = previous.target; this.tilt = previous.tilt; this.zoom = previous.zoom; + this.padding = previous.padding; } } @@ -226,6 +273,7 @@ public final class CameraPosition implements Parcelable { target = update.getTarget(); tilt = update.getTilt(); zoom = update.getZoom(); + padding = update.getPadding(); } } @@ -306,13 +354,44 @@ public final class CameraPosition implements Parcelable { return this; } + /** + * Padding in pixels that shifts the viewport by the specified amount. + * Applied padding is going to persist and impact following camera transformations. + *

+ * Specified in left, top, right, bottom order. + *

+ * + * @param padding Camera padding + * @return this + */ + @NonNull + public Builder padding(@Size(4) double[] padding) { + this.padding = padding; + return this; + } + + /** + * Padding in pixels that shifts the viewport by the specified amount. + * Applied padding is going to persist and impact following camera transformations. + *

+ * Specified in left, top, right, bottom order. + *

+ * + * @return this + */ + @NonNull + public Builder padding(double left, double top, double right, double bottom) { + this.padding = new double[] {left, top, right, bottom}; + return this; + } + /** * Builds the CameraPosition. * * @return CameraPosition */ public CameraPosition build() { - return new CameraPosition(target, zoom, tilt, bearing); + return new CameraPosition(target, zoom, tilt, bearing, padding); } } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactory.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactory.java index 16285468ee..b611456f43 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactory.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactory.java @@ -5,6 +5,7 @@ import android.graphics.PointF; import android.support.annotation.IntDef; import android.support.annotation.NonNull; import android.support.annotation.Nullable; + import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.geometry.LatLngBounds; import com.mapbox.mapboxsdk.maps.MapboxMap; @@ -28,7 +29,7 @@ public final class CameraUpdateFactory { */ public static CameraUpdate newCameraPosition(@NonNull CameraPosition cameraPosition) { return new CameraPositionUpdate(cameraPosition.bearing, cameraPosition.target, cameraPosition.tilt, - cameraPosition.zoom); + cameraPosition.zoom, cameraPosition.padding); } /** @@ -39,7 +40,7 @@ public final class CameraUpdateFactory { * @return CameraUpdate Final Camera Position */ public static CameraUpdate newLatLng(@NonNull LatLng latLng) { - return new CameraPositionUpdate(-1, latLng, -1, -1); + return new CameraPositionUpdate(-1, latLng, -1, -1, null); } /** @@ -48,6 +49,7 @@ public final class CameraUpdateFactory { * current camera position bearing and tilt values. *

* You can specify padding, in order to inset the bounding box from the map view's edges. + * The padding will not persist and impact following camera transformations. *

* * @param bounds Bounds to match Camera position with @@ -64,6 +66,7 @@ public final class CameraUpdateFactory { * provided bearing and tilt values. *

* You can specify padding, in order to inset the bounding box from the map view's edges. + * The padding will not persist and impact following camera transformations. *

* * @param bounds Bounds to match Camera position with @@ -82,6 +85,7 @@ public final class CameraUpdateFactory { * current camera position bearing and tilt values. *

* You can specify padding, in order to inset the bounding box from the map view's edges. + * The padding will not persist and impact following camera transformations. *

* * @param bounds Bounds to base the Camera position out of @@ -102,6 +106,7 @@ public final class CameraUpdateFactory { * provided bearing and tilt values. *

* You can specify padding, in order to inset the bounding box from the map view's edges. + * The padding will not persist and impact following camera transformations. *

* * @param bounds Bounds to base the Camera position out of @@ -120,14 +125,31 @@ public final class CameraUpdateFactory { /** * Returns a CameraUpdate that moves the center of the screen to a latitude and longitude - * specified by a LatLng object, and moves to the given zoom level. + * specified by a LatLng object taking the specified padding into account. * * @param latLng Target location to change to * @param zoom Zoom level to change to * @return CameraUpdate Final Camera Position */ public static CameraUpdate newLatLngZoom(@NonNull LatLng latLng, double zoom) { - return new CameraPositionUpdate(-1, latLng, -1, zoom); + return new CameraPositionUpdate(-1, latLng, -1, zoom, null); + } + + /** + * Returns a CameraUpdate that moves the center of the screen to a latitude and longitude + * specified by a LatLng object, and moves to the given zoom level. + * Applied padding is going to persist and impact following camera transformations. + * + * @param latLng Target location to change to + * @param left Left padding + * @param top Top padding + * @param right Right padding + * @param bottom Bottom padding + * @return CameraUpdate Final Camera Position + */ + public static CameraUpdate newLatLngPadding(@NonNull LatLng latLng, + double left, double top, double right, double bottom) { + return new CameraPositionUpdate(-1, latLng, -1, -1, new double[] {left, top, right, bottom}); } /** @@ -188,7 +210,7 @@ public final class CameraUpdateFactory { * @return CameraUpdate Final Camera Position */ public static CameraUpdate bearingTo(double bearing) { - return new CameraPositionUpdate(bearing, null, -1, -1); + return new CameraPositionUpdate(bearing, null, -1, -1, null); } /** @@ -198,7 +220,34 @@ public final class CameraUpdateFactory { * @return CameraUpdate Final Camera Position */ public static CameraUpdate tiltTo(double tilt) { - return new CameraPositionUpdate(-1, null, tilt, -1); + return new CameraPositionUpdate(-1, null, tilt, -1, null); + } + + /** + * Returns a CameraUpdate that when animated changes the camera padding. + * Applied padding is going to persist and impact following camera transformations. + *

+ * Specified in left, top, right, bottom order. + *

+ * + * @param padding Padding to change to + * @return CameraUpdate Final Camera Position + */ + public static CameraUpdate paddingTo(double[] padding) { + return new CameraPositionUpdate(-1, null, -1, -1, padding); + } + + /** + * Returns a CameraUpdate that when animated changes the camera padding. + * Applied padding is going to persist and impact following camera transformations. + *

+ * Specified in left, top, right, bottom order. + *

+ * + * @return CameraUpdate Final Camera Position + */ + public static CameraUpdate paddingTo(double left, double top, double right, double bottom) { + return paddingTo(new double[] {left, top, right, bottom}); } // @@ -211,12 +260,14 @@ public final class CameraUpdateFactory { private final LatLng target; private final double tilt; private final double zoom; + private final double[] padding; - CameraPositionUpdate(double bearing, LatLng target, double tilt, double zoom) { + CameraPositionUpdate(double bearing, LatLng target, double tilt, double zoom, double[] padding) { this.bearing = bearing; this.target = target; this.tilt = tilt; this.zoom = zoom; + this.padding = padding; } public LatLng getTarget() { @@ -235,10 +286,14 @@ public final class CameraUpdateFactory { return zoom; } + public double[] getPadding() { + return padding; + } + @Override public CameraPosition getCameraPosition(@NonNull MapboxMap mapboxMap) { - CameraPosition previousPosition = mapboxMap.getCameraPosition(); if (target == null) { + CameraPosition previousPosition = mapboxMap.getCameraPosition(); return new CameraPosition.Builder(this) .target(previousPosition.target) .build(); @@ -247,7 +302,7 @@ public final class CameraUpdateFactory { } @Override - public boolean equals(@Nullable Object o) { + public boolean equals(Object o) { if (this == o) { return true; } @@ -266,7 +321,10 @@ public final class CameraUpdateFactory { if (Double.compare(that.zoom, zoom) != 0) { return false; } - return target != null ? target.equals(that.target) : that.target == null; + if (target != null ? !target.equals(that.target) : that.target != null) { + return false; + } + return Arrays.equals(padding, that.padding); } @Override @@ -280,6 +338,7 @@ public final class CameraUpdateFactory { result = 31 * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(zoom); result = 31 * result + (int) (temp ^ (temp >>> 32)); + result = 31 * result + Arrays.hashCode(padding); return result; } @@ -290,6 +349,7 @@ public final class CameraUpdateFactory { + ", target=" + target + ", tilt=" + tilt + ", zoom=" + zoom + + ", padding=" + Arrays.toString(padding) + '}'; } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentOptions.java index 8ab03e7acd..48c89622cd 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentOptions.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentOptions.java @@ -1530,8 +1530,11 @@ public class LocationComponentOptions implements Parcelable { *

* * @param padding The margins for the map in pixels (left, top, right, bottom). + * @deprecated Use {@link CameraPosition.Builder#padding(double, double, double, double)} + * or {@link CameraUpdateFactory#paddingTo(double, double, double, double)} instead. */ @NonNull + @Deprecated public LocationComponentOptions.Builder padding(@Nullable int[] padding) { if (padding == null) { throw new NullPointerException("Null padding"); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java index 8e1019b379..acd5093dad 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java @@ -1540,24 +1540,38 @@ public final class MapboxMap { * frame from the viewport. For instance, if the only the top edge is inset, the * map center is effectively shifted downward. *

+ *

+ * This method sets the padding "lazily". + * This means that the padding is going to be applied with the next camera transformation. + * To apply the padding immediately use {@link CameraPosition.Builder#padding(double, double, double, double)} + * or {@link CameraUpdateFactory#paddingTo(double, double, double, double)}. + *

* * @param left The left margin in pixels. * @param top The top margin in pixels. * @param right The right margin in pixels. * @param bottom The bottom margin in pixels. + * @deprecated Use {@link CameraPosition.Builder#padding(double, double, double, double)} + * or {@link CameraUpdateFactory#paddingTo(double, double, double, double)} instead. */ + @Deprecated public void setPadding(int left, int top, int right, int bottom) { + // TODO padding should be passed as doubles projection.setContentPadding(new int[] {left, top, right, bottom}); uiSettings.invalidate(); } /** - * Returns the current configured content padding on map view. + * Returns the current configured content padding on map view. This might return the currently visible padding + * or the padding cached but not yet applied by {@link #setPadding(int, int, int, int)}. * * @return An array with length 4 in the LTRB order. + * @deprecated Use {@link CameraPosition#padding} instead. */ + @Deprecated @NonNull public int[] getPadding() { + // TODO this should return double[] (semver major change) return projection.getContentPadding(); } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMap.java index da70013713..7f3017c7ae 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMap.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMap.java @@ -41,12 +41,12 @@ interface NativeMap { // Camera API // - void jumpTo(@NonNull LatLng center, double zoom, double pitch, double bearing); + void jumpTo(@NonNull LatLng center, double zoom, double pitch, double bearing, double[] padding); - void easeTo(@NonNull LatLng center, double zoom, double bearing, double pitch, long duration, + void easeTo(@NonNull LatLng center, double zoom, double bearing, double pitch, double[] padding, long duration, boolean easingInterpolator); - void flyTo(@NonNull LatLng center, double zoom, double bearing, double pitch, long duration); + void flyTo(@NonNull LatLng center, double zoom, double bearing, double pitch, double[] padding, long duration); void moveBy(double deltaX, double deltaY, long duration); @@ -161,9 +161,9 @@ interface NativeMap { // Content padding API // - void setContentPadding(float[] padding); + void setContentPadding(double[] padding); - float[] getContentPadding(); + double[] getContentPadding(); // // Query API diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java index fc68a408fa..8496160c7e 100755 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java @@ -10,6 +10,7 @@ import android.support.annotation.Keep; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.text.TextUtils; + import com.mapbox.geojson.Feature; import com.mapbox.geojson.Geometry; import com.mapbox.mapboxsdk.LibraryLoader; @@ -68,6 +69,10 @@ final class NativeMapView implements NativeMap { // Flag to indicate destroy was called private boolean destroyed = false; + // Cached to enable lazily set padding. + // Whenever an animation is schedule, this value is cleared and the source of truth becomes the core transform state. + private double[] edgeInsets; + // Holds the pointer to JNI NativeMapView @Keep private long nativePtr = 0; @@ -241,7 +246,8 @@ final class NativeMapView implements NativeMap { if (checkState("setLatLng")) { return; } - nativeSetLatLng(latLng.getLatitude(), latLng.getLongitude(), duration); + nativeSetLatLng(latLng.getLatitude(), latLng.getLongitude(), + getAnimationPaddingAndClearCachedInsets(null), duration); } @Override @@ -374,30 +380,20 @@ final class NativeMapView implements NativeMap { } @Override - public void setContentPadding(float[] padding) { + public void setContentPadding(double[] padding) { if (checkState("setContentPadding")) { return; } - // TopLeftBottomRight - nativeSetContentPadding( - padding[1] / pixelRatio, - padding[0] / pixelRatio, - padding[3] / pixelRatio, - padding[2] / pixelRatio); + this.edgeInsets = padding; } @Override - public float[] getContentPadding() { + public double[] getContentPadding() { if (checkState("getContentPadding")) { - return new float[] {0, 0, 0, 0}; + return new double[] {0, 0, 0, 0}; } - float[] topLeftBottomRight = nativeGetContentPadding(); - return new float[] { - topLeftBottomRight[1] * pixelRatio, - topLeftBottomRight[0] * pixelRatio, - topLeftBottomRight[3] * pixelRatio, - topLeftBottomRight[2] * pixelRatio - }; + // if cached insets are not applied yet, return them, otherwise, get the padding from the camera + return edgeInsets != null ? edgeInsets : getCameraPosition().padding; } @Override @@ -672,29 +668,31 @@ final class NativeMapView implements NativeMap { } @Override - public void jumpTo(@NonNull LatLng center, double zoom, double pitch, double angle) { + public void jumpTo(@NonNull LatLng center, double zoom, double pitch, double angle, double[] padding) { if (checkState("jumpTo")) { return; } - nativeJumpTo(angle, center.getLatitude(), center.getLongitude(), pitch, zoom); + nativeJumpTo(angle, center.getLatitude(), center.getLongitude(), pitch, zoom, + getAnimationPaddingAndClearCachedInsets(padding)); } @Override - public void easeTo(@NonNull LatLng center, double zoom, double angle, double pitch, long duration, + public void easeTo(@NonNull LatLng center, double zoom, double angle, double pitch, double[] padding, long duration, boolean easingInterpolator) { if (checkState("easeTo")) { return; } nativeEaseTo(angle, center.getLatitude(), center.getLongitude(), duration, pitch, zoom, - easingInterpolator); + getAnimationPaddingAndClearCachedInsets(padding), easingInterpolator); } @Override - public void flyTo(@NonNull LatLng center, double zoom, double angle, double pitch, long duration) { + public void flyTo(@NonNull LatLng center, double zoom, double angle, double pitch, double[] padding, long duration) { if (checkState("flyTo")) { return; } - nativeFlyTo(angle, center.getLatitude(), center.getLongitude(), duration, pitch, zoom); + nativeFlyTo(angle, center.getLatitude(), center.getLongitude(), duration, pitch, zoom, + getAnimationPaddingAndClearCachedInsets(padding)); } @Override @@ -703,7 +701,11 @@ final class NativeMapView implements NativeMap { if (checkState("getCameraValues")) { return new CameraPosition.Builder().build(); } - return nativeGetCameraPosition(); + if (edgeInsets != null) { + return new CameraPosition.Builder(nativeGetCameraPosition()).padding(edgeInsets).build(); + } else { + return nativeGetCameraPosition(); + } } @Override @@ -1126,7 +1128,7 @@ final class NativeMapView implements NativeMap { private native void nativeMoveBy(double dx, double dy, long duration); @Keep - private native void nativeSetLatLng(double latitude, double longitude, long duration); + private native void nativeSetLatLng(double latitude, double longitude, double[] padding, long duration); @NonNull @Keep @@ -1175,12 +1177,6 @@ final class NativeMapView implements NativeMap { @Keep private native void nativeRotateBy(double sx, double sy, double ex, double ey, long duration); - @Keep - private native void nativeSetContentPadding(float top, float left, float bottom, float right); - - @Keep - private native float[] nativeGetContentPadding(); - @Keep private native void nativeSetBearing(double degrees, long duration); @@ -1270,16 +1266,17 @@ final class NativeMapView implements NativeMap { private native double nativeGetTopOffsetPixelsForAnnotationSymbol(String symbolName); @Keep - private native void nativeJumpTo(double angle, double latitude, double longitude, double pitch, double zoom); + private native void nativeJumpTo(double angle, double latitude, double longitude, double pitch, double zoom, + double[] padding); @Keep private native void nativeEaseTo(double angle, double latitude, double longitude, - long duration, double pitch, double zoom, + long duration, double pitch, double zoom, double[] padding, boolean easingInterpolator); @Keep private native void nativeFlyTo(double angle, double latitude, double longitude, - long duration, double pitch, double zoom); + long duration, double pitch, double zoom, double[] padding); @NonNull @Keep @@ -1437,6 +1434,20 @@ final class NativeMapView implements NativeMap { return destroyed; } + private double[] getAnimationPaddingAndClearCachedInsets(double[] providedPadding) { + if (providedPadding == null) { + providedPadding = this.edgeInsets; + } + this.edgeInsets = null; + return providedPadding == null ? null : + new double[] { + providedPadding[1] / pixelRatio, + providedPadding[0] / pixelRatio, + providedPadding[3] / pixelRatio, + providedPadding[2] / pixelRatio + }; + } + public interface ViewCallback { @Nullable Bitmap getViewContent(); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java index 8ed003b821..76395f9564 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java @@ -3,6 +3,7 @@ package com.mapbox.mapboxsdk.maps; import android.graphics.PointF; import android.support.annotation.FloatRange; import android.support.annotation.NonNull; + import com.mapbox.geojson.Point; import com.mapbox.mapboxsdk.constants.GeometryConstants; import com.mapbox.mapboxsdk.geometry.LatLng; @@ -24,7 +25,6 @@ public class Projection { private final NativeMap nativeMapView; @NonNull private final MapView mapView; - private int[] contentPadding = new int[] {0, 0, 0, 0}; Projection(@NonNull NativeMap nativeMapView, @NonNull MapView mapView) { this.nativeMapView = nativeMapView; @@ -32,8 +32,7 @@ public class Projection { } void setContentPadding(int[] contentPadding) { - this.contentPadding = contentPadding; - float[] output = new float[contentPadding.length]; + double[] output = new double[contentPadding.length]; for (int i = 0; i < contentPadding.length; i++) { output[i] = contentPadding[i]; } @@ -41,11 +40,15 @@ public class Projection { } int[] getContentPadding() { - return contentPadding; + double[] padding = nativeMapView.getCameraPosition().padding; + return new int[] {(int) padding[0], (int) padding[1], (int) padding[2], (int) padding[3]}; } + /** + * @deprecated unused + */ + @Deprecated public void invalidateContentPadding() { - setContentPadding(contentPadding); } /** @@ -126,10 +129,11 @@ public class Projection { top = 0; bottom = mapView.getHeight(); } else { - left = (float) contentPadding[0]; - right = (float) (mapView.getWidth() - contentPadding[2]); - top = (float) contentPadding[1]; - bottom = (float) (mapView.getHeight() - contentPadding[3]); + int[] contentPadding = getContentPadding(); + left = contentPadding[0]; + right = mapView.getWidth() - contentPadding[2]; + top = contentPadding[1]; + bottom = mapView.getHeight() - contentPadding[3]; } LatLng center = fromScreenLocation(new PointF(left + (right - left) / 2, top + (bottom - top) / 2)); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java index 1961cf0450..02cd05545d 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java @@ -108,7 +108,8 @@ public final class Transform implements MapView.OnCameraDidChangeListener { if (isValidCameraPosition(cameraPosition)) { cancelTransitions(); cameraChangeDispatcher.onCameraMoveStarted(OnCameraMoveStartedListener.REASON_API_ANIMATION); - nativeMap.jumpTo(cameraPosition.target, cameraPosition.zoom, cameraPosition.tilt, cameraPosition.bearing); + nativeMap.jumpTo(cameraPosition.target, cameraPosition.zoom, cameraPosition.tilt, cameraPosition.bearing, + cameraPosition.padding); cameraChangeDispatcher.onCameraIdle(); invalidateCameraPosition(); handler.post(new Runnable() { @@ -135,7 +136,7 @@ public final class Transform implements MapView.OnCameraDidChangeListener { } mapView.addOnCameraDidChangeListener(this); nativeMap.easeTo(cameraPosition.target, cameraPosition.zoom, cameraPosition.bearing, cameraPosition.tilt, - durationMs, easingInterpolator); + cameraPosition.padding, durationMs, easingInterpolator); } } @@ -155,7 +156,7 @@ public final class Transform implements MapView.OnCameraDidChangeListener { } mapView.addOnCameraDidChangeListener(this); nativeMap.flyTo(cameraPosition.target, cameraPosition.zoom, cameraPosition.bearing, - cameraPosition.tilt, durationMs); + cameraPosition.tilt, cameraPosition.padding, durationMs); } } diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/camera/CameraPositionTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/camera/CameraPositionTest.java index 0c5f3a4be2..6974705fae 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/camera/CameraPositionTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/camera/CameraPositionTest.java @@ -1,21 +1,24 @@ package com.mapbox.mapboxsdk.camera; import android.content.res.TypedArray; -import android.os.Parcelable; +import android.os.Parcel; import com.mapbox.mapboxsdk.R; import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.utils.MockParcel; import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +@RunWith(RobolectricTestRunner.class) public class CameraPositionTest { private static final double DELTA = 1e-15; @@ -23,7 +26,7 @@ public class CameraPositionTest { @Test public void testSanity() { LatLng latLng = new LatLng(1, 2); - CameraPosition cameraPosition = new CameraPosition(latLng, 3, 4, 5); + CameraPosition cameraPosition = new CameraPosition(latLng, 3, 4, 5, new double[] {0, 500, 0, 0}); assertNotNull("cameraPosition should not be null", cameraPosition); } @@ -32,9 +35,10 @@ public class CameraPositionTest { TypedArray typedArray = null; CameraPosition cameraPosition = new CameraPosition.Builder(typedArray).build(); assertEquals("bearing should match", -1, cameraPosition.bearing, DELTA); - assertEquals("latlng should match", null, cameraPosition.target); + assertNull("latlng should be null", cameraPosition.target); assertEquals("tilt should match", -1, cameraPosition.tilt, DELTA); assertEquals("zoom should match", -1, cameraPosition.zoom, DELTA); + assertNull("padding should be null", cameraPosition.padding); } @Test @@ -63,16 +67,16 @@ public class CameraPositionTest { @Test public void testToString() { LatLng latLng = new LatLng(1, 2); - CameraPosition cameraPosition = new CameraPosition(latLng, 3, 4, 5); + CameraPosition cameraPosition = new CameraPosition(latLng, 3, 4, 5, new double[] {0, 500, 0, 0}); assertEquals("toString should match", "Target: LatLng [latitude=1.0, longitude=2.0, altitude=0.0], Zoom:3.0, " - + "Bearing:5.0, Tilt:4.0", cameraPosition.toString()); + + "Bearing:5.0, Tilt:4.0, Padding:[0.0, 500.0, 0.0, 0.0]", cameraPosition.toString()); } @Test public void testHashcode() { LatLng latLng = new LatLng(1, 2); - CameraPosition cameraPosition = new CameraPosition(latLng, 3, 4, 5); - assertEquals("hashCode should match", -1007681505, cameraPosition.hashCode()); + CameraPosition cameraPosition = new CameraPosition(latLng, 3, 4, 5, new double[] {0, 500, 0, 0}); + assertEquals("hashCode should match", -420915327, cameraPosition.hashCode()); } @Test @@ -86,11 +90,12 @@ public class CameraPositionTest { @Test public void testEquals() { LatLng latLng = new LatLng(1, 2); - CameraPosition cameraPosition = new CameraPosition(latLng, 3, 4, 5); - CameraPosition cameraPositionBearing = new CameraPosition(latLng, 3, 4, 9); - CameraPosition cameraPositionTilt = new CameraPosition(latLng, 3, 9, 5); - CameraPosition cameraPositionZoom = new CameraPosition(latLng, 9, 4, 5); - CameraPosition cameraPositionTarget = new CameraPosition(new LatLng(), 3, 4, 5); + CameraPosition cameraPosition = new CameraPosition(latLng, 3, 4, 5, new double[] {0, 500, 0, 0}); + CameraPosition cameraPositionBearing = new CameraPosition(latLng, 3, 4, 9, new double[] {0, 500, 0, 0}); + CameraPosition cameraPositionTilt = new CameraPosition(latLng, 3, 9, 5, new double[] {0, 500, 0, 0}); + CameraPosition cameraPositionZoom = new CameraPosition(latLng, 9, 4, 5, new double[] {0, 500, 0, 0}); + CameraPosition cameraPositionTarget = new CameraPosition(new LatLng(), 3, 4, 5, new double[] {0, 500, 0, 0}); + CameraPosition cameraPositionPadding = new CameraPosition(new LatLng(), 3, 4, 5, new double[] {0, 501, 0, 0}); assertEquals("cameraPosition should match itself", cameraPosition, cameraPosition); assertNotEquals("cameraPosition should not match null", null, cameraPosition); @@ -99,12 +104,17 @@ public class CameraPositionTest { assertNotEquals("cameraPosition should not match for tilt", cameraPositionTilt, cameraPosition); assertNotEquals("cameraPosition should not match for zoom", cameraPositionZoom, cameraPosition); assertNotEquals("cameraPosition should not match for target", cameraPositionTarget, cameraPosition); + assertNotEquals("cameraPosition should not match for padding", cameraPositionPadding, cameraPosition); } @Test public void testParcelable() { - CameraPosition object = new CameraPosition(new LatLng(1, 2), 3, 4, 5); - Parcelable parcelable = MockParcel.obtain(object); - assertEquals("Parcel should match original object", parcelable, object); + CameraPosition cameraPosition1 = new CameraPosition(new LatLng(1, 2), 3, 4, 5, new double[] {0, 500, 0, 0}); + Parcel parcel = Parcel.obtain(); + cameraPosition1.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + + CameraPosition cameraPosition2 = CameraPosition.CREATOR.createFromParcel(parcel); + assertEquals("Parcel should match original object", cameraPosition1, cameraPosition2); } } diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/TransformTest.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/TransformTest.kt index 4f6e915d1f..511101ecb2 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/TransformTest.kt +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/TransformTest.kt @@ -25,8 +25,8 @@ class TransformTest { every { nativeMapView.isDestroyed } returns false every { nativeMapView.cameraPosition } returns CameraPosition.DEFAULT every { nativeMapView.cancelTransitions() } answers {} - every { nativeMapView.jumpTo(any(), any(), any(), any()) } answers {} - every { nativeMapView.flyTo(any(), any(), any(), any(), any()) } answers {} + every { nativeMapView.jumpTo(any(), any(), any(), any(), any()) } answers {} + every { nativeMapView.flyTo(any(), any(), any(), any(), any(), any()) } answers {} every { nativeMapView.minZoom = any() } answers {} every { nativeMapView.maxZoom = any() } answers {} } @@ -44,7 +44,7 @@ class TransformTest { val update = CameraUpdateFactory.newCameraPosition(expected) transform.moveCamera(mapboxMap, update, callback) - verify { nativeMapView.jumpTo(target, -1.0, -1.0, -1.0) } + verify { nativeMapView.jumpTo(target, -1.0, -1.0, -1.0, null) } verify { callback.onFinish() } } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactoryTest.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactoryTest.kt index 50b9626edc..f840e970d1 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactoryTest.kt +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactoryTest.kt @@ -1,11 +1,15 @@ package com.mapbox.mapboxsdk.camera +import android.graphics.PointF +import android.support.test.annotation.UiThreadTest import android.support.test.runner.AndroidJUnit4 import com.mapbox.mapboxsdk.geometry.LatLng import com.mapbox.mapboxsdk.geometry.LatLngBounds import com.mapbox.mapboxsdk.testapp.activity.BaseTest import com.mapbox.mapboxsdk.testapp.activity.espresso.DeviceIndependentTestActivity +import org.junit.Assert import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue import org.junit.Test import org.junit.runner.RunWith @@ -17,127 +21,166 @@ class CameraUpdateFactoryTest : BaseTest() { } @Test + @UiThreadTest fun testLatLngBoundsUntiltedUnrotated() { - rule.runOnUiThread { - mapboxMap.cameraPosition = CameraPosition.Builder() - .target(LatLng(60.0, 24.0)) - .bearing(0.0) - .tilt(0.0) - .build() - - val bounds: LatLngBounds = LatLngBounds.Builder() - .include(LatLng(62.0, 26.0)) - .include(LatLng(58.0, 22.0)) - .build() - - mapboxMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0)) - - val cameraPosition = mapboxMap.cameraPosition - assertEquals("latitude should match:", 60.0, cameraPosition.target.latitude, 0.1) - assertEquals("longitude should match:", 24.0, cameraPosition.target.longitude, 0.1) - assertEquals("bearing should match:", 0.0, cameraPosition.bearing, 0.1) - assertEquals("zoom should match", 5.5, cameraPosition.zoom, 0.1) - assertEquals("tilt should match:", 0.0, cameraPosition.tilt, 0.1) - } + mapboxMap.cameraPosition = CameraPosition.Builder() + .target(LatLng(60.0, 24.0)) + .bearing(0.0) + .tilt(0.0) + .build() + + val bounds: LatLngBounds = LatLngBounds.Builder() + .include(LatLng(62.0, 26.0)) + .include(LatLng(58.0, 22.0)) + .build() + + mapboxMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0)) + + val cameraPosition = mapboxMap.cameraPosition + assertEquals("latitude should match:", 60.0, cameraPosition.target.latitude, 0.1) + assertEquals("longitude should match:", 24.0, cameraPosition.target.longitude, 0.1) + assertEquals("bearing should match:", 0.0, cameraPosition.bearing, 0.1) + assertEquals("zoom should match", 5.5, cameraPosition.zoom, 0.1) + assertEquals("tilt should match:", 0.0, cameraPosition.tilt, 0.1) } @Test + @UiThreadTest fun testLatLngBoundsTilted() { - rule.runOnUiThread { - mapboxMap.cameraPosition = CameraPosition.Builder() - .target(LatLng(60.0, 24.0)) - .bearing(0.0) - .tilt(45.0) - .build() - - val bounds: LatLngBounds = LatLngBounds.Builder() - .include(LatLng(62.0, 26.0)) - .include(LatLng(58.0, 22.0)) - .build() - - mapboxMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0)) - - val cameraPosition = mapboxMap.cameraPosition - assertEquals("latitude should match:", 60.0, cameraPosition.target.latitude, 0.1) - assertEquals("longitude should match:", 24.0, cameraPosition.target.longitude, 0.1) - assertEquals("bearing should match:", 0.0, cameraPosition.bearing, 0.1) - assertEquals("zoom should match", 6.0, cameraPosition.zoom, 0.1) - assertEquals("tilt should match:", 45.0, cameraPosition.tilt, 0.1) - } + mapboxMap.cameraPosition = CameraPosition.Builder() + .target(LatLng(60.0, 24.0)) + .bearing(0.0) + .tilt(45.0) + .build() + + val bounds: LatLngBounds = LatLngBounds.Builder() + .include(LatLng(62.0, 26.0)) + .include(LatLng(58.0, 22.0)) + .build() + + mapboxMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0)) + + val cameraPosition = mapboxMap.cameraPosition + assertEquals("latitude should match:", 60.0, cameraPosition.target.latitude, 0.1) + assertEquals("longitude should match:", 24.0, cameraPosition.target.longitude, 0.1) + assertEquals("bearing should match:", 0.0, cameraPosition.bearing, 0.1) + assertEquals("zoom should match", 6.0, cameraPosition.zoom, 0.1) + assertEquals("tilt should match:", 45.0, cameraPosition.tilt, 0.1) } @Test + @UiThreadTest fun testLatLngBoundsRotated() { - rule.runOnUiThread { - mapboxMap.cameraPosition = CameraPosition.Builder() - .target(LatLng(60.0, 24.0)) - .bearing(30.0) - .tilt(0.0) - .build() - - val bounds: LatLngBounds = LatLngBounds.Builder() - .include(LatLng(62.0, 26.0)) - .include(LatLng(58.0, 22.0)) - .build() - - mapboxMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0)) - - val cameraPosition = mapboxMap.cameraPosition - assertEquals("latitude should match:", 60.0, cameraPosition.target.latitude, 0.1) - assertEquals("longitude should match:", 24.0, cameraPosition.target.longitude, 0.1) - assertEquals("bearing should match:", 30.0, cameraPosition.bearing, 0.1) - assertEquals("zoom should match", 5.3, cameraPosition.zoom, 0.1) - assertEquals("tilt should match:", 0.0, cameraPosition.tilt, 0.1) - } + mapboxMap.cameraPosition = CameraPosition.Builder() + .target(LatLng(60.0, 24.0)) + .bearing(30.0) + .tilt(0.0) + .build() + + val bounds: LatLngBounds = LatLngBounds.Builder() + .include(LatLng(62.0, 26.0)) + .include(LatLng(58.0, 22.0)) + .build() + + mapboxMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0)) + + val cameraPosition = mapboxMap.cameraPosition + assertEquals("latitude should match:", 60.0, cameraPosition.target.latitude, 0.1) + assertEquals("longitude should match:", 24.0, cameraPosition.target.longitude, 0.1) + assertEquals("bearing should match:", 30.0, cameraPosition.bearing, 0.1) + assertEquals("zoom should match", 5.3, cameraPosition.zoom, 0.1) + assertEquals("tilt should match:", 0.0, cameraPosition.tilt, 0.1) } @Test + @UiThreadTest fun testLatLngBoundsTiltedRotated() { - rule.runOnUiThread { - mapboxMap.cameraPosition = CameraPosition.Builder() - .target(LatLng(60.0, 24.0)) - .bearing(30.0) - .tilt(45.0) - .build() - - val bounds: LatLngBounds = LatLngBounds.Builder() - .include(LatLng(62.0, 26.0)) - .include(LatLng(58.0, 22.0)) - .build() - - mapboxMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0)) - - val cameraPosition = mapboxMap.cameraPosition - assertEquals("latitude should match:", 60.0, cameraPosition.target.latitude, 0.1) - assertEquals("longitude should match:", 24.0, cameraPosition.target.longitude, 0.1) - assertEquals("bearing should match:", 30.0, cameraPosition.bearing, 0.1) - assertEquals("zoom should match", 5.6, cameraPosition.zoom, 0.1) - assertEquals("tilt should match:", 45.0, cameraPosition.tilt, 0.1) - } + mapboxMap.cameraPosition = CameraPosition.Builder() + .target(LatLng(60.0, 24.0)) + .bearing(30.0) + .tilt(45.0) + .build() + + val bounds: LatLngBounds = LatLngBounds.Builder() + .include(LatLng(62.0, 26.0)) + .include(LatLng(58.0, 22.0)) + .build() + + mapboxMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0)) + + val cameraPosition = mapboxMap.cameraPosition + assertEquals("latitude should match:", 60.0, cameraPosition.target.latitude, 0.1) + assertEquals("longitude should match:", 24.0, cameraPosition.target.longitude, 0.1) + assertEquals("bearing should match:", 30.0, cameraPosition.bearing, 0.1) + assertEquals("zoom should match", 5.6, cameraPosition.zoom, 0.1) + assertEquals("tilt should match:", 45.0, cameraPosition.tilt, 0.1) } @Test + @UiThreadTest fun testLatLngBoundsWithProvidedTiltAndRotation() { - rule.runOnUiThread { - mapboxMap.cameraPosition = CameraPosition.Builder() - .target(LatLng(60.0, 24.0)) - .bearing(0.0) - .tilt(0.0) - .build() - - val bounds: LatLngBounds = LatLngBounds.Builder() - .include(LatLng(62.0, 26.0)) - .include(LatLng(58.0, 22.0)) - .build() - - mapboxMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 30.0, 40.0, 0)) - - val cameraPosition = mapboxMap.cameraPosition - assertEquals("latitude should match:", 60.0, cameraPosition.target.latitude, 0.1) - assertEquals("longitude should match:", 24.0, cameraPosition.target.longitude, 0.1) - assertEquals("bearing should match:", 30.0, cameraPosition.bearing, 0.1) - assertEquals("zoom should match", 5.6, cameraPosition.zoom, 0.1) - assertEquals("tilt should match:", 40.0, cameraPosition.tilt, 0.1) - } + mapboxMap.cameraPosition = CameraPosition.Builder() + .target(LatLng(60.0, 24.0)) + .bearing(0.0) + .tilt(0.0) + .build() + + val bounds: LatLngBounds = LatLngBounds.Builder() + .include(LatLng(62.0, 26.0)) + .include(LatLng(58.0, 22.0)) + .build() + + mapboxMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 30.0, 40.0, 0)) + + val cameraPosition = mapboxMap.cameraPosition + assertEquals("latitude should match:", 60.0, cameraPosition.target.latitude, 0.1) + assertEquals("longitude should match:", 24.0, cameraPosition.target.longitude, 0.1) + assertEquals("bearing should match:", 30.0, cameraPosition.bearing, 0.1) + assertEquals("zoom should match", 5.6, cameraPosition.zoom, 0.1) + assertEquals("tilt should match:", 40.0, cameraPosition.tilt, 0.1) + } + + @Test + @UiThreadTest + fun withPadding_cameraInvalidated_paddingPersisting() { + val initialCameraPosition = mapboxMap.cameraPosition + val initialPoint = mapboxMap.projection.toScreenLocation(initialCameraPosition.target) + + val bottomPadding = mapView.height / 4 + val leftPadding = mapView.width / 4 + val padding = doubleArrayOf(leftPadding.toDouble(), 0.0, 0.0, bottomPadding.toDouble()) + mapboxMap.moveCamera(CameraUpdateFactory.paddingTo(leftPadding.toDouble(), 0.0, 0.0, bottomPadding.toDouble())) + + Assert.assertArrayEquals(intArrayOf(leftPadding, 0, 0, bottomPadding), mapboxMap.padding) + + val resultingCameraPosition = mapboxMap.cameraPosition + assertEquals(initialCameraPosition.target, resultingCameraPosition.target) + assertEquals( + PointF(initialPoint.x + leftPadding / 2, initialPoint.y - bottomPadding / 2), + mapboxMap.projection.toScreenLocation(resultingCameraPosition.target) + ) + Assert.assertArrayEquals(padding, resultingCameraPosition.padding, 0.0001) + } + + @Test + @UiThreadTest + fun withLatLngPadding_cameraInvalidated_paddingPersisting() { + val expectedTarget = LatLng(2.0, 2.0) + + val topPadding = mapView.height / 4 + val rightPadding = mapView.width / 4 + val padding = doubleArrayOf(0.0, topPadding.toDouble(), rightPadding.toDouble(), 0.0) + mapboxMap.moveCamera(CameraUpdateFactory.newLatLngPadding(expectedTarget, 0.0, topPadding.toDouble(), rightPadding.toDouble(), 0.0)) + + Assert.assertArrayEquals(intArrayOf(0, topPadding, rightPadding, 0), mapboxMap.padding) + + val resultingCameraPosition = mapboxMap.cameraPosition + assertEquals(expectedTarget.latitude, resultingCameraPosition.target.latitude, 0.1) + assertEquals(expectedTarget.longitude, resultingCameraPosition.target.longitude, 0.1) + + val centerLatLng = mapboxMap.projection.fromScreenLocation(PointF((mapView.width / 2).toFloat(), (mapView.height / 2).toFloat())) + assertTrue(centerLatLng.latitude > resultingCameraPosition.target.latitude) + assertTrue(centerLatLng.longitude > resultingCameraPosition.target.longitude) + Assert.assertArrayEquals(padding, resultingCameraPosition.padding, 0.0001) } } \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/NativeMapViewTest.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/NativeMapViewTest.kt index d5834db553..b13bb6b796 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/NativeMapViewTest.kt +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/NativeMapViewTest.kt @@ -15,6 +15,7 @@ import com.mapbox.mapboxsdk.style.layers.TransitionOptions import com.mapbox.mapboxsdk.testapp.utils.TestConstants import junit.framework.Assert.* import org.junit.After +import org.junit.Assert import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -30,6 +31,7 @@ class NativeMapViewTest : AppCenter() { const val BEARING_TEST = 60.0 const val PITCH_TEST = 40.0 const val ZOOM_TEST = 16.0 + val PADDING_TEST = doubleArrayOf(80.0, 150.0, 0.0, 0.0) const val WIDTH = 500 const val HEIGHT = WIDTH val LATLNG_TEST = LatLng(12.0, 34.0) @@ -86,6 +88,18 @@ class NativeMapViewTest : AppCenter() { assertEquals("Longitude should match", expected.longitude, actual.longitude, DELTA) } + @Test + @UiThreadTest + fun testLatLngPadding() { + val expected = LATLNG_TEST + nativeMapView.contentPadding = PADDING_TEST + nativeMapView.setLatLng(expected, 0) + val actual = nativeMapView.latLng + assertEquals("Latitude should match", expected.latitude, actual.latitude, DELTA) + assertEquals("Longitude should match", expected.longitude, actual.longitude, DELTA) + Assert.assertArrayEquals(PADDING_TEST, nativeMapView.cameraPosition.padding, DELTA) + } + @Test @UiThreadTest fun testLatLngDefault() { @@ -145,14 +159,18 @@ class NativeMapViewTest : AppCenter() { .target(LATLNG_TEST) .tilt(PITCH_TEST) .zoom(ZOOM_TEST) + .padding(PADDING_TEST) .build() - nativeMapView.jumpTo(LATLNG_TEST, ZOOM_TEST, PITCH_TEST, BEARING_TEST) + // verify that the lazily set padding is ignored when a value is provided with the camera + nativeMapView.contentPadding = doubleArrayOf(1.0, 2.0, 3.0, 4.0) + nativeMapView.jumpTo(LATLNG_TEST, ZOOM_TEST, PITCH_TEST, BEARING_TEST, PADDING_TEST) val actual = nativeMapView.cameraPosition assertEquals("Latitude should match", expected.target.latitude, actual.target.latitude, DELTA) assertEquals("Longitude should match", expected.target.longitude, actual.target.longitude, DELTA) assertEquals("Bearing should match", expected.bearing, actual.bearing, DELTA) assertEquals("Pitch should match", expected.tilt, actual.tilt, DELTA) assertEquals("Zoom should match", expected.zoom, actual.zoom, DELTA) + Assert.assertArrayEquals(expected.padding, actual.padding, DELTA) } @Test @@ -206,7 +224,7 @@ class NativeMapViewTest : AppCenter() { @Test @UiThreadTest fun testSetContentPadding() { - val expected = floatArrayOf(1.0f, 2.0f, 3.0f, 4.0f) + val expected = doubleArrayOf(1.0, 2.0, 3.0, 4.0) nativeMapView.contentPadding = expected val actual = nativeMapView.contentPadding assertEquals("Left should match", expected[0], actual[0]) @@ -258,14 +276,18 @@ class NativeMapViewTest : AppCenter() { .tilt(30.0) .target(LatLng(12.0, 14.0)) .bearing(20.0) + .padding(PADDING_TEST) .build() - nativeMapView.flyTo(expected.target, expected.zoom, expected.bearing, expected.tilt, 0) + // verify that the lazily set padding is ignored when a value is provided with the camera + nativeMapView.contentPadding = doubleArrayOf(1.0, 2.0, 3.0, 4.0) + nativeMapView.flyTo(expected.target, expected.zoom, expected.bearing, expected.tilt, PADDING_TEST, 0) val actual = nativeMapView.cameraPosition assertEquals("Bearing should match", expected.bearing, actual.bearing, TestConstants.BEARING_DELTA) assertEquals("Latitude should match", expected.target.latitude, actual.target.latitude, TestConstants.LAT_LNG_DELTA) assertEquals("Longitude should match", expected.target.longitude, actual.target.longitude, TestConstants.LAT_LNG_DELTA) assertEquals("Tilt should match", expected.tilt, actual.tilt, TestConstants.TILT_DELTA) assertEquals("Zoom should match", expected.zoom, actual.zoom, TestConstants.ZOOM_DELTA) + Assert.assertArrayEquals(expected.padding, actual.padding, DELTA) } @Test @@ -276,14 +298,18 @@ class NativeMapViewTest : AppCenter() { .tilt(30.0) .target(LatLng(12.0, 14.0)) .bearing(20.0) + .padding(PADDING_TEST) .build() - nativeMapView.easeTo(expected.target, expected.zoom, expected.bearing, expected.tilt, 0, false) + // verify that the lazily set padding is ignored when a value is provided with the camera + nativeMapView.contentPadding = doubleArrayOf(1.0, 2.0, 3.0, 4.0) + nativeMapView.easeTo(expected.target, expected.zoom, expected.bearing, expected.tilt, PADDING_TEST, 0, false) val actual = nativeMapView.cameraPosition assertEquals("Bearing should match", expected.bearing, actual.bearing, TestConstants.BEARING_DELTA) assertEquals("Latitude should match", expected.target.latitude, actual.target.latitude, TestConstants.LAT_LNG_DELTA) assertEquals("Longitude should match", expected.target.longitude, actual.target.longitude, TestConstants.LAT_LNG_DELTA) assertEquals("Tilt should match", expected.tilt, actual.tilt, TestConstants.TILT_DELTA) assertEquals("Zoom should match", expected.zoom, actual.zoom, TestConstants.ZOOM_DELTA) + Assert.assertArrayEquals(expected.padding, actual.padding, DELTA) } @Test @@ -294,8 +320,9 @@ class NativeMapViewTest : AppCenter() { .tilt(0.0) .target(LatLng(0.0, 0.0)) .bearing(0.0) + .padding(PADDING_TEST) .build() - nativeMapView.jumpTo(LatLng(1.0, 2.0), 12.0, 23.0, 1.0) + nativeMapView.jumpTo(LatLng(1.0, 2.0), 12.0, 23.0, 1.0, PADDING_TEST) nativeMapView.resetPosition() val actual = nativeMapView.cameraPosition assertEquals("Bearing should match", expected.bearing, actual.bearing, TestConstants.BEARING_DELTA) @@ -303,6 +330,7 @@ class NativeMapViewTest : AppCenter() { assertEquals("Longitude should match", expected.target.longitude, actual.target.longitude, TestConstants.LAT_LNG_DELTA) assertEquals("Tilt should match", expected.tilt, actual.tilt, TestConstants.TILT_DELTA) assertEquals("Zoom should match", expected.zoom, actual.zoom, TestConstants.ZOOM_DELTA) + Assert.assertArrayEquals(expected.padding, actual.padding, DELTA) } @Test diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraAnimateTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraAnimateTest.java index 625e37798f..69cf1264bf 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraAnimateTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraAnimateTest.java @@ -1,297 +1,11 @@ package com.mapbox.mapboxsdk.testapp.camera; -import android.support.test.espresso.Espresso; -import android.support.test.espresso.IdlingRegistry; -import android.support.test.espresso.idling.CountingIdlingResource; - -import com.mapbox.mapboxsdk.camera.CameraPosition; -import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.geometry.LatLngBounds; +import com.mapbox.mapboxsdk.camera.CameraUpdate; import com.mapbox.mapboxsdk.maps.MapboxMap; -import com.mapbox.mapboxsdk.testapp.activity.BaseTest; -import com.mapbox.mapboxsdk.testapp.activity.espresso.DeviceIndependentTestActivity; -import com.mapbox.mapboxsdk.testapp.utils.TestConstants; - -import org.junit.Test; - -import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke; -import static org.junit.Assert.assertEquals; - -public class CameraAnimateTest extends BaseTest { - - private final CountingIdlingResource animationIdlingResource = - new CountingIdlingResource("animation_idling_resource"); - - @Override - protected Class getActivityClass() { - return DeviceIndependentTestActivity.class; - } +public class CameraAnimateTest extends CameraTest { @Override - public void beforeTest() { - super.beforeTest(); - IdlingRegistry.getInstance().register(animationIdlingResource); - } - - @Test - public void testAnimateToCameraPositionTarget() { - validateTestSetup(); - invoke(mapboxMap, (uiController, mapboxMap) -> { - float zoom = 1.0f; - LatLng moveTarget = new LatLng(1, 1); - CameraPosition initialPosition = new CameraPosition.Builder().target( - new LatLng()).zoom(zoom).bearing(0).tilt(0).build(); - CameraPosition cameraPosition = mapboxMap.getCameraPosition(); - assertEquals("Default camera position should match default", cameraPosition, initialPosition); - - animationIdlingResource.increment(); - mapboxMap.animateCamera(CameraUpdateFactory.newLatLng(moveTarget), new MapboxMap.CancelableCallback() { - @Override - public void onCancel() { - verifyCameraPosition(mapboxMap, moveTarget, zoom, 0, 0); - animationIdlingResource.decrement(); - } - - @Override - public void onFinish() { - verifyCameraPosition(mapboxMap, moveTarget, zoom, 0, 0); - animationIdlingResource.decrement(); - } - }); - }); - - Espresso.onIdle(); - } - - @Test - public void testAnimateToCameraPositionTargetZoom() { - validateTestSetup(); - invoke(mapboxMap, (uiController, mapboxMap) -> { - final float moveZoom = 15.5f; - final LatLng moveTarget = new LatLng(1.0000000001, 1.0000000003); - - animationIdlingResource.increment(); - mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(moveTarget, moveZoom), new MapboxMap - .CancelableCallback() { - @Override - public void onCancel() { - verifyCameraPosition(mapboxMap, moveTarget, moveZoom, 0, 0); - animationIdlingResource.decrement(); - } - - @Override - public void onFinish() { - verifyCameraPosition(mapboxMap, moveTarget, moveZoom, 0, 0); - animationIdlingResource.decrement(); - } - }); - }); - - Espresso.onIdle(); - } - - @Test - public void testAnimateToCameraPosition() { - validateTestSetup(); - invoke(mapboxMap, (uiController, mapboxMap) -> { - final LatLng moveTarget = new LatLng(1.0000000001, 1.0000000003); - final float moveZoom = 15.5f; - final float moveTilt = 45.5f; - final float moveBearing = 12.5f; - - animationIdlingResource.increment(); - mapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition( - new CameraPosition.Builder() - .target(moveTarget) - .zoom(moveZoom) - .tilt(moveTilt) - .bearing(moveBearing) - .build()), - new MapboxMap.CancelableCallback() { - @Override - public void onCancel() { - verifyCameraPosition(mapboxMap, moveTarget, moveZoom, moveBearing, moveTilt); - animationIdlingResource.decrement(); - } - - @Override - public void onFinish() { - verifyCameraPosition(mapboxMap, moveTarget, moveZoom, moveBearing, moveTilt); - animationIdlingResource.decrement(); - } - }); - }); - - Espresso.onIdle(); - } - - @Test - public void testAnimateToBounds() { - validateTestSetup(); - invoke(mapboxMap, (uiController, mapboxMap) -> { - final LatLng centerBounds = new LatLng(1, 1); - LatLng cornerOne = new LatLng(); - LatLng cornerTwo = new LatLng(2, 2); - final LatLngBounds.Builder builder = new LatLngBounds.Builder(); - builder.include(cornerOne); - builder.include(cornerTwo); - - animationIdlingResource.increment(); - mapboxMap.animateCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), 0), new MapboxMap - .CancelableCallback() { - @Override - public void onCancel() { - verifyCameraPosition(mapboxMap, centerBounds, mapboxMap.getCameraPosition().zoom, 0, 0); - animationIdlingResource.decrement(); - } - - @Override - public void onFinish() { - verifyCameraPosition(mapboxMap, centerBounds, mapboxMap.getCameraPosition().zoom, 0, 0); - animationIdlingResource.decrement(); - } - }); - }); - - Espresso.onIdle(); + void executeCameraMovement(CameraUpdate cameraUpdate, MapboxMap.CancelableCallback callback) { + mapboxMap.animateCamera(cameraUpdate, callback); } - - @Test - public void testAnimateToZoomIn() { - validateTestSetup(); - invoke(mapboxMap, (uiController, mapboxMap) -> { - float zoom = 1.0f; - - animationIdlingResource.increment(); - mapboxMap.animateCamera(CameraUpdateFactory.zoomIn(), new MapboxMap.CancelableCallback() { - @Override - public void onCancel() { - verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom + 1, 0, 0); - animationIdlingResource.decrement(); - } - - @Override - public void onFinish() { - verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom + 1, 0, 0); - animationIdlingResource.decrement(); - } - }); - }); - - Espresso.onIdle(); - } - - @Test - public void testAnimateToZoomOut() { - validateTestSetup(); - float zoom = 10.0f; - invoke(mapboxMap, (uiController, mapboxMap) -> { - animationIdlingResource.increment(); - mapboxMap.animateCamera( - CameraUpdateFactory.newLatLngZoom(new LatLng(), zoom), - new MapboxMap.CancelableCallback() { - @Override - public void onCancel() { - verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom, 0, 0); - animationIdlingResource.decrement(); - } - - @Override - public void onFinish() { - verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom, 0, 0); - animationIdlingResource.decrement(); - } - }); - }); - - invoke(mapboxMap, ((uiController, mapboxMap1) -> { - animationIdlingResource.increment(); - mapboxMap.animateCamera(CameraUpdateFactory.zoomOut(), new MapboxMap.CancelableCallback() { - @Override - public void onCancel() { - verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom - 1, 0, 0); - animationIdlingResource.decrement(); - } - - @Override - public void onFinish() { - verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom - 1, 0, 0); - animationIdlingResource.decrement(); - } - }); - })); - - Espresso.onIdle(); - } - - @Test - public void testAnimateToZoomBy() { - validateTestSetup(); - invoke(mapboxMap, (uiController, mapboxMap) -> { - float zoom = 1.0f; - final float zoomBy = 2.45f; - - animationIdlingResource.increment(); - mapboxMap.animateCamera(CameraUpdateFactory.zoomBy(zoomBy), new MapboxMap.CancelableCallback() { - @Override - public void onCancel() { - verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom + zoomBy, 0, 0); - animationIdlingResource.decrement(); - } - - @Override - public void onFinish() { - verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom + zoomBy, 0, 0); - animationIdlingResource.decrement(); - } - }); - }); - - Espresso.onIdle(); - } - - @Test - public void testAnimateToZoomTo() { - validateTestSetup(); - invoke(mapboxMap, (uiController, mapboxMap) -> { - final float zoomTo = 2.45f; - - animationIdlingResource.increment(); - mapboxMap.animateCamera(CameraUpdateFactory.zoomTo(zoomTo), new MapboxMap.CancelableCallback() { - @Override - public void onCancel() { - verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoomTo, 0, 0); - animationIdlingResource.decrement(); - } - - @Override - public void onFinish() { - verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoomTo, 0, 0); - animationIdlingResource.decrement(); - } - }); - }); - - Espresso.onIdle(); - } - - @Override - public void afterTest() { - super.afterTest(); - IdlingRegistry.getInstance().unregister(animationIdlingResource); - } - - private void verifyCameraPosition(MapboxMap mapboxMap, LatLng moveTarget, double moveZoom, double moveBearing, - double moveTilt) { - CameraPosition cameraPosition = mapboxMap.getCameraPosition(); - assertEquals("Moved camera position latitude should match", cameraPosition.target.getLatitude(), - moveTarget.getLatitude(), TestConstants.LAT_LNG_DELTA); - assertEquals("Moved camera position longitude should match", cameraPosition.target.getLongitude(), - moveTarget.getLongitude(), TestConstants.LAT_LNG_DELTA); - assertEquals("Moved zoom should match", cameraPosition.zoom, moveZoom, TestConstants.ZOOM_DELTA); - assertEquals("Moved zoom should match", cameraPosition.tilt, moveTilt, TestConstants.TILT_DELTA); - assertEquals("Moved bearing should match", cameraPosition.bearing, moveBearing, TestConstants.BEARING_DELTA); - } -} - +} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraEaseTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraEaseTest.java index ae81dc1302..be3b18ef8b 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraEaseTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraEaseTest.java @@ -1,294 +1,12 @@ package com.mapbox.mapboxsdk.testapp.camera; -import android.support.test.espresso.Espresso; -import android.support.test.espresso.IdlingRegistry; -import android.support.test.espresso.idling.CountingIdlingResource; - -import com.mapbox.mapboxsdk.camera.CameraPosition; -import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.geometry.LatLngBounds; +import com.mapbox.mapboxsdk.camera.CameraUpdate; import com.mapbox.mapboxsdk.maps.MapboxMap; -import com.mapbox.mapboxsdk.testapp.activity.BaseTest; -import com.mapbox.mapboxsdk.testapp.activity.espresso.DeviceIndependentTestActivity; -import com.mapbox.mapboxsdk.testapp.utils.TestConstants; - -import org.junit.Test; - -import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke; -import static org.junit.Assert.assertEquals; - -public class CameraEaseTest extends BaseTest { - private final CountingIdlingResource animationIdlingResource = - new CountingIdlingResource("animation_idling_resource"); - - @Override - protected Class getActivityClass() { - return DeviceIndependentTestActivity.class; - } +public class CameraEaseTest extends CameraTest { @Override - public void beforeTest() { - super.beforeTest(); - IdlingRegistry.getInstance().register(animationIdlingResource); - } - - @Test - public void testEaseToCameraPositionTarget() { - validateTestSetup(); - invoke(mapboxMap, (uiController, mapboxMap) -> { - float zoom = 1.0f; - LatLng moveTarget = new LatLng(1, 1); - CameraPosition initialPosition = new CameraPosition.Builder().target( - new LatLng()).zoom(zoom).bearing(0).tilt(0).build(); - CameraPosition cameraPosition = mapboxMap.getCameraPosition(); - assertEquals("Default camera position should match default", cameraPosition, initialPosition); - - animationIdlingResource.increment(); - mapboxMap.easeCamera(CameraUpdateFactory.newLatLng(moveTarget), new MapboxMap.CancelableCallback() { - @Override - public void onCancel() { - verifyCameraPosition(mapboxMap, moveTarget, zoom, 0, 0); - animationIdlingResource.decrement(); - } - - @Override - public void onFinish() { - verifyCameraPosition(mapboxMap, moveTarget, zoom, 0, 0); - animationIdlingResource.decrement(); - } - }); - }); - - Espresso.onIdle(); - } - - @Test - public void testEaseToCameraPositionTargetZoom() { - validateTestSetup(); - invoke(mapboxMap, (uiController, mapboxMap) -> { - final float moveZoom = 15.5f; - final LatLng moveTarget = new LatLng(1.0000000001, 1.0000000003); - - animationIdlingResource.increment(); - mapboxMap.easeCamera(CameraUpdateFactory.newLatLngZoom(moveTarget, moveZoom), new MapboxMap.CancelableCallback() { - @Override - public void onCancel() { - verifyCameraPosition(mapboxMap, moveTarget, moveZoom, 0, 0); - animationIdlingResource.decrement(); - } - - @Override - public void onFinish() { - verifyCameraPosition(mapboxMap, moveTarget, moveZoom, 0, 0); - animationIdlingResource.decrement(); - } - }); - }); - - Espresso.onIdle(); - } - - @Test - public void testEaseToCameraPosition() { - validateTestSetup(); - invoke(mapboxMap, (uiController, mapboxMap) -> { - final LatLng moveTarget = new LatLng(1.0000000001, 1.0000000003); - final float moveZoom = 15.5f; - final float moveTilt = 45.5f; - final float moveBearing = 12.5f; - - animationIdlingResource.increment(); - mapboxMap.easeCamera(CameraUpdateFactory.newCameraPosition( - new CameraPosition.Builder() - .target(moveTarget) - .zoom(moveZoom) - .tilt(moveTilt) - .bearing(moveBearing) - .build()), - new MapboxMap.CancelableCallback() { - @Override - public void onCancel() { - verifyCameraPosition(mapboxMap, moveTarget, moveZoom, moveBearing, moveTilt); - animationIdlingResource.decrement(); - } - - @Override - public void onFinish() { - verifyCameraPosition(mapboxMap, moveTarget, moveZoom, moveBearing, moveTilt); - animationIdlingResource.decrement(); - } - } - ); - }); - - Espresso.onIdle(); - } - - @Test - public void testEaseToBounds() { - validateTestSetup(); - invoke(mapboxMap, (uiController, mapboxMap) -> { - final LatLng centerBounds = new LatLng(1, 1); - LatLng cornerOne = new LatLng(); - LatLng cornerTwo = new LatLng(2, 2); - final LatLngBounds.Builder builder = new LatLngBounds.Builder(); - builder.include(cornerOne); - builder.include(cornerTwo); - - animationIdlingResource.increment(); - mapboxMap.easeCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), 0), - new MapboxMap.CancelableCallback() { - @Override - public void onCancel() { - verifyCameraPosition(mapboxMap, centerBounds, mapboxMap.getCameraPosition().zoom, 0, 0); - animationIdlingResource.decrement(); - } - - @Override - public void onFinish() { - verifyCameraPosition(mapboxMap, centerBounds, mapboxMap.getCameraPosition().zoom, 0, 0); - animationIdlingResource.decrement(); - } - }); - }); - - Espresso.onIdle(); - } - - @Test - public void testEaseToZoomIn() { - validateTestSetup(); - invoke(mapboxMap, (uiController, mapboxMap) -> { - float zoom = 1.0f; - - animationIdlingResource.increment(); - mapboxMap.easeCamera(CameraUpdateFactory.zoomIn(), new MapboxMap.CancelableCallback() { - @Override - public void onCancel() { - verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom + 1, 0, 0); - animationIdlingResource.decrement(); - } - - @Override - public void onFinish() { - verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom + 1, 0, 0); - animationIdlingResource.decrement(); - } - }); - }); - - Espresso.onIdle(); - } - - @Test - public void testEaseToZoomOut() { - float zoom = 10.0f; - validateTestSetup(); - invoke(mapboxMap, (uiController, mapboxMap) -> { - animationIdlingResource.increment(); - mapboxMap.easeCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(), zoom), new MapboxMap.CancelableCallback() { - @Override - public void onCancel() { - verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom, 0, 0); - animationIdlingResource.decrement(); - } - - @Override - public void onFinish() { - verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom, 0, 0); - animationIdlingResource.decrement(); - } - }); - }); - - invoke(mapboxMap, (uiController, mapboxMap) -> { - animationIdlingResource.increment(); - mapboxMap.easeCamera(CameraUpdateFactory.zoomOut(), new MapboxMap.CancelableCallback() { - @Override - public void onCancel() { - verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom - 1, 0, 0); - animationIdlingResource.decrement(); - } - - @Override - public void onFinish() { - verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom - 1, 0, 0); - animationIdlingResource.decrement(); - } - }); - }); - - Espresso.onIdle(); - } - - @Test - public void testEaseToZoomBy() { - validateTestSetup(); - invoke(mapboxMap, (uiController, mapboxMap) -> { - float zoom = 1.0f; - final float zoomBy = 2.45f; - - animationIdlingResource.increment(); - mapboxMap.easeCamera(CameraUpdateFactory.zoomBy(zoomBy), new MapboxMap.CancelableCallback() { - @Override - public void onCancel() { - verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom + zoomBy, 0, 0); - animationIdlingResource.decrement(); - } - - @Override - public void onFinish() { - verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom + zoomBy, 0, 0); - animationIdlingResource.decrement(); - } - }); - }); - - Espresso.onIdle(); - } - - @Test - public void testEaseToZoomTo() { - validateTestSetup(); - invoke(mapboxMap, (uiController, mapboxMap) -> { - final float zoomTo = 2.45f; - - animationIdlingResource.increment(); - mapboxMap.easeCamera(CameraUpdateFactory.zoomTo(zoomTo), new MapboxMap.CancelableCallback() { - @Override - public void onCancel() { - verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoomTo, 0, 0); - animationIdlingResource.decrement(); - } - - @Override - public void onFinish() { - verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoomTo, 0, 0); - animationIdlingResource.decrement(); - } - }); - }); - - Espresso.onIdle(); - } - - @Override - public void afterTest() { - super.afterTest(); - IdlingRegistry.getInstance().unregister(animationIdlingResource); - } - - private void verifyCameraPosition(MapboxMap mapboxMap, LatLng moveTarget, double moveZoom, double moveBearing, - double moveTilt) { - CameraPosition cameraPosition = mapboxMap.getCameraPosition(); - assertEquals("Moved camera position latitude should match", cameraPosition.target.getLatitude(), - moveTarget.getLatitude(), TestConstants.LAT_LNG_DELTA); - assertEquals("Moved camera position longitude should match", cameraPosition.target.getLongitude(), - moveTarget.getLongitude(), TestConstants.LAT_LNG_DELTA); - assertEquals("Moved zoom should match", cameraPosition.zoom, moveZoom, TestConstants.ZOOM_DELTA); - assertEquals("Moved zoom should match", cameraPosition.tilt, moveTilt, TestConstants.TILT_DELTA); - assertEquals("Moved bearing should match", cameraPosition.bearing, moveBearing, TestConstants.BEARING_DELTA); + void executeCameraMovement(CameraUpdate cameraUpdate, MapboxMap.CancelableCallback callback) { + mapboxMap.easeCamera(cameraUpdate, callback); } } \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraMoveTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraMoveTest.java index f8aa43d52d..2cd7b6ffe3 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraMoveTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraMoveTest.java @@ -1,294 +1,12 @@ package com.mapbox.mapboxsdk.testapp.camera; -import android.support.test.espresso.Espresso; -import android.support.test.espresso.IdlingRegistry; -import android.support.test.espresso.idling.CountingIdlingResource; - -import com.mapbox.mapboxsdk.camera.CameraPosition; -import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.geometry.LatLngBounds; +import com.mapbox.mapboxsdk.camera.CameraUpdate; import com.mapbox.mapboxsdk.maps.MapboxMap; -import com.mapbox.mapboxsdk.testapp.activity.BaseTest; -import com.mapbox.mapboxsdk.testapp.activity.espresso.DeviceIndependentTestActivity; -import com.mapbox.mapboxsdk.testapp.utils.TestConstants; - -import org.junit.Test; - -import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke; -import static org.junit.Assert.assertEquals; - -public class CameraMoveTest extends BaseTest { - - private final CountingIdlingResource animationIdlingResource = - new CountingIdlingResource("animation_idling_resource"); - - @Override - protected Class getActivityClass() { - return DeviceIndependentTestActivity.class; - } - - @Override - public void beforeTest() { - super.beforeTest(); - IdlingRegistry.getInstance().register(animationIdlingResource); - } - - @Test - public void testMoveToCameraPositionTarget() { - validateTestSetup(); - invoke(mapboxMap, (uiController, mapboxMap) -> { - float zoom = 1.0f; - LatLng moveTarget = new LatLng(1, 1); - CameraPosition initialPosition = new CameraPosition.Builder().target( - new LatLng()).zoom(zoom).bearing(0).tilt(0).build(); - CameraPosition cameraPosition = mapboxMap.getCameraPosition(); - assertEquals("Default camera position should match default", cameraPosition, initialPosition); - - animationIdlingResource.increment(); - mapboxMap.moveCamera(CameraUpdateFactory.newLatLng(moveTarget), new MapboxMap.CancelableCallback() { - @Override - public void onCancel() { - verifyCameraPosition(mapboxMap, moveTarget, zoom, 0, 0); - animationIdlingResource.decrement(); - } - - @Override - public void onFinish() { - verifyCameraPosition(mapboxMap, moveTarget, zoom, 0, 0); - animationIdlingResource.decrement(); - } - }); - }); - - Espresso.onIdle(); - } - - @Test - public void testMoveToCameraPositionTargetZoom() { - validateTestSetup(); - invoke(mapboxMap, (uiController, mapboxMap) -> { - final float moveZoom = 15.5f; - final LatLng moveTarget = new LatLng(1.0000000001, 1.0000000003); - - animationIdlingResource.increment(); - mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(moveTarget, moveZoom), new MapboxMap.CancelableCallback() { - @Override - public void onCancel() { - verifyCameraPosition(mapboxMap, moveTarget, moveZoom, 0, 0); - animationIdlingResource.decrement(); - } - - @Override - public void onFinish() { - verifyCameraPosition(mapboxMap, moveTarget, moveZoom, 0, 0); - animationIdlingResource.decrement(); - } - }); - }); - - Espresso.onIdle(); - } - - @Test - public void testMoveToCameraPosition() { - validateTestSetup(); - invoke(mapboxMap, (uiController, mapboxMap) -> { - final LatLng moveTarget = new LatLng(1.0000000001, 1.0000000003); - final float moveZoom = 15.5f; - final float moveTilt = 45.5f; - final float moveBearing = 12.5f; - - animationIdlingResource.increment(); - mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition( - new CameraPosition.Builder() - .target(moveTarget) - .zoom(moveZoom) - .tilt(moveTilt) - .bearing(moveBearing) - .build()), - new MapboxMap.CancelableCallback() { - @Override - public void onCancel() { - verifyCameraPosition(mapboxMap, moveTarget, moveZoom, moveBearing, moveTilt); - animationIdlingResource.decrement(); - } - - @Override - public void onFinish() { - verifyCameraPosition(mapboxMap, moveTarget, moveZoom, moveBearing, moveTilt); - animationIdlingResource.decrement(); - } - }); - }); - - Espresso.onIdle(); - } - - @Test - public void testMoveToBounds() { - validateTestSetup(); - invoke(mapboxMap, (uiController, mapboxMap) -> { - final LatLng centerBounds = new LatLng(1, 1); - LatLng cornerOne = new LatLng(); - LatLng cornerTwo = new LatLng(2, 2); - final LatLngBounds.Builder builder = new LatLngBounds.Builder(); - builder.include(cornerOne); - builder.include(cornerTwo); - - animationIdlingResource.increment(); - mapboxMap.moveCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), 0), - new MapboxMap.CancelableCallback() { - @Override - public void onCancel() { - verifyCameraPosition(mapboxMap, centerBounds, mapboxMap.getCameraPosition().zoom, 0, 0); - animationIdlingResource.decrement(); - } - - @Override - public void onFinish() { - verifyCameraPosition(mapboxMap, centerBounds, mapboxMap.getCameraPosition().zoom, 0, 0); - animationIdlingResource.decrement(); - } - }); - }); - - Espresso.onIdle(); - } - - @Test - public void testMoveToZoomIn() { - validateTestSetup(); - invoke(mapboxMap, (uiController, mapboxMap) -> { - float zoom = 1.0f; - - animationIdlingResource.increment(); - mapboxMap.moveCamera(CameraUpdateFactory.zoomIn(), new MapboxMap.CancelableCallback() { - @Override - public void onCancel() { - verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom + 1, 0, 0); - animationIdlingResource.decrement(); - } - - @Override - public void onFinish() { - verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom + 1, 0, 0); - animationIdlingResource.decrement(); - } - }); - }); - - Espresso.onIdle(); - } - - @Test - public void testMoveToZoomOut() { - float zoom = 10.0f; - validateTestSetup(); - invoke(mapboxMap, (uiController, mapboxMap) -> { - animationIdlingResource.increment(); - mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(), zoom), new MapboxMap.CancelableCallback() { - @Override - public void onCancel() { - verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom, 0, 0); - animationIdlingResource.decrement(); - } - - @Override - public void onFinish() { - verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom, 0, 0); - animationIdlingResource.decrement(); - } - }); - }); - - invoke(mapboxMap, (uiController, mapboxMap) -> { - animationIdlingResource.increment(); - mapboxMap.moveCamera(CameraUpdateFactory.zoomOut(), new MapboxMap.CancelableCallback() { - @Override - public void onCancel() { - verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom - 1, 0, 0); - animationIdlingResource.decrement(); - } - - @Override - public void onFinish() { - verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom - 1, 0, 0); - animationIdlingResource.decrement(); - } - }); - }); - - Espresso.onIdle(); - } - - @Test - public void testMoveToZoomBy() { - validateTestSetup(); - invoke(mapboxMap, (uiController, mapboxMap) -> { - float zoom = 1.0f; - final float zoomBy = 2.45f; - - animationIdlingResource.increment(); - mapboxMap.moveCamera(CameraUpdateFactory.zoomBy(zoomBy), new MapboxMap.CancelableCallback() { - @Override - public void onCancel() { - verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom + zoomBy, 0, 0); - animationIdlingResource.decrement(); - } - - @Override - public void onFinish() { - verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom + zoomBy, 0, 0); - animationIdlingResource.decrement(); - } - }); - }); - - Espresso.onIdle(); - } - - @Test - public void testMoveToZoomTo() { - validateTestSetup(); - invoke(mapboxMap, (uiController, mapboxMap) -> { - final float zoomTo = 2.45f; - - animationIdlingResource.increment(); - mapboxMap.moveCamera(CameraUpdateFactory.zoomTo(zoomTo), new MapboxMap.CancelableCallback() { - @Override - public void onCancel() { - verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoomTo, 0, 0); - animationIdlingResource.decrement(); - } - - @Override - public void onFinish() { - verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoomTo, 0, 0); - animationIdlingResource.decrement(); - } - }); - }); - - Espresso.onIdle(); - } +public class CameraMoveTest extends CameraTest { @Override - public void afterTest() { - super.afterTest(); - IdlingRegistry.getInstance().unregister(animationIdlingResource); - } - - private void verifyCameraPosition(MapboxMap mapboxMap, LatLng moveTarget, double moveZoom, double moveBearing, - double moveTilt) { - CameraPosition cameraPosition = mapboxMap.getCameraPosition(); - assertEquals("Moved camera position latitude should match", cameraPosition.target.getLatitude(), - moveTarget.getLatitude(), TestConstants.LAT_LNG_DELTA); - assertEquals("Moved camera position longitude should match", cameraPosition.target.getLongitude(), - moveTarget.getLongitude(), TestConstants.LAT_LNG_DELTA); - assertEquals("Moved zoom should match", cameraPosition.zoom, moveZoom, TestConstants.ZOOM_DELTA); - assertEquals("Moved zoom should match", cameraPosition.tilt, moveTilt, TestConstants.TILT_DELTA); - assertEquals("Moved bearing should match", cameraPosition.bearing, moveBearing, TestConstants.BEARING_DELTA); + void executeCameraMovement(CameraUpdate cameraUpdate, MapboxMap.CancelableCallback callback) { + mapboxMap.moveCamera(cameraUpdate, callback); } -} +} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraTest.java new file mode 100644 index 0000000000..ebce9aaec5 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraTest.java @@ -0,0 +1,307 @@ +package com.mapbox.mapboxsdk.testapp.camera; + +import com.mapbox.mapboxsdk.camera.CameraPosition; +import com.mapbox.mapboxsdk.camera.CameraUpdate; +import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.geometry.LatLngBounds; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.testapp.activity.BaseTest; +import com.mapbox.mapboxsdk.testapp.activity.espresso.DeviceIndependentTestActivity; +import com.mapbox.mapboxsdk.testapp.utils.TestConstants; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +public abstract class CameraTest extends BaseTest { + + private CountDownLatch latch; + + @Override + protected Class getActivityClass() { + return DeviceIndependentTestActivity.class; + } + + @Override + public void beforeTest() { + super.beforeTest(); + latch = new CountDownLatch(1); + } + + @Test + public void testToCameraPositionTarget() throws InterruptedException { + validateTestSetup(); + invoke(mapboxMap, (uiController, mapboxMap) -> { + float zoom = 1.0f; + LatLng moveTarget = new LatLng(1, 1); + CameraPosition initialPosition = new CameraPosition.Builder().target( + new LatLng()).zoom(zoom).bearing(0).tilt(0).padding(new double[4]).build(); + CameraPosition cameraPosition = mapboxMap.getCameraPosition(); + assertEquals("Default camera position should match default", cameraPosition, initialPosition); + + executeCameraMovement(CameraUpdateFactory.newLatLng(moveTarget), new MapboxMap.CancelableCallback() { + @Override + public void onCancel() { + verifyCameraPosition(mapboxMap, moveTarget, zoom, 0, 0, new double[4]); + latch.countDown(); + } + + @Override + public void onFinish() { + verifyCameraPosition(mapboxMap, moveTarget, zoom, 0, 0, new double[4]); + latch.countDown(); + } + }); + }); + + if (!latch.await(10, TimeUnit.SECONDS)) { + Assert.fail("timeout"); + } + } + + @Test + public void testToCameraPositionTargetZoom() throws InterruptedException { + validateTestSetup(); + invoke(mapboxMap, (uiController, mapboxMap) -> { + final float moveZoom = 15.5f; + final LatLng moveTarget = new LatLng(1.0000000001, 1.0000000003); + + executeCameraMovement(CameraUpdateFactory.newLatLngZoom(moveTarget, moveZoom), + new MapboxMap.CancelableCallback() { + @Override + public void onCancel() { + verifyCameraPosition(mapboxMap, moveTarget, moveZoom, 0, 0, new double[4]); + latch.countDown(); + } + + @Override + public void onFinish() { + verifyCameraPosition(mapboxMap, moveTarget, moveZoom, 0, 0, new double[4]); + latch.countDown(); + } + }); + }); + + if (!latch.await(10, TimeUnit.SECONDS)) { + Assert.fail("timeout"); + } + } + + @Test + public void testToCameraPosition() throws InterruptedException { + validateTestSetup(); + invoke(mapboxMap, (uiController, mapboxMap) -> { + final LatLng moveTarget = new LatLng(1.0000000001, 1.0000000003); + final float moveZoom = 15.5f; + final float moveTilt = 45.5f; + final float moveBearing = 12.5f; + final double[] movePadding = new double[] {0, 500, 350, 1}; + + executeCameraMovement(CameraUpdateFactory.newCameraPosition( + new CameraPosition.Builder() + .target(moveTarget) + .zoom(moveZoom) + .tilt(moveTilt) + .bearing(moveBearing) + .padding(movePadding) + .build()), + new MapboxMap.CancelableCallback() { + @Override + public void onCancel() { + verifyCameraPosition(mapboxMap, moveTarget, moveZoom, moveBearing, moveTilt, movePadding); + latch.countDown(); + } + + @Override + public void onFinish() { + verifyCameraPosition(mapboxMap, moveTarget, moveZoom, moveBearing, moveTilt, movePadding); + latch.countDown(); + } + }); + }); + + if (!latch.await(10, TimeUnit.SECONDS)) { + Assert.fail("timeout"); + } + } + + @Test + public void testToBounds() throws InterruptedException { + validateTestSetup(); + invoke(mapboxMap, (uiController, mapboxMap) -> { + final LatLng centerBounds = new LatLng(1, 1); + LatLng cornerOne = new LatLng(); + LatLng cornerTwo = new LatLng(2, 2); + final LatLngBounds.Builder builder = new LatLngBounds.Builder(); + builder.include(cornerOne); + builder.include(cornerTwo); + + executeCameraMovement(CameraUpdateFactory.newLatLngBounds(builder.build(), 0), + new MapboxMap.CancelableCallback() { + @Override + public void onCancel() { + verifyCameraPosition(mapboxMap, centerBounds, mapboxMap.getCameraPosition().zoom, 0, 0, new double[4]); + latch.countDown(); + } + + @Override + public void onFinish() { + verifyCameraPosition(mapboxMap, centerBounds, mapboxMap.getCameraPosition().zoom, 0, 0, new double[4]); + latch.countDown(); + } + }); + }); + + if (!latch.await(10, TimeUnit.SECONDS)) { + Assert.fail("timeout"); + } + } + + @Test + public void testToZoomIn() throws InterruptedException { + validateTestSetup(); + invoke(mapboxMap, (uiController, mapboxMap) -> { + float zoom = 1.0f; + + executeCameraMovement(CameraUpdateFactory.zoomIn(), new MapboxMap.CancelableCallback() { + @Override + public void onCancel() { + verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom + 1, 0, 0, new double[4]); + latch.countDown(); + } + + @Override + public void onFinish() { + verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom + 1, 0, 0, new double[4]); + latch.countDown(); + } + }); + }); + + if (!latch.await(10, TimeUnit.SECONDS)) { + Assert.fail("timeout"); + } + } + + @Test + public void testToZoomOut() throws InterruptedException { + float zoom = 10.0f; + validateTestSetup(); + invoke(mapboxMap, (uiController, mapboxMap) -> { + executeCameraMovement(CameraUpdateFactory.newLatLngZoom(new LatLng(), zoom), new MapboxMap.CancelableCallback() { + @Override + public void onCancel() { + verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom, 0, 0, new double[4]); + latch.countDown(); + } + + @Override + public void onFinish() { + verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom, 0, 0, new double[4]); + latch.countDown(); + } + }); + }); + + if (!latch.await(10, TimeUnit.SECONDS)) { + Assert.fail("timeout"); + } + + latch = new CountDownLatch(1); + + invoke(mapboxMap, (uiController, mapboxMap) -> { + executeCameraMovement(CameraUpdateFactory.zoomOut(), new MapboxMap.CancelableCallback() { + @Override + public void onCancel() { + verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom - 1, 0, 0, new double[4]); + latch.countDown(); + } + + @Override + public void onFinish() { + verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom - 1, 0, 0, new double[4]); + latch.countDown(); + } + }); + }); + + if (!latch.await(10, TimeUnit.SECONDS)) { + Assert.fail("timeout"); + } + } + + @Test + public void testToZoomBy() throws InterruptedException { + validateTestSetup(); + invoke(mapboxMap, (uiController, mapboxMap) -> { + float zoom = 1.0f; + final float zoomBy = 2.45f; + + executeCameraMovement(CameraUpdateFactory.zoomBy(zoomBy), new MapboxMap.CancelableCallback() { + @Override + public void onCancel() { + verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom + zoomBy, 0, 0, new double[4]); + latch.countDown(); + } + + @Override + public void onFinish() { + verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom + zoomBy, 0, 0, new double[4]); + latch.countDown(); + } + }); + }); + + if (!latch.await(10, TimeUnit.SECONDS)) { + Assert.fail("timeout"); + } + } + + @Test + public void testToZoomTo() throws InterruptedException { + validateTestSetup(); + invoke(mapboxMap, (uiController, mapboxMap) -> { + final float zoomTo = 2.45f; + + executeCameraMovement(CameraUpdateFactory.zoomTo(zoomTo), new MapboxMap.CancelableCallback() { + @Override + public void onCancel() { + verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoomTo, 0, 0, new double[4]); + latch.countDown(); + } + + @Override + public void onFinish() { + verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoomTo, 0, 0, new double[4]); + latch.countDown(); + } + }); + }); + + if (!latch.await(10, TimeUnit.SECONDS)) { + Assert.fail("timeout"); + } + } + + abstract void executeCameraMovement(CameraUpdate cameraUpdate, MapboxMap.CancelableCallback callback); + + private void verifyCameraPosition(MapboxMap mapboxMap, LatLng moveTarget, double moveZoom, double moveBearing, + double moveTilt, double[] padding) { + CameraPosition cameraPosition = mapboxMap.getCameraPosition(); + assertEquals("Moved camera position latitude should match", cameraPosition.target.getLatitude(), + moveTarget.getLatitude(), TestConstants.LAT_LNG_DELTA); + assertEquals("Moved camera position longitude should match", cameraPosition.target.getLongitude(), + moveTarget.getLongitude(), TestConstants.LAT_LNG_DELTA); + assertEquals("Moved zoom should match", cameraPosition.zoom, moveZoom, TestConstants.ZOOM_DELTA); + assertEquals("Moved zoom should match", cameraPosition.tilt, moveTilt, TestConstants.TILT_DELTA); + assertEquals("Moved bearing should match", cameraPosition.bearing, moveBearing, TestConstants.BEARING_DELTA); + assertArrayEquals("Moved padding should match", cameraPosition.padding, padding, TestConstants.PADDING_DELTA); + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/TestConstants.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/TestConstants.java index 09e27d30f2..255d1e1e7d 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/TestConstants.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/TestConstants.java @@ -5,6 +5,7 @@ public class TestConstants { public static final double BEARING_DELTA = 0.1; public static final double TILT_DELTA = 0.3; public static final double ZOOM_DELTA = 0.3; + public static final double PADDING_DELTA = 0.0001; public static final String TEXT_MARKER_TITLE = "Marker"; public static final String TEXT_MARKER_SNIPPET = "Snippet"; diff --git a/platform/android/src/map/camera_position.cpp b/platform/android/src/map/camera_position.cpp index fcbec5639a..03aca79bda 100644 --- a/platform/android/src/map/camera_position.cpp +++ b/platform/android/src/map/camera_position.cpp @@ -4,9 +4,9 @@ namespace mbgl { namespace android { -jni::Local> CameraPosition::New(jni::JNIEnv &env, mbgl::CameraOptions options) { +jni::Local> CameraPosition::New(jni::JNIEnv &env, mbgl::CameraOptions options, float pixelRatio) { static auto& javaClass = jni::Class::Singleton(env); - static auto constructor = javaClass.GetConstructor, double, double, double>(env); + static auto constructor = javaClass.GetConstructor, double, double, double, jni::Array>(env); // wrap LatLng values coming from core auto center = options.center.value(); @@ -25,21 +25,33 @@ jni::Local> CameraPosition::New(jni::JNIEnv &env, mb // convert tilt, core ranges from [0 rad, 1,0472 rad], android ranges from 0 to 60 double tilt_degrees = options.pitch.value_or(0); - return javaClass.New(env, constructor, LatLng::New(env, center), options.zoom.value_or(0), tilt_degrees, bearing_degrees); + std::vector paddingVect; + auto insets = options.padding.value_or(EdgeInsets {0, 0, 0, 0}); + auto padding = jni::Array::New(env, 4); + paddingVect.push_back(insets.left() * pixelRatio); + paddingVect.push_back(insets.top() * pixelRatio); + paddingVect.push_back(insets.right() * pixelRatio); + paddingVect.push_back(insets.bottom() * pixelRatio); + padding.SetRegion>(env, 0, paddingVect); + + return javaClass.New(env, constructor, LatLng::New(env, center), options.zoom.value_or(0), tilt_degrees, bearing_degrees, padding); } -mbgl::CameraOptions CameraPosition::getCameraOptions(jni::JNIEnv& env, const jni::Object& position) { +mbgl::CameraOptions CameraPosition::getCameraOptions(jni::JNIEnv& env, const jni::Object& position, float pixelRatio) { static auto& javaClass = jni::Class::Singleton(env); static auto bearing = javaClass.GetField(env, "bearing"); static auto target = javaClass.GetField>(env, "target"); static auto tilt = javaClass.GetField(env, "tilt"); static auto zoom = javaClass.GetField(env, "zoom"); + static auto paddingField = javaClass.GetField>(env, "padding"); + static auto padding = position.Get(env, paddingField); auto center = LatLng::getLatLng(env, position.Get(env, target)); return mbgl::CameraOptions { center, - {}, + EdgeInsets {padding.Get(env, 1) * pixelRatio, padding.Get(env, 0) * pixelRatio, + padding.Get(env, 3) * pixelRatio, padding.Get(env, 2) * pixelRatio}, {}, position.Get(env, zoom), position.Get(env, bearing), diff --git a/platform/android/src/map/camera_position.hpp b/platform/android/src/map/camera_position.hpp index 7579f9fed1..b677f04ea0 100644 --- a/platform/android/src/map/camera_position.hpp +++ b/platform/android/src/map/camera_position.hpp @@ -12,9 +12,9 @@ class CameraPosition : private mbgl::util::noncopyable { public: static constexpr auto Name() { return "com/mapbox/mapboxsdk/camera/CameraPosition"; }; - static jni::Local> New(jni::JNIEnv&, mbgl::CameraOptions); + static jni::Local> New(jni::JNIEnv&, mbgl::CameraOptions, float pixelRatio); - static mbgl::CameraOptions getCameraOptions(jni::JNIEnv&, const jni::Object&); + static mbgl::CameraOptions getCameraOptions(jni::JNIEnv&, const jni::Object&, float pixelRatio); static void registerNative(jni::JNIEnv&); }; diff --git a/platform/android/src/native_map_view.cpp b/platform/android/src/native_map_view.cpp index e94ef0153e..8ef757de15 100755 --- a/platform/android/src/native_map_view.cpp +++ b/platform/android/src/native_map_view.cpp @@ -336,13 +336,17 @@ void NativeMapView::moveBy(jni::JNIEnv&, jni::jdouble dx, jni::jdouble dy, jni:: map->moveBy({dx, dy}, animationOptions); } -void NativeMapView::jumpTo(jni::JNIEnv&, jni::jdouble bearing, jni::jdouble latitude, jni::jdouble longitude, jni::jdouble pitch, jni::jdouble zoom) { +void NativeMapView::jumpTo(jni::JNIEnv& env, jni::jdouble bearing, jni::jdouble latitude, jni::jdouble longitude, jni::jdouble pitch, jni::jdouble zoom, const jni::Array& padding) { mbgl::CameraOptions options; if (bearing != -1) { options.bearing = bearing; } options.center = mbgl::LatLng(latitude, longitude); - options.padding = insets; + if (padding) { + assert(padding.Length(env) == 4); + options.padding = mbgl::EdgeInsets{padding.Get(env, 0), padding.Get(env, 1), + padding.Get(env, 2), padding.Get(env, 3)}; + } if (pitch != -1) { options.pitch = pitch; } @@ -353,13 +357,17 @@ void NativeMapView::jumpTo(jni::JNIEnv&, jni::jdouble bearing, jni::jdouble lati map->jumpTo(options); } -void NativeMapView::easeTo(jni::JNIEnv&, jni::jdouble bearing, jni::jdouble latitude, jni::jdouble longitude, jni::jlong duration, jni::jdouble pitch, jni::jdouble zoom, jni::jboolean easing) { +void NativeMapView::easeTo(jni::JNIEnv& env, jni::jdouble bearing, jni::jdouble latitude, jni::jdouble longitude, jni::jlong duration, jni::jdouble pitch, jni::jdouble zoom, const jni::Array& padding, jni::jboolean easing) { mbgl::CameraOptions cameraOptions; if (bearing != -1) { cameraOptions.bearing = bearing; } cameraOptions.center = mbgl::LatLng(latitude, longitude); - cameraOptions.padding = insets; + if (padding) { + assert(padding.Length(env) == 4); + cameraOptions.padding = mbgl::EdgeInsets{padding.Get(env, 0), padding.Get(env, 1), + padding.Get(env, 2), padding.Get(env, 3)}; + } if (pitch != -1) { cameraOptions.pitch = pitch; } @@ -377,13 +385,17 @@ void NativeMapView::easeTo(jni::JNIEnv&, jni::jdouble bearing, jni::jdouble lati map->easeTo(cameraOptions, animationOptions); } -void NativeMapView::flyTo(jni::JNIEnv&, jni::jdouble bearing, jni::jdouble latitude, jni::jdouble longitude, jni::jlong duration, jni::jdouble pitch, jni::jdouble zoom) { +void NativeMapView::flyTo(jni::JNIEnv& env, jni::jdouble bearing, jni::jdouble latitude, jni::jdouble longitude, jni::jlong duration, jni::jdouble pitch, jni::jdouble zoom, const jni::Array& padding) { mbgl::CameraOptions cameraOptions; if (bearing != -1) { cameraOptions.bearing = bearing; } cameraOptions.center = mbgl::LatLng(latitude, longitude); - cameraOptions.padding = insets; + if (padding) { + assert(padding.Length(env) == 4); + cameraOptions.padding = mbgl::EdgeInsets{padding.Get(env, 0), padding.Get(env, 1), + padding.Get(env, 2), padding.Get(env, 3)}; + } if (pitch != -1) { cameraOptions.pitch = pitch; } @@ -397,23 +409,29 @@ void NativeMapView::flyTo(jni::JNIEnv&, jni::jdouble bearing, jni::jdouble latit } jni::Local> NativeMapView::getLatLng(JNIEnv& env) { - return LatLng::New(env, *map->getCameraOptions(insets).center); + return LatLng::New(env, *map->getCameraOptions(mbgl::nullopt).center); } -void NativeMapView::setLatLng(jni::JNIEnv&, jni::jdouble latitude, jni::jdouble longitude, jni::jlong duration) { - map->easeTo(mbgl::CameraOptions().withCenter(mbgl::LatLng(latitude, longitude)).withPadding(insets), - mbgl::AnimationOptions{mbgl::Milliseconds(duration)}); +void NativeMapView::setLatLng(jni::JNIEnv& env, jni::jdouble latitude, jni::jdouble longitude, const jni::Array& padding, jni::jlong duration) { + mbgl::CameraOptions cameraOptions; + cameraOptions.center = mbgl::LatLng(latitude, longitude); + if (padding) { + assert(padding.Length(env) == 4); + cameraOptions.padding = mbgl::EdgeInsets{padding.Get(env, 0), padding.Get(env, 1), + padding.Get(env, 2), padding.Get(env, 3)}; + } + map->easeTo(cameraOptions, mbgl::AnimationOptions{mbgl::Milliseconds(duration)}); } jni::Local> NativeMapView::getCameraForLatLngBounds(jni::JNIEnv& env, const jni::Object& jBounds, double top, double left, double bottom, double right, double bearing, double tilt) { mbgl::EdgeInsets padding = {top, left, bottom, right}; - return CameraPosition::New(env, map->cameraForLatLngBounds(mbgl::android::LatLngBounds::getLatLngBounds(env, jBounds), padding, bearing, tilt)); + return CameraPosition::New(env, map->cameraForLatLngBounds(mbgl::android::LatLngBounds::getLatLngBounds(env, jBounds), padding, bearing, tilt), pixelRatio); } jni::Local> NativeMapView::getCameraForGeometry(jni::JNIEnv& env, const jni::Object& jGeometry, double top, double left, double bottom, double right, double bearing, double tilt) { auto geometry = geojson::Geometry::convert(env, jGeometry); mbgl::EdgeInsets padding = {top, left, bottom, right}; - return CameraPosition::New(env, map->cameraForGeometry(geometry, padding, bearing, tilt)); + return CameraPosition::New(env, map->cameraForGeometry(geometry, padding, bearing, tilt), pixelRatio); } void NativeMapView::setReachability(jni::JNIEnv&, jni::jboolean reachable) { @@ -514,21 +532,6 @@ void NativeMapView::setVisibleCoordinateBounds(JNIEnv& env, const jni::ArrayeaseTo(cameraOptions, animationOptions); } -void NativeMapView::setContentPadding(JNIEnv&, float top, float left, float bottom, float right) { - insets = {top, left, bottom, right}; -} - -jni::Local> NativeMapView::getContentPadding(JNIEnv& env) { - auto result = jni::Array::New(env, 4); - std::vector vect; - vect.push_back(insets.top()); - vect.push_back(insets.left()); - vect.push_back(insets.bottom()); - vect.push_back(insets.right()); - result.SetRegion>(env, 0, vect); - return result; -} - void NativeMapView::scheduleSnapshot(jni::JNIEnv&) { mapRenderer.requestSnapshot([&](PremultipliedImage image) { auto _env = android::AttachEnv(); @@ -546,7 +549,7 @@ void NativeMapView::scheduleSnapshot(jni::JNIEnv&) { } jni::Local> NativeMapView::getCameraPosition(jni::JNIEnv& env) { - return CameraPosition::New(env, map->getCameraOptions(insets)); + return CameraPosition::New(env, map->getCameraOptions(mbgl::nullopt), pixelRatio); } void NativeMapView::updateMarker(jni::JNIEnv& env, jni::jlong markerId, jni::jdouble lat, jni::jdouble lon, const jni::String& jid) { @@ -1096,8 +1099,6 @@ void NativeMapView::registerNative(jni::JNIEnv& env) { METHOD(&NativeMapView::getBearing, "nativeGetBearing"), METHOD(&NativeMapView::resetNorth, "nativeResetNorth"), METHOD(&NativeMapView::setVisibleCoordinateBounds, "nativeSetVisibleCoordinateBounds"), - METHOD(&NativeMapView::setContentPadding, "nativeSetContentPadding"), - METHOD(&NativeMapView::getContentPadding, "nativeGetContentPadding"), METHOD(&NativeMapView::scheduleSnapshot, "nativeTakeSnapshot"), METHOD(&NativeMapView::getCameraPosition, "nativeGetCameraPosition"), METHOD(&NativeMapView::updateMarker, "nativeUpdateMarker"), diff --git a/platform/android/src/native_map_view.hpp b/platform/android/src/native_map_view.hpp index 10b2af0669..26567a003c 100755 --- a/platform/android/src/native_map_view.hpp +++ b/platform/android/src/native_map_view.hpp @@ -91,15 +91,15 @@ public: void moveBy(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jlong); - void jumpTo(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jdouble, jni::jdouble, jni::jdouble); + void jumpTo(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jdouble, jni::jdouble, jni::jdouble, const jni::Array&); - void easeTo(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jdouble, jni::jlong, jni::jdouble, jni::jdouble, jni::jboolean); + void easeTo(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jdouble, jni::jlong, jni::jdouble, jni::jdouble, const jni::Array&, jni::jboolean); - void flyTo(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jdouble, jni::jlong, jni::jdouble, jni::jdouble); + void flyTo(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jdouble, jni::jlong, jni::jdouble, jni::jdouble, const jni::Array&); jni::Local> getLatLng(JNIEnv&); - void setLatLng(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jlong); + void setLatLng(jni::JNIEnv&, jni::jdouble, jni::jdouble, const jni::Array&, jni::jlong); jni::Local> getCameraForLatLngBounds(jni::JNIEnv&, const jni::Object&, double top, double left, double bottom, double right, double bearing, double tilt); @@ -139,10 +139,6 @@ public: void setVisibleCoordinateBounds(JNIEnv&, const jni::Array>&, const jni::Object&, jni::jdouble, jni::jlong); - void setContentPadding(JNIEnv&, float, float, float, float); - - jni::Local> getContentPadding(JNIEnv&); - void scheduleSnapshot(jni::JNIEnv&); jni::Local> getCameraPosition(jni::JNIEnv&); @@ -259,7 +255,6 @@ private: // Ensure these are initialised last std::unique_ptr map; - mbgl::EdgeInsets insets; }; } // namespace android diff --git a/platform/android/src/snapshotter/map_snapshotter.cpp b/platform/android/src/snapshotter/map_snapshotter.cpp index 2d37710605..0b38269ada 100644 --- a/platform/android/src/snapshotter/map_snapshotter.cpp +++ b/platform/android/src/snapshotter/map_snapshotter.cpp @@ -38,7 +38,7 @@ MapSnapshotter::MapSnapshotter(jni::JNIEnv& _env, optional cameraOptions; if (position) { - cameraOptions = CameraPosition::getCameraOptions(_env, position); + cameraOptions = CameraPosition::getCameraOptions(_env, position, pixelRatio); } optional bounds; @@ -124,7 +124,7 @@ void MapSnapshotter::setSize(JNIEnv&, jni::jint width, jni::jint height) { } void MapSnapshotter::setCameraPosition(JNIEnv& env, const jni::Object& position) { - auto options = CameraPosition::getCameraOptions(env, position); + auto options = CameraPosition::getCameraOptions(env, position, pixelRatio); snapshotter->setCameraOptions(options); } -- cgit v1.2.1 From 62e1e03c09bbcd0240ed4bd7eeacc156d6b94cc3 Mon Sep 17 00:00:00 2001 From: Mikhail Pozdnyakov Date: Thu, 29 Aug 2019 13:47:59 +0300 Subject: [core] Move semantics for OfflineDownload::ensureResource() --- platform/default/include/mbgl/storage/offline_download.hpp | 2 +- platform/default/src/mbgl/storage/offline_download.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/platform/default/include/mbgl/storage/offline_download.hpp b/platform/default/include/mbgl/storage/offline_download.hpp index 84b319fb5a..fe5d26f9eb 100644 --- a/platform/default/include/mbgl/storage/offline_download.hpp +++ b/platform/default/include/mbgl/storage/offline_download.hpp @@ -46,7 +46,7 @@ private: * While the request is in progress, it is recorded in `requests`. If the download * is deactivated, all in progress requests are cancelled. */ - void ensureResource(const Resource&, std::function = {}); + void ensureResource(Resource&&, std::function = {}); void onMapboxTileCountLimitExceeded(); diff --git a/platform/default/src/mbgl/storage/offline_download.cpp b/platform/default/src/mbgl/storage/offline_download.cpp index bdcc99cc15..e27e645a13 100644 --- a/platform/default/src/mbgl/storage/offline_download.cpp +++ b/platform/default/src/mbgl/storage/offline_download.cpp @@ -228,7 +228,7 @@ void OfflineDownload::activateDownload() { styleResource.setPriority(Resource::Priority::Low); styleResource.setUsage(Resource::Usage::Offline); - ensureResource(styleResource, [&](Response styleResponse) { + ensureResource(std::move(styleResource), [&](Response styleResponse) { status.requiredResourceCountIsPrecise = true; style::Parser parser; @@ -250,7 +250,7 @@ void OfflineDownload::activateDownload() { sourceResource.setPriority(Resource::Priority::Low); sourceResource.setUsage(Resource::Usage::Offline); - ensureResource(sourceResource, [=](Response sourceResponse) { + ensureResource(std::move(sourceResource), [=](Response sourceResponse) { style::conversion::Error error; optional tileset = style::conversion::convertJSON(*sourceResponse.data, error); if (tileset) { @@ -358,7 +358,7 @@ void OfflineDownload::continueDownload() { } while (!resourcesRemaining.empty() && requests.size() < onlineFileSource.getMaximumConcurrentRequests()) { - ensureResource(resourcesRemaining.front()); + ensureResource(std::move(resourcesRemaining.front())); resourcesRemaining.pop_front(); } } @@ -391,7 +391,7 @@ void OfflineDownload::queueTiles(SourceType type, uint16_t tileSize, const Tiles }); } -void OfflineDownload::ensureResource(const Resource& resource, +void OfflineDownload::ensureResource(Resource&& resource, std::function callback) { assert(resource.priority == Resource::Priority::Low); assert(resource.usage == Resource::Usage::Offline); -- cgit v1.2.1 From 0c80f3a383ba7a9844fa5f2379bb900d462fdd0e Mon Sep 17 00:00:00 2001 From: Mikhail Pozdnyakov Date: Thu, 29 Aug 2019 14:10:41 +0300 Subject: [core] Add OfflineDatabase::markUsedResources() --- platform/default/include/mbgl/storage/offline_database.hpp | 1 + platform/default/src/mbgl/storage/offline_database.cpp | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/platform/default/include/mbgl/storage/offline_database.hpp b/platform/default/include/mbgl/storage/offline_database.hpp index afce87b542..fe286575a4 100644 --- a/platform/default/include/mbgl/storage/offline_database.hpp +++ b/platform/default/include/mbgl/storage/offline_database.hpp @@ -92,6 +92,7 @@ public: bool offlineMapboxTileCountLimitExceeded(); uint64_t getOfflineMapboxTileCount(); bool exceedsOfflineMapboxTileCountLimit(const Resource&); + void markUsedResources(int64_t regionID, const std::list&); private: void initialize(); diff --git a/platform/default/src/mbgl/storage/offline_database.cpp b/platform/default/src/mbgl/storage/offline_database.cpp index d85b560d5a..31442c1bde 100644 --- a/platform/default/src/mbgl/storage/offline_database.cpp +++ b/platform/default/src/mbgl/storage/offline_database.cpp @@ -1278,6 +1278,19 @@ bool OfflineDatabase::exceedsOfflineMapboxTileCountLimit(const Resource& resourc && offlineMapboxTileCountLimitExceeded(); } +void OfflineDatabase::markUsedResources(int64_t regionID, const std::list& resources) try { + if (!db) { + initialize(); + } + mapbox::sqlite::Transaction transaction(*db); + for (const auto& resource : resources) { + markUsed(regionID, resource); + } + transaction.commit(); +} catch (const mapbox::sqlite::Exception& ex) { + handleError(ex, "mark resources as used"); +} + std::exception_ptr OfflineDatabase::resetDatabase() try { removeExisting(); initialize(); -- cgit v1.2.1 From f84e46ad2690e3914efe830682b41dd55dc945f3 Mon Sep 17 00:00:00 2001 From: Mikhail Pozdnyakov Date: Thu, 29 Aug 2019 14:39:57 +0300 Subject: [core] Mark offline region resources in batches --- .../include/mbgl/storage/offline_database.hpp | 4 +-- .../include/mbgl/storage/offline_download.hpp | 2 ++ .../default/src/mbgl/storage/offline_database.cpp | 20 +++--------- .../default/src/mbgl/storage/offline_download.cpp | 31 +++++++++++++----- test/storage/offline_database.test.cpp | 38 ++++++++++------------ 5 files changed, 48 insertions(+), 47 deletions(-) diff --git a/platform/default/include/mbgl/storage/offline_database.hpp b/platform/default/include/mbgl/storage/offline_database.hpp index fe286575a4..e19dcfade9 100644 --- a/platform/default/include/mbgl/storage/offline_database.hpp +++ b/platform/default/include/mbgl/storage/offline_database.hpp @@ -78,8 +78,8 @@ public: std::exception_ptr invalidateRegion(int64_t regionID); // Return value is (response, stored size) - optional> getRegionResource(int64_t regionID, const Resource&); - optional hasRegionResource(int64_t regionID, const Resource&); + optional> getRegionResource(const Resource&); + optional hasRegionResource(const Resource&); uint64_t putRegionResource(int64_t regionID, const Resource&, const Response&); void putRegionResources(int64_t regionID, const std::list>&, OfflineRegionStatus&); diff --git a/platform/default/include/mbgl/storage/offline_download.hpp b/platform/default/include/mbgl/storage/offline_download.hpp index fe5d26f9eb..53b42ae9d1 100644 --- a/platform/default/include/mbgl/storage/offline_download.hpp +++ b/platform/default/include/mbgl/storage/offline_download.hpp @@ -60,10 +60,12 @@ private: std::list> requests; std::unordered_set requiredSourceURLs; std::deque resourcesRemaining; + std::list resourcesToBeMarkedAsUsed; std::list> buffer; void queueResource(Resource&&); void queueTiles(style::SourceType, uint16_t tileSize, const Tileset&); + void markPendingUsedResources(); }; } // namespace mbgl diff --git a/platform/default/src/mbgl/storage/offline_database.cpp b/platform/default/src/mbgl/storage/offline_database.cpp index 31442c1bde..83eea7bcc4 100644 --- a/platform/default/src/mbgl/storage/offline_database.cpp +++ b/platform/default/src/mbgl/storage/offline_database.cpp @@ -877,27 +877,15 @@ std::exception_ptr OfflineDatabase::deleteRegion(OfflineRegion&& region) try { return std::current_exception(); } -optional> OfflineDatabase::getRegionResource(int64_t regionID, const Resource& resource) try { - auto response = getInternal(resource); - - if (response) { - markUsed(regionID, resource); - } - - return response; +optional> OfflineDatabase::getRegionResource(const Resource& resource) try { + return getInternal(resource); } catch (const mapbox::sqlite::Exception& ex) { handleError(ex, "read region resource"); return nullopt; } -optional OfflineDatabase::hasRegionResource(int64_t regionID, const Resource& resource) try { - auto response = hasInternal(resource); - - if (response) { - markUsed(regionID, resource); - } - - return response; +optional OfflineDatabase::hasRegionResource(const Resource& resource) try { + return hasInternal(resource); } catch (const mapbox::sqlite::Exception& ex) { handleError(ex, "query region resource"); return nullopt; diff --git a/platform/default/src/mbgl/storage/offline_download.cpp b/platform/default/src/mbgl/storage/offline_download.cpp index e27e645a13..d28c849541 100644 --- a/platform/default/src/mbgl/storage/offline_download.cpp +++ b/platform/default/src/mbgl/storage/offline_download.cpp @@ -353,10 +353,13 @@ void OfflineDownload::activateDownload() { */ void OfflineDownload::continueDownload() { if (resourcesRemaining.empty() && status.complete()) { + markPendingUsedResources(); setState(OfflineRegionDownloadState::Inactive); return; } + if (resourcesToBeMarkedAsUsed.size() >= 200) markPendingUsedResources(); + while (!resourcesRemaining.empty() && requests.size() < onlineFileSource.getMaximumConcurrentRequests()) { ensureResource(std::move(resourcesRemaining.front())); resourcesRemaining.pop_front(); @@ -391,6 +394,11 @@ void OfflineDownload::queueTiles(SourceType type, uint16_t tileSize, const Tiles }); } +void OfflineDownload::markPendingUsedResources() { + offlineDatabase.markUsedResources(id, resourcesToBeMarkedAsUsed); + resourcesToBeMarkedAsUsed.clear(); +} + void OfflineDownload::ensureResource(Resource&& resource, std::function callback) { assert(resource.priority == Resource::Priority::Low); @@ -399,24 +407,29 @@ void OfflineDownload::ensureResource(Resource&& resource, auto workRequestsIt = requests.insert(requests.begin(), nullptr); *workRequestsIt = util::RunLoop::Get()->invokeCancellable([=]() { requests.erase(workRequestsIt); - + const auto resourceKind = resource.kind; auto getResourceSizeInDatabase = [&] () -> optional { + optional result; if (!callback) { - return offlineDatabase.hasRegionResource(id, resource); - } - optional> response = offlineDatabase.getRegionResource(id, resource); - if (!response) { - return {}; + result = offlineDatabase.hasRegionResource(resource); + } else { + optional> response = offlineDatabase.getRegionResource(resource); + if (response) { + callback(response->first); + result = response->second; + } } - callback(response->first); - return response->second; + + if (result) resourcesToBeMarkedAsUsed.emplace_back(std::move(resource)); + return result; }; optional offlineResponse = getResourceSizeInDatabase(); if (offlineResponse) { + assert(!resourcesToBeMarkedAsUsed.empty()); status.completedResourceCount++; status.completedResourceSize += *offlineResponse; - if (resource.kind == Resource::Kind::Tile) { + if (resourceKind == Resource::Kind::Tile) { status.completedTileCount += 1; status.completedTileSize += *offlineResponse; } diff --git a/test/storage/offline_database.test.cpp b/test/storage/offline_database.test.cpp index 3f6db527d4..24234b0624 100644 --- a/test/storage/offline_database.test.cpp +++ b/test/storage/offline_database.test.cpp @@ -1093,8 +1093,8 @@ TEST(OfflineDatabase, HasRegionResource) { auto region = db.createRegion(definition, OfflineRegionMetadata()); ASSERT_TRUE(region); - EXPECT_FALSE(bool(db.hasRegionResource(region->getID(), Resource::style("http://example.com/1")))); - EXPECT_FALSE(bool(db.hasRegionResource(region->getID(), Resource::style("http://example.com/20")))); + EXPECT_FALSE(bool(db.hasRegionResource(Resource::style("http://example.com/1")))); + EXPECT_FALSE(bool(db.hasRegionResource(Resource::style("http://example.com/20")))); Response response; response.data = randomString(1024); @@ -1103,9 +1103,9 @@ TEST(OfflineDatabase, HasRegionResource) { db.putRegionResource(region->getID(), Resource::style("http://example.com/"s + util::toString(i)), response); } - EXPECT_TRUE(bool(db.hasRegionResource(region->getID(), Resource::style("http://example.com/1")))); - EXPECT_TRUE(bool(db.hasRegionResource(region->getID(), Resource::style("http://example.com/20")))); - EXPECT_EQ(1024, *(db.hasRegionResource(region->getID(), Resource::style("http://example.com/20")))); + EXPECT_TRUE(bool(db.hasRegionResource(Resource::style("http://example.com/1")))); + EXPECT_TRUE(bool(db.hasRegionResource(Resource::style("http://example.com/20")))); + EXPECT_EQ(1024, *(db.hasRegionResource(Resource::style("http://example.com/20")))); EXPECT_EQ(0u, log.uncheckedCount()); } @@ -1131,16 +1131,16 @@ TEST(OfflineDatabase, HasRegionResourceTile) { response.data = std::make_shared("first"); - EXPECT_FALSE(bool(db.hasRegionResource(region->getID(), resource))); + EXPECT_FALSE(bool(db.hasRegionResource(resource))); db.putRegionResource(region->getID(), resource, response); - EXPECT_TRUE(bool(db.hasRegionResource(region->getID(), resource))); - EXPECT_EQ(5, *(db.hasRegionResource(region->getID(), resource))); + EXPECT_TRUE(bool(db.hasRegionResource(resource))); + EXPECT_EQ(5, *(db.hasRegionResource(resource))); auto anotherRegion = db.createRegion(definition, OfflineRegionMetadata()); ASSERT_TRUE(anotherRegion); EXPECT_LT(region->getID(), anotherRegion->getID()); - EXPECT_TRUE(bool(db.hasRegionResource(anotherRegion->getID(), resource))); - EXPECT_EQ(5, *(db.hasRegionResource(anotherRegion->getID(), resource))); + EXPECT_TRUE(bool(db.hasRegionResource(resource))); + EXPECT_EQ(5, *(db.hasRegionResource(resource))); EXPECT_EQ(0u, log.uncheckedCount()); @@ -1488,12 +1488,12 @@ TEST(OfflineDatabase, TEST_REQUIRES_WRITE(DisallowedIO)) { EXPECT_EQ(1u, log.count(warning(ResultCode::Auth, "Can't update region metadata: authorization denied"))); EXPECT_EQ(0u, log.uncheckedCount()); - EXPECT_EQ(nullopt, db.getRegionResource(region->getID(), fixture::resource)); + EXPECT_EQ(nullopt, db.getRegionResource(fixture::resource)); EXPECT_EQ(1u, log.count(warning(ResultCode::Auth, "Can't update timestamp: authorization denied"))); EXPECT_EQ(1u, log.count(warning(ResultCode::Auth, "Can't read region resource: authorization denied"))); EXPECT_EQ(0u, log.uncheckedCount()); - EXPECT_EQ(nullopt, db.hasRegionResource(region->getID(), fixture::resource)); + EXPECT_EQ(nullopt, db.hasRegionResource(fixture::resource)); EXPECT_EQ(1u, log.count(warning(ResultCode::Auth, "Can't query region resource: authorization denied"))); EXPECT_EQ(0u, log.uncheckedCount()); @@ -1566,7 +1566,7 @@ TEST(OfflineDatabase, TEST_REQUIRES_WRITE(MergeDatabaseWithSingleRegion_Update)) EXPECT_EQ(1u, status->completedTileCount); //Verify the modified timestamp matches the tile in the sideloaded db. - auto updatedTile = db.getRegionResource(regionId, + auto updatedTile = db.getRegionResource( Resource::tile("mapbox://tiles/mapbox.satellite/{z}/{x}/{y}{ratio}.webp", 1, 0, 0, 1, Tileset::Scheme::XYZ)); EXPECT_EQ(Timestamp{ Seconds(1520409600) }, *(updatedTile->first.modified)); @@ -1586,8 +1586,7 @@ TEST(OfflineDatabase, MergeDatabaseWithSingleRegion_NoUpdate) { EXPECT_EQ(1u, result->size()); EXPECT_EQ(1u, db.listRegions()->size()); - auto regionId = result->front().getID(); - auto updatedTile = db.getRegionResource(regionId, + auto updatedTile = db.getRegionResource( Resource::tile("mapbox://tiles/mapbox.satellite/{z}/{x}/{y}{ratio}.webp", 1, 0, 0, 1, Tileset::Scheme::XYZ)); @@ -1601,14 +1600,13 @@ TEST(OfflineDatabase, MergeDatabaseWithSingleRegion_AmbientTiles) { OfflineDatabase db(":memory:"); auto result = db.mergeDatabase(filename_sideload); - auto regionId = result->front().getID(); - EXPECT_TRUE(bool(db.hasRegionResource(regionId, Resource::tile("mapbox://tiles/mapbox.satellite/{z}/{x}/{y}{ratio}.png", 1, 0, 0, 1, Tileset::Scheme::XYZ)))); + EXPECT_TRUE(bool(db.hasRegionResource(Resource::tile("mapbox://tiles/mapbox.satellite/{z}/{x}/{y}{ratio}.png", 1, 0, 0, 1, Tileset::Scheme::XYZ)))); //Ambient resources should not be copied - EXPECT_FALSE(bool(db.hasRegionResource(regionId, Resource::style("mapbox://styles/mapbox/streets-v9")))); - EXPECT_FALSE(bool(db.hasRegionResource(regionId, Resource::tile("mapbox://tiles/mapbox.satellite/{z}/{x}/{y}{ratio}.png", 1, 0, 1, 2, Tileset::Scheme::XYZ)))); - EXPECT_FALSE(bool(db.hasRegionResource(regionId, Resource::tile("mapbox://tiles/mapbox.satellite/{z}/{x}/{y}{ratio}.png", 1, 1, 1, 2, Tileset::Scheme::XYZ)))); + EXPECT_FALSE(bool(db.hasRegionResource(Resource::style("mapbox://styles/mapbox/streets-v9")))); + EXPECT_FALSE(bool(db.hasRegionResource(Resource::tile("mapbox://tiles/mapbox.satellite/{z}/{x}/{y}{ratio}.png", 1, 0, 1, 2, Tileset::Scheme::XYZ)))); + EXPECT_FALSE(bool(db.hasRegionResource(Resource::tile("mapbox://tiles/mapbox.satellite/{z}/{x}/{y}{ratio}.png", 1, 1, 1, 2, Tileset::Scheme::XYZ)))); } TEST(OfflineDatabase, MergeDatabaseWithMultipleRegions_New) { -- cgit v1.2.1 From 42ba8a7d12c5968ecb4624b7228d582fa337e697 Mon Sep 17 00:00:00 2001 From: Mikhail Pozdnyakov Date: Thu, 29 Aug 2019 16:29:58 +0300 Subject: [core] const variables for OfflineDownload batch sizes --- platform/default/src/mbgl/storage/offline_download.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/platform/default/src/mbgl/storage/offline_download.cpp b/platform/default/src/mbgl/storage/offline_download.cpp index d28c849541..d426243895 100644 --- a/platform/default/src/mbgl/storage/offline_download.cpp +++ b/platform/default/src/mbgl/storage/offline_download.cpp @@ -21,6 +21,13 @@ #include +namespace { + +const size_t kResourcesBatchSize = 64; +const size_t kMarkBatchSize = 200; + +} // namespace + namespace mbgl { using namespace style; @@ -358,7 +365,7 @@ void OfflineDownload::continueDownload() { return; } - if (resourcesToBeMarkedAsUsed.size() >= 200) markPendingUsedResources(); + if (resourcesToBeMarkedAsUsed.size() >= kMarkBatchSize) markPendingUsedResources(); while (!resourcesRemaining.empty() && requests.size() < onlineFileSource.getMaximumConcurrentRequests()) { ensureResource(std::move(resourcesRemaining.front())); @@ -461,7 +468,7 @@ void OfflineDownload::ensureResource(Resource&& resource, buffer.emplace_back(resource, onlineResponse); // Flush buffer periodically - if (buffer.size() == 64 || resourcesRemaining.size() == 0) { + if (buffer.size() == kResourcesBatchSize || resourcesRemaining.empty()) { try { offlineDatabase.putRegionResources(id, buffer, status); } catch (const MapboxTileLimitExceededException&) { -- cgit v1.2.1 From e9d0fd14dc021c86077c86d5c6a7c4839f6a2cbc Mon Sep 17 00:00:00 2001 From: Mikhail Pozdnyakov Date: Thu, 29 Aug 2019 16:46:01 +0300 Subject: [ios][android] Add change log entry --- platform/android/CHANGELOG.md | 3 +++ platform/ios/CHANGELOG.md | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index 5721df229a..e389775978 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -7,6 +7,9 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to - Introduce `clusterProperties` option for aggregated cluster properties. [#15425](https://github.com/mapbox/mapbox-gl-native/pull/15425) - Expose the `CameraPosition#padding` field and associated utility camera position builders. This gives a choice to set a persisting map padding immediately during a transition instead of setting it lazily `MapboxMap#setPadding`, which required scheduling additional transition to be applied. This also deprecates `MapboxMap#setPadding` as there should be no need for a lazy padding setter. [#15444](https://github.com/mapbox/mapbox-gl-native/pull/15444) + ### Performance improvements + - Mark used offline region resources in batches. [#15521](https://github.com/mapbox/mapbox-gl-native/pull/15521) + ## 8.3.0 - August 28, 2019 [Changes](https://github.com/mapbox/mapbox-gl-native/compare/android-v8.3.0-beta.1...android-v8.3.0) since [Mapbox Maps SDK for Android v8.3.0-beta.1](https://github.com/mapbox/mapbox-gl-native/releases/tag/android-v8.3.0-beta.1): diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 98dc2111b0..fae5599439 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -7,6 +7,10 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Fixed an issue that caused the tilt gesture to trigger too easily and conflict with pinch or pan gestures. ([#15349](https://github.com/mapbox/mapbox-gl-native/pull/15349)) * Fixed a bug with annotation view positions after camera transitions. ([#15122](https://github.com/mapbox/mapbox-gl-native/pull/15122/)) + ### Performance improvements + + * Mark used offline region resources in batches. ([#15521](https://github.com/mapbox/mapbox-gl-native/pull/15521)) + ## 5.3.0 - August 28, 2019 This release changes how offline tile requests are billed — they are now billed on a pay-as-you-go basis and all developers are able raise the offline tile limit for their users. Offline requests were previously exempt from monthly active user (MAU) billing and increasing the offline per-user tile limit to more than 6,000 tiles required the purchase of an enterprise license. By upgrading to this release, you are opting into the changes outlined in [this blog post](https://blog.mapbox.com/offline-maps-for-all-bb0fc51827be) and [#15380](https://github.com/mapbox/mapbox-gl-native/pull/15380). -- cgit v1.2.1 From 544aabd0204235579e99fe4d9b8c2de16f70e897 Mon Sep 17 00:00:00 2001 From: Bruno de Oliveira Abinader Date: Thu, 29 Aug 2019 16:52:15 +0300 Subject: [core] Add requiredTileCount to OfflineRegionStatus --- bin/offline.cpp | 2 ++ include/mbgl/storage/offline.hpp | 5 +++++ platform/default/src/mbgl/storage/offline_download.cpp | 14 ++++++++++---- test/storage/offline_download.test.cpp | 11 +++++++++++ 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/bin/offline.cpp b/bin/offline.cpp index fc8b0f1bd4..fcc6adc3ef 100644 --- a/bin/offline.cpp +++ b/bin/offline.cpp @@ -217,6 +217,8 @@ int main(int argc, char *argv[]) { std::cout << status.completedResourceCount << " / " << status.requiredResourceCount << " resources" + << status.completedTileCount << " / " << status.requiredTileCount + << "tiles" << (status.requiredResourceCountIsPrecise ? "; " : " (indeterminate); ") << status.completedResourceSize << " bytes downloaded" << " (" << bytesPerSecond << " bytes/sec)" diff --git a/include/mbgl/storage/offline.hpp b/include/mbgl/storage/offline.hpp index f702f47edc..baf43dc5f5 100644 --- a/include/mbgl/storage/offline.hpp +++ b/include/mbgl/storage/offline.hpp @@ -128,6 +128,11 @@ public: */ uint64_t completedTileCount = 0; + /** + * The number of tiles that are known to be required for this region. + */ + uint64_t requiredTileCount = 0; + /** * The cumulative size, in bytes, of all tiles that have been fully downloaded. * This is a subset of `completedResourceSize`. diff --git a/platform/default/src/mbgl/storage/offline_download.cpp b/platform/default/src/mbgl/storage/offline_download.cpp index d426243895..939164bd35 100644 --- a/platform/default/src/mbgl/storage/offline_download.cpp +++ b/platform/default/src/mbgl/storage/offline_download.cpp @@ -151,8 +151,9 @@ OfflineRegionStatus OfflineDownload::getStatus() const { auto handleTiledSource = [&] (const variant& urlOrTileset, const uint16_t tileSize) { if (urlOrTileset.is()) { - result->requiredResourceCount += - tileCount(definition, type, tileSize, urlOrTileset.get().zoomRange); + uint64_t tileSourceCount = tileCount(definition, type, tileSize, urlOrTileset.get().zoomRange); + result->requiredTileCount += tileSourceCount; + result->requiredResourceCount += tileSourceCount; } else { result->requiredResourceCount += 1; const auto& url = urlOrTileset.get(); @@ -161,8 +162,9 @@ OfflineRegionStatus OfflineDownload::getStatus() const { style::conversion::Error error; optional tileset = style::conversion::convertJSON(*sourceResponse->data, error); if (tileset) { - result->requiredResourceCount += - tileCount(definition, type, tileSize, (*tileset).zoomRange); + uint64_t tileSourceCount = tileCount(definition, type, tileSize, (*tileset).zoomRange); + result->requiredTileCount += tileSourceCount; + result->requiredResourceCount += tileSourceCount; } } else { result->requiredResourceCountIsPrecise = false; @@ -383,12 +385,16 @@ void OfflineDownload::queueResource(Resource&& resource) { resource.setPriority(Resource::Priority::Low); resource.setUsage(Resource::Usage::Offline); status.requiredResourceCount++; + if (resource.kind == mbgl::Resource::Kind::Tile) { + status.requiredTileCount++; + } resourcesRemaining.push_front(std::move(resource)); } void OfflineDownload::queueTiles(SourceType type, uint16_t tileSize, const Tileset& tileset) { tileCover(definition, type, tileSize, tileset.zoomRange, [&](const auto& tile) { status.requiredResourceCount++; + status.requiredTileCount++; auto tileResource = Resource::tile( tileset.tiles[0], definition.match([](auto& def) { return def.pixelRatio; }), diff --git a/test/storage/offline_download.test.cpp b/test/storage/offline_download.test.cpp index 9cdafaaf1e..2256e04bf1 100644 --- a/test/storage/offline_download.test.cpp +++ b/test/storage/offline_download.test.cpp @@ -150,6 +150,7 @@ TEST(OfflineDownload, InlineSource) { observer->statusChangedFn = [&] (OfflineRegionStatus status) { if (status.complete()) { + EXPECT_EQ(1u, status.completedTileCount); EXPECT_EQ(2u, status.completedResourceCount); EXPECT_EQ(test.size, status.completedResourceSize); EXPECT_TRUE(status.requiredResourceCountIsPrecise); @@ -253,6 +254,7 @@ TEST(OfflineDownload, Activate) { observer->statusChangedFn = [&] (OfflineRegionStatus status) { if (status.complete()) { + EXPECT_EQ(status.completedTileCount, status.requiredTileCount); EXPECT_EQ(264u, status.completedResourceCount); // 256 glyphs, 2 sprite images, 2 sprite jsons, 1 tile, 1 style, source, image EXPECT_EQ(test.size, status.completedResourceSize); @@ -260,6 +262,7 @@ TEST(OfflineDownload, Activate) { OfflineRegionStatus computedStatus = download.getStatus(); EXPECT_EQ(OfflineRegionDownloadState::Inactive, computedStatus.downloadState); EXPECT_EQ(status.requiredResourceCount, computedStatus.requiredResourceCount); + EXPECT_EQ(status.requiredTileCount, computedStatus.requiredTileCount); EXPECT_EQ(status.completedResourceCount, computedStatus.completedResourceCount); EXPECT_EQ(status.completedResourceSize, computedStatus.completedResourceSize); EXPECT_EQ(status.completedTileCount, computedStatus.completedTileCount); @@ -330,6 +333,7 @@ TEST(OfflineDownload, ExcludeIdeographs) { observer->statusChangedFn = [&] (OfflineRegionStatus status) { if (status.complete()) { + EXPECT_EQ(status.completedTileCount, status.requiredTileCount); EXPECT_EQ(138u, status.completedResourceCount); // 130 glyphs, 2 sprite images, 2 sprite jsons, 1 tile, 1 style, source, image EXPECT_EQ(test.size, status.completedResourceSize); @@ -337,6 +341,7 @@ TEST(OfflineDownload, ExcludeIdeographs) { OfflineRegionStatus computedStatus = download.getStatus(); EXPECT_EQ(OfflineRegionDownloadState::Inactive, computedStatus.downloadState); EXPECT_EQ(status.requiredResourceCount, computedStatus.requiredResourceCount); + EXPECT_EQ(status.requiredTileCount, computedStatus.requiredTileCount); EXPECT_EQ(status.completedResourceCount, computedStatus.completedResourceCount); EXPECT_EQ(status.completedResourceSize, computedStatus.completedResourceSize); EXPECT_EQ(status.completedTileCount, computedStatus.completedTileCount); @@ -391,6 +396,8 @@ TEST(OfflineDownload, GetStatusNoResources) { EXPECT_EQ(0u, status.completedResourceCount); EXPECT_EQ(0u, status.completedResourceSize); EXPECT_EQ(1u, status.requiredResourceCount); + EXPECT_EQ(0u, status.completedTileCount); + EXPECT_EQ(0u, status.requiredTileCount); EXPECT_FALSE(status.requiredResourceCountIsPrecise); EXPECT_FALSE(status.complete()); } @@ -414,6 +421,8 @@ TEST(OfflineDownload, GetStatusStyleComplete) { EXPECT_EQ(1u, status.completedResourceCount); EXPECT_EQ(test.size, status.completedResourceSize); EXPECT_EQ(263u, status.requiredResourceCount); + EXPECT_EQ(0u, status.completedTileCount); + EXPECT_EQ(0u, status.requiredTileCount); EXPECT_FALSE(status.requiredResourceCountIsPrecise); EXPECT_FALSE(status.complete()); } @@ -441,6 +450,8 @@ TEST(OfflineDownload, GetStatusStyleAndSourceComplete) { EXPECT_EQ(2u, status.completedResourceCount); EXPECT_EQ(test.size, status.completedResourceSize); EXPECT_EQ(264u, status.requiredResourceCount); + EXPECT_EQ(0u, status.completedTileCount); + EXPECT_EQ(1u, status.requiredTileCount); EXPECT_TRUE(status.requiredResourceCountIsPrecise); EXPECT_FALSE(status.complete()); } -- cgit v1.2.1