用于 Swift 的 GIS 工具,包括 GeoJSON 实现以及许多从 https://turfjs.org 移植的算法。
[String:Any]
、URL
、Data
和 String
加载 GeoJSON 对象并写入到这些类型Codable
和 SwiftData
(见下文)此软件包对“相等”做出了一些假设,即 1e-10
度以内的坐标被认为是相等的(这是微米级的精度,可能有点过 overkill)。请参阅 GISTool.equalityDelta。
此软件包需要 Swift 5.10 或更高版本(至少 Xcode 14),并可在 iOS (>= iOS 13)、macOS (>= macOS 10.15)、tvOS (>= tvOS 13)、watchOS (>= watchOS 6) 以及 Linux 上编译。
dependencies: [
.package(url: "https://github.com/Outdooractive/gis-tools", from: "1.8.2"),
],
targets: [
.target(name: "MyTarget", dependencies: [
.product(name: "GISTools", package: "gis-tools"),
]),
]
另请参阅 API 文档 (通过 Swift Package Index)。
import GISTools
var feature = Feature(Point(Coordinate3D(latitude: 3.870163, longitude: 11.518585)))
feature.properties = [
"test": 1,
"test2": 5.567,
"test3": [1, 2, 3],
"test4": [
"sub1": 1,
"sub2": 2
]
]
// To and from String:
let jsonString = feature.asJsonString(prettyPrinted: true)
let feature = Feature(jsonString: jsonString)
// To and from Data:
let jsonData = feature.asJsonData(prettyPrinted: true)
let feature = Feature(jsonData: jsonData)
// Using Codable:
let jsonData = try JSONEncoder().encode(feature)
let feature = try JSONDecoder().decode(Feature.self, from: jsonData)
// Generic:
let someGeoJson = GeoJsonReader.geoJsonFrom(json: [
"type": "Point",
"coordinates": [100.0, 0.0],
])
let someGeoJson = GeoJsonReader.geoJsonFrom(contentsOf: URL(...))
let someGeoJson = GeoJsonReader.geoJsonFrom(jsonData: Data(...))
let someGeoJson = GeoJsonReader.geoJsonFrom(jsonString: "{\"type\":\"Point\",\"coordinates\":[100.0,0.0]}")
switch someGeoJson {
case let point as Point: ...
}
// or
switch someGeoJson.type {
case .point: ...
}
// Wraps *any* GeoJSON into a FeatureCollection
let featureCollection = FeatureCollection(jsonData: someData)
let featureCollection = try JSONDecoder().decode(FeatureCollection.self, from: someData)
...
引用自 RFC 7946
GeoJSON 是一种基于 JavaScript 对象表示法 (JSON) 的地理空间数据交换格式。
它定义了几种 JSON 对象类型以及它们组合起来表示关于地理要素、其属性和空间范围的数据的方式。
GeoJSON 使用地理坐标参考系统,世界大地测量系统 1984,单位为十进制度。
请先阅读 RFC 以大致了解 GeoJSON 是什么以及不是什么(如果您还不知道所有这些...🙂,这种情况不太可能发生)。
每个 GeoJSON 对象的基础
/// All permitted GeoJSON types.
public enum GeoJsonType: String {
case point = "Point"
case multiPoint = "MultiPoint"
case lineString = "LineString"
case multiLineString = "MultiLineString"
case polygon = "Polygon"
case multiPolygon = "MultiPolygon"
case geometryCollection = "GeometryCollection"
case feature = "Feature"
case featureCollection = "FeatureCollection"
}
/// GeoJSON object type.
var type: GeoJsonType { get }
/// The GeoJSON's projection, which should typically be EPSG:4326.
var projection: Projection { get }
/// All of the receiver's coordinates.
var allCoordinates: [Coordinate3D] { get }
/// Any foreign members, i.e. keys in the JSON that are
/// not part of the GeoJSON standard.
var foreignMembers: [String: Any] { get set }
/// Try to initialize a GeoJSON object from any JSON and calculate a bounding box if necessary.
init?(json: Any?, calculateBoundingBox: Bool)
/// Type erased equality check.
func isEqualTo(_ other: GeoJson) -> Bool
所有 GeoJSON 对象都可以有一个边界框。如果您想使用 R 树空间索引(见下文),则这是必需的。
/// The GeoJSON's projection.
var projection: Projection { get }
/// The receiver's bounding box.
var boundingBox: BoundingBox? { get set }
/// Calculates and returns the receiver's bounding box.
func calculateBoundingBox() -> BoundingBox?
/// Calculates the receiver's bounding box and updates the `boundingBox` property.
///
/// - parameter ifNecessary: Only update the bounding box if the receiver doesn't already have one.
@discardableResult
mutating func updateBoundingBox(onlyIfNecessary ifNecessary: Bool) -> BoundingBox?
/// Check if the receiver is inside or crosses the other bounding box.
///
/// - parameter otherBoundingBox: The bounding box to check.
func intersects(_ otherBoundingBox: BoundingBox) -> Bool
GeoJSON 对象可以从多种来源初始化
/// Try to initialize a GeoJSON object from any JSON.
init?(json: Any?)
/// Try to initialize a GeoJSON object from a file.
init?(contentsOf url: URL)
/// Try to initialize a GeoJSON object from a data object.
init?(jsonData: Data)
/// Try to initialize a GeoJSON object from a string.
init?(jsonString: String)
/// Try to initialize a GeoJSON object from a Decoder.
init(from decoder: Decoder) throws
它们也可以通过多种方式导出
/// Return the GeoJson object as Key/Value pairs.
var asJson: [String: Any] { get }
/// Dump the object as JSON data.
func asJsonData(prettyPrinted: Bool = false) -> Data?
/// Dump the object as a JSON string.
func asJsonString(prettyPrinted: Bool = false) -> String?
/// Write the object in it's JSON represenation to a file.
func write(to url: URL, prettyPrinted: Bool = false) throws
/// Write the GeoJSON object to an Encoder.
func encode(to encoder: Encoder) throws
示例
let point = Point(jsonString: "{\"type\":\"Point\",\"coordinates\":[100.0,0.0]}")!
print(point.allCoordinates)
print(point.asJsonString(prettyPrinted: true)!)
let encoder = JSONEncoder()
encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
let data = try encoder.encode(point)
// This works because `FeatureCollection` will wrap any valid GeoJSON object.
// This is a good way to enforce a common structure for all loaded objects.
let featureCollection = FeatureCollection(jsonData: data)!
重要提示:导入和导出将始终以 EPSG:4326 进行,但有一个例外:没有 SRID 的 GeoJSON 对象将按原样导出。
这是一种从任何看起来像 GeoJSON 的东西创建 GeoJSON 对象的通用方法
/// Try to initialize a GeoJSON object from any JSON.
static func geoJsonFrom(json: Any?) -> GeoJson?
/// Try to initialize a GeoJSON object from a file.
static func geoJsonFrom(contentsOf url: URL) -> GeoJson?
/// Try to initialize a GeoJSON object from a data object.
static func geoJsonFrom(jsonData: Data) -> GeoJson?
/// Try to initialize a GeoJSON object from a string.
static func geoJsonFrom(jsonString: String) -> GeoJson?
示例
let json: [String: Any] = [
"type": "Point",
"coordinates": [100.0, 0.0],
"other": "something",
]
let geoJson = GeoJsonReader.geoJsonFrom(json: json)!
print("Type is \(geoJson.type.rawValue)")
print("Foreign members: \(geoJson.foreignMembers)")
switch geoJson {
case let point as Point:
print("It's a Point!")
case let multiPoint as MultiPoint:
print("It's a MultiPoint!")
case let lineString as LineString:
print("It's a LineString!")
case let multiLineString as MultiLineString:
print("It's a MultiLineString!")
case let polygon as Polygon:
print("It's a Polygon!")
case let multiPolygon as MultiPolygon:
print("It's a MultiPolygon!")
case let geometryCollection as GeometryCollection:
print("It's a GeometryCollection!")
case let feature as Feature:
print("It's a Feature!")
case let featureCollection as FeatureCollection:
print("It's a FeatureCollection!")
default:
assertionFailure("Missed an object type?")
}
重要提示:导入将始终以 EPSG:4326 进行。
坐标是此软件包中最基本的构建块。每个对象和算法都建立在它们之上
/// The coordinates projection, either EPSG:4326 or EPSG:3857.
let projection: Projection
/// The coordinate's `latitude`.
var latitude: CLLocationDegrees
/// The coordinate's `longitude`.
var longitude: CLLocationDegrees
/// The coordinate's `altitude`.
var altitude: CLLocationDistance?
/// Linear referencing, timestamp or whatever you want it to use for.
///
/// The GeoJSON specification doesn't specifiy the meaning of this value,
/// and it doesn't guarantee that parsers won't ignore or discard it. See
/// https://datatracker.ietf.org/doc/html/rfc7946#section-3.1.1.
/// - Important: The JSON for a coordinate will contain a `null` altitude value
/// if `altitude` is `nil` so that `m` won't get lost (since it is
/// the 4th value).
/// This might lead to compatibilty issues with other GeoJSON readers.
var m: Double?
/// Alias for longitude
var x: Double { longitude }
/// Alias for latitude
var y: Double { latitude }
/// Create a coordinate with `latitude`, `longitude`, `altitude` and `m`.
/// Projection will be EPSG:4326.
init(latitude: CLLocationDegrees,
longitude: CLLocationDegrees,
altitude: CLLocationDistance? = nil,
m: Double? = nil)
/// Create a coordinate with ``x``, ``y``, ``z`` and ``m``.
/// Default projection will we EPSG:3857 but can be overridden.
init(
x: Double,
y: Double,
z: Double? = nil,
m: Double? = nil,
projection: Projection = .epsg3857)
/// Reproject this coordinate.
func projected(to newProjection: Projection) -> Coordinate3D
示例
let coordinate = Coordinate3D(latitude: 0.0, longitude: 0.0)
print(coordinate.isZero)
每个 GeoJSON 对象都可以有一个矩形 BoundingBox(请参阅上面的 BoundingBoxRepresentable
)
/// The bounding box's `projection`.
let projection: Projection
/// The bounding boxes south-west (bottom-left) coordinate.
var southWest: Coordinate3D
/// The bounding boxes north-east (upper-right) coordinate.
var northEast: Coordinate3D
/// Create a bounding box with a `southWest` and `northEast` coordinate.
init(southWest: Coordinate3D, northEast: Coordinate3D)
/// Create a bounding box from `coordinates` and an optional padding in kilometers.
init?(coordinates: [Coordinate3D], paddingKilometers: Double = 0.0)
/// Create a bounding box from other bounding boxes.
init?(boundingBoxes: [BoundingBox])
/// Reproject this bounding box.
func projected(to newProjection: Projection) -> BoundingBox
示例
let point = Point(Coordinat3D(latitude: 47.56, longitude: 10.22), calculateBoundingBox: true)
print(point.boundingBox!)
实现 / Point 测试用例
Point
是单个坐标的包装器
/// The receiver's coordinate.
let coordinate: Coordinate3D
/// Initialize a Point with a coordinate.
init(_ coordinate: Coordinate3D, calculateBoundingBox: Bool = false)
/// Reproject the Point.
func projected(to newProjection: Projection) -> Point
示例
let point = Point(Coordinate3D(latitude: 47.56, longitude: 10.22))
MultiPoint
是坐标数组
/// The receiver's coordinates.
let coordinates: [Coordinate3D]
/// The receiver’s coordinates converted to Points.
var points: [Point]
/// Try to initialize a MultiPoint with some coordinates.
init?(_ coordinates: [Coordinate3D], calculateBoundingBox: Bool = false)
/// Try to initialize a MultiPoint with some Points.
init?(_ points: [Point], calculateBoundingBox: Bool = false)
/// Reproject the MultiPoint.
func projected(to newProjection: Projection) -> MultiPoint
示例
let multiPoint = MultiPoint([
Coordinate3D(latitude: 0.0, longitude: 100.0),
Coordinate3D(latitude: 1.0, longitude: 101.0)
])!
LineString
是由两个或多个坐标组成的数组,形成一条线
/// The LineString's coordinates.
let coordinates: [Coordinate3D]
/// Try to initialize a LineString with some coordinates.
init?(_ coordinates: [Coordinate3D], calculateBoundingBox: Bool = false)
/// Initialize a LineString with a LineSegment.
init(_ lineSegment: LineSegment, calculateBoundingBox: Bool = false)
/// Try to initialize a LineString with some LineSegments.
init?(_ lineSegments: [LineSegment], calculateBoundingBox: Bool = false)
/// Reproject the LineString.
func projected(to newProjection: Projection) -> LineString
示例
let lineString = LineString([
Coordinate3D(latitude: 0.0, longitude: 100.0),
Coordinate3D(latitude: 1.0, longitude: 101.0)
])!
let segment = LineSegment(
first: Coordinate3D(latitude: 0.0, longitude: 100.0),
second: Coordinate3D(latitude: 1.0, longitude: 101.0))
let lineString = LineString(lineSegment)
MultiLineString
是 LineString
的数组
/// The MultiLineString's coordinates.
let coordinates: [[Coordinate3D]]
/// The receiver’s coordinates converted to LineStrings.
var lineStrings: [LineString]
/// Try to initialize a MultiLineString with some coordinates.
init?(_ coordinates: [[Coordinate3D]], calculateBoundingBox: Bool = false)
/// Try to initialize a MultiLineString with some LineStrings.
init?(_ lineStrings: [LineString], calculateBoundingBox: Bool = false)
/// Try to initialize a MultiLineString with some LineSegments. Each LineSegment will result in one LineString.
init?(_ lineSegments: [LineSegment], calculateBoundingBox: Bool = false)
/// Reproject the MultiLineString.
func projected(to newProjection: Projection) -> MultiLineString
示例
let multiLineString = MultiLineString([
[Coordinate3D(latitude: 0.0, longitude: 100.0), Coordinate3D(latitude: 1.0, longitude: 101.0)],
[Coordinate3D(latitude: 2.0, longitude: 102.0), Coordinate3D(latitude: 3.0, longitude: 103.0)],
])!
Polygon
是由一个或多个环组成的形状,其中第一个环是包围表面的外环,内环包围表面内的孔洞。有关更多信息,请参阅 RFC 中的 第 3.1.6 节。
/// The receiver's coordinates.
let coordinates: [[Coordinate3D]]
/// The receiver's outer ring.
var outerRing: Ring?
/// All of the receiver's inner rings.
var innerRings: [Ring]?
/// All of the receiver's rings (outer + inner).
var rings: [Ring]
/// Try to initialize a Polygon with some coordinates.
init?(_ coordinates: [[Coordinate3D]], calculateBoundingBox: Bool = false)
/// Try to initialize a Polygon with some Rings.
init?(_ rings: [Ring], calculateBoundingBox: Bool = false)
/// Reproject the Polygon.
func projected(to newProjection: Projection) -> Polygon
示例
let polygonWithHole = Polygon([
[
Coordinate3D(latitude: 0.0, longitude: 100.0),
Coordinate3D(latitude: 0.0, longitude: 101.0),
Coordinate3D(latitude: 1.0, longitude: 101.0),
Coordinate3D(latitude: 1.0, longitude: 100.0),
Coordinate3D(latitude: 0.0, longitude: 100.0)
],
[
Coordinate3D(latitude: 1.0, longitude: 100.8),
Coordinate3D(latitude: 0.0, longitude: 100.8),
Coordinate3D(latitude: 0.0, longitude: 100.2),
Coordinate3D(latitude: 1.0, longitude: 100.2),
Coordinate3D(latitude: 1.0, longitude: 100.8)
],
])!
print(polygonWithHole.area)
MultiPolygon
是 Polygon
的数组
/// The receiver's coordinates.
let coordinates: [[[Coordinate3D]]]
/// The receiver’s coordinates converted to Polygons.
var polygons: [Polygon]
/// Try to initialize a MultiPolygon with some coordinates.
init?(_ coordinates: [[[Coordinate3D]]], calculateBoundingBox: Bool = false)
/// Try to initialize a MultiPolygon with some Polygons.
init?(_ polygons: [Polygon], calculateBoundingBox: Bool = false)
/// Reproject the MultiPolygon.
func projected(to newProjection: Projection) -> MultiPolygon
示例
let multiPolygon = MultiPolygon([
[
[
Coordinate3D(latitude: 2.0, longitude: 102.0),
Coordinate3D(latitude: 2.0, longitude: 103.0),
Coordinate3D(latitude: 3.0, longitude: 103.0),
Coordinate3D(latitude: 3.0, longitude: 102.0),
Coordinate3D(latitude: 2.0, longitude: 102.0),
]
],
[
[
Coordinate3D(latitude: 0.0, longitude: 100.0),
Coordinate3D(latitude: 0.0, longitude: 101.0),
Coordinate3D(latitude: 1.0, longitude: 101.0),
Coordinate3D(latitude: 1.0, longitude: 100.0),
Coordinate3D(latitude: 0.0, longitude: 100.0),
],
[
Coordinate3D(latitude: 0.0, longitude: 100.2),
Coordinate3D(latitude: 1.0, longitude: 100.2),
Coordinate3D(latitude: 1.0, longitude: 100.8),
Coordinate3D(latitude: 0.0, longitude: 100.8),
Coordinate3D(latitude: 0.0, longitude: 100.2),
]
]
])!
GeometryCollection
是 GeoJSON 几何图形的数组,即 Point
、MultiPoint
、LineString
、MultiLineString
、Polygon
、MultiPolygon
甚至 GeometryCollection
,尽管不建议使用后者。有关更多信息,请参阅 RFC 中的 第 3.1.8 节。
/// The GeometryCollection's geometry objects.
let geometries: [GeoJsonGeometry]
/// Initialize a GeometryCollection with a geometry object.
init(_ geometry: GeoJsonGeometry, calculateBoundingBox: Bool = false)
/// Initialize a GeometryCollection with some geometry objects.
init(_ geometries: [GeoJsonGeometry], calculateBoundingBox: Bool = false)
/// Reproject the GeometryCollection.
func projected(to newProjection: Projection) -> GeometryCollection
Feature
是一种容器,恰好包含一个 GeoJSON 几何图形 (Point
、MultiPoint
、LineString
、MultiLineString
、Polygon
、MultiPolygon
、GeometryCollection
),以及一些 properties
和一个可选的 id
/// A GeoJSON identifier that can either be a string or number.
/// Any parsed integer value `Int64.min ⪬ i ⪬ Int64.max` will be cast to `Int`
/// (or `Int64` on 32-bit platforms), values above `Int64.max` will be cast to `UInt`
/// (or `UInt64` on 32-bit platforms).
enum Identifier: Equatable, Hashable, CustomStringConvertible {
case string(String)
case int(Int)
case uint(UInt)
case double(Double)
}
/// An arbitrary identifier.
var id: Identifier?
/// The `Feature`s geometry object.
let geometry: GeoJsonGeometry
/// Only 'Feature' objects may have properties.
var properties: [String: Any]
/// Create a ``Feature`` from any ``GeoJsonGeometry`` object.
init(_ geometry: GeoJsonGeometry,
id: Identifier? = nil,
properties: [String: Any] = [:],
calculateBoundingBox: Bool = false)
/// Reproject the Feature.
func projected(to newProjection: Projection) -> Feature
FeatureCollection
是 Feature
对象的数组
/// The FeatureCollection's Feature objects.
private(set) var features: [Feature]
/// Initialize a FeatureCollection with one Feature.
init(_ feature: Feature, calculateBoundingBox: Bool = false)
/// Initialize a FeatureCollection with some geometry objects.
init(_ geometries: [GeoJsonGeometry], calculateBoundingBox: Bool = false)
/// Normalize any GeoJSON object into a FeatureCollection.
init?(_ geoJson: GeoJson?, calculateBoundingBox: Bool = false)
/// Reproject the FeatureCollection.
func projected(to newProjection: Projection) -> FeatureCollection
此类型有些特殊,因为它的初始化器将接受任何有效的 GeoJSON 对象,并返回一个 FeatureCollection
,如果输入是几何图形,则将输入包装在 Feature
对象中,或者如果输入是 Feature
,则收集输入。
您需要使用转换器才能将 GeoJson 与 SwiftData 一起使用(另请参阅 SwiftData 测试用例)。
首先,像这样注册转换器
GeoJsonTransformer.register()
然后像这样创建您的模型
@Attribute(.transformable(by: GeoJsonTransformer.name.rawValue)) var geoJson: GeoJson?
@Attribute(.transformable(by: GeoJsonTransformer.name.rawValue)) var point: Point?
...
这是必要的,因为 SwiftData 不能很好地与默认的 Codable 实现一起工作,所以您需要自己进行序列化...
支持以下几何类型:point
、linestring
、linearring
、polygon
、multipoint
、multilinestring
、multipolygon
、geometrycollection
和 triangle
。如果您需要更多类型,请打开一个 issue。
每个 GeoJSON 对象都有便捷的方法来将自身编码和解码为 WKB/WKT,并且 Data
和 String
都有扩展,可以从 WKB 和 WKT 解码为 GeoJSON。最终,它们都转发到 WKBCoder
和 WKTCoder
,它们负责繁重的工作。
另请参阅 WKB 测试用例。
解码
// SELECT 'POINT Z (1 2 3)'::geometry;
private let pointZData = Data(hex: "0101000080000000000000F03F00000000000000400000000000000840")!
// Generic
let point = try WKBCoder.decode(wkb: pointData, sourceProjection: .epsg4326) as! Point
let point = pointZData.asGeoJsonGeometry(sourceProjection: .epsg4326) as! Point
// Or create the geometry directly
let point = Point(wkb: pointZData, sourceProjection: .epsg4326)!
// Or create a Feature that contains the geometry
let feature = Feature(wkb: pointZData, sourceProjection: .epsg4326)
let feature = pointZData.asFeature(sourceProjection: .epsg4326)
// Or create a FeatureCollection that contains a feature with the geometry
let featureCollection = FeatureCollection(wkb: pointZData, sourceProjection: .epsg4326)
let featureCollection = pointZData.asFeatureCollection(sourceProjection: .epsg4326)
// Can also reproject on the fly
let point = try WKBCoder.decode(
wkb: pointData,
sourceProjection: .epsg4326,
targetProjection: .epsg3857
) as! Point
print(point.projection)
编码
let point = Point(Coordinate3D(latitude: 0.0, longitude: 100.0))
// Generic
let encodedPoint = WKBCoder.encode(geometry: point, targetProjection: nil)
// Convenience
let encodedPoint = point.asWKB
这与 WKB 完全相同… 另请参阅测试以了解其工作原理:WKT 测试用例
解码
private let pointZString = "POINT Z (1 2 3)"
// Generic
let point = try WKTCoder.decode(wkt: pointZString, sourceProjection: .epsg4326) as! Point
let point = pointZString.asGeoJsonGeometry(sourceProjection: .epsg4326) as! Point
// Or create the geometry directly
let point = Point(wkt: pointZString, sourceProjection: .epsg4326)!
// Or create a Feature that contains the geometry
let feature = Feature(wkt: pointZString, sourceProjection: .epsg4326)
let feature = pointZString.asFeature(sourceProjection: .epsg4326)
// Or create a FeatureCollection that contains a feature with the geometry
let featureCollection = FeatureCollection(wkt: pointZString, sourceProjection: .epsg4326)
let featureCollection = pointZString.asFeatureCollection(sourceProjection: .epsg4326)
// Can also reproject on the fly
let point = try WKTCoder.decode(
wkt: pointZString,
sourceProjection: .epsg4326,
targetProjection: .epsg3857
) as! Point
print(point.projection) // EPSG:3857
编码
let point = Point(Coordinate3D(latitude: 0.0, longitude: 100.0))
// Generic
let encodedPoint = WKTCoder.encode(geometry: point, targetProjection: nil)
// Convenience
let encodedPoint = point.asWKT
此软件包包含一个简单的 R 树实现:RTree 测试用例
var nodes: [Point] = []
50.times {
nodes.append(Point(Coordinate3D(
latitude: Double.random(in: -10.0 ... 10.0),
longitude: Double.random(in: -10.0 ... 10.0))))
}
let rTree = RTree(nodes)
let objects = rTree.search(inBoundingBox: boundingBox)
let objectsAround = rTree.search(aroundCoordinate: center, maximumDistance: maximumDistance)
这是一个用于处理 x/y/z 地图瓦片的助手。
let tile1 = MapTile(x: 138513, y: 91601, z: 18)
let center = tile1.centerCoordinate(projection: .epsg4326) // default
let boundingBox = tile1.boundingBox(projection: .epsg4326) // default
let tile2 = MapTile(coordinate: Coordinate3D(latitude: 47.56, longitude: 10.22), atZoom: 14)
let parent = tile2.parent
let firstChild = tile2.child
let allChildren = tile2.children
let quadkey = tile1.quadkey
let tile3 = MapTile(quadkey: "1202211303220032")
也与地图瓦片没有直接关系
let mpp = MapTile.metersPerPixel(at: 15.0, latitude: 45.0)
提供折线 (Polylines) 的编码器/解码器。
let polyline = [Coordinate3D(latitude: 47.56, longitude: 10.22)].encodePolyline()
let coordinates = polyline.decodePolyline()
提示:大多数算法都针对 EPSG:4326 进行了优化。使用其他投影会由于添加投影而产生性能损失。
名称 | 示例 | 源文件/测试 | |
---|---|---|---|
along (沿线) | let coordinate = lineString.coordinateAlong(distance: 100.0) |
源文件 / 测试 | |
area (面积) | Polygon(…).area |
源文件 | |
bearing (方位角) | Coordinate3D(…).bearing(to: Coordinate3D(…)) |
源文件 / 测试 | |
boolean-clockwise (布尔值-顺时针) | Polygon(…).outerRing?.isClockwise |
源文件 / 测试 | |
boolean-crosses (布尔值-交叉) | TODO | 源文件 | |
boolean-disjoint (布尔值-不相交) | let result = polygon.isDisjoint(with: lineString) |
源文件 / 测试 | |
boolean-intersects (布尔值-相交) | let result = polygon.intersects(with: lineString) |
源文件 | |
boolean-overlap (布尔值-重叠) | lineString1.isOverlapping(with: lineString2) |
源文件 / 测试 | |
boolean-parallel (布尔值-平行) | lineString1.isParallel(to: lineString2) |
源文件 / 测试 | |
boolean-point-in-polygon (布尔值-点在面内) | polygon.contains(Coordinate3D(…)) |
源文件 | |
boolean-point-on-line (布尔值-点在线上) | lineString.checkIsOnLine(Coordinate3D(…)) |
源文件 | |
boolean-valid (布尔值-有效) | anyGeometry.isValid |
源文件 | |
bbox-clip (边界框裁剪) | let clipped = lineString.clipped(to: boundingBox) |
源文件 / 测试 | |
buffer (缓冲区) | TODO | 源文件 | |
center/centroid/center-mean (中心/质心/平均中心) | let center = polygon.center |
源文件 | |
circle (圆) | let circle = point.circle(radius: 5000.0) |
源文件 / 测试 | |
conversions/helpers (转换/助手) | let distance = GISTool.convert(length: 1.0, from: .miles, to: .meters) |
源文件 | |
destination (目的地) | let destination = coordinate.destination(distance: 1000.0, bearing: 173.0) |
源文件 / 测试 | |
distance (距离) | let distance = coordinate1.distance(from: coordinate2) |
源文件 / 测试 | |
flatten (展平) | let featureCollection = anyGeometry.flattened |
源文件 / 测试 | |
frechetDistance (弗雷歇距离) | let distance = lineString.frechetDistance(from: other) |
源文件 / 测试 | |
length (长度) | let length = lineString.length |
源文件 / 测试 | |
line-arc (线弧) | let lineArc = point.lineArc(radius: 5000.0, bearing1: 20.0, bearing2: 60.0) |
源文件 / 测试 | |
line-chunk (线分块) | let chunks = lineString.chunked(segmentLength: 1000.0).lineStrings let dividedLine = lineString.evenlyDivided(segmentLength: 1.0) |
源文件 / 测试 | |
line-intersect (线相交) | let intersections = feature1.intersections(other: feature2) |
源文件 / 测试 | |
line-overlap (线重叠) | let overlappingSegments = lineString1.overlappingSegments(with: lineString2) |
源文件 / 测试 | |
line-segments (线段) | let segments = anyGeometry.lineSegments |
源文件 / 测试 | |
line-slice (线切片) | let slice = lineString.slice(start: Coordinate3D(…), end: Coordinate3D(…)) |
源文件 / 测试 | |
line-slice-along (沿线切片) | let sliced = lineString.sliceAlong(startDistance: 50.0, stopDistance: 2000.0) |
源文件 / 测试 | |
midpoint (中点) | let middle = coordinate1.midpoint(to: coordinate2) |
源文件 / 测试 | |
nearest-point (最近点) | let nearest = anyGeometry.nearestCoordinate(from: Coordinate3D(…)) |
源文件 | |
nearest-point-on-feature (要素上的最近点) | let nearest = anyGeometry. nearestCoordinateOnFeature(from: Coordinate3D(…)) |
源文件 | |
nearest-point-on-line (线上的最近点) | let nearest = lineString.nearestCoordinateOnLine(from: Coordinate3D(…))?.coordinate |
源文件 / 测试 | |
nearest-point-to-line (线外的最近点) | let nearest = lineString. nearestCoordinate(outOf: coordinates) |
源文件 | |
point-on-feature (要素上的点) | let coordinate = anyGeometry.coordinateOnFeature |
源文件 | |
points-within-polygon (面内的点) | let within = polygon.coordinatesWithin(coordinates) |
源文件 | |
point-to-line-distance (点到线距离) | let distance = lineString.distanceFrom(coordinate: Coordinate3D(…)) |
源文件 / 测试 | |
pole-of-inaccessibility (不可达极点) | TODO | 源文件 | |
polygon-to-line (面转线) | var lineStrings = polygon.lineStrings |
源文件 | |
reverse (反转) | let lineStringReversed = lineString.reversed |
源文件 / 测试 | |
rhumb-bearing (恒向线方位角) | let bearing = start.rhumbBearing(to: end) |
源文件 / 测试 | |
rhumb-destination (恒向线目的地) | let destination = coordinate.rhumbDestination(distance: 1000.0, bearing: 0.0) |
源文件 / 测试 | |
rhumb-distance (恒向线距离) | let distance = coordinate1.rhumbDistance(from: coordinate2) |
源文件 / 测试 | |
simplify (简化) | let simplified = lineString. simplified(tolerance: 5.0, highQuality: false) |
源文件 / 测试 | |
tile-cover (瓦片覆盖率) | let tileCover = anyGeometry.tileCover(atZoom: 14) |
源文件 / 测试 | |
transform-coordinates (坐标变换) | let transformed = anyGeometry.transformCoordinates({ $0 }) |
源文件 / 测试 | |
transform-rotate (旋转变换) | let transformed = anyGeometry. transformedRotate(angle: 25.0, pivot: Coordinate3D(…)) |
源文件 / 测试 | |
transform-scale (缩放变换) | let transformed = anyGeometry. transformedScale(factor: 2.5, anchor: .center) |
源文件 / 测试 | |
transform-translate (平移变换) | let transformed = anyGeometry. transformedTranslate(distance: 1000.0, direction: 25.0) |
源文件 / 测试 | |
truncate (截断) | let truncated = lineString.truncated(precision: 2, removeAltitude: true) |
源文件 / 测试 | |
union (联合) | TODO | 源文件 |
目前只有两个
请创建 issue 或打开 pull request 来修复或增强功能。
MIT
Thomas Rasch, Outdooractive