Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
092c8289da
5 changed files with 207 additions and 44 deletions
|
@ -16,6 +16,7 @@ import net.osmand.CollatorStringMatcher.StringMatcherMode;
|
|||
import net.osmand.PlatformUtil;
|
||||
import net.osmand.StringMatcher;
|
||||
import net.osmand.binary.BinaryMapIndexReader.SearchRequest;
|
||||
import net.osmand.binary.BinaryMapPoiReaderAdapter.PoiRegion;
|
||||
import net.osmand.binary.OsmandOdb.AddressNameIndexDataAtom;
|
||||
import net.osmand.binary.OsmandOdb.OsmAndAddressIndex.CitiesIndex;
|
||||
import net.osmand.binary.OsmandOdb.OsmAndAddressNameIndexData;
|
||||
|
@ -55,6 +56,10 @@ public class BinaryMapAddressReaderAdapter {
|
|||
List<CitiesBlock> cities = new ArrayList<BinaryMapAddressReaderAdapter.CitiesBlock>();
|
||||
|
||||
LatLon calculatedCenter = null;
|
||||
public int bottom31;
|
||||
public int top31;
|
||||
public int right31;
|
||||
public int left31;
|
||||
|
||||
public String getEnName() {
|
||||
return enName;
|
||||
|
@ -109,11 +114,39 @@ public class BinaryMapAddressReaderAdapter {
|
|||
return map.readInt();
|
||||
}
|
||||
|
||||
private void readBoundariesIndex(AddressRegion region) throws IOException {
|
||||
while (true) {
|
||||
int t = codedIS.readTag();
|
||||
int tag = WireFormat.getTagFieldNumber(t);
|
||||
switch (tag) {
|
||||
case 0:
|
||||
return;
|
||||
case OsmandOdb.OsmAndTileBox.LEFT_FIELD_NUMBER:
|
||||
region.left31 = codedIS.readUInt32();
|
||||
break;
|
||||
case OsmandOdb.OsmAndTileBox.RIGHT_FIELD_NUMBER:
|
||||
region.right31 = codedIS.readUInt32();
|
||||
break;
|
||||
case OsmandOdb.OsmAndTileBox.TOP_FIELD_NUMBER:
|
||||
region.top31 = codedIS.readUInt32();
|
||||
break;
|
||||
case OsmandOdb.OsmAndTileBox.BOTTOM_FIELD_NUMBER:
|
||||
region.bottom31 = codedIS.readUInt32();
|
||||
break;
|
||||
default:
|
||||
skipUnknownField(t);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected void readAddressIndex(AddressRegion region) throws IOException {
|
||||
while (true) {
|
||||
int t = codedIS.readTag();
|
||||
int tag = WireFormat.getTagFieldNumber(t);
|
||||
int length;
|
||||
int oldLimit;
|
||||
switch (tag) {
|
||||
case 0:
|
||||
if (region.enName == null || region.enName.length() == 0) {
|
||||
|
@ -126,9 +159,16 @@ public class BinaryMapAddressReaderAdapter {
|
|||
case OsmandOdb.OsmAndAddressIndex.NAME_EN_FIELD_NUMBER:
|
||||
region.enName = codedIS.readString();
|
||||
break;
|
||||
case OsmandOdb.OsmAndAddressIndex.BOUNDARIES_FIELD_NUMBER:
|
||||
length = codedIS.readRawVarint32();
|
||||
oldLimit = codedIS.pushLimit(length);
|
||||
readBoundariesIndex(region);
|
||||
codedIS.popLimit(oldLimit);
|
||||
region.enName = codedIS.readString();
|
||||
break;
|
||||
case OsmandOdb.OsmAndAddressIndex.ATTRIBUTETAGSTABLE_FIELD_NUMBER:
|
||||
int length2 = codedIS.readRawVarint32();
|
||||
int oldLimit = codedIS.pushLimit(length2);
|
||||
length = codedIS.readRawVarint32();
|
||||
oldLimit = codedIS.pushLimit(length);
|
||||
region.attributeTagsTable = map.readStringTable();
|
||||
codedIS.popLimit(oldLimit);
|
||||
break;
|
||||
|
@ -156,7 +196,7 @@ public class BinaryMapAddressReaderAdapter {
|
|||
break;
|
||||
case OsmandOdb.OsmAndAddressIndex.NAMEINDEX_FIELD_NUMBER:
|
||||
region.indexNameOffset = codedIS.getTotalBytesRead();
|
||||
int length = readInt();
|
||||
length = readInt();
|
||||
codedIS.seek(region.indexNameOffset + length + 4);
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -341,6 +341,24 @@ public class BinaryMapIndexReader {
|
|||
return false;
|
||||
}
|
||||
|
||||
public boolean containsPoiData(int left31x, int top31y, int right31x, int bottom31y) {
|
||||
for (PoiRegion index : poiIndexes) {
|
||||
if (right31x >= index.left31 && left31x <= index.right31 && index.top31 <= bottom31y && index.bottom31 >= top31y) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean containsAddressData(int left31x, int top31y, int right31x, int bottom31y) {
|
||||
for (AddressRegion index : addressIndexes) {
|
||||
if (right31x >= index.left31 && left31x <= index.right31 && index.top31 <= bottom31y && index.bottom31 >= top31y) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean containsMapData(int tile31x, int tile31y, int zoom) {
|
||||
for (MapIndex mapIndex : mapIndexes) {
|
||||
for (MapRoot root : mapIndex.getRoots()) {
|
||||
|
|
|
@ -59,6 +59,10 @@ public class BinaryMapPoiReaderAdapter {
|
|||
double rightLongitude;
|
||||
double topLatitude;
|
||||
double bottomLatitude;
|
||||
int left31;
|
||||
int right31;
|
||||
int top31;
|
||||
int bottom31;
|
||||
|
||||
public double getLeftLongitude() {
|
||||
return leftLongitude;
|
||||
|
@ -134,16 +138,20 @@ public class BinaryMapPoiReaderAdapter {
|
|||
case 0:
|
||||
return;
|
||||
case OsmandOdb.OsmAndTileBox.LEFT_FIELD_NUMBER:
|
||||
region.leftLongitude = MapUtils.get31LongitudeX(codedIS.readUInt32());
|
||||
region.left31 = codedIS.readUInt32();
|
||||
region.leftLongitude = MapUtils.get31LongitudeX(region.left31);
|
||||
break;
|
||||
case OsmandOdb.OsmAndTileBox.RIGHT_FIELD_NUMBER:
|
||||
region.rightLongitude = MapUtils.get31LongitudeX(codedIS.readUInt32());
|
||||
region.right31 = codedIS.readUInt32();
|
||||
region.rightLongitude = MapUtils.get31LongitudeX(region.right31);
|
||||
break;
|
||||
case OsmandOdb.OsmAndTileBox.TOP_FIELD_NUMBER:
|
||||
region.topLatitude = MapUtils.get31LatitudeY(codedIS.readUInt32());
|
||||
region.top31 = codedIS.readUInt32();
|
||||
region.topLatitude = MapUtils.get31LatitudeY(region.top31);
|
||||
break;
|
||||
case OsmandOdb.OsmAndTileBox.BOTTOM_FIELD_NUMBER:
|
||||
region.bottomLatitude = MapUtils.get31LatitudeY(codedIS.readUInt32());
|
||||
region.bottom31 = codedIS.readUInt32();
|
||||
region.bottomLatitude = MapUtils.get31LatitudeY(region.bottom31);
|
||||
break;
|
||||
default:
|
||||
skipUnknownField(t);
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
package net.osmand.search.example.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
@ -13,6 +15,7 @@ import net.osmand.CollatorStringMatcher.StringMatcherMode;
|
|||
import net.osmand.OsmAndCollator;
|
||||
import net.osmand.ResultMatcher;
|
||||
import net.osmand.StringMatcher;
|
||||
import net.osmand.binary.BinaryMapAddressReaderAdapter;
|
||||
import net.osmand.binary.BinaryMapIndexReader;
|
||||
import net.osmand.binary.BinaryMapIndexReader.SearchPoiTypeFilter;
|
||||
import net.osmand.binary.BinaryMapIndexReader.SearchRequest;
|
||||
|
@ -31,30 +34,32 @@ import net.osmand.osm.PoiFilter;
|
|||
import net.osmand.osm.PoiType;
|
||||
import net.osmand.search.example.SearchUICore.SearchResultMatcher;
|
||||
import net.osmand.search.example.core.SearchPhrase.NameStringMatcher;
|
||||
import net.osmand.search.example.core.SearchPhrase.SearchPhraseDataType;
|
||||
import net.osmand.util.Algorithms;
|
||||
import net.osmand.util.MapUtils;
|
||||
|
||||
|
||||
public class SearchCoreFactory {
|
||||
// TODO fix search amenity by type (category/additional)
|
||||
|
||||
// TODO streets by city
|
||||
// TODO display closest city to villages
|
||||
// TODO search only closest file (global bbox)
|
||||
// TODO limit to one file if city/street/village/poi selected
|
||||
|
||||
// TODO add location parse
|
||||
// TODO add url parse (geo)
|
||||
// TODO fix search amenity by type (category/additional)
|
||||
// TODO streets by city
|
||||
// TODO amenity by name
|
||||
|
||||
// TODO display closest city to villages (and city to street)
|
||||
// TODO automatically increase radius if nothing found
|
||||
// TODO add location parse
|
||||
// TODO add url parse (geo)
|
||||
|
||||
// TODO buildings interpolation
|
||||
// TODO show buildings if street is one or default ( <CITY>, CITY (den ilp), 1186RM)
|
||||
// TODO exclude duplicate streets/cities...
|
||||
// TODO display results momentarily
|
||||
|
||||
// TODO add full text search with comma
|
||||
// TODO add full text search without comma
|
||||
// TODO MED display results momentarily
|
||||
// TODO MED add full text search with comma
|
||||
// TODO MED add full text search without comma
|
||||
|
||||
// TODO LOW automatically increase radius if nothing found
|
||||
|
||||
|
||||
public static abstract class SearchBaseAPI implements SearchCoreAPI {
|
||||
|
@ -63,20 +68,8 @@ public class SearchCoreFactory {
|
|||
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;
|
||||
double pw = MapUtils.getPowZoom(31 - SearchRequest.ZOOM_TO_SEARCH_POI);
|
||||
return new QuadRect(topLeftX * pw, topLeftY * pw, bottomRightX * pw, bottomRightY * pw);
|
||||
public QuadRect getBBoxToSearch(SearchPhrase phrase, int radiusInMeters) {
|
||||
return phrase.getBBoxToSearch(phrase.getRadiusLevel() * radiusInMeters);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -125,6 +118,7 @@ public class SearchCoreFactory {
|
|||
|
||||
private static final int DEFAULT_BBOX_RADIUS = 1000*1000;
|
||||
private static final int LIMIT = 10000;
|
||||
private Map<BinaryMapIndexReader, List<City>> townCities = new LinkedHashMap<>();
|
||||
|
||||
public boolean isLastWordPoi(SearchPhrase p) {
|
||||
return p.isLastWord(ObjectType.POI);
|
||||
|
@ -150,14 +144,10 @@ public class SearchCoreFactory {
|
|||
// int letters = phrase.getLastWord().length() / 3 + 1;
|
||||
final boolean locSpecified = phrase.getLastTokenLocation() != null;
|
||||
LatLon loc = phrase.getLastTokenLocation();
|
||||
final QuadRect streetBbox = getBBoxToSearch(DEFAULT_BBOX_RADIUS, phrase.getRadiusLevel(),
|
||||
phrase.getLastTokenLocation());
|
||||
final QuadRect postcodeBbox = getBBoxToSearch(DEFAULT_BBOX_RADIUS * 5, phrase.getRadiusLevel(),
|
||||
phrase.getLastTokenLocation());
|
||||
final QuadRect villagesBbox = getBBoxToSearch(DEFAULT_BBOX_RADIUS * 5, phrase.getRadiusLevel(),
|
||||
phrase.getLastTokenLocation());
|
||||
final QuadRect cityBbox = getBBoxToSearch(DEFAULT_BBOX_RADIUS * 10, phrase.getRadiusLevel(),
|
||||
phrase.getLastTokenLocation());
|
||||
final QuadRect streetBbox = getBBoxToSearch(phrase, DEFAULT_BBOX_RADIUS);
|
||||
final QuadRect postcodeBbox = getBBoxToSearch(phrase, DEFAULT_BBOX_RADIUS * 5);
|
||||
final QuadRect villagesBbox = getBBoxToSearch(phrase, DEFAULT_BBOX_RADIUS * 5);
|
||||
final QuadRect cityBbox = getBBoxToSearch(phrase, DEFAULT_BBOX_RADIUS * 10);
|
||||
final int priority = isNoSelectedType(phrase) ? 1 : 3;
|
||||
final BinaryMapIndexReader[] currentFile = new BinaryMapIndexReader[1];
|
||||
ResultMatcher<MapObject> rm = new ResultMatcher<MapObject>() {
|
||||
|
@ -217,6 +207,16 @@ public class SearchCoreFactory {
|
|||
resultMatcher.isCancelled();
|
||||
}
|
||||
};
|
||||
Iterator<BinaryMapIndexReader> offlineIndexes = phrase.getOfflineIndexes(DEFAULT_BBOX_RADIUS * 10, 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(BinaryMapIndexReader r : phrase.getOfflineIndexes()) {
|
||||
currentFile[0] = r;
|
||||
SearchRequest<MapObject> req = BinaryMapIndexReader.buildAddressByNameRequest(rm,
|
||||
|
@ -328,11 +328,13 @@ public class SearchCoreFactory {
|
|||
public boolean search(final SearchPhrase phrase, final SearchResultMatcher resultMatcher) throws IOException {
|
||||
if(phrase.isLastWord(ObjectType.POI_TYPE)) {
|
||||
final AbstractPoiType pt = (AbstractPoiType) phrase.getLastSelectedWord().getResult().object;
|
||||
QuadRect bbox = getBBoxToSearch(10000, phrase.getRadiusLevel(), phrase.getLastTokenLocation());
|
||||
QuadRect bbox = getBBoxToSearch(phrase, 10000);
|
||||
List<BinaryMapIndexReader> oo = phrase.getOfflineIndexes();
|
||||
final BinaryMapIndexReader[] selected = new BinaryMapIndexReader[1];
|
||||
final NameStringMatcher ns = phrase.getNameStringMatcher();
|
||||
SearchRequest<Amenity> req = BinaryMapIndexReader.buildSearchPoiRequest((int)bbox.left, (int)bbox.right, (int)bbox.top, (int)bbox.bottom, -1,
|
||||
SearchRequest<Amenity> req = BinaryMapIndexReader.buildSearchPoiRequest(
|
||||
(int)bbox.left, (int)bbox.right,
|
||||
(int)bbox.top, (int)bbox.bottom, -1,
|
||||
new SearchPoiTypeFilter() {
|
||||
|
||||
@Override
|
||||
|
@ -384,9 +386,8 @@ public class SearchCoreFactory {
|
|||
return resultMatcher.isCancelled();
|
||||
}
|
||||
});
|
||||
for(BinaryMapIndexReader o : oo) {
|
||||
for (BinaryMapIndexReader o : oo) {
|
||||
selected[0] = o;
|
||||
|
||||
o.searchPoi(req);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import java.util.ArrayList;
|
|||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -11,7 +12,9 @@ import net.osmand.CollatorStringMatcher;
|
|||
import net.osmand.StringMatcher;
|
||||
import net.osmand.CollatorStringMatcher.StringMatcherMode;
|
||||
import net.osmand.binary.BinaryMapIndexReader;
|
||||
import net.osmand.binary.BinaryMapIndexReader.SearchRequest;
|
||||
import net.osmand.data.LatLon;
|
||||
import net.osmand.data.QuadRect;
|
||||
import net.osmand.util.MapUtils;
|
||||
|
||||
//immutable object
|
||||
|
@ -23,6 +26,12 @@ public class SearchPhrase {
|
|||
private SearchSettings settings;
|
||||
private List<BinaryMapIndexReader> indexes;
|
||||
private String lastWordTrim;
|
||||
private QuadRect cache1kmRect;
|
||||
|
||||
public enum SearchPhraseDataType {
|
||||
MAP, ADDRESS, ROUTING, POI
|
||||
}
|
||||
|
||||
|
||||
public SearchPhrase(SearchSettings settings) {
|
||||
this.settings = settings;
|
||||
|
@ -72,6 +81,93 @@ public class SearchPhrase {
|
|||
return words;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public QuadRect getBBoxToSearch(int radiusInMeters) {
|
||||
QuadRect cache1kmRect = get1km31Rect();
|
||||
if(cache1kmRect == null) {
|
||||
return null;
|
||||
}
|
||||
int max = (1 << 31) - 1;
|
||||
double dx = (cache1kmRect.width() / 2) * radiusInMeters / 1000;
|
||||
double dy = (cache1kmRect.height() / 2) * radiusInMeters / 1000;
|
||||
double topLeftX = Math.max(0, cache1kmRect.left - dx);
|
||||
double topLeftY = Math.max(0, cache1kmRect.top - dy);
|
||||
double bottomRightX = Math.min(max, cache1kmRect.right + dx);
|
||||
double bottomRightY = Math.min(max, cache1kmRect.bottom + dy);
|
||||
return new QuadRect(topLeftX, topLeftY, bottomRightX, bottomRightY);
|
||||
}
|
||||
|
||||
public QuadRect get1km31Rect() {
|
||||
if(cache1kmRect != null) {
|
||||
return cache1kmRect;
|
||||
}
|
||||
LatLon l = getLastTokenLocation();
|
||||
if (l == null) {
|
||||
return null;
|
||||
}
|
||||
float coeff = (float) (1000 / MapUtils.getTileDistanceWidth(SearchRequest.ZOOM_TO_SEARCH_POI));
|
||||
double tx = MapUtils.getTileNumberX(SearchRequest.ZOOM_TO_SEARCH_POI, l.getLongitude());
|
||||
double ty = MapUtils.getTileNumberY(SearchRequest.ZOOM_TO_SEARCH_POI, l.getLatitude());
|
||||
double topLeftX = Math.max(0, tx - coeff);
|
||||
double topLeftY = Math.max(0, ty - coeff);
|
||||
int max = (1 << SearchRequest.ZOOM_TO_SEARCH_POI) - 1;
|
||||
double bottomRightX = Math.min(max, tx + coeff);
|
||||
double bottomRightY = Math.min(max, ty + coeff);
|
||||
double pw = MapUtils.getPowZoom(31 - SearchRequest.ZOOM_TO_SEARCH_POI);
|
||||
cache1kmRect = new QuadRect(topLeftX * pw, topLeftY * pw, bottomRightX * pw, bottomRightY * pw);
|
||||
return cache1kmRect;
|
||||
}
|
||||
|
||||
|
||||
public Iterator<BinaryMapIndexReader> getOfflineIndexes(int meters, final SearchPhraseDataType dt) {
|
||||
List<BinaryMapIndexReader> list = indexes != null ? indexes : settings.getOfflineIndexes();
|
||||
final Iterator<BinaryMapIndexReader> lit = list.iterator();
|
||||
final QuadRect rect = meters > 0 ? getBBoxToSearch(meters) : null;
|
||||
return new Iterator<BinaryMapIndexReader>() {
|
||||
BinaryMapIndexReader next = null;
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
while (lit.hasNext()) {
|
||||
next = lit.next();
|
||||
if(rect != null) {
|
||||
if(dt == SearchPhraseDataType.POI) {
|
||||
if(next.containsPoiData((int)rect.left, (int)rect.right, (int)rect.top, (int)rect.bottom)) {
|
||||
return true;
|
||||
}
|
||||
} else if(dt == SearchPhraseDataType.ADDRESS) {
|
||||
if(next.containsAddressData((int)rect.left, (int)rect.right, (int)rect.top, (int)rect.bottom)) {
|
||||
return true;
|
||||
}
|
||||
} else if(dt == SearchPhraseDataType.ROUTING) {
|
||||
if(next.containsRouteData((int)rect.left, (int)rect.right, (int)rect.top, (int)rect.bottom, 15)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if(next.containsMapData((int)rect.left, (int)rect.right, (int)rect.top, (int)rect.bottom, 15)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BinaryMapIndexReader next() {
|
||||
return next;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
public List<BinaryMapIndexReader> getOfflineIndexes() {
|
||||
if(indexes != null) {
|
||||
return indexes;
|
||||
|
|
Loading…
Reference in a new issue