Mapbox Geocoding (Android Search SDK) - Java
Continue to study the Mapbox SDK. This part is going to learn the Search SDK as I need to get the coordinates(longitude and latitude) of a place before drawing a route from the current position to that place. So I need to input the name of the place I want to go and ask for its coordinates, that is forward geocoding.
Add Dependency
To use Mapbox search SDK, we can use the same token that applied in the previous post Mapbox Map SDK. In this post, I only invoke the SearchEngine.search() and SearchEngine.select() methods from the SDK. I would build a simple UI by myself. Therefore, I can only add Search Core SDK.
dependencies { ...... implementation "com.mapbox.search:mapbox-search-android:1.0.0-beta.36" ...... }
Define UI component
The example provided by Mapbox is programmed by Kotlin and I will use Java here. Since the example does not contain UI. I have to implement it myself. I put the EditText on top of the screen for inputting the name or address of the place. The RecyclerView contains the suggestions provided by Mapbox Search SDK. Then I can click the place shown in the RecyclerView to get the coordinates.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".SearchPlaceFragment"> <EditText android:id = "@+id/searchTextInput" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/search_place" /> <View android:id="@+id/divider" android:layout_width="match_parent" android:layout_height="1dp" android:background="?android:attr/listDivider" /> <androidx.recyclerview.widget.RecyclerView android:id = "@+id/suggestionPlaceRecyclerView" android:layout_width="match_parent" android:layout_weight="1" android:layout_height="0dp" /> </LinearLayout>
Implement the Logic
We first create an instance of SearchEngine which is responsible for acquiring the information of places.
SearchEngine searchEngine = MapboxSearchSdk.createSearchEngineWithBuiltInDataProviders( new SearchEngineSettings(getString(R.string.mapbox_access_token)) ); SearchOptions searchOption = new SearchOptions.Builder() .limit(6) //set maximum 6 place of suggestions return .build();
We need to request 2 times to the Mapbox server in order to get the coordinates. 1st time, we send the words we input as a search key to the Mapbox server and it will return a list of suggestions. We select one of them and send it the Mapbox for the coordinates. Therefore the workflow is like the picture below.
I have added a TextWatcher to the EditText so that we can ask for suggestions of location every time when there is a change in the text field of EditText. The method onSuggestion() of the instantiated object SearchSelectionCallback will be invoked if the search is successful. For accuracy, the callback interface required in this stage is SearchSuggestionsCallback. SearchSelectionCallback is the interface required in the 2nd search step and method onResult() is invoked. SearchSelectionCallback implements SearchSuggestionsCallback interface so we can use the same callback for both steps.
@Override public void onViewCreated(@NotNull View view, Bundle savedInstanceState) { ...... binding.searchTextInput.addTextChangedListener(textChangedListener); ...... } TextWatcher textChangedListener = new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { //searchResultsView.search(s.toString()); if(suggestionSearchRequestTask != null) suggestionSearchRequestTask.cancel(); String newText = s.toString().trim(); if(!TextUtils.isEmpty(newText)) { if(TextUtils.getTrimmedLength(newText) > 2) { //only search where 3 or more characters have been input Log.d("SearchPlace", "start to search keywords"); suggestionSearchRequestTask = searchEngine.search(newText, searchOption, searchCallback); } } } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged(Editable s) { } }; private final SearchSelectionCallback searchCallback = new SearchSelectionCallback(){ @Override public void onSuggestions(@NonNull List<SearchSuggestion> suggestions, @NonNull ResponseInfo responseInfo){ suggestionPlacesList.clear(); if(suggestions.isEmpty()){ Log.i("Search Fragment", "No suggestions found"); }else{ System.out.println("Number of suggestion got: " + suggestions.size()); suggestionPlacesList.addAll(suggestions); searchPlaceAdapter.replaceDataList(suggestionPlacesList); searchPlaceAdapter.notifyDataSetChanged(); } } @Override public void onResult(@NonNull SearchSuggestion suggestion, @NonNull SearchResult result, @NonNull ResponseInfo info) { Point selectPoint = null; if(result.getRoutablePoints() != null) { if(result.getRoutablePoints().size() != 0){ selectPoint = result.getRoutablePoints().get(0).getPoint(); } } if(selectPoint == null) selectPoint = result.getCoordinate(); System.out.println(result.getName() + " Latitude: " + selectPoint.latitude() + ", Longitude: " + selectPoint.longitude() ); Toast.makeText(getContext(),result.getName() + " Latitude: " + selectPoint.latitude() + ", Longitude: " + selectPoint.longitude(), Toast.LENGTH_SHORT ).show(); } @Override public void onCategoryResult(@NonNull SearchSuggestion suggestion, @NonNull List<? extends SearchResult> results, @NonNull ResponseInfo responseInfo) { Log.i("SearchApiExample", "Category search results: " + results); } @Override public void onError(@NonNull Exception e) { Log.i("SearchApiExample", "Search error: ", e); } };
The list of suggestions is shown to the RecyclerView. When we click the place in the list, we use that SearchSuggestion object as a parameter for the 2nd step, invoking searchEninge.select(). So that we can get the coordinates in the method onResult() of callback.
suggestionSearchRequestTask.cancel(); searchEngine.select(suggestionPlacesList.get(position), searchCallback);
P.S For the explanation of adding a click event listener to RecyclerView. Please refer to the page RecyclerView with OnClick Event.
We can get the Point object from the SearchResult of method onResult, or array of Point objects of routablePoint. At this moment, the official website or API webpage does not explain the details of which one we should use.
Now, we have the longitude and latitude of the place we want to go. Next step is to request the route from the Mapbox server by providing the start point(location of mobile phone) and the destination. I will use it in ext
View the source code of this page in GitHub.
留言
發佈留言