Extract data from GeoJSON - ios

I am trying to retrieve the featureclass inside the GeoJSON below.
I have updated the GeoJSON below.
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"scalerank": 8,
"name": "Grill",
"website": "www.rocargo.com/SanNicolas.html",
"natlscale": 5,
"featureclass": "Meat"
},
"geometry": {
"type": "Point",
"coordinates": [-11.1086263, 59.1438153]
}
},
{
"type": "Feature",
"properties": {
"scalerank": 8,
"name": "Queen Vic",
"website": "www.rocargo.com/SanNicolas.html",
"natlscale": 5,
"featureclass": "Fish"
},
"geometry": {
"type": "Point",
"coordinates": [-11.1190539, 59.1498404]
}
},
{
"type": "Feature",
"properties": {
"scalerank": 8,
"name": "Josephines",
"website": "www.rocargo.com/SanNicolas.html",
"natlscale": 5,
"featureclass": "Bar"
},
"geometry": {
"type": "Point",
"coordinates": [-11.1145087,59.142496]
}
},
{
"type": "Feature",
"properties": {
"scalerank": 8,
"name": "Fall",
"website": "www.rocargo.com/SanNicolas.html",
"natlscale": 5,
"featureclass": "Port"
},
"geometry": {
"type": "Point",
"coordinates": [-11.1174109, 59.1402164]
}
}
]
}
The below function can pull all the information above.
func pleaseWork() {
let urlBar = Bundle.main.path(forResource: "bars", ofType: "geojson")!
if let jsonData = NSData(contentsOfFile: urlBar) {
do {
if let jsonResult: NSDictionary = try JSONSerialization.jsonObject(with: jsonData as Data, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary {
if let responseA : NSArray = jsonResult["features"] as? NSArray {
print(responseA)
}
}
}
catch { print("Error while parsing: \(error)") }
}
I can pull all the information however, I am struggling to get the 'featureclass' information. What steps am I missing?
Thanks.
asdfadsfadsfdsafdsafdsfadsfdsafdsafdasf asdfasdfadsfdsa

I recommend to use Decodable in Swift 4. It's very simple and convenient
Create the structs
struct Collection : Decodable {
let type : String
let features : [Feature]
}
struct Feature : Decodable {
let type : String
let properties : Properties
// there is also geometry
}
struct Properties : Decodable {
let scalerank : Int
let name : String
let website : URL
let natlscale : Int
let featureclass : String
}
Decode the data and print the values for name and featureclass
let urlBar = Bundle.main.url(forResource: "bars", withExtension: "geojson")!
do {
let jsonData = try Data(contentsOf: urlBar)
let result = try JSONDecoder().decode(Collection.self, from: jsonData)
for feature in result.features {
print("name", feature.properties.name, "featureclass", feature.properties.featureclass)
}
} catch { print("Error while parsing: \(error)") }

Step by Step only, you can achieve that.
if let responseA : NSArray = jsonResult["features"] as? NSArray {
for dictVal in 0..<responseA.count
{
let featuresDict = responseA[dictVal] as! NSDictionary
let propertiesDict = featuresDict.value(forKey: "properties") as! NSDictionary
let featureClassName = propertiesDict.value(forKey: "featureclass") as! String
print(featureClassName)
}
}
Try to use this link for Complex JSON validation. You will get clarity.

Related

Parsin JSON directory inside another directory

Alamofire.request("https://example.com/stories.php", method: .post, parameters: parameters).validate().responseJSON { response in
switch response.result {
case .success:
if let json = response.result.value {
let json2 = JSON(json)
for (_, subJSON): (String, JSON) in json2[0]["stories"] {
if let arr = subJSON["user"].dictionary {
let title = arr["name"]?.string
let id = arr["id"]?.int
let photo = arr["picture"]?.string
let rel1 = InboxStories(title: title!, storyID: id!, photo:photo!)
cell.arrayOfRels.append(rel1)
}
}
cell.getStoryDel()
}
case .failure(_):
print("hata")
}
}
JSON output
[{
"stories": [{
"id": "s1",
"last_updated": "1582543824",
"user": {
"id": "2",
"name": "testuser",
"picture": "https:\/\/example.com\/ios\/images\/profile_pic\/116534.jpg"
},
"snaps_count": "1",
"snaps": [{
"id": "c1",
"mime_type": "image",
"url": "chttps:\/\/example.com\/ios\/stories\/image\/3434.jpg",
"last_updated": "1582543824"
}]
}],
"count": 1
}]
I am trying to reach user key of stories.
It returns nil in my code. How can I parse it?
stories is also an array, please note the []
And it's pretty confusing to name a dictionary arr 😉
if let stories = json2.array?.first?["stories"].array {
for story in stories {
if let dict = story["user"].dictionary {
let title = dict["name"]!.stringValue
let id = dict["id"]!.stringValue
let photo = dict["picture"]!.stringValue
let rel1 = InboxStories(title: title, storyID: id, photo:photo)
cell.arrayOfRels.append(rel1)
}
}
}
And consider to use Decodable. It's built-in and more comfortable than SwiftyJSON

Validate geojson before creating a Mapbox MGLShape

I'm using the iOS Mapbox SDK to create a MGLShapeCollectionFeature from a goejson FeatureCollection data that comes from a 3rd party API.
guard let feature = try? MGLShape(data: jsonData, encoding: String.Encoding.utf8.rawValue) as? MGLShapeCollectionFeature else {
print("Could not cast to specified MGLShapeCollectionFeature")
return
}
The problem is that the API sometimes returns an invalid geojson where a single Feature does not contain valid coordinates (see below) and initialising the MGLShape fails with a 'NSInvalidArgumentException', reason: 'A multipoint must have at least one vertex.' which is correct.
Is there a way to filter out and drop those invalid Features within a FeatureCollection other that parsing and fixing the geojson manually?
{
"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
]
]
]
}
}
]
}
A possible solution is to decode the JSON with Codable into structs, filter the empty items and encode the object back:
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)
}
As you suggested, I did the filtering myself and wrote this extension on 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
}
}

How to loop through JSON data in SwiftyJSON

I have a JSON data with structure and I don't know how to use for-loop with SwiftyJSON to get each sections entries of "path" and "updated" value. Anyone can help? Thanks.
var jsonData = "{
"css":[
{
"path": "style.css",
"updated": "12432"
},
{
"path": "base.css",
"updated": "34627"
},
],
"html":[
{
"path": "home.htm",
"updated": "3223"
},
{
"path": "about",
"updated": "3987"
}
]
}
"
I tried to code part of for-loop
let json = JSON(jsonData)
for ( ) in json {
let filepath =
let updated =
// do searching another json to found file exists and updates
}
It's on the README, under the section named "loop": https://github.com/SwiftyJSON/SwiftyJSON#loop
// If json is .Dictionary
for (key,subJson):(String, JSON) in json {
//Do something you want
}
// If json is .Array
// The `index` is 0..<json.count's string value
for (index,subJson):(String, JSON) in json {
//Do something you want
}
Applied to your specific JSON structure:
let jsonData = """
{
"css": [
{
"path": "style.css",
"updated": "12432"
},
{
"path": "base.css",
"updated": "34627"
}
],
"html": [
{
"path": "home.htm",
"updated": "3223"
},
{
"path": "about",
"updated": "3987"
}
]
}
""".data(using: .utf8)!
let json = JSON(data: jsonData)
for (_, subJson):(String, JSON) in json {
for (_, subJson):(String, JSON) in subJson {
let filepath = subJson["path"].stringValue
let updated = subJson["updated"].stringValue
print(filepath + " ~ " + updated)
}
}
Using Swift 4 Codable:
struct FileInfo: Decodable {
let path, updated: String
}
let dec = try! JSONDecoder().decode([String:[FileInfo]].self,from: jsonData)
print(dec)
As per your JSON structure, use this :
for (index,subJson):(String, JSON) in json {
print(index) // this prints "css" , "html"
for (key,subJson):(String, JSON) in subJson {
let filepath = subJson["path"]
let updated = subJson["updated"]
}
}

iOS swift separate JSON into array

I have a JSON file like this:
{
"timeline": {
"Milan": {
"placeA": [
{
"name": "Place 1",
"kind": "historic",
},
{
"name": "Place 2",
"kind": "historic",
},
{
"name": "Place 3",
"kind": "historic",
}
]
},
"Paris": {
"placeB": [
{
"name": "Place 1",
"kind": "historic",
},
{
"name": "Place 2",
"kind": "historic",
}
]
}
}
}
and in my app I need to separate this JSON and insert into a array like this for separate data with tableView section:
var arr = [[placeA],[placeB],...]
how can I do that?
P.S I use SwiftyJson
struct City {
let name : String
let kind : String
}
var cities = [City]()
let path = (try? Data(contentsOf: URL(string: "http://www.yourwebsite.json")!)) as Data!
// let jsonData = NSData(contentsOfFile: path) as NSData!
var error : NSError?
let ReadableJSON2 = JSON ( data:path!, options: JSONSerialization.ReadingOptions.mutableContainers, error: nil )
print(error)
do {
let jsonObject = try JSONSerialization.jsonObject(with: path!, options: JSONSerialization.ReadingOptions.mutableContainers) as! [String:AnyObject]
for city in jsonObject["cities"] as! [[String:AnyObject]] {
//let coordinates = position["Position"] as! [String:CLLocationDegrees]
let Cityname = city["name"] as! String
let Citykind = city["kind"] as! String
let city = City(name: cityName,description: description, )
cities.append(city)
}
} catch let error as NSError {
print(error)
}

Swift IOS Reading JSON from url

On the below method, I can get the place value but not the location value. how can I get the location?
Thank you in advance!!
func searchDB(looking: String){
var urlString:String = "URLGOESHERE?q=\(looking)"
let url = NSURL(string: urlString)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url!, completionHandler: { (data, response, error) -> Void in
if error != nil {
println(error)
}
else {
//processing data
if let arr = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: nil) as? [AnyObject] {
for currPlace in arr {
println(currPlace["name"])
println(currPlace["location"])
}
}
else {
errorOccurred = true
}
}//eo potential data
})
task.resume()
}//eom
This is the result output I am getting:
Optional(Buddha-Bar)
Optional(nil)
JSON sample:
sample data:
{
"formatted_address": "8-12 Rue Boissy d'Anglas, 75008 Paris, France",
"geometry": {
"location": {
"lat": 48.868194,
"lng": 2.321596
}
},
"icon": "http://maps.gstatic.com/mapfiles/place_api/icons/bar-71.png",
"id": "560dd225114fd10997f75ee777bad84bcb40c529",
"name": "Buddha-Bar",
"opening_hours": {
"open_now": true,
"weekday_text": []
},
"photos": [
{
"height": 848,
"html_attributions": [],
"photo_reference": "CnRnAAAAifUh9MiqwAgQYdwEp-EnS4e_nPQN_mPYIqdI49UKun_CZKxgtUh_ZqT8QBEqBuel9seoZvyyIVvA5-TlweEqO9_2tORg_cmTi_Cy5L_PAthdZd1_Krqbf7oJNy81RWD3brA8fzeIKJfQTMgo-AT19RIQAg5kKSqeoeedm69uhUWKvBoULDJ1-PoSgv4Lsg5y1rjU_pHm_Ng",
"width": 1919
}
],
"place_id": "ChIJRS81ac1v5kcRRUqQBmTTJJU",
"price_level": 3,
"rating": 3.7,
"reference": "CmReAAAAjJskNN69nw3gBVtqLpsX11Psr-QvK6cHPLhF-oDXAbYq7dwLn65b1svUJOLVnRgAbg4K3w7qCj9_hkXvx20q4YNR2714ZQQw89GyFGCtXAxonRh09_uvgK97DewsYRyUEhAczR_GzOvU0mmG1OZr0X3kGhQeJ1Vr3RSnI6VXyzh83W_LIcUK_g",
"types": [
"bar",
"restaurant",
"food",
"establishment"
]
},
Json data without spaces
sample data:
{
"formatted_address": "8-12 Rue Boissy d'Anglas, 75008 Paris, France",
"geometry": {
"location": {
"lat": 48.868194,
"lng": 2.321596
}
},
"icon": "http://maps.gstatic.com/mapfiles/place_api/icons/bar-71.png",
"id": "560dd225114fd10997f75ee777bad84bcb40c529",
"name": "Buddha-Bar",
"opening_hours": {
"open_now": true,
"weekday_text": []
},
"photos": [
{
"height": 848,
"html_attributions": [],
"photo_reference": "CnRnAAAAifUh9MiqwAgQYdwEp-EnS4e_nPQN_mPYIqdI49UKun_CZKxgtUh_ZqT8QBEqBuel9seoZvyyIVvA5-TlweEqO9_2tORg_cmTi_Cy5L_PAthdZd1_Krqbf7oJNy81RWD3brA8fzeIKJfQTMgo-AT19RIQAg5kKSqeoeedm69uhUWKvBoULDJ1-PoSgv4Lsg5y1rjU_pHm_Ng",
"width": 1919
}
],
"place_id": "ChIJRS81ac1v5kcRRUqQBmTTJJU",
"price_level": 3,
"rating": 3.7,
"reference": "CmReAAAAjJskNN69nw3gBVtqLpsX11Psr-QvK6cHPLhF-oDXAbYq7dwLn65b1svUJOLVnRgAbg4K3w7qCj9_hkXvx20q4YNR2714ZQQw89GyFGCtXAxonRh09_uvgK97DewsYRyUEhAczR_GzOvU0mmG1OZr0X3kGhQeJ1Vr3RSnI6VXyzh83W_LIcUK_g",
"types": [
"bar",
"restaurant",
"food",
"establishment"
]
},
Adding a little formatting to the pertinent part of the data:
sample data: {
"formatted_address": "8-12 Rue Boissy d'Anglas, 75008 Paris, France",
"geometry": {
"location": {
"lat": 48.868194,
"lng": 2.321596
}
},
"icon": "http://maps.gstatic.com/mapfiles/place_api/icons/bar-71.png",
"id": "560dd225114fd10997f75ee777bad84bcb40c529",
"name": "Buddha-Bar",
It is unclear what "sample data:" means as it is not quoted, it may be something added by the print statement (my guess) in which case it is not needed to access the components.
The name would be addresses as:
["name"]
The location is in lat/lon so there will be two accesses:
["geometry"]["location"]["lat"]
["geometry"]["location"]["lon"]
In the above the applicable language syntax must be applied, in Swift there will be some pain.
See json.org for information on JSON.
After some frustration and game of thrones. the messy solution was the one below.
an alternative may be api like
https://github.com/lingoer/SwiftyJSON
func searchDB(looking: String){
var errorOccurred:Bool = false
var urlString:String = "URLGOESHERE?q=\(looking)"
let url = NSURL(string: urlString)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url!, completionHandler: { (data, response, error) -> Void in
if error != nil {
println(error)
errorOccurred = true
} else {
// println(response) //response from post
//processing data
let jsonObject : AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil)
if let statusesArray = jsonObject as? NSArray{
println("********* LEVEL 1 *******")
println(statusesArray[0])
if let aStatus = statusesArray[0] as? NSDictionary{
println("********* LEVEL 2 *******")
println(aStatus)
if let geometry = aStatus["geometry"] as? NSDictionary{
println("********* LEVEL 3 *******")
println(geometry)
if let currLocation = geometry["location"] as? NSDictionary{
println("********* LEVEL 4 *******")
println(currLocation)
println(currLocation["lat"])
println(currLocation["lng"])
}
}
}
}
else {
errorOccurred = true
}
}//eo potential data
})
task.resume()
}//eom

Resources