Google Maps SDK heatmap wont update upon array append - google-maps

EDIT: Look at my comment in the solution to see what I changed in the updateUIView.
I am trying to update a heatmap I have on my map via a button press.
Here is how the screen looks:
GIF of how the screen and interaction look
I have the array that the Map uses set to an #Binding variable and the array itself is initialized as an #State variable. The original preset values (as can be seen in the code below) appear on the map but the modified coordinate does not.
import SwiftUI
import GoogleMaps
import GoogleMapsUtils
struct GoogleMapsViewContainer: View {
#ObservedObject var locationManager = LocationManager()
#State var heatmapWeightedData: [GMUWeightedLatLng] = [
GMUWeightedLatLng(coordinate: CLLocationCoordinate2D(latitude: -33.86, longitude: 151.20), intensity: 1),
GMUWeightedLatLng(coordinate: CLLocationCoordinate2D(latitude: -33.8623, longitude: 151.2003), intensity: 3),
GMUWeightedLatLng(coordinate: CLLocationCoordinate2D(latitude: -33.8635, longitude: 151.2010), intensity: 3),
GMUWeightedLatLng(coordinate: CLLocationCoordinate2D(latitude: -33.8587, longitude: 151.1970), intensity: 3),
GMUWeightedLatLng(coordinate: CLLocationCoordinate2D(latitude: -33.8579, longitude: 151.1987), intensity: 5)
]
#Environment(.dismiss) var dismiss
var body: some View {
VStack {
HStack {
Spacer()
Button("Done") {
dismiss()
}
.padding(.top, 45.0)
}
.padding(.trailing, 30.0)
ZStack {
GoogleMapsView(heatmapWeightedData: $heatmapWeightedData)
VStack {
Spacer()
Button(action: addTrashLocation) {
Text("Mark Trash in My Area")
}
.padding()
.overlay(
RoundedRectangle(cornerRadius: 30)
.stroke(/#START_MENU_TOKEN#/Color(hue: 0.373, saturation: 0.717, brightness: 0.466)/#END_MENU_TOKEN#/, lineWidth: 2)
)
.background(.white)
.cornerRadius(30)
.padding(.bottom, 50.0)
}
}
}
.padding(.top, -30)
.ignoresSafeArea()
}
func addTrashLocation() {
self.heatmapWeightedData.append(GMUWeightedLatLng(coordinate: CLLocationCoordinate2D(latitude: -33.8606, longitude: 151.2011), intensity: 5))
}
}
This below is the GoogleMapsView function the above is referencing.
import SwiftUI
import GoogleMaps
import GoogleMapsUtils
struct GoogleMapsView: UIViewRepresentable {
#ObservedObject var locationManager = LocationManager()
var marker: GMSMarker = GMSMarker()
#Binding var heatmapWeightedData: [GMUWeightedLatLng]
func makeUIView(context: Context) -> GMSMapView {
let camera = GMSCameraPosition.camera(withLatitude: locationManager.latitude, longitude: locationManager.longitude, zoom: 15)
let mapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera)
let heatmapLayer = GMUHeatmapTileLayer()
heatmapLayer.radius = 150
heatmapLayer.weightedData = heatmapWeightedData
heatmapLayer.map = mapView
return mapView
}
func updateUIView(_ mapView: GMSMapView, context: UIViewRepresentableContext<GoogleMapsView>) {
marker.position = CLLocationCoordinate2D(latitude: locationManager.latitude, longitude: locationManager.longitude)
marker.title = "My location"
marker.map = mapView
let heatmapLayer = GMUHeatmapTileLayer()
heatmapLayer.radius = 150
heatmapLayer.weightedData = heatmapWeightedData
heatmapLayer.map = mapView
mapView.animate(toLocation: CLLocationCoordinate2D(latitude: -33.86, longitude: 151.20))
}
}
In order to be able to use the Google Maps SDK in SwiftUI, I went through a lot of trouble messing around with UIViewRepresentable and an AI since the internet couldn't help me with my answers. This situation has led me to many problems and I would not be surprised if this is somehow not working due to some nuance in the UIViewRepresentable I am unaware of.
How do I get the map to update the location?

You should use #StateObject to initialize ObservableObjects
#StateObject var locationManager = LocationManager()
Then use #ObserveObject or #EnvironmentObject to pass around.
#ObservedObject var locationManager : LocationManager
https://developer.apple.com/documentation/swiftui/managing-model-data-in-your-app
Every time you call LocationManager() you are creating a different instance. One does not know about the other.

Related

Getting latitude and longitude from a json file to create a map

I wanted to modify apple's earthquakes project to display a map together with the location magnitude and time. In the json file I can see the coordinates but for the life of me I cannot read them and use them as latitude and longitude for the map. I succeeded to display the map by using the address (title) but the format changes and there are too many possibilities to account for.
The earthquake project can be downloaded at https://developer.apple.com/documentation/coredata/loading_and_displaying_a_large_data_feed
I post the Quake.swift file below so you may have an idea of what I tried. I added a coordinates characteristic to their magnitude, place and time first as an array and then as a string but I always fail to read it and use it to display the map as latitude and longitude.
Thanks in advance for your help.
The json file is long so I post a few lines here to give you an idea of the format:
{"type":"FeatureCollection","metadata":{"generated":1648109722000,"url":"https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_month.geojson","title":"USGS All Earthquakes, Past Month","status":200,"api":"1.10.3","count":9406},"features":[{"type":"Feature","properties":{"mag":4.5,"place":"south of the Fiji Islands","time":1648106910967,"updated":1648108178040,"tz":null,"url":"https://earthquake.usgs.gov/earthquakes/eventpage/us7000gwsr","detail":"https://earthquake.usgs.gov/earthquakes/feed/v1.0/detail/us7000gwsr.geojson","felt":null,"cdi":null,"mmi":null,"alert":null,"status":"reviewed","tsunami":0,"sig":312,"net":"us","code":"7000gwsr","ids":",us7000gwsr,","sources":",us,","types":",origin,phase-data,","nst":null,"dmin":5.374,"rms":1.03,"gap":102,"magType":"mb","type":"earthquake","title":"M 4.5 - south of the Fiji Islands"},"geometry":{"type":"Point","coordinates":[179.1712,-24.5374,534.35]},"id":"us7000gwsr"},
{"type":"Feature","properties":{"mag":1.95000005,"place":"2 km NE of Pāhala, Hawaii","time":1648106708550,"updated":1648106923140,"tz":null,"url":"https://earthquake.usgs.gov/earthquakes/eventpage/hv72960677","detail":"https://earthquake.usgs.gov/earthquakes/feed/v1.0/detail/hv72960677.geojson","felt":null,"cdi":null,"mmi":null,"alert":null,"status":"automatic","tsunami":0,"sig":59,"net":"hv","code":"72960677","ids":",hv72960677,","sources":",hv,","types":",origin,phase-data,","nst":33,"dmin":null,"rms":0.109999999,"gap":136,"magType":"md","type":"earthquake","title":"M 2.0 - 2 km NE of Pāhala, Hawaii"},"geometry":{"type":"Point","coordinates":[-155.463333129883,19.2151660919189,34.9500007629395]},"id":"hv72960677"},
{"type":"Feature","properties":{"mag":1.75,"place":"4km SE of Calabasas, CA","time":1648106545420,"updated":1648109717670,"tz":null,"url":"https://earthquake.usgs.gov/earthquakes/eventpage/ci39976447","detail":"https://earthquake.usgs.gov/earthquakes/feed/v1.0/detail/ci39976447.geojson","felt":7,"cdi":3.1,"mmi":null,"alert":null,"status":"automatic","tsunami":0,"sig":49,"net":"ci","code":"39976447","ids":",ci39976447,","sources":",ci,","types":",dyfi,nearby-cities,origin,phase-data,scitech-link,","nst":33,"dmin":0.04554,"rms":0.27,"gap":56,"magType":"ml","type":"earthquake","title":"M 1.8 - 4km SE of Calabasas, CA"},"geometry":{"type":"Point","coordinates":[-118.61,34.1285,2.92]},"id":"ci39976447"},
The Quake.swift file:
import CoreData
import SwiftUI
import OSLog
// MARK: - Core Data
/// Managed object subclass for the Quake entity.
class Quake: NSManagedObject, Identifiable {
// The characteristics of a quake.
#NSManaged var magnitude: Float
#NSManaged var place: String
#NSManaged var time: Date
#NSManaged var coordinates: String
// A unique identifier used to avoid duplicates in the persistent store.
// Constrain the Quake entity on this attribute in the data model editor.
#NSManaged var code: String
/// Updates a Quake instance with the values from a QuakeProperties.
func update(from quakeProperties: QuakeProperties) throws {
let dictionary = quakeProperties.dictionaryValue
guard let newCode = dictionary["code"] as? String,
let newMagnitude = dictionary["magnitude"] as? Float,
let newPlace = dictionary["place"] as? String,
let newTime = dictionary["time"] as? Date,
let newCoordinates = dictionary["coordinates"] as? String
else {
throw QuakeError.missingData
}
code = newCode
magnitude = newMagnitude
place = newPlace
time = newTime
coordinates = newCoordinates
}
}
// MARK: - SwiftUI
extension Quake {
/// The color which corresponds with the quake's magnitude.
var color: Color {
switch magnitude {
case 0..<1:
return .green
case 1..<2:
return .yellow
case 2..<3:
return .orange
case 3..<5:
return .red
case 5..<Float.greatestFiniteMagnitude:
return .init(red: 0.8, green: 0.2, blue: 0.7)
default:
return .gray
}
}
/// An earthquake for use with canvas previews.
static var preview: Quake {
let quakes = Quake.makePreviews(count: 1)
return quakes[0]
}
#discardableResult
static func makePreviews(count: Int) -> [Quake] {
var quakes = [Quake]()
let viewContext = QuakesProvider.preview.container.viewContext
for index in 0..<count {
let quake = Quake(context: viewContext)
quake.code = UUID().uuidString
quake.time = Date().addingTimeInterval(Double(index) * -300)
quake.magnitude = .random(in: -1.1...10.0)
quake.place = "15km SSW of Cupertino, CA"
quake.coordinates = "-117.7153333,35.8655,7.59"
quakes.append(quake)
}
return quakes
}
}
// MARK: - Codable
/// creating or updating Quake instances.
struct GeoJSON: Decodable {
private enum RootCodingKeys: String, CodingKey {
case features
}
private enum FeatureCodingKeys: String, CodingKey {
case properties
}
private(set) var quakePropertiesList = [QuakeProperties]()
init(from decoder: Decoder) throws {
let rootContainer = try decoder.container(keyedBy: RootCodingKeys.self)
var featuresContainer = try rootContainer.nestedUnkeyedContainer(forKey: .features)
while !featuresContainer.isAtEnd {
let propertiesContainer = try featuresContainer.nestedContainer(keyedBy: FeatureCodingKeys.self)
// Decodes a single quake from the data, and appends it to the array, ignoring invalid data.
if let properties = try? propertiesContainer.decode(QuakeProperties.self, forKey: .properties) {
quakePropertiesList.append(properties)
}
}
}
}
/// A struct encapsulating the properties of a Quake.
struct QuakeProperties: Decodable {
// MARK: Codable
private enum CodingKeys: String, CodingKey {
case magnitude = "mag"
case place
case time
case code
case coordinates
}
let magnitude: Float // 1.9
let place: String // "21km ENE of Honaunau-Napoopoo, Hawaii"
let time: Double // 1539187727610
let code: String // "70643082"
let coordinates: String // [-117.7153333,35.8655,7.59]
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
let rawMagnitude = try? values.decode(Float.self, forKey: .magnitude)
let rawPlace = try? values.decode(String.self, forKey: .place)
let rawTime = try? values.decode(Double.self, forKey: .time)
let rawCode = try? values.decode(String.self, forKey: .code)
let rawCoordinates = try? values.decode(String.self, forKey: .coordinates)
// Ignore earthquakes with missing data.
guard let magntiude = rawMagnitude,
let place = rawPlace,
let time = rawTime,
let code = rawCode,
let coordinates = rawCoordinates
else {
let values = "code = \(rawCode?.description ?? "nil"), "
+ "mag = \(rawMagnitude?.description ?? "nil"), "
+ "place = \(rawPlace?.description ?? "nil"), "
+ "time = \(rawTime?.description ?? "nil"), "
+ "coordinates = \(rawCoordinates?.description ?? "nil")"
let logger = Logger(subsystem: "com.example.apple-samplecode.Earthquakes", category: "parsing")
logger.debug("Ignored: \(values)")
throw QuakeError.missingData
}
self.magnitude = magntiude
self.place = place
self.time = time
self.code = code
self.coordinates = coordinates
}
// The keys must have the same name as the attributes of the Quake entity.
var dictionaryValue: [String: Any] {
[
"magnitude": magnitude,
"place": place,
"time": Date(timeIntervalSince1970: TimeInterval(time) / 1000),
"code": code,
"coordinates": coordinates
]
}
}
The coordinates are not on the same level as properties, they are in a sibling geometry. The basic pattern is
{
"features": [
{
"properties": {
"mag":1.9,
"place":"21km ENE of Honaunau-Napoopoo, Hawaii",
"time":1539187727610,"updated":1539187924350,
"code":"70643082"
},
"geometry" : {
"coordinates": [-122.8096695,38.8364983,1.96]
}
}
]
}
You have to decode the coordinates in GeoJSON by adding geometry to FeatureCodingKeys. And you have to extend the Core Data model to preserve the coordinates.
In the Core Data model add two properties
longitude - Double - non-optional, use scalar type
latitude - Double - non-optional, use scalar type
In Quake.swift
import CoreLocation
In the class Quake add
#NSManaged var latitude: CLLocationDegrees
#NSManaged var longitude: CLLocationDegrees
and replace update(from with
func update(from quakeProperties: QuakeProperties) throws {
let dictionary = quakeProperties.dictionaryValue
guard let newCode = dictionary["code"] as? String,
let newMagnitude = dictionary["magnitude"] as? Float,
let newPlace = dictionary["place"] as? String,
let newTime = dictionary["time"] as? Date,
let newLatitude = dictionary["latitude"] as? CLLocationDegrees,
let newLongitude = dictionary["longitude"] as? CLLocationDegrees
else {
throw QuakeError.missingData
}
code = newCode
magnitude = newMagnitude
place = newPlace
time = newTime
latitude = newLatitude
longitude = newLongitude
}
In the Quake extension add
var coordinate : CLLocationCoordinate2D {
CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
}
In GeoJSON extend FeatureCodingKeys
private enum FeatureCodingKeys: String, CodingKey {
case properties, geometry
}
and replace the while loop with
while !featuresContainer.isAtEnd {
let propertiesContainer = try featuresContainer.nestedContainer(keyedBy: FeatureCodingKeys.self)
// Decodes a single quake from the data, and appends it to the array, ignoring invalid data.
if var properties = try? propertiesContainer.decode(QuakeProperties.self, forKey: .properties),
let geometry = try? propertiesContainer.decode(QuakeGeometry.self, forKey: .geometry) {
let coordinates = geometry.coordinates
properties.longitude = coordinates[0]
properties.latitude = coordinates[1]
quakePropertiesList.append(properties)
}
}
Add the struct
struct QuakeGeometry: Decodable {
let coordinates : [Double]
}
In QuakeProperties add
var latitude : CLLocationDegrees = 0.0
var longitude : CLLocationDegrees = 0.0
and replace dictionaryValue with
var dictionaryValue: [String: Any] {
[
"magnitude": magnitude,
"place": place,
"time": Date(timeIntervalSince1970: TimeInterval(time) / 1000),
"code": code,
"latitude": latitude,
"longitude": longitude
]
}
Finally in DetailView.swift
import MapKit
and replace QuakeDetail with
struct QuakeDetail: View {
var quake: Quake
#State private var region : MKCoordinateRegion
init(quake : Quake) {
self.quake = quake
_region = State(wrappedValue: MKCoordinateRegion(center: quake.coordinate,
span: MKCoordinateSpan(latitudeDelta: 0.3, longitudeDelta: 0.3)))
}
var body: some View {
VStack {
QuakeMagnitude(quake: quake)
Text(quake.place)
.font(.title3)
.bold()
Text("\(quake.time.formatted())")
.foregroundStyle(Color.secondary)
Text("\(quake.latitude) - \(quake.longitude)")
Map(coordinateRegion: $region, annotationItems: [quake]) { item in
MapMarker(coordinate: item.coordinate, tint: .red)
}
}
}
}

Draw polylines between two or more marker on GMSMapView with Swift3

I tried to implement differents solutions for draw polyLines between markers on my GMSMapView but is not working. Do you have others solutions or maybe do you know what's wrong in my code ?
override func viewDidLoad() {
super.viewDidLoad()
let path = GMSMutablePath()
_data = _modelDelivery.getGarageFromDeliver(refDelivery: _delivery._ref)
let camera = GMSCameraPosition.camera(withLatitude: 48.853183, longitude: 2.369144, zoom: 13.0)
self._mapView.camera = camera
let marker = GMSMarker()
marker.position = CLLocationCoordinate2D(latitude: 48.8531827, longitude: 2.3691443000000163)
path.add(marker.position)
marker.title = "Ma position"
marker.icon = GMSMarker.markerImage(with: UIColor.blue)
marker.map = self._mapView
for elem in _data {
let address = elem._adress + ", " + elem._city + ", " + String(elem._zipCode) + ", France"
let geocoder = CLGeocoder()
geocoder.geocodeAddressString(address) {
placemarks, error in
let placemark = placemarks?.first
let lat = placemark?.location?.coordinate.latitude
let lon = placemark?.location?.coordinate.longitude
let marker = GMSMarker()
marker.position = CLLocationCoordinate2D(latitude: lat!, longitude: lon!)
path.add(marker.position)
marker.title = elem._nameOfGarage
marker.snippet = elem._adress + "\nOrdre de passege : "
marker.map = self._mapView
}
}
let rectangle = GMSPolyline(path: path)
rectangle.strokeWidth = 4
rectangle.strokeColor = UIColor.red
rectangle.map = self._mapView
}
I believe you have configured everything fine in https://console.developers.google.com.
We can draw a polyline between two coordinates. You need to create your ServerKey on https://console.developers.google.com where you have created your project for GoogleMaps and then use that Server Key in the URL below.
This draws polyline between two coordinates.
func drawPath() {
//Taken two source and destinations coordinates. Take the coordinates in this way. "Lat,Long" in a String
let sourceString = "28.6562,77.2410"
let destinationString = "27.1750,78.0422"
//GoogleMaps Web API to draw the polyline
let combinedUrl : String = "https://maps.googleapis.com/maps/api/directions/json?" + "origin=\(sourceString)&destination=\(destinationString)&key=YOUR_SERVER_KEY"
let url = URL(string:combinedUrl)
let task = URLSession.shared.dataTask(with: url!) { (data:Data?, response:URLResponse?, error:Error?) in
if error != nil {
print(error.debugDescription)
return
}
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! NSDictionary
//We need to get to the points key in overview_polyline object in order to pass the points to GMSPath.
let route = (((json.object(forKey: "routes") as! NSArray).object(at: 0) as! NSDictionary).object(forKey: "overview_polyline") as! NSDictionary).value(forKey: "points") as! String
//Draw on main thread always else it will crash
DispatchQueue.main.async {
let path = GMSPath(fromEncodedPath:route)!
let polyline = GMSPolyline(path: path)
polyline.strokeColor = UIColor.green
polyline.strokeWidth = 5.0
//mapView is your GoogleMaps Object i.e. _mapView in your case
polyline.map = self.mapView
}
} catch {
}
}
task.resume()
}
JSON points key data required.
Later on you can animate to the drawn region first coordinate using the GMSCameraPosition.
Similarly for multiple points, just pass the respective coordinates in a for loop and call drawPath function.
You can have a look at this Video Tutorial too.

How can I remove the GMSPolyline in Swift 3

I'm making the GMSPolyline in the GoogleMap. When I want to remove the GMSPolyline by coding polyline.map = nil, but it can not working for me. I'm copy some coding in the below.
(added the marker before, I need remove the polyline only)
Thank you for your help!!!
my coding:
func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
//MARK: create the GMSPolyline
let polyline = GMSPolyline()
//MARK: remove the old polyline from the GoogleMap
polyline.map = nil
let origin = "\(mapView.myLocation!.coordinate.latitude),\(mapView.myLocation!.coordinate.longitude)"
let destination = "\(marker.position.latitude),\(marker.position.longitude)"
let url = "https://maps.googleapis.com/maps/api/directions/json?origin=\(origin)&destination=\(destination)&mode=driving"
Alamofire.request(url).responseJSON { response in
let json = JSON(data: response.data!)
let routes = json["routes"].arrayValue
//MARK: print route using Polyline
for route in routes
{
let routeOverviewPolyline = route["overview_polyline"].dictionary
let points = routeOverviewPolyline?["points"]?.stringValue
let path = GMSPath(fromEncodedPath: points!)
polyline = GMSPolyline(path: path)
polyline.strokeWidth = 4
polyline.strokeColor = UIColor.yellow
polyline.isTappable = true
polyline.map = self.mapView
}
}
return false
}
you should clear the map before adding new polyline on the map.
clear() function of GMSMapView
/**
Clears all markup that has been added to the map, including markers, polylines and ground overlays.
This will not clear the visible location dot or reset the current mapType.
*/
Try this
func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
//MARK: create the GMSPolyline
let polyline = GMSPolyline()
//MARK: remove the old polyline from the GoogleMap
mapView.clear()
//TODO:- Redraw all marker here.
let origin = "\(mapView.myLocation!.coordinate.latitude),\(mapView.myLocation!.coordinate.longitude)"
let destination = "\(marker.position.latitude),\(marker.position.longitude)"
let url = "https://maps.googleapis.com/maps/api/directions/json?origin=\(origin)&destination=\(destination)&mode=driving"
Alamofire.request(url).responseJSON { response in
let json = JSON(data: response.data!)
let routes = json["routes"].arrayValue
//MARK: print route using Polyline
for route in routes
{
let routeOverviewPolyline = route["overview_polyline"].dictionary
let points = routeOverviewPolyline?["points"]?.stringValue
let path = GMSPath(fromEncodedPath: points!)
polyline = GMSPolyline(path: path)
polyline.strokeWidth = 4
polyline.strokeColor = UIColor.yellow
polyline.isTappable = true
polyline.map = self.mapView
}
}
return false
}
Declare polyline outside the function as a class property.
var routePolyline: GMSPolyline? = nil
Then to clear old polyline before adding new one, use-
self.routePolyline?.map = nil
This will work as expected!
for poll in mapkit.overlays {
mapkit.removeOverlay(poll)
}
var polyline: GMSPolyline?
func drawPathOnMap() {
if polyline != nil {
//remove existing the gmspoly line from your map
polyline!.map = nil
}
let points = //Your Point
let path = GMSPath(fromEncodedPath: points)
polyline = GMSPolyline(path: path)
polyline?.strokeWidth = 5.0
polyline?.strokeColor = UIColor.init(rgb: 0x7688ED)
polyline!.map = mapView
}

Overlay tiles on Google Maps (Swift 3)

I'm developing an application in Swift 3.0 and iOS10, with the Google Maps SDK.
The application calls WMS with "GMSTileURLConstructor" to make mosaic overlays on the Google Maps map. The overlay is correct at the position level, but the problem is that in the tiles "empty" where the mosaic is not and returns an empty tile, it is a png image, but when changing the opacity of the tile "tileLayer.opacity = 0.2" Makes transparent both the tiles of the supermosaic and the tiles of png and that is why the screen remains like this. Where they are useful with a white color. What solution do you give me? Because I have the bbox on the supermosaic server, is there any way to just make calls about that bbox?
The code in ViewController is:
import UIKit
class ViewController: UIViewController, CLLocationManagerDelegate
{
#IBOutlet weak var mapview: GMSMapView!
let locationManager = CLLocationManager()
let layer: WMSTileOverlay
var url = ""
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
mapview.isMyLocationEnabled = true
mapview.settings.myLocationButton = true
url = ""
self.getCardfromGeoserver()
self.mapview.mapType = .satellite
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
required init?(coder aDecoder: NSCoder) {
self.layer = WMSTileOverlay(urlArg: url)
super.init(coder: aDecoder)
}
// Warnung ueber Geoserver holen
func getCardfromGeoserver() {
mapview.clear()
//mapview.camera = GMSCameraPosition(target: CLLocationCoordinate2D(latitude: 37.529332577842005, longitude: -6.097075880344892), zoom: 17.5, bearing: 0, viewingAngle: 0)
mapview.camera = GMSCameraPosition(target: CLLocationCoordinate2D(latitude: 41.2514352951879, longitude: 0.6398317662822497), zoom: 17, bearing: 0, viewingAngle: 0)
let urls: GMSTileURLConstructor = { (x: UInt, y: UInt, zoom: UInt) -> URL in
let bbox = self.layer.bboxFromXYZ(x, y: y, z: zoom)
let urlKN = "http://localhost:8080/geoserver/cite/wms?SERVICE=WMS&VERSION=1.1.0&REQUEST=GetMap&FORMAT=image%2Fpng&TRANSPARENT=true&STYLES&LAYERS=cite%3A17_RGB&WIDTH=256&HEIGHT=256&SRS=EPSG%3A3857&BBOX=\(bbox.left),\(bbox.bottom),\(bbox.right),\(bbox.top)"
print(urlKN)
return URL(string: urlKN)!
}
let tileLayer = GMSURLTileLayer(urlConstructor: urls)
tileLayer.opacity = 0.2
tileLayer.zIndex = 1
tileLayer.map = nil
tileLayer.map = mapview
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == CLAuthorizationStatus.authorizedWhenInUse {
locationManager.startUpdatingLocation()
mapview.isMyLocationEnabled = true
mapview.settings.myLocationButton = true
}
}
}
The code for WMSTileOverlay is:
import UIKit
class ViewController: UIViewController, CLLocationManagerDelegate {
#IBOutlet weak var mapview: GMSMapView!
let locationManager = CLLocationManager()
let layer: WMSTileOverlay
var url = ""
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
mapview.isMyLocationEnabled = true
mapview.settings.myLocationButton = true
url = ""
self.getCardfromGeoserver()
self.mapview.mapType = .satellite
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
required init?(coder aDecoder: NSCoder) {
self.layer = WMSTileOverlay(urlArg: url)
super.init(coder: aDecoder)
}
// Warnung ueber Geoserver holen
func getCardfromGeoserver() {
mapview.clear()
//mapview.camera = GMSCameraPosition(target: CLLocationCoordinate2D(latitude: 37.529332577842005, longitude: -6.097075880344892), zoom: 17.5, bearing: 0, viewingAngle: 0)
mapview.camera = GMSCameraPosition(target: CLLocationCoordinate2D(latitude: 41.2514352951879, longitude: 0.6398317662822497), zoom: 17, bearing: 0, viewingAngle: 0)
let urls: GMSTileURLConstructor = { (x: UInt, y: UInt, zoom: UInt) -> URL in
let bbox = self.layer.bboxFromXYZ(x, y: y, z: zoom)
let urlKN = "http://localhost:8080/geoserver/cite/wms?SERVICE=WMS&VERSION=1.1.0&REQUEST=GetMap&FORMAT=image%2Fpng&TRANSPARENT=true&STYLES&LAYERS=cite%3A17_RGB&WIDTH=256&HEIGHT=256&SRS=EPSG%3A3857&BBOX=\(bbox.left),\(bbox.bottom),\(bbox.right),\(bbox.top)"
print(urlKN)
return URL(string: urlKN)!
}
let tileLayer = GMSURLTileLayer(urlConstructor: urls)
tileLayer.opacity = 0.2
tileLayer.zIndex = 1
tileLayer.map = nil
tileLayer.map = mapview
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == CLAuthorizationStatus.authorizedWhenInUse {
locationManager.startUpdatingLocation()
mapview.isMyLocationEnabled = true
mapview.settings.myLocationButton = true
}
}
}

how to map JSON object with MapKit using swift

I am working with a MapKit example using swift. The JSON object they are using is a object of arrays. I have a JSON file that is a array of objects. from the example I see they are pulling the properties they want by there location in the array. I need to use the property keys in my objects. How do I do this? Very first time with swift. thanks
here are example files
init(title: String, locationName: String, discipline: String, coordinate: CLLocationCoordinate2D) {
self.title = title
self.locationName = locationName
self.discipline = discipline
self.coordinate = coordinate
super.init()
}
class func fromJSON(json: [JSONValue]) -> Artwork? {
// 1
var title: String
if let titleOrNil = json[16].string {
title = titleOrNil
} else {
title = ""
}
let locationName = json[12].string
let discipline = json[15].string
// 2
let latitude = (json[18].string! as NSString).doubleValue
let longitude = (json[19].string! as NSString).doubleValue
let coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
// 3
return Artwork(title: title, locationName: locationName!, discipline: discipline!, coordinate: coordinate)
}
var subtitle: String {
return locationName
}
// MARK: - MapKit related methods
// pinColor for disciplines: Sculpture, Plaque, Mural, Monument, other
func pinColor() -> MKPinAnnotationColor {
switch discipline {
case "Sculpture", "Plaque":
return .Red
case "Mural", "Monument":
return .Purple
default:
return .Green
}
}
// annotation callout opens this mapItem in Maps app
func mapItem() -> MKMapItem {
let addressDict = [String(kABPersonAddressStreetKey): self.subtitle]
let placemark = MKPlacemark(coordinate: self.coordinate, addressDictionary: addressDict)
let mapItem = MKMapItem(placemark: placemark)
mapItem.name = self.title
return mapItem
}
Snippet of JSON file
[
198,
"9E5E1F28-22AF-459A-841E-5E89B022505E",
198,
1340413921,
"436621",
1340413921,
"436621",
"{\n}",
null,
null,
"1933",
"Bronze plaque mounted on a stone with an inscription marking the site of an artesian well. Located along Wilder Avenue near Artesian Way.",
"1922 Wilder Avenue",
"http://hiculturearts.pastperfect-online.com/34250images/004/193301-2.JPG",
"1933.01",
"Plaque",
"Site of Honolulu's Pioneer Artesian Well",
"Full",
"21.30006",
"-157.827969",
[
null,
"21.30006",
"-157.827969",
null,
false
],
null
],
snippet of JSON file i want to use
{
"id": "301B2",
"name": "Wrenhurst",
"lat": "35.815864",
"lng": "-78.918893",
"status": "Act 2Q12",
"minp": "632000",
"maxp": "678000",
"annStarts": "14",
"annClosings": "0",
"bldList": "Builder example",
"vdl": "0",
"futures": "0",
"lotSize": "95'",
It looks like your example is using SwiftyJSON
You can look at the docs to get more information on how to get data out of a JSONValue object, but specifically, if I understand your question, you need to know how to get values using keys rather than position.
OK, it depends on what values you are getting and whether or not those values can be null in your JSON file (aka nil in Swift)
I'm going to quote the example that you gave and then show you how to get that value from your JSON file
If you know for a fact that a value will never be null in your JSON file and will always be provided:
// The code from your example using array position
let locationName = json[12].string
// Would convert to:
let locationName = json["name"].string
// And in the case of lat / lng
let latitude = (json[18].string! as NSString).doubleValue
// This converts to:
let latitude = (json["lat"].string! as NSString).doubleValue
Simply use the key instead of the position to get at your value. However, this is not the only change you need. Instead of passing an array of JSONValue objects to your factory method, you need to specify a normal JSONValue (or just a JSON object)
So your factory method changes from this:
class func fromJSON(json: [JSONValue]) -> Artwork? {
// 1
var title: String
if let titleOrNil = json[16].string {
title = titleOrNil
} else {
title = ""
}
let locationName = json[12].string
let discipline = json[15].string
// 2
let latitude = (json[18].string! as NSString).doubleValue
let longitude = (json[19].string! as NSString).doubleValue
let coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
// 3
return Artwork(title: title, locationName: locationName!, discipline: discipline!, coordinate: coordinate)
}
To something like this:
// This class method should return an Artwork object or nil if one couldn't be created for any reason.
// In your example, I couldn't find any reason you would return nil, so... in this example, it simply returns and Artwork object
class func fromJSON(json: JSON) -> Artwork {
// 1
var title: String
if let titleOrNil = json["title"].string {
title = titleOrNil
} else {
title = ""
}
let locationName = json["name"].string
let discipline = json["discipline"].string
// 2
let latitude = (json["lat"].string! as NSString).doubleValue
let longitude = (json["lng"].string! as NSString).doubleValue
let coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
// 3
return Artwork(title: title, locationName: locationName!, discipline: discipline!, coordinate: coordinate)
}