Blog

How to save a data object with a related geolocation

by on February 12, 2015

Backendless Geolocation allows you to map a location to a data object. This feature can lead to a lot of interesting opportunities. For example, consider a taxi booking service like Uber. You may have multiple cars/drivers available for hire as well as customers putting in pickup requests. Both drivers and customers may be represented by corresponding data objects and locations on the map.

Consider the following example:

       Driver driver = new Driver();
       driver.setDriverName("Jen Buttons");
       driver.setRating(4);
       driver.setCarMake("Lamborghini");
       driver.setCarModel("Diablo");
    
       Backendless.Data.of(Driver.class).save(driver, new AsyncCallback<Driver>() {
           @Override
           public void handleResponse(final Driver savedDriver) {
               Log.i(TAG, "Driver has been saved");
    
               GeoPoint location = new GeoPoint(32.803468, -96.769879);
               location.addMetadata("city", "Dallas");
               location.addCategory("drivers");
    
               Backendless.Geo.savePoint(location, new AsyncCallback<GeoPoint>() {
                   @Override
                   public void handleResponse(GeoPoint savedLocation) {
                       Log.i(TAG, "Location has been saved with ID " + savedLocation.getObjectId());
    
                       Backendless.Data.of(Driver.class).setRelation(savedDriver, "location:GeoPoint:1",
                           Collections.singletonList(savedLocation), new AsyncCallback<Integer>() {
                               @Override
                               public void handleResponse(Integer response) {
                                   Log.i(TAG, "Relation has been set");
                               }
    
                               @Override
                               public void handleFault(BackendlessFault fault) {
                                   Log.e(TAG, fault.getMessage());
                               }
                           });
                   }
    
                   @Override
                   public void handleFault(BackendlessFault fault) {
                       Log.e(TAG, fault.getMessage());
                   }
               });
           }
    
           @Override
           public void handleFault(BackendlessFault fault) {
               Log.e(TAG, fault.getMessage());
           }
       });

       val driver = Driver()
       driver.driverName = "Jen Buttons"
       driver.rating = 4
       driver.carMake = "Lamborghini"
       driver.carModel = "Diablo"
    
       Backendless.Data.of(Driver::class.java).save(driver, object : AsyncCallback<Driver> {
           override fun handleResponse(savedDriver: Driver) {
               Log.i(TAG, "Driver has been saved")
    
               val location = GeoPoint(32.803468, -96.769879)
               location.addMetadata("city", "Dallas")
               location.addCategory("drivers")
    
               Backendless.Geo.savePoint(location, object : AsyncCallback<GeoPoint> {
                   override fun handleResponse(savedLocation: GeoPoint) {
                       Log.i(TAG, "Location has been saved with ID ${savedLocation.objectId}")
    
                       Backendless.Data.of(Driver::class.java).setRelation(savedDriver, "location:GeoPoint:1",
                           listOf(savedLocation), object : AsyncCallback<Int> {
                               override fun handleResponse(response: Int?) {
                                   Log.i(TAG, "Relation has been set")
                               }
    
                               override fun handleFault(fault: BackendlessFault) {
                                   Log.e(TAG, fault.message)
                               }
                           })
                   }
    
                   override fun handleFault(fault: BackendlessFault) {
                       Log.e(TAG, fault.message)
                   }
               })
           }
    
           override fun handleFault(fault: BackendlessFault) {
               Log.e(TAG, fault.message)
           }
       })


    Driver class:

    @interface Driver : NSObject
    
    @property (strong, nonatomic) NSString *driverName;
    @property (strong, nonatomic) NSString *carMake;
    @property (strong, nonatomic) NSString *carModel;
    @property (nonatomic) NSInteger rating;
    
    @end

    API example:

    Driver *driver = [Driver new];
    driver.driverName = @"Jen Buttons";
    driver.carMake = @"Lamborghini";
    driver.carModel = @"Diablo";
    driver.rating = 5;
        
    DataStoreFactory *dataStore = [Backendless.shared.data of:[Driver class]];    
    [dataStore saveWithEntity:driver responseHandler:^(Driver *savedDriver) {
        GeoPoint *location = [[GeoPoint alloc] initWithLatitude:32.803468 longitude:-96.769879 categories:@[@"drivers"] metadata:@{@"city": @"Dallas"}];
        [Backendless.shared.geo saveGeoPointWithGeoPoint:location responseHandler:^(GeoPoint *savedLocation) {
            [dataStore setRelationWithColumnName:@"location:GeoPoint:1" parentObjectId:savedDriver.objectId childrenObjectIds:@[savedLocation.objectId] responseHandler:^(NSInteger relationSet) {
                NSLog(@"Driver has been saved. Location object ID %@", savedLocation.objectId);
            } errorHandler:^(Fault *fault) {
                NSLog(@"Error: %@", fault.message);
            }];
        } errorHandler:^(Fault *fault) {
            NSLog(@"Error: %@", fault.message);
        }];
    } errorHandler:^(Fault *fault) {
        NSLog(@"Error: %@", fault.message);
    }];


    Driver class:

    @objcMembers class Driver: NSObject {
        var driverName: String?
        var carMake: String?
        var carModel: String?
        var rating: Int = 0
    }

    API example:

    let driver = Driver()
    driver.driverName = "Jen Buttons"
    driver.carMake = "Lamborghini"
    driver.carModel = "Diablo"
    driver.rating = 5
            
    let dataStore = Backendless.shared.data.of(Driver.self)
    dataStore.save(entity: driver, responseHandler: { savedDriver in
        let location = GeoPoint(latitude: 32.803468, longitude: -96.769879, categories: ["drivers"], metadata: ["city": "Dallas"])
        Backendless.shared.geo.saveGeoPoint(geoPoint: location, responseHandler: { savedLocation in
            if let savedDriver = savedDriver as? Driver,
                let driverId = savedDriver.objectId,
                let locationId = savedLocation.objectId {
                dataStore.setRelation(columnName: "location:GeoPoint:1", parentObjectId: driverId, childrenObjectIds: [locationId], responseHandler: { relationSet in
                    print("Driver has been saved. Location object ID \(locationId)")
                }, errorHandler: { fault in
                    print("Error: \(fault.message ?? "")")
                })
            }
        }, 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 saveDriver = async () => {
      const driver = {
        driverName: 'Ruben Barrichelli',
        rating    : 4,
        carMake   : 'Lamborghini',
        carModel  : 'Diablo'
      }
      
      const location = new Backendless.GeoPoint()
    
      location.latitude = 41.878247
      location.longitude = -87.629767
      location.categories = ['drivers']
      location.metadata = {
        city: 'Chicago'
      }
    
      const [savedDriver, savedLocation] = await Promise.all([
        Backendless.Data.of('Driver').save(driver),
        Backendless.Geo.savePoint(location)
      ])
    
      await Backendless.Data.of('Driver').setRelation(savedDriver, 'location:GeoPoint:n', [savedLocation])
    
      console.log(`Driver has been saved. Location object ID ${ savedLocation.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(saveDriver)
      .catch(onError)
    

       Map driver = {
         "driverName": "Jen Buttons",
         "rating": 4,
         "carMake": "Lamborghini",
         "carModel": "Diablo",
       };
    
       Backendless.Data.of("Driver").save(driver).then((savedDriver) {
         print("Driver has been saved");
    
         GeoPoint location = GeoPoint.fromLatLng(32.803468, -96.769879);
         location.addMetadata("city", "Dallas");
         location.addCategory("drivers");
    
         Backendless.Geo.savePoint(location).then((savedLocation) {
           print("Location has been saved with ID ${savedLocation.objectId}");
    
           Backendless.Data.of("Driver").setRelation(savedDriver, "location:GeoPoint:1", children: [savedLocation]).then(
             (response) => print("Relation has been set"));
         });
       });

    If you run both functions and navigate to Backendless Console, you will see the following in the Data screen:

    Notice the “location” property/column contains a hyperlink with the coordinates of the geopoint linked to the data object. If you click the link for the first data object (Jen Buttons driving the Diablo), you will see the following:

    The Console shows the geopoint linked to the data object. The geopoint is displayed with its metadata (see the table) and the plotting on the map. This makes it very convenient for diagnosing data relations and instrumenting your backend storage.

    Enjoy!