2016-07-13 11:59:44 +02:00
|
|
|
package net.osmand.search;
|
2016-07-08 14:32:02 +02:00
|
|
|
|
2016-07-21 11:34:54 +02:00
|
|
|
import net.osmand.Collator;
|
2016-07-12 20:16:42 +02:00
|
|
|
import net.osmand.OsmAndCollator;
|
2016-07-08 14:32:02 +02:00
|
|
|
import net.osmand.PlatformUtil;
|
|
|
|
import net.osmand.ResultMatcher;
|
|
|
|
import net.osmand.binary.BinaryMapIndexReader;
|
2016-07-21 11:34:54 +02:00
|
|
|
import net.osmand.data.Amenity;
|
2016-07-08 14:32:02 +02:00
|
|
|
import net.osmand.data.LatLon;
|
2016-07-10 11:46:39 +02:00
|
|
|
import net.osmand.osm.MapPoiTypes;
|
2016-08-05 08:59:19 +02:00
|
|
|
import net.osmand.search.core.CustomSearchPoiFilter;
|
2016-07-13 22:32:52 +02:00
|
|
|
import net.osmand.search.core.ObjectType;
|
2016-07-13 11:59:44 +02:00
|
|
|
import net.osmand.search.core.SearchCoreAPI;
|
|
|
|
import net.osmand.search.core.SearchCoreFactory;
|
2016-08-03 23:44:52 +02:00
|
|
|
import net.osmand.search.core.SearchCoreFactory.SearchAmenityTypesAPI;
|
2016-07-24 22:34:26 +02:00
|
|
|
import net.osmand.search.core.SearchCoreFactory.SearchBuildingAndIntersectionsByStreetAPI;
|
|
|
|
import net.osmand.search.core.SearchCoreFactory.SearchStreetByCityAPI;
|
2016-07-13 11:59:44 +02:00
|
|
|
import net.osmand.search.core.SearchPhrase;
|
2016-08-05 08:59:19 +02:00
|
|
|
import net.osmand.search.core.SearchPhrase.NameStringMatcher;
|
2016-07-13 11:59:44 +02:00
|
|
|
import net.osmand.search.core.SearchResult;
|
|
|
|
import net.osmand.search.core.SearchSettings;
|
|
|
|
import net.osmand.search.core.SearchWord;
|
2016-07-08 14:32:02 +02:00
|
|
|
import net.osmand.util.Algorithms;
|
2016-07-21 11:34:54 +02:00
|
|
|
import net.osmand.util.MapUtils;
|
2016-07-08 14:32:02 +02:00
|
|
|
|
2016-07-11 10:01:25 +02:00
|
|
|
import org.apache.commons.logging.Log;
|
|
|
|
|
2016-08-13 13:29:02 +02:00
|
|
|
import java.io.IOException;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Collections;
|
|
|
|
import java.util.Comparator;
|
|
|
|
import java.util.Iterator;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.concurrent.LinkedBlockingQueue;
|
|
|
|
import java.util.concurrent.ThreadPoolExecutor;
|
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
|
|
|
2016-07-08 14:32:02 +02:00
|
|
|
public class SearchUICore {
|
|
|
|
|
2016-07-21 11:34:54 +02:00
|
|
|
private static final int TIMEOUT_BETWEEN_CHARS = 200;
|
2016-07-08 14:32:02 +02:00
|
|
|
private static final Log LOG = PlatformUtil.getLog(SearchUICore.class);
|
|
|
|
private SearchPhrase phrase;
|
2016-08-05 13:33:29 +02:00
|
|
|
private SearchResultCollection currentSearchResult;
|
2016-07-08 14:32:02 +02:00
|
|
|
|
|
|
|
private ThreadPoolExecutor singleThreadedExecutor;
|
|
|
|
private LinkedBlockingQueue<Runnable> taskQueue;
|
2016-08-13 13:29:02 +02:00
|
|
|
private Runnable onSearchStart = null;
|
2016-07-08 14:32:02 +02:00
|
|
|
private Runnable onResultsComplete = null;
|
|
|
|
private AtomicInteger requestNumber = new AtomicInteger();
|
2016-07-10 11:46:39 +02:00
|
|
|
private int totalLimit = -1; // -1 unlimited - not used
|
2016-07-08 14:32:02 +02:00
|
|
|
|
|
|
|
List<SearchCoreAPI> apis = new ArrayList<>();
|
|
|
|
private SearchSettings searchSettings;
|
2016-07-10 11:46:39 +02:00
|
|
|
private MapPoiTypes poiTypes;
|
2016-07-08 14:32:02 +02:00
|
|
|
|
|
|
|
|
2016-08-05 14:15:17 +02:00
|
|
|
public SearchUICore(MapPoiTypes poiTypes, String locale, boolean transliterate) {
|
2016-07-10 11:46:39 +02:00
|
|
|
this.poiTypes = poiTypes;
|
2016-07-08 14:32:02 +02:00
|
|
|
taskQueue = new LinkedBlockingQueue<Runnable>();
|
2016-07-28 21:07:49 +02:00
|
|
|
searchSettings = new SearchSettings(new ArrayList<BinaryMapIndexReader>());
|
2016-08-05 14:15:17 +02:00
|
|
|
searchSettings = searchSettings.setLang(locale, transliterate);
|
2016-08-05 13:33:29 +02:00
|
|
|
phrase = new SearchPhrase(searchSettings, OsmAndCollator.primaryCollator());
|
|
|
|
currentSearchResult = new SearchResultCollection(phrase);
|
2016-07-08 14:32:02 +02:00
|
|
|
singleThreadedExecutor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, taskQueue);
|
|
|
|
}
|
|
|
|
|
2016-07-11 10:01:25 +02:00
|
|
|
public static class SearchResultCollection {
|
|
|
|
private List<SearchResult> searchResults;
|
|
|
|
private SearchPhrase phrase;
|
|
|
|
|
2016-08-05 13:33:29 +02:00
|
|
|
public SearchResultCollection(SearchPhrase phrase) {
|
|
|
|
searchResults = new ArrayList<>();
|
2016-07-11 10:01:25 +02:00
|
|
|
this.phrase = phrase;
|
|
|
|
}
|
|
|
|
|
2016-08-05 13:33:29 +02:00
|
|
|
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;
|
2016-07-11 10:01:25 +02:00
|
|
|
}
|
2016-08-05 13:33:29 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
2016-08-05 21:07:49 +02:00
|
|
|
public SearchResultCollection addSearchResults(List<SearchResult> sr, boolean resortAll, boolean removeDuplicates) {
|
2016-08-05 13:33:29 +02:00
|
|
|
if(resortAll) {
|
|
|
|
this.searchResults.addAll(sr);
|
|
|
|
sortSearchResults();
|
|
|
|
if(removeDuplicates) {
|
|
|
|
filterSearchDuplicateResults();
|
|
|
|
}
|
|
|
|
} else {
|
2016-08-05 16:08:07 +02:00
|
|
|
if(!removeDuplicates) {
|
|
|
|
this.searchResults.addAll(sr);
|
|
|
|
} else {
|
2016-08-05 13:33:29 +02:00
|
|
|
ArrayList<SearchResult> addedResults = new ArrayList<>(sr);
|
|
|
|
SearchResultComparator cmp = new SearchResultComparator(phrase);
|
|
|
|
Collections.sort(addedResults, cmp);
|
|
|
|
filterSearchDuplicateResults(addedResults);
|
|
|
|
int i = 0;
|
|
|
|
int j = 0;
|
|
|
|
while(j < addedResults.size()) {
|
|
|
|
SearchResult addedResult = addedResults.get(j);
|
|
|
|
if(i >= searchResults.size()) {
|
|
|
|
if(searchResults.size() == 0 ||
|
|
|
|
!sameSearchResult(addedResult, searchResults.get(searchResults.size() - 1))) {
|
|
|
|
searchResults.add(addedResult);
|
|
|
|
}
|
|
|
|
j++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
SearchResult existingResult = searchResults.get(i);
|
|
|
|
if(sameSearchResult(addedResult, existingResult)) {
|
|
|
|
j++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
int compare = cmp.compare(existingResult, addedResult);
|
|
|
|
if(compare == 0) {
|
|
|
|
// existingResult == addedResult
|
|
|
|
j++;
|
|
|
|
} else if(compare > 0) {
|
|
|
|
// existingResult > addedResult
|
|
|
|
this.searchResults.add(addedResults.get(j));
|
|
|
|
j++;
|
|
|
|
} else {
|
|
|
|
// existingResult < addedResult
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
2016-08-05 21:07:49 +02:00
|
|
|
return this;
|
2016-08-05 13:33:29 +02:00
|
|
|
}
|
|
|
|
|
2016-07-11 10:01:25 +02:00
|
|
|
public List<SearchResult> getCurrentSearchResults() {
|
2016-08-05 13:33:29 +02:00
|
|
|
return Collections.unmodifiableList(searchResults);
|
2016-07-11 10:01:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public SearchPhrase getPhrase() {
|
|
|
|
return phrase;
|
|
|
|
}
|
2016-08-05 13:33:29 +02:00
|
|
|
|
|
|
|
public void sortSearchResults() {
|
|
|
|
Collections.sort(searchResults, new SearchResultComparator(phrase));
|
|
|
|
}
|
|
|
|
|
|
|
|
public void filterSearchDuplicateResults() {
|
|
|
|
filterSearchDuplicateResults(searchResults);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void filterSearchDuplicateResults(List<SearchResult> lst) {
|
|
|
|
Iterator<SearchResult> it = lst.iterator();
|
|
|
|
SearchResult found = null;
|
|
|
|
while(it.hasNext()) {
|
|
|
|
SearchResult r = it.next();
|
|
|
|
if(found != null && sameSearchResult(found, r)) {
|
|
|
|
it.remove();
|
|
|
|
} else {
|
|
|
|
found = r;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public boolean sameSearchResult(SearchResult r1, SearchResult r2) {
|
|
|
|
if(r1.location != null && r2.location != null) {
|
|
|
|
Amenity a1 = null;
|
|
|
|
if(r1.object instanceof Amenity) {
|
|
|
|
a1 = (Amenity) r1.object;
|
|
|
|
}
|
|
|
|
Amenity a2 = null;
|
|
|
|
if(r2.object instanceof Amenity) {
|
|
|
|
a2 = (Amenity) r2.object;
|
|
|
|
}
|
|
|
|
if (r1.localeName.equals(r2.localeName)) {
|
|
|
|
double similarityRadius = 30;
|
|
|
|
if (a1 != null && a2 != null) {
|
|
|
|
// here 2 points are amenity
|
|
|
|
String type1 = a1.getType().getKeyName();
|
|
|
|
String type2 = a2.getType().getKeyName();
|
|
|
|
String subType1 = a1.getSubType();
|
|
|
|
String subType2 = a2.getSubType();
|
|
|
|
if (!type1.equals(type2)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (type1.equals("natural")) {
|
|
|
|
similarityRadius = 10000;
|
|
|
|
} else if (subType1.equals(subType2)) {
|
|
|
|
if (subType1.contains("cn_ref") || subType1.contains("wn_ref")
|
|
|
|
|| (subType1.startsWith("route_hiking_") && subType1.endsWith("n_poi"))) {
|
2016-10-13 19:09:30 +02:00
|
|
|
similarityRadius = 50000;
|
2016-08-05 13:33:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if(ObjectType.isAddress(r1.objectType) && ObjectType.isAddress(r2.objectType)) {
|
|
|
|
similarityRadius = 100;
|
|
|
|
}
|
|
|
|
return MapUtils.getDistance(r1.location, r2.location) < similarityRadius;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2016-07-11 10:01:25 +02:00
|
|
|
}
|
|
|
|
|
2016-07-28 21:02:23 +02:00
|
|
|
public void setPoiTypes(MapPoiTypes poiTypes) {
|
|
|
|
this.poiTypes = poiTypes;
|
|
|
|
}
|
|
|
|
|
2016-07-08 14:32:02 +02:00
|
|
|
public int getTotalLimit() {
|
|
|
|
return totalLimit;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setTotalLimit(int totalLimit) {
|
|
|
|
this.totalLimit = totalLimit;
|
|
|
|
}
|
|
|
|
|
2016-08-03 23:44:52 +02:00
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
public <T> T getApiByClass(Class<T> cl) {
|
|
|
|
for(SearchCoreAPI a : apis) {
|
|
|
|
if(cl.isInstance(a)) {
|
|
|
|
return (T) a;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
public <T extends SearchCoreAPI> SearchResultCollection shallowSearch(Class<T> cl,
|
|
|
|
String text, final ResultMatcher<SearchResult> matcher) throws IOException {
|
|
|
|
T api = getApiByClass(cl);
|
|
|
|
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);
|
|
|
|
|
2016-08-05 13:33:29 +02:00
|
|
|
SearchResultCollection collection = new SearchResultCollection(
|
2016-08-03 23:44:52 +02:00
|
|
|
sphrase);
|
2016-08-05 13:33:29 +02:00
|
|
|
collection.addSearchResults(rm.getRequestResults(), true, true);
|
|
|
|
LOG.info(">> Shallow Search phrase " + phrase + " " + rm.getRequestResults().size());
|
|
|
|
return collection;
|
2016-08-03 23:44:52 +02:00
|
|
|
}
|
2016-08-05 13:33:29 +02:00
|
|
|
return null;
|
2016-08-03 23:44:52 +02:00
|
|
|
}
|
|
|
|
|
2016-07-08 14:32:02 +02:00
|
|
|
public void init() {
|
2016-07-24 22:34:26 +02:00
|
|
|
apis.add(new SearchCoreFactory.SearchLocationAndUrlAPI());
|
2016-07-10 11:46:39 +02:00
|
|
|
apis.add(new SearchCoreFactory.SearchAmenityTypesAPI(poiTypes));
|
2016-07-10 12:20:18 +02:00
|
|
|
apis.add(new SearchCoreFactory.SearchAmenityByTypeAPI(poiTypes));
|
2016-07-10 11:46:39 +02:00
|
|
|
apis.add(new SearchCoreFactory.SearchAmenityByNameAPI());
|
2016-07-24 22:34:26 +02:00
|
|
|
SearchBuildingAndIntersectionsByStreetAPI streetsApi =
|
|
|
|
new SearchCoreFactory.SearchBuildingAndIntersectionsByStreetAPI();
|
|
|
|
apis.add(streetsApi);
|
|
|
|
SearchStreetByCityAPI cityApi = new SearchCoreFactory.SearchStreetByCityAPI(streetsApi);
|
|
|
|
apis.add(cityApi);
|
|
|
|
apis.add(new SearchCoreFactory.SearchAddressByNameAPI(streetsApi, cityApi));
|
2016-07-08 14:32:02 +02:00
|
|
|
}
|
2016-09-28 20:11:57 +02:00
|
|
|
|
|
|
|
public void clearCustomSearchPoiFilters() {
|
|
|
|
for(SearchCoreAPI capi : apis) {
|
|
|
|
if(capi instanceof SearchAmenityTypesAPI) {
|
|
|
|
((SearchAmenityTypesAPI) capi).clearCustomFilters();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-03 23:44:52 +02:00
|
|
|
public void addCustomSearchPoiFilter(CustomSearchPoiFilter poiFilter, int priority) {
|
|
|
|
for(SearchCoreAPI capi : apis) {
|
|
|
|
if(capi instanceof SearchAmenityTypesAPI) {
|
|
|
|
((SearchAmenityTypesAPI) capi).addCustomFilter(poiFilter, priority);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-12 15:47:34 +02:00
|
|
|
public void registerAPI(SearchCoreAPI api) {
|
|
|
|
apis.add(api);
|
|
|
|
}
|
|
|
|
|
2016-07-11 10:01:25 +02:00
|
|
|
|
|
|
|
public SearchResultCollection getCurrentSearchResult() {
|
|
|
|
return currentSearchResult;
|
2016-07-08 14:32:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public SearchPhrase getPhrase() {
|
|
|
|
return phrase;
|
|
|
|
}
|
2016-08-13 13:29:02 +02:00
|
|
|
|
|
|
|
public void setOnSearchStart(Runnable onSearchStart) {
|
|
|
|
this.onSearchStart = onSearchStart;
|
|
|
|
}
|
|
|
|
|
2016-07-08 14:32:02 +02:00
|
|
|
public void setOnResultsComplete(Runnable onResultsComplete) {
|
|
|
|
this.onResultsComplete = onResultsComplete;
|
|
|
|
}
|
2016-07-19 17:39:21 +02:00
|
|
|
|
|
|
|
public SearchSettings getSearchSettings() {
|
|
|
|
return searchSettings;
|
|
|
|
}
|
|
|
|
|
2016-07-10 11:46:39 +02:00
|
|
|
public void updateSettings(SearchSettings settings) {
|
|
|
|
searchSettings = settings;
|
2016-07-08 14:32:02 +02:00
|
|
|
}
|
|
|
|
|
2016-07-11 10:01:25 +02:00
|
|
|
private List<SearchResult> filterCurrentResults(List<SearchResult> rr, SearchPhrase phrase) {
|
|
|
|
List<SearchResult> l = currentSearchResult.searchResults;
|
2016-07-08 14:32:02 +02:00
|
|
|
for(SearchResult r : l) {
|
|
|
|
if(filterOneResult(r, phrase)) {
|
|
|
|
rr.add(r);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return rr;
|
|
|
|
}
|
|
|
|
|
|
|
|
private boolean filterOneResult(SearchResult object, SearchPhrase phrase) {
|
2016-07-10 11:46:39 +02:00
|
|
|
NameStringMatcher nameStringMatcher = phrase.getNameStringMatcher();
|
|
|
|
return nameStringMatcher.matches(object.localeName) || nameStringMatcher.matches(object.otherNames);
|
2016-07-08 14:32:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public boolean selectSearchResult(SearchResult r) {
|
|
|
|
this.phrase = this.phrase.selectWord(r);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-07-28 19:17:26 +02:00
|
|
|
public void resetPhrase() {
|
|
|
|
this.phrase = this.phrase.generateNewPhrase("", searchSettings);
|
|
|
|
}
|
|
|
|
|
2016-07-11 10:01:25 +02:00
|
|
|
public SearchResultCollection search(final String text, final ResultMatcher<SearchResult> matcher) {
|
2016-07-08 14:32:02 +02:00
|
|
|
final int request = requestNumber.incrementAndGet();
|
|
|
|
final SearchPhrase phrase = this.phrase.generateNewPhrase(text, searchSettings);
|
|
|
|
this.phrase = phrase;
|
2016-08-05 13:33:29 +02:00
|
|
|
SearchResultCollection quickRes = new SearchResultCollection(phrase);
|
2016-07-11 10:01:25 +02:00
|
|
|
filterCurrentResults(quickRes.searchResults, phrase);
|
2016-07-12 20:16:42 +02:00
|
|
|
LOG.info("> Search phrase " + phrase + " " + quickRes.searchResults.size());
|
2016-07-08 14:32:02 +02:00
|
|
|
singleThreadedExecutor.submit(new Runnable() {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
try {
|
2016-08-13 13:29:02 +02:00
|
|
|
if (onSearchStart != null) {
|
|
|
|
onSearchStart.run();
|
|
|
|
}
|
2016-07-13 11:59:44 +02:00
|
|
|
SearchResultMatcher rm = new SearchResultMatcher(matcher, request, requestNumber, totalLimit);
|
2016-07-21 11:34:54 +02:00
|
|
|
if(TIMEOUT_BETWEEN_CHARS > 0) {
|
|
|
|
Thread.sleep(TIMEOUT_BETWEEN_CHARS);
|
|
|
|
}
|
2016-07-24 19:05:51 +02:00
|
|
|
if(rm.isCancelled()) {
|
|
|
|
return;
|
|
|
|
}
|
2016-07-08 14:32:02 +02:00
|
|
|
searchInBackground(phrase, rm);
|
|
|
|
if (!rm.isCancelled()) {
|
2016-07-24 19:05:51 +02:00
|
|
|
|
2016-08-05 13:33:29 +02:00
|
|
|
SearchResultCollection collection = new SearchResultCollection(
|
2016-07-11 10:01:25 +02:00
|
|
|
phrase);
|
2016-08-05 13:33:29 +02:00
|
|
|
collection.addSearchResults(rm.getRequestResults(), true, true);
|
|
|
|
LOG.info(">> Search phrase " + phrase + " " + rm.getRequestResults().size());
|
2016-07-11 10:01:25 +02:00
|
|
|
currentSearchResult = collection;
|
2016-07-08 14:32:02 +02:00
|
|
|
if (onResultsComplete != null) {
|
|
|
|
onResultsComplete.run();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
});
|
2016-07-11 10:01:25 +02:00
|
|
|
return quickRes;
|
2016-07-08 14:32:02 +02:00
|
|
|
}
|
2016-07-22 14:25:45 +02:00
|
|
|
|
2016-07-24 22:34:26 +02:00
|
|
|
|
2016-08-05 13:58:01 +02:00
|
|
|
public boolean isSearchMoreAvailable(SearchPhrase phrase) {
|
|
|
|
for(SearchCoreAPI api : apis) {
|
|
|
|
if(api.getSearchPriority(phrase) >= 0 && api.isSearchMoreAvailable(phrase)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2016-07-24 19:05:51 +02:00
|
|
|
|
2016-07-08 14:32:02 +02:00
|
|
|
private void searchInBackground(final SearchPhrase phrase, SearchResultMatcher matcher) {
|
2016-08-03 23:44:52 +02:00
|
|
|
preparePhrase(phrase);
|
2016-07-08 14:32:02 +02:00
|
|
|
ArrayList<SearchCoreAPI> lst = new ArrayList<>(apis);
|
|
|
|
Collections.sort(lst, new Comparator<SearchCoreAPI>() {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int compare(SearchCoreAPI o1, SearchCoreAPI o2) {
|
|
|
|
return Algorithms.compare(o1.getSearchPriority(phrase),
|
|
|
|
o2.getSearchPriority(phrase));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
for(SearchCoreAPI api : lst) {
|
|
|
|
if(matcher.isCancelled()) {
|
|
|
|
break;
|
|
|
|
}
|
2016-07-10 11:46:39 +02:00
|
|
|
if(api.getSearchPriority(phrase) == -1) {
|
|
|
|
continue;
|
|
|
|
}
|
2016-07-08 14:32:02 +02:00
|
|
|
try {
|
|
|
|
api.search(phrase, matcher);
|
2016-08-05 13:33:29 +02:00
|
|
|
|
2016-07-13 22:32:52 +02:00
|
|
|
matcher.apiSearchFinished(api, phrase);
|
2016-07-12 20:16:42 +02:00
|
|
|
} catch (Throwable e) {
|
2016-07-08 14:32:02 +02:00
|
|
|
e.printStackTrace();
|
2016-07-12 20:16:42 +02:00
|
|
|
LOG.error(e.getMessage(), e);
|
2016-07-08 14:32:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-08-03 23:44:52 +02:00
|
|
|
|
|
|
|
private void preparePhrase(final SearchPhrase phrase) {
|
|
|
|
for (SearchWord sw : phrase.getWords()) {
|
|
|
|
if(sw.getResult() != null && sw.getResult().file != null) {
|
|
|
|
phrase.selectFile(sw.getResult().file);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
phrase.sortFiles();
|
|
|
|
}
|
2016-07-08 14:32:02 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-08-05 13:33:29 +02:00
|
|
|
public static class SearchResultMatcher implements ResultMatcher<SearchResult>{
|
2016-07-08 14:32:02 +02:00
|
|
|
private final List<SearchResult> requestResults = new ArrayList<>();
|
2016-07-13 11:59:44 +02:00
|
|
|
private final ResultMatcher<SearchResult> matcher;
|
2016-07-08 14:32:02 +02:00
|
|
|
private final int request;
|
2016-07-13 11:59:44 +02:00
|
|
|
private final int totalLimit;
|
2016-07-24 19:05:51 +02:00
|
|
|
private SearchResult parentSearchResult;
|
2016-07-13 11:59:44 +02:00
|
|
|
private final AtomicInteger requestNumber;
|
2016-07-08 14:32:02 +02:00
|
|
|
int count = 0;
|
|
|
|
|
|
|
|
|
2016-07-13 11:59:44 +02:00
|
|
|
public SearchResultMatcher(ResultMatcher<SearchResult> matcher, int request,
|
|
|
|
AtomicInteger requestNumber, int totalLimit) {
|
2016-07-08 14:32:02 +02:00
|
|
|
this.matcher = matcher;
|
|
|
|
this.request = request;
|
2016-07-13 11:59:44 +02:00
|
|
|
this.requestNumber = requestNumber;
|
|
|
|
this.totalLimit = totalLimit;
|
2016-07-08 14:32:02 +02:00
|
|
|
}
|
|
|
|
|
2016-07-24 22:34:26 +02:00
|
|
|
public SearchResult setParentSearchResult(SearchResult parentSearchResult) {
|
|
|
|
SearchResult prev = this.parentSearchResult;
|
2016-07-24 19:05:51 +02:00
|
|
|
this.parentSearchResult = parentSearchResult;
|
2016-07-24 22:34:26 +02:00
|
|
|
return prev;
|
2016-07-24 19:05:51 +02:00
|
|
|
}
|
|
|
|
|
2016-07-08 14:32:02 +02:00
|
|
|
public List<SearchResult> getRequestResults() {
|
|
|
|
return requestResults;
|
|
|
|
}
|
2016-07-12 20:16:42 +02:00
|
|
|
|
2016-07-24 22:34:26 +02:00
|
|
|
public int getCount() {
|
|
|
|
return requestResults.size();
|
|
|
|
}
|
|
|
|
|
2016-07-13 22:32:52 +02:00
|
|
|
public void apiSearchFinished(SearchCoreAPI api, SearchPhrase phrase) {
|
|
|
|
if(matcher != null) {
|
|
|
|
SearchResult sr = new SearchResult(phrase);
|
|
|
|
sr.objectType = ObjectType.SEARCH_API_FINISHED;
|
|
|
|
sr.object = api;
|
2016-07-24 19:05:51 +02:00
|
|
|
sr.parentSearchResult = parentSearchResult;
|
2016-07-13 22:32:52 +02:00
|
|
|
matcher.publish(sr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-15 10:07:38 +02:00
|
|
|
public void apiSearchRegionFinished(SearchCoreAPI api, BinaryMapIndexReader region, SearchPhrase phrase) {
|
|
|
|
if(matcher != null) {
|
|
|
|
SearchResult sr = new SearchResult(phrase);
|
|
|
|
sr.objectType = ObjectType.SEARCH_API_REGION_FINISHED;
|
|
|
|
sr.object = api;
|
2016-07-24 19:05:51 +02:00
|
|
|
sr.parentSearchResult = parentSearchResult;
|
2016-07-15 10:07:38 +02:00
|
|
|
sr.file = region;
|
|
|
|
matcher.publish(sr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-08 14:32:02 +02:00
|
|
|
@Override
|
|
|
|
public boolean publish(SearchResult object) {
|
|
|
|
if(matcher == null || matcher.publish(object)) {
|
|
|
|
count++;
|
2016-07-24 19:05:51 +02:00
|
|
|
object.parentSearchResult = parentSearchResult;
|
2016-07-08 14:32:02 +02:00
|
|
|
if(totalLimit == -1 || count < totalLimit) {
|
|
|
|
requestResults.add(object);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
@Override
|
|
|
|
public boolean isCancelled() {
|
|
|
|
boolean cancelled = request != requestNumber.get();
|
|
|
|
return cancelled || (matcher != null && matcher.isCancelled());
|
|
|
|
}
|
2016-08-05 13:33:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public static class SearchResultComparator implements Comparator<SearchResult> {
|
|
|
|
private SearchPhrase sp;
|
|
|
|
private Collator collator;
|
|
|
|
private LatLon loc;
|
|
|
|
|
|
|
|
public SearchResultComparator(SearchPhrase sp) {
|
|
|
|
this.sp = sp;
|
|
|
|
this.collator = sp.getCollator();
|
|
|
|
loc = sp.getLastTokenLocation();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int compare(SearchResult o1, SearchResult o2) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
int st1 = Algorithms.extractFirstIntegerNumber(o1.localeName);
|
|
|
|
int st2 = Algorithms.extractFirstIntegerNumber(o2.localeName);
|
|
|
|
if(st1 != st2) {
|
|
|
|
return Algorithms.compare(st1, st2);
|
|
|
|
}
|
|
|
|
cmp = collator.compare(o1.localeName, o2.localeName);
|
|
|
|
if(cmp != 0) {
|
|
|
|
return cmp;
|
|
|
|
}
|
2016-10-19 11:32:20 +02:00
|
|
|
if (o1.object instanceof Amenity && o2.object instanceof Amenity) {
|
2016-10-18 14:19:26 +02:00
|
|
|
// here 2 points are amenity
|
2016-10-19 11:32:20 +02:00
|
|
|
Amenity a1 = (Amenity) o1.object;
|
|
|
|
Amenity a2 = (Amenity) o2.object;
|
2016-10-18 14:19:26 +02:00
|
|
|
String type1 = a1.getType().getKeyName();
|
|
|
|
String type2 = a2.getType().getKeyName();
|
|
|
|
String subType1 = a1.getSubType();
|
|
|
|
String subType2 = a2.getSubType();
|
|
|
|
cmp = collator.compare(type1, type2);
|
|
|
|
if(cmp != 0) {
|
|
|
|
return cmp;
|
|
|
|
}
|
|
|
|
cmp = collator.compare(subType1, subType2);
|
|
|
|
if(cmp != 0) {
|
|
|
|
return cmp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-05 13:33:29 +02:00
|
|
|
s1 = o1.getSearchDistance(loc, 1);
|
|
|
|
s2 = o2.getSearchDistance(loc, 1);
|
|
|
|
return Double.compare(s1, s2);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2016-07-08 14:32:02 +02:00
|
|
|
}
|