diff --git a/README.md b/README.md index dd66aebfd..11c26080a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Cordova GoogleMaps plugin for Android, iOS and Browser v2.7.1 +# Cordova GoogleMaps plugin for Android, iOS and Browser v2.8.0 | Download | Build test (multiple_maps branch)| |----------|---------------------------| @@ -82,18 +82,15 @@ For browser platform, ``` - ## Install optional variables (config.xml) - - ![](https://raw.githubusercontent.com/mapsplugin/cordova-plugin-googlemaps/master/images/icon-android.png) **GOOGLE_MAPS_PLAY_SERVICES_VERSION = (16.0.1)**
- The Google Play Services SDK version. - _You need to specify the same version number with all other plugins._ - Check out the latest version [here](https://developers.google.com/android/guides/releases). - - - ![](https://raw.githubusercontent.com/mapsplugin/cordova-plugin-googlemaps/master/images/icon-android.png) **ANDROID_SUPPORT_V4_VERSION = (27.1.1)**
- This plugin requires the Android support library v4. - _The minimum version is 24.1.0._ - Check out the latest version [here](https://developer.android.com/topic/libraries/support-library/revisions.html). + - ![](https://raw.githubusercontent.com/mapsplugin/cordova-plugin-googlemaps/master/images/icon-android.png) **APP_COMPAT_VERSION = (1.1.0)**
+ **PLAY_SERVICES_BASEMENT_VERSION = (17.2.1)**
+ **PLAY_SERVICES_BASE_VERSION = (17.2.1)**
+ **PLAY_SERVICES_GCM_VERSION = (17.0.0)**
+ **PLAY_SERVICES_LOCATION_VERSION = (17.0.0)**
+ **GOOGLE_MAPS_ANDROID_SDK = (maps-sdk-3.0.0-beta)**
+ cordova-plugin-googlemaps version 2.8.0 or later uses [Maps SDK for Android v.3](https://developers.google.com/maps/documentation/android-sdk/v3-client-migration). - ![](https://raw.githubusercontent.com/mapsplugin/cordova-plugin-googlemaps/master/images/icon-ios.png) **LOCATION_WHEN_IN_USE_DESCRIPTION**
This message is displayed when your application requests **LOCATION PERMISSION for only necessary times**. @@ -113,34 +110,19 @@ For browser platform, ## Release Notes - - **v2.7.1** - - Fix: (iOS) UiWebView references present in v2.7.0 - - - **v2.7.0** - - Re-adoption: cordova-plugin-googlemaps-sdk dependency - - Important update: No longer support `UIWebView` on iOS. `WKWebView` only. - - Fix: (iOS) Can't load image files from local host on ionic 4 / 5 - - Update: (Android) prevent null pointer error in AsyncLoadImage.java - - Fix: Css animation interference when call setDiv and there is a push/pop page - - Fix: (Android/iOS/Browser) KML parser crash - - Fix: flickering and wrong rendering of some DOM elements - - Add: `map.stopAnimation()` - - Fix: can't remove map while map.animateCamera() is running - - Update: (Android) Increase cache memory size - - Update: (Android/iOS) Danish localization - - Fix: (Android) Prevent resize event after `map.setDiv(null)` - - Fix: (Android/iOS) Can not interactive with the map inside
- - Fix: jslint errors - - Fix: marker.setIcon crashes - - Update: Set default value range to heading and tilt - - Fix: (Android/iOS) touch detection is wrong after clicking on back button very soon. - - Fix: An error occurs when you click a marker of marker cluster #2660 - - Remove promise-7.0.4-min.js.map - - Fix: (iOS) bug fix: App crashes if "bearing" property is "" - - Fix: HTMLColor2RGBA() converts to incorrect value - - Fix: (Android) Can't load marker image from the Internet - - many bug fixes... - + - **v2.8.0** + - Important Change: (Android) + If `android.useAndroidX=true` and `android.enableJetifier=true` are defined in `gradle.properties`, + this plugin uses [Maps SDK for Android v.3.0.0 BETA](https://developers.google.com/maps/documentation/android-sdk/v3-client-migration). + If no definition, this plugin uses `com.google.android.gms:play-services-maps` + + - Add: (Android/iOS/Browser) `mapOptions.preferences.restriction` which is able to set the camera bounds. + - Add: (Android/iOS/Browser) `mapOptions.preferences.clickableIcons` which is able to be disable clicking on POI icons. + - Bug fix: (Android/iOS/Browser) `mapOptions.preferences.building` does not work. + - Add: (Android/iOS/Browser) ElevationService + - Add: (Android/iOS/Browser) DirectionsService and `map.addDirectionsRenderer()` + - Change: (Android/iOS/Browser) `map.setDiv()`, `map.setOptions()` returns `Promise`. + - Change: (Android/iOS/Browser) Hides `__pluginDomId` and `__pluginMapId` properties. --------------------------------------------------------------------------------------------------------- diff --git a/package.json b/package.json index bc6c54e48..3aa12f64a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cordova-plugin-googlemaps", - "version": "2.7.1", + "version": "2.8.0-20200709-2008", "description": "Google Maps native SDK for Android and iOS, and Google Maps JavaScript API v3 for browser.", "cordova": { "id": "cordova-plugin-googlemaps", diff --git a/plugin.xml b/plugin.xml index 1a2c801c5..0a9fc9a16 100644 --- a/plugin.xml +++ b/plugin.xml @@ -1,5 +1,5 @@ - + cordova-plugin-googlemaps @@ -62,6 +62,15 @@ + + + + + + + + + @@ -182,6 +191,12 @@ + + + + + + @@ -196,6 +211,8 @@ + + @@ -206,6 +223,8 @@ + + @@ -282,6 +301,9 @@ + + + @@ -316,6 +338,12 @@ + + + + + + @@ -356,6 +384,10 @@ + + + + @@ -516,6 +548,12 @@ + + + + + + diff --git a/src/android/frameworks/pgm-custom.gradle b/src/android/frameworks/pgm-custom.gradle index 0144d229d..ac17586ea 100644 --- a/src/android/frameworks/pgm-custom.gradle +++ b/src/android/frameworks/pgm-custom.gradle @@ -1,3 +1,22 @@ +def getConvertListe(useAndroidX) { + def list = [ + ["com.google.android.gms.maps.model", "com.google.android.libraries.maps.model"], + ["com.google.android.gms.maps", "com.google.android.libraries.maps"] + ] + + if (!useAndroidX) { + def tmp; + def listN = list.size() + for (int i = 0; i < listN; i++) { + tmp = list[i][0] + list[i][0] = list[i][1] + list[i][1] = tmp + } + } + return list +} + + def searchFileInParents(target) { def searchPath = target for (int i = 0; i < 8; i++) { @@ -12,6 +31,59 @@ def searchFileInParents(target) { return null; } +def replacePackages(file, cvtList, useAndroidX) { + def filePath = file.toPath() + def lines = java.nio.file.Files.readAllLines(filePath) + def linesN = lines.size() + def listN = cvtList.size() + def anyChanges = false + for (int i = 0; i < linesN; i++) { + def line = lines.get(i) + def needReplace = false; + if (useAndroidX && line.contains('android.')) { + needReplace = true + } else if (!useAndroidX && line.contains('android')) { + needReplace = true + } + if (needReplace) { + for (int j = 0; j < listN; j++) { + if (line.contains(cvtList[j][0])) { + anyChanges = true + line = line.replaceAll(cvtList[j][0], cvtList[j][1]) + } + } + lines.set(i, line) + } + } + + if (anyChanges) { + println(" [changed] " + file) + def fileWriter = new FileWriter(file) + def output = new BufferedWriter(fileWriter) + for (int i = 0; i < linesN; i++) { + output.writeLine(lines.get(i)) + } + output.flush() + output.close() + fileWriter.close() + } + +} +def replaceFiles(matchingPattern, cvtList, useAndroidX) { + + PatternFilterable patternSet = new PatternSet(); + patternSet.include(matchingPattern); + + def matchedFile = project.files(project.getProjectDir().getParentFile().toPath()).getAsFileTree().matching(patternSet) + matchedFile.each { file -> + if (!java.nio.file.Files.isReadable(file.toPath())) { + return + } + + replacePackages(file, cvtList, useAndroidX) + } +} + android { // #1. Find (project)/config.xml file, // because ionic capacitor does not copy nodes @@ -35,13 +107,13 @@ android { if (widget.preference.size() == 0) { throw new GradleException("Please add '' to the config.xml file.") } - println("preference = " + widget.preference.size()) Properties props = new Properties() def isKeyFound = 0 - def useBetaSdk = 0 - props.setProperty("GOOGLE_MAPS_PLAY_SERVICES_VERSION", "16.0.0"); - props.setProperty("ANDROID_SUPPORT_V4_VERSION", "27.1.1"); - props.setProperty("GOOGLE_MAPS_ANDROID_SDK", ""); + + props.setProperty("GOOGLE_MAPS_PLAY_SERVICES_LOCATION_VERSION", "16.0.0"); + props.setProperty("GOOGLE_MAPS_ANDROID_SUPPORT_V4_VERSION", "27.1.1"); + props.setProperty("GOOGLE_MAPS_PLAY_SERVICES_VERSION", "16.1.0"); + widget.preference.each { pref -> def name = pref.attributes().name @@ -51,14 +123,11 @@ android { isKeyFound = 1 props.setProperty(name, value) } - if (name == "GOOGLE_MAPS_PLAY_SERVICES_VERSION" || - name == "ANDROID_SUPPORT_V4_VERSION") { + if (name == "GOOGLE_MAPS_PLAY_SERVICES_LOCATION_VERSION"|| + name == "GOOGLE_MAPS_PLAY_SERVICES_VERSION" || + name == "GOOGLE_MAPS_ANDROID_SUPPORT_V4_VERSION") { props.setProperty(name, value) } - if (name == "GOOGLE_MAPS_ANDROID_SDK") { - useBetaSdk = 1 - props.setProperty("GOOGLE_MAPS_ANDROID_SDK", value) - } } @@ -66,34 +135,47 @@ android { throw new GradleException("Please add '' to the config.xml file.") } - if (useBetaSdk == 1) { - def libsDirPath = System.getProperty("user.dir") - def libsDir = new File("${libsDirPath}/libs/") - if (!libsDir.exists()) { - libsDir.mkdirs() + def hasUseAndroidX = project.ext.has('android.useAndroidX') + def hasEnableJetifier = project.ext.has('android.enableJetifier') + def useAndroidX = hasUseAndroidX && hasEnableJetifier && + 'true' == project.ext.get('android.useAndroidX') && + 'true' == project.ext.get('android.enableJetifier') + + //----------------------------------------------- + // Replace the source code of this plugin + //----------------------------------------------- + def cvtList = getConvertListe(useAndroidX) + + replaceFiles("**/plugin/google/maps/*.java", cvtList, useAndroidX) + + if (useAndroidX) { + //---------- + // maps SDK + //---------- + + dependencies { + implementation 'com.google.android.libraries.maps:maps:3.1.0-beta' + implementation 'com.android.support:multidex:1.0.3' } - def mapsSDK = props.getProperty("GOOGLE_MAPS_ANDROID_SDK") - def f = new File("${libsDir.absolutePath}/${mapsSDK}.aar") - println("---->lib = " + f.absolutePath); - if (!f.exists()) { - new URL("https://dl.google.com/dl/geosdk/${mapsSDK}.aar").withInputStream{ i -> f.withOutputStream{ it << i }} + defaultConfig { + multiDexEnabled true } + + } else { + def PLAY_SERVICES_VERSION = props.getProperty("GOOGLE_MAPS_PLAY_SERVICES_VERSION") + def PLAY_SERVICES_LOCATION_VERSION = props.getProperty("GOOGLE_MAPS_PLAY_SERVICES_LOCATION_VERSION") + def ANDROID_SUPPORT_V4_VERSION = props.getProperty("GOOGLE_MAPS_ANDROID_SUPPORT_V4_VERSION") + println("---->GOOGLE_MAPS_PLAY_SERVICES_VERSION = ${PLAY_SERVICES_VERSION}") + println("---->GOOGLE_MAPS_PLAY_SERVICES_LOCATION_VERSION = ${PLAY_SERVICES_LOCATION_VERSION}") + println("---->GOOGLE_MAPS_ANDROID_SUPPORT_V4_VERSION = ${ANDROID_SUPPORT_V4_VERSION}") dependencies { - implementation name: mapsSDK, ext:'aar' + implementation "com.google.android.gms:play-services-maps:${PLAY_SERVICES_VERSION}" + implementation "com.google.android.gms:play-services-location:${PLAY_SERVICES_LOCATION_VERSION}" + implementation "com.android.support:support-core-utils:${ANDROID_SUPPORT_V4_VERSION}" } } - def PLAY_SERVICES_VERSION = props.get("GOOGLE_MAPS_PLAY_SERVICES_VERSION") - def ANDROID_SUPPORT_V4_VERSION = props.get("ANDROID_SUPPORT_V4_VERSION") - println("---->PLAY_SERVICES_VERSION = ${PLAY_SERVICES_VERSION}") - println("---->ANDROID_SUPPORT_V4_VERSION = ${ANDROID_SUPPORT_V4_VERSION}") - dependencies { - implementation "com.google.android.gms:play-services-maps:${PLAY_SERVICES_VERSION}" - implementation "com.google.android.gms:play-services-location:${PLAY_SERVICES_VERSION}" - implementation "com.android.support:support-core-utils:${ANDROID_SUPPORT_V4_VERSION}" - } - buildTypes { debug { diff --git a/src/android/frameworks/tbxml-android.gradle b/src/android/frameworks/tbxml-android.gradle index d329088e1..4c4edcefc 100644 --- a/src/android/frameworks/tbxml-android.gradle +++ b/src/android/frameworks/tbxml-android.gradle @@ -13,7 +13,7 @@ dependencies { } android { - compileSdkVersion 26 + compileSdkVersion 28 packagingOptions { exclude 'README' diff --git a/src/android/plugin/google/maps/AsyncGetJsonWithURL.java b/src/android/plugin/google/maps/AsyncGetJsonWithURL.java new file mode 100644 index 000000000..efcda3418 --- /dev/null +++ b/src/android/plugin/google/maps/AsyncGetJsonWithURL.java @@ -0,0 +1,113 @@ +package plugin.google.maps; + +import android.net.Uri; +import android.os.AsyncTask; +import android.util.Log; + +import org.json.JSONObject; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.HashMap; + +public class AsyncGetJsonWithURL extends AsyncTask, Void, JSONObject> { + private AsyncHttpGetInterface callback; + + private final String TAG = "AsyncHttpGet"; + private String BASE_URI = ""; + + public AsyncGetJsonWithURL(String baseUri, AsyncHttpGetInterface callback) { + this.callback = callback; + this.BASE_URI = baseUri; + } + + protected JSONObject doInBackground(HashMap... params) { + + //-------------------------------- + // Load image from the Internet + //-------------------------------- + try { + Uri.Builder builder = Uri.parse(BASE_URI).buildUpon(); + for (String key: params[0].keySet()) { + builder.appendQueryParameter(key, params[0].get(key)); + } + + + String urlStr = builder.build().toString(); +// Log.d("Elevation", "url=" + urlStr); + URL url = new URL(urlStr); + + boolean redirect = true; + HttpURLConnection http = null; + String cookies = null; + int redirectCnt = 0; + while(redirect && redirectCnt < 10) { + redirect = false; + http = (HttpURLConnection)url.openConnection(); + http.setRequestMethod("GET"); + if (cookies != null) { + http.setRequestProperty("Cookie", cookies); + } + http.addRequestProperty("Accept-Language", "en-US,en;q=0.8"); + http.addRequestProperty("User-Agent", "Mozilla"); + http.setInstanceFollowRedirects(true); + HttpURLConnection.setFollowRedirects(true); + + // normally, 3xx is redirect + try { + int status = http.getResponseCode(); + if (status != HttpURLConnection.HTTP_OK) { + if (status == HttpURLConnection.HTTP_MOVED_TEMP + || status == HttpURLConnection.HTTP_MOVED_PERM + || status == HttpURLConnection.HTTP_SEE_OTHER) + redirect = true; + } + if (redirect) { + // get redirect url from "location" header field + url = new URL(http.getHeaderField("Location")); + + // get the cookie if need, for login + cookies = http.getHeaderField("Set-Cookie"); + + // Disconnect the current connection + http.disconnect(); + redirectCnt++; + continue; + } + if (status == HttpURLConnection.HTTP_OK) { + break; + } else { + return null; + } + } catch (Exception e) { + Log.e(TAG, "can not connect to " + url.toString(), e); + } + } + + InputStream inputStream = http.getInputStream(); + + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + int nRead; + byte[] data = new byte[16384]; + while ((nRead = inputStream.read(data, 0, data.length)) != -1) { + buffer.write(data, 0, nRead); + } + buffer.flush(); + inputStream.close(); + return new JSONObject(buffer.toString()); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + + } + + + @Override + protected void onPostExecute(JSONObject result) { + this.callback.onPostExecute(result); + } + +} diff --git a/src/android/plugin/google/maps/AsyncHttpGetInterface.java b/src/android/plugin/google/maps/AsyncHttpGetInterface.java new file mode 100644 index 000000000..b0ca1be8a --- /dev/null +++ b/src/android/plugin/google/maps/AsyncHttpGetInterface.java @@ -0,0 +1,7 @@ +package plugin.google.maps; + +import org.json.JSONObject; + +public interface AsyncHttpGetInterface { + public void onPostExecute(JSONObject result) ; +} diff --git a/src/android/plugin/google/maps/AsyncLoadImage.java b/src/android/plugin/google/maps/AsyncLoadImage.java index 16e0bb04b..9b0ababcd 100644 --- a/src/android/plugin/google/maps/AsyncLoadImage.java +++ b/src/android/plugin/google/maps/AsyncLoadImage.java @@ -197,10 +197,12 @@ protected AsyncLoadImageResult doInBackground(Void... params) { if (!iconUrl.startsWith("data:image")) { if (iconUrl.startsWith("http://localhost") || - iconUrl.startsWith("http://127.0.0.1")) { + iconUrl.startsWith("https://localhost") || + iconUrl.startsWith("http://127.0.0.1") || + iconUrl.startsWith("https://127.0.0.1")) { // Log.d(TAG, String.format("---->(201)iconURL = %s", iconUrl)); if (iconUrl.contains("://")) { - iconUrl = iconUrl.replaceAll("http://.+?/", "file:///android_asset/www/"); + iconUrl = iconUrl.replaceAll("https://.+?/", "file:///android_asset/www/"); } else { // Avoid WebViewLocalServer (because can not make a connection for some reason) iconUrl = "file:///android_asset/www/".concat(iconUrl); @@ -230,6 +232,16 @@ protected AsyncLoadImageResult doInBackground(Void... params) { iconUrl = iconUrl.replaceAll("(\\/\\.\\/+)+", "/"); //Log.d(TAG, "--> iconUrl = " + iconUrl); } + if (iconUrl.startsWith("http://localhost") || + iconUrl.startsWith("http://127.0.0.1")) { +// Log.d(TAG, String.format("---->(201)iconURL = %s", iconUrl)); + if (iconUrl.contains("://")) { + iconUrl = iconUrl.replaceAll("http://.+?/", "file:///android_asset/www/"); + } else { + // Avoid WebViewLocalServer (because can not make a connection for some reason) + iconUrl = "file:///android_asset/www/".concat(iconUrl); + } + } if (iconUrl.indexOf("file://") == 0 && !iconUrl.contains("file:///android_asset/")) { diff --git a/src/android/plugin/google/maps/BitmapCache.java b/src/android/plugin/google/maps/BitmapCache.java index fe3a7f401..b78d2b791 100644 --- a/src/android/plugin/google/maps/BitmapCache.java +++ b/src/android/plugin/google/maps/BitmapCache.java @@ -1,7 +1,7 @@ package plugin.google.maps; import android.graphics.Bitmap; -import android.util.LruCache; +import android.support.v4.util.LruCache; public class BitmapCache extends LruCache { @@ -23,4 +23,4 @@ protected void entryRemoved(boolean evicted, String key, Bitmap oldBitmap, Bitma oldBitmap = null; } } -} \ No newline at end of file +} diff --git a/src/android/plugin/google/maps/CordovaGoogleMaps.java b/src/android/plugin/google/maps/CordovaGoogleMaps.java index 7e965dcee..3b97a6f2d 100644 --- a/src/android/plugin/google/maps/CordovaGoogleMaps.java +++ b/src/android/plugin/google/maps/CordovaGoogleMaps.java @@ -60,7 +60,7 @@ public void initialize(final CordovaInterface cordova, final CordovaWebView webV if (root != null) { return; } - LOG.setLogLevel(LOG.ERROR); + LOG.setLogLevel(LOG.DEBUG); activity = cordova.getActivity(); final View view = webView.getView(); @@ -142,20 +142,20 @@ public void run() { final boolean finalIsNeedToUpdate = isNeedToUpdate; AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(activity); alertDialogBuilder - .setMessage(errorMsg) - .setCancelable(false) - .setPositiveButton(PluginUtil.getPgmStrings(activity,"pgm_google_close_button"), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog,int id) { - dialog.dismiss(); - if (finalIsNeedToUpdate) { - try { - activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=com.google.android.gms"))); - } catch (android.content.ActivityNotFoundException anfe) { - activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://play.google.com/store/apps/details?id=com.google.android.gms"))); + .setMessage(errorMsg) + .setCancelable(false) + .setPositiveButton(PluginUtil.getPgmStrings(activity,"pgm_google_close_button"), new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog,int id) { + dialog.dismiss(); + if (finalIsNeedToUpdate) { + try { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=com.google.android.gms"))); + } catch (android.content.ActivityNotFoundException anfe) { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://play.google.com/store/apps/details?id=com.google.android.gms"))); + } + } } - } - } - }); + }); AlertDialog alertDialog = alertDialogBuilder.create(); // show it @@ -183,13 +183,13 @@ public void onClick(DialogInterface dialog,int id) { AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(activity); alertDialogBuilder - .setMessage(PluginUtil.getPgmStrings(activity,"pgm_api_key_error")) - .setCancelable(false) - .setPositiveButton(PluginUtil.getPgmStrings(activity,"pgm_google_close_button"), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog,int id) { - dialog.dismiss(); - } - }); + .setMessage(PluginUtil.getPgmStrings(activity,"pgm_api_key_error")) + .setCancelable(false) + .setPositiveButton(PluginUtil.getPgmStrings(activity,"pgm_google_close_button"), new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog,int id) { + dialog.dismiss(); + } + }); AlertDialog alertDialog = alertDialogBuilder.create(); // show it @@ -357,13 +357,13 @@ public void putHtmlElements(final JSONArray args, final CallbackContext callback final JSONObject elements = args.getJSONObject(0); if (mPluginLayout == null) { - callbackContext.success(); - return; + callbackContext.success(); + return; } //Log.d(TAG, "--->stopFlag = " + mPluginLayout.stopFlag + ", mPluginLayout.needUpdatePosition = " + mPluginLayout.needUpdatePosition); if (!mPluginLayout.stopFlag || mPluginLayout.needUpdatePosition) { - mPluginLayout.putHTMLElements(elements); + mPluginLayout.putHTMLElements(elements); } //mPluginLayout.updateMapPositions(); @@ -545,7 +545,7 @@ public void onDestroy() { } - /** + /** * Called by the system when the device configuration changes while your activity is running. * * @param newConfig The new device configuration diff --git a/src/android/plugin/google/maps/MyPlugin.java b/src/android/plugin/google/maps/MyPlugin.java index 9af3faab6..348de063a 100644 --- a/src/android/plugin/google/maps/MyPlugin.java +++ b/src/android/plugin/google/maps/MyPlugin.java @@ -28,6 +28,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; + public class MyPlugin extends CordovaPlugin implements MyPluginInterface { public MyPlugin self = null; public final Map methods = new ConcurrentHashMap(); diff --git a/src/android/plugin/google/maps/MyPluginLayout.java b/src/android/plugin/google/maps/MyPluginLayout.java index 044f84d5c..6fc4cea48 100644 --- a/src/android/plugin/google/maps/MyPluginLayout.java +++ b/src/android/plugin/google/maps/MyPluginLayout.java @@ -365,12 +365,13 @@ public void run() { int childCnt = scrollFrameLayout.getChildCount(); View view; int index = childCnt; - for (int i = childCnt - 1; i >= 0; i--) { +// for (int i = childCnt - 1; i >= 0; i--) { + for (int i = 0; i < childCnt; i++) { view = scrollFrameLayout.getChildAt(i); if (view.getTag() == null) { continue; } - if (Integer.parseInt(view.getTag() + "") < depth) { + if (Integer.parseInt(view.getTag() + "") > depth) { index = i; break; } diff --git a/src/android/plugin/google/maps/ObjectCache.java b/src/android/plugin/google/maps/ObjectCache.java index 12df5d247..a520cdd99 100644 --- a/src/android/plugin/google/maps/ObjectCache.java +++ b/src/android/plugin/google/maps/ObjectCache.java @@ -1,6 +1,6 @@ package plugin.google.maps; -import android.util.LruCache; +import android.support.v4.util.LruCache; import java.util.HashSet; diff --git a/src/android/plugin/google/maps/PluginCircle.java b/src/android/plugin/google/maps/PluginCircle.java index 8b53685a1..58b888a78 100644 --- a/src/android/plugin/google/maps/PluginCircle.java +++ b/src/android/plugin/google/maps/PluginCircle.java @@ -253,13 +253,17 @@ public void remove(final JSONArray args, final CallbackContext callbackContext) callbackContext.success(); return; } + + if (pluginMap.objects != null) { + pluginMap.objects.remove("circle_" + circleHashCode); + pluginMap.objects.remove("circle_property_" + circleHashCode); + pluginMap.objects.remove("circle_property_" + circleHashCode); + } + cordova.getActivity().runOnUiThread(new Runnable() { @Override public void run() { circle.remove(); - if (pluginMap.objects != null) { - pluginMap.objects.remove(id); - } callbackContext.success(); } }); diff --git a/src/android/plugin/google/maps/PluginDirectionsService.java b/src/android/plugin/google/maps/PluginDirectionsService.java new file mode 100644 index 000000000..4b98f30bc --- /dev/null +++ b/src/android/plugin/google/maps/PluginDirectionsService.java @@ -0,0 +1,276 @@ +package plugin.google.maps; + +import android.app.Activity; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.util.Log; + +import com.google.android.gms.maps.model.LatLng; + +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaInterface; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.CordovaWebView; +import org.apache.cordova.PluginResult; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.HashMap; +import java.util.List; +import java.util.Locale; + +public class PluginDirectionsService extends CordovaPlugin { + private Activity activity; + private final String TAG = "PluginDirectionsService"; + private String API_KEY = ""; + + public void initialize(final CordovaInterface cordova, final CordovaWebView webView) { + super.initialize(cordova, webView); + activity = cordova.getActivity(); + + try { + ApplicationInfo ai = activity.getPackageManager().getApplicationInfo(activity.getPackageName(), PackageManager.GET_META_DATA); + this.API_KEY = ai.metaData.getString("com.google.android.geo.API_KEY"); + } catch (Exception e) { + // ignore + } + } + + + @Override + public boolean execute(final String action, final JSONArray args, final CallbackContext callbackContext) throws JSONException { + + HashMap params = new HashMap(); + + JSONObject opts = args.getJSONObject(0); + + //------------------- + // required parameters + //------------------- + params.put("origin", _decode_DirectionsRequestLocation(opts.getJSONObject("origin"))); + params.put("destination", _decode_DirectionsRequestLocation(opts.getJSONObject("destination"))); + params.put("key", this.API_KEY); + + //------------------- + // mode parameter + //------------------- + if (opts.has("travelMode")) { + // Default : driving + String travelMode = opts.getString("travelMode").toLowerCase(); + if (!"driving".equals(travelMode)) { + params.put("mode", travelMode); + + //------------------- + // transitOptions parameter + //------------------- + if ("transit".equals(travelMode) && opts.has("transitOptions")) { + JSONObject transitOptions = opts.getJSONObject("transitOptions"); + if (transitOptions.has("arrivalTime")) { + params.put("arrival_time", String.valueOf(transitOptions.getInt("arrivalTime"))); + } + if (transitOptions.has("departureTime")) { + params.put("departure_time", String.valueOf(transitOptions.getInt("departureTime"))); + } + } + } else { + + //------------------- + // DrivingOptions parameter + //------------------- + if (opts.has("drivingOptions")) { + JSONObject drivingOptions = opts.getJSONObject("drivingOptions"); + if (drivingOptions.has("departureTime")) { + params.put("departure_time", String.valueOf(drivingOptions.getInt("departureTime"))); + } + if (drivingOptions.has("trafficModel")) { + String trafficModel = drivingOptions.getString("trafficModel").toLowerCase(); + if (!"best_guess".equals(trafficModel)) { + params.put("traffic_model", trafficModel); + } + } + } + } + + //------------------- + // transitOptions parameter + //------------------- + if (opts.has("transitOptions")) { + JSONObject transitOptions = opts.getJSONObject("transitOptions"); + //------------------- + // transitOptions.modes parameter + //------------------- + if (transitOptions.has("modes")) { + JSONArray modes = transitOptions.getJSONArray("modes"); + StringBuilder buffer = new StringBuilder(); + int n = modes.length(); + for (int i = 0; i < n; i++) { + if (i > 0) { + buffer.append("|"); + } + buffer.append(modes.getString(i).toLowerCase()); + } + params.put("transit_mode", buffer.toString()); + } + //------------------- + // transitOptions.routingPreference parameter + //------------------- + if (transitOptions.has("routingPreference")) { + String routingPreference = transitOptions.getString("routingPreference").toLowerCase(); + params.put("transit_routing_preference", routingPreference); + } + } + } + + + + //------------------- + // waypoints parameter + //------------------- + if (opts.has("waypoints")) { + JSONArray waypoints = opts.getJSONArray("waypoints"); + StringBuilder buffer = new StringBuilder(); + int n = waypoints.length(); + + int cnt = 0; + if (opts.has("optimizeWaypoints") && opts.getBoolean("optimizeWaypoints")) { + cnt = 1; + buffer.append("optimize:true"); + } + for (int i = 0; i < n; i++) { + JSONObject point = waypoints.getJSONObject(i); + if (point.has("location")) { + boolean stopOver = false; + if (point.has("stopover")) { + stopOver = point.getBoolean("stopover"); + } + if (cnt > 0) { + buffer.append("|"); + } + if (!stopOver) { + buffer.append("via:"); + } + buffer.append(_decode_DirectionsRequestLocation(point.getJSONObject("location"))); + cnt++; + } + } + if (cnt > 0) { + params.put("waypoints", buffer.toString()); + } + } + //------------------- + // alternatives parameter + //------------------- + if (opts.has("provideRouteAlternatives") && opts.getBoolean("provideRouteAlternatives")) { + params.put("alternatives", "true"); + } + + //------------------- + // avoid parameter + //------------------- + boolean avoidFerries = false; + boolean avoidHighways = false; + boolean avoidTolls = false; + if (opts.has("avoidFerries")) { + avoidFerries = opts.getBoolean("avoidFerries"); + } + if (opts.has("avoidHighways")) { + avoidHighways = opts.getBoolean("avoidHighways"); + } + if (opts.has("avoidTolls")) { + avoidTolls = opts.getBoolean("avoidTolls"); + } + if (avoidFerries || avoidHighways || avoidTolls) { + StringBuilder buffer = new StringBuilder(); + if (avoidFerries) { + buffer.append("ferries|"); + } + if (avoidTolls) { + buffer.append("tolls|"); + } + if (avoidHighways) { + buffer.append("highways|"); + } + buffer.delete(buffer.length() - 2, buffer.length()); + params.put("avoid", buffer.toString()); + } + //------------------- + // language parameter + //------------------- + String lCode = Locale.getDefault().getLanguage(); + params.put("language", lCode); + + //------------------- + // units parameter + //------------------- + String localeFull = Locale.getDefault().getDisplayLanguage(); + if (opts.has("unitSystem")) { + params.put("units", opts.getString("unitSystem").toLowerCase()); + } else if ("en_US".equals(localeFull)) { + params.put("units", "imperial"); + } else { + params.put("units", "metric"); + } + + //------------------- + // region parameter + //------------------- + if (opts.has("region")) { + params.put("region", opts.getString("region").toLowerCase()); + } + + + AsyncGetJsonWithURL httpGet = new AsyncGetJsonWithURL("https://maps.googleapis.com/maps/api/directions/json", new AsyncHttpGetInterface() { + @Override + public void onPostExecute(JSONObject result) { + PluginResult pluginResult = null; + + try { + if (result == null) { + pluginResult = new PluginResult(PluginResult.Status.ERROR, "UNKNOWN_ERROR"); + } else { + if ("OK".equals(result.getString("status"))) { + pluginResult = new PluginResult(PluginResult.Status.OK, result); + } else { + pluginResult = new PluginResult(PluginResult.Status.ERROR, result.getString("status")); + } + } + } catch (Exception e){ + pluginResult = new PluginResult(PluginResult.Status.ERROR, "UNKNOWN_ERROR"); + } finally { + if (pluginResult == null) { + pluginResult = new PluginResult(PluginResult.Status.ERROR, "UNKNOWN_ERROR"); + } + callbackContext.sendPluginResult(pluginResult); + } + } + }); + + httpGet.execute(params); + return true; + } + + private String _decode_DirectionsRequestLocation(JSONObject position) throws JSONException { + String positionType = position.getString("type"); + + if ("string".equals(positionType)) { + return position.getString("value"); + } + + JSONObject value = position.getJSONObject("value"); + if ("location".equals(positionType)) { + return String.format("%f,%f", value.getDouble("lat"), value.getDouble("lng")); + } + if (value.has("placeId")) { + return String.format("place_id:%s", value.getString("placeId")); + } + if (value.has("location")) { + JSONObject location = value.getJSONObject("location"); + return String.format("%f,%f", location.getDouble("lat"), location.getDouble("lng")); + } + if (value.has("query")) { + return value.getString("query"); + } + return ""; + } +} diff --git a/src/android/plugin/google/maps/PluginElevationService.java b/src/android/plugin/google/maps/PluginElevationService.java new file mode 100644 index 000000000..b2c679ff9 --- /dev/null +++ b/src/android/plugin/google/maps/PluginElevationService.java @@ -0,0 +1,148 @@ +package plugin.google.maps; + +import android.app.Activity; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.util.Log; + +import com.google.android.gms.maps.model.LatLng; + +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaInterface; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.CordovaWebView; +import org.apache.cordova.PluginResult; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.HashMap; +import java.util.List; + +public class PluginElevationService extends CordovaPlugin { + private Activity activity; + private final String TAG = "PluginElevationService"; + private String API_KEY = ""; + + public void initialize(final CordovaInterface cordova, final CordovaWebView webView) { + super.initialize(cordova, webView); + activity = cordova.getActivity(); + + try { + ApplicationInfo ai = activity.getPackageManager().getApplicationInfo(activity.getPackageName(), PackageManager.GET_META_DATA); + this.API_KEY = ai.metaData.getString("com.google.android.geo.API_KEY"); + } catch (Exception e) { + // ignore + } + } + + + @Override + public boolean execute(final String action, final JSONArray args, final CallbackContext callbackContext) throws JSONException { + + cordova.getThreadPool().submit(new Runnable() { + @Override + public void run() { + try { + if ("getElevationAlongPath".equals(action)) { + PluginElevationService.this.getElevationAlongPath(args, callbackContext); + } else if ("getElevationForLocations".equals(action)) { + PluginElevationService.this.getElevationForLocations(args, callbackContext); + } + + } catch (JSONException e) { + e.printStackTrace(); + } + } + }); + return true; + + } + + + + @SuppressWarnings("unused") + public void getElevationAlongPath(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + + HashMap params = new HashMap(); + + JSONObject opts = args.getJSONObject(0); + + JSONArray points = opts.getJSONArray("path"); + List path = PluginUtil.JSONArray2LatLngList(points); + params.put("path", "enc:" + PluginUtil.encodePath(path)); + params.put("samples", opts.getString("samples")); + params.put("key", this.API_KEY); + + AsyncGetJsonWithURL httpGet = new AsyncGetJsonWithURL("https://maps.googleapis.com/maps/api/elevation/json", new AsyncHttpGetInterface() { + @Override + public void onPostExecute(JSONObject result) { + PluginResult pluginResult = null; + + try { + if (result == null) { + pluginResult = new PluginResult(PluginResult.Status.ERROR, "UNKNOWN_ERROR"); + } else { + if ("OK".equals(result.getString("status"))) { + pluginResult = new PluginResult(PluginResult.Status.OK, result); + } else { + pluginResult = new PluginResult(PluginResult.Status.ERROR, result.getString("status")); + } + } + } catch (Exception e){ + pluginResult = new PluginResult(PluginResult.Status.ERROR, "UNKNOWN_ERROR"); + } finally { + if (pluginResult == null) { + pluginResult = new PluginResult(PluginResult.Status.ERROR, "UNKNOWN_ERROR"); + } + callbackContext.sendPluginResult(pluginResult); + } + } + }); + + httpGet.execute(params); + } + + + @SuppressWarnings("unused") + public void getElevationForLocations(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + + HashMap params = new HashMap(); + + JSONObject opts = args.getJSONObject(0); + + JSONArray points = opts.getJSONArray("locations"); + List path = PluginUtil.JSONArray2LatLngList(points); + params.put("locations", "enc:" + PluginUtil.encodePath(path)); + params.put("key", this.API_KEY); + + AsyncGetJsonWithURL httpGet = new AsyncGetJsonWithURL("https://maps.googleapis.com/maps/api/elevation/json", new AsyncHttpGetInterface() { + @Override + public void onPostExecute(JSONObject result) { + PluginResult pluginResult = null; + + try { + if (result == null) { + pluginResult = new PluginResult(PluginResult.Status.ERROR, "UNKNOWN_ERROR"); + } else { + if ("OK".equals(result.getString("status"))) { + pluginResult = new PluginResult(PluginResult.Status.OK, result); + } else { + pluginResult = new PluginResult(PluginResult.Status.ERROR, result.getString("status")); + } + } + } catch (Exception e){ + pluginResult = new PluginResult(PluginResult.Status.ERROR, "UNKNOWN_ERROR"); + } finally { + if (pluginResult == null) { + pluginResult = new PluginResult(PluginResult.Status.ERROR, "UNKNOWN_ERROR"); + } + callbackContext.sendPluginResult(pluginResult); + } + } + }); + + httpGet.execute(params); + } + +} diff --git a/src/android/plugin/google/maps/PluginEnvironment.java b/src/android/plugin/google/maps/PluginEnvironment.java index df8eb98d7..56505db30 100644 --- a/src/android/plugin/google/maps/PluginEnvironment.java +++ b/src/android/plugin/google/maps/PluginEnvironment.java @@ -73,17 +73,6 @@ public void isAvailable(final JSONArray args, final CallbackContext callbackCont return; } - // ------------------------------ - // Check of Google Maps Android API v2 - // ------------------------------ - try { - @SuppressWarnings({ "rawtypes" }) - Class GoogleMapsClass = Class.forName("com.google.android.gms.maps.GoogleMap"); - } catch (Exception e) { - Log.e("GoogleMaps", "Error", e); - callbackContext.error(e.getMessage()); - return; - } callbackContext.success(); } diff --git a/src/android/plugin/google/maps/PluginGroundOverlay.java b/src/android/plugin/google/maps/PluginGroundOverlay.java index e5ba35e4d..229bf5694 100644 --- a/src/android/plugin/google/maps/PluginGroundOverlay.java +++ b/src/android/plugin/google/maps/PluginGroundOverlay.java @@ -207,6 +207,7 @@ public void remove(JSONArray args, final CallbackContext callbackContext) throws } synchronized (pluginMap.objects) { + pluginMap.objects.remove(id); pluginMap.objects.remove(id.replace("groundoverlay_", "groundoverlay_property_")); pluginMap.objects.remove(id.replace("groundoverlay_", "groundoverlay_initOpts_")); pluginMap.objects.remove(id.replace("groundoverlay_", "groundoverlay_bounds_")); diff --git a/src/android/plugin/google/maps/PluginLocationService.java b/src/android/plugin/google/maps/PluginLocationService.java index b385cc212..9976e5ddd 100644 --- a/src/android/plugin/google/maps/PluginLocationService.java +++ b/src/android/plugin/google/maps/PluginLocationService.java @@ -36,7 +36,6 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; - public class PluginLocationService extends CordovaPlugin { private Activity activity; private final String TAG = "PluginLocationService"; diff --git a/src/android/plugin/google/maps/PluginMap.java b/src/android/plugin/google/maps/PluginMap.java index a3d379f0e..0ea45af0f 100644 --- a/src/android/plugin/google/maps/PluginMap.java +++ b/src/android/plugin/google/maps/PluginMap.java @@ -81,7 +81,6 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; - public class PluginMap extends MyPlugin implements OnMarkerClickListener, OnInfoWindowClickListener, OnMapClickListener, OnMapLongClickListener, OnMarkerDragListener, GoogleMap.OnMapLoadedCallback, @@ -99,6 +98,7 @@ public class PluginMap extends MyPlugin implements OnMarkerClickListener, private String mapId; private boolean isVisible = true; private boolean isClickable = true; + private boolean clickableIcons = true; private final String TAG = mapId; private String mapDivId; public Map plugins = new ConcurrentHashMap(); @@ -388,6 +388,19 @@ public void onClick(View v) { } + if (preferences.has("restriction")) { + JSONObject restriction = preferences.getJSONObject("restriction"); + LatLng sw = new LatLng(restriction.getDouble("south"), restriction.getDouble("west")); + LatLng ne = new LatLng(restriction.getDouble("north"), restriction.getDouble("east")); + LatLngBounds bounds = new LatLngBounds(sw, ne); + + map.setLatLngBoundsForCameraTarget(bounds); + + map.setMinZoomPreference((float)restriction.getDouble("minZoom")); + map.setMaxZoomPreference((float)restriction.getDouble("maxZoom")); + + } + if (preferences.has("zoom")) { JSONObject zoom = preferences.getJSONObject("zoom"); if (zoom.has("minZoom")) { @@ -399,16 +412,15 @@ public void onClick(View v) { } - if (preferences.has("gestureBounds")) { - Object target = preferences.get("gestureBounds"); - @SuppressWarnings("rawtypes") - Class targetClass = target.getClass(); - if ("org.json.JSONArray".equals(targetClass.getName())) { - JSONArray points = preferences.getJSONArray("gestureBounds"); - LatLngBounds bounds = PluginUtil.JSONArray2LatLngBounds(points); - map.setLatLngBoundsForCameraTarget(bounds); - } + if (preferences.has("clickableIcons")) { + clickableIcons = preferences.getBoolean("clickableIcons"); } + + + if (preferences.has("building")) { + map.setBuildingsEnabled(preferences.getBoolean("building")); + } + } // Set event listener @@ -497,6 +509,11 @@ public void onCameraIdle() { }); } + /* + //*************************************************************************** + // Google Maps SDK for Android v3 beta causes crash for these processes. + // Tmporally commented out + //*************************************************************************** @Override public void onStart() { super.onStart(); @@ -531,6 +548,7 @@ public void onResume(boolean multitasking) { } //mapCtrl.mPluginLayout.addPluginOverlay(PluginMap.this); } + */ private class AdjustInitCamera implements Runnable { private JSONObject mParams; @@ -690,11 +708,11 @@ public void detachFromWebView(JSONArray args, final CallbackContext callbackCont public void resizeMap(JSONArray args, final CallbackContext callbackContext) throws JSONException { if (mapCtrl.mPluginLayout == null || mapDivId == null) { //Log.d("PluginMap", "---> resizeMap / mPluginLayout = null"); - callbackContext.success(); if (initCameraBounds != null) { mainHandler.postDelayed(new Runnable() { @Override public void run() { + callbackContext.success(); } }, 100); } @@ -726,8 +744,8 @@ public void run() { public void run() { if(mapCtrl.mPluginLayout == null || mapDivId == null) { - callbackContext.success(); - return; + callbackContext.success(); + return; } RectF drawRect = mapCtrl.mPluginLayout.HTMLNodeRectFs.get(mapDivId); @@ -749,7 +767,12 @@ public void run() { params.topMargin = y; mapView.setLayoutParams(params); - callbackContext.success(); + mainHandler.postDelayed(new Runnable() { + @Override + public void run() { + callbackContext.success(); + } + }, 500); } } }); @@ -1277,7 +1300,7 @@ public void run() { e.printStackTrace(); } if (results.cameraBounds != null) { - fitBounds(results.cameraBounds, (int)(results.cameraPadding / density)); + fitBounds(results.cameraBounds, (int)(results.cameraPadding * density)); } } @@ -1322,28 +1345,58 @@ public void run() { } if (preferences.has("zoom")) { - JSONObject zoom = preferences.getJSONObject("zoom"); - if (zoom.has("minZoom")) { - map.setMinZoomPreference((float)zoom.getDouble("minZoom")); - } - if (zoom.has("maxZoom")) { - map.setMaxZoomPreference((float)zoom.getDouble("maxZoom")); + if (!"null".equals(preferences.getString("zoom"))) { + JSONObject zoom = preferences.getJSONObject("zoom"); + if (zoom.has("minZoom")) { + map.setMinZoomPreference((float) zoom.getDouble("minZoom")); + } + if (zoom.has("maxZoom")) { + map.setMaxZoomPreference((float) zoom.getDouble("maxZoom")); + } + } else { + map.setMinZoomPreference(2); + map.setMaxZoomPreference(23); } } + if (preferences.has("building")) { + map.setBuildingsEnabled(preferences.getBoolean("building")); + } + + if (preferences.has("clickableIcons")) { + clickableIcons = preferences.getBoolean("clickableIcons"); + } + + if (preferences.has("restriction")) { + if (!"null".equals(preferences.getString("restriction"))) { - if (preferences.has("gestureBounds")) { - Object target = preferences.get("gestureBounds"); - @SuppressWarnings("rawtypes") - Class targetClass = target.getClass(); - if ("org.json.JSONArray".equals(targetClass.getName())) { - JSONArray points = preferences.getJSONArray("gestureBounds"); - if (points.length() > 0) { - LatLngBounds bounds = PluginUtil.JSONArray2LatLngBounds(points); - map.setLatLngBoundsForCameraTarget(bounds); + JSONObject restriction = preferences.getJSONObject("restriction"); + LatLngBounds.Builder builder = new LatLngBounds.Builder(); + builder.include(new LatLng(restriction.getDouble("south"), restriction.getDouble("west"))); + builder.include(new LatLng(restriction.getDouble("north"), restriction.getDouble("east"))); + map.setLatLngBoundsForCameraTarget(builder.build()); + + map.setMaxZoomPreference((float)restriction.getDouble("maxZoom")); + map.setMinZoomPreference((float)restriction.getDouble("minZoom")); + } else { + + if (preferences.has("zoom") && !"null".equals(preferences.getString("zoom"))) { + JSONObject zoom = preferences.getJSONObject("zoom"); + if (zoom.has("minZoom")) { + map.setMinZoomPreference((float) zoom.getDouble("minZoom")); + } else { + map.setMinZoomPreference(2); + } + if (zoom.has("maxZoom")) { + map.setMaxZoomPreference((float) zoom.getDouble("maxZoom")); + } else { + map.setMaxZoomPreference(23); + } } else { - map.setLatLngBoundsForCameraTarget(null); + map.setMinZoomPreference(2); + map.setMaxZoomPreference(23); } + map.setLatLngBoundsForCameraTarget(null); } } @@ -2933,6 +2986,11 @@ public void onIndoorLevelActivated(IndoorBuilding building) { } @Override public void onPoiClick(PointOfInterest pointOfInterest) { + if (!this.clickableIcons) { + this.onMapClick(pointOfInterest.latLng); + return; + } + String js = String.format(Locale.ENGLISH, "javascript:if('%s' in plugin.google.maps){plugin.google.maps['%s']({evtName: '%s', callback:'_onMapEvent', args:['%s', \"%s\", new plugin.google.maps.LatLng(%f, %f)]});}", mapId, mapId, "poi_click", pointOfInterest.placeId, pointOfInterest.name, pointOfInterest.latLng.latitude, pointOfInterest.latLng.longitude); jsCallback(js); @@ -3077,17 +3135,19 @@ public void run() { hitArea.x = (int)(polyline.getWidth() * density); hitArea.y = hitArea.x; double threshold = calculateDistance( - projection.fromScreenLocation(origin), - projection.fromScreenLocation(hitArea)); - touchPoint = isPointOnTheGeodesicLine(points, point, threshold); - if (touchPoint != null) { + projection.fromScreenLocation(origin), + projection.fromScreenLocation(hitArea)); + LatLng polyTouchPoint = isPointOnTheGeodesicLine(points, point, threshold); + if (polyTouchPoint != null) { + touchPoint = polyTouchPoint; hitOverlay = polyline; maxZIndex = zIndex; continue; } } else { - touchPoint = isPointOnTheLine(points, point); - if (touchPoint != null) { + LatLng polyTouchPoint = isPointOnTheLine(points, point); + if (polyTouchPoint != null) { + touchPoint = polyTouchPoint; hitOverlay = polyline; maxZIndex = zIndex; continue; diff --git a/src/android/plugin/google/maps/PluginMarker.java b/src/android/plugin/google/maps/PluginMarker.java index 0c9f6f7a8..d37050c1a 100644 --- a/src/android/plugin/google/maps/PluginMarker.java +++ b/src/android/plugin/google/maps/PluginMarker.java @@ -855,6 +855,9 @@ public void remove(final JSONArray args, final CallbackContext callbackContext) String propertyId = "marker_property_" + id; pluginMap.objects.remove(propertyId); + String imageSizeKey = "marker_imageSize_" + id; + pluginMap.objects.remove(imageSizeKey); + cordova.getActivity().runOnUiThread(new Runnable() { @Override public void run() { @@ -1147,8 +1150,8 @@ public void onPostExecute(AsyncLoadImage.AsyncLoadImageResult result) { self.pluginMap.objects.remove(markerImgSizeTag); self.pluginMap.objects.put(markerImgSizeTag, imageSize); - result.image.recycle(); - result.image = null; +// result.image.recycle(); // cause crash on maps-sdk-3.1.0-beta +// result.image = null; // The `anchor` of the `icon` property if (iconProperty.containsKey("anchor")) { diff --git a/src/android/plugin/google/maps/PluginPolyline.java b/src/android/plugin/google/maps/PluginPolyline.java index 8d1f52ca9..95643eb8d 100644 --- a/src/android/plugin/google/maps/PluginPolyline.java +++ b/src/android/plugin/google/maps/PluginPolyline.java @@ -39,15 +39,17 @@ public void create(final JSONArray args, final CallbackContext callbackContext) final String hashCode = args.getString(2); polylineHashCode = hashCode; + int pointCnt = 0; if (opts.has("points")) { JSONArray points = opts.getJSONArray("points"); List path = PluginUtil.JSONArray2LatLngList(points); - int i = 0; - for (i = 0; i < path.size(); i++) { + for (int i = 0; i < path.size(); i++) { polylineOptions.add(path.get(i)); builder.include(path.get(i)); } + pointCnt = path.size(); } + final int finalPointCnt = pointCnt; if (opts.has("color")) { color = PluginUtil.parsePluginColor(opts.getJSONArray("color")); polylineOptions.color(color); @@ -85,7 +87,11 @@ public void run() { pluginMap.objects.put(id, polyline); String boundsId = "polyline_bounds_" + hashCode; - pluginMap.objects.put(boundsId, builder.build()); + if (finalPointCnt > 0) { + pluginMap.objects.put(boundsId, builder.build()); + } else { + pluginMap.objects.put(boundsId, new LatLngBounds(new LatLng(360,360), new LatLng(360,360))); + } String propertyId = "polyline_property_" + hashCode; pluginMap.objects.put(propertyId, properties); @@ -160,6 +166,10 @@ public void remove(final JSONArray args, final CallbackContext callbackContext) id = "polyline_bounds_" + polylineHashCode; pluginMap.objects.remove(id); + String propertyKey = "polyline_property_" + polylineHashCode; + pluginMap.objects.remove(propertyKey); + + cordova.getActivity().runOnUiThread(new Runnable() { @Override public void run() { diff --git a/src/android/plugin/google/maps/PluginStreetViewPanorama.java b/src/android/plugin/google/maps/PluginStreetViewPanorama.java index b85a51e47..f14d7cb10 100644 --- a/src/android/plugin/google/maps/PluginStreetViewPanorama.java +++ b/src/android/plugin/google/maps/PluginStreetViewPanorama.java @@ -25,7 +25,6 @@ import java.util.Locale; - public class PluginStreetViewPanorama extends MyPlugin implements IPluginView, StreetViewPanorama.OnStreetViewPanoramaCameraChangeListener, StreetViewPanorama.OnStreetViewPanoramaChangeListener, diff --git a/src/android/plugin/google/maps/PluginUtil.java b/src/android/plugin/google/maps/PluginUtil.java index 1f098ffb6..634362820 100644 --- a/src/android/plugin/google/maps/PluginUtil.java +++ b/src/android/plugin/google/maps/PluginUtil.java @@ -432,4 +432,54 @@ public static String getPgmStrings(Activity activity, String key) { Resources res = activity.getResources(); return res.getString(resId); } + + public static String encodePath(List path) { + double plat = 0; + double plng = 0; + StringBuilder builder = new StringBuilder(); + + for (LatLng location: path) { + builder.append(_encodePoint(plat, plng, location.latitude, location.longitude)); + + plat = location.latitude; + plng = location.longitude; + } + + return builder.toString(); + } + + private static String _encodePoint(double plat, double plng, double lat, double lng) { + long late5 = Math.round(lat * 1e5); + long plate5 = Math.round(plat * 1e5); + + long lnge5 = Math.round(lng * 1e5); + long plnge5 = Math.round(plng * 1e5); + + long dlng = lnge5 - plnge5; + long dlat = late5 - plate5; + + return _encodeSignedNumber(dlat) + _encodeSignedNumber(dlng); + } + private static String _encodeSignedNumber(long num) { + long sgn_num = num << 1; + + if (num < 0) { + sgn_num = ~(sgn_num); + } + + return (_encodeNumber(sgn_num)); + } + + + private static String _encodeNumber(long num) { + StringBuilder builder = new StringBuilder(); + + while (num >= 0x20) { + builder.append(Character.toChars((int) (0x20 | (num & 0x1f)) + 63)); + num >>= 5; + } + + builder.append(Character.toChars((int) num + 63)); + return builder.toString(); + } } diff --git a/src/browser/CordovaGoogleMaps.js b/src/browser/CordovaGoogleMaps.js index 399393039..51dbdb302 100644 --- a/src/browser/CordovaGoogleMaps.js +++ b/src/browser/CordovaGoogleMaps.js @@ -71,13 +71,16 @@ var CordovaGoogleMaps = { // memory cleanup var mapIDs = Object.keys(MAPS); mapIDs.forEach(function(mapId) { - var mapDivId = document.querySelector('[__pluginmapid=\'' + mapId + '\']'); - if (!mapDivId) { - if (MAPS[mapDivId]) { - MAPS[mapDivId].destroy(); + var eles = Array.from(document.querySelectorAll('*')); + eles = eles.filter(function(e) { + return e.__pluginMapId === mapId; + }); + if (eles.length === 0) { + if (MAPS[mapId]) { + MAPS[mapId].destroy(); } - MAPS[mapDivId] = undefined; - delete MAPS[mapDivId]; + MAPS[mapId] = undefined; + delete MAPS[mapId]; } }); @@ -135,13 +138,16 @@ var CordovaGoogleMaps = { // memory cleanup var mapIDs = Object.keys(MAPS); mapIDs.forEach(function(mapId) { - var mapDivId = document.querySelector('[__pluginmapid=\'' + mapId + '\']'); - if (!mapDivId) { - if (MAPS[mapDivId]) { - MAPS[mapDivId].destroy(); + var eles = Array.from(document.querySelectorAll('*')); + eles = eles.filter(function(e) { + return e.__pluginMapId === mapId; + }); + if (eles.length === 0) { + if (MAPS[mapId]) { + MAPS[mapId].destroy(); } - MAPS[mapDivId] = undefined; - delete MAPS[mapDivId]; + MAPS[mapId] = undefined; + delete MAPS[mapId]; } }); diff --git a/src/browser/PluginDirectionsService.js b/src/browser/PluginDirectionsService.js new file mode 100644 index 000000000..e3682ba45 --- /dev/null +++ b/src/browser/PluginDirectionsService.js @@ -0,0 +1,261 @@ + + +var BaseArrayClass = require('cordova-plugin-googlemaps.BaseArrayClass'); + +function _cnv_DirectionsRoute(route) { + if (route.bounds) { + route.bounds = { + 'southwest' : { + 'lat': route.bounds.getSouthWest().lat(), + 'lng': route.bounds.getSouthWest().lng() + }, + 'northeast' : { + 'lat': route.bounds.getNorthEast().lat(), + 'lng': route.bounds.getNorthEast().lng() + } + }; + } + if (route.overview_path) { + route.overview_path = route.overview_path.map(function(latLng) { + return { + 'lat': latLng.lat(), + 'lng': latLng.lng() + }; + }); + } + if (route.legs) { + route.legs = route.legs.map(_cnv_DirectionsLeg); + } + return route; +} + +function _cnv_DirectionsLeg(leg) { + if (leg.arrival_time) { + leg.arrival_time.value = leg.arrival_time.value.getTime(); + } + if (leg.departure_time) { + leg.departure_time.value = leg.departure_time.value.getTime(); + } + if (leg.end_location) { + leg.end_location = { + 'lat': leg.end_location.lat(), + 'lng': leg.end_location.lng() + }; + } + if (leg.start_location) { + leg.start_location = { + 'lat': leg.start_location.lat(), + 'lng': leg.start_location.lng() + }; + } + if (leg.steps) { + leg.steps = leg.steps.map(_cnv_DirectionsStep); + } + if (leg.via_waypoints) { + leg.via_waypoints = leg.via_waypoints.map(function(latLng) { + return { + 'lat': latLng.lat(), + 'lng': latLng.lng() + }; + }); + } + return leg; +} + +function _cnv_DirectionsStep(step) { + if (step.start_location) { + step.start_location = { + 'lat': step.start_location.lat(), + 'lng': step.start_location.lng() + }; + } + if (step.end_location) { + step.end_location = { + 'lat': step.end_location.lat(), + 'lng': step.end_location.lng() + }; + } + if (step.path) { + step.path = step.path.map(function(latLng) { + return { + 'lat': latLng.lat(), + 'lng': latLng.lng() + }; + }); + } + if (step.steps) { + step.steps = step.steps.map(_cnv_DirectionsStep); + } + if (step.transit) { + step.steps = _cnv_TransitDetails(step.transit); + } + return step; +} +function _cnv_TransitDetails(transit) { + + if (transit.arrival_stop) { + transit.arrival_stop.location = { + 'lat': transit.arrival_stop.location.lat(), + 'lng': transit.arrival_stop.location.lng() + }; + } + if (transit.departure_stop) { + transit.departure_stop.location = { + 'lat': transit.departure_stop.location.lat(), + 'lng': transit.departure_stop.location.lng() + }; + } + if (transit.arrival_time) { + transit.arrival_time.value = transit.arrival_time.value.getTime(); + } + if (transit.departure_time) { + transit.departure_time.value = transit.departure_time.value.getTime(); + } + return transit; +} + +var service = null; +var lastRequestTime = 0; +var QUEUE = new BaseArrayClass(); +QUEUE.on('insert_at', function() { + if (!window.google || !window.google.maps) { + return; + } + if (QUEUE.getLength() === 1) { + this.trigger('next'); + } +}); +QUEUE.one('insert_at', function() { + + if (!window.google || !window.google.maps) { + setTimeout(arguments.callee.bind(this), 100); + return; + } + service = new google.maps.DirectionsService(); + this.trigger('next'); +}); +QUEUE.on('next', function() { + var self = QUEUE; + if (!service || self._executing || QUEUE.getLength() === 0) { + return; + } + if (Date.now() - lastRequestTime < 300) { + setTimeout(function() { + self.trigger('next'); + }, 300 + Math.floor(Math.random() * 200)); + return; + } + lastRequestTime = Date.now(); + self._executing = true; + + var cmd = QUEUE.removeAt(0, true); + + service.route(cmd.request, function(result, status) { + switch(status) { + case google.maps.DirectionsStatus.INVALID_REQUEST: + self._executing = false; + cmd.onError('[directions] The DirectionsRequest provided was invalid.'); + return; + case google.maps.DirectionsStatus.MAX_WAYPOINTS_EXCEEDED: + self._executing = false; + cmd.onError('[directions] Too many DirectionsWaypoints were provided in the DirectionsRequest.'); + return; + case google.maps.DirectionsStatus.NOT_FOUND: + self._executing = false; + cmd.onError('[directions] At least one of the origin, destination, or waypoints could not be geocoded.'); + return; + case google.maps.DirectionsStatus.OVER_QUERY_LIMIT: + QUEUE.insertAt(0, cmd); + console.warn('[directions] Due to the OVER_QUERY_LIMIT error, wait 3 sec, then try again.'); + setTimeout(function() { + self._executing = false; + self.trigger('next'); + }, 3000 + Math.floor(Math.random() * 200)); + return; + case google.maps.DirectionsStatus.REQUEST_DENIED: + self._executing = false; + cmd.onError('[directions] Google denited your directions request.'); + return; + case google.maps.DirectionsStatus.UNKNOWN_ERROR: + self._executing = false; + cmd.onError('[directions] There was an unknown error. Please try again.'); + return; + case google.maps.DirectionsStatus.ZERO_RESULTS: + self._executing = false; + cmd.onError('[directions] No route could be found between the origin and destination.'); + return; + } + + if (result.routes) { + result.routes = result.routes.map(_cnv_DirectionsRoute); + } + + self._executing = false; + cmd.onSuccess(result); + + // Insert delay to prevent the OVER_QUERY_LIMIT error. + var delay = 300 + Math.floor(Math.random() * 200); + setTimeout(function() { + self._executing = false; + self.trigger('next'); + }, delay); + }); + +}); + +function _decode_DirectionsRequestLocation(position) { + if (position.type === 'string') { + return position.value; + } else if (position.type === 'location') { + return { + 'lat': position.value.lat, + 'lng': position.value.lng + }; + } else { + var place = {}; + if (position.value.location) { + place.location = position.value.location; + } + if (position.value.placeId) { + place.placeId = position.value.placeId; + } + if (position.value.query) { + place.query = position.value.query; + } + return place; + } +} + +module.exports = { + 'route': function(onSuccess, onError, args) { + var request = args[0]; + + request.origin = _decode_DirectionsRequestLocation(request.origin); + request.destination = _decode_DirectionsRequestLocation(request.destination); + if (request.waypoints) { + request.waypoints = request.waypoints.map(function(point) { + point.location = _decode_DirectionsRequestLocation(point.location); + return point; + }); + } + + if (request.drivingOptions && typeof request.drivingOptions.departureTime === 'number') { + request.drivingOptions.departureTime = new Date(request.drivingOptions.departureTime); + } + if (request.transitOptions && typeof request.transitOptions.departureTime === 'number') { + request.transitOptions.departureTime = new Date(request.transitOptions.departureTime); + } + if (request.transitOptions && typeof request.transitOptions.arrivalTime === 'number') { + request.transitOptions.arrivalTime = new Date(request.transitOptions.arrivalTime); + } + + QUEUE.push({ + 'request': request, + 'onSuccess': onSuccess, + 'onError': onError + }); + } +}; + + +require('cordova/exec/proxy').add('PluginDirectionsService', module.exports); diff --git a/src/browser/PluginElevationService.js b/src/browser/PluginElevationService.js new file mode 100644 index 000000000..787e138dd --- /dev/null +++ b/src/browser/PluginElevationService.js @@ -0,0 +1,132 @@ + + +var BaseArrayClass = require('cordova-plugin-googlemaps.BaseArrayClass'); + +var service = null; +var lastRequestTime = 0; +var QUEUE = new BaseArrayClass(); +QUEUE.on('insert_at', function() { + if (!window.google || !window.google.maps) { + return; + } + if (QUEUE.getLength() === 1) { + this.trigger('next'); + } +}); +QUEUE.one('insert_at', function() { + + if (!window.google || !window.google.maps) { + setTimeout(arguments.callee.bind(this), 100); + return; + } + service = new google.maps.ElevationService(); + this.trigger('next'); +}); +QUEUE.on('next', function() { + var self = QUEUE; + if (!service || self._executing || QUEUE.getLength() === 0) { + return; + } + if (Date.now() - lastRequestTime < 300) { + setTimeout(function() { + self.trigger('next'); + }, 300 + Math.floor(Math.random() * 200)); + return; + } + lastRequestTime = Date.now(); + self._executing = true; + + var cmd = QUEUE.removeAt(0, true); + var methods = { + 'getElevationAlongPath': service.getElevationAlongPath, + 'getElevationForLocations': service.getElevationForLocations + }; + + var execMethod = methods[cmd.method]; + execMethod.call(service, cmd.elevationRequest, function(result, status) { + switch(status) { + case google.maps.ElevationStatus.INVALID_ERROR: + self._executing = false; + cmd.onError('[elevation] The request was invalid'); + return; + case google.maps.ElevationStatus.OVER_QUERY_LIMIT: + QUEUE.insertAt(0, cmd); + console.warn('[elevation] Due to the OVER_QUERY_LIMIT error, wait 3 sec, then try again.'); + setTimeout(function() { + self._executing = false; + self.trigger('next'); + }, 3000 + Math.floor(Math.random() * 200)); + return; + case google.maps.ElevationStatus.REQUEST_DENIED: + self._executing = false; + cmd.onError('[elevation] Google denited your elevation request.'); + return; + case google.maps.ElevationStatus.UNKNOWN_ERROR: + self._executing = false; + cmd.onError('[elevation] There was an unknown error. Please try again.'); + return; + } + + var pluginResults = []; + for (var i = 0; i < result.length; i++) { + pluginResults.push({ + 'elevation': result[i].elevation, + 'location': result[i].location.toJSON(), + 'resolution': result[i].resolution + }); + } + + self._executing = false; + cmd.onSuccess({ + 'results': pluginResults + }); + + // Insert delay to prevent the OVER_QUERY_LIMIT error. + var delay = 300 + Math.floor(Math.random() * 200); + setTimeout(function() { + self._executing = false; + self.trigger('next'); + }, delay); + }); + +}); + +module.exports = { + 'getElevationAlongPath': function(onSuccess, onError, args) { + var request = args[0]; + var params = {}; + params.samples = request.samples; + params.path = []; + for (var i = 0; i < request.path.length; i++) { + params.path.push(request.path[i]); + } + + QUEUE.push({ + 'method': 'getElevationAlongPath', + 'elevationRequest': params, + 'pluginRequest': request, + 'onSuccess': onSuccess, + 'onError': onError + }); + }, + + 'getElevationForLocations': function(onSuccess, onError, args) { + var request = args[0]; + var params = {}; + params.locations = []; + for (var i = 0; i < request.locations.length; i++) { + params.locations.push(request.locations[i]); + } + + QUEUE.push({ + 'method': 'getElevationForLocations', + 'elevationRequest': params, + 'pluginRequest': request, + 'onSuccess': onSuccess, + 'onError': onError + }); + } +}; + + +require('cordova/exec/proxy').add('PluginElevationService', module.exports); diff --git a/src/browser/PluginGeocoder.js b/src/browser/PluginGeocoder.js index 23b768c91..275074895 100644 --- a/src/browser/PluginGeocoder.js +++ b/src/browser/PluginGeocoder.js @@ -1,16 +1,23 @@ - var BaseArrayClass = require('cordova-plugin-googlemaps.BaseArrayClass'); var geocoder = null; var lastRequestTime = 0; var QUEUE = new BaseArrayClass(); QUEUE.on('insert_at', function() { + if (!window.google || !window.google.maps) { + return; + } if (QUEUE.getLength() === 1) { this.trigger('next'); } }); QUEUE.one('insert_at', function() { + + if (!window.google || !window.google.maps) { + setTimeout(arguments.callee.bind(this), 100); + return; + } geocoder = new google.maps.Geocoder(); this.trigger('next'); }); @@ -32,9 +39,11 @@ QUEUE.on('next', function() { geocoder.geocode(cmd.geocoderRequest, function(results, status) { switch(status) { case google.maps.GeocoderStatus.ERROR: + self._executing = false; cmd.onError('[geocoding] Cannot connect to Google servers'); return; case google.maps.GeocoderStatus.INVALID_REQUEST: + self._executing = false; cmd.onError('[geocoding] Invalid request for geocoder'); return; case google.maps.GeocoderStatus.OVER_QUERY_LIMIT: @@ -46,9 +55,11 @@ QUEUE.on('next', function() { }, 3000 + Math.floor(Math.random() * 200)); return; case google.maps.GeocoderStatus.REQUEST_DENIED: + self._executing = false; cmd.onError('[geocoding] Google denited your geocoding request.'); return; case google.maps.GeocoderStatus.UNKNOWN_ERROR: + self._executing = false; cmd.onError('[geocoding] There was an unknown error. Please try again.'); return; } diff --git a/src/browser/PluginGroundOverlay.js b/src/browser/PluginGroundOverlay.js index 4ea07f715..1b20f83c3 100644 --- a/src/browser/PluginGroundOverlay.js +++ b/src/browser/PluginGroundOverlay.js @@ -257,14 +257,14 @@ function CustomGroundOverlay(url, bounds, options) { }); self.set('touchPolygon', touchPolygon); - var markers = []; - for (var i = 0; i < 4; i++) { - markers.push(new google.maps.Marker({ - 'position': {'lat': 0, 'lng': 0}, - 'map': options.map - })); - } - self.set('markers', markers); + // var markers = []; + // for (var i = 0; i < 4; i++) { + // markers.push(new google.maps.Marker({ + // 'position': {'lat': 0, 'lng': 0}, + // 'map': options.map + // })); + // } + // self.set('markers', markers); //--------------------------------------------------------- diff --git a/src/browser/PluginMap.js b/src/browser/PluginMap.js index 44e92bd7e..fb7a0fbd7 100644 --- a/src/browser/PluginMap.js +++ b/src/browser/PluginMap.js @@ -42,24 +42,59 @@ function displayGrayMap(container) { function PluginMap(mapId, options) { var self = this; BaseClass.apply(this); - var mapDiv = document.querySelector('[__pluginMapId=\'' + mapId + '\']'); - mapDiv.style.backgroundColor = 'rgb(229, 227, 223)'; + var eles = Array.from(document.querySelectorAll('*')); + eles = eles.filter(function(e) { + return e.__pluginMapId === mapId; + }); + var actualMapDiv = null; + if (eles.length === 1) { + actualMapDiv = eles[0]; + } var container = document.createElement('div'); container.style.userSelect='none'; container.style['-webkit-user-select']='none'; container.style['-moz-user-select']='none'; container.style['-ms-user-select']='none'; - mapDiv.style.position = 'relative'; container.style.position = 'absolute'; container.style.top = 0; container.style.bottom = 0; container.style.right = 0; container.style.left = 0; - mapDiv.insertBefore(container, mapDiv.firstElementChild); + actualMapDiv.insertBefore(container, actualMapDiv.firstElementChild); + + var shadowRoot = container.attachShadow({mode: 'open'}); + + var style = document.createElement('style'); + style.setAttribute('type', 'text/css'); + style.innerHTML = [ + ':host {color: black;}', + 'button.gm-control-active>img {display: none;}', + 'button.gm-control-active>img:nth-child(1) {display: inline;}' + ].join("\n"); + shadowRoot.appendChild(style); + + var mapDiv = document.createElement('div'); + mapDiv.style.position = 'relative'; + mapDiv.style.left = 0; + mapDiv.style.top = 0; + mapDiv.style.right = 0; + mapDiv.style.bottom = 0; + mapDiv.style.width = "100%"; + mapDiv.style.height = "100%"; + shadowRoot.appendChild(mapDiv); + + mapDiv.style.backgroundColor = 'rgb(229, 227, 223)'; + + + + + // mapDiv.insertBefore(container, mapDiv.firstElementChild); + // shadowRoot.appendChild(container); self.set('isGoogleReady', false); self.set('container', container); + // self.set('shadowRoot', shadowRoot); self.PLUGINS = {}; Object.defineProperty(self, '__pgmId', { @@ -97,7 +132,8 @@ function PluginMap(mapId, options) { minZoom: 2, disableDefaultUI: true, zoomControl: true, - center: {lat: 0, lng: 0} + center: {lat: 0, lng: 0}, + clickableIcons: true }; if (options) { @@ -113,74 +149,49 @@ function PluginMap(mapId, options) { mapInitOptions.zoomControl = options.controls.zoom == true; } } + + if (options.gestures) { + mapInitOptions.draggable = options.gestures.scroll; + mapInitOptions.gestureHandling = options.gestures.scroll; + mapInitOptions.disableDoubleClickZoom = !options.gestures.zoom; + } if (options.preferences) { + + if (options.preferences && options.preferences.restriction) { + mapInitOptions.restriction = { + latLngBounds: { + south: options.preferences.restriction.south, + west: options.preferences.restriction.west, + north: options.preferences.restriction.north, + east: options.preferences.restriction.east + }, + strictBounds: false + }; + } + if (options.preferences.zoom) { mapInitOptions.minZoom = options.preferences.zoom.minZoom; if (options.preferences.zoom.maxZoom) { mapInitOptions.maxZoom = options.preferences.zoom.maxZoom; } } + + if ('clickableIcons' in options.preferences) { + mapInitOptions.clickableIcons = options.preferences.clickableIcons === true; + } } } - var map = new google.maps.Map(container, mapInitOptions); + var map = new google.maps.Map(mapDiv, mapInitOptions); map.mapTypes = mapTypeReg; self.set('map', map); - var boundsLimit = null; - if (options.preferences && options.preferences.gestureBounds && - options.preferences.gestureBounds.length > 0) { - boundsLimit = new google.maps.LatLngBounds(); - options.preferences.gestureBounds.forEach(function(pos) { - boundsLimit.extend(pos); - }); - } - map.set('boundsLimit', boundsLimit); var timeoutError = setTimeout(function() { self.trigger('load_error'); displayGrayMap(mapDiv); }, 3000); - map.addListener('bounds_changed', function() { - var boundsLimit = map.get('boundsLimit'); - if (!boundsLimit) { - return; - } - var visibleBounds = map.getBounds(); - if (boundsLimit.intersects(visibleBounds) || - visibleBounds.contains(boundsLimit.getNorthEast()) && visibleBounds.contains(boundsLimit.getSouthWest()) || - boundsLimit.contains(visibleBounds.getNorthEast()) && boundsLimit.contains(visibleBounds.getSouthWest())) { - return; - } - var center = map.getCenter(); - var dummyLat = center.lat(), - dummyLng = center.lng(); - var ne = boundsLimit.getNorthEast(), - sw = boundsLimit.getSouthWest(); - if (dummyLat < sw.lat() ) { - dummyLat = sw.lat(); - } else if (dummyLat > ne.lat()) { - dummyLat = ne.lat(); - } - if (dummyLng < 0) { - // the Western Hemisphere - if (dummyLng > ne.lng()) { - dummyLng = ne.lng(); - } else if (dummyLng < sw.lng()) { - dummyLng = sw.lng(); - } - } else { - // the Eastern Hemisphere - if (dummyLng > ne.lng()) { - dummyLng = ne.lng(); - } else if (dummyLng < sw.lng()) { - dummyLng = sw.lng(); - } - } - var dummyLatLng = new google.maps.LatLng(dummyLat, dummyLng); - map.panTo(dummyLatLng); - }); google.maps.event.addListenerOnce(map, 'projection_changed', function() { clearTimeout(timeoutError); @@ -224,7 +235,7 @@ function PluginMap(mapId, options) { options.camera.target.forEach(function(pos) { bounds.extend(pos); }); - map.fitBounds(bounds, 5); + map.fitBounds(bounds, 'padding' in options.camera ? options.camera.padding || 0 : 5); } else { map.setCenter(options.camera.target); } @@ -256,7 +267,13 @@ PluginMap.prototype.setOptions = function(onSuccess, onError, args) { var map = self.get('map'), options = args[0]; - var mapInitOptions = {}; + var mapInitOptions = { + draggable: true, + gestureHandling: 'auto', + disableDoubleClickZoom: false, + heading: 0, + tilt: 0 + }; if (options) { if (options.mapType) { @@ -268,26 +285,51 @@ PluginMap.prototype.setOptions = function(onSuccess, onError, args) { if (options.controls) { if (options.controls.zoom !== undefined) { - mapInitOptions.zoomControl = options.controls.zoom == true; + mapInitOptions.zoomControl = options.controls.zoom === true; } } + if (options.gestures) { + mapInitOptions.draggable = options.gestures.scroll === true; + mapInitOptions.gestureHandling = options.gestures.scroll === true; + mapInitOptions.disableDoubleClickZoom = !(options.gestures.zoom === true); + } + if (options.preferences) { - if (options.preferences.zoom) { - mapInitOptions.minZoom = Math.max(options.preferences.zoom || 2, 2); - if (options.preferences.zoom.maxZoom) { - mapInitOptions.maxZoom = options.preferences.zoom.maxZoom; + if ('zoom' in options.preferences) { + if (options.preferences.zoom) { + if ('minZoom' in options.preferences.zoom) { + mapInitOptions.minZoom = Math.max(options.preferences.zoom.minZoom, 2); + } else { + mapInitOptions.minZoom = undefined; + } + if ('maxZoom' in options.preferences.zoom) { + mapInitOptions.maxZoom = Math.min(options.preferences.zoom.maxZoom, 23); + } else { + mapInitOptions.maxZoom = undefined; + } + } else { + mapInitOptions.zoom = undefined; } } - if ('gestureBounds' in options.preferences) { - var boundsLimit = null; - if (options.preferences.gestureBounds && options.preferences.gestureBounds.length > 0) { - boundsLimit = new google.maps.LatLngBounds(); - options.preferences.gestureBounds.forEach(function(pos) { - boundsLimit.extend(pos); - }); + if ('restriction' in options.preferences) { + if (options.preferences.restriction) { + mapInitOptions.restriction = { + latLngBounds: { + south: options.preferences.restriction.south, + west: options.preferences.restriction.west, + north: options.preferences.restriction.north, + east: options.preferences.restriction.east + }, + strictBounds: false + }; + } else { + mapInitOptions.restriction = undefined; } - map.set('boundsLimit', boundsLimit); + } + + if ('clickableIcons' in options.preferences) { + mapInitOptions.clickableIcons = options.preferences.clickableIcons === true; } } @@ -327,6 +369,7 @@ PluginMap.prototype.setOptions = function(onSuccess, onError, args) { onSuccess(); }; + PluginMap.prototype.setActiveMarkerId = function(onSuccess, onError, args) { var self = this, markerId = args[0]; @@ -347,22 +390,44 @@ PluginMap.prototype.setDiv = function(onSuccess, onError, args) { var self = this, map = self.get('map'), container = self.get('container'); + // shadowRoot = self.get('shadowRoot'); + if (!container) { + onError('[map.setDiv] container is undefined'); + return; + } if (args.length === 0) { - if (container && container.parentNode) { - container.parentNode.removeAttribute('__pluginMapId'); - container.parentNode.removeChild(container); + if (container.parentNode && container.parentNode.parentNode) { + container.parentNode.parentNode.removeChild(container.parentNode); } + onSuccess(); } else { - var domId = args[0]; - var mapDiv = document.querySelector('[__pluginDomId=\'' + domId + '\']'); - mapDiv.style.position = 'relative'; - mapDiv.insertBefore(container, mapDiv.firstElementChild); - mapDiv.setAttribute('__pluginMapId', self.__pgmId); + + if (container.parentNode && !container.parentNode.parentNode) { + var domId = args[0]; + var eles = Array.from(document.querySelectorAll('*')); + eles = eles.filter(function(e) { + return e.__pluginDomId === domId; + }); + var actualMapDiv = eles[0]; + Object.defineProperty(actualMapDiv, '__pluginMapId', { + enumerable: false, + value: self.__pgmId + }); + actualMapDiv.style.position = 'relative'; + actualMapDiv.insertBefore(container, actualMapDiv.firstElementChild); + + var offsetHeight = container.offsetHeight; + container.offsetHeight = offsetHeight - 1; + setTimeout(function() { + container.offsetHeight = offsetHeight; + google.maps.event.trigger(map, 'resize'); + }, 1000); + } + + onSuccess(); } - google.maps.event.trigger(map, 'resize'); - onSuccess(); }; PluginMap.prototype.resizeMap = function(onSuccess) { var self = this; diff --git a/src/browser/PluginMarkerCluster.js b/src/browser/PluginMarkerCluster.js index 68b304a2d..68d1e430d 100644 --- a/src/browser/PluginMarkerCluster.js +++ b/src/browser/PluginMarkerCluster.js @@ -550,6 +550,9 @@ function ClusterIconClass(options) { console.error(e); }; img.src = iconUrl; + + // Load icon image at once. The marker of Google Maps JavaScript v3 fails to load after img.onload frequently. + iconMarker.setIcon(icon.url); }); //debug diff --git a/src/browser/PluginStreetViewPanorama.js b/src/browser/PluginStreetViewPanorama.js index c5d45350f..33d3644a1 100644 --- a/src/browser/PluginStreetViewPanorama.js +++ b/src/browser/PluginStreetViewPanorama.js @@ -21,7 +21,11 @@ function displayGrayMap(container) { function PluginStreetViewPanorama(panoramaId, options) { var self = this; BaseClass.apply(this); - var panoramaDiv = document.querySelector('[__pluginMapId=\'' + panoramaId + '\']'); + var eles = Array.from(document.querySelectorAll('*')); + eles = eles.filter(function(e) { + return e.__pluginMapId === panoramaId; + }); + var panoramaDiv = eles[0]; panoramaDiv.style.backgroundColor = 'rgb(229, 227, 223)'; var container = document.createElement('div'); container.style.userSelect='none'; diff --git a/src/ios/GoogleMaps/CordovaGoogleMaps.m b/src/ios/GoogleMaps/CordovaGoogleMaps.m index e5e29de81..ca1fa759d 100644 --- a/src/ios/GoogleMaps/CordovaGoogleMaps.m +++ b/src/ios/GoogleMaps/CordovaGoogleMaps.m @@ -58,6 +58,10 @@ - (void)pluginInitialize [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; [GMSServices provideAPIKey:APIKey]; + + NSUserDefaults *myDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"cordova.plugin.googlemaps"]; + [myDefaults setObject:APIKey forKey:@"GOOGLE_MAPS_API_KEY"]; + [myDefaults synchronize]; }]; //------------------------------- diff --git a/src/ios/GoogleMaps/MyPluginScrollView.m b/src/ios/GoogleMaps/MyPluginScrollView.m index ad9d35f49..14a247e17 100644 --- a/src/ios/GoogleMaps/MyPluginScrollView.m +++ b/src/ios/GoogleMaps/MyPluginScrollView.m @@ -28,27 +28,33 @@ - (id)initWithFrame:(CGRect)aRect - (void)attachView:(UIView *)view depth:(NSInteger)depth { NSArray *subviews = [self subviews]; - UIView *subview; - NSInteger tag; int viewCnt = (int)[subviews count]; int index = viewCnt; - for (int i = 0; i < viewCnt; i++) { - subview = [subviews objectAtIndex: i]; - tag = subview.tag; - if (tag == 0) { - continue; - } - if (tag > depth) { + depth += viewCnt; + + NSArray *sortedArray; + sortedArray = [subviews sortedArrayUsingComparator:^NSComparisonResult(id a, id b) { + NSInteger first = ((UIView*)a).tag; + NSInteger second = ((UIView*)b).tag; + return first - second; + }]; + + + for (int i = 0; i < sortedArray.count; i++) { + ((UIView *)[sortedArray objectAtIndex:i]).tag = i; + + if (i > depth) { index = i; break; } } - + + [self insertSubview:view atIndex:index]; } - (void)detachView:(UIView *)view { [view removeFromSuperview]; - + } - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { diff --git a/src/ios/GoogleMaps/PluginDirectionsService.h b/src/ios/GoogleMaps/PluginDirectionsService.h new file mode 100644 index 000000000..2d4e3f687 --- /dev/null +++ b/src/ios/GoogleMaps/PluginDirectionsService.h @@ -0,0 +1,18 @@ +// +// PluginElevationService.h +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// + +#import +#import +#import "PluginUtil.h" + +@interface PluginDirectionsService : CDVPlugin + +@property (nonatomic, strong) NSOperationQueue *executeQueue; +- (void)getElevationAlongPath:(CDVInvokedUrlCommand*)command; +- (void)getElevationForLocations:(CDVInvokedUrlCommand*)command; +@end diff --git a/src/ios/GoogleMaps/PluginDirectionsService.m b/src/ios/GoogleMaps/PluginDirectionsService.m new file mode 100644 index 000000000..0bfa3102a --- /dev/null +++ b/src/ios/GoogleMaps/PluginDirectionsService.m @@ -0,0 +1,272 @@ +// +// PluginElevationService.m +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// + +#import "PluginDirectionsService.h" + +@implementation PluginDirectionsService + +- (void)pluginUnload +{ + if (self.executeQueue != nil){ + self.executeQueue.suspended = YES; + [self.executeQueue cancelAllOperations]; + self.executeQueue.suspended = NO; + self.executeQueue = nil; + } +} + +- (void)pluginInitialize +{ + self.executeQueue = [NSOperationQueue new]; + self.executeQueue.maxConcurrentOperationCount = 3; + +} + +- (NSString *)_decode_DirectionsRequestLocation: (NSDictionary *)position { + NSString *positionType = [position objectForKey:@"type"]; + + if ([@"string" isEqualToString: positionType]) { + return [[position objectForKey:@"value"] stringValue]; + } + + NSDictionary *value = [position objectForKey:@"value"]; + if ([@"location" isEqualToString:positionType]) { + return [NSString stringWithFormat:@"%f,%f", + [[value objectForKey:@"lat"] doubleValue], + [[value objectForKey:@"lng"] doubleValue] + ]; + } + if ([value objectForKey:@"placeId"]) { + return [NSString stringWithFormat:@"place_id:%@", + [[value objectForKey:@"placeId"] stringValue] + ]; + } + + if ([value objectForKey:@"location"]) { + NSDictionary *location = [value objectForKey:@"location"]; + return [NSString stringWithFormat:@"%f,%f", + [[location objectForKey:@"lat"] doubleValue], + [[location objectForKey:@"lng"] doubleValue] + ]; + } + if ([value objectForKey:@"query"]) { + return [value objectForKey:@"query"]; + } + return @""; +} + +- (void)route:(CDVInvokedUrlCommand*)command { + NSDictionary *opts = [command.arguments objectAtIndex:0]; + NSLog(@"route = %@", opts); + + + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + //----------------------- + // required parameters + //----------------------- + [params setObject:[self _decode_DirectionsRequestLocation:[opts objectForKey:@"origin"]] forKey:@"origin"]; + [params setObject:[self _decode_DirectionsRequestLocation:[opts objectForKey:@"destination"]] forKey:@"destination"]; + + //----------------------- + // mode parameter + //----------------------- + if ([opts objectForKey:@"travelMode"]) { + // Default: driving + NSString *travelMode = [opts objectForKey:@"travelMode"]; + travelMode = [travelMode lowercaseString]; + if (![@"driving" isEqualToString:travelMode]) { + [params setObject:travelMode forKey:@"mode"]; + + //----------------------- + // transitOptions parameter + //----------------------- + if ([@"transit" isEqualToString:travelMode] && + [opts objectForKey:@"transitOptions"]) { + NSDictionary *transitOptions = [opts objectForKey:@"transitOptions"]; + if ([transitOptions objectForKey:@"arrivalTime"]) { + [params setObject:[NSString stringWithFormat:@"%@", [transitOptions objectForKey:@"arrivalTime"]] forKey:@"arrival_time"]; + } + if ([transitOptions objectForKey:@"depatureTime"]) { + [params setObject:[NSString stringWithFormat:@"%@", [transitOptions objectForKey:@"depatureTime"]] forKey:@"depature_time"]; + } + } + } else { + //----------------------- + // DrivingOptions parameter + //----------------------- + if ([opts objectForKey:@"drivingOptions"]) { + NSDictionary *drivingOptions = [opts objectForKey:@"drivingOptions"]; + if ([drivingOptions objectForKey:@"depatureTime"]) { + [params setObject:[NSString stringWithFormat:@"%@", [drivingOptions objectForKey:@"depatureTime"]] forKey:@"depature_time"]; + } + if ([drivingOptions objectForKey:@"trafficModel"]) { + NSString *trafficModel = [[[opts objectForKey:@"trafficModel"] stringValue] lowercaseString]; + [params setObject:trafficModel forKey:@"traffic_model"]; + } + } + } + + //------------------- + // transitOptions parameter + //------------------- + if ([opts objectForKey:@"transitOptions"]) { + NSDictionary *transitOptions = [opts objectForKey:@"transitOptions"]; + //------------------- + // transitOptions.modes parameter + //------------------- + if ([transitOptions objectForKey:@"modes"]) { + NSArray *modes = [transitOptions objectForKey:@"modes"]; + NSString *buffer = @""; + for (int i = 0; i < modes.count; i++) { + if (i > 0) { + buffer = [buffer stringByAppendingString:@"|"]; + } + buffer = [buffer stringByAppendingString:[[modes objectAtIndex:i] lowercaseString]]; + } + [params setObject:buffer forKey:@"transit_mode"]; + } + //------------------- + // transitOptions.routingPreference parameter + //------------------- + if ([transitOptions objectForKey:@"routingPreference"]) { + NSString *routingPreference = [[[transitOptions objectForKey:@"routingPreference"] stringValue] lowercaseString]; + [params setObject:routingPreference forKey:@"transit_routing_preference"]; + } + } + } + //------------------- + // waypoints parameter + //------------------- + if ([opts objectForKey:@"waypoints"]) { + NSArray *waypoints = [opts objectForKey:@"waypoints"]; + NSString *buffer = @""; + int cnt = 0; + if ([opts objectForKey:@"optimizeWayPoints"]) { + cnt = 1; + buffer = @"optimize:true"; + } + for (int i = 0; i < waypoints.count; i++) { + NSDictionary *point = [waypoints objectAtIndex:i]; + bool stopOver = false; + if ([point objectForKey:@"location"]) { + if ([point objectForKey:@"stopover"]) { + stopOver = [point objectForKey:@"stopover"]; + } + if (cnt > 0) { + buffer = [buffer stringByAppendingString:@"%7C"]; + } + if (!stopOver) { + buffer = [buffer stringByAppendingString:@"via:"]; + } + buffer = [buffer stringByAppendingString:[self _decode_DirectionsRequestLocation:[point objectForKey:@"location"]]]; + cnt++; + } + } + if (cnt > 0) { + [params setObject:buffer forKey:@"waypoints"]; + } + } + //------------------- + // alternatives parameter + //------------------- + if ([opts objectForKey:@"provideRouteAlternatives"]) { + [params setObject:@"true" forKey:@"alternatives"]; + } + + //------------------- + // avoid parameter + //------------------- + bool avoidFerries = false; + bool avoidHighways = false; + bool avoidTolls = false; + if ([opts objectForKey:@"avoideFerries"]) { + avoidFerries = [[opts objectForKey:@"avoideFerries"] boolValue]; + } + if ([opts objectForKey:@"avoidHighways"]) { + avoidHighways = [[opts objectForKey:@"avoidHighways"] boolValue]; + } + if ([opts objectForKey:@"avoidTolls"]) { + avoidTolls = [[opts objectForKey:@"avoidTolls"] boolValue]; + } + if (avoidFerries || avoidHighways || avoidTolls) { + NSString *buffer = @""; + int cnt = 0; + if (avoidFerries) { + buffer = @"ferries"; + cnt = 1; + } + if (avoidTolls) { + if (cnt > 0) { + buffer = [buffer stringByAppendingString:@"|"]; + } + buffer = [buffer stringByAppendingString:@"tolls"]; + cnt++; + } + if (avoidHighways) { + if (cnt > 0) { + buffer = [buffer stringByAppendingString:@"|"]; + } + buffer = [buffer stringByAppendingString:@"highways"]; + } + [params setObject:buffer forKey:@"avoid"]; + } + + //------------------- + // language parameter + //------------------- + NSArray *preferredLanguages = [NSLocale preferredLanguages]; + NSString *localeCode = [preferredLanguages objectAtIndex:0]; + NSString *languageCode = [[localeCode componentsSeparatedByString:@"-"] firstObject]; + [params setObject:languageCode forKey:@"language"]; + + //------------------- + // units parameter + //------------------- + if ([opts objectForKey:@"unitSystem"]) { + [params setObject:[[[opts objectForKey:@"unitSystem"] stringValue] lowercaseString] forKey:@"units"]; +// } else if ([@"en_US" isEqualToString:localeCode]) { +// [params setObject:@"imperial" forKey:@"units"]; +// } else { +// [params setObject:@"metric" forKey:@"units"]; + } + + //------------------- + // region parameter + //------------------- + if ([opts objectForKey:@"region"]) { + [params setObject:[[[opts objectForKey:@"region"] stringValue] lowercaseString] forKey:@"region"]; + } + + [self httpGetWithDictionary:params command:command]; +} + +- (void)httpGetWithDictionary:(NSDictionary *)params command:(CDVInvokedUrlCommand *)command +{ + + NSString *urlStr = @"https://maps.googleapis.com/maps/api/directions/json?"; + + NSUserDefaults *myDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"cordova.plugin.googlemaps"]; + NSString *DEFAULT_APIKey = [myDefaults objectForKey:@"GOOGLE_MAPS_API_KEY"]; + urlStr = [urlStr stringByAppendingFormat:@"key=%@&", DEFAULT_APIKey]; + + [self.executeQueue addOperationWithBlock:^{ + [PluginUtil getJsonWithURL:urlStr params:params completionBlock:^(BOOL succeeded, NSDictionary *result, NSString *error) { + + CDVPluginResult* pluginResult; + if (!succeeded) { + NSLog(@"[directions] %@", error); + pluginResult = [CDVPluginResult resultWithStatus: CDVCommandStatus_ERROR messageAsString:error]; + } else { + pluginResult = [CDVPluginResult resultWithStatus: CDVCommandStatus_OK messageAsDictionary:result]; + } + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} + +@end diff --git a/src/ios/GoogleMaps/PluginElevationService.h b/src/ios/GoogleMaps/PluginElevationService.h new file mode 100644 index 000000000..d71af83ac --- /dev/null +++ b/src/ios/GoogleMaps/PluginElevationService.h @@ -0,0 +1,18 @@ +// +// PluginElevationService.h +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// + +#import +#import +#import "PluginUtil.h" + +@interface PluginElevationService : CDVPlugin + +@property (nonatomic, strong) NSOperationQueue *executeQueue; +- (void)getElevationAlongPath:(CDVInvokedUrlCommand*)command; +- (void)getElevationForLocations:(CDVInvokedUrlCommand*)command; +@end diff --git a/src/ios/GoogleMaps/PluginElevationService.m b/src/ios/GoogleMaps/PluginElevationService.m new file mode 100644 index 000000000..4c6ec7b2f --- /dev/null +++ b/src/ios/GoogleMaps/PluginElevationService.m @@ -0,0 +1,107 @@ +// +// PluginElevationService.m +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// + +#import "PluginElevationService.h" + +@implementation PluginElevationService + +- (void)pluginUnload +{ + if (self.executeQueue != nil){ + self.executeQueue.suspended = YES; + [self.executeQueue cancelAllOperations]; + self.executeQueue.suspended = NO; + self.executeQueue = nil; + } +} + +- (void)pluginInitialize +{ + self.executeQueue = [NSOperationQueue new]; + self.executeQueue.maxConcurrentOperationCount = 3; + +} + +- (void)getElevationAlongPath:(CDVInvokedUrlCommand*)command { + NSDictionary *json = [command.arguments objectAtIndex:0]; + + NSArray *path = [json objectForKey:@"path"]; + int i = 0; + NSDictionary *latLng; + GMSMutablePath *mutablePath = [GMSMutablePath path]; + for (i = 0; i < path.count; i++) { + latLng = [path objectAtIndex:i]; + [mutablePath + addCoordinate: + CLLocationCoordinate2DMake([[latLng objectForKey:@"lat"] doubleValue], [[latLng objectForKey:@"lng"] doubleValue])]; + } + + NSString *encodedPoint = [mutablePath encodedPath]; + encodedPoint = [encodedPoint stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLHostAllowedCharacterSet]; + encodedPoint = [NSString stringWithFormat:@"enc:%@", encodedPoint]; + + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + [params setObject:encodedPoint forKey:@"path"]; + [params setObject:[json objectForKey:@"samples"] forKey:@"samples"]; + + [self httpGetWithDictionary:params command:command]; +} + +-(void)getElevationForLocations:(CDVInvokedUrlCommand *)command +{ + + NSDictionary *json = [command.arguments objectAtIndex:0]; + + NSArray *path = [json objectForKey:@"locations"]; + int i = 0; + NSDictionary *latLng; + GMSMutablePath *mutablePath = [GMSMutablePath path]; + for (i = 0; i < path.count; i++) { + latLng = [path objectAtIndex:i]; + [mutablePath + addCoordinate: + CLLocationCoordinate2DMake([[latLng objectForKey:@"lat"] doubleValue], [[latLng objectForKey:@"lng"] doubleValue])]; + } + + NSString *encodedPoint = [mutablePath encodedPath]; + encodedPoint = [encodedPoint stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLHostAllowedCharacterSet]; + encodedPoint = [NSString stringWithFormat:@"enc:%@", encodedPoint]; + + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + [params setObject:encodedPoint forKey:@"locations"]; + + [self httpGetWithDictionary:params command:command]; +} + +- (void)httpGetWithDictionary:(NSDictionary *)params command:(CDVInvokedUrlCommand *)command +{ + + NSString *urlStr = @"https://maps.googleapis.com/maps/api/elevation/json?"; + + NSUserDefaults *myDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"cordova.plugin.googlemaps"]; + NSString *DEFAULT_APIKey = [myDefaults objectForKey:@"GOOGLE_MAPS_API_KEY"]; + urlStr = [urlStr stringByAppendingFormat:@"key=%@&", DEFAULT_APIKey]; + + [self.executeQueue addOperationWithBlock:^{ + [PluginUtil getJsonWithURL:urlStr params:params completionBlock:^(BOOL succeeded, NSDictionary *response, NSString *error) { + + CDVPluginResult* pluginResult; + if (!succeeded) { + NSLog(@"[elevation] %@", error); + pluginResult = [CDVPluginResult resultWithStatus: CDVCommandStatus_ERROR messageAsString:error]; + } else { + NSMutableDictionary *results = [NSMutableDictionary dictionary]; + [results setObject: [response objectForKey:@"results"] forKey:@"results"]; + pluginResult = [CDVPluginResult resultWithStatus: CDVCommandStatus_OK messageAsDictionary:results]; + } + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} + +@end diff --git a/src/ios/GoogleMaps/PluginMap.m b/src/ios/GoogleMaps/PluginMap.m index 31605c94c..29e764842 100644 --- a/src/ios/GoogleMaps/PluginMap.m +++ b/src/ios/GoogleMaps/PluginMap.m @@ -276,7 +276,7 @@ - (void)clear:(CDVInvokedUrlCommand *)command { [self.mapCtrl.plugins removeAllObjects]; - if (command != nil) { + if (command != (id)[NSNull null]) { CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; @@ -589,7 +589,7 @@ -(void)_changeCameraPosition: (NSString*)action requestMethod:(NSString *)reques if (self.isRemoved) { return; } - if (cameraBounds != nil){ + if (cameraBounds != (id)[NSNull null]){ GMSCameraPosition *cameraPosition2 = [GMSCameraPosition cameraWithLatitude:self.mapCtrl.map.camera.target.latitude longitude:self.mapCtrl.map.camera.target.longitude @@ -614,7 +614,7 @@ -(void)_changeCameraPosition: (NSString*)action requestMethod:(NSString *)reques if ([action isEqual: @"moveCamera"]) { [self.mapCtrl.map setCamera:cameraPosition]; - if (cameraBounds != nil){ + if (cameraBounds != (id)[NSNull null]){ double delayInSeconds = 0.5; dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ @@ -661,13 +661,13 @@ - (void)setActiveMarkerId:(CDVInvokedUrlCommand*)command { NSString *markerId = [command.arguments objectAtIndex:0]; GMSMarker *marker = [self.mapCtrl.objects objectForKey:markerId]; - if (marker != nil) { + if (marker != (id)[NSNull null]) { self.mapCtrl.map.selectedMarker = marker; self.mapCtrl.activeMarker = marker; } CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; - [(CDVCommandDelegateImpl *)self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; }]; } @@ -749,13 +749,13 @@ - (void)fromPointToLatLng:(CDVInvokedUrlCommand*)command { - (void)_setOptions:(NSDictionary *)initOptions requestMethod:(NSString *)requestMethod command:(CDVInvokedUrlCommand *)command { [[NSOperationQueue mainQueue] addOperationWithBlock:^{ - + NSLog(@"options=%@", initOptions); BOOL isEnabled = NO; //controls NSDictionary *controls = [initOptions objectForKey:@"controls"]; if (controls) { //compass - if ([controls valueForKey:@"compass"] != nil) { + if ([controls valueForKey:@"compass"] != (id)[NSNull null]) { isEnabled = [[controls valueForKey:@"compass"] boolValue]; if (isEnabled == true) { self.mapCtrl.map.settings.compassButton = YES; @@ -764,7 +764,7 @@ - (void)_setOptions:(NSDictionary *)initOptions requestMethod:(NSString *)reques } } //myLocationButton - if ([controls valueForKey:@"myLocationButton"] != nil) { + if ([controls valueForKey:@"myLocationButton"] != (id)[NSNull null]) { isEnabled = [[controls valueForKey:@"myLocationButton"] boolValue]; if (isEnabled == true) { self.mapCtrl.map.settings.myLocationButton = YES; @@ -773,7 +773,7 @@ - (void)_setOptions:(NSDictionary *)initOptions requestMethod:(NSString *)reques } } //myLocation - if ([controls valueForKey:@"myLocation"] != nil) { + if ([controls valueForKey:@"myLocation"] != (id)[NSNull null]) { isEnabled = [[controls valueForKey:@"myLocation"] boolValue]; if (isEnabled == true) { self.mapCtrl.map.myLocationEnabled = YES; @@ -782,7 +782,7 @@ - (void)_setOptions:(NSDictionary *)initOptions requestMethod:(NSString *)reques } } //indoorPicker - if ([controls valueForKey:@"indoorPicker"] != nil) { + if ([controls valueForKey:@"indoorPicker"] != (id)[NSNull null]) { isEnabled = [[controls valueForKey:@"indoorPicker"] boolValue]; if (isEnabled == true) { self.mapCtrl.map.settings.indoorPicker = YES; @@ -798,22 +798,22 @@ - (void)_setOptions:(NSDictionary *)initOptions requestMethod:(NSString *)reques NSDictionary *gestures = [initOptions objectForKey:@"gestures"]; if (gestures) { //rotate - if ([gestures valueForKey:@"rotate"] != nil) { + if ([gestures valueForKey:@"rotate"] != (id)[NSNull null]) { isEnabled = [[gestures valueForKey:@"rotate"] boolValue]; self.mapCtrl.map.settings.rotateGestures = isEnabled; } //scroll - if ([gestures valueForKey:@"scroll"] != nil) { + if ([gestures valueForKey:@"scroll"] != (id)[NSNull null]) { isEnabled = [[gestures valueForKey:@"scroll"] boolValue]; self.mapCtrl.map.settings.scrollGestures = isEnabled; } //tilt - if ([gestures valueForKey:@"tilt"] != nil) { + if ([gestures valueForKey:@"tilt"] != (id)[NSNull null]) { isEnabled = [[gestures valueForKey:@"tilt"] boolValue]; self.mapCtrl.map.settings.tiltGestures = isEnabled; } //zoom - if ([gestures valueForKey:@"zoom"] != nil) { + if ([gestures valueForKey:@"zoom"] != (id)[NSNull null]) { isEnabled = [[gestures valueForKey:@"zoom"] boolValue]; self.mapCtrl.map.settings.zoomGestures = isEnabled; } @@ -822,19 +822,19 @@ - (void)_setOptions:(NSDictionary *)initOptions requestMethod:(NSString *)reques NSDictionary *preferences = [initOptions objectForKey:@"preferences"]; if (preferences) { //padding - if ([preferences valueForKey:@"padding"] != nil) { + if ([preferences valueForKey:@"padding"] != (id)[NSNull null]) { NSDictionary *padding = [preferences valueForKey:@"padding"]; UIEdgeInsets current = self.mapCtrl.map.padding; - if ([padding objectForKey:@"left"] != nil) { + if ([padding objectForKey:@"left"] != (id)[NSNull null]) { current.left = [[padding objectForKey:@"left"] floatValue]; } - if ([padding objectForKey:@"top"] != nil) { + if ([padding objectForKey:@"top"] != (id)[NSNull null]) { current.top = [[padding objectForKey:@"top"] floatValue]; } - if ([padding objectForKey:@"bottom"] != nil) { + if ([padding objectForKey:@"bottom"] != (id)[NSNull null]) { current.bottom = [[padding objectForKey:@"bottom"] floatValue]; } - if ([padding objectForKey:@"right"] != nil) { + if ([padding objectForKey:@"right"] != (id)[NSNull null]) { current.right = [[padding objectForKey:@"right"] floatValue]; } @@ -842,35 +842,32 @@ - (void)_setOptions:(NSDictionary *)initOptions requestMethod:(NSString *)reques [self.mapCtrl.map setPadding:newPadding]; } //zoom - if ([preferences valueForKey:@"zoom"] != nil) { + if ([preferences valueForKey:@"zoom"] != (id)[NSNull null]) { NSDictionary *zoom = [preferences valueForKey:@"zoom"]; float minZoom = self.mapCtrl.map.minZoom; float maxZoom = self.mapCtrl.map.maxZoom; - if ([zoom objectForKey:@"minZoom"] != nil) { + if ([zoom objectForKey:@"minZoom"] != (id)[NSNull null]) { minZoom = [[zoom objectForKey:@"minZoom"] doubleValue]; } - if ([zoom objectForKey:@"maxZoom"] != nil) { + if ([zoom objectForKey:@"maxZoom"] != (id)[NSNull null]) { maxZoom = [[zoom objectForKey:@"maxZoom"] doubleValue]; } [self.mapCtrl.map setMinZoom:minZoom maxZoom:maxZoom]; } - // gestureBounds - if ([preferences valueForKey:@"gestureBounds"] != nil) { - NSDictionary *latLng = nil; - double latitude, longitude; - int i = 0; - NSArray *latLngList = [preferences objectForKey:@"gestureBounds"]; - GMSMutablePath *path = [GMSMutablePath path]; - for (i = 0; i < [latLngList count]; i++) { - latLng = [latLngList objectAtIndex:i]; - latitude = [[latLng valueForKey:@"lat"] doubleValue]; - longitude = [[latLng valueForKey:@"lng"] doubleValue]; - [path addLatitude:latitude longitude:longitude]; - } + // restriction + NSDictionary *restriction = [preferences valueForKey:@"restriction"]; + if (restriction == (id)[NSNull null]) { + [self _setCameraRestriction:nil]; + } else { + NSDictionary *restriction = [preferences objectForKey:@"restriction"]; + [self _setCameraRestriction:restriction]; + } - [self.mapCtrl.map setCameraTargetBounds:[[GMSCoordinateBounds alloc] initWithPath:path]]; + // building + if ([preferences valueForKey:@"building"] != nil) { + self.mapCtrl.map.buildingsEnabled = [[preferences valueForKey:@"building"] boolValue]; } } @@ -879,7 +876,7 @@ - (void)_setOptions:(NSDictionary *)initOptions requestMethod:(NSString *)reques if (styles) { NSError *error; GMSMapStyle *mapStyle = [GMSMapStyle styleWithJSONString:styles error:&error]; - if (mapStyle != nil) { + if (mapStyle != (id)[NSNull null]) { self.mapCtrl.map.mapStyle = mapStyle; self.mapCtrl.map.mapType = kGMSTypeNormal; } else { @@ -929,6 +926,31 @@ - (void)_setOptions:(NSDictionary *)initOptions requestMethod:(NSString *)reques }]; }; +- (void)_setCameraRestriction:(NSDictionary *)params { + + if (params == (id)[NSNull null] || params == nil) { + self.mapCtrl.map.cameraTargetBounds = nil; + double minZoom = 0; + double maxZoom = 23; + [self.mapCtrl.map setMinZoom:minZoom maxZoom:maxZoom]; + + } else { + + GMSMutablePath *path = [GMSMutablePath path]; + [path + addCoordinate: CLLocationCoordinate2DMake([[params objectForKey:@"south"] doubleValue], [[params objectForKey:@"west"] doubleValue])]; + [path + addCoordinate: CLLocationCoordinate2DMake([[params objectForKey:@"north"] doubleValue], [[params objectForKey:@"east"] doubleValue])]; + + GMSCoordinateBounds *bounds = [[GMSCoordinateBounds alloc] initWithPath:path]; + self.mapCtrl.map.cameraTargetBounds = bounds; + + double minZoom = [[params objectForKey:@"minZoom"] doubleValue]; + double maxZoom = [[params objectForKey:@"maxZoom"] doubleValue]; + [self.mapCtrl.map setMinZoom:minZoom maxZoom:maxZoom]; + } +} + - (void)setOptions:(CDVInvokedUrlCommand *)command { [self.mapCtrl.executeQueue addOperationWithBlock:^{ @@ -961,7 +983,7 @@ - (void)setPadding:(CDVInvokedUrlCommand *)command { - (void)getFocusedBuilding:(CDVInvokedUrlCommand*)command { [[NSOperationQueue mainQueue] addOperationWithBlock:^{ GMSIndoorBuilding *building = self.mapCtrl.map.indoorDisplay.activeBuilding; - if (building != nil || [building.levels count] == 0) { + if (building != (id)[NSNull null] || [building.levels count] == 0) { CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; return; diff --git a/src/ios/GoogleMaps/PluginMarker.m b/src/ios/GoogleMaps/PluginMarker.m index f06e4c659..ece03eeb5 100644 --- a/src/ios/GoogleMaps/PluginMarker.m +++ b/src/ios/GoogleMaps/PluginMarker.m @@ -73,7 +73,7 @@ -(void)create:(CDVInvokedUrlCommand *)command [createResult setObject:markerId forKey:@"__pgmId"]; [[NSOperationQueue mainQueue] addOperationWithBlock:^{ - CDVCommandDelegateImpl *cmdDelegate = (CDVCommandDelegateImpl *)self.commandDelegate; + __weak __auto_type weakSelf = self; [self _create:markerId markerOptions:json callbackBlock:^(BOOL successed, id result) { CDVPluginResult* pluginResult; @@ -93,7 +93,7 @@ -(void)create:(CDVInvokedUrlCommand *)command pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:createResult ]; } - [cmdDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + [weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; }]; }]; @@ -255,7 +255,7 @@ -(void)showInfoWindow:(CDVInvokedUrlCommand *)command } CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; - [(CDVCommandDelegateImpl *)self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; }]; }]; } @@ -270,7 +270,7 @@ -(void)hideInfoWindow:(CDVInvokedUrlCommand *)command self.mapCtrl.map.selectedMarker = nil; self.mapCtrl.activeMarker = nil; CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; - [(CDVCommandDelegateImpl *)self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; }]; }]; } @@ -295,7 +295,7 @@ -(void)getPosition:(CDVInvokedUrlCommand *)command [json setObject:longitude forKey:@"lng"]; CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:json]; - [(CDVCommandDelegateImpl *)self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; }]; } @@ -319,7 +319,7 @@ -(void)setTitle:(CDVInvokedUrlCommand *)command CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; - [(CDVCommandDelegateImpl *)self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; }]; }]; } @@ -338,7 +338,7 @@ -(void)setSnippet:(CDVInvokedUrlCommand *)command marker.snippet = [command.arguments objectAtIndex:1]; CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; - [(CDVCommandDelegateImpl *)self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; }]; }]; } @@ -358,7 +358,7 @@ -(void)remove:(CDVInvokedUrlCommand *)command marker = nil; CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; - [(CDVCommandDelegateImpl *)self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; }]; }]; } @@ -424,7 +424,7 @@ -(void)setIconAnchor:(CDVInvokedUrlCommand *)command } CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; - [(CDVCommandDelegateImpl *)self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; }]; } @@ -450,7 +450,7 @@ -(void)setInfoWindowAnchor:(CDVInvokedUrlCommand *)command } else { pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR]; } - [(CDVCommandDelegateImpl *)self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; }]; }]; @@ -470,7 +470,7 @@ -(void)setOpacity:(CDVInvokedUrlCommand *)command marker.opacity = [[command.arguments objectAtIndex:1] floatValue]; CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; - [(CDVCommandDelegateImpl *)self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; }]; }]; } @@ -488,7 +488,7 @@ -(void)setZIndex:(CDVInvokedUrlCommand *)command marker.zIndex = [[command.arguments objectAtIndex:1] intValue]; CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; - [(CDVCommandDelegateImpl *)self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; }]; }]; } @@ -507,7 +507,7 @@ -(void)setDraggable:(CDVInvokedUrlCommand *)command [marker setDraggable:isEnabled]; CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; - [(CDVCommandDelegateImpl *)self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; }]; }]; } @@ -530,7 +530,7 @@ -(void)setDisableAutoPan:(CDVInvokedUrlCommand *)command NSLog(@"--->propertyId = %@", propertyId); CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; - [(CDVCommandDelegateImpl *)self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; }]; } @@ -560,7 +560,7 @@ -(void)setVisible:(CDVInvokedUrlCommand *)command [self.mapCtrl.objects setObject:properties forKey:propertyId]; CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; - [(CDVCommandDelegateImpl *)self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; }]; }]; } @@ -582,7 +582,7 @@ -(void)setPosition:(CDVInvokedUrlCommand *)command [marker setPosition:position]; CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; - [(CDVCommandDelegateImpl *)self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; }]; }]; } @@ -601,7 +601,7 @@ -(void)setFlat:(CDVInvokedUrlCommand *)command [marker setFlat: isFlat]; CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; - [(CDVCommandDelegateImpl *)self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; }]; }]; } @@ -645,7 +645,7 @@ -(void)setIcon:(CDVInvokedUrlCommand *)command [iconProperty setObject:[rgbColor parsePluginColor] forKey:@"iconColor"]; } - CDVCommandDelegateImpl *cmdDelegate = (CDVCommandDelegateImpl *)self.commandDelegate; + __weak __auto_type weakSelf = self; [self setIcon_:marker iconProperty:iconProperty callbackBlock:^(BOOL successed, id resultObj) { CDVPluginResult* pluginResult; if (successed == NO) { @@ -653,7 +653,7 @@ -(void)setIcon:(CDVInvokedUrlCommand *)command } else { pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; } - [cmdDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + [weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; }]; }]; } @@ -671,7 +671,7 @@ -(void)setRotation:(CDVInvokedUrlCommand *)command [marker setRotation:degrees]; CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; - [(CDVCommandDelegateImpl *)self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; }]; }]; } @@ -686,11 +686,11 @@ -(void)setAnimation:(CDVInvokedUrlCommand *)command GMSMarker *marker = [self.mapCtrl.objects objectForKey:markerId]; NSString *animation = [command.arguments objectAtIndex:1]; - CDVCommandDelegateImpl *cmdDelegate = (CDVCommandDelegateImpl *)self.commandDelegate; + __weak __auto_type weakSelf = self; [self setMarkerAnimation_:animation marker:marker callbackBlock:^(void) { CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; - [cmdDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + [weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; }]; }]; }]; @@ -1463,11 +1463,11 @@ - (void)downloadImageWithURL:(NSURL *)url completionBlock:(void (^)(BOOL succeed if (![fileManager fileExistsAtPath:iconPath]) { NSLog(@"(error)There is no file at '%@'.", iconPath); completionBlock(NO, nil); - return; } else { UIImage *image = [UIImage imageNamed:iconPath]; completionBlock(YES, image); } + return; } NSURLRequest *req = [NSURLRequest requestWithURL:url diff --git a/src/ios/GoogleMaps/PluginMarkerCluster.m b/src/ios/GoogleMaps/PluginMarkerCluster.m index cbf706ff4..a7d61ca37 100644 --- a/src/ios/GoogleMaps/PluginMarkerCluster.m +++ b/src/ios/GoogleMaps/PluginMarkerCluster.m @@ -453,7 +453,7 @@ - (void)redrawClusters:(CDVInvokedUrlCommand*)command { - (void) endRedraw:(CDVInvokedUrlCommand*)command { NSLog(@"--->allResults = %@", self.allResults); CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:self.allResults]; - [(CDVCommandDelegateImpl *)self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; } - (void) deleteProcess:(NSDictionary *) params clusterId:(NSString *)clusterId{ diff --git a/src/ios/GoogleMaps/PluginUtil.h b/src/ios/GoogleMaps/PluginUtil.h index d7454edfa..ed620fb0c 100644 --- a/src/ios/GoogleMaps/PluginUtil.h +++ b/src/ios/GoogleMaps/PluginUtil.h @@ -20,7 +20,7 @@ #import "IPluginProtocol.h" #import "PluginViewController.h" #import -#import + typedef void (^MYCompletionHandler)(NSError *error); @@ -40,6 +40,7 @@ typedef void (^MYCompletionHandler)(NSError *error); @end @interface NSString (GoogleMapsPlugin) +- (NSString *)urlencode; - (NSString*)regReplace:(NSString*)pattern replaceTxt:(NSString*)replaceTxt options:(NSRegularExpressionOptions)options; @end @@ -49,10 +50,6 @@ typedef void (^MYCompletionHandler)(NSError *error); - (UIImage *)resize:(CGFloat)width height:(CGFloat)height; @end -@interface CDVCommandDelegateImpl (GoogleMapsPlugin) -- (void)hookSendPluginResult:(CDVPluginResult*)result callbackId:(NSString*)callbackId; -@end - // // animationDidStop for group animation // http://stackoverflow.com/a/28051909/697856 @@ -72,6 +69,11 @@ typedef void (^TIFAnimationGroupCompletionBlock)(); + (GMSMutablePath *)getMutablePathFromCircle:(CLLocationCoordinate2D)center radius:(double)radius; + (NSString *)getAbsolutePathFromCDVFilePath:(UIView*)theWebView cdvFilePath:(NSString *)cdvFilePath; + (NSString *)PGM_LOCALIZATION:(NSString *)key; ++ (void)getJsonWithURL:(NSString *)baseUrlStr params:(NSDictionary *)params completionBlock:(void (^)(BOOL succeeded, NSDictionary *response, NSString *error))completionBlock; ++ (void)getJsonWithURL:(NSURL *)url completionBlock:(void (^)(BOOL succeeded, NSDictionary *response, NSString *error))completionBlock; ++ (double)getZoomFromBounds:(GMSCoordinateBounds *)bounds mapWidth:(double)mapWidth mapHeight:(double)mapHeight; ++ (double)_zoom:(double)mapPx worldPx:(double)worldPx fraction:(double)fraction; ++ (double)_latRad:(double)lat; @end diff --git a/src/ios/GoogleMaps/PluginUtil.m b/src/ios/GoogleMaps/PluginUtil.m index b2b283d92..6689a9bdc 100644 --- a/src/ios/GoogleMaps/PluginUtil.m +++ b/src/ios/GoogleMaps/PluginUtil.m @@ -60,6 +60,26 @@ - (UIColor*)parsePluginColor @end @implementation NSString (GoogleMapsPlugin) +- (NSString *)urlencode { + NSMutableString *output = [NSMutableString string]; + const unsigned char *source = (const unsigned char *)[self UTF8String]; + int sourceLen = strlen((const char *)source); + for (int i = 0; i < sourceLen; ++i) { + const unsigned char thisChar = source[i]; + if (thisChar == ' '){ + [output appendString:@"+"]; + } else if (thisChar == '.' || thisChar == '-' || thisChar == '_' || thisChar == '~' || + (thisChar >= 'a' && thisChar <= 'z') || + (thisChar >= 'A' && thisChar <= 'Z') || + (thisChar >= '0' && thisChar <= '9')) { + [output appendFormat:@"%c", thisChar]; + } else { + [output appendFormat:@"%%%02X", thisChar]; + } + } + return output; +} + - (NSString*)regReplace:(NSString*)pattern replaceTxt:(NSString*)replaceTxt options:(NSRegularExpressionOptions)options { NSError *error = nil; @@ -108,24 +128,6 @@ -(UIImage *)resize:(CGFloat)width height:(CGFloat)height { @end -/* -@implementation CDVCommandDelegateImpl (GoogleMapsPlugin) - -- (void)hookSendPluginResult:(CDVPluginResult*)result callbackId:(NSString*)callbackId { - - NSRange pos = [callbackId rangeOfString:@"://"]; - if (pos.location == NSNotFound) { - [self sendPluginResult:result callbackId:callbackId]; - } else { - NSArray *tmp = [callbackId componentsSeparatedByString:@"://"]; - NSString *pluginName = [tmp objectAtIndex:0]; - CDVPlugin *plugin = [self getCommandInstance:pluginName]; - [plugin onHookedPluginResult:result callbackId:callbackId]; - } - -} -@end -*/ static char CAAnimationGroupBlockKey; @implementation CAAnimationGroup (Blocks) @@ -466,4 +468,82 @@ + (NSString *)PGM_LOCALIZATION:(NSString *)key { return result; } + ++ (void)getJsonWithURL:(NSString *)urlStr params:(NSDictionary *)params completionBlock:(void (^)(BOOL succeeded, NSDictionary *response, NSString *error))completionBlock { + + NSEnumerator *keys = [params keyEnumerator]; + NSString *pName; + while(pName = [keys nextObject]) { + urlStr = [urlStr stringByAppendingFormat:@"%@=%@&", pName, [params objectForKey:pName]]; + } + NSLog(@"url = %@", urlStr); + NSURL *url = [NSURL URLWithString: urlStr]; + [PluginUtil getJsonWithURL:url completionBlock: completionBlock]; +} + ++ (void)getJsonWithURL:(NSURL *)url completionBlock:(void (^)(BOOL succeeded, NSDictionary *response, NSString *error))completionBlock +{ + + NSURLRequest *req = [NSURLRequest requestWithURL:url + cachePolicy: NSURLRequestReloadIgnoringCacheData + timeoutInterval:5]; + //------------------------------------------------------------- + // Use NSURLSessionDataTask instead of [NSURLConnection sendAsynchronousRequest] + // https://stackoverflow.com/a/20871647 + //------------------------------------------------------------- + NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; + NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration]; + NSURLSessionDataTask *getTask = [session dataTaskWithRequest:req + completionHandler:^(NSData *data, NSURLResponse *res, NSError *error) { + [session finishTasksAndInvalidate]; + + + NSDictionary *json; + if (!error) { + json = [NSJSONSerialization + JSONObjectWithData:data + options:kNilOptions error:&error]; + } + + if (json) { + if ([@"OK" isEqualToString:[json objectForKey:@"status"]]) { + completionBlock(YES, json, nil); + } else { + completionBlock(NO, nil, [json objectForKey:@"status"]); + } + } else { + NSLog(@"[getJsonWithURL] error = %@", error.description); + completionBlock(NO, nil, @"UNKNOWN_ERROR"); + } + }]; + [getTask resume]; + + +} + + ++ (double)_latRad:(double)lat { + double sin = sinh(lat * M_PI / 180); + double radX2 = log((1 + sin) / (1 - sin)) / 2; + return MAX(MIN(radX2, M_PI), -M_PI) / 2; +} + ++ (double)_zoom:(double)mapPx worldPx:(double)worldPx fraction:(double)fraction { + return floor(log(mapPx / worldPx / fraction) / M_LN2); +} + ++ (double)getZoomFromBounds:(GMSCoordinateBounds *)bounds mapWidth:(double)mapWidth mapHeight:(double)mapHeight { + CLLocationCoordinate2D ne = bounds.northEast; + CLLocationCoordinate2D sw = bounds.southWest; + double latFraction = ([PluginUtil _latRad:ne.latitude] - [PluginUtil _latRad:sw.latitude]) / M_PI; + double lngDiff = ne.longitude - sw.longitude; + + double lngFraction = ((lngDiff < 0) ? (lngDiff + 360) : lngDiff) / 360; + + double latZoom = [PluginUtil _zoom: mapHeight worldPx:256 fraction: latFraction]; + double lngZoom = [PluginUtil _zoom: mapWidth worldPx:256 fraction: lngFraction]; + + return MIN(MIN(latZoom, lngZoom), 21); +} + @end diff --git a/src/notas.txt b/src/notas.txt new file mode 100644 index 000000000..7e752d25b --- /dev/null +++ b/src/notas.txt @@ -0,0 +1,4 @@ +2.8.1 - Quito CDVCommandDelegateImpl +Ver: https://github.com/mapsplugin/cordova-plugin-googlemaps/issues/2923 +The problem is that the current version of this plugin uses the CDVCommandDelegateImpl class which has been changed from Public to Private in cordova-ios@7 +so is no longer available for the plugin to use. diff --git a/www/BaseClass.js b/www/BaseClass.js index 94c908332..8065bbe70 100644 --- a/www/BaseClass.js +++ b/www/BaseClass.js @@ -1,3 +1,4 @@ + var VARS_FIELD = typeof Symbol === 'undefined' ? '__vars' + Date.now() : Symbol('vars'); var SUBSCRIPTIONS_FIELD = typeof Symbol === 'undefined' ? '__subs' + Date.now() : Symbol('subscriptions'); @@ -136,21 +137,42 @@ BaseClass.prototype = { }, off: function (eventName, listener) { + var self = this; if (!eventName && !listener) { - this[SUBSCRIPTIONS_FIELD] = {}; - return this; + var eventNames = Object.keys(self[SUBSCRIPTIONS_FIELD]); + eventNames.forEach(function(evtName) { + var keys = Object.keys(self[SUBSCRIPTIONS_FIELD][evtName]); + keys.forEach(function(_hashCode) { + var info = self[SUBSCRIPTIONS_FIELD][evtName][_hashCode]; + if (!info.listener._protect) { + delete self[SUBSCRIPTIONS_FIELD][evtName][_hashCode]; + } + }); + }); + return self; } if (eventName && !listener) { - this[SUBSCRIPTIONS_FIELD][eventName] = null; - delete this[SUBSCRIPTIONS_FIELD][eventName]; - } else if (this[SUBSCRIPTIONS_FIELD][eventName] && listener._hashCode) { - delete this[SUBSCRIPTIONS_FIELD][eventName][listener._hashCode]; + var keys = Object.keys(self[SUBSCRIPTIONS_FIELD][eventName]); + keys.forEach(function(_hashCode) { + var info = self[SUBSCRIPTIONS_FIELD][eventName][_hashCode]; + if (!info.listener._protect) { + delete self[SUBSCRIPTIONS_FIELD][eventName][_hashCode]; + } + }); + if (self[SUBSCRIPTIONS_FIELD][eventName].length === 0) { + delete self[SUBSCRIPTIONS_FIELD][eventName]; + } + } else if (self[SUBSCRIPTIONS_FIELD][eventName] && listener._hashCode) { + if (!self[SUBSCRIPTIONS_FIELD][eventName][listener._hashCode].listener._protect) { + delete self[SUBSCRIPTIONS_FIELD][eventName][listener._hashCode]; + } } - return this; + return self; }, + one: function (eventName, listener) { if (!listener || typeof listener !== 'function') { throw Error('Listener for one()/addEventListenerOnce() method is not a function'); diff --git a/www/Common.js b/www/Common.js index cf6201833..fc6afe04b 100644 --- a/www/Common.js +++ b/www/Common.js @@ -45,11 +45,14 @@ function HTMLColor2RGBA(colorValue, defaultOpacity) { } if (colorStr.match(/^#([0-9A-F]){3}$/i)) { matches = colorStr.match(/([0-9A-F])/ig); + var r = parseInt(matches[0], 16); + var g = parseInt(matches[1], 16); + var b = parseInt(matches[2], 16); return [ - parseInt(matches[0], 16), - parseInt(matches[1], 16), - parseInt(matches[2], 16), + (r << 4) + r, + (g << 4) + g, + (b << 4) + b, alpha ]; } @@ -59,10 +62,14 @@ function HTMLColor2RGBA(colorValue, defaultOpacity) { alpha = parseInt(alpha + alpha, 16); matches = colorStr.match(/([0-9A-F])/ig); + var r = parseInt(matches[0], 16); + var g = parseInt(matches[1], 16); + var b = parseInt(matches[2], 16); + return [ - parseInt(matches[0], 16), - parseInt(matches[1], 16), - parseInt(matches[2], 16), + (r << 4) + r, + (g << 4) + g, + (b << 4) + b, alpha ]; } @@ -295,11 +302,11 @@ function getZIndex(dom) { if (dom.currentStyle) { z = dom.currentStyle['z-index']; } - var elemId = dom.getAttribute('__pluginDomId'); + var elemId = dom.__pluginDomId; var parentNode = dom.parentNode; var parentZIndex = 0; if (parentNode && parentNode.nodeType === Node.ELEMENT_NODE) { - var parentElemId = parentNode.getAttribute('__pluginDomId'); + var parentElemId = parentNode.__pluginDomId; if (parentElemId in internalCache) { parentZIndex = internalCache[parentElemId]; } else { @@ -550,7 +557,7 @@ function convertToPositionArray(array) { array = array || []; if (!utils.isArray(array)) { - if (array.type === 'LatLngBounds') { + if (array.type === 'LatLngBounds' || array.southwest && array.northeast) { array = [ array.southwest, { @@ -677,14 +684,17 @@ function getPluginDomId(element) { if (!element || !shouldWatchByNative(element)) { return; } - var elemId = element.getAttribute('__pluginDomId'); + var elemId = element.__pluginDomId; if (!elemId) { if (element === document.body) { elemId = 'root'; } else { elemId = 'pgm' + Math.floor(Math.random() * Date.now()); } - element.setAttribute('__pluginDomId', elemId); + Object.defineProperty(element, '__pluginDomId', { + enumerable: false, + value: elemId + }); } return elemId; } @@ -724,6 +734,11 @@ function createEvent(eventName, properties) { return evt; } +function hasTransparentClass(div) { + return (div.classList && div.classList.contains('_gmaps_cdv_') || + div.className && div.className.indexOf('_gmaps_cdv_') > -1); +} + function attachTransparentClass(div) { if (div.classList && !div.classList.contains('_gmaps_cdv_')) { @@ -734,36 +749,32 @@ function attachTransparentClass(div) { if (div.shadowRoot) { var styleAttr = div.getAttribute('style') || ''; - if (styleAttr && styleAttr.indexOf('--ion-background-color') === -1) { - styleAttr = styleAttr + ' --ion-background-color: transparent;'; + if (styleAttr && styleAttr.indexOf('--pgm-background-color') === -1) { + styleAttr = styleAttr + ' --ion-background-color: var(--pgm-background-color);'; } div.setAttribute('style', styleAttr); } } -// function dettachTransparentClass(root) { -// -// if (div.classList && div.classList.contains('_gmaps_cdv_')) { -// div.classList.remove('_gmaps_cdv_'); -// } else if (div.className && div.className.indexOf('_gmaps_cdv_') === -1) { -// div.className = div.className.replace('_gmaps_cdv_', ''); -// } -// -// var visibilityCSS = getStyle(node, 'visibility'); -// if (getStyle(div, 'background-color') === 'transparent !important') { -// div.style.backgroundColor = undefined; -// } -// if (getStyle(div, 'background') === 'transparent !important') { -// div.style.backgroundColor = undefined; -// } -// if (getStyle(div, 'background-image') === 'transparent !important') { -// div.style.backgroundImage = undefined; -// } -// if (div.shadowRoot) { -// var hiddenChildren = div.querySelectorAll('*'); -// hiddenChildren = Array.prototype.splice(hiddenChildren, 0); -// hiddenChildren.forEach(dettachTransparentClass); -// } -// } +function detachTransparentClass(div) { + + if (div.classList && div.classList.contains('_gmaps_cdv_')) { + div.classList.remove('_gmaps_cdv_'); + } else if (div.className && div.className.indexOf('_gmaps_cdv_') > -1) { + div.className = div.className.replace('_gmaps_cdv_', ''); + } + + if (div.shadowRoot) { + var styleAttr = div.getAttribute('style') || ''; + if (styleAttr && styleAttr.indexOf('--pgm-background-color') > -1) { + styleAttr = styleAttr.replace('--ion-background-color: var(--pgm-background-color);', ''); + } + div.setAttribute('style', styleAttr); + + var hiddenChildren = div.querySelectorAll('*'); + hiddenChildren = Array.prototype.splice(hiddenChildren, 0); + hiddenChildren.forEach(detachTransparentClass); + } +} module.exports = { _clearInternalCache: _clearInternalCache, @@ -787,8 +798,9 @@ module.exports = { getPluginDomId: getPluginDomId, hashCode: hashCode, createEvent: createEvent, - //dettachTransparentClass: dettachTransparentClass, - attachTransparentClass: attachTransparentClass + detachTransparentClass: detachTransparentClass, + attachTransparentClass: attachTransparentClass, + hasTransparentClass: hasTransparentClass }; if (cordova && cordova.platformId === 'browser') { diff --git a/www/DirectionsRenderer.js b/www/DirectionsRenderer.js new file mode 100644 index 000000000..129f01e8b --- /dev/null +++ b/www/DirectionsRenderer.js @@ -0,0 +1,502 @@ + + +var utils = require('cordova/utils'), + event = require('./event'), + BaseClass = require('./BaseClass'), + BaseArrayClass = require('./BaseArrayClass'), + VisibleRegion = require('./VisibleRegion'), + spherical = require('./spherical'), + encoding = require('./encoding'); + +var _decodePolyline = function(property) { + if (typeof property === 'object' && property.points) { + return encoding.decodePath(property.points); + } else if (typeof property === 'string') { + return encoding.decodePath(property); + } else if (utils.isArray(property)) { + return property; + } +}; + +/***************************************************************************** + * DirectionsRenderer Class + *****************************************************************************/ +var DirectionsRenderer = function(map, exec, options) { + BaseClass.apply(this); + + var self = this; + //self.set("visible", DirectionsRendererOptions.visible === undefined ? true : DirectionsRendererOptions.visible); + //self.set("zIndex", DirectionsRendererOptions.zIndex || 0); + + Object.defineProperty(self, '_isReady', { + value: true, + writable: false + }); + Object.defineProperty(self, 'type', { + value: 'DirectionsRenderer', + writable: false + }); + Object.defineProperty(self, 'map', { + value: map, + writable: false + }); + Object.defineProperty(self, 'exec', { + value: exec, + writable: false + }); + Object.defineProperty(self, 'waypoints', { + value: new BaseArrayClass(), + writable: false + }); + Object.defineProperty(self, 'markers', { + value: new BaseArrayClass(), + writable: false + }); + Object.defineProperty(self, 'waypoints', { + value: new BaseArrayClass(), + writable: false + }); + + Object.defineProperty(self, 'pathList', { + value: new BaseArrayClass(), + writable: false + }); + Object.defineProperty(self, 'pathCollection', { + value: new BaseArrayClass(), + writable: false + }); + options = options || {}; + self.set('options', options); + self.set('markerOptions', options.markerOptions); + + self.set('directions', options.directions || { + 'routes': [] + }); + self.set('draggable', options.draggable || false); + self.set('requestingFlag', false); + self.set('routeIndex', options.routeIndex || 0, false); + + self.on('directions_changed', self._redraw_panel.bind(self)); + self.on('panel_changed', self._panel_changed.bind(self)); + self.set('panel', options.panel); + + self.on('routeIndex_changed', self._redrawRoute.bind(self)); + self.on('overview_path_changed', self._redrawPolyline.bind(self)); + self.waypoints.on('insert_at', self._waypoint_created.bind(self)); + self.waypoints.on('remove_at', self._waypoint_removed.bind(self)); + self.pathList.on('insert_at', self._pathList_created.bind(self)); + self.pathList.on('remove_at', self._pathList_removed.bind(self)); + self.pathList.on('set_at', self._pathList_updated.bind(self)); + + self.trigger('routeIndex_changed'); +}; + +utils.extend(DirectionsRenderer, BaseClass); + +DirectionsRenderer.prototype.getId = function () { + return this.__pgmId; +}; + +DirectionsRenderer.prototype.getRouteIndex = function () { + return this.get('routeIndex') || 0; +}; +DirectionsRenderer.prototype.getDirections = function () { + return this.get('directions') || {}; +}; + +DirectionsRenderer.prototype._panel_changed = function(oldDivId, newDivId) { + var self = this; + if (oldDivId) { + var oldDiv = document.getElementById(oldDivId); + if (oldDiv) { + oldDivId.innerHTML = ''; + } + } + self._redraw_panel(); +}; + +DirectionsRenderer.prototype._redraw_panel = function() { + var self = this; + + var newDivId = self.get('panel'); + if (newDivId) { + var directions = self.get('directions'); + + var newDiv = document.getElementById(newDivId); + if (newDiv && directions.routes.length) { + + newDiv.style.position = 'relative'; + + var routeIndex = self.get('routeIndex'); + var frame = document.createElement('div'); + frame.style.position = 'absolute'; + frame.style.left = '0px'; + frame.style.top = '0px'; + frame.style.bottom = '0px'; + frame.style.right = '0px'; + frame.style.width = '100%'; + frame.style.height = '100%'; + var shadowRoot = frame.attachShadow({mode: 'open'}); + + var style = document.createElement('style'); + shadowRoot.appendChild(style); + style.innerHTML = ` + div.summary1 { + color: green; + font-size: 1.5em; + padding-top: 5%; + padding-bottom: 2%; + } + div.summary2 { + color: green; + font-size: 1em; + } + ul.steps { + list-style-type: none; + padding: 0%; + } + li.step { + padding: 0.5em; + border-bottom: 1px solid #ccc; + } + li:last-child { + border-bottom: none; + } + `; + + var container = document.createElement('div'); + container.style.position = 'relative'; + container.style.overflowY = 'scroll'; + container.style.width = '90%'; + container.style.height = '100%'; + container.style.padding = '0% 5%'; + shadowRoot.appendChild(container); + + var summary = document.createElement('div'); + summary.style.width = '100%'; + + + var summary1 = document.createElement('div'); + summary1.classList.add('summary1'); + summary1.innerHTML = [ + directions.routes[routeIndex].legs[0].duration.text, + directions.routes[routeIndex].legs[0].distance.text + ].join(' / '); + summary.appendChild(summary1); + + var eta = new Date(Date.now() + directions.routes[routeIndex].legs[0].duration.value); + var summary2 = document.createElement('div'); + summary2.classList.add('summary2'); + summary2.innerHTML = eta; + summary.appendChild(summary2); + + var copyrights = document.createElement('div'); + copyrights.style.fontSize = '0.8em'; + copyrights.style.color = 'white'; + copyrights.style.padding = '0.5em 0em'; + copyrights.style.textAlign = 'right'; + copyrights.innerHTML = directions.routes[routeIndex].copyrights; + summary.appendChild(copyrights); + + container.appendChild(summary); + + var ul = document.createElement('ul'); + ul.classList.add('steps'); + + directions.routes[routeIndex].legs[0].steps.forEach(function(step) { + var stepLi = document.createElement('li'); + stepLi.classList.add('step'); + + var stepSummary = document.createElement('div'); + stepSummary.style.width = '100%'; + stepSummary.innerHTML = [ + step.duration.text, + step.distance.text, + ].join(" / "); + stepLi.appendChild(stepSummary); + + var stepInstruction = document.createElement('div'); + stepInstruction.style.width = '100%'; + stepInstruction.innerHTML = step.instructions; + stepLi.appendChild(stepInstruction); + + ul.appendChild(stepLi); + }); + container.appendChild(ul); + newDiv.innerHTML = ''; + newDiv.appendChild(frame); + } + } +}; + +DirectionsRenderer.prototype._pathList_created = function(index) { + var self = this; + var path = self.pathList.getAt(index); + + var polylineOpts = Object.create(self.get('polylineOptions') || {}); + delete polylineOpts.points; + + polylineOpts.color = polylineOpts.color || '#0000FF'; + polylineOpts.width = ('width' in polylineOpts) ? polylineOpts.width : 5; + polylineOpts.points = path; + + var polyline = self.map.addPolyline(polylineOpts); + self.pathCollection.push(polyline); +}; + +DirectionsRenderer.prototype._pathList_updated = function(index) { + var self = this; + var polyline = self.pathCollection.getAt(index); + var path = self.pathList.getAt(index); + polyline.setPoints(path); + + // for test + //polyline.setStrokeColor(`rgb(${index * 63}, ${index * 63}, ${index * 63})`); + +}; + +DirectionsRenderer.prototype._pathList_removed = function(index) { + var self = this; + var polyline = self.pathCollection.removeAt(index); + polyline.remove(); +}; + +DirectionsRenderer.prototype._waypoint_created = function(index) { + var self = this; + var position = self.waypoints.getAt(index); + var markerOpts = Object.create(self.get('markerOptions') || {}); + markerOpts.position = position; + markerOpts.idx = index; + markerOpts.icon = markerOpts.icon || {}; + // if (typeof markerOpts.icon === 'object' && !markerOpts.icon.url) { + // markerOpts.icon.url = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAQAAADZc7J/AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAHdElNRQfkBRMFExpRC/42AAACY0lEQVRIx6WVv0tbYRSGny80BpWYQTBLO7Q4pAXlWqzU3UDjov+B7VSSdknc/AMcdG2HdjKboy5NwWC3WlooRdFmkBbiEBXkkpgovQm+HfwRr97EK55vuXz3vs85F855jxF3i8Ad9dxr/cpRhWOgkx46TKuvjNcv1PSdPBscUAeC9DLAGCN0Gx+Auj7xkX2e8px+IkCZbb7xkz5ekyB4FSLXKSkpS/PakRAVlVRSRULsaF6WkirJrXBV8EdvCTHLY7OnHCsUqAA9xIiTIGp+a4Z/vOOR8aygpIReyVZDC7IUcCUKyNKCGrL1UglXFRcPjpKalK2q0goJjxNSRlXZmlRSjq4BlmRpSw2lZTzlCBll1NCmLC1fBVQ1rnmJbIvszSqyEnMaV1UuwKqGtaNdWW3lCFnaVVHDWj0DnLVyniHumxzrN7buOjkemCHyl2fB0QajQJ6TGwEn5IFRNnB0AahwQD+HKvganwKH6ueASrOCI+pEqFH2BShTI0Kd4ybA+BJ6RwCgkyBluon4kkTopkyQriagh162CZuYL0CMsNmml3AT0GEGWAPiPgwqQBxYY/DcZJqNVLxVI31xN9IIfSwSNWlCbfOHSBM1i/Tx7PzqHL0sS5tqKNN2mKZbDZNwlNKEbFWVaTnO06rJ1oRSXuN8aihTstVQ1tNQsmrI1tQVQ3FZ2l+9oYNZnpg9fWaFAmUgQow4L4iaLc3g8J6H3pZ2WkVKluZUvGaqRc3JUqq9qQLUleMD+wwxesnWv/Krha23WCw/yLN+abEMMsYIXX4WSzMcHXIEdBG+7Wq7Tdx5O/8H1JclvcjQgdMAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjAtMDUtMTlUMDU6MTk6MjArMDA6MDAKWm6xAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIwLTA1LTE5VDA1OjE5OjIwKzAwOjAwewfWDQAAAABJRU5ErkJggg=='; + // markerOpts.icon.size = { + // 'width': 24, + // 'height': 24 + // }; + // markerOpts.icon.anchor = [12, 12]; + // } + + var marker = self.map.addMarker(markerOpts); + self.bindTo('draggable', marker); + marker.on('marker_drag_end', self._onWaypointMoved.bind(self, marker)); + self.markers.push(marker); +}; + +DirectionsRenderer.prototype._waypoint_removed = function(index) { + var self = this; + var marker = self.markers.removeAt(index); + marker.remove(); +}; + +DirectionsRenderer.prototype._onWaypointMoved = function(marker) { + var self = this; + var position = marker.getPosition(); + var index = marker.get('idx'); + self.waypoints.setAt(index, position); + + var requestingFlag = self.get('requestingFlag'); + if (requestingFlag) return; + self.set('requestingFlag', true); + + var n = self.waypoints.getLength(); + var routeIndex = self.get('routeIndex'); + + var directions = self.get('directions'); + var points = []; + if (index == 0) { + points.push(self.waypoints.getAt(0)); + points.push(self.waypoints.getAt(1)); + } else if (index == n - 1) { + points.push(self.waypoints.getAt(n - 2)); + points.push(self.waypoints.getAt(n - 1)); + } else { + points.push(self.waypoints.getAt(index - 1)); + points.push(self.waypoints.getAt(index)); + points.push(self.waypoints.getAt(index + 1)); + } + + directions = directions || { + request: { + travelMod: 'DRIVING' + } + }; + + plugin.google.maps.DirectionsService.route({ + 'origin': points.shift(), + 'destination': points.pop(), + 'travelMode': directions.request.travelMode || 'DRIVING', + 'waypoints': points + }, function(result) { + + // Redraw the polyline + self.set('directions', result); + var route = result.routes[routeIndex]; + var leg = route.legs[0]; + + // var currentPath = []; + // leg.steps.forEach(function(step) { + // currentPath = currentPath.concat(_decodePolyline(step.polyline)); + // }); + var currentPath = _decodePolyline(route.overview_polyline); + + if (index == 0) { + self.pathList.setAt(0, currentPath); + } else if (index == n - 1) { + self.pathList.setAt(index - 1, currentPath); + } else { + + // Find the middle point (= marker point) + var closestDist = Math.pow(2, 32) - 1; + var closestIdx = 0; + var left = 0; + var right = currentPath.length; + while (left < right) { + var idx = Math.floor((left + right) / 2); + var howFar = spherical.computeDistanceBetween(position, currentPath[idx]); + if (howFar < closestDist) { + closestDist = howFar; + closestIdx = idx; + var howFarLeft = spherical.computeDistanceBetween(position, currentPath[Math.floor((left + idx) / 2)]); + var howFarRight = spherical.computeDistanceBetween(position, currentPath[Math.floor((right + idx) / 2)]); + if (howFarLeft < howFarRight && howFarLeft < closestDist) { + right = idx; + } else if (howFarLeft > howFarRight && howFarRight < closestDist) { + left = idx; + } else { + + // The middle point should be in the range of left and right. + idx = left; + while (idx < right) { + var howFar = spherical.computeDistanceBetween(position, currentPath[idx]); + if (howFar < closestDist) { + closestDist = howFar; + closestIdx = idx; + } + idx++; + } + + break; + } + } else { + break; + } + } + + var twoPath = [ + currentPath.slice(0, closestIdx), + currentPath.slice(closestIdx) + ]; + twoPath[0].push(position); + twoPath[1].unshift(position); + self.pathList.setAt(index - 1, twoPath[0]); + self.pathList.setAt(index, twoPath[1]); + } + + + + // prevent the OVER_QUERY_LIMIT + setTimeout(function() { + self.set('requestingFlag', false); + }, 1000); + }); +}; + +DirectionsRenderer.prototype.setOptions = function (options) { + var self = this; + options = options || {}; + self.set('options', options); + self.set('routeIndex', options.routeIndex || 0); + self.set('directions', options.directions || { + 'routes': [], + 'request': 'DRIVING' + }); + +}; +DirectionsRenderer.prototype.setRouteIndex = function(index) { + var self = this; + self.set('routeIndex', index); +}; + +DirectionsRenderer.prototype.remove = function(callback) { + var self = this; + if (self._isRemoved) { + if (typeof callback === 'function') { + return; + } else { + return Promise.resolve(); + } + } + Object.defineProperty(self, '_isRemoved', { + value: true, + writable: false + }); + + + var resolver = function(resolve) { + self.polyline.remove().then(resolve); + }; + + if (typeof callback === 'function') { + resolver.call(self, callback, null); + return; + } else { + return Promise.resolve(); + } +}; +DirectionsRenderer.prototype._redrawRoute = function(oldIdx, newIdx) { + var self = this; + var options = self.get('options'); + var n = self.get('directions').routes.length; + var routeIndex = self.get('routeIndex'); + + if (newIdx < 0 || newIdx > n - 1) { + return; + } + var stepOverlays = new BaseArrayClass(); + var route = self.get('directions').routes[routeIndex]; + + var waypointsRef = self.waypoints; + waypointsRef.empty(); + + var currentPath = []; + var idx = 0; + var lastPosition = route.legs[0].steps[0].start_location; + currentPath = currentPath.concat(_decodePolyline(route.legs[0].steps[0].polyline)); + waypointsRef.push(lastPosition); + var n = route.legs[0].steps.length; + route.legs[0].steps.forEach(function(step, i) { + if (i == 0) return; + + var pos = step.end_location; + currentPath = currentPath.concat(_decodePolyline(step.polyline)); + if (spherical.computeDistanceBetween(lastPosition, pos) < 100 && idx < n) { + return; + } + lastPosition = pos; + + self.pathList.push(currentPath.slice(0)); + currentPath = []; + waypointsRef.push(pos); + }); + if (currentPath.length > 0) { + self.pathList.push(currentPath.slice(0)); + } + + if (!options.preserveViewport) { + setTimeout(function() { + self.map.animateCamera({ + 'target': route.bounds, + 'duration': 1000 + }); + }, 100); + } +}; + +DirectionsRenderer.prototype._redrawPolyline = function(marker) { + var self = this; + var polyline = self.polyline; + var overview_path = self.get('overview_path'); + polyline.setPoints(overview_path); +}; +module.exports = DirectionsRenderer; diff --git a/www/DirectionsService.js b/www/DirectionsService.js new file mode 100644 index 000000000..c6a0f32cd --- /dev/null +++ b/www/DirectionsService.js @@ -0,0 +1,168 @@ + + +/***************************************************************************** + * DirectionsService class + *****************************************************************************/ +function _cnv_DirectionsRoute(route) { + if (route.legs) { + route.legs = route.legs.map(function(leg) { + return _cnv_DirectionsLeg(leg); + }); + } + return route; +} + +function _cnv_DirectionsLeg(leg) { + if (leg.arrival_time) { + leg.arrival_time.value = new Date(leg.arrival_time.value); + } + if (leg.departure_time) { + leg.departure_time.value = new Date(leg.departure_time.value); + } + if (leg.steps) { + leg.steps = leg.steps.map(function(step) { + return _cnv_DirectionsStep(step); + }); + } + return leg; +} + +function _cnv_DirectionsStep(step) { + if (step.steps) { + step.steps = step.steps.map(function(s) { + return _cnv_DirectionsStep(s); + }); + } + if (!step.instructions && step.html_instructions) { + step.instructions = step.html_instructions; + delete step.html_instructions; + } + if (step.transit) { + if (step.transit.arrival_time) { + step.transit.arrival_time.value = new Date(step.transit.arrival_time.value); + } + if (step.transit.departure_time) { + step.transit.departure_time.value = new Date(step.transit.departure_time.value); + } + } + + return step; +} + +function _cnv_DirectionsRequestLocation(position) { + if (typeof position === 'string') { + position = { + 'type': 'string', + 'value': position + }; + } else if (position.lat && position.lng) { + position = { + 'type': 'location', + 'value': { + 'lat': position.lat, + 'lng': position.lng + } + }; + } else { + var place = {}; + if (position.location) { + place.location = { + 'lat': position.location.lat, + 'lng': position.location.lng + }; + } + if (position.placeId) { + place.placeId = position.placeId; + } + if (position.query) { + place.query = position.query; + } + position = { + 'type': 'place', + 'value': place + }; + } + return position; +} + +var DirectionsService = function(exec) { + + return { + route: function(request, callback, errorCallback) { + var self = this; + + if (!request) { + throw new Error('route needs request parameter'); + } + if (!request.destination) { + throw new Error('route() needs request.destination parameter'); + } + if (!request.origin) { + throw new Error('route() needs request.origin parameter'); + } + if (!request.travelMode) { + throw new Error('route() needs request.travelMode parameter'); + } + + request.origin = _cnv_DirectionsRequestLocation(request.origin); + request.destination = _cnv_DirectionsRequestLocation(request.destination); + + if (request.drivingOptions && typeof request.drivingOptions.departureTime === 'date') { + request.drivingOptions.departureTime = request.drivingOptions.departureTime.getTime(); + } + if (request.transitOptions && typeof request.transitOptions.departureTime === 'date') { + request.transitOptions.departureTime = request.transitOptions.departureTime.getTime(); + } + if (request.transitOptions && typeof request.transitOptions.arrivalTime === 'date') { + request.transitOptions.arrivalTime = request.transitOptions.arrivalTime.getTime(); + } + if (request.waypoints) { + request.waypoints = request.waypoints.map(function(waypoint) { + if (waypoint.lat && waypoint.lng) { + waypoint = { + stopover: false, + location: { + 'type': 'location', + 'value': waypoint + } + }; + } else if (waypoint.location) { + waypoint.location = _cnv_DirectionsRequestLocation(waypoint.location); + } + return waypoint; + }); + } + + var resolver = function (resolve, reject) { + exec.call({ + _isReady: true + }, function (result) { + result = result || {}; + result.routes = result.routes.map(function(route) { + return _cnv_DirectionsRoute(route); + }); + result.request = request; + resolve(result); + }, reject, 'PluginDirectionsService', 'route', [request]); + }; + + + var errorHandler = function(result) { + if (typeof errorCallback === 'function') { + errorCallback.call(self, result); + } else { + (self.errorHandler || _errorHandler).call(self, result); + } + }; + if (typeof callback === 'function') { + resolver(callback, errorHandler); + return self; + } else { + return new Promise(resolver); + } + } + }; +}; + + +module.exports = DirectionsService; diff --git a/www/ElevationService.js b/www/ElevationService.js new file mode 100644 index 000000000..57ebb1660 --- /dev/null +++ b/www/ElevationService.js @@ -0,0 +1,123 @@ + +/***************************************************************************** + * ElevationService class + *****************************************************************************/ +var LatLng = require('./LatLng'); + +var ElevationService = function(exec) { + function _errorHandler(err) { + console.error(err); + } + return { + getElevationAlongPath: function(request, callback, errorCallback) { + var self = this; + + if (!request) { + return errorHandler('getElevationAlongPath needs request parameter'); + } + if (!request.samples) { + return errorHandler('getElevationAlongPath needs request.samples parameter'); + } + if (typeof request.samples !== 'number' || + request.samples < 2 || request.samples > 512) { + return errorHandler('getElevationAlongPath needs request.samples must be a value between 2 and 512 inclusive'); + } + + var path = request.path; + if (request.path && request.path.type === 'BaseArrayClass') { + path = request.path.getArray(); + } + for (var i = 0; i < path.length; i++) { + path[i] = { + lat: path[i].lat, + lng: path[i].lng + }; + } + if (path instanceof Array && !Array.isArray(request.path)) { + return errorHandler('getElevationAlongPath needs request.path must be an ILatLng array'); + } + + + var params = { + 'path': path, + samples: request.samples + }; + + var resolver = function (resolve, reject) { + exec.call({ + _isReady: true + }, function (_results) { + resolve(_results.results); + }, reject, 'PluginElevationService', 'getElevationAlongPath', [params]); + }; + + + var errorHandler = function(result) { + if (typeof errorCallback === 'function') { + errorCallback.call(self, result); + } else { + (self.errorHandler || _errorHandler).call(self, result); + } + }; + if (typeof callback === 'function') { + resolver(callback, errorHandler); + return self; + } else { + return new Promise(resolver); + } + }, + + getElevationForLocations: function(request, callback, errorCallback) { + var self = this; + + if (!request) { + return errorHandler('getElevationForLocations needs request parameter'); + } + + var locations = request.locations; + if (request.locations && request.locations.type === 'BaseArrayClass') { + locations = request.locations.getArray(); + } + for (var i = 0; i < locations.length; i++) { + locations[i] = { + lat: locations[i].lat, + lng: locations[i].lng + }; + } + if (locations instanceof Array && !Array.isArray(request.locations)) { + return errorHandler('getElevationAlongPath needs request.locations must be an ILatLng array'); + } + + + var params = { + 'locations': locations + }; + + var resolver = function (resolve, reject) { + exec.call({ + _isReady: true + }, function (_results) { + resolve(_results.results); + }, reject, 'PluginElevationService', 'getElevationForLocations', [params]); + }; + + + var errorHandler = function(result) { + if (typeof errorCallback === 'function') { + errorCallback.call(self, result); + } else { + (self.errorHandler || _errorHandler).call(self, result); + } + }; + if (typeof callback === 'function') { + resolver(callback, errorHandler); + return self; + } else { + return new Promise(resolver); + } + } + }; +}; + + +module.exports = ElevationService; diff --git a/www/Map.js b/www/Map.js index 80373fc0c..926db6d42 100644 --- a/www/Map.js +++ b/www/Map.js @@ -7,6 +7,7 @@ var utils = require('cordova/utils'), BaseClass = require('./BaseClass'), BaseArrayClass = require('./BaseArrayClass'), LatLng = require('./LatLng'), + LatLngBounds = require('./LatLngBounds'), MapTypeId = require('./MapTypeId'), event = require('./event'), VisibleRegion = require('./VisibleRegion'), @@ -18,12 +19,23 @@ var utils = require('cordova/utils'), GroundOverlay = require('./GroundOverlay'), KmlOverlay = require('./KmlOverlay'), KmlLoader = require('./KmlLoader'), + DirectionsRenderer = require('./DirectionsRenderer'), + spherical = require('./spherical'), + encoding = require('./encoding'), MarkerCluster = require('./MarkerCluster'); /** * Google Maps model. */ var exec; +var _protect = function(listener) { + Object.defineProperty(listener, '_protect', { + enumerable: false, + value: true, + writable: false + }); + return listener; +} var Map = function(__pgmId, _exec) { var self = this; exec = _exec; @@ -48,6 +60,21 @@ var Map = function(__pgmId, _exec) { infoWindowLayer.style.overflow = 'visible'; infoWindowLayer.style['z-index'] = 1; + var style = document.createElement('style'); + style.setAttribute('type', 'text/css'); + style.innerHTML = [ + '.pgm-html-info-frame > * {', + 'margin: 0;', + 'padding: 0;', + 'border: 0;', + 'font-size: 100%;', + 'font: inherit;', + 'vertical-align: baseline;', + 'color: black;', + '}' + ].join("\n"); + infoWindowLayer.appendChild(style); + Object.defineProperty(self, '_layers', { value: { info: infoWindowLayer @@ -56,17 +83,17 @@ var Map = function(__pgmId, _exec) { writable: false }); - self.on(event.MAP_CLICK, function() { + self.on(event.MAP_CLICK, _protect(function() { self.set('active_marker', undefined); - }); + })); - self.on('active_marker_changed', function(prevMarker, newMarker) { + self.on('active_marker_changed', _protect(function(prevMarker, newMarker) { var newMarkerId = newMarker ? newMarker.getId() : null; if (prevMarker) { prevMarker.hideInfoWindow.call(prevMarker); } self.exec.call(self, null, null, self.__pgmId, 'setActiveMarkerId', [newMarkerId]); - }); + })); }; utils.extend(Map, Overlay); @@ -77,11 +104,13 @@ utils.extend(Map, Overlay); Map.prototype.refreshLayout = function() { // Webkit redraw mandatory // http://stackoverflow.com/a/3485654/697856 - document.body.style.display = 'inline-block'; - document.body.offsetHeight; - document.body.style.display = ''; + // document.body.style.display = 'inline-block'; + // document.body.offsetHeight; + // document.body.style.display = ''; + document.body.style.transform = 'rotateZ(0deg)'; - this.exec.call(this, null, null, this.__pgmId, 'resizeMap', []); + var self = this; + self.exec.call(self, null, null, self.__pgmId, 'resizeMap', []); }; Map.prototype.getMap = function(meta, div, options) { @@ -97,13 +126,54 @@ Map.prototype.getMap = function(meta, div, options) { this.set('myLocationButton', options.controls.myLocationButton === true); } - if (options.preferences && options.preferences.gestureBounds) { - if (utils.isArray(options.preferences.gestureBounds) || - options.preferences.gestureBounds.type === 'LatLngBounds') { - options.preferences.gestureBounds = common.convertToPositionArray(options.preferences.gestureBounds); + if (options.preferences && options.preferences.restriction) { + + var bounds = new LatLngBounds(); + if (utils.isArray(options.preferences.restriction)) { + options.preferences.restriction.forEach(function(ele) { + if (ele.lat && ele.lng) { + bounds.extend(ele); + } + }); + } else if (options.preferences.restriction.type === 'LatLngBounds' || + options.preferences.restriction.northeast && options.preferences.restriction.southwest) { + bounds.extend(options.preferences.restriction.southwest); + bounds.extend(options.preferences.restriction.northeast); + } + + if (!bounds.southwest || !bounds.northeast) { + console.warn('(getMap) options.preferences.restriction is invalid.'); + delete options.preferences.restriction; + } else { + var minZoom = !div ? 0 : spherical.computeBoundsZoom(bounds, div.offsetWidth, div.offsetHeight, 256); + var maxZoom = 23; + var prefMinZoom = 0; + var prefMaxZoom = 23; + if (options.preferences.zoom) { + if (options.preferences.zoom.minZoom) { + minZoom = options.preferences.zoom.minZoom; + prefMinZoom = minZoom; + } + if (options.preferences.zoom.maxZoom) { + maxZoom = options.preferences.zoom.maxZoom; + maxZoom = minZoom; + } + } + options.preferences.restriction = { + 'south': bounds.southwest.lat, + 'west': bounds.southwest.lng, + 'north': bounds.northeast.lat, + 'east': bounds.northeast.lng, + 'prefMinZoom': prefMinZoom, + 'prefMaxZoom': prefMaxZoom, + 'minZoom': minZoom, + 'maxZoom': maxZoom + }; + self.set('restriction', options.preferences.restriction); } } + if (!common.isDom(div)) { self.set('visible', false); options = div; @@ -184,9 +254,6 @@ Map.prototype.getMap = function(meta, div, options) { _isReady: true }, function() { - //------------------------------------------------------------------------ - // Clear background colors of map div parents after the map is created - //------------------------------------------------------------------------ var div = self.get('div'); if (common.isDom(div)) { @@ -208,14 +275,6 @@ Map.prototype.getMap = function(meta, div, options) { div.insertBefore(self._layers.info, div.firstChild); - while (div.parentNode) { - div.style.backgroundColor = 'rgba(0,0,0,0) !important'; - - // Add _gmaps_cdv_ class - common.attachTransparentClass(div); - - div = div.parentNode; - } } cordova.fireDocumentEvent('plugin_touch', { force: true @@ -250,58 +309,84 @@ Map.prototype.getMap = function(meta, div, options) { Map.prototype.setOptions = function(options) { options = options || {}; + var self = this; + var div = self.get('div'); if (options.controls) { - var myLocation = this.get('myLocation'); - if ('myLocation' in options.controls) { - myLocation = options.controls.myLocation === true; - } - var myLocationButton = this.get('myLocationButton'); - if ('myLocationButton' in options.controls) { - myLocationButton = options.controls.myLocationButton === true; - } - this.set('myLocation', myLocation); - this.set('myLocationButton', myLocationButton); - if (myLocation === true || myLocation === false) { - options.controls.myLocation = myLocation; - } - if (myLocationButton === true || myLocationButton === false) { - options.controls.myLocationButton = myLocationButton; - } - } - if (options.camera) { - if (options.camera.latLng) { - options.camera.target = options.camera.latLng; - delete options.camera.latLng; - } - this.set('camera', options.camera); - if (options.camera.target) { - this.set('camera_target', options.camera.target); - } - if (options.camera.bearing) { - this.set('camera_bearing', options.camera.bearing); - } - if (options.camera.zoom) { - this.set('camera_zoom', options.camera.zoom); - } - if (options.camera.tilt) { - this.set('camera_tilt', options.camera.tilt); - } + self.set('myLocation', options.controls.myLocation === true); + self.set('myLocationButton', options.controls.myLocationButton === true); } - if (options.preferences && options.preferences.gestureBounds) { - if (utils.isArray(options.preferences.gestureBounds) || - options.preferences.gestureBounds.type === 'LatLngBounds') { - options.preferences.gestureBounds = common.convertToPositionArray(options.preferences.gestureBounds); + // if (options.camera && utils.isArray(options.camera.target)) { + // var cameraBounds = new LatLngBounds(); + // options.camera.target.forEach(function(ele) { + // if (ele.lat && ele.lng) { + // cameraBounds.extend(ele); + // } + // }); + // options.camera.target = cameraBounds.getCenter(); + // options.camera.zoom = spherical.computeBoundsZoom(cameraBounds, div.offsetWidth, div.offsetHeight, 256); + // } + if (options.preferences) { + if (options.preferences.restriction) { + + var bounds = new LatLngBounds(); + if (utils.isArray(options.preferences.restriction)) { + options.preferences.restriction.forEach(function(ele) { + if (ele.lat && ele.lng) { + bounds.extend(ele); + } + }); + } else if (options.preferences.restriction.type === 'LatLngBounds' || + options.preferences.restriction.northeast && options.preferences.restriction.southwest) { + bounds.extend(options.preferences.restriction.southwest); + bounds.extend(options.preferences.restriction.northeast); + } + + if (!bounds.southwest || !bounds.northeast) { + console.warn('(setOptions) options.preferences.restriction is invalid.'); + delete options.preferences.restriction; + } else { + var minZoom = !div ? 0 : spherical.computeBoundsZoom(bounds, div.offsetWidth, div.offsetHeight, 256); + var maxZoom = 23; + var prefMinZoom = 0; + var prefMaxZoom = 23; + if (options.preferences.zoom) { + if (options.preferences.zoom.minZoom) { + minZoom = options.preferences.zoom.minZoom; + prefMinZoom = minZoom; + } + if (options.preferences.zoom.maxZoom) { + maxZoom = options.preferences.zoom.maxZoom; + maxZoom = minZoom; + } + } + options.preferences.restriction = { + 'south': bounds.southwest.lat, + 'west': bounds.southwest.lng, + 'north': bounds.northeast.lat, + 'east': bounds.northeast.lng, + 'prefMinZoom': prefMinZoom, + 'prefMaxZoom': prefMaxZoom, + 'minZoom': minZoom, + 'maxZoom': maxZoom + }; + self.set('restriction', options.preferences.restriction); + } + } else { + self.set('restriction', undefined); } } + if (utils.isArray(options.styles)) { options.styles = JSON.stringify(options.styles); } - this.exec.call(this, null, this.errorHandler, this.__pgmId, 'setOptions', [options]); - return this; + return (new Promise(function(resolve, reject) { + self.exec.call(self, resolve, reject, self.__pgmId, 'setOptions', [options]); + })); }; + Map.prototype.getMyLocation = function(params, success_callback, error_callback) { return window.plugin.google.maps.LocationService.getMyLocation.call(this, params, success_callback, error_callback); }; @@ -491,7 +576,7 @@ Map.prototype.animateCamera = function(cameraPosition, callback) { // cameraPosition.padding = 10; // } - if (utils.isArray(target) || target.type === 'LatLngBounds') { + if (utils.isArray(target) || target.type === 'LatLngBounds' || target.southwest && target.northeast) { target = common.convertToPositionArray(target); if (target.length === 0) { // skip if no point is specified @@ -697,6 +782,7 @@ Map.prototype.remove = function(callback) { writable: false }); self.stopAnimation(); + self.set('div', null); self.trigger('remove'); // var div = self.get('div'); @@ -803,14 +889,22 @@ Map.prototype.setDiv = function(div) { var self = this, args = []; - if (!common.isDom(div)) { - div = self.get('div'); - if (common.isDom(div)) { - div.removeAttribute('__pluginMapId'); + var prevDiv = self.get('div'); + if (common.isDom(prevDiv)) { + prevDiv.__pluginMapId = undefined; + } + + if (common.isDom(div)) { + var elemId = common.getPluginDomId(div); + args.push(elemId); + + if (!div.__pluginMapId) { + Object.defineProperty(div, '__pluginMapId', { + enumerable: false, + writable: true, + value: self.__pgmId + }); } - self.set('div', null); - } else { - div.setAttribute('__pluginMapId', self.__pgmId); // Insert the infoWindow layer if (self._layers.info.parentNode) { @@ -835,39 +929,24 @@ Map.prototype.setDiv = function(div) { // div.offsetHeight; // div.style.display = ''; document.body.style.transform = 'rotateZ(0deg)'; + div.style.overflow = 'hidden'; + div.style.position = 'relative'; - self.set('div', div); - - if (cordova.platform === 'browser') { - return; - } - - - positionCSS = common.getStyle(div, 'position'); - if (!positionCSS || positionCSS === 'static') { - div.style.position = 'relative'; - } - var elemId = common.getPluginDomId(div); - args.push(elemId); - while (div.parentNode) { - div.style.backgroundColor = 'rgba(0,0,0,0)'; - - // Add _gmaps_cdv_ class - common.attachTransparentClass(div); - - div = div.parentNode; - } } - self.exec.call(self, function() { - cordova.fireDocumentEvent('plugin_touch', { - force: true, - action: 'setDiv' + self.set('div', div); + + return (new Promise(function(resolve) { + self.exec.call(self, function() { + cordova.fireDocumentEvent('plugin_touch', { + force: true, + action: 'setDiv' + }); + self.refreshLayout(); + resolve(); + }, self.errorHandler, self.__pgmId, 'setDiv', args, { + sync: true }); - self.refreshLayout(); - }, self.errorHandler, self.__pgmId, 'setDiv', args, { - sync: true - }); - return self; + })); }; /** @@ -1068,6 +1147,26 @@ Map.prototype.addKmlOverlay = function(kmlOverlayOptions, callback) { }; +Map.prototype.addDirectionsRenderer = function(directionsRendererOptions) { + var self = this; + directionsRendererOptions = directionsRendererOptions || {}; + + if (directionsRendererOptions.directions) { + + var renderer = new DirectionsRenderer(self, self.exec, directionsRendererOptions); + renderer.setRouteIndex(0); + + return renderer; + } else { + + if (typeof callback === 'function') { + throw new Error('directionsRendererOptions.directions is required.'); + } else { + return Promise.reject('directionsRendererOptions.directions is required.'); + } + } +}; + //------------- // Ground overlay //------------- @@ -1196,6 +1295,9 @@ Map.prototype.addTileOverlay = function(tilelayerOptions, callback) { Map.prototype.addPolygon = function(polygonOptions, callback) { var self = this; polygonOptions.points = polygonOptions.points || []; + if (typeof polygonOptions.points === 'string') { + polygonOptions.points = encoding.decodePath(polygonOptions.points); + } var _orgs = polygonOptions.points; polygonOptions.points = common.convertToPositionArray(polygonOptions.points); polygonOptions.holes = polygonOptions.holes || []; @@ -1254,6 +1356,9 @@ Map.prototype.addPolygon = function(polygonOptions, callback) { Map.prototype.addPolyline = function(polylineOptions, callback) { var self = this; polylineOptions.points = polylineOptions.points || []; + if (typeof polylineOptions.points === 'string') { + polylineOptions.points = encoding.decodePath(polylineOptions.points); + } var _orgs = polylineOptions.points; polylineOptions.points = common.convertToPositionArray(polylineOptions.points); polylineOptions.color = common.HTMLColor2RGBA(polylineOptions.color || '#FF000080', 0.75); diff --git a/www/Polyline.js b/www/Polyline.js index 090fd1774..ce56e7c8e 100644 --- a/www/Polyline.js +++ b/www/Polyline.js @@ -1,5 +1,7 @@ + var utils = require('cordova/utils'), common = require('./Common'), + encoding = require('./encoding'), Overlay = require('./Overlay'); /***************************************************************************** @@ -83,6 +85,9 @@ utils.extend(Polyline, Overlay); Polyline.prototype.setPoints = function (points) { var self = this; + if (typeof points === 'string') { + points = encoding.decodePath(points);; + } var mvcArray = self.points; mvcArray.empty(true); diff --git a/www/event.js b/www/event.js index 1b232a2d7..03885d0ce 100644 --- a/www/event.js +++ b/www/event.js @@ -1,3 +1,5 @@ + + module.exports = { MAP_READY: 'map_ready', MAP_CLICK: 'map_click', @@ -31,5 +33,6 @@ module.exports = { PANORAMA_READY: 'panorama_ready', PANORAMA_CAMERA_CHANGE: 'panorama_camera_change', PANORAMA_LOCATION_CHANGE: 'panorama_location_change', - PANORAMA_CLICK: 'panorama_click' + PANORAMA_CLICK: 'panorama_click', + DIRECTIONS_CHANGED: 'directions_changed' }; diff --git a/www/js_CordovaGoogleMaps-for-android_ios.js b/www/js_CordovaGoogleMaps-for-android_ios.js index e203d46ca..6189b11a9 100644 --- a/www/js_CordovaGoogleMaps-for-android_ios.js +++ b/www/js_CordovaGoogleMaps-for-android_ios.js @@ -1,4 +1,5 @@ + if (!window.Promise) { window.Promise = require('./Promise'); } @@ -75,7 +76,7 @@ function CordovaGoogleMaps(execCmd) { // If some elements are removed from the DOM tree, remove their information. if (mutation.removedNodes) { Array.prototype.slice.call(mutation.removedNodes, 0).forEach(function(node) { - if (node.nodeType !== Node.ELEMENT_NODE || !node.hasAttribute('__pluginDomId')) { + if (node.nodeType !== Node.ELEMENT_NODE || !node.__pluginDomId) { return; } node._isRemoved = true; @@ -88,7 +89,7 @@ function CordovaGoogleMaps(execCmd) { if (mutation.target.nodeType !== Node.ELEMENT_NODE) { return; } - if (mutation.target.hasAttribute('__pluginDomId')) { + if (mutation.target.__pluginDomId) { // elemId = mutation.target.getAttribute('__pluginDomId'); var transformCSS = common.getStyle(mutation.target, 'transform') || common.getStyle(mutation.target, '-webkit-transform') || @@ -123,9 +124,9 @@ function CordovaGoogleMaps(execCmd) { self.on('isSuspended_changed', function(oldValue, newValue) { if (newValue) { - cordova_exec(null, null, 'CordovaGoogleMaps', 'pause', []); + cordova_exec(null, function(){}, 'CordovaGoogleMaps', 'pause', []); } else { - cordova_exec(null, null, 'CordovaGoogleMaps', 'resume', []); + cordova_exec(null, function(){}, 'CordovaGoogleMaps', 'resume', []); } }); } @@ -157,7 +158,7 @@ CordovaGoogleMaps.prototype.traceDomTree = function(element, elemId, isMapChild) pointerEvents: common.getStyle(element, 'pointer-events'), // Only true if element is mapDiv - isMap: element.hasAttribute('__pluginMapId'), + isMap: element.__pluginMapId !== undefined, // Calculate dom clickable region size: common.getDivRect(element), @@ -304,7 +305,7 @@ CordovaGoogleMaps.prototype.putHtmlElements = function() { } // Does this dom is really existed? - var elemId = div.getAttribute('__pluginDomId'); + var elemId = div.__pluginDomId; if (!elemId) { // The map div is removed if (mapId in self.MAPS) { @@ -316,8 +317,11 @@ CordovaGoogleMaps.prototype.putHtmlElements = function() { if (!(elemId in self.domPositions)) { // Is the map div removed? - var ele = document.querySelector('[__pluginMapId="' + mapId + '"]'); - if (!ele) { + var eles = Array.from(document.querySelectorAll('*')); + eles = eles.filter(function(e) { + return e.__pluginMapId === mapId; + }); + if (eles.length === 0) { // If no div element, remove the map. if (mapId in self.MAPS) { self.MAPS[mapId].remove(); @@ -417,7 +421,7 @@ CordovaGoogleMaps.prototype.onTransitionEnd = function(evt) { target = document.body; } target = target.getAttribute === 'function' ? target : document.body; - var elemId = target.getAttribute('__pluginDomId'); + var elemId = target.__pluginDomId; self.transformTargets[elemId] = {left: -1, top: -1, right: -1, bottom: -1, finish: false, target: target}; if (!self.transitionEndTimer) { self.transitionEndTimer = setTimeout(self.detectTransitionFinish.bind(self), 100); @@ -503,7 +507,10 @@ CordovaGoogleMaps.prototype.removeDomTree = function(node) { // Remove the information of children // which have the `__pluginDomId` attribute. - var nodeList = node.querySelectorAll('[__pluginDomId]'); + var nodeList = Array.from(node.querySelectorAll('*')); + nodeList = nodeList.filter(function(child) { + return child.__pluginDomId !== undefined; + }); var children = []; for (var i = 0; i < nodeList.length; i++) { children.push(nodeList[i]); @@ -512,17 +519,19 @@ CordovaGoogleMaps.prototype.removeDomTree = function(node) { var isRemoved = node._isRemoved; children.forEach(function(child) { - var elemId = child.getAttribute('__pluginDomId'); + var elemId = child.__pluginDomId; // If the DOM is removed from the DOM tree, // remove the attribute. // (Note that the `_isRemoved` flag is set in MutationObserver.) if (isRemoved) { - child.removeAttribute('__pluginDomId'); + + + child.__pluginDomId = undefined; // If map div, remove the map also. - if (child.hasAttribute('__pluginMapId')) { - var mapId = child.getAttribute('__pluginMapId'); + if (child.__pluginMapId) { + var mapId = child.__pluginMapId; if (mapId in self.MAPS) { self.MAPS[mapId].remove(); } @@ -573,7 +582,13 @@ CordovaGoogleMaps.prototype.followMapDivPositionOnly = function(opts) { // Obtain only minimum information var mapDiv = map.getDiv(); - var divId = mapDiv.getAttribute('__pluginDomId'); + var divId = mapDiv.__pluginDomId; + if (!divId) { + changed = true; + map.remove(); + return; + } + var divId = mapDiv.__pluginDomId; mapRects[divId] = { size: common.getDivRect(mapDiv), zIndex: common.getZIndex(mapDiv) @@ -609,7 +624,7 @@ CordovaGoogleMaps.prototype.followMapDivPositionOnly = function(opts) { // If changed, move the map views. if (changed || opts.force) { - cordova_exec(null, null, 'CordovaGoogleMaps', 'updateMapPositionOnly', [mapRects]); + cordova_exec(null, function(){}, 'CordovaGoogleMaps', 'updateMapPositionOnly', [mapRects]); return changed; } return false; @@ -653,7 +668,7 @@ CordovaGoogleMaps.prototype.getMap = function(div, mapOptions) { mapId, elem, elemId; if (common.isDom(div)) { - mapId = div.getAttribute('__pluginMapId'); + mapId = div.__pluginMapId; // Wow, the app specifies the map div that has already another map, // but the app try to create new map. @@ -661,7 +676,7 @@ CordovaGoogleMaps.prototype.getMap = function(div, mapOptions) { if (mapId && self.MAPS[mapId].getDiv() !== div) { elem = self.MAPS[mapId].getDiv(); while(elem && elem.nodeType === Node.ELEMENT_NODE) { - elemId = elem.getAttribute('__pluginDomId'); + elemId = elem.__pluginDomId; if (elemId && elemId in self.domPositions) { self.domPositions[elemId].containMapIDs = self.domPositions[elemId].containMapIDs || {}; delete self.domPositions[elemId].containMapIDs[mapId]; @@ -694,51 +709,69 @@ CordovaGoogleMaps.prototype.getMap = function(div, mapOptions) { // (Don't execute this native callback from your code) window.plugin.google.maps[mapId] = nativeCallback.bind(map); - map.on('__isAttached_changed', function(oldValue, newValue) { + var __isAttached_changed_callback = function(oldValue, newValue) { if (newValue) { cordova_exec(null, null, map.__pgmId, 'attachToWebView', []); } else { cordova_exec(null, null, map.__pgmId, 'detachFromWebView', []); } + }; + Object.defineProperty(__isAttached_changed_callback, '_protect', { + enumerable: false, + value: true, + writable: false }); + map.on('__isAttached_changed', __isAttached_changed_callback); // If the mapDiv is changed, clean up the information for old map div, // then add new information for new map div. - map.on('div_changed', function(oldDiv, newDiv) { + var onDivChanged = function(oldDiv, newDiv) { var elemId, ele; if (common.isDom(oldDiv)) { - oldDiv.removeAttribute('__pluginMapId'); + oldDiv.__pluginMapId = undefined; ele = oldDiv; - while(ele && ele != document.body.parentNode) { - elemId = ele.getAttribute('__pluginDomId'); - if (elemId) { + while(ele) { + elemId = ele.__pluginDomId; + if (elemId && (elemId in self.domPositions)) { self.domPositions[elemId].containMapIDs = self.domPositions[elemId].containMapIDs || {}; delete self.domPositions[elemId].containMapIDs[mapId]; + + // If there is no map under this element, remove the '_gmaps_cdv' if ((Object.keys(self.domPositions[elemId].containMapIDs)).length < 1) { delete self.domPositions[elemId]; + common.detachTransparentClass(ele); } } - ele.removeAttribute('__pluginDomId'); - if (ele.classList) { - ele.classList.remove('_gmaps_cdv_'); - } else if (ele.className) { - ele.className = ele.className.replace(/_gmaps_cdv_/g, ''); - ele.className = ele.className.replace(/\s+/g, ' '); - } + ele.__pluginDomId = undefined; ele = ele.parentNode; } } + var background = undefined; if (common.isDom(newDiv)) { elemId = common.getPluginDomId(newDiv); elem = newDiv; var isCached; + var bg; while(elem && elem.nodeType === Node.ELEMENT_NODE) { elemId = common.getPluginDomId(elem); if (common.shouldWatchByNative(elem)) { + if (!common.hasTransparentClass(elem)) { + bg = common.getStyle(elem, '--background'); + if (!bg) { + bg = common.getStyle(elem, 'background-color'); + } + bg = (bg || "").trim(); + background = background || bg; + + // Add _gmaps_cdv_ class + common.attachTransparentClass(elem); + } + + if (elem.shadowRoot) { elem.shadowRoot.addEventListener('transitionend', self.onTransitionEnd.bind(self), {capture: true}); elem.shadowRoot.addEventListener('scroll', self.followMaps.bind(self), {capture: true}); @@ -763,11 +796,32 @@ CordovaGoogleMaps.prototype.getMap = function(div, mapOptions) { elemId = common.getPluginDomId(newDiv); self.domPositions[elemId].isMap = true; + + background = background || '#FFFFFFFF'; + console.log(`background = ${background}`); + background = common.HTMLColor2RGBA(background); + plugin.google.maps.environment.setBackgroundColor(background); } + }; + + Object.defineProperty(onDivChanged, '_protect', { + enumerable: false, + value: true, + writable: false }); + map.on('div_changed', onDivChanged); // If the map is removed, clean up the information. - map.one('remove', self._remove.bind(self, mapId)); + var onRemove = function(mapId) { + self._remove.call(self, mapId); + }; + + Object.defineProperty(onRemove, '_protect', { + enumerable: false, + value: true, + writable: false + }); + map.one('remove', onRemove); self.MAP_CNT++; self.isThereAnyChange = true; @@ -801,7 +855,16 @@ CordovaGoogleMaps.prototype.getPanorama = function(div, streetViewOptions) { self.MAP_CNT++; - panorama.one('remove', self._remove.bind(self, mapId)); + var onRemove = function(mapId) { + self._remove.call(self, mapId) + }; + Object.defineProperty(onRemove, '_protect', { + enumerable: false, + value: true, + writable: false + }); + + panorama.one('remove', onRemove); if (div instanceof Promise) { // This hack code for @ionic-native/google-maps @@ -827,10 +890,16 @@ CordovaGoogleMaps.prototype._remove = function(mapId) { if (map) { var div = map.getDiv(); if (!div) { - div = document.querySelector('[__pluginMapId="' + mapId + '"]'); + var allEle = Array.from(document.querySelectorAll('*')); + allEle = allEle.filter(function(ele) { + return ele.__pluginMapId === mapId; + }); + if (allEle.length === 1) { + div = allEle[0]; + } } if (div) { - div.removeAttribute('__pluginMapId'); + div.__pluginMapId = undefined; } var keys = Object.keys(self.domPositions); @@ -839,6 +908,7 @@ CordovaGoogleMaps.prototype._remove = function(mapId) { delete self.domPositions[elemId].containMapIDs[mapId]; if ((Object.keys(self.domPositions[elemId].containMapIDs)).length < 1) { delete self.domPositions[elemId]; +// common.detachTransparentClass(ele); } }); self.MAPS[mapId].destroy(); @@ -847,10 +917,19 @@ CordovaGoogleMaps.prototype._remove = function(mapId) { delete self.MAPS[mapId]; map = undefined; + // If the app have no map, stop the native timer. if ((Object.keys(self.MAPS)).length === 0) { common._clearInternalCache(); self.pause(); + + var ele = document.body.parentElement; + if (ele.classList) { + ele.classList.remove('_gmaps_cdv_'); + } else if (ele.className) { + ele.className = ele.className.replace(/_gmaps_cdv_/g, ''); + ele.className = ele.className.replace(/\s+/g, ' '); + } } }; @@ -884,7 +963,11 @@ function postPanoramaInit(panorama, div, options) { // If the mapDiv is specified, // the native side needs to know the map div position // before creating the map view. - div.setAttribute('__pluginMapId', mapId); + Object.defineProperty(div, '__pluginMapId', { + enumerable: false, + value: mapId, + writable: true + }); var elemId = common.getPluginDomId(div); var elem = div; @@ -957,7 +1040,11 @@ function postMapInit(map, div, options) { // If the mapDiv is specified, // the native side needs to know the map div position // before creating the map view. - div.setAttribute('__pluginMapId', mapId); + Object.defineProperty(div, '__pluginMapId', { + enumerable: false, + value: mapId, + writable: true + }); var elemId = common.getPluginDomId(div); var elem = div; diff --git a/www/js_CordovaGoogleMaps-for-browser.js b/www/js_CordovaGoogleMaps-for-browser.js index bf4d83bc1..7665a1925 100644 --- a/www/js_CordovaGoogleMaps-for-browser.js +++ b/www/js_CordovaGoogleMaps-for-browser.js @@ -29,8 +29,8 @@ function CordovaGoogleMaps(execCmd) { self.MAP_CNT = 0; var removeMapDiv = function(node) { - if (node.hasAttribute('__pluginmapid') && !node.parentNode) { - var mapId = node.getAttribute('__pluginmapid'); + if (node.__pluginMapId !== undefined && !node.parentNode) { + var mapId = node.__pluginMapId; var map = self.MAPS[mapId]; if (map) { map.remove(); @@ -39,9 +39,12 @@ function CordovaGoogleMaps(execCmd) { } else { var childNodes = Array.prototype.slice.call(node.childNodes); childNodes.forEach(function(child) { - if (child.outerHTML && child.outerHTML.indexOf('__pluginmapid') > -1) { + if (child.outerHTML && child.__pluginMapId) { removeMapDiv(child); } + // if (child.outerHTML && child.outerHTML.indexOf('__pluginmapid') > -1) { + // removeMapDiv(child); + // } }); } }; @@ -56,9 +59,12 @@ function CordovaGoogleMaps(execCmd) { if (record.removedNodes.length > 0) { record.removeNodes = Array.prototype.slice.call(record.removedNodes, 0); record.removeNodes.forEach(function(node) { - if (node.outerHTML && node.outerHTML.indexOf('__pluginmapid') > -1) { + if (node.outerHTML && node.__pluginMapId) { removeMapDiv(node); } + // if (node.outerHTML && node.outerHTML.indexOf('__pluginmapid') > -1) { + // removeMapDiv(node); + // } }); } }); @@ -89,7 +95,7 @@ CordovaGoogleMaps.prototype.getMap = function(div, mapOptions) { mapId; if (common.isDom(div)) { - mapId = div.getAttribute('__pluginMapId'); + mapId = div.__pluginMapId; // Wow, the app specifies the map div that has already another map, // but the app try to create new map. @@ -179,10 +185,19 @@ CordovaGoogleMaps.prototype._remove = function(mapId) { var div = map.getDiv(); if (!div) { - div = document.querySelector('[__pluginMapId="' + mapId + '"]'); + var eles = Array.from(document.querySelectorAll('*')); + eles = eles.filter(function(e) { + return e.__pluginMapId === mapId; + }); + if (eles.length === 1) { + div = eles[0]; + } } if (div) { - div.removeAttribute('__pluginMapId'); + Object.defineProperty(div, '__pluginMapId', { + enumerable: false, + value: undefined + }); } self.MAPS[mapId].destroy(); @@ -215,7 +230,10 @@ function postPanoramaInit(panorama, div, options) { // If the mapDiv is specified, // the native side needs to know the map div position // before creating the map view. - div.setAttribute('__pluginMapId', mapId); + Object.defineProperty(div, '__pluginMapId', { + enumerable: false, + value: mapId + }); if (div.offsetWidth < 100 || div.offsetHeight < 100) { console.error('[GoogleMaps] Minimum container dimention is 100x100 in pixels.', div); @@ -229,7 +247,10 @@ function postPanoramaInit(panorama, div, options) { // If the mapDiv is specified, // the native side needs to know the map div position // before creating the map view. - div.setAttribute('__pluginMapId', mapId); + Object.defineProperty(div, '__pluginMapId', { + enumerable: false, + value: mapId + }); panorama.getPanorama.apply(panorama, args); } @@ -254,7 +275,10 @@ function postMapInit(map, div, options) { // If the mapDiv is specified, // the native side needs to know the map div position // before creating the map view. - div.setAttribute('__pluginMapId', mapId); + Object.defineProperty(div, '__pluginMapId', { + enumerable: false, + value: mapId + }); args.push({ __pgmId: mapId, diff --git a/www/plugin-loader-for-android_ios.js b/www/plugin-loader-for-android_ios.js index 593e64cd8..07257759e 100644 --- a/www/plugin-loader-for-android_ios.js +++ b/www/plugin-loader-for-android_ios.js @@ -154,6 +154,8 @@ if (!cordova) { environment: require('./Environment'), Geocoder: require('./Geocoder')(execCmd), LocationService: require('./LocationService')(execCmd), + ElevationService: require('./ElevationService')(execCmd), + DirectionsService: require('./DirectionsService')(execCmd), geometry: { encoding: require('./encoding'), spherical: require('./spherical'), diff --git a/www/plugin-loader-for-browser.js b/www/plugin-loader-for-browser.js index 87d636a1f..3941d8d19 100644 --- a/www/plugin-loader-for-browser.js +++ b/www/plugin-loader-for-browser.js @@ -28,6 +28,8 @@ module.exports = { MapTypeId: require('cordova-plugin-googlemaps.MapTypeId'), environment: require('cordova-plugin-googlemaps.Environment'), Geocoder: require('cordova-plugin-googlemaps.Geocoder')(execCmd), + ElevationService: require('cordova-plugin-googlemaps.ElevationService')(execCmd), + DirectionsService: require('cordova-plugin-googlemaps.DirectionsService')(execCmd), LocationService: require('cordova-plugin-googlemaps.LocationService')(execCmd), geometry: { encoding: require('cordova-plugin-googlemaps.encoding'), diff --git a/www/pluginInit.js b/www/pluginInit.js index de6fea149..8b0a7f59b 100644 --- a/www/pluginInit.js +++ b/www/pluginInit.js @@ -90,10 +90,11 @@ function pluginInit() { var cssAdjuster = document.createElement('style'); cssAdjuster.setAttribute('type', 'text/css'); cssAdjuster.innerText = [ - 'html, body, ._gmaps_cdv_ {', + '._gmaps_cdv_ {', ' background-image: none !important;', ' background: rgba(0,0,0,0) none !important;', ' background-color: rgba(0,0,0,0) !important;', + ' --pgm-background-color: transparent !important;', '}', '._gmaps_cdv_ .nav-decor {', ' background-color: rgba(0,0,0,0) !important;', diff --git a/www/spherical.js b/www/spherical.js index 04a08c93e..b35c16c27 100644 --- a/www/spherical.js +++ b/www/spherical.js @@ -269,6 +269,38 @@ function computeLength(path) { }); return length * EARTH_RADIUS; } + +/** + * Returns the zoom level that fits for given bounds. + * https://stackoverflow.com/a/13274361/697856 + */ +function computeBoundsZoom(bounds, mapWidth, mapHeight, tileSize) { + var ZOOM_MAX = 23; + + var ne = bounds.northeast; + var sw = bounds.southwest; + + var latFraction = (_latRad(ne.lat) - _latRad(sw.lat)) / Math.PI; + + var lngDiff = ne.lng - sw.lng; + var lngFraction = ((lngDiff < 0) ? (lngDiff + 360) : lngDiff) / 360; + + var latZoom = _zoom(mapHeight, tileSize, latFraction); + var lngZoom = _zoom(mapWidth, tileSize, lngFraction); + + return Math.min(latZoom, lngZoom, ZOOM_MAX); +} + +function _latRad(lat) { + var sin = Math.sin(lat * Math.PI / 180); + var radX2 = Math.log((1 + sin) / (1 - sin)) / 2; + return Math.max(Math.min(radX2, Math.PI), -Math.PI) / 2; +} + +function _zoom(mapPx, worldPx, fraction) { + return Math.floor(Math.log(mapPx / worldPx / fraction) / Math.LN2); +} + module.exports = { computeDistanceBetween: computeDistanceBetween, computeOffset: computeOffset, @@ -277,5 +309,6 @@ module.exports = { computeSignedArea: computeSignedArea, computeHeading: computeHeading, interpolate: interpolate, - computeLength: computeLength + computeLength: computeLength, + computeBoundsZoom: computeBoundsZoom };