Implement search example
This commit is contained in:
parent
9f1c67c4f0
commit
9899c8de5e
11 changed files with 718 additions and 387 deletions
|
@ -17,7 +17,6 @@ import java.io.InputStreamReader;
|
||||||
import java.io.RandomAccessFile;
|
import java.io.RandomAccessFile;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -1462,7 +1461,7 @@ public class BinaryMapIndexReader {
|
||||||
StringMatcherMode matcherMode) {
|
StringMatcherMode matcherMode) {
|
||||||
SearchRequest<T> request = new SearchRequest<T>();
|
SearchRequest<T> request = new SearchRequest<T>();
|
||||||
request.resultMatcher = resultMatcher;
|
request.resultMatcher = resultMatcher;
|
||||||
request.nameQuery = nameRequest;
|
request.nameQuery = nameRequest.trim();
|
||||||
request.matcherMode = matcherMode;
|
request.matcherMode = matcherMode;
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
@ -1553,7 +1552,7 @@ public class BinaryMapIndexReader {
|
||||||
request.top = stop;
|
request.top = stop;
|
||||||
request.bottom = sbottom;
|
request.bottom = sbottom;
|
||||||
request.resultMatcher = resultMatcher;
|
request.resultMatcher = resultMatcher;
|
||||||
request.nameQuery = nameFilter;
|
request.nameQuery = nameFilter.trim();
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,141 +0,0 @@
|
||||||
package net.osmand.search.example;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
|
|
||||||
import net.osmand.ResultMatcher;
|
|
||||||
import net.osmand.StringMatcher;
|
|
||||||
import net.osmand.binary.BinaryMapIndexReader;
|
|
||||||
import net.osmand.data.LatLon;
|
|
||||||
import net.osmand.search.example.core.SearchPhrase;
|
|
||||||
import net.osmand.search.example.core.SearchResult;
|
|
||||||
|
|
||||||
public class SearchUIAdapter {
|
|
||||||
|
|
||||||
private SearchPhrase phrase = new SearchPhrase(null);
|
|
||||||
private List<SearchResult> currentSearchResults = new ArrayList<>();
|
|
||||||
|
|
||||||
private final BinaryMapIndexReader[] searchIndexes;
|
|
||||||
|
|
||||||
private ThreadPoolExecutor singleThreadedExecutor;
|
|
||||||
private LinkedBlockingQueue<Runnable> taskQueue;
|
|
||||||
private Runnable onResultsComplete = null;
|
|
||||||
private AtomicInteger requestNumber = new AtomicInteger();
|
|
||||||
|
|
||||||
public SearchUIAdapter(BinaryMapIndexReader[] searchIndexes) {
|
|
||||||
this.searchIndexes = searchIndexes;
|
|
||||||
taskQueue = new LinkedBlockingQueue<Runnable>();
|
|
||||||
singleThreadedExecutor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,taskQueue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<SearchResult> getCurrentSearchResults() {
|
|
||||||
return currentSearchResults;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOnResultsComplete(Runnable onResultsComplete) {
|
|
||||||
this.onResultsComplete = onResultsComplete;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSearchLocation(LatLon l) {
|
|
||||||
phrase = new SearchPhrase(l);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateSearchPhrase(SearchPhrase p) {
|
|
||||||
// TODO update logic
|
|
||||||
boolean hasSameConstantWords = p.hasSameConstantWords(this.phrase);
|
|
||||||
this.phrase = p;
|
|
||||||
if(hasSameConstantWords) {
|
|
||||||
filterCurrentResults(phrase);
|
|
||||||
}
|
|
||||||
boolean lastWordLooksLikeURLOrNumber = true;
|
|
||||||
if(lastWordLooksLikeURLOrNumber) {
|
|
||||||
// add search result location
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean poiTypeWasNotSelected = true;
|
|
||||||
if(poiTypeWasNotSelected) {
|
|
||||||
// add search result poi filters
|
|
||||||
sortSearchResults(currentSearchResults);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private List<SearchResult> filterCurrentResults(SearchPhrase phrase) {
|
|
||||||
List<SearchResult> rr = new ArrayList<>();
|
|
||||||
List<SearchResult> l = currentSearchResults;
|
|
||||||
for(SearchResult r : l) {
|
|
||||||
if(filterOneResult(r, phrase)) {
|
|
||||||
rr.add(r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return rr;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean filterOneResult(SearchResult object, SearchPhrase phrase) {
|
|
||||||
StringMatcher nameStringMatcher = phrase.getNameStringMatcher();
|
|
||||||
if(phrase.getLastWord().length() <= 2 && phrase.isNoSelectedType()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return nameStringMatcher.matches(object.mainName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<SearchResult> search(final String text) {
|
|
||||||
List<SearchResult> list = new ArrayList<>();
|
|
||||||
final int request = requestNumber.incrementAndGet();
|
|
||||||
final SearchPhrase phrase = this.phrase.generateNewPhrase(text);
|
|
||||||
this.phrase = phrase;
|
|
||||||
list.addAll(filterCurrentResults(phrase));
|
|
||||||
System.out.println("> Search phrase " + phrase + " " + list.size());
|
|
||||||
singleThreadedExecutor.getQueue().drainTo(new ArrayList<>());
|
|
||||||
singleThreadedExecutor.submit(new Runnable() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
Thread.sleep(200);
|
|
||||||
List<SearchResult> rs = new ArrayList<>();
|
|
||||||
for (BinaryMapIndexReader bmir : searchIndexes) {
|
|
||||||
if (bmir.getRegionCenter() != null) {
|
|
||||||
SearchResult sr = new SearchResult();
|
|
||||||
sr.mainName = bmir.getRegionName();
|
|
||||||
sr.location = bmir.getRegionCenter();
|
|
||||||
sr.preferredZoom = 6;
|
|
||||||
if(filterOneResult(sr, phrase)) {
|
|
||||||
rs.add(sr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
boolean cancelled = request != requestNumber.get();
|
|
||||||
if (!cancelled) {
|
|
||||||
sortSearchResults(rs);
|
|
||||||
System.out.println(">> Search phrase " + phrase + " " + rs.size());
|
|
||||||
currentSearchResults = rs;
|
|
||||||
if (onResultsComplete != null) {
|
|
||||||
onResultsComplete.run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sortSearchResults(List<SearchResult> searchResults) {
|
|
||||||
// sort SearchResult by 1. searchDistance 2. Name
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
234
OsmAnd-java/src/net/osmand/search/example/SearchUICore.java
Normal file
234
OsmAnd-java/src/net/osmand/search/example/SearchUICore.java
Normal file
|
@ -0,0 +1,234 @@
|
||||||
|
package net.osmand.search.example;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.text.Collator;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
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;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
|
||||||
|
import net.osmand.PlatformUtil;
|
||||||
|
import net.osmand.ResultMatcher;
|
||||||
|
import net.osmand.StringMatcher;
|
||||||
|
import net.osmand.binary.BinaryMapIndexReader;
|
||||||
|
import net.osmand.data.LatLon;
|
||||||
|
import net.osmand.search.example.core.ObjectType;
|
||||||
|
import net.osmand.search.example.core.SearchCoreAPI;
|
||||||
|
import net.osmand.search.example.core.SearchCoreFactory;
|
||||||
|
import net.osmand.search.example.core.SearchPhrase;
|
||||||
|
import net.osmand.search.example.core.SearchResult;
|
||||||
|
import net.osmand.search.example.core.SearchSettings;
|
||||||
|
import net.osmand.search.example.core.SearchWord;
|
||||||
|
import net.osmand.util.Algorithms;
|
||||||
|
|
||||||
|
public class SearchUICore {
|
||||||
|
|
||||||
|
private static final Log LOG = PlatformUtil.getLog(SearchUICore.class);
|
||||||
|
private SearchPhrase phrase;
|
||||||
|
private List<SearchResult> currentSearchResults = new ArrayList<>();
|
||||||
|
|
||||||
|
private ThreadPoolExecutor singleThreadedExecutor;
|
||||||
|
private LinkedBlockingQueue<Runnable> taskQueue;
|
||||||
|
private Runnable onResultsComplete = null;
|
||||||
|
private AtomicInteger requestNumber = new AtomicInteger();
|
||||||
|
private int totalLimit = 20; // -1 unlimited
|
||||||
|
|
||||||
|
List<SearchCoreAPI> apis = new ArrayList<>();
|
||||||
|
private SearchSettings searchSettings;
|
||||||
|
|
||||||
|
|
||||||
|
public SearchUICore(BinaryMapIndexReader[] searchIndexes) {
|
||||||
|
List<BinaryMapIndexReader> searchIndexesList = Arrays.asList(searchIndexes);
|
||||||
|
taskQueue = new LinkedBlockingQueue<Runnable>();
|
||||||
|
searchSettings = new SearchSettings(searchIndexesList);
|
||||||
|
phrase = new SearchPhrase(searchSettings);
|
||||||
|
singleThreadedExecutor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, taskQueue);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTotalLimit() {
|
||||||
|
return totalLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTotalLimit(int totalLimit) {
|
||||||
|
this.totalLimit = totalLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init() {
|
||||||
|
// apis.add(new SearchAmenityByNameAPI());
|
||||||
|
// apis.add(new SearchAmenityByTypeAPI());
|
||||||
|
// apis.add(new SearchAddressByNameAPI());
|
||||||
|
// apis.add(new SearchStreetByCityAPI());
|
||||||
|
// apis.add(new SearchBuildingAndIntersectionsByStreetAPI());
|
||||||
|
apis.add(new SearchCoreFactory.SearchRegionByNameAPI());
|
||||||
|
apis.add(new SearchCoreFactory.SearchAddressByNameAPI());
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<SearchResult> getCurrentSearchResults() {
|
||||||
|
return currentSearchResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SearchPhrase getPhrase() {
|
||||||
|
return phrase;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnResultsComplete(Runnable onResultsComplete) {
|
||||||
|
this.onResultsComplete = onResultsComplete;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setSearchLocation(LatLon l) {
|
||||||
|
searchSettings = searchSettings.setOriginalLocation(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private List<SearchResult> filterCurrentResults(SearchPhrase phrase) {
|
||||||
|
List<SearchResult> rr = new ArrayList<>();
|
||||||
|
List<SearchResult> l = currentSearchResults;
|
||||||
|
for(SearchResult r : l) {
|
||||||
|
if(filterOneResult(r, phrase)) {
|
||||||
|
rr.add(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean filterOneResult(SearchResult object, SearchPhrase phrase) {
|
||||||
|
StringMatcher nameStringMatcher = phrase.getNameStringMatcher();
|
||||||
|
|
||||||
|
return nameStringMatcher.matches(object.mainName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean selectSearchResult(SearchResult r) {
|
||||||
|
this.phrase = this.phrase.selectWord(r);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<SearchResult> search(final String text, final ResultMatcher<SearchResult> matcher) {
|
||||||
|
List<SearchResult> list = new ArrayList<>();
|
||||||
|
final int request = requestNumber.incrementAndGet();
|
||||||
|
final SearchPhrase phrase = this.phrase.generateNewPhrase(text, searchSettings);
|
||||||
|
this.phrase = phrase;
|
||||||
|
list.addAll(filterCurrentResults(phrase));
|
||||||
|
System.out.println("> Search phrase " + phrase + " " + list.size());
|
||||||
|
singleThreadedExecutor.submit(new Runnable() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
SearchResultMatcher rm = new SearchResultMatcher(matcher, request);
|
||||||
|
if(rm.isCancelled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Thread.sleep(200); // FIXME
|
||||||
|
searchInBackground(phrase, rm);
|
||||||
|
if (!rm.isCancelled()) {
|
||||||
|
sortSearchResults(phrase, rm.getRequestResults());
|
||||||
|
System.out.println(">> Search phrase " + phrase + " " + rm.getRequestResults().size());
|
||||||
|
currentSearchResults = rm.getRequestResults();
|
||||||
|
if (onResultsComplete != null) {
|
||||||
|
onResultsComplete.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void searchInBackground(final SearchPhrase phrase, SearchResultMatcher matcher) {
|
||||||
|
for (SearchWord sw : phrase.getWords()) {
|
||||||
|
if (sw.getType() == ObjectType.REGION) {
|
||||||
|
phrase.selectFile((BinaryMapIndexReader) sw.getResult().object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
api.search(phrase, matcher);
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.error(e.getMessage(), e);
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private void sortSearchResults(SearchPhrase sp, List<SearchResult> searchResults) {
|
||||||
|
// sort SearchResult by 1. searchDistance 2. Name
|
||||||
|
final LatLon loc = sp.getLastTokenLocation();
|
||||||
|
final Collator clt = Collator.getInstance();
|
||||||
|
Collections.sort(searchResults, new Comparator<SearchResult>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(SearchResult o1, SearchResult o2) {
|
||||||
|
double s1 = o1.getSearchDistance(loc);
|
||||||
|
double s2 = o2.getSearchDistance(loc);
|
||||||
|
int cmp = Double.compare(s1, s2);
|
||||||
|
if(cmp != 0) {
|
||||||
|
return cmp;
|
||||||
|
}
|
||||||
|
return clt.compare(o1.mainName, o2.mainName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SearchResultMatcher implements ResultMatcher<SearchResult>{
|
||||||
|
private final List<SearchResult> requestResults = new ArrayList<>();
|
||||||
|
private ResultMatcher<SearchResult> matcher;
|
||||||
|
private final int request;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
|
||||||
|
public SearchResultMatcher(ResultMatcher<SearchResult> matcher, int request) {
|
||||||
|
this.matcher = matcher;
|
||||||
|
this.request = request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<SearchResult> getRequestResults() {
|
||||||
|
return requestResults;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean publish(SearchResult object) {
|
||||||
|
if(matcher == null || matcher.publish(object)) {
|
||||||
|
count++;
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,7 @@ package net.osmand.search.example.core;
|
||||||
public enum ObjectType {
|
public enum ObjectType {
|
||||||
CITY(true), VILLAGE(true), POSTCODE(true), STREET(true), HOUSE(true),
|
CITY(true), VILLAGE(true), POSTCODE(true), STREET(true), HOUSE(true),
|
||||||
POI_TYPE(false), POI(true), LOCATION(true), FAVORITE(true),
|
POI_TYPE(false), POI(true), LOCATION(true), FAVORITE(true),
|
||||||
RECENT_OBJ(true), WPT(true), UNKNOWN_NAME_FILTER(false);
|
REGION(true), RECENT_OBJ(true), WPT(true), UNKNOWN_NAME_FILTER(false);
|
||||||
private boolean hasLocation;
|
private boolean hasLocation;
|
||||||
private ObjectType(boolean location) {
|
private ObjectType(boolean location) {
|
||||||
this.hasLocation = location;
|
this.hasLocation = location;
|
||||||
|
|
|
@ -1,190 +0,0 @@
|
||||||
package net.osmand.search.example.core;
|
|
||||||
|
|
||||||
import gnu.trove.map.hash.TLongObjectHashMap;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import net.osmand.Location;
|
|
||||||
import net.osmand.ResultMatcher;
|
|
||||||
import net.osmand.binary.BinaryMapIndexReader.SearchRequest;
|
|
||||||
import net.osmand.data.LatLon;
|
|
||||||
import net.osmand.data.QuadRect;
|
|
||||||
import net.osmand.util.MapUtils;
|
|
||||||
|
|
||||||
|
|
||||||
public class SearchCore {
|
|
||||||
List<SearchCoreAPI> apis = new ArrayList<>();
|
|
||||||
|
|
||||||
public static abstract class SearchBaseAPI implements SearchCoreAPI {
|
|
||||||
@Override
|
|
||||||
public List<SearchResult> search(SearchPhrase phrase, int radiusLevel, SearchCallback callback,
|
|
||||||
List<SearchResult> existingSearchResults) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public QuadRect getBBoxToSearch(int radiusInMeters, int radiusLevel, LatLon loc) {
|
|
||||||
float calcRadios = radiusLevel * radiusInMeters;
|
|
||||||
float coeff = (float) (calcRadios / MapUtils.getTileDistanceWidth(SearchRequest.ZOOM_TO_SEARCH_POI));
|
|
||||||
double tx = MapUtils.getTileNumberX(SearchRequest.ZOOM_TO_SEARCH_POI, loc.getLongitude());
|
|
||||||
double ty = MapUtils.getTileNumberY(SearchRequest.ZOOM_TO_SEARCH_POI, loc.getLatitude());
|
|
||||||
double topLeftX = tx - coeff;
|
|
||||||
double topLeftY = ty - coeff;
|
|
||||||
double bottomRightX = tx + coeff;
|
|
||||||
double bottomRightY = ty + coeff;
|
|
||||||
return new QuadRect(topLeftX, bottomRightY, bottomRightX, topLeftY);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSearchPriority(SearchPhrase p) {
|
|
||||||
return 10;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class SearchAmenityByNameAPI extends SearchBaseAPI {
|
|
||||||
// BBOX QuadRect bbox = getBBoxToSearch(10000 * typedLettersInStreet, radiusLevel, phrase.getLastTokenLocation());
|
|
||||||
// if(poiObjectNotSelected) {
|
|
||||||
// LIMIT 100
|
|
||||||
// if(poiTypeSelected) {
|
|
||||||
// BBOX - result priority 5, distPriority 1
|
|
||||||
// } else {
|
|
||||||
// BBOX - result priority 5, distPriority 4
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
@Override
|
|
||||||
public int getSearchPriority(SearchPhrase p) {
|
|
||||||
return 10;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class SearchAmenityByTypeAPI extends SearchBaseAPI {
|
|
||||||
public boolean isLastWordPoi(SearchPhrase p ) {
|
|
||||||
return p.isLastWord(ObjectType.POI_TYPE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<SearchResult> search(SearchPhrase phrase, int radiusLevel, SearchCallback callback,
|
|
||||||
List<SearchResult> existingSearchResults) {
|
|
||||||
if(isLastWordPoi(phrase)) {
|
|
||||||
QuadRect bbox = getBBoxToSearch(10000, radiusLevel, phrase.getLastTokenLocation());
|
|
||||||
// TODO NO LIMIT , BBOX - result priority 5, distPriority 1
|
|
||||||
}
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSearchPriority(SearchPhrase p) {
|
|
||||||
if(isLastWordPoi(p)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 10;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class SearchAddressByNameAPI extends SearchBaseAPI {
|
|
||||||
|
|
||||||
public boolean isLastWordPoi(SearchPhrase p ) {
|
|
||||||
return p.isLastWord(ObjectType.POI);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isNoSelectedType(SearchPhrase p ) {
|
|
||||||
return p.isNoSelectedType();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<SearchResult> search(SearchPhrase phrase, int radiusLevel, SearchCallback callback,
|
|
||||||
List<SearchResult> existingSearchResults) {
|
|
||||||
// (search streets in neighboor cities for radiusLevel > 2)
|
|
||||||
if((isLastWordPoi(phrase) || isNoSelectedType(phrase) || radiusLevel >= 2
|
|
||||||
) && !(phrase.isEmpty())) {
|
|
||||||
int typedLettersInStreet = 1;
|
|
||||||
QuadRect bbox = getBBoxToSearch(20000 * typedLettersInStreet, radiusLevel, phrase.getLastTokenLocation());
|
|
||||||
int priority = isNoSelectedType(phrase) ? 1 : 3;
|
|
||||||
// priority
|
|
||||||
// LIMIT 100 * radiusLevel
|
|
||||||
// BBOX streets - result priority 5, distPriority ${priority}
|
|
||||||
// BBOX postcodes - result priority 5, distPriority ${priority}
|
|
||||||
// BBOX / 3 (3 times smaller) villages - result priority 5, distPriority ${priority}
|
|
||||||
// BBOX * 4 (3 times smaller) cities/towns - result priority 5, distPriority (${priority}/10)
|
|
||||||
}
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSearchPriority(SearchPhrase p) {
|
|
||||||
if(isNoSelectedType(p)) {
|
|
||||||
return 5;
|
|
||||||
}
|
|
||||||
if(isLastWordPoi(p)) {
|
|
||||||
return 8;
|
|
||||||
}
|
|
||||||
return 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class SearchStreetByCityAPI extends SearchBaseAPI {
|
|
||||||
@Override
|
|
||||||
public List<SearchResult> search(SearchPhrase phrase, int radiusLevel, SearchCallback callback,
|
|
||||||
List<SearchResult> existingSearchResults) {
|
|
||||||
if(isLastWordCityGroup(phrase)) {
|
|
||||||
// search all streets
|
|
||||||
// TODO LIMIT 100 * radiusLevel - priority 1, distPriority 0 (alphabetic)
|
|
||||||
}
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isLastWordCityGroup(SearchPhrase p ) {
|
|
||||||
return p.isLastWord(ObjectType.CITY) || p.isLastWord(ObjectType.POSTCODE) ||
|
|
||||||
p.isLastWord(ObjectType.VILLAGE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSearchPriority(SearchPhrase p) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class SearchBuildingAndIntersectionsByStreetAPI extends SearchBaseAPI {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<SearchResult> search(SearchPhrase phrase, int radiusLevel, SearchCallback callback,
|
|
||||||
List<SearchResult> existingSearchResults) {
|
|
||||||
if(isLastWordStreet(phrase)) {
|
|
||||||
// search all buildings
|
|
||||||
// TODO NO LIMIT - priority 1, distPriority 0 (alphabetic)
|
|
||||||
}
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isLastWordStreet(SearchPhrase p ) {
|
|
||||||
return p.isLastWord(ObjectType.STREET);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSearchPriority(SearchPhrase p) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void init() {
|
|
||||||
apis.add(new SearchAmenityByNameAPI());
|
|
||||||
apis.add(new SearchAmenityByTypeAPI());
|
|
||||||
apis.add(new SearchAddressByNameAPI());
|
|
||||||
apis.add(new SearchStreetByCityAPI());
|
|
||||||
apis.add(new SearchBuildingAndIntersectionsByStreetAPI());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void callToSearchCoreAPI(
|
|
||||||
SearchPhrase p,
|
|
||||||
ResultMatcher<List<SearchResult>> publisher,
|
|
||||||
ResultMatcher<SearchResult> visitor) {
|
|
||||||
// sort apis by prioirity to search phrase
|
|
||||||
// call apis in for loop
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,20 +1,13 @@
|
||||||
package net.osmand.search.example.core;
|
package net.osmand.search.example.core;
|
||||||
|
|
||||||
import java.util.List;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import net.osmand.search.example.SearchUICore.SearchResultMatcher;
|
||||||
|
|
||||||
public interface SearchCoreAPI {
|
public interface SearchCoreAPI {
|
||||||
|
|
||||||
public int getSearchPriority(SearchPhrase p);
|
public int getSearchPriority(SearchPhrase p);
|
||||||
|
|
||||||
public interface SearchCallback {
|
public boolean search(SearchPhrase phrase, SearchResultMatcher resultMatcher) throws IOException;
|
||||||
|
|
||||||
public boolean accept(SearchResult r);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<SearchResult> search(
|
|
||||||
SearchPhrase phrase,
|
|
||||||
int radiusLevel,
|
|
||||||
SearchCallback callback,
|
|
||||||
List<SearchResult> existingSearchResults);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,300 @@
|
||||||
|
package net.osmand.search.example.core;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import net.osmand.CollatorStringMatcher.StringMatcherMode;
|
||||||
|
import net.osmand.ResultMatcher;
|
||||||
|
import net.osmand.binary.BinaryMapIndexReader;
|
||||||
|
import net.osmand.binary.BinaryMapIndexReader.SearchRequest;
|
||||||
|
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.QuadRect;
|
||||||
|
import net.osmand.data.Street;
|
||||||
|
import net.osmand.search.example.SearchUICore.SearchResultMatcher;
|
||||||
|
import net.osmand.util.MapUtils;
|
||||||
|
|
||||||
|
|
||||||
|
public class SearchCoreFactory {
|
||||||
|
// TODO sort offline indexes by location
|
||||||
|
// TODO test add and delete characters
|
||||||
|
// TODO add location parse
|
||||||
|
// TODO add url parse (geo)
|
||||||
|
// TODO ? boolean hasSameConstantWords = p.hasSameConstantWords(this.phrase);
|
||||||
|
// TODO amenity types
|
||||||
|
// TODO amenity by type
|
||||||
|
// TODO streets by city
|
||||||
|
// TODO buildings by street
|
||||||
|
// TODO display closest city to villages
|
||||||
|
// TODO automatically increase radius if nothing found
|
||||||
|
|
||||||
|
public static abstract class SearchBaseAPI implements SearchCoreAPI {
|
||||||
|
@Override
|
||||||
|
public boolean search(SearchPhrase phrase, SearchResultMatcher resultMatcher) throws IOException {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public QuadRect getBBoxToSearch(int radiusInMeters, int radiusLevel, LatLon loc) {
|
||||||
|
if(loc == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
float calcRadios = radiusLevel * radiusInMeters;
|
||||||
|
float coeff = (float) (calcRadios / MapUtils.getTileDistanceWidth(SearchRequest.ZOOM_TO_SEARCH_POI));
|
||||||
|
double tx = MapUtils.getTileNumberX(SearchRequest.ZOOM_TO_SEARCH_POI, loc.getLongitude());
|
||||||
|
double ty = MapUtils.getTileNumberY(SearchRequest.ZOOM_TO_SEARCH_POI, loc.getLatitude());
|
||||||
|
double topLeftX = tx - coeff;
|
||||||
|
double topLeftY = ty - coeff;
|
||||||
|
double bottomRightX = tx + coeff;
|
||||||
|
double bottomRightY = ty + coeff;
|
||||||
|
return new QuadRect(topLeftX, bottomRightY, bottomRightX, topLeftY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSearchPriority(SearchPhrase p) {
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SearchRegionByNameAPI extends SearchBaseAPI {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean search(SearchPhrase phrase, SearchResultMatcher resultMatcher) {
|
||||||
|
if(phrase.hasObjectType(ObjectType.REGION)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (BinaryMapIndexReader bmir : phrase.getOfflineIndexes()) {
|
||||||
|
if (bmir.getRegionCenter() != null) {
|
||||||
|
SearchResult sr = new SearchResult(phrase);
|
||||||
|
sr.mainName = bmir.getRegionName();
|
||||||
|
sr.object = bmir;
|
||||||
|
sr.objectType = ObjectType.REGION;
|
||||||
|
sr.location = bmir.getRegionCenter();
|
||||||
|
sr.preferredZoom = 6;
|
||||||
|
if (phrase.getLastWord().length() <= 2 && phrase.isNoSelectedType()) {
|
||||||
|
resultMatcher.publish(sr);
|
||||||
|
} else if (phrase.getNameStringMatcher().matches(sr.mainName)) {
|
||||||
|
resultMatcher.publish(sr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSearchPriority(SearchPhrase p) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SearchAddressByNameAPI extends SearchBaseAPI {
|
||||||
|
|
||||||
|
private static final int DEFAULT_BBOX_RADIUS = 1000*10000;
|
||||||
|
private static final int LIMIT = 100;
|
||||||
|
|
||||||
|
public boolean isLastWordPoi(SearchPhrase p) {
|
||||||
|
return p.isLastWord(ObjectType.POI);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLastWordRegion(SearchPhrase p) {
|
||||||
|
return p.isLastWord(ObjectType.REGION);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isNoSelectedType(SearchPhrase p) {
|
||||||
|
return p.isNoSelectedType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean search(final SearchPhrase phrase, final SearchResultMatcher resultMatcher) throws IOException {
|
||||||
|
if (!phrase.hasObjectType(ObjectType.REGION)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (phrase.getLastWord().isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// (search streets in neighboor cities for radiusLevel > 2)
|
||||||
|
if (isLastWordPoi(phrase) || isNoSelectedType(phrase) ||
|
||||||
|
isLastWordRegion(phrase) || phrase.getRadiusLevel() >= 2) {
|
||||||
|
int letters = phrase.getLastWord().length() / 3 + 1;
|
||||||
|
final boolean locSpecified = false; // phrase.getLastTokenLocation() != null;
|
||||||
|
LatLon loc = phrase.getLastTokenLocation();
|
||||||
|
final QuadRect streetBbox = getBBoxToSearch(DEFAULT_BBOX_RADIUS * letters, phrase.getRadiusLevel(),
|
||||||
|
phrase.getLastTokenLocation());
|
||||||
|
final QuadRect postcodeBbox = getBBoxToSearch(DEFAULT_BBOX_RADIUS * 2 * letters, phrase.getRadiusLevel(),
|
||||||
|
phrase.getLastTokenLocation());
|
||||||
|
final QuadRect villagesBbox = getBBoxToSearch(DEFAULT_BBOX_RADIUS * letters, phrase.getRadiusLevel(),
|
||||||
|
phrase.getLastTokenLocation());
|
||||||
|
final QuadRect cityBbox = getBBoxToSearch(DEFAULT_BBOX_RADIUS * 4 * letters, phrase.getRadiusLevel(),
|
||||||
|
phrase.getLastTokenLocation());
|
||||||
|
final int priority = isNoSelectedType(phrase) ? 1 : 3;
|
||||||
|
ResultMatcher<MapObject> rm = new ResultMatcher<MapObject>() {
|
||||||
|
int limit = 0;
|
||||||
|
@Override
|
||||||
|
public boolean publish(MapObject object) {
|
||||||
|
if(isCancelled()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SearchResult sr = new SearchResult(phrase);
|
||||||
|
sr.object = object;
|
||||||
|
sr.mainName = object.getName();
|
||||||
|
sr.location = object.getLocation();
|
||||||
|
sr.priorityDistance = 1;
|
||||||
|
sr.priority = priority;
|
||||||
|
int y = MapUtils.get31TileNumberY(object.getLocation().getLatitude());
|
||||||
|
int x = MapUtils.get31TileNumberX(object.getLocation().getLongitude());
|
||||||
|
if (object instanceof Street) {
|
||||||
|
if(locSpecified && !streetBbox.contains(x, y, x, y)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
sr.objectType = ObjectType.STREET;
|
||||||
|
} else if (object instanceof City) {
|
||||||
|
CityType type = ((City)object).getType();
|
||||||
|
if (type == CityType.CITY || type == CityType.TOWN) {
|
||||||
|
if (locSpecified && !cityBbox.contains(x, y, x, y)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
sr.objectType = ObjectType.CITY;
|
||||||
|
sr.priorityDistance = 0.1;
|
||||||
|
} else if (((City)object).isPostcode()) {
|
||||||
|
if (locSpecified && !postcodeBbox.contains(x, y, x, y)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
sr.objectType = ObjectType.POSTCODE;
|
||||||
|
} else {
|
||||||
|
if (locSpecified && !villagesBbox.contains(x, y, x, y)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
sr.objectType = ObjectType.VILLAGE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
limit ++;
|
||||||
|
resultMatcher.publish(sr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCancelled() {
|
||||||
|
return limit > LIMIT * phrase.getRadiusLevel() ||
|
||||||
|
resultMatcher.isCancelled();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for(BinaryMapIndexReader r : phrase.getOfflineIndexes()) {
|
||||||
|
SearchRequest<MapObject> req = BinaryMapIndexReader.buildAddressByNameRequest(rm,
|
||||||
|
phrase.getLastWord().toLowerCase(), StringMatcherMode.CHECK_ONLY_STARTS_WITH);
|
||||||
|
if(locSpecified) {
|
||||||
|
req.setBBoxRadius(loc.getLatitude(), loc.getLongitude(), DEFAULT_BBOX_RADIUS * 4 * letters);
|
||||||
|
}
|
||||||
|
r.searchAddressDataByName(req);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSearchPriority(SearchPhrase p) {
|
||||||
|
if (isNoSelectedType(p)) {
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
if (isLastWordPoi(p)) {
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SearchAmenityByNameAPI extends SearchBaseAPI {
|
||||||
|
// TODO
|
||||||
|
// BBOX QuadRect bbox = getBBoxToSearch(10000 * typedLettersInStreet, radiusLevel, phrase.getLastTokenLocation());
|
||||||
|
// if(poiObjectNotSelected) {
|
||||||
|
// LIMIT 100
|
||||||
|
// if(poiTypeSelected) {
|
||||||
|
// BBOX - result priority 5, distPriority 1
|
||||||
|
// } else {
|
||||||
|
// BBOX - result priority 5, distPriority 4
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
@Override
|
||||||
|
public int getSearchPriority(SearchPhrase p) {
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SearchAmenityByTypeAPI extends SearchBaseAPI {
|
||||||
|
public boolean isLastWordPoi(SearchPhrase p ) {
|
||||||
|
return p.isLastWord(ObjectType.POI_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean search(SearchPhrase phrase, SearchResultMatcher resultMatcher) {
|
||||||
|
if(isLastWordPoi(phrase)) {
|
||||||
|
QuadRect bbox = getBBoxToSearch(10000, phrase.getRadiusLevel(), phrase.getLastTokenLocation());
|
||||||
|
// TODO NO LIMIT , BBOX - result priority 5, distPriority 1
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSearchPriority(SearchPhrase p) {
|
||||||
|
if(isLastWordPoi(p)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static class SearchStreetByCityAPI extends SearchBaseAPI {
|
||||||
|
@Override
|
||||||
|
public boolean search(SearchPhrase phrase, SearchResultMatcher resultMatcher) {
|
||||||
|
if(isLastWordCityGroup(phrase)) {
|
||||||
|
// search all streets
|
||||||
|
// TODO LIMIT 100 * radiusLevel - priority 1, distPriority 0 (alphabetic)
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLastWordCityGroup(SearchPhrase p ) {
|
||||||
|
return p.isLastWord(ObjectType.CITY) || p.isLastWord(ObjectType.POSTCODE) ||
|
||||||
|
p.isLastWord(ObjectType.VILLAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSearchPriority(SearchPhrase p) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static class SearchBuildingAndIntersectionsByStreetAPI extends SearchBaseAPI {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean search(SearchPhrase phrase, SearchResultMatcher resultMatcher) {
|
||||||
|
if(isLastWordStreet(phrase)) {
|
||||||
|
// search all buildings
|
||||||
|
// TODO NO LIMIT - priority 1, distPriority 0 (alphabetic)
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLastWordStreet(SearchPhrase p ) {
|
||||||
|
return p.isLastWord(ObjectType.STREET);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSearchPriority(SearchPhrase p) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -6,50 +6,56 @@ import java.util.List;
|
||||||
import net.osmand.CollatorStringMatcher;
|
import net.osmand.CollatorStringMatcher;
|
||||||
import net.osmand.StringMatcher;
|
import net.osmand.StringMatcher;
|
||||||
import net.osmand.CollatorStringMatcher.StringMatcherMode;
|
import net.osmand.CollatorStringMatcher.StringMatcherMode;
|
||||||
|
import net.osmand.binary.BinaryMapIndexReader;
|
||||||
import net.osmand.data.LatLon;
|
import net.osmand.data.LatLon;
|
||||||
|
|
||||||
|
//immutable object
|
||||||
public class SearchPhrase {
|
public class SearchPhrase {
|
||||||
|
|
||||||
private List<SearchWord> words = new ArrayList<>();
|
private List<SearchWord> words = new ArrayList<>();
|
||||||
private LatLon originalLocation;
|
|
||||||
private String text = "";
|
private String text = "";
|
||||||
private String lastWord = "";
|
private String lastWord = "";
|
||||||
private CollatorStringMatcher sm;
|
private CollatorStringMatcher sm;
|
||||||
|
private SearchSettings settings;
|
||||||
|
private List<BinaryMapIndexReader> indexes;
|
||||||
|
|
||||||
public SearchPhrase(LatLon location) {
|
public SearchPhrase(SearchSettings settings) {
|
||||||
this.originalLocation = location;
|
this.settings = settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<SearchWord> getWords() {
|
public List<SearchWord> getWords() {
|
||||||
return words;
|
return words;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SearchPhrase generateNewPhrase(String text) {
|
public List<BinaryMapIndexReader> getOfflineIndexes() {
|
||||||
SearchPhrase sp = new SearchPhrase(originalLocation);
|
if(indexes != null) {
|
||||||
String atext = text;
|
return indexes;
|
||||||
if (text.startsWith((this.text + this.lastWord).trim())) {
|
|
||||||
// string is longer
|
|
||||||
atext = text.substring(this.text.length());
|
|
||||||
sp.text = this.text;
|
|
||||||
sp.words = new ArrayList<>(this.words);
|
|
||||||
} else {
|
|
||||||
sp.text = "";
|
|
||||||
}
|
}
|
||||||
// TODO Reuse previous search words if it is shorter
|
return settings.getOfflineIndexes();
|
||||||
if (!atext.contains(",")) {
|
|
||||||
sp.lastWord = atext;
|
|
||||||
} else {
|
|
||||||
String[] ws = atext.split(",");
|
|
||||||
for (int i = 0; i < ws.length - 1; i++) {
|
|
||||||
if (ws[i].trim().length() > 0) {
|
|
||||||
sp.words.add(new SearchWord(ws[i].trim()));
|
|
||||||
}
|
}
|
||||||
sp.text += ws[i] + ",";
|
|
||||||
|
public SearchSettings getSettings() {
|
||||||
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int getRadiusLevel() {
|
||||||
|
return settings.getRadiusLevel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SearchPhrase selectWord(SearchResult res) {
|
||||||
|
SearchPhrase sp = new SearchPhrase(this.settings);
|
||||||
|
sp.words.addAll(this.words);
|
||||||
|
SearchWord sw = new SearchWord(res.mainName.trim(), res);
|
||||||
|
sp.words.add(sw);
|
||||||
|
// sp.text = this.text + sw.getWord() + ", ";
|
||||||
|
// TODO FIX
|
||||||
|
sp.text = this.text + " " + sw.getWord() + ", ";
|
||||||
return sp;
|
return sp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public List<SearchWord> excludefilterWords() {
|
public List<SearchWord> excludefilterWords() {
|
||||||
List<SearchWord> w = new ArrayList<>();
|
List<SearchWord> w = new ArrayList<>();
|
||||||
for(SearchWord s : words) {
|
for(SearchWord s : words) {
|
||||||
|
@ -63,7 +69,7 @@ public class SearchPhrase {
|
||||||
public boolean isLastWord(ObjectType p) {
|
public boolean isLastWord(ObjectType p) {
|
||||||
for (int i = words.size() - 1; i >= 0; i--) {
|
for (int i = words.size() - 1; i >= 0; i--) {
|
||||||
SearchWord sw = words.get(i);
|
SearchWord sw = words.get(i);
|
||||||
if(sw.getType() == ObjectType.POI) {
|
if (sw.getType() == p) {
|
||||||
return true;
|
return true;
|
||||||
} else if (sw.getType() != ObjectType.UNKNOWN_NAME_FILTER) {
|
} else if (sw.getType() != ObjectType.UNKNOWN_NAME_FILTER) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -84,10 +90,23 @@ public class SearchPhrase {
|
||||||
return excludefilterWords().equals(p.excludefilterWords());
|
return excludefilterWords().equals(p.excludefilterWords());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasObjectType(ObjectType p) {
|
||||||
|
for(SearchWord s : words) {
|
||||||
|
if(s.getType() == p) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getText() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
public String getStringRerpresentation() {
|
public String getStringRerpresentation() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
for(SearchWord s : words) {
|
for(SearchWord s : words) {
|
||||||
sb.append(s.getWord()).append(", ");
|
sb.append(s.getWord()).append(" [" + s.getType() + "], ");
|
||||||
}
|
}
|
||||||
sb.append(lastWord);
|
sb.append(lastWord);
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
|
@ -119,9 +138,55 @@ public class SearchPhrase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// last token or myLocationOrVisibleMap if not selected
|
// last token or myLocationOrVisibleMap if not selected
|
||||||
return originalLocation;
|
return settings.getOriginalLocation();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public SearchPhrase generateNewPhrase(String text, SearchSettings settings) {
|
||||||
|
SearchPhrase sp = new SearchPhrase(settings);
|
||||||
|
String atext = text;
|
||||||
|
List<SearchWord> leftWords = this.words;
|
||||||
|
if (text.startsWith((this.text + this.lastWord).trim())) {
|
||||||
|
// string is longer
|
||||||
|
atext = text.substring(this.text.length());
|
||||||
|
sp.text = this.text;
|
||||||
|
sp.words = new ArrayList<>(this.words);
|
||||||
|
leftWords = leftWords.subList(leftWords.size(), leftWords.size());
|
||||||
|
} else {
|
||||||
|
sp.text = "";
|
||||||
|
}
|
||||||
|
if (!atext.contains(",")) {
|
||||||
|
sp.lastWord = atext;
|
||||||
|
} else {
|
||||||
|
String[] ws = atext.split(",");
|
||||||
|
for (int i = 0; i < ws.length - 1; i++) {
|
||||||
|
boolean unknown = true;
|
||||||
|
if (ws[i].trim().length() > 0) {
|
||||||
|
if (leftWords.size() > 0) {
|
||||||
|
if (leftWords.get(0).getWord().equalsIgnoreCase(ws[i].trim())) {
|
||||||
|
sp.words.add(leftWords.get(0));
|
||||||
|
leftWords = leftWords.subList(1, leftWords.size());
|
||||||
|
unknown = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(unknown) {
|
||||||
|
sp.words.add(new SearchWord(ws[i].trim()));
|
||||||
|
}
|
||||||
|
sp.text += ws[i] + ", ";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sp.text = sp.text.trim();
|
||||||
|
return sp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void selectFile(BinaryMapIndexReader object) {
|
||||||
|
if(indexes == null) {
|
||||||
|
indexes = new ArrayList<>();
|
||||||
|
}
|
||||||
|
this.indexes.add(object);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,31 @@
|
||||||
package net.osmand.search.example.core;
|
package net.osmand.search.example.core;
|
||||||
|
|
||||||
import net.osmand.data.LatLon;
|
import net.osmand.data.LatLon;
|
||||||
|
import net.osmand.util.MapUtils;
|
||||||
|
|
||||||
public class SearchResult {
|
public class SearchResult {
|
||||||
|
// search phrase that makes search result valid
|
||||||
|
public SearchPhrase requiredSearchPhrase;
|
||||||
|
|
||||||
public Object object;
|
public Object object;
|
||||||
public ObjectType objectType;
|
public ObjectType objectType;
|
||||||
|
|
||||||
// calculated by formula priority - 1 / (1 + priorityDistance * distance)
|
public double priority;
|
||||||
public double searchDistance;
|
public double priorityDistance;
|
||||||
|
|
||||||
|
public SearchResult(SearchPhrase sp) {
|
||||||
|
this.requiredSearchPhrase = sp;
|
||||||
|
}
|
||||||
|
public double getSearchDistance(LatLon location) {
|
||||||
|
double distance = 0;
|
||||||
|
if(location != null && this.location != null) {
|
||||||
|
distance = MapUtils.getDistance(location, this.location);
|
||||||
|
}
|
||||||
|
return priority - 1 / ( 1 + priorityDistance * distance);
|
||||||
|
}
|
||||||
|
|
||||||
public LatLon location;
|
public LatLon location;
|
||||||
public int preferredZoom = 15;
|
public int preferredZoom = 15;
|
||||||
public String mainName;
|
public String mainName;
|
||||||
// search phrase that makes search result valid
|
|
||||||
public SearchPhrase requiredSearchPhrase;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
package net.osmand.search.example.core;
|
||||||
|
|
||||||
|
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<BinaryMapIndexReader> offlineIndexes = new ArrayList<>();
|
||||||
|
private int radiusLevel = 1;
|
||||||
|
private int totalLimit = -1;
|
||||||
|
|
||||||
|
public SearchSettings(SearchSettings s) {
|
||||||
|
if(s != null) {
|
||||||
|
this.radiusLevel = s.radiusLevel;
|
||||||
|
this.totalLimit = s.totalLimit;
|
||||||
|
this.offlineIndexes = s.offlineIndexes;
|
||||||
|
this.originalLocation = s.originalLocation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public SearchSettings(List<BinaryMapIndexReader> offlineIndexes) {
|
||||||
|
this.offlineIndexes = Collections.unmodifiableList(offlineIndexes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public List<BinaryMapIndexReader> getOfflineIndexes() {
|
||||||
|
return offlineIndexes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRadiusLevel() {
|
||||||
|
return radiusLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTotalLimit() {
|
||||||
|
return totalLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LatLon getOriginalLocation() {
|
||||||
|
return originalLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SearchSettings setOriginalLocation(LatLon l) {
|
||||||
|
SearchSettings s = new SearchSettings(this);
|
||||||
|
s.originalLocation = l;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -5,10 +5,14 @@ import net.osmand.data.LatLon;
|
||||||
public class SearchWord {
|
public class SearchWord {
|
||||||
private String word;
|
private String word;
|
||||||
private SearchResult result;
|
private SearchResult result;
|
||||||
private LatLon location;
|
|
||||||
|
|
||||||
public SearchWord(String word) {
|
public SearchWord(String word) {
|
||||||
this.word = word;
|
this.word = word.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SearchWord(String word, SearchResult res) {
|
||||||
|
this.word = word.trim();
|
||||||
|
this.result = res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObjectType getType() {
|
public ObjectType getType() {
|
||||||
|
@ -24,7 +28,7 @@ public class SearchWord {
|
||||||
}
|
}
|
||||||
|
|
||||||
public LatLon getLocation() {
|
public LatLon getLocation() {
|
||||||
return location;
|
return result == null ? null : result.location;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
Loading…
Reference in a new issue