To get started with Cisco DNA positioning, the MapsIndoors SDK offers all the required building blocks without any external dependencies.
The Position Provider implementation exists at the customer application level, and needs to use the MPPositionProvider interface from the MapsIndoors SDK. The MapsIndoors SDK can then use the positioning results given by the Position Provider when setting the Position Provider with MapsIndoors.setPositionProvider(MPPositionProvider).
The Position Provider should align with the MapsIndoors Floor index convention (floors are indexed as e.g 0, 10, 20, 30 corresponding to ground floor, 1st floor, 2nd floor, 3rd floor, with negative floors indices allowed as well to indicate Floors below ground, e.g. -10). It is therefore up to the Position Provider class to convert any given Floor indexing from the positioning source to that of MapsIndoors.
For a typical Position Provider, the mapping from the positioning's index needs to be mapped to the MapsIndoors Floor format.
The MapsIndoors backend is closely integrated with the CiscoDNA platform, so the MapsIndoors backend handles the floor mapping conversion for that integration. From an application perspective no Floor mapping implementation is required when integrating CiscoDNA positioning through the MapsIndoors platform.
The outer keyset (Map<String, Map<String, Object>>) contains the name of the positioning provider, for example, indooratlas3 for IndoorAtlas, or ciscodna when using Cisco DNA Spaces.
The inner keyset (Map<String, Object>) consist of various attribute fields for a given positioning provider, such as keys, floor mapping etc. These attribute fields will vary across different positioning providers, so refer to their own documentation for details.
This Guide requires you to already have an activity that shows a MapsIndoors Map as well as a Cisco DNA network with positioning active.
Now we will start implementing the CiscoDNA position provider. Create a class called CiscoDNAPositionProvider that implements the MPPositionProvider interface from the MapsIndoors SDK. Then create a constructor that takes a Context.
class CiscoDNAPositionProvider (private val context: Context, private val config: MPCiscoDNAConfig): MPPositionProvider {
privatevar mLatestPosition: MPPositionResultInterface? =nullprivateval positionUpdateListeners =ArrayList<OnPositionUpdateListener>() //If you do not have CiscoDNA set up through MapsIndoors you can write your tenantId here instead of using one from the config
privateval tenantId: String? = config.tenantId}
We will start by implementing logic to each of the implemented methods from the MPPositionProvider interface. Some of these will be generic, while others will be specific to CiscoDNA.
If all of the three above mentioned strings can be acquired, you can ask our endpoint for a CiscoDNA Device ID string. A device ID is only available if there has been a recorded positioning for the device in the past 24 hours. We will implement this as a new method into our CiscoDNAPositionProvider.
class CiscoDNAPositionProvider (private val context: Context, private val config: MPCiscoDNAConfig): MPPositionProvider {
privatevar ciscoDeviceId: String? =nullprivatevar lan: String? =nullprivatevar wan: String? =nullval MAPSINDOORS_CISCO_ENDPOINT ="https://ciscodna.mapsindoors.com/".../** * This method is responsible for gathering the local and external IP addresses * as well as acquiring a device ID from the Cisco DNA API. */privatefunupdateAddressesAndId(onComplete: MPReadyListener?) { lan =getLocalAddress()//mCiscoDeviceId = null;fetchExternalAddress {if (tenantId !=null&& lan !=null&& wan !=null) { val url: String = "$MAPSINDOORS_CISCO_ENDPOINT$tenantId/api/ciscodna/devicelookup?clientIp=$lan&wanIp=$wan"
val client =OkHttpClient()val request: Request= Request.Builder().url(url).build()try {val response = client.newCall(request).execute()if (response.isSuccessful) {val gson =Gson()val json = response.body!!.string()val jsonObject = gson.fromJson(json, JsonObject::class.java) ciscoDeviceId = jsonObject["deviceId"].asString } else { Log.d("ciscodnaprovider","Could not obtain deviceId from backend deviceID request! Code: "+ response.code ) } response.close() } catch (e: IOException) { e.printStackTrace() } } onComplete?.onResult() } }...}
Now you can make a method to start a subscription that we use when starting positioning to receive position updates. You use the SDKs LiveDataManager to create a subscription.
To handle the subscription we just created, we need to create some callbacks in the constructor of the MPPositionProvider to handle the position results and lifecycle of the subscription:
classmyFragment: Fragment(), OnMapReadyCallback {privatevar mPositionProvider: CiscoDNAPositionProvider? =nulloverridefunonCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?,): View {... MapsIndoors.load(requireActivity().applicationContext, "myapikey") { mPositionProvider = IndoorAtlasPositionProvider(requireActivity(), MapsIndoors.getSolution()!!.indoorAtlasConfig!!)
// Attach the position provider to the SDK MapsIndoors.setPositionProvider(mPositionProvider) }//Have a buttton or other logic to start positioning: binding.startPositioningButton.setOnClickListener {//Remember to handle permissions specific to the Position provider you are using mPositionProvider?.startPositioning() }... }}
Lastly, we need to tell MapControl that we want to show the position on the map.
MapControl.create(mapConfig) { mapControl: MapControl?, miError: MIError? -> mMapControl = mapControl// Enable showing the position indicator (aka. the blue dot) mMapControl?.showUserPosition(true)}
A full example implementation of the Cisco DNA position provider can be found here: PositionProviders