public void loadRouteRegionData(
     List<RouteSubregion> toLoad, ResultMatcher<RouteDataObject> matcher) throws IOException {
   Collections.sort(
       toLoad,
       new Comparator<RouteSubregion>() {
         @Override
         public int compare(RouteSubregion o1, RouteSubregion o2) {
           int p1 = o1.filePointer + o1.shiftToData;
           int p2 = o2.filePointer + o2.shiftToData;
           return p1 == p2 ? 0 : (p1 < p2 ? -1 : 1);
         }
       });
   TLongArrayList idMap = new TLongArrayList();
   TLongObjectHashMap<TLongArrayList> restrictionMap = new TLongObjectHashMap<TLongArrayList>();
   for (RouteSubregion rs : toLoad) {
     if (rs.dataObjects == null) {
       codedIS.seek(rs.filePointer + rs.shiftToData);
       int limit = codedIS.readRawVarint32();
       int oldLimit = codedIS.pushLimit(limit);
       readRouteTreeData(rs, idMap, restrictionMap);
       codedIS.popLimit(oldLimit);
     }
     for (RouteDataObject ro : rs.dataObjects) {
       if (ro != null) {
         matcher.publish(ro);
       }
     }
     // free objects
     rs.dataObjects = null;
   }
 }
 public int countSubregions() {
   int cnt = 1;
   if (subregions != null) {
     for (RouteSubregion s : subregions) {
       cnt += s.countSubregions();
     }
   }
   return cnt;
 }
 public int getEstimatedSize() {
   int shallow = 7 * INT_SIZE + 4 * 3;
   if (subregions != null) {
     shallow += 8;
     for (RouteSubregion s : subregions) {
       shallow += s.getEstimatedSize();
     }
   }
   return shallow;
 }
 public List<RouteDataObject> loadRouteRegionData(RouteSubregion rs) throws IOException {
   TLongArrayList idMap = new TLongArrayList();
   TLongObjectHashMap<TLongArrayList> restrictionMap = new TLongObjectHashMap<TLongArrayList>();
   if (rs.dataObjects == null) {
     codedIS.seek(rs.filePointer + rs.shiftToData);
     int limit = codedIS.readRawVarint32();
     int oldLimit = codedIS.pushLimit(limit);
     readRouteTreeData(rs, idMap, restrictionMap);
     codedIS.popLimit(oldLimit);
   }
   List<RouteDataObject> res = rs.dataObjects;
   rs.dataObjects = null;
   return res;
 }
 private RouteSubregion readRouteTree(
     RouteSubregion thisTree, RouteSubregion parentTree, int depth, boolean readCoordinates)
     throws IOException {
   boolean readChildren = depth != 0;
   if (readChildren) {
     thisTree.subregions = new ArrayList<BinaryMapRouteReaderAdapter.RouteSubregion>();
   }
   thisTree.routeReg.regionsRead++;
   while (true) {
     int t = codedIS.readTag();
     int tag = WireFormat.getTagFieldNumber(t);
     switch (tag) {
       case 0:
         return thisTree;
       case RouteDataBox.LEFT_FIELD_NUMBER:
         int i = codedIS.readSInt32();
         if (readCoordinates) {
           thisTree.left = i + (parentTree != null ? parentTree.left : 0);
         }
         break;
       case RouteDataBox.RIGHT_FIELD_NUMBER:
         i = codedIS.readSInt32();
         if (readCoordinates) {
           thisTree.right = i + (parentTree != null ? parentTree.right : 0);
         }
         break;
       case RouteDataBox.TOP_FIELD_NUMBER:
         i = codedIS.readSInt32();
         if (readCoordinates) {
           thisTree.top = i + (parentTree != null ? parentTree.top : 0);
         }
         break;
       case RouteDataBox.BOTTOM_FIELD_NUMBER:
         i = codedIS.readSInt32();
         if (readCoordinates) {
           thisTree.bottom = i + (parentTree != null ? parentTree.bottom : 0);
         }
         break;
       case RouteDataBox.SHIFTTODATA_FIELD_NUMBER:
         thisTree.shiftToData = readInt();
         if (!readChildren) {
           // usually 0
           thisTree.subregions = new ArrayList<BinaryMapRouteReaderAdapter.RouteSubregion>();
           readChildren = true;
         }
         break;
       case RouteDataBox.BOXES_FIELD_NUMBER:
         if (readChildren) {
           RouteSubregion subregion = new RouteSubregion(thisTree.routeReg);
           subregion.length = readInt();
           subregion.filePointer = codedIS.getTotalBytesRead();
           int oldLimit = codedIS.pushLimit(subregion.length);
           readRouteTree(subregion, thisTree, depth - 1, true);
           thisTree.subregions.add(subregion);
           codedIS.popLimit(oldLimit);
           codedIS.seek(subregion.filePointer + subregion.length);
         } else {
           codedIS.seek(thisTree.filePointer + thisTree.length);
           // skipUnknownField(t);
         }
         break;
       default:
         skipUnknownField(t);
         break;
     }
   }
 }
 private void readRouteTreeData(
     RouteSubregion routeTree,
     TLongArrayList idTables,
     TLongObjectHashMap<TLongArrayList> restrictions)
     throws IOException {
   routeTree.dataObjects = new ArrayList<RouteDataObject>();
   idTables.clear();
   restrictions.clear();
   List<String> stringTable = null;
   while (true) {
     int t = codedIS.readTag();
     int tag = WireFormat.getTagFieldNumber(t);
     switch (tag) {
       case 0:
         TLongObjectIterator<TLongArrayList> it = restrictions.iterator();
         while (it.hasNext()) {
           it.advance();
           int from = (int) it.key();
           RouteDataObject fromr = routeTree.dataObjects.get(from);
           fromr.restrictions = new long[it.value().size()];
           for (int k = 0; k < fromr.restrictions.length; k++) {
             int to = (int) (it.value().get(k) >> RouteDataObject.RESTRICTION_SHIFT);
             long valto =
                 (idTables.get(to) << RouteDataObject.RESTRICTION_SHIFT)
                     | ((long) it.value().get(k) & RouteDataObject.RESTRICTION_MASK);
             fromr.restrictions[k] = valto;
           }
         }
         for (RouteDataObject o : routeTree.dataObjects) {
           if (o != null) {
             if (o.id < idTables.size()) {
               o.id = idTables.get((int) o.id);
             }
             if (o.names != null && stringTable != null) {
               int[] keys = o.names.keys();
               for (int j = 0; j < keys.length; j++) {
                 o.names.put(keys[j], stringTable.get(o.names.get(keys[j]).charAt(0)));
               }
             }
           }
         }
         return;
       case RouteDataBlock.DATAOBJECTS_FIELD_NUMBER:
         int length = codedIS.readRawVarint32();
         int oldLimit = codedIS.pushLimit(length);
         RouteDataObject obj =
             readRouteDataObject(routeTree.routeReg, routeTree.left, routeTree.top);
         while (obj.id >= routeTree.dataObjects.size()) {
           routeTree.dataObjects.add(null);
         }
         routeTree.dataObjects.set((int) obj.id, obj);
         codedIS.popLimit(oldLimit);
         break;
       case RouteDataBlock.IDTABLE_FIELD_NUMBER:
         long routeId = 0;
         length = codedIS.readRawVarint32();
         oldLimit = codedIS.pushLimit(length);
         idLoop:
         while (true) {
           int ts = codedIS.readTag();
           int tags = WireFormat.getTagFieldNumber(ts);
           switch (tags) {
             case 0:
               break idLoop;
             case IdTable.ROUTEID_FIELD_NUMBER:
               routeId += codedIS.readSInt64();
               idTables.add(routeId);
               break;
             default:
               skipUnknownField(ts);
               break;
           }
         }
         codedIS.popLimit(oldLimit);
         break;
       case RouteDataBlock.RESTRICTIONS_FIELD_NUMBER:
         length = codedIS.readRawVarint32();
         oldLimit = codedIS.pushLimit(length);
         long from = 0;
         long to = 0;
         long type = 0;
         idLoop:
         while (true) {
           int ts = codedIS.readTag();
           int tags = WireFormat.getTagFieldNumber(ts);
           switch (tags) {
             case 0:
               break idLoop;
             case RestrictionData.FROM_FIELD_NUMBER:
               from = codedIS.readInt32();
               break;
             case RestrictionData.TO_FIELD_NUMBER:
               to = codedIS.readInt32();
               break;
             case RestrictionData.TYPE_FIELD_NUMBER:
               type = codedIS.readInt32();
               break;
             default:
               skipUnknownField(ts);
               break;
           }
         }
         if (!restrictions.containsKey(from)) {
           restrictions.put(from, new TLongArrayList());
         }
         restrictions.get(from).add((to << RouteDataObject.RESTRICTION_SHIFT) + type);
         codedIS.popLimit(oldLimit);
         break;
       case RouteDataBlock.STRINGTABLE_FIELD_NUMBER:
         length = codedIS.readRawVarint32();
         oldLimit = codedIS.pushLimit(length);
         stringTable = map.readStringTable();
         //				codedIS.skipRawBytes(codedIS.getBytesUntilLimit());
         codedIS.popLimit(oldLimit);
         break;
       default:
         skipUnknownField(t);
         break;
     }
   }
 }
  protected void readRouteIndex(RouteRegion region) throws IOException {
    int routeEncodingRule = 1;
    while (true) {
      int t = codedIS.readTag();
      int tag = WireFormat.getTagFieldNumber(t);
      switch (tag) {
        case 0:
          return;
        case OsmandOdb.OsmAndRoutingIndex.NAME_FIELD_NUMBER:
          region.name = codedIS.readString();
          break;
        case OsmandOdb.OsmAndRoutingIndex.RULES_FIELD_NUMBER:
          {
            int len = codedIS.readInt32();
            int oldLimit = codedIS.pushLimit(len);
            readRouteEncodingRule(region, routeEncodingRule++);
            codedIS.skipRawBytes(codedIS.getBytesUntilLimit());
            codedIS.popLimit(oldLimit);
          }
          break;
        case OsmandOdb.OsmAndRoutingIndex.ROOTBOXES_FIELD_NUMBER:
        case OsmandOdb.OsmAndRoutingIndex.BASEMAPBOXES_FIELD_NUMBER:
          {
            RouteSubregion subregion = new RouteSubregion(region);
            subregion.length = readInt();
            subregion.filePointer = codedIS.getTotalBytesRead();
            int oldLimit = codedIS.pushLimit(subregion.length);
            readRouteTree(subregion, null, 0, true);
            if (tag == OsmandOdb.OsmAndRoutingIndex.ROOTBOXES_FIELD_NUMBER) {
              region.subregions.add(subregion);
            } else {
              region.basesubregions.add(subregion);
            }
            codedIS.skipRawBytes(codedIS.getBytesUntilLimit());
            codedIS.popLimit(oldLimit);
            break;
          }
        case OsmandOdb.OsmAndRoutingIndex.BASEBORDERBOX_FIELD_NUMBER:
        case OsmandOdb.OsmAndRoutingIndex.BORDERBOX_FIELD_NUMBER:
          {
            int length = readInt();
            int filePointer = codedIS.getTotalBytesRead();
            if (tag == OsmandOdb.OsmAndRoutingIndex.BORDERBOX_FIELD_NUMBER) {
              region.borderBoxLength = length;
              region.borderBoxPointer = filePointer;
            } else {
              region.baseBorderBoxLength = length;
              region.baseBorderBoxPointer = filePointer;
            }
            codedIS.skipRawBytes(length);
            break;
          }
        case OsmandOdb.OsmAndRoutingIndex.BLOCKS_FIELD_NUMBER:
          {
            // Finish reading file!
            codedIS.skipRawBytes(codedIS.getBytesUntilLimit());
            break;
          }

        default:
          skipUnknownField(t);
          break;
      }
    }
  }