diff options
50 files changed, 1084 insertions, 1064 deletions
diff --git a/.gitignore b/.gitignore index 0f4fa77858..b32baaac09 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,8 @@ xcuserdata /test/fixtures/api/2.png /test/fixtures/offline_database/offline.db /test/fixtures/offline_database/offline.db-* +/test/fixtures/offline_database/satellite.db +/test/fixtures/offline_database/satellite.db-* /test/fixtures/offline_database/invalid.db /test/fixtures/offline_database/invalid.db-* /test/fixtures/offline_database/migrated.db @@ -38,4 +40,4 @@ test/fixtures/storage/assets.zip # Generated list files from code generation /scripts/generate-cmake-files.list /scripts/generate-shaders.list -/scripts/generate-style-code.list
\ No newline at end of file +/scripts/generate-style-code.list diff --git a/INSTALL.md b/INSTALL.md index a28348d15c..e5d059e1bb 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -47,7 +47,7 @@ See the relevant SDK documentation for next steps: * [Maps SDK for Android](platform/android/) * [Maps SDK for iOS](platform/ios/) * [Maps SDK for macOS](platform/macos/) -* [Mapbox Qt SDK](platform/qt/) +* [Maps SDK for Qt](platform/qt/) * [Mapbox GL Native on Linux](platform/linux/) * [node-mapbox-gl-native](platform/node/) @@ -687,6 +687,11 @@ endif android-configuration: platform/android/gradle/configuration.gradle cat platform/android/gradle/configuration.gradle +# Creates a dependency graph using Graphviz +.PHONY: android-graph +android-graph: + cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=none :MapboxGLAndroidSDK:generateDependencyGraphMapboxLibraries + #### Miscellaneous targets ##################################################### .PHONY: style-code @@ -13,7 +13,7 @@ This repository hosts the cross-platform Mapbox GL Native library, plus convenie | [Mapbox Maps SDK for iOS](platform/ios/) | Objective-C or Swift | [![Circle CI build status](https://circleci.com/gh/mapbox/mapbox-gl-native.svg?style=shield)](https://circleci.com/gh/mapbox/workflows/mapbox-gl-native/tree/master) | | [Mapbox Maps SDK for macOS](platform/macos/) | Objective-C, Swift, or AppleScript | [![Circle CI build status](https://circleci.com/gh/mapbox/mapbox-gl-native.svg?style=shield)](https://circleci.com/gh/mapbox/workflows/mapbox-gl-native/tree/master) | | [node-mapbox-gl-native](platform/node/) | Node.js | [![Circle CI build status](https://circleci.com/gh/mapbox/mapbox-gl-native.svg?style=shield)](https://circleci.com/gh/mapbox/workflows/mapbox-gl-native/tree/master) | -| [Mapbox Qt SDK](platform/qt) | C++03 | [![Circle CI build status](https://circleci.com/gh/mapbox/mapbox-gl-native.svg?style=shield)](https://circleci.com/gh/mapbox/workflows/mapbox-gl-native/tree/master) [![AppVeyor CI build status](https://ci.appveyor.com/api/projects/status/3q12kbcooc6df8uc?svg=true)](https://ci.appveyor.com/project/Mapbox/mapbox-gl-native) | +| [Mapbox Maps SDK for Qt](platform/qt) | C++03 | [![Circle CI build status](https://circleci.com/gh/mapbox/mapbox-gl-native.svg?style=shield)](https://circleci.com/gh/mapbox/workflows/mapbox-gl-native/tree/master) [![AppVeyor CI build status](https://ci.appveyor.com/api/projects/status/3q12kbcooc6df8uc?svg=true)](https://ci.appveyor.com/project/Mapbox/mapbox-gl-native) | Additional Mapbox GL Native–based libraries for **hybrid applications** are developed outside of this repository: @@ -21,7 +21,7 @@ Additional Mapbox GL Native–based libraries for **hybrid applications** are de | ---------------------------------------- | --------|-----|------------ | | [React Native](https://github.com/mapbox/react-native-mapbox-gl/) ([npm](https://www.npmjs.com/package/react-native-mapbox-gl)) | :white_check_mark: | :white_check_mark: | Mapbox | | [Apache Cordova](http://plugins.telerik.com/cordova/plugin/mapbox/) ([npm](https://www.npmjs.com/package/cordova-plugin-mapbox)) | :white_check_mark: | :white_check_mark: | Telerik | -| [NativeScript](http://plugins.telerik.com/nativescript/plugin/mapbox/) ([npm](https://www.npmjs.com/package/nativescript-mapbox/)) | :white_check_mark: | :white_check_mark: | Telerik | +| [NativeScript](https://market.nativescript.org/plugins/nativescript-mapbox/) ([npm](https://www.npmjs.com/package/nativescript-mapbox/)) | :white_check_mark: | :white_check_mark: | Telerik | | [Xamarin](https://components.xamarin.com/view/mapboxsdk/) | :white_check_mark: | :white_check_mark: | Xamarin | If your platform or hybrid application framework isn’t listed here, consider embedding [Mapbox GL JS](https://github.com/mapbox/mapbox-gl-js) using the standard Web capabilities on your platform. diff --git a/misc/ca-bundle.crt b/misc/ca-bundle.crt index 29691b9733..2ec7884afd 100644 --- a/misc/ca-bundle.crt +++ b/misc/ca-bundle.crt @@ -1,7 +1,7 @@ ## ## Bundle of CA Root Certificates ## -## Certificate data from Mozilla as of: Wed Jan 17 00:48:54 2018 GMT +## Certificate data from Mozilla as of: Mon Apr 16 12:40:48 2018 GMT ## ## This is a bundle of X.509 certificates of public Certificate Authorities ## (CA). These were automatically extracted from Mozilla's root certificates @@ -14,7 +14,7 @@ ## Just configure this file as the SSLCACertificateFile. ## ## Conversion done with mk-ca-bundle.pl version 1.27. -## SHA256: a3ac15b98179dd2f3c5de076d10b1d53048754372f7207c2f327510cdd78fbd8 +## SHA256: 704f02707ec6b4c4a7597a8c6039b020def11e64f3ef0605a9c3543d48038a57 ## @@ -446,60 +446,6 @@ EtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLH llpwrN9M -----END CERTIFICATE----- -Camerfirma Chambers of Commerce Root -==================================== ------BEGIN CERTIFICATE----- -MIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe -QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i -ZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAx -NjEzNDNaFw0zNzA5MzAxNjEzNDRaMH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZp -cm1hIFNBIENJRiBBODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3Jn -MSIwIAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0BAQEFAAOC -AQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtbunXF/KGIJPov7coISjlU -xFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0dBmpAPrMMhe5cG3nCYsS4No41XQEMIwRH -NaqbYE6gZj3LJgqcQKH0XZi/caulAGgq7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jW -DA+wWFjbw2Y3npuRVDM30pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFV -d9oKDMyXroDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIGA1Ud -EwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5jaGFtYmVyc2lnbi5v -cmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p26EpW1eLTXYGduHRooowDgYDVR0P -AQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hh -bWJlcnNpZ24ub3JnMCcGA1UdEgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYD -VR0gBFEwTzBNBgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz -aWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEBAAxBl8IahsAi -fJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZdp0AJPaxJRUXcLo0waLIJuvvD -L8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wN -UPf6s+xCX6ndbcj0dc97wXImsQEcXCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/n -ADydb47kMgkdTXg0eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1 -erfutGWaIZDgqtCYvDi1czyL+Nw= ------END CERTIFICATE----- - -Camerfirma Global Chambersign Root -================================== ------BEGIN CERTIFICATE----- -MIIExTCCA62gAwIBAgIBADANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe -QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i -ZXJzaWduLm9yZzEgMB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwHhcNMDMwOTMwMTYx -NDE4WhcNMzcwOTMwMTYxNDE4WjB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMeQUMgQ2FtZXJmaXJt -YSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEg -MB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwggEgMA0GCSqGSIb3DQEBAQUAA4IBDQAw -ggEIAoIBAQCicKLQn0KuWxfH2H3PFIP8T8mhtxOviteePgQKkotgVvq0Mi+ITaFgCPS3CU6gSS9J -1tPfnZdan5QEcOw/Wdm3zGaLmFIoCQLfxS+EjXqXd7/sQJ0lcqu1PzKY+7e3/HKE5TWH+VX6ox8O -by4o3Wmg2UIQxvi1RMLQQ3/bvOSiPGpVeAp3qdjqGTK3L/5cPxvusZjsyq16aUXjlg9V9ubtdepl -6DJWk0aJqCWKZQbua795B9Dxt6/tLE2Su8CoX6dnfQTyFQhwrJLWfQTSM/tMtgsL+xrJxI0DqX5c -8lCrEqWhz0hQpe/SyBoT+rB/sYIcd2oPX9wLlY/vQ37mRQklAgEDo4IBUDCCAUwwEgYDVR0TAQH/ -BAgwBgEB/wIBDDA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3JsLmNoYW1iZXJzaWduLm9yZy9j -aGFtYmVyc2lnbnJvb3QuY3JsMB0GA1UdDgQWBBRDnDafsJ4wTcbOX60Qq+UDpfqpFDAOBgNVHQ8B -Af8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgAHMCoGA1UdEQQjMCGBH2NoYW1iZXJzaWducm9vdEBj -aGFtYmVyc2lnbi5vcmcwKgYDVR0SBCMwIYEfY2hhbWJlcnNpZ25yb290QGNoYW1iZXJzaWduLm9y -ZzBbBgNVHSAEVDBSMFAGCysGAQQBgYcuCgEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly9jcHMuY2hh -bWJlcnNpZ24ub3JnL2Nwcy9jaGFtYmVyc2lnbnJvb3QuaHRtbDANBgkqhkiG9w0BAQUFAAOCAQEA -PDtwkfkEVCeR4e3t/mh/YV3lQWVPMvEYBZRqHN4fcNs+ezICNLUMbKGKfKX0j//U2K0X1S0E0T9Y -gOKBWYi+wONGkyT+kL0mojAt6JcmVzWJdJYY9hXiryQZVgICsroPFOrGimbBhkVVi76SvpykBMdJ -PJ7oKXqJ1/6v/2j1pReQvayZzKWGVwlnRtvWFsJG8eSpUPWP0ZIV018+xgBJOm5YstHRJw0lyDL4 -IBHNfTIzSJRUTN3cecQwn+uOuFW114hcxWokPbLTBQNRxgfvzBRydD1ucs4YKIxKoHflCStFREes -t2d/AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A== ------END CERTIFICATE----- - XRamp Global CA Root ==================== -----BEGIN CERTIFICATE----- @@ -710,30 +656,6 @@ RLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubS fZGL+T0yjWW06XyxV3bqxbYoOb8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ -----END CERTIFICATE----- -DST ACES CA X6 -============== ------BEGIN CERTIFICATE----- -MIIECTCCAvGgAwIBAgIQDV6ZCtadt3js2AdWO4YV2TANBgkqhkiG9w0BAQUFADBbMQswCQYDVQQG -EwJVUzEgMB4GA1UEChMXRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QxETAPBgNVBAsTCERTVCBBQ0VT -MRcwFQYDVQQDEw5EU1QgQUNFUyBDQSBYNjAeFw0wMzExMjAyMTE5NThaFw0xNzExMjAyMTE5NTha -MFsxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdDERMA8GA1UE -CxMIRFNUIEFDRVMxFzAVBgNVBAMTDkRTVCBBQ0VTIENBIFg2MIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEAuT31LMmU3HWKlV1j6IR3dma5WZFcRt2SPp/5DgO0PWGSvSMmtWPuktKe1jzI -DZBfZIGxqAgNTNj50wUoUrQBJcWVHAx+PhCEdc/BGZFjz+iokYi5Q1K7gLFViYsx+tC3dr5BPTCa -pCIlF3PoHuLTrCq9Wzgh1SpL11V94zpVvddtawJXa+ZHfAjIgrrep4c9oW24MFbCswKBXy314pow -GCi4ZtPLAZZv6opFVdbgnf9nKxcCpk4aahELfrd755jWjHZvwTvbUJN+5dCOHze4vbrGn2zpfDPy -MjwmR/onJALJfh1biEITajV8fTXpLmaRcpPVMibEdPVTo7NdmvYJywIDAQABo4HIMIHFMA8GA1Ud -EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgHGMB8GA1UdEQQYMBaBFHBraS1vcHNAdHJ1c3Rkc3Qu -Y29tMGIGA1UdIARbMFkwVwYKYIZIAWUDAgEBATBJMEcGCCsGAQUFBwIBFjtodHRwOi8vd3d3LnRy -dXN0ZHN0LmNvbS9jZXJ0aWZpY2F0ZXMvcG9saWN5L0FDRVMtaW5kZXguaHRtbDAdBgNVHQ4EFgQU -CXIGThhDD+XWzMNqizF7eI+og7gwDQYJKoZIhvcNAQEFBQADggEBAKPYjtay284F5zLNAdMEA+V2 -5FYrnJmQ6AgwbN99Pe7lv7UkQIRJ4dEorsTCOlMwiPH1d25Ryvr/ma8kXxug/fKshMrfqfBfBC6t -Fr8hlxCBPeP/h40y3JTlR4peahPJlJU90u7INJXQgNStMgiAVDzgvVJT11J8smk/f3rPanTK+gQq -nExaBqXpIK1FZg9p8d2/6eMyi/rgwYZNcjwu2JN4Cir42NInPRmJX1p7ijvMDNpRrscL9yuwNwXs -vFcj4jjSm2jzVhKIT0J8uDHEtdvkyCE06UgRNe76x5JXxZ805Mf29w4LTJxoeHtxMcfrHuBnQfO3 -oKfN5XozNmr6mis= ------END CERTIFICATE----- - SwissSign Gold CA - G2 ====================== -----BEGIN CERTIFICATE----- @@ -976,27 +898,6 @@ FAkK+qDmfQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdvGDeA U/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= -----END CERTIFICATE----- -Security Communication EV RootCA1 -================================= ------BEGIN CERTIFICATE----- -MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJKUDElMCMGA1UEChMc -U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEqMCgGA1UECxMhU2VjdXJpdHkgQ29tbXVuaWNh -dGlvbiBFViBSb290Q0ExMB4XDTA3MDYwNjAyMTIzMloXDTM3MDYwNjAyMTIzMlowYDELMAkGA1UE -BhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKjAoBgNVBAsTIVNl -Y3VyaXR5IENvbW11bmljYXRpb24gRVYgUm9vdENBMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC -AQoCggEBALx/7FebJOD+nLpCeamIivqA4PUHKUPqjgo0No0c+qe1OXj/l3X3L+SqawSERMqm4miO -/VVQYg+kcQ7OBzgtQoVQrTyWb4vVog7P3kmJPdZkLjjlHmy1V4qe70gOzXppFodEtZDkBp2uoQSX -WHnvIEqCa4wiv+wfD+mEce3xDuS4GBPMVjZd0ZoeUWs5bmB2iDQL87PRsJ3KYeJkHcFGB7hj3R4z -ZbOOCVVSPbW9/wfrrWFVGCypaZhKqkDFMxRldAD5kd6vA0jFQFTcD4SQaCDFkpbcLuUCRarAX1T4 -bepJz11sS6/vmsJWXMY1VkJqMF/Cq/biPT+zyRGPMUzXn0kCAwEAAaNCMEAwHQYDVR0OBBYEFDVK -9U2vP9eCOKyrcWUXdYydVZPmMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqG -SIb3DQEBBQUAA4IBAQCoh+ns+EBnXcPBZsdAS5f8hxOQWsTvoMpfi7ent/HWtWS3irO4G8za+6xm -iEHO6Pzk2x6Ipu0nUBsCMCRGef4Eh3CXQHPRwMFXGZpppSeZq51ihPZRwSzJIxXYKLerJRO1RuGG -Av8mjMSIkh1W/hln8lXkgKNrnKt34VFxDSDbEJrbvXZ5B3eZKK2aXtqxT0QsNY6llsf9g/BYxnnW -mHyojf6GPgcWkuF75x3sM3Z+Qi5KhfmRiWiEA4Glm5q+4zfFVKtWOxgtQaQM+ELbmaDgcm+7XeEW -T1MKZPlO9L9OVL14bIjqv5wTJMJwaaJ/D8g8rQjJsJhAoyrniIPtd490 ------END CERTIFICATE----- - OISTE WISeKey Global Root GA CA =============================== -----BEGIN CERTIFICATE----- @@ -2027,36 +1928,6 @@ NCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqXKVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVv w9y4AyHqnxbxLFS1 -----END CERTIFICATE----- -CA Disig Root R1 -================ ------BEGIN CERTIFICATE----- -MIIFaTCCA1GgAwIBAgIJAMMDmu5QkG4oMA0GCSqGSIb3DQEBBQUAMFIxCzAJBgNVBAYTAlNLMRMw -EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp -ZyBSb290IFIxMB4XDTEyMDcxOTA5MDY1NloXDTQyMDcxOTA5MDY1NlowUjELMAkGA1UEBhMCU0sx -EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp -c2lnIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCqw3j33Jijp1pedxiy -3QRkD2P9m5YJgNXoqqXinCaUOuiZc4yd39ffg/N4T0Dhf9Kn0uXKE5Pn7cZ3Xza1lK/oOI7bm+V8 -u8yN63Vz4STN5qctGS7Y1oprFOsIYgrY3LMATcMjfF9DCCMyEtztDK3AfQ+lekLZWnDZv6fXARz2 -m6uOt0qGeKAeVjGu74IKgEH3G8muqzIm1Cxr7X1r5OJeIgpFy4QxTaz+29FHuvlglzmxZcfe+5nk -CiKxLU3lSCZpq+Kq8/v8kiky6bM+TR8noc2OuRf7JT7JbvN32g0S9l3HuzYQ1VTW8+DiR0jm3hTa -YVKvJrT1cU/J19IG32PK/yHoWQbgCNWEFVP3Q+V8xaCJmGtzxmjOZd69fwX3se72V6FglcXM6pM6 -vpmumwKjrckWtc7dXpl4fho5frLABaTAgqWjR56M6ly2vGfb5ipN0gTco65F97yLnByn1tUD3AjL -LhbKXEAz6GfDLuemROoRRRw1ZS0eRWEkG4IupZ0zXWX4Qfkuy5Q/H6MMMSRE7cderVC6xkGbrPAX -ZcD4XW9boAo0PO7X6oifmPmvTiT6l7Jkdtqr9O3jw2Dv1fkCyC2fg69naQanMVXVz0tv/wQFx1is -XxYb5dKj6zHbHzMVTdDypVP1y+E9Tmgt2BLdqvLmTZtJ5cUoobqwWsagtQIDAQABo0IwQDAPBgNV -HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUiQq0OJMa5qvum5EY+fU8PjXQ -04IwDQYJKoZIhvcNAQEFBQADggIBADKL9p1Kyb4U5YysOMo6CdQbzoaz3evUuii+Eq5FLAR0rBNR -xVgYZk2C2tXck8An4b58n1KeElb21Zyp9HWc+jcSjxyT7Ff+Bw+r1RL3D65hXlaASfX8MPWbTx9B -LxyE04nH4toCdu0Jz2zBuByDHBb6lM19oMgY0sidbvW9adRtPTXoHqJPYNcHKfyyo6SdbhWSVhlM -CrDpfNIZTUJG7L399ldb3Zh+pE3McgODWF3vkzpBemOqfDqo9ayk0d2iLbYq/J8BjuIQscTK5Gfb -VSUZP/3oNn6z4eGBrxEWi1CXYBmCAMBrTXO40RMHPuq2MU/wQppt4hF05ZSsjYSVPCGvxdpHyN85 -YmLLW1AL14FABZyb7bq2ix4Eb5YgOe2kfSnbSM6C3NQCjR0EMVrHS/BsYVLXtFHCgWzN4funodKS -ds+xDzdYpPJScWc/DIh4gInByLUfkmO+p3qKViwaqKactV2zY9ATIKHrkWzQjX2v3wvkF7mGnjix -lAxYjOBVqjtjbZqJYLhkKpLGN/R+Q0O3c+gB53+XD9fyexn9GtePyfqFa3qdnom2piiZk4hA9z7N -UaPK6u95RyG1/jLix8NRb76AdPCkwzryT+lf3xkK8jsTQ6wxpLPn6/wY1gGp8yqPNg7rtLG8t0zJ -a7+h89n07eLw4+1knj0vllJPgFOL ------END CERTIFICATE----- - CA Disig Root R2 ================ -----BEGIN CERTIFICATE----- diff --git a/platform/android/MapboxGLAndroidSDK/.gitignore b/platform/android/MapboxGLAndroidSDK/.gitignore new file mode 100644 index 0000000000..c24bd2563a --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/.gitignore @@ -0,0 +1 @@ +dependency-graph-mapbox-libraries.png diff --git a/platform/android/MapboxGLAndroidSDK/build.gradle b/platform/android/MapboxGLAndroidSDK/build.gradle index f82968051d..c43bc9112b 100644 --- a/platform/android/MapboxGLAndroidSDK/build.gradle +++ b/platform/android/MapboxGLAndroidSDK/build.gradle @@ -145,3 +145,4 @@ apply from: "${rootDir}/gradle/gradle-javadoc.gradle" apply from: "${rootDir}/gradle/gradle-publish.gradle" apply from: "${rootDir}/gradle/gradle-checkstyle.gradle" apply from: "${rootDir}/gradle/gradle-tests-staticblockremover.gradle" +apply from: "${rootDir}/gradle/gradle-dependencies-graph.gradle" diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java index 55494b72d8..05187cf333 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java @@ -240,6 +240,7 @@ public class LatLngBounds implements Parcelable { westLon = temp; } } else { + lonSpan = GeometryConstants.LONGITUDE_SPAN - lonSpan; if (westLon < eastLon) { double temp = eastLon; eastLon = westLon; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java index ab1191c0cc..d0e51f941f 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java @@ -1,6 +1,5 @@ package com.mapbox.mapboxsdk.http; - import android.content.Context; import android.content.pm.PackageInfo; import android.os.Build; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java index f0cb8d973a..929e4b4279 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java @@ -6,6 +6,8 @@ import android.content.pm.PackageManager; import android.content.res.AssetManager; import android.os.Environment; import android.support.annotation.NonNull; +import android.support.annotation.UiThread; + import com.mapbox.mapboxsdk.Mapbox; import com.mapbox.mapboxsdk.constants.MapboxConstants; import timber.log.Timber; @@ -43,6 +45,7 @@ public class FileSource { * @param context the context to derive the cache path from * @return the single instance of FileSource */ + @UiThread public static synchronized FileSource getInstance(Context context) { if (INSTANCE == null) { String cachePath = getCachePath(context); @@ -122,6 +125,8 @@ public class FileSource { initialize(Mapbox.getAccessToken(), cachePath, assetManager); } + public native boolean isActivated(); + public native void activate(); public native void deactivate(); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle index 30e989c79b..edf860a946 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle +++ b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle @@ -72,6 +72,7 @@ dependencies { androidTestImplementation dependenciesList.testRules androidTestImplementation dependenciesList.testEspressoCore androidTestImplementation dependenciesList.testEspressoIntents + androidTestImplementation dependenciesList.testEspressoContrib } apply from: "${rootDir}/gradle/gradle-make.gradle" diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/OrientationTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/OrientationTest.java index 7a1fcbf5f3..89397c30eb 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/OrientationTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/OrientationTest.java @@ -16,17 +16,17 @@ public class OrientationTest extends BaseActivityTest { @Test public void testChangeDeviceOrientation() { onView(isRoot()).perform(orientationLandscape()); - waitLoop(2200); + waitAction(2200); onView(isRoot()).perform(orientationPortrait()); - waitLoop(2500); + waitAction(2500); onView(isRoot()).perform(orientationLandscapeReverse()); - waitLoop(500); + waitAction(500); onView(isRoot()).perform(orientationPortraitReverse()); - waitLoop(1250); + waitAction(1250); onView(isRoot()).perform(orientationLandscape()); - waitLoop(750); + waitAction(750); onView(isRoot()).perform(orientationPortrait()); - waitLoop(950); + waitAction(950); onView(isRoot()).perform(orientationLandscapeReverse()); onView(isRoot()).perform(orientationPortraitReverse()); onView(isRoot()).perform(orientationLandscape()); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/action/WaitAction.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/action/WaitAction.java new file mode 100644 index 0000000000..26a3a2e4ab --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/action/WaitAction.java @@ -0,0 +1,39 @@ +package com.mapbox.mapboxsdk.testapp.action; + +import android.support.test.espresso.UiController; +import android.support.test.espresso.ViewAction; +import android.view.View; + +import org.hamcrest.Matcher; + +import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; + +public final class WaitAction implements ViewAction { + + private static final long DEFAULT_LOOP_TIME = 375; + private final long loopTime; + + public WaitAction() { + this(DEFAULT_LOOP_TIME); + } + + public WaitAction(long loopTime) { + this.loopTime = loopTime; + } + + @Override + public Matcher<View> getConstraints() { + return isDisplayed(); + } + + @Override + public String getDescription() { + return getClass().getSimpleName(); + } + + @Override + public void perform(UiController uiController, View view) { + uiController.loopMainThreadForAtLeast(loopTime); + } +} + diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/BaseActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/BaseActivityTest.java index 3f32443021..6d90c20a46 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/BaseActivityTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/BaseActivityTest.java @@ -6,18 +6,19 @@ import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.support.test.espresso.Espresso; import android.support.test.espresso.IdlingResourceTimeoutException; -import android.support.test.espresso.UiController; -import android.support.test.espresso.ViewAction; import android.support.test.rule.ActivityTestRule; -import android.view.View; + import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.testapp.action.WaitAction; import com.mapbox.mapboxsdk.testapp.utils.OnMapReadyIdlingResource; + import junit.framework.Assert; -import org.hamcrest.Matcher; + import org.junit.After; import org.junit.Before; import org.junit.Rule; + import timber.log.Timber; import static android.support.test.espresso.Espresso.onView; @@ -67,12 +68,12 @@ public abstract class BaseActivityTest { onView(withId(id)).check(matches(isDisplayed())); } - protected void waitLoop() { - waitLoop(500); + protected void waitAction() { + waitAction(500); } - protected void waitLoop(long waitTime) { - onView(withId(R.id.mapView)).perform(new LoopAction(waitTime)); + protected void waitAction(long waitTime) { + onView(withId(R.id.mapView)).perform(new WaitAction(waitTime)); } static boolean isConnected(Context context) { @@ -87,29 +88,5 @@ public abstract class BaseActivityTest { Timber.e("@After test: unregister idle resource"); Espresso.unregisterIdlingResources(idlingResource); } - - private class LoopAction implements ViewAction { - - private long loopTime; - - public LoopAction(long loopTime) { - this.loopTime = loopTime; - } - - @Override - public Matcher<View> getConstraints() { - return isDisplayed(); - } - - @Override - public String getDescription() { - return getClass().getSimpleName(); - } - - @Override - public void perform(UiController uiController, View view) { - uiController.loopMainThreadForAtLeast(loopTime); - } - } } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/CompassViewTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/CompassViewTest.java index 2a510b4dc5..26aee2de98 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/CompassViewTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/CompassViewTest.java @@ -62,7 +62,7 @@ public class CompassViewTest extends BaseActivityTest { .build() ))); onView(withId(R.id.compassView)).perform(click()); - waitLoop(); + waitAction(); onView(withId(R.id.compassView)).check(matches(not(isDisplayed()))); invoke(mapboxMap, (uiController, mapboxMap) -> { CameraPosition cameraPosition = mapboxMap.getCameraPosition(); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/storage/FileSourceTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/storage/FileSourceTest.java new file mode 100644 index 0000000000..554bc988a6 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/storage/FileSourceTest.java @@ -0,0 +1,129 @@ +package com.mapbox.mapboxsdk.testapp.storage; + +import android.os.Looper; +import android.support.test.espresso.UiController; +import android.support.test.espresso.ViewAction; +import android.support.test.rule.ActivityTestRule; +import android.support.test.runner.AndroidJUnit4; +import android.view.View; + +import com.mapbox.mapboxsdk.storage.FileSource; +import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.testapp.action.WaitAction; +import com.mapbox.mapboxsdk.testapp.activity.FeatureOverviewActivity; + +import org.hamcrest.Matcher; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static android.support.test.espresso.Espresso.onView; +import static android.support.test.espresso.Espresso.pressBack; +import static android.support.test.espresso.action.ViewActions.click; +import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; +import static android.support.test.espresso.matcher.ViewMatchers.isRoot; +import static android.support.test.espresso.matcher.ViewMatchers.withId; +import static android.support.test.espresso.matcher.ViewMatchers.withText; +import static com.mapbox.mapboxsdk.testapp.action.OrientationChangeAction.orientationLandscape; +import static junit.framework.TestCase.assertFalse; +import static junit.framework.TestCase.assertTrue; + +@RunWith(AndroidJUnit4.class) +public class FileSourceTest { + + @Rule + public ActivityTestRule<FeatureOverviewActivity> rule = new ActivityTestRule<>(FeatureOverviewActivity.class); + + private FileSource fileSource; + + @Before + public void setUp() throws Exception { + onView(withId(R.id.recyclerView)).perform(new FileSourceCreator()); + } + + @Test + public void testDefault() throws Exception { + assertFalse("FileSource should not be active", fileSource.isActivated()); + } + + @Test + public void testActivateDeactivate() throws Exception { + assertFalse("1) FileSource should not be active", fileSource.isActivated()); + onView(withId(R.id.recyclerView)).perform(new FileSourceActivator(true)); + assertTrue("2) FileSource should be active", fileSource.isActivated()); + onView(withId(R.id.recyclerView)).perform(new FileSourceActivator(false)); + assertFalse("3) FileSource should not be active", fileSource.isActivated()); + } + + @Test + public void testOpenCloseMapView() throws Exception { + assertFalse("1) FileSource should not be active", fileSource.isActivated()); + onView(withText("Simple Map")).perform(click()); + onView(withId(R.id.mapView)).perform(new WaitAction()); + assertTrue("2) FileSource should be active", fileSource.isActivated()); + onView(withId(R.id.mapView)).perform(new WaitAction()); + pressBack(); + assertFalse("3) FileSource should not be active", fileSource.isActivated()); + } + + @Test + public void testRotateMapView() throws Exception { + assertFalse("1) FileSource should not be active", fileSource.isActivated()); + onView(withText("Simple Map")).perform(click()); + onView(withId(R.id.mapView)).perform(new WaitAction()); + onView(isRoot()).perform(orientationLandscape()); + onView(withId(R.id.mapView)).perform(new WaitAction()); + assertTrue("2) FileSource should be active", fileSource.isActivated()); + onView(withId(R.id.mapView)).perform(new WaitAction()); + pressBack(); + assertFalse("3) FileSource should not be active", fileSource.isActivated()); + } + + private class FileSourceCreator implements ViewAction { + @Override + public Matcher<View> getConstraints() { + return isDisplayed(); + } + + @Override + public String getDescription() { + return "Creates the filesource instance on the UI thread"; + } + + @Override + public void perform(UiController uiController, View view) { + assertTrue(Looper.myLooper() == Looper.getMainLooper()); + fileSource = FileSource.getInstance(rule.getActivity()); + } + } + + private class FileSourceActivator implements ViewAction { + + private boolean activate; + + FileSourceActivator(boolean activate) { + this.activate = activate; + } + + @Override + public Matcher<View> getConstraints() { + return isDisplayed(); + } + + @Override + public String getDescription() { + return "Creates the filesource instance on the UI thread"; + } + + @Override + public void perform(UiController uiController, View view) { + assertTrue(Looper.myLooper() == Looper.getMainLooper()); + if (activate) { + fileSource.activate(); + } else { + fileSource.deactivate(); + } + } + } +}
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml index 442635f909..fb1d0ef8a2 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml @@ -69,9 +69,9 @@ android:value=".activity.FeatureOverviewActivity" /> </activity> <activity - android:name=".activity.annotation.AnimatedMarkerActivity" - android:description="@string/description_animated_marker" - android:label="@string/activity_animated_marker"> + android:name=".activity.annotation.AnimatedSymbolLayerActivity" + android:description="@string/description_animated_symbollayer" + android:label="@string/activity_animated_symbollayer"> <meta-data android:name="@string/category" android:value="@string/category_annotation" /> @@ -794,4 +794,4 @@ <!-- android:value="true" /> --> </application> -</manifest>
\ No newline at end of file +</manifest> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java deleted file mode 100644 index e6db071141..0000000000 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java +++ /dev/null @@ -1,283 +0,0 @@ -package com.mapbox.mapboxsdk.testapp.activity.annotation; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; -import android.animation.TypeEvaluator; -import android.animation.ValueAnimator; -import android.os.Bundle; -import android.support.annotation.DrawableRes; -import android.support.v4.content.res.ResourcesCompat; -import android.support.v7.app.AppCompatActivity; -import android.view.View; -import android.view.animation.AccelerateDecelerateInterpolator; - -import com.mapbox.geojson.Point; -import com.mapbox.mapboxsdk.annotations.Icon; -import com.mapbox.mapboxsdk.annotations.IconFactory; -import com.mapbox.mapboxsdk.annotations.Marker; -import com.mapbox.mapboxsdk.annotations.MarkerView; -import com.mapbox.mapboxsdk.annotations.MarkerViewManager; -import com.mapbox.mapboxsdk.annotations.MarkerViewOptions; -import com.mapbox.mapboxsdk.camera.CameraPosition; -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.geometry.LatLngBounds; -import com.mapbox.mapboxsdk.maps.MapView; -import com.mapbox.mapboxsdk.maps.MapboxMap; -import com.mapbox.mapboxsdk.testapp.R; -import com.mapbox.mapboxsdk.testapp.utils.IconUtils; -import com.mapbox.turf.TurfMeasurement; - -import java.util.ArrayList; -import java.util.List; -import java.util.Random; - -/** - * Test activity showcasing animating MarkerViews. - */ -public class AnimatedMarkerActivity extends AppCompatActivity { - - private MapView mapView; - private MapboxMap mapboxMap; - - private LatLng dupontCircle = new LatLng(38.90962, -77.04341); - - private Marker passengerMarker = null; - private MarkerView carMarker = null; - - private Runnable animationRunnable; - - private List<MarkerView> markerViews = new ArrayList<>(); - private boolean stopped; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_animated_marker); - - mapView = (MapView) findViewById(R.id.mapView); - mapView.onCreate(savedInstanceState); - mapView.getMapAsync(mapboxMap -> { - AnimatedMarkerActivity.this.mapboxMap = mapboxMap; - setupMap(); - - animationRunnable = () -> { - for (int i = 0; i < 10; i++) { - addRandomCar(); - } - addPassenger(); - addMainCar(); - }; - mapView.post(animationRunnable); - }); - } - - private void setupMap() { - CameraPosition cameraPosition = new CameraPosition.Builder() - .target(dupontCircle) - .zoom(15) - .build(); - mapboxMap.setCameraPosition(cameraPosition); - } - - private void addPassenger() { - if (isActivityStopped()) { - return; - } - - LatLng randomLatLng = getLatLngInBounds(); - - if (passengerMarker == null) { - Icon icon = IconUtils.drawableToIcon(this, R.drawable.ic_directions_run_black, - ResourcesCompat.getColor(getResources(), R.color.blueAccent, getTheme())); - passengerMarker = mapboxMap.addMarker(new MarkerViewOptions() - .position(randomLatLng) - .icon(icon)); - } else { - passengerMarker.setPosition(randomLatLng); - } - } - - private void addMainCar() { - if (isActivityStopped()) { - return; - } - - LatLng randomLatLng = getLatLngInBounds(); - - if (carMarker == null) { - carMarker = createCarMarker(randomLatLng, R.drawable.ic_taxi_top, - markerView -> { - // Make sure the car marker is selected so that it's always brought to the front (#5285) - mapboxMap.selectMarker(carMarker); - animateMoveToPassenger(carMarker); - }); - markerViews.add(carMarker); - } else { - carMarker.setPosition(randomLatLng); - } - } - - private void animateMoveToPassenger(final MarkerView car) { - if (isActivityStopped()) { - return; - } - - ValueAnimator animator = animateMoveMarker(car, passengerMarker.getPosition()); - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - addPassenger(); - animateMoveToPassenger(car); - } - }); - } - - protected void addRandomCar() { - markerViews.add(createCarMarker(getLatLngInBounds(), R.drawable.ic_car_top, - markerView -> randomlyMoveMarker(markerView))); - } - - private void randomlyMoveMarker(final MarkerView marker) { - if (isActivityStopped()) { - return; - } - - ValueAnimator animator = animateMoveMarker(marker, getLatLngInBounds()); - - // Add listener to restart animation on end - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - randomlyMoveMarker(marker); - } - }); - } - - private ValueAnimator animateMoveMarker(final MarkerView marker, LatLng to) { - marker.setRotation((float) getBearing(marker.getPosition(), to)); - - final ValueAnimator markerAnimator = ObjectAnimator.ofObject( - marker, "position", new LatLngEvaluator(), marker.getPosition(), to); - markerAnimator.setDuration((long) (10 * marker.getPosition().distanceTo(to))); - markerAnimator.setInterpolator(new AccelerateDecelerateInterpolator()); - - // Start - markerAnimator.start(); - - return markerAnimator; - } - - private MarkerView createCarMarker(LatLng start, @DrawableRes int carResource, - MarkerViewManager.OnMarkerViewAddedListener listener) { - Icon icon = IconFactory.getInstance(AnimatedMarkerActivity.this) - .fromResource(carResource); - - // View Markers - return mapboxMap.addMarker(new MarkerViewOptions() - .position(start) - .icon(icon), listener); - - // GL Markers -// return mapboxMap.addMarker(new MarkerOptions() -// .position(start) -// .icon(icon)); - - } - - private LatLng getLatLngInBounds() { - LatLngBounds bounds = mapboxMap.getProjection().getVisibleRegion().latLngBounds; - Random generator = new Random(); - double randomLat = bounds.getLatSouth() + generator.nextDouble() - * (bounds.getLatNorth() - bounds.getLatSouth()); - double randomLon = bounds.getLonWest() + generator.nextDouble() - * (bounds.getLonEast() - bounds.getLonWest()); - return new LatLng(randomLat, randomLon); - } - - @Override - protected void onStart() { - super.onStart(); - mapView.onStart(); - } - - @Override - protected void onResume() { - super.onResume(); - mapView.onResume(); - } - - @Override - protected void onPause() { - super.onPause(); - mapView.onPause(); - } - - @Override - protected void onStop() { - super.onStop(); - - stopped = true; - - // Stop ongoing animations, prevent memory leaks - if (mapboxMap != null) { - MarkerViewManager markerViewManager = mapboxMap.getMarkerViewManager(); - for (MarkerView markerView : markerViews) { - View view = markerViewManager.getView(markerView); - if (view != null) { - view.animate().cancel(); - } - } - } - - // onStop - mapView.onStop(); - mapView.removeCallbacks(animationRunnable); - } - - @Override - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - mapView.onSaveInstanceState(outState); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - mapView.onDestroy(); - } - - @Override - public void onLowMemory() { - super.onLowMemory(); - mapView.onLowMemory(); - } - - /** - * Evaluator for LatLng pairs - */ - private static class LatLngEvaluator implements TypeEvaluator<LatLng> { - - private LatLng latLng = new LatLng(); - - @Override - public LatLng evaluate(float fraction, LatLng startValue, LatLng endValue) { - latLng.setLatitude(startValue.getLatitude() - + ((endValue.getLatitude() - startValue.getLatitude()) * fraction)); - latLng.setLongitude(startValue.getLongitude() - + ((endValue.getLongitude() - startValue.getLongitude()) * fraction)); - return latLng; - } - } - - private double getBearing(LatLng from, LatLng to) { - return TurfMeasurement.bearing( - Point.fromLngLat(from.getLongitude(), from.getLatitude()), - Point.fromLngLat(to.getLongitude(), to.getLatitude()) - ); - } - - private boolean isActivityStopped() { - return stopped; - } -} 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 6277dffe91..5983fb367a 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 @@ -172,9 +172,12 @@ public class CameraAnimatorActivity extends AppCompatActivity implements OnMapRe if (mapboxMap == null) { return false; } - findViewById(R.id.fab).setVisibility(View.GONE); - resetCameraPosition(); - playAnimation(item.getItemId()); + + if (item.getItemId() != android.R.id.home) { + findViewById(R.id.fab).setVisibility(View.GONE); + resetCameraPosition(); + playAnimation(item.getItemId()); + } return super.onOptionsItemSelected(item); } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_marker.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_marker.xml index 0566757d58..252af714e7 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_marker.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_marker.xml @@ -10,9 +10,9 @@ android:id="@id/mapView" android:layout_width="match_parent" android:layout_height="wrap_content" - app:mapbox_cameraTargetLat="51.502615" - app:mapbox_cameraTargetLng="4.972326" - app:mapbox_cameraZoom="6" + app:mapbox_cameraTargetLat="38.90962" + app:mapbox_cameraTargetLng="-77.04341" + app:mapbox_cameraZoom="15" app:mapbox_styleUrl="@string/mapbox_style_light"/> </LinearLayout> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml index 5238176ce8..17d6ad57c6 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml @@ -19,7 +19,7 @@ <string name="description_offline">Offline Map example</string> <string name="description_update_metadata">Update metadata example</string> <string name="description_offline_region_delete">Delete region example</string> - <string name="description_animated_marker">Animate the position change of a marker</string> + <string name="description_animated_symbollayer">Animate the position change of a symbol layer</string> <string name="description_polyline">Add a polyline to a map</string> <string name="description_polygon">Add a polygon to a map</string> <string name="description_scroll_by">Scroll with pixels in x,y direction</string> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml index 89ec586950..3f011bd3ed 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml @@ -4,7 +4,7 @@ <string name="activity_map_fragment">Map Fragment</string> <string name="activity_multimap">Multiple Maps on Screen</string> <string name="activity_add_bulk_markers">Add Markers In Bulk</string> - <string name="activity_animated_marker">Animated Markers</string> + <string name="activity_animated_symbollayer">Animated SymbolLayer</string> <string name="activity_dynamic_marker">Dynamic Marker</string> <string name="activity_polyline">Polyline</string> <string name="activity_polygon">Polygon</string> diff --git a/platform/android/README.md b/platform/android/README.md index 1dbdeee343..31e0a94f2c 100644 --- a/platform/android/README.md +++ b/platform/android/README.md @@ -12,7 +12,7 @@ Alright. So, actually, you may be in the wrong place. From here on in, this READ **To install and use the Mapbox Maps SDK for Android in an application, see the [Mapbox Maps SDK for Android website](https://www.mapbox.com/install/android/).** -[![](https://www.mapbox.com/android-sdk/images/splash.png)](https://www.mapbox.com/android-sdk/) +[![](https://www.mapbox.com/android-docs/assets/overview-map-sdk-322-9abe118316efb5910b6101e222a2e57c.png)](https://www.mapbox.com/android-sdk/) ### Setup environment @@ -89,15 +89,14 @@ More information about building and distributing this project in [DISTRIBUTE.md] #### Using the SDK snapshot -Instead of using the latest stable release of the Maps SDK for Android, you can use a "snapshot" or the beta version if there is one available. Our snapshots are built every time a Github pull request adds code to this repository's `master` branch. If you'd like to use a snapshot build, your Android project's gradle file should have -SNAPSHOT appended to the SDK version number. For example `5.2.0-SNAPSHOT` or: +Instead of using the latest stable release of the Maps SDK for Android, you can use a "snapshot" or the beta version if there is one available. Our snapshots are built every time a Github pull request adds code to this repository's `master` branch. If you'd like to use a snapshot build, your Android project's gradle file should have -SNAPSHOT appended to the SDK version number. For example, the `5.2.0-SNAPSHOT` would look like: ```java // Mapbox SDK dependency -compile('com.mapbox.mapboxsdk:mapbox-android-sdk:5.2.0-SNAPSHOT@aar') { - transitive = true -} +implementation 'com.mapbox.mapboxsdk:mapbox-android-sdk:5.2.0-SNAPSHOT' ``` -You need to have the section below in your build.gradle root folder to be able to resolve the SNAPSHOT dependencies: + +You also need to have the section below in your build.gradle root folder to be able to resolve the SNAPSHOT dependencies: ``` allprojects { repositories { diff --git a/platform/android/gradle/dependencies.gradle b/platform/android/gradle/dependencies.gradle index a5c6ad0da3..69b0c4b97f 100644 --- a/platform/android/gradle/dependencies.gradle +++ b/platform/android/gradle/dependencies.gradle @@ -40,6 +40,7 @@ ext { testRules : "com.android.support.test:rules:${versions.testRunner}", testEspressoCore : "com.android.support.test.espresso:espresso-core:${versions.espresso}", testEspressoIntents : "com.android.support.test.espresso:espresso-intents:${versions.espresso}", + testEspressoContrib : "com.android.support.test.espresso:espresso-contrib:${versions.espresso}", supportAnnotations : "com.android.support:support-annotations:${versions.supportLib}", supportAppcompatV7 : "com.android.support:appcompat-v7:${versions.supportLib}", @@ -54,4 +55,4 @@ ext { leakCanaryDebug : "com.squareup.leakcanary:leakcanary-android:${versions.leakCanary}", leakCanaryRelease : "com.squareup.leakcanary:leakcanary-android-no-op:${versions.leakCanary}" ] -}
\ No newline at end of file +} diff --git a/platform/android/gradle/gradle-dependencies-graph.gradle b/platform/android/gradle/gradle-dependencies-graph.gradle new file mode 100644 index 0000000000..5cbc7b974f --- /dev/null +++ b/platform/android/gradle/gradle-dependencies-graph.gradle @@ -0,0 +1,29 @@ +buildscript { + repositories { + mavenCentral() + } + + dependencies { + classpath "com.vanniktech:gradle-dependency-graph-generator-plugin:0.3.0" + } +} + +import com.vanniktech.dependency.graph.generator.DependencyGraphGeneratorPlugin +import com.vanniktech.dependency.graph.generator.DependencyGraphGeneratorExtension.Generator +import com.vanniktech.dependency.graph.generator.dot.GraphFormattingOptions +import com.vanniktech.dependency.graph.generator.dot.Color +import com.vanniktech.dependency.graph.generator.dot.Shape +import com.vanniktech.dependency.graph.generator.dot.Style + +plugins.apply(DependencyGraphGeneratorPlugin) + +def mapboxGenerator = new Generator( + "mapboxLibraries", // Suffix for our Gradle task. + "", // Root suffix that we don't want in this case. + { dependency -> dependency.getModuleGroup().startsWith("com.mapbox.mapboxsdk") }, // Only want Mapbox libs. + { dependency -> true }, // Include transitive dependencies. +) + +dependencyGraphGenerator { + generators = [mapboxGenerator] +} diff --git a/platform/android/scripts/exclude-activity-gen.json b/platform/android/scripts/exclude-activity-gen.json index eff7fb9cbf..157808aa51 100644 --- a/platform/android/scripts/exclude-activity-gen.json +++ b/platform/android/scripts/exclude-activity-gen.json @@ -18,7 +18,7 @@ "LocationPickerActivity", "GeoJsonClusteringActivity", "RuntimeStyleTestActivity", - "AnimatedMarkerActivity", + "AnimatedSymbolLayerActivity", "ViewPagerActivity", "MapFragmentActivity", "SupportMapFragmentActivity", diff --git a/platform/android/src/file_source.cpp b/platform/android/src/file_source.cpp index d8d715dbd3..58a91f6cf0 100644 --- a/platform/android/src/file_source.cpp +++ b/platform/android/src/file_source.cpp @@ -83,6 +83,13 @@ void FileSource::pause(jni::JNIEnv&) { } } +jni::jboolean FileSource::isResumed(jni::JNIEnv&) { + if (activationCounter) { + return (jboolean) (activationCounter > 0); + } + return (jboolean) false; +} + jni::Class<FileSource> FileSource::javaClass; FileSource* FileSource::getNativePeer(jni::JNIEnv& env, jni::Object<FileSource> jFileSource) { @@ -114,7 +121,8 @@ void FileSource::registerNative(jni::JNIEnv& env) { METHOD(&FileSource::setAPIBaseUrl, "setApiBaseUrl"), METHOD(&FileSource::setResourceTransform, "setResourceTransform"), METHOD(&FileSource::resume, "activate"), - METHOD(&FileSource::pause, "deactivate") + METHOD(&FileSource::pause, "deactivate"), + METHOD(&FileSource::isResumed, "isActivated") ); } diff --git a/platform/android/src/file_source.hpp b/platform/android/src/file_source.hpp index 194f784622..e4295e1b84 100644 --- a/platform/android/src/file_source.hpp +++ b/platform/android/src/file_source.hpp @@ -45,6 +45,8 @@ public: void pause(jni::JNIEnv&); + jni::jboolean isResumed(jni::JNIEnv&); + static jni::Class<FileSource> javaClass; static FileSource* getNativePeer(jni::JNIEnv&, jni::Object<FileSource>); diff --git a/platform/android/src/geojson/point.cpp b/platform/android/src/geojson/point.cpp index aa9dc1a7f6..8a9656ea14 100644 --- a/platform/android/src/geojson/point.cpp +++ b/platform/android/src/geojson/point.cpp @@ -59,4 +59,4 @@ jni::Class<Point> Point::javaClass; } // namespace geojson } // namespace android -} // namespace mbgl
\ No newline at end of file +} // namespace mbgl diff --git a/platform/darwin/src/MGLTileSource.h b/platform/darwin/src/MGLTileSource.h index b0020e4a19..4bf09026f2 100644 --- a/platform/darwin/src/MGLTileSource.h +++ b/platform/darwin/src/MGLTileSource.h @@ -117,6 +117,7 @@ extern MGL_EXPORT const MGLTileSourceOption MGLTileSourceOptionAttributionInfos; */ extern MGL_EXPORT const MGLTileSourceOption MGLTileSourceOptionTileCoordinateSystem; + /** Tile coordinate systems that determine how tile coordinates in tile URLs are interpreted. @@ -141,6 +142,18 @@ typedef NS_ENUM(NSUInteger, MGLTileCoordinateSystem) { MGLTileCoordinateSystemTMS }; + +/** + An `NSNumber` object containing an unsigned integer that specifies the encoding + formula for raster-dem tilesets. The integer corresponds to one of + the constants described in `MGLDEMEncoding`. + + The default value for this option is `MGLDEMEncodingMapbox`. + + This option is not supported by the TileJSON spec. + */ +extern MGL_EXPORT const MGLTileSourceOption MGLTileSourceOptionDEMEncoding; + /** The encoding formula used to generate the raster-dem tileset */ diff --git a/platform/default/mbgl/storage/offline_database.cpp b/platform/default/mbgl/storage/offline_database.cpp index 65c2097182..4611e69f43 100644 --- a/platform/default/mbgl/storage/offline_database.cpp +++ b/platform/default/mbgl/storage/offline_database.cpp @@ -10,11 +10,6 @@ namespace mbgl { -OfflineDatabase::Statement::~Statement() { - stmt.reset(); - stmt.clearBindings(); -} - OfflineDatabase::OfflineDatabase(std::string path_, uint64_t maximumCacheSize_) : path(std::move(path_)), maximumCacheSize(maximumCacheSize_) { @@ -28,7 +23,7 @@ OfflineDatabase::~OfflineDatabase() { statements.clear(); db.reset(); } catch (mapbox::sqlite::Exception& ex) { - Log::Error(Event::Database, ex.code, ex.what()); + Log::Error(Event::Database, (int)ex.code, ex.what()); } } @@ -57,13 +52,13 @@ void OfflineDatabase::ensureSchema() { removeExisting(); connect(mapbox::sqlite::ReadWrite | mapbox::sqlite::Create); } catch (mapbox::sqlite::Exception& ex) { - if (ex.code != mapbox::sqlite::Exception::Code::CANTOPEN && ex.code != mapbox::sqlite::Exception::Code::NOTADB) { + if (ex.code != mapbox::sqlite::ResultCode::CantOpen && ex.code != mapbox::sqlite::ResultCode::NotADB) { Log::Error(Event::Database, "Unexpected error connecting to database: %s", ex.what()); throw; } try { - if (ex.code == mapbox::sqlite::Exception::Code::NOTADB) { + if (ex.code == mapbox::sqlite::ResultCode::NotADB) { removeExisting(); } connect(mapbox::sqlite::ReadWrite | mapbox::sqlite::Create); @@ -92,9 +87,7 @@ void OfflineDatabase::ensureSchema() { } int OfflineDatabase::userVersion() { - auto stmt = db->prepare("PRAGMA user_version"); - stmt.run(); - return stmt.get<int>(0); + return static_cast<int>(getPragma<int64_t>("PRAGMA user_version")); } void OfflineDatabase::removeExisting() { @@ -135,14 +128,12 @@ void OfflineDatabase::migrateToVersion6() { transaction.commit(); } -OfflineDatabase::Statement OfflineDatabase::getStatement(const char * sql) { +mapbox::sqlite::Statement& OfflineDatabase::getStatement(const char* sql) { auto it = statements.find(sql); - - if (it != statements.end()) { - return Statement(*it->second); + if (it == statements.end()) { + it = statements.emplace(sql, std::make_unique<mapbox::sqlite::Statement>(*db, sql)).first; } - - return Statement(*statements.emplace(sql, std::make_unique<mapbox::sqlite::Statement>(db->prepare(sql))).first->second); + return *it->second; } optional<Response> OfflineDatabase::get(const Resource& resource) { @@ -209,41 +200,40 @@ std::pair<bool, uint64_t> OfflineDatabase::putInternal(const Resource& resource, } optional<std::pair<Response, uint64_t>> OfflineDatabase::getResource(const Resource& resource) { - // clang-format off - Statement accessedStmt = getStatement( - "UPDATE resources SET accessed = ?1 WHERE url = ?2"); - // clang-format on - - accessedStmt->bind(1, util::now()); - accessedStmt->bind(2, resource.url); - accessedStmt->run(); + // Update accessed timestamp used for LRU eviction. + { + mapbox::sqlite::Query accessedQuery{ getStatement("UPDATE resources SET accessed = ?1 WHERE url = ?2") }; + accessedQuery.bind(1, util::now()); + accessedQuery.bind(2, resource.url); + accessedQuery.run(); + } // clang-format off - Statement stmt = getStatement( + mapbox::sqlite::Query query{ getStatement( // 0 1 2 3 4 5 "SELECT etag, expires, must_revalidate, modified, data, compressed " "FROM resources " - "WHERE url = ?"); + "WHERE url = ?") }; // clang-format on - stmt->bind(1, resource.url); + query.bind(1, resource.url); - if (!stmt->run()) { + if (!query.run()) { return {}; } Response response; uint64_t size = 0; - response.etag = stmt->get<optional<std::string>>(0); - response.expires = stmt->get<optional<Timestamp>>(1); - response.mustRevalidate = stmt->get<bool>(2); - response.modified = stmt->get<optional<Timestamp>>(3); + response.etag = query.get<optional<std::string>>(0); + response.expires = query.get<optional<Timestamp>>(1); + response.mustRevalidate = query.get<bool>(2); + response.modified = query.get<optional<Timestamp>>(3); - optional<std::string> data = stmt->get<optional<std::string>>(4); + auto data = query.get<optional<std::string>>(4); if (!data) { response.noContent = true; - } else if (stmt->get<bool>(5)) { + } else if (query.get<bool>(5)) { response.data = std::make_shared<std::string>(util::decompress(*data)); size = data->length(); } else { @@ -255,16 +245,13 @@ optional<std::pair<Response, uint64_t>> OfflineDatabase::getResource(const Resou } optional<int64_t> OfflineDatabase::hasResource(const Resource& resource) { - // clang-format off - Statement stmt = getStatement("SELECT length(data) FROM resources WHERE url = ?"); - // clang-format on - - stmt->bind(1, resource.url); - if (!stmt->run()) { + mapbox::sqlite::Query query{ getStatement("SELECT length(data) FROM resources WHERE url = ?") }; + query.bind(1, resource.url); + if (!query.run()) { return {}; } - return stmt->get<optional<int64_t>>(0); + return query.get<optional<int64_t>>(0); } bool OfflineDatabase::putResource(const Resource& resource, @@ -273,19 +260,19 @@ bool OfflineDatabase::putResource(const Resource& resource, bool compressed) { if (response.notModified) { // clang-format off - Statement update = getStatement( + mapbox::sqlite::Query notModifiedQuery{ getStatement( "UPDATE resources " "SET accessed = ?1, " " expires = ?2, " " must_revalidate = ?3 " - "WHERE url = ?4 "); + "WHERE url = ?4 ") }; // clang-format on - update->bind(1, util::now()); - update->bind(2, response.expires); - update->bind(3, response.mustRevalidate); - update->bind(4, resource.url); - update->run(); + notModifiedQuery.bind(1, util::now()); + notModifiedQuery.bind(2, response.expires); + notModifiedQuery.bind(3, response.mustRevalidate); + notModifiedQuery.bind(4, resource.url); + notModifiedQuery.run(); return false; } @@ -296,7 +283,7 @@ bool OfflineDatabase::putResource(const Resource& resource, mapbox::sqlite::Transaction transaction(*db, mapbox::sqlite::Transaction::Immediate); // clang-format off - Statement update = getStatement( + mapbox::sqlite::Query updateQuery{ getStatement( "UPDATE resources " "SET kind = ?1, " " etag = ?2, " @@ -306,81 +293,83 @@ bool OfflineDatabase::putResource(const Resource& resource, " accessed = ?6, " " data = ?7, " " compressed = ?8 " - "WHERE url = ?9 "); + "WHERE url = ?9 ") }; // clang-format on - update->bind(1, int(resource.kind)); - update->bind(2, response.etag); - update->bind(3, response.expires); - update->bind(4, response.mustRevalidate); - update->bind(5, response.modified); - update->bind(6, util::now()); - update->bind(9, resource.url); + updateQuery.bind(1, int(resource.kind)); + updateQuery.bind(2, response.etag); + updateQuery.bind(3, response.expires); + updateQuery.bind(4, response.mustRevalidate); + updateQuery.bind(5, response.modified); + updateQuery.bind(6, util::now()); + updateQuery.bind(9, resource.url); if (response.noContent) { - update->bind(7, nullptr); - update->bind(8, false); + updateQuery.bind(7, nullptr); + updateQuery.bind(8, false); } else { - update->bindBlob(7, data.data(), data.size(), false); - update->bind(8, compressed); + updateQuery.bindBlob(7, data.data(), data.size(), false); + updateQuery.bind(8, compressed); } - update->run(); - if (update->changes() != 0) { + updateQuery.run(); + if (updateQuery.changes() != 0) { transaction.commit(); return false; } // clang-format off - Statement insert = getStatement( + mapbox::sqlite::Query insertQuery{ getStatement( "INSERT INTO resources (url, kind, etag, expires, must_revalidate, modified, accessed, data, compressed) " - "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9) "); + "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9) ") }; // clang-format on - insert->bind(1, resource.url); - insert->bind(2, int(resource.kind)); - insert->bind(3, response.etag); - insert->bind(4, response.expires); - insert->bind(5, response.mustRevalidate); - insert->bind(6, response.modified); - insert->bind(7, util::now()); + insertQuery.bind(1, resource.url); + insertQuery.bind(2, int(resource.kind)); + insertQuery.bind(3, response.etag); + insertQuery.bind(4, response.expires); + insertQuery.bind(5, response.mustRevalidate); + insertQuery.bind(6, response.modified); + insertQuery.bind(7, util::now()); if (response.noContent) { - insert->bind(8, nullptr); - insert->bind(9, false); + insertQuery.bind(8, nullptr); + insertQuery.bind(9, false); } else { - insert->bindBlob(8, data.data(), data.size(), false); - insert->bind(9, compressed); + insertQuery.bindBlob(8, data.data(), data.size(), false); + insertQuery.bind(9, compressed); } - insert->run(); + insertQuery.run(); transaction.commit(); return true; } optional<std::pair<Response, uint64_t>> OfflineDatabase::getTile(const Resource::TileData& tile) { - // clang-format off - Statement accessedStmt = getStatement( - "UPDATE tiles " - "SET accessed = ?1 " - "WHERE url_template = ?2 " - " AND pixel_ratio = ?3 " - " AND x = ?4 " - " AND y = ?5 " - " AND z = ?6 "); - // clang-format on + { + // clang-format off + mapbox::sqlite::Query accessedQuery{ getStatement( + "UPDATE tiles " + "SET accessed = ?1 " + "WHERE url_template = ?2 " + " AND pixel_ratio = ?3 " + " AND x = ?4 " + " AND y = ?5 " + " AND z = ?6 ") }; + // clang-format on - accessedStmt->bind(1, util::now()); - accessedStmt->bind(2, tile.urlTemplate); - accessedStmt->bind(3, tile.pixelRatio); - accessedStmt->bind(4, tile.x); - accessedStmt->bind(5, tile.y); - accessedStmt->bind(6, tile.z); - accessedStmt->run(); + accessedQuery.bind(1, util::now()); + accessedQuery.bind(2, tile.urlTemplate); + accessedQuery.bind(3, tile.pixelRatio); + accessedQuery.bind(4, tile.x); + accessedQuery.bind(5, tile.y); + accessedQuery.bind(6, tile.z); + accessedQuery.run(); + } // clang-format off - Statement stmt = getStatement( + mapbox::sqlite::Query query{ getStatement( // 0 1 2, 3, 4, 5 "SELECT etag, expires, must_revalidate, modified, data, compressed " "FROM tiles " @@ -388,31 +377,31 @@ optional<std::pair<Response, uint64_t>> OfflineDatabase::getTile(const Resource: " AND pixel_ratio = ?2 " " AND x = ?3 " " AND y = ?4 " - " AND z = ?5 "); + " AND z = ?5 ") }; // clang-format on - stmt->bind(1, tile.urlTemplate); - stmt->bind(2, tile.pixelRatio); - stmt->bind(3, tile.x); - stmt->bind(4, tile.y); - stmt->bind(5, tile.z); + query.bind(1, tile.urlTemplate); + query.bind(2, tile.pixelRatio); + query.bind(3, tile.x); + query.bind(4, tile.y); + query.bind(5, tile.z); - if (!stmt->run()) { + if (!query.run()) { return {}; } Response response; uint64_t size = 0; - response.etag = stmt->get<optional<std::string>>(0); - response.expires = stmt->get<optional<Timestamp>>(1); - response.mustRevalidate = stmt->get<bool>(2); - response.modified = stmt->get<optional<Timestamp>>(3); + response.etag = query.get<optional<std::string>>(0); + response.expires = query.get<optional<Timestamp>>(1); + response.mustRevalidate = query.get<bool>(2); + response.modified = query.get<optional<Timestamp>>(3); - optional<std::string> data = stmt->get<optional<std::string>>(4); + optional<std::string> data = query.get<optional<std::string>>(4); if (!data) { response.noContent = true; - } else if (stmt->get<bool>(5)) { + } else if (query.get<bool>(5)) { response.data = std::make_shared<std::string>(util::decompress(*data)); size = data->length(); } else { @@ -425,27 +414,27 @@ optional<std::pair<Response, uint64_t>> OfflineDatabase::getTile(const Resource: optional<int64_t> OfflineDatabase::hasTile(const Resource::TileData& tile) { // clang-format off - Statement stmt = getStatement( + mapbox::sqlite::Query size{ getStatement( "SELECT length(data) " "FROM tiles " "WHERE url_template = ?1 " " AND pixel_ratio = ?2 " " AND x = ?3 " " AND y = ?4 " - " AND z = ?5 "); + " AND z = ?5 ") }; // clang-format on - stmt->bind(1, tile.urlTemplate); - stmt->bind(2, tile.pixelRatio); - stmt->bind(3, tile.x); - stmt->bind(4, tile.y); - stmt->bind(5, tile.z); + size.bind(1, tile.urlTemplate); + size.bind(2, tile.pixelRatio); + size.bind(3, tile.x); + size.bind(4, tile.y); + size.bind(5, tile.z); - if (!stmt->run()) { + if (!size.run()) { return {}; } - return stmt->get<optional<int64_t>>(0); + return size.get<optional<int64_t>>(0); } bool OfflineDatabase::putTile(const Resource::TileData& tile, @@ -454,7 +443,7 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile, bool compressed) { if (response.notModified) { // clang-format off - Statement update = getStatement( + mapbox::sqlite::Query notModifiedQuery{ getStatement( "UPDATE tiles " "SET accessed = ?1, " " expires = ?2, " @@ -463,18 +452,18 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile, " AND pixel_ratio = ?5 " " AND x = ?6 " " AND y = ?7 " - " AND z = ?8 "); + " AND z = ?8 ") }; // clang-format on - update->bind(1, util::now()); - update->bind(2, response.expires); - update->bind(3, response.mustRevalidate); - update->bind(4, tile.urlTemplate); - update->bind(5, tile.pixelRatio); - update->bind(6, tile.x); - update->bind(7, tile.y); - update->bind(8, tile.z); - update->run(); + notModifiedQuery.bind(1, util::now()); + notModifiedQuery.bind(2, response.expires); + notModifiedQuery.bind(3, response.mustRevalidate); + notModifiedQuery.bind(4, tile.urlTemplate); + notModifiedQuery.bind(5, tile.pixelRatio); + notModifiedQuery.bind(6, tile.x); + notModifiedQuery.bind(7, tile.y); + notModifiedQuery.bind(8, tile.z); + notModifiedQuery.run(); return false; } @@ -485,7 +474,7 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile, mapbox::sqlite::Transaction transaction(*db, mapbox::sqlite::Transaction::Immediate); // clang-format off - Statement update = getStatement( + mapbox::sqlite::Query updateQuery{ getStatement( "UPDATE tiles " "SET modified = ?1, " " etag = ?2, " @@ -498,78 +487,75 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile, " AND pixel_ratio = ?9 " " AND x = ?10 " " AND y = ?11 " - " AND z = ?12 "); + " AND z = ?12 ") }; // clang-format on - update->bind(1, response.modified); - update->bind(2, response.etag); - update->bind(3, response.expires); - update->bind(4, response.mustRevalidate); - update->bind(5, util::now()); - update->bind(8, tile.urlTemplate); - update->bind(9, tile.pixelRatio); - update->bind(10, tile.x); - update->bind(11, tile.y); - update->bind(12, tile.z); + updateQuery.bind(1, response.modified); + updateQuery.bind(2, response.etag); + updateQuery.bind(3, response.expires); + updateQuery.bind(4, response.mustRevalidate); + updateQuery.bind(5, util::now()); + updateQuery.bind(8, tile.urlTemplate); + updateQuery.bind(9, tile.pixelRatio); + updateQuery.bind(10, tile.x); + updateQuery.bind(11, tile.y); + updateQuery.bind(12, tile.z); if (response.noContent) { - update->bind(6, nullptr); - update->bind(7, false); + updateQuery.bind(6, nullptr); + updateQuery.bind(7, false); } else { - update->bindBlob(6, data.data(), data.size(), false); - update->bind(7, compressed); + updateQuery.bindBlob(6, data.data(), data.size(), false); + updateQuery.bind(7, compressed); } - update->run(); - if (update->changes() != 0) { + updateQuery.run(); + if (updateQuery.changes() != 0) { transaction.commit(); return false; } // clang-format off - Statement insert = getStatement( + mapbox::sqlite::Query insertQuery{ getStatement( "INSERT INTO tiles (url_template, pixel_ratio, x, y, z, modified, must_revalidate, etag, expires, accessed, data, compressed) " - "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12)"); + "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12)") }; // clang-format on - insert->bind(1, tile.urlTemplate); - insert->bind(2, tile.pixelRatio); - insert->bind(3, tile.x); - insert->bind(4, tile.y); - insert->bind(5, tile.z); - insert->bind(6, response.modified); - insert->bind(7, response.mustRevalidate); - insert->bind(8, response.etag); - insert->bind(9, response.expires); - insert->bind(10, util::now()); + insertQuery.bind(1, tile.urlTemplate); + insertQuery.bind(2, tile.pixelRatio); + insertQuery.bind(3, tile.x); + insertQuery.bind(4, tile.y); + insertQuery.bind(5, tile.z); + insertQuery.bind(6, response.modified); + insertQuery.bind(7, response.mustRevalidate); + insertQuery.bind(8, response.etag); + insertQuery.bind(9, response.expires); + insertQuery.bind(10, util::now()); if (response.noContent) { - insert->bind(11, nullptr); - insert->bind(12, false); + insertQuery.bind(11, nullptr); + insertQuery.bind(12, false); } else { - insert->bindBlob(11, data.data(), data.size(), false); - insert->bind(12, compressed); + insertQuery.bindBlob(11, data.data(), data.size(), false); + insertQuery.bind(12, compressed); } - insert->run(); + insertQuery.run(); transaction.commit(); return true; } std::vector<OfflineRegion> OfflineDatabase::listRegions() { - // clang-format off - Statement stmt = getStatement( - "SELECT id, definition, description FROM regions"); - // clang-format on + mapbox::sqlite::Query query{ getStatement("SELECT id, definition, description FROM regions") }; std::vector<OfflineRegion> result; - while (stmt->run()) { + while (query.run()) { result.push_back(OfflineRegion( - stmt->get<int64_t>(0), - decodeOfflineRegionDefinition(stmt->get<std::string>(1)), - stmt->get<std::vector<uint8_t>>(2))); + query.get<int64_t>(0), + decodeOfflineRegionDefinition(query.get<std::string>(1)), + query.get<std::vector<uint8_t>>(2))); } return result; @@ -578,39 +564,37 @@ std::vector<OfflineRegion> OfflineDatabase::listRegions() { OfflineRegion OfflineDatabase::createRegion(const OfflineRegionDefinition& definition, const OfflineRegionMetadata& metadata) { // clang-format off - Statement stmt = getStatement( + mapbox::sqlite::Query query{ getStatement( "INSERT INTO regions (definition, description) " - "VALUES (?1, ?2) "); + "VALUES (?1, ?2) ") }; // clang-format on - stmt->bind(1, encodeOfflineRegionDefinition(definition)); - stmt->bindBlob(2, metadata); - stmt->run(); + query.bind(1, encodeOfflineRegionDefinition(definition)); + query.bindBlob(2, metadata); + query.run(); - return OfflineRegion(stmt->lastInsertRowId(), definition, metadata); + return OfflineRegion(query.lastInsertRowId(), definition, metadata); } OfflineRegionMetadata OfflineDatabase::updateMetadata(const int64_t regionID, const OfflineRegionMetadata& metadata) { // clang-format off - Statement stmt = getStatement( - "UPDATE regions SET description = ?1" - "WHERE id = ?2"); + mapbox::sqlite::Query query{ getStatement( + "UPDATE regions SET description = ?1 " + "WHERE id = ?2") }; // clang-format on - stmt->bindBlob(1, metadata); - stmt->bind(2, regionID); - stmt->run(); + query.bindBlob(1, metadata); + query.bind(2, regionID); + query.run(); return metadata; } void OfflineDatabase::deleteRegion(OfflineRegion&& region) { - // clang-format off - Statement stmt = getStatement( - "DELETE FROM regions WHERE id = ?"); - // clang-format on - - stmt->bind(1, region.getID()); - stmt->run(); + { + mapbox::sqlite::Query query{ getStatement("DELETE FROM regions WHERE id = ?") }; + query.bind(1, region.getID()); + query.run(); + } evict(0); db->exec("PRAGMA incremental_vacuum"); @@ -656,7 +640,7 @@ uint64_t OfflineDatabase::putRegionResource(int64_t regionID, const Resource& re bool OfflineDatabase::markUsed(int64_t regionID, const Resource& resource) { if (resource.kind == Resource::Kind::Tile) { // clang-format off - Statement insert = getStatement( + mapbox::sqlite::Query insertQuery{ getStatement( "INSERT OR IGNORE INTO region_tiles (region_id, tile_id) " "SELECT ?1, tiles.id " "FROM tiles " @@ -664,24 +648,24 @@ bool OfflineDatabase::markUsed(int64_t regionID, const Resource& resource) { " AND pixel_ratio = ?3 " " AND x = ?4 " " AND y = ?5 " - " AND z = ?6 "); + " AND z = ?6 ") }; // clang-format on const Resource::TileData& tile = *resource.tileData; - insert->bind(1, regionID); - insert->bind(2, tile.urlTemplate); - insert->bind(3, tile.pixelRatio); - insert->bind(4, tile.x); - insert->bind(5, tile.y); - insert->bind(6, tile.z); - insert->run(); - - if (insert->changes() == 0) { + insertQuery.bind(1, regionID); + insertQuery.bind(2, tile.urlTemplate); + insertQuery.bind(3, tile.pixelRatio); + insertQuery.bind(4, tile.x); + insertQuery.bind(5, tile.y); + insertQuery.bind(6, tile.z); + insertQuery.run(); + + if (insertQuery.changes() == 0) { return false; } // clang-format off - Statement select = getStatement( + mapbox::sqlite::Query selectQuery{ getStatement( "SELECT region_id " "FROM region_tiles, tiles " "WHERE region_id != ?1 " @@ -690,58 +674,54 @@ bool OfflineDatabase::markUsed(int64_t regionID, const Resource& resource) { " AND x = ?4 " " AND y = ?5 " " AND z = ?6 " - "LIMIT 1 "); + "LIMIT 1 ") }; // clang-format on - select->bind(1, regionID); - select->bind(2, tile.urlTemplate); - select->bind(3, tile.pixelRatio); - select->bind(4, tile.x); - select->bind(5, tile.y); - select->bind(6, tile.z); - return !select->run(); + selectQuery.bind(1, regionID); + selectQuery.bind(2, tile.urlTemplate); + selectQuery.bind(3, tile.pixelRatio); + selectQuery.bind(4, tile.x); + selectQuery.bind(5, tile.y); + selectQuery.bind(6, tile.z); + return !selectQuery.run(); } else { // clang-format off - Statement insert = getStatement( + mapbox::sqlite::Query insertQuery{ getStatement( "INSERT OR IGNORE INTO region_resources (region_id, resource_id) " "SELECT ?1, resources.id " "FROM resources " - "WHERE resources.url = ?2 "); + "WHERE resources.url = ?2 ") }; // clang-format on - insert->bind(1, regionID); - insert->bind(2, resource.url); - insert->run(); + insertQuery.bind(1, regionID); + insertQuery.bind(2, resource.url); + insertQuery.run(); - if (insert->changes() == 0) { + if (insertQuery.changes() == 0) { return false; } // clang-format off - Statement select = getStatement( + mapbox::sqlite::Query selectQuery{ getStatement( "SELECT region_id " "FROM region_resources, resources " "WHERE region_id != ?1 " " AND resources.url = ?2 " - "LIMIT 1 "); + "LIMIT 1 ") }; // clang-format on - select->bind(1, regionID); - select->bind(2, resource.url); - return !select->run(); + selectQuery.bind(1, regionID); + selectQuery.bind(2, resource.url); + return !selectQuery.run(); } } OfflineRegionDefinition OfflineDatabase::getRegionDefinition(int64_t regionID) { - // clang-format off - Statement stmt = getStatement( - "SELECT definition FROM regions WHERE id = ?1"); - // clang-format on - - stmt->bind(1, regionID); - stmt->run(); + mapbox::sqlite::Query query{ getStatement("SELECT definition FROM regions WHERE id = ?1") }; + query.bind(1, regionID); + query.run(); - return decodeOfflineRegionDefinition(stmt->get<std::string>(0)); + return decodeOfflineRegionDefinition(query.get<std::string>(0)); } OfflineRegionStatus OfflineDatabase::getRegionCompletedStatus(int64_t regionID) { @@ -760,35 +740,35 @@ OfflineRegionStatus OfflineDatabase::getRegionCompletedStatus(int64_t regionID) std::pair<int64_t, int64_t> OfflineDatabase::getCompletedResourceCountAndSize(int64_t regionID) { // clang-format off - Statement stmt = getStatement( + mapbox::sqlite::Query query{ getStatement( "SELECT COUNT(*), SUM(LENGTH(data)) " "FROM region_resources, resources " "WHERE region_id = ?1 " - "AND resource_id = resources.id "); + "AND resource_id = resources.id ") }; // clang-format on - stmt->bind(1, regionID); - stmt->run(); - return { stmt->get<int64_t>(0), stmt->get<int64_t>(1) }; + query.bind(1, regionID); + query.run(); + return { query.get<int64_t>(0), query.get<int64_t>(1) }; } std::pair<int64_t, int64_t> OfflineDatabase::getCompletedTileCountAndSize(int64_t regionID) { // clang-format off - Statement stmt = getStatement( + mapbox::sqlite::Query query{ getStatement( "SELECT COUNT(*), SUM(LENGTH(data)) " "FROM region_tiles, tiles " "WHERE region_id = ?1 " - "AND tile_id = tiles.id "); + "AND tile_id = tiles.id ") }; // clang-format on - stmt->bind(1, regionID); - stmt->run(); - return { stmt->get<int64_t>(0), stmt->get<int64_t>(1) }; + query.bind(1, regionID); + query.run(); + return { query.get<int64_t>(0), query.get<int64_t>(1) }; } template <class T> -T OfflineDatabase::getPragma(const char * sql) { - Statement stmt = getStatement(sql); - stmt->run(); - return stmt->get<T>(0); +T OfflineDatabase::getPragma(const char* sql) { + mapbox::sqlite::Query query{ getStatement(sql) }; + query.run(); + return query.get<T>(0); } // Remove least-recently used resources and tiles until the used database size, @@ -813,7 +793,7 @@ bool OfflineDatabase::evict(uint64_t neededFreeSize) { // size, and because pages can get fragmented on the database. while (usedSize() + neededFreeSize + pageSize > maximumCacheSize) { // clang-format off - Statement accessedStmt = getStatement( + mapbox::sqlite::Query accessedQuery{ getStatement( "SELECT max(accessed) " "FROM ( " " SELECT accessed " @@ -829,16 +809,16 @@ bool OfflineDatabase::evict(uint64_t neededFreeSize) { " WHERE tile_id IS NULL " " ORDER BY accessed ASC LIMIT ?1 " ") " - ); - accessedStmt->bind(1, 50); + ) }; + accessedQuery.bind(1, 50); // clang-format on - if (!accessedStmt->run()) { + if (!accessedQuery.run()) { return false; } - Timestamp accessed = accessedStmt->get<Timestamp>(0); + Timestamp accessed = accessedQuery.get<Timestamp>(0); // clang-format off - Statement stmt1 = getStatement( + mapbox::sqlite::Query resourceQuery{ getStatement( "DELETE FROM resources " "WHERE id IN ( " " SELECT id FROM resources " @@ -846,14 +826,14 @@ bool OfflineDatabase::evict(uint64_t neededFreeSize) { " ON resource_id = resources.id " " WHERE resource_id IS NULL " " AND accessed <= ?1 " - ") "); + ") ") }; // clang-format on - stmt1->bind(1, accessed); - stmt1->run(); - uint64_t changes1 = stmt1->changes(); + resourceQuery.bind(1, accessed); + resourceQuery.run(); + const uint64_t resourceChanges = resourceQuery.changes(); // clang-format off - Statement stmt2 = getStatement( + mapbox::sqlite::Query tileQuery{ getStatement( "DELETE FROM tiles " "WHERE id IN ( " " SELECT id FROM tiles " @@ -861,16 +841,16 @@ bool OfflineDatabase::evict(uint64_t neededFreeSize) { " ON tile_id = tiles.id " " WHERE tile_id IS NULL " " AND accessed <= ?1 " - ") "); + ") ") }; // clang-format on - stmt2->bind(1, accessed); - stmt2->run(); - uint64_t changes2 = stmt2->changes(); + tileQuery.bind(1, accessed); + tileQuery.run(); + const uint64_t tileChanges = tileQuery.changes(); // The cached value of offlineTileCount does not need to be updated // here because only non-offline tiles can be removed by eviction. - if (changes1 == 0 && changes2 == 0) { + if (resourceChanges == 0 && tileChanges == 0) { return false; } } @@ -901,16 +881,16 @@ uint64_t OfflineDatabase::getOfflineMapboxTileCount() { } // clang-format off - Statement stmt = getStatement( + mapbox::sqlite::Query query{ getStatement( "SELECT COUNT(DISTINCT id) " "FROM region_tiles, tiles " "WHERE tile_id = tiles.id " - "AND url_template LIKE 'mapbox://%' "); + "AND url_template LIKE 'mapbox://%' ") }; // clang-format on - stmt->run(); + query.run(); - offlineMapboxTileCount = stmt->get<int64_t>(0); + offlineMapboxTileCount = query.get<int64_t>(0); return *offlineMapboxTileCount; } diff --git a/platform/default/mbgl/storage/offline_database.hpp b/platform/default/mbgl/storage/offline_database.hpp index 91b544a9e0..9673ad8212 100644 --- a/platform/default/mbgl/storage/offline_database.hpp +++ b/platform/default/mbgl/storage/offline_database.hpp @@ -15,6 +15,7 @@ namespace mapbox { namespace sqlite { class Database; class Statement; +class Query; } // namespace sqlite } // namespace mapbox @@ -66,20 +67,7 @@ private: void migrateToVersion5(); void migrateToVersion6(); - class Statement { - public: - explicit Statement(mapbox::sqlite::Statement& stmt_) : stmt(stmt_) {} - Statement(Statement&&) = default; - Statement(const Statement&) = delete; - ~Statement(); - - mapbox::sqlite::Statement* operator->() { return &stmt; }; - - private: - mapbox::sqlite::Statement& stmt; - }; - - Statement getStatement(const char *); + mapbox::sqlite::Statement& getStatement(const char *); optional<std::pair<Response, uint64_t>> getTile(const Resource::TileData&); optional<int64_t> hasTile(const Resource::TileData&); @@ -102,8 +90,8 @@ private: std::pair<int64_t, int64_t> getCompletedTileCountAndSize(int64_t regionID); const std::string path; - std::unique_ptr<::mapbox::sqlite::Database> db; - std::unordered_map<const char *, std::unique_ptr<::mapbox::sqlite::Statement>> statements; + std::unique_ptr<mapbox::sqlite::Database> db; + std::unordered_map<const char *, const std::unique_ptr<mapbox::sqlite::Statement>> statements; template <class T> T getPragma(const char *); diff --git a/platform/default/sqlite3.cpp b/platform/default/sqlite3.cpp index 2e08354fdf..8a567d602e 100644 --- a/platform/default/sqlite3.cpp +++ b/platform/default/sqlite3.cpp @@ -69,14 +69,84 @@ public: template <typename T> using optional = std::experimental::optional<T>; +static const char* codeToString(const int err) { + switch (err) { + case SQLITE_OK: return "SQLITE_OK"; + case SQLITE_ERROR: return "SQLITE_ERROR"; + case SQLITE_INTERNAL: return "SQLITE_INTERNAL"; + case SQLITE_PERM: return "SQLITE_PERM"; + case SQLITE_ABORT: return "SQLITE_ABORT"; + case SQLITE_BUSY: return "SQLITE_BUSY"; + case SQLITE_LOCKED: return "SQLITE_LOCKED"; + case SQLITE_NOMEM: return "SQLITE_NOMEM"; + case SQLITE_READONLY: return "SQLITE_READONLY"; + case SQLITE_INTERRUPT: return "SQLITE_INTERRUPT"; + case SQLITE_IOERR: return "SQLITE_IOERR"; + case SQLITE_CORRUPT: return "SQLITE_CORRUPT"; + case SQLITE_NOTFOUND: return "SQLITE_NOTFOUND"; + case SQLITE_FULL: return "SQLITE_FULL"; + case SQLITE_CANTOPEN: return "SQLITE_CANTOPEN"; + case SQLITE_PROTOCOL: return "SQLITE_PROTOCOL"; + case SQLITE_EMPTY: return "SQLITE_EMPTY"; + case SQLITE_SCHEMA: return "SQLITE_SCHEMA"; + case SQLITE_TOOBIG: return "SQLITE_TOOBIG"; + case SQLITE_CONSTRAINT: return "SQLITE_CONSTRAINT"; + case SQLITE_MISMATCH: return "SQLITE_MISMATCH"; + case SQLITE_MISUSE: return "SQLITE_MISUSE"; + case SQLITE_NOLFS: return "SQLITE_NOLFS"; + case SQLITE_AUTH: return "SQLITE_AUTH"; + case SQLITE_FORMAT: return "SQLITE_FORMAT"; + case SQLITE_RANGE: return "SQLITE_RANGE"; + case SQLITE_NOTADB: return "SQLITE_NOTADB"; + case SQLITE_NOTICE: return "SQLITE_NOTICE"; + case SQLITE_WARNING: return "SQLITE_WARNING"; + case SQLITE_ROW: return "SQLITE_ROW"; + case SQLITE_DONE: return "SQLITE_DONE"; + default: return "<unknown>"; + } +} + static void errorLogCallback(void *, const int err, const char *msg) { - if (err == SQLITE_ERROR) { - mbgl::Log::Error(mbgl::Event::Database, "%s (Code %i)", msg, err); - } else if (err == SQLITE_WARNING) { - mbgl::Log::Warning(mbgl::Event::Database, "%s (Code %i)", msg, err); - } else { - mbgl::Log::Info(mbgl::Event::Database, "%s (Code %i)", msg, err); + auto severity = mbgl::EventSeverity::Info; + + switch (err) { + case SQLITE_ERROR: // Generic error + case SQLITE_INTERNAL: // Internal logic error in SQLite + case SQLITE_PERM: // Access permission denied + case SQLITE_ABORT: // Callback routine requested an abort + case SQLITE_BUSY: // The database file is locked + case SQLITE_LOCKED: // A table in the database is locked + case SQLITE_NOMEM: // A malloc() failed + case SQLITE_READONLY: // Attempt to write a readonly database + case SQLITE_INTERRUPT: // Operation terminated by sqlite3_interrupt( + case SQLITE_IOERR: // Some kind of disk I/O error occurred + case SQLITE_CORRUPT: // The database disk image is malformed + case SQLITE_NOTFOUND: // Unknown opcode in sqlite3_file_control() + case SQLITE_FULL: // Insertion failed because database is full + case SQLITE_CANTOPEN: // Unable to open the database file + case SQLITE_PROTOCOL: // Database lock protocol error + case SQLITE_EMPTY: // Internal use only + case SQLITE_SCHEMA: // The database schema changed + case SQLITE_TOOBIG: // String or BLOB exceeds size limit + case SQLITE_CONSTRAINT: // Abort due to constraint violation + case SQLITE_MISMATCH: // Data type mismatch + case SQLITE_MISUSE: // Library used incorrectly + case SQLITE_NOLFS: // Uses OS features not supported on host + case SQLITE_AUTH: // Authorization denied + case SQLITE_FORMAT: // Not used + case SQLITE_RANGE: // 2nd parameter to sqlite3_bind out of range + case SQLITE_NOTADB: // File opened that is not a database file + severity = mbgl::EventSeverity::Error; + break; + case SQLITE_WARNING: // Warnings from sqlite3_log() + severity = mbgl::EventSeverity::Warning; + break; + case SQLITE_NOTICE: // Notifications from sqlite3_log() + default: + break; } + + mbgl::Log::Record(severity, mbgl::Event::Database, "%s (%s)", msg, codeToString(err)); } const static bool sqliteVersionCheck __attribute__((unused)) = []() { @@ -131,85 +201,93 @@ void Database::exec(const std::string &sql) { } } -Statement Database::prepare(const char *query) { - assert(impl); - return Statement(this, query); +Statement::Statement(Database& db, const char* sql) + : impl(std::make_unique<StatementImpl>(db.impl->db, sql)) { } -Statement::Statement(Database *db, const char *sql) - : impl(std::make_unique<StatementImpl>(db->impl->db, sql)) -{ +Statement::~Statement() { +#ifndef NDEBUG + // Crash if we're destructing this object while we know a Query object references this. + assert(!used); +#endif } -Statement::Statement(Statement &&other) { - *this = std::move(other); -} +Query::Query(Statement& stmt_) : stmt(stmt_) { + assert(stmt.impl); -Statement &Statement::operator=(Statement &&other) { - std::swap(impl, other.impl); - return *this; +#ifndef NDEBUG + assert(!stmt.used); + stmt.used = true; +#endif } -Statement::~Statement() = default; +Query::~Query() { + reset(); + clearBindings(); -template <> void Statement::bind(int offset, std::nullptr_t) { - assert(impl); - impl->check(sqlite3_bind_null(impl->stmt, offset)); +#ifndef NDEBUG + stmt.used = false; +#endif } -template <> void Statement::bind(int offset, int8_t value) { - assert(impl); - impl->check(sqlite3_bind_int64(impl->stmt, offset, value)); +template <> void Query::bind(int offset, std::nullptr_t) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_null(stmt.impl->stmt, offset)); } -template <> void Statement::bind(int offset, int16_t value) { - assert(impl); - impl->check(sqlite3_bind_int64(impl->stmt, offset, value)); +template <> void Query::bind(int offset, int8_t value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value)); } -template <> void Statement::bind(int offset, int32_t value) { - assert(impl); - impl->check(sqlite3_bind_int64(impl->stmt, offset, value)); +template <> void Query::bind(int offset, int16_t value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value)); } -template <> void Statement::bind(int offset, int64_t value) { - assert(impl); - impl->check(sqlite3_bind_int64(impl->stmt, offset, value)); +template <> void Query::bind(int offset, int32_t value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value)); } -template <> void Statement::bind(int offset, uint8_t value) { - assert(impl); - impl->check(sqlite3_bind_int64(impl->stmt, offset, value)); +template <> void Query::bind(int offset, int64_t value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value)); } -template <> void Statement::bind(int offset, uint16_t value) { - assert(impl); - impl->check(sqlite3_bind_int64(impl->stmt, offset, value)); +template <> void Query::bind(int offset, uint8_t value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value)); } -template <> void Statement::bind(int offset, uint32_t value) { - assert(impl); - impl->check(sqlite3_bind_int64(impl->stmt, offset, value)); +template <> void Query::bind(int offset, uint16_t value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value)); } -template <> void Statement::bind(int offset, float value) { - assert(impl); - impl->check(sqlite3_bind_double(impl->stmt, offset, value)); +template <> void Query::bind(int offset, uint32_t value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value)); } -template <> void Statement::bind(int offset, double value) { - assert(impl); - impl->check(sqlite3_bind_double(impl->stmt, offset, value)); +template <> void Query::bind(int offset, float value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_double(stmt.impl->stmt, offset, value)); } -template <> void Statement::bind(int offset, bool value) { - assert(impl); - impl->check(sqlite3_bind_int(impl->stmt, offset, value)); +template <> void Query::bind(int offset, double value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_double(stmt.impl->stmt, offset, value)); } -template <> void Statement::bind(int offset, const char *value) { - assert(impl); - impl->check(sqlite3_bind_text(impl->stmt, offset, value, -1, SQLITE_STATIC)); +template <> void Query::bind(int offset, bool value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int(stmt.impl->stmt, offset, value)); +} + +template <> void Query::bind(int offset, const char *value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_text(stmt.impl->stmt, offset, value, -1, SQLITE_STATIC)); } // We currently cannot use sqlite3_bind_blob64 / sqlite3_bind_text64 because they @@ -219,40 +297,40 @@ template <> void Statement::bind(int offset, const char *value) { // According to http://stackoverflow.com/questions/14288128/what-version-of-sqlite-does-ios-provide, // the first iOS version with 3.8.7+ was 9.0, with 3.8.8. -void Statement::bind(int offset, const char * value, std::size_t length, bool retain) { - assert(impl); +void Query::bind(int offset, const char * value, std::size_t length, bool retain) { + assert(stmt.impl); if (length > std::numeric_limits<int>::max()) { throw std::range_error("value too long for sqlite3_bind_text"); } - impl->check(sqlite3_bind_text(impl->stmt, offset, value, int(length), + stmt.impl->check(sqlite3_bind_text(stmt.impl->stmt, offset, value, int(length), retain ? SQLITE_TRANSIENT : SQLITE_STATIC)); } -void Statement::bind(int offset, const std::string& value, bool retain) { +void Query::bind(int offset, const std::string& value, bool retain) { bind(offset, value.data(), value.size(), retain); } -void Statement::bindBlob(int offset, const void * value, std::size_t length, bool retain) { - assert(impl); +void Query::bindBlob(int offset, const void * value, std::size_t length, bool retain) { + assert(stmt.impl); if (length > std::numeric_limits<int>::max()) { throw std::range_error("value too long for sqlite3_bind_text"); } - impl->check(sqlite3_bind_blob(impl->stmt, offset, value, int(length), + stmt.impl->check(sqlite3_bind_blob(stmt.impl->stmt, offset, value, int(length), retain ? SQLITE_TRANSIENT : SQLITE_STATIC)); } -void Statement::bindBlob(int offset, const std::vector<uint8_t>& value, bool retain) { +void Query::bindBlob(int offset, const std::vector<uint8_t>& value, bool retain) { bindBlob(offset, value.data(), value.size(), retain); } template <> -void Statement::bind( +void Query::bind( int offset, std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds> value) { - assert(impl); - impl->check(sqlite3_bind_int64(impl->stmt, offset, std::chrono::system_clock::to_time_t(value))); + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, std::chrono::system_clock::to_time_t(value))); } -template <> void Statement::bind(int offset, optional<std::string> value) { +template <> void Query::bind(int offset, optional<std::string> value) { if (!value) { bind(offset, nullptr); } else { @@ -261,7 +339,7 @@ template <> void Statement::bind(int offset, optional<std::string> value) { } template <> -void Statement::bind( +void Query::bind( int offset, optional<std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>> value) { if (!value) { @@ -271,86 +349,86 @@ void Statement::bind( } } -bool Statement::run() { - assert(impl); - const int err = sqlite3_step(impl->stmt); - impl->lastInsertRowId = sqlite3_last_insert_rowid(sqlite3_db_handle(impl->stmt)); - impl->changes = sqlite3_changes(sqlite3_db_handle(impl->stmt)); +bool Query::run() { + assert(stmt.impl); + const int err = sqlite3_step(stmt.impl->stmt); + stmt.impl->lastInsertRowId = sqlite3_last_insert_rowid(sqlite3_db_handle(stmt.impl->stmt)); + stmt.impl->changes = sqlite3_changes(sqlite3_db_handle(stmt.impl->stmt)); if (err == SQLITE_DONE) { return false; } else if (err == SQLITE_ROW) { return true; } else if (err != SQLITE_OK) { - throw Exception { err, sqlite3_errmsg(sqlite3_db_handle(impl->stmt)) }; + throw Exception { err, sqlite3_errmsg(sqlite3_db_handle(stmt.impl->stmt)) }; } else { return false; } } -template <> bool Statement::get(int offset) { - assert(impl); - return sqlite3_column_int(impl->stmt, offset); +template <> bool Query::get(int offset) { + assert(stmt.impl); + return sqlite3_column_int(stmt.impl->stmt, offset); } -template <> int Statement::get(int offset) { - assert(impl); - return sqlite3_column_int(impl->stmt, offset); +template <> int Query::get(int offset) { + assert(stmt.impl); + return sqlite3_column_int(stmt.impl->stmt, offset); } -template <> int64_t Statement::get(int offset) { - assert(impl); - return sqlite3_column_int64(impl->stmt, offset); +template <> int64_t Query::get(int offset) { + assert(stmt.impl); + return sqlite3_column_int64(stmt.impl->stmt, offset); } -template <> double Statement::get(int offset) { - assert(impl); - return sqlite3_column_double(impl->stmt, offset); +template <> double Query::get(int offset) { + assert(stmt.impl); + return sqlite3_column_double(stmt.impl->stmt, offset); } -template <> std::string Statement::get(int offset) { - assert(impl); +template <> std::string Query::get(int offset) { + assert(stmt.impl); return { - reinterpret_cast<const char *>(sqlite3_column_blob(impl->stmt, offset)), - size_t(sqlite3_column_bytes(impl->stmt, offset)) + reinterpret_cast<const char *>(sqlite3_column_blob(stmt.impl->stmt, offset)), + size_t(sqlite3_column_bytes(stmt.impl->stmt, offset)) }; } -template <> std::vector<uint8_t> Statement::get(int offset) { - assert(impl); - const auto* begin = reinterpret_cast<const uint8_t*>(sqlite3_column_blob(impl->stmt, offset)); - const uint8_t* end = begin + sqlite3_column_bytes(impl->stmt, offset); +template <> std::vector<uint8_t> Query::get(int offset) { + assert(stmt.impl); + const auto* begin = reinterpret_cast<const uint8_t*>(sqlite3_column_blob(stmt.impl->stmt, offset)); + const uint8_t* end = begin + sqlite3_column_bytes(stmt.impl->stmt, offset); return { begin, end }; } template <> std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds> -Statement::get(int offset) { - assert(impl); +Query::get(int offset) { + assert(stmt.impl); return std::chrono::time_point_cast<std::chrono::seconds>( - std::chrono::system_clock::from_time_t(sqlite3_column_int64(impl->stmt, offset))); + std::chrono::system_clock::from_time_t(sqlite3_column_int64(stmt.impl->stmt, offset))); } -template <> optional<int64_t> Statement::get(int offset) { - assert(impl); - if (sqlite3_column_type(impl->stmt, offset) == SQLITE_NULL) { +template <> optional<int64_t> Query::get(int offset) { + assert(stmt.impl); + if (sqlite3_column_type(stmt.impl->stmt, offset) == SQLITE_NULL) { return optional<int64_t>(); } else { return get<int64_t>(offset); } } -template <> optional<double> Statement::get(int offset) { - assert(impl); - if (sqlite3_column_type(impl->stmt, offset) == SQLITE_NULL) { +template <> optional<double> Query::get(int offset) { + assert(stmt.impl); + if (sqlite3_column_type(stmt.impl->stmt, offset) == SQLITE_NULL) { return optional<double>(); } else { return get<double>(offset); } } -template <> optional<std::string> Statement::get(int offset) { - assert(impl); - if (sqlite3_column_type(impl->stmt, offset) == SQLITE_NULL) { +template <> optional<std::string> Query::get(int offset) { + assert(stmt.impl); + if (sqlite3_column_type(stmt.impl->stmt, offset) == SQLITE_NULL) { return optional<std::string>(); } else { return get<std::string>(offset); @@ -359,9 +437,9 @@ template <> optional<std::string> Statement::get(int offset) { template <> optional<std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>> -Statement::get(int offset) { - assert(impl); - if (sqlite3_column_type(impl->stmt, offset) == SQLITE_NULL) { +Query::get(int offset) { + assert(stmt.impl); + if (sqlite3_column_type(stmt.impl->stmt, offset) == SQLITE_NULL) { return {}; } else { return get<std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>>( @@ -369,24 +447,24 @@ Statement::get(int offset) { } } -void Statement::reset() { - assert(impl); - sqlite3_reset(impl->stmt); +void Query::reset() { + assert(stmt.impl); + sqlite3_reset(stmt.impl->stmt); } -void Statement::clearBindings() { - assert(impl); - sqlite3_clear_bindings(impl->stmt); +void Query::clearBindings() { + assert(stmt.impl); + sqlite3_clear_bindings(stmt.impl->stmt); } -int64_t Statement::lastInsertRowId() const { - assert(impl); - return impl->lastInsertRowId; +int64_t Query::lastInsertRowId() const { + assert(stmt.impl); + return stmt.impl->lastInsertRowId; } -uint64_t Statement::changes() const { - assert(impl); - auto changes_ = impl->changes; +uint64_t Query::changes() const { + assert(stmt.impl); + auto changes_ = stmt.impl->changes; return (changes_ < 0 ? 0 : changes_); } diff --git a/platform/default/sqlite3.hpp b/platform/default/sqlite3.hpp index 82e3ceff6d..20d09b550c 100644 --- a/platform/default/sqlite3.hpp +++ b/platform/default/sqlite3.hpp @@ -19,21 +19,55 @@ enum OpenFlag : int { PrivateCache = 0x00040000, }; -struct Exception : std::runtime_error { - enum Code : int { - OK = 0, - CANTOPEN = 14, - NOTADB = 26 - }; +enum class ResultCode : int { + OK = 0, + Error = 1, + Internal = 2, + Perm = 3, + Abort = 4, + Busy = 5, + Locked = 6, + NoMem = 7, + ReadOnly = 8, + Interrupt = 9, + IOErr = 10, + Corrupt = 11, + NotFound = 12, + Full = 13, + CantOpen = 14, + Protocol = 15, + Schema = 17, + TooBig = 18, + Constraint = 19, + Mismatch = 20, + Misuse = 21, + NoLFS = 22, + Auth = 23, + Range = 25, + NotADB = 26 +}; - Exception(int err, const char *msg) : std::runtime_error(msg), code(err) {} - Exception(int err, const std::string& msg) : std::runtime_error(msg), code(err) {} - const int code = OK; +class Exception : public std::runtime_error { +public: + Exception(int err, const char* msg) + : std::runtime_error(msg), code(static_cast<ResultCode>(err)) { + } + Exception(ResultCode err, const char* msg) + : std::runtime_error(msg), code(err) { + } + Exception(int err, const std::string& msg) + : std::runtime_error(msg), code(static_cast<ResultCode>(err)) { + } + Exception(ResultCode err, const std::string& msg) + : std::runtime_error(msg), code(err) { + } + const ResultCode code = ResultCode::OK; }; class DatabaseImpl; class Statement; class StatementImpl; +class Query; class Database { private: @@ -48,7 +82,6 @@ public: void setBusyTimeout(std::chrono::milliseconds); void exec(const std::string &sql); - Statement prepare(const char *query); private: std::unique_ptr<DatabaseImpl> impl; @@ -56,28 +89,54 @@ private: friend class Statement; }; +// A Statement object represents a prepared statement that can be run repeatedly run with a Query object. class Statement { +public: + Statement(Database& db, const char* sql); + Statement(const Statement&) = delete; + Statement(Statement&&) = delete; + Statement& operator=(const Statement&) = delete; + Statement& operator=(Statement&&) = delete; + ~Statement(); + + friend class Query; + private: - Statement(const Statement &) = delete; - Statement &operator=(const Statement &) = delete; + std::unique_ptr<StatementImpl> impl; + +#ifndef NDEBUG + // This flag stores whether there exists a Query object that uses this prepared statement. + // There may only be one Query object at a time. Statement objects must outlive Query objects. + // While a Query object exists, a Statement object may not be moved or deleted. + bool used = false; +#endif +}; +// A Query object is used to run a database query with a prepared statement (stored in a Statement +// object). There may only exist one Query object per Statement object. Query objects are designed +// to be constructed and destroyed frequently. +class Query { public: - Statement(Database *db, const char *sql); - Statement(Statement &&); - ~Statement(); - Statement &operator=(Statement &&); + Query(Statement&); + Query(const Query&) = delete; + Query(Query&&) = delete; + Query& operator=(const Query&) = delete; + Query& operator=(Query&&) = delete; + ~Query(); - template <typename T> void bind(int offset, T value); + template <typename T> + void bind(int offset, T value); // Text - void bind(int offset, const char *, std::size_t length, bool retain = true); + void bind(int offset, const char*, std::size_t length, bool retain = true); void bind(int offset, const std::string&, bool retain = true); // Blob - void bindBlob(int offset, const void *, std::size_t length, bool retain = true); + void bindBlob(int offset, const void*, std::size_t length, bool retain = true); void bindBlob(int offset, const std::vector<uint8_t>&, bool retain = true); - template <typename T> T get(int offset); + template <typename T> + T get(int offset); bool run(); void reset(); @@ -87,7 +146,7 @@ public: uint64_t changes() const; private: - std::unique_ptr<StatementImpl> impl; + Statement& stmt; }; class Transaction { diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 4c69d94ec4..9d8390803b 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -88,6 +88,18 @@ The 4.0._x_ series of releases will be the last to support iOS 8. The minimum iO * Fixed an issue where requesting location services permission would trigger an unrecoverable loop. ([#11229](https://github.com/mapbox/mapbox-gl-native/pull/11229)) +## 3.7.6 - March 12, 2018 + +* Fixed an issue where full-resolution tiles could fail to replace lower-resolution placeholders. ([#11227](https://github.com/mapbox/mapbox-gl-native/pull/11227)) +* Fixed an issue where tilesets with bounds that cover the entire world would fail to render. ([#11425](https://github.com/mapbox/mapbox-gl-native/pull/11425)) +* Fixed a memory leak in `MGLMapSnapshotter`. ([#11193](https://github.com/mapbox/mapbox-gl-native/pull/11193)) +* Fixed an issue where the pinch gesture could drift beyond bounds imposed by `-[MGLMapViewDelegate mapView:shouldChangeFromCamera:toCamera:]`. ([#11423](https://github.com/mapbox/mapbox-gl-native/pull/11423)) +* Improved the visibility of the heading indicator arrow. ([#11337](https://github.com/mapbox/mapbox-gl-native/pull/11337)) + +## 3.7.5 - February 16, 2018 + +* Fixed an issue where requesting location services permission would trigger an unrecoverable loop. ([#11229](https://github.com/mapbox/mapbox-gl-native/pull/11229)) + ## 3.7.4 - February 12, 2018 * Added the `MGLTileSourceOptionTileCoordinateBounds` option to create an `MGLTileSource` that only supplies tiles within a specific geographic bounding box. ([#11141](https://github.com/mapbox/mapbox-gl-native/pull/11141)) diff --git a/platform/qt/README.md b/platform/qt/README.md index 4d2d887828..018f8823b6 100644 --- a/platform/qt/README.md +++ b/platform/qt/README.md @@ -1,4 +1,4 @@ -# Mapbox Qt SDK +# Mapbox Maps SDK for Qt [![Circle CI build status](https://circleci.com/gh/mapbox/mapbox-gl-native.svg?style=shield)](https://circleci.com/gh/mapbox/workflows/mapbox-gl-native/tree/master) [![AppVeyor CI build status](https://ci.appveyor.com/api/projects/status/3q12kbcooc6df8uc?svg=true)](https://ci.appveyor.com/project/Mapbox/mapbox-gl-native) @@ -9,7 +9,7 @@ available in the Qt SDK since Qt 5.9. Use the [Qt bugtracker](https://bugreports to the plugin and this GitHub repository for bugs related to Mapbox GL Native and the Qt bindings. You should build this repository if you want to develop/contribute using the low level Qt C++ bindings or -want to contribute to the Mapbox GL Native projectusing the Qt port for debugging. +want to contribute to the Mapbox GL Native project using the Qt port for debugging. See the Mapbox Qt landing page for more details: https://www.mapbox.com/qt/ diff --git a/platform/qt/config.qdocconf b/platform/qt/config.qdocconf index d6f0edbd32..9d3b8a7ef4 100644 --- a/platform/qt/config.qdocconf +++ b/platform/qt/config.qdocconf @@ -7,7 +7,7 @@ include($QT_INSTALL_DOCS/global/qt-html-templates-online.qdocconf) Cpp.ignoretokens = Q_MAPBOXGL_EXPORT project = QMapboxGL -description = Mapbox Qt SDK +description = Mapbox Maps SDK for Qt language = Cpp outputdir = docs diff --git a/platform/qt/include/qmapboxgl.hpp b/platform/qt/include/qmapboxgl.hpp index 70fe270902..79eb672d1f 100644 --- a/platform/qt/include/qmapboxgl.hpp +++ b/platform/qt/include/qmapboxgl.hpp @@ -128,6 +128,13 @@ public: MapChangeSourceDidChange }; + enum MapLoadingFailure { + StyleParseFailure, + StyleLoadFailure, + NotFoundFailure, + UnknownFailure + }; + // Determines the orientation of the map. enum NorthOrientation { NorthUpwards, // Default @@ -248,6 +255,7 @@ public slots: signals: void needsRendering(); void mapChanged(QMapboxGL::MapChange); + void mapLoadingFailed(QMapboxGL::MapLoadingFailure, const QString &reason); void copyrightsChanged(const QString ©rightsHtml); void staticRenderFinished(const QString &error); @@ -259,5 +267,6 @@ private: }; Q_DECLARE_METATYPE(QMapboxGL::MapChange); +Q_DECLARE_METATYPE(QMapboxGL::MapLoadingFailure); #endif // QMAPBOXGL_H diff --git a/platform/qt/src/qmapbox.cpp b/platform/qt/src/qmapbox.cpp index 87a9358772..2180f22d07 100644 --- a/platform/qt/src/qmapbox.cpp +++ b/platform/qt/src/qmapbox.cpp @@ -24,7 +24,7 @@ namespace QMapbox { /*! \namespace QMapbox - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt Contains miscellaneous Mapbox bindings used throughout QMapboxGL. */ @@ -74,7 +74,7 @@ namespace QMapbox { /*! \class QMapbox::Feature - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt Represents \l {https://www.mapbox.com/help/define-features/}{map features} via its \a type (PointType, LineStringType or PolygonType), \a geometry, \a @@ -94,7 +94,7 @@ namespace QMapbox { /*! \class QMapbox::ShapeAnnotationGeometry - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt Represents a shape annotation geometry. */ @@ -113,7 +113,7 @@ namespace QMapbox { /*! \class QMapbox::SymbolAnnotation - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt A symbol annotation comprises of its geometry and an icon identifier. */ @@ -121,7 +121,7 @@ namespace QMapbox { /*! \class QMapbox::LineAnnotation - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt Represents a line annotation object, along with its properties. @@ -131,7 +131,7 @@ namespace QMapbox { /*! \class QMapbox::FillAnnotation - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt Represents a fill annotation object, along with its properties. @@ -177,7 +177,7 @@ namespace QMapbox { /*! \class QMapbox::CustomLayerRenderParameters - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt QMapbox::CustomLayerRenderParameters provides the data passed on each render pass for a custom layer. diff --git a/platform/qt/src/qmapboxgl.cpp b/platform/qt/src/qmapboxgl.cpp index 2e736c0aa2..e2c1b31eea 100644 --- a/platform/qt/src/qmapboxgl.cpp +++ b/platform/qt/src/qmapboxgl.cpp @@ -136,7 +136,7 @@ std::unique_ptr<mbgl::style::Image> toStyleImage(const QString &id, const QImage \class QMapboxGLSettings \brief The QMapboxGLSettings class stores the initial configuration for QMapboxGL. - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt QMapboxGLSettings is used to configure QMapboxGL at the moment of its creation. Once created, the QMapboxGLSettings of a QMapboxGL can no longer be changed. @@ -454,7 +454,7 @@ void QMapboxGLSettings::setResourceTransform(const std::function<std::string(con \class QMapboxGL \brief The QMapboxGL class is a Qt wrapper for the Mapbox GL Native engine. - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt QMapboxGL is a Qt friendly version the Mapbox GL Native engine using Qt types and deep integration with Qt event loop. QMapboxGL relies as much as possible @@ -518,6 +518,19 @@ void QMapboxGLSettings::setResourceTransform(const std::function<std::string(con */ /*! + \enum QMapboxGL::MapLoadingFailure + + This enum represents map loading failure type. + + \value StyleParseFailure Failure to parse the style. + \value StyleLoadFailure Failure to load the style data. + \value NotFoundFailure Failure to obtain style resource file. + \value UnknownFailure Unknown map loading failure. + + \sa mapLoadingFailed() +*/ + +/*! \enum QMapboxGL::NorthOrientation This enum sets the orientation of the north bearing. It will directly affect bearing when @@ -1636,6 +1649,13 @@ void QMapboxGL::connectionEstablished() */ /*! + \fn void QMapboxGL::mapLoadingFailed(QMapboxGL::MapLoadingFailure type, const QString &description) + + This signal is emitted when a map loading failure happens. Details of the + failures are provided, including its \a type and textual \a description. +*/ + +/*! \fn void QMapboxGL::copyrightsChanged(const QString ©rightsHtml); This signal is emitted when the copyrights of the current content of the map @@ -1672,6 +1692,7 @@ QMapboxGLPrivate::QMapboxGLPrivate(QMapboxGL *q, const QMapboxGLSettings &settin qRegisterMetaType<QMapboxGL::MapChange>("QMapboxGL::MapChange"); connect(m_mapObserver.get(), SIGNAL(mapChanged(QMapboxGL::MapChange)), q, SIGNAL(mapChanged(QMapboxGL::MapChange))); + connect(m_mapObserver.get(), SIGNAL(mapLoadingFailed(QMapboxGL::MapLoadingFailure,QString)), q, SIGNAL(mapLoadingFailed(QMapboxGL::MapLoadingFailure,QString))); connect(m_mapObserver.get(), SIGNAL(copyrightsChanged(QString)), q, SIGNAL(copyrightsChanged(QString))); // Setup the Map object diff --git a/platform/qt/src/qmapboxgl_map_observer.cpp b/platform/qt/src/qmapboxgl_map_observer.cpp index e180ed8fda..44cb8c41d5 100644 --- a/platform/qt/src/qmapboxgl_map_observer.cpp +++ b/platform/qt/src/qmapboxgl_map_observer.cpp @@ -2,6 +2,10 @@ #include "qmapboxgl_p.hpp" +#include <mbgl/util/exception.hpp> + +#include <exception> + QMapboxGLMapObserver::QMapboxGLMapObserver(QMapboxGLPrivate *d) : d_ptr(d) { @@ -44,9 +48,30 @@ void QMapboxGLMapObserver::onDidFinishLoadingMap() emit mapChanged(QMapboxGL::MapChangeDidFinishLoadingMap); } -void QMapboxGLMapObserver::onDidFailLoadingMap(std::exception_ptr) +void QMapboxGLMapObserver::onDidFailLoadingMap(std::exception_ptr exception) { emit mapChanged(QMapboxGL::MapChangeDidFailLoadingMap); + + QMapboxGL::MapLoadingFailure type; + QString description; + + try { + std::rethrow_exception(exception); + } catch (const mbgl::util::StyleParseException& e) { + type = QMapboxGL::MapLoadingFailure::StyleParseFailure; + description = e.what(); + } catch (const mbgl::util::StyleLoadException& e) { + type = QMapboxGL::MapLoadingFailure::StyleLoadFailure; + description = e.what(); + } catch (const mbgl::util::NotFoundException& e) { + type = QMapboxGL::MapLoadingFailure::NotFoundFailure; + description = e.what(); + } catch (const std::exception& e) { + type = QMapboxGL::MapLoadingFailure::UnknownFailure; + description = e.what(); + } + + emit mapLoadingFailed(type, description); } void QMapboxGLMapObserver::onWillStartRenderingFrame() @@ -65,7 +90,7 @@ void QMapboxGLMapObserver::onDidFinishRenderingFrame(mbgl::MapObserver::RenderMo void QMapboxGLMapObserver::onWillStartRenderingMap() { - emit mapChanged(QMapboxGL::MapChangeWillStartLoadingMap); + emit mapChanged(QMapboxGL::MapChangeWillStartRenderingMap); } void QMapboxGLMapObserver::onDidFinishRenderingMap(mbgl::MapObserver::RenderMode mode) diff --git a/platform/qt/src/qmapboxgl_map_observer.hpp b/platform/qt/src/qmapboxgl_map_observer.hpp index c9d0581a90..98da5b6add 100644 --- a/platform/qt/src/qmapboxgl_map_observer.hpp +++ b/platform/qt/src/qmapboxgl_map_observer.hpp @@ -36,6 +36,7 @@ public: signals: void mapChanged(QMapboxGL::MapChange); + void mapLoadingFailed(QMapboxGL::MapLoadingFailure, const QString &reason); void copyrightsChanged(const QString ©rightsHtml); private: diff --git a/platform/qt/src/sqlite3.cpp b/platform/qt/src/sqlite3.cpp index 80efd6a326..4bcaea0e31 100644 --- a/platform/qt/src/sqlite3.cpp +++ b/platform/qt/src/sqlite3.cpp @@ -24,11 +24,11 @@ namespace mapbox { namespace sqlite { // https://www.sqlite.org/rescode.html#ok -static_assert(mbgl::underlying_type(Exception::OK) == 0, "error"); +static_assert(mbgl::underlying_type(ResultCode::OK) == 0, "error"); // https://www.sqlite.org/rescode.html#cantopen -static_assert(mbgl::underlying_type(Exception::CANTOPEN) == 14, "error"); +static_assert(mbgl::underlying_type(ResultCode::CantOpen) == 14, "error"); // https://www.sqlite.org/rescode.html#notadb -static_assert(mbgl::underlying_type(Exception::NOTADB) == 26, "error"); +static_assert(mbgl::underlying_type(ResultCode::NotADB) == 26, "error"); void checkQueryError(const QSqlQuery& query) { QSqlError lastError = query.lastError(); @@ -57,7 +57,7 @@ void checkDatabaseOpenError(const QSqlDatabase &db) { // always returns -1 for `nativeErrorCode()` on database errors. QSqlError lastError = db.lastError(); if (lastError.type() != QSqlError::NoError) { - throw Exception { Exception::Code::CANTOPEN, "Error opening the database." }; + throw Exception { ResultCode::CantOpen, "Error opening the database." }; } } @@ -74,7 +74,7 @@ public: : connectionName(QString::number(uint64_t(QThread::currentThread())) + incrementCounter()) { if (!QSqlDatabase::drivers().contains("QSQLITE")) { - throw Exception { Exception::Code::CANTOPEN, "SQLite driver not found." }; + throw Exception { ResultCode::CantOpen, "SQLite driver not found." }; } assert(!QSqlDatabase::contains(connectionName)); @@ -186,74 +186,82 @@ void Database::exec(const std::string &sql) { } } -Statement Database::prepare(const char *query) { - return Statement(this, query); -} - -Statement::Statement(Database *db, const char *sql) - : impl(std::make_unique<StatementImpl>(QString(sql), QSqlDatabase::database(db->impl->connectionName))) { +Statement::Statement(Database& db, const char* sql) + : impl(std::make_unique<StatementImpl>(QString(sql), + QSqlDatabase::database(db.impl->connectionName))) { assert(impl); } -Statement::Statement(Statement &&other) - : impl(std::move(other.impl)) { - assert(impl); +Statement::~Statement() { +#ifndef NDEBUG + // Crash if we're destructing this object while we know a Query object references this. + assert(!used); +#endif } -Statement &Statement::operator=(Statement &&other) { - assert(impl); - std::swap(impl, other.impl); - return *this; +Query::Query(Statement& stmt_) : stmt(stmt_) { + assert(stmt.impl); + +#ifndef NDEBUG + assert(!stmt.used); + stmt.used = true; +#endif } -Statement::~Statement() { +Query::~Query() { + reset(); + clearBindings(); + +#ifndef NDEBUG + stmt.used = false; +#endif } -template void Statement::bind(int, int64_t); +template void Query::bind(int, int64_t); template <typename T> -void Statement::bind(int offset, T value) { - assert(impl); +void Query::bind(int offset, T value) { + assert(stmt.impl); // Field numbering starts at 0. - impl->query.bindValue(offset - 1, QVariant::fromValue<T>(value), QSql::In); - checkQueryError(impl->query); + stmt.impl->query.bindValue(offset - 1, QVariant::fromValue<T>(value), QSql::In); + checkQueryError(stmt.impl->query); } template <> -void Statement::bind(int offset, std::nullptr_t) { - assert(impl); +void Query::bind(int offset, std::nullptr_t) { + assert(stmt.impl); // Field numbering starts at 0. - impl->query.bindValue(offset - 1, QVariant(QVariant::Invalid), QSql::In); - checkQueryError(impl->query); + stmt.impl->query.bindValue(offset - 1, QVariant(QVariant::Invalid), QSql::In); + checkQueryError(stmt.impl->query); } template <> -void Statement::bind(int offset, int32_t value) { +void Query::bind(int offset, int32_t value) { bind(offset, static_cast<int64_t>(value)); } template <> -void Statement::bind(int offset, bool value) { +void Query::bind(int offset, bool value) { bind(offset, static_cast<int>(value)); } template <> -void Statement::bind(int offset, int8_t value) { +void Query::bind(int offset, int8_t value) { bind(offset, static_cast<int64_t>(value)); } template <> -void Statement::bind(int offset, uint8_t value) { +void Query::bind(int offset, uint8_t value) { bind(offset, static_cast<int64_t>(value)); } template <> -void Statement::bind(int offset, mbgl::Timestamp value) { +void Query::bind(int offset, mbgl::Timestamp value) { bind(offset, std::chrono::system_clock::to_time_t(value)); } template <> -void Statement::bind(int offset, optional<std::string> value) { +void Query::bind(int offset, optional<std::string> value) { if (value) { bind(offset, *value); } else { @@ -262,7 +270,7 @@ void Statement::bind(int offset, optional<std::string> value) { } template <> -void Statement::bind(int offset, optional<mbgl::Timestamp> value) { +void Query::bind(int offset, optional<mbgl::Timestamp> value) { if (value) { bind(offset, *value); } else { @@ -270,30 +278,25 @@ void Statement::bind(int offset, optional<mbgl::Timestamp> value) { } } -void Statement::bind(int offset, const char* value, std::size_t length, bool retain) { - assert(impl); +void Query::bind(int offset, const char* value, std::size_t length, bool /* retain */) { + assert(stmt.impl); if (length > std::numeric_limits<int>::max()) { // Kept for consistence with the default implementation. throw std::range_error("value too long"); } - // Qt SQLite driver treats QByteArray as blob: we need to explicitly - // declare the variant type as string. - QVariant text(QVariant::Type::String); - text.setValue(retain ? QByteArray(value, length) : QByteArray::fromRawData(value, length)); - // Field numbering starts at 0. - impl->query.bindValue(offset - 1, std::move(text), QSql::In); + stmt.impl->query.bindValue(offset - 1, QString(QByteArray(value, length)), QSql::In); - checkQueryError(impl->query); + checkQueryError(stmt.impl->query); } -void Statement::bind(int offset, const std::string& value, bool retain) { +void Query::bind(int offset, const std::string& value, bool retain) { bind(offset, value.data(), value.size(), retain); } -void Statement::bindBlob(int offset, const void* value_, std::size_t length, bool retain) { - assert(impl); +void Query::bindBlob(int offset, const void* value_, std::size_t length, bool retain) { + assert(stmt.impl); const char* value = reinterpret_cast<const char*>(value_); if (length > std::numeric_limits<int>::max()) { // Kept for consistence with the default implementation. @@ -301,123 +304,123 @@ void Statement::bindBlob(int offset, const void* value_, std::size_t length, boo } // Field numbering starts at 0. - impl->query.bindValue(offset - 1, retain ? QByteArray(value, length) : + stmt.impl->query.bindValue(offset - 1, retain ? QByteArray(value, length) : QByteArray::fromRawData(value, length), QSql::In | QSql::Binary); - checkQueryError(impl->query); + checkQueryError(stmt.impl->query); } -void Statement::bindBlob(int offset, const std::vector<uint8_t>& value, bool retain) { +void Query::bindBlob(int offset, const std::vector<uint8_t>& value, bool retain) { bindBlob(offset, value.data(), value.size(), retain); } -bool Statement::run() { - assert(impl); +bool Query::run() { + assert(stmt.impl); - if (!impl->query.isValid()) { - if (impl->query.exec()) { - impl->lastInsertRowId = impl->query.lastInsertId().value<int64_t>(); - impl->changes = impl->query.numRowsAffected(); + if (!stmt.impl->query.isValid()) { + if (stmt.impl->query.exec()) { + stmt.impl->lastInsertRowId = stmt.impl->query.lastInsertId().value<int64_t>(); + stmt.impl->changes = stmt.impl->query.numRowsAffected(); } else { - checkQueryError(impl->query); + checkQueryError(stmt.impl->query); } } - const bool hasNext = impl->query.next(); - if (!hasNext) impl->query.finish(); + const bool hasNext = stmt.impl->query.next(); + if (!hasNext) stmt.impl->query.finish(); return hasNext; } -template bool Statement::get(int); -template int Statement::get(int); -template int64_t Statement::get(int); -template double Statement::get(int); +template bool Query::get(int); +template int Query::get(int); +template int64_t Query::get(int); +template double Query::get(int); -template <typename T> T Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QVariant value = impl->query.value(offset); - checkQueryError(impl->query); +template <typename T> T Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QVariant value = stmt.impl->query.value(offset); + checkQueryError(stmt.impl->query); return value.value<T>(); } -template <> std::vector<uint8_t> Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QByteArray byteArray = impl->query.value(offset).toByteArray(); - checkQueryError(impl->query); +template <> std::vector<uint8_t> Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QByteArray byteArray = stmt.impl->query.value(offset).toByteArray(); + checkQueryError(stmt.impl->query); std::vector<uint8_t> blob(byteArray.begin(), byteArray.end()); return blob; } -template <> mbgl::Timestamp Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QVariant value = impl->query.value(offset); - checkQueryError(impl->query); +template <> mbgl::Timestamp Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QVariant value = stmt.impl->query.value(offset); + checkQueryError(stmt.impl->query); return std::chrono::time_point_cast<std::chrono::seconds>( std::chrono::system_clock::from_time_t(value.value<::time_t>())); } -template <> optional<int64_t> Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QVariant value = impl->query.value(offset); - checkQueryError(impl->query); +template <> optional<int64_t> Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QVariant value = stmt.impl->query.value(offset); + checkQueryError(stmt.impl->query); if (value.isNull()) return {}; return { value.value<int64_t>() }; } -template <> optional<double> Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QVariant value = impl->query.value(offset); - checkQueryError(impl->query); +template <> optional<double> Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QVariant value = stmt.impl->query.value(offset); + checkQueryError(stmt.impl->query); if (value.isNull()) return {}; return { value.value<double>() }; } -template <> std::string Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QByteArray value = impl->query.value(offset).toByteArray(); - checkQueryError(impl->query); +template <> std::string Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QByteArray value = stmt.impl->query.value(offset).toByteArray(); + checkQueryError(stmt.impl->query); return std::string(value.constData(), value.size()); } -template <> optional<std::string> Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QByteArray value = impl->query.value(offset).toByteArray(); - checkQueryError(impl->query); +template <> optional<std::string> Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QByteArray value = stmt.impl->query.value(offset).toByteArray(); + checkQueryError(stmt.impl->query); if (value.isNull()) return {}; return { std::string(value.constData(), value.size()) }; } -template <> optional<mbgl::Timestamp> Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QVariant value = impl->query.value(offset); - checkQueryError(impl->query); +template <> optional<mbgl::Timestamp> Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QVariant value = stmt.impl->query.value(offset); + checkQueryError(stmt.impl->query); if (value.isNull()) return {}; return { std::chrono::time_point_cast<mbgl::Seconds>( std::chrono::system_clock::from_time_t(value.value<::time_t>())) }; } -void Statement::reset() { - assert(impl); - impl->query.finish(); +void Query::reset() { + assert(stmt.impl); + stmt.impl->query.finish(); } -void Statement::clearBindings() { +void Query::clearBindings() { // no-op } -int64_t Statement::lastInsertRowId() const { - assert(impl); - return impl->lastInsertRowId; +int64_t Query::lastInsertRowId() const { + assert(stmt.impl); + return stmt.impl->lastInsertRowId; } -uint64_t Statement::changes() const { - assert(impl); - return (impl->changes < 0 ? 0 : impl->changes); +uint64_t Query::changes() const { + assert(stmt.impl); + return (stmt.impl->changes < 0 ? 0 : stmt.impl->changes); } Transaction::Transaction(Database& db_, Mode mode) diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index a7beb0f1e2..d81544eed5 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -747,6 +747,11 @@ void Map::Impl::onInvalidate() { } void Map::Impl::onUpdate() { + // Don't load/render anything in still mode until explicitly requested. + if (mode != MapMode::Continuous && !stillImageRequest) { + return; + } + TimePoint timePoint = mode == MapMode::Continuous ? Clock::now() : Clock::time_point::max(); transform.updateTransitions(timePoint); diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp index 2ac714e122..962a6571db 100644 --- a/src/mbgl/renderer/renderer_impl.cpp +++ b/src/mbgl/renderer/renderer_impl.cpp @@ -85,11 +85,6 @@ void Renderer::Impl::setObserver(RendererObserver* observer_) { void Renderer::Impl::render(const UpdateParameters& updateParameters) { if (updateParameters.mode != MapMode::Continuous) { - // Don't load/render anyting in still mode until explicitly requested. - if (!updateParameters.stillImageRequest) { - return; - } - // Reset zoom history state. zoomHistory.first = true; } diff --git a/src/mbgl/util/compression.cpp b/src/mbgl/util/compression.cpp index 30e813cbb8..ee3ebe7cae 100644 --- a/src/mbgl/util/compression.cpp +++ b/src/mbgl/util/compression.cpp @@ -1,6 +1,6 @@ #include <mbgl/util/compression.hpp> -#if defined(__QT__) && defined(_WINDOWS) +#if defined(__QT__) && defined(_WINDOWS) && !defined(__GNUC__) #include <QtZlib/zlib.h> #else #include <zlib.h> diff --git a/src/mbgl/util/tiny_sdf.cpp b/src/mbgl/util/tiny_sdf.cpp index 60839357d5..6edcd83bc2 100644 --- a/src/mbgl/util/tiny_sdf.cpp +++ b/src/mbgl/util/tiny_sdf.cpp @@ -95,7 +95,7 @@ AlphaImage transformRasterToSDF(const AlphaImage& rasterInput, double radius, do for (uint32_t i = 0; i < size; i++) { double distance = gridOuter[i] - gridInner[i]; - sdf.data[i] = std::max(0l, std::min(255l, std::lround(255.0 - 255.0 * (distance / radius + cutoff)))); + sdf.data[i] = std::max(0l, std::min(255l, ::lround(255.0 - 255.0 * (distance / radius + cutoff)))); } return sdf; diff --git a/test/fixtures/offline_database/satellite_test.db b/test/fixtures/offline_database/satellite_test.db Binary files differnew file mode 100644 index 0000000000..95dd8617ff --- /dev/null +++ b/test/fixtures/offline_database/satellite_test.db diff --git a/test/storage/offline_database.test.cpp b/test/storage/offline_database.test.cpp index 94daf59c02..620e6eaa6d 100644 --- a/test/storage/offline_database.test.cpp +++ b/test/storage/offline_database.test.cpp @@ -38,6 +38,10 @@ void writeFile(const char* name, const std::string& data) { mbgl::util::write_file(name, data); } +void copyFile(const char* orig, const char* dest) { + mbgl::util::write_file(dest, mbgl::util::read_file(orig)); +} + } // namespace TEST(OfflineDatabase, TEST_REQUIRES_WRITE(Create)) { @@ -62,7 +66,7 @@ TEST(OfflineDatabase, TEST_REQUIRES_WRITE(SchemaVersion)) { std::string path("test/fixtures/offline_database/offline.db"); { - mapbox::sqlite::Database db(path, mapbox::sqlite::Create | mapbox::sqlite::ReadWrite); + mapbox::sqlite::Database db{ path, mapbox::sqlite::Create | mapbox::sqlite::ReadWrite }; db.exec("PRAGMA user_version = 1"); } @@ -143,6 +147,36 @@ TEST(OfflineDatabase, PutResource) { EXPECT_EQ("second", *updateGetResult->data); } +TEST(OfflineDatabase, TEST_REQUIRES_WRITE(GetResourceFromOfflineRegion)) { + using namespace mbgl; + + createDir("test/fixtures/offline_database"); + deleteFile("test/fixtures/offline_database/satellite.db"); + copyFile("test/fixtures/offline_database/satellite_test.db", "test/fixtures/offline_database/satellite.db"); + + OfflineDatabase db("test/fixtures/offline_database/satellite.db", mapbox::sqlite::ReadOnly); + + Resource resource = Resource::style("mapbox://styles/mapbox/satellite-v9"); + ASSERT_TRUE(db.get(resource)); +} + +TEST(OfflineDatabase, PutAndGetResource) { + using namespace mbgl; + + OfflineDatabase db(":memory:"); + + Response response1; + response1.data = std::make_shared<std::string>("foobar"); + + Resource resource = Resource::style("mapbox://example.com/style"); + + db.put(resource, response1); + + auto response2 = db.get(resource); + + ASSERT_EQ(*response1.data, *(*response2).data); +} + TEST(OfflineDatabase, PutTile) { using namespace mbgl; @@ -565,40 +599,45 @@ TEST(OfflineDatabase, OfflineMapboxTileCount) { } static int databasePageCount(const std::string& path) { - mapbox::sqlite::Database db(path, mapbox::sqlite::ReadOnly); - mapbox::sqlite::Statement stmt = db.prepare("pragma page_count"); - stmt.run(); - return stmt.get<int>(0); + mapbox::sqlite::Database db{ path, mapbox::sqlite::ReadOnly }; + mapbox::sqlite::Statement stmt{ db, "pragma page_count" }; + mapbox::sqlite::Query query{ stmt }; + query.run(); + return query.get<int>(0); } static int databaseUserVersion(const std::string& path) { - mapbox::sqlite::Database db(path, mapbox::sqlite::ReadOnly); - mapbox::sqlite::Statement stmt = db.prepare("pragma user_version"); - stmt.run(); - return stmt.get<int>(0); + mapbox::sqlite::Database db{ path, mapbox::sqlite::ReadOnly }; + mapbox::sqlite::Statement stmt{ db, "pragma user_version" }; + mapbox::sqlite::Query query{ stmt }; + query.run(); + return query.get<int>(0); } static std::string databaseJournalMode(const std::string& path) { - mapbox::sqlite::Database db(path, mapbox::sqlite::ReadOnly); - mapbox::sqlite::Statement stmt = db.prepare("pragma journal_mode"); - stmt.run(); - return stmt.get<std::string>(0); + mapbox::sqlite::Database db{ path, mapbox::sqlite::ReadOnly }; + mapbox::sqlite::Statement stmt{ db, "pragma journal_mode" }; + mapbox::sqlite::Query query{ stmt }; + query.run(); + return query.get<std::string>(0); } static int databaseSyncMode(const std::string& path) { - mapbox::sqlite::Database db(path, mapbox::sqlite::ReadOnly); - mapbox::sqlite::Statement stmt = db.prepare("pragma synchronous"); - stmt.run(); - return stmt.get<int>(0); + mapbox::sqlite::Database db{ path, mapbox::sqlite::ReadOnly }; + mapbox::sqlite::Statement stmt{ db, "pragma synchronous" }; + mapbox::sqlite::Query query{ stmt }; + query.run(); + return query.get<int>(0); } static std::vector<std::string> databaseTableColumns(const std::string& path, const std::string& name) { - mapbox::sqlite::Database db(path, mapbox::sqlite::ReadOnly); + mapbox::sqlite::Database db{ path, mapbox::sqlite::ReadOnly }; const auto sql = std::string("pragma table_info(") + name + ")"; - mapbox::sqlite::Statement stmt = db.prepare(sql.c_str()); + mapbox::sqlite::Statement stmt{ db, sql.c_str() }; + mapbox::sqlite::Query query{ stmt }; std::vector<std::string> columns; - while (stmt.run()) { - columns.push_back(stmt.get<std::string>(1)); + while (query.run()) { + columns.push_back(query.get<std::string>(1)); } return columns; } diff --git a/test/storage/sqlite.test.cpp b/test/storage/sqlite.test.cpp index 36715a2fd0..918200181f 100644 --- a/test/storage/sqlite.test.cpp +++ b/test/storage/sqlite.test.cpp @@ -9,21 +9,23 @@ TEST(SQLite, Statement) { mapbox::sqlite::Database db(":memory:", mapbox::sqlite::Create | mapbox::sqlite::ReadWrite); db.exec("CREATE TABLE test (id INTEGER);"); - mapbox::sqlite::Statement stmt1 = db.prepare("INSERT INTO test (id) VALUES (?1);"); - ASSERT_EQ(stmt1.lastInsertRowId(), 0); - ASSERT_EQ(stmt1.changes(), 0u); - stmt1.bind(1, 10); - stmt1.run(); - ASSERT_EQ(stmt1.lastInsertRowId(), 1); - ASSERT_EQ(stmt1.changes(), 1u); + mapbox::sqlite::Statement stmt1{ db, "INSERT INTO test (id) VALUES (?1);" }; + mapbox::sqlite::Query query1{ stmt1 }; + ASSERT_EQ(query1.lastInsertRowId(), 0); + ASSERT_EQ(query1.changes(), 0u); + query1.bind(1, 10); + query1.run(); + ASSERT_EQ(query1.lastInsertRowId(), 1); + ASSERT_EQ(query1.changes(), 1u); - mapbox::sqlite::Statement stmt2 = db.prepare("INSERT INTO test (id) VALUES (?1);"); - ASSERT_EQ(stmt2.lastInsertRowId(), 0); - ASSERT_EQ(stmt2.changes(), 0u); - stmt2.bind(1, 20); - stmt2.run(); - ASSERT_EQ(stmt2.lastInsertRowId(), 2); - ASSERT_EQ(stmt2.changes(), 1u); + mapbox::sqlite::Statement stmt2{ db, "INSERT INTO test (id) VALUES (?1);" }; + mapbox::sqlite::Query query2{ stmt2 }; + ASSERT_EQ(query2.lastInsertRowId(), 0); + ASSERT_EQ(query2.changes(), 0u); + query2.bind(1, 20); + query2.run(); + ASSERT_EQ(query2.lastInsertRowId(), 2); + ASSERT_EQ(query2.changes(), 1u); } TEST(SQLite, TEST_REQUIRES_WRITE(CantOpenException)) { @@ -33,6 +35,6 @@ TEST(SQLite, TEST_REQUIRES_WRITE(CantOpenException)) { mapbox::sqlite::Database("test/fixtures/offline_database/foobar123.db", mapbox::sqlite::ReadOnly); FAIL(); } catch (mapbox::sqlite::Exception& ex) { - ASSERT_EQ(ex.code, mapbox::sqlite::Exception::Code::CANTOPEN); + ASSERT_EQ(ex.code, mapbox::sqlite::ResultCode::CantOpen); } } |