package net.osmand.plus;
import gnu.trove.list.array.TIntArrayList;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import net.osmand.Algoritms;
import net.osmand.LogUtil;
import net.osmand.binary.BinaryIndexPart;
import net.osmand.binary.BinaryMapDataObject;
import net.osmand.binary.BinaryMapIndexReader;
import net.osmand.binary.BinaryMapIndexReader.MapIndex;
import net.osmand.binary.BinaryMapIndexReader.MapRoot;
import net.osmand.binary.BinaryMapIndexReader.SearchFilter;
import net.osmand.binary.BinaryMapIndexReader.SearchRequest;
import net.osmand.binary.BinaryMapIndexReader.TagValuePair;
import net.osmand.data.Amenity;
import net.osmand.data.AmenityType;
import net.osmand.osm.LatLon;
import net.osmand.osm.MapRenderingTypes;
import net.osmand.osm.MapUtils;
import net.sf.junidecode.Junidecode;
import org.apache.commons.logging.Log;
public class AmenityIndexRepositoryBinary implements AmenityIndexRepository {
private final static Log log = LogUtil.getLog(AmenityIndexRepositoryBinary.class);
private final BinaryMapIndexReader index;
public AmenityIndexRepositoryBinary(BinaryMapIndexReader index) {
this.index = index;
}
@Override
public void close() {
try {
index.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public boolean checkContains(double latitude, double longitude) {
int x = MapUtils.get31TileNumberX(longitude);
int y = MapUtils.get31TileNumberY(latitude);
for(BinaryIndexPart i : index.getIndexes()){
if(i instanceof MapIndex){
List<MapRoot> rs = ((MapIndex) i).getRoots();
if(!rs.isEmpty()){
MapRoot rt = rs.get(0);
if(rt.getLeft() <= x && rt.getRight() >= x &&
rt.getTop() <= y && rt.getBottom() >= y){
return true;
}
}
}
}
return false;
}
@Override
public boolean checkContains(double topLatitude, double leftLongitude, double bottomLatitude, double rightLongitude) {
int leftX = MapUtils.get31TileNumberX(leftLongitude);
int rightX = MapUtils.get31TileNumberX(rightLongitude);
int bottomY = MapUtils.get31TileNumberY(bottomLatitude);
int topY = MapUtils.get31TileNumberY(topLatitude);
for(BinaryIndexPart i : index.getIndexes()){
if(i instanceof MapIndex){
List<MapRoot> rs = ((MapIndex) i).getRoots();
if(!rs.isEmpty()){
MapRoot rt = rs.get(0);
if(rightX < rt.getLeft() || leftX > rt.getRight()){
continue;
}
if(topY > rt.getBottom() || bottomY < rt.getTop()){
continue;
}
return true;
}
}
}
return false;
}
@Override
public List<Amenity> searchAmenities(double topLatitude, double leftLongitude, double bottomLatitude, double rightLongitude, int limit,
final PoiFilter filter, final List<Amenity> amenities) {
long now = System.currentTimeMillis();
int sleft = MapUtils.get31TileNumberX(leftLongitude);
int sright = MapUtils.get31TileNumberX(rightLongitude);
int sbottom = MapUtils.get31TileNumberY(bottomLatitude);
int stop = MapUtils.get31TileNumberY(topLatitude);
SearchRequest<BinaryMapDataObject> req = BinaryMapIndexReader.buildSearchRequest(sleft, sright, stop, sbottom, 16);
req.setSearchFilter(new SearchFilter(){
@Override
public boolean accept(TIntArrayList types, MapIndex root) {
for (int j = 0; j < types.size(); j++) {
int wholeType = types.get(j);
TagValuePair pair = root.decodeType(wholeType);
if (pair != null) {
AmenityType type = MapRenderingTypes.getAmenityType(pair.tag, pair.value);
if (type != null) {
if(filter.acceptTypeSubtype(type, MapRenderingTypes.getAmenitySubtype(pair.tag, pair.value))){
return true;
}
}
}
}
return false;
}
});
try {
index.searchMapIndex(req);
for(BinaryMapDataObject o : req.getSearchResults()){
if(o.getPointsLength() == 0){
continue;
}
int xTile = 0;
int yTile = 0;
for(int i=0; i<o.getPointsLength();i++){
xTile += o.getPoint31XTile(i);
yTile += o.getPoint31YTile(i);
}
double lat = MapUtils.get31LatitudeY(yTile/o.getPointsLength());
double lon = MapUtils.get31LongitudeX(xTile/o.getPointsLength());
for (int j = 0; j < o.getTypes().length; j++) {
TagValuePair pair = o.getMapIndex().decodeType(o.getTypes()[j]);
if(pair != null){
Amenity am = new Amenity();
am.setId(o.getId());
am.setLocation(lat, lon);
am.setName(o.getName());
am.setEnName(Junidecode.unidecode(am.getName()));
AmenityType type = MapRenderingTypes.getAmenityType(pair.tag, pair.value);
String subtype = MapRenderingTypes.getAmenitySubtype(pair.tag, pair.value);
am.setType(type);
am.setSubType(subtype);
am.setOpeningHours(null);
am.setPhone(null);
am.setSite(null);
amenities.add(am);
break;
}
}
}
} catch (IOException e) {
log.error("Error searching amenities", e); //$NON-NLS-1$
return amenities;
}
if (log.isDebugEnabled()) {
log.debug(String.format("Search for %s done in %s ms found %s.", //$NON-NLS-1$
topLatitude + " " + leftLongitude, System.currentTimeMillis() - now, amenities.size())); //$NON-NLS-1$
}
return amenities;
}
// Work with cache (for map copied from AmenityIndexRepositoryOdb)
private String cFilterId;
protected List<Amenity> cachedObjects = new ArrayList<Amenity>();
protected double cTopLatitude;
protected double cBottomLatitude;
protected double cLeftLongitude;
protected double cRightLongitude;
protected int cZoom;
public synchronized boolean checkCachedAmenities(double topLatitude, double leftLongitude, double bottomLatitude, double rightLongitude,
int zoom, String filterId, List<Amenity> toFill, boolean fillFound){
boolean inside = cTopLatitude >= topLatitude && cLeftLongitude <= leftLongitude && cRightLongitude >= rightLongitude
&& cBottomLatitude <= bottomLatitude && zoom == cZoom;
boolean noNeedToSearch = inside && Algoritms.objectEquals(filterId, cFilterId);
if((inside || fillFound) && toFill != null && Algoritms.objectEquals(filterId, cFilterId)){
for(Amenity a : cachedObjects){
LatLon location = a.getLocation();
if (location.getLatitude() <= topLatitude && location.getLongitude() >= leftLongitude && location.getLongitude() <= rightLongitude
&& location.getLatitude() >= bottomLatitude) {
toFill.add(a);
}
}
}
return noNeedToSearch;
}
@Override
public void clearCache() {
cachedObjects.clear();
cTopLatitude = 0;
cBottomLatitude = 0;
cRightLongitude = 0;
cLeftLongitude = 0;
cZoom = 0;
cFilterId = null;
}
@Override
public void evaluateCachedAmenities(double topLatitude, double leftLongitude, double bottomLatitude, double rightLongitude, int zoom,
int limitPoi, PoiFilter filter, List<Amenity> toFill) {
cTopLatitude = topLatitude + (topLatitude - bottomLatitude);
cBottomLatitude = bottomLatitude - (topLatitude - bottomLatitude);
cLeftLongitude = leftLongitude - (rightLongitude - leftLongitude);
cRightLongitude = rightLongitude + (rightLongitude - leftLongitude);
cFilterId = filter == null ? null : filter.getFilterId();
cZoom = zoom;
// first of all put all entities in temp list in order to not freeze other read threads
ArrayList<Amenity> tempList = new ArrayList<Amenity>();
searchAmenities(cTopLatitude, cLeftLongitude, cBottomLatitude, cRightLongitude, limitPoi, filter, tempList);
synchronized (this) {
cachedObjects.clear();
cachedObjects.addAll(tempList);
}
checkCachedAmenities(topLatitude, leftLongitude, bottomLatitude, rightLongitude, cZoom, filter.getFilterId(), toFill, true);
}
}