valor not lng latlngbounds latlng lat google found error convert cannot android android-maps-v2

android - not - marker get lat lng



Desplazamiento del centro del MapFragment para una animaciĆ³n moviendo tanto el lat/lng objetivo como el nivel de zoom (9)

Tengo una IU que tiene un MapFragment con una Vista transparente superpuesta encima. El mapa ocupa toda la pantalla, mientras que la Vista es solo el tercio izquierdo de la pantalla. Como resultado, el "centro" predeterminado del mapa está desactivado. Cuando alguien hace clic en un marcador, quiero centrar ese marcador en el área totalmente visible de MapFragment (no en el centro del MapFragment).

Como esto es difícil de describir con palabras, permítanme usar algunas imágenes. Supongamos que así es como se ve mi UI:

Cuando el usuario hace clic en un marcador, quiero centrarlo y acercarlo para verlo más cerca. Sin ningún ajuste, esto es lo que obtendrá:

Lo que quiero es que el marcador esté centrado, pero en el espacio a la derecha, así:

Es muy fácil lograr esta hazaña si no cambias el nivel de zoom con la proyección del mapa:

// Offset the target latitude/longitude by preset x/y ints LatLng target = new LatLng(latitude, longitude); Projection projection = getMap().getProjection(); Point screenLocation = projection.toScreenLocation(target); screenLocation.x += offsetX; screenLocation.y += offsetY; LatLng offsetTarget = projection.fromScreenLocation(screenLocation); // Animate to the calculated lat/lng getMap().animateCamera(CameraUpdateFactory.newLatLng(offsetTarget));

Sin embargo, si está cambiando el nivel de zoom al mismo tiempo, los cálculos anteriores no funcionan (dado que las compensaciones lat / lng cambian a diferentes niveles de zoom).

Permítanme ver una lista de intentos de solución:

  • Cambiar el nivel de zoom rápidamente, hacer los cálculos, volver a la posición original de la cámara y luego animar. Lamentablemente, el cambio repentino de la cámara (aunque solo sea por una fracción de segundo) es desafortunadamente muy obvio y me gustaría evitar el parpadeo.

  • Superponiendo dos MapFragments uno encima del otro, uno hace los cálculos mientras que el otro muestra. Descubrí que los MapFragments no están diseñados para superponerse unos a otros (hay errores inevitables en esta ruta).

  • Modificando la x / y de la ubicación de la pantalla por la diferencia en el nivel de zoom al cuadrado. Teóricamente, esto debería funcionar, pero siempre está apagado bastante (~ .1 latitud / longitud, que es suficiente para estar muy lejos).

¿Hay alguna manera de calcular el offsetTarget incluso con el cambio de nivel de zoom?


Editar: El código siguiente es para los mapas v1 en desuso. Esta respuesta SO indica cómo realizar acciones similares en v2: Cómo obtener latitud / longitud span en Google Map V2 para Android

Los métodos clave que necesita deben funcionar en long / lat y porcentajes en lugar de píxeles. En este momento, la vista de mapa se enfocará alrededor del centro del mapa y luego se moverá para ''recentrar'' el pasador en el área expuesta. Puede obtener el ancho después del zoom en grados, factorizar algunos sesgos de pantalla y luego volver a centrar el mapa. Aquí hay una captura de pantalla de muestra del código siguiente en acción después de presionar el ''+'': antes después Código principal:

@Override public void onZoom(boolean zoomIn) { // TODO Auto-generated method stub controller.setZoom(zoomIn ? map.getZoomLevel()+1 : map.getZoomLevel()-1); int bias = (int) (map.getLatitudeSpan()* 1.0/3.0); // a fraction of your choosing map.getLongitudeSpan(); controller.animateTo(new GeoPoint(yourLocation.getLatitudeE6(),yourLocation.getLongitudeE6()-bias)); }

Todo el código:

package com.example.testmapview; import com.google.android.maps.GeoPoint; import com.google.android.maps.ItemizedOverlay; import com.google.android.maps.MapActivity; import com.google.android.maps.MapController; import com.google.android.maps.MapView; import android.os.Bundle; import android.app.Activity; import android.graphics.drawable.Drawable; import android.view.Menu; import android.widget.ZoomButtonsController; import android.widget.ZoomButtonsController.OnZoomListener; public class MainActivity extends MapActivity { MapView map; GeoPoint yourLocation; MapController controller; ZoomButtonsController zoomButtons; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); map=(MapView)findViewById(R.id.mapview); map.setBuiltInZoomControls(true); controller = map.getController(); zoomButtons = map.getZoomButtonsController(); zoomButtons.setOnZoomListener(new OnZoomListener(){ @Override public void onVisibilityChanged(boolean arg0) { // TODO Auto-generated method stub } @Override public void onZoom(boolean zoomIn) { // TODO Auto-generated method stub controller.setZoom(zoomIn ? map.getZoomLevel()+1 : map.getZoomLevel()-1); int bias = (int) (map.getLatitudeSpan()* 1.0/3.0); // a fraction of your choosing map.getLongitudeSpan(); controller.animateTo(new GeoPoint(yourLocation.getLatitudeE6(),yourLocation.getLongitudeE6()-bias)); } }); //Dropping a pin, setting center Drawable marker=getResources().getDrawable(android.R.drawable.star_big_on); MyItemizedOverlay myItemizedOverlay = new MyItemizedOverlay(marker); map.getOverlays().add(myItemizedOverlay); yourLocation = new GeoPoint(0, 0); controller.setCenter(yourLocation); myItemizedOverlay.addItem(yourLocation, "myPoint1", "myPoint1"); controller.setZoom(5); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.activity_main, menu); return true; } @Override protected boolean isRouteDisplayed() { // TODO Auto-generated method stub return false; } }

y un diseño básico:

<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.testmapview" android:versionCode="1" android:versionName="1.0" > <meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="YOURKEYHERE:)"/> <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="16" /> <uses-permission android:name="android.permission.INTERNET" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <uses-library android:name="com.google.android.maps"/> <activity android:name="com.example.testmapview.MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>

Si esto no responde completamente a su pregunta, hágamelo saber, ya que este fue un experimento divertido para jugar.


El material de proyección de la API no es tan útil, me encontré con el mismo problema y obtuve el mío.

public class SphericalMercatorProjection { public static PointD latLngToWorldXY(LatLng latLng, double zoomLevel) { final double worldWidthPixels = toWorldWidthPixels(zoomLevel); final double x = latLng.longitude / 360 + .5; final double siny = Math.sin(Math.toRadians(latLng.latitude)); final double y = 0.5 * Math.log((1 + siny) / (1 - siny)) / -(2 * Math.PI) + .5; return new PointD(x * worldWidthPixels, y * worldWidthPixels); } public static LatLng worldXYToLatLng(PointD point, double zoomLevel) { final double worldWidthPixels = toWorldWidthPixels(zoomLevel); final double x = point.x / worldWidthPixels - 0.5; final double lng = x * 360; double y = .5 - (point.y / worldWidthPixels); final double lat = 90 - Math.toDegrees(Math.atan(Math.exp(-y * 2 * Math.PI)) * 2); return new LatLng(lat, lng); } private static double toWorldWidthPixels(double zoomLevel) { return 256 * Math.pow(2, zoomLevel); } }

Usando este código, puede transformar el mapa alrededor de un nuevo objetivo (es decir, no el centro). Opté por usar un sistema de puntos de anclaje, donde (0.5, 0.5) es el predeterminado y representa el centro de la pantalla. (0,0) está arriba a la izquierda y (1, 1) está abajo a la izquierda.

private LatLng getOffsetLocation(LatLng location, double zoom) { Point size = getSize(); PointD anchorOffset = new PointD(size.x * (0.5 - anchor.x), size.y * (0.5 - anchor.y)); PointD screenCenterWorldXY = SphericalMercatorProjection.latLngToWorldXY(location, zoom); PointD newScreenCenterWorldXY = new PointD(screenCenterWorldXY.x + anchorOffset.x, screenCenterWorldXY.y + anchorOffset.y); newScreenCenterWorldXY.rotate(screenCenterWorldXY, cameraPosition.bearing); return SphericalMercatorProjection.worldXYToLatLng(newScreenCenterWorldXY, zoom); }

Básicamente, usas la proyección para obtener la coordenada XY mundial, luego compensas ese punto y lo vuelves a convertir en LatLng. Luego puede pasar esto a su mapa. PointD es un tipo simple que contiene x, y como dobles y también hace rotación.

public class PointD { public double x; public double y; public PointD(double x, double y) { this.x = x; this.y = y; } public void rotate(PointD origin, float angleDeg) { double rotationRadians = Math.toRadians(angleDeg); this.x -= origin.x; this.y -= origin.y; double rotatedX = this.x * Math.cos(rotationRadians) - this.y * Math.sin(rotationRadians); double rotatedY = this.x * Math.sin(rotationRadians) + this.y * Math.cos(rotationRadians); this.x = rotatedX; this.y = rotatedY; this.x += origin.x; this.y += origin.y; } }

Tenga en cuenta que si está actualizando el rumbo y la ubicación del mapa al mismo tiempo, es importante que use el rumbo nuevo en getOffsetLocation.


Encontré una solución que realmente se ajusta al problema. La solución es calcular el centro del desplazamiento en el zoom requerido desde el desplazamiento del original, y luego animar la cámara del mapa. Para esto, primero mueva la cámara del mapa al zoom deseado, calcule el desplazamiento para ese nivel de zoom y luego restaure el zoom original. Después de calcular el nuevo centro, podemos hacer la animación con CameraUpdateFactory.newLatLngZoom.

¡Espero que esto ayude!

private void animateLatLngZoom(LatLng latlng, int reqZoom, int offsetX, int offsetY) { // Save current zoom float originalZoom = mMap.getCameraPosition().zoom; // Move temporarily camera zoom mMap.moveCamera(CameraUpdateFactory.zoomTo(reqZoom)); Point pointInScreen = mMap.getProjection().toScreenLocation(latlng); Point newPoint = new Point(); newPoint.x = pointInScreen.x + offsetX; newPoint.y = pointInScreen.y + offsetY; LatLng newCenterLatLng = mMap.getProjection().fromScreenLocation(newPoint); // Restore original zoom mMap.moveCamera(CameraUpdateFactory.zoomTo(originalZoom)); // Animate a camera with new latlng center and required zoom. mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(newCenterLatLng, reqZoom)); }


Estaba lidiando con la misma tarea y finalicé con la solución similar como última opción:

Modificando la x / y de la ubicación de la pantalla por la diferencia en el nivel de zoom al cuadrado. Teóricamente, esto debería funcionar, pero siempre está apagado bastante (~ .1 latitud / longitud, que es suficiente para estar muy lejos).

Pero en lugar de dividir el desplazamiento del punto en 2<<zoomDifference Estoy calculando el desplazamiento como las diferencias entre los valores de longitud y latitud. Son double , por lo que me da la precisión suficiente. Intente convertir sus desplazamientos en lat / lng, no su ubicación en píxeles y haga el mismo cálculo.

Editado

Finalmente he logrado resolverlo de otra manera (la más apropiada, como creo). Map API v2 tiene parámetros de relleno . Simplemente configúralo para que la cámara tome todas las partes no ocultas de MapView . Entonces la cámara reflejaría el centro de la región acolchada después de animateCamera()


La manera más fácil sería encadenar animaciones de cámara. Primero, amplía normalmente su imagen n. ° 2 usando animateCamera con 3 parámetros y en CancellableCallback.onFinish los cálculos de proyección y anima utilizando su código para cambiar de centro.

Creo (no lo probé) que se verá muy bien si establece algunos buenos valores para la duración de la animación para ambas animaciones. Quizás la segunda animación debería ser mucho más rápida que la primera. Además, si su vista transparente superpuesta no está visible cuando no se selecciona ningún marcador, un buen UX sería animarlo desde la izquierda durante la segunda animación.

La forma más difícil sería implementar su propia Proyección esférica de Mercator, como la que Maps API v2 usa internamente. Si solo necesitas una compensación de longitud, no debería ser tan difícil. Si sus usuarios pueden cambiar el rumbo y no desea restablecerlo a 0 al hacer zoom, también necesita compensación de latitud, y esto probablemente será un poco más complejo de implementar.

También creo que sería bueno tener la funcionalidad de Maps API v2, donde puedes obtener una proyección para cualquier CameraPosition y no solo actual, por lo que es posible que desees enviar una solicitud de función aquí: http://code.google.com/p/gmaps-api-issues/issues/list?can=2&q=apitype=Android2 . Podrías usarlo fácilmente en tu código para el efecto que quieras.


La versión más reciente de la biblioteca de servicios de reproducción agrega una función setPadding() a GoogleMap . Este relleno proyectará todos los movimientos de la cámara al centro del mapa desplazado. Este documento describe con más detalle cómo se comporta el relleno del mapa.

La pregunta originalmente planteada aquí ahora puede resolverse simplemente agregando relleno izquierdo equivalente al ancho del diseño del lado izquierdo.

mMap.setPadding(leftPx, topPx, rightPx, bottomPx);


Para establecer un marcador, utilizamos Overlays en Android, en la clase de superposición ==> en el método draw coloca esta cosa

boundCenter (marcador);

y en constructor también menciono así:

public xxxxxxOverlay(Drawable marker, ImageView dragImage, MapView map) { super(boundCenter(marker)); . . . . . }


Resolví el problema calculando un vector de distancia utilizando los valores LatLng de dos puntos fijos en el mapa, luego creé otro LatLng usando la posición Marker, este vector y un valor de relación predefinido. Hay muchos objetos involucrados en este, sin embargo, no depende del nivel de zoom actual y la rotación del mapa, ¡y podría funcionar tan bien para ti!

// Just a helper function to obtain the device''s display size in px Point displaySize = UIUtil.getDisplaySize(getActivity().getWindowManager().getDefaultDisplay()); // The two fixed points: Bottom end of the map & Top end of the map, // both centered horizontally (because I want to offset the camera vertically... // if you want to offset it to the right, for instance, use the left & right ends) Point up = new Point(displaySize.x / 2, 0); Point down = new Point(displaySize.x / 2, displaySize.y); // Convert to LatLng using the GoogleMap object LatLng latlngUp = googleMap.getProjection().fromScreenLocation(up); LatLng latlngDown = googleMap.getProjection().fromScreenLocation(down); // Create a vector between the "screen point LatLng" objects double[] vector = new double[] {latlngUp.latitude - latlngDown.latitude, latlngUp.longitude - latlngDown.longitude}; // Calculate the offset position LatLng latlngMarker = marker.getPosition(); double offsetRatio = 1.0/2.5; LatLng latlngOffset = new LatLng(latlngMarker.latitude + (offsetRatio * vector[0]), latlngMarker.longitude + (offsetRatio * vector[1])); // Move the camera to the desired position CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLng(latlngOffset); googleMap.animateCamera(cameraUpdate);


Tengo casi exactamente el mismo problema que usted (solo mi compensación es vertical), y probé una solución que no funcionó como se esperaba, pero podría ayudarlo (a nosotros) a encontrar una solución mejor.

Lo que hice fue mover la cámara del mapa para obtener la proyección en el objetivo y aplicar el desplazamiento allí.

Coloqué el código en una clase de Utils y se ve así:

public static CameraBuilder moveUsingProjection(GoogleMap map, CameraBuilder target, int verticalMapOffset) { CameraPosition oldPosition = map.getCameraPosition(); map.moveCamera(target.build(map)); CameraBuilder result = CameraBuilder.builder() .target(moveUsingProjection(map.getProjection(), map.getCameraPosition().target, verticalMapOffset)) .zoom(map.getCameraPosition().zoom).tilt(map.getCameraPosition().tilt); map.moveCamera(CameraUpdateFactory.newCameraPosition(oldPosition)); return result; } public static LatLng moveUsingProjection(Projection projection, LatLng latLng, int verticalMapOffset) { Point screenPoint = projection.toScreenLocation(latLng); screenPoint.y -= verticalMapOffset; return projection.fromScreenLocation(screenPoint); }

Entonces podría usar el CameraBuilder devuelto por estos métodos para la animación, en lugar del objetivo original (no desactivado).

Funciona (más o menos), pero tiene dos grandes problemas:

  • El mapa parpadea a veces (este es el problema más grande, porque hace que el truco no se pueda usar)
  • Es un truco que puede funcionar hoy y puede que no funcione mañana (de hecho, sé que este mismo algoritmo funciona en iOS, por ejemplo).

Así que no fui por esto, pero me hizo pensar en una alternativa: si tuvieras un mapa idéntico pero escondido, podrías usar ese mapa para cálculos intermedios, y luego usar el resultado final en el mapa visible.

Creo que esta alternativa funcionaría, pero es horrible, ya que tendrá que mantener dos mapas idénticos, con la sobrecarga que conlleva, para usted y para el dispositivo.

Como dije, no tengo una solución definitiva para esto, pero tal vez esto ayude un poco.

¡Espero que esto ayude!