From f37e4321a8b5040bc910b0828bd583534c71a3e7 Mon Sep 17 00:00:00 2001 From: Roman Inflianskas Date: Sun, 5 Jun 2016 16:01:29 +0300 Subject: [PATCH 1/3] Improve usability of address search, simplify code --- .../binary/BinaryMapAddressReaderAdapter.java | 38 ++-- .../osmand/binary/BinaryMapIndexReader.java | 22 +- OsmAnd-java/src/net/osmand/data/Postcode.java | 188 ++++++++++++++++++ .../search/SearchByNameAbstractActivity.java | 10 +- .../search/SearchCityByNameActivity.java | 16 +- .../RegionAddressRepositoryBinary.java | 80 ++------ 6 files changed, 263 insertions(+), 91 deletions(-) create mode 100644 OsmAnd-java/src/net/osmand/data/Postcode.java diff --git a/OsmAnd-java/src/net/osmand/binary/BinaryMapAddressReaderAdapter.java b/OsmAnd-java/src/net/osmand/binary/BinaryMapAddressReaderAdapter.java index 04b6d916e4..0624c189aa 100644 --- a/OsmAnd-java/src/net/osmand/binary/BinaryMapAddressReaderAdapter.java +++ b/OsmAnd-java/src/net/osmand/binary/BinaryMapAddressReaderAdapter.java @@ -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; @@ -40,7 +43,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 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 { @@ -240,15 +243,15 @@ public class BinaryMapAddressReaderAdapter { case 0: if(matcher != null) { boolean matches = matcher.matches(c.getName()); - if(!matches) { + if (!matches) { for(String n : c.getAllNames()) { matches = matcher.matches(n); - if(matches) { + if (matches) { break; } } } - if(!matches ) { + if (!matches ) { return null; } } @@ -536,9 +539,9 @@ public class BinaryMapAddressReaderAdapter { } } - public void searchAddressDataByName(AddressRegion reg, SearchRequest req, int[] typeFilter) throws IOException { + public void searchAddressDataByName(AddressRegion reg, SearchRequest req, List typeFilter) throws IOException { TIntArrayList loffsets = new TIntArrayList(); - CollatorStringMatcher matcher = new CollatorStringMatcher( req.nameQuery, StringMatcherMode.CHECK_STARTS_FROM_SPACE); + CollatorStringMatcher matcher = new CollatorStringMatcher(req.nameQuery, StringMatcherMode.CHECK_STARTS_FROM_SPACE); long time = System.currentTimeMillis(); int indexOffset = 0; while (true) { @@ -593,13 +596,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(); @@ -618,10 +621,10 @@ public class BinaryMapAddressReaderAdapter { 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()) { + if (!matches) { + for (String n : s.getAllNames()) { matches = matcher.matches(n); - if(matches) { + if (matches) { break; } } @@ -634,13 +637,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) { + if (obj != null && !published.contains(offset)) { req.publish(obj); + published.add(offset); } codedIS.popLimit(old); } diff --git a/OsmAnd-java/src/net/osmand/binary/BinaryMapIndexReader.java b/OsmAnd-java/src/net/osmand/binary/BinaryMapIndexReader.java index 6803d16497..3d547bd888 100644 --- a/OsmAnd-java/src/net/osmand/binary/BinaryMapIndexReader.java +++ b/OsmAnd-java/src/net/osmand/binary/BinaryMapIndexReader.java @@ -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 getCountryAndRegionNames() { + return new ArrayList<>(Arrays.asList(getRegionNames().get(0).split("_"))); + } + + public String getCountryName() { + return getCountryAndRegionNames().get(0); + } + + private String getRegionName() { + List 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,7 +1298,7 @@ public class BinaryMapIndexReader { return dataObject; } - public List searchAddressDataByName(SearchRequest req, int[] typeFilter) throws IOException { + public List searchAddressDataByName(SearchRequest req, List typeFilter) throws IOException { if (req.nameQuery == null || req.nameQuery.length() == 0) { throw new IllegalArgumentException(); } diff --git a/OsmAnd-java/src/net/osmand/data/Postcode.java b/OsmAnd-java/src/net/osmand/data/Postcode.java new file mode 100644 index 0000000000..c9215e1b33 --- /dev/null +++ b/OsmAnd-java/src/net/osmand/data/Postcode.java @@ -0,0 +1,188 @@ +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); + + 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")); + } + + public static String normalize(String postcode, String country) { +// © CC BY 3.0 2016 GeoNames.org +// with adaptations + Map> rules = new TreeMap>() {{ + 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")); + }}; + postcode = postcode.toUpperCase(); + String result = postcode; + if (rules.containsKey(country)) { + Pattern pattern = Pattern.compile(rules.get(country).get(0)); + String replacement = rules.get(country).get(1); + Matcher matcher = pattern.matcher(postcode); + 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; + } + +} diff --git a/OsmAnd/src/net/osmand/plus/activities/search/SearchByNameAbstractActivity.java b/OsmAnd/src/net/osmand/plus/activities/search/SearchByNameAbstractActivity.java index de331b4e3d..fb79b2ac64 100644 --- a/OsmAnd/src/net/osmand/plus/activities/search/SearchByNameAbstractActivity.java +++ b/OsmAnd/src/net/osmand/plus/activities/search/SearchByNameAbstractActivity.java @@ -373,10 +373,10 @@ public abstract class SearchByNameAbstractActivity extends OsmandListActivity protected void filterLoop(String query, Collection list) { for (T obj : list) { - if(namesFilter.isCancelled){ + if (namesFilter.isCancelled){ break; } - if(filterObject(obj, query)){ + if (filterObject(obj, query)){ Message msg = uiHandler.obtainMessage(MESSAGE_ADD_ENTITY, obj); msg.sendToTarget(); } @@ -412,6 +412,12 @@ public abstract class SearchByNameAbstractActivity extends OsmandListActivity } } +// private void filterOutObjectsInAdapter(String currentFilter) { +// for (int i = 0; i < getListAdapter().getCount(); i++) { +// if (getListAdapter().getItem(i).) +// } +// } + private void addObjectToAdapter(String currentFilter, T obj) { getListAdapter().add(obj); if (currentFilter.length() > 0) { diff --git a/OsmAnd/src/net/osmand/plus/activities/search/SearchCityByNameActivity.java b/OsmAnd/src/net/osmand/plus/activities/search/SearchCityByNameActivity.java index 60b37901ab..82043cbf1f 100644 --- a/OsmAnd/src/net/osmand/plus/activities/search/SearchCityByNameActivity.java +++ b/OsmAnd/src/net/osmand/plus/activities/search/SearchCityByNameActivity.java @@ -124,7 +124,7 @@ public class SearchCityByNameActivity extends SearchByNameAbstractActivity @Override protected void filterLoop(String query, Collection list) { redefineSearchVillagesMode(query.length()); - if (!initializeTaskIsFinished() || (query.length() <= 3 && !searchVillages())) { + if (!initializeTaskIsFinished() || !isVillagesSearchEnabled()) { super.filterLoop(query, list); } else { region.fillWithSuggestedCities(query, new ResultMatcher() { @@ -139,19 +139,19 @@ public class SearchCityByNameActivity extends SearchByNameAbstractActivity msg.sendToTarget(); return true; } - }, searchVillages(), locationToSearch); + }, isVillagesSearchEnabled(), locationToSearch); } } - private boolean searchVillages() { + private boolean isVillagesSearchEnabled() { return searchVillagesMode >= 0; } private void redefineSearchVillagesMode(int queryLen) { if (searchVillagesMode == 1) { searchVillagesMode = 0; - } else if (searchVillagesMode == 0 && queryLen <= 3 && !initialListToFilter.isEmpty()) { + } else if (searchVillagesMode == 0 && !initialListToFilter.isEmpty() && queryLen == 0) { searchVillagesMode = -1; uiHandler.post(new Runnable() { @Override @@ -166,8 +166,8 @@ public class SearchCityByNameActivity extends SearchByNameAbstractActivity @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 +180,8 @@ public class SearchCityByNameActivity extends SearchByNameAbstractActivity name += " - " + OsmAndFormatter.toPublicString(obj.getType(), getMyApplication()); } } - return name; - } else { - return getShortText(obj); } + return name; } @Override diff --git a/OsmAnd/src/net/osmand/plus/resources/RegionAddressRepositoryBinary.java b/OsmAnd/src/net/osmand/plus/resources/RegionAddressRepositoryBinary.java index 267ef6a639..f455824606 100644 --- a/OsmAnd/src/net/osmand/plus/resources/RegionAddressRepositoryBinary.java +++ b/OsmAnd/src/net/osmand/plus/resources/RegionAddressRepositoryBinary.java @@ -3,6 +3,7 @@ package net.osmand.plus.resources; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; @@ -11,8 +12,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; @@ -25,10 +24,12 @@ import net.osmand.data.Building; import net.osmand.data.City; import net.osmand.data.LatLon; import net.osmand.data.MapObject; +import net.osmand.data.Postcode; import net.osmand.data.QuadRect; import net.osmand.data.QuadTree; import net.osmand.data.Street; import net.osmand.plus.OsmandSettings.OsmandPreference; +import net.osmand.util.Algorithms; import net.osmand.util.MapUtils; import org.apache.commons.logging.Log; @@ -40,6 +41,7 @@ public class RegionAddressRepositoryBinary implements RegionAddressRepository { private LinkedHashMap cities = new LinkedHashMap(); + private int POSTCODE_MIN_QUERY_LENGTH = 2; private int ZOOM_QTREE = 10; private QuadTree citiesQtree = new QuadTree(new QuadRect(0, 0, 1 << (ZOOM_QTREE + 1), 1 << (ZOOM_QTREE + 1)), 8, 0.55f); @@ -172,7 +174,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 searchMapObjectsByName(String name, ResultMatcher resultMatcher, int[] typeFilter) { + public synchronized List searchMapObjectsByName(String name, ResultMatcher resultMatcher, List typeFilter) { SearchRequest req = BinaryMapIndexReader.buildAddressByNameRequest(resultMatcher, name); try { log.debug("file=" + file + "; req=" + req); @@ -188,7 +190,7 @@ public class RegionAddressRepositoryBinary implements RegionAddressRepository { return searchMapObjectsByName(name, resultMatcher, null); } - private List fillWithCities(String name, final ResultMatcher resultMatcher, final int type) throws IOException { + private List fillWithCities(String name, final ResultMatcher resultMatcher, final List typeFilter) throws IOException { List result = new ArrayList(); ResultMatcher matcher = new ResultMatcher() { List cache = new ArrayList(); @@ -196,7 +198,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 +213,7 @@ public class RegionAddressRepositoryBinary implements RegionAddressRepository { return resultMatcher.isCancelled(); } }; - List foundCities = searchMapObjectsByName(name, matcher, new int[]{type}); + List foundCities = searchMapObjectsByName(name, matcher, typeFilter); for (MapObject o : foundCities) { result.add((City) o); @@ -223,65 +226,16 @@ public class RegionAddressRepositoryBinary implements RegionAddressRepository { @Override public synchronized List fillWithSuggestedCities(String name, final ResultMatcher resultMatcher, boolean searchVillages, LatLon currentLocation) { - List citiesToFill = new ArrayList(); - 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 citiesToFill = new ArrayList<>(cities.values()); + List cityTypes = (searchVillages ? + Arrays.asList(BinaryMapAddressReaderAdapter.CITY_TOWN_TYPE, BinaryMapAddressReaderAdapter.VILLAGES_TYPE) : + Collections.singletonList(BinaryMapAddressReaderAdapter.VILLAGES_TYPE)); 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 foundCities = fillWithCities(uName, resultMatcher, BinaryMapAddressReaderAdapter.POSTCODES_TYPE); - for (City code : foundCities) { - citiesToFill.add(code); - if (resultMatcher.isCancelled()) { - return citiesToFill; - } - } - + citiesToFill.addAll(fillWithCities(name, resultMatcher, cityTypes)); + if (searchVillages && name.length() >= POSTCODE_MIN_QUERY_LENGTH) { + String postcode = Postcode.normalize(name, file.getCountryName()); + citiesToFill.addAll(fillWithCities(postcode, resultMatcher, Collections.singletonList(BinaryMapAddressReaderAdapter.POSTCODES_TYPE))); } - 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$ } catch (IOException e) { log.error("Disk operation failed", e); //$NON-NLS-1$ } From cdb04bf3434c0508097b3a94d79e6b4337ed9fb1 Mon Sep 17 00:00:00 2001 From: Roman Inflianskas Date: Sun, 5 Jun 2016 18:41:45 +0300 Subject: [PATCH 2/3] Improve speed and usability of address search --- .../binary/BinaryMapAddressReaderAdapter.java | 73 ++-- OsmAnd-java/src/net/osmand/data/Postcode.java | 314 ++++++++++-------- .../search/SearchByNameAbstractActivity.java | 6 - .../search/SearchCityByNameActivity.java | 16 +- .../resources/RegionAddressRepository.java | 4 +- .../RegionAddressRepositoryBinary.java | 20 +- 6 files changed, 246 insertions(+), 187 deletions(-) diff --git a/OsmAnd-java/src/net/osmand/binary/BinaryMapAddressReaderAdapter.java b/OsmAnd-java/src/net/osmand/binary/BinaryMapAddressReaderAdapter.java index 0624c189aa..bf9de9d866 100644 --- a/OsmAnd-java/src/net/osmand/binary/BinaryMapAddressReaderAdapter.java +++ b/OsmAnd-java/src/net/osmand/binary/BinaryMapAddressReaderAdapter.java @@ -7,8 +7,10 @@ import gnu.trove.set.hash.TIntHashSet; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; +import java.util.Map; import net.osmand.CollatorStringMatcher; import net.osmand.CollatorStringMatcher.StringMatcherMode; @@ -25,6 +27,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; @@ -178,8 +181,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); } @@ -230,8 +233,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 additionalTagsTable) throws IOException{ + protected City readCityHeader(CityMatcher matcher, int filePointer, List additionalTagsTable) throws IOException{ int x = 0; int y = 0; City c = null; @@ -241,21 +273,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]); @@ -541,7 +559,16 @@ public class BinaryMapAddressReaderAdapter { public void searchAddressDataByName(AddressRegion reg, SearchRequest req, List 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) { @@ -558,7 +585,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: @@ -620,10 +647,10 @@ 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()); + boolean matches = stringMatcher.matches(s.getName()); if (!matches) { for (String n : s.getAllNames()) { - matches = matcher.matches(n); + matches = stringMatcher.matches(n); if (matches) { break; } @@ -643,7 +670,7 @@ public class BinaryMapAddressReaderAdapter { codedIS.seek(offset); int len = codedIS.readRawVarint32(); int old = codedIS.pushLimit(len); - City obj = readCityHeader(matcher, list.get(j), reg.attributeTagsTable); + City obj = readCityHeader(cityPostcodeMatcher, list.get(j), reg.attributeTagsTable); if (obj != null && !published.contains(offset)) { req.publish(obj); published.add(offset); diff --git a/OsmAnd-java/src/net/osmand/data/Postcode.java b/OsmAnd-java/src/net/osmand/data/Postcode.java index c9215e1b33..af0a80eace 100644 --- a/OsmAnd-java/src/net/osmand/data/Postcode.java +++ b/OsmAnd-java/src/net/osmand/data/Postcode.java @@ -14,6 +14,150 @@ 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> rules = new TreeMap>() {{ + 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")); @@ -23,157 +167,24 @@ public class Postcode { 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) { -// © CC BY 3.0 2016 GeoNames.org -// with adaptations - Map> rules = new TreeMap>() {{ - 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")); - }}; postcode = postcode.toUpperCase(); String result = postcode; - if (rules.containsKey(country)) { - Pattern pattern = Pattern.compile(rules.get(country).get(0)); + if (isCountryKnown(country)) { String replacement = rules.get(country).get(1); - Matcher matcher = pattern.matcher(postcode); + Matcher matcher = getMatcher(postcode, country); result = matcher.replaceAll(replacement); if (!result.equals(postcode)) { log.info("Normalize " + country + "'s postcode: " + postcode + " -> " + result); @@ -185,4 +196,13 @@ public class 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; + } + } diff --git a/OsmAnd/src/net/osmand/plus/activities/search/SearchByNameAbstractActivity.java b/OsmAnd/src/net/osmand/plus/activities/search/SearchByNameAbstractActivity.java index fb79b2ac64..74f2aa2841 100644 --- a/OsmAnd/src/net/osmand/plus/activities/search/SearchByNameAbstractActivity.java +++ b/OsmAnd/src/net/osmand/plus/activities/search/SearchByNameAbstractActivity.java @@ -412,12 +412,6 @@ public abstract class SearchByNameAbstractActivity extends OsmandListActivity } } -// private void filterOutObjectsInAdapter(String currentFilter) { -// for (int i = 0; i < getListAdapter().getCount(); i++) { -// if (getListAdapter().getItem(i).) -// } -// } - private void addObjectToAdapter(String currentFilter, T obj) { getListAdapter().add(obj); if (currentFilter.length() > 0) { diff --git a/OsmAnd/src/net/osmand/plus/activities/search/SearchCityByNameActivity.java b/OsmAnd/src/net/osmand/plus/activities/search/SearchCityByNameActivity.java index 82043cbf1f..3540462988 100644 --- a/OsmAnd/src/net/osmand/plus/activities/search/SearchCityByNameActivity.java +++ b/OsmAnd/src/net/osmand/plus/activities/search/SearchCityByNameActivity.java @@ -13,9 +13,11 @@ import net.osmand.CollatorStringMatcher; import net.osmand.CollatorStringMatcher.StringMatcherMode; import net.osmand.OsmAndCollator; import net.osmand.ResultMatcher; +import net.osmand.StringMatcher; 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; @@ -123,7 +125,7 @@ public class SearchCityByNameActivity extends SearchByNameAbstractActivity @Override protected void filterLoop(String query, Collection list) { - redefineSearchVillagesMode(query.length()); + redefineSearchVillagesMode(query); if (!initializeTaskIsFinished() || !isVillagesSearchEnabled()) { super.filterLoop(query, list); } else { @@ -148,10 +150,10 @@ public class SearchCityByNameActivity extends SearchByNameAbstractActivity return searchVillagesMode >= 0; } - private void redefineSearchVillagesMode(int queryLen) { + private void redefineSearchVillagesMode(String query) { if (searchVillagesMode == 1) { searchVillagesMode = 0; - } else if (searchVillagesMode == 0 && !initialListToFilter.isEmpty() && queryLen == 0) { + } else if (searchVillagesMode == 0 && !initialListToFilter.isEmpty() && query.isEmpty()) { searchVillagesMode = -1; uiHandler.post(new Runnable() { @Override @@ -159,6 +161,14 @@ public class SearchCityByNameActivity extends SearchByNameAbstractActivity searchVillages.setVisibility(View.VISIBLE); } }); + } else if (searchVillagesMode == -1 && Postcode.looksLikePostcodeStart(query, region.getCountryName())) { + searchVillagesMode = 0; + uiHandler.post(new Runnable() { + @Override + public void run() { + searchVillages.setVisibility(View.GONE); + } + }); } } diff --git a/OsmAnd/src/net/osmand/plus/resources/RegionAddressRepository.java b/OsmAnd/src/net/osmand/plus/resources/RegionAddressRepository.java index 4f9057c7ab..ea6057f03b 100644 --- a/OsmAnd/src/net/osmand/plus/resources/RegionAddressRepository.java +++ b/OsmAnd/src/net/osmand/plus/resources/RegionAddressRepository.java @@ -18,7 +18,9 @@ import net.osmand.util.MapUtils; public interface RegionAddressRepository { public String getName(); - + + public String getCountryName(); + public String getFileName() ; public String getLang(); diff --git a/OsmAnd/src/net/osmand/plus/resources/RegionAddressRepositoryBinary.java b/OsmAnd/src/net/osmand/plus/resources/RegionAddressRepositoryBinary.java index f455824606..d4070663bc 100644 --- a/OsmAnd/src/net/osmand/plus/resources/RegionAddressRepositoryBinary.java +++ b/OsmAnd/src/net/osmand/plus/resources/RegionAddressRepositoryBinary.java @@ -227,15 +227,16 @@ public class RegionAddressRepositoryBinary implements RegionAddressRepository { @Override public synchronized List fillWithSuggestedCities(String name, final ResultMatcher resultMatcher, boolean searchVillages, LatLon currentLocation) { List citiesToFill = new ArrayList<>(cities.values()); - List cityTypes = (searchVillages ? - Arrays.asList(BinaryMapAddressReaderAdapter.CITY_TOWN_TYPE, BinaryMapAddressReaderAdapter.VILLAGES_TYPE) : - Collections.singletonList(BinaryMapAddressReaderAdapter.VILLAGES_TYPE)); + List cityTypes = new ArrayList<>(); + cityTypes.add(BinaryMapAddressReaderAdapter.CITY_TOWN_TYPE); + if (searchVillages) { + cityTypes.add(BinaryMapAddressReaderAdapter.VILLAGES_TYPE); + if (searchVillages && name.length() >= POSTCODE_MIN_QUERY_LENGTH) { + cityTypes.add(BinaryMapAddressReaderAdapter.POSTCODES_TYPE); + } + } try { citiesToFill.addAll(fillWithCities(name, resultMatcher, cityTypes)); - if (searchVillages && name.length() >= POSTCODE_MIN_QUERY_LENGTH) { - String postcode = Postcode.normalize(name, file.getCountryName()); - citiesToFill.addAll(fillWithCities(postcode, resultMatcher, Collections.singletonList(BinaryMapAddressReaderAdapter.POSTCODES_TYPE))); - } } catch (IOException e) { log.error("Disk operation failed", e); //$NON-NLS-1$ } @@ -274,6 +275,11 @@ public class RegionAddressRepositoryBinary implements RegionAddressRepository { return fileName; } + @Override + public String getCountryName() { + return file.getCountryName(); + } + @Override public String getFileName() { return fileName; From 9aa222ea70a4a6d16b540b92e29386210ffa5f2a Mon Sep 17 00:00:00 2001 From: Roman Inflianskas Date: Mon, 6 Jun 2016 10:52:17 +0300 Subject: [PATCH 3/3] Address search: search villages if nothing found (related: #2471) --- .../binary/BinaryMapAddressReaderAdapter.java | 2 - .../osmand/binary/BinaryMapIndexReader.java | 3 -- OsmAnd-java/src/net/osmand/data/City.java | 5 --- .../search/SearchByNameAbstractActivity.java | 6 ++- .../search/SearchCityByNameActivity.java | 44 ++++++++++--------- .../search/SearchStreetByNameActivity.java | 12 ++--- .../RegionAddressRepositoryBinary.java | 18 ++++---- 7 files changed, 44 insertions(+), 46 deletions(-) diff --git a/OsmAnd-java/src/net/osmand/binary/BinaryMapAddressReaderAdapter.java b/OsmAnd-java/src/net/osmand/binary/BinaryMapAddressReaderAdapter.java index bf9de9d866..ceb8ab4ff5 100644 --- a/OsmAnd-java/src/net/osmand/binary/BinaryMapAddressReaderAdapter.java +++ b/OsmAnd-java/src/net/osmand/binary/BinaryMapAddressReaderAdapter.java @@ -7,10 +7,8 @@ import gnu.trove.set.hash.TIntHashSet; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.LinkedList; import java.util.List; -import java.util.Map; import net.osmand.CollatorStringMatcher; import net.osmand.CollatorStringMatcher.StringMatcherMode; diff --git a/OsmAnd-java/src/net/osmand/binary/BinaryMapIndexReader.java b/OsmAnd-java/src/net/osmand/binary/BinaryMapIndexReader.java index 3d547bd888..f17ae0949a 100644 --- a/OsmAnd-java/src/net/osmand/binary/BinaryMapIndexReader.java +++ b/OsmAnd-java/src/net/osmand/binary/BinaryMapIndexReader.java @@ -1299,9 +1299,6 @@ public class BinaryMapIndexReader { } public List searchAddressDataByName(SearchRequest req, List typeFilter) throws IOException { - if (req.nameQuery == null || req.nameQuery.length() == 0) { - throw new IllegalArgumentException(); - } for (AddressRegion reg : addressIndexes) { if (reg.indexNameOffset != -1) { codedIS.seek(reg.indexNameOffset); diff --git a/OsmAnd-java/src/net/osmand/data/City.java b/OsmAnd-java/src/net/osmand/data/City.java index 8a21e8c5e7..dff46e1656 100644 --- a/OsmAnd-java/src/net/osmand/data/City.java +++ b/OsmAnd-java/src/net/osmand/data/City.java @@ -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 { diff --git a/OsmAnd/src/net/osmand/plus/activities/search/SearchByNameAbstractActivity.java b/OsmAnd/src/net/osmand/plus/activities/search/SearchByNameAbstractActivity.java index 74f2aa2841..8de33ec920 100644 --- a/OsmAnd/src/net/osmand/plus/activities/search/SearchByNameAbstractActivity.java +++ b/OsmAnd/src/net/osmand/plus/activities/search/SearchByNameAbstractActivity.java @@ -370,17 +370,19 @@ public abstract class SearchByNameAbstractActivity extends OsmandListActivity namesFilter.cancelPreviousFilter(currentFilter); } - - protected void filterLoop(String query, Collection list) { + protected boolean filterLoop(String query, Collection list) { + boolean result = false; for (T obj : list) { if (namesFilter.isCancelled){ break; } if (filterObject(obj, query)){ + result = true; Message msg = uiHandler.obtainMessage(MESSAGE_ADD_ENTITY, obj); msg.sendToTarget(); } } + return result; } diff --git a/OsmAnd/src/net/osmand/plus/activities/search/SearchCityByNameActivity.java b/OsmAnd/src/net/osmand/plus/activities/search/SearchCityByNameActivity.java index 3540462988..cd32fee7ac 100644 --- a/OsmAnd/src/net/osmand/plus/activities/search/SearchCityByNameActivity.java +++ b/OsmAnd/src/net/osmand/plus/activities/search/SearchCityByNameActivity.java @@ -13,7 +13,6 @@ import net.osmand.CollatorStringMatcher; import net.osmand.CollatorStringMatcher.StringMatcherMode; import net.osmand.OsmAndCollator; import net.osmand.ResultMatcher; -import net.osmand.StringMatcher; import net.osmand.data.City; import net.osmand.data.City.CityType; import net.osmand.data.LatLon; @@ -124,12 +123,17 @@ public class SearchCityByNameActivity extends SearchByNameAbstractActivity } @Override - protected void filterLoop(String query, Collection list) { + protected boolean filterLoop(String query, Collection list) { + final boolean[] result = {false}; redefineSearchVillagesMode(query); if (!initializeTaskIsFinished() || !isVillagesSearchEnabled()) { - super.filterLoop(query, list); - } else { - region.fillWithSuggestedCities(query, new ResultMatcher() { + result[0] = super.filterLoop(query, list); + if (!result[0] && !isVillagesSearchEnabled()) { + setVillagesSearchEnabled(true); + } + } + if (!result[0]) { + ResultMatcher resultMatcher = new ResultMatcher() { @Override public boolean isCancelled() { return namesFilter.isCancelled; @@ -137,14 +141,26 @@ public class SearchCityByNameActivity extends SearchByNameAbstractActivity @Override public boolean publish(City object) { + result[0] = true; Message msg = uiHandler.obtainMessage(MESSAGE_ADD_ENTITY, object); msg.sendToTarget(); return true; } - }, isVillagesSearchEnabled(), 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 isVillagesSearchEnabled() { return searchVillagesMode >= 0; @@ -154,21 +170,9 @@ public class SearchCityByNameActivity extends SearchByNameAbstractActivity if (searchVillagesMode == 1) { searchVillagesMode = 0; } else if (searchVillagesMode == 0 && !initialListToFilter.isEmpty() && query.isEmpty()) { - searchVillagesMode = -1; - uiHandler.post(new Runnable() { - @Override - public void run() { - searchVillages.setVisibility(View.VISIBLE); - } - }); + setVillagesSearchEnabled(false); } else if (searchVillagesMode == -1 && Postcode.looksLikePostcodeStart(query, region.getCountryName())) { - searchVillagesMode = 0; - uiHandler.post(new Runnable() { - @Override - public void run() { - searchVillages.setVisibility(View.GONE); - } - }); + setVillagesSearchEnabled(true); } } diff --git a/OsmAnd/src/net/osmand/plus/activities/search/SearchStreetByNameActivity.java b/OsmAnd/src/net/osmand/plus/activities/search/SearchStreetByNameActivity.java index 1583ee54f2..75d4561d96 100644 --- a/OsmAnd/src/net/osmand/plus/activities/search/SearchStreetByNameActivity.java +++ b/OsmAnd/src/net/osmand/plus/activities/search/SearchStreetByNameActivity.java @@ -124,15 +124,17 @@ public class SearchStreetByNameActivity extends SearchByNameAbstractActivity list) { - if(searchWithCity == -1){ + protected boolean filterLoop(String query, Collection 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 cities = new LinkedHashMap(); private int POSTCODE_MIN_QUERY_LENGTH = 2; private int ZOOM_QTREE = 10; @@ -224,19 +220,23 @@ public class RegionAddressRepositoryBinary implements RegionAddressRepository { return result; } - @Override - public synchronized List fillWithSuggestedCities(String name, final ResultMatcher resultMatcher, boolean searchVillages, LatLon currentLocation) { - List citiesToFill = new ArrayList<>(cities.values()); + private List getCityTypeFilter(String name, boolean searchVillages) { List cityTypes = new ArrayList<>(); cityTypes.add(BinaryMapAddressReaderAdapter.CITY_TOWN_TYPE); if (searchVillages) { cityTypes.add(BinaryMapAddressReaderAdapter.VILLAGES_TYPE); - if (searchVillages && name.length() >= POSTCODE_MIN_QUERY_LENGTH) { + if (name.length() >= POSTCODE_MIN_QUERY_LENGTH) { cityTypes.add(BinaryMapAddressReaderAdapter.POSTCODES_TYPE); } } + return cityTypes; + } + + @Override + public synchronized List fillWithSuggestedCities(String name, final ResultMatcher resultMatcher, boolean searchVillages, LatLon currentLocation) { + List citiesToFill = new ArrayList<>(cities.values()); try { - citiesToFill.addAll(fillWithCities(name, resultMatcher, cityTypes)); + citiesToFill.addAll(fillWithCities(name, resultMatcher, getCityTypeFilter(name, searchVillages))); } catch (IOException e) { log.error("Disk operation failed", e); //$NON-NLS-1$ }