diff --git a/OsmAnd-java/src/net/osmand/search/SearchUICore.java b/OsmAnd-java/src/net/osmand/search/SearchUICore.java index cc390ac405..f449931012 100644 --- a/OsmAnd-java/src/net/osmand/search/SearchUICore.java +++ b/OsmAnd-java/src/net/osmand/search/SearchUICore.java @@ -40,24 +40,21 @@ import java.util.concurrent.atomic.AtomicInteger; public class SearchUICore { private static final int TIMEOUT_BETWEEN_CHARS = 200; - private static final Log LOG = PlatformUtil.getLog(SearchUICore.class); + private static final Log LOG = PlatformUtil.getLog(SearchUICore.class); private SearchPhrase phrase; - private SearchResultCollection currentSearchResult; - + private SearchResultCollection currentSearchResult; + private ThreadPoolExecutor singleThreadedExecutor; private LinkedBlockingQueue taskQueue; private Runnable onSearchStart = null; private Runnable onResultsComplete = null; private AtomicInteger requestNumber = new AtomicInteger(); private int totalLimit = -1; // -1 unlimited - not used - + List apis = new ArrayList<>(); private SearchSettings searchSettings; private MapPoiTypes poiTypes; - private SearchCoreFactory.SearchCityByNameAPI searchCityByNameAPI; - private SearchCoreFactory.SearchPostcodeAPI searchPostcodeAPI; - private Class searchApiClass; public SearchUICore(MapPoiTypes poiTypes, String locale, boolean transliterate) { this.poiTypes = poiTypes; @@ -69,82 +66,68 @@ public class SearchUICore { singleThreadedExecutor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, taskQueue); } - public Class getSearchApiClass() { - return searchApiClass; - } - - public void setSearchApiClass(Class searchApiClass) { - this.searchApiClass = searchApiClass; - } - - public boolean isInCustomSearch() { - return searchApiClass != null; - } - public static class SearchResultCollection { private List searchResults; private SearchPhrase phrase; private static final int DEPTH_TO_CHECK_SAME_SEARCH_RESULTS = 20; - + public SearchResultCollection(SearchPhrase phrase) { searchResults = new ArrayList<>(); this.phrase = phrase; } - + public SearchResultCollection combineWithCollection(SearchResultCollection collection, boolean resort, boolean removeDuplicates) { SearchResultCollection src = new SearchResultCollection(phrase); src.addSearchResults(searchResults, false, false); src.addSearchResults(collection.searchResults, resort, removeDuplicates); return src; } - - - + public SearchResultCollection addSearchResults(List sr, boolean resortAll, boolean removeDuplicates) { - if(resortAll) { + if (resortAll) { this.searchResults.addAll(sr); sortSearchResults(); - if(removeDuplicates) { + if (removeDuplicates) { filterSearchDuplicateResults(); } } else { - if(!removeDuplicates) { + if (!removeDuplicates) { this.searchResults.addAll(sr); } else { ArrayList addedResults = new ArrayList<>(sr); SearchResultComparator cmp = new SearchResultComparator(phrase); Collections.sort(addedResults, cmp); filterSearchDuplicateResults(addedResults); - int i = 0; + int i = 0; int j = 0; - while(j < addedResults.size()) { + while (j < addedResults.size()) { SearchResult addedResult = addedResults.get(j); - if(i >= searchResults.size()) { + if (i >= searchResults.size()) { int k = 0; boolean same = false; - while(searchResults.size() > k && k < DEPTH_TO_CHECK_SAME_SEARCH_RESULTS) { - if(sameSearchResult(addedResult, searchResults.get(searchResults.size() - k - 1))) { + while (searchResults.size() > k && k < DEPTH_TO_CHECK_SAME_SEARCH_RESULTS) { + if (sameSearchResult(addedResult, searchResults.get(searchResults.size() - k - 1))) { same = true; break; } k++; } - if(!same) { + if (!same) { searchResults.add(addedResult); } j++; continue; } SearchResult existingResult = searchResults.get(i); - if(sameSearchResult(addedResult, existingResult)) { + if (sameSearchResult(addedResult, existingResult)) { j++; continue; } int compare = cmp.compare(existingResult, addedResult); - if(compare == 0) { + if (compare == 0) { // existingResult == addedResult j++; - } else if(compare > 0) { + } else if (compare > 0) { // existingResult > addedResult this.searchResults.add(addedResults.get(j)); j++; @@ -153,61 +136,58 @@ public class SearchUICore { i++; } } - } } return this; } - + public List getCurrentSearchResults() { return Collections.unmodifiableList(searchResults); } - + public SearchPhrase getPhrase() { return phrase; } public void sortSearchResults() { - Collections.sort(searchResults, new SearchResultComparator(phrase)); + Collections.sort(searchResults, new SearchResultComparator(phrase)); } public void filterSearchDuplicateResults() { filterSearchDuplicateResults(searchResults); } - + private void filterSearchDuplicateResults(List lst) { ListIterator it = lst.listIterator(); LinkedList lstUnique = new LinkedList(); - while(it.hasNext()) { + while (it.hasNext()) { SearchResult r = it.next(); boolean same = false; - for(SearchResult rs : lstUnique) { + for (SearchResult rs : lstUnique) { same = sameSearchResult(rs, r); - if(same) { + if (same) { break; } } - if(same) { + if (same) { it.remove(); } else { lstUnique.add(r); - if(lstUnique.size() > DEPTH_TO_CHECK_SAME_SEARCH_RESULTS) { + if (lstUnique.size() > DEPTH_TO_CHECK_SAME_SEARCH_RESULTS) { lstUnique.remove(0); } } - } + } } - - - + public boolean sameSearchResult(SearchResult r1, SearchResult r2) { - if(r1.location != null && r2.location != null) { + if (r1.location != null && r2.location != null) { Amenity a1 = null; - if(r1.object instanceof Amenity) { + if (r1.object instanceof Amenity) { a1 = (Amenity) r1.object; } Amenity a2 = null; - if(r2.object instanceof Amenity) { + if (r2.object instanceof Amenity) { a2 = (Amenity) r2.object; } if (r1.localeName.equals(r2.localeName)) { @@ -229,7 +209,7 @@ public class SearchUICore { similarityRadius = 50000; } } - } else if(ObjectType.isAddress(r1.objectType) && ObjectType.isAddress(r2.objectType)) { + } else if (ObjectType.isAddress(r1.objectType) && ObjectType.isAddress(r2.objectType)) { similarityRadius = 100; } return MapUtils.getDistance(r1.location, r2.location) < similarityRadius; @@ -240,44 +220,39 @@ public class SearchUICore { return false; } } - + public void setPoiTypes(MapPoiTypes poiTypes) { this.poiTypes = poiTypes; } - + public int getTotalLimit() { return totalLimit; } - + public void setTotalLimit(int totalLimit) { this.totalLimit = totalLimit; } - + @SuppressWarnings("unchecked") public T getApiByClass(Class cl) { for (SearchCoreAPI a : apis) { - if(cl.isInstance(a)) { + if (cl.isInstance(a)) { return (T) a; } } - if (cl.isInstance(searchCityByNameAPI)) { - return (T) searchCityByNameAPI; - } else if (cl.isInstance(searchPostcodeAPI)) { - return (T) searchPostcodeAPI; - } return null; } - + public SearchResultCollection shallowSearch(Class cl, - String text, final ResultMatcher matcher) throws IOException { + String text, final ResultMatcher matcher) throws IOException { T api = getApiByClass(cl); - if(api != null) { + if (api != null) { SearchPhrase sphrase = this.phrase.generateNewPhrase(text, searchSettings); preparePhrase(sphrase); AtomicInteger ai = new AtomicInteger(); SearchResultMatcher rm = new SearchResultMatcher(matcher, ai.get(), ai, totalLimit); api.search(sphrase, rm); - + SearchResultCollection collection = new SearchResultCollection( sphrase); collection.addSearchResults(rm.getRequestResults(), true, true); @@ -286,49 +261,45 @@ public class SearchUICore { } return null; } - + public void init() { apis.add(new SearchCoreFactory.SearchLocationAndUrlAPI()); apis.add(new SearchCoreFactory.SearchAmenityTypesAPI(poiTypes)); apis.add(new SearchCoreFactory.SearchAmenityByTypeAPI(poiTypes)); apis.add(new SearchCoreFactory.SearchAmenityByNameAPI()); - SearchBuildingAndIntersectionsByStreetAPI streetsApi = + SearchBuildingAndIntersectionsByStreetAPI streetsApi = new SearchCoreFactory.SearchBuildingAndIntersectionsByStreetAPI(); apis.add(streetsApi); SearchStreetByCityAPI cityApi = new SearchCoreFactory.SearchStreetByCityAPI(streetsApi); apis.add(cityApi); - SearchCoreFactory.SearchAddressByNameAPI searchAddressByNameAPI - = new SearchCoreFactory.SearchAddressByNameAPI(streetsApi, cityApi); - apis.add(searchAddressByNameAPI); - searchCityByNameAPI = new SearchCoreFactory.SearchCityByNameAPI(searchAddressByNameAPI); - searchPostcodeAPI = new SearchCoreFactory.SearchPostcodeAPI(); + apis.add(new SearchCoreFactory.SearchAddressByNameAPI(streetsApi, cityApi)); } public void clearCustomSearchPoiFilters() { - for(SearchCoreAPI capi : apis) { - if(capi instanceof SearchAmenityTypesAPI) { + for (SearchCoreAPI capi : apis) { + if (capi instanceof SearchAmenityTypesAPI) { ((SearchAmenityTypesAPI) capi).clearCustomFilters(); } } } public void addCustomSearchPoiFilter(CustomSearchPoiFilter poiFilter, int priority) { - for(SearchCoreAPI capi : apis) { - if(capi instanceof SearchAmenityTypesAPI) { + for (SearchCoreAPI capi : apis) { + if (capi instanceof SearchAmenityTypesAPI) { ((SearchAmenityTypesAPI) capi).addCustomFilter(poiFilter, priority); } } } - + public void registerAPI(SearchCoreAPI api) { apis.add(api); } - - + + public SearchResultCollection getCurrentSearchResult() { return currentSearchResult; } - + public SearchPhrase getPhrase() { return phrase; } @@ -348,27 +319,27 @@ public class SearchUICore { public void updateSettings(SearchSettings settings) { searchSettings = settings; } - + private List filterCurrentResults(List rr, SearchPhrase phrase) { List l = currentSearchResult.searchResults; - for(SearchResult r : l) { - if(filterOneResult(r, phrase)) { + for (SearchResult r : l) { + if (filterOneResult(r, phrase)) { rr.add(r); } } return rr; } - + private boolean filterOneResult(SearchResult object, SearchPhrase phrase) { NameStringMatcher nameStringMatcher = phrase.getNameStringMatcher(); - return nameStringMatcher.matches(object.localeName) || nameStringMatcher.matches(object.otherNames); + return nameStringMatcher.matches(object.localeName) || nameStringMatcher.matches(object.otherNames); } public boolean selectSearchResult(SearchResult r) { this.phrase = this.phrase.selectWord(r); return true; } - + public SearchPhrase resetPhrase() { this.phrase = this.phrase.generateNewPhrase("", searchSettings); return this.phrase; @@ -395,7 +366,7 @@ public class SearchUICore { onSearchStart.run(); } SearchResultMatcher rm = new SearchResultMatcher(matcher, request, requestNumber, totalLimit); - if(TIMEOUT_BETWEEN_CHARS > 0) { + if(TIMEOUT_BETWEEN_CHARS > 0) { Thread.sleep(TIMEOUT_BETWEEN_CHARS); } if(rm.isCancelled()) { @@ -403,7 +374,7 @@ public class SearchUICore { } searchInBackground(phrase, rm); if (!rm.isCancelled()) { - + SearchResultCollection collection = new SearchResultCollection( phrase); collection.addSearchResults(rm.getRequestResults(), true, true); @@ -419,15 +390,15 @@ public class SearchUICore { } - + }); return quickRes; } - - + + public boolean isSearchMoreAvailable(SearchPhrase phrase) { - for(SearchCoreAPI api : apis) { - if(api.getSearchPriority(phrase) >= 0 && api.isSearchMoreAvailable(phrase)) { + for (SearchCoreAPI api : apis) { + if (api.getSearchPriority(phrase) >= 0 && api.isSearchMoreAvailable(phrase)) { return true; } } @@ -436,12 +407,7 @@ public class SearchUICore { private void searchInBackground(final SearchPhrase phrase, SearchResultMatcher matcher) { preparePhrase(phrase); - ArrayList lst = new ArrayList<>(); - if (searchApiClass != null) { - lst.add(getApiByClass(searchApiClass)); - } else { - lst.addAll(apis); - } + ArrayList lst = new ArrayList<>(apis); Collections.sort(lst, new Comparator() { @Override @@ -459,7 +425,7 @@ public class SearchUICore { } try { api.search(phrase, matcher); - + matcher.apiSearchFinished(api, phrase); } catch (Throwable e) { e.printStackTrace(); @@ -470,16 +436,16 @@ public class SearchUICore { private void preparePhrase(final SearchPhrase phrase) { for (SearchWord sw : phrase.getWords()) { - if(sw.getResult() != null && sw.getResult().file != null) { + if (sw.getResult() != null && sw.getResult().file != null) { phrase.selectFile(sw.getResult().file); } } phrase.sortFiles(); } - - - - + + + + public static class SearchResultMatcher implements ResultMatcher{ private final List requestResults = new ArrayList<>(); private final ResultMatcher matcher; @@ -488,32 +454,32 @@ public class SearchUICore { private SearchResult parentSearchResult; private final AtomicInteger requestNumber; int count = 0; - - - public SearchResultMatcher(ResultMatcher matcher, int request, - AtomicInteger requestNumber, int totalLimit) { + + + public SearchResultMatcher(ResultMatcher matcher, int request, + AtomicInteger requestNumber, int totalLimit) { this.matcher = matcher; this.request = request; this.requestNumber = requestNumber; this.totalLimit = totalLimit; } - + public SearchResult setParentSearchResult(SearchResult parentSearchResult) { SearchResult prev = this.parentSearchResult; this.parentSearchResult = parentSearchResult; return prev; } - + public List getRequestResults() { return requestResults; } - + public int getCount() { return requestResults.size(); } - + public void apiSearchFinished(SearchCoreAPI api, SearchPhrase phrase) { - if(matcher != null) { + if (matcher != null) { SearchResult sr = new SearchResult(phrase); sr.objectType = ObjectType.SEARCH_API_FINISHED; sr.object = api; @@ -521,7 +487,7 @@ public class SearchUICore { matcher.publish(sr); } } - + public void apiSearchRegionFinished(SearchCoreAPI api, BinaryMapIndexReader region, SearchPhrase phrase) { if(matcher != null) { SearchResult sr = new SearchResult(phrase); @@ -532,14 +498,14 @@ public class SearchUICore { matcher.publish(sr); } } - + @Override public boolean publish(SearchResult object) { - if(matcher == null || matcher.publish(object)) { + if (matcher == null || matcher.publish(object)) { count++; object.parentSearchResult = parentSearchResult; - if(totalLimit == -1 || count < totalLimit) { - requestResults.add(object); + if (totalLimit == -1 || count < totalLimit) { + requestResults.add(object); } return true; } @@ -549,38 +515,42 @@ public class SearchUICore { public boolean isCancelled() { boolean cancelled = request != requestNumber.get(); return cancelled || (matcher != null && matcher.isCancelled()); - } + } } - + public static class SearchResultComparator implements Comparator { private SearchPhrase sp; private Collator collator; private LatLon loc; - + private boolean sortByName; + public SearchResultComparator(SearchPhrase sp) { this.sp = sp; this.collator = sp.getCollator(); loc = sp.getLastTokenLocation(); + sortByName = sp.isSortByName(); } @Override public int compare(SearchResult o1, SearchResult o2) { - if(o1.getFoundWordCount() != o2.getFoundWordCount()) { + if (o1.getFoundWordCount() != o2.getFoundWordCount()) { return -Algorithms.compare(o1.getFoundWordCount(), o2.getFoundWordCount()); } - double s1 = o1.getSearchDistance(loc); - double s2 = o2.getSearchDistance(loc); - int cmp = Double.compare(s1, s2); - if(cmp != 0) { - return cmp; + if (!sortByName || sp.isEmpty()) { + double s1 = o1.getSearchDistance(loc); + double s2 = o2.getSearchDistance(loc); + int cmp = Double.compare(s1, s2); + if (cmp != 0) { + return cmp; + } } int st1 = Algorithms.extractFirstIntegerNumber(o1.localeName); int st2 = Algorithms.extractFirstIntegerNumber(o2.localeName); - if(st1 != st2) { + if (st1 != st2) { return Algorithms.compare(st1, st2); } - cmp = collator.compare(o1.localeName, o2.localeName); - if(cmp != 0) { + int cmp = collator.compare(o1.localeName, o2.localeName); + if (cmp != 0) { return cmp; } if (o1.object instanceof Amenity && o2.object instanceof Amenity) { @@ -592,19 +562,19 @@ public class SearchUICore { String subType1 = a1.getSubType(); String subType2 = a2.getSubType(); cmp = collator.compare(type1, type2); - if(cmp != 0) { + if (cmp != 0) { return cmp; } cmp = collator.compare(subType1, subType2); - if(cmp != 0) { + if (cmp != 0) { return cmp; } } - s1 = o1.getSearchDistance(loc, 1); - s2 = o2.getSearchDistance(loc, 1); + double s1 = o1.getSearchDistance(loc, 1); + double s2 = o2.getSearchDistance(loc, 1); return Double.compare(s1, s2); } - + } } diff --git a/OsmAnd-java/src/net/osmand/search/core/ObjectType.java b/OsmAnd-java/src/net/osmand/search/core/ObjectType.java index b9f21f01d1..95c16bb81b 100644 --- a/OsmAnd-java/src/net/osmand/search/core/ObjectType.java +++ b/OsmAnd-java/src/net/osmand/search/core/ObjectType.java @@ -2,7 +2,7 @@ package net.osmand.search.core; public enum ObjectType { // ADDRESS - CITY(true), VILLAGE(true), POSTCODE(true), STREET(true), HOUSE(true),STREET_INTERSECTION(true), + CITY(true), VILLAGE(true), POSTCODE(true), STREET(true), HOUSE(true), STREET_INTERSECTION(true), // POI POI_TYPE(false), POI(true), // LOCATION @@ -10,9 +10,12 @@ public enum ObjectType { // UI OBJECTS FAVORITE(true), WPT(true), RECENT_OBJ(true), - REGION(true), - SEARCH_API_FINISHED(false), SEARCH_API_REGION_FINISHED(false), + REGION(true), + + SEARCH_API_FINISHED(false), + SEARCH_API_REGION_FINISHED(false), UNKNOWN_NAME_FILTER(false); + private boolean hasLocation; private ObjectType(boolean location) { this.hasLocation = location; @@ -21,8 +24,7 @@ public enum ObjectType { return hasLocation; } - public static boolean isAddress(ObjectType t ) { + public static boolean isAddress(ObjectType t) { return t == CITY || t == VILLAGE || t == POSTCODE || t == STREET || t == HOUSE || t == STREET_INTERSECTION; } - } diff --git a/OsmAnd-java/src/net/osmand/search/core/SearchCoreAPI.java b/OsmAnd-java/src/net/osmand/search/core/SearchCoreAPI.java index df783735da..4ad0c0fb98 100644 --- a/OsmAnd-java/src/net/osmand/search/core/SearchCoreAPI.java +++ b/OsmAnd-java/src/net/osmand/search/core/SearchCoreAPI.java @@ -20,4 +20,5 @@ public interface SearchCoreAPI { */ public boolean isSearchMoreAvailable(SearchPhrase phrase); + boolean isSearchAvailable(ObjectType[] typesToSearch); } diff --git a/OsmAnd-java/src/net/osmand/search/core/SearchCoreFactory.java b/OsmAnd-java/src/net/osmand/search/core/SearchCoreFactory.java index cba177fccb..32bca1d208 100644 --- a/OsmAnd-java/src/net/osmand/search/core/SearchCoreFactory.java +++ b/OsmAnd-java/src/net/osmand/search/core/SearchCoreFactory.java @@ -16,7 +16,6 @@ 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.QuadRect; import net.osmand.data.QuadTree; import net.osmand.data.Street; @@ -49,78 +48,112 @@ import gnu.trove.list.array.TIntArrayList; public class SearchCoreFactory { - + public static final int MAX_DEFAULT_SEARCH_RADIUS = 7; - + //////////////// CONSTANTS ////////// public static final int SEARCH_REGION_API_PRIORITY = 300; public static final int SEARCH_REGION_OBJECT_PRIORITY = 1000; - + // context less public static final int SEARCH_LOCATION_PRIORITY = 0; public static final int SEARCH_AMENITY_TYPE_PRIORITY = 100; public static final int SEARCH_AMENITY_TYPE_API_PRIORITY = 100; - - // context + + // context public static final int SEARCH_STREET_BY_CITY_PRIORITY = 200; public static final int SEARCH_BUILDING_BY_CITY_PRIORITY = 300; public static final int SEARCH_BUILDING_BY_STREET_PRIORITY = 100; public static final int SEARCH_AMENITY_BY_TYPE_PRIORITY = 300; - + // context less (slow) public static final int SEARCH_ADDRESS_BY_NAME_API_PRIORITY = 500; public static final int SEARCH_ADDRESS_BY_NAME_API_PRIORITY_RADIUS2 = 500; public static final int SEARCH_ADDRESS_BY_NAME_PRIORITY = 500; public static final int SEARCH_ADDRESS_BY_NAME_PRIORITY_RADIUS2 = 500; - - // context less (slower) + + // context less (slower) public static final int SEARCH_AMENITY_BY_NAME_PRIORITY = 700; public static final int SEARCH_AMENITY_BY_NAME_API_PRIORITY_IF_POI_TYPE = 700; public static final int SEARCH_AMENITY_BY_NAME_API_PRIORITY_IF_3_CHAR = 700; protected static final double SEARCH_AMENITY_BY_NAME_CITY_PRIORITY_DISTANCE = 0.001; protected static final double SEARCH_AMENITY_BY_NAME_TOWN_PRIORITY_DISTANCE = 0.005; - + public static abstract class SearchBaseAPI implements SearchCoreAPI { + + private ObjectType[] searchTypes; + + protected SearchBaseAPI(ObjectType... searchTypes) { + if (searchTypes == null) { + throw new IllegalArgumentException("Search types are not defined for search core API"); + } + this.searchTypes = searchTypes; + } + + @Override + public boolean isSearchAvailable(ObjectType... typesToSearch) { + if (typesToSearch == null) { + return true; + } else { + for (ObjectType type : searchTypes) { + for (ObjectType ts : typesToSearch) { + if (type == ts) { + return true; + } + } + } + return false; + } + } + @Override public boolean search(SearchPhrase phrase, SearchResultMatcher resultMatcher) throws IOException { - return true; + return isSearchAvailable(phrase.getSearchTypes()); } - + @Override public int getSearchPriority(SearchPhrase p) { return 1; } - + @Override public boolean isSearchMoreAvailable(SearchPhrase phrase) { return phrase.getRadiusLevel() < MAX_DEFAULT_SEARCH_RADIUS; } - - protected void subSearchApiOrPublish(SearchPhrase phrase, - SearchResultMatcher resultMatcher, SearchResult res, SearchBaseAPI api) + + protected void subSearchApiOrPublish(SearchPhrase phrase, + SearchResultMatcher resultMatcher, SearchResult res, SearchBaseAPI api) throws IOException { phrase.countUnknownWordsMatch(res); int cnt = resultMatcher.getCount(); List ws = phrase.getUnknownSearchWords(res.otherWordsMatch); - if(!ws.isEmpty() && api != null) { - SearchPhrase nphrase = phrase.selectWord(res, ws, + if (!ws.isEmpty() && api != null && api.isSearchAvailable(phrase.getSearchTypes())) { + SearchPhrase nphrase = phrase.selectWord(res, ws, phrase.isLastUnknownSearchWordComplete()); SearchResult prev = resultMatcher.setParentSearchResult(res); - res.parentSearchResult = prev; + res.parentSearchResult = prev; api.search(nphrase, resultMatcher); resultMatcher.setParentSearchResult(prev); } - if(resultMatcher.getCount() == cnt) { + if (resultMatcher.getCount() == cnt) { resultMatcher.publish(res); } } } - - + + + public static class SearchRegionByNameAPI extends SearchBaseAPI { + public SearchRegionByNameAPI() { + super(ObjectType.REGION); + } + @Override - public boolean search(SearchPhrase phrase, SearchResultMatcher resultMatcher) { + public boolean search(SearchPhrase phrase, SearchResultMatcher resultMatcher) throws IOException { + if (!super.search(phrase, resultMatcher)) { + return false; + } for (BinaryMapIndexReader bmir : phrase.getOfflineIndexes()) { if (bmir.getRegionCenter() != null) { SearchResult sr = new SearchResult(phrase); @@ -140,12 +173,12 @@ public class SearchCoreFactory { } return true; } - + @Override public boolean isSearchMoreAvailable(SearchPhrase phrase) { return false; } - + @Override public int getSearchPriority(SearchPhrase p) { if(!p.isNoSelectedType()) { @@ -154,7 +187,7 @@ public class SearchCoreFactory { return SEARCH_REGION_API_PRIORITY; } } - + private static String stripBraces(String localeName) { int i = localeName.indexOf('('); String retName = localeName; @@ -168,137 +201,24 @@ public class SearchCoreFactory { return retName; } - public static class SearchPostcodeAPI extends SearchBaseAPI { - private static final int LIMIT = 10000; - - @Override - public boolean search(final SearchPhrase phrase, final SearchResultMatcher resultMatcher) throws IOException { - if (!phrase.isUnknownSearchWordPresent()) { - return false; - } - if (phrase.isNoSelectedType()) { - final NameStringMatcher nm = phrase.getNameStringMatcher(); - final QuadRect bbox = phrase.getRadiusBBoxToSearch(SearchAddressByNameAPI.DEFAULT_ADDRESS_BBOX_RADIUS * 20); - final QuadRect postcodeBbox = phrase.getRadiusBBoxToSearch(SearchAddressByNameAPI.DEFAULT_ADDRESS_BBOX_RADIUS * 5); - Iterator offlineIndexes = phrase.getOfflineIndexes(bbox, SearchPhraseDataType.ADDRESS); - while (offlineIndexes.hasNext()) { - BinaryMapIndexReader r = offlineIndexes.next(); - SearchRequest req = BinaryMapIndexReader.buildAddressRequest(new ResultMatcher() { - @Override - public boolean publish(City object) { - return object.isPostcode() - && !Algorithms.isEmpty(object.getName()) - && nm.matches(object.getName()); - } - - @Override - public boolean isCancelled() { - return resultMatcher.isCancelled(); - } - }); - List l = r.getCities(req, BinaryMapAddressReaderAdapter.POSTCODES_TYPE); - int limit = 0; - for (City c : l) { - LatLon cl = c.getLocation(); - c.setReferenceFile(r); - int y = MapUtils.get31TileNumberY(cl.getLatitude()); - int x = MapUtils.get31TileNumberX(cl.getLongitude()); - if (postcodeBbox.contains(x, y, x, y)) { - SearchResult res = new SearchResult(phrase); - res.object = c; - res.file = (BinaryMapIndexReader) c.getReferenceFile(); - res.localeName = c.getName(phrase.getSettings().getLang(), phrase.getSettings().isTransliterate()); - res.otherNames = c.getAllNames(true); - res.localeRelatedObjectName = res.file.getRegionName(); - res.relatedObject = res.file; - res.location = c.getLocation(); - res.priority = SEARCH_ADDRESS_BY_NAME_PRIORITY; - res.priorityDistance = 0.1; - res.objectType = ObjectType.POSTCODE; - resultMatcher.publish(res); - - if (limit++ > LIMIT * phrase.getRadiusLevel()) { - break; - } - } - } - } - } - return true; - } - } - - public static class SearchCityByNameAPI extends SearchBaseAPI { - - private SearchAddressByNameAPI searchAddressByNameAPI; - private static final int LIMIT = 10000; - - public SearchCityByNameAPI(SearchAddressByNameAPI searchAddressByNameAPI) { - this.searchAddressByNameAPI = searchAddressByNameAPI; - } - - @Override - public boolean search(final SearchPhrase phrase, final SearchResultMatcher resultMatcher) throws IOException { - QuadRect bbox = phrase.getRadiusBBoxToSearch(SearchAddressByNameAPI.DEFAULT_ADDRESS_BBOX_RADIUS * 20); - QuadRect closestBBox = phrase.getRadiusBBoxToSearch(SearchAddressByNameAPI.DEFAULT_ADDRESS_BBOX_RADIUS * 3); - if (phrase.isNoSelectedType()) { - searchAddressByNameAPI.initCities(phrase, bbox); - } - NameStringMatcher nm = null; - List cities; - if (phrase.isUnknownSearchWordPresent()) { - nm = phrase.getNameStringMatcher(); - cities = searchAddressByNameAPI.townCitiesQR.queryInBox(bbox, new ArrayList()); - } else { - cities = searchAddressByNameAPI.townCitiesQR.queryInBox(closestBBox, new ArrayList()); - } - int limit = 0; - for (City c : cities) { - SearchResult res = new SearchResult(phrase); - res.object = c; - res.file = (BinaryMapIndexReader) c.getReferenceFile(); - res.localeName = c.getName(phrase.getSettings().getLang(), phrase.getSettings().isTransliterate()); - res.otherNames = c.getAllNames(true); - res.localeRelatedObjectName = res.file.getRegionName(); - res.relatedObject = res.file; - res.location = c.getLocation(); - res.priority = SEARCH_ADDRESS_BY_NAME_PRIORITY; - res.priorityDistance = 0.1; - res.objectType = ObjectType.CITY; - if (nm == null) { - resultMatcher.publish(res); - } else if (nm.matches(res.localeName) || nm.matches(res.otherNames)) { - resultMatcher.publish(res); - } - if (limit++ > LIMIT * phrase.getRadiusLevel()) { - break; - } - } - return true; - } - - @Override - public int getSearchPriority(SearchPhrase p) { - return searchAddressByNameAPI.getSearchPriority(p); - } - } public static class SearchAddressByNameAPI extends SearchBaseAPI { - + private static final int DEFAULT_ADDRESS_BBOX_RADIUS = 100 * 1000; private static final int LIMIT = 10000; - - + private Map> townCities = new LinkedHashMap<>(); private QuadTree townCitiesQR = new QuadTree(new QuadRect(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE), 8, 0.55f); private List resArray = new ArrayList<>(); private SearchStreetByCityAPI cityApi; private SearchBuildingAndIntersectionsByStreetAPI streetsApi; - - public SearchAddressByNameAPI(SearchBuildingAndIntersectionsByStreetAPI streetsApi, - SearchStreetByCityAPI cityApi) { + + public SearchAddressByNameAPI(SearchBuildingAndIntersectionsByStreetAPI streetsApi, + SearchStreetByCityAPI cityApi) { + super(ObjectType.CITY, ObjectType.VILLAGE, ObjectType.POSTCODE, + ObjectType.STREET, ObjectType.HOUSE, ObjectType.STREET_INTERSECTION); this.streetsApi = streetsApi; this.cityApi = cityApi; } @@ -316,7 +236,7 @@ public class SearchCoreFactory { } return SEARCH_ADDRESS_BY_NAME_API_PRIORITY_RADIUS2; } - + @Override public boolean isSearchMoreAvailable(SearchPhrase phrase) { // case when street is not found for given city is covered by SearchStreetByCityAPI @@ -325,7 +245,10 @@ public class SearchCoreFactory { @Override public boolean search(final SearchPhrase phrase, final SearchResultMatcher resultMatcher) throws IOException { - if (!phrase.isUnknownSearchWordPresent()) { + if (!super.search(phrase, resultMatcher)) { + return false; + } + if (!phrase.isUnknownSearchWordPresent() && !phrase.isEmptyQueryAllowed()) { return false; } // phrase.isLastWord(ObjectType.CITY, ObjectType.VILLAGE, ObjectType.POSTCODE) || phrase.isLastWord(ObjectType.REGION) @@ -338,7 +261,8 @@ public class SearchCoreFactory { return true; } - protected void initCities(final SearchPhrase phrase, QuadRect bbox) throws IOException { + private void initAndSearchCities(final SearchPhrase phrase, final SearchResultMatcher resultMatcher) throws IOException { + QuadRect bbox = phrase.getRadiusBBoxToSearch(DEFAULT_ADDRESS_BBOX_RADIUS * 20); Iterator offlineIndexes = phrase.getOfflineIndexes(bbox, SearchPhraseDataType.ADDRESS); while (offlineIndexes.hasNext()) { BinaryMapIndexReader r = offlineIndexes.next(); @@ -356,13 +280,9 @@ public class SearchCoreFactory { } } } - } - - private void initAndSearchCities(final SearchPhrase phrase, final SearchResultMatcher resultMatcher) throws IOException { - QuadRect bbox = phrase.getRadiusBBoxToSearch(DEFAULT_ADDRESS_BBOX_RADIUS * 20); - initCities(phrase, bbox); - - if (phrase.isNoSelectedType() && bbox != null && phrase.isUnknownSearchWordPresent()) { + if (phrase.isNoSelectedType() && bbox != null + && (phrase.isUnknownSearchWordPresent() || phrase.isEmptyQueryAllowed()) + && phrase.isSearchTypeAllowed(ObjectType.CITY)) { NameStringMatcher nm = phrase.getNameStringMatcher(); resArray.clear(); resArray = townCitiesQR.queryInBox(bbox, resArray); @@ -379,7 +299,9 @@ public class SearchCoreFactory { res.priority = SEARCH_ADDRESS_BY_NAME_PRIORITY; res.priorityDistance = 0.1; res.objectType = ObjectType.CITY; - if (nm.matches(res.localeName) || nm.matches(res.otherNames)) { + if (phrase.isEmptyQueryAllowed() && phrase.isEmpty()) { + resultMatcher.publish(res); + } else if (nm.matches(res.localeName) || nm.matches(res.otherNames)) { subSearchApiOrPublish(phrase, resultMatcher, res, cityApi); } if (limit++ > LIMIT * phrase.getRadiusLevel()) { @@ -389,6 +311,7 @@ public class SearchCoreFactory { } } + private void searchByName(final SearchPhrase phrase, final SearchResultMatcher resultMatcher) throws IOException { if (phrase.getRadiusLevel() > 1 || phrase.getUnknownSearchWordLength() > 3 || phrase.getUnknownSearchWords().size() > 0) { @@ -399,9 +322,9 @@ public class SearchCoreFactory { final QuadRect postcodeBbox = phrase.getRadiusBBoxToSearch(DEFAULT_ADDRESS_BBOX_RADIUS * 5); final QuadRect villagesBbox = phrase.getRadiusBBoxToSearch(DEFAULT_ADDRESS_BBOX_RADIUS * 3); final QuadRect cityBbox = phrase.getRadiusBBoxToSearch(DEFAULT_ADDRESS_BBOX_RADIUS * 5); // covered by separate search before - final int priority = phrase.isNoSelectedType() ? + final int priority = phrase.isNoSelectedType() ? SEARCH_ADDRESS_BY_NAME_PRIORITY : SEARCH_ADDRESS_BY_NAME_PRIORITY_RADIUS2; - final BinaryMapIndexReader[] currentFile = new BinaryMapIndexReader[1]; + final BinaryMapIndexReader[] currentFile = new BinaryMapIndexReader[1]; ResultMatcher rm = new ResultMatcher() { int limit = 0; @Override @@ -423,13 +346,14 @@ public class SearchCoreFactory { int x = MapUtils.get31TileNumberX(object.getLocation().getLongitude()); List closestCities = null; if (object instanceof Street) { - if(locSpecified && !streetBbox.contains(x, y, x, y)) { + if ((locSpecified && !streetBbox.contains(x, y, x, y)) + || !phrase.isSearchTypeAllowed(ObjectType.STREET)) { return false; } - if(object.getName().startsWith("<")) { + if (object.getName().startsWith("<")) { return false; } - if(!phrase.getNameStringMatcher().matches(stripBraces(sr.localeName))) { + if (!phrase.getNameStringMatcher().matches(stripBraces(sr.localeName))) { sr.priorityDistance = 5; } sr.objectType = ObjectType.STREET; @@ -438,23 +362,26 @@ public class SearchCoreFactory { } else if (object instanceof City) { CityType type = ((City)object).getType(); if (type == CityType.CITY || type == CityType.TOWN) { - if(phrase.isNoSelectedType()) { + if (phrase.isNoSelectedType()) { // ignore city/town return false; } - if (locSpecified && !cityBbox.contains(x, y, x, y)) { + if ((locSpecified && !cityBbox.contains(x, y, x, y)) + || !phrase.isSearchTypeAllowed(ObjectType.CITY)) { return false; } - + sr.objectType = ObjectType.CITY; sr.priorityDistance = 0.1; } else if (((City)object).isPostcode()) { - if (locSpecified && !postcodeBbox.contains(x, y, x, y)) { + if ((locSpecified && !postcodeBbox.contains(x, y, x, y)) + || !phrase.isSearchTypeAllowed(ObjectType.POSTCODE)) { return false; } sr.objectType = ObjectType.POSTCODE; } else { - if (locSpecified && !villagesBbox.contains(x, y, x, y)) { + if ((locSpecified && !villagesBbox.contains(x, y, x, y)) + || !phrase.isSearchTypeAllowed(ObjectType.VILLAGE)) { return false; } City c = null; @@ -463,7 +390,7 @@ public class SearchCoreFactory { } double minDist = -1; double pDist = -1; - for(City s : closestCities) { + for (City s : closestCities) { double ll = MapUtils.getDistance(s.getLocation(), object.getLocation()); double pd = s.getType() == CityType.CITY ? ll : ll * 10; if(minDist == -1 || pd < pDist) { @@ -472,10 +399,10 @@ public class SearchCoreFactory { pDist = pd ; } } - if(c != null) { + if (c != null) { sr.localeRelatedObjectName = c.getName(phrase.getSettings().getLang(), phrase.getSettings().isTransliterate()); sr.relatedObject = c; - sr.distRelatedObjectName = minDist; + sr.distRelatedObjectName = minDist; } sr.objectType = ObjectType.VILLAGE; } @@ -487,22 +414,22 @@ public class SearchCoreFactory { return false; } - + @Override public boolean isCancelled() { - return limit > LIMIT * phrase.getRadiusLevel() || + return limit > LIMIT * phrase.getRadiusLevel() || resultMatcher.isCancelled(); } }; - Iterator offlineIterator = phrase.getRadiusOfflineIndexes(DEFAULT_ADDRESS_BBOX_RADIUS * 5, + Iterator offlineIterator = phrase.getRadiusOfflineIndexes(DEFAULT_ADDRESS_BBOX_RADIUS * 5, SearchPhraseDataType.ADDRESS); while (offlineIterator.hasNext()) { BinaryMapIndexReader r = offlineIterator.next(); currentFile[0] = r; immediateResults.clear(); SearchRequest req = BinaryMapIndexReader.buildAddressByNameRequest(rm, phrase - .getUnknownSearchWord().toLowerCase(), + .getUnknownSearchWord().toLowerCase(), phrase.isUnknownSearchWordComplete() ? StringMatcherMode.CHECK_EQUALS_FROM_SPACE : StringMatcherMode.CHECK_STARTS_FROM_SPACE); if (locSpecified) { @@ -511,7 +438,7 @@ public class SearchCoreFactory { } r.searchAddressDataByName(req); for (SearchResult res : immediateResults) { - if(res.objectType == ObjectType.STREET) { + if (res.objectType == ObjectType.STREET) { City ct = ((Street) res.object).getCity(); phrase.countUnknownWordsMatch(res, ct.getName(phrase.getSettings().getLang(), phrase.getSettings().isTransliterate()), ct.getAllNames(true)); @@ -524,42 +451,48 @@ public class SearchCoreFactory { } } } - - } - + + + public static class SearchAmenityByNameAPI extends SearchBaseAPI { private static final int LIMIT = 10000; private static final int BBOX_RADIUS = 500 * 1000; private static final int BBOX_RADIUS_INSIDE = 10000 * 1000; // to support city search for basemap - - + + public SearchAmenityByNameAPI() { + super(ObjectType.POI); + } + @Override public boolean search(final SearchPhrase phrase, final SearchResultMatcher resultMatcher) throws IOException { - if (!phrase.isUnknownSearchWordPresent()) { + if (!super.search(phrase, resultMatcher)) { + return false; + } + if(!phrase.isUnknownSearchWordPresent()) { return false; } final BinaryMapIndexReader[] currentFile = new BinaryMapIndexReader[1]; - Iterator offlineIterator = phrase.getRadiusOfflineIndexes(BBOX_RADIUS, + Iterator offlineIterator = phrase.getRadiusOfflineIndexes(BBOX_RADIUS, SearchPhraseDataType.POI); final NameStringMatcher nm = phrase.getNameStringMatcher(); QuadRect bbox = phrase.getRadiusBBoxToSearch(BBOX_RADIUS_INSIDE); SearchRequest req = BinaryMapIndexReader.buildSearchPoiRequest( - (int) bbox.centerX(), (int) bbox.centerY(), - phrase.getUnknownSearchWord(), - (int) bbox.left, (int) bbox.right, - (int) bbox.top, (int) bbox.bottom, + (int)bbox.centerX(), (int)bbox.centerY(), + phrase.getUnknownSearchWord(), + (int)bbox.left, (int)bbox.right, + (int)bbox.top, (int)bbox.bottom, new ResultMatcher() { int limit = 0; @Override public boolean publish(Amenity object) { - if(limit ++ > LIMIT) { + if (limit ++ > LIMIT) { return false; } SearchResult sr = new SearchResult(phrase); sr.otherNames = object.getAllNames(true); sr.localeName = object.getName(phrase.getSettings().getLang(), phrase.getSettings().isTransliterate()); - if(phrase.isUnknownSearchWordComplete()) { + if (phrase.isUnknownSearchWordComplete()) { if(!nm.matches(sr.localeName) && !nm.matches(sr.otherNames)) { return false; } @@ -568,14 +501,14 @@ public class SearchCoreFactory { sr.preferredZoom = 17; sr.file = currentFile[0]; sr.location = object.getLocation(); - if(object.getSubType().equals("city") || + if (object.getSubType().equals("city") || object.getSubType().equals("country")) { sr.priorityDistance = SEARCH_AMENITY_BY_NAME_CITY_PRIORITY_DISTANCE; sr.preferredZoom = object.getSubType().equals("country") ? 7 : 13; - } else if(object.getSubType().equals("town")) { + } else if (object.getSubType().equals("town")) { sr.priorityDistance = SEARCH_AMENITY_BY_NAME_TOWN_PRIORITY_DISTANCE; } else { - sr.priorityDistance = 1; + sr.priorityDistance = 1; } sr.priority = SEARCH_AMENITY_BY_NAME_PRIORITY; phrase.countUnknownWordsMatch(sr); @@ -589,38 +522,40 @@ public class SearchCoreFactory { return resultMatcher.isCancelled() && (limit < LIMIT) ; } }); + while (offlineIterator.hasNext()) { BinaryMapIndexReader r = offlineIterator.next(); currentFile[0] = r; r.searchPoiByName(req); - + resultMatcher.apiSearchRegionFinished(this, r, phrase); } return true; } - + @Override public int getSearchPriority(SearchPhrase p) { - if(p.hasObjectType(ObjectType.POI) || + if (p.hasObjectType(ObjectType.POI) || !p.isUnknownSearchWordPresent()) { return -1; } - if(p.hasObjectType(ObjectType.POI_TYPE)) { + if (p.hasObjectType(ObjectType.POI_TYPE)) { return -1; } - if(p.getUnknownSearchWordLength() > 3 || p.getRadiusLevel() > 1) { + if (p.getUnknownSearchWordLength() > 3 || p.getRadiusLevel() > 1) { return SEARCH_AMENITY_BY_NAME_API_PRIORITY_IF_3_CHAR; } return -1; } - + @Override public boolean isSearchMoreAvailable(SearchPhrase phrase) { return super.isSearchMoreAvailable(phrase) && getSearchPriority(phrase) != -1; } } - - + + + public static class SearchAmenityTypesAPI extends SearchBaseAPI { private Map translatedNames = new LinkedHashMap<>(); @@ -631,6 +566,7 @@ public class SearchCoreFactory { private MapPoiTypes types; public SearchAmenityTypesAPI(MapPoiTypes types) { + super(ObjectType.POI_TYPE); this.types = types; } @@ -643,10 +579,13 @@ public class SearchCoreFactory { this.customPoiFilters.add(poiFilter); this.customPoiFiltersPriorites.add(priority); } - + @Override public boolean search(SearchPhrase phrase, SearchResultMatcher resultMatcher) throws IOException { - if(translatedNames.isEmpty()) { + if (!super.search(phrase, resultMatcher)) { + return false; + } + if (translatedNames.isEmpty()) { translatedNames = types.getAllTranslatedNames(false); topVisibleFilters = types.getTopVisibleFilters(); categories = types.getCategories(false); @@ -705,35 +644,38 @@ public class SearchCoreFactory { resultMatcher.publish(res); } } - + return true; } - + @Override public boolean isSearchMoreAvailable(SearchPhrase phrase) { return false; } - + @Override public int getSearchPriority(SearchPhrase p) { if (p.hasObjectType(ObjectType.POI) || p.hasObjectType(ObjectType.POI_TYPE)) { return -1; } - if(!p.isNoSelectedType() && !p.isUnknownSearchWordPresent()) { + if (!p.isNoSelectedType() && !p.isUnknownSearchWordPresent()) { return -1; } return SEARCH_AMENITY_TYPE_API_PRIORITY; } } - + + + public static class SearchAmenityByTypeAPI extends SearchBaseAPI { - + private MapPoiTypes types; public SearchAmenityByTypeAPI(MapPoiTypes types) { + super(ObjectType.POI); this.types = types; } - + @Override public boolean isSearchMoreAvailable(SearchPhrase phrase) { return getSearchPriority(phrase) != -1 && super.isSearchMoreAvailable(phrase); @@ -762,30 +704,33 @@ public class SearchCoreFactory { } } } - + @Override public boolean search(final SearchPhrase phrase, final SearchResultMatcher resultMatcher) throws IOException { - if(phrase.isLastWord(ObjectType.POI_TYPE)) { + if (!super.search(phrase, resultMatcher)) { + return false; + } + if (phrase.isLastWord(ObjectType.POI_TYPE)) { Object obj = phrase.getLastSelectedWord().getResult().object; SearchPoiTypeFilter ptf; - if(obj instanceof AbstractPoiType) { + if (obj instanceof AbstractPoiType) { ptf = getPoiTypeFilter((AbstractPoiType) obj); } else if (obj instanceof SearchPoiTypeFilter) { ptf = (SearchPoiTypeFilter) obj; } else { throw new UnsupportedOperationException(); } - + QuadRect bbox = phrase.getRadiusBBoxToSearch(10000); List oo = phrase.getOfflineIndexes(); for (BinaryMapIndexReader o : oo) { ResultMatcher rm = getResultMatcher(phrase, resultMatcher, o); - if(obj instanceof CustomSearchPoiFilter) { + if (obj instanceof CustomSearchPoiFilter) { rm = ((CustomSearchPoiFilter) obj).wrapResultMatcher(rm); } SearchRequest req = BinaryMapIndexReader.buildSearchPoiRequest( - (int)bbox.left, (int)bbox.right, - (int)bbox.top, (int)bbox.bottom, -1, ptf, + (int)bbox.left, (int)bbox.right, + (int)bbox.top, (int)bbox.bottom, -1, ptf, rm); o.searchPoi(req); resultMatcher.apiSearchRegionFinished(this, o, phrase); @@ -795,7 +740,7 @@ public class SearchCoreFactory { } private ResultMatcher getResultMatcher(final SearchPhrase phrase, final SearchResultMatcher resultMatcher, - final BinaryMapIndexReader selected) { + final BinaryMapIndexReader selected) { final NameStringMatcher ns = phrase.getNameStringMatcher(); return new ResultMatcher() { @@ -816,7 +761,7 @@ public class SearchCoreFactory { && !(ns.matches(res.localeName) || ns.matches(res.otherNames))) { return false; } - + res.object = object; res.preferredZoom = 17; res.file = selected; @@ -833,21 +778,20 @@ public class SearchCoreFactory { return resultMatcher.isCancelled(); } }; - } private SearchPoiTypeFilter getPoiTypeFilter(AbstractPoiType pt) { - + acceptedTypes.clear(); poiAdditionals.clear(); updateTypesToAccept(pt); return new SearchPoiTypeFilter() { - + @Override public boolean isEmpty() { return false; } - + @Override public boolean accept(PoiCategory type, String subtype) { if (type == null) { @@ -867,34 +811,40 @@ public class SearchCoreFactory { } }; } - + @Override public int getSearchPriority(SearchPhrase p) { - if(p.isLastWord(ObjectType.POI_TYPE) && + if (p.isLastWord(ObjectType.POI_TYPE) && p.getLastTokenLocation() != null) { return SEARCH_AMENITY_BY_TYPE_PRIORITY; } return -1; } - + } + + public static class SearchStreetByCityAPI extends SearchBaseAPI { - + private SearchBaseAPI streetsAPI; public SearchStreetByCityAPI(SearchBuildingAndIntersectionsByStreetAPI streetsAPI) { + super(ObjectType.HOUSE, ObjectType.STREET, ObjectType.STREET_INTERSECTION); this.streetsAPI = streetsAPI; } - + @Override public boolean isSearchMoreAvailable(SearchPhrase phrase) { // case when street is not found for given city is covered here return phrase.getRadiusLevel() == 1 && getSearchPriority(phrase) != -1; } - + private static int LIMIT = 10000; @Override public boolean search(SearchPhrase phrase, SearchResultMatcher resultMatcher) throws IOException { + if (!super.search(phrase, resultMatcher)) { + return false; + } SearchWord sw = phrase.getLastSelectedWord(); if (isLastWordCityGroup(phrase) && sw.getResult() != null && sw.getResult().file != null) { City c = (City) sw.getResult().object; @@ -908,7 +858,7 @@ public class SearchCoreFactory { SearchResult res = new SearchResult(phrase); res.localeName = object.getName(phrase.getSettings().getLang(), phrase.getSettings().isTransliterate()); res.otherNames = object.getAllNames(true); - if(object.getName().startsWith("<")) { + if (object.getName().startsWith("<")) { // streets related to city continue; } @@ -928,64 +878,72 @@ public class SearchCoreFactory { if (limit++ > LIMIT) { break; } - + } return true; } return true; } - @Override public int getSearchPriority(SearchPhrase p) { - if(isLastWordCityGroup(p)) { + if (isLastWordCityGroup(p)) { return SEARCH_STREET_BY_CITY_PRIORITY; } return -1; } - + } - + public static boolean isLastWordCityGroup(SearchPhrase p ) { - return p.isLastWord(ObjectType.CITY) || p.isLastWord(ObjectType.POSTCODE) || + return p.isLastWord(ObjectType.CITY) || p.isLastWord(ObjectType.POSTCODE) || p.isLastWord(ObjectType.VILLAGE); - } - + } + + + public static class SearchBuildingAndIntersectionsByStreetAPI extends SearchBaseAPI { Street cacheBuilding; - + + public SearchBuildingAndIntersectionsByStreetAPI() { + super(ObjectType.HOUSE, ObjectType.STREET_INTERSECTION); + } + @Override public boolean isSearchMoreAvailable(SearchPhrase phrase) { return false; } - + @Override public boolean search(SearchPhrase phrase, final SearchResultMatcher resultMatcher) throws IOException { + if (!super.search(phrase, resultMatcher)) { + return false; + } Street s = null; int priority = SEARCH_BUILDING_BY_STREET_PRIORITY; - if(phrase.isLastWord(ObjectType.STREET)) { + if (phrase.isLastWord(ObjectType.STREET)) { s = (Street) phrase.getLastSelectedWord().getResult().object; } - if(isLastWordCityGroup(phrase)) { + if (isLastWordCityGroup(phrase)) { priority = SEARCH_BUILDING_BY_CITY_PRIORITY; Object o = phrase.getLastSelectedWord().getResult().object; - if(o instanceof City) { + if (o instanceof City) { List streets = ((City) o).getStreets(); - if(streets.size() == 1) { + if (streets.size() == 1) { s = streets.get(0); } else { - for(Street st : streets) { - if(st.getName().equals(((City) o).getName()) || + for (Street st : streets) { + if (st.getName().equals(((City) o).getName()) || st.getName().equals("<"+((City) o).getName()+">")) { s = st; - break; + break; } } } } } - - if(s != null) { + + if (s != null) { BinaryMapIndexReader file = phrase.getLastSelectedWord().getResult().file; String lw = phrase.getUnknownSearchWord(); NameStringMatcher sm = phrase.getNameStringMatcher(); @@ -1004,6 +962,7 @@ public class SearchCoreFactory { return resultMatcher.isCancelled(); } }); + file.preloadBuildings(s, sr); Collections.sort(s.getBuildings(), new Comparator() { @@ -1018,13 +977,14 @@ public class SearchCoreFactory { } }); } - for(Building b : s.getBuildings()) { + for (Building b : s.getBuildings()) { SearchResult res = new SearchResult(phrase); boolean interpolation = b.belongsToInterpolation(lw); - if(!sm.matches(b.getName()) && !interpolation) { + if ((!sm.matches(b.getName()) && !interpolation) + || !phrase.isSearchTypeAllowed(ObjectType.HOUSE)) { continue; } - + res.localeName = b.getName(phrase.getSettings().getLang(), phrase.getSettings().isTransliterate()); res.otherNames = b.getAllNames(true); res.object = b; @@ -1042,10 +1002,11 @@ public class SearchCoreFactory { res.preferredZoom = 17; resultMatcher.publish(res); } - if(Algorithms.isEmpty(lw) || !Character.isDigit(lw.charAt(0))) { - for(Street street : s.getIntersectedStreets()) { + if (Algorithms.isEmpty(lw) || !Character.isDigit(lw.charAt(0))) { + for (Street street : s.getIntersectedStreets()) { SearchResult res = new SearchResult(phrase); - if(!sm.matches(street.getName()) && !sm.matches(street.getAllNames(true))) { + if ((!sm.matches(street.getName()) && !sm.matches(street.getAllNames(true))) + || !phrase.isSearchTypeAllowed(ObjectType.STREET_INTERSECTION)) { continue; } res.otherNames = street.getAllNames(true); @@ -1062,14 +1023,10 @@ public class SearchCoreFactory { resultMatcher.publish(res); } } - - - } return true; } - @Override public int getSearchPriority(SearchPhrase p) { if (isLastWordCityGroup(p)) { @@ -1081,18 +1038,21 @@ public class SearchCoreFactory { return SEARCH_BUILDING_BY_STREET_PRIORITY; } } - - - - + + + public static class SearchLocationAndUrlAPI extends SearchBaseAPI { - + + public SearchLocationAndUrlAPI() { + super(ObjectType.LOCATION, ObjectType.PARTIAL_LOCATION); + } + @Override public boolean isSearchMoreAvailable(SearchPhrase phrase) { return false; } -// newFormat = PointDescription.FORMAT_DEGREES; + // newFormat = PointDescription.FORMAT_DEGREES; // newFormat = PointDescription.FORMAT_MINUTES; // newFormat = PointDescription.FORMAT_SECONDS; public void testUTM() { @@ -1105,49 +1065,53 @@ public class SearchCoreFactory { LatLonPoint ll = upoint.toLatLonPoint(); LatLon loc = new LatLon(ll.getLatitude(), ll.getLongitude()); } - + @Override public boolean search(SearchPhrase phrase, SearchResultMatcher resultMatcher) throws IOException { - if(!phrase.isUnknownSearchWordPresent()) { + if (!super.search(phrase, resultMatcher)) { + return false; + } + if (!phrase.isUnknownSearchWordPresent()) { return false; } boolean parseUrl = parseUrl(phrase, resultMatcher); - if(!parseUrl) { + if (!parseUrl) { parseLocation(phrase, resultMatcher); } return super.search(phrase, resultMatcher); } + private boolean isKindOfNumber(String s) { - for(int i = 0; i < s.length(); i ++) { + for (int i = 0; i < s.length(); i ++) { char c = s.charAt(i); - if(c >= '0' && c <= '9') { - } else if(c == ':' || c == '.' || c == '#' || c == ',' || c == '-' || c == '\'' || c == '"') { + if (c >= '0' && c <= '9') { + } else if (c == ':' || c == '.' || c == '#' || c == ',' || c == '-' || c == '\'' || c == '"') { } else { return false; } } return true; } - + LatLon parsePartialLocation(String s) { s = s.trim(); - if(s.length() == 0 || !(s.charAt(0) == '-' || Character.isDigit(s.charAt(0)) - || s.charAt(0) == 'S' || s.charAt(0) == 's' - || s.charAt(0) == 'N' || s.charAt(0) == 'n' - || s.contains("://"))) { + if (s.length() == 0 || !(s.charAt(0) == '-' || Character.isDigit(s.charAt(0)) + || s.charAt(0) == 'S' || s.charAt(0) == 's' + || s.charAt(0) == 'N' || s.charAt(0) == 'n' + || s.contains("://"))) { return null; } List d = new ArrayList<>(); List all = new ArrayList<>(); List strings = new ArrayList<>(); splitObjects(s, d, all, strings); - if(d.size() == 0) { + if (d.size() == 0) { return null; } double lat = parse1Coordinate(all, 0, all.size()); return new LatLon(lat, 0); } - + LatLon parseLocation(String s) { s = s.trim(); // detect OLC first @@ -1159,17 +1123,17 @@ public class SearchCoreFactory { return new LatLon(codeArea.getCenterLatitude(), codeArea.getCenterLongitude()); } } - if(s.length() == 0 || !(s.charAt(0) == '-' || Character.isDigit(s.charAt(0)) - || s.charAt(0) == 'S' || s.charAt(0) == 's' - || s.charAt(0) == 'N' || s.charAt(0) == 'n' - || s.contains("://"))) { + if (s.length() == 0 || !(s.charAt(0) == '-' || Character.isDigit(s.charAt(0)) + || s.charAt(0) == 'S' || s.charAt(0) == 's' + || s.charAt(0) == 'N' || s.charAt(0) == 'n' + || s.contains("://"))) { return null; } List d = new ArrayList<>(); List all = new ArrayList<>(); List strings = new ArrayList<>(); splitObjects(s, d, all, strings); - if(d.size() == 0) { + if (d.size() == 0) { return null; } // detect UTM @@ -1205,16 +1169,16 @@ public class SearchCoreFactory { boolean finishDegSplit = false; int northSplit = -1; int eastSplit = -1; - for(int i = 1; i < all.size(); i++ ) { - if(all.get(i - 1) instanceof Double && all.get(i) instanceof Double) { + for (int i = 1; i < all.size(); i++ ) { + if (all.get(i - 1) instanceof Double && all.get(i) instanceof Double) { jointNumbers ++; lastJoin = i; } - if(all.get(i).equals("n") || all.get(i).equals("s") || + if (all.get(i).equals("n") || all.get(i).equals("s") || all.get(i).equals("N") || all.get(i).equals("S")) { northSplit = i + 1; } - if(all.get(i).equals("e") || all.get(i).equals("w") || + if (all.get(i).equals("e") || all.get(i).equals("w") || all.get(i).equals("E") || all.get(i).equals("W")) { eastSplit = i; } @@ -1246,23 +1210,23 @@ public class SearchCoreFactory { } } int split = -1; - if(jointNumbers == 1) { + if (jointNumbers == 1) { split = lastJoin; } - if(northSplit != -1 && northSplit < all.size() -1) { + if (northSplit != -1 && northSplit < all.size() -1) { split = northSplit; - } else if(eastSplit != -1 && eastSplit < all.size() -1) { + } else if (eastSplit != -1 && eastSplit < all.size() -1) { split = eastSplit; - } else if(degSplit != -1 && degSplit < all.size() -1) { + } else if (degSplit != -1 && degSplit < all.size() -1) { split = degSplit; } - - if(split != -1) { + + if (split != -1) { double lat = parse1Coordinate(all, 0, split); double lon = parse1Coordinate(all, split, all.size()); return new LatLon(lat, lon); } - if(d.size() == 2) { + if (d.size() == 2) { return new LatLon(d.get(0), d.get(1)); } // simple url case @@ -1290,10 +1254,10 @@ public class SearchCoreFactory { int ind = d.size() / 2 + 1; int splitEq = -1; for (int i = 0; i < all.size(); i++) { - if(all.get(i) instanceof Double) { + if (all.get(i) instanceof Double) { ind --; } - if(ind == 0) { + if (ind == 0) { splitEq = i; break; } @@ -1305,31 +1269,31 @@ public class SearchCoreFactory { } } return null; - + } - + public double parse1Coordinate(List all, int begin, int end) { boolean neg = false; double d = 0; int type = 0; // degree - 0, minutes - 1, seconds = 2 Double prevDouble = null; - for(int i = begin; i <= end; i++) { + for (int i = begin; i <= end; i++) { Object o = i == end ? "" : all.get(i); if(o.equals("S") || o.equals("W")) { neg = !neg; } if (prevDouble != null) { - if(o.equals("°")) { + if (o.equals("°")) { type = 0; - } else if(o.equals("′") /*o.equals("'")*/) { + } else if (o.equals("′") /*o.equals("'")*/) { // ' can be used as delimeter ignore it type = 1; - } else if(o.equals("\"") || o.equals("″")) { + } else if (o.equals("\"") || o.equals("″")) { type = 2; } if (type == 0) { double ld = prevDouble.doubleValue(); - if(ld < 0) { + if (ld < 0) { ld = -ld; neg = true; } @@ -1341,13 +1305,13 @@ public class SearchCoreFactory { } type++; } - if(o instanceof Double) { + if (o instanceof Double) { prevDouble = (Double) o; } else { prevDouble = null; } } - if(neg) { + if (neg) { d = -d; } return d; @@ -1356,13 +1320,13 @@ public class SearchCoreFactory { private void splitObjects(String s, List d, List all, List strings) { boolean digit = false; int word = -1; - for(int i = 0; i <= s.length(); i++) { + for (int i = 0; i <= s.length(); i++) { char ch = i == s.length() ? ' ' : s.charAt(i); boolean dg = Character.isDigit(ch); boolean nonwh = ch != ',' && ch != ' ' && ch != ';'; if (ch == '.' || dg || ch == '-' ) { - if(!digit) { - if(word != -1) { + if (!digit) { + if (word != -1) { all.add(s.substring(word, i)); strings.add(s.substring(word, i)); } @@ -1376,7 +1340,7 @@ public class SearchCoreFactory { // continue } } else { - if(digit){ + if (digit){ try { double dl = Double.parseDouble(s.substring(word, i)); d.add(dl); @@ -1387,7 +1351,7 @@ public class SearchCoreFactory { } catch (NumberFormatException e) { } } - if(nonwh) { + if (nonwh) { if(!Character.isLetter(ch)) { if(word != -1) { all.add(s.substring(word, i)); @@ -1398,9 +1362,9 @@ public class SearchCoreFactory { word = -1; } else if(word == -1) { word = i; - } + } } else { - if(word != -1) { + if (word != -1) { all.add(s.substring(word, i)); strings.add(s.substring(word, i)); } @@ -1409,21 +1373,23 @@ public class SearchCoreFactory { } } } - + private void parseLocation(SearchPhrase phrase, SearchResultMatcher resultMatcher) { String lw = phrase.getUnknownSearchPhrase(); LatLon l = parseLocation(lw); if (l != null) { - SearchResult sp = new SearchResult(phrase); - sp.priority = SEARCH_LOCATION_PRIORITY; - sp.object = sp.location = l; - sp.localeName = ((float) sp.location.getLatitude()) + ", " + ((float) sp.location.getLongitude()); - sp.objectType = ObjectType.LOCATION; - sp.wordsSpan = lw; - resultMatcher.publish(sp); + if (phrase.isSearchTypeAllowed(ObjectType.LOCATION)) { + SearchResult sp = new SearchResult(phrase); + sp.priority = SEARCH_LOCATION_PRIORITY; + sp.object = sp.location = l; + sp.localeName = ((float) sp.location.getLatitude()) + ", " + ((float) sp.location.getLongitude()); + sp.objectType = ObjectType.LOCATION; + sp.wordsSpan = lw; + resultMatcher.publish(sp); + } } else if (phrase.isNoSelectedType()) { LatLon ll = parsePartialLocation(lw); - if (ll != null) { + if (ll != null && phrase.isSearchTypeAllowed(ObjectType.PARTIAL_LOCATION)) { SearchResult sp = new SearchResult(phrase); sp.priority = SEARCH_LOCATION_PRIORITY; @@ -1434,20 +1400,18 @@ public class SearchCoreFactory { } } } - - private boolean parseUrl(SearchPhrase phrase, SearchResultMatcher resultMatcher) { String text = phrase.getUnknownSearchPhrase(); GeoParsedPoint pnt = GeoPointParserUtil.parse(text); - if(pnt != null && pnt.isGeoPoint()) { + if (pnt != null && pnt.isGeoPoint() && phrase.isSearchTypeAllowed(ObjectType.LOCATION)) { SearchResult sp = new SearchResult(phrase); sp.priority = 0; sp.object = pnt; sp.wordsSpan = text; sp.location = new LatLon(pnt.getLatitude(), pnt.getLongitude()); sp.localeName = ((float)pnt.getLatitude()) +", " + ((float) pnt.getLongitude()); - if(pnt.getZoom() > 0) { + if (pnt.getZoom() > 0) { sp.preferredZoom = pnt.getZoom(); } sp.objectType = ObjectType.LOCATION; @@ -1456,11 +1420,10 @@ public class SearchCoreFactory { } return false; } - + @Override public int getSearchPriority(SearchPhrase p) { return SEARCH_LOCATION_PRIORITY; } } - } diff --git a/OsmAnd-java/src/net/osmand/search/core/SearchPhrase.java b/OsmAnd-java/src/net/osmand/search/core/SearchPhrase.java index 9c9bcb441a..383fafd8e2 100644 --- a/OsmAnd-java/src/net/osmand/search/core/SearchPhrase.java +++ b/OsmAnd-java/src/net/osmand/search/core/SearchPhrase.java @@ -1,5 +1,16 @@ package net.osmand.search.core; +import net.osmand.Collator; +import net.osmand.CollatorStringMatcher; +import net.osmand.CollatorStringMatcher.StringMatcherMode; +import net.osmand.StringMatcher; +import net.osmand.binary.BinaryMapIndexReader; +import net.osmand.binary.BinaryMapIndexReader.SearchRequest; +import net.osmand.data.LatLon; +import net.osmand.data.QuadRect; +import net.osmand.util.Algorithms; +import net.osmand.util.MapUtils; + import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -12,18 +23,6 @@ import java.util.Set; import java.util.TreeSet; import java.util.regex.Pattern; -import net.osmand.Collator; -import net.osmand.CollatorStringMatcher; -import net.osmand.CollatorStringMatcher.StringMatcherMode; -import net.osmand.OsmAndCollator; -import net.osmand.StringMatcher; -import net.osmand.binary.BinaryMapIndexReader; -import net.osmand.binary.BinaryMapIndexReader.SearchRequest; -import net.osmand.data.LatLon; -import net.osmand.data.QuadRect; -import net.osmand.util.Algorithms; -import net.osmand.util.MapUtils; - //immutable object public class SearchPhrase { @@ -288,7 +287,36 @@ public class SearchPhrase { public int getRadiusLevel() { return settings.getRadiusLevel(); } - + + public ObjectType[] getSearchTypes() { + return settings.getSearchTypes(); + } + + public boolean isCustomSearch() { + return getSearchTypes() != null; + } + + public boolean isSearchTypeAllowed(ObjectType searchType) { + if (getSearchTypes() == null) { + return true; + } else { + for (ObjectType type : getSearchTypes()) { + if (type == searchType) { + return true; + } + } + return false; + } + } + + public boolean isEmptyQueryAllowed() { + return settings.isEmptyQueryAllowed(); + } + + public boolean isSortByName() { + return settings.isSortByName(); + } + public SearchPhrase selectWord(SearchResult res) { return selectWord(res, null, false); } @@ -405,15 +433,13 @@ public class SearchPhrase { public boolean isEmpty() { return words.isEmpty() && unknownSearchPhrase.isEmpty(); } - - + public SearchWord getLastSelectedWord() { if(words.isEmpty()) { return null; } return words.get(words.size() - 1); } - public LatLon getWordLocation() { for(int i = words.size() - 1; i >= 0; i--) { @@ -436,7 +462,6 @@ public class SearchPhrase { return settings.getOriginalLocation(); } - public void selectFile(BinaryMapIndexReader object) { if(indexes == null) { indexes = new ArrayList<>(); diff --git a/OsmAnd-java/src/net/osmand/search/core/SearchSettings.java b/OsmAnd-java/src/net/osmand/search/core/SearchSettings.java index efcb637d47..5057315ba6 100644 --- a/OsmAnd-java/src/net/osmand/search/core/SearchSettings.java +++ b/OsmAnd-java/src/net/osmand/search/core/SearchSettings.java @@ -1,22 +1,25 @@ package net.osmand.search.core; +import net.osmand.binary.BinaryMapIndexReader; +import net.osmand.data.LatLon; + import java.util.ArrayList; import java.util.Collections; import java.util.List; -import net.osmand.binary.BinaryMapIndexReader; -import net.osmand.data.LatLon; - // immutable object public class SearchSettings { - + private LatLon originalLocation; private List offlineIndexes = new ArrayList<>(); private int radiusLevel = 1; private int totalLimit = -1; private String lang; private boolean transliterateIfMissing; - + private ObjectType[] searchTypes; + private boolean emptyQueryAllowed; + private boolean sortByName; + public SearchSettings(SearchSettings s) { if(s != null) { this.radiusLevel = s.radiusLevel; @@ -24,6 +27,9 @@ public class SearchSettings { this.totalLimit = s.totalLimit; this.offlineIndexes = s.offlineIndexes; this.originalLocation = s.originalLocation; + this.searchTypes = s.searchTypes; + this.emptyQueryAllowed = s.emptyQueryAllowed; + this.sortByName = s.sortByName; } } @@ -85,5 +91,43 @@ public class SearchSettings { return transliterateIfMissing; } + public ObjectType[] getSearchTypes() { + return searchTypes; + } + public boolean isCustomSearch() { + return searchTypes != null; + } + + public SearchSettings setSearchTypes(ObjectType... searchTypes) { + SearchSettings s = new SearchSettings(this); + s.searchTypes = searchTypes; + return s; + } + + public SearchSettings resetSearchTypes() { + SearchSettings s = new SearchSettings(this); + s.searchTypes = null; + return s; + } + + public boolean isEmptyQueryAllowed() { + return emptyQueryAllowed; + } + + public SearchSettings setEmptyQueryAllowed(boolean emptyQueryAllowed) { + SearchSettings s = new SearchSettings(this); + s.emptyQueryAllowed = emptyQueryAllowed; + return s; + } + + public boolean isSortByName() { + return sortByName; + } + + public SearchSettings setSortByName(boolean sortByName) { + SearchSettings s = new SearchSettings(this); + s.sortByName = sortByName; + return s; + } } diff --git a/OsmAnd/src/net/osmand/plus/search/QuickSearchDialogFragment.java b/OsmAnd/src/net/osmand/plus/search/QuickSearchDialogFragment.java index 75f941b9a3..55ed8bd352 100644 --- a/OsmAnd/src/net/osmand/plus/search/QuickSearchDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/search/QuickSearchDialogFragment.java @@ -74,9 +74,8 @@ import net.osmand.search.SearchUICore; import net.osmand.search.SearchUICore.SearchResultCollection; import net.osmand.search.core.ObjectType; import net.osmand.search.core.SearchCoreAPI; +import net.osmand.search.core.SearchCoreFactory.SearchAddressByNameAPI; import net.osmand.search.core.SearchCoreFactory.SearchAmenityTypesAPI; -import net.osmand.search.core.SearchCoreFactory.SearchCityByNameAPI; -import net.osmand.search.core.SearchCoreFactory.SearchPostcodeAPI; import net.osmand.search.core.SearchPhrase; import net.osmand.search.core.SearchResult; import net.osmand.search.core.SearchSettings; @@ -468,6 +467,7 @@ public class QuickSearchDialogFragment extends DialogFragment implements OsmAndC if (!searchQuery.equalsIgnoreCase(newQueryText)) { searchQuery = newQueryText; if (Algorithms.isEmpty(searchQuery)) { + stopCustomSearch(); searchUICore.resetPhrase(); } else { runSearch(); @@ -491,7 +491,7 @@ public class QuickSearchDialogFragment extends DialogFragment implements OsmAndC String newText = searchUICore.getPhrase().getTextWithoutLastWord(); searchEditText.setText(newText); searchEditText.setSelection(newText.length()); - searchUICore.setSearchApiClass(null); + stopCustomSearch(); } else if (useMapCenter && location != null) { useMapCenter = false; centerLatLon = null; @@ -710,7 +710,7 @@ public class QuickSearchDialogFragment extends DialogFragment implements OsmAndC boolean transliterate = app.getSettings().MAP_TRANSLITERATE_NAMES.get(); searchHelper = app.getSearchUICore(); searchUICore = searchHelper.getCore(); - searchUICore.setSearchApiClass(null); + stopCustomSearch(); location = app.getLocationProvider().getLastKnownLocation(); @@ -882,7 +882,7 @@ public class QuickSearchDialogFragment extends DialogFragment implements OsmAndC searchView.setVisibility(View.GONE); } else if (!show && tabsView.getVisibility() == View.VISIBLE) { tabToolbarView.setVisibility(View.GONE); - buttonToolbarView.setVisibility(searchUICore.isInCustomSearch() ? View.GONE : View.VISIBLE); + buttonToolbarView.setVisibility(searchUICore.getSearchSettings().isCustomSearch() ? View.GONE : View.VISIBLE); tabsView.setVisibility(View.GONE); searchView.setVisibility(View.VISIBLE); } @@ -1008,17 +1008,22 @@ public class QuickSearchDialogFragment extends DialogFragment implements OsmAndC private void reloadCitiesInternal() { try { - SearchResultCollection res = searchUICore.shallowSearch(SearchCityByNameAPI.class, + startSearchingCities(false, false); + SearchResultCollection res = searchUICore.shallowSearch(SearchAddressByNameAPI.class, "", null); + stopCustomSearch(); + List rows = new ArrayList<>(); rows.add(new QuickSearchButtonListItem(app, R.drawable.ic_action_building_number, app.getString(R.string.select_city), new OnClickListener() { @Override public void onClick(View v) { searchEditText.setHint(R.string.type_city_town); - searchUICore.setSearchApiClass(SearchCityByNameAPI.class); + startSearchingCities(true, true); updateTabbarVisibility(false); runSearch(); + searchEditText.requestFocus(); + AndroidUtils.softKeyboardDelayed(searchEditText); } })); rows.add(new QuickSearchButtonListItem(app, R.drawable.ic_action_postcode, @@ -1026,7 +1031,7 @@ public class QuickSearchDialogFragment extends DialogFragment implements OsmAndC @Override public void onClick(View v) { searchEditText.setHint(R.string.type_postcode); - searchUICore.setSearchApiClass(SearchPostcodeAPI.class); + startSearchingPostcodes(true); mainSearchFragment.getAdapter().clear(); updateTabbarVisibility(false); searchEditText.requestFocus(); @@ -1091,6 +1096,34 @@ public class QuickSearchDialogFragment extends DialogFragment implements OsmAndC } } + private void startSearchingCities(boolean sortByName, boolean searchVillages) { + SearchSettings settings = searchUICore.getSearchSettings().setEmptyQueryAllowed(true).setRadiusLevel(1); + if (sortByName) { + settings = settings.setSortByName(true); + } + if (searchVillages) { + settings = settings.setSearchTypes(ObjectType.CITY, ObjectType.VILLAGE); + } else { + settings = settings.setSearchTypes(ObjectType.CITY); + } + searchUICore.updateSettings(settings); + } + + private void startSearchingPostcodes(boolean sortByName) { + SearchSettings settings = searchUICore.getSearchSettings().setSearchTypes(ObjectType.POSTCODE) + .setEmptyQueryAllowed(false).setRadiusLevel(1); + if (sortByName) { + settings = settings.setSortByName(true); + } + searchUICore.updateSettings(settings); + } + + private void stopCustomSearch() { + SearchSettings settings = searchUICore.getSearchSettings().resetSearchTypes() + .setEmptyQueryAllowed(false).setSortByName(false).setRadiusLevel(1); + searchUICore.updateSettings(settings); + } + private void runSearch() { runSearch(searchQuery); } @@ -1268,7 +1301,7 @@ public class QuickSearchDialogFragment extends DialogFragment implements OsmAndC } } searchUICore.selectSearchResult(sr); - searchUICore.setSearchApiClass(null); + stopCustomSearch(); String txt = searchUICore.getPhrase().getText(true); searchQuery = txt; searchEditText.setText(txt); diff --git a/OsmAnd/src/net/osmand/plus/search/QuickSearchHelper.java b/OsmAnd/src/net/osmand/plus/search/QuickSearchHelper.java index 0a124bf783..3ed48c9c2f 100644 --- a/OsmAnd/src/net/osmand/plus/search/QuickSearchHelper.java +++ b/OsmAnd/src/net/osmand/plus/search/QuickSearchHelper.java @@ -1,8 +1,5 @@ package net.osmand.plus.search; -import java.util.Arrays; -import java.util.List; - import net.osmand.binary.BinaryMapIndexReader; import net.osmand.data.FavouritePoint; import net.osmand.data.LatLon; @@ -16,10 +13,14 @@ import net.osmand.search.SearchUICore; import net.osmand.search.SearchUICore.SearchResultCollection; import net.osmand.search.core.CustomSearchPoiFilter; import net.osmand.search.core.ObjectType; -import net.osmand.search.core.SearchCoreFactory; +import net.osmand.search.core.SearchCoreFactory.SearchBaseAPI; import net.osmand.search.core.SearchPhrase; import net.osmand.search.core.SearchResult; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + public class QuickSearchHelper implements ResourceListener { public static final int SEARCH_FAVORITE_API_PRIORITY = 50; @@ -63,76 +64,10 @@ public class QuickSearchHelper implements ResourceListener { setRepositoriesForSearchUICore(app); core.init(); // Register favorites search api - core.registerAPI(new SearchCoreFactory.SearchBaseAPI() { - - @Override - public boolean isSearchMoreAvailable(SearchPhrase phrase) { - return false; - } - - @Override - public boolean search(SearchPhrase phrase, SearchUICore.SearchResultMatcher resultMatcher) { - List favList = app.getFavorites().getFavouritePoints(); - for (FavouritePoint point : favList) { - SearchResult sr = new SearchResult(phrase); - sr.localeName = point.getName(); - sr.object = point; - sr.priority = SEARCH_FAVORITE_OBJECT_PRIORITY; - sr.objectType = ObjectType.FAVORITE; - sr.location = new LatLon(point.getLatitude(), point.getLongitude()); - sr.preferredZoom = 17; - if (phrase.getUnknownSearchWordLength() <= 1 && phrase.isNoSelectedType()) { - resultMatcher.publish(sr); - } else if (phrase.getNameStringMatcher().matches(sr.localeName)) { - resultMatcher.publish(sr); - } - } - return true; - } - - @Override - public int getSearchPriority(SearchPhrase p) { - if(!p.isNoSelectedType() || !p.isUnknownSearchWordPresent()) { - return -1; - } - return SEARCH_FAVORITE_API_PRIORITY; - } - }); + core.registerAPI(new SearchFavoriteAPI(app)); // Register favorites by category search api - core.registerAPI(new SearchCoreFactory.SearchBaseAPI() { - - @Override - public boolean isSearchMoreAvailable(SearchPhrase phrase) { - return false; - } - - @Override - public boolean search(SearchPhrase phrase, SearchUICore.SearchResultMatcher resultMatcher) { - List favList = app.getFavorites().getFavouritePoints(); - for (FavouritePoint point : favList) { - SearchResult sr = new SearchResult(phrase); - sr.localeName = point.getName(); - sr.object = point; - sr.priority = SEARCH_FAVORITE_CATEGORY_PRIORITY; - sr.objectType = ObjectType.FAVORITE; - sr.location = new LatLon(point.getLatitude(), point.getLongitude()); - sr.preferredZoom = 17; - if (point.getCategory() != null && phrase.getNameStringMatcher().matches(point.getCategory())) { - resultMatcher.publish(sr); - } - } - return true; - } - - @Override - public int getSearchPriority(SearchPhrase p) { - if(!p.isNoSelectedType() || !p.isUnknownSearchWordPresent()) { - return -1; - } - return SEARCH_FAVORITE_API_CATEGORY_PRIORITY; - } - }); + core.registerAPI(new SearchFavoriteByCategoryAPI(app)); // Register WptPt search api core.registerAPI(new SearchWptAPI(app)); @@ -156,11 +91,12 @@ public class QuickSearchHelper implements ResourceListener { core.getSearchSettings().setOfflineIndexes(Arrays.asList(binaryMapIndexReaderArray)); } - public static class SearchWptAPI extends SearchCoreFactory.SearchBaseAPI { + public static class SearchWptAPI extends SearchBaseAPI { private OsmandApplication app; public SearchWptAPI(OsmandApplication app) { + super(ObjectType.WPT); this.app = app; } @@ -170,8 +106,10 @@ public class QuickSearchHelper implements ResourceListener { } @Override - public boolean search(SearchPhrase phrase, SearchUICore.SearchResultMatcher resultMatcher) { - + public boolean search(SearchPhrase phrase, SearchUICore.SearchResultMatcher resultMatcher) throws IOException { + if (!super.search(phrase, resultMatcher)) { + return false; + } if (phrase.isEmpty()) { return false; } @@ -209,11 +147,102 @@ public class QuickSearchHelper implements ResourceListener { } } - public static class SearchHistoryAPI extends SearchCoreFactory.SearchBaseAPI { + public static class SearchFavoriteByCategoryAPI extends SearchBaseAPI { + + private OsmandApplication app; + + public SearchFavoriteByCategoryAPI(OsmandApplication app) { + super(ObjectType.FAVORITE); + this.app = app; + } + + @Override + public boolean isSearchMoreAvailable(SearchPhrase phrase) { + return false; + } + + @Override + public boolean search(SearchPhrase phrase, SearchUICore.SearchResultMatcher resultMatcher) throws IOException { + if (!super.search(phrase, resultMatcher)) { + return false; + } + List favList = app.getFavorites().getFavouritePoints(); + for (FavouritePoint point : favList) { + SearchResult sr = new SearchResult(phrase); + sr.localeName = point.getName(); + sr.object = point; + sr.priority = SEARCH_FAVORITE_CATEGORY_PRIORITY; + sr.objectType = ObjectType.FAVORITE; + sr.location = new LatLon(point.getLatitude(), point.getLongitude()); + sr.preferredZoom = 17; + if (point.getCategory() != null && phrase.getNameStringMatcher().matches(point.getCategory())) { + resultMatcher.publish(sr); + } + } + return true; + } + + @Override + public int getSearchPriority(SearchPhrase p) { + if(!p.isNoSelectedType() || !p.isUnknownSearchWordPresent()) { + return -1; + } + return SEARCH_FAVORITE_API_CATEGORY_PRIORITY; + } + } + + public static class SearchFavoriteAPI extends SearchBaseAPI { + + private OsmandApplication app; + + public SearchFavoriteAPI(OsmandApplication app) { + super(ObjectType.FAVORITE); + this.app = app; + } + + @Override + public boolean isSearchMoreAvailable(SearchPhrase phrase) { + return false; + } + + @Override + public boolean search(SearchPhrase phrase, SearchUICore.SearchResultMatcher resultMatcher) throws IOException { + if (!super.search(phrase, resultMatcher)) { + return false; + } + List favList = app.getFavorites().getFavouritePoints(); + for (FavouritePoint point : favList) { + SearchResult sr = new SearchResult(phrase); + sr.localeName = point.getName(); + sr.object = point; + sr.priority = SEARCH_FAVORITE_OBJECT_PRIORITY; + sr.objectType = ObjectType.FAVORITE; + sr.location = new LatLon(point.getLatitude(), point.getLongitude()); + sr.preferredZoom = 17; + if (phrase.getUnknownSearchWordLength() <= 1 && phrase.isNoSelectedType()) { + resultMatcher.publish(sr); + } else if (phrase.getNameStringMatcher().matches(sr.localeName)) { + resultMatcher.publish(sr); + } + } + return true; + } + + @Override + public int getSearchPriority(SearchPhrase p) { + if(!p.isNoSelectedType() || !p.isUnknownSearchWordPresent()) { + return -1; + } + return SEARCH_FAVORITE_API_PRIORITY; + } + } + + public static class SearchHistoryAPI extends SearchBaseAPI { private OsmandApplication app; public SearchHistoryAPI(OsmandApplication app) { + super(ObjectType.RECENT_OBJ); this.app = app; } @@ -223,7 +252,10 @@ public class QuickSearchHelper implements ResourceListener { } @Override - public boolean search(SearchPhrase phrase, SearchUICore.SearchResultMatcher resultMatcher) { + public boolean search(SearchPhrase phrase, SearchUICore.SearchResultMatcher resultMatcher) throws IOException { + if (!super.search(phrase, resultMatcher)) { + return false; + } SearchHistoryHelper helper = SearchHistoryHelper.getInstance(app); List points = helper.getHistoryEntries(); int p = 0;