Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade to Mapbox GL–based Mapbox Static API #54

Merged
merged 22 commits into from
Mar 27, 2017
Merged

Upgrade to Mapbox GL–based Mapbox Static API #54

merged 22 commits into from
Mar 27, 2017

Conversation

1ec5
Copy link
Contributor

@1ec5 1ec5 commented Mar 6, 2017

This PR adds support for the Mapbox GL–powered Mapbox Static API alongside the existing support for the classic Static API. In the process, the existing Swift files have been split up to be more manageable.

The Snapshot class and Overlay subclasses are largely untouched, a consequence of the fact that the new API tries to mimic the classic API’s overlay options. SnapshotOptions has been renamed ClassicSnapshotOptions. This PR takes advantage of the SnapshotOptionsProtocol protocol – meant to provide a common interface for ClassicSnapshotOptions and MarkerOptions – to add another conforming protocol, named SnapshotOptions, that exposes options for working with the new API.

SnapshotOptions is a bit more complex than ClassicSnapshotOptions. Instead of accepting separate centerCoordinate and zoomLevel options, it takes a single camera option of type SnapshotCamera. SnapshotCamera defines both a conventional zoomLevel property and an altitude property (for compatibility with the Mapbox iOS and macOS SDKs’ MGLMapCamera class); the developer can use either.

SnapshotCamera closely parallels the Mapbox iOS and macOS SDKs’ MGLMapCamera class, in much the same way that MapKit’s MKMapSnapshotOptions and MKMapView classes both have camera properties of type MKMapCamera. This library can’t simply reuse MGLMapCamera because it lives in the Mapbox iOS and macOS SDKs, which are separate frameworks. (Perhaps we should move this entire library’s functionality into those SDKs to fix mapbox/mapbox-gl-native#1701?)

GeoJSON(object:) is once again throwable. #23 made it throwable, but #28 had to make it failable instead due to a type mismatch when bridging to Objective-C. As of Swift 3, SE-0112 resolved the type mismatch, so the initializer can once again be throwable without breaking Objective-C support.

The classic snapshot tests have been rewritten to replace clever but untested URL parsing code in tests with much more straightforward string literals that can be verified easily. Now, if you want to know the URL that a particular ClassicSnapshotOptions produces, you can copy the stub’s isPath() call’s argument into a browser instead of having to run the test code. Test fixture images have been added, and the tests verify that an NSImage or UIImage can be created from each test fixture image produced by Snapshot.

To recap:

  • Reorganize Swift files
  • Rename SnapshotOptions to ClassicSnapshotOptions
  • Implement new SnapshotOptions based on GL-based API
  • Make GeoJSON initializer throwable
  • Make unit test stubs more literal
  • Make unit tests rely on image test fixtures
  • Add altitude (distance) as a camera option alongside zoom level, for consistency with MGLMapCamera
  • Add GL snapshot tests
  • Update example applications
  • Update playgrounds and readme
  • Update documentation comments
  • Update readme screenshots once server-side rendering bugs are fixed

Fixes #9.

/cc @frederoni @bsudekum @incanus @friedbunny

1ec5 added 9 commits March 4, 2017 18:06
Renamed SnapshotOptions to ClassicSnapshotOptions. Added a similar SnapshotOptions class tailored to the GL-based Static API. Split out ClassicSnapshotTests.
We previously made this initializer failable instead of throwable because of a compiler bug, but the bug appears to have been resolved in Swift 3.
Split out classic overlay tests into a separate test class. Replaced clever but untested URL parsing code in tests with much more straightforward string literals that can be verified easily. Added test fixture images. Verify that NSImage or UIImage can be created from each test fixture image. Switched to mapbox.streets in overlay tests to make fixtures easier to identify. Added configuration tests.
Upgraded example applications to use the new SnapshotOptions initializer. Refined MBSnapshotCamera initializer for Objective-C in the form of a factory method.
@1ec5
Copy link
Contributor Author

1ec5 commented Mar 7, 2017

Test coverage is at 76.97%:

coverage

Most of the untested code is in convenience methods added for Objective-C compatibility.

@1ec5 1ec5 mentioned this pull request Mar 7, 2017
@1ec5 1ec5 added the refactor label Mar 14, 2017
@1ec5
Copy link
Contributor Author

1ec5 commented Mar 16, 2017

The server-side rendering issues have been fixed, so the remaining work is no longer blocked.

Added altitude as an alternative to zoom level in SnapshotCamera, for consistency with MapKit and the Mapbox iOS SDK.
@1ec5 1ec5 requested a review from bsudekum March 18, 2017 07:11
@1ec5 1ec5 requested a review from frederoni March 18, 2017 07:11
Copy link
Contributor

@friedbunny friedbunny left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not entirely sold on the map camera approach, but that’s a matter of taste.

My first time using the playground here — it’s nice.

#endif

/**
A structure that determines what a snapshot depicts and how it is formatted. A classic snapshot is made by compositing one or more [tile sets](https://www.mapbox.com/help/define-tileset/) with optional overlays using the [classic Mapbox Static API](https://www.mapbox.com/api-documentation/?language=Swift#static-classic).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it a historical oversight that these lines aren’t manually wrapped?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since Xcode automatically reformats the comment in the generated interface (whether you're coming from Swift or Objective-C code), I've been less concerned about documentation comment formatting here than in the gl-native codebase. But if you're concerned about readability for contributors to this codebase, we can address the line wrapping issue in a separate PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We’d have to tread carefully, because the component that translates Swift into Objective-C headers (and thus reStructuredText into Doxygen) is a bit fragile. Objective-C bridging has broken in the past due to something as innocuous as a blank line between - parameter and - note.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly the line length just made it slightly more painful to review.


If you use `Snapshot` to display a [vector tile set](https://www.mapbox.com/help/define-tileset/#vector-tilesets), the snapshot image will depict a wireframe representation of the tile set. To generate a static, styled image of a vector tile set, use the [vector Mapbox Static API](https://www.mapbox.com/api-documentation/?language=Swift#static).
The snapshot image can be used in an image view (`UIImage` on iOS and tvOS, `NSImage` on macOS, `WKImage` on watchOS). To add interactivity, use the `MGLMapView` class provided by the [Mapbox iOS SDK](https://www.mapbox.com/ios-sdk/) or [Mapbox macOS SDK](https://github.com/mapbox/mapbox-gl-native/tree/master/platform/macos/). (See the “[Custom raster style](https://www.mapbox.com/ios-sdk/examples/raster-styles/)” example to display a raster tile set in an `MGLMapView`.)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To add interactivity, use the MGLMapView...

This makes the two libraries sound as if one is necessary for the other or that they are somehow integrated. I think we should frame this more as, “For interactive maps, see our fully-featured SDKs...” and avoid mentioning classnames that impatient readers might expect to exist in this library.

I’m also unsure of the mention of the iOS SDK’s raster tiles example — it strikes me as a non-sequitor, given that that example deals in tilesets and not individual static images.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clarified in 5ccc930.

/**
Size of a tile.
*/
static let tileSize = CGSize(width: 512, height: 512)
Copy link
Contributor

@friedbunny friedbunny Mar 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strictly reading this code, it could be made more clear that tileSize represents an internal implementation detail and not size.

Besides a little disambiguation, is there a source of truth for this that we can link to?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an private variable factored out for the purpose of keeping the altitude conversion code readable.

}

func string(size: CGSize) -> String {
assert(-90...90 ~= centerCoordinate.latitude, "Center latitude must be between −90° and 90°.")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice range comparison.

assert(0...20 ~= zoomLevel, "Zoom level must be between 0 and 20.")
components.append(Double(zoomLevel))

if heading > 0 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does heading need a range check, or will >360 just keep wrapping?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The API wraps it automatically.

/**
A Boolean determining whether the resulting image includes the Mapbox logo.

When shown, the Mapbox logo is located in the lower-left corner of the image. By default, this property is set to `true`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chances are quite high that the developer is required to include the logo, so linking https://www.mapbox.com/help/attribution/ here also makes sense.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noted in 5ccc930.

README.md Outdated
@@ -16,13 +16,13 @@ MapboxStatic.swift pairs well with [MapboxDirections.swift](https://github.com/m
Specify the following dependency in your [Carthage](https://github.com/Carthage/Carthage/) Cartfile:

```sh
github "Mapbox/MapboxStatic.swift" "~> 0.7"
github "Mapbox/MapboxStatic.swift" "master"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Risky... is it necessary not to specify a version?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now. Whenever we rewrite the readme, we have to instruct the developer to get the master version so that the instructions are consistent. Otherwise we'd have to stage changes to the documentation, which would render them inconsistent with the rest of the repository in the meantime.

This shouldn't be a problem, because both CocoaPods and Carthage instruct developers to check in their lock files.

*/

let mapIdentifiers = ["justin.tm2-basemap"]
let styleURL = URL(string: "mapbox://styles/mapbox/streets-v9")!
let mapIdentifiers = ["mapbox.satellite"]
let accessToken = "pk.eyJ1IjoianVzdGluIiwiYSI6IlpDbUJLSUEifQ.4mG8vhelFMju6HpIY-Hi5A"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This token belongs to @incanus — it should be replaced with one belonging to the mapbox account, if we’re going to include one at all.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do need to include the token if we want the playground to function as it does. We can replace it with a Mapbox corporate token, though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced in 49ec1d0.

*/

let mapIdentifiers = ["justin.tm2-basemap"]
let styleURL = URL(string: "mapbox://styles/mapbox/streets-v9")!
let mapIdentifiers = ["mapbox.satellite"]
let accessToken = "pk.eyJ1IjoianVzdGluIiwiYSI6IlpDbUJLSUEifQ.4mG8vhelFMju6HpIY-Hi5A"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto to this access token.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dittoed in 49ec1d0.

Clarified the relationship between Snapshot and the iOS and macOS SDKs. Added a notice about the general requirement to leave the logo enabled.
@1ec5 1ec5 merged commit 8ade96f into master Mar 27, 2017
@1ec5 1ec5 deleted the 1ec5-gl-9 branch March 27, 2017 16:51
@quicklywilliam
Copy link

Huzzah!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Upgrade to Mapbox GL-based Mapbox Static API Add map snapshot API
3 participants