Skip to content

Commit 593393a

Browse files
committed
🎨 Remove most genericity from GeodeticGeometry
1 parent 152354d commit 593393a

22 files changed

+421
-870
lines changed

‎Package.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import PackageDescription
66
let package = Package(
77
name: "swift-geo",
88
platforms: [
9-
.macOS(.v10_15),
9+
// TODO: Support `.macOS(.v10_15)` again by using a backwards compatile logging system
10+
.macOS(.v11),
1011
],
1112
products: [
1213
// Products define the executables and libraries a package produces,
@@ -127,7 +128,11 @@ let package = Package(
127128
.target(name: "WGS84Turf", dependencies: ["WGS84Geometry", "Turf"]),
128129

129130
// 🎭 Conversions from one Coordinate Reference System to another
130-
.target(name: "GeodeticConversions", dependencies: ["Geodesy", "WGS84Core"]),
131+
.target(name: "GeodeticConversions", dependencies: [
132+
"Geodesy",
133+
"GeodeticGeometry",
134+
"WGS84Core",
135+
]),
131136
.testTarget(name: "GeodeticConversionsTests", dependencies: [
132137
"GeodeticConversions",
133138
"WGS84Conversions",

‎Sources/Geodesy/Geodesy.swift

Lines changed: 38 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public protocol EPSGItem {
2020
// MARK: - Coordinates
2121

2222
public protocol Coordinates<CRS>:
23-
Equatable,
23+
Hashable,
2424
Zeroable,
2525
AdditiveArithmetic,
2626
MultiplicativeArithmetic,
@@ -29,20 +29,33 @@ public protocol Coordinates<CRS>:
2929
CustomDebugStringConvertible
3030
{
3131
associatedtype CRS: Geodesy.CoordinateReferenceSystem
32+
associatedtype Components
33+
34+
var components: Components { get set }
35+
36+
init(components: Components)
37+
}
38+
39+
// CustomStringConvertible & CustomDebugStringConvertible
40+
public extension Coordinates {
41+
var description: String { String(describing: self.components) }
42+
var debugDescription: String {
43+
"<\(Self.CRS.epsgName)>\(String(reflecting: self.components))"
44+
}
3245
}
3346

3447
// MARK: 2D Coordinates
3548

36-
public protocol TwoDimensionalCoordinates<CRS>: Geodesy.Coordinates
37-
where CRS: TwoDimensionalCRS
38-
{
49+
public protocol AtLeastTwoDimensionalCoordinates<CRS>: Geodesy.Coordinates
50+
where CRS: AtLeastTwoDimensionalCRS {
3951
associatedtype X: CoordinateComponent
4052
associatedtype Y: CoordinateComponent
4153

4254
var x: X { get set }
4355
var y: Y { get set }
44-
var components: (X, Y) { get set }
45-
56+
}
57+
public protocol TwoDimensionalCoordinates<CRS>: AtLeastTwoDimensionalCoordinates
58+
where Components == (X, Y) {
4659
init(x: X, y: Y)
4760
}
4861

@@ -68,6 +81,9 @@ where CRS: TwoDimensionalCRS
6881
self.x = x
6982
self.y = y
7083
}
84+
public init(components: (X, Y)) {
85+
self.init(x: components.0, y: components.1)
86+
}
7187
}
7288

7389
// Zeroable
@@ -107,12 +123,6 @@ public extension Coordinates2DOf {
107123
}
108124
}
109125

110-
// CustomStringConvertible & CustomDebugStringConvertible
111-
public extension Coordinates2DOf {
112-
var description: String { String(describing: self.components) }
113-
var debugDescription: String { String(reflecting: self.components) }
114-
}
115-
116126
public extension Coordinates2DOf where CRS: GeographicCRS {
117127
var latitude: X { self.x }
118128
var longitude: Y { self.y }
@@ -123,7 +133,7 @@ public extension Coordinates2DOf where CRS: GeographicCRS {
123133

124134
public extension Coordinates2DOf
125135
where CRS: GeographicCRS,
126-
Self.Y: AngularCoordinateComponent
136+
Y: AngularCoordinateComponent
127137
{
128138
var withPositiveLongitude: Self {
129139
Self.init(latitude: self.latitude, longitude: self.longitude.positive)
@@ -132,18 +142,14 @@ where CRS: GeographicCRS,
132142

133143
// MARK: 3D Coordinates
134144

135-
public protocol ThreeDimensionalCoordinates<CRS>: Geodesy.Coordinates
136-
where CRS: ThreeDimensionalCRS
137-
{
138-
associatedtype X: CoordinateComponent
139-
associatedtype Y: CoordinateComponent
145+
public protocol AtLeastThreeDimensionalCoordinates<CRS>: AtLeastTwoDimensionalCoordinates
146+
where CRS: AtLeastTwoDimensionalCRS {
140147
associatedtype Z: CoordinateComponent
141148

142-
var x: X { get set }
143-
var y: Y { get set }
144149
var z: Z { get set }
145-
var components: (X, Y, Z) { get set }
146-
150+
}
151+
public protocol ThreeDimensionalCoordinates<CRS>: AtLeastThreeDimensionalCoordinates
152+
where Components == (X, Y, Z) {
147153
init(x: X, y: Y, z: Z)
148154
}
149155

@@ -171,6 +177,9 @@ public struct Coordinates3DOf<CRS: ThreeDimensionalCRS>: ThreeDimensionalCoordin
171177
self.y = y
172178
self.z = z
173179
}
180+
public init(components: (X, Y, Z)) {
181+
self.init(x: components.0, y: components.1, z: components.2)
182+
}
174183
}
175184

176185
// Zeroable
@@ -210,12 +219,6 @@ public extension Coordinates3DOf {
210219
}
211220
}
212221

213-
// CustomStringConvertible & CustomDebugStringConvertible
214-
public extension Coordinates3DOf {
215-
var description: String { String(describing: self.components) }
216-
var debugDescription: String { String(reflecting: self.components) }
217-
}
218-
219222
public extension ThreeDimensionalCoordinates where CRS: GeographicCRS {
220223
var latitude: X { self.x }
221224
var longitude: Y { self.y }
@@ -227,7 +230,7 @@ public extension ThreeDimensionalCoordinates where CRS: GeographicCRS {
227230

228231
public extension ThreeDimensionalCoordinates
229232
where CRS: GeographicCRS,
230-
Self.Y: AngularCoordinateComponent
233+
Y: AngularCoordinateComponent
231234
{
232235
var withPositiveLongitude: Self {
233236
Self.init(
@@ -247,13 +250,17 @@ public protocol CoordinateReferenceSystem: EPSGItem {
247250
}
248251

249252
public protocol AtLeastTwoDimensionalCRS: CoordinateReferenceSystem
250-
where CoordinateSystem: AtLeastTwoDimensionalCS {}
253+
where CoordinateSystem: AtLeastTwoDimensionalCS,
254+
Coordinates: AtLeastTwoDimensionalCoordinates
255+
{}
251256
public protocol TwoDimensionalCRS: AtLeastTwoDimensionalCRS
252257
where CoordinateSystem: TwoDimensionalCS,
253258
Coordinates: TwoDimensionalCoordinates
254259
{}
255260
public protocol AtLeastThreeDimensionalCRS: AtLeastTwoDimensionalCRS
256-
where CoordinateSystem: AtLeastThreeDimensionalCS {}
261+
where CoordinateSystem: AtLeastThreeDimensionalCS,
262+
Coordinates: AtLeastThreeDimensionalCoordinates
263+
{}
257264
public protocol ThreeDimensionalCRS: AtLeastThreeDimensionalCRS
258265
where CoordinateSystem: ThreeDimensionalCS,
259266
Coordinates: ThreeDimensionalCoordinates

‎Sources/GeodeticConversions/ConvertibleCRS.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//
88

99
import Geodesy
10+
import GeodeticGeometry
1011
import WGS84Core
1112

1213
public protocol ConvertibleCRS: CoordinateReferenceSystem {
@@ -36,3 +37,18 @@ public extension Coordinates3DOf<EPSG4978> {
3637
NewCRS.fromEPSG4978(self) as! C
3738
}
3839
}
40+
41+
public extension Point {
42+
static func + <OtherCRS>(lhs: Self, rhs: Point<OtherCRS>) -> Self
43+
where CRS: ConvertibleCRS, OtherCRS: ConvertibleCRS
44+
{
45+
Self.init(coordinates: lhs.coordinates + rhs.coordinates.to(to: OtherCRS.self))
46+
}
47+
}
48+
49+
public extension Vector {
50+
init<OtherCRS>(from: Point<CRS>, to: Point<OtherCRS>)
51+
where CRS: ConvertibleCRS, OtherCRS: ConvertibleCRS {
52+
self.init(rawValue: to.coordinates.to(CRS.self) - from.coordinates)
53+
}
54+
}

‎Sources/GeodeticGeometry/Concepts/BoundingBox.swift

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,33 +9,32 @@
99
import Geodesy
1010
import SwiftGeoToolbox
1111

12-
public protocol BoundingBox<GeometricSystem>: Hashable, Zeroable {
13-
14-
typealias CRS = GeometricSystem.CRS
15-
associatedtype GeometricSystem: GeodeticGeometry.GeometricSystem<CRS>
16-
typealias Coordinates = GeometricSystem.Coordinates
17-
typealias Size = GeometricSystem.Size
18-
19-
var origin: Coordinates { get }
20-
var size: Size { get }
21-
22-
init(origin: Coordinates, size: Size)
23-
init(min: Coordinates, max: Coordinates)
12+
public struct BoundingBox<CRS: Geodesy.CoordinateReferenceSystem> {
13+
public typealias Coordinates = CRS.Coordinates
14+
public typealias Size = GeodeticGeometry.Size<CRS>
15+
16+
public var origin: Self.Coordinates
17+
public var size: Self.Size
18+
19+
public var center: Self.Coordinates {
20+
self.origin + self.size / 2
21+
}
22+
23+
public init(origin: Self.Coordinates, size: Self.Size) {
24+
self.origin = origin
25+
self.size = size
26+
}
27+
public init(min: Self.Coordinates, max: Self.Coordinates) {
28+
self.init(origin: min, size: .init(from: min, to: max))
29+
}
2430

2531
#warning("TODO: Reimplement `BoundingBox.union(_ other: Self)`")
2632
/// The union of bounding boxes gives a new bounding box that encloses the given two.
27-
// func union(_ other: Self) -> Self
28-
33+
// public func union(_ other: Self) -> Self
2934
}
3035

31-
public extension BoundingBox {
32-
33-
static var zero: Self {
34-
Self.init(origin: .zero, size: .zero)
35-
}
36+
extension BoundingBox: Hashable {}
3637

37-
init(min: Coordinates, max: Coordinates) {
38-
self.init(origin: min, size: Size.init(from: min, to: max))
39-
}
40-
38+
extension BoundingBox: Zeroable {
39+
public static var zero: Self { Self.init(origin: .zero, size: .zero) }
4140
}

‎Sources/GeodeticGeometry/Concepts/Size.swift

Lines changed: 10 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -2,116 +2,25 @@
22
// Size.swift
33
// SwiftGeo
44
//
5-
// Created by Rémi Bardon on 26/03/2022.
5+
// Created by Rémi Bardon on 28/11/2022.
66
// Copyright © 2022 Rémi Bardon. All rights reserved.
77
//
88

99
import Geodesy
10-
import SwiftGeoToolbox
1110

12-
public protocol Size<GeometricSystem>:
13-
Hashable,
14-
SafeRawRepresentable,
15-
Zeroable,
16-
AdditiveArithmetic,
17-
MultiplicativeArithmetic,
18-
InitializableByNumber
19-
{
20-
typealias CRS = GeometricSystem.CRS
21-
associatedtype GeometricSystem: GeodeticGeometry.GeometricSystem
11+
public typealias Size = Vector
2212

23-
init(from: GeometricSystem.Coordinates, to: GeometricSystem.Coordinates)
24-
}
25-
26-
public extension Size where RawValue: Zeroable {
27-
static var zero: Self { Self.init(rawValue: .zero) }
28-
}
29-
30-
public extension Size where RawValue: InitializableByInteger {
31-
init<Source>(_ value: Source) where Source: BinaryInteger {
32-
self.init(rawValue: .init(value))
33-
}
34-
}
35-
36-
public extension Size where RawValue: InitializableByFloatingPoint {
37-
init<Source>(_ value: Source) where Source: BinaryFloatingPoint {
38-
self.init(rawValue: .init(value))
39-
}
40-
}
41-
42-
public extension Size where RawValue == GeometricSystem.Point.Coordinates {
43-
init(from: GeometricSystem.Coordinates, to: GeometricSystem.Coordinates) {
44-
self.init(rawValue: to - from)
45-
}
46-
}
47-
48-
public extension Size where RawValue: AdditiveArithmetic {
49-
static func + (lhs: Self, rhs: Self) -> Self {
50-
Self.init(rawValue: lhs.rawValue + rhs.rawValue)
51-
}
52-
static func - (lhs: Self, rhs: Self) -> Self {
53-
Self.init(rawValue: lhs.rawValue - rhs.rawValue)
54-
}
55-
}
56-
57-
public extension Size where RawValue: MultiplicativeArithmetic {
58-
static func * (lhs: Self, rhs: Self) -> Self {
59-
Self.init(rawValue: lhs.rawValue * rhs.rawValue)
60-
}
61-
static func / (lhs: Self, rhs: Self) -> Self {
62-
Self.init(rawValue: lhs.rawValue / rhs.rawValue)
63-
}
64-
}
65-
66-
// MARK: - 2D
67-
68-
public extension Size where RawValue: AtLeastTwoDimensionalCoordinate {
69-
var dx: RawValue.X { self.rawValue.x }
70-
var dy: RawValue.Y { self.rawValue.y }
71-
72-
/// - Warning: In a geographic CRS, ``Size2D/width`` represents the vertical length,
73-
/// because ``Size2D/DX`` represents the latitude (vertical axis).
74-
/// You can use ``Size2D/horizontalDelta`` to remove ambiguity.
13+
public extension Size where RawValue: AtLeastTwoDimensionalCoordinates {
14+
/// - Warning: In a geographic CRS, ``Vector2D/width`` represents the vertical length,
15+
/// because ``Vector2D/DX`` represents the latitude (vertical axis).
16+
/// You can use ``Vector2D/horizontalDelta`` to remove ambiguity.
7517
var width: RawValue.X { self.dx }
76-
/// - Warning: In a geographic CRS, ``Size2D/height`` represents the horizontal length,
77-
/// because ``Size2D/DY`` represents the longitude (horizontal axis).
78-
/// You can use ``Size2D/horizontalDelta`` to remove ambiguity.
18+
/// - Warning: In a geographic CRS, ``Vector2D/height`` represents the horizontal length,
19+
/// because ``Vector2D/DY`` represents the longitude (horizontal axis).
20+
/// You can use ``Vector2D/horizontalDelta`` to remove ambiguity.
7921
var height: RawValue.Y { self.dy }
80-
81-
var verticalDelta: RawValue.X { self.dx }
82-
var horizontalDelta: RawValue.Y { self.dy }
8322
}
8423

85-
public protocol Size2D<GeometricSystem>: Size {
86-
associatedtype DX: CoordinateComponent
87-
associatedtype DY: CoordinateComponent
88-
89-
/// - Warning: In a geographic CRS, ``Size2D/dx`` represents the vertical length,
90-
/// because ``Size2D/DX`` represents the latitude (vertical axis).
91-
/// You can use ``Size2D/verticalDelta`` to remove ambiguity.
92-
var dx: DX { get }
93-
/// - Warning: In a geographic CRS, ``Size2D/dy`` represents the horizontal length,
94-
/// because ``Size2D/DY`` represents the longitude (horizontal axis).
95-
/// You can use ``Size2D/horizontalDelta`` to remove ambiguity.
96-
var dy: DY { get }
97-
98-
var verticalDelta: DX { get }
99-
var horizontalDelta: DY { get }
100-
}
101-
102-
public extension Size2D {
103-
var verticalDelta: DX { self.dx }
104-
var horizontalDelta: DY { self.dy }
105-
}
106-
107-
// MARK: - 3D
108-
109-
public extension Size where RawValue: AtLeastThreeDimensionalCoordinate {
110-
var dz: RawValue.Z { self.rawValue.z }
24+
public extension Size where RawValue: AtLeastThreeDimensionalCoordinates {
11125
var zHeight: RawValue.Z { self.dz }
11226
}
113-
114-
public protocol Size3D<GeometricSystem>: Size2D {
115-
associatedtype DZ: CoordinateComponent
116-
var dz: DZ { get }
117-
}

0 commit comments

Comments
 (0)