/**
  * set color
  *
  * @param args
  * @param callbackContext
  * @throws JSONException
  */
 @SuppressWarnings("unused")
 private void setColor(final JSONArray args, final CallbackContext callbackContext)
     throws JSONException {
   String id = args.getString(1);
   int color = PluginUtil.parsePluginColor(args.getJSONArray(2));
   this.setInt("setColor", id, color, callbackContext);
 }
  /**
   * Create polyline
   *
   * @param args
   * @param callbackContext
   * @throws JSONException
   */
  @SuppressWarnings("unused")
  private void createPolyline(final JSONArray args, final CallbackContext callbackContext)
      throws JSONException {
    final PolylineOptions polylineOptions = new PolylineOptions();
    int color;
    LatLngBounds.Builder builder = new LatLngBounds.Builder();

    JSONObject opts = args.getJSONObject(1);
    if (opts.has("points")) {
      JSONArray points = opts.getJSONArray("points");
      List<LatLng> path = PluginUtil.JSONArray2LatLngList(points);
      int i = 0;
      for (i = 0; i < path.size(); i++) {
        polylineOptions.add(path.get(i));
        builder.include(path.get(i));
      }
    }
    if (opts.has("color")) {
      color = PluginUtil.parsePluginColor(opts.getJSONArray("color"));
      polylineOptions.color(color);
    }
    if (opts.has("width")) {
      polylineOptions.width(opts.getInt("width") * this.density);
    }
    if (opts.has("visible")) {
      polylineOptions.visible(opts.getBoolean("visible"));
    }
    if (opts.has("geodesic")) {
      polylineOptions.geodesic(opts.getBoolean("geodesic"));
    }
    if (opts.has("zIndex")) {
      polylineOptions.zIndex(opts.getInt("zIndex"));
    }

    Polyline polyline = map.addPolyline(polylineOptions);
    String id = "polyline_" + polyline.getId();
    this.objects.put(id, polyline);

    String boundsId = "polyline_bounds_" + polyline.getId();
    this.objects.put(boundsId, builder.build());

    JSONObject result = new JSONObject();
    result.put("hashCode", polyline.hashCode());
    result.put("id", id);
    callbackContext.success(result);
  }
  /**
   * Set points
   *
   * @param args
   * @param callbackContext
   * @throws JSONException
   */
  @SuppressWarnings("unused")
  private void setPoints(final JSONArray args, final CallbackContext callbackContext)
      throws JSONException {
    String id = args.getString(1);
    Polyline polyline = this.getPolyline(id);

    JSONArray points = args.getJSONArray(2);
    List<LatLng> path = PluginUtil.JSONArray2LatLngList(points);
    polyline.setPoints(path);

    LatLngBounds.Builder builder = new LatLngBounds.Builder();
    for (int i = 0; i < path.size(); i++) {
      builder.include(path.get(i));
    }
    this.objects.put("polyline_bounds_" + polyline.getId(), builder.build());

    this.sendNoResult(callbackContext);
  }
  /**
   * Draw geodesic line
   *
   * @ref http://jamesmccaffrey.wordpress.com/2011/04/17/drawing-a-geodesic-line-for-bing-maps-ajax/
   * @ref
   *     http://spphire9.wordpress.com/2014/02/11/%E4%BA%8C%E6%AC%A1%E3%83%99%E3%82%B8%E3%82%A7%E6%9B%B2%E7%B7%9A%E3%81%A8%E7%B7%9A%E5%88%86%E3%81%AE%E5%BD%93%E3%81%9F%E3%82%8A%E5%88%A4%E5%AE%9A/
   * @ref http://my-clip-devdiary.blogspot.com/2014/01/html5canvas.html
   * @param args
   * @param callbackContext
   * @throws JSONException
   */
  @SuppressWarnings("unused")
  private void createPolyline2(final JSONArray args, final CallbackContext callbackContext)
      throws JSONException {
    final PolylineOptions polylineOptions = new PolylineOptions();
    int color;

    JSONObject opts = args.getJSONObject(1);
    if (opts.has("points")) {
      JSONArray points = opts.getJSONArray("points");
      List<LatLng> path = PluginUtil.JSONArray2LatLngList(points);

      for (int k = 0; k < path.size() - 1; k++) {
        LatLng start = path.get(k);
        LatLng finish = path.get(k + 1);

        if (start.longitude > finish.longitude) {
          start = finish;
          finish = path.get(k);
        }

        // convert to radians
        double lat1 = start.latitude * (Math.PI / 180.0);
        double lng1 = start.longitude * (Math.PI / 180.0);
        double lat2 = finish.latitude * (Math.PI / 180.0);
        double lng2 = finish.longitude * (Math.PI / 180.0);

        double d =
            2
                * Math.asin(
                    Math.sqrt(
                        Math.pow((Math.sin((lat1 - lat2) / 2)), 2)
                            + Math.cos(lat1)
                                * Math.cos(lat2)
                                * Math.pow((Math.sin((lng1 - lng2) / 2)), 2)));
        List<LatLng> wayPoints = new ArrayList<LatLng>();
        double f = 0.00000000f; // fraction of the curve
        double finc = 0.01000000f; // fraction increment

        while (f <= 1.00000000f) {
          double A = Math.sin((1.0 - f) * d) / Math.sin(d);
          double B = Math.sin(f * d) / Math.sin(d);

          double x = A * Math.cos(lat1) * Math.cos(lng1) + B * Math.cos(lat2) * Math.cos(lng2);
          double y = A * Math.cos(lat1) * Math.sin(lng1) + B * Math.cos(lat2) * Math.sin(lng2);
          double z = A * Math.sin(lat1) + B * Math.sin(lat2);
          double lat = Math.atan2(z, Math.sqrt((x * x) + (y * y)));
          double lng = Math.atan2(y, x);

          LatLng wp = new LatLng(lat / (Math.PI / 180.0), lng / (Math.PI / 180.0));
          wayPoints.add(wp);

          f += finc;
        } // while

        // break into waypoints with negative longitudes and those with positive longitudes
        List<LatLng> negLons = new ArrayList<LatLng>(); // lat-lons where the lon part is negative
        List<LatLng> posLons = new ArrayList<LatLng>();
        List<LatLng> connect = new ArrayList<LatLng>();

        for (int i = 0; i < wayPoints.size(); ++i) {
          if (wayPoints.get(i).longitude <= 0.0f) negLons.add(wayPoints.get(i));
          else posLons.add(wayPoints.get(i));
        }

        // we may have to connect over 0.0 longitude
        for (int i = 0; i < wayPoints.size() - 1; ++i) {
          if (wayPoints.get(i).longitude <= 0.0f && wayPoints.get(i + 1).longitude >= 0.0f
              || wayPoints.get(i).longitude >= 0.0f && wayPoints.get(i + 1).longitude <= 0.0f) {
            if (Math.abs(wayPoints.get(i).longitude) + Math.abs(wayPoints.get(i + 1).longitude)
                < 100.0f) {
              connect.add(wayPoints.get(i));
              connect.add(wayPoints.get(i + 1));
            }
          }
        }

        PolylineOptions options = new PolylineOptions();
        options.color(Color.RED);
        options.width(4);
        if (negLons.size() >= 2) {
          options.addAll(negLons);
        }

        if (posLons.size() >= 2) {
          options.addAll(posLons);
        }

        if (connect.size() >= 2) {
          options.addAll(connect);
        }
        map.addPolyline(options);
      }
    }

    this.sendNoResult(callbackContext);
  }