MKMapView setRegion «привязывается» к предопределенным уровням масштабирования? - PullRequest
19 голосов
/ 31 августа 2010

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

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

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

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

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

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

MKMapView setRegion: странное поведение?

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

РЕДАКТИРОВАТЬ:

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

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

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

Ответы [ 7 ]

7 голосов
/ 05 января 2011

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

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

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

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

1 голос
/ 29 октября 2011

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

Если полученный масштабированный масштабированный снимок, скажем, в 1,2 раза больше требуемого: используйте этот алгоритм для исправления:
Предположение: вы хотите настроить отображение карты так, чтобы в точности отображать «longticalMeters» слева направо
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) Примените обратное преобразование к пинам карты, чтобы сохранить их в исходном размере

 [mapView setTransform:mapTransform];
 for (id<MKAnnotation> annotation in self.mapView.annotations)
 {
     [[self.mapView viewForAnnotation:annotation] setTransform:pinTransform];
 }
0 голосов
/ 26 ноября 2013

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

_mapView = [[MKMapView alloc] init];

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

0 голосов
/ 07 января 2013

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

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

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

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

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

0 голосов
/ 20 августа 2012

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];
0 голосов
/ 19 мая 2011

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

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

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

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

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

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

0 голосов
/ 01 сентября 2010

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

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

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

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.

...