private void insertTransportIntoIndex(TransportRoute route) throws SQLException {
    transRouteStat.setLong(1, route.getId());
    transRouteStat.setString(2, route.getType());
    transRouteStat.setString(3, route.getOperator());
    transRouteStat.setString(4, route.getRef());
    transRouteStat.setString(5, route.getName());
    transRouteStat.setString(6, route.getEnName());
    transRouteStat.setInt(7, route.getAvgBothDistance());
    addBatch(transRouteStat);

    writeRouteStops(route, route.getForwardStops(), true);
    writeRouteStops(route, route.getBackwardStops(), false);
  }
  public net.osmand.data.TransportRoute getTransportRoute(int filePointer, TransportIndex ind)
      throws IOException {
    codedIS.seek(filePointer);
    int routeLength = codedIS.readRawVarint32();
    int old = codedIS.pushLimit(routeLength);
    net.osmand.data.TransportRoute dataObject = new net.osmand.data.TransportRoute();
    boolean end = false;
    int name = -1;
    int nameEn = -1;
    int operator = -1;
    int type = -1;
    long rid = 0;
    int rx = 0;
    int ry = 0;
    long did = 0;
    int dx = 0;
    int dy = 0;
    while (!end) {
      int t = codedIS.readTag();
      int tag = WireFormat.getTagFieldNumber(t);
      switch (tag) {
        case 0:
          end = true;
          break;
        case OsmandOdb.TransportRoute.DISTANCE_FIELD_NUMBER:
          dataObject.setDistance(codedIS.readUInt32());
          break;
        case OsmandOdb.TransportRoute.ID_FIELD_NUMBER:
          dataObject.setId(codedIS.readUInt64());
          break;
        case OsmandOdb.TransportRoute.REF_FIELD_NUMBER:
          dataObject.setRef(codedIS.readString());
          break;
        case OsmandOdb.TransportRoute.TYPE_FIELD_NUMBER:
          type = codedIS.readUInt32();
          break;
        case OsmandOdb.TransportRoute.NAME_EN_FIELD_NUMBER:
          nameEn = codedIS.readUInt32();
          break;
        case OsmandOdb.TransportRoute.NAME_FIELD_NUMBER:
          name = codedIS.readUInt32();
          break;
        case OsmandOdb.TransportRoute.OPERATOR_FIELD_NUMBER:
          operator = codedIS.readUInt32();
          break;
        case OsmandOdb.TransportRoute.REVERSESTOPS_FIELD_NUMBER:
          int length = codedIS.readRawVarint32();
          int olds = codedIS.pushLimit(length);
          TransportStop stop = readTransportRouteStop(dx, dy, did);
          dataObject.getBackwardStops().add(stop);
          did = stop.getId();
          dx =
              (int) MapUtils.getTileNumberX(TRANSPORT_STOP_ZOOM, stop.getLocation().getLongitude());
          dy = (int) MapUtils.getTileNumberY(TRANSPORT_STOP_ZOOM, stop.getLocation().getLatitude());
          codedIS.popLimit(olds);
          break;
        case OsmandOdb.TransportRoute.DIRECTSTOPS_FIELD_NUMBER:
          length = codedIS.readRawVarint32();
          olds = codedIS.pushLimit(length);
          stop = readTransportRouteStop(rx, ry, rid);
          dataObject.getForwardStops().add(stop);
          rid = stop.getId();
          rx =
              (int) MapUtils.getTileNumberX(TRANSPORT_STOP_ZOOM, stop.getLocation().getLongitude());
          ry = (int) MapUtils.getTileNumberY(TRANSPORT_STOP_ZOOM, stop.getLocation().getLatitude());
          codedIS.popLimit(olds);
          break;
        default:
          skipUnknownField(t);
          break;
      }
    }
    codedIS.popLimit(old);
    if (name != -1) {
      dataObject.setName(getStringFromStringTable(ind.stringTable, name));
    }
    if (nameEn != -1) {
      dataObject.setEnName(getStringFromStringTable(ind.stringTable, nameEn));
    } else {
      dataObject.setEnName(Junidecode.unidecode(dataObject.getName()));
    }

    if (operator != -1) {
      dataObject.setOperator(getStringFromStringTable(ind.stringTable, operator));
    }
    if (type != -1) {
      dataObject.setType(getStringFromStringTable(ind.stringTable, type));
    }
    for (int i = 0; i < 2; i++) {
      List<TransportStop> stops =
          i == 0 ? dataObject.getForwardStops() : dataObject.getBackwardStops();
      for (TransportStop s : stops) {
        if (s.getName().length() > 0) {
          s.setName(getStringFromStringTable(ind.stringTable, s.getName().charAt(0)));
        }
        if (s.getEnName().length() > 0) {
          s.setEnName(getStringFromStringTable(ind.stringTable, s.getEnName().charAt(0)));
        } else {
          s.setEnName(Junidecode.unidecode(s.getName()));
        }
      }
    }

    return dataObject;
  }
  private TransportRoute indexTransportRoute(Relation rel) {
    String ref = rel.getTag(OSMTagKey.REF);
    String route = rel.getTag(OSMTagKey.ROUTE);
    String operator = rel.getTag(OSMTagKey.OPERATOR);

    Relation master = masterRoutes.get(rel.getId());
    if (master != null) {
      if (ref == null) ref = master.getTag(OSMTagKey.REF);
      if (route == null) route = master.getTag(OSMTagKey.ROUTE);
      if (operator == null) operator = master.getTag(OSMTagKey.OPERATOR);
    }

    if (route == null || ref == null) {
      return null;
    }
    if (!acceptedRoutes.contains(route)) {
      return null;
    }
    TransportRoute r = new TransportRoute(rel, ref);
    r.setOperator(operator);
    r.setType(route);

    if (operator != null) {
      route = operator + " : " + route; // $NON-NLS-1$
    }

    final Map<TransportStop, Integer> forwardStops = new LinkedHashMap<TransportStop, Integer>();
    final Map<TransportStop, Integer> backwardStops = new LinkedHashMap<TransportStop, Integer>();
    int currentStop = 0;
    int forwardStop = 0;
    int backwardStop = 0;
    for (Entry<Entity, String> e : rel.getMemberEntities().entrySet()) {
      if (e.getValue().contains("stop")) { // $NON-NLS-1$
        if (e.getKey() instanceof Node) {
          TransportStop stop = new TransportStop(e.getKey());
          boolean forward = e.getValue().contains("forward"); // $NON-NLS-1$
          boolean backward = e.getValue().contains("backward"); // $NON-NLS-1$
          currentStop++;
          if (forward || !backward) {
            forwardStop++;
          }
          if (backward) {
            backwardStop++;
          }
          boolean common = !forward && !backward;
          int index = -1;
          int i = e.getValue().length() - 1;
          int accum = 1;
          while (i >= 0 && Character.isDigit(e.getValue().charAt(i))) {
            if (index < 0) {
              index = 0;
            }
            index = accum * Character.getNumericValue(e.getValue().charAt(i)) + index;
            accum *= 10;
            i--;
          }
          if (index < 0) {
            index = forward ? forwardStop : (backward ? backwardStop : currentStop);
          }
          if (forward || common) {
            forwardStops.put(stop, index);
            r.getForwardStops().add(stop);
          }
          if (backward || common) {
            if (common) {
              // put with negative index
              backwardStops.put(stop, -index);
            } else {
              backwardStops.put(stop, index);
            }

            r.getBackwardStops().add(stop);
          }
        }

      } else if (e.getKey() instanceof Way) {
        r.addWay((Way) e.getKey());
      }
    }
    if (forwardStops.isEmpty() && backwardStops.isEmpty()) {
      return null;
    }
    Collections.sort(
        r.getForwardStops(),
        new Comparator<TransportStop>() {
          @Override
          public int compare(TransportStop o1, TransportStop o2) {
            return forwardStops.get(o1) - forwardStops.get(o2);
          }
        });
    // all common stops are with negative index (reeval them)
    for (TransportStop s : new ArrayList<TransportStop>(backwardStops.keySet())) {
      if (backwardStops.get(s) < 0) {
        backwardStops.put(s, backwardStops.size() + backwardStops.get(s) - 1);
      }
    }
    Collections.sort(
        r.getBackwardStops(),
        new Comparator<TransportStop>() {
          @Override
          public int compare(TransportStop o1, TransportStop o2) {
            return backwardStops.get(o1) - backwardStops.get(o2);
          }
        });

    return r;
  }
  @Override
  public List<RouteInfoLocation> searchTransportRouteStops(
      double latitude, double longitude, LatLon locationToGo, int zoom) {
    long now = System.currentTimeMillis();
    final LatLon loc = new LatLon(latitude, longitude);
    double tileNumberX = MapUtils.getTileNumberX(zoom, longitude);
    double tileNumberY = MapUtils.getTileNumberY(zoom, latitude);
    double topLatitude = MapUtils.getLatitudeFromTile(zoom, tileNumberY - 0.5);
    double bottomLatitude = MapUtils.getLatitudeFromTile(zoom, tileNumberY + 0.5);
    double leftLongitude = MapUtils.getLongitudeFromTile(zoom, tileNumberX - 0.5);
    double rightLongitude = MapUtils.getLongitudeFromTile(zoom, tileNumberX + 0.5);
    SearchRequest<TransportStop> req =
        BinaryMapIndexReader.buildSearchTransportRequest(
            MapUtils.get31TileNumberX(leftLongitude),
            MapUtils.get31TileNumberX(rightLongitude),
            MapUtils.get31TileNumberY(topLatitude),
            MapUtils.get31TileNumberY(bottomLatitude),
            -1,
            null);
    List<RouteInfoLocation> listRoutes = new ArrayList<RouteInfoLocation>();
    try {
      List<TransportStop> stops = file.searchTransportIndex(req);

      TIntObjectHashMap<TransportStop> stopsToProcess = new TIntObjectHashMap<TransportStop>();
      for (TransportStop s : stops) {
        for (int ref : s.getReferencesToRoutes()) {
          TransportStop exist = stopsToProcess.get(ref);
          if (exist == null
              || MapUtils.getDistance(loc, s.getLocation())
                  < MapUtils.getDistance(loc, exist.getLocation())) {
            stopsToProcess.put(ref, s);
          }
        }
      }
      TIntObjectHashMap<TransportRoute> transportRoutes =
          file.getTransportRoutes(stopsToProcess.keys());
      for (int ref : stopsToProcess.keys()) {
        TransportRoute route = transportRoutes.get(ref);
        TransportStop s = stopsToProcess.get(ref);
        for (int i = 0; i < 2; i++) {
          boolean direction = i == 0;
          List<TransportStop> stps = direction ? route.getForwardStops() : route.getBackwardStops();
          // load only part

          while (!stps.isEmpty() && (stps.get(0).getId().longValue() != s.getId().longValue())) {
            stps.remove(0);
          }
          if (!stps.isEmpty()) {
            RouteInfoLocation r = new RouteInfoLocation();
            r.setRoute(route);
            r.setStart(stps.get(0));
            r.setDirection(direction);
            if (locationToGo != null) {
              int distToLoc = Integer.MAX_VALUE;
              for (TransportStop st : stps) {
                double ndist = MapUtils.getDistance(locationToGo, st.getLocation());
                if (ndist < distToLoc) {
                  distToLoc = (int) ndist;
                  r.setStop(st);
                  r.setDistToLocation(distToLoc);
                }
              }
            }
            listRoutes.add(r);
          }
        }
      }
      if (log.isDebugEnabled()) {
        log.debug(
            String.format(
                "Search for routes done in %s ms found %s.", //$NON-NLS-1$
                System.currentTimeMillis() - now, listRoutes.size()));
      }

      if (locationToGo != null) {
        Collections.sort(
            listRoutes,
            new Comparator<RouteInfoLocation>() {
              @Override
              public int compare(RouteInfoLocation object1, RouteInfoLocation object2) {
                int x =
                    (int)
                        (MapUtils.getDistance(loc, object1.getStart().getLocation())
                            + object1.getDistToLocation());
                int y =
                    (int)
                        (MapUtils.getDistance(loc, object2.getStart().getLocation())
                            + object2.getDistToLocation());
                return x - y;
              }
            });
      } else {
        Collections.sort(
            listRoutes,
            new Comparator<RouteInfoLocation>() {
              @Override
              public int compare(RouteInfoLocation object1, RouteInfoLocation object2) {
                return Double.compare(
                    MapUtils.getDistance(loc, object1.getStart().getLocation()),
                    MapUtils.getDistance(loc, object2.getStart().getLocation()));
              }
            });
      }
    } catch (IOException e) {
      log.error("Disk error", e); // $NON-NLS-1$
    }
    return listRoutes;
  }