Set location on Google map on iOS - ios

I'm basically implementing a functionality where after a user moves the map a button appears, and when the button is selected a new search happens in visible area. The problem is the visible area is not accurate, so when I try to zoom into it again it zooms out.
every time I click the button the function below is called and the map zooms out. I understand that not calling animate and removing the code will avoid the zoom, but I want to know why the zoom is happening, because this may cause problems elsewhere
#IBAction func updateSearchSelected(sender: UIButton) {
let northEast = CLLocationCoordinate2D(latitude: mapView!.projection.visibleRegion().farRight.latitude, longitude: mapView!.projection.visibleRegion().farRight.longitude)
let southWest = CLLocationCoordinate2D(latitude: mapView!.projection.visibleRegion().nearLeft.latitude, longitude: mapView!.projection.visibleRegion().nearLeft.longitude)
print("northEast \(northEast)")
print("southWest \(southWest)")
let coordinateBounds = GMSCoordinateBounds(coordinate: northEast, coordinate: southWest)
let cameraUpdate = GMSCameraUpdate.fit(coordinateBounds)
mapView?.animate(with: cameraUpdate)
}
Here are the logs after clicking the button 3 times
northEast CLLocationCoordinate2D(latitude: 37.84227633765466, longitude: -122.38477706909181)
southWest CLLocationCoordinate2D(latitude: 37.674750313908689, longitude: -122.50391006469727)
northEast CLLocationCoordinate2D(latitude: 37.859492217765393, longitude: -122.37251903861761)
southWest CLLocationCoordinate2D(latitude: 37.657491381296779, longitude: -122.51616809517145)
northEast CLLocationCoordinate2D(latitude: 37.880245733530728, longitude: -122.35773839056492)
southWest CLLocationCoordinate2D(latitude: 37.636675535472314, longitude: -122.53094874322414)

You have to set the zoom level to what you want it to be when you call GMSCameraPosition.camera , for example:
let camera = GMSCameraPosition.camera(withLatitude: -33.8683,
longitude: 151.2086,
zoom: 16)
See: https://developers.google.com/maps/documentation/ios-sdk/views#using_gmscameraupdate
See the section titled Camera Position:
"The map view is modeled as a camera looking down on a flat plane. The position of the camera (and hence the rendering of the map) is specified by the following properties: latitude/longitude location, zoom, bearing and viewing angle."

Related

How to update the marker position on google map by latitude and longitude values

I am new to iOS Swift. I recently started working on GMSMaps. In my code I am able to get the latitude and longitude values of source and destination to find the direction and I am also able to add the marker to map. But, I want to change the position of marker based on changing the source and destination values. When I tried to change source/destination ,then again one more marker is adding to maps. I don't want to add one more marker, I just want to move the previous marker to updated location . If any one gives solution it would be so great.
This is my code which I have written to add marker for source and destination. I have written this code in didSelectRowAtIndexPath method of tableView
let centerr = CLLocationCoordinate2D(latitude: lat, longitude: lon)
var camera = GMSCameraPosition.camera(withLatitude: lat, longitude: lon, zoom: 10);
self.googlemapsview.camera = camera
var marker = GMSMarker(position: centerr)
print("Latitude :- \(lat)")
print("Longitude :-\(lon)")
marker.map = self.googlemapsview
Make the marker as global variable and update its coordinates via position property.
var marker: GMSMarker!
Update marker:
let newPosition = CLLocationCoordinate2D(latitude: newLat, longitude: newLon)
marker.position = newPosition
https://developers.google.com/maps/documentation/ios-sdk/reference/interface_g_m_s_marker#a2b9fdae0160d7acf439889ffcdb5f68b
in tableViewDidSelect
let centerr = CLLocationCoordinate2D(latitude: lat, longitude: lon)
var camera = GMSCameraPosition.camera(withLatitude: lat, longitude: lon, zoom: 10);
self.googlemapsview.camera = camera
marker = GMSMarker(position: centerr)
print("Latitude :- \(lat)")
print("Longitude :-\(lon)")
marker.map = self.googlemapsview

How to check and load coordinate into Google Map (iOS)?

I have a list of coordinates in database, but i would like to load only the coordinates that currently in the view of Google Map and add a marker onto it. How can i do that ?
Once you get latitude and longitude, you can use the following function to add map marker and zoom to marker location :
func showMapMarker() {
self.currentLocationMapView.clear()
DispatchQueue.main.async {
let position = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
let marker = GMSMarker(position: position)
let bounds: GMSCoordinateBounds = GMSCoordinateBounds(coordinate: position, coordinate: position)
marker.title = "Snippet Title"
marker.map = self.currentLocationMapView
marker.snippet = "Snippet Text"
let camera = self.currentLocationMapView.camera(for: bounds, insets: UIEdgeInsets())
self.currentLocationMapView.animate(with: GMSCameraUpdate.fit(bounds, withPadding: 15.0))
self.currentLocationMapView.camera = camera!
}
}

Restrict GMSMapView to certain bounds

I have two coordinates, and I need to restrict my Google Maps map to the frame bounded by those two coordinates. For example, I have
let bounds = GMSCoordinateBounds(
coordinate: CLLocationCoordinate2D(
latitude: 59.615440364671244,
longitude: -17.978949286043644
), coordinate: CLLocationCoordinate2D(
latitude: 33.963318167747758,
longitude: 21.442294009029865
)
)
Then, I write,
map.cameraTargetBounds = bounds
However, this does nothing to restrict the bounds of the map while it should. According to the documentation,
If not nil, [cameraTargetBounds] constrains the camera target so that gestures cannot cause it to leave the specified bounds.
This question did not help me, partially because I must allow zooming as well as panning––it just must be restricted to a certain area.
Why is this not working, and how can I fix it?
You have to use GoogleFletcher. In this we will implement two filters (GMSCoordinateBound and Type). In start bound cordinate we will put current location and at end bound we will just add 1 in our current location cordinates.
Hopefully it helps.
let location = locations.last!
let neBoundsCorner = CLLocationCoordinate2D(latitude:location.coordinate.latitude,
longitude: location.coordinate.longitude)
let swBoundsCorner = CLLocationCoordinate2D(latitude: location.coordinate.latitude + 1, longitude: location.coordinate.longitude + 1)
let bounds = GMSCoordinateBounds(coordinate: neBoundsCorner,coordinate: swBoundsCorner)
let filter = GMSAutocompleteFilter()
filter.type = .establishment
fetcher = GMSAutocompleteFetcher(bounds: bounds, filter: filter)

Finding linear regression bearing from set of coordinates isn't giving desired result

I have a series of coordinates that I'm wanting to find the linear regression for, so I can then find the bearing of the line. I'm using the Linear Regression algorithm from Swift Algorithm Club on this set of coordinates:
51.48163827836369, -0.019464668521006683
51.481654860705667, -0.019452641350085287
51.481674140657908, -0.01943882290242982
51.481690344713748, -0.019423713183982727
51.481705506128442, -0.019419045258473489
51.481722553489625, -0.01940979681751287
51.48173752576799, -0.019412687104136239
51.48174912673462, -0.019409150632213823
51.4817646359283, -0.019389300889997685
51.481779676567427, -0.019388957697628939
51.481792568262044, -0.019402453532393338
51.481804168699682, -0.019415663863242116
51.481822746271966, -0.019423725406568337
51.481838162880258, -0.019428618620622728
51.481855587496689, -0.01942372804705883
51.481867836051975, -0.019430554178484272
51.481883136496599, -0.019432972610502475
51.481899505688553, -0.019425501321734373
51.481914919015246, -0.019424832166464512
51.481932613348015, -0.019457392982985766
51.481949346615657, -0.019472056279255412
51.481968002664601, -0.019458232757642691
51.481986902059589, -0.019446792660346546
51.482003086257393, -0.019433403642779012
Adding this to a map using darrinward.com, I get this path, moving roughly north:
So from there, to get the bearing I take the regression of the first coordinate's longitude (to get the latitude), :
let regression = linearRegression(longitudes, latitudes)
let firstRegressionCoordinate = CLLocationCoordinate2D(latitude: regression(firstCoordinate.longitude), longitude:firstCoordinate.longitude)
let lastRegressionCoordinate = CLLocationCoordinate2D(latitude: regression(lastCoordinate.longitude), longitude: lastCoordinate.longitude)
return firstRegressionCoordinate.bearing(to: lastRegressionCoordinate)
My bearing function works properly, and gives me a bearing of 156.20969º. What I'm expecting is a number closer to 0º, or closer to 360º, given the actual path taken.
I feel the issue isn't with the linear regression algorithm, or with the bearing function, but with my coordinates I'm asking for in order to determine the bearing. Am I asking it for the wrong thing? Is there something else I should be doing differently?
The complete code, that can be run in a playground:
import CoreLocation
public extension FloatingPoint {
public var degreesToRadians: Self { return self * .pi / 180 }
public var radiansToDegrees: Self { return self * 180 / .pi }
}
extension CLLocationCoordinate2D {
///Returns the initial bearing of travel to another coordinate
func bearing(to: CLLocationCoordinate2D) -> CLLocationDegrees {
let fromLatRadians = latitude.degreesToRadians
let fromLongRadians = longitude.degreesToRadians
let toLatRadians = to.latitude.degreesToRadians
let toLongRadians = to.longitude.degreesToRadians
let y = sin(toLongRadians - fromLongRadians) * cos(toLatRadians)
let x = cos(fromLatRadians) * sin(toLatRadians) - sin(fromLatRadians) * cos(toLatRadians) * cos(toLongRadians - fromLongRadians)
var bearing = atan2(y, x).radiansToDegrees
bearing = (bearing + 360.0).truncatingRemainder(dividingBy: 360.0)
return bearing
}
}
extension Array where Element == CLLocationCoordinate2D {
func linearRegressionBearing() -> Double {
var longitudes = [CLLocationDegrees]()
var latitudes = [CLLocationDegrees]()
for coordinate in self {
longitudes.append(coordinate.longitude)
latitudes.append(coordinate.latitude)
}
let regression = linearRegression(longitudes, latitudes)
let firstCoordinate = CLLocationCoordinate2D(latitude: regression(self.first!.longitude), longitude: self.first!.longitude)
let lastCoordinate = CLLocationCoordinate2D(latitude: regression(self.last!.longitude), longitude: self.last!.longitude)
return firstCoordinate.bearing(to: lastCoordinate)
}
}
// A closed form solution
func average(_ input: [Double]) -> Double {
return input.reduce(0, +) / Double(input.count)
}
func multiply(_ a: [Double], _ b: [Double]) -> [Double] {
return zip(a, b).map(*)
}
func linearRegression(_ xs: [Double], _ ys: [Double]) -> (Double) -> Double {
let sum1 = average(multiply(xs, ys)) - average(xs) * average(ys)
let sum2 = average(multiply(xs, xs)) - pow(average(xs), 2)
let slope = sum1 / sum2
let intercept = average(ys) - slope * average(xs)
return { x in intercept + slope * x }
}
let coordinates = [
CLLocationCoordinate2D(latitude: 51.48163827836369, longitude: -0.019464668521006683),
CLLocationCoordinate2D(latitude: 51.481654860705667, longitude: -0.019452641350085287),
CLLocationCoordinate2D(latitude: 51.481674140657908, longitude: -0.01943882290242982),
CLLocationCoordinate2D(latitude: 51.481690344713748, longitude: -0.019423713183982727),
CLLocationCoordinate2D(latitude: 51.481705506128442, longitude: -0.019419045258473489),
CLLocationCoordinate2D(latitude: 51.481722553489625, longitude: -0.01940979681751287),
CLLocationCoordinate2D(latitude: 51.48173752576799, longitude: -0.019412687104136239),
CLLocationCoordinate2D(latitude: 51.48174912673462, longitude: -0.019409150632213823),
CLLocationCoordinate2D(latitude: 51.4817646359283, longitude: -0.019389300889997685),
CLLocationCoordinate2D(latitude: 51.481779676567427, longitude: -0.019388957697628939),
CLLocationCoordinate2D(latitude: 51.481792568262044, longitude: -0.019402453532393338),
CLLocationCoordinate2D(latitude: 51.481804168699682, longitude: -0.019415663863242116),
CLLocationCoordinate2D(latitude: 51.481822746271966, longitude: -0.019423725406568337),
CLLocationCoordinate2D(latitude: 51.481838162880258, longitude: -0.019428618620622728),
CLLocationCoordinate2D(latitude: 51.481855587496689, longitude: -0.01942372804705883),
CLLocationCoordinate2D(latitude: 51.481867836051975, longitude: -0.019430554178484272),
CLLocationCoordinate2D(latitude: 51.481883136496599, longitude: -0.019432972610502475),
CLLocationCoordinate2D(latitude: 51.481899505688553, longitude: -0.019425501321734373),
CLLocationCoordinate2D(latitude: 51.481914919015246, longitude: -0.019424832166464512),
CLLocationCoordinate2D(latitude: 51.481932613348015, longitude: -0.019457392982985766),
CLLocationCoordinate2D(latitude: 51.481949346615657, longitude: -0.019472056279255412),
CLLocationCoordinate2D(latitude: 51.481968002664601, longitude: -0.019458232757642691),
CLLocationCoordinate2D(latitude: 51.481986902059589, longitude: -0.019446792660346546),
CLLocationCoordinate2D(latitude: 51.482003086257393, longitude: -0.019433403642779012)
]
coordinates.linearRegressionBearing()
I confirm that your bearing function works correctly. However, the regression value you get for latitude is 51.45437262420372 - which is where the regression line intersects with longitude = 0.0. I think you need to use both the intersection point and slope of the regression line to calculate fitted lat,lon values (within your map region), and then find the bearing using those values.
LATER ADDITION:
An important issue here is that longitude coordinates are not linear in relation to distance - the distance covered by a degree of longitude varies with latitude. I think the simplest solution is just to convert your array of lat,lon values to lat,normalisedLon values where normalisedLon = lon * cosine(lat).
When you do this the angle of your path from your first coordinate to your last coordinate = 3.055 degrees (just east of north which appears to be correct).
Once you've done this then the slope of your regression line will in fact represent the direction you are seeking i.e. heading = atan(slope).
However, your regression code just doesn't seem to work for me. It should give a result very close to 3 degrees also, but it is way off.

Fit entire Google Map in zoom level in Swift project

I have a google map with a bunch of coordinates
path.addCoordinate(CLLocationCoordinate2DMake(-37.813047, 144.959911))
path.addCoordinate(CLLocationCoordinate2DMake(-37.814895, 144.960759))
path.addCoordinate(CLLocationCoordinate2DMake(-37.814361, 144.963140))
path.addCoordinate(CLLocationCoordinate2DMake(-37.812386, 144.962239))
I would like the map to be automatically zoomed to the best level based on the points however I can't find anything relating to this.
I have this working:
var vancouver = CLLocationCoordinate2DMake(-37.813047, 144.959911)
var calgary = CLLocationCoordinate2DMake(-37.814361, 144.963140)
var bounds = GMSCoordinateBounds(coordinate: vancouver, coordinate: calgary)
var camera = viewMap.cameraForBounds(bounds, insets:UIEdgeInsetsZero)
viewMap.camera = camera
however it only accepts 2 coordinates where I may have up to 100
Thanks
You can use GMSCoordinateBounds(path:) to fit all coordinates. But it will display a world size scale if you update the camera right after your another update. So you can use dispatch_after to solve the problem.
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.whiteColor();
let camera = GMSCameraPosition.cameraWithLatitude(-37.813047, longitude: -72.8561644, zoom:5)
mapView = GMSMapView.mapWithFrame(CGRectZero, camera:camera)
let marker = GMSMarker()
marker.position = camera.target
marker.snippet = "Hello World"
marker.appearAnimation = kGMSMarkerAnimationPop
marker.map = mapView
self.view = mapView
delay(seconds: 2) { () -> () in
let path = GMSMutablePath()
path.addCoordinate(CLLocationCoordinate2DMake(37.36, -122.0))
path.addCoordinate(CLLocationCoordinate2DMake(37.45, -122.0))
path.addCoordinate(CLLocationCoordinate2DMake(37.45, -122.2))
path.addCoordinate(CLLocationCoordinate2DMake(37.36, -122.2))
path.addCoordinate(CLLocationCoordinate2DMake(37.36, -122.0))
let rectangle = GMSPolyline(path: path)
rectangle.map = self.mapView
let bounds = GMSCoordinateBounds(path: path)
self.mapView!.animateWithCameraUpdate(GMSCameraUpdate.fitBounds(bounds, withPadding: 15.0))
}
}
The delay method uses the dispatch_after:
func delay(#seconds: Double, completion:()->()) {
let popTime = dispatch_time(DISPATCH_TIME_NOW, Int64( Double(NSEC_PER_SEC) * seconds ))
dispatch_after(popTime, dispatch_get_main_queue()) {
completion()
}
}
Iterate through your points. The southwest corner is the minimum latitude and longitude. The northeast corner is the maximum latitude and longitude. Once you have those two points, pass them into your cameraForBounds method.
Try finding an optimal viewing box on google maps by:
-Clicking on the north east corner of the area you think will cover the coordinates in a browser. Note the lat & long.
-Then click the south east corner in the same area which you think will enclose all the coordinates & note the lat & long.
Put the latitudes & longitudes in the respective variables of the below code:
var southWest = CLLocationCoordinate2DMake(latitide,longititude)
var northEast = CLLocationCoordinate2DMake(latitude,longitude)
var bounds = GMSCoordinateBounds(coordinate: northEast, coordinate: southWest)
var camera = mapView.cameraForBounds(bounds, insets:UIEdgeInsetsZero)
mapView.camera = camera;

Resources