package net.osmand.binary; import gnu.trove.list.array.TIntArrayList; import gnu.trove.list.array.TLongArrayList; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import net.osmand.Algoritms; import net.osmand.LogUtil; import net.osmand.StringMatcher; import net.osmand.binary.BinaryMapAddressReaderAdapter.AddressRegion; import net.osmand.binary.BinaryMapTransportReaderAdapter.TransportIndex; import net.osmand.data.City; import net.osmand.data.PostCode; import net.osmand.data.Street; import net.osmand.data.TransportStop; import net.osmand.osm.LatLon; import net.osmand.osm.MapRenderingTypes; import net.osmand.osm.MapUtils; import net.osmand.osm.MapRenderingTypes.MapRulType; import net.sf.junidecode.Junidecode; import org.apache.commons.logging.Log; import com.google.protobuf.CodedInputStreamRAF; import com.google.protobuf.WireFormat; public class BinaryMapIndexReader { public final static int TRANSPORT_STOP_ZOOM = 24; private final static Log log = LogUtil.getLog(BinaryMapIndexReader.class); private final RandomAccessFile raf; private int version; private List<MapIndex> mapIndexes = new ArrayList<MapIndex>(); private List<AddressRegion> addressIndexes = new ArrayList<AddressRegion>(); private List<TransportIndex> transportIndexes = new ArrayList<TransportIndex>(); private List<BinaryIndexPart> indexes = new ArrayList<BinaryIndexPart>(); protected CodedInputStreamRAF codedIS; private final BinaryMapTransportReaderAdapter transportAdapter; private final BinaryMapAddressReaderAdapter addressAdapter; public BinaryMapIndexReader(final RandomAccessFile raf) throws IOException { this.raf = raf; codedIS = CodedInputStreamRAF.newInstance(raf, 1024); codedIS.setSizeLimit(Integer.MAX_VALUE); // 2048 MB transportAdapter = new BinaryMapTransportReaderAdapter(this); addressAdapter = new BinaryMapAddressReaderAdapter(this); init(); } private void init() throws IOException { boolean initCorrectly = false; while(true){ int t = codedIS.readTag(); int tag = WireFormat.getTagFieldNumber(t); switch (tag) { case 0: if(!initCorrectly){ throw new IOException("Corrupted file. It should be ended as it starts with version"); //$NON-NLS-1$ } return; case OsmandOdb.OsmAndStructure.VERSION_FIELD_NUMBER : version = codedIS.readUInt32(); break; case OsmandOdb.OsmAndStructure.MAPINDEX_FIELD_NUMBER: MapIndex mapIndex = new MapIndex(); mapIndex.length = readInt(); mapIndex.filePointer = codedIS.getTotalBytesRead(); int oldLimit = codedIS.pushLimit(mapIndex.length); readMapIndex(mapIndex); codedIS.popLimit(oldLimit); codedIS.seek(mapIndex.filePointer + mapIndex.length); mapIndexes.add(mapIndex); indexes.add(mapIndex); break; case OsmandOdb.OsmAndStructure.ADDRESSINDEX_FIELD_NUMBER: AddressRegion region = new AddressRegion(); region.length = readInt(); region.filePointer = codedIS.getTotalBytesRead(); oldLimit = codedIS.pushLimit(region.length); addressAdapter.readAddressIndex(region); codedIS.popLimit(oldLimit); codedIS.seek(region.filePointer + region.length); if(region.name != null){ addressIndexes.add(region); indexes.add(region); } break; case OsmandOdb.OsmAndStructure.TRANSPORTINDEX_FIELD_NUMBER: TransportIndex ind = new TransportIndex(); ind.length = readInt(); ind.filePointer = codedIS.getTotalBytesRead(); oldLimit = codedIS.pushLimit(ind.length); transportAdapter.readTransportIndex(ind); codedIS.popLimit(oldLimit); codedIS.seek(ind.filePointer + ind.length); transportIndexes.add(ind); indexes.add(ind); break; case OsmandOdb.OsmAndStructure.VERSIONCONFIRM_FIELD_NUMBER : int cversion = codedIS.readUInt32(); initCorrectly = cversion == version; break; default: skipUnknownField(t); break; } } } public List<BinaryIndexPart> getIndexes() { return indexes; } public boolean containsMapData(){ return mapIndexes.size() > 0; } public boolean containsAddressData(){ return addressIndexes.size() > 0; } public boolean hasTransportData(){ return transportIndexes.size() > 0; } public RandomAccessFile getRaf() { return raf; } public int readByte() throws IOException{ byte b = codedIS.readRawByte(); if(b < 0){ return b + 256; } else { return b; } } public final int readInt() throws IOException { int ch1 = readByte(); int ch2 = readByte(); int ch3 = readByte(); int ch4 = readByte(); return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0)); } public int getVersion() { return version; } protected void skipUnknownField(int tag) throws IOException { int wireType = WireFormat.getTagWireType(tag); if(wireType == WireFormat.WIRETYPE_FIXED32_LENGTH_DELIMITED){ int length = readInt(); codedIS.skipRawBytes(length); } else { codedIS.skipField(tag); } } /** * Transport public methods */ public net.osmand.data.TransportRoute getTransportRoute(int filePointer) throws IOException { TransportIndex ind = getTransportIndex(filePointer); if(ind == null){ return null; } return transportAdapter.getTransportRoute(filePointer, ind); } public boolean transportStopBelongsTo(TransportStop s){ return getTransportIndex(s.getFileOffset()) != null; } private TransportIndex getTransportIndex(int filePointer) { TransportIndex ind = null; for(TransportIndex i : transportIndexes){ if(i.filePointer <= filePointer && (filePointer - i.filePointer) < i.length){ ind = i; break; } } return ind; } public boolean containTransportData(double latitude, double longitude) { double x = MapUtils.getTileNumberX(TRANSPORT_STOP_ZOOM, longitude); double y = MapUtils.getTileNumberY(TRANSPORT_STOP_ZOOM, latitude); for (TransportIndex index : transportIndexes) { if (index.right >= x && index.left <= x && index.top <= y && index.bottom >= y) { return true; } } return false; } public boolean containTransportData(double topLatitude, double leftLongitude, double bottomLatitude, double rightLongitude){ double leftX = MapUtils.getTileNumberX(TRANSPORT_STOP_ZOOM, leftLongitude); double topY = MapUtils.getTileNumberY(TRANSPORT_STOP_ZOOM, topLatitude); double rightX = MapUtils.getTileNumberX(TRANSPORT_STOP_ZOOM, rightLongitude); double bottomY = MapUtils.getTileNumberY(TRANSPORT_STOP_ZOOM, bottomLatitude); for (TransportIndex index : transportIndexes) { if (index.right >= leftX && index.left <= rightX && index.top <= bottomY && index.bottom >= topY) { return true; } } return false; } public List<TransportStop> searchTransportIndex(SearchRequest<TransportStop> req) throws IOException { for (TransportIndex index : transportIndexes) { if (index.stopsFileLength == 0 || index.right < req.left || index.left > req.right || index.top > req.bottom || index.bottom < req.top) { continue; } codedIS.seek(index.stopsFileOffset); int oldLimit = codedIS.pushLimit(index.stopsFileLength); int offset = req.searchResults.size(); transportAdapter.searchTransportTreeBounds(0, 0, 0, 0, req); codedIS.popLimit(oldLimit); for (int i = offset; i < req.searchResults.size(); i++) { TransportStop st = req.searchResults.get(i); if (st.getName().length() != 0) { st.setName(transportAdapter.getStringFromStringTable(index.stringTable, st.getName().charAt(0))); } if (st.getEnName().length() != 0) { st.setEnName(transportAdapter.getStringFromStringTable(index.stringTable, st.getEnName().charAt(0))); } else { st.setEnName(Junidecode.unidecode(st.getName())); } } } log.info("Search is done. Visit " + req.numberOfVisitedObjects + " objects. Read " + req.numberOfAcceptedObjects + " objects."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ log.info("Read " + req.numberOfReadSubtrees + " subtrees. Go through " + req.numberOfAcceptedSubtrees + " subtrees."); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ return req.getSearchResults(); } /** * Address public methods */ public List<String> getRegionNames(){ List<String> names = new ArrayList<String>(); for(AddressRegion r : addressIndexes){ names.add(r.name); } return names; } private AddressRegion getRegionByName(String name){ for(AddressRegion r : addressIndexes){ if(r.name.equals(name)){ return r; } } throw new IllegalArgumentException(name); } public List<PostCode> getPostcodes(String region) throws IOException { List<PostCode> postcodes = new ArrayList<PostCode>(); AddressRegion r = getRegionByName(region); if(r.postcodesOffset != -1){ codedIS.seek(r.postcodesOffset); int len = readInt(); int old = codedIS.pushLimit(len); addressAdapter.readPostcodes(postcodes); codedIS.popLimit(old); } return postcodes; } public PostCode getPostcodeByName(String region, String name) throws IOException { AddressRegion r = getRegionByName(region); if (r.postcodesOffset != -1) { codedIS.seek(r.postcodesOffset); int len = readInt(); int old = codedIS.pushLimit(len); PostCode p = addressAdapter.findPostcode(name); if (p != null) { return p; } codedIS.popLimit(old); } return null; } public List<City> getCities(String region) throws IOException { List<City> cities = new ArrayList<City>(); AddressRegion r = getRegionByName(region); if(r.citiesOffset != -1){ codedIS.seek(r.citiesOffset); int len = readInt(); int old = codedIS.pushLimit(len); addressAdapter.readCities(cities, null, false); codedIS.popLimit(old); } return cities; } public List<City> getVillages(String region) throws IOException { return getVillages(region, null, false); } public List<City> getVillages(String region, StringMatcher nameMatcher, boolean useEn) throws IOException { List<City> cities = new ArrayList<City>(); AddressRegion r = getRegionByName(region); if(r.villagesOffset != -1){ codedIS.seek(r.villagesOffset); int len = readInt(); int old = codedIS.pushLimit(len); addressAdapter.readCities(cities, nameMatcher, useEn); codedIS.popLimit(old); } return cities; } public void preloadStreets(City c) throws IOException { checkAddressIndex(c.getFileOffset()); codedIS.seek(c.getFileOffset()); int size = codedIS.readRawVarint32(); int old = codedIS.pushLimit(size); addressAdapter.readCity(c, c.getFileOffset(), true, null, false); codedIS.popLimit(old); } public List<Street> findIntersectedStreets(City c, Street s, List<Street> streets) throws IOException { checkAddressIndex(c.getFileOffset()); addressAdapter.findIntersectedStreets(c, s, null, streets); return streets; } public LatLon findStreetIntersection(City c, Street s, Street s2) throws IOException { checkAddressIndex(c.getFileOffset()); return addressAdapter.findIntersectedStreets(c, s, s2, null); } public void preloadStreets(PostCode p) throws IOException { checkAddressIndex(p.getFileOffset()); codedIS.seek(p.getFileOffset()); int size = codedIS.readRawVarint32(); int old = codedIS.pushLimit(size); addressAdapter.readPostcode(p, p.getFileOffset(), true, null); codedIS.popLimit(old); } private void checkAddressIndex(int offset){ boolean ok = false; for(AddressRegion r : addressIndexes){ if(offset >= r.filePointer && offset <= (r.length + r.filePointer)){ ok = true; break; } } if(!ok){ throw new IllegalArgumentException("Illegal offset " + offset); //$NON-NLS-1$ } } public void preloadBuildings(Street s) throws IOException { checkAddressIndex(s.getFileOffset()); codedIS.seek(s.getFileOffset()); int size = codedIS.readRawVarint32(); int old = codedIS.pushLimit(size); addressAdapter.readStreet(s, true, 0, 0, null); codedIS.popLimit(old); } /** * Map public methods */ private void readMapIndex(MapIndex index) throws IOException { while(true){ int t = codedIS.readTag(); int tag = WireFormat.getTagFieldNumber(t); switch (tag) { case 0: if(index.encodingRules.isEmpty()){ // init encoding rules by default Map<String, MapRulType> map = MapRenderingTypes.getDefault().getEncodingRuleTypes(); for(String tags : map.keySet()){ MapRulType rt = map.get(tags); if(rt.getType(null) != 0){ initMapEncodingRule(index, rt.getType(null), rt.getSubType(null), tags, null); } for (String value : rt.getValuesSet()) { initMapEncodingRule(index, rt.getType(value), rt.getSubType(value), tags, value); } } } return; case OsmandOdb.OsmAndMapIndex.NAME_FIELD_NUMBER : index.setName(codedIS.readString()); break; case OsmandOdb.OsmAndMapIndex.RULES_FIELD_NUMBER : int len = codedIS.readInt32(); int oldLimit = codedIS.pushLimit(len); readMapEncodingRule(index); codedIS.popLimit(oldLimit); break; case OsmandOdb.OsmAndMapIndex.LEVELS_FIELD_NUMBER : int length = readInt(); int filePointer = codedIS.getTotalBytesRead(); oldLimit = codedIS.pushLimit(length); MapRoot mapRoot = readMapLevel(); mapRoot.length = length; mapRoot.filePointer = filePointer; index.getRoots().add(mapRoot); codedIS.popLimit(oldLimit); codedIS.seek(filePointer + length); break; default: skipUnknownField(t); break; } } } private void initMapEncodingRule(MapIndex index, int type, int subtype, String tag, String val) { int ind = ((subtype << 5) | type); if(!index.encodingRules.containsKey(tag)){ index.encodingRules.put(tag, new LinkedHashMap<String, Integer>()); } index.encodingRules.get(tag).put(val, ind); if(!index.decodingRules.containsKey(ind)){ index.decodingRules.put(ind, new TagValuePair(tag, val)); } } private void readMapEncodingRule(MapIndex index) throws IOException { int subtype = 0; int type = 0; String tags = null; String val = null; while(true){ int t = codedIS.readTag(); int tag = WireFormat.getTagFieldNumber(t); switch (tag) { case 0: initMapEncodingRule(index, type, subtype, tags, val); return; case OsmandOdb.MapEncodingRule.VALUE_FIELD_NUMBER : val = codedIS.readString().intern(); break; case OsmandOdb.MapEncodingRule.TAG_FIELD_NUMBER : tags = codedIS.readString().intern(); break; case OsmandOdb.MapEncodingRule.SUBTYPE_FIELD_NUMBER : subtype = codedIS.readUInt32(); break; case OsmandOdb.MapEncodingRule.TYPE_FIELD_NUMBER : type = codedIS.readUInt32(); break; default: skipUnknownField(t); break; } } } private MapRoot readMapLevel() throws IOException { MapRoot root = new MapRoot(); while(true){ int t = codedIS.readTag(); int tag = WireFormat.getTagFieldNumber(t); switch (tag) { case 0: return root; case OsmandOdb.MapRootLevel.BOTTOM_FIELD_NUMBER : root.bottom = codedIS.readInt32(); break; case OsmandOdb.MapRootLevel.LEFT_FIELD_NUMBER : root.left = codedIS.readInt32(); break; case OsmandOdb.MapRootLevel.RIGHT_FIELD_NUMBER : root.right = codedIS.readInt32(); break; case OsmandOdb.MapRootLevel.TOP_FIELD_NUMBER : root.top = codedIS.readInt32(); break; case OsmandOdb.MapRootLevel.MAXZOOM_FIELD_NUMBER : root.maxZoom = codedIS.readInt32(); break; case OsmandOdb.MapRootLevel.MINZOOM_FIELD_NUMBER : root.minZoom = codedIS.readInt32(); break; case OsmandOdb.MapRootLevel.ROOT_FIELD_NUMBER : MapTree r = new MapTree(); // left, ... already initialized r.length = readInt(); r.filePointer = codedIS.getTotalBytesRead(); int oldLimit = codedIS.pushLimit(r.length); readMapTreeBounds(r, root.left, root.right, root.top, root.bottom); root.trees.add(r); codedIS.popLimit(oldLimit); codedIS.seek(r.filePointer + r.length); break; default: skipUnknownField(t); break; } } } private void readMapTreeBounds(MapTree tree, int aleft, int aright, int atop, int abottom) throws IOException { int init = 0; while(true){ if(init == 0xf){ return; } int t = codedIS.readTag(); int tag = WireFormat.getTagFieldNumber(t); switch (tag) { case 0: return; case OsmandOdb.MapTree.BOTTOM_FIELD_NUMBER : tree.bottom = codedIS.readSInt32() + abottom; init |= 1; break; case OsmandOdb.MapTree.LEFT_FIELD_NUMBER : tree.left = codedIS.readSInt32() + aleft; init |= 2; break; case OsmandOdb.MapTree.RIGHT_FIELD_NUMBER : tree.right = codedIS.readSInt32() + aright; init |= 4; break; case OsmandOdb.MapTree.TOP_FIELD_NUMBER : tree.top = codedIS.readSInt32() + atop; init |= 8; break; default: skipUnknownField(t); break; } } } public List<BinaryMapDataObject> searchMapIndex(SearchRequest<BinaryMapDataObject> req) throws IOException { req.numberOfVisitedObjects = 0; req.numberOfAcceptedObjects = 0; req.numberOfAcceptedSubtrees = 0; req.numberOfReadSubtrees = 0; for (MapIndex mapIndex : mapIndexes) { for (MapRoot index : mapIndex.getRoots()) { if (index.minZoom <= req.zoom && index.maxZoom >= req.zoom) { if (index.right < req.left || index.left > req.right || index.top > req.bottom || index.bottom < req.top) { continue; } for (MapTree tree : index.trees) { if (tree.right < req.left || tree.left > req.right || tree.top > req.bottom || tree.bottom < req.top) { continue; } codedIS.seek(tree.filePointer); int oldLimit = codedIS.pushLimit(tree.length); searchMapTreeBounds(index.left, index.right, index.top, index.bottom, req, mapIndex); codedIS.popLimit(oldLimit); } } } } log.info("Search is done. Visit " + req.numberOfVisitedObjects + " objects. Read " + req.numberOfAcceptedObjects + " objects."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ log.info("Read " + req.numberOfReadSubtrees + " subtrees. Go through " + req.numberOfAcceptedSubtrees + " subtrees."); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ return req.getSearchResults(); } protected void searchMapTreeBounds(int pleft, int pright, int ptop, int pbottom, SearchRequest<BinaryMapDataObject> req, MapIndex root) 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){ init = 0; // coordinates are init if(cright < req.left || cleft > req.right || ctop > req.bottom || cbottom < req.top){ return; } else { req.numberOfAcceptedSubtrees++; } } switch (tag) { case 0: return; case OsmandOdb.MapTree.BOTTOM_FIELD_NUMBER : cbottom = codedIS.readSInt32() + pbottom; init |= 1; break; case OsmandOdb.MapTree.LEFT_FIELD_NUMBER : cleft = codedIS.readSInt32() + pleft; init |= 2; break; case OsmandOdb.MapTree.RIGHT_FIELD_NUMBER : cright = codedIS.readSInt32() + pright; init |= 4; break; case OsmandOdb.MapTree.TOP_FIELD_NUMBER : ctop = codedIS.readSInt32() + ptop; init |= 8; break; case OsmandOdb.MapTree.LEAFS_FIELD_NUMBER : int length = codedIS.readRawVarint32(); int oldLimit = codedIS.pushLimit(length); if(lastIndexResult == -1){ lastIndexResult = req.searchResults.size(); } BinaryMapDataObject mapObject = readMapDataObject(cleft, cright, ctop, cbottom, req, root); if(mapObject != null){ req.searchResults.add(mapObject); } codedIS.popLimit(oldLimit); break; case OsmandOdb.MapTree.SUBTREES_FIELD_NUMBER : // left, ... already initialized length = readInt(); int filePointer = codedIS.getTotalBytesRead(); oldLimit = codedIS.pushLimit(length); searchMapTreeBounds(cleft, cright, ctop, cbottom, req, root); codedIS.popLimit(oldLimit); codedIS.seek(filePointer + length); if(lastIndexResult >= 0){ throw new IllegalStateException(); } break; case OsmandOdb.MapTree.BASEID_FIELD_NUMBER : case OsmandOdb.MapTree.OLDBASEID_FIELD_NUMBER : long baseId = codedIS.readUInt64(); if (lastIndexResult != -1) { for (int i = lastIndexResult; i < req.searchResults.size(); i++) { BinaryMapDataObject rs = req.searchResults.get(i); rs.id += baseId; if (rs.restrictions != null) { for (int j = 0; j < rs.restrictions.length; j++) { rs.restrictions[j] += baseId; } } } } break; case OsmandOdb.MapTree.STRINGTABLE_FIELD_NUMBER : case OsmandOdb.MapTree.OLDSTRINGTABLE_FIELD_NUMBER : length = codedIS.readRawVarint32(); oldLimit = codedIS.pushLimit(length); List<String> stringTable = readStringTable(); codedIS.popLimit(oldLimit); if (lastIndexResult != -1) { for (int i = lastIndexResult; i < req.searchResults.size(); i++) { BinaryMapDataObject rs = req.searchResults.get(i); if (rs.stringId != -1) { rs.name = stringTable.get(rs.stringId); } } } break; default: skipUnknownField(t); break; } } } private int MASK_TO_READ = ~((1 << BinaryMapIndexWriter.SHIFT_COORDINATES) - 1); private BinaryMapDataObject readMapDataObject(int left, int right, int top, int bottom, SearchRequest<BinaryMapDataObject> req, MapIndex root) throws IOException { int tag = WireFormat.getTagFieldNumber(codedIS.readTag()); if(OsmandOdb.MapData.COORDINATES_FIELD_NUMBER != tag) { throw new IllegalArgumentException(); } req.cacheCoordinates.clear(); int size = codedIS.readRawVarint32(); int old = codedIS.pushLimit(size); int px = left & MASK_TO_READ; int py = top & MASK_TO_READ; boolean contains = false; int minX = Integer.MAX_VALUE; int maxX = 0; int minY = Integer.MAX_VALUE; int maxY = 0; req.numberOfVisitedObjects++; while(codedIS.getBytesUntilLimit() > 0){ int x = (codedIS.readSInt32() << BinaryMapIndexWriter.SHIFT_COORDINATES) + px; int y = (codedIS.readSInt32() << BinaryMapIndexWriter.SHIFT_COORDINATES) + py; req.cacheCoordinates.add(x); req.cacheCoordinates.add(y); px = x; py = y; if(!contains && req.left <= x && req.right >= x && req.top <= y && req.bottom >= y){ contains = true; } if(!contains){ minX = Math.min(minX, x); maxX = Math.max(maxX, x); minY = Math.min(minY, y); maxY = Math.max(maxY, y); } } if(!contains){ if(maxX >= req.left && minX <= req.right && minY <= req.bottom && maxY >= req.top){ contains = true; } } codedIS.popLimit(old); if(!contains){ codedIS.skipRawBytes(codedIS.getBytesUntilLimit()); return null; } // READ types tag = WireFormat.getTagFieldNumber(codedIS.readTag()); if(OsmandOdb.MapData.TYPES_FIELD_NUMBER != tag) { throw new IllegalArgumentException(); } req.cacheTypes.clear(); int sizeL = codedIS.readRawVarint32(); byte[] types = codedIS.readRawBytes(sizeL); for(int i=0; i<sizeL/2; i++){ req.cacheTypes.add(Algoritms.parseSmallIntFromBytes(types, i*2)); } boolean accept = true; if (req.searchFilter != null) { accept = req.searchFilter.accept(req.cacheTypes, root); } if(!accept){ codedIS.skipRawBytes(codedIS.getBytesUntilLimit()); return null; } req.numberOfAcceptedObjects++; BinaryMapDataObject dataObject = new BinaryMapDataObject(); dataObject.coordinates = req.cacheCoordinates.toArray(); dataObject.types = req.cacheTypes.toArray(); dataObject.mapIndex = root; while(true){ int t = codedIS.readTag(); tag = WireFormat.getTagFieldNumber(t); switch (tag) { case 0: return dataObject; case OsmandOdb.MapData.RESTRICTIONS_FIELD_NUMBER : sizeL = codedIS.readRawVarint32(); TLongArrayList list = new TLongArrayList(); old = codedIS.pushLimit(sizeL); while(codedIS.getBytesUntilLimit() > 0){ list.add(codedIS.readSInt64()); } codedIS.popLimit(old); dataObject.restrictions = list.toArray(); break; case OsmandOdb.MapData.HIGHWAYMETA_FIELD_NUMBER : dataObject.highwayAttributes = codedIS.readInt32(); break; case OsmandOdb.MapData.ID_FIELD_NUMBER : dataObject.id = codedIS.readSInt64(); break; case OsmandOdb.MapData.STRINGID_FIELD_NUMBER : dataObject.stringId = codedIS.readUInt32(); break; default: skipUnknownField(t); break; } } } private List<String> readStringTable() throws IOException{ List<String> list = new ArrayList<String>(); while(true){ int t = codedIS.readTag(); int tag = WireFormat.getTagFieldNumber(t); switch (tag) { case 0: return list; case OsmandOdb.StringTable.S_FIELD_NUMBER : list.add(codedIS.readString()); break; default: skipUnknownField(t); break; } } } protected List<AddressRegion> getAddressIndexes() { return addressIndexes; } public static SearchRequest<BinaryMapDataObject> buildSearchRequest(int sleft, int sright, int stop, int sbottom, int zoom){ SearchRequest<BinaryMapDataObject> request = new SearchRequest<BinaryMapDataObject>(); request.left = sleft; request.right = sright; request.top = stop; request.bottom = sbottom; request.zoom = zoom; return request; } public static SearchRequest<TransportStop> buildSearchTransportRequest(int sleft, int sright, int stop, int sbottom, int limit, List<TransportStop> stops){ SearchRequest<TransportStop> request = new SearchRequest<TransportStop>(); if (stops != null) { request.searchResults = stops; } request.left = sleft >> (31 - TRANSPORT_STOP_ZOOM); request.right = sright >> (31 - TRANSPORT_STOP_ZOOM); request.top = stop >> (31 - TRANSPORT_STOP_ZOOM); request.bottom = sbottom >> (31 - TRANSPORT_STOP_ZOOM); request.limit = limit; return request; } public void close() throws IOException{ if(codedIS != null){ raf.close(); codedIS = null; mapIndexes.clear(); addressIndexes.clear(); transportIndexes.clear(); } } public static interface SearchFilter { public boolean accept(TIntArrayList types, MapIndex index); } public static class SearchRequest<T> { // 31 zoom tiles int left = 0; int right = 0; int top = 0; int bottom = 0; int zoom = 15; int limit = -1; List<T> searchResults = new ArrayList<T>(); TIntArrayList cacheCoordinates = new TIntArrayList(); TIntArrayList cacheTypes = new TIntArrayList(); SearchFilter searchFilter = null; // TRACE INFO int numberOfVisitedObjects = 0; int numberOfAcceptedObjects = 0; int numberOfReadSubtrees = 0; int numberOfAcceptedSubtrees = 0; boolean interrupted = false; protected SearchRequest(){ } public SearchFilter getSearchFilter() { return searchFilter; } public void setSearchFilter(SearchFilter searchFilter) { this.searchFilter = searchFilter; } public List<T> getSearchResults() { return searchResults; } public void setInterrupted(boolean interrupted) { this.interrupted = interrupted; } public boolean isInterrupted() { return interrupted; } public void clearSearchResults(){ // recreate whole list to allow GC collect old data searchResults = new ArrayList<T>(); } } public static class MapIndex extends BinaryIndexPart { List<MapRoot> roots = new ArrayList<MapRoot>(); Map<String, Map<String, Integer>> encodingRules = new LinkedHashMap<String, Map<String, Integer>>(); Map<Integer, TagValuePair> decodingRules = new LinkedHashMap<Integer, TagValuePair>(); public List<MapRoot> getRoots() { return roots; } public TagValuePair decodeType(int type, int subtype){ return decodingRules.get(((subtype << 5) | type)); } public TagValuePair decodeType(int wholeType){ if((wholeType & 3) != MapRenderingTypes.POINT_TYPE ){ wholeType = (wholeType >> 2) & MapRenderingTypes.MASK_10; } else { wholeType >>= 2; } return decodingRules.get(wholeType); } } public static class TagValuePair { public String tag; public String value; public int additionalAttribute; public TagValuePair(String tag, String value) { super(); this.tag = tag; this.value = value; } public TagValuePair(String tag, String value, int additionalAttribute) { super(); this.tag = tag; this.value = value; this.additionalAttribute = additionalAttribute; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + additionalAttribute; result = prime * result + ((tag == null) ? 0 : tag.hashCode()); result = prime * result + ((value == null) ? 0 : value.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; TagValuePair other = (TagValuePair) obj; if (additionalAttribute != other.additionalAttribute) return false; if (tag == null) { if (other.tag != null) return false; } else if (!tag.equals(other.tag)) return false; if (value == null) { if (other.value != null) return false; } else if (!value.equals(other.value)) return false; return true; } } public static class MapRoot extends BinaryIndexPart { int minZoom = 0; int maxZoom = 0; int left = 0; int right = 0; int top = 0; int bottom = 0; public int getMinZoom() { return minZoom; } public int getMaxZoom() { return maxZoom; } public int getLeft() { return left; } public int getRight() { return right; } public int getTop() { return top; } public int getBottom() { return bottom; } List<MapTree> trees = new ArrayList<MapTree>(); } private static class MapTree { int filePointer = 0; int length = 0; int left = 0; int right = 0; int top = 0; int bottom = 0; } public static void main(String[] args) throws IOException { RandomAccessFile raf = new RandomAccessFile(new File("/home/victor/projects/OsmAnd/download/384/Spain_europe_1.obf"), "r"); BinaryMapIndexReader reader = new BinaryMapIndexReader(raf); System.out.println("VERSION " + reader.getVersion()); //$NON-NLS-1$ long time = System.currentTimeMillis(); System.out.println(reader.mapIndexes.get(0).encodingRules); // test search // int sleft = MapUtils.get31TileNumberX(27.596); // int sright = MapUtils.get31TileNumberX(27.599); // int stop = MapUtils.get31TileNumberY(53.921); // int sbottom = MapUtils.get31TileNumberY(53.919); // System.out.println("SEARCH " + sleft + " " + sright + " " + stop + " " + sbottom); // // for (BinaryMapDataObject obj : reader.searchMapIndex(buildSearchRequest(sleft, sright, stop, sbottom, 8))) { // if (obj.getName() != null) { // System.out.println(" " + obj.getName()); // } // } // test address index search String reg = reader.getRegionNames().get(0); List<City> cs = reader.getCities(reg); for(City c : cs){ int buildings = 0; // reader.preloadStreets(c); // for(Street s : c.getStreets()){ // reader.preloadBuildings(s); // buildings += s.getBuildings().size(); // } System.out.println(c.getName() + " " + c.getLocation() + " " + c.getStreets().size() + " " + buildings + " " + c.getEnName()); } // List<PostCode> postcodes = reader.getPostcodes(reg); // for(PostCode c : postcodes){ // reader.preloadStreets(c); // System.out.println(c.getName()); // } // List<City> villages = reader.getVillages(reg, new StringMatcher() { // // @Override // public boolean matches(String name) { // return false; // } // }, true); // System.out.println("Villages " + villages.size()); // test transport // for(TransportIndex i : reader.transportIndexes){ // System.out.println(i.left + " " + i.right + " " + i.top + " " + i.bottom); // System.out.println(i.stringTable.cacheOfStrings); // System.out.println(i.stringTable.offsets); // System.out.println(i.stringTable.window); // } // { // int sleft = MapUtils.get31TileNumberX(27.573); // int sright = MapUtils.get31TileNumberX(27.581); // int stop = MapUtils.get31TileNumberY(53.912); // int sbottom = MapUtils.get31TileNumberY(53.908); // for (TransportStop s : reader.searchTransportIndex(buildSearchTransportRequest(sleft, sright, stop, sbottom, 15, null))) { // System.out.println(s.getName()); // for (int i : s.getReferencesToRoutes()) { // TransportRoute route = reader.getTransportRoute(i); // System.out.println(" " + route.getRef() + " " + route.getName() + " " + route.getDistance() + " " // + route.getAvgBothDistance()); // } // } // } // { // int sleft = MapUtils.get31TileNumberX(27.473); // int sright = MapUtils.get31TileNumberX(27.681); // int stop = MapUtils.get31TileNumberY(53.912); // int sbottom = MapUtils.get31TileNumberY(53.708); // for (TransportStop s : reader.searchTransportIndex(buildSearchTransportRequest(sleft, sright, stop, sbottom, 16, null))) { // System.out.println(s.getName()); // for (int i : s.getReferencesToRoutes()) { // TransportRoute route = reader.getTransportRoute(i); // System.out.println(" " + route.getRef() + " " + route.getName() + " " + route.getDistance() + " " // + route.getAvgBothDistance()); // } // } // } System.out.println("MEMORY " + (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory())); //$NON-NLS-1$ System.out.println("Time " + (System.currentTimeMillis() - time)); //$NON-NLS-1$ } }