如何在iOS地图中突出显示国家

22

我正在构建一个应用程序,其中需要在世界地图上动态突出显示一些国家。

这样

简而言之,我想要自定义iOS地图的整个视图,如图所示。是否可以使用MapKit或其他方法实现?提前感谢。


1
你解决了这个问题吗? - ashokdy
1
你得到了这个结果吗?如果你找到了结果,能否请与我分享一些想法? - kalpesh
2个回答

9
您想了解 Mapbox iOS SDK,该SDK允许您使用类似于MapKit的API来制作自定义地图,并做更多的操作。特别是,您需要在TileMill中使用提供的自然地球数据集快速生成自定义地图以展示世界各国边界,启用UTFGrid互动性以便标识被点击的区域,并使用RMShape类在RMAnnotation上为地图添加/着色所需的国家多边形。这听起来有点复杂,但相关工具完全免费且开源,我可以帮助您完成此过程。

Justin,我真的很想看到一个例子,我有同样的需求,并且正在使用矢量图像,但是使用MapBox的解决方案将是史诗级的!最近我也在Twitter上关注了你,@colinpmasters。 - ColinMasters
2
@incanus 我也在尝试在mapbox中解决这个问题。我发现Tilemill现在已经过时了,而且我没有太多的运气。如果您有任何进一步的指针,将不胜感激。 - narco

0
我最近在寻找一些类似的实现可能性,而且你现在可以很容易地实现类似的功能,而无需额外的库。要做到这一点,你需要国家边界坐标数据,你可以从Natural Earth(https://www.naturalearthdata.com/)获取。不幸的是,这些数据的格式在iOS上不能轻松阅读,但你可以使用QGIS(https://www.qgis.org/en/site/forusers/download.html)将其转换为json格式,或者更准确地说是geojson格式,或者你可以直接使用其他人转换的geojson。这里有一个包含国家坐标的geojson:https://github.com/nvkelso/natural-earth-vector/blob/master/geojson/ne_110m_admin_0_countries.geojson,应该满足这个问题的需求。
我们可以解析geojson文件并使用CAShapeLayers绘制地图。下面是一个示例代码,它使用来自geojson的形状随机颜色绘制地图,结果如下方截图所示,我认为与预期结果相当接近,因此修改应该很容易。

map_from_geojson

Geojson解析和地图绘制代码示例:
import UIKit
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: ""))
    }
}

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        loadGeoJson()
    }

    func loadGeoJson() {
        guard let url = Bundle.main.url(forResource: "ne_110m_admin_0_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 = UIColor(hue: Double.random(in: 0...1), saturation: 1, brightness: 1, alpha: 1)
        
            // check https://macwright.com/2015/03/23/geojson-second-bite.html for other types info if needed
            // note that below we do not support it exactly as it should (internal cutouts in polygons are ignored)
            // but for needed purpose it should not make a big difference
        
            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: UIColor) {
        let polygonCoordinates: [CLLocationCoordinate2D] = polygon.map { coordinate in
            CLLocationCoordinate2D(latitude: coordinate[1], longitude: coordinate[0])
        }
        let points: [CGPoint] = polygonCoordinates.map { coordinate in
            coordinateToPoint(coordinate)
        }
        let path = UIBezierPath()
        path.move(to: points[0])
        for point in points {
            path.addLine(to: point)
        }
        path.close()
    
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = path.cgPath
        shapeLayer.fillColor = color.cgColor
        shapeLayer.position = CGPoint(x: 50, y: 200)
    
        view.layer.addSublayer(shapeLayer)
    }

    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)
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接