At eeGeo, we are building tools that we believe will enable application developers to deliver more engaging user experiences using location. By analysis of user behaviour in our open-source Recce app, we have determined empirically that initial engagement is increased by beautiful and responsive maps. The eeGeo 3D map data is generated from a variety of GIS data sources using procedural methods. With an eeGeo 3D map, an application can present rich dynamic data visualisation, weather, and time-of-day effects. The eeGeo SDK library enables applications to stream and display beautiful 3D maps.
The eeGeo SDK is currently available for iOS, Android, and OSX. We also have experimental support for WebGL, Oculus VR, and Windows. The SDK is implemented as a C++ library using OpenGL ES, which supports both fast performance and portability, across a wide range of platforms and a diverse collection of devices. As a developer building an app using eeGeo 3D maps C++ SDK, the primary advantage is the ability to deploy to multiple platforms while only having to write application code once (increasing the potential number of users), and reducing code duplication and maintenance costs.
Recce demonstrates to developers how to use the eeGeo SDK C++ API for write-once portability and fast performance
We recognise that portability is not important for all developers, and integrating a C++ library can sometimes be onerous for existing applications. To best support developers in this situation, we offer platform specific APIs to easily integrate with the C++ SDK. We recently announced our open source Objective-C iOS API for general use; see the example app to quickly get up and running. However, for developers who are interested in using C++ (to gain the “write-once” portability advantages, or to use the SDK APIs directly), we present the open-source Recce application as a reference implementation to help inform development.
The technical implementation of the Recce app is the topic of the remainder of this blog post. Firstly, we’ll see how to quickly and easily get up and running using Recce as a basis for a project. Then, we’ll examine the overall architecture for sharing application code between platforms while allowing each platform to use familiar native UI controls, with safe and unobtrusive concurrency patterns for platforms which require a dedicated UI thread (such as Android). We’ll take a look at a concrete domain within the Recce app (GPS/Compass), and how the app interacts with the SDK and cross platform view in the context of this feature.
The eeGeo app “Recce” is available for general use as a mapping and business search app in the Apple App Store and Google Play Store.
The open source project is available on GitHub under the generic name “mobile-example-app”. The readme file contains detailed instructions for how to get up and running for iOS and Android. At a high level, the main steps are:
At eeGeo, we use Xcode as our iOS development environment, and Eclipse with the Android Development Tools (ADT) and the Native Development Kit (NDK) as our Android development environment. Xcode is straightforward to set up, Eclipse/ADT/NDK is slightly more involved, and is the topic of another blog post.
After setting up a development environment and cloning the GitHub repo, you’ll need to run the update.platform.sh script from within the repo root. This script gets the eeGeo SDK headers and binaries, which the mobile-example-app client code makes use of. The eeGeo SDK is continuously deployed (typically many times daily). The parameters to this script specify the platform you are currently building for, and if a C++ 11 compatible application binary interface (ABI) is required. The eeGeo SDK libraries are offered as both C++ 03 and C++ 11 compatible ABIs.
Like the eeGeo SDK itself, the Recce app source has a modular structure. The repository root contains a cross platform source directory ‘src’, as well as platform specific source directories ‘ios’ and ‘android’. Source for a given application domain resides in a namespace, typically with a module class to initialise the domain services, data repositories, event handlers, etc.
Within a module, types are organised into “SdkModel” and “View” namespaces. These represent a logical separation between the eeGeo SDK APIs, and the view code for presentation of UI. A convention forbids any direct communication between SdkModel and View namespaces; communication must be orchestrated via messages and messages-handlers.
This structure is useful on all platforms, as it helps keep unrelated concerns separated and reduces coupling. However, it is particularly useful on Android, as the official guidelines for 3D rendering on Android devices recommend running the GL renderer on a background thread, which introduces concurrency concerns. Orchestrating communication with event handlers allows the use of a thread-safe message bus to dispatch events, for a simple concurrency model. On platforms such as iOS, where it is practically possible to run both the UI and GL rendering in the same thread, events are dispatched instantly to remove the bus overhead.
While eeGeo use the message bus patterns for handling concurrency concerns in a safe, simple and portable manner, other schemes are possible. See the concurrency documentation for more information.
The types in the “SdkModel” namespace of a given domain may only directly communicate with the eeGeo SDK types, or with other application types in the SdkModel namespaces. All rendering and SDK state updates (such as scene contents, camera, native IO handlers) should occur on the SdkModel thread.
The “View” namespace contains the cross platform view representation for a domain. Typically this is structured into a ViewModel, a Controller, and an abstract View interface. The ViewModel represents the state of a view in a platform agnostic type (which can be queried or manipulated by other view types). The View presents a common abstract interface which can be implemented by a platform specific view-binding (i.e., an Objective-C iOS view or an Android Java class). Finally, the Controller listens for messages on the message bus, and binds event-handlers to manipulate the view model and update the state of the view.
For a concrete example of how a Controller binds together the various event sources, see the CompassController implementation. This type is responsible for listening to events on the UI context generated by the SdkModel GPS observer; this includes location and heading updates, as well as responding to permission changes. Events generated by the manipulation of the View and ViewModel are propagated in the Controller, dispatching to both the View (via direct calls) and SdkModel context (via the message bus). The implementation of this type represents the general pattern for cross platform, concurrency-safe event handling.
The binding to concrete platform specific view classes is performed in the type that implements the abstract view interface. Sticking with the Compass domain as an example, the ICompassView is the interface. This is implemented by ios/ios_src/Compass/View/CompassViewInterop for iOS, and android/jni/Compass/View/CompassView for Android. These bridging types perform the binding to an Objective-C view and a Java view (via the JNI) respectively. This pattern allows most of the view logic and event binding to remain in cross platform code, with only the thin native view layer implemented in platform-specific code; conversely, by using platform-specific view code, idiomatic UI for the particular platform is available.
Other App Features
We’ve taken a detailed look at the structure of the app by focussing on the Compass feature. The structure and patterns discussed are applied generally across all features of the app. In addition to the Compass, which allows the user to interact with the GPS sensor of the device (including setting the camera behaviour to follow and/or orient to the user direction), the Recce app demonstrates:
- A tactile menu based user experience for manipulating the application
- Category and free-text point of interest search and display using an off the shelf third party search service
- In world display for points of interest using both 2D native UI widgets and 3D pins
- Dynamic weather and season control
- Environment flattening to enter “map-mode”
- Animated camera transitioning between in-world locations
- Background preloading of environment resources
- A model for implementing an initial user experience
Using eeGeo 3D Maps
We hope that the open source Recce code enables more developers to use the eeGeo SDK to build great apps on top of beautiful 3D maps. The Recce code is an example of how a rich mobile application might use the eeGeo SDK with a robust, cross-platform architecture; however, the SDK is a standard C++ library, and as such can be integrated into other projects and workflows.
At eeGeo, we’re experimenting with both delivering a great 3D web-based mapping experience with WebGL using Emscripten, and VR mapping experiences using Oculus Rift. Both of these projects make use of the unmodified SDK, with only the client entry points changed to integrate with the Emscripten framework and HTML5 bindings, and the Oculus Rift development kit respectively.
To get started with the SDK with a thinner application than Recce, check out the mobile-SDK-harness repository. This application demonstrates specific narrow examples of SDK usage with a thin entry point client. eeGeo also offer platform specific bindings for iOS, which wraps the SDK in an Objective-C API, distributed via CocoaPods. The SDK offers additional features which are not used in the Recce app, such as data visualisation, environment collision and feature picking, route visualisation, particle effects, and more.
We are currently building more useful mapping features which will be exposed in the SDK, including interior mapping and tours. We’d love to hear feedback on the SDK, or suggestions and requests for new features.