Estoy usando iOS Mapbox SDK para crear una MGLShapeCollectionFeature a partir de datos de goejson FeatureCollection que provienen de una API de terceros.
guard let feature = try? MGLShape(data: jsonData, encoding: String.Encoding.utf8.rawValue) as? MGLShapeCollectionFeature else { print("Could not cast to specified MGLShapeCollectionFeature") return } El problema es que la API a veces devuelve un geojson no válido donde una sola Feature no contiene coordenadas válidas (ver más abajo) y la inicialización de MGLShape falla con una 'NSInvalidArgumentException', reason: 'A multipoint must have at least one vertex.' cual es correcta.
¿Hay alguna manera de filtrar y descartar esas Features no válidas dentro de una FeatureCollection de características que no sea analizar y corregir el geojson manualmente?
{ "type": "FeatureCollection", "features": [ { "type": "Feature", "properties": { "icaoId": "KBOS", "airSigmetType": "AIRMET", "hazard": "IFR" }, "geometry": { "type": "Polygon", "coordinates": [ [ ] ] } }, { "type": "Feature", "properties": { "icaoId": "KSLC", "airSigmetType": "AIRMET", "hazard": "IFR" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -106.63, 49.06 ], [ -104.12, 48.95 ], [ -104.17, 44.8 ], [ -106.91, 46.38 ], [ -106.63, 49.06 ] ] ] } } ] }Una posible solución es decodificar el JSON con Codable en estructuras, filtrar los elementos vacíos y volver a codificar el objeto:
struct FeatureCollection : Codable { let type : String var features : [Feature] } struct Feature : Codable { let type : String let properties : Properties let geometry : Geometry } struct Properties : Codable { let icaoId, airSigmetType, hazard : String } struct Geometry : Codable { let type : String let coordinates : [[[Double]]] } do { var result = try JSONDecoder().decode(FeatureCollection.self, from: jsonData) let filteredFeatures = result.features.filter{$0.geometry.coordinates != [[]]} result.features = filteredFeatures let filteredData = try JSONEncoder().encode(result) guard let feature = try? MGLShape(data: filteredData, encoding: String.Encoding.utf8.rawValue) as? MGLShapeCollectionFeature else { print("Could not cast to specified MGLShapeCollectionFeature") return } } catch { print(error) }Como sugirió, yo mismo filtré y escribí esta extensión en Data
extension Data { func removeEmptyCoordinates() throws -> Data { guard var geojson = try JSONSerialization.jsonObject(with: self, options: []) as? [String: Any] else { return self } fix(geojson: &geojson, processFeatureIf: NSPredicate(format: "geometry.type == 'Polygon'"), keepFeatureIf: NSPredicate(format: "%K[0][SIZE] >= 2", "geometry.coordinates")) return try JSONSerialization.data(withJSONObject: geojson, options: []) } private func fix(geojson: inout [String: Any], processFeatureIf: NSPredicate, keepFeatureIf: NSPredicate) { guard let type = geojson["type"] as? String, type == "FeatureCollection" else { // "Not a FeatureCollection" return } // "No features to fix" guard let features = geojson["features"] as? [[String: Any]] else { return } let filtered = features.filter { feature in if !processFeatureIf.evaluate(with: feature) { // not processing return true } return keepFeatureIf.evaluate(with: feature) } geojson["features"] = filtered } }