Skip to content

Commit

Permalink
Merge pull request #23 from mapbox/1ec5-overlay-struct
Browse files Browse the repository at this point in the history
Rewrite overlay types
  • Loading branch information
1ec5 committed May 15, 2016
2 parents dee5ecd + 7bfc54b commit 8a9d50d
Show file tree
Hide file tree
Showing 7 changed files with 258 additions and 82 deletions.
308 changes: 242 additions & 66 deletions MapboxStatic/Overlay.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,81 +11,266 @@ let allowedCharacterSet: NSCharacterSet = {
return characterSet
}()

public enum MarkerSize: String {
case Small = "s"
case Medium = "m"
case Large = "l"
/**
A feature that can be drawn atop the map.
*/
public protocol Overlay: CustomStringConvertible {
var description: String { get }
}

public class Overlay: CustomStringConvertible {
/**
A feature centered over a specific geographic coordinate.
*/
public protocol Point: Overlay {
/// The geographic coordinate to place the point at.
var coordinate: CLLocationCoordinate2D { get }
}

/**
A pin-shaped marker placed at a specific point on the map.

The Maki icon set is [open source](https://github.com/mapbox/maki/) and [dedicated to the public domain](https://creativecommons.org/publicdomain/zero/1.0/).
*/
public struct Marker: Point {
#if os(iOS)
public typealias Color = UIColor
#elseif os(OSX)
public typealias Color = NSColor
#endif

internal var requestString: String = ""
/**
The size of a marker.
*/
public enum Size: String {
/// Small.
case Small = "s"
/// Medium.
case Medium = "m"
/// Large.
case Large = "l"
}

public var description: String {
return requestString
/// Something simple that can be placed atop a marker.
public enum Label: CustomStringConvertible {
/// A lowercase English letter from A through Z. An uppercase letter is automatically converted to a lowercase letter.
case Letter(Character)
/// A number from 0 through 99.
case Number(Int)
/// The name of a [Maki](https://www.mapbox.com/maki-icons/) icon.
case IconName(String)

public var description: String {
switch self {
case .Letter(let letter):
let lower = "\(letter)".lowercaseString
assert(letter >= "a" && letter <= "z")
return lower
case .Number(let number):
assert(number >= 0 && number < 100)
return "\(number)"
case .IconName(let name):
return "\(name)"
}
}
}

}

public class Marker: Overlay {

/// The geographic coordinate to place the marker at.
public let coordinate: CLLocationCoordinate2D

/**
The size of the marker.

By default, the marker is small.
*/
public let size: Size

/**
A label or Maki icon to place atop the pin.

By default, the marker has no label.
*/
public let label: Label?

/**
The color of the pin part of the marker.

By default, the marker is red.
*/
public let color: Color

/**
Initializes a marker with the given options.

- parameter coordinate: The geographic coordinate to place the marker at.
- parameter size: The size of the marker.
- parameter label: A label or Maki icon to place atop the pin.
*/
public init(coordinate: CLLocationCoordinate2D,
size: MarkerSize = .Small,
label: String = "",
color: Color = .redColor()) {

super.init()

requestString = "pin-"
requestString += size.rawValue

if label.lengthOfBytesUsingEncoding(NSUTF8StringEncoding) > 0 {
requestString += "-" + label
size: Size = .Small,
label: Label? = nil,
color: Color = .redColor()) {
self.coordinate = coordinate
self.size = size
self.label = label
self.color = color
}

public var description: String {
let labelComponent: String
if let label = label {
labelComponent = "-\(label)"
} else {
labelComponent = ""
}

requestString += "+" + color.toHexString()
requestString += "(\(coordinate.longitude),\(coordinate.latitude))"

return "pin-\(size.rawValue)\(labelComponent)+\(color.toHexString())(\(coordinate.longitude),\(coordinate.latitude))"
}

}

public class CustomMarker: Overlay {

public init(coordinate: CLLocationCoordinate2D,
URL: NSURL) {

super.init()

requestString = "url-"
requestString += URL.absoluteString.stringByAddingPercentEncodingWithAllowedCharacters(allowedCharacterSet)!
requestString += "(\(coordinate.longitude),\(coordinate.latitude))"
/**
A custom, online image placed at a specific point on the map.

The marker image is always centered on the specified location. When creating an asymmetric marker like a pin, make sure that the tip of the pin is at the center of the image.
*/
public struct CustomMarker: Overlay {
/// The geographic coordinate to place the marker at.
public let coordinate: CLLocationCoordinate2D

/**
The HTTP or HTTPS URL of the image.

The API caches custom marker images according to the `Expires` and `Cache-Control` headers. If you host the image on your own server, make sure that at least one of these headers is set to an proper value to prevent repeated requests for the image.
*/
public let URL: NSURL

/**
Initializes a marker with the given coordinate and image URL.

- parameter coordinate: The geographic coordinate to place the marker at.
- parameter URL: The HTTP or HTTPS URL of the image.
*/
public init(coordinate: CLLocationCoordinate2D, URL: NSURL) {
self.coordinate = coordinate
self.URL = URL
}

public var description: String {
let escapedURL = URL.absoluteString.stringByAddingPercentEncodingWithAllowedCharacters(allowedCharacterSet)!
return "url-\(escapedURL)(\(coordinate.longitude),\(coordinate.latitude))"
}

}

public class GeoJSON: Overlay {

public init(string: String) {

super.init()

requestString = "geojson("
requestString += string.stringByAddingPercentEncodingWithAllowedCharacters(allowedCharacterSet)!
requestString += ")"

/**
A geographic object in [GeoJSON](https://www.mapbox.com/help/define-geojson/) format.

GeoJSON features may be styled according to the [simplestyle specification](https://github.com/mapbox/simplestyle-spec).
*/
public struct GeoJSON: Overlay {
/// String representation of the GeoJSON object to display.
public let objectString: String

public var description: String {
let escapedObjectString = objectString.stringByAddingPercentEncodingWithAllowedCharacters(allowedCharacterSet)!
return "geojson(\(escapedObjectString))"
}

/**
Initializes a [GeoJSON](https://www.mapbox.com/help/define-geojson/) overlay with the given GeoJSON object.

- parameter object: A valid GeoJSON object.
- throws: If the given object is not a valid JSON object. This initializer does not check whether the object is valid GeoJSON, but invalid GeoJSON will cause the request to fail.
*/
public init(object: [String: AnyObject]) throws {
let data = try NSJSONSerialization.dataWithJSONObject(object, options: [])
objectString = String(data: data, encoding: NSUTF8StringEncoding)!
}

/**
Initializes a [GeoJSON](https://www.mapbox.com/help/define-geojson/) overlay with the given string representation of a GeoJSON object.

This initializer does not check whether the object is valid JSON or GeoJSON, but invalid JSON or GeoJSON will cause the request to fail. To perform basic JSON validation (but not GeoJSON validation), use the `init(object:)` initializer.

- parameter objectString: The string representation of a valid GeoJSON object.
*/
public init(objectString: String) {
self.objectString = objectString
}

}

public class Path: Overlay {

/**
A polyline or polygon placed along a path atop the map.
*/
public struct Path: Overlay {
#if os(iOS)
public typealias Color = UIColor
#elseif os(OSX)
public typealias Color = NSColor
#endif

/**
An array of geographic coordinates defining the path of the overlay.
*/
public let coordinates: [CLLocationCoordinate2D]

/**
The stroke width of the overlay, measured in points.

By default, the overlay is 1 point wide.
*/
public let strokeWidth: Int

/**
The stroke color of the overlay.

By default, the overlay is stroked with Davy’s gray (33% white).
*/
public let strokeColor: Color

/**
The stroke opacity of the overlay, expressed as a percentage such that 0.0 is completely transparent and 1.0 is completely opaque.

By default, the overlay’s stroke is completely opaque.
*/
public let strokeOpacity: Double

/**
The fill color of the overlay.

By default, the overlay is filled with Davy’s gray (33% white).
*/
public let fillColor: Color

/**
The fill opacity of the overlay, expressed as a percentage such that 0.0 is completely transparent and 1.0 is completely opaque.

By default, the overlay’s fill is completely transparent.
*/
public let fillOpacity: Double

/**
Initializes a polyline or polygon overlay with the given options.

- parameter coordinates: An array of geographic coordinates defining the path of the overlay.
- parameter strokeWidth: The stroke width of the overlay, measured in points.
- parameter strokeColor: The stroke color of the overlay.
- parameter strokeOpacity: The stroke opacity of the overlay, expressed as a percentage such that 0.0 is completely transparent and 1.0 is completely opaque.
- parameter fillColor: The fill color of the overlay.
- parameter fillOpacity: The fill opacity of the overlay, expressed as a percentage such that 0.0 is completely transparent and 1.0 is completely opaque.
*/
public init(coordinates: [CLLocationCoordinate2D],
strokeWidth: Int = 1,
strokeColor: Color = Color(hexString: "555"),
strokeOpacity: Double = 1.0,
fillColor: Color = Color(hexString: "555"),
fillOpacity: Double = 0) {
self.coordinates = coordinates
self.strokeWidth = strokeWidth
self.strokeColor = strokeColor
self.strokeOpacity = strokeOpacity
self.fillColor = fillColor
self.fillOpacity = fillOpacity
}

// based on https://github.com/mapbox/polyline

private func polylineEncode(coordinates: [CLLocationCoordinate2D]) -> String {

func encodeCoordinate(let coordinate: CLLocationDegrees) -> String {
Expand Down Expand Up @@ -121,18 +306,9 @@ public class Path: Overlay {

return output.stringByAddingPercentEncodingWithAllowedCharacters(allowedCharacterSet)!
}

public init(pathCoordinates: [CLLocationCoordinate2D],
strokeWidth: Int = 1,
strokeColor: Color = Color(hexString: "555"),
strokeOpacity: Double = 1.0,
fillColor: Color = Color(hexString: "555"),
fillOpacity: Double = 0) {

super.init()

requestString = "path-\(strokeWidth)+\(strokeColor.toHexString())-\(strokeOpacity)+\(fillColor.toHexString())-\(fillOpacity)(\(polylineEncode(pathCoordinates)))"


public var description: String {
let encodedPolyline = polylineEncode(coordinates)
return "path-\(strokeWidth)+\(strokeColor.toHexString())-\(strokeOpacity)+\(fillColor.toHexString())-\(fillOpacity)(\(encodedPolyline))"
}

}
10 changes: 5 additions & 5 deletions MapboxStaticTests/MapboxStaticTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ class MapboxStaticTests: XCTestCase {
func testOverlayBuiltinMarker() {
let lat = 45.52
let lon = -122.681944
let size = MarkerSize.Medium
let size = Marker.Size.Medium
let label = "cafe"
let color = Color.brownColor()
let colorRaw = "996633"
Expand All @@ -241,7 +241,7 @@ class MapboxStaticTests: XCTestCase {
let markerOverlay = Marker(
coordinate: CLLocationCoordinate2D(latitude: lat, longitude: lon),
size: size,
label: label,
label: .IconName("cafe"),
color: color)

var options = SnapshotOptions(
Expand Down Expand Up @@ -310,7 +310,7 @@ class MapboxStaticTests: XCTestCase {
}

let geojsonString = try! NSString(contentsOfURL: geojsonURL, encoding: NSUTF8StringEncoding)
let geojsonOverlay = GeoJSON(string: geojsonString as String)
let geojsonOverlay = GeoJSON(objectString: geojsonString as String)

var options = SnapshotOptions(
mapIdentifiers: mapIdentifiers,
Expand Down Expand Up @@ -346,7 +346,7 @@ class MapboxStaticTests: XCTestCase {
let encodedPolyline = "(upztG%60jxkVn@al@bo@nFWzuAaTcAyZen@)"

let path = Path(
pathCoordinates: [
coordinates: [
CLLocationCoordinate2D(
latitude: 45.52475063103141, longitude: -122.68209457397461
),
Expand Down Expand Up @@ -404,7 +404,7 @@ class MapboxStaticTests: XCTestCase {
let markerOverlay = Marker(
coordinate: CLLocationCoordinate2D(latitude: 45.52, longitude: -122.681944),
size: .Medium,
label: "cafe",
label: .IconName("cafe"),
color: .brownColor())

var options = SnapshotOptions(
Expand Down
2 changes: 1 addition & 1 deletion OS X.playground/Contents.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ snapshot.requestURL
let markerOverlay = Marker(
coordinate: CLLocationCoordinate2D(latitude: 45.52, longitude: -122.681944),
size: .Medium,
label: "cafe",
label: .IconName("cafe"),
color: .brownColor())
options.overlays = [markerOverlay]
snapshot = Snapshot(
Expand Down
Loading

0 comments on commit 8a9d50d

Please sign in to comment.