Подтвердить что ты не робот

MKMapView setRegion "привязывает" к предопределенным уровням масштабирования?

Может ли кто-нибудь подтвердить, что setRegion "привязывается" к предопределенным уровням масштабирования и независимо от того, является ли это поведение таким же спроектированным (хотя и недокументированным) или известной ошибкой? В частности, кажется, что setRegion привязывается к тем же уровням масштабирования, которые соответствуют уровням масштабирования, используемым, когда пользователь дважды отображает карту.

Я пытаюсь восстановить ранее сохраненную область, но это поведение делает невозможным сохранение сохраненной области с помощью масштабирования, а не масштабирование с двойным нажатием.

Большая подсказка для меня, что вещи сломаны на стороне карты, - это то, что происходит, если я назову regionThatFits на текущей текущей карте. Он должен возвращать тот же регион (поскольку он явно соответствует кадру карты), но он возвращает область, которая соответствует следующему более высокому предварительному уровню масштабирования.

setVisibleMapRect ведет себя аналогично.

Будет оценено любое дальнейшее понимание или информация.

Я нашел эти связанные сообщения, но не включил решение или окончательное подтверждение того, что это на самом деле ошибка mapkit:

MKMapView setRegion: нечетное поведение?

MKMapView показывает неправильно сохраненную область

EDIT:

Вот пример, демонстрирующий проблему. Все значения действительны для моего формата карты:

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

ВЫВОД:

DEBUG initialRegion:  47.700199  -122.367111  0.065189  0.067318
DEBUG map.region:  47.700289  -122.367096  0.106287  0.109863

Обратите внимание на несоответствие значений дельты/долготы. Значения карты почти вдвое больше, чем я запрашивал. Большие значения соответствуют одному из уровней масштабирования, которые используются, когда пользователь дважды отображает карту.

4b9b3361

Ответ 1

Да, он привязывается к дискретным уровням. Я провел довольно много экспериментов, и мне кажется, что они имеют кратность 2.68220906e-6 градусов долготы на пиксель.

Итак, если ваша карта заполняет всю ширину экрана, первый уровень охватывает .0008583 градуса, то следующий уровень, который вы можете получить, в два раза больше, чем .001717, а затем следующий в два раза больше .003433, и так далее. Я не уверен, почему они решили нормализовать по долготе, это означает, что фиксированные уровни масштабирования варьируются в зависимости от того, в какой части мира вы смотрите.

Я также потратил много времени, пытаясь понять значение этого числа .68220906e-6 градусов. Он достигает около 30 см на экваторе, что имеет смысл, поскольку фотографии с высоким разрешением, используемые Картами Google, имеют разрешение 30 см, но я ожидал, что они будут использовать широту вместо долготы, чтобы установить уровни масштабирования. Таким образом, при максимальном увеличении вы всегда находите собственное разрешение спутниковых изображений, но кто знает, у них, вероятно, есть разумные люди, чтобы заставить его работать так.

В моем приложении мне нужно отобразить определенный диапазон широты. Я собираюсь поработать над некоторым кодом, чтобы попытаться увеличить карту как можно ближе к ней. Если кто-то заинтересован, свяжитесь со мной.

Ответ 2

Я нашел решение.

Если полученный сниженный уровень масштабирования, есть, скажем, в 1,2 раза больше, чем желаемый: используйте этот алгоритм для исправления:
Asumption: вы хотите настроить отображение карты, чтобы точно показать "продольные метры" слева направо
1) Вычислить шкалу коррекции:
Вычислите связь между полученным продольным интервалом и тем, что вы получили.

    MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(center, 0, longitudinalMeters);

    MKCoordinateRegion regionFits = [mapView regionThatFits: region];
    double correctionFactor = regionFits.span.longitudeDelta / region.span.longitudeDelta;

2) Создайте преобразование и примените его к карте

   CGAffineTransform mapTransform = CGAffineTransformMakeScale(correctionScale, correctionScale);       
   CGAffineTransform pinTransform = CGAffineTransformInvert(mapTransform);
   [mapView setTransform:mapTransform];

3) Примените обратное преобразование к выводам Map, чтобы сохранить их в исходном размере

 [mapView setTransform:mapTransform];
 for (id<MKAnnotation> annotation in self.mapView.annotations)
 {
     [[self.mapView viewForAnnotation:annotation] setTransform:pinTransform];
 }

Ответ 3

Странное поведение, по-видимому, связано с тем, что при запросе определенного региона или размера представления фактический вызов API для google вызывается с центральной точкой и уровнем масштабирования. Например:.

map.setCenter(new google.maps.LatLng(234.3453, 454.2345), 42);

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

@pseudopeach, пожалуйста, сообщите мне о ваших попытках обойти эту проблему. Если бы можно было определить границы уровня масштабирования, тогда запрос области мог бы быть намеренно недооценен, чтобы избежать недостаточного масштабирования. Поскольку вы на это, мне было бы интересно увидеть ваш код, прежде чем я попытаюсь сделать это сам.

Есть интересная категория, которую автор блога Backspace Prolog написал, чтобы позволить прямое манипулирование API Карт Google, эмулируя их подпись setCenter (centerPoint, ZoomLevel). Вы можете найти здесь здесь. Я еще не потратил время, но математика, вероятно, может быть обратная инженерия, чтобы дать средство вычисления уровня масштабирования для данного региона или MapRect. В зависимости от того, насколько далеко он находится в диапазоне уровня масштабирования - то есть, насколько он превысил порог, который вызывает более низкий уровень масштабирования, - он может решить, идти ли на нижний уровень или поддерживать более высокий, недопрашивая.

Это явно поведенческая ошибка, которую необходимо устранить, чтобы MKMapView можно было использовать более утонченным образом.

Ответ 4

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];

Ответ 5

Это старый вопрос, но я недавно подробно изучил карты Google и могу поделиться некоторыми соображениями. Я не знаю, подходит ли это для текущих карт Apple.

Причина, по которой разрешение снимается с предопределенных зум-уровней, заключается в том, что исходные карты, извлеченные из серверов Google, нарисованы с помощью этих уровней увеличения. Размер элементов на этих картах рисуется с учетом определенного разрешения. Например, ширина (в пикселях) дороги на этих картах всегда одна и та же. На картах с более высоким разрешением нарисованы еще более дорогие дороги, но их ширина всегда одна и та же. Разрешение защелкивается до предопределенных уровней, чтобы убедиться, что эти функции всегда изображены с одинаковым размером. То есть, это не ошибка, а функция.

Эти предопределенные разрешения различаются по широте из-за проекции Меркатора на картах. Проецирование Меркатора легко работать, потому что линии широты изображаются прямыми, а горизонтальные и долготные линии прямые и вертикальные. Но с проекцией Меркатора вершина карты имеет несколько более высокое разрешение, чем нижняя (в северном полушарии). Это имеет последствия для подгонки карт вместе на северном и южном краях.

То есть, когда вы начинаете на экваторе и едете на север, тогда разрешение Меркатора, на котором вы проезжаете, будет постепенно увеличиваться. Длинные линии остаются вертикальными, и поэтому долготы остаются неизменными. Но разрешение увеличивается, и поэтому диапазон широты уменьшается. Тем не менее, на всех этих картах дороги имеют одинаковую ширину в пикселях, а тексты изображаются с одинаковым размером шрифта и т.д.

Google использует проекцию Меркатора, где окружность экватора составляет 256 пикселей при увеличении уровня 0. Каждый следующий масштабный уровень удваивает эту сумму. То есть, при масштабировании 1 экватор имеет длину в 512 пикселей, при увеличении уровня 2 экватор имеет длину 1024 пикселя и т.д. Модель земли, которую они используют, представляет собой шар FAI с радиусом ровно 6371 км или окружностью 40030 км. Следовательно, разрешение для zoomLevel 0 на экваторе составляет 156,37 км/пиксель, при увеличении 1 - 78,19 км/пиксель и т.д. Эти разрешения затем меняются с косинусом широты в другом месте на земле.

Ответ 6

Я восстанавливаю регион без проблем и без изменений, как вы описываете. На самом деле невозможно сказать, что конкретно не так в вашем случае, без какого-либо кода, чтобы посмотреть, но вот что работает для меня:

Сохраните значения центра и диапазона где-нибудь. Когда вы их восстанавливаете, задайте как центр, так и интервал.

Восстановление должно выглядеть следующим образом:

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];

Также помните, что этот метод доступен в 4.0: `mapRectThatFits: edgePadding: MapRectThatFits помогает добавить разумную границу, чтобы убедиться, что аннотация на карте не затенена, а прямоугольник, который вы пытаетесь отобразить, полностью видимый. Если вы хотите управлять границей, используйте вызов, который дает вам доступ к установке edgePadding.

Ответ 7

Если вы настроили MapView в InterfaceBuilder, убедитесь, что вы этого не делаете:

_mapView = [[MKMapView alloc] init];

Как только я удалил эту строку инициализации, мое представление карты внезапно начало правильно отвечать на все обновления, которые я ему отправил. Я подозреваю, что случается, что если вы выполняете инициализацию alloc, это фактически создает другое представление, которое не отображается нигде. Тот, который вы видите на экране, является тем, который был инициализирован вашим наконечником. Но если вы назначите init новый, то что-то еще, и он ничего не собирается делать.