/** Start animating the map towards the given point. */ public void animateTo(final double latitude, final double longitude) { final int x = mOsmv.getScrollX(); final int y = mOsmv.getScrollY(); final Point p = TileSystem.LatLongToPixelXY(latitude, longitude, mOsmv.getZoomLevel(), null); final int worldSize_2 = TileSystem.MapSize(mOsmv.getZoomLevel()) / 2; mOsmv .getScroller() .startScroll( x, y, p.x - worldSize_2 - x, p.y - worldSize_2 - y, ANIMATION_DURATION_DEFAULT); mOsmv.postInvalidate(); }
/** Set the map views to the given center. There will be no animation. */ @Override public void setCenter(final IGeoPoint point) { final Point p = TileSystem.LatLongToPixelXY( point.getLatitudeE6() / 1E6, point.getLongitudeE6() / 1E6, this.mOsmv.getZoomLevel(), null); final int worldSize_2 = TileSystem.MapSize(this.mOsmv.getZoomLevel()) / 2; this.mOsmv.scrollTo(p.x - worldSize_2, p.y - worldSize_2); }
@Override protected void draw(final Canvas canvas, final MapView mapView, final boolean shadow) { if (shadow) { return; } final int size = mPoints.size(); if (size < 2) { // nothing to paint return; } final Projection pj = mapView.getProjection(); // 180° in longitude in pixels, for avoiding lines > 180° in length final int halfMapSize = TileSystem.MapSize(mapView.getProjection().getZoomLevel()) / 2; // southern Limit of the map in Pixels, for detecting map crossing lines final int southLimit = pj.toPixelsFromMercator(0, halfMapSize * 2, null).y - 1; final int northLimit = southLimit - halfMapSize * 2 + 1; // calculate clipBounds in screen coordinates for removing "invisible" line segments final Rect clipBounds = new Rect(0, 0, mapView.getWidth() - 1, mapView.getHeight() - 1); // take into account map orientation: if (mapView.getMapOrientation() != 0.0f) GeometryMath.getBoundingBoxForRotatatedRectangle( clipBounds, mapView.getMapOrientation(), clipBounds); // area of visible line segments Rect pathExtension = new Rect( clipBounds); // used for decision: canvas.draw (fast) or bitmap draw (slow, avoiding // OpenGLRenderer problem) boolean lineVisible = false; // precompute new points to the intermediate projection. precomputePoints(pj); final Point projectedPoint0 = mPoints.get(0); // points from the points list final Point screenPoint0 = pj.toPixelsFromProjected(projectedPoint0, mTempPoint1); // points on screen if (screenPoint0.y < northLimit) screenPoint0.y += 2 * halfMapSize; Point projectedPoint1; Point screenPoint1; mPath.rewind(); for (int i = 1; i < size; i++) { // compute next points projectedPoint1 = mPoints.get(i); screenPoint1 = pj.toPixelsFromProjected(projectedPoint1, this.mTempPoint2); if (screenPoint1.y < northLimit) screenPoint1.y += 2 * halfMapSize; if (Math.abs(screenPoint1.x - screenPoint0.x) + Math.abs(screenPoint1.y - screenPoint0.y) <= 1 && screenPoint0.y != southLimit && screenPoint1.y != southLimit) { // do not skip points crossing maps! // skip this point, too close to previous point continue; } // check for lines exceeding 180° in longitude, cut line into two segments if ((Math.abs(screenPoint1.x - screenPoint0.x) > halfMapSize) // check for lines crossing the southern limit || (screenPoint1.y > southLimit) != (screenPoint0.y > southLimit)) { // handle x and y coordinates separately int x0 = screenPoint0.x; int y0 = screenPoint0.y; int x1 = screenPoint1.x; int y1 = screenPoint1.y; // first check x if (Math.abs(screenPoint1.x - screenPoint0.x) > halfMapSize) { // x has to be adjusted if (screenPoint1.x < mapView.getWidth() / 2) { // screenPoint1 is left of screenPoint0 x1 += halfMapSize * 2; // set x1 360° east of screenPoint1 x0 -= halfMapSize * 2; // set x0 360° west of screenPoint0 } else { x1 -= halfMapSize * 2; x0 += halfMapSize * 2; } } // now check y if ((screenPoint1.y > southLimit) != (screenPoint0.y > southLimit)) { // line is crossing from one map to the other if (screenPoint1.y > southLimit) { // screenPoint1 was switched to map below y1 -= halfMapSize * 2; // set y1 into northern map y0 += halfMapSize * 2; // set y0 into map below } else { y1 += halfMapSize * 2; y0 -= halfMapSize * 2; } } // create rectangle of line segment, ensure top < bottom and right < left to obtain valid // rectangle! mLineBounds.set( Math.min(screenPoint0.x, x1), Math.min(screenPoint0.y, y1), Math.max(screenPoint0.x, x1), Math.max(screenPoint0.y, y1)); // check whether this line segment is visible if (Rect.intersects(clipBounds, mLineBounds)) { mPath.moveTo(screenPoint0.x, screenPoint0.y); mPath.lineTo(x1, y1); pathExtension.union( mLineBounds); // caution! buggy for invalid rectangles (top > bottom or right > left) // ! lineVisible = true; } screenPoint0.x = x0; screenPoint0.y = y0; } // end of line break check // check, whether this line segment is visible, ensure top < bottom and right < left to // obtain valid rectangle! mLineBounds.set( Math.min(screenPoint0.x, screenPoint1.x), Math.min(screenPoint0.y, screenPoint1.y), Math.max(screenPoint0.x, screenPoint1.x), Math.max(screenPoint0.y, screenPoint1.y)); if (Rect.intersects(clipBounds, mLineBounds)) { mPath.moveTo(screenPoint0.x, screenPoint0.y); mPath.lineTo(screenPoint1.x, screenPoint1.y); pathExtension.union(mLineBounds); lineVisible = true; } // update starting point to next position screenPoint0.x = screenPoint1.x; screenPoint0.y = screenPoint1.y; } // send only visible lines to canvas, not segments outside screen area if (lineVisible) { // check, whether visible line segments cover less than 2048 pixels rectangle. if (Math.max(pathExtension.width(), pathExtension.height()) <= 2048) { // Use fast canvas.drawPath method. canvas.drawPath(mPath, mPaint); } else { // fixing W/OpenGLRenderer(29239): Shape path too large to be rendered into a texture. // This will occur, if memory exceeds 2040 pixels => Path will disappear. // Draw path into temporary bitmap in screen size, then send bitmap to screen canvas. // <application android:hardwareAccelerated="false" > does not fix the OpenGLRenderer // problem! // Line (80, 80) to (-80, 80) will disappear at zoomLevel 4, Line (90, 90) to (-90, 90) at // zoomLevel 3 final Rect dest = new Rect(0, 0, mapView.getWidth() - 1, mapView.getHeight() - 1); // dest.set(0, 0, mapView.getWidth()-1, mapView.getHeight()-1); // final Bitmap.Config conf = Bitmap.Config.ARGB_8888; // 8 bit Aplpha, RGB if (mBmp == null || mBmp.getWidth() != mapView.getWidth() || mBmp.getHeight() != mapView.getHeight()) { // create Bitmap only once if needed mBmp = Bitmap.createBitmap( mapView.getWidth(), mapView.getHeight(), Bitmap.Config.ARGB_8888); // this most time consuming step, app. 30 ms! } else mBmp.eraseColor( Color .TRANSPARENT); // recycle bitmap and erase the old bitmap, much faster 0.6 .. 0.9 // ms final Canvas bmpcanvas = new Canvas(mBmp); // Draw path to bitmap according to actual screen rotation if (mapView.getMapOrientation() != 0.0f) bmpcanvas.rotate( mapView.getMapOrientation(), mapView.getWidth() / 2, mapView.getHeight() / 2); bmpcanvas.drawPath(mPath, mPaint); // Draw bitmap according to actual screen rotation, derotate canvas if necessary if (mapView.getMapOrientation() != 0.0f) canvas.rotate( -mapView.getMapOrientation(), mapView.getWidth() / 2, mapView.getHeight() / 2); canvas.drawBitmap(mBmp, dest, dest, null); // restore canvas rotation if necessary if (mapView.getMapOrientation() != 0.0f) canvas.rotate( mapView.getMapOrientation(), mapView.getWidth() / 2, mapView.getHeight() / 2); } } }