Merge pull request #2661 from osmandapp/faster-search
Improve speed and usability of address search
This commit is contained in:
commit
52b7a8e502
9 changed files with 370 additions and 141 deletions
|
@ -1,9 +1,12 @@
|
|||
package net.osmand.binary;
|
||||
|
||||
import gnu.trove.list.array.TIntArrayList;
|
||||
import gnu.trove.set.TIntSet;
|
||||
import gnu.trove.set.hash.TIntHashSet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -22,6 +25,7 @@ import net.osmand.data.City;
|
|||
import net.osmand.data.City.CityType;
|
||||
import net.osmand.data.LatLon;
|
||||
import net.osmand.data.MapObject;
|
||||
import net.osmand.data.Postcode;
|
||||
import net.osmand.data.Street;
|
||||
import net.osmand.util.MapUtils;
|
||||
import net.sf.junidecode.Junidecode;
|
||||
|
@ -40,7 +44,7 @@ public class BinaryMapAddressReaderAdapter {
|
|||
public final static int STREET_TYPE = 4;
|
||||
|
||||
private static final Log LOG = PlatformUtil.getLog(BinaryMapAddressReaderAdapter.class);
|
||||
public final static int[] TYPES = { CITY_TOWN_TYPE, POSTCODES_TYPE, VILLAGES_TYPE, STREET_TYPE };
|
||||
public final static List<Integer> TYPES = Arrays.asList(CITY_TOWN_TYPE, POSTCODES_TYPE, VILLAGES_TYPE, STREET_TYPE);
|
||||
public final static int[] CITY_TYPES = { CITY_TOWN_TYPE, POSTCODES_TYPE, VILLAGES_TYPE };
|
||||
|
||||
public static class AddressRegion extends BinaryIndexPart {
|
||||
|
@ -175,8 +179,8 @@ public class BinaryMapAddressReaderAdapter {
|
|||
int fp = codedIS.getTotalBytesRead();
|
||||
int length = codedIS.readRawVarint32();
|
||||
int oldLimit = codedIS.pushLimit(length);
|
||||
City c = readCityHeader(matcher, fp, additionalTagsTable);
|
||||
if(c != null){
|
||||
City c = readCityHeader(new DefaultCityMatcher(matcher), fp, additionalTagsTable);
|
||||
if (c != null) {
|
||||
if (resultMatcher == null || resultMatcher.publish(c)) {
|
||||
cities.add(c);
|
||||
}
|
||||
|
@ -227,8 +231,37 @@ public class BinaryMapAddressReaderAdapter {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface CityMatcher {
|
||||
boolean matches(City city);
|
||||
}
|
||||
|
||||
private class DefaultCityMatcher implements CityMatcher {
|
||||
private StringMatcher stringMatcher = null;
|
||||
|
||||
DefaultCityMatcher(StringMatcher stringMatcher) {
|
||||
this.stringMatcher = stringMatcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(City city) {
|
||||
if (stringMatcher == null) {
|
||||
return true;
|
||||
}
|
||||
boolean matches = stringMatcher.matches(city.getName());
|
||||
if (!matches) {
|
||||
for (String n : city.getAllNames()) {
|
||||
matches = stringMatcher.matches(n);
|
||||
if (matches) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
}
|
||||
|
||||
protected City readCityHeader(StringMatcher matcher, int filePointer, List<String> additionalTagsTable) throws IOException{
|
||||
protected City readCityHeader(CityMatcher matcher, int filePointer, List<String> additionalTagsTable) throws IOException{
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
City c = null;
|
||||
|
@ -238,21 +271,7 @@ public class BinaryMapAddressReaderAdapter {
|
|||
int tag = WireFormat.getTagFieldNumber(t);
|
||||
switch (tag) {
|
||||
case 0:
|
||||
if(matcher != null) {
|
||||
boolean matches = matcher.matches(c.getName());
|
||||
if(!matches) {
|
||||
for(String n : c.getAllNames()) {
|
||||
matches = matcher.matches(n);
|
||||
if(matches) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!matches ) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return c;
|
||||
return (matcher == null || matcher.matches(c)) ? c : null;
|
||||
case OsmandOdb.CityIndex.CITY_TYPE_FIELD_NUMBER :
|
||||
int type = codedIS.readUInt32();
|
||||
c = new City(CityType.values()[type]);
|
||||
|
@ -536,9 +555,18 @@ public class BinaryMapAddressReaderAdapter {
|
|||
}
|
||||
}
|
||||
|
||||
public void searchAddressDataByName(AddressRegion reg, SearchRequest<MapObject> req, int[] typeFilter) throws IOException {
|
||||
public void searchAddressDataByName(AddressRegion reg, SearchRequest<MapObject> req, List<Integer> typeFilter) throws IOException {
|
||||
TIntArrayList loffsets = new TIntArrayList();
|
||||
CollatorStringMatcher matcher = new CollatorStringMatcher( req.nameQuery, StringMatcherMode.CHECK_STARTS_FROM_SPACE);
|
||||
CollatorStringMatcher stringMatcher = new CollatorStringMatcher(req.nameQuery, StringMatcherMode.CHECK_STARTS_FROM_SPACE);
|
||||
String postcode = Postcode.normalize(req.nameQuery, map.getCountryName());
|
||||
final CityMatcher postcodeMatcher = new DefaultCityMatcher(new CollatorStringMatcher(postcode, StringMatcherMode.CHECK_STARTS_FROM_SPACE));
|
||||
final CityMatcher cityMatcher = new DefaultCityMatcher(stringMatcher);
|
||||
final CityMatcher cityPostcodeMatcher = new CityMatcher() {
|
||||
@Override
|
||||
public boolean matches(City city) {
|
||||
return city.isPostcode() ? postcodeMatcher.matches(city) : cityMatcher.matches(city);
|
||||
}
|
||||
};
|
||||
long time = System.currentTimeMillis();
|
||||
int indexOffset = 0;
|
||||
while (true) {
|
||||
|
@ -555,7 +583,7 @@ public class BinaryMapAddressReaderAdapter {
|
|||
indexOffset = codedIS.getTotalBytesRead();
|
||||
int oldLimit = codedIS.pushLimit(length);
|
||||
// here offsets are sorted by distance
|
||||
map.readIndexedStringTable(matcher.getCollator(), req.nameQuery, "", loffsets, 0);
|
||||
map.readIndexedStringTable(stringMatcher.getCollator(), req.nameQuery, "", loffsets, 0);
|
||||
codedIS.popLimit(oldLimit);
|
||||
break;
|
||||
case OsmAndAddressNameIndexData.ATOM_FIELD_NUMBER:
|
||||
|
@ -593,13 +621,13 @@ public class BinaryMapAddressReaderAdapter {
|
|||
}
|
||||
}
|
||||
if (typeFilter == null) {
|
||||
typeFilter = new int[] { CITY_TOWN_TYPE, POSTCODES_TYPE, VILLAGES_TYPE, STREET_TYPE };
|
||||
typeFilter = TYPES;
|
||||
}
|
||||
for (int i = 0; i < typeFilter.length && !req.isCancelled(); i++) {
|
||||
TIntArrayList list = refs[typeFilter[i]];
|
||||
if (typeFilter[i] == STREET_TYPE) {
|
||||
for (int i = 0; i < typeFilter.size() && !req.isCancelled(); i++) {
|
||||
TIntArrayList list = refs[typeFilter.get(i)];
|
||||
if (typeFilter.get(i) == STREET_TYPE) {
|
||||
for (int j = 0; j < list.size() && !req.isCancelled(); j += 2) {
|
||||
City obj = null;
|
||||
City obj;
|
||||
{
|
||||
codedIS.seek(list.get(j + 1));
|
||||
int len = codedIS.readRawVarint32();
|
||||
|
@ -617,11 +645,11 @@ public class BinaryMapAddressReaderAdapter {
|
|||
readStreet(s, null, false, MapUtils.get31TileNumberX(l.getLongitude()) >> 7,
|
||||
MapUtils.get31TileNumberY(l.getLatitude()) >> 7, obj.isPostcode() ? obj.getName() : null,
|
||||
reg.attributeTagsTable);
|
||||
boolean matches = matcher.matches(s.getName());
|
||||
if(!matches) {
|
||||
for(String n : s.getAllNames()) {
|
||||
matches = matcher.matches(n);
|
||||
if(matches) {
|
||||
boolean matches = stringMatcher.matches(s.getName());
|
||||
if (!matches) {
|
||||
for (String n : s.getAllNames()) {
|
||||
matches = stringMatcher.matches(n);
|
||||
if (matches) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -634,13 +662,16 @@ public class BinaryMapAddressReaderAdapter {
|
|||
}
|
||||
} else {
|
||||
list.sort();
|
||||
TIntSet published = new TIntHashSet();
|
||||
for (int j = 0; j < list.size() && !req.isCancelled(); j++) {
|
||||
codedIS.seek(list.get(j));
|
||||
int offset = list.get(j);
|
||||
codedIS.seek(offset);
|
||||
int len = codedIS.readRawVarint32();
|
||||
int old = codedIS.pushLimit(len);
|
||||
City obj = readCityHeader(matcher, list.get(j), reg.attributeTagsTable);
|
||||
if (obj != null) {
|
||||
City obj = readCityHeader(cityPostcodeMatcher, list.get(j), reg.attributeTagsTable);
|
||||
if (obj != null && !published.contains(offset)) {
|
||||
req.publish(obj);
|
||||
published.add(offset);
|
||||
}
|
||||
codedIS.popLimit(old);
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import java.io.InputStreamReader;
|
|||
import java.io.RandomAccessFile;
|
||||
import java.io.Reader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
|
@ -384,6 +385,25 @@ public class BinaryMapIndexReader {
|
|||
return file;
|
||||
}
|
||||
|
||||
private List<String> getCountryAndRegionNames() {
|
||||
return new ArrayList<>(Arrays.asList(getRegionNames().get(0).split("_")));
|
||||
}
|
||||
|
||||
public String getCountryName() {
|
||||
return getCountryAndRegionNames().get(0);
|
||||
}
|
||||
|
||||
private String getRegionName() {
|
||||
List<String> names = getCountryAndRegionNames();
|
||||
if (names.size() >= 2) {
|
||||
String region = names.get(1);
|
||||
region = region.substring(0, 1).toUpperCase() + region.substring(1);
|
||||
return region;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public int readByte() throws IOException {
|
||||
byte b = codedIS.readRawByte();
|
||||
if (b < 0) {
|
||||
|
@ -1278,10 +1298,7 @@ public class BinaryMapIndexReader {
|
|||
return dataObject;
|
||||
}
|
||||
|
||||
public List<MapObject> searchAddressDataByName(SearchRequest<MapObject> req, int[] typeFilter) throws IOException {
|
||||
if (req.nameQuery == null || req.nameQuery.length() == 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
public List<MapObject> searchAddressDataByName(SearchRequest<MapObject> req, List<Integer> typeFilter) throws IOException {
|
||||
for (AddressRegion reg : addressIndexes) {
|
||||
if (reg.indexNameOffset != -1) {
|
||||
codedIS.seek(reg.indexNameOffset);
|
||||
|
|
|
@ -1,13 +1,8 @@
|
|||
package net.osmand.data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import net.osmand.OsmAndCollator;
|
||||
import net.osmand.util.Algorithms;
|
||||
|
||||
public class City extends MapObject {
|
||||
public enum CityType {
|
||||
|
|
208
OsmAnd-java/src/net/osmand/data/Postcode.java
Normal file
208
OsmAnd-java/src/net/osmand/data/Postcode.java
Normal file
|
@ -0,0 +1,208 @@
|
|||
package net.osmand.data;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import net.osmand.PlatformUtil;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
|
||||
public class Postcode {
|
||||
private final static Log log = PlatformUtil.getLog(Postcode.class);
|
||||
// © CC BY 3.0 2016 GeoNames.org
|
||||
// with adaptations
|
||||
private final static Map<String, List<String>> rules = new TreeMap<String, List<String>>() {{
|
||||
put("Algeria", Arrays.asList("(?i)(?:DZ-?)?(\\d{5})", "$1"));
|
||||
put("Andorra", Arrays.asList("(?i)(?:AD-?)?(\\d{3})", "$1"));
|
||||
put("Argentina", Arrays.asList("(?i)(?:AR-?)?([A-Z]\\d{4}[A-Z]{3}|\\d{4})", "$1"));
|
||||
put("Armenia", Arrays.asList("(?i)(?:AM-?)?(\\d{6})", "$1"));
|
||||
put("Australia-oceania", Arrays.asList("(?i)(?:AU-?)?(\\d{4})", "$1"));
|
||||
put("Austria", Arrays.asList("(?i)(?:AT-?)?(\\d{4})", "$1"));
|
||||
put("Azerbaijan", Arrays.asList("(?i)(?:AZ-?)?(\\d{4})", "$1"));
|
||||
put("Bahrain", Arrays.asList("(?i)(?:BH-?)?(\\d{3}\\d?)", "$1"));
|
||||
put("Bangladesh", Arrays.asList("(?i)(?:BD-?)?(\\d{4})", "$1"));
|
||||
put("Barbados", Arrays.asList("(?i)(?:BB-?)?(\\d{5})", "$1"));
|
||||
put("Belarus", Arrays.asList("(?i)(?:BY-?)?(\\d{6})", "$1"));
|
||||
put("Belgium", Arrays.asList("(?i)(?:BE-?)?(\\d{4})", "$1"));
|
||||
put("Bermuda", Arrays.asList("(?i)(?:BM-?)?([A-Z]{2})\\W*(\\d{2})", "$1$2"));
|
||||
put("Bosnia-herzegovina", Arrays.asList("(?i)(?:BA-?)?(\\d{5})", "$1"));
|
||||
put("Brazil", Arrays.asList("(?i)(?:BR-?)?(\\d{5})\\W*(\\d{3})", "$1-$2"));
|
||||
put("Brunei", Arrays.asList("(?i)(?:BN-?)?([A-Z]{2})\\W*(\\d{4})", "$1$2"));
|
||||
put("Bulgaria", Arrays.asList("(?i)(?:BG-?)?(\\d{4})", "$1"));
|
||||
put("Cambodia", Arrays.asList("(?i)(?:KH-?)?(\\d{5})", "$1"));
|
||||
put("Canada", Arrays.asList("(?i)(?:CA-?)?([ABCEGHJKLMNPRSTVXY]\\d[ABCEGHJKLMNPRSTVWXYZ])\\W*(\\d[ABCEGHJKLMNPRSTVWXYZ]\\d)$", "$1 $2"));
|
||||
put("Cape-verde", Arrays.asList("(?i)(?:CV-?)?(\\d{4})", "$1"));
|
||||
put("Chile", Arrays.asList("(?i)(?:CL-?)?(\\d{7})", "$1"));
|
||||
put("China", Arrays.asList("(?i)(?:CN-?)?(\\d{6})", "$1"));
|
||||
put("Christmas-island", Arrays.asList("(?i)(?:CX-?)?(\\d{4})", "$1"));
|
||||
put("Costa-rica", Arrays.asList("(?i)(?:CR-?)?(\\d{4})", "$1"));
|
||||
put("Croatia", Arrays.asList("(?i)(?:HR-?)?(\\d{5})", "$1"));
|
||||
put("Cuba", Arrays.asList("(?i)(?:C[PU]-?)?(\\d{5})", "$1"));
|
||||
put("Cyprus", Arrays.asList("(?i)(?:CY-?)?(\\d{4})", "$1"));
|
||||
put("Czech-republic", Arrays.asList("(?i)(?:CZ-?)?(\\d{5})", "$1"));
|
||||
put("Denmark", Arrays.asList("(?i)(?:DK-?)?(\\d{4})", "$1"));
|
||||
put("Dominican-republic", Arrays.asList("(?i)(?:DO-?)?(\\d{5})", "$1"));
|
||||
put("Ecuador", Arrays.asList("(?i)(?:EC-?)?(\\d{6})", "$1"));
|
||||
put("Egypt", Arrays.asList("(?i)(?:EG-?)?(\\d{5})", "$1"));
|
||||
put("El-salvador", Arrays.asList("(?i)(?:SV-?)?(\\d{4})", "$1"));
|
||||
put("Estonia", Arrays.asList("(?i)(?:EE-?)?(\\d{5})", "$1"));
|
||||
put("Ethiopia", Arrays.asList("(?i)(?:ET-?)?(\\d{4})", "$1"));
|
||||
put("Faroe-islands", Arrays.asList("(?i)(?:FO-?)?(\\d{3})", "$1"));
|
||||
put("Finland", Arrays.asList("(?i)(?:FI-?)?(\\d{5})", "$1"));
|
||||
put("France", Arrays.asList("(?i)(?:FR-?)?(\\d{5})", "$1"));
|
||||
put("French-guiana", Arrays.asList("(?i)(?:GF-?)?((97|98)3\\d{2})", "$1"));
|
||||
put("French-southern-and-antarctic-lands", Arrays.asList("(?i)(?:PF-?)?((97|98)7\\d{2})", "$1"));
|
||||
put("GB", Arrays.asList("(?i)(?:UK-?)?([A-Z]{1,2}[0-9]{1,2}[A-Z]?)\\W*([0-9][A-Z]{2})", "$1 $2"));
|
||||
put("Georgia", Arrays.asList("(?i)(?:GE-?)?(\\d{4})", "$1"));
|
||||
put("Germany", Arrays.asList("(?i)(?:DE-?)?(\\d{5})", "$1"));
|
||||
put("Greece", Arrays.asList("(?i)(?:GR-?)?(\\d{5})", "$1"));
|
||||
put("Greenland", Arrays.asList("(?i)(?:GL-?)?(\\d{4})", "$1"));
|
||||
put("Guadeloupe", Arrays.asList("(?i)(?:GP-?)?((97|98)\\d{3})", "$1"));
|
||||
put("Guatemala", Arrays.asList("(?i)(?:GT-?)?(\\d{5})", "$1"));
|
||||
put("Guinea-bissau", Arrays.asList("(?i)(?:GW-?)?(\\d{4})", "$1"));
|
||||
put("Haiti", Arrays.asList("(?i)(?:HT-?)?(\\d{4})", "$1"));
|
||||
put("Honduras", Arrays.asList("(?i)(?:HN-?)?([A-Z]{2})\\W*(\\d{4}))", "$1$2"));
|
||||
put("Hungary", Arrays.asList("(?i)(?:HU-?)?(\\d{4})", "$1"));
|
||||
put("Iceland", Arrays.asList("(?i)(?:IS-?)?(\\d{3})", "$1"));
|
||||
put("India", Arrays.asList("(?i)(?:IN-?)?(\\d{6})", "$1"));
|
||||
put("Indonesia", Arrays.asList("(?i)(?:ID-?)?(\\d{5})", "$1"));
|
||||
put("Iran", Arrays.asList("(?i)(?:IR-?)?(\\d{10})", "$1"));
|
||||
put("Iraq", Arrays.asList("(?i)(?:IQ-?)?(\\d{5})", "$1"));
|
||||
// put("Ireland", Arrays.asList("(?i)(?:IE-?)?([A-Z]{3}[A-Z]{4})", "$1")); // It's complicated
|
||||
put("Israel", Arrays.asList("(?i)(?:IL-?)?(\\d{5})", "$1"));
|
||||
put("Italy", Arrays.asList("(?i)(?:IT-?)?(\\d{5})", "$1"));
|
||||
put("Japan", Arrays.asList("(?i)(?:JP-?)?(\\d{7})", "$1"));
|
||||
put("Jordan", Arrays.asList("(?i)(?:JO-?)?(\\d{5})", "$1"));
|
||||
put("Kazakhstan", Arrays.asList("(?i)(?:KZ-?)?(\\d{6})", "$1"));
|
||||
put("Kenya", Arrays.asList("(?i)(?:KE-?)?(\\d{5})", "$1"));
|
||||
put("Kuwait", Arrays.asList("(?i)(?:KW-?)?(\\d{5})", "$1"));
|
||||
put("Kyrgyzstan", Arrays.asList("(?i)(?:KG-?)?(\\d{6})", "$1"));
|
||||
put("Laos", Arrays.asList("(?i)(?:LA-?)?(\\d{5})", "$1"));
|
||||
put("Latvia", Arrays.asList("(?i)(?:LV-?)?(\\d{4})", "$1"));
|
||||
put("Lebanon", Arrays.asList("(?i)(?:LB-?)?(\\d{4}(\\d{4})?)", "$1"));
|
||||
put("Lesotho", Arrays.asList("(?i)(?:LS-?)?(\\d{3})", "$1"));
|
||||
put("Liberia", Arrays.asList("(?i)(?:LR-?)?(\\d{4})", "$1"));
|
||||
put("Liechtenstein", Arrays.asList("(?i)(?:LI-?)?(\\d{4})", "$1"));
|
||||
put("Lithuania", Arrays.asList("(?i)(?:LT-?)?(\\d{5})", "$1"));
|
||||
put("Luxembourg", Arrays.asList("(?i)(?:LU-?)?(\\d{4})", "$1"));
|
||||
put("Macedonia", Arrays.asList("(?i)(?:MK-?)?(\\d{4})", "$1"));
|
||||
put("Madagascar", Arrays.asList("(?i)(?:MG-?)?(\\d{3})", "$1"));
|
||||
put("Malaysia", Arrays.asList("(?i)(?:MY-?)?(\\d{5})", "$1"));
|
||||
put("Maldives", Arrays.asList("(?i)(?:MV-?)?(\\d{5})", "$1"));
|
||||
put("Malta", Arrays.asList("(?i)(?:MT-?)?([A-Z]{3})\\W*(\\d{4})", "$1 $2"));
|
||||
put("Martinique", Arrays.asList("(?i)(?:MQ-?)?(\\d{5})", "$1"));
|
||||
put("Mayotte", Arrays.asList("(?i)(?:YT-?)?(\\d{5})", "$1"));
|
||||
put("Mexico", Arrays.asList("(?i)(?:MX-?)?(\\d{5})", "$1"));
|
||||
put("Moldova", Arrays.asList("(?i)(?:MD-?)?(\\d{4})", "$1"));
|
||||
put("Monaco", Arrays.asList("(?i)(?:MC-?)?(\\d{5})", "$1"));
|
||||
put("Mongolia", Arrays.asList("(?i)(?:MN-?)?(\\d{6})", "$1"));
|
||||
put("Montenegro", Arrays.asList("(?i)(?:ME-?)?(\\d{5})", "$1"));
|
||||
put("Morocco", Arrays.asList("(?i)(?:MA-?)?(\\d{5})", "$1"));
|
||||
put("Mozambique", Arrays.asList("(?i)(?:MZ-?)?(\\d{4})", "$1"));
|
||||
put("Myanmar", Arrays.asList("(?i)(?:MM-?)?(\\d{5})", "$1"));
|
||||
put("Nepal", Arrays.asList("(?i)(?:NP-?)?(\\d{5})", "$1"));
|
||||
put("Netherlands", Arrays.asList("(?i)(?:NL-?)?(\\d{4})\\W*([A-Z]{2})", "$1$2"));
|
||||
put("New-zealand", Arrays.asList("(?i)(?:NZ-?)?(\\d{4})", "$1"));
|
||||
put("Nicaragua", Arrays.asList("(?i)(?:NI-?)?(\\d{7})", "$1"));
|
||||
put("Niger", Arrays.asList("(?i)(?:NE-?)?(\\d{4})", "$1"));
|
||||
put("Nigeria", Arrays.asList("(?i)(?:NG-?)?(\\d{6})", "$1"));
|
||||
put("North-korea", Arrays.asList("(?i)(?:KP-?)?(\\d{6})", "$1"));
|
||||
put("Norway", Arrays.asList("(?i)(?:NO-?)?(\\d{4})", "$1"));
|
||||
put("Oman", Arrays.asList("(?i)(?:OM-?)?(\\d{3})", "$1"));
|
||||
put("Pakistan", Arrays.asList("(?i)(?:PK-?)?(\\d{5})", "$1"));
|
||||
put("Papua-new-guinea", Arrays.asList("(?i)(?:PG-?)?(\\d{3})", "$1"));
|
||||
put("Paraguay", Arrays.asList("(?i)(?:PY-?)?(\\d{4})", "$1"));
|
||||
put("Philippines", Arrays.asList("(?i)(?:PH-?)?(\\d{4})", "$1"));
|
||||
put("Poland", Arrays.asList("(?i)(?:PL-?)?(\\d{5})", "$1"));
|
||||
put("Portugal", Arrays.asList("(?i)(?:PT-?)?(\\d{7})", "$1"));
|
||||
put("Puerto-rico", Arrays.asList("(?i)(?:PR-?)?(\\d{9})", "$1"));
|
||||
put("Reunion", Arrays.asList("(?i)(?:RE-?)?((97|98)(4|7|8)\\d{2})", "$1"));
|
||||
put("Romania", Arrays.asList("(?i)(?:RO-?)?(\\d{6})", "$1"));
|
||||
put("Russia", Arrays.asList("(?i)(?:RU-?)?(\\d{6})", "$1"));
|
||||
put("Saint-helena-ascension-and-tristan-da-cunha", Arrays.asList("(?i)(?:SH-?)?(STHL)\\W*(1ZZ)", "$1 $2"));
|
||||
put("Saint-pierre-and-miquelon", Arrays.asList("(?i)(?:PM-?)?(97500)", "$1"));
|
||||
put("San-marino", Arrays.asList("(?i)(?:SM-?)?(4789\\d)", "$1"));
|
||||
put("Saudi-arabia", Arrays.asList("(?i)(?:SA-?)?(\\d{5})", "$1"));
|
||||
put("Senegal", Arrays.asList("(?i)(?:SN-?)?(\\d{5})", "$1"));
|
||||
put("Serbia", Arrays.asList("(?i)(?:RS-?)?(\\d{6})", "$1"));
|
||||
put("Singapore", Arrays.asList("(?i)(?:SG-?)?(\\d{6})", "$1"));
|
||||
put("Slovakia", Arrays.asList("(?i)(?:SK-?)?(\\d{5})", "$1"));
|
||||
put("Slovenia", Arrays.asList("(?i)(?:SI-?)?(\\d{4})", "$1"));
|
||||
put("Somalia", Arrays.asList("(?i)(?:SO-?)?([A-Z]{2})\\W*(\\d{5})", "$1$2"));
|
||||
put("South-africa", Arrays.asList("(?i)(?:ZA-?)?(\\d{4})", "$1"));
|
||||
put("South-korea", Arrays.asList("(?i)(?:KR-?)?(?:SEOUL)?(\\d{3})\\W*(\\d{2,3})", "$1$2"));
|
||||
put("Spain", Arrays.asList("(?i)(?:ES-?)?(\\d{5})", "$1"));
|
||||
put("Sri-lanka", Arrays.asList("(?i)(?:LK-?)?(\\d{5})", "$1"));
|
||||
put("Sudan", Arrays.asList("(?i)(?:SD-?)?(\\d{5})", "$1"));
|
||||
put("Swaziland", Arrays.asList("(?i)(?:SZ-?)?([A-Z]\\d{3})", "$1"));
|
||||
put("Sweden", Arrays.asList("(?i)(?:SE-?)?(\\d{5})", "$1"));
|
||||
put("Switzerland", Arrays.asList("(?i)(?:CH-?)?(\\d{4})", "$1"));
|
||||
put("Taiwan", Arrays.asList("(?i)(?:TW-?)?(\\d{5})", "$1"));
|
||||
put("Tajikistan", Arrays.asList("(?i)(?:TJ-?)?(\\d{6})", "$1"));
|
||||
put("Thailand", Arrays.asList("(?i)(?:TH-?)?(\\d{5})", "$1"));
|
||||
put("Tunisia", Arrays.asList("(?i)(?:TN-?)?(\\d{4})", "$1"));
|
||||
put("Turkey", Arrays.asList("(?i)(?:TR-?)?(\\d{5})", "$1"));
|
||||
put("Turkmenistan", Arrays.asList("(?i)(?:TM-?)?(\\d{6})", "$1"));
|
||||
put("Turks-and-caicos-islands", Arrays.asList("(?i)(?:TC-?)?(TKCA)\\W*(1ZZ)", "$1 $2"));
|
||||
put("Virgin-islands-us", Arrays.asList("(?i)(?:VI-?)?(\\d{5})\\W*(-\\d{4})?", "$1$2"));
|
||||
put("Ukraine", Arrays.asList("(?i)(?:UA-?)?(\\d{2})\\W*(\\d{3})", "$1$2"));
|
||||
put("Us", Arrays.asList("(?i)(?:US-?)?(\\d{5})\\W*(-\\d{4})?", "$1$2"));
|
||||
put("Uruguay", Arrays.asList("(?i)(?:UY-?)?(\\d{5})", "$1"));
|
||||
put("Uzbekistan", Arrays.asList("(?i)(?:UZ-?)?(\\d{6})", "$1"));
|
||||
put("Venezuela", Arrays.asList("(?i)(?:VE-?)?(\\d{4})", "$1"));
|
||||
put("Vietnam", Arrays.asList("(?i)(?:VN-?)?(\\d{6})", "$1"));
|
||||
put("Zambia", Arrays.asList("(?i)(?:ZM-?)?(\\d{5})", "$1"));
|
||||
}};
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(normalize("1101 DL", "Netherlands"));
|
||||
System.out.println(normalize("1101-DL", "Netherlands"));
|
||||
System.out.println(normalize("b288qp", "United Kingdom"));
|
||||
System.out.println(normalize("GIR 0AA", "United Kingdom"));
|
||||
System.out.println(normalize("IV21 2LR", "United Kingdom"));
|
||||
}
|
||||
|
||||
private static boolean isCountryKnown(String country) {
|
||||
return rules.containsKey(country);
|
||||
}
|
||||
|
||||
private static Pattern getPattern(String country) {
|
||||
return Pattern.compile(rules.get(country).get(0));
|
||||
}
|
||||
|
||||
private static Matcher getMatcher(String postcode, String country) {
|
||||
return isCountryKnown(country) ? getPattern(country).matcher(postcode) : null;
|
||||
}
|
||||
|
||||
public static String normalize(String postcode, String country) {
|
||||
postcode = postcode.toUpperCase();
|
||||
String result = postcode;
|
||||
if (isCountryKnown(country)) {
|
||||
String replacement = rules.get(country).get(1);
|
||||
Matcher matcher = getMatcher(postcode, country);
|
||||
result = matcher.replaceAll(replacement);
|
||||
if (!result.equals(postcode)) {
|
||||
log.info("Normalize " + country + "'s postcode: " + postcode + " -> " + result);
|
||||
}
|
||||
if (!matcher.matches()) {
|
||||
log.info("Not matches " + country + "'s postcode regex: " + postcode);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static boolean looksLikePostcodeStart(String s, String country) {
|
||||
boolean result = false;
|
||||
if (isCountryKnown(country)) {
|
||||
Matcher matcher = getMatcher(s, country);
|
||||
result = (matcher != null && matcher.find()) || s.matches("(.+\\d+.*|.*\\d+.+)");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
|
@ -370,17 +370,19 @@ public abstract class SearchByNameAbstractActivity<T> extends OsmandListActivity
|
|||
namesFilter.cancelPreviousFilter(currentFilter);
|
||||
}
|
||||
|
||||
|
||||
protected void filterLoop(String query, Collection<T> list) {
|
||||
protected boolean filterLoop(String query, Collection<T> list) {
|
||||
boolean result = false;
|
||||
for (T obj : list) {
|
||||
if(namesFilter.isCancelled){
|
||||
if (namesFilter.isCancelled){
|
||||
break;
|
||||
}
|
||||
if(filterObject(obj, query)){
|
||||
if (filterObject(obj, query)){
|
||||
result = true;
|
||||
Message msg = uiHandler.obtainMessage(MESSAGE_ADD_ENTITY, obj);
|
||||
msg.sendToTarget();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ import net.osmand.ResultMatcher;
|
|||
import net.osmand.data.City;
|
||||
import net.osmand.data.City.CityType;
|
||||
import net.osmand.data.LatLon;
|
||||
import net.osmand.data.Postcode;
|
||||
import net.osmand.plus.OsmAndFormatter;
|
||||
import net.osmand.plus.OsmandApplication;
|
||||
import net.osmand.plus.OsmandSettings;
|
||||
|
@ -122,12 +123,17 @@ public class SearchCityByNameActivity extends SearchByNameAbstractActivity<City>
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void filterLoop(String query, Collection<City> list) {
|
||||
redefineSearchVillagesMode(query.length());
|
||||
if (!initializeTaskIsFinished() || (query.length() <= 3 && !searchVillages())) {
|
||||
super.filterLoop(query, list);
|
||||
} else {
|
||||
region.fillWithSuggestedCities(query, new ResultMatcher<City>() {
|
||||
protected boolean filterLoop(String query, Collection<City> list) {
|
||||
final boolean[] result = {false};
|
||||
redefineSearchVillagesMode(query);
|
||||
if (!initializeTaskIsFinished() || !isVillagesSearchEnabled()) {
|
||||
result[0] = super.filterLoop(query, list);
|
||||
if (!result[0] && !isVillagesSearchEnabled()) {
|
||||
setVillagesSearchEnabled(true);
|
||||
}
|
||||
}
|
||||
if (!result[0]) {
|
||||
ResultMatcher<City> resultMatcher = new ResultMatcher<City>() {
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return namesFilter.isCancelled;
|
||||
|
@ -135,30 +141,38 @@ public class SearchCityByNameActivity extends SearchByNameAbstractActivity<City>
|
|||
|
||||
@Override
|
||||
public boolean publish(City object) {
|
||||
result[0] = true;
|
||||
Message msg = uiHandler.obtainMessage(MESSAGE_ADD_ENTITY, object);
|
||||
msg.sendToTarget();
|
||||
return true;
|
||||
}
|
||||
}, searchVillages(), locationToSearch);
|
||||
};
|
||||
region.fillWithSuggestedCities(query, resultMatcher, isVillagesSearchEnabled(), locationToSearch);
|
||||
}
|
||||
return result[0];
|
||||
}
|
||||
|
||||
private void setVillagesSearchEnabled(final boolean enable) {
|
||||
searchVillagesMode = enable ? 0 : -1;
|
||||
uiHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
searchVillages.setVisibility(enable ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean searchVillages() {
|
||||
private boolean isVillagesSearchEnabled() {
|
||||
return searchVillagesMode >= 0;
|
||||
}
|
||||
|
||||
private void redefineSearchVillagesMode(int queryLen) {
|
||||
private void redefineSearchVillagesMode(String query) {
|
||||
if (searchVillagesMode == 1) {
|
||||
searchVillagesMode = 0;
|
||||
} else if (searchVillagesMode == 0 && queryLen <= 3 && !initialListToFilter.isEmpty()) {
|
||||
searchVillagesMode = -1;
|
||||
uiHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
searchVillages.setVisibility(View.VISIBLE);
|
||||
}
|
||||
});
|
||||
} else if (searchVillagesMode == 0 && !initialListToFilter.isEmpty() && query.isEmpty()) {
|
||||
setVillagesSearchEnabled(false);
|
||||
} else if (searchVillagesMode == -1 && Postcode.looksLikePostcodeStart(query, region.getCountryName())) {
|
||||
setVillagesSearchEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -166,8 +180,8 @@ public class SearchCityByNameActivity extends SearchByNameAbstractActivity<City>
|
|||
@Override
|
||||
public String getText(City obj) {
|
||||
LatLon l = obj.getLocation();
|
||||
if (getCurrentFilter().length() > 2) {
|
||||
String name = getShortText(obj);
|
||||
String name = getShortText(obj);
|
||||
if (isVillagesSearchEnabled()) {
|
||||
if (obj.getClosestCity() != null) {
|
||||
name += " - " + obj.getClosestCity().getName(region.getLang());
|
||||
LatLon loc = obj.getClosestCity().getLocation();
|
||||
|
@ -180,10 +194,8 @@ public class SearchCityByNameActivity extends SearchByNameAbstractActivity<City>
|
|||
name += " - " + OsmAndFormatter.toPublicString(obj.getType(), getMyApplication());
|
||||
}
|
||||
}
|
||||
return name;
|
||||
} else {
|
||||
return getShortText(obj);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -124,15 +124,17 @@ public class SearchStreetByNameActivity extends SearchByNameAbstractActivity<Str
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void filterLoop(String query, Collection<Street> list) {
|
||||
if(searchWithCity == -1){
|
||||
protected boolean filterLoop(String query, Collection<Street> list) {
|
||||
final boolean[] result = {false};
|
||||
if (searchWithCity == -1) {
|
||||
filter(query, list);
|
||||
} else if (searchWithCity == 0) {
|
||||
for (Street obj : list) {
|
||||
if (namesFilter.isCancelled) {
|
||||
break;
|
||||
}
|
||||
if(filterObject(obj, query)){
|
||||
if (filterObject(obj, query)) {
|
||||
result[0] = true;
|
||||
Message msg = uiHandler.obtainMessage(MESSAGE_ADD_ENTITY, obj);
|
||||
msg.sendToTarget();
|
||||
}
|
||||
|
@ -145,6 +147,7 @@ public class SearchStreetByNameActivity extends SearchByNameAbstractActivity<Str
|
|||
if (object instanceof Street) {
|
||||
if (city == null ||
|
||||
MapUtils.getDistance(city.getLocation(), object.getLocation()) < 100*1000) {
|
||||
result[0] = true;
|
||||
Message msg = uiHandler.obtainMessage(MESSAGE_ADD_ENTITY, object);
|
||||
msg.sendToTarget();
|
||||
return true;
|
||||
|
@ -166,8 +169,7 @@ public class SearchStreetByNameActivity extends SearchByNameAbstractActivity<Str
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
return result[0];
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -18,7 +18,9 @@ import net.osmand.util.MapUtils;
|
|||
public interface RegionAddressRepository {
|
||||
|
||||
public String getName();
|
||||
|
||||
|
||||
public String getCountryName();
|
||||
|
||||
public String getFileName() ;
|
||||
|
||||
public String getLang();
|
||||
|
|
|
@ -11,8 +11,6 @@ import java.util.Map;
|
|||
import java.util.TreeMap;
|
||||
|
||||
import net.osmand.Collator;
|
||||
import net.osmand.CollatorStringMatcher;
|
||||
import net.osmand.CollatorStringMatcher.StringMatcherMode;
|
||||
import net.osmand.OsmAndCollator;
|
||||
import net.osmand.PlatformUtil;
|
||||
import net.osmand.ResultMatcher;
|
||||
|
@ -38,8 +36,8 @@ public class RegionAddressRepositoryBinary implements RegionAddressRepository {
|
|||
private static final Log log = PlatformUtil.getLog(RegionAddressRepositoryBinary.class);
|
||||
private BinaryMapIndexReader file;
|
||||
|
||||
|
||||
private LinkedHashMap<Long, City> cities = new LinkedHashMap<Long, City>();
|
||||
private int POSTCODE_MIN_QUERY_LENGTH = 2;
|
||||
private int ZOOM_QTREE = 10;
|
||||
private QuadTree<City> citiesQtree = new QuadTree<City>(new QuadRect(0, 0, 1 << (ZOOM_QTREE + 1),
|
||||
1 << (ZOOM_QTREE + 1)), 8, 0.55f);
|
||||
|
@ -172,7 +170,7 @@ public class RegionAddressRepositoryBinary implements RegionAddressRepository {
|
|||
// private StringMatcherMode[] streetsCheckMode = new StringMatcherMode[] {StringMatcherMode.CHECK_ONLY_STARTS_WITH,
|
||||
// StringMatcherMode.CHECK_STARTS_FROM_SPACE_NOT_BEGINNING};
|
||||
|
||||
public synchronized List<MapObject> searchMapObjectsByName(String name, ResultMatcher<MapObject> resultMatcher, int[] typeFilter) {
|
||||
public synchronized List<MapObject> searchMapObjectsByName(String name, ResultMatcher<MapObject> resultMatcher, List<Integer> typeFilter) {
|
||||
SearchRequest<MapObject> req = BinaryMapIndexReader.buildAddressByNameRequest(resultMatcher, name);
|
||||
try {
|
||||
log.debug("file=" + file + "; req=" + req);
|
||||
|
@ -188,7 +186,7 @@ public class RegionAddressRepositoryBinary implements RegionAddressRepository {
|
|||
return searchMapObjectsByName(name, resultMatcher, null);
|
||||
}
|
||||
|
||||
private List<City> fillWithCities(String name, final ResultMatcher<City> resultMatcher, final int type) throws IOException {
|
||||
private List<City> fillWithCities(String name, final ResultMatcher<City> resultMatcher, final List<Integer> typeFilter) throws IOException {
|
||||
List<City> result = new ArrayList<City>();
|
||||
ResultMatcher<MapObject> matcher = new ResultMatcher<MapObject>() {
|
||||
List<City> cache = new ArrayList<City>();
|
||||
|
@ -196,7 +194,8 @@ public class RegionAddressRepositoryBinary implements RegionAddressRepository {
|
|||
@Override
|
||||
public boolean publish(MapObject o) {
|
||||
City c = (City) o;
|
||||
if (type == BinaryMapAddressReaderAdapter.VILLAGES_TYPE) {
|
||||
City.CityType type = c.getType();
|
||||
if (type != null && type.ordinal() >= City.CityType.VILLAGE.ordinal()) {
|
||||
if (c.getLocation() != null) {
|
||||
City ct = getClosestCity(c.getLocation(), cache);
|
||||
c.setClosestCity(ct);
|
||||
|
@ -210,7 +209,7 @@ public class RegionAddressRepositoryBinary implements RegionAddressRepository {
|
|||
return resultMatcher.isCancelled();
|
||||
}
|
||||
};
|
||||
List<MapObject> foundCities = searchMapObjectsByName(name, matcher, new int[]{type});
|
||||
List<MapObject> foundCities = searchMapObjectsByName(name, matcher, typeFilter);
|
||||
|
||||
for (MapObject o : foundCities) {
|
||||
result.add((City) o);
|
||||
|
@ -221,67 +220,23 @@ public class RegionAddressRepositoryBinary implements RegionAddressRepository {
|
|||
return result;
|
||||
}
|
||||
|
||||
private List<Integer> getCityTypeFilter(String name, boolean searchVillages) {
|
||||
List<Integer> cityTypes = new ArrayList<>();
|
||||
cityTypes.add(BinaryMapAddressReaderAdapter.CITY_TOWN_TYPE);
|
||||
if (searchVillages) {
|
||||
cityTypes.add(BinaryMapAddressReaderAdapter.VILLAGES_TYPE);
|
||||
if (name.length() >= POSTCODE_MIN_QUERY_LENGTH) {
|
||||
cityTypes.add(BinaryMapAddressReaderAdapter.POSTCODES_TYPE);
|
||||
}
|
||||
}
|
||||
return cityTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized List<City> fillWithSuggestedCities(String name, final ResultMatcher<City> resultMatcher, boolean searchVillages, LatLon currentLocation) {
|
||||
List<City> citiesToFill = new ArrayList<City>();
|
||||
if (cities.isEmpty()) {
|
||||
preloadCities(resultMatcher);
|
||||
citiesToFill.addAll(cities.values());
|
||||
if (!citiesToFill.isEmpty()) {
|
||||
return citiesToFill;
|
||||
}
|
||||
}
|
||||
|
||||
String lang = getLang();
|
||||
preloadCities(null);
|
||||
if (name.length() == 0) {
|
||||
citiesToFill.addAll(cities.values());
|
||||
if (searchVillages) {
|
||||
for (City c : citiesToFill) {
|
||||
resultMatcher.publish(c);
|
||||
}
|
||||
try {
|
||||
citiesToFill.addAll(fillWithCities(name, resultMatcher, BinaryMapAddressReaderAdapter.VILLAGES_TYPE));
|
||||
} catch (IOException e) {
|
||||
log.error("Disk operation failed", e); //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
if (!citiesToFill.isEmpty()) {
|
||||
return citiesToFill;
|
||||
}
|
||||
}
|
||||
List<City> citiesToFill = new ArrayList<>(cities.values());
|
||||
try {
|
||||
// essentially index is created that cities towns are first in cities map
|
||||
if (/*name.length() >= 2 && Algorithms.containsDigit(name) && */searchVillages) {
|
||||
// also try to identify postcodes
|
||||
String uName = name.toUpperCase();
|
||||
List<City> foundCities = fillWithCities(uName, resultMatcher, BinaryMapAddressReaderAdapter.POSTCODES_TYPE);
|
||||
for (City code : foundCities) {
|
||||
citiesToFill.add(code);
|
||||
if (resultMatcher.isCancelled()) {
|
||||
return citiesToFill;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
name = name.toLowerCase();
|
||||
for (City c : cities.values()) {
|
||||
String cName = c.getName(lang); // lower case not needed, collator ensures that
|
||||
if (CollatorStringMatcher.cmatches(collator, cName, name, StringMatcherMode.CHECK_STARTS_FROM_SPACE)) {
|
||||
if (resultMatcher.publish(c)) {
|
||||
citiesToFill.add(c);
|
||||
}
|
||||
}
|
||||
if (resultMatcher.isCancelled()) {
|
||||
return citiesToFill;
|
||||
}
|
||||
}
|
||||
|
||||
int initialSize = citiesToFill.size();
|
||||
if (/*name.length() >= 3 && */searchVillages) {
|
||||
citiesToFill.addAll(fillWithCities(name, resultMatcher, BinaryMapAddressReaderAdapter.VILLAGES_TYPE));
|
||||
}
|
||||
log.debug("Loaded citites " + (citiesToFill.size() - initialSize)); //$NON-NLS-1$
|
||||
citiesToFill.addAll(fillWithCities(name, resultMatcher, getCityTypeFilter(name, searchVillages)));
|
||||
} catch (IOException e) {
|
||||
log.error("Disk operation failed", e); //$NON-NLS-1$
|
||||
}
|
||||
|
@ -320,6 +275,11 @@ public class RegionAddressRepositoryBinary implements RegionAddressRepository {
|
|||
return fileName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCountryName() {
|
||||
return file.getCountryName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
|
|
Loading…
Reference in a new issue