iOS 11: MKOverlayRenderer decouples previously consistent relationship between mapRect and zoomScale

Originator:jordan.smith
Number:rdar://33638875 Date Originated:
Status:Open Resolved:
Product:iOS - MapKit Product Version:iOS 11 beta 1 - 10
Classification: Reproducible:Always
 
Area:
MapKit

Summary:
In previous version of iOS, MKOverlayRenderer has presented subclasses with a zoomScale and mapRect that are equivalent for both canDraw(_:zoomScale:) and draw(_:zoomScale:in:). Returning true when the content is available to draw results in draw(_:zoomScale:in:) being called with the same zoomScale and mapRect parameters passed to canDraw.

Although this is not a documented relationship, it would seem to make sense for several reasons. Tile content (for example, third party mapping provider images) may be cached on a per mapRect basis, and the relationship between mapRect size and zoomScale until now has remained constant. Some third party mapping providers do not support multiple tile sizes per zoom level, requiring a large amount of work to decouple the expected relationship (i.e. fetching multiple tiles, stitching them together, cropping the result, and performing this such that requests are cached and coalesced).

As of iOS 11 beta 4, the mapRect and zoomScale passed to canDraw(_:zoomScale) differs from the values passed to draw(_:zoomScale:in). The latter is called with smaller mapRects, but an equivalent zoomScale. This does not break documented logic, as areas are only drawn inside the mapRect specified by canDraw(_:zoomScale). It does however break assumptions that may have been made about the correlation between mapRect and zoomScale, and that canDraw and draw will be called with matching parameters.

Is this change intentional? It could well be speculated that this is indeed the case, for performance, pre-caching, or other internal reason - and since the behaviour is not documented should have no undue effect. However, the repercussions of such may be significant for anyone drawing content in an MKOverlayRenderer where the above assumptions were made. In some circumstances the work required to undo these assumptions is certainly non trivial.

Steps to Reproduce:
Run the attached project, and take a look at the logged output. Note how differing parameters are logged for each the canDraw(_:zoomScale) and draw(_:zoomScale:in) calls.

Expected Results:
Expected results were for the relationship between zoomLevel and mapRect to be maintained both between iOS 11 and previous versions. The parameters passed to canDraw were also expected to result in a call to draw with matching parameters, provided that true was returned from canDraw.

Observed Results:
canDraw(_:zoomLevel) is called with mapRects that are three times the size passed to draw(_:zoomLevel:in) calls, for correlating zoomLevel values.

Version:
iOS 11 beta 4

Comments

Reported the same issue - reporducible in a plain project Beta 8

I have reported the same issue affecting one of my applications & I was able to reproduce it with a new project, defining a custom tiles overlay rendered the discrepancies mentioned between draw() and canDraw() are present using a 2D base map too. As an output example:

Can draw: MKMapRect(origin: C.MKMapPoint(x: 148897792.0, y: 90177536.0), size: C.MKMapSize(width: 6291456.0, height: 6291456.0))

Drawing function : MKMapRect(origin: C.MKMapPoint(x: 148897792.0, y: 90177536.0), size: C.MKMapSize(width: 2097152.0, height: 2097152.0))

I got exactly 3X size comparing both functions.

My bug was marked as duplicated of yours in Apple Bug reporter

By barbararodeker at Aug. 29, 2017, 12:07 p.m. (reply...)

MapKit MKTileOverlayRenderer inconsistency and 3D Flyover maps

I have just run into exactly the same issue. I have an App which stores Offline Map tiles for display when out of cellular data coverage (or when the user wants to avoid cellular roaming charges).

The discrepancy between the mapRect size supplied in the call to .canDraw() and the mapRect size in the call to .draw() is exactly as you described (3x). But this appears to cause problems only when the Apple Basemap is a 3D Flyover Map. When using a 2D basemap (i.e. Apple Standard, Satellite or Hybrid), the tiles drawn in response to the .draw() method appear in the correct place and scale on the Map.

However when using a 3D Flyover Map, these tiles do not appear at the correct scale (and it seems that only even-numbered tiles are getting displayed at all by MapKit). I'll file a radar with Apple in the hope that this gets fixed before iOS 11 release (hopefully it's not already too late).

Did you get any response from Apple?

By paul.robin.manson at Aug. 17, 2017, 11:20 p.m. (reply...)

Update (or lack thereof)

No update from Apple, and it seems very much still an issue on beta 10.

By jordan.smith at Sept. 8, 2017, 4:14 a.m. (reply...)

Example Project

https://drive.google.com/file/d/0B093qXpgxyolRDF2anhjQ1lFUm8/view?usp=sharing

By jordan.smith at July 31, 2017, 11:28 p.m. (reply...)

Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!