B4A Library GeoFire - Store and request Locations in a Firebase Realtime Database

This is a wrap for this Github project.

GeoFire is an open-source library for Android/Java that allows you to store and query a set of keys based on their geographic location.

At its heart, GeoFire simply stores locations with string keys. Its main benefit however, is the possibility of querying keys within a given geographic area - all in realtime.

GeoFire uses the Firebase database for data storage, allowing query results to be updated in realtime as they change. GeoFire selectively loads only the data near certain locations, keeping your applications light and responsive, even with extremely large datasets.

Setup:
1. Integrate Firebase.
Add Google Play Snipped and Firebase Snippet.
2. Setup Rules for the GeoFire-Part in your Realtimedatabase.

Here are some Examples:

A) Authenticated
{
"rules": {
"<your-geofire-node>": {
// Allow anyone to read the GeoFire index
".read": true,

// Index each location's geohash for faster querying
".indexOn": ["g"],

// Schema validation
"$key": {
// Allow any authentication user to add, update, or remove keys in the GeoFire index
".write": "auth !== null",

// Key validation
".validate": "newData.hasChildren(['g', 'l']) && newData.getPriority().length <= 22 && newData.getPriority().length > 0",

// Geohash validation
"g": {
".validate": "newData.val() == newData.parent().getPriority()"
},

// Location coordinates validation
"l": {
"0" : {
".validate": "newData.isNumber() && newData.val() >= -90 && newData.val() <= 90"
},
"1" : {
".validate": "newData.isNumber() && newData.val() >= -180 && newData.val() <= 180"
},
"$other": {
".validate": false
}
},

// Don't allow any other keys to be written
"$other": {
".validate": false
}
}
}
}
}

B) No deletes
{
"rules": {
"<your-geofire-node>": {
// Allow anyone to read from this node
".read": true,

// Index each location's geohash for faster querying
".indexOn": ["g"],

// Schema validation
"$key": {
// Allow anyone to add or update keys in the GeoFire index, but not remove them
".write": "newData.exists()",

// Key validation
".validate": "newData.hasChildren(['g', 'l']) && newData.getPriority().length <= 22 && newData.getPriority().length > 0",

// Geohash validation
"g": {
".validate": "newData.val() == newData.parent().getPriority()"
},

// Location coordinates validation
"l": {
"0" : {
".validate": "newData.isNumber() && newData.val() >= -90 && newData.val() <= 90"
},
"1" : {
".validate": "newData.isNumber() && newData.val() >= -180 && newData.val() <= 180"
},
"$other": {
".validate": false
}
},

// Don't allow any other keys to be written
"$other": {
".validate": false
}
}
}
}
}

C) No Updates
{
"rules": {
"<your-geofire-node>": {
// Allow anyone to read from this node
".read": true,

// Index each location's geohash for faster querying
".indexOn": ["g"],

// Schema validation
"$key": {
// Allow anyone to add keys in the GeoFire index, but not update or remove them
".write": "!data.exists()",

// Key validation
".validate": "newData.hasChildren(['g', 'l']) && newData.getPriority().length <= 22 && newData.getPriority().length > 0",

// Geohash validation
"g": {
".validate": "newData.val() == newData.parent().getPriority()"
},

// Location coordinates validation
"l": {
"0" : {
".validate": "newData.isNumber() && newData.val() >= -90 && newData.val() <= 90"
},
"1" : {
".validate": "newData.isNumber() && newData.val() >= -180 && newData.val() <= 180"
},
"$other": {
".validate": false
}
},

// Don't allow any other keys to be written
"$other": {
".validate": false
}
}
}
}
}

D) Allow anyone
{
"rules": {
"<your-geofire-node>": {
// Allow anyone to read the GeoFire index
".read": true,

// Index each location's geohash for faster querying
".indexOn": ["g"],

// Schema validation
"$key": {
// Allow anyone to add, update, or remove keys in the GeoFire index
".write": true,

// Key validation
".validate": "newData.hasChildren(['g', 'l']) && newData.getPriority().length <= 22 && newData.getPriority().length > 0",

// Geohash validation
"g": {
".validate": "newData.val() == newData.parent().getPriority()"
},

// Location coordinates validation
"l": {
"0" : {
".validate": "newData.isNumber() && newData.val() >= -90 && newData.val() <= 90"
},
"1" : {
".validate": "newData.isNumber() && newData.val() >= -180 && newData.val() <= 180"
},
"$other": {
".validate": false
}
},

// Don't allow any other keys to be written
"$other": {
".validate": false
}
}
}
}
}

Sample code

B4X:
Sub Globals
    'These global variables will be redeclared each time the activity is created.
    'These variables can only be accessed from this module.

    Private gf As GeoFire
End Sub

Sub Activity_Create(FirstTime As Boolean)
    'Do not forget to load the layout file created with the visual designer. For example:
    'Activity.LoadLayout("Layout1")
    gf.Initialize("GeoFire","geoFire") ' geoFire is the "folder" in your Databaseroot... It can be any subfolder/reference. Make sure the rules mathes the right reference.
    'gf.setLocation("HQ-Mannes",50.7907, 6.4659)
    gf.getLocation("HQ-Mannes")
 
    gf.queryAtLocation(50.7907, 6.4659,50)
End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

Sub GeoFire_onCancelled(key As String)
    Log($"GeoFire_onCancelled(${key})"$)
End Sub
Sub GeoFire_onGeoQueryError(error As String)
    Log($"GeoFire_onGeoQueryError(${error})"$)
End Sub
Sub GeoFire_onGeoQueryReady()
    Log($"GeoFire_onGeoQueryReady()"$)
End Sub
Sub GeoFire_onKeyEntered(key As String, lat As Double, lon As Double)
    Log($"GeoFire_onKeyEntered(${key}, ${lat}, ${lon})"$)
End Sub
Sub GeoFire_onKeyExited(key As String)
    Log($"GeoFire_onKeyExited(${key})"$)
End Sub
Sub GeoFire_onKeyMoved(key As String, lat As Double, lon As Double)
    Log($"GeoFire_onKeyMoved(${key}, ${lat}, ${lon})"$)
End Sub
Sub GeoFire_onLocation(key As String, lat As Double, lon As Double)
    Log($"GeoFire_onLocation(${key}, ${lat}, ${lon})"$)
End Sub
Sub GeoFire_onLocationSet(key As String, error As String)
    Log($"GeoFire_onLocationSet(${key},${error})"$)
End Sub

Based on the code here you can see this Content in your Database

GeoFire004.png


In this case i´m using this rules
{
"rules": {
"users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
},
"geoFire": {
// Allow anyone to read the GeoFire index
".read": true,

// Index each location's geohash for faster querying
".indexOn": ["g"],

// Schema validation
"$key": {
// Allow anyone to add, update, or remove keys in the GeoFire index
".write": true,

// Key validation
".validate": "newData.hasChildren(['g', 'l']) && newData.getPriority().length <= 22 && newData.getPriority().length > 0",

// Geohash validation
"g": {
".validate": "newData.val() == newData.parent().getPriority()"
},

// Location coordinates validation
"l": {
"0" : {
".validate": "newData.isNumber() && newData.val() >= -90 && newData.val() <= 90"
},
"1" : {
".validate": "newData.isNumber() && newData.val() >= -180 && newData.val() <= 180"
},
"$other": {
".validate": false
}
},

// Don't allow any other keys to be written
"$other": {
".validate": false
}
}
}
}
}
 

Attachments

  • FirebaseGeoV1.0.zip
    31.3 KB · Views: 577
Last edited:

ocalle

Active Member
Licensed User
Longtime User
hello, one quiz, with this library is possible send a message chat?
 
Top