Module: MapKit::ZoomLevel::ClassMethods

Includes:
Math
Defined in:
lib/map-kit-wrapper/zoom_level.rb

Overview

Map conversion methods

Instance Method Summary collapse

Instance Method Details

#coordinate_region_with_map_view(map_view, center_coordinate, zoom_level) ⇒ Object

Get the coordiante region for the given zoom level

This would involve wrapping the map from top to bottom, something that a Mercator projection just cannot do.

  • Args :

    • map_view -> A MKMapView

    • center_coordinates -> Coordinates as MKMapPoint

    • zoom_level -> Zoom level as Int

  • Returns :

    • Region as MKCoordinateRegion



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/map-kit-wrapper/zoom_level.rb', line 141

def coordinate_region_with_map_view(map_view, center_coordinate, zoom_level)

  # clamp lat/long values to appropriate ranges
  center_coordinate.latitude = [[-90.0, center_coordinate.latitude].max, 90.0].min
  center_coordinate.longitude = center_coordinate.longitude % 180.0

  # convert center coordiate to pixel space
  center_pixel_x = self.longitude_to_pixel_space_x(center_coordinate.longitude)
  center_pixel_y = self.latitude_to_pixel_space_y(center_coordinate.latitude)

  # determine the scale value from the zoom level
  zoom_exponent = 20 - zoom_level
  zoom_scale = 2 ** zoom_exponent

  # scale the map’s size in pixel space
  map_size_in_pixels = map_view.bounds.size
  scaled_map_width = map_size_in_pixels.width * zoom_scale
  scaled_map_height = map_size_in_pixels.height * zoom_scale

  # figure out the position of the left pixel
  top_left_pixel_x = center_pixel_x - (scaled_map_width / 2)

  # find delta between left and right longitudes
  min_lng = self.pixel_space_x_to_longitude(top_left_pixel_x)
  max_lng = self.pixel_space_x_to_longitude(top_left_pixel_x + scaled_map_width)
  longitude_delta = max_lng - min_lng

  # if we’re at a pole then calculate the distance from the pole towards the equator
  # as MKMapView doesn’t like drawing boxes over the poles
  top_pixel_y = center_pixel_y - (scaled_map_height / 2)
  bottom_pixel_y = center_pixel_y + (scaled_map_height / 2)
  adjusted_center_point = false
  if top_pixel_y > MERCATOR_OFFSET * 2
    top_pixel_y = center_pixel_y - scaled_map_height
    bottom_pixel_y = MERCATOR_OFFSET * 2
    adjusted_center_point = true
  end

  # find delta between top and bottom latitudes
  min_lat = self.pixel_space_y_to_latitude(top_pixel_y)
  max_lat = self.pixel_space_y_to_latitude(bottom_pixel_y)
  latitude_delta = -1 * (max_lat - min_lat)

  # create and return the lat/lng span
  span = MKCoordinateSpanMake(latitude_delta, longitude_delta)
  region = MKCoordinateRegionMake(center_coordinate, span)
  # once again, MKMapView doesn’t like drawing boxes over the poles
  # so adjust the center coordinate to the center of the resulting region
  if adjusted_center_point
    region.center.latitude = self.pixel_space_y_to_latitude((bottom_pixel_y + top_pixel_y) / 2.0)
  end

  region
end

#coordinate_span_with_map_view(map_view, center_coordinate, zoom_level) ⇒ Object

Get the coordiante span for the given zoom level

  • Args :

    • map_view -> A MKMapView

    • center_coordinates -> Coordinates as MKMapPoint

    • zoom_level -> Zoom level as Int

  • Returns :

    • Span as MKCoordinateSpan



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/map-kit-wrapper/zoom_level.rb', line 96

def coordinate_span_with_map_view(map_view, center_coordinate, zoom_level)
  # convert center coordiate to pixel space
  center_pixel_x = self.longitude_to_pixel_space_x(center_coordinate.longitude)
  center_pixel_y = self.latitude_to_pixel_space_y(center_coordinate.latitude)

  # determine the scale value from the zoom level
  zoom_exponent = 20 - zoom_level
  zoom_scale = 2 ** zoom_exponent

  # scale the map’s size in pixel space
  map_size_in_pixels = map_view.bounds.size
  scaled_map_width = map_size_in_pixels.width * zoom_scale
  scaled_map_height = map_size_in_pixels.height * zoom_scale

  # figure out the position of the top-left pixel
  top_left_pixel_x = center_pixel_x - (scaled_map_width / 2)
  top_left_pixel_y = center_pixel_y - (scaled_map_height / 2)

  # find delta between left and right longitudes
  min_lng = self.pixel_space_x_to_longitude(top_left_pixel_x)
  max_lng = self.pixel_space_x_to_longitude(top_left_pixel_x + scaled_map_width)
  longitude_delta = max_lng - min_lng

  # find delta between top and bottom latitudes
  min_lat = self.pixel_space_y_to_latitude(top_left_pixel_y)
  max_lat = self.pixel_space_y_to_latitude(top_left_pixel_y + scaled_map_height)
  latitude_delta = -1 * (max_lat - min_lat)

  # create and return the lat/lng span
  MKCoordinateSpanMake(latitude_delta, longitude_delta)
end

#latitude_to_pixel_space_y(latitude) ⇒ Object

Convert latitude to pixel space y

  • Args :

    • latitude -> Int or Float

  • Returns :

    • Pixel space y as Int



49
50
51
52
53
54
55
56
57
# File 'lib/map-kit-wrapper/zoom_level.rb', line 49

def latitude_to_pixel_space_y(latitude)
  if latitude == 90.0
    0
  elsif latitude == -90.0
    MERCATOR_OFFSET * 2
  else
    (MERCATOR_OFFSET - MERCATOR_RADIUS * log((1 + sin(latitude * PI / 180.0)) / (1 - sin(latitude * PI / 180.0))) / 2.0).round
  end
end

#longitude_to_pixel_space_x(longitude) ⇒ Object

Convert longitude to pixel space x

  • Args :

    • longitude -> Int or Float

  • Returns :

    • Pixel space x as Int



36
37
38
# File 'lib/map-kit-wrapper/zoom_level.rb', line 36

def longitude_to_pixel_space_x(longitude)
  (MERCATOR_OFFSET + MERCATOR_RADIUS * longitude * PI / 180.0).round
end

#pixel_space_x_to_longitude(pixel_x) ⇒ Object

Convert pixel space x to longitude

  • Args :

    • pixel_x -> Int

  • Returns :

    • Longitude as float



68
69
70
# File 'lib/map-kit-wrapper/zoom_level.rb', line 68

def pixel_space_x_to_longitude(pixel_x)
  ((pixel_x.round - MERCATOR_OFFSET) / MERCATOR_RADIUS) * 180.0 / PI
end

#pixel_space_y_to_latitude(pixel_y) ⇒ Object

Convert pixel space y to latitude

  • Args :

    • pixel_y -> Int

  • Returns :

    • Latitude as float



81
82
83
# File 'lib/map-kit-wrapper/zoom_level.rb', line 81

def pixel_space_y_to_latitude(pixel_y)
  (PI / 2.0 - 2.0 * atan(exp((pixel_y.round - MERCATOR_OFFSET) / MERCATOR_RADIUS))) * 180.0 / PI
end