infsoft SDK für Indoor Navigation und mehr
Die Technologie von infsoft gibt es auch als Plugin zur Integration in Apps von Dritten. Das SDK (Software Development Kit) steht derzeit für die mobilen Betriebssysteme Android und iOS sowie als HTML5 Plugin zur Verfügung. Neben einer nativen Implementierung ist auch die Nutzung von Frameworks wie PhoneGap oder Xamarin möglich. Bereits bestehende Applikationen können mit der Indoor Positionsbestimmung und Indoor Navigation von infsoft erweitert werden.
Hier finden Sie weitere Informationen zu den verschiedenen Klassenbibliotheken und deren Implementierung (Englisch):
iOS
infsoft enables you to build iOS map apps using Mapbox.
This means:
- Mapbox-style-object generated server side, containing our maps, based on your data.
- 2D and 3D mode
- infsoft Locator library to display the user’s position
- All Mapbox functionality
GETTING STARTED
Resources
The following examples show how the infsoft services can be integrated into the Mapbox SDK.
Documentation for the Mapbox Maps SDK for iOS comes in the form of:
Installation
Install the Mapbox Maps SDK as described in the instructions of the official homepage.
The current supported SDK Version is 5.6 There is no guarantee that the integration will work with other versions.
The step about placing the access token can be skipped.
Official Mapbox iOS documentation
Configuration
No access token has to be set, because the infsoft style is used for the map.
To give your users a brief explanation of how the app will use their location data if you choose to access their location, add NSLocationWhenInUseUsageDescription and NSLocationAlwaysUsageDescription key to the Info.plist file with a description.
Remove attribution
Because no data is hosted by Mapbox itself it is not necessary to include an attribution.
Attribution and telemetry can be disabled in the following way.
Add a MGLMapboxMetricsEnabledSettingShownInApp key to the Info.plist file with the value YES
// let mapView = ...
// Disable attribution and hide buttons
UserDefaults.standard.set(false, forKey: "MGLMapboxMetricsEnabled")
mapView.attributionButton.isHidden = true
mapView.logoView.isHidden = true
DISPLAY MAP
ViewController
import UIKit
import Mapbox
class ViewController: UIViewController {
let styleHost = "tilesservices.webservices.infsoft.com"
let stylePath = "/api/mapstyle/style/"
let apiKey = "8c97d7c6-0c3a-41de-b67a-fb7628efba79"
override func viewDidLoad() {
super.viewDidLoad()
var components = URLComponents()
components.scheme = "https"
components.host = styleHost
components.path = stylePath + apiKey
let mapView = MGLMapView(frame: view.bounds, styleURL: components.url)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
mapView.setCenter(CLLocationCoordinate2D(latitude: 49.867630660511715,
longitude: 10.89075028896332),
zoomLevel: 16,
animated: false)
view.addSubview(mapView)
// Disable telemetry and hide button
UserDefaults.standard.set(false, forKey: "MGLMapboxMetricsEnabled")
mapView.attributionButton.isHidden = true
mapView.logoView.isHidden = true
}
}
DISPLAY MAP 3D
ViewController
import UIKit
import Mapbox
class ViewController: UIViewController, MGLMapViewDelegate {
let styleHost = "tilesservices.webservices.infsoft.com"
let stylePath = "/api/mapstyle/style/"
let apiKey = "8c97d7c6-0c3a-41de-b67a-fb7628efba79"
let initial3D = "false"
override func viewDidLoad() {
super.viewDidLoad()
var components = URLComponents()
components.scheme = "https"
components.host = styleHost
components.path = stylePath + apiKey
let queryItemConfig = URLQueryItem(name: "config",
value: "3d:\(initial3D)")
components.queryItems = [queryItemConfig]
let mapView = MGLMapView(frame: view.bounds, styleURL: components.url)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
mapView.delegate = self
mapView.setCenter(CLLocationCoordinate2D(latitude: 49.867630660511715,
longitude: 10.89075028896332),
zoomLevel: 16,
animated: false)
view.addSubview(mapView)
// Disable telemetry and hide button
UserDefaults.standard.set(false, forKey: "MGLMapboxMetricsEnabled")
mapView.attributionButton.isHidden = true
mapView.logoView.isHidden = true
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
/// Toggle 2D/3D map
func setDimension(in mapView: MGLMapView, enabled3D: Bool) {
guard let style = mapView.style else {
return
}
if enabled3D {
for layer in style.layers {
if layer.identifier.contains("-loc3d-") {
layer.isVisible = true
} else if layer.identifier.contains("-loc2d-") {
layer.isVisible = false
}
}
} else {
for layer in style.layers {
if layer.identifier.contains("-loc3d-") {
layer.isVisible = false
} else if layer.identifier.contains("-loc2d-") {
layer.isVisible = true
}
}
}
}
// MARK: - Mapbox mapView delegate
func mapView(_ mapView: MGLMapView, didFinishLoading style: MGLStyle) {
setDimension(in: mapView, enabled3D: true)
}
}
SWITCH LEVELS
ViewController
import UIKit
import Mapbox
class ViewController: UIViewController, MGLMapViewDelegate {
let styleHost = "tilesservices.webservices.infsoft.com"
let stylePath = "/api/mapstyle/style/"
let apiKey = "8c97d7c6-0c3a-41de-b67a-fb7628efba79"
let initial3D = "false"
var currentLevel = 0
var numberOfLevels = 4
weak var mapView: MGLMapView?
override func viewDidLoad() {
super.viewDidLoad()
var components = URLComponents()
components.scheme = "https"
components.host = styleHost
components.path = stylePath + apiKey
let queryItemConfig = URLQueryItem(name: "config",
value: "3d:\(initial3D)")
components.queryItems = [queryItemConfig]
let mapView = MGLMapView(frame: view.bounds, styleURL: components.url)
self.mapView = mapView
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
mapView.delegate = self
mapView.setCenter(CLLocationCoordinate2D(latitude: 49.867630660511715,
longitude: 10.89075028896332),
zoomLevel: 16,
animated: false)
view.insertSubview(mapView, at: 0)
// Disable telemetry and hide button
UserDefaults.standard.set(false, forKey: "MGLMapboxMetricsEnabled")
mapView.attributionButton.isHidden = true
mapView.logoView.isHidden = true
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
// MARK: - Actions
@IBAction private func switchLevel(_ sender: Any) {
currentLevel = (currentLevel + 1) % numberOfLevels
guard let mapView = mapView else {
return
}
setLevel(in: mapView, level: currentLevel)
}
// MARK: - Map dimension handling
/// Toggle 2D/3D map
func setDimension(in mapView: MGLMapView, enabled3D: Bool) {
guard let style = mapView.style else {
return
}
if enabled3D {
for layer in style.layers {
if layer.identifier.contains("-loc3d-") {
layer.isVisible = true
} else if layer.identifier.contains("-loc2d-") {
layer.isVisible = false
}
}
} else {
for layer in style.layers {
if layer.identifier.contains("-loc3d-") {
layer.isVisible = false
} else if layer.identifier.contains("-loc2d-") {
layer.isVisible = true
}
}
}
}
// MARK: - Map level handling
private func setLevel(in mapView: MGLMapView, level: Int) {
guard let style = mapView.style else {
return
}
let filterLayers: [MGLVectorStyleLayer] = style.layers.compactMap {
$0 as? MGLVectorStyleLayer
}.filter {
$0.identifier.contains("locls")
}
for layer in filterLayers {
if let predicate = layer.predicate {
guard let rawData = predicate.mgl_jsonExpressionObject as? [Any] else {
return
}
let newPredicate = NSPredicate(mglJSONObject: setFilter(filter: rawData, level: level))
layer.predicate = newPredicate
}
}
}
func setFilter(filter: Any, level: Int) -> Any {
guard var predicates = filter as? [Any] else {
return filter
}
for (index, element) in predicates.enumerated() {
if element as? [Any] != nil {
predicates[index] = self.setFilter(filter: element, level: level)
continue
}
if index == 0 {
continue
}
guard let prev = predicates[index - 1] as? String else {
continue
}
if predicates[index] as? Int == nil {
continue
}
if prev != "level" {
continue
}
predicates[index] = level
}
return predicates
}
// MARK: - Mapbox mapView delegate
func mapView(_ mapView: MGLMapView, didFinishLoading style: MGLStyle) {
setDimension(in: mapView, enabled3D: true)
}
}
DISPLAY ROUTE
RouteViewController
import UIKit
import Mapbox
class RouteViewController: UIViewController, MGLMapViewDelegate {
let styleHost = "tilesservices.webservices.infsoft.com"
let stylePath = "/api/mapstyle/style/"
let apiKey = "8c97d7c6-0c3a-41de-b67a-fb7628efba79"
let initial3D = "false"
override func viewDidLoad() {
super.viewDidLoad()
var components = URLComponents()
components.scheme = "https"
components.host = styleHost
components.path = stylePath + apiKey
let queryItemConfig = URLQueryItem(name: "config",
value: "3d:\(initial3D)")
components.queryItems = [queryItemConfig]
let mapView = MGLMapView(frame: view.bounds, styleURL: components.url)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
mapView.delegate = self
mapView.setCenter(CLLocationCoordinate2D(latitude: 49.867630660511715,
longitude: 10.89075028896332),
zoomLevel: 18,
animated: false)
view.insertSubview(mapView, at: 0)
// Disable telemetry and hide button
UserDefaults.standard.set(false, forKey: "MGLMapboxMetricsEnabled")
mapView.attributionButton.isHidden = true
mapView.logoView.isHidden = true
}
/// This creates a valid URL for requesting route information
/// - Parameters:
/// - startLat: Latitude of starting point
/// - startLon: Longitude of starting point
/// - startLevel: Level ID of starting point
/// - endLat: Latitude of endpoint
/// - endLon: Longitude of endpoint
/// - endLevel: Level ID of endpoint
/// - apiKey: To be used apiKey
/// - localization: Localication identifier, eg. "DE", "EN"
/// - wayContext: Waycontext for route calculation. Empty string is the default value
/// - Returns: A route service URL for the given parameters
func createRouteURL(startLat: Double, startLon: Double, startLevel: Int, endLat: Double, endLon: Double,
endLevel: Int, apiKey: String, localization: String = "EN", wayContext: String = "") -> URL {
var compoments = URLComponents()
compoments.scheme = "https"
compoments.host = "routes.webservices.infsoft.com"
compoments.path = "/API/Calc"
let queryItemAPIKey = URLQueryItem(name: "apikey", value: apiKey)
let queryItemStartLat = URLQueryItem(name: "startlat", value: "\(startLat)")
let queryItemStartLon = URLQueryItem(name: "startlon", value: "\(startLon)")
let queryItemStartLevel = URLQueryItem(name: "startlevel", value: "\(startLevel)")
let queryItemEndLat = URLQueryItem(name: "endlat", value: "\(endLat)")
let queryItemEndLon = URLQueryItem(name: "endlon", value: "\(endLon)")
let queryItemEndLevel = URLQueryItem(name: "endlevel", value: "\(endLevel)")
let queryItemLocalization = URLQueryItem(name: "lcid", value: "\(localization)")
let queryItemWayContext = URLQueryItem(name: "context", value: "\(wayContext)")
compoments.queryItems = [queryItemAPIKey, queryItemStartLat, queryItemStartLon,
queryItemStartLevel, queryItemEndLat, queryItemEndLon,
queryItemEndLevel,
queryItemLocalization, queryItemWayContext]
return compoments.url!
}
func renderGeoJSON(from route: Route, in mapView: MGLMapView) {
guard let routeElement = route.first?.geoJSON, let geoJSONData = try? JSONEncoder().encode(routeElement) else {
return
}
guard let shapeFromGeoJSON = try? MGLShape(data: geoJSONData, encoding: String.Encoding.utf8.rawValue) else {
return
}
guard let style = mapView.style, let source = style.source(withIdentifier: "loc-routes") as? MGLShapeSource else {
return
}
source.shape = shapeFromGeoJSON
}
// MARK: - Mapbox mapView delegate
func mapView(_ mapView: MGLMapView, didFinishLoading style: MGLStyle) {
let session = URLSession(configuration: .default, delegate: nil, delegateQueue: .main)
let routeServiceURL = createRouteURL(startLat: 49.86739, startLon: 10.89190, startLevel: 0,
endLat: 49.86701, endLon: 10.89054, endLevel: 0, apiKey: apiKey)
let task = session.dataTask(with: routeServiceURL, completionHandler: { (data: Data?, _: URLResponse?,
error: Error?) -> Void in
guard error == nil, let routeData = data else {
// Handle error case
return
}
if let routeModel = try? JSONDecoder().decode(Route.self, from: routeData) {
self.renderGeoJSON(from: routeModel, in: mapView)
}
})
task.resume()
}
}
Route JSON model
public typealias Route = [RouteElement]
// MARK: - RouteElement
public struct RouteElement: Codable {
enum CodingKeys: String, CodingKey {
case copyrights = "Copyrights"
case distance = "Distance"
case duration = "Duration"
case endTS = "EndTS"
case startTS = "StartTS"
case endAddress = "EndAddress"
case endLocation = "EndLocation"
case startAddress = "StartAddress"
case startLocation = "StartLocation"
case steps = "Steps"
case context = "Context"
case valid = "Valid"
case revision = "Revision"
case geoJSON = "GeoJson"
}
let copyrights: String
let distance: Distance
let duration: Duration
let endTS, startTS: String
let endAddress: String
let endLocation: PolyPoint
let startAddress: String
let startLocation: PolyPoint
let steps: [Step]
let context: String
let valid: Bool
let revision: Int
let geoJSON: GeoJSON
}
// MARK: - Distance
struct Distance: Codable {
enum CodingKeys: String, CodingKey {
case value = "Value"
case text = "Text"
}
let value: Int
let text: String
}
// MARK: - Duration
struct Duration: Codable {
enum CodingKeys: String, CodingKey {
case value = "Value"
case text = "Text"
}
let value: Int
let text: String
}
// MARK: - EndLocation
struct PolyPoint: Codable {
enum CodingKeys: String, CodingKey {
case latitude = "Latitude"
case longitude = "Longitude"
case level = "Level"
}
let latitude, longitude: Double
let level: Int
}
// MARK: - GeoJSON
struct GeoJSON: Codable {
let type: String
let features: [Feature]
}
// MARK: - Feature
struct Feature: Codable {
let properties: Properties
let type: FeatureType
let geometry: Geometry
}
// MARK: - Geometry
struct Geometry: Codable {
let type: GeometryType
let coordinates: [GeometryCoordinate]
}
enum GeometryCoordinate: Codable {
case double(Double)
case unionArray([RouteCoordinate])
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let value = try? container.decode([RouteCoordinate].self) {
self = .unionArray(value)
return
}
if let value = try? container.decode(Double.self) {
self = .double(value)
return
}
throw DecodingError.typeMismatch(GeometryCoordinate.self,
DecodingError.Context(codingPath: decoder.codingPath,
debugDescription: "Wrong type for GeometryCoordinate"))
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .double(let value):
try container.encode(value)
case .unionArray(let value):
try container.encode(value)
}
}
}
enum RouteCoordinate: Codable {
case double(Double)
case doubleArray([Double])
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let value = try? container.decode([Double].self) {
self = .doubleArray(value)
return
}
if let value = try? container.decode(Double.self) {
self = .double(value)
return
}
throw DecodingError.typeMismatch(RouteCoordinate.self,
DecodingError.Context(codingPath: decoder.codingPath,
debugDescription: "Wrong type for RouteCoordinate"))
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .double(let value):
try container.encode(value)
case .doubleArray(let value):
try container.encode(value)
}
}
}
enum GeometryType: String, Codable {
case lineString = "LineString"
case point = "Point"
case polygon = "Polygon"
}
// MARK: - Properties
struct Properties: Codable {
let level: Int
let style: Style?
let width: Int?
let color: String?
let icon, title: String?
let step: Int?
}
enum Style: String, Codable {
case route
case routedirection
}
enum FeatureType: String, Codable {
case feature = "Feature"
}
// MARK: - Calc
struct Calc: Codable {
enum CodingKeys: String, CodingKey {
case unit = "Unit"
case startVal = "StartVal"
case endVal = "EndVal"
}
let unit: String
let startVal, endVal: Int
}
struct Step: Codable {
enum CodingKeys: String, CodingKey {
case direction = "Direction"
case endLocation = "EndLocation"
case startLocation = "StartLocation"
case endTS = "EndTS"
case startTS = "StartTS"
case distance = "Distance"
case distanceCalc = "DistanceCalc"
case duration = "Duration"
case durationCalc = "DurationCalc"
case instructions = "Instructions"
case instructionsTemplate = "InstructionsTemplate"
case polyline = "Polyline"
case travelMode = "TravelMode"
case travelObjectName = "TravelObjectName"
case travelObjectID = "TravelObjectID"
}
let direction: Int
let endLocation, startLocation: PolyPoint
let endTS, startTS: String
let distance: Distance
let distanceCalc: Calc
let duration: Duration
let durationCalc: Calc
let instructions, instructionsTemplate: String
let polyline: [PolyPoint]
let travelMode: Int
let travelObjectName, travelObjectID: String?
}
USER LOCALIZATION
Add the Locator library
Add the locator library as embedded content to your project. It should look like the following image.
For swift projects you have to import the Locator header in your bridging header file as follows.
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//
#import <InfsoftLocatorLib/InfsoftLocator.h>
Info.plist##
Since the position of the user is determined via Bluetooth, the NSBluetoothAlwaysUsageDescription and NSBluetoothPeripheralUsageDescription key must be additionally added to your Info.plist.
The Locator library also requires NSMotionUsageDescription as a key in your Info.plist file. The library pauses the location updates if no device motion is detected. In this way the battery consumption is reduced.
UserLocalizationViewController
To display the user’s position via BLE, the map’s default location manager must be overwritten.
import UIKit
import Mapbox
class UserLocalizationViewController: UIViewController {
// Sample UI controls. This can be extended as desired
@IBOutlet private weak var userTrackingModeButton: UIButton!
@IBOutlet private weak var mapLevelButton: UIButton!
let styleHost = "tilesservices.webservices.infsoft.com"
let stylePath = "/api/mapstyle/style/"
let apiKey = "316cf74a-290b-4c4f-91c0-43989cdb15d3"
let initial3D = "false"
let numberOfMapLevels = 4
var mapLevel = 0
weak var mapView: MGLMapView?
override func viewDidLoad() {
super.viewDidLoad()
var components = URLComponents()
components.scheme = "https"
components.host = styleHost
components.path = stylePath + apiKey
let queryItemConfig = URLQueryItem(name: "config", value: "3d:\(initial3D)")
components.queryItems = [queryItemConfig]
let mapView = MGLMapView(frame: view.bounds, styleURL: components.url)
self.mapView = mapView
mapView.delegate = self
mapView.userTrackingMode = .follow
mapView.locationManager = CustomLocationManager()
mapView.showsUserLocation = true
mapView.showsUserHeadingIndicator = true
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
mapView.setCenter(CLLocationCoordinate2D(latitude: 50.99319673751623,
longitude: 11.012463569641115),
zoomLevel: 16,
animated: false)
view.insertSubview(mapView, at: 0)
// Disable telemetry and hide button
UserDefaults.standard.set(false, forKey: "MGLMapboxMetricsEnabled")
mapView.attributionButton.isHidden = true
mapView.logoView.isHidden = true
}
// MARK: - Example action outlets
/**
Example action outlet for switching the user tracking mode.
If you want to use this feature just connect an UIButton to this outlet
*/
@IBAction private func switchUserTrackingMode(_ sender: Any) {
guard let mapView = mapView else {
return
}
switch mapView.userTrackingMode {
case .follow:
mapView.userTrackingMode = .none
default:
mapView.userTrackingMode = .follow
}
}
/**
Example action outlet for switching the map level.
If you want to use this feature just connect an UIButton to this outlet
*/
@IBAction private func switchLevel(_ sender: Any) {
let newLevel = (mapLevel + 1) % numberOfMapLevels
guard let mapView = mapView else {
return
}
mapView.userTrackingMode = .none
setMapLevel(in: mapView, level: newLevel)
if let userLocation = mapView.userLocation {
updateUserAnnotation(userLocation, in: mapView)
}
}
/**
Optional method for updating the UI in case the tracking mode
changes
*/
func updateUserTrackingControl(for mode: MGLUserTrackingMode) {
userTrackingModeButton.isSelected = mode == .follow
}
/**
Optional method for updating the UI in case the map level
changes
*/
func updateMapLevelControl(for level: Int) {
mapLevelButton.setTitle("\(level)", for: .normal)
}
// MARK: - Map level handling
/**
This method syncs the map level to the user level if the corresponding mode is selected
*/
func syncMapLevel() {
guard let mapView = mapView else {
return
}
switch mapView.userTrackingMode {
case .follow, .followWithHeading:
guard let userLevel = (mapView.userLocation?.location as? CLLocation3D)?.level else {
break
}
self.setMapLevel(in: mapView, level: userLevel)
default:
break
}
}
func setMapLevel(in mapView: MGLMapView, level: Int) {
guard let style = mapView.style, level != mapLevel else {
return
}
let filterLayers: [MGLVectorStyleLayer] = style.layers.compactMap {
$0 as? MGLVectorStyleLayer
}.filter {
$0.identifier.contains("locls")
}
for layer in filterLayers {
if let predicate = layer.predicate {
guard let rawData = predicate.mgl_jsonExpressionObject as? [Any] else {
return
}
let newPredicate = NSPredicate(mglJSONObject: setFilter(filter: rawData, level: level))
layer.predicate = newPredicate
}
}
mapLevel = level
updateMapLevelControl(for: level)
}
func setFilter(filter: Any, level: Int) -> Any {
guard var predicates = filter as? [Any] else {
return filter
}
for (index, element) in predicates.enumerated() {
if element as? [Any] != nil {
predicates[index] = self.setFilter(filter: element, level: level)
continue
}
if index == 0 {
continue
}
guard let prev = predicates[index - 1] as? String else {
continue
}
if predicates[index] as? Int == nil {
continue
}
if prev != "level" {
continue
}
predicates[index] = level
}
return predicates
}
}
// MARK: - Mapbox mapView delegate
extension UserLocalizationViewController: MGLMapViewDelegate {
func mapView(_ mapView: MGLMapView, didUpdate userLocation: MGLUserLocation?) {
guard let userLocation = mapView.userLocation else {
return
}
syncMapLevel()
updateUserAnnotation(userLocation, in: mapView)
}
func mapView(_ mapView: MGLMapView, didChange mode: MGLUserTrackingMode, animated: Bool) {
syncMapLevel()
updateUserTrackingControl(for: mode)
}
/**
Hide the annotation view if the wrong level is selected. If the default view is used,
this is achieved by setting a large offset. If you implement the annotation view by yourself,
you can do this explicitly using the update() method
*/
func updateUserAnnotation(_ userLocation: MGLUserLocation, in mapView: MGLMapView) {
guard let location3DCoordinate = userLocation.location as? CLLocation3D else {
return
}
let view = mapView.view(for: userLocation)
if location3DCoordinate.level != mapLevel {
view?.centerOffset = CGVector(dx: 0, dy: Int.max)
} else {
view?.centerOffset = CGVector(dx: 0, dy: 0)
}
mapView.updateUserLocationAnnotationView()
}
}
CustomLocationManager
import UIKit
import Mapbox
class CustomLocationManager: NSObject, MGLLocationManager {
weak var delegate: MGLLocationManagerDelegate?
let apiKey = "8c97d7c6-0c3a-41de-b67a-fb7628efba79"
/// CLLocationManager is used for requesting the user heading
lazy var clLocationManger: CLLocationManager = {
let locationManager = CLLocationManager()
locationManager.delegate = self
return locationManager
}()
/// ILLocationManager is used for requesting the user position
lazy var ilLocationManger: ILLocationManager = {
let iLLocationServiceManager = ILLocationServiceManager.getInstance(ILLocationServiceManager_UseAll)
iLLocationServiceManager?.apiKey = apiKey
return ILLocationManager()
}()
var authorizationStatus: CLAuthorizationStatus {
CLLocationManager.authorizationStatus()
}
var headingOrientation: CLDeviceOrientation {
get {
clLocationManger.headingOrientation
}
set {
clLocationManger.headingOrientation = newValue
}
}
func requestAlwaysAuthorization() {
clLocationManger.requestAlwaysAuthorization()
}
func requestWhenInUseAuthorization() {
clLocationManger.requestWhenInUseAuthorization()
}
func startUpdatingLocation() {
ilLocationManger.requestLocationUpdates(self, minTime: 0, minDistance: 0)
}
func stopUpdatingLocation() {
clLocationManger.stopUpdatingLocation()
}
func startUpdatingHeading() {
clLocationManger.startUpdatingHeading()
}
func stopUpdatingHeading() {
clLocationManger.stopUpdatingHeading()
}
func dismissHeadingCalibrationDisplay() {
clLocationManger.dismissHeadingCalibrationDisplay()
}
}
// MARK: - CLLocationManagerDelegate
extension CustomLocationManager: CLLocationManagerDelegate {
/// This method should do nothing. Use ILLocationManager instead
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
}
func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) {
delegate?.locationManager(self, didUpdate: newHeading)
}
func locationManagerShouldDisplayHeadingCalibration(_ manager: CLLocationManager) -> Bool {
delegate?.locationManagerShouldDisplayHeadingCalibration(self) ?? false
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
delegate?.locationManager(self, didFailWithError: error)
}
}
// MARK: - ILLocation
extension CustomLocationManager: ILLocationListener {
func onLocationChanged(_ location: ILLocation!) {
let clLocation3D = CLLocation3D(latitude: location.latitude, longitude: location.longitude)
clLocation3D.level = Int(location.level)
delegate?.locationManager(self, didUpdate: [clLocation3D])
}
}
/// Extension of the class so that the user level can also be specified
class CLLocation3D: CLLocation {
var level: Int?
}
POI SELECTION
POISelectionViewController
import UIKit
import Mapbox
class POISelectionViewController: UIViewController {
// Map style
let styleHost = "tilesservices.webservices.infsoft.com"
let stylePath = "/api/mapstyle/style/"
let apiKey = "8c97d7c6-0c3a-41de-b67a-fb7628efba79"
let initial3D = "false"
// GeoObjects
let geoObjectsHost = "tiles.infsoft.com"
let geoObjectsPath = "/api/geoobj/json/"
// Revision
let revisionHost = "tilesservices.webservices.infsoft.com"
let revisionPath = "/api/geojson/revision"
let numberOfMapLevels = 4
var mapLevel = 0
var geoObjects: GeoObjects?
weak var mapView: MGLMapView?
var revisionNumber: String? {
didSet {
loadGeoObjects()
}
}
override func viewDidLoad() {
super.viewDidLoad()
var components = URLComponents()
components.scheme = "https"
components.host = styleHost
components.path = stylePath + apiKey
let queryItemConfig = URLQueryItem(name: "config", value: "3d:\(initial3D)")
components.queryItems = [queryItemConfig]
let mapView = MGLMapView(frame: view.bounds, styleURL: components.url)
self.mapView = mapView
mapView.delegate = self
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
mapView.setCenter(CLLocationCoordinate2D(latitude: 49.867630660511715,
longitude: 10.89075028896332),
zoomLevel: 16,
animated: false)
view.insertSubview(mapView, at: 0)
// Disable telemetry and hide button
UserDefaults.standard.set(false, forKey: "MGLMapboxMetricsEnabled")
mapView.attributionButton.isHidden = true
mapView.logoView.isHidden = true
addGestureRecognizer()
loadRevisionNumber()
}
// MARK: - Handle annotation
func addGestureRecognizer() {
guard let mapView = mapView else {
return
}
let singleTap = UITapGestureRecognizer(target: self, action: #selector(handleMapTap(sender:)))
for recognizer in mapView.gestureRecognizers! where recognizer is UITapGestureRecognizer {
guard let recognizer = recognizer as? UITapGestureRecognizer else {
continue
}
if recognizer.numberOfTapsRequired == 1 {
continue
}
singleTap.require(toFail: recognizer)
}
mapView.addGestureRecognizer(singleTap)
}
@objc func handleMapTap(sender: UITapGestureRecognizer) {
guard let mapView = mapView else {
return
}
if sender.state == .ended {
let point = sender.location(in: sender.view!)
let layerIdentifiers = Set<String>(arrayLiteral: "locls-pois")
for feature in mapView.visibleFeatures(at: point, styleLayerIdentifiers: layerIdentifiers)
where feature is MGLPointFeature {
guard let selectedFeature = feature as? MGLPointFeature else {
fatalError("Failed to cast selected feature as MGLPointFeature")
}
showAnnotation(feature: selectedFeature)
return
}
let touchCoordinate = mapView.convert(point, toCoordinateFrom: nil)
let touchLocation = CLLocation(latitude: touchCoordinate.latitude, longitude: touchCoordinate.longitude)
let touchRect = CGRect(origin: point, size: .zero).insetBy(dx: -22.0, dy: -22.0)
let possibleFeatures = mapView.visibleFeatures(in: touchRect, styleLayerIdentifiers: Set(layerIdentifiers)).filter { $0 is MGLPointFeature }
// Select the closest feature to the touch center.
let closestFeatures = possibleFeatures.sorted(by: {
return CLLocation(latitude: $0.coordinate.latitude, longitude: $0.coordinate.longitude).distance(from: touchLocation) < CLLocation(latitude: $1.coordinate.latitude, longitude: $1.coordinate.longitude).distance(from: touchLocation)
})
if let feature = closestFeatures.first {
guard let closestFeature = feature as? MGLPointFeature else {
fatalError("Failed to cast selected feature as MGLPointFeature")
}
showAnnotation(feature: closestFeature)
return
}
// If no features were found, deselect the selected annotation, if any.
mapView.deselectAnnotation(mapView.selectedAnnotations.first, animated: true)
}
}
@objc func showAnnotation(feature: MGLPointFeature) {
guard let geoObjects = self.geoObjects, let featureUID = feature.attribute(forKey: "uid") as? String else {
return
}
// Search for the matching GeoObject in the loaded data
let matchingGeoObjects = geoObjects.filter { (geoObject) -> Bool in
geoObject.uid == featureUID
}
guard let firstMatchingGeoObject = matchingGeoObjects.first else {
return
}
let annotation = MGLPointAnnotation()
// Set the custom title for the annotation.
annotation.title = firstMatchingGeoObject.type
annotation.coordinate = feature.coordinate
mapView?.selectAnnotation(annotation, animated: true, completionHandler: nil)
}
// MARK: - GeoObjects
func loadRevisionNumber() {
var components = URLComponents()
components.scheme = "https"
components.host = revisionHost
components.path = revisionPath + "/" + apiKey
guard let url = components.url else {
return
}
let session = URLSession(configuration: .default, delegate: nil, delegateQueue: .main)
let task = session.dataTask(with: url, completionHandler: { (data: Data?, _: URLResponse?, error: Error?) -> Void in
guard error == nil, let revisionData = data else {
// Handle error case
return
}
self.revisionNumber = String(data: revisionData, encoding: .utf8)
})
task.resume()
}
func loadGeoObjects() {
guard let revisionNumber = revisionNumber else {
return
}
var components = URLComponents()
components.scheme = "https"
let localization = "en"
components.host = geoObjectsHost
components.path = geoObjectsPath + apiKey + "/" + localization + "/" + revisionNumber
guard let url = components.url else {
return
}
let session = URLSession(configuration: .default, delegate: nil, delegateQueue: .main)
let task = session.dataTask(with: url, completionHandler: { (data: Data?, _: URLResponse?, error: Error?) -> Void in
guard error == nil, let objectsData = data else {
// Handle error case
return
}
self.geoObjects = try? JSONDecoder().decode(GeoObjects.self, from: objectsData)
})
task.resume()
}
}
extension POISelectionViewController: MGLMapViewDelegate {
func mapView(_ mapView: MGLMapView, didDeselect annotation: MGLAnnotation) {
mapView.removeAnnotation(annotation)
}
func mapView(_ mapView: MGLMapView, annotationCanShowCallout annotation: MGLAnnotation) -> Bool {
return geoObjects != nil
}
func mapView(_ mapView: MGLMapView, calloutViewFor annotation: MGLAnnotation) -> MGLCalloutView? {
return nil
}
}
GeoObject
public typealias GeoObjects = [GeoObject]
// MARK: - GeoObject
@objc public class GeoObject: NSObject, Codable {
public let uid: String
public let type: String
public let props: [String: String]
public func propertyFor(key: String) -> String? {
return props[key]
}
}
Android
infsoft enables you to build Android map apps using Mapbox. This means:
- Mapbox-style-object generated server side, containing our maps, based on your data.
- 2D and 3D mode
- infsoft Locator library to display the user’s position
- All Mapbox functionality
GETTING STARTED
MapBox dependency
Add MapBox to your build.gradle dependencies. MapBox 8.4.0 is supported:
implementation 'com.mapbox.mapboxsdk:mapbox-android-sdk:8.4.0'
COMMON CODE
Here you can find code that is used in multiple demo apps.
Switching levels
The following code is used in every example application, to change the selected level.
import com.mapbox.mapboxsdk.maps.Style;
import com.mapbox.mapboxsdk.style.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.FillExtrusionLayer;
import com.mapbox.mapboxsdk.style.layers.FillLayer;
import com.mapbox.mapboxsdk.style.layers.Layer;
import com.mapbox.mapboxsdk.style.layers.LineLayer;
import com.mapbox.mapboxsdk.style.layers.SymbolLayer;
import java.util.List;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconSize;
/**
* Helper class to change the selected level.
*/
public class LevelSwitch {
/**
* Changes the MapBox filters to display only data for the given level.
*
* @param style MapBox style object on which the filters will be changed.
* @param level The level that should be displayed.
*/
public static void updateLevel(Style style, int level) {
List<Layer> layers = style.getLayers();
for (Layer layer : layers) {
String layerId = layer.getId();
if (layerId.contains("locls")) {
if (layer instanceof SymbolLayer) {
SymbolLayer symbolLayer = (SymbolLayer) layer;
String id = symbolLayer.getId();
Expression updatedFilter = calculateFilter(symbolLayer.getFilter().toArray(), level);
symbolLayer.setFilter(updatedFilter);
} else if (layer instanceof FillExtrusionLayer) {
FillExtrusionLayer fillExtrusionLayer = (FillExtrusionLayer) layer;
Expression updatedFilter = calculateFilter(fillExtrusionLayer.getFilter().toArray(), level);
fillExtrusionLayer.setFilter(updatedFilter);
} else if (layer instanceof FillLayer) {
FillLayer fillLayer = (FillLayer) layer;
Expression updatedFilter = calculateFilter(fillLayer.getFilter().toArray(), level);
fillLayer.setFilter(updatedFilter);
} else if (layer instanceof LineLayer) {
LineLayer lineLayer = (LineLayer) layer;
Expression updatedFilter = calculateFilter(lineLayer.getFilter().toArray(), level);
lineLayer.setFilter(updatedFilter);
}
}
}
}
private static Expression calculateFilter(Object[] currentFilter, int filterLevel) {
ExpressionBuilder builder = new ExpressionBuilder(currentFilter, filterLevel);
Expression updatedFilter = builder.buildExpression();
return updatedFilter;
}
}
import com.mapbox.mapboxsdk.style.expressions.Expression;
import java.util.ArrayList;
import java.util.List;
/**
* Requirement: The level check in an existing filter(comes from the server) must be changed.
* Other values must not be altered.
* <p>
* The filter comes in the form on an Object[]. At the end, an Expression must be returned. The Expression
* MUST ONLY CONSIST OF OTHER EXPRESSIONS.
* <p>
* Every Object[] consists of 1 operator(equals, notEquals, bigger, smaller..) and X ExpressionData.
* ExpressionData can be either ExpressionValue(a single string or float packaged in an expression)
* or another ExpressionBuilder. Example expression:
* ["all",
* ["==", ["geometry-type"], "LineString"],
* ["==", ["get", "level"], 0.0],
* ["any",
* ["==", ["get", "style"], "route"],
* ["all",
* ["==", ["get", "style"], "routedirection"],
* ["==", ["get", "step"], 1.0]
* ]
* ]
* ]
*/
class ExpressionBuilder implements ExpressionData {
private String operator;
private List<ExpressionData> arguments;
private static boolean levelFound = false;
/**
* Breaks the given filter into Expression objects.
*
* @param filter Object[] representing the current MapBox layer filter.
* @param level The level to be filtered for.
*/
ExpressionBuilder(Object[] filter, int level) {
if (filter[0] instanceof String) {
operator = (String) filter[0];
} else {
return;
}
arguments = new ArrayList<>();
for (int i = 1; i < filter.length; i++) {
if (filter[i] instanceof Object[]) {
Object[] subArray = (Object[]) filter[i];
ExpressionBuilder subExpression = new ExpressionBuilder(subArray, level);
arguments.add(subExpression);
} else {
Expression literal;
if (filter[i] instanceof String) {
String valueString = (String) filter[i];
if (valueString.equals("level")) {
levelFound = true;
}
literal = Expression.literal(valueString);
} else if (filter[i] instanceof Float) {
Float valueFloat;
if (levelFound) {
valueFloat = (float) level;
levelFound = false;
} else {
valueFloat = (Float) filter[i];
}
literal = Expression.literal(valueFloat);
} else {
arguments = null;
return;
}
arguments.add(new ExpressionValue(literal));
}
}
}
/**
* Reassembles the filter given in the constructor, with filters for the selected level.
*
* @return MapBox filter for the selected level.
*/
Expression buildExpression() {
Expression expression;
List<Expression> combinedElements = new ArrayList<>();
for (ExpressionData data : arguments) {
if (data instanceof ExpressionBuilder) {
ExpressionBuilder subBuilder = (ExpressionBuilder) data;
combinedElements.add(subBuilder.buildExpression());
} else if (data instanceof ExpressionData) {
ExpressionValue subValue = (ExpressionValue) data;
combinedElements.add(subValue.getValue());
}
}
Expression[] expressions = new Expression[combinedElements.size()];
expressions = combinedElements.toArray(expressions);
expression = new Expression(operator, expressions);
return expression;
}
}
import com.mapbox.mapboxsdk.style.expressions.Expression;
class ExpressionValue implements ExpressionData {
private final Expression value;
ExpressionValue(Expression value) {
this.value = value;
}
Expression getValue() {
return value;
}
}
interface ExpressionData {
}
DISPLAY MAP
activity_display_map
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".DisplayMapActivity">
<com.mapbox.mapboxsdk.maps.MapView
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent">
</com.mapbox.mapboxsdk.maps.MapView>
</RelativeLayout>
DisplayMapActivity
package com.infsoft.android.locaware.demos;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import com.infsoft.android.locaware.demos.level.LevelSwitch;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.maps.Style;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconSize;
public class DisplayMapActivity extends AppCompatActivity implements OnMapReadyCallback, Style.OnStyleLoaded {
private final String STYLE_URL = "https://tilesservices.webservices.infsoft.com/api/mapstyle/style/";
private final String API_KEY = "8c97d7c6-0c3a-41de-b67a-fb7628efba79";
private final String INITIAL_3D = "FALSE";
private MapView mapView;
private MapboxMap mapboxMap;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Mapbox.getInstance(this, "pk.");
TelemetryDefinition telemetry = Mapbox.getTelemetry();
telemetry.setUserTelemetryRequestState(false);
setContentView(R.layout.activity_display_map);
mapView = findViewById(R.id.mapView);
mapView.getMapAsync(this);
}
@Override
public void onMapReady(@NonNull MapboxMap mapboxMap) {
this.mapboxMap = mapboxMap;
String styleUrl = STYLE_URL + API_KEY + "?config=3d:" + INITIAL_3D;
this.mapboxMap.setStyle(new Style.Builder().fromUri(styleUrl), this);
this.mapboxMap.getUiSettings().setAttributionEnabled(false);
this.mapboxMap.getUiSettings().setLogoEnabled(false);
}
@Override
public void onStyleLoaded(@NonNull Style style) {
LevelSwitch.updateLevel(style,0);
CameraPosition cameraPosition = new CameraPosition.Builder()
.target(new LatLng( 49.867630660511715, 10.89075028896332)) // Sets the new camera position
.zoom(18) // Sets the zoom
.bearing(0) // Rotate the camera
.tilt(0) // Set the camera tilt
.build();
this.mapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
}
@Override
public void onStart() {
super.onStart();
if (mapView != null) {
mapView.onStart();
}
}
@Override
public void onResume() {
super.onResume();
if (mapView != null) {
mapView.onResume();
}
}
@Override
public void onPause() {
super.onPause();
if (mapView != null) {
mapView.onPause();
}
}
@Override
public void onStop() {
super.onStop();
if (mapView != null) {
mapView.onStop();
}
}
@Override
public void onLowMemory() {
super.onDestroy();
if (mapView != null) {
mapView.onLowMemory();
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (mapView != null) {
mapView.onDestroy();
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mapView != null) {
mapView.onSaveInstanceState(outState);
}
}
}
LevelSwitch
DISPLAY MAP 3D
activity_display_map3_d
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".DisplayMap3DActivity">
<com.mapbox.mapboxsdk.maps.MapView
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent">
</com.mapbox.mapboxsdk.maps.MapView>
</RelativeLayout>
DisplayMap3DActivity
package com.infsoft.android.locaware.demos;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import com.infsoft.android.locaware.demos.level.LevelSwitch;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.maps.Style;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconSize;
public class DisplayMapActivity extends AppCompatActivity implements OnMapReadyCallback, Style.OnStyleLoaded {
private final String STYLE_URL = "https://tilesservices.webservices.infsoft.com/api/mapstyle/style/";
private final String API_KEY = "8c97d7c6-0c3a-41de-b67a-fb7628efba79";
private final String INITIAL_3D = "FALSE";
private MapView mapView;
private MapboxMap mapboxMap;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Mapbox.getInstance(this, "pk.");
TelemetryDefinition telemetry = Mapbox.getTelemetry();
telemetry.setUserTelemetryRequestState(false);
setContentView(R.layout.activity_display_map);
mapView = findViewById(R.id.mapView);
mapView.getMapAsync(this);
}
@Override
public void onMapReady(@NonNull MapboxMap mapboxMap) {
this.mapboxMap = mapboxMap;
String styleUrl = STYLE_URL + API_KEY + "?config=3d:" + INITIAL_3D;
this.mapboxMap.setStyle(new Style.Builder().fromUri(styleUrl), this);
this.mapboxMap.getUiSettings().setAttributionEnabled(false);
this.mapboxMap.getUiSettings().setLogoEnabled(false);
}
@Override
public void onStyleLoaded(@NonNull Style style) {
LevelSwitch.updateLevel(style,0);
CameraPosition cameraPosition = new CameraPosition.Builder()
.target(new LatLng( 49.867630660511715, 10.89075028896332)) // Sets the new camera position
.zoom(18) // Sets the zoom
.bearing(0) // Rotate the camera
.tilt(0) // Set the camera tilt
.build();
this.mapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
}
@Override
public void onStart() {
super.onStart();
if (mapView != null) {
mapView.onStart();
}
}
@Override
public void onResume() {
super.onResume();
if (mapView != null) {
mapView.onResume();
}
}
@Override
public void onPause() {
super.onPause();
if (mapView != null) {
mapView.onPause();
}
}
@Override
public void onStop() {
super.onStop();
if (mapView != null) {
mapView.onStop();
}
}
@Override
public void onLowMemory() {
super.onDestroy();
if (mapView != null) {
mapView.onLowMemory();
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (mapView != null) {
mapView.onDestroy();
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mapView != null) {
mapView.onSaveInstanceState(outState);
}
}
}
LevelSwitch
SWITCH LEVELS
activity_switch_levels
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".SwitchLevelsActivity">
<com.mapbox.mapboxsdk.maps.MapView
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent">
</com.mapbox.mapboxsdk.maps.MapView>
<Button
android:id="@+id/switchLevelButton"
android:layout_marginTop="15dp"
android:layout_marginStart="15dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SWITCH LEVEL"
android:layout_gravity="left|top"
android:orientation="vertical"
android:textColor="#444444"
/>
</RelativeLayout>
SwitchLevelsActivity
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import com.infsoft.android.locaware.switchlevels.level.LevelSwitch;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.maps.Style;
public class SwitchLevelsActivity extends AppCompatActivity implements OnMapReadyCallback, Style.OnStyleLoaded {
private final String STYLE_URL = "https://tilesservices.webservices.infsoft.com/api/mapstyle/style/";
private final String API_KEY = "8c97d7c6-0c3a-41de-b67a-fb7628efba79";
private final String INITIAL_3D = "FALSE";
private MapView mapView;
private MapboxMap mapboxMap;
private Style loadedStyle;
private int currentLevel = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Mapbox.getInstance(this, "pk.");
TelemetryDefinition telemetry = Mapbox.getTelemetry();
telemetry.setUserTelemetryRequestState(false);
setContentView(R.layout.activity_switch_levels);
mapView = findViewById(R.id.mapView);
mapView.getMapAsync(this);
}
@Override
public void onMapReady(@NonNull MapboxMap mapboxMap) {
this.mapboxMap = mapboxMap;
this.mapboxMap.getUiSettings().setAttributionEnabled(false);
this.mapboxMap.getUiSettings().setLogoEnabled(false);
String styleUrl = STYLE_URL + API_KEY + "?config=3d:" + INITIAL_3D;
this.mapboxMap.setStyle(new Style.Builder().fromUri(styleUrl), this);
}
@Override
public void onStyleLoaded(@NonNull Style style) {
this.loadedStyle = style;
LevelSwitch.updateLevel(style,0);
CameraPosition cameraPosition = new CameraPosition.Builder()
.target(new LatLng( 49.867630660511715, 10.89075028896332)) // Sets the new camera position
.zoom(18) // Sets the zoom
.bearing(0) // Rotate the camera
.tilt(0) // Set the camera tilt
.build();
this.mapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
Button levelSwitch = findViewById(R.id.switchLevelButton);
levelSwitch.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
currentLevel = (currentLevel + 1) % 4;
LevelSwitch.updateLevel(loadedStyle, currentLevel);
}
});
}
@Override
public void onStart() {
super.onStart();
if (mapView != null) {
mapView.onStart();
}
}
@Override
public void onResume() {
super.onResume();
if (mapView != null) {
mapView.onResume();
}
}
@Override
public void onPause() {
super.onPause();
if (mapView != null) {
mapView.onPause();
}
}
@Override
public void onStop() {
super.onStop();
if (mapView != null) {
mapView.onStop();
}
}
@Override
public void onLowMemory() {
super.onDestroy();
if (mapView != null) {
mapView.onLowMemory();
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (mapView != null) {
mapView.onDestroy();
}
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
if (mapView != null) {
mapView.onSaveInstanceState(outState);
}
}
}
LevelSwitch
DISPLAY ROUTE
activity_display_route
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".DisplayRouteActivity">
<com.mapbox.mapboxsdk.maps.MapView
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent">
</com.mapbox.mapboxsdk.maps.MapView>
<Button
android:id="@+id/calcRouteButton"
android:layout_marginTop="15dp"
android:layout_marginStart="15dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CALC ROUTE"
android:layout_gravity="left|top"
android:orientation="vertical"
android:textColor="#444444"
/>
</RelativeLayout>
DisplayRouteActivity
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import com.infsoft.android.locaware.displayroute.level.LevelSwitch;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.maps.Style;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
import com.mapbox.mapboxsdk.style.sources.Source;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.List;
import java.util.concurrent.ExecutionException;
public class DisplayRouteActivity extends AppCompatActivity implements OnMapReadyCallback, Style.OnStyleLoaded {
private final String STYLE_URL = "https://tilesservices.webservices.infsoft.com/api/mapstyle/style/";
private final String API_KEY = "8c97d7c6-0c3a-41de-b67a-fb7628efba79";
private final String INITIAL_3D = "TRUE";
private MapView mapView;
private MapboxMap mapboxMap;
private GeoJsonSource routeSource;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Mapbox.getInstance(this, "pk.");
TelemetryDefinition telemetry = Mapbox.getTelemetry();
telemetry.setUserTelemetryRequestState(false);
setContentView(R.layout.activity_display_route);
mapView = findViewById(R.id.mapView);
mapView.getMapAsync(this);
}
@Override
public void onMapReady(@NonNull MapboxMap mapboxMap) {
this.mapboxMap = mapboxMap;
this.mapboxMap.getUiSettings().setAttributionEnabled(false);
this.mapboxMap.getUiSettings().setLogoEnabled(false);
String styleUrl = STYLE_URL + API_KEY + "?config=3d:" + INITIAL_3D;
this.mapboxMap.setStyle(new Style.Builder().fromUri(styleUrl), this);
}
@Override
public void onStyleLoaded(@NonNull Style style) {
LevelSwitch.updateLevel(style, 0);
initSource(style);
setInitialCamera();
Button levelSwitch = findViewById(R.id.calcRouteButton);
levelSwitch.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
onCalcRouteClicked();
}
});
}
private void setInitialCamera(){
CameraPosition cameraPosition = new CameraPosition.Builder()
.target(new LatLng(49.867630660511715, 10.89075028896332)) // Sets the new camera position
.zoom(18) // Sets the zoom
.bearing(0) // Rotate the camera
.tilt(45) // Set the camera tilt
.build();
this.mapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
}
private void initSource(Style style) {
List<Source> sources = style.getSources();
for (Source source : sources) {
if (source.getId().contains("route") && source instanceof GeoJsonSource) {
routeSource = (GeoJsonSource) source;
}
}
}
private void onCalcRouteClicked() {
String urlString = "https://routes.webservices.infsoft.com/API/Calc?"
+ "apikey=" + API_KEY
+ "&startlat=" + 49.86739
+ "&startlon=" + 10.89190
+ "&startlevel=" + 0
+ "&endlat=" + 49.86701
+ "&endlon=" + 10.89054
+ "&endlevel=" + 0;
String rawRouteJson = null;
try {
RestCall restCall = new RestCall();
rawRouteJson = restCall.execute(urlString).get();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
setGeoJson(rawRouteJson);
}
private void setGeoJson(String rawRouteJson){
JSONArray array;
try {
array = new JSONArray(rawRouteJson);
for (int i = 0; i < array.length(); i++) {
JSONObject jsonObj = array.getJSONObject(i);
rawRouteJson = jsonObj.getString("GeoJson");
}
} catch (JSONException e) {
e.printStackTrace();
}
routeSource.setGeoJson(rawRouteJson);
}
@Override
public void onStart() {
super.onStart();
if (mapView != null) {
mapView.onStart();
}
}
@Override
public void onResume() {
super.onResume();
if (mapView != null) {
mapView.onResume();
}
}
@Override
public void onPause() {
super.onPause();
if (mapView != null) {
mapView.onPause();
}
}
@Override
public void onStop() {
super.onStop();
if (mapView != null) {
mapView.onStop();
}
}
@Override
public void onLowMemory() {
super.onDestroy();
if (mapView != null) {
mapView.onLowMemory();
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (mapView != null) {
mapView.onDestroy();
}
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
if (mapView != null) {
mapView.onSaveInstanceState(outState);
}
}
}
RestCall
import android.os.AsyncTask;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
class RestCall extends AsyncTask<String, Void, String> {
@Override
protected String doInBackground(String... strings) {
String urlString = strings[0];
StringBuilder sb = new StringBuilder();
try {
URL url = new URL(urlString);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Accept", "application/json");
BufferedReader br = new BufferedReader(new InputStreamReader(
(conn.getInputStream())));
String output;
while ((output = br.readLine()) != null) {
sb.append(output);
}
conn.disconnect();
} catch (MalformedURLException e) {
e.printStackTrace();
return null;
} catch (IOException e) {
e.printStackTrace();
return null;
}
return sb.toString();
}
}
LevelSwitch
USER LOCALIZATION
Locator library
1. Add the infsoft Locator Library to your application and reference the library.
2. Add the required permissions in your AndroidManifest:
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
3. Add permission in build.gradle:
dependencies {
implementation 'com.google.code.gson:gson:2.8.8'
}
4. Add the locator service to your AndroidManifest:
<service
android:name="com.infsoft.android.locator.LocatorService"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="android.infsoft.com.library.locator.LocatorService.SERVICE"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<meta-data
android:name="useBLE"
android:value="true"/>
<meta-data
android:name="ignoreWIFI"
android:value="true" />
</service>
activity_user_localization
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".UserLocalizationActivity">
<com.mapbox.mapboxsdk.maps.MapView
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent">
</com.mapbox.mapboxsdk.maps.MapView>
</RelativeLayout>
UserLocalizationActivity
import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.infsoft.android.locator.Location;
import com.infsoft.android.locator.LocationListener;
import com.infsoft.android.locator.LocationManager;
import com.infsoft.android.locaware.userlocalization.level.LevelSwitch;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.location.LocationComponent;
import com.mapbox.mapboxsdk.location.LocationComponentActivationOptions;
import com.mapbox.mapboxsdk.location.modes.RenderMode;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.maps.Style;
import com.mapbox.mapboxsdk.maps.TelemetryDefinition;
public class UserLocalizationActivity extends AppCompatActivity implements OnMapReadyCallback, Style.OnStyleLoaded, LocationListener {
private final String STYLE_URL = "https://tilesservices.webservices.infsoft.com/api/mapstyle/style/";
private final String API_KEY = "8c97d7c6-0c3a-41de-b67a-fb7628efba79";
private final String INITIAL_LEVEL = "0";
private final String INITIAL_3D = "FALSE";
private MapView mapView;
private MapboxMap mapboxMap;
private static LocationManager locationManager;
private LocationComponent locationComponent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Mapbox.getInstance(this, "pk.");
TelemetryDefinition telemetry = Mapbox.getTelemetry();
telemetry.setUserTelemetryRequestState(false);
setContentView(R.layout.activity_user_localization);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 200);
}
mapView = findViewById(R.id.mapView);
mapView.getMapAsync(this);
}
@Override
public void onMapReady(@NonNull MapboxMap mapboxMap) {
this.mapboxMap = mapboxMap;
this.mapboxMap.getUiSettings().setAttributionEnabled(false);
this.mapboxMap.getUiSettings().setLogoEnabled(false);
String styleUrl = STYLE_URL + API_KEY + "?config=level:" + INITIAL_LEVEL + "|3d:" + INITIAL_3D;
this.mapboxMap.setStyle(new Style.Builder().fromUri(styleUrl), this);
}
private void setInitialCamera() {
CameraPosition cameraPosition = new CameraPosition.Builder()
.target(new LatLng(49.867630660511715, 10.89075028896332)) // Sets the new camera position
.zoom(18) // Sets the zoom
.bearing(0) // Rotate the camera
.tilt(0) // Set the camera tilt
.build();
this.mapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
}
@Override
public void onStyleLoaded(@NonNull Style style) {
LevelSwitch.updateLevel(style, 0);
setInitialCamera();
locationComponent = mapboxMap.getLocationComponent();
initMapBoxLocationComponent(style);
initLocator(this);
}
// Make MapBox show the user's position
private void initMapBoxLocationComponent(@NonNull Style loadedMapStyle) {
LocationComponentActivationOptions locationComponentActivationOptions =
LocationComponentActivationOptions.builder(this, loadedMapStyle)
.useDefaultLocationEngine(false)
.build();
locationComponent.activateLocationComponent(locationComponentActivationOptions);
locationComponent.setRenderMode(RenderMode.COMPASS);
locationComponent.setLocationComponentEnabled(true);
}
// Listen to updates from the infsoft Locator library
private void initLocator(Activity activity) {
if (locationManager == null)
locationManager = LocationManager.getService(activity, API_KEY, 101677);
locationManager.requestLocationUpdates(activity, 2000, 1000, this);
}
@Override
public void onLocationChanged(Location location) {
android.location.Location androidLocation = new android.location.Location("");
androidLocation.setLatitude(location.getLatitude());
androidLocation.setLongitude(location.getLongitude());
mapboxMap.getLocationComponent().forceLocationUpdate(androidLocation);
if (location.getLevel() == Integer.valueOf(INITIAL_LEVEL)) {
locationComponent.setLocationComponentEnabled(true);
} else {
locationComponent.setLocationComponentEnabled(false);
}
}
@Override
public void onStart() {
super.onStart();
if (mapView != null) {
mapView.onStart();
}
}
@Override
public void onResume() {
super.onResume();
if (mapView != null) {
mapView.onResume();
}
}
@Override
public void onPause() {
super.onPause();
if (mapView != null) {
mapView.onPause();
}
}
@Override
public void onStop() {
super.onStop();
if (mapView != null) {
mapView.onStop();
}
}
@Override
public void onLowMemory() {
super.onDestroy();
if (mapView != null) {
mapView.onLowMemory();
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (mapView != null) {
mapView.onDestroy();
}
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
if (mapView != null) {
mapView.onSaveInstanceState(outState);
}
}
}
LevelSwitch
POI SELECTION
activity_select_POI
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".SelectPOIActivity">
<com.mapbox.mapboxsdk.maps.MapView
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent">
</com.mapbox.mapboxsdk.maps.MapView>
</RelativeLayout>
SelectPOIActivity
package com.infsoft.android.locaware.selectpoi;
import android.graphics.PointF;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.infsoft.android.locaware.selectpoi.level.LevelSwitch;
import com.mapbox.geojson.Feature;
import com.mapbox.geojson.Geometry;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.annotations.Marker;
import com.mapbox.mapboxsdk.annotations.MarkerOptions;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.maps.Style;
import com.mapbox.mapboxsdk.maps.TelemetryDefinition;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
public class SelectPOIActivity extends AppCompatActivity implements OnMapReadyCallback, Style.OnStyleLoaded {
private final String STYLE_URL = "https://tilesservices.webservices.infsoft.com/api/mapstyle/style/";
private final String API_KEY = "8c97d7c6-0c3a-41de-b67a-fb7628efba79";
private final String INITIAL_3D = "FALSE";
private final String POI_LAYER_ID = "locls-pois";
private MapView mapView;
private MapboxMap mapboxMap;
private List<PoiGeoJsonObject> pois = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Mapbox.getInstance(this, "pk.");
TelemetryDefinition telemetry = Mapbox.getTelemetry();
telemetry.setUserTelemetryRequestState(false);
setContentView(R.layout.activity_select_poi);
mapView = findViewById(R.id.mapView);
mapView.getMapAsync(this);
loadPOIs();
}
@Override
public void onMapReady(@NonNull MapboxMap mapboxMap) {
this.mapboxMap = mapboxMap;
this.mapboxMap.getUiSettings().setAttributionEnabled(false);
this.mapboxMap.getUiSettings().setLogoEnabled(false);
String styleUrl = STYLE_URL + API_KEY + "?config=3d:" + INITIAL_3D;
this.mapboxMap.setStyle(new Style.Builder().fromUri(styleUrl), this);
}
@Override
public void onStyleLoaded(@NonNull Style style) {
LevelSwitch.updateLevel(style, 0);
setInitialCamera();
mapboxMap.addOnMapClickListener(new MapboxMap.OnMapClickListener() {
@Override
public boolean onMapClick(@NonNull LatLng point) {
removeMarkers(mapboxMap);
Feature selectedFeature = findSelectedFeature(point);
PoiGeoJsonObject selectedPoi = findClickedPoi(selectedFeature);
createMarker(selectedPoi, selectedFeature);
return true;
}
});
}
private Feature findSelectedFeature(LatLng point) {
PointF screenPoint = mapboxMap.getProjection().toScreenLocation(point);
List<Feature> features = mapboxMap.queryRenderedFeatures(screenPoint, POI_LAYER_ID);
if (features != null && !features.isEmpty()) {
return features.get(0);
} else {
return null;
}
}
private PoiGeoJsonObject findClickedPoi(Feature selectedFeature) {
if (selectedFeature == null)
return null;
String id = selectedFeature.getStringProperty("uid");
for (PoiGeoJsonObject poi : pois) {
if (poi.uid.equals(id)) {
return poi;
}
}
return null;
}
private void createMarker(PoiGeoJsonObject selectedPoi, Feature selectedFeature) {
if (selectedPoi == null || selectedFeature == null)
return;
String typeField = selectedPoi.type;
AnnotationPoint selectedPOI = featureToAnnotationPoint(selectedFeature);
double lat = selectedPOI.coordinates[1];
double lon = selectedPOI.coordinates[0];
Marker marker = mapboxMap.addMarker(new MarkerOptions()
.position(new LatLng(lat, lon))
.title(typeField));
marker.showInfoWindow(mapboxMap, mapView);
}
private void removeMarkers(MapboxMap mapboxMap) {
List<Marker> markers = mapboxMap.getMarkers();
for (Marker marker : markers) {
mapboxMap.removeMarker(marker);
}
}
private void setInitialCamera() {
CameraPosition cameraPosition = new CameraPosition.Builder()
.target(new LatLng(49.867630660511715, 10.89075028896332)) // Sets the new camera position
.zoom(18) // Sets the zoom
.bearing(0) // Rotate the camera
.tilt(0) // Set the camera tilt
.build();
this.mapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
}
public void loadPOIs() {
String geojsonbaseURL = "https://tiles.infsoft.com/api/geoobj/json/";
String icid = "/en/";
String revision = "0";
String urlString = geojsonbaseURL + API_KEY + icid + revision;
String poiGeoJson = null;
try {
RestCall restCall = new RestCall();
poiGeoJson = restCall.execute(urlString).get();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
pois = objectMapper.readValue(poiGeoJson, new TypeReference<ArrayList<PoiGeoJsonObject>>() {
});
} catch (IOException e) {
e.printStackTrace();
}
}
private AnnotationPoint featureToAnnotationPoint(Feature feature) {
Geometry geometry = feature.geometry();
AnnotationPoint annotationPoint = null;
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
try {
annotationPoint = objectMapper.readValue(geometry.toJson(), AnnotationPoint.class);
} catch (IOException e) {
e.printStackTrace();
return null;
}
return annotationPoint;
}
@Override
public void onStart() {
super.onStart();
if (mapView != null) {
mapView.onStart();
}
}
@Override
public void onResume() {
super.onResume();
if (mapView != null) {
mapView.onResume();
}
}
@Override
public void onPause() {
super.onPause();
if (mapView != null) {
mapView.onPause();
}
}
@Override
public void onStop() {
super.onStop();
if (mapView != null) {
mapView.onStop();
}
}
@Override
public void onLowMemory() {
super.onDestroy();
if (mapView != null) {
mapView.onLowMemory();
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (mapView != null) {
mapView.onDestroy();
}
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
if (mapView != null) {
mapView.onSaveInstanceState(outState);
}
}
}
AnnotationPoint
package com.infsoft.android.locaware.selectpoi;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
@JsonIgnoreProperties(ignoreUnknown = true)
class AnnotationPoint {
// longitued/latitude, order is reversed!
@JsonProperty("coordinates")
public double[] coordinates;
}
PoiGeoJsonObject
package com.infsoft.android.locaware.selectpoi;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
import java.util.HashMap;
@JsonIgnoreProperties(ignoreUnknown = true)
public class PoiGeoJsonObject implements Serializable {
@JsonProperty("uid")
public String uid;
@JsonProperty("type")
public String type;
@JsonProperty("props")
public HashMap<String, String> props;
}
RestCall
package com.infsoft.android.locaware.selectpoi;
import android.os.AsyncTask;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
class RestCall extends AsyncTask<String, Void, String> {
@Override
protected String doInBackground(String... strings) {
String urlString = strings[0];
StringBuilder sb = new StringBuilder();
try {
URL url = new URL(urlString);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Accept", "application/poi");
BufferedReader br = new BufferedReader(new InputStreamReader(
(conn.getInputStream())));
String output;
while ((output = br.readLine()) != null) {
sb.append(output);
}
conn.disconnect();
} catch (MalformedURLException e) {
e.printStackTrace();
return null;
} catch (IOException e) {
e.printStackTrace();
return null;
}
return sb.toString();
}
}
LevelSwitch
HTML
DISPLAY MAP
Initialize a map in an HTML element with Mapbox GL JS.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Display a map</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<script src="https://api.mapbox.com/mapbox-gl-js/v1.6.1/mapbox-gl.js"></script>
<link href="https://api.mapbox.com/mapbox-gl-js/v1.6.1/mapbox-gl.css" rel="stylesheet" />
<style>
body {
margin: 0;
padding: 0;
}
#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<script>
// infsoft api key, please replace this with our own api key to display your maps
var apiKey = '8c97d7c6-0c3a-41de-b67a-fb7628efba79';
// generating the style url for map box
var styleURL = 'https://tilesservices.webservices.infsoft.com/api/mapstyle/style/{apiKey}?config=level:{level}|3d:{3d}';
styleURL = styleURL.replace('{apiKey}', apiKey);
styleURL = styleURL.replace('{level}', '0');
styleURL = styleURL.replace('{3d}', 'false');
var map = new mapboxgl.Map({
container: 'map', // container id
style: styleURL, // stylesheet location including infsoft api key
center: [10.89075028896332, 49.867630660511715], // starting position [lng, lat]
zoom: 18 // starting zoom
});
</script>
</body>
</html>
APIKey
DISPLAY MAP 3D
Initialize a 3D map in an HTML element with Mapbox GL JS.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Display a map</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<script src="https://api.mapbox.com/mapbox-gl-js/v1.6.1/mapbox-gl.js"></script>
<link href="https://api.mapbox.com/mapbox-gl-js/v1.6.1/mapbox-gl.css" rel="stylesheet" />
<style>
body {
margin: 0;
padding: 0;
}
#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<script>
// infsoft api key, please replace this with our own api key to display your maps
var apiKey = '8c97d7c6-0c3a-41de-b67a-fb7628efba79';
// generating the style url for map box
var styleURL = 'https://tilesservices.webservices.infsoft.com/api/mapstyle/style/{apiKey}?config=level:{level}|3d:{3d}';
styleURL = styleURL.replace('{apiKey}', apiKey);
styleURL = styleURL.replace('{level}', '0');
styleURL = styleURL.replace('{3d}', 'true');
var map = new mapboxgl.Map({
container: 'map', // container id
style: styleURL, // stylesheet location including infsoft api key
center: [10.89075028896332, 49.867630660511715], // starting position [lng, lat]
zoom: 18, // starting zoom
pitch: 60 // starting pitch
});
</script>
</body>
</html>
APIKey
SWITCH LEVELS
Switch level of a map in an HTML element with Mapbox GL JS.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Display a map</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<script src="https://api.mapbox.com/mapbox-gl-js/v1.6.1/mapbox-gl.js"></script>
<link href="https://api.mapbox.com/mapbox-gl-js/v1.6.1/mapbox-gl.css" rel="stylesheet" />
<style>
body {
margin: 0;
padding: 0;
}
#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
button {
position: absolute;
margin: 20px;
}
</style>
</head>
<body>
<script>
// extension for LocAware SDK
function LocAwareExt(map) {
this.map = map;
// internal helper
this.setLevelPropertyOfFilter = function (filter, level) {
if (!Array.isArray(filter))
return filter;
for (var i = 0; i < filter.length; i++) {
var item = filter[i];
if (Array.isArray(item)) {
filter[i] = this.setLevelPropertyOfFilter(item, level);
continue;
}
if (i === 0)
continue;
var prev = filter[i - 1];
var current = filter[i];
if (typeof prev !== 'string')
continue;
if (prev !== 'level')
continue;
if (typeof current !== 'number')
continue;
filter[i] = level;
}
return filter;
};
// set the floor level of the map
this.setLevel = function (level) {
// get all layers from map box gl
var layers = this.map.getStyle().layers;
// iterate all layers
for (var i = 0; i < layers.length; i++) {
var layer = layers[i];
// layer is not a specific level layer
if (!layer.id.includes('locls'))
continue;
// get the filter of the layer from map box gl
var filter = this.map.getFilter(layer.id);
filter = this.setLevelPropertyOfFilter(filter, level);
this.map.setFilter(layer.id, filter);
}
};
}
</script>
<div id="map"></div>
<button id="switchlevel">SWITCH LEVEL</button>
<script>
// infsoft api key, please replace this with our own api key to display your maps
var apiKey = '8c97d7c6-0c3a-41de-b67a-fb7628efba79';
// generating the style url for map box
var styleURL = 'https://tilesservices.webservices.infsoft.com/api/mapstyle/style/{apiKey}?config=level:{level}|3d:{3d}';
styleURL = styleURL.replace('{apiKey}', apiKey);
styleURL = styleURL.replace('{level}', '0');
styleURL = styleURL.replace('{3d}', 'false');
var map = new mapboxgl.Map({
container: 'map', // container id
style: styleURL, // stylesheet location including infsoft api key
center: [10.89075028896332, 49.867630660511715], // starting position [lng, lat]
zoom: 18 // starting zoom
});
// add click event for button 'SWITCH LEVEL'
var currentLevel = 0;
var butSwitchLevel = document.getElementById('switchlevel');
butSwitchLevel.addEventListener('click', function () {
// apply new level
currentLevel = (currentLevel + 1) % 4;
// apply it to map via helper class
var helper = new LocAwareExt(map);
helper.setLevel(currentLevel);
});
</script>
</body>
</html>
APIKey
DISPLAY ROUTE
Display route in an HTML element with Mapbox GL JS.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Display a map</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<script src="https://api.mapbox.com/mapbox-gl-js/v1.6.1/mapbox-gl.js"></script>
<link href="https://api.mapbox.com/mapbox-gl-js/v1.6.1/mapbox-gl.css" rel="stylesheet" />
<style>
body {
margin: 0;
padding: 0;
}
#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
button {
position: absolute;
margin: 20px;
}
</style>
</head>
<body>
<div id="map"></div>
<button id="but">CALC ROUTE</button>
<script>
// methode to demo calc route and display route in map
function calcRoute(map) {
// calc route | url
var calcRouteURL = 'https://routes.webservices.infsoft.com/API/Calc?apikey={apiKey}&startlat={startlat}&startlon={startlon}&startlevel={startlevel}&endlat={endlat}&endlon={endlon}&endlevel={endlevel}&lcid=EN&context=';
// set correct api key
calcRouteURL = calcRouteURL.replace('{apiKey}', apiKey);
// set start position
calcRouteURL = calcRouteURL.replace('{startlat}', 49.86739);
calcRouteURL = calcRouteURL.replace('{startlon}', 10.89190);
calcRouteURL = calcRouteURL.replace('{startlevel}', 0);
// set destination position
calcRouteURL = calcRouteURL.replace('{endlat}', 49.86701);
calcRouteURL = calcRouteURL.replace('{endlon}', 10.89054);
calcRouteURL = calcRouteURL.replace('{endlevel}', 0);
// cal rest api to get route
fetch(calcRouteURL).then(res => res.json()).then((out) => {
// our calced route
var calcedRoute = out[0];
// we could not calc a route
if (!calcedRoute.Valid) {
console.log('Calc route failed!');
return;
}
// get the geo json for the map
var geoJsonForMap = calcedRoute.GeoJson;
// set the layers data
var source = map.getSource('loc-routes').setData(geoJsonForMap);
}).catch(err => { throw err });
}
// infsoft api key, please replace this with our own api key to display your maps
var apiKey = '8c97d7c6-0c3a-41de-b67a-fb7628efba79';
// generating the style url for map box
var styleURL = 'https://tilesservices.webservices.infsoft.com/api/mapstyle/style/{apiKey}?config=level:{level}|3d:{3d}';
styleURL = styleURL.replace('{apiKey}', apiKey);
styleURL = styleURL.replace('{level}', '0');
styleURL = styleURL.replace('{3d}', 'false');
var map = new mapboxgl.Map({
container: 'map', // container id
style: styleURL, // stylesheet location including infsoft api key
center: [10.89075028896332, 49.867630660511715], // starting position [lng, lat]
zoom: 18 // starting zoom
});
// add click event for button
var but = document.getElementById('but');
but.addEventListener('click', function () {
// calc a route
calcRoute(map);
});
</script>
</body>
</html>