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) {}
})
Navigate to a search result¶
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
}
})
Get the time at a location¶
One of the two prerequisites need to be met in order to get the time at a location, otherwise the operation will finish with error:
- The coordinate must be inside of already downloaded maps
- Online maps need to be active
You can use ReverseGeocoder
to retrieve the local time given the position and UTC time.
ReverseGeocoderProvider.getInstance().get().getLocalTimeAtLocation(
GeoCoordinates(12.345,67.8910), // the location from which you want to get the local time
System.currentTimeMillis() / 1000L, // in seconds!
object: ReverseGeocoder.TimeAtLocationResultListener {
override fun onError(errorCode: ReverseGeocoder.ErrorCode) {
}
override fun onSuccess(unixTimestamp: Long) {
// result
}
}
)