Add search poi along the path

This commit is contained in:
Victor Shcherb 2014-08-05 11:09:15 +02:00
parent 13b8190d19
commit 441b547a21
8 changed files with 295 additions and 29 deletions

View file

@ -1,31 +1,48 @@
package net.osmand.binary;
import gnu.trove.iterator.TIntIterator;
import gnu.trove.list.array.TIntArrayList;
import gnu.trove.map.TIntObjectMap;
import gnu.trove.map.hash.TIntObjectHashMap;
import gnu.trove.set.hash.TIntHashSet;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import net.osmand.Collator;
import net.osmand.CollatorStringMatcher;
import net.osmand.CollatorStringMatcher.StringMatcherMode;
import net.osmand.Location;
import net.osmand.OsmAndCollator;
import net.osmand.PlatformUtil;
import net.osmand.ResultMatcher;
import net.osmand.StringMatcher;
import net.osmand.binary.BinaryMapAddressReaderAdapter.AddressRegion;
import net.osmand.binary.BinaryMapAddressReaderAdapter.CitiesBlock;
import net.osmand.binary.BinaryMapIndexReader.SearchPoiTypeFilter;
import net.osmand.binary.BinaryMapIndexReader.SearchRequest;
import net.osmand.binary.BinaryMapPoiReaderAdapter.PoiRegion;
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteRegion;
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteSubregion;
@ -43,10 +60,16 @@ import net.osmand.data.MapObject;
import net.osmand.data.Street;
import net.osmand.data.TransportRoute;
import net.osmand.data.TransportStop;
import net.osmand.osm.edit.Way;
import net.osmand.util.Algorithms;
import net.osmand.util.MapUtils;
import org.apache.commons.logging.Log;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import com.google.protobuf.CodedInputStream;
import com.google.protobuf.WireFormat;
@ -1317,6 +1340,8 @@ public class BinaryMapIndexReader {
return buildSearchRequest(sleft, sright, stop, sbottom, zoom, searchFilter, null);
}
public static SearchRequest<BinaryMapDataObject> buildSearchRequest(int sleft, int sright, int stop, int sbottom, int zoom, SearchFilter searchFilter,
ResultMatcher<BinaryMapDataObject> resultMatcher){
SearchRequest<BinaryMapDataObject> request = new SearchRequest<BinaryMapDataObject>();
@ -1345,6 +1370,54 @@ public class BinaryMapIndexReader {
return request;
}
public static SearchRequest<Amenity> buildSearchPoiRequest(List<Location> route, double radius,
SearchPoiTypeFilter poiTypeFilter, ResultMatcher<Amenity> resultMatcher) {
SearchRequest<Amenity> request = new SearchRequest<Amenity>();
float coeff = (float) (radius / MapUtils.getTileDistanceWidth(SearchRequest.ZOOM_TO_SEARCH_POI));
TIntObjectHashMap<List<Location>> zooms = new TIntObjectHashMap<List<Location>>();
for(int i = 1; i < route.size(); i++) {
Location cr = route.get(i);
Location pr = route.get(i - 1);
double tx = MapUtils.getTileNumberX(SearchRequest.ZOOM_TO_SEARCH_POI, cr.getLongitude());
double ty = MapUtils.getTileNumberY(SearchRequest.ZOOM_TO_SEARCH_POI, cr.getLatitude());
double px = MapUtils.getTileNumberX(SearchRequest.ZOOM_TO_SEARCH_POI, pr.getLongitude());
double py = MapUtils.getTileNumberY(SearchRequest.ZOOM_TO_SEARCH_POI, pr.getLatitude());
double topLeftX = Math.min(tx, px) - coeff;
double topLeftY = Math.min(ty, py) - coeff;
double bottomRightX = Math.max(tx, px) + coeff;
double bottomRightY = Math.max(ty, py) + coeff;
for(int x = (int) topLeftX; x <= bottomRightX; x++) {
for(int y = (int) topLeftY; y <= bottomRightY; y++) {
int hash = (x << SearchRequest.ZOOM_TO_SEARCH_POI) + y;
if(!zooms.containsKey(hash)) {
zooms.put(hash, new LinkedList<Location>());
}
List<Location> ll = zooms.get(hash);
ll.add(pr);
ll.add(cr);
}
}
}
int sleft = 0, sright = Integer.MAX_VALUE, stop = 0, sbottom = Integer.MAX_VALUE;
for(int vl : zooms.keys()) {
int x = (vl >> SearchRequest.ZOOM_TO_SEARCH_POI) << (31 - SearchRequest.ZOOM_TO_SEARCH_POI);
int y = (vl & ((1 << SearchRequest.ZOOM_TO_SEARCH_POI) -1)) << (31 - SearchRequest.ZOOM_TO_SEARCH_POI);
sleft = Math.min(x, sleft);
stop = Math.min(y, stop);
sbottom = Math.max(y, sbottom);
sright = Math.max(x, sright);
}
request.radius = radius;
request.left = sleft;
request.right = sright;
request.top = stop;
request.bottom = sbottom;
request.tiles = zooms;
request.poiTypeFilter = poiTypeFilter;
request.resultMatcher = resultMatcher;
return request;
}
public static SearchRequest<Amenity> buildSearchPoiRequest(int sleft, int sright, int stop, int sbottom, int zoom,
SearchPoiTypeFilter poiTypeFilter, ResultMatcher<Amenity> matcher){
@ -1426,6 +1499,7 @@ public class BinaryMapIndexReader {
}
public static class SearchRequest<T> {
public final static int ZOOM_TO_SEARCH_POI = 16;
private List<T> searchResults = new ArrayList<T>();
private boolean land = false;
private boolean ocean = false;
@ -1443,6 +1517,11 @@ public class BinaryMapIndexReader {
int zoom = 15;
int limit = -1;
// search on the path
// stores tile of 16 index and pairs (even length always) of points intersecting tile
TIntObjectHashMap<List<Location>> tiles = null;
double radius = -1;
String nameQuery = null;
@ -1469,6 +1548,12 @@ public class BinaryMapIndexReader {
protected SearchRequest(){
}
public int getTileHashOnPath(double lat, double lon) {
int x = (int) MapUtils.getTileNumberX(SearchRequest.ZOOM_TO_SEARCH_POI, lon);
int y = (int) MapUtils.getTileNumberY(SearchRequest.ZOOM_TO_SEARCH_POI, lat);
return (x << SearchRequest.ZOOM_TO_SEARCH_POI) | y;
}
public boolean publish(T obj){
if(resultMatcher == null || resultMatcher.publish(obj)){
@ -1769,7 +1854,7 @@ public class BinaryMapIndexReader {
}
private static boolean testMapSearch = true;
private static boolean testMapSearch = false;
private static boolean testAddressSearch = false;
private static boolean testPoiSearch = false;
private static boolean testTransportSearch = false;
@ -1805,17 +1890,120 @@ public class BinaryMapIndexReader {
PoiRegion poiRegion = reader.getPoiIndexes().get(0);
testPoiSearch(reader, poiRegion);
testPoiSearchByName(reader);
testSearchOnthePath(reader);
}
println("MEMORY " + (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory())); //$NON-NLS-1$
println("Time " + (System.currentTimeMillis() - time)); //$NON-NLS-1$
}
private static void testSearchOnthePath(BinaryMapIndexReader reader) throws IOException {
float radius = 1000;
long now = System.currentTimeMillis();
println("Searching poi on the path...");
final List<Location> locations = readGPX(new File(
""));
SearchRequest<Amenity> req = buildSearchPoiRequest(locations, radius, new SearchPoiTypeFilter() {
@Override
public boolean accept(AmenityType type, String subcategory) {
if (type == AmenityType.SHOP && subcategory.contains("super")) {
return true;
}
return false;
}
}, null);
req.zoom = -1;
List<Amenity> results = reader.searchPoi(req);
int k = 0;
println("Search done in " + (System.currentTimeMillis() - now) + " ms ");
now = System.currentTimeMillis();
for (Amenity a : results) {
final float dds = dist(a.getLocation(), locations);
if (dds <= radius) {
println("+ " + a.getType() + " " + a.getSubType() + " Dist " + dds + " (=" + (float)a.getDeviateDistance() + ") " + a.getName() + " " + a.getLocation());
k++;
} else {
println(a.getType() + " " + a.getSubType() + " Dist " + dds + " " + a.getName() + " " + a.getLocation());
}
}
println("Filtered in " + (System.currentTimeMillis() - now) + "ms " + k + " of " + results.size());
}
private static float dist(LatLon l, List<Location> locations) {
float dist = Float.POSITIVE_INFINITY;
for(int i = 1; i < locations.size(); i++){
dist = Math.min(dist,(float) MapUtils.getOrthogonalDistance(l.getLatitude(), l.getLongitude(),
locations.get(i-1).getLatitude(), locations.get(i-1).getLongitude(),
locations.get(i).getLatitude(), locations.get(i).getLongitude()));
}
return dist;
}
private static Reader getUTF8Reader(InputStream f) throws IOException {
BufferedInputStream bis = new BufferedInputStream(f);
assert bis.markSupported();
bis.mark(3);
boolean reset = true;
byte[] t = new byte[3];
bis.read(t);
if (t[0] == ((byte) 0xef) && t[1] == ((byte) 0xbb) && t[2] == ((byte) 0xbf)) {
reset = false;
}
if (reset) {
bis.reset();
}
return new InputStreamReader(bis, "UTF-8");
}
private static List<Location> readGPX(File f) {
List<Location> res = new ArrayList<Location>();
try {
StringBuilder content = new StringBuilder();
BufferedReader reader = new BufferedReader(getUTF8Reader(new FileInputStream(f)));
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder dom = factory.newDocumentBuilder();
// {
// String s = null;
// boolean fist = true;
// while ((s = reader.readLine()) != null) {
// if (fist) {
// fist = false;
// }
// content.append(s).append("\n");
// }
// }
// Document doc = dom.parse(new InputSource(new StringReader(content.toString())));
Document doc = dom.parse(new InputSource(reader));
NodeList list = doc.getElementsByTagName("trkpt");
Way w = new Way(-1);
for (int i = 0; i < list.getLength(); i++) {
Element item = (Element) list.item(i);
try {
double lon = Double.parseDouble(item.getAttribute("lon"));
double lat = Double.parseDouble(item.getAttribute("lat"));
final Location o = new Location("");
o.setLatitude(lat);
o.setLongitude(lon);
res.add(o);
} catch (NumberFormatException e) {
}
}
} catch (IOException e) {
throw new RuntimeException(e);
} catch (ParserConfigurationException e) {
throw new RuntimeException(e);
} catch (SAXException e) {
throw new RuntimeException(e);
}
return res;
}
private static void testPoiSearchByName(BinaryMapIndexReader reader) throws IOException {
println("Searching by name...");
int tileZ = 31 - 14;
SearchRequest<Amenity> req = buildSearchPoiRequest(sleft/2+sright/2, stop/2+sbottom/2, "kol",
sleft - 1<<tileZ, sleft + 1<<tileZ, stop - 1<<tileZ, stop + 1<<tileZ, null);
SearchRequest<Amenity> req = buildSearchPoiRequest(0, 0, "roch",
0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE, null);
reader.searchPoiByName(req);
for (Amenity a : req.getSearchResults()) {
println(a.getType() + " " + a.getSubType() + " " + a.getName() + " " + a.getLocation());
@ -2107,5 +2295,7 @@ public class BinaryMapIndexReader {
routeAdapter.initRouteRegion(routeReg);
}
}
}

View file

@ -15,11 +15,13 @@ import java.util.List;
import net.osmand.Collator;
import net.osmand.CollatorStringMatcher;
import net.osmand.CollatorStringMatcher.StringMatcherMode;
import net.osmand.Location;
import net.osmand.PlatformUtil;
import net.osmand.binary.BinaryMapIndexReader.SearchRequest;
import net.osmand.binary.OsmandOdb.OsmAndPoiNameIndex.OsmAndPoiNameIndexData;
import net.osmand.data.Amenity;
import net.osmand.data.AmenityType;
import net.osmand.data.LatLon;
import net.osmand.util.Algorithms;
import net.osmand.util.MapUtils;
import net.sf.junidecode.Junidecode;
@ -588,6 +590,17 @@ public class BinaryMapPoiReaderAdapter {
}
}
private float dist(LatLon l, List<Location> locations) {
float dist = Float.POSITIVE_INFINITY;
// Special iterations because points stored by pairs!
for (int i = 1; i < locations.size(); i += 2) {
dist = Math.min(dist, (float) MapUtils.getOrthogonalDistance(
l.getLatitude(), l.getLongitude(),
locations.get(i - 1).getLatitude(), locations.get(i - 1).getLongitude(),
locations.get(i).getLatitude(), locations.get(i).getLongitude()));
}
return dist;
}
private Amenity readPoiPoint(int left31, int right31, int top31, int bottom31,
int px, int py, int zoom, SearchRequest<Amenity> req, PoiRegion region, boolean checkBounds) throws IOException {
Amenity am = null;
@ -609,6 +622,19 @@ public class BinaryMapPoiReaderAdapter {
am.setEnName(Junidecode.unidecode(am.getName()));
}
req.numberOfAcceptedObjects++;
if (req.radius > 0) {
LatLon loc = am.getLocation();
List<Location> locs = req.tiles.get(req.getTileHashOnPath(loc.getLatitude(), loc.getLongitude()));
if (locs == null) {
return null;
}
float d = dist(am.getLocation(), locs);
if (d > req.radius) {
return null;
} else {
am.setDeviateDistance(d);
}
}
return am;
case OsmandOdb.OsmAndPoiBoxDataAtom.DX_FIELD_NUMBER :
x = (codedIS.readSInt32() + (px << (24 - zoom))) << 7;
@ -793,16 +819,16 @@ public class BinaryMapPoiReaderAdapter {
case OsmandOdb.OsmAndPoiBox.SUBBOXES_FIELD_NUMBER: {
int x = dx + (px << (zoom - pzoom));
int y = dy + (py << (zoom - pzoom));
if(checkBox){
if (checkBox) {
int xL = x << (31 - zoom);
int xR = ((x + 1) << (31 - zoom)) - 1;
int yT = y << (31 - zoom);
int yB = ((y + 1) << (31 - zoom)) - 1;
// check intersection
if(left31 > xR || xL > right31 || bottom31 < yT || yB < top31){
if (left31 > xR || xL > right31 || bottom31 < yT || yB < top31) {
codedIS.skipRawBytes(codedIS.getBytesUntilLimit());
return false;
}
}
req.numberOfAcceptedSubtrees++;
checkBox = false;
}
@ -824,10 +850,20 @@ public class BinaryMapPoiReaderAdapter {
int x = dx + (px << (zoom - pzoom));
int y = dy + (py << (zoom - pzoom));
long l = ((((x << zoom) | y) << 5) | zoom);
offsetsMap.put(readInt(), l);
if(skipTiles != null && zoom >= zoomToSkip){
long val = ((((long) x) >> (zoom - zoomToSkip)) << zoomToSkip) | (((long) y) >> (zoom - zoomToSkip));
skipTiles.add(val);
boolean read = true;
if(req.tiles != null) {
int zx = x << (SearchRequest.ZOOM_TO_SEARCH_POI - zoom);
int zy = y << (SearchRequest.ZOOM_TO_SEARCH_POI - zoom);
read = req.tiles.contains((zx << SearchRequest.ZOOM_TO_SEARCH_POI) + zy);
}
int offset = readInt();
if (read) {
offsetsMap.put(offset, l);
if (skipTiles != null && zoom >= zoomToSkip) {
long val = ((((long) x) >> (zoom - zoomToSkip)) << zoomToSkip)
| (((long) y) >> (zoom - zoomToSkip));
skipTiles.add(val);
}
}
} break;
default:

View file

@ -18,6 +18,7 @@ public class Amenity extends MapObject {
// duplicate for fast access
private String openingHours;
private Map<String, String> additionalInfo;
private double deviateDistance; // for search on path
public Amenity(){
}
@ -62,6 +63,13 @@ public class Amenity extends MapObject {
openingHours = additionalInfo.get(OPENING_HOURS);
}
public double getDeviateDistance() {
return deviateDistance;
}
public void setDeviateDistance(double deviateDistance) {
this.deviateDistance = deviateDistance;
}
public void setAdditionalInfo(String tag, String value) {
if(this.additionalInfo == null){

View file

@ -220,6 +220,12 @@ public class MapUtils {
}
public static double getTileDistanceWidth(float zoom) {
LatLon ll = new LatLon(30, MapUtils.getLongitudeFromTile(zoom, 0));
LatLon ll2 = new LatLon(30, MapUtils.getLongitudeFromTile(zoom, 1));
return getDistance(ll, ll2) ;
}
public static double getLongitudeFromTile(float zoom, double x) {
return x / getPowZoom(zoom) * 360.0 - 180.0;
}

View file

@ -39,6 +39,7 @@ public class SettingsMonitoringActivity extends SettingsBaseActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
requestWindowFeature(Window.FEATURE_PROGRESS);
super.onCreate(savedInstanceState);
setSupportProgressBarIndeterminateVisibility(false);
getSupportActionBar().setTitle(R.string.monitoring_settings);

View file

@ -2,6 +2,7 @@ package net.osmand.plus.resources;
import java.util.List;
import net.osmand.Location;
import net.osmand.ResultMatcher;
import net.osmand.data.Amenity;
import net.osmand.plus.PoiFilter;
@ -20,5 +21,7 @@ public interface AmenityIndexRepository {
public List<Amenity> searchAmenities(int stop, int sleft, int sbottom, int sright, int zoom, PoiFilter filter, List<Amenity> amenities,
ResultMatcher<Amenity> matcher);
public List<Amenity> searchAmenitiesOnThePath(List<Location> locations, double radius, PoiFilter filter, ResultMatcher<Amenity> matcher);
}

View file

@ -6,6 +6,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import net.osmand.Location;
import net.osmand.PlatformUtil;
import net.osmand.ResultMatcher;
import net.osmand.binary.BinaryMapIndexReader;
@ -105,5 +106,30 @@ public class AmenityIndexRepositoryBinary implements AmenityIndexRepository {
}
return amenities;
}
@Override
public synchronized List<Amenity> searchAmenitiesOnThePath(List<Location> locations, double radius, final PoiFilter filter, ResultMatcher<Amenity> matcher) {
long now = System.currentTimeMillis();
SearchPoiTypeFilter poiTypeFilter = new SearchPoiTypeFilter(){
@Override
public boolean accept(AmenityType type, String subcategory) {
return filter.acceptTypeSubtype(type, subcategory);
}
};
List<Amenity> result = null;
SearchRequest<Amenity> req = BinaryMapIndexReader.buildSearchPoiRequest(locations, radius,
poiTypeFilter, filter == null ? matcher : filter.getResultMatcher(matcher));
try {
result = index.searchPoi(req);
} catch (IOException e) {
log.error("Error searching amenities", e); //$NON-NLS-1$
return result;
}
if (log.isDebugEnabled() && result != null) {
log.debug(String.format("Search done in %s ms found %s.", (System.currentTimeMillis() - now), result.size())); //$NON-NLS-1$
}
return result;
}
}

View file

@ -1,8 +1,6 @@
package net.osmand.plus.resources;
import gnu.trove.list.array.TIntArrayList;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@ -23,6 +21,7 @@ import net.osmand.AndroidUtils;
import net.osmand.GeoidAltitudeCorrection;
import net.osmand.IProgress;
import net.osmand.IndexConstants;
import net.osmand.Location;
import net.osmand.PlatformUtil;
import net.osmand.ResultMatcher;
import net.osmand.binary.BinaryMapIndexReader;
@ -736,7 +735,7 @@ public class ResourceManager {
if (index.checkContains(topLatitude, leftLongitude, bottomLatitude, rightLongitude)) {
index.searchAmenities(MapUtils.get31TileNumberY(topLatitude),
MapUtils.get31TileNumberX(leftLongitude), MapUtils.get31TileNumberY(bottomLatitude),
MapUtils.get31TileNumberX(rightLongitude), -1, filter, amenities,
MapUtils.get31TileNumberX(rightLongitude), zoom, filter, amenities,
new ResultMatcher<Amenity>() {
@Override
@ -814,21 +813,18 @@ public class ResourceManager {
return map;
}
public void searchAmenitiesOnTheArea(TIntArrayList tiles16z, PoiFilter filter, ResultMatcher<Amenity> results) {
if (tiles16z.size() > 0) {
int z = 16;
int x = tiles16z.get(0) >> z;
int y = tiles16z.get(0) & ((1 << z) - 1);
public void searchAmenitiesOnThePath(List<Location> locations, double radius, PoiFilter filter, ResultMatcher<Amenity> matcher) {
if (locations != null && locations.size() > 0) {
List<AmenityIndexRepository> repos = new ArrayList<AmenityIndexRepository>();
double topLatitude = MapUtils.getLatitudeFromTile(z, y);
double bottomLatitude = MapUtils.getLatitudeFromTile(z, y + 1);
double leftLongitude = MapUtils.getLongitudeFromTile(z, x);
double rightLongitude = MapUtils.getLongitudeFromTile(z, x + 1);
for (int k = 1; k < tiles16z.size(); k++) {
topLatitude = Math.max(topLatitude, MapUtils.getLatitudeFromTile(z, y));
bottomLatitude = Math.min(bottomLatitude, MapUtils.getLatitudeFromTile(z, y + 1));
leftLongitude = Math.min(leftLongitude, MapUtils.getLongitudeFromTile(z, x));
rightLongitude = Math.max(rightLongitude, MapUtils.getLongitudeFromTile(z, x + 1));
double topLatitude = locations.get(0).getLatitude();
double bottomLatitude = locations.get(0).getLatitude();
double leftLongitude = locations.get(0).getLongitude();
double rightLongitude = locations.get(0).getLongitude();
for(Location l : locations) {
topLatitude = Math.max(topLatitude, l.getLatitude());
bottomLatitude = Math.min(bottomLatitude, l.getLatitude());
leftLongitude = Math.min(leftLongitude, l.getLongitude());
rightLongitude = Math.max(rightLongitude, l.getLongitude());
}
for (AmenityIndexRepository index : amenityRepositories) {
if (index.checkContains(topLatitude, leftLongitude, bottomLatitude, rightLongitude)) {
@ -837,7 +833,7 @@ public class ResourceManager {
}
if (!repos.isEmpty()) {
for(AmenityIndexRepository r : repos) {
// r.searchAmenities(stop, sleft, sbottom, sright, zoom, filter, amenities, matcher)
r.searchAmenitiesOnThePath(locations, radius, filter, matcher);
}
}
}