package net.osmand.binary; import java.io.IOException; import java.util.List; import net.osmand.StringMatcher; import net.osmand.data.Building; import net.osmand.data.City; import net.osmand.data.PostCode; import net.osmand.data.Street; import net.osmand.data.City.CityType; import net.osmand.osm.LatLon; import net.osmand.osm.MapUtils; import net.sf.junidecode.Junidecode; import com.google.protobuf.CodedInputStreamRAF; import com.google.protobuf.WireFormat; public class BinaryMapAddressReaderAdapter { public static class AddressRegion extends BinaryIndexPart { String enName; int postcodesOffset = -1; int villagesOffset = -1; int citiesOffset = -1; } private CodedInputStreamRAF codedIS; private final BinaryMapIndexReader map; protected BinaryMapAddressReaderAdapter(BinaryMapIndexReader map){ this.codedIS = map.codedIS; this.map = map; } private void skipUnknownField(int t) throws IOException { map.skipUnknownField(t); } private int readInt() throws IOException { return map.readInt(); } protected void readAddressIndex(AddressRegion region) throws IOException { while(true){ int t = codedIS.readTag(); int tag = WireFormat.getTagFieldNumber(t); switch (tag) { case 0: if(region.enName == null || region.enName.length() == 0){ region.enName = Junidecode.unidecode(region.name); } return; case OsmandOdb.OsmAndAddressIndex.NAME_FIELD_NUMBER : region.name = codedIS.readString(); break; case OsmandOdb.OsmAndAddressIndex.NAME_EN_FIELD_NUMBER : region.enName = codedIS.readString(); break; case OsmandOdb.OsmAndAddressIndex.CITIES_FIELD_NUMBER : region.citiesOffset = codedIS.getTotalBytesRead(); int length = readInt(); codedIS.seek(region.citiesOffset + length + 4); break; case OsmandOdb.OsmAndAddressIndex.VILLAGES_FIELD_NUMBER : region.villagesOffset = codedIS.getTotalBytesRead(); length = readInt(); codedIS.seek(region.villagesOffset + length + 4); break; case OsmandOdb.OsmAndAddressIndex.POSTCODES_FIELD_NUMBER : region.postcodesOffset = codedIS.getTotalBytesRead(); length = readInt(); codedIS.seek(region.postcodesOffset + length + 4); break; default: skipUnknownField(t); break; } } } protected void readCities(List<City> cities, StringMatcher matcher, boolean useEn) throws IOException { while(true){ int t = codedIS.readTag(); int tag = WireFormat.getTagFieldNumber(t); switch (tag) { case 0: return; case OsmandOdb.CitiesIndex.CITIES_FIELD_NUMBER : int offset = codedIS.getTotalBytesRead(); int length = codedIS.readRawVarint32(); int oldLimit = codedIS.pushLimit(length); City c = readCity(null, offset, false, matcher, useEn); if(c != null){ cities.add(c); } codedIS.popLimit(oldLimit); break; default: skipUnknownField(t); break; } } } protected PostCode readPostcode(PostCode p, int fileOffset, boolean loadStreets, String postcodeFilter) throws IOException{ int x = 0; int y = 0; while(true){ int t = codedIS.readTag(); int tag = WireFormat.getTagFieldNumber(t); switch (tag) { case 0: p.setLocation(MapUtils.get31LatitudeY(y), MapUtils.get31LongitudeX(x)); p.setFileOffset(fileOffset); return p; case OsmandOdb.PostcodeIndex.POSTCODE_FIELD_NUMBER : String name = codedIS.readString(); if(postcodeFilter != null && !postcodeFilter.equalsIgnoreCase(name)){ codedIS.skipRawBytes(codedIS.getBytesUntilLimit()); return null; } if(p == null){ p = new PostCode(name); } p.setName(name); break; case OsmandOdb.PostcodeIndex.X_FIELD_NUMBER : x = codedIS.readFixed32(); break; case OsmandOdb.PostcodeIndex.Y_FIELD_NUMBER : y = codedIS.readFixed32(); break; case OsmandOdb.PostcodeIndex.STREETS_FIELD_NUMBER : int offset = codedIS.getTotalBytesRead(); int length = codedIS.readRawVarint32(); if(loadStreets){ Street s = new Street(null); int oldLimit = codedIS.pushLimit(length); s.setFileOffset(offset); readStreet(s, true, x >> 7, y >> 7, p.getName()); p.registerStreet(s, false); codedIS.popLimit(oldLimit); } else { codedIS.skipRawBytes(length); } break; default: skipUnknownField(t); break; } } } protected City readCity(City c, int fileOffset, boolean loadStreets, StringMatcher nameMatcher, boolean useEn) throws IOException{ int x = 0; int y = 0; int streetInd = 0; while(true){ int t = codedIS.readTag(); int tag = WireFormat.getTagFieldNumber(t); boolean englishNameMatched = false; switch (tag) { case 0: c.setLocation(MapUtils.get31LatitudeY(y), MapUtils.get31LongitudeX(x)); if(c.getEnName().length() == 0){ c.setEnName(Junidecode.unidecode(c.getName())); } return c; case OsmandOdb.CityIndex.CITY_TYPE_FIELD_NUMBER : int type = codedIS.readUInt32(); if(c == null){ c = new City(CityType.values()[type]); c.setFileOffset(fileOffset); } break; case OsmandOdb.CityIndex.ID_FIELD_NUMBER : c.setId(codedIS.readUInt64()); if(nameMatcher != null && useEn && !englishNameMatched){ codedIS.skipRawBytes(codedIS.getBytesUntilLimit()); return null; } break; case OsmandOdb.CityIndex.NAME_EN_FIELD_NUMBER : String enName = codedIS.readString(); if (nameMatcher != null && enName.length() > 0) { if(!nameMatcher.matches(enName)){ codedIS.skipRawBytes(codedIS.getBytesUntilLimit()); return null; } else { englishNameMatched = true; } } c.setEnName(enName); break; case OsmandOdb.CityIndex.NAME_FIELD_NUMBER : String name = codedIS.readString(); c.setName(name); if(nameMatcher != null){ if(!useEn){ if(!nameMatcher.matches(name)) { codedIS.skipRawBytes(codedIS.getBytesUntilLimit()); return null; } } else { if(nameMatcher.matches(Junidecode.unidecode(name))){ englishNameMatched = true; } } } break; case OsmandOdb.CityIndex.X_FIELD_NUMBER : x = codedIS.readFixed32(); break; case OsmandOdb.CityIndex.Y_FIELD_NUMBER : y = codedIS.readFixed32(); break; case OsmandOdb.CityIndex.INTERSECTIONS_FIELD_NUMBER : codedIS.skipRawBytes(codedIS.readRawVarint32()); break; case OsmandOdb.CityIndex.STREETS_FIELD_NUMBER : int offset = codedIS.getTotalBytesRead(); int length = codedIS.readRawVarint32(); if(loadStreets){ Street s = new Street(c); int oldLimit = codedIS.pushLimit(length); s.setFileOffset(offset); s.setIndexInCity(streetInd++); readStreet(s, false, x >> 7, y >> 7, null); c.registerStreet(s); codedIS.popLimit(oldLimit); } else { codedIS.skipRawBytes(length); } break; default: skipUnknownField(t); break; } } } protected Street readStreet(Street s, boolean loadBuildings, int city24X, int city24Y, String postcodeFilter) throws IOException{ int x = 0; int y = 0; boolean loadLocation = city24X != 0 || city24Y != 0; while(true){ int t = codedIS.readTag(); int tag = WireFormat.getTagFieldNumber(t); switch (tag) { case 0: if(loadLocation){ s.setLocation(MapUtils.getLatitudeFromTile(24, y), MapUtils.getLongitudeFromTile(24, x)); } if(s.getEnName().length() == 0){ s.setEnName(Junidecode.unidecode(s.getName())); } return s; case OsmandOdb.StreetIndex.ID_FIELD_NUMBER : s.setId(codedIS.readUInt64()); break; case OsmandOdb.StreetIndex.NAME_EN_FIELD_NUMBER : s.setEnName(codedIS.readString()); break; case OsmandOdb.StreetIndex.NAME_FIELD_NUMBER : s.setName(codedIS.readString()); break; case OsmandOdb.StreetIndex.X_FIELD_NUMBER : int sx = codedIS.readSInt32(); if(loadLocation){ x = sx + city24X; } else { x = (int) MapUtils.getTileNumberX(24, s.getLocation().getLongitude()); } break; case OsmandOdb.StreetIndex.Y_FIELD_NUMBER : int sy = codedIS.readSInt32(); if(loadLocation){ y = sy + city24Y; } else { y = (int) MapUtils.getTileNumberY(24, s.getLocation().getLatitude()); } break; case OsmandOdb.StreetIndex.BUILDINGS_FIELD_NUMBER : int offset = codedIS.getTotalBytesRead(); int length = codedIS.readRawVarint32(); if(loadBuildings){ int oldLimit = codedIS.pushLimit(length); Building b = readBuilding(offset, x, y); if (postcodeFilter == null || postcodeFilter.equalsIgnoreCase(b.getPostcode())) { s.registerBuilding(b); } codedIS.popLimit(oldLimit); } else { codedIS.skipRawBytes(length); } break; default: skipUnknownField(t); break; } } } protected Building readBuilding(int fileOffset, int street24X, int street24Y) throws IOException{ int x = 0; int y = 0; Building b = new Building(); b.setFileOffset(fileOffset); while(true){ int t = codedIS.readTag(); int tag = WireFormat.getTagFieldNumber(t); switch (tag) { case 0: b.setLocation(MapUtils.getLatitudeFromTile(24, y), MapUtils.getLongitudeFromTile(24, x)); if(b.getEnName().length() == 0){ b.setEnName(Junidecode.unidecode(b.getName())); } return b; case OsmandOdb.BuildingIndex.ID_FIELD_NUMBER : b.setId(codedIS.readUInt64()); break; case OsmandOdb.BuildingIndex.NAME_EN_FIELD_NUMBER : b.setEnName(codedIS.readString()); break; case OsmandOdb.BuildingIndex.NAME_FIELD_NUMBER : b.setName(codedIS.readString()); break; case OsmandOdb.BuildingIndex.X_FIELD_NUMBER : x = codedIS.readSInt32() + street24X; break; case OsmandOdb.BuildingIndex.Y_FIELD_NUMBER : y = codedIS.readSInt32() + street24Y; break; case OsmandOdb.BuildingIndex.POSTCODE_FIELD_NUMBER : b.setPostcode(codedIS.readString()); break; default: skipUnknownField(t); break; } } } // 2 different quires : s2 == null -> fill possible streets, s2 != null return LatLon intersection private LatLon readIntersectedStreets(Street[] cityStreets, Street s, Street s2, LatLon parent, List<Street> streets) throws IOException { int size = codedIS.readRawVarint32(); int old = codedIS.pushLimit(size); boolean e = false; while(!e){ int t = codedIS.readTag(); int tag = WireFormat.getTagFieldNumber(t); switch (tag) { case 0: e = true; break; case OsmandOdb.InteresectedStreets.INTERSECTIONS_FIELD_NUMBER: int nsize = codedIS.readRawVarint32(); int nold = codedIS.pushLimit(nsize); int st1 = -1; int st2 = -1; int cx = 0; int cy = 0; boolean end = false; while (!end) { int nt = codedIS.readTag(); int ntag = WireFormat.getTagFieldNumber(nt); switch (ntag) { case 0: end = true; break; case OsmandOdb.StreetIntersection.INTERSECTEDSTREET1_FIELD_NUMBER: st1 = codedIS.readUInt32(); break; case OsmandOdb.StreetIntersection.INTERSECTEDSTREET2_FIELD_NUMBER: st2 = codedIS.readUInt32(); break; case OsmandOdb.StreetIntersection.INTERSECTEDX_FIELD_NUMBER: cx = codedIS.readSInt32(); break; case OsmandOdb.StreetIntersection.INTERSECTEDY_FIELD_NUMBER: cy = codedIS.readSInt32(); break; default: skipUnknownField(nt); } } codedIS.popLimit(nold); if (s2 == null) { // find all intersections if (st1 == s.getIndexInCity() && st2 != -1 && st2 < cityStreets.length && cityStreets[st2] != null) { streets.add(cityStreets[st2]); } else if (st2 == s.getIndexInCity() && st1 != -1 && st1 < cityStreets.length && cityStreets[st1] != null) { streets.add(cityStreets[st1]); } } else { if((st1 == s.getIndexInCity() && st2 == s2.getIndexInCity() ) || (st2 == s.getIndexInCity() && st1 == s2.getIndexInCity())) { int x = (int) (MapUtils.getTileNumberX(24, parent.getLongitude()) + cx); int y = (int) (MapUtils.getTileNumberY(24, parent.getLatitude()) + cy); codedIS.popLimit(old); return new LatLon(MapUtils.getLatitudeFromTile(24, y), MapUtils.getLongitudeFromTile(24, x)); } } break; default: skipUnknownField(t); } } codedIS.popLimit(old); return null; } // do not preload streets in city protected LatLon findIntersectedStreets(City c, Street s, Street s2, List<Street> streets) throws IOException { if(s.getIndexInCity() == -1){ return null; } codedIS.seek(c.getFileOffset()); int size = codedIS.readRawVarint32(); int old = codedIS.pushLimit(size); while(true){ int t = codedIS.readTag(); int tag = WireFormat.getTagFieldNumber(t); switch (tag) { case 0: codedIS.popLimit(old); return null; case OsmandOdb.CityIndex.INTERSECTIONS_FIELD_NUMBER : Street[] cityStreets = new Street[c.getStreets().size()]; for(Street st : c.getStreets()){ if(st.getIndexInCity() >= 0 && st.getIndexInCity() < cityStreets.length){ cityStreets[st.getIndexInCity()] = st; } } LatLon ret = readIntersectedStreets(cityStreets, s, s2, c.getLocation(), streets); codedIS.popLimit(old); return ret; default: skipUnknownField(t); } } } void readPostcodes(List<PostCode> postcodes) throws IOException{ while(true){ int t = codedIS.readTag(); int tag = WireFormat.getTagFieldNumber(t); switch (tag) { case 0: return; case OsmandOdb.PostcodesIndex.POSTCODES_FIELD_NUMBER : int offset = codedIS.getTotalBytesRead(); int length = codedIS.readRawVarint32(); int oldLimit = codedIS.pushLimit(length); postcodes.add(readPostcode(null, offset, false, null)); codedIS.popLimit(oldLimit); break; default: skipUnknownField(t); break; } } } PostCode findPostcode(String name) throws IOException{ while(true){ int t = codedIS.readTag(); int tag = WireFormat.getTagFieldNumber(t); switch (tag) { case 0: return null; case OsmandOdb.PostcodesIndex.POSTCODES_FIELD_NUMBER : int offset = codedIS.getTotalBytesRead(); int length = codedIS.readRawVarint32(); int oldLimit = codedIS.pushLimit(length); PostCode p = readPostcode(null, offset, true, name); codedIS.popLimit(oldLimit); if(p != null){ return p; } break; default: skipUnknownField(t); break; } } } }