Los gráficos de TileProvider se inclinan en niveles de zoom más altos

Actualmente estoy jugando con TileProver en Android Maps API v2 y un poco atascado con el siguiente problema: los gráficos que pinto manualmente en Bitmap se inclinan significativamente en niveles de zoom más altos:

Gráficos está sesgado

Déjame explicar lo que estoy haciendo aquí. Tengo un número de puntos de LatLng y dibujo un círculo para cada punto en un mapa, así que usted zumba – punto permanece en la misma localización geográfica. Como se puede ver en la captura de pantalla, los círculos se ven bien en niveles de zoom inferiores, pero al empezar a hacer zoom en – los círculos se sesgaban.

Así es como se implementa:

 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); } } 

La lógica me dice que esto está sucediendo porque estoy traduciendo LatLng en la posición de la pantalla solamente para 1 azulejo (256×256 que es nivel 0 del zumbido) y entonces para traducir este punto de la pantalla a otros niveles del zumbido, necesito escalar mi mapa de bits y traducir A la posición apropiada. Al mismo tiempo, puesto que el mapa de bits se escala, necesito compensar el radio del círculo, así que divido el radio por el factor de escala. Así que en el nivel de zoom 19 mi factor de escala es ya 1572864, que es enorme. Es como mirar este círculo a través de una lupa enorme. Por eso tengo este efecto.

Así que supongo que la solución sería evitar el escalado de mapa de bits y escalar / traducir sólo coordenadas de pantalla. En este caso, mi radio del círculo será siempre el mismo y no se reducirá.

Desafortunadamente, la matemática de la matriz no es mi habilidad más fuerte, así que mi pregunta es – ¿cómo escalar / traducir el conjunto de puntos para el nivel de zoom arbitrario con un conjunto de puntos calculados para el nivel de zoom '0'?

La forma más sencilla de hacerlo sería tener diferentes instancias de proyección para cada nivel de zoom, pero como la traducción de GeoPoint -> ScreenPoint es bastante costosa, mantendría este enfoque como copia de seguridad y usaría algunas matemáticas sencillas para traducir la pantalla ya existente puntos.

NOTA Tenga en cuenta que necesito específicamente personalizado TileProvider ya que en la aplicación voy a dibujar azulejos mucho más complicados que sólo círculos. Así que la clase de Marker simple no va a funcionar para mí aquí

ACTUALIZACIÓN Aunque he descubierto cómo traducir puntos individuales y evitar el escalado de mapa de bits:

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

Todavía no sé cómo hacer esto con los objetos Path . No puedo traducir / escalar la ruta como lo haría con puntos individuales, así que todavía tengo que escalar mi mapa de bits que hace que los artefactos de dibujo de nuevo (ancho de trazo está sesgada en niveles de zoom más altos):

Introduzca aquí la descripción de la imagen

Y aquí hay un fragmento de código:

 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); } } 

Veo que estás usando el tileview de google, puedes intentar y considerar la biblioteca de mogarius que es un
Código abierto en github

Nunca lo probé antes, pero es compatible con la mayoría de las funcionalidades que necesitas (markers \ dots
Y dibujo de la trayectoria dinámica) fuera de la caja de modo que le ahorre el tiempo que gasta en hacer
Cálculos de matrices para upscaling y downscaling.
También hay un vídeo de demostración para algunos de los usos que hizo, y un gran javadoc que publicó.

  • Explicación de Android Xfermode
  • GlClear () toma demasiado tiempo - Android OpenGL ES 2
  • Ideas para la física para rodar dados
  • Uso de archivos SVG con libgdx
  • Android usando el generador de arte de dispositivo para capturas de pantalla
  • ¿Cómo afecta la profundidad de color y / o el nivel de compresión de las imágenes al rendimiento de la interfaz de usuario?
  • ¿Por qué la muestra de documentación se divide por 2 para calcular inSampleSize para la carga de mapa de bits?
  • ¿El android tiene * nativo * imagen de la ayuda SVG como recursos drenables (iconos)?
  • Rutas de dibujo y aceleración de hardware
  • ¿Hay algún diseñador Android Drawable?
  • Algoritmo Floodfill en Android
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.