Skip to content

Commit 28e5e9f

Browse files
committed
🚧 WIP Boundable
1 parent 3a59664 commit 28e5e9f

File tree

5 files changed

+214
-137
lines changed

5 files changed

+214
-137
lines changed

‎Sources/GeodeticGeometry/Shapes/MultiPoint.swift

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,14 @@
66
// Copyright © 2022 Rémi Bardon. All rights reserved.
77
//
88

9-
import Geodesy
10-
import NonEmpty
9+
import protocol NonEmpty.NonEmptyProtocol
1110

1211
public protocol MultiPoint<GeometricSystem>: Hashable {
1312

14-
typealias CRS = GeometricSystem.CRS
15-
associatedtype GeometricSystem: GeodeticGeometry.GeometricSystem<CRS>
16-
typealias Point = GeometricSystem.Point
17-
associatedtype Points: NonEmptyProtocol
13+
typealias CRS = Self.GeometricSystem.CRS
14+
associatedtype GeometricSystem: GeodeticGeometry.GeometricSystem<Self.CRS>
15+
typealias Point = Self.GeometricSystem.Point
16+
associatedtype Points: NonEmpty.NonEmptyProtocol
1817
where Self.Points.Element == Self.Point
1918

2019
var points: Self.Points { get }
@@ -24,7 +23,7 @@ public protocol MultiPoint<GeometricSystem>: Hashable {
2423
}
2524

2625
extension MultiPoint {
27-
26+
// Type alias defined so we can declare a `Point` associated type
27+
// and `GeometricSystem` is inferred.
2828
public typealias GeometricSystem = Self.Point.GeometricSystem
29-
3029
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//
2+
// Iterable.swift
3+
// SwiftGeo
4+
//
5+
// Created by Rémi Bardon on 28/11/2022.
6+
// Copyright © 2022 Rémi Bardon. All rights reserved.
7+
//
8+
9+
import protocol NonEmpty.NonEmptyProtocol
10+
11+
public protocol Iterable<Element> {
12+
associatedtype Element
13+
associatedtype Iterator: IteratorProtocol<Element>
14+
func makeIterator() -> Iterator
15+
}
16+
17+
public protocol NonEmptyIterable<Base>: Iterable {
18+
associatedtype Base: NonEmptyProtocol
19+
func makeIterator() -> NonEmptyIterator<Base>
20+
}
21+
22+
public struct NonEmptyIterator<Base: NonEmptyProtocol>: IteratorProtocol {
23+
private let _first: Base.Element
24+
private var base: Base.Iterator
25+
private var firstElementAccessed: Bool = false
26+
27+
init(base: Base) {
28+
self._first = base.first
29+
self.base = base.makeIterator()
30+
}
31+
32+
public mutating func first() -> Base.Element {
33+
if self.firstElementAccessed {
34+
return self._first
35+
} else {
36+
// Skip first element in iterator
37+
_ = self.base.next()
38+
self.firstElementAccessed = true
39+
return self._first
40+
}
41+
}
42+
public mutating func next() -> Base.Element? {
43+
self.base.next()
44+
}
45+
}

‎Sources/TurfCore/Boundable.swift

Lines changed: 32 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,36 @@
77
//
88

99
//import NonEmpty
10-
import protocol GeodeticGeometry.BoundingBox
10+
import GeodeticGeometry
11+
12+
public protocol Boundable<BoundingBox> {
13+
associatedtype BoundingBox: GeodeticGeometry.BoundingBox & Boundable
1114

12-
public protocol Boundable {
13-
14-
associatedtype BoundingBox: GeodeticGeometry.BoundingBox
15-
1615
var bbox: BoundingBox { get }
17-
16+
17+
func union(_ other: Self) -> Self
18+
}
19+
20+
// MARK: - Default implementations
21+
22+
// MARK: Bounding box
23+
24+
extension GeodeticGeometry.BoundingBox {
25+
public var bbox: Self { self }
26+
}
27+
28+
// MARK: Shapes
29+
30+
extension GeodeticGeometry.Point where Self.Coordinates: Boundable {
31+
public var bbox: Self.Coordinates.BoundingBox { self.coordinates.bbox }
32+
}
33+
34+
extension GeodeticGeometry.Line where Self.GeometricSystem: GeometricSystemAlgebra {
35+
public var bbox: Self.GeometricSystem.BoundingBox {
36+
Self.GeometricSystem.bbox(forCollection: self.points)
37+
}
1838
}
1939

20-
//extension Coordinate2D: Boundable {}
21-
//
22-
//extension Coordinate3D: Boundable {}
23-
//
2440
//extension Line2D: Boundable {
2541
//
2642
// public var bbox: BoundingBox2D {
@@ -49,27 +65,10 @@ public protocol Boundable {
4965
//
5066
//}
5167

52-
// Extension of protocol 'Collection' cannot have an inheritance clause
53-
//extension Collection: Boundable where Element: Boundable {
54-
//
55-
// public var bbox: Element.BoundingBox {
56-
// self.reduce(.zero, { $0.union($1.bbox) })
57-
// }
58-
//
59-
//}
68+
// MARK: Sequences
6069

61-
//extension Array: Boundable where Element: Boundable {
62-
//
63-
// public var bbox: Element.BoundingBox {
64-
// self.reduce(.zero, { $0.union($1.bbox) })
65-
// }
66-
//
67-
//}
68-
//
69-
//extension Set: Boundable where Element: Boundable {
70-
//
71-
// public var bbox: Element.BoundingBox {
72-
// self.reduce(.zero, { $0.union($1.bbox) })
73-
// }
74-
//
75-
//}
70+
extension Sequence where Self.Element: Boundable, Self.Element.BoundingBox: Boundable {
71+
public var bbox: Self.Element.BoundingBox {
72+
self.reduce(.zero, { $0.union($1.bbox) })
73+
}
74+
}

‎Sources/TurfCore/GeoModels+Turf.swift renamed to ‎Sources/TurfCore/GeodeticGeometry+Turf.swift

Lines changed: 33 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -11,113 +11,50 @@ import NonEmpty
1111
import SwiftGeoToolbox
1212
import ValueWithUnit
1313

14-
#warning("TODO: Replace all the `bbox` by one or two using `Boundable`")
14+
// MARK: - Default `GeometricSystemAlgebra` implementations
1515

16-
// MARK: - Base protocol
17-
18-
public protocol GeometricSystemAlgebra: GeodeticGeometry.GeometricSystem {
19-
20-
// MARK: Bounding box
21-
22-
static func bbox(forPoint point: Self.Point) -> Self.BoundingBox
23-
24-
/// Returns a naive [bounding box](https://en.wikipedia.org/wiki/Minimum_bounding_box)
25-
/// enclosing a cluster of points.
26-
/// - Warning: This does not take into account the curvature of the Earth.
27-
/// - Warning: This is a naive implementation, not taking into account the angular coordinate system
28-
/// (i.e. a cluster around 0°N 180°E will have a bounding box around 0°N 0°E).
29-
static func bbox<C: Collection>(forCollection coordinates: C) -> Self.BoundingBox?
30-
where C.Element == Self.Coordinates
31-
32-
/// Returns a naive [bounding box](https://en.wikipedia.org/wiki/Minimum_bounding_box)
33-
/// enclosing a cluster of points.
34-
/// - Warning: This does not take into account the curvature of the Earth.
35-
/// - Warning: This is a naive implementation, not taking into account the angular coordinate system
36-
/// (i.e. a cluster around 0°N 180°E will have a bounding box around 0°N 0°E).
37-
static func bbox<Points: Collection>(forCollection points: Points) -> Self.BoundingBox?
38-
where Points.Element == Self.Point
39-
40-
/// Returns a naive [bounding box](https://en.wikipedia.org/wiki/Minimum_bounding_box)
41-
/// enclosing a cluster of points.
42-
/// - Warning: This does not take into account the curvature of the Earth.
43-
/// - Warning: This is a naive implementation, not taking into account the angular coordinate system
44-
/// (i.e. a cluster around 0°N 180°E will have a bounding box around 0°N 0°E).
45-
static func bbox<MultiPoint>(forMultiPoint multiPoint: MultiPoint) -> Self.BoundingBox
46-
where MultiPoint: GeodeticGeometry.MultiPoint,
47-
MultiPoint.Point == Self.Point
48-
49-
/// Returns the [bounding box](https://en.wikipedia.org/wiki/Minimum_bounding_box)
50-
/// enclosing a cluster of points.
51-
/// - Warning: This does not take into account the curvature of the Earth.
52-
/// - Note: This implementation takes into account the angular coordinate system
53-
/// (i.e. a cluster around 0°N 180°E will have a bounding box around 0°N 180°E).
54-
static func geographicBBox<C: Collection>(forCollection coordinates: C) -> Self.BoundingBox?
55-
where C.Element == Self.Coordinates
56-
57-
/// Returns the [bounding box](https://en.wikipedia.org/wiki/Minimum_bounding_box)
58-
/// enclosing a cluster of points.
59-
/// - Warning: This does not take into account the curvature of the Earth.
60-
/// - Note: This implementation takes into account the angular coordinate system
61-
/// (i.e. a cluster around 0°N 180°E will have a bounding box around 0°N 180°E).
62-
static func geographicBBox<Points: Collection>(forCollection points: Points) -> Self.BoundingBox?
63-
where Points.Element == Self.Point
64-
65-
/// Returns the [bounding box](https://en.wikipedia.org/wiki/Minimum_bounding_box)
66-
/// enclosing a cluster of points.
67-
/// - Warning: This does not take into account the curvature of the Earth.
68-
/// - Note: This implementation takes into account the angular coordinate system
69-
/// (i.e. a cluster around 0°N 180°E will have a bounding box around 0°N 180°E).
70-
static func geographicBBox<MultiPoint>(forMultiPoint multiPoint: MultiPoint) -> Self.BoundingBox
71-
where MultiPoint: GeodeticGeometry.MultiPoint,
72-
MultiPoint.Point == Self.Point
73-
74-
// MARK: Center
75-
76-
/// Returns the linear center of a cluster of points.
77-
/// - Warning: This does not take into account the curvature of the Earth.
78-
/// - Warning: This is a naive implementation, not taking into account the angular coordinate system
79-
/// (i.e. a cluster around 0°N 180°E will have a center near 0°N 0°E).
80-
static func center<Points: Collection>(forCollection points: Points) -> Self.Coordinates?
81-
where Points.Element == Self.Point
82-
83-
static func center(forBBox bbox: Self.BoundingBox) -> Self.Coordinates
84-
85-
// MARK: Centroid
86-
87-
/// Calculates the centroid of a polygon using the mean of all vertices.
88-
static func centroid<Points: Collection>(forCollection points: Points) -> Self.Coordinates?
89-
where Points.Element == Self.Point
90-
91-
// MARK: Bézier
92-
93-
static func bezier(
94-
forLineString: Self.LineString,
95-
sharpness: Double,
96-
resolution: Double
97-
) -> Self.LineString
16+
public extension GeometricSystemAlgebra {
9817

99-
}
18+
static func bbox(forPoint point: Self.Point) -> Self.BoundingBox {
19+
Self.BoundingBox(origin: point.coordinates, size: .zero)
20+
}
10021

101-
// MARK: - Default implementations
22+
static func bbox<C>(forNonEmptyCollection elements: C) -> Self.BoundingBox
23+
where C: NonEmptyProtocol, C.Element: Boundable<Self.BoundingBox>
24+
{
25+
Self.bbox(forCollection: elements) ?? elements.first.bbox
26+
}
10227

103-
public extension GeometricSystemAlgebra {
104-
105-
static func bbox(forPoint point: Self.Point) -> Self.BoundingBox {
106-
return Self.BoundingBox(origin: point.coordinates, size: .zero)
28+
static func bbox<Iterator>(forIterator iterator: inout Iterator) -> Self.BoundingBox?
29+
where Iterator: IteratorProtocol, Iterator.Element: Boundable<Self.BoundingBox>
30+
{
31+
guard let element = iterator.next() else {
32+
return nil
33+
}
34+
var bbox: Self.BoundingBox = element.bbox
35+
while let element = iterator.next() {
36+
bbox = bbox.union(element.bbox)
37+
}
38+
return bbox
10739
}
10840

109-
static func bbox<C: Collection>(forCollection coordinates: C) -> Self.BoundingBox?
110-
where C.Element == Self.Coordinates
41+
static func bbox<S>(
42+
forNonEmptyIterator iterator: inout NonEmptyIterator<S>
43+
) -> Self.BoundingBox?
44+
where S: Sequence, S.Element: Boundable<Self.BoundingBox>
11145
{
112-
return Self.bbox(forCollection: coordinates.map(Self.Point.init(coordinates:)))
46+
var bbox: Self.BoundingBox = iterator.first().bbox
47+
while let element = iterator.next() {
48+
bbox = bbox.union(element.bbox)
49+
}
50+
return bbox
11351
}
114-
52+
11553
static func bbox<MultiPoint>(forMultiPoint multiPoint: MultiPoint) -> Self.BoundingBox
11654
where MultiPoint: GeodeticGeometry.MultiPoint,
117-
MultiPoint.Point == Self.Point
55+
MultiPoint.Points.Element: Boundable<Self.BoundingBox>
11856
{
119-
return self.bbox(forCollection: multiPoint.points)
120-
?? self.bbox(forPoint: multiPoint.points.first)
57+
Self.bbox(forCollection: multiPoint.points) ?? multiPoint.points.first.bbox
12158
}
12259

12360
static func geographicBBox<C: Collection>(forCollection coordinates: C) -> Self.BoundingBox?

0 commit comments

Comments
 (0)