Skip to content

Commit da78ab2

Browse files
committed
✨ Add bounding box memoization
1 parent 0aa9cda commit da78ab2

File tree

10 files changed

+96
-44
lines changed

10 files changed

+96
-44
lines changed

Sources/GeoJSON/BoundingBox.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import GeoModels
1010

1111
/// A [GeoJSON Bounding Box](https://datatracker.ietf.org/doc/html/rfc7946#section-5).
12-
public protocol BoundingBox: Hashable, Codable {
12+
public protocol BoundingBox: GeoModels.BoundingBox, Codable {
1313

1414
/// This bonding box, but type-erased.
1515
var asAny: AnyBoundingBox { get }
@@ -37,6 +37,13 @@ extension BoundingBox3D: BoundingBox {
3737
/// A type-erased ``BoundingBox``.
3838
public enum AnyBoundingBox: BoundingBox, Hashable, Codable {
3939

40+
public static var zero: AnyBoundingBox = .twoDimensions(.zero)
41+
42+
public func union(_ other: AnyBoundingBox) -> AnyBoundingBox {
43+
#warning("Implement `AnyBoundingBox.union`")
44+
fatalError("Not implemented yet")
45+
}
46+
4047
case twoDimensions(BoundingBox2D)
4148
case threeDimensions(BoundingBox3D)
4249

Sources/GeoJSON/GeoJSON+Codable.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,6 @@ extension Feature {
407407
try container.encodeIfPresent(self.geometry, forKey: .geometry)
408408
try container.encode(self.properties, forKey: .properties)
409409
// TODO: Create GeoJSONEncoder that allows setting "export bboxes" to a boolean value
410-
// TODO: Memoize bboxes not to recompute them all the time (bboxes tree)
411410
try container.encodeIfPresent(self.bbox, forKey: .bbox)
412411
}
413412

@@ -445,7 +444,6 @@ extension FeatureCollection {
445444
try container.encode(Self.geoJSONType, forKey: .geoJSONType)
446445
try container.encodeIfPresent(self.features, forKey: .features)
447446
// TODO: Create GeoJSONEncoder that allows setting "export bboxes" to a boolean value
448-
// TODO: Memoize bboxes not to recompute them all the time (bboxes tree)
449447
try container.encodeIfPresent(self.bbox, forKey: .bbox)
450448
}
451449

Sources/GeoJSON/Geometries/GeometryCollection.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public struct GeometryCollection: CodableGeometry {
1111

1212
public static var geometryType: GeoJSON.`Type`.Geometry { .geometryCollection }
1313

14-
public var bbox: AnyBoundingBox?
14+
public var _bbox: AnyBoundingBox { asAnyGeometry.bbox }
1515

1616
public var asAnyGeometry: AnyGeometry { .geometryCollection(self) }
1717

Sources/GeoJSON/Geometries/Polygon.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public struct LinearRingCoordinates<Point: GeoJSON.Point>: Boundable, Hashable,
2929

3030
public var rawValue: RawValue
3131

32-
public var bbox: RawValue.BoundingBox { rawValue.bbox }
32+
public var _bbox: RawValue.BoundingBox { rawValue.bbox }
3333

3434
public init(rawValue: RawValue) throws {
3535
guard rawValue.first == rawValue.last else {

Sources/GeoJSON/Helpers/GeoJSON+Boundable.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
import NonEmpty
1010
import Turf
1111

12-
extension NonEmpty: Boundable where Element: Boundable {
12+
extension NonEmpty: Boundable where Collection: Hashable, Element: Boundable {
1313

14-
public var bbox: Element.BoundingBox {
14+
public var _bbox: Element.BoundingBox {
1515
self.reduce(.zero, { $0.union($1.bbox) })
1616
}
1717

Sources/GeoJSON/Objects/Geometry.swift

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// File.swift
2+
// Geometry.swift
33
// GeoSwift
44
//
55
// Created by Rémi Bardon on 04/02/2022.
@@ -9,9 +9,9 @@
99
import Turf
1010

1111
/// A [GeoJSON Geometry](https://datatracker.ietf.org/doc/html/rfc7946#section-3.1).
12-
public protocol Geometry: GeoJSON.Object, Hashable {
13-
14-
var bbox: BoundingBox? { get }
12+
public protocol Geometry: GeoJSON.Object, Boundable, Hashable
13+
where BoundingBox: GeoJSON.BoundingBox
14+
{
1515

1616
/// This geometry, but type-erased.
1717
var asAnyGeometry: AnyGeometry { get }
@@ -46,7 +46,7 @@ public protocol SingleGeometry: CodableGeometry {
4646

4747
extension SingleGeometry {
4848

49-
public var bbox: Coordinates.BoundingBox? { coordinates.bbox }
49+
public var _bbox: Coordinates.BoundingBox { coordinates.bbox }
5050

5151
}
5252

@@ -89,23 +89,23 @@ public enum AnyGeometry: Geometry, Hashable, Codable {
8989
// }
9090
// }
9191

92-
public var bbox: AnyBoundingBox? {
92+
public var _bbox: AnyBoundingBox {
9393
switch self {
9494
case .geometryCollection(let geo): return geo.bbox
9595

96-
case .point2D(let geo): return geo.bbox?.asAny
97-
case .multiPoint2D(let geo): return geo.bbox?.asAny
98-
case .lineString2D(let geo): return geo.bbox?.asAny
99-
case .multiLineString2D(let geo): return geo.bbox?.asAny
100-
case .polygon2D(let geo): return geo.bbox?.asAny
101-
case .multiPolygon2D(let geo): return geo.bbox?.asAny
96+
case .point2D(let geo): return geo.bbox.asAny
97+
case .multiPoint2D(let geo): return geo.bbox.asAny
98+
case .lineString2D(let geo): return geo.bbox.asAny
99+
case .multiLineString2D(let geo): return geo.bbox.asAny
100+
case .polygon2D(let geo): return geo.bbox.asAny
101+
case .multiPolygon2D(let geo): return geo.bbox.asAny
102102

103-
case .point3D(let geo): return geo.bbox?.asAny
104-
case .multiPoint3D(let geo): return geo.bbox?.asAny
105-
case .lineString3D(let geo): return geo.bbox?.asAny
106-
case .multiLineString3D(let geo): return geo.bbox?.asAny
107-
case .polygon3D(let geo): return geo.bbox?.asAny
108-
case .multiPolygon3D(let geo): return geo.bbox?.asAny
103+
case .point3D(let geo): return geo.bbox.asAny
104+
case .multiPoint3D(let geo): return geo.bbox.asAny
105+
case .lineString3D(let geo): return geo.bbox.asAny
106+
case .multiLineString3D(let geo): return geo.bbox.asAny
107+
case .polygon3D(let geo): return geo.bbox.asAny
108+
case .multiPolygon3D(let geo): return geo.bbox.asAny
109109
}
110110
}
111111

Sources/GeoJSON/Objects/Object.swift

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,7 @@
77
//
88

99
/// A [GeoJSON Object](https://datatracker.ietf.org/doc/html/rfc7946#section-3).
10-
public protocol Object {
11-
12-
associatedtype BoundingBox: GeoJSON.BoundingBox
13-
14-
var bbox: BoundingBox? { get }
15-
16-
}
10+
public protocol Object {}
1711

1812
public protocol CodableObject: GeoJSON.Object, Codable {
1913

Sources/GeoJSON/Position.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import GeoModels
1010
import Turf
1111

1212
/// A [GeoJSON Position](https://datatracker.ietf.org/doc/html/rfc7946#section-3.1.1).
13-
public protocol Position: Hashable, Boundable {}
13+
public protocol Position: Boundable {}
1414

1515
/// A ``Position`` with two elements (longitude and latitude).
1616
public typealias Position2D = Coordinate2D

Sources/Turf/Boundable.swift

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,74 +12,86 @@ public protocol Boundable {
1212

1313
associatedtype BoundingBox: GeoModels.BoundingBox
1414

15-
var bbox: BoundingBox { get }
15+
var _bbox: BoundingBox { get }
1616

1717
}
1818

19-
extension Coordinate2D: Boundable {
19+
extension Boundable {
2020

21-
public var bbox: BoundingBox2D {
21+
public var bbox: BoundingBox { _bbox }
22+
23+
}
24+
25+
extension Boundable where Self: Hashable {
26+
27+
public var bbox: BoundingBox { BoundingBoxCache.shared.bbox(for: self) }
28+
29+
}
30+
31+
extension Coordinate2D {
32+
33+
public var _bbox: BoundingBox2D {
2234
BoundingBox2D(southWest: self, width: .zero, height: .zero)
2335
}
2436

2537
}
2638

2739
extension Coordinate3D: Boundable {
2840

29-
public var bbox: BoundingBox3D {
41+
public var _bbox: BoundingBox3D {
3042
BoundingBox3D(southWestLow: self, width: .zero, height: .zero, zHeight: .zero)
3143
}
3244

3345
}
3446

3547
extension Line2D: Boundable {
3648

37-
public var bbox: BoundingBox2D {
49+
public var _bbox: BoundingBox2D {
3850
Turf.bbox(for: [start, end])!
3951
}
4052

4153
}
4254

4355
extension Line3D: Boundable {
4456

45-
public var bbox: BoundingBox3D {
57+
public var _bbox: BoundingBox3D {
4658
Turf.bbox(for: [start, end])!
4759
}
4860

4961
}
5062

5163
extension BoundingBox2D: Boundable {
5264

53-
public var bbox: BoundingBox2D { self }
65+
public var _bbox: BoundingBox2D { self }
5466

5567
}
5668

5769
extension BoundingBox3D: Boundable {
5870

59-
public var bbox: BoundingBox3D { self }
71+
public var _bbox: BoundingBox3D { self }
6072

6173
}
6274

6375
// Extension of protocol 'Collection' cannot have an inheritance clause
6476
//extension Collection: Boundable where Element: Boundable {
6577
//
66-
// public var bbox: Element.BoundingBox {
78+
// public var _bbox: Element.BoundingBox {
6779
// self.reduce(.zero, { $0.union($1.bbox) })
6880
// }
6981
//
7082
//}
7183

7284
extension Array: Boundable where Element: Boundable {
7385

74-
public var bbox: Element.BoundingBox {
86+
public var _bbox: Element.BoundingBox {
7587
self.reduce(.zero, { $0.union($1.bbox) })
7688
}
7789

7890
}
7991

8092
extension Set: Boundable where Element: Boundable {
8193

82-
public var bbox: Element.BoundingBox {
94+
public var _bbox: Element.BoundingBox {
8395
self.reduce(.zero, { $0.union($1.bbox) })
8496
}
8597

Sources/Turf/BoundingBoxCache.swift

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//
2+
// BoundingBoxCache.swift
3+
// SwiftGeo
4+
//
5+
// Created by Rémi Bardon on 10/02/2022.
6+
// Copyright © 2022 Rémi Bardon. All rights reserved.
7+
//
8+
9+
import GeoModels
10+
11+
public class BoundingBoxCache {
12+
13+
public static let shared = BoundingBoxCache()
14+
15+
private var values = [AnyHashable: Any]()
16+
17+
private init() {}
18+
19+
internal func store<B: BoundingBox, K: Hashable>(_ value: B, forKey key: K) {
20+
values[AnyHashable(key)] = value
21+
}
22+
23+
internal func get<B: BoundingBox, K: Hashable>(_ type: B.Type, forKey key: K) -> B? {
24+
values[AnyHashable(key)] as? B
25+
}
26+
27+
public func bbox<B: Boundable & Hashable>(for boundable: B) -> B.BoundingBox {
28+
if let cachedValue = self.get(B.BoundingBox.self, forKey: boundable) {
29+
return cachedValue
30+
} else {
31+
let bbox = boundable._bbox
32+
self.store(bbox, forKey: boundable)
33+
return bbox
34+
}
35+
}
36+
37+
public func removeCache<K: Hashable>(for key: K) {
38+
values.removeValue(forKey: AnyHashable(key))
39+
}
40+
41+
}

0 commit comments

Comments
 (0)