Storing and deleting placemarks - google-apps

I am trying to store placemarks in a global array but not sure what I'm actually trying to store. I want to be able to remove the placemark at a later time. Do I just need to store the lat and lng or do I need to store some kind of placemark obj in the array. I've tried to look at the google docs but they are I think written in greek. Could someone point me in the right direction??
Inside a for loop on the global array PASSENGERS we have the following code:
// prepare placemark
var placemark = earth.createPlacemark("");
placemark.setName(PASSENGERS[i].name + " to " + PASSENGERS[i].house);
// prepare icon
var icon = earth.createIcon("");
icon.setHref(url + "/img/" + PASSENGERS[i].username + ".jpg");
// prepare style
var style = earth.createStyle("");
style.getIconStyle().setIcon(icon);
style.getIconStyle().setScale(4.0);
// prepare stylemap
var styleMap = earth.createStyleMap("");
styleMap.setNormalStyle(style);
styleMap.setHighlightStyle(style);
// associate stylemap with placemark
placemark.setStyleSelector(styleMap);
// prepare point
var point = earth.createPoint("");
point.setAltitudeMode(earth.ALTITUDE_RELATIVE_TO_GROUND);
point.setLatitude(building.lat);
point.setLongitude(building.lng);
point.setAltitude(0.0);
// associate placemark with point
placemark.setGeometry(point);
I thought I could print out placemark with:
for (var prop in placemark)
{
console.log(prop + " = " + placemark[prop]);
}
but that doesn't seem to work.

I think you need to take some time and try to understand the docs.
See https://developers.google.com/earth/documentation/placemarks?csw=1
Once you add placemarks to the the plugin they are in memory inside the plugin and there is no need to store them in another array.
You can assign the placemark an id.
// to add
var placemark= ge.createPlacemark('your_id_here');
ge.getFeatures().appendChild(placemark);
// to remove
var myPlacemark= ge.getElementById('your_id_here');
ge.getFeatures().removeChild(myPlacemark);

Related

Allocating the results of Reverse Geocoding to a global variable

I am using Swift 4 and Xcode 9.3.1. I'm including screenshots of the code.
I am new to mobile development/ programming in general and have been thinking about how to phrase this. So this is the best I can explain it:
I am building an app that gets the user's location in order to send assistance through to them. The app gets the user's coordinates, and displays a TextView with their address information. So pretty straight forward mapKit/coreLocation functionality. So far, so good: Getting the coordinates with startUpdatingLocation() works fine, and I've used Reverse Geocoder to get the street name & locality. But they-- meaning the decoded street and locality strings-- only print out if I call them within the closure, not outside it. I've understood (correctly or incorrectly?) that variables that need to be available for multiple functions within a class should to be declared globally at the top. However I can't figure out how to extract the information from this closure in order to use it elsewhere.
I've been googling and reading through questions in stackOverflow and I feel like something really simple is missing but can't figure out what. Things I've tried unsuccessfully so far:
1_ Defining global variables as empty strings at the beginning of the class
and using the variable names inside the closure where the geocoding reverse method happens, in an attempt to store the resulting strings, but when I try to print the variables outside the closure, the global variable is still and empty string ("").
[global variables declared at the top][1]
2_Defining an empty, global array of strings and appending the information from inside the closure to the global array. Still got an empty array outside the closure. (so same as 1)
3_Create a function --func decodedString()-- to return the data as a String, so I can use it by declaring
*let streetLocation : String = decodedString()*
However when I declare that function like this :
var street = ""
var locality = ""
// Get the street address from the coordinates
func deocodedString() -> String {
let geocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(location) { placemarks, error in
if let placemark = placemarks?.first {
self.street = placemark.name!
self.locality = placemark.locality!
let string = "\(self.street), \(self.locality)"
return string
}
}
}
I get an error of: Unexpected non-void return value in void function
unexpected no void return value in void function
Lastly, if I pass the information straight into a TextView within the closure by using the code below, my textView updates successfully-- but I can't format the strings, which I need to do in order to make them look like the design instructions I'm following (aka some bold text, some regular text, and some different sizes of text):
CLGeocoder().reverseGeocodeLocation(location) { placemarks, error in
if let placemark = placemarks?.first {
self.street = placemark.name!
self.locality = placemark.locality!
let string = "\(self.street), \(self.locality)"
self.addressTextView.text = string
}
}
So that's why I can't just pass it through with the textView.text = string approach.
I'd appreciate some help...I have been looking though StackOverFlow, youtube and other tutorial places but I can't figure out what I'm missing, or why my function declaration generates an error. I have already destroyed and reversed my code several times over last 24 hs without getting an independent string that I can apply formatting to before passing it into the textView and I'm at a loss as to how else to approach it.
When you call this function the reverseGeocodeLocation runs in the background thread. So if you want to return the address in this method you should use escaping closure.
func getaddress(_ position:CLLocationCoordinate2D,completion:#escaping (String)->()) {
let geocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(location) { placemarks, error in
if let placemark = placemarks?.first {
let street = placemark.name!
let locality = placemark.locality!
let string = "\(street), \(locality)"
completion(string)
}
}
}
self.getaddress(position.target) { address in
print(address)
self.addressTextView.text = address
}
I had a problem with google geocoder to update the label on the map screen.
So I did this, first, create
swift file name: GoogleAPI just call it as you like.
class GoogleAPI {
static let sharedInstance = GoogleAPI()
private init() { }
var addressValue = ""
public func geocoding(lat: Double, long: Double) {
Alamofire.request("https://maps.googleapis.com/maps/api/geocode/json?latlng=\(lat),\(long)&key=YOUR_GOOGLE_KEY").responseJSON { (response) in
if response.result.isSuccess {
let dataJSON : JSON = JSON(response.result.value!)
self.geocoding(json: dataJSON)
} else {
print("Error \(response.result.error!)")
}
}
}
fileprivate func geocoding(json: JSON) {
let json = json["results"]
let address = json[1]["formatted_address"].stringValue
addressValue = address
print("pin address \(addressValue)")
}
}
This is an API call to Google to fetch all from a response and parse the only street.
After that go to your View Controller with a map where is the pin, map etc..
Set up a pin, marker to be draggable. [marker1.isDraggable = true]
Then add this function
mapView(_ mapView: GMSMapView, didEndDragging marker: GMSMarker)
and add call from above like this :
func mapView(_ mapView: GMSMapView, didEndDragging marker: GMSMarker) {
GoogleAPI.sharedInstance.geocoding(lat: marker.position.latitude, long: marker.position.longitude)
DispatchQueue.main.async {
self.txtSearch.text = GoogleAPI.sharedInstance.addressValue
}
}
txtSearch is my search text field.
yea I know, that can be done better, but no time. this is working.
Swift 4.2

Swift mapView load more pins

I have a method that showing me multiple Annotations on the map. Its About 10 000 annotation so i have to show only annotation that are visible on the map in current time. When I move with map i execute method that return me objects with coordinates that will be visible. But my method will delete all of them and start to adding them to map one by one. My goal is when I present Annotations and i move with map i want to keep the ones that are visible and the others remove. I am using this and FBAnnotationClusteringSwift framework.
this is my method for adding pins.
private var arrOfPinsOnMap:[FBAnnotation] = []
private var clusteringManager = FBClusteringManager()
func addMapPoints(){
self.arrOfPinsOnMap.removeAll()
self.clusteringManager = FBClusteringManager() //everytime recreate instance otherwise there will be duplicates.
for i in 0..<sortedObjectsByDistance_ids.count {
var id_pobor = String()
let pin = FBAnnotation()
guard let tempUniqueE21 = get_sortedObjectsByDistance(i) else {
continue
}
let temp = tempUniqueE21
pin.coordinate = CLLocationCoordinate2D(latitude: tempUniqueE21.lat, longitude: tempUniqueE21.lng)
pin.title = temp.provoz
pin.subtitle = temp.ulice
self.clusteringManager.addAnnotations([pin])
self.arrOfPinsOnMap.append(pin)
}
}
I am calling this method overtime when user move with map. The problem is not with FB framework but with stored Annotation values.

Swift: Removing only one MKPolyline of several

I have two polylines on the map:
var polylineRoute : MKGeodesicPolyline!
var polylineFlight : MKGeodesicPolyline!
I assign each of them a title and add them to the map like this (in different methods):
let polyline = MKGeodesicPolyline(coordinates: &routeCoordinates, count: routeCoordinates.count)
polyline.title = "route"
self.mapView.addOverlay(polyline)
self.polylineRoute = polyline
and
let polyline = MKGeodesicPolyline(coordinates: &routeCoordinates, count: routeCoordinates.count)
polyline.title = "flight"
self.mapView.addOverlay(polyline)
self.polylineFlight = polyline
Now, when a specific action is triggered, I would like to remove only the flight overlay and leave the route overlay intact.
This does not work at all:
func removeFlightPath()
{
self.mapView.removeOverlay(self.polylineFlight)
self.polylineFlight = nil
}
The following works but removes both polylines:
func removeFlightPath()
{
var overlays = mapView.overlays
mapView.removeOverlays(overlays)
}
Is there a working way to remove only one polyline? I searched the forum and there is only one response that is saying that it is possible using the title. However, it does not specify how it can be done.
Thanks a lot!
EDIT:
This solves the issue:
func removeFlightPath()
{
if self.polylineFlight != nil
{
// Overlays that must be removed from the map
var overlaysToRemove = [MKOverlay]()
// All overlays on the map
let overlays = self.mapView.overlays
for overlay in overlays
{
if overlay.title! == "flight"
{
overlaysToRemove.append(overlay)
}
}
self.mapView.removeOverlays(overlaysToRemove)
}
}
I think your source code is correct. Could be that the reference counting is messing it up. As long as the object is referred to, MKGeodesicPolyline will not be removed. In your code, you have used a local variable to create the polyline object. I have tried it without using a local variable and it is removing the polyline.
self.polylineFlight = MKGeodesicPolyline(coordinates: &routeCoordinates, count: routeCoordinates.count)
self.polylineFlight.title = "flight"
polylineFlight doesn't look right. It's built from routeCoordinates, the same as polylineRoute. So removing it would produce no change in the map.
Are you building from the right coordinates?
Can we see before/after screenshots? Or can we see a clarification of "does not work at all"?

How do i return coordinates after forward geocoding?

I am trying to see whether the user is within a certain distance of an address. I have successfully managed to get the users location, and convert the address with forward geocoding. I am left with two sets of coordinates. I am trying to make an if statement saying if they are within "a distance", print something!
Currently when i print the coordinates inside the placemark function i get the desired coordinates. When i call them to create eventLatitude and eventLongitude they become 0.0. I know this is a ascycronous problem, but i am unsure on who to resolve this. Can someone give me an example.
My code is below
before the viewdidload i have these variables
var placemarkLongitude = CLLocationDegrees()
var placemarkLatitude = CLLocationDegrees()
then inside the function i set these variables to the placemark coordinates
if let objects = objects {
for object in objects {
self.geocoder = CLGeocoder()
//get address from object
let COAddress = object.objectForKey("Address")as! String
let COCity = object.objectForKey("City")as! String
let COState = object.objectForKey("State")as! String
let COZipCode = object.objectForKey("ZipCode")as! String
let combinedAddress = "\(COAddress) \(COCity) \(COState) \(COZipCode)" //all parts of address
print(combinedAddress)
//make address a location
self.geocoder.geocodeAddressString(combinedAddress, completionHandler: {(placemarks, error) -> Void in
if(error != nil)
{
print("Error", error)
}
else if let placemark = placemarks?[0]
{
let placemark = placemarks![0]
self.placemarkLatitude = (placemark.location?.coordinate.latitude)! //THIS RETURNS A VALUE
self.placemarkLongitude = (placemark.location?.coordinate.longitude)! //THIS RETURNS A VALUE
print("Longitude: ", self.placemarkLongitude, " Latitude: ", self.placemarkLatitude)
}
})
// user location
let userLatitude = self.locationManager.location?.coordinate.latitude //THIS RETURNS A VALUE
let userLongitude = self.locationManager.location?.coordinate.longitude //THIS RETURNS A VALUE
print("User Location is ", userLatitude, ", " ,userLongitude)
let userLocation = CLLocation(latitude: userLatitude!, longitude: userLongitude!)
// event location
let eventLatitude = self.placemarkLatitude // THIS RETURNS 0.0
let eventLongitude = self.placemarkLatitude // THIS RETURNS 0.0
print("Event Location is ", eventLatitude, ", " ,eventLongitude)
let eventLocation = CLLocation(latitude: eventLatitude, longitude: eventLongitude)
//Measuring my distance to my buddy's (in km)
let distance = userLocation.distanceFromLocation(eventLocation) / 1000
//Display the result in km
print("The distance to event is ", distance)
if (distance < 100) {
print("yay")
}
}
}
You are correct about the asynchronous issue. Basically, you cannot do anything after this code:
// [A1]
self.geocoder.geocodeAddressString(combinedAddress, completionHandler: {
(placemarks, error) -> Void in
// [B] ... put everything _here_
})
// [A2] ... nothing _here_
The reason is that the stuff inside the curly braces (B) happens later than the stuff outside it (including the stuff afterward, A2). In other words, the code in my schematic above runs in the order A1, A2, B. But you are dependent on what happens inside the curly braces, so you need that dependent code to be inside the curly braces so that it executes in sequence with the results of the geocoding.
Of course this also means that the surrounding function cannot return a result, because it returns before the stuff in curly braces has even happened. The code in my schematic goes A1, A2, return! Only later does B happen. So clearly you cannot return anything that happens in B because it hasn't happened yet.
Just pass the coordinate values obtained from the completionHandler to any other method and do what you like to do.
{
self.placemarkLatitude = (placemark.location?.coordinate.latitude)! //THIS RETURNS A VALUE
self.placemarkLongitude = (placemark.location?.coordinate.longitude)! //THIS RETURNS A VALUE
// After this code pass the values like,
passingTheCoordinates(placemarkLatitude, placemarkLongitude)
}
func passingTheCoordinates(latitude:CLLocationDegrees, _ longitude:CLLocationDegrees){
}
Did not have enough reputation to reply your question but I also have this same problem today. I don't know much about your app design but for my case (which is stuck at the same place like you, same func, same problem, can't save to variable). My solution (maybe kinda temporally, does not good) is to save (placemark.location?.coordinate.latitude)! and (placemark.location?.coordinate.longitude)! to CoreData as Double.
This is how I implemented it. As I said before, since I don't know your app much so depend on your need, you might want something else.
LocationManager.sharedInstance.getReverseGeoCodedLocation(address: searchBar.text!, completionHandler: { (location:CLLocation?, placemark:CLPlacemark?, error:NSError?) in
if error != nil {
print((error?.localizedDescription)!)
return
}
if placemark == nil {
print("Location can't be fetched")
return
}
//Saving geo code to Core Data
newEntry.lat = (placemark?.location?.coordinate.latitude)!
newEntry.long = (placemark?.location?.coordinate.longitude)!
})
Credit to this repo for the LocationManager.swift file

Create an array of MKPointAnnotation objects

Currently working with Map Views and adding pins to the map. I know how to add a single point to the map using addAnotation() method. Now, I am trying to add multiple points to the MapView in the easiest way. I've fetched the data (latitude, longitude and name from an online XML file) and stored it in an array and now I want to add all those coordinates+name as pins in the map. For doing so I've declared an array of MKPointAnnotation objects like so:
var pinsArray: [MKPointAnnotation] = []
And then for dumping the collected data to I've done the following:
for i in 0...(myFeed.count-1) {
pinsArray[i].title = myFeed.objectAtIndex(i).objectForKey("NOMBRE")!.stringValue
pinsArray[i].coordinate = CLLocationCoordinate2D(latitude: myFeed[i].objectForKey("LATITUD")!.doubleValue, longitude: myFeed[i].objectForKey("LONGITUD")!.doubleValue)
pinsArray[i].subtitle = ""
mapView.addAnnotation(pinsArray[i])
}
But when I run the app I get an error saying that the array index is out of range (fatal error: Array index out of range). I guess this is a problem on the declaration of the pinsArray, I do not really know how to solve this one.
Try this:
var pinsArray: [MKPointAnnotation] = []
for i in 0...(myFeed.count-1)
{
let pointAnnotation = MKPointAnnotation() // First create an annotation.
pointAnnotation.title = myFeed.objectAtIndex(i).objectForKey("NOMBRE")!.stringValue
pointAnnotation.coordinate = CLLocationCoordinate2D(latitude: myFeed[i].objectForKey("LATITUD")!.doubleValue, longitude: myFeed[i].objectForKey("LONGITUD")!.doubleValue)
pointAnnotation.subtitle = ""
pinsArray.append(pointAnnotation) // Now append this newly created annotation to array.
}
mapView.addAnnotations(pinsArray) // Add all the annotations to map view at once.