-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit c31ba1f
Showing
7 changed files
with
382 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
MapboxStatic copyright (c) 2014, Mapbox. All rights reserved. | ||
|
||
Redistribution and use in source and binary forms, with or without modification, | ||
are permitted provided that the following conditions are met: | ||
|
||
- Redistributions of source code must retain the above copyright notice, this | ||
list of conditions and the following disclaimer. | ||
- Redistributions in binary form must reproduce the above copyright notice, this | ||
list of conditions and the following disclaimer in the documentation and/or | ||
other materials provided with the distribution. | ||
- Neither the name "Mapbox" nor the names of its contributors may be | ||
used to endorse or promote products derived from this software without | ||
specific prior written permission. | ||
|
||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR | ||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,232 @@ | ||
import CoreLocation | ||
import Foundation | ||
import UIKit | ||
|
||
private extension UIColor { | ||
|
||
private func toHexString() -> String { | ||
|
||
var r: CGFloat = 0 | ||
var g: CGFloat = 0 | ||
var b: CGFloat = 0 | ||
var a: CGFloat = 0 | ||
|
||
self.getRed(&r, green: &g, blue: &b, alpha: &a) | ||
|
||
r *= 255 | ||
g *= 255 | ||
b *= 255 | ||
|
||
return NSString(format: "%02x%02x%02x", Int(r), Int(g), Int(b)) | ||
} | ||
|
||
private class func colorWithHexString(var hexString: String) -> UIColor { | ||
|
||
hexString = hexString.stringByReplacingOccurrencesOfString("#", withString: "") | ||
|
||
if (countElements(hexString) == 3) { | ||
hexString = "\(Array(hexString)[0])\(Array(hexString)[0])\(Array(hexString)[1])\(Array(hexString)[1])\(Array(hexString)[2])\(Array(hexString)[2])" | ||
} | ||
|
||
if (countElements(hexString) == 6) { | ||
var r: CGFloat = 0 | ||
var g: CGFloat = 0 | ||
var b: CGFloat = 0 | ||
|
||
var hexInt: UInt32 = 0 | ||
|
||
if (NSScanner(string: hexString).scanHexInt(&hexInt)) { | ||
r = CGFloat((hexInt >> 16) & 0xff) / 255 | ||
g = CGFloat((hexInt >> 8) & 0xff) / 255 | ||
b = CGFloat(hexInt & 0xff) / 255 | ||
|
||
return UIColor(red: r, green: g, blue: b, alpha: 1) | ||
} | ||
} | ||
|
||
return UIColor.blackColor() | ||
} | ||
} | ||
|
||
public class MapboxStaticMap { | ||
|
||
let requestURLStringBase = "https://api.tiles.mapbox.com/v4/" | ||
|
||
public enum ImageFormat: String { | ||
case PNG = "png" | ||
case PNG256 = "png256" | ||
case PNG128 = "png128" | ||
case PNG64 = "png64" | ||
case PNG32 = "png32" | ||
case JPEG = "jpg" | ||
case JPEG90 = "jpg90" | ||
case JPEG80 = "jpg80" | ||
case JPEG70 = "jpg70" | ||
} | ||
|
||
public var requestURL: NSURL | ||
|
||
public lazy var image: UIImage? = { | ||
if let data = NSData(contentsOfURL: self.requestURL) { | ||
return UIImage(data: data) | ||
} else { | ||
return nil | ||
} | ||
}() | ||
|
||
public func imageWithCompletionHandler(handler: (UIImage? -> Void)) { | ||
let task = NSURLSession.sharedSession().dataTaskWithURL(requestURL, completionHandler: { (data: NSData!, response: NSURLResponse!, error: NSError!) in | ||
let image = UIImage(data: data) | ||
dispatch_async(dispatch_get_main_queue(), { Void -> Void in | ||
handler(image) | ||
}) | ||
}) | ||
task.resume() | ||
} | ||
|
||
init(mapID: String, | ||
center: CLLocationCoordinate2D, | ||
zoom: Int, | ||
size: CGSize, | ||
accessToken: String, | ||
format: ImageFormat = .PNG, | ||
retina: Bool = false, | ||
overlays: [Overlay] = []) { | ||
|
||
var requestURLString = requestURLStringBase | ||
requestURLString += mapID | ||
requestURLString += "/" | ||
|
||
if (overlays.count > 0) { | ||
requestURLString += join(",", overlays.map({ return $0.requestString })) | ||
requestURLString += "/" | ||
} | ||
|
||
requestURLString += "\(center.longitude),\(center.latitude),\(zoom)/" | ||
requestURLString += "\(Int(size.width))x\(Int(size.height))" | ||
requestURLString += (retina ? "@2x" : "") | ||
requestURLString += "." | ||
requestURLString += format.rawValue | ||
requestURLString += "?access_token=" | ||
requestURLString += accessToken | ||
|
||
requestURL = NSURL(string: requestURLString)! | ||
} | ||
|
||
public enum MarkerSize: String { | ||
case Small = "s" | ||
case Medium = "m" | ||
case Large = "l" | ||
} | ||
|
||
public class Overlay { | ||
|
||
var requestString: String | ||
|
||
init() { | ||
requestString = "" | ||
} | ||
|
||
} | ||
|
||
public class Marker: Overlay { | ||
|
||
init(coordinate: CLLocationCoordinate2D, | ||
size: MarkerSize = .Small, | ||
label: String = "", | ||
color: UIColor = UIColor.redColor()) { | ||
|
||
super.init() | ||
|
||
requestString = "pin-" | ||
requestString += size.rawValue | ||
|
||
if (countElements(label) > 0) { | ||
requestString += "-" + label | ||
} | ||
|
||
requestString += "+" + color.toHexString() | ||
requestString += "(\(coordinate.longitude),\(coordinate.latitude))" | ||
|
||
} | ||
|
||
} | ||
|
||
public class GeoJSON: Overlay { | ||
|
||
init(GeoJSON: String) { | ||
|
||
super.init() | ||
|
||
requestString = "geojson(" | ||
requestString += GeoJSON.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)! | ||
requestString += ")" | ||
|
||
} | ||
|
||
} | ||
|
||
public class Path: Overlay { | ||
|
||
// based on https://github.com/mapbox/polyline | ||
|
||
private func polylineEncode(coordinates: [CLLocationCoordinate2D]) -> String { | ||
|
||
func encodeCoordinate(var coordinate: CLLocationDegrees) -> String { | ||
|
||
var c = Int(round(coordinate * 1e5)) | ||
|
||
c = c << 1 | ||
|
||
if (c < 0) { | ||
c = ~c | ||
} | ||
|
||
var output = "" | ||
|
||
while (c >= 0x20) { | ||
output += String(UnicodeScalar((0x20 | (c & 0x1f)) + 63)) | ||
c = c >> 5 | ||
} | ||
|
||
output += String(UnicodeScalar(c + 63)) | ||
|
||
return output | ||
} | ||
|
||
var output = encodeCoordinate(coordinates[0].latitude) + encodeCoordinate(coordinates[0].longitude) | ||
|
||
for var i = 1; i < countElements(coordinates); ++i { | ||
let a = coordinates[i] | ||
let b = coordinates[i - 1] | ||
output += encodeCoordinate(a.latitude - b.latitude) | ||
output += encodeCoordinate(a.longitude - b.longitude) | ||
} | ||
|
||
return output.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)! | ||
} | ||
|
||
init(pathCoordinates: [CLLocationCoordinate2D], | ||
strokeWidth: Int = 1, | ||
strokeColor: UIColor = UIColor.colorWithHexString("555"), | ||
strokeOpacity: CGFloat = 1.0, | ||
fillColor: UIColor = UIColor.colorWithHexString("555"), | ||
fillOpacity: CGFloat = 0) { | ||
|
||
super.init() | ||
|
||
requestString = "path" | ||
requestString += "-\(strokeWidth)" | ||
requestString += "+" + strokeColor.toHexString() | ||
requestString += "-\(strokeOpacity)" | ||
requestString += "+" + fillColor.toHexString() | ||
requestString += "-\(fillOpacity)" | ||
requestString += "(" | ||
requestString += polylineEncode(pathCoordinates) | ||
requestString += ")" | ||
|
||
} | ||
|
||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
# MapboxStatic | ||
|
||
MapboxStatic is a Swift library for Mapbox's [static maps API](https://www.mapbox.com/developers/api/static/), with support for overlays, asynchronous imagery fetching, and first-class Swift data types. | ||
|
||
Static maps are flattened PNG or JPG images, ideal for use in table views, image views, and anyplace else you'd like a quick, custom map without the overhead of an interactive view. | ||
|
||
## Installation | ||
|
||
Drag the `MapboxStatic.swift` file into your project. | ||
|
||
## Usage | ||
|
||
You will need a [map ID](https://www.mapbox.com/foundations/glossary/#mapid) from a [custom map style](https://www.mapbox.com/foundations/customizing-the-map) on your Mapbox account. You will also need an [access token](https://www.mapbox.com/developers/api/#access-tokens) in order to use the API. | ||
|
||
### Basics | ||
|
||
The main map class is `MapboxStaticMap`. To create a basic map, specify the center, zoom level, pixel size, and retina scale: | ||
|
||
```swift | ||
let map = MapboxStaticMap( | ||
mapID: <your map ID>, | ||
center: CLLocationCoordinate2D(latitude: 45.52, longitude: -122.681944), | ||
zoom: 13, | ||
size: CGSize(width: 500, height: 300), | ||
accessToken: <your API token>, | ||
retina: (UIScreen.mainScreen().scale > 1.0) | ||
) | ||
``` | ||
|
||
Then, to retrieve an image, you can do it either synchronously (blocking the calling thread): | ||
|
||
```swift | ||
self.imageView.image = map.image | ||
``` | ||
|
||
![](./screenshot_map.png) | ||
|
||
Or you can pass a completion handler to update the UI thread when after the image is retrieved: | ||
|
||
```swift | ||
map.imageWithCompletionHandler({ (image: UIImage?) in | ||
self.imageView.image = image | ||
}) | ||
``` | ||
|
||
If you're using your own HTTP library or routines, you can also retrieve a map object's `requestURL` property. | ||
|
||
```swift | ||
let requestURLToFetch = map.requestURL | ||
``` | ||
|
||
### Overlays | ||
|
||
Overlays are where things get interesting! You can add [Maki markers](https://www.mapbox.com/maki/), GeoJSON geometries, and even paths made of bare coordinates. | ||
|
||
You pass overlays as the `overlays: [Overlays]` parameter during map creation. Here are some versions of our map with various overlays added. | ||
|
||
#### Marker | ||
|
||
```swift | ||
let markerOverlay = MapboxStaticMap.Marker( | ||
coordinate: CLLocationCoordinate2D(latitude: 45.52, longitude: -122.681944), | ||
size: .Medium, | ||
label: "cafe", | ||
color: UIColor.brownColor() | ||
) | ||
``` | ||
|
||
![](./screenshot_marker.png) | ||
|
||
#### GeoJSON | ||
|
||
```swift | ||
let GeoJSONOverlay = MapboxStaticMap.GeoJSON( | ||
GeoJSON: NSString( | ||
contentsOfFile: NSBundle.mainBundle().pathForResource("sample", ofType: "geojson")!, | ||
encoding: NSUTF8StringEncoding, | ||
error: nil | ||
)! | ||
) | ||
``` | ||
|
||
![](./screenshot_geojson.png) | ||
|
||
#### Path | ||
|
||
```swift | ||
let path = MapboxStaticMap.Path( | ||
pathCoordinates: [ | ||
CLLocationCoordinate2D( | ||
latitude: 45.52475063103141, longitude: -122.68209457397461 | ||
), | ||
CLLocationCoordinate2D( | ||
latitude: 45.52451009822193, longitude: -122.67488479614258 | ||
), | ||
CLLocationCoordinate2D( | ||
latitude: 45.51681250530043, longitude: -122.67608642578126 | ||
), | ||
CLLocationCoordinate2D( | ||
latitude: 45.51693278828882, longitude: -122.68999099731445 | ||
), | ||
CLLocationCoordinate2D( | ||
latitude: 45.520300607576864, longitude: -122.68964767456055 | ||
), | ||
CLLocationCoordinate2D( | ||
latitude: 45.52475063103141, longitude: -122.68209457397461 | ||
) | ||
], | ||
strokeWidth: 2, | ||
strokeColor: UIColor.blackColor(), | ||
fillColor: UIColor.redColor(), | ||
fillOpacity: 0.25 | ||
) | ||
``` | ||
|
||
![](./screenshot_path.png) | ||
|
||
### Other options | ||
|
||
When creating a map, you can also specify PNG or JPEG image format as well as various [bandwidth-saving image qualities](https://www.mapbox.com/developers/api/static/#format). | ||
|
||
Be sure to [attribute your map](https://www.mapbox.com/developers/api/static/#attribution) properly! | ||
|
||
### More info | ||
|
||
For more info about the Mapbox static maps API, check out the [web service documentation](https://www.mapbox.com/developers/api/static/). |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.