GeoJSON을 추가, 파싱, 지도에 마커 추가 방법을 알아본다.
features
: 위치 데이터의 배열type
: feature가 대부분 저장됨geometry
: 좌표가 저장됨. property
: 원하는 키와 값을 자유롭게 저장 가능{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [102.0, 0.5]
},
"properties": {
"prop0": "value0"
}
},
{
"type": "Feature",
"geometry": {
"type": "LineString",
"coordinates": [
[102.0, 0.0], [103.0, 1.0], [104.0, 0.0], [105.0, 1.0]
]
},
"properties": {
"prop0": "value0",
"prop1": 0.0
}
},
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[100.0, 0.0], [101.0, 0.0], [101.0, 1.0],
[100.0, 1.0], [100.0, 0.0]
]
]
},
"properties": {
"prop0": "value0",
"prop1": { "this": "that" }
}
}
]
}
geojson.io 에서 제작할 수 있다.
MKGeoJSONObject
타입func fetchMap() async throws -> [MKGeoJSONObject] {
guard let url = URL(string: "https:kxapi.azurewebsites.net/geojson?apiKey=Vlma2ThXYk968FCuLLEy") else {
throw "invalid url"
}
//dataTask 요청. response 사용하지 않으므로 생략(_)
let (data, _) = try await URLSession.shared.data(from: url)
//GeoJSONDecoder. 사용법 동일.
let decoder = MKGeoJSONDecoder()
let results = try decoder.decode(data)
return results
}
override func viewDidLoad() {
super.viewDidLoad()
...
Task{
geoJsonObjects = try await fetchMap()
print(geoJsonObjects)
}
}
MKGeoJSONFeature
로 타입 캐스팅해야한다.Task{
geoJsonObjects = try await fetchMap()
for obj in geoJsonObjects {
guard let feature = obj as? MKGeoJSONFeature else {continue}
print(feature.geometry)
}
}
MKPointAnnotation
으로, 구역은 MKPolygon
으로 파싱된다. Task{
geoJsonObjects = try await fetchMap()
for obj in geoJsonObjects {
guard let feature = obj as? MKGeoJSONFeature else {continue}
let jsonDecoder = JSONDecoder()
guard let pdata = feature.properties,
let properties = try? jsonDecoder.decode(Property.self, from: pdata) else {continue}
guard let category = Category(rawValue: properties.category) else {continue}
//포인트면 위치 마킹
if let pointAnnotation = feature.geometry.first as? MKPointAnnotation {
if category == .attraction {
//지도에 마킹
mapView.addAnnotation(pointAnnotation)
}
}
}
}
if category == .attraction {
pointAnnotation.title = properties.name
pointAnnotation.subtitle = properties.desc
//지도에 마킹
mapView.addAnnotation(pointAnnotation)
}
extension ViewController: MKMapViewDelegate{
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
//사용자의 현재 위치를 표시하는 경우 기본뷰를 표시하도록 nil 리턴
guard !(annotation is MKUserLocation) else {return nil}
//어노테이션이 포인트 어노테이션이면 마커 뷰를 표시
if let pointAnnotation = annotation as? MKPointAnnotation {
let marker = mapView.dequeueReusableAnnotationView(withIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier, for: annotation)
//pointannotation에는 title, subtitle, 좌표 세 가지가 저장된다.
//따라서 우리가 설정한 카테고리에 접근하려면 커스텀 어노테이션을 만들고 어노테이션에 프로퍼티를 함께 저장해야 함.
return marker
}
return nil
}
}
import Foundation
import MapKit
import CoreLocation
class EverlandAnnotation: NSObject, MKAnnotation{
@objc dynamic var coordinate: CLLocationCoordinate2D
let properties: Property
init(coordinate: CLLocationCoordinate2D, properties: Property) {
self.coordinate = coordinate
self.properties = properties
}
var title: String?{
return properties.name
}
var subtitle: String? {
return properties.desc
}
var category: Category?{
return Category(rawValue: properties.category)
}
var image: UIImage? {
guard let category else {return nil}
return UIImage(named: category.rawValue)
}
}
Task{
geoJsonObjects = try await fetchMap()
for obj in geoJsonObjects {
guard let feature = obj as? MKGeoJSONFeature else {continue}
let jsonDecoder = JSONDecoder()
guard let pdata = feature.properties,
let properties = try? jsonDecoder.decode(Property.self, from: pdata) else {continue}
guard let category = Category(rawValue: properties.category) else {continue}
//포인트면 위치 마킹
if let pointAnnotation = feature.geometry.first as? MKPointAnnotation {
if category == .attraction {
//이부분 변경
let annotation = EverlandAnnotation(coordinate: pointAnnotation.coordinate, properties: properties)
//지도에 마킹
mapView.addAnnotation(annotation)
}
}
}
}
extension ViewController: MKMapViewDelegate{
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
//사용자의 현재 위치를 표시하는 경우 기본뷰를 표시하도록 nil 리턴
guard !(annotation is MKUserLocation) else {return nil}
//어노테이션이 포인트 어노테이션이면 마커 뷰를 표시
if let everlandAnnotation = annotation as? EverlandAnnotation {
let marker = mapView.dequeueReusableAnnotationView(withIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier, for: annotation) as! MKMarkerAnnotationView
//마커의 그리프이미지 변경
marker.glyphImage = everlandAnnotation.image
//pointannotation에는 title, subtitle, 좌표 세 가지가 저장된다.
//따라서 우리가 설정한 카테고리에 접근하려면 커스텀 어노테이션을 만들고 어노테이션에 프로퍼티를 함께 저장해야 함.
return marker
}
return nil
}
}
.glyphTintColor
는 그리프이미지 자체의 색을,
.markerTintColor
은 마커의 배경색을 변경하는데 사용한다.