Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ This package provides a small, focused API for composing KML documents suitable

## Quick highlights

- Compose `Placemark`s with `Point`, `LineString`, `Polygon` and derived geometry helpers (circles, arc segments, etc.).
- Compose `Placemark`s with `Point`, `LineString`, `Polygon` and derived geometry helpers (circles, arc sectors, etc.).
- Create reusable styles (`Style`, `LineStyle`, `PolyStyle`, `IconStyle`, `LabelStyle`) and assign them to placemarks.
- `ForeFlightKMLBuilder` collects placemarks and styles, emits a complete `kml` document string.
- Lightweight — no UI code.
Expand Down Expand Up @@ -66,11 +66,11 @@ presentShareSheet(with: url)
- `addPoint` Add a point with style.
- `addLine` Add a line connecting multiple coordinates.
- `addLineCircle` Add a circular line (approximated by line segments).
- `addLineSegment` Add an arc segment line geometry.
- `addLineSector` Add an arc sector line geometry.
- `addPolygon` Add a polygon with outer boundary and optional holes.
- `addPolygonCircle` Add a polygon with outer boundary and optional holes.
- `addPolygonSegment` Add a filled segment polygon (pie slice).
- `addPolygonAnnularSegment` Add a filled annular (ring) segment polygon.
- `addPolygonSector` Add a filled sector polygon (pie slice).
- `addPolygonAnnularSector` Add a filled annular (ring) sector polygon.
- `addLabel` Add a text-only label placemark at a coordinate.

### ForeflightKMLBuilder Export formats
Expand All @@ -81,7 +81,7 @@ presentShareSheet(with: url)

### Underlying elements
- `Placemark` — a Feature containing a geometry (must implement `KMLElement`). Optionally attach a `KMLStyle`.
- Geometry types: `Point`, `Line`, `LineCircle`, `LineSegment` (segment of a Circle), `Polygon`, `PolygonCircle` (filled circle), `PolygonSegment` (filled segment) `LinearRing`.
- Geometry types: `Point`, `Line`, `LineCircle`, `LineSector` (sector of a Circle), `Polygon`, `PolygonCircle` (filled circle), `PolygonSector` (filled sector) `LinearRing`.
- `Style` and substyles: `LineStyle`, `PolyStyle`, `IconStyle`, `LabelStyle`.
- `KMLColor` — helper to create the aabbggrr color values used by KML.

Expand Down
28 changes: 14 additions & 14 deletions Sources/ForeFlightKML/ForeFlightKML+Convenience.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ extension ForeFlightKMLBuilder {
return addPlacemark(placemark)
}

/// Add an arc segment line geometry.
/// Add an arc sector line geometry.
/// - Parameters:
/// - name: Display name in ForeFlight (optional)
/// - center: Center point of the arc
Expand All @@ -110,7 +110,7 @@ extension ForeFlightKMLBuilder {
/// - style: Path style defining line appearance (optional)
/// - Returns: Self for method chaining
@discardableResult
public func addLineSegment(
public func addLineSector(
name: String? = nil,
center: Coordinate,
radiusMeters: Double,
Expand All @@ -124,7 +124,7 @@ extension ForeFlightKMLBuilder {
precondition(radiusMeters > 0, "Radius must be positive")
precondition(numberOfPoints >= 3, "Need at least 3 segments for an arc")

let segment = LineSegment(
let sector = LineSector(
center: center,
radius: radiusMeters,
startAngle: startAngle,
Expand All @@ -134,7 +134,7 @@ extension ForeFlightKMLBuilder {
tessellate: tessellate
)

let placemark = Placemark(name: name, geometry: segment, style: style)
let placemark = Placemark(name: name, geometry: sector, style: style)
return addPlacemark(placemark)
}

Expand Down Expand Up @@ -205,7 +205,7 @@ extension ForeFlightKMLBuilder {
return addPlacemark(placemark)
}

/// Add a filled segment polygon (pie slice).
/// Add a filled sector polygon (pie slice).
/// - Parameters:
/// - name: Display name in ForeFlight (optional)
/// - center: Center point of the arc
Expand All @@ -218,7 +218,7 @@ extension ForeFlightKMLBuilder {
/// - style: Polygon style defining outline and optional fill (optional)
/// - Returns: Self for method chaining
@discardableResult
public func addPolygonSegment(
public func addPolygonSector(
name: String? = nil,
center: Coordinate,
radiusMeters: Double,
Expand All @@ -232,7 +232,7 @@ extension ForeFlightKMLBuilder {
precondition(radiusMeters > 0, "Radius must be positive")
precondition(numberOfPoints >= 3, "Need at least 3 segments for a segment")

let segment = PolygonSegment(
let sector = PolygonSector(
center: center,
radius: radiusMeters,
startAngle: startAngle,
Expand All @@ -242,15 +242,15 @@ extension ForeFlightKMLBuilder {
tessellate: tessellate
)

let placemark = Placemark(name: name, geometry: segment, style: style)
let placemark = Placemark(name: name, geometry: sector, style: style)
return addPlacemark(placemark)
}

/// Add a filled annular (ring) segment polygon.
/// This creates a segment between two radii, excluding the inner circle area.
/// Add a filled annular (ring) sector polygon.
/// This creates a sector between two radii, excluding the inner circle area.
/// - Parameters:
/// - name: Display name in ForeFlight (optional)
/// - center: Center point of the segment
/// - center: Center point of the sector
/// - innerRadius: Inner radius in meters (the "hole" size)
/// - outerRadius: Outer radius in meters
/// - startAngle: Starting angle in degrees (0° = North, clockwise)
Expand All @@ -261,7 +261,7 @@ extension ForeFlightKMLBuilder {
/// - style: Polygon style defining outline and optional fill (optional)
/// - Returns: Self for method chaining
@discardableResult
public func addPolygonAnnularSegment(
public func addPolygonAnnularSector(
name: String? = nil,
center: Coordinate,
innerRadius: Double,
Expand All @@ -277,7 +277,7 @@ extension ForeFlightKMLBuilder {
precondition(outerRadius > innerRadius, "Outer radius must be greater than inner radius")
precondition(numberOfPoints >= 3, "Need at least 3 segments for an annular segment")

let segment = PolygonAnnularSegment(
let sector = PolygonAnnularSector(
center: center,
innerRadius: innerRadius,
outerRadius: outerRadius,
Expand All @@ -288,7 +288,7 @@ extension ForeFlightKMLBuilder {
tessellate: tessellate
)

let placemark = Placemark(name: name, geometry: segment, style: style)
let placemark = Placemark(name: name, geometry: sector, style: style)
return addPlacemark(placemark)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import GeodesySpherical

/// An arc or sector geometry (like a pie slice) defined by center, radius, and angular extents.
///
/// The segment starts from the center, extends to the radius at the start angle,
/// The sector starts from the center, extends to the radius at the start angle,
/// follows the arc to the end angle, then returns to center, creating a closed shape.
///
/// Angles are measured in degrees clockwise from North (0°).
///
public struct LineSegment: KMLElement, LineLike {
/// The coordinates that define this arc segment
public struct LineSector: KMLElement, LineLike {
/// The coordinates that define this arc sector
public var coordinates: [Coordinate]
/// Optional altitude in meters applied to all coordinates
public var altitude: Double?
Expand All @@ -17,7 +17,7 @@ public struct LineSegment: KMLElement, LineLike {
/// Whether to tessellate (follow ground contours) when rendering
public var tessellate: Bool?

/// Create a new arc segment geometry.
/// Create a new arc sector geometry.
/// - Parameters:
/// - center: The center point of the arc
/// - radius: Radius in meters (must be positive)
Expand All @@ -39,7 +39,7 @@ public struct LineSegment: KMLElement, LineLike {
self.altitude = altitude
self.tessellate = tessellate
self.altitudeMode = altitudeMode
self.coordinates = SegmentGeometry.generateSegmentPoints(
self.coordinates = SectorGeometry.generateSectorPoints(
center: center, radius: radius, startAngle: startAngle, endAngle: endAngle,
numberOfPoints: numberOfPoints)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import GeodesySpherical

public struct PolygonAnnularSegment: KMLElement, AltitudeSupport {
public struct PolygonAnnularSector: KMLElement, AltitudeSupport {
let polygon: Polygon

public var altitudeMode: AltitudeMode? { polygon.altitudeMode }
Expand All @@ -15,7 +15,7 @@ public struct PolygonAnnularSegment: KMLElement, AltitudeSupport {
altitudeMode: AltitudeMode? = nil,
tessellate: Bool? = nil
) {
let coordinates = SegmentGeometry.generateAnnularSegmentPoints(
let coordinates = SectorGeometry.generateAnnularSectorPoints(
center: center,
innerRadius: innerRadius,
outerRadius: outerRadius,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import GeodesySpherical

public struct PolygonSegment: KMLElement, AltitudeSupport {
public struct PolygonSector: KMLElement, AltitudeSupport {
let polygon: Polygon

public var altitudeMode: AltitudeMode? { polygon.altitudeMode }
Expand All @@ -15,7 +15,7 @@ public struct PolygonSegment: KMLElement, AltitudeSupport {
altitudeMode: AltitudeMode? = nil,
tessellate: Bool? = nil
) {
let coordinates = SegmentGeometry.generateSegmentPoints(
let coordinates = SectorGeometry.generateSectorPoints(
center: center, radius: radius, startAngle: startAngle, endAngle: endAngle,
numberOfPoints: numberOfPoints)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import Foundation
import GeodesySpherical

internal enum SegmentGeometry {
static func generateSegmentPoints(
internal enum SectorGeometry {
static func generateSectorPoints(
center: Coordinate, radius: Double, startAngle: Double, endAngle: Double,
numberOfPoints: Int
) -> [Coordinate] {
var segmentPoints: [Coordinate] = []
segmentPoints.append(center)
var sectorPoints: [Coordinate] = []
sectorPoints.append(center)

let start = startAngle.truncatingRemainder(dividingBy: 360)
let end = endAngle.truncatingRemainder(dividingBy: 360)
Expand All @@ -25,15 +25,15 @@ internal enum SegmentGeometry {

let endPoint = center.destination(with: radius, bearing: currentAngle)

segmentPoints.append(endPoint)
sectorPoints.append(endPoint)
}

segmentPoints.append(center)
return segmentPoints
sectorPoints.append(center)
return sectorPoints
}

// swiftlint:disable:next function_parameter_count
static func generateAnnularSegmentPoints(
static func generateAnnularSectorPoints(
center: Coordinate,
innerRadius: Double,
outerRadius: Double,
Expand Down
2 changes: 1 addition & 1 deletion Sources/ForeFlightKML/Styles/Geometry/PathStyle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Foundation
///
/// Path styles define how lines are drawn. They apply to:
/// - Line and LineString geometries
/// - LineCircle and LineSegment geometries
/// - LineCircle and LineSector geometries
///
public struct PathStyle: KMLStyle {
public let stroke: LineStyle
Expand Down
2 changes: 1 addition & 1 deletion Sources/ForeFlightKML/Styles/LineStyle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
///
/// This style applies to:
/// - Line and LineString geometries
/// - LineCircle and LineSegment geometries
/// - LineCircle and LineSector geometries
/// - Polygon outline borders
/// - Any other line-based geometry
///
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,34 @@ import XCTest

@testable import ForeFlightKML

final class LineSegmentTests: XCTestCase {
func testBuildSegment() throws {
final class LineSectorTests: XCTestCase {
func testBuildSector() throws {
let builder = ForeFlightKMLBuilder(documentName: "My Test KML")
let center = Coordinate(latitude: 38.8700980, longitude: -77.055967)

let segmentElement = LineSegment.init(
let sectorElement = LineSector.init(
center: center, radius: 200, startAngle: 120.0, endAngle: 150.0)
builder.addPlacemark(Placemark(name: "Pizza Wedge", geometry: segmentElement))
builder.addPlacemark(Placemark(name: "Pizza Wedge", geometry: sectorElement))

let kml = builder.kmlString()

XCTAssertTrue(kml.contains("<LineString>"), "Expected KML to contain the LineSTring name")
}

func testBuildSegmentAltitude() throws {
func testBuildSectorAltitude() throws {
let builder = ForeFlightKMLBuilder(documentName: "My Test KML")
let center = Coordinate(latitude: 38.8700980, longitude: -77.055967)

let segmentElementRed = LineSegment.init(
let sectorElementRed = LineSector.init(
center: center, radius: 200, startAngle: 90.0, endAngle: 150.0, altitude: 2500)
let segmentElementBlue = LineSegment.init(
let sectorElementBlue = LineSector.init(
center: center, radius: 200, startAngle: 330.0, endAngle: 30.0, altitude: 3000)

builder.addPlacemark(
Placemark(name: "Red Pizza Wedge", geometry: segmentElementRed))
Placemark(name: "Red Pizza Wedge", geometry: sectorElementRed))
builder.addPlacemark(
Placemark(
name: "Blue Pizza Wedge", geometry: segmentElementBlue))
name: "Blue Pizza Wedge", geometry: sectorElementBlue))

let kml = builder.kmlString()

Expand Down
Loading