Skip to content

Search

This section explains how to implement online, offline, coordinate, flat or custom places geocoding and search (or combination of any of these) into your Android application.

Autocomplete, Geocoding and SearchPlaces

Applications developed with the Sygic Maps SDK can use geocoding to transform textual query into geographical information. With the SearchManager you can perform three basic actions : Geocoding, ReverseGeocoding and Autocomplete.

Note

If you use the online services, you might want to define to send a request once every 300ms or so, otherwise some requests might end up with an error TOO_MANY_REQUESTS.

Search types

The search engine offers few types of search engines which you can use separately or combined together

  • Coordinate Search - searching coordinates (search input may look like "48.1457, 17.1269")
  • Offline Maps Search - searching within offline installed maps
  • Online Maps Search - searching via online Search service API
  • Flat Search - searching among previously imported flat data
  • Custom Places Search - searching among previously imported custom places via CustomPlacesManager

Each type of search engine could be created via SearchManager and allows you to create search sessions

A Search session is used to actual search itself and has a limited lifetime which follows this lifecycle graph

1) create the search session
2) do [0..N] autocomplete requests 
3) then either of:
    - do nothing
    - geocode with location ID
    - geocode with query
    - do [0..N] search places requests with continuation token
4) close the session

That means, you are not able to use autocomplete operation again after performing at least one of the operations from step 3.

To create, for example, offline maps search engine, you may use this code:

val searchManager = SearchManagerProvider.getInstance().get()
var offlineMapSearch: Search? = null
searchManager.createOfflineMapSearch(object: CreateSearchCallback<OfflineMapSearch> {
    override fun onSuccess(search: OfflineMapSearch) {
        offlineMapSearch = search
        ...
        // create sessions and search
    }

    override fun onError(error: CreateSearchCallback.Error) {
        // search engine not available
    }
})

Please, bear in mind, that offline maps search engine consumes more resources, so try to reuse single instance for creating search sessions. This example shows, how it is possible to combine the coordinate search and the offline map search together into the one search engine:

val searchManager = SearchManagerProvider.getInstance().get()
var offlineMapSearch: Search? = null
var coordinateSearch: Search? = null
...
// obtain both search engines as in the previous example
...

var compositeSearch: Search? = null
searchManager.createCompositeSearch(
    SearchManager.CompositeSearchType.PARALLEL,
    listOf(offlineMapSearch, coordinateSearch),
    object: CreateSearchCallback<CompositeSearch> {
        override fun onSuccess(search: CompositeSearch) {
            compositeSearch = search
            ...
            // create sessions and search within both search engines
        }

        override fun onError(error: CreateSearchCallback.Error) {
            // search engine not available
        }
})

After finding the desired result in autocomplete, you can take its locationID and call Geocode() with it to get more precise information about the location. If you do not need to use autocomplete and have the full string that you want to search for, just use Geocode straight away.

When searching for places, you can create a new placeRequest and use your position, the categories that you want to search for and several other arguments.

For example, offline autocomplete search session with geocode operation may look like this:

val offlineSearch: OfflineMapSearch
...
// obtain search instance via SearchManager
...
val position = GeoCoordinates(51.510175, -0.122445)
val searchRequest = SearchRequest("London Eye", position)
val session = offlineSearch.createSession()

session.autocomplete(searchRequest, object : AutocompleteResultListener {
    override fun onAutocomplete(autocompleteResult: List<AutocompleteResult>) {
        // handle results
    }

    override fun onAutocompleteError(status: ResultStatus) {}
})
session.geocode(searchRequest, object : GeocodingResultsListener {
    override fun onGeocodingResults(geocodingResults: List<GeocodingResult>) {
        // handle results
    }

    override fun onGeocodingResultsError(status: ResultStatus) {}
})

...

// close session when you are done with handling results
session.close()
````

Or online place search session:

=== "Kotlin"

````kotlin
val onlineSearch: OnlineMapSearch
...
// obtain search instance via SearchManager
...
val categories = listOf(PlaceCategories.TouristInformationOffice, PlaceCategories.Museum)
val placeRequest = PlaceRequest(position, categories, /*radius*/ 1000)
val session = onlineSearch.createSession()

session.searchPlaces(placeRequest, object : PlacesListener {
    override fun onPlacesLoaded(places: List<Place>, continuationToken: String?) {
        // handle results
    }

    override fun onPlacesError(status: ResultStatus) {}
})

...

// close session when you are done with handling results
session.close()

Reverse geocoding

Reverse geocoding is the opposite process to geocoding, meaning you query by geographical location and receive textual address of the location. You can define a filter if you do not want to take walkways into account or pass it an emptySet.

ReverseGeocoderProvider.getInstance(object : CoreInitCallback<ReverseGeocoder> {
    override fun onInstance(reverseGeocoder: ReverseGeocoder) {
        reverseGeocoder.reverseGeocode(position, emptySet(), object : ReverseGeocoder.ReverseGeocodingResultListener {
            override fun onReverseGeocodingResult(result: List<ReverseGeocodingResult>) {
                // do not forget to filter out the walkways if you do not want them
            }

            override fun onReverseGeocodingResultError(code: ReverseGeocoder.ErrorCode) {}
        })
    }

    override fun onError(error: CoreInitException) {}
})

During the implementation you will probably stumble upon a task of navigating to a search result. Let's see a simple example of how this would be achieved. Please bear in mind that in this example we do it in a simple callback succession and it's probably not what would be desired to exist in a complete application.

val offlineSearch: OfflineMapSearch
...
// obtain search instance via SearchManager
...
val searchRequest = SearchRequest("champs-elysees 121", GeoCoordinates(48.2879167, 17.2636084))
val searchSession = offlineSearch.createSession()
val router = RouterProvider.getInstance().get()

searchSession.autocomplete(searchRequest, object : AutocompleteResultListener {
  override fun onAutocomplete(autocompleteResult: List<AutocompleteResult>) {
    // here you can show the list of the results to the user
    // the results contain info such as distance to the result, its type, or which parts of the text were matched
    // let's suppose that the user chooses the first result (usually the most relevant one)
    // now let's take the locationID and use it for calling the geocode() function
    searchSession.geocode(GeocodeLocationRequest(autocompleteResult[0].locationId), object : GeocodingResultListener {
        override fun onGeocodingResult(geocodingResult: GeocodingResult) {
            // this is where you get detailed information about the result
            // you also get the GeoCoordinates of the object
            val routePlan = RoutePlan() // create a route plan
            routePlan.setStart(your-last-known-location) // set the start point
            routePlan.setDestination(geocodingResult.location) // set the destination point - using the GeocodingResult
            router.computeRoute(routePlan, object: Router.RouteComputeListener{}) // compute the route. more in the Routing section
        }

        override fun onGeocodingResultError(status: ResultStatus) {
          // handle the errors here
        }
    })
  }

  override fun onAutocompleteError(status: ResultStatus) {
    // handle the errors here
  }
})