package net.osmand.plus.views;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.util.DisplayMetrics;
import android.view.WindowManager;
import net.osmand.ResultMatcher;
import net.osmand.data.LatLon;
import net.osmand.data.PointDescription;
import net.osmand.data.QuadRect;
import net.osmand.data.QuadTree;
import net.osmand.data.RotatedTileBox;
import net.osmand.data.TransportRoute;
import net.osmand.data.TransportStop;
import net.osmand.osm.edit.Node;
import net.osmand.osm.edit.Way;
import net.osmand.plus.R;
import net.osmand.plus.activities.MapActivity;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.TreeSet;
import gnu.trove.list.array.TIntArrayList;
public class TransportStopsLayer extends OsmandMapLayer implements ContextMenuLayer.IContextMenuProvider {
private static final int startZoom = 12;
private static final int startZoomRoute = 10;
private final MapActivity mapActivity;
private OsmandMapTileView view;
private Paint paintIcon;
private Bitmap stopBus;
private Bitmap stopSmall;
private RenderingLineAttributes attrs;
private MapLayerData<List<TransportStop>> data;
private TransportRoute route = null;
private boolean showTransportStops;
private Path path;
public TransportStopsLayer(MapActivity mapActivity) {
this.mapActivity = mapActivity;
}
@SuppressWarnings("deprecation")
@Override
public void initLayer(final OsmandMapTileView view) {
this.view = view;
DisplayMetrics dm = new DisplayMetrics();
WindowManager wmgr = (WindowManager) view.getContext().getSystemService(Context.WINDOW_SERVICE);
wmgr.getDefaultDisplay().getMetrics(dm);
paintIcon = new Paint();
path = new Path();
stopBus = BitmapFactory.decodeResource(view.getResources(), R.drawable.map_transport_stop_bus);
stopSmall = BitmapFactory.decodeResource(view.getResources(), R.drawable.map_transport_stop_small);
attrs = new RenderingLineAttributes("transport_route");
attrs.defaultWidth = (int) (12 * view.getDensity());
attrs.defaultColor = view.getResources().getColor(R.color.transport_route_line);
data = new OsmandMapLayer.MapLayerData<List<TransportStop>>() {
{
ZOOM_THRESHOLD = 0;
}
@Override
public boolean isInterrupted() {
return super.isInterrupted();
}
@Override
public void layerOnPostExecute() {
view.refreshMap();
}
@Override
protected List<TransportStop> calculateResult(RotatedTileBox tileBox) {
QuadRect latLonBounds = tileBox.getLatLonBounds();
if (latLonBounds == null) {
return new ArrayList<>();
}
List<TransportStop> res = view.getApplication().getResourceManager().searchTransportSync(latLonBounds.top, latLonBounds.left,
latLonBounds.bottom, latLonBounds.right, new ResultMatcher<TransportStop>() {
@Override
public boolean publish(TransportStop object) {
return true;
}
@Override
public boolean isCancelled() {
return isInterrupted();
}
});
Collections.sort(res, new Comparator<TransportStop>() {
@Override
public int compare(TransportStop lhs, TransportStop rhs) {
return lhs.getId() < rhs.getId() ? -1 : (lhs.getId().longValue() == rhs.getId().longValue() ? 0 : 1);
}
});
return res;
}
};
}
public void getFromPoint(RotatedTileBox tb, PointF point, List<? super TransportStop> res,
List<TransportStop> objects) {
int ex = (int) point.x;
int ey = (int) point.y;
final int rp = getRadiusPoi(tb);
int radius = rp * 3 / 2;
try {
TreeSet<String> ms = new TreeSet<>();
for (int i = 0; i < objects.size(); i++) {
TransportStop n = objects.get(i);
if (n.getLocation() == null) {
continue;
}
int x = (int) tb.getPixXFromLatLon(n.getLocation().getLatitude(), n.getLocation().getLongitude());
int y = (int) tb.getPixYFromLatLon(n.getLocation().getLatitude(), n.getLocation().getLongitude());
if (Math.abs(x - ex) <= radius && Math.abs(y - ey) <= radius) {
if (!ms.add(n.getName())) {
// only unique names
continue;
}
radius = rp;
res.add(n);
}
}
} catch (IndexOutOfBoundsException e) {
// that's really rare case, but is much efficient than introduce synchronized block
}
}
public TransportRoute getRoute() {
return route;
}
public void setRoute(TransportRoute route) {
this.route = route;
}
public boolean isShowTransportStops() {
return showTransportStops;
}
public void setShowTransportStops(boolean showTransportStops) {
this.showTransportStops = showTransportStops;
}
public int getRadiusPoi(RotatedTileBox tb){
final double zoom = tb.getZoom();
int r;
if(zoom < startZoomRoute){
r = 0;
} else if(zoom <= 15){
r = 8;
} else if(zoom <= 16){
r = 10;
} else if(zoom <= 17){
r = 14;
} else {
r = 18;
}
return (int) (r * tb.getDensity());
}
@Override
public void onPrepareBufferImage(Canvas canvas, RotatedTileBox tb, DrawSettings settings) {
List<TransportStop> objects = null;
if (tb.getZoom() >= startZoomRoute) {
if (route != null) {
objects = route.getForwardStops();
attrs.updatePaints(view, settings, tb);
try {
path.reset();
List<Way> ws = route.getForwardWays();
if (ws != null) {
for (Way w : ws) {
TIntArrayList tx = new TIntArrayList();
TIntArrayList ty = new TIntArrayList();
for (int i = 0; i < w.getNodes().size(); i++) {
Node o = w.getNodes().get(i);
int x = (int) tb.getPixXFromLatLon(o.getLatitude(), o.getLongitude());
int y = (int) tb.getPixYFromLatLon(o.getLatitude(), o.getLongitude());
tx.add(x);
ty.add(y);
}
calculatePath(tb, tx, ty, path);
}
}
attrs.drawPath(canvas, path);
} catch (Exception e) {
// ignore
}
}
}
if (showTransportStops && tb.getZoom() >= startZoom && objects == null) {
data.queryNewData(tb);
objects = data.getResults();
}
if (objects != null) {
float iconSize = stopBus.getWidth() * 3 / 2.5f;
QuadTree<QuadRect> boundIntersections = initBoundIntersections(tb);
List<TransportStop> fullObjects = new ArrayList<>();
for (TransportStop o : objects) {
float x = tb.getPixXFromLatLon(o.getLocation().getLatitude(), o.getLocation().getLongitude());
float y = tb.getPixYFromLatLon(o.getLocation().getLatitude(), o.getLocation().getLongitude());
if (intersects(boundIntersections, x, y, iconSize, iconSize)) {
canvas.drawBitmap(stopSmall, x - stopSmall.getWidth() / 2, y - stopSmall.getHeight() / 2, paintIcon);
} else {
fullObjects.add(o);
}
}
for (TransportStop o : fullObjects) {
float x = tb.getPixXFromLatLon(o.getLocation().getLatitude(), o.getLocation().getLongitude());
float y = tb.getPixYFromLatLon(o.getLocation().getLatitude(), o.getLocation().getLongitude());
Bitmap b = stopBus;
canvas.drawBitmap(b, x - b.getWidth() / 2, y - b.getHeight() / 2, paintIcon);
}
}
}
@Override
public void onDraw(Canvas canvas, RotatedTileBox tb, DrawSettings settings) {
}
@Override
public void destroyLayer() {
}
@Override
public boolean drawInScreenPixels() {
return true;
}
@Override
public boolean onLongPressEvent(PointF point, RotatedTileBox tileBox) {
return false;
}
@Override
public PointDescription getObjectName(Object o) {
if(o instanceof TransportStop){
return new PointDescription(PointDescription.POINT_TYPE_TRANSPORT_STOP, mapActivity.getString(R.string.transport_Stop),
((TransportStop)o).getName());
}
return null;
}
@Override
public boolean disableSingleTap() {
return false;
}
@Override
public boolean disableLongPressOnMap() {
return false;
}
@Override
public boolean isObjectClickable(Object o) {
return false;
}
@Override
public void collectObjectsFromPoint(PointF point, RotatedTileBox tileBox, List<Object> res) {
if(tileBox.getZoom() >= startZoomRoute && route != null) {
getFromPoint(tileBox, point, res, route.getForwardStops());
} else if (tileBox.getZoom() >= startZoom && data.getResults() != null) {
getFromPoint(tileBox, point, res, data.getResults());
}
}
@Override
public LatLon getObjectLocation(Object o) {
if(o instanceof TransportStop){
return ((TransportStop)o).getLocation();
}
return null;
}
}