private TransportStop readTransportStop(
      int shift, int cleft, int cright, int ctop, int cbottom, SearchRequest<TransportStop> req)
      throws IOException {
    int tag = WireFormat.getTagFieldNumber(codedIS.readTag());
    if (OsmandOdb.TransportStop.DX_FIELD_NUMBER != tag) {
      throw new IllegalArgumentException();
    }
    int x = codedIS.readSInt32() + cleft;

    tag = WireFormat.getTagFieldNumber(codedIS.readTag());
    if (OsmandOdb.TransportStop.DY_FIELD_NUMBER != tag) {
      throw new IllegalArgumentException();
    }
    int y = codedIS.readSInt32() + ctop;
    if (req.right < x || req.left > x || req.top > y || req.bottom < y) {
      codedIS.skipRawBytes(codedIS.getBytesUntilLimit());
      return null;
    }

    req.numberOfAcceptedObjects++;
    req.cacheTypes.clear();

    TransportStop dataObject = new TransportStop();
    dataObject.setLocation(
        MapUtils.getLatitudeFromTile(TRANSPORT_STOP_ZOOM, y),
        MapUtils.getLongitudeFromTile(TRANSPORT_STOP_ZOOM, x));
    dataObject.setFileOffset(shift);
    while (true) {
      int t = codedIS.readTag();
      tag = WireFormat.getTagFieldNumber(t);
      switch (tag) {
        case 0:
          dataObject.setReferencesToRoutes(req.cacheTypes.toArray());
          if (dataObject.getEnName().length() == 0) {
            dataObject.setEnName(Junidecode.unidecode(dataObject.getName()));
          }
          return dataObject;
        case OsmandOdb.TransportStop.ROUTES_FIELD_NUMBER:
          req.cacheTypes.add(shift - codedIS.readUInt32());
          break;
        case OsmandOdb.TransportStop.NAME_EN_FIELD_NUMBER:
          dataObject.setEnName("" + ((char) codedIS.readUInt32())); // $NON-NLS-1$
          break;
        case OsmandOdb.TransportStop.NAME_FIELD_NUMBER:
          int i = codedIS.readUInt32();
          dataObject.setName("" + ((char) i)); // $NON-NLS-1$
          break;
        case OsmandOdb.TransportStop.ID_FIELD_NUMBER:
          dataObject.setId(codedIS.readSInt64());
          break;
        default:
          skipUnknownField(t);
          break;
      }
    }
  }
 private TransportStop readTransportRouteStop(int dx, int dy, long did) throws IOException {
   TransportStop dataObject = new TransportStop();
   boolean end = false;
   while (!end) {
     int t = codedIS.readTag();
     int tag = WireFormat.getTagFieldNumber(t);
     switch (tag) {
       case 0:
         if (dataObject.getEnName().length() == 0) {
           dataObject.setEnName(Junidecode.unidecode(dataObject.getName()));
         }
         end = true;
         break;
       case OsmandOdb.TransportRouteStop.NAME_EN_FIELD_NUMBER:
         dataObject.setEnName("" + ((char) codedIS.readUInt32())); // $NON-NLS-1$
         break;
       case OsmandOdb.TransportRouteStop.NAME_FIELD_NUMBER:
         dataObject.setName("" + ((char) codedIS.readUInt32())); // $NON-NLS-1$
         break;
       case OsmandOdb.TransportRouteStop.ID_FIELD_NUMBER:
         did += codedIS.readSInt64();
         break;
       case OsmandOdb.TransportRouteStop.DX_FIELD_NUMBER:
         dx += codedIS.readSInt32();
         break;
       case OsmandOdb.TransportRouteStop.DY_FIELD_NUMBER:
         dy += codedIS.readSInt32();
         break;
       default:
         skipUnknownField(t);
         break;
     }
   }
   dataObject.setId(did);
   dataObject.setLocation(
       MapUtils.getLatitudeFromTile(TRANSPORT_STOP_ZOOM, dy),
       MapUtils.getLongitudeFromTile(TRANSPORT_STOP_ZOOM, dx));
   return dataObject;
 }
  protected void searchTransportTreeBounds(
      int pleft, int pright, int ptop, int pbottom, SearchRequest<TransportStop> req)
      throws IOException {
    int init = 0;
    int lastIndexResult = -1;
    int cright = 0;
    int cleft = 0;
    int ctop = 0;
    int cbottom = 0;
    req.numberOfReadSubtrees++;
    while (true) {
      if (req.isInterrupted()) {
        return;
      }
      int t = codedIS.readTag();
      int tag = WireFormat.getTagFieldNumber(t);
      if (init == 0xf) {
        // coordinates are init
        init = 0;
        if (cright < req.left || cleft > req.right || ctop > req.bottom || cbottom < req.top) {
          return;
        } else {
          req.numberOfAcceptedSubtrees++;
        }
      }
      switch (tag) {
        case 0:
          return;
        case OsmandOdb.TransportStopsTree.BOTTOM_FIELD_NUMBER:
          cbottom = codedIS.readSInt32() + pbottom;
          init |= 1;
          break;
        case OsmandOdb.TransportStopsTree.LEFT_FIELD_NUMBER:
          cleft = codedIS.readSInt32() + pleft;
          init |= 2;
          break;
        case OsmandOdb.TransportStopsTree.RIGHT_FIELD_NUMBER:
          cright = codedIS.readSInt32() + pright;
          init |= 4;
          break;
        case OsmandOdb.TransportStopsTree.TOP_FIELD_NUMBER:
          ctop = codedIS.readSInt32() + ptop;
          init |= 8;
          break;
        case OsmandOdb.TransportStopsTree.LEAFS_FIELD_NUMBER:
          int stopOffset = codedIS.getTotalBytesRead();
          int length = codedIS.readRawVarint32();
          int oldLimit = codedIS.pushLimit(length);
          if (lastIndexResult == -1) {
            lastIndexResult = req.searchResults.size();
          }
          req.numberOfVisitedObjects++;
          TransportStop transportStop =
              readTransportStop(stopOffset, cleft, cright, ctop, cbottom, req);
          if (transportStop != null) {
            req.searchResults.add(transportStop);
          }
          codedIS.popLimit(oldLimit);
          break;
        case OsmandOdb.TransportStopsTree.SUBTREES_FIELD_NUMBER:
          // left, ... already initialized
          length = readInt();
          int filePointer = codedIS.getTotalBytesRead();
          if (req.limit == -1 || req.limit >= req.searchResults.size()) {
            oldLimit = codedIS.pushLimit(length);
            searchTransportTreeBounds(cleft, cright, ctop, cbottom, req);
            codedIS.popLimit(oldLimit);
          }
          codedIS.seek(filePointer + length);

          if (lastIndexResult >= 0) {
            throw new IllegalStateException();
          }
          break;
        case OsmandOdb.TransportStopsTree.BASEID_FIELD_NUMBER:
          long baseId = codedIS.readUInt64();
          if (lastIndexResult != -1) {
            for (int i = lastIndexResult; i < req.searchResults.size(); i++) {
              TransportStop rs = req.searchResults.get(i);
              rs.setId(rs.getId() + baseId);
            }
          }
          break;
        default:
          skipUnknownField(t);
          break;
      }
    }
  }
  public void writeBinaryTransportIndex(
      BinaryMapIndexWriter writer, String regionName, Connection mapConnection)
      throws IOException, SQLException {
    try {
      closePreparedStatements(transRouteStat, transRouteStopsStat, transStopsStat);
      mapConnection.commit();
      transportStopsTree.flush();

      visitedStops = null; // allow gc to collect it
      PreparedStatement selectTransportRouteData =
          mapConnection.prepareStatement(
              "SELECT id, dist, name, name_en, ref, operator, type FROM transport_route"); //$NON-NLS-1$
      PreparedStatement selectTransportData =
          mapConnection.prepareStatement(
              "SELECT S.stop, S.direction,"
                  + //$NON-NLS-1$
                  "  A.latitude,  A.longitude, A.name, A.name_en "
                  + //$NON-NLS-1$
                  "FROM transport_route_stop S INNER JOIN transport_stop A ON A.id = S.stop WHERE S.route = ? ORDER BY S.ord asc"); //$NON-NLS-1$

      writer.startWriteTransportIndex(regionName);

      writer.startWriteTransportRoutes();

      // expect that memory would be enough
      Map<String, Integer> stringTable = createStringTableForTransport();
      Map<Long, Long> transportRoutes = new LinkedHashMap<Long, Long>();

      ResultSet rs = selectTransportRouteData.executeQuery();
      List<TransportStop> directStops = new ArrayList<TransportStop>();
      List<TransportStop> reverseStops = new ArrayList<TransportStop>();
      while (rs.next()) {

        long idRoute = rs.getLong(1);
        int dist = rs.getInt(2);
        String routeName = rs.getString(3);
        String routeEnName = rs.getString(4);
        if (routeEnName != null && routeEnName.equals(Junidecode.unidecode(routeName))) {
          routeEnName = null;
        }
        String ref = rs.getString(5);
        String operator = rs.getString(6);
        String type = rs.getString(7);

        selectTransportData.setLong(1, idRoute);
        ResultSet rset = selectTransportData.executeQuery();
        reverseStops.clear();
        directStops.clear();
        while (rset.next()) {
          boolean dir = rset.getInt(2) != 0;
          long idStop = rset.getInt(1);
          String stopName = rset.getString(5);
          String stopEnName = rset.getString(6);
          if (stopEnName != null && stopEnName.equals(Junidecode.unidecode(stopName))) {
            stopEnName = null;
          }
          TransportStop st = new TransportStop();
          st.setId(idStop);
          st.setName(stopName);
          st.setLocation(rset.getDouble(3), rset.getDouble(4));
          if (stopEnName != null) {
            st.setEnName(stopEnName);
          }
          if (dir) {
            directStops.add(st);
          } else {
            reverseStops.add(st);
          }
        }
        writer.writeTransportRoute(
            idRoute,
            routeName,
            routeEnName,
            ref,
            operator,
            type,
            dist,
            directStops,
            reverseStops,
            stringTable,
            transportRoutes);
      }
      rs.close();
      selectTransportRouteData.close();
      selectTransportData.close();
      writer.endWriteTransportRoutes();

      PreparedStatement selectTransportStop =
          mapConnection.prepareStatement(
              "SELECT A.id,  A.latitude,  A.longitude, A.name, A.name_en FROM transport_stop A where A.id = ?"); //$NON-NLS-1$
      PreparedStatement selectTransportRouteStop =
          mapConnection.prepareStatement(
              "SELECT DISTINCT S.route FROM transport_route_stop S WHERE S.stop = ? "); //$NON-NLS-1$
      long rootIndex = transportStopsTree.getFileHdr().getRootIndex();
      rtree.Node root = transportStopsTree.getReadNode(rootIndex);
      Rect rootBounds = calcBounds(root);
      if (rootBounds != null) {
        writer.startTransportTreeElement(
            rootBounds.getMinX(), rootBounds.getMaxX(), rootBounds.getMinY(), rootBounds.getMaxY());
        writeBinaryTransportTree(
            root,
            transportStopsTree,
            writer,
            selectTransportStop,
            selectTransportRouteStop,
            transportRoutes,
            stringTable);
        writer.endWriteTransportTreeElement();
      }
      selectTransportStop.close();
      selectTransportRouteStop.close();

      writer.writeTransportStringTable(stringTable);

      writer.endWriteTransportIndex();
      writer.flush();
    } catch (RTreeException e) {
      throw new IllegalStateException(e);
    }
  }