In another article, we wrote about how to save Backendless data objects with related geopoint(s). The data-to-geo relations are bidirectional. That means that just as a data object can reference a geopoint (or more than one) as a relation, a geopoint may reference a data object or a collection of in its metadata as well.
Consider the example below. The code saves a geopoint which represents the Eiffel Tower. The geopoint references a data object which represents the tower’s main engineer, Gustavo Eiffel, saved in the Architect table. Notice the “gustavoEiffel” object is a plain Java object (a POJO) which is saved in the Data Service storage in Backendless. The relation between the geopoint and the data object is handled through the geopoint’s metadata:
Architect gustaveEiffel = new Architect();
gustaveEiffel.setName("Gustave Eiffel");
Calendar calendar = Calendar.getInstance();
calendar.set(1923, 11, 27);
gustaveEiffel.setBirthday(calendar.getTime());
gustaveEiffel.setNationality("French");
Backendless.Data.of(Architect.class).save(gustaveEiffel, new AsyncCallback<Architect>() {
   @Override
   public void handleResponse(Architect savedArchitect) {
       GeoPoint eiffelTower = new GeoPoint(48.85815, 2.29452);
       eiffelTower.addCategory("towers");
       eiffelTower.addCategory("placesToVisit");
       eiffelTower.addMetadata("name", "Eiffel Tower");
       eiffelTower.addMetadata("architect", savedArchitect);
       Backendless.Geo.savePoint(eiffelTower, new AsyncCallback<GeoPoint>() {
           @Override
           public void handleResponse(GeoPoint geoPoint) {
               Log.i(TAG, "Geo point has been saved - " + geoPoint.getObjectId());
           }
           @Override
           public void handleFault(BackendlessFault fault) {
               Log.i(TAG, fault.getMessage());
           }
       });
   }
   @Override
   public void handleFault(BackendlessFault fault) {
       Log.e(TAG, fault.getMessage());
   }
});
val gustaveEiffel = Architect()
gustaveEiffel.name = "Gustave Eiffel"
val calendar = Calendar.getInstance()
calendar.set(1923, 11, 27)
gustaveEiffel.birthday = calendar.time
gustaveEiffel.nationality = "French"
Backendless.Data.of(Architect::class.java).save(gustaveEiffel, object : AsyncCallback<Architect> {
   override fun handleResponse(savedArchitect: Architect) {
       val eiffelTower = GeoPoint(48.85815, 2.29452)
       eiffelTower.addCategory("towers")
       eiffelTower.addCategory("placesToVisit")
       eiffelTower.addMetadata("name", "Eiffel Tower")
       eiffelTower.addMetadata("architect", savedArchitect)
       Backendless.Geo.savePoint(eiffelTower, object : AsyncCallback<GeoPoint> {
           override fun handleResponse(geoPoint: GeoPoint) {
               Log.i(TAG, "Geo point has been saved - ${geoPoint.objectId}")
           }
           override fun handleFault(fault: BackendlessFault) {
               Log.i(TAG, fault.message)
           }
       })
   }
   override fun handleFault(fault: BackendlessFault) {
       Log.e(TAG, fault.message)
   }
})
NSString *birthdateString = @"15/12/1832";
NSDateFormatter *dateFormatter = [NSDateFormatter new];
dateFormatter.dateFormat = @"dd/MM/yyyy";
    
Architect *gustaveEiffel = [Architect new];
gustaveEiffel.name = @"Gustave Eiffel";
gustaveEiffel.nationality = @"French";
gustaveEiffel.birthdate = [dateFormatter dateFromString:birthdateString];
    
[[Backendless.shared.data of:[Architect class]] saveWithEntity:gustaveEiffel responseHandler:^(Architect * savedArchitect) {
    NSDictionary *metadata = @{@"name": @"Eiffel Tower", @"architect": savedArchitect};
    GeoPoint *eiffelTower = [[GeoPoint alloc] initWithLatitude:48.85815 longitude:2.29452 categories:@[@"towers", @"placesToVisit"] metadata:metadata];
    [Backendless.shared.geo saveGeoPointWithGeoPoint:eiffelTower responseHandler:^(GeoPoint *savedPoint) {
        NSLog(@"Geo point has been saved - %@", savedPoint.objectId);
    } errorHandler:^(Fault *fault) {
        NSLog(@"Error: %@", fault.message);
    }];
        
} errorHandler:^(Fault *fault) {
    NSLog(@"Error: %@", fault.message);
}];
let birthdateString = "15/12/1832"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd/MM/yyyy"
        
let gustaveEiffel = Architect()
gustaveEiffel.name = "Gustave Eiffel"
gustaveEiffel.nationality = "French"
gustaveEiffel.birthdate = dateFormatter.date(from: birthdateString)
        
Backendless.shared.data.of(Architect.self).save(entity: gustaveEiffel, responseHandler: { savedArchitect in
    let metadata = ["name": "Eiffel Tower", "architect": savedArchitect]
    let eiffelTower = GeoPoint(latitude: 48.85815, longitude: 2.29452, categories: ["towers", "placesToVisit"], metadata: metadata)
    Backendless.shared.geo.saveGeoPoint(geoPoint: eiffelTower, responseHandler: { savedPoint in
        print("Geo point has been saved - \(savedPoint.objectId ?? "")")
    }, errorHandler: { fault in
        print("Error: \(fault.message ?? "")")
    })
}, errorHandler: { fault in
    print("Error: \(fault.message ?? "")")
})
const Backendless = require('backendless')
/*
 Or use `import Backendless from 'backendless'` for client side.
 If you don't use npm or yarn to install modules, you can add the following line
 <script src="//api.backendless.com/sdk/js/latest/backendless.min.js"></script>
 to your index.html file and use the global Backendless variable.
*/
Backendless.initApp('YOUR_APP_ID', 'YOUR_JS_API_KEY')
const saveArchitect = async () => {
  const architect = new Architect()
  architect.setName('Gustave Eiffel')
  architect.setBirthday(new Date('05/12/1980').getTime())
  architect.setNationality('French')
  const eiffelTower = new Backendless.GeoPoint()
  eiffelTower.latitude = 48.85815
  eiffelTower.longitude = 2.29452
  eiffelTower.categories = ['towers', 'placesToVisit']
  eiffelTower.metadata = {
    name     : 'Eiffel Tower',
    architect: architect
  }
  const savedPoint = await Backendless.Geo.savePoint(eiffelTower)
  console.log(`Geo point has been saved - ${ savedPoint.objectId }`)
}
const onError = error => {
  console.error('Server reported an error: ', error.message)
  console.error('error code: ', error.code)
  console.error('http status: ', error.status)
}
Promise.resolve()
  .then(saveArchitect)
  .catch(onError)
Map gustaveEiffel = {
 "name": "Gustave Eiffel",
 "birthday": DateTime(1923, 11, 27),
 "nationality": "French"
};
Backendless.Data.of("Architect").save(gustaveEiffel).then((savedArchitect) {
 GeoPoint eiffelTower = GeoPoint.fromLatLng(48.85815, 2.29452);
 eiffelTower.categories = ["towers", "placesToVisit"];
 eiffelTower.metadata = {"name": "Eiffel Tower", "architect": savedArchitect};
 Backendless.Geo.savePoint(eiffelTower).then(
   (geoPoint) => print("Geo point has been saved - ${geoPoint.objectId}"));
});
The Architect class:
public class Architect {
   private String nationality;
   private String name;
   private Date birthday;
   private String objectId;
   public void setName(String name) {
       this.name = name;
   }
   public String getName() {
       return name;
   }
   public void setBirthday(Date birthday) {
       this.birthday = birthday;
   }
   public Date getBirthday() {
       return birthday;
   }
   public void setNationality(String nationality) {
       this.nationality = nationality;
   }
   public String getNationality() {
       return nationality;
   }
   public String getObjectId() {
       return objectId;
   }
   public void setObjectId(String objectId) {
       this.objectId = objectId;
   }
}
import java.util.Date data class Architect(var objectId: String? = null, var birthday: Date? = null, var name: String? = null, var nationality: String? = null)
@interface Architect : NSObject @property (strong, nonatomic) NSString *name; @property (strong, nonatomic) NSString *nationality; @property (strong, nonatomic) NSDate *birthdate; @end
@objcMembers class Architect: NSObject {
    var name: String?
    var nationality: String?
    var birthdate: Date?
}
function Architect() {
  let nationality = ''
  let name = ''
  let birthday
  this.___class = 'Architect'
  this.setName = function (name) {
    this.name = name
    return this
  }
  this.getName = function () {
    return this.name
  }
  this.setBirthday = function (birthday) {
    this.birthday = birthday
    return this
  }
  this.getBirthdat = function () {
    return this.birthday
  }
  this.setNationality = function (nationality) {
    this.nationality = nationality
    return this
  }
  this.getNationality = function () {
    return this.nationality
  }
}
The Backendless SDK for Flutter uses the map/dictionary-based approach for data persistence.
Once you run the code and open the Geolocation screen in Backendless Console, you will see the following:
If you click the Architect object link in the geopoint’s metadata column, the Console displays the following popup which makes it very easy to see the related data object:
Alternatively, if you navigate to the Data screen and select the Architect table, it will show the saved data object:
Enjoy!