public NearbyStationsResult queryNearbyStations(
      final Location location, final int maxDistance, final int maxStations) throws IOException {
    if (location.hasLocation()) {
      final StringBuilder uri = new StringBuilder(queryEndpoint);
      uri.append(jsonNearbyStationsParameters(location, maxDistance, maxStations));

      return jsonNearbyStations(uri.toString());
    } else if (location.type == LocationType.STATION && location.hasId()) {
      final StringBuilder uri = new StringBuilder(stationBoardEndpoint);
      uri.append(xmlNearbyStationsParameters(location.id));

      return xmlNearbyStations(uri.toString());
    } else {
      throw new IllegalArgumentException("cannot handle: " + location.toDebugString());
    }
  }
  public NearbyStationsResult queryNearbyStations(
      final Location location, final int maxDistance, final int maxStations) throws IOException {
    final StringBuilder uri = new StringBuilder(API_BASE);

    if (location.hasLocation()) {
      uri.append("query.exe/dny");
      uri.append("?performLocating=2&tpl=stop2json");
      uri.append("&look_maxno=").append(maxStations != 0 ? maxStations : 200);
      uri.append("&look_maxdist=").append(maxDistance != 0 ? maxDistance : 5000);
      uri.append("&look_stopclass=").append(allProductsInt());
      uri.append("&look_nv=get_stopweight|yes");
      uri.append("&look_x=").append(location.lon);
      uri.append("&look_y=").append(location.lat);

      return jsonNearbyStations(uri.toString());
    } else if (location.type == LocationType.STATION && location.hasId()) {
      uri.append("bhftafel.exe/dn");
      uri.append("?near=Anzeigen");
      uri.append("&distance=").append(maxDistance != 0 ? maxDistance / 1000 : 50);
      uri.append("&input=").append(location.id);

      final CharSequence page = ParserUtils.scrape(uri.toString());

      final Matcher m = P_NEARBY_STATIONS_BY_STATION.matcher(page);

      final List<Location> stations = new ArrayList<Location>();
      while (m.find()) {
        final int sId = Integer.parseInt(m.group(1));
        final String sName = ParserUtils.resolveEntities(m.group(2).trim());

        final Location station = new Location(LocationType.STATION, sId, null, sName);
        stations.add(station);
      }

      if (maxStations == 0 || maxStations >= stations.size())
        return new NearbyStationsResult(null, stations);
      else return new NearbyStationsResult(null, stations.subList(0, maxStations));
    } else {
      throw new IllegalArgumentException("cannot handle: " + location.toDebugString());
    }
  }
  public NearbyStationsResult queryNearbyStations(
      final Location location, final int maxDistance, final int maxStations) throws IOException {
    if (location.hasLocation()) {
      final StringBuilder uri = new StringBuilder(queryEndpoint);
      uri.append(jsonNearbyStationsParameters(location, maxDistance, maxStations));

      return jsonNearbyStations(uri.toString());
    } else if (location.type == LocationType.STATION && location.hasId()) {
      final StringBuilder uri = new StringBuilder(stationBoardEndpoint);
      uri.append("?near=Anzeigen");
      uri.append("&distance=").append(maxDistance != 0 ? maxDistance / 1000 : 50);
      uri.append("&input=").append(location.id);

      final CharSequence page = ParserUtils.scrape(uri.toString());

      final Matcher mError = P_NEARBY_ERRORS.matcher(page);
      if (mError.find()) {
        if (mError.group(1) != null)
          return new NearbyStationsResult(null, NearbyStationsResult.Status.INVALID_STATION);
      }

      final List<Location> stations = new ArrayList<Location>();

      final Matcher mOwn = P_NEARBY_OWN.matcher(page);
      if (mOwn.find()) {
        final int parsedId = Integer.parseInt(mOwn.group(1));
        final int parsedLon = (int) (Float.parseFloat(mOwn.group(2)) * 1E6);
        final int parsedLat = (int) (Float.parseFloat(mOwn.group(3)) * 1E6);
        final String[] parsedPlaceAndName =
            splitPlaceAndName(ParserUtils.urlDecode(mOwn.group(4), ISO_8859_1));
        stations.add(
            new Location(
                LocationType.STATION,
                parsedId,
                parsedLat,
                parsedLon,
                parsedPlaceAndName[0],
                parsedPlaceAndName[1]));
      }

      final Matcher mPage = P_NEARBY_PAGE.matcher(page);
      if (mPage.find()) {
        final Matcher mCoarse = P_NEARBY_COARSE.matcher(mPage.group(1));

        while (mCoarse.find()) {
          final Matcher mFineLocation = P_NEARBY_FINE_LOCATION.matcher(mCoarse.group(1));

          if (mFineLocation.find()) {
            final int parsedId = Integer.parseInt(mFineLocation.group(1));
            final String[] parsedPlaceAndName =
                splitPlaceAndName(ParserUtils.resolveEntities(mFineLocation.group(2)));
            final Location station =
                new Location(
                    LocationType.STATION, parsedId, parsedPlaceAndName[0], parsedPlaceAndName[1]);
            if (!stations.contains(station)) stations.add(station);
          } else {
            throw new IllegalArgumentException("cannot parse '" + mCoarse.group(1) + "' on " + uri);
          }
        }

        if (maxStations == 0 || maxStations >= stations.size())
          return new NearbyStationsResult(null, stations);
        else return new NearbyStationsResult(null, stations.subList(0, maxStations));
      } else {
        throw new IllegalArgumentException("cannot parse '" + page + "' on " + uri);
      }
    } else {
      throw new IllegalArgumentException("cannot handle: " + location.toDebugString());
    }
  }