Map
The Map module returns facility markers for a given coordinate or map viewport. Use these markers to build an interactive map experience -- pins, callouts, and an "Add" flow that associates a facility with the member's account so it can be geofenced.
Data Model
MapMarker
Each marker represents a facility suitable for rendering on a map.
| Field | Type | Description |
|---|---|---|
facilityId | Int? | Unique facility identifier. |
name | String? | Display name of the facility. |
latitude | Double? | Geographic latitude. |
longitude | Double? | Geographic longitude. |
Search Nearby
Retrieve facilities around a specific coordinate. Use this when the map first opens, centered on the member's current location.
- Android (Kotlin)
- iOS (Swift)
lifecycleScope.launch {
val markers = withContext(Dispatchers.IO) {
AFCore.map().getNearbyFacilities(
latitude = memberLocation.latitude,
longitude = memberLocation.longitude
)
}
mapRenderer.showMarkers(markers.filterNotNull())
}
let markers = try await AFCore.shared.map().getNearbyFacilities(
latitude: memberLocation.coordinate.latitude,
longitude: memberLocation.coordinate.longitude
)
mapView.showMarkers(markers.compactMap { $0 })
Search by Viewport
Retrieve facilities within the visible map region. Call this when the user pans or zooms the map.
Parameters
| Parameter | Type | Description |
|---|---|---|
upperLeftLat | Double | Latitude of the upper-left corner of the viewport. |
upperLeftLng | Double | Longitude of the upper-left corner of the viewport. |
lowerRightLat | Double | Latitude of the lower-right corner of the viewport. |
lowerRightLng | Double | Longitude of the lower-right corner of the viewport. |
- Android (Kotlin)
- iOS (Swift)
// Called when the map camera becomes idle after a pan or zoom
fun onCameraIdle(bounds: LatLngBounds) {
lifecycleScope.launch {
val markers = withContext(Dispatchers.IO) {
AFCore.map().getFacilitiesByViewPort(
upperLeftLat = bounds.northeast.latitude,
upperLeftLng = bounds.southwest.longitude,
lowerRightLat = bounds.southwest.latitude,
lowerRightLng = bounds.northeast.longitude
)
}
mapRenderer.showMarkers(markers.filterNotNull())
}
}
// Called when the map region finishes changing
func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
let region = mapView.region
let upperLeft = CLLocationCoordinate2D(
latitude: region.center.latitude + region.span.latitudeDelta / 2,
longitude: region.center.longitude - region.span.longitudeDelta / 2
)
let lowerRight = CLLocationCoordinate2D(
latitude: region.center.latitude - region.span.latitudeDelta / 2,
longitude: region.center.longitude + region.span.longitudeDelta / 2
)
Task {
let markers = try await AFCore.shared.map().getFacilitiesByViewPort(
upperLeftLat: upperLeft.latitude,
upperLeftLng: upperLeft.longitude,
lowerRightLat: lowerRight.latitude,
lowerRightLng: lowerRight.longitude
)
self.updateAnnotations(markers.compactMap { $0 })
}
}
Add a Facility from the Map
When the member selects a marker and confirms, add the facility to their account using the Facilities module. Once added, the facility becomes eligible for geofence monitoring.
- Android (Kotlin)
- iOS (Swift)
val result = AFCore.facilities().update(facilityId = marker.facilityId, verified = true)
if (result.status) {
showSuccess("Facility added")
}
let result = try await AFCore.shared.facilities().update(
facilityId: Int32(marker.facilityId),
verified: true
)
if result.status {
showSuccess("Facility added")
}
The Map module does not start geofencing by itself. Adding a facility associates it with the member; your app's geofencing flow (via startMonitoring()) handles visit detection.
Best Practices
- Load nearby on first open, then switch to viewport. Use
getNearbyFacilitieswhen the map first appears, then callgetFacilitiesByViewPortafter the user pans or zooms. This reduces initial request size. - Debounce camera events. Map frameworks fire
regionDidChange/onCameraIdlefrequently during gestures. Debounce calls to avoid excessive API requests. - Cluster dense markers. If the server returns many facilities in a small area, use marker clustering for readability and performance.
- Disable the "Add" button for existing facilities. Cross-reference map markers with the member's facility list to prevent duplicate additions.
- Cache the last response. Keep the most recent marker set in memory to avoid flicker on small pan or zoom adjustments.
Quick Reference
- Android (Kotlin)
- iOS (Swift)
AFCore.map().getNearbyFacilities(latitude, longitude)
AFCore.map().getFacilitiesByViewPort(upperLeftLat, upperLeftLng, lowerRightLat, lowerRightLng)
AFCore.facilities().update(facilityId, verified = true) // Add from map
try await AFCore.shared.map().getNearbyFacilities(latitude:longitude:)
try await AFCore.shared.map().getFacilitiesByViewPort(upperLeftLat:upperLeftLng:lowerRightLat:lowerRightLng:)
try await AFCore.shared.facilities().update(facilityId:verified:) // Add from map