iphone - ¿MKMapView setRegion “se ajusta” a niveles de zoom predefinidos?
mapkit (7)
¿Alguien puede confirmar que setRegion se "ajuste" a niveles de zoom predefinidos y si este comportamiento es o no está diseñado (aunque no documentado) o es un error conocido? Específicamente, parece que setRegion se ajusta a los mismos niveles de zoom que corresponden a los niveles de zoom utilizados cuando el usuario toca el mapa con una doble pulsación.
Estoy intentando restaurar una región guardada anteriormente, pero este comportamiento hace que sea imposible si la región guardada se configuró mediante un zoom de pellizco y no un zoom de doble toque.
Una gran pista para mí de que las cosas se rompen en el lado de mapkit es lo que ocurre si llamo a regionThatFits en la región actual del mapa. Debería devolver la misma región (ya que obviamente se ajusta al marco del mapa) pero en cambio devuelve la región que corresponde al siguiente nivel de zoom predefinido más alto.
setVisibleMapRect se comporta de manera similar.
Cualquier apreciación o información adicional sería apreciada.
Encontré estas publicaciones relacionadas pero no incluí una solución o confirmación definitiva de que esto es en realidad un error de mapkit:
MKMapView setRegion: extraño comportamiento?
MKMapView muestra la región guardada incorrectamente
EDITAR:
Aquí hay un ejemplo que demuestra el problema. Todos los valores son válidos para la relación de aspecto de mi vista de mapa:
MKCoordinateRegion initialRegion;
initialRegion.center.latitude = 47.700200f;
initialRegion.center.longitude = -122.367109f;
initialRegion.span.latitudeDelta = 0.065189f;
initialRegion.span.longitudeDelta = 0.067318f;
[map setRegion:initialRegion animated:NO];
NSLog(@"DEBUG initialRegion: %f %f %f %f", initialRegion.center.latitude, initialRegion.center.longitude, initialRegion.span.latitudeDelta, initialRegion.span.longitudeDelta);
NSLog(@"DEBUG map.region: %f %f %f %f", map.region.center.latitude, map.region.center.longitude, map.region.span.latitudeDelta, map.region.span.longitudeDelta);
SALIDA:
DEBUG initialRegion: 47.700199 -122.367111 0.065189 0.067318
DEBUG map.region: 47.700289 -122.367096 0.106287 0.109863
Observe la discrepancia en los valores delta de latitud / longitud. Los valores del mapa son casi el doble de lo que pedí. Los valores más grandes corresponden a uno de los niveles de zoom utilizados cuando el usuario toca dos veces el mapa.
El extraño comportamiento parece deberse al hecho de que mientras uno solicita una región o tamaño de vista en particular, la llamada API real a google se invoca con un punto central y un nivel de zoom. P.EJ:
map.setCenter(new google.maps.LatLng(234.3453, 454.2345), 42);
Ahora Apple podría solicitar el nivel de zoom adecuado y luego ajustar el tamaño de la vista para adaptarse a la solicitud de la región real, pero parece que no lo hacen. Estoy dibujando rutas de autobús en un mapa, y una de mis rutas apenas activa un nivel de zoom más grande y, por lo tanto, es demasiado pequeña (zoom inferior) y se ve fea y destrozada.
@pseudopeach, por favor, infórmeme sobre el progreso de sus intentos de solucionar este problema. Si uno pudiera detectar los límites de un nivel de zoom, la solicitud de región podría entonces ser subestimada deliberadamente para evitar el sub-zoom. Ya que estás en esto, me gustaría ver tu código antes de intentarlo yo mismo.
Hay una categoría interesante que el autor del blog Backspace Prolog ha escrito para habilitar la manipulación directa de la API de Google Maps mediante la emulación de su firma de llamada setCenter (centerPoint, ZoomLevel). Puedes encontrarlo here . Todavía no he gastado el tiempo, pero es probable que los cálculos matemáticos se puedan diseñar por ingeniería inversa para obtener un medio de calcular el nivel de zoom para una Región o MapRect determinada. Dependiendo de lo lejos que esté dentro del rango del nivel de zoom, es decir, de lo lejos que esté por encima del umbral que activa el nivel de zoom inferior, podría decidir si irá al nivel más bajo o se mantendrá en uno más alto mediante una solicitud insuficiente.
Esto es claramente un error de comportamiento que debe solucionarse para que MKMapView pueda usarse de una manera más refinada.
Encontré una solución.
Si el nivel de zoom ajustado recibido es, digamos un factor de 1.2 más grande que el deseado: use este algoritmo para corregir:
Asunción: desea configurar la vista del mapa para que muestre exactamente "Medidores longitudinales" de izquierda a derecha
1) Calcular la escala de corrección:
Calcula la relación entre el tramo longitudinal que recibiste, a la que tienes.
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(center, 0, longitudinalMeters);
MKCoordinateRegion regionFits = [mapView regionThatFits: region];
double correctionFactor = regionFits.span.longitudeDelta / region.span.longitudeDelta;
2) Crea la transformación y aplícala al mapa.
CGAffineTransform mapTransform = CGAffineTransformMakeScale(correctionScale, correctionScale);
CGAffineTransform pinTransform = CGAffineTransformInvert(mapTransform);
[mapView setTransform:mapTransform];
3) Aplique la transformación inversa a los pines del mapa, para mantenerlos en su tamaño original
[mapView setTransform:mapTransform];
for (id<MKAnnotation> annotation in self.mapView.annotations)
{
[[self.mapView viewForAnnotation:annotation] setTransform:pinTransform];
}
Esta es una pregunta antigua, pero recientemente investigué los mapas de Google en detalle y puedo compartir algunas ideas. No sé si esto también es válido para los mapas actuales de Apple.
La razón por la que la resolución se ajusta a niveles de zoom predefinidos es porque los mapas originales obtenidos de los servidores de Google se dibujan con esos niveles de zoom. El tamaño de las características en esos mapas se dibuja con una cierta resolución en mente. Por ejemplo, el ancho (en píxeles) de una carretera en esos mapas es siempre el mismo. En los mapas de mayor resolución, se dibujan más carreteras secundarias, pero su ancho es siempre el mismo. La resolución se ajusta a niveles predefinidos para asegurarse de que esas características siempre se representen con el mismo tamaño. Es decir, no es un error sino una característica.
Esas resoluciones predefinidas varían con la latitud debido a la proyección de Mercator de los mapas. Es fácil trabajar con la proyección de Mercator porque las líneas de latitud se representan rectas y las horizontales y las líneas de longitud rectas y verticales. Pero con la proyección de Mercator, la parte superior del mapa tiene una resolución ligeramente más alta que la parte inferior (en el hemisferio norte). Eso tiene consecuencias para encajar los mapas en los bordes norte y sur.
Es decir, cuando comienza en el ecuador y conduce hacia el norte, entonces la resolución de los mapas de Mercator sobre los que conduce aumentará gradualmente. Las líneas de longitud permanecen verticales, y por lo tanto los tramos de longitud siguen siendo los mismos. Pero la resolución aumenta, y por lo tanto, el intervalo de latitud disminuye. Aún así, en todos esos mapas, las carreteras tienen el mismo ancho en píxeles, y los textos se representan en el mismo tamaño de fuente, etc.
Google utiliza una proyección de Mercator donde la circunferencia del ecuador es de 256 píxeles en el nivel de zoom 0. Cada siguiente nivel de zoom duplica esa cantidad. Es decir, en el nivel de zoom 1, el ecuador tiene una longitud de 512 píxeles, en el nivel de zoom 2, el ecuador tiene una longitud de 1024 píxeles, etc. El modelo de la Tierra que usan es un globo de FAI con un radio de exactamente 6371 km, o una circunferencia de 40030 km. Por lo tanto, la resolución para zoomLevel 0 en el ecuador es 156.37 km / pixel, en zoomlevel 1 es 78.19 km / pixel, etc. Estas resoluciones luego varían con el coseno de la latitud en cualquier otro lugar de la tierra.
Restauro la región sin problemas y sin variaciones como usted describe. Es realmente imposible saber qué es específicamente incorrecto en tu caso sin un código que ver, pero esto es lo que funciona para mí:
Guarde los valores del centro y del intervalo en algún lugar. Cuando los está restaurando, establezca específicamente el centro y el tramo.
La restauración debe tener este aspecto:
MKCoordinateRegion initialRegion;
initialRegion.center.latitude = Value you''ve stored
initialRegion.center.longitude = Value you''ve stored
initialRegion.span.latitudeDelta = Value you''ve stored
initialRegion.span.longitudeDelta = Value you''ve stored
[self.mapView setRegion:initialRegion animated:NO];
También recuerde que este método está disponible en 4.0: `mapRectThatFits: edgePadding: MapRectThatFits agrega un borde razonable para garantizar que la anotación del mapa en el borde no esté oculta y que el indicador que está intentando mostrar sea totalmente visible. Si desea controlar el borde, use la llamada que le da acceso para configurar edgePadding también.
Sí, se ajusta a niveles discretos. He hecho un poco de experimentación, y parece que me gustan los múltiplos de 2.68220906e-6 grados de longitud por píxel.
Entonces, si su mapa llena todo el ancho de la pantalla, el primer nivel abarca .0008583 grados, luego el siguiente nivel que puede obtener es el doble, .001717, y luego el siguiente es el doble, .003433, y así sucesivamente . No estoy seguro de por qué optaron por normalizarse por longitud, ya que los niveles de zoom varían según la parte del mundo que estés viendo.
También pasé mucho tiempo tratando de entender el significado de ese número .68220906e-6 grados. El ecuador se aproxima a unos 30 cm, lo que tiene sentido ya que las fotos de alta resolución utilizadas por Google Maps tienen una resolución de 30 cm, pero habría esperado que usaran la latitud en lugar de la longitud para establecer los niveles de zoom. De esa manera, con el zoom máximo, siempre tendrá la resolución nativa de las imágenes satelitales, pero quién sabe, probablemente tengan alguna razón inteligente para hacer que funcione así.
En mi aplicación necesito mostrar un cierto rango de latitud. Voy a trabajar en algún código para tratar de acercar el mapa lo más cerca posible de eso. Si alguien está interesado, contáctame.
Si configura MapView en InterfaceBuilder, asegúrese de no hacer esto:
_mapView = [[MKMapView alloc] init];
Tan pronto como eliminé esta línea de inicio, mi vista de mapa comenzó a responder de manera adecuada a todas las actualizaciones que le envié. Sospecho que lo que sucede es que si realiza el inicio de asignación, en realidad está creando otra vista que no se muestra en ninguna parte. El que ve en la pantalla es el inicializado por su plumilla. Pero si asignas una nueva a init, entonces eso es algo en otro lugar y no va a hacer nada.
MKCoordinateRegion region;
region.center.latitude = latitude;
region.center.longitude = longitude;
region.span.latitudeDelta = 5.0;
region.span.longitudeDelta = 5.0;
[mapView setRegion:region animated:YES];