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

Attributes #118

Merged
merged 18 commits into from
Apr 10, 2017
Merged

Attributes #118

merged 18 commits into from
Apr 10, 2017

Conversation

bsudekum
Copy link

@bsudekum bsudekum commented Mar 21, 2017

Adds optional query param annotation. This is an array of annotation types, allowed enum values: .distance, .duration, .speed, .node.

Still need to add documentation.

/cc @1ec5

// MapboxDirections
//
// Created by Bobby Sudekum on 3/20/17.
// Copyright © 2017 Mapbox. All rights reserved.
Copy link
Contributor

Choose a reason for hiding this comment

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

Omit this file header comment. This is a permissively licensed project under version control, so the comment adds little value.

@@ -98,6 +130,9 @@ open class RouteLeg: NSObject, NSSecureCoding {
*/
open let steps: [RouteStep]


open let annotation: [AnnotationType:[Any]]?
Copy link
Contributor

Choose a reason for hiding this comment

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

Annotation is a poor name for this information for the following reasons:

  • It's a collection of data, so a plural word is preferable.
  • The Mapbox iOS and macOS SDKs already have a concept of annotations (for consistency with MapKit) that's completely different: markers and overlays on a map. Google Maps calls them markers, but developers are required to use the Mapbox SDKs with this library anyways.
  • The term doesn't say anything about the relationship between the route leg and the information indicated by the property.

Some suggestions:

  • nodeAttributes (so far only for the nodeIdentifiers key) and segmentAttributes (for the other keys)
  • attributes
  • userInfo – Although this may sound strange, it's consistent with Cocoa APIs like NSError and NSNotification.

Copy link
Contributor

Choose a reason for hiding this comment

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

With a split between "node attributes" and "segment attributes", we'll eventually be able to distribute the attribute values among each step (while still retaining the full, leg-level attribute structure as an option).

Copy link
Contributor

Choose a reason for hiding this comment

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

A little trivia: "segment" used to be a primitive data type in OpenStreetMap back in the day.


case duration

case nodes
Copy link
Contributor

Choose a reason for hiding this comment

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

(I'm going to call annotation types "attributes" for the rest of this review; see below.)

Let's call this openStreetMapNodeIdentifier, similar to how MapboxGeocoder.swift has a wikidataItemIdentifier. If on the other hand we expect the same underlying string to be used for non-OSM data sources, nodeIdentifier would be better. (I'd like to avoid using "node" by itself, because perhaps we may associate more attributes with OSM nodes in the future.)

@objc(AnnotationType)
public enum AnnotationType: Int, CustomStringConvertible {

case congestion
Copy link
Contributor

Choose a reason for hiding this comment

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

"Congestion" is a strange word to use on its own, since it's uncountable. (Imagine talking about "congestions".) Let's call it congestionLevel.


var annotation: Annotation = [:]
if json["annotation"] != nil {
let rawAnnoation = json["annotation"] as! [String: Any]
Copy link
Contributor

Choose a reason for hiding this comment

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

s/annoation/annotation/

if json["annotation"] != nil {
let rawAnnoation = json["annotation"] as! [String: Any]
if rawAnnoation["congestion"] != nil {
annotation[.congestion] = (rawAnnoation["congestion"] as! [String]).map { CongestionLevel(description: $0) ?? .unknown}
Copy link
Contributor

Choose a reason for hiding this comment

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

To avoid repeating yourself, use an if let … as? … above`.

@@ -256,6 +258,15 @@ open class RouteOptions: NSObject {
params.append(URLQueryItem(name: "radiuses", value: accuracies))
}


if annotation.count > 0 {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: !annotation.isEmpty

@@ -211,6 +211,8 @@ open class RouteOptions: NSObject {
*/
open var routeShapeResolution = RouteShapeResolution.low

open var annotation: [AnnotationType] = []
Copy link
Contributor

Choose a reason for hiding this comment

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

attributes

@@ -40,6 +68,9 @@ open class RouteLeg: NSObject, NSSecureCoding {
}
source = decodedSource


annotation = nil
Copy link
Contributor

Choose a reason for hiding this comment

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

The encode method encodes the attribute data, so the decode method should decode it.


case distance

case duration
Copy link
Contributor

Choose a reason for hiding this comment

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

"Durations" can indicate lots of things. Let's call this expectedTravelTime, for consistency with the expectedTravelTime property of RouteLeg and RouteStep.

@bsudekum
Copy link
Author

@1ec5 updated with feedback

@@ -15,6 +15,8 @@
358D48871E2EAB4500F32A65 /* MBRouteOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 358D48851E2EAB4500F32A65 /* MBRouteOptions.m */; };
358D48881E2EAB4500F32A65 /* MBRouteOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 358D48851E2EAB4500F32A65 /* MBRouteOptions.m */; };
358D48891E2EAB4500F32A65 /* MBRouteOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 358D48851E2EAB4500F32A65 /* MBRouteOptions.m */; };
C51538CC1E807FF00093FF3E /* MBAnnotation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C51538CB1E807FF00093FF3E /* MBAnnotation.swift */; };
Copy link
Contributor

Choose a reason for hiding this comment

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

This file needs to be added to all four framework targets. Because it’s only in the iOS framework target, macOS, tvOS, and watchOS builds are failing with numerous errors, starting with:

▸ Compiling MBRouteLeg.swift

❌  /Users/vagrant/git/MapboxDirections/MBRouteLeg.swift:11:36: use of undeclared type 'SegmentAttribute'

    typealias SegmentAttributes = [SegmentAttribute:[Any]]
                                   ^~~~~~~~~~~~~~~~



❌  /Users/vagrant/git/MapboxDirections/MBRouteLeg.swift:129:34: use of undeclared type 'SegmentAttribute'

    open let segmentAttributes: [SegmentAttribute:[Any]]?
                                 ^~~~~~~~~~~~~~~~



❌  /Users/vagrant/git/MapboxDirections/MBRouteLeg.swift:173:14: cannot invoke 'RouteLegV4.init' with an argument list of type '(steps: [RouteStepV4], json: JSONDictionary, source: Waypoint, destination: Waypoint, profileIdentifier: MBDirectionsProfileIdentifier)'

        self.init(steps: steps, json: json, source: source, destination: destination, profileIdentifier: profileIdentifier)
             ^

@@ -15,6 +15,8 @@
358D48871E2EAB4500F32A65 /* MBRouteOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 358D48851E2EAB4500F32A65 /* MBRouteOptions.m */; };
358D48881E2EAB4500F32A65 /* MBRouteOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 358D48851E2EAB4500F32A65 /* MBRouteOptions.m */; };
358D48891E2EAB4500F32A65 /* MBRouteOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 358D48851E2EAB4500F32A65 /* MBRouteOptions.m */; };
C51538CC1E807FF00093FF3E /* MBAnnotation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C51538CB1E807FF00093FF3E /* MBAnnotation.swift */; };
C5247D711E818A24004B6154 /* AnnotationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5247D701E818A24004B6154 /* AnnotationTests.swift */; };
Copy link
Contributor

Choose a reason for hiding this comment

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

Likewise, this file needs to be added to all four unit test targets.

@@ -0,0 +1,45 @@
import Foundation

@objc(AnnotationType)
Copy link
Contributor

Choose a reason for hiding this comment

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

This type needs a documentation comment.

@objc(AnnotationType)
public enum SegmentAttribute: Int, CustomStringConvertible {

case distance
Copy link
Contributor

Choose a reason for hiding this comment

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

Each of these cases needs a documentation comment.

import Foundation

@objc(AnnotationType)
public enum SegmentAttribute: Int, CustomStringConvertible {
Copy link
Contributor

Choose a reason for hiding this comment

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

This type should be called simply Attribute, because openStreetMapNodeIdentifier is a node attribute, not a segment attribute.

@@ -40,6 +63,9 @@ open class RouteLeg: NSObject, NSSecureCoding {
}
source = decodedSource


segmentAttributes = nil
Copy link
Contributor

Choose a reason for hiding this comment

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

@@ -256,6 +258,16 @@ open class RouteOptions: NSObject {
params.append(URLQueryItem(name: "radiuses", value: accuracies))
}


if segmentAttributes.count > 0 {
Copy link
Contributor

Choose a reason for hiding this comment

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


if segmentAttributes.count > 0 {
let segmentAttributesStrings = segmentAttributes.map {
$0.description
Copy link
Contributor

Choose a reason for hiding this comment

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

String(describing: $0)

XCTAssertNotNil(annotation[.distance])
XCTAssertNotNil(annotation[.expectedTravelTime])
XCTAssertNotNil(annotation[.openStreetMapNodeIdentifier])
XCTAssertNotNil(annotation[.speed])
Copy link
Contributor

Choose a reason for hiding this comment

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

These tests should also check specific attribute values.

XCTAssertNotNil(annotation[.openStreetMapNodeIdentifier])
XCTAssertNotNil(annotation[.speed])

let nodes = annotation[.openStreetMapNodeIdentifier]!.count
Copy link
Contributor

Choose a reason for hiding this comment

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

Check that there are the right number of node identifier attributes.

@1ec5 1ec5 changed the title Add annotation option Attributes Apr 1, 2017
@1ec5 1ec5 added the improvement Improvement for an existing feature. label Apr 1, 2017
Copy link
Contributor

@1ec5 1ec5 left a comment

Choose a reason for hiding this comment

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

Some bad news about Objective-C bridging, but it turns out that addressing these issues will make the API make more sense even in Swift.

@@ -59,6 +84,10 @@ open class RouteLeg: NSObject, NSSecureCoding {
return nil
}
profileIdentifier = MBDirectionsProfileIdentifier(rawValue: decodedProfileIdentifier)

segmentAttributes = decoder.decodeObject(of: [NSArray.self], forKey: "segmentAttributes") as? [Attribute: [Any]] ?? [:]
Copy link
Contributor

Choose a reason for hiding this comment

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

The code below encodes the object as a dictionary. This line tries to decode the object as an NSArray, which will produce nil. Even if it were to somehow produce an array, this line then casts the array to a dictionary, which will produce nil.

The class you pass into decodeObject(of:forKey:) should match the class of the object you pass into encode(_:forKey:) below.

@@ -211,6 +211,10 @@ open class RouteOptions: NSObject {
*/
open var routeShapeResolution = RouteShapeResolution.low

open var segmentAttributes: [Attribute] = []
Copy link
Contributor

Choose a reason for hiding this comment

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

An array of enum values won't bridge to Objective-C. Besides, we want an OptionSet for this particular property, since it doesn't make sense to specify .speeds twice, for example. I think we're going to need something similar to LaneIndication – an Objective-C NS_OPTION instead of this Swift enumeration.

@@ -98,6 +129,11 @@ open class RouteLeg: NSObject, NSSecureCoding {
*/
open let steps: [RouteStep]


open let segmentAttributes: [Attribute:[Any]]?
Copy link
Contributor

Choose a reason for hiding this comment

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

This property won't bridge to Objective-C, because NSDictionary's keys need to be objects, not enumeration values. (Option sets won't bridge either.) We'll probably want to use an Objective-C string enumeration (NS_STRING_ENUM) instead of the Swift enumeration. It would be separate from the option set used in RouteOptions.

If that's too much trouble, RouteLeg could have a separate property for each attribute. For example, segmentSpeeds would be of type [CLLocationSpeed]. That would be pretty clean, but I don't know what the story will be for any unsupported attributes we get back from the API.


MBAttributeSpeed = (1 << 4),

MBAttributeAll = 0x0ffffUL
Copy link
Contributor

Choose a reason for hiding this comment

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

We probably don’t need an “all” option for attributes.

@bsudekum
Copy link
Author

bsudekum commented Apr 5, 2017

@1ec5 this is ready for another round of 👀

Copy link
Contributor

@1ec5 1ec5 left a comment

Choose a reason for hiding this comment

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

This is coming together nicely. The tests are looking good too. Don’t forget to add documentation for any new types and properties.

binary "https://www.mapbox.com/ios-sdk/Mapbox-iOS-SDK.json" "3.4.2"
github "AliSoftware/OHHTTPStubs" "57feceaabf333e72b2c637dfba6c13a7a5c49619"
binary "https://www.mapbox.com/ios-sdk/Mapbox-iOS-SDK.json" "3.5.0"
github "AliSoftware/OHHTTPStubs" "f90c2bb0fb882e43761ab963ca8869d349d2c6e3"
Copy link
Contributor

Choose a reason for hiding this comment

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

This is fine, but in the future, let’s split out any changes that occur due to running carthage update into separate PRs, so that we can narrow down any regressions they cause. If you just need to get set up, use carthage bootstrap instead.

Copy link
Author

Choose a reason for hiding this comment

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

I'll revert this

self.segmentAttributes = segmentAttributes
self.nodeAttributes = nodeAttributes

var segmentDistances: [CLLocationDistance]? = nil
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: an optional is nil by default; no need to set it to nil.


open let nodeAttributes: [Attribute:[Any]]?
open let nodeOpenStreetMapNodeIdentifiers: [Int]?
Copy link
Contributor

Choose a reason for hiding this comment

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

To avoid overflow, OSM node IDs must be stored as 64-bit integers. Int can be either 32-bit or 64-bit according to the device’s architecture. To be on the safe side and to clarify our intent, use Int64.


open let nodeAttributes: [Attribute:[Any]]?
open let nodeOpenStreetMapNodeIdentifiers: [Int]?
Copy link
Contributor

Choose a reason for hiding this comment

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

“Node” is in the name twice, unnecessarily: rename this property to openStreetMapNodeIdentifiers.

@@ -129,10 +134,13 @@ open class RouteLeg: NSObject, NSSecureCoding {
*/
open let steps: [RouteStep]

open let segmentDistances: [CLLocationDistance]?

open let segmentExpectedTravelTimes: [TimeInterval]?
Copy link
Contributor

Choose a reason for hiding this comment

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

The name of this property makes it sound like a verb, “to segment the expected travel times”. Rename this property to expectedSegmentTravelTimes.

String(describing: $0)
}.joined(separator: ",")
}.joined(separator: ",")
Copy link
Contributor

Choose a reason for hiding this comment

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

Attribute.description is already a comma-separated list, so you can set attributesStrings to String(describing: segmentAttributes + .openStreetMapNodeIdentifier) to get the same effect.

@@ -211,6 +211,10 @@ open class RouteOptions: NSObject {
*/
open var routeShapeResolution = RouteShapeResolution.low

open var segmentAttributes: [Attribute] = []
Copy link
Contributor

Choose a reason for hiding this comment

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

Now that Attribute is an OptionSet, we no longer need an array of Attributes; a single Attribute can store multiple options.

@@ -211,6 +211,10 @@ open class RouteOptions: NSObject {
*/
open var routeShapeResolution = RouteShapeResolution.low

open var segmentAttributes: [Attribute] = []

open var nodeAttributes: [Attribute] = []
Copy link
Contributor

Choose a reason for hiding this comment

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

Compared to the situation in RouteLeg before, there’s no need to distinguish between segment and node attributes in RouteOptions – the two behave exactly the same as far as requesting a route is concerned. A single attributeOptions property will do.

@@ -0,0 +1,10 @@
typedef NS_OPTIONS(NSUInteger, MBAttribute) {
Copy link
Contributor

Choose a reason for hiding this comment

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

One more rename! Now that this is an option set instead of a single attribute, rename this type to MBAttributeOptions, so it’s easier to talk about individual attributes versus the whole set of attributes.

Bobby Sudekum added 2 commits April 7, 2017 11:21
@bsudekum
Copy link
Author

bsudekum commented Apr 7, 2017

@1ec5 fixed

Copy link
Contributor

@1ec5 1ec5 left a comment

Choose a reason for hiding this comment

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

A little more finessing around options, plus documentation, and I think this will be good to go!

@@ -211,6 +211,10 @@ open class RouteOptions: NSObject {
*/
open var routeShapeResolution = RouteShapeResolution.low

open var segmentAttributes: AttributeOptions = []

open var nodeAttributes: AttributeOptions = []
Copy link
Contributor

Choose a reason for hiding this comment

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

Combine these two properties into a single attributes property.

Bobby Sudekum added 2 commits April 9, 2017 09:19
@bsudekum
Copy link
Author

bsudekum commented Apr 9, 2017

Started documentation in 5c2c5c0

@@ -1,10 +1,17 @@
/**
Attributes are metadata information for a given route. The number of attributes returned will be a direct 1-1 relationship with the route's full geometry. Each type will return an ordered list of requested attributes. For `.distance`, `.expectedTrabelTime`, and `.speed` there will be one less value when compared to the route geometry. This is because these values represent the data on segment between geometry points.
Copy link
Contributor

Choose a reason for hiding this comment

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

The .distance, .expectedTrabelTime, and .speed attributes have one fewer value than the .openStreetMapNodeIdentifier attribute.

Creates an AttributeOptions from the given description strings.
*/
public init?(description: String) {
var scope: AttributeOptions = []
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: funky indentation.

*/
typedef NS_OPTIONS(NSUInteger, MBAttributeOptions) {

/// Segment distance.
Copy link
Contributor

Choose a reason for hiding this comment

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

Distances are measured in meters.

// Segment expected travel time in seconds.
MBAttributeExpectedTravelTime = (1 << 2),

// Segment current speed.
Copy link
Contributor

Choose a reason for hiding this comment

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

Speeds are measured in meters per second.

// Segment current speed.
MBAttributeSpeed = (1 << 3),

// Unique OpenStreetMap node.
Copy link
Contributor

Choose a reason for hiding this comment

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

This option returns unique node identifiers, not the nodes themselves. Link to this OpenStreetMap Wiki article for more information. Also note that this attribute option is added automatically when any of the other attributes are added.

Copy link
Author

Choose a reason for hiding this comment

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

Also note that this attribute option is added automatically when any of the other attributes are added.

This is not true:
image

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, I thought we had baked that logic into RouteOptions, but it seems to have been removed. 👍

@@ -211,6 +211,13 @@ open class RouteOptions: NSObject {
*/
open var routeShapeResolution = RouteShapeResolution.low

/**
AttributeOptions for the route. Any combination of `.AttributeOptions` can be specified.
Copy link
Contributor

Choose a reason for hiding this comment

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

Remove the extra dot: .AttributeOptions implies that there’s an AttributeOptions.AttributeOptions.

/**
AttributeOptions for the route. Any combination of `.AttributeOptions` can be specified.

The default value of this property is `[]`.
Copy link
Contributor

Choose a reason for hiding this comment

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

By default, no attribute options are specified.

@@ -211,6 +211,13 @@ open class RouteOptions: NSObject {
*/
open var routeShapeResolution = RouteShapeResolution.low

/**
AttributeOptions for the route. Any combination of `.AttributeOptions` can be specified.

Copy link
Contributor

Choose a reason for hiding this comment

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

Note that this option requires the routeShapeResolution property to be set to .full. (The default is .low.)

Copy link
Author

Choose a reason for hiding this comment

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

Unfortunately, this is not true. This should be ticketed upstream.

Copy link
Contributor

Choose a reason for hiding this comment

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

Can you elaborate? Do the attributes still work with .low resolution? Do the values make any sense?

Copy link
Contributor

Choose a reason for hiding this comment

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

If it’s the case that .low resolution yields the same values (which reflect the .full geometry), let’s recommend that the developer set .full to make use of these options.

open let expectedSegmentTravelTimes: [TimeInterval]?

/**
An array of current speeds along segment. This value is dynamic and does not represent the speed limit but rather the average speed for a given segment
Copy link
Contributor

Choose a reason for hiding this comment

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

An array containing the expected average speed (measured in meters per second) between each coordinate in the route leg geometry.

These values are dynamic; rather than speed limits, they account for the road’s classification and/or any traffic congestion (if the profile identifier is set to .automobileAvoidingTraffic).

This property is set if the RouteOptions.attributeOptions property contains .speed.

open let segmentSpeeds: [CLLocationSpeed]?

/**
An array of OpenStreetMap nodes.
Copy link
Contributor

Choose a reason for hiding this comment

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

An array containing OpenStreetMap node identifiers, one for each coordinate along the route geometry.

This property is set if the RouteOptions.attributeOptions property contains .openStreetMapNodeIdentifier.

@@ -0,0 +1,17 @@
/**
Attributes are metadata information for a given route. The number of attributes returned will be a direct 1-1 relationship with the route's full geometry. The .distance, .expectedTrabelTime, and .speed attributes have one fewer value than the .openStreetMapNodeIdentifier attribute.
Copy link
Contributor

Choose a reason for hiding this comment

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

Typo: s/expectedTrabelTime/expectedTravelTime/

@@ -0,0 +1,17 @@
/**
Attributes are metadata information for a given route. The number of attributes returned will be a direct 1-1 relationship with the route's full geometry. The .distance, .expectedTrabelTime, and .speed attributes have one fewer value than the .openStreetMapNodeIdentifier attribute.
Copy link
Contributor

Choose a reason for hiding this comment

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

Surround references like .distance in backticks so jazzy can autolink them.

// Segment current speed. Speeds are measured in meters per second.
MBAttributeSpeed = (1 << 3),

// Unique node identifiers (https://wiki.openstreetmap.org/wiki/Node)
Copy link
Contributor

Choose a reason for hiding this comment

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

Use Markdown syntax:

[OpenStreetMap node identifier](https://wiki.openstreetmap.org/wiki/Node).

open let segmentSpeeds: [CLLocationSpeed]?

/**
An array containing OpenStreetMap node identifiers (https://wiki.openstreetmap.org/wiki/Node), one for each coordinate along the route geometry.
Copy link
Contributor

Choose a reason for hiding this comment

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

Use Markdown syntax here too.

@bsudekum
Copy link
Author

@1ec5 think this is ready to go

Copy link
Contributor

@1ec5 1ec5 left a comment

Choose a reason for hiding this comment

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

Whew! Thanks for all the iterations; this looks great.

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

Successfully merging this pull request may close these issues.

2 participants