package net.osmand.plus; import java.io.IOException; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import net.osmand.LogUtil; import net.osmand.binary.BinaryMapIndexReader; import net.osmand.binary.BinaryMapIndexReader.SearchRequest; import net.osmand.data.TransportRoute; import net.osmand.data.TransportStop; import net.osmand.osm.LatLon; import net.osmand.osm.MapUtils; import org.apache.commons.logging.Log; public class TransportIndexRepositoryBinary implements TransportIndexRepository { private static final Log log = LogUtil.getLog(TransportIndexRepositoryBinary.class); private final BinaryMapIndexReader file; protected List<TransportStop> cachedObjects = new ArrayList<TransportStop>(); protected double cTopLatitude; protected double cBottomLatitude; protected double cLeftLongitude; protected double cRightLongitude; private int cZoom; public TransportIndexRepositoryBinary(BinaryMapIndexReader file) { this.file = file; } @Override public boolean checkContains(double latitude, double longitude) { return file.containTransportData(latitude, longitude); } @Override public boolean checkContains(double topLatitude, double leftLongitude, double bottomLatitude, double rightLongitude) { return file.containTransportData(topLatitude, leftLongitude, bottomLatitude, rightLongitude); } public boolean checkCachedObjects(double topLatitude, double leftLongitude, double bottomLatitude, double rightLongitude, int zoom, List<TransportStop> toFill){ return checkCachedObjects(topLatitude, leftLongitude, bottomLatitude, rightLongitude, zoom, toFill, false); } public synchronized boolean checkCachedObjects(double topLatitude, double leftLongitude, double bottomLatitude, double rightLongitude, int zoom, List<TransportStop> toFill, boolean fillFound){ boolean inside = cTopLatitude >= topLatitude && cLeftLongitude <= leftLongitude && cRightLongitude >= rightLongitude && cBottomLatitude <= bottomLatitude && cZoom == zoom; boolean noNeedToSearch = inside; if((inside || fillFound) && toFill != null){ for(TransportStop a : cachedObjects){ LatLon location = a.getLocation(); if (location.getLatitude() <= topLatitude && location.getLongitude() >= leftLongitude && location.getLongitude() <= rightLongitude && location.getLatitude() >= bottomLatitude) { toFill.add(a); } } } return noNeedToSearch; } public List<TransportStop> searchTransportStops(double topLatitude, double leftLongitude, double bottomLatitude, double rightLongitude, int limit, List<TransportStop> stops) { long now = System.currentTimeMillis(); try { file.searchTransportIndex(BinaryMapIndexReader.buildSearchTransportRequest(MapUtils.get31TileNumberX(leftLongitude), MapUtils.get31TileNumberX(rightLongitude), MapUtils.get31TileNumberY(topLatitude), MapUtils.get31TileNumberY(bottomLatitude), limit, stops)); if (log.isDebugEnabled()) { log.debug(String.format("Search for %s done in %s ms found %s.", //$NON-NLS-1$ topLatitude + " " + leftLongitude, System.currentTimeMillis() - now, stops.size())); //$NON-NLS-1$ } } catch (IOException e) { log.error("Disk error ", e); //$NON-NLS-1$ } return stops; } /** * * @param stop * @param format * 0} - ref, {1} - type, {2} - name, {3} - name_en * @return null if something goes wrong */ public List<String> getRouteDescriptionsForStop(TransportStop stop, String format) { assert acceptTransportStop(stop); long now = System.currentTimeMillis(); MessageFormat f = new MessageFormat(format); List<String> res = new ArrayList<String>(); for(int r : stop.getReferencesToRoutes()) { try { TransportRoute route = file.getTransportRoute(r); if(route == null){ return null; } res.add(f.format(new String[] { route.getRef()+"", route.getType()+"", route.getName()+"", route.getEnName()+""})); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ } catch (IOException e) { log.error("Disk error ", e); //$NON-NLS-1$ } } if (log.isDebugEnabled()) { log.debug(String.format("Search for stop %s done in %s ms found %s.", //$NON-NLS-1$ stop.getId() + "", System.currentTimeMillis() - now, res.size())); //$NON-NLS-1$ } return res; } public void evaluateCachedTransportStops(double topLatitude, double leftLongitude, double bottomLatitude, double rightLongitude, int zoom, int limit, List<TransportStop> toFill) { cTopLatitude = topLatitude + (topLatitude - bottomLatitude); cBottomLatitude = bottomLatitude - (topLatitude - bottomLatitude); cLeftLongitude = leftLongitude - (rightLongitude - leftLongitude); cRightLongitude = rightLongitude + (rightLongitude - leftLongitude); cZoom = zoom; // first of all put all entities in temp list in order to not freeze other read threads ArrayList<TransportStop> tempList = new ArrayList<TransportStop>(); searchTransportStops(cTopLatitude, cLeftLongitude, cBottomLatitude, cRightLongitude, limit, tempList); synchronized (this) { cachedObjects.clear(); cachedObjects.addAll(tempList); } checkCachedObjects(topLatitude, leftLongitude, bottomLatitude, rightLongitude, cZoom, toFill); } public List<RouteInfoLocation> searchTransportRouteStops(double latitude, double longitude, LatLon locationToGo, int zoom) { long now = System.currentTimeMillis(); final LatLon loc = new LatLon(latitude, longitude); double tileNumberX = MapUtils.getTileNumberX(zoom, longitude); double tileNumberY = MapUtils.getTileNumberY(zoom, latitude); double topLatitude = MapUtils.getLatitudeFromTile(zoom, tileNumberY - 0.5); double bottomLatitude = MapUtils.getLatitudeFromTile(zoom, tileNumberY + 0.5); double leftLongitude = MapUtils.getLongitudeFromTile(zoom, tileNumberX - 0.5); double rightLongitude = MapUtils.getLongitudeFromTile(zoom, tileNumberX + 0.5); SearchRequest<TransportStop> req = BinaryMapIndexReader.buildSearchTransportRequest(MapUtils.get31TileNumberX(leftLongitude), MapUtils.get31TileNumberX(rightLongitude), MapUtils.get31TileNumberY(topLatitude), MapUtils.get31TileNumberY(bottomLatitude), -1, null); List<RouteInfoLocation> listRoutes = new ArrayList<RouteInfoLocation>(); try { List<TransportStop> stops = file.searchTransportIndex(req); Map<Long, RouteInfoLocation> registeredRoutes = new LinkedHashMap<Long, RouteInfoLocation>(); for (TransportStop s : stops) { for (int ref : s.getReferencesToRoutes()) { TransportRoute route = file.getTransportRoute(ref); for (int i = 0; i < 2; i++) { boolean direction = i == 0; List<TransportStop> stps = direction ? route.getForwardStops() : route.getBackwardStops(); // load only part while (!stps.isEmpty() && (stps.get(0).getId().longValue() != s.getId().longValue())) { stps.remove(0); } if (!stps.isEmpty()) { long idToPut = route.getId() << 1 + (direction ? 1 : 0); if (registeredRoutes.containsKey(idToPut)) { TransportStop st = registeredRoutes.get(idToPut).getStart(); if (MapUtils.getDistance(loc, st.getLocation()) < MapUtils.getDistance(loc, s.getLocation())) { continue; } } RouteInfoLocation r = new RouteInfoLocation(); r.setRoute(route); r.setStart(stps.get(0)); r.setDirection(direction); if (locationToGo != null) { int distToLoc = Integer.MAX_VALUE; for (TransportStop st : stps) { double ndist = MapUtils.getDistance(locationToGo, st.getLocation()); if (ndist < distToLoc) { distToLoc = (int) ndist; r.setStop(st); r.setDistToLocation(distToLoc); } } } registeredRoutes.put(idToPut, r); } } } } if (log.isDebugEnabled()) { log.debug(String.format("Search for routes done in %s ms found %s.", //$NON-NLS-1$ System.currentTimeMillis() - now, registeredRoutes.size())); } listRoutes = new ArrayList<RouteInfoLocation>(registeredRoutes.values()); if (locationToGo != null) { Collections.sort(listRoutes, new Comparator<RouteInfoLocation>() { @Override public int compare(RouteInfoLocation object1, RouteInfoLocation object2) { int x = (int) (MapUtils.getDistance(loc, object1.getStart().getLocation()) + object1.getDistToLocation()); int y = (int) (MapUtils.getDistance(loc, object2.getStart().getLocation()) + object2.getDistToLocation()); return x - y; } }); } else { Collections.sort(listRoutes, new Comparator<RouteInfoLocation>() { @Override public int compare(RouteInfoLocation object1, RouteInfoLocation object2) { return Double.compare(MapUtils.getDistance(loc, object1.getStart().getLocation()), MapUtils.getDistance(loc, object2 .getStart().getLocation())); } }); } } catch (IOException e) { log.error("Disk error", e); //$NON-NLS-1$ } return listRoutes; } @Override public boolean acceptTransportStop(TransportStop stop) { return file.transportStopBelongsTo(stop); } @Override public void close() { try { file.close(); } catch (IOException e) { e.printStackTrace(); } } }