diff --git a/OsmAnd-java/src/main/java/net/osmand/search/SearchUICore.java b/OsmAnd-java/src/main/java/net/osmand/search/SearchUICore.java index 4e328ad156..e690af01fe 100644 --- a/OsmAnd-java/src/main/java/net/osmand/search/SearchUICore.java +++ b/OsmAnd-java/src/main/java/net/osmand/search/SearchUICore.java @@ -731,9 +731,12 @@ public class SearchUICore { @Override public int compare(SearchResult o1, SearchResult o2) { - if (!ObjectType.isTopVisible(o1.objectType) && !ObjectType.isTopVisible(o2.objectType) - && o1.getFoundWordCount() != o2.getFoundWordCount()) { - return -Algorithms.compare(o1.getFoundWordCount(), o2.getFoundWordCount()); + if (!ObjectType.isTopVisible(o1.objectType) && !ObjectType.isTopVisible(o2.objectType)) { + if (o1.isUnknownPhraseMatches() != o2.isUnknownPhraseMatches()) { + return o1.isUnknownPhraseMatches() ? -1 : 1; + } else if (o1.getFoundWordCount() != o2.getFoundWordCount()) { + return -Algorithms.compare(o1.getFoundWordCount(), o2.getFoundWordCount()); + } } if (!sortByName) { double s1 = o1.getSearchDistance(loc); @@ -748,6 +751,17 @@ public class SearchUICore { if (st1 != st2) { return Algorithms.compare(st1, st2); } + if (o1.parentSearchResult != null && o2.parentSearchResult != null) { + if (o1.parentSearchResult == o2.parentSearchResult) { + int cmp = collator.compare(o1.localeName, o2.localeName); + if (cmp != 0) { + return cmp; + } + } + double s1 = o1.getSearchDistance(loc, 1); + double s2 = o2.getSearchDistance(loc, 1); + return Double.compare(s1, s2); + } int cmp = collator.compare(o1.localeName, o2.localeName); if (cmp != 0) { return cmp; diff --git a/OsmAnd-java/src/main/java/net/osmand/search/core/SearchCoreFactory.java b/OsmAnd-java/src/main/java/net/osmand/search/core/SearchCoreFactory.java index dbea3ebfbf..bbdc5a87e6 100644 --- a/OsmAnd-java/src/main/java/net/osmand/search/core/SearchCoreFactory.java +++ b/OsmAnd-java/src/main/java/net/osmand/search/core/SearchCoreFactory.java @@ -315,14 +315,9 @@ public class SearchCoreFactory { if (phrase.isNoSelectedType() && bbox != null && (phrase.isUnknownSearchWordPresent() || phrase.isEmptyQueryAllowed()) && phrase.isSearchTypeAllowed(ObjectType.CITY)) { - String wrd = phrase.getUnknownWordToSearch(); - NameStringMatcher nm = phrase.getNameStringMatcher(wrd, phrase.isUnknownSearchWordComplete()); - String unknownSearchPhrase = phrase.getUnknownSearchPhrase().trim(); - NameStringMatcher phraseMatcher = null; - if (!Algorithms.isEmpty(unknownSearchPhrase)) { - phraseMatcher = new NameStringMatcher(unknownSearchPhrase, StringMatcherMode.CHECK_EQUALS); - } - // NameStringMatcher nm = phrase.getNameStringMatcher(); + String word = phrase.getUnknownWordToSearch(); + NameStringMatcher nm = phrase.getNameStringMatcher(word, phrase.isUnknownSearchWordComplete()); + NameStringMatcher wordEqualsMatcher = phrase.getNameStringMatcher(word, true); resArray.clear(); resArray = townCitiesQR.queryInBox(bbox, resArray); int limit = 0; @@ -341,10 +336,8 @@ public class SearchCoreFactory { if (phrase.isEmptyQueryAllowed() && phrase.isEmpty()) { resultMatcher.publish(res); } else if (nm.matches(res.localeName) || nm.matches(res.otherNames)) { - res.firstUnknownWordMatches = wrd.equals(phrase.getUnknownSearchWord()); - if (phraseMatcher != null) { - res.unknownPhraseMatches = phraseMatcher.matches(res.localeName) || phraseMatcher.matches(res.otherNames); - } + res.firstUnknownWordMatches = word.equals(phrase.getUnknownSearchWord()); + res.unknownPhraseMatches = wordEqualsMatcher.matches(res.localeName) || wordEqualsMatcher.matches(res.otherNames); subSearchApiOrPublish(phrase, resultMatcher, res, cityApi); } if (limit++ > LIMIT * phrase.getRadiusLevel()) { @@ -472,11 +465,7 @@ public class SearchCoreFactory { SearchPhraseDataType.ADDRESS); String wordToSearch = phrase.getUnknownWordToSearch(); - String unknownSearchPhrase = phrase.getUnknownSearchPhrase().trim(); - NameStringMatcher phraseMatcher = null; - if (!Algorithms.isEmpty(unknownSearchPhrase)) { - phraseMatcher = new NameStringMatcher(unknownSearchPhrase, StringMatcherMode.CHECK_EQUALS); - } + NameStringMatcher wordEqualsMatcher = phrase.getNameStringMatcher(wordToSearch, true); while (offlineIterator.hasNext() && wordToSearch.length() > 0) { BinaryMapIndexReader r = offlineIterator.next(); currentFile[0] = r; @@ -491,9 +480,7 @@ public class SearchCoreFactory { r.searchAddressDataByName(req); for (SearchResult res : immediateResults) { res.firstUnknownWordMatches = wordToSearch.equals(phrase.getUnknownSearchWord()); - if (phraseMatcher != null) { - res.unknownPhraseMatches = phraseMatcher.matches(res.localeName) || phraseMatcher.matches(res.otherNames); - } + res.unknownPhraseMatches = wordEqualsMatcher.matches(res.localeName) || wordEqualsMatcher.matches(res.otherNames); if (res.objectType == ObjectType.STREET) { City ct = ((Street) res.object).getCity(); phrase.countUnknownWordsMatch(res, @@ -679,12 +666,15 @@ public class SearchCoreFactory { List results = new ArrayList(); NameStringMatcher nm = new NameStringMatcher(phrase.getUnknownSearchPhrase(), StringMatcherMode.CHECK_ONLY_STARTS_WITH_TRIM); + + Set filters = new HashSet<>(); for (AbstractPoiType pf : topVisibleFilters) { if (!phrase.isUnknownSearchWordPresent() || nm.matches(pf.getTranslation()) || nm.matches(pf.getEnTranslation()) || nm.matches(pf.getSynonyms())) { results.add(pf); + filters.add(pf.getTranslation()); } } if (phrase.isUnknownSearchWordPresent()) { @@ -694,6 +684,7 @@ public class SearchCoreFactory { || nm.matches(c.getEnTranslation()) || nm.matches(c.getSynonyms()))) { results.add(c); + filters.add(c.getTranslation()); } } Iterator> it = translatedNames.entrySet().iterator(); @@ -701,7 +692,7 @@ public class SearchCoreFactory { Entry e = it.next(); PoiType pt = e.getValue(); if (pt.getCategory() != types.getOtherMapCategory()) { - if (!results.contains(pt) + if (!results.contains(pt) && !filters.contains(pt.getTranslation()) && (nm.matches(pt.getEnTranslation()) || nm.matches(pt.getTranslation()) || nm.matches(pt.getSynonyms()))) { @@ -763,6 +754,10 @@ public class SearchCoreFactory { if (!p.isNoSelectedType() && !p.isUnknownSearchWordPresent()) { return -1; } + SearchWord lastSelectedWord = p.getLastSelectedWord(); + if (lastSelectedWord != null && ObjectType.isAddress(lastSelectedWord.getType())) { + return -1; + } return SEARCH_AMENITY_TYPE_API_PRIORITY; } } @@ -1156,6 +1151,7 @@ public class SearchCoreFactory { } String lw = phrase.getUnknownWordToSearchBuilding(); NameStringMatcher buildingMatch = phrase.getNameStringMatcher(lw, phrase.isLastUnknownSearchWordComplete()); + NameStringMatcher startMatch = new NameStringMatcher(lw, StringMatcherMode.CHECK_ONLY_STARTS_WITH); for (Building b : s.getBuildings()) { SearchResult res = new SearchResult(phrase); boolean interpolation = b.belongsToInterpolation(lw); @@ -1169,6 +1165,7 @@ public class SearchCoreFactory { res.file = file; res.priority = priority; res.priorityDistance = 0; + res.firstUnknownWordMatches = startMatch.matches(res.localeName); res.relatedObject = s; res.localeRelatedObjectName = s.getName(phrase.getSettings().getLang(), phrase.getSettings().isTransliterate()); res.objectType = ObjectType.HOUSE; @@ -1342,6 +1339,9 @@ public class SearchCoreFactory { @Override public int getSearchPriority(SearchPhrase p) { + if (!p.isNoSelectedType() || !p.isUnknownSearchWordPresent()) { + return -1; + } return SEARCH_LOCATION_PRIORITY; } } diff --git a/OsmAnd-java/src/main/java/net/osmand/search/core/SearchPhrase.java b/OsmAnd-java/src/main/java/net/osmand/search/core/SearchPhrase.java index ff13b5a6ef..dc1a9d7b75 100644 --- a/OsmAnd-java/src/main/java/net/osmand/search/core/SearchPhrase.java +++ b/OsmAnd-java/src/main/java/net/osmand/search/core/SearchPhrase.java @@ -48,7 +48,8 @@ public class SearchPhrase { private static final String ALLDELIMITERS = "\\s|,"; private static final Pattern reg = Pattern.compile(ALLDELIMITERS); private Collator clt; - + private static Comparator commonWordsComparator; + private static Set conjunctions = new TreeSet<>(); static { // the @@ -111,6 +112,20 @@ public class SearchPhrase { conjunctions.add("den"); conjunctions.add("dr"); conjunctions.add("y"); + + commonWordsComparator = new Comparator() { + + @Override + public int compare(String o1, String o2) { + int i1 = CommonWords.getCommonSearch(o1.toLowerCase()); + int i2 = CommonWords.getCommonSearch(o2.toLowerCase()); + if (i1 != i2) { + return icompare(i1, i2); + } + // compare length without numbers to not include house numbers + return -icompare(lengthWithoutNumbers(o1), lengthWithoutNumbers(o2)); + } + }; } @@ -142,7 +157,7 @@ public class SearchPhrase { for(SearchWord w : leftWords) { if(restText.startsWith(w.getWord() + DELIMITER)) { sp.words.add(w); - restText = restText.substring(w.getWord().length() + DELIMITER.length()).trim(); + restText = restText.substring(w.getWord().length() + DELIMITER.length()); } else { break; } @@ -746,42 +761,32 @@ public class SearchPhrase { } return getUnknownSearchWord(); } - + + private static int lengthWithoutNumbers(String s) { + int len = 0; + for(int k = 0; k < s.length(); k++) { + if (s.charAt(k) >= '0' && s.charAt(k) <= '9') { + + } else { + len++; + } + } + return len; + } + public String getUnknownWordToSearch() { List unknownSearchWords = getUnknownSearchWords(); - String wordToSearch = getUnknownSearchWord(); if (unknownSearchWords.size() > 0) { List searchWords = new ArrayList<>(unknownSearchWords); searchWords.add(0, getUnknownSearchWord()); - Collections.sort(searchWords, new Comparator() { - - private int lengthWithoutNumbers(String s) { - int len = 0; - for(int k = 0; k < s.length(); k++) { - if(s.charAt(k) >= '0' && s.charAt(k) <= '9') { - - } else { - len++; - } - } - return len; + Collections.sort(searchWords, commonWordsComparator); + for (String s : searchWords) { + if (s.length() > 0 && !Character.isDigit(s.charAt(0))) { + return s; } - - @Override - public int compare(String o1, String o2) { - int i1 = CommonWords.getCommonSearch(o1.toLowerCase()); - int i2 = CommonWords.getCommonSearch(o2.toLowerCase()); - if (i1 != i2) { - return icompare(i1, i2); - } - // compare length without numbers to not include house numbers - return -icompare(lengthWithoutNumbers(o1), lengthWithoutNumbers(o2)); - } - }); - wordToSearch = searchWords.get(0); + } } - return wordToSearch; } diff --git a/OsmAnd-java/src/main/java/net/osmand/search/core/SearchResult.java b/OsmAnd-java/src/main/java/net/osmand/search/core/SearchResult.java index 03294e6e8a..73da12db7e 100644 --- a/OsmAnd-java/src/main/java/net/osmand/search/core/SearchResult.java +++ b/OsmAnd-java/src/main/java/net/osmand/search/core/SearchResult.java @@ -21,24 +21,28 @@ public class SearchResult { public Collection otherWordsMatch = null; public boolean firstUnknownWordMatches = true; public boolean unknownPhraseMatches = false; - - + + public boolean isUnknownPhraseMatches() { + boolean res = unknownPhraseMatches; + if (!res && parentSearchResult != null) { + res = parentSearchResult.unknownPhraseMatches; + } + return res; + } + public SearchResult(SearchPhrase sp) { this.requiredSearchPhrase = sp; } public int getFoundWordCount() { int inc = 0; - if(firstUnknownWordMatches) { + if (firstUnknownWordMatches) { inc = 1; } - if (unknownPhraseMatches) { - inc += 1000; - } - if(otherWordsMatch != null) { + if (otherWordsMatch != null) { inc += otherWordsMatch.size(); } - if(parentSearchResult != null) { + if (parentSearchResult != null) { inc += parentSearchResult.getFoundWordCount(); } return inc; diff --git a/OsmAnd/res/values-ru/phrases.xml b/OsmAnd/res/values-ru/phrases.xml index 87121a232d..ed7347c36d 100644 --- a/OsmAnd/res/values-ru/phrases.xml +++ b/OsmAnd/res/values-ru/phrases.xml @@ -1,7 +1,7 @@ Булочная - Винно-водочный магазин;Алкоголь + Вино-водочный магазин;Алкоголь Продукты Торговый центр Напитки @@ -154,7 +154,7 @@ Шиномонтаж Пункт техосмотра;Техосмотр Автомойка - Автомобильная заправка;Автозаправка;Бензоколонка;АЗС;Бензозаправка + Автомобильная заправка;Автозаправка;Бензоколонка;АЗС;Бензозаправка;Заправка Зарядная станция Ремонтная эстакада/яма Подкачка шин @@ -192,7 +192,7 @@ Прокат велосипедов;Велопрокат Велопарковка;Велосипедная парковка;Парковка для велосипедов - Станция канатной дороги + Станция канатной дороги;Канатка Канатная дорога с кабинами Канатная дорога с небольшими кабинами Канатная дорога с открытыми креслами @@ -218,7 +218,7 @@ Шлюпочная мастерская Очистные сооружения Водонапорная башня - Ворота шлюза + Ворота шлюза;Шлюз Поворотная точка на воде Небольшая плотина Дамба @@ -245,8 +245,8 @@ Урна для мусора Тип - Пункт приёма - Контейнер + Пункт приёма;Центр утилизации + Контейнер;Бак Принимаемые отходы Стекло @@ -338,14 +338,14 @@ Тюрьма ЗАГС Посольство - Государственное учреждение - Служба судебных приставов + Государственное учреждение;Госучреждение + Служба судебных приставов;Судебные приставы Прокуратура Пенсионый фонд Миграционная служба Налоговая инспекция Административное учреждение - Таможенный пункт + Таможенный пункт;Таможня Аптека Больница @@ -398,10 +398,10 @@ Специалист по подготовке налоговых деклараций Стадион - Спортивный центр + Спортивный центр;Спортцентр Поле для гольфа Каток - Спортивная площадка, поле, корт + Спортивная площадка, поле, корт;Спортплощадка Беговая дорожка Велотрек Ипподром @@ -421,7 +421,7 @@ Скалолазание Крикет Крокет - Велосипедный спорт + Велосипедный спорт;Велоспорт Прыжки в воду Плавание с аквалангом Собачьи бега @@ -464,7 +464,7 @@ Археологические раскопки Поле битвы Межевой камень - Замок + Замок;Крепость Городские ворота Фортификационные сооружения Фонтан @@ -474,10 +474,10 @@ Судно (музей) Историческая шахта Монумент (памятник) - Зоопарк + Зоопарк;Зверинец;Зоологический парк Парк развлечений - Гостиница, отель + Гостиница;Отель Гостевой дом Хостел Мотель @@ -502,8 +502,8 @@ Придорожная святыня Информация Часы - Турагентство - Место с хорошим видом + Турагентство;Туристическое агенство + Место с хорошим видом;Обзорная площадка Место для лагеря Ночлег Место для пикника @@ -550,7 +550,7 @@ Танцплощадка Ночной клуб;Диско Стрипклуб - Пляжный комплекс + Пляжный комплекс;Пляжный курорт Площадка для выгула собак Место для рыбалки Охотничья вышка @@ -627,18 +627,18 @@ Маникюр Парикмахерская Массажный салон - Тату салон + Тату салон;Татуировки Химчистка Прачечная Прокат автомобилей Кратковременный прокат автомобилей Прокат лодок - Туалет + Туалет;Санузел Сауна - Бордель + Бордель;Публичный дом Морг Крематорий - Интернет-кафе + Интернет-кафе;Интернет-салон;Киберкафе Банк Банкомат @@ -649,7 +649,7 @@ Оплата bitcoin Вход в пещеру - Горная вершина + Горная вершина;Пик Седловая точка (перевал) Вулкан Кратер @@ -720,7 +720,7 @@ Город (небольшой) Деревня Деревня (небольшая) - Пригород (suburb) + Пригород Хутор Урочище @@ -745,7 +745,7 @@ Веломотокросс Канадский футбол Серфинг - Исторические пушки + Историческая пушка Гавань Колесо обозрения Карусель @@ -832,7 +832,7 @@ Трасса для санных повозок Зимняя игровая площадка Прокат лыж - Место для курения + Место для курения;Курилка Канал Вода Лес @@ -852,7 +852,7 @@ Метан (CNG) Смесь 1:25 Смесь 1:50 - Фото магазин + Фотомагазин Энергетика Землепользование Мастерские @@ -905,7 +905,7 @@ Не используется по назначению Оператор - Торговая марка + Торговая марка;Бренд Оплата Да Нет @@ -1146,10 +1146,10 @@ Сад Пустошь - Газон + Газон;Трава Луг - Заросли кустарника - Сельскохозяйственные угодья + Заросли кустарника;Кусты + Сельскохозяйственные угодья;Сельхозугодья Вырубка Песок Общественная территория @@ -1171,7 +1171,7 @@ Волнорез Мол - Приют для животных + Приют для животных;Приют для бездомных животных Собаки Кошки @@ -1563,7 +1563,7 @@ Пеленальный столик Пеленальный столик отсутствует - Пеленальная комната + Пеленальная комната;Комната матери и ребёнка Максимальное время стоянки @@ -1694,7 +1694,7 @@ Поверхность Песок Галька - Камни + Каменистая;Камни Дымовая труба @@ -2465,9 +2465,9 @@ Место для хранения лодок Калитка - Фейсбук - Твиттер - Скайп + Фейсбук;Facebook + Твиттер;Twitter + Скайп;Skype Стиль сада: кухня Туалет: общественный Терминал @@ -2638,7 +2638,7 @@ Фитнес-центр Фитнес - Бильярд + Бильярд;Бильярдная Микроволновая печь: присутствует Микроволновая печь: отсутствует @@ -2981,7 +2981,7 @@ Только для инвалидных кресел Отвал породы - Детский летний лагерь + Детский летний лагерь;Детский оздоровительный лагерь;Пионерлагерь Оздоровительный центр Управление переездом: автоматическое @@ -3358,7 +3358,7 @@ Аварийная инфраструктура - Отвесная скала + Отвесная скала;Утёс;Крутой обрыв Место содержания животных Для лошадей @@ -3729,7 +3729,7 @@ Магазин каминов Горный массив - Ущелье + Ущелье;Кулуар;Ложбина Теснина Магазин лодок diff --git a/OsmAnd/src/net/osmand/plus/search/QuickSearchListAdapter.java b/OsmAnd/src/net/osmand/plus/search/QuickSearchListAdapter.java index bd64b7871a..d5b80bc07b 100644 --- a/OsmAnd/src/net/osmand/plus/search/QuickSearchListAdapter.java +++ b/OsmAnd/src/net/osmand/plus/search/QuickSearchListAdapter.java @@ -215,8 +215,14 @@ public class QuickSearchListAdapter extends ArrayAdapter { SearchUICore searchUICore = app.getSearchUICore().getCore(); SearchPhrase searchPhrase = searchUICore.getPhrase(); - String textTitle = app.getString(R.string.nothing_found_in_radius) + " " - + OsmAndFormatter.getFormattedDistance(searchUICore.getMinimalSearchRadius(searchPhrase), app); + String textTitle; + int minimalSearchRadius = searchUICore.getMinimalSearchRadius(searchPhrase); + if (searchUICore.isSearchMoreAvailable(searchPhrase) && minimalSearchRadius != Integer.MAX_VALUE) { + textTitle = app.getString(R.string.nothing_found_in_radius) + " " + + OsmAndFormatter.getFormattedDistance(minimalSearchRadius, app); + } else { + textTitle = app.getString(R.string.search_nothing_found); + } ((TextView) view.findViewById(R.id.empty_search_title)).setText(textTitle); View increaseRadiusRow = view.findViewById(R.id.increase_radius_row);