mapnavproviderrefapp/src/mnrpmapmodel.cpp
branchRCL_3
changeset 18 870918037e16
parent 0 522cd55cc3d7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mapnavproviderrefapp/src/mnrpmapmodel.cpp	Wed Sep 01 12:31:27 2010 +0100
@@ -0,0 +1,1251 @@
+/*
+* 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 );
+            }
+        }
+    }
+