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!