/* encode GeoPoint into nearest address URI */
  protected String getGeoPointGeocodeURL(String address, String country) {
    StringBuffer sb = new StringBuffer();
    GoogleSig sig = this.getSignature();

    /* country */
    if (StringTools.isBlank(country)) {
      country = this.getProperties().getString(PROP_countryCodeBias, DEFAULT_COUNTRY);
    }

    /* predefined URL */
    String gcURL = this.getProperties().getString(PROP_geocodeURL, null);
    if (!StringTools.isBlank(gcURL)) {
      sb.append(gcURL);
      sb.append("&q=").append(URIArg.encodeArg(address));
      if (!StringTools.isBlank(country)) {
        // country code bias: http://en.wikipedia.org/wiki/CcTLD
        sb.append("&gl=").append(country);
      }
      return sb.toString();
    }

    /* assemble URL */
    sb.append(this.getGeoPointGeocodeURI());
    sb.append("output=xml");
    sb.append("&oe=utf8");

    /* address/country */
    sb.append("&q=").append(URIArg.encodeArg(address));
    if (!StringTools.isBlank(country)) {
      sb.append("&gl=").append(country);
    }

    /* sensor */
    String sensor = this.getProperties().getString(PROP_sensor, null);
    if (!StringTools.isBlank(sensor)) {
      sb.append("&sensor=").append(sensor);
    }

    /* channel */
    String channel = this.getProperties().getString(PROP_channel, null);
    if (!StringTools.isBlank(channel)) {
      sb.append("&channel=").append(channel);
    }

    /* key */
    String auth = this.getAuthorization();
    if (StringTools.isBlank(auth) || auth.startsWith("*")) {
      // invalid key
    } else if (auth.startsWith(CLIENT_ID_PREFIX)) {
      sb.append("&client=").append(auth);
    } else {
      sb.append("&key=").append(auth);
    }

    /* return url */
    String defURL = sb.toString();
    if (sig == null) {
      return defURL;
    } else {
      String urlStr = sig.signURL(defURL);
      return (urlStr != null) ? urlStr : defURL;
    }
  }
  /* encode GeoPoint into nearest address URI */
  protected String getAddressReverseGeocodeURL(GeoPoint gp, String localStr) {
    StringBuffer sb = new StringBuffer();
    GoogleSig sig = this.getSignature();

    /* predefined URL */
    String rgURL = this.getProperties().getString(PROP_reverseGeocodeURL, null);
    if (!StringTools.isBlank(rgURL)) {
      sb.append(rgURL);
      sb.append("&ll=")
          .append(gp.getLatitudeString(GeoPoint.SFORMAT_DEC_5, null))
          .append(",")
          .append(gp.getLongitudeString(GeoPoint.SFORMAT_DEC_5, null));
      String defURL = sb.toString();
      if (sig == null) {
        return defURL;
      } else {
        String urlStr = sig.signURL(defURL);
        return (urlStr != null) ? urlStr : defURL;
      }
    }

    /* assemble URL */
    sb.append(this.getAddressReverseGeocodeURI());
    sb.append("output=xml");
    sb.append("&oe=utf8");

    /* latitude/longitude */
    sb.append("&ll=")
        .append(gp.getLatitudeString(GeoPoint.SFORMAT_DEC_5, null))
        .append(",")
        .append(gp.getLongitudeString(GeoPoint.SFORMAT_DEC_5, null));

    /* sensor */
    String sensor = this.getProperties().getString(PROP_sensor, null);
    if (!StringTools.isBlank(sensor)) {
      sb.append("&sensor=").append(sensor);
    }

    /* key */
    String channel = this.getProperties().getString(PROP_channel, null);
    String auth = this.getAuthorization();
    if (StringTools.isBlank(auth) || auth.startsWith("*")) {
      // invalid key
    } else if (auth.startsWith(CLIENT_ID_PREFIX)) {
      sb.append("&client=").append(auth);
      if (StringTools.isBlank(channel)) {
        channel = DBConfig.getServiceAccountID(null);
      }
    } else {
      sb.append("&key=").append(auth);
    }

    /* channel */
    if (!StringTools.isBlank(channel)) {
      sb.append("&channel=").append(channel);
    }

    /* localization ("&hl=") */
    if (!StringTools.isBlank(localStr)) {
      sb.append("&hl=").append(localStr);
    }

    /* return url */
    String defURL = sb.toString();
    if (sig == null) {
      return defURL;
    } else {
      String urlStr = sig.signURL(defURL);
      return (urlStr != null) ? urlStr : defURL;
    }
  }