Introduction
Welcome to Mappy.
We build geospatial platforms connecting venues to their guests. Our turnkey solutions (developer tools, including the Mappy API and Mappy SDK, for mobile apps), improve the guest experience using interactive and social custom maps. Our data analytics platform and operations dashboards are designed to inform data driven decisions.
Mappy SDKs provide an elegant and composable interface for mapping, geocoding, and routing.
The Mappy SDKs leverages the power of best in class technology, including ESRI using the ArcGIS Runtime SDKs as dependencies.
This Implementation Documentation describes how to use the latest version of the Mappy SDK for iOS and Android for ski resorts. Terms of Service for our developer tools, data analytics platform, and mapping services portal are found here.
We also have Technical Documentation for Swift and Technical Documentation for Kotlin which describes individual components of the SDK.
Register your App with Mappy
Your app needs to be registered with Mappy to use the Mappy SDK in your app. For a quote, please email info@beMappy.io. Mappy will provide customers an Id and Secret. Be sure to store them securely.
Prepare Development Requirements
iOS Development Requirements
- Xcode
14.2
or later installed - Cocoapods or Swift Package Manager can be used to include the Mappy SDK.
- Optionally CocoaPods can be installed using any of these guides:
- official guide. This guide is usually the simplest. If you run into problems, try the sudo-less installation or [troubleshooting installing CocoaPods] sections.
- using Homebrew. CocoaPods recommends using the system Ruby, but if you run into problems with the official guides then, especially if you already use Homebrew, this is probably your best option. You will have to install homebrew first, if you don't already have it installed.
- Xcode Project settings:
- Targets iOS platform versions
14.0
or later
- Targets iOS platform versions
- A physical Apple device is preferred.
If you have registered and received your Client ID and Secret Key, we have a quick-start sample you can download or clone in case you want to see some code examples in action before starting a new Xcode project.
Android Development Requirements
- Android Studio, with Android SDK. Latest versions are recommended.
- A physical Android device with Android version 6 (Marshmallow) or later.
If you have registered and received your Client ID and Secret Key, we have a quick-start sample you can download or clone in case you want to see some code examples in action before starting a new Android project.
Add Mappy SDK to your App
iOS
Include Mappy in your project using either Swift Package Manager or CocoaPods as follows:
Use Swift Package Manager to include Mappy in your project
- Open your project in Xcode
- From the File menu choose
Add Packages...
- Search for
https://github.com/beMappy/Mappy-Swift
- Select
Mappy-Swift
- Select Dependency Rule
Up to Next Major Version
- Verify that the version range is from at least version
0.8.11
- Click
Add Package
- Verify the Mappy SDK version: in the Project navigator under Package Dependencies, find Mappy and verify the grey version text is
0.8.4
or greater.
Use CocoaPods to include Mappy in your project
- Close your project
- From a command line go to your project's root directory i.e.
cd <MappyProject>
. - Create a Podfile for your project with the command
pod init
. - Edit your project's newly created
Podfile
file, and addpod 'Mappy'
after the# Pods for ...
line. - Set the platform to iOS 14 by adding or uncommenting the line
platform :ios, '14.0'
at the top of the Podfile. - Install Mappy into your project with the command
pod install
. - During the install process the versions of the dependencies will be listed to the terminal, they will also be listed in the Podfile.lock file. Verify that the Mappy version is
0.8.3
or later. - After the installation completes, open the new
.xcworkspace
file (not the ".xcodeproj" file), and verify that the Mappy dependancy is in the Pods project. It will be inPods -> Pods -> Mappy
. - Later, to update to the latest Mappy SDK version available for the specified platform, from the project directory run the command
pod update
.
Android
maven
dependencyResolutionManagement {
/*
...
*/
repositories {
/*
...
*/
mavenCentral()
maven {
url 'https://esri.jfrog.io/artifactory/arcgis'
}
}
}
In the settings.gradle
(Project Settings) file add the Jfrog and Maven Central to dependencyResolutionManagement { repositories {} } list.
dependencies
dependencies {
implement("io.github.bemappy:mappy:0.10.14")
}
In the build.gradle
(Module :app) file, for the project where you want the SDK installed, add the io.github.bemappy:mappy
to dependencies {} list.
You can find the latest version from here:
Exclude Duplicated Packages
android {
packagingOptions {
resources.excludes.add("META-INF/INDEX.LIST")
resources.excludes.add("META-INF/DEPENDENCIES")
resources.excludes.add("META-INF/io.netty.versions.properties")
}
}
In the build.gradle
(Module :app) file, in android {}
add these settings to exclude duplicated packages. Otherwise duplicated packages may prevent compilation.
Initialize Mappy in Your App
Before using Mappy in your app, it needs to be initialized.
iOS
To initialize use this code
import UIKit
import Mappy
import Combine
...
let mappyUserIdKey: String = "mappyUserId"
var mappyInitCancellable: AnyCancellable?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// UserDefaults used for demonstration purpose only.
mappyInitCancellable = MappyCore.initialize(
clientId: <#String#>,
secret: <#String#> /* ClientSecret */,
mappyUserId: UserDefaults.standard.string(forKey: self.mappyUserIdKey)
deferTracking: false, // true - if the application doesn't want to track the user's location. Note: User statistics and visual tracks depend on the tracking.
appTrackingInfo: <AppTrackingInfo> // App specific information tracked along with the user location.
)
.sink(receiveCompletion: {
if case .failure(let error) = $0 {
// TODO: handle error
print("initialize: error: \(error)")
}
}, receiveValue: { mappyMeta in
// !!!: sometimes the Mappy SDK can successfully return a MappyMeta object even though there was an error. In that case the MappyMeta object will contain error data, and no error will be passed to the receiveCompletion block.
guard let mappyUserId = mappyMeta.mappyUserId else {
// TODO: handle error
print("errorDescription: \(mappyMeta.error?.errorDescription ?? "")")
return
}
UserDefaults.standard.setValue(mappyUserId, forKey: self.mappyUserIdKey)
// Override point for customization after MappyCore Initialized.
})
// Override point for customization after application launch.
return true
}
Make sure to replace `ClientId` and `ClientSecret` with your credentials.
- Import
Mappy
,UIKit
, andCombine
modules in your UIApplicationDelegate. - Configure a Mappy shared instance, by calling MappyCore.initialize typically in your app's initializer or app delegate's
application(_:didFinishLaunchingWithOptions:)
method: - Make sure that you app stores the
mappyUserId
String on it's first launch, and passes that value to the initialize method on subsequent launches.
This function instantiates the Mappy SDK, so the SDK APIs can be used across the project. This static method requires a Client ID, Client Secret, an a returning user's Mappy User ID. In the first invocation the User ID is not required, and will be generated and passed to the app in the receiveValue:
block.
It is the application's responsibility to store the Mappy User ID accros user sessions, and to use it to initialize Mappy in subsequent sessions.
The Mappy User ID is associated with the Client ID and Client Secret credentials, and cannot be used with another set of Client credentials. When switching between development credentials and production credentials care should be taken to use a Mappy User ID which is associated with the credentials used to initialize Mappy.
Android
Using Kotlin Coroutines
val mappy = Mappy.createInstance(applicationContext)
CoroutineScope(Dispatchers.IO).launch {
val userId = mappy.initialize(clientId: String, clientSecret: String, mappyUserId: String?)
}
Using Callback
mappy.initialize(clientId: String, clientSecret: String, mappyUserId: String?, object: CompletionCallback<String> {
override fun onSuccess(result: String) {
//Initialization Success
}
override fun onError(throwable: Throwable) {
//Initialization Error
}
})
Mappy mappy = Mappy.createInstance(getApplicationContext());
mappy.initialize(String clientId, String clientSecret, @Nullable String mappyUserId, new CompletionCallback<String>() {
@Override
public void onSuccess(String result) {
//Initialization Success
}
@Override
public void onError(@NonNull Throwable throwable) {
//Initialization Error
}
});
- Import the Mappy module.
- Create a Mappy instance with Mappy.createInstance(context: Context). Be sure to pass a valid context.
- And initialize your Mappy instance with either:
This function helps to instantiate the Mappy SDK. Post initialization, SDK APIs can be used internally across the project. This method requires a Client Id, a secret and the user Id. The user Id is generated by Mappy. In the first invocation, you can pass nil as the User Id. The SDK will then generate a new user Id and send it back to the app in the completion block. It is the application's responsibility to store and pass that user Id across user sessions.
Venues
Get All Venues
let venuesService = VenuesService()
venuesService.getVenues()
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { completion in
switch completion {
case .finished: break
case .failure(let error):
print("error: \(error)")
}
}, receiveValue: { [weak self] venues in
// Received venues
})
.store(in: &cancellables)
Using Kotlin Coroutines
val venueService = VenueService.createInstance(applicationContext)
CoroutineScope(Dispatchers.IO).launch {
val venues = venueService.getVenues()
}
Using Callback
venueService.getVenues(object: CompletionCallback<List<Venue>> {
override fun onSuccess(result: List<Venue>) {
//Get Venues Successful
}
override fun onError(throwable: Throwable) {
//Get Venues Error
}
})
VenueService venueService = VenueService.createInstance(getApplicationContext());
venueService.getVenues(new CompletionCallback<List<Venue>>() {
@Override
public void onSuccess(List<Venue> result) {
//Get Venues Successful
}
@Override
public void onError(@NonNull Throwable throwable) {
//Get Venues Error
}
});
This function provides a list of venues associated with a particular Client Id. The data returned includes all details of available venues. You can get these IDs from a venue returned by VenueService().getVenues().
Get a Specific Venue
let venuesService = VenuesService()
venuesService.getVenue(venueId: <#String#> /* from the venueId property of a Venue */)
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { completion in
switch completion {
case .finished: break
case .failure(let error):
print("error: \(error)")
}
}, receiveValue: { [weak self] venue in
// Received Venue
})
.store(in: &cancellables)
Using Kotlin Coroutines
val venueService = VenueService.createInstance(applicationContext);
CoroutineScope(Dispatchers.IO).launch {
venueService.getVenue(<ID>)
}
Using Callback
venueService.getVenue(id: String, object: CompletionCallback<Venue> {
override fun onSuccess(result: Venue) {
//Get Venue Successful
}
override fun onError(throwable: Throwable) {
//Get Venue Error
}
})
VenueService venueService = VenueService.createInstance(getApplicationContext());
venueService.getVenue(String id, new CompletionCallback<Venue>() {
@Override
public void onSuccess(Venue venue) {
//Get Venue Successful
}
@Override
public void onError(@NonNull Throwable throwable) {
//Get Venue Error
}
});
This function helps to get the details for a specific venue using its identifier.
Parameters
Parameter | DataType | Description |
---|---|---|
VenueId | String | The ID of the venue to retrieve |
User Statistics
Get User's Statistics
StatsService - GetUserStats
self.statsService = StatsService()
self.statsService.getUserStats(userId: <userId>, venueId: <venueId>, startTime: <startTime>, endTime: <endTime>)
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { completion in
switch completion {
case .finished: break
case .failure(let error):
print("error: \(error)")
}
}, receiveValue: { userStats in
print("maximumAltitudeInMetres: \(userStats.maximumAltitudeInMetres)")
})
.store(in: &cancellables)
Using Kotlin Coroutines
val statsService = StatsService.createInstance(applicationContext);
CoroutineScope(Dispatchers.IO).launch {
statsService.getUserStats()
}
Using Callback
statsService.getUserStats(object: CompletionCallback<Stats> {
override fun onSuccess(result: Stats) {
//Get Stats Successful
}
override fun onError(throwable: Throwable) {
//Get Stats Error
}
})
StatsService statsService = StatsService.createInstance(getApplicationContext());
statsService.getUserStats(new CompletionCallback<Stats>() {
@Override
public void onSuccess(Stats stats) {
//Get Stats Successful
}
@Override
public void onError(@NonNull Throwable throwable) {
//Get Stats Error
}
});
This function retrieves the statistics for a user. Optionally the results can be restricted to stats from specific venu, and within a specified date range.
The list of stats availible can be found in the technical documentation at iOS - UserStats, and Android - Stats
Parameters
Parameter | DataType | Description |
---|---|---|
userId | String | The ID of any particular venue in which the user Skied, or null to use the current user ID. |
venueId | String | The ID of any particular venue in which the user Skied, or null to retrieve statistics for all venues. |
startTime | Long | A Unix time (iOS: in seconds; Android: in milliseconds). This restricts the retrieved results to only those created after startTime. When set to nil/null it will retrieve: all results before the endTime parameter, or all results if endTime is also null. |
endTime | Long | A Unix time (iOS: in seconds; Android: in milliseconds). This restricts the retrieved results to only those created before endTime. When set to nil/null it will retrieve: all results after the startTime parameter, or all results if startTime is also null. |
Rendering
Map
Map - Load
map = Map(mapInfo: <#Venue#>)
map.load()
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { completion in
switch completion {
case .finished: break
case .failure(let error):
print("error: \(error)")
}
}, receiveValue: { [weak self, weak map, weak mapView] _ in
// Map has been loaded
if let map = map {
self?.mapView?.map = map
}
})
.store(in: &cancellables)
val map = Map(venue: Venue)
map.load(context: Context, object : CompletionCallback<Map>() {
fun onSuccess(map: Map) {
binding.mapView.setMap(map)
}
fun onError(throwable: Throwable) {
// Error Handling
}
})
Map - Using Jetpack Compose
// must execute load method successfully first on the map object
MapView(
map = map,
modifier = Modifier,
mapController = MapController()
)
Map map = new Map(venue: Venue);
map.load(this, new CompletionCallback<Map>() {
@Override
public void onSuccess(@NonNull Map map) {
binding.mapView.setMap(map);
}
@Override
public void onError(@NonNull Throwable throwable) {
// Error Handling
}
});
A Map is a class that represents the 2D map. You need to inject all the required data associated with a venue to instantiate and load a Map.
Get Features
Map - Get Features
map.getFeatures()
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { completion in
switch completion {
case .finished: break
case .failure(let error):
print("error: \(error)")
}
}, receiveValue: { [weak self] features in
// Received features
})
.store(in: &cancellables)
Get Features - Using Kotlin Coroutines
val map = Map(venue)
CoroutineScope(Dispatchers.IO).launch {
val loadedMap = map.load()
loadedMap.getFeatures()
}
Get Features - Using Callback
// must execute load method successfully first on the map object
loadedMap.getFeatures(object: CompletionCallback<List<Feature>> {
override fun onSuccess(result: List<Feature>) {
//Get Features Successful
}
override fun onError(throwable: Throwable) {
//Get Features Error
}
})
// must execute load method successfully first on the map object
loadedMap.getFeatures(new CompletionCallback<List<Feature>>() {
@Override
public void onSuccess(List<Feature> result) {
//Get Features Successful
}
@Override
public void onError(@NonNull Throwable throwable) {
//Get Features Error
}
});
Get all the available features in the map like Ski run, lift, buidling, amenity, bookmarks, etc.
Search features by keyword
Map - Search Features
map.getFeatures(text: <#String#> /* search text */)
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { completion in
switch completion {
case .finished: break
case .failure(let error):
print("error: \(error)")
}
}, receiveValue: { [weak self] features in
// Received features
})
.store(in: &cancellables)
Search features - Using Kotlin Coroutines
val map = Map(venue)
CoroutineScope(Dispatchers.IO).launch {
val loadedMap = map.load()
loadedMap.getFeatures(keyword: String)
}
Search features - Using Callback
// must execute load method successfully first on the map object
loadedMap.getFeatures(keyword: String, object: CompletionCallback<List<Feature>> {
override fun onSuccess(result: List<Feature>) {
//Get Features Successful
}
override fun onError(throwable: Throwable) {
//Get Features Error
}
})
// must execute load method successfully first on the map object
loadedMap.getFeatures(String keyword, new CompletionCallback<List<Feature>>() {
@Override
public void onSuccess(List<Feature> result) {
//Get Features Successful
}
@Override
public void onError(@NonNull Throwable throwable) {
//Get Features Error
}
});
Get all the available features in the map like Ski run, lift, buidling, amenity, bookmarks, etc. that contains a given keyword.
Filter features by type
Map - Filter Features
map.getFeatures(category: .<#FeatureCategory#>(.any))
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { completion in
switch completion {
case .finished: break
case .failure(let error):
print("error: \(error)")
}
}, receiveValue: { [weak self] features in
// Received features
})
.store(in: &cancellables)
Filter features - Using Kotlin Coroutines
val map = Map(venue)
CoroutineScope(Dispatchers.IO).launch {
val loadedMap = map.load()
loadedMap.getFeatures(classType: Class)
}
Filter features - Using Callback
// must execute load method successfully first on the map object
loadedMap.getFeatures(classType: Class, object: CompletionCallback<List<Feature>> {
override fun onSuccess(result: List<Feature>) {
//Get Features Successful
}
override fun onError(throwable: Throwable) {
//Get Features Error
}
})
/**
* Possible classTypes:
* - SkiLift::class.java
* - SkiSlope::class.java
* - Building::class.java
* - BuildingAmenity::class.java
*/
// must execute load method successfully first on the map object
loadedMap.getFeatures(Class classType, new CompletionCallback<List<Feature>>() {
@Override
public void onSuccess(List<Feature> result) {
//Get Features Successful
}
@Override
public void onError(@NonNull Throwable throwable) {
//Get Features Error
}
});
/**
* Possible classTypes:
* - SkiLift.class
* - SkiSlope.class
* - Building.class
* - BuildingAmenity.class
*/
Get all the available features in the map like Ski run, lift, buidling, amenity, bookmarks, etc. that matches a given type.
Map Features
- Attribute Name: objectid
- Data Type: ObjectID
- Recommended Uses: Unique Identifier of feature
- Attribute Name: name
- Data Type: String
- Length: 60
- Recommended Uses: Search, list, badge name, selection, network routing, sort by alphabetical
- Attribute Name: length
- Data Type: Single
- Recommended Uses: In the badge and selection
- Attribute Name: trav_time
- Data Type: single
- Attribute Name: Length_feet
- Data Type: integer
- Attribute Name: pass_pos
- Data Type: string
- Length: 255
- Recommended Uses: Stand / Sit
- Attribute Name: type
- Data Type: string
- Length: 255
- Recommended Uses: Notates the type of lift e.g. 4 person chairlift that signifies which graphic to show. Included in the list, badge, selection, network routing
- Value List: lift_type
- Coded Values: Magic Carpet, Tow Rope, 2 Chairlift, 3 Chair Lift, 4 Chair Lift, 5 Chair Lift, 6 Chair Lift, Gondola, Tram
- Attribute Name: lift_number
- Data Type: Small integer
- Recommended Uses: search, list, badge name, in addition to a name the lifts have numbers, only used at some large resorts, not applicable for smaller resorts
- Attribute Name: objectid
- Data Type: ObjectID
- Recommended Uses: Unique Identifier of feature
- Attribute Name: name
- Data Type: string
- Length: 100
- Recommended Uses: Search, badge name, selection, network routing, sort by alphabetical
- Attribute Name: skill_level
- Data Type: intiger
- Recommended Uses: Notates the level of run that signifies which graphic to show per run e.g. Advanced
- Value List: skill_level
- Coded Values: 1 (Beginner), 2 (Intermediate), 3 (Advanced), 4 (Expert), 5 (Beginner Intermediate), 6 (Intermediate Advanced), 7 (Advanced Expert)
- Attribute Name: slope
- Data Type: Single
- Recommended Uses: Search, badge name, selection
- Attribute Name: avg_spd
- Data Type: Single
- Attribute Name: length
- Data Type: single
- Attribute Name: run_type
- Data Type: string
- Length: 255
- Recommended Uses: Turn by turn navigation
- Value List: run_type
- Coded Values: Main Run, Connector, Walking Path, Nordic Trail, Hike to Terrain, Tunnel, Private Skiway, Catwalk
- Attribute Name: trav_time
- Data Type: single
- Attribute Name: generalized_level
- Data Type: intiger
- Recommended Uses: Notates the level for user login and network routing (turn by turn directions by skill level)
- Value List: skill_level_gen
- Coded Values: 1 (Beginner), 2 (Intermediate), 3 (Advanced), 4 (Expert)
- Attribute Name: complexity
- Data Type: string
- Length: 255
- Value List: complexity
- Coded Values: Glades, Moguls
- Attribute Name: extreme_conditions
- Data Type: string
- Length: 255
- Value List: y_n
- Coded Values: Yes, No
- Attribute Name: objectid
- Data Type: ObjectID
- Recommended Uses: Unique Identifier of feature
- Attribute Name: objectid
- Data Type: ObjectID
- Recommended Uses: Unique Identifier of feature
- Attribute Name: building_name
- Data Type: string
- Length: 255
- Recommended Uses: In the badge name as a subtitle to the amenity in the list view and selection, amenities will be within a building and show the building name, or if it is an amenity like terrain park or kid zone (point features temporarily in this feature class) it will be 'On Mountain'
- Attribute Name: amenity_class
- Data Type: string
- Length: 255
- Recommended Uses: Search filter
- Value List: amenity_class
- Coded Values: Food and Beverage, Guest Services, Retail, Ski Features, Service, First Aid, Other, Accommodation, Transportation, Event, Label
- Attribute Name: amenity_type
- Data Type: string
- Length: 255
- Recommended Uses: Search filter
- Value List: amenity_type
- Coded Values: Bar, Casual Dining, Full Service Restaurant, Childcare, Information, Lockers, Other, Restroom, Ski School, Ski Valet, Ticket Office, Art Gallery, Bank, Grocery, Rental Shop, Repair Shop, Retail Store, Bowl, Kid Zone, Racing Zone, Slow Zone, Terrain Park, Tubing Area, Unspecified Ski Feature, ATM, Health and Wellness, Postal, Recreation, Construction, Corporate, Miscellaneous, Postal, Real Estate, Recreation, Medical Clinic, Ski Patrol, Elevator, Fountain, Ice Rink, Playground, Statue, Condo, Hotel, Bus Stop, Parking, Entertainment, Festival, Wellness, Meeting, Sporting Event, Base Area, Peak Elevation, Plaza
- Attribute Name: name
- Data Type: string
- Length: 255
- Recommended Uses: Name of the amenity on the list, search, badge name, selection, network routing, sort alphabetical
- Attribute Name: phone
- Data Type: string
- Length: 255
- Recommended Uses: In the badge name during selection
- Attribute Name: website
- Data Type: string
- Length: 10000
- Recommended Uses: In the badge name, selection
- Attribute Name: foreign_id
- Data Type: string
- Length: 255
- Attribute Name: foreign_source
- Data Type: string
- Length: 255
- Attribute Name: unique_id
- Data Type: string
- Length: 255
- Attribute Name: objectid
- Data Type: ObjectID
- Recommended Uses: Unique Identifier of feature
- Attribute Name: name
- Data Type: string
- Length: 100
- Recommended Uses: Search, badge name, selection, network routing, sort by alphabetical
MapView
MapView - Initialize
MapView()
MapView - Using XML UI
import io.bemappy.sdk.ui.MapView
MapView(context: Context)
MapView - Using Jetpack Compose
import io.bemappy.sdk.ui.compose.MapView
MapView(map: Map, modifier: Modifier, mapController: MapController)
// A recomended practice is to use Modifier.fillMaxSize() for the map view
import io.bemappy.sdk.ui.MapView;
new MapView(Context context)
MapView is an UI class that helps in rendering the 2D map. Once the map is loaded, add the map to MapView and then you can add the same to your UI hierarchy.
Rotate To North
MapView - Rotate To North
mapView.rotateToNorth()
Rotate - Using XML UI (with View Binding or Data Binding)
binding.mapView.rotateToNorth()
Rotate - Using Jetpack Compose
// must execute load method successfully first on the map object
val mapController = remember { MapController() }
MapView(
map = loadedMap,
modifier = Modifier,
mapController = mapController
)
mapController.rotateToNorth()
binding.mapView.rotateToNorth();
Rotates the map to the default orientation.
Set Initial ViewPoint
MapView - Set Initial ViewPoint
mapView.setInitialViewpoint()
Initial ViewPoint - Using XML UI (with View Binding or Data Binding)
binding.mapView.setInitialViewpoint()
Initial ViewPoint - Using Jetpack Compose
mapController.setInitialViewpoint()
binding.mapView.setInitialViewpoint();
Restores the map to its initial state where the venue is entirely visible
Tap on a Feature
Tap on a Feature
mapView.event
.receive(on: DispatchQueue.main)
.sink(receiveValue: {
switch $0 {
case .onTap(let feature):
// Handle the tap event
default:
break
}
})
.store(in: &cancellables)
Tap Listener - Using XML UI (with View Binding or Data Binding)
binding.mapView.setTapListener(object: TapListener {
override fun onTouch(feature: Feature?) {
// handle tap
}
}
)
Tap Listener - Using Jetpack Compose
MapView(
map = loadedMap,
modifier = Modifier,
mapController = mapController,
onTap = { feature -> // handle tap }
)
binding.mapView.setTapListener((feature) -> {
//handle tap
});
A callback that will be invoked with the feature the user taps on.
View Point Change
MapView - View Point Change
mapView.event
.receive(on: DispatchQueue.main)
.sink(receiveValue: {
switch $0 {
case .onViewpointChange:
// Handle the event
default:
break
}
})
.store(in: &cancellables)
View Point Change Listener - Using XML UI (with View Binding or Data Binding)
binding.mapView.setViewPointChangeListener(object: ViewpointChangeListener {
override fun onChanged() {
//handle viewpoint change
}
})
View Point Change Listener - Using Jetpack Compose
MapView(
map = loadedMap,
modifier = Modifier.fillMaxSize(),
mapController = mapController,
onViewPointChanged = {
//handle viewpoint change
}
)
binding.mapView.setViewPointChangeListener(() -> {
//handle viewpoint change
});
A callback that will be invoked every time when the user interacts with the Map except in the case of a tap.
Select a Feature
Select a Feature
mapView.select(feature: <#Feature#>)
Select a Feature - Using XML UI (with View Binding or Data Binding)
binding.mapView.selectFeature(feature: Feature)
Select a Feature - Using Jetpack Compose
MapView(
map = loadedMap,
modifier = Modifier,
mapController = mapController
)
mapController.selectFeature(feature: Feature)
Select a Feature
binding.mapView.selectFeature(Feature feature)
A function that highlights and zooms to the feature provided
Unselect a Feature
Unselect a Feature
mapView.select(feature: nil)
Unselect a Feature - Using XML UI (with View Binding or Data Binding)
binding.mapView.unselectFeature()
Unselect a Feature - Using Jetpack Compose
MapView(
map = loadedMap,
modifier = Modifier.fillMaxSize(),
mapController = mapController
)
mapController.unselectFeature()
Unselect a Feature
binding.mapView.unselectFeature()
A function that removes the highlight from the selected feature
Display User Pins
MapView - Display User Pins
mapView.displayUserPins(using: <#[UserPinLocation]#>)
MapView - Display User Pins - Using Jetpack Compose
MapView(
map = loadedMap,
modifier = Modifier.fillMaxSize(),
mapController = mapController
)
mapController.displayUserPin(userLocations: List<UserLocation>)
MapView - Display User Pins - Using View Binding
binding.mapView.displayUserPin(userLocations: List<UserLocation>)
MapView - Display User Pins - Using View Binding
binding.mapView.displayUserPin(<UserLocation>, <String>, <BitmapDrawable>)
A function that plots the pins on the map.
Remove User Pins
MapView - Remove User Pins
mapView.displayUserPins(using: [])
MapView - Remove User Pins - Using Jetpack Compose
MapView(
map = loadedMap,
modifier = Modifier.fillMaxSize(),
mapController = mapController
)
mapController.removeUserPins()
MapView - Remove User Pins - Using View Binding
binding.mapView.removeUserPins()
MapView - Remove User Pins - Using View Binding
binding.mapView.removeUserPins()
To remove the pins from the map.
Tracks
Render User's Tracks
MapView - Render user's tracks
// Current day range
let startTime = Calendar.current.startOfDay(for: Date()).timeIntervalSince1970
let endTime = Date(timeIntervalSince1970: Calendar.current.startOfDay(for: Date()).timeIntervalSince1970 + (86400 - 1)).timeIntervalSince1970 // 86400 -> 1 day in seconds
self.mapView.renderUserTrack(startTime: startTime, endTime: endTime)
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { completion in
switch completion {
case .finished: break
case .failure(let error):
print("error: \(error)")
}
}, receiveValue: { status in
print("status: \(status)")
})
.store(in: &cancellables)
MapView - Render user's tracks - Using Jetpack Compose
MapView(
map = loadedMap,
modifier = Modifier.fillMaxSize(),
mapController = mapController
)
// Start time
val beg = Calendar.getInstance()
beg.timeInMillis = 0
// End time
val end = Calendar.getInstance()
// without callback and optional parameters
mapController.renderUserTracks(
startTimeMilli = beg.timeInMillis,
endTimeMilli = end.timeInMillis,
)
// using callback
mapController.renderUserTracks(
userId = "optional_user_id",
venueId = "optional_venue_id",
startTimeMilli = beg.timeInMillis,
endTimeMilli = end.timeInMillis,
completionCallback = object : CompletionCallback<Boolean> {
override fun onSuccess(result: Boolean) {
if (result) {
// Track rendered
} else {
// No track available to render for the provided time duration
}
}
override fun onError(throwable: Throwable) {
// Error while getting user's tracks
}
}
)
MapView - Render user's tracks - Using View Binding
binding.mapView.renderUserTracks(
startTimeMilli = beg.timeInMillis,
endTimeMilli = end.timeInMillis,
)
MapView - Render user's tracks - Using View Binding
Calendar beg = Calendar.getInstance();
beg.setTimeInMillis(0);
Calendar end = Calendar.getInstance();
mapController.renderUserTracks(null,
null,
beg.getTimeInMillis(),
end.getTimeInMillis(),
new CompletionCallback<Boolean>() {
@Override
public void onSuccess(Boolean result) {
if (result) {
// Track rendered
} else {
// No track available to render for the provided time duration
}
}
@Override
public void onError(@NonNull Throwable throwable) {
// Error while getting user's tracks
}
});
Renders the user's tracks on the map.
Remove user's tracks
MapView - Remove user's tracks
self.mapView.removeUserTrack()
MapView - Remove user's tracks - Using Jetpack Compose
MapView(
map = loadedMap,
modifier = Modifier.fillMaxSize(),
mapController = mapController
)
// without callback
mapController.removeUserTrack()
// using callback
mapController.removeUserTrack(completionCallback = object : CompletionCallback<Boolean> {
override fun onSuccess(result: Boolean) {
// Track removed
}
override fun onError(throwable: Throwable) {
// Error while removing user's tracks
}
})
MapView - Remove user's tracks - Using View Binding
binding.mapView.removeUserTrack()
MapView - Render user's tracks - Using View Binding
mapController.removeUserTrack(
new CompletionCallback<Boolean>() {
@Override
public void onSuccess(Boolean result) {
// Track removed
}
@Override
public void onError(@NonNull Throwable throwable) {
// Error while removing user's tracks
}
});
Removes the user's tracks from the map.
Hide user's tracks
MapView - Hide user's tracks
self.mapView.hideUserTrack()
MapView - Hide user's tracks - Using Jetpack Compose
MapView(
map = loadedMap,
modifier = Modifier.fillMaxSize(),
mapController = mapController
)
mapController.hideUserTracks()
MapView - Hide user's tracks - Using View Binding
binding.mapView.hideUserTracks()
MapView - Hide user's tracks - Using View Binding
binding.mapView.hideUserTracks()
Make the user's tracks on the map invisible if already rendered.
To check user's tracks rendering status
MapView - Track's Redering Status
self.mapView.isUserTrackRendered
MapView - Check if user's tracks has been rendered - Using Jetpack Compose
MapView(
map = loadedMap,
modifier = Modifier.fillMaxSize(),
mapController = mapController
)
mapController.isUserTrackRendered()
MapView - Check if user's tracks has been rendered - Using View Binding
binding.mapView.isUserTrackRendered()
MapView - Check if user's tracks has been rendered - Using View Binding
binding.mapView.isUserTrackRendered()
Determines whether the user's tracks has been rendered on the map.
Check user's tracks visibility
MapView - Check if user's tracks is currently visible - Using Jetpack Compose
MapView(
map = loadedMap,
modifier = Modifier.fillMaxSize(),
mapController = mapController
)
mapController.isUserTrackVisible()
MapView - Check if user's tracks is currently visible - Using View Binding
binding.mapView.isUserTrackVisible()
MapView - Check if user's tracks is currently visible - Using View Binding
binding.mapView.isUserTrackVisible()
Determines whether the user's tracks is currently visible on the map.
Scene
Scene - Load
scene = Scene(sceneInfo: <#Venue#>)
scene.load()
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { completion in
switch completion {
case .finished: break
case .failure(let error):
print("error: \(error)")
}
}, receiveValue: { [weak self, weak scene, weak sceneView] _ in
// Scene has been loaded
if let scene = scene {
self?.sceneView?.scene = scene
}
})
.store(in: &cancellables)
Scene - Using XML UI (with View Binding or Data Binding)
val scene = Scene(venue: Venue)
scene.load(context: Context, object : CompletionCallback<Scene>() {
fun onSuccess(scene: Scene) {
binding.sceneView.setScene(scene)
}
fun onError(throwable: Throwable) {
// Error Handling
}
})
Scene - Using Jetpack Compose
// must execute load method successfully first on the scene object
SceneView(
scene = scene,
modifier = Modifier,
sceneController = SceneController()
)
Scene scene = new Scene(Venue venue);
scene.load(Context context, new CompletionCallback<Scene>() {
@Override
public void onSuccess(@NonNull Scene scene) {
binding.sceneView.setScene(scene);
}
@Override
public void onError(@NonNull Throwable throwable) {
// Error Handling
}
});
A scene is a class that represents the 3D map. You need to inject in all the required data associated with a venue to instantiate and load a Scene.
Get Features
Scene - Get Features
scene.getFeatures()
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { completion in
switch completion {
case .finished: break
case .failure(let error):
print("error: \(error)")
}
}, receiveValue: { [weak self] features in
// Received features
})
.store(in: &cancellables)
Get Features - Using Kotlin Coroutines
val scene = Scene(venue: Venue)
CoroutineScope(Dispatchers.IO).launch {
val loadedScene = scene.load()
loadedScene.getFeatures()
}
Get Features - Using Callback
// must execute load method successfully first on the scene object
loadedScene.getFeatures(object: CompletionCallback<List<Feature>> {
override fun onSuccess(result: List<Feature>) {
//Get Features Successful
}
override fun onError(throwable: Throwable) {
//Get Features Error
}
})
// must execute load method successfully first on the map object
loadedScene.getFeatures(new CompletionCallback<List<Feature>>() {
@Override
public void onSuccess(List<Feature> result) {
//Get Features Successful
}
@Override
public void onError(@NonNull Throwable throwable) {
//Get Features Error
}
});
Get all the available features in the scene like Ski run, lift, buidling, amenity, bookmarks, etc.
Search features by keyword
Scene - Search Features
scene.getFeatures(text: <#String#>)
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { completion in
switch completion {
case .finished: break
case .failure(let error):
print("error: \(error)")
}
}, receiveValue: { [weak self] features in
// Received features
})
.store(in: &cancellables)
Search features - Using Kotlin Coroutines
val scene = Scene(venue: Venue)
CoroutineScope(Dispatchers.IO).launch {
val loadedScene = scene.load()
loadedScene.getFeatures(keyword: String)
}
Search features - Using Callback
// must execute load method successfully first on the scene object
loadedScene.getFeatures(keyword: String, object: CompletionCallback<List<Feature>> {
override fun onSuccess(result: List<Feature>) {
//Get Features Successful
}
override fun onError(throwable: Throwable) {
//Get Features Error
}
})
// must execute load method successfully first on the map object
loadedScene.getFeatures(String keyword, new CompletionCallback<List<Feature>>() {
@Override
public void onSuccess(List<Feature> result) {
//Get Features Successful
}
@Override
public void onError(@NonNull Throwable throwable) {
//Get Features Error
}
});
Get all the available features in the scene like Ski run, lift, buidling, amenity, bookmarks, etc. that contains a given keyword.
Filter features by type
Scene - Filter Features
scene.getFeatures(category: <#FeatureCategory#>)
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { completion in
switch completion {
case .finished: break
case .failure(let error):
print("error: \(error)")
}
}, receiveValue: { [weak self] features in
// Received features
})
.store(in: &cancellables)
Filter features - Using Kotlin Coroutines
val scene = Scene(venue: Venue)
CoroutineScope(Dispatchers.IO).launch {
val loadedScene = scene.load()
loadedScene.getFeatures(classType: Class)
}
Filter features - Using Callback
// must execute load method successfully first on the map object
loadedScene.getFeatures(classType: Class, object: CompletionCallback<List<Feature>> {
override fun onSuccess(result: List<Feature>) {
//Get Features Successful
}
override fun onError(throwable: Throwable) {
//Get Features Error
}
})
/**
* Possible classTypes:
* - SkiLift::class.java
* - SkiSlope::class.java
* - Building::class.java
* - BuildingAmenity::class.java
*/
// must execute load method successfully first on the map object
loadedScene.getFeatures(Class classType, new CompletionCallback<List<Feature>>() {
@Override
public void onSuccess(List<Feature> result) {
//Get Features Successful
}
@Override
public void onError(@NonNull Throwable throwable) {
//Get Features Error
}
});
/**
* Possible classTypes:
* - SkiLift.class
* - SkiSlope.class
* - Building.class
* - BuildingAmenity.class
*/
Get all the available features in the scene like Ski run, lift, buidling, amenity, bookmarks, etc. that matches a given type.
SceneView
SceneView - Initialize
SceneView()
SceneView - Using XML UI
import io.bemappy.sdk.ui.SceneView
SceneView(context: Context)
SceneView - Using Jetpack Compose
val sceneController = remember { SceneController() }
SceneView(
scene = loadedScene,
modifier = Modifier,
sceneController = sceneController
)
import io.bemappy.sdk.ui.SceneView;
new SceneView(Context context)
SceneView is an UI class that helps in rendering the 3D map. Once the scene is loaded, add the scene to SceneView and then you can add the same to your UI hierarchy.
Rotate To North
SceneView - Rotate To North
sceneView.rotateToNorth()
Rotate To North - Using XML UI (with View Binding or Data Binding)
binding.sceneView.rotateToNorth()
Rotate To North - Using Jetpack Compose
// must execute load method successfully first on the map object
val sceneController = remember { SceneController() }
SceneView(
scene = loadedScene,
modifier = Modifier,
sceneController = sceneController
)
sceneController.rotateToNorth()
binding.sceneView.rotateToNorth();
Rotates the scene to the default orientation
Set Initial ViewPoint
SceneView - Set Initial ViewPoint
sceneView.setInitialViewpoint()
Set Initial ViewPoint - Using XML UI (with View Binding or Data Binding)
binding.sceneView.setInitialViewpoint()
Set Initial ViewPoint - Using Jetpack Compose
sceneController.setInitialViewpoint()
binding.sceneView.setInitialViewpoint();
Restores the scene to its initial state where the venue is entirely visible.
Tap on a Feature
Tap on a Feature
sceneView.event
.receive(on: DispatchQueue.main)
.sink(receiveValue: {
switch $0 {
case .onTap(let feature):
// Handle the tap event
default:
break
}
})
.store(in: &cancellables)
Tap Listener - Using XML UI (with View Binding or Data Binding)
binding.sceneView.setTapListener(object: TapListener {
override fun onTouch(feature: Feature?) {
// handle tap
}
}
)
Tap Listener - Using Jetpack Compose
SceneView(
scene = loadedScene,
modifier = Modifier,
sceneController = sceneController,
onTap = { feature -> // handle tap }
)
binding.sceneView.setTapListener((feature) -> {
//handle tap
});
A callback that will be invoked with the feature the user taps on.
View Point Change
SceneView - View Point Change
sceneView.event
.receive(on: DispatchQueue.main)
.sink(receiveValue: {
switch $0 {
case .onViewpointChange:
// Handle the event
default:
break
}
})
.store(in: &cancellables)
View Point Change Listener - Using XML UI (with View Binding or Data Binding)
binding.sceneView.setViewPointChangeListener(object: ViewpointChangeListener {
override fun onChanged() {
//handle viewpoint change
}
})
View Point Change Listener - Using Jetpack Compose
SceneView(
scene = loadedScene,
modifier = Modifier.fillMaxSize(),
sceneController = sceneController,
onViewPointChanged = {
//handle viewpoint change
}
)
// lambda example
binding.sceneView.setViewPointChangeListener(() -> {
// handle viewpoint change
});
A callback that will be invoked every time when the user interacts with the Scene except in the case of a tap.
Select a Feature
Select a Feature
sceneView.select(feature: <#Feature?#>)
Select a Feature - Using XML UI (with View Binding or Data Binding)
binding.sceneView.selectFeature(feature: Feature)
Select a Feature - Using Jetpack Compose
SceneView(
scene = loadedScene,
modifier = Modifier,
sceneController = sceneController
)
sceneController.selectFeature(feature: Feature)
Select a Feature - Using Jetpack Compose
binding.sceneView.selectFeature(Feature feature)
A function that highlights and zooms to the feature provided
Unselect a Feature
Unselect a Feature
sceneView.select(feature: nil)
Unselect a Feature - Using XML UI (with View Binding or Data Binding)
binding.sceneView.unselectFeature()
Unselect a Feature - Using Jetpack Compose
SceneView(
scene = loadedScene,
modifier = Modifier.fillMaxSize(),
sceneController = sceneController
)
sceneController.unselectFeature()
Unselect a Feature
binding.sceneView.unselectFeature()
A function that removes the highlight from the selected feature
Display User Pins
SceneView - Display User Pins
sceneView.displayUserPins(using: <#[UserPinLocation]#>)
SceneView - Display User Pins - Using Jetpack Compose
SceneView(
scene = loadedScene,
modifier = Modifier.fillMaxSize(),
sceneController = sceneController
)
sceneController.displayUserPin(userLocations: List<UserLocation>)
SceneView - Display User Pins - Using View Binding
binding.sceneView.displayUserPin(userLocations: List<UserLocation>)
SceneView - Display User Pins - Using View Binding
binding.sceneView.displayUserPin(userLocations: List<UserLocation>)
A function that plots the pins on the scene.
Remove User Pins
SceneView - Remove User Pins
sceneView.displayUserPins(using: [])
SceneView - Remove User Pins - Using Jetpack Compose
SceneView(
scene = loadedScene,
modifier = Modifier.fillMaxSize(),
sceneController = sceneController
)
sceneController.removeUserPins()
SceneView - Remove User Pins - Using View Binding
binding.sceneView.removeUserPins()
SceneView - Remove User Pins - Using View Binding
binding.sceneView.removeUserPins()
To remove the pins from the scene.
Tracks
Render user's tracks
SceneView - Render user's tracks
// Current day range
let startTime = Calendar.current.startOfDay(for: Date()).timeIntervalSince1970
let endTime = Date(timeIntervalSince1970: Calendar.current.startOfDay(for: Date()).timeIntervalSince1970 + (86400 - 1)).timeIntervalSince1970 // 86400 -> 1 day in seconds
self.sceneView.renderUserTrack(startTime: startTime, endTime: endTime)
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { completion in
switch completion {
case .finished: break
case .failure(let error):
print("error: \(error)")
}
}, receiveValue: { status in
print("status: \(status)")
})
.store(in: &cancellables)
SceneView - Render user's tracks - Using Jetpack Compose
SceneView(
scene = loadedScene,
modifier = Modifier.fillMaxSize(),
sceneController = sceneController
)
// Start time
val beg = Calendar.getInstance()
beg.timeInMillis = 0
// End time
val end = Calendar.getInstance()
// without callback and optional parameters
sceneController.renderUserTracks(
startTimeMilli = beg.timeInMillis,
endTimeMilli = end.timeInMillis,
)
// using callback
sceneController.renderUserTracks(
userId = "optional_user_id",
venueId = "optional_venue_id",
startTimeMilli = beg.timeInMillis,
endTimeMilli = end.timeInMillis,
completionCallback = object : CompletionCallback<Boolean> {
override fun onSuccess(result: Boolean) {
if (result) {
// Track rendered
} else {
// No tracks are available
}
}
override fun onError(throwable: Throwable) {
// Error while getting user's tracks
}
}
)
SceneView - Render user's tracks - Using View Binding
binding.sceneView.renderUserTracks(
startTimeMilli = beg.timeInMillis,
endTimeMilli = end.timeInMillis,
)
SceneView - Render user's tracks - Using View Binding
Calendar beg = Calendar.getInstance();
beg.setTimeInMillis(0);
Calendar end = Calendar.getInstance();
sceneController.renderUserTracks(null,
null,
beg.getTimeInMillis(),
end.getTimeInMillis(),
new CompletionCallback<Boolean>() {
@Override
public void onSuccess(Boolean result) {
if (result) {
// Track rendered
} else {
// No track available to render for the provided time duration
}
}
@Override
public void onError(@NonNull Throwable throwable) {
// Error while getting user's tracks
}
});
Renders the user's tracks on the scene.
Remove user's tracks
SceneView - Remove user's tracks - Using Jetpack Compose
SceneView - Remove user's tracks
self.sceneView.removeUserTrack()
SceneView(
scene = loadedScene,
modifier = Modifier.fillMaxSize(),
sceneController = sceneController
)
// without callback
sceneController.removeUserTrack()
// using callback
sceneController.removeUserTrack(completionCallback = object : CompletionCallback<Boolean> {
override fun onSuccess(result: Boolean) {
// Track removed
}
override fun onError(throwable: Throwable) {
// Error while removing user's tracks
}
})
SceneView - Remove user's tracks - Using View Binding
binding.sceneView.removeUserTrack()
SceneView - Render user's tracks - Using View Binding
sceneController.removeUserTrack(
new CompletionCallback<Boolean>() {
@Override
public void onSuccess(Boolean result) {
// Track removed
}
@Override
public void onError(@NonNull Throwable throwable) {
// Error while removing user's tracks
}
});
Removes the user's tracks from the scene.
Hide user's tracks
SceneView - Hide user's tracks
self.sceneView.hideUserTrack()
SceneView - Hide user's tracks - Using Jetpack Compose
SceneView(
scene = loadedScene,
modifier = Modifier.fillMaxSize(),
sceneController = sceneController
)
sceneController.hideUserTracks()
SceneView - Hide user's tracks - Using View Binding
binding.sceneView.hideUserTracks()
SceneView - Hide user's tracks - Using View Binding
binding.sceneView.hideUserTracks()
Make the user's tracks on the scene invisible if already rendered.
To check user's tracks rendering status
SceneView - Track's Redering Status
self.sceneView.isUserTrackRendered
SceneView - Check if user's tracks have been rendered - Using Jetpack Compose
SceneView(
scene = loadedScene,
modifier = Modifier.fillMaxSize(),
sceneController = sceneController
)
sceneController.isUserTrackRendered()
SceneView - Check if user's tracks have been rendered - Using View Binding
binding.sceneView.isUserTrackRendered()
SceneView - Check if user's tracks have been rendered - Using View Binding
binding.sceneView.isUserTrackRendered()
Determines whether the user's tracks have been rendered on the scene.
Check user's tracks visibility
SceneView - Check if user's tracks are currently visible - Using Jetpack Compose
SceneView(
scene = loadedScene,
modifier = Modifier.fillMaxSize(),
sceneController = sceneController
)
sceneController.isUserTrackVisible()
SceneView - Check if user's tracks are currently visible - Using View Binding
binding.sceneView.isUserTrackVisible()
SceneView - Check if user's tracks are currently visible - Using View Binding
binding.sceneView.isUserTrackVisible()
Determines whether the user's tracks are currently visible on the scene.
Location Tracking
Start location tracking
Location Tracking - Start
TrackingService.current.resume(appTrackingInfo: <AppTrackingInfo>)
val trackingService: TrackingService.createInstance(applicationContext)
// Using Kotlin Coroutines
CoroutineScope(Dispatchers.IO).launch {
trackingService.resume(appTrackingInfo: AppTrackingInfo)
}
TrackingService trackingService = TrackingService.createInstance(applicationContext);
trackingService.resume(AppTrackingInfo appTrackingInfo);
To start/resume the user's location tracking. NOTE: By default the Tracking service starts tracking when the SDK is initialized. This invocation would be useful when we want to toggle the tracking process.
Suspend tracking
Location Tracking - Stop
TrackingService.current.suspend()
val trackingService: TrackingService.createInstance(applicationContext)
trackingService.suspend()
TrackingService trackingService = TrackingService.createInstance(applicationContext);
trackingService.suspend();
To suspend the user's location tracking.
Update tracking payload
Location Tracking - Update App Info
TrackingService.current.appTrackingInfo = <AppTrackingInfo>
val trackingService: TrackingService.createInstance(applicationContext)
// Using Kotlin Coroutines
CoroutineScope(Dispatchers.IO).launch {
trackingService.changeTrackingPayload(appTrackingInfo: AppTrackingInfo)
}
TrackingService trackingService = TrackingService.createInstance(applicationContext);
trackingService.changeTrackingPayload(AppTrackingInfo appTrackingInfo);
To update the current tracking payload. It is necessary that the application updates the tracking service with necessary app specific information at least once. Please refer to the code snippets on how to update the current tracking payload.
To check tracking status
Location Tracking - Status
TrackingService.current.isTracking
val trackingService: TrackingService.createInstance(applicationContext)
trackingService.isTracking()
TrackingService trackingService = TrackingService.createInstance(applicationContext);
trackingService.isTracking();
Check if tracking is currently active. Returns true if tracking is active, false otherwise.
Location Sharing
Group CRUD
The GroupsService helps in creating, reading, updating and deleting groups. It can also add or remove users to and from the groups. It is mandatory to initialize the SDK before creating the instance and invoking the functions of this service.
Create a Group
Group CRUD - Create Group
groupsService.createGroup(name: <#String#>)
.sink(receiveCompletion: {
if case .failure(let error) = $0 {
// Error
}
}, receiveValue: { [weak self] in
// $0 - Group Data
})
.store(in: &cancellables)
Group CRUD - Create Group
val groupService = GroupService.createInstance(applicationContext)
//Using Kotlin Coroutines
CoroutineScope(Dispatchers.IO).launch {
val group = groupService.createGroup(groupName: String, userRole: String)
}
//using Callback
groupService.createGroup(groupName: String, userRole: String, object : CompletionCallback<Group> {
override fun onError(throwable: Throwable) {
//On Error
}
override fun onSuccess(result: Group) {
//On Success
}
})
Group CRUD - Create Group
GroupService groupService = GroupService.createInstance(getApplicationContext());
groupService.createGroup(String groupName, String userRole, new CompletionCallback<List<Group>>() {
@Override
public void onSuccess(Group result) {
//Create group Successful
}
@Override
public void onError(@NonNull Throwable throwable) {
//Create group Error
}
});
To create a group.
Fetch Groups
Group CRUD - Fetch Groups
groupsService.fetchGroups()
.sink(receiveCompletion: {
if case .failure(let error) = $0 {
// Error
}
}, receiveValue: { [weak self] in
// $0 - Groups
})
.store(in: &cancellables)
Group CRUD - Fetch Groups
val groupService = GroupService.createInstance(applicationContext)
//Using Kotlin Coroutines
CoroutineScope(Dispatchers.IO).launch {
val groups = groupService.getGroups()
}
//Using callback
groupService.getGroups(object : CompletionCallback<List<Group>> {
override fun onError(throwable: Throwable) {
//On Error
}
override fun onSuccess(result: List<Group>) {
//On Success
}
})
Group CRUD - Fetch Groups
GroupService groupService = GroupService.createInstance(getApplicationContext());
groupService.getGroups(new CompletionCallback<List<Group>>() {
@Override
public void onSuccess(List<Group> result) {
//Get groups Successful
}
@Override
public void onError(@NonNull Throwable throwable) {
//Get groups Error
}
});
To fetch all the user groups.
Fetch Group Info
Group CRUD - Fetch Groups Info
groupsService.fetchGroupInfo(groupId: <#String#>)
.sink(receiveCompletion: {
if case .failure(let error) = $0 {
// Error
}
}, receiveValue: { [weak self] in
// $0 - Group Data
})
.store(in: &cancellables)
Group CRUD - Fetch Groups Info
val groupService = GroupService.createInstance(applicationContext)
//Using Kotlin Coroutines
CoroutineScope(Dispatchers.IO).launch {
val group = groupService.getGroup(groupId: String)
}
//Using callback
groupService.getGroup(groupId: String, object: CompletionCallback<Group> {
override fun onError(throwable: Throwable) {
TODO("Not yet implemented")
}
override fun onSuccess(result: Group) {
TODO("Not yet implemented")
}
})
Group CRUD - Fetch Groups Info
GroupService groupService = GroupService.createInstance(getApplicationContext());
groupService.getGroup(String groupId, new CompletionCallback<List<Group>>() {
@Override
public void onSuccess(Group result) {
//Get group Successful
}
@Override
public void onError(@NonNull Throwable throwable) {
//Get group Error
}
});
To fetch the groups data using its identifier.
Update a Group
Group CRUD - Update Group
groupsService.updateGroup(groupId: <#String#>, name: <#String#> /* new name */)
.sink(receiveCompletion: {
if case .failure(let error) = $0 {
// Error
}
}, receiveValue: { [weak self] in
// $0 - Group Data
})
.store(in: &cancellables)
Group CRUD - Update Group
val groupService = GroupService.createInstance(applicationContext)
//Using Kotlin Coroutines
CoroutineScope(Dispatchers.IO).launch {
val group = groupService.updateGroup(groupId: String, name: String)
}
//Using callback
groupService.updateGroup(groupId: String, name: String, object: CompletionCallback<Group> {
override fun onSuccess(result: Group) {
//On Success
}
override fun onError(throwable: Throwable) {
//On Error
}
})
Group CRUD - Update Group
GroupService groupService = GroupService.createInstance(getApplicationContext());
groupService.updateGroup(String groupId, String name, new CompletionCallback<List<Group>>() {
@Override
public void onSuccess(Group result) {
//Update group Successful
}
@Override
public void onError(@NonNull Throwable throwable) {
//Update group Error
}
});
To update a group.
Delete a Group
Group CRUD - Delete Group
groupsService.deleteGroup(groupId: <#String#>)
.sink(receiveCompletion: {
if case .failure(let error) = $0 {
// Error
}
}, receiveValue: { [weak self]
// $0 - Group Data
})
.store(in: &cancellables)
Group CRUD - Delete Group
val groupService = GroupService.createInstance(applicationContext)
//Using Kotlin Coroutines
CoroutineScope(Dispatchers.IO).launch {
val group = groupService.deleteGroup(groupId: String)
}
//Using callback
groupService.deleteGroup(groupId: String, object: CompletionCallback<Group> {
override fun onSuccess(result: Group) {
//On Success
}
override fun onError(throwable: Throwable) {
//On Error
}
})
Group CRUD - Delete Group
GroupService groupService = GroupService.createInstance(getApplicationContext());
groupService.deleteGroup(String groupId, new CompletionCallback<List<Group>>() {
@Override
public void onSuccess(Group result) {
//Delete group Successful
}
@Override
public void onError(@NonNull Throwable throwable) {
//Delete group Error
}
});
To delete a group.
Add a user
Group CRUD - Add User
groupsService.addUser(userId: <#String#>, groupCode: <#String#>)
.sink(receiveCompletion: { completion in
if case .failure(let error) = completion {
// failure condition
}
}, receiveValue: { [weak self] group in
// success condition
// “group” is a Group instance of the group the user for "userId" was added to.
})
.store(in: &cancellables)
Group CRUD - Add User
val groupService = GroupService.createInstance(applicationContext)
//Using Kotlin Coroutines
CoroutineScope(Dispatchers.IO).launch {
val group = groupService.addUserToGroup(groupCode: String, userId: String, userRole: String)
}
//Using callback
groupService.addUserToGroup(groupCode: String, userId: String, userRole: String, object: CompletionCallback<Group> {
override fun onSuccess(result: Group) {
//On Success
}
override fun onError(throwable: Throwable) {
//On Error
}
})
Group CRUD - Add User
GroupService groupService = GroupService.createInstance(getApplicationContext());
groupService.addUserToGroup(String groupCode, String userId, new CompletionCallback<List<Group>>() {
@Override
public void onSuccess(Group result) {
//Add user to group Successful
}
@Override
public void onError(@NonNull Throwable throwable) {
//Add user to group Error
}
});
To add a user.
The current limit is 50 users per group. Once a group reaches its users limit, the error message 'groupUserLimitExceeded' will be returned. Please contact support@bemappy.io to increase the limit for number of users per group.
Remove a user
Group CRUD - Remove User
groupsService.removeUser(userId: user.userId, groupId: groupID)
.sink(receiveCompletion: {
if case .failure(let error) = $0 {
// Error
}
}, receiveValue: { [weak self] in
// $0 - Group data
})
.store(in: &cancellables)
Group CRUD - Remove User
val groupService = GroupService.createInstance(applicationContext)
//Using Kotlin Coroutines
CoroutineScope(Dispatchers.IO).launch {
val group = groupService.deleteUserInGroup(groupId: String, userId: String)
}
//Using callback
groupService.deleteUserInGroup(groupId: String, userId: String, object: CompletionCallback<Group> {
override fun onSuccess(result: Group) {
//On Success
}
override fun onError(throwable: Throwable) {
//On Error
}
})
Group CRUD - Remove User
GroupService groupService = GroupService.createInstance(getApplicationContext());
groupService.deleteUserInGroup(String groupId, String userId, new CompletionCallback<List<Group>>() {
@Override
public void onSuccess(Group result) {
//Delete user in group Successful
}
@Override
public void onError(@NonNull Throwable throwable) {
//Delete user in group Error
}
});
To remove a user
Group Location
Receive group members' locations and location updates, and share a user's location with group members
To use group location features an instance of GroupLocationService must be initialized and kept. There must only be one instance of GroupLocationService for the lifetime of the app.
On Event
Group Location - On Event
groupLocationService.onEvent
.sink(receiveCompletion: {
if case .failure(let error) = $0 {
// handle error
print(error.localizedDescription)
}
}, receiveValue: { [weak self] groupEvent in
switch groupEvent {
case .userLocation(let userLocation) :
print(userLocation);
case .userAddedToGroup(groupId: let groupId, userId: let userId)
print("\(groupId) \(userId)")
case .userRemovedFromGroup(groupId: let groupId, userId: let userId)
print("\(groupId) \(userId)")
}
})
.store(in: &cancellables)
Group Location - On Event
val groupLocationService = GroupLocationService.createInstance(context)
groupLocationService.onEvent(object : EventCallback<GroupEvent> {
override fun onFailure(throwable: Throwable) {
//On Failure
}
override fun onMessage(event: GroupEvent) {
//On Message
}
})
Group Location - On Event
GroupLocationService groupLocationService = GroupLocationService.createInstance(context)
groupLocationService.onEvent(new EventCallback<GroupEvent>() {
@Override
public void onMessage(GroupEvent event) {
//On Message
}
@Override
public void onFailure(Throwable throwable) {
//On Failure
}
})
After inializing a GroupLocationService instance, the next thing that needs to be set up is an onEvent callback to receive group location service notifications. If listenToGroups has been run, the receiveValue
block of onEvent will be called when any group member's location updates, or a user is added or removed from the group.
When a group member's location update is received it can be updated or added to an array/list of user pins and displayed in a mapView or displayed in a sceneView
Share User Location
Group Location - Share User Location
groupLocationService.shareUserLocation()
.sink(receiveCompletion: {
if case .failure(let error) = $0 {
// Error
}
}, receiveValue: {
// This is called when this user's device location changes. This may never be called.
})
.store(in: &self.cancellables)
Group Location - Share User Location
val groupLocationService = GroupLocationService.createInstance(context)
groupLocationService.startSharingLocation(object: EventCallback<Location> {
override fun onMessage(event: Location) {
// This is called when this user's device location changes. This may never be called.
}
override fun onFailure(throwable: Throwable) {
// Handle Failure
}
})
Group Location - Share User Location
GroupLocationService groupLocationService = GroupLocationService.createInstance(context)
groupLocationService.startSharingLocation(new EventCallback<Void>() {
@Override
public void onMessage(Location event) {
// This is called when this user's device location changes. This may never be called.
}
@Override
public void onFailure(Throwable throwable) {
//On Failure
}
})
The startSharingLocation() method of a GroupLocationService starts sending real-time location updates to other members of the user’s groups. It is required so other members of the user’s group can receive live location updates for this user.
Other users in the group will receive updates for this user's location in the callback to onEvent (only on iOS) or startListeningToGroups.
In case you also want to do something on this user’s device each time this user’s location is sent to the group, the onMessage(event: Location) callback is called each time this user’s new location is sent to the group.
Stop Sharing User Location
Group Location - Stop Sharing
groupLocationService.stopSharingUserLocation()
Group Location - Stop Sharing
val groupLocationService = GroupLocationService.createInstance(context)
groupLocationService.stopSharingLocation()
Group Location - Stop Sharing
GroupLocationService groupLocationService = GroupLocationService.createInstance(context)
groupLocationService.stopSharingLocation()
To stop sharing the user's location with the user's groups.
Listen To Groups
Group Location - Listen To Groups
groupLocationService.listenToGroups()
.sink(receiveCompletion: {
if case .failure(let error) = $0 {
// Error
}
}, receiveValue: {
// Started listening
})
.store(in: &self.cancellables)
Group Location - Listen To Groups
//using kotlin flow
groupLocationService.startListeningToGroups().catch {
//Location Flow Error
}.collectLatest { result: UserLocation ->
//Location Flow Collect Result
}
//using callback
groupLocationService.startListeningToGroups(object: EventCallback<List<GroupUser>> {
override fun onMessage(event: List<GroupUser>) {
//On Message
}
override fun onFailure(throwable: Throwable) {
//On Failure
}
})
Group Location - Listen To Groups
groupLocationService.startListeningToGroups(new EventCallback<UserLocation>() {
@Override
public void onMessage(List<GroupUser> event) {
//On Message
}
@Override
public void onFailure(@NonNull Throwable throwable) {
//On Failure
}
})
To start listening to other users' locations who are part of the user's groups.
Stop Listening To Groups
Group Location - Stop Listening
groupLocationService.stopListeningToGroups()
Group Location - Stop Listening
groupLocationService.stopListeningToGroups()
Group Location - Stop Listening
groupLocationService.stopListeningToGroups()
To stop listening to other users' locations who are part of the user's groups.
Navigation
Map
Get Routes
Navigation - Map - Routes
map.getRoutes(from: <#Feature#>, to: <#Feature#>, skiLevel: <#SkiSlope.Level#>)
.sink(receiveCompletion: {
if case .failure(let error) = $0 {
// Error
}
}, receiveValue: { [weak self] in
// $0 - Routes
}).store(in: &cancellables)
Navigation - Map - Routes
map.getRoutes(origin: Feature, destination: Feature, skiLevel: Level, callback: CompletionCallback<List<Route>>)
Navigation - Map - Routes
map.getRoutes(Feature origin, Feature destination, Level skiLevel, CompletionCallback<List<Route>> callback)
To find possible routes from one feature(origin) to another(destination) using the difficulty level.
MapView
Display a Route
Navigation - MapView - Display a Route
mapView.display(route: <#RouteResponse?#>)
Navigation - MapView - Display a Route - XML Based UI
mapView.display(route: Route)
Navigation - MapView - Display a Route - Jetpack Compose
mapController.display(route: Route)
Navigation - MapView - Display a Route
mapView.display(Route route)
To display the route on the map.
Start Navigation
Navigation - MapView - Start Navigation
let routeTracker = mapView.startNavigation(route: <#RouteResponse#>)
Navigation - MapView - Start Navigation - XML Based UI
val routeTracker = mapView.startNavigation(route: Route, destinationImage: BitmapDrawable, traversedRouteColor: Int, remainingRouteColor: Int)
Navigation - MapView - Start Navigation - Jetpack Compose
val routeTracker = mapController.startNavigation(route: Route, destinationImage: BitmapDrawable, traversedRouteColor: Int, remainingRouteColor: Int)
Note there is not RouteTracker object in android because everything is handled in the callback
Navigation - MapView - Start Navigation
RouteTracker routeTracker = mapView.startNavigation(Route route, BitmapDrawable destinationImage, Int traversedRouteColor, Int remainingRouteColor)
To start the turn by turn navigation using a route. This function returns a RouteTracker using which the application user interface can be updated during the navigation.
End Navigation
Navigation - MapView - End Navigation
mapView.stopNavigation()
Navigation - MapView - End Navigation - XML Based UI
mapView.stopNavigation()
Navigation - MapView - End Navigation - Jetpack Compose
mapController.stopNavigation()
Navigation - MapView - End Navigation
mapView.stopNavigation()
To end the navigation
Scene
Get Routes
Navigation - Scene - Routes
scene.getRoutes(from: <#Feature#>, to: <#Feature#>, skiLevel: <#SkiSlope.Level#>)
.sink(receiveCompletion: {
if case .failure(let error) = $0 {
// Error
}
}, receiveValue: { [weak self] in
// $0 - Routes
}).store(in: &cancellables)
Navigation - Scene - Routes
scene.getRoutes(origin: Feature, destination: Feature, skiLevel: Level, callback: CompletionCallback<List<Route>>)
Navigation - Scene - Routes
scene.getRoutes(Feature origin, Feature destination, Level skiLevel, CompletionCallback<List<Route>> callback)
To find possible routes from one feature(origin) to another(destination) using the difficulty level.
SceneView
Display a Route
Navigation - SceneView - Display a Route
sceneView.display(route: <#RouteResponse?#>)
Navigation - SceneView - Display a Route - XML Based UI
sceneView.display(route: Route)
Navigation - SceneView - Display a Route - Jetpack Compose
sceneController.display(route: Route)
Navigation - SceneView - Display a Route
sceneView.display(Route route)
To display the route on the scene.
Start Navigation
Navigation - SceneView - Start Navigation
let routeTracker = sceneView.startNavigation(route: <#RouteResponse#>)
Navigation - SceneView - Start Navigation - XML Based UI
val routeTracker = sceneView.startNavigation(route: Route, destinationImage: BitmapDrawable, traversedRouteColor: Int, remainingRouteColor: Int)
Navigation - SceneView - Start Navigation - Jetpack Compose
val routeTracker = sceneController.startNavigation(route: Route, destinationImage: BitmapDrawable, traversedRouteColor: Int, remainingRouteColor: Int)
Navigation - SceneView - Start Navigation
sceneView.startNavigation(Route route, BitmapDrawable destinationImage, Int traversedRouteColor, Int remainingRouteColor)
Note there is not RouteTracker object in android because everything is handled in the callback
To start the turn by turn navigation using a route. This function returns a RouteTracker using which the application user interface can be updated during the navigation.
End Navigation
Navigation - SceneView - End Navigation
sceneView.stopNavigation()
Navigation - SceneView - End Navigation - XML Based UI
sceneView.stopNavigation()
Navigation - SceneView - End Navigation - Jetpack Compose
sceneController.stopNavigation()
Navigation - SceneView - End Navigation
sceneView.stopNavigation()
To end the navigation
RouteTracker
Uses a location to provide status and progress updates as a route is traversed (by a moving vehicle, for example).
RouteTracker can give the time or distance to the next maneuver, notify if the location is off-route, and regenerate a new route if necessary.
Routing Events
Navigation - RouteTracker - Routing Events
routeTracker.onEvent
.sink(receiveValue: {
switch $0 {
case .onError(let error):
// Error
case .onTurnByTurnNavigationResponse(let response):
// Navigation status update
case .didReroute(let routeResponse):
// New route
case .didReachDestination:
// Reached destination - Navigation Ended
}
}).store(in: &cancellables)
Navigation - RouteTracker - Routing Events
routeTracker.onEvent(object: NavigationCallback<NavigationTurn> {
override fun onUpdate(result: NavigationTurn) {
//on Update
}
override fun onError(throwable: Throwable) {
//On Error
}
})
Navigation - RouteTracker - Routing Events
routeTracker.onEvent(new NavigationCallback<NavigationTurn>() {
@Override
public void onError(@NonNull Throwable throwable) {
//On Error
}
@Override
public void onUpdate(NavigationTurn result) {
//On Update
}
})
To listen to the events related to the route that is being navigated.
Audio Navigation
Navigation - RouteTracker - Enable / Disable voice directions
routeTracker.voiceDirectionsActive = <true / false>
Navigation - RouteTracker - Enable / Disable voice directions
routeTracker.areVoiceDirectionsActive = <true / false>
Navigation - RouteTracker - Enable / Disable voice directions
routeTracker.areVoiceDirectionsActive = <true / false>
To toggle the audio navigation.
Mappy Assets
- Download iOS Assets
- Download Android Assets
Icon Asset Name accommodation hotel accommodation condo accommodation location Icon Asset Name events health event entertainment events sport events meeting events festival Icon Asset Name first aid medical clinic first aid location aeds first aid ski patrol first aid fire hydrant Icon Asset Name food restaurant food bar food casual dining food location Icon Asset Name guest services information guest services lost and found guest services ski school kid zone guest services lockers guest services restroom guest services zip tours guest services ticket office guest services location Icon Asset Name other fountain other playground area other ice rink other atm other racing village summit other post office interactive building other restroom other location other ada accessible other elevator other terrain park other statue building Icon Asset Name retail art gallery retail store retail shop retail bank retail spa retail repair shop retail location retail ticket office Icon Asset Name service corporate tours service atm service health service real estate service location service post office service construction service ticket office Icon Asset Name ski features racing ski features slow zone ski features kid zone ski features ski valet ski features location ski features terrain park Icon Asset Name transportation bus transportation location transportation parking Icon Asset Name avatar 1 avatar 2 avatar 3 avatar 4 avatar 5 avatar 6 avatar 7 avatar 8 avatar 9 avatar 10 avatar 11 avatar 12 avatar 13 avatar 14 avatar 15 avatar 16 avatar 17 avatar 18 avatar 19 avatar 20 avatar 21 avatar 22 avatar 23 avatar 24 avatar 25 avatar 26 avatar 27 avatar 28 avatar 29 avatar 30 avatar 31 avatar 32 avatar 33 avatar 34 avatar 35 avatar grey 1 avatar 36 avatar grey 4 avatar grey 3 avatar grey 2 Icon Asset Name alert avatar directions building Icon Asset Name gondola tram chair lift 6 towrope chair lift 3 chair lift 4 magic carpet chair lift 5 chair lift 2 Icon Asset Name rotate settings home sign close zoom in north layers sort chevron down measurement gallery show website basemap gallery close 1 about collapse more hide search mountain view 3d view edit zoom out chevron right grid view card view elevation phone compass find location 1 current position 2d view pan Share search 1 Icon Asset Name settings 3d meet artist tutorial location sharing map legend ski patrol crew user tips coming soon stats Icon Asset Name beginner intermediate advanced expert beginner intermediate intermediate advanced expert extreme Icon Asset Name rotate pan zoom in zoom out tilt