Highlight countries, SwiftUI version
Here is a geojson: https://github.com/datasets/geo-boundaries-world-110m/blob/main/countries.geojson
import SwiftUI
import CoreLocation
struct GeoJson: Codable {
let type: String
let features: [GeoJsonFeature]
}
struct GeoJsonFeature: Codable {
let type: String
let geometry: GeoJsonGeometry
}
struct GeoJsonGeometry: Codable {
let type: String
let coordinates: GeoJsonCoordinates
}
struct GeoJsonCoordinates: Codable {
var point: [Double]?
var line: [[Double]]?
var polygon: [[[Double]]]?
var multiPolygon: [[[[Double]]]]?
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let point = try? container.decode([Double].self) {
self.point = point
return
}
if let line = try? container.decode([[Double]].self) {
self.line = line
return
}
if let polygon = try? container.decode([[[Double]]].self) {
self.polygon = polygon
return
}
if let multiPolygon = try? container.decode([[[[Double]]]].self) {
self.multiPolygon = multiPolygon
return
}
throw DecodingError.valueNotFound(Self.self, .init(codingPath: [], debugDescription: ""))
}
}
struct ContentView: View {
@State private var shapes: [PolygonShape] = []
var body: some View {
ZStack {
ForEach(shapes) { shape in
Path { path in
guard let firstPoint = shape.points.first else { return }
path.move(to: firstPoint)
for point in shape.points {
path.addLine(to: point)
}
path.closeSubpath()
}
.fill(shape.color)
}
}
.onAppear {
loadGeoJson()
}
.frame(width: 300, height: 200)
.border(Color.black, width: 1)
}
func loadGeoJson() {
guard let url = Bundle.main.url(forResource: "countries", withExtension: "geojson"),
let data = try? Data(contentsOf: url),
let geoJson = try? JSONDecoder().decode(GeoJson.self, from: data)
else {
return
}
for feature in geoJson.features {
let geometry = feature.geometry
let randomColor = Color(hue: Double.random(in: 0...1), saturation: 1, brightness: 1)
if geometry.type == "Polygon", let coordinates = feature.geometry.coordinates.polygon {
for polygon in coordinates {
addShape(polygon: polygon, color: randomColor)
}
}
if geometry.type == "MultiPolygon", let coordinates = feature.geometry.coordinates.multiPolygon {
for multiPolygon in coordinates {
for polygon in multiPolygon {
addShape(polygon: polygon, color: randomColor)
}
}
}
}
}
func addShape(polygon: [[Double]], color: Color) {
let polygonCoordinates: [CLLocationCoordinate2D] = polygon.map { coordinate in
CLLocationCoordinate2D(latitude: coordinate[1], longitude: coordinate[0])
}
let points: [CGPoint] = polygonCoordinates.map { coordinate in
coordinateToPoint(coordinate)
}
let shape = PolygonShape(points: points, color: color)
shapes.append(shape)
}
func coordinateToPoint(_ coordinate: CLLocationCoordinate2D) -> CGPoint {
let width = 300.0
let height = 200.0
let x = (coordinate.longitude + 180.0) * (width / 360.0)
let latitudeRadians = coordinate.latitude * .pi / 180.0
let n = log(tan((.pi / 4.0) + (latitudeRadians / 2.0)))
let y = (height / 2.0) - (width * n / (2.0 * .pi))
return CGPoint(x: x, y: y)
}
}
struct PolygonShape: Identifiable {
let id = UUID()
let points: [CGPoint]
let color: Color
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
You can also add: let properties: [String: AnyCodableGeoJson] to get more data from .geojson
struct GeoJsonFeature: Codable {
let type: String
let geometry: GeoJsonGeometry
let properties: [String: AnyCodableGeoJson]
}