Merge remote-tracking branch 'origin/master'

This commit is contained in:
Weblate 2017-03-24 18:13:46 +01:00
commit a4cb36b009
8 changed files with 647 additions and 577 deletions

View file

@ -42,7 +42,7 @@ public class SearchUICore {
private static final int TIMEOUT_BETWEEN_CHARS = 200;
private static final Log LOG = PlatformUtil.getLog(SearchUICore.class);
private SearchPhrase phrase;
private SearchResultCollection currentSearchResult;
private SearchResultCollection currentSearchResult;
private ThreadPoolExecutor singleThreadedExecutor;
private LinkedBlockingQueue<Runnable> taskQueue;
@ -55,9 +55,6 @@ public class SearchUICore {
private SearchSettings searchSettings;
private MapPoiTypes poiTypes;
private SearchCoreFactory.SearchCityByNameAPI searchCityByNameAPI;
private SearchCoreFactory.SearchPostcodeAPI searchPostcodeAPI;
private Class<? extends SearchCoreAPI> searchApiClass;
public SearchUICore(MapPoiTypes poiTypes, String locale, boolean transliterate) {
this.poiTypes = poiTypes;
@ -69,18 +66,6 @@ public class SearchUICore {
singleThreadedExecutor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, taskQueue);
}
public Class<? extends SearchCoreAPI> getSearchApiClass() {
return searchApiClass;
}
public void setSearchApiClass(Class<? extends SearchCoreAPI> searchApiClass) {
this.searchApiClass = searchApiClass;
}
public boolean isInCustomSearch() {
return searchApiClass != null;
}
public static class SearchResultCollection {
private List<SearchResult> searchResults;
private SearchPhrase phrase;
@ -98,17 +83,15 @@ public class SearchUICore {
return src;
}
public SearchResultCollection addSearchResults(List<SearchResult> 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<SearchResult> addedResults = new ArrayList<>(sr);
@ -117,34 +100,34 @@ public class SearchUICore {
filterSearchDuplicateResults(addedResults);
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,7 +136,6 @@ public class SearchUICore {
i++;
}
}
}
}
return this;
@ -178,36 +160,34 @@ public class SearchUICore {
private void filterSearchDuplicateResults(List<SearchResult> lst) {
ListIterator<SearchResult> it = lst.listIterator();
LinkedList<SearchResult> lstUnique = new LinkedList<SearchResult>();
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;
@ -256,22 +236,17 @@ public class SearchUICore {
@SuppressWarnings("unchecked")
public <T> T getApiByClass(Class<T> 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 <T extends SearchCoreAPI> SearchResultCollection shallowSearch(Class<T> cl,
String text, final ResultMatcher<SearchResult> matcher) throws IOException {
String text, final ResultMatcher<SearchResult> 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();
@ -297,24 +272,20 @@ public class SearchUICore {
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);
}
}
@ -351,8 +322,8 @@ public class SearchUICore {
private List<SearchResult> filterCurrentResults(List<SearchResult> rr, SearchPhrase phrase) {
List<SearchResult> l = currentSearchResult.searchResults;
for(SearchResult r : l) {
if(filterOneResult(r, phrase)) {
for (SearchResult r : l) {
if (filterOneResult(r, phrase)) {
rr.add(r);
}
}
@ -426,8 +397,8 @@ public class SearchUICore {
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<SearchCoreAPI> lst = new ArrayList<>();
if (searchApiClass != null) {
lst.add(getApiByClass(searchApiClass));
} else {
lst.addAll(apis);
}
ArrayList<SearchCoreAPI> lst = new ArrayList<>(apis);
Collections.sort(lst, new Comparator<SearchCoreAPI>() {
@Override
@ -470,7 +436,7 @@ 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);
}
}
@ -491,7 +457,7 @@ public class SearchUICore {
public SearchResultMatcher(ResultMatcher<SearchResult> matcher, int request,
AtomicInteger requestNumber, int totalLimit) {
AtomicInteger requestNumber, int totalLimit) {
this.matcher = matcher;
this.request = request;
this.requestNumber = requestNumber;
@ -513,7 +479,7 @@ public class SearchUICore {
}
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;
@ -535,10 +501,10 @@ public class SearchUICore {
@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) {
if (totalLimit == -1 || count < totalLimit) {
requestResults.add(object);
}
return true;
@ -556,31 +522,35 @@ public class SearchUICore {
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,17 +562,17 @@ 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);
}

View file

@ -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
@ -11,8 +11,11 @@ public enum ObjectType {
FAVORITE(true), WPT(true), RECENT_OBJ(true),
REGION(true),
SEARCH_API_FINISHED(false), SEARCH_API_REGION_FINISHED(false),
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;
}
}

View file

@ -20,4 +20,5 @@ public interface SearchCoreAPI {
*/
public boolean isSearchMoreAvailable(SearchPhrase phrase);
boolean isSearchAvailable(ObjectType[] typesToSearch);
}

View file

@ -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;
@ -81,9 +80,35 @@ public class SearchCoreFactory {
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
@ -97,12 +122,12 @@ public class SearchCoreFactory {
}
protected void subSearchApiOrPublish(SearchPhrase phrase,
SearchResultMatcher resultMatcher, SearchResult res, SearchBaseAPI api)
SearchResultMatcher resultMatcher, SearchResult res, SearchBaseAPI api)
throws IOException {
phrase.countUnknownWordsMatch(res);
int cnt = resultMatcher.getCount();
List<String> ws = phrase.getUnknownSearchWords(res.otherWordsMatch);
if(!ws.isEmpty() && api != null) {
if (!ws.isEmpty() && api != null && api.isSearchAvailable(phrase.getSearchTypes())) {
SearchPhrase nphrase = phrase.selectWord(res, ws,
phrase.isLastUnknownSearchWordComplete());
SearchResult prev = resultMatcher.setParentSearchResult(res);
@ -110,17 +135,25 @@ public class SearchCoreFactory {
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);
@ -168,128 +201,13 @@ 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<BinaryMapIndexReader> offlineIndexes = phrase.getOfflineIndexes(bbox, SearchPhraseDataType.ADDRESS);
while (offlineIndexes.hasNext()) {
BinaryMapIndexReader r = offlineIndexes.next();
SearchRequest<City> req = BinaryMapIndexReader.buildAddressRequest(new ResultMatcher<City>() {
@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<City> 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<City> cities;
if (phrase.isUnknownSearchWordPresent()) {
nm = phrase.getNameStringMatcher();
cities = searchAddressByNameAPI.townCitiesQR.queryInBox(bbox, new ArrayList<City>());
} else {
cities = searchAddressByNameAPI.townCitiesQR.queryInBox(closestBBox, new ArrayList<City>());
}
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<BinaryMapIndexReader, List<City>> townCities = new LinkedHashMap<>();
private QuadTree<City> townCitiesQR = new QuadTree<City>(new QuadRect(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE),
8, 0.55f);
@ -298,7 +216,9 @@ public class SearchCoreFactory {
private SearchBuildingAndIntersectionsByStreetAPI streetsApi;
public SearchAddressByNameAPI(SearchBuildingAndIntersectionsByStreetAPI streetsApi,
SearchStreetByCityAPI cityApi) {
SearchStreetByCityAPI cityApi) {
super(ObjectType.CITY, ObjectType.VILLAGE, ObjectType.POSTCODE,
ObjectType.STREET, ObjectType.HOUSE, ObjectType.STREET_INTERSECTION);
this.streetsApi = streetsApi;
this.cityApi = cityApi;
}
@ -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<BinaryMapIndexReader> 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) {
@ -423,13 +346,14 @@ public class SearchCoreFactory {
int x = MapUtils.get31TileNumberX(object.getLocation().getLongitude());
List<City> 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,7 +399,7 @@ 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;
@ -502,7 +429,7 @@ public class SearchCoreFactory {
currentFile[0] = r;
immediateResults.clear();
SearchRequest<MapObject> 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,19 +451,25 @@ 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];
@ -545,21 +478,21 @@ public class SearchCoreFactory {
final NameStringMatcher nm = phrase.getNameStringMatcher();
QuadRect bbox = phrase.getRadiusBBoxToSearch(BBOX_RADIUS_INSIDE);
SearchRequest<Amenity> req = BinaryMapIndexReader.buildSearchPoiRequest(
(int) bbox.centerX(), (int) bbox.centerY(),
(int)bbox.centerX(), (int)bbox.centerY(),
phrase.getUnknownSearchWord(),
(int) bbox.left, (int) bbox.right,
(int) bbox.top, (int) bbox.bottom,
(int)bbox.left, (int)bbox.right,
(int)bbox.top, (int)bbox.bottom,
new ResultMatcher<Amenity>() {
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,11 +501,11 @@ 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;
@ -589,6 +522,7 @@ public class SearchCoreFactory {
return resultMatcher.isCancelled() && (limit < LIMIT) ;
}
});
while (offlineIterator.hasNext()) {
BinaryMapIndexReader r = offlineIterator.next();
currentFile[0] = r;
@ -601,14 +535,14 @@ public class SearchCoreFactory {
@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;
@ -621,6 +555,7 @@ public class SearchCoreFactory {
}
public static class SearchAmenityTypesAPI extends SearchBaseAPI {
private Map<String, PoiType> translatedNames = new LinkedHashMap<>();
@ -631,6 +566,7 @@ public class SearchCoreFactory {
private MapPoiTypes types;
public SearchAmenityTypesAPI(MapPoiTypes types) {
super(ObjectType.POI_TYPE);
this.types = types;
}
@ -646,7 +582,10 @@ public class SearchCoreFactory {
@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);
@ -719,18 +658,21 @@ public class SearchCoreFactory {
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;
}
@ -765,10 +707,13 @@ 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;
@ -780,7 +725,7 @@ public class SearchCoreFactory {
List<BinaryMapIndexReader> oo = phrase.getOfflineIndexes();
for (BinaryMapIndexReader o : oo) {
ResultMatcher<Amenity> rm = getResultMatcher(phrase, resultMatcher, o);
if(obj instanceof CustomSearchPoiFilter) {
if (obj instanceof CustomSearchPoiFilter) {
rm = ((CustomSearchPoiFilter) obj).wrapResultMatcher(rm);
}
SearchRequest<Amenity> req = BinaryMapIndexReader.buildSearchPoiRequest(
@ -795,7 +740,7 @@ public class SearchCoreFactory {
}
private ResultMatcher<Amenity> getResultMatcher(final SearchPhrase phrase, final SearchResultMatcher resultMatcher,
final BinaryMapIndexReader selected) {
final BinaryMapIndexReader selected) {
final NameStringMatcher ns = phrase.getNameStringMatcher();
return new ResultMatcher<Amenity>() {
@ -833,7 +778,6 @@ public class SearchCoreFactory {
return resultMatcher.isCancelled();
}
};
}
private SearchPoiTypeFilter getPoiTypeFilter(AbstractPoiType pt) {
@ -870,7 +814,7 @@ 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;
}
@ -879,10 +823,13 @@ public class SearchCoreFactory {
}
public static class SearchStreetByCityAPI extends SearchBaseAPI {
private SearchBaseAPI streetsAPI;
public SearchStreetByCityAPI(SearchBuildingAndIntersectionsByStreetAPI streetsAPI) {
super(ObjectType.HOUSE, ObjectType.STREET, ObjectType.STREET_INTERSECTION);
this.streetsAPI = streetsAPI;
}
@ -895,6 +842,9 @@ public class SearchCoreFactory {
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;
}
@ -935,10 +885,9 @@ public class SearchCoreFactory {
return true;
}
@Override
public int getSearchPriority(SearchPhrase p) {
if(isLastWordCityGroup(p)) {
if (isLastWordCityGroup(p)) {
return SEARCH_STREET_BY_CITY_PRIORITY;
}
return -1;
@ -951,9 +900,15 @@ public class SearchCoreFactory {
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;
@ -961,21 +916,24 @@ public class SearchCoreFactory {
@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<Street> 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;
@ -985,7 +943,7 @@ public class SearchCoreFactory {
}
}
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<Building>() {
@ -1018,10 +977,11 @@ 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;
}
@ -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)) {
@ -1084,15 +1041,18 @@ public class SearchCoreFactory {
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() {
@ -1108,20 +1068,24 @@ public class SearchCoreFactory {
@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;
}
@ -1131,17 +1095,17 @@ public class SearchCoreFactory {
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<Double> d = new ArrayList<>();
List<Object> all = new ArrayList<>();
List<String> 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());
@ -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<Double> d = new ArrayList<>();
List<Object> all = new ArrayList<>();
List<String> 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;
}
@ -1313,23 +1277,23 @@ public class SearchCoreFactory {
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<Double> d, List<Object> all, List<String> 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));
@ -1400,7 +1364,7 @@ public class SearchCoreFactory {
word = i;
}
} else {
if(word != -1) {
if (word != -1) {
all.add(s.substring(word, i));
strings.add(s.substring(word, i));
}
@ -1414,16 +1378,18 @@ public class SearchCoreFactory {
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;
@ -1435,19 +1401,17 @@ 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;
@ -1462,5 +1426,4 @@ public class SearchCoreFactory {
return SEARCH_LOCATION_PRIORITY;
}
}
}

View file

@ -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 {
@ -289,6 +288,35 @@ public class SearchPhrase {
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);
}
@ -406,7 +434,6 @@ public class SearchPhrase {
return words.isEmpty() && unknownSearchPhrase.isEmpty();
}
public SearchWord getLastSelectedWord() {
if(words.isEmpty()) {
return null;
@ -414,7 +441,6 @@ public class SearchPhrase {
return words.get(words.size() - 1);
}
public LatLon getWordLocation() {
for(int i = words.size() - 1; i >= 0; i--) {
SearchWord sw = words.get(i);
@ -436,7 +462,6 @@ public class SearchPhrase {
return settings.getOriginalLocation();
}
public void selectFile(BinaryMapIndexReader object) {
if(indexes == null) {
indexes = new ArrayList<>();

View file

@ -1,12 +1,12 @@
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 {
@ -16,6 +16,9 @@ public class SearchSettings {
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) {
@ -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;
}
}

View file

@ -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<QuickSearchListItem> 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);

View file

@ -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<FavouritePoint> 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<FavouritePoint> 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,12 @@ public class QuickSearchHelper implements ResourceListener {
}
}
public static class SearchHistoryAPI extends SearchCoreFactory.SearchBaseAPI {
public static class SearchFavoriteByCategoryAPI extends SearchBaseAPI {
private OsmandApplication app;
public SearchHistoryAPI(OsmandApplication app) {
public SearchFavoriteByCategoryAPI(OsmandApplication app) {
super(ObjectType.FAVORITE);
this.app = app;
}
@ -223,7 +162,100 @@ 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;
}
List<FavouritePoint> 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<FavouritePoint> 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;
}
@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;
}
SearchHistoryHelper helper = SearchHistoryHelper.getInstance(app);
List<SearchHistoryHelper.HistoryEntry> points = helper.getHistoryEntries();
int p = 0;