SwiftOverpassAPI 0.1.3

SwiftOverpassAPI 0.1.3

Edward Samson 维护。



  • 作者:
  • ebsamson3

SwiftOverpassAPI

CI Status Version License Platform

Busch Stadium

一个用于查询、解码和可视化 Overpass API 数据的 Swift 模块。

什么是 Overpass API?

Overpass API 是一个只读数据库,用于查询 OpenStreetMap 项目提供的开源测绘信息。更多信息请访问 Overpass API 维基OpenStreetMap 维基

安装

SwiftOverpassAPI 通过 CocoaPods 提供。要安装它,只需将以下行添加到您的 Podfile 中:

pod 'SwiftOverpassAPI'

使用

创建边界框

创建一个包含查询的框形区域

选项1:使用 MKCoordinateRegion 初始化

let center = CLLocationCoordinate2D(
	latitude: 37.7749,
	longitude: -122.4194)

let queryRegion = MKCoordinateRegion(
	center: center,
	latitudinalMeters: 50000,
	longitudinalMeters: 50000)

let boundingBox = OPBoundingBox(region: region)

选项2:使用纬度和经度初始化

let boundingBox = OPBoundingBox(
	minLatitude: 38.62661651293796,
	minLongitude: -90.1998908782745,
	maxLatitude: 38.627383487062005,
	maxLongitude: -90.1989091217254)

构建查询

对于简单的查询生成,您可以使用 OPQueryBuilder

do {
	let query = try OPQueryBuilder()
		.setTimeOut(180) //1
		.setElementTypes([.relation]) //2
		.addTagFilter(key: "network", value: "BART", exactMatch: false) //3
		.addTagFilter(key: "type", value: "route") //4
		.addTagFilter(key: "name") //5
		.setBoundingBox(boundingBox) //6
		.setOutputType(.geometry) //7
		.buildQueryString() //8
} catch {
	print(error.localizedDescription)
}
  1. 设置服务器请求的超时时间
  2. 设置一个或多个要查询的元素类型(可以是 .node.way 和/或 .relation 的任意组合)
  3. 过滤包含 "network" 标记值为 "BART" 的元素(不区分大小写)
  4. 过滤 "type" 标记值精确为 "route" 的元素
  5. 过滤所有具有 "name" 标记的元素。可以具有任何关联的值。
  6. 在指定的边界框内查询
  7. 指定查询的输出类型(请参阅以下关于“选择查询输出类型”的部分)
  8. 构建一个字符串,将其传递给访问 Overpass API 端点的 overpass 客户端

Overpass 查询语言允许进行多样化和强大的查询,这使得构建一个万能的查询构建器变得非常困难。对于更复杂的查询,您可能需要直接构建查询字符串

let boundingBoxString = OPBoundingBox(region: region).toString()

let query = """
	    data=[out:json];
	    node["network"="BART"]
		["railway"="stop"]
		\(boundingBoxString)
		->.bartStops;
	    (
		way(around.bartStops:200)["amenity"="cinema"];
		node(around.bartStops:200)["amenity"="cinema"];
	    );
	    out center;
	    """

此查询找到所有距离任何 BART(湾区快速交通)站点不到 200 米的剧院。要了解更多关于 Overpass 查询语言的信息,我建议查看 Overpass 语言指南Overpass 查询语言 WikiOverpass API 示例。您可以在浏览器中使用 Overpass Turbo 测试 overpass 查询。

选择查询输出类型

当使用 OPQueryBuiler 时,您可以从以下输出类型中选择

public enum OPQueryOutputType {
	case standard, center, geometry, recurseDown, recurseUp, recurseUpAndDown
	
	// The Overpass API language syntax for each output type
	func toString() -> String {
		switch self {
		case .standard:
			return "out;"
		case .recurseDown:
			return "(._;>;);out;"
		case .recurseUp:
			return "(._;<;);out;"
		case .recurseUpAndDown:
			return "((._;<;);>;);out;"
		case .geometry:
			return "out geom;"
		case .center:
			return "out center;"
		}
	}
}
  • 标准:不带获取额外元素或几何信息的基本输出
  • 递归向下:启用查询元素的完整几何重建。返回查询元素以及
    • 所有初始结果集中的路径的节点;加上
    • 所有初始结果集中的关系成员的节点和路径;加上
    • 所有初始结果集路径的节点
  • 递增上: 返回查询元素以及
    • 所有包含初始结果集中出现节点的路径
    • 所有包含初始结果集中出现节点或路径的关系
    • 所有包含在初始结果集中出现的路径的关系
  • 递增上下: 首先递增上,然后对向上递增的结果递增下
  • 几何: 返回完整的足够用于可视化的元素几何信息
  • 中心: 返回元素包含其中心坐标。当您不想可视化完整的元素几何时,这是最佳/最有效率的选项。

制作Overpass请求

let client = OPClient() //1
client.endpoint = .kumiSystems //2

//3
client.fetchElements(query: query) { result in
	switch result {
	case .failure(let error):
		print(error.localizedDescription)
	case .success(let elements):
		print(elements) // Do something with returned the elements 
	}
}
  1. 实例化客户端
  2. 指定一个端点:提供的免费使用的端点通常会较慢,并可能限制您的使用。为了更好的性能,您可以指定您自己的自定义端点。
  3. 获取元素:解码后的响应将以字典的形式返回,键是Overpass元素的数据库ID。

生成MapKit可视化

为所有元素生成返回的元素字典的可视化

// Creates a dictionary of mapkit visualizations keyed by the corresponding element's id
let visualizations = OPVisualizationGenerator
	.mapKitVisualizations(forElements: elements)

为单个元素生成可视化

if let visualization = OPVisualizationGenerator.mapKitVisualization(forElement: element) {
	// Do something
} else {
	print("Element doesn't have a geometry to visualize")
}

通过MKMapView显示可视化

步骤1: 使用包含的可视化生成器将叠加和注释添加到mapView中

func addVisualizations(_ visualizations: [Int: OPMapKitVisualization]) {
		
	var annotations = [MKAnnotation]()
	var polylines = [MKPolyline]()
	var polygons = [MKPolygon]()
		
	for visualization in visualizations.values {
		switch visualization {
		case .annotation(let annotation):
			newAnnotations.append(annotation)
		case .polyline(let polyline):
			polylines.append(polyline)
		case .polylines(let newPolylines):
			polylines.append(contentsOf: newPolylines)
		case .polygon(let polygon):
			polygons.append(polygon)
		case .polygons(let newPolygons):
			polygons.append(contentsOf: newPolygons)
		}
	}

	if #available(iOS 13, *) {
		// MKMultipolyline and MKMultipolygon generate a single renderer for all of their elements. If available, it is more efficient than creating a renderer for each overlay. 
    	let multiPolyline = MKMultiPolyline(polylines)
		let multiPolygon = MKMultiPolygon(polygons)
		mapView.addOverlay(multiPolygon)
		mapView.addOverlay(multiPolyline)
	} else {
		mapView.addOverlays(polygons)
		mapView.addOverlays(polylines)
	}

	mapView.addAnnotations(annotations)
}

根据其情况,一个可视化可以有以下相关值类型之一

  1. MKAnnotation:用于单个坐标。注释的标题是元素名称标签的值。
  2. MKPolyline:通常用于道路
  3. MKPolygon:通常用于简单的结构,如建筑物
  4. [MKPolyline]:一个相关多线数组,如在路径或航道中的集合
  5. [MKPolygon]:组成更复杂结构的多个多边形数组。

步骤2: 显示叠加和注释的视图

extension MapViewController: MKMapViewDelegate {
	// Delegate method for rendering overlays
	func mapView(
		_ mapView: MKMapView,
		rendererFor overlay: MKOverlay) -> MKOverlayRenderer
	{
		let strokeWidth: CGFloat = 2
		let strokeColor = UIColor.theme
		let fillColor = UIColor.theme.withAlphaComponent(0.5)
		
		if let polyline = overlay as? MKPolyline {
			let renderer = MKPolylineRenderer(
				polyline: polyline)
			renderer.strokeColor = strokeColor
			renderer.lineWidth = strokeWidth
			return renderer
		} else if let polygon = overlay as? MKPolygon {
			let renderer = MKPolygonRenderer(
				polygon: polygon)
			renderer.fillColor = fillColor
			renderer.strokeColor = strokeColor
			renderer.lineWidth = strokeWidth
			return renderer
		}	else if let multiPolyline = overlay as? MKMultiPolyline {
			let renderer = MKMultiPolylineRenderer(
				multiPolyline: multiPolyline)
			renderer.strokeColor = strokeColor
			renderer.lineWidth = strokeWidth
			return renderer
		} else if let multiPolygon = overlay as? MKMultiPolygon {
			let renderer = MKMultiPolygonRenderer(
				multiPolygon: multiPolygon)
			renderer.fillColor = fillColor
			renderer.strokeColor = strokeColor
			renderer.lineWidth = strokeWidth
			return renderer
		} else {
			return MKOverlayRenderer()
		}
	}

	/*
		// Make sure to add the following when configure your mapView:
		
		let markerReuseIdentifier = "MarkerAnnotationView"
		
		mapView.register(
			MKMarkerAnnotationView.self,
			forAnnotationViewWithReuseIdentifier: markerReuseIdentifier)
	*/
	
	// Delegate method for setting annotation views.
	func mapView(
		_ mapView: MKMapView,
		viewFor annotation: MKAnnotation) -> MKAnnotationView?
	{
		guard 
			let pointAnnotation = annotation as? MKPointAnnotation 
		else {
			return nil
		}
		
		let view = MKMarkerAnnotationView(
			annotation: pointAnnotation,
			reuseIdentifier: markerReuseIdentifier)
		
		view.markerTintColor = UIColor.theme
		return view
	}
}

示例应用

Chicago Buildings Chicago Tourism Bart Subway Lines

要运行示例项目,请克隆仓库,然后在示例目录中首先运行pod install

作者

ebsamson3, [email protected]

致谢

感谢所有为Overpass API和OpenStreetMap做出贡献的人。感谢Martin Raifer,他的osmtogeojson代码帮助我节省了大量时间,并帮助我理解如何处理Overpass API元素。

许可证

SwiftOverpassAPI遵循MIT许可证。请参阅LICENSE文件了解详细信息。