MKMapView+ZoomLevel.m 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. #import "MKMapView+ZoomLevel.h"
  2. #define MERCATOR_OFFSET 268435456
  3. #define MERCATOR_RADIUS 85445659.44705395
  4. #define TILE_SIZE 512
  5. @implementation MKMapView (ZoomLevel)
  6. #pragma mark -
  7. #pragma mark Map conversion methods
  8. + (double)longitudeToPixelSpaceX:(double)longitude
  9. {
  10. return round(MERCATOR_OFFSET + MERCATOR_RADIUS * longitude * M_PI / 180.0);
  11. }
  12. + (double)latitudeToPixelSpaceY:(double)latitude
  13. {
  14. if (latitude == 90.0) {
  15. return 0;
  16. } else if (latitude == -90.0) {
  17. return MERCATOR_OFFSET * 2;
  18. } else {
  19. return round(MERCATOR_OFFSET - MERCATOR_RADIUS * logf((1 + sinf(latitude * M_PI / 180.0)) / (1 - sinf(latitude * M_PI / 180.0))) / 2.0);
  20. }
  21. }
  22. + (double)pixelSpaceXToLongitude:(double)pixelX
  23. {
  24. return ((round(pixelX) - MERCATOR_OFFSET) / MERCATOR_RADIUS) * 180.0 / M_PI;
  25. }
  26. + (double)pixelSpaceYToLatitude:(double)pixelY
  27. {
  28. return (M_PI / 2.0 - 2.0 * atan(exp((round(pixelY) - MERCATOR_OFFSET) / MERCATOR_RADIUS))) * 180.0 / M_PI;
  29. }
  30. #pragma mark -
  31. #pragma mark Helper methods
  32. - (MKCoordinateSpan)coordinateSpanWithMapView:(MKMapView *)mapView
  33. centerCoordinate:(CLLocationCoordinate2D)centerCoordinate
  34. andZoomLevel:(NSUInteger)zoomLevel
  35. {
  36. // convert center coordiate to pixel space
  37. double centerPixelX = [MKMapView longitudeToPixelSpaceX:centerCoordinate.longitude];
  38. double centerPixelY = [MKMapView latitudeToPixelSpaceY:centerCoordinate.latitude];
  39. // determine the scale value from the zoom level
  40. NSInteger zoomExponent = 20 - zoomLevel;
  41. double zoomScale = pow(2, zoomExponent);
  42. zoomScale*=(TILE_SIZE/256);
  43. // scale the map’s size in pixel space
  44. CGSize mapSizeInPixels = mapView.bounds.size;
  45. double scaledMapWidth = mapSizeInPixels.width * zoomScale;
  46. double scaledMapHeight = mapSizeInPixels.height * zoomScale;
  47. // figure out the position of the top-left pixel
  48. double topLeftPixelX = centerPixelX - (scaledMapWidth / 2);
  49. double topLeftPixelY = centerPixelY - (scaledMapHeight / 2);
  50. // find delta between left and right longitudes
  51. CLLocationDegrees minLng = [MKMapView pixelSpaceXToLongitude:topLeftPixelX];
  52. CLLocationDegrees maxLng = [MKMapView pixelSpaceXToLongitude:topLeftPixelX + scaledMapWidth];
  53. CLLocationDegrees longitudeDelta = maxLng - minLng;
  54. // find delta between top and bottom latitudes
  55. CLLocationDegrees minLat = [MKMapView pixelSpaceYToLatitude:topLeftPixelY];
  56. CLLocationDegrees maxLat = [MKMapView pixelSpaceYToLatitude:topLeftPixelY + scaledMapHeight];
  57. CLLocationDegrees latitudeDelta = -1 * (maxLat - minLat);
  58. // create and return the lat/lng span
  59. MKCoordinateSpan span = MKCoordinateSpanMake(latitudeDelta, longitudeDelta);
  60. return span;
  61. }
  62. #pragma mark -
  63. #pragma mark Public methods
  64. - (void)setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
  65. zoomLevel:(NSUInteger)zoomLevel
  66. animated:(BOOL)animated
  67. {
  68. // clamp large numbers to 28
  69. zoomLevel = MIN(zoomLevel, 28);
  70. // use the zoom level to compute the region
  71. MKCoordinateSpan span = [self coordinateSpanWithMapView:self centerCoordinate:centerCoordinate andZoomLevel:zoomLevel];
  72. MKCoordinateRegion region = MKCoordinateRegionMake(centerCoordinate, span);
  73. // set the region like normal
  74. [self setRegion:region animated:animated];
  75. }
  76. //KMapView cannot display tiles that cross the pole (as these would involve wrapping the map from top to bottom, something that a Mercator projection just cannot do).
  77. -(MKCoordinateRegion)coordinateRegionWithMapView:(MKMapView *)mapView
  78. centerCoordinate:(CLLocationCoordinate2D)centerCoordinate
  79. andZoomLevel:(NSUInteger)zoomLevel
  80. {
  81. // clamp lat/long values to appropriate ranges
  82. centerCoordinate.latitude = MIN(MAX(-90.0, centerCoordinate.latitude), 90.0);
  83. centerCoordinate.longitude = fmod(centerCoordinate.longitude, 180.0);
  84. // convert center coordiate to pixel space
  85. double centerPixelX = [MKMapView longitudeToPixelSpaceX:centerCoordinate.longitude];
  86. double centerPixelY = [MKMapView latitudeToPixelSpaceY:centerCoordinate.latitude];
  87. // determine the scale value from the zoom level
  88. NSInteger zoomExponent = 20 - zoomLevel;
  89. double zoomScale = pow(2, zoomExponent);
  90. zoomScale*=(TILE_SIZE/256);
  91. // scale the map’s size in pixel space
  92. CGSize mapSizeInPixels = mapView.bounds.size;
  93. double scaledMapWidth = mapSizeInPixels.width * zoomScale;
  94. double scaledMapHeight = mapSizeInPixels.height * zoomScale;
  95. // figure out the position of the left pixel
  96. double topLeftPixelX = centerPixelX - (scaledMapWidth / 2);
  97. // find delta between left and right longitudes
  98. CLLocationDegrees minLng = [MKMapView pixelSpaceXToLongitude:topLeftPixelX];
  99. CLLocationDegrees maxLng = [MKMapView pixelSpaceXToLongitude:topLeftPixelX + scaledMapWidth];
  100. CLLocationDegrees longitudeDelta = maxLng - minLng;
  101. // if we’re at a pole then calculate the distance from the pole towards the equator
  102. // as MKMapView doesn’t like drawing boxes over the poles
  103. double topPixelY = centerPixelY - (scaledMapHeight / 2);
  104. double bottomPixelY = centerPixelY + (scaledMapHeight / 2);
  105. BOOL adjustedCenterPoint = NO;
  106. if (topPixelY > MERCATOR_OFFSET * 2) {
  107. topPixelY = centerPixelY - scaledMapHeight;
  108. bottomPixelY = MERCATOR_OFFSET * 2;
  109. adjustedCenterPoint = YES;
  110. }
  111. // find delta between top and bottom latitudes
  112. CLLocationDegrees minLat = [MKMapView pixelSpaceYToLatitude:topPixelY];
  113. CLLocationDegrees maxLat = [MKMapView pixelSpaceYToLatitude:bottomPixelY];
  114. CLLocationDegrees latitudeDelta = -1 * (maxLat - minLat);
  115. // create and return the lat/lng span
  116. MKCoordinateSpan span = MKCoordinateSpanMake(latitudeDelta, longitudeDelta);
  117. MKCoordinateRegion region = MKCoordinateRegionMake(centerCoordinate, span);
  118. // once again, MKMapView doesn’t like drawing boxes over the poles
  119. // so adjust the center coordinate to the center of the resulting region
  120. if (adjustedCenterPoint) {
  121. region.center.latitude = [MKMapView pixelSpaceYToLatitude:((bottomPixelY + topPixelY) / 2.0)];
  122. }
  123. return region;
  124. }
  125. - (NSUInteger) zoomLevel {
  126. //
  127. // double d= 360 * self.frame.size.width /256/self.region.span.longitudeDelta;
  128. // return log2(d);
  129. //
  130. MKCoordinateRegion region = self.region;
  131. double centerPixelX = [MKMapView longitudeToPixelSpaceX: region.center.longitude];
  132. double topLeftPixelX = [MKMapView longitudeToPixelSpaceX: region.center.longitude - region.span.longitudeDelta / 2];
  133. double scaledMapWidth = (centerPixelX - topLeftPixelX) * 2;
  134. CGSize mapSizeInPixels = self.bounds.size;
  135. double zoomScale = scaledMapWidth / mapSizeInPixels.width;
  136. double zoomExponent = log(zoomScale/(TILE_SIZE/256)) / log(2);
  137. double zoomLevel = 20 - zoomExponent;
  138. return zoomLevel;
  139. }
  140. @end