function gmclipline (fm, to, bounds) {
  var n = bounds.getNorthEast().lat();
  var e = bounds.getNorthEast().lng();
  var s = bounds.getSouthWest().lat();
  var w = bounds.getSouthWest().lng();

  // trivial case
  if (bounds.contains (fm) && bounds.contains (to)) {
    // alert ("case trivial");
    return [fm, to];
  }
  else if (bounds.contains (fm) && !bounds.contains (to)) {
    return subclip (fm, to, n, e, s, w);
  }
  else if (!bounds.contains (fm) && bounds.contains (to)) {
    return subclip (to, fm, n, e, s, w);
  }
  // both are outside
  else {
    // attempt to clip at 'to'
    var l1 = subclip (fm, to, n, e, s, w);
    // if failed, return immediately
    if (l1 == false) return false;
    var newto = l1[1];
    return subclip (newto, fm, n, e, s, w);
  }
}

// this function always clips line (a, b) by modifying b
function subclip (a, b, n, e, s, w) {
  // if lat()s are (about) equal, solve for lng()s
  if (Math.abs (a.lat() - b.lat()) < 0.0000001) {
    if (b.lng() > e) b = new GLatLng (b.lat(), e);
    if (b.lng() < w) b = new GLatLng (b.lat(), w);
    // alert ("case samelat");
    return [a, b];
  }
  // else if lng()s are (about) equal, solve for lat()s
  else if (Math.abs (a.lng() - b.lng()) < 0.0000001) {
    if (b.lat() > n) b = new GLatLng (n, b.lng());
    if (b.lat() < s) b = new GLatLng (s, b.lng());
    // alert ("case samelng");
    return [a, b];
  }
  // else do the real work
  else {

    var cl_n = false;
    var cl_s = false;
    var cl_e = false;
    var cl_w = false;

    // solve for lat=n(orth)
    var lng_n =
     b.lng() + (n - b.lat()) * (b.lng() - a.lng()) / (b.lat() - a.lat());
    if (w <= lng_n && lng_n <= e && (
     (a.lng() <= lng_n && lng_n <= b.lng()) ||
     (a.lng() >= lng_n && lng_n >= b.lng()))) {
      cl_n = true;
    }
    // solve for lat=s(outh)
    var lng_s =
     b.lng() + (s - b.lat()) * (b.lng() - a.lng()) / (b.lat() - a.lat());
    if (w <= lng_s && lng_s <= e && (
     (a.lng() <= lng_s && lng_s <= b.lng()) ||
     (a.lng() >= lng_s && lng_s >= b.lng()))) {
      cl_s = true;
    }
    // solve for lng=e(ast)
    var lat_e =
     b.lat() + (e - b.lng()) * (b.lat() - a.lat()) / (b.lng() - a.lng());
    if (s <= lat_e && lat_e <= n && (
     (a.lat() <= lat_e && lat_e <= b.lat()) ||
     (a.lat() >= lat_e && lat_e >= b.lat()))) {
      cl_e = true;
    }
    // solve for lng=w(est)
    var lat_w =
     b.lat() + (w - b.lng()) * (b.lat() - a.lat()) / (b.lng() - a.lng());
    if (s <= lat_w && lat_w <= n && (
     (a.lat() <= lat_w && lat_w <= b.lat()) ||
     (a.lat() >= lat_w && lat_w >= b.lat()))) {
      cl_w = true;
    }

    // check north-clip only if a is to the south of n,
    if (a.lat() < n && cl_n) {
      b = new GLatLng (n, lng_n);
      // alert ("case lat=north");
      return [a, b];
    }
    // , then check south-clip only if a is to the north of s,
    if (a.lat() > s && cl_s) {
      b = new GLatLng (s, lng_s);
      // alert ("case lat=south");
      return [a, b];
    }

    // same for east-west: check east-clip only if a is to the west of e,
    if (a.lng() < e && cl_e) {
      b = new GLatLng (lat_e, e);
      // alert ("case lng=east");
      return [a, b];
    }
    // , then check west-clip only if a is to the east of w
    if (a.lng() > w && cl_w) {
      b = new GLatLng (lat_w, w);
      // alert ("case lng=west");
      return [a, b];
    }

    return false;
  }
}
