locationmanager/locationtrail/src/clocationrecord.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Fri, 19 Feb 2010 23:14:48 +0200
branchRCL_3
changeset 3 6752808b2036
parent 1 acef663c1218
child 6 646a02f170b9
permissions -rw-r--r--
Revision: 201003 Kit: 201007

/*
* Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies). 
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:  A class for recording and storing locations.
*
*/

#include <e32cmn.h> 
#include <lbserrors.h>
#include <lbssatellite.h>

#include "rlocationtrail.h"
#include "clocationrecord.h"
#include "cnetworkinfo.h"
#include "locationmanagerdebug.h"
#include "locationtraildefs.h"
#include "locationtrailpskeys.h"
#include "mdeconstants.h"
#include <centralrepository.h>


using namespace MdeConstants;

// --------------------------------------------------------------------------
// CLocationRecord::NewL
// --------------------------------------------------------------------------
//
EXPORT_C CLocationRecord* CLocationRecord::NewL()
    {
    CLocationRecord* self = new (ELeave) CLocationRecord();
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop( self );
    return self;
    }
        
// --------------------------------------------------------------------------
// CLocationRecord::CLocationRecord
// --------------------------------------------------------------------------
//  
CLocationRecord::CLocationRecord()
    : iNetworkInfoTimer( NULL ),
    iState( RLocationTrail::ETrailStopped ),
    iTrailCaptureSetting( RLocationTrail::ECaptureAll ),
    iLocationCounter( 0 ),
    iRequestCurrentLoc( EFalse ),
    iTrailStarted( EFalse ),
    iLastGPSFixState( EFalse ),
    iLastLocationId( 0 )
    {
    iMaxTrailSize = KMaxTrailLength / KUpdateInterval;
    }

// --------------------------------------------------------------------------
// CLocationRecord::ConstructL
// --------------------------------------------------------------------------
//    
void CLocationRecord::ConstructL()
    {
    const TInt KMillion = 1000000;
    TInt err = iProperty.Define( KPSUidLocationTrail, KLocationTrailState, RProperty::EInt );
    if ( err != KErrNone && err != KErrAlreadyExists )
        {
        User::Leave( err );
        }
    User::LeaveIfError( iProperty.Set( KPSUidLocationTrail,
        KLocationTrailState, (TInt) RLocationTrail::ETrailStopped ) ); 

    iNetworkInfo = CNetworkInfo::NewL( this );
    iPositionInfo = CPositionInfo::NewL( this );
	iRemapper = CLocationRemappingAO::NewL();
    iNetworkInfoTimer = CPeriodic::NewL( CActive::EPriorityStandard );
    
    TInt interval( 0 );
    TRAP(err, ReadCenRepValueL(KIntervalKey, interval));
    LOG1("CLocationManagerServer::ConstructL, cenrep interval value:%d", interval);
    
    if (interval == 0 || err != KErrNone )
    	{
        LOG1("CLocationManagerServer::ConstructL, cenrep interval err:%d", err);
    	iInterval = KUpdateInterval;
    	}
    else 
    	{
    	iInterval = interval * KMillion;
    	}

    TRAP(err, ReadCenRepValueL(KLocationDeltaKey, iLocationDelta));
    LOG1("CLocationManagerServer::ConstructL, location delta value:%d", iLocationDelta);
    
    if (iLocationDelta == 0)
    	{
        LOG1("CLocationManagerServer::ConstructL, location delta err:%d", err);
        iLocationDelta = KLocationDelta;
    	}

    }
    
// --------------------------------------------------------------------------
// CLocationRecord::~CLocationRecord
// --------------------------------------------------------------------------
//    
CLocationRecord::~CLocationRecord()
    {
    Stop();
    iProperty.Delete( KPSUidLocationTrail, KLocationTrailState );
    iProperty.Close();
    iTrail.Close();
    
    delete iNetworkInfo;
    delete iPositionInfo;
    delete iNetworkInfoTimer;
	if (iRemapper)
		{
		iRemapper->StopRemapping();
		delete iRemapper;
		}
    }

// --------------------------------------------------------------------------
// CLocationRecord::CurrentState
// --------------------------------------------------------------------------
//
EXPORT_C void CLocationRecord::LocationTrailState( TLocTrailState& aState )
    {
    aState = iState;
    }

// --------------------------------------------------------------------------
// CLocationRecord::StartL
// --------------------------------------------------------------------------
//
EXPORT_C void CLocationRecord::StartL( RLocationTrail::TTrailCaptureSetting aCaptureSetting )
    {
    LOG( "CLocationRecord::StartL(), begin" );
    iTrailCaptureSetting = aCaptureSetting;
    if ( aCaptureSetting == RLocationTrail::ECaptureAll && !iPositionInfo->IsActive() )
        {
        if( iState == RLocationTrail::ETrailStopped  )
            {
            iTrail.Reset();
            }
        iPositionInfo->StartL( aCaptureSetting, iInterval );
        }
    else if ( aCaptureSetting == RLocationTrail::ECaptureNetworkInfo )
    	{
    	// Update and store network info in location trail immediately.
    	// Timer will trigger the update again later.
    	UpdateNetworkInfo( this );
    	
        if ( iNetworkInfoTimer && iNetworkInfoTimer->IsActive() )
        	{
        	iNetworkInfoTimer->Cancel();
        	}
        	
        StartTimerL();
    	}
    
    iLastLocationId = 0;
    
    SetCurrentState( RLocationTrail::ETrailStarting );
    
    iTrailStarted = ETrue;
    LOG( "CLocationRecord::StartL(), end" );
    }

// --------------------------------------------------------------------------
// CLocationRecord::Stop
// --------------------------------------------------------------------------
//
EXPORT_C void CLocationRecord::Stop()
    {
    LOG( "CLocationRecord::StopL(), begin" );
    iPositionInfo->Stop();
    iTrailStarted = EFalse;
    
    if ( iNetworkInfoTimer && iNetworkInfoTimer->IsActive() )
    	{
    	iNetworkInfoTimer->Cancel();
    	}

    if ( iRemapper )
    	{
    	iRemapper->ResetQueue();
    	}
    SetCurrentState( RLocationTrail::ETrailStopped );
    LOG( "CLocationRecord::StopL(), end" );
    }

// --------------------------------------------------------------------------
// CLocationRecord::SetStateToStop
// --------------------------------------------------------------------------
//
EXPORT_C void CLocationRecord::SetStateToStopping()
	{
	SetCurrentState( RLocationTrail::ETrailStopping );
	}

// --------------------------------------------------------------------------
// CLocationRecord::GetLocationByTimeL
// --------------------------------------------------------------------------
//
EXPORT_C void CLocationRecord::GetLocationByTimeL( const TTime aTime, 
												   TLocationData& aLocationData,
                                                   TLocTrailState& aState )
    {
    LOG( "CLocationRecord::GetLocationByTimeL(), begin" );
    TInt posFound( EFalse );

#ifdef _DEBUG
    _LIT( KDateTimeFormat, "%Y/%M/%D %H:%T:%S" );
    const TInt DateTimeStrMaxLength = 20;
    LOG1( "CLocationRecord::GetLocationByTimeL - aTime: %Ld", aTime.Int64() );
    TBuf<DateTimeStrMaxLength> str1;
    aTime.FormatL( str1, KDateTimeFormat );
    LOG1( "CLocationRecord::GetLocationByTimeL - aTime: %S", &str1 );
#endif

    TTimeIntervalSeconds interval;
    TTimeIntervalSeconds nextInterval;
    for ( TInt i(iTrail.Count()-1) ; i >= 0 && !posFound ; i-- )
        {
        TInt err = iTrail[i].iTimeStamp.SecondsFrom( aTime, interval );
        
        TInt timeDiff = Abs( interval.Int() );

#ifdef _DEBUG
        LOG1( "CLocationRecord::GetLocationByTimeL - Trail timestamp: %Ld", iTrail[i].iTimeStamp.Int64() );
        TBuf<DateTimeStrMaxLength> str;
        iTrail[i].iTimeStamp.FormatL( str, KDateTimeFormat );
        LOG1( "CLocationRecord::GetLocationByTimeL - Trail timestamp: %S", &str );
        LOG1( "CLocationRecord::GetLocationByTimeL - timeDiff: %d", timeDiff );
#endif

        if ( err == KErrNone && timeDiff <= KIntervalSeconds )
            {
            // The nearest time is in iTrail[i] or in iTrail[i-1].
            if ( i > 0 )
                {
                iTrail[i-1].iTimeStamp.SecondsFrom( aTime, nextInterval );
                
                TInt nextDiff = Abs( nextInterval.Int() );
                    
                if ( nextDiff < timeDiff )
                    {
                    aLocationData = iTrail[i-1].iLocationData;
                    aState = iTrail[i-1].iTrailState;
                    }
                else
                    {
                    aLocationData = iTrail[i].iLocationData;
                    aState = iTrail[i].iTrailState;
                    }                    
                }            
            else
                {
                aLocationData = iTrail[i].iLocationData;
                aState = iTrail[i].iTrailState;
                }
            posFound = ETrue;
            }
        }
    if ( !posFound )
        {
        User::Leave( KErrNotFound );
        }
    LOG( "CLocationRecord::GetLocationByTimeL(), end" );
    }
    
// --------------------------------------------------------------------------
// CLocationRecord::RequestLocationL
// --------------------------------------------------------------------------
//
EXPORT_C void CLocationRecord::RequestLocationL()
    {
    iRequestCurrentLoc = ETrue;
    if ( iTrailCaptureSetting != RLocationTrail::ECaptureNetworkInfo &&
    	!iPositionInfo->IsActive() )
        {
        iPositionInfo->StartL( iTrailCaptureSetting, iInterval );
        }
    else if ( iTrailCaptureSetting == RLocationTrail::ECaptureNetworkInfo )
    	{
    	TPositionSatelliteInfo posInfo;
    	CTelephony::TNetworkInfoV1 network = CTelephony::TNetworkInfoV1();
    	GetNetworkInfo( network );
       	iObserver->CurrentLocation( posInfo, network, KErrNone );
        iRequestCurrentLoc = EFalse;
    	}
    }

// --------------------------------------------------------------------------
// CLocationRecord::CancelLocationRequest
// --------------------------------------------------------------------------
//    
EXPORT_C void CLocationRecord::CancelLocationRequest()
    {
    iRequestCurrentLoc = EFalse;
    if ( !iTrailStarted )
        {
        iPositionInfo->Stop();
        }
    }
        
        
// --------------------------------------------------------------------------
// CLocationRecord::GetNetworkInfo
// --------------------------------------------------------------------------
//
EXPORT_C void CLocationRecord::GetNetworkInfo( CTelephony::TNetworkInfoV1& aNetworkInfo )
    {
    LOG("CLocationRecord::GetNetworkInfo");

    aNetworkInfo = iNetwork;
    }
    
// --------------------------------------------------------------------------
// CLocationRecord::SetObserver
// --------------------------------------------------------------------------
//
EXPORT_C void CLocationRecord::SetObserver( MLocationTrailObserver* aObserver)
    {
    iObserver = aObserver;
    }

// --------------------------------------------------------------------------
// CLocationRecord::SetAddObserver
// --------------------------------------------------------------------------
//
EXPORT_C void CLocationRecord::SetAddObserver( MLocationAddObserver* aObserver)
    {
    iAddObserver = aObserver;
    }

// --------------------------------------------------------------------------
// From MNetworkInfoObserver.
// CLocationRecord::Position
// --------------------------------------------------------------------------
//    
void CLocationRecord::Position( const TPositionInfo& aPositionInfo,
                                const TInt aError  )
    {    
    const TPositionSatelliteInfo& positionSatelliteInfo = 
    	static_cast<const TPositionSatelliteInfo&>(aPositionInfo);

    if ( iRequestCurrentLoc )
        {
        HandleLocationRequest( positionSatelliteInfo, aError );
        }
    if( iState == RLocationTrail::ETrailStopped )
    	{
    	LOG("CLocationRecord::Position - trail stopped");
    	return;
    	}
    
    if ( !iTrailStarted )
        {
        iPositionInfo->NextPosition();
        return;
        }
    switch ( aError )
        {
        case KPositionPartialUpdate: // fall through
        case KPositionQualityLoss: 
            {
            // Location is stored, even if it may not be valid.
            StoreLocation( positionSatelliteInfo ); 
            LOG("CLocationRecord::Position - partial update");
            if ( iState != RLocationTrail::EWaitingGPSData && 
            	 iState != RLocationTrail::ETrailStopping ) 
                {
                SetCurrentState( RLocationTrail::EWaitingGPSData );
            	LOG("CLocationRecord::Position trail waiting for gps");
                }
            break;
            }
        case KErrNone:
            {
            StoreLocation( positionSatelliteInfo );
            LOG("CLocationRecord::Position - good GPS coordinates");
            if ( iState != RLocationTrail::ETrailStarted ) 
                {
                if ( iRemapper )
                	{
                	LOG("CLocationRecord::Position start remapping");
                	iLastLocationId = 0;
                	TBool createLocation = iRemapper->CheckQueue();
                	if( createLocation )
                		{
                		TRAP_IGNORE(	
                		TItemId locationId = DoCreateLocationL( iNewItem.iLocationData );
                		iRemapper->UpdateRelationsL( locationId );
                		)
                		}
               		iRemapper->StartRemappingObjects( iNewItem.iLocationData );
                	}
                if ( iState != RLocationTrail::ETrailStopping )
                	{
                    SetCurrentState( RLocationTrail::ETrailStarted );
                	LOG("CLocationRecord::Position trail started");
                	}
                }
            break;
            }
        default:
            {
            StoreLocation( positionSatelliteInfo );
            LOG1("CLocationRecord::Position - searching GPS, aError %d", aError );
            if ( iState != RLocationTrail::ESearchingGPS &&
               	 iState != RLocationTrail::ETrailStopping ) 
                {
                SetCurrentState( RLocationTrail::ESearchingGPS );
            	LOG("CLocationRecord::Position trail searching gps");
                }
            break;
            }      
        }
    TBool fixState = CheckGPSFix( positionSatelliteInfo );
    LOG1( "CLocationRecord::Position fixState %d", fixState );
    LOG1( "CLocationRecord::Position iLastGPSFixState %d", iLastGPSFixState );
    
    if ( iObserver && iLastGPSFixState != fixState )
    	{
    	LOG("CLocationRecord::Position quality changed");
    	iObserver->GPSSignalQualityChanged( positionSatelliteInfo );
    	}
    
   	iLastGPSFixState = fixState;
    
    iPositionInfo->NextPosition();
    }

TBool CLocationRecord::CheckGPSFix( const TPositionSatelliteInfo& aSatelliteInfo )
	{
	TPosition position;
	aSatelliteInfo.GetPosition( position );
	LOG1( "CLocationRecord::CheckGPSFix latitude %f", position.Latitude() );
	LOG1( "CLocationRecord::CheckGPSFix longitude %f", position.Longitude() );
	TBool ret = ( Math::IsNaN(position.Latitude()) || Math::IsNaN(position.Longitude()) ) 
		? EFalse : ETrue;
   	return ret;
	}
    
// --------------------------------------------------------------------------
// From MPositionerObserver.
// CLocationRecord::NetworkInfo
// --------------------------------------------------------------------------
//    
void CLocationRecord::NetworkInfo( const CTelephony::TNetworkInfoV1 &aNetworkInfo, 
		const TInt aError )
    {
    LOG("CLocationRecord::NetworkInfo");
    if ( aError == KErrNone )
        {
        LOG("CLocationRecord::NetworkInfo - KErrNone");
        iNetwork = aNetworkInfo;
        if (iNetwork.iAccess == CTelephony::ENetworkAccessUtran)
        	{
        	iNetwork.iLocationAreaCode = 0;
        	}
        if ( iState == RLocationTrail::ETrailStarting && iTrailStarted )
        	{
        	SetCurrentState( RLocationTrail::ETrailStarted );
        	}
        }
    else
        {
        LOG1("CLocationRecord::NetworkInfo - %d", aError );
        iNetwork = CTelephony::TNetworkInfoV1();
        iNetwork.iAreaKnown = EFalse;
        iNetwork.iAccess = CTelephony::ENetworkAccessUnknown;
        iNetwork.iCellId = 0;
        iNetwork.iLocationAreaCode = 0;
        iNetwork.iCountryCode.Zero();
        iNetwork.iNetworkId.Zero();
        }
    }

// --------------------------------------------------------------------------
// CLocationRecord::StoreLocationL
// --------------------------------------------------------------------------
//    
void CLocationRecord::StoreLocation( const TPositionSatelliteInfo& aSatelliteInfo )
    {
    aSatelliteInfo.GetPosition( iNewItem.iLocationData.iPosition );
    aSatelliteInfo.GetCourse( iNewItem.iLocationData.iCourse );
    iNewItem.iLocationData.iSatellites = aSatelliteInfo.NumSatellitesUsed();
    iNewItem.iLocationData.iQuality = aSatelliteInfo.HorizontalDoP();
    
    // Network info
    GetNetworkInfo( iNewItem.iLocationData.iNetworkInfo );
    // Get Universal time
    iNewItem.iTimeStamp.UniversalTime();
    iNewItem.iTrailState = iState;
    
    TInt error = iTrail.Append( iNewItem );
    
    // If appending an item to the trail fails because of OOM, remove oldest trail items
    // until the new item fits or there's only one item left in the trail.
    while ( error == KErrNoMemory && iTrail.Count() > 1 )
		{
		LOG("CLocationRecord::StoreLocation - Out of memory! Shortening trail!");
		iTrail.Remove( 0 );
		error = iTrail.Append( iNewItem );
		}
    
    if ( iTrail.Count() > iMaxTrailSize )
        {
        iTrail.Remove( 0 );
        }
    
    if( iAddObserver )
    	{
    	iAddObserver->LocationAdded( iNewItem, aSatelliteInfo );
    	}
    }
    
// --------------------------------------------------------------------------
// CLocationRecord::SetCurrentState
// --------------------------------------------------------------------------
//        
void CLocationRecord::SetCurrentState( TLocTrailState aState )    
    {
    LOG1( "CLocationRecord::SetCurrentState(), begin, state:%d", aState );
    iState = aState;
    iProperty.Set( KPSUidLocationTrail, KLocationTrailState, (TInt) aState );
    if ( iObserver )
        {
        iObserver->LocationTrailStateChange();
        }
    LOG( "CLocationRecord::SetCurrentState(), end" );
    }
    
// --------------------------------------------------------------------------
// CLocationRecord::HandleLocationRequest
// --------------------------------------------------------------------------
//
void CLocationRecord::HandleLocationRequest( const TPositionSatelliteInfo& aSatelliteInfo, 
                                             const TInt aError )    
    {
	CTelephony::TNetworkInfoV1 network = CTelephony::TNetworkInfoV1();
    if ( aError == KErrNone )
        {
       	GetNetworkInfo( network );
        iObserver->CurrentLocation( aSatelliteInfo, network, aError );
        iRequestCurrentLoc = EFalse;
        if ( !iTrailStarted )
            {
            iPositionInfo->Stop();
            }
        }
    else
        {
        iLocationCounter++;
        if ( iLocationCounter > KCurrentLocTimeoutCount )
            {
            iObserver->CurrentLocation( aSatelliteInfo, network, KErrTimedOut );
            iRequestCurrentLoc = EFalse;
            iLocationCounter = 0;
            if ( !iTrailStarted )
                {
                iPositionInfo->Stop();
                }
            }       
        }    
    }

TInt CLocationRecord::UpdateNetworkInfo( TAny* aAny )
	{
	TPositionSatelliteInfo nullPositionInfo;
	CLocationRecord* self = STATIC_CAST( CLocationRecord*, aAny );
	self->StoreLocation( nullPositionInfo );
	return KErrNone;
	}


EXPORT_C void CLocationRecord::CreateLocationObjectL( const TLocationData& aLocationData,
		const TUint& aObjectId )
	{
	TItemId locationId = DoCreateLocationL( aLocationData );
	CreateRelationL( aObjectId, locationId );
	}


EXPORT_C void CLocationRecord::LocationSnapshotL( const TUint& aObjectId )
	{
	LOG("CLocationRecord::LocationSnapshotL");
	
	TBool previousMatch = EFalse;
	CMdENamespaceDef& namespaceDef = iMdeSession->GetDefaultNamespaceDefL();

	// get locationdata from trail with object time
	TTime timestamp = GetMdeObjectTimeL( aObjectId );
	TLocationData locationData;
	TLocTrailState state;
	GetLocationByTimeL( timestamp, locationData, state );
	
	iObjectId = aObjectId;
	iLocationData = locationData;

	// capture only network data
	if ( iTrailCaptureSetting == RLocationTrail::ECaptureNetworkInfo )
		{
		CTelephony::TNetworkInfoV1* net = &locationData.iNetworkInfo;
		
		if ( net->iCellId == 0 && 
				net->iLocationAreaCode == 0 &&
				net->iCountryCode.Length() == 0 &&
				net->iNetworkId.Length() == 0 )
			{
			// nothing to do
			LOG("CLocationRecord::LocationSnapshotL - no network info available");
			}
		else if ( iLastLocationId != 0 )
			{
			CTelephony::TNetworkInfoV1* lastnet = &iLastLocation.iNetworkInfo;
			
			// compare to previous network info
			TItemId locationId = iLastLocationId;
			if ( lastnet->iCellId != net->iCellId ||
					lastnet->iLocationAreaCode != net->iLocationAreaCode ||
					lastnet->iCountryCode != net->iCountryCode ||
					lastnet->iNetworkId != net->iNetworkId )
				{
				LOG("CLocationRecord::LocationSnapshotL - network info changed");
				locationId = DoCreateLocationL( locationData );
				}
			CreateRelationL( aObjectId, locationId );
			}
		else 
			{
			// new location
			TItemId locationId = DoCreateLocationL( locationData );
			CreateRelationL( aObjectId, locationId );
			}
		return;
		}
	
	// coordinates empty (will be remapped)
	if ( Math::IsNaN( locationData.iPosition.Latitude() ) && 
			Math::IsNaN( locationData.iPosition.Longitude() ))
		{
		TRemapItem remapItem;
		remapItem.iObjectId = aObjectId;
		remapItem.iTime = timestamp;
		
		CTelephony::TNetworkInfoV1* net = &locationData.iNetworkInfo;

		// no network info (offline mode + no GPS fix)
		if ( net->iCellId == 0 && 
				net->iLocationAreaCode == 0 &&
				net->iCountryCode.Length() == 0 &&
				net->iNetworkId.Length() == 0 )
			{
			LOG("CLocationRecord::LocationSnapshotL - empty remap item created");
			}
		// check match for last created locationobject
		else if ( iLastLocationId != 0 )
			{
			TItemId locationId;
			CTelephony::TNetworkInfoV1* lastnet = &iLastLocation.iNetworkInfo;

			// networkinfo changed from last location
			if ( lastnet->iCellId != net->iCellId ||
					lastnet->iLocationAreaCode != net->iLocationAreaCode ||
					lastnet->iCountryCode != net->iCountryCode ||
					lastnet->iNetworkId != net->iNetworkId )
				{
				LOG("CLocationRecord::LocationSnapshotL - remap with new network info");
				locationId = DoCreateLocationL( locationData );
				}		
			else
				{
				LOG("CLocationRecord::LocationSnapshotL - remap with previous network info");
				locationId = iLastLocationId;
				}
			TItemId relationId = CreateRelationL( aObjectId, locationId );
			remapItem.iLocationId = locationId;
			remapItem.iRelationId = relationId;
			}
		else
			{
			// new location with only network data
			TItemId locationId = DoCreateLocationL( locationData );
			TItemId relationId = CreateRelationL( aObjectId, locationId );
			remapItem.iLocationId = locationId;
			remapItem.iRelationId = relationId;
			}
		iRemapper->Append( remapItem );
		return;
		}
		
	// valid coordinates found
	if ( iLastLocationId != 0 )
		{
		CTelephony::TNetworkInfoV1* net = &locationData.iNetworkInfo;
		CTelephony::TNetworkInfoV1* lastnet = &iLastLocation.iNetworkInfo;
		
		// first check if networkinfo matches last created location
		if ( lastnet->iCellId == net->iCellId &&
				lastnet->iLocationAreaCode == net->iLocationAreaCode &&
				lastnet->iCountryCode == net->iCountryCode &&
				lastnet->iNetworkId == net->iNetworkId )
			{
			LOG("CLocationRecord::LocationSnapshotL - network info matches");
			
			// if both locations have valid coordinates, calculate distance between points
			if ( !Math::IsNaN( iLastLocation.iPosition.Latitude() ) && 
					!Math::IsNaN( iLastLocation.iPosition.Longitude() ) && 
					!Math::IsNaN( locationData.iPosition.Latitude() ) && 
					!Math::IsNaN( locationData.iPosition.Longitude() ))
				{
				TReal32 distance;
				TInt err = locationData.iPosition.Distance(iLastLocation.iPosition, distance);
				
				if ( distance < iLocationDelta )
					{
					LOG("CLocationRecord::LocationSnapshotL - location close to the previous one");
					previousMatch = ETrue;
					CreateRelationL( aObjectId, iLastLocationId );
					LOG("CLocationRecord::CreateLocationObjectL - last location matched");
					}
				}
			}
		}
	
	// last location did not match, find existing one from DB
	if( !previousMatch )
		{
		LOG("CLocationRecord::LocationSnapshotL - query location");
		const TReal64 KMeterInDegrees = 0.000009;
		const TReal64 KPi = 3.14159265358979;
		const TReal32 K180Degrees = 180.0;
	
		TReal64 latitude = locationData.iPosition.Latitude();
		TReal64 longitude = locationData.iPosition.Longitude();
		// calculate distance in degrees
		TReal64 cosine;
		Math::Cos(cosine, locationData.iPosition.Latitude() * KPi / K180Degrees );
		TReal64 latDelta = iLocationDelta * KMeterInDegrees;
		TReal64 lonDelta = latDelta * cosine;
		
		CMdEObjectDef& locationObjectDef = namespaceDef.GetObjectDefL( Location::KLocationObject );
		
		CMdEPropertyDef& latitudeDef = locationObjectDef.GetPropertyDefL(
				Location::KLatitudeProperty );
		CMdEPropertyDef& longitudeDef = locationObjectDef.GetPropertyDefL(
				Location::KLongitudeProperty );
		CMdEPropertyDef& cellIdDef = locationObjectDef.GetPropertyDefL(
				Location::KCellIdProperty );
		CMdEPropertyDef& locationCodeDef = locationObjectDef.GetPropertyDefL( 
				Location::KLocationAreaCodeProperty );
		CMdEPropertyDef& countryCodeDef = locationObjectDef.GetPropertyDefL( 
				Location::KCountryCodeProperty );
		CMdEPropertyDef& networkCodeDef = locationObjectDef.GetPropertyDefL( 
				Location::KNetworkCodeProperty );
		
		iLocationQuery = iMdeSession->NewObjectQueryL( namespaceDef, locationObjectDef, this );
		CMdELogicCondition& cond = iLocationQuery->Conditions();
		cond.SetOperator( ELogicConditionOperatorAnd );
		
		LOG1( "CLocationRecord::LocationSnapshotL latitude: %f", latitude);
		LOG1( "CLocationRecord::LocationSnapshotL latdelta: %f", latDelta);
		LOG1( "CLocationRecord::LocationSnapshotL longitude: %f", longitude);
		LOG1( "CLocationRecord::LocationSnapshotL londelta: %f", lonDelta);
		
		cond.AddPropertyConditionL( latitudeDef, 
				TMdERealBetween( latitude - latDelta, latitude + latDelta ));
		cond.AddPropertyConditionL( longitudeDef, 
				TMdERealBetween( longitude - lonDelta, longitude + lonDelta ));
		cond.AddPropertyConditionL( cellIdDef, 
				TMdEUintEqual( locationData.iNetworkInfo.iCellId) );
		cond.AddPropertyConditionL( locationCodeDef, 
				TMdEUintEqual( locationData.iNetworkInfo.iLocationAreaCode) );
		cond.AddPropertyConditionL( countryCodeDef, ETextPropertyConditionCompareEquals,
				locationData.iNetworkInfo.iCountryCode );
		cond.AddPropertyConditionL( networkCodeDef, ETextPropertyConditionCompareEquals,
				locationData.iNetworkInfo.iNetworkId );
		
		iLocationQuery->FindL();			
		}
	}

	
TItemId CLocationRecord::DoCreateLocationL( const TLocationData& aLocationData )
	{
	LOG("CLocationRecord::DoCreateLocationL - start");
	TItemId locationObjectId;
	
	CMdENamespaceDef& namespaceDef = iMdeSession->GetDefaultNamespaceDefL();

	CMdEObjectDef& locationObjectDef = namespaceDef.GetObjectDefL( Location::KLocationObject );
	
	// required object properties
	CMdEPropertyDef& creationDef = locationObjectDef.GetPropertyDefL(
			Object::KCreationDateProperty );
	CMdEPropertyDef& modifiedDef = locationObjectDef.GetPropertyDefL(
			Object::KLastModifiedDateProperty );
	CMdEPropertyDef& sizeDef = locationObjectDef.GetPropertyDefL(
			Object::KSizeProperty );
	CMdEPropertyDef& itemTypeDef = locationObjectDef.GetPropertyDefL(
			Object::KItemTypeProperty );
	CMdEPropertyDef& offSetDef = locationObjectDef.GetPropertyDefL( 
			Object::KTimeOffsetProperty );

	// location related properties
	CMdEPropertyDef& cellIdDef = locationObjectDef.GetPropertyDefL(
			Location::KCellIdProperty );
	CMdEPropertyDef& latitudeDef = locationObjectDef.GetPropertyDefL(
			Location::KLatitudeProperty );
	CMdEPropertyDef& longitudeDef = locationObjectDef.GetPropertyDefL(
			Location::KLongitudeProperty );
	CMdEPropertyDef& altitudeDef = locationObjectDef.GetPropertyDefL(
			Location::KAltitudeProperty );

	CMdEPropertyDef& directionDef = locationObjectDef.GetPropertyDefL(
			Location::KDirectionProperty );
	CMdEPropertyDef& speedDef = locationObjectDef.GetPropertyDefL( 
			Location::KSpeedProperty );
	CMdEPropertyDef& locationCodeDef = locationObjectDef.GetPropertyDefL( 
			Location::KLocationAreaCodeProperty );
	CMdEPropertyDef& countryCodeDef = locationObjectDef.GetPropertyDefL( 
			Location::KCountryCodeProperty );
	CMdEPropertyDef& networkCodeDef = locationObjectDef.GetPropertyDefL( 
			Location::KNetworkCodeProperty );
	CMdEPropertyDef& qualityDef = locationObjectDef.GetPropertyDefL( 
			Location::KQualityProperty );

	// location object
	CMdEObject* locationObject = NULL;

	locationObject = iMdeSession->NewObjectL( locationObjectDef, Object::KAutomaticUri );
	CleanupStack::PushL( locationObject );

	TTime timestamp( 0 );
	timestamp.UniversalTime();

	TTimeIntervalSeconds timeOffset = User::UTCOffset();
	TTime localTime = timestamp + timeOffset;
	
	// required object properties
	locationObject->AddTimePropertyL( creationDef, localTime );
	locationObject->AddTimePropertyL( modifiedDef, timestamp );
	locationObject->AddUint32PropertyL( sizeDef, 0 ); // always zero size for location objects
	locationObject->AddTextPropertyL( itemTypeDef, Location::KLocationItemType );
	locationObject->AddInt16PropertyL( offSetDef, timeOffset.Int() / 60 );
	
	LOG1( "CLocationRecord::DoCreateLocationL - location created with stamp: %Ld", timestamp.Int64() );
	
	// location related properties
	if ( !Math::IsNaN( aLocationData.iPosition.Latitude() ) && 
		 !Math::IsNaN( aLocationData.iPosition.Longitude() ))
		{
		locationObject->AddReal64PropertyL( latitudeDef, aLocationData.iPosition.Latitude() );
		locationObject->AddReal64PropertyL( longitudeDef, aLocationData.iPosition.Longitude() );
		}
	if ( !Math::IsNaN( aLocationData.iPosition.Altitude() ) )
		{
		locationObject->AddReal64PropertyL( altitudeDef, aLocationData.iPosition.Altitude() );
		}
	if ( !Math::IsNaN( aLocationData.iCourse.Course() ) )
		{
		locationObject->AddReal32PropertyL( directionDef, aLocationData.iCourse.Course() );
		}
	if ( !Math::IsNaN( aLocationData.iCourse.Speed() ) )
		{
		locationObject->AddReal32PropertyL( speedDef, aLocationData.iCourse.Speed() );
		}
	if ( !Math::IsNaN( aLocationData.iQuality ) )
		{
		locationObject->AddReal32PropertyL( qualityDef, aLocationData.iQuality );
		}

	// network related properties
	if ( aLocationData.iNetworkInfo.iAreaKnown )
		{
		if ( aLocationData.iNetworkInfo.iAccess != CTelephony::ENetworkAccessUnknown )
			{
			locationObject->AddUint32PropertyL( cellIdDef, aLocationData.iNetworkInfo.iCellId );
			
			}
		if ( aLocationData.iNetworkInfo.iLocationAreaCode != 0 &&
			aLocationData.iNetworkInfo.iAccess != CTelephony::ENetworkAccessUnknown )
			{
			locationObject->AddUint32PropertyL( locationCodeDef, 
					aLocationData.iNetworkInfo.iLocationAreaCode );
			
			}
		if ( aLocationData.iNetworkInfo.iCountryCode.Length() > 0 )
			{
			locationObject->AddTextPropertyL( countryCodeDef, 
					aLocationData.iNetworkInfo.iCountryCode );
			
			}
		if ( aLocationData.iNetworkInfo.iNetworkId.Length() > 0 )
			{
			locationObject->AddTextPropertyL(networkCodeDef, aLocationData.iNetworkInfo.iNetworkId);
			
			}
		}

	// Add the location object to the database.
	locationObjectId = iMdeSession->AddObjectL( *locationObject );

	iLastLocationId = locationObjectId;
	iLastLocation = aLocationData;

	CleanupStack::PopAndDestroy( locationObject );
	
	LOG("CLocationRecord::DoCreateLocationL - end");
	
	return locationObjectId;
	}


TItemId CLocationRecord::CreateRelationL( const TUint& aObjectId, const TUint& aLocationId )
	{ 
	LOG("CLocationRecord::CreateRelationL - start");
	CMdENamespaceDef& namespaceDef = iMdeSession->GetDefaultNamespaceDefL();
	
	// "contains" relation definition
	CMdERelationDef& containsRelDef = namespaceDef.GetRelationDefL( 
			Relations::KContainsLocation );

	CMdERelation* relationObject = iMdeSession->NewRelationLC( containsRelDef,
			aObjectId, aLocationId, 0 );
	if ( !relationObject )
		{
		User::Leave( KErrBadHandle );
		}
	TItemId relationId = iMdeSession->AddRelationL( *relationObject );

	CleanupStack::PopAndDestroy( relationObject );
	LOG("CLocationRecord::CreateRelationL - end");
	
	return relationId; 
	}

// --------------------------------------------------------------------------
// CLocationManagerServer::ReadCenRepValueL
// --------------------------------------------------------------------------
//
void CLocationRecord::ReadCenRepValueL(TInt aKey, TInt& aValue)
	{
	LOG( "CLocationRecord::::ReadCenRepValueL(), begin" );
	CRepository* repository;
	repository = CRepository::NewLC( KRepositoryUid );
	User::LeaveIfError(repository->Get( aKey, aValue));
	CleanupStack::PopAndDestroy(repository);
    LOG( "CLocationRecord::::ReadCenRepValueL(), end" );   
	}

void CLocationRecord::HandleQueryNewResults(CMdEQuery& /*aQuery*/, TInt /*aFirstNewItemIndex*/, 
		TInt /*aNewItemCount*/)
	{
	}

void CLocationRecord::HandleQueryCompleted(CMdEQuery& aQuery, TInt aError)
    {
    LOG("CLocationRecord::HandleQueryCompleted - start");
    const TInt count = aQuery.Count();
    LOG1("CLocationRecord::HandleQueryCompleted count: %d", count);

    CMdENamespaceDef* namespaceDef = NULL;

    TRAP_IGNORE( namespaceDef = &iMdeSession->GetDefaultNamespaceDefL() );
    if ( namespaceDef )
        {
        CMdEObjectDef* locationObjectDef = NULL;

        TRAP_IGNORE( locationObjectDef = &namespaceDef->GetObjectDefL( Location::KLocationObject ) );
        if ( locationObjectDef )
        	{
        	CMdEPropertyDef* latitudeDef = NULL;
        	CMdEPropertyDef* longitudeDef = NULL;
        	CMdEPropertyDef* altitudeDef = NULL;
        	
            TRAP_IGNORE( 
            		latitudeDef = &locationObjectDef->GetPropertyDefL(
            				Location::KLatitudeProperty );
            		longitudeDef = &locationObjectDef->GetPropertyDefL(	
            				Location::KLongitudeProperty );
            		altitudeDef = &locationObjectDef->GetPropertyDefL( 
            				Location::KAltitudeProperty );
            		);

            if( latitudeDef && longitudeDef && altitudeDef )
            	{
	            TBool created = EFalse;
	            for ( TInt i = 0; i < count; i++ )
	                {
	                LOG1("CLocationRecord::HandleQueryCompleted check item: %d", i);
	                CMdEItem& item = aQuery.ResultItem(i);
	                CMdEObject& locationObject = static_cast<CMdEObject&>(item);
	
	                CMdEProperty* latProp = NULL;
	                CMdEProperty* lonProp = NULL; 
	                CMdEProperty* altProp = NULL;
	
	                locationObject.Property( *latitudeDef, latProp, 0 );
	                locationObject.Property( *longitudeDef, lonProp, 0 );
	                locationObject.Property( *altitudeDef, altProp, 0 );
	
	                if ( latProp && lonProp )
	                    {
	                    TReal32 distance;
	                    TCoordinate newCoords;
	                    if ( altProp )
	                        {
	                        TRAP_IGNORE( newCoords = TCoordinate( latProp->Real64ValueL(), lonProp->Real64ValueL(), (TReal32)altProp->Real64ValueL() ) );
	                        }
	                    else
	                        {
	                        TRAP_IGNORE( newCoords = TCoordinate( latProp->Real64ValueL(), lonProp->Real64ValueL() ) );
	                        }
	                    
	                    const TInt err = iLocationData.iPosition.Distance(newCoords, distance);
	                    
	                    if ( distance < iLocationDelta )
	                        {
	                        LOG("CLocationRecord::HandleQueryCompleted - match found in db");
	                        TRAPD( err, CreateRelationL( iObjectId, locationObject.Id() ) );
	                        if( err == KErrNone)
	                            {
	                            created = ETrue;
	                            i = count;
	                            }
	                        else
	                            {
	                            aError = err;
	                            }
	                        }
	                    }
	                }

	            if ( !created && aError == KErrNone )
	                {
	                LOG("CLocationRecord::HandleQueryCompleted - no match found in db, create new");
	                TInt locationId( 0 );
	                TRAPD( err, locationId = DoCreateLocationL( iLocationData ) );
	                LOG1("CLocationRecord::HandleQueryCompleted - DoCreateLocationL err: %d", err);
	                if( err == KErrNone )
	                    {
	                    TRAP( err, CreateRelationL( iObjectId, locationId ));
	                    LOG1("CLocationRecord::HandleQueryCompleted - CreateRelationL err: %d", err);
	                    }
	                }
            	}
            }
        }

    LOG("CLocationRecord::HandleQueryCompleted - end");
    }

EXPORT_C void CLocationRecord::SetMdeSession( CMdESession* aSession )
	{
	iMdeSession = aSession;
	TRAPD(err, iRemapper->InitialiseL( aSession ));
	if( err != KErrNone )
		{
		delete iRemapper;
		iRemapper = NULL;
		}
	}

void CLocationRecord::StartTimerL()
	{
	LOG("CLocationRecord::StartTimerL");
	
	if( !iNetworkInfoTimer->IsActive() )
	    {
	    iNetworkInfoTimer->Start( iInterval, iInterval, TCallBack( UpdateNetworkInfo, this ) );
	    }
	}

TTime CLocationRecord::GetMdeObjectTimeL( TItemId aObjectId ) 
    {
    CMdENamespaceDef& namespaceDef = iMdeSession->GetDefaultNamespaceDefL();

    CMdEObjectDef& objectDef = namespaceDef.GetObjectDefL( Object::KBaseObject );
    CMdEPropertyDef& timeDef = objectDef.GetPropertyDefL( Object::KLastModifiedDateProperty );

    CMdEObject* object = NULL;
    CMdEProperty* property = NULL;
    
    object = iMdeSession->GetObjectL( aObjectId );
    object->Property( timeDef, property, 0 );
    if ( !property )
        {
        User::Leave( KErrNotFound );
        }
    return property->TimeValueL();
    }

EXPORT_C TBool CLocationRecord::RemappingNeeded()
	{
	return iRemapper->ItemsInQueue();
	}

// End of file