mapnavproviderrefapp/src/mnrpmapmodel.cpp
author Pat Downey <patd@symbian.org>
Wed, 01 Sep 2010 12:31:27 +0100
branchRCL_3
changeset 18 870918037e16
parent 0 522cd55cc3d7
permissions -rw-r--r--
Revert incorrect RCL_3 drop: Revision: 201033 Kit: 201035

/*
* Copyright (c) 2006-2007 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:  CMnrpMapModel class implementation
*
*/



#include <coemain.h>
#include <barsread.h>
#include <bautils.h>

#include <aknlists.h>
#include <AknIconUtils.h>
#include <AknWaitDialog.h>

#include <fbs.h>
#include <imageconversion.h>
#include <icl/imagedata.h>

#include <lbs.h>
#include <lbsposition.h>
#include <lbspositioninfo.h>

#include <EPos_CPosLandmark.h>
#include <EPos_CPosLandmarkDatabase.h>
#include <EPos_CPosLandmarkCategory.h>
#include <EPos_CPosLmCategoryManager.h>

#include "debug.h"
#include "mnrpengine.h"
#include "mnrputils.h"
#include "mnrpappserver.h"

#include "mnrpmapviewservice.h"
#ifdef RD_MAPNAV_BITMAP_ACCESS
#include "mnrpmapimageservice.h"
#endif
#include "mnrpmapmodel.h"

const TInt KUpdateInterval = 1 * 1000 * 1000; // 1 sec
const TInt KUpdateTimeout = 10 * 1000 * 1000; // 10 sec

const TInt KMaxX = 180;
const TInt KMinX = -180;
const TInt KMapWidth = KMaxX - KMinX;

const TInt KMaxY = 90;
const TInt KMinY = -90;
const TInt KMapHeight = KMaxY - KMinY;

const TReal KDegree = 1.0;
const TReal KMinute = KDegree / 60.0;
const TReal KSecond = KMinute / 60.0;
const TReal KMilliSecond = KSecond / 1000.0;

const TReal KMaxCell = 60 * KDegree;
const TReal KMinScale = 10 * KMilliSecond; // 0.01 second / 1 px

const TInt KBigCellsInView = 2;

const TInt KCellDividers[] = { 6, 2, 5 };
const TInt KNumCellDividers = 3;

const TReal KDefaultRadius = 1; // 1 degree
const TReal KRadiusBorderFactor = 1.1; // 110%

const TReal KEarthEquator = 40075000;
//const TReal KEarthRadius = 6371010;

const TInt KLmTextOffset = 3;
const TInt KLmTextShadowOffset = 1;

const TInt KGridPenSize( 1 );

// ============================ LOCAL FUNCTIONS ===============================

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
TPoint AbsoluteToImage( TRealPoint aAbsolute, TReal aImageToAbsoluteScale )
    {
    TInt s = ( KMapWidth / 2 + aAbsolute.iX ) / aImageToAbsoluteScale;
    TInt t = ( KMapHeight / 2 - aAbsolute.iY ) / aImageToAbsoluteScale;

    return TPoint( s , t );
    }

// ============================ MEMBER CLASSES' FUNCTIONS ===============================

// -----------------------------------------------------------------------------
//  TRealPoint implementation
// -----------------------------------------------------------------------------
//

TRealPoint::TRealPoint()
    {
    TRealX nan;
    nan.SetNaN();

    iX = nan;
    iY = nan;
    }

TRealPoint::TRealPoint( TReal aX, TReal aY )
    : iX( aX ), iY( aY )
    {
    }

TRealPoint::TRealPoint( TCoordinate aCoordinate )
    : iX( aCoordinate.Longitude() ), iY( aCoordinate.Latitude() )
    {
    }

TCoordinate TRealPoint::Coordinate()
    {
    return TCoordinate( iY, iX );
    }

TRealPoint TRealPoint::operator+(
    const TRealPoint& aRight )
    {
    return TRealPoint( this->iX + aRight.iX, this->iY + aRight.iY );
    }

TRealPoint TRealPoint::operator-(
    const TRealPoint& aRight )
    {
    return TRealPoint( this->iX - aRight.iX, this->iY - aRight.iY );
    }

TRealPoint TRealPoint::operator*( const TReal& aRight )
    {
    return TRealPoint( this->iX * aRight, this->iY * aRight );
    }

TRealPoint TRealPoint::operator/( const TReal& aRight )
    {
    return TRealPoint( this->iX / aRight, this->iY / aRight );
    }

void TRealPoint::Set( TReal aX, TReal aY )
    {
    iX = aX;
    iY = aY;
    }

TBool TRealPoint::IsValid()
    {
    return ( !Math::IsNaN( iX ) && !Math::IsNaN( iY ) );
    }

// -----------------------------------------------------------------------------
//  CDrawItem implementation
// -----------------------------------------------------------------------------
//
CMnrpMapModel::CDrawItem* CMnrpMapModel::CDrawItem::NewLC(
    const CPosLandmark& aLm )
    {
    CDrawItem* self = new (ELeave) CDrawItem;
    CleanupStack::PushL( self );

    TPtrC name;
    aLm.GetLandmarkName( name );
    self->iText = MnrpUtils::PrintableStringLC( name );
    CleanupStack::Pop( self->iText );

    TLocality loc;
    if ( KErrNone == aLm.GetPosition( loc ) )
        {
        self->SetAbsolutePosition( loc );
        }

    TPtrC iconFile;
    TInt iconIndex, maskIndex;
    if ( KErrNone == aLm.GetIcon( iconFile, iconIndex, maskIndex ) )
        {
        AknIconUtils::CreateIconL( self->iBitmap, self->iMask, iconFile, iconIndex, maskIndex );
        AknIconUtils::PreserveIconData( self->iBitmap );
        }

    return self;
    }

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
CMnrpMapModel::CDrawItem* CMnrpMapModel::CDrawItem::NewLC(
    const CPosLandmark& aLm,
    CPosLmCategoryManager& aCatman )
    {
    CDrawItem* self = NewLC( aLm );

    if ( !self->iBitmap )
        {
        // try icon from category
        RArray<TPosLmItemId> cats;
        CleanupClosePushL( cats );
        aLm.GetCategoriesL( cats );

        if ( cats.Count() == 1 )
            {
            CPosLandmarkCategory* cat = aCatman.ReadCategoryLC( cats[0] );

            TPtrC iconFile;
            TInt iconIndex, maskIndex;
            if ( KErrNone == cat->GetIcon( iconFile, iconIndex, maskIndex ) )
                {
                AknIconUtils::CreateIconL( self->iBitmap, self->iMask, iconFile, iconIndex, maskIndex );
                AknIconUtils::PreserveIconData( self->iBitmap );
                }

            CleanupStack::PopAndDestroy( cat );
            }
        CleanupStack::PopAndDestroy( &cats );
        }

    return self;
    }

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
void CMnrpMapModel::CDrawItem::SetAbsolutePosition( const TLocality& aLocality )
    {
    iAbsolutePosition = TRealPoint( aLocality.Longitude(), aLocality.Latitude() );
    }

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
CMnrpMapModel::CDrawItem::~CDrawItem()
    {
    if ( iBitmap )
        {
        AknIconUtils::DestroyIconData( iBitmap );
        }
    delete iBitmap;
    delete iMask;
    delete iText;
    }

// ============================ MEMBER FUNCTIONS ===============================

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
CMnrpMapModel::CMnrpMapModel(
    MMapModelObserver& aObserver,
    CMnrpEngine& aEngine )
    :
    iObserver( &aObserver ), iEngine( aEngine )
    {
    iScale = 1;
    iViewOrigin.Set( 0, 0 );
    }

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
CMnrpMapModel::CMnrpMapModel( CMnrpEngine& aEngine )
    : iEngine( aEngine )
    {
    iScale = 1;
    iViewOrigin.Set( 0, 0 );
    }

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
CMnrpMapModel::~CMnrpMapModel()
    {
    delete iPositionRequest;
    iDrawItems.ResetAndDestroy();
    }

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
EXPORT_C CMnrpMapModel* CMnrpMapModel::NewL(
    MMapModelObserver& aObserver,
    CMnrpEngine& aEngine )
    {
    CMnrpMapModel* self = new (ELeave) CMnrpMapModel( aObserver, aEngine );
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop( self );
    return self;
    }

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
EXPORT_C CMnrpMapModel* CMnrpMapModel::NewL( CMnrpEngine& aEngine )
    {
    CMnrpMapModel* self = new (ELeave) CMnrpMapModel( aEngine );
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop( self );
    return self;
    }

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
void CMnrpMapModel::ConstructL()
    {
    _LIT( KRequestorName, "MnRefProvider" );
    iPositionRequest = CMnrpPositionRequest::NewL( KRequestorName, *this );

    TPositionUpdateOptions options;
    options.SetUpdateInterval( TTimeIntervalMicroSeconds( KUpdateInterval ) );
    options.SetUpdateTimeOut( TTimeIntervalMicroSeconds( KUpdateTimeout ) );
    iPositionRequest->SetOptionsL( options );

    AddCitiesL();
    }

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
TBool CMnrpMapModel::CheckDrawItemHasPositionL( CDrawItem& aItem, const CPosLandmark& aLandmark )
	{
    if ( !aItem.AbsolutePosition().IsValid() )
        {
        // try to find coordinate by address
        CPosLandmark* poi = iEngine.AddressToCoordL( aLandmark );
        if ( poi )
        	{
            TLocality pos;
            poi->GetPosition( pos );
            aItem.SetAbsolutePosition( pos );
        	}
    	delete poi;
        }
    
    return aItem.AbsolutePosition().IsValid();
	}

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
EXPORT_C void CMnrpMapModel::UpdateModelL( CMnrpMapViewService& aService )
    {
    ASSERT( iObserver );

    iDrawItems.ResetAndDestroy();

    iShowCurrentLocation =
        aService.CurrentLocationOption() != CMnMapView::ECurrentLocationDisabled;

    if ( iShowCurrentLocation )
        {
        iPositionRequest->FetchNewPosition();
        }

    // Add client-defined marks
    
    iNumIgnoredLandmarks = 0;
    TRealPoint min, max;

    // add non-linked landmarks
    for ( TInt i = 0; i < aService.LandmarksToShow().Count(); i++)
        {
        const CPosLandmark* lm = aService.LandmarksToShow()[i];
        
        CDrawItem* item = CDrawItem::NewLC( *lm );
        if ( CheckDrawItemHasPositionL( *item, *lm ) )
        	{
        	iDrawItems.AppendL( item );
            CleanupStack::Pop( item );
            AdjustBoundingBox( *item, min, max );
        	}
        else
        	{
            CleanupStack::PopAndDestroy( item );
            iNumIgnoredLandmarks++;
        	}
        }

    // add linked landmarks
    for ( TInt db = 0; db < aService.LandmarksToShowDatabases().Count(); db++ )
        {
        const HBufC* uri = aService.LandmarksToShowDatabases()[db];

        LOG1("MnRefProvider::LinkedLandmarks: database (%S)", uri );

        CPosLandmarkDatabase* lmdb = CPosLandmarkDatabase::OpenL( *uri );
        CleanupStack::PushL( lmdb );

        if ( lmdb->IsInitializingNeeded() )
            {
            ExecuteAndDeleteLD( lmdb->InitializeL() );
            }

        CPosLmCategoryManager* catman = CPosLmCategoryManager::NewL( *lmdb );
        CleanupStack::PushL( catman );

        for ( TInt i = 0; i < aService.LinkedLandmarksToShow(db).Count(); i++)
            {
            TPosLmItemId id = aService.LinkedLandmarksToShow(db)[i];
            LOG1("MnRefProvider::LinkedLandmarks: id (%d)", id );

            CPosLandmark* lm = lmdb->ReadLandmarkLC( id );

            CDrawItem* item = CDrawItem::NewLC( *lm, *catman );
            if ( CheckDrawItemHasPositionL( *item, *lm ) )
            	{
            	iDrawItems.AppendL( item );
                CleanupStack::Pop( item );
                AdjustBoundingBox( *item, min, max );
            	}
            else
            	{
                CleanupStack::PopAndDestroy( item );
                iNumIgnoredLandmarks++;
            	}

            CleanupStack::PopAndDestroy( lm );
            }

        CleanupStack::PopAndDestroy( catman );
        CleanupStack::PopAndDestroy( lmdb );
        }

    // Add predefined places
    AddCitiesL();

    LOG4("MnRefProvider::BoundingBox: %f, %f, %f, %f", min.iX, min.iY, max.iX, max.iY );

    // set origin
    iUseCurrentLocationAsOrigin = EFalse;
    if ( aService.IsAreaCentralPointSet() )
        {
        TCoordinate center;
        aService.GetAreaCentralPoint( center );
        iViewOrigin.Set( center.Longitude(), center.Latitude() );
        }
    else if ( !Math::IsNaN( max.iX ) ) // others are also valid then
        {
        iViewOrigin.Set( ( max.iX + min.iX ) / 2, ( max.iY + min.iY ) / 2 );
        }
    else if ( iShowCurrentLocation )
        {
        // get current location and use it as center point
        // this implementation defers location acquiring
        iUseCurrentLocationAsOrigin = ETrue;
        // FetchCurrentLocation();
        iViewOrigin.Set( 0, 0 );
        }
    else
        {
        iViewOrigin.Set( 0, 0 );
        }

    LOG2("MnRefProvider: viewOrigin (%f, %f)", iViewOrigin.iX, iViewOrigin.iY);

    // set scale
    TReal desiredScale = 0;
    TReal radius = 0;

    if ( !Math::IsNaN( aService.AreaRadius() ) )
        {
         // Radius setting is easy in this implementation
         // because map is rectangular just convert meters to degrees
        radius = Abs( aService.AreaRadius() ) / KEarthEquator * 360;
        LOG1("MnRefProvider: user radius (%f)", radius );
        }
    else if ( !Math::IsNaN( max.iX ) ) // others are also valid then
        {
        // get radius from bounding box
        radius = Max( Abs( max.iX - min.iX ), Abs( max.iY - min.iY ) );
        LOG2("MnRefProvider: bounding radius (%f), with border (%f)", radius, radius * KRadiusBorderFactor );
        radius *= KRadiusBorderFactor;
        }
    else
        {
        // default radius
        }

    if ( radius <= 0 )
        {
        radius = KDefaultRadius;
        LOG1("MnRefProvider: default radius (%f)", radius );
        }

    desiredScale = radius / TReal( Max( iScreenSize.iWidth, iScreenSize.iHeight ) );

    LOG3("MnRefProvider: desired scale (%f), min (%f), max(%f)",
        desiredScale, KMinScale, MaxScale() );

    iScale = Max( Min( desiredScale, MaxScale() ), KMinScale );
    CheckBorders();

    LOG1("MnRefProvider: scale (%f)", iScale );

    iObserver->HandleModelUpdateL();
    }


#ifdef RD_MAPNAV_BITMAP_ACCESS
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
EXPORT_C void CMnrpMapModel::UpdateModelL( const CMnrpMapImageService& aService )
    {
    iDrawItems.ResetAndDestroy();

    iShowCurrentLocation =
        aService.ShowOptions() & CMnMapImage::EShowOptionCurrentLocation;

    if ( iShowCurrentLocation )
        {
        iPositionRequest->FetchNewPosition();
        }

    if ( aService.ShowOptions() & CMnMapImage::EShowOptionLandmarks)
        {

        // Add user-defined marks
        TRealPoint min, max;

        // add default landmarks
        CPosLandmarkDatabase* lmdb = CPosLandmarkDatabase::OpenL();
        CleanupStack::PushL( lmdb );

        if ( lmdb->IsInitializingNeeded() )
            {
            ExecuteAndDeleteLD( lmdb->InitializeL() );
            }

        CPosLmCategoryManager* catman = CPosLmCategoryManager::NewL( *lmdb );
        CleanupStack::PushL( catman );

        CPosLmItemIterator* iter = lmdb->LandmarkIteratorL();
        CleanupStack::PushL( iter );

        TInt count = iter->NumOfItemsL();
        for ( TInt i = 0; i < count; i++)
            {
            TPosLmItemId id = iter->NextL();

            CPosLandmark* lm = lmdb->ReadLandmarkLC( id );

            CDrawItem* item = CDrawItem::NewLC( *lm, *catman );
            if ( CheckDrawItemHasPositionL( *item, *lm ) )
            	{
            	iDrawItems.AppendL( item );
                CleanupStack::Pop( item );
                AdjustBoundingBox( *item, min, max );
            	}
            else
            	{
                CleanupStack::PopAndDestroy( item );
            	}

            CleanupStack::PopAndDestroy( lm );
            }
        CleanupStack::PopAndDestroy( iter );
        CleanupStack::PopAndDestroy( catman );
        CleanupStack::PopAndDestroy( lmdb );

        LOG4("MnRefProvider::BoundingBox: %f, %f, %f, %f", min.iX, min.iY, max.iX, max.iY );
        }

    if ( aService.ShowOptions() & CMnMapImage::EShowOptionPois )
        {
        // Add predefined places
        AddCitiesL();
        }

    TMnMapImageParams params = aService.MapImageParams();

    UpdateModel( params );
    }

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
EXPORT_C void CMnrpMapModel::UpdateModel( const TMnMapImageParams& aParams )
    {
    SetScreenSize( aParams.Size() );

    TCoordinate center;
    aParams.GetCenterPoint( center );
    iViewOrigin.Set( center.Longitude(), center.Latitude() );

    LOG2("MnRefProvider: viewOrigin (%f, %f)", iViewOrigin.iX, iViewOrigin.iY);

    // set scale
    TReal desiredScale = 0;
    TReal32 width, height;
    aParams.GetVisibleArea( width, height );
    TReal radius = Max( width, height );

    if ( radius <= 0 )
        {
        radius = KDefaultRadius;
        LOG1("MnRefProvider: default radius (%f)", radius );
        }

    desiredScale = radius / TReal( Max( iScreenSize.iWidth, iScreenSize.iHeight ) );

    LOG3("MnRefProvider: desired scale (%f), min (%f), max(%f)",
        desiredScale, KMinScale, MaxScale() );

    iScale = Max( Min( desiredScale, MaxScale() ), KMinScale );
    CheckBorders();

    LOG1("MnRefProvider: scale (%f)", iScale );
    }
#endif

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
void CMnrpMapModel::AddCitiesL()
    {
    TArray<CPosLandmark*> places = iEngine.KnownPlacesL();
    for ( TInt i = 0; i < places.Count(); i++)
        {
        CPosLandmark* lm = places[i];
        CDrawItem* item = CDrawItem::NewLC( *lm );
        item->iOwnPoi = ETrue;
        iDrawItems.AppendL( item );
        CleanupStack::Pop( item );
        }
    }

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
EXPORT_C void CMnrpMapModel::GetGrid( CMnrpMapModel::TGrid& aGrid ) const
    {
    TReal angle = Min( Width(), Height() );

    TReal minAngle = Min( iScreenSize.iWidth, iScreenSize.iHeight ) * KMinScale;

    if ( angle > minAngle )
        {
        // 1. Find nice grid scale

        TInt dividerIndex = 0;
        TReal cellSize = KMaxCell;

        while ( cellSize * KBigCellsInView > angle )
            {
            cellSize /= KCellDividers[dividerIndex];

            if ( ++dividerIndex == KNumCellDividers )
                {
                dividerIndex = 0;
                }
            }

        aGrid.iAbsoluteGridStep = cellSize;

        // 2. Find closest grid point

        TReal x = TInt( iViewOrigin.iX / cellSize ) * cellSize;
        TReal y = TInt( iViewOrigin.iY / cellSize ) * cellSize;

        aGrid.iAbsoluteReference = TRealPoint( x, y ).Coordinate();
        }
    else
        {
        aGrid.iAbsoluteReference = TCoordinate( 0, 0 );
        aGrid.iAbsoluteGridStep = minAngle / KBigCellsInView;
        }

    // convert to screen coordinates
    aGrid.iReference = AbsoluteToScreen( aGrid.iAbsoluteReference );
    aGrid.iGridStep = AbsoluteToScreen( aGrid.iAbsoluteGridStep );
    }

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
EXPORT_C void CMnrpMapModel::Zoom( TReal aZoomRate )
    {
    iScale *= aZoomRate;
    CheckBorders();
    }

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
EXPORT_C void CMnrpMapModel::GoCenter()
    {
    iViewOrigin.Set( 0, 0 );
    CheckBorders();
    }

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
EXPORT_C void CMnrpMapModel::MoveTo( TCoordinate aTarget )
    {
    iViewOrigin = aTarget;
    CheckBorders();
    }

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
void CMnrpMapModel::CheckBorders()
    {
    if ( Height() > KMapHeight || Width() > KMapWidth )
        {
        iScale = MaxScale();
        }
    if ( iScale < KMinScale )
        {
        iScale = KMinScale;
        }
    if ( Left() < KMinX )
        {
        iViewOrigin.iX = KMinX + Width() / 2;
        }
    if ( Right() > KMaxX )
        {
        iViewOrigin.iX = KMaxX - Width() / 2;
        }
    if ( Top() > KMaxY )
        {
        iViewOrigin.iY = KMaxY - Height() / 2;
        }
    if ( Bottom() < KMinY )
        {
        iViewOrigin.iY = KMinY + Height() / 2;
        }
    }

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
EXPORT_C TReal CMnrpMapModel::Left() const
    {
    return iViewOrigin.iX - Width() / 2;
    }

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
EXPORT_C TReal CMnrpMapModel::Right() const
    {
    return iViewOrigin.iX + Width() / 2;
    }

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
EXPORT_C TReal CMnrpMapModel::Top() const
    {
    return iViewOrigin.iY + Height() / 2;
    }

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
EXPORT_C TReal CMnrpMapModel::Bottom() const
    {
    return iViewOrigin.iY - Height() / 2;
    }

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
EXPORT_C TReal CMnrpMapModel::Width() const
    {
    return TReal( iScreenSize.iWidth ) * iScale;
    }

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
EXPORT_C TReal CMnrpMapModel::Height() const
    {
    return TReal( iScreenSize.iHeight ) * iScale;
    }

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
EXPORT_C TCoordinate CMnrpMapModel::Center() const
    {
    return TCoordinate( iViewOrigin.iY, iViewOrigin.iX );
    }

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
TArray<CMnrpMapModel::CDrawItem*> CMnrpMapModel::Items() const
    {
    // adjust all items to screen coordinates
    for ( TInt i = 0; i < iDrawItems.Count(); i++ )
        {
        iDrawItems[i]->iScreenPosition = AbsoluteToScreen( iDrawItems[i]->iAbsolutePosition );
        }
    return iDrawItems.Array();
    }

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
CMnrpMapModel::TCurrentLocation CMnrpMapModel::CurrentLocation() const
    {
    TCurrentLocation current;

    TPosition pos;
    iPosInfo.GetPosition( pos );

    current.iAbsoluteLocation = pos;

    if ( !Math::IsNaN( pos.Latitude() ) && !Math::IsNaN( pos.Longitude() ) )
        {
        current.iLocation = AbsoluteToScreen( current.iAbsoluteLocation );

        // calculate error radius in degrees and pixels
        if ( !Math::IsNaN( pos.HorizontalAccuracy() ) )
            {
            current.iAbsoluteErrorDegrees = ( pos.HorizontalAccuracy() / KEarthEquator ) * 360;
            current.iErrorRadius = current.iAbsoluteErrorDegrees / iScale;
            }
        else
            {
            current.iErrorRadius = 0;
            current.iAbsoluteErrorDegrees = 0;
            }

        current.iIsValid = ETrue;
        }
    else
        {
        current.iIsValid = EFalse;
        }

    return current;
    }

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
EXPORT_C void CMnrpMapModel::SetScreenSize( TSize aScreenSize )
    {
    __ASSERT_DEBUG( aScreenSize.iWidth >= 1 && aScreenSize.iHeight >= 1,
                    Panic( KErrGeneral ) );
    iScreenSize = aScreenSize;
    CheckBorders();
    }

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
TReal CMnrpMapModel::MaxScale() const
    {
    TReal scaleByWidth = KMapWidth / TReal( iScreenSize.iWidth );
    TReal scaleByHeight = KMapHeight / TReal( iScreenSize.iHeight );
    return Min( scaleByWidth, scaleByHeight );
    }

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
TPoint CMnrpMapModel::AbsoluteToScreen( TRealPoint aAbsolutePoint ) const
    {
    TRealPoint realScreen = ( aAbsolutePoint - TRealPoint( Left(), Bottom() ) ) / iScale;
    return TPoint( realScreen.iX, iScreenSize.iHeight - realScreen.iY );
    }

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
EXPORT_C TPoint CMnrpMapModel::AbsoluteToScreen( TCoordinate aAbsolutePoint ) const
    {
    TRealPoint real( aAbsolutePoint.Longitude(), aAbsolutePoint.Latitude() );
    return AbsoluteToScreen( real );
    }

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
TInt CMnrpMapModel::AbsoluteToScreen( TReal aDistance ) const
    {
    return aDistance / iScale;
    }

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
EXPORT_C TCoordinate CMnrpMapModel::ScreenToAbsolute( TPoint aScreenPoint ) const
    {
    TReal xdiff = aScreenPoint.iX * iScale;
    TReal ydiff = aScreenPoint.iY * iScale;
    return TCoordinate( Top() - ydiff, Left() + xdiff );
    }

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
EXPORT_C TReal CMnrpMapModel::ScreenToAbsolute( TInt aDistance ) const
    {
    return aDistance * iScale;
    }

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
void CMnrpMapModel::AdjustBoundingBox(
    CMnrpMapModel::CDrawItem& aItem,
    TRealPoint& aMin,
    TRealPoint& aMax )
    {
    // X
    if ( !Math::IsNaN( aItem.iAbsolutePosition.iX ) )
        {
        if ( Math::IsNaN( aMin.iX ) || aItem.iAbsolutePosition.iX < aMin.iX )
            {
            aMin.iX = aItem.iAbsolutePosition.iX;
            }
        if ( Math::IsNaN( aMax.iX ) || aItem.iAbsolutePosition.iX > aMax.iX )
            {
            aMax.iX = aItem.iAbsolutePosition.iX;
            }
        }

    // Y
    if ( !Math::IsNaN( aItem.iAbsolutePosition.iY ) )
        {
        if ( Math::IsNaN( aMin.iY ) || aItem.iAbsolutePosition.iY < aMin.iY )
            {
            aMin.iY = aItem.iAbsolutePosition.iY;
            }
        if ( Math::IsNaN( aMax.iY ) || aItem.iAbsolutePosition.iY > aMax.iY )
            {
            aMax.iY = aItem.iAbsolutePosition.iY;
            }
        }
    }

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
void CMnrpMapModel::HandlePositionRequestCompletedL( TInt aError )
    {
    if ( !aError )
        {
        iPositionRequest->GetPosition( iPosInfo );
        if ( iObserver )
            {
            iObserver->HandleModelUpdateL();
            }
        }
    if ( iShowCurrentLocation )
        {
        iPositionRequest->FetchNewPosition();
        }
    }

// =====================================================
// DRAWING METHODS
// =====================================================

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
EXPORT_C void CMnrpMapModel::RenderL(
    CFbsBitmap& aBitmap,
    TRect aTargetRect,
    CFont* aTextFont,
    CFont* aItemFont ) const
	{
	// create an off-screen device and context
	CFbsBitmapDevice* bitmapDevice = CFbsBitmapDevice::NewL( &aBitmap );
	CleanupStack::PushL( bitmapDevice );

	CFbsBitGc* gc = NULL;
	User::LeaveIfError( bitmapDevice->CreateContext( gc ) );
	CleanupStack::PushL( gc );

    gc->UseFont( aTextFont );

    // get data
    TGrid grid;
    GetGrid( grid );

    TBool isBackground = EFalse;
    // background
    if ( !isBackground )
        {
        // no map background, clear map area
        gc->Clear( aTargetRect );
        }

    gc->SetClippingRect( aTargetRect );
    gc->SetOrigin( aTargetRect.iTl );

    DrawGrid( *gc, aTargetRect.Size(), grid );

    gc->DiscardFont(); // text font
    gc->UseFont( aItemFont );

    // draw items
    DrawLandmarks( *gc, *aItemFont, aTargetRect.Size(), isBackground ); // invert shadow and text color
    DrawCurrentLocation( *gc, aTargetRect.Size() );

    // cleanup

    gc->DiscardFont(); // item font

	CleanupStack::PopAndDestroy( gc );
	CleanupStack::PopAndDestroy( bitmapDevice );
	}

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
EXPORT_C TInt CMnrpMapModel::NumIgnoredLandmarks()
	{
	return iNumIgnoredLandmarks;
	}

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
void CMnrpMapModel::DrawGrid(
    CFbsBitGc& aGc,
    const TSize& aBoxSize,
    CMnrpMapModel::TGrid& aGrid ) const
    {
    const TRgb KGridLineColor( 128, 128, 128 );

    aGc.SetPenSize( TSize( KGridPenSize, KGridPenSize ) );
    aGc.SetPenColor( KGridLineColor );

    if ( aGrid.iGridStep > 0 )
        {
        // 1. Vertical lines
        TInt x = aGrid.iReference.iX;
        // find leftmost vertical line
        while ( x > 0 )
        	{
        	x -= aGrid.iGridStep;
        	}
        x += aGrid.iGridStep;
        // draw lines selecting different line colors for major and minor lines
        while ( x < aBoxSize.iWidth )
            {
            DrawVerticalLine( aGc, x, aBoxSize );
            x += aGrid.iGridStep;
            }

        // 2. Horizontal lines
        TInt y = aGrid.iReference.iY;
        // find topmost vertical line
        while ( y > 0 )
        	{
        	y -= aGrid.iGridStep;
        	}
        y += aGrid.iGridStep;
        // draw lines selecting different line colors for major and minor lines
        while ( y < aBoxSize.iHeight )
            {
            DrawHorizontalLine( aGc, y, aBoxSize );
            y += aGrid.iGridStep;
            }
        }
    }

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
void CMnrpMapModel::DrawVerticalLine( CFbsBitGc& aGc, TInt aX, TSize aBoxSize ) const
    {
    aGc.DrawLine( TPoint( aX, 0 ), TPoint( aX, aBoxSize.iHeight ) );
    }

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
void CMnrpMapModel::DrawHorizontalLine( CFbsBitGc& aGc, TInt aY, TSize aBoxSize ) const
    {
    aGc.DrawLine( TPoint( 0, aY ), TPoint( aBoxSize.iWidth, aY ) );
    }

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
void CMnrpMapModel::DrawLandmarks(
    CFbsBitGc& aGc,
    CFont& aFont,
    const TSize& aBoxSize,
    TBool aSwitchColorAndShadow ) const
    {
    const TSize KItemMarkSize( 3, 3 ); // pixels
    const TSize KItemIconSize( 20, 20 ); // pixels

    const TRgb KPoiColor( 0, 0, 192 );
    const TRgb KPoiTextColor( 0, 0, 0 );
    const TRgb KPoiTextShadowColor( 224, 224, 224 );
    const TRgb KLmColor( 255, 0, 0 );
    const TRgb KLmTextColor( 255, 0, 0 );
    const TRgb KLmTextShadowColor( 224, 224, 224 );

    TArray<CMnrpMapModel::CDrawItem*> items = Items();

    RArray<TRect> textBoxes;

    for ( TInt i = 0; i < items.Count(); i++ )
        {
        const TPoint& pos = items[i]->Position();
        if ( pos.iX >= 0 && pos.iX < aBoxSize.iWidth &&
             pos.iY >= 0 && pos.iY < aBoxSize.iHeight )
            {
            CFbsBitmap* icon = items[i]->Bitmap();
            CFbsBitmap* mask = items[i]->Mask();

            if ( icon && mask )
                {
                AknIconUtils::SetSize( icon, KItemIconSize );

                TRect iconBox( pos, KItemIconSize );
                iconBox.Move( -iconBox.Width() / 2, -iconBox.Height() / 2 );

                aGc.DrawBitmapMasked(
                    iconBox,
                    icon,
                    KItemIconSize,
                    mask,
                    EFalse );
                }
            else
                {
                if ( items[i]->IsPoi() )
                    {
                    aGc.SetPenColor( KPoiColor );
                    }
                else
                    {
                    aGc.SetPenColor( KLmColor );
                    }

                TRect markBox( pos, KItemMarkSize );
                markBox.Move( -markBox.Width() / 2, -markBox.Height() / 2 );
                aGc.SetPenSize( KItemMarkSize );
                aGc.Plot( pos );
                }

            if ( items[i]->Text().Length() )
                {
                TPoint textPos( pos );
                textPos.iX += KLmTextOffset;

                TSize textSize( aFont.TextWidthInPixels( items[i]->Text() ), aFont.HeightInPixels() );
                TRect textBox( textPos, textSize );

                // verify this textbox does not intersect any previous ones
                TBool overlaps = EFalse;
                for ( TInt b = 0; b < textBoxes.Count(); b++ )
                    {
                    if ( textBox.Intersects( textBoxes[b] ) )
                        {
                        overlaps = ETrue;
                        break;
                        }
                    }

                if ( !overlaps )
                    {
                    textBoxes.Append( textBox );

                    aGc.SetPenSize( TSize( 1, 1 ) );

                    TRgb textColor, textShadowColor;

	                if ( items[i]->IsPoi() )
	                    {
	                    textColor = KPoiTextColor;
						textShadowColor = KPoiTextShadowColor;
	                    }
	                else
	                    {
	                    textColor = KLmTextColor;
						textShadowColor = KLmTextShadowColor;
	                    }

                    // text shadow
                    TPoint shadowPos( textPos + TPoint( KLmTextShadowOffset, KLmTextShadowOffset ) );
                    aGc.SetPenColor( aSwitchColorAndShadow ? textColor : textShadowColor );
                    aGc.DrawText( items[i]->Text(), shadowPos );

                    // text itself
                    aGc.SetPenColor( aSwitchColorAndShadow ? textShadowColor : textColor );
                    aGc.DrawText( items[i]->Text(), textPos );
                    }
                }
            }
        }
    textBoxes.Close();
    }

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//
void CMnrpMapModel::DrawCurrentLocation(
    CFbsBitGc& aGc,
    const TSize& aBoxSize ) const
    {
    const TSize KPositionPenSize( 20, 20 );
    const TSize KPositionErrorPenSize( 1, 1 );
    const TRgb KPositionColor( 255, 255, 0 );

    // draw current location
    CMnrpMapModel::TCurrentLocation location = CurrentLocation();

    if ( location.iIsValid )
        {
        TPoint& pos = location.iLocation;

        if ( pos.iX >= 0 && pos.iX < aBoxSize.iWidth &&
             pos.iY >= 0 && pos.iY < aBoxSize.iHeight )
            {
            if ( location.iErrorRadius )
                {
                TRect circleBox( pos.iX, pos.iY, pos.iX, pos.iY );
                circleBox.Grow( location.iErrorRadius, location.iErrorRadius );

                aGc.SetPenSize( KPositionErrorPenSize );
                aGc.SetBrushStyle( CGraphicsContext::EVerticalHatchBrush );
                aGc.SetBrushColor( KPositionColor );
                aGc.DrawEllipse( circleBox );
                }

            aGc.SetPenColor( KPositionColor );
            aGc.SetPenSize( KPositionPenSize );
            aGc.Plot( pos );
            }
        }
    }