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:
- Cómo dibujar con framebuffer en Android?
- Tabla Dinámica de Android
- Trazado gráfico dinámico con openGL en Android
- Android AvoidXfermode está obsoleto desde la API 16, ¿hay un reemplazo?
- Cómo averiguar la escala de una matriz de gráficos en Android
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):
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); } }
- ¿Hay un cargador de nueve parches para el iPhone?
- Android Bitmap.createBitmap () rellena montón
- Android: rotar recorta las esquinas de la forma
- ¿Cómo iniciar la programación de juegos sin un artista gráfico?
- Cómo utilizar las cámaras LibGDX con Box2D Debug Renderers
- ¿Es posible pellizcar un gradiente estirable?
- Dibujar mapa de bits en el clip actual en el lienzo con borde (Pintura)
- ¿Hay una manera de dibujar gráficos y gráficos en Android?
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ó.
- Recuperar la imagen de mysql-php (android)
- Cómo detectar si el lanzador admite la rotación de la pantalla principal