Графика TileProvider искажается при более высоких уровнях масштабирования

В настоящее время я играю с TileProver в Android Maps API v2, и у TileProver следующая проблема: графика, которую я рисую вручную в Bitmap, значительно искажается при более высоких уровнях масштабирования:

Графика перекошена

Позвольте мне объяснить, что я здесь делаю. У меня есть число точек LatLng, и я рисую круг для каждой точки на карте, так что вы увеличиваете точку останова в том же географическом местоположении. Как вы можете видеть на скриншоте, круги выглядят отлично на более низких уровнях масштабирования, но по мере того, как вы начинаете увеличивать масштаб, круги становятся искаженными.

Вот как это реализовано:

 package trickyandroid.com.locationtracking; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; import android.util.Log; import com.google.android.gms.maps.model.LatLng; import com.google.android.gms.maps.model.Tile; import com.google.android.gms.maps.model.TileProvider; import com.google.maps.android.geometry.Point; import com.google.maps.android.projection.SphericalMercatorProjection; import java.io.ByteArrayOutputStream; /** * Created by paveld on 8/8/14. */ public class CustomTileProvider implements TileProvider { private final int TILE_SIZE = 256; private int density = 1; private int tileSizeScaled; private Paint circlePaint; private SphericalMercatorProjection projection; private Point[] points; public CustomTileProvider(Context context) { density = 3; //hardcoded for now, but should be driven by DisplayMetrics.density tileSizeScaled = TILE_SIZE * density; projection = new SphericalMercatorProjection(TILE_SIZE); points = generatePoints(); circlePaint = new Paint(); circlePaint.setAntiAlias(true); circlePaint.setColor(0xFF000000); circlePaint.setStyle(Paint.Style.FILL); } private Point[] generatePoints() { Point[] points = new Point[6]; points[0] = projection.toPoint(new LatLng(47.603861, -122.333393)); points[1] = projection.toPoint(new LatLng(47.600389, -122.326741)); points[2] = projection.toPoint(new LatLng(47.598942, -122.318973)); points[3] = projection.toPoint(new LatLng(47.599000, -122.311549)); points[4] = projection.toPoint(new LatLng(47.601373, -122.301721)); points[5] = projection.toPoint(new LatLng(47.609764, -122.311850)); return points; } @Override public Tile getTile(int x, int y, int zoom) { Bitmap bitmap = Bitmap.createBitmap(tileSizeScaled, tileSizeScaled, Bitmap.Config.ARGB_8888); float scale = (float) (Math.pow(2, zoom) * density); Matrix m = new Matrix(); m.setScale(scale, scale); m.postTranslate(-x * tileSizeScaled, -y * tileSizeScaled); Canvas c = new Canvas(bitmap); c.setMatrix(m); for (Point p : points) { c.drawCircle((float) px, (float) py, 20 / scale, circlePaint); } return bitmapToTile(bitmap); } private Tile bitmapToTile(Bitmap bmp) { ByteArrayOutputStream stream = new ByteArrayOutputStream(); bmp.compress(Bitmap.CompressFormat.PNG, 100, stream); byte[] bitmapdata = stream.toByteArray(); return new Tile(tileSizeScaled, tileSizeScaled, bitmapdata); } } 

Логика говорит мне, что это происходит потому, что я переводил LatLng в позицию экрана только для 1 плитки (256×256, которая является уровнем масштабирования 0), а затем для перевода этой экранной точки на другие уровни масштабирования мне нужно масштабировать растровое изображение и переводить На соответствующую позицию. В то же время, поскольку битмап масштабируется, мне нужно компенсировать радиус окружности, поэтому я делю радиус по масштабному коэффициенту. Таким образом, на уровне масштабирования 19 мой масштабный коэффициент уже 1572864, который огромен. Это похоже на просмотр этого круга с помощью огромного увеличительного стекла. Вот почему у меня такой эффект.

Поэтому я предполагаю, что решение будет состоять в том, чтобы избежать масштабирования растрового изображения и масштабирования / перевода только экранных координат. В этом случае радиус круга всегда будет одинаковым и не будет уменьшаться.

К сожалению, математическая математика не является моим самым сильным умением, поэтому мой вопрос: как мне масштабировать / переводить множество точек для произвольного уровня масштабирования с набором точек, рассчитанных для уровня масштабирования «0»?

Самый простой способ сделать это – иметь разные экземпляры Projection для каждого уровня масштабирования, но поскольку GeoPoint -> ScreenPoint-перевод – довольно дорогостоящая операция, я бы сохранил этот подход в качестве резервной копии и использовал некоторую простую математику для перевода уже существующего экрана точки.

ПРИМЕЧАНИЕ. Обратите внимание, что мне нужен специальный пользовательский TileProvider поскольку в приложении я буду рисовать гораздо более сложные плитки, чем просто круги. Так что простой класс Marker не будет работать для меня здесь

ОБНОВЛЕНИЕ Несмотря на то, что я понял, как переводить отдельные точки и избегать масштабирования растрового изображения:

 c.drawCircle((float) px * scale - (x * tileSizeScaled), (float) py * scale - (y * tileSizeScaled), 20, circlePaint); 

Я до сих пор не знаю, как это сделать с объектами Path . Я не могу переводить / масштабировать путь, как вы делали бы это с отдельными очками, поэтому мне еще нужно масштабировать растровое изображение, которое снова вызывает рисование артефактов (ширина штриха искажается при более высоких уровнях масштабирования):

Введите описание изображения здесь

И вот фрагмент кода:

 package trickyandroid.com.locationtracking; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Path; import com.google.android.gms.maps.model.LatLng; import com.google.android.gms.maps.model.Tile; import com.google.android.gms.maps.model.TileProvider; import com.google.maps.android.geometry.Point; import com.google.maps.android.projection.SphericalMercatorProjection; import java.io.ByteArrayOutputStream; /** * Created by paveld on 8/8/14. */ public class CustomTileProvider implements TileProvider { private final int TILE_SIZE = 256; private int density = 1; private int tileSizeScaled; private SphericalMercatorProjection projection; private Point[] points; private Path path; private Paint pathPaint; public CustomTileProvider(Context context) { density = 3; //hardcoded for now, but should be driven by DisplayMetrics.density tileSizeScaled = TILE_SIZE * density; projection = new SphericalMercatorProjection(TILE_SIZE); points = generatePoints(); path = generatePath(points); pathPaint = new Paint(); pathPaint.setAntiAlias(true); pathPaint.setColor(0xFF000000); pathPaint.setStyle(Paint.Style.STROKE); pathPaint.setStrokeCap(Paint.Cap.ROUND); pathPaint.setStrokeJoin(Paint.Join.ROUND); } private Path generatePath(Point[] points) { Path path = new Path(); path.moveTo((float) points[0].x, (float) points[0].y); for (int i = 1; i < points.length; i++) { path.lineTo((float) points[i].x, (float) points[i].y); } return path; } private Point[] generatePoints() { Point[] points = new Point[10]; points[0] = projection.toPoint(new LatLng(47.603861, -122.333393)); points[1] = projection.toPoint(new LatLng(47.600389, -122.326741)); points[2] = projection.toPoint(new LatLng(47.598942, -122.318973)); points[3] = projection.toPoint(new LatLng(47.599000, -122.311549)); points[4] = projection.toPoint(new LatLng(47.601373, -122.301721)); points[5] = projection.toPoint(new LatLng(47.609764, -122.311850)); points[6] = projection.toPoint(new LatLng(47.599221, -122.311531)); points[7] = projection.toPoint(new LatLng(47.599663, -122.312410)); points[8] = projection.toPoint(new LatLng(47.598823, -122.312614)); points[9] = projection.toPoint(new LatLng(47.599959, -122.310651)); return points; } @Override public Tile getTile(int x, int y, int zoom) { Bitmap bitmap = Bitmap.createBitmap(tileSizeScaled, tileSizeScaled, Bitmap.Config.ARGB_8888); float scale = (float) (Math.pow(2, zoom) * density); Canvas c = new Canvas(bitmap); Matrix m = new Matrix(); m.setScale(scale, scale); m.postTranslate(-x * tileSizeScaled, -y * tileSizeScaled); c.setMatrix(m); pathPaint.setStrokeWidth(6 * density / scale); c.drawPath(path, pathPaint); return bitmapToTile(bitmap); } private Tile bitmapToTile(Bitmap bmp) { ByteArrayOutputStream stream = new ByteArrayOutputStream(); bmp.compress(Bitmap.CompressFormat.PNG, 100, stream); byte[] bitmapdata = stream.toByteArray(); return new Tile(tileSizeScaled, tileSizeScaled, bitmapdata); } } 

Я вижу, что вы используете tileview google, вы можете попробовать и рассмотреть библиотеку mogarius, которая является
С открытым исходным кодом на github

Я никогда не пробовал это раньше, но он поддерживает большинство функций, которые вам нужны (маркеры \ точки
И динамический рисунок пути) из коробки, так что вы сэкономите время, затрачиваемое на создание
Матричные вычисления для масштабирования и масштабирования.
Есть также демонстрационное видео для некоторых из его использования, и большой javadoc, который он опубликовал.