Skip to content

Commit

Permalink
Draft: API cleanup (#41)
Browse files Browse the repository at this point in the history
- API cleanup, renamed some `BoundingBox` functions
- Missing Sendable conformance for some types
  • Loading branch information
trasch authored Jan 9, 2024
1 parent cd0f451 commit 271ffa1
Show file tree
Hide file tree
Showing 18 changed files with 145 additions and 38 deletions.
92 changes: 78 additions & 14 deletions Sources/GISTools/GeoJson/BoundingBox.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import Foundation
/// A GeoJSON bounding box.
public struct BoundingBox:
GeoJsonReadable,
Projectable,
CustomStringConvertible,
Sendable
{
Expand Down Expand Up @@ -42,7 +41,16 @@ public struct BoundingBox:
}

/// Create a bounding box from `coordinates` and an optional padding in kilometers.
public init?(coordinates: [Coordinate3D], paddingKilometers: Double = 0.0) {
@available(*, deprecated, renamed: "init(coordinates:padding:)", message: "Padding is now expressed in meters")
public init?(coordinates: [Coordinate3D], paddingKilometers: Double) {
self.init(coordinates: coordinates, padding: paddingKilometers * 1000.0)
}

/// Create a bounding box from `coordinates` and an optional padding.
///
/// - Parameters:
/// - padding: The padding, in meters
public init?(coordinates: [Coordinate3D], padding: CLLocationDistance = 0.0) {
guard !coordinates.isEmpty else { return nil }

self.projection = coordinates.first?.projection ?? .epsg4326
Expand All @@ -60,21 +68,21 @@ public struct BoundingBox:
northEast.longitude = max(northEast.longitude, currentLocationLongitude)
}

if paddingKilometers > 0.0 {
if padding > 0.0 {
switch projection {
case .epsg3857:
southWest.latitude -= paddingKilometers * 1000.0
northEast.latitude += paddingKilometers * 1000.0
southWest.longitude -= paddingKilometers * 1000.0
northEast.longitude += paddingKilometers * 1000.0
southWest.latitude -= padding
northEast.latitude += padding
southWest.longitude -= padding
northEast.longitude += padding

case .epsg4326:
// Length of one minute at this latitude
let oneDegreeLongitudeDistanceInKilometers: Double = cos(southWest.latitude * Double.pi / 180.0) * 111.0
let oneDegreeLatitudeDistanceInKilometers = 111.0
let oneDegreeLatitudeDistance: CLLocationDistance = GISTool.earthCircumference / 360.0 // ~111 km
let oneDegreeLongitudeDistance: CLLocationDistance = cos(southWest.latitude * Double.pi / 180.0) * oneDegreeLatitudeDistance

let longitudeDistance: Double = (paddingKilometers / oneDegreeLongitudeDistanceInKilometers)
let latitudeDistance: Double = (paddingKilometers / oneDegreeLatitudeDistanceInKilometers)
let longitudeDistance: Double = (padding / oneDegreeLongitudeDistance)
let latitudeDistance: Double = (padding / oneDegreeLatitudeDistance)

southWest.latitude -= latitudeDistance
northEast.latitude += latitudeDistance
Expand Down Expand Up @@ -162,17 +170,34 @@ public struct BoundingBox:
}

/// Returns a copy of the receiver with some padding in kilometers.
@available(*, deprecated, renamed: "padded(_:)", message: "Padding is now expressed in meters")
public func with(padding paddingKilometers: Double) -> BoundingBox {
BoundingBox(
coordinates: [southWest, northEast],
paddingKilometers: paddingKilometers)!
}

/// Returns a copy of the receiver with some padding horizontally and vertically.
///
/// - Parameters:
/// - padding: The padding, in meters
public func padded(_ padding: CLLocationDistance) -> BoundingBox {
BoundingBox(
coordinates: [southWest, northEast],
padding: padding)!
}

/// Returns a copy of the receiver expanded by `degrees`.
@available(*, deprecated, renamed: "expanded(byDegrees:)", message: "Renamed to expaned(byDegrees:)")
public func expand(_ degrees: CLLocationDegrees) -> BoundingBox {
expanded(byDegrees: degrees)
}

/// Returns a copy of the receiver expanded by `degrees` horizontally and vertically.
public func expanded(byDegrees degrees: CLLocationDegrees) -> BoundingBox {
switch projection {
case .epsg3857:
return projected(to: .epsg4326).expand(degrees).projected(to: .epsg3857)
return projected(to: .epsg4326).expanded(byDegrees: degrees).projected(to: .epsg3857)

case .epsg4326:
return BoundingBox(
Expand All @@ -185,19 +210,40 @@ public struct BoundingBox:
}

/// Returns a copy of the receiver expanded by `distance` diagonally.
@available(*, deprecated, renamed: "expanded(byDistance:)", message: "Renamed to expaned(byDistance:)")
public func expand(distance: CLLocationDistance) -> BoundingBox {
expanded(byDistance: distance)
}

/// Returns a copy of the receiver expanded by `distance` diagonally.
///
/// - Parameters:
/// - distance: The distance from the receiver, in meters
public func expanded(byDistance distance: CLLocationDistance) -> BoundingBox {
BoundingBox(
southWest: southWest.destination(distance: distance, bearing: 225.0),
northEast: northEast.destination(distance: distance, bearing: 45.0))
}

/// Returns a copy of the receiver that also includes `coordinate`.
@available(*, deprecated, renamed: "expanded(byIncluding:)", message: "Renamed to expanded(byIncluding:)")
public func expand(including coordinate: Coordinate3D) -> BoundingBox {
expanded(byIncluding: coordinate)
}

/// Returns a copy of the receiver that also includes `coordinate`.
public func expanded(byIncluding coordinate: Coordinate3D) -> BoundingBox {
BoundingBox(coordinates: [southWest, northEast, coordinate.projected(to: projection)])!
}

/// Returns a copy of the receiver that also includes the other `boundingBox`.
@available(*, deprecated, renamed: "expanded(byIncluding:)", message: "Renamed to expanded(byIncluding:)")
public func expand(including boundingBox: BoundingBox) -> BoundingBox {
expanded(byIncluding: boundingBox)
}

/// Returns a copy of the receiver that also includes the other `boundingBox`.
public func expanded(byIncluding boundingBox: BoundingBox) -> BoundingBox {
BoundingBox(coordinates: [
southWest,
northEast,
Expand All @@ -215,7 +261,7 @@ public struct BoundingBox:

// MARK: - Projection

extension BoundingBox {
extension BoundingBox: Projectable {

/// Reproject this bounding box.
public func projected(to newProjection: Projection) -> BoundingBox {
Expand All @@ -234,20 +280,38 @@ extension BoundingBox {
extension BoundingBox {

/// Create a bounding box from `coordinates` and an optional padding in kilometers.
public init?(coordinates: [CLLocationCoordinate2D], paddingKilometers: Double = 0.0) {
@available(*, deprecated, renamed: "init(coordinates:padding:)", message: "Padding is now expressed in meters")
public init?(coordinates: [CLLocationCoordinate2D], paddingKilometers: Double) {
self.init(coordinates: coordinates.map({ Coordinate3D($0) }), paddingKilometers: paddingKilometers)
}

/// Create a bounding box from `coordinates` and an optional padding.
///
/// - Parameters:
/// - padding: The padding, in meters
public init?(coordinates: [CLLocationCoordinate2D], padding: Double = 0.0) {
self.init(coordinates: coordinates.map({ Coordinate3D($0) }), padding: padding)
}

/// Create a bounding box from a south-west and north-east coordinate.
public init(southWest: CLLocationCoordinate2D, northEast: CLLocationCoordinate2D) {
self.init(southWest: Coordinate3D(southWest), northEast: Coordinate3D(northEast))
}

/// Create a bounding box from `locations` and an optional padding in kilometers.
@available(*, deprecated, renamed: "init(locations:padding:)", message: "Padding is now expressed in meters")
public init?(locations: [CLLocation], paddingKilometers: Double = 0.0) {
self.init(coordinates: locations.map({ Coordinate3D($0) }), paddingKilometers: paddingKilometers)
}

/// Create a bounding box from `locations` and an optional padding.
///
/// - Parameters:
/// - padding: The padding, in meters
public init?(locations: [CLLocation], padding: Double = 0.0) {
self.init(coordinates: locations.map({ Coordinate3D($0) }), padding: padding)
}

/// Create a bounding box from a south-west and north-east coordinate.
public init(southWest: CLLocation, northEast: CLLocation) {
self.init(southWest: Coordinate3D(southWest), northEast: Coordinate3D(northEast))
Expand Down
3 changes: 1 addition & 2 deletions Sources/GISTools/GeoJson/Coordinate3D.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import Foundation
/// A three dimensional coordinate (``latitude``/``y``, ``longitude``/``x``, ``altitude``/``z``)
/// plus a generic value ``m``.
public struct Coordinate3D:
Projectable,
CustomStringConvertible,
Sendable
{
Expand Down Expand Up @@ -247,7 +246,7 @@ extension Coordinate3D {

// MARK: - Projection

extension Coordinate3D {
extension Coordinate3D: Projectable {

/// Reproject this coordinate.
public func projected(to newProjection: Projection) -> Coordinate3D {
Expand Down
6 changes: 3 additions & 3 deletions Sources/GISTools/GeoJson/Feature.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Foundation
public struct Feature: GeoJson {

/// A GeoJSON identifier that can either be a string or number.
public enum Identifier: Equatable, Hashable, CustomStringConvertible {
public enum Identifier: Equatable, Hashable, CustomStringConvertible, Sendable {
case string(String)
case int(Int)
case double(Double)
Expand Down Expand Up @@ -61,11 +61,11 @@ public struct Feature: GeoJson {
}

/// Only 'Feature' objects may have properties.
public var properties: [String: Any]
public var properties: [String: Sendable]

public var boundingBox: BoundingBox?

public var foreignMembers: [String: Any] = [:]
public var foreignMembers: [String: Sendable] = [:]

/// Create a ``Feature`` from any ``GeoJsonGeometry`` object.
public init(
Expand Down
2 changes: 1 addition & 1 deletion Sources/GISTools/GeoJson/FeatureCollection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public struct FeatureCollection:

public var boundingBox: BoundingBox?

public var foreignMembers: [String: Any] = [:]
public var foreignMembers: [String: Sendable] = [:]

public init() {
self.features = []
Expand Down
5 changes: 3 additions & 2 deletions Sources/GISTools/GeoJson/GeoJson.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ public protocol GeoJson:
Projectable,
ValidatableGeoJson,
Codable,
CustomDebugStringConvertible
CustomDebugStringConvertible,
Sendable
{

/// GeoJSON object type.
Expand All @@ -45,7 +46,7 @@ public protocol GeoJson:
/// Any foreign members, i.e. keys in the JSON that are
/// not part of the GeoJSON standard.
/// - important: `values` must be a valid JSON objects or serialization will fail.
var foreignMembers: [String: Any] { get set }
var foreignMembers: [String: Sendable] { get set }

/// Try to initialize a GeoJSON object from any JSON and calculate a bounding box if necessary.
///
Expand Down
2 changes: 1 addition & 1 deletion Sources/GISTools/GeoJson/GeometryCollection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public struct GeometryCollection: GeoJsonGeometry {

public var boundingBox: BoundingBox?

public var foreignMembers: [String: Any] = [:]
public var foreignMembers: [String: Sendable] = [:]

/// Initialize a GeometryCollection with a geometry object.
public init(_ geometry: GeoJsonGeometry, calculateBoundingBox: Bool = false) {
Expand Down
4 changes: 2 additions & 2 deletions Sources/GISTools/GeoJson/LineSegment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import CoreLocation
import Foundation

/// A `LineSegment` is a line with exactly two coordinates.
public struct LineSegment: Projectable, Sendable {
public struct LineSegment: Sendable {

public var boundingBox: BoundingBox?

Expand Down Expand Up @@ -46,7 +46,7 @@ extension LineSegment {

// MARK: - Projection

extension LineSegment {
extension LineSegment: Projectable {

public func projected(to newProjection: Projection) -> LineSegment {
guard newProjection != projection else { return self }
Expand Down
2 changes: 1 addition & 1 deletion Sources/GISTools/GeoJson/LineString.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public struct LineString:

public var boundingBox: BoundingBox?

public var foreignMembers: [String: Any] = [:]
public var foreignMembers: [String: Sendable] = [:]

public var lineStrings: [LineString] {
return [self]
Expand Down
2 changes: 1 addition & 1 deletion Sources/GISTools/GeoJson/MultiLineString.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public struct MultiLineString:

public var boundingBox: BoundingBox?

public var foreignMembers: [String: Any] = [:]
public var foreignMembers: [String: Sendable] = [:]

public var lineStrings: [LineString] {
return coordinates.compactMap { LineString($0) }
Expand Down
2 changes: 1 addition & 1 deletion Sources/GISTools/GeoJson/MultiPoint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public struct MultiPoint:

public var boundingBox: BoundingBox?

public var foreignMembers: [String: Any] = [:]
public var foreignMembers: [String: Sendable] = [:]

public var points: [Point] {
return coordinates.map { Point($0) }
Expand Down
2 changes: 1 addition & 1 deletion Sources/GISTools/GeoJson/MultiPolygon.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public struct MultiPolygon:

public var boundingBox: BoundingBox?

public var foreignMembers: [String: Any] = [:]
public var foreignMembers: [String: Sendable] = [:]

public var polygons: [Polygon] {
return coordinates.compactMap { Polygon($0) }
Expand Down
2 changes: 1 addition & 1 deletion Sources/GISTools/GeoJson/Point.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public struct Point: PointGeometry {

public var boundingBox: BoundingBox?

public var foreignMembers: [String: Any] = [:]
public var foreignMembers: [String: Sendable] = [:]

public var points: [Point] {
return [self]
Expand Down
2 changes: 1 addition & 1 deletion Sources/GISTools/GeoJson/Polygon.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public struct Polygon:

public var boundingBox: BoundingBox?

public var foreignMembers: [String: Any] = [:]
public var foreignMembers: [String: Sendable] = [:]

public var polygons: [Polygon] {
return [self]
Expand Down
11 changes: 11 additions & 0 deletions Sources/GISTools/GeoJson/Projectable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,14 @@ public protocol Projectable {
func projected(to newProjection: Projection) -> Self

}

extension Projectable {

/// Reproject this coordinate.
public mutating func project(to newProjection: Projection) {
guard newProjection != projection else { return }

self = projected(to: newProjection)
}

}
2 changes: 1 addition & 1 deletion Sources/GISTools/GeoJson/RTree.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public enum RTreeSortOption: Sendable {
// MARK: - RTree

/// An efficient implementation of the packed Hilbert R-tree algorithm.
public struct RTree<T: BoundingBoxRepresentable> {
public struct RTree<T: BoundingBoxRepresentable & Sendable>: Sendable {

/// The R-Tree's `projection`.
public let projection: Projection
Expand Down
7 changes: 2 additions & 5 deletions Sources/GISTools/GeoJson/Ring.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,7 @@ import Foundation
/// - A linear ring MUST follow the right-hand rule with respect to the
/// area it bounds, i.e., exterior rings are counterclockwise, and
/// holes are clockwise.
public struct Ring:
Projectable,
Sendable
{
public struct Ring: Sendable {

public var projection: Projection {
coordinates.first?.projection ?? .noSRID
Expand Down Expand Up @@ -47,7 +44,7 @@ public struct Ring:

// MARK: - Projection

extension Ring {
extension Ring: Projectable {

public func projected(to newProjection: Projection) -> Ring {
guard newProjection != projection else { return self }
Expand Down
2 changes: 1 addition & 1 deletion Sources/GISTools/Other/MapTile.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import CoreLocation
#endif
import Foundation

public struct MapTile: CustomStringConvertible {
public struct MapTile: CustomStringConvertible, Sendable {

public let x: Int
public let y: Int
Expand Down
Loading

0 comments on commit 271ffa1

Please sign in to comment.