package net.osmand.search.core;
import com.google.openlocationcode.OpenLocationCode;
import com.jwetherell.openmap.common.LatLonPoint;
import com.jwetherell.openmap.common.UTMPoint;
import net.osmand.CollatorStringMatcher.StringMatcherMode;
import net.osmand.ResultMatcher;
import net.osmand.binary.BinaryMapAddressReaderAdapter;
import net.osmand.binary.BinaryMapIndexReader;
import net.osmand.binary.BinaryMapIndexReader.SearchPoiTypeFilter;
import net.osmand.binary.BinaryMapIndexReader.SearchRequest;
import net.osmand.binary.CommonWords;
import net.osmand.data.Amenity;
import net.osmand.data.Building;
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.QuadTree;
import net.osmand.data.Street;
import net.osmand.osm.AbstractPoiType;
import net.osmand.osm.MapPoiTypes;
import net.osmand.osm.PoiCategory;
import net.osmand.osm.PoiFilter;
import net.osmand.osm.PoiType;
import net.osmand.search.SearchUICore.SearchResultMatcher;
import net.osmand.search.core.SearchPhrase.NameStringMatcher;
import net.osmand.search.core.SearchPhrase.SearchPhraseDataType;
import net.osmand.util.Algorithms;
import net.osmand.util.GeoPointParserUtil;
import net.osmand.util.GeoPointParserUtil.GeoParsedPoint;
import net.osmand.util.MapUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import gnu.trove.list.array.TIntArrayList;
public class SearchCoreFactory {
public static final int MAX_DEFAULT_SEARCH_RADIUS = 7;
//////////////// CONSTANTS //////////
public static final int SEARCH_REGION_API_PRIORITY = 300;
public static final int SEARCH_REGION_OBJECT_PRIORITY = 1000;
// context less
public static final int SEARCH_LOCATION_PRIORITY = 0;
public static final int SEARCH_AMENITY_TYPE_PRIORITY = 100;
public static final int SEARCH_AMENITY_TYPE_API_PRIORITY = 100;
// context
public static final int SEARCH_STREET_BY_CITY_PRIORITY = 200;
public static final int SEARCH_BUILDING_BY_CITY_PRIORITY = 300;
public static final int SEARCH_BUILDING_BY_STREET_PRIORITY = 100;
public static final int SEARCH_AMENITY_BY_TYPE_PRIORITY = 300;
// context less (slow)
public static final int SEARCH_ADDRESS_BY_NAME_API_PRIORITY = 500;
public static final int SEARCH_ADDRESS_BY_NAME_API_PRIORITY_RADIUS2 = 500;
public static final int SEARCH_ADDRESS_BY_NAME_PRIORITY = 500;
public static final int SEARCH_ADDRESS_BY_NAME_PRIORITY_RADIUS2 = 500;
// context less (slower)
public static final int SEARCH_AMENITY_BY_NAME_PRIORITY = 700;
public static final int SEARCH_AMENITY_BY_NAME_API_PRIORITY_IF_POI_TYPE = 700;
public static final int SEARCH_AMENITY_BY_NAME_API_PRIORITY_IF_3_CHAR = 700;
protected static final double SEARCH_AMENITY_BY_NAME_CITY_PRIORITY_DISTANCE = 0.001;
protected static final double SEARCH_AMENITY_BY_NAME_TOWN_PRIORITY_DISTANCE = 0.005;
public static abstract class SearchBaseAPI implements SearchCoreAPI {
private ObjectType[] searchTypes;
protected SearchBaseAPI(ObjectType... searchTypes) {
if (searchTypes == null) {
throw new IllegalArgumentException("Search types are not defined for search core API");
}
this.searchTypes = searchTypes;
}
@Override
public boolean isSearchAvailable(SearchPhrase p) {
ObjectType[] typesToSearch = p.getSearchTypes();
ObjectType exclusiveSearchType = p.getExclusiveSearchType();
if (exclusiveSearchType != null) {
return searchTypes != null && searchTypes.length == 1 && searchTypes[0] == exclusiveSearchType;
} else 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;
}
@Override
public int getSearchPriority(SearchPhrase p) {
return 1;
}
@Override
public boolean isSearchMoreAvailable(SearchPhrase phrase) {
return phrase.getRadiusLevel() < MAX_DEFAULT_SEARCH_RADIUS;
}
protected void subSearchApiOrPublish(SearchPhrase phrase,
SearchResultMatcher resultMatcher, SearchResult res, SearchBaseAPI api)
throws IOException {
phrase.countUnknownWordsMatch(res);
int cnt = resultMatcher.getCount();
List<String> ws = phrase.getUnknownSearchWords(res.otherWordsMatch);
if(!res.firstUnknownWordMatches) {
ws.add(phrase.getUnknownSearchWord());
}
// publish result to set parentSearchResult before search
resultMatcher.publish(res);
if (!ws.isEmpty() && api != null && api.isSearchAvailable(phrase)) {
SearchPhrase nphrase = phrase.selectWord(res, ws,
phrase.isLastUnknownSearchWordComplete());
SearchResult prev = resultMatcher.setParentSearchResult(res);
res.parentSearchResult = prev;
api.search(nphrase, resultMatcher);
resultMatcher.setParentSearchResult(prev);
}
// 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) throws IOException {
for (BinaryMapIndexReader bmir : phrase.getOfflineIndexes()) {
if (bmir.getRegionCenter() != null) {
SearchResult sr = new SearchResult(phrase);
sr.localeName = bmir.getRegionName();
sr.object = bmir;
sr.file = bmir;
sr.priority = SEARCH_REGION_OBJECT_PRIORITY;
sr.objectType = ObjectType.REGION;
sr.location = bmir.getRegionCenter();
sr.preferredZoom = 6;
if (phrase.getUnknownSearchWordLength() <= 1 && phrase.isNoSelectedType()) {
resultMatcher.publish(sr);
} else if (phrase.getNameStringMatcher().matches(sr.localeName)) {
resultMatcher.publish(sr);
}
}
}
return true;
}
@Override
public boolean isSearchMoreAvailable(SearchPhrase phrase) {
return false;
}
@Override
public int getSearchPriority(SearchPhrase p) {
if(!p.isNoSelectedType()) {
return -1;
}
return SEARCH_REGION_API_PRIORITY;
}
}
private static String stripBraces(String localeName) {
int i = localeName.indexOf('(');
String retName = localeName;
if (i > -1) {
retName = localeName.substring(0, i);
int j = localeName.indexOf(')', i);
if (j > -1) {
retName = retName.trim() + ' ' + localeName.substring(j);
}
}
return retName;
}
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);
private List<City> resArray = new ArrayList<>();
private SearchStreetByCityAPI cityApi;
private SearchBuildingAndIntersectionsByStreetAPI streetsApi;
public SearchAddressByNameAPI(SearchBuildingAndIntersectionsByStreetAPI streetsApi,
SearchStreetByCityAPI cityApi) {
super(ObjectType.CITY, ObjectType.VILLAGE, ObjectType.POSTCODE,
ObjectType.STREET, ObjectType.HOUSE, ObjectType.STREET_INTERSECTION);
this.streetsApi = streetsApi;
this.cityApi = cityApi;
}
@Override
public int getSearchPriority(SearchPhrase p) {
if (!p.isNoSelectedType() && p.getRadiusLevel() == 1) {
return -1;
}
if(p.isLastWord(ObjectType.POI) || p.isLastWord(ObjectType.POI_TYPE)) {
return -1;
}
if (p.isNoSelectedType()) {
return SEARCH_ADDRESS_BY_NAME_API_PRIORITY;
}
return SEARCH_ADDRESS_BY_NAME_API_PRIORITY_RADIUS2;
}
@Override
public boolean isSearchMoreAvailable(SearchPhrase phrase) {
// case when street is not found for given city is covered by SearchStreetByCityAPI
return getSearchPriority(phrase) != -1 && super.isSearchMoreAvailable(phrase);
}
@Override
public boolean search(final SearchPhrase phrase, final SearchResultMatcher resultMatcher) throws IOException {
if (!phrase.isUnknownSearchWordPresent() && !phrase.isEmptyQueryAllowed()) {
return false;
}
// phrase.isLastWord(ObjectType.CITY, ObjectType.VILLAGE, ObjectType.POSTCODE) || phrase.isLastWord(ObjectType.REGION)
if (phrase.isNoSelectedType() || phrase.getRadiusLevel() >= 2) {
initAndSearchCities(phrase, resultMatcher);
// not publish results (let it sort)
// resultMatcher.apiSearchFinished(this, phrase);
searchByName(phrase, resultMatcher);
}
return true;
}
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();
if (!townCities.containsKey(r)) {
BinaryMapIndexReader.buildAddressRequest(null);
List<City> l = r.getCities(null, BinaryMapAddressReaderAdapter.CITY_TOWN_TYPE);
townCities.put(r, l);
for (City c : l) {
LatLon cl = c.getLocation();
c.setReferenceFile(r);
int y = MapUtils.get31TileNumberY(cl.getLatitude());
int x = MapUtils.get31TileNumberX(cl.getLongitude());
QuadRect qr = new QuadRect(x, y, x, y);
townCitiesQR.insert(c, qr);
}
}
}
if (phrase.isNoSelectedType() && bbox != null
&& (phrase.isUnknownSearchWordPresent() || phrase.isEmptyQueryAllowed())
&& phrase.isSearchTypeAllowed(ObjectType.CITY)) {
String wrd = phrase.getUnknownWordToSearch();
NameStringMatcher nm = phrase.getNameStringMatcher(wrd, phrase.isUnknownSearchWordComplete());
// NameStringMatcher nm = phrase.getNameStringMatcher();
resArray.clear();
resArray = townCitiesQR.queryInBox(bbox, resArray);
int limit = 0;
for (City c : resArray) {
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 (phrase.isEmptyQueryAllowed() && phrase.isEmpty()) {
resultMatcher.publish(res);
} else if (nm.matches(res.localeName) || nm.matches(res.otherNames)) {
res.firstUnknownWordMatches = wrd.equals(phrase.getUnknownSearchWord());
subSearchApiOrPublish(phrase, resultMatcher, res, cityApi);
}
if (limit++ > LIMIT * phrase.getRadiusLevel()) {
break;
}
}
}
}
private void searchByName(final SearchPhrase phrase, final SearchResultMatcher resultMatcher)
throws IOException {
if (phrase.getRadiusLevel() > 1 || phrase.getUnknownSearchWordLength() > 3 || phrase.getUnknownSearchWords().size() > 0) {
final boolean locSpecified = phrase.getLastTokenLocation() != null;
LatLon loc = phrase.getLastTokenLocation();
final List<SearchResult> immediateResults = new ArrayList<>();
final QuadRect streetBbox = phrase.getRadiusBBoxToSearch(DEFAULT_ADDRESS_BBOX_RADIUS);
final QuadRect postcodeBbox = phrase.getRadiusBBoxToSearch(DEFAULT_ADDRESS_BBOX_RADIUS * 5);
final QuadRect villagesBbox = phrase.getRadiusBBoxToSearch(DEFAULT_ADDRESS_BBOX_RADIUS * 3);
final QuadRect cityBbox = phrase.getRadiusBBoxToSearch(DEFAULT_ADDRESS_BBOX_RADIUS * 5); // covered by separate search before
final int priority = phrase.isNoSelectedType() ?
SEARCH_ADDRESS_BY_NAME_PRIORITY : SEARCH_ADDRESS_BY_NAME_PRIORITY_RADIUS2;
final BinaryMapIndexReader[] currentFile = new BinaryMapIndexReader[1];
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.file = currentFile[0];
sr.localeName = object.getName(phrase.getSettings().getLang(), phrase.getSettings().isTransliterate());
sr.otherNames = object.getAllNames(true);
sr.localeRelatedObjectName = sr.file.getRegionName();
sr.relatedObject = sr.file;
sr.location = object.getLocation();
sr.priorityDistance = 1;
sr.priority = priority;
int y = MapUtils.get31TileNumberY(object.getLocation().getLatitude());
int x = MapUtils.get31TileNumberX(object.getLocation().getLongitude());
List<City> closestCities = null;
if (object instanceof Street) {
if ((locSpecified && !streetBbox.contains(x, y, x, y))
|| !phrase.isSearchTypeAllowed(ObjectType.STREET)) {
return false;
}
if (object.getName().startsWith("<")) {
return false;
}
if (!phrase.getNameStringMatcher().matches(stripBraces(sr.localeName))) {
sr.priorityDistance = 5;
}
sr.objectType = ObjectType.STREET;
sr.localeRelatedObjectName = ((Street)object).getCity().getName(phrase.getSettings().getLang(), phrase.getSettings().isTransliterate());
sr.relatedObject = ((Street)object).getCity();
} else if (object instanceof City) {
CityType type = ((City)object).getType();
if (type == CityType.CITY || type == CityType.TOWN) {
if (phrase.isNoSelectedType()) {
// ignore city/town
return false;
}
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))
|| !phrase.isSearchTypeAllowed(ObjectType.POSTCODE)) {
return false;
}
sr.objectType = ObjectType.POSTCODE;
} else {
if ((locSpecified && !villagesBbox.contains(x, y, x, y))
|| !phrase.isSearchTypeAllowed(ObjectType.VILLAGE)) {
return false;
}
City c = null;
if (closestCities == null) {
closestCities = townCitiesQR.queryInBox(villagesBbox, new ArrayList<City>());
}
double minDist = -1;
double pDist = -1;
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) {
c = s;
minDist = ll;
pDist = pd ;
}
}
if (c != null) {
sr.localeRelatedObjectName = c.getName(phrase.getSettings().getLang(), phrase.getSettings().isTransliterate());
sr.relatedObject = c;
sr.distRelatedObjectName = minDist;
}
sr.objectType = ObjectType.VILLAGE;
}
} else {
return false;
}
limit ++;
immediateResults.add(sr);
return false;
}
@Override
public boolean isCancelled() {
return limit > LIMIT * phrase.getRadiusLevel() ||
resultMatcher.isCancelled();
}
};
Iterator<BinaryMapIndexReader> offlineIterator = phrase.getRadiusOfflineIndexes(DEFAULT_ADDRESS_BBOX_RADIUS * 5,
SearchPhraseDataType.ADDRESS);
String wordToSearch = phrase.getUnknownWordToSearch();
while (offlineIterator.hasNext() && wordToSearch.length() > 0) {
BinaryMapIndexReader r = offlineIterator.next();
currentFile[0] = r;
immediateResults.clear();
SearchRequest<MapObject> req = BinaryMapIndexReader.buildAddressByNameRequest(rm, wordToSearch.toLowerCase(),
phrase.isUnknownSearchWordComplete() ? StringMatcherMode.CHECK_EQUALS_FROM_SPACE
: StringMatcherMode.CHECK_STARTS_FROM_SPACE);
if (locSpecified) {
req.setBBoxRadius(loc.getLatitude(), loc.getLongitude(),
phrase.getRadiusSearch(DEFAULT_ADDRESS_BBOX_RADIUS * 5));
}
r.searchAddressDataByName(req);
for (SearchResult res : immediateResults) {
res.firstUnknownWordMatches = wordToSearch.equals(phrase.getUnknownSearchWord());
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));
subSearchApiOrPublish(phrase, resultMatcher, res, streetsApi);
} else {
subSearchApiOrPublish(phrase, resultMatcher, res, cityApi);
}
}
resultMatcher.apiSearchRegionFinished(this, r, phrase);
}
}
}
}
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()) {
return false;
}
final BinaryMapIndexReader[] currentFile = new BinaryMapIndexReader[1];
Iterator<BinaryMapIndexReader> offlineIterator = phrase.getRadiusOfflineIndexes(BBOX_RADIUS,
SearchPhraseDataType.POI);
final NameStringMatcher nm = phrase.getNameStringMatcher();
QuadRect bbox = phrase.getRadiusBBoxToSearch(BBOX_RADIUS_INSIDE);
SearchRequest<Amenity> req = BinaryMapIndexReader.buildSearchPoiRequest(
(int)bbox.centerX(), (int)bbox.centerY(),
phrase.getUnknownSearchWord(),
(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) {
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(!nm.matches(sr.localeName) && !nm.matches(sr.otherNames)) {
return false;
}
}
sr.object = object;
sr.preferredZoom = 17;
sr.file = currentFile[0];
sr.location = object.getLocation();
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")) {
sr.priorityDistance = SEARCH_AMENITY_BY_NAME_TOWN_PRIORITY_DISTANCE;
} else {
sr.priorityDistance = 1;
}
sr.priority = SEARCH_AMENITY_BY_NAME_PRIORITY;
phrase.countUnknownWordsMatch(sr);
sr.objectType = ObjectType.POI;
resultMatcher.publish(sr);
return false;
}
@Override
public boolean isCancelled() {
return resultMatcher.isCancelled() && (limit < LIMIT) ;
}
});
while (offlineIterator.hasNext()) {
BinaryMapIndexReader r = offlineIterator.next();
currentFile[0] = r;
r.searchPoiByName(req);
resultMatcher.apiSearchRegionFinished(this, r, phrase);
}
return true;
}
@Override
public int getSearchPriority(SearchPhrase p) {
if (p.hasObjectType(ObjectType.POI) ||
!p.isUnknownSearchWordPresent()) {
return -1;
}
if (p.hasObjectType(ObjectType.POI_TYPE)) {
return -1;
}
if (p.getUnknownSearchWordLength() > 3 || p.getRadiusLevel() > 1) {
return SEARCH_AMENITY_BY_NAME_API_PRIORITY_IF_3_CHAR;
}
return -1;
}
@Override
public boolean isSearchMoreAvailable(SearchPhrase phrase) {
return super.isSearchMoreAvailable(phrase) && getSearchPriority(phrase) != -1;
}
}
public static class SearchAmenityTypesAPI extends SearchBaseAPI {
private Map<String, PoiType> translatedNames = new LinkedHashMap<>();
private List<PoiFilter> topVisibleFilters;
private List<PoiCategory> categories;
private List<CustomSearchPoiFilter> customPoiFilters = new ArrayList<>();
private TIntArrayList customPoiFiltersPriorites = new TIntArrayList();
private MapPoiTypes types;
public SearchAmenityTypesAPI(MapPoiTypes types) {
super(ObjectType.POI_TYPE);
this.types = types;
}
public void clearCustomFilters() {
this.customPoiFilters.clear();
this.customPoiFiltersPriorites.clear();
}
public void addCustomFilter(CustomSearchPoiFilter poiFilter, int priority) {
this.customPoiFilters.add(poiFilter);
this.customPoiFiltersPriorites.add(priority);
}
@Override
public boolean search(SearchPhrase phrase, SearchResultMatcher resultMatcher) throws IOException {
if (translatedNames.isEmpty()) {
translatedNames = types.getAllTranslatedNames(false);
topVisibleFilters = types.getTopVisibleFilters();
categories = types.getCategories(false);
}
// results.clear();
List<AbstractPoiType> results = new ArrayList<AbstractPoiType>() ;
NameStringMatcher nm = phrase.getNameStringMatcher();
for (PoiFilter pf : topVisibleFilters) {
if (!phrase.isUnknownSearchWordPresent() || nm.matches(pf.getTranslation())) {
results.add(pf);
}
}
if (phrase.isUnknownSearchWordPresent()) {
for (PoiCategory c : categories) {
if (!results.contains(c) && nm.matches(c.getTranslation())) {
results.add(c);
}
}
Iterator<Entry<String, PoiType>> it = translatedNames.entrySet().iterator();
while (it.hasNext()) {
Entry<String, PoiType> e = it.next();
PoiType pt = e.getValue();
if (pt.getCategory() != types.getOtherMapCategory()) {
if (!results.contains(pt) && (nm.matches(e.getKey()) || nm.matches(pt.getTranslation()))) {
results.add(pt);
}
List<PoiType> additionals = pt.getPoiAdditionals();
if (additionals != null) {
for (PoiType a : additionals) {
if (!a.isReference() && !results.contains(a) && (nm.matches(a.getKeyName().replace('_', ' ')) || nm.matches(a.getTranslation()))) {
results.add(a);
}
}
}
}
}
}
for (AbstractPoiType pt : results) {
SearchResult res = new SearchResult(phrase);
res.localeName = pt.getTranslation();
res.object = pt;
res.priority = SEARCH_AMENITY_TYPE_PRIORITY;
res.priorityDistance = 0;
res.objectType = ObjectType.POI_TYPE;
resultMatcher.publish(res);
}
for (int i = 0; i < customPoiFilters.size(); i++) {
CustomSearchPoiFilter csf = customPoiFilters.get(i);
int p = customPoiFiltersPriorites.get(i);
if (!phrase.isUnknownSearchWordPresent() || nm.matches(csf.getName())) {
SearchResult res = new SearchResult(phrase);
res.localeName = csf.getName();
res.object = csf;
res.priority = SEARCH_AMENITY_TYPE_PRIORITY + p;
res.objectType = ObjectType.POI_TYPE;
resultMatcher.publish(res);
}
}
return true;
}
@Override
public boolean isSearchMoreAvailable(SearchPhrase phrase) {
return false;
}
@Override
public int getSearchPriority(SearchPhrase p) {
if (p.hasObjectType(ObjectType.POI) || p.hasObjectType(ObjectType.POI_TYPE)) {
return -1;
}
if (!p.isNoSelectedType() && !p.isUnknownSearchWordPresent()) {
return -1;
}
return SEARCH_AMENITY_TYPE_API_PRIORITY;
}
}
public static class SearchAmenityByTypeAPI extends SearchBaseAPI {
private MapPoiTypes types;
public SearchAmenityByTypeAPI(MapPoiTypes types) {
super(ObjectType.POI);
this.types = types;
}
@Override
public boolean isSearchMoreAvailable(SearchPhrase phrase) {
return getSearchPriority(phrase) != -1 && super.isSearchMoreAvailable(phrase);
}
private Map<PoiCategory, LinkedHashSet<String>> acceptedTypes = new LinkedHashMap<PoiCategory,
LinkedHashSet<String>>();
private Map<String, PoiType> poiAdditionals = new HashMap<String, PoiType>();
public void updateTypesToAccept(AbstractPoiType pt) {
pt.putTypes(acceptedTypes);
if (pt instanceof PoiType && ((PoiType) pt).isAdditional() && ((PoiType) pt).getParentType() != null) {
fillPoiAdditionals(((PoiType) pt).getParentType());
} else {
fillPoiAdditionals(pt);
}
}
private void fillPoiAdditionals(AbstractPoiType pt) {
for (PoiType add : pt.getPoiAdditionals()) {
poiAdditionals.put(add.getKeyName().replace('_', ':').replace(' ', ':'), add);
poiAdditionals.put(add.getTranslation().replace(' ', ':').toLowerCase(), add);
}
if (pt instanceof PoiFilter && !(pt instanceof PoiCategory)) {
for (PoiType ps : ((PoiFilter) pt).getPoiTypes()) {
fillPoiAdditionals(ps);
}
}
}
@Override
public boolean search(final SearchPhrase phrase, final SearchResultMatcher resultMatcher) throws IOException {
if (phrase.isLastWord(ObjectType.POI_TYPE)) {
Object obj = phrase.getLastSelectedWord().getResult().object;
SearchPoiTypeFilter ptf;
if (obj instanceof AbstractPoiType) {
ptf = getPoiTypeFilter((AbstractPoiType) obj);
} else if (obj instanceof SearchPoiTypeFilter) {
ptf = (SearchPoiTypeFilter) obj;
} else {
throw new UnsupportedOperationException();
}
QuadRect bbox = phrase.getRadiusBBoxToSearch(10000);
List<BinaryMapIndexReader> oo = phrase.getOfflineIndexes();
for (BinaryMapIndexReader o : oo) {
ResultMatcher<Amenity> rm = getResultMatcher(phrase, resultMatcher, o);
if (obj instanceof CustomSearchPoiFilter) {
rm = ((CustomSearchPoiFilter) obj).wrapResultMatcher(rm);
}
SearchRequest<Amenity> req = BinaryMapIndexReader.buildSearchPoiRequest(
(int)bbox.left, (int)bbox.right,
(int)bbox.top, (int)bbox.bottom, -1, ptf,
rm);
o.searchPoi(req);
resultMatcher.apiSearchRegionFinished(this, o, phrase);
}
}
return true;
}
private ResultMatcher<Amenity> getResultMatcher(final SearchPhrase phrase, final SearchResultMatcher resultMatcher,
final BinaryMapIndexReader selected) {
final NameStringMatcher ns = phrase.getNameStringMatcher();
return new ResultMatcher<Amenity>() {
@Override
public boolean publish(Amenity object) {
SearchResult res = new SearchResult(phrase);
res.localeName = object.getName(phrase.getSettings().getLang(), phrase.getSettings().isTransliterate());
res.otherNames = object.getAllNames(true);
if (Algorithms.isEmpty(res.localeName)) {
AbstractPoiType st = types.getAnyPoiTypeByKey(object.getSubType());
if (st != null) {
res.localeName = st.getTranslation();
} else {
res.localeName = object.getSubType();
}
}
if (phrase.isUnknownSearchWordPresent()
&& !(ns.matches(res.localeName) || ns.matches(res.otherNames))) {
return false;
}
res.object = object;
res.preferredZoom = 17;
res.file = selected;
res.location = object.getLocation();
res.priority = SEARCH_AMENITY_BY_TYPE_PRIORITY;
res.priorityDistance = 1;
res.objectType = ObjectType.POI;
resultMatcher.publish(res);
return false;
}
@Override
public boolean isCancelled() {
return resultMatcher.isCancelled();
}
};
}
private SearchPoiTypeFilter getPoiTypeFilter(AbstractPoiType pt) {
acceptedTypes.clear();
poiAdditionals.clear();
updateTypesToAccept(pt);
return new SearchPoiTypeFilter() {
@Override
public boolean isEmpty() {
return false;
}
@Override
public boolean accept(PoiCategory type, String subtype) {
if (type == null) {
return true;
}
if (!types.isRegisteredType(type)) {
type = types.getOtherPoiCategory();
}
if (!acceptedTypes.containsKey(type)) {
return false;
}
LinkedHashSet<String> set = acceptedTypes.get(type);
if (set == null) {
return true;
}
return set.contains(subtype);
}
};
}
@Override
public int getSearchPriority(SearchPhrase p) {
if (p.isLastWord(ObjectType.POI_TYPE) &&
p.getLastTokenLocation() != null) {
return SEARCH_AMENITY_BY_TYPE_PRIORITY;
}
return -1;
}
}
public static class SearchStreetByCityAPI extends SearchBaseAPI {
private SearchBaseAPI streetsAPI;
public SearchStreetByCityAPI(SearchBuildingAndIntersectionsByStreetAPI streetsAPI) {
super(ObjectType.HOUSE, ObjectType.STREET, ObjectType.STREET_INTERSECTION);
this.streetsAPI = streetsAPI;
}
@Override
public boolean isSearchMoreAvailable(SearchPhrase phrase) {
// case when street is not found for given city is covered here
return phrase.getRadiusLevel() == 1 && getSearchPriority(phrase) != -1;
}
private static int LIMIT = 10000;
@Override
public boolean search(SearchPhrase phrase, SearchResultMatcher resultMatcher) throws IOException {
SearchWord sw = phrase.getLastSelectedWord();
if (isLastWordCityGroup(phrase) && sw.getResult() != null && sw.getResult().file != null) {
City c = (City) sw.getResult().object;
if (c.getStreets().isEmpty()) {
sw.getResult().file.preloadStreets(c, null);
}
int limit = 0;
String wordToSearch = phrase.getUnknownWordToSearch();
boolean firstUnknownWordMatches = wordToSearch.equals(phrase.getUnknownSearchWord());
NameStringMatcher nm = phrase.getNameStringMatcher(wordToSearch, phrase.isUnknownSearchWordComplete());
for (Street object : c.getStreets()) {
SearchResult res = new SearchResult(phrase);
res.localeName = object.getName(phrase.getSettings().getLang(), phrase.getSettings().isTransliterate());
res.otherNames = object.getAllNames(true);
if (object.getName().startsWith("<")) {
// streets related to city
continue;
}
if (phrase.isUnknownSearchWordPresent()
&& !(nm.matches(res.localeName) || nm.matches(res.otherNames))) {
continue;
}
res.firstUnknownWordMatches = firstUnknownWordMatches ||
phrase.getNameStringMatcher().matches(res.localeName) ||
phrase.getNameStringMatcher().matches(res.otherNames);
res.localeRelatedObjectName = c.getName(phrase.getSettings().getLang(), phrase.getSettings().isTransliterate());
res.object = object;
res.preferredZoom = 17;
res.file = sw.getResult().file;
res.location = object.getLocation();
res.priority = SEARCH_STREET_BY_CITY_PRIORITY;
//res.priorityDistance = 1;
res.objectType = ObjectType.STREET;
subSearchApiOrPublish(phrase, resultMatcher, res, streetsAPI);
if (limit++ > LIMIT) {
break;
}
}
return true;
}
return true;
}
@Override
public int getSearchPriority(SearchPhrase p) {
if (isLastWordCityGroup(p)) {
return SEARCH_STREET_BY_CITY_PRIORITY;
}
return -1;
}
}
public static boolean isLastWordCityGroup(SearchPhrase p ) {
return p.isLastWord(ObjectType.CITY) || p.isLastWord(ObjectType.POSTCODE) ||
p.isLastWord(ObjectType.VILLAGE);
}
public static class SearchBuildingAndIntersectionsByStreetAPI extends SearchBaseAPI {
Street cacheBuilding;
public SearchBuildingAndIntersectionsByStreetAPI() {
super(ObjectType.HOUSE, ObjectType.STREET_INTERSECTION);
}
@Override
public boolean isSearchMoreAvailable(SearchPhrase phrase) {
return false;
}
@Override
public boolean search(SearchPhrase phrase, final SearchResultMatcher resultMatcher) throws IOException {
Street s = null;
int priority = SEARCH_BUILDING_BY_STREET_PRIORITY;
if (phrase.isLastWord(ObjectType.STREET)) {
s = (Street) phrase.getLastSelectedWord().getResult().object;
}
if (isLastWordCityGroup(phrase)) {
priority = SEARCH_BUILDING_BY_CITY_PRIORITY;
Object o = phrase.getLastSelectedWord().getResult().object;
if (o instanceof City) {
List<Street> streets = ((City) o).getStreets();
if (streets.size() == 1) {
s = streets.get(0);
} else {
for (Street st : streets) {
if (st.getName().equals(((City) o).getName()) ||
st.getName().equals("<"+((City) o).getName()+">")) {
s = st;
break;
}
}
}
}
}
if (s != null) {
BinaryMapIndexReader file = phrase.getLastSelectedWord().getResult().file;
if (cacheBuilding != s) {
cacheBuilding = s;
SearchRequest<Building> sr = BinaryMapIndexReader
.buildAddressRequest(new ResultMatcher<Building>() {
@Override
public boolean publish(Building object) {
return true;
}
@Override
public boolean isCancelled() {
return resultMatcher.isCancelled();
}
});
file.preloadBuildings(s, sr);
Collections.sort(s.getBuildings(), new Comparator<Building>() {
@Override
public int compare(Building o1, Building o2) {
int i1 = Algorithms.extractFirstIntegerNumber(o1.getName());
int i2 = Algorithms.extractFirstIntegerNumber(o2.getName());
if (i1 == i2) {
return 0;
}
return Algorithms.compare(i1, i2);
}
});
}
String lw = phrase.getUnknownWordToSearchBuilding();
NameStringMatcher buildingMatch = phrase.getNameStringMatcher(lw, phrase.isLastUnknownSearchWordComplete());
for (Building b : s.getBuildings()) {
SearchResult res = new SearchResult(phrase);
boolean interpolation = b.belongsToInterpolation(lw);
if ((!buildingMatch.matches(b.getName()) && !interpolation)
|| !phrase.isSearchTypeAllowed(ObjectType.HOUSE)) {
continue;
}
res.localeName = b.getName(phrase.getSettings().getLang(), phrase.getSettings().isTransliterate());
res.otherNames = b.getAllNames(true);
res.object = b;
res.file = file;
res.priority = priority;
res.priorityDistance = 0;
res.relatedObject = s;
res.localeRelatedObjectName = s.getName(phrase.getSettings().getLang(), phrase.getSettings().isTransliterate());
res.objectType = ObjectType.HOUSE;
if(interpolation) {
res.location = b.getLocation(b.interpolation(lw));
} else {
res.location = b.getLocation();
}
res.preferredZoom = 17;
resultMatcher.publish(res);
}
String streetIntersection = phrase.getUnknownWordToSearch();
NameStringMatcher streetMatch = phrase.getNameStringMatcher(streetIntersection, phrase.isLastUnknownSearchWordComplete());
if (Algorithms.isEmpty(streetIntersection) ||
(!Character.isDigit(streetIntersection.charAt(0)) &&
CommonWords.getCommonSearch(streetIntersection) == -1) ) {
for (Street street : s.getIntersectedStreets()) {
SearchResult res = new SearchResult(phrase);
if ((!streetMatch.matches(street.getName()) && !streetMatch.matches(street.getAllNames(true)))
|| !phrase.isSearchTypeAllowed(ObjectType.STREET_INTERSECTION)) {
continue;
}
res.otherNames = street.getAllNames(true);
res.localeName = street.getName(phrase.getSettings().getLang(), phrase.getSettings().isTransliterate());
res.object = street;
res.file = file;
res.relatedObject = s;
res.priority = priority + 1;
res.localeRelatedObjectName = s.getName(phrase.getSettings().getLang(), phrase.getSettings().isTransliterate());
res.priorityDistance = 0;
res.objectType = ObjectType.STREET_INTERSECTION;
res.location = street.getLocation();
res.preferredZoom = 16;
resultMatcher.publish(res);
}
}
}
return true;
}
@Override
public int getSearchPriority(SearchPhrase p) {
if (isLastWordCityGroup(p)) {
return SEARCH_BUILDING_BY_CITY_PRIORITY;
}
if (!p.isLastWord(ObjectType.STREET)) {
return -1;
}
return SEARCH_BUILDING_BY_STREET_PRIORITY;
}
}
public static class SearchLocationAndUrlAPI extends SearchBaseAPI {
public SearchLocationAndUrlAPI() {
super(ObjectType.LOCATION, ObjectType.PARTIAL_LOCATION);
}
@Override
public boolean isSearchMoreAvailable(SearchPhrase phrase) {
return false;
}
// newFormat = PointDescription.FORMAT_DEGREES;
// newFormat = PointDescription.FORMAT_MINUTES;
// newFormat = PointDescription.FORMAT_SECONDS;
public void testUTM() {
double northing = 0;
double easting = 0;
String zone = "";
char c = zone.charAt(zone.length() -1);
int z = Integer.parseInt(zone.substring(0, zone.length() - 1));
UTMPoint upoint = new UTMPoint(northing, easting, z, c);
LatLonPoint ll = upoint.toLatLonPoint();
LatLon loc = new LatLon(ll.getLatitude(), ll.getLongitude());
}
@Override
public boolean search(SearchPhrase phrase, SearchResultMatcher resultMatcher) throws IOException {
if (!phrase.isUnknownSearchWordPresent()) {
return false;
}
boolean parseUrl = parseUrl(phrase, resultMatcher);
if (!parseUrl) {
parseLocation(phrase, resultMatcher);
}
return super.search(phrase, resultMatcher);
}
private boolean isKindOfNumber(String s) {
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 == '"') {
} else {
return false;
}
}
return true;
}
LatLon parsePartialLocation(String s) {
s = s.trim();
if (s.length() == 0 || !(s.charAt(0) == '-' || Character.isDigit(s.charAt(0))
|| s.charAt(0) == 'S' || s.charAt(0) == 's'
|| s.charAt(0) == 'N' || s.charAt(0) == 'n'
|| s.contains("://"))) {
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) {
return null;
}
double lat = parse1Coordinate(all, 0, all.size());
return new LatLon(lat, 0);
}
LatLon parseLocation(String s) {
s = s.trim();
// detect OLC first
// avoid throwing exceptions by carefully checking exceptions
if (s.length() > 0 && OpenLocationCode.isValidCode(s)) {
OpenLocationCode olc = new OpenLocationCode(s);
if (olc.isFull()) {
OpenLocationCode.CodeArea codeArea = olc.decode();
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("://"))) {
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) {
return null;
}
// detect UTM
if (all.size() == 4 && d.size() == 3 && all.get(1) instanceof String) {
char ch = all.get(1).toString().charAt(0);
if (Character.isLetter(ch)) {
UTMPoint upoint = new UTMPoint(d.get(2), d.get(1), d.get(0).intValue(), ch);
LatLonPoint ll = upoint.toLatLonPoint();
return new LatLon(ll.getLatitude(), ll.getLongitude());
}
}
if (all.size() == 3 && d.size() == 2 && all.get(1) instanceof String) {
char ch = all.get(1).toString().charAt(0);
String combined = strings.get(2);
if (Character.isLetter(ch)) {
try {
String east = combined.substring(0, combined.length() / 2);
String north = combined.substring(combined.length() / 2, combined.length());
UTMPoint upoint = new UTMPoint(Double.parseDouble(north), Double.parseDouble(east), d.get(0)
.intValue(), ch);
LatLonPoint ll = upoint.toLatLonPoint();
return new LatLon(ll.getLatitude(), ll.getLongitude());
} catch (NumberFormatException e) {
}
}
}
// try to find split lat/lon position
int jointNumbers = 0;
int lastJoin = 0;
int degSplit = -1;
int degType = -1; // 0 - degree, 1 - minutes, 2 - seconds
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) {
jointNumbers ++;
lastJoin = i;
}
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") ||
all.get(i).equals("E") || all.get(i).equals("W")) {
eastSplit = i;
}
int dg = -1;
if (all.get(i).equals("°")) {
dg = 0;
} else if (all.get(i).equals("\'") || all.get(i).equals("′")) {
dg = 1;
} else if (all.get(i).equals("″") || all.get(i).equals("\"")) {
dg = 2;
}
if (dg != -1) {
if (!finishDegSplit) {
if (degType < dg) {
degSplit = i + 1;
degType = dg;
} else {
finishDegSplit = true;
degType = dg;
}
} else {
if (degType < dg) {
degType = dg;
} else {
// reject delimiter
degSplit = -1;
}
}
}
}
int split = -1;
if (jointNumbers == 1) {
split = lastJoin;
}
if (northSplit != -1 && northSplit < all.size() -1) {
split = northSplit;
} else if (eastSplit != -1 && eastSplit < all.size() -1) {
split = eastSplit;
} else if (degSplit != -1 && degSplit < all.size() -1) {
split = degSplit;
}
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) {
return new LatLon(d.get(0), d.get(1));
}
// simple url case
if (s.contains("://")) {
double lat = 0;
double lon = 0;
boolean only2decimals = true;
for (int i = 0; i < d.size(); i++) {
if (d.get(i).doubleValue() != d.get(i).intValue()) {
if (lat == 0) {
lat = d.get(i);
} else if (lon == 0) {
lon = d.get(i);
} else {
only2decimals = false;
}
}
}
if (lat != 0 && lon != 0 && only2decimals) {
return new LatLon(lat, lon);
}
}
// split by equal number of digits
if (d.size() > 2 && d.size() % 2 == 0) {
int ind = d.size() / 2 + 1;
int splitEq = -1;
for (int i = 0; i < all.size(); i++) {
if (all.get(i) instanceof Double) {
ind --;
}
if (ind == 0) {
splitEq = i;
break;
}
}
if (splitEq != -1) {
double lat = parse1Coordinate(all, 0, splitEq);
double lon = parse1Coordinate(all, splitEq, all.size());
return new LatLon(lat, lon);
}
}
return null;
}
public double parse1Coordinate(List<Object> all, int begin, int end) {
boolean neg = false;
double d = 0;
int type = 0; // degree - 0, minutes - 1, seconds = 2
Double prevDouble = null;
for (int i = begin; i <= end; i++) {
Object o = i == end ? "" : all.get(i);
if(o.equals("S") || o.equals("W")) {
neg = !neg;
}
if (prevDouble != null) {
if (o.equals("°")) {
type = 0;
} else if (o.equals("′") /*o.equals("'")*/) {
// ' can be used as delimeter ignore it
type = 1;
} else if (o.equals("\"") || o.equals("″")) {
type = 2;
}
if (type == 0) {
double ld = prevDouble.doubleValue();
if (ld < 0) {
ld = -ld;
neg = true;
}
d += ld;
} else if (type == 1) {
d += prevDouble.doubleValue() / 60.f;
} else /*if (type == 1) */ {
d += prevDouble.doubleValue() / 3600.f;
}
type++;
}
if (o instanceof Double) {
prevDouble = (Double) o;
} else {
prevDouble = null;
}
}
if (neg) {
d = -d;
}
return d;
}
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++) {
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) {
all.add(s.substring(word, i));
strings.add(s.substring(word, i));
}
digit = true;
word = i;
} else {
if(word == -1) {
word = i;
}
// if digit
// continue
}
} else {
if (digit){
try {
double dl = Double.parseDouble(s.substring(word, i));
d.add(dl);
all.add(dl);
strings.add(s.substring(word, i));
digit = false;
word = -1;
} catch (NumberFormatException e) {
}
}
if (nonwh) {
if(!Character.isLetter(ch)) {
if(word != -1) {
all.add(s.substring(word, i));
strings.add(s.substring(word, i));
}
all.add(s.substring(i, i + 1));
strings.add(s.substring(i, i +1));
word = -1;
} else if(word == -1) {
word = i;
}
} else {
if (word != -1) {
all.add(s.substring(word, i));
strings.add(s.substring(word, i));
}
word = -1;
}
}
}
}
private void parseLocation(SearchPhrase phrase, SearchResultMatcher resultMatcher) {
String lw = phrase.getUnknownSearchPhrase();
LatLon l = parseLocation(lw);
if (l != null) {
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 && phrase.isSearchTypeAllowed(ObjectType.PARTIAL_LOCATION)) {
SearchResult sp = new SearchResult(phrase);
sp.priority = SEARCH_LOCATION_PRIORITY;
sp.object = sp.location = ll;
sp.localeName = ((float) sp.location.getLatitude()) + ", <input> ";
sp.objectType = ObjectType.PARTIAL_LOCATION;
resultMatcher.publish(sp);
}
}
}
private boolean parseUrl(SearchPhrase phrase, SearchResultMatcher resultMatcher) {
String text = phrase.getUnknownSearchPhrase();
GeoParsedPoint pnt = GeoPointParserUtil.parse(text);
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) {
sp.preferredZoom = pnt.getZoom();
}
sp.objectType = ObjectType.LOCATION;
resultMatcher.publish(sp);
return true;
}
return false;
}
@Override
public int getSearchPriority(SearchPhrase p) {
return SEARCH_LOCATION_PRIORITY;
}
}
}