void TransitionToWorldPointStage::Update(float dt)
            {
                if(m_jumpIfFar && ShouldJumpTo(m_endTransitionInterestPointEcef))
                {
                    m_transitionTime = m_transitionDuration;

                    // Calling SetView here instead of just doing an early-out so that anything that depends on the camera's position will work
                    // (such as mapscene startup searches) [MPLY-8855]
                    Eegeo::v3 endHeadingVector = ComputeHeadingVector(m_endTransitionInterestPointEcef, m_endInterestHeading);
                    Eegeo::Space::EcefTangentBasis newInterestBasis(m_endTransitionInterestPointEcef, endHeadingVector);
                    m_gpsGlobeCameraController.SetView(newInterestBasis, m_endInterestDistance);
                    return;
                }
                
                m_transitionTime += dt;
                float transitionParam = Eegeo::Math::SmoothStep(0.f, 1.f, m_transitionTime / m_transitionDuration);
                
                float interpolatedDistance = Eegeo::Math::Lerp(m_startInterestDistance, m_endInterestDistance, transitionParam);
                Eegeo::dv3 interpolatedInterestPosition = Eegeo::dv3::Lerp(m_startTransitionInterestPointEcef, m_endTransitionInterestPointEcef, transitionParam);
                float currentAssumedAltitude = 0;
                
                m_terrainHeightProvider.TryGetHeight(interpolatedInterestPosition, 0, currentAssumedAltitude);
                
                if(interpolatedInterestPosition.Length() < Eegeo::Space::EarthConstants::Radius + currentAssumedAltitude)
                {
                    interpolatedInterestPosition = interpolatedInterestPosition.Norm() * (Eegeo::Space::EarthConstants::Radius + currentAssumedAltitude);
                }
                
                float interpolatedHeading = Eegeo::Math::Lerp<float>(m_startInterestHeading, m_endInterestHeading, transitionParam);
                Eegeo::v3 interpolatedHeadingVector = ComputeHeadingVector(interpolatedInterestPosition, interpolatedHeading);
                
                Eegeo::Space::EcefTangentBasis newInterestBasis(interpolatedInterestPosition, interpolatedHeadingVector);
                m_gpsGlobeCameraController.SetView(newInterestBasis, interpolatedDistance);
            }
            void CameraTransitionController::Update(float dt)
            {
                if(!IsTransitioning())
                {
                    return;
                }

                m_transitionTime += dt;
                float transitionParam = Eegeo::Math::SmoothStep(0.f, 1.f, m_transitionTime / m_transitionDuration);

                float interpolatedDistance = Eegeo::Math::Lerp(m_startInterestDistance, m_endInterestDistance, transitionParam);
                Eegeo::dv3 interpolatedInterestPosition = Eegeo::dv3::Lerp(m_startTransitionInterestPointEcef, m_endTransitionInterestPointEcef, transitionParam);
                float currentAssumedAltitude = 0;

                m_terrainHeightProvider.TryGetHeight(interpolatedInterestPosition, 0, currentAssumedAltitude);

                if(interpolatedInterestPosition.Length() < Eegeo::Space::EarthConstants::Radius + currentAssumedAltitude)
                {
                    interpolatedInterestPosition = interpolatedInterestPosition.Norm() * (Eegeo::Space::EarthConstants::Radius + currentAssumedAltitude);
                }

                float interpolatedHeading = Eegeo::Math::Lerp<float>(m_startInterestHeading, m_endInterestHeading, transitionParam);
                Eegeo::v3 interpolatedHeadingVector = ComputeHeadingVector(interpolatedInterestPosition, interpolatedHeading);

                Eegeo::Space::EcefTangentBasis newInterestBasis(interpolatedInterestPosition, interpolatedHeadingVector);
                m_cameraController.SetView(newInterestBasis, interpolatedDistance);

                if(transitionParam >= 1.f)
                {
                    StopCurrentTransition();
                }
            }
            bool GpsMarkerModel::UpdateGpsPosition(float dt)
            {
                Eegeo::Space::LatLong locationServiceLatLong(0,0);
                if(!m_locationService.GetLocation(locationServiceLatLong))
                {
                    return false;
                }

                float terrainHeight = 0.0f;
                Eegeo::dv3 ecefPositionFlat = locationServiceLatLong.ToECEF();

                if(m_locationService.IsIndoors())
                {
                    double altitude;
                    m_locationService.GetAltitude(altitude);
                    terrainHeight = static_cast<float>(altitude);
                }

                Eegeo::dv3 newLocationEcef = ecefPositionFlat + (ecefPositionFlat.Norm() * terrainHeight);

                float halfLife = 0.25f;
                float jumpThreshold = 50.0f;

                if(m_currentLocationEcef.SquareDistanceTo(newLocationEcef) < jumpThreshold * jumpThreshold)
                {
                    m_currentLocationEcef = m_currentLocationEcef.Norm() * newLocationEcef.Length();
                    m_currentLocationEcef = Eegeo::Helpers::MathsHelpers::ExpMoveTowards(m_currentLocationEcef, newLocationEcef, halfLife, dt, 0.01f);
                }
                else
                {
                    m_currentLocationEcef = newLocationEcef;
                }

                const Eegeo::Space::LatLong& coord = Eegeo::Space::LatLong::FromECEF(m_currentLocationEcef);
                m_blueSphereModel.SetCoordinate(coord);

                const Eegeo::Resources::Interiors::InteriorId& locationServiceInteriorId = m_locationService.GetInteriorId();

                int indoorMapFloorId = 0;
                if (TryGetLocationServiceIndoorMapFloorId(locationServiceInteriorId, indoorMapFloorId))
                {
                    m_blueSphereModel.SetIndoorMap(locationServiceInteriorId.Value(), indoorMapFloorId);
                }
                else
                {
                    m_blueSphereModel.SetIndoorMap(std::string(), 0);
                }

                if(!m_locationService.GetHorizontalAccuracy(m_currentAccuracyMeters))
                {
                    m_currentAccuracyMeters = 0.0f;
                }

                // return true regardless of whether or not we successfully found an indoorMapFloorId
                return true;
            }
                bool PoiRingTouchController::PerformRayPick(const Eegeo::dv3 &rayOrigin,
                                                            Eegeo::dv3 &rayDirection,
                                                            Eegeo::dv3 &out_rayIntersectionPoint,
                                                            double &out_intersectionParam,
                                                            float &out_terrainHeight,
                                                            float &out_heightAboveTerrain)
                {
                    bool rayPick = false;

                    if(m_appModeModel.GetAppMode() == AppModes::SdkModel::InteriorMode && m_interiorTransitionModel.InteriorIsVisible())
                    {
                        const Eegeo::Resources::Interiors::InteriorsModel* interiorsModel = m_interiorInteractionModel.GetInteriorModel();
                        
                        Eegeo_ASSERT(interiorsModel, "Couldn't get current interiorsModel");
                        
                        const Eegeo::dv3 originNormal = interiorsModel->GetTangentBasis().GetUp();
                        
                        const int selectedFloorIndex = m_interiorInteractionModel.GetSelectedFloorIndex();

                        float floorHeightAboveSeaLevel = Helpers::InteriorHeightHelpers::GetFloorHeightAboveSeaLevel(*interiorsModel, selectedFloorIndex);
                        
                        const Eegeo::dv3 point = originNormal * (floorHeightAboveSeaLevel + Eegeo::Space::EarthConstants::Radius);
                        
                        out_terrainHeight = interiorsModel->GetTangentSpaceBounds().GetMin().y;
                        out_heightAboveTerrain = floorHeightAboveSeaLevel - out_terrainHeight;
                        rayPick = Eegeo::Geometry::IntersectionTests::RayIntersectsWithPlane(rayOrigin, rayDirection, originNormal, point, out_intersectionParam, out_rayIntersectionPoint);
                    }
                    else
                    {
                        rayPick = m_rayPicker.TryGetRayIntersection(rayOrigin, rayDirection, out_rayIntersectionPoint, out_intersectionParam);
                        if(rayPick)
                        {
                            out_terrainHeight = static_cast<float>(out_rayIntersectionPoint.Length() - Eegeo::Space::EarthConstants::Radius);
                            out_heightAboveTerrain = 0.0f;
                        }
                    }
                    if(!rayPick)
                    {
                        rayPick = Eegeo::Geometry::IntersectionTests::GetRayEarthSphereIntersection(rayOrigin, rayDirection, out_rayIntersectionPoint, Eegeo::Space::EarthConstants::RadiusSquared);
                        if(rayPick)
                        {
                            out_terrainHeight = 0.0f;
                            out_heightAboveTerrain = 0.0f;
                            out_intersectionParam = (out_rayIntersectionPoint - rayOrigin).Length();
                        }
                    }

                    return rayPick;
                }