Landmarks Search API: Using the Landmarks Search API

Searching landmarks

The client uses CPosLandmarkSearch to search landmarks in a landmarks database. To create a CPosLandmarkSearch instance, the client must specify a database to search in. A handle to an open database (CPosLandmarkDatabase instance) is passed to CPosLandmarkSearch.

Configuring search

CPosLandmarkSearch can be configured by setting certain parameters described below.

Maximum number of matches

The client can set a maximum number of matches by calling CPosLandmarkSearch. The search operation stops if the maximum number of matches is reached.

If the client does not set a maximum, the search returns all the matches in the database. The client can also set this explicitly by passing KPosLmMaxNumOfMatchesUnlimited to CPosLandmarkSearch.

Search previous matches only

If the CPosLandmarkSearch instance has just finished a search, the client can specify that only these matches should be considered in the next search. This is specified by passing a boolean value to CPosLandmarkSearch when the search is started. In that way, the client can refine its search, for instance, if there are too many matches.

Searching previous matches is useful if there are too many matches and the application user wants to narrow down the search before listing all matches.

It is also possible to use CPosLmIdListCriteria to search previous matches that are not immediate previous matches (see Search criteria).

Sort preference

The client can specify that the matching landmarks or landmark categories should be sorted.

One of CPosLandmarkSearch method overloads takes TPosLmSortPref as parameter. In TPosLmSortPref, the client can specify whether the landmarks should be sorted by name in ascending or descending order.

Specifying search criteria

In order to specify what landmarks to search for the client uses criterion classes. The criteria are passed to CPosLandmarkSearch when the search is started. In this section, each criterion class is explained.

Landmarks by category

CPosLmCategoryCriteria is used to search for landmarks, which belong to a certain category. The category is specified in one of the following ways:

If no category is specified, the search operation retrieves uncategorized landmarks.

The predefined global landmark categories are listed in Landmarks API Specification.

Matching text pattern

CPosLmTextCriteria is used to search for landmarks, which contain a certain text. The criterion is defined by providing a text to search for and the position fields and text attributes to search in each landmark. For example, the user can search for “Chinese” in the landmark description and landmark name attributes.

The search is case insensitive.

If no position fields or landmark attributes to search are specified, all the position fields and landmark attributes are searched in each landmark.

Wild card characters are supported in the search string: "?" matches any single character and "*" matches zero or more consecutive characters.

Landmarks within an area

CPosLmAreaCriteria is used to search for landmarks, which reside in a certain area. The search area is defined by providing two latitude and two longitude values that specify the borders of the area.

Note: This search does not consider landmark coverage radius (coverage radius is defined in Landmarks API).

The area borders must fulfill the following rules:

The east border longitude can be less than the west border longitude. This defines an area, which crosses the 180 meridian.

If the east and west border longitudes are equal, only landmarks that lie on the specified longitude are returned. Similarly, if the north and south border latitudes are equal, only landmarks that lie on the specified latitude are returned.

If west longitude is set to -180 and east longitude is set to +180, all longitudes are included in the search.

Nearest landmarks

CPosLmNearestCriteria is used to find the landmarks, which are closest to a certain coordinate. By default, the matches returned in the search are sorted in an ascending distance order.

Since this operation returns all the landmarks in the database by default, it is recommended to combine the nearest criteria with a limit on the number of matches. The match limit is set by calling CPosLandmarkSearch.

It is often a good idea to specify a maximum distance to narrow down the search. This is done by calling CPosLmNearestCriteria.

By default, the coverage radius of the landmarks is not considered in the search; that is, the distance to the landmark center point is used. The client can change this behavior by calling CPosLmNearestCriteria. If ETrue is passed to this function, coverage radius is considered; that is, the effective distance is the distance to the landmark center point minus the coverage radius. If the search coordinate lies within a landmark’s coverage area, the effective distance is zero.

Composite criterion

CPosLmCompositeCriteria is used to search for landmarks by combining multiple search criteria. For instance, to search for all restaurants in the area, this class can be used to combine CPosLmAreaCriteria and CPosLmCategoryCriteria

The client combines the criteria by passing each criterion instance to CPosLmCompositeCriteria.

If CPosLmNearestCriteria is used and no sort preference is specified, the result will be sorted by distance. If more than one CPosLmNearestCriteria are combined using CPosLmCompositeCriteria, the sort order will be undefined unless a sort preference is specified.

Note: It is not allowed to use nested composite criterion.

ID list

CPosLmIdListCriteria is used if the client only wants to search a subset of the landmarks in a database. The client passes a list of landmark IDs to specify which landmarks to include in the search.

This criterion must be combined with other search criteria using CPosLmCompositeCriteria (see Composite criterion above).

For example, if this criterion is combined with CPosLmTextCriteria, the search operation searches the landmarks specified in the ID list criterion and returns those that match the given text string.

Note: Only one ID list criterion is allowed in a composite criterion.

Executing search

A search for landmarks is started by calling CPosLandmarkSearch.

This function returns a CPosLmOperation instance, which is used to execute the search operation. The operation can either be executed all at once or in incremental steps. An active object can be used to incrementally run the search in the background. CPosLmOperation runs the operation all at once and CPosLmOperation runs the operation incrementally. When the search is complete, the client must delete the CPosLmOperation object.

Instead of calling CPosLmOperation, the client can call the global function ExecuteAndDeleteLD() which also deletes the operation object. For example:

ExecuteAndDeleteLD( search->StartLandmarkSearchL( criteria ) );

If the search is run incrementally, the client is informed of the search progress. The client passes a TReal32 variable to CPosLmOperation which contains the progress when NextStep() completes.

Progress is a floating point number in the interval [0.0, 1.0]. 0.0 indicates that the operation has not started and 1.0 indicates that the operation has completed.

The client also passes TRequestStatus to CPosLmOperation. The request status is set to KPosLmOperationNotComplete if the step has completed, but more steps are needed before the operation is complete. The request status is KErrNone if the operation has finished successfully. The status is set to an error code if the operation has failed.

The IDs of the matches from the search can be retrieved by calling CPosLandmarkSearch. It is possible to call it also during a search to retrieve any matches encountered so far, but it is not guaranteed that the matches are sorted. However, the matches in the iterator are always sorted, also during a search, if a display data is set to CPosLandmarkSearch before the search is started.

The sequence diagram below shows how client searches landmarks by text criterion and reads matches from database.

Figure 4: Search landmarks sequence diagram

The following code example shows how to perform a search synchronously (not incrementally) in a landmark database.

// Create a search object and provide the CPosLandmarkDatabase object.
CPosLandmarkSearch* search = CPosLandmarkSearch::NewL( *database );
CleanupStack::PushL( search );

// Create the search criterion
_LIT( KSearchString, "flowers" );
CPosLmTextCriteria* crit = CPosLmTextCriteria::NewLC();
crit->SetTextL( KSearchString );

// Start the search and execute it at once.
ExecuteAndDeleteLD( search->StartLandmarkSearchL( *crit ) );

CleanupStack::PopAndDestroy( crit );

// Retrieve an iterator to access the matching landmarks.
CPosLmItemIterator* iter = search->MatchIteratorL();
CleanupStack::PushL( iter );

// Iterate the search matches.
TPosLmItemId lmID;
while ( ( lmID = iter->NextL() ) != KPosLmNullItemId )
    {
    CPosLandmark* lm = database->ReadLandmarkLC( lmID );

    // Do something with the landmark information

    CleanupStack::PopAndDestroy( lm );
    }

CleanupStack::PopAndDestroy( iter );
CleanupStack::PopAndDestroy( search );

The following example shows how to use composite criterion to search for restaurants, which contain the text “thai”.

// Create the composite criterion
CPosLmCompositeCriteria* compCrit = CPosLmCompositeCriteria::NewLC(
    CPosLmCompositeCriteria::ECompositionAND );

// Create the category search criterion and add it to composite
_LIT( KCategoryName, "restaurant" );
CPosLmCategoryCriteria* catCrit = CPosLmCategoryCriteria::NewLC();
catCrit->SetCategoryNameL( KCategoryName );

User::LeaveIfError( compCrit->AddArgument( catCrit ) );
// Ownership of the category criterion has been passed to the composite
CleanupStack::Pop( catCrit );

// Create the text search criterion and add it to composite
_LIT( KSearchString, "thai" );
CPosLmTextCriteria* textCrit = CPosLmTextCriteria::NewLC();
textCrit->SetTextL( KSearchString );

User::LeaveIfError( compCrit->AddArgument( textCrit ) );
// Ownership of the text criterion has been passed to the composite
CleanupStack::Pop( textCrit );

// Start the search
ExecuteAndDeleteLD( search->StartLandmarkSearchL( *compCrit ) );
CleanupStack::PopAndDestroy( compCrit );

// Retrieve matches

Retrieving displayable results

The client can specify a displayable data collection that will be populated with the matching landmarks or landmark categories during the search. This is done by creating a CPosLmDisplayData instance and passing it to CPosLandmarkSearch. The display data object will be reset each time a new search is started.

The client can specify that only partial landmark data will be read from the database by calling CPosLmDisplayData. If the client does not set partial read parameters, full landmark data will be read. The client can also set this explicitly by calling CPosLmDisplayData.

When searching for categories, full category data is always read from the database.

The client can unset a previously set display data by calling CPosLandmarkSearch.

If display data has been set before the search is started, all matches from the search can be retrieved by calling CPosLmDisplayData for all the indexes in the interval [0, CPosLmDisplayData - 1]. By calling CPosLmDisplayItem or CPosLmDisplayItem, the client gets access to the match.

During a search, CPosLmDisplayData can be called repeatedly to get the index of each new match found and CPosLmDisplayData can be called for each new index. The matches in the display data are always sorted, even during a search.

The following sequence diagram shows how to perform a search synchronously (not incrementally) in a landmark database using display data.

Figure 5: Using display data sequence diagram

The following example shows how to search a landmark database incrementally. The client also requests to sort the matches. The implementation is encapsulated in active object class CSearchHandler.

class CSearchHandler : public CActive
    {
    public: // constructor and destructor

        static CSearchHandler* NewL(CPosLandmarkDatabase* aDb);
        virtual ~CSearchHandler();

    public:
 
        void StartLandmarkSearchL(CPosLmSearchCriteria* aCriteria);
        void StartCategorySearchL(CPosLmSearchCriteria* aCriteria);

        void NextSearchStep();
        void CleanupSearch();

    public: // from CActive

        void RunL();
        void DoCancel();
        TInt RunError(TInt aError);

    private:

        CSearchHandler(CPosLandmarkDatabase* aDb);
        void ConstructL();

    private:
        CPosLandmarkDatabase* iDb;
        CPosLandmarkSearch* iSearch;
        CPosLmDisplayData* iDisplayData;
        CPosLmOperation* iSearchOperation;
        TReal32 iProgress;
        TBool iIsSearchingForLandmarks;
    };

CSearchHandler::CSearchHandler( CPosLandmarkDatabase* aDb )
: CActive( EPriorityNormal ), iDb( aDb )
    {
    }

void CSearchHandler::ConstructL()
    {
    iSearch = CPosLandmarkSearch::NewL( *iDb );

    iDisplayData = CPosLmDisplayData::NewL();
    iSearch->SetDisplayData( *iDisplayData );
    }

CSearchHandler* CSearchHandler::NewL( CPosLandmarkDatabase* aDb )
    {
    CSearchHandler* self = new (ELeave) CSearchHandler( aDb );
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop();
    return self;
    }

CSearchHandler::~CSearchHandler()
    {
    Cancel();
    iSearch->UnsetDisplayData();
    delete iDisplayData;
    delete iSearch;
    }

void CSearchHandler::StartLandmarkSearchL( CPosLmSearchCriteria* aCriteria )
    {
    TPosLmSortPref sp( CPosLandmark::ELandmarkName, TPosLmSortPref::EAscending );
    iSearchOperation = iSearch->StartLandmarkSearchL( *aCriteria, sp );
    iIsSearchingForLandmarks = ETrue;

    // Perform the first step in the incremental search operation.
    NextSearchStep();
    }

void CSearchHandler::StartCategorySearchL( CPosLmSearchCriteria* aCriteria )
    {
    iSearch->StartCategorySearchL( *aCriteria,
    CPosLmCategoryManager::ECategorySortOrderNameAscending );
    iIsSearchingForLandmarks = EFalse;

    // Perform the first step in the incremental search operation.
    NextSearchStep();
    }

void CSearchHandler::NextSearchStep()
    {
    iSearchOperation->NextStep( iStatus, iProgress );
    SetActive();
    }

void CSearchHandler::CleanupSearch()
    {
    // Delete the search operation. This will cancel the operation if it is not
    // already complete.
    delete iSearchOperation;
    iSearchOperation = NULL;
    }

void CSearchHandler::RunL()
    {
    // Get all new matches since last step.
    TInt newItemIndex = iDisplayData->NewItemIndex();
    while ( newItemIndex != KPosLmNoNewItems )
        {
        CPosLmDisplayItem& item = iDisplayData->DisplayItem( newItemIndex );
        if (iIsSearchingForLandmarks)
            {
            const CPosLandmark& lm = item.Landmark();
            // Do something with the landmark information
            }
        else
            {
            const CPosLandmarkCategory& category = item.Category();
            // Do something with the landmark category information
            }
 
        newItemIndex = iDisplayData->NewItemIndex();
        }

    if ( iStatus == KPosLmOperationNotComplete )
        { 
        // The search operation has not completed.
        // Use value iProgress to show progress bar to the application user.

        // Perform the next search step
        NextSearchStep();
        }
    else
        { // The search operation has completed.
        User::LeaveIfError( iStatus.Int() );

        CleanupSearch();
        }
    }

void CSearchHandler::DoCancel()
    {
    CleanupSearch();
    }

TInt CSearchHandler::RunError( TInt /*aError*/ )
    {
    // Notify application user of error and cleanup.
    CleanupSearch();
    return KErrNone;
    }

Searching categories

A search for landmark categories is started by calling CPosLandmarkSearch. All the other steps are similar to those for searching landmarks with a few exceptions.

Searching for categories supports only one search criterion, CPosLmCatNameCriteria: search for landmark categories with a certain name. Wild card characters in the search string are supported: "?" matches any single character and "*" matches zero or more consecutive characters. The search is case insensitive.

CPosLandmarkSearch takes CPosLandmarkCategory as parameter. It can be set to no sorting, ascending by category name or descending by category name.

Similarly to searching landmarks searching categories also supports limiting maximum amount of results and searching within previous results only.

Following code example shows how a client can search for categories, which contain word "food" (in order to find categories such as "Food and beverages", "Chinese food", etc.).

// Create a search object and provide the CPosLandmarkDatabase object.
CPosCategorySearch* search = CPosCategorySearch::NewL( *database );
CleanupStack::PushL( search );

// Create the search criterion
_LIT( KSearchString, "*food*" ); // using wildcards
CPosLmCatNameCriteria* crit = CPosLmCatNameCriteria::NewLC();
crit->SetTextL( KSearchString );

// Start the search and execute it at once.
ExecuteAndDeleteLD( search->StartLandmarkSearchL( *crit ) );

CleanupStack::PopAndDestroy( crit );

// Retrieve an iterator to access the matching landmarks.
CPosLmItemIterator* iter = search->MatchIteratorL();
CleanupStack::PushL( iter );

// Iterate the search matches.
TPosLmItemId categoryID;
while ( ( categoryID = iter->NextL() ) != KPosLmNullItemId )
    {
    CPosLandmarkCategory* category = database->ReadCategoryLC( categoryID );

    // Do something with the category information

    CleanupStack::PopAndDestroy( category );
    }

CleanupStack::PopAndDestroy( iter );
CleanupStack::PopAndDestroy( search );

Search multiple databases

Searching for landmarks or landmark categories in multiple landmark databases is rather similar to searching in one database.

The client creates a CPosLmMultiDbSearch instance by passing an array containing the URIs of the landmark databases to search to CPosLmMultiDbSearch. The client can also change which databases to search by calling CPosLmMultiDbSearch.

However, there are some restrictions on the criterion classes and some extra functionality needed to handle several databases:

This example shows how to perform a search synchronously (not incrementally) in multiple landmark databases.

void SearchInDatabasesL( const CDesCArray& aDatabaseURIs )
{
// Create a multi search object and provide a list of database URIs.
CPosLmMultiDbSearch* search = CPosLmMultiDbSearch::NewL( aDatabaseURIs );
CleanupStack::PushL( search );

// Create a display data object.
CPosLmDisplayData* displayData = CPosLmDisplayData::NewL();
CleanupStack::PushL( displayData );

// Set the display data to the search object.
search->SetDisplayData( *displayData );

// Create the search criterion
_LIT( KSearchString, "flowers" );
CPosLmTextCriteria* crit = CPosLmTextCriteria::NewLC();
crit->SetTextL( KSearchString );

// Start the search and execute it all at once.
ExecuteAndDeleteLD( search->StartLandmarkSearchL( *crit ) );
CleanupStack::PopAndDestroy( crit );

// Check if any errors have occured.
TUint numOfErrors = search->NumOfSearchErrors();
for ( TUint i = 0; i < numOfErrors; i++ )
    {
    CPosLmMultiDbSearch::TSearchError searchError;
    search->GetSearchError( i, searchError );
    // Do something with error
    }

// Iterate the search matches.
for ( TInt i = 0; i < displayData->Count(); i++ )
    {
    const CPosLandmark& lm = displayData->DisplayItem( i ).Landmark();
    // Do something with the landmark information
    }

// Unset display data collection so that it is not reset by next search
search->UnsetDisplayData();

CleanupStack::PopAndDestroy( displayData );
CleanupStack::PopAndDestroy( search );

Error handling

Landmarks Search API uses the standard Symbian error reporting mechanism. In case of a serious error, panics are used. Otherwise, errors are reported through return codes or leaves.

Landmarks Search API uses the same panic code category as Landmarks API. The panic codes are documented in Landmarks API specification.

Memory overhead

If there are several matches (more than 1000) in a search, a large amount of memory is needed to store the matches. It might therefore be a good idea to set a maximum number of matches before starting the search. Note, however, that it is the first found matches that are retrieved. For example, if the maximum number of matches is set when searching in sorted order, the result can be without a match although its name is in the beginning of the sort order.

Extensions to the API

There are no extensions defined to Landmarks Search API.

Security issues

Landmarks are considered as important user data and this applies some access limitations to client applications. For example, in order to be able to read landmarks from landmark database client must have ReadUserData capability. The NetworkServices is required to access network-based databases. Whenever special capabilities are needed to utilize a class or method, they are listed in appropriate class and method descriptions.


Copyright © Nokia Corporation 2001-2008
Back to top