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