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;
  }